/*
 * agente - executa o agente tecnico agente-at
 * Uso: pp --agente=agente-at --ler-mensagem <arquivo>
 *
 * Resolucao de arquivos:
 *   1) agentes/agente-at.json do sistema atual
 *   2) core/biblioteca-agentes-fixos/agente-at.json como fallback
 *
 * Executa somente agentes declarados e suportados pelo runtime atual.
 */

#define _GNU_SOURCE
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <dirent.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>
#include "../restricao.h"

static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --agente=agente-at --ler-mensagem <arquivo-mensagem>\n"
        "  pp --agente=<agente-consultor>:<conector>\n"
        "  pp --agente=agente-at --ajuda:<grupo>\n"
        "  pp --agente --ajuda\n"
        "\n"
        "Descrição:\n"
        "  O comando --agente executa o agente técnico agente-at a partir de\n"
        "  mensagens entregues pela escuta ou um agente consultor derivado de spec\n"
        "  para analisar conectores e gerar árvore SA em relspec/.\n"
        "\n"
        "Grupos de ajuda: uso, entrada, execucao, arquivos, agentes, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda completa\n"
        "  --ajuda:<grupo>                Exibe apenas um grupo\n"
        "\n"
        "2) Entrada e identificação [entrada identificacao]\n"
        "  --agente=agente-at             Agente técnico suportado\n"
        "  --agente=<agente>:<conector>   Agente consultor sincronizado por spec\n"
        "  --ler-mensagem <arquivo>       Processa mensagem entregue pela escuta (agente-at)\n"
        "\n"
        "3) Execução [execucao runtime]\n"
        "  O agente-at lê o JSON da mensagem e executa manutenção técnica rastreável.\n"
        "  Agentes consultores analisam o conector alvo e constroem relatório via pp --sa em relspec/.\n"
        "  Conectores continuam sendo executados por runtime-conector, não por este comando.\n"
        "\n"
        "4) Resolução de JSONs [arquivos resolucao]\n"
        "  Manifesto agente-at: agentes/agente-at.json local ou fallback em core/biblioteca-agentes-fixos/.\n"
        "  Manifesto consultor: agentes/<agente>.json criado por pp --sincronizar-agente=<agente>.\n"
        "\n"
        "5) Agentes fixos e consultores [agentes manual]\n"
        "  agente-at              Executor técnico por mensagem do parallm/espaco\n"
        "  <agente>:<conector>    Consultor de conformidade de spec para conector\n"
        "\n"
        "6) Exemplos [exemplos]\n"
        "  pp --agente=agente-at --ler-mensagem espaco/processando/mensagem.json\n"
        "  pp --sincronizar-agente=seguranca_zero_trust_web_api\n"
        "  pp --agente=seguranca_zero_trust_web_api:conector-email\n",
        stdout);
}

static int arg_ajuda(const char *arg)
{
    return arg && (!strcmp(arg, "--ajuda") || !strcmp(arg, "--help") || !strcmp(arg, "-h") ||
                   !strncmp(arg, "--ajuda:", 8) || !strncmp(arg, "--ajuda=", 8) ||
                   !strncmp(arg, "--help:", 7) || !strncmp(arg, "--help=", 7));
}

static void uso_agente_at(void)
{
    fputs(
        "Uso:\n"
        "  pp --agente=agente-at --ler-mensagem <arquivo-mensagem>\n"
        "  pp --agente=agente-at --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Executor tecnico autonomo acionado por mensagem, normalmente criada pelo\n"
        "  pp --parallm. Ele aplica um plano declarativo de arquivos com backups,\n"
        "  atualiza os mapas SA, gera divergencias e registra relatorio em tarefas/agente-at/.\n"
        "\n"
        "Grupos de ajuda: uso, operador, prerequisitos, execucao, artefatos, seguranca, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda do agente-at\n"
        "  --ajuda:<grupo>                Filtra grupo. Ex: pp --agente=agente-at --ajuda:seguranca\n"
        "  --ler-mensagem <arquivo>       Mensagem agente-at.executar entregue pelo espaco/parallm\n"
        "\n"
        "2) Visao do operador [operador manual]\n"
        "  Use manualmente apenas para reprocessar uma mensagem valida ja gerada ou para\n"
        "  depurar uma execução do parallm. Para pedir alterações novas, prefira\n"
        "  registrar um GC em uma árvore SA e executar pp --sa=<arvore> --prompt=<gc>.\n"
        "\n"
        "3) Pre-requisitos [prerequisitos entrada]\n"
        "  Obrigatorio: agentes/agente-at.json ou fallback na biblioteca fixa\n"
        "  Obrigatorio: mensagem com idmensagem agente-at.executar e dados do parallm\n"
        "  Obrigatorio: conta LLM configurada/mapeada quando a execução precisar gerar plano\n"
        "  O contexto vem exclusivamente da mensagem.\n"
        "\n"
        "4) Execução manual [execucao passo-a-passo]\n"
        "  1. Localize a mensagem em espaco/processando ou artefato de depuração.\n"
        "  2. Rode pp --agente=agente-at --ler-mensagem <arquivo>.\n"
        "  3. Após aplicar alterações, o agente-at roda pp --atualizar-manual-funcional e pp --divergencia-sistema.\n"
        "  4. Revise o relatorio em tarefas/agente-at/<execucao>/relatorio-execucao.json.\n"
        "  5. Em erro, mantenha contexto/plano/stdout para auditoria; em sucesso, temporarios sao limpos.\n"
        "\n"
        "5) Artefatos gerados [artefatos saida arquivos]\n"
        "  tarefas/agente-at/<execucao>/relatorio-execucao.json\n"
        "  Backups dos arquivos alterados antes de sobrescrever/substituir/deletar\n"
        "  Em falha: contexto-recebido.json, prompt-operacoes.txt, llm-stdout.txt, plano-operacoes.json\n"
        "\n"
        "6) Segurança e restrições [seguranca restricoes]\n"
        "  Não toca em secretos/, contas-llm/, espaco/, sessoes/ ou .git/.\n"
        "  Não usa shell arbitrário para implementar; aplica apenas operações declarativas validadas.\n"
        "  Specs novas ou diretivas devem ficar exclusivamente em spec-diretivas/ do sistema atual.\n"
        "  Comunicação externa somente via pp --api; nunca publicação direta no espaço.\n"
        "\n"
        "7) Exemplos [exemplos]\n"
        "  pp --sa=arvores/manutencao-sistema --prompt=rev-divergencias-001 --rel\n"
        "  pp --agente=agente-at --ler-mensagem espaco/processando/mensagem.json\n",
        stdout);
}

static int uso_agente_especifico(const char *agente)
{
    if (!strcmp(agente, "agente-at")) { uso_agente_at(); return 1; }
    fprintf(stderr, "agente: agente nao encontrado: %s\n", agente ? agente : "");
    return 0;
}

static int eh_dir(const char *p)
{
    struct stat st;
    return stat(p, &st) == 0 && S_ISDIR(st.st_mode);
}

static int termina_com(const char *s, const char *suf)
{
    size_t a = strlen(s), b = strlen(suf);
    return a >= b && strcmp(s + a - b, suf) == 0;
}

static int eh_arq(const char *p)
{
    struct stat st;
    return stat(p, &st) == 0 && S_ISREG(st.st_mode);
}

static int normalizar_nome(const char *in, char *out, size_t tam)
{
    if (!in || !*in || !out || tam == 0) return -1;
    char tmp[PATH_MAX];
    snprintf(tmp, sizeof(tmp), "%s", in);
    char *v = tmp;
    while (isspace((unsigned char)*v)) v++;
    if (!strncmp(v, "agentes/", 8)) v += 8;
    size_t len = strlen(v);
    while (len > 0 && isspace((unsigned char)v[len - 1])) v[--len] = '\0';
    if (!*v || strchr(v, '/') || strchr(v, '\\') || strstr(v, "..")) return -1;
    size_t n = strlen(v);
    if (n >= 5 && !strcasecmp(v + n - 5, ".json")) v[n - 5] = '\0';
    size_t j = 0;
    int sep = 0;
    for (size_t i = 0; v[i] && j + 1 < tam; i++) {
        unsigned char c = (unsigned char)v[i];
        if (isalnum(c)) { out[j++] = (char)tolower(c); sep = 0; }
        else if (c == '-' || c == '_' || isspace(c)) {
            if (!sep && j > 0) out[j++] = '-';
            sep = 1;
        } else if (c >= 128) {
            if (!sep && j > 0) out[j++] = '-';
            sep = 1;
        }
    }
    while (j > 0 && out[j - 1] == '-') j--;
    out[j] = '\0';
    return out[0] ? 0 : -1;
}

static char *ler_arquivo(const char *p)
{
    FILE *f = fopen(p, "rb");
    if (!f) return NULL;
    if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; }
    long t = ftell(f);
    if (t < 0 || t > 20 * 1024 * 1024) { fclose(f); return NULL; }
    rewind(f);
    char *b = malloc((size_t)t + 1);
    if (!b) { fclose(f); return NULL; }
    size_t n = fread(b, 1, (size_t)t, f);
    int erro = ferror(f);
    fclose(f);
    if (erro) { free(b); return NULL; }
    b[n] = '\0';
    return b;
}

static int raiz_sisc_de_sistema(const char *raiz_sistema, char *out, size_t tam)
{
    char tmp[PATH_MAX];
    snprintf(tmp, sizeof(tmp), "%s", raiz_sistema);
    char *slash = strrchr(tmp, '/');
    if (!slash || slash == tmp) return -1;
    *slash = '\0';
    snprintf(out, tam, "%s", tmp);
    return 0;
}

/* Resolve o caminho do JSON de definicao do agente.
 * 1) Primeiro procura em agentes/ do sistema atual (<raiz_sistema>/agentes/<agente>.json).
 * 2) Se nao encontrar, usa a definicao padrao em core/biblioteca-agentes-fixos/<agente>.json
 *    diretamente, sem fazer copia (somente leitura).
 *
 * Isso permite que cada sistema tenha sua propria versao alterada de um agente em agentes/,
 * ou utilize a versao padrao da biblioteca central quando nao houver sobrescrita local.
 *
 * Retorna 0 em caso de sucesso (json_path_out preenchido com caminho valido),
 * ou -1 se o agente nao foi encontrado em nenhum local. */
static int resolver_agente_json(const char *raiz_sistema,
                                const char *agente,
                                char *json_path_out,
                                size_t json_path_out_sz)
{
    /* Passo 1: procura no sistema atual */
    snprintf(json_path_out, json_path_out_sz, "%s/agentes/%s.json", raiz_sistema, agente);
    if (eh_arq(json_path_out)) return 0;

    /* Compatibilidade: specs usam underscore no nome do arquivo, enquanto alguns
     * comandos humanos podem informar hifens. */
    char alt[PATH_MAX];
    snprintf(alt, sizeof(alt), "%s", agente ? agente : "");
    int mudou = 0;
    for (char *p = alt; *p; p++) {
        if (*p == '-') { *p = '_'; mudou = 1; }
    }
    if (mudou) {
        snprintf(json_path_out, json_path_out_sz, "%s/agentes/%s.json", raiz_sistema, alt);
        if (eh_arq(json_path_out)) return 0;
    }

    /* Passo 2: fallback para a biblioteca central (somente leitura, sem copia) */
    char raiz_sisc[PATH_MAX];
    if (raiz_sisc_de_sistema(raiz_sistema, raiz_sisc, sizeof(raiz_sisc)) != 0) return -1;

    snprintf(json_path_out, json_path_out_sz, "%s/core/biblioteca-agentes-fixos/%s.json", raiz_sisc, agente);
    if (eh_arq(json_path_out)) return 0;

    return -1;
}

