@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.
- package/README.md +70 -20
- package/dist/{activation-DhWJ3p8N.js → activation-BCzMOTaV.js} +3 -3
- package/dist/{activation-DhWJ3p8N.js.map → activation-BCzMOTaV.js.map} +1 -1
- package/dist/agent.d.ts +2 -3
- package/dist/agent.js +4 -5
- package/dist/agent.js.map +1 -1
- package/dist/{analyst-loop-DvSciOfB.js → analyst-loop-BE8cDs5Q.js} +2 -2
- package/dist/{analyst-loop-DvSciOfB.js.map → analyst-loop-BE8cDs5Q.js.map} +1 -1
- package/dist/analyst-loop.d.ts +1 -1
- package/dist/analyst-loop.js +1 -1
- package/dist/authoring-Dv3t6SXe.js +163 -0
- package/dist/authoring-Dv3t6SXe.js.map +1 -0
- package/dist/candidate-execution/index.js +4 -4
- package/dist/{candidate-execution-BFpq-Xi6.js → candidate-execution-BNxKr-Bu.js} +5 -5
- package/dist/{candidate-execution-BFpq-Xi6.js.map → candidate-execution-BNxKr-Bu.js.map} +1 -1
- package/dist/{conversation-BpLQZGPH.js → conversation-DNtxaJ1Z.js} +113 -31
- package/dist/conversation-DNtxaJ1Z.js.map +1 -0
- package/dist/conversation.d.ts +2 -2
- package/dist/conversation.js +2 -2
- package/dist/environment-provider-CxvSd1W6.d.ts +86 -0
- package/dist/{environment-provider-DqFS6FSZ.js → environment-provider-Dyg8DtLK.js} +8 -4
- package/dist/environment-provider-Dyg8DtLK.js.map +1 -0
- package/dist/environment-provider.d.ts +1 -1
- package/dist/environment-provider.js +1 -1
- package/dist/graph-xWdv53Le.js +471 -0
- package/dist/graph-xWdv53Le.js.map +1 -0
- package/dist/{improvement-cycle-IJgCbKWQ.js → improvement-cycle-Csp38cWg.js} +133 -224
- package/dist/improvement-cycle-Csp38cWg.js.map +1 -0
- package/dist/{index-BhZhQw77.d.ts → index-CoO7atyo.d.ts} +556 -1278
- package/dist/{index-BhuzfG2r.d.ts → index-DwGtu9nc.d.ts} +7 -9
- package/dist/{index-Efjb3nrQ.d.ts → index-qYHpsmG2.d.ts} +36 -18
- package/dist/index.d.ts +353 -11
- package/dist/index.js +111 -354
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +6 -6
- package/dist/intelligence.js +9 -8
- package/dist/intelligence.js.map +1 -1
- package/dist/kernel.d.ts +7 -5
- package/dist/kernel.js +13 -9
- package/dist/{knowledge-DF63xPr4.js → knowledge-DPEu4f-0.js} +19 -17
- package/dist/knowledge-DPEu4f-0.js.map +1 -0
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{loop-runner-bin-Ckp_9tmD.d.ts → loop-runner-bin-BwgZ8m_7.d.ts} +6 -3
- package/dist/{loop-runner-bin-CWqOpCEw.js → loop-runner-bin-dg6li2-b.js} +5 -27
- package/dist/loop-runner-bin-dg6li2-b.js.map +1 -0
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/materialization-COJ1UYQ-.js +272 -0
- package/dist/materialization-COJ1UYQ-.js.map +1 -0
- package/dist/mcp/bin.js +39 -47
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +24 -26
- package/dist/mcp/index.js +67 -84
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/memory-bin.js +1 -1
- package/dist/{memory-server-DL6cE2Ag.js → memory-server-eD2baiRO.js} +3 -3
- package/dist/{memory-server-DL6cE2Ag.js.map → memory-server-eD2baiRO.js.map} +1 -1
- package/dist/model-policy-CqziaqS1.js +232 -0
- package/dist/model-policy-CqziaqS1.js.map +1 -0
- package/dist/{openai-tools-B68JaOCx.d.ts → openai-tools-D3oMyVGl.d.ts} +2 -2
- package/dist/{openai-tools-D3XfrrQ6.js → openai-tools-zRphjXS4.js} +2 -2
- package/dist/openai-tools-zRphjXS4.js.map +1 -0
- package/dist/{prepare--8EvLqCr.js → prepare-DYWjVcPx.js} +169 -153
- package/dist/prepare-DYWjVcPx.js.map +1 -0
- package/dist/primeintellect/index.d.ts +7 -6
- package/dist/primeintellect/index.js +9 -11
- package/dist/primeintellect/index.js.map +1 -1
- package/dist/profiles.d.ts +21 -174
- package/dist/profiles.js +67 -276
- package/dist/profiles.js.map +1 -1
- package/dist/{protected-model-port-CXVfOUu_.js → protected-model-port-48ALLGxT.js} +2 -2
- package/dist/{protected-model-port-CXVfOUu_.js.map → protected-model-port-48ALLGxT.js.map} +1 -1
- package/dist/{redact-PQzmE1Jn.d.ts → redact-9_Gf-8m3.d.ts} +58 -69
- package/dist/{researcher-CoVqNhfI.js → researcher-Skz5-Uc8.js} +50 -26
- package/dist/researcher-Skz5-Uc8.js.map +1 -0
- package/dist/{run-layout-C2FGmZ3v.js → run-layout-CeJsEAom.js} +2 -2
- package/dist/{run-layout-C2FGmZ3v.js.map → run-layout-CeJsEAom.js.map} +1 -1
- package/dist/runtime-D-QfLbSd.d.ts +893 -0
- package/dist/{runtime-5uDVVfER.js → runtime-cOzDOOHr.js} +315 -1191
- package/dist/runtime-cOzDOOHr.js.map +1 -0
- package/dist/{sandbox-events-Yhd1GYWl.js → sandbox-events-CRDwc5WN.js} +42 -5
- package/dist/sandbox-events-CRDwc5WN.js.map +1 -0
- package/dist/snapshot-CXiiuHhL.js +21 -0
- package/dist/snapshot-CXiiuHhL.js.map +1 -0
- package/dist/{spawn-journal-DsZKDqeh.js → spawn-journal-saHQzqYi.js} +15 -21
- package/dist/spawn-journal-saHQzqYi.js.map +1 -0
- package/dist/stream-agent-turn-C852AgMT.d.ts +128 -0
- package/dist/stream-agent-turn-rYgaOLO0.js +910 -0
- package/dist/stream-agent-turn-rYgaOLO0.js.map +1 -0
- package/dist/{structural-rollout-zY0oqQzO.js → structural-rollout-3uxVGcg2.js} +459 -235
- package/dist/structural-rollout-3uxVGcg2.js.map +1 -0
- package/dist/{supervise-CsTKbH9R.js → supervise-DHYX8gO2.js} +867 -4788
- package/dist/supervise-DHYX8gO2.js.map +1 -0
- package/dist/{supervisor-DpjO0Gmy.js → supervisor-CV6Jh28D.js} +7439 -2897
- package/dist/supervisor-CV6Jh28D.js.map +1 -0
- package/dist/testing.d.ts +3 -1
- package/dist/testing.js +271 -221
- package/dist/testing.js.map +1 -1
- package/dist/{tool-server-RcWgLIsL.js → tool-server-Gs3VvfSK.js} +22 -9
- package/dist/tool-server-Gs3VvfSK.js.map +1 -0
- package/dist/{top-app-5unxqovu.js → top-app-G4M18b6i.js} +3 -3
- package/dist/{top-app-5unxqovu.js.map → top-app-G4M18b6i.js.map} +1 -1
- package/dist/tui/bin.js +1 -1
- package/dist/tui/index.js +1 -1
- package/dist/{environment-provider-CUFsyymu.d.ts → types-C6Q-J0Dt.d.ts} +51 -114
- package/dist/{types-DnNGJ5Gz.d.ts → types-ebIY0dMG.d.ts} +556 -30
- package/dist/{util-MVgdwuIS.js → util-Bw6srryQ.js} +3 -2
- package/dist/{util-MVgdwuIS.js.map → util-Bw6srryQ.js.map} +1 -1
- package/dist/{workspace-archive-BQxvkypI.js → workspace-archive-aOfJ47ms.js} +3 -3
- package/dist/{workspace-archive-BQxvkypI.js.map → workspace-archive-aOfJ47ms.js.map} +1 -1
- package/package.json +13 -15
- package/skills/agent-graphs/IMPROVE.md +3 -3
- package/skills/agent-graphs/SKILL.md +4 -5
- package/skills/agent-graphs/cases/review-pipeline.json +1 -2
- package/skills/agent-graphs/cases/unmeasured-harness.json +2 -4
- package/dist/backends-CiOCyRHb.js +0 -743
- package/dist/backends-CiOCyRHb.js.map +0 -1
- package/dist/conversation-BpLQZGPH.js.map +0 -1
- package/dist/environment-provider-DqFS6FSZ.js.map +0 -1
- package/dist/improvement-cycle-IJgCbKWQ.js.map +0 -1
- package/dist/index-DLM0W1h1.d.ts +0 -545
- package/dist/knowledge-DF63xPr4.js.map +0 -1
- package/dist/local-harness-BIajef4A.d.ts +0 -465
- package/dist/loop-runner-bin-CWqOpCEw.js.map +0 -1
- package/dist/model-resolution-Btd9iIKV.js +0 -98
- package/dist/model-resolution-Btd9iIKV.js.map +0 -1
- package/dist/openai-tools-D3XfrrQ6.js.map +0 -1
- package/dist/prepare--8EvLqCr.js.map +0 -1
- package/dist/researcher-CoVqNhfI.js.map +0 -1
- package/dist/runtime-5uDVVfER.js.map +0 -1
- package/dist/sandbox-events-Yhd1GYWl.js.map +0 -1
- package/dist/spawn-journal-DsZKDqeh.js.map +0 -1
- package/dist/structural-rollout-zY0oqQzO.js.map +0 -1
- package/dist/supervise-CsTKbH9R.js.map +0 -1
- package/dist/supervisor-DpjO0Gmy.js.map +0 -1
- package/dist/tool-server-RcWgLIsL.js.map +0 -1
- package/dist/types-C9j4qg6l.d.ts +0 -500
- 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
|