floe-guard 0.13.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/livekit.cjs +24 -0
- package/dist/adapters/livekit.cjs.map +1 -1
- package/dist/adapters/livekit.d.cts +1 -1
- package/dist/adapters/livekit.d.ts +1 -1
- package/dist/adapters/livekit.js +1 -1
- package/dist/adapters/retell.cjs +24 -0
- package/dist/adapters/retell.cjs.map +1 -1
- package/dist/adapters/retell.d.cts +1 -1
- package/dist/adapters/retell.d.ts +1 -1
- package/dist/adapters/retell.js +1 -1
- package/dist/adapters/vapi.cjs +24 -0
- package/dist/adapters/vapi.cjs.map +1 -1
- package/dist/adapters/vapi.d.cts +2 -2
- package/dist/adapters/vapi.d.ts +2 -2
- package/dist/adapters/vapi.js +1 -1
- package/dist/{chunk-5Q5VQC6J.js → chunk-TAK6I7CN.js} +25 -1
- package/dist/chunk-TAK6I7CN.js.map +1 -0
- package/dist/{guard-BXtXoark.d.cts → guard-Ct38zQZS.d.cts} +6 -4
- package/dist/{guard-BXtXoark.d.ts → guard-Ct38zQZS.d.ts} +6 -4
- package/dist/index.cjs +42 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +19 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cost_map.json +24 -0
- package/dist/chunk-5Q5VQC6J.js.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/sync.ts","../src/pricing.ts","../src/cost_map.json","../src/guard.ts","../src/latency.ts","../src/middleware.ts","../src/retry.ts","../src/voice-pricing.ts","../src/gates.ts"],"sourcesContent":["/**\n * floe-guard — a local budget guardrail for AI agents.\n *\n * Hard-stops your agent before its next LLM call when it would cross a USD spend\n * ceiling. This package is the Vercel AI SDK (TypeScript) adapter; the Python\n * package `floe-guard` (pip) carries the LiteLLM / CrewAI / LangChain adapters.\n */\n\nexport {\n BudgetGuard,\n type BudgetGuardOptions,\n type BudgetAdvisory,\n type BudgetReservation,\n type ReservationHandle,\n type SpendEvent,\n} from \"./guard.js\";\nexport {\n LatencyBudget,\n type LatencyBudgetOptions,\n type LatencyAdvisory,\n} from \"./latency.js\";\nexport {\n FloeGuardError,\n BudgetExceeded,\n TokenBudgetExceeded,\n DeadlineExceeded,\n UnpriceableModelError,\n UnpriceableVoiceError,\n LedgerSyncError,\n} from \"./errors.js\";\nexport { pushLedger } from \"./sync.js\";\nexport {\n budgetGuardMiddleware,\n type BudgetGuardMiddleware,\n} from \"./middleware.js\";\nexport {\n type ManualPrice,\n type PricedModel,\n resolvePrice,\n priceTokens,\n} from \"./pricing.js\";\nexport {\n withBudgetRetry,\n type BudgetRetryOptions,\n type RetryPlan,\n} from \"./retry.js\";\n\nexport * as pricing from \"./pricing.js\";\n\nexport {\n type VoiceMode,\n type VoiceRate,\n lookupVoiceRate,\n resolveVoiceRate,\n voiceLegCost,\n priceVoiceLeg,\n} from \"./voice-pricing.js\";\n\n// gates as a namespace, mirroring Python's `floe_guard.gates` — so\n// `import { gates } from \"floe-guard\"` reads like the Python package.\nexport * as gates from \"./gates.js\";\n","/**\n * Exceptions for floe-guard.\n *\n * Everything derives from {@link FloeGuardError} (the package-root base) so callers\n * can catch the whole family with a single `catch (e) { if (e instanceof FloeGuardError) ... }`.\n *\n * Mirrors `src/floe_guard/errors.py` in the Python package — message formats are\n * kept byte-for-byte identical so both adapters read the same.\n */\n\n/** Base class for every error raised by floe-guard. */\nexport class FloeGuardError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FloeGuardError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown before an LLM call that would cross the configured spend ceiling.\n *\n * The guard throws this *instead of* letting the next call run, so a runaway loop\n * stops here rather than burning more money.\n */\nexport class BudgetExceeded extends FloeGuardError {\n readonly spentUsd: number;\n readonly limitUsd: number;\n\n constructor(spentUsd: number, limitUsd: number, message?: string) {\n // Subclasses (the token twin) pass their own message through so they don't\n // have to reassign `this.message` after super().\n super(\n message ??\n `BUDGET EXCEEDED — call blocked (spent $${spentUsd.toFixed(6)} of $${limitUsd.toFixed(6)} ceiling)`,\n );\n this.name = \"BudgetExceeded\";\n this.spentUsd = spentUsd;\n this.limitUsd = limitUsd;\n }\n}\n\n/**\n * Thrown before a call that would cross a token ceiling (aggregate or step).\n *\n * The token twin of {@link BudgetExceeded} — it extends it so the same retry\n * logic that treats a USD block as terminal (`!(error instanceof\n * BudgetExceeded)`) treats a token block as terminal too, with no extra wiring.\n * `spentUsd` / `limitUsd` are inherited but not meaningful here; the token\n * fields are the payload.\n *\n * `scope` is `\"aggregate\"` (the guard-wide `tokenLimit`) or `\"step\"` (the\n * innermost active {@link BudgetGuard.step} cap).\n */\nexport class TokenBudgetExceeded extends BudgetExceeded {\n readonly spentTokens: number;\n readonly limitTokens: number;\n readonly scope: \"aggregate\" | \"step\";\n\n constructor(spentTokens: number, limitTokens: number, scope: \"aggregate\" | \"step\") {\n // Pass the token-shaped message through super (BudgetExceeded's default is\n // dollar-shaped); 0/0 for the inherited spentUsd/limitUsd — a token block\n // spent no tracked USD.\n super(\n 0,\n 0,\n `TOKEN BUDGET EXCEEDED — call blocked (${scope}: ${spentTokens} of ` +\n `${limitTokens} token ceiling)`,\n );\n this.name = \"TokenBudgetExceeded\";\n this.spentTokens = spentTokens;\n this.limitTokens = limitTokens;\n this.scope = scope;\n }\n}\n\n/**\n * Thrown when a model cannot be priced and the guard is fail-closed.\n *\n * We refuse rather than silently accrue $0 — \"we cannot cap what we cannot price\".\n * Pass a manual price (`priceOverrides` or `record(..., { price })`) to make the\n * model enforceable.\n */\nexport class UnpriceableModelError extends FloeGuardError {\n readonly model: string;\n\n constructor(model: string) {\n super(\n `Cannot price model '${model}': not in the bundled cost map and no ` +\n `manual price was given. The guard cannot enforce a budget on spend ` +\n `it cannot measure. Pass a price override to enable enforcement.`,\n );\n this.name = \"UnpriceableModelError\";\n this.model = model;\n }\n}\n\n/**\n * Thrown when a voice leg (STT/TTS/telephony) cannot be priced and the guard is\n * fail-closed.\n *\n * The voice twin of {@link UnpriceableModelError}: we refuse rather than silently\n * accrue $0 — \"we cannot cap what we cannot price\". It fires when an adapter is\n * asked to meter a leg for a vendor that is absent from the bundled voice cost map\n * (or whose entry has the wrong unit/mode for the leg) and no per-unit override was\n * given. Pass a per-unit rate (`stt_usd_per_second` / `tts_usd_per_1k_chars` /\n * `telephony_usd_per_minute`) to make the leg enforceable.\n *\n * Mirrors `UnpriceableVoiceError` in `src/floe_guard/errors.py`.\n */\nexport class UnpriceableVoiceError extends FloeGuardError {\n readonly vendor: string | null;\n readonly mode: string;\n\n constructor(vendor: string | null, mode: string) {\n // Match Python's `{vendor!r}`: a string is quoted, None renders as `None`.\n const shown = vendor === null ? \"None\" : `'${vendor}'`;\n super(\n `Cannot price ${mode} vendor ${shown}: not in the bundled voice cost ` +\n `map (or its entry has the wrong unit for a ${mode} leg) and no per-unit ` +\n `override was given. The guard cannot enforce a budget on spend it ` +\n `cannot measure. Pass a per-unit rate to enable enforcement.`,\n );\n this.name = \"UnpriceableVoiceError\";\n this.vendor = vendor;\n this.mode = mode;\n }\n}\n\n/**\n * Thrown when an **opt-in** ledger sync to Reconcile Mode fails.\n *\n * Covers a missing API key, a non-https/malformed base URL, a non-2xx response\n * (401 bad/missing key, 403 agent closed/suspended), a network/timeout failure, or\n * a malformed response. Sync is off by default and only runs when the caller\n * explicitly opts in ({@link BudgetGuard.enableSync} + {@link BudgetGuard.sync}) —\n * this error never fires for a guard that hasn't opted in, because such a guard\n * never sends.\n *\n * Mirrors `LedgerSyncError` in `src/floe_guard/errors.py`.\n */\nexport class LedgerSyncError extends FloeGuardError {\n constructor(message: string) {\n super(message);\n this.name = \"LedgerSyncError\";\n }\n}\n\n/**\n * Thrown before a call whose projected duration would blow the SLA.\n *\n * The latency twin of {@link BudgetExceeded}: `LatencyBudget.check()` throws this\n * *instead of* letting the next tool/model call start, so the chain sheds work or\n * falls back to a faster path rather than violating the end-user SLA. Cooperative —\n * killing an already-running stalled call is the framework's job (AbortSignal),\n * not the guard's.\n *\n * Mirrors `DeadlineExceeded` in `src/floe_guard/errors.py` — message format is\n * kept byte-for-byte identical so both adapters read the same.\n */\n/** Shared millisecond rounding for cross-language message parity: `toFixed(0)`\n * rounds half-up while Python's `:.0f` rounds half-to-even, so tie values\n * would break the byte-for-byte contract. Both packages use floor(x + 0.5)\n * (see `_round_half_up` in `src/floe_guard/errors.py`). */\nfunction roundHalfUp(ms: number): number {\n return Math.floor(ms + 0.5);\n}\n\nexport class DeadlineExceeded extends FloeGuardError {\n readonly elapsedMs: number;\n readonly slaMs: number;\n\n constructor(elapsedMs: number, slaMs: number) {\n super(\n `DEADLINE EXCEEDED — call blocked (elapsed ${roundHalfUp(elapsedMs)}ms of ${roundHalfUp(slaMs)}ms SLA)`,\n );\n this.name = \"DeadlineExceeded\";\n this.elapsedMs = elapsedMs;\n this.slaMs = slaMs;\n }\n}\n","/**\n * Opt-in ledger sync — push the local spend ledger to Floe's Reconcile Mode.\n *\n * **Zero-telemetry is the default, and stays the default.** Nothing in this module\n * runs unless the caller *explicitly* opts in with a Floe API key — via\n * {@link BudgetGuard.enableSync} + {@link BudgetGuard.sync}. There is no implicit\n * enablement and no background send: the ledger leaves your process only when you\n * call one of those. Your ledger, your key, your choice.\n *\n * **Why sync at all.** Floe's gateway can't see spend it never routed — BYOK,\n * self-hosted, or off-path LLM/tool calls. Pushing your local ledger into Reconcile\n * Mode is how that spend lands on the ledger and your **Coverage Score** becomes\n * computable. Budget, not balance: this reports *what you already spent* for\n * coverage/attribution; it does not move money or change any wallet balance.\n *\n * **What leaves the process — exactly the {@link BudgetGuard.exportLog} JSONL** and\n * nothing else: one line per spend event, each a priced-cost record — `timestamp`,\n * `kind` (`\"llm\"`/`\"tool\"`), `model_or_tool`, `prompt_tokens`, `completion_tokens`,\n * `cost_usd`, and the optional `label` / `reserved` you set. **No prompts, no\n * message content, no identifiers** beyond a `label` you choose.\n *\n * Mirrors `src/floe_guard/sync.py` in the Python package — same behaviour\n * (https-only, key-required, fail-closed), transported over `fetch` instead of\n * `urllib`.\n *\n * Opt in: https://dev-dashboard.floelabs.xyz · https://floelabs.xyz\n */\n\nimport { LedgerSyncError } from \"./errors.js\";\n\nconst FLOE_API_KEY_ENV = \"FLOE_API_KEY\";\nconst FLOE_API_BASE_URL_ENV = \"FLOE_API_BASE_URL\";\nconst DEFAULT_BASE_URL = \"https://credit-api.floelabs.xyz\";\nconst LEDGER_SYNC_PATH = \"/v1/agents/ledger/sync\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n// The ONLY keys allowed to leave the process — the exportLog() schema. Enforced\n// (not just documented) in validateLedger(): a line carrying anything else is\n// rejected, so a hand-supplied file can't smuggle prompts / content / identifiers\n// past the privacy contract.\nconst ALLOWED_KEYS = new Set([\n \"timestamp\",\n \"kind\",\n \"model_or_tool\",\n \"prompt_tokens\",\n \"completion_tokens\",\n \"cost_usd\",\n \"label\",\n \"reserved\",\n]);\nconst REQUIRED_KEYS = [\"timestamp\", \"kind\", \"model_or_tool\", \"cost_usd\"];\n\n/**\n * Read an environment variable without a hard `process` reference — the package\n * compiles with `types: []` (no `@types/node`), so `process` is untyped. Reads\n * live at call time so tests that mutate the env are honoured.\n */\nfunction envVar(name: string): string {\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return proc?.env?.[name] ?? \"\";\n}\n\n/**\n * Parse and validate every ledger line against the `exportLog()` schema.\n *\n * Throws {@link LedgerSyncError} on the first malformed / unknown-field / invalid\n * record, **before** anything is sent — so only priced spend events ever leave the\n * process, even when the ledger came from a hand-edited file. This is the privacy\n * contract *enforced*, not merely asserted: an **extra/unknown key** (a smuggled\n * `prompt`, `user_id`, …) is rejected outright. Mirrors `_validate_ledger` in the\n * Python client.\n *\n * (JS `typeof x === \"number\"` already excludes booleans and `Number.isInteger`\n * rejects them too, so — unlike Python, where `bool` is an `int` subclass — no\n * explicit boolean guards are needed.)\n */\nfunction validateLedger(jsonl: string): void {\n let i = 0;\n for (const raw of jsonl.split(\"\\n\")) {\n i += 1;\n const line = raw.trim();\n if (!line) continue;\n let event: unknown;\n try {\n event = JSON.parse(line);\n } catch (err) {\n throw new LedgerSyncError(`Ledger line ${i} is not valid JSON: ${describeCause(err)}`);\n }\n if (typeof event !== \"object\" || event === null || Array.isArray(event)) {\n throw new LedgerSyncError(`Ledger line ${i} is not a JSON object.`);\n }\n const ev = event as Record<string, unknown>;\n const extra = Object.keys(ev).filter((k) => !ALLOWED_KEYS.has(k));\n if (extra.length) {\n throw new LedgerSyncError(\n `Ledger line ${i} has fields outside the exportLog() schema: ` +\n `${JSON.stringify(extra.sort())}. Only priced spend events may be synced — ` +\n \"no prompts, content, or identifiers.\",\n );\n }\n const missing = REQUIRED_KEYS.filter((k) => !(k in ev));\n if (missing.length) {\n throw new LedgerSyncError(\n `Ledger line ${i} is missing required field(s): ${JSON.stringify(missing)}.`,\n );\n }\n if (ev.kind !== \"llm\" && ev.kind !== \"tool\") {\n throw new LedgerSyncError(\n `Ledger line ${i}: kind must be 'llm' or 'tool', got ${JSON.stringify(ev.kind)}.`,\n );\n }\n if (typeof ev.model_or_tool !== \"string\") {\n throw new LedgerSyncError(`Ledger line ${i}: model_or_tool must be a string.`);\n }\n const cost = ev.cost_usd;\n if (typeof cost !== \"number\" || !Number.isFinite(cost) || cost < 0) {\n throw new LedgerSyncError(\n `Ledger line ${i}: cost_usd must be a finite, non-negative number, got ${JSON.stringify(cost)}.`,\n );\n }\n const ts = ev.timestamp;\n if (typeof ts !== \"number\" || !Number.isFinite(ts)) {\n throw new LedgerSyncError(\n `Ledger line ${i}: timestamp must be a finite number, got ${JSON.stringify(ts)}.`,\n );\n }\n for (const field of [\"prompt_tokens\", \"completion_tokens\"] as const) {\n const value = ev[field];\n // Absent (undefined) or explicit null are fine — tool events carry null; a\n // present value must be an integer.\n if (value !== undefined && value !== null && !Number.isInteger(value)) {\n throw new LedgerSyncError(`Ledger line ${i}: ${field} must be an integer or null.`);\n }\n }\n if (\"label\" in ev && typeof ev.label !== \"string\") {\n throw new LedgerSyncError(`Ledger line ${i}: label must be a string.`);\n }\n if (\"reserved\" in ev) {\n const reserved = ev.reserved;\n if (typeof reserved !== \"number\" || !Number.isFinite(reserved)) {\n throw new LedgerSyncError(`Ledger line ${i}: reserved must be a finite number.`);\n }\n }\n }\n}\n\n/**\n * POST an {@link BudgetGuard.exportLog} JSONL ledger to Reconcile Mode and resolve\n * with the number of events the server accepted.\n *\n * This is the ONLY function that sends the ledger, and it sends only when called.\n * An empty ledger is a no-op (resolves `0` with **no** `fetch` call).\n *\n * @param jsonl the ledger as newline-delimited JSON — `exportLog()` output.\n * @param apiKey Floe agent key (`floe_<hex>`, `read_write`). Defaults to the\n * `FLOE_API_KEY` env var.\n * @param opts.baseUrl API base. Defaults to `FLOE_API_BASE_URL`, else the prod host.\n * @param opts.timeoutMs request timeout in milliseconds (default 30_000).\n * @throws {LedgerSyncError} missing key, a non-https/malformed base URL, a non-2xx\n * response, network/timeout, or a malformed response body. The key and ledger are\n * never sent over a non-https or malformed URL.\n */\nexport async function pushLedger(\n jsonl: string,\n apiKey?: string,\n opts: { baseUrl?: string; timeoutMs?: number } = {},\n): Promise<number> {\n // An empty ledger never touches the network — nothing to send.\n if (!jsonl.trim()) return 0;\n\n // Enforce the privacy contract BEFORE any network: only exportLog() events —\n // no prompts, content, or identifiers — may leave, even from a hand-supplied file.\n validateLedger(jsonl);\n\n const key = ((apiKey ?? \"\") || envVar(FLOE_API_KEY_ENV)).trim();\n if (!key) {\n throw new LedgerSyncError(\n `No Floe API key: pass apiKey or set ${FLOE_API_KEY_ENV}. ` +\n \"Sync is opt-in and needs your key.\",\n );\n }\n\n const envBase = envVar(FLOE_API_BASE_URL_ENV).trim();\n const base = ((opts.baseUrl ?? \"\").trim() || envBase || DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n let parsed: URL;\n try {\n parsed = new URL(base);\n } catch {\n // The request carries the Floe agent key as a bearer token AND your spend\n // ledger — never send either over a malformed URL.\n throw new LedgerSyncError(\n `Refusing to send the ledger to '${base}': ` +\n \"the base URL must be an https:// URL with a host.\",\n );\n }\n if (parsed.protocol !== \"https:\" || !parsed.host) {\n // Same guard for a well-formed but non-https (or hostless) URL.\n throw new LedgerSyncError(\n `Refusing to send the ledger to '${base}': ` +\n \"the base URL must be an https:// URL with a host.\",\n );\n }\n const url = `${base}${LEDGER_SYNC_PATH}`;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n // Refuse redirects: a 3xx would otherwise let the key + ledger be re-sent to\n // another host. (The Fetch spec strips `Authorization` on a cross-origin\n // redirect, but `redirect: \"error\"` is the belt-and-suspenders — nothing is\n // re-sent anywhere, same-origin or not.)\n redirect: \"error\",\n headers: {\n Authorization: `Bearer ${key}`,\n \"Content-Type\": \"application/x-ndjson\",\n Accept: \"application/json\",\n },\n body: jsonl,\n signal: controller.signal,\n });\n } catch (err) {\n throw new LedgerSyncError(`Could not reach Floe at ${url}: ${describeCause(err)}`);\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n throw new LedgerSyncError(await describeHttpError(response));\n }\n\n let payload: unknown;\n try {\n payload = JSON.parse(await response.text());\n } catch (err) {\n throw new LedgerSyncError(`Malformed JSON from Floe at ${url}: ${describeCause(err)}`);\n }\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n throw new LedgerSyncError(`Unexpected response shape from Floe at ${url}.`);\n }\n // \"synced\" is the count the server accepted (new rows); \"duplicates\" (already\n // ingested, idempotent) are not counted here. Absent → 0; present must be a real\n // non-negative integer — reject bool/float/negative/garbage rather than coerce.\n const synced = (payload as Record<string, unknown>).synced;\n if (synced === undefined || synced === null) return 0;\n if (typeof synced !== \"number\" || !Number.isInteger(synced) || synced < 0) {\n throw new LedgerSyncError(`Invalid 'synced' count from Floe at ${url}: ${JSON.stringify(synced)}.`);\n }\n return synced;\n}\n\nfunction describeCause(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nasync function describeHttpError(response: Response): Promise<string> {\n let detail = \"\";\n try {\n const data: unknown = JSON.parse(await response.text());\n if (\n typeof data === \"object\" &&\n data !== null &&\n typeof (data as Record<string, unknown>).error === \"string\"\n ) {\n detail = ` (${(data as Record<string, unknown>).error as string})`;\n }\n } catch {\n // Non-JSON / empty error body — the status code alone is the message.\n }\n const code = response.status;\n if (code === 401) return `Floe rejected the API key (401 unauthorized)${detail}.`;\n if (code === 403) {\n return (\n `Floe refused the sync (403 forbidden)${detail} — the agent may be ` +\n \"closed/suspended, or the key is read-only (a read_write key is required).\"\n );\n }\n return `Floe returned HTTP ${code}${detail}.`;\n}\n","/**\n * Offline token pricing from a vendored LiteLLM cost map.\n *\n * Mirrors `src/floe_guard/pricing.py`: BOTH the input and output per-token prices\n * must be finite numbers, otherwise the model is treated as unpriceable. A\n * half-valid entry would silently undercharge — so we refuse it (fail-closed).\n *\n * No network. The cost map (`cost_map.json`) is a snapshot of LiteLLM's\n * `model_prices_and_context_window.json`; refresh it on a schedule or estimates\n * drift as vendors change prices.\n */\n\nimport costMapJson from \"./cost_map.json\";\n\n/** A user-supplied per-token price, in USD, for a model the map cannot price. */\nexport interface ManualPrice {\n inputCostPerToken: number;\n outputCostPerToken: number;\n}\n\n/** A resolved per-token price plus where it came from. */\nexport interface PricedModel {\n inputCostPerToken: number;\n outputCostPerToken: number;\n source: \"override\" | \"cost_map\";\n}\n\ninterface CostMapEntry {\n input_cost_per_token?: unknown;\n output_cost_per_token?: unknown;\n}\n\nconst COST_MAP = costMapJson as Record<string, CostMapEntry>;\n\n/**\n * The one `<provider>/` prefix that is safe to strip: the remainder of a\n * `groq/…` id is the ChatGroq id the map vendors (e.g. `groq/qwen/qwen3-32b` →\n * `qwen/qwen3-32b`). `openai/` and `anthropic/` are deliberately excluded:\n * their own model ids never contain slashes (single-segment remainders are\n * already covered by the bare-last-segment fallback), so a multi-segment\n * remainder under those prefixes is some OTHER vendor's model behind an\n * OpenAI-compatible endpoint (vLLM, OpenRouter, …) — bridging it into a\n * Groq-priced key would under-meter. Unknown prefixes fail closed the same way.\n */\nconst PROVIDER_PREFIXES = new Set([\"groq\"]);\n\n/**\n * A trailing dated-snapshot suffix: Anthropic's `-20250929` or OpenAI's\n * `-2024-08-06`. Vendors resolve alias ids to dated snapshots in responses, so a\n * snapshot the map doesn't list yet prices at its alias entry (same model, same\n * rate) instead of failing closed. (`\\d` is ASCII-only in JS; the Python regex\n * uses re.ASCII to match.)\n */\nconst DATE_SUFFIX = /-(?:\\d{8}|\\d{4}-\\d{2}-\\d{2})$/;\n\n/**\n * Lookup keys for a model id in two specificity groups, deduplicated.\n *\n * Group 1 (exact): the raw id, the id with a known `provider/` first segment\n * stripped, the bare last segment. Group 2 (date-stripped): the same forms\n * with a trailing dated-snapshot suffix removed. Kept separate so a\n * less-specific date-stripped key (in overrides OR the map) can never shadow\n * an exact dated entry — e.g. an alias override must not absorb a snapshot\n * the map prices differently.\n */\nfunction candidateGroups(model: string): [string[], string[]] {\n const m = model.trim();\n const base = [m];\n const firstSlash = m.indexOf(\"/\");\n if (firstSlash !== -1 && PROVIDER_PREFIXES.has(m.slice(0, firstSlash))) {\n base.push(m.slice(firstSlash + 1));\n }\n const lastSlash = m.lastIndexOf(\"/\");\n if (lastSlash !== -1) {\n base.push(m.slice(lastSlash + 1));\n }\n const exact: string[] = [];\n for (const cand of base) {\n if (cand && !exact.includes(cand)) exact.push(cand);\n }\n const stripped: string[] = [];\n for (const cand of exact) {\n const c = cand.replace(DATE_SUFFIX, \"\");\n if (c && !exact.includes(c) && !stripped.includes(c)) stripped.push(c);\n }\n return [exact, stripped];\n}\n\nfunction bothFinite(a: unknown, b: unknown): boolean {\n return (\n typeof a === \"number\" &&\n typeof b === \"number\" &&\n Number.isFinite(a) &&\n Number.isFinite(b)\n );\n}\n\n/**\n * Resolve a model to its per-token price, or `null` if it cannot be priced.\n *\n * Per specificity group (exact forms first, date-stripped fallbacks second):\n * overrides win, then the bundled cost map. Fail-closed: the first matching\n * entry must have finite prices, else `null`.\n */\nexport function resolvePrice(\n model: string,\n overrides?: Record<string, ManualPrice>,\n): PricedModel | null {\n for (const cands of candidateGroups(model)) {\n if (overrides) {\n for (const cand of cands) {\n // Own-property check so a key like \"constructor\" can't pull a function\n // off Object.prototype and be treated as a price entry.\n const ov = Object.prototype.hasOwnProperty.call(overrides, cand)\n ? overrides[cand]\n : undefined;\n if (ov !== undefined) {\n if (bothFinite(ov.inputCostPerToken, ov.outputCostPerToken)) {\n return {\n inputCostPerToken: ov.inputCostPerToken,\n outputCostPerToken: ov.outputCostPerToken,\n source: \"override\",\n };\n }\n return null;\n }\n }\n }\n\n for (const cand of cands) {\n const entry = Object.prototype.hasOwnProperty.call(COST_MAP, cand)\n ? COST_MAP[cand]\n : undefined;\n if (!entry) continue;\n\n const input = entry.input_cost_per_token;\n const output = entry.output_cost_per_token;\n if (!bothFinite(input, output)) return null;\n\n return {\n inputCostPerToken: input as number,\n outputCostPerToken: output as number,\n source: \"cost_map\",\n };\n }\n }\n return null;\n}\n\n/** USD cost for token usage. Negative counts are clamped to zero. */\nexport function priceTokens(\n priced: PricedModel,\n promptTokens: number,\n completionTokens: number,\n): number {\n const p = Math.max(0, promptTokens);\n const c = Math.max(0, completionTokens);\n const cost = p * priced.inputCostPerToken + c * priced.outputCostPerToken;\n if (!Number.isFinite(cost)) {\n // Defense-in-depth: resolvePrice already guarantees finite rates.\n throw new Error(\"Non-finite LLM cost — pricing entry is invalid\");\n }\n return Math.max(0, cost);\n}\n","{\n \"chatgpt-4o-latest\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"claude-3-7-sonnet-20250219\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-3-haiku-20240307\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-3-opus-20240229\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-4-opus-20250514\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-4-sonnet-20250514\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-fable-5\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-haiku-4-5\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-haiku-4-5-20251001\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-1\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-1-20250805\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-20250514\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-5-20251101\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-6\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-6-20260205\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-7\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-7-20260416\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-8\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-20250514\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-5\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-5-20250929\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-6\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-5\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-0125\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-0613\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-1106\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4-0613\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-2025-04-14\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-mini-2025-04-14\": {\n \"input_cost_per_token\": 8e-7,\n \"output_cost_per_token\": 0.0000032,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-nano-2025-04-14\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 8e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-2024-08-06\": {\n \"input_cost_per_token\": 0.00000375,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-2024-11-20\": {\n \"input_cost_per_token\": 0.00000375,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-mini-2024-07-18\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:o4-mini-2025-04-16\": {\n \"input_cost_per_token\": 0.000004,\n \"output_cost_per_token\": 0.000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash-001\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash-lite\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash-lite-001\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-computer-use-preview-10-2025\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-lite\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-lite-preview-06-17\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-lite-preview-09-2025\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-native-audio-latest\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-native-audio-preview-09-2025\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-native-audio-preview-12-2025\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-preview-09-2025\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-pro\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-pro-preview-tts\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3-flash-preview\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.000003,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3-pro-preview\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-flash-lite\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-flash-lite-preview\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-flash-live-preview\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-pro-preview\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-pro-preview-customtools\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.5-flash\": {\n \"input_cost_per_token\": 0.0000015,\n \"output_cost_per_token\": 0.000009,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.5-flash-lite\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.6-flash\": {\n \"input_cost_per_token\": 0.0000015,\n \"output_cost_per_token\": 0.0000075,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-embedding-001\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"embedding\"\n },\n \"gemini-embedding-2\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"embedding\"\n },\n \"gemini-embedding-2-preview\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"embedding\"\n },\n \"gemini-exp-1206\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-flash-latest\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-flash-lite-latest\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-gemma-2-27b-it\": {\n \"input_cost_per_token\": 3.5e-7,\n \"output_cost_per_token\": 0.00000105,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-gemma-2-9b-it\": {\n \"input_cost_per_token\": 3.5e-7,\n \"output_cost_per_token\": 0.00000105,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-omni-flash-preview\": {\n \"input_cost_per_token\": 0.0000015,\n \"output_cost_per_token\": 0.000009,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-pro-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-robotics-er-1.5-preview\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-robotics-er-1.6-preview\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-robotics-er-2-preview\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-0125\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-1106\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-16k\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000004,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0125-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0314\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0613\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-1106-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo-2024-04-09\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-2025-04-14\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-mini\": {\n \"input_cost_per_token\": 4e-7,\n \"output_cost_per_token\": 0.0000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-mini-2025-04-14\": {\n \"input_cost_per_token\": 4e-7,\n \"output_cost_per_token\": 0.0000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-nano\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-nano-2025-04-14\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-05-13\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-08-06\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-11-20\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview-2024-12-17\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview-2025-06-03\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-2024-07-18\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-audio-preview\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-audio-preview-2024-12-17\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-search-preview\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-search-preview-2025-03-11\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-search-preview\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-search-preview-2025-03-11\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-2025-08-07\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-chat\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-chat-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-mini\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-mini-2025-08-07\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-nano\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-nano-2025-08-07\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-search-api\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-search-api-2025-10-14\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1-2025-11-13\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1-chat-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2-2025-12-11\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2-chat-latest\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.3-chat-latest\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-2026-03-05\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-mini\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-mini-2026-03-17\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-nano\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-nano-2026-03-17\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.5-2026-04-23\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6-luna\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.0000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6-sol\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6-terra\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-1.5\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-2025-08-28\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini-2025-10-06\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini-2025-12-15\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"llama-3.1-8b-instant\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 8e-8,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"llama-3.3-70b-versatile\": {\n \"input_cost_per_token\": 5.9e-7,\n \"output_cost_per_token\": 7.9e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"meta-llama/llama-4-scout-17b-16e-instruct\": {\n \"input_cost_per_token\": 1.1e-7,\n \"output_cost_per_token\": 3.4e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"o1\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o1-2024-12-17\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-2025-04-16\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-mini\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-mini-2025-01-31\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o4-mini\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o4-mini-2025-04-16\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"openai/gpt-oss-120b\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"openai/gpt-oss-20b\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"openai/gpt-oss-safeguard-20b\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"qwen/qwen3-32b\": {\n \"input_cost_per_token\": 2.9e-7,\n \"output_cost_per_token\": 5.9e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"text-embedding-3-large\": {\n \"input_cost_per_token\": 1.3e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-3-small\": {\n \"input_cost_per_token\": 2e-8,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-ada-002\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-ada-002-v2\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"__voice__\": {\n \"deepgram-nova-3\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0001283333,\n \"provider\": \"deepgram\"\n },\n \"deepgram-nova-3-multilingual\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0001533333,\n \"provider\": \"deepgram\"\n },\n \"deepgram-nova-3-base\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0002416667,\n \"provider\": \"deepgram\"\n },\n \"assemblyai-universal-streaming\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0000416667,\n \"provider\": \"assemblyai\"\n },\n \"elevenlabs-multilingual-v2\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.1,\n \"provider\": \"elevenlabs\"\n },\n \"elevenlabs-flash-v2.5\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.05,\n \"provider\": \"elevenlabs\"\n },\n \"elevenlabs-turbo-v2.5\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.05,\n \"provider\": \"elevenlabs\"\n },\n \"cartesia-sonic\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.03,\n \"provider\": \"cartesia\"\n },\n \"cartesia-line\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.06,\n \"provider\": \"cartesia\"\n },\n \"rime-mist-v2\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.03,\n \"provider\": \"rime\"\n },\n \"twilio-us-inbound-local\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.0085,\n \"provider\": \"twilio\"\n },\n \"twilio-us-outbound-local\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.014,\n \"provider\": \"twilio\"\n },\n \"twilio-us-sip-inbound\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.004,\n \"provider\": \"twilio\"\n }\n }\n}\n","/**\n * The local, in-process budget guard.\n *\n * `BudgetGuard` is a kill-switch that lives in the LLM call path. The contract:\n *\n * 1. Call {@link BudgetGuard.check} BEFORE every LLM call. If the *next* call\n * would cross the ceiling, it throws {@link BudgetExceeded} and the call never\n * runs.\n * 2. Call {@link BudgetGuard.record} AFTER every response, with the token usage.\n * It prices the tokens offline and accrues the USD into a running total.\n *\n * **Concurrency.** `check()` then `record()` is a check-then-act with an `await`\n * in between. Fire several model calls at once (e.g. `Promise.all`) and they all\n * `check()` against the same under-limit total before any `record()` lands, so\n * the ceiling is blown (see issue #18). {@link BudgetGuard.reserve} /\n * {@link BudgetGuard.settle} close that gap: `reserve()` holds the estimated cost\n * in flight (synchronously, before the await), so parallel callers each take\n * their own slice of the ceiling. JS is single-threaded, so an in-flight counter\n * is enough — no lock needed. The middleware uses it; `check`/`record` are\n * unchanged.\n *\n * This is a faithful port of `src/floe_guard/guard.py` — same prediction logic,\n * same epsilon handling, same fail-closed default.\n */\n\nimport { BudgetExceeded, TokenBudgetExceeded, UnpriceableModelError } from \"./errors.js\";\nimport { pushLedger } from \"./sync.js\";\nimport {\n type ManualPrice,\n priceTokens,\n resolvePrice,\n} from \"./pricing.js\";\n\n/** Tolerance for float rounding in the running spend total (well below $0.000001). */\nconst EPS = 1e-12;\n\n/**\n * A two-dimensional in-flight hold (USD + tokens) returned by\n * {@link BudgetGuard.reserve} when a token ceiling or an active\n * {@link BudgetGuard.step} is involved. A dumb value handle — pass it straight\n * back to {@link BudgetGuard.settle} / {@link BudgetGuard.release}. When neither\n * tokens nor a step are involved, `reserve` returns a plain `number` instead\n * (byte-for-byte the old behaviour), so {@link ReservationHandle} is the union.\n */\nexport interface BudgetReservation {\n readonly usd: number;\n readonly tokens: number;\n}\n\n/**\n * A plain `number` when USD-only (backward compatible); a\n * {@link BudgetReservation} when a token ceiling or an active step is in play.\n */\nexport type ReservationHandle = number | BudgetReservation;\n\nfunction isReservation(h: ReservationHandle): h is BudgetReservation {\n return (\n typeof h === \"object\" &&\n h !== null &&\n typeof (h as BudgetReservation).usd === \"number\" &&\n typeof (h as BudgetReservation).tokens === \"number\"\n );\n}\n\n/** One scope on the step stack (see {@link BudgetGuard.step}). Mutable. */\ninterface StepState {\n readonly maxUsd: number | null;\n readonly maxTokens: number | null;\n spentUsd: number;\n spentTokens: number;\n reservedUsd: number;\n reservedTokens: number;\n}\n\n/**\n * One priced spend event in the guard's per-call ledger.\n *\n * Every {@link BudgetGuard.record} / {@link BudgetGuard.settle} /\n * {@link BudgetGuard.recordTool} / {@link BudgetGuard.settleTool} that accrues\n * spend appends exactly one event, so\n * the ledger's costs sum to `spentUsd` (unless a `maxLogEvents` ring buffer has\n * evicted old events). The schema is identical in the Python\n * package (`SpendEvent` in `src/floe_guard/guard.py`) and\n * {@link BudgetGuard.exportLog} serialises it with the same snake_case keys in\n * both languages, so every agent emits the same shape regardless of stack.\n */\nexport interface SpendEvent {\n /** Unix epoch seconds (UTC). */\n readonly timestamp: number;\n readonly kind: \"llm\" | \"tool\";\n readonly modelOrTool: string;\n /** `null` for tool events. */\n readonly promptTokens: number | null;\n /** `null` for tool events. */\n readonly completionTokens: number | null;\n readonly costUsd: number;\n /** Caller-supplied tag (agent/task name). */\n readonly label?: string;\n /** The reservation settled by this call, if any. */\n readonly reserved?: number;\n}\n\nexport interface BudgetGuardOptions {\n /** Per-model manual prices for models the bundled cost map cannot price. */\n priceOverrides?: Record<string, ManualPrice>;\n /**\n * When `true` (default), recording an unpriceable model without a manual price\n * warns loudly AND throws {@link UnpriceableModelError}. When `false`, it warns\n * and skips accrual (you have opted into un-enforced spend for that model).\n */\n failClosed?: boolean;\n /**\n * Optional callback invoked with `(spentUsd, limitUsd)` right before\n * {@link BudgetExceeded} is thrown. Defaults to printing the\n * `BUDGET EXCEEDED — call blocked` banner to stderr. Fires on **USD** ceiling\n * crossings only; a token-ceiling block ({@link TokenBudgetExceeded}, aggregate\n * or step) is thrown without invoking it, since the callback is dollar-shaped.\n */\n onBlock?: (spentUsd: number, limitUsd: number) => void;\n /**\n * Utilization (basis points, 0..10000) at which {@link BudgetGuard.advisory}\n * flags `nearLimit` so an agent can taper before the hard-stop. Default 8000.\n */\n nearLimitBps?: number;\n /**\n * Optional cap on the per-call spend ledger ({@link BudgetGuard.spendLog}).\n * When set, the ledger is a ring buffer keeping the most recent N events so a\n * long-running agent's memory stays bounded; the running totals are\n * unaffected. Default: keep every event.\n */\n maxLogEvents?: number;\n /**\n * Aggregate token ceiling. `null`/undefined disables the token dimension\n * entirely (a pure USD guard, unchanged). `0` blocks the first token.\n */\n tokenLimit?: number;\n}\n\n/**\n * A context-aware spend signal for the single local budget.\n *\n * Mirrors the core fields of hosted Floe's `X-Floe-Budget-Advisory` header, so\n * agent logic that reads it (taper as you approach the cap, stop at it) ports\n * unchanged to the hosted path. Hosted adds what a local, single-budget guard\n * cannot know: which of several caps is tightest (`scope` across\n * `credit_line | session | task | api | vendor`), cross-vendor reasoning,\n * server-truth balances, and rolling-window reset timing.\n *\n * This is a **soft** signal — the model may ignore it. The hard-stop\n * ({@link BudgetGuard.check}) is what enforces the ceiling; the advisory is\n * upside (let the agent finish on budget rather than be cut off).\n */\nexport interface BudgetAdvisory {\n nearLimit: boolean;\n /** Utilization in basis points, 0..10000 (8500 = 85%). */\n usedBps: number;\n remainingUsd: number;\n limitUsd: number;\n spentUsd: number;\n /** Hosted reports the tightest cap across all scopes; local is always \"local\". */\n scope: \"local\";\n /**\n * The guard's own next-call estimate (the costlier of the last LLM and last\n * tool call — the same value the default reservation uses). 0 until the first\n * call is recorded, so a planner can't divide by a cold estimate.\n *\n * Optional so adding it stays a non-breaking, additive change for any code\n * that constructs a `BudgetAdvisory` literal; `advisory()` always sets it.\n */\n expectedCost?: number;\n /**\n * How many more calls the remaining budget buys at expectedCost:\n * floor(remainingUsd / expectedCost). null when expectedCost is 0 (no call\n * recorded yet) — unknown, not zero. Optional for the same additive reason as\n * expectedCost; `advisory()` always sets it.\n */\n estCallsRemaining?: number | null;\n /**\n * Aggregate token utilization in basis points (0..10000), mirroring usedBps\n * for the token ceiling. null when no `tokenLimit` is set (nothing to signal).\n */\n tokenUsedBps?: number | null;\n /** Tokens left before the aggregate token ceiling. null when no `tokenLimit`. */\n remainingTokens?: number | null;\n /**\n * Per-step headroom for the innermost active step — present (non-null) ONLY\n * while a step() is active AND that dimension is capped. null otherwise.\n */\n stepRemainingUsd?: number | null;\n stepRemainingTokens?: number | null;\n /**\n * Spend rate in USD per minute over this guard's lifetime (spentUsd ÷ minutes\n * since the guard was created) — the number voice teams watch. null only when no\n * wall-clock time has elapsed yet. Make one guard per call/turn for a per-call\n * burn rate; a long-lived guard reports the session average. Optional for the\n * same additive reason as the fields above; `advisory()` always sets it.\n */\n burnRateUsdPerMin?: number | null;\n}\n\nexport class BudgetGuard {\n readonly limitUsd: number;\n spentUsd = 0;\n /**\n * Wall-clock start (ms since epoch) of this guard's spend window, for the $/min\n * burn rate (advisory().burnRateUsdPerMin). One guard per call/turn ⇒ a per-call\n * rate. Set at construction; tests set it directly to control elapsed time.\n */\n createdAtMs = Date.now();\n priceOverrides?: Record<string, ManualPrice>;\n failClosed: boolean;\n nearLimitBps: number;\n /** Aggregate token ceiling, or null when the token dimension is disabled. */\n readonly tokenLimit: number | null;\n /** Aggregate tokens accrued (prompt + completion + cache buckets). */\n spentTokens = 0;\n /** Tokens held in-flight — the token twin of `reserved`. */\n private reservedTokens = 0;\n /** Step stack: innermost step is last. Empty when no step() is active. */\n private readonly steps: StepState[] = [];\n\n private readonly onBlock: (spentUsd: number, limitUsd: number) => void;\n /**\n * Costs of the most recent priced LLM call and tool call, tracked\n * SEPARATELY: the default next-call prediction is the max of the two, so a\n * cheap tool call can't shrink the estimate right before an expensive LLM\n * call (or vice versa) — conservative beats one-call-too-late.\n */\n private lastLlmCost = 0;\n private lastToolCost = 0;\n /** USD held for in-flight calls (reserved, not yet settled). Counts toward the ceiling. */\n private reserved = 0;\n /** Per-call ledger, oldest first; a ring buffer when maxLogEvents is set. */\n private readonly spendEvents: SpendEvent[] = [];\n private readonly maxLogEvents?: number;\n /**\n * Per-tool running totals (settleTool/recordTool) — the tool side of the one\n * shared ceiling, exposed via the toolCosts getter. null-prototype: tool\n * names are caller-supplied strings, so a \"__proto__\" name is stored as\n * plain data instead of mutating the object's prototype.\n */\n private readonly toolCostTotals: Record<string, number> = Object.create(null);\n\n /**\n * Opt-in ledger sync — OFF by default (zero-telemetry). No key is stored and\n * nothing is ever sent until enableSync() is called; see enableSync()/sync().\n */\n private syncEnabled = false;\n private syncKey: string | null = null;\n private syncBase: string | null = null;\n\n /**\n * @param limitUsd the spend ceiling, in USD. `0` blocks the very first call.\n */\n constructor(limitUsd: number, options: BudgetGuardOptions = {}) {\n if (!Number.isFinite(limitUsd) || limitUsd < 0) {\n // NaN/Infinity would make every check() comparison fail-open, silently\n // disabling the guard — reject them up front.\n throw new RangeError(\n `limitUsd must be a finite, non-negative number, got ${limitUsd}`,\n );\n }\n // `=== undefined` (not `??`) so an explicit null is rejected by validation\n // rather than silently defaulting — matches Python, which rejects None.\n const nearLimitBps = options.nearLimitBps === undefined ? 8000 : options.nearLimitBps;\n if (!Number.isInteger(nearLimitBps) || nearLimitBps < 0 || nearLimitBps > 10000) {\n throw new RangeError(\n `nearLimitBps must be an integer in 0..10000, got ${nearLimitBps}`,\n );\n }\n if (\n options.maxLogEvents !== undefined &&\n (!Number.isInteger(options.maxLogEvents) || options.maxLogEvents < 0)\n ) {\n throw new RangeError(\n `maxLogEvents must be a non-negative integer, got ${options.maxLogEvents}`,\n );\n }\n if (\n options.tokenLimit !== undefined &&\n (!Number.isInteger(options.tokenLimit) || options.tokenLimit < 0)\n ) {\n throw new RangeError(\n `tokenLimit must be a non-negative integer, got ${options.tokenLimit}`,\n );\n }\n this.limitUsd = limitUsd;\n this.tokenLimit = options.tokenLimit ?? null;\n this.maxLogEvents = options.maxLogEvents;\n this.priceOverrides = options.priceOverrides;\n this.failClosed = options.failClosed ?? true;\n this.onBlock = options.onBlock ?? defaultOnBlock;\n this.nearLimitBps = nearLimitBps;\n }\n\n /**\n * Throw {@link BudgetExceeded} if the next call would cross the ceiling.\n *\n * Call this immediately before each LLM request. The \"next call\" is estimated\n * conservatively as the costlier of the last LLM call and the last tool call\n * (override with `estimatedNextCost`); the\n * first call is always allowed unless the ceiling is already met. In-flight\n * reservations count toward the total, so this stays correct alongside\n * {@link BudgetGuard.reserve}.\n *\n * Note: `check` is a non-binding peek. For parallel calls, use `reserve()` /\n * `settle()`, which hold the estimate across the await.\n */\n check(estimatedNextCost?: number, options: { estimatedTokens?: number } = {}): void {\n const rawEstimate =\n estimatedNextCost === undefined ? this.defaultEstimate() : estimatedNextCost;\n if (!Number.isFinite(rawEstimate)) {\n // NaN/Infinity would poison the comparisons and fail-open — reject it\n // (parity with the constructor's Number.isFinite guard).\n throw new RangeError(\n `estimatedNextCost must be a finite number, got ${rawEstimate}`,\n );\n }\n const estimate = Math.max(0, rawEstimate);\n this.validateTokenEstimate(options.estimatedTokens);\n const tokens = Math.max(0, options.estimatedTokens ?? 0);\n const blocked = this.blockingCross(estimate, tokens);\n if (blocked !== null) this.raiseBlock(blocked);\n }\n\n /**\n * Atomically check the ceiling AND hold the estimated cost in flight.\n *\n * The concurrency-safe enforcement path: call before the request and hold the\n * returned reservation across the await, so parallel callers can't all clear\n * the same stale total. Throws {@link BudgetExceeded} (without reserving) if\n * the reservation would cross the ceiling. Returns the reservation handle to\n * pass to {@link BudgetGuard.settle} (or {@link BudgetGuard.release} on error).\n * `estimatedCost` defaults to the costlier of the last LLM call and the last\n * tool call.\n */\n reserve(estimatedCost?: number, options: { estimatedTokens?: number } = {}): ReservationHandle {\n const rawEstimate = estimatedCost === undefined ? this.defaultEstimate() : estimatedCost;\n if (!Number.isFinite(rawEstimate)) {\n // NaN would poison this.reserved and fail-open the ceiling — reject it.\n throw new RangeError(\n `estimatedCost must be a finite number, got ${rawEstimate}`,\n );\n }\n const estimate = Math.max(0, rawEstimate);\n // Reject a fractional/NaN/Infinity token estimate BEFORE mutating\n // reservedTokens/step holds — a NaN would fail-open the integer token\n // comparisons and corrupt the tally. A negative int is clamped by Math.max.\n this.validateTokenEstimate(options.estimatedTokens);\n const tokens = Math.max(0, options.estimatedTokens ?? 0);\n const blocked = this.blockingCross(estimate, tokens);\n if (blocked !== null) this.raiseBlock(blocked);\n this.reserved += estimate;\n this.reservedTokens += tokens;\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n step.reservedUsd += estimate;\n step.reservedTokens += tokens;\n }\n // Plain number only when nothing token- or step-shaped is in play, so\n // pre-existing USD-only callers get byte-for-byte the old handle.\n if (tokens === 0 && step === null) return estimate;\n return { usd: estimate, tokens };\n }\n\n /**\n * Release a reservation and record the actual cost. `record` is `settle` with\n * no reservation. Returns the USD cost of this call; unpriceable-model handling\n * matches {@link BudgetGuard.record}, and any held reservation is released even\n * on the warn-and-skip path. A priced call appends one {@link SpendEvent} to\n * {@link BudgetGuard.spendLog} (`label` tags it, e.g. with an agent/task name);\n * the warn-and-skip path accrues nothing and logs nothing, so the ledger stays\n * in lockstep with `spentUsd`.\n */\n settle(\n model: string,\n promptTokens: number,\n completionTokens: number,\n options: { reserved?: ReservationHandle; price?: ManualPrice; label?: string } = {},\n ): number {\n const reserved = options.reserved ?? 0;\n // A bad reserved handle would corrupt this.reserved and break the ceiling for\n // OTHER in-flight calls (negative → phantom hold; Infinity → clears all holds).\n // reservedUsdOf validates the handle (raw number, or a BudgetReservation's fields).\n const reservedUsd = this.reservedUsdOf(reserved);\n let overrides = this.priceOverrides;\n if (options.price !== undefined) {\n overrides = { ...(overrides ?? {}), [model]: options.price };\n }\n\n const priced = resolvePrice(model, overrides);\n if (priced === null) {\n console.warn(\n `Cannot price model '${model}': not in the bundled cost map and no ` +\n `manual price given. The budget guard cannot enforce a ceiling on ` +\n `spend it cannot measure — pass { price } or set it in priceOverrides.`,\n );\n // Release any held reservation on BOTH paths. Fail-closed must not leak\n // the in-flight hold, or reserved grows permanently and remainingUsd\n // shrinks until reserve() starts blocking everything.\n this.release(reserved);\n if (this.failClosed) {\n throw new UnpriceableModelError(model);\n }\n return 0;\n }\n\n let cost: number;\n try {\n cost = priceTokens(priced, promptTokens, completionTokens);\n } catch (err) {\n // priceTokens can throw (e.g. non-finite costs). Release the in-flight\n // hold before re-throwing so `reserved` doesn't leak and shrink\n // remainingUsd permanently — same fail-safe as the unpriceable path above.\n this.release(reserved);\n throw err;\n }\n if (reserved) {\n this.consumeReservation(reserved);\n }\n // Tokens accrued match what priceTokens bills (prompt + completion; negative\n // counts clamp to 0, as in pricing).\n const accruedTokens = Math.max(0, promptTokens) + Math.max(0, completionTokens);\n this.spentUsd += cost;\n this.spentTokens += accruedTokens;\n this.accrueStep(cost, accruedTokens);\n // Clamp a sub-epsilon float overshoot back to the limit so the running total\n // never reports as having crossed the ceiling by a rounding artifact.\n if (this.spentUsd - this.limitUsd > 0 && this.spentUsd - this.limitUsd < EPS) {\n this.spentUsd = this.limitUsd;\n }\n this.lastLlmCost = cost;\n this.appendEvent({\n timestamp: Date.now() / 1000,\n kind: \"llm\",\n modelOrTool: model,\n promptTokens,\n completionTokens,\n costUsd: cost,\n ...(options.label !== undefined ? { label: options.label } : {}),\n // 0 means \"no reservation\" (the plain record() path) — omit rather than\n // log a meaningless zero. Log the USD amount so the ledger schema stays\n // number-shaped even for a BudgetReservation.\n ...(reservedUsd ? { reserved: reservedUsd } : {}),\n });\n return cost;\n }\n\n /**\n * Price one response's tokens offline and add the cost to the total.\n *\n * Returns the USD cost of this call. If the model is unpriceable and no `price`\n * is given, behaviour depends on `failClosed`: warn + throw (default), or\n * warn + skip accrual.\n */\n record(\n model: string,\n promptTokens: number,\n completionTokens: number,\n options: { price?: ManualPrice; label?: string } = {},\n ): number {\n return this.settle(model, promptTokens, completionTokens, {\n reserved: 0,\n price: options.price,\n label: options.label,\n });\n }\n\n /**\n * Atomically check the ceiling AND hold a tool call's cost in flight.\n *\n * The tool-spend counterpart of {@link BudgetGuard.reserve} — and STRONGER\n * than the LLM path, because a paid tool's price is usually known exactly\n * before the call, so the pre-call hard-stop is precise rather than\n * estimated:\n *\n * const handle = guard.reserveTool(0.02); // throws BEFORE Apollo runs\n * const result = await apollo.peopleLookup(...);\n * guard.settleTool(\"apollo.people_lookup\", 0.02, { reserved: handle });\n *\n * Throws {@link BudgetExceeded} (without reserving) if the call would cross\n * the ceiling. The estimate is required — tools have no last-cost prediction\n * worth falling back to. Pass the returned handle to\n * {@link BudgetGuard.settleTool}, or {@link BudgetGuard.release} on failure.\n */\n reserveTool(estimatedCost: number): ReservationHandle {\n if (estimatedCost === undefined) {\n // reserve(undefined) would silently fall back to the last-cost prediction\n // (0 on a fresh guard) — an unguarded tool call. A missing price must\n // fail loudly, e.g. guard.reserveTool(priceTable[tool]).\n throw new RangeError(\"reserveTool requires an estimated cost, got undefined\");\n }\n if (!Number.isFinite(estimatedCost) || estimatedCost < 0) {\n // reserve() clamps a negative estimate to 0 (lenient LLM contract) — for\n // a tool that would reserve nothing: the same unguarded call.\n throw new RangeError(\n `estimatedCost must be a finite, non-negative number, got ${estimatedCost}`,\n );\n }\n return this.reserve(estimatedCost);\n }\n\n /**\n * Release a reservation and record a tool call's actual cost.\n *\n * `recordTool` is `settleTool` with no reservation. The caller supplies the\n * cost — tools have no token usage to price. Accrues into the same\n * `spentUsd` ceiling as tokens, tallies the per-tool total\n * ({@link BudgetGuard.toolCosts}), updates the tool side of the next-call\n * estimate (tracked separately from the LLM side; the default prediction is\n * the max of the two, so a tool-hammering loop's plain `check()` stops\n * BEFORE the crossing call without a cheap tool shrinking the LLM\n * prediction), and appends\n * a `kind: \"tool\"` {@link SpendEvent} to {@link BudgetGuard.spendLog}.\n * Returns `costUsd`.\n */\n settleTool(\n tool: string,\n costUsd: number,\n options: { reserved?: ReservationHandle; label?: string } = {},\n ): number {\n if (!Number.isFinite(costUsd) || costUsd < 0) {\n throw new RangeError(`costUsd must be a finite, non-negative number, got ${costUsd}`);\n }\n const reserved = options.reserved ?? 0;\n // A bad reserved handle would corrupt the in-flight tally and break the\n // ceiling for OTHER calls — same contract as settle(). reservedUsdOf\n // validates the handle (raw number, or a BudgetReservation's fields).\n const reservedUsd = this.reservedUsdOf(reserved);\n if (reserved) {\n this.consumeReservation(reserved);\n }\n this.spentUsd += costUsd;\n // A paid tool spends the step's USD too (no tokens to price).\n this.accrueStep(costUsd, 0);\n // Same sub-epsilon clamp as settle(): never report a rounding-artifact\n // crossing of the ceiling.\n if (this.spentUsd - this.limitUsd > 0 && this.spentUsd - this.limitUsd < EPS) {\n this.spentUsd = this.limitUsd;\n }\n this.lastToolCost = costUsd;\n this.toolCostTotals[tool] = (this.toolCostTotals[tool] ?? 0) + costUsd;\n this.appendEvent({\n timestamp: Date.now() / 1000,\n kind: \"tool\",\n modelOrTool: tool,\n promptTokens: null,\n completionTokens: null,\n costUsd,\n ...(options.label !== undefined ? { label: options.label } : {}),\n ...(reservedUsd ? { reserved: reservedUsd } : {}),\n });\n return costUsd;\n }\n\n /**\n * Accrue a non-LLM cost (a paid tool/API call) against the same ceiling.\n *\n * Post-hoc accrual for costs only known after the call (metered APIs); when\n * the price is known up front, {@link BudgetGuard.reserveTool} /\n * {@link BudgetGuard.settleTool} give the stronger pre-call hard-stop. See\n * `settleTool` for the full contract. Returns `costUsd`.\n */\n recordTool(tool: string, costUsd: number, options: { label?: string } = {}): number {\n return this.settleTool(tool, costUsd, { reserved: 0, label: options.label });\n }\n\n /**\n * Drop an in-flight reservation without recording spend (e.g. the call failed\n * before producing usage). Safe to call with `0`.\n */\n release(reserved: ReservationHandle): void {\n // Validate before the zero-check so a NaN handle throws instead of being\n // silently dropped (a leak); a bad handle corrupts the in-flight tally.\n // reservedUsdOf validates the handle (raw number, or a BudgetReservation's fields).\n this.reservedUsdOf(reserved);\n if (!reserved) return;\n this.consumeReservation(reserved);\n }\n\n /** USD left before the ceiling, net of in-flight reservations (never negative). */\n get remainingUsd(): number {\n return Math.max(0, this.limitUsd - this.spentUsd - this.reserved);\n }\n\n /**\n * Per-tool running USD totals, keyed by the name given to `settleTool()` /\n * `recordTool()` — e.g. `{\"apollo.people_lookup\": 0.42, \"exa.search\": 0.11}`.\n * Makes the token/tool split of the one shared ceiling inspectable\n * (`spentUsd - sum of toolCosts` is the token side). Returns a snapshot copy.\n */\n get toolCosts(): Record<string, number> {\n return { ...this.toolCostTotals };\n }\n\n /**\n * The per-call spend ledger, oldest first — one {@link SpendEvent} per priced\n * `record()` / `settle()` / `recordTool()` / `settleTool()`. Returns a\n * snapshot copy: mutating it cannot corrupt the ledger.\n */\n get spendLog(): SpendEvent[] {\n return [...this.spendEvents];\n }\n\n /**\n * The spend ledger as JSONL — one event per line, newline-terminated.\n *\n * The schema is stable and language-independent (snake_case keys, fixed order;\n * optional fields omitted when absent), identical to the Python package's\n * `export_log()`, so heterogeneous agents produce logs you can concatenate and\n * analyse as one stream. (The *schema* is the contract, not the bytes: the two\n * runtimes may render the same float differently, e.g. JS `0.0000025` vs\n * Python `2.5e-06`.) Empty ledger yields `\"\"`.\n */\n exportLog(): string {\n return this.spendEvents\n .map((e) => {\n // snake_case wire shape, fixed key order — the cross-language schema.\n const row: Record<string, unknown> = {\n timestamp: e.timestamp,\n kind: e.kind,\n model_or_tool: e.modelOrTool,\n prompt_tokens: e.promptTokens,\n completion_tokens: e.completionTokens,\n cost_usd: e.costUsd,\n };\n if (e.label !== undefined) row.label = e.label;\n if (e.reserved !== undefined) row.reserved = e.reserved;\n return `${JSON.stringify(row)}\\n`;\n })\n .join(\"\");\n }\n\n /**\n * **Opt in** to pushing this guard's ledger to Floe's Reconcile Mode.\n *\n * Off by default — this is the *only* way to turn sync on, and it turns on\n * nothing else: it stores your key/endpoint and does **not** send. Nothing\n * leaves the process until you call {@link BudgetGuard.sync}. Zero-telemetry\n * stays the default; there is no background send.\n *\n * Why: Floe's gateway can't see spend it never routed (BYOK, self-hosted,\n * off-path). Syncing your local ledger is what makes that spend — and your\n * **Coverage Score** — visible. Budget, not balance: it reports what you already\n * spent for coverage/attribution; it moves no money.\n *\n * What will leave the process when you call {@link BudgetGuard.sync}: exactly the\n * {@link BudgetGuard.exportLog} JSONL (priced spend events — no prompts, no\n * content, no identifiers beyond a `label` you set).\n *\n * @param apiKey Floe agent key (`floe_<hex>`, `read_write`). Defaults to the\n * `FLOE_API_KEY` env var at `sync()` time.\n * @param options.baseUrl API base. Defaults to `FLOE_API_BASE_URL`, else prod.\n *\n * Revoke with {@link BudgetGuard.disableSync}; the guard sends nothing after.\n */\n enableSync(apiKey?: string, options: { baseUrl?: string } = {}): void {\n this.syncEnabled = true;\n this.syncKey = apiKey ?? null;\n this.syncBase = options.baseUrl ?? null;\n }\n\n /**\n * Turn ledger sync back off (revoke the opt-in). The guard sends nothing after\n * this; {@link BudgetGuard.sync} throws until {@link BudgetGuard.enableSync} is\n * called again.\n */\n disableSync(): void {\n this.syncEnabled = false;\n this.syncKey = null;\n this.syncBase = null;\n }\n\n /**\n * Push the current spend ledger to Reconcile Mode; resolve with events accepted.\n *\n * Requires {@link BudgetGuard.enableSync} first — this is the explicit send, the\n * only thing that transmits the ledger. Throws if sync isn't enabled (so a guard\n * that never opted in can never send). An empty ledger is a no-op (`0`, no\n * network). Re-syncing is safe: the server is idempotent, so already-ingested\n * events are not double-counted.\n *\n * @throws {Error} sync was not enabled (call {@link BudgetGuard.enableSync} first).\n * @throws {LedgerSyncError} the opt-in send failed (missing key, non-2xx, network).\n */\n async sync(): Promise<number> {\n // Snapshot opt-in state + key/base synchronously, before any await: a\n // disableSync() interleaved on a later microtask can't clear the key mid-call\n // (which would silently fall back to $FLOE_API_KEY) or let an upload start\n // after revocation. A disable happens-before (we throw) or happens-after (we\n // send the key we captured while still enabled).\n const enabled = this.syncEnabled;\n const key = this.syncKey;\n const base = this.syncBase;\n if (!enabled) {\n throw new Error(\n \"ledger sync is not enabled — call guard.enableSync(apiKey) first. \" +\n \"Sync is opt-in and off by default (zero-telemetry).\",\n );\n }\n return pushLedger(this.exportLog(), key ?? undefined, { baseUrl: base ?? undefined });\n }\n\n /**\n * The default next-call prediction when the caller supplies no estimate.\n * Conservative: the costlier of the last LLM call and the last tool call — a\n * mixed loop predicts the pricier kind, which at worst blocks one call early\n * (fail-closed) rather than letting a crossing call through because the LAST\n * event happened to be cheap.\n */\n private defaultEstimate(): number {\n return Math.max(this.lastLlmCost, this.lastToolCost);\n }\n\n /**\n * Subtract a settled/released hold from the in-flight tally. A handle larger\n * than EVERYTHING currently held cannot have come from a matching\n * `reserve()` — throwing beats silently clamping, which would free OTHER\n * callers' holds and fail the ceiling open. The epsilon absorbs float dust\n * from accumulating and draining many holds; per-caller over-release (a\n * handle within the total but larger than the caller's own hold) is\n * undetectable without per-handle tracking and remains the caller's\n * responsibility.\n */\n private consumeReservation(reserved: ReservationHandle): void {\n const usd = isReservation(reserved) ? reserved.usd : reserved;\n const tokens = isReservation(reserved) ? reserved.tokens : 0;\n if (usd > this.reserved + EPS) {\n throw new RangeError(\n `reserved handle (${usd}) exceeds total in-flight reservations ` +\n `(${this.reserved}) — a handle must come from a matching reserve()`,\n );\n }\n if (tokens > this.reservedTokens) {\n throw new RangeError(\n `reserved token handle (${tokens}) exceeds total in-flight token ` +\n `reservations (${this.reservedTokens}) — a handle must come from a matching reserve()`,\n );\n }\n this.reserved = Math.max(0, this.reserved - usd);\n this.reservedTokens = Math.max(0, this.reservedTokens - tokens);\n // Drain the innermost active step too (mirrors the aggregate drain). A\n // handle is drained against whatever step is innermost now — the\n // sequential-loop contract; concurrent parallel steps are out of scope (#46).\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n step.reservedUsd = Math.max(0, step.reservedUsd - usd);\n step.reservedTokens = Math.max(0, step.reservedTokens - tokens);\n }\n }\n\n /**\n * The USD amount of a handle, validated. Both shapes are checked — a raw\n * number and a {@link BudgetReservation}'s `usd`/`tokens` fields — so a bad\n * hand-rolled handle can't corrupt the in-flight tally.\n */\n /**\n * Validate a caller-supplied token estimate. Rejects a fraction, NaN,\n * Infinity, or boolean (via `Number.isInteger`) so it can't disable the\n * integer token hard-stops or corrupt the in-flight token tally. `undefined`\n * (the default) is fine; a negative integer is clamped by `Math.max(0, ...)`,\n * matching the lenient USD estimate. Mirrors Python's `_validate_token_estimate`.\n */\n private validateTokenEstimate(estimatedTokens: number | undefined): void {\n if (estimatedTokens !== undefined && !Number.isInteger(estimatedTokens)) {\n throw new RangeError(`estimatedTokens must be an integer, got ${estimatedTokens}`);\n }\n }\n\n private reservedUsdOf(reserved: ReservationHandle): number {\n if (isReservation(reserved)) {\n // A BudgetReservation is public (re-exported), so validate its fields here\n // — a hand-rolled { usd: NaN, tokens: -1 } must not slip past into\n // consumeReservation and corrupt the in-flight tallies. Mirrors Python's\n // BudgetReservation.__post_init__.\n if (!Number.isFinite(reserved.usd) || reserved.usd < 0) {\n throw new RangeError(\n `reserved.usd must be a finite, non-negative number, got ${reserved.usd}`,\n );\n }\n if (!Number.isInteger(reserved.tokens) || reserved.tokens < 0) {\n throw new RangeError(\n `reserved.tokens must be a non-negative integer, got ${reserved.tokens}`,\n );\n }\n return reserved.usd;\n }\n if (!Number.isFinite(reserved) || reserved < 0) {\n throw new RangeError(`reserved must be a finite, non-negative number, got ${reserved}`);\n }\n return reserved;\n }\n\n /**\n * Accrue a settled call into the innermost active step (no-op when none).\n * Sequential-loop contract: the innermost step owns the call.\n */\n private accrueStep(cost: number, tokens: number): void {\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n step.spentUsd += cost;\n step.spentTokens += tokens;\n }\n }\n\n /**\n * The ONE choke point, across two dimensions and two scopes. Returns the first\n * ceiling that blocks as `[dimension, scope]` — dimension `\"usd\" | \"tokens\"`,\n * scope `\"aggregate\" | \"step\"` — or `null` if the call fits everywhere.\n * Aggregate is checked before step (the guard-wide ceiling is the hard limit).\n */\n private blockingCross(\n estimateUsd: number,\n estimateTokens: number,\n ): [\"usd\" | \"tokens\", \"aggregate\" | \"step\"] | null {\n // Aggregate USD — same comparison the original check/reserve used.\n const committed = this.spentUsd + this.reserved;\n if (committed > this.limitUsd - EPS || committed + estimateUsd > this.limitUsd + EPS) {\n return [\"usd\", \"aggregate\"];\n }\n // Aggregate tokens (integers — no epsilon).\n if (this.tokenLimit !== null) {\n const committedT = this.spentTokens + this.reservedTokens;\n if (committedT >= this.tokenLimit || committedT + estimateTokens > this.tokenLimit) {\n return [\"tokens\", \"aggregate\"];\n }\n }\n // Innermost active step's caps (crossing an outer step requires first\n // crossing the inner one, so the innermost is sufficient).\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n if (step.maxUsd !== null) {\n const sCommitted = step.spentUsd + step.reservedUsd;\n if (sCommitted > step.maxUsd - EPS || sCommitted + estimateUsd > step.maxUsd + EPS) {\n return [\"usd\", \"step\"];\n }\n }\n if (step.maxTokens !== null) {\n const sCommittedT = step.spentTokens + step.reservedTokens;\n if (sCommittedT >= step.maxTokens || sCommittedT + estimateTokens > step.maxTokens) {\n return [\"tokens\", \"step\"];\n }\n }\n }\n return null;\n }\n\n /** Notify + throw the right error for a [dimension, scope] block. */\n private raiseBlock(blocked: [\"usd\" | \"tokens\", \"aggregate\" | \"step\"]): never {\n const [dimension, scope] = blocked;\n if (dimension === \"usd\") {\n this.onBlock(this.spentUsd, this.limitUsd);\n throw new BudgetExceeded(this.spentUsd, this.limitUsd);\n }\n let spentT: number;\n let limitT: number;\n if (scope === \"step\") {\n const step = this.steps[this.steps.length - 1];\n spentT = step.spentTokens + step.reservedTokens;\n limitT = step.maxTokens ?? 0;\n } else {\n spentT = this.spentTokens + this.reservedTokens;\n limitT = this.tokenLimit ?? 0;\n }\n throw new TokenBudgetExceeded(spentT, limitT, scope);\n }\n\n /**\n * Scope a per-step USD and/or token cap for a **sequential agent loop**.\n *\n * Runs `fn` with a step pushed onto the guard's stack; the\n * enforcement/accrual path honours the innermost active step *on top of* the\n * aggregate ceilings. A call that would cross the step's `maxUsd` / `maxTokens`\n * is hard-blocked ({@link BudgetExceeded} / {@link TokenBudgetExceeded} with\n * `scope: \"step\"`) even if the aggregate budget has room. `fn` receives the\n * SAME guard, so no adapter needs to know about steps. The step is popped when\n * `fn` settles or throws. **Not for concurrent parallel steps on one guard** —\n * that's out of scope for issue #46; use one guard per parallel branch.\n */\n step<T>(\n options: { maxUsd?: number; maxTokens?: number },\n fn: (guard: BudgetGuard) => T,\n ): T {\n const { maxUsd, maxTokens } = options;\n if (maxUsd !== undefined && (!Number.isFinite(maxUsd) || maxUsd < 0)) {\n throw new RangeError(`maxUsd must be a finite, non-negative number, got ${maxUsd}`);\n }\n if (maxTokens !== undefined && (!Number.isInteger(maxTokens) || maxTokens < 0)) {\n throw new RangeError(`maxTokens must be a non-negative integer, got ${maxTokens}`);\n }\n const state: StepState = {\n maxUsd: maxUsd ?? null,\n maxTokens: maxTokens ?? null,\n spentUsd: 0,\n spentTokens: 0,\n reservedUsd: 0,\n reservedTokens: 0,\n };\n this.steps.push(state);\n // Pop our own state; splice by identity so an out-of-order exit can't\n // silently drop someone else's step.\n const pop = (): void => {\n const idx = this.steps.lastIndexOf(state);\n if (idx !== -1) this.steps.splice(idx, 1);\n };\n let result: T;\n try {\n result = fn(this);\n } catch (err) {\n pop();\n throw err;\n }\n // An async `fn` returns a promise synchronously — popping now would disable\n // step enforcement across every `await` inside it (the common agent-loop\n // shape). Keep the step on the stack until the promise settles, then pop.\n if (result != null && typeof (result as { then?: unknown }).then === \"function\") {\n return (result as unknown as Promise<unknown>).finally(pop) as unknown as T;\n }\n pop();\n return result;\n }\n\n private appendEvent(event: SpendEvent): void {\n // Frozen for parity with Python's frozen dataclass: spendLog copies the\n // array but shares the event objects, so an unfrozen event would let a\n // consumer silently rewrite logged history.\n this.spendEvents.push(Object.freeze(event));\n if (this.maxLogEvents !== undefined && this.spendEvents.length > this.maxLogEvents) {\n // Ring buffer: drop the oldest overflow (at most one per append).\n this.spendEvents.splice(0, this.spendEvents.length - this.maxLogEvents);\n }\n }\n\n /**\n * Context-aware spend advisory for this budget — see {@link BudgetAdvisory}.\n *\n * `nearLimit` flips once utilization reaches `nearLimitBps` (default 80%), so an\n * agent can taper *before* the hard-stop. Advisory only: read it to adapt;\n * {@link BudgetGuard.check} is what enforces the ceiling.\n */\n advisory(): BudgetAdvisory {\n // Floor (not round) so usedBps never over-reports utilization and nearLimit\n // flips exactly when the threshold is reached; the epsilon absorbs float noise\n // and Math.floor matches Python's int() exactly (round() would diverge).\n const usedBps =\n this.limitUsd <= 0\n ? 10000\n : Math.max(0, Math.min(10000, Math.floor((this.spentUsd / this.limitUsd) * 10000 + 1e-9)));\n const remainingUsd = Math.max(0, this.limitUsd - this.spentUsd);\n // The costlier of the last LLM and tool call — the guard's own default\n // reservation estimate; 0 before any call is recorded.\n const expectedCost = Math.max(this.lastLlmCost, this.lastToolCost);\n // +1e-9 absorbs float noise so e.g. 0.6/0.2 floors to 3 not 2 (same epsilon\n // rationale as usedBps above, and keeps Python int() parity).\n const estCallsRemaining =\n expectedCost > 0 ? Math.floor(remainingUsd / expectedCost + 1e-9) : null;\n // Burn rate: spend ÷ minutes elapsed since the guard was created. null until\n // any wall-clock time has passed (avoids a divide-by-zero at t0); $0 spent over\n // real elapsed time is a legitimate 0/min, not null.\n const elapsedMin = (Date.now() - this.createdAtMs) / 60000;\n const burnRateUsdPerMin = elapsedMin > 0 ? this.spentUsd / elapsedMin : null;\n // Aggregate token utilization, mirroring usedBps. null (no signal) when the\n // token dimension is unset — a pure USD guard's advisory is unchanged.\n let tokenUsedBps: number | null = null;\n let remainingTokens: number | null = null;\n let nearToken = false;\n if (this.tokenLimit !== null) {\n tokenUsedBps =\n this.tokenLimit <= 0\n ? 10000\n : Math.max(\n 0,\n Math.min(10000, Math.floor((this.spentTokens / this.tokenLimit) * 10000 + 1e-9)),\n );\n remainingTokens = Math.max(0, this.tokenLimit - this.spentTokens);\n nearToken = tokenUsedBps >= this.nearLimitBps;\n }\n // Innermost active step's headroom + nearness (per dimension, only when\n // capped). null keeps the fields absent for old readers.\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n let stepRemainingUsd: number | null = null;\n let stepRemainingTokens: number | null = null;\n let nearStep = false;\n if (step !== null) {\n if (step.maxUsd !== null) {\n stepRemainingUsd = Math.max(0, step.maxUsd - step.spentUsd);\n if (step.maxUsd <= 0) {\n nearStep = true;\n } else {\n const sBps = Math.floor((step.spentUsd / step.maxUsd) * 10000 + 1e-9);\n nearStep = nearStep || sBps >= this.nearLimitBps;\n }\n }\n if (step.maxTokens !== null) {\n stepRemainingTokens = Math.max(0, step.maxTokens - step.spentTokens);\n if (step.maxTokens <= 0) {\n nearStep = true;\n } else {\n const sTBps = Math.floor((step.spentTokens / step.maxTokens) * 10000 + 1e-9);\n nearStep = nearStep || sTBps >= this.nearLimitBps;\n }\n }\n }\n return {\n // nearLimit now also flips when a token ceiling or the active step is near\n // its cap, so a router can downshift before ANY hard-stop.\n nearLimit: usedBps >= this.nearLimitBps || nearToken || nearStep,\n usedBps,\n // Settled budget: limit minus accrued spend, deliberately NOT net of\n // in-flight reservations. Unlike the remainingUsd getter (which subtracts\n // `reserved`), the advisory is a soft utilization signal about money already\n // spent, while the getter reports what a new call can still claim.\n remainingUsd,\n limitUsd: this.limitUsd,\n spentUsd: this.spentUsd,\n scope: \"local\",\n expectedCost,\n estCallsRemaining,\n tokenUsedBps,\n remainingTokens,\n stepRemainingUsd,\n stepRemainingTokens,\n burnRateUsdPerMin,\n };\n }\n}\n\nfunction defaultOnBlock(spentUsd: number, limitUsd: number): void {\n console.error(\n \"BUDGET EXCEEDED — call blocked\\n\" +\n ` spent so far: $${spentUsd.toFixed(6)} | ceiling: $${limitUsd.toFixed(6)}\\n` +\n \" The next call would cross your budget; floe-guard stopped your agent \" +\n \"before it ran.\",\n );\n}\n","/**\n * LatencyBudget — a cumulative tool-chain deadline, sibling to BudgetGuard.\n *\n * BudgetGuard stops an agent before its next call crosses a USD ceiling;\n * LatencyBudget stops it before the next call would blow an end-user SLA:\n *\n * ```ts\n * const deadline = new LatencyBudget(5000);\n * ...\n * deadline.check(800); // throws DeadlineExceeded when projected over\n * if (deadline.advisory().nearDeadline) useFasterModel();\n * router.pick({ maxLatencyMs: deadline.remainingMs });\n * ```\n *\n * Design notes (mirroring `src/floe_guard/latency.py`):\n * - **Monotonic clock** — `performance.now()`, never wall time.\n * - **Cooperative, not preemptive** — the guard provides the deadline signal;\n * aborting a stalled in-flight call is the framework's job (AbortSignal).\n * `check()` prevents the NEXT call from starting.\n * - **Advisory symmetry** — `nearDeadline` / `usedBps` / `remainingMs` are the\n * latency twin of BudgetGuard's `nearLimit` / `usedBps` / `remainingUsd`.\n * - **In-process scope** — one instance per request/run; distributed latency\n * tracking is out of scope.\n */\n\nimport { DeadlineExceeded } from \"./errors.js\";\n\n/** A context-aware deadline signal — the latency twin of {@link BudgetAdvisory}.\n * Soft by design; the hard-stop is {@link LatencyBudget.check}. */\nexport interface LatencyAdvisory {\n nearDeadline: boolean;\n /** SLA consumed, basis points 0..10000 (8500 = 85%). */\n usedBps: number;\n remainingMs: number;\n slaMs: number;\n elapsedMs: number;\n}\n\nexport interface LatencyBudgetOptions {\n /**\n * Utilization (basis points, 0..10000) at which {@link LatencyBudget.advisory}\n * flags `nearDeadline` so an agent can downshift to a faster path before the\n * wall. Default 8000 (80%), matching BudgetGuard's `nearLimitBps`.\n */\n nearDeadlineBps?: number;\n /** Invoked with `(elapsedMs, slaMs)` right before {@link DeadlineExceeded} is thrown. */\n onBlock?: (elapsedMs: number, slaMs: number) => void;\n /** Milliseconds-returning monotonic clock, injectable for tests. Defaults to `performance.now`. */\n clock?: () => number;\n}\n\nexport class LatencyBudget {\n readonly slaMs: number;\n readonly nearDeadlineBps: number;\n private readonly onBlock?: (elapsedMs: number, slaMs: number) => void;\n private readonly clock: () => number;\n private readonly startedAt: number;\n\n /** The budget starts counting at construction — build it when the request\n * (and its SLA) starts. */\n constructor(slaMs: number, options: LatencyBudgetOptions = {}) {\n if (!(Number.isFinite(slaMs) && slaMs > 0)) {\n throw new RangeError(\"slaMs must be > 0\");\n }\n const nearDeadlineBps = options.nearDeadlineBps ?? 8000;\n if (!Number.isInteger(nearDeadlineBps) || nearDeadlineBps < 0 || nearDeadlineBps > 10000) {\n throw new RangeError(\"nearDeadlineBps must be an integer 0..10000\");\n }\n this.slaMs = slaMs;\n this.nearDeadlineBps = nearDeadlineBps;\n this.onBlock = options.onBlock;\n this.clock = options.clock ?? (() => performance.now());\n this.startedAt = this.clock();\n }\n\n /** Milliseconds since construction (monotonic). */\n get elapsedMs(): number {\n return this.clock() - this.startedAt;\n }\n\n /** Milliseconds left before the SLA, floored at 0 — the readable signal a\n * router uses to pick a faster fallback or truncate work mid-chain. */\n get remainingMs(): number {\n return Math.max(0, this.slaMs - this.elapsedMs);\n }\n\n /**\n * Throw {@link DeadlineExceeded} when the projected elapsed time (now +\n * `expectedMs` for the upcoming call) would blow the SLA. Call it\n * immediately before each tool/model call; pass 0 to only gate on time\n * already spent.\n */\n check(expectedMs = 0): void {\n if (!(Number.isFinite(expectedMs) && expectedMs >= 0)) {\n throw new RangeError(\"expectedMs must be >= 0\");\n }\n const elapsed = this.elapsedMs;\n if (elapsed + expectedMs > this.slaMs) {\n this.onBlock?.(elapsed, this.slaMs);\n throw new DeadlineExceeded(elapsed, this.slaMs);\n }\n }\n\n /** The soft near-deadline signal — symmetric to `BudgetGuard.advisory()`. */\n advisory(): LatencyAdvisory {\n const elapsed = this.elapsedMs;\n // round (not floor) — matches the Python twin; float clock arithmetic can\n // land a hair under an exact boundary.\n const usedBps = elapsed > 0 ? Math.min(10000, Math.round((elapsed * 10000) / this.slaMs)) : 0;\n return {\n nearDeadline: usedBps >= this.nearDeadlineBps,\n usedBps,\n remainingMs: Math.max(0, this.slaMs - elapsed),\n slaMs: this.slaMs,\n elapsedMs: elapsed,\n };\n }\n}\n","/**\n * Vercel AI SDK middleware that enforces a {@link BudgetGuard} in the call path.\n *\n * This is the TypeScript counterpart to the Python framework adapters. The AI SDK\n * is TypeScript-only, so it ships as its own npm package.\n *\n * Works with BOTH `ai@4` (`LanguageModelV1Middleware`) and `ai@5`\n * (`LanguageModelV2Middleware`). The two majors renamed the middleware type and\n * the usage fields (`promptTokens`/`completionTokens` → `inputTokens`/\n * `outputTokens`), so this module deliberately imports nothing from `ai`: the\n * middleware is typed structurally against the surface both majors share, and\n * usage is read from whichever field pair the installed SDK reports.\n *\n * - `wrapGenerate({ doGenerate, model })` — we `reserve()` (throws to hard-stop)\n * BEFORE calling `doGenerate()`, hold the reservation across the await, then\n * `settle()` from `result.usage`.\n * - `wrapStream({ doStream, model })` — we `reserve()` BEFORE `doStream()`, then\n * `settle()` from the `finish` part as the stream drains.\n *\n * Reserving before the await is what makes parallel calls (`Promise.all` over\n * several generations) honour the ceiling: each holds its slice instead of all\n * reading the same stale total (issue #18). The reservation is released if the\n * call throws, or if a stream ends without reporting usage.\n *\n * The model id used for pricing comes from `model.modelId`.\n */\n\nimport type { BudgetGuard } from \"./guard.js\";\n\n/**\n * The middleware call surface shared by `ai@4` and `ai@5`. Both majors invoke\n * `wrapGenerate`/`wrapStream` with an options object carrying these members\n * (plus richer `model` fields we don't read). `doGenerate`/`doStream` are\n * declared optional so every 4.x/5.x minor's options type stays assignable —\n * the SDK always provides the one each hook actually calls.\n */\ninterface MiddlewareCallOptions {\n doGenerate?: () => PromiseLike<any>;\n doStream?: () => PromiseLike<any>;\n model: { modelId: string };\n params?: unknown;\n}\n\n/**\n * Structural stand-in for `LanguageModelV1Middleware` (ai@4) and\n * `LanguageModelV2Middleware` (ai@5) — assignable to the `middleware` option of\n * `wrapLanguageModel` on either major.\n */\nexport interface BudgetGuardMiddleware {\n wrapGenerate: (options: MiddlewareCallOptions) => Promise<any>;\n wrapStream: (options: MiddlewareCallOptions) => Promise<any>;\n}\n\n/**\n * Read prompt/completion token counts from an ai@4 usage object\n * (`promptTokens`/`completionTokens`) or an ai@5 one (`inputTokens`/\n * `outputTokens`). Throws when either count is missing or non-numeric: the guard\n * cannot meter spend it cannot see, and treating it as $0 would fail open.\n */\nfunction usageTokens(\n modelId: string,\n usage: unknown,\n): { promptTokens: number; completionTokens: number } {\n const u = usage as\n | {\n promptTokens?: unknown;\n completionTokens?: unknown;\n inputTokens?: unknown;\n outputTokens?: unknown;\n }\n | null\n | undefined;\n const promptTokens = u?.promptTokens ?? u?.inputTokens;\n const completionTokens = u?.completionTokens ?? u?.outputTokens;\n if (typeof promptTokens !== \"number\" || typeof completionTokens !== \"number\") {\n throw new Error(\n `Model '${modelId}' reported no token usage — the budget guard cannot ` +\n `meter spend it cannot see, so this call is rejected rather than ` +\n `treated as free.`,\n );\n }\n return { promptTokens, completionTokens };\n}\n\n/**\n * Build a budget-guard middleware that hard-stops the model before a call\n * crosses the guard's USD ceiling, and records priced token usage after.\n * Compatible with `wrapLanguageModel` from both `ai@4` and `ai@5`.\n *\n * @example\n * import { wrapLanguageModel } from \"ai\";\n * import { openai } from \"@ai-sdk/openai\";\n * import { BudgetGuard, budgetGuardMiddleware } from \"floe-guard\";\n *\n * const guard = new BudgetGuard(5.00);\n * const model = wrapLanguageModel({\n * model: openai(\"gpt-4o\"),\n * middleware: budgetGuardMiddleware(guard),\n * });\n */\nexport function budgetGuardMiddleware(guard: BudgetGuard): BudgetGuardMiddleware {\n return {\n async wrapGenerate({ doGenerate, model }) {\n const reserved = guard.reserve(); // throws BudgetExceeded before the call runs\n let result: any;\n try {\n result = await doGenerate!();\n } catch (err) {\n guard.release(reserved); // the call failed before settle() took ownership\n throw err;\n }\n // From here settle() OWNS the reservation: it releases the hold on its own\n // throw (unpriceable / non-finite cost) and consumes it on success. Releasing\n // again would double-subtract and clear a concurrent call's in-flight hold.\n let handled = false;\n try {\n const { promptTokens, completionTokens } = usageTokens(\n model.modelId,\n result?.usage,\n );\n handled = true;\n guard.settle(model.modelId, promptTokens, completionTokens, { reserved });\n return result;\n } catch (err) {\n if (!handled) guard.release(reserved); // failed reading usage, before settle()\n throw err;\n }\n },\n\n async wrapStream({ doStream, model }) {\n const reserved = guard.reserve(); // throws BudgetExceeded before the stream starts\n let streamResult: any;\n try {\n streamResult = await doStream!();\n } catch (err) {\n guard.release(reserved); // the call failed before settle() took ownership\n throw err;\n }\n const { stream, ...rest } = streamResult as {\n stream: ReadableStream<any>;\n } & Record<string, unknown>;\n\n // `handled` flips once the reservation is disposed — settled on the finish\n // part, or released exactly once if the stream produced no usage (flush) or\n // ended early via error/cancellation (cancel). settle() owns disposal once\n // called, so nothing else may release after `handled` is set, or we'd\n // double-subtract and clear a concurrent call's hold.\n let handled = false;\n const guarded = stream.pipeThrough(\n new TransformStream<any, any>({\n transform(chunk, controller) {\n if (chunk?.type === \"finish\" && !handled) {\n try {\n const { promptTokens, completionTokens } = usageTokens(\n model.modelId,\n chunk.usage,\n );\n handled = true;\n guard.settle(model.modelId, promptTokens, completionTokens, { reserved });\n } catch (err) {\n // Release only if we never reached settle() (e.g. usage missing).\n // If settle() itself threw, it already released its own hold.\n if (!handled) {\n handled = true;\n guard.release(reserved);\n }\n throw err;\n }\n }\n controller.enqueue(chunk);\n },\n flush() {\n // Clean close with no finish/usage part — free the held budget.\n if (!handled) {\n handled = true;\n guard.release(reserved);\n }\n },\n cancel() {\n // Upstream error or consumer cancellation: flush() does not run here\n // (Web Streams: flush and cancel are mutually exclusive), so release\n // the still-held reservation.\n if (!handled) {\n handled = true;\n guard.release(reserved);\n }\n },\n // `cancel` is valid per the Streams spec and supported in Node 18+, but\n // TS's Transformer lib type lags and omits it — cast to keep the type check.\n } as Transformer<any, any>),\n );\n\n return { stream: guarded, ...rest };\n },\n };\n}\n","import { BudgetExceeded } from \"./errors.js\";\nimport { type BudgetAdvisory, BudgetGuard } from \"./guard.js\";\n\nexport interface RetryPlan<T> {\n /** Operation to run for this retry attempt. */\n call: () => T | Promise<T>;\n /**\n * Estimated retry cost passed to `guard.check()` before the retry runs.\n * Leave undefined to use the guard's default last-call estimate.\n */\n estimatedCost?: number;\n}\n\nexport interface BudgetRetryOptions<T> {\n /** Estimated cost for retrying the original call. */\n estimatedCost?: number;\n /** Total attempts, including the first call. Default: 2. */\n maxAttempts?: number;\n /** Choose a cheaper retry plan when `guard.advisory().nearLimit` is true. */\n onDegrade?: (\n error: unknown,\n advisory: BudgetAdvisory,\n ) => RetryPlan<T> | Promise<RetryPlan<T> | undefined> | undefined;\n /** Decide whether an error is retryable. Defaults to all non-budget errors. */\n retryIf?: (error: unknown) => boolean;\n}\n\nfunction defaultRetryIf(error: unknown): boolean {\n return !(error instanceof BudgetExceeded);\n}\n\n/**\n * Run `call` with budget-aware retries.\n *\n * The first attempt runs unchanged. If it fails with a retryable error, the\n * helper retries as-is with ample budget, asks `onDegrade` for a cheaper plan\n * when near the limit, and always calls `guard.check(estimatedCost)` before a\n * retry so an over-budget retry is blocked before it runs.\n */\nexport async function withBudgetRetry<T>(\n guard: BudgetGuard,\n call: () => T | Promise<T>,\n options: BudgetRetryOptions<T> = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? 2;\n if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {\n throw new RangeError(`maxAttempts must be an integer >= 1, got ${maxAttempts}`);\n }\n const retryIf = options.retryIf ?? defaultRetryIf;\n let plan: RetryPlan<T> = { call, estimatedCost: options.estimatedCost };\n\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n try {\n return await plan.call();\n } catch (error) {\n if (attempt >= maxAttempts || !retryIf(error)) {\n throw error;\n }\n plan = await nextPlan(guard, error, plan, options.onDegrade);\n }\n }\n\n throw new Error(\"unreachable\");\n}\n\nasync function nextPlan<T>(\n guard: BudgetGuard,\n error: unknown,\n current: RetryPlan<T>,\n onDegrade: BudgetRetryOptions<T>[\"onDegrade\"],\n): Promise<RetryPlan<T>> {\n const advisory = guard.advisory();\n if (advisory.nearLimit && onDegrade !== undefined) {\n const degraded = await onDegrade(error, advisory);\n if (degraded !== undefined) {\n guard.check(degraded.estimatedCost);\n return degraded;\n }\n }\n guard.check(current.estimatedCost);\n return current;\n}\n","/**\n * Offline pricing for voice legs (STT / TTS / telephony) from the vendored map.\n *\n * The voice twin of `pricing.ts`. The token cost map is per-token; a voice\n * pipeline's bill is per-second (STT), per-1k-chars (TTS), and per-minute\n * (telephony) instead, so the voice rates live under the reserved `\"__voice__\"`\n * key of `cost_map.json` with their own schema:\n *\n * \"deepgram-nova-3\": {\n * \"mode\": \"stt\", // discriminator: \"stt\" | \"tts\" | \"telephony\"\n * \"unit\": \"usd_per_second\", // explicit unit — a mismatch fails closed\n * \"rate\": 0.0001283333, // USD in that unit\n * \"provider\": \"deepgram\"\n * }\n *\n * Same fail-closed contract as token pricing: a vendor absent from the map (or an\n * entry whose `unit`/`mode` does not match the leg it is asked to price) is\n * unpriceable — {@link lookupVoiceRate} returns `null` and the adapter throws\n * {@link UnpriceableVoiceError} rather than silently metering the leg at $0. Pass a\n * per-unit override to enforce a leg the map cannot price.\n *\n * No network. The rates are a **drift-prone snapshot** of each vendor's public\n * list price — refresh them (`scripts/update-cost-map.mjs`) like the token map, or\n * estimates drift as vendors change prices. Telephony is **US-only in v1**.\n *\n * This is a faithful port of `src/floe_guard/voice_pricing.py`.\n */\n\nimport costMapJson from \"./cost_map.json\";\nimport { UnpriceableVoiceError } from \"./errors.js\";\n\nexport type VoiceMode = \"stt\" | \"tts\" | \"telephony\";\n\ninterface VoiceMapEntry {\n mode?: unknown;\n unit?: unknown;\n rate?: unknown;\n provider?: unknown;\n}\n\n/** The vendored voice rates — the reserved `\"__voice__\"` section of the cost map. */\nconst VOICE_MAP = ((costMapJson as Record<string, unknown>)[\"__voice__\"] ?? {}) as Record<\n string,\n VoiceMapEntry\n>;\n\n/**\n * The one unit each leg is billed in. An entry whose `unit` disagrees with its\n * leg is a schema mismatch and fails closed — a Deepgram $/min figure stored\n * without the ÷60 conversion would over-bill 60x if it were silently accepted.\n */\nexport const unitForMode: Record<VoiceMode, string> = {\n stt: \"usd_per_second\",\n tts: \"usd_per_1k_chars\",\n telephony: \"usd_per_minute\",\n};\n\n/** A resolved per-unit voice rate plus where it came from. */\nexport interface VoiceRate {\n mode: string;\n unit: string;\n rate: number;\n source: \"override\" | \"cost_map\";\n}\n\nfunction finiteNonNegative(value: unknown): value is number {\n // typeof excludes booleans (Python's _finite_non_negative excludes bool too).\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0;\n}\n\n/**\n * Resolve a voice vendor to its per-unit rate, or `null` if unpriceable.\n *\n * The pure resolver (the voice twin of {@link resolvePrice}): it never throws.\n * Fail-closed on every mismatch — a missing vendor, an entry whose `mode` is not\n * `mode`, an entry whose `unit` is not the canonical unit for `mode`, or a\n * non-finite/negative rate — so a schema slip can never surface as a silent,\n * valid-looking price.\n */\nexport function lookupVoiceRate(model: string | null | undefined, mode: VoiceMode): number | null {\n if (model === null || model === undefined) return null;\n const entry = Object.prototype.hasOwnProperty.call(VOICE_MAP, model)\n ? VOICE_MAP[model]\n : undefined;\n if (!entry) return null;\n if (entry.mode !== mode) return null;\n if (entry.unit !== unitForMode[mode]) return null;\n const rate = entry.rate;\n if (!finiteNonNegative(rate)) return null;\n return rate;\n}\n\n/**\n * Resolve a leg's per-unit rate, fail-closed. An `override` wins over the map.\n *\n * Throws {@link UnpriceableVoiceError} when neither an override nor the bundled\n * map can price the leg — the guard refuses to meter spend it cannot measure.\n */\nexport function resolveVoiceRate(\n model: string | null | undefined,\n mode: VoiceMode,\n override?: number | null,\n): VoiceRate {\n if (override !== undefined && override !== null) {\n if (!finiteNonNegative(override)) {\n throw new RangeError(\n `voice ${mode} override must be a finite, non-negative number, got ${override}`,\n );\n }\n return { mode, unit: unitForMode[mode], rate: override, source: \"override\" };\n }\n const rate = lookupVoiceRate(model, mode);\n if (rate === null) throw new UnpriceableVoiceError(model ?? null, mode);\n return { mode, unit: unitForMode[mode], rate, source: \"cost_map\" };\n}\n\n/**\n * USD for one leg. `quantity` is seconds (stt), characters (tts), or minutes\n * (telephony); negative quantities clamp to zero.\n */\nexport function voiceLegCost(mode: VoiceMode, quantity: number, rate: number): number {\n // Reject non-finite quantities: Math.max(0, NaN) is NaN, so a NaN/Infinity\n // quantity would return a non-finite USD amount that then poisons the guard's\n // running total (NaN disables every ceiling comparison). Negatives still clamp.\n if (!Number.isFinite(quantity)) {\n throw new RangeError(`voice ${mode} quantity must be a finite number, got ${quantity}`);\n }\n const q = Math.max(0, quantity);\n if (mode === \"stt\") return q * rate; // seconds * $/second\n if (mode === \"tts\") return (q / 1000) * rate; // chars / 1000 * $/1k-chars\n if (mode === \"telephony\") return q * rate; // minutes * $/minute\n throw new RangeError(`unknown voice mode ${mode}`);\n}\n\n/**\n * USD for one voice leg, or `null` when the leg is unconfigured (skip it).\n *\n * The single entry point the adapters use. A leg with neither a vendor (`model`)\n * nor an `override` is unconfigured — return `null` so the token-only contract is\n * preserved and nothing is metered. A leg that *is* configured but cannot be\n * priced throws {@link UnpriceableVoiceError} (via {@link resolveVoiceRate}) rather\n * than accruing a silent $0.\n */\nexport function priceVoiceLeg(\n mode: VoiceMode,\n quantity: number,\n options: { model?: string | null; override?: number | null } = {},\n): number | null {\n const model = options.model ?? null;\n const override = options.override ?? null;\n if (model === null && override === null) return null;\n const resolved = resolveVoiceRate(model, mode, override);\n return voiceLegCost(mode, quantity, resolved.rate);\n}\n","/**\n * Pre-call admission gates for voice orchestrators.\n *\n * These decide **at the call boundary** whether to *admit* or *reject* an incoming\n * call, from the guard's remaining budget. They return the exact JSON shape each\n * provider's inbound webhook expects, so a free/local user can reject a call on\n * budget exhaustion with the **same contract** the hosted gateway serves — the paid\n * upgrade is a URL swap, not a rewrite.\n *\n * Scope — strictly pre-call. A gate answers one question: *given the budget left,\n * do we let this call start?* It does **not** intervene mid-call. Once a call is\n * admitted it runs to completion; nothing here cuts a call off partway.\n *\n * Budget, not balance: admission reads the guard's local *remaining budget*, a\n * ceiling signal — not an account balance. US-only telephony, v1.\n *\n * A faithful port of `src/floe_guard/gates.py`.\n */\n\nimport type { BudgetGuard } from \"./guard.js\";\n\n/**\n * True when there isn't budget left to admit one more call.\n *\n * A **non-binding preflight read**: it checks the guard's `remainingUsd` but does\n * *not* reserve anything, so under concurrent inbound calls it can admit more than\n * the budget strictly covers (two calls may see the same headroom and both pass).\n * It is coarse admission control — the binding, atomic hard-stop is the in-call\n * guard (`check` / `reserve` while the call runs). This mirrors the hosted pre-dial\n * gate, which is likewise check-only.\n *\n * With the default `estimatedCallUsd = 0` a call is rejected only once the budget\n * is fully spent (`remainingUsd` at or below zero). Pass an estimate (e.g.\n * `$/min × expected minutes`) to reject earlier, when the remaining budget can't\n * cover it; an estimate that *exactly* equals the remaining budget still admits\n * (inclusive ceiling, matching `BudgetGuard.reserve`). Budget signal, not a balance.\n *\n * @throws RangeError when `estimatedCallUsd` is negative or non-finite — a bad\n * estimate must not silently admit an exhausted guard (NaN/negative comparisons\n * are always false, the same trap `BudgetGuard` rejects for `limitUsd`).\n */\nexport function budgetExhausted(\n guard: BudgetGuard,\n options: { estimatedCallUsd?: number } = {},\n): boolean {\n // Default ONLY undefined — a `null` estimate is malformed input and must reach\n // validation (below) rather than being silently coerced to 0 (which would admit\n // the call with no intended headroom). Mirrors Python raising on a None estimate.\n const estimatedCallUsd = options.estimatedCallUsd === undefined ? 0 : options.estimatedCallUsd;\n if (!Number.isFinite(estimatedCallUsd) || estimatedCallUsd < 0) {\n throw new RangeError(\n `estimatedCallUsd must be a finite, non-negative number, got ${estimatedCallUsd}`,\n );\n }\n const remainingUsd = guard.remainingUsd;\n return remainingUsd <= 0 || remainingUsd < estimatedCallUsd;\n}\n\n/**\n * Generic pre-call admission decision: `true` = admit, `false` = reject.\n *\n * The provider-agnostic gate for Pipecat, custom telephony, or any inbound hook\n * without a first-class helper below. Wire the `false` case into your provider's\n * reject path.\n *\n * TODO(bland): Bland's *Send Call* metadata field name is an OPEN VERIFICATION\n * ITEM (pending confirmation) — this package does not invent it. Use `preCall` to\n * get the admit/reject decision and map `false` onto Bland's Pathway Webhook node\n * once the field name is confirmed.\n */\nexport function preCall(guard: BudgetGuard, options: { estimatedCallUsd?: number } = {}): boolean {\n return !budgetExhausted(guard, options);\n}\n\n/**\n * Response for Retell's inbound-call webhook — reject or admit the call.\n *\n * On budget exhaustion returns `{ call_inbound: { reject: true } }`; otherwise\n * `{ call_inbound: {...} }` carrying any `admit` overrides you pass\n * (`dynamic_variables`, `metadata`, `override_agent_id`, …).\n *\n * Retell contract (verified against docs.retellai.com/features/inbound-call-webhook):\n * **only the boolean `true` rejects** — any other value is ignored. The webhook\n * fires for inbound **phone and SMS** calls (not dial-to-sip); its behaviour for\n * web calls is not documented, so treat this as phone/SMS-only. 10s timeout, up to\n * 3 retries.\n */\nexport function retell(\n guard: BudgetGuard,\n options: { estimatedCallUsd?: number; admit?: Record<string, unknown> } = {},\n): { call_inbound: Record<string, unknown> } {\n if (budgetExhausted(guard, { estimatedCallUsd: options.estimatedCallUsd })) {\n return { call_inbound: { reject: true } };\n }\n // The gate's budget decision — not the caller's overrides — controls admission.\n // Strip `reject` from admit overrides so an errant `admit: { reject: true }` on\n // an available-budget call can't flip the response into Retell's reject shape.\n const safeAdmit = { ...(options.admit ?? {}) };\n delete safeAdmit.reject;\n return { call_inbound: safeAdmit };\n}\n\n/**\n * Response for Vapi's `assistant-request` webhook — reject or admit the call.\n *\n * On budget exhaustion returns `{ error: errorMessage }` (Vapi speaks it, then ends\n * the call). Otherwise admits with `{ assistantId }` if given (`assistantId` takes\n * precedence), else `{ assistant }`.\n *\n * Vapi has **no** `reject: true` boolean — rejection *is* returning an error, and\n * admission *is* returning an assistant. Respond within **~7.5 s** or the call may\n * fail (verified against docs.vapi.ai/server-url/spam-call-rejection).\n *\n * @throws RangeError when admitted (budget available) but neither `assistant` nor\n * `assistantId` was provided — there'd be nothing to hand Vapi.\n */\nexport function vapi(\n guard: BudgetGuard,\n options: {\n assistant?: Record<string, unknown>;\n assistantId?: string;\n errorMessage?: string;\n estimatedCallUsd?: number;\n } = {},\n): Record<string, unknown> {\n const {\n assistant,\n assistantId,\n errorMessage = \"Sorry, this agent is out of budget right now.\",\n estimatedCallUsd,\n } = options;\n if (budgetExhausted(guard, { estimatedCallUsd })) {\n return { error: errorMessage };\n }\n if (assistantId !== undefined && assistantId !== null) {\n return { assistantId };\n }\n if (assistant !== undefined && assistant !== null) {\n return { assistant };\n }\n throw new RangeError(\n \"vapi() admitted the call but has nothing to return: pass assistant or \" +\n \"assistantId for the admit path.\",\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACWO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAQO,IAAM,iBAAN,cAA6B,eAAe;AAAA,EACxC;AAAA,EACA;AAAA,EAET,YAAY,UAAkB,UAAkB,SAAkB;AAGhE;AAAA,MACE,WACE,+CAA0C,SAAS,QAAQ,CAAC,CAAC,QAAQ,SAAS,QAAQ,CAAC,CAAC;AAAA,IAC5F;AACA,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAClB;AACF;AAcO,IAAM,sBAAN,cAAkC,eAAe;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,aAAqB,aAAqB,OAA6B;AAIjF;AAAA,MACE;AAAA,MACA;AAAA,MACA,8CAAyC,KAAK,KAAK,WAAW,OACzD,WAAW;AAAA,IAClB;AACA,SAAK,OAAO;AACZ,SAAK,cAAc;AACnB,SAAK,cAAc;AACnB,SAAK,QAAQ;AAAA,EACf;AACF;AASO,IAAM,wBAAN,cAAoC,eAAe;AAAA,EAC/C;AAAA,EAET,YAAY,OAAe;AACzB;AAAA,MACE,uBAAuB,KAAK;AAAA,IAG9B;AACA,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;AAeO,IAAM,wBAAN,cAAoC,eAAe;AAAA,EAC/C;AAAA,EACA;AAAA,EAET,YAAY,QAAuB,MAAc;AAE/C,UAAM,QAAQ,WAAW,OAAO,SAAS,IAAI,MAAM;AACnD;AAAA,MACE,gBAAgB,IAAI,WAAW,KAAK,8EACY,IAAI;AAAA,IAGtD;AACA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;AAcO,IAAM,kBAAN,cAA8B,eAAe;AAAA,EAClD,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAkBA,SAAS,YAAY,IAAoB;AACvC,SAAO,KAAK,MAAM,KAAK,GAAG;AAC5B;AAEO,IAAM,mBAAN,cAA+B,eAAe;AAAA,EAC1C;AAAA,EACA;AAAA,EAET,YAAY,WAAmB,OAAe;AAC5C;AAAA,MACE,kDAA6C,YAAY,SAAS,CAAC,SAAS,YAAY,KAAK,CAAC;AAAA,IAChG;AACA,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,QAAQ;AAAA,EACf;AACF;;;ACtJA,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAM3B,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,gBAAgB,CAAC,aAAa,QAAQ,iBAAiB,UAAU;AAOvE,SAAS,OAAO,MAAsB;AACpC,QAAM,OAAQ,WAA0E;AACxF,SAAO,MAAM,MAAM,IAAI,KAAK;AAC9B;AAgBA,SAAS,eAAe,OAAqB;AAC3C,MAAI,IAAI;AACR,aAAW,OAAO,MAAM,MAAM,IAAI,GAAG;AACnC,SAAK;AACL,UAAM,OAAO,IAAI,KAAK;AACtB,QAAI,CAAC,KAAM;AACX,QAAI;AACJ,QAAI;AACF,cAAQ,KAAK,MAAM,IAAI;AAAA,IACzB,SAAS,KAAK;AACZ,YAAM,IAAI,gBAAgB,eAAe,CAAC,uBAAuB,cAAc,GAAG,CAAC,EAAE;AAAA,IACvF;AACA,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,YAAM,IAAI,gBAAgB,eAAe,CAAC,wBAAwB;AAAA,IACpE;AACA,UAAM,KAAK;AACX,UAAM,QAAQ,OAAO,KAAK,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC;AAChE,QAAI,MAAM,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR,eAAe,CAAC,+CACX,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC;AAAA,MAEnC;AAAA,IACF;AACA,UAAM,UAAU,cAAc,OAAO,CAAC,MAAM,EAAE,KAAK,GAAG;AACtD,QAAI,QAAQ,QAAQ;AAClB,YAAM,IAAI;AAAA,QACR,eAAe,CAAC,kCAAkC,KAAK,UAAU,OAAO,CAAC;AAAA,MAC3E;AAAA,IACF;AACA,QAAI,GAAG,SAAS,SAAS,GAAG,SAAS,QAAQ;AAC3C,YAAM,IAAI;AAAA,QACR,eAAe,CAAC,uCAAuC,KAAK,UAAU,GAAG,IAAI,CAAC;AAAA,MAChF;AAAA,IACF;AACA,QAAI,OAAO,GAAG,kBAAkB,UAAU;AACxC,YAAM,IAAI,gBAAgB,eAAe,CAAC,mCAAmC;AAAA,IAC/E;AACA,UAAM,OAAO,GAAG;AAChB,QAAI,OAAO,SAAS,YAAY,CAAC,OAAO,SAAS,IAAI,KAAK,OAAO,GAAG;AAClE,YAAM,IAAI;AAAA,QACR,eAAe,CAAC,yDAAyD,KAAK,UAAU,IAAI,CAAC;AAAA,MAC/F;AAAA,IACF;AACA,UAAM,KAAK,GAAG;AACd,QAAI,OAAO,OAAO,YAAY,CAAC,OAAO,SAAS,EAAE,GAAG;AAClD,YAAM,IAAI;AAAA,QACR,eAAe,CAAC,4CAA4C,KAAK,UAAU,EAAE,CAAC;AAAA,MAChF;AAAA,IACF;AACA,eAAW,SAAS,CAAC,iBAAiB,mBAAmB,GAAY;AACnE,YAAM,QAAQ,GAAG,KAAK;AAGtB,UAAI,UAAU,UAAa,UAAU,QAAQ,CAAC,OAAO,UAAU,KAAK,GAAG;AACrE,cAAM,IAAI,gBAAgB,eAAe,CAAC,KAAK,KAAK,8BAA8B;AAAA,MACpF;AAAA,IACF;AACA,QAAI,WAAW,MAAM,OAAO,GAAG,UAAU,UAAU;AACjD,YAAM,IAAI,gBAAgB,eAAe,CAAC,2BAA2B;AAAA,IACvE;AACA,QAAI,cAAc,IAAI;AACpB,YAAM,WAAW,GAAG;AACpB,UAAI,OAAO,aAAa,YAAY,CAAC,OAAO,SAAS,QAAQ,GAAG;AAC9D,cAAM,IAAI,gBAAgB,eAAe,CAAC,qCAAqC;AAAA,MACjF;AAAA,IACF;AAAA,EACF;AACF;AAkBA,eAAsB,WACpB,OACA,QACA,OAAiD,CAAC,GACjC;AAEjB,MAAI,CAAC,MAAM,KAAK,EAAG,QAAO;AAI1B,iBAAe,KAAK;AAEpB,QAAM,QAAQ,UAAU,OAAO,OAAO,gBAAgB,GAAG,KAAK;AAC9D,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR,uCAAuC,gBAAgB;AAAA,IAEzD;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,qBAAqB,EAAE,KAAK;AACnD,QAAM,SAAS,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AAC5F,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,IAAI;AAAA,EACvB,QAAQ;AAGN,UAAM,IAAI;AAAA,MACR,mCAAmC,IAAI;AAAA,IAEzC;AAAA,EACF;AACA,MAAI,OAAO,aAAa,YAAY,CAAC,OAAO,MAAM;AAEhD,UAAM,IAAI;AAAA,MACR,mCAAmC,IAAI;AAAA,IAEzC;AAAA,EACF;AACA,QAAM,MAAM,GAAG,IAAI,GAAG,gBAAgB;AAEtC,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,aAAa,kBAAkB;AACvF,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,KAAK;AAAA,MAC1B,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,MAKR,UAAU;AAAA,MACV,SAAS;AAAA,QACP,eAAe,UAAU,GAAG;AAAA,QAC5B,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,MACA,MAAM;AAAA,MACN,QAAQ,WAAW;AAAA,IACrB,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,IAAI,gBAAgB,2BAA2B,GAAG,KAAK,cAAc,GAAG,CAAC,EAAE;AAAA,EACnF,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,gBAAgB,MAAM,kBAAkB,QAAQ,CAAC;AAAA,EAC7D;AAEA,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,MAAM,SAAS,KAAK,CAAC;AAAA,EAC5C,SAAS,KAAK;AACZ,UAAM,IAAI,gBAAgB,+BAA+B,GAAG,KAAK,cAAc,GAAG,CAAC,EAAE;AAAA,EACvF;AACA,MAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,MAAM,QAAQ,OAAO,GAAG;AAC7E,UAAM,IAAI,gBAAgB,0CAA0C,GAAG,GAAG;AAAA,EAC5E;AAIA,QAAM,SAAU,QAAoC;AACpD,MAAI,WAAW,UAAa,WAAW,KAAM,QAAO;AACpD,MAAI,OAAO,WAAW,YAAY,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,GAAG;AACzE,UAAM,IAAI,gBAAgB,uCAAuC,GAAG,KAAK,KAAK,UAAU,MAAM,CAAC,GAAG;AAAA,EACpG;AACA,SAAO;AACT;AAEA,SAAS,cAAc,KAAsB;AAC3C,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAEA,eAAe,kBAAkB,UAAqC;AACpE,MAAI,SAAS;AACb,MAAI;AACF,UAAM,OAAgB,KAAK,MAAM,MAAM,SAAS,KAAK,CAAC;AACtD,QACE,OAAO,SAAS,YAChB,SAAS,QACT,OAAQ,KAAiC,UAAU,UACnD;AACA,eAAS,KAAM,KAAiC,KAAe;AAAA,IACjE;AAAA,EACF,QAAQ;AAAA,EAER;AACA,QAAM,OAAO,SAAS;AACtB,MAAI,SAAS,IAAK,QAAO,+CAA+C,MAAM;AAC9E,MAAI,SAAS,KAAK;AAChB,WACE,wCAAwC,MAAM;AAAA,EAGlD;AACA,SAAO,sBAAsB,IAAI,GAAG,MAAM;AAC5C;;;ACxRA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA,EACE,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,8BAA8B;AAAA,IAC5B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,2BAA2B;AAAA,IACzB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,4BAA4B;AAAA,IAC1B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,kBAAkB;AAAA,IAChB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mBAAmB;AAAA,IACjB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,4BAA4B;AAAA,IAC1B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mBAAmB;AAAA,IACjB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,4BAA4B;AAAA,IAC1B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mBAAmB;AAAA,IACjB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,4BAA4B;AAAA,IAC1B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mBAAmB;AAAA,IACjB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,4BAA4B;AAAA,IAC1B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mBAAmB;AAAA,IACjB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,IACf,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,4BAA4B;AAAA,IAC1B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,8BAA8B;AAAA,IAC5B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mBAAmB;AAAA,IACjB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,IACf,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,8BAA8B;AAAA,IAC5B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,8BAA8B;AAAA,IAC5B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,2CAA2C;AAAA,IACzC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,uCAAuC;AAAA,IACrC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yCAAyC;AAAA,IACvC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wCAAwC;AAAA,IACtC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iDAAiD;AAAA,IAC/C,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iDAAiD;AAAA,IAC/C,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oCAAoC;AAAA,IAClC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,kBAAkB;AAAA,IAChB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,8BAA8B;AAAA,IAC5B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iCAAiC;AAAA,IAC/B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iCAAiC;AAAA,IAC/B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sCAAsC;AAAA,IACpC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,8BAA8B;AAAA,IAC5B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mBAAmB;AAAA,IACjB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,uBAAuB;AAAA,IACrB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,4BAA4B;AAAA,IAC1B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,kCAAkC;AAAA,IAChC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,kCAAkC;AAAA,IAChC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,gCAAgC;AAAA,IAC9B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,IACf,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,IACZ,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,IACZ,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,eAAe;AAAA,IACb,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,uBAAuB;AAAA,IACrB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,gBAAgB;AAAA,IACd,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,2BAA2B;AAAA,IACzB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,gBAAgB;AAAA,IACd,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,2BAA2B;AAAA,IACzB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,UAAU;AAAA,IACR,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mCAAmC;AAAA,IACjC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,mCAAmC;AAAA,IACjC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,eAAe;AAAA,IACb,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wCAAwC;AAAA,IACtC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,8BAA8B;AAAA,IAC5B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yCAAyC;AAAA,IACvC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oCAAoC;AAAA,IAClC,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,IACZ,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,qBAAqB;AAAA,IACnB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,IACZ,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,IACZ,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,yBAAyB;AAAA,IACvB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,oBAAoB;AAAA,IAClB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,+BAA+B;AAAA,IAC7B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,uBAAuB;AAAA,IACrB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,uBAAuB;AAAA,IACrB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,uBAAuB;AAAA,IACrB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,gBAAgB;AAAA,IACd,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,2BAA2B;AAAA,IACzB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,gBAAgB;AAAA,IACd,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,2BAA2B;AAAA,IACzB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,gBAAgB;AAAA,IACd,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,eAAe;AAAA,IACb,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,IACf,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,aAAa;AAAA,IACX,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,IACf,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,kBAAkB;AAAA,IAChB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,wBAAwB;AAAA,IACtB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,2BAA2B;AAAA,IACzB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6CAA6C;AAAA,IAC3C,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,IAAM;AAAA,IACJ,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,IACf,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,IAAM;AAAA,IACJ,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,iBAAiB;AAAA,IACf,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,uBAAuB;AAAA,IACrB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,gCAAgC;AAAA,IAC9B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,kBAAkB;AAAA,IAChB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,0BAA0B;AAAA,IACxB,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,6BAA6B;AAAA,IAC3B,sBAAwB;AAAA,IACxB,uBAAyB;AAAA,IACzB,kBAAoB;AAAA,IACpB,MAAQ;AAAA,EACV;AAAA,EACA,WAAa;AAAA,IACX,mBAAmB;AAAA,MACjB,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,gCAAgC;AAAA,MAC9B,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,wBAAwB;AAAA,MACtB,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,kCAAkC;AAAA,MAChC,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,8BAA8B;AAAA,MAC5B,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,yBAAyB;AAAA,MACvB,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,yBAAyB;AAAA,MACvB,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,iBAAiB;AAAA,MACf,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,gBAAgB;AAAA,MACd,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,2BAA2B;AAAA,MACzB,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,4BAA4B;AAAA,MAC1B,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,IACA,yBAAyB;AAAA,MACvB,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,MAAQ;AAAA,MACR,UAAY;AAAA,IACd;AAAA,EACF;AACF;;;AD7/BA,IAAM,WAAW;AAYjB,IAAM,oBAAoB,oBAAI,IAAI,CAAC,MAAM,CAAC;AAS1C,IAAM,cAAc;AAYpB,SAAS,gBAAgB,OAAqC;AAC5D,QAAM,IAAI,MAAM,KAAK;AACrB,QAAM,OAAO,CAAC,CAAC;AACf,QAAM,aAAa,EAAE,QAAQ,GAAG;AAChC,MAAI,eAAe,MAAM,kBAAkB,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,GAAG;AACtE,SAAK,KAAK,EAAE,MAAM,aAAa,CAAC,CAAC;AAAA,EACnC;AACA,QAAM,YAAY,EAAE,YAAY,GAAG;AACnC,MAAI,cAAc,IAAI;AACpB,SAAK,KAAK,EAAE,MAAM,YAAY,CAAC,CAAC;AAAA,EAClC;AACA,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,MAAM;AACvB,QAAI,QAAQ,CAAC,MAAM,SAAS,IAAI,EAAG,OAAM,KAAK,IAAI;AAAA,EACpD;AACA,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,KAAK,QAAQ,aAAa,EAAE;AACtC,QAAI,KAAK,CAAC,MAAM,SAAS,CAAC,KAAK,CAAC,SAAS,SAAS,CAAC,EAAG,UAAS,KAAK,CAAC;AAAA,EACvE;AACA,SAAO,CAAC,OAAO,QAAQ;AACzB;AAEA,SAAS,WAAW,GAAY,GAAqB;AACnD,SACE,OAAO,MAAM,YACb,OAAO,MAAM,YACb,OAAO,SAAS,CAAC,KACjB,OAAO,SAAS,CAAC;AAErB;AASO,SAAS,aACd,OACA,WACoB;AACpB,aAAW,SAAS,gBAAgB,KAAK,GAAG;AAC1C,QAAI,WAAW;AACb,iBAAW,QAAQ,OAAO;AAGxB,cAAM,KAAK,OAAO,UAAU,eAAe,KAAK,WAAW,IAAI,IAC3D,UAAU,IAAI,IACd;AACJ,YAAI,OAAO,QAAW;AACpB,cAAI,WAAW,GAAG,mBAAmB,GAAG,kBAAkB,GAAG;AAC3D,mBAAO;AAAA,cACL,mBAAmB,GAAG;AAAA,cACtB,oBAAoB,GAAG;AAAA,cACvB,QAAQ;AAAA,YACV;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,OAAO,UAAU,eAAe,KAAK,UAAU,IAAI,IAC7D,SAAS,IAAI,IACb;AACJ,UAAI,CAAC,MAAO;AAEZ,YAAM,QAAQ,MAAM;AACpB,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,WAAW,OAAO,MAAM,EAAG,QAAO;AAEvC,aAAO;AAAA,QACL,mBAAmB;AAAA,QACnB,oBAAoB;AAAA,QACpB,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,YACd,QACA,cACA,kBACQ;AACR,QAAM,IAAI,KAAK,IAAI,GAAG,YAAY;AAClC,QAAM,IAAI,KAAK,IAAI,GAAG,gBAAgB;AACtC,QAAM,OAAO,IAAI,OAAO,oBAAoB,IAAI,OAAO;AACvD,MAAI,CAAC,OAAO,SAAS,IAAI,GAAG;AAE1B,UAAM,IAAI,MAAM,qDAAgD;AAAA,EAClE;AACA,SAAO,KAAK,IAAI,GAAG,IAAI;AACzB;;;AEjIA,IAAM,MAAM;AAqBZ,SAAS,cAAc,GAA8C;AACnE,SACE,OAAO,MAAM,YACb,MAAM,QACN,OAAQ,EAAwB,QAAQ,YACxC,OAAQ,EAAwB,WAAW;AAE/C;AA0IO,IAAM,cAAN,MAAkB;AAAA,EACd;AAAA,EACT,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,cAAc,KAAK,IAAI;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAES;AAAA;AAAA,EAET,cAAc;AAAA;AAAA,EAEN,iBAAiB;AAAA;AAAA,EAER,QAAqB,CAAC;AAAA,EAEtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,cAAc;AAAA,EACd,eAAe;AAAA;AAAA,EAEf,WAAW;AAAA;AAAA,EAEF,cAA4B,CAAC;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAyC,uBAAO,OAAO,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpE,cAAc;AAAA,EACd,UAAyB;AAAA,EACzB,WAA0B;AAAA;AAAA;AAAA;AAAA,EAKlC,YAAY,UAAkB,UAA8B,CAAC,GAAG;AAC9D,QAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,WAAW,GAAG;AAG9C,YAAM,IAAI;AAAA,QACR,uDAAuD,QAAQ;AAAA,MACjE;AAAA,IACF;AAGA,UAAM,eAAe,QAAQ,iBAAiB,SAAY,MAAO,QAAQ;AACzE,QAAI,CAAC,OAAO,UAAU,YAAY,KAAK,eAAe,KAAK,eAAe,KAAO;AAC/E,YAAM,IAAI;AAAA,QACR,oDAAoD,YAAY;AAAA,MAClE;AAAA,IACF;AACA,QACE,QAAQ,iBAAiB,WACxB,CAAC,OAAO,UAAU,QAAQ,YAAY,KAAK,QAAQ,eAAe,IACnE;AACA,YAAM,IAAI;AAAA,QACR,oDAAoD,QAAQ,YAAY;AAAA,MAC1E;AAAA,IACF;AACA,QACE,QAAQ,eAAe,WACtB,CAAC,OAAO,UAAU,QAAQ,UAAU,KAAK,QAAQ,aAAa,IAC/D;AACA,YAAM,IAAI;AAAA,QACR,kDAAkD,QAAQ,UAAU;AAAA,MACtE;AAAA,IACF;AACA,SAAK,WAAW;AAChB,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,eAAe,QAAQ;AAC5B,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,mBAA4B,UAAwC,CAAC,GAAS;AAClF,UAAM,cACJ,sBAAsB,SAAY,KAAK,gBAAgB,IAAI;AAC7D,QAAI,CAAC,OAAO,SAAS,WAAW,GAAG;AAGjC,YAAM,IAAI;AAAA,QACR,kDAAkD,WAAW;AAAA,MAC/D;AAAA,IACF;AACA,UAAM,WAAW,KAAK,IAAI,GAAG,WAAW;AACxC,SAAK,sBAAsB,QAAQ,eAAe;AAClD,UAAM,SAAS,KAAK,IAAI,GAAG,QAAQ,mBAAmB,CAAC;AACvD,UAAM,UAAU,KAAK,cAAc,UAAU,MAAM;AACnD,QAAI,YAAY,KAAM,MAAK,WAAW,OAAO;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,QAAQ,eAAwB,UAAwC,CAAC,GAAsB;AAC7F,UAAM,cAAc,kBAAkB,SAAY,KAAK,gBAAgB,IAAI;AAC3E,QAAI,CAAC,OAAO,SAAS,WAAW,GAAG;AAEjC,YAAM,IAAI;AAAA,QACR,8CAA8C,WAAW;AAAA,MAC3D;AAAA,IACF;AACA,UAAM,WAAW,KAAK,IAAI,GAAG,WAAW;AAIxC,SAAK,sBAAsB,QAAQ,eAAe;AAClD,UAAM,SAAS,KAAK,IAAI,GAAG,QAAQ,mBAAmB,CAAC;AACvD,UAAM,UAAU,KAAK,cAAc,UAAU,MAAM;AACnD,QAAI,YAAY,KAAM,MAAK,WAAW,OAAO;AAC7C,SAAK,YAAY;AACjB,SAAK,kBAAkB;AACvB,UAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,IAAI;AACrE,QAAI,SAAS,MAAM;AACjB,WAAK,eAAe;AACpB,WAAK,kBAAkB;AAAA,IACzB;AAGA,QAAI,WAAW,KAAK,SAAS,KAAM,QAAO;AAC1C,WAAO,EAAE,KAAK,UAAU,OAAO;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OACE,OACA,cACA,kBACA,UAAiF,CAAC,GAC1E;AACR,UAAM,WAAW,QAAQ,YAAY;AAIrC,UAAM,cAAc,KAAK,cAAc,QAAQ;AAC/C,QAAI,YAAY,KAAK;AACrB,QAAI,QAAQ,UAAU,QAAW;AAC/B,kBAAY,EAAE,GAAI,aAAa,CAAC,GAAI,CAAC,KAAK,GAAG,QAAQ,MAAM;AAAA,IAC7D;AAEA,UAAM,SAAS,aAAa,OAAO,SAAS;AAC5C,QAAI,WAAW,MAAM;AACnB,cAAQ;AAAA,QACN,uBAAuB,KAAK;AAAA,MAG9B;AAIA,WAAK,QAAQ,QAAQ;AACrB,UAAI,KAAK,YAAY;AACnB,cAAM,IAAI,sBAAsB,KAAK;AAAA,MACvC;AACA,aAAO;AAAA,IACT;AAEA,QAAI;AACJ,QAAI;AACF,aAAO,YAAY,QAAQ,cAAc,gBAAgB;AAAA,IAC3D,SAAS,KAAK;AAIZ,WAAK,QAAQ,QAAQ;AACrB,YAAM;AAAA,IACR;AACA,QAAI,UAAU;AACZ,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AAGA,UAAM,gBAAgB,KAAK,IAAI,GAAG,YAAY,IAAI,KAAK,IAAI,GAAG,gBAAgB;AAC9E,SAAK,YAAY;AACjB,SAAK,eAAe;AACpB,SAAK,WAAW,MAAM,aAAa;AAGnC,QAAI,KAAK,WAAW,KAAK,WAAW,KAAK,KAAK,WAAW,KAAK,WAAW,KAAK;AAC5E,WAAK,WAAW,KAAK;AAAA,IACvB;AACA,SAAK,cAAc;AACnB,SAAK,YAAY;AAAA,MACf,WAAW,KAAK,IAAI,IAAI;AAAA,MACxB,MAAM;AAAA,MACN,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,MAI9D,GAAI,cAAc,EAAE,UAAU,YAAY,IAAI,CAAC;AAAA,IACjD,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OACE,OACA,cACA,kBACA,UAAmD,CAAC,GAC5C;AACR,WAAO,KAAK,OAAO,OAAO,cAAc,kBAAkB;AAAA,MACxD,UAAU;AAAA,MACV,OAAO,QAAQ;AAAA,MACf,OAAO,QAAQ;AAAA,IACjB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,YAAY,eAA0C;AACpD,QAAI,kBAAkB,QAAW;AAI/B,YAAM,IAAI,WAAW,uDAAuD;AAAA,IAC9E;AACA,QAAI,CAAC,OAAO,SAAS,aAAa,KAAK,gBAAgB,GAAG;AAGxD,YAAM,IAAI;AAAA,QACR,4DAA4D,aAAa;AAAA,MAC3E;AAAA,IACF;AACA,WAAO,KAAK,QAAQ,aAAa;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,WACE,MACA,SACA,UAA4D,CAAC,GACrD;AACR,QAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,GAAG;AAC5C,YAAM,IAAI,WAAW,sDAAsD,OAAO,EAAE;AAAA,IACtF;AACA,UAAM,WAAW,QAAQ,YAAY;AAIrC,UAAM,cAAc,KAAK,cAAc,QAAQ;AAC/C,QAAI,UAAU;AACZ,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,SAAK,YAAY;AAEjB,SAAK,WAAW,SAAS,CAAC;AAG1B,QAAI,KAAK,WAAW,KAAK,WAAW,KAAK,KAAK,WAAW,KAAK,WAAW,KAAK;AAC5E,WAAK,WAAW,KAAK;AAAA,IACvB;AACA,SAAK,eAAe;AACpB,SAAK,eAAe,IAAI,KAAK,KAAK,eAAe,IAAI,KAAK,KAAK;AAC/D,SAAK,YAAY;AAAA,MACf,WAAW,KAAK,IAAI,IAAI;AAAA,MACxB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB;AAAA,MACA,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,MAC9D,GAAI,cAAc,EAAE,UAAU,YAAY,IAAI,CAAC;AAAA,IACjD,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,WAAW,MAAc,SAAiB,UAA8B,CAAC,GAAW;AAClF,WAAO,KAAK,WAAW,MAAM,SAAS,EAAE,UAAU,GAAG,OAAO,QAAQ,MAAM,CAAC;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,UAAmC;AAIzC,SAAK,cAAc,QAAQ;AAC3B,QAAI,CAAC,SAAU;AACf,SAAK,mBAAmB,QAAQ;AAAA,EAClC;AAAA;AAAA,EAGA,IAAI,eAAuB;AACzB,WAAO,KAAK,IAAI,GAAG,KAAK,WAAW,KAAK,WAAW,KAAK,QAAQ;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,YAAoC;AACtC,WAAO,EAAE,GAAG,KAAK,eAAe;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,WAAyB;AAC3B,WAAO,CAAC,GAAG,KAAK,WAAW;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,YAAoB;AAClB,WAAO,KAAK,YACT,IAAI,CAAC,MAAM;AAEV,YAAM,MAA+B;AAAA,QACnC,WAAW,EAAE;AAAA,QACb,MAAM,EAAE;AAAA,QACR,eAAe,EAAE;AAAA,QACjB,eAAe,EAAE;AAAA,QACjB,mBAAmB,EAAE;AAAA,QACrB,UAAU,EAAE;AAAA,MACd;AACA,UAAI,EAAE,UAAU,OAAW,KAAI,QAAQ,EAAE;AACzC,UAAI,EAAE,aAAa,OAAW,KAAI,WAAW,EAAE;AAC/C,aAAO,GAAG,KAAK,UAAU,GAAG,CAAC;AAAA;AAAA,IAC/B,CAAC,EACA,KAAK,EAAE;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,WAAW,QAAiB,UAAgC,CAAC,GAAS;AACpE,SAAK,cAAc;AACnB,SAAK,UAAU,UAAU;AACzB,SAAK,WAAW,QAAQ,WAAW;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAoB;AAClB,SAAK,cAAc;AACnB,SAAK,UAAU;AACf,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,OAAwB;AAM5B,UAAM,UAAU,KAAK;AACrB,UAAM,MAAM,KAAK;AACjB,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,WAAO,WAAW,KAAK,UAAU,GAAG,OAAO,QAAW,EAAE,SAAS,QAAQ,OAAU,CAAC;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,kBAA0B;AAChC,WAAO,KAAK,IAAI,KAAK,aAAa,KAAK,YAAY;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,mBAAmB,UAAmC;AAC5D,UAAM,MAAM,cAAc,QAAQ,IAAI,SAAS,MAAM;AACrD,UAAM,SAAS,cAAc,QAAQ,IAAI,SAAS,SAAS;AAC3D,QAAI,MAAM,KAAK,WAAW,KAAK;AAC7B,YAAM,IAAI;AAAA,QACR,oBAAoB,GAAG,2CACjB,KAAK,QAAQ;AAAA,MACrB;AAAA,IACF;AACA,QAAI,SAAS,KAAK,gBAAgB;AAChC,YAAM,IAAI;AAAA,QACR,0BAA0B,MAAM,iDACb,KAAK,cAAc;AAAA,MACxC;AAAA,IACF;AACA,SAAK,WAAW,KAAK,IAAI,GAAG,KAAK,WAAW,GAAG;AAC/C,SAAK,iBAAiB,KAAK,IAAI,GAAG,KAAK,iBAAiB,MAAM;AAI9D,UAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,IAAI;AACrE,QAAI,SAAS,MAAM;AACjB,WAAK,cAAc,KAAK,IAAI,GAAG,KAAK,cAAc,GAAG;AACrD,WAAK,iBAAiB,KAAK,IAAI,GAAG,KAAK,iBAAiB,MAAM;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcQ,sBAAsB,iBAA2C;AACvE,QAAI,oBAAoB,UAAa,CAAC,OAAO,UAAU,eAAe,GAAG;AACvE,YAAM,IAAI,WAAW,2CAA2C,eAAe,EAAE;AAAA,IACnF;AAAA,EACF;AAAA,EAEQ,cAAc,UAAqC;AACzD,QAAI,cAAc,QAAQ,GAAG;AAK3B,UAAI,CAAC,OAAO,SAAS,SAAS,GAAG,KAAK,SAAS,MAAM,GAAG;AACtD,cAAM,IAAI;AAAA,UACR,2DAA2D,SAAS,GAAG;AAAA,QACzE;AAAA,MACF;AACA,UAAI,CAAC,OAAO,UAAU,SAAS,MAAM,KAAK,SAAS,SAAS,GAAG;AAC7D,cAAM,IAAI;AAAA,UACR,uDAAuD,SAAS,MAAM;AAAA,QACxE;AAAA,MACF;AACA,aAAO,SAAS;AAAA,IAClB;AACA,QAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,WAAW,GAAG;AAC9C,YAAM,IAAI,WAAW,uDAAuD,QAAQ,EAAE;AAAA,IACxF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,WAAW,MAAc,QAAsB;AACrD,UAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,IAAI;AACrE,QAAI,SAAS,MAAM;AACjB,WAAK,YAAY;AACjB,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cACN,aACA,gBACiD;AAEjD,UAAM,YAAY,KAAK,WAAW,KAAK;AACvC,QAAI,YAAY,KAAK,WAAW,OAAO,YAAY,cAAc,KAAK,WAAW,KAAK;AACpF,aAAO,CAAC,OAAO,WAAW;AAAA,IAC5B;AAEA,QAAI,KAAK,eAAe,MAAM;AAC5B,YAAM,aAAa,KAAK,cAAc,KAAK;AAC3C,UAAI,cAAc,KAAK,cAAc,aAAa,iBAAiB,KAAK,YAAY;AAClF,eAAO,CAAC,UAAU,WAAW;AAAA,MAC/B;AAAA,IACF;AAGA,UAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,IAAI;AACrE,QAAI,SAAS,MAAM;AACjB,UAAI,KAAK,WAAW,MAAM;AACxB,cAAM,aAAa,KAAK,WAAW,KAAK;AACxC,YAAI,aAAa,KAAK,SAAS,OAAO,aAAa,cAAc,KAAK,SAAS,KAAK;AAClF,iBAAO,CAAC,OAAO,MAAM;AAAA,QACvB;AAAA,MACF;AACA,UAAI,KAAK,cAAc,MAAM;AAC3B,cAAM,cAAc,KAAK,cAAc,KAAK;AAC5C,YAAI,eAAe,KAAK,aAAa,cAAc,iBAAiB,KAAK,WAAW;AAClF,iBAAO,CAAC,UAAU,MAAM;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,WAAW,SAA0D;AAC3E,UAAM,CAAC,WAAW,KAAK,IAAI;AAC3B,QAAI,cAAc,OAAO;AACvB,WAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AACzC,YAAM,IAAI,eAAe,KAAK,UAAU,KAAK,QAAQ;AAAA,IACvD;AACA,QAAI;AACJ,QAAI;AACJ,QAAI,UAAU,QAAQ;AACpB,YAAM,OAAO,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAC7C,eAAS,KAAK,cAAc,KAAK;AACjC,eAAS,KAAK,aAAa;AAAA,IAC7B,OAAO;AACL,eAAS,KAAK,cAAc,KAAK;AACjC,eAAS,KAAK,cAAc;AAAA,IAC9B;AACA,UAAM,IAAI,oBAAoB,QAAQ,QAAQ,KAAK;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,KACE,SACA,IACG;AACH,UAAM,EAAE,QAAQ,UAAU,IAAI;AAC9B,QAAI,WAAW,WAAc,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI;AACpE,YAAM,IAAI,WAAW,qDAAqD,MAAM,EAAE;AAAA,IACpF;AACA,QAAI,cAAc,WAAc,CAAC,OAAO,UAAU,SAAS,KAAK,YAAY,IAAI;AAC9E,YAAM,IAAI,WAAW,iDAAiD,SAAS,EAAE;AAAA,IACnF;AACA,UAAM,QAAmB;AAAA,MACvB,QAAQ,UAAU;AAAA,MAClB,WAAW,aAAa;AAAA,MACxB,UAAU;AAAA,MACV,aAAa;AAAA,MACb,aAAa;AAAA,MACb,gBAAgB;AAAA,IAClB;AACA,SAAK,MAAM,KAAK,KAAK;AAGrB,UAAM,MAAM,MAAY;AACtB,YAAM,MAAM,KAAK,MAAM,YAAY,KAAK;AACxC,UAAI,QAAQ,GAAI,MAAK,MAAM,OAAO,KAAK,CAAC;AAAA,IAC1C;AACA,QAAI;AACJ,QAAI;AACF,eAAS,GAAG,IAAI;AAAA,IAClB,SAAS,KAAK;AACZ,UAAI;AACJ,YAAM;AAAA,IACR;AAIA,QAAI,UAAU,QAAQ,OAAQ,OAA8B,SAAS,YAAY;AAC/E,aAAQ,OAAuC,QAAQ,GAAG;AAAA,IAC5D;AACA,QAAI;AACJ,WAAO;AAAA,EACT;AAAA,EAEQ,YAAY,OAAyB;AAI3C,SAAK,YAAY,KAAK,OAAO,OAAO,KAAK,CAAC;AAC1C,QAAI,KAAK,iBAAiB,UAAa,KAAK,YAAY,SAAS,KAAK,cAAc;AAElF,WAAK,YAAY,OAAO,GAAG,KAAK,YAAY,SAAS,KAAK,YAAY;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAA2B;AAIzB,UAAM,UACJ,KAAK,YAAY,IACb,MACA,KAAK,IAAI,GAAG,KAAK,IAAI,KAAO,KAAK,MAAO,KAAK,WAAW,KAAK,WAAY,MAAQ,IAAI,CAAC,CAAC;AAC7F,UAAM,eAAe,KAAK,IAAI,GAAG,KAAK,WAAW,KAAK,QAAQ;AAG9D,UAAM,eAAe,KAAK,IAAI,KAAK,aAAa,KAAK,YAAY;AAGjE,UAAM,oBACJ,eAAe,IAAI,KAAK,MAAM,eAAe,eAAe,IAAI,IAAI;AAItE,UAAM,cAAc,KAAK,IAAI,IAAI,KAAK,eAAe;AACrD,UAAM,oBAAoB,aAAa,IAAI,KAAK,WAAW,aAAa;AAGxE,QAAI,eAA8B;AAClC,QAAI,kBAAiC;AACrC,QAAI,YAAY;AAChB,QAAI,KAAK,eAAe,MAAM;AAC5B,qBACE,KAAK,cAAc,IACf,MACA,KAAK;AAAA,QACH;AAAA,QACA,KAAK,IAAI,KAAO,KAAK,MAAO,KAAK,cAAc,KAAK,aAAc,MAAQ,IAAI,CAAC;AAAA,MACjF;AACN,wBAAkB,KAAK,IAAI,GAAG,KAAK,aAAa,KAAK,WAAW;AAChE,kBAAY,gBAAgB,KAAK;AAAA,IACnC;AAGA,UAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,IAAI;AACrE,QAAI,mBAAkC;AACtC,QAAI,sBAAqC;AACzC,QAAI,WAAW;AACf,QAAI,SAAS,MAAM;AACjB,UAAI,KAAK,WAAW,MAAM;AACxB,2BAAmB,KAAK,IAAI,GAAG,KAAK,SAAS,KAAK,QAAQ;AAC1D,YAAI,KAAK,UAAU,GAAG;AACpB,qBAAW;AAAA,QACb,OAAO;AACL,gBAAM,OAAO,KAAK,MAAO,KAAK,WAAW,KAAK,SAAU,MAAQ,IAAI;AACpE,qBAAW,YAAY,QAAQ,KAAK;AAAA,QACtC;AAAA,MACF;AACA,UAAI,KAAK,cAAc,MAAM;AAC3B,8BAAsB,KAAK,IAAI,GAAG,KAAK,YAAY,KAAK,WAAW;AACnE,YAAI,KAAK,aAAa,GAAG;AACvB,qBAAW;AAAA,QACb,OAAO;AACL,gBAAM,QAAQ,KAAK,MAAO,KAAK,cAAc,KAAK,YAAa,MAAQ,IAAI;AAC3E,qBAAW,YAAY,SAAS,KAAK;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA;AAAA;AAAA,MAGL,WAAW,WAAW,KAAK,gBAAgB,aAAa;AAAA,MACxD;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA;AAAA,MACA,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,eAAe,UAAkB,UAAwB;AAChE,UAAQ;AAAA,IACN;AAAA,mBACsB,SAAS,QAAQ,CAAC,CAAC,kBAAkB,SAAS,QAAQ,CAAC,CAAC;AAAA;AAAA,EAGhF;AACF;;;ACv9BO,IAAM,gBAAN,MAAoB;AAAA,EAChB;AAAA,EACA;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAIjB,YAAY,OAAe,UAAgC,CAAC,GAAG;AAC7D,QAAI,EAAE,OAAO,SAAS,KAAK,KAAK,QAAQ,IAAI;AAC1C,YAAM,IAAI,WAAW,mBAAmB;AAAA,IAC1C;AACA,UAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,kBAAkB,KAAK,kBAAkB,KAAO;AACxF,YAAM,IAAI,WAAW,6CAA6C;AAAA,IACpE;AACA,SAAK,QAAQ;AACb,SAAK,kBAAkB;AACvB,SAAK,UAAU,QAAQ;AACvB,SAAK,QAAQ,QAAQ,UAAU,MAAM,YAAY,IAAI;AACrD,SAAK,YAAY,KAAK,MAAM;AAAA,EAC9B;AAAA;AAAA,EAGA,IAAI,YAAoB;AACtB,WAAO,KAAK,MAAM,IAAI,KAAK;AAAA,EAC7B;AAAA;AAAA;AAAA,EAIA,IAAI,cAAsB;AACxB,WAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK,SAAS;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,aAAa,GAAS;AAC1B,QAAI,EAAE,OAAO,SAAS,UAAU,KAAK,cAAc,IAAI;AACrD,YAAM,IAAI,WAAW,yBAAyB;AAAA,IAChD;AACA,UAAM,UAAU,KAAK;AACrB,QAAI,UAAU,aAAa,KAAK,OAAO;AACrC,WAAK,UAAU,SAAS,KAAK,KAAK;AAClC,YAAM,IAAI,iBAAiB,SAAS,KAAK,KAAK;AAAA,IAChD;AAAA,EACF;AAAA;AAAA,EAGA,WAA4B;AAC1B,UAAM,UAAU,KAAK;AAGrB,UAAM,UAAU,UAAU,IAAI,KAAK,IAAI,KAAO,KAAK,MAAO,UAAU,MAAS,KAAK,KAAK,CAAC,IAAI;AAC5F,WAAO;AAAA,MACL,cAAc,WAAW,KAAK;AAAA,MAC9B;AAAA,MACA,aAAa,KAAK,IAAI,GAAG,KAAK,QAAQ,OAAO;AAAA,MAC7C,OAAO,KAAK;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,EACF;AACF;;;AC1DA,SAAS,YACP,SACA,OACoD;AACpD,QAAM,IAAI;AASV,QAAM,eAAe,GAAG,gBAAgB,GAAG;AAC3C,QAAM,mBAAmB,GAAG,oBAAoB,GAAG;AACnD,MAAI,OAAO,iBAAiB,YAAY,OAAO,qBAAqB,UAAU;AAC5E,UAAM,IAAI;AAAA,MACR,UAAU,OAAO;AAAA,IAGnB;AAAA,EACF;AACA,SAAO,EAAE,cAAc,iBAAiB;AAC1C;AAkBO,SAAS,sBAAsB,OAA2C;AAC/E,SAAO;AAAA,IACL,MAAM,aAAa,EAAE,YAAY,MAAM,GAAG;AACxC,YAAM,WAAW,MAAM,QAAQ;AAC/B,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,WAAY;AAAA,MAC7B,SAAS,KAAK;AACZ,cAAM,QAAQ,QAAQ;AACtB,cAAM;AAAA,MACR;AAIA,UAAI,UAAU;AACd,UAAI;AACF,cAAM,EAAE,cAAc,iBAAiB,IAAI;AAAA,UACzC,MAAM;AAAA,UACN,QAAQ;AAAA,QACV;AACA,kBAAU;AACV,cAAM,OAAO,MAAM,SAAS,cAAc,kBAAkB,EAAE,SAAS,CAAC;AACxE,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,YAAI,CAAC,QAAS,OAAM,QAAQ,QAAQ;AACpC,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IAEA,MAAM,WAAW,EAAE,UAAU,MAAM,GAAG;AACpC,YAAM,WAAW,MAAM,QAAQ;AAC/B,UAAI;AACJ,UAAI;AACF,uBAAe,MAAM,SAAU;AAAA,MACjC,SAAS,KAAK;AACZ,cAAM,QAAQ,QAAQ;AACtB,cAAM;AAAA,MACR;AACA,YAAM,EAAE,QAAQ,GAAG,KAAK,IAAI;AAS5B,UAAI,UAAU;AACd,YAAM,UAAU,OAAO;AAAA,QACrB,IAAI,gBAA0B;AAAA,UAC5B,UAAU,OAAO,YAAY;AAC3B,gBAAI,OAAO,SAAS,YAAY,CAAC,SAAS;AACxC,kBAAI;AACF,sBAAM,EAAE,cAAc,iBAAiB,IAAI;AAAA,kBACzC,MAAM;AAAA,kBACN,MAAM;AAAA,gBACR;AACA,0BAAU;AACV,sBAAM,OAAO,MAAM,SAAS,cAAc,kBAAkB,EAAE,SAAS,CAAC;AAAA,cAC1E,SAAS,KAAK;AAGZ,oBAAI,CAAC,SAAS;AACZ,4BAAU;AACV,wBAAM,QAAQ,QAAQ;AAAA,gBACxB;AACA,sBAAM;AAAA,cACR;AAAA,YACF;AACA,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,UACA,QAAQ;AAEN,gBAAI,CAAC,SAAS;AACZ,wBAAU;AACV,oBAAM,QAAQ,QAAQ;AAAA,YACxB;AAAA,UACF;AAAA,UACA,SAAS;AAIP,gBAAI,CAAC,SAAS;AACZ,wBAAU;AACV,oBAAM,QAAQ,QAAQ;AAAA,YACxB;AAAA,UACF;AAAA;AAAA;AAAA,QAGF,CAA0B;AAAA,MAC5B;AAEA,aAAO,EAAE,QAAQ,SAAS,GAAG,KAAK;AAAA,IACpC;AAAA,EACF;AACF;;;ACxKA,SAAS,eAAe,OAAyB;AAC/C,SAAO,EAAE,iBAAiB;AAC5B;AAUA,eAAsB,gBACpB,OACA,MACA,UAAiC,CAAC,GACtB;AACZ,QAAM,cAAc,QAAQ,eAAe;AAC3C,MAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,GAAG;AACrD,UAAM,IAAI,WAAW,4CAA4C,WAAW,EAAE;AAAA,EAChF;AACA,QAAM,UAAU,QAAQ,WAAW;AACnC,MAAI,OAAqB,EAAE,MAAM,eAAe,QAAQ,cAAc;AAEtE,WAAS,UAAU,GAAG,WAAW,aAAa,WAAW,GAAG;AAC1D,QAAI;AACF,aAAO,MAAM,KAAK,KAAK;AAAA,IACzB,SAAS,OAAO;AACd,UAAI,WAAW,eAAe,CAAC,QAAQ,KAAK,GAAG;AAC7C,cAAM;AAAA,MACR;AACA,aAAO,MAAM,SAAS,OAAO,OAAO,MAAM,QAAQ,SAAS;AAAA,IAC7D;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,aAAa;AAC/B;AAEA,eAAe,SACb,OACA,OACA,SACA,WACuB;AACvB,QAAM,WAAW,MAAM,SAAS;AAChC,MAAI,SAAS,aAAa,cAAc,QAAW;AACjD,UAAM,WAAW,MAAM,UAAU,OAAO,QAAQ;AAChD,QAAI,aAAa,QAAW;AAC1B,YAAM,MAAM,SAAS,aAAa;AAClC,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,MAAM,QAAQ,aAAa;AACjC,SAAO;AACT;;;ACxCA,IAAM,YAAc,iBAAwC,WAAW,KAAK,CAAC;AAUtE,IAAM,cAAyC;AAAA,EACpD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,WAAW;AACb;AAUA,SAAS,kBAAkB,OAAiC;AAE1D,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS;AACzE;AAWO,SAAS,gBAAgB,OAAkC,MAAgC;AAChG,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,QAAQ,OAAO,UAAU,eAAe,KAAK,WAAW,KAAK,IAC/D,UAAU,KAAK,IACf;AACJ,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,SAAS,KAAM,QAAO;AAChC,MAAI,MAAM,SAAS,YAAY,IAAI,EAAG,QAAO;AAC7C,QAAM,OAAO,MAAM;AACnB,MAAI,CAAC,kBAAkB,IAAI,EAAG,QAAO;AACrC,SAAO;AACT;AAQO,SAAS,iBACd,OACA,MACA,UACW;AACX,MAAI,aAAa,UAAa,aAAa,MAAM;AAC/C,QAAI,CAAC,kBAAkB,QAAQ,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,SAAS,IAAI,wDAAwD,QAAQ;AAAA,MAC/E;AAAA,IACF;AACA,WAAO,EAAE,MAAM,MAAM,YAAY,IAAI,GAAG,MAAM,UAAU,QAAQ,WAAW;AAAA,EAC7E;AACA,QAAM,OAAO,gBAAgB,OAAO,IAAI;AACxC,MAAI,SAAS,KAAM,OAAM,IAAI,sBAAsB,SAAS,MAAM,IAAI;AACtE,SAAO,EAAE,MAAM,MAAM,YAAY,IAAI,GAAG,MAAM,QAAQ,WAAW;AACnE;AAMO,SAAS,aAAa,MAAiB,UAAkB,MAAsB;AAIpF,MAAI,CAAC,OAAO,SAAS,QAAQ,GAAG;AAC9B,UAAM,IAAI,WAAW,SAAS,IAAI,0CAA0C,QAAQ,EAAE;AAAA,EACxF;AACA,QAAM,IAAI,KAAK,IAAI,GAAG,QAAQ;AAC9B,MAAI,SAAS,MAAO,QAAO,IAAI;AAC/B,MAAI,SAAS,MAAO,QAAQ,IAAI,MAAQ;AACxC,MAAI,SAAS,YAAa,QAAO,IAAI;AACrC,QAAM,IAAI,WAAW,sBAAsB,IAAI,EAAE;AACnD;AAWO,SAAS,cACd,MACA,UACA,UAA+D,CAAC,GACjD;AACf,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,WAAW,QAAQ,YAAY;AACrC,MAAI,UAAU,QAAQ,aAAa,KAAM,QAAO;AAChD,QAAM,WAAW,iBAAiB,OAAO,MAAM,QAAQ;AACvD,SAAO,aAAa,MAAM,UAAU,SAAS,IAAI;AACnD;;;ACzJA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyCO,SAAS,gBACd,OACA,UAAyC,CAAC,GACjC;AAIT,QAAM,mBAAmB,QAAQ,qBAAqB,SAAY,IAAI,QAAQ;AAC9E,MAAI,CAAC,OAAO,SAAS,gBAAgB,KAAK,mBAAmB,GAAG;AAC9D,UAAM,IAAI;AAAA,MACR,+DAA+D,gBAAgB;AAAA,IACjF;AAAA,EACF;AACA,QAAM,eAAe,MAAM;AAC3B,SAAO,gBAAgB,KAAK,eAAe;AAC7C;AAcO,SAAS,QAAQ,OAAoB,UAAyC,CAAC,GAAY;AAChG,SAAO,CAAC,gBAAgB,OAAO,OAAO;AACxC;AAeO,SAAS,OACd,OACA,UAA0E,CAAC,GAChC;AAC3C,MAAI,gBAAgB,OAAO,EAAE,kBAAkB,QAAQ,iBAAiB,CAAC,GAAG;AAC1E,WAAO,EAAE,cAAc,EAAE,QAAQ,KAAK,EAAE;AAAA,EAC1C;AAIA,QAAM,YAAY,EAAE,GAAI,QAAQ,SAAS,CAAC,EAAG;AAC7C,SAAO,UAAU;AACjB,SAAO,EAAE,cAAc,UAAU;AACnC;AAgBO,SAAS,KACd,OACA,UAKI,CAAC,GACoB;AACzB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,EACF,IAAI;AACJ,MAAI,gBAAgB,OAAO,EAAE,iBAAiB,CAAC,GAAG;AAChD,WAAO,EAAE,OAAO,aAAa;AAAA,EAC/B;AACA,MAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD,WAAO,EAAE,YAAY;AAAA,EACvB;AACA,MAAI,cAAc,UAAa,cAAc,MAAM;AACjD,WAAO,EAAE,UAAU;AAAA,EACrB;AACA,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/sync.ts","../src/pricing.ts","../src/cost_map.json","../src/guard.ts","../src/latency.ts","../src/middleware.ts","../src/retry.ts","../src/voice-pricing.ts","../src/gates.ts"],"sourcesContent":["/**\n * floe-guard — a local budget guardrail for AI agents.\n *\n * Hard-stops your agent before its next LLM call when it would cross a USD spend\n * ceiling. This package is the Vercel AI SDK (TypeScript) adapter; the Python\n * package `floe-guard` (pip) carries the LiteLLM / CrewAI / LangChain adapters.\n */\n\nexport {\n BudgetGuard,\n type BudgetGuardOptions,\n type BudgetAdvisory,\n type BudgetReservation,\n type ReservationHandle,\n type SpendEvent,\n} from \"./guard.js\";\nexport {\n LatencyBudget,\n type LatencyBudgetOptions,\n type LatencyAdvisory,\n} from \"./latency.js\";\nexport {\n FloeGuardError,\n BudgetExceeded,\n TokenBudgetExceeded,\n DeadlineExceeded,\n UnpriceableModelError,\n UnpriceableVoiceError,\n LedgerSyncError,\n} from \"./errors.js\";\nexport { pushLedger } from \"./sync.js\";\nexport {\n budgetGuardMiddleware,\n type BudgetGuardMiddleware,\n} from \"./middleware.js\";\nexport {\n type ManualPrice,\n type PricedModel,\n resolvePrice,\n priceTokens,\n} from \"./pricing.js\";\nexport {\n withBudgetRetry,\n type BudgetRetryOptions,\n type RetryPlan,\n} from \"./retry.js\";\n\nexport * as pricing from \"./pricing.js\";\n\nexport {\n type VoiceMode,\n type VoiceRate,\n lookupVoiceRate,\n resolveVoiceRate,\n voiceLegCost,\n priceVoiceLeg,\n} from \"./voice-pricing.js\";\n\n// gates as a namespace, mirroring Python's `floe_guard.gates` — so\n// `import { gates } from \"floe-guard\"` reads like the Python package.\nexport * as gates from \"./gates.js\";\n","/**\n * Exceptions for floe-guard.\n *\n * Everything derives from {@link FloeGuardError} (the package-root base) so callers\n * can catch the whole family with a single `catch (e) { if (e instanceof FloeGuardError) ... }`.\n *\n * Mirrors `src/floe_guard/errors.py` in the Python package — message formats are\n * kept byte-for-byte identical so both adapters read the same.\n */\n\n/** Base class for every error raised by floe-guard. */\nexport class FloeGuardError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FloeGuardError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown before an LLM call that would cross the configured spend ceiling.\n *\n * The guard throws this *instead of* letting the next call run, so a runaway loop\n * stops here rather than burning more money.\n */\nexport class BudgetExceeded extends FloeGuardError {\n readonly spentUsd: number;\n readonly limitUsd: number;\n\n constructor(spentUsd: number, limitUsd: number, message?: string) {\n // Subclasses (the token twin) pass their own message through so they don't\n // have to reassign `this.message` after super().\n super(\n message ??\n `BUDGET EXCEEDED — call blocked (spent $${spentUsd.toFixed(6)} of $${limitUsd.toFixed(6)} ceiling)`,\n );\n this.name = \"BudgetExceeded\";\n this.spentUsd = spentUsd;\n this.limitUsd = limitUsd;\n }\n}\n\n/**\n * Thrown before a call that would cross a token ceiling (aggregate or step).\n *\n * The token twin of {@link BudgetExceeded} — it extends it so the same retry\n * logic that treats a USD block as terminal (`!(error instanceof\n * BudgetExceeded)`) treats a token block as terminal too, with no extra wiring.\n * `spentUsd` / `limitUsd` are inherited but not meaningful here; the token\n * fields are the payload.\n *\n * `scope` is `\"aggregate\"` (the guard-wide `tokenLimit`) or `\"step\"` (the\n * innermost active {@link BudgetGuard.step} cap).\n */\nexport class TokenBudgetExceeded extends BudgetExceeded {\n readonly spentTokens: number;\n readonly limitTokens: number;\n readonly scope: \"aggregate\" | \"step\";\n\n constructor(spentTokens: number, limitTokens: number, scope: \"aggregate\" | \"step\") {\n // Pass the token-shaped message through super (BudgetExceeded's default is\n // dollar-shaped); 0/0 for the inherited spentUsd/limitUsd — a token block\n // spent no tracked USD.\n super(\n 0,\n 0,\n `TOKEN BUDGET EXCEEDED — call blocked (${scope}: ${spentTokens} of ` +\n `${limitTokens} token ceiling)`,\n );\n this.name = \"TokenBudgetExceeded\";\n this.spentTokens = spentTokens;\n this.limitTokens = limitTokens;\n this.scope = scope;\n }\n}\n\n/**\n * Thrown when a model cannot be priced and the guard is fail-closed.\n *\n * We refuse rather than silently accrue $0 — \"we cannot cap what we cannot price\".\n * Pass a manual price (`priceOverrides` or `record(..., { price })`) to make the\n * model enforceable.\n */\nexport class UnpriceableModelError extends FloeGuardError {\n readonly model: string;\n\n constructor(model: string) {\n super(\n `Cannot price model '${model}': not in the bundled cost map and no ` +\n `manual price was given. The guard cannot enforce a budget on spend ` +\n `it cannot measure. Pass a price override to enable enforcement.`,\n );\n this.name = \"UnpriceableModelError\";\n this.model = model;\n }\n}\n\n/**\n * Thrown when a voice leg (STT/TTS/telephony) cannot be priced and the guard is\n * fail-closed.\n *\n * The voice twin of {@link UnpriceableModelError}: we refuse rather than silently\n * accrue $0 — \"we cannot cap what we cannot price\". It fires when an adapter is\n * asked to meter a leg for a vendor that is absent from the bundled voice cost map\n * (or whose entry has the wrong unit/mode for the leg) and no per-unit override was\n * given. Pass a per-unit rate (`stt_usd_per_second` / `tts_usd_per_1k_chars` /\n * `telephony_usd_per_minute`) to make the leg enforceable.\n *\n * Mirrors `UnpriceableVoiceError` in `src/floe_guard/errors.py`.\n */\nexport class UnpriceableVoiceError extends FloeGuardError {\n readonly vendor: string | null;\n readonly mode: string;\n\n constructor(vendor: string | null, mode: string) {\n // Match Python's `{vendor!r}`: a string is quoted, None renders as `None`.\n const shown = vendor === null ? \"None\" : `'${vendor}'`;\n super(\n `Cannot price ${mode} vendor ${shown}: not in the bundled voice cost ` +\n `map (or its entry has the wrong unit for a ${mode} leg) and no per-unit ` +\n `override was given. The guard cannot enforce a budget on spend it ` +\n `cannot measure. Pass a per-unit rate to enable enforcement.`,\n );\n this.name = \"UnpriceableVoiceError\";\n this.vendor = vendor;\n this.mode = mode;\n }\n}\n\n/**\n * Thrown when an **opt-in** ledger sync to Reconcile Mode fails.\n *\n * Covers a missing API key, a non-https/malformed base URL, a non-2xx response\n * (401 bad/missing key, 403 agent closed/suspended), a network/timeout failure, or\n * a malformed response. Sync is off by default and only runs when the caller\n * explicitly opts in ({@link BudgetGuard.enableSync} + {@link BudgetGuard.sync}) —\n * this error never fires for a guard that hasn't opted in, because such a guard\n * never sends.\n *\n * Mirrors `LedgerSyncError` in `src/floe_guard/errors.py`.\n */\nexport class LedgerSyncError extends FloeGuardError {\n constructor(message: string) {\n super(message);\n this.name = \"LedgerSyncError\";\n }\n}\n\n/**\n * Thrown before a call whose projected duration would blow the SLA.\n *\n * The latency twin of {@link BudgetExceeded}: `LatencyBudget.check()` throws this\n * *instead of* letting the next tool/model call start, so the chain sheds work or\n * falls back to a faster path rather than violating the end-user SLA. Cooperative —\n * killing an already-running stalled call is the framework's job (AbortSignal),\n * not the guard's.\n *\n * Mirrors `DeadlineExceeded` in `src/floe_guard/errors.py` — message format is\n * kept byte-for-byte identical so both adapters read the same.\n */\n/** Shared millisecond rounding for cross-language message parity: `toFixed(0)`\n * rounds half-up while Python's `:.0f` rounds half-to-even, so tie values\n * would break the byte-for-byte contract. Both packages use floor(x + 0.5)\n * (see `_round_half_up` in `src/floe_guard/errors.py`). */\nfunction roundHalfUp(ms: number): number {\n return Math.floor(ms + 0.5);\n}\n\nexport class DeadlineExceeded extends FloeGuardError {\n readonly elapsedMs: number;\n readonly slaMs: number;\n\n constructor(elapsedMs: number, slaMs: number) {\n super(\n `DEADLINE EXCEEDED — call blocked (elapsed ${roundHalfUp(elapsedMs)}ms of ${roundHalfUp(slaMs)}ms SLA)`,\n );\n this.name = \"DeadlineExceeded\";\n this.elapsedMs = elapsedMs;\n this.slaMs = slaMs;\n }\n}\n","/**\n * Opt-in ledger sync — push the local spend ledger to Floe's Reconcile Mode.\n *\n * **Zero-telemetry is the default, and stays the default.** Nothing in this module\n * runs unless the caller *explicitly* opts in with a Floe API key — via\n * {@link BudgetGuard.enableSync} + {@link BudgetGuard.sync}. There is no implicit\n * enablement and no background send: the ledger leaves your process only when you\n * call one of those. Your ledger, your key, your choice.\n *\n * **Why sync at all.** Floe's gateway can't see spend it never routed — BYOK,\n * self-hosted, or off-path LLM/tool calls. Pushing your local ledger into Reconcile\n * Mode is how that spend lands on the ledger and your **Coverage Score** becomes\n * computable. Budget, not balance: this reports *what you already spent* for\n * coverage/attribution; it does not move money or change any wallet balance.\n *\n * **What leaves the process — exactly the {@link BudgetGuard.exportLog} JSONL** and\n * nothing else: one line per spend event, each a priced-cost record — `timestamp`,\n * `kind` (`\"llm\"`/`\"tool\"`), `model_or_tool`, `prompt_tokens`, `completion_tokens`,\n * `cost_usd`, and the optional `label` / `reserved` you set. **No prompts, no\n * message content, no identifiers** beyond a `label` you choose.\n *\n * Mirrors `src/floe_guard/sync.py` in the Python package — same behaviour\n * (https-only, key-required, fail-closed), transported over `fetch` instead of\n * `urllib`.\n *\n * Opt in: https://dev-dashboard.floelabs.xyz · https://floelabs.xyz\n */\n\nimport { LedgerSyncError } from \"./errors.js\";\n\nconst FLOE_API_KEY_ENV = \"FLOE_API_KEY\";\nconst FLOE_API_BASE_URL_ENV = \"FLOE_API_BASE_URL\";\nconst DEFAULT_BASE_URL = \"https://credit-api.floelabs.xyz\";\nconst LEDGER_SYNC_PATH = \"/v1/agents/ledger/sync\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n// The ONLY keys allowed to leave the process — the exportLog() schema. Enforced\n// (not just documented) in validateLedger(): a line carrying anything else is\n// rejected, so a hand-supplied file can't smuggle prompts / content / identifiers\n// past the privacy contract.\nconst ALLOWED_KEYS = new Set([\n \"timestamp\",\n \"kind\",\n \"model_or_tool\",\n \"prompt_tokens\",\n \"completion_tokens\",\n \"cost_usd\",\n \"label\",\n \"reserved\",\n]);\nconst REQUIRED_KEYS = [\"timestamp\", \"kind\", \"model_or_tool\", \"cost_usd\"];\n\n/**\n * Read an environment variable without a hard `process` reference — the package\n * compiles with `types: []` (no `@types/node`), so `process` is untyped. Reads\n * live at call time so tests that mutate the env are honoured.\n */\nfunction envVar(name: string): string {\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return proc?.env?.[name] ?? \"\";\n}\n\n/**\n * Parse and validate every ledger line against the `exportLog()` schema.\n *\n * Throws {@link LedgerSyncError} on the first malformed / unknown-field / invalid\n * record, **before** anything is sent — so only priced spend events ever leave the\n * process, even when the ledger came from a hand-edited file. This is the privacy\n * contract *enforced*, not merely asserted: an **extra/unknown key** (a smuggled\n * `prompt`, `user_id`, …) is rejected outright. Mirrors `_validate_ledger` in the\n * Python client.\n *\n * (JS `typeof x === \"number\"` already excludes booleans and `Number.isInteger`\n * rejects them too, so — unlike Python, where `bool` is an `int` subclass — no\n * explicit boolean guards are needed.)\n */\nfunction validateLedger(jsonl: string): void {\n let i = 0;\n for (const raw of jsonl.split(\"\\n\")) {\n i += 1;\n const line = raw.trim();\n if (!line) continue;\n let event: unknown;\n try {\n event = JSON.parse(line);\n } catch (err) {\n throw new LedgerSyncError(`Ledger line ${i} is not valid JSON: ${describeCause(err)}`);\n }\n if (typeof event !== \"object\" || event === null || Array.isArray(event)) {\n throw new LedgerSyncError(`Ledger line ${i} is not a JSON object.`);\n }\n const ev = event as Record<string, unknown>;\n const extra = Object.keys(ev).filter((k) => !ALLOWED_KEYS.has(k));\n if (extra.length) {\n throw new LedgerSyncError(\n `Ledger line ${i} has fields outside the exportLog() schema: ` +\n `${JSON.stringify(extra.sort())}. Only priced spend events may be synced — ` +\n \"no prompts, content, or identifiers.\",\n );\n }\n const missing = REQUIRED_KEYS.filter((k) => !(k in ev));\n if (missing.length) {\n throw new LedgerSyncError(\n `Ledger line ${i} is missing required field(s): ${JSON.stringify(missing)}.`,\n );\n }\n if (ev.kind !== \"llm\" && ev.kind !== \"tool\") {\n throw new LedgerSyncError(\n `Ledger line ${i}: kind must be 'llm' or 'tool', got ${JSON.stringify(ev.kind)}.`,\n );\n }\n if (typeof ev.model_or_tool !== \"string\") {\n throw new LedgerSyncError(`Ledger line ${i}: model_or_tool must be a string.`);\n }\n const cost = ev.cost_usd;\n if (typeof cost !== \"number\" || !Number.isFinite(cost) || cost < 0) {\n throw new LedgerSyncError(\n `Ledger line ${i}: cost_usd must be a finite, non-negative number, got ${JSON.stringify(cost)}.`,\n );\n }\n const ts = ev.timestamp;\n if (typeof ts !== \"number\" || !Number.isFinite(ts)) {\n throw new LedgerSyncError(\n `Ledger line ${i}: timestamp must be a finite number, got ${JSON.stringify(ts)}.`,\n );\n }\n for (const field of [\"prompt_tokens\", \"completion_tokens\"] as const) {\n const value = ev[field];\n // Absent (undefined) or explicit null are fine — tool events carry null; a\n // present value must be an integer.\n if (value !== undefined && value !== null && !Number.isInteger(value)) {\n throw new LedgerSyncError(`Ledger line ${i}: ${field} must be an integer or null.`);\n }\n }\n if (\"label\" in ev && typeof ev.label !== \"string\") {\n throw new LedgerSyncError(`Ledger line ${i}: label must be a string.`);\n }\n if (\"reserved\" in ev) {\n const reserved = ev.reserved;\n if (typeof reserved !== \"number\" || !Number.isFinite(reserved)) {\n throw new LedgerSyncError(`Ledger line ${i}: reserved must be a finite number.`);\n }\n }\n }\n}\n\n/**\n * POST an {@link BudgetGuard.exportLog} JSONL ledger to Reconcile Mode and resolve\n * with the number of events the server accepted.\n *\n * This is the ONLY function that sends the ledger, and it sends only when called.\n * An empty ledger is a no-op (resolves `0` with **no** `fetch` call).\n *\n * @param jsonl the ledger as newline-delimited JSON — `exportLog()` output.\n * @param apiKey Floe agent key (`floe_<hex>`, `read_write`). Defaults to the\n * `FLOE_API_KEY` env var.\n * @param opts.baseUrl API base. Defaults to `FLOE_API_BASE_URL`, else the prod host.\n * @param opts.timeoutMs request timeout in milliseconds (default 30_000).\n * @throws {LedgerSyncError} missing key, a non-https/malformed base URL, a non-2xx\n * response, network/timeout, or a malformed response body. The key and ledger are\n * never sent over a non-https or malformed URL.\n */\nexport async function pushLedger(\n jsonl: string,\n apiKey?: string,\n opts: { baseUrl?: string; timeoutMs?: number } = {},\n): Promise<number> {\n // An empty ledger never touches the network — nothing to send.\n if (!jsonl.trim()) return 0;\n\n // Enforce the privacy contract BEFORE any network: only exportLog() events —\n // no prompts, content, or identifiers — may leave, even from a hand-supplied file.\n validateLedger(jsonl);\n\n const key = ((apiKey ?? \"\") || envVar(FLOE_API_KEY_ENV)).trim();\n if (!key) {\n throw new LedgerSyncError(\n `No Floe API key: pass apiKey or set ${FLOE_API_KEY_ENV}. ` +\n \"Sync is opt-in and needs your key.\",\n );\n }\n\n const envBase = envVar(FLOE_API_BASE_URL_ENV).trim();\n const base = ((opts.baseUrl ?? \"\").trim() || envBase || DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n let parsed: URL;\n try {\n parsed = new URL(base);\n } catch {\n // The request carries the Floe agent key as a bearer token AND your spend\n // ledger — never send either over a malformed URL.\n throw new LedgerSyncError(\n `Refusing to send the ledger to '${base}': ` +\n \"the base URL must be an https:// URL with a host.\",\n );\n }\n if (parsed.protocol !== \"https:\" || !parsed.host) {\n // Same guard for a well-formed but non-https (or hostless) URL.\n throw new LedgerSyncError(\n `Refusing to send the ledger to '${base}': ` +\n \"the base URL must be an https:// URL with a host.\",\n );\n }\n const url = `${base}${LEDGER_SYNC_PATH}`;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n // Refuse redirects: a 3xx would otherwise let the key + ledger be re-sent to\n // another host. (The Fetch spec strips `Authorization` on a cross-origin\n // redirect, but `redirect: \"error\"` is the belt-and-suspenders — nothing is\n // re-sent anywhere, same-origin or not.)\n redirect: \"error\",\n headers: {\n Authorization: `Bearer ${key}`,\n \"Content-Type\": \"application/x-ndjson\",\n Accept: \"application/json\",\n },\n body: jsonl,\n signal: controller.signal,\n });\n } catch (err) {\n throw new LedgerSyncError(`Could not reach Floe at ${url}: ${describeCause(err)}`);\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n throw new LedgerSyncError(await describeHttpError(response));\n }\n\n let payload: unknown;\n try {\n payload = JSON.parse(await response.text());\n } catch (err) {\n throw new LedgerSyncError(`Malformed JSON from Floe at ${url}: ${describeCause(err)}`);\n }\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n throw new LedgerSyncError(`Unexpected response shape from Floe at ${url}.`);\n }\n // \"synced\" is the count the server accepted (new rows); \"duplicates\" (already\n // ingested, idempotent) are not counted here. Absent → 0; present must be a real\n // non-negative integer — reject bool/float/negative/garbage rather than coerce.\n const synced = (payload as Record<string, unknown>).synced;\n if (synced === undefined || synced === null) return 0;\n if (typeof synced !== \"number\" || !Number.isInteger(synced) || synced < 0) {\n throw new LedgerSyncError(`Invalid 'synced' count from Floe at ${url}: ${JSON.stringify(synced)}.`);\n }\n return synced;\n}\n\nfunction describeCause(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nasync function describeHttpError(response: Response): Promise<string> {\n let detail = \"\";\n try {\n const data: unknown = JSON.parse(await response.text());\n if (\n typeof data === \"object\" &&\n data !== null &&\n typeof (data as Record<string, unknown>).error === \"string\"\n ) {\n detail = ` (${(data as Record<string, unknown>).error as string})`;\n }\n } catch {\n // Non-JSON / empty error body — the status code alone is the message.\n }\n const code = response.status;\n if (code === 401) return `Floe rejected the API key (401 unauthorized)${detail}.`;\n if (code === 403) {\n return (\n `Floe refused the sync (403 forbidden)${detail} — the agent may be ` +\n \"closed/suspended, or the key is read-only (a read_write key is required).\"\n );\n }\n return `Floe returned HTTP ${code}${detail}.`;\n}\n","/**\n * Offline token pricing from a vendored LiteLLM cost map.\n *\n * Mirrors `src/floe_guard/pricing.py`: BOTH the input and output per-token prices\n * must be finite numbers, otherwise the model is treated as unpriceable. A\n * half-valid entry would silently undercharge — so we refuse it (fail-closed).\n *\n * No network. The cost map (`cost_map.json`) is a snapshot of LiteLLM's\n * `model_prices_and_context_window.json`; refresh it on a schedule or estimates\n * drift as vendors change prices.\n */\n\nimport costMapJson from \"./cost_map.json\";\n\n/** A user-supplied per-token price, in USD, for a model the map cannot price. */\nexport interface ManualPrice {\n inputCostPerToken: number;\n outputCostPerToken: number;\n}\n\n/** A resolved per-token price plus where it came from. */\nexport interface PricedModel {\n inputCostPerToken: number;\n outputCostPerToken: number;\n source: \"override\" | \"cost_map\";\n}\n\ninterface CostMapEntry {\n input_cost_per_token?: unknown;\n output_cost_per_token?: unknown;\n}\n\nconst COST_MAP = costMapJson as Record<string, CostMapEntry>;\n\n/**\n * The one `<provider>/` prefix that is safe to strip: the remainder of a\n * `groq/…` id is the ChatGroq id the map vendors (e.g. `groq/qwen/qwen3-32b` →\n * `qwen/qwen3-32b`). `openai/` and `anthropic/` are deliberately excluded:\n * their own model ids never contain slashes (single-segment remainders are\n * already covered by the bare-last-segment fallback), so a multi-segment\n * remainder under those prefixes is some OTHER vendor's model behind an\n * OpenAI-compatible endpoint (vLLM, OpenRouter, …) — bridging it into a\n * Groq-priced key would under-meter. Unknown prefixes fail closed the same way.\n */\nconst PROVIDER_PREFIXES = new Set([\"groq\"]);\n\n/**\n * A trailing dated-snapshot suffix: Anthropic's `-20250929` or OpenAI's\n * `-2024-08-06`. Vendors resolve alias ids to dated snapshots in responses, so a\n * snapshot the map doesn't list yet prices at its alias entry (same model, same\n * rate) instead of failing closed. (`\\d` is ASCII-only in JS; the Python regex\n * uses re.ASCII to match.)\n */\nconst DATE_SUFFIX = /-(?:\\d{8}|\\d{4}-\\d{2}-\\d{2})$/;\n\n/**\n * Lookup keys for a model id in two specificity groups, deduplicated.\n *\n * Group 1 (exact): the raw id, the id with a known `provider/` first segment\n * stripped, the bare last segment. Group 2 (date-stripped): the same forms\n * with a trailing dated-snapshot suffix removed. Kept separate so a\n * less-specific date-stripped key (in overrides OR the map) can never shadow\n * an exact dated entry — e.g. an alias override must not absorb a snapshot\n * the map prices differently.\n */\nfunction candidateGroups(model: string): [string[], string[]] {\n const m = model.trim();\n const base = [m];\n const firstSlash = m.indexOf(\"/\");\n if (firstSlash !== -1 && PROVIDER_PREFIXES.has(m.slice(0, firstSlash))) {\n base.push(m.slice(firstSlash + 1));\n }\n const lastSlash = m.lastIndexOf(\"/\");\n if (lastSlash !== -1) {\n base.push(m.slice(lastSlash + 1));\n }\n const exact: string[] = [];\n for (const cand of base) {\n if (cand && !exact.includes(cand)) exact.push(cand);\n }\n const stripped: string[] = [];\n for (const cand of exact) {\n const c = cand.replace(DATE_SUFFIX, \"\");\n if (c && !exact.includes(c) && !stripped.includes(c)) stripped.push(c);\n }\n return [exact, stripped];\n}\n\nfunction bothFinite(a: unknown, b: unknown): boolean {\n return (\n typeof a === \"number\" &&\n typeof b === \"number\" &&\n Number.isFinite(a) &&\n Number.isFinite(b)\n );\n}\n\n/**\n * Resolve a model to its per-token price, or `null` if it cannot be priced.\n *\n * Per specificity group (exact forms first, date-stripped fallbacks second):\n * overrides win, then the bundled cost map. Fail-closed: the first matching\n * entry must have finite prices, else `null`.\n */\nexport function resolvePrice(\n model: string,\n overrides?: Record<string, ManualPrice>,\n): PricedModel | null {\n for (const cands of candidateGroups(model)) {\n if (overrides) {\n for (const cand of cands) {\n // Own-property check so a key like \"constructor\" can't pull a function\n // off Object.prototype and be treated as a price entry.\n const ov = Object.prototype.hasOwnProperty.call(overrides, cand)\n ? overrides[cand]\n : undefined;\n if (ov !== undefined) {\n if (bothFinite(ov.inputCostPerToken, ov.outputCostPerToken)) {\n return {\n inputCostPerToken: ov.inputCostPerToken,\n outputCostPerToken: ov.outputCostPerToken,\n source: \"override\",\n };\n }\n return null;\n }\n }\n }\n\n for (const cand of cands) {\n const entry = Object.prototype.hasOwnProperty.call(COST_MAP, cand)\n ? COST_MAP[cand]\n : undefined;\n if (!entry) continue;\n\n const input = entry.input_cost_per_token;\n const output = entry.output_cost_per_token;\n if (!bothFinite(input, output)) return null;\n\n return {\n inputCostPerToken: input as number,\n outputCostPerToken: output as number,\n source: \"cost_map\",\n };\n }\n }\n return null;\n}\n\n/** USD cost for token usage. Negative counts are clamped to zero. */\nexport function priceTokens(\n priced: PricedModel,\n promptTokens: number,\n completionTokens: number,\n): number {\n const p = Math.max(0, promptTokens);\n const c = Math.max(0, completionTokens);\n const cost = p * priced.inputCostPerToken + c * priced.outputCostPerToken;\n if (!Number.isFinite(cost)) {\n // Defense-in-depth: resolvePrice already guarantees finite rates.\n throw new Error(\"Non-finite LLM cost — pricing entry is invalid\");\n }\n return Math.max(0, cost);\n}\n","{\n \"chatgpt-4o-latest\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"claude-3-7-sonnet-20250219\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-3-haiku-20240307\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-3-opus-20240229\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-4-opus-20250514\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-4-sonnet-20250514\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-fable-5\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-haiku-4-5\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-haiku-4-5-20251001\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-mythos-5\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-mythos-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-1\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-1-20250805\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-20250514\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-5-20251101\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-6\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-6-20260205\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-7\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-7-20260416\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-8\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-20250514\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-5\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-5-20250929\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-6\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-5\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-0125\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-0613\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-1106\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4-0613\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-2025-04-14\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-mini-2025-04-14\": {\n \"input_cost_per_token\": 8e-7,\n \"output_cost_per_token\": 0.0000032,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-nano-2025-04-14\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 8e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-2024-08-06\": {\n \"input_cost_per_token\": 0.00000375,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-2024-11-20\": {\n \"input_cost_per_token\": 0.00000375,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-mini-2024-07-18\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:o4-mini-2025-04-16\": {\n \"input_cost_per_token\": 0.000004,\n \"output_cost_per_token\": 0.000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash-001\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash-lite\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.0-flash-lite-001\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-computer-use-preview-10-2025\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-lite\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-lite-preview-06-17\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-lite-preview-09-2025\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-native-audio-latest\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-native-audio-preview-09-2025\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-native-audio-preview-12-2025\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-flash-preview-09-2025\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-pro\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-2.5-pro-preview-tts\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3-flash-preview\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.000003,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3-pro-preview\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-flash-lite\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-flash-lite-preview\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-flash-live-preview\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-pro-preview\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.1-pro-preview-customtools\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.5-flash\": {\n \"input_cost_per_token\": 0.0000015,\n \"output_cost_per_token\": 0.000009,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.5-flash-lite\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.6-flash\": {\n \"input_cost_per_token\": 0.0000015,\n \"output_cost_per_token\": 0.0000075,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-3.7-flash\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.00000375,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-embedding-001\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"embedding\"\n },\n \"gemini-embedding-2\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"embedding\"\n },\n \"gemini-embedding-2-preview\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"embedding\"\n },\n \"gemini-exp-1206\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-flash-latest\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-flash-lite-latest\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-gemma-2-27b-it\": {\n \"input_cost_per_token\": 3.5e-7,\n \"output_cost_per_token\": 0.00000105,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-gemma-2-9b-it\": {\n \"input_cost_per_token\": 3.5e-7,\n \"output_cost_per_token\": 0.00000105,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-omni-flash-preview\": {\n \"input_cost_per_token\": 0.0000015,\n \"output_cost_per_token\": 0.000009,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-pro-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-robotics-er-1.5-preview\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000025,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-robotics-er-1.6-preview\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-robotics-er-2-preview\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gemini-robotics-er-2-streaming-preview\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"gemini\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-0125\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-1106\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-16k\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000004,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0125-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0314\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0613\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-1106-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo-2024-04-09\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-2025-04-14\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-mini\": {\n \"input_cost_per_token\": 4e-7,\n \"output_cost_per_token\": 0.0000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-mini-2025-04-14\": {\n \"input_cost_per_token\": 4e-7,\n \"output_cost_per_token\": 0.0000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-nano\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-nano-2025-04-14\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-05-13\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-08-06\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-11-20\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview-2024-12-17\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview-2025-06-03\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-2024-07-18\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-audio-preview\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-audio-preview-2024-12-17\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-search-preview\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-search-preview-2025-03-11\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-search-preview\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-search-preview-2025-03-11\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-2025-08-07\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-chat\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-chat-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-mini\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-mini-2025-08-07\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-nano\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-nano-2025-08-07\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-search-api\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-search-api-2025-10-14\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1-2025-11-13\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1-chat-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2-2025-12-11\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2-chat-latest\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.3-chat-latest\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-2026-03-05\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-mini\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-mini-2026-03-17\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-nano\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-nano-2026-03-17\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.5-2026-04-23\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6-luna\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.0000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6-sol\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.6-terra\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-1.5\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-2025-08-28\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini-2025-10-06\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini-2025-12-15\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"llama-3.1-8b-instant\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 8e-8,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"llama-3.3-70b-versatile\": {\n \"input_cost_per_token\": 5.9e-7,\n \"output_cost_per_token\": 7.9e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"meta-llama/llama-4-scout-17b-16e-instruct\": {\n \"input_cost_per_token\": 1.1e-7,\n \"output_cost_per_token\": 3.4e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"o1\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o1-2024-12-17\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-2025-04-16\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-mini\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-mini-2025-01-31\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o4-mini\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o4-mini-2025-04-16\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"openai/gpt-oss-120b\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"openai/gpt-oss-20b\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"openai/gpt-oss-safeguard-20b\": {\n \"input_cost_per_token\": 7.5e-8,\n \"output_cost_per_token\": 3e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"qwen/qwen3-32b\": {\n \"input_cost_per_token\": 2.9e-7,\n \"output_cost_per_token\": 5.9e-7,\n \"litellm_provider\": \"groq\",\n \"mode\": \"chat\"\n },\n \"text-embedding-3-large\": {\n \"input_cost_per_token\": 1.3e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-3-small\": {\n \"input_cost_per_token\": 2e-8,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-ada-002\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-ada-002-v2\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"__voice__\": {\n \"deepgram-nova-3\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0001283333,\n \"provider\": \"deepgram\"\n },\n \"deepgram-nova-3-multilingual\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0001533333,\n \"provider\": \"deepgram\"\n },\n \"deepgram-nova-3-base\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0002416667,\n \"provider\": \"deepgram\"\n },\n \"assemblyai-universal-streaming\": {\n \"mode\": \"stt\",\n \"unit\": \"usd_per_second\",\n \"rate\": 0.0000416667,\n \"provider\": \"assemblyai\"\n },\n \"elevenlabs-multilingual-v2\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.1,\n \"provider\": \"elevenlabs\"\n },\n \"elevenlabs-flash-v2.5\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.05,\n \"provider\": \"elevenlabs\"\n },\n \"elevenlabs-turbo-v2.5\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.05,\n \"provider\": \"elevenlabs\"\n },\n \"cartesia-sonic\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.03,\n \"provider\": \"cartesia\"\n },\n \"cartesia-line\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.06,\n \"provider\": \"cartesia\"\n },\n \"rime-mist-v2\": {\n \"mode\": \"tts\",\n \"unit\": \"usd_per_1k_chars\",\n \"rate\": 0.03,\n \"provider\": \"rime\"\n },\n \"twilio-us-inbound-local\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.0085,\n \"provider\": \"twilio\"\n },\n \"twilio-us-outbound-local\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.014,\n \"provider\": \"twilio\"\n },\n \"twilio-us-sip-inbound\": {\n \"mode\": \"telephony\",\n \"unit\": \"usd_per_minute\",\n \"rate\": 0.004,\n \"provider\": \"twilio\"\n }\n }\n}\n","/**\n * The local, in-process budget guard.\n *\n * `BudgetGuard` is a kill-switch that lives in the LLM call path. The contract:\n *\n * 1. Call {@link BudgetGuard.check} BEFORE every LLM call. If the *next* call\n * would cross the ceiling, it throws {@link BudgetExceeded} and the call never\n * runs.\n * 2. Call {@link BudgetGuard.record} AFTER every response, with the token usage.\n * It prices the tokens offline and accrues the USD into a running total.\n *\n * **Concurrency.** `check()` then `record()` is a check-then-act with an `await`\n * in between. Fire several model calls at once (e.g. `Promise.all`) and they all\n * `check()` against the same under-limit total before any `record()` lands, so\n * the ceiling is blown (see issue #18). {@link BudgetGuard.reserve} /\n * {@link BudgetGuard.settle} close that gap: `reserve()` holds the estimated cost\n * in flight (synchronously, before the await), so parallel callers each take\n * their own slice of the ceiling. JS is single-threaded, so an in-flight counter\n * is enough — no lock needed. The middleware uses it; `check`/`record` are\n * unchanged.\n *\n * This is a faithful port of `src/floe_guard/guard.py` — same prediction logic,\n * same epsilon handling, same fail-closed default.\n */\n\nimport { BudgetExceeded, TokenBudgetExceeded, UnpriceableModelError } from \"./errors.js\";\nimport { pushLedger } from \"./sync.js\";\nimport {\n type ManualPrice,\n priceTokens,\n resolvePrice,\n} from \"./pricing.js\";\n\n/** Tolerance for float rounding in the running spend total (well below $0.000001). */\nconst EPS = 1e-12;\n\n/**\n * A two-dimensional in-flight hold (USD + tokens) returned by\n * {@link BudgetGuard.reserve} when a token ceiling or an active\n * {@link BudgetGuard.step} is involved. A dumb value handle — pass it straight\n * back to {@link BudgetGuard.settle} / {@link BudgetGuard.release}. When neither\n * tokens nor a step are involved, `reserve` returns a plain `number` instead\n * (byte-for-byte the old behaviour), so {@link ReservationHandle} is the union.\n */\nexport interface BudgetReservation {\n readonly usd: number;\n readonly tokens: number;\n}\n\n/**\n * A plain `number` when USD-only (backward compatible); a\n * {@link BudgetReservation} when a token ceiling or an active step is in play.\n */\nexport type ReservationHandle = number | BudgetReservation;\n\nfunction isReservation(h: ReservationHandle): h is BudgetReservation {\n return (\n typeof h === \"object\" &&\n h !== null &&\n typeof (h as BudgetReservation).usd === \"number\" &&\n typeof (h as BudgetReservation).tokens === \"number\"\n );\n}\n\n/** One scope on the step stack (see {@link BudgetGuard.step}). Mutable. */\ninterface StepState {\n readonly maxUsd: number | null;\n readonly maxTokens: number | null;\n spentUsd: number;\n spentTokens: number;\n reservedUsd: number;\n reservedTokens: number;\n}\n\n/**\n * One priced spend event in the guard's per-call ledger.\n *\n * Every {@link BudgetGuard.record} / {@link BudgetGuard.settle} /\n * {@link BudgetGuard.recordTool} / {@link BudgetGuard.settleTool} that accrues\n * spend appends exactly one event, so\n * the ledger's costs sum to `spentUsd` (unless a `maxLogEvents` ring buffer has\n * evicted old events). The schema is identical in the Python\n * package (`SpendEvent` in `src/floe_guard/guard.py`) and\n * {@link BudgetGuard.exportLog} serialises it with the same snake_case keys in\n * both languages, so every agent emits the same shape regardless of stack.\n */\nexport interface SpendEvent {\n /** Unix epoch seconds (UTC). */\n readonly timestamp: number;\n readonly kind: \"llm\" | \"tool\";\n readonly modelOrTool: string;\n /** `null` for tool events. */\n readonly promptTokens: number | null;\n /** `null` for tool events. */\n readonly completionTokens: number | null;\n readonly costUsd: number;\n /** Caller-supplied tag (agent/task name). */\n readonly label?: string;\n /** The reservation settled by this call, if any. */\n readonly reserved?: number;\n}\n\nexport interface BudgetGuardOptions {\n /** Per-model manual prices for models the bundled cost map cannot price. */\n priceOverrides?: Record<string, ManualPrice>;\n /**\n * When `true` (default), recording an unpriceable model without a manual price\n * warns loudly AND throws {@link UnpriceableModelError}. When `false`, it warns\n * and skips accrual (you have opted into un-enforced spend for that model).\n */\n failClosed?: boolean;\n /**\n * Optional callback invoked with `(spentUsd, limitUsd)` right before\n * {@link BudgetExceeded} is thrown. Defaults to printing the\n * `BUDGET EXCEEDED — call blocked` banner to stderr. Fires on **USD** ceiling\n * crossings only; a token-ceiling block ({@link TokenBudgetExceeded}, aggregate\n * or step) is thrown without invoking it, since the callback is dollar-shaped.\n */\n onBlock?: (spentUsd: number, limitUsd: number) => void;\n /**\n * Utilization (basis points, 0..10000) at which {@link BudgetGuard.advisory}\n * flags `nearLimit` so an agent can taper before the hard-stop. Default 8000.\n */\n nearLimitBps?: number;\n /**\n * Optional cap on the per-call spend ledger ({@link BudgetGuard.spendLog}).\n * When set, the ledger is a ring buffer keeping the most recent N events so a\n * long-running agent's memory stays bounded; the running totals are\n * unaffected. Default: keep every event.\n */\n maxLogEvents?: number;\n /**\n * Aggregate token ceiling. `null`/undefined disables the token dimension\n * entirely (a pure USD guard, unchanged). `0` blocks the first token.\n */\n tokenLimit?: number;\n}\n\n/**\n * A context-aware spend signal for the single local budget.\n *\n * Mirrors the core fields of hosted Floe's `X-Floe-Budget-Advisory` header, so\n * agent logic that reads it (taper as you approach the cap, stop at it) ports\n * unchanged to the hosted path. Hosted adds what a local, single-budget guard\n * cannot know: which of several caps is tightest (`scope` across\n * `credit_line | session | task | api | vendor`), cross-vendor reasoning,\n * server-truth balances, and rolling-window reset timing.\n *\n * This is a **soft** signal — the model may ignore it. The hard-stop\n * ({@link BudgetGuard.check}) is what enforces the ceiling; the advisory is\n * upside (let the agent finish on budget rather than be cut off).\n */\nexport interface BudgetAdvisory {\n nearLimit: boolean;\n /** Utilization in basis points, 0..10000 (8500 = 85%). */\n usedBps: number;\n remainingUsd: number;\n limitUsd: number;\n spentUsd: number;\n /** Hosted reports the tightest cap across all scopes; local is always \"local\". */\n scope: \"local\";\n /**\n * The guard's own next-call estimate (the costlier of the last LLM and last\n * tool call — the same value the default reservation uses). 0 until the first\n * call is recorded, so a planner can't divide by a cold estimate.\n *\n * Optional so adding it stays a non-breaking, additive change for any code\n * that constructs a `BudgetAdvisory` literal; `advisory()` always sets it.\n */\n expectedCost?: number;\n /**\n * How many more calls the remaining budget buys at expectedCost:\n * floor(remainingUsd / expectedCost). null when expectedCost is 0 (no call\n * recorded yet) — unknown, not zero. Optional for the same additive reason as\n * expectedCost; `advisory()` always sets it.\n */\n estCallsRemaining?: number | null;\n /**\n * Aggregate token utilization in basis points (0..10000), mirroring usedBps\n * for the token ceiling. null when no `tokenLimit` is set (nothing to signal).\n */\n tokenUsedBps?: number | null;\n /** Tokens left before the aggregate token ceiling. null when no `tokenLimit`. */\n remainingTokens?: number | null;\n /**\n * Per-step headroom for the innermost active step — present (non-null) ONLY\n * while a step() is active AND that dimension is capped. null otherwise.\n */\n stepRemainingUsd?: number | null;\n stepRemainingTokens?: number | null;\n /**\n * Spend rate in USD per minute over this guard's lifetime (spentUsd ÷ minutes\n * since the guard was created) — the number voice teams watch. null only when no\n * wall-clock time has elapsed yet. Make one guard per call/turn for a per-call\n * burn rate; a long-lived guard reports the session average. Optional for the\n * same additive reason as the fields above; `advisory()` always sets it.\n */\n burnRateUsdPerMin?: number | null;\n}\n\nexport class BudgetGuard {\n readonly limitUsd: number;\n spentUsd = 0;\n /**\n * Wall-clock start (ms since epoch) of this guard's spend window, for the $/min\n * burn rate (advisory().burnRateUsdPerMin). One guard per call/turn ⇒ a per-call\n * rate. Set at construction; tests set it directly to control elapsed time.\n */\n createdAtMs = Date.now();\n priceOverrides?: Record<string, ManualPrice>;\n failClosed: boolean;\n nearLimitBps: number;\n /** Aggregate token ceiling, or null when the token dimension is disabled. */\n readonly tokenLimit: number | null;\n /** Aggregate tokens accrued (prompt + completion + cache buckets). */\n spentTokens = 0;\n /** Tokens held in-flight — the token twin of `reserved`. */\n private reservedTokens = 0;\n /** Step stack: innermost step is last. Empty when no step() is active. */\n private readonly steps: StepState[] = [];\n\n private readonly onBlock: (spentUsd: number, limitUsd: number) => void;\n /**\n * Costs of the most recent priced LLM call and tool call, tracked\n * SEPARATELY: the default next-call prediction is the max of the two, so a\n * cheap tool call can't shrink the estimate right before an expensive LLM\n * call (or vice versa) — conservative beats one-call-too-late.\n */\n private lastLlmCost = 0;\n private lastToolCost = 0;\n /** USD held for in-flight calls (reserved, not yet settled). Counts toward the ceiling. */\n private reserved = 0;\n /** Per-call ledger, oldest first; a ring buffer when maxLogEvents is set. */\n private readonly spendEvents: SpendEvent[] = [];\n private readonly maxLogEvents?: number;\n /**\n * Per-tool running totals (settleTool/recordTool) — the tool side of the one\n * shared ceiling, exposed via the toolCosts getter. null-prototype: tool\n * names are caller-supplied strings, so a \"__proto__\" name is stored as\n * plain data instead of mutating the object's prototype.\n */\n private readonly toolCostTotals: Record<string, number> = Object.create(null);\n\n /**\n * Opt-in ledger sync — OFF by default (zero-telemetry). No key is stored and\n * nothing is ever sent until enableSync() is called; see enableSync()/sync().\n */\n private syncEnabled = false;\n private syncKey: string | null = null;\n private syncBase: string | null = null;\n\n /**\n * @param limitUsd the spend ceiling, in USD. `0` blocks the very first call.\n */\n constructor(limitUsd: number, options: BudgetGuardOptions = {}) {\n if (!Number.isFinite(limitUsd) || limitUsd < 0) {\n // NaN/Infinity would make every check() comparison fail-open, silently\n // disabling the guard — reject them up front.\n throw new RangeError(\n `limitUsd must be a finite, non-negative number, got ${limitUsd}`,\n );\n }\n // `=== undefined` (not `??`) so an explicit null is rejected by validation\n // rather than silently defaulting — matches Python, which rejects None.\n const nearLimitBps = options.nearLimitBps === undefined ? 8000 : options.nearLimitBps;\n if (!Number.isInteger(nearLimitBps) || nearLimitBps < 0 || nearLimitBps > 10000) {\n throw new RangeError(\n `nearLimitBps must be an integer in 0..10000, got ${nearLimitBps}`,\n );\n }\n if (\n options.maxLogEvents !== undefined &&\n (!Number.isInteger(options.maxLogEvents) || options.maxLogEvents < 0)\n ) {\n throw new RangeError(\n `maxLogEvents must be a non-negative integer, got ${options.maxLogEvents}`,\n );\n }\n if (\n options.tokenLimit !== undefined &&\n (!Number.isInteger(options.tokenLimit) || options.tokenLimit < 0)\n ) {\n throw new RangeError(\n `tokenLimit must be a non-negative integer, got ${options.tokenLimit}`,\n );\n }\n this.limitUsd = limitUsd;\n this.tokenLimit = options.tokenLimit ?? null;\n this.maxLogEvents = options.maxLogEvents;\n this.priceOverrides = options.priceOverrides;\n this.failClosed = options.failClosed ?? true;\n this.onBlock = options.onBlock ?? defaultOnBlock;\n this.nearLimitBps = nearLimitBps;\n }\n\n /**\n * Throw {@link BudgetExceeded} if the next call would cross the ceiling.\n *\n * Call this immediately before each LLM request. The \"next call\" is estimated\n * conservatively as the costlier of the last LLM call and the last tool call\n * (override with `estimatedNextCost`); the\n * first call is always allowed unless the ceiling is already met. In-flight\n * reservations count toward the total, so this stays correct alongside\n * {@link BudgetGuard.reserve}.\n *\n * Note: `check` is a non-binding peek. For parallel calls, use `reserve()` /\n * `settle()`, which hold the estimate across the await.\n */\n check(estimatedNextCost?: number, options: { estimatedTokens?: number } = {}): void {\n const rawEstimate =\n estimatedNextCost === undefined ? this.defaultEstimate() : estimatedNextCost;\n if (!Number.isFinite(rawEstimate)) {\n // NaN/Infinity would poison the comparisons and fail-open — reject it\n // (parity with the constructor's Number.isFinite guard).\n throw new RangeError(\n `estimatedNextCost must be a finite number, got ${rawEstimate}`,\n );\n }\n const estimate = Math.max(0, rawEstimate);\n this.validateTokenEstimate(options.estimatedTokens);\n const tokens = Math.max(0, options.estimatedTokens ?? 0);\n const blocked = this.blockingCross(estimate, tokens);\n if (blocked !== null) this.raiseBlock(blocked);\n }\n\n /**\n * Atomically check the ceiling AND hold the estimated cost in flight.\n *\n * The concurrency-safe enforcement path: call before the request and hold the\n * returned reservation across the await, so parallel callers can't all clear\n * the same stale total. Throws {@link BudgetExceeded} (without reserving) if\n * the reservation would cross the ceiling. Returns the reservation handle to\n * pass to {@link BudgetGuard.settle} (or {@link BudgetGuard.release} on error).\n * `estimatedCost` defaults to the costlier of the last LLM call and the last\n * tool call.\n */\n reserve(estimatedCost?: number, options: { estimatedTokens?: number } = {}): ReservationHandle {\n const rawEstimate = estimatedCost === undefined ? this.defaultEstimate() : estimatedCost;\n if (!Number.isFinite(rawEstimate)) {\n // NaN would poison this.reserved and fail-open the ceiling — reject it.\n throw new RangeError(\n `estimatedCost must be a finite number, got ${rawEstimate}`,\n );\n }\n const estimate = Math.max(0, rawEstimate);\n // Reject a fractional/NaN/Infinity token estimate BEFORE mutating\n // reservedTokens/step holds — a NaN would fail-open the integer token\n // comparisons and corrupt the tally. A negative int is clamped by Math.max.\n this.validateTokenEstimate(options.estimatedTokens);\n const tokens = Math.max(0, options.estimatedTokens ?? 0);\n const blocked = this.blockingCross(estimate, tokens);\n if (blocked !== null) this.raiseBlock(blocked);\n this.reserved += estimate;\n this.reservedTokens += tokens;\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n step.reservedUsd += estimate;\n step.reservedTokens += tokens;\n }\n // Plain number only when nothing token- or step-shaped is in play, so\n // pre-existing USD-only callers get byte-for-byte the old handle.\n if (tokens === 0 && step === null) return estimate;\n return { usd: estimate, tokens };\n }\n\n /**\n * Release a reservation and record the actual cost. `record` is `settle` with\n * no reservation. Returns the USD cost of this call; unpriceable-model handling\n * matches {@link BudgetGuard.record}, and any held reservation is released even\n * on the warn-and-skip path. A priced call appends one {@link SpendEvent} to\n * {@link BudgetGuard.spendLog} (`label` tags it, e.g. with an agent/task name);\n * the warn-and-skip path accrues nothing and logs nothing, so the ledger stays\n * in lockstep with `spentUsd`.\n */\n settle(\n model: string,\n promptTokens: number,\n completionTokens: number,\n options: { reserved?: ReservationHandle; price?: ManualPrice; label?: string } = {},\n ): number {\n const reserved = options.reserved ?? 0;\n // A bad reserved handle would corrupt this.reserved and break the ceiling for\n // OTHER in-flight calls (negative → phantom hold; Infinity → clears all holds).\n // reservedUsdOf validates the handle (raw number, or a BudgetReservation's fields).\n const reservedUsd = this.reservedUsdOf(reserved);\n let overrides = this.priceOverrides;\n if (options.price !== undefined) {\n overrides = { ...(overrides ?? {}), [model]: options.price };\n }\n\n const priced = resolvePrice(model, overrides);\n if (priced === null) {\n console.warn(\n `Cannot price model '${model}': not in the bundled cost map and no ` +\n `manual price given. The budget guard cannot enforce a ceiling on ` +\n `spend it cannot measure — pass { price } or set it in priceOverrides.`,\n );\n // Release any held reservation on BOTH paths. Fail-closed must not leak\n // the in-flight hold, or reserved grows permanently and remainingUsd\n // shrinks until reserve() starts blocking everything.\n this.release(reserved);\n if (this.failClosed) {\n throw new UnpriceableModelError(model);\n }\n return 0;\n }\n\n let cost: number;\n try {\n cost = priceTokens(priced, promptTokens, completionTokens);\n } catch (err) {\n // priceTokens can throw (e.g. non-finite costs). Release the in-flight\n // hold before re-throwing so `reserved` doesn't leak and shrink\n // remainingUsd permanently — same fail-safe as the unpriceable path above.\n this.release(reserved);\n throw err;\n }\n if (reserved) {\n this.consumeReservation(reserved);\n }\n // Tokens accrued match what priceTokens bills (prompt + completion; negative\n // counts clamp to 0, as in pricing).\n const accruedTokens = Math.max(0, promptTokens) + Math.max(0, completionTokens);\n this.spentUsd += cost;\n this.spentTokens += accruedTokens;\n this.accrueStep(cost, accruedTokens);\n // Clamp a sub-epsilon float overshoot back to the limit so the running total\n // never reports as having crossed the ceiling by a rounding artifact.\n if (this.spentUsd - this.limitUsd > 0 && this.spentUsd - this.limitUsd < EPS) {\n this.spentUsd = this.limitUsd;\n }\n this.lastLlmCost = cost;\n this.appendEvent({\n timestamp: Date.now() / 1000,\n kind: \"llm\",\n modelOrTool: model,\n promptTokens,\n completionTokens,\n costUsd: cost,\n ...(options.label !== undefined ? { label: options.label } : {}),\n // 0 means \"no reservation\" (the plain record() path) — omit rather than\n // log a meaningless zero. Log the USD amount so the ledger schema stays\n // number-shaped even for a BudgetReservation.\n ...(reservedUsd ? { reserved: reservedUsd } : {}),\n });\n return cost;\n }\n\n /**\n * Price one response's tokens offline and add the cost to the total.\n *\n * Returns the USD cost of this call. If the model is unpriceable and no `price`\n * is given, behaviour depends on `failClosed`: warn + throw (default), or\n * warn + skip accrual.\n */\n record(\n model: string,\n promptTokens: number,\n completionTokens: number,\n options: { price?: ManualPrice; label?: string } = {},\n ): number {\n return this.settle(model, promptTokens, completionTokens, {\n reserved: 0,\n price: options.price,\n label: options.label,\n });\n }\n\n /**\n * Atomically check the ceiling AND hold a tool call's cost in flight.\n *\n * The tool-spend counterpart of {@link BudgetGuard.reserve} — and STRONGER\n * than the LLM path, because a paid tool's price is usually known exactly\n * before the call, so the pre-call hard-stop is precise rather than\n * estimated:\n *\n * const handle = guard.reserveTool(0.02); // throws BEFORE Apollo runs\n * const result = await apollo.peopleLookup(...);\n * guard.settleTool(\"apollo.people_lookup\", 0.02, { reserved: handle });\n *\n * Throws {@link BudgetExceeded} (without reserving) if the call would cross\n * the ceiling. The estimate is required — tools have no last-cost prediction\n * worth falling back to. Pass the returned handle to\n * {@link BudgetGuard.settleTool}, or {@link BudgetGuard.release} on failure.\n */\n reserveTool(estimatedCost: number): ReservationHandle {\n if (estimatedCost === undefined) {\n // reserve(undefined) would silently fall back to the last-cost prediction\n // (0 on a fresh guard) — an unguarded tool call. A missing price must\n // fail loudly, e.g. guard.reserveTool(priceTable[tool]).\n throw new RangeError(\"reserveTool requires an estimated cost, got undefined\");\n }\n if (!Number.isFinite(estimatedCost) || estimatedCost < 0) {\n // reserve() clamps a negative estimate to 0 (lenient LLM contract) — for\n // a tool that would reserve nothing: the same unguarded call.\n throw new RangeError(\n `estimatedCost must be a finite, non-negative number, got ${estimatedCost}`,\n );\n }\n return this.reserve(estimatedCost);\n }\n\n /**\n * Release a reservation and record a tool call's actual cost.\n *\n * `recordTool` is `settleTool` with no reservation. The caller supplies the\n * cost — tools have no token usage to price. Accrues into the same\n * `spentUsd` ceiling as tokens, tallies the per-tool total\n * ({@link BudgetGuard.toolCosts}), updates the tool side of the next-call\n * estimate (tracked separately from the LLM side; the default prediction is\n * the max of the two, so a tool-hammering loop's plain `check()` stops\n * BEFORE the crossing call without a cheap tool shrinking the LLM\n * prediction), and appends\n * a `kind: \"tool\"` {@link SpendEvent} to {@link BudgetGuard.spendLog}.\n * Returns `costUsd`.\n */\n settleTool(\n tool: string,\n costUsd: number,\n options: { reserved?: ReservationHandle; label?: string } = {},\n ): number {\n if (!Number.isFinite(costUsd) || costUsd < 0) {\n throw new RangeError(`costUsd must be a finite, non-negative number, got ${costUsd}`);\n }\n const reserved = options.reserved ?? 0;\n // A bad reserved handle would corrupt the in-flight tally and break the\n // ceiling for OTHER calls — same contract as settle(). reservedUsdOf\n // validates the handle (raw number, or a BudgetReservation's fields).\n const reservedUsd = this.reservedUsdOf(reserved);\n if (reserved) {\n this.consumeReservation(reserved);\n }\n this.spentUsd += costUsd;\n // A paid tool spends the step's USD too (no tokens to price).\n this.accrueStep(costUsd, 0);\n // Same sub-epsilon clamp as settle(): never report a rounding-artifact\n // crossing of the ceiling.\n if (this.spentUsd - this.limitUsd > 0 && this.spentUsd - this.limitUsd < EPS) {\n this.spentUsd = this.limitUsd;\n }\n this.lastToolCost = costUsd;\n this.toolCostTotals[tool] = (this.toolCostTotals[tool] ?? 0) + costUsd;\n this.appendEvent({\n timestamp: Date.now() / 1000,\n kind: \"tool\",\n modelOrTool: tool,\n promptTokens: null,\n completionTokens: null,\n costUsd,\n ...(options.label !== undefined ? { label: options.label } : {}),\n ...(reservedUsd ? { reserved: reservedUsd } : {}),\n });\n return costUsd;\n }\n\n /**\n * Accrue a non-LLM cost (a paid tool/API call) against the same ceiling.\n *\n * Post-hoc accrual for costs only known after the call (metered APIs); when\n * the price is known up front, {@link BudgetGuard.reserveTool} /\n * {@link BudgetGuard.settleTool} give the stronger pre-call hard-stop. See\n * `settleTool` for the full contract. Returns `costUsd`.\n */\n recordTool(tool: string, costUsd: number, options: { label?: string } = {}): number {\n return this.settleTool(tool, costUsd, { reserved: 0, label: options.label });\n }\n\n /**\n * Drop an in-flight reservation without recording spend (e.g. the call failed\n * before producing usage). Safe to call with `0`.\n */\n release(reserved: ReservationHandle): void {\n // Validate before the zero-check so a NaN handle throws instead of being\n // silently dropped (a leak); a bad handle corrupts the in-flight tally.\n // reservedUsdOf validates the handle (raw number, or a BudgetReservation's fields).\n this.reservedUsdOf(reserved);\n if (!reserved) return;\n this.consumeReservation(reserved);\n }\n\n /** USD left before the ceiling, net of in-flight reservations (never negative). */\n get remainingUsd(): number {\n return Math.max(0, this.limitUsd - this.spentUsd - this.reserved);\n }\n\n /**\n * Per-tool running USD totals, keyed by the name given to `settleTool()` /\n * `recordTool()` — e.g. `{\"apollo.people_lookup\": 0.42, \"exa.search\": 0.11}`.\n * Makes the token/tool split of the one shared ceiling inspectable\n * (`spentUsd - sum of toolCosts` is the token side). Returns a snapshot copy.\n */\n get toolCosts(): Record<string, number> {\n return { ...this.toolCostTotals };\n }\n\n /**\n * The per-call spend ledger, oldest first — one {@link SpendEvent} per priced\n * `record()` / `settle()` / `recordTool()` / `settleTool()`. Returns a\n * snapshot copy: mutating it cannot corrupt the ledger.\n */\n get spendLog(): SpendEvent[] {\n return [...this.spendEvents];\n }\n\n /**\n * The spend ledger as JSONL — one event per line, newline-terminated.\n *\n * The schema is stable and language-independent (snake_case keys, fixed order;\n * optional fields omitted when absent), identical to the Python package's\n * `export_log()`, so heterogeneous agents produce logs you can concatenate and\n * analyse as one stream. (The *schema* is the contract, not the bytes: the two\n * runtimes may render the same float differently, e.g. JS `0.0000025` vs\n * Python `2.5e-06`.) Empty ledger yields `\"\"`.\n */\n exportLog(): string {\n return this.spendEvents\n .map((e) => {\n // snake_case wire shape, fixed key order — the cross-language schema.\n const row: Record<string, unknown> = {\n timestamp: e.timestamp,\n kind: e.kind,\n model_or_tool: e.modelOrTool,\n prompt_tokens: e.promptTokens,\n completion_tokens: e.completionTokens,\n cost_usd: e.costUsd,\n };\n if (e.label !== undefined) row.label = e.label;\n if (e.reserved !== undefined) row.reserved = e.reserved;\n return `${JSON.stringify(row)}\\n`;\n })\n .join(\"\");\n }\n\n /**\n * **Opt in** to pushing this guard's ledger to Floe's Reconcile Mode.\n *\n * Off by default — this is the *only* way to turn sync on, and it turns on\n * nothing else: it stores your key/endpoint and does **not** send. Nothing\n * leaves the process until you call {@link BudgetGuard.sync}. Zero-telemetry\n * stays the default; there is no background send.\n *\n * Why: Floe's gateway can't see spend it never routed (BYOK, self-hosted,\n * off-path). Syncing your local ledger is what makes that spend — and your\n * **Coverage Score** — visible. Budget, not balance: it reports what you already\n * spent for coverage/attribution; it moves no money.\n *\n * What will leave the process when you call {@link BudgetGuard.sync}: exactly the\n * {@link BudgetGuard.exportLog} JSONL (priced spend events — no prompts, no\n * content, no identifiers beyond a `label` you set).\n *\n * @param apiKey Floe agent key (`floe_<hex>`, `read_write`). Defaults to the\n * `FLOE_API_KEY` env var at `sync()` time.\n * @param options.baseUrl API base. Defaults to `FLOE_API_BASE_URL`, else prod.\n *\n * Revoke with {@link BudgetGuard.disableSync}; the guard sends nothing after.\n */\n enableSync(apiKey?: string, options: { baseUrl?: string } = {}): void {\n this.syncEnabled = true;\n this.syncKey = apiKey ?? null;\n this.syncBase = options.baseUrl ?? null;\n }\n\n /**\n * Turn ledger sync back off (revoke the opt-in). The guard sends nothing after\n * this; {@link BudgetGuard.sync} throws until {@link BudgetGuard.enableSync} is\n * called again.\n */\n disableSync(): void {\n this.syncEnabled = false;\n this.syncKey = null;\n this.syncBase = null;\n }\n\n /**\n * Push the current spend ledger to Reconcile Mode; resolve with events accepted.\n *\n * Requires {@link BudgetGuard.enableSync} first — this is the explicit send, the\n * only thing that transmits the ledger. Throws if sync isn't enabled (so a guard\n * that never opted in can never send). An empty ledger is a no-op (`0`, no\n * network). Re-syncing is safe: the server is idempotent, so already-ingested\n * events are not double-counted.\n *\n * @throws {Error} sync was not enabled (call {@link BudgetGuard.enableSync} first).\n * @throws {LedgerSyncError} the opt-in send failed (missing key, non-2xx, network).\n */\n async sync(): Promise<number> {\n // Snapshot opt-in state + key/base synchronously, before any await: a\n // disableSync() interleaved on a later microtask can't clear the key mid-call\n // (which would silently fall back to $FLOE_API_KEY) or let an upload start\n // after revocation. A disable happens-before (we throw) or happens-after (we\n // send the key we captured while still enabled).\n const enabled = this.syncEnabled;\n const key = this.syncKey;\n const base = this.syncBase;\n if (!enabled) {\n throw new Error(\n \"ledger sync is not enabled — call guard.enableSync(apiKey) first. \" +\n \"Sync is opt-in and off by default (zero-telemetry).\",\n );\n }\n return pushLedger(this.exportLog(), key ?? undefined, { baseUrl: base ?? undefined });\n }\n\n /**\n * The default next-call prediction when the caller supplies no estimate.\n * Conservative: the costlier of the last LLM call and the last tool call — a\n * mixed loop predicts the pricier kind, which at worst blocks one call early\n * (fail-closed) rather than letting a crossing call through because the LAST\n * event happened to be cheap.\n */\n private defaultEstimate(): number {\n return Math.max(this.lastLlmCost, this.lastToolCost);\n }\n\n /**\n * Subtract a settled/released hold from the in-flight tally. A handle larger\n * than EVERYTHING currently held cannot have come from a matching\n * `reserve()` — throwing beats silently clamping, which would free OTHER\n * callers' holds and fail the ceiling open. The epsilon absorbs float dust\n * from accumulating and draining many holds; per-caller over-release (a\n * handle within the total but larger than the caller's own hold) is\n * undetectable without per-handle tracking and remains the caller's\n * responsibility.\n */\n private consumeReservation(reserved: ReservationHandle): void {\n const usd = isReservation(reserved) ? reserved.usd : reserved;\n const tokens = isReservation(reserved) ? reserved.tokens : 0;\n if (usd > this.reserved + EPS) {\n throw new RangeError(\n `reserved handle (${usd}) exceeds total in-flight reservations ` +\n `(${this.reserved}) — a handle must come from a matching reserve()`,\n );\n }\n if (tokens > this.reservedTokens) {\n throw new RangeError(\n `reserved token handle (${tokens}) exceeds total in-flight token ` +\n `reservations (${this.reservedTokens}) — a handle must come from a matching reserve()`,\n );\n }\n this.reserved = Math.max(0, this.reserved - usd);\n this.reservedTokens = Math.max(0, this.reservedTokens - tokens);\n // Drain the innermost active step too (mirrors the aggregate drain). A\n // handle is drained against whatever step is innermost now — the\n // sequential-loop contract; concurrent parallel steps are out of scope (#46).\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n step.reservedUsd = Math.max(0, step.reservedUsd - usd);\n step.reservedTokens = Math.max(0, step.reservedTokens - tokens);\n }\n }\n\n /**\n * The USD amount of a handle, validated. Both shapes are checked — a raw\n * number and a {@link BudgetReservation}'s `usd`/`tokens` fields — so a bad\n * hand-rolled handle can't corrupt the in-flight tally.\n */\n /**\n * Validate a caller-supplied token estimate. Rejects a fraction, NaN,\n * Infinity, or boolean (via `Number.isInteger`) so it can't disable the\n * integer token hard-stops or corrupt the in-flight token tally. `undefined`\n * (the default) is fine; a negative integer is clamped by `Math.max(0, ...)`,\n * matching the lenient USD estimate. Mirrors Python's `_validate_token_estimate`.\n */\n private validateTokenEstimate(estimatedTokens: number | undefined): void {\n if (estimatedTokens !== undefined && !Number.isInteger(estimatedTokens)) {\n throw new RangeError(`estimatedTokens must be an integer, got ${estimatedTokens}`);\n }\n }\n\n private reservedUsdOf(reserved: ReservationHandle): number {\n if (isReservation(reserved)) {\n // A BudgetReservation is public (re-exported), so validate its fields here\n // — a hand-rolled { usd: NaN, tokens: -1 } must not slip past into\n // consumeReservation and corrupt the in-flight tallies. Mirrors Python's\n // BudgetReservation.__post_init__.\n if (!Number.isFinite(reserved.usd) || reserved.usd < 0) {\n throw new RangeError(\n `reserved.usd must be a finite, non-negative number, got ${reserved.usd}`,\n );\n }\n if (!Number.isInteger(reserved.tokens) || reserved.tokens < 0) {\n throw new RangeError(\n `reserved.tokens must be a non-negative integer, got ${reserved.tokens}`,\n );\n }\n return reserved.usd;\n }\n if (!Number.isFinite(reserved) || reserved < 0) {\n throw new RangeError(`reserved must be a finite, non-negative number, got ${reserved}`);\n }\n return reserved;\n }\n\n /**\n * Accrue a settled call into the innermost active step (no-op when none).\n * Sequential-loop contract: the innermost step owns the call.\n */\n private accrueStep(cost: number, tokens: number): void {\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n step.spentUsd += cost;\n step.spentTokens += tokens;\n }\n }\n\n /**\n * The ONE choke point, across two dimensions and two scopes. Returns the first\n * ceiling that blocks as `[dimension, scope, spent, limit]` — dimension\n * `\"usd\" | \"tokens\"`, scope `\"aggregate\" | \"step\"` — or `null` if the call\n * fits everywhere. Aggregate is checked before step (the guard-wide ceiling\n * is the hard limit). `spent` / `limit` are captured here so `raiseBlock`\n * (called outside the lock) reads no shared state and stays race-free.\n */\n private blockingCross(\n estimateUsd: number,\n estimateTokens: number,\n ): [\"usd\" | \"tokens\", \"aggregate\" | \"step\", number, number] | null {\n // Aggregate USD — same comparison the original check/reserve used.\n const committed = this.spentUsd + this.reserved;\n if (committed > this.limitUsd - EPS || committed + estimateUsd > this.limitUsd + EPS) {\n return [\"usd\", \"aggregate\", this.spentUsd, this.limitUsd];\n }\n // Aggregate tokens (integers — no epsilon).\n if (this.tokenLimit !== null) {\n const committedT = this.spentTokens + this.reservedTokens;\n if (committedT >= this.tokenLimit || committedT + estimateTokens > this.tokenLimit) {\n return [\"tokens\", \"aggregate\", committedT, this.tokenLimit];\n }\n }\n // Innermost active step's caps (crossing an outer step requires first\n // crossing the inner one, so the innermost is sufficient).\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n if (step !== null) {\n if (step.maxUsd !== null) {\n const sCommitted = step.spentUsd + step.reservedUsd;\n if (sCommitted > step.maxUsd - EPS || sCommitted + estimateUsd > step.maxUsd + EPS) {\n // Report the step's own committed spend and ceiling so BudgetExceeded\n // names the actual violated boundary, not the aggregate values.\n return [\"usd\", \"step\", sCommitted, step.maxUsd];\n }\n }\n if (step.maxTokens !== null) {\n const sCommittedT = step.spentTokens + step.reservedTokens;\n if (sCommittedT >= step.maxTokens || sCommittedT + estimateTokens > step.maxTokens) {\n return [\"tokens\", \"step\", sCommittedT, step.maxTokens];\n }\n }\n }\n return null;\n }\n\n /** Notify + throw the right error for a [dimension, scope, spent, limit] block. */\n private raiseBlock(blocked: [\"usd\" | \"tokens\", \"aggregate\" | \"step\", number, number]): never {\n const [dimension, scope, spent, limit] = blocked;\n if (dimension === \"usd\") {\n this.onBlock(spent, limit);\n throw new BudgetExceeded(spent, limit);\n }\n // Token block — counts came from blockingCross; on_block is dollar-shaped\n // so a token block skips it.\n throw new TokenBudgetExceeded(Math.trunc(spent), Math.trunc(limit), scope);\n }\n\n /**\n * Scope a per-step USD and/or token cap for a **sequential agent loop**.\n *\n * Runs `fn` with a step pushed onto the guard's stack; the\n * enforcement/accrual path honours the innermost active step *on top of* the\n * aggregate ceilings. A call that would cross the step's `maxUsd` / `maxTokens`\n * is hard-blocked ({@link BudgetExceeded} / {@link TokenBudgetExceeded} with\n * `scope: \"step\"`) even if the aggregate budget has room. `fn` receives the\n * SAME guard, so no adapter needs to know about steps. The step is popped when\n * `fn` settles or throws. **Not for concurrent parallel steps on one guard** —\n * that's out of scope for issue #46; use one guard per parallel branch.\n */\n step<T>(\n options: { maxUsd?: number; maxTokens?: number },\n fn: (guard: BudgetGuard) => T,\n ): T {\n const { maxUsd, maxTokens } = options;\n if (maxUsd !== undefined && (!Number.isFinite(maxUsd) || maxUsd < 0)) {\n throw new RangeError(`maxUsd must be a finite, non-negative number, got ${maxUsd}`);\n }\n if (maxTokens !== undefined && (!Number.isInteger(maxTokens) || maxTokens < 0)) {\n throw new RangeError(`maxTokens must be a non-negative integer, got ${maxTokens}`);\n }\n const state: StepState = {\n maxUsd: maxUsd ?? null,\n maxTokens: maxTokens ?? null,\n spentUsd: 0,\n spentTokens: 0,\n reservedUsd: 0,\n reservedTokens: 0,\n };\n this.steps.push(state);\n // Pop our own state; splice by identity so an out-of-order exit can't\n // silently drop someone else's step.\n const pop = (): void => {\n const idx = this.steps.lastIndexOf(state);\n if (idx !== -1) this.steps.splice(idx, 1);\n };\n let result: T;\n try {\n result = fn(this);\n } catch (err) {\n pop();\n throw err;\n }\n // An async `fn` returns a promise synchronously — popping now would disable\n // step enforcement across every `await` inside it (the common agent-loop\n // shape). Keep the step on the stack until the promise settles, then pop.\n if (result != null && typeof (result as { then?: unknown }).then === \"function\") {\n return (result as unknown as Promise<unknown>).finally(pop) as unknown as T;\n }\n pop();\n return result;\n }\n\n private appendEvent(event: SpendEvent): void {\n // Frozen for parity with Python's frozen dataclass: spendLog copies the\n // array but shares the event objects, so an unfrozen event would let a\n // consumer silently rewrite logged history.\n this.spendEvents.push(Object.freeze(event));\n if (this.maxLogEvents !== undefined && this.spendEvents.length > this.maxLogEvents) {\n // Ring buffer: drop the oldest overflow (at most one per append).\n this.spendEvents.splice(0, this.spendEvents.length - this.maxLogEvents);\n }\n }\n\n /**\n * Context-aware spend advisory for this budget — see {@link BudgetAdvisory}.\n *\n * `nearLimit` flips once utilization reaches `nearLimitBps` (default 80%), so an\n * agent can taper *before* the hard-stop. Advisory only: read it to adapt;\n * {@link BudgetGuard.check} is what enforces the ceiling.\n */\n advisory(): BudgetAdvisory {\n // Floor (not round) so usedBps never over-reports utilization and nearLimit\n // flips exactly when the threshold is reached; the epsilon absorbs float noise\n // and Math.floor matches Python's int() exactly (round() would diverge).\n const usedBps =\n this.limitUsd <= 0\n ? 10000\n : Math.max(0, Math.min(10000, Math.floor((this.spentUsd / this.limitUsd) * 10000 + 1e-9)));\n const remainingUsd = Math.max(0, this.limitUsd - this.spentUsd);\n // The costlier of the last LLM and tool call — the guard's own default\n // reservation estimate; 0 before any call is recorded.\n const expectedCost = Math.max(this.lastLlmCost, this.lastToolCost);\n // +1e-9 absorbs float noise so e.g. 0.6/0.2 floors to 3 not 2 (same epsilon\n // rationale as usedBps above, and keeps Python int() parity).\n const estCallsRemaining =\n expectedCost > 0 ? Math.floor(remainingUsd / expectedCost + 1e-9) : null;\n // Burn rate: spend ÷ minutes elapsed since the guard was created. null until\n // any wall-clock time has passed (avoids a divide-by-zero at t0); $0 spent over\n // real elapsed time is a legitimate 0/min, not null.\n const elapsedMin = (Date.now() - this.createdAtMs) / 60000;\n const burnRateUsdPerMin = elapsedMin > 0 ? this.spentUsd / elapsedMin : null;\n // Aggregate token utilization, mirroring usedBps. null (no signal) when the\n // token dimension is unset — a pure USD guard's advisory is unchanged.\n let tokenUsedBps: number | null = null;\n let remainingTokens: number | null = null;\n let nearToken = false;\n if (this.tokenLimit !== null) {\n tokenUsedBps =\n this.tokenLimit <= 0\n ? 10000\n : Math.max(\n 0,\n Math.min(10000, Math.floor((this.spentTokens / this.tokenLimit) * 10000 + 1e-9)),\n );\n remainingTokens = Math.max(0, this.tokenLimit - this.spentTokens);\n nearToken = tokenUsedBps >= this.nearLimitBps;\n }\n // Innermost active step's headroom + nearness (per dimension, only when\n // capped). null keeps the fields absent for old readers.\n const step = this.steps.length ? this.steps[this.steps.length - 1] : null;\n let stepRemainingUsd: number | null = null;\n let stepRemainingTokens: number | null = null;\n let nearStep = false;\n if (step !== null) {\n if (step.maxUsd !== null) {\n stepRemainingUsd = Math.max(0, step.maxUsd - step.spentUsd);\n if (step.maxUsd <= 0) {\n nearStep = true;\n } else {\n const sBps = Math.floor((step.spentUsd / step.maxUsd) * 10000 + 1e-9);\n nearStep = nearStep || sBps >= this.nearLimitBps;\n }\n }\n if (step.maxTokens !== null) {\n stepRemainingTokens = Math.max(0, step.maxTokens - step.spentTokens);\n if (step.maxTokens <= 0) {\n nearStep = true;\n } else {\n const sTBps = Math.floor((step.spentTokens / step.maxTokens) * 10000 + 1e-9);\n nearStep = nearStep || sTBps >= this.nearLimitBps;\n }\n }\n }\n return {\n // nearLimit now also flips when a token ceiling or the active step is near\n // its cap, so a router can downshift before ANY hard-stop.\n nearLimit: usedBps >= this.nearLimitBps || nearToken || nearStep,\n usedBps,\n // Settled budget: limit minus accrued spend, deliberately NOT net of\n // in-flight reservations. Unlike the remainingUsd getter (which subtracts\n // `reserved`), the advisory is a soft utilization signal about money already\n // spent, while the getter reports what a new call can still claim.\n remainingUsd,\n limitUsd: this.limitUsd,\n spentUsd: this.spentUsd,\n scope: \"local\",\n expectedCost,\n estCallsRemaining,\n tokenUsedBps,\n remainingTokens,\n stepRemainingUsd,\n stepRemainingTokens,\n burnRateUsdPerMin,\n };\n }\n}\n\nfunction defaultOnBlock(spentUsd: number, limitUsd: number): void {\n console.error(\n \"BUDGET EXCEEDED — call blocked\\n\" +\n ` spent so far: $${spentUsd.toFixed(6)} | ceiling: $${limitUsd.toFixed(6)}\\n` +\n \" The next call would cross your budget; floe-guard stopped your agent \" +\n \"before it ran.\",\n );\n}\n","/**\n * LatencyBudget — a cumulative tool-chain deadline, sibling to BudgetGuard.\n *\n * BudgetGuard stops an agent before its next call crosses a USD ceiling;\n * LatencyBudget stops it before the next call would blow an end-user SLA:\n *\n * ```ts\n * const deadline = new LatencyBudget(5000);\n * ...\n * deadline.check(800); // throws DeadlineExceeded when projected over\n * if (deadline.advisory().nearDeadline) useFasterModel();\n * router.pick({ maxLatencyMs: deadline.remainingMs });\n * ```\n *\n * Design notes (mirroring `src/floe_guard/latency.py`):\n * - **Monotonic clock** — `performance.now()`, never wall time.\n * - **Cooperative, not preemptive** — the guard provides the deadline signal;\n * aborting a stalled in-flight call is the framework's job (AbortSignal).\n * `check()` prevents the NEXT call from starting.\n * - **Advisory symmetry** — `nearDeadline` / `usedBps` / `remainingMs` are the\n * latency twin of BudgetGuard's `nearLimit` / `usedBps` / `remainingUsd`.\n * - **In-process scope** — one instance per request/run; distributed latency\n * tracking is out of scope.\n */\n\nimport { DeadlineExceeded } from \"./errors.js\";\n\n/** A context-aware deadline signal — the latency twin of {@link BudgetAdvisory}.\n * Soft by design; the hard-stop is {@link LatencyBudget.check}. */\nexport interface LatencyAdvisory {\n nearDeadline: boolean;\n /** SLA consumed, basis points 0..10000 (8500 = 85%). */\n usedBps: number;\n remainingMs: number;\n slaMs: number;\n elapsedMs: number;\n}\n\nexport interface LatencyBudgetOptions {\n /**\n * Utilization (basis points, 0..10000) at which {@link LatencyBudget.advisory}\n * flags `nearDeadline` so an agent can downshift to a faster path before the\n * wall. Default 8000 (80%), matching BudgetGuard's `nearLimitBps`.\n */\n nearDeadlineBps?: number;\n /** Invoked with `(elapsedMs, slaMs)` right before {@link DeadlineExceeded} is thrown. */\n onBlock?: (elapsedMs: number, slaMs: number) => void;\n /** Milliseconds-returning monotonic clock, injectable for tests. Defaults to `performance.now`. */\n clock?: () => number;\n}\n\nexport class LatencyBudget {\n readonly slaMs: number;\n readonly nearDeadlineBps: number;\n private readonly onBlock?: (elapsedMs: number, slaMs: number) => void;\n private readonly clock: () => number;\n private readonly startedAt: number;\n\n /** The budget starts counting at construction — build it when the request\n * (and its SLA) starts. */\n constructor(slaMs: number, options: LatencyBudgetOptions = {}) {\n if (!(Number.isFinite(slaMs) && slaMs > 0)) {\n throw new RangeError(\"slaMs must be > 0\");\n }\n const nearDeadlineBps = options.nearDeadlineBps ?? 8000;\n if (!Number.isInteger(nearDeadlineBps) || nearDeadlineBps < 0 || nearDeadlineBps > 10000) {\n throw new RangeError(\"nearDeadlineBps must be an integer 0..10000\");\n }\n this.slaMs = slaMs;\n this.nearDeadlineBps = nearDeadlineBps;\n this.onBlock = options.onBlock;\n this.clock = options.clock ?? (() => performance.now());\n this.startedAt = this.clock();\n }\n\n /** Milliseconds since construction (monotonic). */\n get elapsedMs(): number {\n return this.clock() - this.startedAt;\n }\n\n /** Milliseconds left before the SLA, floored at 0 — the readable signal a\n * router uses to pick a faster fallback or truncate work mid-chain. */\n get remainingMs(): number {\n return Math.max(0, this.slaMs - this.elapsedMs);\n }\n\n /**\n * Throw {@link DeadlineExceeded} when the projected elapsed time (now +\n * `expectedMs` for the upcoming call) would blow the SLA. Call it\n * immediately before each tool/model call; pass 0 to only gate on time\n * already spent.\n */\n check(expectedMs = 0): void {\n if (!(Number.isFinite(expectedMs) && expectedMs >= 0)) {\n throw new RangeError(\"expectedMs must be >= 0\");\n }\n const elapsed = this.elapsedMs;\n if (elapsed + expectedMs > this.slaMs) {\n this.onBlock?.(elapsed, this.slaMs);\n throw new DeadlineExceeded(elapsed, this.slaMs);\n }\n }\n\n /** The soft near-deadline signal — symmetric to `BudgetGuard.advisory()`. */\n advisory(): LatencyAdvisory {\n const elapsed = this.elapsedMs;\n // round (not floor) — matches the Python twin; float clock arithmetic can\n // land a hair under an exact boundary.\n const usedBps = elapsed > 0 ? Math.min(10000, Math.round((elapsed * 10000) / this.slaMs)) : 0;\n return {\n nearDeadline: usedBps >= this.nearDeadlineBps,\n usedBps,\n remainingMs: Math.max(0, this.slaMs - elapsed),\n slaMs: this.slaMs,\n elapsedMs: elapsed,\n };\n }\n}\n","/**\n * Vercel AI SDK middleware that enforces a {@link BudgetGuard} in the call path.\n *\n * This is the TypeScript counterpart to the Python framework adapters. The AI SDK\n * is TypeScript-only, so it ships as its own npm package.\n *\n * Works with BOTH `ai@4` (`LanguageModelV1Middleware`) and `ai@5`\n * (`LanguageModelV2Middleware`). The two majors renamed the middleware type and\n * the usage fields (`promptTokens`/`completionTokens` → `inputTokens`/\n * `outputTokens`), so this module deliberately imports nothing from `ai`: the\n * middleware is typed structurally against the surface both majors share, and\n * usage is read from whichever field pair the installed SDK reports.\n *\n * - `wrapGenerate({ doGenerate, model })` — we `reserve()` (throws to hard-stop)\n * BEFORE calling `doGenerate()`, hold the reservation across the await, then\n * `settle()` from `result.usage`.\n * - `wrapStream({ doStream, model })` — we `reserve()` BEFORE `doStream()`, then\n * `settle()` from the `finish` part as the stream drains.\n *\n * Reserving before the await is what makes parallel calls (`Promise.all` over\n * several generations) honour the ceiling: each holds its slice instead of all\n * reading the same stale total (issue #18). The reservation is released if the\n * call throws, is cancelled by the consumer, or if a stream completes without a\n * usage-bearing finish part — in the last case the stream is also rejected with\n * an error rather than treated as a free ($0) call (fail-closed).\n *\n * The model id used for pricing comes from `model.modelId`.\n */\n\nimport type { BudgetGuard } from \"./guard.js\";\n\n/**\n * The middleware call surface shared by `ai@4` and `ai@5`. Both majors invoke\n * `wrapGenerate`/`wrapStream` with an options object carrying these members\n * (plus richer `model` fields we don't read). `doGenerate`/`doStream` are\n * declared optional so every 4.x/5.x minor's options type stays assignable —\n * the SDK always provides the one each hook actually calls.\n */\ninterface MiddlewareCallOptions {\n doGenerate?: () => PromiseLike<any>;\n doStream?: () => PromiseLike<any>;\n model: { modelId: string };\n params?: unknown;\n}\n\n/**\n * Structural stand-in for `LanguageModelV1Middleware` (ai@4) and\n * `LanguageModelV2Middleware` (ai@5) — assignable to the `middleware` option of\n * `wrapLanguageModel` on either major.\n */\nexport interface BudgetGuardMiddleware {\n wrapGenerate: (options: MiddlewareCallOptions) => Promise<any>;\n wrapStream: (options: MiddlewareCallOptions) => Promise<any>;\n}\n\n/**\n * Read prompt/completion token counts from an ai@4 usage object\n * (`promptTokens`/`completionTokens`) or an ai@5 one (`inputTokens`/\n * `outputTokens`). Throws when either count is missing or non-numeric: the guard\n * cannot meter spend it cannot see, and treating it as $0 would fail open.\n */\nfunction usageTokens(\n modelId: string,\n usage: unknown,\n): { promptTokens: number; completionTokens: number } {\n const u = usage as\n | {\n promptTokens?: unknown;\n completionTokens?: unknown;\n inputTokens?: unknown;\n outputTokens?: unknown;\n }\n | null\n | undefined;\n const promptTokens = u?.promptTokens ?? u?.inputTokens;\n const completionTokens = u?.completionTokens ?? u?.outputTokens;\n if (typeof promptTokens !== \"number\" || typeof completionTokens !== \"number\") {\n throw new Error(\n `Model '${modelId}' reported no token usage — the budget guard cannot ` +\n `meter spend it cannot see, so this call is rejected rather than ` +\n `treated as free.`,\n );\n }\n return { promptTokens, completionTokens };\n}\n\n/**\n * Build a budget-guard middleware that hard-stops the model before a call\n * crosses the guard's USD ceiling, and records priced token usage after.\n * Compatible with `wrapLanguageModel` from both `ai@4` and `ai@5`.\n *\n * @example\n * import { wrapLanguageModel } from \"ai\";\n * import { openai } from \"@ai-sdk/openai\";\n * import { BudgetGuard, budgetGuardMiddleware } from \"floe-guard\";\n *\n * const guard = new BudgetGuard(5.00);\n * const model = wrapLanguageModel({\n * model: openai(\"gpt-4o\"),\n * middleware: budgetGuardMiddleware(guard),\n * });\n */\nexport function budgetGuardMiddleware(guard: BudgetGuard): BudgetGuardMiddleware {\n return {\n async wrapGenerate({ doGenerate, model }) {\n const reserved = guard.reserve(); // throws BudgetExceeded before the call runs\n let result: any;\n try {\n result = await doGenerate!();\n } catch (err) {\n guard.release(reserved); // the call failed before settle() took ownership\n throw err;\n }\n // From here settle() OWNS the reservation: it releases the hold on its own\n // throw (unpriceable / non-finite cost) and consumes it on success. Releasing\n // again would double-subtract and clear a concurrent call's in-flight hold.\n let handled = false;\n try {\n const { promptTokens, completionTokens } = usageTokens(\n model.modelId,\n result?.usage,\n );\n handled = true;\n guard.settle(model.modelId, promptTokens, completionTokens, { reserved });\n return result;\n } catch (err) {\n if (!handled) guard.release(reserved); // failed reading usage, before settle()\n throw err;\n }\n },\n\n async wrapStream({ doStream, model }) {\n const reserved = guard.reserve(); // throws BudgetExceeded before the stream starts\n let streamResult: any;\n try {\n streamResult = await doStream!();\n } catch (err) {\n guard.release(reserved); // the call failed before settle() took ownership\n throw err;\n }\n const { stream, ...rest } = streamResult as {\n stream: ReadableStream<any>;\n } & Record<string, unknown>;\n\n // `handled` flips once the reservation is disposed — settled on the finish\n // part, or released exactly once if the stream produced no usage (flush) or\n // ended early via error/cancellation (cancel). settle() owns disposal once\n // called, so nothing else may release after `handled` is set, or we'd\n // double-subtract and clear a concurrent call's hold.\n let handled = false;\n const guarded = stream.pipeThrough(\n new TransformStream<any, any>({\n transform(chunk, controller) {\n if (chunk?.type === \"finish\" && !handled) {\n try {\n const { promptTokens, completionTokens } = usageTokens(\n model.modelId,\n chunk.usage,\n );\n handled = true;\n guard.settle(model.modelId, promptTokens, completionTokens, { reserved });\n } catch (err) {\n // Release only if we never reached settle() (e.g. usage missing).\n // If settle() itself threw, it already released its own hold.\n if (!handled) {\n handled = true;\n guard.release(reserved);\n }\n throw err;\n }\n }\n controller.enqueue(chunk);\n },\n flush() {\n // The stream closed cleanly but never emitted a usage-bearing\n // finish part. Per the package's fail-closed philosophy (see\n // usageTokens()), we cannot treat an unmetered LLM call as free:\n // release the hold first (so the ceiling is not permanently\n // depleted), then throw so the consumer learns the call was not\n // settled. This mirrors how wrapGenerate() rejects missing usage.\n if (!handled) {\n handled = true;\n guard.release(reserved);\n throw new Error(\n `Model '${model.modelId}' stream completed without a token-usage ` +\n `finish part — the budget guard cannot meter spend it cannot see, ` +\n `so this call is rejected rather than treated as free.`,\n );\n }\n },\n cancel() {\n // Upstream error or consumer cancellation: flush() does not run here\n // (Web Streams: flush and cancel are mutually exclusive), so release\n // the still-held reservation.\n if (!handled) {\n handled = true;\n guard.release(reserved);\n }\n },\n // `cancel` is valid per the Streams spec and supported in Node 18+, but\n // TS's Transformer lib type lags and omits it — cast to keep the type check.\n } as Transformer<any, any>),\n );\n\n return { stream: guarded, ...rest };\n },\n };\n}\n","import { FloeGuardError } from \"./errors.js\";\nimport { type BudgetAdvisory, BudgetGuard } from \"./guard.js\";\n\nexport interface RetryPlan<T> {\n /** Operation to run for this retry attempt. */\n call: () => T | Promise<T>;\n /**\n * Estimated retry cost passed to `guard.check()` before the retry runs.\n * Leave undefined to use the guard's default last-call estimate.\n */\n estimatedCost?: number;\n}\n\nexport interface BudgetRetryOptions<T> {\n /** Estimated cost for retrying the original call. */\n estimatedCost?: number;\n /** Total attempts, including the first call. Default: 2. */\n maxAttempts?: number;\n /** Choose a cheaper retry plan when `guard.advisory().nearLimit` is true. */\n onDegrade?: (\n error: unknown,\n advisory: BudgetAdvisory,\n ) => RetryPlan<T> | Promise<RetryPlan<T> | undefined> | undefined;\n /**\n * Decide whether an error is retryable.\n *\n * Defaults to all non-{@link FloeGuardError} errors — every\n * {@link FloeGuardError} (including {@link BudgetExceeded} and\n * {@link UnpriceableModelError}) is treated as terminal because retrying a\n * deterministic guard error cannot fix it and would only multiply LLM spend.\n */\n retryIf?: (error: unknown) => boolean;\n}\n\n/**\n * Return `true` only for transient, non-{@link FloeGuardError} errors.\n *\n * Every {@link FloeGuardError} — including {@link BudgetExceeded} and\n * {@link UnpriceableModelError} — is considered terminal: retrying cannot fix\n * a deterministic guard error and would only multiply LLM spend.\n */\nfunction defaultRetryIf(error: unknown): boolean {\n return !(error instanceof FloeGuardError);\n}\n\n/**\n * Run `call` with budget-aware retries.\n *\n * The first attempt runs unchanged. If it fails with a retryable error, the\n * helper retries as-is with ample budget, asks `onDegrade` for a cheaper plan\n * when near the limit, and always calls `guard.check(estimatedCost)` before a\n * retry so an over-budget retry is blocked before it runs.\n */\nexport async function withBudgetRetry<T>(\n guard: BudgetGuard,\n call: () => T | Promise<T>,\n options: BudgetRetryOptions<T> = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? 2;\n if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {\n throw new RangeError(`maxAttempts must be an integer >= 1, got ${maxAttempts}`);\n }\n const retryIf = options.retryIf ?? defaultRetryIf;\n let plan: RetryPlan<T> = { call, estimatedCost: options.estimatedCost };\n\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n try {\n return await plan.call();\n } catch (error) {\n if (attempt >= maxAttempts || !retryIf(error)) {\n throw error;\n }\n plan = await nextPlan(guard, error, plan, options.onDegrade);\n }\n }\n\n throw new Error(\"unreachable\");\n}\n\nasync function nextPlan<T>(\n guard: BudgetGuard,\n error: unknown,\n current: RetryPlan<T>,\n onDegrade: BudgetRetryOptions<T>[\"onDegrade\"],\n): Promise<RetryPlan<T>> {\n const advisory = guard.advisory();\n if (advisory.nearLimit && onDegrade !== undefined) {\n const degraded = await onDegrade(error, advisory);\n if (degraded !== undefined) {\n guard.check(degraded.estimatedCost);\n return degraded;\n }\n }\n guard.check(current.estimatedCost);\n return current;\n}\n","/**\n * Offline pricing for voice legs (STT / TTS / telephony) from the vendored map.\n *\n * The voice twin of `pricing.ts`. The token cost map is per-token; a voice\n * pipeline's bill is per-second (STT), per-1k-chars (TTS), and per-minute\n * (telephony) instead, so the voice rates live under the reserved `\"__voice__\"`\n * key of `cost_map.json` with their own schema:\n *\n * \"deepgram-nova-3\": {\n * \"mode\": \"stt\", // discriminator: \"stt\" | \"tts\" | \"telephony\"\n * \"unit\": \"usd_per_second\", // explicit unit — a mismatch fails closed\n * \"rate\": 0.0001283333, // USD in that unit\n * \"provider\": \"deepgram\"\n * }\n *\n * Same fail-closed contract as token pricing: a vendor absent from the map (or an\n * entry whose `unit`/`mode` does not match the leg it is asked to price) is\n * unpriceable — {@link lookupVoiceRate} returns `null` and the adapter throws\n * {@link UnpriceableVoiceError} rather than silently metering the leg at $0. Pass a\n * per-unit override to enforce a leg the map cannot price.\n *\n * No network. The rates are a **drift-prone snapshot** of each vendor's public\n * list price — refresh them (`scripts/update-cost-map.mjs`) like the token map, or\n * estimates drift as vendors change prices. Telephony is **US-only in v1**.\n *\n * This is a faithful port of `src/floe_guard/voice_pricing.py`.\n */\n\nimport costMapJson from \"./cost_map.json\";\nimport { UnpriceableVoiceError } from \"./errors.js\";\n\nexport type VoiceMode = \"stt\" | \"tts\" | \"telephony\";\n\ninterface VoiceMapEntry {\n mode?: unknown;\n unit?: unknown;\n rate?: unknown;\n provider?: unknown;\n}\n\n/** The vendored voice rates — the reserved `\"__voice__\"` section of the cost map. */\nconst VOICE_MAP = ((costMapJson as Record<string, unknown>)[\"__voice__\"] ?? {}) as Record<\n string,\n VoiceMapEntry\n>;\n\n/**\n * The one unit each leg is billed in. An entry whose `unit` disagrees with its\n * leg is a schema mismatch and fails closed — a Deepgram $/min figure stored\n * without the ÷60 conversion would over-bill 60x if it were silently accepted.\n */\nexport const unitForMode: Record<VoiceMode, string> = {\n stt: \"usd_per_second\",\n tts: \"usd_per_1k_chars\",\n telephony: \"usd_per_minute\",\n};\n\n/** A resolved per-unit voice rate plus where it came from. */\nexport interface VoiceRate {\n mode: string;\n unit: string;\n rate: number;\n source: \"override\" | \"cost_map\";\n}\n\nfunction finiteNonNegative(value: unknown): value is number {\n // typeof excludes booleans (Python's _finite_non_negative excludes bool too).\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0;\n}\n\n/**\n * Resolve a voice vendor to its per-unit rate, or `null` if unpriceable.\n *\n * The pure resolver (the voice twin of {@link resolvePrice}): it never throws.\n * Fail-closed on every mismatch — a missing vendor, an entry whose `mode` is not\n * `mode`, an entry whose `unit` is not the canonical unit for `mode`, or a\n * non-finite/negative rate — so a schema slip can never surface as a silent,\n * valid-looking price.\n */\nexport function lookupVoiceRate(model: string | null | undefined, mode: VoiceMode): number | null {\n if (model === null || model === undefined) return null;\n const entry = Object.prototype.hasOwnProperty.call(VOICE_MAP, model)\n ? VOICE_MAP[model]\n : undefined;\n if (!entry) return null;\n if (entry.mode !== mode) return null;\n if (entry.unit !== unitForMode[mode]) return null;\n const rate = entry.rate;\n if (!finiteNonNegative(rate)) return null;\n return rate;\n}\n\n/**\n * Resolve a leg's per-unit rate, fail-closed. An `override` wins over the map.\n *\n * Throws {@link UnpriceableVoiceError} when neither an override nor the bundled\n * map can price the leg — the guard refuses to meter spend it cannot measure.\n */\nexport function resolveVoiceRate(\n model: string | null | undefined,\n mode: VoiceMode,\n override?: number | null,\n): VoiceRate {\n if (override !== undefined && override !== null) {\n if (!finiteNonNegative(override)) {\n throw new RangeError(\n `voice ${mode} override must be a finite, non-negative number, got ${override}`,\n );\n }\n return { mode, unit: unitForMode[mode], rate: override, source: \"override\" };\n }\n const rate = lookupVoiceRate(model, mode);\n if (rate === null) throw new UnpriceableVoiceError(model ?? null, mode);\n return { mode, unit: unitForMode[mode], rate, source: \"cost_map\" };\n}\n\n/**\n * USD for one leg. `quantity` is seconds (stt), characters (tts), or minutes\n * (telephony); negative quantities clamp to zero.\n */\nexport function voiceLegCost(mode: VoiceMode, quantity: number, rate: number): number {\n // Reject non-finite quantities: Math.max(0, NaN) is NaN, so a NaN/Infinity\n // quantity would return a non-finite USD amount that then poisons the guard's\n // running total (NaN disables every ceiling comparison). Negatives still clamp.\n if (!Number.isFinite(quantity)) {\n throw new RangeError(`voice ${mode} quantity must be a finite number, got ${quantity}`);\n }\n const q = Math.max(0, quantity);\n if (mode === \"stt\") return q * rate; // seconds * $/second\n if (mode === \"tts\") return (q / 1000) * rate; // chars / 1000 * $/1k-chars\n if (mode === \"telephony\") return q * rate; // minutes * $/minute\n throw new RangeError(`unknown voice mode ${mode}`);\n}\n\n/**\n * USD for one voice leg, or `null` when the leg is unconfigured (skip it).\n *\n * The single entry point the adapters use. A leg with neither a vendor (`model`)\n * nor an `override` is unconfigured — return `null` so the token-only contract is\n * preserved and nothing is metered. A leg that *is* configured but cannot be\n * priced throws {@link UnpriceableVoiceError} (via {@link resolveVoiceRate}) rather\n * than accruing a silent $0.\n */\nexport function priceVoiceLeg(\n mode: VoiceMode,\n quantity: number,\n options: { model?: string | null; override?: number | null } = {},\n): number | null {\n const model = options.model ?? null;\n const override = options.override ?? null;\n if (model === null && override === null) return null;\n const resolved = resolveVoiceRate(model, mode, override);\n return voiceLegCost(mode, quantity, resolved.rate);\n}\n","/**\n * Pre-call admission gates for voice orchestrators.\n *\n * These decide **at the call boundary** whether to *admit* or *reject* an incoming\n * call, from the guard's remaining budget. They return the exact JSON shape each\n * provider's inbound webhook expects, so a free/local user can reject a call on\n * budget exhaustion with the **same contract** the hosted gateway serves — the paid\n * upgrade is a URL swap, not a rewrite.\n *\n * Scope — strictly pre-call. A gate answers one question: *given the budget left,\n * do we let this call start?* It does **not** intervene mid-call. Once a call is\n * admitted it runs to completion; nothing here cuts a call off partway.\n *\n * Budget, not balance: admission reads the guard's local *remaining budget*, a\n * ceiling signal — not an account balance. US-only telephony, v1.\n *\n * A faithful port of `src/floe_guard/gates.py`.\n */\n\nimport type { BudgetGuard } from \"./guard.js\";\n\n/**\n * True when there isn't budget left to admit one more call.\n *\n * A **non-binding preflight read**: it checks the guard's `remainingUsd` but does\n * *not* reserve anything, so under concurrent inbound calls it can admit more than\n * the budget strictly covers (two calls may see the same headroom and both pass).\n * It is coarse admission control — the binding, atomic hard-stop is the in-call\n * guard (`check` / `reserve` while the call runs). This mirrors the hosted pre-dial\n * gate, which is likewise check-only.\n *\n * With the default `estimatedCallUsd = 0` a call is rejected only once the budget\n * is fully spent (`remainingUsd` at or below zero). Pass an estimate (e.g.\n * `$/min × expected minutes`) to reject earlier, when the remaining budget can't\n * cover it; an estimate that *exactly* equals the remaining budget still admits\n * (inclusive ceiling, matching `BudgetGuard.reserve`). Budget signal, not a balance.\n *\n * @throws RangeError when `estimatedCallUsd` is negative or non-finite — a bad\n * estimate must not silently admit an exhausted guard (NaN/negative comparisons\n * are always false, the same trap `BudgetGuard` rejects for `limitUsd`).\n */\nexport function budgetExhausted(\n guard: BudgetGuard,\n options: { estimatedCallUsd?: number } = {},\n): boolean {\n // Default ONLY undefined — a `null` estimate is malformed input and must reach\n // validation (below) rather than being silently coerced to 0 (which would admit\n // the call with no intended headroom). Mirrors Python raising on a None estimate.\n const estimatedCallUsd = options.estimatedCallUsd === undefined ? 0 : options.estimatedCallUsd;\n if (!Number.isFinite(estimatedCallUsd) || estimatedCallUsd < 0) {\n throw new RangeError(\n `estimatedCallUsd must be a finite, non-negative number, got ${estimatedCallUsd}`,\n );\n }\n const remainingUsd = guard.remainingUsd;\n return remainingUsd <= 0 || remainingUsd < estimatedCallUsd;\n}\n\n/**\n * Generic pre-call admission decision: `true` = admit, `false` = reject.\n *\n * The provider-agnostic gate for Pipecat, custom telephony, or any inbound hook\n * without a first-class helper below. Wire the `false` case into your provider's\n * reject path.\n *\n * TODO(bland): Bland's *Send Call* metadata field name is an OPEN VERIFICATION\n * ITEM (pending confirmation) — this package does not invent it. Use `preCall` to\n * get the admit/reject decision and map `false` onto Bland's Pathway Webhook node\n * once the field name is confirmed.\n */\nexport function preCall(guard: BudgetGuard, options: { estimatedCallUsd?: number } = {}): boolean {\n return !budgetExhausted(guard, options);\n}\n\n/**\n * Response for Retell's inbound-call webhook — reject or admit the call.\n *\n * On budget exhaustion returns `{ call_inbound: { reject: true } }`; otherwise\n * `{ call_inbound: {...} }` carrying any `admit` overrides you pass\n * (`dynamic_variables`, `metadata`, `override_agent_id`, …).\n *\n * Retell contract (verified against docs.retellai.com/features/inbound-call-webhook):\n * **only the boolean `true` rejects** — any other value is ignored. The webhook\n * fires for inbound **phone and SMS** calls (not dial-to-sip); its behaviour for\n * web calls is not documented, so treat this as phone/SMS-only. 10s timeout, up to\n * 3 retries.\n */\nexport function retell(\n guard: BudgetGuard,\n options: { estimatedCallUsd?: number; admit?: Record<string, unknown> } = {},\n): { call_inbound: Record<string, unknown> } {\n if (budgetExhausted(guard, { estimatedCallUsd: options.estimatedCallUsd })) {\n return { call_inbound: { reject: true } };\n }\n // The gate's budget decision — not the caller's overrides — controls admission.\n // Strip `reject` from admit overrides so an errant `admit: { reject: true }` on\n // an available-budget call can't flip the response into Retell's reject shape.\n const safeAdmit = { ...(options.admit ?? {}) };\n delete safeAdmit.reject;\n return { call_inbound: safeAdmit };\n}\n\n/**\n * Response for Vapi's `assistant-request` webhook — reject or admit the call.\n *\n * On budget exhaustion returns `{ error: errorMessage }` (Vapi speaks it, then ends\n * the call). Otherwise admits with `{ assistantId }` if given (`assistantId` takes\n * precedence), else `{ assistant }`.\n *\n * Vapi has **no** `reject: true` boolean — rejection *is* returning an error, and\n * admission *is* returning an assistant. Respond within **~7.5 s** or the call may\n * fail (verified against docs.vapi.ai/server-url/spam-call-rejection).\n *\n * @throws RangeError when admitted (budget available) but neither `assistant` nor\n * `assistantId` was provided — there'd be nothing to hand Vapi.\n */\nexport function vapi(\n guard: BudgetGuard,\n options: {\n assistant?: Record<string, unknown>;\n assistantId?: string;\n errorMessage?: string;\n estimatedCallUsd?: number;\n } = {},\n): Record<string, unknown> {\n const {\n assistant,\n assistantId,\n errorMessage = \"Sorry, this agent is out of budget right now.\",\n estimatedCallUsd,\n } = options;\n if (budgetExhausted(guard, { estimatedCallUsd })) {\n return { error: errorMessage };\n }\n if (assistantId !== undefined && assistantId !== null) {\n return { assistantId };\n }\n if (assistant !== undefined && assistant !== null) {\n return { assistant };\n }\n throw new RangeError(\n \"vapi() admitted the call but has nothing to return: pass assistant or \" +\n \"assistantId for the admit path.\",\n );\n}\n"],"mappings":"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","names":[]}
|