@tangle-network/agent-runtime 0.93.1 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +4 -3
  2. package/dist/agent.d.ts +1 -1
  3. package/dist/agent.js +4 -5
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/candidate-execution/index.d.ts +10 -3
  7. package/dist/candidate-execution/index.js +8 -2
  8. package/dist/{chunk-XDSWWUKE.js → chunk-4E2GM63H.js} +6 -29
  9. package/dist/chunk-4E2GM63H.js.map +1 -0
  10. package/dist/{chunk-LQQPGRKT.js → chunk-4GBT4WQ6.js} +2 -2
  11. package/dist/{chunk-TKJ3Q6CQ.js → chunk-55P6V5JT.js} +2 -2
  12. package/dist/{chunk-OX3QRJOA.js → chunk-7I2AOKCI.js} +280 -250
  13. package/dist/chunk-7I2AOKCI.js.map +1 -0
  14. package/dist/{chunk-UO2L5VTP.js → chunk-FHZHADIM.js} +1328 -20
  15. package/dist/chunk-FHZHADIM.js.map +1 -0
  16. package/dist/{chunk-VMHKMNEU.js → chunk-H2D3DCEB.js} +8 -161
  17. package/dist/chunk-H2D3DCEB.js.map +1 -0
  18. package/dist/chunk-ISTDY47H.js +849 -0
  19. package/dist/chunk-ISTDY47H.js.map +1 -0
  20. package/dist/{chunk-P6B3Z7PR.js → chunk-MRYPFDHJ.js} +3 -3
  21. package/dist/{chunk-BVVRQ4YC.js → chunk-OO4EK3JB.js} +1228 -18
  22. package/dist/chunk-OO4EK3JB.js.map +1 -0
  23. package/dist/{chunk-CMYMTRGA.js → chunk-PX6SXX3M.js} +26 -3
  24. package/dist/chunk-PX6SXX3M.js.map +1 -0
  25. package/dist/{chunk-FRBHUNQ7.js → chunk-RTVVH2KL.js} +2 -2
  26. package/dist/{chunk-R5GWDTM3.js → chunk-ZEYAT33L.js} +2 -2
  27. package/dist/{completion-gate-BLaiN0-X.d.ts → completion-gate-BDkEYB_-.d.ts} +1 -1
  28. package/dist/{coordination-DxJ83oZA.d.ts → coordination-EGoRbbsd.d.ts} +4 -4
  29. package/dist/environment-provider.d.ts +2 -2
  30. package/dist/{improve-DDhQaaJT.d.ts → improve-ZzTEkpF_.d.ts} +11 -14
  31. package/dist/index.d.ts +35 -106
  32. package/dist/index.js +91 -35
  33. package/dist/index.js.map +1 -1
  34. package/dist/intelligence.d.ts +22 -5
  35. package/dist/intelligence.js +113 -19
  36. package/dist/intelligence.js.map +1 -1
  37. package/dist/knowledge.d.ts +6 -6
  38. package/dist/knowledge.js +4 -4
  39. package/dist/lifecycle.js +2 -2
  40. package/dist/{loop-runner-bin-kKUNGLyV.d.ts → loop-runner-bin-BRXC1RwC.d.ts} +2 -2
  41. package/dist/loop-runner-bin.d.ts +5 -5
  42. package/dist/loop-runner-bin.js +5 -6
  43. package/dist/loops.d.ts +456 -17
  44. package/dist/loops.js +38 -40
  45. package/dist/mcp/bin.js +4 -4
  46. package/dist/mcp/index.d.ts +8 -8
  47. package/dist/mcp/index.js +6 -7
  48. package/dist/mcp/index.js.map +1 -1
  49. package/dist/{openai-tools-E3woykz9.d.ts → openai-tools-D0ZSRCC6.d.ts} +1 -1
  50. package/dist/{prepare-DiVGKcwS.d.ts → prepare-BKxAiUcH.d.ts} +1 -1
  51. package/dist/profiles.d.ts +1 -1
  52. package/dist/{sanitize-C9go6tXj.d.ts → sanitize-C2jicjNf.d.ts} +1 -1
  53. package/dist/{supervise-T2pazU3G.d.ts → supervise-Gmf709kn.d.ts} +4 -4
  54. package/dist/{types-DAdIm4AC.d.ts → types-Cg_teUPO.d.ts} +2 -2
  55. package/dist/{types-B00NtbCs.d.ts → types-DWA64rbJ.d.ts} +1 -1
  56. package/dist/{worktree-fanout-BUb2Ag02.d.ts → worktree-fanout-BrB4att3.d.ts} +233 -233
  57. package/package.json +4 -4
  58. package/skills/build-with-agent-runtime/SKILL.md +1 -1
  59. package/dist/chunk-BVVRQ4YC.js.map +0 -1
  60. package/dist/chunk-CMYMTRGA.js.map +0 -1
  61. package/dist/chunk-FDJ7AHXG.js +0 -1229
  62. package/dist/chunk-FDJ7AHXG.js.map +0 -1
  63. package/dist/chunk-J2K6WIG6.js +0 -2172
  64. package/dist/chunk-J2K6WIG6.js.map +0 -1
  65. package/dist/chunk-OX3QRJOA.js.map +0 -1
  66. package/dist/chunk-UO2L5VTP.js.map +0 -1
  67. package/dist/chunk-VMHKMNEU.js.map +0 -1
  68. package/dist/chunk-XDSWWUKE.js.map +0 -1
  69. package/dist/structural-rollout-DHGDbhvR.d.ts +0 -446
  70. /package/dist/{chunk-LQQPGRKT.js.map → chunk-4GBT4WQ6.js.map} +0 -0
  71. /package/dist/{chunk-TKJ3Q6CQ.js.map → chunk-55P6V5JT.js.map} +0 -0
  72. /package/dist/{chunk-P6B3Z7PR.js.map → chunk-MRYPFDHJ.js.map} +0 -0
  73. /package/dist/{chunk-FRBHUNQ7.js.map → chunk-RTVVH2KL.js.map} +0 -0
  74. /package/dist/{chunk-R5GWDTM3.js.map → chunk-ZEYAT33L.js.map} +0 -0
@@ -3,14 +3,8 @@ import {
3
3
  } from "./chunk-DPEUKJRO.js";
