@tangle-network/agent-runtime 0.93.2 → 0.94.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 +4 -3
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +5 -6
- package/dist/agent.js.map +1 -1
- package/dist/{agentic-generator-B8oeE2Yv.d.ts → agentic-generator-D9ZmAuiL.d.ts} +1 -1
- package/dist/analyst-loop.d.ts +1 -1
- package/dist/candidate-execution/index.d.ts +10 -3
- package/dist/candidate-execution/index.js +9 -3
- package/dist/{chunk-6K5CI33W.js → chunk-D336OZHK.js} +1232 -19
- package/dist/chunk-D336OZHK.js.map +1 -0
- package/dist/{chunk-TU73X3I6.js → chunk-HPCJBBLS.js} +2 -2
- package/dist/chunk-ISTDY47H.js +849 -0
- package/dist/chunk-ISTDY47H.js.map +1 -0
- package/dist/{chunk-RTVVH2KL.js → chunk-JEP2WM24.js} +2 -2
- package/dist/{chunk-Z7K5BPKZ.js → chunk-K7OPNHU4.js} +2 -2
- package/dist/{chunk-OX3QRJOA.js → chunk-KHLE3C5G.js} +281 -251
- package/dist/chunk-KHLE3C5G.js.map +1 -0
- package/dist/{chunk-XDSWWUKE.js → chunk-MZH4HORF.js} +6 -29
- package/dist/chunk-MZH4HORF.js.map +1 -0
- package/dist/chunk-PJNGPWTA.js +634 -0
- package/dist/chunk-PJNGPWTA.js.map +1 -0
- package/dist/{chunk-X5KJKS33.js → chunk-RTSZNYXM.js} +3 -3
- package/dist/{chunk-WGR6QJ5H.js → chunk-SJHFJOCY.js} +8 -161
- package/dist/chunk-SJHFJOCY.js.map +1 -0
- package/dist/{chunk-2BQMAZAB.js → chunk-URMJTSXB.js} +1378 -28
- package/dist/chunk-URMJTSXB.js.map +1 -0
- package/dist/{chunk-3D2RHC4K.js → chunk-WIPGQ4GT.js} +1 -1
- package/dist/{chunk-3D2RHC4K.js.map → chunk-WIPGQ4GT.js.map} +1 -1
- package/dist/{chunk-R5GWDTM3.js → chunk-ZEYAT33L.js} +2 -2
- package/dist/{completion-gate-BLaiN0-X.d.ts → completion-gate-BAj3dXeW.d.ts} +14 -2
- package/dist/{coordination-DxJ83oZA.d.ts → coordination-B3ZuApR_.d.ts} +7 -5
- package/dist/environment-provider.d.ts +2 -2
- package/dist/{improve-DDhQaaJT.d.ts → improve-r8SvIxYv.d.ts} +13 -16
- package/dist/index.d.ts +39 -110
- package/dist/index.js +92 -36
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +24 -7
- package/dist/intelligence.js +114 -20
- package/dist/intelligence.js.map +1 -1
- package/dist/knowledge.d.ts +7 -7
- package/dist/knowledge.js +5 -5
- package/dist/lifecycle.d.ts +3 -3
- package/dist/lifecycle.js +2 -2
- package/dist/{local-harness-dcD5WTTr.d.ts → local-harness-B7bNuNJw.d.ts} +49 -1
- package/dist/{loop-runner-bin-kKUNGLyV.d.ts → loop-runner-bin-D4bMIxmB.d.ts} +2 -2
- package/dist/loop-runner-bin.d.ts +6 -6
- package/dist/loop-runner-bin.js +6 -7
- package/dist/loops.d.ts +457 -18
- package/dist/loops.js +39 -41
- package/dist/mcp/bin.js +5 -5
- package/dist/mcp/index.d.ts +10 -10
- package/dist/mcp/index.js +9 -8
- package/dist/{mcp-serve-verifier-Bg4C3p5S.d.ts → mcp-serve-verifier-BuXjaZDE.d.ts} +1 -1
- package/dist/{openai-tools-E3woykz9.d.ts → openai-tools-D0ZSRCC6.d.ts} +1 -1
- package/dist/{prepare-DiVGKcwS.d.ts → prepare-BKxAiUcH.d.ts} +1 -1
- package/dist/profiles.d.ts +1 -1
- package/dist/{sanitize-C9go6tXj.d.ts → sanitize-C2jicjNf.d.ts} +1 -1
- package/dist/{supervise-T2pazU3G.d.ts → supervise-DTNcLDl2.d.ts} +4 -4
- package/dist/{types-DAdIm4AC.d.ts → types-Bb-CqxJH.d.ts} +5 -2
- package/dist/{types-B00NtbCs.d.ts → types-DWA64rbJ.d.ts} +1 -1
- package/dist/{worktree-fanout-BUb2Ag02.d.ts → worktree-fanout-IMKqdV_E.d.ts} +244 -244
- package/package.json +4 -4
- package/skills/build-with-agent-runtime/SKILL.md +1 -1
- package/dist/chunk-2BQMAZAB.js.map +0 -1
- package/dist/chunk-6K5CI33W.js.map +0 -1
- package/dist/chunk-FDJ7AHXG.js +0 -1229
- package/dist/chunk-FDJ7AHXG.js.map +0 -1
- package/dist/chunk-J2K6WIG6.js +0 -2172
- package/dist/chunk-J2K6WIG6.js.map +0 -1
- package/dist/chunk-OX3QRJOA.js.map +0 -1
- package/dist/chunk-PX6SXX3M.js +0 -146
- package/dist/chunk-PX6SXX3M.js.map +0 -1
- package/dist/chunk-WGR6QJ5H.js.map +0 -1
- package/dist/chunk-XDSWWUKE.js.map +0 -1
- package/dist/structural-rollout-DHGDbhvR.d.ts +0 -446
- /package/dist/{chunk-TU73X3I6.js.map → chunk-HPCJBBLS.js.map} +0 -0
- /package/dist/{chunk-RTVVH2KL.js.map → chunk-JEP2WM24.js.map} +0 -0
- /package/dist/{chunk-Z7K5BPKZ.js.map → chunk-K7OPNHU4.js.map} +0 -0
- /package/dist/{chunk-X5KJKS33.js.map → chunk-RTSZNYXM.js.map} +0 -0
- /package/dist/{chunk-R5GWDTM3.js.map → chunk-ZEYAT33L.js.map} +0 -0
package/dist/chunk-J2K6WIG6.js
DELETED
|
@@ -1,2172 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
contentAddress
|
|
3
|
-
} from "./chunk-3D2RHC4K.js";
|
|
4
|
-
import {
|
|
5
|
-
addTokenUsage,
|
|
6
|
-
zeroTokenUsage
|
|
7
|
-
} from "./chunk-3MDZX7YU.js";
|
|
8
|
-
import {
|
|
9
|
-
ConfigError,
|
|
10
|
-
RuntimeRunStateError,
|
|
11
|
-
ValidationError
|
|
12
|
-
} from "./chunk-YEJR7IXO.js";
|
|
13
|
-
|
|
14
|
-
// src/runtime/supervise/model-policy.ts
|
|
15
|
-
function assertModelAllowed(model, allowed) {
|
|
16
|
-
if (!allowed || model === void 0) return;
|
|
17
|
-
if (!allowed.includes(model)) {
|
|
18
|
-
throw new ConfigError(
|
|
19
|
-
`model ${JSON.stringify(model)} is not in the allowed set ${JSON.stringify([...allowed])}`
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// src/runtime/router-client.ts
|
|
25
|
-
import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
|
|
26
|
-
|
|
27
|
-
// src/runtime/tool-loop.ts
|
|
28
|
-
function estimateConversationTokens(messages) {
|
|
29
|
-
let chars = 0;
|
|
30
|
-
for (const m of messages) {
|
|
31
|
-
const content = m.content;
|
|
32
|
-
chars += typeof content === "string" ? content.length : safeJsonLength(content);
|
|
33
|
-
const calls = m.tool_calls;
|
|
34
|
-
if (calls) for (const c of calls) chars += c.function?.arguments?.length ?? 0;
|
|
35
|
-
}
|
|
36
|
-
return Math.ceil(chars / 4);
|
|
37
|
-
}
|
|
38
|
-
function safeJsonLength(value) {
|
|
39
|
-
if (value === void 0 || value === null) return 0;
|
|
40
|
-
try {
|
|
41
|
-
return JSON.stringify(value)?.length ?? 0;
|
|
42
|
-
} catch {
|
|
43
|
-
return String(value).length;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
async function maybeCompact(messages, c, turn) {
|
|
47
|
-
const head = c.preserveHead !== void 0 && c.preserveHead > 0 ? c.preserveHead : 2;
|
|
48
|
-
if (messages.length <= head + 1) return false;
|
|
49
|
-
const estimate = c.estimateTokens ?? estimateConversationTokens;
|
|
50
|
-
const before = estimate(messages);
|
|
51
|
-
if (before <= c.thresholdTokens) return false;
|
|
52
|
-
const digest = await c.distill([...messages]);
|
|
53
|
-
messages.splice(head, messages.length - head, {
|
|
54
|
-
role: "user",
|
|
55
|
-
content: `[earlier work compacted to save context \u2014 progress so far]
|
|
56
|
-
${digest}`
|
|
57
|
-
});
|
|
58
|
-
c.onCompact?.({ turn, beforeTokens: before, afterTokens: estimate(messages) });
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
async function runBrainLoop(opts) {
|
|
62
|
-
const maxTurns = opts.maxTurns ?? 4;
|
|
63
|
-
const messages = [...opts.initialMessages];
|
|
64
|
-
let toolCalls = 0;
|
|
65
|
-
let lastText = "";
|
|
66
|
-
const usage = { input: 0, output: 0 };
|
|
67
|
-
const toolTrace = [];
|
|
68
|
-
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
69
|
-
if (opts.hooks?.stopBefore?.(turn)) break;
|
|
70
|
-
await opts.hooks?.beforeTurn?.(turn, messages);
|
|
71
|
-
if (opts.compaction) await maybeCompact(messages, opts.compaction, turn);
|
|
72
|
-
const r = await opts.chat(messages, opts.tools);
|
|
73
|
-
if (r.usage) {
|
|
74
|
-
usage.input += r.usage.input;
|
|
75
|
-
usage.output += r.usage.output;
|
|
76
|
-
opts.hooks?.onUsage?.(r.usage);
|
|
77
|
-
}
|
|
78
|
-
if (r.content) lastText = r.content;
|
|
79
|
-
if (r.toolCalls.length === 0) {
|
|
80
|
-
if (r.content) messages.push({ role: "assistant", content: r.content });
|
|
81
|
-
return { final: lastText, turns: turn, toolCalls, toolTrace, usage, messages };
|
|
82
|
-
}
|
|
83
|
-
messages.push({
|
|
84
|
-
role: "assistant",
|
|
85
|
-
content: r.content ?? "",
|
|
86
|
-
tool_calls: r.toolCalls.map((tc) => ({
|
|
87
|
-
id: tc.id,
|
|
88
|
-
type: "function",
|
|
89
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
90
|
-
}))
|
|
91
|
-
});
|
|
92
|
-
for (const tc of r.toolCalls) {
|
|
93
|
-
toolCalls += 1;
|
|
94
|
-
let args = {};
|
|
95
|
-
try {
|
|
96
|
-
args = JSON.parse(tc.arguments);
|
|
97
|
-
} catch {
|
|
98
|
-
messages.push({
|
|
99
|
-
role: "tool",
|
|
100
|
-
tool_call_id: tc.id,
|
|
101
|
-
content: `error: arguments were not valid JSON: ${tc.arguments.slice(0, 200)}`
|
|
102
|
-
});
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
const out = await opts.execute(tc.name, args);
|
|
106
|
-
messages.push({ role: "tool", tool_call_id: tc.id, content: out });
|
|
107
|
-
toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// src/runtime/router-client.ts
|
|
114
|
-
async function routerChatWithUsage(cfg, messages, opts) {
|
|
115
|
-
const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
116
|
-
const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
|
|
117
|
-
let temperature = opts?.temperature ?? 0.2;
|
|
118
|
-
const body = () => ({
|
|
119
|
-
model: cfg.model,
|
|
120
|
-
messages,
|
|
121
|
-
temperature,
|
|
122
|
-
max_tokens: opts?.maxTokens ?? 8192,
|
|
123
|
-
...opts?.reasoningEffort ? { reasoning_effort: opts.reasoningEffort } : {}
|
|
124
|
-
});
|
|
125
|
-
if (cfg.complete) return parseChatResult(await cfg.complete(body()), cfg.model);
|
|
126
|
-
let lastErr = "";
|
|
127
|
-
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
128
|
-
const res = await fetch(url, {
|
|
129
|
-
method: "POST",
|
|
130
|
-
headers,
|
|
131
|
-
body: JSON.stringify(body()),
|
|
132
|
-
...opts?.signal ? { signal: opts.signal } : {}
|
|
133
|
-
});
|
|
134
|
-
if (res.ok) return parseChatResult(await res.json(), cfg.model);
|
|
135
|
-
const status = res.status;
|
|
136
|
-
const text = (await res.text()).slice(0, 200);
|
|
137
|
-
lastErr = `router ${status}: ${text}`;
|
|
138
|
-
if (status === 400 && /temperature/i.test(text) && temperature !== 1) {
|
|
139
|
-
temperature = 1;
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
if (![408, 425, 429, 500, 502, 503, 504, 520, 522, 524].includes(status))
|
|
143
|
-
throw new Error(lastErr);
|
|
144
|
-
if (attempt < 4) await new Promise((r) => setTimeout(r, 800 * 2 ** attempt));
|
|
145
|
-
}
|
|
146
|
-
throw new Error(`${lastErr} (exhausted retries)`);
|
|
147
|
-
}
|
|
148
|
-
function parseChatResult(json, model) {
|
|
149
|
-
const data = json;
|
|
150
|
-
const u = data.usage;
|
|
151
|
-
const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
|
|
152
|
-
const costUsd = usage && isModelPriced(model) ? estimateCost(usage.input, usage.output, model) : void 0;
|
|
153
|
-
const msg = data.choices?.[0]?.message;
|
|
154
|
-
const { content, reasoning } = splitReasoning(
|
|
155
|
-
msg?.content ?? "",
|
|
156
|
-
msg?.reasoning ?? msg?.reasoning_content
|
|
157
|
-
);
|
|
158
|
-
return {
|
|
159
|
-
content,
|
|
160
|
-
...reasoning ? { reasoning } : {},
|
|
161
|
-
...usage ? { usage } : {},
|
|
162
|
-
...costUsd !== void 0 ? { costUsd } : {}
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
function splitReasoning(rawContent, fieldReasoning) {
|
|
166
|
-
const open = rawContent.indexOf("<think>");
|
|
167
|
-
if (open !== -1) {
|
|
168
|
-
const close = rawContent.indexOf("</think>", open);
|
|
169
|
-
const inline = close !== -1 ? rawContent.slice(open + 7, close) : rawContent.slice(open + 7);
|
|
170
|
-
const rest = close !== -1 ? rawContent.slice(0, open) + rawContent.slice(close + 8) : rawContent.slice(0, open);
|
|
171
|
-
const reasoning = [fieldReasoning, inline.trim()].filter(Boolean).join("\n");
|
|
172
|
-
return { content: rest.trim(), ...reasoning ? { reasoning } : {} };
|
|
173
|
-
}
|
|
174
|
-
return { content: rawContent, ...fieldReasoning ? { reasoning: fieldReasoning } : {} };
|
|
175
|
-
}
|
|
176
|
-
async function routerChatWithTools(cfg, messages, tools, opts) {
|
|
177
|
-
const body = {
|
|
178
|
-
model: cfg.model,
|
|
179
|
-
messages,
|
|
180
|
-
tools,
|
|
181
|
-
tool_choice: opts?.toolChoice ?? "auto",
|
|
182
|
-
temperature: opts?.temperature ?? 0.3,
|
|
183
|
-
...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
|
|
184
|
-
};
|
|
185
|
-
const raw = cfg.complete ? await cfg.complete(body) : await (async () => {
|
|
186
|
-
const res = await fetch(`${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
|
|
187
|
-
method: "POST",
|
|
188
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` },
|
|
189
|
-
body: JSON.stringify(body),
|
|
190
|
-
...opts?.signal ? { signal: opts.signal } : {}
|
|
191
|
-
});
|
|
192
|
-
if (!res.ok) throw new Error(`router ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
193
|
-
return res.json();
|
|
194
|
-
})();
|
|
195
|
-
const data = raw;
|
|
196
|
-
const msg = data.choices?.[0]?.message;
|
|
197
|
-
const toolCalls = (msg?.tool_calls ?? []).map((tc, i) => ({
|
|
198
|
-
id: tc.id ?? `call_${i}`,
|
|
199
|
-
name: tc.function?.name ?? "",
|
|
200
|
-
arguments: tc.function?.arguments ?? "{}"
|
|
201
|
-
}));
|
|
202
|
-
const u = data.usage;
|
|
203
|
-
const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
|
|
204
|
-
const costUsd = usage && isModelPriced(cfg.model) ? estimateCost(usage.input, usage.output, cfg.model) : void 0;
|
|
205
|
-
return {
|
|
206
|
-
content: msg?.content ?? null,
|
|
207
|
-
toolCalls,
|
|
208
|
-
...usage ? { usage } : {},
|
|
209
|
-
...costUsd !== void 0 ? { costUsd } : {}
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
async function routerToolLoop(cfg, system, user, tools, execute, opts) {
|
|
213
|
-
const initialMessages = opts?.initialMessages ?? [
|
|
214
|
-
{ role: "system", content: system },
|
|
215
|
-
{ role: "user", content: user }
|
|
216
|
-
];
|
|
217
|
-
return runBrainLoop({
|
|
218
|
-
chat: (messages, toolSpecs) => routerChatWithTools(cfg, messages, toolSpecs, {
|
|
219
|
-
...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
|
|
220
|
-
...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
|
|
221
|
-
...opts?.signal ? { signal: opts.signal } : {}
|
|
222
|
-
}),
|
|
223
|
-
tools,
|
|
224
|
-
execute,
|
|
225
|
-
initialMessages,
|
|
226
|
-
maxTurns: opts?.maxTurns ?? 4
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
function routerBrain(cfg, opts = {}) {
|
|
230
|
-
const temperature = opts.temperature ?? 0.4;
|
|
231
|
-
return (messages, tools) => routerChatWithTools(cfg, messages, tools, { temperature, toolChoice: "auto" });
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// src/runtime-hooks.ts
|
|
235
|
-
function defineRuntimeHooks(hooks) {
|
|
236
|
-
return hooks;
|
|
237
|
-
}
|
|
238
|
-
function composeRuntimeHooks(...entries) {
|
|
239
|
-
const hooks = entries.filter((entry) => !!entry);
|
|
240
|
-
return {
|
|
241
|
-
onEvent: hooks.some((hook) => hook.onEvent) ? (event, context) => {
|
|
242
|
-
const pending = [];
|
|
243
|
-
for (const hook of hooks) {
|
|
244
|
-
const result = hook.onEvent?.(event, context);
|
|
245
|
-
if (isThenable(result)) pending.push(Promise.resolve(result));
|
|
246
|
-
}
|
|
247
|
-
if (pending.length > 0) return Promise.all(pending).then(() => void 0);
|
|
248
|
-
return void 0;
|
|
249
|
-
} : void 0,
|
|
250
|
-
onDecisionPoint: hooks.some((hook) => hook.onDecisionPoint) ? (point, context) => {
|
|
251
|
-
const pending = [];
|
|
252
|
-
for (const hook of hooks) {
|
|
253
|
-
const result = hook.onDecisionPoint?.(point, context);
|
|
254
|
-
if (isThenable(result)) pending.push(Promise.resolve(result));
|
|
255
|
-
}
|
|
256
|
-
if (pending.length > 0) return Promise.all(pending).then(() => void 0);
|
|
257
|
-
return void 0;
|
|
258
|
-
} : void 0,
|
|
259
|
-
onHookError: hooks.some((hook) => hook.onHookError) ? (error, context) => {
|
|
260
|
-
const pending = [];
|
|
261
|
-
for (const hook of hooks) {
|
|
262
|
-
const result = hook.onHookError?.(error, context);
|
|
263
|
-
if (isThenable(result)) pending.push(Promise.resolve(result));
|
|
264
|
-
}
|
|
265
|
-
if (pending.length > 0) return Promise.all(pending).then(() => void 0);
|
|
266
|
-
return void 0;
|
|
267
|
-
} : void 0
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
function notifyRuntimeHookEvent(hooks, event, context = {}) {
|
|
271
|
-
const onEvent = hooks?.onEvent;
|
|
272
|
-
if (!onEvent) return;
|
|
273
|
-
try {
|
|
274
|
-
const result = onEvent(event, context);
|
|
275
|
-
if (isThenable(result)) {
|
|
276
|
-
void result.catch((error) => {
|
|
277
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
278
|
-
hook: "onEvent",
|
|
279
|
-
eventId: event.id,
|
|
280
|
-
target: event.target,
|
|
281
|
-
phase: event.phase
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
} catch (error) {
|
|
286
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
287
|
-
hook: "onEvent",
|
|
288
|
-
eventId: event.id,
|
|
289
|
-
target: event.target,
|
|
290
|
-
phase: event.phase
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
function notifyRuntimeDecisionPoint(hooks, point, context = {}) {
|
|
295
|
-
const onDecisionPoint = hooks?.onDecisionPoint;
|
|
296
|
-
if (!onDecisionPoint) return;
|
|
297
|
-
try {
|
|
298
|
-
const result = onDecisionPoint(point, context);
|
|
299
|
-
if (isThenable(result)) {
|
|
300
|
-
void result.catch((error) => {
|
|
301
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
302
|
-
hook: "onDecisionPoint",
|
|
303
|
-
decisionId: point.id,
|
|
304
|
-
decisionKind: point.kind
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
} catch (error) {
|
|
309
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
310
|
-
hook: "onDecisionPoint",
|
|
311
|
-
decisionId: point.id,
|
|
312
|
-
decisionKind: point.kind
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
function notifyRuntimeHookError(hooks, error, context) {
|
|
317
|
-
try {
|
|
318
|
-
const result = hooks?.onHookError?.(error, context);
|
|
319
|
-
if (isThenable(result)) void result.catch(() => void 0);
|
|
320
|
-
} catch {
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
function isThenable(value) {
|
|
324
|
-
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
325
|
-
}
|
|
326
|
-
function toError(error) {
|
|
327
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// src/runtime/supervise/scope.ts
|
|
331
|
-
var nestedScopeSeamKey = "nested-scope";
|
|
332
|
-
function makeNestedScopeSeam(args, childNodeId) {
|
|
333
|
-
return {
|
|
334
|
-
depth: args.depth,
|
|
335
|
-
...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
|
|
336
|
-
journalRoot: args.root,
|
|
337
|
-
mount(nestedRoot, signal) {
|
|
338
|
-
return createScope({
|
|
339
|
-
parentId: childNodeId,
|
|
340
|
-
root: nestedRoot,
|
|
341
|
-
pool: args.pool,
|
|
342
|
-
journal: args.journal,
|
|
343
|
-
blobs: args.blobs,
|
|
344
|
-
executors: args.executors,
|
|
345
|
-
// Re-seed the parent's NON-recursion seams (sandbox/router for leaf grandchildren);
|
|
346
|
-
// the nested scope adds its OWN nested-scope seam per child in `spawn`.
|
|
347
|
-
seams: args.seams,
|
|
348
|
-
depth: args.depth + 1,
|
|
349
|
-
...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
|
|
350
|
-
signal,
|
|
351
|
-
...args.now ? { now: args.now } : {},
|
|
352
|
-
...args.hooks ? { hooks: args.hooks } : {}
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
function createScope(args) {
|
|
358
|
-
const children = /* @__PURE__ */ new Map();
|
|
359
|
-
let spawnOrdinal = 0;
|
|
360
|
-
let cursorSeq = 0;
|
|
361
|
-
let meterSeq = 0;
|
|
362
|
-
const now = args.now ?? Date.now;
|
|
363
|
-
function spawn(agent, task, opts) {
|
|
364
|
-
if (args.maxDepth !== void 0 && args.depth >= args.maxDepth) {
|
|
365
|
-
return { ok: false, reason: "depth-exceeded" };
|
|
366
|
-
}
|
|
367
|
-
const spec = agent.executorSpec;
|
|
368
|
-
if (!isAgentSpec(spec)) {
|
|
369
|
-
throw new ValidationError(
|
|
370
|
-
`scope.spawn: agent "${agent.name}" exposes no \`executorSpec\` (AgentSpec) to resolve a Executor`
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
const resolved = args.executors.resolve(spec);
|
|
374
|
-
if (!resolved.succeeded) throw new ValidationError(`scope.spawn: ${resolved.error}`);
|
|
375
|
-
const reservation = args.pool.reserve(opts.budget);
|
|
376
|
-
if (!reservation.ok) return { ok: false, reason: reservation.reason };
|
|
377
|
-
try {
|
|
378
|
-
const ordinal = spawnOrdinal++;
|
|
379
|
-
const id = `${args.parentId}:s${ordinal}`;
|
|
380
|
-
const childAbort = new AbortController();
|
|
381
|
-
const cascadeAbort = () => childAbort.abort();
|
|
382
|
-
if (args.signal.aborted) childAbort.abort();
|
|
383
|
-
else args.signal.addEventListener("abort", cascadeAbort, { once: true });
|
|
384
|
-
const ctx = {
|
|
385
|
-
signal: childAbort.signal,
|
|
386
|
-
seams: { ...args.seams, [nestedScopeSeamKey]: makeNestedScopeSeam(args, id) }
|
|
387
|
-
};
|
|
388
|
-
const executor = resolved.value(spec, ctx);
|
|
389
|
-
const handle = {
|
|
390
|
-
id,
|
|
391
|
-
label: opts.label,
|
|
392
|
-
get status() {
|
|
393
|
-
return children.get(id)?.status ?? "cancelled";
|
|
394
|
-
},
|
|
395
|
-
abort(reason) {
|
|
396
|
-
childAbort.abort(reason);
|
|
397
|
-
}
|
|
398
|
-
};
|
|
399
|
-
const live = {
|
|
400
|
-
id,
|
|
401
|
-
status: "acquiring",
|
|
402
|
-
runtime: executor.runtime,
|
|
403
|
-
budget: opts.budget,
|
|
404
|
-
label: opts.label,
|
|
405
|
-
spent: zeroSpend(),
|
|
406
|
-
settled: void 0,
|
|
407
|
-
delivered: false,
|
|
408
|
-
executorDone: false,
|
|
409
|
-
...executor.deliver ? { deliver: executor.deliver.bind(executor) } : {}
|
|
410
|
-
};
|
|
411
|
-
children.set(id, live);
|
|
412
|
-
void args.journal.appendEvent(args.root, {
|
|
413
|
-
kind: "spawned",
|
|
414
|
-
id,
|
|
415
|
-
parent: args.parentId,
|
|
416
|
-
label: opts.label,
|
|
417
|
-
budget: opts.budget,
|
|
418
|
-
runtime: executor.runtime,
|
|
419
|
-
seq: ordinal,
|
|
420
|
-
at: new Date(now()).toISOString()
|
|
421
|
-
});
|
|
422
|
-
notifyRuntimeHookEvent(
|
|
423
|
-
args.hooks,
|
|
424
|
-
{
|
|
425
|
-
id: `${id}:spawn`,
|
|
426
|
-
runId: args.root,
|
|
427
|
-
target: "agent.spawn",
|
|
428
|
-
phase: "after",
|
|
429
|
-
timestamp: now(),
|
|
430
|
-
stepIndex: ordinal,
|
|
431
|
-
parentId: args.parentId,
|
|
432
|
-
payload: {
|
|
433
|
-
childId: id,
|
|
434
|
-
label: opts.label,
|
|
435
|
-
runtime: executor.runtime,
|
|
436
|
-
budget: opts.budget,
|
|
437
|
-
depth: args.depth
|
|
438
|
-
}
|
|
439
|
-
},
|
|
440
|
-
{ signal: args.signal }
|
|
441
|
-
);
|
|
442
|
-
const settled = runChild(
|
|
443
|
-
live,
|
|
444
|
-
executor,
|
|
445
|
-
childAbort,
|
|
446
|
-
task,
|
|
447
|
-
opts,
|
|
448
|
-
args.pool,
|
|
449
|
-
reservation.ticket,
|
|
450
|
-
args.blobs
|
|
451
|
-
).then((s) => {
|
|
452
|
-
live.resolved = s;
|
|
453
|
-
return s;
|
|
454
|
-
}).finally(() => {
|
|
455
|
-
args.signal.removeEventListener("abort", cascadeAbort);
|
|
456
|
-
});
|
|
457
|
-
live.settled = settled;
|
|
458
|
-
return { ok: true, handle };
|
|
459
|
-
} catch (err) {
|
|
460
|
-
args.pool.reconcile(reservation.ticket, zeroSpend());
|
|
461
|
-
throw err;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
async function next() {
|
|
465
|
-
const undelivered = () => [...children.values()].filter((c) => !c.delivered);
|
|
466
|
-
if (undelivered().length === 0) return null;
|
|
467
|
-
for (; ; ) {
|
|
468
|
-
const pending = undelivered();
|
|
469
|
-
if (pending.length === 0) return null;
|
|
470
|
-
const ready = pending.find((c) => c.resolved !== void 0);
|
|
471
|
-
const chosen = ready ?? await raceFirstSettled(pending);
|
|
472
|
-
if (chosen.delivered) continue;
|
|
473
|
-
chosen.delivered = true;
|
|
474
|
-
const seq = cursorSeq++;
|
|
475
|
-
const settlement = chosen.resolved;
|
|
476
|
-
if (!settlement) {
|
|
477
|
-
throw new ValidationError(
|
|
478
|
-
`scope.next: child '${chosen.id}' won the settle race without a resolved value`
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
return finalizeSettlement(chosen, settlement, seq, args, now);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
async function nextResolved() {
|
|
485
|
-
for (; ; ) {
|
|
486
|
-
const candidates = [...children.values()].filter((c) => !c.delivered);
|
|
487
|
-
const pick = candidates.find((c) => c.resolved !== void 0) ?? candidates.find((c) => c.executorDone);
|
|
488
|
-
if (!pick) return null;
|
|
489
|
-
const settlement = await pick.settled;
|
|
490
|
-
if (pick.delivered) continue;
|
|
491
|
-
pick.delivered = true;
|
|
492
|
-
const seq = cursorSeq++;
|
|
493
|
-
return finalizeSettlement(pick, settlement, seq, args, now);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
function send(nodeId, msg) {
|
|
497
|
-
const child = children.get(nodeId);
|
|
498
|
-
if (!child || child.delivered || !child.deliver) return false;
|
|
499
|
-
child.deliver(msg);
|
|
500
|
-
return true;
|
|
501
|
-
}
|
|
502
|
-
async function meter(spend, detail) {
|
|
503
|
-
const seq = meterSeq++;
|
|
504
|
-
args.pool.observe(spend);
|
|
505
|
-
await args.journal.appendEvent(args.root, {
|
|
506
|
-
kind: "metered",
|
|
507
|
-
id: args.parentId,
|
|
508
|
-
spend,
|
|
509
|
-
seq,
|
|
510
|
-
at: new Date(now()).toISOString()
|
|
511
|
-
});
|
|
512
|
-
notifyRuntimeHookEvent(
|
|
513
|
-
args.hooks,
|
|
514
|
-
{
|
|
515
|
-
id: `${args.parentId}:meter:${seq}`,
|
|
516
|
-
runId: args.root,
|
|
517
|
-
target: "agent.turn",
|
|
518
|
-
phase: "after",
|
|
519
|
-
timestamp: now(),
|
|
520
|
-
parentId: args.parentId,
|
|
521
|
-
payload: { spend, ...detail ?? {} }
|
|
522
|
-
},
|
|
523
|
-
{ signal: args.signal }
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
return {
|
|
527
|
-
spawn,
|
|
528
|
-
next,
|
|
529
|
-
nextResolved,
|
|
530
|
-
send,
|
|
531
|
-
signal: args.signal,
|
|
532
|
-
meter,
|
|
533
|
-
get view() {
|
|
534
|
-
return makeTreeView(args.parentId, children);
|
|
535
|
-
},
|
|
536
|
-
get budget() {
|
|
537
|
-
return args.pool.readout();
|
|
538
|
-
}
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
async function raceFirstSettled(pending) {
|
|
542
|
-
return Promise.race(pending.map((c) => c.settled.then(() => c)));
|
|
543
|
-
}
|
|
544
|
-
async function finalizeSettlement(child, settlement, seq, args, now) {
|
|
545
|
-
const handle = frozenHandle(child);
|
|
546
|
-
if (settlement.kind === "down") {
|
|
547
|
-
child.status = "failed";
|
|
548
|
-
await args.journal.appendEvent(args.root, {
|
|
549
|
-
kind: "settled",
|
|
550
|
-
id: child.id,
|
|
551
|
-
status: "down",
|
|
552
|
-
spent: child.spent,
|
|
553
|
-
infra: settlement.infra,
|
|
554
|
-
seq,
|
|
555
|
-
at: new Date(now()).toISOString()
|
|
556
|
-
});
|
|
557
|
-
if (settlement.metered) {
|
|
558
|
-
await args.journal.appendEvent(args.root, {
|
|
559
|
-
kind: "metered",
|
|
560
|
-
id: child.id,
|
|
561
|
-
spend: settlement.metered,
|
|
562
|
-
seq,
|
|
563
|
-
at: new Date(now()).toISOString()
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
notifyRuntimeHookEvent(
|
|
567
|
-
args.hooks,
|
|
568
|
-
{
|
|
569
|
-
id: `${child.id}:settled`,
|
|
570
|
-
runId: args.root,
|
|
571
|
-
target: "agent.child",
|
|
572
|
-
phase: "after",
|
|
573
|
-
timestamp: now(),
|
|
574
|
-
stepIndex: seq,
|
|
575
|
-
parentId: args.parentId,
|
|
576
|
-
payload: {
|
|
577
|
-
childId: child.id,
|
|
578
|
-
status: "down",
|
|
579
|
-
reason: settlement.reason,
|
|
580
|
-
infra: settlement.infra,
|
|
581
|
-
spent: child.spent
|
|
582
|
-
}
|
|
583
|
-
},
|
|
584
|
-
{ signal: args.signal }
|
|
585
|
-
);
|
|
586
|
-
return {
|
|
587
|
-
kind: "down",
|
|
588
|
-
handle,
|
|
589
|
-
reason: settlement.reason,
|
|
590
|
-
infra: settlement.infra,
|
|
591
|
-
restartCount: settlement.restartCount,
|
|
592
|
-
seq
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
child.status = "done";
|
|
596
|
-
child.outRef = settlement.outRef;
|
|
597
|
-
child.spent = settlement.spent;
|
|
598
|
-
await args.journal.appendEvent(args.root, {
|
|
599
|
-
kind: "settled",
|
|
600
|
-
id: child.id,
|
|
601
|
-
status: "done",
|
|
602
|
-
outRef: settlement.outRef,
|
|
603
|
-
...settlement.verdict ? { verdict: settlement.verdict } : {},
|
|
604
|
-
spent: settlement.spent,
|
|
605
|
-
seq,
|
|
606
|
-
at: new Date(now()).toISOString()
|
|
607
|
-
});
|
|
608
|
-
if (settlement.metered) {
|
|
609
|
-
await args.journal.appendEvent(args.root, {
|
|
610
|
-
kind: "metered",
|
|
611
|
-
id: child.id,
|
|
612
|
-
spend: settlement.metered,
|
|
613
|
-
seq,
|
|
614
|
-
at: new Date(now()).toISOString()
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
notifyRuntimeHookEvent(
|
|
618
|
-
args.hooks,
|
|
619
|
-
{
|
|
620
|
-
id: `${child.id}:settled`,
|
|
621
|
-
runId: args.root,
|
|
622
|
-
target: "agent.child",
|
|
623
|
-
phase: "after",
|
|
624
|
-
timestamp: now(),
|
|
625
|
-
stepIndex: seq,
|
|
626
|
-
parentId: args.parentId,
|
|
627
|
-
payload: {
|
|
628
|
-
childId: child.id,
|
|
629
|
-
status: "done",
|
|
630
|
-
outRef: settlement.outRef,
|
|
631
|
-
score: settlement.verdict?.score,
|
|
632
|
-
valid: settlement.verdict?.valid,
|
|
633
|
-
spent: settlement.spent
|
|
634
|
-
}
|
|
635
|
-
},
|
|
636
|
-
{ signal: args.signal }
|
|
637
|
-
);
|
|
638
|
-
return {
|
|
639
|
-
kind: "done",
|
|
640
|
-
handle,
|
|
641
|
-
out: settlement.out,
|
|
642
|
-
outRef: settlement.outRef,
|
|
643
|
-
...settlement.verdict ? { verdict: settlement.verdict } : {},
|
|
644
|
-
spent: settlement.spent,
|
|
645
|
-
seq
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
async function runChild(live, executor, childAbort, task, opts, pool, ticket, blobs) {
|
|
649
|
-
let reconciled = false;
|
|
650
|
-
const reconcileOnce = (spend) => {
|
|
651
|
-
if (reconciled) return;
|
|
652
|
-
reconciled = true;
|
|
653
|
-
pool.reconcile(ticket, clampSpend(spend, opts.budget));
|
|
654
|
-
};
|
|
655
|
-
try {
|
|
656
|
-
live.status = "running";
|
|
657
|
-
const ran = executor.execute(task, childAbort.signal);
|
|
658
|
-
let artifact;
|
|
659
|
-
if (isAsyncIterable(ran)) {
|
|
660
|
-
const spend = await foldStream(ran);
|
|
661
|
-
live.spent = spend;
|
|
662
|
-
artifact = executor.resultArtifact();
|
|
663
|
-
reconcileOnce(spend);
|
|
664
|
-
} else {
|
|
665
|
-
const terminal = await ran;
|
|
666
|
-
live.spent = terminal.spent;
|
|
667
|
-
artifact = terminal;
|
|
668
|
-
reconcileOnce(terminal.spent);
|
|
669
|
-
}
|
|
670
|
-
live.executorDone = true;
|
|
671
|
-
const ownMetered = executor.metered?.();
|
|
672
|
-
if (childAbort.signal.aborted) {
|
|
673
|
-
await teardownSafe(executor, opts.shutdown ?? "brutalKill");
|
|
674
|
-
return downRecord("aborted before settle", true, ownMetered);
|
|
675
|
-
}
|
|
676
|
-
const outRef = contentAddress(artifact.out);
|
|
677
|
-
await blobs.put(outRef, artifact.out);
|
|
678
|
-
await teardownSafe(executor, opts.shutdown ?? "infinity");
|
|
679
|
-
return {
|
|
680
|
-
kind: "done",
|
|
681
|
-
out: artifact.out,
|
|
682
|
-
outRef,
|
|
683
|
-
...artifact.verdict ? { verdict: artifact.verdict } : {},
|
|
684
|
-
spent: live.spent,
|
|
685
|
-
...ownMetered ? { metered: ownMetered } : {}
|
|
686
|
-
};
|
|
687
|
-
} catch (err) {
|
|
688
|
-
live.executorDone = true;
|
|
689
|
-
reconcileOnce(live.spent);
|
|
690
|
-
await teardownSafe(executor, "brutalKill");
|
|
691
|
-
const aborted = childAbort.signal.aborted || isAbortError(err);
|
|
692
|
-
return downRecord(errMessage(err), aborted || isInfraError(err), executor.metered?.());
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
function settledToIteration(settled) {
|
|
696
|
-
if (settled.kind === "down") {
|
|
697
|
-
throw new ValidationError(
|
|
698
|
-
`settledToIteration: cannot adapt a 'down' settlement (node '${settled.handle.id}', seq ${settled.seq}) to an Iteration`
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
return {
|
|
702
|
-
index: settled.seq,
|
|
703
|
-
task: void 0,
|
|
704
|
-
agentRunName: settled.handle.label,
|
|
705
|
-
output: settled.out,
|
|
706
|
-
...settled.verdict ? { verdict: settled.verdict } : {},
|
|
707
|
-
events: [],
|
|
708
|
-
startedAt: 0,
|
|
709
|
-
endedAt: settled.spent.ms,
|
|
710
|
-
costUsd: settled.spent.usd,
|
|
711
|
-
tokenUsage: { input: settled.spent.tokens.input, output: settled.spent.tokens.output }
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
function makeTreeView(root, children) {
|
|
715
|
-
const nodes = [...children.values()].map((c) => ({
|
|
716
|
-
id: c.id,
|
|
717
|
-
parent: root,
|
|
718
|
-
label: c.label,
|
|
719
|
-
status: c.status,
|
|
720
|
-
runtime: c.runtime,
|
|
721
|
-
budget: c.budget,
|
|
722
|
-
spent: c.spent,
|
|
723
|
-
...c.outRef ? { outRef: c.outRef } : {}
|
|
724
|
-
}));
|
|
725
|
-
return {
|
|
726
|
-
root,
|
|
727
|
-
nodes,
|
|
728
|
-
inFlight: nodes.filter((n) => n.status === "running" || n.status === "acquiring").length
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
function frozenHandle(child) {
|
|
732
|
-
return {
|
|
733
|
-
id: child.id,
|
|
734
|
-
label: child.label,
|
|
735
|
-
status: child.status,
|
|
736
|
-
abort() {
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
async function foldStream(stream) {
|
|
741
|
-
const tokens = { input: 0, output: 0 };
|
|
742
|
-
let usd = 0;
|
|
743
|
-
let iterations = 0;
|
|
744
|
-
for await (const ev of stream) {
|
|
745
|
-
if (ev.kind === "tokens") {
|
|
746
|
-
tokens.input += ev.input;
|
|
747
|
-
tokens.output += ev.output;
|
|
748
|
-
} else if (ev.kind === "cost") {
|
|
749
|
-
usd += ev.usd;
|
|
750
|
-
} else {
|
|
751
|
-
iterations += 1;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
return { iterations, tokens, usd, ms: 0 };
|
|
755
|
-
}
|
|
756
|
-
function clampSpend(spend, budget) {
|
|
757
|
-
const totalTokens2 = spend.tokens.input + spend.tokens.output;
|
|
758
|
-
const tokensOk = totalTokens2 <= budget.maxTokens;
|
|
759
|
-
const itersOk = spend.iterations <= budget.maxIterations;
|
|
760
|
-
const usdOk = budget.maxUsd === void 0 || spend.usd <= budget.maxUsd;
|
|
761
|
-
if (tokensOk && itersOk && usdOk) return spend;
|
|
762
|
-
const ratio = !tokensOk && totalTokens2 > 0 ? budget.maxTokens / totalTokens2 : 1;
|
|
763
|
-
return {
|
|
764
|
-
iterations: Math.min(spend.iterations, budget.maxIterations),
|
|
765
|
-
tokens: ratio < 1 ? {
|
|
766
|
-
input: Math.floor(spend.tokens.input * ratio),
|
|
767
|
-
output: Math.floor(spend.tokens.output * ratio)
|
|
768
|
-
} : spend.tokens,
|
|
769
|
-
usd: budget.maxUsd === void 0 ? spend.usd : Math.min(spend.usd, budget.maxUsd),
|
|
770
|
-
ms: spend.ms
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
async function teardownSafe(executor, grace) {
|
|
774
|
-
try {
|
|
775
|
-
await executor.teardown(grace);
|
|
776
|
-
} catch {
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
function downRecord(reason, infra, metered) {
|
|
780
|
-
return { kind: "down", reason, infra, restartCount: 0, ...metered ? { metered } : {} };
|
|
781
|
-
}
|
|
782
|
-
function zeroSpend() {
|
|
783
|
-
return { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
784
|
-
}
|
|
785
|
-
function isAsyncIterable(value) {
|
|
786
|
-
return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
|
|
787
|
-
}
|
|
788
|
-
function isAgentSpec(value) {
|
|
789
|
-
if (typeof value !== "object" || value === null) return false;
|
|
790
|
-
const v = value;
|
|
791
|
-
return "profile" in v && "harness" in v;
|
|
792
|
-
}
|
|
793
|
-
function isAbortError(err) {
|
|
794
|
-
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
795
|
-
}
|
|
796
|
-
function isInfraError(err) {
|
|
797
|
-
return err instanceof ValidationError;
|
|
798
|
-
}
|
|
799
|
-
function errMessage(err) {
|
|
800
|
-
if (err instanceof Error) return err.message;
|
|
801
|
-
return String(err);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
// src/runtime/supervise/budget.ts
|
|
805
|
-
function spendFromUsageEvents(events) {
|
|
806
|
-
const tokens = zeroTokenUsage();
|
|
807
|
-
let usd = 0;
|
|
808
|
-
let iterations = 0;
|
|
809
|
-
for (const ev of events) {
|
|
810
|
-
if (ev.kind === "tokens") {
|
|
811
|
-
addTokenUsage(tokens, { input: ev.input, output: ev.output });
|
|
812
|
-
} else if (ev.kind === "cost") {
|
|
813
|
-
usd += ev.usd;
|
|
814
|
-
} else {
|
|
815
|
-
iterations += 1;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
return { iterations, tokens, usd, ms: 0 };
|
|
819
|
-
}
|
|
820
|
-
async function foldUsage(events) {
|
|
821
|
-
if (Array.isArray(events)) return spendFromUsageEvents(events);
|
|
822
|
-
const tokens = zeroTokenUsage();
|
|
823
|
-
let usd = 0;
|
|
824
|
-
let iterations = 0;
|
|
825
|
-
for await (const ev of events) {
|
|
826
|
-
if (ev.kind === "tokens") {
|
|
827
|
-
addTokenUsage(tokens, { input: ev.input, output: ev.output });
|
|
828
|
-
} else if (ev.kind === "cost") {
|
|
829
|
-
usd += ev.usd;
|
|
830
|
-
} else {
|
|
831
|
-
iterations += 1;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
return { iterations, tokens, usd, ms: 0 };
|
|
835
|
-
}
|
|
836
|
-
function totalTokens(usage) {
|
|
837
|
-
return usage.input + usage.output;
|
|
838
|
-
}
|
|
839
|
-
function createBudgetPool(root, now = Date.now) {
|
|
840
|
-
let freeTokens = root.maxTokens;
|
|
841
|
-
let reservedTokens = 0;
|
|
842
|
-
let committedTokens = 0;
|
|
843
|
-
const usdCapped = root.maxUsd !== void 0;
|
|
844
|
-
let freeUsd = root.maxUsd ?? 0;
|
|
845
|
-
let reservedUsd = 0;
|
|
846
|
-
let committedUsd = 0;
|
|
847
|
-
let freeIterations = root.maxIterations;
|
|
848
|
-
let reservedIterations = 0;
|
|
849
|
-
let committedIterations = 0;
|
|
850
|
-
const absoluteDeadlineMs = root.deadlineMs !== void 0 ? now() + root.deadlineMs : 0;
|
|
851
|
-
let nextTicketId = 0;
|
|
852
|
-
const open = /* @__PURE__ */ new Set();
|
|
853
|
-
function reserve(b) {
|
|
854
|
-
const wantTokens = b.maxTokens;
|
|
855
|
-
const wantUsd = b.maxUsd ?? 0;
|
|
856
|
-
const wantIterations = b.maxIterations;
|
|
857
|
-
if (wantTokens > freeTokens) return { ok: false, reason: "budget-exhausted" };
|
|
858
|
-
if (wantIterations > freeIterations) return { ok: false, reason: "budget-exhausted" };
|
|
859
|
-
if (wantUsd > 0 && (!usdCapped || wantUsd > freeUsd)) {
|
|
860
|
-
return { ok: false, reason: "budget-exhausted" };
|
|
861
|
-
}
|
|
862
|
-
freeTokens -= wantTokens;
|
|
863
|
-
reservedTokens += wantTokens;
|
|
864
|
-
freeIterations -= wantIterations;
|
|
865
|
-
reservedIterations += wantIterations;
|
|
866
|
-
if (wantUsd > 0) {
|
|
867
|
-
freeUsd -= wantUsd;
|
|
868
|
-
reservedUsd += wantUsd;
|
|
869
|
-
}
|
|
870
|
-
const id = nextTicketId++;
|
|
871
|
-
open.add(id);
|
|
872
|
-
return {
|
|
873
|
-
ok: true,
|
|
874
|
-
ticket: { id, reserved: { tokens: wantTokens, usd: wantUsd, iterations: wantIterations } }
|
|
875
|
-
};
|
|
876
|
-
}
|
|
877
|
-
function reconcile(ticket, spent) {
|
|
878
|
-
if (!open.has(ticket.id)) {
|
|
879
|
-
throw new Error(`budget pool: reconcile of unknown or already-settled ticket ${ticket.id}`);
|
|
880
|
-
}
|
|
881
|
-
open.delete(ticket.id);
|
|
882
|
-
const { tokens: rTokens, usd: rUsd, iterations: rIterations } = ticket.reserved;
|
|
883
|
-
const spentTokens = totalTokens(spent.tokens);
|
|
884
|
-
if (spentTokens > rTokens) {
|
|
885
|
-
throw new Error(
|
|
886
|
-
`budget pool: ticket ${ticket.id} spent ${spentTokens} tokens > reserved ${rTokens}`
|
|
887
|
-
);
|
|
888
|
-
}
|
|
889
|
-
if (spent.iterations > rIterations) {
|
|
890
|
-
throw new Error(
|
|
891
|
-
`budget pool: ticket ${ticket.id} spent ${spent.iterations} iterations > reserved ${rIterations}`
|
|
892
|
-
);
|
|
893
|
-
}
|
|
894
|
-
if (usdCapped && spent.usd > rUsd) {
|
|
895
|
-
throw new Error(`budget pool: ticket ${ticket.id} spent $${spent.usd} > reserved $${rUsd}`);
|
|
896
|
-
}
|
|
897
|
-
reservedTokens -= rTokens;
|
|
898
|
-
committedTokens += spentTokens;
|
|
899
|
-
freeTokens += rTokens - spentTokens;
|
|
900
|
-
reservedIterations -= rIterations;
|
|
901
|
-
committedIterations += spent.iterations;
|
|
902
|
-
freeIterations += rIterations - spent.iterations;
|
|
903
|
-
if (usdCapped && rUsd > 0) {
|
|
904
|
-
reservedUsd -= rUsd;
|
|
905
|
-
committedUsd += spent.usd;
|
|
906
|
-
freeUsd += rUsd - spent.usd;
|
|
907
|
-
} else {
|
|
908
|
-
committedUsd += spent.usd;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
function observe(spend) {
|
|
912
|
-
const tokens = totalTokens(spend.tokens);
|
|
913
|
-
freeTokens -= tokens;
|
|
914
|
-
committedTokens += tokens;
|
|
915
|
-
freeIterations -= spend.iterations;
|
|
916
|
-
committedIterations += spend.iterations;
|
|
917
|
-
committedUsd += spend.usd;
|
|
918
|
-
if (usdCapped) freeUsd -= spend.usd;
|
|
919
|
-
}
|
|
920
|
-
function readout() {
|
|
921
|
-
return {
|
|
922
|
-
tokensLeft: freeTokens,
|
|
923
|
-
usdLeft: usdCapped ? freeUsd : 0,
|
|
924
|
-
usdCapped,
|
|
925
|
-
deadlineMs: absoluteDeadlineMs,
|
|
926
|
-
reservedTokens
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
function assertNoOpenTickets() {
|
|
930
|
-
if (open.size > 0) {
|
|
931
|
-
throw new Error(
|
|
932
|
-
`budget pool: ${open.size} reservation(s) still open at join barrier (leaked ticket ids: ${[...open].join(", ")}) \u2014 conserved-pool invariant violated`
|
|
933
|
-
);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
return {
|
|
937
|
-
reserve,
|
|
938
|
-
reconcile,
|
|
939
|
-
spendFrom: foldUsage,
|
|
940
|
-
readout,
|
|
941
|
-
observe,
|
|
942
|
-
assertNoOpenTickets
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// src/runtime/supervise/supervisor.ts
|
|
947
|
-
var defaultMaxDepth = 4;
|
|
948
|
-
function createSupervisor() {
|
|
949
|
-
let attached;
|
|
950
|
-
async function run(root, task, opts) {
|
|
951
|
-
const now = opts.now ?? Date.now;
|
|
952
|
-
const pool = createBudgetPool(opts.budget, now);
|
|
953
|
-
await opts.journal.beginTree(opts.runId, new Date(now()).toISOString());
|
|
954
|
-
await opts.journal.appendEvent(opts.runId, {
|
|
955
|
-
kind: "spawned",
|
|
956
|
-
id: opts.runId,
|
|
957
|
-
label: "root",
|
|
958
|
-
budget: opts.budget,
|
|
959
|
-
runtime: "inline",
|
|
960
|
-
seq: 0,
|
|
961
|
-
at: new Date(now()).toISOString()
|
|
962
|
-
});
|
|
963
|
-
const controller = new AbortController();
|
|
964
|
-
const cascadeAbort = (reason) => {
|
|
965
|
-
if (controller.signal.aborted) return;
|
|
966
|
-
controller.abort(reason);
|
|
967
|
-
};
|
|
968
|
-
const onCallerAbort = () => cascadeAbort("caller signal aborted");
|
|
969
|
-
if (opts.signal) {
|
|
970
|
-
if (opts.signal.aborted) cascadeAbort("caller signal aborted");
|
|
971
|
-
else opts.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
972
|
-
}
|
|
973
|
-
const breaker = createIntensityBreaker(opts, () => cascadeAbort("intensity breaker tripped"));
|
|
974
|
-
const journal = wrapJournalForBreaker(opts.journal, breaker);
|
|
975
|
-
const scope = createScope({
|
|
976
|
-
parentId: opts.runId,
|
|
977
|
-
root: opts.runId,
|
|
978
|
-
pool,
|
|
979
|
-
journal,
|
|
980
|
-
blobs: opts.blobs,
|
|
981
|
-
executors: opts.executors,
|
|
982
|
-
seams: {},
|
|
983
|
-
depth: 0,
|
|
984
|
-
maxDepth: opts.maxDepth ?? defaultMaxDepth,
|
|
985
|
-
signal: controller.signal,
|
|
986
|
-
now,
|
|
987
|
-
hooks: opts.hooks
|
|
988
|
-
});
|
|
989
|
-
const openScope = scope;
|
|
990
|
-
if (attached) {
|
|
991
|
-
attached.bind({ scope: openScope, cascadeAbort, signal: pushRootSignal(cascadeAbort) });
|
|
992
|
-
}
|
|
993
|
-
let actOutcome;
|
|
994
|
-
try {
|
|
995
|
-
const out = await root.act(task, scope);
|
|
996
|
-
actOutcome = { ok: true, out };
|
|
997
|
-
} catch (error) {
|
|
998
|
-
actOutcome = { ok: false, error };
|
|
999
|
-
} finally {
|
|
1000
|
-
await drainLiveChildren(openScope, controller);
|
|
1001
|
-
if (opts.signal) opts.signal.removeEventListener("abort", onCallerAbort);
|
|
1002
|
-
if (attached) attached.unbind();
|
|
1003
|
-
}
|
|
1004
|
-
const tree = scope.view;
|
|
1005
|
-
if (actOutcome.ok) {
|
|
1006
|
-
pool.assertNoOpenTickets();
|
|
1007
|
-
const out = actOutcome.out;
|
|
1008
|
-
if (out !== void 0) {
|
|
1009
|
-
const outRef = contentAddress(out);
|
|
1010
|
-
await opts.blobs.put(outRef, out);
|
|
1011
|
-
const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
|
|
1012
|
-
return {
|
|
1013
|
-
kind: "winner",
|
|
1014
|
-
out,
|
|
1015
|
-
outRef,
|
|
1016
|
-
tree,
|
|
1017
|
-
spentTotal: addSpend(childWork, driverInference),
|
|
1018
|
-
...isNonEmptySpend(driverInference) ? { spentBreakdown: { driverInference, childWork } } : {}
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
return noWinner();
|
|
1022
|
-
}
|
|
1023
|
-
return noWinner();
|
|
1024
|
-
async function noWinner() {
|
|
1025
|
-
const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
|
|
1026
|
-
return {
|
|
1027
|
-
kind: "no-winner",
|
|
1028
|
-
reason: classifyNoWinner(controller, pool, opts, breaker),
|
|
1029
|
-
tree,
|
|
1030
|
-
downCount: breaker.downCount(),
|
|
1031
|
-
spentTotal: addSpend(childWork, driverInference)
|
|
1032
|
-
};
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
function attach(h) {
|
|
1036
|
-
const control = rootControls.get(h);
|
|
1037
|
-
if (!control) {
|
|
1038
|
-
throw new RuntimeRunStateError(
|
|
1039
|
-
"supervisor.attach: handle was not minted by createRootHandle (no control channel)"
|
|
1040
|
-
);
|
|
1041
|
-
}
|
|
1042
|
-
attached = control;
|
|
1043
|
-
}
|
|
1044
|
-
return { run, attach };
|
|
1045
|
-
}
|
|
1046
|
-
var rootControls = /* @__PURE__ */ new WeakMap();
|
|
1047
|
-
function pushRootSignal(cascadeAbort) {
|
|
1048
|
-
return (msg) => {
|
|
1049
|
-
if (msg.kind === "cancel") cascadeAbort(msg.reason ?? "root signal: cancel");
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
function createIntensityBreaker(opts, trip) {
|
|
1053
|
-
const max = opts.maxRestarts;
|
|
1054
|
-
const within = opts.withinMs;
|
|
1055
|
-
const armed = max !== void 0 && within !== void 0;
|
|
1056
|
-
const recent = [];
|
|
1057
|
-
let total = 0;
|
|
1058
|
-
let isTripped = false;
|
|
1059
|
-
return {
|
|
1060
|
-
recordDown(at) {
|
|
1061
|
-
total += 1;
|
|
1062
|
-
if (!armed || isTripped) return;
|
|
1063
|
-
recent.push(at);
|
|
1064
|
-
const cutoff = at - within;
|
|
1065
|
-
while (recent.length > 0 && recent[0] < cutoff) recent.shift();
|
|
1066
|
-
if (recent.length > max) {
|
|
1067
|
-
isTripped = true;
|
|
1068
|
-
trip();
|
|
1069
|
-
}
|
|
1070
|
-
},
|
|
1071
|
-
tripped() {
|
|
1072
|
-
return isTripped;
|
|
1073
|
-
},
|
|
1074
|
-
downCount() {
|
|
1075
|
-
return total;
|
|
1076
|
-
}
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
1079
|
-
function wrapJournalForBreaker(journal, breaker) {
|
|
1080
|
-
return {
|
|
1081
|
-
loadTree: (root) => journal.loadTree(root),
|
|
1082
|
-
beginTree: (root, at) => journal.beginTree(root, at),
|
|
1083
|
-
appendEvent: (root, ev) => {
|
|
1084
|
-
if (ev.kind === "settled" && ev.status === "down") breaker.recordDown(Date.parse(ev.at));
|
|
1085
|
-
return journal.appendEvent(root, ev);
|
|
1086
|
-
}
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
async function drainLiveChildren(scope, controller) {
|
|
1090
|
-
const hasLive = scope.view.inFlight > 0;
|
|
1091
|
-
if (!hasLive) return;
|
|
1092
|
-
if (!controller.signal.aborted) controller.abort();
|
|
1093
|
-
await Promise.allSettled([drainCursor(scope)]);
|
|
1094
|
-
}
|
|
1095
|
-
async function drainCursor(scope) {
|
|
1096
|
-
for (; ; ) {
|
|
1097
|
-
const settled = await scope.next();
|
|
1098
|
-
if (settled === null) return;
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
function classifyNoWinner(controller, pool, opts, breaker) {
|
|
1102
|
-
if (breaker.tripped()) return "all-children-down";
|
|
1103
|
-
if (controller.signal.aborted) return "aborted";
|
|
1104
|
-
if (poolExhausted(pool, opts)) return "budget-exhausted";
|
|
1105
|
-
return "all-children-down";
|
|
1106
|
-
}
|
|
1107
|
-
function poolExhausted(pool, opts) {
|
|
1108
|
-
const r = pool.readout();
|
|
1109
|
-
if (r.tokensLeft <= 0) return true;
|
|
1110
|
-
if (opts.budget.maxUsd !== void 0 && r.usdLeft <= 0) return true;
|
|
1111
|
-
if (opts.budget.deadlineMs !== void 0 && r.deadlineMs > 0 && (opts.now ?? Date.now)() >= r.deadlineMs) {
|
|
1112
|
-
return true;
|
|
1113
|
-
}
|
|
1114
|
-
return false;
|
|
1115
|
-
}
|
|
1116
|
-
async function spentFromJournal(journal, root) {
|
|
1117
|
-
const events = await journal.loadTree(root);
|
|
1118
|
-
if (events === void 0) {
|
|
1119
|
-
throw new RuntimeRunStateError(
|
|
1120
|
-
`supervisor: spawn tree '${root}' is missing from the journal after run (corrupted log)`
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
const childWork = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
1124
|
-
const driverInference = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
1125
|
-
for (const ev of events) {
|
|
1126
|
-
if (ev.kind === "settled") accumulate(childWork, ev.spent);
|
|
1127
|
-
else if (ev.kind === "metered") accumulate(driverInference, ev.spend);
|
|
1128
|
-
}
|
|
1129
|
-
return { childWork, driverInference };
|
|
1130
|
-
}
|
|
1131
|
-
function accumulate(a, b) {
|
|
1132
|
-
a.iterations += b.iterations;
|
|
1133
|
-
a.tokens.input += b.tokens.input;
|
|
1134
|
-
a.tokens.output += b.tokens.output;
|
|
1135
|
-
a.usd += b.usd;
|
|
1136
|
-
a.ms += b.ms;
|
|
1137
|
-
}
|
|
1138
|
-
function addSpend(a, b) {
|
|
1139
|
-
return {
|
|
1140
|
-
iterations: a.iterations + b.iterations,
|
|
1141
|
-
tokens: { input: a.tokens.input + b.tokens.input, output: a.tokens.output + b.tokens.output },
|
|
1142
|
-
usd: a.usd + b.usd,
|
|
1143
|
-
ms: a.ms + b.ms
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
function isNonEmptySpend(s) {
|
|
1147
|
-
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
// src/sanitize.ts
|
|
1151
|
-
function sanitizeKnowledgeReadinessReport(report, options = {}) {
|
|
1152
|
-
return {
|
|
1153
|
-
taskId: report.taskId,
|
|
1154
|
-
readinessScore: report.readinessScore,
|
|
1155
|
-
recommendedAction: report.recommendedAction,
|
|
1156
|
-
severity: report.severity,
|
|
1157
|
-
reason: report.reason,
|
|
1158
|
-
blockingMissingRequirements: report.blockingMissingRequirements.map(
|
|
1159
|
-
(requirement) => sanitizeKnowledgeRequirement(requirement, options)
|
|
1160
|
-
),
|
|
1161
|
-
nonBlockingGaps: report.nonBlockingGaps.map(
|
|
1162
|
-
(requirement) => sanitizeKnowledgeRequirement(requirement, options)
|
|
1163
|
-
),
|
|
1164
|
-
evidenceCount: report.bundle.evidenceIds.length,
|
|
1165
|
-
evidenceIds: options.includeEvidenceIds ? report.bundle.evidenceIds : void 0,
|
|
1166
|
-
missingRequirementIds: report.bundle.missing.map((requirement) => requirement.id)
|
|
1167
|
-
};
|
|
1168
|
-
}
|
|
1169
|
-
function sanitizeAgentRuntimeEvent(event, options = {}) {
|
|
1170
|
-
const base = { type: event.type, task: sanitizeTask(event.task, options) };
|
|
1171
|
-
if (event.type === "readiness_start" || event.type === "task_start" || event.type === "control_start") {
|
|
1172
|
-
return event.type === "control_start" ? { ...base, knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options) } : base;
|
|
1173
|
-
}
|
|
1174
|
-
if (event.type === "readiness_end") {
|
|
1175
|
-
return { ...base, knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options) };
|
|
1176
|
-
}
|
|
1177
|
-
if (event.type === "questions_start") {
|
|
1178
|
-
return {
|
|
1179
|
-
...base,
|
|
1180
|
-
questions: event.questions.map((question) => sanitizeQuestion(question, options))
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1183
|
-
if (event.type === "questions_end") {
|
|
1184
|
-
return {
|
|
1185
|
-
...base,
|
|
1186
|
-
questions: event.questions.map((question) => sanitizeQuestion(question, options)),
|
|
1187
|
-
userAnswers: options.includeUserAnswers ? event.userAnswers : redactRecord(event.userAnswers)
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
|
-
if (event.type === "acquisition_start") {
|
|
1191
|
-
return { ...base, acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan) };
|
|
1192
|
-
}
|
|
1193
|
-
if (event.type === "acquisition_end") {
|
|
1194
|
-
return {
|
|
1195
|
-
...base,
|
|
1196
|
-
acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan),
|
|
1197
|
-
acquiredEvidenceCount: event.acquiredEvidenceIds.length,
|
|
1198
|
-
acquiredEvidenceIds: options.includeEvidenceIds ? event.acquiredEvidenceIds : void 0
|
|
1199
|
-
};
|
|
1200
|
-
}
|
|
1201
|
-
if (event.type === "control_step") {
|
|
1202
|
-
return { ...base, step: sanitizeControlStep(event.step, options) };
|
|
1203
|
-
}
|
|
1204
|
-
if (event.type === "control_end") {
|
|
1205
|
-
return { ...base, control: sanitizeControlRun(event.control, options) };
|
|
1206
|
-
}
|
|
1207
|
-
return { ...base, status: event.status, reason: event.reason };
|
|
1208
|
-
}
|
|
1209
|
-
function sanitizeRuntimeStreamEvent(event, options = {}) {
|
|
1210
|
-
const withTask = "task" in event && event.task ? { task: sanitizeTask(event.task, options) } : {};
|
|
1211
|
-
const withSession = "session" in event && event.session ? { session: sanitizeRuntimeSession(event.session, options) } : {};
|
|
1212
|
-
if (event.type === "readiness_end") {
|
|
1213
|
-
return {
|
|
1214
|
-
type: event.type,
|
|
1215
|
-
...withTask,
|
|
1216
|
-
timestamp: event.timestamp,
|
|
1217
|
-
decision: event.decision,
|
|
1218
|
-
knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options)
|
|
1219
|
-
};
|
|
1220
|
-
}
|
|
1221
|
-
if (event.type === "questions_start") {
|
|
1222
|
-
return {
|
|
1223
|
-
type: event.type,
|
|
1224
|
-
...withTask,
|
|
1225
|
-
timestamp: event.timestamp,
|
|
1226
|
-
questions: event.questions.map((question) => sanitizeQuestion(question, options))
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
if (event.type === "questions_end") {
|
|
1230
|
-
return {
|
|
1231
|
-
type: event.type,
|
|
1232
|
-
...withTask,
|
|
1233
|
-
timestamp: event.timestamp,
|
|
1234
|
-
questions: event.questions.map((question) => sanitizeQuestion(question, options)),
|
|
1235
|
-
userAnswers: options.includeUserAnswers ? event.userAnswers : redactRecord(event.userAnswers)
|
|
1236
|
-
};
|
|
1237
|
-
}
|
|
1238
|
-
if (event.type === "acquisition_start") {
|
|
1239
|
-
return {
|
|
1240
|
-
type: event.type,
|
|
1241
|
-
...withTask,
|
|
1242
|
-
timestamp: event.timestamp,
|
|
1243
|
-
acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan)
|
|
1244
|
-
};
|
|
1245
|
-
}
|
|
1246
|
-
if (event.type === "acquisition_end") {
|
|
1247
|
-
return {
|
|
1248
|
-
type: event.type,
|
|
1249
|
-
...withTask,
|
|
1250
|
-
timestamp: event.timestamp,
|
|
1251
|
-
acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan),
|
|
1252
|
-
acquiredEvidenceCount: event.acquiredEvidenceIds.length,
|
|
1253
|
-
acquiredEvidenceIds: options.includeEvidenceIds ? event.acquiredEvidenceIds : void 0
|
|
1254
|
-
};
|
|
1255
|
-
}
|
|
1256
|
-
if (event.type === "tool_call") {
|
|
1257
|
-
return {
|
|
1258
|
-
type: event.type,
|
|
1259
|
-
...withTask,
|
|
1260
|
-
...withSession,
|
|
1261
|
-
timestamp: event.timestamp,
|
|
1262
|
-
toolName: event.toolName,
|
|
1263
|
-
toolCallId: event.toolCallId,
|
|
1264
|
-
args: options.includeControlPayloads ? event.args : void 0
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
if (event.type === "tool_result") {
|
|
1268
|
-
return {
|
|
1269
|
-
type: event.type,
|
|
1270
|
-
...withTask,
|
|
1271
|
-
...withSession,
|
|
1272
|
-
timestamp: event.timestamp,
|
|
1273
|
-
toolName: event.toolName,
|
|
1274
|
-
toolCallId: event.toolCallId,
|
|
1275
|
-
result: options.includeControlPayloads ? event.result : void 0
|
|
1276
|
-
};
|
|
1277
|
-
}
|
|
1278
|
-
if (event.type === "llm_call") {
|
|
1279
|
-
return {
|
|
1280
|
-
type: event.type,
|
|
1281
|
-
...withTask,
|
|
1282
|
-
...withSession,
|
|
1283
|
-
timestamp: event.timestamp,
|
|
1284
|
-
model: event.model,
|
|
1285
|
-
tokensIn: event.tokensIn,
|
|
1286
|
-
tokensOut: event.tokensOut,
|
|
1287
|
-
costUsd: event.costUsd,
|
|
1288
|
-
latencyMs: event.latencyMs,
|
|
1289
|
-
finishReason: event.finishReason
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
if (event.type === "artifact") {
|
|
1293
|
-
return {
|
|
1294
|
-
type: event.type,
|
|
1295
|
-
...withTask,
|
|
1296
|
-
...withSession,
|
|
1297
|
-
timestamp: event.timestamp,
|
|
1298
|
-
artifactId: event.artifactId,
|
|
1299
|
-
name: event.name,
|
|
1300
|
-
mimeType: event.mimeType,
|
|
1301
|
-
uri: options.includeEvidenceIds ? event.uri : void 0,
|
|
1302
|
-
content: options.includeControlPayloads ? event.content : void 0,
|
|
1303
|
-
metadata: options.includeMetadata ? event.metadata : void 0
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
if (event.type === "proposal_created") {
|
|
1307
|
-
return {
|
|
1308
|
-
type: event.type,
|
|
1309
|
-
...withTask,
|
|
1310
|
-
...withSession,
|
|
1311
|
-
timestamp: event.timestamp,
|
|
1312
|
-
proposalId: event.proposalId,
|
|
1313
|
-
title: options.includeControlPayloads ? event.title : void 0,
|
|
1314
|
-
content: options.includeControlPayloads ? event.content : void 0,
|
|
1315
|
-
status: event.status
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
if (event.type === "final") {
|
|
1319
|
-
const sanitizedError = event.error !== void 0 ? {
|
|
1320
|
-
kind: event.error.kind,
|
|
1321
|
-
message: event.error.message,
|
|
1322
|
-
status: event.error.status,
|
|
1323
|
-
body: options.includeControlPayloads ? event.error.body : void 0
|
|
1324
|
-
} : void 0;
|
|
1325
|
-
return {
|
|
1326
|
-
type: event.type,
|
|
1327
|
-
...withTask,
|
|
1328
|
-
...withSession,
|
|
1329
|
-
timestamp: event.timestamp,
|
|
1330
|
-
status: event.status,
|
|
1331
|
-
reason: event.reason,
|
|
1332
|
-
text: options.includeControlPayloads ? event.text : void 0,
|
|
1333
|
-
metadata: options.includeMetadata ? event.metadata : void 0,
|
|
1334
|
-
...sanitizedError !== void 0 ? { error: sanitizedError } : {}
|
|
1335
|
-
};
|
|
1336
|
-
}
|
|
1337
|
-
return {
|
|
1338
|
-
type: event.type,
|
|
1339
|
-
...withTask,
|
|
1340
|
-
...withSession,
|
|
1341
|
-
timestamp: "timestamp" in event ? event.timestamp : void 0,
|
|
1342
|
-
...pickPublicStreamFields(event)
|
|
1343
|
-
};
|
|
1344
|
-
}
|
|
1345
|
-
function sanitizeTask(task, options) {
|
|
1346
|
-
return {
|
|
1347
|
-
id: task.id,
|
|
1348
|
-
intent: task.intent,
|
|
1349
|
-
domain: task.domain,
|
|
1350
|
-
inputs: options.includeInputs ? task.inputs : task.inputs ? "[redacted]" : void 0,
|
|
1351
|
-
requiredKnowledge: task.requiredKnowledge?.map(
|
|
1352
|
-
(requirement) => sanitizeKnowledgeRequirement(requirement, options)
|
|
1353
|
-
),
|
|
1354
|
-
metadata: options.includeMetadata ? task.metadata : task.metadata ? "[redacted]" : void 0
|
|
1355
|
-
};
|
|
1356
|
-
}
|
|
1357
|
-
function sanitizeRuntimeSession(session, options) {
|
|
1358
|
-
return {
|
|
1359
|
-
id: session.id,
|
|
1360
|
-
backend: session.backend,
|
|
1361
|
-
status: session.status,
|
|
1362
|
-
hasResumeToken: Boolean(session.resumeToken),
|
|
1363
|
-
createdAt: session.createdAt,
|
|
1364
|
-
updatedAt: session.updatedAt,
|
|
1365
|
-
metadata: options.includeMetadata ? session.metadata : session.metadata ? "[redacted]" : void 0
|
|
1366
|
-
};
|
|
1367
|
-
}
|
|
1368
|
-
function sanitizeKnowledgeRequirement(requirement, options) {
|
|
1369
|
-
const includeDescription = options.includeRequirementDescriptions && requirement.sensitivity !== "secret";
|
|
1370
|
-
return {
|
|
1371
|
-
id: requirement.id,
|
|
1372
|
-
description: includeDescription ? requirement.description : void 0,
|
|
1373
|
-
requiredFor: requirement.requiredFor,
|
|
1374
|
-
category: requirement.category,
|
|
1375
|
-
acquisitionMode: requirement.acquisitionMode,
|
|
1376
|
-
importance: requirement.importance,
|
|
1377
|
-
freshness: requirement.freshness,
|
|
1378
|
-
sensitivity: requirement.sensitivity,
|
|
1379
|
-
confidenceNeeded: requirement.confidenceNeeded,
|
|
1380
|
-
currentConfidence: requirement.currentConfidence,
|
|
1381
|
-
evidenceCount: requirement.evidenceIds.length,
|
|
1382
|
-
evidenceIds: options.includeEvidenceIds ? requirement.evidenceIds : void 0,
|
|
1383
|
-
fallbackPolicy: requirement.fallbackPolicy
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1386
|
-
function sanitizeQuestion(question, options) {
|
|
1387
|
-
return {
|
|
1388
|
-
id: question.id,
|
|
1389
|
-
question: options.includeRequirementDescriptions && question.answerType !== "credential" ? question.question : void 0,
|
|
1390
|
-
reason: options.includeRequirementDescriptions ? question.reason : void 0,
|
|
1391
|
-
requirementId: question.requirementId,
|
|
1392
|
-
importance: question.importance,
|
|
1393
|
-
answerType: question.answerType,
|
|
1394
|
-
impactIfUnknown: options.includeRequirementDescriptions ? question.impactIfUnknown : void 0,
|
|
1395
|
-
optionCount: question.options?.length ?? 0
|
|
1396
|
-
};
|
|
1397
|
-
}
|
|
1398
|
-
function sanitizeAcquisitionPlan(plan) {
|
|
1399
|
-
return {
|
|
1400
|
-
id: plan.id,
|
|
1401
|
-
requirementIds: plan.requirementIds,
|
|
1402
|
-
mode: plan.mode,
|
|
1403
|
-
priority: plan.priority,
|
|
1404
|
-
expectedEvidenceCount: plan.expectedEvidenceIds?.length ?? 0,
|
|
1405
|
-
questionCount: plan.questions?.length ?? 0
|
|
1406
|
-
};
|
|
1407
|
-
}
|
|
1408
|
-
function sanitizeControlStep(step, options) {
|
|
1409
|
-
const actionOutcome = step.actionOutcome;
|
|
1410
|
-
return {
|
|
1411
|
-
index: step.index,
|
|
1412
|
-
decisionType: step.decision.type,
|
|
1413
|
-
reason: step.decision.reason,
|
|
1414
|
-
action: options.includeControlPayloads && step.decision.type === "continue" ? step.decision.action : void 0,
|
|
1415
|
-
result: options.includeControlPayloads && actionOutcome?.ok ? actionOutcome.result : void 0,
|
|
1416
|
-
actionOk: actionOutcome?.ok,
|
|
1417
|
-
actionError: actionOutcome?.ok === false ? actionOutcome.error : void 0,
|
|
1418
|
-
durationMs: actionOutcome?.durationMs,
|
|
1419
|
-
evalsBefore: summarizeEvals(step.evalsBefore, options),
|
|
1420
|
-
evalsAfter: summarizeEvals(step.evalsAfter, options),
|
|
1421
|
-
startedAt: step.startedAt,
|
|
1422
|
-
endedAt: step.endedAt
|
|
1423
|
-
};
|
|
1424
|
-
}
|
|
1425
|
-
function sanitizeControlRun(control, options) {
|
|
1426
|
-
return {
|
|
1427
|
-
pass: control.pass,
|
|
1428
|
-
completed: control.completed,
|
|
1429
|
-
reason: control.reason,
|
|
1430
|
-
score: control.score,
|
|
1431
|
-
stepCount: control.steps.length,
|
|
1432
|
-
wallMs: control.wallMs,
|
|
1433
|
-
spentCostUsd: control.spentCostUsd,
|
|
1434
|
-
failureClass: control.failureClass,
|
|
1435
|
-
stoppedBy: control.stoppedBy,
|
|
1436
|
-
runId: control.runId,
|
|
1437
|
-
runtimeErrorCount: control.runtimeErrors.length,
|
|
1438
|
-
finalEvals: summarizeEvals(control.finalEvals, options)
|
|
1439
|
-
};
|
|
1440
|
-
}
|
|
1441
|
-
function summarizeEvals(evals, options) {
|
|
1442
|
-
return evals.map((evalResult) => ({
|
|
1443
|
-
id: evalResult.id,
|
|
1444
|
-
passed: evalResult.passed,
|
|
1445
|
-
score: evalResult.score,
|
|
1446
|
-
severity: evalResult.severity,
|
|
1447
|
-
objective: evalResult.objective,
|
|
1448
|
-
detail: options.includeEvalDetails ? evalResult.detail : void 0,
|
|
1449
|
-
evidence: options.includeEvalDetails ? evalResult.evidence : void 0
|
|
1450
|
-
}));
|
|
1451
|
-
}
|
|
1452
|
-
function redactRecord(record) {
|
|
1453
|
-
return Object.fromEntries(Object.keys(record).map((key) => [key, "[redacted]"]));
|
|
1454
|
-
}
|
|
1455
|
-
function pickPublicStreamFields(event) {
|
|
1456
|
-
if (event.type === "session_created" || event.type === "session_resumed") return {};
|
|
1457
|
-
if (event.type === "backend_start" || event.type === "backend_end")
|
|
1458
|
-
return { backend: event.backend };
|
|
1459
|
-
if (event.type === "backend_error") {
|
|
1460
|
-
const sanitizedError = event.error !== void 0 ? {
|
|
1461
|
-
kind: event.error.kind,
|
|
1462
|
-
status: event.error.status
|
|
1463
|
-
} : void 0;
|
|
1464
|
-
return {
|
|
1465
|
-
backend: event.backend,
|
|
1466
|
-
message: event.message,
|
|
1467
|
-
recoverable: event.recoverable,
|
|
1468
|
-
...sanitizedError !== void 0 ? { error: sanitizedError } : {}
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
|
-
if (event.type === "task_end") return { status: event.status, reason: event.reason };
|
|
1472
|
-
if (event.type === "text_delta" || event.type === "reasoning_delta") return { text: event.text };
|
|
1473
|
-
return {};
|
|
1474
|
-
}
|
|
1475
|
-
function createRuntimeEventCollector(options = {}) {
|
|
1476
|
-
const events = [];
|
|
1477
|
-
return {
|
|
1478
|
-
events,
|
|
1479
|
-
onEvent: (event) => {
|
|
1480
|
-
events.push(sanitizeAgentRuntimeEvent(event, options));
|
|
1481
|
-
}
|
|
1482
|
-
};
|
|
1483
|
-
}
|
|
1484
|
-
function createRuntimeStreamEventCollector(options = {}) {
|
|
1485
|
-
const events = [];
|
|
1486
|
-
const eventCountsByType = {};
|
|
1487
|
-
let firstSessionId;
|
|
1488
|
-
let finalStatus;
|
|
1489
|
-
let finalReason;
|
|
1490
|
-
let finalText = "";
|
|
1491
|
-
return {
|
|
1492
|
-
events,
|
|
1493
|
-
onEvent: (event) => {
|
|
1494
|
-
events.push(sanitizeRuntimeStreamEvent(event, options));
|
|
1495
|
-
eventCountsByType[event.type] = (eventCountsByType[event.type] ?? 0) + 1;
|
|
1496
|
-
if (event.type === "text_delta") finalText += event.text;
|
|
1497
|
-
if (!firstSessionId && (event.type === "session_created" || event.type === "session_resumed")) {
|
|
1498
|
-
firstSessionId = event.session.id;
|
|
1499
|
-
}
|
|
1500
|
-
if (event.type === "final") {
|
|
1501
|
-
finalStatus = event.status;
|
|
1502
|
-
finalReason = event.reason;
|
|
1503
|
-
}
|
|
1504
|
-
},
|
|
1505
|
-
summary() {
|
|
1506
|
-
return {
|
|
1507
|
-
eventCount: events.length,
|
|
1508
|
-
eventCountsByType: { ...eventCountsByType },
|
|
1509
|
-
firstSessionId,
|
|
1510
|
-
finalStatus,
|
|
1511
|
-
finalReason,
|
|
1512
|
-
finalText
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
|
-
};
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
// src/otel-export.ts
|
|
1519
|
-
var SCOPE = { name: "@tangle-network/agent-runtime", version: "0.83.0" };
|
|
1520
|
-
var GEN_AI = {
|
|
1521
|
-
operation: "gen_ai.operation.name",
|
|
1522
|
-
agentName: "gen_ai.agent.name",
|
|
1523
|
-
conversationId: "gen_ai.conversation.id",
|
|
1524
|
-
inputTokens: "gen_ai.usage.input_tokens",
|
|
1525
|
-
outputTokens: "gen_ai.usage.output_tokens"
|
|
1526
|
-
};
|
|
1527
|
-
function createOtelExporter(config) {
|
|
1528
|
-
const resolvedEndpoint = config?.endpoint ?? (typeof process !== "undefined" ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT : void 0);
|
|
1529
|
-
if (!resolvedEndpoint) return void 0;
|
|
1530
|
-
const endpoint = resolvedEndpoint;
|
|
1531
|
-
const headers = config?.headers ?? parseHeadersFromEnv();
|
|
1532
|
-
const batchSize = config?.batchSize ?? 64;
|
|
1533
|
-
const flushIntervalMs = config?.flushIntervalMs ?? 5e3;
|
|
1534
|
-
const serviceName = config?.serviceName ?? "agent-runtime";
|
|
1535
|
-
const resourceAttrs = config?.resourceAttributes ?? {};
|
|
1536
|
-
const pending = [];
|
|
1537
|
-
let timer;
|
|
1538
|
-
let stopped = false;
|
|
1539
|
-
const exporter = {
|
|
1540
|
-
exportSpan(span) {
|
|
1541
|
-
if (stopped) return;
|
|
1542
|
-
pending.push(span);
|
|
1543
|
-
if (pending.length >= batchSize) {
|
|
1544
|
-
void doFlush();
|
|
1545
|
-
}
|
|
1546
|
-
},
|
|
1547
|
-
async flush() {
|
|
1548
|
-
await doFlush();
|
|
1549
|
-
},
|
|
1550
|
-
async shutdown() {
|
|
1551
|
-
stopped = true;
|
|
1552
|
-
if (timer !== void 0) {
|
|
1553
|
-
clearInterval(timer);
|
|
1554
|
-
timer = void 0;
|
|
1555
|
-
}
|
|
1556
|
-
await doFlush();
|
|
1557
|
-
}
|
|
1558
|
-
};
|
|
1559
|
-
timer = setInterval(() => {
|
|
1560
|
-
if (pending.length > 0) void doFlush();
|
|
1561
|
-
}, flushIntervalMs);
|
|
1562
|
-
if (typeof timer === "object" && "unref" in timer) {
|
|
1563
|
-
;
|
|
1564
|
-
timer.unref();
|
|
1565
|
-
}
|
|
1566
|
-
async function doFlush() {
|
|
1567
|
-
if (pending.length === 0) return;
|
|
1568
|
-
const batch = pending.splice(0);
|
|
1569
|
-
const body = {
|
|
1570
|
-
resourceSpans: [
|
|
1571
|
-
{
|
|
1572
|
-
resource: {
|
|
1573
|
-
attributes: toAttributes({
|
|
1574
|
-
"service.name": serviceName,
|
|
1575
|
-
...resourceAttrs
|
|
1576
|
-
})
|
|
1577
|
-
},
|
|
1578
|
-
scopeSpans: [{ scope: SCOPE, spans: batch }]
|
|
1579
|
-
}
|
|
1580
|
-
]
|
|
1581
|
-
};
|
|
1582
|
-
const url = `${endpoint.replace(/\/+$/, "")}/v1/traces`;
|
|
1583
|
-
try {
|
|
1584
|
-
await fetch(url, {
|
|
1585
|
-
method: "POST",
|
|
1586
|
-
headers: { "content-type": "application/json", ...headers },
|
|
1587
|
-
body: JSON.stringify(body)
|
|
1588
|
-
});
|
|
1589
|
-
} catch {
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
return exporter;
|
|
1593
|
-
}
|
|
1594
|
-
function loopEventToOtelSpan(event, traceId, parentSpanId) {
|
|
1595
|
-
const spanId = generateSpanId();
|
|
1596
|
-
const attrs = {
|
|
1597
|
-
"loop.event_kind": event.kind,
|
|
1598
|
-
"loop.run_id": event.runId
|
|
1599
|
-
};
|
|
1600
|
-
for (const [k, v] of Object.entries(event.payload)) {
|
|
1601
|
-
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
1602
|
-
attrs[`loop.${k}`] = v;
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
const ts = msToNs(event.timestamp);
|
|
1606
|
-
return {
|
|
1607
|
-
traceId: padTraceId(traceId),
|
|
1608
|
-
spanId,
|
|
1609
|
-
parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
|
|
1610
|
-
name: event.kind,
|
|
1611
|
-
kind: 1,
|
|
1612
|
-
startTimeUnixNano: ts,
|
|
1613
|
-
endTimeUnixNano: ts,
|
|
1614
|
-
attributes: toAttributes(attrs),
|
|
1615
|
-
status: { code: 1 }
|
|
1616
|
-
};
|
|
1617
|
-
}
|
|
1618
|
-
function flatOtelSpan(name, attributes, traceId, timestampMs, parentSpanId, endTimestampMs = timestampMs) {
|
|
1619
|
-
const start = msToNs(timestampMs);
|
|
1620
|
-
const end = msToNs(Math.max(timestampMs, endTimestampMs));
|
|
1621
|
-
return {
|
|
1622
|
-
traceId: padTraceId(traceId),
|
|
1623
|
-
spanId: generateSpanId(),
|
|
1624
|
-
parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
|
|
1625
|
-
name,
|
|
1626
|
-
kind: 1,
|
|
1627
|
-
startTimeUnixNano: start,
|
|
1628
|
-
endTimeUnixNano: end,
|
|
1629
|
-
attributes: toAttributes(attributes),
|
|
1630
|
-
status: { code: 1 }
|
|
1631
|
-
};
|
|
1632
|
-
}
|
|
1633
|
-
function eventTimestampMs(event) {
|
|
1634
|
-
if ("timestamp" in event && typeof event.timestamp === "string") {
|
|
1635
|
-
const parsed = Date.parse(event.timestamp);
|
|
1636
|
-
if (Number.isFinite(parsed)) return parsed;
|
|
1637
|
-
}
|
|
1638
|
-
return Date.now();
|
|
1639
|
-
}
|
|
1640
|
-
function serialized(value) {
|
|
1641
|
-
try {
|
|
1642
|
-
return JSON.stringify(value) ?? String(value);
|
|
1643
|
-
} catch {
|
|
1644
|
-
return String(value);
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
function mcpIdentity(toolName) {
|
|
1648
|
-
if (!toolName.startsWith("mcp__")) return {};
|
|
1649
|
-
const [, server, ...toolParts] = toolName.split("__");
|
|
1650
|
-
return {
|
|
1651
|
-
...server ? { server } : {},
|
|
1652
|
-
...toolParts.length > 0 ? { tool: toolParts.join("__") } : {}
|
|
1653
|
-
};
|
|
1654
|
-
}
|
|
1655
|
-
function buildRuntimeEventOtelSpans(events, traceId, parentSpanId, options = {}) {
|
|
1656
|
-
return events.map((event) => {
|
|
1657
|
-
const sanitized = sanitizeRuntimeStreamEvent(event, options);
|
|
1658
|
-
const safe = options.redact ? options.redact(sanitized) : sanitized;
|
|
1659
|
-
const record = safe && typeof safe === "object" && !Array.isArray(safe) ? safe : { value: safe };
|
|
1660
|
-
const attrs = {
|
|
1661
|
-
"tangle.runtime.event_type": event.type,
|
|
1662
|
-
"tangle.runtime.event": serialized(record)
|
|
1663
|
-
};
|
|
1664
|
-
let name = `tangle.runtime.${event.type}`;
|
|
1665
|
-
if (event.type === "tool_call" || event.type === "tool_result") {
|
|
1666
|
-
name = `agent.${event.type}`;
|
|
1667
|
-
attrs["tool.name"] = event.toolName;
|
|
1668
|
-
if (event.toolCallId) attrs["tool.call_id"] = event.toolCallId;
|
|
1669
|
-
const mcp = mcpIdentity(event.toolName);
|
|
1670
|
-
if (mcp.server) attrs["mcp.server"] = mcp.server;
|
|
1671
|
-
if (mcp.tool) attrs["mcp.tool.name"] = mcp.tool;
|
|
1672
|
-
const payload = event.type === "tool_call" ? record.args : record.result;
|
|
1673
|
-
if (payload !== void 0) {
|
|
1674
|
-
attrs[event.type === "tool_call" ? "tool.input" : "tool.output"] = serialized(payload);
|
|
1675
|
-
}
|
|
1676
|
-
} else if (event.type === "llm_call") {
|
|
1677
|
-
name = "gen_ai.client.inference";
|
|
1678
|
-
attrs["gen_ai.request.model"] = event.model;
|
|
1679
|
-
if (event.tokensIn !== void 0) attrs["gen_ai.usage.input_tokens"] = event.tokensIn;
|
|
1680
|
-
if (event.tokensOut !== void 0) attrs["gen_ai.usage.output_tokens"] = event.tokensOut;
|
|
1681
|
-
if (event.costUsd !== void 0) attrs["tangle.cost.usd"] = event.costUsd;
|
|
1682
|
-
if (event.latencyMs !== void 0) attrs["tangle.latency_ms"] = event.latencyMs;
|
|
1683
|
-
if (event.finishReason !== void 0)
|
|
1684
|
-
attrs["gen_ai.response.finish_reasons"] = event.finishReason;
|
|
1685
|
-
} else if (event.type === "backend_error") {
|
|
1686
|
-
attrs["error.type"] = event.error?.kind ?? "backend";
|
|
1687
|
-
attrs["error.message"] = event.message;
|
|
1688
|
-
} else if (event.type === "final") {
|
|
1689
|
-
attrs["tangle.outcome.status"] = event.status;
|
|
1690
|
-
attrs["tangle.outcome.reason"] = event.reason;
|
|
1691
|
-
if (event.error) {
|
|
1692
|
-
attrs["error.type"] = event.error.kind;
|
|
1693
|
-
attrs["error.message"] = event.error.message;
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
const startMs = eventTimestampMs(event);
|
|
1697
|
-
const endMs = event.type === "llm_call" && event.latencyMs !== void 0 && Number.isFinite(event.latencyMs) ? startMs + event.latencyMs : startMs;
|
|
1698
|
-
const span = flatOtelSpan(name, attrs, traceId, startMs, parentSpanId, endMs);
|
|
1699
|
-
if (event.type === "backend_error" || event.type === "final" && event.status !== "completed") {
|
|
1700
|
-
span.status = { code: 2, message: attrs["error.message"]?.toString() ?? event.type };
|
|
1701
|
-
}
|
|
1702
|
-
return span;
|
|
1703
|
-
});
|
|
1704
|
-
}
|
|
1705
|
-
function buildLoopOtelSpans(events, traceId, rootParentSpanId) {
|
|
1706
|
-
const tid = padTraceId(traceId);
|
|
1707
|
-
return buildLoopSpanNodes(events).map((node) => ({
|
|
1708
|
-
traceId: tid,
|
|
1709
|
-
spanId: node.spanId,
|
|
1710
|
-
parentSpanId: node.parentSpanId ? padSpanId(node.parentSpanId) : rootParentSpanId ? padSpanId(rootParentSpanId) : void 0,
|
|
1711
|
-
name: node.name,
|
|
1712
|
-
kind: 1,
|
|
1713
|
-
startTimeUnixNano: msToNs(node.startMs),
|
|
1714
|
-
endTimeUnixNano: msToNs(node.endMs),
|
|
1715
|
-
attributes: toAttributes(node.attrs),
|
|
1716
|
-
status: { code: node.error ? 2 : 1 }
|
|
1717
|
-
}));
|
|
1718
|
-
}
|
|
1719
|
-
function buildLoopSpanNodes(events) {
|
|
1720
|
-
if (events.length === 0) return [];
|
|
1721
|
-
const out = [];
|
|
1722
|
-
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
1723
|
-
const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
1724
|
-
const rec = (v) => v && typeof v === "object" ? v : {};
|
|
1725
|
-
const started = events.find((e) => e.kind === "loop.started");
|
|
1726
|
-
const ended = events.find((e) => e.kind === "loop.ended");
|
|
1727
|
-
const runId = events[0]?.runId ?? "";
|
|
1728
|
-
const rootStart = started?.timestamp ?? events[0].timestamp;
|
|
1729
|
-
const rootEnd = ended?.timestamp ?? events[events.length - 1].timestamp;
|
|
1730
|
-
const rootId = generateSpanId();
|
|
1731
|
-
const make = (spanId, parentSpanId, name, kind, startMs, endMs, attrs, error = false) => ({
|
|
1732
|
-
spanId,
|
|
1733
|
-
parentSpanId,
|
|
1734
|
-
name,
|
|
1735
|
-
kind,
|
|
1736
|
-
startMs,
|
|
1737
|
-
endMs,
|
|
1738
|
-
attrs,
|
|
1739
|
-
error
|
|
1740
|
-
});
|
|
1741
|
-
const sp = rec(started?.payload);
|
|
1742
|
-
const rootAttrs = {
|
|
1743
|
-
[GEN_AI.operation]: "invoke_workflow",
|
|
1744
|
-
[GEN_AI.conversationId]: runId,
|
|
1745
|
-
// Explicit run identity under the tangle namespace so a consuming system of
|
|
1746
|
-
// record (Tangle Intelligence's run spine) maps this span tree to one run
|
|
1747
|
-
// deterministically, instead of inferring it from the conversation id.
|
|
1748
|
-
"tangle.run.id": runId,
|
|
1749
|
-
"tangle.loop.driver": str(sp.driver) ?? "driver"
|
|
1750
|
-
};
|
|
1751
|
-
if (Array.isArray(sp.agentRunNames) && sp.agentRunNames.length > 0) {
|
|
1752
|
-
rootAttrs["tangle.loop.agents"] = sp.agentRunNames.map(String).join(",");
|
|
1753
|
-
rootAttrs["tangle.subject.key"] = String(sp.agentRunNames[0]);
|
|
1754
|
-
}
|
|
1755
|
-
if (ended) {
|
|
1756
|
-
const ep = rec(ended.payload);
|
|
1757
|
-
const win = num(ep.winnerIterationIndex);
|
|
1758
|
-
if (win !== void 0) rootAttrs["tangle.loop.winner.iteration_index"] = win;
|
|
1759
|
-
const cost = num(ep.totalCostUsd);
|
|
1760
|
-
if (cost !== void 0) rootAttrs["tangle.cost.usd"] = cost;
|
|
1761
|
-
const dur = num(ep.durationMs);
|
|
1762
|
-
if (dur !== void 0) rootAttrs["tangle.loop.duration_ms"] = dur;
|
|
1763
|
-
const iters = num(ep.iterations);
|
|
1764
|
-
if (iters !== void 0) rootAttrs["tangle.loop.iterations"] = iters;
|
|
1765
|
-
}
|
|
1766
|
-
out.push(make(rootId, void 0, "loop", "loop", rootStart, rootEnd, rootAttrs));
|
|
1767
|
-
const iterStartTs = /* @__PURE__ */ new Map();
|
|
1768
|
-
const placementByIdx = /* @__PURE__ */ new Map();
|
|
1769
|
-
let currentRoundId;
|
|
1770
|
-
let pendingRound;
|
|
1771
|
-
const flushRound = (endMs) => {
|
|
1772
|
-
if (!pendingRound) return;
|
|
1773
|
-
out.push(
|
|
1774
|
-
make(
|
|
1775
|
-
pendingRound.id,
|
|
1776
|
-
rootId,
|
|
1777
|
-
"loop.round",
|
|
1778
|
-
"round",
|
|
1779
|
-
pendingRound.start,
|
|
1780
|
-
endMs,
|
|
1781
|
-
pendingRound.attrs
|
|
1782
|
-
)
|
|
1783
|
-
);
|
|
1784
|
-
pendingRound = void 0;
|
|
1785
|
-
};
|
|
1786
|
-
for (const e of events) {
|
|
1787
|
-
const p = rec(e.payload);
|
|
1788
|
-
switch (e.kind) {
|
|
1789
|
-
case "loop.plan": {
|
|
1790
|
-
flushRound(e.timestamp);
|
|
1791
|
-
const id = generateSpanId();
|
|
1792
|
-
const roundIdx = num(p.roundIndex) ?? 0;
|
|
1793
|
-
const attrs = {
|
|
1794
|
-
[GEN_AI.operation]: "invoke_workflow",
|
|
1795
|
-
"tangle.loop.round.index": roundIdx,
|
|
1796
|
-
"tangle.loop.move.kind": str(p.moveKind) ?? "unknown",
|
|
1797
|
-
"tangle.loop.move.round": roundIdx,
|
|
1798
|
-
"tangle.loop.move.width": num(p.plannedCount) ?? 0
|
|
1799
|
-
};
|
|
1800
|
-
const r = str(p.rationale);
|
|
1801
|
-
if (r) attrs["tangle.loop.move.rationale"] = r;
|
|
1802
|
-
const parent = num(p.parentIndex);
|
|
1803
|
-
if (parent !== void 0) attrs["tangle.loop.move.parent_index"] = parent;
|
|
1804
|
-
if (Array.isArray(p.childIndices) && p.childIndices.length > 0) {
|
|
1805
|
-
attrs["tangle.loop.move.child_indices"] = p.childIndices.map(String).join(",");
|
|
1806
|
-
}
|
|
1807
|
-
pendingRound = { id, start: e.timestamp, attrs };
|
|
1808
|
-
currentRoundId = id;
|
|
1809
|
-
break;
|
|
1810
|
-
}
|
|
1811
|
-
case "loop.iteration.started": {
|
|
1812
|
-
const idx = num(p.iterationIndex);
|
|
1813
|
-
if (idx !== void 0) iterStartTs.set(idx, e.timestamp);
|
|
1814
|
-
break;
|
|
1815
|
-
}
|
|
1816
|
-
case "loop.iteration.dispatch": {
|
|
1817
|
-
const idx = num(p.iterationIndex);
|
|
1818
|
-
if (idx === void 0) break;
|
|
1819
|
-
const place = {};
|
|
1820
|
-
const kind = str(p.placement);
|
|
1821
|
-
if (kind) place["tangle.loop.placement.kind"] = kind;
|
|
1822
|
-
const sid = str(p.sandboxId);
|
|
1823
|
-
if (sid) place["tangle.sandbox.id"] = sid;
|
|
1824
|
-
const fid = str(p.fleetId);
|
|
1825
|
-
if (fid) place["tangle.fleet.id"] = fid;
|
|
1826
|
-
const mid = str(p.machineId);
|
|
1827
|
-
if (mid) place["tangle.machine.id"] = mid;
|
|
1828
|
-
placementByIdx.set(idx, place);
|
|
1829
|
-
break;
|
|
1830
|
-
}
|
|
1831
|
-
case "loop.iteration.ended": {
|
|
1832
|
-
const idx = num(p.iterationIndex) ?? 0;
|
|
1833
|
-
const start = iterStartTs.get(idx) ?? e.timestamp;
|
|
1834
|
-
const err = str(p.error);
|
|
1835
|
-
const attrs = {
|
|
1836
|
-
[GEN_AI.operation]: "invoke_agent",
|
|
1837
|
-
"tangle.loop.iteration.index": idx
|
|
1838
|
-
};
|
|
1839
|
-
const agent = str(p.agentRunName);
|
|
1840
|
-
if (agent) attrs[GEN_AI.agentName] = agent;
|
|
1841
|
-
const tu = rec(p.tokenUsage);
|
|
1842
|
-
const inTok = num(tu.input);
|
|
1843
|
-
if (inTok !== void 0) attrs[GEN_AI.inputTokens] = inTok;
|
|
1844
|
-
const outTok = num(tu.output);
|
|
1845
|
-
if (outTok !== void 0) attrs[GEN_AI.outputTokens] = outTok;
|
|
1846
|
-
const cost = num(p.costUsd);
|
|
1847
|
-
if (cost !== void 0) attrs["tangle.cost.usd"] = cost;
|
|
1848
|
-
const verdict = rec(p.verdict);
|
|
1849
|
-
if (typeof verdict.valid === "boolean") attrs["tangle.loop.verdict.valid"] = verdict.valid;
|
|
1850
|
-
const score = num(verdict.score);
|
|
1851
|
-
if (score !== void 0) attrs["tangle.loop.verdict.score"] = score;
|
|
1852
|
-
if (err) attrs["tangle.loop.error"] = err;
|
|
1853
|
-
const gid = num(p.groupId);
|
|
1854
|
-
if (gid !== void 0) attrs["tangle.loop.iteration.group_id"] = gid;
|
|
1855
|
-
const par = num(p.parentIndex);
|
|
1856
|
-
if (par !== void 0) attrs["tangle.loop.iteration.parent_index"] = par;
|
|
1857
|
-
const dur = num(p.durationMs);
|
|
1858
|
-
if (dur !== void 0) attrs["tangle.loop.iteration.duration_ms"] = dur;
|
|
1859
|
-
const preview = str(p.outputPreview);
|
|
1860
|
-
if (preview) attrs["tangle.loop.iteration.output_preview"] = preview;
|
|
1861
|
-
Object.assign(attrs, placementByIdx.get(idx) ?? {});
|
|
1862
|
-
out.push(
|
|
1863
|
-
make(
|
|
1864
|
-
generateSpanId(),
|
|
1865
|
-
currentRoundId ?? rootId,
|
|
1866
|
-
"loop.iteration",
|
|
1867
|
-
"branch",
|
|
1868
|
-
start,
|
|
1869
|
-
e.timestamp,
|
|
1870
|
-
attrs,
|
|
1871
|
-
err !== void 0
|
|
1872
|
-
)
|
|
1873
|
-
);
|
|
1874
|
-
break;
|
|
1875
|
-
}
|
|
1876
|
-
case "loop.decision": {
|
|
1877
|
-
if (pendingRound) {
|
|
1878
|
-
const dec = str(p.decision);
|
|
1879
|
-
if (dec) pendingRound.attrs["tangle.loop.decision"] = dec;
|
|
1880
|
-
flushRound(e.timestamp);
|
|
1881
|
-
}
|
|
1882
|
-
currentRoundId = void 0;
|
|
1883
|
-
break;
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
flushRound(rootEnd);
|
|
1888
|
-
return out;
|
|
1889
|
-
}
|
|
1890
|
-
function parseHeadersFromEnv() {
|
|
1891
|
-
if (typeof process === "undefined") return {};
|
|
1892
|
-
const raw = process.env.OTEL_EXPORTER_OTLP_HEADERS;
|
|
1893
|
-
if (!raw) return {};
|
|
1894
|
-
const out = {};
|
|
1895
|
-
for (const pair of raw.split(",")) {
|
|
1896
|
-
const eq = pair.indexOf("=");
|
|
1897
|
-
if (eq < 0) continue;
|
|
1898
|
-
const key = pair.slice(0, eq).trim();
|
|
1899
|
-
const value = pair.slice(eq + 1).trim();
|
|
1900
|
-
if (key) out[key] = value;
|
|
1901
|
-
}
|
|
1902
|
-
return out;
|
|
1903
|
-
}
|
|
1904
|
-
function toAttributes(record) {
|
|
1905
|
-
return Object.entries(record).flatMap(([key, value]) => {
|
|
1906
|
-
if (typeof value === "number" && !Number.isFinite(value)) return [];
|
|
1907
|
-
return [
|
|
1908
|
-
{
|
|
1909
|
-
key,
|
|
1910
|
-
value: typeof value === "number" ? Number.isInteger(value) ? { intValue: value.toString() } : { doubleValue: value } : typeof value === "boolean" ? { boolValue: value } : { stringValue: value }
|
|
1911
|
-
}
|
|
1912
|
-
];
|
|
1913
|
-
});
|
|
1914
|
-
}
|
|
1915
|
-
function msToNs(ms) {
|
|
1916
|
-
const safeMs = Number.isFinite(ms) ? ms : Date.now();
|
|
1917
|
-
return (BigInt(Math.floor(safeMs)) * 1000000n).toString();
|
|
1918
|
-
}
|
|
1919
|
-
function padSpanId(id) {
|
|
1920
|
-
const cleaned = id.replace(/-/g, "");
|
|
1921
|
-
return cleaned.slice(0, 16).padEnd(16, "0");
|
|
1922
|
-
}
|
|
1923
|
-
function padTraceId(id) {
|
|
1924
|
-
const cleaned = id.replace(/-/g, "");
|
|
1925
|
-
return cleaned.slice(0, 32).padEnd(32, "0");
|
|
1926
|
-
}
|
|
1927
|
-
function generateSpanId() {
|
|
1928
|
-
const bytes = new Uint8Array(8);
|
|
1929
|
-
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
1930
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
1931
|
-
} else {
|
|
1932
|
-
for (let i = 0; i < 8; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
1933
|
-
}
|
|
1934
|
-
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1935
|
-
}
|
|
1936
|
-
var INTELLIGENCE_WIRE_VERSION = "2026-05-26.v1";
|
|
1937
|
-
var DEFAULT_INTELLIGENCE_BASE = "https://intelligence.tangle.tools";
|
|
1938
|
-
async function exportEvalRuns(events, config) {
|
|
1939
|
-
if (events.length === 0) return { ok: true, status: 0, accepted: 0, rejected: [] };
|
|
1940
|
-
const apiKey = config?.apiKey ?? (typeof process !== "undefined" ? process.env.TANGLE_API_KEY : void 0);
|
|
1941
|
-
if (!apiKey)
|
|
1942
|
-
throw new Error("exportEvalRuns: apiKey required (pass config.apiKey or set TANGLE_API_KEY)");
|
|
1943
|
-
const base = config?.base ?? (typeof process !== "undefined" ? process.env.TANGLE_INTELLIGENCE_URL : void 0) ?? DEFAULT_INTELLIGENCE_BASE;
|
|
1944
|
-
const url = `${base.replace(/\/+$/, "")}/v1/ingest/eval-runs`;
|
|
1945
|
-
const res = await fetch(url, {
|
|
1946
|
-
method: "POST",
|
|
1947
|
-
headers: {
|
|
1948
|
-
"content-type": "application/json",
|
|
1949
|
-
authorization: `Bearer ${apiKey}`,
|
|
1950
|
-
"X-Tangle-Wire-Version": INTELLIGENCE_WIRE_VERSION,
|
|
1951
|
-
...config?.idempotencyKey ? { "Idempotency-Key": config.idempotencyKey } : {}
|
|
1952
|
-
},
|
|
1953
|
-
body: JSON.stringify({ wireVersion: INTELLIGENCE_WIRE_VERSION, events })
|
|
1954
|
-
});
|
|
1955
|
-
let parsed = {};
|
|
1956
|
-
try {
|
|
1957
|
-
parsed = await res.json();
|
|
1958
|
-
} catch {
|
|
1959
|
-
}
|
|
1960
|
-
return {
|
|
1961
|
-
ok: res.ok,
|
|
1962
|
-
status: res.status,
|
|
1963
|
-
accepted: parsed.accepted ?? (res.ok ? events.length : 0),
|
|
1964
|
-
rejected: parsed.rejected ?? []
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
|
|
1968
|
-
// src/runtime/supervise/driver-executor.ts
|
|
1969
|
-
var driverRuntime = "driver";
|
|
1970
|
-
var driverRole = "driver";
|
|
1971
|
-
function isDriverSpec(spec) {
|
|
1972
|
-
const role = spec.profile.metadata?.role;
|
|
1973
|
-
if (role !== driverRole) return false;
|
|
1974
|
-
const driver = spec.driver;
|
|
1975
|
-
if (!isAgent(driver)) {
|
|
1976
|
-
throw new ValidationError(
|
|
1977
|
-
"driverExecutor: a driver-role spec must carry a `driver` Agent to run inside its nested scope"
|
|
1978
|
-
);
|
|
1979
|
-
}
|
|
1980
|
-
return true;
|
|
1981
|
-
}
|
|
1982
|
-
var driverExecutorFactory = (spec, ctx) => {
|
|
1983
|
-
if (!isDriverSpec(spec)) {
|
|
1984
|
-
throw new ValidationError(
|
|
1985
|
-
'driverExecutorFactory: spec is not a driver child (no role:"driver" marker)'
|
|
1986
|
-
);
|
|
1987
|
-
}
|
|
1988
|
-
const driver = spec.driver;
|
|
1989
|
-
const journal = spec.journal;
|
|
1990
|
-
const seam = readNestedScopeSeam(ctx);
|
|
1991
|
-
let artifact;
|
|
1992
|
-
let meteredSpend;
|
|
1993
|
-
return {
|
|
1994
|
-
runtime: driverRuntime,
|
|
1995
|
-
async execute(task, signal) {
|
|
1996
|
-
const nestedRoot = nestedTreeKey(seam, journal);
|
|
1997
|
-
await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
|
|
1998
|
-
const nestedScope = seam.mount(nestedRoot, signal);
|
|
1999
|
-
try {
|
|
2000
|
-
const out = await driver.act(task, nestedScope);
|
|
2001
|
-
const events = await loadTreeEvents(journal, nestedRoot);
|
|
2002
|
-
const settled = events.filter(isSettled);
|
|
2003
|
-
meteredSpend = nonZeroOrUndef(sumMetered(events));
|
|
2004
|
-
const verdict = deriveDeliveryVerdict(settled);
|
|
2005
|
-
artifact = {
|
|
2006
|
-
outRef: `${driverRuntime}:${nestedRoot}`,
|
|
2007
|
-
out,
|
|
2008
|
-
spent: sumSpend(settled),
|
|
2009
|
-
...verdict ? { verdict } : {}
|
|
2010
|
-
};
|
|
2011
|
-
return artifact;
|
|
2012
|
-
} catch (err) {
|
|
2013
|
-
meteredSpend = await safeSumMetered(journal, nestedRoot);
|
|
2014
|
-
throw err;
|
|
2015
|
-
}
|
|
2016
|
-
},
|
|
2017
|
-
metered() {
|
|
2018
|
-
return meteredSpend;
|
|
2019
|
-
},
|
|
2020
|
-
teardown() {
|
|
2021
|
-
return Promise.resolve({ destroyed: true });
|
|
2022
|
-
},
|
|
2023
|
-
resultArtifact() {
|
|
2024
|
-
if (!artifact) {
|
|
2025
|
-
throw new ValidationError("driverExecutor: resultArtifact() read before execute()");
|
|
2026
|
-
}
|
|
2027
|
-
return artifact;
|
|
2028
|
-
}
|
|
2029
|
-
};
|
|
2030
|
-
};
|
|
2031
|
-
function withDriverExecutor(base) {
|
|
2032
|
-
return {
|
|
2033
|
-
register: base.register.bind(base),
|
|
2034
|
-
resolve(spec) {
|
|
2035
|
-
const role = spec.profile.metadata?.role;
|
|
2036
|
-
if (role === driverRole && !spec.executor) {
|
|
2037
|
-
return { succeeded: true, value: driverExecutorFactory };
|
|
2038
|
-
}
|
|
2039
|
-
return base.resolve(spec);
|
|
2040
|
-
}
|
|
2041
|
-
};
|
|
2042
|
-
}
|
|
2043
|
-
function nestedTreeKey(seam, journal) {
|
|
2044
|
-
return `${seam.journalRoot}/d${nextNestOrdinal(journal)}`;
|
|
2045
|
-
}
|
|
2046
|
-
var nestCounters = /* @__PURE__ */ new WeakMap();
|
|
2047
|
-
function nextNestOrdinal(journal) {
|
|
2048
|
-
let c = nestCounters.get(journal);
|
|
2049
|
-
if (!c) {
|
|
2050
|
-
c = { n: 0 };
|
|
2051
|
-
nestCounters.set(journal, c);
|
|
2052
|
-
}
|
|
2053
|
-
return c.n++;
|
|
2054
|
-
}
|
|
2055
|
-
async function loadTreeEvents(journal, nestedRoot) {
|
|
2056
|
-
const events = await journal.loadTree(nestedRoot);
|
|
2057
|
-
if (events === void 0) {
|
|
2058
|
-
throw new ValidationError(
|
|
2059
|
-
`driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`
|
|
2060
|
-
);
|
|
2061
|
-
}
|
|
2062
|
-
return events;
|
|
2063
|
-
}
|
|
2064
|
-
function isSettled(ev) {
|
|
2065
|
-
return ev.kind === "settled";
|
|
2066
|
-
}
|
|
2067
|
-
function sumSpend(settled) {
|
|
2068
|
-
const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
2069
|
-
for (const ev of settled) {
|
|
2070
|
-
total.iterations += ev.spent.iterations;
|
|
2071
|
-
total.tokens.input += ev.spent.tokens.input;
|
|
2072
|
-
total.tokens.output += ev.spent.tokens.output;
|
|
2073
|
-
total.usd += ev.spent.usd;
|
|
2074
|
-
total.ms += ev.spent.ms;
|
|
2075
|
-
}
|
|
2076
|
-
return total;
|
|
2077
|
-
}
|
|
2078
|
-
function sumMetered(events) {
|
|
2079
|
-
const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
2080
|
-
for (const ev of events) {
|
|
2081
|
-
if (ev.kind !== "metered") continue;
|
|
2082
|
-
total.iterations += ev.spend.iterations;
|
|
2083
|
-
total.tokens.input += ev.spend.tokens.input;
|
|
2084
|
-
total.tokens.output += ev.spend.tokens.output;
|
|
2085
|
-
total.usd += ev.spend.usd;
|
|
2086
|
-
total.ms += ev.spend.ms;
|
|
2087
|
-
}
|
|
2088
|
-
return total;
|
|
2089
|
-
}
|
|
2090
|
-
function isNonZeroSpend(s) {
|
|
2091
|
-
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
|
|
2092
|
-
}
|
|
2093
|
-
function nonZeroOrUndef(s) {
|
|
2094
|
-
return isNonZeroSpend(s) ? s : void 0;
|
|
2095
|
-
}
|
|
2096
|
-
async function safeSumMetered(journal, nestedRoot) {
|
|
2097
|
-
try {
|
|
2098
|
-
return nonZeroOrUndef(sumMetered(await loadTreeEvents(journal, nestedRoot)));
|
|
2099
|
-
} catch {
|
|
2100
|
-
return void 0;
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
function deriveDeliveryVerdict(settled) {
|
|
2104
|
-
let sawChild = false;
|
|
2105
|
-
let anyValid = false;
|
|
2106
|
-
let bestValidScore;
|
|
2107
|
-
let bestDoneScore;
|
|
2108
|
-
for (const ev of settled) {
|
|
2109
|
-
sawChild = true;
|
|
2110
|
-
if (ev.status !== "done") continue;
|
|
2111
|
-
const score = ev.verdict?.score;
|
|
2112
|
-
if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) {
|
|
2113
|
-
bestDoneScore = score;
|
|
2114
|
-
}
|
|
2115
|
-
if (ev.verdict?.valid === true) {
|
|
2116
|
-
anyValid = true;
|
|
2117
|
-
if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) {
|
|
2118
|
-
bestValidScore = score;
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
if (!sawChild) return void 0;
|
|
2123
|
-
return {
|
|
2124
|
-
valid: anyValid,
|
|
2125
|
-
score: anyValid ? bestValidScore ?? 1 : bestDoneScore ?? 0
|
|
2126
|
-
};
|
|
2127
|
-
}
|
|
2128
|
-
function readNestedScopeSeam(ctx) {
|
|
2129
|
-
const seam = ctx.seams[nestedScopeSeamKey];
|
|
2130
|
-
if (!seam || typeof seam.mount !== "function") {
|
|
2131
|
-
throw new ValidationError(
|
|
2132
|
-
`driverExecutor: missing required seam "${nestedScopeSeamKey}" \u2014 a driver child must be spawned through a Scope that seeds it (the keystone scope does)`
|
|
2133
|
-
);
|
|
2134
|
-
}
|
|
2135
|
-
return seam;
|
|
2136
|
-
}
|
|
2137
|
-
function isAgent(value) {
|
|
2138
|
-
return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
|
-
export {
|
|
2142
|
-
assertModelAllowed,
|
|
2143
|
-
runBrainLoop,
|
|
2144
|
-
routerChatWithUsage,
|
|
2145
|
-
routerChatWithTools,
|
|
2146
|
-
routerToolLoop,
|
|
2147
|
-
routerBrain,
|
|
2148
|
-
defineRuntimeHooks,
|
|
2149
|
-
composeRuntimeHooks,
|
|
2150
|
-
notifyRuntimeHookEvent,
|
|
2151
|
-
notifyRuntimeDecisionPoint,
|
|
2152
|
-
createScope,
|
|
2153
|
-
settledToIteration,
|
|
2154
|
-
withDriverExecutor,
|
|
2155
|
-
spendFromUsageEvents,
|
|
2156
|
-
createBudgetPool,
|
|
2157
|
-
createSupervisor,
|
|
2158
|
-
sanitizeKnowledgeReadinessReport,
|
|
2159
|
-
sanitizeAgentRuntimeEvent,
|
|
2160
|
-
sanitizeRuntimeStreamEvent,
|
|
2161
|
-
createRuntimeEventCollector,
|
|
2162
|
-
createRuntimeStreamEventCollector,
|
|
2163
|
-
createOtelExporter,
|
|
2164
|
-
loopEventToOtelSpan,
|
|
2165
|
-
flatOtelSpan,
|
|
2166
|
-
buildRuntimeEventOtelSpans,
|
|
2167
|
-
buildLoopOtelSpans,
|
|
2168
|
-
buildLoopSpanNodes,
|
|
2169
|
-
INTELLIGENCE_WIRE_VERSION,
|
|
2170
|
-
exportEvalRuns
|
|
2171
|
-
};
|
|
2172
|
-
//# sourceMappingURL=chunk-J2K6WIG6.js.map
|