@theokit/sdk-budget 0.1.0 → 0.2.0
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/CHANGELOG.md +25 -3
- package/dist/index.cjs +38 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -5
- package/dist/index.d.ts +10 -5
- package/dist/index.js +38 -18
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
# Changelog — @theokit/sdk-budget
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1706517: M1-6 — multi-round usage aggregation is honest-null (plan `m1-usage-honest-null`).
|
|
8
|
+
|
|
9
|
+
Fixes a cost-honesty bug: `computeUsdCost` returned `0` for an unknown model, so a per-round cost that is genuinely UNKNOWN was silently summed as `$0`, making `createUsdBudgetTracker.getTotalUsd()` report a dishonest cheap/complete total and a `maxUsd` cap evaluate against an under-counted spend.
|
|
10
|
+
|
|
11
|
+
- `computeUsdCost(...)` now returns `number | undefined` — `undefined` for an unknown model (a known model with zero tokens still returns a real `0`). Aligns with the cost contract (`D377-cost-status-closed-enum.md`: amount-unknown ≠ `$0`), matching `@theokit/sdk/messages`' `costAmountUsd`.
|
|
12
|
+
- `createUsdBudgetTracker` POISONS the aggregate: once any round's cost is unknown, `getTotalUsd()` returns `undefined` (and stays undefined — a later known round does not resurrect it). Tokens are always known and still counted.
|
|
13
|
+
- `check()` FAILS CLOSED on a `maxUsd` cap when cost is unknown (returns `cost_limit` — it cannot prove the run is under budget). The `maxTokens` cap is unaffected.
|
|
14
|
+
|
|
15
|
+
**Type change:** `computeUsdCost` and `getTotalUsd()` now return `number | undefined` (was `number`). Consumers must branch on `undefined` (the point of the honest-null contract).
|
|
16
|
+
|
|
3
17
|
## [Unreleased]
|
|
4
18
|
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Multi-round usage aggregation is honest-null (M1-6).** `computeUsdCost` returned `0` for an unknown model, so a per-round cost that is genuinely UNKNOWN was silently summed as `$0` — making `getTotalUsd()` report a dishonest cheap/complete total and a `maxUsd` cap evaluate against under-counted spend. Now: `computeUsdCost(...): number | undefined` returns `undefined` for an unknown model (a known model with zero tokens still returns a real `0`); `createUsdBudgetTracker` poisons the aggregate so `getTotalUsd(): number | undefined` returns `undefined` once any round's cost is unknown (sticky; tokens still counted); `check()` fails closed on a `maxUsd` cap when cost is unknown (`cost_limit`). Aligns with the cost contract `D377-cost-status-closed-enum.md`. **Type change:** `computeUsdCost`/`getTotalUsd()` now return `number | undefined`.
|
|
22
|
+
|
|
5
23
|
### Added (Phase 2 physical Stage 1 — iter 19, 2026-06-08)
|
|
24
|
+
|
|
6
25
|
- Physically-extracted Budget internals into `sdk-budget/src/internal/`:
|
|
7
26
|
- `calendar-window` — UTC-aligned 1h/1d/1w/30d/365d window helpers.
|
|
8
27
|
- `enforcement` — `preflightCheck` + `chargeAndCheckThresholds` with
|
|
@@ -15,11 +34,12 @@
|
|
|
15
34
|
`deleteBudget` / `snapshotAll` / `defaultMode` / `getBudgetOptionsRaw`.
|
|
16
35
|
- Public exports added to the main barrel:
|
|
17
36
|
`createBudget, defaultMode, deleteBudget, getBudget, getBudgetOptionsRaw,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
listBudgets, snapshotAll, chargeAndCheckThresholds, preflightCheck,
|
|
38
|
+
inferApiMode, normalizeUsage, startOfDayUtc, startOfWeekUtc,
|
|
39
|
+
windowStartMs, charge, spentIn`.
|
|
21
40
|
|
|
22
41
|
### Changed (iter 19)
|
|
42
|
+
|
|
23
43
|
- Package now ships ~568 LOC of canonical Budget logic (was 0 LOC pre-Stage-1).
|
|
24
44
|
- Bundle size: 2.51 KB → 13.47 KB ESM (the extracted internals).
|
|
25
45
|
- `peerDependency`: `@theokit/sdk >= 1.7.0` confirmed (ADR-008's
|
|
@@ -28,6 +48,7 @@
|
|
|
28
48
|
## [0.1.0] — 2026-06-08
|
|
29
49
|
|
|
30
50
|
### Added
|
|
51
|
+
|
|
31
52
|
- Initial release. Consumes the `BudgetTracker` port from `@theokit/sdk@>=1.7.0`
|
|
32
53
|
(SDK 2.0 Phase 2 / T2.X).
|
|
33
54
|
- `createUsdBudgetTracker({ maxTokens?, maxUsd?, pricing? })` — USD-cost-
|
|
@@ -38,6 +59,7 @@
|
|
|
38
59
|
- `computeUsdCost(pricing, model, type, tokens)` — pure helper.
|
|
39
60
|
|
|
40
61
|
### Notes
|
|
62
|
+
|
|
41
63
|
- Live-rate fetching / ledger persistence / per-user aggregation deferred
|
|
42
64
|
to future versions. This release ships the package foundation + a
|
|
43
65
|
working USD impl so the SDK 2.0 cohort can publish (Phase 7).
|
package/dist/index.cjs
CHANGED
|
@@ -357,17 +357,46 @@ var BUILTIN_PRICING = Object.freeze({
|
|
|
357
357
|
});
|
|
358
358
|
function computeUsdCost(pricing, model, type, tokens) {
|
|
359
359
|
const entry = pricing[model];
|
|
360
|
-
if (entry === void 0) return 0;
|
|
360
|
+
if (entry === void 0) return void 0;
|
|
361
361
|
const ratePerMillion = type === "input" ? entry.inputPerMillionUsd : entry.outputPerMillionUsd;
|
|
362
362
|
if (!Number.isFinite(tokens) || tokens <= 0) return 0;
|
|
363
363
|
return tokens / 1e6 * ratePerMillion;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
// src/usd-budget-tracker.ts
|
|
367
|
+
function evaluateCostCap(maxUsd, costKnown, totalUsd) {
|
|
368
|
+
if (maxUsd === void 0) return null;
|
|
369
|
+
if (!costKnown) {
|
|
370
|
+
return {
|
|
371
|
+
allowed: false,
|
|
372
|
+
reason: "cost_limit",
|
|
373
|
+
detail: `cost unknown \u2014 cannot verify maxUsd ${maxUsd}`
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
if (totalUsd >= maxUsd) {
|
|
377
|
+
return {
|
|
378
|
+
allowed: false,
|
|
379
|
+
reason: "cost_limit",
|
|
380
|
+
detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
function evaluateTokenCap(maxTokens, totalTokens) {
|
|
386
|
+
if (maxTokens !== void 0 && totalTokens >= maxTokens) {
|
|
387
|
+
return {
|
|
388
|
+
allowed: false,
|
|
389
|
+
reason: "token_limit",
|
|
390
|
+
detail: `${totalTokens} >= maxTokens ${maxTokens}`
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
367
395
|
function createUsdBudgetTracker(options = {}) {
|
|
368
396
|
let totalTokens = 0;
|
|
369
397
|
let iterations = 0;
|
|
370
398
|
let totalUsd = 0;
|
|
399
|
+
let costKnown = true;
|
|
371
400
|
const maxTokens = options.maxTokens;
|
|
372
401
|
const maxUsd = options.maxUsd;
|
|
373
402
|
const pricing = options.pricing !== void 0 ? { ...BUILTIN_PRICING, ...options.pricing } : BUILTIN_PRICING;
|
|
@@ -376,30 +405,21 @@ function createUsdBudgetTracker(options = {}) {
|
|
|
376
405
|
const t = Number.isFinite(event.tokens) && event.tokens > 0 ? event.tokens : 0;
|
|
377
406
|
if (t === 0) return;
|
|
378
407
|
totalTokens += t;
|
|
379
|
-
|
|
408
|
+
const cost = computeUsdCost(pricing, event.model, event.type, t);
|
|
409
|
+
if (cost === void 0) {
|
|
410
|
+
costKnown = false;
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (costKnown) totalUsd += cost;
|
|
380
414
|
},
|
|
381
415
|
check() {
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
allowed: false,
|
|
385
|
-
reason: "cost_limit",
|
|
386
|
-
detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
if (maxTokens !== void 0 && totalTokens >= maxTokens) {
|
|
390
|
-
return {
|
|
391
|
-
allowed: false,
|
|
392
|
-
reason: "token_limit",
|
|
393
|
-
detail: `${totalTokens} >= maxTokens ${maxTokens}`
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
return { allowed: true };
|
|
416
|
+
return evaluateCostCap(maxUsd, costKnown, totalUsd) ?? evaluateTokenCap(maxTokens, totalTokens) ?? { allowed: true };
|
|
397
417
|
},
|
|
398
418
|
getTotal() {
|
|
399
419
|
return { tokens: totalTokens, iterations };
|
|
400
420
|
},
|
|
401
421
|
getTotalUsd() {
|
|
402
|
-
return totalUsd;
|
|
422
|
+
return costKnown ? totalUsd : void 0;
|
|
403
423
|
},
|
|
404
424
|
nextIteration() {
|
|
405
425
|
iterations += 1;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../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":["withCwdMutex","ConfigurationError","BudgetExceededError"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\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 DENTRO do mesmo mutex que charge — sem race.\n * EC-9: charge() é called inside 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\nlet 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/** Diagnostic — total logs across all budgets. */\nfunction __getLogCountForTests(name?: string): number {\n if (name !== undefined) return state.logs.get(name)?.length ?? 0;\n let total = 0;\n for (const arr of state.logs.values()) total += arr.length;\n return total;\n}\n\nfunction __resetLedgerForTests(): void {\n state = { logs: new Map(), lastGcAt: Date.now() };\n}\n\n/** Force GC manually (tests only). */\nasync function __evictNowForTests(): Promise<void> {\n await withCwdMutex(MUTEX_KEY, async () => {\n gcOlderThanOneYear(Date.now());\n });\n}\n\n/** Force-insert a log at a specific timestamp (tests only). */\nfunction __injectLogForTests(name: string, timestamp: number, amountUsd: number): void {\n const list = state.logs.get(name) ?? [];\n list.push({ timestamp, amountUsd });\n state.logs.set(name, list);\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 __resetRegistryForTests(): void {\n registry.clear();\n}\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` antes da LLM call se qualquer limit seria\n * excedido (EC-9 — caller chama dentro do mutex section).\n * - `chargeAndCheckThresholds(name, actualUsd)` — apply charge ao\n * ledger + invoca onThreshold/onExceed callbacks isolated em\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 * (referencia/hermes-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 * - cline#10266 — OpenAI-compat proxies (OpenRouter, Vercel AI Gateway,\n * Cline) 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 // cline#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. Returns 0 (NOT throws)\n * when the model is unknown — consumers should override the pricing\n * table to add their model.\n */\nexport function computeUsdCost(\n pricing: Readonly<Record<string, ModelPricing>>,\n model: string,\n type: \"input\" | \"output\",\n tokens: number,\n): number {\n const entry = pricing[model];\n if (entry === undefined) return 0;\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/**\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 } {\n let totalTokens = 0;\n let iterations = 0;\n let totalUsd = 0;\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 totalUsd += computeUsdCost(pricing, event.model, event.type, t);\n },\n\n check(): BudgetCheck {\n if (maxUsd !== undefined && totalUsd >= maxUsd) {\n return {\n allowed: false,\n reason: \"cost_limit\",\n detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`,\n };\n }\n if (maxTokens !== undefined && totalTokens >= maxTokens) {\n return {\n allowed: false,\n reason: \"token_limit\",\n detail: `${totalTokens} >= maxTokens ${maxTokens}`,\n };\n }\n return { allowed: true };\n },\n\n getTotal(): BudgetTotal {\n return { tokens: totalTokens, iterations };\n },\n\n getTotalUsd(): number {\n return totalUsd;\n },\n\n nextIteration(): void {\n iterations += 1;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../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":["withCwdMutex","ConfigurationError","BudgetExceededError"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\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 DENTRO do mesmo mutex que charge — sem race.\n * EC-9: charge() é called inside 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\nlet 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/** Diagnostic — total logs across all budgets. */\nfunction __getLogCountForTests(name?: string): number {\n if (name !== undefined) return state.logs.get(name)?.length ?? 0;\n let total = 0;\n for (const arr of state.logs.values()) total += arr.length;\n return total;\n}\n\nfunction __resetLedgerForTests(): void {\n state = { logs: new Map(), lastGcAt: Date.now() };\n}\n\n/** Force GC manually (tests only). */\nasync function __evictNowForTests(): Promise<void> {\n await withCwdMutex(MUTEX_KEY, async () => {\n gcOlderThanOneYear(Date.now());\n });\n}\n\n/** Force-insert a log at a specific timestamp (tests only). */\nfunction __injectLogForTests(name: string, timestamp: number, amountUsd: number): void {\n const list = state.logs.get(name) ?? [];\n list.push({ timestamp, amountUsd });\n state.logs.set(name, list);\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 __resetRegistryForTests(): void {\n registry.clear();\n}\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` antes da LLM call se qualquer limit seria\n * excedido (EC-9 — caller chama dentro do mutex section).\n * - `chargeAndCheckThresholds(name, actualUsd)` — apply charge ao\n * ledger + invoca onThreshold/onExceed callbacks isolated em\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 * (referencia/hermes-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 * - cline#10266 — OpenAI-compat proxies (OpenRouter, Vercel AI Gateway,\n * Cline) 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 // cline#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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -136,11 +136,16 @@ interface ModelPricing {
|
|
|
136
136
|
/** Built-in pricing table — exhaustive enough for v0.1; users supply overrides for niche models. */
|
|
137
137
|
declare const BUILTIN_PRICING: Readonly<Record<string, ModelPricing>>;
|
|
138
138
|
/**
|
|
139
|
-
* Compute USD cost for a single token event.
|
|
140
|
-
*
|
|
141
|
-
*
|
|
139
|
+
* Compute USD cost for a single token event.
|
|
140
|
+
*
|
|
141
|
+
* Returns `undefined` (NOT `0`, NOT throws) when the model is UNKNOWN — its
|
|
142
|
+
* cost is genuinely unknown, and coercing it to `$0` would be dishonest (cost
|
|
143
|
+
* contract `D377-cost-status-closed-enum.md`: amount-unknown ≠ `$0`). Consumers
|
|
144
|
+
* who want a price for a niche model supply an override via
|
|
145
|
+
* `createUsdBudgetTracker({ pricing })`. A KNOWN model with zero/invalid tokens
|
|
146
|
+
* returns `0` — that is a real, known `$0`.
|
|
142
147
|
*/
|
|
143
|
-
declare function computeUsdCost(pricing: Readonly<Record<string, ModelPricing>>, model: string, type: "input" | "output", tokens: number): number;
|
|
148
|
+
declare function computeUsdCost(pricing: Readonly<Record<string, ModelPricing>>, model: string, type: "input" | "output", tokens: number): number | undefined;
|
|
144
149
|
|
|
145
150
|
/**
|
|
146
151
|
* `createUsdBudgetTracker` — USD-cost-aware `BudgetTracker` impl shipped
|
|
@@ -185,7 +190,7 @@ interface UsdBudgetTrackerOptions {
|
|
|
185
190
|
*/
|
|
186
191
|
declare function createUsdBudgetTracker(options?: UsdBudgetTrackerOptions): BudgetTracker & {
|
|
187
192
|
nextIteration(): void;
|
|
188
|
-
getTotalUsd(): number;
|
|
193
|
+
getTotalUsd(): number | undefined;
|
|
189
194
|
};
|
|
190
195
|
|
|
191
196
|
export { BUILTIN_PRICING, type ModelPricing, type UsdBudgetTrackerOptions, charge, chargeAndCheckThresholds, computeUsdCost, createBudget, createUsdBudgetTracker, defaultMode, deleteBudget, getBudget, getBudgetOptionsRaw, inferApiMode, listBudgets, normalizeUsage, preflightCheck, snapshotAll, spentIn, startOfDayUtc, startOfWeekUtc, windowStartMs };
|
package/dist/index.d.ts
CHANGED
|
@@ -136,11 +136,16 @@ interface ModelPricing {
|
|
|
136
136
|
/** Built-in pricing table — exhaustive enough for v0.1; users supply overrides for niche models. */
|
|
137
137
|
declare const BUILTIN_PRICING: Readonly<Record<string, ModelPricing>>;
|
|
138
138
|
/**
|
|
139
|
-
* Compute USD cost for a single token event.
|
|
140
|
-
*
|
|
141
|
-
*
|
|
139
|
+
* Compute USD cost for a single token event.
|
|
140
|
+
*
|
|
141
|
+
* Returns `undefined` (NOT `0`, NOT throws) when the model is UNKNOWN — its
|
|
142
|
+
* cost is genuinely unknown, and coercing it to `$0` would be dishonest (cost
|
|
143
|
+
* contract `D377-cost-status-closed-enum.md`: amount-unknown ≠ `$0`). Consumers
|
|
144
|
+
* who want a price for a niche model supply an override via
|
|
145
|
+
* `createUsdBudgetTracker({ pricing })`. A KNOWN model with zero/invalid tokens
|
|
146
|
+
* returns `0` — that is a real, known `$0`.
|
|
142
147
|
*/
|
|
143
|
-
declare function computeUsdCost(pricing: Readonly<Record<string, ModelPricing>>, model: string, type: "input" | "output", tokens: number): number;
|
|
148
|
+
declare function computeUsdCost(pricing: Readonly<Record<string, ModelPricing>>, model: string, type: "input" | "output", tokens: number): number | undefined;
|
|
144
149
|
|
|
145
150
|
/**
|
|
146
151
|
* `createUsdBudgetTracker` — USD-cost-aware `BudgetTracker` impl shipped
|
|
@@ -185,7 +190,7 @@ interface UsdBudgetTrackerOptions {
|
|
|
185
190
|
*/
|
|
186
191
|
declare function createUsdBudgetTracker(options?: UsdBudgetTrackerOptions): BudgetTracker & {
|
|
187
192
|
nextIteration(): void;
|
|
188
|
-
getTotalUsd(): number;
|
|
193
|
+
getTotalUsd(): number | undefined;
|
|
189
194
|
};
|
|
190
195
|
|
|
191
196
|
export { BUILTIN_PRICING, type ModelPricing, type UsdBudgetTrackerOptions, charge, chargeAndCheckThresholds, computeUsdCost, createBudget, createUsdBudgetTracker, defaultMode, deleteBudget, getBudget, getBudgetOptionsRaw, inferApiMode, listBudgets, normalizeUsage, preflightCheck, snapshotAll, spentIn, startOfDayUtc, startOfWeekUtc, windowStartMs };
|
package/dist/index.js
CHANGED
|
@@ -355,17 +355,46 @@ var BUILTIN_PRICING = Object.freeze({
|
|
|
355
355
|
});
|
|
356
356
|
function computeUsdCost(pricing, model, type, tokens) {
|
|
357
357
|
const entry = pricing[model];
|
|
358
|
-
if (entry === void 0) return 0;
|
|
358
|
+
if (entry === void 0) return void 0;
|
|
359
359
|
const ratePerMillion = type === "input" ? entry.inputPerMillionUsd : entry.outputPerMillionUsd;
|
|
360
360
|
if (!Number.isFinite(tokens) || tokens <= 0) return 0;
|
|
361
361
|
return tokens / 1e6 * ratePerMillion;
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
// src/usd-budget-tracker.ts
|
|
365
|
+
function evaluateCostCap(maxUsd, costKnown, totalUsd) {
|
|
366
|
+
if (maxUsd === void 0) return null;
|
|
367
|
+
if (!costKnown) {
|
|
368
|
+
return {
|
|
369
|
+
allowed: false,
|
|
370
|
+
reason: "cost_limit",
|
|
371
|
+
detail: `cost unknown \u2014 cannot verify maxUsd ${maxUsd}`
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
if (totalUsd >= maxUsd) {
|
|
375
|
+
return {
|
|
376
|
+
allowed: false,
|
|
377
|
+
reason: "cost_limit",
|
|
378
|
+
detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
function evaluateTokenCap(maxTokens, totalTokens) {
|
|
384
|
+
if (maxTokens !== void 0 && totalTokens >= maxTokens) {
|
|
385
|
+
return {
|
|
386
|
+
allowed: false,
|
|
387
|
+
reason: "token_limit",
|
|
388
|
+
detail: `${totalTokens} >= maxTokens ${maxTokens}`
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
365
393
|
function createUsdBudgetTracker(options = {}) {
|
|
366
394
|
let totalTokens = 0;
|
|
367
395
|
let iterations = 0;
|
|
368
396
|
let totalUsd = 0;
|
|
397
|
+
let costKnown = true;
|
|
369
398
|
const maxTokens = options.maxTokens;
|
|
370
399
|
const maxUsd = options.maxUsd;
|
|
371
400
|
const pricing = options.pricing !== void 0 ? { ...BUILTIN_PRICING, ...options.pricing } : BUILTIN_PRICING;
|
|
@@ -374,30 +403,21 @@ function createUsdBudgetTracker(options = {}) {
|
|
|
374
403
|
const t = Number.isFinite(event.tokens) && event.tokens > 0 ? event.tokens : 0;
|
|
375
404
|
if (t === 0) return;
|
|
376
405
|
totalTokens += t;
|
|
377
|
-
|
|
406
|
+
const cost = computeUsdCost(pricing, event.model, event.type, t);
|
|
407
|
+
if (cost === void 0) {
|
|
408
|
+
costKnown = false;
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (costKnown) totalUsd += cost;
|
|
378
412
|
},
|
|
379
413
|
check() {
|
|
380
|
-
|
|
381
|
-
return {
|
|
382
|
-
allowed: false,
|
|
383
|
-
reason: "cost_limit",
|
|
384
|
-
detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
if (maxTokens !== void 0 && totalTokens >= maxTokens) {
|
|
388
|
-
return {
|
|
389
|
-
allowed: false,
|
|
390
|
-
reason: "token_limit",
|
|
391
|
-
detail: `${totalTokens} >= maxTokens ${maxTokens}`
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
return { allowed: true };
|
|
414
|
+
return evaluateCostCap(maxUsd, costKnown, totalUsd) ?? evaluateTokenCap(maxTokens, totalTokens) ?? { allowed: true };
|
|
395
415
|
},
|
|
396
416
|
getTotal() {
|
|
397
417
|
return { tokens: totalTokens, iterations };
|
|
398
418
|
},
|
|
399
419
|
getTotalUsd() {
|
|
400
|
-
return totalUsd;
|
|
420
|
+
return costKnown ? totalUsd : void 0;
|
|
401
421
|
},
|
|
402
422
|
nextIteration() {
|
|
403
423
|
iterations += 1;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../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 * 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 DENTRO do mesmo mutex que charge — sem race.\n * EC-9: charge() é called inside 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\nlet 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/** Diagnostic — total logs across all budgets. */\nfunction __getLogCountForTests(name?: string): number {\n if (name !== undefined) return state.logs.get(name)?.length ?? 0;\n let total = 0;\n for (const arr of state.logs.values()) total += arr.length;\n return total;\n}\n\nfunction __resetLedgerForTests(): void {\n state = { logs: new Map(), lastGcAt: Date.now() };\n}\n\n/** Force GC manually (tests only). */\nasync function __evictNowForTests(): Promise<void> {\n await withCwdMutex(MUTEX_KEY, async () => {\n gcOlderThanOneYear(Date.now());\n });\n}\n\n/** Force-insert a log at a specific timestamp (tests only). */\nfunction __injectLogForTests(name: string, timestamp: number, amountUsd: number): void {\n const list = state.logs.get(name) ?? [];\n list.push({ timestamp, amountUsd });\n state.logs.set(name, list);\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 __resetRegistryForTests(): void {\n registry.clear();\n}\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` antes da LLM call se qualquer limit seria\n * excedido (EC-9 — caller chama dentro do mutex section).\n * - `chargeAndCheckThresholds(name, actualUsd)` — apply charge ao\n * ledger + invoca onThreshold/onExceed callbacks isolated em\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 * (referencia/hermes-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 * - cline#10266 — OpenAI-compat proxies (OpenRouter, Vercel AI Gateway,\n * Cline) 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 // cline#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. Returns 0 (NOT throws)\n * when the model is unknown — consumers should override the pricing\n * table to add their model.\n */\nexport function computeUsdCost(\n pricing: Readonly<Record<string, ModelPricing>>,\n model: string,\n type: \"input\" | \"output\",\n tokens: number,\n): number {\n const entry = pricing[model];\n if (entry === undefined) return 0;\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/**\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 } {\n let totalTokens = 0;\n let iterations = 0;\n let totalUsd = 0;\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 totalUsd += computeUsdCost(pricing, event.model, event.type, t);\n },\n\n check(): BudgetCheck {\n if (maxUsd !== undefined && totalUsd >= maxUsd) {\n return {\n allowed: false,\n reason: \"cost_limit\",\n detail: `USD ${totalUsd.toFixed(6)} >= maxUsd ${maxUsd}`,\n };\n }\n if (maxTokens !== undefined && totalTokens >= maxTokens) {\n return {\n allowed: false,\n reason: \"token_limit\",\n detail: `${totalTokens} >= maxTokens ${maxTokens}`,\n };\n }\n return { allowed: true };\n },\n\n getTotal(): BudgetTotal {\n return { tokens: totalTokens, iterations };\n },\n\n getTotalUsd(): number {\n return totalUsd;\n },\n\n nextIteration(): void {\n iterations += 1;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../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 * 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 DENTRO do mesmo mutex que charge — sem race.\n * EC-9: charge() é called inside 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\nlet 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/** Diagnostic — total logs across all budgets. */\nfunction __getLogCountForTests(name?: string): number {\n if (name !== undefined) return state.logs.get(name)?.length ?? 0;\n let total = 0;\n for (const arr of state.logs.values()) total += arr.length;\n return total;\n}\n\nfunction __resetLedgerForTests(): void {\n state = { logs: new Map(), lastGcAt: Date.now() };\n}\n\n/** Force GC manually (tests only). */\nasync function __evictNowForTests(): Promise<void> {\n await withCwdMutex(MUTEX_KEY, async () => {\n gcOlderThanOneYear(Date.now());\n });\n}\n\n/** Force-insert a log at a specific timestamp (tests only). */\nfunction __injectLogForTests(name: string, timestamp: number, amountUsd: number): void {\n const list = state.logs.get(name) ?? [];\n list.push({ timestamp, amountUsd });\n state.logs.set(name, list);\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 __resetRegistryForTests(): void {\n registry.clear();\n}\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` antes da LLM call se qualquer limit seria\n * excedido (EC-9 — caller chama dentro do mutex section).\n * - `chargeAndCheckThresholds(name, actualUsd)` — apply charge ao\n * ledger + invoca onThreshold/onExceed callbacks isolated em\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 * (referencia/hermes-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 * - cline#10266 — OpenAI-compat proxies (OpenRouter, Vercel AI Gateway,\n * Cline) 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 // cline#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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theokit/sdk-budget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "USD-cost-aware BudgetTracker impls for @theokit/sdk — extends the counter-based reference with per-model pricing. Consumes the kernel-facing BudgetTracker port from sdk-core.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/usetheo/theokit-sdk#readme",
|
|
@@ -37,11 +37,6 @@
|
|
|
37
37
|
"CHANGELOG.md",
|
|
38
38
|
"LICENSE"
|
|
39
39
|
],
|
|
40
|
-
"scripts": {
|
|
41
|
-
"build": "tsup",
|
|
42
|
-
"test": "vitest run",
|
|
43
|
-
"typecheck": "tsc --noEmit"
|
|
44
|
-
},
|
|
45
40
|
"peerDependencies": {
|
|
46
41
|
"@theokit/sdk": ">=1.7.0"
|
|
47
42
|
},
|
|
@@ -49,9 +44,14 @@
|
|
|
49
44
|
"tsup": "^8.3.5",
|
|
50
45
|
"typescript": "^5.7.2",
|
|
51
46
|
"vitest": "^4.1.8",
|
|
52
|
-
"@theokit/sdk": "
|
|
47
|
+
"@theokit/sdk": "2.4.0"
|
|
53
48
|
},
|
|
54
49
|
"publishConfig": {
|
|
55
50
|
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"typecheck": "tsc --noEmit"
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|