@tangle-network/agent-runtime 0.142.2 → 0.143.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/{activation-o7JKmtxi.js → activation-mi8TgSLM.js} +2 -2
  2. package/dist/{activation-o7JKmtxi.js.map → activation-mi8TgSLM.js.map} +1 -1
  3. package/dist/agent.d.ts +2 -2
  4. package/dist/agent.js +3 -3
  5. package/dist/{analyst-loop-GcX0BY75.js → analyst-loop-CvkmfhUB.js} +2 -2
  6. package/dist/{analyst-loop-GcX0BY75.js.map → analyst-loop-CvkmfhUB.js.map} +1 -1
  7. package/dist/analyst-loop.d.ts +1 -1
  8. package/dist/analyst-loop.js +1 -1
  9. package/dist/{authoring-Bm1WlGPv.js → authoring-DuyShLdw.js} +2 -2
  10. package/dist/{authoring-Bm1WlGPv.js.map → authoring-DuyShLdw.js.map} +1 -1
  11. package/dist/{conversation-DXKJHyqB.js → conversation-1hNtDeMM.js} +3 -3
  12. package/dist/{conversation-DXKJHyqB.js.map → conversation-1hNtDeMM.js.map} +1 -1
  13. package/dist/conversation.d.ts +1 -1
  14. package/dist/conversation.js +1 -1
  15. package/dist/durable.d.ts +2 -2
  16. package/dist/durable.js +1 -1
  17. package/dist/{environment-provider-BicwCDK5.d.ts → environment-provider-B8eZ85Ga.d.ts} +3 -3
  18. package/dist/{environment-provider-DCEACNt9.js → environment-provider-DCBJ13VP.js} +2 -2
  19. package/dist/{environment-provider-DCEACNt9.js.map → environment-provider-DCBJ13VP.js.map} +1 -1
  20. package/dist/environment-provider.d.ts +1 -1
  21. package/dist/environment-provider.js +1 -1
  22. package/dist/{graph-DhrLuLOS.js → graph-De_XEsfH.js} +2 -2
  23. package/dist/{graph-DhrLuLOS.js.map → graph-De_XEsfH.js.map} +1 -1
  24. package/dist/{improvement-cycle-lcwlR6E2.js → improvement-cycle-Bi-Tf4QF.js} +5 -5
  25. package/dist/{improvement-cycle-lcwlR6E2.js.map → improvement-cycle-Bi-Tf4QF.js.map} +1 -1
  26. package/dist/{index-Bx3-tqG-.d.ts → index-B8rKtH3U.d.ts} +4 -4
  27. package/dist/{index-D_aH0F4C.d.ts → index-C2caGomG.d.ts} +12 -27
  28. package/dist/{index-CV1xRWy2.d.ts → index-D4sLkOPY.d.ts} +3 -3
  29. package/dist/index.d.ts +9 -9
  30. package/dist/index.js +9 -9
  31. package/dist/intelligence.d.ts +5 -5
  32. package/dist/intelligence.js +4 -4
  33. package/dist/kernel.d.ts +7 -7
  34. package/dist/kernel.js +10 -10
  35. package/dist/{knowledge-C9LlSJAR.js → knowledge-CVE6Wk1i.js} +3 -3
  36. package/dist/{knowledge-C9LlSJAR.js.map → knowledge-CVE6Wk1i.js.map} +1 -1
  37. package/dist/knowledge.d.ts +1 -1
  38. package/dist/knowledge.js +1 -1
  39. package/dist/{loop-runner-bin-CctBy5-X.d.ts → loop-runner-bin-BoHdQqLm.d.ts} +3 -3
  40. package/dist/{loop-runner-bin-DFo6Ss-1.js → loop-runner-bin-DdaI1nLL.js} +4 -4
  41. package/dist/{loop-runner-bin-DFo6Ss-1.js.map → loop-runner-bin-DdaI1nLL.js.map} +1 -1
  42. package/dist/loop-runner-bin.d.ts +1 -1
  43. package/dist/loop-runner-bin.js +1 -1
  44. package/dist/{materialization-CrdcfWXp.js → materialization-P86EciBw.js} +43 -2
  45. package/dist/materialization-P86EciBw.js.map +1 -0
  46. package/dist/mcp/bin.js +3 -3
  47. package/dist/mcp/index.d.ts +5 -5
  48. package/dist/mcp/index.js +4 -4
  49. package/dist/{openai-tools-CSm460sC.js → openai-tools-BiAWKjh6.js} +2 -2
  50. package/dist/{openai-tools-CSm460sC.js.map → openai-tools-BiAWKjh6.js.map} +1 -1
  51. package/dist/{openai-tools-t5-6h5bj.d.ts → openai-tools-z-whdYNC.d.ts} +2 -2
  52. package/dist/primeintellect/index.d.ts +1 -1
  53. package/dist/profiles.d.ts +1 -1
  54. package/dist/{redact-B0gDSTZO.d.ts → redact-DexTuAiX.d.ts} +3 -3
  55. package/dist/{runtime-DQs5xHdt.js → runtime-BnqjbOGX.js} +18 -12
  56. package/dist/{runtime-DQs5xHdt.js.map → runtime-BnqjbOGX.js.map} +1 -1
  57. package/dist/{runtime-htqI3fKL.d.ts → runtime-J6Toky8K.d.ts} +22 -10
  58. package/dist/{sandbox-events-BK-g19wx.js → sandbox-events-COwCWUVB.js} +6 -49
  59. package/dist/sandbox-events-COwCWUVB.js.map +1 -0
  60. package/dist/{stream-agent-turn-Dfy12OjG.d.ts → stream-agent-turn-C1Vs_3B9.d.ts} +6 -3
  61. package/dist/{stream-agent-turn-BMHUIuQS.js → stream-agent-turn-CXRugSGO.js} +55 -14
  62. package/dist/stream-agent-turn-CXRugSGO.js.map +1 -0
  63. package/dist/{structural-rollout-CzZKDkmA.js → structural-rollout-CJgj9YXH.js} +4 -4
  64. package/dist/{structural-rollout-CzZKDkmA.js.map → structural-rollout-CJgj9YXH.js.map} +1 -1
  65. package/dist/{supervise-BEs5MB91.js → supervise-hCgG6ROJ.js} +3 -3
  66. package/dist/{supervise-BEs5MB91.js.map → supervise-hCgG6ROJ.js.map} +1 -1
  67. package/dist/{supervisor-B_0ISTFG.js → supervisor-s8SfZYbW.js} +107 -43
  68. package/dist/supervisor-s8SfZYbW.js.map +1 -0
  69. package/dist/testing.d.ts +2 -2
  70. package/dist/testing.js +11 -11
  71. package/dist/{types-DZrBoBpN.d.ts → types-DeX8ibLc.d.ts} +4 -1
  72. package/dist/{types-_7w24mMg.d.ts → types-Df9vulGc.d.ts} +2 -2
  73. package/package.json +5 -5
  74. package/dist/materialization-CrdcfWXp.js.map +0 -1
  75. package/dist/sandbox-events-BK-g19wx.js.map +0 -1
  76. package/dist/stream-agent-turn-BMHUIuQS.js.map +0 -1
  77. package/dist/supervisor-B_0ISTFG.js.map +0 -1
@@ -1,9 +1,10 @@
1
- import { E as SandboxClient, i as ExecCtx, k as Validator } from "./types-DZrBoBpN.js";
2
- import { I as Scope, Q as UsageEvent, U as Spend, d as ExecutorFactory, gn as ExecutorProgress, ln as TraceSource, m as ExecutorRegistry } from "./types-_7w24mMg.js";
3
- import { i as AgentEnvironmentProvider, o as AgentEnvironmentProviderRegistry, w as ProviderExecutorOptions } from "./environment-provider-BicwCDK5.js";
1
+ import { E as SandboxClient, i as ExecCtx, k as Validator } from "./types-DeX8ibLc.js";
2
+ import { I as Scope, Q as UsageEvent, U as Spend, a as DefaultVerdict, d as ExecutorFactory, gn as ExecutorProgress, ln as TraceSource, m as ExecutorRegistry } from "./types-Df9vulGc.js";
3
+ import { i as AgentEnvironmentProvider, o as AgentEnvironmentProviderRegistry, w as ProviderExecutorOptions } from "./environment-provider-B8eZ85Ga.js";
4
4
  import { AgentProfile, AgentProfileMcpServer, HarnessType, ReasoningEffort } from "@tangle-network/agent-interface";
5
5
  import { WorkspacePlanReceipt } from "@tangle-network/agent-profile-materialize";
6
6
  import { ChildProcess } from "node:child_process";
7
+ import { AgentRunOutcome } from "@tangle-network/sandbox/runtime";
7
8
  import { BackendType, SandboxEvent } from "@tangle-network/sandbox";
8
9
  //#region src/mcp/protocol.d.ts
