@tangle-network/agent-runtime 0.91.0 → 0.92.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +4 -2
  2. package/dist/agent.d.ts +3 -3
  3. package/dist/agent.js +106 -25
  4. package/dist/agent.js.map +1 -1
  5. package/dist/{mcp-serve-verifier-XsX8rkB9.d.ts → agentic-generator-B8oeE2Yv.d.ts} +6 -33
  6. package/dist/analyst-loop.d.ts +1 -1
  7. package/dist/candidate-execution/index.d.ts +104 -0
  8. package/dist/candidate-execution/index.js +34 -0
  9. package/dist/candidate-execution/index.js.map +1 -0
  10. package/dist/chunk-3D2RHC4K.js +73 -0
  11. package/dist/chunk-3D2RHC4K.js.map +1 -0
  12. package/dist/chunk-3MDZX7YU.js +125 -0
  13. package/dist/chunk-3MDZX7YU.js.map +1 -0
  14. package/dist/chunk-4FPXIMSI.js +659 -0
  15. package/dist/chunk-4FPXIMSI.js.map +1 -0
  16. package/dist/chunk-6O73TRHW.js +142 -0
  17. package/dist/chunk-6O73TRHW.js.map +1 -0
  18. package/dist/chunk-A62TP7SK.js +4784 -0
  19. package/dist/chunk-A62TP7SK.js.map +1 -0
  20. package/dist/{chunk-AD7JW4QG.js → chunk-BVVRQ4YC.js} +23 -1231
  21. package/dist/chunk-BVVRQ4YC.js.map +1 -0
  22. package/dist/chunk-FDJ7AHXG.js +1229 -0
  23. package/dist/chunk-FDJ7AHXG.js.map +1 -0
  24. package/dist/{chunk-FF77IBQM.js → chunk-FRBHUNQ7.js} +2 -141
  25. package/dist/chunk-FRBHUNQ7.js.map +1 -0
  26. package/dist/chunk-J2K6WIG6.js +2172 -0
  27. package/dist/chunk-J2K6WIG6.js.map +1 -0
  28. package/dist/{chunk-JRS3YSRZ.js → chunk-LQQPGRKT.js} +2 -2
  29. package/dist/{chunk-IOUUITQA.js → chunk-P6B3Z7PR.js} +3 -3
  30. package/dist/chunk-PH65PR4F.js +860 -0
  31. package/dist/chunk-PH65PR4F.js.map +1 -0
  32. package/dist/{chunk-7ON74BQO.js → chunk-R5GWDTM3.js} +2 -2
  33. package/dist/{chunk-DWWII6N2.js → chunk-TKJ3Q6CQ.js} +2 -2
  34. package/dist/{chunk-NC66AM3S.js → chunk-UO2L5VTP.js} +32 -1413
  35. package/dist/chunk-UO2L5VTP.js.map +1 -0
  36. package/dist/{chunk-BZF3KQ6G.js → chunk-VSWBYWFK.js} +4 -122
  37. package/dist/chunk-VSWBYWFK.js.map +1 -0
  38. package/dist/{completion-gate-DkAnUmpb.d.ts → completion-gate-BLaiN0-X.d.ts} +1 -1
  39. package/dist/{coordination-rRj5hjJK.d.ts → coordination-DxJ83oZA.d.ts} +12 -5
  40. package/dist/environment-provider.d.ts +2 -2
  41. package/dist/environment-provider.js +2 -1
  42. package/dist/improve-CUVCq7xg.d.ts +152 -0
  43. package/dist/{improvement-adapter-CDR8QNVM.d.ts → improvement-adapter-BieWeK5J.d.ts} +16 -0
  44. package/dist/index.d.ts +27 -1092
  45. package/dist/index.js +108 -6491
  46. package/dist/index.js.map +1 -1
  47. package/dist/intelligence.d.ts +160 -13
  48. package/dist/intelligence.js +534 -59
  49. package/dist/intelligence.js.map +1 -1
  50. package/dist/knowledge.d.ts +6 -6
  51. package/dist/knowledge.js +6 -4
  52. package/dist/lifecycle.d.ts +2 -1
  53. package/dist/lifecycle.js +5 -3
  54. package/dist/lifecycle.js.map +1 -1
  55. package/dist/{loop-runner-bin-DTbZVGfM.d.ts → loop-runner-bin-kKUNGLyV.d.ts} +2 -2
  56. package/dist/loop-runner-bin.d.ts +5 -5
  57. package/dist/loop-runner-bin.js +8 -5
  58. package/dist/loops.d.ts +16 -16
  59. package/dist/loops.js +47 -41
  60. package/dist/mcp/bin.js +6 -4
  61. package/dist/mcp/bin.js.map +1 -1
  62. package/dist/mcp/index.d.ts +8 -8
  63. package/dist/mcp/index.js +9 -6
  64. package/dist/mcp/index.js.map +1 -1
  65. package/dist/mcp-serve-verifier-Bg4C3p5S.d.ts +34 -0
  66. package/dist/{openai-tools-C4ZfUD4L.d.ts → openai-tools-E3woykz9.d.ts} +1 -1
  67. package/dist/prepare-Z08a4heC.d.ts +713 -0
  68. package/dist/profiles.d.ts +1 -1
  69. package/dist/{router-client-DJImUDlm.d.ts → sanitize-C9go6tXj.d.ts} +113 -1
  70. package/dist/{structural-rollout-MwlpgQ-6.d.ts → structural-rollout-DHGDbhvR.d.ts} +3 -3
  71. package/dist/{supervise-DPmYPk0j.d.ts → supervise-T2pazU3G.d.ts} +4 -4
  72. package/dist/{types-SyuwunY_.d.ts → types-B00NtbCs.d.ts} +1 -1
  73. package/dist/{types-eMNgWgFi.d.ts → types-DAdIm4AC.d.ts} +1 -1
  74. package/dist/{worktree-fanout-BDFQIO-Y.d.ts → worktree-fanout-BUb2Ag02.d.ts} +3 -3
  75. package/package.json +11 -6
  76. package/skills/build-with-agent-runtime/SKILL.md +1 -1
  77. package/dist/chunk-AD7JW4QG.js.map +0 -1
  78. package/dist/chunk-BZF3KQ6G.js.map +0 -1
  79. package/dist/chunk-FF77IBQM.js.map +0 -1
  80. package/dist/chunk-IVGYLCFH.js +0 -381
  81. package/dist/chunk-IVGYLCFH.js.map +0 -1
  82. package/dist/chunk-NC66AM3S.js.map +0 -1
  83. /package/dist/{chunk-JRS3YSRZ.js.map → chunk-LQQPGRKT.js.map} +0 -0
  84. /package/dist/{chunk-IOUUITQA.js.map → chunk-P6B3Z7PR.js.map} +0 -0
  85. /package/dist/{chunk-7ON74BQO.js.map → chunk-R5GWDTM3.js.map} +0 -0
  86. /package/dist/{chunk-DWWII6N2.js.map → chunk-TKJ3Q6CQ.js.map} +0 -0
@@ -2,23 +2,37 @@ import {
2
2
  UI_LENSES
3
3
  } from "./chunk-DPEUKJRO.js";
4
4
  import {
5
- buildLoopSpanNodes
6
- } from "./chunk-IVGYLCFH.js";
5
+ assertModelAllowed,
6
+ buildLoopSpanNodes,
7
+ createSupervisor,
8
+ notifyRuntimeHookEvent,
9
+ routerBrain,
10
+ routerChatWithUsage,
11
+ runBrainLoop,
12
+ withDriverExecutor
13
+ } from "./chunk-J2K6WIG6.js";
14
+ import {
15
+ InMemoryResultBlobStore,
16
+ InMemorySpawnJournal,
17
+ contentAddress
18
+ } from "./chunk-3D2RHC4K.js";
19
+ import {
20
+ providerAsExecutor,
21
+ resolveAgentEnvironmentProvider
22
+ } from "./chunk-VSWBYWFK.js";
7
23
  import {
8
24
  addTokenUsage,
9
25
  deleteBoxSafe,
10
26
  mapWithConcurrency,
11
- providerAsExecutor,
12
27
  randomSuffix,
13
28
  randomUuid,
14
- resolveAgentEnvironmentProvider,
15
29
  sleep,
16
30
  stringifySafe,
17
31
  throwAbort,
18
32
  throwIfAborted,
19
33
  withTimeout,
20
34
  zeroTokenUsage
21
- } from "./chunk-BZF3KQ6G.js";
35
+ } from "./chunk-3MDZX7YU.js";
22
36
  import {
23
37
  extractLlmCallEvent
24
38
  } from "./chunk-FVJ7M3DA.js";