4
4
  import {
5
5
  assertModelAllowed,
6
- buildLoopSpanNodes,
7
- createSupervisor,
8
- notifyRuntimeHookEvent,
9
- routerBrain,
10
- routerChatWithUsage,
11
- runBrainLoop,
12
- withDriverExecutor
13
- } from "./chunk-J2K6WIG6.js";
6
+ buildLoopSpanNodes
7
+ } from "./chunk-ISTDY47H.js";
14
8
  import {
15
9
  InMemoryResultBlobStore,
16
10
  InMemorySpawnJournal,
@@ -39,11 +33,12 @@ import {
39
33
  import {
40
34
  harnessInvocation,
41
35
  runLocalHarness
42
- } from "./chunk-CMYMTRGA.js";
36
+ } from "./chunk-PX6SXX3M.js";
43
37
  import {
44
38
  AgentEvalError,
45
39
  ConfigError,
46
40
  NotFoundError,
41
+ RuntimeRunStateError,
47
42
  ValidationError
48
43
  } from "./chunk-YEJR7IXO.js";
49
44
 
@@ -89,6 +84,576 @@ function isAsyncIterable(v) {
89
84
  return v != null && typeof v[Symbol.asyncIterator] === "function";
90
85
  }
91
86
 
87
+ // src/runtime-hooks.ts
88
+ function defineRuntimeHooks(hooks) {
89
+ return hooks;
90
+ }
91
+ function composeRuntimeHooks(...entries) {
92
+ const hooks = entries.filter((entry) => !!entry);
93
+ return {
94
+ onEvent: hooks.some((hook) => hook.onEvent) ? (event, context) => {
95
+ const pending = [];
96
+ for (const hook of hooks) {
97
+ const result = hook.onEvent?.(event, context);
98
+ if (isThenable(result)) pending.push(Promise.resolve(result));
99
+ }
100
+ if (pending.length > 0) return Promise.all(pending).then(() => void 0);
101
+ return void 0;
102
+ } : void 0,
103
+ onDecisionPoint: hooks.some((hook) => hook.onDecisionPoint) ? (point, context) => {
104
+ const pending = [];
105
+ for (const hook of hooks) {
106
+ const result = hook.onDecisionPoint?.(point, context);
107
+ if (isThenable(result)) pending.push(Promise.resolve(result));
108
+ }
109
+ if (pending.length > 0) return Promise.all(pending).then(() => void 0);
110
+ return void 0;
111
+ } : void 0,
112
+ onHookError: hooks.some((hook) => hook.onHookError) ? (error, context) => {
113
+ const pending = [];
114
+ for (const hook of hooks) {
115
+ const result = hook.onHookError?.(error, context);
116
+ if (isThenable(result)) pending.push(Promise.resolve(result));
117
+ }
118
+ if (pending.length > 0) return Promise.all(pending).then(() => void 0);
119
+ return void 0;
120
+ } : void 0
121
+ };
122
+ }
123
+ function notifyRuntimeHookEvent(hooks, event, context = {}) {
124
+ const onEvent = hooks?.onEvent;
125
+ if (!onEvent) return;
126
+ try {
127
+ const result = onEvent(event, context);
128
+ if (isThenable(result)) {
129
+ void result.catch((error) => {
130
+ notifyRuntimeHookError(hooks, toError(error), {
131
+ hook: "onEvent",
132
+ eventId: event.id,
133
+ target: event.target,
134
+ phase: event.phase
135
+ });
136
+ });
137
+ }
138
+ } catch (error) {
139
+ notifyRuntimeHookError(hooks, toError(error), {
140
+ hook: "onEvent",
141
+ eventId: event.id,
142
+ target: event.target,
143
+ phase: event.phase
144
+ });
145
+ }
146
+ }
147
+ function notifyRuntimeDecisionPoint(hooks, point, context = {}) {
148
+ const onDecisionPoint = hooks?.onDecisionPoint;
149
+ if (!onDecisionPoint) return;
150
+ try {
151
+ const result = onDecisionPoint(point, context);
152
+ if (isThenable(result)) {
153
+ void result.catch((error) => {
154
+ notifyRuntimeHookError(hooks, toError(error), {
155
+ hook: "onDecisionPoint",
156
+ decisionId: point.id,
157
+ decisionKind: point.kind
158
+ });
159
+ });
160
+ }
161
+ } catch (error) {
162
+ notifyRuntimeHookError(hooks, toError(error), {
163
+ hook: "onDecisionPoint",
164
+ decisionId: point.id,
165
+ decisionKind: point.kind
166
+ });
167
+ }
168
+ }
169
+ function notifyRuntimeHookError(hooks, error, context) {
170
+ try {
171
+ const result = hooks?.onHookError?.(error, context);
172
+ if (isThenable(result)) void result.catch(() => void 0);
173
+ } catch {
174
+ }
175
+ }
176
+ function isThenable(value) {
177
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
178
+ }
179
+ function toError(error) {
180
+ return error instanceof Error ? error : new Error(String(error));
181
+ }
182
+
183
+ // src/runtime/supervise/scope.ts
184
+ var nestedScopeSeamKey = "nested-scope";
185
+ function makeNestedScopeSeam(args, childNodeId) {
186
+ return {
187
+ depth: args.depth,
188
+ ...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
189
+ journalRoot: args.root,
190
+ mount(nestedRoot, signal) {
191
+ return createScope({
192
+ parentId: childNodeId,
193
+ root: nestedRoot,
194
+ pool: args.pool,
195
+ journal: args.journal,
196
+ blobs: args.blobs,
197
+ executors: args.executors,
198
+ // Re-seed the parent's NON-recursion seams (sandbox/router for leaf grandchildren);
199
+ // the nested scope adds its OWN nested-scope seam per child in `spawn`.
200
+ seams: args.seams,
201
+ depth: args.depth + 1,
202
+ ...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
203
+ signal,
204
+ ...args.now ? { now: args.now } : {},
205
+ ...args.hooks ? { hooks: args.hooks } : {}
206
+ });
207
+ }
208
+ };
209
+ }
210
+ function createScope(args) {
211
+ const children = /* @__PURE__ */ new Map();
212
+ let spawnOrdinal = 0;
213
+ let cursorSeq = 0;
214
+ let meterSeq = 0;
215
+ const now = args.now ?? Date.now;
216
+ function spawn4(agent, task, opts) {
217
+ if (args.maxDepth !== void 0 && args.depth >= args.maxDepth) {
218
+ return { ok: false, reason: "depth-exceeded" };
219
+ }
220
+ const spec = agent.executorSpec;
221
+ if (!isAgentSpec(spec)) {
222
+ throw new ValidationError(
223
+ `scope.spawn: agent "${agent.name}" exposes no \`executorSpec\` (AgentSpec) to resolve a Executor`
224
+ );
225
+ }
226
+ const resolved = args.executors.resolve(spec);
227
+ if (!resolved.succeeded) throw new ValidationError(`scope.spawn: ${resolved.error}`);
228
+ const reservation = args.pool.reserve(opts.budget);
229
+ if (!reservation.ok) return { ok: false, reason: reservation.reason };
230
+ try {
231
+ const ordinal = spawnOrdinal++;
232
+ const id = `${args.parentId}:s${ordinal}`;
233
+ const childAbort = new AbortController();
234
+ const cascadeAbort = () => childAbort.abort();
235
+ if (args.signal.aborted) childAbort.abort();
236
+ else args.signal.addEventListener("abort", cascadeAbort, { once: true });
237
+ const ctx = {
238
+ signal: childAbort.signal,
239
+ seams: { ...args.seams, [nestedScopeSeamKey]: makeNestedScopeSeam(args, id) }
240
+ };
241
+ const executor = resolved.value(spec, ctx);
242
+ const handle = {
243
+ id,
244
+ label: opts.label,
245
+ get status() {
246
+ return children.get(id)?.status ?? "cancelled";
247
+ },
248
+ abort(reason) {
249
+ childAbort.abort(reason);
250
+ }
251
+ };
252
+ const live = {
253
+ id,
254
+ status: "acquiring",
255
+ runtime: executor.runtime,
256
+ budget: opts.budget,
257
+ label: opts.label,
258
+ spent: zeroSpend(),
259
+ settled: void 0,
260
+ delivered: false,
261
+ executorDone: false,
262
+ ...executor.deliver ? { deliver: executor.deliver.bind(executor) } : {}
263
+ };
264
+ children.set(id, live);
265
+ void args.journal.appendEvent(args.root, {
266
+ kind: "spawned",
267
+ id,
268
+ parent: args.parentId,
269
+ label: opts.label,
270
+ budget: opts.budget,
271
+ runtime: executor.runtime,
272
+ seq: ordinal,
273
+ at: new Date(now()).toISOString()
274
+ });
275
+ notifyRuntimeHookEvent(
276
+ args.hooks,
277
+ {
278
+ id: `${id}:spawn`,
279
+ runId: args.root,
280
+ target: "agent.spawn",
281
+ phase: "after",
282
+ timestamp: now(),
283
+ stepIndex: ordinal,
284
+ parentId: args.parentId,
285
+ payload: {
286
+ childId: id,
287
+ label: opts.label,
288
+ runtime: executor.runtime,
289
+ budget: opts.budget,
290
+ depth: args.depth
291
+ }
292
+ },
293
+ { signal: args.signal }
294
+ );
295
+ const settled = runChild(
296
+ live,
297
+ executor,
298
+ childAbort,
299
+ task,
300
+ opts,
301
+ args.pool,
302
+ reservation.ticket,
303
+ args.blobs
304
+ ).then((s) => {
305
+ live.resolved = s;
306
+ return s;
307
+ }).finally(() => {
308
+ args.signal.removeEventListener("abort", cascadeAbort);
309
+ });
310
+ live.settled = settled;
311
+ return { ok: true, handle };
312
+ } catch (err) {
313
+ args.pool.reconcile(reservation.ticket, zeroSpend());
314
+ throw err;
315
+ }
316
+ }
317
+ async function next() {
318
+ const undelivered = () => [...children.values()].filter((c) => !c.delivered);
319
+ if (undelivered().length === 0) return null;
320
+ for (; ; ) {
321
+ const pending = undelivered();
322
+ if (pending.length === 0) return null;
323
+ const ready = pending.find((c) => c.resolved !== void 0);
324
+ const chosen = ready ?? await raceFirstSettled(pending);
325
+ if (chosen.delivered) continue;
326
+ chosen.delivered = true;
327
+ const seq = cursorSeq++;
328
+ const settlement = chosen.resolved;
329
+ if (!settlement) {
330
+ throw new ValidationError(
331
+ `scope.next: child '${chosen.id}' won the settle race without a resolved value`
332
+ );
333
+ }
334
+ return finalizeSettlement(chosen, settlement, seq, args, now);
335
+ }
336
+ }
337
+ async function nextResolved() {
338
+ for (; ; ) {
339
+ const candidates = [...children.values()].filter((c) => !c.delivered);
340
+ const pick = candidates.find((c) => c.resolved !== void 0) ?? candidates.find((c) => c.executorDone);
341
+ if (!pick) return null;
342
+ const settlement = await pick.settled;
343
+ if (pick.delivered) continue;
344
+ pick.delivered = true;
345
+ const seq = cursorSeq++;
346
+ return finalizeSettlement(pick, settlement, seq, args, now);
347
+ }
348
+ }
349
+ function send(nodeId, msg) {
350
+ const child = children.get(nodeId);
351
+ if (!child || child.delivered || !child.deliver) return false;
352
+ child.deliver(msg);
353
+ return true;
354
+ }
355
+ async function meter(spend, detail) {
356
+ const seq = meterSeq++;
357
+ args.pool.observe(spend);
358
+ await args.journal.appendEvent(args.root, {
359
+ kind: "metered",
360
+ id: args.parentId,
361
+ spend,
362
+ seq,
363
+ at: new Date(now()).toISOString()
364
+ });
365
+ notifyRuntimeHookEvent(
366
+ args.hooks,
367
+ {
368
+ id: `${args.parentId}:meter:${seq}`,
369
+ runId: args.root,
370
+ target: "agent.turn",
371
+ phase: "after",
372
+ timestamp: now(),
373
+ parentId: args.parentId,
374
+ payload: { spend, ...detail ?? {} }
375
+ },
376
+ { signal: args.signal }
377
+ );
378
+ }
379
+ return {
380
+ spawn: spawn4,
381
+ next,
382
+ nextResolved,
383
+ send,
384
+ signal: args.signal,
385
+ meter,
386
+ get view() {
387
+ return makeTreeView(args.parentId, children);
388
+ },
389
+ get budget() {
390
+ return args.pool.readout();
391
+ }
392
+ };
393
+ }
394
+ async function raceFirstSettled(pending) {
395
+ return Promise.race(pending.map((c) => c.settled.then(() => c)));
396
+ }
397
+ async function finalizeSettlement(child, settlement, seq, args, now) {
398
+ const handle = frozenHandle(child);
399
+ if (settlement.kind === "down") {
400
+ child.status = "failed";
401
+ await args.journal.appendEvent(args.root, {
402
+ kind: "settled",
403
+ id: child.id,
404
+ status: "down",
405
+ spent: child.spent,
406
+ infra: settlement.infra,
407
+ seq,
408
+ at: new Date(now()).toISOString()
409
+ });
410
+ if (settlement.metered) {
411
+ await args.journal.appendEvent(args.root, {
412
+ kind: "metered",
413
+ id: child.id,
414
+ spend: settlement.metered,
415
+ seq,
416
+ at: new Date(now()).toISOString()
417
+ });
418
+ }
419
+ notifyRuntimeHookEvent(
420
+ args.hooks,
421
+ {
422
+ id: `${child.id}:settled`,
423
+ runId: args.root,
424
+ target: "agent.child",
425
+ phase: "after",
426
+ timestamp: now(),
427
+ stepIndex: seq,
428
+ parentId: args.parentId,
429
+ payload: {
430
+ childId: child.id,
431
+ status: "down",
432
+ reason: settlement.reason,
433
+ infra: settlement.infra,
434
+ spent: child.spent
435
+ }
436
+ },
437
+ { signal: args.signal }
438
+ );
439
+ return {
440
+ kind: "down",
441
+ handle,
442
+ reason: settlement.reason,
443
+ infra: settlement.infra,
444
+ restartCount: settlement.restartCount,
445
+ seq
446
+ };
447
+ }
448
+ child.status = "done";
449
+ child.outRef = settlement.outRef;
450
+ child.spent = settlement.spent;
451
+ await args.journal.appendEvent(args.root, {
452
+ kind: "settled",
453
+ id: child.id,
454
+ status: "done",
455
+ outRef: settlement.outRef,
456
+ ...settlement.verdict ? { verdict: settlement.verdict } : {},
457
+ spent: settlement.spent,
458
+ seq,
459
+ at: new Date(now()).toISOString()
460
+ });
461
+ if (settlement.metered) {
462
+ await args.journal.appendEvent(args.root, {
463
+ kind: "metered",
464
+ id: child.id,
465
+ spend: settlement.metered,
466
+ seq,
467
+ at: new Date(now()).toISOString()
468
+ });
469
+ }
470
+ notifyRuntimeHookEvent(
471
+ args.hooks,
472
+ {
473
+ id: `${child.id}:settled`,
474
+ runId: args.root,
475
+ target: "agent.child",
476
+ phase: "after",
477
+ timestamp: now(),
478
+ stepIndex: seq,
479
+ parentId: args.parentId,
480
+ payload: {
481
+ childId: child.id,
482
+ status: "done",
483
+ outRef: settlement.outRef,
484
+ score: settlement.verdict?.score,
485
+ valid: settlement.verdict?.valid,
486
+ spent: settlement.spent
487
+ }
488
+ },
489
+ { signal: args.signal }
490
+ );
491
+ return {
492
+ kind: "done",
493
+ handle,
494
+ out: settlement.out,
495
+ outRef: settlement.outRef,
496
+ ...settlement.verdict ? { verdict: settlement.verdict } : {},
497
+ spent: settlement.spent,
498
+ seq
499
+ };
500
+ }
501
+ async function runChild(live, executor, childAbort, task, opts, pool, ticket, blobs) {
502
+ let reconciled = false;
503
+ const reconcileOnce = (spend) => {
504
+ if (reconciled) return;
505
+ reconciled = true;
506
+ pool.reconcile(ticket, clampSpend(spend, opts.budget));
507
+ };
508
+ try {
509
+ live.status = "running";
510
+ const ran = executor.execute(task, childAbort.signal);
511
+ let artifact;
512
+ if (isAsyncIterable2(ran)) {
513
+ const spend = await foldStream(ran);
514
+ live.spent = spend;
515
+ artifact = executor.resultArtifact();
516
+ reconcileOnce(spend);
517
+ } else {
518
+ const terminal = await ran;
519
+ live.spent = terminal.spent;
520
+ artifact = terminal;
521
+ reconcileOnce(terminal.spent);
522
+ }
523
+ live.executorDone = true;
524
+ const ownMetered = executor.metered?.();
525
+ if (childAbort.signal.aborted) {
526
+ await teardownSafe(executor, opts.shutdown ?? "brutalKill");
527
+ return downRecord("aborted before settle", true, ownMetered);
528
+ }
529
+ const outRef = contentAddress(artifact.out);
530
+ await blobs.put(outRef, artifact.out);
531
+ await teardownSafe(executor, opts.shutdown ?? "infinity");
532
+ return {
533
+ kind: "done",
534
+ out: artifact.out,
535
+ outRef,
536
+ ...artifact.verdict ? { verdict: artifact.verdict } : {},
537
+ spent: live.spent,
538
+ ...ownMetered ? { metered: ownMetered } : {}
539
+ };
540
+ } catch (err) {
541
+ live.executorDone = true;
542
+ reconcileOnce(live.spent);
543
+ await teardownSafe(executor, "brutalKill");
544
+ const aborted = childAbort.signal.aborted || isAbortError(err);
545
+ return downRecord(errMessage(err), aborted || isInfraError(err), executor.metered?.());
546
+ }
547
+ }
548
+ function settledToIteration(settled) {
549
+ if (settled.kind === "down") {
550
+ throw new ValidationError(
551
+ `settledToIteration: cannot adapt a 'down' settlement (node '${settled.handle.id}', seq ${settled.seq}) to an Iteration`
552
+ );
553
+ }
554
+ return {
555
+ index: settled.seq,
556
+ task: void 0,
557
+ agentRunName: settled.handle.label,
558
+ output: settled.out,
559
+ ...settled.verdict ? { verdict: settled.verdict } : {},
560
+ events: [],
561
+ startedAt: 0,
562
+ endedAt: settled.spent.ms,
563
+ costUsd: settled.spent.usd,
564
+ tokenUsage: { input: settled.spent.tokens.input, output: settled.spent.tokens.output }
565
+ };
566
+ }
567
+ function makeTreeView(root, children) {
568
+ const nodes = [...children.values()].map((c) => ({
569
+ id: c.id,
570
+ parent: root,
571
+ label: c.label,
572
+ status: c.status,
573
+ runtime: c.runtime,
574
+ budget: c.budget,
575
+ spent: c.spent,
576
+ ...c.outRef ? { outRef: c.outRef } : {}
577
+ }));
578
+ return {
579
+ root,
580
+ nodes,
581
+ inFlight: nodes.filter((n) => n.status === "running" || n.status === "acquiring").length
582
+ };
583
+ }
584
+ function frozenHandle(child) {
585
+ return {
586
+ id: child.id,
587
+ label: child.label,
588
+ status: child.status,
589
+ abort() {
590
+ }
591
+ };
592
+ }
593
+ async function foldStream(stream) {
594
+ const tokens = { input: 0, output: 0 };
595
+ let usd = 0;
596
+ let iterations = 0;
597
+ for await (const ev of stream) {
598
+ if (ev.kind === "tokens") {
599
+ tokens.input += ev.input;
600
+ tokens.output += ev.output;
601
+ } else if (ev.kind === "cost") {
602
+ usd += ev.usd;
603
+ } else {
604
+ iterations += 1;
605
+ }
606
+ }
607
+ return { iterations, tokens, usd, ms: 0 };
608
+ }
609
+ function clampSpend(spend, budget) {
610
+ const totalTokens2 = spend.tokens.input + spend.tokens.output;
611
+ const tokensOk = totalTokens2 <= budget.maxTokens;
612
+ const itersOk = spend.iterations <= budget.maxIterations;
613
+ const usdOk = budget.maxUsd === void 0 || spend.usd <= budget.maxUsd;
614
+ if (tokensOk && itersOk && usdOk) return spend;
615
+ const ratio = !tokensOk && totalTokens2 > 0 ? budget.maxTokens / totalTokens2 : 1;
616
+ return {
617
+ iterations: Math.min(spend.iterations, budget.maxIterations),
618
+ tokens: ratio < 1 ? {
619
+ input: Math.floor(spend.tokens.input * ratio),
620
+ output: Math.floor(spend.tokens.output * ratio)
621
+ } : spend.tokens,
622
+ usd: budget.maxUsd === void 0 ? spend.usd : Math.min(spend.usd, budget.maxUsd),
623
+ ms: spend.ms
624
+ };
625
+ }
626
+ async function teardownSafe(executor, grace) {
627
+ try {
628
+ await executor.teardown(grace);
629
+ } catch {
630
+ }
631
+ }
632
+ function downRecord(reason, infra, metered) {
633
+ return { kind: "down", reason, infra, restartCount: 0, ...metered ? { metered } : {} };
634
+ }
635
+ function zeroSpend() {
636
+ return { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
637
+ }
638
+ function isAsyncIterable2(value) {
639
+ return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
640
+ }
641
+ function isAgentSpec(value) {
642
+ if (typeof value !== "object" || value === null) return false;
643
+ const v = value;
644
+ return "profile" in v && "harness" in v;
645
+ }
646
+ function isAbortError(err) {
647
+ return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
648
+ }
649
+ function isInfraError(err) {
650
+ return err instanceof ValidationError;
651
+ }
652
+ function errMessage(err) {
653
+ if (err instanceof Error) return err.message;
654
+ return String(err);
655
+ }
656
+
92
657
  // src/mcp/worktree.ts
93
658
  import { spawn } from "child_process";
94
659
  async function runGitAsync(args, cwd, runner) {
@@ -175,6 +740,216 @@ async function removeWorktree(options) {
175
740
  ).catch(() => void 0);
176
741
  }
177
742
 
743
+ // src/runtime/router-client.ts
744
+ import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
745
+
746
+ // src/runtime/tool-loop.ts
747
+ function estimateConversationTokens(messages) {
748
+ let chars = 0;
749
+ for (const m of messages) {
750
+ const content = m.content;
751
+ chars += typeof content === "string" ? content.length : safeJsonLength(content);
752
+ const calls = m.tool_calls;
753
+ if (calls) for (const c of calls) chars += c.function?.arguments?.length ?? 0;
754
+ }
755
+ return Math.ceil(chars / 4);
756
+ }
757
+ function safeJsonLength(value) {
758
+ if (value === void 0 || value === null) return 0;
759
+ try {
760
+ return JSON.stringify(value)?.length ?? 0;
761
+ } catch {
762
+ return String(value).length;
763
+ }
764
+ }
765
+ async function maybeCompact(messages, c, turn) {
766
+ const head = c.preserveHead !== void 0 && c.preserveHead > 0 ? c.preserveHead : 2;
767
+ if (messages.length <= head + 1) return false;
768
+ const estimate = c.estimateTokens ?? estimateConversationTokens;
769
+ const before = estimate(messages);
770
+ if (before <= c.thresholdTokens) return false;
771
+ const digest = await c.distill([...messages]);
772
+ messages.splice(head, messages.length - head, {
773
+ role: "user",
774
+ content: `[earlier work compacted to save context \u2014 progress so far]
775
+ ${digest}`
776
+ });
777
+ c.onCompact?.({ turn, beforeTokens: before, afterTokens: estimate(messages) });
778
+ return true;
779
+ }
780
+ async function runBrainLoop(opts) {
781
+ const maxTurns = opts.maxTurns ?? 4;
782
+ const messages = [...opts.initialMessages];
783
+ let toolCalls = 0;
784
+ let lastText = "";
785
+ const usage = { input: 0, output: 0 };
786
+ const toolTrace = [];
787
+ for (let turn = 1; turn <= maxTurns; turn += 1) {
788
+ if (opts.hooks?.stopBefore?.(turn)) break;
789
+ await opts.hooks?.beforeTurn?.(turn, messages);
790
+ if (opts.compaction) await maybeCompact(messages, opts.compaction, turn);
791
+ const r = await opts.chat(messages, opts.tools);
792
+ if (r.usage) {
793
+ usage.input += r.usage.input;
794
+ usage.output += r.usage.output;
795
+ opts.hooks?.onUsage?.(r.usage);
796
+ }
797
+ if (r.content) lastText = r.content;
798
+ if (r.toolCalls.length === 0) {
799
+ if (r.content) messages.push({ role: "assistant", content: r.content });
800
+ return { final: lastText, turns: turn, toolCalls, toolTrace, usage, messages };
801
+ }
802
+ messages.push({
803
+ role: "assistant",
804
+ content: r.content ?? "",
805
+ tool_calls: r.toolCalls.map((tc) => ({
806
+ id: tc.id,
807
+ type: "function",
808
+ function: { name: tc.name, arguments: tc.arguments }
809
+ }))
810
+ });
811
+ for (const tc of r.toolCalls) {
812
+ toolCalls += 1;
813
+ let args = {};
814
+ try {
815
+ args = JSON.parse(tc.arguments);
816
+ } catch {
817
+ messages.push({
818
+ role: "tool",
819
+ tool_call_id: tc.id,
820
+ content: `error: arguments were not valid JSON: ${tc.arguments.slice(0, 200)}`
821
+ });
822
+ continue;
823
+ }
824
+ const out = await opts.execute(tc.name, args);
825
+ messages.push({ role: "tool", tool_call_id: tc.id, content: out });
826
+ toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
827
+ }
828
+ }
829
+ return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
830
+ }
831
+
832
+ // src/runtime/router-client.ts
833
+ async function routerChatWithUsage(cfg, messages, opts) {
834
+ const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
835
+ const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
836
+ let temperature = opts?.temperature ?? 0.2;
837
+ const body = () => ({
838
+ model: cfg.model,
839
+ messages,
840
+ temperature,
841
+ max_tokens: opts?.maxTokens ?? 8192,
842
+ ...opts?.reasoningEffort ? { reasoning_effort: opts.reasoningEffort } : {}
843
+ });
844
+ if (cfg.complete) return parseChatResult(await cfg.complete(body()), cfg.model);
845
+ let lastErr = "";
846
+ for (let attempt = 0; attempt < 5; attempt += 1) {
847
+ const res = await fetch(url, {
848
+ method: "POST",
849
+ headers,
850
+ body: JSON.stringify(body()),
851
+ ...opts?.signal ? { signal: opts.signal } : {}
852
+ });
853
+ if (res.ok) return parseChatResult(await res.json(), cfg.model);
854
+ const status = res.status;
855
+ const text = (await res.text()).slice(0, 200);
856
+ lastErr = `router ${status}: ${text}`;
857
+ if (status === 400 && /temperature/i.test(text) && temperature !== 1) {
858
+ temperature = 1;
859
+ continue;
860
+ }
861
+ if (![408, 425, 429, 500, 502, 503, 504, 520, 522, 524].includes(status))
862
+ throw new Error(lastErr);
863
+ if (attempt < 4) await new Promise((r) => setTimeout(r, 800 * 2 ** attempt));
864
+ }
865
+ throw new Error(`${lastErr} (exhausted retries)`);
866
+ }
867
+ function parseChatResult(json, model) {
868
+ const data = json;
869
+ const u = data.usage;
870
+ const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
871
+ const costUsd = usage && isModelPriced(model) ? estimateCost(usage.input, usage.output, model) : void 0;
872
+ const msg = data.choices?.[0]?.message;
873
+ const { content, reasoning } = splitReasoning(
874
+ msg?.content ?? "",
875
+ msg?.reasoning ?? msg?.reasoning_content
876
+ );
877
+ return {
878
+ content,
879
+ ...reasoning ? { reasoning } : {},
880
+ ...usage ? { usage } : {},
881
+ ...costUsd !== void 0 ? { costUsd } : {}
882
+ };
883
+ }
884
+ function splitReasoning(rawContent, fieldReasoning) {
885
+ const open = rawContent.indexOf("<think>");
886
+ if (open !== -1) {
887
+ const close = rawContent.indexOf("</think>", open);
888
+ const inline = close !== -1 ? rawContent.slice(open + 7, close) : rawContent.slice(open + 7);
889
+ const rest = close !== -1 ? rawContent.slice(0, open) + rawContent.slice(close + 8) : rawContent.slice(0, open);
890
+ const reasoning = [fieldReasoning, inline.trim()].filter(Boolean).join("\n");
891
+ return { content: rest.trim(), ...reasoning ? { reasoning } : {} };
892
+ }
893
+ return { content: rawContent, ...fieldReasoning ? { reasoning: fieldReasoning } : {} };
894
+ }
895
+ async function routerChatWithTools(cfg, messages, tools, opts) {
896
+ const body = {
897
+ model: cfg.model,
898
+ messages,
899
+ tools,
900
+ tool_choice: opts?.toolChoice ?? "auto",
901
+ temperature: opts?.temperature ?? 0.3,
902
+ ...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
903
+ };
904
+ const raw = cfg.complete ? await cfg.complete(body) : await (async () => {
905
+ const res = await fetch(`${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
906
+ method: "POST",
907
+ headers: { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` },
908
+ body: JSON.stringify(body),
909
+ ...opts?.signal ? { signal: opts.signal } : {}
910
+ });
911
+ if (!res.ok) throw new Error(`router ${res.status}: ${(await res.text()).slice(0, 200)}`);
912
+ return res.json();
913
+ })();
914
+ const data = raw;
915
+ const msg = data.choices?.[0]?.message;
916
+ const toolCalls = (msg?.tool_calls ?? []).map((tc, i) => ({
917
+ id: tc.id ?? `call_${i}`,
918
+ name: tc.function?.name ?? "",
919
+ arguments: tc.function?.arguments ?? "{}"
920
+ }));
921
+ const u = data.usage;
922
+ const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
923
+ const costUsd = usage && isModelPriced(cfg.model) ? estimateCost(usage.input, usage.output, cfg.model) : void 0;
924
+ return {
925
+ content: msg?.content ?? null,
926
+ toolCalls,
927
+ ...usage ? { usage } : {},
928
+ ...costUsd !== void 0 ? { costUsd } : {}
929
+ };
930
+ }
931
+ async function routerToolLoop(cfg, system, user, tools, execute, opts) {
932
+ const initialMessages = opts?.initialMessages ?? [
933
+ { role: "system", content: system },
934
+ { role: "user", content: user }
935
+ ];
936
+ return runBrainLoop({
937
+ chat: (messages, toolSpecs) => routerChatWithTools(cfg, messages, toolSpecs, {
938
+ ...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
939
+ ...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
940
+ ...opts?.signal ? { signal: opts.signal } : {}
941
+ }),
942
+ tools,
943
+ execute,
944
+ initialMessages,
945
+ maxTurns: opts?.maxTurns ?? 4
946
+ });
947
+ }
948
+ function routerBrain(cfg, opts = {}) {
949
+ const temperature = opts.temperature ?? 0.4;
950
+ return (messages, tools) => routerChatWithTools(cfg, messages, tools, { temperature, toolChoice: "auto" });
951
+ }
952
+
178
953
  // src/runtime/sandbox-acquire.ts
