/** * The thing spending the money can finally ask, and be told no. * * 1.44 gave an endpoint that *answers*. An agent may consult it and ignore it, * which is fine — advice an implementation can skip is still advice worth * having. What was missing is the shape of a refusal an agent can act on. * * **A guard that only says no teaches a caller to stop asking.** A model told * "denied" with no alternative has exactly two moves: send it anyway, or fail * the user's request. Both are worse than the call it wanted to make. So every * refusal here arrives with the levers that exist — this work routes to a * cheaper model that still fits, a batch window would halve it — each with * what it is worth *for this call*, and the assumption it rests on. * * **The guard never spends to answer.** No provider call, no LLM pass, no * pull. The answer comes from the store and the catalogue, or it says it * cannot tell. A cost guard that costs money to consult is a joke with a bill * attached. * * **An alternative the prompt does not fit in is not an alternative.** A * cheaper model with a smaller context window does not make this call cheaper; * it makes it impossible. Those are filtered out here rather than offered and * blamed later. */ import type { AnswerRequest, CostAnswer } from './answer.js'; import type { MeasuredPosition, PolicyJudgement } from './judgement.js'; import type { LimitsConfig, WaiveEntry } from './config-schema.js'; import type { PricingCatalogue } from './pricing.js'; import type { PlanAssumption } from './plan.js'; export type GuardVerdict = 'yes' | 'no' | 'cannot-tell'; export interface GuardAlternative { kind: 'route' | 'batch' | 'route+batch'; /** The model this moves to, when it moves. */ model: { id: string; displayName: string; } | null; /** * What this alternative saves **on this call** — not per month. * * The caller is deciding one call, right now. A monthly figure would be the * right number at the wrong moment, and an agent has no way to act on it. */ savingUsd: number; /** What the log cannot confirm, typed as everywhere since 1.38. */ assumes: PlanAssumption[]; /** * Whether the described call fits this alternative's context window. * * Only `true` ever reaches a caller — the false ones are dropped before * they are offered. The field exists so the rule is visible in the type * rather than buried in a filter nobody reads. */ fits: true; } export interface GuardAnswer { schemaVersion: 1; verdict: GuardVerdict; /** The full cost answer, halves and provenance intact. */ cost: CostAnswer; /** * What to do instead, dearest saving first. Present on a refusal, and on a * yes as well: an agent that can spend less while still being allowed to * spend should be told so. */ alternatives: GuardAlternative[]; /** * A one-line reason a human will read in a log. The fields above are what a * machine acts on; this is never the only place a fact appears. */ because: string; /** * The `limits` policy, judged for this call by `judgeLimits` — the same * function the gateway and `serve` call, which is what "one policy, three * doors" means in code. Always present: with no policy it says `no-policy` * rather than being absent, because a missing field and a judged absence * are different answers. */ policy: PolicyJudgement; } export interface GuardRequest extends AnswerRequest { /** Whether the caller says this work can wait for a batch window. */ batchEligible?: boolean; /** The `limits` block, when the caller holds one. Judged, never re-derived. */ limits?: LimitsConfig; /** Measured spend per scope, from the caller's own log — never a guess. */ position?: MeasuredPosition; /** The workload this call belongs to, for the per-label ceiling. */ label?: string; /** The conversation, for the per-session ceiling. Never echoed back. */ session?: string; /** The config's `waive` list — a silenced limit answers yes, on the record. */ waive?: readonly WaiveEntry[]; } export declare function guardSpend(request: GuardRequest, options: { catalogue: PricingCatalogue; on?: Date; }): GuardAnswer; //# sourceMappingURL=guard.d.ts.map