@tangle-network/agent-runtime 0.93.2 → 0.94.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +4 -3
  2. package/dist/agent.d.ts +1 -1
  3. package/dist/agent.js +5 -6
  4. package/dist/agent.js.map +1 -1
  5. package/dist/{agentic-generator-B8oeE2Yv.d.ts → agentic-generator-D9ZmAuiL.d.ts} +1 -1
  6. package/dist/analyst-loop.d.ts +1 -1
  7. package/dist/candidate-execution/index.d.ts +10 -3
  8. package/dist/candidate-execution/index.js +9 -3
  9. package/dist/{chunk-6K5CI33W.js → chunk-D336OZHK.js} +1232 -19
  10. package/dist/chunk-D336OZHK.js.map +1 -0
  11. package/dist/{chunk-TU73X3I6.js → chunk-HPCJBBLS.js} +2 -2
  12. package/dist/chunk-ISTDY47H.js +849 -0
  13. package/dist/chunk-ISTDY47H.js.map +1 -0
  14. package/dist/{chunk-RTVVH2KL.js → chunk-JEP2WM24.js} +2 -2
  15. package/dist/{chunk-Z7K5BPKZ.js → chunk-K7OPNHU4.js} +2 -2
  16. package/dist/{chunk-OX3QRJOA.js → chunk-KHLE3C5G.js} +281 -251
  17. package/dist/chunk-KHLE3C5G.js.map +1 -0
  18. package/dist/{chunk-XDSWWUKE.js → chunk-MZH4HORF.js} +6 -29
  19. package/dist/chunk-MZH4HORF.js.map +1 -0
  20. package/dist/chunk-PJNGPWTA.js +634 -0
  21. package/dist/chunk-PJNGPWTA.js.map +1 -0
  22. package/dist/{chunk-X5KJKS33.js → chunk-RTSZNYXM.js} +3 -3
  23. package/dist/{chunk-WGR6QJ5H.js → chunk-SJHFJOCY.js} +8 -161
  24. package/dist/chunk-SJHFJOCY.js.map +1 -0
  25. package/dist/{chunk-2BQMAZAB.js → chunk-URMJTSXB.js} +1378 -28
  26. package/dist/chunk-URMJTSXB.js.map +1 -0
  27. package/dist/{chunk-3D2RHC4K.js → chunk-WIPGQ4GT.js} +1 -1
  28. package/dist/{chunk-3D2RHC4K.js.map → chunk-WIPGQ4GT.js.map} +1 -1
  29. package/dist/{chunk-R5GWDTM3.js → chunk-ZEYAT33L.js} +2 -2
  30. package/dist/{completion-gate-BLaiN0-X.d.ts → completion-gate-BAj3dXeW.d.ts} +14 -2
  31. package/dist/{coordination-DxJ83oZA.d.ts → coordination-B3ZuApR_.d.ts} +7 -5
  32. package/dist/environment-provider.d.ts +2 -2
  33. package/dist/{improve-DDhQaaJT.d.ts → improve-r8SvIxYv.d.ts} +13 -16
  34. package/dist/index.d.ts +39 -110
  35. package/dist/index.js +92 -36
  36. package/dist/index.js.map +1 -1
  37. package/dist/intelligence.d.ts +24 -7
  38. package/dist/intelligence.js +114 -20
  39. package/dist/intelligence.js.map +1 -1
  40. package/dist/knowledge.d.ts +7 -7
  41. package/dist/knowledge.js +5 -5
  42. package/dist/lifecycle.d.ts +3 -3
  43. package/dist/lifecycle.js +2 -2
  44. package/dist/{local-harness-dcD5WTTr.d.ts → local-harness-B7bNuNJw.d.ts} +49 -1
  45. package/dist/{loop-runner-bin-kKUNGLyV.d.ts → loop-runner-bin-D4bMIxmB.d.ts} +2 -2
  46. package/dist/loop-runner-bin.d.ts +6 -6
  47. package/dist/loop-runner-bin.js +6 -7
  48. package/dist/loops.d.ts +457 -18
  49. package/dist/loops.js +39 -41
  50. package/dist/mcp/bin.js +5 -5
  51. package/dist/mcp/index.d.ts +10 -10
  52. package/dist/mcp/index.js +9 -8
  53. package/dist/{mcp-serve-verifier-Bg4C3p5S.d.ts → mcp-serve-verifier-BuXjaZDE.d.ts} +1 -1
  54. package/dist/{openai-tools-E3woykz9.d.ts → openai-tools-D0ZSRCC6.d.ts} +1 -1
  55. package/dist/{prepare-DiVGKcwS.d.ts → prepare-BKxAiUcH.d.ts} +1 -1
  56. package/dist/profiles.d.ts +1 -1
  57. package/dist/{sanitize-C9go6tXj.d.ts → sanitize-C2jicjNf.d.ts} +1 -1
  58. package/dist/{supervise-T2pazU3G.d.ts → supervise-DTNcLDl2.d.ts} +4 -4
  59. package/dist/{types-DAdIm4AC.d.ts → types-Bb-CqxJH.d.ts} +5 -2
  60. package/dist/{types-B00NtbCs.d.ts → types-DWA64rbJ.d.ts} +1 -1
  61. package/dist/{worktree-fanout-BUb2Ag02.d.ts → worktree-fanout-IMKqdV_E.d.ts} +244 -244
  62. package/package.json +4 -4
  63. package/skills/build-with-agent-runtime/SKILL.md +1 -1
  64. package/dist/chunk-2BQMAZAB.js.map +0 -1
  65. package/dist/chunk-6K5CI33W.js.map +0 -1
  66. package/dist/chunk-FDJ7AHXG.js +0 -1229
  67. package/dist/chunk-FDJ7AHXG.js.map +0 -1
  68. package/dist/chunk-J2K6WIG6.js +0 -2172
  69. package/dist/chunk-J2K6WIG6.js.map +0 -1
  70. package/dist/chunk-OX3QRJOA.js.map +0 -1
  71. package/dist/chunk-PX6SXX3M.js +0 -146
  72. package/dist/chunk-PX6SXX3M.js.map +0 -1
  73. package/dist/chunk-WGR6QJ5H.js.map +0 -1
  74. package/dist/chunk-XDSWWUKE.js.map +0 -1
  75. package/dist/structural-rollout-DHGDbhvR.d.ts +0 -446
  76. /package/dist/{chunk-TU73X3I6.js.map → chunk-HPCJBBLS.js.map} +0 -0
  77. /package/dist/{chunk-RTVVH2KL.js.map → chunk-JEP2WM24.js.map} +0 -0
  78. /package/dist/{chunk-Z7K5BPKZ.js.map → chunk-K7OPNHU4.js.map} +0 -0
  79. /package/dist/{chunk-X5KJKS33.js.map → chunk-RTSZNYXM.js.map} +0 -0
  80. /package/dist/{chunk-R5GWDTM3.js.map → chunk-ZEYAT33L.js.map} +0 -0
