@tangle-network/agent-runtime 0.128.0 → 0.131.1

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 (139) hide show
  1. package/README.md +70 -20
  2. package/dist/{activation-DhWJ3p8N.js → activation-BCzMOTaV.js} +3 -3
  3. package/dist/{activation-DhWJ3p8N.js.map → activation-BCzMOTaV.js.map} +1 -1
  4. package/dist/agent.d.ts +2 -3
  5. package/dist/agent.js +4 -5
  6. package/dist/agent.js.map +1 -1
  7. package/dist/{analyst-loop-DvSciOfB.js → analyst-loop-BE8cDs5Q.js} +2 -2
  8. package/dist/{analyst-loop-DvSciOfB.js.map → analyst-loop-BE8cDs5Q.js.map} +1 -1
  9. package/dist/analyst-loop.d.ts +1 -1
  10. package/dist/analyst-loop.js +1 -1
  11. package/dist/authoring-Dv3t6SXe.js +163 -0
  12. package/dist/authoring-Dv3t6SXe.js.map +1 -0
  13. package/dist/candidate-execution/index.js +4 -4
  14. package/dist/{candidate-execution-BFpq-Xi6.js → candidate-execution-BNxKr-Bu.js} +5 -5
  15. package/dist/{candidate-execution-BFpq-Xi6.js.map → candidate-execution-BNxKr-Bu.js.map} +1 -1
  16. package/dist/{conversation-BpLQZGPH.js → conversation-DNtxaJ1Z.js} +113 -31
  17. package/dist/conversation-DNtxaJ1Z.js.map +1 -0
  18. package/dist/conversation.d.ts +2 -2
  19. package/dist/conversation.js +2 -2
  20. package/dist/environment-provider-CxvSd1W6.d.ts +86 -0
  21. package/dist/{environment-provider-DqFS6FSZ.js → environment-provider-Dyg8DtLK.js} +8 -4
  22. package/dist/environment-provider-Dyg8DtLK.js.map +1 -0
  23. package/dist/environment-provider.d.ts +1 -1
  24. package/dist/environment-provider.js +1 -1
  25. package/dist/graph-xWdv53Le.js +471 -0
  26. package/dist/graph-xWdv53Le.js.map +1 -0
  27. package/dist/{improvement-cycle-IJgCbKWQ.js → improvement-cycle-Csp38cWg.js} +133 -224
  28. package/dist/improvement-cycle-Csp38cWg.js.map +1 -0
  29. package/dist/{index-BhZhQw77.d.ts → index-CoO7atyo.d.ts} +556 -1278
  30. package/dist/{index-BhuzfG2r.d.ts → index-DwGtu9nc.d.ts} +7 -9
  31. package/dist/{index-Efjb3nrQ.d.ts → index-qYHpsmG2.d.ts} +36 -18
  32. package/dist/index.d.ts +353 -11
  33. package/dist/index.js +111 -354
  34. package/dist/index.js.map +1 -1
  35. package/dist/intelligence.d.ts +6 -6
  36. package/dist/intelligence.js +9 -8
  37. package/dist/intelligence.js.map +1 -1
  38. package/dist/kernel.d.ts +7 -5
  39. package/dist/kernel.js +13 -9
  40. package/dist/{knowledge-DF63xPr4.js → knowledge-DPEu4f-0.js} +19 -17
  41. package/dist/knowledge-DPEu4f-0.js.map +1 -0
  42. package/dist/knowledge.d.ts +1 -1
  43. package/dist/knowledge.js +1 -1
  44. package/dist/{loop-runner-bin-Ckp_9tmD.d.ts → loop-runner-bin-BwgZ8m_7.d.ts} +6 -3
  45. package/dist/{loop-runner-bin-CWqOpCEw.js → loop-runner-bin-dg6li2-b.js} +5 -27
  46. package/dist/loop-runner-bin-dg6li2-b.js.map +1 -0
  47. package/dist/loop-runner-bin.d.ts +1 -1
  48. package/dist/loop-runner-bin.js +1 -1
  49. package/dist/materialization-COJ1UYQ-.js +272 -0
  50. package/dist/materialization-COJ1UYQ-.js.map +1 -0
  51. package/dist/mcp/bin.js +39 -47
  52. package/dist/mcp/bin.js.map +1 -1
  53. package/dist/mcp/index.d.ts +24 -26
  54. package/dist/mcp/index.js +67 -84
  55. package/dist/mcp/index.js.map +1 -1
  56. package/dist/mcp/memory-bin.js +1 -1
  57. package/dist/{memory-server-DL6cE2Ag.js → memory-server-eD2baiRO.js} +3 -3
  58. package/dist/{memory-server-DL6cE2Ag.js.map → memory-server-eD2baiRO.js.map} +1 -1
  59. package/dist/model-policy-CqziaqS1.js +232 -0
  60. package/dist/model-policy-CqziaqS1.js.map +1 -0
  61. package/dist/{openai-tools-B68JaOCx.d.ts → openai-tools-D3oMyVGl.d.ts} +2 -2
  62. package/dist/{openai-tools-D3XfrrQ6.js → openai-tools-zRphjXS4.js} +2 -2
  63. package/dist/openai-tools-zRphjXS4.js.map +1 -0
  64. package/dist/{prepare--8EvLqCr.js → prepare-DYWjVcPx.js} +169 -153
  65. package/dist/prepare-DYWjVcPx.js.map +1 -0
  66. package/dist/primeintellect/index.d.ts +7 -6
  67. package/dist/primeintellect/index.js +9 -11
  68. package/dist/primeintellect/index.js.map +1 -1
  69. package/dist/profiles.d.ts +21 -174
  70. package/dist/profiles.js +67 -276
  71. package/dist/profiles.js.map +1 -1
  72. package/dist/{protected-model-port-CXVfOUu_.js → protected-model-port-48ALLGxT.js} +2 -2
  73. package/dist/{protected-model-port-CXVfOUu_.js.map → protected-model-port-48ALLGxT.js.map} +1 -1
  74. package/dist/{redact-PQzmE1Jn.d.ts → redact-9_Gf-8m3.d.ts} +58 -69
  75. package/dist/{researcher-CoVqNhfI.js → researcher-Skz5-Uc8.js} +50 -26
  76. package/dist/researcher-Skz5-Uc8.js.map +1 -0
  77. package/dist/{run-layout-C2FGmZ3v.js → run-layout-CeJsEAom.js} +2 -2
  78. package/dist/{run-layout-C2FGmZ3v.js.map → run-layout-CeJsEAom.js.map} +1 -1
  79. package/dist/runtime-D-QfLbSd.d.ts +893 -0
  80. package/dist/{runtime-5uDVVfER.js → runtime-cOzDOOHr.js} +315 -1191
  81. package/dist/runtime-cOzDOOHr.js.map +1 -0
  82. package/dist/{sandbox-events-Yhd1GYWl.js → sandbox-events-CRDwc5WN.js} +42 -5
  83. package/dist/sandbox-events-CRDwc5WN.js.map +1 -0
  84. package/dist/snapshot-CXiiuHhL.js +21 -0
  85. package/dist/snapshot-CXiiuHhL.js.map +1 -0
  86. package/dist/{spawn-journal-DsZKDqeh.js → spawn-journal-saHQzqYi.js} +15 -21
  87. package/dist/spawn-journal-saHQzqYi.js.map +1 -0
  88. package/dist/stream-agent-turn-C852AgMT.d.ts +128 -0
  89. package/dist/stream-agent-turn-rYgaOLO0.js +910 -0
  90. package/dist/stream-agent-turn-rYgaOLO0.js.map +1 -0
  91. package/dist/{structural-rollout-zY0oqQzO.js → structural-rollout-3uxVGcg2.js} +459 -235
  92. package/dist/structural-rollout-3uxVGcg2.js.map +1 -0
  93. package/dist/{supervise-CsTKbH9R.js → supervise-DHYX8gO2.js} +867 -4788
  94. package/dist/supervise-DHYX8gO2.js.map +1 -0
  95. package/dist/{supervisor-DpjO0Gmy.js → supervisor-CV6Jh28D.js} +7439 -2897
  96. package/dist/supervisor-CV6Jh28D.js.map +1 -0
  97. package/dist/testing.d.ts +3 -1
  98. package/dist/testing.js +271 -221
  99. package/dist/testing.js.map +1 -1
  100. package/dist/{tool-server-RcWgLIsL.js → tool-server-Gs3VvfSK.js} +22 -9
  101. package/dist/tool-server-Gs3VvfSK.js.map +1 -0
  102. package/dist/{top-app-5unxqovu.js → top-app-G4M18b6i.js} +3 -3
  103. package/dist/{top-app-5unxqovu.js.map → top-app-G4M18b6i.js.map} +1 -1
  104. package/dist/tui/bin.js +1 -1
  105. package/dist/tui/index.js +1 -1
  106. package/dist/{environment-provider-CUFsyymu.d.ts → types-C6Q-J0Dt.d.ts} +51 -114
  107. package/dist/{types-DnNGJ5Gz.d.ts → types-ebIY0dMG.d.ts} +556 -30
  108. package/dist/{util-MVgdwuIS.js → util-Bw6srryQ.js} +3 -2
  109. package/dist/{util-MVgdwuIS.js.map → util-Bw6srryQ.js.map} +1 -1
  110. package/dist/{workspace-archive-BQxvkypI.js → workspace-archive-aOfJ47ms.js} +3 -3
  111. package/dist/{workspace-archive-BQxvkypI.js.map → workspace-archive-aOfJ47ms.js.map} +1 -1
  112. package/package.json +13 -15
  113. package/skills/agent-graphs/IMPROVE.md +3 -3
  114. package/skills/agent-graphs/SKILL.md +4 -5
  115. package/skills/agent-graphs/cases/review-pipeline.json +1 -2
  116. package/skills/agent-graphs/cases/unmeasured-harness.json +2 -4
  117. package/dist/backends-CiOCyRHb.js +0 -743
  118. package/dist/backends-CiOCyRHb.js.map +0 -1
  119. package/dist/conversation-BpLQZGPH.js.map +0 -1
  120. package/dist/environment-provider-DqFS6FSZ.js.map +0 -1
  121. package/dist/improvement-cycle-IJgCbKWQ.js.map +0 -1
  122. package/dist/index-DLM0W1h1.d.ts +0 -545
  123. package/dist/knowledge-DF63xPr4.js.map +0 -1
  124. package/dist/local-harness-BIajef4A.d.ts +0 -465
  125. package/dist/loop-runner-bin-CWqOpCEw.js.map +0 -1
  126. package/dist/model-resolution-Btd9iIKV.js +0 -98
  127. package/dist/model-resolution-Btd9iIKV.js.map +0 -1
  128. package/dist/openai-tools-D3XfrrQ6.js.map +0 -1
  129. package/dist/prepare--8EvLqCr.js.map +0 -1
  130. package/dist/researcher-CoVqNhfI.js.map +0 -1
  131. package/dist/runtime-5uDVVfER.js.map +0 -1
  132. package/dist/sandbox-events-Yhd1GYWl.js.map +0 -1
  133. package/dist/spawn-journal-DsZKDqeh.js.map +0 -1
  134. package/dist/structural-rollout-zY0oqQzO.js.map +0 -1
  135. package/dist/supervise-CsTKbH9R.js.map +0 -1
  136. package/dist/supervisor-DpjO0Gmy.js.map +0 -1
  137. package/dist/tool-server-RcWgLIsL.js.map +0 -1
  138. package/dist/types-C9j4qg6l.d.ts +0 -500
  139. package/skills/agent-graphs/cases/floor-trap-pi.json +0 -11
