@tangle-network/agent-runtime 0.72.0 → 0.74.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 (53) hide show
  1. package/dist/agent.d.ts +34 -2
  2. package/dist/agent.js +5 -3
  3. package/dist/agent.js.map +1 -1
  4. package/dist/analyst-loop.d.ts +1 -1
  5. package/dist/analyst-loop.js +3 -2
  6. package/dist/{chunk-P5OKDSLB.js → chunk-IODKUOBA.js} +5 -3
  7. package/dist/{chunk-P5OKDSLB.js.map → chunk-IODKUOBA.js.map} +1 -1
  8. package/dist/chunk-JPURCA2O.js +266 -0
  9. package/dist/chunk-JPURCA2O.js.map +1 -0
  10. package/dist/{chunk-VYXBFWHZ.js → chunk-MRWXCFV5.js} +2 -2
  11. package/dist/chunk-NBV35BR6.js +68 -0
  12. package/dist/chunk-NBV35BR6.js.map +1 -0
  13. package/dist/chunk-O2UPHN7X.js +114 -0
  14. package/dist/chunk-O2UPHN7X.js.map +1 -0
  15. package/dist/{chunk-HXVMLRYU.js → chunk-QKNBYHMK.js} +7 -7
  16. package/dist/{chunk-PHKNOAOU.js → chunk-SA5GCF2X.js} +25 -7
  17. package/dist/chunk-SA5GCF2X.js.map +1 -0
  18. package/dist/{chunk-VLF5RHEQ.js → chunk-T2HVQVB4.js} +1 -66
  19. package/dist/chunk-T2HVQVB4.js.map +1 -0
  20. package/dist/{chunk-55WKGOGU.js → chunk-ZKMOIEOB.js} +123 -149
  21. package/dist/chunk-ZKMOIEOB.js.map +1 -0
  22. package/dist/{coordination-CFVF0OzX.d.ts → coordination-DEVknvQo.d.ts} +9 -2
  23. package/dist/generator-YkAQrOoD.d.ts +382 -0
  24. package/dist/index.d.ts +13 -167
  25. package/dist/index.js +21 -267
  26. package/dist/index.js.map +1 -1
  27. package/dist/intelligence.d.ts +1 -1
  28. package/dist/lifecycle.d.ts +868 -0
  29. package/dist/lifecycle.js +979 -0
  30. package/dist/lifecycle.js.map +1 -0
  31. package/dist/local-harness-BE_h8szs.d.ts +93 -0
  32. package/dist/{loop-runner-bin-B7biH0Gk.d.ts → loop-runner-bin-BPthX22l.d.ts} +2 -2
  33. package/dist/loop-runner-bin.d.ts +6 -5
  34. package/dist/loop-runner-bin.js +7 -5
  35. package/dist/loops.d.ts +47 -10
  36. package/dist/loops.js +7 -3
  37. package/dist/mcp/bin.js +7 -5
  38. package/dist/mcp/bin.js.map +1 -1
  39. package/dist/mcp/index.d.ts +8 -6
  40. package/dist/mcp/index.js +8 -5
  41. package/dist/mcp/index.js.map +1 -1
  42. package/dist/mcp-serve-verifier-CT1KLTG_.d.ts +162 -0
  43. package/dist/{openai-tools-DOI5mKNw.d.ts → openai-tools-D-VCAEmw.d.ts} +1 -1
  44. package/dist/profiles.d.ts +1 -1
  45. package/dist/{types-K8-xkiw1.d.ts → types-YimN9PQP.d.ts} +80 -1
  46. package/dist/{worktree-JXGJ1MxQ.d.ts → worktree-CtuEQ7bZ.d.ts} +2 -93
  47. package/dist/{worktree-fanout-DJHQy7Ux.d.ts → worktree-fanout-Cdez8GR7.d.ts} +16 -5
  48. package/package.json +6 -1
  49. package/dist/chunk-55WKGOGU.js.map +0 -1
  50. package/dist/chunk-PHKNOAOU.js.map +0 -1
  51. package/dist/chunk-VLF5RHEQ.js.map +0 -1
  52. /package/dist/{chunk-VYXBFWHZ.js.map → chunk-MRWXCFV5.js.map} +0 -0
  53. /package/dist/{chunk-HXVMLRYU.js.map → chunk-QKNBYHMK.js.map} +0 -0