@@ -3,19 +3,13 @@ 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,
17
11
  contentAddress
18
- } from "./chunk-3D2RHC4K.js";
12
+ } from "./chunk-WIPGQ4GT.js";
19
13
  import {
20
14
  providerAsExecutor,
21
15
  resolveAgentEnvironmentProvider
@@ -39,11 +33,12 @@ import {
39
33
  import {
40
34
  harnessInvocation,
41
35
  runLocalHarness
42
- } from "./chunk-PX6SXX3M.js";
36
+ } from "./chunk-PJNGPWTA.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,577 @@ 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
+ ...spend.usdKnown === false ? { usdKnown: false } : {},
624
+ ms: spend.ms
625
+ };
626
+ }
627
+ async function teardownSafe(executor, grace) {
628
+ try {
629
+ await executor.teardown(grace);
630
+ } catch {
631
+ }
632
+ }
633
+ function downRecord(reason, infra, metered) {
634
+ return { kind: "down", reason, infra, restartCount: 0, ...metered ? { metered } : {} };
635
+ }
636
+ function zeroSpend() {
637
+ return { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
638
+ }
639
+ function isAsyncIterable2(value) {
640
+ return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
641
+ }
642
+ function isAgentSpec(value) {
643
+ if (typeof value !== "object" || value === null) return false;
644
+ const v = value;
645
+ return "profile" in v && "harness" in v;
646
+ }
647
+ function isAbortError(err) {
648
+ return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
649
+ }
650
+ function isInfraError(err) {
651
+ return err instanceof ValidationError;
652
+ }
653
+ function errMessage(err) {
654
+ if (err instanceof Error) return err.message;
655
+ return String(err);
656
+ }
657
+
92
658
  // src/mcp/worktree.ts
93
659
  import { spawn } from "child_process";
94
660
  async function runGitAsync(args, cwd, runner) {
@@ -175,6 +741,216 @@ async function removeWorktree(options) {
175
741
  ).catch(() => void 0);
176
742
  }
177
743
 
744
+ // src/runtime/router-client.ts
745
+ import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
746
+
747
+ // src/runtime/tool-loop.ts
748
+ function estimateConversationTokens(messages) {
749
+ let chars = 0;
750
+ for (const m of messages) {
751
+ const content = m.content;
752
+ chars += typeof content === "string" ? content.length : safeJsonLength(content);
753
+ const calls = m.tool_calls;
754
+ if (calls) for (const c of calls) chars += c.function?.arguments?.length ?? 0;
755
+ }
756
+ return Math.ceil(chars / 4);
757
+ }
758
+ function safeJsonLength(value) {
759
+ if (value === void 0 || value === null) return 0;
760
+ try {
761
+ return JSON.stringify(value)?.length ?? 0;
762
+ } catch {
763
+ return String(value).length;
764
+ }
765
+ }
766
+ async function maybeCompact(messages, c, turn) {
767
+ const head = c.preserveHead !== void 0 && c.preserveHead > 0 ? c.preserveHead : 2;
768
+ if (messages.length <= head + 1) return false;
769
+ const estimate = c.estimateTokens ?? estimateConversationTokens;
770
+ const before = estimate(messages);
771
+ if (before <= c.thresholdTokens) return false;
772
+ const digest = await c.distill([...messages]);
773
+ messages.splice(head, messages.length - head, {
774
+ role: "user",
775
+ content: `[earlier work compacted to save context \u2014 progress so far]
776
+ ${digest}`
777
+ });
778
+ c.onCompact?.({ turn, beforeTokens: before, afterTokens: estimate(messages) });
779
+ return true;
780
+ }
781
+ async function runBrainLoop(opts) {
782
+ const maxTurns = opts.maxTurns ?? 4;
783
+ const messages = [...opts.initialMessages];
784
+ let toolCalls = 0;
785
+ let lastText = "";
786
+ const usage = { input: 0, output: 0 };
787
+ const toolTrace = [];
788
+ for (let turn = 1; turn <= maxTurns; turn += 1) {
789
+ if (opts.hooks?.stopBefore?.(turn)) break;
790
+ await opts.hooks?.beforeTurn?.(turn, messages);
791
+ if (opts.compaction) await maybeCompact(messages, opts.compaction, turn);
792
+ const r = await opts.chat(messages, opts.tools);
793
+ if (r.usage) {
794
+ usage.input += r.usage.input;
795
+ usage.output += r.usage.output;
796
+ opts.hooks?.onUsage?.(r.usage);
797
+ }
798
+ if (r.content) lastText = r.content;
799
+ if (r.toolCalls.length === 0) {
800
+ if (r.content) messages.push({ role: "assistant", content: r.content });
801
+ return { final: lastText, turns: turn, toolCalls, toolTrace, usage, messages };
802
+ }
803
+ messages.push({
804
+ role: "assistant",
805
+ content: r.content ?? "",
806
+ tool_calls: r.toolCalls.map((tc) => ({
807
+ id: tc.id,
808
+ type: "function",
809
+ function: { name: tc.name, arguments: tc.arguments }
810
+ }))
811
+ });
812
+ for (const tc of r.toolCalls) {
813
+ toolCalls += 1;
814
+ let args = {};
815
+ try {
816
+ args = JSON.parse(tc.arguments);
817
+ } catch {
818
+ messages.push({
819
+ role: "tool",
820
+ tool_call_id: tc.id,
821
+ content: `error: arguments were not valid JSON: ${tc.arguments.slice(0, 200)}`
822
+ });
823
+ continue;
824
+ }
825
+ const out = await opts.execute(tc.name, args);
826
+ messages.push({ role: "tool", tool_call_id: tc.id, content: out });
827
+ toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
828
+ }
829
+ }
830
+ return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
831
+ }
832
+
833
+ // src/runtime/router-client.ts
834
+ async function routerChatWithUsage(cfg, messages, opts) {
835
+ const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
836
+ const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
837
+ let temperature = opts?.temperature ?? 0.2;
838
+ const body = () => ({
839
+ model: cfg.model,
840
+ messages,
841
+ temperature,
842
+ max_tokens: opts?.maxTokens ?? 8192,
843
+ ...opts?.reasoningEffort ? { reasoning_effort: opts.reasoningEffort } : {}
844
+ });
845
+ if (cfg.complete) return parseChatResult(await cfg.complete(body()), cfg.model);
846
+ let lastErr = "";
847
+ for (let attempt = 0; attempt < 5; attempt += 1) {
848
+ const res = await fetch(url, {
849
+ method: "POST",
850
+ headers,
851
+ body: JSON.stringify(body()),
852
+ ...opts?.signal ? { signal: opts.signal } : {}
853
+ });
854
+ if (res.ok) return parseChatResult(await res.json(), cfg.model);
855
+ const status = res.status;
856
+ const text = (await res.text()).slice(0, 200);
857
+ lastErr = `router ${status}: ${text}`;
858
+ if (status === 400 && /temperature/i.test(text) && temperature !== 1) {
859
+ temperature = 1;
860
+ continue;
861
+ }
862
+ if (![408, 425, 429, 500, 502, 503, 504, 520, 522, 524].includes(status))
863
+ throw new Error(lastErr);
864
+ if (attempt < 4) await new Promise((r) => setTimeout(r, 800 * 2 ** attempt));
865
+ }
866
+ throw new Error(`${lastErr} (exhausted retries)`);
867
+ }
868
+ function parseChatResult(json, model) {
869
+ const data = json;
870
+ const u = data.usage;
871
+ const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
872
+ const costUsd = usage && isModelPriced(model) ? estimateCost(usage.input, usage.output, model) : void 0;
873
+ const msg = data.choices?.[0]?.message;
874
+ const { content, reasoning } = splitReasoning(
875
+ msg?.content ?? "",
876
+ msg?.reasoning ?? msg?.reasoning_content
877
+ );
878
+ return {
879
+ content,
880
+ ...reasoning ? { reasoning } : {},
881
+ ...usage ? { usage } : {},
882
+ ...costUsd !== void 0 ? { costUsd } : {}
883
+ };
884
+ }
885
+ function splitReasoning(rawContent, fieldReasoning) {
886
+ const open = rawContent.indexOf("<think>");
887
+ if (open !== -1) {
888
+ const close = rawContent.indexOf("</think>", open);
889
+ const inline = close !== -1 ? rawContent.slice(open + 7, close) : rawContent.slice(open + 7);
890
+ const rest = close !== -1 ? rawContent.slice(0, open) + rawContent.slice(close + 8) : rawContent.slice(0, open);
891
+ const reasoning = [fieldReasoning, inline.trim()].filter(Boolean).join("\n");
892
+ return { content: rest.trim(), ...reasoning ? { reasoning } : {} };
893
+ }
894
+ return { content: rawContent, ...fieldReasoning ? { reasoning: fieldReasoning } : {} };
895
+ }
896
+ async function routerChatWithTools(cfg, messages, tools, opts) {
897
+ const body = {
898
+ model: cfg.model,
899
+ messages,
900
+ tools,
901
+ tool_choice: opts?.toolChoice ?? "auto",
902
+ temperature: opts?.temperature ?? 0.3,
903
+ ...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
904
+ };
905
+ const raw = cfg.complete ? await cfg.complete(body) : await (async () => {
906
+ const res = await fetch(`${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
907
+ method: "POST",
908
+ headers: { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` },
909
+ body: JSON.stringify(body),
910
+ ...opts?.signal ? { signal: opts.signal } : {}
911
+ });
912
+ if (!res.ok) throw new Error(`router ${res.status}: ${(await res.text()).slice(0, 200)}`);
913
+ return res.json();
914
+ })();
915
+ const data = raw;
916
+ const msg = data.choices?.[0]?.message;
917
+ const toolCalls = (msg?.tool_calls ?? []).map((tc, i) => ({
918
+ id: tc.id ?? `call_${i}`,
919
+ name: tc.function?.name ?? "",
920
+ arguments: tc.function?.arguments ?? "{}"
921
+ }));
922
+ const u = data.usage;
923
+ const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
924
+ const costUsd = usage && isModelPriced(cfg.model) ? estimateCost(usage.input, usage.output, cfg.model) : void 0;
925
+ return {
926
+ content: msg?.content ?? null,
927
+ toolCalls,
928
+ ...usage ? { usage } : {},
929
+ ...costUsd !== void 0 ? { costUsd } : {}
930
+ };
931
+ }
932
+ async function routerToolLoop(cfg, system, user, tools, execute, opts) {
933
+ const initialMessages = opts?.initialMessages ?? [
934
+ { role: "system", content: system },
935
+ { role: "user", content: user }
936
+ ];
937
+ return runBrainLoop({
938
+ chat: (messages, toolSpecs) => routerChatWithTools(cfg, messages, toolSpecs, {
939
+ ...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
940
+ ...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
941
+ ...opts?.signal ? { signal: opts.signal } : {}
942
+ }),
943
+ tools,
944
+ execute,
945
+ initialMessages,
946
+ maxTurns: opts?.maxTurns ?? 4
947
+ });
948
+ }
949
+ function routerBrain(cfg, opts = {}) {
950
+ const temperature = opts.temperature ?? 0.4;
951
+ return (messages, tools) => routerChatWithTools(cfg, messages, tools, { temperature, toolChoice: "auto" });
952
+ }
953
+
178
954
  // src/runtime/sandbox-acquire.ts
