/** * cost — shared cost-accounting helper emitted by LLMCall + Agent. * * Pattern: Strategy (PricingTable port) + Event emission (typedEmit). * Role: core/ layer. When a runner is configured with a PricingTable, * every LLM response drives a `cost.tick` event carrying per-call * tokens/USD plus cumulative run totals. When a `costBudget` is * also set, the first crossing emits `cost.limit_hit` with * `action: 'warn'` (library never auto-aborts; consumers decide). * Emits: agentfootprint.cost.tick * agentfootprint.cost.limit_hit */ import type { PricingTable } from '../adapters/types.js'; export interface CostAccountingScope { cumTokensInput: number; cumTokensOutput: number; cumEstimatedUsd: number; costBudgetHit: boolean; } /** * What a `costBudget` accepts, and what it does when crossed (8.14.0). * * A bare `number` is `{ usd, onExceed: 'warn' }` — byte-for-byte what every * release before 8.14.0 did — so no existing agent changes behaviour. */ export type CostBudget = number | { readonly usd: number; readonly onExceed: 'warn' | 'halt'; }; /** Normalized form. */ export interface ResolvedCostBudget { readonly usd: number; readonly onExceed: 'warn' | 'halt'; } /** * Normalize a `costBudget`, refusing a shape that cannot mean anything. * * `'halt'` is refused for `LLMCall`: halting means "stop at the next iteration * boundary", and one call has no next boundary. Accepting it there would give * a consumer a stop button wired to nothing — the same silent no-op * {@link assertCostBudgetHasPricing} exists to remove. * * @param runner the class name for the message — `'Agent'` or `'LLMCall'`. */ export declare function resolveCostBudget(runner: 'Agent' | 'LLMCall', costBudget: CostBudget | undefined): ResolvedCostBudget | undefined; /** * Refuse a `costBudget` with no `pricingTable` to measure it against (8.13.0). * * The budget is denominated in USD and {@link emitCostTick} returns on its first * line when there is no pricing table, so before this refusal the pair emitted * NOTHING — no `cost.tick`, and no `cost.limit_hit` however much a run spent. A * budget that cannot be crossed is not a lenient budget; it is a guard rail that * was never installed, and it looks identical from the outside to one that * simply has not been hit yet. * * Shared by BOTH runners that take the pair. Leaving the sibling class with the * same silent no-op would be a governance fix that itself drops half the cases. * * @param runner the class name for the message — `'Agent'` or `'LLMCall'`. */ export declare function assertCostBudgetHasPricing(runner: 'Agent' | 'LLMCall', pricingTable: PricingTable | undefined, costBudget: CostBudget | undefined): void; type Usage = { readonly input: number; readonly output: number; readonly cacheRead?: number; readonly cacheWrite?: number; }; /** * Who billed this call, and for what. * * One argument rather than two so the pair cannot drift: a caller that knows * the model always has the slot for the provider in front of it, and the one * caller that genuinely cannot name a provider (a window strategy that reports * `spend` without declaring `billing`) omits it deliberately instead of by * forgetting a parameter. */ type BilledBy = { /** `LLMProvider.name`. Omitted only where the caller genuinely cannot know it. */ readonly provider?: string; /** The model id `pricePerToken` is asked about. */ readonly model: string; }; /** * Emit `cost.tick` for the just-completed LLM response and, if the * consumer set a `costBudget`, emit a one-shot `cost.limit_hit` the first * time cumulative USD crosses the budget. Does nothing when no * `pricingTable` is configured — zero overhead on runs without costing. * * Scope must carry the running cumulative counters; callers seed them * in their Seed stage. */ export declare function emitCostTick(scope: CostAccountingScope & { $emit: (name: string, payload?: unknown) => void; }, pricingTable: PricingTable | undefined, costBudget: ResolvedCostBudget | undefined, billedBy: BilledBy, usage: Usage): void; export {};