179
954
  var RETRYABLE_HTTP = /* @__PURE__ */ new Set([502, 503, 504, 522, 524, 408, 425, 429]);
180
955
  var TERMINAL_STATUS = /* @__PURE__ */ new Set(["failed", "expired", "stopped"]);
@@ -865,13 +1640,13 @@ async function executeIteration(args) {
865
1640
  await destroySandboxSafe(box, args.ctx.traceEmitter, args.runId, args.now);
866
1641
  }
867
1642
  }
868
- if (isAbortError(slot.error) || args.signal.aborted) {
1643
+ if (isAbortError2(slot.error) || args.signal.aborted) {
869
1644
  if (slot.error) throw slot.error;
870
1645
  throwAbort();
871
1646
  }
872
1647
  if (slot.error instanceof ValidationError) throw slot.error;
873
1648
  }
874
- function isAbortError(err) {
1649
+ function isAbortError2(err) {
875
1650
  return err instanceof Error && err.name === "AbortError";
876
1651
  }
877
1652
  var TEARDOWN_TIMEOUT_MS2 = 15e3;
@@ -1385,7 +2160,7 @@ import { randomUUID as randomUUID2 } from "crypto";
1385
2160
  import { request as httpRequest } from "http";
1386
2161
  import { request as httpsRequest } from "https";
1387
2162
  import { Readable } from "stream";
1388
- import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
2163
+ import { estimateCost as estimateCost2, isModelPriced as isModelPriced2 } from "@tangle-network/agent-eval";
1389
2164
  var routerSeamKey = "router";
1390
2165
  var sandboxSeamKey = "sandbox";
1391
2166
  var cliSeamKey = "cli";
@@ -1406,7 +2181,7 @@ function contentRef(prefix, value) {
1406
2181
  }
1407
2182
  return `${prefix}:${(h >>> 0).toString(16).padStart(8, "0")}`;
1408
2183
  }
