@tangle-network/agent-runtime 0.208.0 → 0.209.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 (51) hide show
  1. package/dist/{activation-9wJniom2.js → activation-Cjhm5Tds.js} +2 -2
  2. package/dist/{activation-9wJniom2.js.map → activation-Cjhm5Tds.js.map} +1 -1
  3. package/dist/{activation-Dan50rKs.d.ts → activation-tFdeirCa.d.ts} +2 -2
  4. package/dist/agent.d.ts +1 -1
  5. package/dist/agent.js +2 -2
  6. package/dist/{coordination-driver-CqZGBqdt.js → coordination-driver-C-hPz1zy.js} +33 -6
  7. package/dist/coordination-driver-C-hPz1zy.js.map +1 -0
  8. package/dist/{delegate-CU7bWMuW.js → delegate-CB4CkJdV.js} +2 -2
  9. package/dist/{delegate-CU7bWMuW.js.map → delegate-CB4CkJdV.js.map} +1 -1
  10. package/dist/durable.d.ts +2 -2
  11. package/dist/durable.js +2 -2
  12. package/dist/{graph-B0cNCFmD.js → graph-Ud2-Ytos.js} +3 -3
  13. package/dist/{graph-B0cNCFmD.js.map → graph-Ud2-Ytos.js.map} +1 -1
  14. package/dist/{improvement-cycle-Bsm8TBDT.js → improvement-cycle-B7ia6rTj.js} +3 -3
  15. package/dist/{improvement-cycle-Bsm8TBDT.js.map → improvement-cycle-B7ia6rTj.js.map} +1 -1
  16. package/dist/{index-CHkf3b2C.d.ts → index-0p5DiJoy.d.ts} +3 -124
  17. package/dist/index.d.ts +4 -4
  18. package/dist/index.js +7 -7
  19. package/dist/intelligence.d.ts +2 -2
  20. package/dist/intelligence.js +3 -3
  21. package/dist/kernel.d.ts +3 -3
  22. package/dist/kernel.js +8 -8
  23. package/dist/{loop-runner-bin-CLxG15ta.js → loop-runner-bin-B30d3Yla.js} +3 -3
  24. package/dist/{loop-runner-bin-CLxG15ta.js.map → loop-runner-bin-B30d3Yla.js.map} +1 -1
  25. package/dist/{loop-runner-bin-BUVcdgNU.d.ts → loop-runner-bin-q-xGzEK9.d.ts} +3 -3
  26. package/dist/loop-runner-bin.d.ts +1 -1
  27. package/dist/loop-runner-bin.js +1 -1
  28. package/dist/mcp/bin.js +3 -3
  29. package/dist/mcp/index.d.ts +2 -2
  30. package/dist/mcp/index.js +4 -4
  31. package/dist/{provision-supervisor-CEMk0grI.js → provision-supervisor-C-nLaaQ_.js} +3 -3
  32. package/dist/{provision-supervisor-CEMk0grI.js.map → provision-supervisor-C-nLaaQ_.js.map} +1 -1
  33. package/dist/{redact-BMwd8IBm.js → redact-va_1mmv8.js} +290 -55
  34. package/dist/redact-va_1mmv8.js.map +1 -0
  35. package/dist/{runtime-ByeeZbA3.js → runtime-D0_b9wFH.js} +12 -8
  36. package/dist/{runtime-ByeeZbA3.js.map → runtime-D0_b9wFH.js.map} +1 -1
  37. package/dist/{server-Bl1F2Y3v.js → server-pq1x3C2i.js} +3 -3
  38. package/dist/{server-Bl1F2Y3v.js.map → server-pq1x3C2i.js.map} +1 -1
  39. package/dist/{stream-agent-turn-B3NfR48z.d.ts → stream-agent-turn-BaKYHbHg.d.ts} +161 -20
  40. package/dist/{structural-rollout-DsGWoyj4.js → structural-rollout-BaYBLUeI.js} +2 -2
  41. package/dist/{structural-rollout-DsGWoyj4.js.map → structural-rollout-BaYBLUeI.js.map} +1 -1
  42. package/dist/{supervise-D3r_gEyO.js → supervise-CMWpmY_1.js} +54 -16
  43. package/dist/supervise-CMWpmY_1.js.map +1 -0
  44. package/dist/testing.d.ts +2 -2
  45. package/dist/testing.js +12 -12
  46. package/dist/tui/index.d.ts +1 -1
  47. package/dist/tui/index.js +1 -1
  48. package/package.json +4 -4
  49. package/dist/coordination-driver-CqZGBqdt.js.map +0 -1
  50. package/dist/redact-BMwd8IBm.js.map +0 -1
  51. package/dist/supervise-D3r_gEyO.js.map +0 -1
@@ -4113,6 +4113,69 @@ function publicCreateOptions(create) {
4113
4113
  };
4114
4114
  }
4115
4115
  //#endregion
4116
+ //#region src/runtime/supervise/resources.ts
4117
+ /** Validate caller-owned names and units without assigning domain meaning to them. */
4118
+ function assertResources(resources, kind, label) {
4119
+ if (resources === void 0) return;
4120
+ if (resources === null || typeof resources !== "object" || Array.isArray(resources)) throw new Error(`${label} must be a resource map`);
4121
+ for (const [name, resource] of Object.entries(resources)) {
4122
+ if (!name.trim() || !resource || typeof resource !== "object" || typeof resource.unit !== "string" || !resource.unit.trim()) throw new Error(`${label}: resource names and units must be non-empty strings`);
4123
+ const value = kind === "limit" ? "limit" in resource ? resource.limit : void 0 : "amount" in resource ? resource.amount : void 0;
4124
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new Error(`${label}.${name}.${kind} must be a non-negative safe integer`);
4125
+ if (kind === "amount" && (!("known" in resource) || typeof resource.known !== "boolean")) throw new Error(`${label}.${name}.known must be a boolean`);
4126
+ }
4127
+ }
4128
+ /** Missing contributors are not usage claims; enforced omissions are marked before aggregation.
4129
+ * Overflow retains a safe lower bound and marks it unknown, preserving readable component receipts. */
4130
+ function addResourceSpend(...maps) {
4131
+ const totals = /* @__PURE__ */ new Map();
4132
+ for (const resources of maps) {
4133
+ assertResources(resources, "amount", "resource spend");
4134
+ for (const [name, resource] of Object.entries(resources ?? {})) {
4135
+ const prior = totals.get(name);
4136
+ if (prior && prior.unit !== resource.unit) throw new Error(`resource ${name}: unit mismatch`);
4137
+ const amount = (prior?.amount ?? 0) + resource.amount;
4138
+ totals.set(name, {
4139
+ unit: resource.unit,
4140
+ amount: Math.min(amount, Number.MAX_SAFE_INTEGER),
4141
+ known: (prior?.known ?? true) && resource.known && Number.isSafeInteger(amount)
4142
+ });
4143
+ }
4144
+ }
4145
+ return totals.size ? { resources: Object.fromEntries(totals) } : {};
4146
+ }
4147
+ /** A terminal omission under an enforced ceiling is unknown, never measured zero. */
4148
+ function withBudgetResources(spend, budget, notStarted = false) {
4149
+ const resources = new Map(Object.entries(addResourceSpend(spend.resources).resources ?? {}));
4150
+ for (const [name, limit] of Object.entries(budget.resources ?? {})) {
4151
+ const observed = resources.get(name);
4152
+ if (observed && observed.unit !== limit.unit) throw new Error(`resource ${name}: unit mismatch`);
4153
+ if (!observed) resources.set(name, {
4154
+ unit: limit.unit,
4155
+ amount: 0,
4156
+ known: notStarted
4157
+ });
4158
+ }
4159
+ return resources.size ? {
4160
+ ...spend,
4161
+ resources: Object.fromEntries(resources)
4162
+ } : spend;
4163
+ }
4164
+ /** Stream increments and a terminal total describe the same work; never sum them twice. */
4165
+ function resourceTelemetry(streamed, terminal) {
4166
+ const resources = new Map(Object.entries(addResourceSpend(terminal.resources).resources ?? {}));
4167
+ for (const [name, value] of Object.entries(streamed.resources ?? {})) {
4168
+ const other = resources.get(name);
4169
+ if (other && other.unit !== value.unit) throw new Error(`resource ${name}: unit mismatch`);
4170
+ resources.set(name, {
4171
+ ...value,
4172
+ amount: Math.max(value.amount, other?.amount ?? 0),
4173
+ known: value.known && (other?.known ?? true) && (other === void 0 || other.amount === value.amount)
4174
+ });
4175
+ }
4176
+ return addResourceSpend(Object.fromEntries(resources));
4177
+ }
4178
+ //#endregion
4116
4179
  //#region src/sanitize.ts
4117
4180
  /** Strip PII and large blobs from a `KnowledgeReadinessReport` for safe telemetry emission. @stable */
