@tangle-network/agent-eval 0.115.3 → 0.117.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.
Files changed (134) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/analyst/index.d.ts +16 -11
  3. package/dist/analyst/index.js +33 -25
  4. package/dist/analyst/index.js.map +1 -1
  5. package/dist/{analyze-runs-BYHg6Irm.d.ts → analyze-runs--2x39HZ7.d.ts} +3 -3
  6. package/dist/{baseline-DsNteOgR.d.ts → baseline-DKq3gJpP.d.ts} +6 -3
  7. package/dist/belief-state/index.d.ts +6 -6
  8. package/dist/belief-state/index.js +1 -1
  9. package/dist/benchmarks/index.d.ts +12 -5
  10. package/dist/benchmarks/index.js +11 -10
  11. package/dist/builder-eval/index.d.ts +4 -4
  12. package/dist/builder-eval/index.js +1 -1
  13. package/dist/{calibration-Dz8TQV4y.d.ts → calibration-C8MTS7cw.d.ts} +2 -2
  14. package/dist/campaign/index.d.ts +247 -34
  15. package/dist/campaign/index.js +33 -13
  16. package/dist/chunk-3YYRZDON.js +45 -0
  17. package/dist/chunk-3YYRZDON.js.map +1 -0
  18. package/dist/{chunk-RPDDVKI7.js → chunk-4JLWXDYA.js} +2 -2
  19. package/dist/{chunk-WSBUZMBU.js → chunk-CCZIVI3F.js} +54 -115
  20. package/dist/chunk-CCZIVI3F.js.map +1 -0
  21. package/dist/{chunk-J6P6PK2R.js → chunk-FQNLDL4D.js} +3 -3
  22. package/dist/{chunk-ONM6PEAE.js → chunk-GQCZRZ7L.js} +2 -2
  23. package/dist/chunk-HHWE3POT.js +94 -0
  24. package/dist/chunk-HHWE3POT.js.map +1 -0
  25. package/dist/{chunk-ADYLPOSX.js → chunk-HQPHZGL6.js} +1112 -135
  26. package/dist/chunk-HQPHZGL6.js.map +1 -0
  27. package/dist/{chunk-FAOEFFRT.js → chunk-IDZTTFRR.js} +390 -78
  28. package/dist/chunk-IDZTTFRR.js.map +1 -0
  29. package/dist/{chunk-3LXTCTWL.js → chunk-JSDVRFAP.js} +2 -2
  30. package/dist/{chunk-MHNQWM4I.js → chunk-LQUTGLOZ.js} +5 -1
  31. package/dist/chunk-LQUTGLOZ.js.map +1 -0
  32. package/dist/{chunk-4D5RVB3W.js → chunk-LTVG32KX.js} +30 -5
  33. package/dist/chunk-LTVG32KX.js.map +1 -0
  34. package/dist/{chunk-5S5NJ63F.js → chunk-MGEHEHSN.js} +807 -15
  35. package/dist/chunk-MGEHEHSN.js.map +1 -0
  36. package/dist/{chunk-GY4SYVPJ.js → chunk-NJC7U437.js} +97 -25
  37. package/dist/chunk-NJC7U437.js.map +1 -0
  38. package/dist/{chunk-NYFUT3B3.js → chunk-ODVOOEWQ.js} +31 -10
  39. package/dist/chunk-ODVOOEWQ.js.map +1 -0
  40. package/dist/{chunk-LNQEP766.js → chunk-S2F4J57L.js} +44 -4
  41. package/dist/chunk-S2F4J57L.js.map +1 -0
  42. package/dist/chunk-VCTY3W6J.js +798 -0
  43. package/dist/chunk-VCTY3W6J.js.map +1 -0
  44. package/dist/chunk-VF3XSYTI.js +545 -0
  45. package/dist/chunk-VF3XSYTI.js.map +1 -0
  46. package/dist/{chunk-TLDB7WRY.js → chunk-YZPO4UHR.js} +28 -31
  47. package/dist/chunk-YZPO4UHR.js.map +1 -0
  48. package/dist/{chunk-KG4TD7EQ.js → chunk-ZUXV7UWZ.js} +1425 -697
  49. package/dist/chunk-ZUXV7UWZ.js.map +1 -0
  50. package/dist/cli.js +4 -2
  51. package/dist/cli.js.map +1 -1
  52. package/dist/{code-agent-session-D-g04tcy.d.ts → code-agent-session-CjZsVd19.d.ts} +1 -1
  53. package/dist/contract/index.d.ts +45 -31
  54. package/dist/contract/index.js +58 -19
  55. package/dist/contract/index.js.map +1 -1
  56. package/dist/{control-CcBiAEnn.d.ts → control-6vuGfmDH.d.ts} +5 -5
  57. package/dist/control.d.ts +6 -6
  58. package/dist/cost-ledger-DWy3XdJc.d.ts +183 -0
  59. package/dist/{default-registry-DltpYR5u.d.ts → default-registry-DaK8b3fv.d.ts} +2 -1
  60. package/dist/{emitter-BRchAAAx.d.ts → emitter-CjD7vUwv.d.ts} +2 -2
  61. package/dist/{failure-cluster-C48PiReX.d.ts → failure-cluster-DOAcSJ87.d.ts} +2 -2
  62. package/dist/{feedback-trajectory-pDcz1lQ1.d.ts → feedback-trajectory-BUnM58xL.d.ts} +3 -3
  63. package/dist/fuzz.d.ts +8 -16
  64. package/dist/fuzz.js +72 -42
  65. package/dist/fuzz.js.map +1 -1
  66. package/dist/{gepa-dne9JDPL.d.ts → gepa-eESocoDi.d.ts} +64 -12
  67. package/dist/hosted/index.d.ts +14 -7
  68. package/dist/{index-BTEpx9He.d.ts → index-PdX4VnPA.d.ts} +3 -3
  69. package/dist/index.d.ts +97 -55
  70. package/dist/index.js +343 -244
  71. package/dist/index.js.map +1 -1
  72. package/dist/{insight-report-IwwvqZZv.d.ts → insight-report-DY4nDW9Q.d.ts} +1 -1
  73. package/dist/{integrity-qemeBAyx.d.ts → integrity-DqlBiLyK.d.ts} +1 -1
  74. package/dist/kind-factory-ClZmO25A.d.ts +171 -0
  75. package/dist/{llm-client-DyqEH4jH.d.ts → llm-client-qoDd18Qz.d.ts} +27 -3
  76. package/dist/meta-eval/index.d.ts +8 -7
  77. package/dist/meta-eval/index.js +1 -1
  78. package/dist/multishot/index.d.ts +10 -3
  79. package/dist/openapi.json +1 -1
  80. package/dist/pipelines/index.d.ts +16 -6
  81. package/dist/pipelines/index.js +119 -23
  82. package/dist/pipelines/index.js.map +1 -1
  83. package/dist/{kind-factory-DcNg13sZ.d.ts → policy-edit-wG9uFEFm.d.ts} +114 -167
  84. package/dist/{pre-registration-D8h7ZxNL.d.ts → pre-registration-BWQhJ3vz.d.ts} +23 -4
  85. package/dist/{provenance-Bibyg1U9.d.ts → provenance-DpjwyseI.d.ts} +28 -16
  86. package/dist/{query-Ck190MOd.d.ts → query-CF7PG61p.d.ts} +5 -3
  87. package/dist/{release-report-CCtzajxP.d.ts → release-report-C8G2i5Xi.d.ts} +2 -2
  88. package/dist/reporting.d.ts +10 -9
  89. package/dist/{researcher-Dq-EtpbE.d.ts → researcher-C8XyxQsu.d.ts} +7 -7
  90. package/dist/rl.d.ts +17 -12
  91. package/dist/rl.js +2 -2
  92. package/dist/{rubric-predictive-validity-DYTLjGWu.d.ts → rubric-predictive-validity-p49lLVrE.d.ts} +1 -1
  93. package/dist/{run-campaign-UADIM77S.js → run-campaign-IM26A6PD.js} +4 -2
  94. package/dist/{run-record-B7RTi_ix.d.ts → run-record-BDH49H2E.d.ts} +2 -2
  95. package/dist/{runtime-trajectory-Dws7Kpgi.d.ts → runtime-trajectory-DGBIUt4B.d.ts} +1 -1
  96. package/dist/{schema-SGWcK9wa.d.ts → schema-B3Q3l9Z_.d.ts} +2 -0
  97. package/dist/{semantic-concept-judge-DxJmRkyJ.d.ts → semantic-concept-judge-CXnPEJbf.d.ts} +23 -5
  98. package/dist/{statistics-oUbOJe-S.d.ts → statistics-KUnG73jH.d.ts} +1 -1
  99. package/dist/{storage-Dw_f7WMt.d.ts → storage-DrX3v_5B.d.ts} +12 -1
  100. package/dist/{store-BsVi7ncX.d.ts → store-DGqD0Pyo.d.ts} +1 -1
  101. package/dist/storyboard/index.d.ts +1 -1
  102. package/dist/{summary-report-BJ5aNwZ1.d.ts → summary-report-C5bKFfm-.d.ts} +2 -2
  103. package/dist/{test-graded-scenario-mzYBKspu.d.ts → test-graded-scenario-B0ybnPY7.d.ts} +3 -3
  104. package/dist/traces.d.ts +19 -10
  105. package/dist/traces.js +16 -4
  106. package/dist/{types-C5gJrOVT.d.ts → types-BSw1rOUB.d.ts} +97 -38
  107. package/dist/{types-C7DGg5ex.d.ts → types-BkfcQnxV.d.ts} +15 -0
  108. package/dist/wire/index.d.ts +28 -19
  109. package/dist/wire/index.js +4 -2
  110. package/docs/design/loop-taxonomy.md +1 -2
  111. package/docs/distributed-driver.md +1 -1
  112. package/package.json +3 -3
  113. package/dist/chunk-4D5RVB3W.js.map +0 -1
  114. package/dist/chunk-5S5NJ63F.js.map +0 -1
  115. package/dist/chunk-ADYLPOSX.js.map +0 -1
  116. package/dist/chunk-FAOEFFRT.js.map +0 -1
  117. package/dist/chunk-GY4SYVPJ.js.map +0 -1
  118. package/dist/chunk-I6LVHOV3.js +0 -205
  119. package/dist/chunk-I6LVHOV3.js.map +0 -1
  120. package/dist/chunk-KG4TD7EQ.js.map +0 -1
  121. package/dist/chunk-LNQEP766.js.map +0 -1
  122. package/dist/chunk-MHNQWM4I.js.map +0 -1
  123. package/dist/chunk-NYFUT3B3.js.map +0 -1
  124. package/dist/chunk-QMXXSNC4.js +0 -761
  125. package/dist/chunk-QMXXSNC4.js.map +0 -1
  126. package/dist/chunk-TLDB7WRY.js.map +0 -1
  127. package/dist/chunk-WSBUZMBU.js.map +0 -1
  128. package/dist/cost-ledger-DuSqlw5B.d.ts +0 -113
  129. package/dist/policy-edit-RLn8GWof.d.ts +0 -103
  130. /package/dist/{chunk-RPDDVKI7.js.map → chunk-4JLWXDYA.js.map} +0 -0
  131. /package/dist/{chunk-J6P6PK2R.js.map → chunk-FQNLDL4D.js.map} +0 -0
  132. /package/dist/{chunk-ONM6PEAE.js.map → chunk-GQCZRZ7L.js.map} +0 -0
  133. /package/dist/{chunk-3LXTCTWL.js.map → chunk-JSDVRFAP.js.map} +0 -0
  134. /package/dist/{run-campaign-UADIM77S.js.map → run-campaign-IM26A6PD.js.map} +0 -0