9
10
  /**
@@ -770,6 +771,21 @@ interface ResolvedMcpServerLaunch {
770
771
  */
771
772
  declare function resolveMcpServerLaunch(server: AgentProfileMcpServer, keys: KeyProvider | undefined, label: string): Promise<ResolvedMcpServerLaunch>;
772
773
  //#endregion
774
+ //#region src/runtime/sandbox-executor-output.d.ts
775
+ /** One tool call retained in a Sandbox executor artifact. */
776
+ interface SandboxExecutorToolCall {
777
+ id?: string;
778
+ name: string;
779
+ arguments: unknown;
780
+ }
781
+ /** Parsed output of one Sandbox executor turn. */
782
+ interface SandboxLeafOut {
783
+ events: SandboxEvent[];
784
+ content: string;
785
+ toolCalls?: SandboxExecutorToolCall[];
786
+ outcome?: AgentRunOutcome;
787
+ }
788
+ //#endregion
773
789
  //#region src/runtime/supervise/inbox.d.ts
774
790
  /** A message from the run's AUTHORITY — the parent driver. These two kinds carry instruction. */
775
791
  interface AuthorityInboxMessage {
@@ -833,6 +849,7 @@ interface SteerableSandboxSession {
833
849
  artifact(): {
834
850
  outRef: string;
835
851
  out: unknown;
852
+ verdict?: DefaultVerdict;
836
853
  spent: Spend;
837
854
  } | undefined;
838
855
  teardown(): Promise<void>;
@@ -1109,11 +1126,6 @@ interface RouterToolsSeam {
1109
1126
  durationMs?: number;
1110
1127
  }) => void;
1111
1128
  }
1112
- /** Parsed output of the sandbox leaf: the iteration's raw event stream. What a
1113
- * `SandboxSeam.validator` receives as its `output` argument. */
1114
- interface SandboxLeafOut {
1115
- events: SandboxEvent[];
1116
- }
1117
1129
  /**
1118
1130
  * The leaf `createWorktreeCliExecutor` as a backend-as-data factory: a supervisor-authored
1119
1131
  * `AgentProfile` driving claude / codex / opencode on its own worktree. `budgetExempt` like
@@ -1161,5 +1173,5 @@ declare function createExecutor(config: ExecutorConfig): ExecutorFactory<unknown
1161
1173
  */
1162
1174
  declare function createExecutorRegistry(): ExecutorRegistry;
1163
1175
  //#endregion
1164
- export { WorktreeHandle as $, resolveSecretEnv as A, JsonRpcResponse as At, LOCAL_HARNESSES as B, PeerInboxMessage as C, PeerMailboxOptions as Ct, envKeyProvider as D, peerMailTools as Dt, ResolvedMcpServerLaunch as E, isPeerMailEnvelope as Et, WorktreeProfileMaterializationReceipt as F, localHarnessExecutable as G, LocalHarnessResult as H, CodexExecutionEvidence as I, CreateWorktreeOptions as J, parseCodexTokenUsage as K, CodexExecutionPolicy as L, WorktreeCheckRunner as M, McpTransport as Mt, WorktreeCommandResult as N, mcpSecretEnvMetadataKey as O, peerMailVerbNames as Ot, WorktreeHarnessResult as P, RemoveWorktreeOptions as Q, CodexTokenUsage as R, InboxMessage as S, PeerMailbox as St, KeyProvider as T, createPeerMailbox as Tt, RunLocalHarnessOptions as U, LocalHarness as V, harnessSupportsReasoningEffort as W, DiffResult as X, DiffOptions as Y, GitRunner as Z, SteerableSandboxArgs as _, PeerMailLimits as _t, CliWorktreeSeam as a, ToolLoopCallContext as at, AuthorityInboxMessage as b, PeerMailRefusal as bt, RouterSeam as c, ToolLoopCompactionOptions as ct, SandboxSeam as d, AUTHORITY_MARKERS as dt, captureWorktreeDiff as et, cliWorktreeExecutor as f, DEFAULT_PEER_MAIL_LIMITS as ft, SandboxSteeringOptions as g, PeerMailKind as gt, DEFAULT_SANDBOX_STEERING_MAX_TURNS as h, PeerMailEvent as ht, CliWorktreeBridgeSeam as i, ToolSpec as it, secretEnvOfMcpServer as j, McpToolDescriptor as jt, resolveMcpServerLaunch as k, JsonRpcMessage as kt, RouterToolsSeam as l, ToolLoopMessageRecord as lt, createExecutorRegistry as m, PeerMailEnvelope as mt, BridgeSeam as n, removeWorktree as nt, ExecutorConfig as o, ToolLoopChat as ot, createExecutor as p, PEER_MAIL_WIRE_KEY as pt, runLocalHarness as q, CliSeam as r, RouterTransportConfig as rt, ProviderSeam as s, ToolLoopCompaction as st, BridgeModelCredential as t, createWorktree as tt, SandboxLeafOut as u, ToolLoopToolCall as ut, SteerableSandboxSession as v, PeerMailOutcome as vt, createInbox as w, claimsAuthority as wt, Inbox as x, PeerMailSendInput as xt, createSteerableSandboxSession as y, PeerMailReadout as yt, DEFAULT_LOCAL_HARNESS as z };
1165
- //# sourceMappingURL=runtime-htqI3fKL.d.ts.map
1176
+ export { RemoveWorktreeOptions as $, resolveMcpServerLaunch as A, JsonRpcMessage as At, DEFAULT_LOCAL_HARNESS as B, createInbox as C, PeerMailbox as Ct, ResolvedMcpServerLaunch as D, isPeerMailEnvelope as Dt, KeyProvider as E, createPeerMailbox as Et, WorktreeHarnessResult as F, harnessSupportsReasoningEffort as G, LocalHarness as H, WorktreeProfileMaterializationReceipt as I, runLocalHarness as J, localHarnessExecutable as K, CodexExecutionEvidence as L, secretEnvOfMcpServer as M, McpToolDescriptor as Mt, WorktreeCheckRunner as N, McpTransport as Nt, envKeyProvider as O, peerMailTools as Ot, WorktreeCommandResult as P, GitRunner as Q, CodexExecutionPolicy as R, PeerInboxMessage as S, PeerMailSendInput as St, SandboxLeafOut as T, claimsAuthority as Tt, LocalHarnessResult as U, LOCAL_HARNESSES as V, RunLocalHarnessOptions as W, DiffOptions as X, CreateWorktreeOptions as Y, DiffResult as Z, SteerableSandboxSession as _, PeerMailKind as _t, CliWorktreeSeam as a, ToolSpec as at, Inbox as b, PeerMailReadout as bt, RouterSeam as c, ToolLoopCompaction as ct, cliWorktreeExecutor as d, ToolLoopToolCall as dt, WorktreeHandle as et, createExecutor as f, AUTHORITY_MARKERS as ft, SteerableSandboxArgs as g, PeerMailEvent as gt, SandboxSteeringOptions as h, PeerMailEnvelope as ht, CliWorktreeBridgeSeam as i, RouterTransportConfig as it, resolveSecretEnv as j, JsonRpcResponse as jt, mcpSecretEnvMetadataKey as k, peerMailVerbNames as kt, RouterToolsSeam as l, ToolLoopCompactionOptions as lt, DEFAULT_SANDBOX_STEERING_MAX_TURNS as m, PEER_MAIL_WIRE_KEY as mt, BridgeSeam as n, createWorktree as nt, ExecutorConfig as o, ToolLoopCallContext as ot, createExecutorRegistry as p, DEFAULT_PEER_MAIL_LIMITS as pt, parseCodexTokenUsage as q, CliSeam as r, removeWorktree as rt, ProviderSeam as s, ToolLoopChat as st, BridgeModelCredential as t, captureWorktreeDiff as tt, SandboxSeam as u, ToolLoopMessageRecord as ut, createSteerableSandboxSession as v, PeerMailLimits as vt, SandboxExecutorToolCall as w, PeerMailboxOptions as wt, InboxMessage as x, PeerMailRefusal as xt, AuthorityInboxMessage as y, PeerMailOutcome as yt, CodexTokenUsage as z };
1177
+ //# sourceMappingURL=runtime-J6Toky8K.d.ts.map
@@ -155,43 +155,6 @@ function copyPlainSpine(value, seen) {
155
155
  for (const [key, child] of Object.entries(value)) copy[key] = copyPlainSpine(child, seen);
156
156
  return copy;
157
157
  }
