@tangle-network/agent-runtime 6.0.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{activation-BRfZOfwb.js → activation-DNqjZEo7.js} +4 -4
  3. package/dist/{activation-BRfZOfwb.js.map → activation-DNqjZEo7.js.map} +1 -1
  4. package/dist/agent.js +4 -4
  5. package/dist/{bundle-vFFiCigS.js → bundle-BET6gl5r.js} +2 -2
  6. package/dist/{bundle-vFFiCigS.js.map → bundle-BET6gl5r.js.map} +1 -1
  7. package/dist/candidate-execution/index.js +4 -4
  8. package/dist/{coordination-manager-RSJg5GmY.js → coordination-manager-CYElgkMI.js} +7 -6
  9. package/dist/{coordination-manager-RSJg5GmY.js.map → coordination-manager-CYElgkMI.js.map} +1 -1
  10. package/dist/{coordination-map-routing-DVjW5Gx0.js → coordination-map-routing-CKRNZSaV.js} +2 -2
  11. package/dist/{coordination-map-routing-DVjW5Gx0.js.map → coordination-map-routing-CKRNZSaV.js.map} +1 -1
  12. package/dist/{delegate-C0cprMUN.js → delegate-dW2jsqFF.js} +2 -2
  13. package/dist/{delegate-C0cprMUN.js.map → delegate-dW2jsqFF.js.map} +1 -1
  14. package/dist/durable.js +12 -11
  15. package/dist/durable.js.map +1 -1
  16. package/dist/failure-cause-BzjUUsRz.js +3208 -0
  17. package/dist/failure-cause-BzjUUsRz.js.map +1 -0
  18. package/dist/{graph-cEg-gquv.js → graph-A7AvOP1I.js} +7 -6
  19. package/dist/{graph-cEg-gquv.js.map → graph-A7AvOP1I.js.map} +1 -1
  20. package/dist/{graph-registry-C-hjRx2t.js → graph-registry-C4ASeL2P.js} +1 -1
  21. package/dist/{graph-registry-B25FxBZN.js → graph-registry-DbCwfHMY.js} +3 -3
  22. package/dist/{graph-registry-B25FxBZN.js.map → graph-registry-DbCwfHMY.js.map} +1 -1
  23. package/dist/{improve-DNl4_6lP.js → improve-4qJclc9J.js} +10 -8
  24. package/dist/{improve-DNl4_6lP.js.map → improve-4qJclc9J.js.map} +1 -1
  25. package/dist/index.js +13 -11
  26. package/dist/index.js.map +1 -1
  27. package/dist/intelligence.js +8 -8
  28. package/dist/jsonl-file-BeUa5EDx.js +232 -0
  29. package/dist/jsonl-file-BeUa5EDx.js.map +1 -0
  30. package/dist/kernel.js +14 -13
  31. package/dist/{loop-runner-DQJjowz0.js → loop-runner-DZxVXLLU.js} +2 -2
  32. package/dist/{loop-runner-DQJjowz0.js.map → loop-runner-DZxVXLLU.js.map} +1 -1
  33. package/dist/loop-runner-bin.js +1 -1
  34. package/dist/mcp/bin.js +3 -3
  35. package/dist/mcp/index.js +7 -7
  36. package/dist/mcp/memory-bin.js +1 -1
  37. package/dist/{prepare-B6SQA12v.js → prepare-O1HbzOqs.js} +4 -3
  38. package/dist/{prepare-B6SQA12v.js.map → prepare-O1HbzOqs.js.map} +1 -1
  39. package/dist/{profile-DN8Dw2Kp.js → profile-DcRoyryP.js} +2 -2
  40. package/dist/{profile-DN8Dw2Kp.js.map → profile-DcRoyryP.js.map} +1 -1
  41. package/dist/{provision-supervisor-CtAaJCUa.js → provision-supervisor-CBeTMpMY.js} +7 -6
  42. package/dist/{provision-supervisor-CtAaJCUa.js.map → provision-supervisor-CBeTMpMY.js.map} +1 -1
  43. package/dist/{run-cancellation-BCuu6khW.js → run-cancellation-ZmRSYLu5.js} +4 -3
  44. package/dist/{run-cancellation-BCuu6khW.js.map → run-cancellation-ZmRSYLu5.js.map} +1 -1
  45. package/dist/{run-state-ByHiAjUs.js → run-state-Dn2AIE5m.js} +6 -6
  46. package/dist/{run-state-ByHiAjUs.js.map → run-state-Dn2AIE5m.js.map} +1 -1
  47. package/dist/{runtime-9EYvBcHG.js → runtime-D5eL70Is.js} +20 -17
  48. package/dist/{runtime-9EYvBcHG.js.map → runtime-D5eL70Is.js.map} +1 -1
  49. package/dist/{scope-DzhBJrRN.js → scope-DSm2RW5I.js} +9 -3204
  50. package/dist/scope-DSm2RW5I.js.map +1 -0
  51. package/dist/{search-executor-BJRNavck.js → search-executor-1qaSeY70.js} +6 -4
  52. package/dist/{search-executor-BJRNavck.js.map → search-executor-1qaSeY70.js.map} +1 -1
  53. package/dist/{server-DH0P6OY_.js → server-Dp4JKuTZ.js} +5 -5
  54. package/dist/{server-DH0P6OY_.js.map → server-Dp4JKuTZ.js.map} +1 -1
  55. package/dist/{settle-record-BSDDf9Z4.js → settle-record-DTh0cvF-.js} +2 -2
  56. package/dist/{settle-record-BSDDf9Z4.js.map → settle-record-DTh0cvF-.js.map} +1 -1
  57. package/dist/snapshot-Cgj2Wzi6.js +210 -0
  58. package/dist/snapshot-Cgj2Wzi6.js.map +1 -0
  59. package/dist/{structural-rollout-DgvJLTO0.js → structural-rollout-BwyAIuww.js} +6 -4
  60. package/dist/{structural-rollout-DgvJLTO0.js.map → structural-rollout-BwyAIuww.js.map} +1 -1
  61. package/dist/{supervise-Cqhkb2Oj.js → supervise-C2Do7hEd.js} +11 -10
  62. package/dist/{supervise-Cqhkb2Oj.js.map → supervise-C2Do7hEd.js.map} +1 -1
  63. package/dist/{supervision-discovery-DI5GIFBJ.js → supervision-discovery-BNChDps7.js} +5 -4
  64. package/dist/{supervision-discovery-DI5GIFBJ.js.map → supervision-discovery-BNChDps7.js.map} +1 -1
  65. package/dist/{supervisor-B6sl14OR.js → supervisor-jXN2xXiU.js} +8 -5
  66. package/dist/{supervisor-B6sl14OR.js.map → supervisor-jXN2xXiU.js.map} +1 -1
  67. package/dist/testing.js +13 -13
  68. package/dist/{tool-server-CZWd_Opp.js → tool-server-B9q2dtnW.js} +11 -8
  69. package/dist/tool-server-B9q2dtnW.js.map +1 -0
  70. package/dist/{top-web-DGoXGxH8.js → top-web-CQNkrh_j.js} +7 -6
  71. package/dist/{top-web-DGoXGxH8.js.map → top-web-CQNkrh_j.js.map} +1 -1
  72. package/dist/tui/bin.js +3 -3
  73. package/dist/tui/index.js +2 -2
  74. package/dist/util-C_cdPNC4.js +653 -0
  75. package/dist/util-C_cdPNC4.js.map +1 -0
  76. package/dist/{workspace-streams-BqNM06y4.js → workspace-streams-uB9rRbms.js} +3 -652
  77. package/dist/workspace-streams-uB9rRbms.js.map +1 -0
  78. package/package.json +3 -3
  79. package/dist/jsonl-file-CeVYTIQJ.js +0 -439
  80. package/dist/jsonl-file-CeVYTIQJ.js.map +0 -1
  81. package/dist/scope-DzhBJrRN.js.map +0 -1
  82. package/dist/tool-server-CZWd_Opp.js.map +0 -1
  83. package/dist/workspace-streams-BqNM06y4.js.map +0 -1
@@ -1,21 +1,20 @@
1
- import { c as RetainedInteractiveAdmissionError, d as RetainedRunDispatchBindingError, f as RuntimeRunStateError, l as RetainedInteractiveBindingError, m as ValidationError, r as BackendTransportError, t as AgentEvalError$1, u as RetainedRunAdmissionError } from "./errors-AzSuGI23.js";
2
- import { A as verifiedArtifactChunks, Ct as boundedChildDeadlineAt, Dt as teardownExecutor, Et as singleFlightTeardown, G as cloneSpend, H as addSpend, J as errorMessage, K as cloneTokenUsage, P as deepFreezeCandidate, Q as isAsyncIterable, R as sha256Bytes$1, St as armDeadlineTimer, U as addTokenUsage, V as abortError, W as chargedTokens, X as hasCompleteCacheBreakdown, Y as fnv1a32Hex, dt as zeroSpend, et as promptCacheTokenClasses, ft as zeroTokenUsage, gt as withBudgetResources, h as workspaceLimits, ht as resourceTelemetry, i as readAgentCandidateWorkspaceArtifactFiles, it as sleep, lt as unreportedSpend, m as safeArchivePath, mt as assertResources, pt as addResourceSpend, vt as CHILD_DEADLINE_REASON, wt as checkInIdleMs } from "./workspace-streams-BqNM06y4.js";
3
- import { _ as errorText, a as readCommittedJsonLines, b as resolveRedactor, c as appendCommittedJsonLine, d as detachedSnapshot, f as shareParsedStrings, g as errorProperty, l as sha256, m as errMessage, p as causeChain, r as prepareJsonlAppend, s as withJsonlFileLock, t as isNoEntError, v as defaultRedactor } from "./jsonl-file-CeVYTIQJ.js";
1
+ import { c as RetainedInteractiveAdmissionError, d as RetainedRunDispatchBindingError, f as RuntimeRunStateError, l as RetainedInteractiveBindingError, m as ValidationError, u as RetainedRunAdmissionError } from "./errors-AzSuGI23.js";
2
+ import { A as withBudgetResources, B as singleFlightTeardown, C as unreportedSpend, D as addResourceSpend, I as armDeadlineTimer, L as boundedChildDeadlineAt, M as CHILD_DEADLINE_REASON, R as checkInIdleMs, T as zeroSpend, V as teardownExecutor, a as cloneSpend, c as errorMessage, f as isAsyncIterable, i as chargedTokens, k as resourceTelemetry, n as addSpend, o as cloneTokenUsage, t as abortError, v as sleep } from "./util-C_cdPNC4.js";
3
+ import { $ as assertValidBudget, F as executorFailureInfra, G as budgetRecoverySeeds, H as BudgetReconcileFault, I as executorFailureReason, J as refreshBudgetLease, K as completedLeafBacking, M as contentAddress, P as executorFailure, Q as spendFromUsageTotals, U as budgetLeaseUpdates, W as budgetLeases, X as newUsageTotals, Y as meterUsageEvent, et as assertValidSpend, f as executorEvidenceWriter, g as prepareRetainedExecutor, h as leafContinuationExecutionId, j as traceRootWriter, lt as extractTransportEventIdentity, n as runtimeFailureCause, o as settlementFailureCause, q as createBudgetPool, s as BUDGET_CONTINUATION_TASK, t as reportedFailureCause, u as RetainedExecutionPendingError, ut as parseCanonicalTransportEvent, wt as recordValue, y as retainedExecutorSeamKey } from "./failure-cause-BzjUUsRz.js";
4
+ import { a as errMessage, c as errorText, d as resolveRedactor, l as defaultRedactor, n as detachedSnapshot, r as shareParsedStrings } from "./snapshot-Cgj2Wzi6.js";
4
5
  import { t as assertExecutableAgentProfile } from "./model-policy-JOcg3NRs.js";
6
+ import { a as readCommittedJsonLines, c as appendCommittedJsonLine, l as sha256, r as prepareJsonlAppend, s as withJsonlFileLock, t as isNoEntError } from "./jsonl-file-BeUa5EDx.js";
7
+ import { A as verifiedArtifactChunks, P as deepFreezeCandidate, R as sha256Bytes$1, h as workspaceLimits, i as readAgentCandidateWorkspaceArtifactFiles, m as safeArchivePath } from "./workspace-streams-uB9rRbms.js";
5
8
  import { a as writeAtomicDurableFile, n as assertNoSymlinkDescendant, o as captureRunWriteContext, r as publishExclusiveDurableFile, s as fenceRunWrite, t as appendDurableFile } from "./durable-file-DstVeHcj.js";
6
9
  import { a as withPursuitContext, i as notifyRuntimeHookEvent } from "./runtime-hooks-CsW2pfFk.js";
7
10
  import * as traceContract from "@tangle-network/agent-trace-contract";
8
11
  import { ATTR, OPERATION_NAME_ATTR, toW3CSpanId, toW3CTraceId } from "@tangle-network/agent-trace-contract";
9
- import { AgentEnvironmentCapabilitiesSchema, AgentExactRunControlRefSchema, AgentInteractiveSessionControlClaimAcknowledgementSchema, AgentInteractiveSessionControlClaimSchema, AgentInteractiveSessionPromptAcknowledgementSchema, AgentInteractiveSessionRefSchema, AgentInteractiveSessionStatusSchema, AgentInteractiveSessionStopAcknowledgementSchema, AgentNativeContextContinuationResultSchema, AgentRunCancellationAcknowledgementSchema, AgentRunCancellationRequestSchema, AgentTurnInputSchema, AgentTurnResultSchema, CanonicalStreamEventSchema, ContextTransferReceiptSchema, ContextTransferRequestSchema, InteractionAcknowledgementSchema, InteractionResponseCommandSchema, NativeContextBoundaryProofSchema, NativeContextContinuationRequestSchema, RuntimeEventEnvelopeSchema, TerminalReplayWindowSchema, TerminalSessionRefSchema, agentCandidateArtifactRefSchema, agentCandidateWorkspaceManifestMaterialSchema, agentCandidateWorkspaceSnapshotEvidenceSchema, agentInteractiveSessionControlClaimAcknowledgementMatchesRequest, agentInteractiveSessionControlClaimMatchesRef, agentInteractiveSessionPromptAcknowledgementMatchesCommand, agentInteractiveSessionRefMatchesStart, agentInteractiveSessionRunRef, agentInteractiveSessionStatusMatchesRef, agentInteractiveSessionStopAcknowledgementMatchesCommand, agentNativeContextContinuationResultMatchesRequest, agentProfileSchema, agentRunCancellationAcknowledgementMatchesRequest, agentRunCancellationRequestDigest, agentWorkspaceSnapshotRefDigest, canonicalAgentProfileDigest, canonicalCandidateDigest, contextTransferReceiptMatches, exactAgentInteractiveSessionStart, nativeContextContinuationTurnDigest, sha256DigestSchema, workspaceCheckpointRequestDigest, workspaceCheckpointResultMatchesRequest, workspaceCleanupAcknowledgementMatches, workspaceCleanupRequestDigest } from "@tangle-network/agent-interface";
12
+ import { AgentEnvironmentCapabilitiesSchema, AgentExactRunControlRefSchema, AgentInteractiveSessionControlClaimAcknowledgementSchema, AgentInteractiveSessionControlClaimSchema, AgentInteractiveSessionPromptAcknowledgementSchema, AgentInteractiveSessionRefSchema, AgentInteractiveSessionStatusSchema, AgentInteractiveSessionStopAcknowledgementSchema, AgentNativeContextContinuationResultSchema, AgentRunCancellationAcknowledgementSchema, AgentRunCancellationRequestSchema, AgentTurnInputSchema, AgentTurnResultSchema, ContextTransferReceiptSchema, ContextTransferRequestSchema, InteractionAcknowledgementSchema, InteractionResponseCommandSchema, NativeContextBoundaryProofSchema, NativeContextContinuationRequestSchema, RuntimeEventEnvelopeSchema, TerminalReplayWindowSchema, TerminalSessionRefSchema, agentCandidateArtifactRefSchema, agentCandidateWorkspaceManifestMaterialSchema, agentCandidateWorkspaceSnapshotEvidenceSchema, agentInteractiveSessionControlClaimAcknowledgementMatchesRequest, agentInteractiveSessionControlClaimMatchesRef, agentInteractiveSessionPromptAcknowledgementMatchesCommand, agentInteractiveSessionRefMatchesStart, agentInteractiveSessionRunRef, agentInteractiveSessionStatusMatchesRef, agentInteractiveSessionStopAcknowledgementMatchesCommand, agentNativeContextContinuationResultMatchesRequest, agentProfileSchema, agentRunCancellationAcknowledgementMatchesRequest, agentRunCancellationRequestDigest, agentWorkspaceSnapshotRefDigest, canonicalAgentProfileDigest, canonicalCandidateDigest, contextTransferReceiptMatches, exactAgentInteractiveSessionStart, nativeContextContinuationTurnDigest, sha256DigestSchema, workspaceCheckpointRequestDigest, workspaceCheckpointResultMatchesRequest, workspaceCleanupAcknowledgementMatches, workspaceCleanupRequestDigest } from "@tangle-network/agent-interface";
10
13
  import { isToolSpan, toolSpansToTraceAnalysisStore } from "@tangle-network/agent-eval";
11
14
  import { isRecord } from "@tangle-network/agent-core";
12
- import { collectAgentFinalMessageText, collectAgentResponseText } from "@tangle-network/sandbox";
13
15
  import { z } from "zod";
14
16
  import { redact } from "@tangle-network/agent-eval/traces";
15
- import { AgentTurnFailureSchema } from "@tangle-network/agent-interface/environment-provider";
16
17
  import { createHash, randomUUID } from "node:crypto";
17
- import { setImmediate as setImmediate$1 } from "node:timers/promises";
18
- import { LedgerCanonicalizationError, canonicalString, jsonDocument, merkleLeafHash, merkleRoot, merkleSalt } from "@tangle-network/agent-eval/ledger-core";
19
18
  import { appendFileSync, closeSync, constants, existsSync, fstatSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, statSync, unlinkSync, writeSync } from "node:fs";
20
19
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
21
20
  import { mkdir, open, realpath, stat } from "node:fs/promises";
@@ -381,3200 +380,6 @@ function delay(ms, signal) {
381
380
  signal?.addEventListener("abort", onAbort, { once: true });
382
381
  });
383
382
  }