@@ -1,58 +1,3 @@
1
- // src/errors.ts
2
- import { AgentEvalError } from "@tangle-network/agent-eval";
3
- import {
4
- AgentEvalError as AgentEvalError2,
5
- ConfigError,
6
- JudgeError,
7
- NotFoundError,
8
- ValidationError
9
- } from "@tangle-network/agent-eval";
10
- var SessionMismatchError = class extends AgentEvalError {
11
- sessionBackend;
12
- requestedBackend;
13
- constructor(sessionBackend, requestedBackend, options) {
14
- super(
15
- "validation",
16
- `Cannot resume ${sessionBackend} session with ${requestedBackend} backend`,
17
- options
18
- );
19
- this.sessionBackend = sessionBackend;
20
- this.requestedBackend = requestedBackend;
21
- }
22
- };
23
- var BackendTransportError = class extends AgentEvalError {
24
- backend;
25
- status;
26
- /**
27
- * Truncated upstream response body (≤2 KiB) when available. Diagnostic
28
- * only — surfaces in `backend_error.error.body` and `final.error.body`
29
- * so operators can see "free_tier_limit", "invalid_api_key", etc. without
30
- * cracking the log line open.
31
- */
32
- body;
33
- constructor(backend, message, options) {
34
- super("config", message, options);
35
- this.backend = backend;
36
- this.status = options?.status;
37
- this.body = options?.body;
38
- }
39
- };
40
- var RuntimeRunStateError = class extends AgentEvalError {
41
- constructor(message, options) {
42
- super("validation", message, options);
43
- }
44
- };
45
- var PlannerError = class extends AgentEvalError {
46
- constructor(message, options) {
47
- super("validation", message, options);
48
- }
49
- };
50
- var AnalystError = class extends AgentEvalError {
51
- constructor(message, options) {
52
- super("validation", message, options);
53
- }
54
- };
55
-
56
1
  // src/runtime/sandbox-events.ts
57
2
  function extractLlmCallEvent(event, agentRunName) {
58
3
  if (!event || typeof event !== "object") return void 0;
@@ -127,17 +72,7 @@ function mapSandboxEvent(event, opts = {}) {
127
72
  }
128
73
 
129
74
  export {
130
- SessionMismatchError,
131
- BackendTransportError,
132
- RuntimeRunStateError,
133
- PlannerError,
134
- AnalystError,
135
- AgentEvalError2 as AgentEvalError,
136
- ConfigError,
137
- JudgeError,
138
- NotFoundError,
139
- ValidationError,
140
75
  extractLlmCallEvent,
141
76
  mapSandboxEvent
142
77
  };
143
- //# sourceMappingURL=chunk-VLF5RHEQ.js.map
78
+ //# sourceMappingURL=chunk-T2HVQVB4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/sandbox-events.ts"],"sourcesContent":["/**\n * Sandbox-event → runtime-event mapping.\n *\n * The sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`\n * whose `type` vocabulary is backend-determined (opencode, etc.) rather than\n * enumerated by the SDK. Two consumers project it:\n * - the loop kernel's cost ledger (`extractLlmCallEvent`) — sums usage off\n * every cost-bearing event, regardless of stream shape;\n * - the `AgentRuntime.act` streaming contract (`mapSandboxEvent`) — projects\n * incremental events to the `RuntimeStreamEvent` chat-UX vocabulary.\n *\n * Both live here so the empirically-observed `type` vocabulary has one home.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport type { RuntimeStreamEvent } from '../types'\n\n/**\n * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when\n * the event carries usage/cost data. Returns `undefined` for non-cost events\n * so the kernel can iterate the full stream without branching.\n *\n * Canonical cost-carrying types observed in the wild:\n * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`\n * - `message.completed` / `result` — `data: { usage: { inputTokens,\n * outputTokens, totalCostUsd? } }`\n * - `cost.usage` / `usage` — same shape under a dedicated type\n *\n * Numeric coercion is strict: `Number.isFinite` gates every accumulator write\n * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.\n */\nexport function extractLlmCallEvent(\n event: SandboxEvent,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'llm_call' || type === 'cost.usage' || type === 'usage') {\n return buildLlmCall(data, agentRunName)\n }\n if (type === 'message.completed' || type === 'result' || type === 'final') {\n const usage = data.usage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName)\n }\n // sandbox 0.4.0 terminal event: `data = { tokenUsage: { inputTokens, outputTokens,\n // reasoningTokens, cacheReadInputTokens }, totalCostUsd }`. Usage lives under\n // `tokenUsage` (not `usage`) and the cost is top-level — neither matched the\n // branches above, so an in-process loopDispatch run reported {0,0} and the\n // backend-integrity guard misread a real run as a stub. Reasoning tokens are\n // billed output (reasoning models), so they fold into the output count.\n if (type === 'done') {\n const usage = data.tokenUsage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n const out = pickFiniteNumber(usage, ['outputTokens', 'completion_tokens', 'tokensOut'])\n const reasoning = pickFiniteNumber(usage, ['reasoningTokens'])\n const mergedOut =\n out !== undefined || reasoning !== undefined ? (out ?? 0) + (reasoning ?? 0) : undefined\n return buildLlmCall(\n {\n inputTokens: usage.inputTokens,\n outputTokens: mergedOut,\n totalCostUsd: data.totalCostUsd,\n model: data.model ?? usage.model,\n },\n agentRunName,\n )\n }\n return undefined\n}\n\nfunction buildLlmCall(\n data: Record<string, unknown>,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n const tokensIn = pickFiniteNumber(data, ['tokensIn', 'inputTokens', 'prompt_tokens'])\n const tokensOut = pickFiniteNumber(data, ['tokensOut', 'outputTokens', 'completion_tokens'])\n const costUsd = pickFiniteNumber(data, ['costUsd', 'totalCostUsd', 'cost_usd', 'cost'])\n if (tokensIn === undefined && tokensOut === undefined && costUsd === undefined) {\n return undefined\n }\n const model = typeof data.model === 'string' && data.model.length > 0 ? data.model : agentRunName\n const event: RuntimeStreamEvent & { type: 'llm_call' } = {\n type: 'llm_call',\n model,\n }\n if (tokensIn !== undefined) event.tokensIn = tokensIn\n if (tokensOut !== undefined) event.tokensOut = tokensOut\n if (costUsd !== undefined) event.costUsd = costUsd\n return event\n}\n\nfunction pickFiniteNumber(data: Record<string, unknown>, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\n/**\n * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,\n * for runtimes that bridge a sandbox `streamPrompt` into the\n * `AgentRuntime.act` streaming contract. Returns `undefined` for events that\n * have no faithful projection — the raw stream is preserved separately for the\n * `OutputAdapter`, so an unmapped event never loses data.\n *\n * Mapped (the task-optional incremental variants — no synthesized task\n * lifecycle, no guessed tool-part shapes):\n * - `message.part.updated` text part → `text_delta`\n * - `message.part.updated` reasoning/thinking part → `reasoning_delta`\n * - cost-bearing events → `llm_call` (shared with the ledger extractor)\n *\n * The opencode backend emits incremental text as\n * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;\n * `delta` is the increment, `part.text` the running accumulation.\n */\nexport function mapSandboxEvent(\n event: SandboxEvent,\n opts: { agentRunName?: string } = {},\n): RuntimeStreamEvent | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n const partType = String(part.type ?? '')\n const delta = typeof data.delta === 'string' ? data.delta : undefined\n const text = delta ?? (typeof part.text === 'string' ? part.text : undefined)\n if (text === undefined) return undefined\n if (partType === 'text') return { type: 'text_delta', text }\n if (partType === 'reasoning' || partType === 'thinking')\n return { type: 'reasoning_delta', text }\n return undefined\n }\n\n return extractLlmCallEvent(event, opts.agentRunName ?? 'agent')\n}\n"],"mappings":"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","names":[]}
@@ -1,127 +1,24 @@
1
1
  import {
2
2
  buildLoopSpanNodes
3
3
  } from "./chunk-VMNEQHJR.js";
4
+ import {
5
+ extractLlmCallEvent
6
+ } from "./chunk-T2HVQVB4.js";
7
+ import {
8
+ harnessInvocation,
9
+ runLocalHarness
10
+ } from "./chunk-O2UPHN7X.js";
4
11
  import {
5
12
  AgentEvalError,
6
13
  ConfigError,
7
14
  NotFoundError,
8
15
  RuntimeRunStateError,
9
- ValidationError,
10
- extractLlmCallEvent
11
- } from "./chunk-VLF5RHEQ.js";
16
+ ValidationError
17
+ } from "./chunk-NBV35BR6.js";
12
18
  import {
13
19
  UI_LENSES
14
20
  } from "./chunk-WIR4HOOJ.js";