static int agente_conta_llm_valida(const char *s)
{
    if (!s || !*s) return 0;
    size_t n = strlen(s);
    if (n >= PATH_MAX) return 0;
    for (size_t i = 0; i < n; i++) {
        unsigned char c = (unsigned char)s[i];
        if (!(isalnum(c) || c == '.' || c == '_' || c == '-')) return 0;
    }
    return 1;
}

static void agente_normalizar_conta_llm(const char *in, char *out, size_t tam)
{
    if (!out || tam == 0) return;
    out[0] = '\0';
    if (!in) return;
    while (*in && isspace((unsigned char)*in)) in++;
    size_t n = strlen(in);
    while (n > 0 && isspace((unsigned char)in[n - 1])) n--;
    if (n == 0 || n >= tam) return;
    char tmp[PATH_MAX];
    if (n >= sizeof(tmp)) return;
    memcpy(tmp, in, n);
    tmp[n] = '\0';
    if (!agente_conta_llm_valida(tmp)) return;
    snprintf(out, tam, "%s", tmp);
    size_t len = strlen(out);
    if (len < 5 || strcmp(out + len - 5, ".json") != 0) {
        if (len + 5 >= tam) { out[0] = '\0'; return; }
        strcat(out, ".json");
    }
}

static char *json_str_intervalo(const char *ini, const char *fim, const char *k)
{
    if (!ini || !fim || fim <= ini || !k) return NULL;
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", k);
    size_t plen = strlen(pat);
    for (const char *p = ini; p < fim; p++) {
        const char *m = strstr(p, pat);
        if (!m || m >= fim || m + plen >= fim) return NULL;
        const char *q = strchr(m + plen, ':');
        if (!q || q >= fim) return NULL;
        q++;
        while (q < fim && isspace((unsigned char)*q)) q++;
        if (q >= fim || *q != '"') { p = m + plen; continue; }
        q++;
        size_t cap = 128, n = 0;
        char *out = malloc(cap);
        if (!out) return NULL;
        while (q < fim && *q != '"') {
            char c = *q++;
            if (c == '\\' && q < fim) {
                c = *q++;
                if (c == 'n') c = '\n';
                else if (c == 't') c = '\t';
                else if (c == 'r') c = '\r';
            }
            if (n + 1 >= cap) {
                cap *= 2;
                char *nb = realloc(out, cap);
                if (!nb) { free(out); return NULL; }
                out = nb;
            }
            out[n++] = c;
        }
        out[n] = '\0';
        return out;
    }
    return NULL;
}

static const char *json_objeto_fim(const char *obj)
{
    if (!obj || *obj != '{') return NULL;
    int nivel = 0, em_string = 0, esc = 0;
    for (const char *p = obj; *p; p++) {
        unsigned char c = (unsigned char)*p;
        if (em_string) {
            if (esc) { esc = 0; continue; }
            if (c == '\\') { esc = 1; continue; }
            if (c == '"') em_string = 0;
            continue;
        }
        if (c == '"') { em_string = 1; continue; }
        if (c == '{') nivel++;
        else if (c == '}') {
            nivel--;
            if (nivel == 0) return p + 1;
        }
    }
    return NULL;
}

static char *agente_llm_arquivo_configuracao(const char *json)
{
    const char *p = strstr(json ? json : "", "\"llm\"");
    if (!p) return NULL;
    p = strchr(p + 5, ':');
    if (!p) return NULL;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '{') return NULL;
    const char *fim = json_objeto_fim(p);
    if (!fim) return NULL;
    char *cfg = json_str_intervalo(p, fim, "arquivoConfiguracao");
    if (!cfg) cfg = json_str_intervalo(p, fim, "arquivo-configuracao");
    if (!cfg) cfg = json_str_intervalo(p, fim, "arquivo_configuracao");
    return cfg;
}

static int agente_configurar_llm_do_manifesto(const char *json, const char *agente)
{
    char *cfg = agente_llm_arquivo_configuracao(json);
    if (!cfg) return 0;

    char conta[PATH_MAX];
    agente_normalizar_conta_llm(cfg, conta, sizeof(conta));
    free(cfg);
    if (!conta[0]) {
        fprintf(stderr, "agente: llm.arquivoConfiguracao invalido no manifesto de %s.\n", agente ? agente : "");
        return -1;
    }
    setenv("SISC_CONTA_LLM", conta, 1);
    return 1;
}

static void json_escape(FILE *f, const char *s)
{
    fputc('"', f);
    for (const unsigned char *p = (const unsigned char *)(s ? s : ""); *p; p++) {
        switch (*p) {
            case '"': fputs("\\\"", f); break;
            case '\\': fputs("\\\\", f); break;
            case '\n': fputs("\\n", f); break;
            case '\r': fputs("\\r", f); break;
            case '\t': fputs("\\t", f); break;
            default: if (*p < 0x20) fprintf(f, "\\u%04x", *p); else fputc(*p, f);
        }
    }
    fputc('"', f);
}

#define AGENTE_AT_MAX_PROMPT (2 * 1024 * 1024)
#define AGENTE_AT_MAX_PROMPT_ORIGINAL 24000
#define AGENTE_AT_MAX_RESPOSTA_PARALLM 30000
#define AGENTE_AT_MAX_CONTEXTO_HEB 12000

static char *agente_at_json_str(const char *j, const char *k)
{
    if (!j || !k) return NULL;
    char pat[160];
    snprintf(pat, sizeof(pat), "\"%s\"", k);
    const char *p = strstr(j, pat);
    if (!p) return NULL;
    p = strchr(p + strlen(pat), ':');
    if (!p) return NULL;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '"') return NULL;
    p++;

    size_t cap = strlen(p) + 1;
    char *out = malloc(cap);
    if (!out) return NULL;
    size_t n = 0;
    int esc = 0;
    while (*p) {
        unsigned char c = (unsigned char)*p++;
        if (esc) {
            switch (c) {
                case 'n': out[n++] = '\n'; break;
                case 'r': out[n++] = '\r'; break;
                case 't': out[n++] = '\t'; break;
                case '"': out[n++] = '"'; break;
                case '\\': out[n++] = '\\'; break;
                case '/': out[n++] = '/'; break;
                case 'u':
                    /* Unicode simplificado: preserva marcador legivel. */
                    if (n + 6 < cap) { out[n++] = '\\'; out[n++] = 'u'; }
                    for (int i = 0; i < 4 && *p; i++) out[n++] = *p++;
                    break;
                default: out[n++] = (char)c; break;
            }
            esc = 0;
            continue;
        }
        if (c == '\\') { esc = 1; continue; }
        if (c == '"') break;
        out[n++] = (char)c;
    }
    out[n] = '\0';
    return out;
}

static char *agente_at_substr(const char *ini, const char *fim)
{
    if (!ini || !fim || fim < ini) return NULL;
    size_t n = (size_t)(fim - ini);
    char *out = malloc(n + 1);
    if (!out) return NULL;
    memcpy(out, ini, n);
    out[n] = '\0';
    return out;
}

static char *agente_at_limitar_texto(const char *texto, size_t max, const char *rotulo)
{
    if (!texto) return strdup("");
    size_t n = strlen(texto);
    if (n <= max) return strdup(texto);
    const char *msg = "\n\n[... conteudo truncado pelo agente-at para manter a chamada LLM pequena; use o contexto salvo em tarefas/agente-at se precisar auditar ...]\n\n";
    size_t rlen = strlen(rotulo ? rotulo : ""), mlen = strlen(msg);
    char *out = malloc(max + mlen + rlen + 96);
    if (!out) return NULL;
    size_t cab = max / 2;
    size_t cauda = max - cab;
    snprintf(out, max + mlen + rlen + 96,
             "%.*s%s[%s: original com %zu bytes; enviado com %zu bytes]\n%.*s",
             (int)cab, texto, msg, rotulo ? rotulo : "secao", n, max,
             (int)cauda, texto + n - cauda);
    return out;
}

static char *agente_at_nome_arvore_de_token(const char *p)
{
    if (!p) return NULL;
    while (*p && (isspace((unsigned char)*p) || *p == '=' || *p == '`' || *p == '\'' || *p == '"')) p++;
    const char *ini = p;
    while (*p) {
        unsigned char c = (unsigned char)*p;
        if (isalnum(c) || c == '_' || c == '-' || c == '/' || c == '.') { p++; continue; }
        break;
    }
    const char *fim = p;
    while (fim > ini && (fim[-1] == '.' || fim[-1] == ',' || fim[-1] == ';' || fim[-1] == ':' || fim[-1] == ')' || fim[-1] == '`' || fim[-1] == '\'' || fim[-1] == '"')) fim--;
    if ((size_t)(fim - ini) > 5 && strncmp(fim - 5, ".json", 5) == 0) fim -= 5;
    while (fim > ini && fim[-1] == '/') fim--;
    if (fim <= ini) return NULL;

    size_t n = (size_t)(fim - ini);
    if (n >= 256) return NULL;
    char *nome = malloc(n + 1);
    if (!nome) return NULL;
    memcpy(nome, ini, n);
    nome[n] = '\0';
    if (!strcmp(nome, "inteira") || !strcmp(nome, "atual") || !strcmp(nome, "arvore") || !strcmp(nome, "árvore")) {
        free(nome);
        return NULL;
    }
    return nome;
}

static char *agente_at_extrair_nome_arvore(const char *texto)
{
    if (!texto) return NULL;

    const char *p = strstr(texto, "--sa=");
    if (p) {
        char *nome = agente_at_nome_arvore_de_token(p + 5);
        if (nome) return nome;
    }

    p = strstr(texto, "arvores/");
    if (p) {
        char *nome = agente_at_nome_arvore_de_token(p);
        if (nome) return nome;
    }

    const char *marcas[] = { "árvore ", "arvore ", "árvore `", "arvore `" };
    for (size_t i = 0; i < sizeof(marcas) / sizeof(marcas[0]); i++) {
        p = strstr(texto, marcas[i]);
        if (!p) continue;
        p += strlen(marcas[i]);
        char *nome = agente_at_nome_arvore_de_token(p);
        if (nome) return nome;
    }
    return NULL;
}

