@tangle-network/agent-runtime 0.69.0 → 0.70.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.
- package/README.md +81 -13
- package/dist/agent.d.ts +4 -5
- package/dist/agent.js +3 -4
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop.d.ts +3 -4
- package/dist/{chunk-K4FQSEXQ.js → chunk-BGOLR66M.js} +6 -6
- package/dist/{chunk-G6U3GVK2.js → chunk-CRAH5EY2.js} +2 -2
- package/dist/{chunk-RTDI2DHD.js → chunk-HY4YOQM2.js} +6 -6
- package/dist/chunk-HY4YOQM2.js.map +1 -0
- package/dist/{chunk-QZ6FA5LM.js → chunk-LJZ5GC6C.js} +3 -3
- package/dist/{chunk-NLRA6434.js → chunk-YFOPWG74.js} +577 -525
- package/dist/chunk-YFOPWG74.js.map +1 -0
- package/dist/{coordination-Curpzeyc.d.ts → coordination-C7WxwHXq.d.ts} +8 -1
- package/dist/{delegates-CLFNAKyi.d.ts → delegates-DqAgo32T.d.ts} +105 -10
- package/dist/{improvement-adapter-BC4HhuAR.d.ts → improvement-adapter-CioiEE2z.d.ts} +1 -1
- package/dist/index.d.ts +439 -100
- package/dist/index.js +591 -130
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +2 -3
- package/dist/{loop-runner-bin-B6dzNZC8.d.ts → loop-runner-bin-a8bu4O5-.d.ts} +4 -4
- package/dist/loop-runner-bin.d.ts +7 -10
- package/dist/loop-runner-bin.js +6 -7
- package/dist/loops.d.ts +3149 -16
- package/dist/loops.js +33 -60
- package/dist/mcp/bin.js +13 -14
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +8 -12
- package/dist/mcp/index.js +10 -12
- package/dist/mcp/index.js.map +1 -1
- package/dist/{openai-tools-CA2N3-Ak.d.ts → openai-tools-DPx9Gzvn.d.ts} +1 -1
- package/dist/profiles.d.ts +94 -6
- package/dist/profiles.js +320 -9
- package/dist/profiles.js.map +1 -1
- package/dist/{router-client-30Y_pca8.d.ts → router-client-C7kp_ECN.d.ts} +37 -1
- package/dist/{substrate-CUgk7F7s.d.ts → substrate-BoRXgvka.d.ts} +52 -1
- package/dist/{types-p8dWBIXL.d.ts → types-BC3bZpH0.d.ts} +1 -1
- package/dist/{types-Crxftafi.d.ts → types-BYa2ZOAx.d.ts} +83 -2
- package/dist/{worktree-fanout-DUiKPApb.d.ts → worktree-fanout-gNfl0Byj.d.ts} +9 -37
- package/package.json +17 -37
- package/dist/audit.d.ts +0 -93
- package/dist/audit.js +0 -312
- package/dist/audit.js.map +0 -1
- package/dist/chunk-4B6U4CVQ.js +0 -15
- package/dist/chunk-4B6U4CVQ.js.map +0 -1
- package/dist/chunk-NLRA6434.js.map +0 -1
- package/dist/chunk-O2UPHN7X.js +0 -114
- package/dist/chunk-O2UPHN7X.js.map +0 -1
- package/dist/chunk-RTDI2DHD.js.map +0 -1
- package/dist/coder-2leJPOvC.d.ts +0 -52
- package/dist/improvement.d.ts +0 -208
- package/dist/improvement.js +0 -343
- package/dist/improvement.js.map +0 -1
- package/dist/local-harness-BE_h8szs.d.ts +0 -93
- package/dist/run-loop-D3PwlG7J.d.ts +0 -112
- package/dist/runtime-hooks-C7JwKb9E.d.ts +0 -70
- package/dist/runtime.d.ts +0 -3120
- package/dist/runtime.js +0 -263
- package/dist/runtime.js.map +0 -1
- package/dist/topology.d.ts +0 -126
- package/dist/topology.js +0 -333
- package/dist/topology.js.map +0 -1
- package/dist/workflow.d.ts +0 -551
- package/dist/workflow.js +0 -1781
- package/dist/workflow.js.map +0 -1
- /package/dist/{chunk-K4FQSEXQ.js.map → chunk-BGOLR66M.js.map} +0 -0
- /package/dist/{chunk-G6U3GVK2.js.map → chunk-CRAH5EY2.js.map} +0 -0
- /package/dist/{chunk-QZ6FA5LM.js.map → chunk-LJZ5GC6C.js.map} +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
buildLoopSpanNodes
|
|
3
|
+
} from "./chunk-G3RGMA7C.js";
|
|
4
4
|
import {
|
|
5
5
|
AgentEvalError,
|
|
6
6
|
AnalystError,
|
|
7
|
+
ConfigError,
|
|
7
8
|
NotFoundError,
|
|
8
9
|
PlannerError,
|
|
9
10
|
RuntimeRunStateError,
|
|
@@ -11,12 +12,11 @@ import {
|
|
|
11
12
|
extractLlmCallEvent
|
|
12
13
|
} from "./chunk-VLF5RHEQ.js";
|
|
13
14
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from "./chunk-G3RGMA7C.js";
|
|
15
|
+
UI_LENSES
|
|
16
|
+
} from "./chunk-WIR4HOOJ.js";
|
|
17
|
+
|
|
18
|
+
// src/runtime/index.ts
|
|
19
|
+
import { computeFindingId as computeFindingId2, makeFinding as makeFinding3 } from "@tangle-network/agent-eval";
|
|
20
20
|
|
|
21
21
|
// src/durable/spawn-journal.ts
|
|
22
22
|
import { createHash } from "crypto";
|
|
@@ -149,38 +149,6 @@ var InMemoryResultBlobStore = class {
|
|
|
149
149
|
return this.blobs.has(outRef) ? this.blobs.get(outRef) : void 0;
|
|
150
150
|
}
|
|
151
151
|
};
|
|
152
|
-
var FileResultBlobStore = class {
|
|
153
|
-
constructor(dir) {
|
|
154
|
-
this.dir = dir;
|
|
155
|
-
}
|
|
156
|
-
dir;
|
|
157
|
-
async put(outRef, artifact) {
|
|
158
|
-
assertContentAddress(outRef, artifact);
|
|
159
|
-
const fs = await import("fs/promises");
|
|
160
|
-
await fs.mkdir(this.dir, { recursive: true });
|
|
161
|
-
const fh = await fs.open(this.blobPath(outRef), "w");
|
|
162
|
-
try {
|
|
163
|
-
await fh.write(JSON.stringify(artifact));
|
|
164
|
-
await fh.sync();
|
|
165
|
-
} finally {
|
|
166
|
-
await fh.close();
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
async get(outRef) {
|
|
170
|
-
const fs = await import("fs/promises");
|
|
171
|
-
let text;
|
|
172
|
-
try {
|
|
173
|
-
text = await fs.readFile(this.blobPath(outRef), "utf8");
|
|
174
|
-
} catch (err) {
|
|
175
|
-
if (isNoEntError(err)) return void 0;
|
|
176
|
-
throw err;
|
|
177
|
-
}
|
|
178
|
-
return JSON.parse(text);
|
|
179
|
-
}
|
|
180
|
-
blobPath(outRef) {
|
|
181
|
-
return `${this.dir}/${outRef.replace(/:/g, "-")}.json`;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
152
|
function assertContentAddress(outRef, artifact) {
|
|
185
153
|
const expected = contentAddress(artifact);
|
|
186
154
|
if (outRef !== expected) {
|
|
@@ -217,90 +185,6 @@ var InMemorySpawnJournal = class {
|
|
|
217
185
|
tree.events.push({ ...ev });
|
|
218
186
|
}
|
|
219
187
|
};
|
|
220
|
-
var FileSpawnJournal = class {
|
|
221
|
-
constructor(path2) {
|
|
222
|
-
this.path = path2;
|
|
223
|
-
}
|
|
224
|
-
path;
|
|
225
|
-
async loadTree(root) {
|
|
226
|
-
const fs = await import("fs/promises");
|
|
227
|
-
let text;
|
|
228
|
-
try {
|
|
229
|
-
text = await fs.readFile(this.path, "utf8");
|
|
230
|
-
} catch (err) {
|
|
231
|
-
if (isNoEntError(err)) return void 0;
|
|
232
|
-
throw err;
|
|
233
|
-
}
|
|
234
|
-
const lines = text.split("\n").filter((line) => line.length > 0);
|
|
235
|
-
let begun = false;
|
|
236
|
-
const events = [];
|
|
237
|
-
for (const line of lines) {
|
|
238
|
-
const record = JSON.parse(line);
|
|
239
|
-
if (record.root !== root) continue;
|
|
240
|
-
if (record.kind === "begin") {
|
|
241
|
-
begun = true;
|
|
242
|
-
} else {
|
|
243
|
-
if (!begun) {
|
|
244
|
-
throw new Error(
|
|
245
|
-
`spawn journal corrupted: event for tree '${root}' precedes its begin record`
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
assertSeqUnique(root, events, record.event);
|
|
249
|
-
events.push(record.event);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return begun ? events : void 0;
|
|
253
|
-
}
|
|
254
|
-
async beginTree(root, at) {
|
|
255
|
-
const existing = await this.loadTreeBegin(root);
|
|
256
|
-
if (existing) {
|
|
257
|
-
if (existing !== at) {
|
|
258
|
-
throw new Error(
|
|
259
|
-
`spawn tree '${root}' already begun in ${this.path} at ${existing}; refusing to overwrite with ${at}`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
await this.appendRecord({ kind: "begin", root, at });
|
|
265
|
-
}
|
|
266
|
-
async appendEvent(root, ev) {
|
|
267
|
-
const events = await this.loadTree(root);
|
|
268
|
-
if (events === void 0) {
|
|
269
|
-
throw new Error(`appendEvent called for unknown spawn tree '${root}'; call beginTree first`);
|
|
270
|
-
}
|
|
271
|
-
assertSeqUnique(root, events, ev);
|
|
272
|
-
await this.appendRecord({ kind: "event", root, event: ev });
|
|
273
|
-
}
|
|
274
|
-
async loadTreeBegin(root) {
|
|
275
|
-
const fs = await import("fs/promises");
|
|
276
|
-
let text;
|
|
277
|
-
try {
|
|
278
|
-
text = await fs.readFile(this.path, "utf8");
|
|
279
|
-
} catch (err) {
|
|
280
|
-
if (isNoEntError(err)) return void 0;
|
|
281
|
-
throw err;
|
|
282
|
-
}
|
|
283
|
-
const lines = text.split("\n").filter((line) => line.length > 0);
|
|
284
|
-
for (const line of lines) {
|
|
285
|
-
const record = JSON.parse(line);
|
|
286
|
-
if (record.root === root && record.kind === "begin") return record.at;
|
|
287
|
-
}
|
|
288
|
-
return void 0;
|
|
289
|
-
}
|
|
290
|
-
async appendRecord(record) {
|
|
291
|
-
const fs = await import("fs/promises");
|
|
292
|
-
const path2 = await import("path");
|
|
293
|
-
await fs.mkdir(path2.dirname(this.path), { recursive: true });
|
|
294
|
-
const fh = await fs.open(this.path, "a");
|
|
295
|
-
try {
|
|
296
|
-
await fh.write(`${JSON.stringify(record)}
|
|
297
|
-
`);
|
|
298
|
-
await fh.sync();
|
|
299
|
-
} finally {
|
|
300
|
-
await fh.close();
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
188
|
function assertSeqUnique(root, events, ev) {
|
|
305
189
|
if (ev.kind === "spawned" || ev.kind === "metered") return;
|
|
306
190
|
if (events.some((e) => e.kind !== "spawned" && e.kind !== "metered" && e.seq === ev.seq)) {
|
|
@@ -309,148 +193,6 @@ function assertSeqUnique(root, events, ev) {
|
|
|
309
193
|
);
|
|
310
194
|
}
|
|
311
195
|
}
|
|
312
|
-
async function replaySpawnTree(journal, blobs, root) {
|
|
313
|
-
const events = await journal.loadTree(root);
|
|
314
|
-
if (events === void 0) {
|
|
315
|
-
throw new Error(`replaySpawnTree: no journaled tree for root '${root}'`);
|
|
316
|
-
}
|
|
317
|
-
const ordered = [...events].sort((a, b) => a.seq - b.seq);
|
|
318
|
-
const labels = /* @__PURE__ */ new Map();
|
|
319
|
-
for (const ev of ordered) {
|
|
320
|
-
if (ev.kind === "spawned") labels.set(ev.id, ev.label);
|
|
321
|
-
}
|
|
322
|
-
const settled = [];
|
|
323
|
-
for (const ev of ordered) {
|
|
324
|
-
if (ev.kind === "spawned") continue;
|
|
325
|
-
if (ev.kind === "metered") continue;
|
|
326
|
-
if (ev.kind === "cancelled") {
|
|
327
|
-
settled.push({
|
|
328
|
-
kind: "down",
|
|
329
|
-
handle: replayHandle(ev.id, labels.get(ev.id) ?? ev.id, "cancelled"),
|
|
330
|
-
reason: ev.reason,
|
|
331
|
-
infra: false,
|
|
332
|
-
restartCount: 0,
|
|
333
|
-
seq: ev.seq
|
|
334
|
-
});
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
if (ev.status === "down") {
|
|
338
|
-
settled.push({
|
|
339
|
-
kind: "down",
|
|
340
|
-
handle: replayHandle(ev.id, labels.get(ev.id) ?? ev.id, "failed"),
|
|
341
|
-
reason: ev.verdict?.notes ?? "child down",
|
|
342
|
-
infra: ev.infra === true,
|
|
343
|
-
restartCount: 0,
|
|
344
|
-
seq: ev.seq
|
|
345
|
-
});
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
if (ev.outRef === void 0) {
|
|
349
|
-
throw new Error(
|
|
350
|
-
`replaySpawnTree: settled-done event for '${ev.id}' (seq ${ev.seq}) has no outRef; cannot rehydrate the result the driver branched on`
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
const out = await blobs.get(ev.outRef);
|
|
354
|
-
if (out === void 0) {
|
|
355
|
-
throw new Error(
|
|
356
|
-
`replaySpawnTree: blob store has no artifact for outRef '${ev.outRef}' (node '${ev.id}', seq ${ev.seq})`
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
settled.push({
|
|
360
|
-
kind: "done",
|
|
361
|
-
handle: replayHandle(ev.id, labels.get(ev.id) ?? ev.id, "done"),
|
|
362
|
-
out,
|
|
363
|
-
outRef: ev.outRef,
|
|
364
|
-
verdict: ev.verdict,
|
|
365
|
-
spent: ev.spent,
|
|
366
|
-
seq: ev.seq
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
return settled;
|
|
370
|
-
}
|
|
371
|
-
function replayHandle(id, label, status) {
|
|
372
|
-
return {
|
|
373
|
-
id,
|
|
374
|
-
label,
|
|
375
|
-
status,
|
|
376
|
-
abort() {
|
|
377
|
-
throw new Error(`cannot abort node '${id}': replayed handles are terminal, not live`);
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
function materializeTreeView(events) {
|
|
382
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
383
|
-
let root;
|
|
384
|
-
const spawns = events.filter((ev) => ev.kind === "spawned").sort((a, b) => a.seq - b.seq);
|
|
385
|
-
const settlements = events.filter((ev) => ev.kind !== "spawned" && ev.kind !== "metered").sort((a, b) => a.seq - b.seq);
|
|
386
|
-
for (const ev of spawns) {
|
|
387
|
-
if (ev.parent === void 0 && root === void 0) root = ev.id;
|
|
388
|
-
nodes.set(ev.id, {
|
|
389
|
-
id: ev.id,
|
|
390
|
-
parent: ev.parent,
|
|
391
|
-
label: ev.label,
|
|
392
|
-
status: "pending",
|
|
393
|
-
runtime: ev.runtime,
|
|
394
|
-
budget: ev.budget,
|
|
395
|
-
spent: zeroSpend()
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
for (const ev of settlements) {
|
|
399
|
-
if (ev.kind === "settled") {
|
|
400
|
-
const node = requireNode(nodes, ev.id);
|
|
401
|
-
node.status = ev.status === "done" ? "done" : "failed";
|
|
402
|
-
node.spent = ev.spent;
|
|
403
|
-
node.outRef = ev.outRef;
|
|
404
|
-
} else {
|
|
405
|
-
const node = requireNode(nodes, ev.id);
|
|
406
|
-
node.status = "cancelled";
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
for (const ev of events) {
|
|
410
|
-
if (ev.kind !== "metered") continue;
|
|
411
|
-
const node = requireNode(nodes, ev.id);
|
|
412
|
-
node.spent = addJournalSpend(node.spent, ev.spend);
|
|
413
|
-
}
|
|
414
|
-
const snapshots = [...nodes.values()].map(freezeSnapshot);
|
|
415
|
-
return {
|
|
416
|
-
root: root ?? snapshots[0]?.id ?? "",
|
|
417
|
-
nodes: snapshots,
|
|
418
|
-
inFlight: snapshots.filter((n) => n.status === "running" || n.status === "acquiring").length
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
function zeroSpend() {
|
|
422
|
-
return { iterations: 0, tokens: zeroTokenUsage(), usd: 0, ms: 0 };
|
|
423
|
-
}
|
|
424
|
-
function addJournalSpend(a, b) {
|
|
425
|
-
return {
|
|
426
|
-
iterations: a.iterations + b.iterations,
|
|
427
|
-
tokens: { input: a.tokens.input + b.tokens.input, output: a.tokens.output + b.tokens.output },
|
|
428
|
-
usd: a.usd + b.usd,
|
|
429
|
-
ms: a.ms + b.ms
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
function requireNode(nodes, id) {
|
|
433
|
-
const node = nodes.get(id);
|
|
434
|
-
if (!node) {
|
|
435
|
-
throw new Error(`spawn journal corrupted: settle/cancel for node '${id}' with no prior spawn`);
|
|
436
|
-
}
|
|
437
|
-
return node;
|
|
438
|
-
}
|
|
439
|
-
function freezeSnapshot(node) {
|
|
440
|
-
return {
|
|
441
|
-
id: node.id,
|
|
442
|
-
parent: node.parent,
|
|
443
|
-
label: node.label,
|
|
444
|
-
status: node.status,
|
|
445
|
-
runtime: node.runtime,
|
|
446
|
-
budget: node.budget,
|
|
447
|
-
spent: node.spent,
|
|
448
|
-
outRef: node.outRef
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
function isNoEntError(err) {
|
|
452
|
-
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
453
|
-
}
|
|
454
196
|
|
|
455
197
|
// src/runtime/anytime.ts
|
|
456
198
|
var median = (xs) => {
|
|
@@ -2143,7 +1885,7 @@ function createScope(args) {
|
|
|
2143
1885
|
let cursorSeq = 0;
|
|
2144
1886
|
let meterSeq = 0;
|
|
2145
1887
|
const now = args.now ?? Date.now;
|
|
2146
|
-
function
|
|
1888
|
+
function spawn5(agent, task, opts) {
|
|
2147
1889
|
if (args.maxDepth !== void 0 && args.depth >= args.maxDepth) {
|
|
2148
1890
|
return { ok: false, reason: "depth-exceeded" };
|
|
2149
1891
|
}
|
|
@@ -2185,7 +1927,7 @@ function createScope(args) {
|
|
|
2185
1927
|
runtime: executor.runtime,
|
|
2186
1928
|
budget: opts.budget,
|
|
2187
1929
|
label: opts.label,
|
|
2188
|
-
spent:
|
|
1930
|
+
spent: zeroSpend(),
|
|
2189
1931
|
settled: void 0,
|
|
2190
1932
|
delivered: false,
|
|
2191
1933
|
...executor.deliver ? { deliver: executor.deliver.bind(executor) } : {}
|
|
@@ -2239,7 +1981,7 @@ function createScope(args) {
|
|
|
2239
1981
|
live.settled = settled;
|
|
2240
1982
|
return { ok: true, handle };
|
|
2241
1983
|
} catch (err) {
|
|
2242
|
-
args.pool.reconcile(reservation.ticket,
|
|
1984
|
+
args.pool.reconcile(reservation.ticket, zeroSpend());
|
|
2243
1985
|
throw err;
|
|
2244
1986
|
}
|
|
2245
1987
|
}
|
|
@@ -2294,7 +2036,7 @@ function createScope(args) {
|
|
|
2294
2036
|
);
|
|
2295
2037
|
}
|
|
2296
2038
|
return {
|
|
2297
|
-
spawn:
|
|
2039
|
+
spawn: spawn5,
|
|
2298
2040
|
next,
|
|
2299
2041
|
send,
|
|
2300
2042
|
signal: args.signal,
|
|
@@ -2546,7 +2288,7 @@ async function teardownSafe(executor, grace) {
|
|
|
2546
2288
|
function downRecord(reason, infra, metered) {
|
|
2547
2289
|
return { kind: "down", reason, infra, restartCount: 0, ...metered ? { metered } : {} };
|
|
2548
2290
|
}
|
|
2549
|
-
function
|
|
2291
|
+
function zeroSpend() {
|
|
2550
2292
|
return { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
2551
2293
|
}
|
|
2552
2294
|
function isAsyncIterable2(value) {
|
|
@@ -3042,7 +2784,7 @@ var FileCorpus = class {
|
|
|
3042
2784
|
try {
|
|
3043
2785
|
text = await fs.readFile(this.path, "utf8");
|
|
3044
2786
|
} catch (err) {
|
|
3045
|
-
if (
|
|
2787
|
+
if (isNoEntError(err)) return /* @__PURE__ */ new Map();
|
|
3046
2788
|
throw err;
|
|
3047
2789
|
}
|
|
3048
2790
|
const lines = text.split("\n").filter((line) => line.length > 0);
|
|
@@ -3116,30 +2858,13 @@ function renderLine(record) {
|
|
|
3116
2858
|
function freeze(record) {
|
|
3117
2859
|
return Object.freeze({ ...record });
|
|
3118
2860
|
}
|
|
3119
|
-
function
|
|
2861
|
+
function isNoEntError(err) {
|
|
3120
2862
|
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
3121
2863
|
}
|
|
3122
2864
|
|
|
3123
2865
|
// src/runtime/supervise/driver-executor.ts
|
|
3124
2866
|
var driverRuntime = "driver";
|
|
3125
2867
|
var driverRole = "driver";
|
|
3126
|
-
function driverChild(name, driver, journal) {
|
|
3127
|
-
const spec = {
|
|
3128
|
-
profile: { name, metadata: { role: driverRole } },
|
|
3129
|
-
harness: null,
|
|
3130
|
-
driver,
|
|
3131
|
-
journal
|
|
3132
|
-
};
|
|
3133
|
-
return {
|
|
3134
|
-
name,
|
|
3135
|
-
executorSpec: spec,
|
|
3136
|
-
act() {
|
|
3137
|
-
throw new ValidationError(
|
|
3138
|
-
`driverChild: "${name}" was run directly; a driver child runs through its nested-scope executor`
|
|
3139
|
-
);
|
|
3140
|
-
}
|
|
3141
|
-
};
|
|
3142
|
-
}
|
|
3143
2868
|
function isDriverSpec(spec) {
|
|
3144
2869
|
const role = spec.profile.metadata?.role;
|
|
3145
2870
|
if (role !== driverRole) return false;
|
|
@@ -3311,12 +3036,64 @@ function isAgent(value) {
|
|
|
3311
3036
|
}
|
|
3312
3037
|
|
|
3313
3038
|
// src/runtime/supervise/runtime.ts
|
|
3314
|
-
import { spawn as
|
|
3039
|
+
import { spawn as spawn4 } from "child_process";
|
|
3315
3040
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3316
3041
|
import { estimateCost as estimateCost2, isModelPriced as isModelPriced2 } from "@tangle-network/agent-eval";
|
|
3317
3042
|
|
|
3318
3043
|
// src/runtime/router-client.ts
|
|
3319
3044
|
import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
|
|
3045
|
+
|
|
3046
|
+
// src/runtime/tool-loop.ts
|
|
3047
|
+
async function runBrainLoop(opts) {
|
|
3048
|
+
const maxTurns = opts.maxTurns ?? 4;
|
|
3049
|
+
const messages = [...opts.initialMessages];
|
|
3050
|
+
let toolCalls = 0;
|
|
3051
|
+
let lastText = "";
|
|
3052
|
+
const usage = { input: 0, output: 0 };
|
|
3053
|
+
const toolTrace = [];
|
|
3054
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
3055
|
+
if (opts.hooks?.stopBefore?.(turn)) break;
|
|
3056
|
+
await opts.hooks?.beforeTurn?.(turn, messages);
|
|
3057
|
+
const r = await opts.chat(messages, opts.tools);
|
|
3058
|
+
if (r.usage) {
|
|
3059
|
+
usage.input += r.usage.input;
|
|
3060
|
+
usage.output += r.usage.output;
|
|
3061
|
+
opts.hooks?.onUsage?.(r.usage);
|
|
3062
|
+
}
|
|
3063
|
+
if (r.content) lastText = r.content;
|
|
3064
|
+
if (r.toolCalls.length === 0)
|
|
3065
|
+
return { final: lastText, turns: turn, toolCalls, toolTrace, usage, messages };
|
|
3066
|
+
messages.push({
|
|
3067
|
+
role: "assistant",
|
|
3068
|
+
content: r.content ?? "",
|
|
3069
|
+
tool_calls: r.toolCalls.map((tc) => ({
|
|
3070
|
+
id: tc.id,
|
|
3071
|
+
type: "function",
|
|
3072
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
3073
|
+
}))
|
|
3074
|
+
});
|
|
3075
|
+
for (const tc of r.toolCalls) {
|
|
3076
|
+
toolCalls += 1;
|
|
3077
|
+
let args = {};
|
|
3078
|
+
try {
|
|
3079
|
+
args = JSON.parse(tc.arguments);
|
|
3080
|
+
} catch {
|
|
3081
|
+
messages.push({
|
|
3082
|
+
role: "tool",
|
|
3083
|
+
tool_call_id: tc.id,
|
|
3084
|
+
content: `error: arguments were not valid JSON: ${tc.arguments.slice(0, 200)}`
|
|
3085
|
+
});
|
|
3086
|
+
continue;
|
|
3087
|
+
}
|
|
3088
|
+
const out = await opts.execute(tc.name, args);
|
|
3089
|
+
messages.push({ role: "tool", tool_call_id: tc.id, content: out });
|
|
3090
|
+
toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
// src/runtime/router-client.ts
|
|
3320
3097
|
async function routerChatWithUsage(cfg, messages, opts) {
|
|
3321
3098
|
const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
3322
3099
|
const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
|
|
@@ -3394,56 +3171,25 @@ async function routerChatWithTools(cfg, messages, tools, opts) {
|
|
|
3394
3171
|
};
|
|
3395
3172
|
}
|
|
3396
3173
|
async function routerToolLoop(cfg, system, user, tools, execute, opts) {
|
|
3397
|
-
const
|
|
3398
|
-
const messages = opts?.initialMessages ? [...opts.initialMessages] : [
|
|
3174
|
+
const initialMessages = opts?.initialMessages ?? [
|
|
3399
3175
|
{ role: "system", content: system },
|
|
3400
3176
|
{ role: "user", content: user }
|
|
3401
3177
|
];
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
const usage = { input: 0, output: 0 };
|
|
3405
|
-
const toolTrace = [];
|
|
3406
|
-
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
3407
|
-
const r = await routerChatWithTools(cfg, messages, tools, {
|
|
3178
|
+
return runBrainLoop({
|
|
3179
|
+
chat: (messages, toolSpecs) => routerChatWithTools(cfg, messages, toolSpecs, {
|
|
3408
3180
|
...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
|
|
3409
3181
|
...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
|
|
3410
3182
|
...opts?.signal ? { signal: opts.signal } : {}
|
|
3411
|
-
})
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
content: r.content ?? "",
|
|
3422
|
-
tool_calls: r.toolCalls.map((tc) => ({
|
|
3423
|
-
id: tc.id,
|
|
3424
|
-
type: "function",
|
|
3425
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
3426
|
-
}))
|
|
3427
|
-
});
|
|
3428
|
-
for (const tc of r.toolCalls) {
|
|
3429
|
-
toolCalls += 1;
|
|
3430
|
-
let args = {};
|
|
3431
|
-
try {
|
|
3432
|
-
args = JSON.parse(tc.arguments);
|
|
3433
|
-
} catch {
|
|
3434
|
-
messages.push({
|
|
3435
|
-
role: "tool",
|
|
3436
|
-
tool_call_id: tc.id,
|
|
3437
|
-
content: `error: arguments were not valid JSON: ${tc.arguments.slice(0, 200)}`
|
|
3438
|
-
});
|
|
3439
|
-
continue;
|
|
3440
|
-
}
|
|
3441
|
-
const out = await execute(tc.name, args);
|
|
3442
|
-
messages.push({ role: "tool", tool_call_id: tc.id, content: out });
|
|
3443
|
-
toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
|
|
3444
|
-
}
|
|
3445
|
-
}
|
|
3446
|
-
return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
|
|
3183
|
+
}),
|
|
3184
|
+
tools,
|
|
3185
|
+
execute,
|
|
3186
|
+
initialMessages,
|
|
3187
|
+
maxTurns: opts?.maxTurns ?? 4
|
|
3188
|
+
});
|
|
3189
|
+
}
|
|
3190
|
+
function routerBrain(cfg, opts = {}) {
|
|
3191
|
+
const temperature = opts.temperature ?? 0.4;
|
|
3192
|
+
return (messages, tools) => routerChatWithTools(cfg, messages, tools, { temperature, toolChoice: "auto" });
|
|
3447
3193
|
}
|
|
3448
3194
|
|
|
3449
3195
|
// src/runtime/supervise/inbox.ts
|
|
@@ -3495,14 +3241,123 @@ ${lines.join("\n")}`;
|
|
|
3495
3241
|
import { randomUUID } from "crypto";
|
|
3496
3242
|
|
|
3497
3243
|
// src/mcp/worktree-harness.ts
|
|
3498
|
-
import { spawn as
|
|
3244
|
+
import { spawn as spawn3 } from "child_process";
|
|
3499
3245
|
|
|
3500
|
-
// src/mcp/
|
|
3246
|
+
// src/mcp/local-harness.ts
|
|
3501
3247
|
import { spawn } from "child_process";
|
|
3248
|
+
var HARNESS_INVOCATIONS = {
|
|
3249
|
+
claude: {
|
|
3250
|
+
command: "claude",
|
|
3251
|
+
buildArgs: (taskPrompt) => ["--headless", "-p", taskPrompt],
|
|
3252
|
+
modelArgs: (model) => ["-m", model]
|
|
3253
|
+
},
|
|
3254
|
+
codex: {
|
|
3255
|
+
command: "codex",
|
|
3256
|
+
buildArgs: (taskPrompt) => ["run", taskPrompt],
|
|
3257
|
+
modelArgs: (model) => ["-m", model]
|
|
3258
|
+
},
|
|
3259
|
+
opencode: {
|
|
3260
|
+
command: "opencode",
|
|
3261
|
+
buildArgs: (taskPrompt) => ["run", taskPrompt],
|
|
3262
|
+
modelArgs: (model) => ["-m", model]
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
3265
|
+
function harnessInvocation(harness, profile, taskPrompt) {
|
|
3266
|
+
const invocation = HARNESS_INVOCATIONS[harness];
|
|
3267
|
+
if (!invocation) {
|
|
3268
|
+
throw new Error(`harnessInvocation: unknown harness ${String(harness)}`);
|
|
3269
|
+
}
|
|
3270
|
+
const systemPrompt = profile.prompt?.systemPrompt;
|
|
3271
|
+
const composedPrompt = typeof systemPrompt === "string" && systemPrompt.trim().length > 0 ? `${systemPrompt}
|
|
3272
|
+
|
|
3273
|
+
${taskPrompt}` : taskPrompt;
|
|
3274
|
+
const args = invocation.buildArgs(composedPrompt);
|
|
3275
|
+
const model = profile.model?.default;
|
|
3276
|
+
if (typeof model === "string" && model.length > 0) {
|
|
3277
|
+
args.push(...invocation.modelArgs(model));
|
|
3278
|
+
}
|
|
3279
|
+
return { command: invocation.command, args };
|
|
3280
|
+
}
|
|
3281
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
3282
|
+
function runLocalHarness(options) {
|
|
3283
|
+
const { harness, cwd, taskPrompt } = options;
|
|
3284
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
3285
|
+
const env = options.env ?? process.env;
|
|
3286
|
+
const spawnImpl = options.spawn ?? spawn;
|
|
3287
|
+
const invocation = HARNESS_INVOCATIONS[harness];
|
|
3288
|
+
if (!invocation) {
|
|
3289
|
+
return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`));
|
|
3290
|
+
}
|
|
3291
|
+
const startedAt = Date.now();
|
|
3292
|
+
const command = options.invocation?.command ?? invocation.command;
|
|
3293
|
+
const args = options.invocation ? [...options.invocation.args] : invocation.buildArgs(taskPrompt);
|
|
3294
|
+
return new Promise((resolve, reject) => {
|
|
3295
|
+
let child;
|
|
3296
|
+
try {
|
|
3297
|
+
child = spawnImpl(command, args, { cwd, env, stdio: "pipe" });
|
|
3298
|
+
} catch (err) {
|
|
3299
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
3300
|
+
return;
|
|
3301
|
+
}
|
|
3302
|
+
child.stdin?.end();
|
|
3303
|
+
let stdout = "";
|
|
3304
|
+
let stderr = "";
|
|
3305
|
+
let timedOut = false;
|
|
3306
|
+
let settled = false;
|
|
3307
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
3308
|
+
timedOut = true;
|
|
3309
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
3310
|
+
}, timeoutMs) : null;
|
|
3311
|
+
if (timer && typeof timer.unref === "function") {
|
|
3312
|
+
;
|
|
3313
|
+
timer.unref();
|
|
3314
|
+
}
|
|
3315
|
+
const onAbort = () => {
|
|
3316
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
3317
|
+
};
|
|
3318
|
+
if (options.signal) {
|
|
3319
|
+
if (options.signal.aborted) onAbort();
|
|
3320
|
+
else options.signal.addEventListener("abort", onAbort, { once: true });
|
|
3321
|
+
}
|
|
3322
|
+
child.stdout?.on("data", (chunk) => {
|
|
3323
|
+
stdout += String(chunk);
|
|
3324
|
+
});
|
|
3325
|
+
child.stderr?.on("data", (chunk) => {
|
|
3326
|
+
stderr += String(chunk);
|
|
3327
|
+
});
|
|
3328
|
+
const finalize3 = (result) => {
|
|
3329
|
+
if (settled) return;
|
|
3330
|
+
settled = true;
|
|
3331
|
+
if (timer) clearTimeout(timer);
|
|
3332
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
3333
|
+
resolve(result);
|
|
3334
|
+
};
|
|
3335
|
+
child.on("error", (err) => {
|
|
3336
|
+
if (settled) return;
|
|
3337
|
+
settled = true;
|
|
3338
|
+
if (timer) clearTimeout(timer);
|
|
3339
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
3340
|
+
reject(err);
|
|
3341
|
+
});
|
|
3342
|
+
child.on("close", (code, signal) => {
|
|
3343
|
+
finalize3({
|
|
3344
|
+
exitCode: code,
|
|
3345
|
+
stdout,
|
|
3346
|
+
stderr,
|
|
3347
|
+
killedBySignal: signal,
|
|
3348
|
+
durationMs: Date.now() - startedAt,
|
|
3349
|
+
timedOut
|
|
3350
|
+
});
|
|
3351
|
+
});
|
|
3352
|
+
});
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
// src/mcp/worktree.ts
|
|
3356
|
+
import { spawn as spawn2 } from "child_process";
|
|
3502
3357
|
async function runGitAsync(args, cwd, runner) {
|
|
3503
3358
|
if (runner) return runner(args, { cwd });
|
|
3504
3359
|
return new Promise((resolve, reject) => {
|
|
3505
|
-
const proc =
|
|
3360
|
+
const proc = spawn2("git", args, { cwd, stdio: "pipe" });
|
|
3506
3361
|
let stdout = "";
|
|
3507
3362
|
let stderr = "";
|
|
3508
3363
|
proc.stdout?.on("data", (c) => {
|
|
@@ -3668,7 +3523,7 @@ async function runChecks(opts) {
|
|
|
3668
3523
|
}
|
|
3669
3524
|
function defaultRunCommand(opts) {
|
|
3670
3525
|
return new Promise((resolve, reject) => {
|
|
3671
|
-
const child =
|
|
3526
|
+
const child = spawn3("/bin/sh", ["-c", opts.command], {
|
|
3672
3527
|
cwd: opts.cwd,
|
|
3673
3528
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3674
3529
|
});
|
|
@@ -3791,7 +3646,7 @@ function contentRef(prefix, value) {
|
|
|
3791
3646
|
}
|
|
3792
3647
|
return `${prefix}:${(h >>> 0).toString(16).padStart(8, "0")}`;
|
|
3793
3648
|
}
|
|
3794
|
-
function
|
|
3649
|
+
function zeroSpend2() {
|
|
3795
3650
|
return { iterations: 0, tokens: zeroTokenUsage(), usd: 0, ms: 0 };
|
|
3796
3651
|
}
|
|
3797
3652
|
var routerInlineExecutor = (spec, ctx) => {
|
|
@@ -3963,15 +3818,24 @@ var routerToolsInlineExecutor = (spec, ctx) => {
|
|
|
3963
3818
|
const toolName = tc?.function?.name ?? "";
|
|
3964
3819
|
let result;
|
|
3965
3820
|
let status = "ok";
|
|
3821
|
+
const toolStartedAt = Date.now();
|
|
3966
3822
|
try {
|
|
3967
3823
|
result = await seam.executeToolCall(toolName, args, task);
|
|
3968
3824
|
} catch (e) {
|
|
3969
3825
|
status = "error";
|
|
3970
3826
|
result = `error: ${e instanceof Error ? e.message : String(e)}`;
|
|
3971
3827
|
}
|
|
3828
|
+
const toolEndedAt = Date.now();
|
|
3972
3829
|
messages.push({ role: "tool", tool_call_id: id, content: result });
|
|
3973
3830
|
try {
|
|
3974
|
-
seam.onToolStep?.({
|
|
3831
|
+
seam.onToolStep?.({
|
|
3832
|
+
toolName,
|
|
3833
|
+
args,
|
|
3834
|
+
status,
|
|
3835
|
+
startedAt: toolStartedAt,
|
|
3836
|
+
endedAt: toolEndedAt,
|
|
3837
|
+
durationMs: toolEndedAt - toolStartedAt
|
|
3838
|
+
});
|
|
3975
3839
|
} catch {
|
|
3976
3840
|
}
|
|
3977
3841
|
}
|
|
@@ -4151,7 +4015,7 @@ var cliExecutor = (_spec, ctx) => {
|
|
|
4151
4015
|
};
|
|
4152
4016
|
async function* streamCliLeaf(args) {
|
|
4153
4017
|
const prompt = taskToPrompt(args.task);
|
|
4154
|
-
const proc =
|
|
4018
|
+
const proc = spawn4(args.seam.bin, args.seam.args ?? [], {
|
|
4155
4019
|
...args.seam.cwd ? { cwd: args.seam.cwd } : {},
|
|
4156
4020
|
env: { ...process.env, ...args.seam.env ?? {} },
|
|
4157
4021
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4188,7 +4052,7 @@ async function* streamCliLeaf(args) {
|
|
|
4188
4052
|
);
|
|
4189
4053
|
}
|
|
4190
4054
|
const out = { content: chunks.join("") };
|
|
4191
|
-
args.onArtifact({ outRef: contentRef("cli", out), out, spent:
|
|
4055
|
+
args.onArtifact({ outRef: contentRef("cli", out), out, spent: zeroSpend2() });
|
|
4192
4056
|
yield { kind: "iteration" };
|
|
4193
4057
|
}
|
|
4194
4058
|
function killWithGrace(proc, grace) {
|
|
@@ -4803,40 +4667,6 @@ function createSupervisor() {
|
|
|
4803
4667
|
return { run, attach };
|
|
4804
4668
|
}
|
|
4805
4669
|
var rootControls = /* @__PURE__ */ new WeakMap();
|
|
4806
|
-
function createRootHandle() {
|
|
4807
|
-
let binding;
|
|
4808
|
-
const handle = {
|
|
4809
|
-
view() {
|
|
4810
|
-
if (!binding) {
|
|
4811
|
-
throw new RuntimeRunStateError(
|
|
4812
|
-
"RootHandle.view: handle is not bound to a live run (attach it before run, read after run starts)"
|
|
4813
|
-
);
|
|
4814
|
-
}
|
|
4815
|
-
return binding.scope.view;
|
|
4816
|
-
},
|
|
4817
|
-
signal(msg) {
|
|
4818
|
-
if (!binding) {
|
|
4819
|
-
throw new RuntimeRunStateError("RootHandle.signal: handle is not bound to a live run");
|
|
4820
|
-
}
|
|
4821
|
-
binding.signal(msg);
|
|
4822
|
-
},
|
|
4823
|
-
abort(reason) {
|
|
4824
|
-
if (!binding) {
|
|
4825
|
-
throw new RuntimeRunStateError("RootHandle.abort: handle is not bound to a live run");
|
|
4826
|
-
}
|
|
4827
|
-
binding.cascadeAbort(reason ?? "root handle aborted");
|
|
4828
|
-
}
|
|
4829
|
-
};
|
|
4830
|
-
rootControls.set(handle, {
|
|
4831
|
-
bind(b) {
|
|
4832
|
-
binding = b;
|
|
4833
|
-
},
|
|
4834
|
-
unbind() {
|
|
4835
|
-
binding = void 0;
|
|
4836
|
-
}
|
|
4837
|
-
});
|
|
4838
|
-
return handle;
|
|
4839
|
-
}
|
|
4840
4670
|
function pushRootSignal(cascadeAbort) {
|
|
4841
4671
|
return (msg) => {
|
|
4842
4672
|
if (msg.kind === "cancel") cascadeAbort(msg.reason ?? "root signal: cancel");
|
|
@@ -5105,12 +4935,12 @@ async function trajectoryReport(journal, blobs, root, options = {}) {
|
|
|
5105
4935
|
label: ev.label,
|
|
5106
4936
|
runtime: ev.runtime,
|
|
5107
4937
|
status: "pending",
|
|
5108
|
-
ownSpend:
|
|
4938
|
+
ownSpend: zeroSpend3(),
|
|
5109
4939
|
children: []
|
|
5110
4940
|
});
|
|
5111
4941
|
}
|
|
5112
4942
|
for (const ev of closes) {
|
|
5113
|
-
const node =
|
|
4943
|
+
const node = requireNode(nodes, ev.id, root);
|
|
5114
4944
|
if (ev.kind === "cancelled") {
|
|
5115
4945
|
node.status = "cancelled";
|
|
5116
4946
|
continue;
|
|
@@ -5122,7 +4952,7 @@ async function trajectoryReport(journal, blobs, root, options = {}) {
|
|
|
5122
4952
|
}
|
|
5123
4953
|
for (const ev of events) {
|
|
5124
4954
|
if (ev.kind !== "metered") continue;
|
|
5125
|
-
const node =
|
|
4955
|
+
const node = requireNode(nodes, ev.id, root);
|
|
5126
4956
|
node.ownSpend = addNodeSpend(node.ownSpend, ev.spend);
|
|
5127
4957
|
}
|
|
5128
4958
|
if (!nodes.has(root)) {
|
|
@@ -5132,7 +4962,7 @@ async function trajectoryReport(journal, blobs, root, options = {}) {
|
|
|
5132
4962
|
}
|
|
5133
4963
|
for (const ev of spawns) {
|
|
5134
4964
|
if (ev.parent === void 0) continue;
|
|
5135
|
-
|
|
4965
|
+
requireNode(nodes, ev.parent, root).children.push(ev.id);
|
|
5136
4966
|
}
|
|
5137
4967
|
const rolledUp = rollUpSpend(nodes, root);
|
|
5138
4968
|
if (options.withOutputs) {
|
|
@@ -5180,7 +5010,7 @@ function rollUpSpend(nodes, root) {
|
|
|
5180
5010
|
while (stack.length > 0) {
|
|
5181
5011
|
const frame = stack.pop();
|
|
5182
5012
|
if (frame === void 0) continue;
|
|
5183
|
-
const node =
|
|
5013
|
+
const node = requireNode(nodes, frame.id, root);
|
|
5184
5014
|
if (!frame.expanded) {
|
|
5185
5015
|
stack.push({ id: frame.id, expanded: true });
|
|
5186
5016
|
for (const child of node.children) stack.push({ id: child, expanded: false });
|
|
@@ -5229,7 +5059,7 @@ function countStatuses(reported) {
|
|
|
5229
5059
|
for (const node of reported) counts[node.status] += 1;
|
|
5230
5060
|
return counts;
|
|
5231
5061
|
}
|
|
5232
|
-
function
|
|
5062
|
+
function zeroSpend3() {
|
|
5233
5063
|
return { iterations: 0, tokens: zeroTokenUsage(), usd: 0, ms: 0 };
|
|
5234
5064
|
}
|
|
5235
5065
|
function addNodeSpend(a, b) {
|
|
@@ -5289,7 +5119,7 @@ function isNode(node) {
|
|
|
5289
5119
|
function bySeq(a, b) {
|
|
5290
5120
|
return a.seq - b.seq;
|
|
5291
5121
|
}
|
|
5292
|
-
function
|
|
5122
|
+
function requireNode(nodes, id, root) {
|
|
5293
5123
|
const node = nodes.get(id);
|
|
5294
5124
|
if (!node) {
|
|
5295
5125
|
throw new Error(
|
|
@@ -5694,7 +5524,7 @@ var perChild = (innerTurns) => ({
|
|
|
5694
5524
|
maxIterations: innerTurns + 1,
|
|
5695
5525
|
maxTokens: 1e6
|
|
5696
5526
|
});
|
|
5697
|
-
function
|
|
5527
|
+
function depthStrategy(surface, task, opts, cfg) {
|
|
5698
5528
|
const innerTurns = opts.innerTurns ?? 4;
|
|
5699
5529
|
let pendingSteer;
|
|
5700
5530
|
return {
|
|
@@ -5757,7 +5587,7 @@ Address each with the tools, verify they took, then continue.`;
|
|
|
5757
5587
|
}
|
|
5758
5588
|
};
|
|
5759
5589
|
}
|
|
5760
|
-
function
|
|
5590
|
+
function breadthStrategy(_surface, task, opts, cfg) {
|
|
5761
5591
|
const innerTurns = opts.innerTurns ?? 4;
|
|
5762
5592
|
return {
|
|
5763
5593
|
name: "breadth",
|
|
@@ -5800,11 +5630,11 @@ function breadthDriver(_surface, task, opts, cfg) {
|
|
|
5800
5630
|
}
|
|
5801
5631
|
var sample = {
|
|
5802
5632
|
name: "sample",
|
|
5803
|
-
driver: (surface, task, opts, budget) =>
|
|
5633
|
+
driver: (surface, task, opts, budget) => breadthStrategy(surface, task, opts, { width: budget })
|
|
5804
5634
|
};
|
|
5805
5635
|
var refine = {
|
|
5806
5636
|
name: "refine",
|
|
5807
|
-
driver: (surface, task, opts, budget) =>
|
|
5637
|
+
driver: (surface, task, opts, budget) => depthStrategy(surface, task, opts, { maxShots: budget })
|
|
5808
5638
|
};
|
|
5809
5639
|
function defineStrategy(name, run) {
|
|
5810
5640
|
return {
|
|
@@ -6935,6 +6765,9 @@ ${summary}`,
|
|
|
6935
6765
|
};
|
|
6936
6766
|
}
|
|
6937
6767
|
|
|
6768
|
+
// src/runtime/supervise/authoring.ts
|
|
6769
|
+
import { computeFindingId, makeFinding as makeFinding2 } from "@tangle-network/agent-eval";
|
|
6770
|
+
|
|
6938
6771
|
// src/runtime/supervise/completion-gate.ts
|
|
6939
6772
|
function gateOnDeliverable(inner, deliverable) {
|
|
6940
6773
|
let gated;
|
|
@@ -6987,13 +6820,13 @@ function asAuthoredProfile(raw) {
|
|
|
6987
6820
|
...typeof p.model === "string" ? { model: p.model } : {}
|
|
6988
6821
|
};
|
|
6989
6822
|
}
|
|
6990
|
-
function
|
|
6823
|
+
function supervisorInstructions(opts) {
|
|
6991
6824
|
return [
|
|
6992
6825
|
"You are a SUPERVISOR. You do NOT do the work yourself \u2014 your job is to DESIGN and DRIVE specialist worker agents.",
|
|
6993
6826
|
"",
|
|
6994
6827
|
"For the task you are given:",
|
|
6995
6828
|
"1. DECOMPOSE it into the smallest set of sub-tasks a single focused worker can each deliver.",
|
|
6996
|
-
"2. For EACH sub-task, AUTHOR a worker by calling
|
|
6829
|
+
"2. For EACH sub-task, AUTHOR a worker by calling spawn_agent with a COMPLETE `profile`:",
|
|
6997
6830
|
" \u2022 name: a short id for the worker.",
|
|
6998
6831
|
' \u2022 systemPrompt: rich, specific instructions for THIS sub-task \u2014 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.',
|
|
6999
6832
|
" \u2022 model: the model best suited to this sub-task (omit to use the default).",
|
|
@@ -7044,6 +6877,93 @@ function authoredWorker(profile, opts) {
|
|
|
7044
6877
|
};
|
|
7045
6878
|
return { name: profile.name, act: async () => "", executorSpec: spec };
|
|
7046
6879
|
}
|
|
6880
|
+
var defaultProfileRichnessThresholds = {
|
|
6881
|
+
minSystemPromptChars: 600,
|
|
6882
|
+
minSystemPromptLines: 6
|
|
6883
|
+
};
|
|
6884
|
+
function resolveSystemPrompt(profile) {
|
|
6885
|
+
const pr = profile.prompt;
|
|
6886
|
+
if (typeof pr === "string") return pr;
|
|
6887
|
+
if (pr && typeof pr === "object") {
|
|
6888
|
+
const o = pr;
|
|
6889
|
+
if (typeof o.systemPrompt === "string") return o.systemPrompt;
|
|
6890
|
+
if (typeof o.system === "string") return o.system;
|
|
6891
|
+
}
|
|
6892
|
+
return "";
|
|
6893
|
+
}
|
|
6894
|
+
function assessAuthoredProfile(profile, opts) {
|
|
6895
|
+
const th = { ...defaultProfileRichnessThresholds, ...opts?.thresholds ?? {} };
|
|
6896
|
+
const systemPrompt = resolveSystemPrompt(profile);
|
|
6897
|
+
const trimmed = systemPrompt.trim();
|
|
6898
|
+
const systemPromptChars = trimmed.length;
|
|
6899
|
+
const systemPromptLines = trimmed ? trimmed.split("\n").filter((l) => l.trim().length > 0).length : 0;
|
|
6900
|
+
const sentenceCount = trimmed ? (trimmed.match(/[.!?](\s|$)/g) ?? []).length || (trimmed ? 1 : 0) : 0;
|
|
6901
|
+
const hasDescription = typeof profile.description === "string" && profile.description.trim().length > 0;
|
|
6902
|
+
const tools = profile.tools;
|
|
6903
|
+
const hasTools = !!tools && Object.keys(tools).length > 0;
|
|
6904
|
+
const skills = profile.resources?.skills;
|
|
6905
|
+
const hasSkills = Array.isArray(skills) && skills.length > 0;
|
|
6906
|
+
const mcp = profile.mcp;
|
|
6907
|
+
const hasMcp = !!mcp && Object.keys(mcp).length > 0;
|
|
6908
|
+
const subagents = profile.subagents;
|
|
6909
|
+
const hasSubagents = !!subagents && Object.keys(subagents).length > 0;
|
|
6910
|
+
const reasons = [];
|
|
6911
|
+
const promptThin = systemPromptChars < th.minSystemPromptChars || systemPromptLines < th.minSystemPromptLines;
|
|
6912
|
+
if (promptThin)
|
|
6913
|
+
reasons.push(
|
|
6914
|
+
`system prompt is thin (${systemPromptChars} chars, ${systemPromptLines} lines; need \u2265${th.minSystemPromptChars} chars and \u2265${th.minSystemPromptLines} lines)`
|
|
6915
|
+
);
|
|
6916
|
+
if (!hasTools)
|
|
6917
|
+
reasons.push("no tools granted (a worker can only act through the tools you grant it)");
|
|
6918
|
+
if (!hasSkills) reasons.push("no skills attached (no reusable how-to notes injected)");
|
|
6919
|
+
if (opts?.needsMcp && !hasMcp) reasons.push("no MCP server, but the task needs data/tool access");
|
|
6920
|
+
const signals = [!promptThin, hasTools, hasSkills, hasDescription, opts?.needsMcp ? hasMcp : true];
|
|
6921
|
+
const richness = signals.filter(Boolean).length / signals.length;
|
|
6922
|
+
const thin = promptThin || !hasTools && !hasSkills && !hasMcp;
|
|
6923
|
+
return {
|
|
6924
|
+
name: profile.name ?? "worker",
|
|
6925
|
+
systemPrompt,
|
|
6926
|
+
systemPromptChars,
|
|
6927
|
+
systemPromptLines,
|
|
6928
|
+
sentenceCount,
|
|
6929
|
+
hasDescription,
|
|
6930
|
+
hasTools,
|
|
6931
|
+
hasSkills,
|
|
6932
|
+
hasMcp,
|
|
6933
|
+
hasSubagents,
|
|
6934
|
+
richness,
|
|
6935
|
+
thin,
|
|
6936
|
+
reasons
|
|
6937
|
+
};
|
|
6938
|
+
}
|
|
6939
|
+
function profileRichnessFinding(richness, opts) {
|
|
6940
|
+
const analyst_id = opts?.analystId ?? "profile-richness";
|
|
6941
|
+
const subject = richness.name;
|
|
6942
|
+
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)}%).`;
|
|
6943
|
+
const severity = richness.thin ? richness.richness < 0.25 ? "high" : "medium" : "info";
|
|
6944
|
+
return makeFinding2({
|
|
6945
|
+
analyst_id,
|
|
6946
|
+
severity,
|
|
6947
|
+
area: "profile-quality",
|
|
6948
|
+
claim,
|
|
6949
|
+
subject,
|
|
6950
|
+
confidence: 0.9,
|
|
6951
|
+
evidence_refs: [
|
|
6952
|
+
{
|
|
6953
|
+
kind: "metric",
|
|
6954
|
+
uri: `profile:${subject}`,
|
|
6955
|
+
excerpt: `chars=${richness.systemPromptChars} lines=${richness.systemPromptLines} tools=${richness.hasTools} skills=${richness.hasSkills} mcp=${richness.hasMcp} richness=${richness.richness.toFixed(2)}`
|
|
6956
|
+
}
|
|
6957
|
+
],
|
|
6958
|
+
...richness.thin ? { recommended_action: `Re-author "${richness.name}" with: ${richness.reasons.join("; ")}.` } : {},
|
|
6959
|
+
id_basis: computeFindingId({
|
|
6960
|
+
analyst_id,
|
|
6961
|
+
area: "profile-quality",
|
|
6962
|
+
subject,
|
|
6963
|
+
claim: `richness:${richness.thin ? "thin" : "rich"}`
|
|
6964
|
+
})
|
|
6965
|
+
});
|
|
6966
|
+
}
|
|
7047
6967
|
|
|
7048
6968
|
// src/runtime/supervise/event-bus.ts
|
|
7049
6969
|
function createEventBus(now = Date.now) {
|
|
@@ -7102,7 +7022,19 @@ function createEventBus(now = Date.now) {
|
|
|
7102
7022
|
}
|
|
7103
7023
|
|
|
7104
7024
|
// src/mcp/tools/coordination.ts
|
|
7105
|
-
var
|
|
7025
|
+
var coordinationVerbNames = [
|
|
7026
|
+
"spawn_agent",
|
|
7027
|
+
"observe_agent",
|
|
7028
|
+
"steer_agent",
|
|
7029
|
+
"await_event",
|
|
7030
|
+
"list_questions",
|
|
7031
|
+
"answer_question",
|
|
7032
|
+
"ask_parent",
|
|
7033
|
+
"stop",
|
|
7034
|
+
"list_analysts",
|
|
7035
|
+
"run_analyst"
|
|
7036
|
+
];
|
|
7037
|
+
var idArg = { type: "string", description: "The workerId returned by spawn_agent." };
|
|
7106
7038
|
function createCoordinationTools(opts) {
|
|
7107
7039
|
let stopped = false;
|
|
7108
7040
|
let reason;
|
|
@@ -7126,6 +7058,28 @@ function createCoordinationTools(opts) {
|
|
|
7126
7058
|
throw new Error("coordination tools: arguments must be an object");
|
|
7127
7059
|
return raw;
|
|
7128
7060
|
};
|
|
7061
|
+
const mergeBudget = (base, raw) => {
|
|
7062
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
7063
|
+
throw new Error('coordination tools: "budget" must be an object');
|
|
7064
|
+
const o = raw;
|
|
7065
|
+
const field = (name) => {
|
|
7066
|
+
const v = o[name];
|
|
7067
|
+
if (v === void 0) return void 0;
|
|
7068
|
+
if (typeof v !== "number" || !Number.isFinite(v))
|
|
7069
|
+
throw new Error(`coordination tools: "budget.${name}" must be a finite number`);
|
|
7070
|
+
return v;
|
|
7071
|
+
};
|
|
7072
|
+
const maxIterations = field("maxIterations");
|
|
7073
|
+
const maxTokens = field("maxTokens");
|
|
7074
|
+
const maxUsd = field("maxUsd");
|
|
7075
|
+
const deadlineMs = field("deadlineMs");
|
|
7076
|
+
return {
|
|
7077
|
+
maxIterations: maxIterations ?? base.maxIterations,
|
|
7078
|
+
maxTokens: maxTokens ?? base.maxTokens,
|
|
7079
|
+
...(maxUsd ?? base.maxUsd) === void 0 ? {} : { maxUsd: maxUsd ?? base.maxUsd },
|
|
7080
|
+
...(deadlineMs ?? base.deadlineMs) === void 0 ? {} : { deadlineMs: deadlineMs ?? base.deadlineMs }
|
|
7081
|
+
};
|
|
7082
|
+
};
|
|
7129
7083
|
const level = (v) => {
|
|
7130
7084
|
if (v === "worker" || v === "driver" || v === "loop") return v;
|
|
7131
7085
|
throw new Error('coordination tools: "level" must be worker, driver, or loop');
|
|
@@ -7244,29 +7198,40 @@ function createCoordinationTools(opts) {
|
|
|
7244
7198
|
};
|
|
7245
7199
|
const tools = [
|
|
7246
7200
|
{
|
|
7247
|
-
name: "
|
|
7248
|
-
description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed.",
|
|
7201
|
+
name: "spawn_agent",
|
|
7202
|
+
description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed. Pass an optional `budget` (per-field) to give a hard sub-task more than the default \u2014 it merges over the per-worker default; the conserved pool is still the hard fence.",
|
|
7249
7203
|
inputSchema: {
|
|
7250
7204
|
type: "object",
|
|
7251
7205
|
properties: {
|
|
7252
7206
|
profile: { description: "The worker/driver profile to run." },
|
|
7253
7207
|
task: { description: "The task the worker should perform." },
|
|
7254
|
-
label: { type: "string", description: "Optional trace label." }
|
|
7208
|
+
label: { type: "string", description: "Optional trace label." },
|
|
7209
|
+
budget: {
|
|
7210
|
+
type: "object",
|
|
7211
|
+
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.",
|
|
7212
|
+
properties: {
|
|
7213
|
+
maxIterations: { type: "number" },
|
|
7214
|
+
maxTokens: { type: "number" },
|
|
7215
|
+
maxUsd: { type: "number" },
|
|
7216
|
+
deadlineMs: { type: "number" }
|
|
7217
|
+
}
|
|
7218
|
+
}
|
|
7255
7219
|
},
|
|
7256
7220
|
required: ["profile", "task"]
|
|
7257
7221
|
},
|
|
7258
7222
|
handler: (raw) => {
|
|
7259
7223
|
const a = obj2(raw);
|
|
7260
7224
|
const agent = opts.makeWorkerAgent(a.profile);
|
|
7225
|
+
const budget = a.budget === void 0 ? opts.perWorker : mergeBudget(opts.perWorker, a.budget);
|
|
7261
7226
|
const res = opts.scope.spawn(agent, a.task, {
|
|
7262
|
-
budget
|
|
7227
|
+
budget,
|
|
7263
7228
|
label: typeof a.label === "string" ? a.label : "worker"
|
|
7264
7229
|
});
|
|
7265
7230
|
return Promise.resolve(res.ok ? { workerId: res.handle.id } : { error: res.reason });
|
|
7266
7231
|
}
|
|
7267
7232
|
},
|
|
7268
7233
|
{
|
|
7269
|
-
name: "
|
|
7234
|
+
name: "observe_agent",
|
|
7270
7235
|
description: "Inspect a worker status, spend, and settled output artifact when available.",
|
|
7271
7236
|
inputSchema: { type: "object", properties: { workerId: idArg }, required: ["workerId"] },
|
|
7272
7237
|
handler: async (raw) => {
|
|
@@ -7283,7 +7248,7 @@ function createCoordinationTools(opts) {
|
|
|
7283
7248
|
}
|
|
7284
7249
|
},
|
|
7285
7250
|
{
|
|
7286
|
-
name: "
|
|
7251
|
+
name: "steer_agent",
|
|
7287
7252
|
description: "Send a message DOWN to a still-LIVE worker (parent\u2192child): a new instruction, a course correction, or a continuation. The worker drains it at its next step boundary \u2014 and before it may settle, so it cannot finish while a message it never read is pending. A worker that already settled is gone (returns delivered:false) \u2014 spawn a fresh one instead.",
|
|
7288
7253
|
inputSchema: {
|
|
7289
7254
|
type: "object",
|
|
@@ -7504,13 +7469,26 @@ function deadlinePassed(scope, now) {
|
|
|
7504
7469
|
const b = scope.budget;
|
|
7505
7470
|
return b.deadlineMs > 0 && now() >= b.deadlineMs;
|
|
7506
7471
|
}
|
|
7507
|
-
function
|
|
7508
|
-
if (typeof opts.
|
|
7509
|
-
throw new ValidationError("
|
|
7472
|
+
function driverAgent(opts) {
|
|
7473
|
+
if (typeof opts.brain !== "function") {
|
|
7474
|
+
throw new ValidationError("driverAgent: opts.brain must be a function");
|
|
7475
|
+
}
|
|
7476
|
+
if ((opts.extraTools?.length ?? 0) > 0 && typeof opts.executeExtraTool !== "function") {
|
|
7477
|
+
throw new ValidationError(
|
|
7478
|
+
"driverAgent: extraTools requires executeExtraTool (how to run a work-tool call)"
|
|
7479
|
+
);
|
|
7480
|
+
}
|
|
7481
|
+
const reserved = new Set(coordinationVerbNames);
|
|
7482
|
+
for (const t of opts.extraTools ?? []) {
|
|
7483
|
+
if (reserved.has(t.name)) {
|
|
7484
|
+
throw new ValidationError(
|
|
7485
|
+
`driverAgent: extra work tool "${t.name}" collides with a coordination verb`
|
|
7486
|
+
);
|
|
7487
|
+
}
|
|
7510
7488
|
}
|
|
7511
7489
|
if (opts.maxTurns !== void 0 && opts.maxTurns < 0) {
|
|
7512
7490
|
throw new ValidationError(
|
|
7513
|
-
"
|
|
7491
|
+
"driverAgent: maxTurns must be >= 0 (0 lifts the turn cap; bounds become the conserved pool + deadline + abort)"
|
|
7514
7492
|
);
|
|
7515
7493
|
}
|
|
7516
7494
|
const maxTurns = opts.maxTurns === 0 ? runawayTripwireTurns : opts.maxTurns ?? 16;
|
|
@@ -7525,24 +7503,23 @@ function coordinationDriverAgent(opts) {
|
|
|
7525
7503
|
perWorker: opts.perWorker
|
|
7526
7504
|
});
|
|
7527
7505
|
const byName = new Map(coord.tools.map((t) => [t.name, t]));
|
|
7528
|
-
const toolSpecs =
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7506
|
+
const toolSpecs = [
|
|
7507
|
+
...coord.tools.map((t) => ({
|
|
7508
|
+
type: "function",
|
|
7509
|
+
function: { name: t.name, description: t.description, parameters: t.inputSchema }
|
|
7510
|
+
})),
|
|
7511
|
+
// Work tools the driver calls DIRECTLY — so it can ACT, not only delegate.
|
|
7512
|
+
...(opts.extraTools ?? []).map((t) => ({
|
|
7513
|
+
type: "function",
|
|
7514
|
+
function: { name: t.name, description: t.description, parameters: t.parameters }
|
|
7515
|
+
}))
|
|
7516
|
+
];
|
|
7533
7517
|
const system = typeof opts.systemPrompt === "function" ? opts.systemPrompt(task) : opts.systemPrompt;
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
if (poolStarved(scope, opts.perWorker) || deadlinePassed(scope, now)) break;
|
|
7538
|
-
const res = await opts.chat.next({ system, messages, tools: toolSpecs });
|
|
7539
|
-
const calls = res.toolCalls ?? [];
|
|
7518
|
+
let turn = 0;
|
|
7519
|
+
const chat = async (messages, tools) => {
|
|
7520
|
+
const res = await opts.brain(messages, tools);
|
|
7540
7521
|
if (res.usage || res.costUsd !== void 0) {
|
|
7541
7522
|
const turnSpend = {
|
|
7542
|
-
// iterations:0 — the conserved iteration channel (`maxIterations`) budgets CHILD rounds,
|
|
7543
|
-
// not driver turns; counting turns there would conflate the two AND make a driver arm's
|
|
7544
|
-
// iteration count diverge from a blind arm's. The driver is bounded by maxTurns + the
|
|
7545
|
-
// token/usd pool; its turn COUNT stays observable via the per-turn `agent.turn` events.
|
|
7546
7523
|
iterations: 0,
|
|
7547
7524
|
tokens: { input: res.usage?.input ?? 0, output: res.usage?.output ?? 0 },
|
|
7548
7525
|
usd: res.costUsd ?? 0,
|
|
@@ -7552,28 +7529,47 @@ function coordinationDriverAgent(opts) {
|
|
|
7552
7529
|
kind: "driver-inference",
|
|
7553
7530
|
driver: opts.name,
|
|
7554
7531
|
turn,
|
|
7555
|
-
toolCalls:
|
|
7532
|
+
toolCalls: (res.toolCalls ?? []).map((c) => c.name)
|
|
7556
7533
|
});
|
|
7557
7534
|
}
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
}
|
|
7535
|
+
turn += 1;
|
|
7536
|
+
return res;
|
|
7537
|
+
};
|
|
7538
|
+
await runBrainLoop({
|
|
7539
|
+
chat,
|
|
7540
|
+
tools: toolSpecs,
|
|
7541
|
+
execute: async (name, args) => {
|
|
7542
|
+
if (opts.executeExtraTool) {
|
|
7543
|
+
const worked = await runExtraTool(opts.executeExtraTool, name, args);
|
|
7544
|
+
if (worked !== null && worked !== void 0) return worked;
|
|
7545
|
+
}
|
|
7546
|
+
const tool = byName.get(name);
|
|
7547
|
+
const result = tool ? await runTool(tool, args) : { error: `unknown tool: ${name}` };
|
|
7548
|
+
return safeJson(result);
|
|
7549
|
+
},
|
|
7550
|
+
initialMessages: [
|
|
7551
|
+
{ role: "system", content: system },
|
|
7552
|
+
{ role: "user", content: stringifyTask(task) }
|
|
7553
|
+
],
|
|
7554
|
+
maxTurns,
|
|
7555
|
+
// The conserved-pool + deadline + external-stop bound (what maxTurns=0 relies on): a driver
|
|
7556
|
+
// that can no longer spawn (pool starved) or has run past the deadline stops here instead of
|
|
7557
|
+
// burning turns. Checked before each inference turn.
|
|
7558
|
+
hooks: {
|
|
7559
|
+
stopBefore: () => coord.isStopped() || scope.signal.aborted || poolStarved(scope, opts.perWorker) || deadlinePassed(scope, now)
|
|
7571
7560
|
}
|
|
7572
|
-
}
|
|
7561
|
+
});
|
|
7573
7562
|
return finalize2(coord, opts.blobs);
|
|
7574
7563
|
}
|
|
7575
7564
|
};
|
|
7576
7565
|
}
|
|
7566
|
+
async function runExtraTool(execute, name, args) {
|
|
7567
|
+
try {
|
|
7568
|
+
return await execute(name, args);
|
|
7569
|
+
} catch (e) {
|
|
7570
|
+
return `error: ${e instanceof Error ? e.message : String(e)}`;
|
|
7571
|
+
}
|
|
7572
|
+
}
|
|
7577
7573
|
async function runTool(tool, args) {
|
|
7578
7574
|
try {
|
|
7579
7575
|
return await tool.handler(args);
|
|
@@ -7581,13 +7577,16 @@ async function runTool(tool, args) {
|
|
|
7581
7577
|
return { error: e instanceof Error ? e.message : String(e) };
|
|
7582
7578
|
}
|
|
7583
7579
|
}
|
|
7584
|
-
async function
|
|
7585
|
-
const delivered =
|
|
7580
|
+
async function finalizeBestDelivered(settled, blobs) {
|
|
7581
|
+
const delivered = settled.filter((w) => w.status === "done" && w.valid === true);
|
|
7586
7582
|
if (delivered.length === 0) return void 0;
|
|
7587
7583
|
let best = delivered[0];
|
|
7588
7584
|
for (const w of delivered) if ((w.score ?? 0) > (best.score ?? 0)) best = w;
|
|
7589
7585
|
return best.outRef ? await blobs.get(best.outRef) : void 0;
|
|
7590
7586
|
}
|
|
7587
|
+
async function finalize2(coord, blobs) {
|
|
7588
|
+
return finalizeBestDelivered(coord.settled(), blobs);
|
|
7589
|
+
}
|
|
7591
7590
|
function stringifyTask(task) {
|
|
7592
7591
|
return typeof task === "string" ? task : safeJson(task);
|
|
7593
7592
|
}
|
|
@@ -8549,7 +8548,7 @@ function createDetachedTurnTrace(options) {
|
|
|
8549
8548
|
function detachedRunningPhase(elapsedMs) {
|
|
8550
8549
|
return elapsedMs === void 0 ? "detached-running" : `detached-running ${Math.round(elapsedMs / 1e3)}s`;
|
|
8551
8550
|
}
|
|
8552
|
-
function
|
|
8551
|
+
function createDetachedTurnResumeDriver(options) {
|
|
8553
8552
|
const cancelHooked = /* @__PURE__ */ new Set();
|
|
8554
8553
|
return {
|
|
8555
8554
|
intervalMs: options.intervalMs ?? DEFAULT_TICK_INTERVAL_MS,
|
|
@@ -9811,6 +9810,16 @@ function watchTrace(source, opts = {}) {
|
|
|
9811
9810
|
});
|
|
9812
9811
|
}
|
|
9813
9812
|
|
|
9813
|
+
// src/runtime/supervise/model-policy.ts
|
|
9814
|
+
function assertModelAllowed(model, allowed) {
|
|
9815
|
+
if (!allowed || model === void 0) return;
|
|
9816
|
+
if (!allowed.includes(model)) {
|
|
9817
|
+
throw new ConfigError(
|
|
9818
|
+
`model ${JSON.stringify(model)} is not in the allowed set ${JSON.stringify([...allowed])}`
|
|
9819
|
+
);
|
|
9820
|
+
}
|
|
9821
|
+
}
|
|
9822
|
+
|
|
9814
9823
|
// src/runtime/supervise/patch-checks.ts
|
|
9815
9824
|
var DEFAULT_MAX_DIFF_LINES = 400;
|
|
9816
9825
|
var secretPathPattern = /(^|\/)(\.env(\.|$)|.*\.(pem|key|p12|pfx|keystore|wallet)|id_rsa|id_ed25519|secrets?\.json|credentials?\.json)$/i;
|
|
@@ -9835,12 +9844,6 @@ function countDiffLines(patch) {
|
|
|
9835
9844
|
}
|
|
9836
9845
|
return count;
|
|
9837
9846
|
}
|
|
9838
|
-
function isNonEmptyPatch(patch) {
|
|
9839
|
-
return touchedPathsFromPatch(patch).length > 0 && patch.trim().length > 0;
|
|
9840
|
-
}
|
|
9841
|
-
function touchesSecretPath(patch) {
|
|
9842
|
-
return touchedPathsFromPatch(patch).filter((p) => secretPathPattern.test(p));
|
|
9843
|
-
}
|
|
9844
9847
|
function runCoderChecks(input, constraints = {}) {
|
|
9845
9848
|
const maxDiff = constraints.maxDiffLines ?? DEFAULT_MAX_DIFF_LINES;
|
|
9846
9849
|
const forbidden = constraints.forbiddenPaths ?? [];
|
|
@@ -9927,72 +9930,132 @@ function signalPass(value, required) {
|
|
|
9927
9930
|
return !required;
|
|
9928
9931
|
}
|
|
9929
9932
|
|
|
9930
|
-
// src/runtime/supervise/
|
|
9931
|
-
function
|
|
9932
|
-
const
|
|
9933
|
+
// src/runtime/supervise/run-context.ts
|
|
9934
|
+
function createInMemoryRunContext(opts = {}) {
|
|
9935
|
+
const base = createExecutorRegistry();
|
|
9933
9936
|
return {
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
...messages.map(toOpenAI)
|
|
9938
|
-
];
|
|
9939
|
-
const oaTools = tools.map((t) => ({
|
|
9940
|
-
type: "function",
|
|
9941
|
-
function: { name: t.name, description: t.description, parameters: t.parameters }
|
|
9942
|
-
}));
|
|
9943
|
-
const r = await routerChatWithTools(c, oa, oaTools, { temperature, toolChoice: "auto" });
|
|
9944
|
-
return {
|
|
9945
|
-
...r.content ? { content: r.content } : {},
|
|
9946
|
-
// Forward the router's REAL usage + cost so the driver meters its own inference against the
|
|
9947
|
-
// conserved pool (the integrity hole: these were dropped on the floor before).
|
|
9948
|
-
...r.usage ? { usage: r.usage } : {},
|
|
9949
|
-
...typeof r.costUsd === "number" ? { costUsd: r.costUsd } : {},
|
|
9950
|
-
toolCalls: r.toolCalls.map((tc) => ({
|
|
9951
|
-
id: tc.id,
|
|
9952
|
-
name: tc.name,
|
|
9953
|
-
arguments: safeParse(tc.arguments)
|
|
9954
|
-
}))
|
|
9955
|
-
};
|
|
9956
|
-
}
|
|
9937
|
+
journal: new InMemorySpawnJournal(),
|
|
9938
|
+
blobs: new InMemoryResultBlobStore(),
|
|
9939
|
+
executors: opts.withDriver ? withDriverExecutor(base) : base
|
|
9957
9940
|
};
|
|
9958
9941
|
}
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
9942
|
+
|
|
9943
|
+
// src/runtime/supervise/supervisor-agent.ts
|
|
9944
|
+
function supervisorAgent(profile, deps) {
|
|
9945
|
+
const name = profile.name ?? "supervisor";
|
|
9946
|
+
const systemPrompt = profile.systemPrompt ?? "";
|
|
9947
|
+
const harness = profile.harness ?? null;
|
|
9948
|
+
if (harness === null) {
|
|
9949
|
+
const brain = deps.brain ?? routerBrainFromProfile(profile, deps);
|
|
9950
|
+
return driverAgent({
|
|
9951
|
+
name,
|
|
9952
|
+
brain,
|
|
9953
|
+
blobs: deps.blobs,
|
|
9954
|
+
makeWorkerAgent: deps.makeWorkerAgent,
|
|
9955
|
+
perWorker: deps.perWorker,
|
|
9956
|
+
systemPrompt,
|
|
9957
|
+
...deps.extraTools ? { extraTools: deps.extraTools } : {},
|
|
9958
|
+
...deps.executeExtraTool ? { executeExtraTool: deps.executeExtraTool } : {},
|
|
9959
|
+
...deps.maxTurns !== void 0 ? { maxTurns: deps.maxTurns } : {}
|
|
9960
|
+
});
|
|
9970
9961
|
}
|
|
9971
|
-
|
|
9972
|
-
|
|
9962
|
+
const driveHarness = deps.driveHarness;
|
|
9963
|
+
if (!driveHarness) {
|
|
9964
|
+
throw new ValidationError(
|
|
9965
|
+
`supervisorAgent: profile.harness="${harness}" needs deps.driveHarness (how to run the harness with the coordination MCP mounted)`
|
|
9966
|
+
);
|
|
9973
9967
|
}
|
|
9974
|
-
return {
|
|
9968
|
+
return {
|
|
9969
|
+
name,
|
|
9970
|
+
async act(task, scope) {
|
|
9971
|
+
const mcp = await serveCoordinationMcp({
|
|
9972
|
+
scope,
|
|
9973
|
+
blobs: deps.blobs,
|
|
9974
|
+
makeWorkerAgent: deps.makeWorkerAgent,
|
|
9975
|
+
perWorker: deps.perWorker
|
|
9976
|
+
});
|
|
9977
|
+
try {
|
|
9978
|
+
await driveHarness({ profile, task, scope, coordinationMcpUrl: mcp.url });
|
|
9979
|
+
return await finalizeBestDelivered(mcp.settled(), deps.blobs);
|
|
9980
|
+
} finally {
|
|
9981
|
+
await mcp.close();
|
|
9982
|
+
}
|
|
9983
|
+
}
|
|
9984
|
+
};
|
|
9975
9985
|
}
|
|
9976
|
-
function
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9986
|
+
function routerBrainFromProfile(profile, deps) {
|
|
9987
|
+
if (!deps.router) {
|
|
9988
|
+
throw new ValidationError(
|
|
9989
|
+
"supervisorAgent: a router-brained supervisor (harness null) needs deps.router (or deps.brain)"
|
|
9990
|
+
);
|
|
9981
9991
|
}
|
|
9992
|
+
return routerBrain({ ...deps.router, model: profile.model ?? deps.router.model });
|
|
9982
9993
|
}
|
|
9983
9994
|
|
|
9984
|
-
// src/runtime/supervise/
|
|
9985
|
-
function
|
|
9986
|
-
|
|
9995
|
+
// src/runtime/supervise/supervise.ts
|
|
9996
|
+
function workerFromBackend(backend, deliverable) {
|
|
9997
|
+
return (rawProfile) => {
|
|
9998
|
+
const p = rawProfile ?? {};
|
|
9999
|
+
const name = typeof p.name === "string" && p.name.length > 0 ? p.name : "worker";
|
|
10000
|
+
const spec = { profile: rawProfile, harness: null };
|
|
10001
|
+
const ctx = { signal: new AbortController().signal, seams: {} };
|
|
10002
|
+
const built = createExecutor(backend)(spec, ctx);
|
|
10003
|
+
const executor = deliverable ? gateOnDeliverable(built, deliverable) : built;
|
|
10004
|
+
return { name, act: async () => "", executorSpec: { ...spec, executor } };
|
|
10005
|
+
};
|
|
10006
|
+
}
|
|
10007
|
+
function defaultPerWorker(budget) {
|
|
9987
10008
|
return {
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
executors: opts.withDriver ? withDriverExecutor(base) : base
|
|
10009
|
+
maxIterations: budget.maxIterations,
|
|
10010
|
+
maxTokens: Math.max(1, Math.floor(budget.maxTokens / 4))
|
|
9991
10011
|
};
|
|
9992
10012
|
}
|
|
10013
|
+
function supervise(profile, task, opts) {
|
|
10014
|
+
const backendModel = opts.backend?.model;
|
|
10015
|
+
assertModelAllowed(opts.router?.model, opts.allowedModels);
|
|
10016
|
+
assertModelAllowed(profile.model, opts.allowedModels);
|
|
10017
|
+
assertModelAllowed(
|
|
10018
|
+
typeof backendModel === "string" ? backendModel : void 0,
|
|
10019
|
+
opts.allowedModels
|
|
10020
|
+
);
|
|
10021
|
+
const ctx = createInMemoryRunContext({ withDriver: true });
|
|
10022
|
+
const blobs = opts.blobs ?? ctx.blobs;
|
|
10023
|
+
const perWorker = opts.perWorker ?? defaultPerWorker(opts.budget);
|
|
10024
|
+
let makeWorkerAgent = opts.makeWorkerAgent;
|
|
10025
|
+
if (!makeWorkerAgent) {
|
|
10026
|
+
if (!opts.backend) {
|
|
10027
|
+
throw new ValidationError(
|
|
10028
|
+
"supervise: provide opts.backend (where workers run) or opts.makeWorkerAgent"
|
|
10029
|
+
);
|
|
10030
|
+
}
|
|
10031
|
+
makeWorkerAgent = workerFromBackend(opts.backend, opts.deliverable);
|
|
10032
|
+
}
|
|
10033
|
+
const agent = supervisorAgent(profile, {
|
|
10034
|
+
blobs,
|
|
10035
|
+
makeWorkerAgent,
|
|
10036
|
+
perWorker,
|
|
10037
|
+
...opts.router ? { router: opts.router } : {},
|
|
10038
|
+
...opts.brain ? { brain: opts.brain } : {},
|
|
10039
|
+
...opts.driveHarness ? { driveHarness: opts.driveHarness } : {},
|
|
10040
|
+
...opts.extraTools ? { extraTools: opts.extraTools } : {},
|
|
10041
|
+
...opts.executeExtraTool ? { executeExtraTool: opts.executeExtraTool } : {},
|
|
10042
|
+
...opts.maxTurns !== void 0 ? { maxTurns: opts.maxTurns } : {}
|
|
10043
|
+
});
|
|
10044
|
+
return createSupervisor().run(agent, task, {
|
|
10045
|
+
budget: opts.budget,
|
|
10046
|
+
runId: opts.runId ?? "supervise",
|
|
10047
|
+
journal: ctx.journal,
|
|
10048
|
+
blobs,
|
|
10049
|
+
executors: ctx.executors,
|
|
10050
|
+
maxDepth: opts.maxDepth ?? 8,
|
|
10051
|
+
...opts.now ? { now: opts.now } : {}
|
|
10052
|
+
});
|
|
10053
|
+
}
|
|
9993
10054
|
|
|
9994
10055
|
// src/runtime/supervise/trace-source.ts
|
|
9995
10056
|
function toToolSpan(input, runId, seq, at) {
|
|
10057
|
+
const startedAt = typeof input.startedAt === "number" ? input.startedAt : at;
|
|
10058
|
+
const endedAt = typeof input.endedAt === "number" ? input.endedAt : startedAt;
|
|
9996
10059
|
return {
|
|
9997
10060
|
spanId: `${runId}-t${seq}`,
|
|
9998
10061
|
runId,
|
|
@@ -10001,8 +10064,8 @@ function toToolSpan(input, runId, seq, at) {
|
|
|
10001
10064
|
toolName: input.toolName,
|
|
10002
10065
|
args: input.args,
|
|
10003
10066
|
status: input.status ?? "ok",
|
|
10004
|
-
startedAt
|
|
10005
|
-
endedAt
|
|
10067
|
+
startedAt,
|
|
10068
|
+
endedAt,
|
|
10006
10069
|
...input.result !== void 0 ? { result: input.result } : {}
|
|
10007
10070
|
};
|
|
10008
10071
|
}
|
|
@@ -10042,7 +10105,7 @@ var decodeOpenAiPart = (p) => {
|
|
|
10042
10105
|
const rawArgs = fn?.arguments ?? p.arguments;
|
|
10043
10106
|
return {
|
|
10044
10107
|
toolName: name,
|
|
10045
|
-
args: typeof rawArgs === "string" ?
|
|
10108
|
+
args: typeof rawArgs === "string" ? safeParse(rawArgs) : rawArgs ?? {},
|
|
10046
10109
|
...str(p.id) ? { callId: p.id } : str(fn?.id) ? { callId: fn?.id } : {}
|
|
10047
10110
|
};
|
|
10048
10111
|
};
|
|
@@ -10066,7 +10129,7 @@ function decodeToolPart(part, harness) {
|
|
|
10066
10129
|
}
|
|
10067
10130
|
return void 0;
|
|
10068
10131
|
}
|
|
10069
|
-
function
|
|
10132
|
+
function safeParse(s) {
|
|
10070
10133
|
try {
|
|
10071
10134
|
return JSON.parse(s);
|
|
10072
10135
|
} catch {
|
|
@@ -10512,13 +10575,11 @@ function tail(s) {
|
|
|
10512
10575
|
}
|
|
10513
10576
|
|
|
10514
10577
|
export {
|
|
10578
|
+
runLocalHarness,
|
|
10579
|
+
assertModelAllowed,
|
|
10515
10580
|
contentAddress,
|
|
10516
10581
|
InMemoryResultBlobStore,
|
|
10517
|
-
FileResultBlobStore,
|
|
10518
10582
|
InMemorySpawnJournal,
|
|
10519
|
-
FileSpawnJournal,
|
|
10520
|
-
replaySpawnTree,
|
|
10521
|
-
materializeTreeView,
|
|
10522
10583
|
anytimeReport,
|
|
10523
10584
|
renderAnytimeTable,
|
|
10524
10585
|
defaultAuditorInstruction,
|
|
@@ -10541,7 +10602,6 @@ export {
|
|
|
10541
10602
|
probeSandboxCapabilities,
|
|
10542
10603
|
createSandboxLineage,
|
|
10543
10604
|
runLoop,
|
|
10544
|
-
describeSandboxPlacement,
|
|
10545
10605
|
createSandboxForSpec,
|
|
10546
10606
|
defaultSelectWinner,
|
|
10547
10607
|
loopDispatch,
|
|
@@ -10550,7 +10610,6 @@ export {
|
|
|
10550
10610
|
createScopeAnalyst,
|
|
10551
10611
|
registryScopeAnalyst,
|
|
10552
10612
|
buildSteerContext,
|
|
10553
|
-
nestedScopeSeamKey,
|
|
10554
10613
|
createScope,
|
|
10555
10614
|
settledToIteration,
|
|
10556
10615
|
selectValidWinner,
|
|
@@ -10564,14 +10623,10 @@ export {
|
|
|
10564
10623
|
InMemoryCorpus,
|
|
10565
10624
|
FileCorpus,
|
|
10566
10625
|
renderCorpusToInstructions,
|
|
10567
|
-
driverRuntime,
|
|
10568
|
-
driverChild,
|
|
10569
|
-
isDriverSpec,
|
|
10570
|
-
driverExecutorFactory,
|
|
10571
|
-
withDriverExecutor,
|
|
10572
10626
|
routerChatWithUsage,
|
|
10573
10627
|
routerChatWithTools,
|
|
10574
10628
|
routerToolLoop,
|
|
10629
|
+
routerBrain,
|
|
10575
10630
|
createInbox,
|
|
10576
10631
|
createWorktree,
|
|
10577
10632
|
captureWorktreeDiff,
|
|
@@ -10584,7 +10639,6 @@ export {
|
|
|
10584
10639
|
spendFromUsageEvents,
|
|
10585
10640
|
createBudgetPool,
|
|
10586
10641
|
createSupervisor,
|
|
10587
|
-
createRootHandle,
|
|
10588
10642
|
createShapeRegistry,
|
|
10589
10643
|
builtinShapes,
|
|
10590
10644
|
registerShape,
|
|
@@ -10593,8 +10647,8 @@ export {
|
|
|
10593
10647
|
trajectoryReport,
|
|
10594
10648
|
equalKOnCost,
|
|
10595
10649
|
promotionGate,
|
|
10596
|
-
|
|
10597
|
-
|
|
10650
|
+
depthStrategy,
|
|
10651
|
+
breadthStrategy,
|
|
10598
10652
|
sample,
|
|
10599
10653
|
refine,
|
|
10600
10654
|
defineStrategy,
|
|
@@ -10613,11 +10667,15 @@ export {
|
|
|
10613
10667
|
runStrategyEvolution,
|
|
10614
10668
|
gateOnDeliverable,
|
|
10615
10669
|
asAuthoredProfile,
|
|
10616
|
-
|
|
10670
|
+
supervisorInstructions,
|
|
10617
10671
|
authoredWorker,
|
|
10672
|
+
defaultProfileRichnessThresholds,
|
|
10673
|
+
assessAuthoredProfile,
|
|
10674
|
+
profileRichnessFinding,
|
|
10618
10675
|
createEventBus,
|
|
10619
10676
|
createCoordinationTools,
|
|
10620
|
-
|
|
10677
|
+
driverAgent,
|
|
10678
|
+
finalizeBestDelivered,
|
|
10621
10679
|
InMemoryFeedbackStore,
|
|
10622
10680
|
eventToSnapshot,
|
|
10623
10681
|
DelegationStateCorruptError,
|
|
@@ -10636,7 +10694,7 @@ export {
|
|
|
10636
10694
|
parseDetachedSessionRef,
|
|
10637
10695
|
detachedTurnEvents,
|
|
10638
10696
|
runDetachedTurn,
|
|
10639
|
-
|
|
10697
|
+
createDetachedTurnResumeDriver,
|
|
10640
10698
|
DELEGATE_CODE_TOOL_NAME,
|
|
10641
10699
|
DELEGATE_CODE_DESCRIPTION,
|
|
10642
10700
|
DELEGATE_CODE_INPUT_SCHEMA,
|
|
@@ -10672,22 +10730,14 @@ export {
|
|
|
10672
10730
|
serveCoordinationMcp,
|
|
10673
10731
|
defaultToolDetectors,
|
|
10674
10732
|
watchTrace,
|
|
10675
|
-
touchedPathsFromPatch,
|
|
10676
|
-
countDiffLines,
|
|
10677
|
-
isNonEmptyPatch,
|
|
10678
|
-
touchesSecretPath,
|
|
10679
10733
|
runCoderChecks,
|
|
10680
10734
|
patchDelivered,
|
|
10681
|
-
routerDriverChat,
|
|
10682
10735
|
createInMemoryRunContext,
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
decodeOpenAiPart,
|
|
10687
|
-
toolPartDecoders,
|
|
10736
|
+
supervisorAgent,
|
|
10737
|
+
workerFromBackend,
|
|
10738
|
+
supervise,
|
|
10688
10739
|
decodeToolPart,
|
|
10689
10740
|
createPushTraceSource,
|
|
10690
|
-
createPartsTraceSource,
|
|
10691
10741
|
sandboxSessionTraceSource,
|
|
10692
10742
|
analyzeTrace,
|
|
10693
10743
|
worktreeFanout,
|
|
@@ -10696,6 +10746,8 @@ export {
|
|
|
10696
10746
|
localShell,
|
|
10697
10747
|
gitWorkspace,
|
|
10698
10748
|
jjWorkspace,
|
|
10699
|
-
runInWorkspace
|
|
10749
|
+
runInWorkspace,
|
|
10750
|
+
computeFindingId2 as computeFindingId,
|
|
10751
|
+
makeFinding3 as makeFinding
|
|
10700
10752
|
};
|
|
10701
|
-
//# sourceMappingURL=chunk-
|
|
10753
|
+
//# sourceMappingURL=chunk-YFOPWG74.js.map
|