384
- // src/record-value.ts
385
- function recordValue(value) {
386
- return isRecord(value) ? value : void 0;
387
- }
388
- // src/runtime/harness-usage.ts
389
- /**
390
- * Read and validate one codex `turn.completed` usage record.
391
- *
392
- * Every counter must be a non-negative safe integer. Two cross-field invariants hold, and they are
393
- * what states that the two named counters are SUBSETS of their totals rather than additions to
394
- * them: `cached_input_tokens <= input_tokens` and `reasoning_output_tokens <= output_tokens`.
395
- * Measured against the codex CLI: a turn reporting `output_tokens 1523` with
396
- * `reasoning_output_tokens 1516` answered with about seven tokens of text.
397
- *
398
- * `label` names the surface the record came from, so one message serves both readers.
399
- * Throws `ValidationError` on any violation.
400
- */
401
- function parseCodexUsageRecord(usage, label) {
402
- const record = recordValue(usage);
403
- if (record === void 0) throw new ValidationError(`${label}: usage must be an object, received ${describe$1(usage)}`);
404
- const inputTokens = naturalNumber(record.input_tokens, "input_tokens", label);
405
- const cachedInputTokens = naturalNumber(record.cached_input_tokens, "cached_input_tokens", label);
406
- const outputTokens = naturalNumber(record.output_tokens, "output_tokens", label);
407
- const reasoningOutputTokens = naturalNumber(record.reasoning_output_tokens, "reasoning_output_tokens", label);
408
- const cacheWriteInputTokens = record.cache_write_input_tokens === void 0 ? void 0 : naturalNumber(record.cache_write_input_tokens, "cache_write_input_tokens", label);
409
- if (cachedInputTokens > inputTokens) throw new ValidationError(`${label}: cached_input_tokens exceeds input_tokens`);
410
- if (reasoningOutputTokens > outputTokens) throw new ValidationError(`${label}: reasoning_output_tokens exceeds output_tokens`);
411
- return {
412
- inputTokens,
413
- cachedInputTokens,
414
- outputTokens,
415
- reasoningOutputTokens,
416
- ...cacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens }
417
- };
418
- }
419
- /** Names the wire record every codex decode error is reported against. */
420
- const codexUsageContext = "codex turn.completed";
421
- /**
422
- * codex reports the tokens of a turn inside its own `turn.completed` event, which rides the
423
- * transport either as `{ type: 'raw', data: { type: 'turn.completed', usage: { … } } }` or as
424
- * the provider-normalized Interface envelope `{ type: 'raw', data: { type: 'raw', event: {
425
- * type: 'turn.completed', usage: { … } } } }`. It emits no canonical usage event, so this adapter
426
- * is a codex worker's only usage source.
427
- *
428
- * The record is read by `parseCodexUsageRecord`, the same reader `runLocalHarness` uses on the
429
- * codex CLI's own stdout, so both surfaces hold one field policy and both cross-field invariants.
430
- *
431
- * A `turn.completed` that carries a `usage` member the reader cannot read is a `ValidationError`
432
- * naming the field. A `turn.completed` with no `usage` member reports no usage at all, which
433
- * leaves the turn's tokens unknown rather than zero.
434
- */
435
- const decodeCodexTurnUsage = (event) => {
436
- if (String(event.type ?? "") !== "raw") return void 0;
437
- const envelope = recordValue(event.data);
438
- const data = envelope?.type === "raw" && recordValue(envelope.event) !== void 0 ? recordValue(envelope.event) : envelope;
439
- if (data === void 0 || data.type !== "turn.completed" || data.usage === void 0) return;
440
- const record = parseCodexUsageRecord(data.usage, codexUsageContext);
441
- return {
442
- harness: "codex",
443
- input: record.inputTokens,
444
- output: record.outputTokens,
445
- cachedInput: record.cachedInputTokens,
446
- reasoningOutput: record.reasoningOutputTokens,
447
- ...record.cacheWriteInputTokens === void 0 ? {} : { cacheWriteInput: record.cacheWriteInputTokens }
448
- };
449
- };
450
- /** Claude's adapter forwards one delta per response snapshot, keyed and deduplicated there. */
451
- const decodeClaudeAssistantUsage = (event) => {
452
- if (String(event.type ?? "") !== "raw") return void 0;
453
- const envelope = recordValue(event.data);
454
- const data = envelope?.type === "raw" && recordValue(envelope.event) !== void 0 ? recordValue(envelope.event) : envelope;
455
- if (data?.type !== "assistant.usage") return void 0;
456
- if (typeof data.message_id !== "string" || data.message_id.length === 0) throw new ValidationError("claude assistant.usage: missing response identity");
457
- const usage = recordValue(data.usage);
458
- if (!usage) throw new ValidationError("claude assistant.usage: usage must be an object");
459
- const fresh = naturalNumber(usage.input_tokens, "input_tokens", "claude assistant.usage");
460
- const cachedInput = naturalNumber(usage.cache_read_input_tokens, "cache_read_input_tokens", "claude assistant.usage");
461
- const cacheWriteInput = naturalNumber(usage.cache_creation_input_tokens, "cache_creation_input_tokens", "claude assistant.usage");
462
- const output = naturalNumber(usage.output_tokens, "output_tokens", "claude assistant.usage");
463
- const input = fresh + cachedInput + cacheWriteInput;
464
- if (!Number.isSafeInteger(input)) throw new ValidationError("claude assistant.usage: total input tokens exceed a safe integer");
465
- return {
466
- harness: "claude-code",
467
- input,
468
- output,
469
- cachedInput,
470
- cacheWriteInput
471
- };
472
- };
473
- /**
474
- * The harness → usage-decoder registry. Add a harness by adding one entry.
475
- *
476
- * Keyed by `HarnessType`, the vocabulary the `harness` argument is drawn from, so a key no caller
477
- * can produce does not compile. A harness absent from this registry reports usage through the
478
- * canonical events or not at all.
479
- */
480
- const harnessUsageDecoders = {
481
- codex: decodeCodexTurnUsage,
482
- "claude-code": decodeClaudeAssistantUsage
483
- };
484
- /**
485
- * Decode a sandbox event with one harness's adapter, or `undefined` when the event carries no
486
- * harness-native usage.
487
- *
488
- * A NAMED harness reads with that harness's adapter only, and a named harness with no adapter
489
- * reports nothing. It never falls through to another harness's adapter: a different harness's
490
- * `turn.completed` decoded as codex would either drop the counters codex does not name or fail on
491
- * a field codex requires, and both answers would be about the wrong harness. The composite over
492
- * every registered adapter runs only when the caller cannot name the harness.
493
- *
494
- * Throws `ValidationError` when an adapter recognizes the event as its harness's usage carrier and
495
- * cannot read the numbers.
496
- */
497
- function decodeHarnessUsage(event, harness) {
498
- if (!event || typeof event !== "object") return void 0;
499
- if (harness !== void 0) return harnessUsageDecoders[harness]?.(event);
500
- for (const decode of new Set(Object.values(harnessUsageDecoders))) {
501
- const usage = decode(event);
502
- if (usage !== void 0) return usage;
503
- }
504
- }
505
- function naturalNumber(value, field, label) {
506
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new ValidationError(`${label}: usage.${field} must be a non-negative safe integer, received ${describe$1(value)}`);
507
- return value;
508
- }
509
- function describe$1(value) {
510
- if (value === null) return "null";
511
- if (typeof value === "number" || typeof value === "boolean") return String(value);
512
- if (typeof value === "string") return JSON.stringify(value);
513
- if (Array.isArray(value)) return "an array";
514
- return typeof value;
515
- }
516
- // src/runtime/sandbox-events.ts
517
- /**
518
- * Forward a sandbox event to an optional observer without letting observer
519
- * behavior affect the run. The observer receives a defensive copy, synchronous
520
- * throws are swallowed, and returned promises are deliberately not awaited.
521
- */
522
- function notifySandboxEventObserver(event, observer, meta) {
523
- if (!observer) return;
524
- try {
525
- const result = observer(cloneEventForObserver(event), meta);
526
- if (result && typeof result.then === "function") result.then(void 0, () => {});
527
- } catch {}
528
- }
529
- function cloneEventForObserver(event) {
530
- try {
531
- return structuredClone(event);
532
- } catch {
533
- return copyPlainSpine(event, /* @__PURE__ */ new WeakMap());
534
- }
535
- }
536
- function copyPlainSpine(value, seen) {
537
- if (value === null || typeof value !== "object") return value;
538
- const existing = seen.get(value);
539
- if (existing !== void 0) return existing;
540
- if (Array.isArray(value)) {
541
- const copy = [];
542
- seen.set(value, copy);
543
- for (const item of value) copy.push(copyPlainSpine(item, seen));
544
- return copy;
545
- }
546
- const proto = Object.getPrototypeOf(value);
547
- if (proto !== Object.prototype && proto !== null) return {};
548
- const copy = {};
549
- seen.set(value, copy);
550
- for (const [key, child] of Object.entries(value)) copy[key] = copyPlainSpine(child, seen);
551
- return copy;
552
- }
553
- /**
554
- * Read the served execution identity off one Sandbox event: the platform reports `effectiveBackend`
555
- * on `execution.started` and again on the terminal event. Absence stays unknown — a request is
556
- * not a receipt, so nothing here may be inferred from what was asked for.
557
- */
558
- function sandboxEventServedBackend(event) {
559
- const served = recordValue(recordValue(event?.data)?.effectiveBackend);
560
- const named = [
561
- "provider",
562
- "model",
563
- "source"
564
- ].flatMap((key) => {
565
- const value = firstString(served?.[key]);
566
- return value === void 0 ? [] : [[key, value]];
567
- });
568
- if (!named.some(([key]) => key !== "source")) return void 0;
569
- return Object.fromEntries(named);
570
- }
571
- /**
572
- * Fail the execution when the platform reports serving a model other than the exact one asked for.
573
- * Measured motive (agent-runtime#892): 6 of 6 boxes declaring `zai-coding-plan/glm-5.2` reported
574
- * `deepseek/deepseek-v4-flash` (`source: "environment"`) while the materialization receipt recorded
575
- * the declared model as known; only reading the report back detects the substitution.
576
- * Silent when the platform reports no served model: unobserved stays unobserved.
577
- */
578
- function assertSandboxServedModel(event, expected) {
579
- const wanted = expected?.model?.trim();
580
- const served = wanted ? sandboxEventServedBackend(event) : void 0;
581
- if (!wanted || served?.model === void 0) return;
582
- if (sameModelId(served.model, wanted, [served.provider, expected?.provider])) return;
583
- const attribution = Object.entries({
584
- provider: served.provider,
585
- source: served.source
586
- }).flatMap(([key, value]) => value === void 0 ? [] : [`${key} ${JSON.stringify(value)}`]);
587
- const detail = attribution.length > 0 ? ` (${attribution.join(", ")})` : "";
588
- throw new Error(`sandbox served model ${JSON.stringify(served.model)}${detail} instead of the exact profile model ${JSON.stringify(wanted)}`);
589
- }
590
- /**
591
- * Do two model ids name the same model, allowing for routing prefixes?
592
- *
593
- * Either side may spell the model bare (`glm-5.2`), provider-qualified
594
- * (`zai-coding-plan/glm-5.2`), or route-qualified, and the platform reports the provider in its
595
- * own field rather than always in the id. So the comparison drops any known provider prefix and
596
- * then accepts a `/`-boundary suffix match: a longer route to the SAME leaf model is a routing
597
- * difference, not a substitution.
598
- *
599
- * What it deliberately does NOT absorb is a different leaf: `glm-5.2` against `glm-5.3` stays a
600
- * mismatch, which is the substitution measured directly against the provider API (a request for
601
- * `glm-5.2` was served `glm-5.3`). A version suffix is the whole difference between two
602
- * instruments, so nothing here may treat it as noise.
603
- */
604
- function sameModelId(served, wanted, providers) {
605
- const a = stripProviderPrefix(served, providers);
606
- const b = stripProviderPrefix(wanted, providers);
607
- return a === b || a.endsWith(`/${b}`) || b.endsWith(`/${a}`);
608
- }
609
- function stripProviderPrefix(id, providers) {
610
- let out = id.trim().toLowerCase();
611
- for (const provider of providers) {
612
- const prefix = provider?.trim().toLowerCase();
613
- if (prefix && out.startsWith(`${prefix}/`)) out = out.slice(prefix.length + 1);
614
- }
615
- return out;
616
- }
617
- function firstString(...values) {
618
- return values.find((value) => typeof value === "string" && value.length > 0);
619
- }
620
- const terminalTypes = /* @__PURE__ */ new Set([
621
- "message.completed",
622
- "result",
623
- "final",
624
- "done"
625
- ]);
626
- /** True for an event type that ends a sandbox turn. */
627
- function isSandboxTerminalEvent(type) {
628
- return terminalTypes.has(type);
629
- }
630
- /**
631
- * Which member of a terminal event's `data` carries its usage receipt.
632
- *
633
- * The one place the terminal types legitimately differ, stated rather than implied by two lists:
634
- * sandbox 0.4.0's `done` reports under `tokenUsage` with the cost at the top level, every other
635
- * terminal type reports under `usage`.
636
- */
637
- function sandboxTerminalUsageField(type) {
638
- return type === "done" ? "tokenUsage" : "usage";
639
- }
640
- /**
641
- * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when
642
- * the event carries usage/cost data. Returns `undefined` for non-cost events
643
- * so the kernel can iterate the full stream without branching.
644
- *
645
- * Pure by contract: it never throws on a failed run. The terminal truth
646
- * boundary is the public Sandbox outcome tracker, applied after the complete
647
- * stream. Post-hoc readers — {@link sumSandboxUsage}, the
648
- * analyst trace store, the chat projection — must stay able to read a failed
649
- * turn's events, which is when reading them matters most.
650
- *
651
- * Canonical cost-carrying types observed in the wild:
652
- * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`
653
- * - `message.completed` / `result` — `data: { usage: { inputTokens,
654
- * outputTokens, totalCostUsd? } }`
655
- * - `cost.usage` / `usage` — same shape under a dedicated type
656
- *
657
- * Numeric coercion is strict: `Number.isFinite` gates every accumulator write
658
- * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.
659
- */
660
- function extractLlmCallEvent(event, agentRunName) {
661
- const data = canonicalUsageData(event);
662
- return data === void 0 ? void 0 : buildLlmCall(data, agentRunName);
663
- }
664
- function canonicalUsageData(event) {
665
- if (!event || typeof event !== "object") return void 0;
666
- const type = String(event.type ?? "");
667
- const data = event.data && typeof event.data === "object" ? event.data : {};
668
- if (type === "llm_call" || type === "cost.usage" || type === "usage") return usageReceiptData(data, recordValue(data.usage));
669
- if (type === "error" || isSandboxTerminalEvent(type)) {
670
- const field = type === "error" ? "tokenUsage" : sandboxTerminalUsageField(type);
671
- return usageReceiptData(data, recordValue(data[field]) ?? recordValue(data[field === "usage" ? "tokenUsage" : "usage"]));
672
- }
673
- }
674
- /** A {@link SandboxUsageLedger} for one worker. Pass the worker's harness to decode with that
675
- * harness's adapter; omit it to try every registered adapter. */
676
- function createSandboxUsageLedger(harness) {
677
- let held = [];
678
- let sawCanonical = false;
679
- const credited = {};
680
- const receipts = /* @__PURE__ */ new Map();
681
- const settledTokens = zeroTokenUsage();
682
- let turnTokens = zeroTokenUsage();
683
- let turnUsageUnknown = false;
684
- let settledUsage;
685
- return {
686
- settledUsage: () => settledUsage === void 0 ? void 0 : { ...settledUsage },
687
- tokenUsage() {
688
- const total = cloneTokenUsage(settledTokens);
689
- addTokenUsage(total, turnTokens);
690
- return total;
691
- },
692
- observe(event, agentRunName) {
693
- const call = extractLlmCallEvent(event, agentRunName);
694
- if (call) {
695
- sawCanonical = true;
696
- const receipt = creditCanonicalUsage(event, call, credited, receipts, (receipt, mode) => {
697
- refineCanonicalTokens(turnTokens, call, receipt, mode);
698
- });
699
- if (receipt?.tokensKnown === false) {
700
- turnTokens.tokensKnown = false;
701
- turnUsageUnknown = true;
702
- }
703
- return receipt;
704
- }
705
- let usage;
706
- try {
707
- usage = decodeHarnessUsage(event, harness);
708
- } catch (err) {
709
- turnUsageUnknown = true;
710
- return {
711
- type: "llm_call",
712
- model: agentRunName,
713
- tokensKnown: false,
714
- usdKnown: false,
715
- tokensUnknownReason: err instanceof Error ? err.message : String(err)
716
- };
717
- }
718
- if (usage) held.push(usage);
719
- },
720
- settleTurn(agentRunName) {
721
- const reports = held;
722
- const canonical = sawCanonical;
723
- held = [];
724
- sawCanonical = false;
725
- const receipt = !canonical && reports.length > 0 ? harnessUsageLlmCall(reports, agentRunName) : void 0;
726
- if (receipt) creditCanonicalUsage({
727
- type: "llm_call",
728
- data: {}
729
- }, receipt, credited, receipts, (credit, mode) => refineCanonicalTokens(turnTokens, receipt, credit, mode));
730
- settledUsage = !turnUsageUnknown && credited.tokensIn !== void 0 && credited.tokensOut !== void 0 ? {
731
- inputTokens: credited.tokensIn,
732
- outputTokens: credited.tokensOut,
733
- ...credited.totalTokens === void 0 ? {} : { totalTokens: credited.totalTokens },
734
- ...credited.reasoningTokens === void 0 ? {} : { reasoningTokens: credited.reasoningTokens },
735
- ...credited["promptCache.readTokens"] === void 0 ? {} : { cacheReadInputTokens: credited["promptCache.readTokens"] },
736
- ...credited["promptCache.writeTokens"] === void 0 ? {} : { cacheCreationInputTokens: credited["promptCache.writeTokens"] },
737
- ...credited.costUsd === void 0 ? {} : { cost: credited.costUsd }
738
- } : void 0;
739
- for (const key of Object.keys(credited)) delete credited[key];
740
- receipts.clear();
741
- addTokenUsage(settledTokens, turnTokens);
742
- turnTokens = zeroTokenUsage();
743
- turnUsageUnknown = false;
744
- return receipt;
745
- }
746
- };
747
- }
748
- /** Shared receipt semantics; an undeclared nonterminal observation is not a cumulative total. */
749
- function canonicalSandboxUsageMode(event) {
750
- const declaredMode = recordValue(event.data)?.usageMode;
751
- return declaredMode === "delta" || declaredMode === "cumulative" ? declaredMode : declaredMode === void 0 && event.type === "llm_call" ? "delta" : declaredMode === void 0 && (event.type === "error" || isSandboxTerminalEvent(String(event.type))) ? "cumulative" : void 0;
752
- }
753
- /** Terminal totals replace already credited usage; only per-call receipts add to it. */
754
- function creditCanonicalUsage(event, call, credited, receipts, onCredit) {
755
- const mode = canonicalSandboxUsageMode(event);
756
- const result = { ...call };
757
- const data = recordValue(event.data) ?? {};
758
- const usageData = canonicalUsageData(event) ?? {};
759
- const supplemental = Object.fromEntries(["totalTokens", "reasoningTokens"].flatMap((key) => {
760
- const value = usageData[key];
761
- return value === void 0 ? [] : [[key, typeof value === "number" && Number.isFinite(value) ? value : null]];
762
- }));
763
- const explicitTokensUnknown = [
764
- data,
765
- recordValue(data.usage),
766
- recordValue(data.tokenUsage)
767
- ].some((receipt) => ["tokensKnown", "tokens_known"].some((key) => receipt?.[key] !== void 0 && receipt[key] !== true));
768
- if (call.tokensIn === void 0 && call.tokensOut === void 0 && !call.tokensUnknownReason && !explicitTokensUnknown) delete result.tokensKnown;
769
- let reason = mode === void 0 ? "usage receipt has no declared delta or cumulative semantics" : void 0;
770
- if (event.id) {
771
- const signature = JSON.stringify({
772
- mode,
773
- call,
774
- supplemental
775
- });
776
- const previous = receipts.get(event.id);
777
- if (previous === signature) return void 0;
778
- if (previous !== void 0) return {
779
- type: "llm_call",
780
- model: call.model,
781
- tokensKnown: false,
782
- usdKnown: false,
783
- tokensUnknownReason: `usage receipt ${event.id} changed after it was credited`
784
- };
785
- receipts.set(event.id, signature);
786
- }
787
- const credit = (key, value) => {
788
- const previous = credited[key] ?? 0;
789
- if (!Number.isFinite(value) || value < 0) {
790
- reason = `usage receipt has an invalid ${key}`;
791
- return 0;
792
- }
793
- if (mode === "delta") {
794
- credited[key] = previous + value;
795
- return value;
796
- }
797
- if (value < previous) reason = `cumulative usage ${key} is below already credited usage`;
798
- credited[key] = Math.max(previous, value);
799
- return Math.max(0, value - previous);
800
- };
801
- for (const key of [
802
- "tokensIn",
803
- "tokensOut",
804
- "costUsd",
805
- "estimatedCostUsd"
806
- ]) if (call[key] !== void 0) result[key] = credit(key, call[key]);
807
- for (const [key, value] of Object.entries(supplemental)) if (typeof value !== "number") reason = `usage receipt has an invalid ${key}`;
808
- else credit(key, value);
809
- if (call.promptCache) result.promptCache = Object.fromEntries(Object.entries(call.promptCache).map(([key, value]) => [key, typeof value === "number" ? credit(`promptCache.${key}`, value) : value]));
810
- if (reason !== void 0) {
811
- result.usdKnown = false;
812
- if (call.tokensIn !== void 0 || call.tokensOut !== void 0) {
813
- result.tokensKnown = false;
814
- result.tokensUnknownReason = reason;
815
- }
816
- }
817
- onCredit(result, mode);
818
- return result;
819
- }
820
- /** Cache partitions belong to the original receipt, not the smaller incremental charge. */
821
- function refineCanonicalTokens(tokens, call, receipt, mode) {
822
- const input = receipt.tokensIn ?? 0;
823
- const output = receipt.tokensOut ?? 0;
824
- const cache = promptCacheTokenClasses(call.tokensIn, call.promptCache);
825
- if (mode !== "cumulative") {
826
- addTokenUsage(tokens, {
827
- input,
828
- output,
829
- ...cache
830
- });
831
- return;
832
- }
833
- tokens.input += input;
834
- tokens.output += output;
835
- if (cache.freshInput !== void 0 && call.tokensIn === tokens.input && receipt.tokensKnown !== false) {
836
- tokens.freshInput = cache.freshInput;
837
- tokens.cacheRead = cache.cacheRead;
838
- tokens.cacheWrite = cache.cacheWrite;
839
- delete tokens.cacheBreakdownKnown;
840
- return;
841
- }
842
- if (cache.cacheRead !== void 0) tokens.cacheRead = Math.max(tokens.cacheRead ?? 0, cache.cacheRead);
843
- if (cache.cacheWrite !== void 0) tokens.cacheWrite = Math.max(tokens.cacheWrite ?? 0, cache.cacheWrite);
844
- if ((tokens.freshInput ?? 0) + (tokens.cacheRead ?? 0) + (tokens.cacheWrite ?? 0) > tokens.input) {
845
- delete tokens.freshInput;
846
- delete tokens.cacheRead;
847
- delete tokens.cacheWrite;
848
- }
849
- if (input > 0 || cache.cacheBreakdownKnown === false) tokens.cacheBreakdownKnown = false;
850
- }
851
- /**
852
- * Fold harness-native usage reports into the canonical `llm_call` the accounting paths read.
853
- *
854
- * Every counter a `HarnessUsage` carries beside `input` and `output` CLASSIFIES one of those two
855
- * totals, so none of them is added to the total it describes:
856
- *
857
- * - the prompt-cache counters classify `input` and ride `promptCache`, the convention the
858
- * terminal `tokenUsage` branch above already follows and `promptCacheTokenClasses` folds,
859
- * where `freshInput = input - cacheRead - cacheWrite`;
860
- * - `reasoningOutput` classifies `output`. Codex counts its reasoning tokens INSIDE
861
- * `output_tokens` — a turn reporting `output_tokens 1523` with `reasoning_output_tokens 1516`
862
- * answered with about seven tokens of text — and `parseCodexUsageRecord` holds that as an
863
- * invariant. The canonical `llm_call` carries no reasoning class, so the count is not
864
- * forwarded to {@link buildLlmCall}. Canonical output totals already include reasoning.
865
- *
866
- * A counter no report carried stays absent, because a zero would claim the provider measured none.
867
- */
868
- function harnessUsageLlmCall(reports, agentRunName) {
869
- let inputTokens = 0;
870
- let outputTokens = 0;
871
- let readTokens;
872
- let writeTokens;
873
- for (const report of reports) {
874
- inputTokens += report.input;
875
- outputTokens += report.output;
876
- if (report.cachedInput !== void 0) readTokens = (readTokens ?? 0) + report.cachedInput;
877
- if (report.cacheWriteInput !== void 0) writeTokens = (writeTokens ?? 0) + report.cacheWriteInput;
878
- }
879
- const promptCache = {};
880
- if (readTokens !== void 0) promptCache.readTokens = readTokens;
881
- if (writeTokens !== void 0) promptCache.writeTokens = writeTokens;
882
- return buildLlmCall({
883
- inputTokens,
884
- outputTokens,
885
- ...Object.keys(promptCache).length > 0 ? { promptCache } : {}
886
- }, agentRunName);
887
- }
888
- /**
889
- * Sum the token usage + USD cost of a sandbox turn's events — the one honest way to meter an
890
- * `openSandboxRun` cell. Folds a {@link SandboxUsageLedger} over the stream, so it reads usage off
891
- * EVERY backend event shape — the canonical events plus a harness that reports usage only in its
892
- * own event — and a `runProfileMatrix` dispatch can report it to `ctx.cost`:
893
- *
894
- * receipt: (turn) => {
895
- * const u = sumSandboxUsage(turn.events)
896
- * return { model, inputTokens: u.input, outputTokens: u.output,
897
- * ...(u.tokensKnown === false ? { usageUnknown: true } : {}),
898
- * ...(u.usdKnown !== false && u.costUsd > 0 ? { actualCostUsd: u.costUsd } : {}),
899
- * ...(u.usdKnown === false ? { costUnknown: true } : {}),
900
- * ...(u.estimatedCostUsd !== undefined ? { estimatedCostUsd: u.estimatedCostUsd } : {}) }
901
- * }
902
- *
903
- * Without this a cell reads `{tokens:0, cost:0}` and the backend-integrity guard correctly aborts the
904
- * matrix as a stub. `agentRunName` is the fallback model label for cost-only events (default `'agent'`).
905
- *
906
- * Pure by contract, like the ledger it folds: it never throws. A harness receipt the ledger cannot
907
- * read leaves the result at `tokensKnown: false` with `tokensUnknownReason` carrying the decode
908
- * message — an unreadable receipt is a different fact from a turn that reported no usage, and a
909
- * post-hoc reader that threw would lose the whole failed turn it exists to report.
910
- */
911
- function sumSandboxUsage(events, agentRunName = "agent") {
912
- let input = 0;
913
- let output = 0;
914
- let costUsd = 0;
915
- let estimatedCostUsd = 0;
916
- let sawEstimate = false;
917
- let sawCall = false;
918
- let sawTokenReceipt = false;
919
- let tokensKnown = true;
920
- let usdKnown = true;
921
- let unreadable;
922
- const ledger = createSandboxUsageLedger();
923
- const credit = (call) => {
924
- sawCall = true;
925
- sawTokenReceipt ||= call.tokensIn !== void 0 || call.tokensOut !== void 0;
926
- input += call.tokensIn ?? 0;
927
- output += call.tokensOut ?? 0;
928
- costUsd += call.costUsd ?? 0;
929
- if (call.tokensKnown === false) tokensKnown = false;
930
- if (call.usdKnown === false) usdKnown = false;
931
- unreadable ??= call.tokensUnknownReason;
932
- if (call.estimatedCostUsd !== void 0) {
933
- estimatedCostUsd += call.estimatedCostUsd;
934
- sawEstimate = true;
935
- }
936
- };
937
- for (const ev of events) {
938
- const call = ledger.observe(ev, agentRunName);
939
- if (call) credit(call);
940
- }
941
- const harnessCall = ledger.settleTurn(agentRunName);
942
- if (harnessCall) credit(harnessCall);
943
- return {
944
- input,
945
- output,
946
- costUsd,
947
- ...sawTokenReceipt && tokensKnown ? {} : { tokensKnown: false },
948
- ...sawCall && usdKnown ? {} : { usdKnown: false },
949
- ...sawEstimate ? { estimatedCostUsd } : {},
950
- ...unreadable === void 0 ? {} : { tokensUnknownReason: unreadable }
951
- };
952
- }
953
- function usageReceiptData(envelope, usage = {}) {
954
- const merged = {
955
- ...usage,
956
- ...envelope
957
- };
958
- for (const aliases of [
959
- [
960
- "tokensIn",
961
- "inputTokens",
962
- "prompt_tokens"
963
- ],
964
- [
965
- "tokensOut",
966
- "outputTokens",
967
- "completion_tokens"
968
- ],
969
- [
970
- "costUsd",
971
- "totalCostUsd",
972
- "cost_usd",
973
- "cost"
974
- ],
975
- ["costProvenance", "cost_provenance"]
976
- ]) {
977
- const key = aliases.find((alias) => envelope[alias] !== void 0);
978
- if (key === void 0) continue;
979
- for (const alias of aliases) delete merged[alias];
980
- merged[key] = envelope[key];
981
- }
982
- for (const aliases of [["tokensKnown", "tokens_known"], [
983
- "costKnown",
984
- "cost_known",
985
- "usdKnown",
986
- "usd_known"
987
- ]]) {
988
- const values = [usage, envelope].flatMap((receipt) => aliases.map((key) => receipt[key]).filter((value) => value !== void 0));
989
- if (values.length > 0) merged[aliases[0]] = values.every((value) => value === true);
990
- }
991
- return merged;
992
- }
993
- function buildLlmCall(data, agentRunName) {
994
- const tokensIn = pickFiniteNumber(data, [
995
- "tokensIn",
996
- "inputTokens",
997
- "prompt_tokens"
998
- ]);
999
- const outputTokens = pickFiniteNumber(data, [
1000
- "tokensOut",
1001
- "outputTokens",
1002
- "completion_tokens"
1003
- ]);
1004
- const reasoningTokens = pickFiniteNumber(data, ["reasoningTokens"]);
1005
- const tokensOut = outputTokens ?? reasoningTokens;
1006
- const reportedCostUsd = pickFiniteNumber(data, [
1007
- "costUsd",
1008
- "totalCostUsd",
1009
- "cost_usd",
1010
- "cost"
1011
- ]);
1012
- const explicitTokensKnown = data.tokensKnown ?? data.tokens_known;
1013
- const explicitCostKnown = data.costKnown ?? data.cost_known ?? data.usdKnown ?? data.usd_known;
1014
- const costProvenance = data.costProvenance ?? data.cost_provenance;
1015
- const explicitEstimate = pickFiniteNumber(data, ["estimatedCostUsd", "estimated_cost_usd"]);
1016
- const catalogEstimate = costProvenance === "catalog-estimate" ? explicitEstimate ?? reportedCostUsd : explicitEstimate;
1017
- const costUsd = costProvenance === "catalog-estimate" ? void 0 : reportedCostUsd;
1018
- const promptCache = readPromptCacheUsage(data);
1019
- const tokensKnown = explicitTokensKnown !== false && tokensIn !== void 0 && outputTokens !== void 0;
1020
- const usdKnown = explicitCostKnown !== false && costUsd !== void 0 && (costProvenance === void 0 || costProvenance === "provider-receipt" || costProvenance === "billing-receipt");
1021
- const tools = readOfferedTools(data.tools);
1022
- if (tokensIn === void 0 && tokensOut === void 0 && costUsd === void 0 && catalogEstimate === void 0 && promptCache === void 0 && tools === void 0 && explicitTokensKnown !== false && explicitCostKnown !== false) return;
1023
- const event = {
1024
- type: "llm_call",
1025
- model: typeof data.model === "string" && data.model.length > 0 ? data.model : agentRunName
1026
- };
1027
- if (tokensIn !== void 0) event.tokensIn = tokensIn;
1028
- if (tokensOut !== void 0) event.tokensOut = tokensOut;
1029
- if (!tokensKnown) event.tokensKnown = false;
1030
- if (costUsd !== void 0) event.costUsd = costUsd;
1031
- if (!usdKnown) event.usdKnown = false;
1032
- if (catalogEstimate !== void 0) event.estimatedCostUsd = catalogEstimate;
1033
- if (promptCache !== void 0) event.promptCache = promptCache;
1034
- if (tools !== void 0) event.tools = tools;
1035
- return event;
1036
- }
1037
- /** `data.tools`, when it is a non-empty array of non-empty tool-name strings. Anything else
1038
- * (absent, empty, wrong element type) is treated as "the producer did not report its offered
1039
- * set" — never a placeholder empty list, which would read as "offered zero tools" downstream. */
1040
- function readOfferedTools(value) {
1041
- if (!Array.isArray(value) || value.length === 0) return void 0;
1042
- const names = value.filter((v) => typeof v === "string" && v.length > 0);
1043
- return names.length === value.length ? names : void 0;
1044
- }
1045
- /**
1046
- * Every wire spelling of a prompt-cache READ counter, in precedence order: the canonical name
1047
- * first, then the spellings observed on the paths that reach this extractor — the sandbox terminal
1048
- * `tokenUsage` record, the cli-bridge OpenAI-compatible usage object (which normalizes every
1049
- * backend to Anthropic's `cache_read_input_tokens`), Anthropic verbatim, OpenAI's
1050
- * `prompt_tokens_details.cached_tokens`, and DeepSeek's `prompt_cache_hit_tokens`.
1051
- */
1052
- const PROMPT_CACHE_READ_KEYS = [
1053
- "readTokens",
1054
- "read_tokens",
1055
- "cacheReadInputTokens",
1056
- "cache_read_input_tokens",
1057
- "cacheRead",
1058
- "cache_read",
1059
- "cachedInputTokens",
1060
- "cachedTokens",
1061
- "cached_tokens",
1062
- "prompt_cache_hit_tokens"
1063
- ];
1064
- /**
1065
- * Every wire spelling of a prompt-cache WRITE counter. OpenAI reports no write counter at all, so
1066
- * an OpenAI-shaped usage record matches nothing here and the write stays absent — which is the
1067
- * point: absent is not zero.
1068
- */
1069
- const PROMPT_CACHE_WRITE_KEYS = [
1070
- "writeTokens",
1071
- "write_tokens",
1072
- "cacheWriteInputTokens",
1073
- "cache_write_input_tokens",
1074
- "cacheCreationInputTokens",
1075
- "cache_creation_input_tokens",
1076
- "cacheWrite",
1077
- "cache_write"
1078
- ];
1079
- const PROMPT_CACHE_MISS_KEYS = [
1080
- "missTokens",
1081
- "miss_tokens",
1082
- "prompt_cache_miss_tokens"
1083
- ];
1084
- const PROMPT_CACHE_SAVINGS_KEYS = ["readSavingsUsd", "read_savings_usd"];
1085
- /**
1086
- * Read the provider's own prompt-cache accounting off one usage-bearing event payload, in the
1087
- * `PromptCacheUsage` vocabulary the router, driver, and chat-client paths already speak.
1088
- *
1089
- * Two sources, one record. A payload that already carries a `promptCache` / `prompt_cache` object
1090
- * is forwarded VERBATIM — those fields are the provider's own report, and an unrecognized one must
1091
- * stay visible rather than be bucketed away. On top of that, flat provider counters are translated
1092
- * to the canonical names, filling only the names the verbatim object did not already define.
1093
- *
1094
- * A counter the payload does not carry is left out. Defaulting it to zero would assert the provider
1095
- * measured no cache, which is a different fact from a provider that reported nothing — and it is
1096
- * the fact the budget reads to decide whether its charge is a measurement or a bound.
1097
- */
1098
- function readPromptCacheUsage(data) {
1099
- const declared = finiteMetadata(data.promptCache ?? data.prompt_cache);
1100
- const nested = recordValue(data.promptCache) ?? recordValue(data.prompt_cache);
1101
- const sources = [
1102
- nested,
1103
- data,
1104
- recordValue(data.prompt_tokens_details)
1105
- ].filter((s) => s !== void 0);
1106
- const canonical = {};
1107
- const readTokens = pickTokenCount(sources, PROMPT_CACHE_READ_KEYS);
1108
- const writeTokens = pickTokenCount(sources, PROMPT_CACHE_WRITE_KEYS);
1109
- const missTokens = pickTokenCount(sources, PROMPT_CACHE_MISS_KEYS);
1110
- const readSavingsUsd = pickNonNegativeNumber(sources, PROMPT_CACHE_SAVINGS_KEYS);
1111
- const status = nested !== void 0 && typeof nested.status === "string" ? nested.status : void 0;
1112
- if (readTokens !== void 0) canonical.readTokens = readTokens;
1113
- if (writeTokens !== void 0) canonical.writeTokens = writeTokens;
1114
- if (missTokens !== void 0) canonical.missTokens = missTokens;
1115
- if (readSavingsUsd !== void 0) canonical.readSavingsUsd = readSavingsUsd;
1116
- if (status !== void 0) canonical.status = status;
1117
- const merged = {
1118
- ...canonical,
1119
- ...declared
1120
- };
1121
- return Object.keys(merged).length > 0 ? merged : void 0;
1122
- }
1123
- function pickTokenCount(sources, keys) {
1124
- for (const key of keys) for (const source of sources) {
1125
- const value = source[key];
1126
- if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) return value;
1127
- }
1128
- }
1129
- function pickNonNegativeNumber(sources, keys) {
1130
- for (const key of keys) for (const source of sources) {
1131
- const value = source[key];
1132
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
1133
- }
1134
- }
1135
- function finiteMetadata(value) {
1136
- if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1137
- const result = {};
1138
- for (const [key, entry] of Object.entries(value)) if (typeof entry === "number" && Number.isFinite(entry) || typeof entry === "string") result[key] = entry;
1139
- return Object.keys(result).length > 0 ? result : void 0;
1140
- }
1141
- function pickFiniteNumber(data, keys) {
1142
- for (const key of keys) {
1143
- const value = data[key];
1144
- if (typeof value === "number" && Number.isFinite(value)) return value;
1145
- }
1146
- }
1147
- /**
1148
- * Collect one turn's events, keeping only the newest snapshot of each streamed text or
1149
- * reasoning part.
1150
- *
1151
- * opencode sends `message.part.updated` once per token, and every frame carries the part's
1152
- * whole cumulative text, so keeping every frame holds O(L²) bytes per turn: two concurrent
1153
- * audit workflows took the platform API from 459 MB to an OOM at 1.95 GB in 11 minutes.
1154
- * A snapshot (a frame whose part carries `text`) whose text extends the previous snapshot
1155
- * of the same part replaces it and moves to the newest position, which is the rule the
1156
- * Sandbox SDK's text collectors already apply, so the collected text is unchanged. The kept
1157
- * frame drops its `delta`, because its text already holds every earlier increment and a
1158
- * reader that joins `delta ?? part.text` across frames would otherwise read only the last
1159
- * one. Delta-only frames and every other event are kept as they arrived.
1160
- */
1161
- function createSandboxTurnEventCollector() {
1162
- const slots = [];
1163
- const snapshots = /* @__PURE__ */ new Map();
1164
- return {
1165
- push(event) {
1166
- const snapshot = partSnapshot(event);
1167
- if (!snapshot) {
1168
- slots.push(event);
1169
- return;
1170
- }
1171
- const previous = snapshots.get(snapshot.key);
1172
- const extends_ = previous !== void 0 && snapshot.text.startsWith(previous.text);
1173
- if (extends_) slots[previous.at] = void 0;
1174
- snapshots.set(snapshot.key, {
1175
- at: slots.length,
1176
- text: snapshot.text
1177
- });
1178
- slots.push(extends_ ? withoutDelta(event) : event);
1179
- },
1180
- events() {
1181
- return slots.filter((event) => event !== void 0);
1182
- }
1183
- };
1184
- }
1185
- function partSnapshot(event) {
1186
- if (event?.type !== "message.part.updated") return void 0;
1187
- const data = asRecord(event.data);
1188
- const part = asRecord(data?.part) ?? asRecord(asRecord(data?.properties)?.part);
1189
- if (!part || typeof part.id !== "string") return void 0;
1190
- if (part.type !== "text" && part.type !== "reasoning" && part.type !== "thinking") return void 0;
1191
- const text = typeof part.text === "string" ? part.text : typeof part.content === "string" ? part.content : void 0;
1192
- return text === void 0 ? void 0 : {
1193
- key: `${part.type}:${part.id}`,
1194
- text
1195
- };
1196
- }
1197
- function withoutDelta(event) {
1198
- const data = asRecord(event.data);
1199
- if (!data || !("delta" in data)) return event;
1200
- const { delta: _delta, ...rest } = data;
1201
- return {
1202
- ...event,
1203
- data: rest
1204
- };
1205
- }
1206
- function asRecord(value) {
1207
- return value !== null && typeof value === "object" ? value : void 0;
1208
- }
1209
- // src/runtime/timestamps.ts
1210
- const validTimestamp = "2026-01-01T00:00:00.000Z";
1211
- /** Validate a timestamp with the public runtime envelope contract. */
1212
- function assertRuntimeTimestamp(value, label) {
1213
- try {
1214
- RuntimeEventEnvelopeSchema.parse({
1215
- runId: "runtime",
1216
- eventId: "event",
1217
- sequence: 0,
1218
- occurredAt: value,
1219
- receivedAt: validTimestamp,
1220
- event: {
1221
- type: "status",
1222
- status: "processing"
1223
- }
1224
- });
1225
- } catch (error) {
1226
- throw new Error(`${label} must be a valid ISO timestamp`, { cause: error });
1227
- }
1228
- }
1229
- // src/runtime/sandbox-transport-events.ts
1230
- /** Canonical Sandbox/provider wire-envelope decoding and transport identity. */
1231
- const CANONICAL_STREAM_EVENT_TYPES = /* @__PURE__ */ new Set([
1232
- "child-task",
1233
- "message.part.updated",
1234
- "tool-heartbeat",
1235
- "tool-slow",
1236
- "model-processing",
1237
- "status",
1238
- "warning",
1239
- "raw",
1240
- "session.updated",
1241
- "interaction",
1242
- "interaction.cancel",
1243
- "plan.submitted"
1244
- ]);
1245
- /** Decode one known Agent Interface event from a Sandbox event. */
1246
- function canonicalStreamEventFromSandboxEvent(event) {
1247
- if (!event || typeof event !== "object") return void 0;
1248
- const type = String(event.type ?? "");
1249
- const data = event.data && typeof event.data === "object" ? event.data : {};
1250
- const normalized = data.normalized;
1251
- if (normalized === void 0 && !CANONICAL_STREAM_EVENT_TYPES.has(type)) return void 0;
1252
- return parseCanonicalTransportEvent(type, data, normalized, "sandbox");
1253
- }
1254
- /**
1255
- * Transport type whose payload is the harness's OWN event, passed through verbatim.
1256
- *
1257
- * A `raw` payload names a harness-native type from a vocabulary the canonical schema does not
1258
- * define (codex emits `thread.started` / `turn.started` / `turn.completed`). The transport type is
1259
- * therefore the only canonical statement such an event makes, and its payload must never be read
1260
- * as a canonical type: the mismatch guard below exists to catch a producer that MISLABELS a
1261
- * canonical event, which a harness-native payload cannot do. A payload that does satisfy the
1262
- * canonical `raw` shape (`backend` + `event`) still parses; anything else is not a canonical
1263
- * event, and the caller reads it off the transport event instead.
1264
- */
1265
- const harnessNativePayloadType = "raw";
1266
- /** Parse canonical payload fields without treating transport identity as event data. */
1267
- function parseCanonicalTransportEvent(type, data, normalized, source) {
1268
- if (!isRecord(data)) throw new Error(`${source} emitted a canonical event without an object payload`);
1269
- const outerType = String(type ?? "");
1270
- const harnessNative = outerType === harnessNativePayloadType;
1271
- if (normalized !== void 0) {
1272
- if (!harnessNative && typeof normalized === "object" && normalized !== null && "type" in normalized && typeof normalized.type === "string" && normalized.type !== outerType) throw new Error(`${source} canonical event type "${normalized.type}" does not match transport type "${outerType}"`);
1273
- const candidate = CanonicalStreamEventSchema.safeParse(normalized);
1274
- if (!candidate.success) {
1275
- if (harnessNative) return void 0;
1276
- throw new Error(`${source} emitted an invalid normalized canonical event`, { cause: candidate.error });
1277
- }
1278
- return candidate.data;
1279
- }
1280
- const { type: embeddedType, eventId: _eventId, cursor: _cursor, sequence: _sequence, occurredAt: _occurredAt, normalized: _normalized, ...payload } = data;
1281
- if (!harnessNative && embeddedType !== void 0 && embeddedType !== outerType) throw new Error(`${source} canonical event type "${String(embeddedType)}" does not match transport type "${outerType}"`);
1282
- const candidate = CanonicalStreamEventSchema.safeParse({
1283
- ...payload,
1284
- type: outerType
1285
- });
1286
- return candidate.success ? candidate.data : void 0;
1287
- }
1288
- /** Extract shared event identity without privileging one provider wire shape. */
1289
- function extractTransportEventIdentity(event) {
1290
- const record = isRecord(event) ? event : {};
1291
- const data = isRecord(record.data) ? record.data : {};
1292
- const providerEvent = isRecord(record.providerEvent) ? record.providerEvent : {};
1293
- const providerData = isRecord(providerEvent.data) ? providerEvent.data : {};
1294
- const eventId = canonicalEventId(record, data, providerEvent, providerData);
1295
- const cursor = stableString(record.cursor ?? data.cursor ?? providerEvent.cursor ?? providerData.cursor);
1296
- const sequence = optionalSequence(record.sequence ?? data.sequence ?? providerEvent.sequence ?? providerData.sequence);
1297
- const occurredAt = optionalTimestamp(record.occurredAt ?? data.occurredAt ?? providerEvent.occurredAt ?? providerData.occurredAt);
1298
- return {
1299
- ...eventId === void 0 ? {} : { eventId },
1300
- ...cursor === void 0 ? {} : { cursor },
1301
- ...sequence === void 0 ? {} : { sequence },
1302
- ...occurredAt === void 0 ? {} : { occurredAt }
1303
- };
1304
- }
1305
- function canonicalEventId(record, data, providerEvent, providerData) {
1306
- const present = [
1307
- ["data.eventId", data.eventId],
1308
- ["providerEvent.eventId", providerEvent.eventId],
1309
- ["providerData.eventId", providerData.eventId],
1310
- ["record.eventId", record.eventId]
1311
- ].filter(([, value]) => value !== void 0);
1312
- if (present.length === 0) return stableString(record.id ?? providerEvent.id);
1313
- const normalized = present.map(([source, value]) => {
1314
- const eventId = stableString(value);
1315
- if (eventId === void 0) throw new Error(`transport event ${source} must be a stable string`);
1316
- return {
1317
- source,
1318
- eventId
1319
- };
1320
- });
1321
- const first = normalized[0];
1322
- if (normalized.some((candidate) => candidate.eventId !== first.eventId)) throw new Error("transport event canonical identities disagree");
1323
- return first.eventId;
1324
- }
1325
- function stableString(value) {
1326
- return typeof value === "string" && value.length > 0 && value.trim() === value ? value : void 0;
1327
- }
1328
- function finiteNonNegativeInteger(value) {
1329
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1330
- }
1331
- function optionalSequence(value) {
1332
- if (value === void 0) return void 0;
1333
- if (finiteNonNegativeInteger(value) === void 0) throw new Error("transport event sequence must be a non-negative safe integer");
1334
- return value;
1335
- }
1336
- function optionalTimestamp(value) {
1337
- if (value === void 0) return void 0;
1338
- assertRuntimeTimestamp(value, "occurredAt");
1339
- return value;
1340
- }
1341
- // src/runtime/sandbox-executor-output.ts
1342
- /** Streamed and terminal Sandbox executor output, using one tool-part projector. */
1343
- /**
1344
- * Fresh per-turn {@link SandboxToolPartState} for {@link mapSandboxToolEvent} — an
1345
- * empty call-status map so each turn projects tool frames independently.
1346
- *
1347
- * @experimental
1348
- */
1349
- function createSandboxToolPartState() {
1350
- return {
1351
- statusByCall: /* @__PURE__ */ new Map(),
1352
- seq: 0
1353
- };
1354
- }
1355
- /** Statuses that settle one tool call as a failure. A failed tool call stays a tool result:
1356
- * the run's terminal state comes from the public Sandbox outcome tracker, never from here. */
1357
- const TERMINAL_FAILURE = /^(error|errored|failed|failure|cancelled|canceled|timeout|timed_out)$/i;
1358
- /**
1359
- * Project one `SandboxEvent` onto the `tool_call` / `tool_result` variants of
1360
- * `RuntimeStreamEvent` — the tool-part projection `mapSandboxEvent`
1361
- * deliberately does NOT perform. Opt-in and additive: `mapSandboxEvent`'s
1362
- * default vocabulary (text/reasoning deltas + `llm_call`) is unchanged;
1363
- * consumers that need the tool surface (chat UIs rendering tool activity)
1364
- * compose this projector alongside it — `streamAgentTurn` does exactly that
1365
- * under its `preserveToolParts` option.
1366
- *
1367
- * Handled shapes (observed on the opencode / claude-code sandbox backends):
1368
- * - `message.part.updated` with `part.type === 'tool'` — stateful: a
1369
- * `tool_call` on the call id's first frame (args from `state.input` or
1370
- * `state.metadata.input`), a `tool_result` when the status transitions to
1371
- * `completed` (result from `state.output` / `metadata.output`) or to a
1372
- * terminal failure (result is `{ error, status, output? }` — the error
1373
- * surfaced in-band, never dropped).
1374
- * - bare `tool*` event types (`tool.call`, `tool_result`, …) — stateless:
1375
- * `*result*` types project to `tool_result`, the rest to `tool_call`.
1376
- * - codex `raw` events whose inner `item.started` / `item.updated` /
1377
- * `item.completed` carries a command, MCP, collaboration, web search or
1378
- * file change item — stateful on the item id, like a tool part. Other raw
1379
- * events, including opencode's raw `tool_use` echoes, project nothing.
1380
- *
1381
- * Returns `[]` for every non-tool event.
1382
- *
1383
- * @experimental
1384
- */
1385
- function mapSandboxToolEvent(event, state) {
1386
- if (!event || typeof event !== "object") return [];
1387
- const type = String(event.type ?? "");
1388
- const data = event.data && typeof event.data === "object" ? event.data : {};
1389
- if (type === "message.part.updated") {
1390
- const part = data.part && typeof data.part === "object" ? data.part : {};
1391
- if (String(part.type ?? "") !== "tool") return [];
1392
- return projectToolPart(part, state, typeof event.id === "string" ? event.id : void 0);
1393
- }
1394
- if (type === "raw") return projectCodexItem(data, state);
1395
- if (CANONICAL_STREAM_EVENT_TYPES.has(type)) return [];
1396
- if (type.includes("tool")) {
1397
- const callId = pickString(data, [
1398
- "toolCallId",
1399
- "tool_use_id",
1400
- "id"
1401
- ]) ?? (typeof event.id === "string" ? event.id : void 0) ?? `sandbox-tool-${++state.seq}`;
1402
- const toolName = pickString(data, [
1403
- "name",
1404
- "toolName",
1405
- "tool"
1406
- ]) ?? "sandbox_tool";
1407
- if (type.includes("result")) return [{
1408
- type: "tool_result",
1409
- toolName,
1410
- toolCallId: callId,
1411
- result: data.output ?? data.result ?? data.content ?? data
1412
- }];
1413
- return [{
1414
- type: "tool_call",
1415
- toolName,
1416
- toolCallId: callId,
1417
- args: data.input ?? data.args ?? {}
1418
- }];
1419
- }
1420
- return [];
1421
- }
1422
- function projectToolPart(part, state, eventId) {
1423
- const callId = pickString(part, [
1424
- "callID",
1425
- "callId",
1426
- "toolCallId",
1427
- "id"
1428
- ]) ?? eventId ?? `sandbox-tool-${++state.seq}`;
1429
- const toolName = pickString(part, [
1430
- "tool",
1431
- "toolName",
1432
- "name"
1433
- ]) ?? "sandbox_tool";
1434
- const toolState = part.state && typeof part.state === "object" ? part.state : {};
1435
- const metadata = toolState.metadata && typeof toolState.metadata === "object" ? toolState.metadata : {};
1436
- const status = pickString(toolState, ["status"]) ?? "updated";
1437
- const previous = state.statusByCall.get(callId);
1438
- if (previous === "completed" || previous !== void 0 && TERMINAL_FAILURE.test(previous)) return [];
1439
- const out = [];
1440
- if (previous === void 0) out.push({
1441
- type: "tool_call",
1442
- toolName,
1443
- toolCallId: callId,
1444
- args: toolState.input ?? metadata.input ?? {}
1445
- });
1446
- state.statusByCall.set(callId, status);
1447
- if (status === "completed") out.push({
1448
- type: "tool_result",
1449
- toolName,
1450
- toolCallId: callId,
1451
- result: toolState.output ?? metadata.output ?? ""
1452
- });
1453
- else if (TERMINAL_FAILURE.test(status)) {
1454
- const message = pickString(toolState, ["error", "message"]) ?? pickString(metadata, ["error", "message"]) ?? `sandbox tool ended with status ${status}`;
1455
- const output = toolState.output ?? metadata.output;
1456
- out.push({
1457
- type: "tool_result",
1458
- toolName,
1459
- toolCallId: callId,
1460
- result: {
1461
- error: message,
1462
- status,
1463
- ...output !== void 0 ? { output } : {}
1464
- }
1465
- });
1466
- }
1467
- return out;
1468
- }
1469
- /** Codex item types that are tool calls. Reasoning, messages and todo lists are not. */
1470
- const CODEX_TOOL_ITEMS = /* @__PURE__ */ new Set([
1471
- "command_execution",
1472
- "mcp_tool_call",
1473
- "collab_tool_call",
1474
- "web_search",
1475
- "file_change"
1476
- ]);
1477
- /** Item fields that describe the call's progress or outcome rather than what it was asked to do. */
1478
- const CODEX_ITEM_OUTCOME_FIELDS = /* @__PURE__ */ new Set([
1479
- "id",
1480
- "type",
1481
- "status",
1482
- "result",
1483
- "error",
1484
- "aggregated_output",
1485
- "exit_code"
1486
- ]);
1487
- /**
1488
- * Project a codex `item.started` / `item.updated` / `item.completed` tool item through the same
1489
- * per-call state as a tool part, keyed on the item id: one `tool_call` on its first frame and one
1490
- * `tool_result` when it completes. Tool names follow Eval's codex intake: `server/tool` for MCP.
1491
- */
1492
- function projectCodexItem(data, state) {
1493
- if (data.backend !== "codex" || !data.event || typeof data.event !== "object") return [];
1494
- const inner = data.event;
1495
- const phase = inner.type;
1496
- if (phase !== "item.started" && phase !== "item.updated" && phase !== "item.completed") return [];
1497
- if (!inner.item || typeof inner.item !== "object") return [];
1498
- const item = inner.item;
1499
- const itemType = pickString(item, ["type"]);
1500
- if (itemType === void 0 || !CODEX_TOOL_ITEMS.has(itemType)) return [];
1501
- const error = item.error && typeof item.error === "object" ? pickString(item.error, ["message"]) ?? `${itemType} failed` : void 0;
1502
- const failed = item.status === "failed" || error !== void 0 || itemType === "command_execution" && typeof item.exit_code === "number" && item.exit_code !== 0;
1503
- const status = phase !== "item.completed" ? "running" : failed ? "failed" : "completed";
1504
- const input = Object.fromEntries(Object.entries(item).filter(([key]) => !CODEX_ITEM_OUTCOME_FIELDS.has(key)));
1505
- const output = item.result ?? item.aggregated_output ?? item.changes;
1506
- return projectToolPart({
1507
- ...typeof item.id === "string" ? { callID: item.id } : {},
1508
- tool: codexToolName(itemType, item),
1509
- state: {
1510
- status,
1511
- input,
1512
- ...output === void 0 || output === null ? {} : { output },
1513
- ...status === "failed" ? { error: error ?? `${itemType} failed` } : {}
1514
- }
1515
- }, state, void 0);
1516
- }
1517
- function codexToolName(itemType, item) {
1518
- if (itemType === "mcp_tool_call") {
1519
- const name = [pickString(item, ["server"]), pickString(item, ["tool"])].filter((part) => part !== void 0).join("/");
1520
- return name === "" ? "mcp" : name;
1521
- }
1522
- if (itemType === "collab_tool_call") return pickString(item, ["tool"]) ?? "subagent";
1523
- if (itemType === "web_search" || itemType === "file_change") return itemType;
1524
- return pickString(item, ["name"]) ?? itemType;
1525
- }
1526
- function pickString(data, keys) {
1527
- for (const key of keys) {
1528
- const value = data[key];
1529
- if (typeof value === "string" && value.length > 0) return value;
1530
- }
1531
- }
1532
- /**
1533
- * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,
1534
- * for runtimes that bridge a sandbox `streamPrompt` into the
1535
- * `AgentRuntime.act` streaming contract. Returns `undefined` for events that
1536
- * have no faithful projection — the raw stream is preserved separately for the
1537
- * `OutputAdapter`, so an unmapped event never loses data.
1538
- *
1539
- * Mapped (the task-optional incremental variants — no synthesized task
1540
- * lifecycle, no guessed tool-part shapes):
1541
- * - `message.part.updated` text part → `text_delta`
1542
- * - `message.part.updated` reasoning/thinking part → `reasoning_delta`
1543
- * - cost-bearing events → `llm_call` (shared with the ledger extractor)
1544
- *
1545
- * Tool parts are deliberately NOT mapped here (unchanged default) — compose
1546
- * {@link mapSandboxToolEvent} alongside when a consumer needs them.
1547
- *
1548
- * The opencode backend emits incremental text as
1549
- * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;
1550
- * `delta` is the increment, `part.text` the running accumulation.
1551
- */
1552
- function mapSandboxEvent(event, opts = {}) {
1553
- if (!event || typeof event !== "object") return void 0;
1554
- const type = String(event.type ?? "");
1555
- const data = event.data && typeof event.data === "object" ? event.data : {};
1556
- if (type === "message.part.updated") {
1557
- const part = data.part && typeof data.part === "object" ? data.part : {};
1558
- const partType = String(part.type ?? "");
1559
- const text = (typeof data.delta === "string" ? data.delta : void 0) ?? (typeof part.text === "string" ? part.text : void 0);
1560
- if (text === void 0) return void 0;
1561
- if (partType === "text") return {
1562
- type: "text_delta",
1563
- text
1564
- };
1565
- if (partType === "reasoning" || partType === "thinking") return {
1566
- type: "reasoning_delta",
1567
- text
1568
- };
1569
- return;
1570
- }
1571
- return extractLlmCallEvent(event, opts.agentRunName ?? "agent");
1572
- }
1573
- /**
1574
- * Project one `SandboxEvent` onto Runtime's executor progress vocabulary: incremental text and
1575
- * reasoning, tool calls and results, and an interaction request. It composes the existing
1576
- * projections ({@link mapSandboxEvent}, {@link mapSandboxToolEvent}, and the canonical Agent
1577
- * Interface decode) so every sandbox-shaped executor publishes live output through one reader.
1578
- * Usage-bearing events project to nothing here — accounting stays on the `tokens`/`cost`
1579
- * channels.
1580
- *
1581
- * Pass one {@link SandboxToolPartState} per turn so a multi-frame tool call yields one call and
1582
- * at most one result.
1583
- *
1584
- * @experimental
1585
- */
1586
- function sandboxProgressEvents(event, state) {
1587
- const canonical = canonicalStreamEventFromSandboxEvent(event);
1588
- if (canonical?.type === "interaction") return [{
1589
- kind: "interaction",
1590
- request: canonical.request
1591
- }];
1592
- if (canonical?.type === "child-task") return [{
1593
- kind: "child_task",
1594
- event: canonical
1595
- }];
1596
- const tools = mapSandboxToolEvent(event, state).map((projected) => projected.type === "tool_call" ? {
1597
- kind: "tool_call",
1598
- toolName: projected.toolName,
1599
- ...projected.toolCallId === void 0 ? {} : { toolCallId: projected.toolCallId },
1600
- ...projected.args === void 0 ? {} : { args: projected.args }
1601
- } : {
1602
- kind: "tool_result",
1603
- toolName: projected.toolName,
1604
- ...projected.toolCallId === void 0 ? {} : { toolCallId: projected.toolCallId },
1605
- ...projected.result === void 0 ? {} : { result: projected.result }
1606
- });
1607
- if (tools.length > 0) return tools;
1608
- const mapped = mapSandboxEvent(event);
1609
- if (mapped?.type === "text_delta") return [{
1610
- kind: "text_delta",
1611
- text: mapped.text
1612
- }];
1613
- if (mapped?.type === "reasoning_delta") return [{
1614
- kind: "reasoning_delta",
1615
- text: mapped.text
1616
- }];
1617
- return [];
1618
- }
1619
- /** Project the complete Sandbox event stream into the standard executor result shape. */
1620
- function sandboxLeafOutputFromEvents(events) {
1621
- const content = collectAgentFinalMessageText(events) ?? collectAgentResponseText(events);
1622
- const toolCalls = sandboxToolCalls(events);
1623
- const servedBackend = lastServedBackend(events);
1624
- return {
1625
- events,
1626
- content,
1627
- output: sandboxOutputMarker(content, events.some(carriesText)),
1628
- ...servedBackend === void 0 ? {} : { servedBackend },
1629
- ...toolCalls.length === 0 ? {} : { toolCalls }
1630
- };
1631
- }
1632
- /**
1633
- * Classify observed text into the explicit output marker. `sawTextEvent` says whether the stream
1634
- * carried a text-bearing event at all, which is what separates a box that answered nothing from a
1635
- * box whose answer was never observed.
1636
- */
1637
- function sandboxOutputMarker(content, sawTextEvent = content !== void 0) {
1638
- if (content !== void 0 && content.length > 0) return {
1639
- kind: "text",
1640
- bytes: Buffer.byteLength(content, "utf8")
1641
- };
1642
- return sawTextEvent ? { kind: "empty" } : { kind: "absent" };
1643
- }
1644
- /** True when the event carries a text field a turn's answer could come from, empty or not. */
1645
- function carriesText(event) {
1646
- if (!event || typeof event !== "object") return false;
1647
- const data = event.data && typeof event.data === "object" ? event.data : {};
1648
- if (typeof data.finalText === "string" || typeof data.text === "string") return true;
1649
- if (typeof data.delta === "string") return true;
1650
- const part = data.part && typeof data.part === "object" ? data.part : void 0;
1651
- return part !== void 0 && part.type === "text" && typeof part.text === "string";
1652
- }
1653
- /** The last served identity the platform reported across a turn's events, or `undefined`. */
1654
- function lastServedBackend(events) {
1655
- let served;
1656
- for (const event of events) {
1657
- const next = sandboxEventServedBackend(event);
1658
- if (next !== void 0) served = next;
1659
- }
1660
- return served;
1661
- }
1662
- function sandboxToolCalls(events) {
1663
- const state = createSandboxToolPartState();
1664
- return events.flatMap((event) => mapSandboxToolEvent(event, state).flatMap((projected) => projected.type === "tool_call" ? [{
1665
- ...projected.toolCallId === void 0 ? {} : { id: projected.toolCallId },
1666
- name: projected.toolName,
1667
- arguments: projected.args ?? {}
1668
- }] : []));
1669
- }
1670
- /** Read the final text off a terminal sandbox event, when present. */
1671
- function terminalTextFromSandboxEvent(event) {
1672
- if (!event || typeof event !== "object") return void 0;
1673
- if (!isSandboxTerminalEvent(String(event.type ?? ""))) return void 0;
1674
- const data = event.data && typeof event.data === "object" ? event.data : {};
1675
- for (const key of [
1676
- "finalText",
1677
- "text",
1678
- "response",
1679
- "content"
1680
- ]) {
1681
- const value = data[key];
1682
- if (typeof value === "string") return value;
1683
- }
1684
- }
1685
- // src/runtime/supervise/spend-validation.ts
1686
- /** Reject malformed ceilings before they can mint capacity through negative reservations. */
1687
- function assertValidBudget(budget, label = "budget") {
1688
- const safeInteger = (value, field) => {
1689
- if (!Number.isSafeInteger(value) || value < 0) throw new Error(`${label}.${field} must be a non-negative safe integer`);
1690
- };
1691
- const finiteNonNegative = (value, field) => {
1692
- if (!Number.isFinite(value) || value < 0) throw new Error(`${label}.${field} must be a non-negative finite number`);
1693
- };
1694
- assertResources(budget.resources, "limit", `${label}.resources`);
1695
- if (budget.maxIterations !== void 0) safeInteger(budget.maxIterations, "maxIterations");
1696
- if (budget.maxTokens !== void 0) safeInteger(budget.maxTokens, "maxTokens");
1697
- if (budget.maxUsd !== void 0) finiteNonNegative(budget.maxUsd, "maxUsd");
1698
- if (budget.deadlineMs !== void 0) finiteNonNegative(budget.deadlineMs, "deadlineMs");
1699
- }
1700
- function assertValidSpend(spend, label) {
1701
- assertResources(spend.resources, "amount", `${label}.resources`);
1702
- if (!Number.isSafeInteger(spend.iterations) || spend.iterations < 0) throw new Error(`${label}.iterations must be a non-negative safe integer`);
1703
- for (const [field, value] of [
1704
- ["input", spend.tokens.input],
1705
- ["output", spend.tokens.output],
1706
- ["freshInput", spend.tokens.freshInput],
1707
- ["cacheRead", spend.tokens.cacheRead],
1708
- ["cacheWrite", spend.tokens.cacheWrite]
1709
- ]) {
1710
- if (value === void 0) continue;
1711
- if (!Number.isSafeInteger(value) || value < 0) throw new Error(`${label}.tokens.${field} must be a non-negative safe integer`);
1712
- }
1713
- for (const [field, value] of [["tokensKnown", spend.tokens.tokensKnown], ["cacheBreakdownKnown", spend.tokens.cacheBreakdownKnown]]) if (value !== void 0 && value !== false) throw new Error(`${label}.tokens.${field} must be false when present`);
1714
- const { freshInput, cacheRead, cacheWrite } = spend.tokens;
1715
- if (freshInput !== void 0 && cacheRead !== void 0 && cacheWrite !== void 0) {
1716
- const classified = freshInput + cacheRead + cacheWrite;
1717
- if (spend.tokens.cacheBreakdownKnown !== false) {
1718
- if (classified !== spend.tokens.input) throw new Error(`${label}.tokens cache classes must sum to input`);
1719
- } else if (classified > spend.tokens.input) throw new Error(`${label}.tokens cache classes must not exceed input`);
1720
- }
1721
- for (const [field, value] of [["usd", spend.usd], ["ms", spend.ms]]) if (!Number.isFinite(value) || value < 0) throw new Error(`${label}.${field} must be a non-negative finite number`);
1722
- if (spend.usdEstimated !== void 0) {
1723
- if (!Number.isFinite(spend.usdEstimated) || spend.usdEstimated < 0) throw new Error(`${label}.usdEstimated must be a non-negative finite number`);
1724
- if (spend.usdEstimated > spend.usd) throw new Error(`${label}.usdEstimated must not exceed ${label}.usd`);
1725
- if (spend.usdKnown !== false) throw new Error(`${label}.usdEstimated requires ${label}.usdKnown false`);
1726
- }
1727
- }
1728
- // src/runtime/supervise/budget-ledger.ts
1729
- /**
1730
- * The balances of one conserved budget pool, and the arithmetic that moves them. `createBudgetPool`
1731
- * (`./budget`) owns tickets, the wait queue and the public contract; this module owns the numbers:
1732
- * `total ≡ free + reserved + committed` per channel, the taint flags that say a balance is a
1733
- * ceiling rather than a measurement, and the named-resource states.
1734
- */
1735
- function openBudgetLedger(root, runStartedAtMs) {
1736
- return {
1737
- tokensCapped: root.maxTokens !== void 0,
1738
- iterationsCapped: root.maxIterations !== void 0,
1739
- usdCapped: root.maxUsd !== void 0,
1740
- absoluteDeadlineMs: root.deadlineMs === void 0 ? 0 : runStartedAtMs + root.deadlineMs,
1741
- resources: new Map(Object.entries(root.resources ?? {}).map(([name, value]) => [name, {
1742
- ...value,
1743
- remaining: value.limit,
1744
- reserved: 0,
1745
- committed: 0,
1746
- known: true
1747
- }])),
1748
- freeTokens: root.maxTokens ?? 0,
1749
- reservedTokens: 0,
1750
- committedTokens: 0,
1751
- tokensTainted: false,
1752
- cacheBreakdownTainted: false,
1753
- freeUsd: root.maxUsd ?? 0,
1754
- reservedUsd: 0,
1755
- committedUsd: 0,
1756
- usdTainted: false,
1757
- usdMeasured: true,
1758
- freeIterations: root.maxIterations ?? 0,
1759
- reservedIterations: 0,
1760
- committedIterations: 0,
1761
- ownerSpent: {
1762
- iterations: 0,
1763
- tokens: zeroTokenUsage(),
1764
- usd: 0,
1765
- ms: 0
1766
- }
1767
- };
1768
- }
1769
- function ledgerValidateUnits(l, spend) {
1770
- for (const [name, value] of Object.entries(spend.resources ?? {})) {
1771
- const limit = l.resources.get(name);
1772
- if (limit && limit.unit !== value.unit) throw new Error(`resource ${name}: unit mismatch`);
1773
- }
1774
- }
1775
- /** Record that `spend` carried an unreported or unclassified channel. */
1776
- function noteUnknowns(l, spend) {
1777
- if (spend.tokensKnown === false) l.tokensTainted = true;
1778
- if (!hasCompleteCacheBreakdown(spend.tokens)) l.cacheBreakdownTainted = true;
1779
- if (spend.usdKnown === false) l.usdMeasured = false;
1780
- }
1781
- /**
1782
- * Commit named-resource spend and classify it. `allocations` is the settling reservation's own
1783
- * allocations, or `undefined` for spend holding no reservation (`observe`, restore). Unverifiable
1784
- * usage is a `fault` (the remaining balance is consumed so admission closes); measured usage above
1785
- * an allocation is `overspent`; without allocations, usage leaving a root limit overdrawn is
1786
- * `rootExceeded`. A reservation within its own allocation is never charged with a root overdraw:
1787
- * a negative balance at its settlement was caused, and reported, by earlier spend.
1788
- */
1789
- function ledgerCommitResources(l, spend, allocations, requireAll = true) {
1790
- let fault;
1791
- let rootExceeded;
1792
- const overspent = [];
1793
- for (const [name, state] of l.resources) {
1794
- const value = spend.resources?.[name];
1795
- if (!requireAll && value === void 0) continue;
1796
- const allocation = allocations?.[name]?.limit ?? 0;
1797
- const amount = value?.amount ?? 0;
1798
- state.reserved -= allocation;
1799
- const committed = state.committed + amount;
1800
- const overflow = !Number.isSafeInteger(committed);
1801
- state.committed = Math.min(committed, Number.MAX_SAFE_INTEGER);
1802
- state.remaining = state.limit - state.reserved - state.committed;
1803
- if (value?.known !== true || overflow) {
1804
- state.known = false;
1805
- const retained = Math.max(0, state.remaining);
1806
- state.committed += retained;
1807
- state.remaining -= retained;
1808
- fault ??= overflow ? `resource ${name}: amount overflow under an enforced limit` : `resource ${name}: unknown usage under an enforced limit`;
1809
- } else if (allocations !== void 0) {
1810
- if (amount > allocation) overspent.push({
1811
- channel: `resource:${name}`,
1812
- reserved: allocation,
1813
- spent: amount
1814
- });
1815
- } else if (state.remaining < 0) rootExceeded ??= `resource ${name}: exceeded root limit ${state.limit}`;
1816
- }
1817
- return {
1818
- ...fault ? { fault } : {},
1819
- overspent,
1820
- ...rootExceeded ? { rootExceeded } : {}
1821
- };
1822
- }
1823
- /** Every channel `b` does not fit, measured against the free balance less `keep`. */
1824
- function ledgerShortfalls(l, b, keep) {
1825
- const wantTokens = b.maxTokens ?? 0;
1826
- const wantUsd = b.maxUsd ?? 0;
1827
- const wantIterations = b.maxIterations ?? 0;
1828
- const shortfalls = [];
1829
- const short = (channel, requested, free, held, closedByUnknownSpend = false) => {
1830
- shortfalls.push({
1831
- channel,
1832
- requested,
1833
- free: closedByUnknownSpend ? 0 : Math.max(0, free),
1834
- ...closedByUnknownSpend || held <= 0 ? {} : { held },
1835
- ...closedByUnknownSpend ? { closedByUnknownSpend: true } : {}
1836
- });
1837
- };
1838
- for (const [name, state] of l.resources) {
1839
- const limit = b.resources[name].limit;
1840
- if (!state.known) short(`resource:${name}`, limit, 0, 0, true);
1841
- else if (limit > state.remaining) short(`resource:${name}`, limit, state.remaining, state.reserved);
1842
- }
1843
- if (l.usdCapped && l.usdTainted) short("usd", wantUsd, 0, 0, true);
1844
- const kept = (share = 0, spent) => Math.max(0, share - spent);
1845
- const tokensFree = l.freeTokens - kept(keep?.tokens, chargedTokens(l.ownerSpent.tokens));
1846
- const iterationsFree = l.freeIterations - kept(keep?.iterations, l.ownerSpent.iterations);
1847
- if (l.tokensCapped && wantTokens > tokensFree) short("tokens", wantTokens, tokensFree, l.reservedTokens);
1848
- if (l.iterationsCapped && wantIterations > iterationsFree) short("iterations", wantIterations, iterationsFree, l.reservedIterations);
1849
- const usdFree = l.freeUsd - kept(keep?.usd, l.ownerSpent.usd);
1850
- if (l.usdCapped && !l.usdTainted && wantUsd > usdFree) short("usd", wantUsd, usdFree, l.reservedUsd);
1851
- return shortfalls;
1852
- }
1853
- /** Move `b` from free to reserved. The caller has checked that it fits. */
1854
- function ledgerTake(l, b) {
1855
- for (const [name, state] of l.resources) {
1856
- const amount = b.resources[name].limit;
1857
- state.remaining -= amount;
1858
- state.reserved += amount;
1859
- }
1860
- if (l.tokensCapped) {
1861
- l.freeTokens -= b.maxTokens ?? 0;
1862
- l.reservedTokens += b.maxTokens ?? 0;
1863
- }
1864
- if (l.iterationsCapped) {
1865
- l.freeIterations -= b.maxIterations ?? 0;
1866
- l.reservedIterations += b.maxIterations ?? 0;
1867
- }
1868
- l.freeUsd -= b.maxUsd ?? 0;
1869
- l.reservedUsd += b.maxUsd ?? 0;
1870
- }
1871
- /**
1872
- * The overspends a settlement of `spent` against `held` decides, before any balance moves. An
1873
- * OVERSPEND is measured spend above the reservation; unknown dollars under a dollar cap (a fault
1874
- * the caller reports) are flagged `unknownUnderCap`. `usdBudgeted` is whether the child declared a
1875
- * dollar ceiling: without `maxUsd` it reserved NO dollars, so under a capped root its dollars are
1876
- * OBSERVED spend, committed and debited from `free` like `observe`d inference.
1877
- */
1878
- function ledgerClassifyOverspend(l, held, usdBudgeted, spent, limits) {
1879
- const unknownUnderCap = l.usdCapped && spent.usdKnown === false;
1880
- const spentTokens = chargedTokens(spent.tokens);
1881
- const overspent = [];
1882
- const tokens = limits === void 0 ? held.tokens : limits.maxTokens;
1883
- const iterations = limits === void 0 ? held.iterations : limits.maxIterations;
1884
- if (tokens !== void 0 && spentTokens > tokens) overspent.push({
1885
- channel: "tokens",
1886
- reserved: tokens,
1887
- spent: spentTokens
1888
- });
1889
- if (iterations !== void 0 && spent.iterations > iterations) overspent.push({
1890
- channel: "iterations",
1891
- reserved: iterations,
1892
- spent: spent.iterations
1893
- });
1894
- if (!unknownUnderCap && l.usdCapped && usdBudgeted && spent.usd > held.usd) overspent.push({
1895
- channel: "usd",
1896
- reserved: held.usd,
1897
- spent: spent.usd
1898
- });
1899
- for (const name of l.resources.keys()) {
1900
- const value = spent.resources?.[name];
1901
- const limit = held.resources?.[name]?.limit ?? 0;
1902
- if (value?.known && value.amount > limit) overspent.push({
1903
- channel: `resource:${name}`,
1904
- reserved: limit,
1905
- spent: value.amount
1906
- });
1907
- }
1908
- return {
1909
- overspent,
1910
- unknownUnderCap
1911
- };
1912
- }
1913
- /**
1914
- * Release the whole reservation `held`, then commit actual spend; the difference is the refund
1915
- * that flows back to `free`. Unconditional arithmetic: nothing here throws on spend it can read.
1916
- */
1917
- function ledgerSettle(l, held, spent, unknownUnderCap) {
1918
- noteUnknowns(l, spent);
1919
- const spentTokens = chargedTokens(spent.tokens);
1920
- l.committedTokens += spentTokens;
1921
- if (l.tokensCapped) {
1922
- l.reservedTokens -= held.tokens;
1923
- l.freeTokens += held.tokens - spentTokens;
1924
- }
1925
- l.committedIterations += spent.iterations;
1926
- if (l.iterationsCapped) {
1927
- l.reservedIterations -= held.iterations;
1928
- l.freeIterations += held.iterations - spent.iterations;
1929
- }
1930
- if (l.usdCapped) {
1931
- l.reservedUsd -= held.usd;
1932
- l.committedUsd += spent.usd;
1933
- if (unknownUnderCap) {
1934
- l.usdTainted = true;
1935
- l.freeUsd = 0;
1936
- } else l.freeUsd += held.usd - spent.usd;
1937
- } else l.committedUsd += spent.usd;
1938
- return ledgerCommitResources(l, spent, held.resources ?? {});
1939
- }
1940
- /**
1941
- * Direct free → committed debit (no ticket). `free` may go negative on overspend: the readout then
1942
- * reports exhaustion and the in-loop guard halts the driver. Work with an unreported token count
1943
- * still debits what IS known and marks the balance incomplete; refusing to record it is the "free
1944
- * turn" the pool must never see.
1945
- */
1946
- function ledgerObserve(l, spend, unknownUnderCap, requireAllResources) {
1947
- debit(l, spend);
1948
- l.ownerSpent = addSpend(l.ownerSpent, spend);
1949
- if (unknownUnderCap) l.usdTainted = true;
1950
- return ledgerCommitResources(l, spend, void 0, requireAllResources);
1951
- }
1952
- /** Move measured `spend` from free to committed on the token, iteration and dollar channels. */
1953
- function debit(l, spend) {
1954
- noteUnknowns(l, spend);
1955
- const tokens = chargedTokens(spend.tokens);
1956
- if (l.tokensCapped) l.freeTokens -= tokens;
1957
- l.committedTokens += tokens;
1958
- if (l.iterationsCapped) l.freeIterations -= spend.iterations;
1959
- l.committedIterations += spend.iterations;
1960
- l.committedUsd += spend.usd;
1961
- if (l.usdCapped) l.freeUsd -= spend.usd;
1962
- }
1963
- /**
1964
- * Reconstruct the balances from a prior process's durable record. Measured prior spend is debited
1965
- * exactly once; a started-without-terminal child is charged at its entire reservation, so restart
1966
- * never mints capacity and the false known flags stay visible. Direct arithmetic, not `observe`:
1967
- * the prior spend ALREADY happened, and refusing to record it would be a zero-cost restart.
1968
- */
1969
- function ledgerRestore(l, root, restore) {
1970
- for (const held of restore.heldReservations ?? []) {
1971
- assertValidBudget(held, "restored remaining reservation");
1972
- const tokens = held.maxTokens ?? 0;
1973
- const iterations = held.maxIterations ?? 0;
1974
- const usd = held.maxUsd ?? 0;
1975
- l.committedTokens += tokens;
1976
- l.committedIterations += iterations;
1977
- l.committedUsd += usd;
1978
- if (l.tokensCapped) l.freeTokens -= tokens;
1979
- if (l.iterationsCapped) l.freeIterations -= iterations;
1980
- if (l.usdCapped) l.freeUsd -= usd;
1981
- if (tokens > 0) l.tokensTainted = true;
1982
- if (usd > 0) l.usdMeasured = false;
1983
- for (const [name, value] of Object.entries(held.resources ?? {})) {
1984
- const resource = l.resources.get(name);
1985
- if (!resource || resource.unit !== value.unit) throw new Error(`restored reservation resource ${name}: unit mismatch`);
1986
- resource.committed += value.limit;
1987
- resource.remaining -= value.limit;
1988
- if (value.limit > 0) resource.known = false;
1989
- }
1990
- }
1991
- if (restore.owner) l.ownerSpent = addSpend(l.ownerSpent, restore.owner);
1992
- if (restore.committed !== void 0) {
1993
- const committed = restore.committed;
1994
- assertValidSpend(committed, "budget restore committed");
1995
- ledgerValidateUnits(l, committed);
1996
- ledgerCommitResources(l, committed);
1997
- debit(l, committed);
1998
- if (l.usdCapped && committed.usdKnown === false) {
1999
- l.usdTainted = true;
2000
- if (l.freeUsd > 0) l.committedUsd += l.freeUsd;
2001
- l.freeUsd = 0;
2002
- }
2003
- }
2004
- for (const [index, uncertain] of (restore.uncertainReservations ?? []).entries()) {
2005
- assertValidBudget(uncertain, `budget restore uncertainReservations[${index}]`);
2006
- ledgerCommitResources(l, withBudgetResources({
2007
- iterations: 0,
2008
- tokens: zeroTokenUsage(),
2009
- usd: 0,
2010
- ms: 0
2011
- }, root));
2012
- l.tokensTainted = true;
2013
- l.usdMeasured = false;
2014
- const tokens = uncertain.maxTokens ?? (l.tokensCapped ? Math.max(0, l.freeTokens) : 0);
2015
- const iterations = uncertain.maxIterations ?? (l.iterationsCapped ? Math.max(0, l.freeIterations) : 0);
2016
- l.committedTokens += tokens;
2017
- l.committedIterations += iterations;
2018
- if (l.tokensCapped) l.freeTokens -= tokens;
2019
- if (l.iterationsCapped) l.freeIterations -= iterations;
2020
- if (!l.usdCapped) continue;
2021
- if (uncertain.maxUsd === void 0) {
2022
- l.usdTainted = true;
2023
- l.committedUsd += Math.max(0, l.freeUsd);
2024
- l.freeUsd = 0;
2025
- } else {
2026
- l.freeUsd -= uncertain.maxUsd;
2027
- l.committedUsd += uncertain.maxUsd;
2028
- }
2029
- }
2030
- }
2031
- // src/runtime/supervise/usage-metering.ts
2032
- /** Fold a normalized `UsageEvent` array into a `Spend`. Tokens and usd are separate
2033
- * channels; iterations come from `'iteration'` events. Pure; `ms` stays zero (the
2034
- * pool does not read wall-clock). */
2035
- function spendFromUsageEvents(events) {
2036
- const totals = newUsageTotals();
2037
- for (const ev of events) meterUsageEvent(totals, ev);
2038
- return spendFromUsageTotals(totals);
2039
- }
2040
- /** Fresh accounting totals, optionally restored from verified committed spend. */
2041
- function newUsageTotals(priorSpent) {
2042
- if (priorSpent !== void 0) assertValidSpend(priorSpent, "prior committed usage");
2043
- return {
2044
- ...addResourceSpend(priorSpent?.resources),
2045
- tokens: priorSpent === void 0 ? zeroTokenUsage() : cloneTokenUsage(priorSpent.tokens),
2046
- tokensKnown: priorSpent?.tokensKnown !== false,
2047
- ...priorSpent?.tokensProvenance === void 0 ? {} : { tokensProvenance: priorSpent.tokensProvenance },
2048
- usd: priorSpent?.usd ?? 0,
2049
- usdEstimated: priorSpent?.usdEstimated ?? 0,
2050
- usdKnown: priorSpent?.usdKnown !== false,
2051
- iterations: priorSpent?.iterations ?? 0
2052
- };
2053
- }
2054
- /**
2055
- * Fold ONE usage event into running totals — the single place that decides which event kinds
2056
- * meter. `progress` carries observed output rather than accounting, so it meters nothing; only
2057
- * `iteration` advances the iteration count.
2058
- */
2059
- function meterUsageEvent(totals, ev) {
2060
- if (ev.kind === "resource") {
2061
- totals.resources = addResourceSpend(totals.resources, Object.fromEntries([[ev.name, {
2062
- unit: ev.unit,
2063
- amount: ev.amount,
2064
- known: ev.known
2065
- }]])).resources;
2066
- return;
2067
- }
2068
- if (ev.kind === "tokens") {
2069
- if (ev.mode === "cumulative") {
2070
- if (ev.input < totals.tokens.input || ev.output < totals.tokens.output) throw new ValidationError("cumulative executor tokens must not decrease");
2071
- const snapshot = zeroTokenUsage();
2072
- addTokenUsage(snapshot, {
2073
- ...ev,
2074
- ...ev.freshInput === void 0 && totals.tokens.freshInput !== void 0 ? { freshInput: totals.tokens.freshInput } : {},
2075
- ...ev.cacheRead === void 0 && totals.tokens.cacheRead !== void 0 ? { cacheRead: totals.tokens.cacheRead } : {},
2076
- ...ev.cacheWrite === void 0 && totals.tokens.cacheWrite !== void 0 ? { cacheWrite: totals.tokens.cacheWrite } : {},
2077
- ...totals.tokens.tokensKnown === false ? { tokensKnown: false } : {}
2078
- });
2079
- totals.tokens = snapshot;
2080
- } else addTokenUsage(totals.tokens, ev);
2081
- if (ev.tokensKnown === false) totals.tokensKnown = false;
2082
- const source = ev.provenance ?? "stream-receipt";
2083
- totals.tokensProvenance = totals.tokensProvenance === void 0 || totals.tokensProvenance === source ? source : "mixed";
2084
- return;
2085
- }
2086
- if (ev.kind === "cost") {
2087
- totals.usd += ev.usd;
2088
- if (ev.usdKnown === false) {
2089
- totals.usdEstimated += ev.usdEstimated ?? 0;
2090
- totals.usdKnown = false;
2091
- }
2092
- return;
2093
- }
2094
- if (ev.kind === "iteration") totals.iterations += 1;
2095
- }
2096
- /** Project folded totals onto the conserved-pool `Spend`. `ms` stays zero — the pool does not
2097
- * read wall-clock. */
2098
- function spendFromUsageTotals(totals) {
2099
- return {
2100
- ...addResourceSpend(totals.resources),
2101
- iterations: totals.iterations,
2102
- tokens: totals.tokens,
2103
- ...totals.tokensKnown ? {} : { tokensKnown: false },
2104
- usd: totals.usd,
2105
- ...totals.usdKnown ? {} : { usdKnown: false },
2106
- ...totals.usdEstimated > 0 ? { usdEstimated: totals.usdEstimated } : {},
2107
- ...totals.tokensProvenance === void 0 || totals.tokensProvenance === "stream-receipt" ? {} : { tokensProvenance: totals.tokensProvenance },
2108
- ms: 0
2109
- };
2110
- }
2111
- async function foldUsage(events) {
2112
- if (Array.isArray(events)) return spendFromUsageEvents(events);
2113
- const totals = newUsageTotals();
2114
- for await (const ev of events) meterUsageEvent(totals, ev);
2115
- return spendFromUsageTotals(totals);
2116
- }
2117
- // src/runtime/supervise/budget.ts
2118
- /**
2119
- * Conserved budget accounting: total ≡ free + reserved + committed on each capped channel.
2120
- * Reservation moves current free capacity to a single-use ticket; reconciliation commits
2121
- * measured spend and refunds the remainder. It closes the ticket before reporting any fault.
2122
- * Measured overspend reduces future capacity and returns a BudgetViolation.
2123
- *
2124
- * Tokens use chargedTokens (input − cacheRead + output). Missing cache classification charges
2125
- * the full prompt and marks the split unknown. Prices never weight this channel. Named resources
2126
- * require explicit units and complete measurements; the pool trusts receipts, not external state.
2127
- * Unknown enforced resource or dollar usage closes that channel's admission. Unknown tokens
2128
- * preserve observed counts as a lower bound without ending the run. An uncapped child dollar
2129
- * channel reports actual dollars without treating them as an overspend of zero.
2130
- *
2131
- * Recovery preserves durable cost and outstanding backing, queuing only missing funding until
2132
- * actual settlements provide it. Fresh spawns reserve current funds. Restore debits each liability once,
2133
- * preserves unknown markers, and keeps the original deadline. No I/O or ambient clock is read.
2134
- * @experimental
2135
- */
2136
- /** A reconciliation whose spend the pool cannot verify, thrown after the reservation settled.
2137
- * `budgetViolation` carries any measured overspend on the same settlement. */
2138
- var BudgetReconcileFault = class extends Error {
2139
- budgetViolation;
2140
- constructor(message, budgetViolation) {
2141
- super(message);
2142
- this.name = "BudgetReconcileFault";
2143
- if (budgetViolation !== void 0) this.budgetViolation = budgetViolation;
2144
- }
2145
- };
2146
- /**
2147
- * A waiting reservation that can never be granted: every open reservation settled and the free
2148
- * balance still cannot cover it. `shortfalls` are the channels that did not fit at that moment.
2149
- */
2150
- var ReservationWaitRefused = class extends Error {
2151
- reason = "budget-exhausted";
2152
- shortfalls;
2153
- constructor(shortfalls) {
2154
- super(`budget-exhausted: nothing left running could return enough budget for this worker (${shortfalls.map((shortfall) => `${shortfall.channel} requested ${shortfall.requested}, free ${shortfall.free}`).join("; ")})`);
2155
- this.name = "ReservationWaitRefused";
2156
- this.shortfalls = shortfalls;
2157
- }
2158
- };
2159
- /** Private lease operations; public pools retain their existing interface. */
2160
- const budgetRecoverySeeds = /* @__PURE__ */ new WeakMap();
2161
- const budgetLeases = /* @__PURE__ */ new WeakMap();
2162
- const budgetLeaseUpdates = /* @__PURE__ */ new WeakMap();
2163
- async function refreshBudgetLease(pool) {
2164
- await budgetLeaseUpdates.get(pool)?.();
2165
- }
2166
- /**
2167
- * Create a conserved reservation pool from a root `Budget`. `runStartedAtMs` is the WALL-CLOCK
2168
- * instant the root was recorded; the deadline is `runStartedAtMs + root.deadlineMs` (`0` when
2169
- * none), an absolute instant. A resumed pool passes the ORIGINAL instant.
2170
- */
2171
- function createBudgetPool(root, runStartedAtMs, restore = {}) {
2172
- assertValidBudget(root, "root budget");
2173
- if (!Number.isFinite(runStartedAtMs) || runStartedAtMs < 0) throw new Error("budget runStartedAtMs must be a non-negative finite number");
2174
- const ledger = openBudgetLedger(root, runStartedAtMs);
2175
- let nextTicketId = 0;
2176
- const open = /* @__PURE__ */ new Map();
2177
- /** Tickets admitted to wait for budget, in the order they asked. Each is also in `open`. */
2178
- const waiting = [];
2179
- /** Open tickets that hold nothing: waiting, or refused and not yet reconciled. */
2180
- const holdingNothing = /* @__PURE__ */ new Set();
2181
- const leases = new ReservationLeases(ledger, root, open, holdingNothing, waiting, grantWaiting, recover, budgetRecoverySeeds.get(restore));
2182
- /** Grant waiting tickets in the order they asked while the head fits. When the head does not
2183
- * fit and no reservation is held that could return budget, nothing can ever cover it: refuse
2184
- * it, and go on with the next. */
2185
- function grantWaiting() {
2186
- while (waiting.length > 0) {
2187
- const head = waiting[0];
2188
- if (head.refresh) head.budget = head.refresh();
2189
- const shortfalls = ledgerShortfalls(ledger, head.budget, head.keep).filter((entry) => !head.refresh || entry.closedByUnknownSpend || entry.requested > 0);
2190
- if (shortfalls.length === 0) {
2191
- waiting.shift();
2192
- ledgerTake(ledger, head.budget);
2193
- holdingNothing.delete(head.ticket.id);
2194
- head.acquired?.();
2195
- head.grant();
2196
- continue;
2197
- }
2198
- if (leases.hasRefundSource(head.ticket.id) && !shortfalls.some((entry) => entry.closedByUnknownSpend)) return;
2199
- waiting.shift();
2200
- head.refuse(new ReservationWaitRefused(shortfalls));
2201
- }
2202
- }
2203
- /** Admit `b` to wait: it holds nothing until settlements return enough, in the order asked. */
2204
- function admitToWait(b, holder, keep) {
2205
- const id = nextTicketId++;
2206
- open.set(id, holder);
2207
- const ticket = ticketFor(id, b, ledger);
2208
- leases.remember(ticket, keep);
2209
- let grant;
2210
- let refuse;
2211
- const granted = new Promise((resolve, reject) => {
2212
- grant = resolve;
2213
- refuse = reject;
2214
- });
2215
- granted.catch(() => void 0);
2216
- holdingNothing.add(id);
2217
- waiting.push({
2218
- ticket,
2219
- budget: b,
2220
- keep,
2221
- grant,
2222
- refuse
2223
- });
2224
- return {
2225
- ok: true,
2226
- ticket,
2227
- granted
2228
- };
2229
- }
2230
- function recover(budget, holder, nodeId, keep) {
2231
- const seed = leases.seeds.get(nodeId);
2232
- if (!seed) return reserve(budget, holder, {
2233
- wait: true,
2234
- keep
2235
- });
2236
- if (seed.closed) throw new ValidationError("closed custody backing cannot authorize execution");
2237
- leases.seeds.delete(nodeId);
2238
- const ticket = ticketFor(nextTicketId++, budget, ledger);
2239
- open.set(ticket.id, holder);
2240
- leases.remember(ticket, keep);
2241
- leases.restore(ticket, budget, seed.spent, seed.held);
2242
- const granted = leases.resume(ticket);
2243
- return {
2244
- ok: true,
2245
- ticket,
2246
- ...leases.isPaused(ticket) ? { granted } : {}
2247
- };
2248
- }
2249
- function reserve(b, holder = { stage: "admitted" }, options = {}) {
2250
- assertValidBudget(b, "reservation budget");
2251
- assertResourceDeclarations(ledger, b);
2252
- if (leases.blocked) return {
2253
- ok: false,
2254
- reason: "budget-exhausted"
2255
- };
2256
- const wantUsd = b.maxUsd ?? 0;
2257
- const keep = options.keep;
2258
- const shortfalls = ledgerShortfalls(ledger, b, keep);
2259
- if (shortfalls.length === 0 && wantUsd > 0 && !ledger.usdCapped) return {
2260
- ok: false,
2261
- reason: "usd-unbudgeted"
2262
- };
2263
- const queueAhead = options.wait === true && waiting.length > 0;
2264
- if (shortfalls.length === 0 && !queueAhead) {
2265
- ledgerTake(ledger, b);
2266
- const id = nextTicketId++;
2267
- open.set(id, holder);
2268
- const ticket = ticketFor(id, b, ledger);
2269
- leases.remember(ticket, keep);
2270
- return {
2271
- ok: true,
2272
- ticket
2273
- };
2274
- }
2275
- if (options.wait !== true || shortfalls.some((shortfall) => shortfall.closedByUnknownSpend) || !leases.hasRefundSource()) return {
2276
- ok: false,
2277
- reason: "budget-exhausted",
2278
- shortfalls
2279
- };
2280
- return admitToWait(b, holder, keep);
2281
- }
2282
- function attribute(ticket, holder) {
2283
- const current = open.get(ticket.id);
2284
- if (current === void 0) return;
2285
- open.set(ticket.id, {
2286
- ...current,
2287
- ...holder
2288
- });
2289
- }
2290
- function reconcile(ticket, spent) {
2291
- if (!open.has(ticket.id)) throw new Error(`budget pool: reconcile of unknown or already-settled ticket ${ticket.id}`);
2292
- assertValidSpend(spent, `budget pool ticket ${ticket.id} spend`);
2293
- ledgerValidateUnits(ledger, spent);
2294
- const heldNothing = holdingNothing.delete(ticket.id);
2295
- const index = waiting.findIndex((entry) => entry.ticket.id === ticket.id);
2296
- if (index >= 0) waiting.splice(index, 1);
2297
- const adjustment = leases.settle(ticket, spent);
2298
- const held = adjustment?.held ?? (heldNothing ? {
2299
- tokens: 0,
2300
- usd: 0,
2301
- iterations: 0,
2302
- resources: void 0
2303
- } : ticket.reserved);
2304
- const charge = adjustment?.charge ?? spent;
2305
- const usdBudgeted = ticket.reserved.usdBudgeted !== false;
2306
- const { overspent, unknownUnderCap } = ledgerClassifyOverspend(ledger, heldNothing && adjustment === void 0 ? held : ticket.reserved, usdBudgeted, spent, heldNothing && adjustment === void 0 ? {
2307
- maxTokens: 0,
2308
- maxIterations: 0
2309
- } : ticket.limits);
2310
- let fault = unknownUnderCap ? `ticket ${ticket.id} reported unknown dollar cost under a dollar-capped budget` : void 0;
2311
- open.delete(ticket.id);
2312
- const resourceSettlement = ledgerSettle(ledger, held, charge, unknownUnderCap);
2313
- fault ??= resourceSettlement.fault;
2314
- grantWaiting();
2315
- const violation = overspent.length === 0 ? void 0 : Object.freeze({ overspent: Object.freeze(overspent.map((entry) => Object.freeze(entry))) });
2316
- if (fault !== void 0) throw new BudgetReconcileFault(`budget pool: ${fault}`, violation);
2317
- return violation;
2318
- }
2319
- function observe(spend, options = {}) {
2320
- assertValidSpend(spend, "observed spend");
2321
- ledgerValidateUnits(ledger, spend);
2322
- const unknownUnderCap = ledger.usdCapped && spend.usdKnown === false;
2323
- if (unknownUnderCap && options.alreadySpent !== true) throw new ValidationError("budget pool: cannot observe unknown dollar cost under a dollar-capped budget");
2324
- const settlement = ledgerObserve(ledger, spend, unknownUnderCap, !options.partial);
2325
- const violation = settlement.fault ?? settlement.rootExceeded;
2326
- if (violation) throw new ValidationError(`budget pool: ${violation}`);
2327
- }
2328
- function readout() {
2329
- return {
2330
- ...ledger.resources.size ? { resources: Object.fromEntries([...ledger.resources].map(([name, value]) => [name, { ...value }])) } : {},
2331
- tokensLeft: ledger.freeTokens,
2332
- tokensCapped: ledger.tokensCapped,
2333
- committedTokens: ledger.committedTokens,
2334
- tokensKnown: !ledger.tokensTainted,
2335
- cacheBreakdownKnown: !ledger.cacheBreakdownTainted,
2336
- usdLeft: ledger.usdCapped ? ledger.usdTainted ? 0 : ledger.freeUsd : 0,
2337
- usdCapped: ledger.usdCapped,
2338
- usdKnown: ledger.usdMeasured && !ledger.usdTainted,
2339
- iterationsLeft: ledger.freeIterations,
2340
- iterationsCapped: ledger.iterationsCapped,
2341
- deadlineMs: ledger.absoluteDeadlineMs,
2342
- reservedTokens: ledger.reservedTokens
2343
- };
2344
- }
2345
- function openReservations() {
2346
- return [...open].map(([ticketId, holder]) => Object.freeze({
2347
- ticketId,
2348
- ...holder
2349
- }));
2350
- }
2351
- function assertNoOpenTickets() {
2352
- if (open.size > 0) throw new Error(`budget pool: ${open.size} reservation(s) still open at join barrier (${openReservations().map(describeLeakedReservation).join("; ")}) — conserved-pool invariant violated`);
2353
- }
2354
- for (const seed of leases.seeds.values()) ledgerTake(ledger, seed.held);
2355
- ledgerRestore(ledger, root, restore);
2356
- for (const seed of leases.seeds.values()) if (!seed.closed) ledgerRestore(ledger, root, { committed: seed.spent });
2357
- const pool = {
2358
- reserve,
2359
- attribute,
2360
- reconcile,
2361
- spendFrom: foldUsage,
2362
- readout,
2363
- observe,
2364
- assertNoOpenTickets,
2365
- openReservations
2366
- };
2367
- budgetLeases.set(pool, leases);
2368
- return pool;
2369
- }
2370
- var ReservationLeases = class {
2371
- ledger;
2372
- root;
2373
- open;
2374
- empty;
2375
- waiting;
2376
- pump;
2377
- recover;
2378
- blocked = false;
2379
- rebased = /* @__PURE__ */ new Map();
2380
- kept = /* @__PURE__ */ new Map();
2381
- tickets = /* @__PURE__ */ new Map();
2382
- seeds;
2383
- constructor(ledger, root, open, empty, waiting, pump, recover, seeds = []) {
2384
- this.ledger = ledger;
2385
- this.root = root;
2386
- this.open = open;
2387
- this.empty = empty;
2388
- this.waiting = waiting;
2389
- this.pump = pump;
2390
- this.recover = recover;
2391
- this.seeds = new Map(seeds.map(({ id, ...seed }) => [id, seed]));
2392
- }
2393
- restore(ticket, ceiling, spent, held) {
2394
- this.rebased.set(ticket.id, {
2395
- ceiling,
2396
- spent,
2397
- held,
2398
- liabilities: held,
2399
- paused: true
2400
- });
2401
- if (!hasAllocation(heldAmounts(held, this.ledger))) this.empty.add(ticket.id);
2402
- }
2403
- remember(ticket, keep) {
2404
- this.tickets.set(ticket.id, ticket);
2405
- this.kept.set(ticket.id, keep && { ...keep });
2406
- }
2407
- hasRefundSource(except) {
2408
- for (const [id, ticket] of this.tickets) {
2409
- if (id === except || !this.open.has(id) || this.empty.has(id)) continue;
2410
- const state = this.rebased.get(id);
2411
- if (hasAllocation(state ? heldAmounts(state.held, this.ledger) : ticket.reserved)) return true;
2412
- }
2413
- return [...this.seeds.values()].some((seed) => !seed.closed && hasAllocation(heldAmounts(seed.held, this.ledger)));
2414
- }
2415
- liabilities(spent) {
2416
- const l = this.ledger;
2417
- const keep = (cap, reserved, committed, reported, unknown = false) => cap === void 0 ? void 0 : Math.max(reserved + Math.max(0, committed - reported), unknown ? Math.max(0, cap - reported) : 0);
2418
- return {
2419
- ...this.root.maxTokens === void 0 ? {} : { maxTokens: keep(this.root.maxTokens, l.reservedTokens, l.committedTokens, chargedTokens(spent.tokens), l.tokensTainted) },
2420
- ...this.root.maxIterations === void 0 ? {} : { maxIterations: keep(this.root.maxIterations, l.reservedIterations, l.committedIterations, spent.iterations) },
2421
- ...this.root.maxUsd === void 0 ? {} : { maxUsd: keep(this.root.maxUsd, l.reservedUsd, l.committedUsd, spent.usd, l.usdTainted || !l.usdMeasured) },
2422
- ...this.root.resources === void 0 ? {} : { resources: Object.fromEntries([...l.resources].map(([name, value]) => [name, {
2423
- unit: value.unit,
2424
- limit: keep(value.limit, value.reserved, value.committed, spent.resources?.[name]?.amount ?? 0, !value.known)
2425
- }])) }
2426
- };
2427
- }
2428
- rebase(ticket, ceiling, spent, liabilities, pause = false) {
2429
- const old = this.rebased.get(ticket.id);
2430
- if (!this.open.has(ticket.id) && !old?.closed) return;
2431
- const state = old ?? {
2432
- ceiling,
2433
- spent: zeroSpend(),
2434
- held: ceiling,
2435
- paused: true,
2436
- liabilities
2437
- };
2438
- state.paused ||= pause;
2439
- const next = this.plannedHold(ticket, ceiling, spent, liabilities, pause);
2440
- const held = heldAmounts(state.held, this.ledger);
2441
- const delta = withBudgetResources(unreportedSpend(spent, state.spent), ceiling, true);
2442
- ledgerSettle(this.ledger, held, delta, this.ledger.usdCapped && delta.usdKnown === false);
2443
- ledgerTake(this.ledger, next);
2444
- state.spent = spent;
2445
- state.held = next;
2446
- state.liabilities = liabilities;
2447
- this.rebased.set(ticket.id, state);
2448
- if (hasAllocation(heldAmounts(next, this.ledger))) this.empty.delete(ticket.id);
2449
- else this.empty.add(ticket.id);
2450
- this.pump();
2451
- }
2452
- plannedHold(ticket, ceiling, spent, liabilities, pause) {
2453
- return pause || (this.rebased.get(ticket.id)?.paused ?? true) ? liabilities : maximumBudget(remainingBudget(ceiling, spent), liabilities);
2454
- }
2455
- resume(ticket) {
2456
- const state = this.rebased.get(ticket.id);
2457
- if (!state?.paused) return Promise.resolve();
2458
- if (!this.open.has(ticket.id)) return Promise.reject(new ValidationError("closed admission cannot resume"));
2459
- const refresh = () => differenceBudget(remainingBudget(state.ceiling, state.spent), state.held);
2460
- const acquired = () => {
2461
- state.paused = false;
2462
- state.held = maximumBudget(remainingBudget(state.ceiling, state.spent), state.held);
2463
- };
2464
- const budget = refresh();
2465
- const keep = this.kept.get(ticket.id);
2466
- const shortfalls = ledgerShortfalls(this.ledger, budget, keep).filter((entry) => entry.closedByUnknownSpend || entry.requested > 0);
2467
- if (shortfalls.length === 0 && (this.waiting.length === 0 || !hasAllocation(heldAmounts(budget, this.ledger)))) {
2468
- ledgerTake(this.ledger, budget);
2469
- this.empty.delete(ticket.id);
2470
- acquired();
2471
- return Promise.resolve();
2472
- }
2473
- if (shortfalls.some((entry) => entry.closedByUnknownSpend) || !this.hasRefundSource(ticket.id)) return Promise.reject(new ReservationWaitRefused(shortfalls));
2474
- const granted = new Promise((grant, refuse) => {
2475
- this.waiting.push({
2476
- ticket,
2477
- budget,
2478
- keep,
2479
- refresh,
2480
- acquired,
2481
- grant,
2482
- refuse
2483
- });
2484
- });
2485
- granted.catch(() => void 0);
2486
- return granted;
2487
- }
2488
- reservation(ticket) {
2489
- return this.rebased.get(ticket.id)?.held;
2490
- }
2491
- isPaused(ticket) {
2492
- return this.rebased.get(ticket.id)?.paused === true;
2493
- }
2494
- isBacked(ticket) {
2495
- return this.open.has(ticket.id) || this.rebased.get(ticket.id)?.closed === true;
2496
- }
2497
- observeTerminal(ticket, spent) {
2498
- const state = this.rebased.get(ticket.id);
2499
- if (state && !state.closed) state.liabilities = completedLeafBacking(state.held, spent, state.spent);
2500
- }
2501
- acknowledgeTeardown(ticket) {
2502
- const state = this.rebased.get(ticket.id);
2503
- if (!state?.closed) return;
2504
- const next = completedLeafBacking(state.held, state.spent, state.spent, true);
2505
- const refund = differenceBudget(state.held, next);
2506
- ledgerSettle(this.ledger, heldAmounts(refund, this.ledger), withBudgetResources(zeroSpend(), state.ceiling, true), false);
2507
- state.held = state.liabilities = next;
2508
- if (!hasAllocation(heldAmounts(next, this.ledger))) this.rebased.delete(ticket.id);
2509
- this.pump();
2510
- }
2511
- settle(ticket, spent) {
2512
- const state = this.rebased.get(ticket.id);
2513
- this.kept.delete(ticket.id);
2514
- this.tickets.delete(ticket.id);
2515
- if (!state) return void 0;
2516
- const charge = unreportedSpend(spent, state.spent);
2517
- const held = differenceBudget(state.held, state.liabilities);
2518
- if (hasAllocation(heldAmounts(state.liabilities, this.ledger))) {
2519
- state.closed = true;
2520
- state.paused = true;
2521
- state.spent = spent;
2522
- state.held = state.liabilities;
2523
- return {
2524
- held: heldAmounts(held, this.ledger),
2525
- charge
2526
- };
2527
- }
2528
- this.rebased.delete(ticket.id);
2529
- return {
2530
- held: heldAmounts(state.held, this.ledger),
2531
- charge
2532
- };
2533
- }
2534
- };
2535
- /** A real leaf result ends model work; named physical backing awaits its cleanup ACK.
2536
- * Unknown past usage remains backed even after the resource itself has been released. */
2537
- function completedLeafBacking(held, spent, prior, cleanupConfirmed = false) {
2538
- const delta = unreportedSpend(spent, prior);
2539
- return {
2540
- ...held,
2541
- ...held.maxTokens === void 0 || spent.tokensKnown === false || spent.tokens.tokensKnown === false ? {} : { maxTokens: 0 },
2542
- ...held.maxIterations === void 0 ? {} : { maxIterations: 0 },
2543
- ...held.maxUsd === void 0 || spent.usdKnown === false ? {} : { maxUsd: 0 },
2544
- ...held.resources === void 0 ? {} : { resources: Object.fromEntries(Object.entries(held.resources).map(([name, value]) => [name, {
2545
- ...value,
2546
- limit: spent.resources?.[name]?.known !== true ? value.limit : cleanupConfirmed ? 0 : Math.max(0, value.limit - (delta.resources?.[name]?.amount ?? 0))
2547
- }])) }
2548
- };
2549
- }
2550
- function heldAmounts(budget, ledger) {
2551
- return {
2552
- tokens: ledger.tokensCapped ? budget.maxTokens ?? 0 : 0,
2553
- iterations: ledger.iterationsCapped ? budget.maxIterations ?? 0 : 0,
2554
- usd: budget.maxUsd ?? 0,
2555
- ...budget.resources ? { resources: budget.resources } : {}
2556
- };
2557
- }
2558
- function remainingBudget(budget, spent) {
2559
- return differenceBudget(budget, {
2560
- maxTokens: chargedTokens(spent.tokens),
2561
- maxIterations: spent.iterations,
2562
- maxUsd: spent.usd,
2563
- resources: Object.fromEntries(Object.entries(spent.resources ?? {}).map(([name, value]) => [name, {
2564
- unit: value.unit,
2565
- limit: value.amount
2566
- }]))
2567
- });
2568
- }
2569
- function maximumBudget(grant, backing) {
2570
- return {
2571
- ...grant,
2572
- ...grant.maxTokens === void 0 ? {} : { maxTokens: Math.max(grant.maxTokens, backing.maxTokens ?? 0) },
2573
- ...grant.maxIterations === void 0 ? {} : { maxIterations: Math.max(grant.maxIterations, backing.maxIterations ?? 0) },
2574
- ...grant.maxUsd === void 0 ? {} : { maxUsd: Math.max(grant.maxUsd, backing.maxUsd ?? 0) },
2575
- ...grant.resources === void 0 ? {} : { resources: Object.fromEntries(Object.entries(grant.resources).map(([name, value]) => [name, {
2576
- unit: value.unit,
2577
- limit: Math.max(value.limit, backing.resources?.[name]?.limit ?? 0)
2578
- }])) }
2579
- };
2580
- }
2581
- function differenceBudget(total, prior) {
2582
- return {
2583
- ...total.maxTokens === void 0 ? {} : { maxTokens: Math.max(0, total.maxTokens - (prior.maxTokens ?? 0)) },
2584
- ...total.maxIterations === void 0 ? {} : { maxIterations: Math.max(0, total.maxIterations - (prior.maxIterations ?? 0)) },
2585
- ...total.maxUsd === void 0 ? {} : { maxUsd: Math.max(0, total.maxUsd - (prior.maxUsd ?? 0)) },
2586
- ...total.resources === void 0 ? {} : { resources: Object.fromEntries(Object.entries(total.resources).map(([name, value]) => [name, {
2587
- unit: value.unit,
2588
- limit: Math.max(0, value.limit - (prior.resources?.[name]?.limit ?? 0))
2589
- }])) }
2590
- };
2591
- }
2592
- function hasAllocation(budget) {
2593
- return budget.tokens > 0 || budget.iterations > 0 || budget.usd > 0 || Object.values(budget.resources ?? {}).some((value) => value.limit > 0);
2594
- }
2595
- /** A reservation must name a limit for exactly the resources the root declared, in its units. */
2596
- function assertResourceDeclarations(ledger, b) {
2597
- for (const [name, state] of ledger.resources) {
2598
- const wanted = b.resources?.[name];
2599
- if (!wanted) throw new ValidationError(`resource ${name}: child must declare its limit`);
2600
- if (wanted.unit !== state.unit) throw new ValidationError(`resource ${name}: unit mismatch`);
2601
- }
2602
- for (const name of Object.keys(b.resources ?? {})) if (!ledger.resources.has(name)) throw new ValidationError(`resource ${name}: root must declare its limit`);
2603
- }
2604
- function ticketFor(id, b, ledger) {
2605
- return {
2606
- id,
2607
- limits: {
2608
- ...b.maxTokens === void 0 ? {} : { maxTokens: b.maxTokens },
2609
- ...b.maxIterations === void 0 ? {} : { maxIterations: b.maxIterations }
2610
- },
2611
- reserved: {
2612
- ...b.resources === void 0 ? {} : { resources: Object.fromEntries(Object.entries(b.resources).map(([name, value]) => [name, { ...value }])) },
2613
- tokens: ledger.tokensCapped ? b.maxTokens ?? 0 : 0,
2614
- usd: b.maxUsd ?? 0,
2615
- iterations: ledger.iterationsCapped ? b.maxIterations ?? 0 : 0,
2616
- usdBudgeted: b.maxUsd !== void 0
2617
- }
2618
- };
2619
- }
2620
- /** Name one leaked reservation by its holder. */
2621
- function describeLeakedReservation(leak) {
2622
- const parts = [`ticket ${leak.ticketId}`];
2623
- if (leak.childId !== void 0) parts.push(`child ${leak.childId}`);
2624
- if (leak.label !== void 0) parts.push(`label ${leak.label}`);
2625
- if (leak.assignment !== void 0) parts.push(`assignment ${leak.assignment}`);
2626
- parts.push(`stage ${leak.stage}`);
2627
- return parts.join(", ");
2628
- }
2629
- // src/runtime/supervise/credential-refusal.ts
2630
- /**
2631
- * The account owner refused an unsupported credential route before dispatch. This is the
2632
- * provider SDK's machine contract, not an HTTP status, class-name guess, or prose match.
2633
- */
2634
- function isCredentialRefusal(error) {
2635
- if (!(error instanceof Error)) return false;
2636
- try {
2637
- return Object.getOwnPropertyDescriptor(error, "code")?.value === "model_credential_refused";
2638
- } catch {
2639
- return false;
2640
- }
2641
- }
2642
- /**
2643
- * The message prefix of agent-provider-tangle's generic credential command failure. The provider
2644
- * exports no type or constant for it, so the kernel credential test raises it through the
2645
- * provider's own command resolver and fails when the message drifts.
2646
- */
2647
- const CREDENTIAL_COMMAND_FAILURE = "Tangle credential command failed (";
2648
- /**
2649
- * The exit code of agent-provider-tangle's credential command (`acct sandbox resolve`) in the
2650
- * error or its causes, or undefined.
2651
- *
2652
- * The provider reports the command's generic failure as `Tangle credential command failed
2653
- * (exit N)` with the process's `exitCode` and `terminationSignal` as own fields; a refusal it can
2654
- * parse (exit 75 or 78) arrives typed instead and is classified on its code. Before any dispatch
2655
- * this failure ran nothing, and counting it as an unresolved pending invocation stopped 10 nodes
2656
- * and lost 7 runs between 2026-10-12 and 2026-10-19 (test-acceptance-canary-20261015c met 26 in
2657
- * 2.2 minutes). It pauses like a capacity refusal, bounded by the deadline, the budget and
2658
- * cancellation as every pause is.
2659
- */
2660
- function credentialCommandExit(error) {
2661
- for (const link of causeChain(error)) try {
2662
- if (!(link instanceof Error) || !link.message.startsWith(CREDENTIAL_COMMAND_FAILURE) || !Object.hasOwn(link, "terminationSignal")) continue;
2663
- const exitCode = Reflect.get(link, "exitCode");
2664
- if (typeof exitCode === "number" && Number.isInteger(exitCode) && exitCode > 0) return exitCode;
2665
- } catch {}
2666
- }
2667
- // src/runtime/json-content-hash.ts
2668
- /**
2669
- * Stable hash for the trace payload. Not cryptographic — only used so
2670
- * downstream eval pipelines can group iterations whose task / output is the
2671
- * same. Bare structural hash; non-JSON values stringify via their `toString`.
2672
- */
2673
- function hashJson(value) {
2674
- const str = jsonContentText(value);
2675
- return fnv1a32Hex(str);
2676
- }
2677
- /** JSON text with String fallback, including quoted JSON strings and cyclic values. */
2678
- function jsonContentText(value) {
2679
- let str;
2680
- try {
2681
- str = JSON.stringify(value) ?? String(value);
2682
- } catch {
2683
- str = String(value);
2684
- }
2685
- return str;
2686
- }
2687
- // src/runtime/supervise/executor-outcome.ts
2688
- const outcomeSchema = AgentTurnResultSchema.pick({
2689
- success: true,
2690
- error: true,
2691
- failure: true
2692
- }).extend({
2693
- errorCode: AgentTurnResultSchema.shape.error,
2694
- statusCode: z.number().int().min(400).max(599).optional()
2695
- }).strict();
2696
- /** Read only the executor envelope; application output and grades are not execution status. */
2697
- function executorFailure(result) {
2698
- if (result.outcome === void 0) return void 0;
2699
- const outcome = outcomeSchema.parse(result.outcome);
2700
- if (outcome.success) return void 0;
2701
- return {
2702
- error: outcome.error ?? "Agent execution failed",
2703
- ...outcome.failure === void 0 ? {} : { failure: outcome.failure },
2704
- ...outcome.errorCode === void 0 ? {} : { errorCode: outcome.errorCode },
2705
- ...outcome.statusCode === void 0 ? {} : { statusCode: outcome.statusCode }
2706
- };
2707
- }
2708
- /** The failure text alone, for callers that record a settlement reason. */
2709
- function executorFailureReason(result) {
2710
- return executorFailure(result)?.error;
2711
- }
2712
- /** Canonical producer classifications take precedence over the legacy code/message vocabulary. */
2713
- const TURN_FAILURE_POLICY = {
2714
- capacity: {
2715
- classification: "unavailable",
2716
- failureKind: "infra",
2717
- infra: true,
2718
- code: "upstream-unavailable",
2719
- owner: "platform"
2720
- },
2721
- caller: {
2722
- classification: "terminal",
2723
- failureKind: "deterministic",
2724
- infra: void 0,
2725
- code: "request-rejected",
2726
- owner: "caller"
2727
- },
2728
- infra: {
2729
- classification: "transient",
2730
- failureKind: "infra",
2731
- infra: true,
2732
- code: "transport",
2733
- owner: "platform"
2734
- },
2735
- work: {
2736
- classification: "transient",
2737
- failureKind: "deterministic",
2738
- infra: void 0,
2739
- code: "work-failed",
2740
- owner: "work"
2741
- }
2742
- };
2743
- const turnFailurePolicy = (failure) => TURN_FAILURE_POLICY[failure.kind];
2744
- /** Validate the shared contract on an envelope or any wrapped cause; malformed legacy data falls back. */
2745
- function turnFailureOf(value) {
2746
- for (const link of causeChain(value)) try {
2747
- const parsed = AgentTurnFailureSchema.safeParse(Reflect.get(link, "failure"));
2748
- if (parsed.success) return parsed.data;
2749
- } catch {}
2750
- }
2751
- const PLATFORM_FAILURE_CODES = new Set([
2752
- "QUOTA_EXCEEDED",
2753
- "provider_quota_exceeded",
2754
- "provider_quota_exhausted",
2755
- "buffer.overflow",
2756
- "EXECUTION_INTERRUPTED",
2757
- "TIMEOUT",
2758
- "ECONNRESET",
2759
- "ECONNREFUSED",
2760
- "ETIMEDOUT",
2761
- "EPIPE",
2762
- "EADDRINUSE"
2763
- ].map((code) => code.toLowerCase()));
2764
- const PLATFORM_FAILURE_MESSAGES = [
2765
- /^terminated$/u,
2766
- /Execution exceeded its time limit/u,
2767
- /Platform key[- ]verification/u,
2768
- /Platform key delegation failed/u,
2769
- /Bad Gateway/iu,
2770
- /Cannot connect to API/u,
2771
- /Failed to connect to Sandbox API/u,
2772
- /fetch failed/u,
2773
- /A sandbox lifecycle operation is already in progress/u,
2774
- /exceeded its string bound/u,
2775
- /exceeds its JSON bound/u,
2776
- /No provider served model/u,
2777
- /Invalid API key/u,
2778
- /provider_quota_exceeded|provider_quota_exhausted|QUOTA_EXCEEDED|rate limit/iu,
2779
- /is at its box cap/u,
2780
- /could not materialize the seat credential/u,
2781
- /OpenCode exited while a model step was still in progress/u
2782
- ];
2783
- /** Producer-owned classification first; legacy platform codes/messages otherwise leave attribution unknown. */
2784
- function executorFailureInfra(failure) {
2785
- if (failure.failure) return turnFailurePolicy(failure.failure).infra;
2786
- const code = failure.errorCode?.toLowerCase();
2787
- if (code !== void 0 && PLATFORM_FAILURE_CODES.has(code)) return true;
2788
- return PLATFORM_FAILURE_MESSAGES.some((pattern) => pattern.test(failure.error)) ? true : void 0;
2789
- }
2790
- /** Preserve deterministic result pointers across built-in executors and replay.
2791
- * FNV-1a is an identity convention here, not a cryptographic integrity check. */
2792
- function contentRef(prefix, value) {
2793
- const str = jsonContentText(value);
2794
- return `${prefix}:${fnv1a32Hex(str)}`;
2795
- }
2796
- // src/durable/content-address.ts
2797
- /** Stable content address shared by result and trace artifacts. */
2798
- function contentAddress(artifact) {
2799
- return `sha256:${createHash("sha256").update(stableStringify(artifact), "utf-8").digest("hex")}`;
2800
- }
2801
- function stableStringify(value) {
2802
- if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
2803
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
2804
- return `{${Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
2805
- }
2806
- // src/runtime/supervise/trace-root.ts
2807
- /**
2808
- * Trace roots: one Merkle root over the events a turn archived, so a single event can be proven
2809
- * against the hash its journal record carries without reading the rest of the archive.
2810
- *
2811
- * The leaves are the archived events in stored order, each hashed with its own random salt, then
2812
- * one summary leaf that counts the events and the tool calls among them. The artifact keeps every
2813
- * leaf and salt and is stored under its content address in the run's result blob store, beside
2814
- * the archive it commits to. A verifier recomputes leaf `i` from `events[i]` of the `outRef` blob
2815
- * and `salts[i]`, checks it against `leaves[i]`, and checks `merkleRoot(leaves)` against the root
2816
- * the journal recorded. Leaf identity is `(outRef, index)`: an event's own `id` is a session
2817
- * buffer offset and is absent on terminal events.
2818
- *
2819
- * One event array has one root per node. A result always gets its own. Evidence whose events the
2820
- * node already rooted gets none: a retained owner's deferred workspace capture stores the turn it
2821
- * just committed again, with the workspace fields added, under another `outRef`. A second root
2822
- * over the same events would give one turn two count candidates and two run-root leaves.
2823
- *
2824
- * Salts are random, so a trace root can never be recomputed from the archive alone. Recovery and
2825
- * replay copy the recorded receipt; they never derive a new one.
2826
- */
2827
- /** Schema identifier of a stored {@link TraceRootArtifact}. */
2828
- const TRACE_ROOT_SCHEMA = "agent-runtime.trace-root.v1";
2829
- /** Schema identifier of the {@link TraceSummary} carried by a trace root's last leaf. */
2830
- const TRACE_SUMMARY_SCHEMA = "agent-runtime.trace-summary.v1";
2831
- /** @internal */
2832
- function traceRootWriter(blobs, nodeId) {
2833
- const roots = /* @__PURE__ */ new Map();
2834
- /** Digests of the event arrays this node holds a stored root for. */
2835
- const rooted = /* @__PURE__ */ new Set();
2836
- const memoized = (outRef, out, evidence) => {
2837
- const known = roots.get(outRef);
2838
- if (known !== void 0) return known;
2839
- const pending = (async () => {
2840
- const events = archivedEvents(out);
2841
- if (events === void 0) return void 0;
2842
- if (evidence) {
2843
- await Promise.allSettled([...roots.values()]);
2844
- if (rooted.has(await eventsDigest(events))) return void 0;
2845
- }
2846
- const built = await traceRootArtifact(nodeId, outRef, events).catch((error) => {
2847
- if (error instanceof LedgerCanonicalizationError) return void 0;
2848
- throw error;
2849
- });
2850
- if (built === void 0) return void 0;
2851
- const { artifact, digest } = built;
2852
- await setImmediate$1();
2853
- const ref = contentAddress(artifact);
2854
- await setImmediate$1();
2855
- await blobs.put(ref, artifact);
2856
- rooted.add(digest);
2857
- return {
2858
- root: artifact.root,
2859
- ref,
2860
- leafCount: artifact.leafCount
2861
- };
2862
- })();
2863
- roots.set(outRef, pending);
2864
- pending.catch(() => roots.delete(outRef));
2865
- return pending;
2866
- };
2867
- return {
2868
- write: (outRef, result) => result.outputFormat === "json" ? Promise.resolve(void 0) : memoized(outRef, result.out, false),
2869
- writeEvidence: (outRef, out) => memoized(outRef, out, true),
2870
- async recorded(outRef) {
2871
- if (outRef === void 0) return void 0;
2872
- return roots.get(outRef)?.catch(() => void 0);
2873
- }
2874
- };
2875
- }
2876
- function archivedEvents(out) {
2877
- if (out === null || typeof out !== "object" || Array.isArray(out)) return void 0;
2878
- const events = out.events;
2879
- return Array.isArray(events) ? events : void 0;
2880
- }
2881
- /** The longest stretch of hashing between yields to the event loop. Hashing runs about 40 µs per
2882
- * event, and the largest archived turn holds 65,276 events: unbroken, that blocked the process
2883
- * for 2.7 s while sibling turns, coordination calls and heartbeats waited. A slice is measured
2884
- * in time, not events, because one event can be megabytes. */
2885
- const SLICE_MS = 10;
2886
- /** Yields to the event loop once a slice has run `SLICE_MS`; `undefined` while it has not. */
2887
- function slicer() {
2888
- let started = performance.now();
2889
- return () => {
2890
- if (performance.now() - started < SLICE_MS) return void 0;
2891
- return setImmediate$1().then(() => {
2892
- started = performance.now();
2893
- });
2894
- };
2895
- }
2896
- /** The identity of an event array as stored: the digest of each event's stored JSON, in order.
2897
- * Two arrays with one digest have the same leaves' content. */
2898
- async function eventsDigest(events) {
2899
- const hash = createHash("sha256");
2900
- const slice = slicer();
2901
- for (const event of events) {
2902
- const yielded = slice();
2903
- if (yielded !== void 0) await yielded;
2904
- hash.update(storedText(event)).update("\n");
2905
- }
2906
- return hash.digest("hex");
2907
- }
2908
- async function traceRootArtifact(nodeId, outRef, archived) {
2909
- const salts = [];
2910
- const leaves = [];
2911
- const tree = merkleTree();
2912
- const identity = createHash("sha256");
2913
- const tools = createSandboxToolPartState();
2914
- const toolCalls = /* @__PURE__ */ new Map();
2915
- const slice = slicer();
2916
- for (const event of archived) {
2917
- const yielded = slice();
2918
- if (yielded !== void 0) await yielded;
2919
- const text = storedText(event);
2920
- identity.update(text).update("\n");
2921
- const stored = JSON.parse(text);
2922
- const salt = merkleSalt();
2923
- const leaf = merkleLeafHash(canonicalString(jsonDocument(stored)), salt);
2924
- salts.push(salt);
2925
- leaves.push(leaf);
2926
- tree.push(leaf);
2927
- for (const projected of mapSandboxToolEvent(stored, tools)) {
2928
- if (projected.type !== "tool_call") continue;
2929
- toolCalls.set(projected.toolName, (toolCalls.get(projected.toolName) ?? 0) + 1);
2930
- }
2931
- }
2932
- const summary = {
2933
- schema: TRACE_SUMMARY_SCHEMA,
2934
- events: archived.length,
2935
- toolCalls: Object.fromEntries(toolCalls)
2936
- };
2937
- const salt = merkleSalt();
2938
- const leaf = merkleLeafHash(canonicalString(summary), salt);
2939
- salts.push(salt);
2940
- leaves.push(leaf);
2941
- tree.push(leaf);
2942
- return {
2943
- artifact: {
2944
- schema: TRACE_ROOT_SCHEMA,
2945
- nodeId,
2946
- outRef,
2947
- root: tree.root(),
2948
- leafCount: leaves.length,
2949
- salts,
2950
- leaves,
2951
- summary
2952
- },
2953
- digest: identity.digest("hex")
2954
- };
2955
- }
2956
- /**
2957
- * `merkleRoot(leaves)` built as the leaves arrive, so its interior hashes run inside the sliced
2958
- * leaf loop instead of in one block after it (0.4 s for 65,277 leaves). It keeps the perfect
2959
- * subtrees of RFC 9162, largest first, and joins two with ledger-core's own interior hash,
2960
- * `merkleRoot([left, right])`; the root folds them from the right, as RFC 9162 splits at the
2961
- * largest power of two.
2962
- */
2963
- function merkleTree() {
2964
- const peaks = [];
2965
- return {
2966
- push(leaf) {
2967
- let hash = leaf;
2968
- let size = 1;
2969
- while (peaks.at(-1)?.size === size) {
2970
- hash = merkleRoot([peaks.pop().hash, hash]);
2971
- size *= 2;
2972
- }
2973
- peaks.push({
2974
- hash,
2975
- size
2976
- });
2977
- },
2978
- root() {
2979
- let root = peaks.at(-1).hash;
2980
- for (let index = peaks.length - 2; index >= 0; index -= 1) root = merkleRoot([peaks[index].hash, root]);
2981
- return root;
2982
- }
2983
- };
2984
- }
2985
- /** One array item as `JSON.stringify` writes it: `JSON.parse` of it is what `events[i]` holds. */
2986
- function storedText(event) {
2987
- return JSON.stringify(event) ?? "null";
2988
- }
2989
- // src/runtime/supervise/upstream-unavailable.ts
2990
- /** Shared capacity recognition and pause policy for drivers and retained leaves. */
2991
- /** Legacy capacity codes for producers without the canonical failure contract. */
2992
- const UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
2993
- "provider_quota_exhausted",
2994
- "provider_quota_exceeded",
2995
- "provider_rate_limit",
2996
- "upstream_unavailable",
2997
- "provider_key_invalid",
2998
- "rate_limit_exceeded",
2999
- "rate_limit_error",
3000
- "overloaded_error",
3001
- "capacity_exhausted",
3002
- "host_pressure",
3003
- "provisioning_paused"
3004
- ]);
3005
- /** 429 Too Many Requests, 503 Service Unavailable, and 529, which Anthropic uses for overload. */
3006
- const UNAVAILABLE_STATUSES = /* @__PURE__ */ new Set([
3007
- 429,
3008
- 503,
3009
- 529
3010
- ]);
3011
- /** Legacy text fallback reads whole code tokens and explicitly framed HTTP statuses. */
3012
- function unavailableSignalInText(text) {
3013
- for (const token of text.toLowerCase().matchAll(/[a-z_]+/gu)) if (UNAVAILABLE_CODES.has(token[0])) return token[0];
3014
- const status = printedStatus(text);
3015
- return status !== void 0 && UNAVAILABLE_STATUSES.has(status) ? `http-${status}` : void 0;
3016
- }
3017
- /** The HTTP status a harness printed in its `status code <n>` framing, if any. */
3018
- const printedStatus = (text) => {
3019
- const status = text.match(/\bstatus code (\d{3})\b/u)?.[1];
3020
- return status === void 0 ? void 0 : Number(status);
3021
- };
3022
- /** Held room for an otherwise placeable shape is temporary capacity; oversized shapes stay refused. */
3023
- const HELD_ROOM_REFUSAL = /\bits room is held placeable\b/u;
3024
- /**
3025
- * The code or status that marks a failed turn outcome as an upstream capacity refusal, or
3026
- * `undefined`. The outcome's own `errorCode` decides first, with Sandbox's held-room text beside its
3027
- * code; other text is read only when no code or numeric status decided, because a harness CLI can
3028
- * report the router's refusal as text.
3029
- */
3030
- function unavailableSignalOfFailure(failure) {
3031
- const typed = turnFailureOf(failure);
3032
- if (typed) return turnFailurePolicy(typed).classification === "unavailable" ? typed.code : void 0;
3033
- const code = failure.errorCode?.toLowerCase();
3034
- if (code !== void 0 && UNAVAILABLE_CODES.has(code)) return code;
3035
- if (code === "no_host_fits_shape" && HELD_ROOM_REFUSAL.test(failure.error)) return "host-room-held";
3036
- if (failure.statusCode !== void 0) return UNAVAILABLE_STATUSES.has(failure.statusCode) ? `http-${failure.statusCode}` : void 0;
3037
- return unavailableSignalInText(failure.error);
3038
- }
3039
- const DEFAULT_UNAVAILABLE_PAUSE_MS = 15e3;
3040
- const DEFAULT_MAX_UNAVAILABLE_PAUSE_MS = 3e5;
3041
- /**
3042
- * The pause after the `consecutive`-th refusal in a row (1-based): the first pause, doubled per
3043
- * earlier refusal, up to the ceiling. The one rule for a driver that re-enters and a leaf that
3044
- * continues in its environment, so an outage costs both the same number of turns.
3045
- */
3046
- function unavailablePauseMs(consecutive, policy = {}) {
3047
- return cappedDoublingMs(consecutive, policy.unavailablePauseMs ?? DEFAULT_UNAVAILABLE_PAUSE_MS, policy.maxUnavailablePauseMs ?? DEFAULT_MAX_UNAVAILABLE_PAUSE_MS);
3048
- }
3049
- /** `first` doubled per earlier consecutive wait (`consecutive` is 1-based), up to `ceiling`. */
3050
- function cappedDoublingMs(consecutive, first, ceiling) {
3051
- const start = Math.max(0, first);
3052
- return Math.min(Math.max(start, ceiling), start * 2 ** Math.max(0, consecutive - 1));
3053
- }
3054
- /** The longest hold a refusal's own reset time may ask for: a 5-hour plan window plus a margin. */
3055
- const MAX_RESET_HOLD_MS = 216e5;
3056
- /** How often a refusal whose reset is beyond {@link MAX_RESET_HOLD_MS} is probed again. */
3057
- const FAR_RESET_PROBE_MS = 36e5;
3058
- /** Read canonical reset/retry-after before legacy structured reset fields; never parse text. */
3059
- function reportedResetAt(error, nowMs = Date.now()) {
3060
- const typed = turnFailureOf(error);
3061
- if (typed) return typed.resetAt !== void 0 ? Date.parse(typed.resetAt) : typed.retryAfterMs !== void 0 ? nowMs + typed.retryAfterMs : void 0;
3062
- for (const cause of causeChain(error)) {
3063
- let value;
3064
- try {
3065
- value = Reflect.get(cause, "resetAt");
3066
- } catch {
3067
- continue;
3068
- }
3069
- const at = typeof value === "number" ? value : typeof value === "string" ? Date.parse(value) : NaN;
3070
- if (Number.isFinite(at)) return at;
3071
- }
3072
- }
3073
- /**
3074
- * Until when a reported reset holds a pause, or `undefined` for none (absent or past: the caller
3075
- * keeps its doubling pause). A reset more than MAX_RESET_HOLD_MS ahead is held FAR_RESET_PROBE_MS
3076
- * at a time; on magnetar-rust-20261019c a far reset drew about 12 attempts an hour.
3077
- */
3078
- function unavailableResumeAt(resetAt, nowMs) {
3079
- if (resetAt === void 0 || resetAt <= nowMs) return void 0;
3080
- return resetAt - nowMs > MAX_RESET_HOLD_MS ? nowMs + FAR_RESET_PROBE_MS : resetAt;
3081
- }
3082
- /**
3083
- * Spread a wait over `[ms / 2, ms]`. Every driver a Platform restart cut off fails within the same
3084
- * seconds; without jitter their doubling schedules stay aligned and they all re-enter together
3085
- * when the service returns. `random` returns a value in `[0, 1)`; 0 keeps the full wait.
3086
- */
3087
- function jitteredMs(ms, random) {
3088
- return ms - Math.floor(ms / 2 * Math.min(1, Math.max(0, random())));
3089
- }
3090
- // src/runtime/supervise/retained-executor.ts
3091
- /** Scope owns the durable writer; provider executors only publish sanitized admissions. */
3092
- const retainedExecutorSeamKey = "runtime.retainedExecutor";
3093
- /** @internal A capture may finish after cancellation; its durable citation remains appendable. */
3094
- function executorEvidenceWriter(args) {
3095
- const traceRoots = args.traceRoots ?? traceRootWriter(args.blobs, args.nodeId);
3096
- return async (out) => {
3097
- const snapshot = detachedSnapshot(out, "executor evidence");
3098
- const outRef = contentAddress(snapshot);
3099
- await args.blobs.put(outRef, snapshot);
3100
- const traceRoot = await traceRoots.writeEvidence(outRef, snapshot);
3101
- await args.journal.appendEvent(args.rootId, {
3102
- kind: "execution-evidence",
3103
- id: args.nodeId,
3104
- outRef,
3105
- ...traceRoot === void 0 ? {} : { traceRoot },
3106
- seq: args.nextSequence(),
3107
- at: new Date(args.now()).toISOString()
3108
- });
3109
- };
3110
- }
3111
- function retainedExecutorContext(ctx) {
3112
- return ctx.seams[retainedExecutorSeamKey];
3113
- }
3114
- const causeMessages = {
3115
- unobservable: "retained provider execution requires reconciliation before replacement",
3116
- "provider-contract": "retained provider response violated its contract; original execution remains unresolved",
3117
- "request-rejected": "retained provider execution was refused before it ran; nothing to reconcile",
3118
- transport: "retained provider execution lost its transport; status in doubt",
3119
- "nested-recovery": "retained nested execution requires recovery before replacement"
3120
- };
3121
- /**
3122
- * agent-provider-tangle's `TangleDispatchNotAdmittedError` (since 3.6.11): Sandbox answered a
3123
- * dispatch with `dispatched: false` and named another execution that owns the session or turn.
3124
- * Nothing ran for the request.
3125
- */
3126
- const DISPATCH_NOT_ADMITTED_CODE = "DISPATCH_NOT_ADMITTED";
3127
- /**
3128
- * agent-provider-tangle's `TangleDispatchExecutionChangedError` (since 5.0.8): Sandbox CONFIRMED
3129
- * the dispatch but named an execution id other than the requested run's. The provider's own
3130
- * answer violates its contract, identically on every retry (agent-sdk #475: 107 retries over
3131
- * 39 minutes, all classed transient, before the run settled all-children-down).
3132
- */
3133
- const DISPATCH_EXECUTION_CHANGED_CODE = "DISPATCH_EXECUTION_CHANGED";
3134
- /**
3135
- * The Sandbox SDK's codes for a box that cannot be provisioned for this profile, by the HTTP status
3136
- * each is sent with: a mount outside the box's writable roots and a materialization that would fail
3137
- * the same way again (422), a mounted file changed on both sides since the last mount (409). Two
3138
- * bare 500s are read by text: that conflict before Sandbox typed it, and two mounts of one target
3139
- * (magnetar-rust-sandbox-20261031g's and 20261031h's managers retried them every turn). On run
3140
- * queens21-h22-capture-root-1-20261019c 154 attempts across 4 workers retried a read-only mount of
3141
- * `/work/queens21_count.c` as a lost transport. The same codes at another status may be transient.
3142
- */
3143
- const PROVISIONING_REFUSALS = /* @__PURE__ */ new Map([
3144
- ["PROFILE_FILE_MOUNT_OUTSIDE_ROOTS", 422],
3145
- ["PROFILE_FILES_MATERIALIZE_FAILED", 422],
3146
- ["PROFILE_FILE_CONFLICT", 409]
3147
- ]);
3148
- const TEXT_REFUSALS = /Refusing to replace existing profile file: |Duplicate profile file target: /u;
3149
- /** True for an error that is itself a deterministic provisioning refusal (see above). */
3150
- const isProvisioningRefusal = (value) => refusedAs(PROVISIONING_REFUSALS, value) || value instanceof Error && TEXT_REFUSALS.test(errorProperty(value, "message") ?? "");
3151
- /**
3152
- * Platform refusals the orchestrator answers BEFORE it forwards a dispatch, by machine code and
3153
- * the HTTP status it is sent with. Run 002f69d5: `HUB_CREDENTIAL_REFRESH_FAILED` at 503 left no
3154
- * effect, yet read as a lost transport. Any other 5xx stays a transport failure, in doubt.
3155
- */
3156
- const PLATFORM_PREDISPATCH_REFUSALS = /* @__PURE__ */ new Map([["HUB_CREDENTIAL_REFRESH_FAILED", 503]]);
3157
- /** True for an error that is itself a named platform refusal of a dispatch (see above). */
3158
- const isPlatformPredispatchRefusal = (value) => refusedAs(PLATFORM_PREDISPATCH_REFUSALS, value);
3159
- /** True when `value` carries one of `refusals`' codes at the status that code is sent with. */
3160
- function refusedAs(refusals, value) {
3161
- try {
3162
- const code = readString(value, "code");
3163
- const status = readNumber(value, "status") ?? readNumber(value, "statusCode");
3164
- const expected = code === void 0 ? void 0 : refusals.get(code);
3165
- return expected !== void 0 && expected === status;
3166
- } catch {
3167
- return false;
3168
- }
3169
- }
3170
- /** The contract-error codes that wrap a failed READ rather than name a broken answer. */
3171
- const readFailureCodes = /* @__PURE__ */ new Set(["RETAINED_RESULT_READ_FAILED", "RETAINED_CONTROL_REF_READ_FAILED"]);
3172
- /** Socket-level failures, by the codes Node and undici assign, plus the Sandbox SDK's own. */
3173
- const TRANSPORT_ERROR_CODES = /* @__PURE__ */ new Set([
3174
- "ECONNRESET",
3175
- "ECONNREFUSED",
3176
- "ETIMEDOUT",
3177
- "ENOTFOUND",
3178
- "EAI_AGAIN",
3179
- "EPIPE",
3180
- "UND_ERR_SOCKET",
3181
- "UND_ERR_CONNECT_TIMEOUT",
3182
- "UND_ERR_HEADERS_TIMEOUT",
3183
- "UND_ERR_BODY_TIMEOUT",
3184
- "NETWORK_ERROR"
3185
- ]);
3186
- /** Refused by a schema or bound, the same way every attempt: agent-provider-tangle's JsonBoundError
3187
- * (#1204's exhibit 6) or the Sandbox SDK refusing a capability document as HTTP 502, which
3188
- * institutional-compiler-20261031c:s14 retried 44 times as a lost transport. */
3189
- function isSchemaRejection(value) {
3190
- const code = readString(value, "code");
3191
- return readString(value, "name") === "ZodError" || Array.isArray(Reflect.get(value, "issues")) || code === "JSON_BOUND_VIOLATION" || code === "INVALID_CAPABILITY_DOCUMENT";
3192
- }
3193
- function readNumber(value, key) {
3194
- const read = Reflect.get(value, key);
3195
- return typeof read === "number" && Number.isFinite(read) ? read : void 0;
3196
- }
3197
- function readString(value, key) {
3198
- const read = Reflect.get(value, key);
3199
- return typeof read === "string" ? read : void 0;
3200
- }
3201
- const credentialAdmissionRefusals = /* @__PURE__ */ new WeakSet();
3202
- /**
3203
- * Walk the cause chain (bounded, cycle-safe) and name the first cause whose structure decides.
3204
- *
3205
- * `phase` says whether a 4xx or a client deadline can mean "refused before it ran": only at
3206
- * admission. After admission the same status means the provider cannot resolve what it
3207
- * admitted, and the honest name is the refusal.
3208
- */
3209
- function classifyRetainedPendingCause(cause, phase = "execution", onCredentialRefusal) {
3210
- const seen = /* @__PURE__ */ new Set();
3211
- const queue = [{
3212
- value: cause,
3213
- phase
3214
- }];
3215
- let steps = 0;
3216
- while (queue.length > 0 && steps < 12) {
3217
- steps += 1;
3218
- const { value, phase: at } = queue.shift();
3219
- if (typeof value !== "object" || value === null || seen.has(value)) continue;
3220
- seen.add(value);
3221
- if (value instanceof RetainedExecutionPendingError) {
3222
- if (credentialAdmissionRefusals.has(value)) {
3223
- if (at === "execution") return "unobservable";
3224
- onCredentialRefusal?.();
3225
- }
3226
- return value.pendingCause;
3227
- }
3228
- const name = readString(value, "name");
3229
- const code = readString(value, "code");
3230
- const status = readNumber(value, "status") ?? readNumber(value, "statusCode");
3231
- if (name === "RetainedRunProviderContractError") {
3232
- if (code === void 0 || !readFailureCodes.has(code)) return "provider-contract";
3233
- queue.unshift({
3234
- value: Reflect.get(value, "cause"),
3235
- phase: "execution"
3236
- });
3237
- continue;
3238
- }
3239
- if (isSchemaRejection(value)) return at === "admission" ? "request-rejected" : "provider-contract";
3240
- if (code === "DISPATCH_NOT_ADMITTED") return at === "admission" ? "request-rejected" : "provider-contract";
3241
- if (code === "DISPATCH_EXECUTION_CHANGED") return "provider-contract";
3242
- if (isProvisioningRefusal(value)) return "request-rejected";
3243
- if (isCredentialRefusal(value)) {
3244
- if (at === "execution") return "unobservable";
3245
- onCredentialRefusal?.();
3246
- return "request-rejected";
3247
- }
3248
- if (code !== void 0 && TRANSPORT_ERROR_CODES.has(code)) return "transport";
3249
- if (at === "admission" && isPlatformPredispatchRefusal(value)) return "request-rejected";
3250
- if (name === "NetworkError" || name === "ServerError") return "transport";
3251
- if (status === void 0 && at === "admission" && code !== void 0 && UNAVAILABLE_CODES.has(code.toLowerCase())) return "request-rejected";
3252
- if (status !== void 0) {
3253
- if (status >= 500) return "transport";
3254
- if (at === "admission" && status !== 408) return "request-rejected";
3255
- return "unobservable";
3256
- }
3257
- const errors = Reflect.get(value, "errors");
3258
- if (Array.isArray(errors)) for (const member of errors) queue.push({
3259
- value: member,
3260
- phase: at
3261
- });
3262
- queue.push({
3263
- value: Reflect.get(value, "cause"),
3264
- phase: at
3265
- });
3266
- }
3267
- return "unobservable";
3268
- }
3269
- /**
3270
- * Local observation stopped; the retained provider execution has no accepted terminal result.
3271
- *
3272
- * The message names WHICH of the situations in {@link RetainedPendingCause} this is, and the
3273
- * `pendingCause` field carries it as a value the settlement records, so a reader never splits
3274
- * the population on the message's wording. The cause chain is kept for the `caused by` receipt.
3275
- */
3276
- var RetainedExecutionPendingError = class extends Error {
3277
- pendingCause;
3278
- phase;
3279
- constructor(cause, pendingCause = "execution") {
3280
- let credentialRefused = false;
3281
- const resolved = pendingCause === "admission" || pendingCause === "execution" ? classifyRetainedPendingCause(cause, pendingCause, () => {
3282
- credentialRefused = true;
3283
- }) : pendingCause;
3284
- super(causeMessages[resolved], { cause });
3285
- this.name = "RetainedExecutionPendingError";
3286
- this.pendingCause = resolved;
3287
- this.phase = pendingCause === "admission" ? "admission" : "execution";
3288
- if (credentialRefused) credentialAdmissionRefusals.add(this);
3289
- }
3290
- };
3291
- /** The instruction a keyed continuation runs in the held session: the node's task is unchanged,
3292
- * and its conversation and workspace are where the budget stop left them. */
3293
- const BUDGET_CONTINUATION_TASK = "Your previous turn reached its budget and stopped. Your manager gave you more budget to continue the same task in the same session: your conversation so far and the files in your workspace are where you left them. Continue from where you stopped. Do not start over.";
3294
- /** The execution id of a leaf's later invocation, derived from its `execution-input` sequence so
3295
- * a recovery derives the same one. A first invocation keeps the node id. */
3296
- function leafContinuationExecutionId(nodeId, inputSeq) {
3297
- return `${nodeId}:input:${inputSeq}`;
3298
- }
3299
- const retainedPreparations = /* @__PURE__ */ new WeakMap();
3300
- /** Reconstruct an admitted worker through its original composition boundary. @internal */
3301
- function registerRetainedExecutorPreparation(factory, prepare) {
3302
- retainedPreparations.set(factory, prepare);
3303
- return factory;
3304
- }
3305
- function prepareRetainedExecutor(factory, input) {
3306
- return retainedPreparations.get(factory)?.(input);
3307
- }
3308
- // src/runtime/failure-cause.ts
3309
- const MAX_CAUSE_CHARS = 2048;
3310
- const MACHINE_CODE_PATTERN = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u;
3311
- /** @internal Canonical producer-supplied remediation, shared by transport and read models. */
3312
- const runtimeFailureRemediationSchema = z.object({
3313
- cause: z.enum([
3314
- "api_key_selected",
3315
- "provider_unresolved",
3316
- "subscription_unverified",
3317
- "not_authenticated"
3318
- ]),
3319
- fix: z.string().min(1).max(256),
3320
- harness: z.string().min(1).max(64),
3321
- provider: z.string().min(1).max(128).nullable()
3322
- });
3323
- /** @internal Validate Runtime-owned failure values on observation transports. */
3324
- const runtimeFailureCauseSchema = z.object({
3325
- code: z.enum([
3326
- "aborted",
3327
- "deadline-exceeded",
3328
- "budget-exhausted",
3329
- "credential-refused",
3330
- "request-rejected",
3331
- "upstream-unavailable",
3332
- "transport",
3333
- "provider-contract",
3334
- "retained-unobservable",
3335
- "nested-recovery",
3336
- "platform",
3337
- "work-failed",
3338
- "unknown"
3339
- ]),
3340
- owner: z.enum([
3341
- "provider",
3342
- "platform",
3343
- "runtime",
3344
- "work",
3345
- "caller",
3346
- "unknown"
3347
- ]),
3348
- cause: z.string().max(MAX_CAUSE_CHARS),
3349
- /** The upstream or provider machine code the failure carried, when it carried one. */
3350
- providerCode: z.string().regex(MACHINE_CODE_PATTERN).optional(),
3351
- /** The HTTP status the provider's typed error carried with that code. */
3352
- providerStatus: z.number().int().min(100).max(599).optional(),
3353
- /** The provider's typed retry-after hint, in milliseconds. A hint, never an automatic retry. */
3354
- retryAfterMs: z.number().int().nonnegative().optional(),
3355
- remediation: runtimeFailureRemediationSchema.readonly().optional()
3356
- }).readonly();
3357
- const retainedFailures = {
3358
- unobservable: {
3359
- code: "retained-unobservable",
3360
- owner: "provider"
3361
- },
3362
- "provider-contract": {
3363
- code: "provider-contract",
3364
- owner: "provider"
3365
- },
3366
- "request-rejected": {
3367
- code: "request-rejected",
3368
- owner: "caller"
3369
- },
3370
- transport: {
3371
- code: "transport",
3372
- owner: "platform"
3373
- },
3374
- "nested-recovery": {
3375
- code: "nested-recovery",
3376
- owner: "runtime"
3377
- }
3378
- };
3379
- function read(value, key) {
3380
- try {
3381
- return Reflect.get(value, key);
3382
- } catch {
3383
- return;
3384
- }
3385
- }
3386
- const UNATTRIBUTED = {
3387
- code: "unknown",
3388
- owner: "unknown"
3389
- };
3390
- /** One link's own structure: budget refusal, credential refusal, machine code, HTTP status. */
3391
- function structuralFailure(link) {
3392
- if (link instanceof ReservationWaitRefused || read(link, "reason") === "budget-exhausted") return {
3393
- code: "budget-exhausted",
3394
- owner: "caller"
3395
- };
3396
- const code = upstreamMachineCode([link]);
3397
- if (isCredentialRefusal(link) || code === "SEAT_NOT_LOGGED_IN") return {
3398
- code: "credential-refused",
3399
- owner: "caller"
3400
- };
3401
- const status = read(link, "status") ?? read(link, "statusCode");
3402
- if (code === "MODEL_NOT_FOUND") return {
3403
- code: "request-rejected",
3404
- owner: "caller"
3405
- };
3406
- if (typeof code === "string" && UNAVAILABLE_CODES.has(code.toLowerCase())) return {
3407
- code: "upstream-unavailable",
3408
- owner: "platform"
3409
- };
3410
- if (typeof status === "number" && Number.isInteger(status)) {
3411
- if (UNAVAILABLE_STATUSES.has(status)) return {
3412
- code: "upstream-unavailable",
3413
- owner: "platform"
3414
- };
3415
- if (status >= 500 || status === 0 || status === 408) return {
3416
- code: "transport",
3417
- owner: "platform"
3418
- };
3419
- if (status === 401 || status === 403) return {
3420
- code: "credential-refused",
3421
- owner: "caller"
3422
- };
3423
- if (status >= 400) return {
3424
- code: "request-rejected",
3425
- owner: "caller"
3426
- };
3427
- }
3428
- if (typeof code === "string" && TRANSPORT_ERROR_CODES.has(code)) return {
3429
- code: "transport",
3430
- owner: "platform"
3431
- };
3432
- if (typeof code === "string" && PLATFORM_FAILURE_CODES.has(code.toLowerCase())) return {
3433
- code: "platform",
3434
- owner: "platform"
3435
- };
3436
- const name = read(link, "name");
3437
- if (name === "NetworkError" || name === "ServerError") return {
3438
- code: "transport",
3439
- owner: "platform"
3440
- };
3441
- }
3442
- /** @internal The same machine-code validation used by public failure values. */
3443
- function runtimeProviderCode(value) {
3444
- return typeof value === "string" && MACHINE_CODE_PATTERN.test(value) ? value : void 0;
3445
- }
3446
- function upstreamMachineCode(links) {
3447
- const typed = links.map(turnFailureOf).find((value) => value !== void 0);
3448
- if (typed) return runtimeProviderCode(typed.code);
3449
- return [
3450
- "upstreamCode",
3451
- "errorCode",
3452
- "code"
3453
- ].flatMap((field) => links.map((link) => runtimeProviderCode(read(link, field)))).find((code) => code !== void 0);
3454
- }
3455
- /** The typed HTTP status and retry-after of the link carrying the provider's code. */
3456
- function upstreamResponse(links, providerCode) {
3457
- const typed = links.map(turnFailureOf).find((value) => value !== void 0);
3458
- if (typed?.retryAfterMs !== void 0) return { retryAfterMs: typed.retryAfterMs };
3459
- const link = links.find((each) => [
3460
- "upstreamCode",
3461
- "errorCode",
3462
- "code"
3463
- ].some((field) => read(each, field) === providerCode));
3464
- if (providerCode === void 0 || link === void 0) return {};
3465
- const status = read(link, "status") ?? read(link, "statusCode");
3466
- const retryAfterMs = read(link, "retryAfterMs");
3467
- return {
3468
- ...Number.isInteger(status) && status >= 100 && status <= 599 ? { providerStatus: status } : {},
3469
- ...Number.isInteger(retryAfterMs) && retryAfterMs >= 0 ? { retryAfterMs } : {}
3470
- };
3471
- }
3472
- /**
3473
- * Classify a thrown failure. Pass the caller's signal so a cancellation the caller asked for is
3474
- * attributed to the caller even when the provider surfaced it as an ordinary error, and say when
3475
- * the operation's own deadline expired so a blown deadline is never reported as a cancellation.
3476
- */
3477
- function runtimeFailureCause(error, context = {}) {
3478
- const cause = errMessage(error).slice(0, MAX_CAUSE_CHARS);
3479
- const caller = context.signal?.aborted ? "aborted" : context.deadlineExpired ? "deadline-exceeded" : void 0;
3480
- try {
3481
- const links = causeChain(error);
3482
- const providerCode = upstreamMachineCode(links);
3483
- const upstream = links.find((link) => link instanceof BackendTransportError && link.backend === "bridge" && link.status === 403 && link.upstreamCode === "subscription_required" && link.providerDispatch === "not_started");
3484
- const remedy = runtimeFailureRemediationSchema.readonly().safeParse(upstream?.remediation);
3485
- const failure = ({ code, owner }) => ({
3486
- code,
3487
- owner,
3488
- cause,
3489
- ...providerCode === void 0 ? {} : { providerCode },
3490
- ...upstreamResponse(links, providerCode),
3491
- ...code === "credential-refused" && providerCode === "subscription_required" && remedy.success ? { remediation: remedy.data } : {}
3492
- });
3493
- if (caller !== void 0) return failure({
3494
- code: caller,
3495
- owner: "caller"
3496
- });
3497
- for (const link of links) {
3498
- if (link instanceof RetainedExecutionPendingError && link.pendingCause === "provider-contract") return failure(retainedFailures["provider-contract"]);
3499
- if (read(link, "name") === "RetainedRunProviderContractError") return failure(retainedFailures[classifyRetainedPendingCause(link)]);
3500
- }
3501
- const typed = error instanceof AgentEvalError$1 && !(error instanceof BackendTransportError) ? void 0 : turnFailureOf(error);
3502
- if (typed) return failure(turnFailurePolicy(typed));
3503
- if (context.signal === void 0 && links.some((link) => read(link, "name") === "AbortError")) return failure({
3504
- code: "aborted",
3505
- owner: "caller"
3506
- });
3507
- for (const link of links) {
3508
- if (link instanceof RetainedExecutionPendingError) {
3509
- if (link.pendingCause === "request-rejected" && links.some(isPlatformPredispatchRefusal)) return failure({
3510
- code: "request-rejected",
3511
- owner: "platform"
3512
- });
3513
- return failure(retainedFailures[link.pendingCause]);
3514
- }
3515
- if (read(link, "name") === "RetainedRunProviderContractError") return failure(retainedFailures[classifyRetainedPendingCause(link)]);
3516
- }
3517
- for (const link of links) {
3518
- const classified = structuralFailure(link);
3519
- if (classified !== void 0) return failure(classified);
3520
- }
3521
- return failure(UNATTRIBUTED);
3522
- } catch {
3523
- return {
3524
- ...caller === void 0 ? UNATTRIBUTED : {
3525
- code: caller,
3526
- owner: "caller"
3527
- },
3528
- cause
3529
- };
3530
- }
3531
- }
3532
- /**
3533
- * Classify a failure the execution reported without throwing — a Sandbox outcome or executor
3534
- * envelope with `success: false` — from its machine code and HTTP status. A harness that reports
3535
- * a failure with neither does not say who must act, so it is `unknown`, never the work's fault.
3536
- */
3537
- function reportedFailureCause(outcome) {
3538
- return runtimeFailureCause(Object.assign(new Error(outcome.error ?? "execution reported a failure"), {
3539
- errorCode: outcome.errorCode,
3540
- statusCode: outcome.statusCode,
3541
- failure: outcome.failure
3542
- }));
3543
- }
3544
- /**
3545
- * Classify a down settlement from the typed facts Runtime recorded while the cause was still a
3546
- * value: the retained cause, the failure classified where the executor threw, then `infra: false`,
3547
- * the throw path's statement that the work itself failed. `infra: true` is not an owner: Runtime
3548
- * also sets it for cancellations, budget reconcile faults and validation errors, and from the
3549
- * platform's failure messages, so a settlement with nothing else typed is `unknown`.
3550
- */
3551
- function settlementFailureCause(down) {
3552
- const cause = errMessage(down.reason).slice(0, MAX_CAUSE_CHARS);
3553
- if (down.retainedPendingCause !== void 0) {
3554
- const retained = retainedFailures[down.retainedPendingCause];
3555
- if (down.failure !== void 0 && down.failure.code === retained.code) return {
3556
- ...down.failure,
3557
- cause
3558
- };
3559
- return {
3560
- ...retained,
3561
- cause,
3562
- ...down.failure?.providerCode === void 0 ? {} : { providerCode: down.failure.providerCode }
3563
- };
3564
- }
3565
- if (down.failure !== void 0 && down.failure.code !== "unknown") return {
3566
- ...down.failure,
3567
- cause
3568
- };
3569
- return {
3570
- ...down.infra === false ? {
3571
- code: "work-failed",
3572
- owner: "work"
3573
- } : UNATTRIBUTED,
3574
- cause,
3575
- ...down.failure?.providerCode === void 0 ? {} : { providerCode: down.failure.providerCode }
3576
- };
3577
- }
3578
383
  // src/runtime/supervise/flow-brain.ts
3579
384
  const flowOwners = /* @__PURE__ */ new WeakSet();
3580
385
  /** Runtime schedules this model-free brain under graph edge caps, pool, deadline and abort. */
@@ -16957,6 +13762,6 @@ async function recoverRetainedChildren(args, children, spawn, next) {
16957
13762
  throw error;
16958
13763
  }
16959
13764
  }
16960
- export { hasRetriableTeardowns as $, parseCodexUsageRecord as $i, readBoundHarnessSessions as $n, RetainedExecutionPendingError as $r, FileResultBlobStore as $t, isTraceAnalysisStore as A, spendFromUsageTotals as Ai, mergeTraceEnv as An, recordRuntimeOwnedProviderModel as Ar, scopeOwnerExecutorNodeContext as At, FileSpawnJournal as B, extractTransportEventIdentity as Bi, createRuntimeEventCollector as Bn, executableAgentProfileSnapshot as Br, scopeRetainedOwnerRestorePoint as Bt, writeWorkerSteerAcknowledgement as C, hashJson as Ci, verifyObserverRecords as Cn, knownMaterializationReceipt as Cr, scopeRetainedOwnerResult as Ct, prepareScopeResume as D, meterUsageEvent as Di, workerTraceEnv as Dn, recordRuntimeOwnedProviderAttemptStart as Dr, recordScopeOwnerMaterialization as Dt, resolveWorkerSlots as E, createBudgetPool as Ei, WORKER_TRACE_PROPAGATION as En, recordRuntimeOwnedDriveHarnessProviderEvidence as Er, noteScopeOwnerPlannedExecution as Et, attachWorker as F, sandboxLeafOutputFromEvents as Fi, createOpenInferenceFileExporter as Fn, runtimeOwnedPendingExecutorMaterialization as Fr, beginScopeRetainedOwnerDrive as Ft, settledToIteration as G, extractLlmCallEvent as Gi, captureBoundHarnessTranscript as Gn, reportedFailureCause as Gr, scopeRetainedOwnerContext as Gt, spawnJournalRecordEvents as H, canonicalSandboxUsageMode as Hi, sanitizeKnowledgeReadinessReport as Hn, isRuntimeFlowBrain as Hr, scopeRetainedOwnerCheckpointing as Ht, readWorkerInteractiveBinding as I, sandboxOutputMarker as Ii, createOtelExporter as In, runtimeOwnedScopeOwnerControl as Ir, scopeRetainedOwnerSettled as It, freeSlots as J, sameModelId as Ji, harnessTranscriptFromLines as Jn, runtimeFailureRemediationSchema as Jr, ownedTreeRootSpawn as Jt, assertRecursiveReservationPolicy as K, isSandboxTerminalEvent as Ki, captureHarnessTranscript as Kn, runtimeFailureCause as Kr, scopeRetainedOwnerJournalRoot as Kt, reconnectRetainedInteractiveRun as L, sandboxProgressEvents as Li, flatOtelSpan as Ln, runtimeOwnedScopeOwnerRuntime as Lr, scopeRetainedOwnerTurnEnded as Lt, workerTraceAnalysisStore as M, createSandboxToolPartState as Mi, buildLoopOtelSpans as Mn, runtimeOwnedExecutorExecutionBinding as Mr, coordinationProfileToolPrefix as Mt, nestedScopeSeamKey as N, mapSandboxEvent as Ni, buildLoopSpanNodes as Nn, runtimeOwnedExecutorMaterialization as Nr, providerVisibleProfile as Nt, sumSpendFromEvents as O, newUsageTotals as Oi, workerTraceHeaders as On, recordRuntimeOwnedProviderDispatchNotStarted as Or, restoreScopeOwnerAcceptedExecution as Ot, interactiveAdmissionSeamKey as P, mapSandboxToolEvent as Pi, buildRuntimeEventOtelSpans as Pn, runtimeOwnedExecutorProviderEvidence as Pr, runtimeGraphMetadataKey as Pt, closeScopeAdmission as Q, decodeHarnessUsage as Qi, HARNESS_TRANSCRIPT_READ_LIMITS as Qn, DISPATCH_NOT_ADMITTED_CODE as Qr, nestedDriverTreeRoot as Qt, recoverRetainedInteractiveRun as R, terminalTextFromSandboxEvent as Ri, generateSpanId as Rn, unknownExecutionBindingReceipt as Rr, assertRetainedOwnerCoordinationRekey as Rt, writeWorkerSteer as S, turnFailurePolicy as Si, observerRecordDigest as Sn, knownExecutionBindingReceipt as Sr, scopeRetainedOwnerPriorSpend as St, openSlotGroup as T, isCredentialRefusal as Ti, environmentGone as Tn, providerAttemptEvidence as Tr, finalizeScopeOwnerMaterialization as Tt, appendTeardownConfirmation as U, createSandboxTurnEventCollector as Ui, sanitizeRuntimeStreamEvent as Un, runtimeFlowAgent as Ur, bindScopeRetainedOwnerEnvironmentId as Ut, appendSpawnEvents as V, assertSandboxServedModel as Vi, createRuntimeStreamEventCollector as Vn, executableAgentSpecSnapshot as Vr, noteScopeRetainedOwnerCoordination as Vt, reenteredWorkers as W, createSandboxUsageLedger as Wi, HARNESS_TRANSCRIPT_SETTLE_TIMEOUT_MS as Wn, runtimeFlowBrain as Wr, bindScopeRetainedOwnerProvider as Wt, rollingDispatch as X, sandboxTerminalUsageField as Xi, persistHarnessTranscript as Xn, settlementFailureCause as Xr, attestNestedDriverTreeOwner as Xt, queueOf as Y, sandboxEventServedBackend as Yi, harnessTranscriptUnavailable as Yn, runtimeProviderCode as Yr, materializeTreeView as Yt, closeRetainedSlots as Z, sumSandboxUsage as Zi, readHarnessTranscript as Zn, DISPATCH_EXECUTION_CHANGED_CODE as Zr, driverRuntime as Zt, supervisorRunDir as _, contentAddress as _i, acceptedInteractiveControl as _n, attestRuntimeOwnedScopeOwner as _r, environmentReader as _t, readRootStream as a, freezeControlRef as aa, retainedExecutorSeamKey as ai, executorObservationSeamKey as an, readWorkerProgress as ar, recordScopeOwnerPause as at, workerInboxFileFromEventDir as b, executorFailureInfra as bi, FileObserverJournal as bn, finalizeRuntimeOwnedPendingExecutor as br, consumeScopeRetainedOwnerResult as bt, cancelWorker as c, abortReason as ca, cappedDoublingMs as ci, stopRetainedNativeExecution as cn, SEAT_RECHECK_MS as cr, releaseRetainedEnvironments as ct, legacySupervisorRunsRoot as d, signalAbortError as da, reportedResetAt as di, mintRetainedIdentity as dn, authoredSeatStages as dr, startScopeRecoveries as dt, recordValue as ea, TRANSPORT_ERROR_CODES as ei, encodeResultBlob as en, resolveHarnessTranscriptCapture as er, meterRuntimeOwnedAccounting as et, readWorkerCancelRequests as f, promptFromAgentTurnInput as fa, unavailablePauseMs as fi, reconnectRetainedRun as fn, seatExecutionProfile as fr, unconfirmedTeardowns as ft, safeWorkerFile as g, TRACE_SUMMARY_SCHEMA as gi, retainedCreateMaterial as gn, attestRuntimeOwnedPendingExecutor as gr, scopeRetainedOwnerResourceReader as gt, readWorkerSteerRequests as h, TRACE_ROOT_SCHEMA as hi, startRetainedRunInEnvironment as hn, attestRuntimeOwnedExecutor as hr, releaseScopeRetainedOwnerForSeatPause as ht, nodeStreamFile as i, exactSession as ia, retainedExecutorContext as ii, executorObservation as in, describeToolArgs as ir, recordScopeOwnerDriverAttempt as it, parseWorkerToolTraceArtifact as j, assertValidBudget as ji, readTraceContextFromEnv as jn, runtimeOwnedDriveHarnessProviderEvidence as jr, coordinationMcpAlias as jt, replaySpawnTree as k, spendFromUsageEvents as ki, createPropagatingTraceEmitter as kn, recordRuntimeOwnedProviderIdentityConflict as kr, scopeOwnerExecutionObservation as kt, claimWorkerSteerDelivery as l, linkAbort as la, jitteredMs as li, verifyWorkspaceMarker as ln, SeatChain as lr, retryUnconfirmedTeardowns as lt, readWorkerSteerAcknowledgement as m, providerMessageText as ma, unavailableSignalOfFailure as mi, startRetainedRun as mn, attestRuntimeOwnedDeferredExecutor as mr, prepareScopeRetainedOwnerSeatSwitch as mt, ROOT_STREAM_FILE as n, assertEventBinding as na, isSchemaRejection as ni, closesCursorSlot as nn, DEFAULT_STALL_AFTER_MS as nr, pauseScopeSeatAdmission as nt, readRootStreamReceipt as o, sameControlCoordinates as oa, UNAVAILABLE_CODES as oi, observeModelRequest as on, isLiveNodeStatus as or, registerNestedTeardownScope as ot, readWorkerCancellation as p, promptOptionsFromAgentTurnInput as pa, unavailableResumeAt as pi, recoverRetainedRun as pn, subscriptionUsageLimitSignal as pr, waitForScopeChildren as pt, deriveNodeExecutionIdentity as q, notifySandboxEventObserver as qi, harnessTranscriptArtifact as qn, runtimeFailureCauseSchema as qr, loadSpawnForest as qt, createRootStreamSink as r, awaitAbortable$1 as ra, registerRetainedExecutorPreparation as ri, insideCursorNamespace as rn, createActivityLog as rr, recordScopeOwnerBarrenPause as rt, appendWorkerControlEvent as s, RunCancellationReason as sa, UNAVAILABLE_STATUSES as si, describeRetainedNativeStops as sn, isTerminalNodeStatus as sr, releaseHeldEnvironments as st, createScope as t, RetainedRunProviderContractError as ta, isProvisioningRefusal as ti, SpawnEventIndex as tn, retainHarnessTranscript as tr, meterRuntimeOwnedProviderAttempt as tt, legacySupervisorRunDir as u, runAbortable as ua, printedStatus as ui, assertRetainedRunReplayMaterial as un, SeatPauseError as ur, settleManagerWork as ut, supervisorRunsRoot as v, contentRef as vi, offerInteractiveControl as vn, authoredAgentIdentity as vr, resolveSpawnResourcePaths as vt, createWorkerSlots as w, credentialCommandExit as wi, destroyInteractiveEnvironment as wn, newExecutionAttemptId as wr, beginScopeOwnerAttempt as wt, writeWorkerCancellation as x, turnFailureOf as xi, createFileObserverHooks as xn, inheritRuntimeOwnedExecutorAttestation as xr, prepareScopeRetainedOwnerTask as xt, workerCancellationsDir as y, executorFailure as yi, prepareInteractiveDelegation as yn, authoredProfileDigest as yr, abandonScopeRetainedOwnerInvocation as yt, startRetainedInteractiveRun as z, canonicalStreamEventFromSandboxEvent as zi, loopEventToOtelSpan as zn, unknownMaterializationReceipt as zr, reconcileScopeRetainedOwnerEnvironment as zt };
13765
+ export { hasRetriableTeardowns as $, readBoundHarnessSessions as $n, linkAbort as $r, FileResultBlobStore as $t, isTraceAnalysisStore as A, mergeTraceEnv as An, recordRuntimeOwnedProviderModel as Ar, scopeOwnerExecutorNodeContext as At, FileSpawnJournal as B, createRuntimeEventCollector as Bn, executableAgentProfileSnapshot as Br, scopeRetainedOwnerRestorePoint as Bt, writeWorkerSteerAcknowledgement as C, verifyObserverRecords as Cn, knownMaterializationReceipt as Cr, scopeRetainedOwnerResult as Ct, prepareScopeResume as D, workerTraceEnv as Dn, recordRuntimeOwnedProviderAttemptStart as Dr, recordScopeOwnerMaterialization as Dt, resolveWorkerSlots as E, WORKER_TRACE_PROPAGATION as En, recordRuntimeOwnedDriveHarnessProviderEvidence as Er, noteScopeOwnerPlannedExecution as Et, attachWorker as F, createOpenInferenceFileExporter as Fn, runtimeOwnedPendingExecutorMaterialization as Fr, beginScopeRetainedOwnerDrive as Ft, settledToIteration as G, captureBoundHarnessTranscript as Gn, RetainedRunProviderContractError as Gr, scopeRetainedOwnerContext as Gt, spawnJournalRecordEvents as H, sanitizeKnowledgeReadinessReport as Hn, isRuntimeFlowBrain as Hr, scopeRetainedOwnerCheckpointing as Ht, readWorkerInteractiveBinding as I, createOtelExporter as In, runtimeOwnedScopeOwnerControl as Ir, scopeRetainedOwnerSettled as It, freeSlots as J, harnessTranscriptFromLines as Jn, exactSession as Jr, ownedTreeRootSpawn as Jt, assertRecursiveReservationPolicy as K, captureHarnessTranscript as Kn, assertEventBinding as Kr, scopeRetainedOwnerJournalRoot as Kt, reconnectRetainedInteractiveRun as L, flatOtelSpan as Ln, runtimeOwnedScopeOwnerRuntime as Lr, scopeRetainedOwnerTurnEnded as Lt, workerTraceAnalysisStore as M, buildLoopOtelSpans as Mn, runtimeOwnedExecutorExecutionBinding as Mr, coordinationProfileToolPrefix as Mt, nestedScopeSeamKey as N, buildLoopSpanNodes as Nn, runtimeOwnedExecutorMaterialization as Nr, providerVisibleProfile as Nt, sumSpendFromEvents as O, workerTraceHeaders as On, recordRuntimeOwnedProviderDispatchNotStarted as Or, restoreScopeOwnerAcceptedExecution as Ot, interactiveAdmissionSeamKey as P, buildRuntimeEventOtelSpans as Pn, runtimeOwnedExecutorProviderEvidence as Pr, runtimeGraphMetadataKey as Pt, closeScopeAdmission as Q, HARNESS_TRANSCRIPT_READ_LIMITS as Qn, abortReason as Qr, nestedDriverTreeRoot as Qt, recoverRetainedInteractiveRun as R, generateSpanId as Rn, unknownExecutionBindingReceipt as Rr, assertRetainedOwnerCoordinationRekey as Rt, writeWorkerSteer as S, observerRecordDigest as Sn, knownExecutionBindingReceipt as Sr, scopeRetainedOwnerPriorSpend as St, openSlotGroup as T, environmentGone as Tn, providerAttemptEvidence as Tr, finalizeScopeOwnerMaterialization as Tt, appendTeardownConfirmation as U, sanitizeRuntimeStreamEvent as Un, runtimeFlowAgent as Ur, bindScopeRetainedOwnerEnvironmentId as Ut, appendSpawnEvents as V, createRuntimeStreamEventCollector as Vn, executableAgentSpecSnapshot as Vr, noteScopeRetainedOwnerCoordination as Vt, reenteredWorkers as W, HARNESS_TRANSCRIPT_SETTLE_TIMEOUT_MS as Wn, runtimeFlowBrain as Wr, bindScopeRetainedOwnerProvider as Wt, rollingDispatch as X, persistHarnessTranscript as Xn, sameControlCoordinates as Xr, attestNestedDriverTreeOwner as Xt, queueOf as Y, harnessTranscriptUnavailable as Yn, freezeControlRef as Yr, materializeTreeView as Yt, closeRetainedSlots as Z, readHarnessTranscript as Zn, RunCancellationReason as Zr, driverRuntime as Zt, supervisorRunDir as _, acceptedInteractiveControl as _n, attestRuntimeOwnedScopeOwner as _r, environmentReader as _t, readRootStream as a, executorObservationSeamKey as an, readWorkerProgress as ar, recordScopeOwnerPause as at, workerInboxFileFromEventDir as b, FileObserverJournal as bn, finalizeRuntimeOwnedPendingExecutor as br, consumeScopeRetainedOwnerResult as bt, cancelWorker as c, stopRetainedNativeExecution as cn, SEAT_RECHECK_MS as cr, releaseRetainedEnvironments as ct, legacySupervisorRunsRoot as d, mintRetainedIdentity as dn, authoredSeatStages as dr, startScopeRecoveries as dt, runAbortable as ei, encodeResultBlob as en, resolveHarnessTranscriptCapture as er, meterRuntimeOwnedAccounting as et, readWorkerCancelRequests as f, reconnectRetainedRun as fn, seatExecutionProfile as fr, unconfirmedTeardowns as ft, safeWorkerFile as g, retainedCreateMaterial as gn, attestRuntimeOwnedPendingExecutor as gr, scopeRetainedOwnerResourceReader as gt, readWorkerSteerRequests as h, startRetainedRunInEnvironment as hn, attestRuntimeOwnedExecutor as hr, releaseScopeRetainedOwnerForSeatPause as ht, nodeStreamFile as i, providerMessageText as ii, executorObservation as in, describeToolArgs as ir, recordScopeOwnerDriverAttempt as it, parseWorkerToolTraceArtifact as j, readTraceContextFromEnv as jn, runtimeOwnedDriveHarnessProviderEvidence as jr, coordinationMcpAlias as jt, replaySpawnTree as k, createPropagatingTraceEmitter as kn, recordRuntimeOwnedProviderIdentityConflict as kr, scopeOwnerExecutionObservation as kt, claimWorkerSteerDelivery as l, verifyWorkspaceMarker as ln, SeatChain as lr, retryUnconfirmedTeardowns as lt, readWorkerSteerAcknowledgement as m, startRetainedRun as mn, attestRuntimeOwnedDeferredExecutor as mr, prepareScopeRetainedOwnerSeatSwitch as mt, ROOT_STREAM_FILE as n, promptFromAgentTurnInput as ni, closesCursorSlot as nn, DEFAULT_STALL_AFTER_MS as nr, pauseScopeSeatAdmission as nt, readRootStreamReceipt as o, observeModelRequest as on, isLiveNodeStatus as or, registerNestedTeardownScope as ot, readWorkerCancellation as p, recoverRetainedRun as pn, subscriptionUsageLimitSignal as pr, waitForScopeChildren as pt, deriveNodeExecutionIdentity as q, harnessTranscriptArtifact as qn, awaitAbortable$1 as qr, loadSpawnForest as qt, createRootStreamSink as r, promptOptionsFromAgentTurnInput as ri, insideCursorNamespace as rn, createActivityLog as rr, recordScopeOwnerBarrenPause as rt, appendWorkerControlEvent as s, describeRetainedNativeStops as sn, isTerminalNodeStatus as sr, releaseHeldEnvironments as st, createScope as t, signalAbortError as ti, SpawnEventIndex as tn, retainHarnessTranscript as tr, meterRuntimeOwnedProviderAttempt as tt, legacySupervisorRunDir as u, assertRetainedRunReplayMaterial as un, SeatPauseError as ur, settleManagerWork as ut, supervisorRunsRoot as v, offerInteractiveControl as vn, authoredAgentIdentity as vr, resolveSpawnResourcePaths as vt, createWorkerSlots as w, destroyInteractiveEnvironment as wn, newExecutionAttemptId as wr, beginScopeOwnerAttempt as wt, writeWorkerCancellation as x, createFileObserverHooks as xn, inheritRuntimeOwnedExecutorAttestation as xr, prepareScopeRetainedOwnerTask as xt, workerCancellationsDir as y, prepareInteractiveDelegation as yn, authoredProfileDigest as yr, abandonScopeRetainedOwnerInvocation as yt, startRetainedInteractiveRun as z, loopEventToOtelSpan as zn, unknownMaterializationReceipt as zr, reconcileScopeRetainedOwnerEnvironment as zt };
16961
13766
 
16962
- //# sourceMappingURL=scope-DzhBJrRN.js.map
13767
+ //# sourceMappingURL=scope-DSm2RW5I.js.map