1409
- function zeroSpend() {
2184
+ function zeroSpend2() {
1410
2185
  return { iterations: 0, tokens: zeroTokenUsage(), usd: 0, ms: 0 };
1411
2186
  }
1412
2187
  var routerInlineExecutor = (spec, ctx) => {
@@ -1600,7 +2375,7 @@ var routerToolsInlineExecutor = (spec, ctx) => {
1600
2375
  }
1601
2376
  }
1602
2377
  }
1603
- const usd = isModelPriced(model) ? estimateCost(tokens.input, tokens.output, model) : 0;
2378
+ const usd = isModelPriced2(model) ? estimateCost2(tokens.input, tokens.output, model) : 0;
1604
2379
  const spent = { iterations: turns, tokens, usd, ms: Date.now() - started };
1605
2380
  const out = { content: lastText };
1606
2381
  artifact = { outRef: contentRef("router-tools", { model, content: lastText }), out, spent };
@@ -1812,7 +2587,7 @@ async function* streamCliLeaf(args) {
1812
2587
  );
1813
2588
  }
1814
2589
  const out = { content: chunks.join("") };
1815
- args.onArtifact({ outRef: contentRef("cli", out), out, spent: zeroSpend() });
2590
+ args.onArtifact({ outRef: contentRef("cli", out), out, spent: zeroSpend2() });
1816
2591
  yield { kind: "iteration" };
