import { z } from "zod"; import type { Json } from "./ids.js"; /** * Usage & pricing v3 (spec §5) — the Cloud services' meter refusal, surfaced * honestly client-side. Every Vendo Cloud door refuses exhausted meters with * HTTP 402, the stable code `meter-exhausted`, and one structured body: * `{ meter, used, limit, resets_at, reason, exits: { upgrade_url, * byo_docs_url } }`. That body is the ONLY source of truth — OSS performs no * entitlement checks of any kind (locked constraint); this module just * recognizes the shape and renders it as one operator-grade sentence, reused * verbatim by the thread banner (via the agent's safe stream-error rail), the * CLI, and doctor. */ export interface MeterExhausted { /** The refused meter's canonical id (e.g. `ai_tokens`) — or undefined when * the body carried none / an unprintable value (the format falls back). */ meter?: string; /** `"usd"` means every figure in this refusal is dollars — render currency. * Absent means counts, so a host pinned to an older @vendoai/vendo/core still * renders a correct (if unit-less) sentence against a new server. */ unit?: "usd"; used?: number; limit?: number; /** ISO timestamp; kept verbatim (format renders just the date part). */ resetsAt?: string; /** Short refusal reason token from the body (e.g. `allowance`, `spend-cap`). */ reason?: string; upgradeUrl?: string; byoDocsUrl?: string; } /** The console's stable refusal code (one shape, all services). */ export declare const METER_EXHAUSTED_CODE = "meter-exhausted"; /** The refusal BODY as every Cloud door WRITES it — the wire's own snake_case, * not the camelCase `MeterExhausted` the parser below produces. The producing * door and any second reader validate against this rather than re-listing the * fields; `parseMeterExhausted` stays deliberately looser, because a host * pinned to an older core must still render a sentence from a partial body. */ export declare const meterExhaustedBodySchema: z.ZodObject<{ meter: z.ZodString; unit: z.ZodOptional>; used: z.ZodNumber; limit: z.ZodNumber; resets_at: z.ZodString; reason: z.ZodString; exits: z.ZodObject<{ upgrade_url: z.ZodString; byo_docs_url: z.ZodString; }, "strip", z.ZodTypeAny, { upgrade_url: string; byo_docs_url: string; }, { upgrade_url: string; byo_docs_url: string; }>; }, "strip", z.ZodTypeAny, { reason: string; meter: string; used: number; limit: number; resets_at: string; exits: { upgrade_url: string; byo_docs_url: string; }; unit?: "usd" | undefined; }, { reason: string; meter: string; used: number; limit: number; resets_at: string; exits: { upgrade_url: string; byo_docs_url: string; }; unit?: "usd" | undefined; }>; export type MeterExhaustedBody = z.infer; /** * Recognize the meter-exhausted refusal in a parsed 402 body. Tolerant to the * envelope: the stable code and the fields are read from the body root and * from its `error` member (the console's enveloped-error twin), the `error` * member winning where both carry a field. Anything that isn't the shape → * undefined (callers keep their existing 402 mapping). */ export declare function parseMeterExhausted(payload: unknown): MeterExhausted | undefined; /** * Recognize the refusal on a thrown error object — the model gateway's 402 * reaches the agent as a provider APICallError carrying the raw response * body/data, never as a VendoError. Only OUR formatter's output ever renders * from it, so the safe-error policy (ENG-214) holds. */ export declare function meterExhaustedFromError(error: unknown): MeterExhausted | undefined; /** * The one user-visible refusal sentence: names the meter, the usage-vs-limit * figures and reset date when the body carried them, and the two exits * (spec §5: upgrade / BYO) at the body's own URLs — the refusal body is the * only source of truth, so no URL is ever invented client-side; a partial * body degrades to naming the exits without links. The thread banner, the * CLI, and doctor all print exactly this. */ export declare function formatMeterExhausted(refusal: MeterExhausted): string; /** The refusal as VendoError detail (Json), so programmatic consumers keep the * structured fields alongside the crafted message. */ export declare function meterExhaustedDetail(refusal: MeterExhausted): Json;