@tangle-network/agent-runtime 0.219.0 → 0.220.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{activation-C-8VqFSN.js → activation-D3yrSA_b.js} +2 -2
  2. package/dist/{activation-C-8VqFSN.js.map → activation-D3yrSA_b.js.map} +1 -1
  3. package/dist/agent.d.ts +1 -1
  4. package/dist/agent.js +2 -2
  5. package/dist/{coordination-driver-CvinSUI-.js → coordination-driver-BxGh-Tnj.js} +599 -355
  6. package/dist/coordination-driver-BxGh-Tnj.js.map +1 -0
  7. package/dist/{delegate-BH8r8hkQ.js → delegate-CiGfF17V.js} +2 -2
  8. package/dist/{delegate-BH8r8hkQ.js.map → delegate-CiGfF17V.js.map} +1 -1
  9. package/dist/durable.d.ts +1 -1
  10. package/dist/durable.js +2 -2
  11. package/dist/{graph-5N4gLnOt.js → graph-D9tSg6Vd.js} +3 -3
  12. package/dist/{graph-5N4gLnOt.js.map → graph-D9tSg6Vd.js.map} +1 -1
  13. package/dist/{improvement-cycle-BIAKVZ3B.js → improvement-cycle-BZ_KfBap.js} +3 -3
  14. package/dist/{improvement-cycle-BIAKVZ3B.js.map → improvement-cycle-BZ_KfBap.js.map} +1 -1
  15. package/dist/{index-CLDpu-bs.d.ts → index-B9_U4zYI.d.ts} +16 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.js +7 -7
  18. package/dist/intelligence.js +3 -3
  19. package/dist/kernel.d.ts +1 -1
  20. package/dist/kernel.js +8 -8
  21. package/dist/{loop-runner-bin-Cn9DBrLv.d.ts → loop-runner-bin-BaeOlnpi.d.ts} +2 -2
  22. package/dist/{loop-runner-bin-CTEJAcac.js → loop-runner-bin-DkU6vtuc.js} +3 -3
  23. package/dist/{loop-runner-bin-CTEJAcac.js.map → loop-runner-bin-DkU6vtuc.js.map} +1 -1
  24. package/dist/loop-runner-bin.d.ts +1 -1
  25. package/dist/loop-runner-bin.js +1 -1
  26. package/dist/mcp/bin.js +3 -3
  27. package/dist/mcp/index.d.ts +1 -1
  28. package/dist/mcp/index.js +4 -4
  29. package/dist/{provision-supervisor-40EOGn14.js → provision-supervisor-Cg3fETK-.js} +3 -3
  30. package/dist/{provision-supervisor-40EOGn14.js.map → provision-supervisor-Cg3fETK-.js.map} +1 -1
  31. package/dist/{runtime-BDK_GOAd.js → runtime-DZcPHLwa.js} +8 -8
  32. package/dist/{runtime-BDK_GOAd.js.map → runtime-DZcPHLwa.js.map} +1 -1
  33. package/dist/{server-DuvbSAVA.js → server-BgPZIHRd.js} +3 -3
  34. package/dist/{server-DuvbSAVA.js.map → server-BgPZIHRd.js.map} +1 -1
  35. package/dist/{structural-rollout-BAr61WjX.js → structural-rollout-iCePfrcK.js} +2 -2
  36. package/dist/{structural-rollout-BAr61WjX.js.map → structural-rollout-iCePfrcK.js.map} +1 -1
  37. package/dist/{supervise-W_mtzswa.js → supervise-CSYu0BeH.js} +440 -726
  38. package/dist/supervise-CSYu0BeH.js.map +1 -0
  39. package/dist/{supervisor-WRrw0nlk.js → supervisor-n8DDNGli.js} +368 -31
  40. package/dist/supervisor-n8DDNGli.js.map +1 -0
  41. package/dist/testing.d.ts +1 -1
  42. package/dist/testing.js +12 -12
  43. package/dist/tui/index.d.ts +1 -1
  44. package/dist/tui/index.js +1 -1
  45. package/package.json +1 -1
  46. package/dist/coordination-driver-CvinSUI-.js.map +0 -1
  47. package/dist/supervise-W_mtzswa.js.map +0 -1
  48. package/dist/supervisor-WRrw0nlk.js.map +0 -1
@@ -1,385 +1,355 @@
1
- import { An as unmeteredSpend, Fn as withBudgetResources, Ht as isLiveNodeStatus, Jr as isNoEntError, Nn as addResourceSpend, Nt as createInbox, Qn as resolveRedactor, Rt as createPeerMailbox, Ut as isTerminalNodeStatus, W as freeSlots, Xr as prepareJsonlAppend, Yr as parseCommittedJsonLines, Zr as writeAllBytes, a as pickBestDelivered, ct as InMemoryResultBlobStore, d as withDriverExecutor, et as createExecutorRegistry, g as meterRuntimeOwnedProviderAttempt, ln as runBrainLoop, lt as InMemorySpawnJournal, o as runFinalizer, oi as providerAttemptEvidence, ot as FileResultBlobStore, r as bestDelivered, s as runTree, st as FileSpawnJournal, vi as detachedFrozen, vt as parseWorkerToolTraceArtifact, wn as promptCacheTokenClasses, xt as assertValidBudget, yn as chargedTokens, yt as workerTraceAnalysisStore } from "./supervisor-WRrw0nlk.js";
1
+ import { $r as parseCommittedJsonLines, Cn as chargedTokens, Ct as workerTraceAnalysisStore, Ht as createPeerMailbox, J as freeSlots, Kt as isLiveNodeStatus, Ln as addResourceSpend, Lt as createInbox, On as promptCacheTokenClasses, Pn as unmeteredSpend, Qr as isNoEntError, Si as detachedFrozen, St as parseWorkerToolTraceArtifact, Tt as assertValidBudget, a as pickBestDelivered, b as meterRuntimeOwnedProviderAttempt, dt as FileSpawnJournal, ei as prepareJsonlAppend, ft as InMemoryResultBlobStore, h as withDriverExecutor, it as createExecutorRegistry, nr as resolveRedactor, o as runFinalizer, pn as runBrainLoop, pt as InMemorySpawnJournal, qt as isTerminalNodeStatus, r as bestDelivered, s as runTree, ti as writeAllBytes, ui as providerAttemptEvidence, ut as FileResultBlobStore, zn as withBudgetResources } from "./supervisor-n8DDNGli.js";
2
2
  import { f as RuntimeRunStateError, m as ValidationError } from "./errors-DodWX-cb.js";
3
3
  import { A as writeWorkerCancellation, M as writeWorkerSteerAcknowledgement, b as workerCancellationsDir, c as readWorkerCancelRequests, d as readWorkerSteerRequests, k as writeRunCancellation, l as readWorkerCancellation, o as readRunCancelRequest, r as claimWorkerSteerDelivery, s as readRunCancellation, u as readWorkerSteerAcknowledgement } from "./run-layout-hZREkcfu.js";
4
4
  import { agentProfileSchema, canonicalCandidateDigest } from "@tangle-network/agent-interface";
5
5
  import { argHash, errorStreakDetector, observeAll, repeatedActionDetector } from "@tangle-network/agent-eval";
