/** * Budget Controller: orçamento de execução com custo, chamadas e degradação. * * O `PhaseTokenBudget` já existente controla tokens por fase e continua sendo * a fonte de verdade disso. Este módulo compõe em cima dele o que faltava para * um runtime comercializável: custo em USD, teto de chamadas de tool, teto de * agentes, teto de retries, tempo de parede, e a ESCADA DE DEGRADAÇÃO. * * Regra dura: nunca ultrapassar o orçamento em silêncio. Quando a pressão * sobe, o controlador devolve o próximo passo de degradação para quem executa * aplicar (reduzir contexto, reduzir saída, trocar modelo, reduzir paralelismo, * cortar tarefa opcional, pedir aprovação humana), nessa ordem. */ import { PhaseTokenBudget, type PhaseId } from './budget.js'; export interface ExecutionBudgetLimits { maxTokens: number; maxCostUsd?: number; maxTimeMs?: number; maxAgents?: number; maxRetries?: number; maxToolCalls?: number; /** * Tarefas em voo simultâneas. Sem teto, um batch grande dispara todas as * chamadas de uma vez e um provider com rate limit apertado transforma * paralelismo em 429: o healing gasta mais do que a execução serial teria * gasto. */ maxConcurrency?: number; } /** Passos da escada, do mais barato de aplicar ao mais invasivo. */ export type DegradationStep = 'reduce-context' | 'reduce-output' | 'downgrade-model' | 'reduce-parallelism' | 'drop-optional-tasks' | 'require-human-approval'; export declare const DEGRADATION_LADDER: DegradationStep[]; /** Pressão a partir da qual a escada começa. */ export declare const DEGRADATION_FLOOR = 0.6; /** * Cada degrau tem o SEU limiar, distribuído entre `DEGRADATION_FLOOR` e 1. * Sem isso, um único limiar faria a escada inteira ser consumida em sequência * assim que a pressão passasse dele: um run com 6 tarefas chegaria a "pedir * aprovação humana" só por ter 6 chamadas, não por estar realmente no limite. * Pedir intervenção humana é o último recurso e exige 93% de consumo. */ export declare const DEGRADATION_THRESHOLDS: Record; export interface SpendRecord { phase: PhaseId; tokens: number; costUsd?: number; /** Tokens servidos do cache do provider (não contam como economia local). */ cachedTokens?: number; model?: string; } export interface SpendResult { ok: boolean; /** Motivo da recusa quando `ok` é false. */ reason?: string; /** Limite estourado, quando houve. */ limit?: 'phase-tokens' | 'total-tokens' | 'cost' | 'time'; } export interface TokenTelemetry { inputTokens: number; outputTokens: number; totalTokens: number; budgetTokens: number; savedTokens: number; estimatedCostUsd: number; maxCostUsd?: number; cacheHits: number; cacheMisses: number; /** Tokens de prompt servidos do cache do provider. */ providerCachedTokens: number; /** Chars de contexto economizados pelo Context Resolver. */ contextCharsSaved: number; parallelTasks: number; modelEscalations: number; retries: number; toolCalls: number; agentsUsed: number; degradationsApplied: DegradationStep[]; } export declare class ExecutionBudget { readonly limits: ExecutionBudgetLimits; readonly phases: PhaseTokenBudget; private readonly startedAt; private inputTokens; private outputTokens; private costUsd; private cacheHits; private cacheMisses; private providerCached; private contextCharsSaved; private parallelTasks; private escalations; private retries; private toolCalls; private readonly agents; private readonly applied; /** * `phases` permite COMPARTILHAR um `PhaseTokenBudget` já existente em vez de * criar outro. Sem isso, o runtime passa a ter duas contas de token para o * mesmo run (a do orçamento por fase e a do controlador), e elas divergem em * silêncio: exatamente o tipo de número mentiroso que este módulo existe * para impedir. */ constructor(limits: ExecutionBudgetLimits, complexity?: number, startedAt?: number, phases?: PhaseTokenBudget); /** Restaura gasto persistido (resume de checkpoint). */ restore(state: { phaseSpent?: Partial>; costUsd?: number; inputTokens?: number; outputTokens?: number; retries?: number; }): void; /** * Registra gasto. Recusa (sem contabilizar) quando o gasto estouraria um * teto: a decisão de abortar/degradar fica com quem chama, mas o número * nunca passa do limite escondido. */ spend(record: SpendRecord): SpendResult; recordCacheHit(savedTokens?: number): void; private savedByCache; recordCacheMiss(): void; recordContextSaving(chars: number): void; recordParallelBatch(size: number): void; recordEscalation(): void; recordRetry(): boolean; recordToolCall(): boolean; recordAgent(agent: string): boolean; get totalTokens(): number; /** * Tokens que ainda cabem no teto do run. Nunca negativo: estourado é 0, e * quem decide o que fazer com o estouro é `spend()`. * * Existe para alimentar o roteamento por nó — escolher modelo sem saber o * saldo é como o `RoutingContext` funcionava até aqui, congelado no início * do run com o teto INICIAL como se nada tivesse sido gasto. */ get remainingTokens(): number; get spentUsd(): number; /** * Custo que ainda cabe no run, em USD, ou `undefined` quando não há teto * declarado. Existe para que um executor capaz de recusar por conta própria * (o CLI de agente aceita `--max-budget-usd`) receba o teto REAL restante em * vez de gastar primeiro e ser cobrado depois: o degrau mais confiável do * Budget Controller é o que o processo executor também conhece. */ get remainingUsd(): number | undefined; get elapsedMs(): number; /** * Pressão orçamentária em [0,1]: o maior consumo relativo entre tokens * totais, uso de QUALQUER fase, custo e tempo. 1 = teto atingido em pelo * menos uma dimensão. * * A fase entra na conta porque o gasto real é limitado por fase, não pelo * total: `execution` recebe 65% do total, então gastar tudo o que a execução * pode gastar deixaria a pressão em 0.65 se olhássemos só o total. A escada * de degradação nunca passaria do primeiro degrau, mesmo com a fase que * importa esgotada. O sinal correto é "estou perto de ficar sem o orçamento * que estou de fato usando". */ pressure(): number; /** * Próximo passo de degradação, ou null quando ainda há folga (pressão * abaixo de 0.6) ou a escada já foi toda aplicada. Cada chamada que devolve * um passo o marca como aplicado: a escada nunca repete o mesmo degrau. */ nextDegradation(floor?: number): DegradationStep | null; /** * Todos os degraus que a pressão atual justifica e que ainda não foram * aplicados, na ordem da escada. Quem executa aplica todos de uma vez: sob * pressão de 90%, não faz sentido aplicar um degrau por chamada e só chegar * ao corte de tarefas opcionais cinco chamadas depois. */ pendingDegradations(floor?: number): DegradationStep[]; /** Passos já aplicados, na ordem. */ degradations(): DegradationStep[]; telemetry(): TokenTelemetry; /** Linha compacta para CLI/trace. */ static formatTelemetry(t: TokenTelemetry): string; } //# sourceMappingURL=execution-budget.d.ts.map