158
- /** Event types that settle one sandbox execution. A status or `success` flag is read as a
159
- * verdict on the whole execution only here; on any other event it describes that event. */
160
- const TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set([
161
- "done",
162
- "result",
163
- "final"
164
- ]);
165
- /**
166
- * Return the terminal failure carried by one Sandbox event.
167
- *
168
- * Sandbox transports report execution failure in-band: commonly an `error`
169
- * event followed by a synthetic `done`. Treating the iterable as successfully
170
- * drained therefore turns a provider/configuration failure into a completed
171
- * empty artifact.
172
- *
173
- * The decoder reads a failure from exactly two places, so that a mid-stream
174
- * event describing its OWN failure cannot fail the execution: an `error`-typed
175
- * event, and a terminal event (`done`/`result`/`final`) whose `success` is
176
- * `false` or whose status is a failed one. A failing tool part therefore stays
177
- * a tool result, which is what {@link mapSandboxToolEvent} already projects it
178
- * to.
179
- */
180
- function sandboxEventFailure(event) {
181
- if (!event || typeof event !== "object") return void 0;
182
- const type = String(event.type ?? "");
183
- const data = event.data && typeof event.data === "object" ? event.data : {};
184
- const outcome = plainRecord(data.outcome);
185
- const status = firstString(data.status, outcome?.type, outcome?.status, plainRecord(data.result)?.status);
186
- const terminalFailure = TERMINAL_EVENT_TYPES.has(type) && (data.success === false || status !== void 0 && TERMINAL_FAILURE.test(status));
187
- if (type !== "error" && !terminalFailure) return void 0;
188
- return describeSandboxError(data.error) ?? describeSandboxError(outcome?.error) ?? describeSandboxError(plainRecord(data.result)?.error) ?? (typeof data.message === "string" && data.message.length > 0 ? data.message : void 0) ?? (status !== void 0 ? `sandbox execution ended with status ${status}` : "sandbox execution failed");
189
- }
190
- /** Fail the live execution instead of allowing an in-band failure to become an empty success. */
191
- function assertSandboxEventSucceeded(event) {
192
- const failure = sandboxEventFailure(event);
193
- if (failure !== void 0) throw new Error(`sandbox execution failed: ${failure}`);
194
- }
195
158
  /**
196
159
  * Read the served execution identity off one Sandbox event.
197
160
  *
@@ -264,12 +227,6 @@ function stripProviderPrefix(id, providers) {
264
227
  }
265
228
  return out;
266
229
  }
267
- function describeSandboxError(value) {
268
- if (typeof value === "string" && value.length > 0) return value;
269
- if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
270
- const error = value;
271
- return firstString(error.message, error.error, error.reason, error.code);
272
- }
273
230
  function firstString(...values) {
274
231
  return values.find((value) => typeof value === "string" && value.length > 0);
275
232
  }
@@ -279,8 +236,8 @@ function firstString(...values) {
279
236
  * so the kernel can iterate the full stream without branching.
280
237
  *
281
238
  * Pure by contract: it never throws on a failed run. The terminal truth
282
- * boundary is {@link assertSandboxEventSucceeded}, applied by the two paths
283
- * that SETTLE an execution. Post-hoc readers — {@link sumSandboxUsage}, the
239
+ * boundary is the public Sandbox outcome tracker, applied after the complete
240
+ * stream. Post-hoc readers — {@link sumSandboxUsage}, the
284
241
  * analyst trace store, the chat projection — must stay able to read a failed
285
242
  * turn's events, which is when reading them matters most.
286
243
  *
@@ -534,8 +491,8 @@ function createSandboxToolPartState() {
534
491
  seq: 0
535
492
  };
536
493
  }
537
- /** Statuses that settle a tool call, or a whole execution, as a failure. One vocabulary for
538
- * both so a status never counts as failed in one projection and settled-fine in the other. */
494
+ /** Statuses that settle one tool call as a failure. A failed tool call stays a tool result:
495
+ * the run's terminal state comes from the public Sandbox outcome tracker, never from here. */
539
496
  const TERMINAL_FAILURE = /^(error|errored|failed|failure|cancelled|canceled|timeout|timed_out)$/i;