@@ -0,0 +1,798 @@
1
+ import {
2
+ estimateCost,
3
+ isModelPriced,
4
+ resolveModelPricing
5
+ } from "./chunk-VI2UW6B6.js";
6
+ import {
7
+ ValidationError
8
+ } from "./chunk-ONWEPEDO.js";
9
+
10
+ // src/cost-ledger.ts
11
+ import { z } from "zod";
12
+ var CostCeilingReachedError = class extends ValidationError {
13
+ constructor(ceilingUsd, committedAndReservedUsd, requestedUsd, phase, actor) {
14
+ super(
15
+ `CostLedger: reserving ${requestedUsd} for '${actor}' during '${phase}' would exceed ceiling ${ceilingUsd} with ${committedAndReservedUsd} already committed or reserved`
16
+ );
17
+ }
18
+ };
19
+ var CostAccountingIncompleteError = class extends ValidationError {
20
+ };
21
+ var CostReservationExceededError = class extends ValidationError {
22
+ constructor(actor, actualUsd, maximumUsd) {
23
+ super(
24
+ `CostLedger: '${actor}' charged ${actualUsd}, exceeding its enforced maximum ${maximumUsd}`
25
+ );
26
+ }
27
+ };
28
+ var CostCallConflictError = class extends ValidationError {
29
+ callId;
30
+ receipt;
31
+ constructor(message, options = {}) {
32
+ super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
33
+ this.callId = options.callId;
34
+ this.receipt = options.receipt ? cloneReceipt(options.receipt) : void 0;
35
+ }
36
+ };
37
+ var CostLedgerPersistenceError = class extends ValidationError {
38
+ callId;
39
+ receipt;
40
+ constructor(cause, callId, receipt) {
41
+ super(
42
+ `CostLedger: failed to persist${callId ? ` call '${callId}'` : ""}: ${toError(cause).message}`,
43
+ { cause }
44
+ );
45
+ this.callId = callId;
46
+ this.receipt = receipt ? cloneReceipt(receipt) : void 0;
47
+ }
48
+ };
49
+ var CostReceiptCaptureError = class extends ValidationError {
50
+ callId;
51
+ receipt;
52
+ receiptError;
53
+ constructor(callId, cause, receiptError, receipt) {
54
+ super(`CostLedger: could not capture the provider receipt for call '${callId}'`, { cause });
55
+ this.callId = callId;
56
+ this.receipt = receipt ? cloneReceipt(receipt) : void 0;
57
+ this.receiptError = toError(receiptError);
58
+ }
59
+ };
60
+ var CostLedger = class {
61
+ records = /* @__PURE__ */ new Map();
62
+ activeCallIds = /* @__PURE__ */ new Set();
63
+ lateCallIds = /* @__PURE__ */ new Set();
64
+ completedTasks = 0;
65
+ revision = "memory";
66
+ costLimitPersisted = false;
67
+ costCeilingUsd;
68
+ persistence;
69
+ constructor(input) {
70
+ const options = typeof input === "number" ? { costCeilingUsd: input } : input ?? {};
71
+ this.persistence = options.persistence;
72
+ if (options.costCeilingUsd !== void 0) {
73
+ assertNonNegative(options.costCeilingUsd, "costCeilingUsd");
74
+ }
75
+ let persistedCostCeilingUsd;
76
+ if (this.persistence) {
77
+ let stored;
78
+ try {
79
+ stored = this.persistence.read();
80
+ } catch (cause) {
81
+ throw new CostLedgerPersistenceError(cause);
82
+ }
83
+ assertString(stored.revision, "persistence revision");
84
+ this.revision = stored.revision;
85
+ const restored = parseEvents(stored.events);
86
+ for (const record of restored.records) this.records.set(record.callId, record);
87
+ this.completedTasks = restored.completedTasks;
88
+ persistedCostCeilingUsd = restored.costCeilingUsd;
89
+ }
90
+ if (options.costCeilingUsd !== void 0 && persistedCostCeilingUsd !== void 0 && options.costCeilingUsd !== persistedCostCeilingUsd) {
91
+ throw new ValidationError(
92
+ `CostLedger: requested cost ceiling ${options.costCeilingUsd} does not match persisted ceiling ${persistedCostCeilingUsd}`
93
+ );
94
+ }
95
+ this.costCeilingUsd = persistedCostCeilingUsd ?? options.costCeilingUsd;
96
+ this.costLimitPersisted = !this.persistence || this.costCeilingUsd === void 0 || persistedCostCeilingUsd !== void 0;
97
+ if (this.costCeilingUsd !== void 0 && [...this.records.values()].some(
98
+ (record) => record.status === "pending" && record.maximumCostUsd === void 0
99
+ )) {
100
+ throw new ValidationError("CostLedger: capped event log contains an unbounded pending call");
101
+ }
102
+ if (options.receipts?.length) {
103
+ const imported = options.receipts.map(
104
+ (receipt, index) => parseImportedReceipt(receipt, `imported receipt ${index + 1}`)
105
+ );
106
+ this.ensureCostLimitPersisted();
107
+ for (const receipt of imported) this.appendRecord(receipt);
108
+ }
109
+ }
110
+ async runPaidCall(input) {
111
+ let callId;
112
+ let pending;
113
+ try {
114
+ callId = resolveCallId(input.callId);
115
+ validateAttribution(input);
116
+ if (input.signal?.aborted) {
117
+ return { succeeded: false, callId, error: abortError(input.signal) };
118
+ }
119
+ if (this.records.has(callId)) {
120
+ return {
121
+ succeeded: false,
122
+ callId,
123
+ error: new CostCallConflictError(`CostLedger: callId '${callId}' already exists`)
124
+ };
125
+ }
126
+ this.ensureCostLimitPersisted(callId);
127
+ const summary = this.summary();
128
+ if (summary.unresolvedCalls > 0) {
129
+ return {
130
+ succeeded: false,
131
+ callId,
132
+ error: new CostAccountingIncompleteError(
133
+ `CostLedger: ${summary.unresolvedCalls} unresolved call(s) must be reconciled before new paid work`
134
+ )
135
+ };
136
+ }
137
+ const maximumCostUsd = this.resolveMaximum(input.maximumCharge);
138
+ if (this.costCeilingUsd !== void 0 && this.hasIncompleteSettledCall()) {
139
+ return {
140
+ succeeded: false,
141
+ callId,
142
+ error: new CostAccountingIncompleteError(
143
+ `CostLedger: accounting is incomplete; refusing paid call '${input.actor}' during '${input.phase}'`
144
+ )
145
+ };
146
+ }
147
+ if (this.costCeilingUsd !== void 0) {
148
+ const committedAndReserved = summary.totalCostUsd + summary.reservedCostUsd;
149
+ if (committedAndReserved + maximumCostUsd > this.costCeilingUsd) {
150
+ return {
151
+ succeeded: false,
152
+ callId,
153
+ error: new CostCeilingReachedError(
154
+ this.costCeilingUsd,
155
+ committedAndReserved,
156
+ maximumCostUsd,
157
+ input.phase,
158
+ input.actor
159
+ )
160
+ };
161
+ }
162
+ }
163
+ pending = {
164
+ status: "pending",
165
+ callId,
166
+ channel: input.channel,
167
+ phase: input.phase,
168
+ actor: input.actor,
169
+ model: pendingModel(input),
170
+ ...maximumCostUsd === void 0 ? {} : { maximumCostUsd },
171
+ ...input.tags ? { tags: { ...input.tags } } : {},
172
+ timestamp: Date.now()
173
+ };
174
+ this.appendRecord(pending);
175
+ this.activeCallIds.add(callId);
176
+ } catch (error) {
177
+ return { succeeded: false, ...callId ? { callId } : {}, error: toError(error) };
178
+ }
179
+ try {
180
+ return await this.execute(input, pending);
181
+ } catch (error) {
182
+ return { succeeded: false, callId: pending.callId, error: toError(error) };
183
+ } finally {
184
+ if (!this.lateCallIds.has(pending.callId)) this.activeCallIds.delete(pending.callId);
185
+ }
186
+ }
187
+ /** Settle a call left pending by a crashed process after reconciling with the provider. */
188
+ reconcile(callId, observed, options = {}) {
189
+ const pending = [...this.records.values()].find(
190
+ (record) => record.callId === callId && record.status === "pending"
191
+ );
192
+ if (!pending) throw new CostCallConflictError(`CostLedger: no pending call '${callId}'`);
193
+ if (this.activeCallIds.has(callId)) {
194
+ throw new CostCallConflictError(`CostLedger: call '${callId}' is still active`);
195
+ }
196
+ this.ensureCostLimitPersisted(callId);
197
+ return this.commitReceipt(pending, observed, options.error);
198
+ }
199
+ list(filter) {
200
+ return [...this.records.values()].filter((record) => record.status === "settled").filter((receipt) => matches(receipt, filter)).map(cloneReceipt);
201
+ }
202
+ summary(filter) {
203
+ const records = [...this.records.values()].filter((record) => matches(record, filter));
204
+ const pending = records.filter(
205
+ (record) => record.status === "pending"
206
+ );
207
+ const receipts = records.filter((record) => record.status === "settled");
208
+ const byChannel = /* @__PURE__ */ new Map();
209
+ const unpriced = /* @__PURE__ */ new Set();
210
+ const incompleteReasons = pending.map(
211
+ (record) => `call '${record.callId}' for '${record.actor}' is pending`
212
+ );
213
+ let inputTokens = 0;
214
+ let outputTokens = 0;
215
+ let cachedTokens = 0;
216
+ let totalCostUsd = 0;
217
+ for (const receipt of receipts) {
218
+ inputTokens += receipt.inputTokens;
219
+ outputTokens += receipt.outputTokens;
220
+ cachedTokens += receipt.cachedTokens ?? 0;
221
+ totalCostUsd += receipt.costUsd;
222
+ if (receipt.costUnknown) {
223
+ unpriced.add(receipt.model);
224
+ incompleteReasons.push(
225
+ receipt.error ?? `cost unknown for '${receipt.actor}' using '${receipt.model}'`
226
+ );
227
+ }
228
+ if (receipt.usageUnknown) {
229
+ incompleteReasons.push(
230
+ `token usage unknown for '${receipt.actor}' using '${receipt.model}'`
231
+ );
232
+ }
233
+ if (receipt.maximumCostUsd !== void 0 && receipt.costUsd > receipt.maximumCostUsd) {
234
+ incompleteReasons.push(
235
+ `'${receipt.actor}' charged ${receipt.costUsd}, exceeding its enforced maximum ${receipt.maximumCostUsd}`
236
+ );
237
+ }
238
+ const rollup = byChannel.get(receipt.channel) ?? emptyRollup(receipt.channel);
239
+ rollup.calls += 1;
240
+ rollup.inputTokens += receipt.inputTokens;
241
+ rollup.outputTokens += receipt.outputTokens;
242
+ rollup.cachedTokens += receipt.cachedTokens ?? 0;
243
+ rollup.costUsd += receipt.costUsd;
244
+ if (receipt.costUnknown) rollup.unpricedCalls += 1;
245
+ if (receipt.usageUnknown) rollup.unknownUsageCalls += 1;
246
+ byChannel.set(receipt.channel, rollup);
247
+ }
248
+ return {
249
+ totalCalls: receipts.length,
250
+ pendingCalls: pending.length,
251
+ unresolvedCalls: pending.filter(
252
+ (record) => !this.activeCallIds.has(record.callId) || this.lateCallIds.has(record.callId)
253
+ ).length,
254
+ reservedCostUsd: pending.reduce((sum, record) => sum + (record.maximumCostUsd ?? 0), 0),
255
+ inputTokens,
256
+ outputTokens,
257
+ cachedTokens,
258
+ totalCostUsd,
259
+ byChannel: [...byChannel.values()].sort((a, b) => a.channel.localeCompare(b.channel)),
260
+ unpricedModels: [...unpriced].sort(),
261
+ fullyPriced: unpriced.size === 0,
262
+ usageComplete: receipts.every((receipt) => !receipt.usageUnknown),
263
+ accountingComplete: incompleteReasons.length === 0,
264
+ incompleteReasons: [...new Set(incompleteReasons)]
265
+ };
266
+ }
267
+ markCompleted(count = 1) {
268
+ if (!Number.isInteger(count) || count < 0) {
269
+ throw new ValidationError(
270
+ `CostLedger.markCompleted: count must be a non-negative integer, got ${count}`
271
+ );
272
+ }
273
+ if (count === 0) return;
274
+ this.ensureCostLimitPersisted();
275
+ this.appendEvent({ version: 1, completedTasks: count });
276
+ this.completedTasks += count;
277
+ }
278
+ costPerCompletedTask() {
279
+ return this.completedTasks === 0 ? null : this.summary().totalCostUsd / this.completedTasks;
280
+ }
281
+ async execute(input, pending) {
282
+ const signal = input.signal ?? new AbortController().signal;
283
+ if (signal.aborted) {
284
+ return this.commitOutcome(pending, abortError(signal), {
285
+ model: pending.model,
286
+ inputTokens: 0,
287
+ outputTokens: 0,
288
+ actualCostUsd: 0
289
+ });
290
+ }
291
+ const operation = Promise.resolve().then(() => input.execute(signal, pending.callId));
292
+ const settled = await settle(operation, signal);
293
+ if (settled.kind === "aborted") {
294
+ this.lateCallIds.add(pending.callId);
295
+ this.captureLateOutcome(input, pending, operation);
296
+ return paidFailure(pending.callId, abortError(signal));
297
+ }
298
+ if (settled.kind === "error") {
299
+ let observed;
300
+ try {
301
+ observed = input.receiptFromError?.(settled.error);
302
+ } catch (receiptError) {
303
+ return this.captureFailure(pending, settled.error, receiptError);
304
+ }
305
+ return this.commitOutcome(pending, settled.error, observed ?? unknownReceipt(pending.model));
306
+ }
307
+ try {
308
+ const receipt = this.commitReceipt(pending, input.receipt(settled.value));
309
+ if (receipt.maximumCostUsd !== void 0 && receipt.costUsd > receipt.maximumCostUsd) {
310
+ return paidFailure(
311
+ pending.callId,
312
+ new CostReservationExceededError(pending.actor, receipt.costUsd, receipt.maximumCostUsd),
313
+ receipt
314
+ );
315
+ }
316
+ return { succeeded: true, callId: pending.callId, value: settled.value, receipt };
317
+ } catch (receiptError) {
318
+ if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) {
319
+ return paidFailure(pending.callId, receiptError);
320
+ }
321
+ return this.captureFailure(pending, receiptError, receiptError);
322
+ }
323
+ }
324
+ captureLateOutcome(input, pending, operation) {
325
+ void operation.then(
326
+ (value) => {
327
+ if (this.records.get(pending.callId)?.status !== "pending") return;
328
+ try {
329
+ this.commitReceipt(pending, input.receipt(value));
330
+ } catch {
331
+ }
332
+ },
333
+ (cause) => {
334
+ if (this.records.get(pending.callId)?.status !== "pending") return;
335
+ const error = toError(cause);
336
+ try {
337
+ const observed = input.receiptFromError?.(error);
338
+ this.commitReceipt(pending, observed ?? unknownReceipt(pending.model), error.message);
339
+ } catch (receiptError) {
340
+ if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) {
341
+ return;
342
+ }
343
+ this.captureFailure(pending, error, receiptError);
344
+ }
345
+ }
346
+ ).finally(() => {
347
+ this.lateCallIds.delete(pending.callId);
348
+ this.activeCallIds.delete(pending.callId);
349
+ });
350
+ }
351
+ commitOutcome(pending, error, observed) {
352
+ try {
353
+ const receipt = this.commitReceipt(pending, observed, error.message);
354
+ return paidFailure(pending.callId, error, receipt);
355
+ } catch (receiptError) {
356
+ if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) {
357
+ return paidFailure(pending.callId, receiptError);
358
+ }
359
+ return this.captureFailure(pending, error, receiptError);
360
+ }
361
+ }
362
+ captureFailure(pending, cause, receiptError) {
363
+ try {
364
+ const receipt = this.commitReceipt(
365
+ pending,
366
+ unknownReceipt(pending.model),
367
+ toError(receiptError).message
368
+ );
369
+ return paidFailure(
370
+ pending.callId,
371
+ new CostReceiptCaptureError(pending.callId, cause, receiptError, receipt),
372
+ receipt
373
+ );
374
+ } catch (error) {
375
+ const typed = error instanceof Error ? error : toError(error);
376
+ return paidFailure(pending.callId, typed);
377
+ }
378
+ }
379
+ commitReceipt(pending, observed, error) {
380
+ const receipt = buildReceipt(pending, observed, error);
381
+ if (this.records.get(pending.callId)?.status !== "pending") {
382
+ throw new CostCallConflictError(`CostLedger: call '${pending.callId}' is not pending`);
383
+ }
384
+ this.appendRecord(receipt);
385
+ return cloneReceipt(receipt);
386
+ }
387
+ resolveMaximum(maximum) {
388
+ if (!maximum) {
389
+ if (this.costCeilingUsd !== void 0) {
390
+ throw new CostAccountingIncompleteError(
391
+ "CostLedger: capped paid calls require a hard maximumCharge before execution"
392
+ );
393
+ }
394
+ return void 0;
395
+ }
396
+ if ("externallyEnforcedMaximumUsd" in maximum) {
397
+ assertNonNegative(
398
+ maximum.externallyEnforcedMaximumUsd,
399
+ "maximumCharge.externallyEnforcedMaximumUsd"
400
+ );
401
+ return maximum.externallyEnforcedMaximumUsd;
402
+ }
403
+ const priced = costForUsage(maximum.model, maximum);
404
+ if (priced.costUnknown) {
405
+ if (this.costCeilingUsd !== void 0) {
406
+ throw new CostAccountingIncompleteError(
407
+ `CostLedger: cannot reserve unpriced model '${maximum.model}' in a capped run`
408
+ );
409
+ }
410
+ return void 0;
411
+ }
412
+ return priced.costUsd;
413
+ }
414
+ hasIncompleteSettledCall() {
415
+ return [...this.records.values()].some(
416
+ (record) => record.status === "settled" && (record.costUnknown || record.usageUnknown || record.maximumCostUsd !== void 0 && record.costUsd > record.maximumCostUsd)
417
+ );
418
+ }
419
+ appendRecord(record) {
420
+ const callId = record.callId;
421
+ const receipt = record.status === "settled" ? record : void 0;
422
+ validateTransition(this.records, record);
423
+ const event = {
424
+ version: 1,
425
+ record: cloneRecord(record)
426
+ };
427
+ this.appendEvent(event, callId, receipt);
428
+ this.records.set(callId, cloneRecord(record));
429
+ }
430
+ ensureCostLimitPersisted(callId) {
431
+ if (this.costLimitPersisted || this.costCeilingUsd === void 0) return;
432
+ this.appendEvent({ version: 1, costCeilingUsd: this.costCeilingUsd }, callId);
433
+ this.costLimitPersisted = true;
434
+ }
435
+ appendEvent(event, callId, receipt) {
436
+ try {
437
+ if (this.persistence) {
438
+ const nextRevision = this.persistence.append(this.revision, `${JSON.stringify(event)}
439
+ `);
440
+ if (nextRevision === void 0) {
441
+ throw new CostCallConflictError(
442
+ `CostLedger: persisted revision changed while writing call '${callId}'`,
443
+ { callId, receipt }
444
+ );
445
+ }
446
+ assertString(nextRevision, "persistence revision");
447
+ this.revision = nextRevision;
448
+ }
449
+ } catch (cause) {
450
+ if (cause instanceof CostCallConflictError) throw cause;
451
+ throw new CostLedgerPersistenceError(cause, callId, receipt);
452
+ }
453
+ }
454
+ };
455
+ function modelPriceKey(model) {
456
+ return isModelPriced(model) ? model : null;
457
+ }
458
+ function costForUsage(model, usage) {
459
+ assertUsage(usage);
460
+ if (!resolveModelPricing(model)) return { costUsd: 0, costUnknown: true };
461
+ return {
462
+ costUsd: estimateCost(usage.inputTokens + (usage.cachedTokens ?? 0), usage.outputTokens, model),
463
+ costUnknown: false
464
+ };
465
+ }
466
+ async function settle(promise, signal) {
467
+ return await new Promise((resolve) => {
468
+ let done = false;
469
+ const finish = (value) => {
470
+ if (done) return;
471
+ done = true;
472
+ signal.removeEventListener("abort", onAbort);
473
+ resolve(value);
474
+ };
475
+ const onAbort = () => finish({ kind: "aborted" });
476
+ signal.addEventListener("abort", onAbort, { once: true });
477
+ if (signal.aborted) onAbort();
478
+ promise.then(
479
+ (value) => finish({ kind: "value", value }),
480
+ (error) => finish({ kind: "error", error: toError(error) })
481
+ );
482
+ });
483
+ }
484
+ function buildReceipt(pending, observed, error) {
485
+ assertUsage(observed);
486
+ assertString(observed.model, "receipt.model");
487
+ const estimated = costForUsage(observed.model, observed);
488
+ const hasActual = observed.actualCostUsd !== void 0;
489
+ if (hasActual && observed.costUnknown === true) {
490
+ throw new ValidationError(
491
+ "CostLedger: a receipt cannot have both actualCostUsd and costUnknown=true"
492
+ );
493
+ }
494
+ if (hasActual) assertNonNegative(observed.actualCostUsd, "actualCostUsd");
495
+ const usageUnknown = observed.usageUnknown === true;
496
+ const costUnknown = observed.costUnknown === true || !hasActual && (usageUnknown || estimated.costUnknown);
497
+ const resolvedPricing = !hasActual && !costUnknown ? resolveModelPricing(observed.model) : null;
498
+ return parseReceipt(
499
+ {
500
+ status: "settled",
501
+ callId: pending.callId,
502
+ channel: pending.channel,
503
+ phase: pending.phase,
504
+ actor: pending.actor,
505
+ model: observed.model,
506
+ inputTokens: observed.inputTokens,
507
+ outputTokens: observed.outputTokens,
508
+ ...observed.cachedTokens === void 0 ? {} : { cachedTokens: observed.cachedTokens },
509
+ costUsd: costUnknown ? 0 : hasActual ? observed.actualCostUsd : estimated.costUsd,
510
+ costUnknown,
511
+ usageUnknown,
512
+ ...resolvedPricing ? {
513
+ pricing: {
514
+ inputUsdPerThousand: resolvedPricing.input,
515
+ outputUsdPerThousand: resolvedPricing.output
516
+ }
517
+ } : {},
518
+ ...hasActual ? { actualCostUsd: observed.actualCostUsd } : {},
519
+ ...pending.maximumCostUsd === void 0 ? {} : { maximumCostUsd: pending.maximumCostUsd },
520
+ ...error ? { error } : {},
521
+ ...pending.tags ? { tags: { ...pending.tags } } : {},
522
+ timestamp: pending.timestamp
523
+ },
524
+ "provider receipt"
525
+ );
526
+ }
527
+ var NonEmptyString = z.string().refine((value) => value.trim().length > 0, "must be non-empty");
528
+ var TokenCount = z.number().int().nonnegative().finite();
529
+ var NonNegative = z.number().nonnegative().finite();
530
+ var Positive = z.number().positive().finite();
531
+ var Tags = z.record(NonEmptyString, z.string());
532
+ var CostPricingSchema = z.strictObject({
533
+ inputUsdPerThousand: Positive,
534
+ outputUsdPerThousand: Positive
535
+ });
536
+ var CostCallBaseShape = {
537
+ callId: NonEmptyString,
538
+ channel: NonEmptyString,
539
+ phase: NonEmptyString,
540
+ actor: NonEmptyString,
541
+ model: NonEmptyString,
542
+ maximumCostUsd: NonNegative.optional(),
543
+ tags: Tags.optional(),
544
+ timestamp: NonNegative
545
+ };
546
+ var PendingCostCallSchema = z.strictObject({
547
+ status: z.literal("pending"),
548
+ ...CostCallBaseShape
549
+ });
550
+ var CostReceiptSchema = z.strictObject({
551
+ status: z.literal("settled"),
552
+ ...CostCallBaseShape,
553
+ inputTokens: TokenCount,
554
+ outputTokens: TokenCount,
555
+ cachedTokens: TokenCount.optional(),
556
+ costUsd: NonNegative,
557
+ costUnknown: z.boolean(),
558
+ usageUnknown: z.boolean().default(false),
559
+ pricing: CostPricingSchema.optional(),
560
+ actualCostUsd: NonNegative.optional(),
561
+ error: z.string().optional()
562
+ }).superRefine((receipt, ctx) => {
563
+ if (receipt.actualCostUsd !== void 0) {
564
+ if (receipt.costUnknown || receipt.costUsd !== receipt.actualCostUsd) {
565
+ ctx.addIssue({ code: "custom", message: "actual cost must be known and equal costUsd" });
566
+ }
567
+ if (receipt.pricing !== void 0) {
568
+ ctx.addIssue({ code: "custom", message: "actual cost must not include estimated pricing" });
569
+ }
570
+ return;
571
+ }
572
+ if (receipt.costUnknown) {
573
+ if (receipt.costUsd !== 0) {
574
+ ctx.addIssue({ code: "custom", message: "unknown cost must have costUsd 0" });
575
+ }
576
+ if (receipt.pricing !== void 0) {
577
+ ctx.addIssue({ code: "custom", message: "unknown cost must not include estimated pricing" });
578
+ }
579
+ return;
580
+ }
581
+ if (receipt.usageUnknown) {
582
+ ctx.addIssue({ code: "custom", message: "known estimated cost requires known usage" });
583
+ }
584
+ if (!receipt.pricing) {
585
+ ctx.addIssue({ code: "custom", message: "known estimated cost requires a pricing snapshot" });
586
+ return;
587
+ }
588
+ const expected = costFromPricing(receipt, receipt.pricing);
589
+ if (receipt.costUsd !== expected) {
590
+ ctx.addIssue({
591
+ code: "custom",
592
+ message: `estimated cost ${receipt.costUsd} does not match pricing snapshot ${expected}`
593
+ });
594
+ }
595
+ });
596
+ var CostLedgerEventSchema = z.union([
597
+ z.strictObject({
598
+ version: z.literal(1),
599
+ record: z.union([PendingCostCallSchema, CostReceiptSchema])
600
+ }),
601
+ z.strictObject({
602
+ version: z.literal(1),
603
+ completedTasks: TokenCount
604
+ }),
605
+ z.strictObject({
606
+ version: z.literal(1),
607
+ costCeilingUsd: NonNegative
608
+ })
609
+ ]);
610
+ function parseEvents(serialized) {
611
+ const records = /* @__PURE__ */ new Map();
612
+ let completedTasks = 0;
613
+ let costCeilingUsd;
614
+ let lineNumber = 0;
615
+ try {
616
+ for (const line of serialized.split("\n")) {
617
+ if (!line.trim()) continue;
618
+ lineNumber += 1;
619
+ const event = CostLedgerEventSchema.parse(JSON.parse(line));
620
+ if ("record" in event) {
621
+ validateTransition(records, event.record);
622
+ records.set(event.record.callId, cloneRecord(event.record));
623
+ } else if ("completedTasks" in event) {
624
+ completedTasks += event.completedTasks;
625
+ if (!Number.isSafeInteger(completedTasks)) {
626
+ throw new ValidationError("CostLedger: completed task count exceeds safe integer range");
627
+ }
628
+ } else {
629
+ if (costCeilingUsd !== void 0) {
630
+ throw new ValidationError("CostLedger: duplicate persisted cost ceiling");
631
+ }
632
+ costCeilingUsd = event.costCeilingUsd;
633
+ }
634
+ }
635
+ return {
636
+ records: [...records.values()],
637
+ completedTasks,
638
+ ...costCeilingUsd === void 0 ? {} : { costCeilingUsd }
639
+ };
640
+ } catch (cause) {
641
+ throw new ValidationError(
642
+ `CostLedger: invalid persisted event ${lineNumber || 1}: ${validationMessage(cause)}`,
643
+ { cause }
644
+ );
645
+ }
646
+ }
647
+ function validateTransition(records, record) {
648
+ const current = records.get(record.callId);
649
+ if (!current) return;
650
+ if (record.status === "pending" || current.status === "settled") {
651
+ throw new CostCallConflictError(`CostLedger: duplicate callId '${record.callId}'`);
652
+ }
653
+ if (!sameAttribution(current, record)) {
654
+ throw new ValidationError(`CostLedger: receipt attribution changed for call '${record.callId}'`);
655
+ }
656
+ }
657
+ function sameAttribution(before, after) {
658
+ return before.channel === after.channel && before.phase === after.phase && before.actor === after.actor && before.maximumCostUsd === after.maximumCostUsd && before.timestamp === after.timestamp && JSON.stringify(before.tags ?? {}) === JSON.stringify(after.tags ?? {});
659
+ }
660
+ function parseReceipt(value, path) {
661
+ try {
662
+ return CostReceiptSchema.parse(value);
663
+ } catch (cause) {
664
+ throw new ValidationError(`CostLedger: invalid ${path}: ${validationMessage(cause)}`, { cause });
665
+ }
666
+ }
667
+ function parseImportedReceipt(value, path) {
668
+ if (typeof value !== "object" || value === null) return parseReceipt(value, path);
669
+ const candidate = { ...value };
670
+ if (candidate.status === "settled" && candidate.actualCostUsd === void 0 && candidate.costUnknown === false && candidate.pricing === void 0 && typeof candidate.model === "string") {
671
+ const pricing = resolveModelPricing(candidate.model);
672
+ if (pricing) {
673
+ candidate.pricing = {
674
+ inputUsdPerThousand: pricing.input,
675
+ outputUsdPerThousand: pricing.output
676
+ };
677
+ }
678
+ }
679
+ return parseReceipt(candidate, path);
680
+ }
681
+ function validateAttribution(input) {
682
+ assertString(input.channel, "channel");
683
+ assertString(input.phase, "phase");
684
+ assertString(input.actor, "actor");
685
+ if (input.model !== void 0) assertString(input.model, "model");
686
+ if (input.tags !== void 0) {
687
+ const parsed = Tags.safeParse(input.tags);
688
+ if (!parsed.success)
689
+ throw new ValidationError(`CostLedger: invalid tags: ${parsed.error.message}`);
690
+ }
691
+ }
692
+ function matches(record, filter) {
693
+ if (!filter) return true;
694
+ if (filter.channel !== void 0 && record.channel !== filter.channel) return false;
695
+ if (filter.phase !== void 0 && record.phase !== filter.phase) return false;
696
+ return Object.entries(filter.tags ?? {}).every(([key, value]) => record.tags?.[key] === value);
697
+ }
698
+ function cloneRecord(record) {
699
+ return record.status === "settled" ? cloneReceipt(record) : { ...record, tags: record.tags ? { ...record.tags } : void 0 };
700
+ }
701
+ function cloneReceipt(receipt) {
702
+ return {
703
+ ...receipt,
704
+ tags: receipt.tags ? { ...receipt.tags } : void 0,
705
+ pricing: receipt.pricing ? { ...receipt.pricing } : void 0
706
+ };
707
+ }
708
+ function costFromPricing(usage, pricing) {
709
+ return (usage.inputTokens + (usage.cachedTokens ?? 0)) / 1e3 * pricing.inputUsdPerThousand + usage.outputTokens / 1e3 * pricing.outputUsdPerThousand;
710
+ }
711
+ function emptyRollup(channel) {
712
+ return {
713
+ channel,
714
+ calls: 0,
715
+ inputTokens: 0,
716
+ outputTokens: 0,
717
+ cachedTokens: 0,
718
+ costUsd: 0,
719
+ unpricedCalls: 0,
720
+ unknownUsageCalls: 0
721
+ };
722
+ }
723
+ function pendingModel(input) {
724
+ if (input.model) return input.model;
725
+ if (input.maximumCharge && "model" in input.maximumCharge) return input.maximumCharge.model;
726
+ return "unknown";
727
+ }
728
+ function unknownReceipt(model) {
729
+ return { model, inputTokens: 0, outputTokens: 0, costUnknown: true, usageUnknown: true };
730
+ }
731
+ function resolveCallId(input) {
732
+ if (input !== void 0) {
733
+ assertString(input, "callId");
734
+ return input;
735
+ }
736
+ if (typeof globalThis.crypto?.randomUUID !== "function") {
737
+ throw new ValidationError("CostLedger: crypto.randomUUID is required when callId is omitted");
738
+ }
739
+ return globalThis.crypto.randomUUID();
740
+ }
741
+ function abortError(signal) {
742
+ const reason = signal.reason;
743
+ if (reason instanceof Error) return reason;
744
+ const error = new Error("CostLedger: paid call aborted");
745
+ error.name = "AbortError";
746
+ return error;
747
+ }
748
+ function toError(value) {
749
+ return value instanceof Error ? value : new Error(String(value));
750
+ }
751
+ function paidFailure(callId, error, receipt) {
752
+ return {
753
+ succeeded: false,
754
+ callId,
755
+ error,
756
+ ...receipt ? { receipt: cloneReceipt(receipt) } : {}
757
+ };
758
+ }
759
+ function validationMessage(cause) {
760
+ return cause instanceof z.ZodError ? z.prettifyError(cause) : toError(cause).message;
761
+ }
762
+ function assertUsage(usage) {
763
+ assertTokenCount(usage.inputTokens, "inputTokens");
764
+ assertTokenCount(usage.outputTokens, "outputTokens");
765
+ if (usage.cachedTokens !== void 0) assertTokenCount(usage.cachedTokens, "cachedTokens");
766
+ }
767
+ function assertTokenCount(value, name) {
768
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
769
+ throw new ValidationError(
770
+ `CostLedger: ${name} must be a non-negative integer, got ${String(value)}`
771
+ );
772
+ }
773
+ }
774
+ function assertNonNegative(value, name) {
775
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
776
+ throw new ValidationError(
777
+ `CostLedger: ${name} must be a non-negative finite number, got ${String(value)}`
778
+ );
779
+ }
780
+ }
781
+ function assertString(value, name) {
782
+ if (typeof value !== "string" || value.trim().length === 0) {
783
+ throw new ValidationError(`CostLedger: ${name} must be a non-empty string`);
784
+ }
785
+ }
786
+
787
+ export {
788
+ CostCeilingReachedError,
789
+ CostAccountingIncompleteError,
790
+ CostReservationExceededError,
791
+ CostCallConflictError,
792
+ CostLedgerPersistenceError,
793
+ CostReceiptCaptureError,
794
+ CostLedger,
795
+ modelPriceKey,
796
+ costForUsage
797
+ };
798
+ //# sourceMappingURL=chunk-VCTY3W6J.js.map