static int agente_at_resolver_path_arvore(const char *raiz, const char *nome, char *path, size_t pathsz, char *rel, size_t relsz)
{
    if (!nome || !*nome || !path || pathsz == 0) return -1;
    const char *base = raiz ? raiz : ".";
    const char *suf = termina_com(nome, ".json") ? "" : ".json";

    char candidatos[3][PATH_MAX * 2];
    int total = 0;
    if (strchr(nome, '/')) {
        snprintf(candidatos[total++], sizeof(candidatos[0]), "%s%s", nome, suf);
    } else {
        snprintf(candidatos[total++], sizeof(candidatos[0]), "%s%s", nome, suf);
        snprintf(candidatos[total++], sizeof(candidatos[0]), "arvores/%s%s", nome, suf);
    }

    for (int i = 0; i < total; i++) {
        char abs[PATH_MAX * 2];
        snprintf(abs, sizeof(abs), "%s/%s", base, candidatos[i]);
        if (!eh_arq(abs)) continue;
        snprintf(path, pathsz, "%s", abs);
        if (rel && relsz) snprintf(rel, relsz, "%s", candidatos[i]);
        return 0;
    }
    return -1;
}

static int agente_at_json_eh_arvore_sa(const char *json)
{
    if (!json) return 0;
    const char *p = json;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '{') return 0;

    const char *tipo = strstr(p, "\"tipo\"");
    if (!tipo) return 0;
    const char *v = strchr(tipo, ':');
    if (!v) return 0;
    v++;
    while (*v && isspace((unsigned char)*v)) v++;
    return strncmp(v, "\"arvore-sa\"", 11) == 0;
}

static char *agente_at_ler_arvore_referenciada(const char *raiz, const char *prompt_original, const char *resposta)
{
    char *nome = agente_at_extrair_nome_arvore(prompt_original);
    if (!nome) nome = agente_at_extrair_nome_arvore(resposta);
    if (!nome) return strdup("");
    char path[PATH_MAX * 2], rel[PATH_MAX * 2];
    if (agente_at_resolver_path_arvore(raiz, nome, path, sizeof(path), rel, sizeof(rel)) != 0) {
        free(nome);
        return strdup("");
    }
    char *json = ler_arquivo(path);
    if (!json || !agente_at_json_eh_arvore_sa(json)) {
        free(nome);
        free(json);
        return strdup("");
    }
    size_t n = strlen(json) + strlen(rel) + 128;
    char *out = malloc(n);
    if (!out) { free(nome); free(json); return NULL; }
    snprintf(out, n, "Arquivo atual %s:\n```json\n%s\n```\n", rel, json);
    free(nome); free(json);
    return out;
}

static char *agente_at_extrair_objeto_json(const char *texto)
{
    if (!texto) return NULL;
    const char *ini = strchr(texto, '{');
    if (!ini) return NULL;
    int nivel = 0, em_string = 0, esc = 0;
    for (const char *p = ini; *p; p++) {
        unsigned char c = (unsigned char)*p;
        if (em_string) {
            if (esc) { esc = 0; continue; }
            if (c == '\\') { esc = 1; continue; }
            if (c == '"') em_string = 0;
            continue;
        }
        if (c == '"') { em_string = 1; continue; }
        if (c == '{') nivel++;
        else if (c == '}') {
            nivel--;
            if (nivel == 0) return agente_at_substr(ini, p + 1);
            if (nivel < 0) return NULL;
        }
    }
    return NULL;
}

static int agente_at_mkdir_p(const char *path)
{
    if (!path || !*path) return -1;
    char tmp[PATH_MAX * 2];
    snprintf(tmp, sizeof(tmp), "%s", path);
    size_t len = strlen(tmp);
    while (len > 1 && tmp[len - 1] == '/') tmp[--len] = '\0';
    for (char *p = tmp + 1; *p; p++) {
        if (*p == '/') {
            *p = '\0';
            if (mkdir(tmp, 0775) != 0 && errno != EEXIST) return -1;
            *p = '/';
        }
    }
    if (mkdir(tmp, 0775) != 0 && errno != EEXIST) return -1;
    chmod(tmp, 0775);
    return 0;
}

static int agente_at_parent_mkdir(const char *path)
{
    char tmp[PATH_MAX * 2];
    snprintf(tmp, sizeof(tmp), "%s", path ? path : "");
    char *s = strrchr(tmp, '/');
    if (!s) return 0;
    *s = '\0';
    return agente_at_mkdir_p(tmp);
}

static int agente_at_caminho_seguro(const char *raiz, const char *rel, char *out, size_t outsz, char *erro, size_t errosz)
{
    if (!raiz || !rel || !*rel) { snprintf(erro, errosz, "caminho vazio"); return -1; }
    while (isspace((unsigned char)*rel)) rel++;
    if (*rel == '/') { snprintf(erro, errosz, "caminho absoluto proibido: %s", rel); return -1; }

    char tmp[PATH_MAX];
    snprintf(tmp, sizeof(tmp), "%s", rel);
    for (char *p = tmp; *p; p++) if (*p == '\\') *p = '/';

    char norm[PATH_MAX] = "";
    char prim[256] = "";
    char *save = NULL;
    char *tok = strtok_r(tmp, "/", &save);
    while (tok) {
        if (!strcmp(tok, ".") || !*tok) { tok = strtok_r(NULL, "/", &save); continue; }
        if (!strcmp(tok, "..")) { snprintf(erro, errosz, "segmento '..' proibido em caminho"); return -1; }
        if (!prim[0]) snprintf(prim, sizeof(prim), "%s", tok);
        if (norm[0]) strncat(norm, "/", sizeof(norm) - strlen(norm) - 1);
        strncat(norm, tok, sizeof(norm) - strlen(norm) - 1);
        tok = strtok_r(NULL, "/", &save);
    }
    if (!norm[0]) { snprintf(erro, errosz, "caminho vazio"); return -1; }
    if (!strcmp(prim, ".git") || !strcmp(prim, "secretos") || !strcmp(prim, "contas-llm") ||
        !strcmp(prim, "espaco") || !strcmp(prim, "sessoes")) {
        snprintf(erro, errosz, "diretorio protegido para agente-at: %s", prim);
        return -1;
    }

    /* Politica canonica de specs: dentro de um sistema, specs so podem existir em
     * spec-diretivas/. Isso bloqueia criacao acidental fora da raiz local. */
    const char *ultimo = strrchr(norm, '/');
    ultimo = ultimo ? ultimo + 1 : norm;
    int caminho_canonico_spec = (!strcmp(norm, "spec-diretivas") || !strncmp(norm, "spec-diretivas/", 15));
    int parece_spec = ((strncmp(ultimo, "spec_", 5) == 0 || strncmp(ultimo, "diretiva-", 9) == 0) && termina_com(ultimo, ".json"));
    if (!caminho_canonico_spec && (parece_spec || strstr(norm, "/spec-diretivas") != NULL)) {
        snprintf(erro, errosz, "specs so podem ser criadas/editadas em spec-diretivas/ do sistema atual: %s", norm);
        return -1;
    }

    snprintf(out, outsz, "%s/%s", raiz, norm);
    return 0;
}

static int agente_at_copiar_arquivo(const char *origem, const char *destino)
{
    FILE *in = fopen(origem, "rb");
    if (!in) return -1;
    if (agente_at_parent_mkdir(destino) != 0) { fclose(in); return -1; }
    FILE *out = fopen(destino, "wb");
    if (!out) { fclose(in); return -1; }
    char buf[8192];
    size_t n;
    int erro = 0;
    while ((n = fread(buf, 1, sizeof(buf), in)) > 0) {
        if (fwrite(buf, 1, n, out) != n) { erro = 1; break; }
    }
    if (ferror(in)) erro = 1;
    fclose(in);
    if (fclose(out) != 0) erro = 1;
    return erro ? -1 : 0;
}

static int agente_at_backup(const char *raiz, const char *run_dir, const char *abs, const char *rel, char *backup_rel, size_t backup_sz)
{
    if (!eh_arq(abs)) { if (backup_rel && backup_sz) backup_rel[0] = '\0'; return 0; }
    char dst[PATH_MAX * 2];
    snprintf(dst, sizeof(dst), "%s/backups/%s", run_dir, rel ? rel : "arquivo");
    if (agente_at_copiar_arquivo(abs, dst) != 0) return -1;
    if (backup_rel && backup_sz) {
        if (!strncmp(dst, raiz, strlen(raiz)) && dst[strlen(raiz)] == '/') snprintf(backup_rel, backup_sz, "%s", dst + strlen(raiz) + 1);
        else snprintf(backup_rel, backup_sz, "%s", dst);
    }
    return 0;
}

static int agente_at_escrever_texto(const char *path, const char *conteudo, int append)
{
    if (agente_at_parent_mkdir(path) != 0) return -1;
    FILE *f = fopen(path, append ? "ab" : "wb");
    if (!f) return -1;
    size_t n = strlen(conteudo ? conteudo : "");
    int ok = fwrite(conteudo ? conteudo : "", 1, n, f) == n;
    return fclose(f) == 0 && ok ? 0 : -1;
}

static int agente_at_remover_recursivo(const char *path)
{
    struct stat st;
    if (lstat(path, &st) != 0) return errno == ENOENT ? 0 : -1;
    if (S_ISDIR(st.st_mode)) {
        DIR *d = opendir(path);
        if (!d) return -1;
        struct dirent *e;
        while ((e = readdir(d))) {
            if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue;
            char filho[PATH_MAX * 2];
            snprintf(filho, sizeof(filho), "%s/%s", path, e->d_name);
            if (agente_at_remover_recursivo(filho) != 0) { closedir(d); return -1; }
        }
        closedir(d);
        return rmdir(path);
    }
    return unlink(path);
}

static void agente_at_limpar_execucao_concluida(const char *raiz, const char *run_dir, const char *sid)
{
    DIR *d = opendir(run_dir);
    if (d) {
        struct dirent *e;
        while ((e = readdir(d))) {
            if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue;
            if (!strcmp(e->d_name, "relatorio-execucao.json")) continue;
            char p[PATH_MAX * 2];
            snprintf(p, sizeof(p), "%s/%s", run_dir, e->d_name);
            agente_at_remover_recursivo(p);
        }
        closedir(d);
    }

    char tarefas_dir[PATH_MAX * 2];
    snprintf(tarefas_dir, sizeof(tarefas_dir), "%s/tarefas/agente-at", raiz ? raiz : ".");
    d = opendir(tarefas_dir);
    if (d) {
        const char *base_atual = strrchr(run_dir, '/');
        base_atual = base_atual ? base_atual + 1 : run_dir;
        struct dirent *e;
        while ((e = readdir(d))) {
            if (e->d_name[0] == '.') continue;
            if (!strcmp(e->d_name, base_atual)) continue;
            const char *suf = strchr(e->d_name, '-');
            if (!suf || strcmp(suf + 1, sid ? sid : "") != 0) continue;
            char p[PATH_MAX * 2];
            snprintf(p, sizeof(p), "%s/%s", tarefas_dir, e->d_name);
            agente_at_remover_recursivo(p);
        }
        closedir(d);
    }
    printf("agente-at: execução concluída limpa; mantido somente relatorio-execucao.json em %s\n", run_dir);
}

static int agente_at_contar_ocorrencias(const char *texto, const char *agulha)
{
    if (!texto || !agulha || !*agulha) return 0;
    int c = 0;
    const char *p = texto;
    size_t n = strlen(agulha);
    while ((p = strstr(p, agulha))) { c++; p += n; }
    return c;
}