179
955
  var RETRYABLE_HTTP = /* @__PURE__ */ new Set([502, 503, 504, 522, 524, 408, 425, 429]);
180
956
  var TERMINAL_STATUS = /* @__PURE__ */ new Set(["failed", "expired", "stopped"]);
@@ -865,13 +1641,13 @@ async function executeIteration(args) {
865
1641
  await destroySandboxSafe(box, args.ctx.traceEmitter, args.runId, args.now);
866
1642
  }
867
1643
  }
868
- if (isAbortError(slot.error) || args.signal.aborted) {
1644
+ if (isAbortError2(slot.error) || args.signal.aborted) {
869
1645
  if (slot.error) throw slot.error;
870
1646
  throwAbort();
871
1647
  }
872
1648
  if (slot.error instanceof ValidationError) throw slot.error;
873
1649
  }
874
- function isAbortError(err) {
1650
+ function isAbortError2(err) {
875
1651
  return err instanceof Error && err.name === "AbortError";
876
1652
  }
877
1653
  var TEARDOWN_TIMEOUT_MS2 = 15e3;
@@ -1205,13 +1981,15 @@ async function runWorktreeHarness(opts) {
1205
1981
  const { command, args } = harnessInvocation(opts.harness, opts.profile, opts.taskPrompt, {
1206
1982
  // This helper created the candidate worktree above; autonomous Claude
1207
1983
  // edits are permitted only inside that isolated checkout.
1208
- dangerouslySkipPermissions: opts.harness === "claude"
1984
+ dangerouslySkipPermissions: opts.harness === "claude",
1985
+ ...opts.codexReproducible ? { codexReproducible: true } : {}
1209
1986
  });
1210
1987
  const harnessResult = await runHarness({
1211
1988
  harness: opts.harness,
1212
1989
  cwd: worktree.path,
1213
1990
  taskPrompt: opts.taskPrompt,
1214
1991
  invocation: { command, args },
1992
+ ...opts.codexReproducible ? { codexReproducible: true } : {},
1215
1993
  ...opts.harnessTimeoutMs !== void 0 ? { timeoutMs: opts.harnessTimeoutMs } : {},
1216
1994
  ...opts.signal ? { signal: opts.signal } : {}
1217
1995
  });
@@ -1239,7 +2017,15 @@ async function runWorktreeHarness(opts) {
1239
2017
  killedBySignal: harnessResult.killedBySignal,
1240
2018
  durationMs: harnessResult.durationMs,
1241
2019
  stdout: harnessResult.stdout,
1242
- stderr: harnessResult.stderr
2020
+ stderr: harnessResult.stderr,
2021
+ ...harnessResult.usage ? { usage: harnessResult.usage } : {},
2022
+ ...harnessResult.evidence ? {
2023
+ cliVersion: harnessResult.evidence.cliVersion,
2024
+ effectivePromptSha256: harnessResult.evidence.effectivePromptSha256,
2025
+ nonPromptArgsSha256: harnessResult.evidence.nonPromptArgsSha256,
2026
+ controlledConfigSha256: harnessResult.evidence.controlledConfigSha256,
2027
+ executionPolicy: harnessResult.evidence.policy
2028
+ } : {}
1243
2029
  },
1244
2030
  ...checks ? { checks } : {}
1245
2031
  };
@@ -1307,9 +2093,17 @@ function createWorktreeCliExecutor(options) {
1307
2093
  if (typeof options.taskPrompt !== "string" || options.taskPrompt.length === 0) {
1308
2094
  throw new ValidationError("createWorktreeCliExecutor: taskPrompt required");
1309
2095
  }
2096
+ if (options.codexReproducible && options.harness !== "codex") {
2097
+ throw new ValidationError(
2098
+ 'createWorktreeCliExecutor: codexReproducible requires harness "codex"'
2099
+ );
2100
+ }
2101
+ if (options.codexReproducible && options.budgetExempt === true) {
2102
+ throw new ValidationError("createWorktreeCliExecutor: codexReproducible cannot be budgetExempt");
2103
+ }
1310
2104
  const runId = options.runId ?? randomUUID();
1311
2105
  const controller = new AbortController();
1312
- const budgetExempt = options.budgetExempt ?? true;
2106
+ const budgetExempt = options.budgetExempt ?? !options.codexReproducible;
1313
2107
  let run;
1314
2108
  let artifact;
1315
2109
  return {
@@ -1328,6 +2122,7 @@ function createWorktreeCliExecutor(options) {
1328
2122
  ...options.testCmd !== void 0 ? { testCmd: options.testCmd } : {},
1329
2123
  ...options.typecheckCmd !== void 0 ? { typecheckCmd: options.typecheckCmd } : {},
1330
2124
  ...options.harnessTimeoutMs !== void 0 ? { harnessTimeoutMs: options.harnessTimeoutMs } : {},
2125
+ ...options.codexReproducible ? { codexReproducible: true } : {},
1331
2126
  ...options.checkTimeoutMs !== void 0 ? { checkTimeoutMs: options.checkTimeoutMs } : {},
1332
2127
  ...options.checkOutputCap !== void 0 ? { checkOutputCap: options.checkOutputCap } : {},
1333
2128
  ...linked ? { signal: linked } : {},
@@ -1335,13 +2130,20 @@ function createWorktreeCliExecutor(options) {
1335
2130
  ...options.runHarness ? { runHarness: options.runHarness } : {},
1336
2131
  ...options.runCommand ? { runCommand: options.runCommand } : {}
1337
2132
  });
2133
+ const usage = run.result.harness.usage;
2134
+ if (!budgetExempt && !usage) {
2135
+ const completed = run;
2136
+ run = void 0;
2137
+ await completed.cleanup();
2138
+ throw new ValidationError(
2139
+ "createWorktreeCliExecutor: metered harness run returned no token usage"
2140
+ );
2141
+ }
1338
2142
  const spent = {
1339
2143
  iterations: 1,
1340
- // The worktree-harness core surfaces no token/usd usage, so tokens/usd are a genuine zero
1341
- // (NOT a fabricated cost). When budgetExempt is true the pool ignores this spend entirely;
1342
- // when explicitly false the scope debits exactly this captured spend — the real iteration.
1343
- tokens: zeroTokenUsage(),
2144
+ tokens: usage ? { input: usage.inputTokens, output: usage.outputTokens } : { input: 0, output: 0 },
1344
2145
  usd: 0,
2146
+ ...usage ? { usdKnown: false } : {},
1345
2147
  ms: Date.now() - started
1346
2148
  };
1347
2149
  artifact = { outRef: contentAddress(run.result), out: run.result, spent };
@@ -1385,7 +2187,7 @@ import { randomUUID as randomUUID2 } from "crypto";
1385
2187
  import { request as httpRequest } from "http";
1386
2188
  import { request as httpsRequest } from "https";
1387
2189
  import { Readable } from "stream";
1388
- import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
2190
+ import { estimateCost as estimateCost2, isModelPriced as isModelPriced2 } from "@tangle-network/agent-eval";
1389
2191
  var routerSeamKey = "router";
1390
2192
  var sandboxSeamKey = "sandbox";
1391
2193
  var cliSeamKey = "cli";
@@ -1406,7 +2208,7 @@ function contentRef(prefix, value) {
1406
2208
  }
1407
2209
  return `${prefix}:${(h >>> 0).toString(16).padStart(8, "0")}`;
1408
2210
  }
1409
- function zeroSpend() {
2211
+ function zeroSpend2() {
1410
2212
  return { iterations: 0, tokens: zeroTokenUsage(), usd: 0, ms: 0 };
1411
2213
  }
1412
2214
  var routerInlineExecutor = (spec, ctx) => {
@@ -1600,7 +2402,7 @@ var routerToolsInlineExecutor = (spec, ctx) => {
1600
2402
  }
1601
2403
  }
1602
2404
  }
1603
- const usd = isModelPriced(model) ? estimateCost(tokens.input, tokens.output, model) : 0;
2405
+ const usd = isModelPriced2(model) ? estimateCost2(tokens.input, tokens.output, model) : 0;
1604
2406
  const spent = { iterations: turns, tokens, usd, ms: Date.now() - started };
1605
2407
  const out = { content: lastText };
1606
2408
  artifact = { outRef: contentRef("router-tools", { model, content: lastText }), out, spent };
@@ -1812,7 +2614,7 @@ async function* streamCliLeaf(args) {
1812
2614
  );
1813
2615
  }
1814
2616
  const out = { content: chunks.join("") };
1815
- args.onArtifact({ outRef: contentRef("cli", out), out, spent: zeroSpend() });
2617
+ args.onArtifact({ outRef: contentRef("cli", out), out, spent: zeroSpend2() });
1816
2618
  yield { kind: "iteration" };
1817
2619
  }
