@tangle-network/agent-runtime 0.116.0 → 0.117.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 (81) hide show
  1. package/README.md +7 -1
  2. package/dist/{activation-DuqQhee6.js → activation-BZ2FyX0O.js} +3 -3
  3. package/dist/{activation-DuqQhee6.js.map → activation-BZ2FyX0O.js.map} +1 -1
  4. package/dist/agent.d.ts +3 -64
  5. package/dist/agent.js +5 -207
  6. package/dist/agent.js.map +1 -1
  7. package/dist/{analyst-loop-BoNIG2hA.js → analyst-loop-DvSciOfB.js} +2 -2
  8. package/dist/{analyst-loop-BoNIG2hA.js.map → analyst-loop-DvSciOfB.js.map} +1 -1
  9. package/dist/analyst-loop.js +1 -1
  10. package/dist/candidate-execution/index.d.ts +3 -3
  11. package/dist/candidate-execution/index.js +5 -5
  12. package/dist/{candidate-execution-CfpJrd3o.js → candidate-execution-PX61zXd2.js} +4 -4
  13. package/dist/{candidate-execution-CfpJrd3o.js.map → candidate-execution-PX61zXd2.js.map} +1 -1
  14. package/dist/{environment-provider-CWsRh6Uz.d.ts → environment-provider-CTZjdIUg.d.ts} +298 -32
  15. package/dist/{environment-provider-CCaEhA-l.js → environment-provider-CzcEyRL7.js} +140 -31
  16. package/dist/environment-provider-CzcEyRL7.js.map +1 -0
  17. package/dist/environment-provider.d.ts +1 -1
  18. package/dist/environment-provider.js +1 -1
  19. package/dist/{improvement-cycle-C1cmjvPD.js → improvement-cycle-Bcqz8rZI.js} +160 -19
  20. package/dist/improvement-cycle-Bcqz8rZI.js.map +1 -0
  21. package/dist/{index-CYkDeM5L.d.ts → index-BHSYaWrC.d.ts} +3 -3
  22. package/dist/{index-DcLMNnG5.d.ts → index-BTUG4Xln.d.ts} +6 -6
  23. package/dist/{index-COumPQka.d.ts → index-DPWJxSRa.d.ts} +847 -275
  24. package/dist/{index-C-FYUuFG.d.ts → index-X2YwyWkc.d.ts} +2 -2
  25. package/dist/index.d.ts +8 -8
  26. package/dist/index.js +12 -12
  27. package/dist/intelligence.d.ts +8 -7
  28. package/dist/intelligence.js +15 -9
  29. package/dist/intelligence.js.map +1 -1
  30. package/dist/kernel.d.ts +3 -3
  31. package/dist/kernel.js +8 -8
  32. package/dist/{knowledge-DOzbywZT.js → knowledge-CDlrayRc.js} +7 -7
  33. package/dist/knowledge-CDlrayRc.js.map +1 -0
  34. package/dist/knowledge.d.ts +1 -1
  35. package/dist/knowledge.js +1 -1
  36. package/dist/{loop-runner-bin-BuQjc5DR.js → loop-runner-bin-BxhhDtmG.js} +4 -4
  37. package/dist/{loop-runner-bin-BuQjc5DR.js.map → loop-runner-bin-BxhhDtmG.js.map} +1 -1
  38. package/dist/{loop-runner-bin-BFrhPLKt.d.ts → loop-runner-bin-oyL7it5O.d.ts} +3 -3
  39. package/dist/loop-runner-bin.d.ts +1 -1
  40. package/dist/loop-runner-bin.js +1 -1
  41. package/dist/mcp/bin.js +43 -10
  42. package/dist/mcp/bin.js.map +1 -1
  43. package/dist/mcp/index.d.ts +3 -43
  44. package/dist/mcp/index.js +5 -204
  45. package/dist/mcp/index.js.map +1 -1
  46. package/dist/{openai-tools-_Wyp4udO.js → openai-tools-CO9X36zy.js} +2 -2
  47. package/dist/{openai-tools-_Wyp4udO.js.map → openai-tools-CO9X36zy.js.map} +1 -1
  48. package/dist/{prepare-BHQBb02e.js → prepare-BbI6qGOW.js} +5 -2
  49. package/dist/prepare-BbI6qGOW.js.map +1 -0
  50. package/dist/primeintellect/index.d.ts +1 -1
  51. package/dist/{protected-model-port-BP6Z4eau.d.ts → protected-model-port-B0yXMpLL.d.ts} +10 -2
  52. package/dist/{protected-model-port-DqAH1Z2M.js → protected-model-port-COnyZfML.js} +2 -2
  53. package/dist/{protected-model-port-DqAH1Z2M.js.map → protected-model-port-COnyZfML.js.map} +1 -1
  54. package/dist/{runtime-Ut1pkd2n.js → runtime-Ce39zfqn.js} +145 -53
  55. package/dist/runtime-Ce39zfqn.js.map +1 -0
  56. package/dist/{sandbox-events-DeI5xX8P.js → sandbox-events-Yhd1GYWl.js} +4 -2
  57. package/dist/sandbox-events-Yhd1GYWl.js.map +1 -0
  58. package/dist/spawn-journal-B_mu_oH2.js +857 -0
  59. package/dist/spawn-journal-B_mu_oH2.js.map +1 -0
  60. package/dist/{structural-rollout-CVY_0hJp.js → structural-rollout-DLwW69R4.js} +4 -4
  61. package/dist/structural-rollout-DLwW69R4.js.map +1 -0
  62. package/dist/{supervise-BUR9ByF7.js → supervise-U7_K5yuV.js} +2598 -821
  63. package/dist/supervise-U7_K5yuV.js.map +1 -0
  64. package/dist/{supervisor-BBbPBXpe.js → supervisor-CAXiCKnX.js} +2808 -973
  65. package/dist/supervisor-CAXiCKnX.js.map +1 -0
  66. package/dist/testing.js +98 -76
  67. package/dist/testing.js.map +1 -1
  68. package/dist/{workspace-archive-DXzJq7WP.js → workspace-archive-CqPIijFA.js} +2 -2
  69. package/dist/{workspace-archive-DXzJq7WP.js.map → workspace-archive-CqPIijFA.js.map} +1 -1
  70. package/package.json +1 -1
  71. package/dist/environment-provider-CCaEhA-l.js.map +0 -1
  72. package/dist/improvement-cycle-C1cmjvPD.js.map +0 -1
  73. package/dist/knowledge-DOzbywZT.js.map +0 -1
  74. package/dist/prepare-BHQBb02e.js.map +0 -1
  75. package/dist/runtime-Ut1pkd2n.js.map +0 -1
  76. package/dist/sandbox-events-DeI5xX8P.js.map +0 -1
  77. package/dist/spawn-journal-DCPbicXB.js +0 -457
  78. package/dist/spawn-journal-DCPbicXB.js.map +0 -1
  79. package/dist/structural-rollout-CVY_0hJp.js.map +0 -1
  80. package/dist/supervise-BUR9ByF7.js.map +0 -1
  81. package/dist/supervisor-BBbPBXpe.js.map +0 -1
@@ -1,18 +1,18 @@
1
1
  import { c as RuntimeRunStateError, i as ConfigError, o as NotFoundError, t as AgentEvalError$1, u as ValidationError } from "./errors-DEAvWQPy.js";
2
+ import { S as detachedSnapshot, b as workerTraceAnalysisStore, d as writeAllBytes, i as InMemorySpawnJournal, l as parseCommittedJsonLines, n as FileSpawnJournal, r as InMemoryResultBlobStore, t as FileResultBlobStore, u as prepareJsonlAppend, x as contentAddress } from "./spawn-journal-B_mu_oH2.js";
2
3
  import { a as mapWithConcurrency, c as sleep, d as throwIfAborted, f as withTimeout, l as stringifySafe, n as addTokenUsage, o as randomSuffix, p as zeroTokenUsage, r as deleteBoxSafe, s as randomUuid, t as abortError, u as throwAbort } from "./util-Cc9g9Y-o.js";
3
- import { a as contentAddress, i as InMemorySpawnJournal, n as FileSpawnJournal, r as InMemoryResultBlobStore, t as FileResultBlobStore } from "./spawn-journal-DCPbicXB.js";
4
- import { $ as removeWorktree, B as createActivityLog, D as toOtelAttributes, H as routerBrain, Q as createWorktree, S as createOtelExporter, T as generateSpanId, W as routerChatWithUsage, X as runWorktreeHarness, Y as runWorktreeChecks, Z as captureWorktreeDiff, a as runFinalizer, b as buildLoopSpanNodes, i as pickBestDelivered, l as withDriverExecutor, m as workerTraceSeamKey, n as bestDelivered, o as runTree, p as workerTraceEnv, q as runBrainLoop, t as createSupervisor } from "./supervisor-BBbPBXpe.js";
4
+ import { C as buildLoopSpanNodes, Ct as controlProfileMaterialization, Dt as promptControlProfileMaterialization, E as createOtelExporter, Et as profileMaterializationAxes, Ot as promptModelProfileMaterialization, Pt as worktreeCliProfileMaterialization, Q as assertValidBudget, St as assertProfileMaterialization, Tt as fullProfileMaterialization, W as createActivityLog, X as DEFAULT_SUCCESSFUL_SHUTDOWN_MS, Z as teardownExecutor, _t as worktreeProfileExecutionPlan, a as pickBestDelivered, at as runtimeOwnedExecutorExecutionBinding, bt as removeWorktree, c as driverChild, ct as routerBrain, d as deriveNodeExecutionIdentity, et as spendFromUsageEvents, f as recordScopeOwnerMaterialization, g as workerTraceEnv, gt as runWorktreeHarness, ht as runWorktreeChecks, it as newExecutionAttemptId, j as toOtelAttributes, k as generateSpanId, l as withDriverExecutor, n as createSupervisor, nt as attestRuntimeOwnedScopeOwner, o as runFinalizer, ot as runtimeOwnedExecutorMaterialization, p as scopeOwnerExecutorNodeContext, pt as runBrainLoop, q as freeSlots, r as bestDelivered, rt as inheritRuntimeOwnedExecutorAttestation, s as runTree, st as runtimeOwnedScopeOwnerRuntime, tt as attestRuntimeOwnedExecutor, ut as routerChatWithUsage, vt as captureWorktreeDiff, wt as defineProfileMaterializationContract, yt as createWorktree } from "./supervisor-CAXiCKnX.js";
5
5
  import { i as notifyRuntimeHookEvent, t as composeRuntimeHooks } from "./runtime-hooks-C7iJOWm3.js";
6
- import { a as notifySandboxEventObserver, n as extractLlmCallEvent } from "./sandbox-events-DeI5xX8P.js";
7
- import { i as resolveAgentEnvironmentProvider, n as providerAsExecutor, s as buildBackendOptions } from "./environment-provider-CCaEhA-l.js";
6
+ import { a as notifySandboxEventObserver, n as extractLlmCallEvent } from "./sandbox-events-Yhd1GYWl.js";
7
+ import { i as resolveAgentEnvironmentProvider, n as providerAsExecutor, r as providerAsSandboxClient, s as buildBackendOptions } from "./environment-provider-CzcEyRL7.js";
8
8
  import { t as createStdioToolServer } from "./tool-server-RcWgLIsL.js";
9
9
  import { n as UI_LENSES } from "./substrate-B0TYNrXn.js";
10
10
  import { argHash, computeFindingId, errorStreakDetector, estimateCost, isModelPriced, makeFinding, observeAll, repeatedActionDetector } from "@tangle-network/agent-eval";
11
- import { agentProfileSchema } from "@tangle-network/agent-interface";
11
+ import { agentProfileSchema, canonicalCandidateDigest, mergeAgentProfiles, validateAgentProfileSecurity } from "@tangle-network/agent-interface";
12
12
  import { randomUUID } from "node:crypto";
13
13
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
14
14
  import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
15
- import path, { dirname, isAbsolute, join, sep } from "node:path";
15
+ import path, { dirname, isAbsolute, join, resolve, sep } from "node:path";
16
16
  import { spawn } from "node:child_process";
17
17
  import { homedir, tmpdir } from "node:os";
18
18
  import { createServer, request } from "node:http";
@@ -20,6 +20,28 @@ import { request as request$1 } from "node:https";
20
20
  import { Readable, Writable } from "node:stream";
21
21
  //#region src/runtime/supervise/completion-gate.ts