static int agente_at_substituir_uma(const char *path, const char *de, const char *para)
{
    char *orig = ler_arquivo(path);
    if (!orig) return -1;
    int qtd = agente_at_contar_ocorrencias(orig, de);
    if (qtd != 1) { free(orig); return qtd == 0 ? -2 : -3; }
    char *pos = strstr(orig, de);
    size_t a = (size_t)(pos - orig), nd = strlen(de), np = strlen(para ? para : ""), no = strlen(orig);
    char *novo = malloc(no - nd + np + 1);
    if (!novo) { free(orig); return -1; }
    memcpy(novo, orig, a);
    memcpy(novo + a, para ? para : "", np);
    strcpy(novo + a + np, pos + nd);
    int r = agente_at_escrever_texto(path, novo, 0);
    free(orig); free(novo);
    return r;
}

static int agente_at_executar_parallm(const char *prompt, char **saida_out)
{
    if (saida_out) *saida_out = NULL;
    char exe[PATH_MAX];
    ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
    if (n <= 0) return 1;
    exe[n] = '\0';
    char *slash = strrchr(exe, '/');
    if (!slash) return 1;
    strcpy(slash + 1, "parallm");

    int fd[2];
    if (pipe(fd) != 0) return 1;
    pid_t pid = fork();
    if (pid < 0) { close(fd[0]); close(fd[1]); return 1; }
    if (pid == 0) {
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);
        char conta_arg[PATH_MAX + 32];
        char *args[6];
        int pos = 0;
        args[pos++] = "agente-at";
        args[pos++] = (char *)prompt;
        args[pos++] = "--sem-heb";
        args[pos++] = "--nivel=0";
        const char *conta = getenv("SISC_CONTA_LLM");
        if (conta && *conta) {
            snprintf(conta_arg, sizeof(conta_arg), "--conta-llm=%s", conta);
            args[pos++] = conta_arg;
        }
        args[pos] = NULL;
        execv(exe, args);
        perror("agente-at: execv parallm");
        _exit(127);
    }
    close(fd[1]);
    size_t cap = 8192, len = 0;
    char *buf = malloc(cap);
    if (!buf) { close(fd[0]); waitpid(pid, NULL, 0); return 1; }
    char tmp[4096];
    ssize_t r;
    while ((r = read(fd[0], tmp, sizeof(tmp))) > 0) {
        if (len + (size_t)r + 1 > cap) {
            while (len + (size_t)r + 1 > cap) cap *= 2;
            char *nb = realloc(buf, cap);
            if (!nb) { free(buf); close(fd[0]); waitpid(pid, NULL, 0); return 1; }
            buf = nb;
        }
        memcpy(buf + len, tmp, (size_t)r);
        len += (size_t)r;
    }
    close(fd[0]);
    buf[len] = '\0';
    int st = 0;
    waitpid(pid, &st, 0);
    if (saida_out) *saida_out = buf; else free(buf);
    if (WIFEXITED(st)) return WEXITSTATUS(st);
    return 1;
}

static int agente_at_resolver_pp_path(const char *raiz, char *out, size_t tam)
{
    if (!out || tam == 0) return -1;
    out[0] = '\0';
    char cand[PATH_MAX * 2];

    if (raiz && *raiz) {
        snprintf(cand, sizeof(cand), "%s/../pp", raiz);
        if (access(cand, X_OK) == 0) { snprintf(out, tam, "%s", cand); return 0; }
        snprintf(cand, sizeof(cand), "%s/pp", raiz);
        if (access(cand, X_OK) == 0) { snprintf(out, tam, "%s", cand); return 0; }
    }

    char exe[PATH_MAX * 2];
    ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
    if (n > 0) {
        exe[n] = '\0';
        char *p = strstr(exe, "/core/comandos/");
        if (p) {
            *p = '\0';
            snprintf(cand, sizeof(cand), "%s/pp", exe);
            if (access(cand, X_OK) == 0) { snprintf(out, tam, "%s", cand); return 0; }
        }
    }

    if (access("./pp", X_OK) == 0) { snprintf(out, tam, "./pp"); return 0; }
    if (access("../pp", X_OK) == 0) { snprintf(out, tam, "../pp"); return 0; }
    return -1;
}

static int agente_at_exec_pp(const char *raiz, char *const pp_args[])
{
    char pp_path[PATH_MAX * 2];
    if (agente_at_resolver_pp_path(raiz, pp_path, sizeof(pp_path)) != 0) {
        fprintf(stderr, "agente-at: nao foi possivel localizar pp para pos-validacao.\n");
        return 127;
    }

    char *args[16];
    int pos = 0;
    args[pos++] = pp_path;
    for (int i = 0; pp_args && pp_args[i] && pos < 15; i++) args[pos++] = pp_args[i];
    args[pos] = NULL;

    pid_t pid = fork();
    if (pid < 0) return 1;
    if (pid == 0) {
        if (raiz && *raiz && chdir(raiz) != 0) _exit(127);
        execv(pp_path, args);
        perror("agente-at: execv pp");
        _exit(127);
    }
    int st = 0;
    if (waitpid(pid, &st, 0) < 0) return 1;
    if (WIFEXITED(st)) return WEXITSTATUS(st);
    return 1;
}

static void agente_at_relatorio_registrar_pos_validacoes(const char *run_dir, int rc_mapa, int rc_div)
{
    char rel_path[PATH_MAX * 2];
    snprintf(rel_path, sizeof(rel_path), "%s/relatorio-execucao.json", run_dir ? run_dir : ".");
    char *orig = ler_arquivo(rel_path);
    FILE *f = fopen(rel_path, "wb");
    if (!f) { free(orig); return; }

    if (orig) {
        char *fim = strrchr(orig, '}');
        if (fim) {
            fwrite(orig, 1, (size_t)(fim - orig), f);
            fputs(",\n", f);
        } else {
            fputs("{\n", f);
        }
    } else {
        fputs("{\n", f);
    }

    fputs("  \"posValidacoes\": [\n", f);
    fputs("    {\"comando\": ", f); json_escape(f, "pp --atualizar-manual-funcional");
    fprintf(f, ", \"codigo\": %d, \"ok\": %s},\n", rc_mapa, rc_mapa == 0 ? "true" : "false");
    fputs("    {\"comando\": ", f); json_escape(f, "pp --divergencia-sistema");
    fprintf(f, ", \"codigo\": %d, \"ok\": %s}\n", rc_div, rc_div == 0 ? "true" : "false");
    fputs("  ],\n", f);
    fputs("  \"statusFinal\": ", f); json_escape(f, (rc_mapa == 0 && rc_div == 0) ? "ok" : "erro-pos-validacao");
    fputs("\n}\n", f);
    fclose(f);
    free(orig);
}

static int agente_at_pos_alteracoes(const char *raiz, const char *run_dir)
{
    printf("agente-at: pós-atualização: executando pp --atualizar-manual-funcional\n");
    char *mapa_args[] = {"--atualizar-manual-funcional", NULL};
    int rc_mapa = agente_at_exec_pp(raiz, mapa_args);

    printf("agente-at: pós-atualização: executando pp --divergencia-sistema\n");
    char *div_args[] = {"--divergencia-sistema", NULL};
    int rc_div = agente_at_exec_pp(raiz, div_args);

    agente_at_relatorio_registrar_pos_validacoes(run_dir, rc_mapa, rc_div);
    printf("agente-at: pós-atualização concluída: mapa=%d divergencia-sistema=%d\n", rc_mapa, rc_div);
    return rc_mapa ? rc_mapa : rc_div;
}

static int agente_at_encontrar_array_operacoes(const char *plano, const char **ini, const char **fim)
{
    const char *p = strstr(plano, "\"operacoes\"");
    if (!p) p = strstr(plano, "\"operações\"");
    if (!p) return -1;
    p = strchr(p, '[');
    if (!p) return -1;
    int nivel = 0, em_string = 0, esc = 0;
    for (const char *q = p; *q; q++) {
        unsigned char c = (unsigned char)*q;
        if (em_string) {
            if (esc) { esc = 0; continue; }
            if (c == '\\') { esc = 1; continue; }
            if (c == '"') em_string = 0;
            continue;
        }
        if (c == '"') { em_string = 1; continue; }
        if (c == '[') nivel++;
        else if (c == ']') {
            nivel--;
            if (nivel == 0) { *ini = p + 1; *fim = q; return 0; }
        }
    }
    return -1;
}

static char *agente_at_proximo_objeto(const char **cursor, const char *fim)
{
    const char *p = *cursor;
    while (p < fim && *p != '{') p++;
    if (p >= fim) { *cursor = fim; return NULL; }
    int nivel = 0, em_string = 0, esc = 0;
    for (const char *q = p; q < fim; q++) {
        unsigned char c = (unsigned char)*q;
        if (em_string) {
            if (esc) { esc = 0; continue; }
            if (c == '\\') { esc = 1; continue; }
            if (c == '"') em_string = 0;
            continue;
        }
        if (c == '"') { em_string = 1; continue; }
        if (c == '{') nivel++;
        else if (c == '}') {
            nivel--;
            if (nivel == 0) { *cursor = q + 1; return agente_at_substr(p, q + 1); }
        }
    }
    *cursor = fim;
    return NULL;
}

