@theokit/sdk-budget 0.3.2 → 0.3.3

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BudgetTracker } from '@theokit/sdk';
1
+ import { BudgetMode, BudgetSnapshot, BudgetHandle, BudgetOptions, TokenUsage, BudgetWindow, BudgetTracker } from '@theokit/sdk';
2
2
 
3
3
  /**
4
4
  * M7-6 — honest-null cost render helper. Turns the `number | undefined` cost
@@ -38,9 +38,35 @@ declare function formatCostUsd(cost: number | undefined, opts?: FormatCostUsdOpt
38
38
  *
39
39
  * @internal
40
40
  */
41
+ /**
42
+ * Midnight UTC of `now`'s day — the start of a `1d` budget window.
43
+ *
44
+ * Always UTC, never the host's local timezone: "1 USD per day" resets at 00:00Z, so a budget in
45
+ * UTC-05:00 resets at 19:00 local. That is deliberate — a budget shared by processes in different
46
+ * regions must reset at one instant — but it surprises anyone reading a daily total at local
47
+ * midnight.
48
+ */
41
49
  declare function startOfDayUtc(now?: Date): Date;
50
+ /**
51
+ * Midnight UTC on the MONDAY of `now`'s week — the start of a `1w` budget window.
52
+ *
53
+ * ISO 8601, so the week starts on Monday, not Sunday. This is calendar-aligned rather than rolling:
54
+ * "5 USD per week" resets on Monday, which is what a person reading a weekly budget expects, and not
55
+ * a trailing 168 hours. `30d` and `365d` are relative for the mirror-image reason — nobody expects a
56
+ * monthly budget to reset on the 1st.
57
+ */
42
58
  declare function startOfWeekUtc(now?: Date): Date;
43
- /** Returns the inclusive start timestamp (ms) for the given window relative to `now`. */
59
+ /**
60
+ * Inclusive start timestamp (ms) of `window`, as `spentIn` uses it to decide which charges count.
61
+ *
62
+ * Two different behaviours behind one enum, and the difference is visible in every total:
63
+ *
64
+ * - `1d` / `1w` are CALENDAR-aligned to UTC (midnight; ISO Monday). Spend resets at a boundary.
65
+ * - `1h` / `30d` / `365d` are ROLLING — `now` minus the duration. Nothing ever "resets"; the
66
+ * oldest charges simply fall out of the window.
67
+ *
68
+ * Throws on a value outside `BudgetWindow`, which TypeScript already prevents.
69
+ */
44
70
  declare function windowStartMs(window: BudgetWindow, now?: Date): number;
45
71
 
46
72
  /**
@@ -56,23 +82,47 @@ declare function windowStartMs(window: BudgetWindow, now?: Date): number;
56
82
  * @internal
57
83
  */
58
84
  /**
59
- * Throws BudgetExceededError if `mode === "block"` and any limit
60
- * would be exceeded. No-op for audit/warn modes (post-charge checks
61
- * handle those).
85
+ * Refuse an upcoming call that would push a `"block"`-mode budget past one of its limits. Call it
86
+ * BEFORE the LLM request, with your cost estimate for that request.
62
87
  *
63
- * Caller invokes this BEFORE the LLM call.
88
+ * Throws `BudgetExceededError` (carrying `budgetName`, `window`, the projected `spentUsd`, the
89
+ * `limitUsd` and `mode: "block"`) for the FIRST limit where `alreadySpent + estimatedUsd` exceeds
90
+ * the limit. The comparison is strict, so landing exactly on the limit is allowed through — while
91
+ * the post-charge `onExceed` callback fires at `>=`. The two boundaries do not agree.
92
+ *
93
+ * SILENTLY DOES NOTHING in three cases, and none of them is distinguishable from "you are within
94
+ * budget" at the call site:
95
+ *
96
+ * - `name` is not registered — a typo, or a budget deleted since. Nothing is enforced.
97
+ * - the budget's mode is `"warn"` or `"audit"` — those never block by design.
98
+ * - the estimate is low. Enforcement is only as good as `estimatedUsd`; passing 0 disables it.
64
99
  */
65
100
  declare function preflightCheck(name: string, estimatedUsd: number): void;
66
101
  /**
67
- * Charge the budget + dispatch threshold/exceed callbacks (EC-8 isolated).
102
+ * Record the ACTUAL cost of a completed call against a budget and fire its callbacks. Call it after
103
+ * the LLM request, paired with `preflightCheck` before it.
104
+ *
105
+ * NEVER THROWS on budget state — including when the charge tips a limit. Enforcement happens on the
106
+ * next `preflightCheck`; this one only records and notifies. A callback that throws is caught and
107
+ * logged to stderr, so your `onThreshold` / `onExceed` cannot break the run either.
108
+ *
109
+ * Per mode:
110
+ *
111
+ * - `"audit"` — charge only, no callbacks.
112
+ * - `"warn"` — charge, then `onThreshold` at 80% or 95%, then `onExceed` at 100%. With no
113
+ * `onExceed` handler it writes a line to stderr instead.
68
114
  *
69
- * - In `audit` mode: charge only, no callbacks.
70
- * - In `warn` mode: charge + onThreshold (80/95) + onExceed (100). No throw.
71
- * - In `block` mode: charge + onThreshold + onExceed. No throw post-call
72
- * (preflightCheck already prevented exceed for the upcoming call;
73
- * this protects against simultaneous-call races where multiple sends
74
- * each pass preflight independently last one to charge may still
75
- * tip a limit. We document the case rather than retroactively throw).
115
+ * Per limit, exactly ONE callback fires per call: the highest matched threshold, or `onExceed`,
116
+ * never both. But there is NO de-duplication across calls a budget sitting at 90% fires
117
+ * `onThreshold(0.8)` again on every single charge, and one past its limit fires `onExceed` on
118
+ * every charge forever. Debounce inside your handler if it pages anyone. A budget with three
119
+ * limits evaluates all three, so one call can fire three callbacks.
120
+ * - `"block"`same callbacks as `"warn"`, still no throw. Concurrent sends each pass their own
121
+ * preflight, so the last to charge can legitimately tip a limit that preflight had cleared.
122
+ *
123
+ * A DELETED OR UNKNOWN BUDGET LOSES THE CHARGE. The function writes a warning to stderr and
124
+ * returns — the spend is not recorded anywhere, so a budget removed mid-flight under-reports rather
125
+ * than over-reports. This is the one path where the ledger and reality diverge on purpose.
76
126
  */
77
127
  declare function chargeAndCheckThresholds(name: string, actualUsd: number): Promise<void>;
78
128
 
@@ -90,9 +140,40 @@ declare function chargeAndCheckThresholds(name: string, actualUsd: number): Prom
90
140
  *
91
141
  * @internal
92
142
  */
93
- /** Charge a budget. Idempotent across concurrent calls via withCwdMutex. */
143
+ /**
144
+ * Append `amountUsd` to the spend ledger for the budget called `name`, timestamped now.
145
+ *
146
+ * NOT idempotent. `withCwdMutex` SERIALIZES concurrent calls so two appends cannot interleave; it
147
+ * does not deduplicate them. Calling this twice with the same arguments records the spend twice,
148
+ * and there is no charge id to reconcile against — an at-least-once caller needs its own guard.
149
+ *
150
+ * NOT VALIDATED against the registry either: charging a name that was never passed to
151
+ * `createBudget` (a typo, say) succeeds silently and accumulates spend that no limit enforces and
152
+ * that `snapshotAll` — which iterates the REGISTRY — will never show.
153
+ *
154
+ * `chargeAndCheckThresholds` does not rescue you from that, and swapping to it trades one silent
155
+ * failure for another: on an unknown or already-deleted name it writes one line to stderr and
156
+ * RETURNS WITHOUT CHARGING, so the spend is lost rather than merely invisible. Neither function
157
+ * throws. Validate the name against `getBudgetOptionsRaw` / `getBudget` if it matters which of the
158
+ * two failures you get.
159
+ *
160
+ * `amountUsd <= 0` is a no-op, so a refund cannot be expressed here.
161
+ *
162
+ * The ledger is a MODULE-LEVEL SINGLETON: process-wide, shared by every budget and every agent in
163
+ * the process, and lost on exit — there is no persistence across restarts. Entries older than a
164
+ * year are garbage-collected opportunistically.
165
+ */
94
166
  declare function charge(name: string, amountUsd: number): Promise<void>;
95
- /** Return total spend in the given window for `name`. Snapshot read (no mutex needed). */
167
+ /**
168
+ * Total USD recorded for `name` inside `window`, summed at call time.
169
+ *
170
+ * Returns `0` for a name that was never charged — indistinguishable from a budget that exists and
171
+ * has spent nothing. Use `getBudget(name)` when you need to tell those apart.
172
+ *
173
+ * Lock-free: it reads the live array without taking the mutex, so a charge landing mid-read is
174
+ * simply included or not. Window boundaries are those of {@link windowStartMs} — `1d` and `1w` are
175
+ * UTC calendar-aligned, the rest are rolling.
176
+ */
96
177
  declare function spentIn(name: string, window: BudgetWindow, now?: Date): number;
97
178
 
98
179
  /**
@@ -117,7 +198,34 @@ declare function spentIn(name: string, window: BudgetWindow, now?: Date): number
117
198
  * @internal
118
199
  */
119
200
  type ApiMode = "anthropic_messages" | "openai_chat_completions" | "openai_responses";
201
+ /**
202
+ * Which usage dialect a provider reports in, inferred from its name.
203
+ *
204
+ * Providers do not agree on the shape of a usage object: Anthropic reports `input_tokens` /
205
+ * `output_tokens`, OpenAI's Responses API reports yet another, and the large
206
+ * OpenAI-chat-compatible family (openai, openrouter, deepseek, google, ollama, lmstudio, …) shares
207
+ * one. This maps a provider name onto that choice.
208
+ *
209
+ * UNKNOWN NAMES FALL BACK to the OpenAI chat-completions shape, because that is what almost every
210
+ * compatible endpoint speaks — a new proxy usually works without a change here. Pass
211
+ * {@link normalizeUsage}'s `apiMode` explicitly when a provider is compatible in its wire format but
212
+ * not in its name.
213
+ */
120
214
  declare function inferApiMode(provider: string): ApiMode;