1818
2620
  function killWithGrace(proc, grace) {
@@ -2200,7 +3002,7 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
2200
3002
  inner = bridgeExecutor(spec, bridgeCtx);
2201
3003
  for (const msg of pending.splice(0)) inner.deliver?.(msg);
2202
3004
  const run = inner.execute(seam.taskPrompt, linked);
2203
- if (isAsyncIterable2(run)) {
3005
+ if (isAsyncIterable3(run)) {
2204
3006
  for await (const event of run) yield event;
2205
3007
  bridgeArtifact = inner.resultArtifact();
2206
3008
  } else {
@@ -2293,7 +3095,7 @@ function bridgeOutputText(out) {
2293
3095
  return String(out);
2294
3096
  }
2295
3097
  }
2296
- function isAsyncIterable2(value) {
3098
+ function isAsyncIterable3(value) {
2297
3099
  return value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
2298
3100
  }
2299
3101
  var cliWorktreeExecutor = (spec, ctx) => {
@@ -2315,6 +3117,7 @@ var cliWorktreeExecutor = (spec, ctx) => {
2315
3117
  ...seam.runId ? { runId: seam.runId } : {},
2316
3118
  ...seam.baseRef ? { baseRef: seam.baseRef } : {},
2317
3119
  ...seam.harnessTimeoutMs !== void 0 ? { harnessTimeoutMs: seam.harnessTimeoutMs } : {},
3120
+ ...seam.codexReproducible ? { codexReproducible: true } : {},
2318
3121
  ...seam.testCmd !== void 0 ? { testCmd: seam.testCmd } : {},
2319
3122
  ...seam.typecheckCmd !== void 0 ? { typecheckCmd: seam.typecheckCmd } : {},
2320
3123
  ...seam.checkTimeoutMs !== void 0 ? { checkTimeoutMs: seam.checkTimeoutMs } : {},
@@ -2451,6 +3254,181 @@ function mergeAbortSignals(...signals) {
2451
3254
  return c.signal;
2452
3255
  }
2453
3256
 
3257
+ // src/runtime/supervise/driver-executor.ts
3258
+ var driverRuntime = "driver";
3259
+ var driverRole = "driver";
3260
+ function isDriverSpec(spec) {
3261
+ const role = spec.profile.metadata?.role;
3262
+ if (role !== driverRole) return false;
3263
+ const driver = spec.driver;
3264
+ if (!isAgent(driver)) {
3265
+ throw new ValidationError(
3266
+ "driverExecutor: a driver-role spec must carry a `driver` Agent to run inside its nested scope"
3267
+ );
3268
+ }
3269
+ return true;
3270
+ }
3271
+ var driverExecutorFactory = (spec, ctx) => {
3272
+ if (!isDriverSpec(spec)) {
3273
+ throw new ValidationError(
3274
+ 'driverExecutorFactory: spec is not a driver child (no role:"driver" marker)'
3275
+ );
3276
+ }
3277
+ const driver = spec.driver;
3278
+ const journal = spec.journal;
3279
+ const seam = readNestedScopeSeam(ctx);
3280
+ let artifact;
3281
+ let meteredSpend;
3282
+ return {
3283
+ runtime: driverRuntime,
3284
+ async execute(task, signal) {
3285
+ const nestedRoot = nestedTreeKey(seam, journal);
3286
+ await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
3287
+ const nestedScope = seam.mount(nestedRoot, signal);
3288
+ try {
3289
+ const out = await driver.act(task, nestedScope);
3290
+ const events = await loadTreeEvents(journal, nestedRoot);
3291
+ const settled = events.filter(isSettled);
3292
+ meteredSpend = nonZeroOrUndef(sumMetered(events));
3293
+ const verdict = deriveDeliveryVerdict(settled);
3294
+ artifact = {
3295
+ outRef: `${driverRuntime}:${nestedRoot}`,
3296
+ out,
3297
+ spent: sumSpend(settled),
3298
+ ...verdict ? { verdict } : {}
3299
+ };
3300
+ return artifact;
3301
+ } catch (err) {
3302
+ meteredSpend = await safeSumMetered(journal, nestedRoot);
3303
+ throw err;
3304
+ }
3305
+ },
3306
+ metered() {
3307
+ return meteredSpend;
3308
+ },
3309
+ teardown() {
3310
+ return Promise.resolve({ destroyed: true });
3311
+ },
3312
+ resultArtifact() {
3313
+ if (!artifact) {
3314
+ throw new ValidationError("driverExecutor: resultArtifact() read before execute()");
3315
+ }
3316
+ return artifact;
3317
+ }
3318
+ };
3319
+ };
3320
+ function withDriverExecutor(base) {
3321
+ return {
3322
+ register: base.register.bind(base),
3323
+ resolve(spec) {
3324
+ const role = spec.profile.metadata?.role;
3325
+ if (role === driverRole && !spec.executor) {
3326
+ return { succeeded: true, value: driverExecutorFactory };
3327
+ }
3328
+ return base.resolve(spec);
3329
+ }
3330
+ };
3331
+ }
3332
+ function nestedTreeKey(seam, journal) {
3333
+ return `${seam.journalRoot}/d${nextNestOrdinal(journal)}`;
3334
+ }
3335
+ var nestCounters = /* @__PURE__ */ new WeakMap();
3336
+ function nextNestOrdinal(journal) {
3337
+ let c = nestCounters.get(journal);
3338
+ if (!c) {
3339
+ c = { n: 0 };
3340
+ nestCounters.set(journal, c);
3341
+ }
3342
+ return c.n++;
3343
+ }
3344
+ async function loadTreeEvents(journal, nestedRoot) {
3345
+ const events = await journal.loadTree(nestedRoot);
3346
+ if (events === void 0) {
3347
+ throw new ValidationError(
3348
+ `driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`
3349
+ );
3350
+ }
3351
+ return events;
3352
+ }
3353
+ function isSettled(ev) {
3354
+ return ev.kind === "settled";
3355
+ }
3356
+ function sumSpend(settled) {
3357
+ const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3358
+ for (const ev of settled) {
3359
+ total.iterations += ev.spent.iterations;
3360
+ total.tokens.input += ev.spent.tokens.input;
3361
+ total.tokens.output += ev.spent.tokens.output;
3362
+ total.usd += ev.spent.usd;
3363
+ if (ev.spent.usdKnown === false) total.usdKnown = false;
3364
+ total.ms += ev.spent.ms;
3365
+ }
3366
+ return total;
3367
+ }
3368
+ function sumMetered(events) {
3369
+ const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3370
+ for (const ev of events) {
3371
+ if (ev.kind !== "metered") continue;
3372
+ total.iterations += ev.spend.iterations;
3373
+ total.tokens.input += ev.spend.tokens.input;
3374
+ total.tokens.output += ev.spend.tokens.output;
3375
+ total.usd += ev.spend.usd;
3376
+ if (ev.spend.usdKnown === false) total.usdKnown = false;
3377
+ total.ms += ev.spend.ms;
3378
+ }
3379
+ return total;
3380
+ }
3381
+ function isNonZeroSpend(s) {
3382
+ return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
3383
+ }
3384
+ function nonZeroOrUndef(s) {
3385
+ return isNonZeroSpend(s) ? s : void 0;
3386
+ }
3387
+ async function safeSumMetered(journal, nestedRoot) {
3388
+ try {
3389
+ return nonZeroOrUndef(sumMetered(await loadTreeEvents(journal, nestedRoot)));
3390
+ } catch {
3391
+ return void 0;
3392
+ }
3393
+ }
3394
+ function deriveDeliveryVerdict(settled) {
3395
+ let sawChild = false;
3396
+ let anyValid = false;
3397
+ let bestValidScore;
3398
+ let bestDoneScore;
3399
+ for (const ev of settled) {
3400
+ sawChild = true;
3401
+ if (ev.status !== "done") continue;
3402
+ const score = ev.verdict?.score;
3403
+ if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) {
3404
+ bestDoneScore = score;
3405
+ }
3406
+ if (ev.verdict?.valid === true) {
3407
+ anyValid = true;
3408
+ if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) {
3409
+ bestValidScore = score;
3410
+ }
3411
+ }
3412
+ }
3413
+ if (!sawChild) return void 0;
3414
+ return {
3415
+ valid: anyValid,
3416
+ score: anyValid ? bestValidScore ?? 1 : bestDoneScore ?? 0
3417
+ };
3418
+ }
3419
+ function readNestedScopeSeam(ctx) {
3420
+ const seam = ctx.seams[nestedScopeSeamKey];
3421
+ if (!seam || typeof seam.mount !== "function") {
3422
+ throw new ValidationError(
3423
+ `driverExecutor: missing required seam "${nestedScopeSeamKey}" \u2014 a driver child must be spawned through a Scope that seeds it (the keystone scope does)`
3424
+ );
3425
+ }
3426
+ return seam;
3427
+ }
3428
+ function isAgent(value) {
3429
+ return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
3430
+ }
3431
+
2454
3432
  // src/runtime/supervise/run-context.ts
2455
3433
  function createInMemoryRunContext(opts = {}) {
2456
3434
  const base = createExecutorRegistry();
@@ -2461,6 +3439,364 @@ function createInMemoryRunContext(opts = {}) {
2461
3439
  };
2462
3440
  }
2463
3441
 
3442
+ // src/runtime/supervise/budget.ts
3443
+ function spendFromUsageEvents(events) {
3444
+ const tokens = zeroTokenUsage();
3445
+ let usd = 0;
3446
+ let iterations = 0;
3447
+ for (const ev of events) {
3448
+ if (ev.kind === "tokens") {
3449
+ addTokenUsage(tokens, { input: ev.input, output: ev.output });
3450
+ } else if (ev.kind === "cost") {
3451
+ usd += ev.usd;
3452
+ } else {
3453
+ iterations += 1;
3454
+ }
3455
+ }
3456
+ return { iterations, tokens, usd, ms: 0 };
3457
+ }
3458
+ async function foldUsage(events) {
3459
+ if (Array.isArray(events)) return spendFromUsageEvents(events);
3460
+ const tokens = zeroTokenUsage();
3461
+ let usd = 0;
3462
+ let iterations = 0;
3463
+ for await (const ev of events) {
3464
+ if (ev.kind === "tokens") {
3465
+ addTokenUsage(tokens, { input: ev.input, output: ev.output });
3466
+ } else if (ev.kind === "cost") {
3467
+ usd += ev.usd;
3468
+ } else {
3469
+ iterations += 1;
3470
+ }
3471
+ }
3472
+ return { iterations, tokens, usd, ms: 0 };
3473
+ }
3474
+ function totalTokens(usage) {
3475
+ return usage.input + usage.output;
3476
+ }
3477
+ function createBudgetPool(root, now = Date.now) {
3478
+ let freeTokens = root.maxTokens;
3479
+ let reservedTokens = 0;
3480
+ let committedTokens = 0;
3481
+ const usdCapped = root.maxUsd !== void 0;
3482
+ let freeUsd = root.maxUsd ?? 0;
3483
+ let reservedUsd = 0;
3484
+ let committedUsd = 0;
3485
+ let freeIterations = root.maxIterations;
3486
+ let reservedIterations = 0;
3487
+ let committedIterations = 0;
3488
+ const absoluteDeadlineMs = root.deadlineMs !== void 0 ? now() + root.deadlineMs : 0;
3489
+ let nextTicketId = 0;
3490
+ const open = /* @__PURE__ */ new Set();
3491
+ function reserve(b) {
3492
+ const wantTokens = b.maxTokens;
3493
+ const wantUsd = b.maxUsd ?? 0;
3494
+ const wantIterations = b.maxIterations;
3495
+ if (wantTokens > freeTokens) return { ok: false, reason: "budget-exhausted" };
3496
+ if (wantIterations > freeIterations) return { ok: false, reason: "budget-exhausted" };
3497
+ if (wantUsd > 0 && (!usdCapped || wantUsd > freeUsd)) {
3498
+ return { ok: false, reason: "budget-exhausted" };
3499
+ }
3500
+ freeTokens -= wantTokens;
3501
+ reservedTokens += wantTokens;
3502
+ freeIterations -= wantIterations;
3503
+ reservedIterations += wantIterations;
3504
+ if (wantUsd > 0) {
3505
+ freeUsd -= wantUsd;
3506
+ reservedUsd += wantUsd;
3507
+ }
3508
+ const id = nextTicketId++;
3509
+ open.add(id);
3510
+ return {
3511
+ ok: true,
3512
+ ticket: { id, reserved: { tokens: wantTokens, usd: wantUsd, iterations: wantIterations } }
3513
+ };
3514
+ }
3515
+ function reconcile(ticket, spent) {
3516
+ if (!open.has(ticket.id)) {
3517
+ throw new Error(`budget pool: reconcile of unknown or already-settled ticket ${ticket.id}`);
3518
+ }
3519
+ open.delete(ticket.id);
3520
+ const { tokens: rTokens, usd: rUsd, iterations: rIterations } = ticket.reserved;
3521
+ if (usdCapped && spent.usdKnown === false) {
3522
+ throw new Error(
3523
+ `budget pool: ticket ${ticket.id} reported unknown dollar cost under a dollar-capped budget`
3524
+ );
3525
+ }
3526
+ const spentTokens = totalTokens(spent.tokens);
3527
+ if (spentTokens > rTokens) {
3528
+ throw new Error(
3529
+ `budget pool: ticket ${ticket.id} spent ${spentTokens} tokens > reserved ${rTokens}`
3530
+ );
3531
+ }
3532
+ if (spent.iterations > rIterations) {
3533
+ throw new Error(
3534
+ `budget pool: ticket ${ticket.id} spent ${spent.iterations} iterations > reserved ${rIterations}`
3535
+ );
3536
+ }
3537
+ if (usdCapped && spent.usd > rUsd) {
3538
+ throw new Error(`budget pool: ticket ${ticket.id} spent $${spent.usd} > reserved $${rUsd}`);
3539
+ }
3540
+ reservedTokens -= rTokens;
3541
+ committedTokens += spentTokens;
3542
+ freeTokens += rTokens - spentTokens;
3543
+ reservedIterations -= rIterations;
3544
+ committedIterations += spent.iterations;
3545
+ freeIterations += rIterations - spent.iterations;
3546
+ if (usdCapped && rUsd > 0) {
3547
+ reservedUsd -= rUsd;
3548
+ committedUsd += spent.usd;
3549
+ freeUsd += rUsd - spent.usd;
3550
+ } else {
3551
+ committedUsd += spent.usd;
3552
+ }
3553
+ }
3554
+ function observe(spend) {
3555
+ if (usdCapped && spend.usdKnown === false) {
3556
+ throw new Error(
3557
+ "budget pool: cannot observe unknown dollar cost under a dollar-capped budget"
3558
+ );
3559
+ }
3560
+ const tokens = totalTokens(spend.tokens);
3561
+ freeTokens -= tokens;
3562
+ committedTokens += tokens;
3563
+ freeIterations -= spend.iterations;
3564
+ committedIterations += spend.iterations;
3565
+ committedUsd += spend.usd;
3566
+ if (usdCapped) freeUsd -= spend.usd;
3567
+ }
3568
+ function readout() {
3569
+ return {
3570
+ tokensLeft: freeTokens,
3571
+ usdLeft: usdCapped ? freeUsd : 0,
3572
+ usdCapped,
3573
+ deadlineMs: absoluteDeadlineMs,
3574
+ reservedTokens
3575
+ };
3576
+ }
3577
+ function assertNoOpenTickets() {
3578
+ if (open.size > 0) {
3579
+ throw new Error(
3580
+ `budget pool: ${open.size} reservation(s) still open at join barrier (leaked ticket ids: ${[...open].join(", ")}) \u2014 conserved-pool invariant violated`
3581
+ );
3582
+ }
3583
+ }
3584
+ return {
3585
+ reserve,
3586
+ reconcile,
3587
+ spendFrom: foldUsage,
3588
+ readout,
3589
+ observe,
3590
+ assertNoOpenTickets
3591
+ };
3592
+ }
3593
+
3594
+ // src/runtime/supervise/supervisor.ts
3595
+ var defaultMaxDepth = 4;
3596
+ function createSupervisor() {
3597
+ let attached;
3598
+ async function run(root, task, opts) {
3599
+ const now = opts.now ?? Date.now;
3600
+ const pool = createBudgetPool(opts.budget, now);
3601
+ await opts.journal.beginTree(opts.runId, new Date(now()).toISOString());
3602
+ await opts.journal.appendEvent(opts.runId, {
3603
+ kind: "spawned",
3604
+ id: opts.runId,
3605
+ label: "root",
3606
+ budget: opts.budget,
3607
+ runtime: "inline",
3608
+ seq: 0,
3609
+ at: new Date(now()).toISOString()
3610
+ });
3611
+ const controller = new AbortController();
3612
+ const cascadeAbort = (reason) => {
3613
+ if (controller.signal.aborted) return;
3614
+ controller.abort(reason);
3615
+ };
3616
+ const onCallerAbort = () => cascadeAbort("caller signal aborted");
3617
+ if (opts.signal) {
3618
+ if (opts.signal.aborted) cascadeAbort("caller signal aborted");
3619
+ else opts.signal.addEventListener("abort", onCallerAbort, { once: true });
3620
+ }
3621
+ const breaker = createIntensityBreaker(opts, () => cascadeAbort("intensity breaker tripped"));
3622
+ const journal = wrapJournalForBreaker(opts.journal, breaker);
3623
+ const scope = createScope({
3624
+ parentId: opts.runId,
3625
+ root: opts.runId,
3626
+ pool,
3627
+ journal,
3628
+ blobs: opts.blobs,
3629
+ executors: opts.executors,
3630
+ seams: {},
3631
+ depth: 0,
3632
+ maxDepth: opts.maxDepth ?? defaultMaxDepth,
3633
+ signal: controller.signal,
3634
+ now,
3635
+ hooks: opts.hooks
3636
+ });
3637
+ const openScope = scope;
3638
+ if (attached) {
3639
+ attached.bind({ scope: openScope, cascadeAbort, signal: pushRootSignal(cascadeAbort) });
3640
+ }
3641
+ let actOutcome;
3642
+ try {
3643
+ const out = await root.act(task, scope);
3644
+ actOutcome = { ok: true, out };
3645
+ } catch (error) {
3646
+ actOutcome = { ok: false, error };
3647
+ } finally {
3648
+ await drainLiveChildren(openScope, controller);
3649
+ if (opts.signal) opts.signal.removeEventListener("abort", onCallerAbort);
3650
+ if (attached) attached.unbind();
3651
+ }
3652
+ const tree = scope.view;
3653
+ if (actOutcome.ok) {
3654
+ pool.assertNoOpenTickets();
3655
+ const out = actOutcome.out;
3656
+ if (out !== void 0) {
3657
+ const outRef = contentAddress(out);
3658
+ await opts.blobs.put(outRef, out);
3659
+ const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
3660
+ return {
3661
+ kind: "winner",
3662
+ out,
3663
+ outRef,
3664
+ tree,
3665
+ spentTotal: addSpend(childWork, driverInference),
3666
+ ...isNonEmptySpend(driverInference) ? { spentBreakdown: { driverInference, childWork } } : {}
3667
+ };
3668
+ }
3669
+ return noWinner();
3670
+ }
3671
+ return noWinner();
3672
+ async function noWinner() {
3673
+ const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
3674
+ return {
3675
+ kind: "no-winner",
3676
+ reason: classifyNoWinner(controller, pool, opts, breaker),
3677
+ tree,
3678
+ downCount: breaker.downCount(),
3679
+ spentTotal: addSpend(childWork, driverInference)
3680
+ };
3681
+ }
3682
+ }
3683
+ function attach(h) {
3684
+ const control = rootControls.get(h);
3685
+ if (!control) {
3686
+ throw new RuntimeRunStateError(
3687
+ "supervisor.attach: handle was not minted by createRootHandle (no control channel)"
3688
+ );
3689
+ }
3690
+ attached = control;
3691
+ }
3692
+ return { run, attach };
3693
+ }
3694
+ var rootControls = /* @__PURE__ */ new WeakMap();
3695
+ function pushRootSignal(cascadeAbort) {
3696
+ return (msg) => {
3697
+ if (msg.kind === "cancel") cascadeAbort(msg.reason ?? "root signal: cancel");
3698
+ };
3699
+ }
3700
+ function createIntensityBreaker(opts, trip) {
3701
+ const max = opts.maxRestarts;
3702
+ const within = opts.withinMs;
3703
+ const armed = max !== void 0 && within !== void 0;
3704
+ const recent = [];
3705
+ let total = 0;
3706
+ let isTripped = false;
3707
+ return {
3708
+ recordDown(at) {
3709
+ total += 1;
3710
+ if (!armed || isTripped) return;
3711
+ recent.push(at);
3712
+ const cutoff = at - within;
3713
+ while (recent.length > 0 && recent[0] < cutoff) recent.shift();
3714
+ if (recent.length > max) {
3715
+ isTripped = true;
3716
+ trip();
3717
+ }
3718
+ },
3719
+ tripped() {
3720
+ return isTripped;
3721
+ },
3722
+ downCount() {
3723
+ return total;
3724
+ }
3725
+ };
3726
+ }
3727
+ function wrapJournalForBreaker(journal, breaker) {
3728
+ return {
3729
+ loadTree: (root) => journal.loadTree(root),
3730
+ beginTree: (root, at) => journal.beginTree(root, at),
3731
+ appendEvent: (root, ev) => {
3732
+ if (ev.kind === "settled" && ev.status === "down") breaker.recordDown(Date.parse(ev.at));
3733
+ return journal.appendEvent(root, ev);
3734
+ }
3735
+ };
3736
+ }
3737
+ async function drainLiveChildren(scope, controller) {
3738
+ const hasLive = scope.view.inFlight > 0;
3739
+ if (!hasLive) return;
3740
+ if (!controller.signal.aborted) controller.abort();
3741
+ await Promise.allSettled([drainCursor(scope)]);
3742
+ }
3743
+ async function drainCursor(scope) {
3744
+ for (; ; ) {
3745
+ const settled = await scope.next();
3746
+ if (settled === null) return;
3747
+ }
3748
+ }
3749
+ function classifyNoWinner(controller, pool, opts, breaker) {
3750
+ if (breaker.tripped()) return "all-children-down";
3751
+ if (controller.signal.aborted) return "aborted";
3752
+ if (poolExhausted(pool, opts)) return "budget-exhausted";
3753
+ return "all-children-down";
3754
+ }
3755
+ function poolExhausted(pool, opts) {
3756
+ const r = pool.readout();
3757
+ if (r.tokensLeft <= 0) return true;
3758
+ if (opts.budget.maxUsd !== void 0 && r.usdLeft <= 0) return true;
3759
+ if (opts.budget.deadlineMs !== void 0 && r.deadlineMs > 0 && (opts.now ?? Date.now)() >= r.deadlineMs) {
3760
+ return true;
3761
+ }
3762
+ return false;
3763
+ }
3764
+ async function spentFromJournal(journal, root) {
3765
+ const events = await journal.loadTree(root);
3766
+ if (events === void 0) {
3767
+ throw new RuntimeRunStateError(
3768
+ `supervisor: spawn tree '${root}' is missing from the journal after run (corrupted log)`
3769
+ );
3770
+ }
3771
+ const childWork = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3772
+ const driverInference = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3773
+ for (const ev of events) {
3774
+ if (ev.kind === "settled") accumulate(childWork, ev.spent);
3775
+ else if (ev.kind === "metered") accumulate(driverInference, ev.spend);
3776
+ }
3777
+ return { childWork, driverInference };
3778
+ }
3779
+ function accumulate(a, b) {
3780
+ a.iterations += b.iterations;
3781
+ a.tokens.input += b.tokens.input;
3782
+ a.tokens.output += b.tokens.output;
3783
+ a.usd += b.usd;
3784
+ if (b.usdKnown === false) a.usdKnown = false;
3785
+ a.ms += b.ms;
3786
+ }
3787
+ function addSpend(a, b) {
3788
+ return {
3789
+ iterations: a.iterations + b.iterations,
3790
+ tokens: { input: a.tokens.input + b.tokens.input, output: a.tokens.output + b.tokens.output },
3791
+ usd: a.usd + b.usd,
3792
+ ...a.usdKnown === false || b.usdKnown === false ? { usdKnown: false } : {},
3793
+ ms: a.ms + b.ms
3794
+ };
3795
+ }
3796
+ function isNonEmptySpend(s) {
3797
+ return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
3798
+ }
3799
+
2464
3800
  // src/runtime/supervise/event-bus.ts
2465
3801
  function createEventBus(now = Date.now) {
2466
3802
  const queue = [];
@@ -3117,7 +4453,7 @@ ${narrative}` : roster;
3117
4453
  return `${roster}
3118
4454
 
3119
4455
  ## Progress notes
3120
- Summary unavailable: ${errMessage(e)}`;
4456
+ Summary unavailable: ${errMessage2(e)}`;
3121
4457
  }
3122
4458
  }),
3123
4459
  ...opts.compaction.onCompact ? { onCompact: opts.compaction.onCompact } : {},
@@ -3188,7 +4524,7 @@ function safeJson(v) {
3188
4524
  return String(v);
3189
4525
  }
3190
4526
  }