static int agente_at_aplicar_plano(const char *raiz, const char *run_dir, const char *plano_json)
{
    const char *arr_ini = NULL, *arr_fim = NULL;
    if (agente_at_encontrar_array_operacoes(plano_json, &arr_ini, &arr_fim) != 0) {
        fprintf(stderr, "agente-at: plano sem array operacoes.\n");
        return 1;
    }

    char res_path[PATH_MAX * 2], rel_path[PATH_MAX * 2];
    snprintf(res_path, sizeof(res_path), "%s/resultados.tmp.json", run_dir);
    snprintf(rel_path, sizeof(rel_path), "%s/relatorio-execucao.json", run_dir);
    FILE *resf = fopen(res_path, "wb");
    if (!resf) return 1;
    fputs("[\n", resf);

    int total = 0, erros = 0, first = 1;
    const char *cur = arr_ini;
    char *obj;
    while ((obj = agente_at_proximo_objeto(&cur, arr_fim)) != NULL) {
        total++;
        char erro[1024] = "";
        char abs[PATH_MAX * 2] = "";
        char backup[PATH_MAX * 2] = "";
        int ok = 0;
        char *tipo = agente_at_json_str(obj, "tipo");
        char *caminho = agente_at_json_str(obj, "caminho");
        if (!caminho) caminho = agente_at_json_str(obj, "arquivo");
        if (!tipo || !*tipo) snprintf(erro, sizeof(erro), "tipo ausente");
        else if (!caminho || !*caminho) snprintf(erro, sizeof(erro), "caminho ausente");
        else if (agente_at_caminho_seguro(raiz, caminho, abs, sizeof(abs), erro, sizeof(erro)) != 0) {
            /* erro ja preenchido */
        } else if (!strcmp(tipo, "mkdir")) {
            free(tipo); tipo = strdup("criar_diretorio");
        }
        if (tipo && !strcmp(tipo, "write")) { free(tipo); tipo = strdup("escrever"); }
        if (tipo && !strcmp(tipo, "replace")) { free(tipo); tipo = strdup("substituir"); }
        if (tipo && !strcmp(tipo, "append")) { free(tipo); tipo = strdup("anexar"); }
        if (tipo && !strcmp(tipo, "delete")) { free(tipo); tipo = strdup("deletar"); }

        if (!erro[0] && tipo && !strcmp(tipo, "criar_diretorio")) {
            ok = agente_at_mkdir_p(abs) == 0;
            if (!ok) snprintf(erro, sizeof(erro), "falha ao criar diretorio");
        } else if (!erro[0] && tipo && !strcmp(tipo, "escrever")) {
            char *conteudo = agente_at_json_str(obj, "conteudo");
            if (!conteudo) conteudo = agente_at_json_str(obj, "content");
            if (!conteudo) snprintf(erro, sizeof(erro), "conteudo ausente");
            else {
                if (agente_at_backup(raiz, run_dir, abs, caminho, backup, sizeof(backup)) != 0) snprintf(erro, sizeof(erro), "falha ao criar backup");
                else { ok = agente_at_escrever_texto(abs, conteudo, 0) == 0; if (!ok) snprintf(erro, sizeof(erro), "falha ao escrever arquivo"); }
            }
            free(conteudo);
        } else if (!erro[0] && tipo && !strcmp(tipo, "anexar")) {
            char *conteudo = agente_at_json_str(obj, "conteudo");
            if (!conteudo) conteudo = agente_at_json_str(obj, "content");
            if (!conteudo) snprintf(erro, sizeof(erro), "conteudo ausente");
            else {
                if (agente_at_backup(raiz, run_dir, abs, caminho, backup, sizeof(backup)) != 0) snprintf(erro, sizeof(erro), "falha ao criar backup");
                else { ok = agente_at_escrever_texto(abs, conteudo, 1) == 0; if (!ok) snprintf(erro, sizeof(erro), "falha ao anexar arquivo"); }
            }
            free(conteudo);
        } else if (!erro[0] && tipo && !strcmp(tipo, "substituir")) {
            char *de = agente_at_json_str(obj, "procurar");
            if (!de) de = agente_at_json_str(obj, "oldText");
            if (!de) de = agente_at_json_str(obj, "de");
            char *para = agente_at_json_str(obj, "substituirPor");
            if (!para) para = agente_at_json_str(obj, "newText");
            if (!para) para = agente_at_json_str(obj, "para");
            if (!de || !*de || !para) snprintf(erro, sizeof(erro), "substituicao exige procurar/substituirPor");
            else if (agente_at_backup(raiz, run_dir, abs, caminho, backup, sizeof(backup)) != 0) snprintf(erro, sizeof(erro), "falha ao criar backup");
            else {
                int sr = agente_at_substituir_uma(abs, de, para);
                ok = sr == 0;
                if (sr == -2) snprintf(erro, sizeof(erro), "texto a substituir nao encontrado");
                else if (sr == -3) snprintf(erro, sizeof(erro), "texto a substituir ocorre mais de uma vez");
                else if (sr != 0) snprintf(erro, sizeof(erro), "falha ao substituir");
            }
            free(de); free(para);
        } else if (!erro[0] && tipo && !strcmp(tipo, "deletar")) {
            char *conf = agente_at_json_str(obj, "confirmacao");
            if (!conf || strcmp(conf, "DELETAR") != 0) snprintf(erro, sizeof(erro), "deletar exige confirmacao=DELETAR");
            else if (eh_dir(abs)) snprintf(erro, sizeof(erro), "deletar diretorio e proibido");
            else if (!eh_arq(abs)) ok = 1;
            else if (agente_at_backup(raiz, run_dir, abs, caminho, backup, sizeof(backup)) != 0) snprintf(erro, sizeof(erro), "falha ao criar backup");
            else { ok = unlink(abs) == 0; if (!ok) snprintf(erro, sizeof(erro), "falha ao deletar arquivo"); }
            free(conf);
        } else if (!erro[0]) {
            snprintf(erro, sizeof(erro), "tipo de operacao invalido: %s", tipo ? tipo : "");
        }

        if (!ok) erros++;
        if (!first) fputs(",\n", resf);
        first = 0;
        fprintf(resf, "  {\"indice\": %d, \"ok\": %s, \"tipo\": ", total, ok ? "true" : "false");
        json_escape(resf, tipo ? tipo : "");
        fputs(", \"caminho\": ", resf); json_escape(resf, caminho ? caminho : "");
        if (backup[0]) { fputs(", \"backup\": ", resf); json_escape(resf, backup); }
        if (erro[0]) { fputs(", \"erro\": ", resf); json_escape(resf, erro); }
        fputs("}", resf);
        free(tipo); free(caminho); free(obj);
    }
    fputs("\n]\n", resf);
    fclose(resf);

    char *resultados = ler_arquivo(res_path);
    FILE *rel = fopen(rel_path, "wb");
    if (rel) {
        fprintf(rel, "{\n  \"status\": \"%s\",\n  \"totalOperacoes\": %d,\n  \"erros\": %d,\n  \"resultados\": %s\n}\n",
                erros ? "erro" : "ok", total, erros, resultados ? resultados : "[]");
        fclose(rel);
    }
    free(resultados);
    unlink(res_path);
    printf("agente-at: operações aplicadas: %d/%d\n", total - erros, total);
    printf("agente-at: relatório: %s\n", rel_path);
    return erros ? 1 : 0;
}

static char *agente_at_montar_prompt(const char *raiz, const char *protocolo, const char *prompt_original, const char *resposta, const char *contexto)
{
    const char *base =
        "Você é o agente-at do SISC, um executor técnico autônomo.\n"
        "Converta o contexto recebido do --parallm em um plano executável de operações de arquivo.\n"
        "Arquivos criados pelo pp --sa são identificados pelo campo raiz único \"tipo\": \"arvore-sa\".\n"
        "Quando a resposta do --parallm descrever uma alteração em árvore --sa, opere somente arquivos que tenham esse identificador e gere uma operação segura sobre a árvore referenciada.\n\n"
        "Responda SOMENTE com JSON válido, sem markdown e sem texto fora do JSON.\n"
        "Formato obrigatório:\n"
        "{\"resumo\":\"...\",\"risco\":\"baixo|medio|alto\",\"operacoes\":["
        "{\"tipo\":\"criar_diretorio\",\"caminho\":\"dir/relativo\"},"
        "{\"tipo\":\"escrever\",\"caminho\":\"arquivo\",\"conteudo\":\"conteudo completo\"},"
        "{\"tipo\":\"substituir\",\"caminho\":\"arquivo\",\"procurar\":\"texto exato\",\"substituirPor\":\"novo texto\"},"
        "{\"tipo\":\"anexar\",\"caminho\":\"arquivo\",\"conteudo\":\"conteudo\"},"
        "{\"tipo\":\"deletar\",\"caminho\":\"arquivo\",\"confirmacao\":\"DELETAR\"}],\"validacoes\":[\"...\"]}\n\n"
        "Regras: use apenas caminhos relativos; nunca use /, .., .git, secretos, contas-llm, espaco ou sessoes; "
        "specs novas, specs hibridas e diretivas-persona-agente de uso do sistema so podem ser criadas/editadas em spec-diretivas/ na raiz do sistema atual; "
        "nunca crie specs em siscore/spec-diretivas interno, raiz local ou subdiretorios alternativos; "
        "se não houver segurança, retorne operacoes vazia.\n";
    char *po = agente_at_limitar_texto(prompt_original, AGENTE_AT_MAX_PROMPT_ORIGINAL, "promptOriginal");
    char *rp = agente_at_limitar_texto(resposta, AGENTE_AT_MAX_RESPOSTA_PARALLM, "respostaParallm");
    char *ch = agente_at_limitar_texto(contexto, AGENTE_AT_MAX_CONTEXTO_HEB, "contextoHeb");
    char *arvore_atual = agente_at_ler_arvore_referenciada(raiz, prompt_original, resposta);

    if (!po || !rp || !ch || !arvore_atual) { free(po); free(rp); free(ch); free(arvore_atual); return NULL; }
    size_t n = strlen(base) + strlen(raiz ? raiz : "") + strlen(protocolo ? protocolo : "") +
               strlen(po) + strlen(rp) + strlen(ch) + strlen(arvore_atual) + 4096;
    if (n > AGENTE_AT_MAX_PROMPT) n = AGENTE_AT_MAX_PROMPT;
    char *out = malloc(n);
    if (!out) { free(po); free(rp); free(ch); free(arvore_atual); return NULL; }
    snprintf(out, n,
        "%s\nRaiz do sistema: %s\n\n=== PROTOCOLO ===\n%s\n\n=== PROMPT ORIGINAL DO PARALLM ===\n%s\n\n=== RESPOSTA DO PARALLM ===\n%s\n\n=== ARVORE REFERENCIADA ATUAL ===\n%s\n\n=== CONTEXTO HEB/HRB RELEVANTE/TRUNCADO ===\n%s\n\nGere o JSON de operações agora.",
        base, raiz ? raiz : "", protocolo ? protocolo : "", po, rp, arvore_atual, ch);
    free(po); free(rp); free(ch); free(arvore_atual);
    return out;
}

typedef struct { char *buf; size_t len, cap; } AgStr;

static void agstr_init(AgStr *s)
{
    s->cap = 8192;
    s->len = 0;
    s->buf = malloc(s->cap);
    if (s->buf) s->buf[0] = '\0';
}

static int agstr_addn(AgStr *s, const char *txt, size_t n)
{
    if (!s || !s->buf) return -1;
    if (s->len + n + 1 > s->cap) {
        while (s->len + n + 1 > s->cap) s->cap *= 2;
        char *nb = realloc(s->buf, s->cap);
        if (!nb) return -1;
        s->buf = nb;
    }
    memcpy(s->buf + s->len, txt, n);
    s->len += n;
    s->buf[s->len] = '\0';
    return 0;
}

static int agstr_add(AgStr *s, const char *txt)
{
    return agstr_addn(s, txt ? txt : "", strlen(txt ? txt : ""));
}

static int agstr_addf(AgStr *s, const char *fmt, ...)
{
    va_list ap, ap2;
    va_start(ap, fmt);
    va_copy(ap2, ap);
    int n = vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);
    if (n < 0) { va_end(ap2); return -1; }
    char *tmp = malloc((size_t)n + 1);
    if (!tmp) { va_end(ap2); return -1; }
    vsnprintf(tmp, (size_t)n + 1, fmt, ap2);
    va_end(ap2);
    int r = agstr_add(s, tmp);
    free(tmp);
    return r;
}

static int agente_consultor_ident_seguro(const char *s)
{
    if (!s || !*s || strstr(s, "..") || strchr(s, '/') || strchr(s, '\\')) return 0;
    for (const char *p = s; *p; p++) {
        unsigned char c = (unsigned char)*p;
        if (!(isalnum(c) || c == '_' || c == '-' || c == '.')) return 0;
    }
    return 1;
}