215
+ /**
216
+ * Turn a provider's raw usage object into the SDK's canonical {@link TokenUsage}.
217
+ *
218
+ * NEVER THROWS, and that is the point: usage arrives from a third party at the end of a successful
219
+ * call, so a missing or malformed field must not fail a request the model already answered. `null`,
220
+ * `undefined` and non-objects all yield an all-zero usage, and unrecognised fields are dropped.
221
+ *
222
+ * The cost of that tolerance is that a zero is ambiguous — it means "the provider reported nothing
223
+ * usable" as readily as "no tokens". A budget that suddenly stops accruing is the symptom of a
224
+ * dialect mismatch, not of free traffic.
225
+ *
226
+ * `apiMode` overrides the guess {@link inferApiMode} makes from the provider name; supply it for a
227
+ * compatible endpoint whose name is not recognised.
228
+ */
121
229
  declare function normalizeUsage(rawUsage: unknown, opts: {
122
230
  provider: string;
123
231
  apiMode?: ApiMode;
@@ -129,12 +237,81 @@ declare function normalizeUsage(rawUsage: unknown, opts: {
129
237
  *
130
238
  * @internal
131
239
  */
240
+ /**
241
+ * Register a budget under `opts.name` and return its live handle.
242
+ *
243
+ * ```ts
244
+ * const b = createBudget({ name: "daily", mode: "block", limits: [{ window: "1d", limitUsd: 5 }] });
245
+ * ```
246
+ *
247
+ * THROWS `ConfigurationError(code: "invalid_budget_name")` in two cases: a name that does not match
248
+ * `^[a-z0-9][a-z0-9_-]*$` (lowercase only — `"Daily"` and `"my.budget"` are rejected), and a name
249
+ * already registered. Duplicate registration is deliberately an error rather than an idempotent
250
+ * return, so a second `createBudget("daily", …)` with different limits cannot silently win.
251
+ *
252
+ * Registering does NOT reset spend. The ledger is keyed by NAME and outlives the registry entry, so
253
+ * re-creating a budget after `deleteBudget` inherits everything charged under that name — a
254
+ * config reload keeps enforcing the day's spend, which is usually right and is a surprise if you
255
+ * expected a fresh start.
256
+ *
257
+ * Process-local and non-persistent: nothing survives a restart, so re-create budgets at startup.
258
+ * `mode` defaults to `"warn"` (see {@link defaultMode}) — a budget created without one observes and
259
+ * does not block.
260
+ */
132
261
  declare function createBudget(opts: BudgetOptions): BudgetHandle;
262
+ /**
263
+ * The live handle for a registered budget, or `undefined` when the name is unknown.
264
+ *
265
+ * `undefined` is the honest answer for "never created" — it does NOT mean "zero spend". A budget
266
+ * that exists but has not been charged returns a handle whose `spentIn(...)` is 0, and the two are
267
+ * different facts: the first means nothing is enforcing a limit.
268
+ *
269
+ * The registry is per-PROCESS and holds no persistence, so a fresh process starts with no budgets
270
+ * and no ledger. Re-create them at startup.
271
+ */
133
272
  declare function getBudget(name: string): BudgetHandle | undefined;
273
+ /**
274
+ * Every budget registered in this process, in insertion order.
275
+ *
276
+ * Empty after a restart — see {@link getBudget} on process-local state. Use it to enumerate what is
277
+ * being enforced; use {@link snapshotAll} when you want the numbers rather than the handles.
278
+ */
134
279
  declare function listBudgets(): readonly BudgetHandle[];
280
+ /**
281
+ * Remove a budget from the registry. Returns `false` when the name was not registered.
282
+ *
283
+ * This stops ENFORCEMENT; it does not refund or clear the ledger. Re-creating a budget under the
284
+ * same name inherits the spend already recorded for that name, which is usually what you want after
285
+ * a config reload and a surprise if you were expecting a reset.
286
+ */
135
287
  declare function deleteBudget(name: string): boolean;
288
+ /**
289
+ * One row per budget PER WINDOW — a budget with three limits produces three rows, not one.
290
+ *
291
+ * `ratio` is `spentUsd / limitUsd`, and is 0 when the limit is 0 rather than `Infinity`, so a
292
+ * zero-limit budget does not poison a dashboard that sums or charts these. Spend is computed at call
293
+ * time from the ledger, counting only entries inside each window, so the same budget reports
294
+ * different numbers for `1d` and `30d`.
295
+ */
136
296
  declare function snapshotAll(): readonly BudgetSnapshot[];
297
+ /**
298
+ * The stored {@link BudgetOptions} exactly as registered, or `undefined` when the name is unknown.
299
+ *
300
+ * Distinct from {@link getBudget}, which returns a HANDLE with live accessors. Use this when you
301
+ * need the configuration itself — the limits array, the callbacks, the declared mode — for example
302
+ * to render it or to re-register the same shape elsewhere.
303
+ *
304
+ * The object is the registry's own, held by reference: mutating it changes what the budget enforces
305
+ * without going through `Budget.create`, which is a bug waiting to happen. Copy before editing.
306
+ */
137
307
  declare function getBudgetOptionsRaw(name: string): BudgetOptions | undefined;
308
+ /**
309
+ * The mode a budget enforces, resolving the omitted case to `"warn"`.
310
+ *
311
+ * `"warn"` fires the callbacks and lets the call through; `"block"` refuses it. The default is
312
+ * deliberate: a budget added for observability must not start rejecting traffic because someone
313
+ * forgot a field.
314
+ */
138
315
  declare function defaultMode(opts: BudgetOptions): BudgetMode;
139
316
 
140
317
  /**
@@ -150,13 +327,36 @@ declare function defaultMode(opts: BudgetOptions): BudgetMode;
150
327
  *
151
328
  * @public
152
329
  */
330
+ /**
331
+ * The two rates needed to price one model, in USD per 1,000,000 tokens.
332
+ *
333
+ * List prices only — this package models no cached-input discount, no batch tier and no negotiated
334
+ * rate, so a cost computed here is an upper bound for anyone on a discount and simply wrong for a
335
+ * provider that bills per request.
336
+ *
337
+ * @public
338
+ */
153
339
  interface ModelPricing {
154
340
  /** USD per 1,000,000 input tokens. */
155
341
  readonly inputPerMillionUsd: number;
156
342
  /** USD per 1,000,000 output tokens. */
157
343
  readonly outputPerMillionUsd: number;
158
344
  }
159
- /** Built-in pricing table — exhaustive enough for v0.1; users supply overrides for niche models. */
345
+ /**
346
+ * The nine models this package prices out of the box, keyed by the exact `model` string a
347
+ * `BudgetUsageEvent` carries: four OpenAI, three Anthropic, two Google.
348
+ *
349
+ * Lookup is exact-match. No prefix stripping, no aliasing, no fuzzy fallback — `"gpt-4o"` and
350
+ * `"openrouter/openai/gpt-4o"` both MISS the `"openai/gpt-4o"` entry, and a miss means unknown
351
+ * cost, which under a `maxUsd` cap denies the run. Pass overrides through
352
+ * `createUsdBudgetTracker({ pricing })` rather than expecting a match.
353
+ *
354
+ * Prices are a dated snapshot (verified 2026-06) of public list prices and DRIFT — treat a total
355
+ * derived from them as an estimate, not as a bill. `Object.freeze` here is shallow: the map cannot
356
+ * gain keys, but the `ModelPricing` objects inside it are mutable.
357
+ *
358
+ * @public
359
+ */
160
360
  declare const BUILTIN_PRICING: Readonly<Record<string, ModelPricing>>;
161
361
  /**
162
362
  * Compute USD cost for a single token event.
@@ -197,19 +397,71 @@ declare function computeUsdCost(pricing: Readonly<Record<string, ModelPricing>>,
197
397
  * @public
198
398
  */
199
399
 
200
- /** Options for `createUsdBudgetTracker`. */
400
+ /**
401
+ * Options for {@link createUsdBudgetTracker}. Omitting all of them builds a tracker that counts and
402
+ * never denies.
403
+ *
404
+ * @public
405
+ */
201
406
  interface UsdBudgetTrackerOptions {
202
- /** Hard ceiling on total tokens (input + output combined). */
407
+ /**
408
+ * Ceiling on total tokens, input and output combined. `check()` denies with
409
+ * `reason: "token_limit"` once the running total REACHES it (`>=`, not `>`).
410
+ */
203
411
  readonly maxTokens?: number;
204
- /** Hard ceiling on cumulative USD spend. */
412
+ /**
413
+ * Ceiling on cumulative USD. `check()` denies with `reason: "cost_limit"` once the total reaches
414
+ * it.
415
+ *
416
+ * TRAP — setting this makes an UNPRICED MODEL DENY EVERYTHING. If any tracked event names a model
417
+ * absent from the pricing table, the total cost becomes permanently unknown, and a cap that
418
+ * cannot be verified fails CLOSED: every subsequent `check()` returns
419
+ * `{ allowed: false, reason: "cost_limit", detail: "cost unknown — …" }`. This is intentional
420
+ * (better than spending unbounded), but it means a model id that merely differs in spelling from
421
+ * a {@link BUILTIN_PRICING} key halts the agent. Supply `pricing` for anything not in that table,
422
+ * or leave `maxUsd` unset and gate on `maxTokens`.
423
+ */
205
424
  readonly maxUsd?: number;
206
- /** Optional override map — model id → pricing entry. Merged onto BUILTIN_PRICING. */
425
+ /**
426
+ * Per-model prices, spread OVER {@link BUILTIN_PRICING} — so an entry here replaces a built-in of
427
+ * the same key, and built-ins you do not name are kept.
428
+ *
429
+ * Keys are matched EXACTLY against `BudgetUsageEvent.model`. There is no prefix, alias or
430
+ * provider-stripping logic: `"gpt-4o"` does not match the built-in `"openai/gpt-4o"`. Use the
431
+ * same id string you pass to `Agent.create({ model })`.
432
+ */
207
433
  readonly pricing?: Readonly<Record<string, ModelPricing>>;
208
434
  }
209
435
  /**
210
- * Build a fresh USD-aware tracker. The returned object exposes the
211
- * `BudgetTracker` contract PLUS `getTotalUsd()` + `nextIteration()`
212
- * helpers for explicit iteration counting.
436
+ * Build a `BudgetTracker` that caps an agent run on tokens, on USD, or on both.
437
+ *
438
+ * ```ts
439
+ * const agent = await Agent.create({
440
+ * model: { id: "openai/gpt-4o-mini" },
441
+ * budgetTracker: createUsdBudgetTracker({ maxUsd: 0.5 }),
442
+ * });
443
+ * ```
444
+ *
445
+ * The agent loop drives it: `track()` on every usage event, `nextIteration()` once per turn, and
446
+ * `check()` before each turn — a denial halts the loop. Everything is per-instance and in-memory,
447
+ * so one tracker is one run; reuse it across runs and the caps carry over.
448
+ *
449
+ * Three things a caller gets wrong here:
450
+ *
451
+ * - **`getTotal()` has no cost in it.** It returns `{ tokens, iterations }` only, and
452
+ * `BudgetTotal.costUsd` is always `undefined` however much was spent. USD comes from the extra
453
+ * `getTotalUsd()` on the returned object, which is `number | undefined` — `undefined` meaning
454
+ * UNKNOWN, never zero.
455
+ * - **Unknown cost is a one-way door.** The first event naming a model outside the pricing table
456
+ * makes `getTotalUsd()` `undefined` for the rest of the run; a later priced event does not
457
+ * restore it. Combined with `maxUsd`, that also denies every later `check()` — see
458
+ * {@link UsdBudgetTrackerOptions.maxUsd}.
459
+ * - **There is no iteration cap.** `iterations` is counted and reported, and nothing ever gates on
460
+ * it. Use `createCounterBudgetTracker` from `@theokit/sdk` when you need `maxIterations`.
461
+ *
462
+ * `check()` evaluates the cost cap first, so a run breaching both caps reports `"cost_limit"`.
463
+ * `track()` never throws and never rejects input — a non-finite or non-positive `tokens` is
464
+ * discarded, silently.
213
465
  */
214
466
  declare function createUsdBudgetTracker(options?: UsdBudgetTrackerOptions): BudgetTracker & {
215
467
  nextIteration(): void;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/format-cost.ts","../src/internal/calendar-window.ts","../src/internal/ledger.ts","../src/internal/registry.ts","../src/internal/enforcement.ts","../src/internal/normalize-usage.ts","../src/usd-pricing.ts","../src/usd-budget-tracker.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * M7-6 — honest-null cost render helper. Turns the `number | undefined` cost\n * (from `computeUsdCost` / `getTotalUsd`, M1-6) into a display string: an\n * unknown cost renders as `\"—\"` (NEVER a dishonest `\"$0\"`), a real number\n * renders as `\"$X.XX\"`. Composes the cost-honesty contract — a known-zero cost\n * (`0`) is distinct from an unknown one (`undefined`).\n *\n * @public\n */\n\n/** Options for {@link formatCostUsd}. */\nexport interface FormatCostUsdOptions {\n /** Marker for an unknown cost (`undefined`). Default `\"—\"`. */\n readonly unknown?: string;\n /** Currency prefix for a known cost. Default `\"$\"`. */\n readonly currency?: string;\n}\n\n/**\n * Render a USD cost for display. `undefined` -> the unknown marker (`\"—\"` by\n * default); a number -> `\"$X.XX\"` (2 decimal places). `0` is a real known-zero\n * and renders `\"$0.00\"`, NOT the unknown marker.\n */\nexport function formatCostUsd(cost: number | undefined, opts: FormatCostUsdOptions = {}): string {\n if (cost === undefined) return opts.unknown ?? \"—\";\n return `${opts.currency ?? \"$\"}${cost.toFixed(2)}`;\n}\n","/**\n * UTC-aligned calendar window helpers (ADR D382).\n *\n * - `1h` — relative (now - 1 hour).\n * - `1d` — UTC midnight (current UTC day).\n * - `1w` — UTC monday 00:00:00 (current UTC week, Monday is week start).\n * - `30d` — relative 30 days.\n * - `365d` — relative 365 days.\n *\n * `1d` and `1w` are calendar-aligned because users expect \"1 USD per day\"\n * = \"since midnight UTC\", not a rolling 24h.\n * `30d`/`365d` are relative because nobody expects \"since the 1st\".\n *\n * @internal\n */\n\nimport type { BudgetWindow } from \"@theokit/sdk\";\n\nexport function startOfDayUtc(now: Date = new Date()): Date {\n return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n}\n\nexport function startOfWeekUtc(now: Date = new Date()): Date {\n // ISO 8601 week starts on Monday. getUTCDay() returns 0 (Sun) .. 6 (Sat).\n const dayOfWeek = now.getUTCDay();\n const daysSinceMonday = (dayOfWeek + 6) % 7; // Mon=0, Sun=6\n const start = startOfDayUtc(now);\n start.setUTCDate(start.getUTCDate() - daysSinceMonday);\n return start;\n}\n\nconst MS_PER_HOUR = 60 * 60 * 1000;\nconst MS_PER_DAY = 24 * MS_PER_HOUR;\n\n/** Returns the inclusive start timestamp (ms) for the given window relative to `now`. */\nexport function windowStartMs(window: BudgetWindow, now: Date = new Date()): number {\n switch (window) {\n case \"1h\":\n return now.getTime() - MS_PER_HOUR;\n case \"1d\":\n return startOfDayUtc(now).getTime();\n case \"1w\":\n return startOfWeekUtc(now).getTime();\n case \"30d\":\n return now.getTime() - 30 * MS_PER_DAY;\n case \"365d\":\n return now.getTime() - 365 * MS_PER_DAY;\n default: {\n const _exhaustive: never = window;\n throw new Error(`unreachable window: ${_exhaustive as string}`);\n }\n }\n}\n","/**\n * In-process Budget ledger (ADR D385).\n *\n * Singleton mutex-protected. Stores per-budget ChargeLog[] arrays;\n * `spentIn(window)` filters by timestamp.\n *\n * EC-6: GC eviction runs INSIDE the same mutex as charge — no race.\n * EC-9: charge() is called inside the same critical section as preflight\n * check by Budget enforcement.\n *\n * Persistence cross-restart: deferred to v0.2 (JsonFile pattern).\n *\n * @internal\n */\n\nimport { type BudgetWindow, withCwdMutex } from \"@theokit/sdk\";\nimport { windowStartMs } from \"./calendar-window.js\";\n\ninterface ChargeLog {\n timestamp: number;\n amountUsd: number;\n}\n\nconst MS_PER_YEAR = 365 * 24 * 60 * 60 * 1000;\nconst GC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\nconst GC_LOGS_THRESHOLD = 10_000;\n\ninterface LedgerState {\n readonly logs: Map<string, ChargeLog[]>;\n lastGcAt: number;\n}\n\nconst state: LedgerState = {\n logs: new Map(),\n lastGcAt: Date.now(),\n};\n\nconst MUTEX_KEY = \"budget-ledger\";\n\nfunction shouldGc(now: number): boolean {\n if (now - state.lastGcAt < GC_INTERVAL_MS) return false;\n let totalLogs = 0;\n for (const arr of state.logs.values()) totalLogs += arr.length;\n return totalLogs > GC_LOGS_THRESHOLD;\n}\n\nfunction gcOlderThanOneYear(now: number): void {\n const cutoff = now - MS_PER_YEAR;\n for (const [name, arr] of state.logs.entries()) {\n const kept = arr.filter((l) => l.timestamp >= cutoff);\n if (kept.length === 0) state.logs.delete(name);\n else state.logs.set(name, kept);\n }\n state.lastGcAt = now;\n}\n\n/** Charge a budget. Idempotent across concurrent calls via withCwdMutex. */\nexport async function charge(name: string, amountUsd: number): Promise<void> {\n if (amountUsd <= 0) return;\n await withCwdMutex(MUTEX_KEY, async () => {\n const now = Date.now();\n const list = state.logs.get(name) ?? [];\n list.push({ timestamp: now, amountUsd });\n state.logs.set(name, list);\n if (shouldGc(now)) gcOlderThanOneYear(now);\n });\n}\n\n/** Return total spend in the given window for `name`. Snapshot read (no mutex needed). */\nexport function spentIn(name: string, window: BudgetWindow, now: Date = new Date()): number {\n const arr = state.logs.get(name);\n if (arr === undefined) return 0;\n const sinceMs = windowStartMs(window, now);\n let total = 0;\n for (const log of arr) {\n if (log.timestamp >= sinceMs) total += log.amountUsd;\n }\n return total;\n}\n","/**\n * Internal Budget registry — keeps live `BudgetOptions` per name.\n * Singleton; no persistence (D385).\n *\n * @internal\n */\n\nimport {\n type BudgetHandle,\n type BudgetMode,\n type BudgetOptions,\n type BudgetSnapshot,\n type BudgetWindow,\n ConfigurationError,\n} from \"@theokit/sdk\";\nimport { spentIn } from \"./ledger.js\";\n\nconst NAME_GRAMMAR = /^[a-z0-9][a-z0-9_-]*$/;\n\nconst registry = new Map<string, BudgetOptions>();\n\nfunction validateBudgetName(name: string): void {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new ConfigurationError(\"Budget name must be a non-empty string\", {\n code: \"invalid_budget_name\",\n });\n }\n if (!NAME_GRAMMAR.test(name)) {\n throw new ConfigurationError(\n `Budget name \"${name}\" must match ^[a-z0-9][a-z0-9_-]*$ (lowercase + dash/underscore, start alphanumeric)`,\n { code: \"invalid_budget_name\" },\n );\n }\n}\n\nexport function createBudget(opts: BudgetOptions): BudgetHandle {\n // EC-7: name validation\n validateBudgetName(opts.name);\n if (registry.has(opts.name)) {\n // EC-16: duplicate throws (vs Task.submit idempotent return)\n throw new ConfigurationError(`Budget \"${opts.name}\" already exists`, {\n code: \"invalid_budget_name\",\n });\n }\n registry.set(opts.name, opts);\n return buildHandle(opts);\n}\n\nexport function getBudget(name: string): BudgetHandle | undefined {\n const opts = registry.get(name);\n if (opts === undefined) return undefined;\n return buildHandle(opts);\n}\n\nexport function listBudgets(): readonly BudgetHandle[] {\n return [...registry.values()].map(buildHandle);\n}\n\nexport function deleteBudget(name: string): boolean {\n return registry.delete(name);\n}\n\nexport function snapshotAll(): readonly BudgetSnapshot[] {\n const result: BudgetSnapshot[] = [];\n for (const opts of registry.values()) {\n for (const lim of opts.limits) {\n const spent = spentIn(opts.name, lim.window);\n result.push({\n name: opts.name,\n window: lim.window,\n spentUsd: spent,\n limitUsd: lim.limitUsd,\n ratio: lim.limitUsd > 0 ? spent / lim.limitUsd : 0,\n });\n }\n }\n return result;\n}\n\nexport function getBudgetOptionsRaw(name: string): BudgetOptions | undefined {\n return registry.get(name);\n}\n\nexport function defaultMode(opts: BudgetOptions): BudgetMode {\n return opts.mode ?? \"warn\";\n}\n\nfunction buildHandle(opts: BudgetOptions): BudgetHandle {\n return {\n name: opts.name,\n mode: defaultMode(opts),\n scope: opts.scope,\n limits: opts.limits,\n spentIn: (window: BudgetWindow) => spentIn(opts.name, window),\n remainingIn: (window: BudgetWindow) => {\n const lim = opts.limits.find((l) => l.window === window);\n if (lim === undefined) return Number.POSITIVE_INFINITY;\n return Math.max(0, lim.limitUsd - spentIn(opts.name, window));\n },\n };\n}\n","/**\n * Budget enforcement (ADRs D383, D386, EC-7/8/9).\n *\n * - `preflightCheck(name, estimatedUsd)` — em `block` mode, throw\n * `BudgetExceededError` before the LLM call if any limit would be\n * exceeded (EC-9 — the caller invokes it inside the mutex section).\n * - `chargeAndCheckThresholds(name, actualUsd)` — apply charge ao\n * ledger + invokes onThreshold/onExceed callbacks isolated in\n * try/catch (EC-8).\n *\n * @internal\n */\n\nimport { BudgetExceededError, type BudgetMode, type BudgetOptions } from \"@theokit/sdk\";\nimport { charge, spentIn } from \"./ledger.js\";\nimport { defaultMode, getBudgetOptionsRaw } from \"./registry.js\";\n\nconst THRESHOLDS = [0.8, 0.95] as const;\ntype Threshold = (typeof THRESHOLDS)[number];\n\n/**\n * Throws BudgetExceededError if `mode === \"block\"` and any limit\n * would be exceeded. No-op for audit/warn modes (post-charge checks\n * handle those).\n *\n * Caller invokes this BEFORE the LLM call.\n */\nexport function preflightCheck(name: string, estimatedUsd: number): void {\n const opts = getBudgetOptionsRaw(name);\n if (opts === undefined) return; // EC-20: budget deleted; charge becomes no-op\n if (defaultMode(opts) !== \"block\") return;\n for (const lim of opts.limits) {\n const currentSpent = spentIn(opts.name, lim.window);\n if (currentSpent + estimatedUsd > lim.limitUsd) {\n throw new BudgetExceededError({\n budgetName: opts.name,\n window: lim.window,\n spentUsd: currentSpent + estimatedUsd,\n limitUsd: lim.limitUsd,\n mode: \"block\",\n });\n }\n }\n}\n\n/**\n * Charge the budget + dispatch threshold/exceed callbacks (EC-8 isolated).\n *\n * - In `audit` mode: charge only, no callbacks.\n * - In `warn` mode: charge + onThreshold (80/95) + onExceed (100). No throw.\n * - In `block` mode: charge + onThreshold + onExceed. No throw post-call\n * (preflightCheck already prevented exceed for the upcoming call;\n * this protects against simultaneous-call races where multiple sends\n * each pass preflight independently — last one to charge may still\n * tip a limit. We document the case rather than retroactively throw).\n */\nexport async function chargeAndCheckThresholds(name: string, actualUsd: number): Promise<void> {\n const opts = getBudgetOptionsRaw(name);\n if (opts === undefined) {\n // EC-20: budget deleted during in-flight call. Charge is silent no-op.\n process.stderr.write(\n `[budget] charge for deleted budget \"${name}\" is a no-op (was the budget removed during in-flight send?)\\n`,\n );\n return;\n }\n const mode = defaultMode(opts);\n\n await charge(opts.name, actualUsd);\n\n if (mode === \"audit\") return;\n await dispatchCallbacksFor(opts, mode);\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: 3-mode × 3-threshold dispatch table inherently branchy; pulling out helpers loses local context.\nasync function dispatchCallbacksFor(opts: BudgetOptions, mode: BudgetMode): Promise<void> {\n for (const lim of opts.limits) {\n const spent = spentIn(opts.name, lim.window);\n if (lim.limitUsd <= 0) {\n if (spent > 0) await dispatchExceed(opts, lim.window, spent, lim.limitUsd, mode);\n continue;\n }\n const ratio = spent / lim.limitUsd;\n if (ratio >= 1) {\n await dispatchExceed(opts, lim.window, spent, lim.limitUsd, mode);\n } else {\n // Iterate descending so the HIGHEST matched threshold fires (0.95 not 0.8)\n for (const t of [...THRESHOLDS].reverse() as Threshold[]) {\n if (ratio >= t) {\n await dispatchThreshold(opts, lim.window, spent, lim.limitUsd, t);\n break;\n }\n }\n }\n }\n}\n\nasync function dispatchThreshold(\n opts: BudgetOptions,\n window: BudgetOptions[\"limits\"][number][\"window\"],\n spentUsd: number,\n limitUsd: number,\n threshold: Threshold,\n): Promise<void> {\n if (opts.onThreshold === undefined) return;\n try {\n await opts.onThreshold({\n budgetName: opts.name,\n window,\n threshold,\n spentUsd,\n limitUsd,\n });\n } catch (err) {\n // EC-8: callback throw isolated\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[budget] onThreshold callback threw: ${msg}\\n`);\n }\n}\n\nasync function dispatchExceed(\n opts: BudgetOptions,\n window: BudgetOptions[\"limits\"][number][\"window\"],\n spentUsd: number,\n limitUsd: number,\n mode: BudgetMode,\n): Promise<void> {\n if (opts.onExceed === undefined) {\n if (mode === \"warn\") {\n process.stderr.write(\n `[budget] \"${opts.name}\" exceeded ${window} limit: $${spentUsd.toFixed(4)} > $${limitUsd.toFixed(4)}\\n`,\n );\n }\n return;\n }\n try {\n await opts.onExceed({\n budgetName: opts.name,\n window,\n spentUsd,\n limitUsd,\n mode,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[budget] onExceed callback threw: ${msg}\\n`);\n }\n}\n","/**\n * normalizeUsage — convert provider-shaped raw `usage` object to\n * canonical `TokenUsage`. Ports Hermes Agent's `normalize_usage`\n * (reference/peer-agent/agent/usage_pricing.py:672-742).\n *\n * Handles 3 API shapes:\n * - Anthropic Messages: 4 explicit buckets (input/output/cache_read/cache_creation).\n * - OpenAI Chat Completions: prompt_tokens INCLUDES cache; subtract cached_tokens.\n * - OpenAI Responses (Codex): input_tokens INCLUDES cache; same subtraction.\n *\n * Edge cases:\n * - a peer#10266 — OpenAI-compat proxies (OpenRouter, a peer vendor AI Gateway,\n * a peer) routing Claude expose Anthropic-style top-level fields\n * (cache_read_input_tokens / cache_creation_input_tokens). Both\n * locations are checked with top-level fallback.\n * - Null/undefined fields → 0 via `int()` coerce.\n * - String token counts → parsed via int.\n * - Negative values → clamped to 0 (defensive against proxy bugs).\n *\n * @internal\n */\n\nimport type { TokenUsage } from \"@theokit/sdk\";\n\ntype ApiMode = \"anthropic_messages\" | \"openai_chat_completions\" | \"openai_responses\";\n\nfunction int(v: unknown): number {\n if (typeof v === \"number\") return Number.isFinite(v) ? Math.max(0, Math.trunc(v)) : 0;\n if (typeof v === \"string\") {\n const n = Number.parseInt(v, 10);\n return Number.isFinite(n) ? Math.max(0, n) : 0;\n }\n return 0;\n}\n\nfunction buildTotal(buckets: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n}): number {\n // total = visible input + cache buckets + output (reasoning counted via output)\n return (\n buckets.inputTokens + buckets.outputTokens + buckets.cacheReadTokens + buckets.cacheWriteTokens\n );\n}\n\nfunction omitUndefined(usage: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n reasoningTokens: number;\n totalTokens: number;\n}): TokenUsage {\n return {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n ...(usage.cacheReadTokens > 0 ? { cacheReadTokens: usage.cacheReadTokens } : {}),\n ...(usage.cacheWriteTokens > 0 ? { cacheWriteTokens: usage.cacheWriteTokens } : {}),\n ...(usage.reasoningTokens > 0 ? { reasoningTokens: usage.reasoningTokens } : {}),\n totalTokens: usage.totalTokens,\n };\n}\n\nexport function inferApiMode(provider: string): ApiMode {\n const p = provider.toLowerCase();\n if (p === \"anthropic\" || p === \"claude\" || p === \"bedrock_anthropic\") {\n return \"anthropic_messages\";\n }\n if (p === \"openai-codex\" || p === \"codex\") return \"openai_responses\";\n // openai, openrouter, deepseek, google (compat), ollama (compat), lmstudio (compat), etc\n return \"openai_chat_completions\";\n}\n\ninterface RawRecord {\n [k: string]: unknown;\n}\n\nexport function normalizeUsage(\n rawUsage: unknown,\n opts: { provider: string; apiMode?: ApiMode },\n): TokenUsage {\n if (rawUsage === null || rawUsage === undefined) {\n return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\n }\n if (typeof rawUsage !== \"object\") {\n return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\n }\n const raw = rawUsage as RawRecord;\n const mode = opts.apiMode ?? inferApiMode(opts.provider);\n\n if (mode === \"anthropic_messages\") return normalizeAnthropic(raw);\n if (mode === \"openai_responses\") return normalizeOpenAIResponses(raw);\n return normalizeOpenAIChat(raw);\n}\n\nfunction normalizeAnthropic(raw: RawRecord): TokenUsage {\n const inputTokens = int(raw.input_tokens);\n const outputTokens = int(raw.output_tokens);\n const cacheReadTokens = int(raw.cache_read_input_tokens);\n const cacheWriteTokens = int(raw.cache_creation_input_tokens);\n const usage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n reasoningTokens: 0,\n totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }),\n };\n return omitUndefined(usage);\n}\n\nfunction normalizeOpenAIResponses(raw: RawRecord): TokenUsage {\n const inputTotal = int(raw.input_tokens);\n const outputTokens = int(raw.output_tokens);\n const inputDetails = (raw.input_tokens_details as RawRecord | undefined) ?? {};\n const outputDetails = (raw.output_tokens_details as RawRecord | undefined) ?? {};\n const cacheReadTokens = int(inputDetails.cached_tokens);\n const cacheWriteTokens = int(inputDetails.cache_creation_tokens);\n const reasoningTokens = int(outputDetails.reasoning_tokens);\n const inputTokens = Math.max(0, inputTotal - cacheReadTokens - cacheWriteTokens);\n const usage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n reasoningTokens,\n totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }),\n };\n return omitUndefined(usage);\n}\n\nfunction normalizeOpenAIChat(raw: RawRecord): TokenUsage {\n const promptTotal = int(raw.prompt_tokens);\n const outputTokens = int(raw.completion_tokens);\n const promptDetails = (raw.prompt_tokens_details as RawRecord | undefined) ?? {};\n const completionDetails = (raw.completion_tokens_details as RawRecord | undefined) ?? {};\n\n // a peer#10266 fallback — proxies expose Anthropic-style top-level fields when routing Claude\n const cacheReadTokens = int(promptDetails.cached_tokens) || int(raw.cache_read_input_tokens);\n const cacheWriteTokens =\n int(promptDetails.cache_write_tokens) || int(raw.cache_creation_input_tokens);\n\n const reasoningTokens = int(completionDetails.reasoning_tokens);\n const inputTokens = Math.max(0, promptTotal - cacheReadTokens - cacheWriteTokens);\n const usage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n reasoningTokens,\n totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }),\n };\n return omitUndefined(usage);\n}\n","/**\n * Built-in per-model USD pricing table (SDK 2.0 Phase 2 / sdk-budget T2.X).\n *\n * Prices are in USD per 1M tokens (input / output). Sources: each\n * provider's public pricing page; values verified 2026-06 against\n * the same tier used in sdk-core's `internal/budget/pricing-data.json`.\n *\n * The table is intentionally CONSERVATIVE — only the most-used models\n * land here. Consumers needing exhaustive coverage can supply an\n * override via `createUsdBudgetTracker({ pricing })`.\n *\n * @public\n */\n\nexport interface ModelPricing {\n /** USD per 1,000,000 input tokens. */\n readonly inputPerMillionUsd: number;\n /** USD per 1,000,000 output tokens. */\n readonly outputPerMillionUsd: number;\n}\n\n/** Built-in pricing table — exhaustive enough for v0.1; users supply overrides for niche models. */\nexport const BUILTIN_PRICING: Readonly<Record<string, ModelPricing>> = Object.freeze({\n // OpenAI / OpenRouter aliases\n \"openai/gpt-4o\": { inputPerMillionUsd: 2.5, outputPerMillionUsd: 10 },\n \"openai/gpt-4o-mini\": { inputPerMillionUsd: 0.15, outputPerMillionUsd: 0.6 },\n \"openai/gpt-4-turbo\": { inputPerMillionUsd: 10, outputPerMillionUsd: 30 },\n \"openai/gpt-3.5-turbo\": { inputPerMillionUsd: 0.5, outputPerMillionUsd: 1.5 },\n // Anthropic / Claude\n \"anthropic/claude-3-5-sonnet-latest\": { inputPerMillionUsd: 3, outputPerMillionUsd: 15 },\n \"anthropic/claude-3-5-haiku-latest\": { inputPerMillionUsd: 0.8, outputPerMillionUsd: 4 },\n \"anthropic/claude-3-opus-latest\": { inputPerMillionUsd: 15, outputPerMillionUsd: 75 },\n // Google\n \"google/gemini-1.5-pro\": { inputPerMillionUsd: 1.25, outputPerMillionUsd: 5 },\n \"google/gemini-1.5-flash\": { inputPerMillionUsd: 0.075, outputPerMillionUsd: 0.3 },\n});\n\n/**\n * Compute USD cost for a single token event.\n *\n * Returns `undefined` (NOT `0`, NOT throws) when the model is UNKNOWN — its\n * cost is genuinely unknown, and coercing it to `$0` would be dishonest (cost\n * contract `D377-cost-status-closed-enum.md`: amount-unknown ≠ `$0`). Consumers\n * who want a price for a niche model supply an override via\n * `createUsdBudgetTracker({ pricing })`. A KNOWN model with zero/invalid tokens\n * returns `0` — that is a real, known `$0`.\n */\nexport function computeUsdCost(\n pricing: Readonly<Record<string, ModelPricing>>,\n model: string,\n type: \"input\" | \"output\",\n tokens: number,\n): number | undefined {\n const entry = pricing[model];\n if (entry === undefined) return undefined;\n const ratePerMillion = type === \"input\" ? entry.inputPerMillionUsd : entry.outputPerMillionUsd;\n if (!Number.isFinite(tokens) || tokens <= 0) return 0;\n return (tokens / 1_000_000) * ratePerMillion;\n}\n","/**\n * `createUsdBudgetTracker` — USD-cost-aware `BudgetTracker` impl shipped\n * in `@theokit/sdk-budget` (SDK 2.0 Phase 2 / T2.X).\n *\n * Extends the counter pattern from `createCounterBudgetTracker`\n * (sdk-core reference impl) with per-model USD cost computation via\n * the `BUILTIN_PRICING` table.\n *\n * Use cases:\n * - Cap total spend per agent run (`maxUsd`).\n * - Cap total tokens (`maxTokens`) AND USD ceiling simultaneously.\n * - Observe cumulative USD from outside (`getTotalUsd()`).\n *\n * Tracker.check() returns `allowed: false` when ANY of the configured\n * caps is exceeded. `reason` is one of:\n * - `\"token_limit\"` — `maxTokens` reached\n * - `\"cost_limit\"` — `maxUsd` reached\n *\n * Layered design (mirrors counter impl):\n * - `track()` is sync + non-throwing (clamps invalid values).\n * - `check()` is sync (allowed: true unless a cap is exceeded).\n * - `getTotal()` returns the SDK-required token + iteration totals\n * ONLY (USD is exposed via the bonus `getTotalUsd()` method).\n *\n * @public\n */\n\nimport type { BudgetCheck, BudgetTotal, BudgetTracker, BudgetUsageEvent } from \"@theokit/sdk\";\nimport { BUILTIN_PRICING, computeUsdCost, type ModelPricing } from \"./usd-pricing.js\";\n\n/** Options for `createUsdBudgetTracker`. */\nexport interface UsdBudgetTrackerOptions {\n /** Hard ceiling on total tokens (input + output combined). */\n readonly maxTokens?: number;\n /** Hard ceiling on cumulative USD spend. */\n readonly maxUsd?: number;\n /** Optional override map — model id → pricing entry. Merged onto BUILTIN_PRICING. */\n readonly pricing?: Readonly<Record<string, ModelPricing>>;\n}\n\n/** The cost-cap denial for the current state, or `null` if the cost cap is satisfied. */\nfunction evaluateCostCap(\n maxUsd: number | undefined,\n costKnown: boolean,\n totalUsd: number,\n): BudgetCheck | null {\n if (maxUsd === undefined) return null;\n if (!costKnown) {\n // Fail closed: a spend cap is set but cost is unknown — we cannot prove the\n // run is under budget, so deny rather than allow unbounded spend.\n return {\n allowed: false,\n reason: \"cost_limit\",\n detail: `cost unknown — cannot verify maxUsd ${maxUsd}`,\n };\n }\n if (totalUsd >= maxUsd) {\n return {\n allowed: false,\n reason: \"cost_limit\",\n detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`,\n };\n }\n return null;\n}\n\n/** The token-cap denial for the current state, or `null` if the token cap is satisfied. */\nfunction evaluateTokenCap(maxTokens: number | undefined, totalTokens: number): BudgetCheck | null {\n if (maxTokens !== undefined && totalTokens >= maxTokens) {\n return {\n allowed: false,\n reason: \"token_limit\",\n detail: `${totalTokens} >= maxTokens ${maxTokens}`,\n };\n }\n return null;\n}\n\n/**\n * Build a fresh USD-aware tracker. The returned object exposes the\n * `BudgetTracker` contract PLUS `getTotalUsd()` + `nextIteration()`\n * helpers for explicit iteration counting.\n */\nexport function createUsdBudgetTracker(\n options: UsdBudgetTrackerOptions = {},\n): BudgetTracker & { nextIteration(): void; getTotalUsd(): number | undefined } {\n let totalTokens = 0;\n let iterations = 0;\n let totalUsd = 0;\n // Honest-null (D377): once any round's cost is UNKNOWN, the aggregate becomes\n // unknown and STAYS unknown — a later known round does not resurrect it.\n let costKnown = true;\n const maxTokens = options.maxTokens;\n const maxUsd = options.maxUsd;\n const pricing: Readonly<Record<string, ModelPricing>> =\n options.pricing !== undefined ? { ...BUILTIN_PRICING, ...options.pricing } : BUILTIN_PRICING;\n\n return {\n track(event: BudgetUsageEvent): void {\n // Sync + non-throwing per contract. Invalid events silently clamped.\n const t = Number.isFinite(event.tokens) && event.tokens > 0 ? event.tokens : 0;\n if (t === 0) return;\n totalTokens += t;\n const cost = computeUsdCost(pricing, event.model, event.type, t);\n if (cost === undefined) {\n costKnown = false; // poison — do NOT add 0 (that would be a dishonest $0)\n return;\n }\n if (costKnown) totalUsd += cost;\n },\n\n check(): BudgetCheck {\n // Cost cap is evaluated first (USD is the higher-signal denial for a human).\n return (\n evaluateCostCap(maxUsd, costKnown, totalUsd) ??\n evaluateTokenCap(maxTokens, totalTokens) ?? { allowed: true }\n );\n },\n\n getTotal(): BudgetTotal {\n return { tokens: totalTokens, iterations };\n },\n\n getTotalUsd(): number | undefined {\n return costKnown ? totalUsd : undefined;\n },\n\n nextIteration(): void {\n iterations += 1;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/format-cost.ts","../src/internal/calendar-window.ts","../src/internal/ledger.ts","../src/internal/registry.ts","../src/internal/enforcement.ts","../src/internal/normalize-usage.ts","../src/usd-pricing.ts","../src/usd-budget-tracker.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * M7-6 — honest-null cost render helper. Turns the `number | undefined` cost\n * (from `computeUsdCost` / `getTotalUsd`, M1-6) into a display string: an\n * unknown cost renders as `\"—\"` (NEVER a dishonest `\"$0\"`), a real number\n * renders as `\"$X.XX\"`. Composes the cost-honesty contract — a known-zero cost\n * (`0`) is distinct from an unknown one (`undefined`).\n *\n * @public\n */\n\n/** Options for {@link formatCostUsd}. */\nexport interface FormatCostUsdOptions {\n /** Marker for an unknown cost (`undefined`). Default `\"—\"`. */\n readonly unknown?: string;\n /** Currency prefix for a known cost. Default `\"$\"`. */\n readonly currency?: string;\n}\n\n/**\n * Render a USD cost for display. `undefined` -> the unknown marker (`\"—\"` by\n * default); a number -> `\"$X.XX\"` (2 decimal places). `0` is a real known-zero\n * and renders `\"$0.00\"`, NOT the unknown marker.\n */\nexport function formatCostUsd(cost: number | undefined, opts: FormatCostUsdOptions = {}): string {\n if (cost === undefined) return opts.unknown ?? \"—\";\n return `${opts.currency ?? \"$\"}${cost.toFixed(2)}`;\n}\n","/**\n * UTC-aligned calendar window helpers (ADR D382).\n *\n * - `1h` — relative (now - 1 hour).\n * - `1d` — UTC midnight (current UTC day).\n * - `1w` — UTC monday 00:00:00 (current UTC week, Monday is week start).\n * - `30d` — relative 30 days.\n * - `365d` — relative 365 days.\n *\n * `1d` and `1w` are calendar-aligned because users expect \"1 USD per day\"\n * = \"since midnight UTC\", not a rolling 24h.\n * `30d`/`365d` are relative because nobody expects \"since the 1st\".\n *\n * @internal\n */\n\nimport type { BudgetWindow } from \"@theokit/sdk\";\n\n/**\n * Midnight UTC of `now`'s day — the start of a `1d` budget window.\n *\n * Always UTC, never the host's local timezone: \"1 USD per day\" resets at 00:00Z, so a budget in\n * UTC-05:00 resets at 19:00 local. That is deliberate — a budget shared by processes in different\n * regions must reset at one instant — but it surprises anyone reading a daily total at local\n * midnight.\n */\nexport function startOfDayUtc(now: Date = new Date()): Date {\n return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n}\n\n/**\n * Midnight UTC on the MONDAY of `now`'s week — the start of a `1w` budget window.\n *\n * ISO 8601, so the week starts on Monday, not Sunday. This is calendar-aligned rather than rolling:\n * \"5 USD per week\" resets on Monday, which is what a person reading a weekly budget expects, and not\n * a trailing 168 hours. `30d` and `365d` are relative for the mirror-image reason — nobody expects a\n * monthly budget to reset on the 1st.\n */\nexport function startOfWeekUtc(now: Date = new Date()): Date {\n // ISO 8601 week starts on Monday. getUTCDay() returns 0 (Sun) .. 6 (Sat).\n const dayOfWeek = now.getUTCDay();\n const daysSinceMonday = (dayOfWeek + 6) % 7; // Mon=0, Sun=6\n const start = startOfDayUtc(now);\n start.setUTCDate(start.getUTCDate() - daysSinceMonday);\n return start;\n}\n\nconst MS_PER_HOUR = 60 * 60 * 1000;\nconst MS_PER_DAY = 24 * MS_PER_HOUR;\n\n/**\n * Inclusive start timestamp (ms) of `window`, as `spentIn` uses it to decide which charges count.\n *\n * Two different behaviours behind one enum, and the difference is visible in every total:\n *\n * - `1d` / `1w` are CALENDAR-aligned to UTC (midnight; ISO Monday). Spend resets at a boundary.\n * - `1h` / `30d` / `365d` are ROLLING — `now` minus the duration. Nothing ever \"resets\"; the\n * oldest charges simply fall out of the window.\n *\n * Throws on a value outside `BudgetWindow`, which TypeScript already prevents.\n */\nexport function windowStartMs(window: BudgetWindow, now: Date = new Date()): number {\n switch (window) {\n case \"1h\":\n return now.getTime() - MS_PER_HOUR;\n case \"1d\":\n return startOfDayUtc(now).getTime();\n case \"1w\":\n return startOfWeekUtc(now).getTime();\n case \"30d\":\n return now.getTime() - 30 * MS_PER_DAY;\n case \"365d\":\n return now.getTime() - 365 * MS_PER_DAY;\n default: {\n const _exhaustive: never = window;\n throw new Error(`unreachable window: ${_exhaustive as string}`);\n }\n }\n}\n","/**\n * In-process Budget ledger (ADR D385).\n *\n * Singleton mutex-protected. Stores per-budget ChargeLog[] arrays;\n * `spentIn(window)` filters by timestamp.\n *\n * EC-6: GC eviction runs INSIDE the same mutex as charge — no race.\n * EC-9: charge() is called inside the same critical section as preflight\n * check by Budget enforcement.\n *\n * Persistence cross-restart: deferred to v0.2 (JsonFile pattern).\n *\n * @internal\n */\n\nimport { type BudgetWindow, withCwdMutex } from \"@theokit/sdk\";\nimport { windowStartMs } from \"./calendar-window.js\";\n\ninterface ChargeLog {\n timestamp: number;\n amountUsd: number;\n}\n\nconst MS_PER_YEAR = 365 * 24 * 60 * 60 * 1000;\nconst GC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\nconst GC_LOGS_THRESHOLD = 10_000;\n\ninterface LedgerState {\n readonly logs: Map<string, ChargeLog[]>;\n lastGcAt: number;\n}\n\nconst state: LedgerState = {\n logs: new Map(),\n lastGcAt: Date.now(),\n};\n\nconst MUTEX_KEY = \"budget-ledger\";\n\nfunction shouldGc(now: number): boolean {\n if (now - state.lastGcAt < GC_INTERVAL_MS) return false;\n let totalLogs = 0;\n for (const arr of state.logs.values()) totalLogs += arr.length;\n return totalLogs > GC_LOGS_THRESHOLD;\n}\n\nfunction gcOlderThanOneYear(now: number): void {\n const cutoff = now - MS_PER_YEAR;\n for (const [name, arr] of state.logs.entries()) {\n const kept = arr.filter((l) => l.timestamp >= cutoff);\n if (kept.length === 0) state.logs.delete(name);\n else state.logs.set(name, kept);\n }\n state.lastGcAt = now;\n}\n\n/**\n * Append `amountUsd` to the spend ledger for the budget called `name`, timestamped now.\n *\n * NOT idempotent. `withCwdMutex` SERIALIZES concurrent calls so two appends cannot interleave; it\n * does not deduplicate them. Calling this twice with the same arguments records the spend twice,\n * and there is no charge id to reconcile against — an at-least-once caller needs its own guard.\n *\n * NOT VALIDATED against the registry either: charging a name that was never passed to\n * `createBudget` (a typo, say) succeeds silently and accumulates spend that no limit enforces and\n * that `snapshotAll` — which iterates the REGISTRY — will never show.\n *\n * `chargeAndCheckThresholds` does not rescue you from that, and swapping to it trades one silent\n * failure for another: on an unknown or already-deleted name it writes one line to stderr and\n * RETURNS WITHOUT CHARGING, so the spend is lost rather than merely invisible. Neither function\n * throws. Validate the name against `getBudgetOptionsRaw` / `getBudget` if it matters which of the\n * two failures you get.\n *\n * `amountUsd <= 0` is a no-op, so a refund cannot be expressed here.\n *\n * The ledger is a MODULE-LEVEL SINGLETON: process-wide, shared by every budget and every agent in\n * the process, and lost on exit — there is no persistence across restarts. Entries older than a\n * year are garbage-collected opportunistically.\n */\nexport async function charge(name: string, amountUsd: number): Promise<void> {\n if (amountUsd <= 0) return;\n await withCwdMutex(MUTEX_KEY, async () => {\n const now = Date.now();\n const list = state.logs.get(name) ?? [];\n list.push({ timestamp: now, amountUsd });\n state.logs.set(name, list);\n if (shouldGc(now)) gcOlderThanOneYear(now);\n });\n}\n\n/**\n * Total USD recorded for `name` inside `window`, summed at call time.\n *\n * Returns `0` for a name that was never charged — indistinguishable from a budget that exists and\n * has spent nothing. Use `getBudget(name)` when you need to tell those apart.\n *\n * Lock-free: it reads the live array without taking the mutex, so a charge landing mid-read is\n * simply included or not. Window boundaries are those of {@link windowStartMs} — `1d` and `1w` are\n * UTC calendar-aligned, the rest are rolling.\n */\nexport function spentIn(name: string, window: BudgetWindow, now: Date = new Date()): number {\n const arr = state.logs.get(name);\n if (arr === undefined) return 0;\n const sinceMs = windowStartMs(window, now);\n let total = 0;\n for (const log of arr) {\n if (log.timestamp >= sinceMs) total += log.amountUsd;\n }\n return total;\n}\n","/**\n * Internal Budget registry — keeps live `BudgetOptions` per name.\n * Singleton; no persistence (D385).\n *\n * @internal\n */\n\nimport {\n type BudgetHandle,\n type BudgetMode,\n type BudgetOptions,\n type BudgetSnapshot,\n type BudgetWindow,\n ConfigurationError,\n} from \"@theokit/sdk\";\nimport { spentIn } from \"./ledger.js\";\n\nconst NAME_GRAMMAR = /^[a-z0-9][a-z0-9_-]*$/;\n\nconst registry = new Map<string, BudgetOptions>();\n\nfunction validateBudgetName(name: string): void {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new ConfigurationError(\"Budget name must be a non-empty string\", {\n code: \"invalid_budget_name\",\n });\n }\n if (!NAME_GRAMMAR.test(name)) {\n throw new ConfigurationError(\n `Budget name \"${name}\" must match ^[a-z0-9][a-z0-9_-]*$ (lowercase + dash/underscore, start alphanumeric)`,\n { code: \"invalid_budget_name\" },\n );\n }\n}\n\n/**\n * Register a budget under `opts.name` and return its live handle.\n *\n * ```ts\n * const b = createBudget({ name: \"daily\", mode: \"block\", limits: [{ window: \"1d\", limitUsd: 5 }] });\n * ```\n *\n * THROWS `ConfigurationError(code: \"invalid_budget_name\")` in two cases: a name that does not match\n * `^[a-z0-9][a-z0-9_-]*$` (lowercase only — `\"Daily\"` and `\"my.budget\"` are rejected), and a name\n * already registered. Duplicate registration is deliberately an error rather than an idempotent\n * return, so a second `createBudget(\"daily\", …)` with different limits cannot silently win.\n *\n * Registering does NOT reset spend. The ledger is keyed by NAME and outlives the registry entry, so\n * re-creating a budget after `deleteBudget` inherits everything charged under that name — a\n * config reload keeps enforcing the day's spend, which is usually right and is a surprise if you\n * expected a fresh start.\n *\n * Process-local and non-persistent: nothing survives a restart, so re-create budgets at startup.\n * `mode` defaults to `\"warn\"` (see {@link defaultMode}) — a budget created without one observes and\n * does not block.\n */\nexport function createBudget(opts: BudgetOptions): BudgetHandle {\n // EC-7: name validation\n validateBudgetName(opts.name);\n if (registry.has(opts.name)) {\n // EC-16: duplicate throws (vs Task.submit idempotent return)\n throw new ConfigurationError(`Budget \"${opts.name}\" already exists`, {\n code: \"invalid_budget_name\",\n });\n }\n registry.set(opts.name, opts);\n return buildHandle(opts);\n}\n\n/**\n * The live handle for a registered budget, or `undefined` when the name is unknown.\n *\n * `undefined` is the honest answer for \"never created\" — it does NOT mean \"zero spend\". A budget\n * that exists but has not been charged returns a handle whose `spentIn(...)` is 0, and the two are\n * different facts: the first means nothing is enforcing a limit.\n *\n * The registry is per-PROCESS and holds no persistence, so a fresh process starts with no budgets\n * and no ledger. Re-create them at startup.\n */\nexport function getBudget(name: string): BudgetHandle | undefined {\n const opts = registry.get(name);\n if (opts === undefined) return undefined;\n return buildHandle(opts);\n}\n\n/**\n * Every budget registered in this process, in insertion order.\n *\n * Empty after a restart — see {@link getBudget} on process-local state. Use it to enumerate what is\n * being enforced; use {@link snapshotAll} when you want the numbers rather than the handles.\n */\nexport function listBudgets(): readonly BudgetHandle[] {\n return [...registry.values()].map(buildHandle);\n}\n\n/**\n * Remove a budget from the registry. Returns `false` when the name was not registered.\n *\n * This stops ENFORCEMENT; it does not refund or clear the ledger. Re-creating a budget under the\n * same name inherits the spend already recorded for that name, which is usually what you want after\n * a config reload and a surprise if you were expecting a reset.\n */\nexport function deleteBudget(name: string): boolean {\n return registry.delete(name);\n}\n\n/**\n * One row per budget PER WINDOW — a budget with three limits produces three rows, not one.\n *\n * `ratio` is `spentUsd / limitUsd`, and is 0 when the limit is 0 rather than `Infinity`, so a\n * zero-limit budget does not poison a dashboard that sums or charts these. Spend is computed at call\n * time from the ledger, counting only entries inside each window, so the same budget reports\n * different numbers for `1d` and `30d`.\n */\nexport function snapshotAll(): readonly BudgetSnapshot[] {\n const result: BudgetSnapshot[] = [];\n for (const opts of registry.values()) {\n for (const lim of opts.limits) {\n const spent = spentIn(opts.name, lim.window);\n result.push({\n name: opts.name,\n window: lim.window,\n spentUsd: spent,\n limitUsd: lim.limitUsd,\n ratio: lim.limitUsd > 0 ? spent / lim.limitUsd : 0,\n });\n }\n }\n return result;\n}\n\n/**\n * The stored {@link BudgetOptions} exactly as registered, or `undefined` when the name is unknown.\n *\n * Distinct from {@link getBudget}, which returns a HANDLE with live accessors. Use this when you\n * need the configuration itself — the limits array, the callbacks, the declared mode — for example\n * to render it or to re-register the same shape elsewhere.\n *\n * The object is the registry's own, held by reference: mutating it changes what the budget enforces\n * without going through `Budget.create`, which is a bug waiting to happen. Copy before editing.\n */\nexport function getBudgetOptionsRaw(name: string): BudgetOptions | undefined {\n return registry.get(name);\n}\n\n/**\n * The mode a budget enforces, resolving the omitted case to `\"warn\"`.\n *\n * `\"warn\"` fires the callbacks and lets the call through; `\"block\"` refuses it. The default is\n * deliberate: a budget added for observability must not start rejecting traffic because someone\n * forgot a field.\n */\nexport function defaultMode(opts: BudgetOptions): BudgetMode {\n return opts.mode ?? \"warn\";\n}\n\nfunction buildHandle(opts: BudgetOptions): BudgetHandle {\n return {\n name: opts.name,\n mode: defaultMode(opts),\n scope: opts.scope,\n limits: opts.limits,\n spentIn: (window: BudgetWindow) => spentIn(opts.name, window),\n remainingIn: (window: BudgetWindow) => {\n const lim = opts.limits.find((l) => l.window === window);\n if (lim === undefined) return Number.POSITIVE_INFINITY;\n return Math.max(0, lim.limitUsd - spentIn(opts.name, window));\n },\n };\n}\n","/**\n * Budget enforcement (ADRs D383, D386, EC-7/8/9).\n *\n * - `preflightCheck(name, estimatedUsd)` — em `block` mode, throw\n * `BudgetExceededError` before the LLM call if any limit would be\n * exceeded (EC-9 — the caller invokes it inside the mutex section).\n * - `chargeAndCheckThresholds(name, actualUsd)` — apply charge ao\n * ledger + invokes onThreshold/onExceed callbacks isolated in\n * try/catch (EC-8).\n *\n * @internal\n */\n\nimport { BudgetExceededError, type BudgetMode, type BudgetOptions } from \"@theokit/sdk\";\nimport { charge, spentIn } from \"./ledger.js\";\nimport { defaultMode, getBudgetOptionsRaw } from \"./registry.js\";\n\nconst THRESHOLDS = [0.8, 0.95] as const;\ntype Threshold = (typeof THRESHOLDS)[number];\n\n/**\n * Refuse an upcoming call that would push a `\"block\"`-mode budget past one of its limits. Call it\n * BEFORE the LLM request, with your cost estimate for that request.\n *\n * Throws `BudgetExceededError` (carrying `budgetName`, `window`, the projected `spentUsd`, the\n * `limitUsd` and `mode: \"block\"`) for the FIRST limit where `alreadySpent + estimatedUsd` exceeds\n * the limit. The comparison is strict, so landing exactly on the limit is allowed through — while\n * the post-charge `onExceed` callback fires at `>=`. The two boundaries do not agree.\n *\n * SILENTLY DOES NOTHING in three cases, and none of them is distinguishable from \"you are within\n * budget\" at the call site:\n *\n * - `name` is not registered — a typo, or a budget deleted since. Nothing is enforced.\n * - the budget's mode is `\"warn\"` or `\"audit\"` — those never block by design.\n * - the estimate is low. Enforcement is only as good as `estimatedUsd`; passing 0 disables it.\n */\nexport function preflightCheck(name: string, estimatedUsd: number): void {\n const opts = getBudgetOptionsRaw(name);\n if (opts === undefined) return; // EC-20: budget deleted; charge becomes no-op\n if (defaultMode(opts) !== \"block\") return;\n for (const lim of opts.limits) {\n const currentSpent = spentIn(opts.name, lim.window);\n if (currentSpent + estimatedUsd > lim.limitUsd) {\n throw new BudgetExceededError({\n budgetName: opts.name,\n window: lim.window,\n spentUsd: currentSpent + estimatedUsd,\n limitUsd: lim.limitUsd,\n mode: \"block\",\n });\n }\n }\n}\n\n/**\n * Record the ACTUAL cost of a completed call against a budget and fire its callbacks. Call it after\n * the LLM request, paired with `preflightCheck` before it.\n *\n * NEVER THROWS on budget state — including when the charge tips a limit. Enforcement happens on the\n * next `preflightCheck`; this one only records and notifies. A callback that throws is caught and\n * logged to stderr, so your `onThreshold` / `onExceed` cannot break the run either.\n *\n * Per mode:\n *\n * - `\"audit\"` — charge only, no callbacks.\n * - `\"warn\"` — charge, then `onThreshold` at 80% or 95%, then `onExceed` at 100%. With no\n * `onExceed` handler it writes a line to stderr instead.\n *\n * Per limit, exactly ONE callback fires per call: the highest matched threshold, or `onExceed`,\n * never both. But there is NO de-duplication across calls — a budget sitting at 90% fires\n * `onThreshold(0.8)` again on every single charge, and one past its limit fires `onExceed` on\n * every charge forever. Debounce inside your handler if it pages anyone. A budget with three\n * limits evaluates all three, so one call can fire three callbacks.\n * - `\"block\"` — same callbacks as `\"warn\"`, still no throw. Concurrent sends each pass their own\n * preflight, so the last to charge can legitimately tip a limit that preflight had cleared.\n *\n * A DELETED OR UNKNOWN BUDGET LOSES THE CHARGE. The function writes a warning to stderr and\n * returns — the spend is not recorded anywhere, so a budget removed mid-flight under-reports rather\n * than over-reports. This is the one path where the ledger and reality diverge on purpose.\n */\nexport async function chargeAndCheckThresholds(name: string, actualUsd: number): Promise<void> {\n const opts = getBudgetOptionsRaw(name);\n if (opts === undefined) {\n // EC-20: budget deleted during in-flight call. Charge is silent no-op.\n process.stderr.write(\n `[budget] charge for deleted budget \"${name}\" is a no-op (was the budget removed during in-flight send?)\\n`,\n );\n return;\n }\n const mode = defaultMode(opts);\n\n await charge(opts.name, actualUsd);\n\n if (mode === \"audit\") return;\n await dispatchCallbacksFor(opts, mode);\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: 3-mode × 3-threshold dispatch table inherently branchy; pulling out helpers loses local context.\nasync function dispatchCallbacksFor(opts: BudgetOptions, mode: BudgetMode): Promise<void> {\n for (const lim of opts.limits) {\n const spent = spentIn(opts.name, lim.window);\n if (lim.limitUsd <= 0) {\n if (spent > 0) await dispatchExceed(opts, lim.window, spent, lim.limitUsd, mode);\n continue;\n }\n const ratio = spent / lim.limitUsd;\n if (ratio >= 1) {\n await dispatchExceed(opts, lim.window, spent, lim.limitUsd, mode);\n } else {\n // Iterate descending so the HIGHEST matched threshold fires (0.95 not 0.8)\n for (const t of [...THRESHOLDS].reverse() as Threshold[]) {\n if (ratio >= t) {\n await dispatchThreshold(opts, lim.window, spent, lim.limitUsd, t);\n break;\n }\n }\n }\n }\n}\n\nasync function dispatchThreshold(\n opts: BudgetOptions,\n window: BudgetOptions[\"limits\"][number][\"window\"],\n spentUsd: number,\n limitUsd: number,\n threshold: Threshold,\n): Promise<void> {\n if (opts.onThreshold === undefined) return;\n try {\n await opts.onThreshold({\n budgetName: opts.name,\n window,\n threshold,\n spentUsd,\n limitUsd,\n });\n } catch (err) {\n // EC-8: callback throw isolated\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[budget] onThreshold callback threw: ${msg}\\n`);\n }\n}\n\nasync function dispatchExceed(\n opts: BudgetOptions,\n window: BudgetOptions[\"limits\"][number][\"window\"],\n spentUsd: number,\n limitUsd: number,\n mode: BudgetMode,\n): Promise<void> {\n if (opts.onExceed === undefined) {\n if (mode === \"warn\") {\n process.stderr.write(\n `[budget] \"${opts.name}\" exceeded ${window} limit: $${spentUsd.toFixed(4)} > $${limitUsd.toFixed(4)}\\n`,\n );\n }\n return;\n }\n try {\n await opts.onExceed({\n budgetName: opts.name,\n window,\n spentUsd,\n limitUsd,\n mode,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[budget] onExceed callback threw: ${msg}\\n`);\n }\n}\n","/**\n * normalizeUsage — convert provider-shaped raw `usage` object to\n * canonical `TokenUsage`. Ports Hermes Agent's `normalize_usage`\n * (reference/peer-agent/agent/usage_pricing.py:672-742).\n *\n * Handles 3 API shapes:\n * - Anthropic Messages: 4 explicit buckets (input/output/cache_read/cache_creation).\n * - OpenAI Chat Completions: prompt_tokens INCLUDES cache; subtract cached_tokens.\n * - OpenAI Responses (Codex): input_tokens INCLUDES cache; same subtraction.\n *\n * Edge cases:\n * - a peer#10266 — OpenAI-compat proxies (OpenRouter, a peer vendor AI Gateway,\n * a peer) routing Claude expose Anthropic-style top-level fields\n * (cache_read_input_tokens / cache_creation_input_tokens). Both\n * locations are checked with top-level fallback.\n * - Null/undefined fields → 0 via `int()` coerce.\n * - String token counts → parsed via int.\n * - Negative values → clamped to 0 (defensive against proxy bugs).\n *\n * @internal\n */\n\nimport type { TokenUsage } from \"@theokit/sdk\";\n\ntype ApiMode = \"anthropic_messages\" | \"openai_chat_completions\" | \"openai_responses\";\n\nfunction int(v: unknown): number {\n if (typeof v === \"number\") return Number.isFinite(v) ? Math.max(0, Math.trunc(v)) : 0;\n if (typeof v === \"string\") {\n const n = Number.parseInt(v, 10);\n return Number.isFinite(n) ? Math.max(0, n) : 0;\n }\n return 0;\n}\n\nfunction buildTotal(buckets: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n}): number {\n // total = visible input + cache buckets + output (reasoning counted via output)\n return (\n buckets.inputTokens + buckets.outputTokens + buckets.cacheReadTokens + buckets.cacheWriteTokens\n );\n}\n\nfunction omitUndefined(usage: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n reasoningTokens: number;\n totalTokens: number;\n}): TokenUsage {\n return {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n ...(usage.cacheReadTokens > 0 ? { cacheReadTokens: usage.cacheReadTokens } : {}),\n ...(usage.cacheWriteTokens > 0 ? { cacheWriteTokens: usage.cacheWriteTokens } : {}),\n ...(usage.reasoningTokens > 0 ? { reasoningTokens: usage.reasoningTokens } : {}),\n totalTokens: usage.totalTokens,\n };\n}\n\n/**\n * Which usage dialect a provider reports in, inferred from its name.\n *\n * Providers do not agree on the shape of a usage object: Anthropic reports `input_tokens` /\n * `output_tokens`, OpenAI's Responses API reports yet another, and the large\n * OpenAI-chat-compatible family (openai, openrouter, deepseek, google, ollama, lmstudio, …) shares\n * one. This maps a provider name onto that choice.\n *\n * UNKNOWN NAMES FALL BACK to the OpenAI chat-completions shape, because that is what almost every\n * compatible endpoint speaks — a new proxy usually works without a change here. Pass\n * {@link normalizeUsage}'s `apiMode` explicitly when a provider is compatible in its wire format but\n * not in its name.\n */\nexport function inferApiMode(provider: string): ApiMode {\n const p = provider.toLowerCase();\n if (p === \"anthropic\" || p === \"claude\" || p === \"bedrock_anthropic\") {\n return \"anthropic_messages\";\n }\n if (p === \"openai-codex\" || p === \"codex\") return \"openai_responses\";\n // openai, openrouter, deepseek, google (compat), ollama (compat), lmstudio (compat), etc\n return \"openai_chat_completions\";\n}\n\ninterface RawRecord {\n [k: string]: unknown;\n}\n\n/**\n * Turn a provider's raw usage object into the SDK's canonical {@link TokenUsage}.\n *\n * NEVER THROWS, and that is the point: usage arrives from a third party at the end of a successful\n * call, so a missing or malformed field must not fail a request the model already answered. `null`,\n * `undefined` and non-objects all yield an all-zero usage, and unrecognised fields are dropped.\n *\n * The cost of that tolerance is that a zero is ambiguous — it means \"the provider reported nothing\n * usable\" as readily as \"no tokens\". A budget that suddenly stops accruing is the symptom of a\n * dialect mismatch, not of free traffic.\n *\n * `apiMode` overrides the guess {@link inferApiMode} makes from the provider name; supply it for a\n * compatible endpoint whose name is not recognised.\n */\nexport function normalizeUsage(\n rawUsage: unknown,\n opts: { provider: string; apiMode?: ApiMode },\n): TokenUsage {\n if (rawUsage === null || rawUsage === undefined) {\n return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\n }\n if (typeof rawUsage !== \"object\") {\n return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\n }\n const raw = rawUsage as RawRecord;\n const mode = opts.apiMode ?? inferApiMode(opts.provider);\n\n if (mode === \"anthropic_messages\") return normalizeAnthropic(raw);\n if (mode === \"openai_responses\") return normalizeOpenAIResponses(raw);\n return normalizeOpenAIChat(raw);\n}\n\nfunction normalizeAnthropic(raw: RawRecord): TokenUsage {\n const inputTokens = int(raw.input_tokens);\n const outputTokens = int(raw.output_tokens);\n const cacheReadTokens = int(raw.cache_read_input_tokens);\n const cacheWriteTokens = int(raw.cache_creation_input_tokens);\n const usage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n reasoningTokens: 0,\n totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }),\n };\n return omitUndefined(usage);\n}\n\nfunction normalizeOpenAIResponses(raw: RawRecord): TokenUsage {\n const inputTotal = int(raw.input_tokens);\n const outputTokens = int(raw.output_tokens);\n const inputDetails = (raw.input_tokens_details as RawRecord | undefined) ?? {};\n const outputDetails = (raw.output_tokens_details as RawRecord | undefined) ?? {};\n const cacheReadTokens = int(inputDetails.cached_tokens);\n const cacheWriteTokens = int(inputDetails.cache_creation_tokens);\n const reasoningTokens = int(outputDetails.reasoning_tokens);\n const inputTokens = Math.max(0, inputTotal - cacheReadTokens - cacheWriteTokens);\n const usage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n reasoningTokens,\n totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }),\n };\n return omitUndefined(usage);\n}\n\nfunction normalizeOpenAIChat(raw: RawRecord): TokenUsage {\n const promptTotal = int(raw.prompt_tokens);\n const outputTokens = int(raw.completion_tokens);\n const promptDetails = (raw.prompt_tokens_details as RawRecord | undefined) ?? {};\n const completionDetails = (raw.completion_tokens_details as RawRecord | undefined) ?? {};\n\n // a peer#10266 fallback — proxies expose Anthropic-style top-level fields when routing Claude\n const cacheReadTokens = int(promptDetails.cached_tokens) || int(raw.cache_read_input_tokens);\n const cacheWriteTokens =\n int(promptDetails.cache_write_tokens) || int(raw.cache_creation_input_tokens);\n\n const reasoningTokens = int(completionDetails.reasoning_tokens);\n const inputTokens = Math.max(0, promptTotal - cacheReadTokens - cacheWriteTokens);\n const usage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n reasoningTokens,\n totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }),\n };\n return omitUndefined(usage);\n}\n","/**\n * Built-in per-model USD pricing table (SDK 2.0 Phase 2 / sdk-budget T2.X).\n *\n * Prices are in USD per 1M tokens (input / output). Sources: each\n * provider's public pricing page; values verified 2026-06 against\n * the same tier used in sdk-core's `internal/budget/pricing-data.json`.\n *\n * The table is intentionally CONSERVATIVE — only the most-used models\n * land here. Consumers needing exhaustive coverage can supply an\n * override via `createUsdBudgetTracker({ pricing })`.\n *\n * @public\n */\n\n/**\n * The two rates needed to price one model, in USD per 1,000,000 tokens.\n *\n * List prices only — this package models no cached-input discount, no batch tier and no negotiated\n * rate, so a cost computed here is an upper bound for anyone on a discount and simply wrong for a\n * provider that bills per request.\n *\n * @public\n */\nexport interface ModelPricing {\n /** USD per 1,000,000 input tokens. */\n readonly inputPerMillionUsd: number;\n /** USD per 1,000,000 output tokens. */\n readonly outputPerMillionUsd: number;\n}\n\n/**\n * The nine models this package prices out of the box, keyed by the exact `model` string a\n * `BudgetUsageEvent` carries: four OpenAI, three Anthropic, two Google.\n *\n * Lookup is exact-match. No prefix stripping, no aliasing, no fuzzy fallback — `\"gpt-4o\"` and\n * `\"openrouter/openai/gpt-4o\"` both MISS the `\"openai/gpt-4o\"` entry, and a miss means unknown\n * cost, which under a `maxUsd` cap denies the run. Pass overrides through\n * `createUsdBudgetTracker({ pricing })` rather than expecting a match.\n *\n * Prices are a dated snapshot (verified 2026-06) of public list prices and DRIFT — treat a total\n * derived from them as an estimate, not as a bill. `Object.freeze` here is shallow: the map cannot\n * gain keys, but the `ModelPricing` objects inside it are mutable.\n *\n * @public\n */\nexport const BUILTIN_PRICING: Readonly<Record<string, ModelPricing>> = Object.freeze({\n // OpenAI / OpenRouter aliases\n \"openai/gpt-4o\": { inputPerMillionUsd: 2.5, outputPerMillionUsd: 10 },\n \"openai/gpt-4o-mini\": { inputPerMillionUsd: 0.15, outputPerMillionUsd: 0.6 },\n \"openai/gpt-4-turbo\": { inputPerMillionUsd: 10, outputPerMillionUsd: 30 },\n \"openai/gpt-3.5-turbo\": { inputPerMillionUsd: 0.5, outputPerMillionUsd: 1.5 },\n // Anthropic / Claude\n \"anthropic/claude-3-5-sonnet-latest\": { inputPerMillionUsd: 3, outputPerMillionUsd: 15 },\n \"anthropic/claude-3-5-haiku-latest\": { inputPerMillionUsd: 0.8, outputPerMillionUsd: 4 },\n \"anthropic/claude-3-opus-latest\": { inputPerMillionUsd: 15, outputPerMillionUsd: 75 },\n // Google\n \"google/gemini-1.5-pro\": { inputPerMillionUsd: 1.25, outputPerMillionUsd: 5 },\n \"google/gemini-1.5-flash\": { inputPerMillionUsd: 0.075, outputPerMillionUsd: 0.3 },\n});\n\n/**\n * Compute USD cost for a single token event.\n *\n * Returns `undefined` (NOT `0`, NOT throws) when the model is UNKNOWN — its\n * cost is genuinely unknown, and coercing it to `$0` would be dishonest (cost\n * contract `D377-cost-status-closed-enum.md`: amount-unknown ≠ `$0`). Consumers\n * who want a price for a niche model supply an override via\n * `createUsdBudgetTracker({ pricing })`. A KNOWN model with zero/invalid tokens\n * returns `0` — that is a real, known `$0`.\n */\nexport function computeUsdCost(\n pricing: Readonly<Record<string, ModelPricing>>,\n model: string,\n type: \"input\" | \"output\",\n tokens: number,\n): number | undefined {\n const entry = pricing[model];\n if (entry === undefined) return undefined;\n const ratePerMillion = type === \"input\" ? entry.inputPerMillionUsd : entry.outputPerMillionUsd;\n if (!Number.isFinite(tokens) || tokens <= 0) return 0;\n return (tokens / 1_000_000) * ratePerMillion;\n}\n","/**\n * `createUsdBudgetTracker` — USD-cost-aware `BudgetTracker` impl shipped\n * in `@theokit/sdk-budget` (SDK 2.0 Phase 2 / T2.X).\n *\n * Extends the counter pattern from `createCounterBudgetTracker`\n * (sdk-core reference impl) with per-model USD cost computation via\n * the `BUILTIN_PRICING` table.\n *\n * Use cases:\n * - Cap total spend per agent run (`maxUsd`).\n * - Cap total tokens (`maxTokens`) AND USD ceiling simultaneously.\n * - Observe cumulative USD from outside (`getTotalUsd()`).\n *\n * Tracker.check() returns `allowed: false` when ANY of the configured\n * caps is exceeded. `reason` is one of:\n * - `\"token_limit\"` — `maxTokens` reached\n * - `\"cost_limit\"` — `maxUsd` reached\n *\n * Layered design (mirrors counter impl):\n * - `track()` is sync + non-throwing (clamps invalid values).\n * - `check()` is sync (allowed: true unless a cap is exceeded).\n * - `getTotal()` returns the SDK-required token + iteration totals\n * ONLY (USD is exposed via the bonus `getTotalUsd()` method).\n *\n * @public\n */\n\nimport type { BudgetCheck, BudgetTotal, BudgetTracker, BudgetUsageEvent } from \"@theokit/sdk\";\nimport { BUILTIN_PRICING, computeUsdCost, type ModelPricing } from \"./usd-pricing.js\";\n\n/**\n * Options for {@link createUsdBudgetTracker}. Omitting all of them builds a tracker that counts and\n * never denies.\n *\n * @public\n */\nexport interface UsdBudgetTrackerOptions {\n /**\n * Ceiling on total tokens, input and output combined. `check()` denies with\n * `reason: \"token_limit\"` once the running total REACHES it (`>=`, not `>`).\n */\n readonly maxTokens?: number;\n /**\n * Ceiling on cumulative USD. `check()` denies with `reason: \"cost_limit\"` once the total reaches\n * it.\n *\n * TRAP — setting this makes an UNPRICED MODEL DENY EVERYTHING. If any tracked event names a model\n * absent from the pricing table, the total cost becomes permanently unknown, and a cap that\n * cannot be verified fails CLOSED: every subsequent `check()` returns\n * `{ allowed: false, reason: \"cost_limit\", detail: \"cost unknown — …\" }`. This is intentional\n * (better than spending unbounded), but it means a model id that merely differs in spelling from\n * a {@link BUILTIN_PRICING} key halts the agent. Supply `pricing` for anything not in that table,\n * or leave `maxUsd` unset and gate on `maxTokens`.\n */\n readonly maxUsd?: number;\n /**\n * Per-model prices, spread OVER {@link BUILTIN_PRICING} — so an entry here replaces a built-in of\n * the same key, and built-ins you do not name are kept.\n *\n * Keys are matched EXACTLY against `BudgetUsageEvent.model`. There is no prefix, alias or\n * provider-stripping logic: `\"gpt-4o\"` does not match the built-in `\"openai/gpt-4o\"`. Use the\n * same id string you pass to `Agent.create({ model })`.\n */\n readonly pricing?: Readonly<Record<string, ModelPricing>>;\n}\n\n/** The cost-cap denial for the current state, or `null` if the cost cap is satisfied. */\nfunction evaluateCostCap(\n maxUsd: number | undefined,\n costKnown: boolean,\n totalUsd: number,\n): BudgetCheck | null {\n if (maxUsd === undefined) return null;\n if (!costKnown) {\n // Fail closed: a spend cap is set but cost is unknown — we cannot prove the\n // run is under budget, so deny rather than allow unbounded spend.\n return {\n allowed: false,\n reason: \"cost_limit\",\n detail: `cost unknown — cannot verify maxUsd ${maxUsd}`,\n };\n }\n if (totalUsd >= maxUsd) {\n return {\n allowed: false,\n reason: \"cost_limit\",\n detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`,\n };\n }\n return null;\n}\n\n/** The token-cap denial for the current state, or `null` if the token cap is satisfied. */\nfunction evaluateTokenCap(maxTokens: number | undefined, totalTokens: number): BudgetCheck | null {\n if (maxTokens !== undefined && totalTokens >= maxTokens) {\n return {\n allowed: false,\n reason: \"token_limit\",\n detail: `${totalTokens} >= maxTokens ${maxTokens}`,\n };\n }\n return null;\n}\n\n/**\n * Build a `BudgetTracker` that caps an agent run on tokens, on USD, or on both.\n *\n * ```ts\n * const agent = await Agent.create({\n * model: { id: \"openai/gpt-4o-mini\" },\n * budgetTracker: createUsdBudgetTracker({ maxUsd: 0.5 }),\n * });\n * ```\n *\n * The agent loop drives it: `track()` on every usage event, `nextIteration()` once per turn, and\n * `check()` before each turn — a denial halts the loop. Everything is per-instance and in-memory,\n * so one tracker is one run; reuse it across runs and the caps carry over.\n *\n * Three things a caller gets wrong here:\n *\n * - **`getTotal()` has no cost in it.** It returns `{ tokens, iterations }` only, and\n * `BudgetTotal.costUsd` is always `undefined` however much was spent. USD comes from the extra\n * `getTotalUsd()` on the returned object, which is `number | undefined` — `undefined` meaning\n * UNKNOWN, never zero.\n * - **Unknown cost is a one-way door.** The first event naming a model outside the pricing table\n * makes `getTotalUsd()` `undefined` for the rest of the run; a later priced event does not\n * restore it. Combined with `maxUsd`, that also denies every later `check()` — see\n * {@link UsdBudgetTrackerOptions.maxUsd}.\n * - **There is no iteration cap.** `iterations` is counted and reported, and nothing ever gates on\n * it. Use `createCounterBudgetTracker` from `@theokit/sdk` when you need `maxIterations`.\n *\n * `check()` evaluates the cost cap first, so a run breaching both caps reports `\"cost_limit\"`.\n * `track()` never throws and never rejects input — a non-finite or non-positive `tokens` is\n * discarded, silently.\n */\nexport function createUsdBudgetTracker(\n options: UsdBudgetTrackerOptions = {},\n): BudgetTracker & { nextIteration(): void; getTotalUsd(): number | undefined } {\n let totalTokens = 0;\n let iterations = 0;\n let totalUsd = 0;\n // Honest-null (D377): once any round's cost is UNKNOWN, the aggregate becomes\n // unknown and STAYS unknown — a later known round does not resurrect it.\n let costKnown = true;\n const maxTokens = options.maxTokens;\n const maxUsd = options.maxUsd;\n const pricing: Readonly<Record<string, ModelPricing>> =\n options.pricing !== undefined ? { ...BUILTIN_PRICING, ...options.pricing } : BUILTIN_PRICING;\n\n return {\n track(event: BudgetUsageEvent): void {\n // Sync + non-throwing per contract. Invalid events silently clamped.\n const t = Number.isFinite(event.tokens) && event.tokens > 0 ? event.tokens : 0;\n if (t === 0) return;\n totalTokens += t;\n const cost = computeUsdCost(pricing, event.model, event.type, t);\n if (cost === undefined) {\n costKnown = false; // poison — do NOT add 0 (that would be a dishonest $0)\n return;\n }\n if (costKnown) totalUsd += cost;\n },\n\n check(): BudgetCheck {\n // Cost cap is evaluated first (USD is the higher-signal denial for a human).\n return (\n evaluateCostCap(maxUsd, costKnown, totalUsd) ??\n evaluateTokenCap(maxTokens, totalTokens) ?? { allowed: true }\n );\n },\n\n getTotal(): BudgetTotal {\n return { tokens: totalTokens, iterations };\n },\n\n getTotalUsd(): number | undefined {\n return costKnown ? totalUsd : undefined;\n },\n\n nextIteration(): void {\n iterations += 1;\n },\n };\n}\n"]}