6
- import { randomUUID } from "node:crypto";
6
+ import { createHash, randomUUID } from "node:crypto";
7
7
  import { mkdirSync, readFileSync, watch } from "node:fs";
8
- import { join } from "node:path";
9
- //#region src/runtime/supervise/run-cancellation.ts
10
- /** Apply a durable request through the run's existing abort controller. */
11
- function applyRunCancellation(dir, abortRun, now, path = "turn-boundary") {
12
- const request = readRunCancelRequest(dir);
13
- if (request === void 0) return void 0;
14
- const prior = readRunCancellation(dir, request.operationId);
15
- if (prior !== void 0) return prior;
16
- abortRun(request.reason ?? "run cancel requested", request);
17
- const observedAt = now();
18
- const appliedAfterMs = Math.max(0, Date.parse(observedAt) - Date.parse(request.at));
19
- const record = {
20
- operationId: request.operationId,
21
- effect: "cancel_requested",
22
- path,
23
- appliedAfterMs,
24
- ...request.deadlineMs === void 0 ? {} : { deadlineExceeded: appliedAfterMs > request.deadlineMs },
25
- requestedAt: request.at,
26
- observedAt,
27
- ...request.reason === void 0 ? {} : { reason: request.reason },
28
- detail: "root abort issued to the whole run; termination not yet proven"
29
- };
30
- writeRunCancellation(dir, record);
31
- return record;
8
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
9
+ import { open, realpath, stat } from "node:fs/promises";
10
+ //#region src/runtime/supervise/detector-monitor.ts
11
+ /**
12
+ *
13
+ * The ONLINE analyst: watch a `TraceSource` and fold each tool span through agent-eval's published
14
+ * streaming detector kernel (`repeatedActionDetector`/`errorStreakDetector` — the SAME kernel the
15
+ * control loop folds), firing `onSignal` the moment a worker loops or error-storms. Substrate-
16
+ * agnostic: it consumes spans from any source (owned router/bridge loop OR a sandbox box session),
17
+ * never the raw tool seam. Detection logic + the failure taxonomy live in agent-eval; not reimplemented.
18
+ *
19
+ * @experimental
20
+ */
21
+ /** The default online panel for a tool-call pipe: a worker repeating the same call, or hammering
22
+ * consecutive errors. (No-progress needs a domain progress-probe, so it is opt-in, not default.)
23
+ *
24
+ * Coverage note: `repeated-action` works for EVERY harness (it needs only tool name + args, which
25
+ * every adapter provides). `error-streak` needs per-call status — opencode carries it inline
26
+ * (`state.status`, VALIDATED live), but claude-code/codex tool-call parts do NOT (their errors live
27
+ * in separate result blocks not yet decoded), so error-streak is silent for those until result-block
28
+ * decoding is added + live-validated. It is in the panel because it is correct where status exists. */
29
+ function defaultToolDetectors() {
30
+ return [repeatedActionDetector({ maxRepeated: 3 }), errorStreakDetector({ maxErrors: 3 })];
32
31
  }
33
- /** External drivers may remain silent indefinitely, so turn boundaries cannot observe control. */
34
- function watchRunCancellation(dir, abortRun) {
35
- mkdirSync(workerCancellationsDir(dir), { recursive: true });
36
- let active = true;
37
- let failure;
38
- const check = (path = "observer") => {
39
- if (failure !== void 0) throw failure;
40
- if (active) applyRunCancellation(dir, abortRun, () => (/* @__PURE__ */ new Date()).toISOString(), path);
41
- };
42
- const fail = (error) => {
43
- if (!active) return;
44
- failure = error;
45
- abortRun(`durable run cancellation observer failed: ${String(error)}`);
46
- };
47
- const watcher = watch(workerCancellationsDir(dir), (_event, filename) => {
48
- if (!active || filename !== null && filename !== "run.request.json") return;
32
+ /** Subscribe to a `TraceSource` and run the streaming detectors over its live spans. Returns an
33
+ * unsubscribe. A defensive `argHash` failure (circular args) never throws out of the side-channel. */
34
+ function watchTrace(source, opts = {}) {
35
+ const detectors = opts.detectors ?? defaultToolDetectors();
36
+ return source.onSpan((span) => {
37
+ let fingerprint;
49
38
  try {
50
- check();
51
- } catch (error) {
52
- fail(error);
39
+ fingerprint = `${span.toolName}|${argHash(span.args)}`;
40
+ } catch {
41
+ fingerprint = `${span.toolName}|<unhashable>`;
53
42
  }
43
+ const signals = observeAll(detectors, {
44
+ actionFingerprint: fingerprint,
45
+ ...span.status ? { status: span.status } : {},
46
+ label: span.toolName
47
+ });
48
+ for (const s of signals) opts.onSignal?.(s, span);
54
49
  });
55
- watcher.on("error", fail);
56
- const fallback = setInterval(() => {
57
- try {
58
- check("fallback");
59
- } catch (error) {
60
- fail(error);
50
+ }
51
+ //#endregion
52
+ //#region src/runtime/supervise/event-bus.ts
53
+ /** 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.
54
+ * @experimental In-process queue; durability is a transport swap that does not exist yet. */
55
+ function createEventBus(now = Date.now) {
56
+ const queue = [];
57
+ const log = [];
58
+ const subscribers = [];
59
+ const byKind = {};
60
+ const staged = /* @__PURE__ */ new WeakMap();
61
+ let seq = 0;
62
+ let published = 0;
63
+ let pulled = 0;
64
+ const matches = (r, kinds) => !kinds || kinds.includes(r.event.type);
65
+ const bestIndex = (kinds) => {
66
+ let best = -1;
67
+ let bestPriority = Number.NEGATIVE_INFINITY;
68
+ for (let i = 0; i < queue.length; i++) {
69
+ const r = queue[i];
70
+ if (!r || !matches(r, kinds)) continue;
71
+ if (r.priority > bestPriority) {
72
+ best = i;
73
+ bestPriority = r.priority;
74
+ }
61
75
  }
62
- }, 100);
63
- fallback.unref();
76
+ return best;
77
+ };
64
78
  return {
65
- check,
66
- close() {
67
- active = false;
68
- watcher.close();
69
- clearInterval(fallback);
79
+ async publish(event, opts) {
80
+ const record = staged.get(event) ?? {
81
+ seq: seq++,
82
+ at: now(),
83
+ priority: opts?.priority ?? 0,
84
+ event
85
+ };
86
+ staged.set(event, record);
87
+ for (const handler of subscribers) await handler(record);
88
+ staged.delete(event);
89
+ if (opts?.queue !== false) queue.push(record);
90
+ log.push(record);
91
+ published += 1;
92
+ byKind[event.type] = (byKind[event.type] ?? 0) + 1;
93
+ return record;
94
+ },
95
+ pull(kinds) {
96
+ const i = bestIndex(kinds);
97
+ if (i < 0) return void 0;
98
+ pulled++;
99
+ return queue.splice(i, 1)[0]?.event;
100
+ },
101
+ subscribe(handler) {
102
+ subscribers.push(handler);
103
+ return () => {
104
+ const i = subscribers.indexOf(handler);
105
+ if (i >= 0) subscribers.splice(i, 1);
106
+ };
107
+ },
108
+ pending(kinds) {
109
+ return kinds ? queue.filter((r) => matches(r, kinds)).length : queue.length;
110
+ },
111
+ history() {
112
+ return log;
113
+ },
114
+ stats() {
115
+ return {
116
+ published,
117
+ pulled,
118
+ byKind: { ...byKind }
119
+ };
70
120
  }
71
121
  };
72
122
  }
73
123
  //#endregion
74
- //#region src/runtime/supervise/coordination-log.ts
124
+ //#region src/mcp/tools/spawn-resource-paths.ts
75
125
  /**
76
- * Durable side-log for coordination evidence the spawn journal does not own: questions, analyst
77
- * findings, answer decisions, authorized continuation receipts, delivery-attempt markers, and
78
- * delivery outcomes. A durable run
79
- * (`supervise({ runDir })`) appends them as they publish and loads them on resume, so a restarted
80
- * coordinator retains the exact evidence produced by prior processes.
126
+ * Inline resources a manager hands a child by PATH instead of by content.
81
127
  *
82
- * Answer down-events also fold status on load: a question answered before the crash reloads as
83
- * `answered`, not as a re-blocking `open`. Settled events are skipped (the spawn journal is their
84
- * ledger). A receipt followed by an attempt but no outcome proves the process died in the delivery
85
- * window; that outcome remains unknown and no prior instruction is auto-delivered.
128
+ * The canonical profile schema knows two resource kinds, `inline` (a content string) and `github`.
129
+ * A manager that wants a child to receive a data file therefore had one way to do it: emit the
130
+ * bytes inside its own `spawn` tool call. That is a transcription by a language model, and it does
131
+ * not survive size. Measured 2026-09-12 (discovery-lab `mech-interp-foundations-glm2-b-20260912b`):
132
+ * a 29,144-character base64 payload on the manager's disk reached three children as 15,928
133
+ * characters with 11 substitutions, and a fourth child received the literal placeholder the
134
+ * manager meant to replace. Seven blind-check attempts across two runs delivered no data.
86
135
  *
87
- * JSONL, one fsynced record per event, keyed by `runId` several runs may share one log file
88
- * exactly as they share one spawn-journal file.
136
+ * `{ kind: 'inline', name, path }` lets the coordination server be the transport: it reads the
137
+ * file under the manager's workspace root, fills `content`, and the profile that reaches the
138
+ * schema, the journal, and the provider is an ordinary inline resource. Nothing downstream
139
+ * changes; the model's output stops carrying the bytes.
89
140
  *
90
- * @experimental
141
+ * Fail-closed rules, each with the reason a manager needs to fix its call:
142
+ * - no root configured → refused (a nested manager in a remote sandbox has no host directory
143
+ * the server may read for it)
144
+ * - absolute path, `..` escape, or a symlink that resolves outside the root → refused; the
145
+ * workspace root is the only directory a manager may hand out
146
+ * - not a regular file → refused
147
+ * - over {@link SPAWN_RESOURCE_PATH_MAX_BYTES} → refused naming the bound
148
+ * - bytes that are not valid UTF-8 → refused; `content` is a string, so binary data is encoded
149
+ * (base64) by the manager first, exactly as it would be for a content resource
91
150
  */
92
- /** Persist prior context plus exact continuation authorization, attempt, and result evidence.
93
- * Settlements have their own journal. */
94
- function persisted(event) {
95
- return event.type !== "settled";
151
+ const SPAWN_RESOURCE_PATH_MAX_BYTES = 4 * 1024 * 1024;
152
+ function isRecord(value) {
153
+ return typeof value === "object" && value !== null && !Array.isArray(value);
96
154
  }
97
- /** FS-backed `CoordinationLog`: append-only JSONL, fsynced per record. */
98
- var FileCoordinationLog = class {
99
- path;
100
- appendTail = Promise.resolve();
101
- constructor(path) {
102
- this.path = path;
155
+ /** An inline ref that names a path and carries no content. Anything else is left untouched so the
156
+ * canonical schema keeps its verdict on it. */
157
+ function pathInlineRef(value) {
158
+ if (!isRecord(value)) return void 0;
159
+ if (value.kind !== "inline" || value.content !== void 0) return void 0;
160
+ if (typeof value.path !== "string" || typeof value.name !== "string") return void 0;
161
+ return {
162
+ kind: "inline",
163
+ name: value.name,
164
+ path: value.path
165
+ };
166
+ }
167
+ async function readUnderRoot(root, requested) {
168
+ if (requested.length === 0) return {
169
+ ok: false,
170
+ reason: "path is empty"
171
+ };
172
+ if (isAbsolute(requested)) return {
173
+ ok: false,
174
+ reason: `path must be relative to the workspace root, not absolute (${requested})`
175
+ };
176
+ const rootReal = await realpath(root).catch(() => void 0);
177
+ if (rootReal === void 0) return {
178
+ ok: false,
179
+ reason: `workspace root ${root} does not exist`
180
+ };
181
+ const candidate = resolve(rootReal, requested);
182
+ if (!inside(rootReal, candidate)) return {
183
+ ok: false,
184
+ reason: `path ${requested} leaves the workspace root`
185
+ };
186
+ let handle;
187
+ try {
188
+ handle = await open(candidate, "r");
189
+ } catch (error) {
190
+ return {
191
+ ok: false,
192
+ reason: `path ${requested}: ${describe(error)}`
193
+ };
103
194
  }
104
- async append(runId, record, ownerId) {
105
- if (!persisted(record.event)) return;
106
- const append = this.appendTail.then(() => this.appendRecord(runId, record, ownerId));
107
- this.appendTail = append.catch(() => void 0);
108
- return append;
195
+ try {
196
+ const info = await handle.stat();
197
+ if (!info.isFile()) return {
198
+ ok: false,
199
+ reason: `path ${requested} is not a regular file`
200
+ };
201
+ const real = await realpath(candidate).catch(() => void 0);
202
+ if (real === void 0 || !inside(rootReal, real)) return {
203
+ ok: false,
204
+ reason: `path ${requested} resolves outside the workspace root`
205
+ };
206
+ const named = await stat(real).catch(() => void 0);
207
+ if (named === void 0 || named.dev !== info.dev || named.ino !== info.ino) return {
208
+ ok: false,
209
+ reason: `path ${requested} changed while it was being read; retry the spawn`
210
+ };
211
+ if (info.size > 4194304) return {
212
+ ok: false,
213
+ reason: `path ${requested} is ${info.size} bytes; an inline resource is at most ${SPAWN_RESOURCE_PATH_MAX_BYTES} bytes`
214
+ };
215
+ const bytes = await handle.readFile();
216
+ if (bytes.length > 4194304) return {
217
+ ok: false,
218
+ reason: `path ${requested} is ${bytes.length} bytes; an inline resource is at most ${SPAWN_RESOURCE_PATH_MAX_BYTES} bytes`
219
+ };
220
+ const content = bytes.toString("utf8");
221
+ if (!Buffer.from(content, "utf8").equals(bytes)) return {
222
+ ok: false,
223
+ reason: `path ${requested} is not valid UTF-8; encode binary data (base64) before handing it to a child`
224
+ };
225
+ return {
226
+ ok: true,
227
+ content,
228
+ byteLength: bytes.length,
229
+ sha256: createHash("sha256").update(bytes).digest("hex")
230
+ };
231
+ } catch (error) {
232
+ return {
233
+ ok: false,
234
+ reason: `path ${requested}: ${describe(error)}`
235
+ };
236
+ } finally {
237
+ await handle.close().catch(() => void 0);
109
238
  }
110
- async appendRecord(runId, busRecord, ownerId) {
111
- const fs = await import("node:fs/promises");
112
- const path = await import("node:path");
113
- await fs.mkdir(path.dirname(this.path), { recursive: true });
114
- const record = {
115
- runId,
116
- ...ownerId !== void 0 ? { ownerId } : {},
117
- ...busRecord
239
+ }
240
+ /** `target` is `rootReal` itself or strictly below it; both must already be absolute. */
241
+ function inside(rootReal, target) {
242
+ const rel = relative(rootReal, target);
243
+ return rel !== "" && !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel);
244
+ }
245
+ function describe(error) {
246
+ const code = error?.code;
247
+ if (code === "ENOENT") return "does not exist under the workspace root";
248
+ if (code === "EACCES" || code === "EPERM") return "is not readable by the runtime process";
249
+ if (code === "EISDIR") return "is not a regular file";
250
+ return error instanceof Error ? error.message : String(error);
251
+ }
252
+ /**
253
+ * Replace every `{ kind: 'inline', name, path }` under `profile.resources` with the inline
254
+ * resource its bytes make. Returns the profile unchanged (same reference) when nothing names a
255
+ * path, so callers pay nothing on the common case.
256
+ */
257
+ async function resolveSpawnResourcePaths(profile, root) {
258
+ if (!isRecord(profile) || !isRecord(profile.resources)) return {
259
+ ok: true,
260
+ profile,
261
+ resolved: []
262
+ };
263
+ const resources = profile.resources;
264
+ const resolved = [];
265
+ let touched = false;
266
+ const next = { ...resources };
267
+ const substitute = async (at, ref) => {
268
+ if (root === void 0) return { error: "this manager has no workspace root the coordination server may read, so a resource by path cannot be resolved; pass content, or a github resource" };
269
+ const read = await readUnderRoot(root, ref.path);
270
+ if (!read.ok) return { error: read.reason };
271
+ resolved.push({
272
+ at,
273
+ path: ref.path,
274
+ byteLength: read.byteLength,
275
+ sha256: read.sha256
276
+ });
277
+ touched = true;
278
+ return {
279
+ kind: "inline",
280
+ name: ref.name,
281
+ content: read.content
118
282
  };
119
- const needsSeparator = await prepareJsonlAppend(this.path);
120
- const fh = await fs.open(this.path, "a");
121
- try {
122
- await writeAllBytes(fh, `${needsSeparator ? "\n" : ""}${JSON.stringify(record)}\n`);
123
- await fh.sync();
124
- } finally {
125
- await fh.close();
283
+ };
284
+ for (const list of [
285
+ "tools",
286
+ "skills",
287
+ "agents"
288
+ ]) {
289
+ const entries = resources[list];
290
+ if (!Array.isArray(entries)) continue;
291
+ const out = [];
292
+ for (const [index, entry] of entries.entries()) {
293
+ const ref = pathInlineRef(entry);
294
+ if (ref === void 0) {
295
+ out.push(entry);
296
+ continue;
297
+ }
298
+ const at = `${list}[${index}]`;
299
+ const replaced = await substitute(at, ref);
300
+ if ("error" in replaced) return {
301
+ ok: false,
302
+ at,
303
+ reason: replaced.error
304
+ };
305
+ out.push(replaced);
126
306
  }
307
+ next[list] = out;
127
308
  }
128
- async load(runId, ownerId) {
129
- const fs = await import("node:fs/promises");
130
- let text;
131
- try {
132
- text = await fs.readFile(this.path, "utf8");
133
- } catch (err) {
134
- if (isNoEntError(err)) return emptyPriorCoordination(ownerId);
135
- throw err;
309
+ if (Array.isArray(resources.files)) {
310
+ const out = [];
311
+ for (const [index, mount] of resources.files.entries()) {
312
+ const ref = isRecord(mount) ? pathInlineRef(mount.resource) : void 0;
313
+ if (ref === void 0 || !isRecord(mount)) {
314
+ out.push(mount);
315
+ continue;
316
+ }
317
+ const at = `files[${index}].resource`;
318
+ const replaced = await substitute(at, ref);
319
+ if ("error" in replaced) return {
320
+ ok: false,
321
+ at,
322
+ reason: replaced.error
323
+ };
324
+ out.push({
325
+ ...mount,
326
+ resource: replaced
327
+ });
136
328
  }
137
- const byId = /* @__PURE__ */ new Map();
138
- const findings = [];
139
- const escalations = [];
140
- const analystDefinitions = [];
141
- const continuations = [];
142
- const deliveryEvidence = [];
143
- const mail = [];
144
- const records = [];
145
- let legacySeq = 0;
146
- for (const stored of parseCommittedJsonLines(text, this.path)) {
147
- if (stored.runId !== runId) continue;
148
- if (ownerId !== void 0 && stored.ownerId !== ownerId) continue;
149
- const record = "seq" in stored ? {
150
- seq: stored.seq,
151
- at: stored.at,
152
- priority: stored.priority,
153
- event: stored.event
154
- } : {
155
- seq: legacySeq++,
156
- at: Date.parse(stored.at),
157
- priority: 0,
158
- event: stored.event
159
- };
160
- records.push(record);
161
- const ev = record.event;
162
- if (ev.type === "delivery-attempt" || ev.type === "steer" || ev.type === "answer") deliveryEvidence.push(ev);
163
- if (ev.type === "question") byId.set(ev.question.id, ev.question);
164
- else if (ev.type === "finding") findings.push(ev.finding);
165
- else if (ev.type === "answer") {
166
- const prior = byId.get(ev.questionId);
167
- if (prior && ev.down.delivered) byId.set(ev.questionId, {
168
- ...prior,
169
- status: "answered",
170
- decision: {
171
- kind: "answer",
172
- answer: ev.down.instruction,
173
- by: "prior-run"
174
- }
175
- });
176
- } else if (ev.type === "instruction") continuations.push(ev.instruction);
177
- else if (ev.type === "mail") mail.push(ev.mail);
178
- else if (ev.type === "escalation") escalations.push(ev.escalation);
179
- else if (ev.type === "analyst-defined") analystDefinitions.push(ev.analyst);
180
- }
181
- return {
182
- ...ownerId !== void 0 ? { ownerId } : {},
183
- questions: [...byId.values()],
184
- findings,
185
- escalations,
186
- analystDefinitions,
187
- continuations,
188
- deliveryEvidence,
189
- mail,
190
- records
329
+ next.files = out;
330
+ }
331
+ const instructions = pathInlineRef(resources.instructions);
332
+ if (instructions !== void 0) {
333
+ const replaced = await substitute("instructions", instructions);
334
+ if ("error" in replaced) return {
335
+ ok: false,
336
+ at: "instructions",
337
+ reason: replaced.error
191
338
  };
339
+ next.instructions = replaced;
192
340
  }
193
- };
194
- function emptyPriorCoordination(ownerId) {
195
- return {
196
- ...ownerId !== void 0 ? { ownerId } : {},
197
- questions: [],
198
- findings: [],
199
- escalations: [],
200
- analystDefinitions: [],
201
- continuations: [],
202
- deliveryEvidence: [],
203
- mail: [],
204
- records: []
205
- };
206
- }
207
- //#endregion
208
- //#region src/runtime/supervise/run-context.ts
209
- /**
210
- *
211
- * `createInMemoryRunContext` — the one-call bundle of the in-memory stores a
212
- * `createSupervisor().run(root, task, opts)` needs: a fresh `InMemorySpawnJournal`
213
- * (the event-sourced spawn log), a fresh `InMemoryResultBlobStore` (the
214
- * content-addressed `outRef` payload store the driver's `observe`/`finalize` reads
215
- * settled outputs through), and a fresh `createExecutorRegistry()` (the open
216
- * `AgentSpec → Executor` resolver).
217
- *
218
- * It exists to kill the boilerplate every offline/local supervised run repeats by
219
- * hand — three constructors threaded into `SupervisorOpts` — and to single-source the
220
- * ONE wiring invariant that is easy to get wrong: when the root is the recursive
221
- * `driverAgent` LLM-driver brain AND it may spawn nested managed children, the registry MUST be
222
- * wrapped with `withDriverExecutor` so a child constructed by `driverChild` resolves to the
223
- * nested-scope executor — and that SAME blob
224
- * store MUST be the one passed to `driverAgent({ blobs })`, or the driver
225
- * reads from a different store than the scope writes to. Pass `{ withDriver: true }`
226
- * and reuse the returned `blobs` for both.
227
- *
228
- * The spread shape matches `SupervisorOpts` exactly, so the call site reads:
229
- * const run = createInMemoryRunContext()
230
- * await createSupervisor().run(root, task, { budget, runId, ...run })
231
- *
232
- * @experimental
233
- */
234
- /**
235
- * Build a fresh in-memory run context. Every call returns NEW stores (no shared global
236
- * state between runs), so two runs never cross-contaminate their journals/blobs.
237
- */
238
- function createInMemoryRunContext(opts = {}) {
239
- const base = createExecutorRegistry();
240
- return {
241
- journal: new InMemorySpawnJournal(),
242
- blobs: new InMemoryResultBlobStore(),
243
- executors: opts.withDriver ? withDriverExecutor(base) : base
244
- };
245
- }
246
- /**
247
- * Build a DURABLE run context: the spawn journal and the result blobs are file-backed (fsynced
248
- * per append/write) under `dir`, and the context carries `resume: true` so spreading it into
249
- * `SupervisorOpts` makes the supervisor `loadTree`-first. A run that dies mid-flight therefore
250
- * resumes when it is re-run with the SAME `runId` and the SAME `dir`: the committed children come
251
- * back on `Scope.resume` (rehydrated by `replaySpawnTree`) instead of being re-executed.
252
- *
253
- * Layout: `${dir}/spawn-journal.jsonl` (one JSONL record per event), `${dir}/blobs/` (one
254
- * content-addressed JSON file per settled result), and `${dir}/coordination-log.jsonl`
255
- * (questions, findings, answer decisions, and authorized continuation receipts retained as
256
- * evidence). The directory is created on first write.
257
- *
258
- * Opt-in by construction — `createInMemoryRunContext()` is unchanged and stays the default, so no
259
- * existing consumer writes to disk or resumes unless it asks for this.
260
- */
261
- function createFileRunContext(dir, opts = {}) {
262
- const base = createExecutorRegistry();
263
- return {
264
- journal: new FileSpawnJournal(`${dir}/spawn-journal.jsonl`),
265
- blobs: new FileResultBlobStore(`${dir}/blobs`),
266
- executors: opts.withDriver ? withDriverExecutor(base) : base,
267
- resume: true,
268
- coordinationLog: new FileCoordinationLog(`${dir}/coordination-log.jsonl`)
269
- };
270
- }
271
- //#endregion
272
- //#region src/runtime/supervise/detector-monitor.ts
273
- /**
274
- *
275
- * The ONLINE analyst: watch a `TraceSource` and fold each tool span through agent-eval's published
276
- * streaming detector kernel (`repeatedActionDetector`/`errorStreakDetector` — the SAME kernel the
277
- * control loop folds), firing `onSignal` the moment a worker loops or error-storms. Substrate-
278
- * agnostic: it consumes spans from any source (owned router/bridge loop OR a sandbox box session),
279
- * never the raw tool seam. Detection logic + the failure taxonomy live in agent-eval; not reimplemented.
280
- *
281
- * @experimental
282
- */
283
- /** The default online panel for a tool-call pipe: a worker repeating the same call, or hammering
284
- * consecutive errors. (No-progress needs a domain progress-probe, so it is opt-in, not default.)
285
- *
286
- * Coverage note: `repeated-action` works for EVERY harness (it needs only tool name + args, which
287
- * every adapter provides). `error-streak` needs per-call status — opencode carries it inline
288
- * (`state.status`, VALIDATED live), but claude-code/codex tool-call parts do NOT (their errors live
289
- * in separate result blocks not yet decoded), so error-streak is silent for those until result-block
290
- * decoding is added + live-validated. It is in the panel because it is correct where status exists. */
291
- function defaultToolDetectors() {
292
- return [repeatedActionDetector({ maxRepeated: 3 }), errorStreakDetector({ maxErrors: 3 })];
293
- }
294
- /** Subscribe to a `TraceSource` and run the streaming detectors over its live spans. Returns an
295
- * unsubscribe. A defensive `argHash` failure (circular args) never throws out of the side-channel. */
296
- function watchTrace(source, opts = {}) {
297
- const detectors = opts.detectors ?? defaultToolDetectors();
298
- return source.onSpan((span) => {
299
- let fingerprint;
300
- try {
301
- fingerprint = `${span.toolName}|${argHash(span.args)}`;
302
- } catch {
303
- fingerprint = `${span.toolName}|<unhashable>`;
304
- }
305
- const signals = observeAll(detectors, {
306
- actionFingerprint: fingerprint,
307
- ...span.status ? { status: span.status } : {},
308
- label: span.toolName
309
- });
310
- for (const s of signals) opts.onSignal?.(s, span);
311
- });
312
- }
313
- //#endregion
314
- //#region src/runtime/supervise/event-bus.ts
315
- /** 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.
316
- * @experimental In-process queue; durability is a transport swap that does not exist yet. */
317
- function createEventBus(now = Date.now) {
318
- const queue = [];
319
- const log = [];
320
- const subscribers = [];
321
- const byKind = {};
322
- const staged = /* @__PURE__ */ new WeakMap();
323
- let seq = 0;
324
- let published = 0;
325
- let pulled = 0;
326
- const matches = (r, kinds) => !kinds || kinds.includes(r.event.type);
327
- const bestIndex = (kinds) => {
328
- let best = -1;
329
- let bestPriority = Number.NEGATIVE_INFINITY;
330
- for (let i = 0; i < queue.length; i++) {
331
- const r = queue[i];
332
- if (!r || !matches(r, kinds)) continue;
333
- if (r.priority > bestPriority) {
334
- best = i;
335
- bestPriority = r.priority;
336
- }
337
- }
338
- return best;
341
+ if (!touched) return {
342
+ ok: true,
343
+ profile,
344
+ resolved: []
339
345
  };
340
346
  return {
341
- async publish(event, opts) {
342
- const record = staged.get(event) ?? {
343
- seq: seq++,
344
- at: now(),
345
- priority: opts?.priority ?? 0,
346
- event
347
- };
348
- staged.set(event, record);
349
- for (const handler of subscribers) await handler(record);
350
- staged.delete(event);
351
- if (opts?.queue !== false) queue.push(record);
352
- log.push(record);
353
- published += 1;
354
- byKind[event.type] = (byKind[event.type] ?? 0) + 1;
355
- return record;
356
- },
357
- pull(kinds) {
358
- const i = bestIndex(kinds);
359
- if (i < 0) return void 0;
360
- pulled++;
361
- return queue.splice(i, 1)[0]?.event;
362
- },
363
- subscribe(handler) {
364
- subscribers.push(handler);
365
- return () => {
366
- const i = subscribers.indexOf(handler);
367
- if (i >= 0) subscribers.splice(i, 1);
368
- };
347
+ ok: true,
348
+ profile: {
349
+ ...profile,
350
+ resources: next
369
351
  },
370
- pending(kinds) {
371
- return kinds ? queue.filter((r) => matches(r, kinds)).length : queue.length;
372
- },
373
- history() {
374
- return log;
375
- },
376
- stats() {
377
- return {
378
- published,
379
- pulled,
380
- byKind: { ...byKind }
381
- };
382
- }
352
+ resolved
383
353
  };
384
354
  }
385
355
  //#endregion
@@ -786,7 +756,7 @@ const spawnProfileFields = [
786
756
  },
787
757
  {
788
758
  name: "resources",
789
- description: "Files, tools, skills, and agents materialized into the child workspace before it starts. Brief form: `{ files?, tools?, skills?, agents? }`, each an array whose entries are `{ kind: \"inline\", name, content }` or `{ kind: \"github\", repository?, path, ref? }` (`files` entries wrap that as `{ path, resource, executable? }`). A child typically gets `files` for seed inputs and `skills` for a procedure it must follow; the canonical AgentProfile schema carries the full form and governs validation.",
759
+ description: "Files, tools, skills, and agents materialized into the child workspace before it starts. Brief form: `{ files?, tools?, skills?, agents? }`, each an array whose entries are `{ kind: \"inline\", name, content }`, `{ kind: \"inline\", name, path }`, or `{ kind: \"github\", repository?, path, ref? }` (`files` entries wrap that as `{ path, resource, executable? }`). An inline `path` is relative to YOUR workspace and is read by the runtime, so use it for any file over a few hundred bytes: content you retype in this call arrives truncated and altered, a path arrives byte-exact and the result reports its sha256. A child typically gets `files` for seed inputs and `skills` for a procedure it must follow; the canonical AgentProfile schema carries the full form and governs validation.",
790
760
  brief: {
791
761
  type: "object",
792
762
  properties: {
@@ -1929,7 +1899,16 @@ function createCoordinationTools(opts) {
1929
1899
  live,
1930
1900
  freeSlots: freeWorkerSlots()
1931
1901
  }))(liveWorkerCount()));
1932
- const parsedProfile = agentProfileSchema.safeParse(a.profile);
1902
+ const resourcePaths = await resolveSpawnResourcePaths(a.profile, opts.spawnResourceRoot);
1903
+ if (!resourcePaths.ok) return {
1904
+ error: "invalid-profile",
1905
+ reason: `resources.${resourcePaths.at}: ${resourcePaths.reason}`,
1906
+ issues: [{
1907
+ path: `resources.${resourcePaths.at}`,
1908
+ message: resourcePaths.reason
1909
+ }]
1910
+ };
1911
+ const parsedProfile = agentProfileSchema.safeParse(resourcePaths.profile);
1933
1912
  if (!parsedProfile.success) return Promise.resolve({
1934
1913
  error: "invalid-profile",
1935
1914
  reason: `the profile you wrote is not a valid AgentProfile: ${parsedProfile.error.issues.map((issue) => `${issue.path.join(".") || "profile"} ${issue.message}`).join("; ")}`,
@@ -2023,7 +2002,8 @@ function createCoordinationTools(opts) {
2023
2002
  ...continuity.continuity === "resume" ? { resume: continuity.resume } : {},
2024
2003
  live: liveWorkerCount(),
2025
2004
  freeSlots: freeWorkerSlots(),
2026
- ...priorHistory
2005
+ ...priorHistory,
2006
+ ...resourcePaths.resolved.length === 0 ? {} : { resourcesFromPath: resourcePaths.resolved }
2027
2007
  } : {
2028
2008
  error: res.reason,
2029
2009
  reason: res.reason === "usd-unbudgeted" ? "this run's root budget declares no maxUsd, so a child budget naming maxUsd can never be admitted at any amount — spawn with a budget that omits maxUsd" : res.reason === "in-doubt" ? "this key has a prior worker recorded as started without a terminal receipt; no replacement was started because that remote worker may still be running — inspect or recover the exact prior execution before retrying" : `the conserved pool refused this spawn (${String(res.reason)}); the run has no allocation left to give this worker`,
@@ -2642,6 +2622,269 @@ function safeJsonText(value) {
2642
2622
  }
2643
2623
  }
2644
2624
  //#endregion
2625
+ //#region src/runtime/supervise/run-cancellation.ts
2626
+ /** Apply a durable request through the run's existing abort controller. */
2627
+ function applyRunCancellation(dir, abortRun, now, path = "turn-boundary") {
2628
+ const request = readRunCancelRequest(dir);
2629
+ if (request === void 0) return void 0;
2630
+ const prior = readRunCancellation(dir, request.operationId);
2631
+ if (prior !== void 0) return prior;
2632
+ abortRun(request.reason ?? "run cancel requested", request);
2633
+ const observedAt = now();
2634
+ const appliedAfterMs = Math.max(0, Date.parse(observedAt) - Date.parse(request.at));
2635
+ const record = {
2636
+ operationId: request.operationId,
2637
+ effect: "cancel_requested",
2638
+ path,
2639
+ appliedAfterMs,
2640
+ ...request.deadlineMs === void 0 ? {} : { deadlineExceeded: appliedAfterMs > request.deadlineMs },
2641
+ requestedAt: request.at,
2642
+ observedAt,
2643
+ ...request.reason === void 0 ? {} : { reason: request.reason },
2644
+ detail: "root abort issued to the whole run; termination not yet proven"
2645
+ };
2646
+ writeRunCancellation(dir, record);
2647
+ return record;
2648
+ }
2649
+ /** External drivers may remain silent indefinitely, so turn boundaries cannot observe control. */
2650
+ function watchRunCancellation(dir, abortRun) {
2651
+ mkdirSync(workerCancellationsDir(dir), { recursive: true });
2652
+ let active = true;
2653
+ let failure;
2654
+ const check = (path = "observer") => {
2655
+ if (failure !== void 0) throw failure;
2656
+ if (active) applyRunCancellation(dir, abortRun, () => (/* @__PURE__ */ new Date()).toISOString(), path);
2657
+ };
2658
+ const fail = (error) => {
2659
+ if (!active) return;
2660
+ failure = error;
2661
+ abortRun(`durable run cancellation observer failed: ${String(error)}`);
2662
+ };
2663
+ const watcher = watch(workerCancellationsDir(dir), (_event, filename) => {
2664
+ if (!active || filename !== null && filename !== "run.request.json") return;
2665
+ try {
2666
+ check();
2667
+ } catch (error) {
2668
+ fail(error);
2669
+ }
2670
+ });
2671
+ watcher.on("error", fail);
2672
+ const fallback = setInterval(() => {
2673
+ try {
2674
+ check("fallback");
2675
+ } catch (error) {
2676
+ fail(error);
2677
+ }
2678
+ }, 100);
2679
+ fallback.unref();
2680
+ return {
2681
+ check,
2682
+ close() {
2683
+ active = false;
2684
+ watcher.close();
2685
+ clearInterval(fallback);
2686
+ }
2687
+ };
2688
+ }
2689
+ //#endregion
2690
+ //#region src/runtime/supervise/coordination-log.ts
2691
+ /**
2692
+ * Durable side-log for coordination evidence the spawn journal does not own: questions, analyst
2693
+ * findings, answer decisions, authorized continuation receipts, delivery-attempt markers, and
2694
+ * delivery outcomes. A durable run
2695
+ * (`supervise({ runDir })`) appends them as they publish and loads them on resume, so a restarted
2696
+ * coordinator retains the exact evidence produced by prior processes.
2697
+ *
2698
+ * Answer down-events also fold status on load: a question answered before the crash reloads as
2699
+ * `answered`, not as a re-blocking `open`. Settled events are skipped (the spawn journal is their
2700
+ * ledger). A receipt followed by an attempt but no outcome proves the process died in the delivery
2701
+ * window; that outcome remains unknown and no prior instruction is auto-delivered.
2702
+ *
2703
+ * JSONL, one fsynced record per event, keyed by `runId` — several runs may share one log file
2704
+ * exactly as they share one spawn-journal file.
2705
+ *
2706
+ * @experimental
2707
+ */
2708
+ /** Persist prior context plus exact continuation authorization, attempt, and result evidence.
2709
+ * Settlements have their own journal. */
2710
+ function persisted(event) {
2711
+ return event.type !== "settled";
2712
+ }
2713
+ /** FS-backed `CoordinationLog`: append-only JSONL, fsynced per record. */
2714
+ var FileCoordinationLog = class {
2715
+ path;
2716
+ appendTail = Promise.resolve();
2717
+ constructor(path) {
2718
+ this.path = path;
2719
+ }
2720
+ async append(runId, record, ownerId) {
2721
+ if (!persisted(record.event)) return;
2722
+ const append = this.appendTail.then(() => this.appendRecord(runId, record, ownerId));
2723
+ this.appendTail = append.catch(() => void 0);
2724
+ return append;
2725
+ }
2726
+ async appendRecord(runId, busRecord, ownerId) {
2727
+ const fs = await import("node:fs/promises");
2728
+ const path = await import("node:path");
2729
+ await fs.mkdir(path.dirname(this.path), { recursive: true });
2730
+ const record = {
2731
+ runId,
2732
+ ...ownerId !== void 0 ? { ownerId } : {},
2733
+ ...busRecord
2734
+ };
2735
+ const needsSeparator = await prepareJsonlAppend(this.path);
2736
+ const fh = await fs.open(this.path, "a");
2737
+ try {
2738
+ await writeAllBytes(fh, `${needsSeparator ? "\n" : ""}${JSON.stringify(record)}\n`);
2739
+ await fh.sync();
2740
+ } finally {
2741
+ await fh.close();
2742
+ }
2743
+ }
2744
+ async load(runId, ownerId) {
2745
+ const fs = await import("node:fs/promises");
2746
+ let text;
2747
+ try {
2748
+ text = await fs.readFile(this.path, "utf8");
2749
+ } catch (err) {
2750
+ if (isNoEntError(err)) return emptyPriorCoordination(ownerId);
2751
+ throw err;
2752
+ }
2753
+ const byId = /* @__PURE__ */ new Map();
2754
+ const findings = [];
2755
+ const escalations = [];
2756
+ const analystDefinitions = [];
2757
+ const continuations = [];
2758
+ const deliveryEvidence = [];
2759
+ const mail = [];
2760
+ const records = [];
2761
+ let legacySeq = 0;
2762
+ for (const stored of parseCommittedJsonLines(text, this.path)) {
2763
+ if (stored.runId !== runId) continue;
2764
+ if (ownerId !== void 0 && stored.ownerId !== ownerId) continue;
2765
+ const record = "seq" in stored ? {
2766
+ seq: stored.seq,
2767
+ at: stored.at,
2768
+ priority: stored.priority,
2769
+ event: stored.event
2770
+ } : {
2771
+ seq: legacySeq++,
2772
+ at: Date.parse(stored.at),
2773
+ priority: 0,
2774
+ event: stored.event
2775
+ };
2776
+ records.push(record);
2777
+ const ev = record.event;
2778
+ if (ev.type === "delivery-attempt" || ev.type === "steer" || ev.type === "answer") deliveryEvidence.push(ev);
2779
+ if (ev.type === "question") byId.set(ev.question.id, ev.question);
2780
+ else if (ev.type === "finding") findings.push(ev.finding);
2781
+ else if (ev.type === "answer") {
2782
+ const prior = byId.get(ev.questionId);
2783
+ if (prior && ev.down.delivered) byId.set(ev.questionId, {
2784
+ ...prior,
2785
+ status: "answered",
2786
+ decision: {
2787
+ kind: "answer",
2788
+ answer: ev.down.instruction,
2789
+ by: "prior-run"
2790
+ }
2791
+ });
2792
+ } else if (ev.type === "instruction") continuations.push(ev.instruction);
2793
+ else if (ev.type === "mail") mail.push(ev.mail);
2794
+ else if (ev.type === "escalation") escalations.push(ev.escalation);
2795
+ else if (ev.type === "analyst-defined") analystDefinitions.push(ev.analyst);
2796
+ }
2797
+ return {
2798
+ ...ownerId !== void 0 ? { ownerId } : {},
2799
+ questions: [...byId.values()],
2800
+ findings,
2801
+ escalations,
2802
+ analystDefinitions,
2803
+ continuations,
2804
+ deliveryEvidence,
2805
+ mail,
2806
+ records
2807
+ };
2808
+ }
2809
+ };
2810
+ function emptyPriorCoordination(ownerId) {
2811
+ return {
2812
+ ...ownerId !== void 0 ? { ownerId } : {},
2813
+ questions: [],
2814
+ findings: [],
2815
+ escalations: [],
2816
+ analystDefinitions: [],
2817
+ continuations: [],
2818
+ deliveryEvidence: [],
2819
+ mail: [],
2820
+ records: []
2821
+ };
2822
+ }
2823
+ //#endregion
2824
+ //#region src/runtime/supervise/run-context.ts
2825
+ /**
2826
+ *
2827
+ * `createInMemoryRunContext` — the one-call bundle of the in-memory stores a
2828
+ * `createSupervisor().run(root, task, opts)` needs: a fresh `InMemorySpawnJournal`
2829
+ * (the event-sourced spawn log), a fresh `InMemoryResultBlobStore` (the
2830
+ * content-addressed `outRef` payload store the driver's `observe`/`finalize` reads
2831
+ * settled outputs through), and a fresh `createExecutorRegistry()` (the open
2832
+ * `AgentSpec → Executor` resolver).
2833
+ *
2834
+ * It exists to kill the boilerplate every offline/local supervised run repeats by
2835
+ * hand — three constructors threaded into `SupervisorOpts` — and to single-source the
2836
+ * ONE wiring invariant that is easy to get wrong: when the root is the recursive
2837
+ * `driverAgent` LLM-driver brain AND it may spawn nested managed children, the registry MUST be
2838
+ * wrapped with `withDriverExecutor` so a child constructed by `driverChild` resolves to the
2839
+ * nested-scope executor — and that SAME blob
2840
+ * store MUST be the one passed to `driverAgent({ blobs })`, or the driver
2841
+ * reads from a different store than the scope writes to. Pass `{ withDriver: true }`
2842
+ * and reuse the returned `blobs` for both.
2843
+ *
2844
+ * The spread shape matches `SupervisorOpts` exactly, so the call site reads:
2845
+ * const run = createInMemoryRunContext()
2846
+ * await createSupervisor().run(root, task, { budget, runId, ...run })
2847
+ *
2848
+ * @experimental
2849
+ */
2850
+ /**
2851
+ * Build a fresh in-memory run context. Every call returns NEW stores (no shared global
2852
+ * state between runs), so two runs never cross-contaminate their journals/blobs.
2853
+ */
2854
+ function createInMemoryRunContext(opts = {}) {
2855
+ const base = createExecutorRegistry();
2856
+ return {
2857
+ journal: new InMemorySpawnJournal(),
2858
+ blobs: new InMemoryResultBlobStore(),
2859
+ executors: opts.withDriver ? withDriverExecutor(base) : base
2860
+ };
2861
+ }
2862
+ /**
2863
+ * Build a DURABLE run context: the spawn journal and the result blobs are file-backed (fsynced
2864
+ * per append/write) under `dir`, and the context carries `resume: true` so spreading it into
2865
+ * `SupervisorOpts` makes the supervisor `loadTree`-first. A run that dies mid-flight therefore
2866
+ * resumes when it is re-run with the SAME `runId` and the SAME `dir`: the committed children come
2867
+ * back on `Scope.resume` (rehydrated by `replaySpawnTree`) instead of being re-executed.
2868
+ *
2869
+ * Layout: `${dir}/spawn-journal.jsonl` (one JSONL record per event), `${dir}/blobs/` (one
2870
+ * content-addressed JSON file per settled result), and `${dir}/coordination-log.jsonl`
2871
+ * (questions, findings, answer decisions, and authorized continuation receipts retained as
2872
+ * evidence). The directory is created on first write.
2873
+ *
2874
+ * Opt-in by construction — `createInMemoryRunContext()` is unchanged and stays the default, so no
2875
+ * existing consumer writes to disk or resumes unless it asks for this.
2876
+ */
2877
+ function createFileRunContext(dir, opts = {}) {
2878
+ const base = createExecutorRegistry();
2879
+ return {
2880
+ journal: new FileSpawnJournal(`${dir}/spawn-journal.jsonl`),
2881
+ blobs: new FileResultBlobStore(`${dir}/blobs`),
2882
+ executors: opts.withDriver ? withDriverExecutor(base) : base,
2883
+ resume: true,
2884
+ coordinationLog: new FileCoordinationLog(`${dir}/coordination-log.jsonl`)
2885
+ };
2886
+ }
2887
+ //#endregion
2645
2888
  //#region src/runtime/anytime.ts
2646
2889
  /**
2647
2890
  * The best-so-far fold — the ONE definition of "how good was the run after k results", shared by
@@ -3480,6 +3723,7 @@ function driverAgent(opts) {
3480
3723
  ...opts.continuityByProfile ? { continuityByProfile: opts.continuityByProfile } : {},
3481
3724
  ...opts.preflightSpawn ? { preflightSpawn: opts.preflightSpawn } : {},
3482
3725
  ...opts.resolveSpawnProfile ? { resolveSpawnProfile: opts.resolveSpawnProfile } : {},
3726
+ ...opts.spawnResourceRoot ? { spawnResourceRoot: opts.spawnResourceRoot } : {},
3483
3727
  ...opts.escalateQuestion ? { escalateQuestion: opts.escalateQuestion } : {},
3484
3728
  ...opts.onEvent ? { onEvent: opts.onEvent } : {},
3485
3729
  ...opts.replaySettlements ? { replaySettlements: true } : {},
@@ -3811,6 +4055,6 @@ function errMessage(e) {
3811
4055
  return e instanceof Error ? e.message : String(e);
3812
4056
  }
3813
4057
  //#endregion
3814
- export { watchTrace as A, createCoordinationTools as C, questionEscalationTargets as D, parseAuthoredAnalystDefinition as E, watchRunCancellation as F, createInMemoryRunContext as M, FileCoordinationLog as N, createEventBus as O, applyRunCancellation as P, coordinationVerbNames as S, normalizeAnalyzeOnSettle as T, renderAnytimeTable as _, allOf as a, analystToolGroupNames as b, createProgressTracker as c, progressStop as d, sampleFromSettled as f, plateauLength as g, bestSoFar as h, finalizeBestDelivered as i, createFileRunContext as j, defaultToolDetectors as k, noProgressFor as l, areaUnderCurve as m, createSteerAcknowledger as n, allWorkersStalled as o, anytimeReport as p, driverAgent as r, anyOf as s, createCancelAcknowledger as t, plateau as u, ANALYST_DEFINITION_BOUNDS as v, downMessageRefusalReasons as w, canonicalFindingEvent as x, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as y };
4058
+ export { normalizeAnalyzeOnSettle as A, ANALYST_DEFINITION_BOUNDS as C, coordinationVerbNames as D, canonicalFindingEvent as E, watchTrace as F, questionEscalationTargets as M, createEventBus as N, createCoordinationTools as O, defaultToolDetectors as P, watchRunCancellation as S, analystToolGroupNames as T, renderAnytimeTable as _, allOf as a, FileCoordinationLog as b, createProgressTracker as c, progressStop as d, sampleFromSettled as f, plateauLength as g, bestSoFar as h, finalizeBestDelivered as i, parseAuthoredAnalystDefinition as j, downMessageRefusalReasons as k, noProgressFor as l, areaUnderCurve as m, createSteerAcknowledger as n, allWorkersStalled as o, anytimeReport as p, driverAgent as r, anyOf as s, createCancelAcknowledger as t, plateau as u, createFileRunContext as v, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as w, applyRunCancellation as x, createInMemoryRunContext as y };
3815
4059
 
3816
- //# sourceMappingURL=coordination-driver-CvinSUI-.js.map
4060
+ //# sourceMappingURL=coordination-driver-BxGh-Tnj.js.map