540
497
  /**
541
498
  * Project one `SandboxEvent` onto the `tool_call` / `tool_result` variants of
@@ -690,6 +647,6 @@ function mapSandboxEvent(event, opts = {}) {
690
647
  return extractLlmCallEvent(event, opts.agentRunName ?? "agent");
691
648
  }
692
649
  //#endregion
693
- export { extractLlmCallEvent as a, notifySandboxEventObserver as c, sumSandboxUsage as d, extractTransportEventIdentity as f, createSandboxToolPartState as i, sandboxEventFailure as l, assertSandboxServedModel as n, mapSandboxEvent as o, parseCanonicalTransportEvent as p, canonicalStreamEventFromSandboxEvent as r, mapSandboxToolEvent as s, assertSandboxEventSucceeded as t, sandboxEventServedBackend as u };
650
+ export { mapSandboxEvent as a, sandboxEventServedBackend as c, parseCanonicalTransportEvent as d, extractLlmCallEvent as i, sumSandboxUsage as l, canonicalStreamEventFromSandboxEvent as n, mapSandboxToolEvent as o, createSandboxToolPartState as r, notifySandboxEventObserver as s, assertSandboxServedModel as t, extractTransportEventIdentity as u };
694
651
 
695
- //# sourceMappingURL=sandbox-events-BK-g19wx.js.map
652
+ //# sourceMappingURL=sandbox-events-COwCWUVB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-events-COwCWUVB.js","names":[],"sources":["../src/runtime/timestamps.ts","../src/runtime/sandbox-transport-events.ts","../src/runtime/sandbox-events.ts"],"sourcesContent":["import { RuntimeEventEnvelopeSchema } from '@tangle-network/agent-interface'\n\nconst validTimestamp = '2026-01-01T00:00:00.000Z'\n\n/** Validate a timestamp with the public runtime envelope contract. */\nexport function assertRuntimeTimestamp(value: unknown, label: string): asserts value is string {\n try {\n RuntimeEventEnvelopeSchema.parse({\n runId: 'runtime',\n eventId: 'event',\n sequence: 0,\n occurredAt: value,\n receivedAt: validTimestamp,\n event: { type: 'status', status: 'processing' },\n })\n } catch (error) {\n throw new Error(`${label} must be a valid ISO timestamp`, { cause: error })\n }\n}\n\n/** Return whether a value satisfies the public runtime timestamp contract. */\nexport function isRuntimeTimestamp(value: unknown): value is string {\n try {\n assertRuntimeTimestamp(value, 'occurredAt')\n return true\n } catch {\n return false\n }\n}\n","import { CanonicalStreamEventSchema, type StreamEvent } from '@tangle-network/agent-interface'\nimport { assertRuntimeTimestamp } from './timestamps'\n\n/** Parse canonical payload fields without treating transport identity as event data. */\nexport function parseCanonicalTransportEvent(\n type: unknown,\n data: Record<string, unknown>,\n normalized: unknown,\n source: string,\n): StreamEvent | undefined {\n if (!isRecord(data)) {\n throw new Error(`${source} emitted a canonical event without an object payload`)\n }\n const outerType = String(type ?? '')\n if (normalized !== undefined) {\n if (\n typeof normalized === 'object' &&\n normalized !== null &&\n 'type' in normalized &&\n typeof normalized.type === 'string' &&\n normalized.type !== outerType\n ) {\n throw new Error(\n `${source} canonical event type \"${normalized.type}\" does not match transport type \"${outerType}\"`,\n )\n }\n const candidate = CanonicalStreamEventSchema.safeParse(normalized)\n if (!candidate.success) {\n throw new Error(`${source} emitted an invalid normalized canonical event`, {\n cause: candidate.error,\n })\n }\n return candidate.data\n }\n const {\n type: embeddedType,\n eventId: _eventId,\n cursor: _cursor,\n sequence: _sequence,\n occurredAt: _occurredAt,\n normalized: _normalized,\n ...payload\n } = data\n if (embeddedType !== undefined && embeddedType !== outerType) {\n throw new Error(\n `${source} canonical event type \"${String(embeddedType)}\" does not match transport type \"${outerType}\"`,\n )\n }\n const candidate = CanonicalStreamEventSchema.safeParse({ ...payload, type: outerType })\n return candidate.success ? candidate.data : undefined\n}\n\n/** Identity fields carried by either the transport envelope or its data payload. */\nexport interface TransportEventIdentity {\n readonly eventId?: string\n readonly cursor?: string\n readonly sequence?: number\n readonly occurredAt?: string\n}\n\n/** Extract shared event identity without privileging one provider wire shape. */\nexport function extractTransportEventIdentity(event: unknown): TransportEventIdentity {\n const record = isRecord(event) ? event : {}\n const data = isRecord(record.data) ? record.data : {}\n const providerEvent = isRecord(record.providerEvent) ? record.providerEvent : {}\n const providerData = isRecord(providerEvent.data) ? providerEvent.data : {}\n const eventId = canonicalEventId(record, data, providerEvent, providerData)\n const cursor = stableString(\n record.cursor ?? data.cursor ?? providerEvent.cursor ?? providerData.cursor,\n )\n const sequence = optionalSequence(\n record.sequence ?? data.sequence ?? providerEvent.sequence ?? providerData.sequence,\n )\n const occurredAt = optionalTimestamp(\n record.occurredAt ?? data.occurredAt ?? providerEvent.occurredAt ?? providerData.occurredAt,\n )\n return {\n ...(eventId === undefined ? {} : { eventId }),\n ...(cursor === undefined ? {} : { cursor }),\n ...(sequence === undefined ? {} : { sequence }),\n ...(occurredAt === undefined ? {} : { occurredAt }),\n }\n}\n\nfunction canonicalEventId(\n record: Record<string, unknown>,\n data: Record<string, unknown>,\n providerEvent: Record<string, unknown>,\n providerData: Record<string, unknown>,\n): string | undefined {\n const candidates = [\n ['data.eventId', data.eventId],\n ['providerEvent.eventId', providerEvent.eventId],\n ['providerData.eventId', providerData.eventId],\n ['record.eventId', record.eventId],\n ] as const\n const present = candidates.filter(([, value]) => value !== undefined)\n // `record.id` may be the provider's replay cursor, so use it only without a canonical field.\n if (present.length === 0) return stableString(record.id ?? providerEvent.id)\n\n const normalized = present.map(([source, value]) => {\n const eventId = stableString(value)\n if (eventId === undefined) throw new Error(`transport event ${source} must be a stable string`)\n return { source, eventId }\n })\n const first = normalized[0]!\n if (normalized.some((candidate) => candidate.eventId !== first.eventId)) {\n throw new Error('transport event canonical identities disagree')\n }\n return first.eventId\n}\n\nfunction stableString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 && value.trim() === value ? value : undefined\n}\n\nfunction finiteNonNegativeInteger(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined\n}\n\nfunction optionalSequence(value: unknown): number | undefined {\n if (value === undefined) return undefined\n if (finiteNonNegativeInteger(value) === undefined) {\n throw new Error('transport event sequence must be a non-negative safe integer')\n }\n return value as number\n}\n\nfunction optionalTimestamp(value: unknown): string | undefined {\n if (value === undefined) return undefined\n assertRuntimeTimestamp(value, 'occurredAt')\n return value\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","/**\n * Sandbox-event → runtime-event mapping.\n *\n * The sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`\n * whose `type` vocabulary is backend-determined (opencode, etc.) rather than\n * enumerated by the SDK. Two consumers project it:\n * - the loop kernel's cost ledger (`extractLlmCallEvent`) — sums usage off\n * every cost-bearing event, regardless of stream shape;\n * - the `AgentRuntime.act` streaming contract (`mapSandboxEvent`) — projects\n * incremental events to the `RuntimeStreamEvent` chat-UX vocabulary.\n *\n * Both live here so the empirically-observed `type` vocabulary has one home.\n */\n\nimport type { StreamEvent } from '@tangle-network/agent-interface'\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport type { RuntimeStreamEvent } from '../types'\nimport { parseCanonicalTransportEvent } from './sandbox-transport-events'\n\nconst CANONICAL_STREAM_EVENT_TYPES: ReadonlySet<string> = new Set([\n 'message.part.updated',\n 'tool-heartbeat',\n 'tool-slow',\n 'model-processing',\n 'status',\n 'warning',\n 'raw',\n 'session.updated',\n 'interaction',\n 'interaction.cancel',\n 'plan.submitted',\n])\n\n/** Decode one known Agent Interface event from a Sandbox event. */\nexport function canonicalStreamEventFromSandboxEvent(event: SandboxEvent): StreamEvent | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n const normalized = data.normalized\n if (normalized === undefined && !CANONICAL_STREAM_EVENT_TYPES.has(type)) return undefined\n return parseCanonicalTransportEvent(type, data, normalized, 'sandbox')\n}\n\n/**\n * Forward a sandbox event to an optional observer without letting observer\n * behavior affect the run. The observer receives a defensive copy, synchronous\n * throws are swallowed, and returned promises are deliberately not awaited.\n */\nexport function notifySandboxEventObserver<Meta>(\n event: SandboxEvent,\n observer: ((event: SandboxEvent, meta: Meta) => void | PromiseLike<void>) | undefined,\n meta: Meta,\n): void {\n if (!observer) return\n try {\n const result = observer(cloneEventForObserver(event), meta)\n if (result && typeof result.then === 'function') {\n void result.then(undefined, () => {})\n }\n } catch {\n // Live observation is optional and must never interrupt the event stream.\n }\n}\n\nfunction cloneEventForObserver(event: SandboxEvent): SandboxEvent {\n try {\n return structuredClone(event)\n } catch {\n return copyPlainSpine(event, new WeakMap()) as SandboxEvent\n }\n}\n\nfunction copyPlainSpine(value: unknown, seen: WeakMap<object, unknown>): unknown {\n if (value === null || typeof value !== 'object') return value\n const existing = seen.get(value)\n if (existing !== undefined) return existing\n if (Array.isArray(value)) {\n const copy: unknown[] = []\n seen.set(value, copy)\n for (const item of value) copy.push(copyPlainSpine(item, seen))\n return copy\n }\n const proto = Object.getPrototypeOf(value)\n if (proto !== Object.prototype && proto !== null) return {}\n const copy: Record<string, unknown> = {}\n seen.set(value, copy)\n for (const [key, child] of Object.entries(value)) {\n copy[key] = copyPlainSpine(child, seen)\n }\n return copy\n}\n\n/** The provider/model the platform reports it actually bound to a turn, when it reports one.\n * `source` is the platform's own account of where that choice came from — `environment` means\n * the platform chose, not the request. */\nexport interface SandboxServedBackend {\n readonly provider?: string\n readonly model?: string\n readonly source?: string\n}\n\n/**\n * Read the served execution identity off one Sandbox event.\n *\n * The platform reports `effectiveBackend` on `execution.started` and again on the terminal\n * event (`@tangle-network/sandbox`, `EffectiveBackend`). Absence returns `undefined`\n * and must stay unknown — a request is not a receipt, so nothing here may be inferred from\n * what was asked for.\n */\nexport function sandboxEventServedBackend(event: SandboxEvent): SandboxServedBackend | undefined {\n if (!event || typeof event !== 'object') return undefined\n const data = plainRecord(event.data)\n const served = plainRecord(data?.effectiveBackend)\n if (!served) return undefined\n const provider = firstString(served.provider)\n const model = firstString(served.model)\n const source = firstString(served.source)\n if (provider === undefined && model === undefined) return undefined\n return {\n ...(provider !== undefined ? { provider } : {}),\n ...(model !== undefined ? { model } : {}),\n ...(source !== undefined ? { source } : {}),\n }\n}\n\n/**\n * Fail the execution when the platform reports serving a model other than the exact one asked for.\n *\n * Measured motive (agent-runtime#892, live infrastructure 2026-08-17): 6 of 6 boxes whose profile\n * declared `zai-coding-plan/glm-5.2` reported\n * `{\"provider\":\"openai-compat\",\"model\":\"deepseek/deepseek-v4-flash\",\"source\":\"environment\"}`,\n * while the materialization receipt recorded the declared model as `status: \"known\"`. Sending\n * `backend.model` makes that substitution unlikely; only reading the report back makes it\n * detectable. A run that cannot say which model produced its evidence must not settle as one\n * that can.\n *\n * Silent when the platform reports no served model: unobserved stays unobserved.\n */\nexport function assertSandboxServedModel(\n event: SandboxEvent,\n expected: { readonly provider?: string; readonly model?: string } | undefined,\n): void {\n const wanted = expected?.model?.trim()\n if (!wanted) return\n const served = sandboxEventServedBackend(event)\n if (served?.model === undefined) return\n if (sameModelId(served.model, wanted, [served.provider, expected?.provider])) return\n const attribution = [\n served.provider !== undefined ? `provider ${JSON.stringify(served.provider)}` : undefined,\n served.source !== undefined ? `source ${JSON.stringify(served.source)}` : undefined,\n ].filter((part): part is string => part !== undefined)\n const detail = attribution.length > 0 ? ` (${attribution.join(', ')})` : ''\n throw new Error(\n `sandbox served model ${JSON.stringify(served.model)}${detail} instead of the exact profile model ${JSON.stringify(wanted)}`,\n )\n}\n\n/**\n * Do two model ids name the same model, allowing for routing prefixes?\n *\n * Either side may spell the model bare (`glm-5.2`), provider-qualified\n * (`zai-coding-plan/glm-5.2`), or route-qualified, and the platform reports the provider in its\n * own field rather than always in the id. So the comparison drops any known provider prefix and\n * then accepts a `/`-boundary suffix match: a longer route to the SAME leaf model is a routing\n * difference, not a substitution.\n *\n * What it deliberately does NOT absorb is a different leaf: `glm-5.2` against `glm-5.3` stays a\n * mismatch, which is the substitution measured directly against the provider API (a request for\n * `glm-5.2` was served `glm-5.3`). A version suffix is the whole difference between two\n * instruments, so nothing here may treat it as noise.\n */\nfunction sameModelId(\n served: string,\n wanted: string,\n providers: readonly (string | undefined)[],\n): boolean {\n const a = stripProviderPrefix(served, providers)\n const b = stripProviderPrefix(wanted, providers)\n return a === b || a.endsWith(`/${b}`) || b.endsWith(`/${a}`)\n}\n\nfunction stripProviderPrefix(id: string, providers: readonly (string | undefined)[]): string {\n let out = id.trim().toLowerCase()\n for (const provider of providers) {\n const prefix = provider?.trim().toLowerCase()\n if (prefix && out.startsWith(`${prefix}/`)) out = out.slice(prefix.length + 1)\n }\n return out\n}\n\nfunction firstString(...values: unknown[]): string | undefined {\n return values.find((value): value is string => typeof value === 'string' && value.length > 0)\n}\n\n/**\n * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when\n * the event carries usage/cost data. Returns `undefined` for non-cost events\n * so the kernel can iterate the full stream without branching.\n *\n * Pure by contract: it never throws on a failed run. The terminal truth\n * boundary is the public Sandbox outcome tracker, applied after the complete\n * stream. Post-hoc readers — {@link sumSandboxUsage}, the\n * analyst trace store, the chat projection — must stay able to read a failed\n * turn's events, which is when reading them matters most.\n *\n * Canonical cost-carrying types observed in the wild:\n * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`\n * - `message.completed` / `result` — `data: { usage: { inputTokens,\n * outputTokens, totalCostUsd? } }`\n * - `cost.usage` / `usage` — same shape under a dedicated type\n *\n * Numeric coercion is strict: `Number.isFinite` gates every accumulator write\n * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.\n */\nexport function extractLlmCallEvent(\n event: SandboxEvent,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'llm_call' || type === 'cost.usage' || type === 'usage') {\n return buildLlmCall(data, agentRunName)\n }\n if (type === 'message.completed' || type === 'result' || type === 'final') {\n const usage = data.usage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName)\n }\n // sandbox 0.4.0 terminal event: `data = { tokenUsage: { inputTokens, outputTokens,\n // reasoningTokens, cacheReadInputTokens }, totalCostUsd }`. Usage lives under\n // `tokenUsage` (not `usage`) and the cost is top-level — neither matched the\n // branches above, so an in-process loopDispatch run reported {0,0} and the\n // backend-integrity guard misread a real run as a stub. Reasoning tokens are\n // billed output (reasoning models), so they fold into the output count. The prompt-cache\n // counters are read off the SAME `tokenUsage` record rather than re-derived, because only\n // that record states what the provider billed.\n if (type === 'done') {\n const usage = data.tokenUsage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n const out = pickFiniteNumber(usage, ['outputTokens', 'completion_tokens', 'tokensOut'])\n const reasoning = pickFiniteNumber(usage, ['reasoningTokens'])\n const mergedOut =\n out !== undefined || reasoning !== undefined ? (out ?? 0) + (reasoning ?? 0) : undefined\n const cache = readPromptCacheUsage(usage)\n return buildLlmCall(\n {\n inputTokens: usage.inputTokens,\n outputTokens: mergedOut,\n totalCostUsd: data.totalCostUsd,\n model: data.model ?? usage.model,\n ...(cache !== undefined ? { promptCache: cache } : {}),\n },\n agentRunName,\n )\n }\n return undefined\n}\n\n/**\n * Sum the token usage + USD cost of a sandbox turn's events — the one honest way to meter an\n * `openSandboxRun` cell. Folds `extractLlmCallEvent` over the stream (which reads usage off EVERY backend\n * event shape), so a `runProfileMatrix` dispatch can report it to `ctx.cost`:\n *\n * receipt: (turn) => {\n * const u = sumSandboxUsage(turn.events)\n * return { model, inputTokens: u.input, outputTokens: u.output,\n * ...(u.tokensKnown === false ? { usageUnknown: true } : {}),\n * ...(u.usdKnown !== false && u.costUsd > 0 ? { actualCostUsd: u.costUsd } : {}),\n * ...(u.usdKnown === false ? { costUnknown: true } : {}),\n * ...(u.estimatedCostUsd !== undefined ? { estimatedCostUsd: u.estimatedCostUsd } : {}) }\n * }\n *\n * Without this a cell reads `{tokens:0, cost:0}` and the backend-integrity guard correctly aborts the\n * matrix as a stub. `agentRunName` is the fallback model label for cost-only events (default `'agent'`).\n */\nexport function sumSandboxUsage(\n events: readonly SandboxEvent[],\n agentRunName = 'agent',\n): {\n input: number\n output: number\n costUsd: number\n tokensKnown?: false\n usdKnown?: false\n estimatedCostUsd?: number\n} {\n let input = 0\n let output = 0\n let costUsd = 0\n let estimatedCostUsd = 0\n let sawEstimate = false\n let sawCall = false\n let tokensKnown = true\n let usdKnown = true\n for (const ev of events) {\n const call = extractLlmCallEvent(ev, agentRunName)\n if (!call) continue\n sawCall = true\n input += call.tokensIn ?? 0\n output += call.tokensOut ?? 0\n costUsd += call.costUsd ?? 0\n if (call.tokensKnown === false) tokensKnown = false\n if (call.usdKnown === false) usdKnown = false\n if (call.estimatedCostUsd !== undefined) {\n estimatedCostUsd += call.estimatedCostUsd\n sawEstimate = true\n }\n }\n return {\n input,\n output,\n costUsd,\n ...(sawCall && tokensKnown ? {} : { tokensKnown: false as const }),\n ...(sawCall && usdKnown ? {} : { usdKnown: false as const }),\n ...(sawEstimate ? { estimatedCostUsd } : {}),\n }\n}\n\nfunction buildLlmCall(\n data: Record<string, unknown>,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n const tokensIn = pickFiniteNumber(data, ['tokensIn', 'inputTokens', 'prompt_tokens'])\n const outputTokens = pickFiniteNumber(data, ['tokensOut', 'outputTokens', 'completion_tokens'])\n const reasoningTokens = pickFiniteNumber(data, ['reasoningTokens'])\n const tokensOut =\n outputTokens !== undefined || reasoningTokens !== undefined\n ? (outputTokens ?? 0) + (reasoningTokens ?? 0)\n : undefined\n const reportedCostUsd = pickFiniteNumber(data, ['costUsd', 'totalCostUsd', 'cost_usd', 'cost'])\n const explicitTokensKnown = data.tokensKnown ?? data.tokens_known\n const explicitCostKnown = data.costKnown ?? data.cost_known ?? data.usdKnown ?? data.usd_known\n const costProvenance = data.costProvenance ?? data.cost_provenance\n const explicitEstimate = pickFiniteNumber(data, ['estimatedCostUsd', 'estimated_cost_usd'])\n const catalogEstimate =\n costProvenance === 'catalog-estimate' ? (explicitEstimate ?? reportedCostUsd) : explicitEstimate\n const costUsd = costProvenance === 'catalog-estimate' ? undefined : reportedCostUsd\n const promptCache = readPromptCacheUsage(data)\n const tokensKnown =\n explicitTokensKnown !== false && tokensIn !== undefined && tokensOut !== undefined\n // Sandbox's canonical terminal `totalCostUsd` is already the provider-reported receipt. The\n // current SDK carries no provenance tag, so absence means the canonical receipt rather than\n // \"unknown\". Explicit estimates and explicit false-known markers remain unknown.\n const usdKnown =\n explicitCostKnown !== false &&\n costUsd !== undefined &&\n (costProvenance === undefined ||\n costProvenance === 'provider-receipt' ||\n costProvenance === 'billing-receipt')\n if (\n tokensIn === undefined &&\n tokensOut === undefined &&\n costUsd === undefined &&\n catalogEstimate === undefined &&\n promptCache === undefined &&\n explicitTokensKnown !== false &&\n explicitCostKnown !== false\n ) {\n return undefined\n }\n const model = typeof data.model === 'string' && data.model.length > 0 ? data.model : agentRunName\n const event: RuntimeStreamEvent & { type: 'llm_call' } = {\n type: 'llm_call',\n model,\n }\n if (tokensIn !== undefined) event.tokensIn = tokensIn\n if (tokensOut !== undefined) event.tokensOut = tokensOut\n if (!tokensKnown) event.tokensKnown = false\n if (costUsd !== undefined) event.costUsd = costUsd\n if (!usdKnown) event.usdKnown = false\n if (catalogEstimate !== undefined) event.estimatedCostUsd = catalogEstimate\n if (promptCache !== undefined) event.promptCache = promptCache\n return event\n}\n\n/**\n * Every wire spelling of a prompt-cache READ counter, in precedence order: the canonical name\n * first, then the spellings observed on the paths that reach this extractor — the sandbox terminal\n * `tokenUsage` record, the cli-bridge OpenAI-compatible usage object (which normalizes every\n * backend to Anthropic's `cache_read_input_tokens`), Anthropic verbatim, OpenAI's\n * `prompt_tokens_details.cached_tokens`, and DeepSeek's `prompt_cache_hit_tokens`.\n */\nconst PROMPT_CACHE_READ_KEYS = [\n 'readTokens',\n 'read_tokens',\n 'cacheReadInputTokens',\n 'cache_read_input_tokens',\n 'cacheRead',\n 'cache_read',\n 'cachedInputTokens',\n 'cachedTokens',\n 'cached_tokens',\n 'prompt_cache_hit_tokens',\n]\n\n/**\n * Every wire spelling of a prompt-cache WRITE counter. OpenAI reports no write counter at all, so\n * an OpenAI-shaped usage record matches nothing here and the write stays absent — which is the\n * point: absent is not zero.\n */\nconst PROMPT_CACHE_WRITE_KEYS = [\n 'writeTokens',\n 'write_tokens',\n 'cacheWriteInputTokens',\n 'cache_write_input_tokens',\n 'cacheCreationInputTokens',\n 'cache_creation_input_tokens',\n 'cacheWrite',\n 'cache_write',\n]\n\nconst PROMPT_CACHE_MISS_KEYS = ['missTokens', 'miss_tokens', 'prompt_cache_miss_tokens']\n\nconst PROMPT_CACHE_SAVINGS_KEYS = ['readSavingsUsd', 'read_savings_usd']\n\n/**\n * Read the provider's own prompt-cache accounting off one usage-bearing event payload, in the\n * `PromptCacheUsage` vocabulary the router, driver, and chat-client paths already speak.\n *\n * Two sources, one record. A payload that already carries a `promptCache` / `prompt_cache` object\n * is forwarded VERBATIM — those fields are the provider's own report, and an unrecognized one must\n * stay visible rather than be bucketed away. On top of that, flat provider counters are translated\n * to the canonical names, filling only the names the verbatim object did not already define.\n *\n * A counter the payload does not carry is left out. Defaulting it to zero would assert the provider\n * measured no cache, which is a different fact from a provider that reported nothing — and it is\n * the fact the budget reads to decide whether its charge is a measurement or a bound.\n */\nfunction readPromptCacheUsage(\n data: Record<string, unknown>,\n): Record<string, number | string> | undefined {\n const declared = finiteMetadata(data.promptCache ?? data.prompt_cache)\n const nested = plainRecord(data.promptCache) ?? plainRecord(data.prompt_cache)\n const details = plainRecord(data.prompt_tokens_details)\n const sources = [nested, data, details].filter(\n (s): s is Record<string, unknown> => s !== undefined,\n )\n\n const canonical: Record<string, number | string> = {}\n const readTokens = pickTokenCount(sources, PROMPT_CACHE_READ_KEYS)\n const writeTokens = pickTokenCount(sources, PROMPT_CACHE_WRITE_KEYS)\n const missTokens = pickTokenCount(sources, PROMPT_CACHE_MISS_KEYS)\n const readSavingsUsd = pickNonNegativeNumber(sources, PROMPT_CACHE_SAVINGS_KEYS)\n const status =\n nested !== undefined && typeof nested.status === 'string' ? nested.status : undefined\n if (readTokens !== undefined) canonical.readTokens = readTokens\n if (writeTokens !== undefined) canonical.writeTokens = writeTokens\n if (missTokens !== undefined) canonical.missTokens = missTokens\n if (readSavingsUsd !== undefined) canonical.readSavingsUsd = readSavingsUsd\n if (status !== undefined) canonical.status = status\n\n const merged = { ...canonical, ...declared }\n return Object.keys(merged).length > 0 ? merged : undefined\n}\n\nfunction plainRecord(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n}\n\nfunction pickTokenCount(\n sources: readonly Record<string, unknown>[],\n keys: readonly string[],\n): number | undefined {\n for (const key of keys) {\n for (const source of sources) {\n const value = source[key]\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value\n }\n }\n return undefined\n}\n\nfunction pickNonNegativeNumber(\n sources: readonly Record<string, unknown>[],\n keys: readonly string[],\n): number | undefined {\n for (const key of keys) {\n for (const source of sources) {\n const value = source[key]\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) return value\n }\n }\n return undefined\n}\n\nfunction finiteMetadata(value: unknown): Record<string, number | string> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined\n const result: Record<string, number | string> = {}\n for (const [key, entry] of Object.entries(value)) {\n if ((typeof entry === 'number' && Number.isFinite(entry)) || typeof entry === 'string') {\n result[key] = entry\n }\n }\n return Object.keys(result).length > 0 ? result : undefined\n}\n\nfunction pickFiniteNumber(data: Record<string, unknown>, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\n/**\n * Cross-event state for {@link mapSandboxToolEvent}. Sandbox backends emit a\n * tool invocation as MANY `message.part.updated` frames on the same call id\n * (pending → running → completed), so faithful projection needs per-call\n * status memory: one `tool_call` on first sighting, at most one `tool_result`\n * on the terminal transition, nothing on intermediate re-frames. Create one\n * state per turn via {@link createSandboxToolPartState}.\n *\n * @experimental\n */\nexport interface SandboxToolPartState {\n /** Last seen status per tool call id. A terminal status is sticky — later\n * frames on a settled call project to nothing. */\n statusByCall: Map<string, string>\n /** Sequence for synthesized call ids when an event carries none. */\n seq: number\n}\n\n/**\n * Fresh per-turn {@link SandboxToolPartState} for {@link mapSandboxToolEvent} — an\n * empty call-status map so each turn projects tool frames independently.\n *\n * @experimental\n */\nexport function createSandboxToolPartState(): SandboxToolPartState {\n return { statusByCall: new Map(), seq: 0 }\n}\n\n/** Statuses that settle one tool call as a failure. A failed tool call stays a tool result:\n * the run's terminal state comes from the public Sandbox outcome tracker, never from here. */\nconst TERMINAL_FAILURE = /^(error|errored|failed|failure|cancelled|canceled|timeout|timed_out)$/i\n\n/**\n * Project one `SandboxEvent` onto the `tool_call` / `tool_result` variants of\n * `RuntimeStreamEvent` — the tool-part projection `mapSandboxEvent`\n * deliberately does NOT perform. Opt-in and additive: `mapSandboxEvent`'s\n * default vocabulary (text/reasoning deltas + `llm_call`) is unchanged;\n * consumers that need the tool surface (chat UIs rendering tool activity)\n * compose this projector alongside it — `streamAgentTurn` does exactly that\n * under its `preserveToolParts` option.\n *\n * Handled shapes (observed on the opencode / claude-code sandbox backends):\n * - `message.part.updated` with `part.type === 'tool'` — stateful: a\n * `tool_call` on the call id's first frame (args from `state.input` or\n * `state.metadata.input`), a `tool_result` when the status transitions to\n * `completed` (result from `state.output` / `metadata.output`) or to a\n * terminal failure (result is `{ error, status, output? }` — the error\n * surfaced in-band, never dropped).\n * - bare `tool*` event types (`tool.call`, `tool_result`, …) — stateless:\n * `*result*` types project to `tool_result`, the rest to `tool_call`.\n *\n * Returns `[]` for every non-tool event.\n *\n * @experimental\n */\nexport function mapSandboxToolEvent(\n event: SandboxEvent,\n state: SandboxToolPartState,\n): (RuntimeStreamEvent & { type: 'tool_call' | 'tool_result' })[] {\n if (!event || typeof event !== 'object') return []\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n if (String(part.type ?? '') !== 'tool') return []\n return projectToolPart(part, state, typeof event.id === 'string' ? event.id : undefined)\n }\n\n if (type.includes('tool')) {\n const callId =\n pickString(data, ['toolCallId', 'tool_use_id', 'id']) ??\n (typeof event.id === 'string' ? event.id : undefined) ??\n `sandbox-tool-${++state.seq}`\n const toolName = pickString(data, ['name', 'toolName', 'tool']) ?? 'sandbox_tool'\n if (type.includes('result')) {\n return [\n {\n type: 'tool_result',\n toolName,\n toolCallId: callId,\n result: data.output ?? data.result ?? data.content ?? data,\n },\n ]\n }\n return [\n { type: 'tool_call', toolName, toolCallId: callId, args: data.input ?? data.args ?? {} },\n ]\n }\n\n return []\n}\n\nfunction projectToolPart(\n part: Record<string, unknown>,\n state: SandboxToolPartState,\n eventId: string | undefined,\n): (RuntimeStreamEvent & { type: 'tool_call' | 'tool_result' })[] {\n const callId =\n pickString(part, ['callID', 'callId', 'toolCallId', 'id']) ??\n eventId ??\n `sandbox-tool-${++state.seq}`\n const toolName = pickString(part, ['tool', 'toolName', 'name']) ?? 'sandbox_tool'\n const toolState =\n part.state && typeof part.state === 'object' ? (part.state as Record<string, unknown>) : {}\n const metadata =\n toolState.metadata && typeof toolState.metadata === 'object'\n ? (toolState.metadata as Record<string, unknown>)\n : {}\n const status = pickString(toolState, ['status']) ?? 'updated'\n\n const previous = state.statusByCall.get(callId)\n const settled =\n previous === 'completed' || (previous !== undefined && TERMINAL_FAILURE.test(previous))\n if (settled) return []\n\n const out: (RuntimeStreamEvent & { type: 'tool_call' | 'tool_result' })[] = []\n if (previous === undefined) {\n out.push({\n type: 'tool_call',\n toolName,\n toolCallId: callId,\n args: toolState.input ?? metadata.input ?? {},\n })\n }\n state.statusByCall.set(callId, status)\n\n if (status === 'completed') {\n out.push({\n type: 'tool_result',\n toolName,\n toolCallId: callId,\n result: toolState.output ?? metadata.output ?? '',\n })\n } else if (TERMINAL_FAILURE.test(status)) {\n const message =\n pickString(toolState, ['error', 'message']) ??\n pickString(metadata, ['error', 'message']) ??\n `sandbox tool ended with status ${status}`\n const output = toolState.output ?? metadata.output\n out.push({\n type: 'tool_result',\n toolName,\n toolCallId: callId,\n result: { error: message, status, ...(output !== undefined ? { output } : {}) },\n })\n }\n return out\n}\n\nfunction pickString(data: Record<string, unknown>, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return undefined\n}\n\n/**\n * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,\n * for runtimes that bridge a sandbox `streamPrompt` into the\n * `AgentRuntime.act` streaming contract. Returns `undefined` for events that\n * have no faithful projection — the raw stream is preserved separately for the\n * `OutputAdapter`, so an unmapped event never loses data.\n *\n * Mapped (the task-optional incremental variants — no synthesized task\n * lifecycle, no guessed tool-part shapes):\n * - `message.part.updated` text part → `text_delta`\n * - `message.part.updated` reasoning/thinking part → `reasoning_delta`\n * - cost-bearing events → `llm_call` (shared with the ledger extractor)\n *\n * Tool parts are deliberately NOT mapped here (unchanged default) — compose\n * {@link mapSandboxToolEvent} alongside when a consumer needs them.\n *\n * The opencode backend emits incremental text as\n * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;\n * `delta` is the increment, `part.text` the running accumulation.\n */\nexport function mapSandboxEvent(\n event: SandboxEvent,\n opts: { agentRunName?: string } = {},\n): RuntimeStreamEvent | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n const partType = String(part.type ?? '')\n const delta = typeof data.delta === 'string' ? data.delta : undefined\n const text = delta ?? (typeof part.text === 'string' ? part.text : undefined)\n if (text === undefined) return undefined\n if (partType === 'text') return { type: 'text_delta', text }\n if (partType === 'reasoning' || partType === 'thinking')\n return { type: 'reasoning_delta', text }\n return undefined\n }\n\n return extractLlmCallEvent(event, opts.agentRunName ?? 'agent')\n}\n\nexport {\n extractTransportEventIdentity,\n parseCanonicalTransportEvent,\n type TransportEventIdentity,\n} from './sandbox-transport-events'\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
- import { J as AgentTaskStatus, Y as BackendErrorDetail, at as RuntimeStreamEvent } from "./types-DZrBoBpN.js";
2
- import { d as ExecutorFactory } from "./types-_7w24mMg.js";
1
+ import { J as AgentTaskStatus, Y as BackendErrorDetail, at as RuntimeStreamEvent } from "./types-DeX8ibLc.js";
2
+ import { d as ExecutorFactory } from "./types-Df9vulGc.js";
3
3
  import { AgentProfile } from "@tangle-network/agent-interface";
4
+ import { AgentRunOutcome } from "@tangle-network/sandbox/runtime";
4
5
  import { PromptOptions, SandboxEvent, SandboxInstance } from "@tangle-network/sandbox";
5
6
  import { AgentTurnInput, AgentTurnInput as AgentTurnInput$1 } from "@tangle-network/agent-interface/environment-provider";
6
7
  //#region src/runtime/stream-agent-turn.d.ts
@@ -99,6 +100,8 @@ interface CollectedAgentTurn {
99
100
  events: RuntimeStreamEvent[];
100
101
  status: AgentTaskStatus;
101
102
  error?: BackendErrorDetail;
103
+ /** Public Sandbox outcome, when the turn ran through a Sandbox stream or executor. */
104
+ sandboxOutcome?: AgentRunOutcome;
102
105
  }
103
106
  /**
104
107
  * Run ONE agent turn on any backend kind and stream its events. Yields the
@@ -122,4 +125,4 @@ declare function streamAgentTurn(backend: AgentTurnBackend, input: AgentTurnInpu
122
125
  declare function collectAgentTurn(stream: AsyncIterable<RuntimeStreamEvent>): Promise<CollectedAgentTurn>;
123
126
  //#endregion
124
127
  export { StreamAgentTurnOptions as a, CollectedAgentTurn as i, AgentTurnInput$1 as n, collectAgentTurn as o, AgentTurnUsage as r, streamAgentTurn as s, AgentTurnBackend as t };
125
- //# sourceMappingURL=stream-agent-turn-Dfy12OjG.d.ts.map
128
+ //# sourceMappingURL=stream-agent-turn-C1Vs_3B9.d.ts.map
@@ -1,10 +1,11 @@
1
1
  import { D as providerMessageText, E as promptOptionsFromAgentTurnInput, T as promptFromAgentTurnInput, d as awaitAbortable } from "./retained-run-binding-Dp2e8xne.js";
2
2
  import { m as ValidationError, p as SessionMismatchError, r as BackendTransportError } from "./errors-CDZ8XsVj.js";
3
- import { i as createSandboxToolPartState, o as mapSandboxEvent, r as canonicalStreamEventFromSandboxEvent, s as mapSandboxToolEvent } from "./sandbox-events-BK-g19wx.js";
3
+ import { a as mapSandboxEvent, n as canonicalStreamEventFromSandboxEvent, o as mapSandboxToolEvent, r as createSandboxToolPartState } from "./sandbox-events-COwCWUVB.js";
4
+ import { C as unknownExecutionBindingReceipt, D as projectSandboxOutcome, O as readSandboxOutcome, T as executableAgentProfileSnapshot, a as authoredProfileDigest, c as knownExecutionBindingReceipt, l as knownMaterializationReceipt, v as runtimeOwnedExecutorExecutionBinding, w as unknownMaterializationReceipt, x as runtimeOwnedPendingExecutorMaterialization, y as runtimeOwnedExecutorMaterialization } from "./materialization-P86EciBw.js";
4
5
  import { c as profileProviderModel, i as concreteModelId, o as profileBridgeWireModel } from "./model-policy-DgRJIJse.js";
5
- import { C as unknownExecutionBindingReceipt, T as executableAgentProfileSnapshot, a as authoredProfileDigest, c as knownExecutionBindingReceipt, l as knownMaterializationReceipt, v as runtimeOwnedExecutorExecutionBinding, w as unknownMaterializationReceipt, x as runtimeOwnedPendingExecutorMaterialization, y as runtimeOwnedExecutorMaterialization } from "./materialization-CrdcfWXp.js";
6
6
  import { canonicalCandidateDigest, renderInputPartsAsText } from "@tangle-network/agent-interface";
7
7
  import { scoreKnowledgeReadiness } from "@tangle-network/agent-eval";
8
+ import { createAgentRunOutcomeTracker } from "@tangle-network/sandbox/runtime";
8
9
  //#region src/sessions.ts
9
10
  /**
10
11
  *
@@ -431,7 +432,9 @@ async function* streamAgentTurnInternal(backend, input, opts) {
431
432
  promptCache: {},
432
433
  tokensKnown: false,
433
434
  usdKnown: false,
434
- sawLlmCall: false
435
+ sawLlmCall: false,
436
+ sawTokenUsage: false,
437
+ sawCostUsage: false
435
438
  };
436
439
  const deadline = deriveTurnSignal(opts.signal, opts.timeoutMs ?? 0);
437
440
  const startedAt = Date.now();
@@ -517,10 +520,11 @@ async function* streamAgentTurnInternal(backend, input, opts) {
517
520
  ({materialization, executionBinding} = executorEvidence(executor, profile, attemptId));
518
521
  assertExactExecutorEvidence(materialization, executionBinding);
519
522
  }
520
- yield buildFinalEvent(task, session, acc, {
523
+ const terminalOutcome = acc.sandboxOutcome ? projectSandboxOutcome(acc.sandboxOutcome) : {
521
524
  status: "completed",
522
525
  reason: "turn completed"
523
- }, turnProvenance(startedAt, opts.timeoutMs, profileDigest, taskDigest, materialization, executionBinding, correlation));
526
+ };
527
+ yield buildFinalEvent(task, session, acc, terminalOutcome, turnProvenance(startedAt, opts.timeoutMs, profileDigest, taskDigest, materialization, executionBinding, correlation));
524
528
  } catch (err) {
525
529
  if (materialization?.status === "unknown" && materialization.reason === "executor-receipt-pending" && executionBinding?.status === "unknown") {
526
530
  const terminal = unknownMaterializationReceipt({
@@ -532,9 +536,10 @@ async function* streamAgentTurnInternal(backend, input, opts) {
532
536
  materialization = terminal;
533
537
  }
534
538
  const callerAborted = opts.signal?.aborted === true;
535
- const status = callerAborted ? "aborted" : "failed";
536
- const message = err instanceof Error ? err.message : String(err);
537
- const error = err instanceof BackendTransportError ? {
539
+ const sandboxProjection = acc.sandboxOutcome ? projectSandboxOutcome(acc.sandboxOutcome) : void 0;
540
+ const status = callerAborted ? "aborted" : sandboxProjection?.status ?? "failed";
541
+ const message = sandboxProjection?.reason ?? (err instanceof Error ? err.message : String(err));
542
+ const error = sandboxProjection?.error ?? (err instanceof BackendTransportError ? {
538
543
  kind: "transport",
539
544
  message,
540
545
  status: err.status,
@@ -542,7 +547,7 @@ async function* streamAgentTurnInternal(backend, input, opts) {
542
547
  } : {
543
548
  kind: "backend",
544
549
  message
545
- };
550
+ });
546
551
  yield {
547
552
  type: "backend_error",
548
553
  task,
@@ -582,6 +587,7 @@ async function collectAgentTurn(stream) {
582
587
  if (final?.type !== "final") throw new Error(`collectAgentTurn: stream ended without a terminal 'final' event (last: ${final ? final.type : "none"})`);
583
588
  const metadata = final.metadata ?? {};
584
589
  const resultMetadata = metadata.result && typeof metadata.result === "object" ? metadata.result : void 0;
590
+ const sandboxOutcome = metadata.sandboxOutcome && typeof metadata.sandboxOutcome === "object" ? metadata.sandboxOutcome : void 0;
585
591
  const tokenUsage = metadata.tokenUsage && typeof metadata.tokenUsage === "object" ? metadata.tokenUsage : {};
586
592
  const usage = {
587
593
  input: finiteNumber(tokenUsage.input) ?? 0,
@@ -611,7 +617,8 @@ async function collectAgentTurn(stream) {
611
617
  toolCalls,
612
618
  events,
613
619
  status: final.status,
614
- ...final.error ? { error: final.error } : {}
620
+ ...final.error ? { error: final.error } : {},
621
+ ...sandboxOutcome ? { sandboxOutcome } : {}
615
622
  };
616
623
  }
617
624
  /** Start the backend's session when it owns one (`chat` kind); mint a local
@@ -645,7 +652,9 @@ async function* driveBoxTurn(box, input, signal, agentRunName, acc, cfg) {
645
652
  signal
646
653
  });
647
654
  const toolParts = cfg.preserveToolParts ? createSandboxToolPartState() : void 0;
655
+ const outcomeTracker = createAgentRunOutcomeTracker();
648
656
  for await (const event of abortableValues(stream, signal)) {
657
+ outcomeTracker.observe(event);
649
658
  if (cfg.onRawEvent) await awaitAbortable(Promise.resolve().then(() => cfg.onRawEvent(event)), signal);
650
659
  const terminalText = terminalTextFromSandboxEvent(event);
651
660
  if (terminalText !== void 0) acc.terminalText = terminalText;
@@ -661,6 +670,9 @@ async function* driveBoxTurn(box, input, signal, agentRunName, acc, cfg) {
661
670
  yield mapped;
662
671
  }
663
672
  }
673
+ acc.sandboxOutcome = outcomeTracker.finish();
674
+ const projection = projectSandboxOutcome(acc.sandboxOutcome);
675
+ if (projection.status === "failed") throw new Error(projection.reason);
664
676
  }
665
677
  /** One turn over an in-process backend: its own `stream()` surface, projected
666
678
  * through the same `normalizeBackendStreamEvent` the task lifecycle applies. */
@@ -683,10 +695,14 @@ async function* driveExecutorTurn(executor, task, session, input, signal, acc, d
683
695
  const run = executor.execute(taskValue, signal);
684
696
  let result;
685
697
  if (isAsyncIterable(run)) {
686
- for await (const _usage of abortableValues(run, signal)) throwIfAborted(signal);
698
+ for await (const usage of abortableValues(run, signal)) {
699
+ foldUsageEvent(usage, acc);
700
+ throwIfAborted(signal);
701
+ }
687
702
  result = executor.resultArtifact();
688
703
  } else result = await awaitAbortable(run, signal);
689
704
  acc.result = result;
705
+ acc.sandboxOutcome = readSandboxOutcome(result.out);
690
706
  acc.terminalText = executorResultText(result.out);
691
707
  acc.input = result.spent.tokens.input;
692
708
  acc.output = result.spent.tokens.output;
@@ -744,6 +760,7 @@ async function* driveExecutorTurn(executor, task, session, input, signal, acc, d
744
760
  },
745
761
  timestamp: nowIso()
746
762
  };
763
+ if (acc.sandboxOutcome && projectSandboxOutcome(acc.sandboxOutcome).status === "failed") throw new Error(projectSandboxOutcome(acc.sandboxOutcome).reason);
747
764
  }