1817
2592
  }
1818
2593
  function killWithGrace(proc, grace) {
@@ -2200,7 +2975,7 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
2200
2975
  inner = bridgeExecutor(spec, bridgeCtx);
2201
2976
  for (const msg of pending.splice(0)) inner.deliver?.(msg);
2202
2977
  const run = inner.execute(seam.taskPrompt, linked);
2203
- if (isAsyncIterable2(run)) {
2978
+ if (isAsyncIterable3(run)) {
2204
2979
  for await (const event of run) yield event;
2205
2980
  bridgeArtifact = inner.resultArtifact();
2206
2981
  } else {
@@ -2293,7 +3068,7 @@ function bridgeOutputText(out) {
2293
3068
  return String(out);
2294
3069
  }
2295
3070
  }
2296
- function isAsyncIterable2(value) {
3071
+ function isAsyncIterable3(value) {
2297
3072
  return value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
2298
3073
  }
2299
3074
  var cliWorktreeExecutor = (spec, ctx) => {
@@ -2451,6 +3226,179 @@ function mergeAbortSignals(...signals) {
2451
3226
  return c.signal;
2452
3227
  }
2453
3228
 
3229
+ // src/runtime/supervise/driver-executor.ts
3230
+ var driverRuntime = "driver";
3231
+ var driverRole = "driver";
3232
+ function isDriverSpec(spec) {
3233
+ const role = spec.profile.metadata?.role;
3234
+ if (role !== driverRole) return false;
3235
+ const driver = spec.driver;
3236
+ if (!isAgent(driver)) {
3237
+ throw new ValidationError(
3238
+ "driverExecutor: a driver-role spec must carry a `driver` Agent to run inside its nested scope"
3239
+ );
3240
+ }
3241
+ return true;
3242
+ }
3243
+ var driverExecutorFactory = (spec, ctx) => {
3244
+ if (!isDriverSpec(spec)) {
3245
+ throw new ValidationError(
3246
+ 'driverExecutorFactory: spec is not a driver child (no role:"driver" marker)'
3247
+ );
3248
+ }
3249
+ const driver = spec.driver;
3250
+ const journal = spec.journal;
3251
+ const seam = readNestedScopeSeam(ctx);
3252
+ let artifact;
3253
+ let meteredSpend;
3254
+ return {
3255
+ runtime: driverRuntime,
3256
+ async execute(task, signal) {
3257
+ const nestedRoot = nestedTreeKey(seam, journal);
3258
+ await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
3259
+ const nestedScope = seam.mount(nestedRoot, signal);
3260
+ try {
3261
+ const out = await driver.act(task, nestedScope);
3262
+ const events = await loadTreeEvents(journal, nestedRoot);
3263
+ const settled = events.filter(isSettled);
3264
+ meteredSpend = nonZeroOrUndef(sumMetered(events));
3265
+ const verdict = deriveDeliveryVerdict(settled);
3266
+ artifact = {
3267
+ outRef: `${driverRuntime}:${nestedRoot}`,
3268
+ out,
3269
+ spent: sumSpend(settled),
3270
+ ...verdict ? { verdict } : {}
3271
+ };
3272
+ return artifact;
3273
+ } catch (err) {
3274
+ meteredSpend = await safeSumMetered(journal, nestedRoot);
3275
+ throw err;
3276
+ }
3277
+ },
3278
+ metered() {
3279
+ return meteredSpend;
3280
+ },
3281
+ teardown() {
3282
+ return Promise.resolve({ destroyed: true });
3283
+ },
3284
+ resultArtifact() {
3285
+ if (!artifact) {
3286
+ throw new ValidationError("driverExecutor: resultArtifact() read before execute()");
3287
+ }
3288
+ return artifact;
3289
+ }
3290
+ };
3291
+ };
3292
+ function withDriverExecutor(base) {
3293
+ return {
3294
+ register: base.register.bind(base),
3295
+ resolve(spec) {
3296
+ const role = spec.profile.metadata?.role;
3297
+ if (role === driverRole && !spec.executor) {
3298
+ return { succeeded: true, value: driverExecutorFactory };
3299
+ }
3300
+ return base.resolve(spec);
3301
+ }
3302
+ };
3303
+ }
3304
+ function nestedTreeKey(seam, journal) {
3305
+ return `${seam.journalRoot}/d${nextNestOrdinal(journal)}`;
3306
+ }
3307
+ var nestCounters = /* @__PURE__ */ new WeakMap();
3308
+ function nextNestOrdinal(journal) {
3309
+ let c = nestCounters.get(journal);
3310
+ if (!c) {
3311
+ c = { n: 0 };
3312
+ nestCounters.set(journal, c);
3313
+ }
3314
+ return c.n++;
3315
+ }
3316
+ async function loadTreeEvents(journal, nestedRoot) {
3317
+ const events = await journal.loadTree(nestedRoot);
3318
+ if (events === void 0) {
3319
+ throw new ValidationError(
3320
+ `driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`
3321
+ );
3322
+ }
3323
+ return events;
3324
+ }
3325
+ function isSettled(ev) {
3326
+ return ev.kind === "settled";
3327
+ }
3328
+ function sumSpend(settled) {
3329
+ const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3330
+ for (const ev of settled) {
3331
+ total.iterations += ev.spent.iterations;
3332
+ total.tokens.input += ev.spent.tokens.input;
3333
+ total.tokens.output += ev.spent.tokens.output;
3334
+ total.usd += ev.spent.usd;
3335
+ total.ms += ev.spent.ms;
3336
+ }
3337
+ return total;
3338
+ }
3339
+ function sumMetered(events) {
3340
+ const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3341
+ for (const ev of events) {
3342
+ if (ev.kind !== "metered") continue;
3343
+ total.iterations += ev.spend.iterations;
3344
+ total.tokens.input += ev.spend.tokens.input;
3345
+ total.tokens.output += ev.spend.tokens.output;
3346
+ total.usd += ev.spend.usd;
3347
+ total.ms += ev.spend.ms;
3348
+ }
3349
+ return total;
3350
+ }
3351
+ function isNonZeroSpend(s) {
3352
+ return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
3353
+ }
3354
+ function nonZeroOrUndef(s) {
3355
+ return isNonZeroSpend(s) ? s : void 0;
3356
+ }
3357
+ async function safeSumMetered(journal, nestedRoot) {
3358
+ try {
3359
+ return nonZeroOrUndef(sumMetered(await loadTreeEvents(journal, nestedRoot)));
3360
+ } catch {
3361
+ return void 0;
3362
+ }
3363
+ }
3364
+ function deriveDeliveryVerdict(settled) {
3365
+ let sawChild = false;
3366
+ let anyValid = false;
3367
+ let bestValidScore;
3368
+ let bestDoneScore;
3369
+ for (const ev of settled) {
3370
+ sawChild = true;
3371
+ if (ev.status !== "done") continue;
3372
+ const score = ev.verdict?.score;
3373
+ if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) {
3374
+ bestDoneScore = score;
3375
+ }
3376
+ if (ev.verdict?.valid === true) {
3377
+ anyValid = true;
3378
+ if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) {
3379
+ bestValidScore = score;
3380
+ }
3381
+ }
3382
+ }
3383
+ if (!sawChild) return void 0;
3384
+ return {
3385
+ valid: anyValid,
3386
+ score: anyValid ? bestValidScore ?? 1 : bestDoneScore ?? 0
3387
+ };
3388
+ }
3389
+ function readNestedScopeSeam(ctx) {
3390
+ const seam = ctx.seams[nestedScopeSeamKey];
3391
+ if (!seam || typeof seam.mount !== "function") {
3392
+ throw new ValidationError(
3393
+ `driverExecutor: missing required seam "${nestedScopeSeamKey}" \u2014 a driver child must be spawned through a Scope that seeds it (the keystone scope does)`
3394
+ );
3395
+ }
3396
+ return seam;
3397
+ }
3398
+ function isAgent(value) {
3399
+ return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
3400
+ }
3401
+
2454
3402
  // src/runtime/supervise/run-context.ts
2455
3403
  function createInMemoryRunContext(opts = {}) {
2456
3404
  const base = createExecutorRegistry();
@@ -2461,6 +3409,352 @@ function createInMemoryRunContext(opts = {}) {
2461
3409
  };
2462
3410
  }
2463
3411
 
3412
+ // src/runtime/supervise/budget.ts
3413
+ function spendFromUsageEvents(events) {
3414
+ const tokens = zeroTokenUsage();
3415
+ let usd = 0;
3416
+ let iterations = 0;
3417
+ for (const ev of events) {
3418
+ if (ev.kind === "tokens") {
3419
+ addTokenUsage(tokens, { input: ev.input, output: ev.output });
3420
+ } else if (ev.kind === "cost") {
3421
+ usd += ev.usd;
3422
+ } else {
3423
+ iterations += 1;
3424
+ }
3425
+ }
3426
+ return { iterations, tokens, usd, ms: 0 };
3427
+ }
3428
+ async function foldUsage(events) {
3429
+ if (Array.isArray(events)) return spendFromUsageEvents(events);
3430
+ const tokens = zeroTokenUsage();
3431
+ let usd = 0;
3432
+ let iterations = 0;
3433
+ for await (const ev of events) {
3434
+ if (ev.kind === "tokens") {
3435
+ addTokenUsage(tokens, { input: ev.input, output: ev.output });
3436
+ } else if (ev.kind === "cost") {
3437
+ usd += ev.usd;
3438
+ } else {
3439
+ iterations += 1;
3440
+ }
3441
+ }
3442
+ return { iterations, tokens, usd, ms: 0 };
3443
+ }
3444
+ function totalTokens(usage) {
3445
+ return usage.input + usage.output;
3446
+ }
3447
+ function createBudgetPool(root, now = Date.now) {
3448
+ let freeTokens = root.maxTokens;
3449
+ let reservedTokens = 0;
3450
+ let committedTokens = 0;
3451
+ const usdCapped = root.maxUsd !== void 0;
3452
+ let freeUsd = root.maxUsd ?? 0;
3453
+ let reservedUsd = 0;
3454
+ let committedUsd = 0;
3455
+ let freeIterations = root.maxIterations;
3456
+ let reservedIterations = 0;
3457
+ let committedIterations = 0;
3458
+ const absoluteDeadlineMs = root.deadlineMs !== void 0 ? now() + root.deadlineMs : 0;
3459
+ let nextTicketId = 0;
3460
+ const open = /* @__PURE__ */ new Set();
3461
+ function reserve(b) {
3462
+ const wantTokens = b.maxTokens;
3463
+ const wantUsd = b.maxUsd ?? 0;
3464
+ const wantIterations = b.maxIterations;
3465
+ if (wantTokens > freeTokens) return { ok: false, reason: "budget-exhausted" };
3466
+ if (wantIterations > freeIterations) return { ok: false, reason: "budget-exhausted" };
3467
+ if (wantUsd > 0 && (!usdCapped || wantUsd > freeUsd)) {
3468
+ return { ok: false, reason: "budget-exhausted" };
3469
+ }
3470
+ freeTokens -= wantTokens;
3471
+ reservedTokens += wantTokens;
3472
+ freeIterations -= wantIterations;
3473
+ reservedIterations += wantIterations;
3474
+ if (wantUsd > 0) {
3475
+ freeUsd -= wantUsd;
3476
+ reservedUsd += wantUsd;
3477
+ }
3478
+ const id = nextTicketId++;
3479
+ open.add(id);
3480
+ return {
3481
+ ok: true,
3482
+ ticket: { id, reserved: { tokens: wantTokens, usd: wantUsd, iterations: wantIterations } }
3483
+ };
3484
+ }
3485
+ function reconcile(ticket, spent) {
3486
+ if (!open.has(ticket.id)) {
3487
+ throw new Error(`budget pool: reconcile of unknown or already-settled ticket ${ticket.id}`);
3488
+ }
3489
+ open.delete(ticket.id);
3490
+ const { tokens: rTokens, usd: rUsd, iterations: rIterations } = ticket.reserved;
3491
+ const spentTokens = totalTokens(spent.tokens);
3492
+ if (spentTokens > rTokens) {
3493
+ throw new Error(
3494
+ `budget pool: ticket ${ticket.id} spent ${spentTokens} tokens > reserved ${rTokens}`
3495
+ );
3496
+ }
3497
+ if (spent.iterations > rIterations) {
3498
+ throw new Error(
3499
+ `budget pool: ticket ${ticket.id} spent ${spent.iterations} iterations > reserved ${rIterations}`
3500
+ );
3501
+ }
3502
+ if (usdCapped && spent.usd > rUsd) {
3503
+ throw new Error(`budget pool: ticket ${ticket.id} spent $${spent.usd} > reserved $${rUsd}`);
3504
+ }
3505
+ reservedTokens -= rTokens;
3506
+ committedTokens += spentTokens;
3507
+ freeTokens += rTokens - spentTokens;
3508
+ reservedIterations -= rIterations;
3509
+ committedIterations += spent.iterations;
3510
+ freeIterations += rIterations - spent.iterations;
3511
+ if (usdCapped && rUsd > 0) {
3512
+ reservedUsd -= rUsd;
3513
+ committedUsd += spent.usd;
3514
+ freeUsd += rUsd - spent.usd;
3515
+ } else {
3516
+ committedUsd += spent.usd;
3517
+ }
3518
+ }
3519
+ function observe(spend) {
3520
+ const tokens = totalTokens(spend.tokens);
3521
+ freeTokens -= tokens;
3522
+ committedTokens += tokens;
3523
+ freeIterations -= spend.iterations;
3524
+ committedIterations += spend.iterations;
3525
+ committedUsd += spend.usd;
3526
+ if (usdCapped) freeUsd -= spend.usd;
3527
+ }
3528
+ function readout() {
3529
+ return {
3530
+ tokensLeft: freeTokens,
3531
+ usdLeft: usdCapped ? freeUsd : 0,
3532
+ usdCapped,
3533
+ deadlineMs: absoluteDeadlineMs,
3534
+ reservedTokens
3535
+ };
3536
+ }
3537
+ function assertNoOpenTickets() {
3538
+ if (open.size > 0) {
3539
+ throw new Error(
3540
+ `budget pool: ${open.size} reservation(s) still open at join barrier (leaked ticket ids: ${[...open].join(", ")}) \u2014 conserved-pool invariant violated`
3541
+ );
3542
+ }
3543
+ }
3544
+ return {
3545
+ reserve,
3546
+ reconcile,
3547
+ spendFrom: foldUsage,
3548
+ readout,
3549
+ observe,
3550
+ assertNoOpenTickets
3551
+ };
3552
+ }
3553
+
3554
+ // src/runtime/supervise/supervisor.ts
3555
+ var defaultMaxDepth = 4;
3556
+ function createSupervisor() {
3557
+ let attached;
3558
+ async function run(root, task, opts) {
3559
+ const now = opts.now ?? Date.now;
3560
+ const pool = createBudgetPool(opts.budget, now);
3561
+ await opts.journal.beginTree(opts.runId, new Date(now()).toISOString());
3562
+ await opts.journal.appendEvent(opts.runId, {
3563
+ kind: "spawned",
3564
+ id: opts.runId,
3565
+ label: "root",
3566
+ budget: opts.budget,
3567
+ runtime: "inline",
3568
+ seq: 0,
3569
+ at: new Date(now()).toISOString()
3570
+ });
3571
+ const controller = new AbortController();
3572
+ const cascadeAbort = (reason) => {
3573
+ if (controller.signal.aborted) return;
3574
+ controller.abort(reason);
3575
+ };
3576
+ const onCallerAbort = () => cascadeAbort("caller signal aborted");
3577
+ if (opts.signal) {
3578
+ if (opts.signal.aborted) cascadeAbort("caller signal aborted");
3579
+ else opts.signal.addEventListener("abort", onCallerAbort, { once: true });
3580
+ }
3581
+ const breaker = createIntensityBreaker(opts, () => cascadeAbort("intensity breaker tripped"));
3582
+ const journal = wrapJournalForBreaker(opts.journal, breaker);
3583
+ const scope = createScope({
3584
+ parentId: opts.runId,
3585
+ root: opts.runId,
3586
+ pool,
3587
+ journal,
3588
+ blobs: opts.blobs,
3589
+ executors: opts.executors,
3590
+ seams: {},
3591
+ depth: 0,
3592
+ maxDepth: opts.maxDepth ?? defaultMaxDepth,
3593
+ signal: controller.signal,
3594
+ now,
3595
+ hooks: opts.hooks
3596
+ });
3597
+ const openScope = scope;
3598
+ if (attached) {
3599
+ attached.bind({ scope: openScope, cascadeAbort, signal: pushRootSignal(cascadeAbort) });
3600
+ }
3601
+ let actOutcome;
3602
+ try {
3603
+ const out = await root.act(task, scope);
3604
+ actOutcome = { ok: true, out };
3605
+ } catch (error) {
3606
+ actOutcome = { ok: false, error };
3607
+ } finally {
3608
+ await drainLiveChildren(openScope, controller);
3609
+ if (opts.signal) opts.signal.removeEventListener("abort", onCallerAbort);
3610
+ if (attached) attached.unbind();
3611
+ }
3612
+ const tree = scope.view;
3613
+ if (actOutcome.ok) {
3614
+ pool.assertNoOpenTickets();
3615
+ const out = actOutcome.out;
3616
+ if (out !== void 0) {
3617
+ const outRef = contentAddress(out);
3618
+ await opts.blobs.put(outRef, out);
3619
+ const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
3620
+ return {
3621
+ kind: "winner",
3622
+ out,
3623
+ outRef,
3624
+ tree,
3625
+ spentTotal: addSpend(childWork, driverInference),
3626
+ ...isNonEmptySpend(driverInference) ? { spentBreakdown: { driverInference, childWork } } : {}
3627
+ };
3628
+ }
3629
+ return noWinner();
3630
+ }
3631
+ return noWinner();
3632
+ async function noWinner() {
3633
+ const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
3634
+ return {
3635
+ kind: "no-winner",
3636
+ reason: classifyNoWinner(controller, pool, opts, breaker),
3637
+ tree,
3638
+ downCount: breaker.downCount(),
3639
+ spentTotal: addSpend(childWork, driverInference)
3640
+ };
3641
+ }
3642
+ }
3643
+ function attach(h) {
3644
+ const control = rootControls.get(h);
3645
+ if (!control) {
3646
+ throw new RuntimeRunStateError(
3647
+ "supervisor.attach: handle was not minted by createRootHandle (no control channel)"
3648
+ );
3649
+ }
3650
+ attached = control;
3651
+ }
3652
+ return { run, attach };
3653
+ }
3654
+ var rootControls = /* @__PURE__ */ new WeakMap();
3655
+ function pushRootSignal(cascadeAbort) {
3656
+ return (msg) => {
3657
+ if (msg.kind === "cancel") cascadeAbort(msg.reason ?? "root signal: cancel");
3658
+ };
3659
+ }
3660
+ function createIntensityBreaker(opts, trip) {
3661
+ const max = opts.maxRestarts;
3662
+ const within = opts.withinMs;
3663
+ const armed = max !== void 0 && within !== void 0;
3664
+ const recent = [];
3665
+ let total = 0;
3666
+ let isTripped = false;
3667
+ return {
3668
+ recordDown(at) {
3669
+ total += 1;
3670
+ if (!armed || isTripped) return;
3671
+ recent.push(at);
3672
+ const cutoff = at - within;
3673
+ while (recent.length > 0 && recent[0] < cutoff) recent.shift();
3674
+ if (recent.length > max) {
3675
+ isTripped = true;
3676
+ trip();
3677
+ }
3678
+ },
3679
+ tripped() {
3680
+ return isTripped;
3681
+ },
3682
+ downCount() {
3683
+ return total;
3684
+ }
3685
+ };
3686
+ }
3687
+ function wrapJournalForBreaker(journal, breaker) {
3688
+ return {
3689
+ loadTree: (root) => journal.loadTree(root),
3690
+ beginTree: (root, at) => journal.beginTree(root, at),
3691
+ appendEvent: (root, ev) => {
3692
+ if (ev.kind === "settled" && ev.status === "down") breaker.recordDown(Date.parse(ev.at));
3693
+ return journal.appendEvent(root, ev);
3694
+ }
3695
+ };
3696
+ }
3697
+ async function drainLiveChildren(scope, controller) {
3698
+ const hasLive = scope.view.inFlight > 0;
3699
+ if (!hasLive) return;
3700
+ if (!controller.signal.aborted) controller.abort();
3701
+ await Promise.allSettled([drainCursor(scope)]);
3702
+ }
3703
+ async function drainCursor(scope) {
3704
+ for (; ; ) {
3705
+ const settled = await scope.next();
3706
+ if (settled === null) return;
3707
+ }
3708
+ }
3709
+ function classifyNoWinner(controller, pool, opts, breaker) {
3710
+ if (breaker.tripped()) return "all-children-down";
3711
+ if (controller.signal.aborted) return "aborted";
3712
+ if (poolExhausted(pool, opts)) return "budget-exhausted";
3713
+ return "all-children-down";
3714
+ }
3715
+ function poolExhausted(pool, opts) {
3716
+ const r = pool.readout();
3717
+ if (r.tokensLeft <= 0) return true;
3718
+ if (opts.budget.maxUsd !== void 0 && r.usdLeft <= 0) return true;
3719
+ if (opts.budget.deadlineMs !== void 0 && r.deadlineMs > 0 && (opts.now ?? Date.now)() >= r.deadlineMs) {
3720
+ return true;
3721
+ }
3722
+ return false;
3723
+ }
3724
+ async function spentFromJournal(journal, root) {
3725
+ const events = await journal.loadTree(root);
3726
+ if (events === void 0) {
3727
+ throw new RuntimeRunStateError(
3728
+ `supervisor: spawn tree '${root}' is missing from the journal after run (corrupted log)`
3729
+ );
3730
+ }
3731
+ const childWork = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3732
+ const driverInference = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3733
+ for (const ev of events) {
3734
+ if (ev.kind === "settled") accumulate(childWork, ev.spent);
3735
+ else if (ev.kind === "metered") accumulate(driverInference, ev.spend);
3736
+ }
3737
+ return { childWork, driverInference };
3738
+ }
3739
+ function accumulate(a, b) {
3740
+ a.iterations += b.iterations;
3741
+ a.tokens.input += b.tokens.input;
3742
+ a.tokens.output += b.tokens.output;
3743
+ a.usd += b.usd;
3744
+ a.ms += b.ms;
3745
+ }
3746
+ function addSpend(a, b) {
3747
+ return {
3748
+ iterations: a.iterations + b.iterations,
3749
+ tokens: { input: a.tokens.input + b.tokens.input, output: a.tokens.output + b.tokens.output },
3750
+ usd: a.usd + b.usd,
3751
+ ms: a.ms + b.ms
3752
+ };
3753
+ }
3754
+ function isNonEmptySpend(s) {
3755
+ return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
3756
+ }
3757
+
2464
3758
  // src/runtime/supervise/event-bus.ts
2465
3759
  function createEventBus(now = Date.now) {
2466
3760
  const queue = [];
@@ -3117,7 +4411,7 @@ ${narrative}` : roster;
3117
4411
  return `${roster}
3118
4412
 
3119
4413
  ## Progress notes
3120
- Summary unavailable: ${errMessage(e)}`;
4414
+ Summary unavailable: ${errMessage2(e)}`;
3121
4415
  }
3122
4416
  }),