22
22
  /**
23
+ *
24
+ * The completion-oracle: **settled ⟺ DELIVERED.**
25
+ *
26
+ * Foreman's one hard lesson (0/18 self-improvement deliverables) — "done" must mean a check
27
+ * PASSED, not the agent's say-so. `gateOnDeliverable` wraps an `Executor` so its settlement
28
+ * is `valid` ONLY when the deliverable check passes. The child still RUNS and settles (its
29
+ * spend is conserved into the pool either way), but a child that ran WITHOUT delivering
30
+ * settles `valid:false` — so a keep-best driver never counts it as done, and a gate never
31
+ * inflates with self-judged wins.
32
+ *
33
+ * Dual-purpose by construction:
34
+ * - product: the agent fleet only advances on real, checked deliverables.
35
+ * - proof: the gate's `valid` is the honest settle — equal-k comparisons can't be gamed by an
36
+ * arm that "ran" without producing the artifact.
37
+ *
38
+ * The check is a DEPLOYABLE oracle — a test command, a state verifier, the commit0 judge —
39
+ * read off the child's output, never the model judging itself. A throwing check is
40
+ * fail-closed (not delivered), never a crash.
41
+ *
42
+ * @experimental
43
+ */
44
+ /**
23
45
  * Wrap an `Executor` so its settlement `valid` reflects the deliverable check, not the
24
46
  * inner verdict. Handles both `execute` shapes (one-shot `Promise<ExecutorResult>` and
25
47
  * streaming `AsyncIterable<UsageEvent>` + `resultArtifact()`); the check runs once the inner
@@ -39,7 +61,22 @@ function gateOnDeliverable(inner, deliverable) {
39
61
  score: baseScore ?? (delivered ? 1 : 0)
40
62
  };
41
63
  };
42
- return {
64
+ /**
65
+ * Ask the delivery question once, from whatever the inner executor managed to produce.
66
+ *
67
+ * Fail-closed on the artifact being unavailable: an executor that never produced one delivered
68
+ * nothing, and leaving `gated` unset keeps the existing invalid-by-default reading.
69
+ */
70
+ const settleVerdict = async () => {
71
+ let art;
72
+ try {
73
+ art = inner.resultArtifact();
74
+ } catch {
75
+ return;
76
+ }
77
+ gated = await check(art.out, art.verdict?.score);
78
+ };
79
+ return inheritRuntimeOwnedExecutorAttestation(inner, {
43
80
  runtime: inner.runtime,
44
81
  ...inner.budgetExempt !== void 0 ? { budgetExempt: inner.budgetExempt } : {},
45
82
  ...inner.deliver ? { deliver: (m) => inner.deliver?.(m) } : {},
@@ -48,13 +85,21 @@ function gateOnDeliverable(inner, deliverable) {
48
85
  ...inner.metered ? { metered: () => inner.metered?.() } : {},
49
86
  execute(task, signal) {
50
87
  const r = inner.execute(task, signal);
51
- if (isAsyncIterable$1(r)) return (async function* () {
52
- for await (const ev of r) yield ev;
53
- const art = inner.resultArtifact();
54
- gated = await check(art.out, art.verdict?.score);
88
+ if (isAsyncIterable$2(r)) return (async function* () {
89
+ try {
90
+ for await (const ev of r) yield ev;
91
+ } finally {
92
+ await settleVerdict();
93
+ }
55
94
  })();
56
95
  return (async () => {
57
- const res = await r;
96
+ let res;
97
+ try {
98
+ res = await r;
99
+ } catch (error) {
100
+ await settleVerdict();
101
+ throw error;
102
+ }
58
103
  gated = await check(res.out, res.verdict?.score);
59
104
  return {
60
105
  ...res,
@@ -70,19 +115,281 @@ function gateOnDeliverable(inner, deliverable) {
70
115
  verdict: gated ?? art.verdict
71
116
  };
72
117
  }
73
- };
118
+ });
74
119
  }
75
- function isAsyncIterable$1(v) {
120
+ function isAsyncIterable$2(v) {
76
121
  return v != null && typeof v[Symbol.asyncIterator] === "function";
77
122
  }
78
123
  //#endregion
79
- //#region src/runtime/supervise/model-policy.ts
124
+ //#region src/runtime/supervise/authoring.ts
125
+ /**
126
+ *
127
+ * The supervisor's intelligence is AUTHORING the agents it spawns — not pressing buttons.
128
+ *
129
+ * Every agent here is three things: instructions (system prompt), tools, and a model — its
130
+ * `AgentProfile`. The supervisor's job is to WRITE those profiles: read the task, decompose it,
131
+ * and for each sub-task author a tailored worker recipe. `supervisorInstructions` is the how-to the
132
+ * supervisor reads (its system prompt); `authoredWorker` builds a worker AGENT from a profile the
133
+ * supervisor authored — the authored systemPrompt + model shape the worker's call.
134
+ *
135
+ * The skill is the single OPTIMIZABLE surface: edit it → the supervisor designs better agents.
136
+ * That is the self-improvement lever (the prompt/skill lever), not the execution plumbing.
137
+ *
138
+ * @experimental
139
+ */
140
+ /** Narrow an untyped `spawn_agent` profile argument to an `AuthoredProfile`, or null if the
141
+ * supervisor failed to author one (empty/placeholder profile — a skill violation worth catching). */
142
+ function asAuthoredProfile(raw) {
143
+ const parsed = agentProfileSchema.safeParse(raw);
144
+ if (!parsed.success) return null;
145
+ const systemPrompt = parsed.data.prompt?.systemPrompt;
146
+ if (typeof systemPrompt !== "string" || systemPrompt.trim().length === 0) return null;
147
+ return {
148
+ ...parsed.data,
149
+ name: typeof parsed.data.name === "string" && parsed.data.name.length > 0 ? parsed.data.name : "worker",
150
+ prompt: {
151
+ ...parsed.data.prompt,
152
+ systemPrompt
153
+ }
154
+ };
155
+ }
80
156
  /**
81
- * `assertModelAllowed` a fail-loud guard that restricts a run to a chosen subset of
82
- * models. The two front doors (`supervise()` / `improve()`) call it once per configured
83
- * model at resolve time, so a run that names a model outside the allowed set throws before
84
- * any compute is spent never silently swapped or silently allowed.
157
+ * Lift a profile the supervisor AUTHORED into the canonical shape every executor reads.
158
+ *
159
+ * The skill asks for `systemPrompt` and `model` as flat fields the vocabulary a model writes
160
+ * well while `AgentProfile` carries them as `prompt.systemPrompt` and `model.default`. Nothing
161
+ * downstream reads the flat form: the router and cli-bridge leaves read `profile.prompt
162
+ * .systemPrompt`, and the sandbox leaf hands the profile to a strict schema that REJECTS the flat
163
+ * key outright (`Unrecognized key: "systemPrompt"`), which fails the worker's every round. Lift
164
+ * both here, once, so what the supervisor writes is what the worker runs.
165
+ *
166
+ * Purely additive: a profile already canonical is returned untouched, and a flat field is dropped
167
+ * only after its canonical slot is filled. Both spellings of the same standing instruction, set to
168
+ * DIFFERENT text, is a contradiction with no safe reading — it fails loud, matching
169
+ * `resolveSupervisorProfile`'s rule for the supervisor's own profile.
85
170
  */
171
+ function canonicalizeAuthoredProfile(raw) {
172
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw ?? {};
173
+ const authored = { ...raw };
174
+ const flatPrompt = authored.systemPrompt;
175
+ if (typeof flatPrompt === "string" && flatPrompt.trim().length > 0) {
176
+ const prompt = authored.prompt && typeof authored.prompt === "object" && !Array.isArray(authored.prompt) ? { ...authored.prompt } : {};
177
+ const canonicalPrompt = prompt.systemPrompt;
178
+ if (typeof canonicalPrompt === "string" && canonicalPrompt !== flatPrompt) throw new ValidationError(`canonicalizeAuthoredProfile: prompt.systemPrompt and systemPrompt are both set and differ — they are the same standing instruction, so author exactly one (prompt.systemPrompt: ${JSON.stringify(canonicalPrompt.slice(0, 80))}; systemPrompt: ${JSON.stringify(flatPrompt.slice(0, 80))})`);
179
+ if (typeof canonicalPrompt !== "string") prompt.systemPrompt = flatPrompt;
180
+ authored.prompt = prompt;
181
+ delete authored.systemPrompt;
182
+ }
183
+ if (typeof authored.model === "string" && authored.model.trim().length > 0) authored.model = { default: authored.model };
184
+ return authored;
185
+ }
186
+ /** The supervisor SKILL — the how-to the supervisor reads (its system prompt). THE optimizable
187
+ * surface: editing this changes how the supervisor designs every agent it spawns. */
188
+ function supervisorInstructions(opts) {
189
+ return [
190
+ "You are a SUPERVISOR. You do NOT do the work yourself — your job is to DESIGN and DRIVE specialist worker agents.",
191
+ "",
192
+ "For the task you are given:",
193
+ "1. DECOMPOSE it into the smallest set of sub-tasks a single focused worker can each deliver.",
194
+ "2. For EACH sub-task, AUTHOR a worker by calling spawn_agent with a COMPLETE `profile`:",
195
+ " • name and description: who this specialist is and why it exists.",
196
+ " • prompt.systemPrompt: rich instructions for THIS sub-task — exact output, process, evidence, and what \"done\" means.",
197
+ " • model.default, model.reasoningEffort, and harness: choose the execution system deliberately when the task benefits from it.",
198
+ " • tools, mcp, resources.skills/files/instructions, hooks, subagents, permissions, and modes: grant or attach every capability the worker needs; omit an axis only when it is intentionally unnecessary.",
199
+ " • metadata.role=\"driver\" when this child should be a sub-supervisor that may author and drive its own children.",
200
+ " NEVER spawn a worker with an empty profile. The quality of the worker IS the quality of the profile you write.",
201
+ "3. await_event (kinds:['settled']) to collect each worker. Its result says valid:true only if the deployable check passed.",
202
+ "4. If a worker did NOT deliver, AUTHOR A NEW profile whose prompt.systemPrompt names the SPECIFIC failure and how to fix it — never just retry the same profile.",
203
+ "5. Stop (reply with no tool call) once the work is delivered. You cannot declare done yourself — only a delivered (valid:true) worker counts.",
204
+ ...opts?.goal ? ["", `The goal: ${opts.goal}`] : []
205
+ ].join("\n");
206
+ }
207
+ /** Build a router-only worker from an authored profile. This helper executes the prompt/model axes;
208
+ * use `workerFromBackend` for full materialization of tools, MCP, resources, hooks, and subagents. */
209
+ function authoredWorker(profile, opts) {
210
+ const model = profile.model?.default ?? opts.cfg.model;
211
+ const executorFactory = (spec, ctx) => {
212
+ let artifact;
213
+ const executionId = ctx.node?.nodeId ?? `authored-router-${profile.name}`;
214
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(executionId);
215
+ return gateOnDeliverable(attestRuntimeOwnedExecutor({
216
+ runtime: "router",
217
+ async execute(_t, signal) {
218
+ const res = await routerChatWithUsage({
219
+ ...opts.cfg,
220
+ model
221
+ }, [{
222
+ role: "system",
223
+ content: profile.prompt.systemPrompt
224
+ }, {
225
+ role: "user",
226
+ content: opts.taskPrompt
227
+ }], {
228
+ temperature: opts.temperature ?? .4,
229
+ ...signal ? { signal } : {}
230
+ });
231
+ artifact = {
232
+ outRef: contentAddress(res.content),
233
+ out: res.content,
234
+ spent: {
235
+ iterations: 1,
236
+ tokens: res.usage ?? {
237
+ input: 0,
238
+ output: 0
239
+ },
240
+ usd: res.costUsd ?? 0,
241
+ ms: 0
242
+ }
243
+ };
244
+ return artifact;
245
+ },
246
+ teardown: () => Promise.resolve({ destroyed: true }),
247
+ resultArtifact: () => {
248
+ if (!artifact) throw new Error("authoredWorker: resultArtifact read before execute");
249
+ return artifact;
250
+ }
251
+ }, {
252
+ effectiveProfile: spec.profile,
253
+ backend: "router",
254
+ model: {
255
+ status: "known",
256
+ id: model
257
+ },
258
+ execution: {
259
+ kind: "request",
260
+ id: executionId
261
+ },
262
+ materializer: "authored-router-prompt",
263
+ plan: {
264
+ kind: "authored-router-completion",
265
+ model,
266
+ temperature: opts.temperature ?? .4,
267
+ taskPrompt: opts.taskPrompt
268
+ }
269
+ }, {
270
+ attemptId,
271
+ binding: {
272
+ endpoint: opts.cfg.routerBaseUrl,
273
+ executionId,
274
+ model
275
+ },
276
+ descriptor: {
277
+ kind: "router-request",
278
+ transport: "http",
279
+ backend: "router"
280
+ }
281
+ }), opts.deliverable);
282
+ };
283
+ const spec = {
284
+ profile,
285
+ harness: null,
286
+ executorFactory
287
+ };
288
+ return {
289
+ name: profile.name,
290
+ act: async () => "",
291
+ executorSpec: spec
292
+ };
293
+ }
294
+ /** Default thresholds for `ProfileRichnessThresholds` — 600 chars / 6 lines minimum system prompt. */
295
+ const defaultProfileRichnessThresholds = {
296
+ minSystemPromptChars: 600,
297
+ minSystemPromptLines: 6
298
+ };
299
+ /** Read the system prompt from any authored shape: canonical `prompt.systemPrompt`, the sandbox
300
+ * `prompt.system` convention, or a bare-string `prompt`. */
301
+ function resolveSystemPrompt(profile) {
302
+ const pr = profile.prompt;
303
+ if (typeof pr === "string") return pr;
304
+ if (pr && typeof pr === "object") {
305
+ const o = pr;
306
+ if (typeof o.systemPrompt === "string") return o.systemPrompt;
307
+ if (typeof o.system === "string") return o.system;
308
+ }
309
+ return "";
310
+ }
311
+ /** OBSERVE one authored `AgentProfile` and score its richness (no judge verdict is read). The task
312
+ * context (`needsMcp`) lets a domain say "this work needs a data/tool MCP" so a missing MCP counts. */
313
+ function assessAuthoredProfile(profile, opts) {
314
+ const th = {
315
+ ...defaultProfileRichnessThresholds,
316
+ ...opts?.thresholds ?? {}
317
+ };
318
+ const systemPrompt = resolveSystemPrompt(profile);
319
+ const trimmed = systemPrompt.trim();
320
+ const systemPromptChars = trimmed.length;
321
+ const systemPromptLines = trimmed ? trimmed.split("\n").filter((l) => l.trim().length > 0).length : 0;
322
+ const sentenceCount = trimmed ? (trimmed.match(/[.!?](\s|$)/g) ?? []).length || (trimmed ? 1 : 0) : 0;
323
+ const hasDescription = typeof profile.description === "string" && profile.description.trim().length > 0;
324
+ const tools = profile.tools;
325
+ const hasTools = !!tools && Object.keys(tools).length > 0;
326
+ const skills = profile.resources?.skills;
327
+ const hasSkills = Array.isArray(skills) && skills.length > 0;
328
+ const mcp = profile.mcp;
329
+ const hasMcp = !!mcp && Object.keys(mcp).length > 0;
330
+ const subagents = profile.subagents;
331
+ const hasSubagents = !!subagents && Object.keys(subagents).length > 0;
332
+ const reasons = [];
333
+ const promptThin = systemPromptChars < th.minSystemPromptChars || systemPromptLines < th.minSystemPromptLines;
334
+ if (promptThin) reasons.push(`system prompt is thin (${systemPromptChars} chars, ${systemPromptLines} lines; need ≥${th.minSystemPromptChars} chars and ≥${th.minSystemPromptLines} lines)`);
335
+ if (!hasTools) reasons.push("no tools granted (a worker can only act through the tools you grant it)");
336
+ if (!hasSkills) reasons.push("no skills attached (no reusable how-to notes injected)");
337
+ if (opts?.needsMcp && !hasMcp) reasons.push("no MCP server, but the task needs data/tool access");
338
+ const signals = [
339
+ !promptThin,
340
+ hasTools,
341
+ hasSkills,
342
+ hasDescription,
343
+ opts?.needsMcp ? hasMcp : true
344
+ ];
345
+ const richness = signals.filter(Boolean).length / signals.length;
346
+ const thin = promptThin || !hasTools && !hasSkills && !hasMcp;
347
+ return {
348
+ name: profile.name ?? "worker",
349
+ systemPrompt,
350
+ systemPromptChars,
351
+ systemPromptLines,
352
+ sentenceCount,
353
+ hasDescription,
354
+ hasTools,
355
+ hasSkills,
356
+ hasMcp,
357
+ hasSubagents,
358
+ richness,
359
+ thin,
360
+ reasons
361
+ };
362
+ }
363
+ /** Turn a {@link ProfileRichness} verdict into a bus-routable `AnalystFinding` (area `profile-quality`).
364
+ * Severity scales with thinness; the recommended action names the MISSING lever so the supervisor can
365
+ * re-author. `subject` = the worker name so per-worker findings diff cleanly across re-authors. */
366
+ function profileRichnessFinding(richness, opts) {
367
+ const analyst_id = opts?.analystId ?? "profile-richness";
368
+ const subject = richness.name;
369
+ const claim = richness.thin ? `Worker "${richness.name}" was authored as a THIN profile: ${richness.reasons.join("; ")}.` : `Worker "${richness.name}" was authored as a rich profile (richness ${(richness.richness * 100).toFixed(0)}%).`;
370
+ return makeFinding({
371
+ analyst_id,
372
+ severity: richness.thin ? richness.richness < .25 ? "high" : "medium" : "info",
373
+ area: "profile-quality",
374
+ claim,
375
+ subject,
376
+ confidence: .9,
377
+ evidence_refs: [{
378
+ kind: "metric",
379
+ uri: `profile:${subject}`,
380
+ excerpt: `chars=${richness.systemPromptChars} lines=${richness.systemPromptLines} tools=${richness.hasTools} skills=${richness.hasSkills} mcp=${richness.hasMcp} richness=${richness.richness.toFixed(2)}`
381
+ }],
382
+ ...richness.thin ? { recommended_action: `Re-author "${richness.name}" with: ${richness.reasons.join("; ")}.` } : {},
383
+ id_basis: computeFindingId({
384
+ analyst_id,
385
+ area: "profile-quality",
386
+ subject,
387
+ claim: `richness:${richness.thin ? "thin" : "rich"}`
388
+ })
389
+ });
390
+ }
391
+ //#endregion
392
+ //#region src/runtime/supervise/model-policy.ts
86
393
  /**
87
394
  * Throw a `ConfigError` when `allowed` is set, `model` is defined, and `model` is not a
88
395
  * member of `allowed`. No-op when `allowed` is unset (the unrestricted default) or when
@@ -92,6 +399,14 @@ function assertModelAllowed(model, allowed) {
92
399
  if (!allowed || model === void 0) return;
93
400
  if (!allowed.includes(model)) throw new ConfigError(`model ${JSON.stringify(model)} is not in the allowed set ${JSON.stringify([...allowed])}`);
94
401
  }
402
+ /** Check every canonical model-bearing field in a complete profile, including the models a
403
+ * backend may select for cheap work, named subagents, or modes. */
404
+ function assertProfileModelsAllowed(profile, allowed) {
405
+ assertModelAllowed(profile.model?.default, allowed);
406
+ assertModelAllowed(profile.model?.small, allowed);
407
+ for (const subagent of Object.values(profile.subagents ?? {})) assertModelAllowed(subagent.model, allowed);
408
+ for (const mode of Object.values(profile.modes ?? {})) assertModelAllowed(mode.model, allowed);
409
+ }
95
410
  //#endregion
96
411
  //#region src/runtime/supervise/otel-spans.ts
97
412
  /**
@@ -402,59 +717,96 @@ function truncate(value) {
402
717
  }
403
718
  //#endregion
404
719
  //#region src/runtime/supervise/coordination-log.ts
405
- /** Should this bus event be persisted? Questions and findings ARE the prior context a resumed
406
- * driver needs; answers fold their status; everything else has a better ledger or none. */
720
+ /**
721
+ * Durable side-log for coordination evidence the spawn journal does not own: questions, analyst
722
+ * findings, answer decisions, authorized continuation receipts, delivery-attempt markers, and
723
+ * delivery outcomes. A durable run
724
+ * (`supervise({ runDir })`) appends them as they publish and loads them on resume, so a restarted
725
+ * coordinator retains the exact evidence produced by prior processes.
726
+ *
727
+ * Answer down-events also fold status on load: a question answered before the crash reloads as
728
+ * `answered`, not as a re-blocking `open`. Settled events are skipped (the spawn journal is their
729
+ * ledger). A receipt followed by an attempt but no outcome proves the process died in the delivery
730
+ * window; that outcome remains unknown and no prior instruction is auto-delivered.
731
+ *
732
+ * JSONL, one fsynced record per event, keyed by `runId` — several runs may share one log file
733
+ * exactly as they share one spawn-journal file.
734
+ *
735
+ * @experimental
736
+ */
737
+ /** Persist prior context plus exact continuation authorization, attempt, and result evidence.
738
+ * Settlements have their own journal. */
407
739
  function persisted(event) {
408
- return event.type === "question" || event.type === "finding" || event.type === "answer";
740
+ return event.type !== "settled";
409
741
  }
410
742
  /** FS-backed `CoordinationLog`: append-only JSONL, fsynced per record. */
411
743
  var FileCoordinationLog = class {
412
744
  path;
745
+ appendTail = Promise.resolve();
413
746
  constructor(path) {
414
747
  this.path = path;
415
748
  }
416
- async append(runId, event, at) {
417
- if (!persisted(event)) return;
749
+ async append(runId, record, ownerId) {
750
+ if (!persisted(record.event)) return;
751
+ const append = this.appendTail.then(() => this.appendRecord(runId, record, ownerId));
752
+ this.appendTail = append.catch(() => void 0);
753
+ return append;
754
+ }
755
+ async appendRecord(runId, busRecord, ownerId) {
418
756
  const fs = await import("node:fs/promises");
419
757
  const path = await import("node:path");
420
758
  await fs.mkdir(path.dirname(this.path), { recursive: true });
421
759
  const record = {
422
760
  runId,
423
- at,
424
- event
761
+ ...ownerId !== void 0 ? { ownerId } : {},
762
+ ...busRecord
425
763
  };
764
+ const needsSeparator = await prepareJsonlAppend(this.path);
426
765
  const fh = await fs.open(this.path, "a");
427
766
  try {
428
- await fh.write(`${JSON.stringify(record)}\n`);
767
+ await writeAllBytes(fh, `${needsSeparator ? "\n" : ""}${JSON.stringify(record)}\n`);
429
768
  await fh.sync();
430
769
  } finally {
431
770
  await fh.close();
432
771
  }
433
772
  }
434
- async load(runId) {
773
+ async load(runId, ownerId) {
435
774
  const fs = await import("node:fs/promises");
436
775
  let text;
437
776
  try {
438
777
  text = await fs.readFile(this.path, "utf8");
439
778
  } catch (err) {
440
- if (isNoEntError(err)) return {
441
- questions: [],
442
- findings: []
443
- };
779
+ if (isNoEntError(err)) return emptyPriorCoordination(ownerId);
444
780
  throw err;
445
781
  }
446
782
  const byId = /* @__PURE__ */ new Map();
447
783
  const findings = [];
448
- for (const line of text.split("\n")) {
449
- if (line.length === 0) continue;
450
- const record = JSON.parse(line);
451
- if (record.runId !== runId) continue;
784
+ const continuations = [];
785
+ const deliveryEvidence = [];
786
+ const records = [];
787
+ let legacySeq = 0;
788
+ for (const stored of parseCommittedJsonLines(text, this.path)) {
789
+ if (stored.runId !== runId) continue;
790
+ if (ownerId !== void 0 && stored.ownerId !== ownerId) continue;
791
+ const record = "seq" in stored ? {
792
+ seq: stored.seq,
793
+ at: stored.at,
794
+ priority: stored.priority,
795
+ event: stored.event
796
+ } : {
797
+ seq: legacySeq++,
798
+ at: Date.parse(stored.at),
799
+ priority: 0,
800
+ event: stored.event
801
+ };
802
+ records.push(record);
452
803
  const ev = record.event;
804
+ if (ev.type === "delivery-attempt" || ev.type === "steer" || ev.type === "answer") deliveryEvidence.push(ev);
453
805
  if (ev.type === "question") byId.set(ev.question.id, ev.question);
454
806
  else if (ev.type === "finding") findings.push(ev.finding);
455
807
  else if (ev.type === "answer") {
456
808
  const prior = byId.get(ev.questionId);
457
- if (prior) byId.set(ev.questionId, {
809
+ if (prior && ev.down.delivered) byId.set(ev.questionId, {
458
810
  ...prior,
459
811
  status: "answered",
460
812
  decision: {
@@ -463,14 +815,28 @@ var FileCoordinationLog = class {
463
815
  by: "prior-run"
464
816
  }
465
817
  });
466
- }
818
+ } else if (ev.type === "instruction") continuations.push(ev.instruction);
467
819
  }
468
820
  return {
821
+ ...ownerId !== void 0 ? { ownerId } : {},
469
822
  questions: [...byId.values()],
470
- findings
823
+ findings,
824
+ continuations,
825
+ deliveryEvidence,
826
+ records
471
827
  };
472
828
  }
473
829
  };
830
+ function emptyPriorCoordination(ownerId) {
831
+ return {
832
+ ...ownerId !== void 0 ? { ownerId } : {},
833
+ questions: [],
834
+ findings: [],
835
+ continuations: [],
836
+ deliveryEvidence: [],
837
+ records: []
838
+ };
839
+ }
474
840
  function isNoEntError(err) {
475
841
  return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
476
842
  }
@@ -1561,9 +1927,10 @@ function createInbox() {
1561
1927
  return {
1562
1928
  deliver(msg) {
1563
1929
  const m = parseDown(msg);
1564
- if (!m) return;
1930
+ if (!m) return false;
1565
1931
  pending.push(m);
1566
1932
  if (m.interrupt && live && !live.signal.aborted) live.abort();
1933
+ return true;
1567
1934
  },
1568
1935
  drain() {
1569
1936
  return pending.splice(0, pending.length);
@@ -2220,8 +2587,8 @@ function sandboxSessionTraceSource(box, sessionId, opts = {}) {
2220
2587
  * | `profile.prompt.systemPrompt` | honored — prepended to the task text (pi RPC takes no separate system-prompt channel) |
2221
2588
  * | `profile.mcp` | honored — written to this execution's own file and passed as `--mcp-config` for `pi-mcp-adapter`; see `pi-mcp.ts` |
2222
2589
  * | `profile.extensions.pi.load` | honored — lowered to `--no-extensions` + `--extension <abs>` |
2223
- * | `profile.prompt.instructions` | DROPPEDfold into `systemPrompt` before calling |
2224
- * | `profile.model` | DROPPED — the seam's `model` is the only model channel; a profile that disagrees with the seam is silently overridden by the seam |
2590
+ * | `profile.prompt.instructions` | honoredappended to the system prompt, one per line |
2591
+ * | `profile.model.default` | honoredoverrides the seam's `model`; the seam is the fallback for profiles that select none |
2225
2592
  * | `profile.model.reasoningEffort` | DROPPED — no `--thinking` flag is emitted, so pi's configured `defaultThinkingLevel` applies |
2226
2593
  * | `profile.tools` | DROPPED — no `--no-tools` / allow-deny mapping; pi runs its full builtin tool set |
2227
2594
  * | `profile.permissions` | DROPPED |
@@ -2242,12 +2609,17 @@ const PI_RUNTIME = "pi";
2242
2609
  const piSeamKey = "pi";
2243
2610
  /** Build the `Executor` for one pi worker. Registered as runtime `'pi'`. */
2244
2611
  const piExecutor = (spec, ctx) => {
2245
- const seam = readPiSeam(ctx);
2612
+ const seam = {
2613
+ ...readPiSeam(ctx),
2614
+ ...spec.profile.model?.default ? { model: spec.profile.model.default } : {}
2615
+ };
2246
2616
  const traceEnv = workerTraceEnv(ctx);
2247
2617
  const inbox = createInbox();
2248
2618
  const activity = createActivityLog(seam.activityWindow ?? 12);
2249
2619
  const runId = `pi-${spec.profile.name ?? "worker"}-${Date.now()}`;
2250
2620
  const trace = createPushTraceSource({ runId });
2621
+ const executionId = ctx.node?.nodeId ?? `pi-run-${randomUUID()}`;
2622
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(executionId);
2251
2623
  const derived = [];
2252
2624
  const controller = new AbortController();
2253
2625
  const cascade = () => controller.abort();
@@ -2260,7 +2632,7 @@ const piExecutor = (spec, ctx) => {
2260
2632
  proc: void 0,
2261
2633
  artifact: void 0
2262
2634
  };
2263
- return {
2635
+ const executor = {
2264
2636
  runtime: "pi",
2265
2637
  deliver: (m) => inbox.deliver(m),
2266
2638
  progress: () => ({
@@ -2301,6 +2673,44 @@ const piExecutor = (spec, ctx) => {
2301
2673
  return state.artifact;
2302
2674
  }
2303
2675
  };
2676
+ if (ctx.node === void 0) return executor;
2677
+ return attestRuntimeOwnedExecutor(executor, {
2678
+ effectiveProfile: spec.profile,
2679
+ backend: "pi",
2680
+ model: seam.model ? {
2681
+ status: "known",
2682
+ id: seam.model
2683
+ } : {
2684
+ status: "unknown",
2685
+ reason: "pi selected its configured default model"
2686
+ },
2687
+ execution: {
2688
+ kind: "run",
2689
+ id: executionId
2690
+ },
2691
+ materializer: "pi-rpc-agent-profile",
2692
+ plan: {
2693
+ kind: "pi-rpc-session",
2694
+ bin: seam.bin ?? "pi",
2695
+ args: seam.args ?? [],
2696
+ cwd: seam.cwd ?? null,
2697
+ model: seam.model ?? null,
2698
+ turnTimeoutMs: seam.turnTimeoutMs ?? null
2699
+ }
2700
+ }, {
2701
+ attemptId,
2702
+ binding: {
2703
+ executionId,
2704
+ bin: seam.bin ?? "pi",
2705
+ cwd: seam.cwd ?? null,
2706
+ model: seam.model ?? null
2707
+ },
2708
+ descriptor: {
2709
+ kind: "pi-rpc-run",
2710
+ transport: "process",
2711
+ backend: "pi"
2712
+ }
2713
+ });
2304
2714
  };
2305
2715
  /**
2306
2716
  * One pi RPC session, run to `agent_settled`. Every steer delivered while the turn is in flight is
@@ -2317,6 +2727,7 @@ async function* streamPiSession(args) {
2317
2727
  };
2318
2728
  let usd = 0;
2319
2729
  let usdKnown = true;
2730
+ let tokensKnown = true;
2320
2731
  throwIfAborted(args.signal);
2321
2732
  throwIfAborted(args.controller.signal);
2322
2733
  const piMcp = preparePiMcp(args.spec.profile, {
@@ -2383,7 +2794,7 @@ async function* streamPiSession(args) {
2383
2794
  };
2384
2795
  args.signal.addEventListener("abort", abortAll, { once: true });
2385
2796
  args.controller.signal.addEventListener("abort", abortAll, { once: true });
2386
- const system = args.spec.profile.prompt?.systemPrompt;
2797
+ const system = [args.spec.profile.prompt?.systemPrompt, ...args.spec.profile.prompt?.instructions ?? []].filter((line) => typeof line === "string" && line.trim().length > 0).join("\n");
2387
2798
  const opening = system ? `${system}\n\n${taskText(args.task)}` : taskText(args.task);
2388
2799
  const deadline = seam.turnTimeoutMs ? Date.now() + seam.turnTimeoutMs : void 0;
2389
2800
  const sendPrompt = (message, streamingBehavior) => {
@@ -2413,6 +2824,7 @@ async function* streamPiSession(args) {
2413
2824
  }
2414
2825
  const projected = projectPiEvent(ev, args, tokens, pendingTools);
2415
2826
  if (projected.assistant) lastAssistant = projected.assistant;
2827
+ if (projected.tokensUnknown) tokensKnown = false;
2416
2828
  for (const usage of projected.events) {
2417
2829
  if (usage.kind === "cost") {
2418
2830
  usd += usage.usd;
@@ -2466,6 +2878,7 @@ async function* streamPiSession(args) {
2466
2878
  const spent = {
2467
2879
  iterations: state.turns,
2468
2880
  tokens,
2881
+ ...tokensKnown ? {} : { tokensKnown: false },
2469
2882
  usd,
2470
2883
  ...usdKnown ? {} : { usdKnown: false },
2471
2884
  ms: Date.now() - started
@@ -2569,9 +2982,11 @@ function projectPiEvent(ev, args, tokens, pendingTools) {
2569
2982
  });
2570
2983
  out.push({ kind: "iteration" });
2571
2984
  const assistant = readAssistantOutcome(ev.message);
2985
+ const tokensUnknown = !usage || usage.tokensKnown === false;
2572
2986
  return {
2573
2987
  events: out,
2574
- ...assistant ? { assistant } : {}
2988
+ ...assistant ? { assistant } : {},
2989
+ ...tokensUnknown ? { tokensUnknown: true } : {}
2575
2990
  };
2576
2991
  }
2577
2992
  return { events: out };
@@ -2584,14 +2999,18 @@ function readUsage(message) {
2584
2999
  const usage = message.usage;
2585
3000
  if (!usage || typeof usage !== "object") return void 0;
2586
3001
  const u = usage;
2587
- const input = num(u.prompt_tokens) ?? (num(u.input) ?? num(u.inputTokens) ?? 0) + (num(u.cacheRead) ?? num(u.cache_read_input_tokens) ?? num(u.cacheReadInputTokens) ?? 0) + (num(u.cacheWrite) ?? num(u.cache_creation_input_tokens) ?? num(u.cacheCreationInputTokens) ?? 0);
2588
- const output = num(u.output) ?? num(u.outputTokens) ?? num(u.completion_tokens) ?? 0;
3002
+ const promptTokens = num(u.prompt_tokens);
3003
+ const freshInput = num(u.input) ?? num(u.inputTokens);
3004
+ const outputRaw = num(u.output) ?? num(u.outputTokens) ?? num(u.completion_tokens);
3005
+ const input = promptTokens ?? (freshInput ?? 0) + (num(u.cacheRead) ?? num(u.cache_read_input_tokens) ?? num(u.cacheReadInputTokens) ?? 0) + (num(u.cacheWrite) ?? num(u.cache_creation_input_tokens) ?? num(u.cacheCreationInputTokens) ?? 0);
3006
+ const output = outputRaw ?? 0;
2589
3007
  const costRaw = u.cost;
2590
3008
  const reportedUsd = num(costRaw) ?? (costRaw && typeof costRaw === "object" ? num(costRaw.total) ?? num(costRaw.totalCost) : void 0);
2591
3009
  return {
2592
3010
  input,
2593
3011
  output,
2594
- ...reportedUsd !== void 0 && reportedUsd > 0 ? { usd: reportedUsd } : {}
3012
+ ...reportedUsd !== void 0 && reportedUsd > 0 ? { usd: reportedUsd } : {},
3013
+ tokensKnown: promptTokens !== void 0 || freshInput !== void 0 || outputRaw !== void 0
2595
3014
  };
2596
3015
  }
2597
3016
  function readAssistantOutcome(message) {
@@ -3026,34 +3445,38 @@ function readPromptOptions(loopCtx) {
3026
3445
  * Build a worktree-CLI leaf `Executor`. Per-spawn (a fresh worktree + abort + teardown each), so a
3027
3446
  * fanout of N profiles = N parallel worktrees that never clobber each other.
3028
3447
  *
3029
- * Fail-loud: an empty `repoRoot`/`harness`/`taskPrompt` throws at construction. `resultArtifact()`
3030
- * before `execute()` resolves throws.
3448
+ * Fail-loud: an empty `repoRoot`/`harness` or an explicitly empty `taskPrompt` throws at
3449
+ * construction. Calling `execute(undefined, signal)` without a configured prompt throws before a
3450
+ * worktree is created. `resultArtifact()` before `execute()` resolves throws.
3031
3451
  *
3032
3452
  * @experimental
3033
3453
  */
3034
3454
  function createWorktreeCliExecutor(options) {
3035
3455
  if (!options.repoRoot) throw new ValidationError("createWorktreeCliExecutor: repoRoot required");
3036
3456
  if (!options.harness) throw new ValidationError("createWorktreeCliExecutor: harness required");
3037
- if (typeof options.taskPrompt !== "string" || options.taskPrompt.length === 0) throw new ValidationError("createWorktreeCliExecutor: taskPrompt required");
3457
+ if (options.taskPrompt !== void 0 && (typeof options.taskPrompt !== "string" || options.taskPrompt.length === 0)) throw new ValidationError("createWorktreeCliExecutor: taskPrompt required");
3038
3458
  if (options.codexReproducible && options.harness !== "codex") throw new ValidationError("createWorktreeCliExecutor: codexReproducible requires harness \"codex\"");
3039
3459
  if (options.codexReproducible && options.budgetExempt === true) throw new ValidationError("createWorktreeCliExecutor: codexReproducible cannot be budgetExempt");
3040
3460
  if (options.codexReadDeniedPaths !== void 0 && !options.codexReproducible) throw new ValidationError("createWorktreeCliExecutor: codexReadDeniedPaths requires codexReproducible");
3041
3461
  const runId = options.runId ?? randomUUID();
3462
+ const attemptId = options.executionAttemptId ?? newExecutionAttemptId(runId);
3042
3463
  const controller = new AbortController();
3043
3464
  const budgetExempt = options.budgetExempt ?? !options.codexReproducible;
3044
3465
  let run;
3045
3466
  let artifact;
3046
- return {
3467
+ const profilePlan = worktreeProfileExecutionPlan(options.profile, options.harness);
3468
+ return attestRuntimeOwnedExecutor({
3047
3469
  runtime: "cli",
3048
3470
  budgetExempt,
3049
- async execute(_task, signal) {
3471
+ async execute(task, signal) {
3050
3472
  const linked = linkSignals$1(signal, controller.signal);
3051
3473
  const started = Date.now();
3474
+ const taskPrompt = executionTaskPrompt(task, options.taskPrompt);
3052
3475
  run = await runWorktreeHarness({
3053
3476
  repoRoot: options.repoRoot,
3054
3477
  profile: options.profile,
3055
3478
  harness: options.harness,
3056
- taskPrompt: options.taskPrompt,
3479
+ taskPrompt,
3057
3480
  runId,
3058
3481
  ...options.baseRef ? { baseRef: options.baseRef } : {},
3059
3482
  ...options.testCmd !== void 0 ? { testCmd: options.testCmd } : {},
@@ -3108,7 +3531,65 @@ function createWorktreeCliExecutor(options) {
3108
3531
  if (!artifact) throw new ValidationError("createWorktreeCliExecutor: resultArtifact() read before execute() resolved");
3109
3532
  return artifact;
3110
3533
  }
3111
- };
3534
+ }, {
3535
+ effectiveProfile: options.profile,
3536
+ backend: `cli-worktree:${options.harness}`,
3537
+ model: options.profile.model?.default ? {
3538
+ status: "known",
3539
+ id: options.profile.model.default
3540
+ } : {
3541
+ status: "unknown",
3542
+ reason: `${options.harness} selected its configured default model`
3543
+ },
3544
+ execution: {
3545
+ kind: "worktree-run",
3546
+ id: runId
3547
+ },
3548
+ materializer: "agent-profile-worktree-plan",
3549
+ plan: {
3550
+ kind: "worktree-cli",
3551
+ profilePlan,
3552
+ harness: options.harness,
3553
+ baseRef: options.baseRef ?? "HEAD",
3554
+ harnessTimeoutMs: options.harnessTimeoutMs ?? null,
3555
+ codexReproducible: options.codexReproducible === true,
3556
+ codexReadDeniedPaths: options.codexReadDeniedPaths ?? [],
3557
+ testCmd: options.testCmd ?? null,
3558
+ typecheckCmd: options.typecheckCmd ?? null,
3559
+ checkTimeoutMs: options.checkTimeoutMs ?? null,
3560
+ checkOutputCap: options.checkOutputCap ?? 16e3
3561
+ }
3562
+ }, {
3563
+ attemptId,
3564
+ binding: {
3565
+ repoRoot: options.repoRoot,
3566
+ runId,
3567
+ harness: options.harness,
3568
+ model: options.profile.model?.default ?? null,
3569
+ baseRef: options.baseRef ?? "HEAD"
3570
+ },
3571
+ descriptor: {
3572
+ kind: "worktree-cli-run",
3573
+ transport: "process",
3574
+ backend: options.harness
3575
+ }
3576
+ });
3577
+ }
3578
+ /** A scoped execution task is authoritative. The configured prompt remains only as the
3579
+ * unambiguous direct-call default for existing `execute(undefined, signal)` consumers. */
3580
+ function executionTaskPrompt(task, configuredPrompt) {
3581
+ if (task === void 0) {
3582
+ if (configuredPrompt !== void 0) return configuredPrompt;
3583
+ throw new ValidationError("createWorktreeCliExecutor: execute task required when taskPrompt is not configured");
3584
+ }
3585
+ if (typeof task === "string") return task;
3586
+ try {
3587
+ const encoded = JSON.stringify(task);
3588
+ if (encoded !== void 0) return encoded;
3589
+ } catch (error) {
3590
+ throw new ValidationError("createWorktreeCliExecutor: execute task must be JSON-serializable", { cause: error });
3591
+ }
3592
+ throw new ValidationError("createWorktreeCliExecutor: execute task must be JSON-serializable");
3112
3593
  }
3113
3594
  /** Link two abort signals into one that fires when either does. Returns `undefined` when neither
3114
3595
  * is present so the harness runner gets no signal at all. */
@@ -3195,7 +3676,7 @@ function zeroSpend() {
3195
3676
  */
3196
3677
  const routerInlineExecutor = (spec, ctx) => {
3197
3678
  const seam = readSeam(ctx, routerSeamKey, "router/inline");
3198
- const model = seam.model ?? spec.profile.model?.default;
3679
+ const model = spec.profile.model?.default ?? seam.model;
3199
3680
  if (!model) throw new ValidationError("routerInlineExecutor: no model — set RouterSeam.model or AgentProfile.model.default");
3200
3681
  if (!seam.routerBaseUrl || !seam.routerKey) throw new ValidationError("routerInlineExecutor: RouterSeam.routerBaseUrl + routerKey required");
3201
3682
  const controller = new AbortController();
@@ -3205,7 +3686,9 @@ const routerInlineExecutor = (spec, ctx) => {
3205
3686
  abortIfSignalled();
3206
3687
  if (!ctx.signal.aborted) ctx.signal.addEventListener("abort", abortIfSignalled, { once: true });
3207
3688
  let artifact;
3208
- return {
3689
+ const executionId = ctx.node?.nodeId ?? `router-request-${randomUUID()}`;
3690
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(executionId);
3691
+ return attestRuntimeOwnedExecutor({
3209
3692
  runtime: "router",
3210
3693
  async execute(task, signal) {
3211
3694
  const messages = taskToMessages(task, spec);
@@ -3223,6 +3706,8 @@ const routerInlineExecutor = (spec, ctx) => {
3223
3706
  output: r.usage.output
3224
3707
  } : zeroTokenUsage(),
3225
3708
  usd: r.costUsd ?? 0,
3709
+ ...r.usage ? {} : { tokensKnown: false },
3710
+ ...r.costUsd === void 0 ? { usdKnown: false } : {},
3226
3711
  ms: Date.now() - started
3227
3712
  };
3228
3713
  const out = { content: r.content };
@@ -3247,7 +3732,35 @@ const routerInlineExecutor = (spec, ctx) => {
3247
3732
  spent: artifact.spent
3248
3733
  };
3249
3734
  }
3250
- };
3735
+ }, {
3736
+ effectiveProfile: spec.profile,
3737
+ backend: "router",
3738
+ model: {
3739
+ status: "known",
3740
+ id: model
3741
+ },
3742
+ execution: {
3743
+ kind: "request",
3744
+ id: executionId
3745
+ },
3746
+ materializer: "router-prompt-model",
3747
+ plan: {
3748
+ kind: "openai-chat-completion",
3749
+ model
3750
+ }
3751
+ }, {
3752
+ attemptId,
3753
+ binding: {
3754
+ endpoint: seam.routerBaseUrl,
3755
+ executionId,
3756
+ model
3757
+ },
3758
+ descriptor: {
3759
+ kind: "router-request",
3760
+ transport: "http",
3761
+ backend: "router"
3762
+ }
3763
+ });
3251
3764
  };
3252
3765
  const routerToolsSeamKey = "router-tools";
3253
3766
  /**
@@ -3256,7 +3769,7 @@ const routerToolsSeamKey = "router-tools";
3256
3769
  */
3257
3770
  const routerToolsInlineExecutor = (spec, ctx) => {
3258
3771
  const seam = readSeam(ctx, routerToolsSeamKey, "router-tools");
3259
- const model = seam.model ?? spec.profile.model?.default;
3772
+ const model = spec.profile.model?.default ?? seam.model;
3260
3773
  if (!model) throw new ValidationError("routerToolsInlineExecutor: no model — set RouterToolsSeam.model or AgentProfile.model.default");
3261
3774
  if (!seam.routerBaseUrl || !seam.routerKey) throw new ValidationError("routerToolsInlineExecutor: RouterToolsSeam.routerBaseUrl + routerKey required");
3262
3775
  const maxTurns = seam.maxTurns ?? 200;
@@ -3268,13 +3781,16 @@ const routerToolsInlineExecutor = (spec, ctx) => {
3268
3781
  if (!ctx.signal.aborted) ctx.signal.addEventListener("abort", abortIfSignalled, { once: true });
3269
3782
  const inbox = createInbox();
3270
3783
  let artifact;
3271
- return {
3784
+ const executionId = ctx.node?.nodeId ?? `router-tools-run-${randomUUID()}`;
3785
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(executionId);
3786
+ return attestRuntimeOwnedExecutor({
3272
3787
  runtime: "router",
3273
3788
  deliver: (m) => inbox.deliver(m),
3274
3789
  async execute(task, signal) {
3275
3790
  const started = Date.now();
3276
3791
  const messages = [...taskToMessages(task, spec)];
3277
3792
  const tokens = zeroTokenUsage();
3793
+ let tokensKnown = true;
3278
3794
  let turns = 0;
3279
3795
  let lastText = "";
3280
3796
  const flush = () => {
@@ -3325,7 +3841,7 @@ const routerToolsInlineExecutor = (spec, ctx) => {
3325
3841
  if (u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number") {
3326
3842
  tokens.input += u.prompt_tokens;
3327
3843
  tokens.output += u.completion_tokens;
3328
- }
3844
+ } else tokensKnown = false;
3329
3845
  const msg = data.choices?.[0]?.message;
3330
3846
  if (msg?.content) lastText = msg.content;
3331
3847
  const toolCalls = msg?.tool_calls ?? [];
@@ -3387,11 +3903,14 @@ const routerToolsInlineExecutor = (spec, ctx) => {
3387
3903
  } catch {}
3388
3904
  }
3389
3905
  }
3390
- const usd = isModelPriced(model) ? estimateCost(tokens.input, tokens.output, model) : 0;
3906
+ const priced = isModelPriced(model);
3907
+ const usd = priced ? estimateCost(tokens.input, tokens.output, model) : 0;
3391
3908
  const spent = {
3392
3909
  iterations: turns,
3393
3910
  tokens,
3911
+ ...tokensKnown ? {} : { tokensKnown: false },
3394
3912
  usd,
3913
+ ...!priced || !tokensKnown ? { usdKnown: false } : {},
3395
3914
  ms: Date.now() - started
3396
3915
  };
3397
3916
  const out = { content: lastText };
@@ -3416,7 +3935,37 @@ const routerToolsInlineExecutor = (spec, ctx) => {
3416
3935
  spent: artifact.spent
3417
3936
  };
3418
3937
  }
3419
- };
3938
+ }, {
3939
+ effectiveProfile: spec.profile,
3940
+ backend: "router-tools",
3941
+ model: {
3942
+ status: "known",
3943
+ id: model
3944
+ },
3945
+ execution: {
3946
+ kind: "run",
3947
+ id: executionId
3948
+ },
3949
+ materializer: "router-tools-prompt-model",
3950
+ plan: {
3951
+ kind: "openai-tool-loop",
3952
+ model,
3953
+ maxTurns,
3954
+ tools: seam.tools
3955
+ }
3956
+ }, {
3957
+ attemptId,
3958
+ binding: {
3959
+ endpoint: seam.routerBaseUrl,
3960
+ executionId,
3961
+ model
3962
+ },
3963
+ descriptor: {
3964
+ kind: "router-tool-loop",
3965
+ transport: "http",
3966
+ backend: "router-tools"
3967
+ }
3968
+ });
3420
3969
  };
3421
3970
  /**
3422
3971
  * COMPOSES `runAgentRounds` as a single-task leaf: one box, a refine driver bounded to
@@ -3445,11 +3994,48 @@ const sandboxExecutor = (spec, ctx) => {
3445
3994
  abortIfSignalled();
3446
3995
  if (!ctx.signal.aborted) ctx.signal.addEventListener("abort", abortIfSignalled, { once: true });
3447
3996
  let artifact;
3448
- if (seam.steering) {
3449
- const inbox = createInbox();
3450
- const session = createSteerableSandboxSession({
3451
- controller,
3452
- profile: spec.profile,
3997
+ const executionId = ctx.node?.nodeId ?? `sandbox-run-${randomUUID()}`;
3998
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(executionId);
3999
+ const sandboxMaterialization = {
4000
+ effectiveProfile: spec.profile,
4001
+ backend: harness,
4002
+ model: spec.profile.model?.default ? {
4003
+ status: "known",
4004
+ id: spec.profile.model.default
4005
+ } : {
4006
+ status: "unknown",
4007
+ reason: "sandbox harness selected its default model"
4008
+ },
4009
+ execution: {
4010
+ kind: "run",
4011
+ id: executionId
4012
+ },
4013
+ materializer: "sandbox-agent-profile",
4014
+ plan: {
4015
+ kind: "sandbox-agent-rounds",
4016
+ harness,
4017
+ maxIterations,
4018
+ steering: seam.steering !== void 0
4019
+ }
4020
+ };
4021
+ const sandboxBinding = {
4022
+ attemptId,
4023
+ binding: {
4024
+ executionId,
4025
+ harness,
4026
+ model: spec.profile.model?.default ?? null
4027
+ },
4028
+ descriptor: {
4029
+ kind: "sandbox-run",
4030
+ transport: "sandbox",
4031
+ backend: harness
4032
+ }
4033
+ };
4034
+ if (seam.steering) {
4035
+ const inbox = createInbox();
4036
+ const session = createSteerableSandboxSession({
4037
+ controller,
4038
+ profile: spec.profile,
3453
4039
  harness,
3454
4040
  sandboxClient: seam.sandboxClient,
3455
4041
  inbox,
@@ -3459,7 +4045,7 @@ const sandboxExecutor = (spec, ctx) => {
3459
4045
  ...Object.keys(traceEnv).length > 0 ? { traceEnv } : {},
3460
4046
  contentRef
3461
4047
  });
3462
- return {
4048
+ return attestRuntimeOwnedExecutor({
3463
4049
  runtime: "sandbox",
3464
4050
  deliver: (m) => inbox.deliver(m),
3465
4051
  progress: () => session.progress(),
@@ -3477,13 +4063,13 @@ const sandboxExecutor = (spec, ctx) => {
3477
4063
  if (!a) throw new ValidationError("sandboxExecutor(steering): resultArtifact() read before stream drained");
3478
4064
  return a;
3479
4065
  }
3480
- };
4066
+ }, sandboxMaterialization, sandboxBinding);
3481
4067
  }
3482
4068
  const output = { parse(events) {
3483
4069
  return { events };
3484
4070
  } };
3485
4071
  const driver = singleShotDriver(maxIterations);
3486
- return {
4072
+ return attestRuntimeOwnedExecutor({
3487
4073
  runtime: "sandbox",
3488
4074
  execute(task, signal) {
3489
4075
  return streamSandboxLeaf({
@@ -3511,7 +4097,7 @@ const sandboxExecutor = (spec, ctx) => {
3511
4097
  if (!artifact) throw new ValidationError("sandboxExecutor: resultArtifact() read before stream drained");
3512
4098
  return artifact;
3513
4099
  }
3514
- };
4100
+ }, sandboxMaterialization, sandboxBinding);
3515
4101
  };
3516
4102
  async function* streamSandboxLeaf(args) {
3517
4103
  const linked = new AbortController();
@@ -3547,8 +4133,10 @@ async function* streamSandboxLeaf(args) {
3547
4133
  };
3548
4134
  try {
3549
4135
  const result = await runAgentRounds(loopOptions);
4136
+ const failure = failedRound(result);
4137
+ if (failure) throw failure;
3550
4138
  const out = result.winner?.output ?? { events: [] };
3551
- const verdict = result.winner?.verdict;
4139
+ const verdict = result.winner?.verdict ?? leafVerdict(result);
3552
4140
  const spent = {
3553
4141
  iterations: result.iterations.length,
3554
4142
  tokens: {
@@ -3582,11 +4170,39 @@ async function* streamSandboxLeaf(args) {
3582
4170
  args.controller.signal.removeEventListener("abort", cascade);
3583
4171
  }
3584
4172
  }
4173
+ /** The loop's own failure, when NO iteration produced an output: the first error it
4174
+ * recorded, renamed so the settled worker names the leaf it died in. `undefined`
4175
+ * when any iteration produced an output — a partly-failed round still has material
4176
+ * to settle on. */
4177
+ function failedRound(result) {
4178
+ if (result.iterations.length === 0) return void 0;
4179
+ if (result.iterations.some((iteration) => iteration.output !== void 0)) return void 0;
4180
+ const first = result.iterations.find((iteration) => iteration.error)?.error;
4181
+ if (!first) return void 0;
4182
+ return new Error(`sandboxExecutor: agent round failed — ${first.message}`, { cause: first });
4183
+ }
4184
+ /**
4185
+ * The leaf's OWN verdict, for a round the loop scored no validator against.
4186
+ *
4187
+ * `settled ⟺ delivered` is written by the completion oracle, and a caller that
4188
+ * passes one keeps it: `gateOnDeliverable` wraps this executor and overrides
4189
+ * `valid` from its check. This is the sandbox backend's structural answer for a
4190
+ * run with no oracle at all — without it nothing ever writes `valid`, no settled
4191
+ * child is ever DELIVERED, and the finalizer has nothing to select no matter how
4192
+ * well the worker ran. Structural, never self-reported: the harness completed a
4193
+ * round and returned an output artifact, or it did not.
4194
+ */
4195
+ function leafVerdict(result) {
4196
+ if (result.winner?.output === void 0) return void 0;
4197
+ return {
4198
+ valid: true,
4199
+ score: 1
4200
+ };
4201
+ }
3585
4202
  /**
3586
4203
  * Spawns a subprocess (`bin` + `args`). It cannot account tokens, so it is
3587
- * `budgetExempt: true`: its spend is NOT metered against the conserved pool and
3588
- * its iterations are EXCLUDED from the equal-k arms by construction (the
3589
- * resolver/equal-k path checks `budgetExempt`). teardown is SIGTERM → SIGKILL
4204
+ * `budgetExempt: true`: it remains usable as a direct executor, while budgeted supervision
4205
+ * refuses it before process execution because the CLI exposes no usage receipt. teardown is SIGTERM → SIGKILL
3590
4206
  * with a grace window. Streaming: yields one `iteration` event on clean exit.
3591
4207
  */
3592
4208
  const cliExecutor = (_spec, ctx) => {
@@ -3601,7 +4217,9 @@ const cliExecutor = (_spec, ctx) => {
3601
4217
  if (!ctx.signal.aborted) ctx.signal.addEventListener("abort", abortIfSignalled, { once: true });
3602
4218
  let proc;
3603
4219
  let artifact;
3604
- return {
4220
+ const executionId = ctx.node?.nodeId ?? `cli-process-${randomUUID()}`;
4221
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(executionId);
4222
+ return attestRuntimeOwnedExecutor({
3605
4223
  runtime: "cli",
3606
4224
  budgetExempt: true,
3607
4225
  execute(task, signal) {
@@ -3628,7 +4246,39 @@ const cliExecutor = (_spec, ctx) => {
3628
4246
  if (!artifact) throw new ValidationError("cliExecutor: resultArtifact() read before stream drained");
3629
4247
  return artifact;
3630
4248
  }
3631
- };
4249
+ }, {
4250
+ effectiveProfile: _spec.profile,
4251
+ backend: "cli",
4252
+ model: {
4253
+ status: "unknown",
4254
+ reason: "raw subprocess has no model identity contract"
4255
+ },
4256
+ execution: {
4257
+ kind: "process-attempt",
4258
+ id: executionId
4259
+ },
4260
+ materializer: "raw-cli-stdin",
4261
+ plan: {
4262
+ kind: "raw-cli-process",
4263
+ bin: seam.bin,
4264
+ args: seam.args ?? [],
4265
+ cwd: seam.cwd ?? null,
4266
+ envOverrides: seam.env ?? {},
4267
+ ambientEnvironment: "inherited"
4268
+ }
4269
+ }, {
4270
+ attemptId,
4271
+ binding: {
4272
+ executionId,
4273
+ bin: seam.bin,
4274
+ cwd: seam.cwd ?? null
4275
+ },
4276
+ descriptor: {
4277
+ kind: "cli-process",
4278
+ transport: "process",
4279
+ backend: "cli"
4280
+ }
4281
+ });
3632
4282
  };
3633
4283
  async function* streamCliLeaf(args) {
3634
4284
  const prompt = taskToPrompt(args.task);
@@ -3717,33 +4367,34 @@ function killWithGrace(proc, grace) {
3717
4367
  * - STEERABLE: the down-leg `inbox` is drained at each turn boundary; a queued
3718
4368
  * steer becomes the next turn's prompt on the same session, and the worker can't
3719
4369
  * settle while a steer it never read is pending (the sandbox/router contract).
3720
- * - ABORT: the caller signal + teardown fold into the per-turn fetch signal; a
3721
- * forceful (`interrupt`) steer aborts the in-flight turn so the worker re-plans.
4370
+ * - ABORT: reader abort only detaches HTTP. Interrupt/teardown then call the
4371
+ * bridge's explicit cancel operation and wait for the owned run to terminate.
3722
4372
  *
3723
4373
  * Reports REAL usage when the bridge surfaces it, never a fabricated cost.
3724
4374
  */
3725
- /** Resolve the bridge wire model for this spawn: a per-create `backend` override
3726
- * (harness + model) wins over the seam default, encoded as `${harness}/${model}`.
3727
- * Absent an override the seam `model` is used verbatim. */
3728
- function bridgeCellModel(seamModel, ctx) {
4375
+ /** Resolve the bridge wire model for this spawn. Per-create matrix settings win, then the
4376
+ * canonical profile's harness/model preferences, then the bridge's configured fallback. */
4377
+ function bridgeCellModel(seamModel, ctx, profile) {
3729
4378
  const backend = ctx.seams.createOptions?.backend;
3730
- const harness = backend?.type;
3731
- const model = backend?.model?.model;
4379
+ const profileHarness = profile.harness === "cli-base" ? void 0 : profile.harness;
4380
+ const harness = backend?.type ?? profileHarness;
4381
+ const model = backend?.model?.model ?? profile.model?.default;
3732
4382
  if (!harness && !model) return seamModel;
3733
- const h = harness ?? "";
3734
- const m = model ?? seamModel;
3735
- if (!h) return m;
3736
- return m.startsWith(`${h}/`) ? m : `${h}/${m}`;
4383
+ if (!harness) return model;
4384
+ if (model) return model.startsWith(`${harness}/`) ? model : `${harness}/${model}`;
4385
+ return seamModel?.startsWith(`${harness}/`) ? seamModel : void 0;
3737
4386
  }
3738
4387
  const bridgeExecutor = (spec, ctx) => {
3739
4388
  const base = readSeam(ctx, bridgeSeamKey, "bridge");
4389
+ const effectiveProfile = agentProfileSchema.parse(mergeAgentProfiles(spec.profile, base.agentProfile) ?? spec.profile);
3740
4390
  const seam = {
3741
4391
  ...base,
3742
- model: bridgeCellModel(base.model, ctx)
4392
+ model: bridgeCellModel(base.model, ctx, effectiveProfile)
3743
4393
  };
3744
- if (!seam.bridgeUrl || !seam.bridgeBearer || !seam.model) throw new ValidationError("bridgeExecutor: BridgeSeam.bridgeUrl + bridgeBearer + model required");
4394
+ if (!seam.bridgeUrl || !seam.bridgeBearer || !seam.model) throw new ValidationError("bridgeExecutor: bridgeUrl + bridgeBearer and a profile or bridge model are required");
3745
4395
  const maxTurns = seam.maxTurns ?? 200;
3746
4396
  const sessionId = seam.sessionId ?? `bridge-${spec.profile.name ?? "worker"}-${randomUUID()}`;
4397
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(sessionId);
3747
4398
  const controller = new AbortController();
3748
4399
  const abortIfSignalled = () => {
3749
4400
  if (ctx.signal.aborted) controller.abort();
@@ -3752,27 +4403,31 @@ const bridgeExecutor = (spec, ctx) => {
3752
4403
  if (!ctx.signal.aborted) ctx.signal.addEventListener("abort", abortIfSignalled, { once: true });
3753
4404
  const inbox = createInbox();
3754
4405
  let artifact;
3755
- return {
4406
+ const activeRuns = /* @__PURE__ */ new Map();
4407
+ return attestRuntimeOwnedExecutor({
3756
4408
  runtime: "cli",
3757
4409
  deliver: (m) => inbox.deliver(m),
3758
4410
  execute(task, signal) {
3759
4411
  return streamBridgeSession({
3760
4412
  task,
3761
4413
  signal,
3762
- spec,
4414
+ profile: effectiveProfile,
3763
4415
  seam,
3764
4416
  sessionId,
3765
4417
  maxTurns,
3766
4418
  inbox,
3767
4419
  controller,
4420
+ activeRuns,
3768
4421
  onArtifact: (a) => {
3769
4422
  artifact = a;
3770
4423
  }
3771
4424
  });
3772
4425
  },
3773
- teardown(_grace) {
4426
+ async teardown(grace) {
3774
4427
  controller.abort();
3775
- return Promise.resolve({ destroyed: true });
4428
+ const remaining = [...activeRuns.values()].filter((run) => !run.terminal);
4429
+ if (remaining.length === 0) return { destroyed: true };
4430
+ return { destroyed: (await Promise.all(remaining.map((run) => cancelBridgeRunToTerminal(seam, run, grace)))).every(Boolean) };
3776
4431
  },
3777
4432
  resultArtifact() {
3778
4433
  if (!artifact) throw new ValidationError("bridgeExecutor: resultArtifact() read before stream drained");
@@ -3781,7 +4436,40 @@ const bridgeExecutor = (spec, ctx) => {
3781
4436
  spent: artifact.spent
3782
4437
  };
3783
4438
  }
3784
- };
4439
+ }, {
4440
+ effectiveProfile,
4441
+ backend: "bridge",
4442
+ model: {
4443
+ status: "known",
4444
+ id: seam.model
4445
+ },
4446
+ execution: {
4447
+ kind: "session",
4448
+ id: sessionId
4449
+ },
4450
+ materializer: "cli-bridge-agent-profile",
4451
+ plan: {
4452
+ kind: "cli-bridge-session",
4453
+ cwd: seam.cwd ?? null,
4454
+ maxTurns,
4455
+ timeoutMs: seam.timeoutMs ?? null,
4456
+ streaming: true
4457
+ }
4458
+ }, {
4459
+ attemptId,
4460
+ binding: {
4461
+ bridgeUrl: seam.bridgeUrl,
4462
+ cwd: seam.cwd ?? null,
4463
+ effectiveProfile,
4464
+ model: seam.model,
4465
+ sessionId
4466
+ },
4467
+ descriptor: {
4468
+ kind: "bridge-session",
4469
+ transport: "http",
4470
+ backend: "bridge"
4471
+ }
4472
+ });
3785
4473
  };
3786
4474
  /**
3787
4475
  * One resumable cli-bridge session, run as a streamed turn loop. Turn 0 sends the
@@ -3795,12 +4483,13 @@ async function* streamBridgeSession(args) {
3795
4483
  const started = Date.now();
3796
4484
  const external = mergeAbortSignals(args.signal, args.controller.signal);
3797
4485
  const tokens = zeroTokenUsage();
4486
+ let tokensKnown = true;
3798
4487
  let usd = 0;
4488
+ let usdKnown = true;
3799
4489
  let turns = 0;
3800
4490
  let lastText = "";
3801
4491
  const toolCalls = [];
3802
4492
  let nextPrompt = taskToPrompt(args.task);
3803
- const system = args.spec.profile.prompt?.systemPrompt;
3804
4493
  for (let t = 0; t < args.maxTurns; t += 1) {
3805
4494
  const pending = inbox.drain();
3806
4495
  if (pending.length) {
@@ -3809,10 +4498,6 @@ async function* streamBridgeSession(args) {
3809
4498
  }
3810
4499
  if (nextPrompt === void 0) break;
3811
4500
  const messages = [];
3812
- if (t === 0 && typeof system === "string" && system.length > 0) messages.push({
3813
- role: "system",
3814
- content: system
3815
- });
3816
4501
  messages.push({
3817
4502
  role: "user",
3818
4503
  content: nextPrompt
@@ -3824,45 +4509,46 @@ async function* streamBridgeSession(args) {
3824
4509
  if (external.aborted) turnController.abort();
3825
4510
  else external.addEventListener("abort", abortTurn);
3826
4511
  interruptSig.addEventListener("abort", abortTurn, { once: true });
3827
- const timer = seam.timeoutMs ? setTimeout(abortTurn, seam.timeoutMs) : void 0;
4512
+ let timedOut = false;
4513
+ const timer = seam.timeoutMs ? setTimeout(() => {
4514
+ timedOut = true;
4515
+ abortTurn();
4516
+ }, seam.timeoutMs) : void 0;
3828
4517
  const cleanup = () => {
3829
4518
  external.removeEventListener("abort", abortTurn);
3830
4519
  if (timer) clearTimeout(timer);
3831
4520
  };
3832
- let res;
3833
- try {
3834
- res = await bridgeStreamPost(seam.bridgeUrl, {
3835
- bearer: seam.bridgeBearer,
3836
- sessionId: args.sessionId,
3837
- body: {
3838
- model: seam.model,
3839
- stream: true,
3840
- session_id: args.sessionId,
3841
- ...seam.cwd ? { cwd: seam.cwd } : {},
3842
- ...seam.agentProfile ? { agent_profile: seam.agentProfile } : {},
3843
- messages
3844
- },
3845
- signal: turnController.signal
3846
- });
3847
- } catch (e) {
3848
- cleanup();
3849
- if (e instanceof DOMException && e.name === "AbortError" && interruptSig.aborted && !args.signal.aborted && !args.controller.signal.aborted) continue;
3850
- throw e;
3851
- }
3852
- if (!res.ok) {
3853
- cleanup();
3854
- throw new ValidationError(`bridgeExecutor: bridge ${res.status}: ${(await res.text()).slice(0, 300)}`);
3855
- }
3856
- if (!res.body) {
3857
- cleanup();
3858
- throw new ValidationError("bridgeExecutor: bridge response had no body to stream");
3859
- }
4521
+ const activeRun = {
4522
+ id: `bridge-run-${randomUUID()}`,
4523
+ lastEventId: 0,
4524
+ terminal: false
4525
+ };
4526
+ args.activeRuns.set(activeRun.id, activeRun);
4527
+ const requestBody = {
4528
+ model: seam.model,
4529
+ stream: true,
4530
+ run_id: activeRun.id,
4531
+ session_id: args.sessionId,
4532
+ ...seam.cwd ? { cwd: seam.cwd } : {},
4533
+ agent_profile: args.profile,
4534
+ messages
4535
+ };
3860
4536
  let turnText = "";
4537
+ let turnTokensKnown = false;
4538
+ let turnUsdKnown = false;
4539
+ let interrupted = false;
3861
4540
  try {
3862
- for await (const chunk of parseSseChatStream(res.body)) {
4541
+ for await (const chunk of streamDurableBridgeRun({
4542
+ seam,
4543
+ sessionId: args.sessionId,
4544
+ body: requestBody,
4545
+ signal: turnController.signal,
4546
+ run: activeRun
4547
+ })) {
3863
4548
  if (chunk.content) turnText += chunk.content;
3864
4549
  if (chunk.toolCall) toolCalls.push(chunk.toolCall);
3865
4550
  if (chunk.usage) {
4551
+ turnTokensKnown = true;
3866
4552
  tokens.input += chunk.usage.input;
3867
4553
  tokens.output += chunk.usage.output;
3868
4554
  yield {
@@ -3871,26 +4557,43 @@ async function* streamBridgeSession(args) {
3871
4557
  output: chunk.usage.output
3872
4558
  };
3873
4559
  }
3874
- if (typeof chunk.cost === "number" && chunk.cost > 0) {
3875
- usd += chunk.cost;
3876
- yield {
3877
- kind: "cost",
3878
- usd: chunk.cost
3879
- };
4560
+ if (typeof chunk.cost === "number") {
4561
+ turnUsdKnown = true;
4562
+ if (chunk.cost > 0) {
4563
+ usd += chunk.cost;
4564
+ yield {
4565
+ kind: "cost",
4566
+ usd: chunk.cost
4567
+ };
4568
+ }
3880
4569
  }
3881
4570
  }
4571
+ } catch (error) {
4572
+ if (interruptSig.aborted && !args.signal.aborted && !args.controller.signal.aborted) {
4573
+ if (!await cancelBridgeRunToTerminal(seam, activeRun, "infinity", external)) throw new ValidationError(`bridgeExecutor: interrupted run ${activeRun.id} did not reach terminal state`);
4574
+ interrupted = true;
4575
+ } else {
4576
+ if (timedOut && !activeRun.terminal) await requestBridgeRunCancellation(seam, activeRun, 0);
4577
+ throw error;
4578
+ }
3882
4579
  } finally {
3883
4580
  cleanup();
3884
4581
  }
4582
+ if (interruptSig.aborted && !args.signal.aborted && !args.controller.signal.aborted) interrupted = true;
3885
4583
  turns += 1;
4584
+ if (!turnTokensKnown) tokensKnown = false;
4585
+ if (!turnUsdKnown) usdKnown = false;
3886
4586
  yield { kind: "iteration" };
3887
- if (turnText) lastText = turnText;
4587
+ if (!interrupted && turnText) lastText = turnText;
4588
+ if (interrupted) continue;
3888
4589
  if (inbox.pending() === 0) break;
3889
4590
  }
3890
4591
  const spent = {
3891
4592
  iterations: turns,
3892
4593
  tokens,
4594
+ ...tokensKnown ? {} : { tokensKnown: false },
3893
4595
  usd,
4596
+ ...usdKnown ? {} : { usdKnown: false },
3894
4597
  ms: Date.now() - started
3895
4598
  };
3896
4599
  const out = {
@@ -3907,6 +4610,69 @@ async function* streamBridgeSession(args) {
3907
4610
  spent
3908
4611
  });
3909
4612
  }
4613
+ const BRIDGE_MAX_RECONNECTS = 3;
4614
+ const BRIDGE_CANCEL_LONG_POLL_MS = 3e4;
4615
+ const BRIDGE_BRUTAL_KILL_WAIT_MS = 150;
4616
+ /**
4617
+ * Drain one server-owned bridge run. A transport loss replays from the last
4618
+ * contiguous event id under the SAME run id and request bytes. No unnumbered,
4619
+ * duplicate, or skipped event is accepted: an exact replay contract that
4620
+ * cannot prove continuity fails instead of returning a plausible partial answer.
4621
+ */
4622
+ async function* streamDurableBridgeRun(args) {
4623
+ let reconnects = 0;
4624
+ let pendingUpstreamError;
4625
+ for (;;) {
4626
+ let res;
4627
+ try {
4628
+ res = await bridgeStreamPost(args.seam.bridgeUrl, {
4629
+ bearer: args.seam.bridgeBearer,
4630
+ sessionId: args.sessionId,
4631
+ runId: args.run.id,
4632
+ afterEventId: args.run.lastEventId,
4633
+ body: args.body,
4634
+ signal: args.signal
4635
+ });
4636
+ } catch (error) {
4637
+ if (args.signal.aborted) throw error;
4638
+ if (reconnects >= BRIDGE_MAX_RECONNECTS) throw new ValidationError(`bridgeExecutor: run ${args.run.id} disconnected before terminal acknowledgement after ${reconnects + 1} attempts: ${errorMessage$1(error)}`);
4639
+ reconnects += 1;
4640
+ continue;
4641
+ }
4642
+ if (!res.ok) throw new ValidationError(`bridgeExecutor: bridge ${res.status}: ${(await res.text()).slice(0, 300)}`);
4643
+ if (!res.body) throw new ValidationError("bridgeExecutor: bridge response had no body to stream");
4644
+ assertBridgeResponseIdentity(res, args.run);
4645
+ let sawDone = false;
4646
+ try {
4647
+ for await (const event of parseSseChatStream(res.body)) {
4648
+ if (event.kind === "done") {
4649
+ sawDone = true;
4650
+ break;
4651
+ }
4652
+ const expected = args.run.lastEventId + 1;
4653
+ if (event.id !== expected) throw new ValidationError(`bridgeExecutor: run ${args.run.id} replay gap: expected event ${expected}, received ${event.id}`);
4654
+ args.run.lastEventId = event.id;
4655
+ if (event.error) pendingUpstreamError = event.error;
4656
+ if (event.chunk) yield event.chunk;
4657
+ }
4658
+ } catch (error) {
4659
+ if (args.signal.aborted) throw error;
4660
+ if (error instanceof ValidationError) throw error;
4661
+ if (reconnects >= BRIDGE_MAX_RECONNECTS) throw new ValidationError(`bridgeExecutor: run ${args.run.id} stream disconnected before terminal acknowledgement after ${reconnects + 1} attempts: ${errorMessage$1(error)}`);
4662
+ reconnects += 1;
4663
+ continue;
4664
+ }
4665
+ if (sawDone) {
4666
+ args.run.terminal = true;
4667
+ if (pendingUpstreamError) throw pendingUpstreamError;
4668
+ return;
4669
+ }
4670
+ if (pendingUpstreamError) throw pendingUpstreamError;
4671
+ if (args.signal.aborted) throw new DOMException("bridgeExecutor: turn aborted", "AbortError");
4672
+ if (reconnects >= BRIDGE_MAX_RECONNECTS) throw new ValidationError(`bridgeExecutor: run ${args.run.id} ended without terminal acknowledgement after ${reconnects + 1} attempts`);
4673
+ reconnects += 1;
4674
+ }
4675
+ }
3910
4676
  /**
3911
4677
  * POST one streamed turn to the cli-bridge over the `node:http(s)` core client
3912
4678
  * instead of global `fetch`. The bridge runs a harness CLI and streams SSE only
@@ -3934,15 +4700,22 @@ function bridgeStreamPost(url, args) {
3934
4700
  "content-type": "application/json",
3935
4701
  authorization: `Bearer ${args.bearer}`,
3936
4702
  "x-session-id": args.sessionId,
4703
+ "x-run-id": args.runId,
4704
+ ...args.afterEventId > 0 ? { "last-event-id": String(args.afterEventId) } : {},
3937
4705
  "content-length": Buffer.byteLength(payload)
3938
4706
  },
3939
4707
  timeout: 0
3940
4708
  }, (res) => {
4709
+ response = res;
4710
+ res.once("close", () => args.signal.removeEventListener("abort", onAbort));
3941
4711
  const status = res.statusCode ?? 0;
4712
+ const ok = status >= 200 && status < 300;
4713
+ const body = Readable.toWeb(res);
3942
4714
  resolve({
3943
- ok: status >= 200 && status < 300,
4715
+ ok,
3944
4716
  status,
3945
- body: Readable.toWeb(res),
4717
+ headers: res.headers,
4718
+ body,
3946
4719
  text: async () => {
3947
4720
  const chunks = [];
3948
4721
  for await (const c of res) chunks.push(c);
@@ -3950,8 +4723,10 @@ function bridgeStreamPost(url, args) {
3950
4723
  }
3951
4724
  });
3952
4725
  });
4726
+ let response;
3953
4727
  const onAbort = () => {
3954
4728
  req.destroy(new DOMException("bridgeExecutor: turn aborted", "AbortError"));
4729
+ if (response && "destroy" in response && typeof response.destroy === "function") response.destroy(new DOMException("bridgeExecutor: turn aborted", "AbortError"));
3955
4730
  };
3956
4731
  if (args.signal.aborted) onAbort();
3957
4732
  else args.signal.addEventListener("abort", onAbort, { once: true });
@@ -3959,16 +4734,109 @@ function bridgeStreamPost(url, args) {
3959
4734
  args.signal.removeEventListener("abort", onAbort);
3960
4735
  reject(e);
3961
4736
  });
3962
- req.on("close", () => args.signal.removeEventListener("abort", onAbort));
4737
+ req.on("close", () => {
4738
+ if (!response) args.signal.removeEventListener("abort", onAbort);
4739
+ });
3963
4740
  req.write(payload);
3964
4741
  req.end();
3965
4742
  });
3966
4743
  }
4744
+ function bridgeHeader(headers, name) {
4745
+ const raw = headers[name.toLowerCase()];
4746
+ if (Array.isArray(raw)) return raw.length === 1 ? raw[0] : void 0;
4747
+ return raw;
4748
+ }
4749
+ function assertBridgeResponseIdentity(response, run) {
4750
+ assertBridgeIdentityHeaders(response.headers, run);
4751
+ }
4752
+ function assertBridgeIdentityHeaders(headers, run) {
4753
+ const responseRunId = bridgeHeader(headers, "x-run-id");
4754
+ if (responseRunId !== run.id) throw new ValidationError(`bridgeExecutor: bridge run identity mismatch: expected ${run.id}, received ${responseRunId ?? "missing"}`);
4755
+ const digest = bridgeHeader(headers, "x-run-request-digest");
4756
+ if (!digest || !/^sha256:[a-f0-9]{64}$/u.test(digest)) throw new ValidationError("bridgeExecutor: bridge response omitted a valid request digest");
4757
+ if (run.requestDigest !== void 0 && run.requestDigest !== digest) throw new ValidationError(`bridgeExecutor: bridge request digest changed for run ${run.id}: expected ${run.requestDigest}, received ${digest}`);
4758
+ run.requestDigest = digest;
4759
+ }
4760
+ /** Explicitly cancel one server-owned run and long-poll for its terminal snapshot. */
4761
+ function bridgeCancelPost(seam, run, waitMs) {
4762
+ const target = new URL(`${seam.bridgeUrl.replace(/\/$/, "")}/v1/runs/${encodeURIComponent(run.id)}/cancel`);
4763
+ target.searchParams.set("wait_ms", String(waitMs));
4764
+ const requestFn = target.protocol === "https:" ? request$1 : request;
4765
+ return new Promise((resolve, reject) => {
4766
+ const req = requestFn(target, {
4767
+ method: "POST",
4768
+ headers: {
4769
+ authorization: `Bearer ${seam.bridgeBearer}`,
4770
+ "x-run-id": run.id,
4771
+ "content-length": "0"
4772
+ },
4773
+ timeout: 0
4774
+ }, (res) => {
4775
+ (async () => {
4776
+ const chunks = [];
4777
+ for await (const chunk of res) chunks.push(Buffer.from(chunk));
4778
+ resolve({
4779
+ status: res.statusCode ?? 0,
4780
+ headers: res.headers,
4781
+ text: Buffer.concat(chunks).toString("utf8")
4782
+ });
4783
+ })().catch(reject);
4784
+ });
4785
+ req.on("error", reject);
4786
+ req.end();
4787
+ });
4788
+ }
4789
+ async function requestBridgeRunCancellation(seam, run, waitMs) {
4790
+ if (run.terminal) return true;
4791
+ if (run.cancelInFlight) return run.cancelInFlight;
4792
+ const work = (async () => {
4793
+ const response = await bridgeCancelPost(seam, run, waitMs);
4794
+ if (response.status === 404) throw new ValidationError(`bridgeExecutor: bridge no longer knows run ${run.id}; terminal state is unproven`);
4795
+ if (response.status !== 200 && response.status !== 202) throw new ValidationError(`bridgeExecutor: cancel ${run.id} returned ${response.status}: ${response.text.slice(0, 300)}`);
4796
+ assertBridgeIdentityHeaders(response.headers, run);
4797
+ let parsed;
4798
+ try {
4799
+ parsed = JSON.parse(response.text);
4800
+ } catch {
4801
+ throw new ValidationError(`bridgeExecutor: cancel ${run.id} returned invalid JSON`);
4802
+ }
4803
+ if (parsed.run?.id !== run.id || parsed.run.requestDigest !== run.requestDigest || typeof parsed.terminal !== "boolean" || typeof parsed.run.terminal !== "boolean" || parsed.terminal !== parsed.run.terminal) throw new ValidationError(`bridgeExecutor: cancel ${run.id} returned an inconsistent terminal snapshot`);
4804
+ if (response.status === 200 && parsed.terminal === true) {
4805
+ run.terminal = true;
4806
+ return true;
4807
+ }
4808
+ if (response.status === 202 && parsed.terminal === false) return false;
4809
+ throw new ValidationError(`bridgeExecutor: cancel ${run.id} status ${response.status} disagreed with terminal=${String(parsed.terminal)}`);
4810
+ })();
4811
+ run.cancelInFlight = work;
4812
+ try {
4813
+ return await work;
4814
+ } finally {
4815
+ if (run.cancelInFlight === work) run.cancelInFlight = void 0;
4816
+ }
4817
+ }
4818
+ async function cancelBridgeRunToTerminal(seam, run, grace, stopSignal) {
4819
+ if (run.terminal) return true;
4820
+ const deadline = grace === "infinity" ? void 0 : Date.now() + (grace === "brutalKill" ? BRIDGE_BRUTAL_KILL_WAIT_MS : Math.max(0, grace));
4821
+ let first = true;
4822
+ for (;;) {
4823
+ const remaining = deadline === void 0 ? BRIDGE_CANCEL_LONG_POLL_MS : deadline - Date.now();
4824
+ if (!first && remaining <= 0) return false;
4825
+ if (!first && stopSignal?.aborted) return false;
4826
+ if (await requestBridgeRunCancellation(seam, run, Math.max(0, Math.min(stopSignal ? 1e3 : BRIDGE_CANCEL_LONG_POLL_MS, deadline === void 0 ? remaining : Math.max(0, remaining))))) return true;
4827
+ first = false;
4828
+ if (deadline !== void 0 && Date.now() >= deadline) return false;
4829
+ await new Promise((resolve) => setTimeout(resolve, 10));
4830
+ }
4831
+ }
4832
+ function errorMessage$1(error) {
4833
+ return error instanceof Error ? error.message : String(error);
4834
+ }
3967
4835
  /**
3968
4836
  * Parse cli-bridge's OpenAI-compatible SSE stream into normalized chunks. Each
3969
- * `data:` line is an OpenAI chat-completion chunk (`choices[].delta`); `[DONE]`
3970
- * and SSE comments (`:` keepalives) terminate/skip. Mirrors how `streamSandboxLeaf`
3971
- * folds a box's event stream same `UsageEvent` currency, different wire shape.
4837
+ * `data:` line is an OpenAI chat-completion chunk (`choices[].delta`). Every
4838
+ * run-owned frame, including an id-only comment, is returned so the caller can
4839
+ * prove a contiguous replay sequence. Transport keepalives have no id and are ignored.
3972
4840
  */
3973
4841
  async function* parseSseChatStream(body) {
3974
4842
  const reader = body.getReader();
@@ -3979,18 +4847,18 @@ async function* parseSseChatStream(body) {
3979
4847
  const { done, value } = await reader.read();
3980
4848
  if (done) break;
3981
4849
  buf += decoder.decode(value, { stream: true });
3982
- let sep = buf.indexOf("\n\n");
3983
- while (sep !== -1) {
3984
- const frame = buf.slice(0, sep);
3985
- buf = buf.slice(sep + 2);
3986
- const chunk = parseSseFrame(frame);
3987
- if (chunk === "done") return;
3988
- if (chunk) yield chunk;
3989
- sep = buf.indexOf("\n\n");
4850
+ let separator = /\r?\n\r?\n/u.exec(buf);
4851
+ while (separator) {
4852
+ const frame = buf.slice(0, separator.index);
4853
+ buf = buf.slice(separator.index + separator[0].length);
4854
+ const event = parseSseFrame(frame);
4855
+ if (event) yield event;
4856
+ separator = /\r?\n\r?\n/u.exec(buf);
3990
4857
  }
3991
4858
  }
4859
+ buf += decoder.decode();
3992
4860
  const tail = parseSseStreamTail(buf);
3993
- if (tail !== void 0 && tail !== "done") yield tail;
4861
+ if (tail !== void 0) yield tail;
3994
4862
  } finally {
3995
4863
  reader.releaseLock();
3996
4864
  }
@@ -4012,25 +4880,41 @@ function parseSseStreamTail(buf) {
4012
4880
  }
4013
4881
  if (parsed.error) throw new ValidationError(`bridgeExecutor: bridge upstream error: ${parsed.error.message ?? parsed.error.type ?? "unknown"}`);
4014
4882
  }
4015
- /** Parse one SSE frame (possibly multi-line `data:`/comment) into a chunk, `'done'`,
4016
- * or undefined (comment/keepalive/empty). */
4883
+ /** Parse one SSE frame into a numbered run event, terminal marker, or unnumbered keepalive. */
4017
4884
  function parseSseFrame(frame) {
4018
4885
  const dataLines = [];
4886
+ let id;
4019
4887
  for (const rawLine of frame.split("\n")) {
4020
4888
  const line = rawLine.replace(/\r$/, "");
4021
4889
  if (!line || line.startsWith(":")) continue;
4890
+ if (line.startsWith("id:")) {
4891
+ const rawId = line.slice(3).trim();
4892
+ if (!/^[1-9][0-9]*$/u.test(rawId)) throw new ValidationError(`bridgeExecutor: invalid SSE event id ${JSON.stringify(rawId)}`);
4893
+ const parsedId = Number(rawId);
4894
+ if (!Number.isSafeInteger(parsedId)) throw new ValidationError(`bridgeExecutor: SSE event id exceeds safe integer range`);
4895
+ id = parsedId;
4896
+ continue;
4897
+ }
4022
4898
  if (line.startsWith("data:")) dataLines.push(line.slice(5).trimStart());
4023
4899
  }
4024
- if (dataLines.length === 0) return void 0;
4900
+ if (dataLines.length === 0) return id === void 0 ? void 0 : {
4901
+ kind: "event",
4902
+ id
4903
+ };
4025
4904
  const data = dataLines.join("\n");
4026
- if (data === "[DONE]") return "done";
4905
+ if (data === "[DONE]") return { kind: "done" };
4027
4906
  let parsed;
4028
4907
  try {
4029
4908
  parsed = JSON.parse(data);
4030
4909
  } catch {
4031
- return;
4910
+ throw new ValidationError("bridgeExecutor: bridge emitted a non-JSON SSE data frame");
4032
4911
  }
4033
- if (parsed.error) throw new ValidationError(`bridgeExecutor: bridge stream error: ${parsed.error.message ?? parsed.error.type ?? "unknown"}`);
4912
+ if (id === void 0) throw new ValidationError("bridgeExecutor: bridge emitted an unnumbered run event");
4913
+ if (parsed.error) return {
4914
+ kind: "event",
4915
+ id,
4916
+ error: new ValidationError(`bridgeExecutor: bridge stream error: ${parsed.error.message ?? parsed.error.type ?? "unknown"}`)
4917
+ };
4034
4918
  const out = {};
4035
4919
  const choice = parsed.choices?.[0];
4036
4920
  const content = choice?.delta?.content ?? choice?.message?.content;
@@ -4043,7 +4927,11 @@ function parseSseFrame(frame) {
4043
4927
  output: u.completion_tokens ?? 0
4044
4928
  };
4045
4929
  if (typeof u?.cost === "number") out.cost = u.cost;
4046
- return Object.keys(out).length > 0 ? out : void 0;
4930
+ return {
4931
+ kind: "event",
4932
+ id,
4933
+ ...Object.keys(out).length > 0 ? { chunk: out } : {}
4934
+ };
4047
4935
  }
4048
4936
  function bridgeWorktreeExecutor(spec, ctx, seam) {
4049
4937
  const bridge = seam.bridge;
@@ -4051,6 +4939,9 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
4051
4939
  if (!bridge.bridgeUrl || !bridge.bridgeBearer) throw new ValidationError("cliWorktreeExecutor: bridge.bridgeUrl + bridge.bridgeBearer required");
4052
4940
  const runId = seam.runId ?? randomUUID();
4053
4941
  const sessionId = bridge.sessionId ?? `bridge-worktree-${runId}`;
4942
+ const attemptId = ctx.node?.attemptId ?? newExecutionAttemptId(runId);
4943
+ const effectiveProfile = agentProfileSchema.parse(mergeAgentProfiles(spec.profile, bridge.agentProfile) ?? spec.profile);
4944
+ const model = bridgeCellModel(bridge.model, ctx, effectiveProfile);
4054
4945
  const controller = new AbortController();
4055
4946
  const pending = [];
4056
4947
  let inner;
@@ -4075,11 +4966,11 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
4075
4966
  }
4076
4967
  pending.push(msg);
4077
4968
  };
4078
- return {
4969
+ return attestRuntimeOwnedExecutor({
4079
4970
  runtime: "cli",
4080
4971
  budgetExempt: seam.budgetExempt ?? false,
4081
4972
  deliver,
4082
- execute(_task, signal) {
4973
+ execute(task, signal) {
4083
4974
  return (async function* bridgeWorktreeStream() {
4084
4975
  const started = Date.now();
4085
4976
  const linked = mergeAbortSignals(signal, controller.signal);
@@ -4095,10 +4986,10 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
4095
4986
  const bridgeSeam = {
4096
4987
  bridgeUrl: bridge.bridgeUrl,
4097
4988
  bridgeBearer: bridge.bridgeBearer,
4098
- model: resolveBridgeWorktreeModel(spec, bridge),
4099
4989
  cwd: worktree.path,
4100
4990
  sessionId,
4101
- ...bridge.agentProfile ? { agentProfile: bridge.agentProfile } : { agentProfile: spec.profile },
4991
+ ...bridge.model ? { model: bridge.model } : {},
4992
+ ...bridge.agentProfile ? { agentProfile: bridge.agentProfile } : {},
4102
4993
  ...bridge.timeoutMs !== void 0 ? { timeoutMs: bridge.timeoutMs } : {},
4103
4994
  ...bridge.maxTurns !== void 0 ? { maxTurns: bridge.maxTurns } : {}
4104
4995
  };
@@ -4112,8 +5003,8 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
4112
5003
  };
4113
5004
  inner = bridgeExecutor(spec, bridgeCtx);
4114
5005
  for (const msg of pending.splice(0)) inner.deliver?.(msg);
4115
- const run = inner.execute(seam.taskPrompt, linked);
4116
- if (isAsyncIterable(run)) {
5006
+ const run = inner.execute(task, linked);
5007
+ if (isAsyncIterable$1(run)) {
4117
5008
  for await (const event of run) yield event;
4118
5009
  bridgeArtifact = inner.resultArtifact();
4119
5010
  } else bridgeArtifact = await run;
@@ -4179,13 +5070,48 @@ function bridgeWorktreeExecutor(spec, ctx, seam) {
4179
5070
  if (!artifact) throw new ValidationError("cliWorktreeExecutor: bridge resultArtifact() read before stream drained");
4180
5071
  return artifact;
4181
5072
  }
4182
- };
4183
- }
4184
- function resolveBridgeWorktreeModel(spec, bridge) {
4185
- if (bridge.model) return bridge.model;
4186
- const model = spec.profile.model?.default;
4187
- if (typeof model === "string" && model.length > 0) return model;
4188
- throw new ValidationError("cliWorktreeExecutor: bridge.model or AgentProfile.model.default required");
5073
+ }, {
5074
+ effectiveProfile,
5075
+ backend: "bridge-worktree",
5076
+ model: model ? {
5077
+ status: "known",
5078
+ id: model
5079
+ } : {
5080
+ status: "unknown",
5081
+ reason: "bridge worktree profile did not select a model"
5082
+ },
5083
+ execution: {
5084
+ kind: "worktree-session",
5085
+ id: `${runId}:${sessionId}`
5086
+ },
5087
+ materializer: "bridge-worktree-agent-profile",
5088
+ plan: {
5089
+ kind: "bridge-worktree-session",
5090
+ runId,
5091
+ sessionId,
5092
+ baseRef: seam.baseRef ?? "HEAD",
5093
+ model: model ?? null,
5094
+ testCmd: seam.testCmd ?? null,
5095
+ typecheckCmd: seam.typecheckCmd ?? null,
5096
+ checkTimeoutMs: seam.checkTimeoutMs ?? seam.harnessTimeoutMs ?? bridge.timeoutMs ?? 300 * 1e3,
5097
+ checkOutputCap: seam.checkOutputCap ?? 16e3
5098
+ }
5099
+ }, {
5100
+ attemptId,
5101
+ binding: {
5102
+ bridgeUrl: bridge.bridgeUrl,
5103
+ effectiveProfile,
5104
+ model: model ?? null,
5105
+ repoRoot: seam.repoRoot,
5106
+ runId,
5107
+ sessionId
5108
+ },
5109
+ descriptor: {
5110
+ kind: "bridge-worktree-session",
5111
+ transport: "http",
5112
+ backend: "bridge-worktree"
5113
+ }
5114
+ });
4189
5115
  }
4190
5116
  function bridgeOutputText(out) {
4191
5117
  if (typeof out === "string") return out;
@@ -4199,7 +5125,7 @@ function bridgeOutputText(out) {
4199
5125
  return String(out);
4200
5126
  }
4201
5127
  }
4202
- function isAsyncIterable(value) {
5128
+ function isAsyncIterable$1(value) {
4203
5129
  return value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
4204
5130
  }
4205
5131
  /**
@@ -4209,14 +5135,14 @@ function isAsyncIterable(value) {
4209
5135
  */
4210
5136
  const cliWorktreeExecutor = (spec, ctx) => {
4211
5137
  const seam = readSeam(ctx, cliWorktreeSeamKey, "cli-worktree");
4212
- if (!seam.repoRoot || !seam.taskPrompt) throw new ValidationError("cliWorktreeExecutor: CliWorktreeSeam.repoRoot + taskPrompt required");
5138
+ if (!seam.repoRoot) throw new ValidationError("cliWorktreeExecutor: CliWorktreeSeam.repoRoot required");
4213
5139
  if (seam.bridge) return bridgeWorktreeExecutor(spec, ctx, seam);
4214
5140
  if (!seam.harness) throw new ValidationError("cliWorktreeExecutor: CliWorktreeSeam.harness required when bridge is not set");
4215
5141
  return createWorktreeCliExecutor({
4216
5142
  repoRoot: seam.repoRoot,
4217
5143
  profile: spec.profile,
4218
5144
  harness: seam.harness,
4219
- taskPrompt: seam.taskPrompt,
5145
+ ...seam.taskPrompt !== void 0 ? { taskPrompt: seam.taskPrompt } : {},
4220
5146
  ...seam.runId ? { runId: seam.runId } : {},
4221
5147
  ...seam.baseRef ? { baseRef: seam.baseRef } : {},
4222
5148
  ...seam.harnessTimeoutMs !== void 0 ? { harnessTimeoutMs: seam.harnessTimeoutMs } : {},
@@ -4228,9 +5154,110 @@ const cliWorktreeExecutor = (spec, ctx) => {
4228
5154
  ...seam.checkOutputCap !== void 0 ? { checkOutputCap: seam.checkOutputCap } : {},
4229
5155
  ...seam.runGit ? { runGit: seam.runGit } : {},
4230
5156
  ...seam.runCommand ? { runCommand: seam.runCommand } : {},
4231
- ...seam.budgetExempt !== void 0 ? { budgetExempt: seam.budgetExempt } : {}
5157
+ ...seam.budgetExempt !== void 0 ? { budgetExempt: seam.budgetExempt } : {},
5158
+ ...ctx.node?.attemptId !== void 0 ? { executionAttemptId: ctx.node.attemptId } : {}
4232
5159
  });
4233
5160
  };
5161
+ /** Capture one public executor configuration at its call boundary. All data that selects policy,
5162
+ * model, process, limits, profile overlays, or backend behavior is detached and deeply frozen.
5163
+ * Explicit service/function fields remain live by reference because they are executable ports,
5164
+ * not portable configuration. */
5165
+ function snapshotExecutorConfig(config) {
5166
+ switch (config.backend) {
5167
+ case "router-tools": {
5168
+ const { executeToolCall, onToolStep, ...decisionData } = config;
5169
+ const snapshot = detachedSnapshot(decisionData, "createExecutor router-tools config");
5170
+ return Object.freeze({
5171
+ ...snapshot,
5172
+ executeToolCall,
5173
+ ...onToolStep === void 0 ? {} : { onToolStep }
5174
+ });
5175
+ }
5176
+ case "cli-worktree": {
5177
+ const { runGit, runCommand, ...decisionData } = config;
5178
+ const snapshot = detachedSnapshot(decisionData, "createExecutor cli-worktree config");
5179
+ return Object.freeze({
5180
+ ...snapshot,
5181
+ ...runGit === void 0 ? {} : { runGit },
5182
+ ...runCommand === void 0 ? {} : { runCommand }
5183
+ });
5184
+ }
5185
+ case "provider": {
5186
+ const { provider, registry, taskToTurn, ...decisionData } = config;
5187
+ const snapshot = detachedSnapshot(decisionData, "createExecutor provider config");
5188
+ const resolvedProvider = resolveAgentEnvironmentProvider(provider, registry);
5189
+ return Object.freeze({
5190
+ ...snapshot,
5191
+ provider: resolvedProvider,
5192
+ ...taskToTurn === void 0 ? {} : { taskToTurn }
5193
+ });
5194
+ }
5195
+ case "sandbox": {
5196
+ const { sandboxClient, loopCtx, ...decisionData } = config;
5197
+ if (loopCtx === void 0) {
5198
+ const snapshot = detachedSnapshot(decisionData, "createExecutor sandbox config");
5199
+ return Object.freeze({
5200
+ ...snapshot,
5201
+ sandboxClient
5202
+ });
5203
+ }
5204
+ const { hooks, traceEmitter, onSandboxEvent, runHandle, ...loopDecisionData } = loopCtx;
5205
+ const snapshot = detachedSnapshot({
5206
+ ...decisionData,
5207
+ loopCtx: loopDecisionData
5208
+ }, "createExecutor sandbox config");
5209
+ const loopSnapshot = snapshot.loopCtx;
5210
+ return Object.freeze({
5211
+ ...snapshot,
5212
+ sandboxClient,
5213
+ loopCtx: Object.freeze({
5214
+ ...loopSnapshot,
5215
+ ...hooks === void 0 ? {} : { hooks },
5216
+ ...traceEmitter === void 0 ? {} : { traceEmitter },
5217
+ ...onSandboxEvent === void 0 ? {} : { onSandboxEvent },
5218
+ ...runHandle === void 0 ? {} : { runHandle }
5219
+ })
5220
+ });
5221
+ }
5222
+ case "router":
5223
+ case "bridge":
5224
+ case "cli":
5225
+ case "pi": return detachedSnapshot(config, `createExecutor ${config.backend} config`);
5226
+ }
5227
+ }
5228
+ /** A backend config reused for multiple workers/managers cannot pin execution identity or carry a
5229
+ * profile overlay applied after Scope hashed the authored profile. Direct single-execution
5230
+ * `createExecutor` calls may still use those fields. */
5231
+ function captureReusableExecutorConfig(config, context) {
5232
+ const captured = snapshotExecutorConfig(config);
5233
+ if ((captured.backend === "bridge" ? captured.agentProfile : captured.backend === "cli-worktree" ? captured.bridge?.agentProfile : void 0) !== void 0) throw new ValidationError(`${context}: backend agentProfile overlays are not allowed because they change the effective profile after spawn identity is fixed`);
5234
+ const fixedIdentity = captured.backend === "bridge" && captured.sessionId !== void 0 ? "sessionId" : captured.backend === "cli-worktree" && captured.runId !== void 0 ? "runId" : captured.backend === "cli-worktree" && captured.bridge?.sessionId !== void 0 ? "bridge.sessionId" : void 0;
5235
+ if (fixedIdentity !== void 0) throw new ValidationError(`${context}: fixed ${fixedIdentity} is not allowed on a reusable backend; let each execution derive an isolated id`);
5236
+ return captured;
5237
+ }
5238
+ /** Bind one already-captured reusable backend to the durable identity of the execution that will
5239
+ * use it. Stateful bridge backends need an explicit external id: a random default isolates two
5240
+ * siblings but cannot reconnect a replacement process to the same harness session. Non-stateful
5241
+ * backends carry no external execution id and are returned unchanged. */
5242
+ function bindReusableExecutorExecutionId(captured, executionId) {
5243
+ if (typeof executionId !== "string" || executionId.length === 0) throw new ValidationError("bindReusableExecutorExecutionId: executionId must be a non-empty string");
5244
+ switch (captured.backend) {
5245
+ case "bridge": return Object.freeze({
5246
+ ...captured,
5247
+ sessionId: executionId
5248
+ });
5249
+ case "cli-worktree": return Object.freeze({
5250
+ ...captured,
5251
+ runId: executionId
5252
+ });
5253
+ case "router":
5254
+ case "router-tools":
5255
+ case "cli":
5256
+ case "provider":
5257
+ case "pi":
5258
+ case "sandbox": return captured;
5259
+ }
5260
+ }
4234
5261
  /**
4235
5262
  * The single built-in executor factory. Picks a leaf backend by data (`config.backend`),
4236
5263
  * injects the matching seam, and delegates to that backend's built-in implementation.
@@ -4240,8 +5267,10 @@ const cliWorktreeExecutor = (spec, ctx) => {
4240
5267
  * `UsageEvent` reporting channel.
4241
5268
  */
4242
5269
  function createExecutor(config) {
5270
+ const captured = snapshotExecutorConfig(config);
4243
5271
  return (spec, ctx) => {
4244
- const { backend, ...seam } = config;
5272
+ const { backend, ...seamData } = captured;
5273
+ const seam = Object.freeze(seamData);
4245
5274
  const seamed = {
4246
5275
  ...ctx,
4247
5276
  seams: {
@@ -4249,7 +5278,7 @@ function createExecutor(config) {
4249
5278
  [backend]: seam
4250
5279
  }
4251
5280
  };
4252
- switch (config.backend) {
5281
+ switch (captured.backend) {
4253
5282
  case "router": return routerInlineExecutor(spec, seamed);
4254
5283
  case "router-tools": return routerToolsInlineExecutor(spec, seamed);
4255
5284
  case "bridge": return bridgeExecutor(spec, seamed);
@@ -4258,10 +5287,41 @@ function createExecutor(config) {
4258
5287
  case "pi": return piExecutor(spec, seamed);
4259
5288
  case "provider": {
4260
5289
  const providerSeam = readSeam(seamed, providerSeamKey, "provider");
4261
- return providerAsExecutor(resolveAgentEnvironmentProvider(providerSeam.provider, providerSeam.registry), providerSeam)(spec, seamed);
5290
+ const provider = resolveAgentEnvironmentProvider(providerSeam.provider, providerSeam.registry);
5291
+ if (providerSeam.steering) {
5292
+ if (providerSeam.taskToTurn) throw new ValidationError("createExecutor(provider, steering): taskToTurn is not representable by the text-only steerable session");
5293
+ if (providerSeam.destroyOnSettle === false) throw new ValidationError("createExecutor(provider, steering): destroyOnSettle=false conflicts with the session-owned environment lifecycle");
5294
+ const harness = requiredProviderProfileHarness(spec, providerSeam);
5295
+ const sandboxClient = providerAsSandboxClient(provider, {
5296
+ defaults: {
5297
+ ...providerSeam.defaults ?? {},
5298
+ signal: seamed.signal
5299
+ },
5300
+ requireTerminalEvent: providerSeam.requireTerminalEvent,
5301
+ requireSession: true
5302
+ });
5303
+ const providerCtx = {
5304
+ ...seamed,
5305
+ seams: {
5306
+ ...seamed.seams,
5307
+ [sandboxSeamKey]: {
5308
+ sandboxClient,
5309
+ steering: providerSeam.steering
5310
+ }
5311
+ }
5312
+ };
5313
+ return {
5314
+ ...sandboxExecutor({
5315
+ ...spec,
5316
+ harness
5317
+ }, providerCtx),
5318
+ runtime: providerSeam.runtime ?? provider.name
5319
+ };
5320
+ }
5321
+ return providerAsExecutor(provider, providerSeam)(spec, seamed);
4262
5322
  }
4263
5323
  case "sandbox": {
4264
- const harness = spec.harness ?? config.harness ?? null;
5324
+ const harness = spec.harness ?? captured.harness ?? null;
4265
5325
  return sandboxExecutor({
4266
5326
  ...spec,
4267
5327
  harness
@@ -4270,6 +5330,13 @@ function createExecutor(config) {
4270
5330
  }
4271
5331
  };
4272
5332
  }
5333
+ function requiredProviderProfileHarness(spec, seam) {
5334
+ const harness = spec.profile.harness;
5335
+ if (harness === void 0) throw new ValidationError("createExecutor(provider, steering): AgentProfile.harness is required");
5336
+ if (spec.harness != null && spec.harness !== harness) throw new ValidationError(`createExecutor(provider, steering): AgentSpec.harness "${spec.harness}" conflicts with AgentProfile.harness "${harness}"`);
5337
+ if (seam.defaults?.backend !== void 0 && seam.defaults.backend !== harness) throw new ValidationError(`createExecutor(provider, steering): provider default backend "${seam.defaults.backend}" conflicts with AgentProfile.harness "${harness}"`);
5338
+ return harness;
5339
+ }
4273
5340
  /**
4274
5341
  * The open resolver/registry. Pre-registers the three built-ins under their
4275
5342
  * runtime tags (`'router'`, `'sandbox'`, `'cli'`) and accepts `register(name,
@@ -4277,8 +5344,8 @@ function createExecutor(config) {
4277
5344
  * without touching the registry at all. NOT a closed switch; registration + BYO
4278
5345
  * ARE the extension points.
4279
5346
  *
4280
- * `resolve` precedence (frozen in `ExecutorRegistry`): a BYO `spec.executor` →
4281
- * `harness === null` → the `'router'` factory; else a registered factory for the
5347
+ * `resolve` precedence (frozen in `ExecutorRegistry`): a BYO `spec.executorFactory` →
5348
+ * `spec.executor` → `harness === null` → the `'router'` factory; else a registered factory for the
4282
5349
  * harness-derived runtime (`'sandbox'` for any `BackendType`); else fail loud.
4283
5350
  */
4284
5351
  function createExecutorRegistry() {
@@ -4294,6 +5361,10 @@ function createExecutorRegistry() {
4294
5361
  factories.set(runtime, factory);
4295
5362
  },
4296
5363
  resolve(spec) {
5364
+ if (spec.executorFactory) return {
5365
+ succeeded: true,
5366
+ value: spec.executorFactory
5367
+ };
4297
5368
  if (spec.executor) {
4298
5369
  const byo = spec.executor;
4299
5370
  return {
@@ -4347,11 +5418,11 @@ function taskToPrompt(task) {
4347
5418
  }
4348
5419
  return JSON.stringify(task);
4349
5420
  }
4350
- /** Router messages from the opaque task + the profile's system prompt, when set. */
5421
+ /** Router messages from the opaque task + every portable profile prompt instruction. */
4351
5422
  function taskToMessages(task, spec) {
4352
5423
  const messages = [];
4353
- const system = spec.profile.prompt?.systemPrompt;
4354
- if (typeof system === "string" && system.length > 0) messages.push({
5424
+ const system = [spec.profile.prompt?.systemPrompt, ...spec.profile.prompt?.instructions ?? []].filter((line) => typeof line === "string" && line.trim().length > 0).join("\n");
5425
+ if (system.length > 0) messages.push({
4355
5426
  role: "system",
4356
5427
  content: system
4357
5428
  });
@@ -4449,8 +5520,9 @@ function createInMemoryRunContext(opts = {}) {
4449
5520
  * back on `Scope.resume` (rehydrated by `replaySpawnTree`) instead of being re-executed.
4450
5521
  *
4451
5522
  * Layout: `${dir}/spawn-journal.jsonl` (one JSONL record per event), `${dir}/blobs/` (one
4452
- * content-addressed JSON file per settled result), and `${dir}/coordination-log.jsonl` (questions
4453
- * + findings, replayed into a resumed driver). The directory is created on first write.
5523
+ * content-addressed JSON file per settled result), and `${dir}/coordination-log.jsonl`
5524
+ * (questions, findings, answer decisions, and authorized continuation receipts retained as
5525
+ * evidence). The directory is created on first write.
4454
5526
  *
4455
5527
  * Opt-in by construction — `createInMemoryRunContext()` is unchanged and stays the default, so no
4456
5528
  * existing consumer writes to disk or resumes unless it asks for this.
@@ -4508,168 +5580,6 @@ function watchTrace(source, opts = {}) {
4508
5580
  });
4509
5581
  }
4510
5582
  //#endregion
4511
- //#region src/runtime/supervise/dispatch.ts
4512
- /**
4513
- *
4514
- * `rollingDispatch` — the REFILLING dispatch policy over a `Scope`. It keeps `width` children in
4515
- * flight and admits the next queued unit of work the instant one settles, instead of waiting for
4516
- * a whole round to drain (`fanout`) or for a driver to decide again (`spawn → await → spawn`).
4517
- *
4518
- * The gap it closes: `fanout` opens every item at once and then drains — one round, no refill —
4519
- * and a driver brain's manual loop opens one worker per model turn. Between those two there was
4520
- * nothing that holds N slots full, which is why a 5-worker run can peak at 2 live workers and sit
4521
- * ~50% idle. This is a policy over the EXISTING `Scope.spawn` / `Scope.next` primitives: it adds
4522
- * no second admission path, so the conserved budget pool stays the only fence on total work and
4523
- * `width` is only a fence on simultaneous work.
4524
- *
4525
- * Fail-closed by construction: an admission rejection (`budget-exhausted` / `depth-exceeded`) is
4526
- * recorded and STOPS further admission — the loop then drains what is already live and returns.
4527
- * It never retries a rejected spawn against the same pool, and it never spawns past `width`.
4528
- *
4529
- * ── The concurrency-cap ledger (read this before adding a fourth cap) ──────────────────────────
4530
- *
4531
- * Three unrelated caps bound "how much runs at once" in this stack, at three different layers.
4532
- * They are NOT aware of each other, and the smallest one silently wins:
4533
- *
4534
- * 1. `CoordinationToolsOptions.maxLiveWorkers` (`src/mcp/tools/coordination.ts`) — supervisor
4535
- * level. How many workers may be spawned-but-not-settled at once; `spawn_agent` fails closed
4536
- * with `error: 'max-live-workers'` past it. Unset by default ⇒ NO cap at this layer.
4537
- * 2. `SandboxLineage`'s `maxConcurrency` / `DEFAULT_FORK_CONCURRENCY = 4`
4538
- * (`src/runtime/sandbox-lineage.ts`) — kernel level. How many BOXES one `runAgentRounds` fork wave
4539
- * provisions at once. It bounds a single leaf's fanout, not the supervisor's worker count.
4540
- * 3. A host's own live-box governor (e.g. the kernel's `ComputeGovernor`, `maxSandboxes = 4`) — fleet
4541
- * level. How many sandboxes may exist across the whole host process.
4542
- *
4543
- * The honest effective limit on simultaneous WORKERS is the minimum of the caps that apply to the
4544
- * worker layer — (1) and (3). (2) is a different unit (boxes per fork wave inside one leaf) and
4545
- * must not be min'd into it, or a 4-way fork inside one worker reads as a 4-worker ceiling.
4546
- * `effectiveConcurrency` computes that minimum in one place so a host derives ONE number and
4547
- * passes it to BOTH `maxLiveWorkers` and this dispatcher's `width`, instead of leaving a fleet
4548
- * governor of 4 and an unset worker fence as unrelated numbers.
4549
- *
4550
- * ── Why this is not a copy of the kernel's batch loop ──────────────────────────────────────────
4551
- *
4552
- * `runBatch` (`src/runtime/run-loop.ts`) runs the same hold-N-slots-full shape over the KERNEL's
4553
- * substrate: bare promises in a `Set`, raced with `Promise.race`, bounded by `maxConcurrency`.
4554
- * This one runs it over the SUPERVISOR's substrate — `Scope.spawn`'s atomic reservation against
4555
- * the conserved pool, `Scope.next`'s journaled settlement cursor, and the blob store behind each
4556
- * result. Neither can be expressed in the other's terms without dragging its whole substrate
4557
- * along: the kernel loop has no budget to reserve and no cursor to order settlements by, and this
4558
- * one cannot race raw promises because a settlement is only real once it is journaled. The shape
4559
- * repeating across the two deliberate layers is resonance, not duplication — do not "unify" them
4560
- * into a shared helper that would have to know about both.
4561
- *
4562
- * @experimental
4563
- */
4564
- /**
4565
- * Run the refilling dispatch loop over `scope` until the queue is dry (or a stop fires) and every
4566
- * admitted child has settled. Returns the settlements in cursor order plus the admission ledger.
4567
- *
4568
- * The loop is: fill free slots from `nextUnit` → `await scope.next()` → deliver the settlement →
4569
- * refill → repeat. Because the refill happens immediately after each settlement rather than after
4570
- * a whole round, a slow child never idles the other slots.
4571
- */
4572
- async function rollingDispatch(scope, opts) {
4573
- if (!Number.isInteger(opts.width) || opts.width < 1) throw new ValidationError(`rollingDispatch: width must be a positive integer, got ${String(opts.width)}`);
4574
- const settled = [];
4575
- const rejected = [];
4576
- const live = /* @__PURE__ */ new Set();
4577
- let admitted = 0;
4578
- let peakLive = 0;
4579
- let queueDry = false;
4580
- let stopReason = "drained";
4581
- const fill = async () => {
4582
- while (live.size < opts.width) {
4583
- if (queueDry) return;
4584
- if (scope.signal.aborted) {
4585
- stopReason = "aborted";
4586
- queueDry = true;
4587
- return;
4588
- }
4589
- if (opts.shouldStop?.() === true) {
4590
- stopReason = "stopped";
4591
- queueDry = true;
4592
- return;
4593
- }
4594
- const unit = await opts.nextUnit();
4595
- if (unit === void 0) {
4596
- queueDry = true;
4597
- return;
4598
- }
4599
- const res = scope.spawn(unit.agent, unit.task, unit.opts);
4600
- if (!res.ok) {
4601
- rejected.push(`${unit.opts.label}: ${res.reason}`);
4602
- stopReason = "not-admitted";
4603
- queueDry = true;
4604
- return;
4605
- }
4606
- live.add(res.handle.id);
4607
- admitted += 1;
4608
- if (live.size > peakLive) peakLive = live.size;
4609
- }
4610
- };
4611
- await fill();
4612
- while (live.size > 0) {
4613
- const s = await scope.next();
4614
- if (s === null) break;
4615
- live.delete(s.handle.id);
4616
- settled.push(s);
4617
- await opts.onSettled?.(s);
4618
- await fill();
4619
- }
4620
- return {
4621
- settled,
4622
- admitted,
4623
- rejected,
4624
- stopReason,
4625
- peakLive
4626
- };
4627
- }
4628
- /**
4629
- * Free worker slots under a simultaneity cap: `cap - live`, floored at 0, or `null` when there is
4630
- * no cap (the conserved pool is then the only fence and "free slots" is not a finite number).
4631
- * The one place the answer is computed, so the driver-facing tool payload and a dispatcher agree.
4632
- */
4633
- function freeSlots(liveCount, cap) {
4634
- if (cap === void 0 || cap <= 0) return null;
4635
- return Math.max(0, cap - liveCount);
4636
- }
4637
- /**
4638
- * The ONE honest effective limit on simultaneous workers: the minimum of the caps that actually
4639
- * bound the worker layer. Ignores unset/non-positive caps; returns `undefined` when no cap applies
4640
- * (uncapped — the conserved pool remains the only fence).
4641
- *
4642
- * Deliberately does NOT fold in `SandboxLineage`'s fork concurrency: that bounds boxes inside ONE
4643
- * leaf's fork wave, a different unit. Folding it in would report a 4-worker ceiling for what is
4644
- * really a 4-box fanout inside a single worker.
4645
- *
4646
- * Use it once, at the top of a run, and pass the result to BOTH `maxLiveWorkers` and a
4647
- * dispatcher's `width` — that is what turns three unrelated numbers into one.
4648
- */
4649
- function effectiveConcurrency(caps) {
4650
- const applicable = [caps.maxLiveWorkers, caps.maxSandboxes].filter((c) => typeof c === "number" && c > 0);
4651
- if (applicable.length === 0) return void 0;
4652
- return Math.min(...applicable);
4653
- }
4654
- /** Convenience: a `DispatchUnit` factory over a fixed array of tasks, for the common case where
4655
- * the queue is known up front and only the refill behavior is wanted. */
4656
- function queueOf(units, budget) {
4657
- let i = 0;
4658
- return () => {
4659
- const u = units[i];
4660
- if (u === void 0) return void 0;
4661
- i += 1;
4662
- return {
4663
- agent: u.agent,
4664
- task: u.task,
4665
- opts: {
4666
- budget,
4667
- label: u.label
4668
- }
4669
- };
4670
- };
4671
- }
4672
- //#endregion
4673
5583
  //#region src/runtime/supervise/event-bus.ts
4674
5584
  /** Create the child→parent coordination bus: one typed pipe for settled outputs, questions, and analyst findings, with a priority-ordered pull queue and a pass-through subscribe lane. */
4675
5585
  function createEventBus(now = Date.now) {
@@ -4677,7 +5587,9 @@ function createEventBus(now = Date.now) {
4677
5587
  const log = [];
4678
5588
  const subscribers = [];
4679
5589
  const byKind = {};
5590
+ const staged = /* @__PURE__ */ new WeakMap();
4680
5591
  let seq = 0;
5592
+ let published = 0;
4681
5593
  let pulled = 0;
4682
5594
  const matches = (r, kinds) => !kinds || kinds.includes(r.event.type);
4683
5595
  const bestIndex = (kinds) => {
@@ -4695,16 +5607,19 @@ function createEventBus(now = Date.now) {
4695
5607
  };
4696
5608
  return {
4697
5609
  async publish(event, opts) {
4698
- const record = {
5610
+ const record = staged.get(event) ?? {
4699
5611
  seq: seq++,
4700
5612
  at: now(),
4701
5613
  priority: opts?.priority ?? 0,
4702
5614
  event
4703
5615
  };
5616
+ staged.set(event, record);
5617
+ for (const handler of subscribers) await handler(record);
5618
+ staged.delete(event);
4704
5619
  if (opts?.queue !== false) queue.push(record);
4705
5620
  log.push(record);
5621
+ published += 1;
4706
5622
  byKind[event.type] = (byKind[event.type] ?? 0) + 1;
4707
- for (const handler of subscribers) await handler(record);
4708
5623
  return record;
4709
5624
  },
4710
5625
  pull(kinds) {
@@ -4728,7 +5643,7 @@ function createEventBus(now = Date.now) {
4728
5643
  },
4729
5644
  stats() {
4730
5645
  return {
4731
- published: seq,
5646
+ published,
4732
5647
  pulled,
4733
5648
  byKind: { ...byKind }
4734
5649
  };
@@ -4936,14 +5851,6 @@ function spawnProfileArg() {
4936
5851
  }).properties));
4937
5852
  return spawnProfileArgCache;
4938
5853
  }
4939
- /** Freeze a JSON-Schema tree. The derived schema is plain JSON data with no cycles. */
4940
- function deepFreeze(value) {
4941
- if (value && typeof value === "object" && !Object.isFrozen(value)) {
4942
- Object.freeze(value);
4943
- for (const nested of Object.values(value)) deepFreeze(nested);
4944
- }
4945
- return value;
4946
- }
4947
5854
  /** Build the driver's MCP tools over a live scope. */
4948
5855
  function createCoordinationTools(opts) {
4949
5856
  const deliverable = opts.deliverable;
@@ -4956,23 +5863,76 @@ function createCoordinationTools(opts) {
4956
5863
  const questionPolicy = opts.questionPolicy ?? "auto";
4957
5864
  const completedKeys = /* @__PURE__ */ new Set();
4958
5865
  const keyByWorker = /* @__PURE__ */ new Map();
5866
+ let unkeyedAssignmentOrdinal = nextUnkeyedAssignmentOrdinal(opts.scope);
4959
5867
  for (const [key, prior] of opts.scope.resume?.keys ?? []) if (prior.state === "completed") completedKeys.add(key);
4960
- for (const s of opts.scope.resume?.settled ?? []) ledger.push(s.kind === "done" ? {
4961
- id: s.handle.id,
4962
- status: "done",
4963
- score: s.verdict?.score ?? 0,
4964
- valid: s.verdict?.valid ?? false,
4965
- outRef: s.outRef
4966
- } : {
4967
- id: s.handle.id,
4968
- status: "down",
4969
- reason: s.reason
4970
- });
5868
+ const nodeForWorker = (id) => opts.scope.view.nodes.find((node) => node.id === id) ?? opts.scope.resume?.view.nodes.find((node) => node.id === id);
5869
+ const projectSettled = (settled, resumed = false) => {
5870
+ const node = nodeForWorker(settled.handle.id);
5871
+ const assignmentId = settled.handle.assignmentId ?? node?.assignmentId;
5872
+ const identity = settled.handle.identity ?? node?.identity;
5873
+ const materialization = settled.handle.materialization ?? node?.materialization;
5874
+ const executionBindings = settled.handle.executionBindings ?? node?.executionBindings;
5875
+ const settledAt = settled.settledAt ?? node?.settledAt;
5876
+ const trace = settled.trace ?? node?.trace ?? {
5877
+ status: "unavailable",
5878
+ reason: "legacy-settlement-without-trace-evidence"
5879
+ };
5880
+ const common = {
5881
+ id: settled.handle.id,
5882
+ ...assignmentId === void 0 ? {} : { assignmentId },
5883
+ ...identity === void 0 ? {} : { identity },
5884
+ ...materialization === void 0 ? {} : { materialization },
5885
+ ...executionBindings === void 0 ? {} : { executionBindings },
5886
+ ...settledAt === void 0 ? {} : { settledAt },
5887
+ trace,
5888
+ ...resumed ? { resumed: true } : {}
5889
+ };
5890
+ return deepFreezeDetached(settled.kind === "done" ? {
5891
+ ...common,
5892
+ status: "done",
5893
+ spent: settled.spent,
5894
+ ...settled.verdict?.score === void 0 ? {} : { score: settled.verdict.score },
5895
+ ...settled.verdict?.valid === void 0 ? {} : { valid: settled.verdict.valid },
5896
+ outRef: settled.outRef
5897
+ } : {
5898
+ ...common,
5899
+ status: "down",
5900
+ ...node?.spent === void 0 ? {} : { spent: node.spent },
5901
+ reason: settled.reason
5902
+ });
5903
+ };
5904
+ const resumedWorkers = [];
5905
+ for (const s of opts.scope.resume?.settled ?? []) {
5906
+ const worker = projectSettled(s, true);
5907
+ resumedWorkers.push(worker);
5908
+ ledger.push(worker);
5909
+ }
4971
5910
  const bus = createEventBus();
4972
5911
  if (opts.onEvent) {
4973
5912
  const cb = opts.onEvent;
4974
- bus.subscribe((rec) => cb(rec.event));
4975
- }
5913
+ bus.subscribe((rec) => cb(rec.event, rec));
5914
+ }
5915
+ const resumeEvents = opts.replaySettlements ? resumedWorkers.map((worker) => deepFreezeDetached({
5916
+ type: "settled",
5917
+ worker
5918
+ })) : [];
5919
+ let resumeEventIndex = 0;
5920
+ let readyInFlight;
5921
+ const ready = () => {
5922
+ if (resumeEventIndex >= resumeEvents.length) return Promise.resolve();
5923
+ if (readyInFlight) return readyInFlight;
5924
+ readyInFlight = (async () => {
5925
+ while (resumeEventIndex < resumeEvents.length) {
5926
+ const event = resumeEvents[resumeEventIndex];
5927
+ if (!event) break;
5928
+ await bus.publish(event);
5929
+ resumeEventIndex += 1;
5930
+ }
5931
+ })().finally(() => {
5932
+ readyInFlight = void 0;
5933
+ });
5934
+ return readyInFlight;
5935
+ };
4976
5936
  const urgencyPriority = (u) => u === "blocks-run" ? 20 : u === "blocks-step" ? 10 : 0;
4977
5937
  const str = (v, field) => {
4978
5938
  if (typeof v !== "string" || v.length === 0) throw new Error(`coordination tools: "${field}" must be a non-empty string`);
@@ -4995,12 +5955,14 @@ function createCoordinationTools(opts) {
4995
5955
  const maxTokens = field("maxTokens");
4996
5956
  const maxUsd = field("maxUsd");
4997
5957
  const deadlineMs = field("deadlineMs");
4998
- return {
5958
+ const merged = {
4999
5959
  maxIterations: maxIterations ?? base.maxIterations,
5000
5960
  maxTokens: maxTokens ?? base.maxTokens,
5001
5961
  ...(maxUsd ?? base.maxUsd) === void 0 ? {} : { maxUsd: maxUsd ?? base.maxUsd },
5002
5962
  ...(deadlineMs ?? base.deadlineMs) === void 0 ? {} : { deadlineMs: deadlineMs ?? base.deadlineMs }
5003
5963
  };
5964
+ assertValidBudget(merged, "coordination tools: budget");
5965
+ return merged;
5004
5966
  };
5005
5967
  const level = (v) => {
5006
5968
  if (v === "worker" || v === "driver" || v === "loop") return v;
@@ -5010,43 +5972,27 @@ function createCoordinationTools(opts) {
5010
5972
  if (v === "continue-without" || v === "blocks-step" || v === "blocks-run") return v;
5011
5973
  throw new Error("coordination tools: \"urgency\" must be continue-without, blocks-step, or blocks-run");
5012
5974
  };
5013
- const recordSettled = (s) => {
5014
- const settledAt = Date.now();
5975
+ const commitSettled = (s, w) => {
5015
5976
  const settledKey = keyByWorker.get(s.handle.id);
5016
5977
  if (settledKey !== void 0 && s.kind === "done") completedKeys.add(settledKey);
5017
- const w = s.kind === "done" ? {
5018
- id: s.handle.id,
5019
- status: "done",
5020
- score: s.verdict?.score ?? 0,
5021
- valid: s.verdict?.valid ?? false,
5022
- outRef: s.outRef,
5023
- settledAt
5024
- } : {
5025
- id: s.handle.id,
5026
- status: "down",
5027
- reason: s.reason,
5028
- settledAt
5029
- };
5030
5978
  ledger.push(w);
5031
5979
  unwatchWorker(w.id);
5032
- return w;
5033
5980
  };
5034
- const drainSettlement = async () => {
5035
- const s = await opts.scope.next();
5036
- if (!s) return false;
5037
- const w = recordSettled(s);
5038
- await bus.publish({
5039
- type: "settled",
5040
- worker: w
5041
- });
5042
- if (w.status === "done" && w.outRef && opts.analysts && opts.analyzeOnSettle?.length) {
5043
- const trace = await opts.blobs.get(w.outRef);
5981
+ let pendingSettlement;
5982
+ const flushPendingSettlement = async () => {
5983
+ const pending = pendingSettlement;
5984
+ if (!pending) return false;
5985
+ await bus.publish(pending.event);
5986
+ commitSettled(pending.settled, pending.worker);
5987
+ pendingSettlement = void 0;
5988
+ if (pending.analyze && pending.worker.status === "done" && pending.worker.trace.status === "available" && opts.analysts && opts.analyzeOnSettle?.length) {
5989
+ const trace = await workerTraceAnalysisStore(pending.worker.trace, opts.blobs);
5044
5990
  for (const analyst of opts.analyzeOnSettle) {
5045
5991
  const findings = await opts.analysts.run(analyst, trace);
5046
5992
  await bus.publish({
5047
5993
  type: "finding",
5048
5994
  finding: {
5049
- fromWorker: w.id,
5995
+ fromWorker: pending.worker.id,
5050
5996
  analyst,
5051
5997
  findings
5052
5998
  }
@@ -5055,16 +6001,41 @@ function createCoordinationTools(opts) {
5055
6001
  }
5056
6002
  return true;
5057
6003
  };
6004
+ const drainSettlement = async () => {
6005
+ if (!pendingSettlement) {
6006
+ const settled = await opts.scope.next();
6007
+ if (!settled) return false;
6008
+ const worker = projectSettled(settled);
6009
+ pendingSettlement = {
6010
+ settled,
6011
+ worker,
6012
+ event: deepFreezeDetached({
6013
+ type: "settled",
6014
+ worker
6015
+ }),
6016
+ analyze: true
6017
+ };
6018
+ }
6019
+ return flushPendingSettlement();
6020
+ };
5058
6021
  const drainResolved = async () => {
5059
6022
  let drained = 0;
5060
6023
  for (;;) {
5061
- const s = await opts.scope.nextResolved();
5062
- if (!s) return drained;
5063
- const w = recordSettled(s);
5064
- await bus.publish({
5065
- type: "settled",
5066
- worker: w
5067
- });
6024
+ if (!pendingSettlement) {
6025
+ const settled = await opts.scope.nextResolved();
6026
+ if (!settled) return drained;
6027
+ const worker = projectSettled(settled);
6028
+ pendingSettlement = {
6029
+ settled,
6030
+ worker,
6031
+ event: deepFreezeDetached({
6032
+ type: "settled",
6033
+ worker
6034
+ }),
6035
+ analyze: false
6036
+ };
6037
+ }
6038
+ await flushPendingSettlement();
5068
6039
  drained += 1;
5069
6040
  }
5070
6041
  };
@@ -5078,21 +6049,100 @@ function createCoordinationTools(opts) {
5078
6049
  down
5079
6050
  }, { queue: false });
5080
6051
  }
6052
+ const authorizeInstruction = (kind, workerId, instruction, interrupt, questionId) => {
6053
+ const workerIdentity = opts.scope.view.nodes.find((node) => node.id === workerId)?.identity;
6054
+ let authorizedInstruction = instruction;
6055
+ if (opts.authorizeDownMessage) {
6056
+ if (workerIdentity === void 0) throw new Error(`coordination tools: cannot authorize ${kind} for worker ${JSON.stringify(workerId)} without durable identity`);
6057
+ const decision = deepFreezeDetached(opts.authorizeDownMessage(deepFreezeDetached({
6058
+ kind,
6059
+ workerId,
6060
+ workerIdentity,
6061
+ instruction,
6062
+ interrupt,
6063
+ ...questionId !== void 0 ? { questionId } : {}
6064
+ })));
6065
+ if (typeof decision !== "object" || decision === null || Array.isArray(decision) || typeof decision.instruction !== "string" || decision.instruction.length === 0) throw new Error("coordination tools: authorizeDownMessage must return an instruction");
6066
+ authorizedInstruction = decision.instruction;
6067
+ }
6068
+ return deepFreezeDetached({
6069
+ receiptId: randomUUID(),
6070
+ kind,
6071
+ toWorker: workerId,
6072
+ instruction: authorizedInstruction,
6073
+ instructionDigest: canonicalCandidateDigest(authorizedInstruction),
6074
+ ...workerIdentity !== void 0 ? { workerIdentity } : {},
6075
+ interrupt,
6076
+ ...questionId !== void 0 ? { questionId } : {}
6077
+ });
6078
+ };
6079
+ /** Publish before `scope.send`: an awaited durable subscriber therefore commits the exact bytes
6080
+ * before the worker can observe them. */
6081
+ const recordInstruction = async (instruction) => {
6082
+ await bus.publish({
6083
+ type: "instruction",
6084
+ instruction
6085
+ }, { queue: false });
6086
+ };
6087
+ /** Commit delivery intent after the authorization receipt and before `Scope.send`. An attempt with
6088
+ * no matching outcome after a crash is explicitly unknown and must never be replayed. */
6089
+ const recordDeliveryAttempt = async (instruction) => {
6090
+ const attempt = deepFreezeDetached({
6091
+ receiptId: instruction.receiptId,
6092
+ kind: instruction.kind,
6093
+ toWorker: instruction.toWorker,
6094
+ instructionDigest: instruction.instructionDigest,
6095
+ interrupt: instruction.interrupt,
6096
+ ...instruction.questionId !== void 0 ? { questionId: instruction.questionId } : {}
6097
+ });
6098
+ await bus.publish({
6099
+ type: "delivery-attempt",
6100
+ attempt
6101
+ }, { queue: false });
6102
+ return attempt;
6103
+ };
6104
+ const deliveryOutcome = (workerId, delivered) => {
6105
+ if (delivered) return "delivered";
6106
+ if (opts.scope.signal.aborted) return "scope-stopped";
6107
+ const node = opts.scope.view.nodes.find((candidate) => candidate.id === workerId);
6108
+ if (!node) return "unknown-worker";
6109
+ if (!isLive(node.status)) return "already-settled";
6110
+ return "runtime-has-no-inbox";
6111
+ };
6112
+ const attemptDelivery = async (instruction, message) => {
6113
+ await recordDeliveryAttempt(instruction);
6114
+ let delivered = false;
6115
+ let outcome;
6116
+ let error;
6117
+ try {
6118
+ delivered = opts.scope.send(instruction.toWorker, message);
6119
+ outcome = deliveryOutcome(instruction.toWorker, delivered);
6120
+ } catch (cause) {
6121
+ outcome = "runtime-error";
6122
+ error = cause instanceof Error ? cause.message : String(cause);
6123
+ }
6124
+ const down = deepFreezeDetached({
6125
+ receiptId: instruction.receiptId,
6126
+ toWorker: instruction.toWorker,
6127
+ instruction: instruction.instruction,
6128
+ instructionDigest: instruction.instructionDigest,
6129
+ delivered,
6130
+ outcome,
6131
+ ...error !== void 0 ? { error } : {}
6132
+ });
6133
+ if (instruction.kind === "answer") await sendDown("answer", down, str(instruction.questionId, "questionId"));
6134
+ else await sendDown("steer", down);
6135
+ if (error !== void 0) throw new Error(`coordination tools: delivery failed: ${error}`);
6136
+ return down;
6137
+ };
5081
6138
  const projectEvent = (ev) => {
5082
6139
  if (ev.type === "settled") {
5083
- const w = ev.worker;
5084
- return w.status === "done" ? {
5085
- type: "settled",
5086
- settled: w.id,
5087
- status: "done",
5088
- score: w.score,
5089
- valid: w.valid,
5090
- outRef: w.outRef
5091
- } : {
6140
+ const { id, status, ...evidence } = ev.worker;
6141
+ return {
5092
6142
  type: "settled",
5093
- settled: w.id,
5094
- status: "down",
5095
- reason: w.reason
6143
+ settled: id,
6144
+ status,
6145
+ ...evidence
5096
6146
  };
5097
6147
  }
5098
6148
  if (ev.type === "question") return {
@@ -5108,12 +6158,25 @@ function createCoordinationTools(opts) {
5108
6158
  ...ev.down,
5109
6159
  questionId: ev.questionId
5110
6160
  };
5111
- return {
6161
+ if (ev.type === "instruction") return {
6162
+ type: "instruction",
6163
+ ...ev.instruction
6164
+ };
6165
+ if (ev.type === "delivery-attempt") return {
6166
+ type: "delivery-attempt",
6167
+ ...ev.attempt
6168
+ };
6169
+ return {
5112
6170
  type: ev.type,
5113
6171
  ...ev.down
5114
6172
  };
5115
6173
  };
5116
- const nextQuestionId = (from) => `${from}:q${questionSeq++}`;
6174
+ const nextQuestionId = (from) => {
6175
+ for (;;) {
6176
+ const id = `${from}:q${questionSeq++}`;
6177
+ if (!questions.some((question) => question.id === id)) return id;
6178
+ }
6179
+ };
5117
6180
  const normalizeQuestion = (q, fallbackFrom) => {
5118
6181
  const from = str(q.from ?? fallbackFrom, "from");
5119
6182
  return {
@@ -5181,13 +6244,30 @@ function createCoordinationTools(opts) {
5181
6244
  };
5182
6245
  const maxLiveWorkers = opts.maxLiveWorkers;
5183
6246
  const isLive = (status) => status !== "done" && status !== "failed" && status !== "cancelled";
5184
- const liveWorkerCount = () => opts.scope.view.nodes.filter((n) => isLive(n.status)).length;
5185
- const liveSnapshot = () => opts.scope.view.nodes.filter((n) => isLive(n.status)).map((n) => ({
5186
- id: n.id,
5187
- status: n.status,
5188
- spent: n.spent
5189
- }));
5190
- const freeWorkerSlots = () => freeSlots(liveWorkerCount(), maxLiveWorkers);
6247
+ const localLiveWorkerCount = () => opts.scope.view.nodes.filter((n) => isLive(n.status)).length;
6248
+ const sharedWorkerCapacity = () => {
6249
+ return opts.scope.workerCapacity;
6250
+ };
6251
+ const usesTreeWideLimit = () => {
6252
+ const capacity = sharedWorkerCapacity();
6253
+ return capacity !== void 0 && capacity.freeSlots !== null;
6254
+ };
6255
+ const liveWorkerCount = () => usesTreeWideLimit() ? sharedWorkerCapacity()?.live ?? localLiveWorkerCount() : localLiveWorkerCount();
6256
+ const projectNodeEvidence = (node, resumed = false) => ({
6257
+ id: node.id,
6258
+ status: node.status,
6259
+ ...node.assignmentId === void 0 ? {} : { assignmentId: node.assignmentId },
6260
+ ...node.identity === void 0 ? {} : { identity: node.identity },
6261
+ ...node.materialization === void 0 ? {} : { materialization: node.materialization },
6262
+ ...node.executionBindings === void 0 ? {} : { executionBindings: node.executionBindings },
6263
+ spent: node.spent,
6264
+ ...node.settledAt === void 0 ? {} : { settledAt: node.settledAt },
6265
+ ...node.outRef === void 0 ? {} : { outRef: node.outRef },
6266
+ ...node.trace === void 0 ? {} : { trace: node.trace },
6267
+ ...resumed ? { resumed: true } : {}
6268
+ });
6269
+ const liveSnapshot = () => opts.scope.view.nodes.filter((n) => isLive(n.status)).map((n) => projectNodeEvidence(n));
6270
+ const freeWorkerSlots = () => usesTreeWideLimit() ? sharedWorkerCapacity()?.freeSlots ?? null : freeSlots(localLiveWorkerCount(), maxLiveWorkers);
5191
6271
  const readProgress = (id) => {
5192
6272
  const scope = opts.scope;
5193
6273
  if (typeof scope.progress !== "function") return void 0;
@@ -5288,10 +6368,22 @@ function createCoordinationTools(opts) {
5288
6368
  type: "object",
5289
6369
  description: "Optional per-spawn budget that merges over the per-worker default (per field). Only set the ceilings this sub-task needs raised; the conserved pool still fences.",
5290
6370
  properties: {
5291
- maxIterations: { type: "number" },
5292
- maxTokens: { type: "number" },
5293
- maxUsd: { type: "number" },
5294
- deadlineMs: { type: "number" }
6371
+ maxIterations: {
6372
+ type: "number",
6373
+ minimum: 0
6374
+ },
6375
+ maxTokens: {
6376
+ type: "number",
6377
+ minimum: 0
6378
+ },
6379
+ maxUsd: {
6380
+ type: "number",
6381
+ minimum: 0
6382
+ },
6383
+ deadlineMs: {
6384
+ type: "number",
6385
+ minimum: 0
6386
+ }
5295
6387
  }
5296
6388
  }
5297
6389
  },
@@ -5300,28 +6392,47 @@ function createCoordinationTools(opts) {
5300
6392
  handler: (raw) => {
5301
6393
  const a = obj(raw);
5302
6394
  const key = a.key === void 0 ? void 0 : str(a.key, "key");
5303
- if (!(key !== void 0 && completedKeys.has(key)) && maxLiveWorkers !== void 0 && maxLiveWorkers > 0 && liveWorkerCount() >= maxLiveWorkers) return Promise.resolve({
6395
+ if (!(key !== void 0 && completedKeys.has(key)) && !usesTreeWideLimit() && maxLiveWorkers !== void 0 && maxLiveWorkers > 0 && liveWorkerCount() >= maxLiveWorkers) return Promise.resolve({
5304
6396
  error: "max-live-workers",
5305
6397
  live: liveWorkerCount(),
5306
6398
  freeSlots: freeWorkerSlots()
5307
6399
  });
5308
- const agent = opts.makeWorkerAgent(a.profile);
5309
- const budget = a.budget === void 0 ? opts.perWorker : mergeBudget(opts.perWorker, a.budget);
5310
- const res = opts.scope.spawn(agent, a.task, {
6400
+ const parsedProfile = agentProfileSchema.safeParse(a.profile);
6401
+ if (!parsedProfile.success) return Promise.resolve({
6402
+ error: "invalid-profile",
6403
+ issues: parsedProfile.error.issues.map((issue) => ({
6404
+ path: issue.path.join("."),
6405
+ message: issue.message
6406
+ }))
6407
+ });
6408
+ const profile = deepFreezeDetached(parsedProfile.data);
6409
+ const task = deepFreezeDetached(a.task);
6410
+ const label = typeof a.label === "string" ? a.label : "worker";
6411
+ const budget = Object.freeze(a.budget === void 0 ? opts.perWorker : mergeBudget(opts.perWorker, a.budget));
6412
+ const assignmentId = key !== void 0 ? `key:${key}` : `ordinal:${unkeyedAssignmentOrdinal++}`;
6413
+ const context = Object.freeze({
6414
+ assignmentId,
6415
+ parentNodeId: opts.scope.view.root,
6416
+ budget,
6417
+ task,
6418
+ label,
6419
+ ...key !== void 0 ? { key } : {}
6420
+ });
6421
+ const res = opts.scope.spawn(() => opts.makeWorkerAgent(profile, context), task, {
5311
6422
  budget,
5312
- label: typeof a.label === "string" ? a.label : "worker",
6423
+ label,
6424
+ assignmentId,
5313
6425
  ...key !== void 0 ? { key } : {}
5314
6426
  });
5315
6427
  if (res.ok && res.prior?.state === "completed") {
5316
6428
  const s = res.prior.settled;
5317
6429
  if (key !== void 0) completedKeys.add(key);
6430
+ const { id, status, resumed: _resumed, ...evidence } = projectSettled(s);
5318
6431
  return Promise.resolve({
5319
- workerId: s.handle.id,
6432
+ workerId: id,
5320
6433
  resumed: "completed",
5321
- status: "done",
5322
- score: s.verdict?.score ?? 0,
5323
- valid: s.verdict?.valid ?? false,
5324
- outRef: s.outRef,
6434
+ status,
6435
+ ...evidence,
5325
6436
  live: liveWorkerCount(),
5326
6437
  freeSlots: freeWorkerSlots()
5327
6438
  });
@@ -5337,6 +6448,10 @@ function createCoordinationTools(opts) {
5337
6448
  } : {};
5338
6449
  return Promise.resolve(res.ok ? {
5339
6450
  workerId: res.handle.id,
6451
+ assignmentId: res.handle.assignmentId ?? assignmentId,
6452
+ ...res.handle.identity === void 0 ? {} : { identity: res.handle.identity },
6453
+ ...res.handle.materialization === void 0 ? {} : { materialization: res.handle.materialization },
6454
+ ...res.handle.executionBindings === void 0 ? {} : { executionBindings: res.handle.executionBindings },
5340
6455
  live: liveWorkerCount(),
5341
6456
  freeSlots: freeWorkerSlots(),
5342
6457
  ...priorHistory
@@ -5364,19 +6479,16 @@ function createCoordinationTools(opts) {
5364
6479
  if (!resumed) return { error: `unknown workerId ${JSON.stringify(id)}` };
5365
6480
  const output = resumed.outRef ? await opts.blobs.get(resumed.outRef) : void 0;
5366
6481
  return {
5367
- status: resumed.status,
5368
- spent: resumed.spent,
6482
+ ...projectNodeEvidence(resumed, true),
5369
6483
  outRef: resumed.outRef ?? null,
5370
6484
  output: output ?? null,
5371
- progress: null,
5372
- resumed: true
6485
+ progress: null
5373
6486
  };
5374
6487
  }
5375
6488
  const output = node.outRef ? await opts.blobs.get(node.outRef) : void 0;
5376
6489
  const progress = readProgress(id);
5377
6490
  return {
5378
- status: node.status,
5379
- spent: node.spent,
6491
+ ...projectNodeEvidence(node),
5380
6492
  outRef: node.outRef ?? null,
5381
6493
  output: output ?? null,
5382
6494
  progress: progress ?? null
@@ -5406,24 +6518,20 @@ function createCoordinationTools(opts) {
5406
6518
  const workerId = str(a.workerId, "workerId");
5407
6519
  const instruction = str(a.instruction, "instruction");
5408
6520
  const interrupt = a.interrupt === true;
5409
- const delivered = opts.scope.send(workerId, {
5410
- steer: instruction,
6521
+ const authorized = authorizeInstruction("steer", workerId, instruction, interrupt);
6522
+ await recordInstruction(authorized);
6523
+ const delivery = await attemptDelivery(authorized, {
6524
+ steer: authorized.instruction,
5411
6525
  interrupt
5412
6526
  });
5413
- await sendDown("steer", {
5414
- toWorker: workerId,
5415
- instruction,
5416
- delivered
5417
- });
5418
- if (delivered) return {
5419
- delivered,
6527
+ if (delivery.delivered) return {
6528
+ delivered: true,
5420
6529
  progress: readProgress(workerId) ?? null
5421
6530
  };
5422
- const progress = readProgress(workerId);
5423
6531
  return {
5424
- delivered,
5425
- reason: !progress ? "unknown-worker" : !progress.live ? "already-settled" : "runtime-has-no-inbox",
5426
- progress: progress ?? null
6532
+ delivered: false,
6533
+ reason: delivery.outcome,
6534
+ progress: readProgress(workerId) ?? null
5427
6535
  };
5428
6536
  }
5429
6537
  },
@@ -5505,25 +6613,24 @@ function createCoordinationTools(opts) {
5505
6613
  const questionId = str(a.questionId, "questionId");
5506
6614
  if (typeof a.answer === "string" && a.answer.length > 0) {
5507
6615
  const answer = a.answer;
5508
- const question = decideQuestion(questionId, {
5509
- kind: "answer",
5510
- answer,
5511
- by: typeof a.by === "string" && a.by.length > 0 ? a.by : "user"
5512
- });
5513
- const interrupt = question.urgency === "blocks-run" || question.urgency === "blocks-step";
5514
- const delivered = opts.scope.send(question.from, {
5515
- answer,
6616
+ const pendingQuestion = questions.find((question) => question.id === questionId);
6617
+ if (pendingQuestion === void 0) throw new Error(`unknown questionId ${JSON.stringify(questionId)}`);
6618
+ const interrupt = pendingQuestion.urgency === "blocks-run" || pendingQuestion.urgency === "blocks-step";
6619
+ const authorized = authorizeInstruction("answer", pendingQuestion.from, answer, interrupt, questionId);
6620
+ await recordInstruction(authorized);
6621
+ const delivery = await attemptDelivery(authorized, {
6622
+ answer: authorized.instruction,
5516
6623
  questionId,
5517
6624
  interrupt
5518
6625
  });
5519
- await sendDown("answer", {
5520
- toWorker: question.from,
5521
- instruction: answer,
5522
- delivered
5523
- }, questionId);
5524
6626
  return {
5525
- question,
5526
- delivered
6627
+ question: delivery.delivered ? decideQuestion(questionId, {
6628
+ kind: "answer",
6629
+ answer: authorized.instruction,
6630
+ by: typeof a.by === "string" && a.by.length > 0 ? a.by : "user"
6631
+ }) : pendingQuestion,
6632
+ delivered: delivery.delivered,
6633
+ ...delivery.delivered ? {} : { reason: delivery.outcome }
5527
6634
  };
5528
6635
  }
5529
6636
  if (typeof a.deferReason === "string" && a.deferReason.length > 0) return Promise.resolve({ question: decideQuestion(questionId, {
@@ -5689,16 +6796,29 @@ function createCoordinationTools(opts) {
5689
6796
  handler: async (raw) => {
5690
6797
  const a = obj(raw);
5691
6798
  const id = str(a.workerId, "workerId");
5692
- const node = opts.scope.view.nodes.find((n) => n.id === id);
6799
+ const node = nodeForWorker(id);
5693
6800
  if (!node) return { error: `unknown workerId ${JSON.stringify(id)}` };
5694
- if (!node.outRef) return { error: `worker ${JSON.stringify(id)} has not settled — no trace to analyze yet` };
5695
- const trace = await opts.blobs.get(node.outRef);
5696
- return { findings: await opts.analysts?.run(str(a.kind, "kind"), trace) };
6801
+ if (isLive(node.status)) return { error: `worker ${JSON.stringify(id)} has not settled — no trace to analyze yet` };
6802
+ const trace = ledger.find((worker) => worker.id === id)?.trace ?? node.trace ?? {
6803
+ status: "unavailable",
6804
+ reason: "legacy-settlement-without-trace-evidence"
6805
+ };
6806
+ let store;
6807
+ try {
6808
+ store = await workerTraceAnalysisStore(trace, opts.blobs);
6809
+ } catch (error) {
6810
+ return {
6811
+ error: error instanceof Error ? error.message : String(error),
6812
+ trace
6813
+ };
6814
+ }
6815
+ return { findings: await opts.analysts?.run(str(a.kind, "kind"), store) };
5697
6816
  }
5698
6817
  });
5699
6818
  }
5700
6819
  return {
5701
6820
  tools,
6821
+ ready,
5702
6822
  history: () => bus.history(),
5703
6823
  raiseFinding: (finding) => bus.publish({
5704
6824
  type: "finding",
@@ -5713,6 +6833,31 @@ function createCoordinationTools(opts) {
5713
6833
  drainResolved
5714
6834
  };
5715
6835
  }
6836
+ function nextUnkeyedAssignmentOrdinal(scope) {
6837
+ let next = 0;
6838
+ const views = [scope.resume?.view, scope.view];
6839
+ for (const view of views) {
6840
+ if (view === void 0) continue;
6841
+ for (const node of view.nodes) {
6842
+ const match = /^ordinal:(\d+)$/.exec(node.assignmentId ?? "");
6843
+ if (match === null) continue;
6844
+ const ordinal = Number(match[1]);
6845
+ if (!Number.isSafeInteger(ordinal)) throw new Error(`coordination: durable assignment id '${node.assignmentId}' exceeds the safe ordinal range`);
6846
+ next = Math.max(next, ordinal + 1);
6847
+ }
6848
+ }
6849
+ if (!Number.isSafeInteger(next)) throw new Error("coordination: durable assignment ordinal space is exhausted");
6850
+ return next;
6851
+ }
6852
+ function deepFreezeDetached(value) {
6853
+ return deepFreeze(structuredClone(value));
6854
+ }
6855
+ function deepFreeze(value, seen = /* @__PURE__ */ new Set()) {
6856
+ if (value === null || typeof value !== "object" || seen.has(value)) return value;
6857
+ seen.add(value);
6858
+ for (const child of Object.values(value)) deepFreeze(child, seen);
6859
+ return Object.freeze(value);
6860
+ }
5716
6861
  //#endregion
5717
6862
  //#region src/runtime/anytime.ts
5718
6863
  /**
@@ -6132,10 +7277,11 @@ const runawayTripwireTurns = 2e3;
6132
7277
  * overspend usd up to the turn tripwire). */
6133
7278
  function poolStarved(scope, perWorker) {
6134
7279
  const b = scope.budget;
6135
- if (b.reservedTokens > 0) return false;
7280
+ if (scope.view.inFlight > 0 || scope.view.waiting > 0) return false;
6136
7281
  const tokenStarved = b.tokensLeft < perWorker.maxTokens;
6137
- const usdStarved = b.usdCapped && b.usdLeft <= 0;
6138
- return tokenStarved || usdStarved;
7282
+ const iterationStarved = b.iterationsLeft <= 0;
7283
+ const usdStarved = b.usdCapped && (b.usdLeft <= 0 || perWorker.maxUsd !== void 0 && b.usdLeft < perWorker.maxUsd);
7284
+ return tokenStarved || iterationStarved || usdStarved;
6139
7285
  }
6140
7286
  /** The absolute wall-clock deadline (when the root set one) has passed. */
6141
7287
  function deadlinePassed(scope, now) {
@@ -6168,17 +7314,29 @@ function driverAgent(opts) {
6168
7314
  if ((opts.extraTools?.length ?? 0) > 0 && typeof opts.executeExtraTool !== "function") throw new ValidationError("driverAgent: extraTools requires executeExtraTool (how to run a work-tool call)");
6169
7315
  if ((opts.analyzeOnSettle?.length ?? 0) > 0 && !opts.analysts) throw new ValidationError("driverAgent: analyzeOnSettle requires analysts (the lens registry the kinds resolve against)");
6170
7316
  const reserved = new Set(coordinationVerbNames);
6171
- for (const t of opts.extraTools ?? []) if (reserved.has(t.name)) throw new ValidationError(`driverAgent: extra work tool "${t.name}" collides with a coordination verb`);
7317
+ for (const tool of opts.nodeTools ?? []) {
7318
+ if (reserved.has(tool.name)) throw new ValidationError(`driverAgent: node tool "${tool.name}" collides with a coordination verb or another node tool`);
7319
+ reserved.add(tool.name);
7320
+ }
7321
+ for (const t of opts.extraTools ?? []) {
7322
+ if (reserved.has(t.name)) throw new ValidationError(`driverAgent: extra work tool "${t.name}" collides with a coordination verb or node tool`);
7323
+ reserved.add(t.name);
7324
+ }
6172
7325
  if (opts.maxTurns !== void 0 && opts.maxTurns < 0) throw new ValidationError("driverAgent: maxTurns must be >= 0 (0 lifts the turn cap; bounds become the conserved pool + deadline + abort)");
6173
7326
  const maxTurns = opts.maxTurns === 0 ? runawayTripwireTurns : opts.maxTurns ?? 16;
6174
7327
  const now = opts.now ?? Date.now;
7328
+ const inbox = opts.inbox ?? createInbox();
6175
7329
  return {
6176
7330
  name: opts.name,
7331
+ deliver(message) {
7332
+ return inbox.deliver(message);
7333
+ },
6177
7334
  async act(task, scope) {
6178
7335
  const coord = createCoordinationTools({
6179
7336
  scope,
6180
7337
  blobs: opts.blobs,
6181
7338
  makeWorkerAgent: opts.makeWorkerAgent,
7339
+ ...opts.authorizeDownMessage ? { authorizeDownMessage: opts.authorizeDownMessage } : {},
6182
7340
  perWorker: opts.perWorker,
6183
7341
  ...opts.deliverable ? { deliverable: opts.deliverable } : {},
6184
7342
  ...opts.maxLiveWorkers !== void 0 ? { maxLiveWorkers: opts.maxLiveWorkers } : {},
@@ -6187,28 +7345,41 @@ function driverAgent(opts) {
6187
7345
  ...opts.watchWorkers ? { watchWorkers: opts.watchWorkers } : {},
6188
7346
  ...opts.stallAfterMs !== void 0 ? { stallAfterMs: opts.stallAfterMs } : {},
6189
7347
  ...opts.onEvent ? { onEvent: opts.onEvent } : {},
7348
+ ...opts.replaySettlements ? { replaySettlements: true } : {},
6190
7349
  ...opts.priorCoordination?.questions.length ? { priorQuestions: opts.priorCoordination.questions } : {}
6191
7350
  });
7351
+ await coord.ready();
6192
7352
  for (const w of scope.resume?.waits ?? []) {
6193
7353
  const rearmed = scope.wait(w.spec, { label: w.label });
6194
7354
  if (!rearmed.ok) throw new RuntimeRunStateError(`driverAgent: cannot re-arm resumed wait '${w.label}' (${rearmed.reason})`);
6195
7355
  }
6196
- const byName = new Map(coord.tools.map((t) => [t.name, t]));
6197
- const toolSpecs = [...coord.tools.map((t) => ({
6198
- type: "function",
6199
- function: {
6200
- name: t.name,
6201
- description: t.description,
6202
- parameters: t.inputSchema
6203
- }
6204
- })), ...(opts.extraTools ?? []).map((t) => ({
6205
- type: "function",
6206
- function: {
6207
- name: t.name,
6208
- description: t.description,
6209
- parameters: t.parameters
6210
- }
6211
- }))];
7356
+ const byName = new Map([...coord.tools, ...opts.nodeTools ?? []].map((t) => [t.name, t]));
7357
+ const toolSpecs = [
7358
+ ...coord.tools.map((t) => ({
7359
+ type: "function",
7360
+ function: {
7361
+ name: t.name,
7362
+ description: t.description,
7363
+ parameters: t.inputSchema
7364
+ }
7365
+ })),
7366
+ ...(opts.nodeTools ?? []).map((t) => ({
7367
+ type: "function",
7368
+ function: {
7369
+ name: t.name,
7370
+ description: t.description,
7371
+ parameters: t.inputSchema
7372
+ }
7373
+ })),
7374
+ ...(opts.extraTools ?? []).map((t) => ({
7375
+ type: "function",
7376
+ function: {
7377
+ name: t.name,
7378
+ description: t.description,
7379
+ parameters: t.parameters
7380
+ }
7381
+ }))
7382
+ ];
6212
7383
  const system = typeof opts.systemPrompt === "function" ? opts.systemPrompt(task) : opts.systemPrompt;
6213
7384
  const tracker = opts.stopRule ? createProgressTracker({ now }) : void 0;
6214
7385
  let progressStopReason;
@@ -6288,20 +7459,32 @@ function driverAgent(opts) {
6288
7459
  ...scope.resume ? [{
6289
7460
  role: "user",
6290
7461
  content: resumeBrief(scope.resume, opts.priorCoordination)
7462
+ }] : hasPriorCoordination(opts.priorCoordination) ? [{
7463
+ role: "user",
7464
+ content: priorCoordinationBrief(opts.priorCoordination)
6291
7465
  }] : []
6292
7466
  ],
6293
7467
  maxTurns,
6294
- hooks: { stopBefore: () => {
6295
- if (coord.isStopped() || scope.signal.aborted || poolStarved(scope, opts.perWorker) || deadlinePassed(scope, now)) return true;
6296
- if (!opts.stopRule || !tracker) return false;
6297
- const decision = progressStop(tracker, opts.stopRule, coord, scope, now, opts.stallAfterMs);
6298
- if (!decision.stop) return false;
6299
- if (progressStopReason === void 0) {
6300
- progressStopReason = decision.reason;
6301
- opts.onProgressStop?.(decision.reason);
7468
+ hooks: {
7469
+ beforeTurn: (_turn, messages) => {
7470
+ const pending = inbox.drain();
7471
+ if (pending.length > 0) messages.push({
7472
+ role: "user",
7473
+ content: inbox.fold(pending)
7474
+ });
7475
+ },
7476
+ stopBefore: () => {
7477
+ if (coord.isStopped() || scope.signal.aborted || poolStarved(scope, opts.perWorker) || deadlinePassed(scope, now)) return true;
7478
+ if (!opts.stopRule || !tracker) return false;
7479
+ const decision = progressStop(tracker, opts.stopRule, coord, scope, now, opts.stallAfterMs);
7480
+ if (!decision.stop) return false;
7481
+ if (progressStopReason === void 0) {
7482
+ progressStopReason = decision.reason;
7483
+ opts.onProgressStop?.(decision.reason);
7484
+ }
7485
+ return true;
6302
7486
  }
6303
- return true;
6304
- } }
7487
+ }
6305
7488
  });
6306
7489
  await coord.drainResolved();
6307
7490
  const submitted = coord.submittedResult();
@@ -6318,8 +7501,9 @@ function driverAgent(opts) {
6318
7501
  /**
6319
7502
  * The factual context a resumed driver starts from — everything the durable stores prove about
6320
7503
  * the prior process(es): committed settlements, per-key states (completed / lost / failed),
6321
- * re-armed waits, carried-over questions and findings, and the spend already paid. Injected as
6322
- * the brain's first user-context on a resumed run so it continues from the unresolved work.
7504
+ * re-armed waits, carried-over questions/findings/continuation receipts, and spend already paid.
7505
+ * Injected as the brain's first user-context on a resumed run so it continues from unresolved work;
7506
+ * old continuation receipts are evidence and are never auto-delivered.
6323
7507
  */
6324
7508
  function resumeBrief(resume, prior) {
6325
7509
  const lines = [
@@ -6339,13 +7523,36 @@ function resumeBrief(resume, prior) {
6339
7523
  if (lost.length > 0) lines.push("", "Keys LOST in flight with the prior process — this is the unresolved work; spawn_agent with the same key starts a fresh attempt:", ...lost.map(([k, v]) => `- ${k} (prior attempt ${v.id}, ${v.label})`));
6340
7524
  if (failed.length > 0) lines.push("", "Keys whose prior attempt FAILED (settled down) — spawn_agent with the same key retries:", ...failed.map(([k, v]) => `- ${k} (prior attempt ${v.id}, ${v.label})`));
6341
7525
  if (resume.waits.length > 0) lines.push("", "Pending waits RE-ARMED on their original deadlines (they settle through await_event):", ...resume.waits.map((w) => `- ${w.label} (${w.spec.kind})`));
6342
- const openQuestions = (prior?.questions ?? []).filter((q) => q.status === "open" || q.status === "escalated");
6343
- if (openQuestions.length > 0) lines.push("", "Questions carried over, still undecided (answer_question decides them; list_questions shows all):", ...openQuestions.map((q) => `- [${q.id}] from=${q.from}, urgency=${q.urgency}: ${q.question}`));
6344
- if ((prior?.findings.length ?? 0) > 0) lines.push("", "Analyst findings from the prior process:", ...(prior?.findings ?? []).map((f) => `- ${f.analyst} on ${f.fromWorker}: ${safeJson(f.findings)}`));
7526
+ appendPriorCoordination(lines, prior);
6345
7527
  const spent = resume.priorSpend;
6346
7528
  lines.push("", "Budget the run ALREADY spent before this process (it counts toward the run total):", `- child work: tokens in=${spent.childWork.tokens.input} out=${spent.childWork.tokens.output}, usd=${spent.childWork.usd}, iterations=${spent.childWork.iterations}`, `- driver inference: tokens in=${spent.driverInference.tokens.input} out=${spent.driverInference.tokens.output}, usd=${spent.driverInference.usd}`);
6347
7529
  return lines.join("\n");
6348
7530
  }
7531
+ function hasPriorCoordination(prior) {
7532
+ return prior !== void 0 && (prior.questions.length > 0 || prior.findings.length > 0 || prior.continuations.length > 0 || prior.deliveryEvidence.length > 0);
7533
+ }
7534
+ function priorCoordinationBrief(prior) {
7535
+ const lines = [
7536
+ "PRIOR COORDINATION EVIDENCE: this logical supervisor ran in an earlier process.",
7537
+ "Use the evidence below as context. Never auto-deliver an old continuation; issue a new",
7538
+ "authorized instruction only when current live state still warrants it."
7539
+ ];
7540
+ appendPriorCoordination(lines, prior);
7541
+ return lines.join("\n");
7542
+ }
7543
+ function appendPriorCoordination(lines, prior) {
7544
+ const openQuestions = (prior?.questions ?? []).filter((q) => q.status === "open" || q.status === "escalated");
7545
+ if (openQuestions.length > 0) lines.push("", "Questions carried over, still undecided (answer_question decides them; list_questions shows all):", ...openQuestions.map((q) => `- [${q.id}] from=${q.from}, urgency=${q.urgency}: ${q.question}`));
7546
+ if ((prior?.findings.length ?? 0) > 0) lines.push("", "Analyst findings from the prior process:", ...(prior?.findings ?? []).map((f) => `- ${f.analyst} on ${f.fromWorker}: ${safeJson(f.findings)}`));
7547
+ if ((prior?.continuations.length ?? 0) > 0) {
7548
+ const attempts = new Set((prior?.deliveryEvidence ?? []).filter((event) => event.type === "delivery-attempt").map((event) => event.attempt.receiptId));
7549
+ const outcomes = new Map((prior?.deliveryEvidence ?? []).filter((event) => event.type === "steer" || event.type === "answer").map((event) => [event.down.receiptId, event.down.outcome]));
7550
+ lines.push("", "Authorized continuations committed by the prior process (evidence only; never replayed automatically):", ...(prior?.continuations ?? []).map((continuation) => {
7551
+ const delivery = outcomes.get(continuation.receiptId) ?? (attempts.has(continuation.receiptId) ? "unknown-after-crash" : "not-attempted-before-crash");
7552
+ return `- receipt=${continuation.receiptId}, ${continuation.kind} → ${continuation.toWorker}, instruction=${continuation.instructionDigest}, delivery=${delivery}`;
7553
+ }));
7554
+ }
7555
+ }
6349
7556
  /** Run a work tool. A throw is data to the driver (it can recover next turn), not a crash — fold
6350
7557
  * the error back as a string result. null/undefined passes through (the caller treats it as "not
6351
7558
  * handled" and falls to the coordination dispatch). */
@@ -7243,203 +8450,6 @@ function canonicalize(value) {
7243
8450
  return out;
7244
8451
  }
7245
8452
  //#endregion
7246
- //#region src/runtime/supervise/authoring.ts
7247
- /**
7248
- *
7249
- * The supervisor's intelligence is AUTHORING the agents it spawns — not pressing buttons.
7250
- *
7251
- * Every agent here is three things: instructions (system prompt), tools, and a model — its
7252
- * `AgentProfile`. The supervisor's job is to WRITE those profiles: read the task, decompose it,
7253
- * and for each sub-task author a tailored worker recipe. `supervisorInstructions` is the how-to the
7254
- * supervisor reads (its system prompt); `authoredWorker` builds a worker AGENT from a profile the
7255
- * supervisor authored — the authored systemPrompt + model shape the worker's call.
7256
- *
7257
- * The skill is the single OPTIMIZABLE surface: edit it → the supervisor designs better agents.
7258
- * That is the self-improvement lever (the prompt/skill lever), not the execution plumbing.
7259
- *
7260
- * @experimental
7261
- */
7262
- /** Narrow an untyped `spawn_agent` profile argument to an `AuthoredProfile`, or null if the
7263
- * supervisor failed to author one (empty/placeholder profile — a skill violation worth catching). */
7264
- function asAuthoredProfile(raw) {
7265
- const p = raw;
7266
- if (!p || typeof p.systemPrompt !== "string" || p.systemPrompt.trim().length === 0) return null;
7267
- return {
7268
- name: typeof p.name === "string" && p.name.length > 0 ? p.name : "worker",
7269
- systemPrompt: p.systemPrompt,
7270
- ...typeof p.model === "string" ? { model: p.model } : {}
7271
- };
7272
- }
7273
- /** The supervisor SKILL — the how-to the supervisor reads (its system prompt). THE optimizable
7274
- * surface: editing this changes how the supervisor designs every agent it spawns. */
7275
- function supervisorInstructions(opts) {
7276
- return [
7277
- "You are a SUPERVISOR. You do NOT do the work yourself — your job is to DESIGN and DRIVE specialist worker agents.",
7278
- "",
7279
- "For the task you are given:",
7280
- "1. DECOMPOSE it into the smallest set of sub-tasks a single focused worker can each deliver.",
7281
- "2. For EACH sub-task, AUTHOR a worker by calling spawn_agent with a COMPLETE `profile`:",
7282
- " • name: a short id for the worker.",
7283
- " • systemPrompt: rich, specific instructions for THIS sub-task — tell the worker exactly what to produce, how to use its tools fully, and what \"done\" means. Never a one-liner; write the prompt a power-user would write.",
7284
- " • model: the model best suited to this sub-task (omit to use the default).",
7285
- " NEVER spawn a worker with an empty profile. The quality of the worker IS the quality of the profile you write.",
7286
- "3. await_event (kinds:['settled']) to collect each worker. Its result says valid:true only if the deployable check passed.",
7287
- "4. If a worker did NOT deliver, AUTHOR A NEW worker whose systemPrompt names the SPECIFIC failure and how to fix it — never just retry the same prompt.",
7288
- "5. Stop (reply with no tool call) once the work is delivered. You cannot declare done yourself — only a delivered (valid:true) worker counts.",
7289
- ...opts?.goal ? ["", `The goal: ${opts.goal}`] : []
7290
- ].join("\n");
7291
- }
7292
- /** Build a worker AGENT from a profile the supervisor authored: the authored `systemPrompt` +
7293
- * `model` shape the worker's one model call; the deliverable gates settlement (valid ⟺ delivered). */
7294
- function authoredWorker(profile, opts) {
7295
- let artifact;
7296
- const model = profile.model ?? opts.cfg.model;
7297
- const gated = gateOnDeliverable({
7298
- runtime: "router",
7299
- async execute(_t, signal) {
7300
- const res = await routerChatWithUsage({
7301
- ...opts.cfg,
7302
- model
7303
- }, [{
7304
- role: "system",
7305
- content: profile.systemPrompt
7306
- }, {
7307
- role: "user",
7308
- content: opts.taskPrompt
7309
- }], {
7310
- temperature: opts.temperature ?? .4,
7311
- ...signal ? { signal } : {}
7312
- });
7313
- artifact = {
7314
- outRef: contentAddress(res.content),
7315
- out: res.content,
7316
- spent: {
7317
- iterations: 1,
7318
- tokens: res.usage ?? {
7319
- input: 0,
7320
- output: 0
7321
- },
7322
- usd: res.costUsd ?? 0,
7323
- ms: 0
7324
- }
7325
- };
7326
- return artifact;
7327
- },
7328
- teardown: () => Promise.resolve({ destroyed: true }),
7329
- resultArtifact: () => {
7330
- if (!artifact) throw new Error("authoredWorker: resultArtifact read before execute");
7331
- return artifact;
7332
- }
7333
- }, opts.deliverable);
7334
- const spec = {
7335
- profile: { name: profile.name },
7336
- harness: null,
7337
- executor: gated
7338
- };
7339
- return {
7340
- name: profile.name,
7341
- act: async () => "",
7342
- executorSpec: spec
7343
- };
7344
- }
7345
- /** Default thresholds for `ProfileRichnessThresholds` — 600 chars / 6 lines minimum system prompt. */
7346
- const defaultProfileRichnessThresholds = {
7347
- minSystemPromptChars: 600,
7348
- minSystemPromptLines: 6
7349
- };
7350
- /** Read the system prompt from any authored shape: canonical `prompt.systemPrompt`, the sandbox
7351
- * `prompt.system` convention, or a bare-string `prompt`. */
7352
- function resolveSystemPrompt(profile) {
7353
- const pr = profile.prompt;
7354
- if (typeof pr === "string") return pr;
7355
- if (pr && typeof pr === "object") {
7356
- const o = pr;
7357
- if (typeof o.systemPrompt === "string") return o.systemPrompt;
7358
- if (typeof o.system === "string") return o.system;
7359
- }
7360
- return "";
7361
- }
7362
- /** OBSERVE one authored `AgentProfile` and score its richness (no judge verdict is read). The task
7363
- * context (`needsMcp`) lets a domain say "this work needs a data/tool MCP" so a missing MCP counts. */
7364
- function assessAuthoredProfile(profile, opts) {
7365
- const th = {
7366
- ...defaultProfileRichnessThresholds,
7367
- ...opts?.thresholds ?? {}
7368
- };
7369
- const systemPrompt = resolveSystemPrompt(profile);
7370
- const trimmed = systemPrompt.trim();
7371
- const systemPromptChars = trimmed.length;
7372
- const systemPromptLines = trimmed ? trimmed.split("\n").filter((l) => l.trim().length > 0).length : 0;
7373
- const sentenceCount = trimmed ? (trimmed.match(/[.!?](\s|$)/g) ?? []).length || (trimmed ? 1 : 0) : 0;
7374
- const hasDescription = typeof profile.description === "string" && profile.description.trim().length > 0;
7375
- const tools = profile.tools;
7376
- const hasTools = !!tools && Object.keys(tools).length > 0;
7377
- const skills = profile.resources?.skills;
7378
- const hasSkills = Array.isArray(skills) && skills.length > 0;
7379
- const mcp = profile.mcp;
7380
- const hasMcp = !!mcp && Object.keys(mcp).length > 0;
7381
- const subagents = profile.subagents;
7382
- const hasSubagents = !!subagents && Object.keys(subagents).length > 0;
7383
- const reasons = [];
7384
- const promptThin = systemPromptChars < th.minSystemPromptChars || systemPromptLines < th.minSystemPromptLines;
7385
- if (promptThin) reasons.push(`system prompt is thin (${systemPromptChars} chars, ${systemPromptLines} lines; need ≥${th.minSystemPromptChars} chars and ≥${th.minSystemPromptLines} lines)`);
7386
- if (!hasTools) reasons.push("no tools granted (a worker can only act through the tools you grant it)");
7387
- if (!hasSkills) reasons.push("no skills attached (no reusable how-to notes injected)");
7388
- if (opts?.needsMcp && !hasMcp) reasons.push("no MCP server, but the task needs data/tool access");
7389
- const signals = [
7390
- !promptThin,
7391
- hasTools,
7392
- hasSkills,
7393
- hasDescription,
7394
- opts?.needsMcp ? hasMcp : true
7395
- ];
7396
- const richness = signals.filter(Boolean).length / signals.length;
7397
- const thin = promptThin || !hasTools && !hasSkills && !hasMcp;
7398
- return {
7399
- name: profile.name ?? "worker",
7400
- systemPrompt,
7401
- systemPromptChars,
7402
- systemPromptLines,
7403
- sentenceCount,
7404
- hasDescription,
7405
- hasTools,
7406
- hasSkills,
7407
- hasMcp,
7408
- hasSubagents,
7409
- richness,
7410
- thin,
7411
- reasons
7412
- };
7413
- }
7414
- /** Turn a {@link ProfileRichness} verdict into a bus-routable `AnalystFinding` (area `profile-quality`).
7415
- * Severity scales with thinness; the recommended action names the MISSING lever so the supervisor can
7416
- * re-author. `subject` = the worker name so per-worker findings diff cleanly across re-authors. */
7417
- function profileRichnessFinding(richness, opts) {
7418
- const analyst_id = opts?.analystId ?? "profile-richness";
7419
- const subject = richness.name;
7420
- const claim = richness.thin ? `Worker "${richness.name}" was authored as a THIN profile: ${richness.reasons.join("; ")}.` : `Worker "${richness.name}" was authored as a rich profile (richness ${(richness.richness * 100).toFixed(0)}%).`;
7421
- return makeFinding({
7422
- analyst_id,
7423
- severity: richness.thin ? richness.richness < .25 ? "high" : "medium" : "info",
7424
- area: "profile-quality",
7425
- claim,
7426
- subject,
7427
- confidence: .9,
7428
- evidence_refs: [{
7429
- kind: "metric",
7430
- uri: `profile:${subject}`,
7431
- excerpt: `chars=${richness.systemPromptChars} lines=${richness.systemPromptLines} tools=${richness.hasTools} skills=${richness.hasSkills} mcp=${richness.hasMcp} richness=${richness.richness.toFixed(2)}`
7432
- }],
7433
- ...richness.thin ? { recommended_action: `Re-author "${richness.name}" with: ${richness.reasons.join("; ")}.` } : {},
7434
- id_basis: computeFindingId({
7435
- analyst_id,
7436
- area: "profile-quality",
7437
- subject,
7438
- claim: `richness:${richness.thin ? "thin" : "rich"}`
7439
- })
7440
- });
7441
- }
7442
- //#endregion
7443
8453
  //#region src/runtime/supervise/delegate.ts
7444
8454
  /**
7445
8455
  *
@@ -7468,15 +8478,15 @@ const defaultDelegateBudget = {
7468
8478
  maxIterations: 50,
7469
8479
  maxTokens: 2e5
7470
8480
  };
7471
- /** Build the DEFAULT authoring supervisor profile: a router-brained supervisor (`harness: null`)
8481
+ /** Build the DEFAULT authoring supervisor profile: a router-brained supervisor (`harness: cli-base`)
7472
8482
  * whose standing instruction IS the authoring-agent-profiles skill, so it decomposes the intent and
7473
8483
  * AUTHORS a worker profile per sub-task. No worker profile is baked in here. */
7474
8484
  function authoringSupervisorProfile(model, override) {
7475
8485
  return {
7476
8486
  name: override?.name ?? "delegate-supervisor",
7477
- harness: null,
7478
- ...model ? { model } : {},
7479
- systemPrompt: override?.systemPrompt ?? supervisorInstructions()
8487
+ harness: "cli-base",
8488
+ ...model ? { model: { default: model } } : {},
8489
+ prompt: { systemPrompt: override?.systemPrompt ?? supervisorInstructions() }
7480
8490
  };
7481
8491
  }
7482
8492
  /**
@@ -7561,11 +8571,19 @@ function validateDelegateArgs(raw) {
7561
8571
  * conserved `spentTotal`, so the agent always learns the cost — even on a no-winner, never a faked
7562
8572
  * output and never a fabricated zero spend. */
7563
8573
  function toDelegateResult(result) {
7564
- if (result.kind === "no-winner") return {
7565
- status: "no-winner",
7566
- reason: result.reason,
7567
- spentTotal: result.spentTotal
7568
- };
8574
+ if (result.kind === "no-winner") {
8575
+ const rejection = result.error;
8576
+ const error = typeof rejection?.name === "string" && typeof rejection.message === "string" ? {
8577
+ name: rejection.name,
8578
+ message: rejection.message
8579
+ } : void 0;
8580
+ return {
8581
+ status: "no-winner",
8582
+ reason: result.reason,
8583
+ ...error ? { error } : {},
8584
+ spentTotal: result.spentTotal
8585
+ };
8586
+ }
7569
8587
  return {
7570
8588
  status: "winner",
7571
8589
  out: result.out,
@@ -8325,6 +9343,7 @@ async function serveCoordinationMcp(opts) {
8325
9343
  scope: opts.scope,
8326
9344
  blobs: opts.blobs,
8327
9345
  makeWorkerAgent: opts.makeWorkerAgent,
9346
+ ...opts.authorizeDownMessage ? { authorizeDownMessage: opts.authorizeDownMessage } : {},
8328
9347
  perWorker: opts.perWorker,
8329
9348
  ...opts.deliverable ? { deliverable: opts.deliverable } : {},
8330
9349
  ...opts.maxLiveWorkers !== void 0 ? { maxLiveWorkers: opts.maxLiveWorkers } : {},
@@ -8334,11 +9353,13 @@ async function serveCoordinationMcp(opts) {
8334
9353
  ...opts.watchWorkers ? { watchWorkers: opts.watchWorkers } : {},
8335
9354
  ...opts.stallAfterMs !== void 0 ? { stallAfterMs: opts.stallAfterMs } : {},
8336
9355
  ...opts.onEvent ? { onEvent: opts.onEvent } : {},
9356
+ ...opts.replaySettlements ? { replaySettlements: true } : {},
8337
9357
  ...opts.questionPolicy ? { questionPolicy: opts.questionPolicy } : {},
8338
9358
  ...opts.priorQuestions?.length ? { priorQuestions: opts.priorQuestions } : {}
8339
9359
  });
9360
+ await coord.ready();
8340
9361
  const mcp = createMcpServer({
8341
- extraTools: coord.tools,
9362
+ extraTools: [...coord.tools, ...opts.nodeTools ?? []],
8342
9363
  serverName: "coordination"
8343
9364
  });
8344
9365
  const server = createServer((req, res) => {
@@ -8510,45 +9531,81 @@ function assertCoordinationBinding(binding) {
8510
9531
  }
8511
9532
  /** Build a supervisor `Agent` from its profile: the brain resolves from `profile.harness` (backend-as-data), the same resolution rule as every worker. */
8512
9533
  function supervisorAgent(profile, deps) {
8513
- const name = profile.name ?? "supervisor";
8514
- const harness = profile.harness ?? null;
8515
- const profilePrompt = resolveSupervisorSystemPrompt(profile);
9534
+ const stableProfile = detachedSnapshot(profile, "supervisorAgent profile");
9535
+ const resolveTools = deps.resolveSupervisorTools;
9536
+ const observeNodeEvent = deps.observeNodeEvent;
9537
+ const nodeContextSeed = deps.nodeContext === void 0 ? void 0 : detachedSnapshot(deps.nodeContext, "supervisorAgent node context");
9538
+ if ((resolveTools || observeNodeEvent) && !nodeContextSeed) throw new ValidationError("supervisorAgent: nodeContext is required with resolveSupervisorTools or observeNodeEvent");
9539
+ const name = stableProfile.name ?? "supervisor";
9540
+ const harness = stableProfile.harness === void 0 || stableProfile.harness === null || stableProfile.harness === "cli-base" ? null : stableProfile.harness;
9541
+ const profilePrompt = resolveSupervisorSystemPrompt(stableProfile);
8516
9542
  const coordination = deps.coordination ? { ...deps.coordination } : void 0;
8517
9543
  assertCoordinationBinding(coordination);
8518
9544
  if (harness === null && coordination !== void 0) throw new ConfigError("supervisorAgent: coordination binding is only meaningful for a harness-brained supervisor (profile.harness set). A router-brained supervisor calls the coordination verbs in process and serves no MCP, so this binding would be silently ignored.");
8519
- if (harness !== null && deps.compaction) throw new ValidationError("supervisorAgent: compaction is only supported for router-brained supervisors (profile.harness null)");
8520
- if (harness === null) return driverAgent({
8521
- name,
8522
- brain: deps.brain ?? routerBrainFromProfile(profile, deps),
8523
- blobs: deps.blobs,
8524
- makeWorkerAgent: deps.makeWorkerAgent,
8525
- perWorker: deps.perWorker,
8526
- systemPrompt: resolveSupervisorSystemPrompt(profile, defaultSupervisorPrompt) ?? defaultSupervisorPrompt,
8527
- ...deps.deliverable ? { deliverable: deps.deliverable } : {},
8528
- ...deps.maxLiveWorkers !== void 0 ? { maxLiveWorkers: deps.maxLiveWorkers } : {},
8529
- ...deps.extraTools ? { extraTools: deps.extraTools } : {},
8530
- ...deps.executeExtraTool ? { executeExtraTool: deps.executeExtraTool } : {},
8531
- ...deps.analysts ? { analysts: deps.analysts } : {},
8532
- ...deps.analyzeOnSettle ? { analyzeOnSettle: deps.analyzeOnSettle } : {},
8533
- ...deps.watchWorkers ? { watchWorkers: deps.watchWorkers } : {},
8534
- ...deps.stallAfterMs !== void 0 ? { stallAfterMs: deps.stallAfterMs } : {},
8535
- ...deps.stopRule ? { stopRule: deps.stopRule } : {},
8536
- ...deps.onProgressStop ? { onProgressStop: deps.onProgressStop } : {},
8537
- ...deps.maxTurns !== void 0 ? { maxTurns: deps.maxTurns } : {},
8538
- ...deps.compaction ? { compaction: deps.compaction } : {},
8539
- ...deps.onEvent ? { onEvent: deps.onEvent } : {},
8540
- ...deps.priorCoordination ? { priorCoordination: deps.priorCoordination } : {},
8541
- ...deps.finalizer ? { finalizer: deps.finalizer } : {}
8542
- });
9545
+ if (harness !== null && deps.compaction) throw new ValidationError("supervisorAgent: compaction is only supported for router-brained supervisors (profile.harness omitted or cli-base)");
9546
+ if (harness === null) {
9547
+ const brain = deps.brain ?? routerBrainFromProfile(stableProfile, deps);
9548
+ const inbox = createInbox();
9549
+ const build = (priorCoordination, nodeTools, onEvent) => driverAgent({
9550
+ name,
9551
+ brain,
9552
+ blobs: deps.blobs,
9553
+ makeWorkerAgent: deps.makeWorkerAgent,
9554
+ ...deps.authorizeDownMessage ? { authorizeDownMessage: deps.authorizeDownMessage } : {},
9555
+ perWorker: deps.perWorker,
9556
+ systemPrompt: resolveSupervisorSystemPrompt(stableProfile, defaultSupervisorPrompt) ?? defaultSupervisorPrompt,
9557
+ ...deps.deliverable ? { deliverable: deps.deliverable } : {},
9558
+ ...nodeTools?.length ? { nodeTools } : {},
9559
+ ...deps.maxLiveWorkers !== void 0 ? { maxLiveWorkers: deps.maxLiveWorkers } : {},
9560
+ ...deps.extraTools ? { extraTools: deps.extraTools } : {},
9561
+ ...deps.executeExtraTool ? { executeExtraTool: deps.executeExtraTool } : {},
9562
+ ...deps.analysts ? { analysts: deps.analysts } : {},
9563
+ ...deps.analyzeOnSettle ? { analyzeOnSettle: deps.analyzeOnSettle } : {},
9564
+ ...deps.watchWorkers ? { watchWorkers: deps.watchWorkers } : {},
9565
+ ...deps.stallAfterMs !== void 0 ? { stallAfterMs: deps.stallAfterMs } : {},
9566
+ ...deps.stopRule ? { stopRule: deps.stopRule } : {},
9567
+ ...deps.onProgressStop ? { onProgressStop: deps.onProgressStop } : {},
9568
+ ...deps.maxTurns !== void 0 ? { maxTurns: deps.maxTurns } : {},
9569
+ ...deps.compaction ? { compaction: deps.compaction } : {},
9570
+ ...onEvent ? { onEvent } : {},
9571
+ ...deps.replaySettlements ? { replaySettlements: true } : {},
9572
+ ...priorCoordination ? { priorCoordination } : {},
9573
+ ...deps.finalizer ? { finalizer: deps.finalizer } : {},
9574
+ inbox
9575
+ });
9576
+ if (!deps.loadPriorCoordination && !resolveTools && !observeNodeEvent) return build(deps.priorCoordination, void 0, deps.onEvent);
9577
+ return {
9578
+ name,
9579
+ deliver(message) {
9580
+ return inbox.deliver(message);
9581
+ },
9582
+ async act(task, scope) {
9583
+ const context = nodeContextSeed ? supervisorNodeContext(nodeContextSeed, stableProfile, task, scope) : void 0;
9584
+ const priorCoordination = await deps.loadPriorCoordination?.();
9585
+ const nodeTools = resolveTools && context ? await bindSupervisorTools(resolveTools, context, scope.signal) : void 0;
9586
+ const onEvent = bindSupervisorNodeObserver(context, observeNodeEvent, deps.onEvent);
9587
+ return build(priorCoordination, nodeTools, onEvent).act(task, scope);
9588
+ }
9589
+ };
9590
+ }
8543
9591
  const driveHarness = deps.driveHarness;
8544
9592
  if (!driveHarness) throw new ValidationError(`supervisorAgent: profile.harness="${harness}" needs deps.driveHarness (how to run the harness with the coordination MCP mounted)`);
8545
- return {
9593
+ const deliver = driveHarness.deliver?.bind(driveHarness);
9594
+ const externalAgent = {
8546
9595
  name,
9596
+ ...deliver ? { deliver(message) {
9597
+ return deliver(message);
9598
+ } } : {},
8547
9599
  async act(task, scope) {
9600
+ const context = nodeContextSeed ? supervisorNodeContext(nodeContextSeed, stableProfile, task, scope) : void 0;
9601
+ const priorCoordination = deps.loadPriorCoordination ? await deps.loadPriorCoordination() : deps.priorCoordination;
9602
+ const nodeTools = resolveTools && context ? await bindSupervisorTools(resolveTools, context, scope.signal) : void 0;
9603
+ const onEvent = bindSupervisorNodeObserver(context, observeNodeEvent, deps.onEvent);
8548
9604
  const mcp = await serveCoordinationMcp({
8549
9605
  scope,
8550
9606
  blobs: deps.blobs,
8551
9607
  makeWorkerAgent: deps.makeWorkerAgent,
9608
+ ...deps.authorizeDownMessage ? { authorizeDownMessage: deps.authorizeDownMessage } : {},
8552
9609
  perWorker: deps.perWorker,
8553
9610
  ...coordination?.host !== void 0 ? { host: coordination.host } : {},
8554
9611
  ...coordination?.port !== void 0 ? { port: coordination.port } : {},
@@ -8559,17 +9616,24 @@ function supervisorAgent(profile, deps) {
8559
9616
  ...deps.analyzeOnSettle ? { analyzeOnSettle: deps.analyzeOnSettle } : {},
8560
9617
  ...deps.watchWorkers ? { watchWorkers: deps.watchWorkers } : {},
8561
9618
  ...deps.stallAfterMs !== void 0 ? { stallAfterMs: deps.stallAfterMs } : {},
8562
- ...deps.onEvent ? { onEvent: deps.onEvent } : {},
8563
- ...deps.priorCoordination?.questions.length ? { priorQuestions: deps.priorCoordination.questions } : {}
9619
+ ...onEvent ? { onEvent } : {},
9620
+ ...deps.replaySettlements ? { replaySettlements: true } : {},
9621
+ ...priorCoordination?.questions.length ? { priorQuestions: priorCoordination.questions } : {},
9622
+ ...nodeTools?.length ? { nodeTools } : {}
8564
9623
  });
8565
9624
  try {
8566
9625
  try {
8567
9626
  await driveHarness({
8568
- profile,
9627
+ profile: stableProfile,
8569
9628
  ...profilePrompt !== void 0 ? { systemPrompt: profilePrompt } : {},
8570
9629
  task,
8571
9630
  scope,
8572
- coordinationMcpUrl: mcp.url
9631
+ coordinationMcpUrl: mcp.url,
9632
+ coordinationTools: (nodeTools ?? []).map(({ name, description, inputSchema }) => ({
9633
+ name,
9634
+ description,
9635
+ inputSchema
9636
+ }))
8573
9637
  });
8574
9638
  } catch (error) {
8575
9639
  if (!mcp.submittedResult()) throw error;
@@ -8588,9 +9652,57 @@ function supervisorAgent(profile, deps) {
8588
9652
  }
8589
9653
  }
8590
9654
  };
9655
+ const runtime = runtimeOwnedScopeOwnerRuntime(driveHarness);
9656
+ return runtime === void 0 ? externalAgent : attestRuntimeOwnedScopeOwner(externalAgent, runtime);
9657
+ }
9658
+ function supervisorNodeContext(seed, profile, task, scope) {
9659
+ return detachedSnapshot({
9660
+ ...seed,
9661
+ nodeId: scope.view.root,
9662
+ profile,
9663
+ task
9664
+ }, "supervisorAgent trusted node context");
9665
+ }
9666
+ async function bindSupervisorTools(resolveTools, context, signal) {
9667
+ const resolved = await resolveTools(context);
9668
+ if (!Array.isArray(resolved)) throw new ValidationError("supervisorAgent: resolveSupervisorTools must return an array");
9669
+ const invocationContext = Object.freeze({
9670
+ ...context,
9671
+ signal
9672
+ });
9673
+ const names = new Set(coordinationVerbNames);
9674
+ return Object.freeze(resolved.map((rawTool, index) => {
9675
+ if (typeof rawTool !== "object" || rawTool === null || Array.isArray(rawTool)) throw new ValidationError(`supervisorAgent: resolved tool at index ${index} must be a descriptor`);
9676
+ const { name, description, inputSchema, handler } = rawTool;
9677
+ if (typeof name !== "string" || name.length === 0) throw new ValidationError(`supervisorAgent: resolved tool at index ${index} needs a non-empty name`);
9678
+ if (names.has(name)) throw new ValidationError(`supervisorAgent: resolved tool "${name}" collides with a coordination verb or another resolved tool`);
9679
+ names.add(name);
9680
+ if (typeof description !== "string" || description.length === 0) throw new ValidationError(`supervisorAgent: resolved tool "${name}" needs a description`);
9681
+ if (typeof inputSchema !== "object" || inputSchema === null || Array.isArray(inputSchema)) throw new ValidationError(`supervisorAgent: resolved tool "${name}" needs an inputSchema`);
9682
+ if (typeof handler !== "function") throw new ValidationError(`supervisorAgent: resolved tool "${name}" needs a handler`);
9683
+ const descriptor = detachedSnapshot({
9684
+ name,
9685
+ description,
9686
+ inputSchema
9687
+ }, `supervisorAgent resolved tool ${JSON.stringify(name)}`);
9688
+ return Object.freeze({
9689
+ ...descriptor,
9690
+ handler: (raw) => handler(detachedSnapshot(raw, `supervisorAgent tool ${JSON.stringify(name)} input`), invocationContext)
9691
+ });
9692
+ }));
9693
+ }
9694
+ function bindSupervisorNodeObserver(context, observeNodeEvent, onEvent) {
9695
+ if (!observeNodeEvent && !onEvent) return void 0;
9696
+ return async (event, record) => {
9697
+ if (observeNodeEvent) {
9698
+ if (!context) throw new ValidationError("supervisorAgent: observeNodeEvent has no trusted node context");
9699
+ await observeNodeEvent(context, event, record);
9700
+ }
9701
+ await onEvent?.(event, record);
9702
+ };
8591
9703
  }
8592
9704
  function routerBrainFromProfile(profile, deps) {
8593
- if (!deps.router) throw new ValidationError("supervisorAgent: a router-brained supervisor (harness null) needs deps.router (or deps.brain)");
9705
+ if (!deps.router) throw new ValidationError("supervisorAgent: a router-brained supervisor (harness omitted or cli-base) needs deps.router (or deps.brain)");
8594
9706
  const modelId = resolveSupervisorModelId(profile);
8595
9707
  return routerBrain({
8596
9708
  ...deps.router,
@@ -8600,6 +9712,15 @@ function routerBrainFromProfile(profile, deps) {
8600
9712
  //#endregion
8601
9713
  //#region src/runtime/supervise/supervise.ts
8602
9714
  /**
9715
+ * `supervise` — the one-call "just invoke the supervisor". Builds + runs a supervisor from its
9716
+ * profile with sensible defaults, so the common case is `supervise(profile, task, { backend, budget })`
9717
+ * instead of hand-wiring `blobs` / `perWorker` / `journal` / `executors` / `maxDepth`. The raw seams
9718
+ * (`supervisorAgent` + `createSupervisor().run`) stay available for power use.
9719
+ *
9720
+ * `workerFromBackend` derives the worker seam (`makeWorkerAgent`) from a backend config + an optional
9721
+ * completion oracle — so "where the workers run" is one data choice, not a hand-rolled factory.
9722
+ */
9723
+ /**
8603
9724
  * Build the worker seam from a backend (WHERE workers run) + an optional completion oracle (the
8604
9725
  * deliverable check that makes "settled ⟺ delivered" true — the guard against "ran but didn't
8605
9726
  * deliver"). The ONE place a backend becomes a spawnable worker.
@@ -8613,28 +9734,291 @@ function routerBrainFromProfile(profile, deps) {
8613
9734
  * ahead of the worker seam.
8614
9735
  */
8615
9736
  function workerFromBackend(backend, deliverable, seams) {
8616
- return (rawProfile) => {
8617
- const p = rawProfile ?? {};
8618
- const name = typeof p.name === "string" && p.name.length > 0 ? p.name : "worker";
8619
- const spec = {
8620
- profile: rawProfile,
8621
- harness: null
8622
- };
8623
- const ctx = {
8624
- signal: new AbortController().signal,
8625
- seams: seams?.() ?? {}
9737
+ const capturedBackend = captureReusableExecutorConfig(backend, "workerFromBackend");
9738
+ const unscopedNamespace = randomUUID();
9739
+ let unscopedOrdinal = 0;
9740
+ return (rawProfile, spawnContext) => {
9741
+ const parsed = agentProfileSchema.safeParse(canonicalizeAuthoredProfile(rawProfile));
9742
+ if (!parsed.success) throw new ValidationError(`workerFromBackend: invalid AgentProfile: ${parsed.error.message}`);
9743
+ const profile = parsed.data;
9744
+ assertBackendProfileMaterialization(profile, capturedBackend, "workerFromBackend");
9745
+ const name = profile.name ?? "worker";
9746
+ const assignmentId = spawnContext?.assignmentId ?? `unscoped:${unscopedNamespace}:${unscopedOrdinal++}`;
9747
+ const baseFactory = createExecutor(bindReusableExecutorExecutionId(capturedBackend, externalExecutionId("supervised-worker", { assignmentId })));
9748
+ const executorFactory = (spec, ctx) => {
9749
+ const extraSeams = seams?.();
9750
+ const built = baseFactory(spec, extraSeams === void 0 ? ctx : {
9751
+ ...ctx,
9752
+ seams: {
9753
+ ...extraSeams,
9754
+ ...ctx.seams
9755
+ }
9756
+ });
9757
+ return deliverable ? gateOnDeliverable(built, deliverable) : built;
8626
9758
  };
8627
- const built = createExecutor(backend)(spec, ctx);
8628
- const executor = deliverable ? gateOnDeliverable(built, deliverable) : built;
8629
9759
  return {
8630
9760
  name,
8631
9761
  act: async () => "",
8632
9762
  executorSpec: {
8633
- ...spec,
8634
- executor
9763
+ profile,
9764
+ harness: null,
9765
+ executorFactory,
9766
+ ...spawnContext?.execution ? { execution: spawnContext.execution } : {}
9767
+ }
9768
+ };
9769
+ };
9770
+ }
9771
+ function externalExecutionId(kind, identity) {
9772
+ return `${kind}-${canonicalCandidateDigest({
9773
+ kind,
9774
+ identity
9775
+ }).slice(7)}`;
9776
+ }
9777
+ function backendProfileMaterialization(backend) {
9778
+ switch (backend.backend) {
9779
+ case "bridge":
9780
+ case "sandbox":
9781
+ case "provider": return fullProfileMaterialization;
9782
+ case "cli-worktree": return backend.bridge ? fullProfileMaterialization : worktreeCliProfileMaterialization;
9783
+ case "router":
9784
+ case "router-tools":
9785
+ case "pi": return promptModelProfileMaterialization;
9786
+ case "cli": return controlProfileMaterialization;
9787
+ }
9788
+ }
9789
+ function assertProfileContract(profile, contract, context) {
9790
+ assertProfileMaterialization({
9791
+ contract,
9792
+ changedAxes: profileMaterializationAxes(profile),
9793
+ context
9794
+ });
9795
+ }
9796
+ function assertBackendProfileMaterialization(profile, backend, context) {
9797
+ assertProfileContract(profile, backendProfileMaterialization(backend), context);
9798
+ }
9799
+ /**
9800
+ * The ROOT router-brained supervisor's materialization claim. The router arm consumes the
9801
+ * identity fields, the resolved system prompt (`systemPrompt` + `prompt.instructions` +
9802
+ * `resources.instructions`), and the resolved model id (`model.default`); the remaining model
9803
+ * HINTS (`small`, `provider`, `reasoningEffort`, `metadata`) are accepted as documented-unhonored
9804
+ * router-arm material (`supervisorAgent`'s contract table states each one), so a canonical
9805
+ * profile carrying ordinary hints is not refused. Every behavioral axis — tools, permissions,
9806
+ * MCP, hooks, modes, subagents, file resources — still fails loud before any compute.
9807
+ */
9808
+ const routerSupervisorProfileMaterialization = defineProfileMaterializationContract({
9809
+ name: "router-supervisor-execution",
9810
+ axes: [
9811
+ "name",
9812
+ "description",
9813
+ "version",
9814
+ "tags",
9815
+ "systemPrompt",
9816
+ "instructions",
9817
+ "resourceInstructions",
9818
+ "modelDefault",
9819
+ "modelSmall",
9820
+ "modelProvider",
9821
+ "modelReasoningEffort",
9822
+ "modelMetadata",
9823
+ "harness",
9824
+ "metadata"
9825
+ ]
9826
+ });
9827
+ const coordinationMcpAlias = "agent-runtime-coordination";
9828
+ const defaultAllowedMcpHosts = [];
9829
+ Object.freeze(defaultAllowedMcpHosts);
9830
+ /** Manager-authored profiles are untrusted until product policy says otherwise. Remote MCP and
9831
+ * ambient connection grants therefore fail closed by default, in addition to local MCP and hooks. */
9832
+ const DEFAULT_AUTHORED_PROFILE_SECURITY_POLICY = Object.freeze({
9833
+ allowLocalMcp: false,
9834
+ allowHooks: false,
9835
+ allowedMcpHosts: defaultAllowedMcpHosts,
9836
+ allowConnections: false
9837
+ });
9838
+ function isExternalSupervisor(profile) {
9839
+ return profile.harness !== void 0 && profile.harness !== "cli-base";
9840
+ }
9841
+ function automaticDriverBackendSupported(backend) {
9842
+ return backend.backend === "bridge";
9843
+ }
9844
+ function backendProfileOverlays(backend) {
9845
+ if (!backend) return [];
9846
+ if (backend.backend === "bridge" && backend.agentProfile) return [backend.agentProfile];
9847
+ if (backend.backend === "cli-worktree" && backend.bridge?.agentProfile) return [backend.bridge.agentProfile];
9848
+ return [];
9849
+ }
9850
+ /** Run a harness-brained manager through the same executor factory as its children. The manager's
9851
+ * full profile is preserved, the live coordination server is added under one reserved alias, and
9852
+ * every streamed turn is charged to the manager's scope before it may continue. */
9853
+ function driveHarnessFromBackend(backend, executionId, now = Date.now) {
9854
+ const boundBackend = bindReusableExecutorExecutionId(captureReusableExecutorConfig(backend, "driveHarnessFromBackend"), executionId);
9855
+ const baseFactory = createExecutor(boundBackend);
9856
+ let activeExecutor;
9857
+ const drive = async ({ profile, task, scope, coordinationMcpUrl, coordinationTools }) => {
9858
+ const initialBudget = scope.budget;
9859
+ if (!(scope.view.inFlight > 0 || scope.view.waiting > 0) && (initialBudget.tokensLeft <= 0 || initialBudget.iterationsLeft <= 0 || initialBudget.usdCapped && initialBudget.usdLeft <= 0 || initialBudget.deadlineMs > 0 && now() >= initialBudget.deadlineMs)) throw new ValidationError("driveHarnessFromBackend: supervisor budget exhausted");
9860
+ const canonicalDriverProfile = agentProfileSchema.parse(profile);
9861
+ if (canonicalDriverProfile.mcp?.[coordinationMcpAlias] !== void 0) throw new ValidationError(`driveHarnessFromBackend: profile MCP alias ${JSON.stringify(coordinationMcpAlias)} is reserved`);
9862
+ const effectiveProfile = agentProfileSchema.parse({
9863
+ ...canonicalDriverProfile,
9864
+ mcp: {
9865
+ ...canonicalDriverProfile.mcp,
9866
+ [coordinationMcpAlias]: {
9867
+ transport: "http",
9868
+ url: coordinationMcpUrl
9869
+ }
8635
9870
  }
9871
+ });
9872
+ const stableCoordinationTools = detachedSnapshot(coordinationTools, "driveHarnessFromBackend coordination tools");
9873
+ const spec = {
9874
+ profile: effectiveProfile,
9875
+ harness: boundBackend.backend === "sandbox" ? effectiveProfile.harness ?? boundBackend.harness ?? null : null
9876
+ };
9877
+ const executor = baseFactory(spec, {
9878
+ signal: scope.signal,
9879
+ node: scopeOwnerExecutorNodeContext(scope),
9880
+ seams: {}
9881
+ });
9882
+ activeExecutor = executor;
9883
+ let completed = false;
9884
+ let started = false;
9885
+ let terminalAccountingCaptured = false;
9886
+ let pendingUsage = [];
9887
+ let teardownStarted = false;
9888
+ const deadlineAtMs = scope.budget.deadlineMs || void 0;
9889
+ const teardownOnce = async (grace) => {
9890
+ if (teardownStarted) return;
9891
+ teardownStarted = true;
9892
+ await teardownExecutor(executor, grace, deadlineAtMs, now);
8636
9893
  };
9894
+ const meterPending = async () => {
9895
+ if (pendingUsage.length === 0) return;
9896
+ const batch = pendingUsage;
9897
+ pendingUsage = [];
9898
+ await scope.meter(spendFromUsageEvents(batch), {
9899
+ role: "driver",
9900
+ runtime: executor.runtime
9901
+ });
9902
+ const budget = scope.budget;
9903
+ if (budget.tokensLeft <= 0 || budget.usdCapped && budget.usdLeft <= 0 || budget.deadlineMs > 0 && now() >= budget.deadlineMs) throw new ValidationError("driveHarnessFromBackend: supervisor budget exhausted");
9904
+ };
9905
+ let failed = false;
9906
+ let failure;
9907
+ try {
9908
+ const declaration = runtimeOwnedExecutorMaterialization(executor);
9909
+ const executionBinding = runtimeOwnedExecutorExecutionBinding(executor);
9910
+ if (declaration === void 0 || executionBinding === void 0) throw new ValidationError(`driveHarnessFromBackend: built-in runtime ${JSON.stringify(executor.runtime)} has no trusted materialization declaration or execution binding`);
9911
+ await recordScopeOwnerMaterialization(scope, executor.runtime, {
9912
+ ...declaration,
9913
+ effectiveProfile: canonicalDriverProfile,
9914
+ platformAttachments: { [coordinationMcpAlias]: {
9915
+ kind: "coordination-mcp",
9916
+ transport: "http",
9917
+ tools: stableCoordinationTools
9918
+ } }
9919
+ }, {
9920
+ ...executionBinding,
9921
+ binding: {
9922
+ stableBinding: executionBinding.binding,
9923
+ platformAttachments: { [coordinationMcpAlias]: {
9924
+ transport: "http",
9925
+ url: coordinationMcpUrl
9926
+ } }
9927
+ },
9928
+ descriptor: {
9929
+ ...executionBinding.descriptor,
9930
+ coordination: true
9931
+ }
9932
+ });
9933
+ if (executor.budgetExempt) throw new ValidationError(`driveHarnessFromBackend: runtime ${JSON.stringify(executor.runtime)} does not report usage and cannot drive a budgeted supervisor`);
9934
+ started = true;
9935
+ const run = executor.execute(task, scope.signal);
9936
+ if (isAsyncIterable(run)) {
9937
+ for await (const event of run) if (event.kind === "iteration") await meterPending();
9938
+ else pendingUsage.push(event);
9939
+ await meterPending();
9940
+ const artifact = executor.resultArtifact();
9941
+ terminalAccountingCaptured = true;
9942
+ if (artifact.spent.tokensKnown === false || artifact.spent.usdKnown === false) await scope.meter({
9943
+ iterations: 0,
9944
+ tokens: {
9945
+ input: 0,
9946
+ output: 0
9947
+ },
9948
+ ...artifact.spent.tokensKnown === false ? { tokensKnown: false } : {},
9949
+ usd: 0,
9950
+ ...artifact.spent.usdKnown === false ? { usdKnown: false } : {},
9951
+ ms: 0
9952
+ }, {
9953
+ role: "driver",
9954
+ runtime: executor.runtime,
9955
+ telemetry: "unknown"
9956
+ });
9957
+ } else {
9958
+ const artifact = await run;
9959
+ terminalAccountingCaptured = true;
9960
+ await scope.meter({
9961
+ ...artifact.spent,
9962
+ iterations: 0
9963
+ }, {
9964
+ role: "driver",
9965
+ runtime: executor.runtime
9966
+ });
9967
+ }
9968
+ completed = true;
9969
+ } catch (error) {
9970
+ failed = true;
9971
+ failure = error;
9972
+ } finally {
9973
+ try {
9974
+ await meterPending();
9975
+ } catch (error) {
9976
+ if (!failed) {
9977
+ failed = true;
9978
+ failure = error;
9979
+ }
9980
+ }
9981
+ if (failed && started && !terminalAccountingCaptured) try {
9982
+ await scope.meter({
9983
+ iterations: 0,
9984
+ tokens: {
9985
+ input: 0,
9986
+ output: 0
9987
+ },
9988
+ tokensKnown: false,
9989
+ usd: 0,
9990
+ usdKnown: false,
9991
+ ms: 0
9992
+ }, {
9993
+ role: "driver",
9994
+ runtime: executor.runtime,
9995
+ telemetry: "unknown-after-failure"
9996
+ });
9997
+ } catch (error) {
9998
+ const budget = scope.budget;
9999
+ if (budget.tokensKnown !== false || budget.usdCapped && budget.usdKnown !== false) failure = error;
10000
+ }
10001
+ try {
10002
+ await teardownOnce(completed ? DEFAULT_SUCCESSFUL_SHUTDOWN_MS : "brutalKill");
10003
+ } catch (error) {
10004
+ if (!failed) {
10005
+ failed = true;
10006
+ failure = error;
10007
+ }
10008
+ }
10009
+ if (activeExecutor === executor) activeExecutor = void 0;
10010
+ }
10011
+ if (failed) throw failure;
10012
+ };
10013
+ drive.deliver = (message) => {
10014
+ const deliver = activeExecutor?.deliver;
10015
+ if (!deliver) return false;
10016
+ return deliver.call(activeExecutor, message) !== false;
8637
10017
  };
10018
+ return attestRuntimeOwnedScopeOwner(drive, "cli");
10019
+ }
10020
+ function isAsyncIterable(value) {
10021
+ return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
8638
10022
  }
8639
10023
  /** Resolve one option that may be given as a value OR as a name into `opts.registry`. Both failure
8640
10024
  * modes name the option, the requested name, and the table it was looked up in — a typo must not
@@ -8648,80 +10032,473 @@ function resolveNamed(option, table, value, registry) {
8648
10032
  if (entry === void 0) throw new ConfigError(`supervise: opts.${option} = ${JSON.stringify(value)} is not in opts.registry.${table} — the table resolved no entry under that name`);
8649
10033
  return entry;
8650
10034
  }
8651
- /** A quarter of the token pool per worker → ~4 workers fit before `poolStarved` halts spawning. */
10035
+ function captureDeliverable(deliverable, context) {
10036
+ if (typeof deliverable !== "object" || deliverable === null || Array.isArray(deliverable)) throw new ValidationError(`${context}: deliverable must be an object`);
10037
+ if (typeof deliverable.check !== "function") throw new ValidationError(`${context}: deliverable.check must be a function`);
10038
+ return Object.freeze({
10039
+ ...detachedSnapshot({ describe: deliverable.describe }, `${context} configuration`),
10040
+ check: deliverable.check
10041
+ });
10042
+ }
10043
+ /** Capture the public one-call configuration before any asynchronous work starts. Decision data is
10044
+ * detached and frozen; executable ports are copied as the exact references selected at intake.
10045
+ * Service internals intentionally remain live, while replacing a callback/service on the caller's
10046
+ * mutable options object can no longer change an in-flight run. */
10047
+ function captureSuperviseOptions(opts) {
10048
+ const { backend, driverBackend, deliverable, resolveDeliverable, router, compaction, watchWorkers, analysts, makeWorkerAgent, blobs, journal, probes, registry, hooks, otel, authorizeSpawn, authorizeMessage, isDriverProfile, brain, driveHarness, resolveDriveHarness, resolveSupervisorTools, onCoordinationEvent, executeExtraTool, stopRule, onProgressStop, finalizer, now, signal, rootHandle, ...decisionData } = opts;
10049
+ const capturedData = detachedSnapshot(decisionData, "supervise options");
10050
+ const capturedBackend = backend === void 0 ? void 0 : snapshotExecutorConfig(backend);
10051
+ const capturedDriverBackend = driverBackend === void 0 ? void 0 : snapshotExecutorConfig(driverBackend);
10052
+ const capturedDeliverable = deliverable === void 0 || typeof deliverable === "string" ? deliverable : captureDeliverable(deliverable, "supervise deliverable");
10053
+ const capturedRouter = router === void 0 ? void 0 : (() => {
10054
+ const { complete, ...routerData } = router;
10055
+ return Object.freeze({
10056
+ ...detachedSnapshot(routerData, "supervise router configuration"),
10057
+ ...complete === void 0 ? {} : { complete }
10058
+ });
10059
+ })();
10060
+ const capturedCompaction = compaction === void 0 ? void 0 : (() => {
10061
+ const { distill, estimateTokens, onCompact, ...compactionData } = compaction;
10062
+ return Object.freeze({
10063
+ ...detachedSnapshot(compactionData, "supervise compaction configuration"),
10064
+ ...distill === void 0 ? {} : { distill },
10065
+ ...estimateTokens === void 0 ? {} : { estimateTokens },
10066
+ ...onCompact === void 0 ? {} : { onCompact }
10067
+ });
10068
+ })();
10069
+ const capturedWatchWorkers = watchWorkers === void 0 ? void 0 : Object.freeze({
10070
+ ...detachedSnapshot({ maxFindingsPerWorker: watchWorkers.maxFindingsPerWorker }, "supervise worker-watch configuration"),
10071
+ ...watchWorkers.detectors === void 0 ? {} : { detectors: Object.freeze([...watchWorkers.detectors]) }
10072
+ });
10073
+ const capturedAnalysts = analysts === void 0 || typeof analysts === "string" ? analysts : Object.freeze({
10074
+ kinds: detachedSnapshot(analysts.kinds, "supervise analyst kinds"),
10075
+ run: analysts.run
10076
+ });
10077
+ return Object.freeze({
10078
+ ...capturedData,
10079
+ ...capturedBackend === void 0 ? {} : { backend: capturedBackend },
10080
+ ...capturedDriverBackend === void 0 ? {} : { driverBackend: capturedDriverBackend },
10081
+ ...capturedDeliverable === void 0 ? {} : { deliverable: capturedDeliverable },
10082
+ ...resolveDeliverable === void 0 ? {} : { resolveDeliverable },
10083
+ ...capturedRouter === void 0 ? {} : { router: capturedRouter },
10084
+ ...capturedCompaction === void 0 ? {} : { compaction: capturedCompaction },
10085
+ ...capturedWatchWorkers === void 0 ? {} : { watchWorkers: capturedWatchWorkers },
10086
+ ...capturedAnalysts === void 0 ? {} : { analysts: capturedAnalysts },
10087
+ ...makeWorkerAgent === void 0 ? {} : { makeWorkerAgent },
10088
+ ...blobs === void 0 ? {} : { blobs },
10089
+ ...journal === void 0 ? {} : { journal },
10090
+ ...probes === void 0 ? {} : { probes },
10091
+ ...authorizeSpawn === void 0 ? {} : { authorizeSpawn },
10092
+ ...authorizeMessage === void 0 ? {} : { authorizeMessage },
10093
+ ...isDriverProfile === void 0 ? {} : { isDriverProfile },
10094
+ ...brain === void 0 ? {} : { brain },
10095
+ ...driveHarness === void 0 ? {} : { driveHarness },
10096
+ ...resolveDriveHarness === void 0 ? {} : { resolveDriveHarness },
10097
+ ...resolveSupervisorTools === void 0 ? {} : { resolveSupervisorTools },
10098
+ ...onCoordinationEvent === void 0 ? {} : { onCoordinationEvent },
10099
+ ...executeExtraTool === void 0 ? {} : { executeExtraTool },
10100
+ ...stopRule === void 0 ? {} : { stopRule },
10101
+ ...onProgressStop === void 0 ? {} : { onProgressStop },
10102
+ ...finalizer === void 0 ? {} : { finalizer },
10103
+ ...now === void 0 ? {} : { now },
10104
+ ...signal === void 0 ? {} : { signal },
10105
+ ...rootHandle === void 0 ? {} : { rootHandle },
10106
+ ...registry === void 0 ? {} : { registry },
10107
+ ...hooks === void 0 ? {} : { hooks },
10108
+ ...otel === void 0 ? {} : { otel }
10109
+ });
10110
+ }
10111
+ /** A quarter of token and optional dollar capacity per worker; nested managers partition again. */
8652
10112
  function defaultPerWorker(budget) {
8653
10113
  return {
8654
- maxIterations: budget.maxIterations,
8655
- maxTokens: Math.max(1, Math.floor(budget.maxTokens / 4))
10114
+ maxIterations: Math.max(1, Math.floor(budget.maxIterations / 4)),
10115
+ maxTokens: Math.max(1, Math.floor(budget.maxTokens / 4)),
10116
+ ...budget.maxUsd !== void 0 ? { maxUsd: budget.maxUsd / 4 } : {}
8656
10117
  };
8657
10118
  }
10119
+ function freezeDetached(value) {
10120
+ return detachedSnapshot(value, "supervise");
10121
+ }
10122
+ function freezeDetachedProfile(value) {
10123
+ return freezeDetached(agentProfileSchema.parse(value));
10124
+ }
10125
+ /**
10126
+ * Map the two loose `SupervisorProfile` spellings onto their canonical `AgentProfile` form before
10127
+ * the strict schema parse, so both documented spellings run the SAME canonical pipeline and share
10128
+ * one identity digest:
10129
+ * - a string `model` IS `model.default`;
10130
+ * - a top-level `systemPrompt` IS `prompt.systemPrompt` (two disagreeing values are a fault);
10131
+ * - `harness: null` selects the router brain, which canonically is an ABSENT harness.
10132
+ * A canonical profile passes through byte-identical; every other field is left for the schema to
10133
+ * accept or refuse.
10134
+ */
10135
+ function canonicalSupervisorProfileInput(profile) {
10136
+ if (typeof profile !== "object" || profile === null) return profile;
10137
+ const { harness, model, systemPrompt, prompt, ...rest } = profile;
10138
+ const promptSystem = prompt?.systemPrompt;
10139
+ if (systemPrompt !== void 0 && promptSystem !== void 0 && systemPrompt !== promptSystem) throw new ValidationError("supervise: profile.prompt.systemPrompt and profile.systemPrompt are both set and differ — they are the same standing instruction, so keep exactly one");
10140
+ const canonicalPrompt = systemPrompt !== void 0 ? {
10141
+ ...prompt,
10142
+ systemPrompt
10143
+ } : prompt;
10144
+ return {
10145
+ ...rest,
10146
+ ...harness === null || harness === void 0 ? {} : { harness },
10147
+ ...model === void 0 ? {} : { model: typeof model === "string" ? { default: model } : model },
10148
+ ...canonicalPrompt === void 0 ? {} : { prompt: canonicalPrompt }
10149
+ };
10150
+ }
10151
+ function canonicalExecution(profile, task, rawExecution, context) {
10152
+ const execution = rawExecution === void 0 ? void 0 : freezeDetached(rawExecution);
10153
+ if (execution !== void 0) {
10154
+ if (typeof execution !== "object" || execution === null || Array.isArray(execution)) throw new ValidationError(`${context}: execution must be an object`);
10155
+ const unknown = Object.keys(execution).filter((key) => key !== "candidateDigest" && key !== "correlation");
10156
+ if (unknown.length > 0) throw new ValidationError(`${context}: unknown execution fields: ${unknown.join(", ")}`);
10157
+ }
10158
+ const identity = deriveNodeExecutionIdentity({
10159
+ profile,
10160
+ execution
10161
+ }, task);
10162
+ if (!identity?.profileDigest || !identity.taskDigest) throw new ValidationError(`${context}: profile and task must be finite, acyclic canonical JSON for durable identity`);
10163
+ const ref = identity.candidateDigest || identity.correlation ? Object.freeze({
10164
+ ...identity.candidateDigest ? { candidateDigest: identity.candidateDigest } : {},
10165
+ ...identity.correlation ? { correlation: identity.correlation } : {}
10166
+ }) : void 0;
10167
+ return {
10168
+ identity,
10169
+ ...ref ? { ref } : {}
10170
+ };
10171
+ }
10172
+ function rootCoordinationOwner(identity) {
10173
+ return canonicalCandidateDigest({
10174
+ kind: "supervisor-root",
10175
+ identity
10176
+ });
10177
+ }
10178
+ function childCoordinationOwner(parentOwnerId, identity, context, depth) {
10179
+ return canonicalCandidateDigest({
10180
+ kind: "supervisor-child",
10181
+ parentOwnerId,
10182
+ identity,
10183
+ assignment: {
10184
+ id: context.assignmentId,
10185
+ label: context.label,
10186
+ key: context.key ?? null,
10187
+ depth
10188
+ }
10189
+ });
10190
+ }
10191
+ function supervisionRunNamespace(runDir, runId) {
10192
+ return canonicalCandidateDigest(runDir === void 0 ? {
10193
+ kind: "supervise-ephemeral-run",
10194
+ runId,
10195
+ nonce: randomUUID()
10196
+ } : {
10197
+ kind: "supervise-durable-run",
10198
+ runId,
10199
+ runDir: resolve(runDir)
10200
+ });
10201
+ }
10202
+ function workerAssignmentNamespace(runNamespace, parentOwnerId, assignmentId) {
10203
+ return canonicalCandidateDigest({
10204
+ kind: "supervise-worker-assignment",
10205
+ runNamespace,
10206
+ parentOwnerId,
10207
+ assignmentId
10208
+ });
10209
+ }
10210
+ /** Hash only durable coordination meaning. Bus sequence/timestamp are delivery metadata and a
10211
+ * resumed projection's marker describes the reader, not the original settlement. */
10212
+ function coordinationEventId(context, event) {
10213
+ const durableEvent = event.type === "settled" && event.worker.resumed === true ? (() => {
10214
+ const { resumed: _resumed, ...worker } = event.worker;
10215
+ return {
10216
+ type: "settled",
10217
+ worker
10218
+ };
10219
+ })() : event;
10220
+ return canonicalCandidateDigest({
10221
+ kind: "supervise-coordination-event",
10222
+ runNamespace: context.runNamespace,
10223
+ ownerId: context.ownerId,
10224
+ event: detachedSnapshot(durableEvent, "supervise coordination event identity")
10225
+ });
10226
+ }
8658
10227
  /** One-call supervisor: build + run a supervisor from its profile with sensible defaults; the raw `supervisorAgent` + `createSupervisor().run` seams stay available for power use. */
8659
10228
  function supervise(profile, task, opts) {
8660
- const backendModel = opts.backend?.model;
8661
- assertModelAllowed(opts.router?.model, opts.allowedModels);
8662
- assertModelAllowed(resolveSupervisorModelId(profile), opts.allowedModels);
8663
- assertModelAllowed(typeof backendModel === "string" ? backendModel : void 0, opts.allowedModels);
8664
- const deliverable = resolveNamed("deliverable", "deliverables", opts.deliverable, opts.registry?.deliverables);
8665
- const finalizer = resolveNamed("finalizer", "finalizers", opts.finalizer, opts.registry?.finalizers);
8666
- const analysts = resolveNamed("analysts", "analysts", opts.analysts, opts.registry?.analysts);
8667
- const probes = resolveNamed("probes", "probes", opts.probes, opts.registry?.probes);
8668
- assertCoordinationBinding(opts.coordination);
8669
- const ctx = opts.runDir !== void 0 ? createFileRunContext(opts.runDir, { withDriver: true }) : createInMemoryRunContext({ withDriver: true });
8670
- const blobs = opts.blobs ?? ctx.blobs;
8671
- const perWorker = opts.perWorker ?? defaultPerWorker(opts.budget);
8672
- const runId = opts.runId ?? "supervise";
10229
+ const options = captureSuperviseOptions(opts);
10230
+ assertValidBudget(options.budget, "supervise budget");
10231
+ const parsedProfile = agentProfileSchema.safeParse(canonicalSupervisorProfileInput(profile));
10232
+ if (!parsedProfile.success) throw new ValidationError(`supervise: invalid AgentProfile: ${parsedProfile.error.message}`);
10233
+ const canonicalProfile = freezeDetachedProfile(parsedProfile.data);
10234
+ const canonicalTask = freezeDetached(task);
10235
+ if (options.makeWorkerAgent && options.authorizeSpawn) throw new ValidationError("supervise: authorizeSpawn cannot be combined with caller-owned makeWorkerAgent; wrap and authorize the custom factory explicitly or use backend-derived workers");
10236
+ if (options.makeWorkerAgent && options.resolveDeliverable) throw new ValidationError("supervise: resolveDeliverable applies only to backend-derived workers; wrap a caller-owned makeWorkerAgent with its completion checks explicitly");
10237
+ const authorizeDownFor = (parent, depth) => {
10238
+ if (!options.authorizeSpawn && !options.authorizeMessage) return void 0;
10239
+ return (input) => {
10240
+ if (!options.authorizeMessage) throw new ValidationError("supervise: authorizeMessage is required before steer_agent or answer_question when authorizeSpawn is enabled");
10241
+ return freezeDetached(options.authorizeMessage(freezeDetached({
10242
+ ...input,
10243
+ parent,
10244
+ depth
10245
+ })));
10246
+ };
10247
+ };
10248
+ const rootExecution = canonicalExecution(canonicalProfile, canonicalTask, options.execution, "supervise root");
10249
+ const backendModel = options.backend?.model;
10250
+ const driverBackendModel = options.driverBackend?.model;
10251
+ if ([...backendProfileOverlays(options.backend), ...backendProfileOverlays(options.driverBackend)].length > 0) throw new ValidationError("supervise: backend agentProfile overlays are not allowed because they run after spawn authorization; merge the overlay into the exact profile before calling supervise");
10252
+ assertModelAllowed(options.router?.model, options.allowedModels);
10253
+ assertProfileModelsAllowed(canonicalProfile, options.allowedModels);
10254
+ assertModelAllowed(typeof backendModel === "string" ? backendModel : void 0, options.allowedModels);
10255
+ assertModelAllowed(typeof driverBackendModel === "string" ? driverBackendModel : void 0, options.allowedModels);
10256
+ const deliverable = resolveNamed("deliverable", "deliverables", options.deliverable, options.registry?.deliverables);
10257
+ const finalizer = resolveNamed("finalizer", "finalizers", options.finalizer, options.registry?.finalizers);
10258
+ const analysts = resolveNamed("analysts", "analysts", options.analysts, options.registry?.analysts);
10259
+ const probes = resolveNamed("probes", "probes", options.probes, options.registry?.probes);
10260
+ assertCoordinationBinding(options.coordination);
10261
+ const ctx = options.runDir !== void 0 ? createFileRunContext(options.runDir, { withDriver: true }) : createInMemoryRunContext({ withDriver: true });
10262
+ const blobs = options.blobs ?? ctx.blobs;
10263
+ const perWorker = options.perWorker ?? defaultPerWorker(options.budget);
10264
+ assertValidBudget(perWorker, "supervise perWorker");
10265
+ const journal = options.journal ?? ctx.journal;
10266
+ const runId = options.runId ?? "supervise";
10267
+ const runNamespace = supervisionRunNamespace(options.runDir, runId);
8673
10268
  const log = ctx.coordinationLog;
8674
- const now = opts.now ?? Date.now;
10269
+ const rootOwnerId = rootCoordinationOwner(rootExecution.identity);
10270
+ const observeNodeEvent = options.onCoordinationEvent ? async (context, event, record) => {
10271
+ await options.onCoordinationEvent?.(context, coordinationEventId(context, event), record);
10272
+ } : void 0;
10273
+ const managerBackend = options.driverBackend ?? options.backend;
10274
+ if (options.driveHarness && options.resolveDriveHarness) throw new ValidationError("supervise: provide driveHarness or resolveDriveHarness, not both");
10275
+ const driverMaterialization = Boolean(options.driveHarness || options.resolveDriveHarness) ? options.driveHarnessMaterialization ?? fullProfileMaterialization : managerBackend && automaticDriverBackendSupported(managerBackend) ? backendProfileMaterialization(managerBackend) : void 0;
10276
+ if (isExternalSupervisor(canonicalProfile) && !options.driveHarness && !options.resolveDriveHarness && (!managerBackend || !automaticDriverBackendSupported(managerBackend))) throw new ValidationError(`supervise: external supervisor profile.harness=${JSON.stringify(canonicalProfile.harness)} requires a local bridge driverBackend, an explicit driveHarness, or resolveDriveHarness with reachable coordination transport`);
10277
+ const harnessClaims = /* @__PURE__ */ new WeakMap();
10278
+ const claimDriveHarness = (rawHarness, ownerId) => {
10279
+ if (typeof rawHarness !== "function") throw new ValidationError("supervise: resolveDriveHarness must return a DriveHarness function");
10280
+ const harness = rawHarness;
10281
+ const deliver = harness.deliver;
10282
+ if (deliver !== void 0 && typeof deliver !== "function") throw new ValidationError("supervise: driveHarness.deliver must be a function when provided");
10283
+ const claim = harnessClaims.get(harness);
10284
+ const conflictingOwner = claim ? [...claim.owners].find((claimedOwner) => claimedOwner !== ownerId) : void 0;
10285
+ const steerable = typeof deliver === "function";
10286
+ if (conflictingOwner !== void 0 && (steerable || claim?.steerable === true)) throw new ValidationError(`supervise: steerable driveHarness is already bound to manager owner ${JSON.stringify(conflictingOwner)}; resolveDriveHarness must return a distinct steerable instance for owner ${JSON.stringify(ownerId)}`);
10287
+ if (claim) {
10288
+ claim.owners.add(ownerId);
10289
+ claim.steerable ||= steerable;
10290
+ } else harnessClaims.set(harness, {
10291
+ owners: /* @__PURE__ */ new Set([ownerId]),
10292
+ steerable
10293
+ });
10294
+ return harness;
10295
+ };
10296
+ const driveHarnessForOwner = (context) => {
10297
+ if (options.resolveDriveHarness) return claimDriveHarness(options.resolveDriveHarness(context), context.ownerId);
10298
+ if (options.driveHarness) return claimDriveHarness(options.driveHarness, context.ownerId);
10299
+ return managerBackend && automaticDriverBackendSupported(managerBackend) ? driveHarnessFromBackend(managerBackend, externalExecutionId("supervised-manager", {
10300
+ runNamespace,
10301
+ ownerId: context.ownerId
10302
+ }), options.now ?? Date.now) : void 0;
10303
+ };
10304
+ const rootDriveHarness = isExternalSupervisor(canonicalProfile) ? driveHarnessForOwner(freezeDetached({
10305
+ runId,
10306
+ runNamespace,
10307
+ ownerId: rootOwnerId,
10308
+ depth: 0,
10309
+ identity: rootExecution.identity,
10310
+ profile: canonicalProfile,
10311
+ task: canonicalTask
10312
+ })) : void 0;
10313
+ const rootOwnerRuntime = !isExternalSupervisor(canonicalProfile) || rootDriveHarness === void 0 ? void 0 : runtimeOwnedScopeOwnerRuntime(rootDriveHarness);
10314
+ assertProfileContract(canonicalProfile, isExternalSupervisor(canonicalProfile) ? driverMaterialization : options.brain ? promptControlProfileMaterialization : routerSupervisorProfileMaterialization, "supervise root");
10315
+ const now = options.now ?? Date.now;
8675
10316
  let spans;
8676
- let makeWorkerAgent = opts.makeWorkerAgent;
10317
+ let makeWorkerAgent = options.makeWorkerAgent;
8677
10318
  if (!makeWorkerAgent) {
8678
- if (!opts.backend) throw new ValidationError("supervise: provide opts.backend (where workers run) or opts.makeWorkerAgent");
8679
- makeWorkerAgent = workerFromBackend(opts.backend, deliverable, () => spans ? { [workerTraceSeamKey]: spans.workerTrace(runId) } : {});
10319
+ if (!options.backend) throw new ValidationError("supervise: provide opts.backend (where workers run) or opts.makeWorkerAgent");
10320
+ const makeLeaf = workerFromBackend(options.backend, deliverable);
10321
+ const securityPolicy = options.profileSecurity ?? DEFAULT_AUTHORED_PROFILE_SECURITY_POLICY;
10322
+ const makeRecursiveWorkerFor = (parent, parentIdentity, depth, parentOwnerId) => {
10323
+ const makeRecursiveWorker = (authoredProfile, spawnContext) => {
10324
+ if (!spawnContext) throw new ValidationError("supervise: backend-derived workers require spawn context");
10325
+ const input = freezeDetachedProfile(authoredProfile);
10326
+ const authorizationInput = Object.freeze({
10327
+ profile: input,
10328
+ parent,
10329
+ parentIdentity,
10330
+ parentNodeId: spawnContext.parentNodeId,
10331
+ assignmentId: spawnContext.assignmentId,
10332
+ task: spawnContext.task,
10333
+ budget: spawnContext.budget,
10334
+ label: spawnContext.label,
10335
+ ...spawnContext.key !== void 0 ? { key: spawnContext.key } : {},
10336
+ depth
10337
+ });
10338
+ const decision = options.authorizeSpawn ? freezeDetached(options.authorizeSpawn(authorizationInput)) : Object.freeze({
10339
+ profile: input,
10340
+ ...spawnContext.execution ? { execution: spawnContext.execution } : {}
10341
+ });
10342
+ if (typeof decision !== "object" || decision === null || Array.isArray(decision)) throw new ValidationError("supervise: authorizeSpawn must return an AuthorizedSpawn");
10343
+ const authorized = freezeDetachedProfile(decision.profile);
10344
+ const childExecution = canonicalExecution(authorized, spawnContext.task, decision.execution, `supervise spawn ${JSON.stringify(spawnContext.label)}`);
10345
+ const authorizedContext = Object.freeze({
10346
+ ...spawnContext,
10347
+ ...childExecution.ref ? { execution: childExecution.ref } : {}
10348
+ });
10349
+ const postAuthorizationContext = freezeDetached({
10350
+ profile: authorized,
10351
+ parent,
10352
+ parentIdentity,
10353
+ execution: childExecution.identity,
10354
+ parentNodeId: spawnContext.parentNodeId,
10355
+ assignmentId: spawnContext.assignmentId,
10356
+ task: spawnContext.task,
10357
+ budget: spawnContext.budget,
10358
+ label: spawnContext.label,
10359
+ ...spawnContext.key !== void 0 ? { key: spawnContext.key } : {},
10360
+ depth
10361
+ });
10362
+ const security = validateAgentProfileSecurity(authorized, securityPolicy);
10363
+ if (!security.ok) throw new ValidationError(`supervise: spawned AgentProfile refused: ${security.issues.filter((issue) => issue.level === "error").map((issue) => `${issue.code}${issue.path ? ` at ${issue.path}` : ""}`).join(", ")}`);
10364
+ assertProfileModelsAllowed(authorized, options.allowedModels);
10365
+ let isDriver;
10366
+ if (options.isDriverProfile) {
10367
+ const driverDecision = options.isDriverProfile(postAuthorizationContext);
10368
+ if (typeof driverDecision !== "boolean") throw new ValidationError("supervise: isDriverProfile must return a boolean");
10369
+ isDriver = driverDecision;
10370
+ } else isDriver = authorized.metadata?.role === "driver";
10371
+ if (!isDriver) {
10372
+ const selectedDeliverable = options.resolveDeliverable?.(postAuthorizationContext);
10373
+ const leafDeliverable = selectedDeliverable === void 0 ? deliverable : captureDeliverable(selectedDeliverable, `supervise deliverable for ${JSON.stringify(spawnContext.label)}`);
10374
+ return (leafDeliverable === deliverable ? makeLeaf : workerFromBackend(options.backend, leafDeliverable))(authorized, Object.freeze({
10375
+ ...authorizedContext,
10376
+ assignmentId: workerAssignmentNamespace(runNamespace, parentOwnerId, spawnContext.assignmentId)
10377
+ }));
10378
+ }
10379
+ const ownerId = childCoordinationOwner(parentOwnerId, childExecution.identity, spawnContext, depth);
10380
+ const nestedDriveHarness = isExternalSupervisor(authorized) ? driveHarnessForOwner(freezeDetached({
10381
+ runId,
10382
+ runNamespace,
10383
+ ownerId,
10384
+ depth,
10385
+ identity: childExecution.identity,
10386
+ assignmentId: spawnContext.assignmentId,
10387
+ profile: authorized,
10388
+ task: spawnContext.task
10389
+ })) : void 0;
10390
+ if (isExternalSupervisor(authorized) && !nestedDriveHarness) throw new ValidationError(`supervise: authored external supervisor profile.harness=${JSON.stringify(authorized.harness)} requires a local bridge driverBackend, an explicit driveHarness, or resolveDriveHarness with reachable coordination transport`);
10391
+ assertProfileContract(authorized, isExternalSupervisor(authorized) ? driverMaterialization : promptModelProfileMaterialization, `supervise driver ${JSON.stringify(spawnContext.label)}`);
10392
+ const childFactory = makeRecursiveWorkerFor(authorized, childExecution.identity, depth + 1, ownerId);
10393
+ const nestedPerWorker = defaultPerWorker(spawnContext.budget);
10394
+ const authorizeNestedMessage = authorizeDownFor(authorized, depth + 1);
10395
+ return driverChild(authorized, supervisorAgent(authorized, {
10396
+ blobs,
10397
+ makeWorkerAgent: childFactory,
10398
+ ...authorizeNestedMessage ? { authorizeDownMessage: authorizeNestedMessage } : {},
10399
+ perWorker: nestedPerWorker,
10400
+ ...options.router ? { router: options.router } : {},
10401
+ ...nestedDriveHarness ? { driveHarness: nestedDriveHarness } : {},
10402
+ nodeContext: {
10403
+ runId,
10404
+ runNamespace,
10405
+ ownerId,
10406
+ depth,
10407
+ identity: childExecution.identity,
10408
+ assignmentId: spawnContext.assignmentId
10409
+ },
10410
+ ...options.resolveSupervisorTools ? { resolveSupervisorTools: options.resolveSupervisorTools } : {},
10411
+ ...observeNodeEvent ? {
10412
+ observeNodeEvent,
10413
+ replaySettlements: true
10414
+ } : {},
10415
+ ...analysts ? { analysts } : {},
10416
+ ...options.analyzeOnSettle ? { analyzeOnSettle: options.analyzeOnSettle } : {},
10417
+ ...options.watchWorkers ? { watchWorkers: options.watchWorkers } : {},
10418
+ ...options.stallAfterMs !== void 0 ? { stallAfterMs: options.stallAfterMs } : {},
10419
+ ...options.stopRule ? { stopRule: options.stopRule } : {},
10420
+ ...options.onProgressStop ? { onProgressStop: options.onProgressStop } : {},
10421
+ ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
10422
+ ...options.compaction ? { compaction: options.compaction } : {},
10423
+ ...log ? {
10424
+ onEvent: (_event, record) => log.append(runId, record, ownerId),
10425
+ loadPriorCoordination: () => log.load(runId, ownerId)
10426
+ } : {},
10427
+ ...finalizer ? { finalizer } : {}
10428
+ }), journal, childExecution.ref);
10429
+ };
10430
+ return makeRecursiveWorker;
10431
+ };
10432
+ makeWorkerAgent = makeRecursiveWorkerFor(canonicalProfile, rootExecution.identity, 1, rootOwnerId);
8680
10433
  }
8681
10434
  const workerFactory = makeWorkerAgent;
8682
10435
  const start = async () => {
8683
- const priorCoordination = log ? await log.load(runId) : void 0;
8684
- const agent = supervisorAgent(profile, {
10436
+ const priorCoordination = log ? await log.load(runId, rootOwnerId) : void 0;
10437
+ const authorizeRootMessage = authorizeDownFor(canonicalProfile, 1);
10438
+ const agent = supervisorAgent(canonicalProfile, {
8685
10439
  blobs,
8686
10440
  makeWorkerAgent: workerFactory,
10441
+ ...authorizeRootMessage ? { authorizeDownMessage: authorizeRootMessage } : {},
8687
10442
  perWorker,
10443
+ ...log ? { onEvent: (_event, record) => log.append(runId, record, rootOwnerId) } : {},
8688
10444
  ...deliverable ? { deliverable } : {},
8689
- ...log ? { onEvent: (ev) => log.append(runId, ev, new Date(now()).toISOString()) } : {},
8690
- ...priorCoordination && (priorCoordination.questions.length > 0 || priorCoordination.findings.length > 0) ? { priorCoordination } : {},
10445
+ ...priorCoordination && (priorCoordination.questions.length > 0 || priorCoordination.findings.length > 0 || priorCoordination.continuations.length > 0 || priorCoordination.deliveryEvidence.length > 0) ? { priorCoordination } : {},
8691
10446
  ...finalizer ? { finalizer } : {},
8692
- ...opts.coordination ? { coordination: opts.coordination } : {},
8693
- ...opts.maxLiveWorkers !== void 0 ? { maxLiveWorkers: opts.maxLiveWorkers } : {},
8694
- ...opts.router ? { router: opts.router } : {},
8695
- ...opts.brain ? { brain: opts.brain } : {},
8696
- ...opts.driveHarness ? { driveHarness: opts.driveHarness } : {},
8697
- ...opts.extraTools ? { extraTools: opts.extraTools } : {},
8698
- ...opts.executeExtraTool ? { executeExtraTool: opts.executeExtraTool } : {},
10447
+ ...options.coordination ? { coordination: options.coordination } : {},
10448
+ ...options.maxLiveWorkers !== void 0 ? { maxLiveWorkers: options.maxLiveWorkers } : {},
10449
+ ...options.router ? { router: options.router } : {},
10450
+ ...options.brain ? { brain: options.brain } : {},
10451
+ ...rootDriveHarness ? { driveHarness: rootDriveHarness } : {},
10452
+ nodeContext: {
10453
+ runId,
10454
+ runNamespace,
10455
+ ownerId: rootOwnerId,
10456
+ depth: 0,
10457
+ identity: rootExecution.identity
10458
+ },
10459
+ ...options.resolveSupervisorTools ? { resolveSupervisorTools: options.resolveSupervisorTools } : {},
10460
+ ...observeNodeEvent ? {
10461
+ observeNodeEvent,
10462
+ replaySettlements: true
10463
+ } : {},
10464
+ ...options.extraTools ? { extraTools: options.extraTools } : {},
10465
+ ...options.executeExtraTool ? { executeExtraTool: options.executeExtraTool } : {},
8699
10466
  ...analysts ? { analysts } : {},
8700
- ...opts.analyzeOnSettle ? { analyzeOnSettle: opts.analyzeOnSettle } : {},
8701
- ...opts.watchWorkers ? { watchWorkers: opts.watchWorkers } : {},
8702
- ...opts.stallAfterMs !== void 0 ? { stallAfterMs: opts.stallAfterMs } : {},
8703
- ...opts.stopRule ? { stopRule: opts.stopRule } : {},
8704
- ...opts.onProgressStop ? { onProgressStop: opts.onProgressStop } : {},
8705
- ...opts.maxTurns !== void 0 ? { maxTurns: opts.maxTurns } : {},
8706
- ...opts.compaction ? { compaction: opts.compaction } : {}
10467
+ ...options.analyzeOnSettle ? { analyzeOnSettle: options.analyzeOnSettle } : {},
10468
+ ...options.watchWorkers ? { watchWorkers: options.watchWorkers } : {},
10469
+ ...options.stallAfterMs !== void 0 ? { stallAfterMs: options.stallAfterMs } : {},
10470
+ ...options.stopRule ? { stopRule: options.stopRule } : {},
10471
+ ...options.onProgressStop ? { onProgressStop: options.onProgressStop } : {},
10472
+ ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
10473
+ ...options.compaction ? { compaction: options.compaction } : {}
8707
10474
  });
8708
- spans = opts.otel ? createSupervisorSpanRecorder({
10475
+ spans = options.otel ? createSupervisorSpanRecorder({
8709
10476
  runId,
8710
- ...opts.otel,
10477
+ ...options.otel,
8711
10478
  now
8712
10479
  }) : void 0;
8713
10480
  const recorder = spans;
8714
- const hooks = recorder ? composeRuntimeHooks(opts.hooks, recorder.hooks) : opts.hooks;
8715
- const run = createSupervisor().run(agent, task, {
8716
- budget: opts.budget,
10481
+ const hooks = recorder ? composeRuntimeHooks(options.hooks, recorder.hooks) : options.hooks;
10482
+ const supervisor = createSupervisor();
10483
+ if (options.rootHandle) supervisor.attach(options.rootHandle);
10484
+ const run = supervisor.run(agent, canonicalTask, {
10485
+ budget: options.budget,
8717
10486
  runId,
8718
- journal: opts.journal ?? ctx.journal,
10487
+ journal,
8719
10488
  blobs,
8720
10489
  executors: ctx.executors,
8721
- maxDepth: opts.maxDepth ?? 8,
10490
+ rootIdentity: rootExecution.identity,
10491
+ ...rootOwnerRuntime === void 0 ? {} : { rootMaterialization: {
10492
+ runtime: rootOwnerRuntime,
10493
+ declaration: "deferred",
10494
+ authoredProfile: canonicalProfile
10495
+ } },
10496
+ maxDepth: options.maxDepth ?? 8,
10497
+ ...options.maxLiveWorkers !== void 0 ? { maxLiveWorkers: options.maxLiveWorkers } : {},
8722
10498
  ...probes ? { probes } : {},
8723
10499
  ...ctx.resume === true ? { resume: true } : {},
8724
- ...opts.now ? { now: opts.now } : {},
10500
+ ...options.now ? { now: options.now } : {},
10501
+ ...options.signal ? { signal: options.signal } : {},
8725
10502
  ...hooks ? { hooks } : {},
8726
10503
  ...recorder ? { workerTrace: recorder.workerTrace } : {}
8727
10504
  });
@@ -8738,6 +10515,6 @@ function supervise(profile, task, opts) {
8738
10515
  return start();
8739
10516
  }
8740
10517
  //#endregion
8741
- export { InMemoryDelegationStore as $, FileCoordinationLog as $t, DELEGATE_INPUT_SCHEMA as A, createWorktreeCliExecutor as At, profileRichnessFinding as B, PI_MCP_ADAPTER_ENV as Bt, validateDelegateUiAuditArgs as C, defaultToolDetectors as Ct, createDelegateFeedbackHandler as D, cliWorktreeExecutor as Dt, DELEGATE_FEEDBACK_TOOL_NAME as E, createInMemoryRunContext as Et, delegate as F, piSeamKey as Ft, DELEGATION_TRACE_MAX_SPANS as G, createInbox as Gt, DelegationTaskQueue as H, buildPiMcpServers as Ht, asAuthoredProfile as I, createPushTraceSource as It, composeLoopTraceEmitters as J, runAgentRounds as Jt, buildDelegationTraceSpans as K, createSandboxForSpec as Kt, assessAuthoredProfile as L, decodeToolPart as Lt, createDelegateHandler as M, createSteerableSandboxSession as Mt, validateDelegateArgs as N, PI_RUNTIME as Nt, validateDelegateFeedbackArgs as O, createExecutor as Ot, defaultDelegateBudget as P, piExecutor as Pt, FileDelegationStore as Q, acquireSandbox as Qt, authoredWorker as R, sandboxSessionTraceSource as Rt, createDelegateUiAuditHandler as S, rollingDispatch as St, DELEGATE_FEEDBACK_INPUT_SCHEMA as T, createFileRunContext as Tt, hashIdempotencyInput as U, piMcpAdapterAvailable as Ut, supervisorInstructions as V, PI_MCP_CONFIG_FLAG as Vt, DELEGATION_TRACE_MAX_BYTES as W, preparePiMcp as Wt, DelegationPersistenceError as X, createSandboxLineage as Xt, createDelegationTraceCollector as Y, runLoop as Yt, DelegationStateCorruptError as Z, probeSandboxCapabilities as Zt, createDelegationHistoryHandler as _, createCoordinationTools as _t, supervisorAgent as a, allWorkersStalled as at, DELEGATE_UI_AUDIT_INPUT_SCHEMA as b, freeSlots as bt, createMcpServer as c, noProgressFor as ct, DELEGATION_STATUS_TOOL_NAME as d, anytimeReport as dt, createSupervisorSpanRecorder as en, InMemoryFeedbackStore as et, createDelegationStatusHandler as f, areaUnderCurve as ft, DELEGATION_HISTORY_TOOL_NAME as g, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as gt, DELEGATION_HISTORY_INPUT_SCHEMA as h, renderAnytimeTable as ht, resolveSupervisorProfile as i, allOf as it, DELEGATE_TOOL_NAME as j, DEFAULT_SANDBOX_STEERING_MAX_TURNS as jt, DELEGATE_DESCRIPTION as k, createExecutorRegistry as kt, DELEGATION_STATUS_DESCRIPTION as l, plateau as lt, DELEGATION_HISTORY_DESCRIPTION as m, plateauLength as mt, workerFromBackend as n, gateOnDeliverable as nn, driverAgent as nt, serveCoordinationMcp as o, anyOf as ot, validateDelegationStatusArgs as p, bestSoFar as pt, capDelegationTrace as q, defaultSelectWinner as qt, assertCoordinationBinding as r, finalizeBestDelivered as rt, createInProcessTransport as s, createProgressTracker as st, supervise as t, assertModelAllowed as tn, eventToSnapshot as tt, DELEGATION_STATUS_INPUT_SCHEMA as u, sampleFromSettled as ut, validateDelegationHistoryArgs as v, createEventBus as vt, DELEGATE_FEEDBACK_DESCRIPTION as w, watchTrace as wt, DELEGATE_UI_AUDIT_TOOL_NAME as x, queueOf as xt, DELEGATE_UI_AUDIT_DESCRIPTION as y, effectiveConcurrency as yt, defaultProfileRichnessThresholds as z, PI_MCP_ADAPTER as zt };
10518
+ export { allOf as $, profileRichnessFinding as $t, DELEGATE_DESCRIPTION as A, PI_MCP_ADAPTER as At, DELEGATION_TRACE_MAX_SPANS as B, runLoop as Bt, createDelegateUiAuditHandler as C, createSteerableSandboxSession as Ct, DELEGATE_FEEDBACK_TOOL_NAME as D, createPushTraceSource as Dt, DELEGATE_FEEDBACK_INPUT_SCHEMA as E, piSeamKey as Et, defaultDelegateBudget as F, preparePiMcp as Ft, DelegationPersistenceError as G, createSupervisorSpanRecorder as Gt, capDelegationTrace as H, probeSandboxCapabilities as Ht, delegate as I, createInbox as It, InMemoryDelegationStore as J, asAuthoredProfile as Jt, DelegationStateCorruptError as K, assertModelAllowed as Kt, DelegationTaskQueue as L, createSandboxForSpec as Lt, DELEGATE_TOOL_NAME as M, PI_MCP_CONFIG_FLAG as Mt, createDelegateHandler as N, buildPiMcpServers as Nt, createDelegateFeedbackHandler as O, decodeToolPart as Ot, validateDelegateArgs as P, piMcpAdapterAvailable as Pt, finalizeBestDelivered as Q, defaultProfileRichnessThresholds as Qt, hashIdempotencyInput as R, defaultSelectWinner as Rt, DELEGATE_UI_AUDIT_TOOL_NAME as S, DEFAULT_SANDBOX_STEERING_MAX_TURNS as St, DELEGATE_FEEDBACK_DESCRIPTION as T, piExecutor as Tt, composeLoopTraceEmitters as U, acquireSandbox as Ut, buildDelegationTraceSpans as V, createSandboxLineage as Vt, createDelegationTraceCollector as W, FileCoordinationLog as Wt, eventToSnapshot as X, authoredWorker as Xt, InMemoryFeedbackStore as Y, assessAuthoredProfile as Yt, driverAgent as Z, canonicalizeAuthoredProfile as Zt, DELEGATION_HISTORY_TOOL_NAME as _, createInMemoryRunContext as _t, resolveSupervisorProfile as a, sampleFromSettled as at, DELEGATE_UI_AUDIT_DESCRIPTION as b, createExecutorRegistry as bt, createInProcessTransport as c, bestSoFar as ct, DELEGATION_STATUS_INPUT_SCHEMA as d, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as dt, supervisorInstructions as en, allWorkersStalled as et, DELEGATION_STATUS_TOOL_NAME as f, createCoordinationTools as ft, DELEGATION_HISTORY_INPUT_SCHEMA as g, createFileRunContext as gt, DELEGATION_HISTORY_DESCRIPTION as h, watchTrace as ht, assertCoordinationBinding as i, plateau as it, DELEGATE_INPUT_SCHEMA as j, PI_MCP_ADAPTER_ENV as jt, validateDelegateFeedbackArgs as k, sandboxSessionTraceSource as kt, createMcpServer as l, plateauLength as lt, validateDelegationStatusArgs as m, defaultToolDetectors as mt, supervise as n, createProgressTracker as nt, supervisorAgent as o, anytimeReport as ot, createDelegationStatusHandler as p, createEventBus as pt, FileDelegationStore as q, assertProfileModelsAllowed as qt, workerFromBackend as r, noProgressFor as rt, serveCoordinationMcp as s, areaUnderCurve as st, DEFAULT_AUTHORED_PROFILE_SECURITY_POLICY as t, gateOnDeliverable as tn, anyOf as tt, DELEGATION_STATUS_DESCRIPTION as u, renderAnytimeTable as ut, createDelegationHistoryHandler as v, cliWorktreeExecutor as vt, validateDelegateUiAuditArgs as w, PI_RUNTIME as wt, DELEGATE_UI_AUDIT_INPUT_SCHEMA as x, createWorktreeCliExecutor as xt, validateDelegationHistoryArgs as y, createExecutor as yt, DELEGATION_TRACE_MAX_BYTES as z, runAgentRounds as zt };
8742
10519
 
8743
- //# sourceMappingURL=supervise-BUR9ByF7.js.map
10520
+ //# sourceMappingURL=supervise-U7_K5yuV.js.map