@@ -0,0 +1,910 @@
1
+ import { l as SessionMismatchError, r as BackendTransportError, u as ValidationError } from "./errors-DEAvWQPy.js";
2
+ import { i as mapSandboxToolEvent, r as mapSandboxEvent, t as createSandboxToolPartState } from "./sandbox-events-CRDwc5WN.js";
3
+ import { c as profileProviderModel, i as concreteModelId, o as profileBridgeWireModel } from "./model-policy-CqziaqS1.js";
4
+ import { _ as unknownMaterializationReceipt, a as authoredProfileDigest, c as knownExecutionBindingReceipt, f as runtimeOwnedExecutorExecutionBinding, g as unknownExecutionBindingReceipt, l as knownMaterializationReceipt, m as runtimeOwnedPendingExecutorMaterialization, p as runtimeOwnedExecutorMaterialization, v as executableAgentProfileSnapshot } from "./materialization-COJ1UYQ-.js";
5
+ import { scoreKnowledgeReadiness } from "@tangle-network/agent-eval";
6
+ import { canonicalCandidateDigest } from "@tangle-network/agent-interface";
7
+ //#region src/sessions.ts
8
+ /**
9
+ *
10
+ * Session helpers + an in-memory `RuntimeSessionStore` implementation suitable
11
+ * for tests, scratch processes, and per-request scratch storage in serverless
12
+ * runtimes. Durable stores (D1, postgres, Durable Objects) implement the same
13
+ * interface from `./types`.
14
+ *
15
+ * @stable
16
+ */
17
+ /** @internal */
18
+ function newRuntimeSession(backend, requestedId, metadata) {
19
+ const now = nowIso();
20
+ return {
21
+ id: requestedId || crypto.randomUUID(),
22
+ backend,
23
+ status: "active",
24
+ createdAt: now,
25
+ updatedAt: now,
26
+ metadata
27
+ };
28
+ }
29
+ /** @internal */
30
+ function touchSession(session) {
31
+ return {
32
+ ...session,
33
+ updatedAt: nowIso()
34
+ };
35
+ }
36
+ /** @internal */
37
+ function nowIso() {
38
+ return (/* @__PURE__ */ new Date()).toISOString();
39
+ }
40
+ /** Start or resume one backend session and persist its current identity. @internal */
41
+ async function startOrResumeRuntimeSession(options) {
42
+ const existing = options.resume && options.sessionId ? await options.store?.get(options.sessionId) : void 0;
43
+ if (!existing) {
44
+ const input = resolveSessionInput(options.input);
45
+ const session = options.backend.start ? await options.backend.start(input, {
46
+ ...options.context,
47
+ requestedSessionId: options.sessionId
48
+ }) : newRuntimeSession(options.backend.kind, options.sessionId);
49
+ assertSessionBackend(session, options.backend.kind);
50
+ options.validateSession?.(session);
51
+ await options.store?.put(session);
52
+ return {
53
+ session,
54
+ input,
55
+ resumed: false
56
+ };
57
+ }
58
+ assertSessionBackend(existing, options.backend.kind);
59
+ const input = resolveSessionInput(options.backend.resume !== void 0 ? options.continuationInput ?? options.input : options.input);
60
+ const session = options.backend.resume ? await options.backend.resume(existing, input, options.context) : touchSession({
61
+ ...existing,
62
+ status: "active"
63
+ });
64
+ assertSessionBackend(session, options.backend.kind);
65
+ options.validateSession?.(session);
66
+ await options.store?.put(session);
67
+ return {
68
+ session,
69
+ input,
70
+ resumed: true
71
+ };
72
+ }
73
+ function resolveSessionInput(input) {
74
+ return typeof input === "function" ? input() : input;
75
+ }
76
+ function assertSessionBackend(session, backend) {
77
+ if (session.backend !== backend) throw new SessionMismatchError(session.backend, backend);
78
+ }
79
+ /** In-memory `RuntimeSessionStore` for single-process use and tests. @stable */
80
+ var InMemoryRuntimeSessionStore = class {
81
+ sessions = /* @__PURE__ */ new Map();
82
+ events = /* @__PURE__ */ new Map();
83
+ get(sessionId) {
84
+ return this.sessions.get(sessionId);
85
+ }
86
+ put(session) {
87
+ this.sessions.set(session.id, session);
88
+ }
89
+ appendEvent(sessionId, event) {
90
+ const existing = this.events.get(sessionId) ?? [];
91
+ existing.push(event);
92
+ this.events.set(sessionId, existing);
93
+ }
94
+ listEvents(sessionId) {
95
+ return [...this.events.get(sessionId) ?? []];
96
+ }
97
+ };
98
+ //#endregion
99
+ //#region src/backends.ts
100
+ /**
101
+ * Provider-neutral backend adapters for `runAgentTaskStream`.
102
+ *
103
+ * Model inference is deliberately absent. Paid model work enters the Runtime executor with an
104
+ * exact `AgentProfile`; these adapters only normalize a caller-owned iterable or sandbox stream.
105
+ *
106
+ * @stable
107
+ */
108
+ /** Wrap any custom async-iterable stream into a typed `AgentExecutionBackend`. @stable */
109
+ function createIterableBackend(options) {
110
+ return options;
111
+ }
112
+ /** Build an `AgentExecutionBackend` backed by a sandbox/sidecar `streamPrompt` call. @stable */
113
+ function createSandboxPromptBackend(options) {
114
+ const kind = options.kind ?? "sandbox";
115
+ return {
116
+ kind,
117
+ async start(input, context) {
118
+ const box = await options.getBox(input, context);
119
+ return newRuntimeSession(kind, options.getSessionId?.(box, input) ?? context.requestedSessionId, { resumable: true });
120
+ },
121
+ resume(session) {
122
+ return touchSession({
123
+ ...session,
124
+ status: "active"
125
+ });
126
+ },
127
+ async *stream(input, context) {
128
+ const box = await options.getBox(input, context);
129
+ const message = input.message ?? input.messages?.at(-1)?.content ?? context.task.intent;
130
+ for await (const event of options.streamPrompt(box, message, context)) {
131
+ const mapped = options.mapEvent?.(event, context) ?? mapCommonBackendEvent(event, context);
132
+ if (mapped) yield mapped;
133
+ }
134
+ }
135
+ };
136
+ }
137
+ /** @internal */
138
+ function normalizeBackendStreamEvent(event, task, session) {
139
+ if ("task" in event && event.task && "session" in event && event.session && "timestamp" in event && event.timestamp) return event;
140
+ return {
141
+ ...event,
142
+ task: "task" in event && event.task ? event.task : task,
143
+ session: "session" in event && event.session ? event.session : session,
144
+ timestamp: "timestamp" in event && event.timestamp ? event.timestamp : nowIso()
145
+ };
146
+ }
147
+ function mapCommonBackendEvent(event, context) {
148
+ if (!event || typeof event !== "object") return void 0;
149
+ const record = event;
150
+ const type = String(record.type ?? "");
151
+ const data = record.data && typeof record.data === "object" ? record.data : record;
152
+ if (type === "message.part.updated" || type === "text_delta" || type === "delta") {
153
+ const part = data.part;
154
+ const partText = part !== void 0 && typeof part === "object" && (part.type === "text" || part.type === void 0) ? stringValue(part.text) : void 0;
155
+ const text = stringValue(data.text) ?? stringValue(data.delta) ?? stringValue(record.text) ?? partText;
156
+ return text ? {
157
+ type: "text_delta",
158
+ task: context.task,
159
+ session: context.session,
160
+ text,
161
+ timestamp: nowIso()
162
+ } : void 0;
163
+ }
164
+ if (type === "reasoning_delta") {
165
+ const text = stringValue(data.text) ?? stringValue(record.text);
166
+ return text ? {
167
+ type: "reasoning_delta",
168
+ task: context.task,
169
+ session: context.session,
170
+ text,
171
+ timestamp: nowIso()
172
+ } : void 0;
173
+ }
174
+ if (type === "tool_call") return {
175
+ type: "tool_call",
176
+ task: context.task,
177
+ session: context.session,
178
+ toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
179
+ toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
180
+ args: data.args ?? data.input ?? record.args,
181
+ timestamp: nowIso()
182
+ };
183
+ if (type === "tool_result") return {
184
+ type: "tool_result",
185
+ task: context.task,
186
+ session: context.session,
187
+ toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
188
+ toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
189
+ result: data.result ?? data.output ?? record.result,
190
+ timestamp: nowIso()
191
+ };
192
+ if (type === "artifact") {
193
+ const artifactId = stringValue(data.artifactId) ?? stringValue(data.id) ?? stringValue(record.artifactId);
194
+ if (!artifactId) return void 0;
195
+ return {
196
+ type: "artifact",
197
+ task: context.task,
198
+ session: context.session,
199
+ artifactId,
200
+ name: stringValue(data.name) ?? stringValue(record.name),
201
+ mimeType: stringValue(data.mimeType) ?? stringValue(record.mimeType),
202
+ uri: stringValue(data.uri) ?? stringValue(record.uri),
203
+ content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
204
+ metadata: data.metadata && typeof data.metadata === "object" ? data.metadata : void 0,
205
+ timestamp: nowIso()
206
+ };
207
+ }
208
+ if (type === "proposal_created" || type === "proposal" || type === "filing") {
209
+ const proposalId = stringValue(data.proposalId) ?? stringValue(data.id) ?? stringValue(record.proposalId);
210
+ if (!proposalId) return void 0;
211
+ const status = stringValue(data.status) ?? stringValue(record.status);
212
+ return {
213
+ type: "proposal_created",
214
+ task: context.task,
215
+ session: context.session,
216
+ proposalId,
217
+ title: stringValue(data.title) ?? stringValue(record.title) ?? proposalId,
218
+ status: status === "pending" || status === "approved" || status === "rejected" ? status : void 0,
219
+ content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
220
+ timestamp: nowIso()
221
+ };
222
+ }
223
+ if (type === "result" || type === "final") {
224
+ const text = stringValue(data.finalText) ?? stringValue(data.text) ?? stringValue(record.text);
225
+ return text ? {
226
+ type: "text_delta",
227
+ task: context.task,
228
+ session: context.session,
229
+ text,
230
+ timestamp: nowIso()
231
+ } : void 0;
232
+ }
233
+ }
234
+ function stringValue(value) {
235
+ return typeof value === "string" && value.length > 0 ? value : void 0;
236
+ }
237
+ //#endregion
238
+ //#region src/runtime/stream-agent-turn.ts
239
+ /**
240
+ * `streamAgentTurn` — the ONE run-a-turn event-stream contract over every
241
+ * execution substrate: a sandbox box (`SandboxInstance.streamPrompt`), a
242
+ * one-shot Runtime-owned `Executor` (cli-bridge / router / sandbox, via
243
+ * `ExecutorFactory`), and an in-process `AgentExecutionBackend`.
244
+ *
245
+ * One function, one vocabulary: every backend kind yields the existing
246
+ * `RuntimeStreamEvent` union incrementally and ALWAYS terminates with a
247
+ * `final` event whose `text` is the turn's final text and whose
248
+ * `metadata.tokenUsage` / `metadata.costUsd` / `metadata.model` carry the
249
+ * turn's metered usage. `collectAgentTurn` drains a stream into that terminal
250
+ * summary plus the full event list.
251
+ *
252
+ * This is a UNIFICATION seam, not a new stream parser — each kind is a thin
253
+ * adapter over code that already exists and is already hardened:
254
+ * - `box` — `mapSandboxEvent` + `extractLlmCallEvent` (sandbox-events.ts)
255
+ * project the sandbox event stream; its requested profile is
256
+ * explicitly recorded as unverified because the box already exists.
257
+ * - `executor` — Runtime materializes the exact `AgentProfile`, records its
258
+ * identity receipts, drives the executor once, and tears it
259
+ * down after capturing the terminal artifact.
260
+ * - `chat` — the backend's own `stream()` surface, normalized by
261
+ * `normalizeBackendStreamEvent`; its requested profile is likewise
262
+ * unverified because an arbitrary backend cannot attest its setup.
263
+ *
264
+ * Distinct from `openSandboxRun` (box-only, session resume over one persistent
265
+ * artifact, raw `SandboxEvent` deliverables) and from `runAgentTaskStream`
266
+ * (full task lifecycle: knowledge preflight, session store, resume). This is
267
+ * the minimal turn primitive underneath both worlds: prompt in, one normalized
268
+ * event stream out, terminal result+usage guaranteed on every non-thrown path.
269
+ *
270
+ * Stream envelope: `backend_start` → incremental events → (`backend_error` on
271
+ * failure) → `final`. A caller-initiated abort terminates with
272
+ * `final.status: 'aborted'`; an expired `timeoutMs` deadline with
273
+ * `final.status: 'failed'` — so cancellation stays distinguishable from a
274
+ * blown deadline.
275
+ *
276
+ * Mid-stream lifecycle work needs NO extra API: the generator is pull-based,
277
+ * so the producer is suspended between yields and resumes only when the caller
278
+ * pulls again. A consumer can therefore run arbitrary async work between
279
+ * events — sync state on each `tool_result`, decide a no-op retry after
280
+ * draining, run a pre-`done` flush when it receives `final` and BEFORE it
281
+ * forwards its own terminal event downstream. The interleaving is guaranteed
282
+ * (and locked by test): nothing is produced past the event the caller is
283
+ * holding.
284
+ *
285
+ * @stable
286
+ */
287
+ function turnIntent(input) {
288
+ if (typeof input === "string") return input;
289
+ for (let index = input.messages.length - 1; index >= 0; index -= 1) {
290
+ const message = input.messages[index];
291
+ if (message?.role === "user" && typeof message.content === "string") return message.content;
292
+ }
293
+ return "structured agent turn";
294
+ }
295
+ function turnBackendInput(task, input) {
296
+ if (typeof input === "string") return {
297
+ task,
298
+ message: input
299
+ };
300
+ return {
301
+ task,
302
+ messages: input.messages.map((message) => ({ ...message }))
303
+ };
304
+ }
305
+ function executorEvidence(executor, profile, attemptId) {
306
+ const profileDigest = authoredProfileDigest(profile);
307
+ const declaration = runtimeOwnedExecutorMaterialization(executor);
308
+ if (!profileDigest || !declaration) {
309
+ const materialization = unknownMaterializationReceipt({
310
+ ...profileDigest ? { authoredProfileDigest: profileDigest } : {},
311
+ runtime: executor.runtime,
312
+ reason: declaration ? "invalid-executor-report" : "executor-did-not-report"
313
+ });
314
+ return {
315
+ materialization,
316
+ executionBinding: unknownExecutionBindingReceipt(materialization, attemptId, declaration ? "invalid-executor-report" : "executor-did-not-report")
317
+ };
318
+ }
319
+ try {
320
+ const materialization = knownMaterializationReceipt({
321
+ authoredProfileDigest: profileDigest,
322
+ runtime: executor.runtime,
323
+ declaration
324
+ });
325
+ const binding = runtimeOwnedExecutorExecutionBinding(executor);
326
+ if (!binding || binding.attemptId !== attemptId) throw new Error("executor attempt id mismatch");
327
+ return {
328
+ materialization,
329
+ executionBinding: knownExecutionBindingReceipt(materialization, binding)
330
+ };
331
+ } catch {
332
+ const materialization = unknownMaterializationReceipt({
333
+ authoredProfileDigest: profileDigest,
334
+ runtime: executor.runtime,
335
+ reason: "invalid-executor-report"
336
+ });
337
+ return {
338
+ materialization,
339
+ executionBinding: unknownExecutionBindingReceipt(materialization, attemptId, "invalid-executor-report")
340
+ };
341
+ }
342
+ }
343
+ /** Exact turns require the model-bearing profile to be the sole behavioral authority. Runtime
344
+ * checks the trusted declaration before execution so a seam fallback cannot hide behind the same
345
+ * profile digest. */
346
+ function assertExactExecutorDeclaration(executor, profile) {
347
+ const profileModel = profileProviderModel(profile);
348
+ if (!profileModel) throw new ValidationError("streamAgentTurn: exact AgentProfile.model.default must name the concrete model");
349
+ const declaration = runtimeOwnedExecutorMaterialization(executor) ?? runtimeOwnedPendingExecutorMaterialization(executor)?.declaration;
350
+ if (!declaration) return;
351
+ if (declaration.model.status !== "known") throw new ValidationError("streamAgentTurn: exact executor did not materialize a known model identity");
352
+ if (declaration.backend === "router" || declaration.backend === "router-tools") {
353
+ const plan = declaration.plan;
354
+ if (declaration.model.id !== profileModel) throw new ValidationError("streamAgentTurn: Router executor model differs from AgentProfile.model.default");
355
+ if (plan.configuredModel !== null && plan.configuredModel !== void 0 && concreteModelId(String(plan.configuredModel)) !== profileModel) throw new ValidationError("streamAgentTurn: configured Router model conflicts with AgentProfile.model.default");
356
+ const expectedEffort = profile.model?.reasoningEffort ?? null;
357
+ if (plan.configuredReasoningEffort !== null && plan.configuredReasoningEffort !== void 0 && plan.configuredReasoningEffort !== expectedEffort) throw new ValidationError("streamAgentTurn: configured Router reasoning conflicts with AgentProfile.model.reasoningEffort");
358
+ if ((plan.reasoningEffort ?? null) !== expectedEffort) throw new ValidationError("streamAgentTurn: Router reasoning effort must come from AgentProfile.model.reasoningEffort");
359
+ }
360
+ if (declaration.backend === "bridge" || declaration.backend === "bridge-worktree") {
361
+ const expectedWireModel = profileBridgeWireModel(profile);
362
+ if (!expectedWireModel || declaration.model.id !== expectedWireModel) throw new ValidationError("streamAgentTurn: bridge executor model differs from the AgentProfile harness/provider/model wire id");
363
+ const plan = declaration.plan;
364
+ if (plan.configuredModel !== null && plan.configuredModel !== void 0 && concreteModelId(String(plan.configuredModel)) !== expectedWireModel) throw new ValidationError("streamAgentTurn: configured bridge model conflicts with the AgentProfile harness/provider/model wire id");
365
+ }
366
+ }
367
+ function turnProvenance(startedAt, timeoutMs, profileDigest, taskDigest, materialization, executionBinding, correlation) {
368
+ const endedAt = Date.now();
369
+ return {
370
+ ...profileDigest ? { identity: {
371
+ profileDigest,
372
+ taskDigest,
373
+ ...Object.keys(correlation).length > 0 ? { correlation } : {}
374
+ } } : { taskDigest },
375
+ ...materialization ? { materialization } : {},
376
+ ...executionBinding ? { executionBindings: [executionBinding] } : {},
377
+ budget: { timeoutMs: timeoutMs ?? null },
378
+ timing: {
379
+ startedAt: new Date(startedAt).toISOString(),
380
+ endedAt: new Date(endedAt).toISOString(),
381
+ durationMs: endedAt - startedAt
382
+ }
383
+ };
384
+ }
385
+ /**
386
+ * Run ONE agent turn on any backend kind and stream its events. Yields the
387
+ * `RuntimeStreamEvent` vocabulary incrementally and always ends with a `final`
388
+ * event carrying the turn's text and usage (`metadata.tokenUsage`,
389
+ * `metadata.costUsd?`, `metadata.model?`) — on success, failure, abort, and
390
+ * timeout alike. The generator never throws; failures surface in-band as
391
+ * `backend_error` + `final` with a typed `error` detail.
392
+ *
393
+ * @stable
394
+ */
395
+ async function* streamAgentTurn(backend, input, opts = {}) {
396
+ assertTurnTimeout(opts.timeoutMs);
397
+ if (backend.kind !== "executor") throw new ValidationError("streamAgentTurn: exact execution accepts only kind 'executor'; use Runtime-owned creation");
398
+ yield* streamAgentTurnInternal(backend, input, opts);
399
+ }
400
+ function assertTurnTimeout(timeoutMs) {
401
+ if (timeoutMs === void 0) return;
402
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647) throw new ValidationError("streamAgentTurn: timeoutMs must be an integer from 1 to 2147483647");
403
+ }
404
+ function assertTurnIdentity(value, field) {
405
+ if (value !== void 0 && (typeof value !== "string" || value.trim().length === 0)) throw new ValidationError(`streamAgentTurn: ${field} must be a non-empty string`);
406
+ }
407
+ async function* streamAgentTurnInternal(backend, input, opts) {
408
+ const label = backend.kind === "chat" ? backend.backend.kind : backend.kind;
409
+ const profile = backend.kind === "executor" ? executableAgentProfileSnapshot(backend.profile, "streamAgentTurn") : void 0;
410
+ const profileDigest = profile ? authoredProfileDigest(profile) : void 0;
411
+ assertTurnIdentity(opts.callId, "callId");
412
+ assertTurnIdentity(opts.correlationId, "correlationId");
413
+ const correlation = {
414
+ ...opts.callId ? { callId: opts.callId } : {},
415
+ ...opts.correlationId ? { correlationId: opts.correlationId } : {}
416
+ };
417
+ const taskDigest = canonicalCandidateDigest(input);
418
+ const task = {
419
+ id: `turn-${crypto.randomUUID()}`,
420
+ intent: turnIntent(input),
421
+ ...profileDigest ? { metadata: { identity: {
422
+ profileDigest,
423
+ taskDigest,
424
+ ...Object.keys(correlation).length > 0 ? { correlation } : {}
425
+ } } } : {}
426
+ };
427
+ const acc = {
428
+ deltaText: "",
429
+ input: 0,
430
+ output: 0,
431
+ costUsd: 0,
432
+ estimatedCostUsd: 0,
433
+ sawEstimatedCost: false,
434
+ promptCache: {},
435
+ tokensKnown: false,
436
+ usdKnown: false,
437
+ sawLlmCall: false
438
+ };
439
+ const deadline = deriveTurnSignal(opts.signal, opts.timeoutMs ?? 0);
440
+ const startedAt = Date.now();
441
+ let session;
442
+ let executor;
443
+ let materialization;
444
+ let executionBinding;
445
+ try {
446
+ const nodeId = task.id;
447
+ const attemptId = `${nodeId}:attempt:${crypto.randomUUID()}`;
448
+ if (backend.kind === "executor") {
449
+ executor = backend.factory({
450
+ profile,
451
+ harness: null
452
+ }, {
453
+ signal: deadline.signal,
454
+ seams: {},
455
+ node: {
456
+ rootId: nodeId,
457
+ parentId: nodeId,
458
+ nodeId,
459
+ attemptId,
460
+ identity: {
461
+ profileDigest,
462
+ taskDigest,
463
+ ...Object.keys(correlation).length > 0 ? { correlation } : {}
464
+ }
465
+ }
466
+ });
467
+ assertExactExecutorDeclaration(executor, profile);
468
+ const pending = runtimeOwnedPendingExecutorMaterialization(executor);
469
+ if (pending !== void 0) {
470
+ if (pending.runtime !== executor.runtime || pending.binding.attemptId !== attemptId) throw new ValidationError("streamAgentTurn: pending executor did not bind the kernel-minted attempt");
471
+ if (authoredProfileDigest(pending.declaration.effectiveProfile) !== profileDigest) throw new ValidationError("streamAgentTurn: pending executor changed the authored AgentProfile before execution");
472
+ materialization = unknownMaterializationReceipt({
473
+ authoredProfileDigest: profileDigest,
474
+ runtime: executor.runtime,
475
+ reason: "executor-receipt-pending"
476
+ });
477
+ executionBinding = unknownExecutionBindingReceipt(materialization, attemptId, "executor-receipt-pending");
478
+ } else {
479
+ ({materialization, executionBinding} = executorEvidence(executor, profile, attemptId));
480
+ assertExactExecutorEvidence(materialization, executionBinding);
481
+ }
482
+ } else {
483
+ materialization = unknownMaterializationReceipt({
484
+ runtime: backend.kind === "box" ? "sandbox" : label,
485
+ reason: "executor-did-not-report"
486
+ });
487
+ executionBinding = unknownExecutionBindingReceipt(materialization, attemptId, "executor-did-not-report");
488
+ }
489
+ session = await startTurnSession(backend, task, input, deadline.signal, label);
490
+ yield {
491
+ type: "backend_start",
492
+ task,
493
+ session,
494
+ backend: executor?.runtime ?? label,
495
+ metadata: {
496
+ ...profileDigest ? { identity: {
497
+ profileDigest,
498
+ taskDigest,
499
+ ...Object.keys(correlation).length > 0 ? { correlation } : {}
500
+ } } : { taskDigest },
501
+ ...materialization ? { materialization } : {},
502
+ ...executionBinding ? { executionBindings: [executionBinding] } : {},
503
+ budget: { timeoutMs: opts.timeoutMs ?? null },
504
+ timing: { startedAt: new Date(startedAt).toISOString() }
505
+ },
506
+ timestamp: nowIso()
507
+ };
508
+ const inner = backend.kind === "chat" ? driveChatTurn(backend.backend, task, session, input, deadline.signal, acc) : backend.kind === "executor" ? driveExecutorTurn(executor, task, session, input, deadline.signal, acc, materializedModel(materialization, profile)) : driveBoxTurn(backend.box, turnIntent(input), deadline.signal, backend.agentRunName ?? "agent", acc, {
509
+ ...backend.options ? { options: backend.options } : {},
510
+ preserveToolParts: opts.preserveToolParts === true,
511
+ ...opts.onRawEvent ? { onRawEvent: opts.onRawEvent } : {}
512
+ });
513
+ for await (const event of inner) {
514
+ yield event;
515
+ throwIfAborted(deadline.signal);
516
+ }
517
+ if (backend.kind === "executor") {
518
+ ({materialization, executionBinding} = executorEvidence(executor, profile, attemptId));
519
+ assertExactExecutorEvidence(materialization, executionBinding);
520
+ }
521
+ yield buildFinalEvent(task, session, acc, {
522
+ status: "completed",
523
+ reason: "turn completed"
524
+ }, turnProvenance(startedAt, opts.timeoutMs, profileDigest, taskDigest, materialization, executionBinding, correlation));
525
+ } catch (err) {
526
+ const callerAborted = opts.signal?.aborted === true;
527
+ const status = callerAborted ? "aborted" : "failed";
528
+ const message = err instanceof Error ? err.message : String(err);
529
+ const error = err instanceof BackendTransportError ? {
530
+ kind: "transport",
531
+ message,
532
+ status: err.status,
533
+ body: err.body
534
+ } : {
535
+ kind: "backend",
536
+ message
537
+ };
538
+ yield {
539
+ type: "backend_error",
540
+ task,
541
+ ...session ? { session } : {},
542
+ backend: label,
543
+ message,
544
+ recoverable: !callerAborted,
545
+ error,
546
+ timestamp: nowIso()
547
+ };
548
+ yield buildFinalEvent(task, session, acc, {
549
+ status,
550
+ reason: message,
551
+ error
552
+ }, turnProvenance(startedAt, opts.timeoutMs, profileDigest, taskDigest, materialization, executionBinding, correlation));
553
+ } finally {
554
+ await executor?.teardown("brutalKill").catch(() => void 0);
555
+ deadline.dispose();
556
+ }
557
+ }
558
+ function assertExactExecutorEvidence(materialization, executionBinding) {
559
+ if (materialization.status !== "known" || executionBinding.status !== "known") throw new ValidationError("streamAgentTurn: exact profile execution requires terminally validated Runtime materialization and execution binding evidence");
560
+ if (materialization.effectiveProfileDigest !== materialization.authoredProfileDigest) throw new ValidationError("streamAgentTurn: executor changed the authored AgentProfile; exact turn execution refuses profile overlays");
561
+ }
562
+ /**
563
+ * Drain a `streamAgentTurn` stream (or any `RuntimeStreamEvent` stream that
564
+ * honors its terminal contract) into the turn summary plus the full event
565
+ * list. Fail-loud: throws when the stream ends without a terminal `final`
566
+ * event — a stream that violates the contract must not read as an empty turn.
567
+ *
568
+ * @stable
569
+ */
570
+ async function collectAgentTurn(stream) {
571
+ const events = [];
572
+ for await (const event of stream) events.push(event);
573
+ const final = events.at(-1);
574
+ if (final?.type !== "final") throw new Error(`collectAgentTurn: stream ended without a terminal 'final' event (last: ${final ? final.type : "none"})`);
575
+ const metadata = final.metadata ?? {};
576
+ const resultMetadata = metadata.result && typeof metadata.result === "object" ? metadata.result : void 0;
577
+ const tokenUsage = metadata.tokenUsage && typeof metadata.tokenUsage === "object" ? metadata.tokenUsage : {};
578
+ const usage = {
579
+ input: finiteNumber(tokenUsage.input) ?? 0,
580
+ output: finiteNumber(tokenUsage.output) ?? 0,
581
+ ...metadata.tokensKnown === false ? { tokensKnown: false } : {}
582
+ };
583
+ const costUsd = finiteNumber(metadata.costUsd);
584
+ if (costUsd !== void 0) usage.costUsd = costUsd;
585
+ if (metadata.usdKnown === false) usage.usdKnown = false;
586
+ const estimatedCostUsd = finiteNumber(metadata.estimatedCostUsd);
587
+ if (estimatedCostUsd !== void 0) usage.estimatedCostUsd = estimatedCostUsd;
588
+ if (metadata.promptCache && typeof metadata.promptCache === "object") usage.promptCache = metadata.promptCache;
589
+ const reasoningTokens = finiteNumber(metadata.reasoningTokens);
590
+ if (reasoningTokens !== void 0) usage.reasoningTokens = reasoningTokens;
591
+ if (typeof metadata.model === "string" && metadata.model.length > 0) usage.model = metadata.model;
592
+ const transportAttempts = finiteNumber(metadata.transportAttempts);
593
+ const toolCalls = events.filter((event) => event.type === "tool_call").map((event) => ({
594
+ ...event.toolCallId ? { id: event.toolCallId } : {},
595
+ name: event.toolName,
596
+ arguments: typeof event.args === "string" ? event.args : JSON.stringify(event.args ?? {})
597
+ }));
598
+ return {
599
+ finalText: final.text ?? "",
600
+ ...resultMetadata && "output" in resultMetadata ? { output: resultMetadata.output } : {},
601
+ usage,
602
+ ...transportAttempts !== void 0 ? { transportAttempts } : {},
603
+ toolCalls,
604
+ events,
605
+ status: final.status,
606
+ ...final.error ? { error: final.error } : {}
607
+ };
608
+ }
609
+ /** Start the backend's session when it owns one (`chat` kind); mint a local
610
+ * correlation session otherwise. Box/executor turns carry no server session
611
+ * here — resume lives in `openSandboxRun`/`SandboxLineage`, not this primitive. */
612
+ async function startTurnSession(backend, task, input, signal, label) {
613
+ if (backend.kind === "chat" && backend.backend.start) return backend.backend.start(turnBackendInput(task, input), {
614
+ task,
615
+ knowledge: emptyReadiness(task),
616
+ signal
617
+ });
618
+ return newRuntimeSession(label);
619
+ }
620
+ /**
621
+ * One turn over a box: `box.streamPrompt` projected through the existing
622
+ * `mapSandboxEvent` (text/reasoning deltas +
623
+ * cost-bearing `llm_call`s), plus the opt-in `mapSandboxToolEvent` tool-part
624
+ * projection. Usage accumulates off the mapped `llm_call` events — the same
625
+ * fold `sumSandboxUsage` applies. Final text prefers the terminal
626
+ * `result`/`done`/`final` payload over concatenated deltas, because the
627
+ * sandbox `message.part.updated` fallback may carry running accumulations.
628
+ */
629
+ async function* driveBoxTurn(box, prompt, signal, agentRunName, acc, cfg) {
630
+ const callOptions = {
631
+ ...cfg.options ?? {},
632
+ signal
633
+ };
634
+ const stream = box.streamPrompt(prompt, callOptions);
635
+ const toolParts = cfg.preserveToolParts ? createSandboxToolPartState() : void 0;
636
+ for await (const event of stream) {
637
+ if (cfg.onRawEvent) await cfg.onRawEvent(event);
638
+ const terminalText = terminalTextFromSandboxEvent(event);
639
+ if (terminalText !== void 0) acc.terminalText = terminalText;
640
+ if (toolParts) for (const toolEvent of mapSandboxToolEvent(event, toolParts)) yield toolEvent;
641
+ const mapped = mapSandboxEvent(event, { agentRunName });
642
+ if (!mapped) continue;
643
+ foldEvent(mapped, acc, agentRunName);
644
+ yield mapped;
645
+ }
646
+ }
647
+ /** One turn over an in-process backend: its own `stream()` surface, projected
648
+ * through the same `normalizeBackendStreamEvent` the task lifecycle applies. */
649
+ async function* driveChatTurn(backend, task, session, input, signal, acc) {
650
+ const backendInput = turnBackendInput(task, input);
651
+ const context = {
652
+ task,
653
+ knowledge: emptyReadiness(task),
654
+ session,
655
+ signal
656
+ };
657
+ for await (const raw of backend.stream(backendInput, context)) {
658
+ const event = normalizeBackendStreamEvent(raw, task, session);
659
+ foldEvent(event, acc);
660
+ yield event;
661
+ }
662
+ }
663
+ async function* driveExecutorTurn(executor, task, session, input, signal, acc, declaredModel) {
664
+ const taskValue = typeof input === "string" ? input : { messages: input.messages };
665
+ const run = executor.execute(taskValue, signal);
666
+ let result;
667
+ if (isAsyncIterable(run)) {
668
+ for await (const _usage of run) throwIfAborted(signal);
669
+ result = executor.resultArtifact();
670
+ } else result = await run;
671
+ acc.result = result;
672
+ acc.terminalText = executorResultText(result.out);
673
+ acc.input = result.spent.tokens.input;
674
+ acc.output = result.spent.tokens.output;
675
+ acc.costUsd = result.spent.usd;
676
+ const estimatedCostUsd = executorResultEstimatedCost(result.out);
677
+ if (estimatedCostUsd !== void 0) {
678
+ acc.estimatedCostUsd = estimatedCostUsd;
679
+ acc.sawEstimatedCost = true;
680
+ }
681
+ Object.assign(acc.promptCache, executorResultPromptCache(result.out));
682
+ acc.reasoningTokens = executorResultReasoningTokens(result.out);
683
+ acc.tokensKnown = result.spent.tokensKnown !== false;
684
+ acc.usdKnown = result.spent.usdKnown !== false;
685
+ acc.sawLlmCall = true;
686
+ const model = executorResultModel(result.out);
687
+ if (model) acc.model = model;
688
+ acc.stopReason = executorResultStopReason(result.out);
689
+ acc.transportAttempts = executorResultTransportAttempts(result.out);
690
+ const latencyMs = result.spent.ms;
691
+ yield {
692
+ type: "llm_call",
693
+ task,
694
+ session,
695
+ model: model ?? declaredModel ?? executor.runtime,
696
+ ...acc.tokensKnown ? {
697
+ tokensIn: acc.input,
698
+ tokensOut: acc.output
699
+ } : {},
700
+ ...acc.usdKnown ? { costUsd: acc.costUsd } : {},
701
+ ...acc.tokensKnown ? {} : { tokensKnown: false },
702
+ ...acc.usdKnown ? {} : { usdKnown: false },
703
+ ...acc.sawEstimatedCost ? { estimatedCostUsd: acc.estimatedCostUsd } : {},
704
+ ...Object.keys(acc.promptCache).length > 0 ? { promptCache: acc.promptCache } : {},
705
+ latencyMs,
706
+ timestamp: nowIso()
707
+ };
708
+ for (const call of executorResultToolCalls(result.out)) yield {
709
+ type: "tool_call",
710
+ task,
711
+ session,
712
+ toolName: call.name,
713
+ ...call.id ? { toolCallId: call.id } : {},
714
+ args: call.arguments,
715
+ timestamp: nowIso()
716
+ };
717
+ yield {
718
+ type: "artifact",
719
+ task,
720
+ session,
721
+ artifactId: result.outRef,
722
+ name: "agent-turn-result",
723
+ metadata: {
724
+ spend: result.spent,
725
+ ...result.verdict ? { verdict: result.verdict } : {}
726
+ },
727
+ timestamp: nowIso()
728
+ };
729
+ }
730
+ function isAsyncIterable(value) {
731
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value;
732
+ }
733
+ function executorResultText(value) {
734
+ if (typeof value === "string") return value;
735
+ if (!value || typeof value !== "object") return "";
736
+ const content = value.content;
737
+ return typeof content === "string" ? content : "";
738
+ }
739
+ function executorResultModel(value) {
740
+ if (!value || typeof value !== "object") return void 0;
741
+ const model = value.model;
742
+ return typeof model === "string" && model.length > 0 ? model : void 0;
743
+ }
744
+ function executorResultStopReason(value) {
745
+ if (!value || typeof value !== "object") return void 0;
746
+ const reason = value.finishReason;
747
+ return typeof reason === "string" && reason.length > 0 ? reason : void 0;
748
+ }
749
+ function executorResultTransportAttempts(value) {
750
+ if (!value || typeof value !== "object") return void 0;
751
+ const attempts = value.transportAttempts;
752
+ return typeof attempts === "number" && Number.isSafeInteger(attempts) && attempts > 0 ? attempts : void 0;
753
+ }
754
+ function executorResultEstimatedCost(value) {
755
+ if (!value || typeof value !== "object") return void 0;
756
+ return finiteNumber(value.estimatedCostUsd);
757
+ }
758
+ function executorResultPromptCache(value) {
759
+ if (!value || typeof value !== "object") return {};
760
+ const raw = value.promptCache;
761
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
762
+ const cache = {};
763
+ for (const [key, entry] of Object.entries(raw)) if (typeof entry === "number" && Number.isFinite(entry) || typeof entry === "string") cache[key] = entry;
764
+ return cache;
765
+ }
766
+ function executorResultReasoningTokens(value) {
767
+ if (!value || typeof value !== "object") return void 0;
768
+ const count = value.reasoningTokens;
769
+ return Number.isSafeInteger(count) && count >= 0 ? count : void 0;
770
+ }
771
+ function executorResultToolCalls(value) {
772
+ if (!value || typeof value !== "object") return [];
773
+ const raw = value.toolCalls;
774
+ if (!Array.isArray(raw)) return [];
775
+ return raw.flatMap((entry) => {
776
+ if (!entry || typeof entry !== "object") return [];
777
+ const call = entry;
778
+ if (typeof call.name !== "string") return [];
779
+ return [{
780
+ ...typeof call.id === "string" ? { id: call.id } : {},
781
+ name: call.name,
782
+ arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments ?? {})
783
+ }];
784
+ });
785
+ }
786
+ /** Fold one normalized event into the turn accumulator (text + usage).
787
+ * `fallbackModelLabel` — a mapper-stamped run label to exclude from
788
+ * `usage.model` (it is not a backend-reported model). */
789
+ function foldEvent(event, acc, fallbackModelLabel) {
790
+ if (event.type === "text_delta") {
791
+ acc.deltaText += event.text;
792
+ return;
793
+ }
794
+ if (event.type === "llm_call") {
795
+ const tokensReported = event.tokensKnown !== false && event.tokensIn !== void 0 && event.tokensOut !== void 0;
796
+ const usdReported = event.usdKnown !== false && event.costUsd !== void 0;
797
+ if (!acc.sawLlmCall) {
798
+ acc.tokensKnown = tokensReported;
799
+ acc.usdKnown = usdReported;
800
+ acc.sawLlmCall = true;
801
+ } else {
802
+ acc.tokensKnown &&= tokensReported;
803
+ acc.usdKnown &&= usdReported;
804
+ }
805
+ acc.input += event.tokensIn ?? 0;
806
+ acc.output += event.tokensOut ?? 0;
807
+ acc.costUsd += event.costUsd ?? 0;
808
+ if (event.estimatedCostUsd !== void 0) {
809
+ acc.estimatedCostUsd += event.estimatedCostUsd;
810
+ acc.sawEstimatedCost = true;
811
+ }
812
+ if (event.promptCache) Object.assign(acc.promptCache, event.promptCache);
813
+ if (event.model && event.model !== fallbackModelLabel) acc.model = event.model;
814
+ }
815
+ }
816
+ /** Read the final text off a terminal sandbox event, when present. */
817
+ function terminalTextFromSandboxEvent(event) {
818
+ if (!event || typeof event !== "object") return void 0;
819
+ const type = String(event.type ?? "");
820
+ if (type !== "result" && type !== "done" && type !== "final") return void 0;
821
+ const data = event.data && typeof event.data === "object" ? event.data : {};
822
+ for (const key of [
823
+ "finalText",
824
+ "text",
825
+ "response",
826
+ "content"
827
+ ]) {
828
+ const value = data[key];
829
+ if (typeof value === "string") return value;
830
+ }
831
+ }
832
+ function buildFinalEvent(task, session, acc, outcome, provenance) {
833
+ const finalText = acc.terminalText ?? acc.deltaText;
834
+ return {
835
+ type: "final",
836
+ task,
837
+ ...session ? { session } : {},
838
+ status: outcome.status,
839
+ reason: outcome.status === "completed" ? acc.stopReason ?? outcome.reason : outcome.reason,
840
+ ...finalText ? { text: finalText } : {},
841
+ metadata: {
842
+ tokenUsage: {
843
+ input: acc.input,
844
+ output: acc.output
845
+ },
846
+ ...acc.tokensKnown ? {} : { tokensKnown: false },
847
+ ...acc.usdKnown ? { costUsd: acc.costUsd } : { usdKnown: false },
848
+ ...acc.sawEstimatedCost ? { estimatedCostUsd: acc.estimatedCostUsd } : {},
849
+ ...Object.keys(acc.promptCache).length > 0 ? { promptCache: acc.promptCache } : {},
850
+ ...acc.reasoningTokens !== void 0 ? { reasoningTokens: acc.reasoningTokens } : {},
851
+ ...acc.model ? { model: acc.model } : {},
852
+ ...acc.stopReason ? { stopReason: acc.stopReason } : {},
853
+ ...acc.transportAttempts !== void 0 ? { transportAttempts: acc.transportAttempts } : {},
854
+ ...acc.result ? { result: {
855
+ outRef: acc.result.outRef,
856
+ output: acc.result.out,
857
+ ...acc.result.verdict ? { verdict: acc.result.verdict } : {},
858
+ spent: acc.result.spent
859
+ } } : {},
860
+ ...provenance
861
+ },
862
+ ...outcome.error ? { error: outcome.error } : {},
863
+ timestamp: nowIso()
864
+ };
865
+ }
866
+ function materializedModel(receipt, profile) {
867
+ if (receipt?.status === "known" && receipt.model.status === "known") return receipt.model.id;
868
+ const fallback = profile.model?.default;
869
+ return typeof fallback === "string" && fallback.length > 0 ? fallback : void 0;
870
+ }
871
+ /** Minimal ready-by-construction readiness report for a requirement-free turn. */
872
+ function emptyReadiness(task) {
873
+ return scoreKnowledgeReadiness({
874
+ taskId: task.id,
875
+ requirements: []
876
+ });
877
+ }
878
+ function finiteNumber(value) {
879
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
880
+ }
881
+ function throwIfAborted(signal) {
882
+ if (!signal.aborted) return;
883
+ throw signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason));
884
+ }
885
+ /**
886
+ * Derive the turn's effective abort signal: fires when EITHER the caller's
887
+ * signal aborts OR the `timeoutMs` deadline elapses. `dispose()` clears the
888
+ * timer so a finished turn never leaks a pending timeout. `timeoutMs <= 0`
889
+ * disables the deadline. Node-portable (no `AbortSignal.any`, which needs
890
+ * >=20.3 — the package floor is >=20).
891
+ */
892
+ function deriveTurnSignal(callerSignal, timeoutMs) {
893
+ const controller = new AbortController();
894
+ const timer = timeoutMs > 0 ? setTimeout(() => controller.abort(/* @__PURE__ */ new Error(`agent turn timed out after ${timeoutMs}ms`)), timeoutMs) : void 0;
895
+ if (timer && typeof timer.unref === "function") timer.unref();
896
+ const onCallerAbort = () => controller.abort(callerSignal?.reason ?? /* @__PURE__ */ new Error("agent turn aborted"));
897
+ if (callerSignal) if (callerSignal.aborted) onCallerAbort();
898
+ else callerSignal.addEventListener("abort", onCallerAbort, { once: true });
899
+ return {
900
+ signal: controller.signal,
901
+ dispose: () => {
902
+ if (timer) clearTimeout(timer);
903
+ callerSignal?.removeEventListener("abort", onCallerAbort);
904
+ }
905
+ };
906
+ }
907
+ //#endregion
908
+ 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 };
909
+
910
+ //# sourceMappingURL=stream-agent-turn-rYgaOLO0.js.map