3123
4417
  ...opts.compaction.onCompact ? { onCompact: opts.compaction.onCompact } : {},
@@ -3188,7 +4482,7 @@ function safeJson(v) {
3188
4482
  return String(v);
3189
4483
  }
3190
4484
  }
3191
- function errMessage(e) {
4485
+ function errMessage2(e) {
3192
4486
  return e instanceof Error ? e.message : String(e);
3193
4487
  }
3194
4488
 
@@ -5189,10 +6483,21 @@ async function delegate(intent, opts = {}) {
5189
6483
 
5190
6484
  export {
5191
6485
  gateOnDeliverable,
6486
+ defineRuntimeHooks,
6487
+ composeRuntimeHooks,
6488
+ notifyRuntimeHookEvent,
6489
+ notifyRuntimeDecisionPoint,
6490
+ createScope,
6491
+ settledToIteration,
6492
+ withDriverExecutor,
5192
6493
  createWorktree,
5193
6494
  captureWorktreeDiff,
5194
6495
  removeWorktree,
5195
6496
  runWorktreeHarness,
6497
+ routerChatWithUsage,
6498
+ routerChatWithTools,
6499
+ routerToolLoop,
6500
+ routerBrain,
5196
6501
  acquireSandbox,
5197
6502
  probeSandboxCapabilities,
5198
6503
  createSandboxLineage,
@@ -5205,6 +6510,9 @@ export {
5205
6510
  createExecutor,
5206
6511
  createExecutorRegistry,
5207
6512
  createInMemoryRunContext,
6513
+ spendFromUsageEvents,
6514
+ createBudgetPool,
6515
+ createSupervisor,
5208
6516
  createEventBus,
5209
6517
  createCoordinationTools,
5210
6518
  driverAgent,
@@ -5263,4 +6571,4 @@ export {
5263
6571
  workerFromBackend,
5264
6572
  supervise
5265
6573
  };
5266
- //# sourceMappingURL=chunk-UO2L5VTP.js.map
6574
+ //# sourceMappingURL=chunk-FHZHADIM.js.map