748
765
  function executorTaskValue(input) {
749
766
  const messages = input.providerOptions?.messages;
@@ -835,6 +852,26 @@ function executorResultToolCalls(value) {
835
852
  }];
836
853
  });
837
854
  }
855
+ /** Fold one normalized executor usage event into the turn accumulator. */
856
+ function foldUsageEvent(event, acc) {
857
+ if (event.kind === "tokens") {
858
+ const known = event.tokensKnown !== false;
859
+ acc.tokensKnown = acc.sawTokenUsage ? acc.tokensKnown && known : known;
860
+ acc.sawTokenUsage = true;
861
+ acc.input += event.input;
862
+ acc.output += event.output;
863
+ } else if (event.kind === "cost") {
864
+ const known = event.usdKnown !== false;
865
+ acc.usdKnown = acc.sawCostUsage ? acc.usdKnown && known : known;
866
+ acc.sawCostUsage = true;
867
+ acc.costUsd += event.usd;
868
+ if (event.usdEstimated !== void 0) {
869
+ acc.estimatedCostUsd += event.usdEstimated;
870
+ acc.sawEstimatedCost = true;
871
+ }
872
+ }
873
+ if (event.kind === "tokens" || event.kind === "cost") acc.sawLlmCall = true;
874
+ }
838
875
  /** Fold one normalized event into the turn accumulator (text + usage).
839
876
  * `fallbackModelLabel` — a mapper-stamped run label to exclude from
840
877
  * `usage.model` (it is not a backend-reported model). */