15
21
 
16
- // src/mcp/local-harness.ts
17
- import { spawn } from "child_process";
18
- var HARNESS_INVOCATIONS = {
19
- claude: {
20
- command: "claude",
21
- buildArgs: (taskPrompt) => ["--headless", "-p", taskPrompt],
22
- modelArgs: (model) => ["-m", model]
23
- },
24
- codex: {
25
- command: "codex",
26
- buildArgs: (taskPrompt) => ["run", taskPrompt],
27
- modelArgs: (model) => ["-m", model]
28
- },
29
- opencode: {
30
- command: "opencode",
31
- buildArgs: (taskPrompt) => ["run", taskPrompt],
32
- modelArgs: (model) => ["-m", model]
33
- }
34
- };
35
- function harnessInvocation(harness, profile, taskPrompt) {
36
- const invocation = HARNESS_INVOCATIONS[harness];
37
- if (!invocation) {
38
- throw new Error(`harnessInvocation: unknown harness ${String(harness)}`);
39
- }
40
- const systemPrompt = profile.prompt?.systemPrompt;
41
- const composedPrompt = typeof systemPrompt === "string" && systemPrompt.trim().length > 0 ? `${systemPrompt}
42
-
43
- ${taskPrompt}` : taskPrompt;
44
- const args = invocation.buildArgs(composedPrompt);
45
- const model = profile.model?.default;
46
- if (typeof model === "string" && model.length > 0) {
47
- args.push(...invocation.modelArgs(model));
48
- }
49
- return { command: invocation.command, args };
50
- }
51
- var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
52
- function runLocalHarness(options) {
53
- const { harness, cwd, taskPrompt } = options;
54
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
55
- const env = options.env ?? process.env;
56
- const spawnImpl = options.spawn ?? spawn;
57
- const invocation = HARNESS_INVOCATIONS[harness];
58
- if (!invocation) {
59
- return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`));
60
- }
61
- const startedAt = Date.now();
62
- const command = options.invocation?.command ?? invocation.command;
63
- const args = options.invocation ? [...options.invocation.args] : invocation.buildArgs(taskPrompt);
64
- return new Promise((resolve, reject) => {
65
- let child;
66
- try {
67
- child = spawnImpl(command, args, { cwd, env, stdio: "pipe" });
68
- } catch (err) {
69
- reject(err instanceof Error ? err : new Error(String(err)));
70
- return;
71
- }
72
- child.stdin?.end();
73
- let stdout = "";
74
- let stderr = "";
75
- let timedOut = false;
76
- let settled = false;
77
- const timer = timeoutMs > 0 ? setTimeout(() => {
78
- timedOut = true;
79
- if (!child.killed) child.kill("SIGTERM");
80
- }, timeoutMs) : null;
81
- if (timer && typeof timer.unref === "function") {
82
- ;
83
- timer.unref();
84
- }
85
- const onAbort = () => {
86
- if (!child.killed) child.kill("SIGTERM");
87
- };
88
- if (options.signal) {
89
- if (options.signal.aborted) onAbort();
90
- else options.signal.addEventListener("abort", onAbort, { once: true });
91
- }
92
- child.stdout?.on("data", (chunk) => {
93
- stdout += String(chunk);
94
- });
95
- child.stderr?.on("data", (chunk) => {
96
- stderr += String(chunk);
97
- });
98
- const finalize3 = (result) => {
99
- if (settled) return;
100
- settled = true;
101
- if (timer) clearTimeout(timer);
102
- options.signal?.removeEventListener("abort", onAbort);
103
- resolve(result);
104
- };
105
- child.on("error", (err) => {
106
- if (settled) return;
107
- settled = true;
108
- if (timer) clearTimeout(timer);
109
- options.signal?.removeEventListener("abort", onAbort);
110
- reject(err);
111
- });
112
- child.on("close", (code, signal) => {
113
- finalize3({
114
- exitCode: code,
115
- stdout,
116
- stderr,
117
- killedBySignal: signal,
118
- durationMs: Date.now() - startedAt,
119
- timedOut
120
- });
121
- });
122
- });
123
- }
124
-
125
22
  // src/runtime/supervise/model-policy.ts
126
23
  function assertModelAllowed(model, allowed) {
127
24
  if (!allowed || model === void 0) return;
@@ -162,6 +59,9 @@ function throwAbort() {
162
59
  function throwIfAborted(signal) {
163
60
  if (signal?.aborted) throw abortError();
164
61
  }
62
+ function isAbortError(err) {
63
+ return err instanceof Error && err.name === "AbortError";
64
+ }
165
65
  function sleep(ms, signal) {
166
66
  return new Promise((resolve) => {
167
67
  if (signal?.aborted) {
@@ -564,6 +464,8 @@ function createSandboxLineage(client, capabilities, options = {}) {
564
464
  throw new ValidationError("createSandboxLineage: client.create is required");
565
465
  }
566
466
  const streaming = options.streaming ?? "sse";
467
+ const recordMount = options.recordMount ?? (() => {
468
+ });
567
469
  const forkConcurrency = Math.max(
568
470
  1,
569
471
  Math.floor(options.maxConcurrency ?? DEFAULT_FORK_CONCURRENCY)
@@ -573,7 +475,7 @@ function createSandboxLineage(client, capabilities, options = {}) {
573
475
  if (signal.aborted) throwAbort();
574
476
  const opts = buildBackendOptions(spec.profile, spec.sandboxOverrides);
575
477
  const box = await acquireSandbox(client, opts, { signal });
576
- await spec.prepareBox?.(box, { signal });
478
+ await spec.prepareBox?.(box, { signal, recordMount });
577
479
  owned.push(box);
578
480
  return box;
579
481
  };
@@ -602,7 +504,7 @@ function createSandboxLineage(client, capabilities, options = {}) {
602
504
  if (checkpointId !== void 0) {
603
505
  const box2 = await forkFromCheckpoint(parent.box, checkpointId, signal);
604
506
  owned.push(box2);
605
- await spec.prepareBox?.(box2, { signal });
507
+ await spec.prepareBox?.(box2, { signal, recordMount });
606
508
  const sessionId2 = mintSessionId();
607
509
  return {
608
510
  handle: { box: box2, sessionId: sessionId2 },
@@ -691,13 +593,17 @@ async function runLoop(options) {
691
593
  const loopStart = now();
692
594
  const driverName = options.driver.name ?? "driver";
693
595
  const iterations = [];
596
+ const mounts = [];
597
+ const recordMount = (entry) => {
598
+ mounts.push(entry);
599
+ };
694
600
  let round = 0;
695
601
  const ownedBoxes = [];
696
602
  const collectBox = options.onWorkerBox ? (box) => {
697
603
  ownedBoxes.push(box);
698
604
  options.onWorkerBox?.(box);
699
605
  } : void 0;
700
- const lineageState = await setUpLineage(options, maxConcurrency);
606
+ const lineageState = await setUpLineage(options, maxConcurrency, recordMount);
701
607
  emitRunLoopHook(options, {
702
608
  target: "agent.run",
703
609
  phase: "before",
@@ -808,7 +714,8 @@ async function runLoop(options) {
808
714
  parentIndex,
809
715
  collectBox,
810
716
  lineagePlan,
811
- lineageState
717
+ lineageState,
718
+ recordMount
812
719
  });
813
720
  if (controller.signal.aborted) throwAbort();
814
721
  emitRunLoopHook(options, {
@@ -835,11 +742,19 @@ async function runLoop(options) {
835
742
  payload: { decision: stringifySafe(decision), historyLength: iterations.length }
836
743
  });
837
744
  if (isTerminalDecision(decision)) {
838
- return await finalizeAndEmitEnded(options, decision, iterations, loopStart, now, runId);
745
+ return await finalizeAndEmitEnded(
746
+ options,
747
+ decision,
748
+ iterations,
749
+ loopStart,
750
+ now,
751
+ runId,
752
+ mounts
753
+ );
839
754
  }
840
755
  if (lineageState) await pruneLineage(lineageState, iterations);
841
756
  }
842
- return await decideAndFinalize(options, iterations, loopStart, now, runId);
757
+ return await decideAndFinalize(options, iterations, loopStart, now, runId, mounts);
843
758
  } finally {
844
759
  if (options.ctx.signal) options.ctx.signal.removeEventListener("abort", onOuterAbort);
845
760
  await Promise.allSettled(
@@ -849,7 +764,7 @@ async function runLoop(options) {
849
764
  if (lineageState) await lineageState.lineage.teardown();
850
765
  }
851
766
  }
852
- async function setUpLineage(options, maxConcurrency) {
767
+ async function setUpLineage(options, maxConcurrency, recordMount) {
853
768
  const lineageOpts = options.lineage;
854
769
  if (!lineageOpts || !lineageOpts.sessionContinuity && !lineageOpts.forkFanout) return void 0;
855
770
  if (options.onWorkerBox) {
@@ -861,7 +776,8 @@ async function setUpLineage(options, maxConcurrency) {
861
776
  return {
862
777
  lineage: createSandboxLineage(options.ctx.sandboxClient, capabilities, {
863
778
  maxConcurrency,
864
- streaming: lineageOpts.streaming
779
+ streaming: lineageOpts.streaming,
780
+ recordMount
865
781
  }),
866
782
  options: lineageOpts,
867
783
  handles: /* @__PURE__ */ new Map(),
@@ -991,7 +907,7 @@ async function executeIteration(args) {
991
907
  args.lineageState?.handles.set(args.item.index, acquired.handle);
992
908
  stream = acquired.events;
993
909
  } else {
994
- box = await createSandboxForSpec(args.ctx.sandboxClient, spec, args.signal);
910
+ box = await createSandboxForSpec(args.ctx.sandboxClient, spec, args.signal, args.recordMount);
995
911
  const prompt = spec.taskToPrompt(args.item.task);
996
912
  stream = args.streaming === "poll" ? promptEvents("poll", box, prompt, `${args.runId}-i${args.item.index}`, args.signal) : box.streamPrompt(prompt, { signal: args.signal });
997
913
  }
@@ -1060,13 +976,13 @@ async function executeIteration(args) {
1060
976
  await destroySandboxSafe(box, args.ctx.traceEmitter, args.runId, args.now);
1061
977
  }
1062
978
  }
1063
- if (isAbortError(slot.error) || args.signal.aborted) {
979
+ if (isAbortError2(slot.error) || args.signal.aborted) {
1064
980
  if (slot.error) throw slot.error;
1065
981
  throwAbort();
1066
982
  }
1067
983
  if (slot.error instanceof ValidationError) throw slot.error;
1068
984
  }
1069
- function isAbortError(err) {
985
+ function isAbortError2(err) {
1070
986
  return err instanceof Error && err.name === "AbortError";
1071
987
  }
1072
988
  var TEARDOWN_TIMEOUT_MS2 = 15e3;
@@ -1120,15 +1036,35 @@ function readSandboxId(box) {
1120
1036
  const raw = box.id;
1121
1037
  return typeof raw === "string" && raw.length > 0 ? raw : void 0;
1122
1038
  }
1123
- async function createSandboxForSpec(client, spec, signal) {
1039
+ async function createSandboxForSpec(client, spec, signal, recordMount) {
1124
1040
  const opts = buildBackendOptions(spec.profile, spec.sandboxOverrides);
1125
1041
  if (signal.aborted) throwAbort();
1126
1042
  const box = await acquireSandbox(client, opts, { signal });
1127
- await spec.prepareBox?.(box, { signal });
1043
+ await invokePrepareBox(spec, box, signal, recordMount);
1128
1044
  return box;
1129
1045
  }
1046
+ async function invokePrepareBox(spec, box, signal, recordMount) {
1047
+ if (!spec.prepareBox) return;
1048
+ await spec.prepareBox(box, { signal, recordMount: recordMount ?? noopMountRecorder });
1049
+ }
1050
+ var noopMountRecorder = () => {
1051
+ };
1130
1052
  function finalize(args) {
1131
- const winner = args.options.selectWinner ? args.options.selectWinner(args.iterations) : args.options.driver.selectWinner?.(args.iterations) ?? defaultSelectWinner(args.iterations);
1053
+ let selector;
1054
+ let winner;
1055
+ if (args.options.selectWinner) {
1056
+ selector = "caller";
1057
+ winner = args.options.selectWinner(args.iterations);
1058
+ } else {
1059
+ const authored = args.options.driver.selectWinner?.(args.iterations);
1060
+ if (authored) {
1061
+ selector = "driver";
1062
+ winner = authored;
1063
+ } else {
1064
+ selector = "default";
1065
+ winner = defaultSelectWinner(args.iterations);
1066
+ }
1067
+ }
1132
1068
  const costUsd = args.iterations.reduce((sum, iter) => sum + (iter.costUsd || 0), 0);
1133
1069
  const tokenUsage = args.iterations.reduce((acc, iter) => {
1134
1070
  addTokenUsage(acc, iter.tokenUsage);
@@ -1140,11 +1076,36 @@ function finalize(args) {
1140
1076
  winner,
1141
1077
  durationMs: args.now() - args.startMs,
1142
1078
  costUsd,
1143
- tokenUsage
1079
+ tokenUsage,
1080
+ provenance: {
1081
+ mounts: args.mounts,
1082
+ selectionReceipts: buildSelectionReceipts(args.iterations, winner, selector)
1083
+ }
1144
1084
  };
1145
1085
  return result;
1146
1086
  }
1147
- async function decideAndFinalize(options, iterations, startMs, now, runId) {
1087
+ function buildSelectionReceipts(iterations, winner, selector) {
1088
+ const receipts = [];
1089
+ for (const iter of iterations) {
1090
+ if (iter.output === void 0 || iter.error) continue;
1091
+ const selected = winner?.iterationIndex === iter.index;
1092
+ const receipt = {
1093
+ candidateIndex: iter.index,
1094
+ selected,
1095
+ selector
1096
+ };
1097
+ if (iter.verdict?.score !== void 0) receipt.score = iter.verdict.score;
1098
+ if (selector === "default") receipt.reason = defaultSelectorReason(iter, selected);
1099
+ receipts.push(receipt);
1100
+ }
1101
+ return receipts;
1102
+ }
1103
+ function defaultSelectorReason(iter, selected) {
1104
+ const valid = iter.verdict?.valid === true;
1105
+ if (selected) return valid ? "best valid score" : "best non-errored score (no valid candidate)";
1106
+ return valid ? "valid but not top score" : "not selected";
1107
+ }
1108
+ async function decideAndFinalize(options, iterations, startMs, now, runId, mounts) {
1148
1109
  emitRunLoopHook(options, {
1149
1110
  target: "agent.decision",
1150
1111
  phase: "before",
@@ -1166,10 +1127,10 @@ async function decideAndFinalize(options, iterations, startMs, now, runId) {
1166
1127
  timestamp: now(),
1167
1128
  payload: { decision: stringifySafe(decision), historyLength: iterations.length }
1168
1129
  });
1169
- return finalizeAndEmitEnded(options, decision, iterations, startMs, now, runId);
1130
+ return finalizeAndEmitEnded(options, decision, iterations, startMs, now, runId, mounts);
1170
1131
  }
1171
- async function finalizeAndEmitEnded(options, decision, iterations, startMs, now, runId) {
1172
- const result = finalize({ options, decision, iterations, startMs, now, runId });
1132
+ async function finalizeAndEmitEnded(options, decision, iterations, startMs, now, runId, mounts) {
1133
+ const result = finalize({ options, decision, iterations, startMs, now, runId, mounts });
1173
1134
  emitRunLoopHook(options, {
1174
1135
  target: "agent.run",
1175
1136
  phase: "after",
@@ -1293,7 +1254,7 @@ function createScope(args) {
1293
1254
  let cursorSeq = 0;
1294
1255
  let meterSeq = 0;
1295
1256
  const now = args.now ?? Date.now;
1296
- function spawn5(agent, task, opts) {
1257
+ function spawn4(agent, task, opts) {
1297
1258
  if (args.maxDepth !== void 0 && args.depth >= args.maxDepth) {
1298
1259
  return { ok: false, reason: "depth-exceeded" };
1299
1260
  }
@@ -1444,7 +1405,7 @@ function createScope(args) {
1444
1405
  );
1445
1406
  }
1446
1407
  return {
1447
- spawn: spawn5,
1408
+ spawn: spawn4,
1448
1409
  next,
1449
1410
  send,
1450
1411
  signal: args.signal,
@@ -1605,7 +1566,7 @@ async function runChild(live, executor, childAbort, task, opts, pool, ticket, bl
1605
1566
  } catch (err) {
1606
1567
  reconcileOnce(live.spent);
1607
1568
  await teardownSafe(executor, "brutalKill");
1608
- const aborted = childAbort.signal.aborted || isAbortError2(err);
1569
+ const aborted = childAbort.signal.aborted || isAbortError3(err);
1609
1570
  return downRecord(errMessage(err), aborted || isInfraError(err), executor.metered?.());
1610
1571
  }
1611
1572
  }
@@ -1707,7 +1668,7 @@ function isAgentSpec(value) {
1707
1668
  const v = value;
1708
1669
  return "profile" in v && "harness" in v;
1709
1670
  }
1710
- function isAbortError2(err) {
1671
+ function isAbortError3(err) {
1711
1672
  return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
1712
1673
  }
1713
1674
  function isInfraError(err) {
@@ -1916,11 +1877,11 @@ ${lines.join("\n")}`;
1916
1877
  }
1917
1878
 
1918
1879
  // src/mcp/worktree.ts
1919
- import { spawn as spawn2 } from "child_process";
1880
+ import { spawn } from "child_process";
1920
1881
  async function runGitAsync(args, cwd, runner) {
1921
1882
  if (runner) return runner(args, { cwd });
1922
1883
  return new Promise((resolve, reject) => {
1923
- const proc = spawn2("git", args, { cwd, stdio: "pipe" });
1884
+ const proc = spawn("git", args, { cwd, stdio: "pipe" });
1924
1885
  let stdout = "";
1925
1886
  let stderr = "";
1926
1887
  proc.stdout?.on("data", (c) => {
@@ -2005,7 +1966,7 @@ async function removeWorktree(options) {
2005
1966
  import { randomUUID } from "crypto";
2006
1967
 
2007
1968
  // src/mcp/worktree-harness.ts
2008
- import { spawn as spawn3 } from "child_process";
1969
+ import { spawn as spawn2 } from "child_process";
2009
1970
  var defaultCheckOutputCap = 16e3;
2010
1971
  async function runWorktreeHarness(opts) {
2011
1972
  const runHarness = opts.runHarness ?? runLocalHarness;
@@ -2090,7 +2051,7 @@ async function runChecks(opts) {
2090
2051
  }
2091
2052
  function defaultRunCommand(opts) {
2092
2053
  return new Promise((resolve, reject) => {
2093
- const child = spawn3("/bin/sh", ["-c", opts.command], {
2054
+ const child = spawn2("/bin/sh", ["-c", opts.command], {
2094
2055
  cwd: opts.cwd,
2095
2056
  stdio: ["ignore", "pipe", "pipe"]
2096
2057
  });
@@ -2126,12 +2087,12 @@ function createWorktreeCliExecutor(options) {
2126
2087
  }
2127
2088
  const runId = options.runId ?? randomUUID();
2128
2089
  const controller = new AbortController();
2090
+ const budgetExempt = options.budgetExempt ?? true;
2129
2091
  let run;
2130
2092
  let artifact;
2131
2093
  return {
2132
2094
  runtime: "cli",
2133
- // A harness CLI cannot account tokens — exclude it from the conserved pool + equal-k.
2134
- budgetExempt: true,
2095
+ budgetExempt,
2135
2096
  async execute(_task, signal) {
2136
2097
  const linked = linkSignals(signal, controller.signal);
2137
2098
  const started = Date.now();
@@ -2153,7 +2114,9 @@ function createWorktreeCliExecutor(options) {
2153
2114
  });
2154
2115
  const spent = {
2155
2116
  iterations: 1,
2156
- // budgetExempt: spend is recorded zero (not metered), never a fabricated cost.
2117
+ // The worktree-harness core surfaces no token/usd usage, so tokens/usd are a genuine zero
2118
+ // (NOT a fabricated cost). When budgetExempt is true the pool ignores this spend entirely;
2119
+ // when explicitly false the scope debits exactly this captured spend — the real iteration.
2157
2120
  tokens: zeroTokenUsage(),
2158
2121
  usd: 0,
2159
2122
  ms: Date.now() - started
@@ -2194,7 +2157,7 @@ function linkSignals(a, b) {
2194
2157
  }
2195
2158
 
2196
2159
  // src/runtime/supervise/runtime.ts
2197
- import { spawn as spawn4 } from "child_process";
2160
+ import { spawn as spawn3 } from "child_process";
2198
2161
  import { randomUUID as randomUUID2 } from "crypto";
2199
2162
  import { estimateCost as estimateCost2, isModelPriced as isModelPriced2 } from "@tangle-network/agent-eval";
2200
2163
  var routerSeamKey = "router";
@@ -2585,7 +2548,7 @@ var cliExecutor = (_spec, ctx) => {
2585
2548
  };
2586
2549
  async function* streamCliLeaf(args) {
2587
2550
  const prompt = taskToPrompt(args.task);
2588
- const proc = spawn4(args.seam.bin, args.seam.args ?? [], {
2551
+ const proc = spawn3(args.seam.bin, args.seam.args ?? [], {
2589
2552
  ...args.seam.cwd ? { cwd: args.seam.cwd } : {},
2590
2553
  env: { ...process.env, ...args.seam.env ?? {} },
2591
2554
  stdio: ["pipe", "pipe", "pipe"]
@@ -3769,10 +3732,14 @@ function createCoordinationTools(opts) {
3769
3732
  return q.status !== "answered" && q.status !== "deferred";
3770
3733
  });
3771
3734
  };
3735
+ const maxLiveWorkers = opts.maxLiveWorkers;
3736
+ const liveWorkerCount = () => opts.scope.view.nodes.filter(
3737
+ (n) => n.status !== "done" && n.status !== "failed" && n.status !== "cancelled"
3738
+ ).length;
3772
3739
  const tools = [
3773
3740
  {
3774
3741
  name: "spawn_agent",
3775
- description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed. Pass an optional `budget` (per-field) to give a hard sub-task more than the default \u2014 it merges over the per-worker default; the conserved pool is still the hard fence.",
3742
+ description: 'Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed. Pass an optional `budget` (per-field) to give a hard sub-task more than the default \u2014 it merges over the per-worker default; the conserved pool is still the hard fence. When a max-live-workers cap is set it also fails closed (`error: "max-live-workers"`) while that many workers are still in flight \u2014 settle or steer one before spawning another.',
3776
3743
  inputSchema: {
3777
3744
  type: "object",
3778
3745
  properties: {
@@ -3794,6 +3761,8 @@ function createCoordinationTools(opts) {
3794
3761
  },
3795
3762
  handler: (raw) => {
3796
3763
  const a = obj(raw);
3764
+ if (maxLiveWorkers !== void 0 && maxLiveWorkers > 0 && liveWorkerCount() >= maxLiveWorkers)
3765
+ return Promise.resolve({ error: "max-live-workers" });
3797
3766
  const agent = opts.makeWorkerAgent(a.profile);
3798
3767
  const budget = a.budget === void 0 ? opts.perWorker : mergeBudget(opts.perWorker, a.budget);
3799
3768
  const res = opts.scope.spawn(agent, a.task, {
@@ -4073,7 +4042,8 @@ function driverAgent(opts) {
4073
4042
  scope,
4074
4043
  blobs: opts.blobs,
4075
4044
  makeWorkerAgent: opts.makeWorkerAgent,
4076
- perWorker: opts.perWorker
4045
+ perWorker: opts.perWorker,
4046
+ ...opts.maxLiveWorkers !== void 0 ? { maxLiveWorkers: opts.maxLiveWorkers } : {}
4077
4047
  });
4078
4048
  const byName = new Map(coord.tools.map((t) => [t.name, t]));
4079
4049
  const toolSpecs = [
@@ -5157,6 +5127,7 @@ function supervise(profile, task, opts) {
5157
5127
  blobs,
5158
5128
  makeWorkerAgent,
5159
5129
  perWorker,
5130
+ ...opts.maxLiveWorkers !== void 0 ? { maxLiveWorkers: opts.maxLiveWorkers } : {},
5160
5131
  ...opts.router ? { router: opts.router } : {},
5161
5132
  ...opts.brain ? { brain: opts.brain } : {},
5162
5133
  ...opts.driveHarness ? { driveHarness: opts.driveHarness } : {},
@@ -6036,6 +6007,7 @@ async function serveCoordinationMcp(opts) {
6036
6007
  blobs: opts.blobs,
6037
6008
  makeWorkerAgent: opts.makeWorkerAgent,
6038
6009
  perWorker: opts.perWorker,
6010
+ ...opts.maxLiveWorkers !== void 0 ? { maxLiveWorkers: opts.maxLiveWorkers } : {},
6039
6011
  ...opts.analysts ? { analysts: opts.analysts } : {},
6040
6012
  ...opts.analyzeOnSettle ? { analyzeOnSettle: opts.analyzeOnSettle } : {},
6041
6013
  ...opts.onEvent ? { onEvent: opts.onEvent } : {},
@@ -6112,6 +6084,7 @@ function supervisorAgent(profile, deps) {
6112
6084
  makeWorkerAgent: deps.makeWorkerAgent,
6113
6085
  perWorker: deps.perWorker,
6114
6086
  systemPrompt,
6087
+ ...deps.maxLiveWorkers !== void 0 ? { maxLiveWorkers: deps.maxLiveWorkers } : {},
6115
6088
  ...deps.extraTools ? { extraTools: deps.extraTools } : {},
6116
6089
  ...deps.executeExtraTool ? { executeExtraTool: deps.executeExtraTool } : {},
6117
6090
  ...deps.maxTurns !== void 0 ? { maxTurns: deps.maxTurns } : {}
@@ -6130,7 +6103,8 @@ function supervisorAgent(profile, deps) {
6130
6103
  scope,
6131
6104
  blobs: deps.blobs,
6132
6105
  makeWorkerAgent: deps.makeWorkerAgent,
6133
- perWorker: deps.perWorker
6106
+ perWorker: deps.perWorker,
6107
+ ...deps.maxLiveWorkers !== void 0 ? { maxLiveWorkers: deps.maxLiveWorkers } : {}
6134
6108
  });
6135
6109
  try {
6136
6110
  await driveHarness({ profile, task, scope, coordinationMcpUrl: mcp.url });
@@ -6151,12 +6125,12 @@ function routerBrainFromProfile(profile, deps) {
6151
6125
  }
6152
6126
 
6153
6127
  export {
6154
- runLocalHarness,
6155
6128
  assertModelAllowed,
6156
6129
  deleteBoxSafe,
6157
6130
  randomSuffix,
6158
6131
  throwAbort,
6159
6132
  throwIfAborted,
6133
+ isAbortError,
6160
6134
  sleep,
6161
6135
  stringifySafe,
6162
6136
  zeroTokenUsage,
@@ -6253,4 +6227,4 @@ export {
6253
6227
  createInProcessTransport,
6254
6228
  serveCoordinationMcp
6255
6229
  };
6256
- //# sourceMappingURL=chunk-55WKGOGU.js.map
6230
+ //# sourceMappingURL=chunk-ZKMOIEOB.js.map