/** * Per-user limits: Vendo counts, the host decides. * * The host's `limits` callback is asked once before each metered action and its * verdict is honored. Everything else about a limit lives HERE — reading the * meter, invoking the policy, recording what an allow spent, and the fail mode — * so a choke point is one `gate` call and can never grow its own half of the * rule. * * The fail mode is the load-bearing decision: a policy that throws DENIES. A * limits system that fails open stops limiting silently, so the host keeps * believing they have a cap while every user is unlimited — strictly worse than * a turn that was refused and said so. */ import { type LimitAction, type LimitsCallback, type RunContext, type StoreOps, type ToolRegistry } from "./core/index.js"; import type { VendoComposition } from "./compose-context.js"; /** The decision a choke point acts on — `LimitDecision`'s two forms collapsed to one, so no caller re-derives the boolean/object grammar. */ export type LimitVerdict = { allow: true; } | { allow: false; message?: string; retryable?: true; }; export interface Limiter { gate(action: LimitAction, ctx: RunContext): Promise; } /** The host's policy, bound to the meter it decides on. * * `ops` is the usage family and not the whole `StoreOps` because a limiter * against a store with no meter would read every user as zero — composition * refuses that outright (`composeLimits`), so it cannot arrive here. */ export declare function createLimiter({ callback, ops }: { callback: LimitsCallback; ops: NonNullable; }): Limiter; /** The generation choke — `vendo_make`, the ONE door an app is built through, * asked before it runs. A deny answers the agent with the same `blocked` * outcome every other refusal on this registry uses, and raises the card the * person reads on the call's own stream, so the turn CARRIES ON: unlike a * refused message, a refused generation is something the agent can talk about. * * Wrapped at composition rather than inside `@vendoai/vendo/apps`, so a deployment * with no `limits` key executes the registry it always executed. */ export declare const limitGenerations: (tools: ToolRegistry, limiter: Limiter) => ToolRegistry; /** The `limits` key, composed ONLY when the host set one — unset leaves no * limiter, and every choke point then costs a single undefined check. * * `StoreOps.usage` is optional (`store.ts`: a store with nowhere to meter says * so by omitting the family), so a policy against a meterless store is refused * HERE rather than enforced against counts that are all zero. */ export declare const composeLimits: (composition: VendoComposition) => Pick;