static int agente_consultor_normalizar_conector(const char *in, char *out, size_t tam)
{
    if (!in || !*in || !out || tam == 0) return -1;
    char tmp[PATH_MAX];
    snprintf(tmp, sizeof(tmp), "%s", in);
    char *v = tmp;
    while (isspace((unsigned char)*v)) v++;
    if (!strncmp(v, "conectores/", 11)) v += 11;
    if (strchr(v, '/') || strchr(v, '\\') || strstr(v, "..")) return -1;
    size_t len = strlen(v);
    while (len > 0 && isspace((unsigned char)v[len - 1])) v[--len] = '\0';
    if (len >= 5 && !strcasecmp(v + len - 5, ".json")) v[len - 5] = '\0';

    size_t j = 0;
    int sep = 0;
    for (size_t i = 0; v[i] && j + 1 < tam; i++) {
        unsigned char c = (unsigned char)v[i];
        if (isalnum(c)) { out[j++] = (char)tolower(c); sep = 0; }
        else if (c == '-' || c == '_' || isspace(c)) {
            if (!sep && j > 0) out[j++] = '-';
            sep = 1;
        } else return -1;
    }
    while (j > 0 && out[j - 1] == '-') j--;
    out[j] = '\0';
    return out[0] ? 0 : -1;
}

static char *agente_consultor_json_objeto_raw(const char *j, const char *k)
{
    if (!j || !k) return NULL;
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", k);
    const char *p = strstr(j, pat);
    if (!p) return NULL;
    p = strchr(p + strlen(pat), ':');
    if (!p) return NULL;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '{') return NULL;
    const char *fim = json_objeto_fim(p);
    if (!fim) return NULL;
    return agente_at_substr(p, fim);
}

static int agente_consultor_resolver_spec(const char *raiz, const char *agente, const char *manifesto,
                                          char *path, size_t pathsz, char *rel, size_t relsz)
{
    char *dir = agente_consultor_json_objeto_raw(manifesto, "diretivaOrigem");
    char *r = dir ? json_str_intervalo(dir, dir + strlen(dir), "arquivo") : NULL;
    if (r && *r && r[0] != '/' && !strstr(r, "..") && !strchr(r, '\\')) {
        snprintf(rel, relsz, "%s", r);
        snprintf(path, pathsz, "%s/%s", raiz, r);
        free(dir); free(r);
        if (eh_arq(path)) return 0;
    }
    free(dir); free(r);

    char ag_alt[256];
    snprintf(ag_alt, sizeof(ag_alt), "%s", agente ? agente : "");
    for (char *p = ag_alt; *p; p++) if (*p == '-') *p = '_';
    snprintf(rel, relsz, "spec-diretivas/spec_%s.json", ag_alt);
    snprintf(path, pathsz, "%s/%s", raiz, rel);
    return eh_arq(path) ? 0 : -1;
}

static int agente_consultor_ext_texto(const char *nome)
{
    const char *p = strrchr(nome ? nome : "", '.');
    if (!p) return 0;
    return !strcasecmp(p, ".json") || !strcasecmp(p, ".php") || !strcasecmp(p, ".c") ||
           !strcasecmp(p, ".h") || !strcasecmp(p, ".md") || !strcasecmp(p, ".txt") ||
           !strcasecmp(p, ".html") || !strcasecmp(p, ".js") || !strcasecmp(p, ".css");
}

static int agente_consultor_coletar_arquivo(const char *abs, const char *rel, AgStr *ctx, size_t *orcamento)
{
    if (!agente_consultor_ext_texto(rel) || !orcamento || *orcamento == 0) return 0;
    struct stat st;
    if (stat(abs, &st) != 0 || !S_ISREG(st.st_mode)) return 0;
    if (st.st_size > 90000) {
        agstr_addf(ctx, "\n### %s\n[arquivo com %ld bytes omitido por limite]\n", rel, (long)st.st_size);
        return 0;
    }
    char *c = ler_arquivo(abs);
    if (!c) return 0;
    size_t n = strlen(c);
    if (n > *orcamento) n = *orcamento;
    agstr_addf(ctx, "\n### %s\n```\n", rel);
    agstr_addn(ctx, c, n);
    agstr_add(ctx, "\n```\n");
    *orcamento -= n;
    free(c);
    return 1;
}

static int agente_consultor_coletar_rec(const char *base_abs, const char *rel, AgStr *ctx, int prof, size_t *orcamento)
{
    if (prof > 5 || !orcamento || *orcamento == 0) return 0;
    char dir_abs[PATH_MAX * 2];
    snprintf(dir_abs, sizeof(dir_abs), "%s/%s", base_abs, rel ? rel : "");
    DIR *d = opendir(dir_abs);
    if (!d) return 0;
    int total = 0;
    struct dirent *e;
    while ((e = readdir(d)) != NULL && *orcamento > 0) {
        if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..") || e->d_name[0] == '.') continue;
        if (!strcmp(e->d_name, "secretos") || !strcmp(e->d_name, "espaco") || !strcmp(e->d_name, ".git")) continue;
        char rel_f[PATH_MAX * 2];
        if (rel && *rel) snprintf(rel_f, sizeof(rel_f), "%s/%s", rel, e->d_name);
        else snprintf(rel_f, sizeof(rel_f), "%s", e->d_name);
        char abs_f[PATH_MAX * 2];
        snprintf(abs_f, sizeof(abs_f), "%s/%s", base_abs, rel_f);
        struct stat st;
        if (lstat(abs_f, &st) != 0 || S_ISLNK(st.st_mode)) continue;
        if (S_ISDIR(st.st_mode)) total += agente_consultor_coletar_rec(base_abs, rel_f, ctx, prof + 1, orcamento);
        else if (S_ISREG(st.st_mode)) total += agente_consultor_coletar_arquivo(abs_f, rel_f, ctx, orcamento);
    }
    closedir(d);
    return total;
}

static int agente_consultor_resolver_conector(const char *raiz, const char *alvo, char *dir_out, size_t dirsz, char *nome_out, size_t nomesz)
{
    char nome[256];
    if (agente_consultor_normalizar_conector(alvo, nome, sizeof(nome)) != 0) return -1;
    char cand[PATH_MAX * 2];
    snprintf(cand, sizeof(cand), "%s/conectores/%s", raiz, nome);
    if (eh_dir(cand)) {
        snprintf(dir_out, dirsz, "%s", cand);
        snprintf(nome_out, nomesz, "%s", nome);
        return 0;
    }
    if (strncmp(nome, "conector-", 9) != 0) {
        snprintf(cand, sizeof(cand), "%s/conectores/conector-%s", raiz, nome);
        if (eh_dir(cand)) {
            snprintf(dir_out, dirsz, "%s", cand);
            snprintf(nome_out, nomesz, "conector-%s", nome);
            return 0;
        }
    }
    return -1;
}

static int agente_consultor_exec_pp_capture(const char *raiz, char *const pp_args[], char **saida_out)
{
    if (saida_out) *saida_out = NULL;
    char pp_path[PATH_MAX * 2];
    if (agente_at_resolver_pp_path(raiz, pp_path, sizeof(pp_path)) != 0) return 127;

    int fd[2];
    if (pipe(fd) != 0) return 1;
    pid_t pid = fork();
    if (pid < 0) { close(fd[0]); close(fd[1]); return 1; }
    if (pid == 0) {
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);
        if (raiz && *raiz && chdir(raiz) != 0) _exit(127);
        char *args[16];
        int pos = 0;
        args[pos++] = pp_path;
        for (int i = 0; pp_args && pp_args[i] && pos < 15; i++) args[pos++] = pp_args[i];
        args[pos] = NULL;
        execv(pp_path, args);
        _exit(127);
    }
    close(fd[1]);
    size_t cap = 8192, len = 0;
    char *buf = malloc(cap);
    if (!buf) { close(fd[0]); waitpid(pid, NULL, 0); return 1; }
    char tmp[4096];
    ssize_t r;
    while ((r = read(fd[0], tmp, sizeof(tmp))) > 0) {
        if (len + (size_t)r + 1 > cap) {
            while (len + (size_t)r + 1 > cap) cap *= 2;
            char *nb = realloc(buf, cap);
            if (!nb) { free(buf); close(fd[0]); waitpid(pid, NULL, 0); return 1; }
            buf = nb;
        }
        memcpy(buf + len, tmp, (size_t)r);
        len += (size_t)r;
    }
    close(fd[0]);
    buf[len] = '\0';
    int st = 0;
    waitpid(pid, &st, 0);
    if (saida_out) *saida_out = buf; else free(buf);
    if (WIFEXITED(st)) return WEXITSTATUS(st);
    return 1;
}

static int agente_consultor_exec_pp_stdin(const char *raiz, char *const pp_args[], const char *entrada)
{
    char pp_path[PATH_MAX * 2];
    if (agente_at_resolver_pp_path(raiz, pp_path, sizeof(pp_path)) != 0) return 127;

    int fd[2];
    if (pipe(fd) != 0) return 1;
    pid_t pid = fork();
    if (pid < 0) { close(fd[0]); close(fd[1]); return 1; }
    if (pid == 0) {
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        if (raiz && *raiz && chdir(raiz) != 0) _exit(127);
        char *args[16];
        int pos = 0;
        args[pos++] = pp_path;
        for (int i = 0; pp_args && pp_args[i] && pos < 15; i++) args[pos++] = pp_args[i];
        args[pos] = NULL;
        execv(pp_path, args);
        _exit(127);
    }
    close(fd[0]);
    const char *txt = entrada ? entrada : "";
    size_t n = strlen(txt);
    if (n > 0) (void)write(fd[1], txt, n);
    close(fd[1]);
    int st = 0;
    if (waitpid(pid, &st, 0) < 0) return 1;
    if (WIFEXITED(st)) return WEXITSTATUS(st);
    return 1;
}

static void agente_consultor_limpar_item(char *s)
{
    if (!s) return;
    for (char *p = s; *p; p++) if (*p == '\n' || *p == '\r' || *p == '\t') *p = ' ';
    char *ini = s;
    while (isspace((unsigned char)*ini)) ini++;
    if (ini != s) memmove(s, ini, strlen(ini) + 1);
    size_t n = strlen(s);
    while (n > 0 && isspace((unsigned char)s[n - 1])) s[--n] = '\0';
    if (n > 1000) { s[1000] = '\0'; }
}

static int agente_consultor_json_int(const char *obj, const char *chave, int padrao)
{
    if (!obj || !chave) return padrao;
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", chave);
    const char *p = strstr(obj, pat);
    if (!p) return padrao;
    p = strchr(p + strlen(pat), ':');
    if (!p) return padrao;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p == '"') p++;
    int v = atoi(p);
    return v > 0 ? v : padrao;
}

static int agente_consultor_encontrar_array(const char *json, const char *chave, const char **ini, const char **fim)
{
    if (!json || !chave || !ini || !fim) return -1;
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", chave);
    const char *p = strstr(json, pat);
    if (!p) return -1;
    p = strchr(p + strlen(pat), '[');
    if (!p) return -1;
    int nivel = 0, em_string = 0, esc = 0;
    for (const char *q = p; *q; q++) {
        unsigned char c = (unsigned char)*q;
        if (em_string) {
            if (esc) { esc = 0; continue; }
            if (c == '\\') { esc = 1; continue; }
            if (c == '"') em_string = 0;
            continue;
        }
        if (c == '"') { em_string = 1; continue; }
        if (c == '[') nivel++;
        else if (c == ']') {
            nivel--;
            if (nivel == 0) { *ini = p + 1; *fim = q; return 0; }
        }
    }
    return -1;
}