@@ -903,12 +940,16 @@ function buildFinalEvent(task, session, acc, outcome, provenance) {
903
940
  ...acc.model ? { model: acc.model } : {},
904
941
  ...acc.stopReason ? { stopReason: acc.stopReason } : {},
905
942
  ...acc.transportAttempts !== void 0 ? { transportAttempts: acc.transportAttempts } : {},
943
+ ...acc.sandboxOutcome ? {
944
+ sandboxOutcome: acc.sandboxOutcome,
945
+ verdict: projectSandboxOutcome(acc.sandboxOutcome).verdict
946
+ } : {},
906
947
  ...acc.result ? { result: {
907
948
  outRef: acc.result.outRef,
908
949
  output: acc.result.out,
909
- ...acc.result.verdict ? { verdict: acc.result.verdict } : {},
950
+ ...acc.sandboxOutcome ? { verdict: projectSandboxOutcome(acc.sandboxOutcome).verdict } : acc.result.verdict ? { verdict: acc.result.verdict } : {},
910
951
  spent: acc.result.spent
911
- } } : {},
952
+ } } : acc.sandboxOutcome ? { result: { verdict: projectSandboxOutcome(acc.sandboxOutcome).verdict } } : {},
912
953
  ...provenance
913
954
  },
914
955
  ...outcome.error ? { error: outcome.error } : {},
@@ -959,4 +1000,4 @@ function deriveTurnSignal(callerSignal, timeoutMs) {
959
1000
  //#endregion
960
1001
  export { normalizeBackendStreamEvent as a, nowIso as c, createSandboxPromptBackend as i, startOrResumeRuntimeSession as l, streamAgentTurn as n, InMemoryRuntimeSessionStore as o, createIterableBackend as r, newRuntimeSession as s, collectAgentTurn as t, touchSession as u };
961
1002
 
962
- //# sourceMappingURL=stream-agent-turn-BMHUIuQS.js.map
1003
+ //# sourceMappingURL=stream-agent-turn-CXRugSGO.js.map