/** * Commander (LEVEL 0): o cérebro executivo do runtime. * * Recebe um objetivo, classifica complexidade e domínios, escolhe o MODO de * execução (direct/assisted/orchestrated/autonomous), gera um Task Contract * por tarefa com critérios de aceite verificáveis, monta o Task Graph e * estima custo antes de qualquer execução. * * O Commander NÃO executa tarefa: ele decide e delega. E é determinístico por * padrão (regra "Deterministic Core"): classificação, modo, contratos, * critérios e estimativa saem de heurísticas e dos schemas de artefato já * existentes, sem nenhuma chamada de modelo. Uma decomposição assistida por * LLM pode ser injetada via `decompose`, mas o resultado passa por validação * determinística e cai no template quando não conforma. * * Ganho central: antes, TODA tarefa (inclusive "converta 10 dólares para * reais") virava um grafo de 3 a 9 nós com avaliação e crítica. Agora o modo * é proporcional ao problema. */ import type { ExecutionGraph } from '../types.js'; import { ExecutionGraphBuilder } from './graph.js'; import { Planner } from './planner.js'; import { type AcceptanceCriterion, type AgentRole, type ExecutionMode, type TaskContract } from '../contracts/task-contract.js'; import type { AgentCapabilityRegistry } from '../registry/capabilities.js'; import { type Domain } from './domains.js'; export type { Domain } from './domains.js'; export interface Classification { /** 1 (trivial) a 5 (projeto inteiro). */ complexity: 1 | 2 | 3 | 4 | 5; domains: Domain[]; /** Categoria legada usada pelos templates do Planner. */ category: string; reasoning: 'low' | 'medium' | 'high'; /** Risco em [0,1]: alto quando o objetivo toca segurança/dados sensíveis. */ risk: number; reasons: string[]; } /** * Classifica o objetivo. Determinístico e barato: nenhuma chamada de modelo, * então classificar não custa token nenhum. */ export declare function classify(objective: string): Classification; /** * Escolhe o modo proporcional ao problema. Override explícito sempre vence. * * `hints.knownFailures` é o sinal da memória: quando o runtime já falhou antes * em algo parecido, o problema se mostrou mais difícil do que a classificação * léxica sugere, e o modo sobe UM degrau. Um degrau só — memória é evidência * de dificuldade, não licença para gastar o modo mais caro. */ export declare function decideMode(classification: Classification, override?: ExecutionMode, hints?: { knownFailures?: number; }): { mode: ExecutionMode; reason: string; }; /** * Deriva critérios de aceite do SCHEMA REAL do artefato esperado * (`contracts/artifacts.ts`), não de texto inventado: campos obrigatórios * viram checks `contains`, `minSize` vira `min-size`, proibições viram * `not-contains`. Se o schema muda, os critérios acompanham. */ export declare function acceptanceForKind(nodeId: string, kind: string): AcceptanceCriterion[]; export interface CommanderInput { objective: string; /** Override de modo (CLI `--mode`). */ mode?: ExecutionMode; /** Agente pedido explicitamente pelo usuário (`izanagi run architect ...`). */ agent?: string; /** Chain de skills já resolvida pelo chamador (compatibilidade com a CLI atual). */ skillChain?: string[]; /** Teto global de tokens do run. */ maxTokens?: number; /** * Piso de tokens por nó que CONSOME modelo, imposto pelo executor. * * `budgetForMode` foi calibrado quando um nó era uma requisição HTTP com * prompt curto. O executor por CLI de agente tem um custo fixo por chamada * (o system prompt do próprio CLI hospedeiro, cobrado sempre) e um nó dele * foi MEDIDO em ~18.000 tokens sem tools. Contra o teto de 2.000 do modo * `direct`, todo run pelo caminho sem API key estourava no primeiro nó e * terminava em `HUMAN_REQUIRED` sem gravar entrega: o comando mais básico do * framework não entregava nada. * * Isto NÃO é o teto: é o piso. `maxTokens` explícito do usuário continua * vencendo, porque um teto que o usuário declarou é uma decisão dele (e a * CLI já avisa quando está abaixo do piso medido). */ minTokensPerNode?: number; /** * Tokens que o teto do run precisa ter para que UMA retentativa caiba na fase * `recovery`, quando o modo permite retentar. * * O piso por nó dimensiona a fase `execution`. A retentativa é cobrada de * outra fase, com outra fatia, e sem esta segunda conta o healing era * decorativo: medido num run orchestrated real, `recovery` fechou em * 16.420/16.420 tentando reexecutar um nó de ~20.000 tokens. O runtime * decidia curar, tentava, e morria no orçamento antes de chamar o modelo. */ minTokensPerRetry?: number; /** Teto global de custo em USD: quando a estimativa estoura, o modo degrada. */ maxCostUsd?: number; /** Registro de capacidades para escolher agentes por capacidade, não por nome fixo. */ capabilities?: AgentCapabilityRegistry; /** Estimador de custo por (papel, tokens). Injetado pelo ModelRouter. */ estimateCostUsd?: (role: AgentRole, tokens: number) => number; /** Decomposição assistida por modelo (opcional). Valida antes de aceitar. */ decompose?: (objective: string, classification: Classification) => DecomposedTask[] | null; /** * Memória do runtime, consultada de forma SELETIVA: padrões de falha * relevantes ao objetivo e taxa de sucesso por agente. Nunca a memória * inteira injetada no contexto. */ memory?: PlanningMemory; /** * Ranking de skills por objetivo (`SkillResolver.rankSkills`). Quando * presente, CADA tarefa carrega as skills do próprio objetivo em vez da * chain do agente para o run inteiro. */ resolveSkills?: (objective: string, limit: number) => string[]; /** * Diretório de entrega, RELATIVO à raiz do projeto (`--output`). Quando * presente, o plano ganha um nó `kind: 'tool'` que grava o que o run * produziu e cuja verificação confere o arquivo escrito. Ausente: nenhum nó * do grafo recebe permissão de escrita, e o comportamento é o de antes. */ output?: string; /** * Piso de força de verificação em [0,1] (`--min-quality`). * * Declarado, o Commander compara os modos possíveis e escolhe o MAIS BARATO * que ainda atinge o piso — em vez de simplesmente executar o modo que a * classificação sugeriu. Ausente, nada muda: o plano é o de sempre, e o * único ajuste continua sendo a degradação por teto de custo. * * O piso é sobre EVIDÊNCIA, não sobre resultado: ver `planQuality`. */ minQuality?: number; /** * Lê o projeto antes de decidir (`--survey`). Acrescenta um nó de tool na * CABEÇA do grafo que levanta stack, manifestos e árvore de forma * determinística, e o resultado entra no contexto mínimo das tarefas raiz. * Ignorado em modo `direct`: uma resposta de uma chamada não justifica * dobrar o grafo para levantar o terreno. */ survey?: boolean; /** * Critérios de aceite fornecidos pelo USUÁRIO, já parseados * (`contracts/acceptance.ts`). Entram nos contratos das tarefas terminais de * produto, junto com os que o Commander deriva do schema. * * Os dois conjuntos respondem perguntas diferentes e por isso convivem: os * gerados perguntam "o artefato tem a forma certa?", estes perguntam "é isto * que foi pedido?". Sem este campo só a primeira pergunta era feita. */ acceptance?: AcceptanceCriterion[]; /** * Roda o comando de teste do projeto no fim do grafo (`--verify-tests`). * * Opt-in porque executa um processo do projeto (o `scripts.test` do * manifesto, ou o runner da linguagem detectada) com o ambiente herdado. É a * mesma confiança de digitar `npm test`, e por isso é uma decisão de quem * roda, nunca um default. Ignorado em modo `direct`: uma resposta de uma * chamada não escreve arquivo nenhum, então não há o que a suíte meça. */ verifyTests?: boolean; } /** * Fatia da memória que o planejamento consulta. Interface estreita de * propósito: o Commander não precisa conhecer o `MemoryStore` inteiro, e um * teste pode passar um objeto literal. */ export interface PlanningMemory { findRelevantFailures(query: string): Array<{ pattern: string; occurrences: number; confidence: number; }>; /** * Decisões de runs ANTERIORES sobre objetivos semelhantes, já com resultado * conhecido (`DecisionJournal.findRelevant`). Opcional: um `PlanningMemory` * de teste, ou um store sem journal, continua válido sem ela. * * O journal era write-only: gravado no planejamento, lido só por `izanagi * explain`. Log de auditoria para humano é útil e não é retrieval — nada no * runtime consultava a própria escolha anterior. */ pastDecisions?(objective: string, kind: string): Array<{ chosen: string; outcomeStatus: string; relevance: number; }>; /** * Com `domain`, o recorte daquele domínio; sem ele, o agregado do agente. * `undefined` significa ausência de histórico, não histórico ruim. */ agentStats(agent: string, domain?: string): { runs: number; successes: number; failures: number; } | undefined; } /** Tarefa proposta por uma decomposição externa (LLM ou plugin). */ export interface DecomposedTask { id: string; objective: string; agent?: string; outputKind?: string; dependencies?: string[]; role?: AgentRole; optional?: boolean; } export interface PlanEstimate { nodes: number; parallelStages: number; /** Soma dos tetos de token dos contratos: limite superior, não previsão. */ maxTokens: number; /** Custo em USD no pior caso (todos os tetos gastos). Ausente sem estimador. */ maxCostUsd?: number; byRole: Record; /** * Força de VERIFICAÇÃO do plano em [0,1] (ver `planQuality`). * * Não é uma previsão da qualidade da entrega, e chamar isso de "qualidade" * sem esta frase seria vender previsão onde há contagem: mede quanta * evidência o plano se compromete a produzir sobre o próprio trabalho. */ quality: number; } /** * Um plano candidato, para a escolha entre estratégias. * * A especificação pede que, havendo estratégia equivalente mais barata, ela * seja preferida "respeitando a qualidade mínima configurada". Até aqui não * havia nem candidatos nem piso: o único ajuste era descer a escada de modo * quando o custo estourava, e isso é redução de ESCOPO (menos nós, menos * verificação), não uma estratégia equivalente mais barata. */ export interface PlanCandidate { mode: ExecutionMode; estimate: PlanEstimate; /** Por que este candidato foi aceito ou recusado. */ verdict: string; } /** * Força de verificação de um plano, em [0,1]. * * Mede os COMPROMISSOS que o plano assume sobre verificar o próprio trabalho. * Cinco propriedades observáveis do conjunto de contratos, com peso declarado * aqui e nenhuma delas derivada de execução: * * 0.25 política estrita: exige TODOS os critérios, inclusive os opcionais * 0.25 revisão independente: existe nó que produz `critique` * 0.15 avaliação independente: existe nó que produz `evaluation` * 0.15 algum critério SEMÂNTICO: há pergunta que schema nenhum responde * 0.20 fração de tarefas com critério que NÃO veio do schema do artefato * * A primeira versão desta função media critérios por tarefa, e a média tinha um * incentivo perverso: acrescentar um nó pouco verificado ABAIXAVA a nota de um * plano que verifica mais no total, então um piso de qualidade empurraria para * grafos menores. Pior, a contagem de critérios é função da riqueza do SCHEMA * do artefato (um nó `raw` tem um critério, um `security-report` tem seis), e * isso é propriedade do tipo de saída, não do rigor do plano. As cinco * propriedades acima são monótonas no que importa: um plano que se compromete * com mais verificação nunca pontua menos. * * O que esta nota NÃO é: previsão de que a entrega será melhor. Um plano com * mais verificação não produz trabalho melhor — produz mais evidência sobre o * trabalho, que é outra coisa, e é a única das duas que se pode garantir antes * de executar. */ export declare function planQuality(contracts: TaskContract[]): number; export interface CommanderPlan { runObjective: string; mode: ExecutionMode; modeReason: string; classification: Classification; graph: ExecutionGraph; contracts: TaskContract[]; estimate: PlanEstimate; /** Decisões tomadas na fase de planejamento (entram no Decision Journal). */ decisions: string[]; /** Problemas nos contratos gerados. Vazio em plano saudável. */ issues: string[]; /** * Estratégias comparadas antes da escolha, com o veredito de cada uma. * * Presente só quando houve comparação (piso de qualidade declarado): sem * piso não há o que comparar contra, e uma lista de um item só daria à * escolha uma aparência de deliberação que não houve. */ candidates?: PlanCandidate[]; } /** O que o Commander precisa saber sobre a falha — e nada além disso. */ export interface ReplanFailure { nodeId: string; error: string; /** Tentativa em que a falha aconteceu (1 = primeira). */ attempt: number; /** Critérios de aceite não comprovados pela Verification Engine. */ unmet?: string[]; /** Referência do artefato reprovado (`runId:nodeId`), não o conteúdo dele. */ artifactRef?: string; /** Agente que produziu a falha: sai da disputa na nova escolha. */ agent?: string; } export interface ReplanResult { graph: ExecutionGraph; contracts: TaskContract[]; decisions: string[]; /** * O que mudou entre o Plano A e o Plano B. Vazio significa que o * replanejamento não encontrou nada para mudar — e isso precisa aparecer, * senão "replanejou" vira sinônimo de "tentou de novo". */ changes: string[]; } export declare class Commander { private readonly planner; private readonly builder; constructor(planner?: Planner, builder?: ExecutionGraphBuilder); /** * Planeja o run inteiro. Cost-aware: se a estimativa estoura `maxCostUsd`, o * modo degrada um degrau e o plano é refeito, registrando o motivo (nunca * ultrapassa o orçamento em silêncio). */ plan(input: CommanderInput): CommanderPlan; /** * Replanejamento: produz um Plano B, não o Plano A com um nó reaberto. * * O `Planner.replan` legado marcava concluídos como `skipped`, reabria o nó * falho e devolvia o MESMO grafo: mesmo agente, mesmo papel, mesma * decomposição. Repetir a tentativa que já falhou é a definição de gastar * orçamento sem aprender nada. * * Escada determinística, nesta ordem: trocar o agente (o que falhou sai da * disputa) -> subir o papel (mais capacidade para a mesma tarefa) -> quebrar * a tarefa em duas (rascunho + fechamento dirigido aos critérios não * comprovados). Da segunda tentativa em diante, trocar agente E subir papel * ao mesmo tempo. Nada mudou = `changes` vazio, e quem chamou decide o que * fazer com isso: "replanejou" não pode virar sinônimo de "tentou de novo". * * O Commander recebe só o DELTA da falha (nó, causa, critérios não * comprovados, referência do artefato, tentativa). Nunca a execução inteira. */ replan(previous: { graph: ExecutionGraph; contracts?: TaskContract[]; }, failure: ReplanFailure, input: CommanderInput): ReplanResult; /** Constrói grafo + contratos para um modo específico. */ private buildForMode; /** DIRECT: um nó. Sem crítico, sem avaliador, sem gate. */ private directNodes; /** ASSISTED: especialista executa, verificação determinística fecha. */ private assistedNodes; /** * ORCHESTRATED/AUTONOMOUS: reaproveita os templates do Planner (já validados * e testados) e aplica duas correções do Commander: decomposição externa * quando fornecida, e marcação de nós opcionais para early stopping. */ private graphNodes; /** * Seleciona agente por capacidade quando há registro; senão devolve null. * * Agentes com histórico ruim saem da disputa — mas só quando sobra * alternativa: excluir todo mundo transformaria memória em paralisia. */ private pickAgent; /** * Melhor agente para o objetivo, com o papel como PREFERÊNCIA e não como * portão. * * O papel existe para não gastar um commander numa extração, e isso continua * valendo. O que não vale é o inverso: filtrar por papel primeiro e aceitar o * que sobrar, por pior que seja. Medido no catálogo real, esse portão dava * `skill-architect` (0.115) para "Definir a arquitetura de um SaaS * multi-tenant" tendo `architect` (0.380) na mesa, e `ai-engineer` para * "projetar um agente novo" tendo `agent-architect`. Nos dois casos o certo é * commander e o portão o descartava sem olhar a nota. * * A regra: fica no papel enquanto o candidato do papel for comparável ao * melhor de todos. Quando ele é MUITO pior, o papel cede — e o teto de custo * não fica desprotegido, porque quem paga a conta é o Budget Controller, que * age sobre o gasto e não sobre o rótulo. */ private bestWithRolePreference; /** * Agentes reprovados pelo histórico: taxa de sucesso abaixo do piso, com * amostra suficiente para a taxa significar alguma coisa. * * O recorte por DOMÍNIO vem primeiro: um agente que vai mal em frontend e bem * em backend não pode ser descartado de um trabalho de backend. Só quando não * há amostra suficiente naquele domínio o agregado global entra como sinal — * é menos preciso, mas é o único disponível enquanto o histórico é curto. */ /** * Agentes que já foram escolhidos para um objetivo semelhante e cujo run NÃO * fechou. * * Um resultado só não basta: `FAIL` acontece por motivos que não são do * agente (teto estourado, provider fora do ar), e queimar um agente por um * incidente transformaria ruído em política. A barra é a mesma da memória: * recorrência. */ /** * Memoizado por objeto de input: `plan()` e `pickAgent` consultam o mesmo * histórico, e ler o journal do disco uma vez por nó do grafo seria pagar * I/O por uma resposta que não muda durante o planejamento. */ private readonly burnedCache; private burnedByObjective; private computeBurned; private unreliableAgents; /** * Substitui a chain do run pelas skills relevantes ao objetivo DESTE nó. * Nós determinísticos (gate, evaluator, validator) não carregam skill: não * há prompt para elas ocuparem. */ /** * Troca o agente dos nós cujo agente já falhou neste objetivo, quando existe * alternativa. Sem alternativa, o nó fica como está: melhor um agente com * histórico ruim que nenhum agente, e o motivo já está nas decisões do plano. */ private swapBurnedAgents; private withTaskSkills; /** Contrato completo de um nó, com critérios derivados do schema do artefato. */ private contractFor; /** Estimativa de teto: soma dos budgets por papel, convertida em USD quando há estimador. */ estimate(contracts: TaskContract[], estimateCostUsd?: (role: AgentRole, tokens: number) => number): PlanEstimate; } /** Id do gate de revisão do orquestrador (usado por testes e instrumentação). */ export declare const ORCHESTRATOR_REVIEW_NODE_ID = "orchestrator-review"; /** * Valida uma decomposição externa antes de confiar nela: ids únicos, * dependências existentes, sem ciclo trivial. Erros derrubam a decomposição * inteira de volta para o template (nunca executa plano malformado). */ export declare function validateDecomposition(tasks: DecomposedTask[]): string[]; //# sourceMappingURL=commander.d.ts.map