static int agente_consultor_sa_adicionar(const char *raiz, const char *arvore, const char *nome, const char *pai)
{
    char sa_arg[PATH_MAX * 2];
    char ag_arg[1400];
    char em_arg[128];
    snprintf(sa_arg, sizeof(sa_arg), "--sa=%s", arvore);
    snprintf(ag_arg, sizeof(ag_arg), "--ag=%s", nome ? nome : "");
    char *args[4];
    args[0] = sa_arg;
    args[1] = ag_arg;
    if (pai && *pai) {
        snprintf(em_arg, sizeof(em_arg), "--em=%s", pai);
        args[2] = em_arg;
        args[3] = NULL;
    } else {
        args[2] = NULL;
    }
    return agente_consultor_exec_pp_stdin(raiz, args, "s\n");
}

static int agente_consultor_criar_arvore_sa(const char *raiz, const char *arvore, const char *plano_json)
{
    const char *ini = NULL, *fim = NULL;
    if (agente_consultor_encontrar_array(plano_json, "itens", &ini, &fim) != 0) {
        fprintf(stderr, "agente-consultor: resposta sem array JSON 'itens'.\n");
        return 1;
    }

    int cont[16] = {0};
    char ids[16][128];
    memset(ids, 0, sizeof(ids));
    int total = 0, erros = 0;
    const char *cur = ini;
    char *obj;
    while ((obj = agente_at_proximo_objeto(&cur, fim)) != NULL) {
        int nivel = agente_consultor_json_int(obj, "nivel", 1);
        if (nivel < 1) nivel = 1;
        if (nivel > 8) nivel = 8;
        while (nivel > 1 && !ids[nivel - 1][0]) nivel--;

        char *nome = agente_at_json_str(obj, "nome");
        if (!nome) nome = agente_at_json_str(obj, "titulo");
        agente_consultor_limpar_item(nome);
        if (!nome || !*nome) { free(nome); free(obj); continue; }

        char pai[128] = "";
        if (nivel > 1) snprintf(pai, sizeof(pai), "%s", ids[nivel - 1]);
        cont[nivel]++;
        for (int i = nivel + 1; i < 16; i++) { cont[i] = 0; ids[i][0] = '\0'; }
        if (nivel == 1) snprintf(ids[nivel], sizeof(ids[nivel]), "%d", cont[nivel]);
        else {
            char novo_id[128];
            snprintf(novo_id, sizeof(novo_id), "%s.%d", ids[nivel - 1], cont[nivel]);
            snprintf(ids[nivel], sizeof(ids[nivel]), "%s", novo_id);
        }

        int rc = agente_consultor_sa_adicionar(raiz, arvore, nome, pai);
        if (rc != 0) {
            fprintf(stderr, "agente-consultor: falha ao adicionar item SA nivel %d: %s\n", nivel, nome);
            erros++;
        } else total++;
        free(nome);
        free(obj);
    }

    printf("agente-consultor: itens SA adicionados: %d\n", total);
    return erros ? 1 : 0;
}

static char *agente_consultor_montar_prompt(const char *agente, const char *conector, const char *manifesto,
                                            const char *spec_rel, const char *spec_json, const char *ctx_conector)
{
    char *persona = json_str_intervalo(manifesto, manifesto + strlen(manifesto), "persona");
    char *objetivo = json_str_intervalo(manifesto, manifesto + strlen(manifesto), "objetivo");
    char *spec_lim = agente_at_limitar_texto(spec_json, 90000, "spec");
    char *ctx_lim = agente_at_limitar_texto(ctx_conector, 140000, "conector");
    if (!spec_lim || !ctx_lim) { free(persona); free(objetivo); free(spec_lim); free(ctx_lim); return NULL; }

    const char *base =
        "Você é um agente consultor de conformidade derivado de spec do SISC.\n"
        "Você NÃO é construtor e NÃO deve aplicar alterações diretamente.\n"
        "Sua tarefa é analisar o conector alvo contra a spec/persona e gerar uma árvore SA em relspec/.\n"
        "As implementações serão feitas depois por GCs criados pelo operador na árvore.\n\n"
        "Responda SOMENTE JSON válido, sem markdown e sem texto fora do JSON.\n"
        "Formato obrigatório:\n"
        "{\"itens\":[{\"nivel\":1,\"nome\":\"...\"},{\"nivel\":2,\"nome\":\"...\"}]}\n\n"
        "Regras para itens:\n"
        "- Crie uma análise detalhada, objetiva e acionável.\n"
        "- Inclua resumo executivo, evidências analisadas, conformidades, lacunas, riscos, atualizações necessárias, testes/evidências de aceite e GCs sugeridos.\n"
        "- Cada item deve ser uma frase curta e autocontida; use níveis 1..4.\n"
        "- Não peça para editar diretamente; formule como pendências para operador/GC.\n"
        "- Não invente arquivos inexistentes; quando faltar evidência, declare a lacuna.\n";

    size_t n = strlen(base) + strlen(agente ? agente : "") + strlen(conector ? conector : "") +
               strlen(manifesto ? manifesto : "") + strlen(spec_rel ? spec_rel : "") + strlen(spec_lim) + strlen(ctx_lim) +
               strlen(persona ? persona : "") + strlen(objetivo ? objetivo : "") + 4096;
    char *out = malloc(n);
    if (!out) { free(persona); free(objetivo); free(spec_lim); free(ctx_lim); return NULL; }
    snprintf(out, n,
             "%s\n=== AGENTE ===\nNome: %s\nConector alvo: %s\nPersona: %s\nObjetivo: %s\n\n=== MANIFESTO DO AGENTE ===\n```json\n%s\n```\n\n=== SPEC DE ORIGEM (%s) ===\n```json\n%s\n```\n\n=== ARTEFATOS DO CONECTOR ===\n%s\n\nGere agora o JSON de itens para a árvore SA.",
             base, agente ? agente : "", conector ? conector : "", persona ? persona : "", objetivo ? objetivo : "",
             manifesto ? manifesto : "", spec_rel ? spec_rel : "", spec_lim, ctx_lim);
    free(persona); free(objetivo); free(spec_lim); free(ctx_lim);
    return out;
}

static int executar_agente_consultor(const char *raiz, const char *agente, const char *conector, const char *manifesto)
{
    if (!conector || !*conector) {
        fprintf(stderr, "agente-consultor: informe o conector no formato pp --agente=%s:<conector>.\n", agente ? agente : "");
        return 64;
    }

    char conector_dir[PATH_MAX * 2], conector_nome[256];
    if (agente_consultor_resolver_conector(raiz, conector, conector_dir, sizeof(conector_dir), conector_nome, sizeof(conector_nome)) != 0) {
        fprintf(stderr, "agente-consultor: conector nao encontrado em conectores/: %s\n", conector);
        return 66;
    }

    char spec_path[PATH_MAX * 2], spec_rel[PATH_MAX * 2];
    if (agente_consultor_resolver_spec(raiz, agente, manifesto, spec_path, sizeof(spec_path), spec_rel, sizeof(spec_rel)) != 0) {
        fprintf(stderr, "agente-consultor: spec de origem nao encontrada para agente %s.\n", agente ? agente : "");
        return 66;
    }
    char *spec_json = ler_arquivo(spec_path);
    if (!spec_json) return 66;

    AgStr ctx;
    agstr_init(&ctx);
    size_t orc = 170000;
    agstr_addf(&ctx, "Conector resolvido: %s\nDiretório: conectores/%s\n", conector_nome, conector_nome);
    char base_rel[PATH_MAX * 2];
    snprintf(base_rel, sizeof(base_rel), "conectores/%s", conector_nome);
    agente_consultor_coletar_rec(raiz, base_rel, &ctx, 0, &orc);

    char cat[PATH_MAX * 2], catrel[PATH_MAX * 2];
    snprintf(catrel, sizeof(catrel), "web-api/catalogo-%s.json", conector_nome);
    snprintf(cat, sizeof(cat), "%s/%s", raiz, catrel);
    if (eh_arq(cat) && orc > 0) agente_consultor_coletar_arquivo(cat, catrel, &ctx, &orc);

    char *sa_ctx = NULL;
    char ctx_arg[512];
    snprintf(ctx_arg, sizeof(ctx_arg), "--ctx-extrair=%s %s relspec conector", agente, conector_nome);
    char *ctx_args[] = {"--sa", ctx_arg, "--origem=sistema", "--max-itens=20", "--max-bytes=30000", "--include-ra", NULL};
    if (agente_consultor_exec_pp_capture(raiz, ctx_args, &sa_ctx) == 0 && sa_ctx && *sa_ctx) {
        agstr_add(&ctx, "\n=== CONTEXTO SA-CTX DO SISTEMA ===\n");
        agstr_add(&ctx, sa_ctx);
        agstr_add(&ctx, "\n");
    }
    free(sa_ctx);

    char *prompt = agente_consultor_montar_prompt(agente, conector_nome, manifesto, spec_rel, spec_json, ctx.buf ? ctx.buf : "");
    free(spec_json);
    free(ctx.buf);
    if (!prompt) return 70;

    char tarefas[PATH_MAX * 2];
    snprintf(tarefas, sizeof(tarefas), "%s/tarefas/agente-consultor/%ld-%s-%s", raiz, (long)time(NULL), agente, conector_nome);
    agente_at_mkdir_p(tarefas);
    char prompt_path[PATH_MAX * 2];
    snprintf(prompt_path, sizeof(prompt_path), "%s/prompt-analise.txt", tarefas);
    agente_at_escrever_texto(prompt_path, prompt, 0);

    printf("agente-consultor: analisando %s com agente %s...\n", conector_nome, agente);
    printf("agente-consultor: prompt salvo em %s\n", prompt_path);
    char *saida = NULL;
    int rc = agente_at_executar_parallm(prompt, &saida);
    free(prompt);
    char out_path[PATH_MAX * 2];
    snprintf(out_path, sizeof(out_path), "%s/llm-stdout.txt", tarefas);
    if (saida) agente_at_escrever_texto(out_path, saida, 0);
    if (rc != 0 || !saida) {
        fprintf(stderr, "agente-consultor: LLM falhou ao gerar análise (codigo %d).\n", rc);
        free(saida);
        return rc ? rc : 1;
    }

    char *plano = agente_at_extrair_objeto_json(saida);
    if (!plano) {
        fprintf(stderr, "agente-consultor: LLM nao retornou JSON de itens. Saida salva em %s\n", out_path);
        free(saida);
        return 65;
    }
    char plano_path[PATH_MAX * 2];
    snprintf(plano_path, sizeof(plano_path), "%s/itens-sa.json", tarefas);
    agente_at_escrever_texto(plano_path, plano, 0);

    char relspec_dir[PATH_MAX * 2];
    snprintf(relspec_dir, sizeof(relspec_dir), "%s/relspec", raiz);
    if (agente_at_mkdir_p(relspec_dir) != 0) {
        fprintf(stderr, "agente-consultor: nao foi possivel criar relspec/.\n");
        free(saida); free(plano);
        return 74;
    }

    char arvore[PATH_MAX];
    snprintf(arvore, sizeof(arvore), "relspec/%s--%s--%ld", agente, conector_nome, (long)time(NULL));
    rc = agente_consultor_criar_arvore_sa(raiz, arvore, plano);
    if (rc == 0) {
        printf("agente-consultor: árvore criada via pp --sa: %s.json\n", arvore);
        printf("agente-consultor: revise a árvore e crie GCs nela para aplicar atualizações.\n");
    }
    free(saida);
    free(plano);
    return rc;
}