4118
4181
  function sanitizeKnowledgeReadinessReport(report, options = {}) {
@@ -6150,30 +6213,23 @@ function retainedTurnMaterial(input, contextTransfer) {
6150
6213
  }
6151
6214
  //#endregion
6152
6215
  //#region src/runtime/retained-run-start.ts
6153
- const MAX_RETAINED_IDENTITY_BYTES = 128;
6154
6216
  /**
6155
6217
  * Mint deterministic dispatch coordinates from the two caller-supplied keys.
6156
6218
  * The same `(idempotencyKey, turnId)` pair yields the same coordinates in
6157
6219
  * every process, so a pre-dispatch admission record always names the exact
6158
- * session and execution the dispatch will request. Short inputs keep a
6159
- * readable URL-encoded identity. Long inputs use a full SHA-256 digest so
6160
- * provider storage layers never receive an overlong composite identifier.
6220
+ * session and execution the dispatch will request. A full SHA-256 digest
6221
+ * keeps provider session identifiers bounded and safe for workspace paths.
6161
6222
  */
6162
6223
  function mintRetainedIdentity(idempotencyKey, turnId) {
6163
- const base = `${encodeURIComponent(idempotencyKey)}:${encodeURIComponent(turnId)}`;
6164
6224
  const digest = canonicalCandidateDigest({
6165
6225
  kind: "retained-identity.v1",
6166
- base
6226
+ base: `${encodeURIComponent(idempotencyKey)}:${encodeURIComponent(turnId)}`
6167
6227
  }).slice(7);
6168
6228
  return {
6169
- sessionId: boundedRetainedIdentity("retained-session", base, digest),
6170
- executionId: boundedRetainedIdentity("retained-execution", base, digest)
6229
+ sessionId: `retained-session-${digest}`,
6230
+ executionId: `retained-execution-${digest}`
6171
6231
  };
6172
6232
  }
6173
- function boundedRetainedIdentity(prefix, base, digest) {
6174
- const readable = `${prefix}:${base}`;
6175
- return readable.length <= MAX_RETAINED_IDENTITY_BYTES ? readable : `${prefix}:${digest}`;
6176
- }
6177
6233
  /**
6178
6234
  * Dispatch one detached, replayable run and return only after exact durable
6179
6235
  * coordinates are confirmed by the provider and persisted by the caller.
@@ -6188,12 +6244,14 @@ function boundedRetainedIdentity(prefix, base, digest) {
6188
6244
  async function startRetainedRun(options) {
6189
6245
  assertStableText(options.environment.idempotencyKey, "environment idempotency key");
6190
6246
  assertStableText(options.turn.turnId, "turn idempotency key");
6191
- if (options.identity !== void 0) {
6192
- assertStableText(options.identity.sessionId, "retained session id");
6193
- assertStableText(options.identity.executionId, "retained execution id");
6194
- }
6195
6247
  if (typeof options.onAdmission !== "function") throw new Error("startRetainedRun requires an awaited onAdmission durability hook");
6196
- const identity = options.identity ?? mintRetainedIdentity(options.environment.idempotencyKey, options.turn.turnId);
6248
+ const { sessionId, executionId } = options.identity ?? options.intent ?? mintRetainedIdentity(options.environment.idempotencyKey, options.turn.turnId);
6249
+ const identity = {
6250
+ sessionId,
6251
+ executionId
6252
+ };
6253
+ assertStableText(identity.sessionId, "retained session id");
6254
+ assertStableText(identity.executionId, "retained execution id");
6197
6255
  const contextTransfer = retainedContextTransfer(options.turn.contextTransfer);
6198
6256
  if (!options.provider.get) throw new Error(`provider "${options.provider.name}" cannot reconstruct an environment by id`);
6199
6257
  const intent = retainedRunIntent(options, identity, contextTransfer);
@@ -6422,7 +6480,9 @@ async function recoverRetainedRun(options) {
6422
6480
  }
6423
6481
  /** Check public replay material before reconnecting an already-dispatched execution. */
6424
6482
  function assertRetainedRunReplayMaterial(provider, replay, admission) {
6425
- const identity = replay.identity ?? mintRetainedIdentity(replay.environment.idempotencyKey, replay.turn.turnId);
6483
+ const identity = replay.identity ?? admission;
6484
+ assertStableText(identity.sessionId, "retained session id");
6485
+ assertStableText(identity.executionId, "retained execution id");
6426
6486
  assertExactRetainedRunIntent(admission, retainedRunIntent({
6427
6487
  ...replay,
6428
6488
  provider
@@ -6849,6 +6909,7 @@ function unmeteredSpend(ms) {
6849
6909
  /** Copy a conserved spend without dropping a completeness marker or the catalog-priced part. */
6850
6910
  function cloneSpend(spend) {
6851
6911
  return {
6912
+ ...addResourceSpend(spend.resources),
6852
6913
  iterations: spend.iterations,
6853
6914
  tokens: cloneTokenUsage(spend.tokens),
6854
6915
  ...spend.tokensKnown === false ? { tokensKnown: false } : {},
@@ -6868,6 +6929,7 @@ function addSpend(a, b) {
6868
6929
  const tokens = cloneTokenUsage(a.tokens);
6869
6930
  addTokenUsage(tokens, b.tokens);
6870
6931
  return {
6932
+ ...addResourceSpend(a.resources, b.resources),
6871
6933
  iterations: a.iterations + b.iterations,
6872
6934
  tokens,
6873
6935
  ...a.tokensKnown === false || b.tokensKnown === false ? { tokensKnown: false } : {},
@@ -9009,7 +9071,7 @@ async function routerChatWithUsage(cfg, messages, opts) {
9009
9071
  }
9010
9072
  function parseChatResult(json, model, transportAttempts) {
9011
9073
  const data = json;
9012
- const { usage, costUsd, costProvenance, billedCostUsd, cache } = meterTurn(data.usage, model);
9074
+ const { usage, resources, costUsd, costProvenance, billedCostUsd, cache } = meterTurn(data.usage, model, transportAttempts);
9013
9075
  const msg = data.choices?.[0]?.message;
9014
9076
  if (!msg) throw new ValidationError$1("router completion: no choices[0].message");
9015
9077
  const { content, reasoning } = splitReasoning(msg?.content ?? "", msg?.reasoning ?? msg?.reasoning_content);
@@ -9019,6 +9081,7 @@ function parseChatResult(json, model, transportAttempts) {
9019
9081
  ...reportedModel(data.model) ? { model: reportedModel(data.model) } : {},
9020
9082
  ...reasoning ? { reasoning } : {},
9021
9083
  ...usage ? { usage } : {},
9084
+ ...resources ? { resources } : {},
9022
9085
  ...costUsd !== void 0 ? { costUsd } : {},
9023
9086
  ...costProvenance ? { costProvenance } : {},
9024
9087
  ...billedCostUsd !== void 0 ? { billedCostUsd } : {},
@@ -9085,13 +9148,14 @@ async function routerChatWithTools(cfg, messages, tools, opts) {
9085
9148
  name: tc.function?.name ?? "",
9086
9149
  arguments: tc.function?.arguments ?? "{}"
9087
9150
  }));
9088
- const { usage, costUsd, costProvenance, billedCostUsd, cache } = meterTurn(data.usage, cfg.model);
9151
+ const { usage, resources, costUsd, costProvenance, billedCostUsd, cache } = meterTurn(data.usage, cfg.model, transportAttempts);
9089
9152
  return {
9090
9153
  content: msg?.content ?? null,
9091
9154
  toolCalls,
9092
9155
  transportAttempts,
9093
9156
  ...reportedModel(data.model) ? { model: reportedModel(data.model) } : {},
9094
9157
  ...usage ? { usage } : {},
9158
+ ...resources ? { resources } : {},
9095
9159
  ...costUsd !== void 0 ? { costUsd } : {},
9096
9160
  ...costProvenance ? { costProvenance } : {},
9097
9161
  ...billedCostUsd !== void 0 ? { billedCostUsd } : {},
@@ -9141,7 +9205,12 @@ function providerRequestExtras(extraBody, reservedFields) {
9141
9205
  * fabricated 0: a phantom 0 reads as a free call to the conserved budget pool, which would then
9142
9206
  * over-spend. Shared by the buffered and streamed transports so both meter identically.
9143
9207
  */
9144
- function meterTurn(raw, model) {
9208
+ function meterTurn(raw, model, transportAttempts) {
9209
+ let resources = addResourceSpend(raw?.resources).resources;
9210
+ if (transportAttempts > 1 && resources !== void 0) resources = Object.fromEntries(Object.entries(resources).map(([name, value]) => [name, {
9211
+ ...value,
9212
+ known: false
9213
+ }]));
9145
9214
  const reasoning = providerReasoningTokens(raw);
9146
9215
  const usage = raw && typeof raw.prompt_tokens === "number" && typeof raw.completion_tokens === "number" ? {
9147
9216
  input: raw.prompt_tokens,
@@ -9151,6 +9220,7 @@ function meterTurn(raw, model) {
9151
9220
  const cache = readPromptCache(raw);
9152
9221
  const billedCostUsd = providerBilledCost(raw);
9153
9222
  if (!usage) return {
9223
+ ...resources ? { resources } : {},
9154
9224
  ...billedCostUsd !== void 0 ? { billedCostUsd } : {},
9155
9225
  ...cache ? { cache } : {}
9156
9226
  };
@@ -9158,6 +9228,7 @@ function meterTurn(raw, model) {
9158
9228
  const costUsd = localEstimate !== void 0 && cache?.readSavingsUsd !== void 0 ? Math.max(0, localEstimate - cache.readSavingsUsd) : localEstimate;
9159
9229
  return {
9160
9230
  usage,
9231
+ ...resources ? { resources } : {},
9161
9232
  ...costUsd !== void 0 ? {
9162
9233
  costUsd,
9163
9234
  costProvenance: "catalog-estimate"
@@ -9273,7 +9344,7 @@ async function streamRouterChatWithTools(cfg, messages, tools, opts) {
9273
9344
  name: call.name ?? "",
9274
9345
  arguments: call.arguments || "{}"
9275
9346
  }));
9276
- const { usage, costUsd, costProvenance, billedCostUsd, cache } = meterTurn(rawUsage, cfg.model);
9347
+ const { usage, resources, costUsd, costProvenance, billedCostUsd, cache } = meterTurn(rawUsage, cfg.model, transportAttempts);
9277
9348
  return {
9278
9349
  content: sawContent ? split.content : null,
9279
9350
  toolCalls,
@@ -9282,6 +9353,7 @@ async function streamRouterChatWithTools(cfg, messages, tools, opts) {
9282
9353
  ...split.reasoning ? { reasoning: split.reasoning } : {},
9283
9354
  ...finishReason !== void 0 ? { finishReason } : {},
9284
9355
  ...usage ? { usage } : {},
9356
+ ...resources ? { resources } : {},
9285
9357
  ...costUsd !== void 0 ? { costUsd } : {},
9286
9358
  ...costProvenance ? { costProvenance } : {},
9287
9359
  ...billedCostUsd !== void 0 ? { billedCostUsd } : {},
@@ -9527,6 +9599,7 @@ function routerBrain(cfg, opts = {}) {
9527
9599
  content: result.content,
9528
9600
  toolCalls: result.toolCalls,
9529
9601
  ...result.usage !== void 0 ? { usage: result.usage } : {},
9602
+ ...addResourceSpend(result.resources),
9530
9603
  ...result.usageUnknown === true ? { usageUnknown: true } : {},
9531
9604
  ...result.model !== void 0 ? { model: result.model } : {},
9532
9605
  ...result.cache !== void 0 ? { promptCache: Object.freeze({ ...result.cache }) } : {},
@@ -10967,8 +11040,8 @@ function priceUnreceiptedWork(work) {
10967
11040
  /**
10968
11041
  *
10969
11042
  * The conserved budget reservation pool — the invariant the whole instrument
10970
- * rests on (critique M5/B3). One root `Budget` becomes a conserved pool of three
10971
- * quantities (tokens, usd, iterations) plus an absolute deadline. Children reserve
11043
+ * rests on (critique M5/B3). One root `Budget` becomes a conserved pool of standard
11044
+ * quantities (tokens, usd, iterations) and caller-named resources plus an absolute deadline. Children reserve
10972
11045
  * atomically at spawn and reconcile at settle:
10973
11046
  *
10974
11047
  * total ≡ free + reserved + committed (for every known quantity)
@@ -10992,16 +11065,11 @@ function priceUnreceiptedWork(work) {
10992
11065
  * it trusts a reported `input`: the token channel is an accounting unit, not a trust boundary
10993
11066
  * against a provider that misreports its own usage.
10994
11067
  *
10995
- * THE POOL RESERVES AGAINST THREE QUANTITIES AND NO MORE. `Spend.boxMinutes` — platform box wall
10996
- * time is reported and summed, and it is never reserved, never committed, and never refunded.
10997
- * The reason is the conservation law above: a reservation is only meaningful against a ceiling a
10998
- * caller declared and a measurement the pool can trust, and box minutes have neither. `Budget`
10999
- * declares no box ceiling, and the number the sandbox executor reports is DERIVED from the box
11000
- * lifetime it watched (`boxMinutesProvenance: 'estimated'`), not a platform receipt. Reserving
11001
- * against an estimate would let a derived number refuse real work, which is the same defect as a
11002
- * gate that cannot fire, inverted. When the platform reports minutes itself
11003
- * (`boxMinutesProvenance: 'observed'`) and a `Budget` gains a box ceiling, the channel becomes a
11004
- * conserved quantity; until both hold, it is evidence only.
11068
+ * Named resources require explicit units, ceilings, and complete executor measurements.
11069
+ * Missing or unknown enforced measurements close admission for that resource.
11070
+ * The pool trusts those receipts; it does not independently meter external systems.
11071
+ * `Spend.boxMinutes` remains evidence only and has no implicit resource ceiling.
11072
+ * Callers must not present estimated box lifetime as a measured resource receipt.
11005
11073
  *
11006
11074
  * Pure and deterministic: the run's start instant is supplied, there is no I/O, and no
11007
11075
  * wall-clock or RNG read. A `reserve`/`reconcile` ticket is single-use (fail-loud on double or
@@ -11033,12 +11101,14 @@ function assertValidBudget(budget, label = "budget") {
11033
11101
  const finiteNonNegative = (value, field) => {
11034
11102
  if (!Number.isFinite(value) || value < 0) throw new Error(`${label}.${field} must be a non-negative finite number`);
11035
11103
  };
11104
+ assertResources(budget.resources, "limit", `${label}.resources`);
11036
11105
  safeInteger(budget.maxIterations, "maxIterations");
11037
11106
  safeInteger(budget.maxTokens, "maxTokens");
11038
11107
  if (budget.maxUsd !== void 0) finiteNonNegative(budget.maxUsd, "maxUsd");
11039
11108
  if (budget.deadlineMs !== void 0) finiteNonNegative(budget.deadlineMs, "deadlineMs");
11040
11109
  }
11041
11110
  function assertValidSpend(spend, label) {
11111
+ assertResources(spend.resources, "amount", `${label}.resources`);
11042
11112
  if (!Number.isSafeInteger(spend.iterations) || spend.iterations < 0) throw new Error(`${label}.iterations must be a non-negative safe integer`);
11043
11113
  for (const [field, value] of [
11044
11114
  ["input", spend.tokens.input],
@@ -11090,6 +11160,14 @@ function newUsageTotals() {
11090
11160
  * `iteration` advances the iteration count.
11091
11161
  */
11092
11162
  function meterUsageEvent(totals, ev) {
11163
+ if (ev.kind === "resource") {
11164
+ totals.resources = addResourceSpend(totals.resources, Object.fromEntries([[ev.name, {
11165
+ unit: ev.unit,
11166
+ amount: ev.amount,
11167
+ known: ev.known
11168
+ }]])).resources;
11169
+ return;
11170
+ }
11093
11171
  if (ev.kind === "tokens") {
11094
11172
  addTokenUsage(totals.tokens, ev);
11095
11173
  if (ev.tokensKnown === false) totals.tokensKnown = false;
@@ -11111,6 +11189,7 @@ function meterUsageEvent(totals, ev) {
11111
11189
  * read wall-clock. */
11112
11190
  function spendFromUsageTotals(totals) {
11113
11191
  return {
11192
+ ...addResourceSpend(totals.resources),
11114
11193
  iterations: totals.iterations,
11115
11194
  tokens: totals.tokens,
11116
11195
  ...totals.tokensKnown ? {} : { tokensKnown: false },
@@ -11153,10 +11232,56 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11153
11232
  let reservedIterations = 0;
11154
11233
  let committedIterations = 0;
11155
11234
  const absoluteDeadlineMs = root.deadlineMs === void 0 ? 0 : runStartedAtMs + root.deadlineMs;
11235
+ const resources = new Map(Object.entries(root.resources ?? {}).map(([name, value]) => [name, {
11236
+ ...value,
11237
+ remaining: value.limit,
11238
+ reserved: 0,
11239
+ committed: 0,
11240
+ known: true
11241
+ }]));
11242
+ function validateResourceUnits(spend) {
11243
+ for (const [name, value] of Object.entries(spend.resources ?? {})) {
11244
+ const limit = resources.get(name);
11245
+ if (limit && limit.unit !== value.unit) throw new Error(`resource ${name}: unit mismatch`);
11246
+ }
11247
+ }
11248
+ function commitResources(spend, reserved = {}, requireAll = true) {
11249
+ let violation;
11250
+ for (const [name, state] of resources) {
11251
+ const value = spend.resources?.[name];
11252
+ if (!requireAll && value === void 0) continue;
11253
+ const allocation = reserved?.[name]?.limit ?? 0;
11254
+ const amount = value?.amount ?? 0;
11255
+ state.reserved -= allocation;
11256
+ const committed = state.committed + amount;
11257
+ const overflow = !Number.isSafeInteger(committed);
11258
+ state.committed = Math.min(committed, Number.MAX_SAFE_INTEGER);
11259
+ state.remaining = state.limit - state.reserved - state.committed;
11260
+ if (value?.known !== true || overflow) {
11261
+ state.known = false;
11262
+ const retained = Math.max(0, state.remaining);
11263
+ state.committed += retained;
11264
+ state.remaining -= retained;
11265
+ violation ??= overflow ? `resource ${name}: amount overflow under an enforced limit` : `resource ${name}: unknown usage under an enforced limit`;
11266
+ } else if (reserved?.[name] && amount > allocation) violation ??= `resource ${name}: spent ${amount} > reserved ${allocation}`;
11267
+ else if (state.remaining < 0) violation ??= `resource ${name}: exceeded root limit ${state.limit}`;
11268
+ }
11269
+ return violation;
11270
+ }
11156
11271
  let nextTicketId = 0;
11157
11272
  const open = /* @__PURE__ */ new Set();
11158
11273
  function reserve(b) {
11159
11274
  assertValidBudget(b, "reservation budget");
11275
+ for (const [name, state] of resources) {
11276
+ const wanted = b.resources?.[name];
11277
+ if (!wanted) throw new ValidationError$1(`resource ${name}: child must declare its limit`);
11278
+ if (wanted.unit !== state.unit) throw new ValidationError$1(`resource ${name}: unit mismatch`);
11279
+ if (!state.known || wanted.limit > state.remaining) return {
11280
+ ok: false,
11281
+ reason: "budget-exhausted"
11282
+ };
11283
+ }
11284
+ for (const name of Object.keys(b.resources ?? {})) if (!resources.has(name)) throw new ValidationError$1(`resource ${name}: root must declare its limit`);
11160
11285
  const wantTokens = b.maxTokens;
11161
11286
  const wantUsd = b.maxUsd ?? 0;
11162
11287
  const wantIterations = b.maxIterations;
@@ -11180,6 +11305,11 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11180
11305
  ok: false,
11181
11306
  reason: "budget-exhausted"
11182
11307
  };
11308
+ for (const [name, state] of resources) {
11309
+ const amount = b.resources[name].limit;
11310
+ state.remaining -= amount;
11311
+ state.reserved += amount;
11312
+ }
11183
11313
  freeTokens -= wantTokens;
11184
11314
  reservedTokens += wantTokens;
11185
11315
  freeIterations -= wantIterations;
@@ -11195,6 +11325,7 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11195
11325
  ticket: {
11196
11326
  id,
11197
11327
  reserved: {
11328
+ ...b.resources === void 0 ? {} : { resources: Object.fromEntries(Object.entries(b.resources).map(([name, value]) => [name, { ...value }])) },
11198
11329
  tokens: wantTokens,
11199
11330
  usd: wantUsd,
11200
11331
  iterations: wantIterations,
@@ -11206,6 +11337,7 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11206
11337
  function reconcile(ticket, spent) {
11207
11338
  if (!open.has(ticket.id)) throw new Error(`budget pool: reconcile of unknown or already-settled ticket ${ticket.id}`);
11208
11339
  assertValidSpend(spent, `budget pool ticket ${ticket.id} spend`);
11340
+ validateResourceUnits(spent);
11209
11341
  const { tokens: rTokens, usd: rUsd, iterations: rIterations } = ticket.reserved;
11210
11342
  const unknownUnderCap = usdCapped && spent.usdKnown === false;
11211
11343
  const spentTokens = chargedTokens(spent.tokens);
@@ -11233,10 +11365,13 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11233
11365
  freeUsd = 0;
11234
11366
  } else freeUsd += rUsd - spent.usd;
11235
11367
  } else committedUsd += spent.usd;
11368
+ const resourceViolation = commitResources(spent, ticket.reserved.resources);
11369
+ violation ??= resourceViolation;
11236
11370
  if (violation !== void 0) throw new Error(`budget pool: ${violation}`);
11237
11371
  }
11238
- function observe(spend) {
11372
+ function observe(spend, options = {}) {
11239
11373
  assertValidSpend(spend, "observed spend");
11374
+ validateResourceUnits(spend);
11240
11375
  if (usdCapped && spend.usdKnown === false) throw new ValidationError$1("budget pool: cannot observe unknown dollar cost under a dollar-capped budget");
11241
11376
  if (spend.tokensKnown === false) tokensTainted = true;
11242
11377
  if (!hasCompleteCacheBreakdown(spend.tokens)) cacheBreakdownTainted = true;
@@ -11248,9 +11383,12 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11248
11383
  committedIterations += spend.iterations;
11249
11384
  committedUsd += spend.usd;
11250
11385
  if (usdCapped) freeUsd -= spend.usd;
11386
+ const violation = commitResources(spend, {}, !options.partial);
11387
+ if (violation) throw new ValidationError$1(`budget pool: ${violation}`);
11251
11388
  }
11252
11389
  function readout() {
11253
11390
  return {
11391
+ ...resources.size ? { resources: Object.fromEntries([...resources].map(([name, value]) => [name, { ...value }])) } : {},
11254
11392
  tokensLeft: freeTokens,
11255
11393
  tokensKnown: !tokensTainted,
11256
11394
  cacheBreakdownKnown: !cacheBreakdownTainted,
@@ -11267,6 +11405,8 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11267
11405
  }
11268
11406
  if (restore.committed !== void 0) {
11269
11407
  assertValidSpend(restore.committed, "budget restore committed");
11408
+ validateResourceUnits(restore.committed);
11409
+ commitResources(restore.committed);
11270
11410
  const committed = restore.committed;
11271
11411
  if (committed.tokensKnown === false) tokensTainted = true;
11272
11412
  if (!hasCompleteCacheBreakdown(committed.tokens)) cacheBreakdownTainted = true;
@@ -11288,6 +11428,12 @@ function createBudgetPool(root, runStartedAtMs, restore = {}) {
11288
11428
  }
11289
11429
  for (const [index, uncertain] of (restore.uncertainReservations ?? []).entries()) {
11290
11430
  assertValidBudget(uncertain, `budget restore uncertainReservations[${index}]`);
11431
+ commitResources(withBudgetResources({
11432
+ iterations: 0,
11433
+ tokens: zeroTokenUsage(),
11434
+ usd: 0,
11435
+ ms: 0
11436
+ }, root));
11291
11437
  tokensTainted = true;
11292
11438
  usdMeasured = false;
11293
11439
  freeTokens -= uncertain.maxTokens;
@@ -13103,6 +13249,7 @@ function readWorkerProgress(scope, executor, now, stallAfterMs = DEFAULT_STALL_A
13103
13249
  const idleMs = Math.max(0, now - lastActivityAt);
13104
13250
  const note = executor?.note;
13105
13251
  return {
13252
+ ...addResourceSpend(scope.resources),
13106
13253
  id: scope.id,
13107
13254
  status: scope.status,
13108
13255
  live,
@@ -14270,6 +14417,7 @@ const routerInlineExecutor = (spec, ctx) => {
14270
14417
  }));
14271
14418
  if (r.model !== void 0) recordRuntimeOwnedProviderModel(executor, r.model);
14272
14419
  const spent = {
14420
+ ...addResourceSpend(r.resources),
14273
14421
  iterations: 1,
14274
14422
  tokens: r.usage ? cloneTokenUsage({
14275
14423
  input: r.usage.input,
@@ -14420,6 +14568,7 @@ const routerToolsInlineExecutor = (spec, ctx) => {
14420
14568
  let billedUsd = 0;
14421
14569
  let usdKnown = true;
14422
14570
  let turns = 0;
14571
+ let resources;
14423
14572
  let transportAttempts = 0;
14424
14573
  let observedModel;
14425
14574
  let reasoningTokens = 0;
@@ -14468,6 +14617,10 @@ const routerToolsInlineExecutor = (spec, ctx) => {
14468
14617
  cleanup();
14469
14618
  if (e instanceof DOMException && e.name === "AbortError" && interruptSig.aborted && !signal.aborted && !controller.signal.aborted) {
14470
14619
  turns += 1;
14620
+ resources = addResourceSpend(Object.fromEntries(Object.entries(resources ?? {}).map(([name, value]) => [name, {
14621
+ ...value,
14622
+ known: false
14623
+ }]))).resources;
14471
14624
  transportAttempts += routerTransportAttemptsFromError(e) ?? 1;
14472
14625
  tokensKnown = false;
14473
14626
  usdKnown = false;
@@ -14478,6 +14631,12 @@ const routerToolsInlineExecutor = (spec, ctx) => {
14478
14631
  }
14479
14632
  cleanup();
14480
14633
  turns += 1;
14634
+ const priorResources = resources;
14635
+ resources = addResourceSpend(resources, res.resources).resources;
14636
+ resources = addResourceSpend(Object.fromEntries(Object.entries(resources ?? {}).map(([name, value]) => [name, {
14637
+ ...value,
14638
+ known: value.known && res.resources?.[name] !== void 0 && (turns === 1 || priorResources?.[name] !== void 0)
14639
+ }]))).resources;
14481
14640
  transportAttempts += res.transportAttempts;
14482
14641
  if (res.model !== void 0) recordRuntimeOwnedProviderModel(executor, res.model);
14483
14642
  assertObservedRouterModel(res.model, model, "routerToolsInlineExecutor");
@@ -14587,6 +14746,7 @@ const routerToolsInlineExecutor = (spec, ctx) => {
14587
14746
  }
14588
14747
  const estimatedUsd = isModelPriced(model) ? estimateCost(tokens.input, tokens.output, model) : void 0;
14589
14748
  const spent = {
14749
+ ...addResourceSpend(resources),
14590
14750
  iterations: turns,
14591
14751
  tokens,
14592
14752
  ...tokensKnown ? {} : { tokensKnown: false },
@@ -17913,7 +18073,12 @@ async function startRetainedInteractiveRun(options) {
17913
18073
  const profile = agentProfileSchema.parse(startOptions.environment.profile);
17914
18074
  if (profile.harness === void 0) throw new Error("retained interactive runs require AgentProfile.harness");
17915
18075
  const requestedProfileDigest = canonicalAgentProfileDigest(profile);
17916
- const identity = mintRetainedIdentity(startOptions.environment.idempotencyKey, startOptions.interactiveIdempotencyKey);
18076
+ const identity = startOptions.intent === void 0 ? mintRetainedIdentity(startOptions.environment.idempotencyKey, startOptions.interactiveIdempotencyKey) : {
18077
+ sessionId: startOptions.intent.sessionId,
18078
+ executionId: startOptions.intent.executionId
18079
+ };
18080
+ assertStableText(identity.sessionId, "retained session id");
18081
+ assertStableText(identity.executionId, "retained execution id");
17917
18082
  const intent = interactiveIntent(startOptions, profile, identity);
17918
18083
  if (startOptions.intent === void 0) await admitDurably(startOptions.onAdmission, intent);
17919
18084
  else assertExactInteractiveIntent(startOptions.intent, intent);
@@ -17987,9 +18152,21 @@ function exactRecoveryRequest(admission) {
17987
18152
  assertStableText(admission.interactiveIdempotencyKey, "interactive idempotency key");
17988
18153
  const request = exactAgentInteractiveSessionStart(admission.request);
17989
18154
  const identity = mintRetainedIdentity(admission.idempotencyKey, admission.interactiveIdempotencyKey);
17990
- if (request.run.provider !== admission.provider || request.run.environmentId !== admission.environmentId || request.run.sessionId !== identity.sessionId || request.run.executionId !== identity.executionId) throw new Error("interactive admission does not match its recovery coordinates");
18155
+ if (request.run.provider !== admission.provider || request.run.environmentId !== admission.environmentId || (request.run.sessionId !== identity.sessionId || request.run.executionId !== identity.executionId) && !matchesHistoricalInteractiveIdentity(admission, request)) throw new Error("interactive admission does not match its recovery coordinates");
17991
18156
  return request;
17992
18157
  }
18158
+ function matchesHistoricalInteractiveIdentity(admission, request) {
18159
+ const base = `${encodeURIComponent(admission.idempotencyKey)}:${encodeURIComponent(admission.interactiveIdempotencyKey)}`;
18160
+ const digest = canonicalCandidateDigest({
18161
+ kind: "retained-identity.v1",
18162
+ base
18163
+ }).slice(7);
18164
+ const historicalId = (prefix) => {
18165
+ const readable = `${prefix}:${base}`;
18166
+ return readable.length <= 128 ? readable : `${prefix}:${digest}`;
18167
+ };
18168
+ return request.run.sessionId === historicalId("retained-session") && request.run.executionId === historicalId("retained-execution");
18169
+ }
17993
18170
  /** Rebuild controls for one exact provider-owned coding-agent process. @stable */
17994
18171
  async function reconnectRetainedInteractiveRun(options) {
17995
18172
  options.signal?.throwIfAborted();
@@ -18758,6 +18935,8 @@ function sumSpendFromEvents(events) {
18758
18935
  const rootBudget = events.find((event) => event.kind === "spawned" && event.parent === void 0)?.budget;
18759
18936
  let remainingRootUsd = Math.max(0, (rootBudget?.maxUsd ?? 0) - totals.childWork.usd - totals.driverInference.usd);
18760
18937
  for (const budget of uncertainSpawnBudgets(events)) {
18938
+ const unknown = withBudgetResources(zeroSpend(), budget);
18939
+ Object.assign(totals.childWork, addResourceSpend(totals.childWork.resources, unknown.resources));
18761
18940
  totals.childWork.iterations += budget.maxIterations;
18762
18941
  totals.childWork.tokens.input += budget.maxTokens;
18763
18942
  totals.childWork.tokensKnown = false;
@@ -18769,10 +18948,17 @@ function sumSpendFromEvents(events) {
18769
18948
  return totals;
18770
18949
  }
18771
18950
  function sumMeasuredSpendFromEvents(events) {
18951
+ const budgets = new Map(events.flatMap((event) => event.kind === "spawned" ? [[event.id, event.budget]] : []));
18772
18952
  let childWork = zeroSpend();
18773
18953
  let driverInference = zeroSpend();
18774
- for (const ev of events) if (ev.kind === "settled" || ev.kind === "cancelled" && ev.spent !== void 0) childWork = addSpend(childWork, ev.spent);
18775
- else if (ev.kind === "metered") driverInference = addSpend(driverInference, ev.spend);
18954
+ const owners = /* @__PURE__ */ new Map();
18955
+ for (const ev of events) if (ev.kind === "settled" || ev.kind === "cancelled") childWork = addSpend(childWork, withBudgetResources(ev.spent ?? {
18956
+ ...zeroSpend(),
18957
+ tokensKnown: false,
18958
+ usdKnown: false
18959
+ }, budgets.get(ev.id) ?? {}));
18960
+ else if (ev.kind === "metered") owners.set(ev.id, addSpend(owners.get(ev.id) ?? zeroSpend(), ev.spend));
18961
+ for (const [id, spend] of owners) driverInference = addSpend(driverInference, withBudgetResources(spend, budgets.get(id) ?? {}));
18776
18962
  return {
18777
18963
  childWork,
18778
18964
  driverInference
@@ -18783,12 +18969,14 @@ async function prepareScopeResume(opts, events, signal, now, parentId = opts.run
18783
18969
  const prepared = await prepareInterruptedExecutors(opts, events, signal, now, parentId);
18784
18970
  const prior = prepared.events;
18785
18971
  const recovering = new Set(prepared.recoveries.map((item) => item.spawned.id));
18786
- const measured = sumMeasuredSpendFromEvents(prior.filter((event) => event.kind !== "metered" || !recovering.has(event.id)));
18972
+ const measuredEvents = prior.filter((event) => event.kind !== "metered" || !recovering.has(event.id));
18973
+ const measured = sumMeasuredSpendFromEvents(measuredEvents);
18974
+ const hasCommittedEvidence = measuredEvents.some((event) => event.kind === "metered" || event.kind === "settled" || event.kind === "cancelled");
18787
18975
  const settled = await replaySpawnTree(opts.journal, opts.blobs, opts.runId);
18788
18976
  signal.throwIfAborted();
18789
18977
  return {
18790
18978
  poolRestore: {
18791
- committed: addSpend(measured.childWork, measured.driverInference),
18979
+ ...hasCommittedEvidence ? { committed: addSpend(measured.childWork, measured.driverInference) } : {},
18792
18980
  uncertainReservations: uncertainSpawnBudgets(prior, recovering)
18793
18981
  },
18794
18982
  resumeFrom: {
@@ -19383,7 +19571,7 @@ function createScope(args) {
19383
19571
  spec = prepared.spec;
19384
19572
  identity = prepared.identity;
19385
19573
  if (opts.key !== void 0 && !isCompleteIdentity(identity)) {
19386
- args.pool.reconcile(reservation.ticket, zeroSpend());
19574
+ args.pool.reconcile(reservation.ticket, withBudgetResources(zeroSpend(), opts.budget, true));
19387
19575
  permit.release();
19388
19576
  return {
19389
19577
  ok: false,
@@ -19397,7 +19585,7 @@ function createScope(args) {
19397
19585
  if (!outcome.succeeded) throw new ValidationError$1(`scope.spawn: ${outcome.error}`);
19398
19586
  resolved = outcome;
19399
19587
  } catch (error) {
19400
- args.pool.reconcile(reservation.ticket, zeroSpend());
19588
+ args.pool.reconcile(reservation.ticket, withBudgetResources(zeroSpend(), opts.budget, true));
19401
19589
  permit.release();
19402
19590
  throw error;
19403
19591
  }
@@ -19759,7 +19947,7 @@ function createScope(args) {
19759
19947
  permit.release();
19760
19948
  clearChildDeadline?.();
19761
19949
  if (cascadeAbort) args.signal.removeEventListener("abort", cascadeAbort);
19762
- args.pool.reconcile(reservation.ticket, zeroSpend());
19950
+ args.pool.reconcile(reservation.ticket, withBudgetResources(zeroSpend(), opts.budget, true));
19763
19951
  throw err;
19764
19952
  }
19765
19953
  }
@@ -19987,6 +20175,7 @@ function createScope(args) {
19987
20175
  steerable: child.deliver !== void 0 && !child.delivered,
19988
20176
  startedAt: child.startedAt,
19989
20177
  lastActivityAt: child.lastActivityAt,
20178
+ ...addResourceSpend(child.spent.resources),
19990
20179
  turns: child.spent.iterations,
19991
20180
  tokens: child.spent.tokens,
19992
20181
  ...child.spent.tokensKnown === false ? { tokensKnown: false } : {},
@@ -20025,10 +20214,12 @@ function createScope(args) {
20025
20214
  }
20026
20215
  async function meterInternal(spend, detail, providerModel, accountingOnly = false) {
20027
20216
  if (args.signal.aborted) throw new ValidationError$1("scope.meter: cannot record new driver work after scope abort");
20217
+ const partial = providerModel !== void 0 || accountingOnly;
20218
+ if (!partial) spend = withBudgetResources(spend, args.pool.readout());
20028
20219
  const seq = meterSeq++;
20029
20220
  let observeError;
20030
20221
  try {
20031
- args.pool.observe(spend);
20222
+ args.pool.observe(spend, { partial });
20032
20223
  } catch (error) {
20033
20224
  observeError = error;
20034
20225
  }
@@ -20628,6 +20819,23 @@ async function runChild(live, executor, childAbort, task, opts, pool, ticket, bl
20628
20819
  if (reconciled) return reconciliationError;
20629
20820
  reconciled = true;
20630
20821
  try {
20822
+ try {
20823
+ spend = withBudgetResources(spend, opts.budget, !started);
20824
+ live.spent = withBudgetResources(live.spent, opts.budget, !started || executor.accounting?.() !== void 0);
20825
+ } catch (error) {
20826
+ spend = withBudgetResources({
20827
+ ...spend,
20828
+ resources: void 0
20829
+ }, opts.budget);
20830
+ live.spent = withBudgetResources({
20831
+ ...live.spent,
20832
+ resources: void 0
20833
+ }, opts.budget);
20834
+ try {
20835
+ pool.reconcile(ticket, spend);
20836
+ } catch {}
20837
+ throw error;
20838
+ }
20631
20839
  pool.reconcile(ticket, spend);
20632
20840
  return;
20633
20841
  } catch (error) {
@@ -20775,7 +20983,11 @@ async function runChild(live, executor, childAbort, task, opts, pool, ticket, bl
20775
20983
  if (started && !terminalTelemetryCaptured && accounting === void 0) live.spent = {
20776
20984
  ...live.spent,
20777
20985
  tokensKnown: false,
20778
- usdKnown: false
20986
+ usdKnown: false,
20987
+ ...live.spent.resources === void 0 ? {} : { resources: Object.fromEntries(Object.entries(live.spent.resources).map(([name, value]) => [name, {
20988
+ ...value,
20989
+ known: false
20990
+ }])) }
20779
20991
  };
20780
20992
  const reconcileError = reconcileOnce(accounting?.reservation ?? live.spent);
20781
20993
  return downRecord(errMessage(err), evidenceError !== void 0 || teardownError !== void 0 || reconcileError !== void 0 || aborted || isInfraError(err), trace, executor.metered?.(), providerModel);
@@ -20933,6 +21145,7 @@ async function foldStream(stream, onProgress, signal) {
20933
21145
  const ev = next.value;
20934
21146
  meterUsageEvent(totals, ev);
20935
21147
  await onProgress?.({
21148
+ ...addResourceSpend(totals.resources),
20936
21149
  iterations: totals.iterations,
20937
21150
  tokens: cloneTokenUsage(totals.tokens),
20938
21151
  ...totals.tokensKnown ? {} : { tokensKnown: false },
@@ -20954,6 +21167,7 @@ function preserveUnknownTelemetry(streamed, terminal) {
20954
21167
  const terminalTokens = cloneTokenUsage(terminal.tokens);
20955
21168
  return {
20956
21169
  ...streamed,
21170
+ ...resourceTelemetry(streamed, terminal),
20957
21171
  tokens: {
20958
21172
  ...streamed.tokens,
20959
21173
  ...streamed.tokens.freshInput === void 0 && terminalTokens.freshInput !== void 0 ? { freshInput: terminalTokens.freshInput } : {},
@@ -21315,7 +21529,7 @@ function sumMetered(events) {
21315
21529
  * went unmeasured did real work, and dropping its `metered` event here would re-hide upstream the
21316
21530
  * very turn the driver refused to skip. */
21317
21531
  function isNonZeroSpend(s) {
21318
- return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false;
21532
+ return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false || Object.keys(s.resources ?? {}).length > 0;
21319
21533
  }
21320
21534
  /** A spend, or `undefined` when it is all-zero — so `metered()` returns undefined for a driver
21321
21535
  * whose sub-tree did no inference (and the parent journals no empty `metered` event). */
@@ -21396,6 +21610,15 @@ function unreportedSpend(total, prior) {
21396
21610
  }
21397
21611
  return {
21398
21612
  ...total,
21613
+ ...addResourceSpend(total.resources === void 0 ? void 0 : Object.fromEntries(Object.entries(total.resources).map(([name, value]) => {
21614
+ const previous = prior.resources?.[name];
21615
+ if (previous && previous.unit !== value.unit) throw new ValidationError$1(`resource ${name}: unit mismatch`);
21616
+ return [name, {
21617
+ ...value,
21618
+ amount: Math.max(0, value.amount - (previous?.amount ?? 0)),
21619
+ known: value.known && (previous?.known ?? true)
21620
+ }];
21621
+ }))),
21399
21622
  iterations: Math.max(0, total.iterations - prior.iterations),
21400
21623
  tokens,
21401
21624
  usd: Math.max(0, total.usd - prior.usd),
@@ -22194,9 +22417,12 @@ async function terminalAccounting(journal, root, elapsedMs) {
22194
22417
  * result this list rides on. */
22195
22418
  function spendGapsFromEvents(events) {
22196
22419
  const labels = /* @__PURE__ */ new Map();
22420
+ const budgets = /* @__PURE__ */ new Map();
22197
22421
  const terminal = /* @__PURE__ */ new Set();
22198
- for (const ev of events) if (ev.kind === "spawned") labels.set(ev.id, ev.label);
22199
- else if (closesCursorSlot(ev)) terminal.add(ev.id);
22422
+ for (const ev of events) if (ev.kind === "spawned") {
22423
+ labels.set(ev.id, ev.label);
22424
+ budgets.set(ev.id, ev.budget);
22425
+ } else if (closesCursorSlot(ev)) terminal.add(ev.id);
22200
22426
  const gaps = /* @__PURE__ */ new Map();
22201
22427
  const record = (id, kind, channels) => {
22202
22428
  if (channels.length === 0) return;
@@ -22209,9 +22435,17 @@ function spendGapsFromEvents(events) {
22209
22435
  channels: new Set(channels)
22210
22436
  });
22211
22437
  };
22212
- for (const ev of events) if (ev.kind === "spawned" && ev.parent !== void 0 && !terminal.has(ev.id)) record(ev.id, "never-settled", ["tokens", "usd"]);
22213
- else if (ev.kind === "cancelled" && ev.spent === void 0) record(ev.id, "unreported", ["tokens", "usd"]);
22214
- else if (ev.kind === "settled" || ev.kind === "cancelled" && ev.spent !== void 0) record(ev.id, "unreported", unknownChannels(ev.spent));
22438
+ for (const ev of events) if (ev.kind === "spawned" && ev.parent !== void 0 && !terminal.has(ev.id)) record(ev.id, "never-settled", [
22439
+ "tokens",
22440
+ "usd",
22441
+ ...Object.keys(ev.budget.resources ?? {}).map((name) => `resource:${name}`)
22442
+ ]);
22443
+ else if (ev.kind === "cancelled" && ev.spent === void 0) record(ev.id, "unreported", [
22444
+ "tokens",
22445
+ "usd",
22446
+ ...Object.keys(budgets.get(ev.id)?.resources ?? {}).map((name) => `resource:${name}`)
22447
+ ]);
22448
+ else if (ev.kind === "settled" || ev.kind === "cancelled" && ev.spent !== void 0) record(ev.id, "unreported", unknownChannels(withBudgetResources(ev.spent, budgets.get(ev.id) ?? {})));
22215
22449
  else if (ev.kind === "metered") record(ev.id, "unreported", unknownChannels(ev.spend));
22216
22450
  return [...gaps.values()].map((gap) => {
22217
22451
  const label = labels.get(gap.id);
@@ -22228,13 +22462,14 @@ function unknownChannels(spend) {
22228
22462
  const channels = [];
22229
22463
  if (spend.tokensKnown === false) channels.push("tokens");
22230
22464
  if (spend.usdKnown === false) channels.push("usd");
22465
+ for (const [name, resource] of Object.entries(spend.resources ?? {})) if (!resource.known) channels.push(`resource:${name}`);
22231
22466
  return channels;
22232
22467
  }
22233
22468
  /** True when any driver metered inference this run (so the winner carries a `spentBreakdown`).
22234
22469
  * Checks every channel `addSpend` sums — including `ms` — so the gate stays consistent with the
22235
22470
  * total even though the coordination driver currently stamps `ms: 0`. */
22236
22471
  function isNonEmptySpend(s) {
22237
- return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false;
22472
+ return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false || Object.values(s.resources ?? {}).some((resource) => resource.amount > 0 || !resource.known);
22238
22473
  }
22239
22474
  //#endregion
22240
22475
  //#region src/redact.ts
@@ -22320,6 +22555,6 @@ function resolveRedactor(redact) {
22320
22555
  return (value) => defaultRedactor(redact(value));
22321
22556
  }
22322
22557
  //#endregion
22323
- export { bridgeAdmissionRefusal as $, buildLoopSpanNodes as $n, newExecutionAttemptId as $r, spendFromUsageEvents as $t, consumeScopeRetainedOwnerResult as A, awaitAbortable$1 as Ai, throwIfAborted as An, fullProfileMaterialization as Ar, peerMailTools as At, workerInteractiveBindingFile as B, startRetainedRun as Bn, validateProfileMaterialization as Br, insideCursorNamespace as Bt, settledToIteration as C, mapSandboxToolEvent as Ci, hasCompleteCacheBreakdown as Cn, localHarnessExecutable as Cr, createInbox as Ct, timerAt as D, sumSandboxUsage as Di, sleep as Dn, assertProfileMaterialization as Dr, claimsAuthority as Dt, pollFor as E, sandboxProgressEvents as Ei, randomSuffix as En, AGENT_PROFILE_MATERIALIZATION_AXES as Er, PEER_MAIL_WIRE_KEY as Et, interactiveAdmissionSeamKey as F, RunCancellationReason as Fn, promptResourceProfileMaterialization as Fr, taskToPrompt as Ft, destroyInteractiveEnvironment as G, harnessUsageIsEmpty as Gn, writeAllBytes as Gr, WORKER_TOOL_TRACE_SCHEMA_VERSION as Gt, reconnectRetainedInteractiveRun as H, retainedCreateMaterial as Hn, isNoEntError as Hr, materializeTreeView as Ht, readWorkerInteractiveAdmissions as I, abortError$1 as In, renderProfileMaterializationIssues as Ir, FileResultBlobStore as It, queueOf as J, mergeTraceEnv as Jn, authoredProfileDigest as Jr, parseWorkerToolTraceArtifact as Jt, effectiveConcurrency as K, readCodexRolloutSession as Kn, attestRuntimeOwnedPendingExecutor as Kr, captureWorkerTraceEvidence as Kt, workerInteractiveAdmissionFile as L, linkAbort as Ln, renderUnsupported as Lr, FileSpawnJournal as Lt, scopeRetainedOwnerContext as M, promptOptionsFromAgentTurnInput as Mi, zeroSpend as Mn, promptControlProfileMaterialization as Mr, isLiveNodeStatus as Mt, scopeRetainedOwnerPriorSpend as N, providerMessageText as Ni, registerRetainedExecutorPreparation as Nn, promptModelProfileMaterialization as Nr, isTerminalNodeStatus as Nt, validateWaitSpec as O, decodeHarnessUsage as Oi, stringifySafe as On, controlProfileMaterialization as Or, createPeerMailbox as Ot, scopeRetainedOwnerResult as P, retainedExecutorSeamKey as Pn, promptOnlyProfileMaterialization as Pr, createInPlaceCliExecutor as Pt, bindReusableExecutorExecutionId as Q, buildLoopOtelSpans as Qn, knownMaterializationReceipt as Qr, createBudgetPool as Qt, attachWorker as R, reconnectRetainedRun as Rn, sandboxActProfileMaterialization as Rr, InMemoryResultBlobStore as Rt, scopeOwnerExecutorNodeContext as S, mapSandboxEvent as Si, deleteBoxSafe as Sn, harnessSupportsReasoningEffort as Sr, readWorkerProgress as St, isWaitOutcome as T, sandboxEventServedBackend as Ti, promptCacheTokenClasses as Tn, CodexExecutionDiagnosticError as Tr, DEFAULT_PEER_MAIL_LIMITS as Tt, recoverRetainedInteractiveRun as U, addHarnessUsage as Un, parseCommittedJsonLines as Ur, pendingWaits as Ut, workerInteractiveBindingsDir as V, startRetainedRunInEnvironment as Vn, worktreeCliProfileMaterialization as Vr, loadSpawnForest as Vt, startRetainedInteractiveRun as W, createCodexRolloutStoreReader as Wn, prepareJsonlAppend as Wr, replaySpawnTree as Wt, DEFAULT_SUCCESSFUL_SHUTDOWN_MS as X, traceContextToEnv as Xn, inheritRuntimeOwnedExecutorAttestation as Xr, contentAddress as Xt, rollingDispatch as Y, readTraceContextFromEnv as Yn, finalizeRuntimeOwnedPendingExecutor as Yr, workerTraceAnalysisStore as Yt, teardownExecutor as Z, INTELLIGENCE_WIRE_VERSION as Zn, knownExecutionBindingReceipt as Zr, assertValidBudget as Zt, deriveNodeExecutionIdentity as _, canonicalStreamEventFromSandboxEvent as _i, resolveAgentEnvironmentProvider as _n, captureWorktreeDiff as _r, createPushTraceSource as _t, createSupervisor as a, runtimeOwnedExecutorProviderEvidence as ai, createSandboxLineage as an, generateSpanId as ar, cliWorktreeExecutor as at, recordScopeOwnerMaterialization as b, extractLlmCallEvent as bi, chargedTokens as bn, DEFAULT_LOCAL_HARNESS as br, DEFAULT_STALL_AFTER_MS as bt, pickBestDelivered as c, unknownExecutionBindingReceipt as ci, assertBoxlessPromptOptions as cn, padTraceId as cr, snapshotExecutorConfig as ct, driverChild as d, executableAgentSpecSnapshot as di, runBrainLoop as dn, createRuntimeStreamEventCollector as dr, readWorkerTraceContext as dt, providerAttemptEvidence as ei, TERMINAL_DECISIONS as en, buildRuntimeEventOtelSpans as er, bridgeModelRouteRefusal as et, driverExecutorFactory as f, detachedFrozen as fi, canonicalObservedModelParts as fn, sanitizeAgentRuntimeEvent as fr, workerTraceEnv as ft, createScope as g, assertSandboxServedModel as gi, providerAsSandboxClient as gn, runWorktreeHarness as gr, createSteerableSandboxSession as gt, beginScopeOwnerAttempt as h, readSandboxOutcome as hi, providerAsExecutor as hn, runSettledCommand as hr, DEFAULT_SANDBOX_STEERING_MAX_TURNS as ht, createRootHandle as i, runtimeOwnedExecutorMaterialization as ii, runAgentRounds as in, flatOtelSpan as ir, cliInPlaceExecutor as it, prepareScopeRetainedOwnerTask as j, promptFromAgentTurnInput as ji, unmeteredSpend as jn, profileMaterializationAxes$1 as jr, peerMailVerbNames as jt, waitUntil as k, abortError$2 as ki, throwAbort as kn, defineProfileMaterializationContract as kr, isPeerMailEnvelope as kt, runFinalizer as l, unknownMaterializationReceipt as li, readPromptOptions as ln, toOtelAttributes as lr, createWorktreeCliExecutor as lt, withDriverExecutor as m, projectSandboxOutcome as mi, createAgentEnvironmentProviderRegistry as mn, sanitizeRuntimeStreamEvent as mr, workerTraceSeamKey as mt, defaultRedactorIdentityMaterial as n, runtimeOwnedDriveHarnessProviderEvidence as ni, defaultSelectWinner as nn, createOtelExporter as nr, bridgeStopSignalKey as nt, bestDelivered as o, runtimeOwnedPendingExecutorMaterialization as oi, probeSandboxCapabilities as on, loopEventToOtelSpan as or, createExecutor as ot, isDriverSpec as p, detachedSnapshot as pi, observedModelMatchesDeclared as pn, sanitizeKnowledgeReadinessReport as pr, workerTraceHeaders as pt, freeSlots as q, createPropagatingTraceEmitter as qn, attestRuntimeOwnedScopeOwner as qr, isTraceAnalysisStore as qt, resolveRedactor as r, runtimeOwnedExecutorExecutionBinding as ri, isTerminalDecision as rn, exportEvalRuns as rr, captureReusableExecutorConfig as rt, collectDelivered as s, runtimeOwnedScopeOwnerRuntime as si, acquireSandbox as sn, padSpanId as sr, createExecutorRegistry as st, defaultRedactor as t, recordRuntimeOwnedDriveHarnessProviderEvidence as ti, createSandboxForSpec as tn, createOpenInferenceFileExporter as tr, bridgeRuntimeAttachmentsKey as tt, runTree as u, executableAgentProfileSnapshot as ui, routerBrain as un, createRuntimeEventCollector as ur, WORKER_TRACE_PROPAGATION as ut, meterRuntimeOwnedAccounting as v, createSandboxToolPartState as vi, sandboxClientAsProvider as vn, createWorktree as vr, decodeToolPart as vt, createWaitProbes as w, notifySandboxEventObserver as wi, isAbortError$2 as wn, parseCodexTokenUsage as wr, AUTHORITY_MARKERS as wt, restoreScopeOwnerAcceptedExecution as x, isSandboxTerminalEvent as xi, cloneSpend as xn, LOCAL_HARNESSES as xr, createActivityLog as xt, meterRuntimeOwnedProviderAttempt as y, createSandboxUsageLedger as yi, addSpend as yn, removeWorktree as yr, sandboxSessionTraceSource as yt, readWorkerInteractiveBinding as z, recoverRetainedRun as zn, unsupportedProfileDimensions as zr, InMemorySpawnJournal as zt };
22558
+ export { bridgeAdmissionRefusal as $, buildLoopSpanNodes as $n, inheritRuntimeOwnedExecutorAttestation as $r, spendFromUsageEvents as $t, consumeScopeRetainedOwnerResult as A, sumSandboxUsage as Ai, throwIfAborted as An, assertProfileMaterialization as Ar, peerMailTools as At, workerInteractiveBindingFile as B, startRetainedRun as Bn, renderUnsupported as Br, insideCursorNamespace as Bt, settledToIteration as C, extractLlmCallEvent as Ci, hasCompleteCacheBreakdown as Cn, DEFAULT_LOCAL_HARNESS as Cr, createInbox as Ct, timerAt as D, notifySandboxEventObserver as Di, sleep as Dn, parseCodexTokenUsage as Dr, claimsAuthority as Dt, pollFor as E, mapSandboxToolEvent as Ei, randomSuffix as En, localHarnessExecutable as Er, PEER_MAIL_WIRE_KEY as Et, interactiveAdmissionSeamKey as F, promptOptionsFromAgentTurnInput as Fi, RunCancellationReason as Fn, promptControlProfileMaterialization as Fr, taskToPrompt as Ft, destroyInteractiveEnvironment as G, harnessUsageIsEmpty as Gn, isNoEntError as Gr, WORKER_TOOL_TRACE_SCHEMA_VERSION as Gt, reconnectRetainedInteractiveRun as H, retainedCreateMaterial as Hn, unsupportedProfileDimensions as Hr, materializeTreeView as Ht, readWorkerInteractiveAdmissions as I, providerMessageText as Ii, abortError$1 as In, promptModelProfileMaterialization as Ir, FileResultBlobStore as It, queueOf as J, mergeTraceEnv as Jn, writeAllBytes as Jr, parseWorkerToolTraceArtifact as Jt, effectiveConcurrency as K, readCodexRolloutSession as Kn, parseCommittedJsonLines as Kr, captureWorkerTraceEvidence as Kt, workerInteractiveAdmissionFile as L, linkAbort as Ln, promptOnlyProfileMaterialization as Lr, FileSpawnJournal as Lt, scopeRetainedOwnerContext as M, abortError$2 as Mi, zeroSpend as Mn, defineProfileMaterializationContract as Mr, isLiveNodeStatus as Mt, scopeRetainedOwnerPriorSpend as N, awaitAbortable$1 as Ni, registerRetainedExecutorPreparation as Nn, fullProfileMaterialization as Nr, isTerminalNodeStatus as Nt, validateWaitSpec as O, sandboxEventServedBackend as Oi, stringifySafe as On, CodexExecutionDiagnosticError as Or, createPeerMailbox as Ot, scopeRetainedOwnerResult as P, promptFromAgentTurnInput as Pi, retainedExecutorSeamKey as Pn, profileMaterializationAxes$1 as Pr, createInPlaceCliExecutor as Pt, bindReusableExecutorExecutionId as Q, buildLoopOtelSpans as Qn, finalizeRuntimeOwnedPendingExecutor as Qr, createBudgetPool as Qt, attachWorker as R, reconnectRetainedRun as Rn, promptResourceProfileMaterialization as Rr, InMemoryResultBlobStore as Rt, scopeOwnerExecutorNodeContext as S, createSandboxUsageLedger as Si, deleteBoxSafe as Sn, removeWorktree as Sr, readWorkerProgress as St, isWaitOutcome as T, mapSandboxEvent as Ti, promptCacheTokenClasses as Tn, harnessSupportsReasoningEffort as Tr, DEFAULT_PEER_MAIL_LIMITS as Tt, recoverRetainedInteractiveRun as U, addHarnessUsage as Un, validateProfileMaterialization as Ur, pendingWaits as Ut, workerInteractiveBindingsDir as V, startRetainedRunInEnvironment as Vn, sandboxActProfileMaterialization as Vr, loadSpawnForest as Vt, startRetainedInteractiveRun as W, createCodexRolloutStoreReader as Wn, worktreeCliProfileMaterialization as Wr, replaySpawnTree as Wt, DEFAULT_SUCCESSFUL_SHUTDOWN_MS as X, traceContextToEnv as Xn, attestRuntimeOwnedScopeOwner as Xr, contentAddress as Xt, rollingDispatch as Y, readTraceContextFromEnv as Yn, attestRuntimeOwnedPendingExecutor as Yr, workerTraceAnalysisStore as Yt, teardownExecutor as Z, INTELLIGENCE_WIRE_VERSION as Zn, authoredProfileDigest as Zr, assertValidBudget as Zt, deriveNodeExecutionIdentity as _, projectSandboxOutcome as _i, resolveAgentEnvironmentProvider as _n, withBudgetResources as _r, createPushTraceSource as _t, createSupervisor as a, runtimeOwnedDriveHarnessProviderEvidence as ai, createSandboxLineage as an, generateSpanId as ar, cliWorktreeExecutor as at, recordScopeOwnerMaterialization as b, canonicalStreamEventFromSandboxEvent as bi, chargedTokens as bn, captureWorktreeDiff as br, DEFAULT_STALL_AFTER_MS as bt, pickBestDelivered as c, runtimeOwnedExecutorProviderEvidence as ci, assertBoxlessPromptOptions as cn, padTraceId as cr, snapshotExecutorConfig as ct, driverChild as d, unknownExecutionBindingReceipt as di, runBrainLoop as dn, createRuntimeStreamEventCollector as dr, readWorkerTraceContext as dt, knownExecutionBindingReceipt as ei, TERMINAL_DECISIONS as en, buildRuntimeEventOtelSpans as er, bridgeModelRouteRefusal as et, driverExecutorFactory as f, unknownMaterializationReceipt as fi, canonicalObservedModelParts as fn, sanitizeAgentRuntimeEvent as fr, workerTraceEnv as ft, createScope as g, detachedSnapshot as gi, providerAsSandboxClient as gn, resourceTelemetry as gr, createSteerableSandboxSession as gt, beginScopeOwnerAttempt as h, detachedFrozen as hi, providerAsExecutor as hn, addResourceSpend as hr, DEFAULT_SANDBOX_STEERING_MAX_TURNS as ht, createRootHandle as i, recordRuntimeOwnedDriveHarnessProviderEvidence as ii, runAgentRounds as in, flatOtelSpan as ir, cliInPlaceExecutor as it, prepareScopeRetainedOwnerTask as j, decodeHarnessUsage as ji, unmeteredSpend as jn, controlProfileMaterialization as jr, peerMailVerbNames as jt, waitUntil as k, sandboxProgressEvents as ki, throwAbort as kn, AGENT_PROFILE_MATERIALIZATION_AXES as kr, isPeerMailEnvelope as kt, runFinalizer as l, runtimeOwnedPendingExecutorMaterialization as li, readPromptOptions as ln, toOtelAttributes as lr, createWorktreeCliExecutor as lt, withDriverExecutor as m, executableAgentSpecSnapshot as mi, createAgentEnvironmentProviderRegistry as mn, sanitizeRuntimeStreamEvent as mr, workerTraceSeamKey as mt, defaultRedactorIdentityMaterial as n, newExecutionAttemptId as ni, defaultSelectWinner as nn, createOtelExporter as nr, bridgeStopSignalKey as nt, bestDelivered as o, runtimeOwnedExecutorExecutionBinding as oi, probeSandboxCapabilities as on, loopEventToOtelSpan as or, createExecutor as ot, isDriverSpec as p, executableAgentProfileSnapshot as pi, observedModelMatchesDeclared as pn, sanitizeKnowledgeReadinessReport as pr, workerTraceHeaders as pt, freeSlots as q, createPropagatingTraceEmitter as qn, prepareJsonlAppend as qr, isTraceAnalysisStore as qt, resolveRedactor as r, providerAttemptEvidence as ri, isTerminalDecision as rn, exportEvalRuns as rr, captureReusableExecutorConfig as rt, collectDelivered as s, runtimeOwnedExecutorMaterialization as si, acquireSandbox as sn, padSpanId as sr, createExecutorRegistry as st, defaultRedactor as t, knownMaterializationReceipt as ti, createSandboxForSpec as tn, createOpenInferenceFileExporter as tr, bridgeRuntimeAttachmentsKey as tt, runTree as u, runtimeOwnedScopeOwnerRuntime as ui, routerBrain as un, createRuntimeEventCollector as ur, WORKER_TRACE_PROPAGATION as ut, meterRuntimeOwnedAccounting as v, readSandboxOutcome as vi, sandboxClientAsProvider as vn, runSettledCommand as vr, decodeToolPart as vt, createWaitProbes as w, isSandboxTerminalEvent as wi, isAbortError$2 as wn, LOCAL_HARNESSES as wr, AUTHORITY_MARKERS as wt, restoreScopeOwnerAcceptedExecution as x, createSandboxToolPartState as xi, cloneSpend as xn, createWorktree as xr, createActivityLog as xt, meterRuntimeOwnedProviderAttempt as y, assertSandboxServedModel as yi, addSpend as yn, runWorktreeHarness as yr, sandboxSessionTraceSource as yt, readWorkerInteractiveBinding as z, recoverRetainedRun as zn, renderProfileMaterializationIssues as zr, InMemorySpawnJournal as zt };
22324
22559
 
22325
- //# sourceMappingURL=redact-BMwd8IBm.js.map
22560
+ //# sourceMappingURL=redact-va_1mmv8.js.map