@@ -30,1213 +44,9 @@ import {
30
44
  AgentEvalError,
31
45
  ConfigError,
32
46
  NotFoundError,
33
- RuntimeRunStateError,
34
47
  ValidationError
35
48
  } from "./chunk-YEJR7IXO.js";
36
49
 
37
- // src/durable/spawn-journal.ts
38
- import { createHash } from "crypto";
39
- function contentAddress(artifact) {
40
- const hex = createHash("sha256").update(stableStringify(artifact), "utf-8").digest("hex");
41
- return `sha256:${hex}`;
42
- }
43
- function stableStringify(value) {
44
- if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
45
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
46
- const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
47
- return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
48
- }
49
- var InMemoryResultBlobStore = class {
50
- blobs = /* @__PURE__ */ new Map();
51
- async put(outRef, artifact) {
52
- assertContentAddress(outRef, artifact);
53
- this.blobs.set(outRef, artifact);
54
- }
55
- async get(outRef) {
56
- return this.blobs.has(outRef) ? this.blobs.get(outRef) : void 0;
57
- }
58
- };
59
- function assertContentAddress(outRef, artifact) {
60
- const expected = contentAddress(artifact);
61
- if (outRef !== expected) {
62
- throw new Error(
63
- `blob outRef '${outRef}' does not match the artifact content hash '${expected}'; a content-addressed store refuses a mismatched ref (breaks the replay invariant)`
64
- );
65
- }
66
- }
67
- var InMemorySpawnJournal = class {
68
- trees = /* @__PURE__ */ new Map();
69
- async loadTree(root) {
70
- const tree = this.trees.get(root);
71
- if (!tree) return void 0;
72
- return tree.events.map((ev) => ({ ...ev }));
73
- }
74
- async beginTree(root, at) {
75
- const existing = this.trees.get(root);
76
- if (existing) {
77
- if (existing.begunAt !== at) {
78
- throw new Error(
79
- `spawn tree '${root}' already begun at ${existing.begunAt}; refusing to overwrite with ${at}`
80
- );
81
- }
82
- return;
83
- }
84
- this.trees.set(root, { begunAt: at, events: [] });
85
- }
86
- async appendEvent(root, ev) {
87
- const tree = this.trees.get(root);
88
- if (!tree) {
89
- throw new Error(`appendEvent called for unknown spawn tree '${root}'; call beginTree first`);
90
- }
91
- assertSeqUnique(root, tree.events, ev);
92
- tree.events.push({ ...ev });
93
- }
94
- };
95
- function assertSeqUnique(root, events, ev) {
96
- if (ev.kind === "spawned" || ev.kind === "metered") return;
97
- if (events.some((e) => e.kind !== "spawned" && e.kind !== "metered" && e.seq === ev.seq)) {
98
- throw new Error(
99
- `spawn journal corrupted: duplicate cursor seq ${ev.seq} in tree '${root}'; the cursor order replay relies on is not unique`
100
- );
101
- }
102
- }
103
-
104
- // src/runtime/supervise/model-policy.ts
105
- function assertModelAllowed(model, allowed) {
106
- if (!allowed || model === void 0) return;
107
- if (!allowed.includes(model)) {
108
- throw new ConfigError(
109
- `model ${JSON.stringify(model)} is not in the allowed set ${JSON.stringify([...allowed])}`
110
- );
111
- }
112
- }
113
-
114
- // src/runtime/router-client.ts
115
- import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
116
-
117
- // src/runtime/tool-loop.ts
118
- function estimateConversationTokens(messages) {
119
- let chars = 0;
120
- for (const m of messages) {
121
- const content = m.content;
122
- chars += typeof content === "string" ? content.length : safeJsonLength(content);
123
- const calls = m.tool_calls;
124
- if (calls) for (const c of calls) chars += c.function?.arguments?.length ?? 0;
125
- }
126
- return Math.ceil(chars / 4);
127
- }
128
- function safeJsonLength(value) {
129
- if (value === void 0 || value === null) return 0;
130
- try {
131
- return JSON.stringify(value)?.length ?? 0;
132
- } catch {
133
- return String(value).length;
134
- }
135
- }
136
- async function maybeCompact(messages, c, turn) {
137
- const head = c.preserveHead !== void 0 && c.preserveHead > 0 ? c.preserveHead : 2;
138
- if (messages.length <= head + 1) return false;
139
- const estimate = c.estimateTokens ?? estimateConversationTokens;
140
- const before = estimate(messages);
141
- if (before <= c.thresholdTokens) return false;
142
- const digest = await c.distill([...messages]);
143
- messages.splice(head, messages.length - head, {
144
- role: "user",
145
- content: `[earlier work compacted to save context \u2014 progress so far]
146
- ${digest}`
147
- });
148
- c.onCompact?.({ turn, beforeTokens: before, afterTokens: estimate(messages) });
149
- return true;
150
- }
151
- async function runBrainLoop(opts) {
152
- const maxTurns = opts.maxTurns ?? 4;
153
- const messages = [...opts.initialMessages];
154
- let toolCalls = 0;
155
- let lastText = "";
156
- const usage = { input: 0, output: 0 };
157
- const toolTrace = [];
158
- for (let turn = 1; turn <= maxTurns; turn += 1) {
159
- if (opts.hooks?.stopBefore?.(turn)) break;
160
- await opts.hooks?.beforeTurn?.(turn, messages);
161
- if (opts.compaction) await maybeCompact(messages, opts.compaction, turn);
162
- const r = await opts.chat(messages, opts.tools);
163
- if (r.usage) {
164
- usage.input += r.usage.input;
165
- usage.output += r.usage.output;
166
- opts.hooks?.onUsage?.(r.usage);
167
- }
168
- if (r.content) lastText = r.content;
169
- if (r.toolCalls.length === 0) {
170
- if (r.content) messages.push({ role: "assistant", content: r.content });
171
- return { final: lastText, turns: turn, toolCalls, toolTrace, usage, messages };
172
- }
173
- messages.push({
174
- role: "assistant",
175
- content: r.content ?? "",
176
- tool_calls: r.toolCalls.map((tc) => ({
177
- id: tc.id,
178
- type: "function",
179
- function: { name: tc.name, arguments: tc.arguments }
180
- }))
181
- });
182
- for (const tc of r.toolCalls) {
183
- toolCalls += 1;
184
- let args = {};
185
- try {
186
- args = JSON.parse(tc.arguments);
187
- } catch {
188
- messages.push({
189
- role: "tool",
190
- tool_call_id: tc.id,
191
- content: `error: arguments were not valid JSON: ${tc.arguments.slice(0, 200)}`
192
- });
193
- continue;
194
- }
195
- const out = await opts.execute(tc.name, args);
196
- messages.push({ role: "tool", tool_call_id: tc.id, content: out });
197
- toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
198
- }
199
- }
200
- return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
201
- }
202
-
203
- // src/runtime/router-client.ts
204
- async function routerChatWithUsage(cfg, messages, opts) {
205
- const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
206
- const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
207
- let temperature = opts?.temperature ?? 0.2;
208
- const body = () => ({
209
- model: cfg.model,
210
- messages,
211
- temperature,
212
- max_tokens: opts?.maxTokens ?? 8192,
213
- ...opts?.reasoningEffort ? { reasoning_effort: opts.reasoningEffort } : {}
214
- });
215
- if (cfg.complete) return parseChatResult(await cfg.complete(body()), cfg.model);
216
- let lastErr = "";
217
- for (let attempt = 0; attempt < 5; attempt += 1) {
218
- const res = await fetch(url, {
219
- method: "POST",
220
- headers,
221
- body: JSON.stringify(body()),
222
- ...opts?.signal ? { signal: opts.signal } : {}
223
- });
224
- if (res.ok) return parseChatResult(await res.json(), cfg.model);
225
- const status = res.status;
226
- const text = (await res.text()).slice(0, 200);
227
- lastErr = `router ${status}: ${text}`;
228
- if (status === 400 && /temperature/i.test(text) && temperature !== 1) {
229
- temperature = 1;
230
- continue;
231
- }
232
- if (![408, 425, 429, 500, 502, 503, 504, 520, 522, 524].includes(status))
233
- throw new Error(lastErr);
234
- if (attempt < 4) await new Promise((r) => setTimeout(r, 800 * 2 ** attempt));
235
- }
236
- throw new Error(`${lastErr} (exhausted retries)`);
237
- }
238
- function parseChatResult(json, model) {
239
- const data = json;
240
- const u = data.usage;
241
- const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
242
- const costUsd = usage && isModelPriced(model) ? estimateCost(usage.input, usage.output, model) : void 0;
243
- const msg = data.choices?.[0]?.message;
244
- const { content, reasoning } = splitReasoning(
245
- msg?.content ?? "",
246
- msg?.reasoning ?? msg?.reasoning_content
247
- );
248
- return {
249
- content,
250
- ...reasoning ? { reasoning } : {},
251
- ...usage ? { usage } : {},
252
- ...costUsd !== void 0 ? { costUsd } : {}
253
- };
254
- }
255
- function splitReasoning(rawContent, fieldReasoning) {
256
- const open = rawContent.indexOf("<think>");
257
- if (open !== -1) {
258
- const close = rawContent.indexOf("</think>", open);
259
- const inline = close !== -1 ? rawContent.slice(open + 7, close) : rawContent.slice(open + 7);
260
- const rest = close !== -1 ? rawContent.slice(0, open) + rawContent.slice(close + 8) : rawContent.slice(0, open);
261
- const reasoning = [fieldReasoning, inline.trim()].filter(Boolean).join("\n");
262
- return { content: rest.trim(), ...reasoning ? { reasoning } : {} };
263
- }
264
- return { content: rawContent, ...fieldReasoning ? { reasoning: fieldReasoning } : {} };
265
- }
266
- async function routerChatWithTools(cfg, messages, tools, opts) {
267
- const body = {
268
- model: cfg.model,
269
- messages,
270
- tools,
271
- tool_choice: opts?.toolChoice ?? "auto",
272
- temperature: opts?.temperature ?? 0.3,
273
- ...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
274
- };
275
- const raw = cfg.complete ? await cfg.complete(body) : await (async () => {
276
- const res = await fetch(`${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
277
- method: "POST",
278
- headers: { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` },
279
- body: JSON.stringify(body),
280
- ...opts?.signal ? { signal: opts.signal } : {}
281
- });
282
- if (!res.ok) throw new Error(`router ${res.status}: ${(await res.text()).slice(0, 200)}`);
283
- return res.json();
284
- })();
285
- const data = raw;
286
- const msg = data.choices?.[0]?.message;
287
- const toolCalls = (msg?.tool_calls ?? []).map((tc, i) => ({
288
- id: tc.id ?? `call_${i}`,
289
- name: tc.function?.name ?? "",
290
- arguments: tc.function?.arguments ?? "{}"
291
- }));
292
- const u = data.usage;
293
- const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
294
- const costUsd = usage && isModelPriced(cfg.model) ? estimateCost(usage.input, usage.output, cfg.model) : void 0;
295
- return {
296
- content: msg?.content ?? null,
297
- toolCalls,
298
- ...usage ? { usage } : {},
299
- ...costUsd !== void 0 ? { costUsd } : {}
300
- };
301
- }
302
- async function routerToolLoop(cfg, system, user, tools, execute, opts) {
303
- const initialMessages = opts?.initialMessages ?? [
304
- { role: "system", content: system },
305
- { role: "user", content: user }
306
- ];
307
- return runBrainLoop({
308
- chat: (messages, toolSpecs) => routerChatWithTools(cfg, messages, toolSpecs, {
309
- ...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
310
- ...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
311
- ...opts?.signal ? { signal: opts.signal } : {}
312
- }),
313
- tools,
314
- execute,
315
- initialMessages,
316
- maxTurns: opts?.maxTurns ?? 4
317
- });
318
- }
319
- function routerBrain(cfg, opts = {}) {
320
- const temperature = opts.temperature ?? 0.4;
321
- return (messages, tools) => routerChatWithTools(cfg, messages, tools, { temperature, toolChoice: "auto" });
322
- }
323
-
324
- // src/runtime-hooks.ts
325
- function defineRuntimeHooks(hooks) {
326
- return hooks;
327
- }
328
- function composeRuntimeHooks(...entries) {
329
- const hooks = entries.filter((entry) => !!entry);
330
- return {
331
- onEvent: hooks.some((hook) => hook.onEvent) ? (event, context) => {
332
- const pending = [];
333
- for (const hook of hooks) {
334
- const result = hook.onEvent?.(event, context);
335
- if (isThenable(result)) pending.push(Promise.resolve(result));
336
- }
337
- if (pending.length > 0) return Promise.all(pending).then(() => void 0);
338
- return void 0;
339
- } : void 0,
340
- onDecisionPoint: hooks.some((hook) => hook.onDecisionPoint) ? (point, context) => {
341
- const pending = [];
342
- for (const hook of hooks) {
343
- const result = hook.onDecisionPoint?.(point, context);
344
- if (isThenable(result)) pending.push(Promise.resolve(result));
345
- }
346
- if (pending.length > 0) return Promise.all(pending).then(() => void 0);
347
- return void 0;
348
- } : void 0,
349
- onHookError: hooks.some((hook) => hook.onHookError) ? (error, context) => {
350
- const pending = [];
351
- for (const hook of hooks) {
352
- const result = hook.onHookError?.(error, context);
353
- if (isThenable(result)) pending.push(Promise.resolve(result));
354
- }
355
- if (pending.length > 0) return Promise.all(pending).then(() => void 0);
356
- return void 0;
357
- } : void 0
358
- };
359
- }
360
- function notifyRuntimeHookEvent(hooks, event, context = {}) {
361
- const onEvent = hooks?.onEvent;
362
- if (!onEvent) return;
363
- try {
364
- const result = onEvent(event, context);
365
- if (isThenable(result)) {
366
- void result.catch((error) => {
367
- notifyRuntimeHookError(hooks, toError(error), {
368
- hook: "onEvent",
369
- eventId: event.id,
370
- target: event.target,
371
- phase: event.phase
372
- });
373
- });
374
- }
375
- } catch (error) {
376
- notifyRuntimeHookError(hooks, toError(error), {
377
- hook: "onEvent",
378
- eventId: event.id,
379
- target: event.target,
380
- phase: event.phase
381
- });
382
- }
383
- }
384
- function notifyRuntimeDecisionPoint(hooks, point, context = {}) {
385
- const onDecisionPoint = hooks?.onDecisionPoint;
386
- if (!onDecisionPoint) return;
387
- try {
388
- const result = onDecisionPoint(point, context);
389
- if (isThenable(result)) {
390
- void result.catch((error) => {
391
- notifyRuntimeHookError(hooks, toError(error), {
392
- hook: "onDecisionPoint",
393
- decisionId: point.id,
394
- decisionKind: point.kind
395
- });
396
- });
397
- }
398
- } catch (error) {
399
- notifyRuntimeHookError(hooks, toError(error), {
400
- hook: "onDecisionPoint",
401
- decisionId: point.id,
402
- decisionKind: point.kind
403
- });
404
- }
405
- }
406
- function notifyRuntimeHookError(hooks, error, context) {
407
- try {
408
- const result = hooks?.onHookError?.(error, context);
409
- if (isThenable(result)) void result.catch(() => void 0);
410
- } catch {
411
- }
412
- }
413
- function isThenable(value) {
414
- return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
415
- }
416
- function toError(error) {
417
- return error instanceof Error ? error : new Error(String(error));
418
- }
419
-
420
- // src/runtime/supervise/scope.ts
421
- var nestedScopeSeamKey = "nested-scope";
422
- function makeNestedScopeSeam(args, childNodeId) {
423
- return {
424
- depth: args.depth,
425
- ...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
426
- journalRoot: args.root,
427
- mount(nestedRoot, signal) {
428
- return createScope({
429
- parentId: childNodeId,
430
- root: nestedRoot,
431
- pool: args.pool,
432
- journal: args.journal,
433
- blobs: args.blobs,
434
- executors: args.executors,
435
- // Re-seed the parent's NON-recursion seams (sandbox/router for leaf grandchildren);
436
- // the nested scope adds its OWN nested-scope seam per child in `spawn`.
437
- seams: args.seams,
438
- depth: args.depth + 1,
439
- ...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
440
- signal,
441
- ...args.now ? { now: args.now } : {},
442
- ...args.hooks ? { hooks: args.hooks } : {}
443
- });
444
- }
445
- };
446
- }
447
- function createScope(args) {
448
- const children = /* @__PURE__ */ new Map();
449
- let spawnOrdinal = 0;
450
- let cursorSeq = 0;
451
- let meterSeq = 0;
452
- const now = args.now ?? Date.now;
453
- function spawn4(agent, task, opts) {
454
- if (args.maxDepth !== void 0 && args.depth >= args.maxDepth) {
455
- return { ok: false, reason: "depth-exceeded" };
456
- }
457
- const spec = agent.executorSpec;
458
- if (!isAgentSpec(spec)) {
459
- throw new ValidationError(
460
- `scope.spawn: agent "${agent.name}" exposes no \`executorSpec\` (AgentSpec) to resolve a Executor`
461
- );
462
- }
463
- const resolved = args.executors.resolve(spec);
464
- if (!resolved.succeeded) throw new ValidationError(`scope.spawn: ${resolved.error}`);
465
- const reservation = args.pool.reserve(opts.budget);
466
- if (!reservation.ok) return { ok: false, reason: reservation.reason };
467
- try {
468
- const ordinal = spawnOrdinal++;
469
- const id = `${args.parentId}:s${ordinal}`;
470
- const childAbort = new AbortController();
471
- const cascadeAbort = () => childAbort.abort();
472
- if (args.signal.aborted) childAbort.abort();
473
- else args.signal.addEventListener("abort", cascadeAbort, { once: true });
474
- const ctx = {
475
- signal: childAbort.signal,
476
- seams: { ...args.seams, [nestedScopeSeamKey]: makeNestedScopeSeam(args, id) }
477
- };
478
- const executor = resolved.value(spec, ctx);
479
- const handle = {
480
- id,
481
- label: opts.label,
482
- get status() {
483
- return children.get(id)?.status ?? "cancelled";
484
- },
485
- abort(reason) {
486
- childAbort.abort(reason);
487
- }
488
- };
489
- const live = {
490
- id,
491
- status: "acquiring",
492
- runtime: executor.runtime,
493
- budget: opts.budget,
494
- label: opts.label,
495
- spent: zeroSpend(),
496
- settled: void 0,
497
- delivered: false,
498
- executorDone: false,
499
- ...executor.deliver ? { deliver: executor.deliver.bind(executor) } : {}
500
- };
501
- children.set(id, live);
502
- void args.journal.appendEvent(args.root, {
503
- kind: "spawned",
504
- id,
505
- parent: args.parentId,
506
- label: opts.label,
507
- budget: opts.budget,
508
- runtime: executor.runtime,
509
- seq: ordinal,
510
- at: new Date(now()).toISOString()
511
- });
512
- notifyRuntimeHookEvent(
513
- args.hooks,
514
- {
515
- id: `${id}:spawn`,
516
- runId: args.root,
517
- target: "agent.spawn",
518
- phase: "after",
519
- timestamp: now(),
520
- stepIndex: ordinal,
521
- parentId: args.parentId,
522
- payload: {
523
- childId: id,
524
- label: opts.label,
525
- runtime: executor.runtime,
526
- budget: opts.budget,
527
- depth: args.depth
528
- }
529
- },
530
- { signal: args.signal }
531
- );
532
- const settled = runChild(
533
- live,
534
- executor,
535
- childAbort,
536
- task,
537
- opts,
538
- args.pool,
539
- reservation.ticket,
540
- args.blobs
541
- ).then((s) => {
542
- live.resolved = s;
543
- return s;
544
- }).finally(() => {
545
- args.signal.removeEventListener("abort", cascadeAbort);
546
- });
547
- live.settled = settled;
548
- return { ok: true, handle };
549
- } catch (err) {
550
- args.pool.reconcile(reservation.ticket, zeroSpend());
551
- throw err;
552
- }
553
- }
554
- async function next() {
555
- const undelivered = () => [...children.values()].filter((c) => !c.delivered);
556
- if (undelivered().length === 0) return null;
557
- for (; ; ) {
558
- const pending = undelivered();
559
- if (pending.length === 0) return null;
560
- const ready = pending.find((c) => c.resolved !== void 0);
561
- const chosen = ready ?? await raceFirstSettled(pending);
562
- if (chosen.delivered) continue;
563
- chosen.delivered = true;
564
- const seq = cursorSeq++;
565
- const settlement = chosen.resolved;
566
- if (!settlement) {
567
- throw new ValidationError(
568
- `scope.next: child '${chosen.id}' won the settle race without a resolved value`
569
- );
570
- }
571
- return finalizeSettlement(chosen, settlement, seq, args, now);
572
- }
573
- }
574
- async function nextResolved() {
575
- for (; ; ) {
576
- const candidates = [...children.values()].filter((c) => !c.delivered);
577
- const pick = candidates.find((c) => c.resolved !== void 0) ?? candidates.find((c) => c.executorDone);
578
- if (!pick) return null;
579
- const settlement = await pick.settled;
580
- if (pick.delivered) continue;
581
- pick.delivered = true;
582
- const seq = cursorSeq++;
583
- return finalizeSettlement(pick, settlement, seq, args, now);
584
- }
585
- }
586
- function send(nodeId, msg) {
587
- const child = children.get(nodeId);
588
- if (!child || child.delivered || !child.deliver) return false;
589
- child.deliver(msg);
590
- return true;
591
- }
592
- async function meter(spend, detail) {
593
- const seq = meterSeq++;
594
- args.pool.observe(spend);
595
- await args.journal.appendEvent(args.root, {
596
- kind: "metered",
597
- id: args.parentId,
598
- spend,
599
- seq,
600
- at: new Date(now()).toISOString()
601
- });
602
- notifyRuntimeHookEvent(
603
- args.hooks,
604
- {
605
- id: `${args.parentId}:meter:${seq}`,
606
- runId: args.root,
607
- target: "agent.turn",
608
- phase: "after",
609
- timestamp: now(),
610
- parentId: args.parentId,
611
- payload: { spend, ...detail ?? {} }
612
- },
613
- { signal: args.signal }
614
- );
615
- }
616
- return {
617
- spawn: spawn4,
618
- next,
619
- nextResolved,
620
- send,
621
- signal: args.signal,
622
- meter,
623
- get view() {
624
- return makeTreeView(args.parentId, children);
625
- },
626
- get budget() {
627
- return args.pool.readout();
628
- }
629
- };
630
- }
631
- async function raceFirstSettled(pending) {
632
- return Promise.race(pending.map((c) => c.settled.then(() => c)));
633
- }
634
- async function finalizeSettlement(child, settlement, seq, args, now) {
635
- const handle = frozenHandle(child);
636
- if (settlement.kind === "down") {
637
- child.status = "failed";
638
- await args.journal.appendEvent(args.root, {
639
- kind: "settled",
640
- id: child.id,
641
- status: "down",
642
- spent: child.spent,
643
- infra: settlement.infra,
644
- seq,
645
- at: new Date(now()).toISOString()
646
- });
647
- if (settlement.metered) {
648
- await args.journal.appendEvent(args.root, {
649
- kind: "metered",
650
- id: child.id,
651
- spend: settlement.metered,
652
- seq,
653
- at: new Date(now()).toISOString()
654
- });
655
- }
656
- notifyRuntimeHookEvent(
657
- args.hooks,
658
- {
659
- id: `${child.id}:settled`,
660
- runId: args.root,
661
- target: "agent.child",
662
- phase: "after",
663
- timestamp: now(),
664
- stepIndex: seq,
665
- parentId: args.parentId,
666
- payload: {
667
- childId: child.id,
668
- status: "down",
669
- reason: settlement.reason,
670
- infra: settlement.infra,
671
- spent: child.spent
672
- }
673
- },
674
- { signal: args.signal }
675
- );
676
- return {
677
- kind: "down",
678
- handle,
679
- reason: settlement.reason,
680
- infra: settlement.infra,
681
- restartCount: settlement.restartCount,
682
- seq
683
- };
684
- }
685
- child.status = "done";
686
- child.outRef = settlement.outRef;
687
- child.spent = settlement.spent;
688
- await args.journal.appendEvent(args.root, {
689
- kind: "settled",
690
- id: child.id,
691
- status: "done",
692
- outRef: settlement.outRef,
693
- ...settlement.verdict ? { verdict: settlement.verdict } : {},
694
- spent: settlement.spent,
695
- seq,
696
- at: new Date(now()).toISOString()
697
- });
698
- if (settlement.metered) {
699
- await args.journal.appendEvent(args.root, {
700
- kind: "metered",
701
- id: child.id,
702
- spend: settlement.metered,
703
- seq,
704
- at: new Date(now()).toISOString()
705
- });
706
- }
707
- notifyRuntimeHookEvent(
708
- args.hooks,
709
- {
710
- id: `${child.id}:settled`,
711
- runId: args.root,
712
- target: "agent.child",
713
- phase: "after",
714
- timestamp: now(),
715
- stepIndex: seq,
716
- parentId: args.parentId,
717
- payload: {
718
- childId: child.id,
719
- status: "done",
720
- outRef: settlement.outRef,
721
- score: settlement.verdict?.score,
722
- valid: settlement.verdict?.valid,
723
- spent: settlement.spent
724
- }
725
- },
726
- { signal: args.signal }
727
- );
728
- return {
729
- kind: "done",
730
- handle,
731
- out: settlement.out,
732
- outRef: settlement.outRef,
733
- ...settlement.verdict ? { verdict: settlement.verdict } : {},
734
- spent: settlement.spent,
735
- seq
736
- };
737
- }
738
- async function runChild(live, executor, childAbort, task, opts, pool, ticket, blobs) {
739
- let reconciled = false;
740
- const reconcileOnce = (spend) => {
741
- if (reconciled) return;
742
- reconciled = true;
743
- pool.reconcile(ticket, clampSpend(spend, opts.budget));
744
- };
745
- try {
746
- live.status = "running";
747
- const ran = executor.execute(task, childAbort.signal);
748
- let artifact;
749
- if (isAsyncIterable(ran)) {
750
- const spend = await foldStream(ran);
751
- live.spent = spend;
752
- artifact = executor.resultArtifact();
753
- reconcileOnce(spend);
754
- } else {
755
- const terminal = await ran;
756
- live.spent = terminal.spent;
757
- artifact = terminal;
758
- reconcileOnce(terminal.spent);
759
- }
760
- live.executorDone = true;
761
- const ownMetered = executor.metered?.();
762
- if (childAbort.signal.aborted) {
763
- await teardownSafe(executor, opts.shutdown ?? "brutalKill");
764
- return downRecord("aborted before settle", true, ownMetered);
765
- }
766
- const outRef = contentAddress(artifact.out);
767
- await blobs.put(outRef, artifact.out);
768
- await teardownSafe(executor, opts.shutdown ?? "infinity");
769
- return {
770
- kind: "done",
771
- out: artifact.out,
772
- outRef,
773
- ...artifact.verdict ? { verdict: artifact.verdict } : {},
774
- spent: live.spent,
775
- ...ownMetered ? { metered: ownMetered } : {}
776
- };
777
- } catch (err) {
778
- live.executorDone = true;
779
- reconcileOnce(live.spent);
780
- await teardownSafe(executor, "brutalKill");
781
- const aborted = childAbort.signal.aborted || isAbortError(err);
782
- return downRecord(errMessage(err), aborted || isInfraError(err), executor.metered?.());
783
- }
784
- }
785
- function settledToIteration(settled) {
786
- if (settled.kind === "down") {
787
- throw new ValidationError(
788
- `settledToIteration: cannot adapt a 'down' settlement (node '${settled.handle.id}', seq ${settled.seq}) to an Iteration`
789
- );
790
- }
791
- return {
792
- index: settled.seq,
793
- task: void 0,
794
- agentRunName: settled.handle.label,
795
- output: settled.out,
796
- ...settled.verdict ? { verdict: settled.verdict } : {},
797
- events: [],
798
- startedAt: 0,
799
- endedAt: settled.spent.ms,
800
- costUsd: settled.spent.usd,
801
- tokenUsage: { input: settled.spent.tokens.input, output: settled.spent.tokens.output }
802
- };
803
- }
804
- function makeTreeView(root, children) {
805
- const nodes = [...children.values()].map((c) => ({
806
- id: c.id,
807
- parent: root,
808
- label: c.label,
809
- status: c.status,
810
- runtime: c.runtime,
811
- budget: c.budget,
812
- spent: c.spent,
813
- ...c.outRef ? { outRef: c.outRef } : {}
814
- }));
815
- return {
816
- root,
817
- nodes,
818
- inFlight: nodes.filter((n) => n.status === "running" || n.status === "acquiring").length
819
- };
820
- }
821
- function frozenHandle(child) {
822
- return {
823
- id: child.id,
824
- label: child.label,
825
- status: child.status,
826
- abort() {
827
- }
828
- };
829
- }
830
- async function foldStream(stream) {
831
- const tokens = { input: 0, output: 0 };
832
- let usd = 0;
833
- let iterations = 0;
834
- for await (const ev of stream) {
835
- if (ev.kind === "tokens") {
836
- tokens.input += ev.input;
837
- tokens.output += ev.output;
838
- } else if (ev.kind === "cost") {
839
- usd += ev.usd;
840
- } else {
841
- iterations += 1;
842
- }
843
- }
844
- return { iterations, tokens, usd, ms: 0 };
845
- }
846
- function clampSpend(spend, budget) {
847
- const totalTokens2 = spend.tokens.input + spend.tokens.output;
848
- const tokensOk = totalTokens2 <= budget.maxTokens;
849
- const itersOk = spend.iterations <= budget.maxIterations;
850
- const usdOk = budget.maxUsd === void 0 || spend.usd <= budget.maxUsd;
851
- if (tokensOk && itersOk && usdOk) return spend;
852
- const ratio = !tokensOk && totalTokens2 > 0 ? budget.maxTokens / totalTokens2 : 1;
853
- return {
854
- iterations: Math.min(spend.iterations, budget.maxIterations),
855
- tokens: ratio < 1 ? {
856
- input: Math.floor(spend.tokens.input * ratio),
857
- output: Math.floor(spend.tokens.output * ratio)
858
- } : spend.tokens,
859
- usd: budget.maxUsd === void 0 ? spend.usd : Math.min(spend.usd, budget.maxUsd),
860
- ms: spend.ms
861
- };
862
- }
863
- async function teardownSafe(executor, grace) {
864
- try {
865
- await executor.teardown(grace);
866
- } catch {
867
- }
868
- }
869
- function downRecord(reason, infra, metered) {
870
- return { kind: "down", reason, infra, restartCount: 0, ...metered ? { metered } : {} };
871
- }
872
- function zeroSpend() {
873
- return { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
874
- }
875
- function isAsyncIterable(value) {
876
- return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
877
- }
878
- function isAgentSpec(value) {
879
- if (typeof value !== "object" || value === null) return false;
880
- const v = value;
881
- return "profile" in v && "harness" in v;
882
- }
883
- function isAbortError(err) {
884
- return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
885
- }
886
- function isInfraError(err) {
887
- return err instanceof ValidationError;
888
- }
889
- function errMessage(err) {
890
- if (err instanceof Error) return err.message;
891
- return String(err);
892
- }
893
-
894
- // src/runtime/supervise/budget.ts
895
- function spendFromUsageEvents(events) {
896
- const tokens = zeroTokenUsage();
897
- let usd = 0;
898
- let iterations = 0;
899
- for (const ev of events) {
900
- if (ev.kind === "tokens") {
901
- addTokenUsage(tokens, { input: ev.input, output: ev.output });
902
- } else if (ev.kind === "cost") {
903
- usd += ev.usd;
904
- } else {
905
- iterations += 1;
906
- }
907
- }
908
- return { iterations, tokens, usd, ms: 0 };
909
- }
910
- async function foldUsage(events) {
911
- if (Array.isArray(events)) return spendFromUsageEvents(events);
912
- const tokens = zeroTokenUsage();
913
- let usd = 0;
914
- let iterations = 0;
915
- for await (const ev of events) {
916
- if (ev.kind === "tokens") {
917
- addTokenUsage(tokens, { input: ev.input, output: ev.output });
918
- } else if (ev.kind === "cost") {
919
- usd += ev.usd;
920
- } else {
921
- iterations += 1;
922
- }
923
- }
924
- return { iterations, tokens, usd, ms: 0 };
925
- }
926
- function totalTokens(usage) {
927
- return usage.input + usage.output;
928
- }
929
- function createBudgetPool(root, now = Date.now) {
930
- let freeTokens = root.maxTokens;
931
- let reservedTokens = 0;
932
- let committedTokens = 0;
933
- const usdCapped = root.maxUsd !== void 0;
934
- let freeUsd = root.maxUsd ?? 0;
935
- let reservedUsd = 0;
936
- let committedUsd = 0;
937
- let freeIterations = root.maxIterations;
938
- let reservedIterations = 0;
939
- let committedIterations = 0;
940
- const absoluteDeadlineMs = root.deadlineMs !== void 0 ? now() + root.deadlineMs : 0;
941
- let nextTicketId = 0;
942
- const open = /* @__PURE__ */ new Set();
943
- function reserve(b) {
944
- const wantTokens = b.maxTokens;
945
- const wantUsd = b.maxUsd ?? 0;
946
- const wantIterations = b.maxIterations;
947
- if (wantTokens > freeTokens) return { ok: false, reason: "budget-exhausted" };
948
- if (wantIterations > freeIterations) return { ok: false, reason: "budget-exhausted" };
949
- if (wantUsd > 0 && (!usdCapped || wantUsd > freeUsd)) {
950
- return { ok: false, reason: "budget-exhausted" };
951
- }
952
- freeTokens -= wantTokens;
953
- reservedTokens += wantTokens;
954
- freeIterations -= wantIterations;
955
- reservedIterations += wantIterations;
956
- if (wantUsd > 0) {
957
- freeUsd -= wantUsd;
958
- reservedUsd += wantUsd;
959
- }
960
- const id = nextTicketId++;
961
- open.add(id);
962
- return {
963
- ok: true,
964
- ticket: { id, reserved: { tokens: wantTokens, usd: wantUsd, iterations: wantIterations } }
965
- };
966
- }
967
- function reconcile(ticket, spent) {
968
- if (!open.has(ticket.id)) {
969
- throw new Error(`budget pool: reconcile of unknown or already-settled ticket ${ticket.id}`);
970
- }
971
- open.delete(ticket.id);
972
- const { tokens: rTokens, usd: rUsd, iterations: rIterations } = ticket.reserved;
973
- const spentTokens = totalTokens(spent.tokens);
974
- if (spentTokens > rTokens) {
975
- throw new Error(
976
- `budget pool: ticket ${ticket.id} spent ${spentTokens} tokens > reserved ${rTokens}`
977
- );
978
- }
979
- if (spent.iterations > rIterations) {
980
- throw new Error(
981
- `budget pool: ticket ${ticket.id} spent ${spent.iterations} iterations > reserved ${rIterations}`
982
- );
983
- }
984
- if (usdCapped && spent.usd > rUsd) {
985
- throw new Error(`budget pool: ticket ${ticket.id} spent $${spent.usd} > reserved $${rUsd}`);
986
- }
987
- reservedTokens -= rTokens;
988
- committedTokens += spentTokens;
989
- freeTokens += rTokens - spentTokens;
990
- reservedIterations -= rIterations;
991
- committedIterations += spent.iterations;
992
- freeIterations += rIterations - spent.iterations;
993
- if (usdCapped && rUsd > 0) {
994
- reservedUsd -= rUsd;
995
- committedUsd += spent.usd;
996
- freeUsd += rUsd - spent.usd;
997
- } else {
998
- committedUsd += spent.usd;
999
- }
1000
- }
1001
- function observe(spend) {
1002
- const tokens = totalTokens(spend.tokens);
1003
- freeTokens -= tokens;
1004
- committedTokens += tokens;
1005
- freeIterations -= spend.iterations;
1006
- committedIterations += spend.iterations;
1007
- committedUsd += spend.usd;
1008
- if (usdCapped) freeUsd -= spend.usd;
1009
- }
1010
- function readout() {
1011
- return {
1012
- tokensLeft: freeTokens,
1013
- usdLeft: usdCapped ? freeUsd : 0,
1014
- usdCapped,
1015
- deadlineMs: absoluteDeadlineMs,
1016
- reservedTokens
1017
- };
1018
- }
1019
- function assertNoOpenTickets() {
1020
- if (open.size > 0) {
1021
- throw new Error(
1022
- `budget pool: ${open.size} reservation(s) still open at join barrier (leaked ticket ids: ${[...open].join(", ")}) \u2014 conserved-pool invariant violated`
1023
- );
1024
- }
1025
- }
1026
- return {
1027
- reserve,
1028
- reconcile,
1029
- spendFrom: foldUsage,
1030
- readout,
1031
- observe,
1032
- assertNoOpenTickets
1033
- };
1034
- }
1035
-
1036
- // src/runtime/supervise/supervisor.ts
1037
- var defaultMaxDepth = 4;
1038
- function createSupervisor() {
1039
- let attached;
1040
- async function run(root, task, opts) {
1041
- const now = opts.now ?? Date.now;
1042
- const pool = createBudgetPool(opts.budget, now);
1043
- await opts.journal.beginTree(opts.runId, new Date(now()).toISOString());
1044
- await opts.journal.appendEvent(opts.runId, {
1045
- kind: "spawned",
1046
- id: opts.runId,
1047
- label: "root",
1048
- budget: opts.budget,
1049
- runtime: "inline",
1050
- seq: 0,
1051
- at: new Date(now()).toISOString()
1052
- });
1053
- const controller = new AbortController();
1054
- const cascadeAbort = (reason) => {
1055
- if (controller.signal.aborted) return;
1056
- controller.abort(reason);
1057
- };
1058
- const onCallerAbort = () => cascadeAbort("caller signal aborted");
1059
- if (opts.signal) {
1060
- if (opts.signal.aborted) cascadeAbort("caller signal aborted");
1061
- else opts.signal.addEventListener("abort", onCallerAbort, { once: true });
1062
- }
1063
- const breaker = createIntensityBreaker(opts, () => cascadeAbort("intensity breaker tripped"));
1064
- const journal = wrapJournalForBreaker(opts.journal, breaker);
1065
- const scope = createScope({
1066
- parentId: opts.runId,
1067
- root: opts.runId,
1068
- pool,
1069
- journal,
1070
- blobs: opts.blobs,
1071
- executors: opts.executors,
1072
- seams: {},
1073
- depth: 0,
1074
- maxDepth: opts.maxDepth ?? defaultMaxDepth,
1075
- signal: controller.signal,
1076
- now,
1077
- hooks: opts.hooks
1078
- });
1079
- const openScope = scope;
1080
- if (attached) {
1081
- attached.bind({ scope: openScope, cascadeAbort, signal: pushRootSignal(cascadeAbort) });
1082
- }
1083
- let actOutcome;
1084
- try {
1085
- const out = await root.act(task, scope);
1086
- actOutcome = { ok: true, out };
1087
- } catch (error) {
1088
- actOutcome = { ok: false, error };
1089
- } finally {
1090
- await drainLiveChildren(openScope, controller);
1091
- if (opts.signal) opts.signal.removeEventListener("abort", onCallerAbort);
1092
- if (attached) attached.unbind();
1093
- }
1094
- const tree = scope.view;
1095
- if (actOutcome.ok) {
1096
- pool.assertNoOpenTickets();
1097
- const out = actOutcome.out;
1098
- if (out !== void 0) {
1099
- const outRef = contentAddress(out);
1100
- await opts.blobs.put(outRef, out);
1101
- const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
1102
- return {
1103
- kind: "winner",
1104
- out,
1105
- outRef,
1106
- tree,
1107
- spentTotal: addSpend(childWork, driverInference),
1108
- ...isNonEmptySpend(driverInference) ? { spentBreakdown: { driverInference, childWork } } : {}
1109
- };
1110
- }
1111
- return noWinner();
1112
- }
1113
- return noWinner();
1114
- async function noWinner() {
1115
- const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
1116
- return {
1117
- kind: "no-winner",
1118
- reason: classifyNoWinner(controller, pool, opts, breaker),
1119
- tree,
1120
- downCount: breaker.downCount(),
1121
- spentTotal: addSpend(childWork, driverInference)
1122
- };
1123
- }
1124
- }
1125
- function attach(h) {
1126
- const control = rootControls.get(h);
1127
- if (!control) {
1128
- throw new RuntimeRunStateError(
1129
- "supervisor.attach: handle was not minted by createRootHandle (no control channel)"
1130
- );
1131
- }
1132
- attached = control;
1133
- }
1134
- return { run, attach };
1135
- }
1136
- var rootControls = /* @__PURE__ */ new WeakMap();
1137
- function pushRootSignal(cascadeAbort) {
1138
- return (msg) => {
1139
- if (msg.kind === "cancel") cascadeAbort(msg.reason ?? "root signal: cancel");
1140
- };
1141
- }
1142
- function createIntensityBreaker(opts, trip) {
1143
- const max = opts.maxRestarts;
1144
- const within = opts.withinMs;
1145
- const armed = max !== void 0 && within !== void 0;
1146
- const recent = [];
1147
- let total = 0;
1148
- let isTripped = false;
1149
- return {
1150
- recordDown(at) {
1151
- total += 1;
1152
- if (!armed || isTripped) return;
1153
- recent.push(at);
1154
- const cutoff = at - within;
1155
- while (recent.length > 0 && recent[0] < cutoff) recent.shift();
1156
- if (recent.length > max) {
1157
- isTripped = true;
1158
- trip();
1159
- }
1160
- },
1161
- tripped() {
1162
- return isTripped;
1163
- },
1164
- downCount() {
1165
- return total;
1166
- }
1167
- };
1168
- }
1169
- function wrapJournalForBreaker(journal, breaker) {
1170
- return {
1171
- loadTree: (root) => journal.loadTree(root),
1172
- beginTree: (root, at) => journal.beginTree(root, at),
1173
- appendEvent: (root, ev) => {
1174
- if (ev.kind === "settled" && ev.status === "down") breaker.recordDown(Date.parse(ev.at));
1175
- return journal.appendEvent(root, ev);
1176
- }
1177
- };
1178
- }
1179
- async function drainLiveChildren(scope, controller) {
1180
- const hasLive = scope.view.inFlight > 0;
1181
- if (!hasLive) return;
1182
- if (!controller.signal.aborted) controller.abort();
1183
- await Promise.allSettled([drainCursor(scope)]);
1184
- }
1185
- async function drainCursor(scope) {
1186
- for (; ; ) {
1187
- const settled = await scope.next();
1188
- if (settled === null) return;
1189
- }
1190
- }
1191
- function classifyNoWinner(controller, pool, opts, breaker) {
1192
- if (breaker.tripped()) return "all-children-down";
1193
- if (controller.signal.aborted) return "aborted";
1194
- if (poolExhausted(pool, opts)) return "budget-exhausted";
1195
- return "all-children-down";
1196
- }
1197
- function poolExhausted(pool, opts) {
1198
- const r = pool.readout();
1199
- if (r.tokensLeft <= 0) return true;
1200
- if (opts.budget.maxUsd !== void 0 && r.usdLeft <= 0) return true;
1201
- if (opts.budget.deadlineMs !== void 0 && r.deadlineMs > 0 && (opts.now ?? Date.now)() >= r.deadlineMs) {
1202
- return true;
1203
- }
1204
- return false;
1205
- }
1206
- async function spentFromJournal(journal, root) {
1207
- const events = await journal.loadTree(root);
1208
- if (events === void 0) {
1209
- throw new RuntimeRunStateError(
1210
- `supervisor: spawn tree '${root}' is missing from the journal after run (corrupted log)`
1211
- );
1212
- }
1213
- const childWork = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
1214
- const driverInference = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
1215
- for (const ev of events) {
1216
- if (ev.kind === "settled") accumulate(childWork, ev.spent);
1217
- else if (ev.kind === "metered") accumulate(driverInference, ev.spend);
1218
- }
1219
- return { childWork, driverInference };
1220
- }
1221
- function accumulate(a, b) {
1222
- a.iterations += b.iterations;
1223
- a.tokens.input += b.tokens.input;
1224
- a.tokens.output += b.tokens.output;
1225
- a.usd += b.usd;
1226
- a.ms += b.ms;
1227
- }
1228
- function addSpend(a, b) {
1229
- return {
1230
- iterations: a.iterations + b.iterations,
1231
- tokens: { input: a.tokens.input + b.tokens.input, output: a.tokens.output + b.tokens.output },
1232
- usd: a.usd + b.usd,
1233
- ms: a.ms + b.ms
1234
- };
1235
- }
1236
- function isNonEmptySpend(s) {
1237
- return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
1238
- }
1239
-
1240
50
  // src/runtime/supervise/completion-gate.ts
1241
51
  function gateOnDeliverable(inner, deliverable) {
1242
52
  let gated;
@@ -1255,7 +65,7 @@ function gateOnDeliverable(inner, deliverable) {
1255
65
  ...inner.deliver ? { deliver: (m) => inner.deliver?.(m) } : {},
1256
66
  execute(task, signal) {
1257
67
  const r = inner.execute(task, signal);
1258
- if (isAsyncIterable2(r)) {
68
+ if (isAsyncIterable(r)) {
1259
69
  return (async function* () {
1260
70
  for await (const ev of r) yield ev;
1261
71
  const art = inner.resultArtifact();
@@ -1275,7 +85,7 @@ function gateOnDeliverable(inner, deliverable) {
1275
85
  }
1276
86
  };
1277
87
  }
1278
- function isAsyncIterable2(v) {
88
+ function isAsyncIterable(v) {
1279
89
  return v != null && typeof v[Symbol.asyncIterator] === "function";
1280
90
  }
1281
91
 
@@ -2055,13 +865,13 @@ async function executeIteration(args) {
2055
865
  await destroySandboxSafe(box, args.ctx.traceEmitter, args.runId, args.now);
2056
866
  }
2057
867
  }
2058
- if (isAbortError2(slot.error) || args.signal.aborted) {
868
+ if (isAbortError(slot.error) || args.signal.aborted) {
2059
869
  if (slot.error) throw slot.error;
2060
870
  throwAbort();
2061
871
  }
2062
872
  if (slot.error instanceof ValidationError) throw slot.error;
2063
873
  }
2064
- function isAbortError2(err) {
874
+ function isAbortError(err) {
2065
875
  return err instanceof Error && err.name === "AbortError";
2066
876
  }
2067
877
  var TEARDOWN_TIMEOUT_MS2 = 15e3;
@@ -2575,7 +1385,7 @@ import { randomUUID as randomUUID2 } from "crypto";
2575
1385
  import { request as httpRequest } from "http";
2576
1386
  import { request as httpsRequest } from "https";
2577
1387
  import { Readable } from "stream";
2578
- import { estimateCost as estimateCost2, isModelPriced as isModelPriced2 } from "@tangle-network/agent-eval";
1388
+ import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
2579
1389
  var routerSeamKey = "router";
2580
1390
  var sandboxSeamKey = "sandbox";
2581
1391
  var cliSeamKey = "cli";
@@ -2596,7 +1406,7 @@ function contentRef(prefix, value) {
2596
1406
  }
2597
1407
  return `${prefix}:${(h >>> 0).toString(16).padStart(8, "0")}`;
2598
1408
  }
2599
- function zeroSpend2() {
1409
+ function zeroSpend() {
2600
1410
  return { iterations: 0, tokens: zeroTokenUsage(), usd: 0, ms: 0 };
2601
1411
  }
2602
1412
  var routerInlineExecutor = (spec, ctx) => {
@@ -2790,7 +1600,7 @@ var routerToolsInlineExecutor = (spec, ctx) => {
2790
1600
  }
2791
1601
  }
2792
1602
  }
2793
- const usd = isModelPriced2(model) ? estimateCost2(tokens.input, tokens.output, model) : 0;
1603
+ const usd = isModelPriced(model) ? estimateCost(tokens.input, tokens.output, model) : 0;
2794
1604
  const spent = { iterations: turns, tokens, usd, ms: Date.now() - started };
2795
1605
  const out = { content: lastText };
2796
1606
  artifact = { outRef: contentRef("router-tools", { model, content: lastText }), out, spent };
@@ -3002,7 +1812,7 @@ async function* streamCliLeaf(args) {
3002
1812
  );
3003
1813
  }
3004
1814
  const out = { content: chunks.join("") };
3005
- args.onArtifact({ outRef: contentRef("cli", out), out, spent: zeroSpend2() });
1815
+ args.onArtifact({ outRef: contentRef("cli", out), out, spent: zeroSpend() });
3006
1816
  yield { kind: "iteration" };
3007
1817
  }
3008
1818
  function killWithGrace(proc, grace) {
@@ -3390,7 +2200,7 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
3390
2200
  inner = bridgeExecutor(spec, bridgeCtx);
3391
2201
  for (const msg of pending.splice(0)) inner.deliver?.(msg);
3392
2202
  const run = inner.execute(seam.taskPrompt, linked);
3393
- if (isAsyncIterable3(run)) {
2203
+ if (isAsyncIterable2(run)) {
3394
2204
  for await (const event of run) yield event;
3395
2205
  bridgeArtifact = inner.resultArtifact();
3396
2206
  } else {
@@ -3483,7 +2293,7 @@ function bridgeOutputText(out) {
3483
2293
  return String(out);
3484
2294
  }
3485
2295
  }
3486
- function isAsyncIterable3(value) {
2296
+ function isAsyncIterable2(value) {
3487
2297
  return value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
3488
2298
  }
3489
2299
  var cliWorktreeExecutor = (spec, ctx) => {
@@ -3641,179 +2451,6 @@ function mergeAbortSignals(...signals) {
3641
2451
  return c.signal;
3642
2452
  }
3643
2453
 
3644
- // src/runtime/supervise/driver-executor.ts
3645
- var driverRuntime = "driver";
3646
- var driverRole = "driver";
3647
- function isDriverSpec(spec) {
3648
- const role = spec.profile.metadata?.role;
3649
- if (role !== driverRole) return false;
3650
- const driver = spec.driver;
3651
- if (!isAgent(driver)) {
3652
- throw new ValidationError(
3653
- "driverExecutor: a driver-role spec must carry a `driver` Agent to run inside its nested scope"
3654
- );
3655
- }
3656
- return true;
3657
- }
3658
- var driverExecutorFactory = (spec, ctx) => {
3659
- if (!isDriverSpec(spec)) {
3660
- throw new ValidationError(
3661
- 'driverExecutorFactory: spec is not a driver child (no role:"driver" marker)'
3662
- );
3663
- }
3664
- const driver = spec.driver;
3665
- const journal = spec.journal;
3666
- const seam = readNestedScopeSeam(ctx);
3667
- let artifact;
3668
- let meteredSpend;
3669
- return {
3670
- runtime: driverRuntime,
3671
- async execute(task, signal) {
3672
- const nestedRoot = nestedTreeKey(seam, journal);
3673
- await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
3674
- const nestedScope = seam.mount(nestedRoot, signal);
3675
- try {
3676
- const out = await driver.act(task, nestedScope);
3677
- const events = await loadTreeEvents(journal, nestedRoot);
3678
- const settled = events.filter(isSettled);
3679
- meteredSpend = nonZeroOrUndef(sumMetered(events));
3680
- const verdict = deriveDeliveryVerdict(settled);
3681
- artifact = {
3682
- outRef: `${driverRuntime}:${nestedRoot}`,
3683
- out,
3684
- spent: sumSpend(settled),
3685
- ...verdict ? { verdict } : {}
3686
- };
3687
- return artifact;
3688
- } catch (err) {
3689
- meteredSpend = await safeSumMetered(journal, nestedRoot);
3690
- throw err;
3691
- }
3692
- },
3693
- metered() {
3694
- return meteredSpend;
3695
- },
3696
- teardown() {
3697
- return Promise.resolve({ destroyed: true });
3698
- },
3699
- resultArtifact() {
3700
- if (!artifact) {
3701
- throw new ValidationError("driverExecutor: resultArtifact() read before execute()");
3702
- }
3703
- return artifact;
3704
- }
3705
- };
3706
- };
3707
- function withDriverExecutor(base) {
3708
- return {
3709
- register: base.register.bind(base),
3710
- resolve(spec) {
3711
- const role = spec.profile.metadata?.role;
3712
- if (role === driverRole && !spec.executor) {
3713
- return { succeeded: true, value: driverExecutorFactory };
3714
- }
3715
- return base.resolve(spec);
3716
- }
3717
- };
3718
- }
3719
- function nestedTreeKey(seam, journal) {
3720
- return `${seam.journalRoot}/d${nextNestOrdinal(journal)}`;
3721
- }
3722
- var nestCounters = /* @__PURE__ */ new WeakMap();
3723
- function nextNestOrdinal(journal) {
3724
- let c = nestCounters.get(journal);
3725
- if (!c) {
3726
- c = { n: 0 };
3727
- nestCounters.set(journal, c);
3728
- }
3729
- return c.n++;
3730
- }
3731
- async function loadTreeEvents(journal, nestedRoot) {
3732
- const events = await journal.loadTree(nestedRoot);
3733
- if (events === void 0) {
3734
- throw new ValidationError(
3735
- `driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`
3736
- );
3737
- }
3738
- return events;
3739
- }
3740
- function isSettled(ev) {
3741
- return ev.kind === "settled";
3742
- }
3743
- function sumSpend(settled) {
3744
- const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3745
- for (const ev of settled) {
3746
- total.iterations += ev.spent.iterations;
3747
- total.tokens.input += ev.spent.tokens.input;
3748
- total.tokens.output += ev.spent.tokens.output;
3749
- total.usd += ev.spent.usd;
3750
- total.ms += ev.spent.ms;
3751
- }
3752
- return total;
3753
- }
3754
- function sumMetered(events) {
3755
- const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3756
- for (const ev of events) {
3757
- if (ev.kind !== "metered") continue;
3758
- total.iterations += ev.spend.iterations;
3759
- total.tokens.input += ev.spend.tokens.input;
3760
- total.tokens.output += ev.spend.tokens.output;
3761
- total.usd += ev.spend.usd;
3762
- total.ms += ev.spend.ms;
3763
- }
3764
- return total;
3765
- }
3766
- function isNonZeroSpend(s) {
3767
- return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
3768
- }
3769
- function nonZeroOrUndef(s) {
3770
- return isNonZeroSpend(s) ? s : void 0;
3771
- }
3772
- async function safeSumMetered(journal, nestedRoot) {
3773
- try {
3774
- return nonZeroOrUndef(sumMetered(await loadTreeEvents(journal, nestedRoot)));
3775
- } catch {
3776
- return void 0;
3777
- }
3778
- }
3779
- function deriveDeliveryVerdict(settled) {
3780
- let sawChild = false;
3781
- let anyValid = false;
3782
- let bestValidScore;
3783
- let bestDoneScore;
3784
- for (const ev of settled) {
3785
- sawChild = true;
3786
- if (ev.status !== "done") continue;
3787
- const score = ev.verdict?.score;
3788
- if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) {
3789
- bestDoneScore = score;
3790
- }
3791
- if (ev.verdict?.valid === true) {
3792
- anyValid = true;
3793
- if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) {
3794
- bestValidScore = score;
3795
- }
3796
- }
3797
- }
3798
- if (!sawChild) return void 0;
3799
- return {
3800
- valid: anyValid,
3801
- score: anyValid ? bestValidScore ?? 1 : bestDoneScore ?? 0
3802
- };
3803
- }
3804
- function readNestedScopeSeam(ctx) {
3805
- const seam = ctx.seams[nestedScopeSeamKey];
3806
- if (!seam || typeof seam.mount !== "function") {
3807
- throw new ValidationError(
3808
- `driverExecutor: missing required seam "${nestedScopeSeamKey}" \u2014 a driver child must be spawned through a Scope that seeds it (the keystone scope does)`
3809
- );
3810
- }
3811
- return seam;
3812
- }
3813
- function isAgent(value) {
3814
- return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
3815
- }
3816
-
3817
2454
  // src/runtime/supervise/run-context.ts
3818
2455
  function createInMemoryRunContext(opts = {}) {
3819
2456
  const base = createExecutorRegistry();
@@ -4480,7 +3117,7 @@ ${narrative}` : roster;
4480
3117
  return `${roster}
4481
3118
 
4482
3119
  ## Progress notes
4483
- Summary unavailable: ${errMessage2(e)}`;
3120
+ Summary unavailable: ${errMessage(e)}`;
4484
3121
  }
4485
3122
  }),
4486
3123
  ...opts.compaction.onCompact ? { onCompact: opts.compaction.onCompact } : {},
@@ -4551,7 +3188,7 @@ function safeJson(v) {
4551
3188
  return String(v);
4552
3189
  }
4553
3190
  }
4554
- function errMessage2(e) {
3191
+ function errMessage(e) {
4555
3192
  return e instanceof Error ? e.message : String(e);
4556
3193
  }
4557
3194
 
@@ -6551,24 +5188,6 @@ async function delegate(intent, opts = {}) {
6551
5188
  }
6552
5189
 
6553
5190
  export {
6554
- contentAddress,
6555
- InMemoryResultBlobStore,
6556
- InMemorySpawnJournal,
6557
- assertModelAllowed,
6558
- routerChatWithUsage,
6559
- routerChatWithTools,
6560
- routerToolLoop,
6561
- routerBrain,
6562
- defineRuntimeHooks,
6563
- composeRuntimeHooks,
6564
- notifyRuntimeHookEvent,
6565
- notifyRuntimeDecisionPoint,
6566
- createScope,
6567
- settledToIteration,
6568
- withDriverExecutor,
6569
- spendFromUsageEvents,
6570
- createBudgetPool,
6571
- createSupervisor,
6572
5191
  gateOnDeliverable,
6573
5192
  createWorktree,
6574
5193
  captureWorktreeDiff,
@@ -6644,4 +5263,4 @@ export {
6644
5263
  workerFromBackend,
6645
5264
  supervise
6646
5265
  };
6647
- //# sourceMappingURL=chunk-NC66AM3S.js.map
5266
+ //# sourceMappingURL=chunk-UO2L5VTP.js.map