static int executar_agente_at(const char *raiz, const char *arquivo_msg)
{
    if (!arquivo_msg || !*arquivo_msg) {
        fprintf(stderr, "agente-at: execução direta exige --ler-mensagem com contexto do parallm.\n");
        return 64;
    }
    char *msg_json = ler_arquivo(arquivo_msg);
    if (!msg_json) { fprintf(stderr, "agente-at: nao foi possivel ler mensagem.\n"); return 65; }

    char *prompt_original = agente_at_json_str(msg_json, "promptOriginal");
    char *resposta = agente_at_json_str(msg_json, "respostaParallm");
    if (!resposta) resposta = agente_at_json_str(msg_json, "resposta");
    char *contexto = agente_at_json_str(msg_json, "contextoHeb");
    char *mid = agente_at_json_str(msg_json, "mensagemId");
    char *proto = agente_at_extrair_objeto_json(strstr(msg_json, "\"_protocolo\"") ? strstr(msg_json, "\"_protocolo\"") : msg_json);

    if (!resposta || !*resposta) {
        fprintf(stderr, "agente-at: mensagem sem respostaParallm.\n");
        free(msg_json); free(prompt_original); free(resposta); free(contexto); free(mid); free(proto);
        return 65;
    }

    char sid[256];
    snprintf(sid, sizeof(sid), "%s", mid ? mid : "manual");
    for (char *p = sid; *p; p++) if (!isalnum((unsigned char)*p) && *p != '.' && *p != '_' && *p != '-') *p = '-';
    char run_dir[PATH_MAX * 2];
    snprintf(run_dir, sizeof(run_dir), "%s/tarefas/agente-at/%ld-%s", raiz, (long)time(NULL), sid);
    if (agente_at_mkdir_p(run_dir) != 0) { fprintf(stderr, "agente-at: nao foi possivel criar run dir.\n"); return 74; }

    char ctx_path[PATH_MAX * 2]; snprintf(ctx_path, sizeof(ctx_path), "%s/contexto-recebido.json", run_dir);
    FILE *ctx = fopen(ctx_path, "wb");
    if (ctx) {
        fputs("{\n  \"promptOriginal\": ", ctx); json_escape(ctx, prompt_original ? prompt_original : "");
        fputs(",\n  \"respostaParallm\": ", ctx); json_escape(ctx, resposta ? resposta : "");
        fputs(",\n  \"contextoHeb\": ", ctx); json_escape(ctx, contexto ? contexto : "");
        fputs("\n}\n", ctx); fclose(ctx);
    }
    printf("agente-at: contexto recebido em %s\n", ctx_path);

    char *prompt = agente_at_montar_prompt(raiz, proto ? proto : "", prompt_original, resposta, contexto);
    if (!prompt) return 70;
    char prompt_path[PATH_MAX * 2]; snprintf(prompt_path, sizeof(prompt_path), "%s/prompt-operacoes.txt", run_dir);
    agente_at_escrever_texto(prompt_path, prompt, 0);

    printf("agente-at: solicitando plano executável ao LLM mapeado para agente-at...\n");
    char *saida = NULL;
    int rc = agente_at_executar_parallm(prompt, &saida);
    free(prompt);
    char out_path[PATH_MAX * 2]; snprintf(out_path, sizeof(out_path), "%s/llm-stdout.txt", run_dir);
    if (saida) agente_at_escrever_texto(out_path, saida, 0);
    if (rc != 0 || !saida) {
        fprintf(stderr, "agente-at: parallm falhou ao gerar plano (codigo %d).\n", rc);
        free(msg_json); free(prompt_original); free(resposta); free(contexto); free(mid); free(proto); free(saida);
        return rc ? rc : 1;
    }

    char *plano = agente_at_extrair_objeto_json(saida);
    if (!plano) {
        fprintf(stderr, "agente-at: LLM nao retornou JSON de plano.\n");
        free(msg_json); free(prompt_original); free(resposta); free(contexto); free(mid); free(proto); free(saida);
        return 65;
    }
    char plano_path[PATH_MAX * 2]; snprintf(plano_path, sizeof(plano_path), "%s/plano-operacoes.json", run_dir);
    agente_at_escrever_texto(plano_path, plano, 0);
    rc = agente_at_aplicar_plano(raiz, run_dir, plano);

    if (rc == 0) {
        int pr = agente_at_pos_alteracoes(raiz, run_dir);
        if (pr == 0) agente_at_limpar_execucao_concluida(raiz, run_dir, sid);
        else rc = pr;
    }
    free(msg_json); free(prompt_original); free(resposta); free(contexto); free(mid); free(proto); free(saida); free(plano);
    return rc ? 74 : 0;
}

static int normalizar_agente_consultor_nome(const char *in, char *out, size_t tam)
{
    if (!in || !*in || !out || tam == 0) return -1;
    char tmp[PATH_MAX];
    snprintf(tmp, sizeof(tmp), "%s", in);
    char *v = tmp;
    while (isspace((unsigned char)*v)) v++;
    if (!strncmp(v, "agentes/", 8)) v += 8;
    if (strchr(v, '/') || strchr(v, '\\') || strstr(v, "..")) return -1;
    size_t len = strlen(v);
    while (len > 0 && isspace((unsigned char)v[len - 1])) v[--len] = '\0';
    if (len >= 5 && !strcasecmp(v + len - 5, ".json")) v[len - 5] = '\0';
    size_t j = 0;
    int sep = 0;
    for (size_t i = 0; v[i] && j + 1 < tam; i++) {
        unsigned char c = (unsigned char)v[i];
        if (isalnum(c)) { out[j++] = (char)tolower(c); sep = 0; }
        else if (c == '_' || c == '-' || isspace(c)) {
            char s = c == '-' ? '-' : '_';
            if (!sep && j > 0) out[j++] = s;
            sep = 1;
        } else return -1;
    }
    while (j > 0 && (out[j - 1] == '_' || out[j - 1] == '-')) j--;
    out[j] = '\0';
    return out[0] ? 0 : -1;
}

static int parse_agente_arg(const char *arg, char *agente, size_t agsz, char *alvo, size_t alvosz)
{
    if (!arg || !*arg) return -1;
    alvo[0] = '\0';
    const char *dois = strchr(arg, ':');
    if (!dois) return normalizar_nome(arg, agente, agsz);

    char a[256], t[256];
    size_t na = (size_t)(dois - arg);
    if (na == 0 || na >= sizeof(a)) return -1;
    memcpy(a, arg, na);
    a[na] = '\0';
    snprintf(t, sizeof(t), "%s", dois + 1);
    if (normalizar_agente_consultor_nome(a, agente, agsz) != 0) return -1;
    if (!agente_consultor_ident_seguro(agente)) return -1;
    if (agente_consultor_normalizar_conector(t, alvo, alvosz) != 0) return -1;
    return 0;
}

int main(int argc, char **argv)
{
    if (restricao_verificar("agente") != 0) return 1;

    if (argc < 2) { uso(); return 1; }
    if (arg_ajuda(argv[1])) { uso(); return 0; }

    char agente[256], alvo_conector[256], msg[PATH_MAX];
    agente[0] = alvo_conector[0] = msg[0] = '\0';
    if (parse_agente_arg(argv[1], agente, sizeof(agente), alvo_conector, sizeof(alvo_conector)) != 0) {
        fprintf(stderr, "agente: nome de agente invalido.\n");
        return 1;
    }

    for (int i = 2; i < argc; i++) {
        if (arg_ajuda(argv[i])) {
            if (!strcmp(agente, "agente")) { uso(); return 0; }
            if (!strcmp(agente, "agente-at")) {
                if (!uso_agente_especifico(agente)) return 1;
            } else {
                printf("Uso:\n  pp --agente=%s:<conector>\n\n", agente);
                printf("Agente consultor derivado de spec. Use pp --sincronizar-agente=%s para criar/atualizar o manifesto.\n", agente);
                printf("A execução gera uma árvore via pp --sa em relspec/ e não aplica alterações diretamente.\n");
            }
            return 0;
        }
    }

    for (int i = 2; i < argc; i++) {
        if (!strncmp(argv[i], "--ler-mensagem=", 15)) {
            snprintf(msg, sizeof(msg), "%s", argv[i] + 15);
        } else if (!strcmp(argv[i], "--ler-mensagem")) {
            if (i + 1 >= argc) { fprintf(stderr, "agente-at: --ler-mensagem exige arquivo.\n"); return 1; }
            snprintf(msg, sizeof(msg), "%s", argv[++i]);
        }
    }

    char raiz[PATH_MAX];
    if (!getcwd(raiz, sizeof(raiz))) { perror("agente: getcwd"); return 1; }

    char json_path[PATH_MAX * 2];
    if (resolver_agente_json(raiz, agente, json_path, sizeof(json_path)) != 0) {
        if (!strcmp(agente, "agente-at"))
            fprintf(stderr, "agente: arquivo obrigatorio nao encontrado: agentes/%s.json (nem na biblioteca core/biblioteca-agentes-fixos/)\n", agente);
        else
            fprintf(stderr, "agente: consultor nao encontrado: agentes/%s.json (rode pp --sincronizar-agente=%s)\n", agente, agente);
        return 1;
    }

    char *json = ler_arquivo(json_path);
    if (!json) {
        fprintf(stderr, "agente: nao foi possivel ler manifesto: %s\n", json_path);
        return 1;
    }
    int llm_rc = agente_configurar_llm_do_manifesto(json, agente);
    if (llm_rc < 0) { free(json); return 1; }
    if (llm_rc == 0) {
        fprintf(stderr, "agente: manifesto de %s sem llm.arquivoConfiguracao; conta LLM do agente nao garantida.\n", agente);
        free(json);
        return 1;
    }

    printf("Agente carregado: %s\n", json_path);

    if (!strcmp(agente, "agente-at")) {
        if (!msg[0]) {
            fprintf(stderr, "agente-at: execução direta exige --ler-mensagem <arquivo>.\n");
            free(json);
            return 1;
        }
        free(json);
        return executar_agente_at(raiz, msg);
    }

    if (msg[0]) {
        fprintf(stderr, "agente-consultor: --ler-mensagem é exclusivo do agente-at. Use pp --agente=%s:<conector>.\n", agente);
        free(json);
        return 1;
    }
    if (!alvo_conector[0]) {
        fprintf(stderr, "agente-consultor: informe o alvo no formato pp --agente=%s:<conector>.\n", agente);
        free(json);
        return 1;
    }

    int rc = executar_agente_consultor(raiz, agente, alvo_conector, json);
    free(json);
    return rc;
}
