/** * Agent CLI Adapter — executa nós do grafo delegando a um agente de codificação * JÁ INSTALADO E AUTENTICADO na máquina (hoje: `claude`, o Claude Code CLI), * em modo print (não interativo), por subprocesso. * * POR QUE ISSO EXISTE * ------------------- * Antes deste adapter, `izanagi run` sem API key caía em * `createHeadlessProducer`: o grafo era planejado de verdade, roteado de * verdade, verificado de verdade — e os nós eram SIMULADOS. O framework * orquestrava, mas não executava trabalho. Quem não quer colar uma chave de * API nem subir um modelo local ficava com um planejador, não com um runtime. * * O agente de codificação que a pessoa já usa no terminal resolve isso: ele já * está autenticado (assinatura/OAuth do próprio CLI), já roda local, e expõe * modo não interativo com telemetria de uso. Izanagi não precisa de chave * nenhuma — precisa de um executor. Este é o executor de custo zero de setup. * * NÃO é "mais um provider de LLM": é uma camada de PROCESSO. A diferença * prática está no que o CLI aceita e o que uma API HTTP não aceita: * * Izanagi flag do `claude` * ------------------------- -------------------------------- * modelo do papel --model * teto de custo do nó --max-budget-usd * política de tools --restricted / --tools * escrita (opt-in explícito) --permission-mode acceptEdits * sem estado residual --no-session-persistence / --strict-mcp-config * telemetria real --output-format json (usage + total_cost_usd) * * O custo volta MEDIDO pelo próprio CLI (`total_cost_usd`), não estimado por * tabela de preço — é a única superfície do runtime onde o custo é fato. * * DECISÕES QUE PARECEM DETALHE E NÃO SÃO * -------------------------------------- * 1. Prompt vai por STDIN, nunca por argv. O system prompt de um nó com chain * de skills passa de 30KB; o limite de linha de comando do Windows é 32767 * caracteres para o comando inteiro. Por argv, o run quebraria justamente * nos nós mais ricos. Por stdin não existe limite prático. * * 2. Default é `--restricted --tools ""`: nenhuma tool, nenhuma execução de * comando, nenhum settings do projeto carregado. Medido nesta máquina, isso * derruba o system prompt do CLI de 20848 para 4095 tokens de entrada * (US$ 0,042 -> US$ 0,005 na mesma pergunta). Além de mais barato é mais * determinístico e mais seguro: o produtor de artefato de um nó não precisa * de shell. Quem quiser grounding no repositório liga tools de leitura; * quem quiser escrita precisa pedir escrita. * * 3. `spawn` sem shell, argv em array: o objetivo do usuário entra como dado, * nunca como texto interpretado por um shell. * * 4. Guarda de profundidade: um `izanagi run` disparado DE DENTRO de uma sessão * do agente pode spawnar o agente (profundidade 0 -> 1). O que não pode é a * recursão continuar. Estourado o teto, o adapter fica `configured: false` e * o runtime degrada para headless com aviso, em vez de estourar no meio do * grafo. */ import type { CompletionOptions, CompletionResult, ModelAdapter } from './client.js'; /** Teto de profundidade de recursão (agente -> izanagi -> agente -> ...). */ export declare const DEFAULT_MAX_DEPTH = 1; /** * Timeout por chamada. Um agente de codificação em modo print pode pensar por * bem mais tempo que um POST de chat completion (ele tem loop de raciocínio * próprio), então o default é maior que o do cliente HTTP e tem env separada. */ export declare const DEFAULT_TIMEOUT_MS = 300000; /** Teto de stdout acumulado (proteção de memória contra saída patológica). */ export declare const MAX_STDOUT_BYTES: number; /** * Custo fixo de tokens que o CLI hospedeiro cobra por chamada, MEDIDO nesta * máquina com `--restricted --tools ""` (o default do adapter): 4095 tokens de * entrada só do system prompt do próprio agente, antes de qualquer instrução * do Izanagi. Com as customizações do projeto carregadas o número foi 20848. * * O runtime precisa desse número porque o orçamento de um nó não é só o * prompt que o Izanagi escreve: um `--budget` pequeno estoura no primeiro nó e * o usuário lê "orçamento da fase execution esgotado" sem saber por quê. */ export declare const AGENT_CLI_OVERHEAD_TOKENS = 4095; /** * Unidade de planejamento: quanto custa UM nó neste executor. * * Medido nesta máquina em 2026-09-10, com `--agent-tools none`: * * | Caso | Tokens do nó | * |---------------------------------------------------|--------------| * | primeira medição, nó isolado | ~18.000 | * | modo `direct` (três execuções do mesmo objetivo) | 19.799 · 20.706 · 20.114 | * | modo `orchestrated`, nó de specialist com survey no contexto | ~26.500 (106.140 em 4 chamadas) | * | idem, com retentativa | ~29.400 (147.140 em 5 chamadas) | * * O valor é o do TOPO da faixa, e não a média das medições, porque o erro não é * simétrico: um teto folgado não gasta um token a mais (o gasto é o que o nó * consome, e quem limita dinheiro é `--max-cost`, cobrado sobre custo MEDIDO), * enquanto um teto curto aborta o run depois de todo o custo já ter sido pago. * Foi exatamente o que aconteceu duas vezes com 18.000: um run orchestrated * produziu cinco artefatos válidos (7,4KB + 13,7KB + 23,7KB) e terminou sem * gravar arquivo nenhum. * * O nó do modo `direct` é mais barato que isto, e tudo bem: o teto dele sobra. * Dimensionar pelo caso mais barato é que quebraria o caso mais caro. */ export declare const AGENT_CLI_TOKENS_PER_NODE = 30000; /** * Menor fatia que a fase `execution` recebe do teto do run, derivada dos * próprios pesos do alocador. * * O piso usa a MENOR das complexidades, e não a do meio: um piso que só vale * para uma complexidade é um piso que falha nas outras duas, e falhar por * orçamento mal declarado é o pior tipo de falha, porque parece falha do * modelo. */ export declare const MIN_EXECUTION_PHASE_SHARE: number; /** O mesmo para a fase que paga as retentativas. */ export declare const MIN_RECOVERY_PHASE_SHARE: number; /** Tokens que um nó precisa ter disponível para caber, já com a folga. */ export declare function nodeCostWithHeadroom(policy: AgentCLIToolPolicy): number; /** * Folga sobre o consumo MEDIDO. * * A medição é de execuções observadas, e a próxima pode ser maior: o gasto de * um nó varia com o tamanho da resposta e com quanto contexto o Context * Resolver injetou. A folga cobre essa variação sem que a constante precise ser * reescrita a cada run — que é o erro que esta linha já cometeu uma vez, quando * o piso era a média medida e reprovava metade das execuções. */ export declare const AGENT_CLI_VARIANCE_HEADROOM = 1.3; /** * Piso de `--budget` recomendado para um run de um nó neste executor, sem * tools: consumo medido, mais folga de variância, dividido pela menor fatia da * fase `execution`, arredondado para o milhar. */ export declare const AGENT_CLI_MIN_RECOMMENDED_BUDGET: number; /** * O mesmo nó com `--agent-tools read`, MEDIDO: 47,4k de entrada e 20,3k de * saída (67,7k no total). Ler o repositório multiplica o custo por ~3,8x * porque o agente faz várias voltas de tool antes de responder. * * O número existe para que o aviso de orçamento seja honesto por política em * vez de citar um único valor que só vale para o caso sem tools. */ export declare const AGENT_CLI_TOKENS_PER_NODE_WITH_TOOLS = 68000; /** Piso recomendado por política de tools (mesma conta do caso sem tools). */ export declare const AGENT_CLI_MIN_BUDGET_WITH_TOOLS: number; /** Piso de `--budget` recomendado para um nó, dada a política de tools. */ export declare function recommendedBudget(policy: AgentCLIToolPolicy): number; /** Tokens medidos de um nó, dada a política de tools. */ export declare function measuredTokensPerNode(policy: AgentCLIToolPolicy): number; /** Política de tools do subprocesso. */ export type AgentCLIToolPolicy = 'none' | 'read' | 'write'; /** Tools de leitura usadas em `policy: 'read'` (grounding no repositório real). */ export declare const READ_TOOLS: string[]; /** Tools de `policy: 'write'`: leitura + escrita de arquivo. Sem Bash, de propósito. */ export declare const WRITE_TOOLS: string[]; /** Profundidade atual da cadeia de subprocessos de agente. */ export declare function currentDepth(env?: NodeJS.ProcessEnv): number; /** * Política de tools declarada pelo ambiente. * * `write` NÃO é acessível por acidente: exige o valor literal `write`, porque * é o único valor que autoriza o subprocesso a alterar arquivos do projeto. */ export declare function toolPolicyFromEnv(env?: NodeJS.ProcessEnv): AgentCLIToolPolicy; /** * Dentro de um test runner o executor fica DESLIGADO por padrão. * * Sem isto, qualquer teste que chame a CLI em processo (`cli.test`, * `cli-hitl.test`) passaria a spawnar o agente de verdade e a suíte gastaria * cota real da assinatura de quem rodou `npm test` — medido: um único teste de * compatibilidade de flag saiu de milissegundos para 28 segundos de chamada de * modelo. O default seguro é o inverso: teste roda headless. * * `IZANAGI_AGENT_CLI_IN_TESTS=1` libera, para quem quer justamente um teste de * integração contra o CLI instalado. */ export declare function isSuppressedInTests(env?: NodeJS.ProcessEnv): boolean; /** * Procura um executável no PATH sem shell e sem `which`/`where` (spawnar um * processo só para descobrir se dá para spawnar outro é caro e, no Windows, * depende de qual shell atende). * * No Windows testa as extensões do PATHEXT: `claude` no disco é `claude.exe`, * e `fs.existsSync('claude')` daria falso negativo. */ export declare function findExecutable(bin: string, env?: NodeJS.ProcessEnv): string | null; /** Requisição normalizada que o spec traduz para argv do CLI concreto. */ export interface AgentCLIRequest { model: string; /** Instruções de operação do nó (system prompt compilado pelo Izanagi). */ system?: string; /** Mensagem do usuário/objetivo. */ prompt: string; toolPolicy: AgentCLIToolPolicy; /** Teto de custo desta chamada, em USD. Ausente = sem teto no subprocesso. */ maxCostUsd?: number; /** Agente nativo do host a assumir a sessão (passthrough opcional). */ agent?: string; } /** Resposta normalizada extraída do stdout do CLI. */ export interface AgentCLIResponse { text: string; /** Tokens realmente consumidos (entrada + criação/leitura de cache + saída). */ tokens: number; /** Tokens servidos do cache de prompt, quando o CLI reporta. */ cachedTokens?: number; /** Custo medido pelo próprio CLI, em USD. */ costUsd?: number; /** Modelo que o CLI de fato usou, quando reportado. */ model?: string; } export interface AgentCLISpec { /** Id do provider no ModelRouter/LLMClient (ex.: `claude-cli`). */ readonly provider: string; /** Nome do executável no PATH (sem extensão). */ readonly bin: string; /** Nome legível para mensagens ao usuário. */ readonly label: string; /** Como instalar/autenticar, citado quando o binário não é encontrado. */ readonly install: string; /** * CLIs conhecidos podem ser detectados sem que Izanagi finja conhecer seu * protocolo de execução. Specs discovery-only aparecem no diagnóstico, mas * nunca entram no roteamento nem são marcados como disponíveis. */ readonly executionSupported?: boolean; buildArgs(req: AgentCLIRequest): string[]; /** Texto enviado por stdin (prompt e, se necessário, o system embutido). */ buildStdin(req: AgentCLIRequest): string; /** Lança `Error` quando o próprio CLI reportou falha. */ parse(stdout: string): AgentCLIResponse; } /** * Delimitadores do bloco de instruções enviado por stdin. * * O system prompt do Izanagi não cabe em argv (ver cabeçalho), e o CLI não lê * system por stdin. A saída é mandar o bloco marcado no corpo da mensagem e * usar `--append-system-prompt` (curto, cabe em argv) apenas para dizer ao * agente COMO tratar esse bloco. Assim a instrução continua tendo estatuto de * instrução, sem depender de flag não documentada. */ export declare const SYSTEM_OPEN = ""; export declare const SYSTEM_CLOSE = ""; export declare const TASK_OPEN = ""; export declare const TASK_CLOSE = ""; /** * Marca de falha do EXECUTOR (e não do trabalho): o processo terminou sem * chegar à API. Quem carrega o texto é a mensagem de erro, e quem a lê é o * classificador do healing. */ export declare const EXECUTOR_UNAVAILABLE = "executor indispon\u00EDvel"; /** * Traduz a saída de um CLI que terminou com código diferente de zero. * * Antes, 400 caracteres crus do stdout viravam a mensagem de erro do nó. Numa * recusa do CLI hospedeiro isso é o envelope JSON de telemetria: o usuário lia * `saiu com código 1: {"duration_api_ms":0,"stop_reason":"stop_sequence",...}` * e não tinha como saber que nada foi executado. * * O sinal está no próprio envelope: `duration_api_ms` zero com uso zero * significa que o processo nem falou com a API — quota, autenticação ou * indisponibilidade. Isso é falha de EXECUTOR, não do trabalho do nó, e a * diferença importa porque retentar não conserta a primeira e pode consertar a * segunda. */ export declare function describeFailure(code: number | null, stderr: string, stdout: string): string; /** Contrato de operação passado em argv (curto por necessidade). */ export declare const CLAUDE_CONTRACT: string; /** * Acréscimo ao contrato quando o subprocesso roda SEM tool nenhuma. * * Medido em 2026-09-10, num nó `security-report` com `--agent-tools none`: o * artefato gravado foram onze linhas imitando uma transcrição de tool-calls * (`**Tool Call: rg -il "jwt"**`, `Status: Completed`, `Terminal:` e uma lista * de arquivos), citando `src/runtime/security/tokens.ts` e * `webhookSecurity.ts` — dois arquivos que não existem neste repositório. Sem * tools, o modelo não pode ler nada; o que ele fez foi ENCENAR a leitura. * * O aviso é preventivo e não substitui a detecção: `validateArtifact` reprova * a transcrição encenada de qualquer forma, porque um contrato no prompt é um * pedido, e um artefato entregue é um fato. */ export declare const NO_TOOLS_CONTRACT: string; /** Contrato completo para uma política de tools. */ export declare function contractFor(policy: AgentCLIToolPolicy): string; export declare const claudeCLISpec: AgentCLISpec; /** * CLIs conhecidos são listados para diagnóstico e roteamento honesto. A * presença do binário não autoriza um adapter inventado: até existir um * contrato de stdout/flags testado, o estado permanece unsupported. */ export declare const KNOWN_AGENT_CLI_SPECS: AgentCLISpec[]; /** Specs conhecidos, por id de provider. */ export declare const AGENT_CLI_SPECS: Record; /** Ids de providers com adapter real; entram no runtime e no roteamento. */ export declare const AGENT_CLI_PROVIDERS: string[]; /** Ids conhecidos apenas para diagnóstico de instalação/capability. */ export declare const KNOWN_AGENT_CLI_PROVIDERS: string[]; /** CLIs realmente executáveis e disponíveis, em ordem configurável. */ export declare function availableAgentCLIProviders(env?: NodeJS.ProcessEnv): string[]; export interface SpawnResult { stdout: string; stderr: string; code: number | null; timedOut: boolean; aborted: boolean; } /** Executa o binário com argv em array (sem shell) e stdin fechado ao fim. */ export declare function runAgentCLI(bin: string, args: string[], stdin: string, opts: { cwd?: string; timeoutMs: number; signal?: AbortSignal; env?: NodeJS.ProcessEnv; }): Promise; /** * Adapter de um CLI de agente. Implementa `ModelAdapter`, então entra no * `LLMClient` como qualquer provider e atravessa run/SDK/models/juiz/arena sem * que nenhum chamador mude. */ export declare class AgentCLIAdapter implements ModelAdapter { readonly provider: string; private readonly spec; private readonly env; private readonly cwd; private resolvedBin; constructor(spec: AgentCLISpec, opts?: { env?: NodeJS.ProcessEnv; cwd?: string; }); /** Caminho absoluto do binário, memoizado (`null` = não está no PATH). */ get binPath(): string | null; /** * `true` quando o binário existe, o adapter não foi desligado e a * profundidade de recursão ainda cabe. * * Não verifica autenticação: não existe checagem barata e offline disso, e * spawnar o agente só para perguntar "você está logado?" custaria uma * chamada de modelo por run. Falha de auth aparece como erro real do CLI na * primeira chamada, com o stderr dele na mensagem. */ get configured(): boolean; /** Motivo de não estar utilizável, para mensagem ao usuário. `null` = utilizável. */ get unavailableReason(): string | null; complete(opts: CompletionOptions): Promise; } /** Adapters de CLI de agente conhecidos, prontos para o `LLMClient`. */ export declare function defaultAgentCLIAdapters(opts?: { env?: NodeJS.ProcessEnv; cwd?: string; }): AgentCLIAdapter[]; /** * Diagnóstico dos executores sem chave, para `izanagi doctor`/`models`. * Determinístico e offline: só olha PATH, env e profundidade. */ export declare function agentCLIStatus(env?: NodeJS.ProcessEnv): Array<{ provider: string; label: string; bin: string; path: string | null; available: boolean; reason: string | null; install: string; }>; /** * Diretório temporário do adapter. Existe para os specs que precisarem de * arquivo auxiliar; o spec do Claude não precisa (tudo vai por stdin/argv). */ export declare function tempDir(): string; //# sourceMappingURL=agent-cli.d.ts.map