3191
- function errMessage(e) {
4527
+ function errMessage2(e) {
3192
4528
  return e instanceof Error ? e.message : String(e);
3193
4529
  }
3194
4530
 
@@ -5189,10 +6525,21 @@ async function delegate(intent, opts = {}) {
5189
6525
 
5190
6526
  export {
5191
6527
  gateOnDeliverable,
6528
+ defineRuntimeHooks,
6529
+ composeRuntimeHooks,
6530
+ notifyRuntimeHookEvent,
6531
+ notifyRuntimeDecisionPoint,
6532
+ createScope,
6533
+ settledToIteration,
6534
+ withDriverExecutor,
5192
6535
  createWorktree,
5193
6536
  captureWorktreeDiff,
5194
6537
  removeWorktree,
5195
6538
  runWorktreeHarness,
6539
+ routerChatWithUsage,
6540
+ routerChatWithTools,
6541
+ routerToolLoop,
6542
+ routerBrain,
5196
6543
  acquireSandbox,
5197
6544
  probeSandboxCapabilities,
5198
6545
  createSandboxLineage,
@@ -5205,6 +6552,9 @@ export {
5205
6552
  createExecutor,
5206
6553
  createExecutorRegistry,
5207
6554
  createInMemoryRunContext,
6555
+ spendFromUsageEvents,
6556
+ createBudgetPool,
6557
+ createSupervisor,
5208
6558
  createEventBus,
5209
6559
  createCoordinationTools,
5210
6560
  driverAgent,
@@ -5263,4 +6613,4 @@ export {
5263
6613
  workerFromBackend,
5264
6614
  supervise
5265
6615
  };
5266
- //# sourceMappingURL=chunk-2BQMAZAB.js.map
6616
+ //# sourceMappingURL=chunk-URMJTSXB.js.map