@wrongstack/core 0.1.10 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-6KPqsFx6.d.ts → agent-bridge-DmBiCipY.d.ts} +1 -1
- package/dist/{compactor-B4mQZXf2.d.ts → compactor-DSl2FK7a.d.ts} +1 -1
- package/dist/{config-BU9f_5yH.d.ts → config-DXrqb41m.d.ts} +1 -1
- package/dist/{context-BmM2xGUZ.d.ts → context-u0bryklF.d.ts} +8 -0
- package/dist/coordination/index.d.ts +210 -12
- package/dist/coordination/index.js +941 -67
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +18 -18
- package/dist/defaults/index.js +953 -41
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-BMNaEFZl.d.ts → events-B6Q03pTu.d.ts} +73 -1
- package/dist/execution/index.d.ts +11 -11
- package/dist/index.d.ts +61 -28
- package/dist/index.js +1077 -48
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-Dzgg4x1w.d.ts → mcp-servers-BA1Ofmfj.d.ts} +3 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/{multi-agent-fmkRHtof.d.ts → multi-agent-BDfkxL5C.d.ts} +71 -3
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-DBjaoXFq.d.ts → path-resolver-Crkt8wTQ.d.ts} +2 -2
- package/dist/{plugin-DJk6LL8B.d.ts → plugin-CoYYZKdn.d.ts} +19 -6
- package/dist/{renderer-rk_1Swwc.d.ts → renderer-0A2ZEtca.d.ts} +1 -1
- package/dist/sdd/index.d.ts +3 -3
- package/dist/{secret-scrubber-CicHLN4G.d.ts → secret-scrubber-3TLUkiCV.d.ts} +1 -1
- package/dist/{secret-scrubber-DF88luOe.d.ts → secret-scrubber-CwYliRWd.d.ts} +1 -1
- package/dist/security/index.d.ts +20 -4
- package/dist/security/index.js +13 -1
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-BbJqiEP4.d.ts → selector-BRqzvugb.d.ts} +1 -1
- package/dist/{session-reader-Drq8RvJu.d.ts → session-reader-C3x96CDR.d.ts} +1 -1
- package/dist/{skill-DhfSizKv.d.ts → skill-Bx8jxznf.d.ts} +1 -1
- package/dist/storage/index.d.ts +164 -6
- package/dist/storage/index.js +273 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/{system-prompt-BC_8ypCG.d.ts → system-prompt-CG9jU5-5.d.ts} +9 -1
- package/dist/{tool-executor-CpuJPYm9.d.ts → tool-executor-CYdZdtno.d.ts} +4 -4
- package/dist/types/index.d.ts +15 -15
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3116,11 +3116,11 @@ function validateAgainstSchema(value, schema) {
|
|
|
3116
3116
|
walk2(value, schema, "", errors);
|
|
3117
3117
|
return { ok: errors.length === 0, errors };
|
|
3118
3118
|
}
|
|
3119
|
-
function walk2(value, schema,
|
|
3119
|
+
function walk2(value, schema, path18, errors) {
|
|
3120
3120
|
if (schema.enum !== void 0) {
|
|
3121
3121
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
3122
3122
|
errors.push({
|
|
3123
|
-
path:
|
|
3123
|
+
path: path18 || "<root>",
|
|
3124
3124
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
3125
3125
|
});
|
|
3126
3126
|
return;
|
|
@@ -3129,7 +3129,7 @@ function walk2(value, schema, path17, errors) {
|
|
|
3129
3129
|
if (typeof schema.type === "string") {
|
|
3130
3130
|
if (!checkType(value, schema.type)) {
|
|
3131
3131
|
errors.push({
|
|
3132
|
-
path:
|
|
3132
|
+
path: path18 || "<root>",
|
|
3133
3133
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
3134
3134
|
});
|
|
3135
3135
|
return;
|
|
@@ -3139,19 +3139,19 @@ function walk2(value, schema, path17, errors) {
|
|
|
3139
3139
|
const obj = value;
|
|
3140
3140
|
for (const req of schema.required ?? []) {
|
|
3141
3141
|
if (!(req in obj)) {
|
|
3142
|
-
errors.push({ path: joinPath(
|
|
3142
|
+
errors.push({ path: joinPath(path18, req), message: "required property missing" });
|
|
3143
3143
|
}
|
|
3144
3144
|
}
|
|
3145
3145
|
if (schema.properties) {
|
|
3146
3146
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
3147
3147
|
if (key in obj) {
|
|
3148
|
-
walk2(obj[key], subSchema, joinPath(
|
|
3148
|
+
walk2(obj[key], subSchema, joinPath(path18, key), errors);
|
|
3149
3149
|
}
|
|
3150
3150
|
}
|
|
3151
3151
|
}
|
|
3152
3152
|
}
|
|
3153
3153
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
3154
|
-
value.forEach((item, i) => walk2(item, schema.items, `${
|
|
3154
|
+
value.forEach((item, i) => walk2(item, schema.items, `${path18}[${i}]`, errors));
|
|
3155
3155
|
}
|
|
3156
3156
|
}
|
|
3157
3157
|
function checkType(value, type) {
|
|
@@ -3439,6 +3439,12 @@ var FileSessionWriter = class {
|
|
|
3439
3439
|
tokenIn = 0;
|
|
3440
3440
|
tokenOut = 0;
|
|
3441
3441
|
filePath;
|
|
3442
|
+
/** Public accessor for the JSONL path — required by SessionWriter so
|
|
3443
|
+
* observability surfaces (`/fleet log`, FleetPanel) can locate the
|
|
3444
|
+
* transcript without recomputing the path from session metadata. */
|
|
3445
|
+
get transcriptPath() {
|
|
3446
|
+
return this.filePath || void 0;
|
|
3447
|
+
}
|
|
3442
3448
|
initDone = false;
|
|
3443
3449
|
resumed;
|
|
3444
3450
|
appendFailCount = 0;
|
|
@@ -4368,6 +4374,272 @@ var SessionAnalyzer = class {
|
|
|
4368
4374
|
return last - first;
|
|
4369
4375
|
}
|
|
4370
4376
|
};
|
|
4377
|
+
async function loadTodosCheckpoint(filePath) {
|
|
4378
|
+
let raw;
|
|
4379
|
+
try {
|
|
4380
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
4381
|
+
} catch {
|
|
4382
|
+
return null;
|
|
4383
|
+
}
|
|
4384
|
+
try {
|
|
4385
|
+
const parsed = JSON.parse(raw);
|
|
4386
|
+
if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) return null;
|
|
4387
|
+
return parsed.todos.filter(
|
|
4388
|
+
(t2) => !!t2 && typeof t2.id === "string" && typeof t2.content === "string" && typeof t2.status === "string"
|
|
4389
|
+
);
|
|
4390
|
+
} catch {
|
|
4391
|
+
return null;
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
async function saveTodosCheckpoint(filePath, sessionId, todos) {
|
|
4395
|
+
const payload = {
|
|
4396
|
+
version: 1,
|
|
4397
|
+
sessionId,
|
|
4398
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4399
|
+
todos: [...todos]
|
|
4400
|
+
};
|
|
4401
|
+
try {
|
|
4402
|
+
await atomicWrite(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
|
|
4403
|
+
} catch (err) {
|
|
4404
|
+
console.warn(
|
|
4405
|
+
"[todos-checkpoint] save failed:",
|
|
4406
|
+
err instanceof Error ? err.message : String(err)
|
|
4407
|
+
);
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
4411
|
+
let timer = null;
|
|
4412
|
+
let pending = null;
|
|
4413
|
+
const flush = () => {
|
|
4414
|
+
timer = null;
|
|
4415
|
+
if (pending) {
|
|
4416
|
+
void saveTodosCheckpoint(filePath, sessionId, pending);
|
|
4417
|
+
pending = null;
|
|
4418
|
+
}
|
|
4419
|
+
};
|
|
4420
|
+
const unsubscribe = state.onChange((change) => {
|
|
4421
|
+
if (change.kind !== "todos_replaced") return;
|
|
4422
|
+
pending = change.todos;
|
|
4423
|
+
if (timer) clearTimeout(timer);
|
|
4424
|
+
timer = setTimeout(flush, 150);
|
|
4425
|
+
});
|
|
4426
|
+
return () => {
|
|
4427
|
+
unsubscribe();
|
|
4428
|
+
if (timer) {
|
|
4429
|
+
clearTimeout(timer);
|
|
4430
|
+
flush();
|
|
4431
|
+
}
|
|
4432
|
+
};
|
|
4433
|
+
}
|
|
4434
|
+
async function loadPlan(filePath) {
|
|
4435
|
+
let raw;
|
|
4436
|
+
try {
|
|
4437
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
4438
|
+
} catch {
|
|
4439
|
+
return null;
|
|
4440
|
+
}
|
|
4441
|
+
try {
|
|
4442
|
+
const parsed = JSON.parse(raw);
|
|
4443
|
+
if (parsed?.version !== 1 || !Array.isArray(parsed.items)) return null;
|
|
4444
|
+
return parsed;
|
|
4445
|
+
} catch {
|
|
4446
|
+
return null;
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
async function savePlan(filePath, plan) {
|
|
4450
|
+
try {
|
|
4451
|
+
await atomicWrite(filePath, JSON.stringify(plan, null, 2), { mode: 384 });
|
|
4452
|
+
} catch (err) {
|
|
4453
|
+
console.warn(
|
|
4454
|
+
"[plan-store] save failed:",
|
|
4455
|
+
err instanceof Error ? err.message : String(err)
|
|
4456
|
+
);
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
function emptyPlan(sessionId, title) {
|
|
4460
|
+
return {
|
|
4461
|
+
version: 1,
|
|
4462
|
+
sessionId,
|
|
4463
|
+
title,
|
|
4464
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4465
|
+
items: []
|
|
4466
|
+
};
|
|
4467
|
+
}
|
|
4468
|
+
function addPlanItem(plan, title, details) {
|
|
4469
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4470
|
+
const item = {
|
|
4471
|
+
id: `plan_${Date.now()}_${randomUUID().slice(0, 6)}`,
|
|
4472
|
+
title,
|
|
4473
|
+
details,
|
|
4474
|
+
status: "open",
|
|
4475
|
+
createdAt: now,
|
|
4476
|
+
updatedAt: now
|
|
4477
|
+
};
|
|
4478
|
+
return {
|
|
4479
|
+
plan: { ...plan, items: [...plan.items, item], updatedAt: now },
|
|
4480
|
+
item
|
|
4481
|
+
};
|
|
4482
|
+
}
|
|
4483
|
+
function removePlanItem(plan, idOrIndex) {
|
|
4484
|
+
const idx = matchIndex(plan, idOrIndex);
|
|
4485
|
+
if (idx === -1) return plan;
|
|
4486
|
+
return {
|
|
4487
|
+
...plan,
|
|
4488
|
+
items: plan.items.filter((_, i) => i !== idx),
|
|
4489
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4490
|
+
};
|
|
4491
|
+
}
|
|
4492
|
+
function setPlanItemStatus(plan, idOrIndex, status) {
|
|
4493
|
+
const idx = matchIndex(plan, idOrIndex);
|
|
4494
|
+
if (idx === -1) return plan;
|
|
4495
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4496
|
+
const items = plan.items.map(
|
|
4497
|
+
(it, i) => i === idx ? { ...it, status, updatedAt: now } : it
|
|
4498
|
+
);
|
|
4499
|
+
return { ...plan, items, updatedAt: now };
|
|
4500
|
+
}
|
|
4501
|
+
function clearPlan(plan) {
|
|
4502
|
+
return { ...plan, items: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4503
|
+
}
|
|
4504
|
+
function formatPlan(plan) {
|
|
4505
|
+
if (plan.items.length === 0) return "Plan is empty.";
|
|
4506
|
+
const lines = [];
|
|
4507
|
+
if (plan.title) lines.push(`# ${plan.title}`);
|
|
4508
|
+
plan.items.forEach((it, i) => {
|
|
4509
|
+
const mark = it.status === "done" ? "[x]" : it.status === "in_progress" ? "[~]" : "[ ]";
|
|
4510
|
+
lines.push(`${i + 1}. ${mark} ${it.title}`);
|
|
4511
|
+
if (it.details) {
|
|
4512
|
+
for (const line of it.details.split("\n")) lines.push(` ${line}`);
|
|
4513
|
+
}
|
|
4514
|
+
});
|
|
4515
|
+
return lines.join("\n");
|
|
4516
|
+
}
|
|
4517
|
+
function matchIndex(plan, idOrIndex) {
|
|
4518
|
+
const asNum = Number.parseInt(idOrIndex, 10);
|
|
4519
|
+
if (!Number.isNaN(asNum) && asNum >= 1 && asNum <= plan.items.length) return asNum - 1;
|
|
4520
|
+
const byId = plan.items.findIndex((it) => it.id === idOrIndex);
|
|
4521
|
+
if (byId !== -1) return byId;
|
|
4522
|
+
const lower = idOrIndex.toLowerCase();
|
|
4523
|
+
return plan.items.findIndex((it) => it.title.toLowerCase().includes(lower));
|
|
4524
|
+
}
|
|
4525
|
+
function attachPlanCheckpoint(_state, _filePath, _sessionId) {
|
|
4526
|
+
return () => void 0;
|
|
4527
|
+
}
|
|
4528
|
+
async function loadDirectorState(filePath) {
|
|
4529
|
+
let raw;
|
|
4530
|
+
try {
|
|
4531
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
4532
|
+
} catch {
|
|
4533
|
+
return null;
|
|
4534
|
+
}
|
|
4535
|
+
try {
|
|
4536
|
+
const parsed = JSON.parse(raw);
|
|
4537
|
+
if (parsed?.version !== 1) return null;
|
|
4538
|
+
return parsed;
|
|
4539
|
+
} catch {
|
|
4540
|
+
return null;
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
var DirectorStateCheckpoint = class {
|
|
4544
|
+
snapshot;
|
|
4545
|
+
filePath;
|
|
4546
|
+
timer = null;
|
|
4547
|
+
debounceMs;
|
|
4548
|
+
writing = false;
|
|
4549
|
+
rewriteRequested = false;
|
|
4550
|
+
constructor(filePath, init, debounceMs = 250) {
|
|
4551
|
+
this.filePath = filePath;
|
|
4552
|
+
this.debounceMs = debounceMs;
|
|
4553
|
+
this.snapshot = {
|
|
4554
|
+
version: 1,
|
|
4555
|
+
directorRunId: init.directorRunId,
|
|
4556
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4557
|
+
spawnCount: 0,
|
|
4558
|
+
maxSpawns: init.maxSpawns,
|
|
4559
|
+
spawnDepth: init.spawnDepth,
|
|
4560
|
+
maxSpawnDepth: init.maxSpawnDepth,
|
|
4561
|
+
subagents: [],
|
|
4562
|
+
tasks: []
|
|
4563
|
+
};
|
|
4564
|
+
}
|
|
4565
|
+
current() {
|
|
4566
|
+
return this.snapshot;
|
|
4567
|
+
}
|
|
4568
|
+
recordSpawn(sub, spawnCount) {
|
|
4569
|
+
this.snapshot = {
|
|
4570
|
+
...this.snapshot,
|
|
4571
|
+
spawnCount,
|
|
4572
|
+
subagents: [...this.snapshot.subagents.filter((s) => s.id !== sub.id), sub]
|
|
4573
|
+
};
|
|
4574
|
+
this.bumpUpdatedAt();
|
|
4575
|
+
this.schedule();
|
|
4576
|
+
}
|
|
4577
|
+
recordTaskAssigned(task) {
|
|
4578
|
+
const exists = this.snapshot.tasks.some((t2) => t2.taskId === task.taskId);
|
|
4579
|
+
this.snapshot = {
|
|
4580
|
+
...this.snapshot,
|
|
4581
|
+
tasks: exists ? this.snapshot.tasks.map((t2) => t2.taskId === task.taskId ? { ...t2, ...task } : t2) : [...this.snapshot.tasks, task]
|
|
4582
|
+
};
|
|
4583
|
+
this.bumpUpdatedAt();
|
|
4584
|
+
this.schedule();
|
|
4585
|
+
}
|
|
4586
|
+
recordTaskStatus(taskId, patch) {
|
|
4587
|
+
this.snapshot = {
|
|
4588
|
+
...this.snapshot,
|
|
4589
|
+
tasks: this.snapshot.tasks.map(
|
|
4590
|
+
(t2) => t2.taskId === taskId ? { ...t2, ...patch } : t2
|
|
4591
|
+
)
|
|
4592
|
+
};
|
|
4593
|
+
this.bumpUpdatedAt();
|
|
4594
|
+
this.schedule();
|
|
4595
|
+
}
|
|
4596
|
+
setUsage(usage) {
|
|
4597
|
+
this.snapshot = { ...this.snapshot, usage };
|
|
4598
|
+
this.bumpUpdatedAt();
|
|
4599
|
+
this.schedule();
|
|
4600
|
+
}
|
|
4601
|
+
/** Force a synchronous flush — used by Director.shutdown(). */
|
|
4602
|
+
async flush() {
|
|
4603
|
+
if (this.timer) {
|
|
4604
|
+
clearTimeout(this.timer);
|
|
4605
|
+
this.timer = null;
|
|
4606
|
+
}
|
|
4607
|
+
await this.persist();
|
|
4608
|
+
}
|
|
4609
|
+
bumpUpdatedAt() {
|
|
4610
|
+
this.snapshot = { ...this.snapshot, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4611
|
+
}
|
|
4612
|
+
schedule() {
|
|
4613
|
+
if (this.timer) return;
|
|
4614
|
+
this.timer = setTimeout(() => {
|
|
4615
|
+
this.timer = null;
|
|
4616
|
+
void this.persist();
|
|
4617
|
+
}, this.debounceMs);
|
|
4618
|
+
}
|
|
4619
|
+
async persist() {
|
|
4620
|
+
if (this.writing) {
|
|
4621
|
+
this.rewriteRequested = true;
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4624
|
+
this.writing = true;
|
|
4625
|
+
try {
|
|
4626
|
+
await atomicWrite(this.filePath, JSON.stringify(this.snapshot, null, 2), {
|
|
4627
|
+
mode: 384
|
|
4628
|
+
});
|
|
4629
|
+
} catch (err) {
|
|
4630
|
+
console.warn(
|
|
4631
|
+
"[director-state] checkpoint write failed:",
|
|
4632
|
+
err instanceof Error ? err.message : String(err)
|
|
4633
|
+
);
|
|
4634
|
+
} finally {
|
|
4635
|
+
this.writing = false;
|
|
4636
|
+
if (this.rewriteRequested) {
|
|
4637
|
+
this.rewriteRequested = false;
|
|
4638
|
+
this.schedule();
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
};
|
|
4371
4643
|
var DefaultPermissionPolicy = class {
|
|
4372
4644
|
policy = {};
|
|
4373
4645
|
loaded = false;
|
|
@@ -4476,6 +4748,18 @@ var DefaultPermissionPolicy = class {
|
|
|
4476
4748
|
return void 0;
|
|
4477
4749
|
}
|
|
4478
4750
|
};
|
|
4751
|
+
var AutoApprovePermissionPolicy = class {
|
|
4752
|
+
async evaluate(tool) {
|
|
4753
|
+
if (tool.permission === "deny") {
|
|
4754
|
+
return { permission: "deny", source: "default", reason: "tool default deny" };
|
|
4755
|
+
}
|
|
4756
|
+
return { permission: "auto", source: "yolo" };
|
|
4757
|
+
}
|
|
4758
|
+
async trust() {
|
|
4759
|
+
}
|
|
4760
|
+
async reload() {
|
|
4761
|
+
}
|
|
4762
|
+
};
|
|
4479
4763
|
var DefaultSkillLoader = class {
|
|
4480
4764
|
dirs;
|
|
4481
4765
|
cache;
|
|
@@ -5516,6 +5800,10 @@ var FleetBus = class {
|
|
|
5516
5800
|
"iteration.started",
|
|
5517
5801
|
"iteration.completed",
|
|
5518
5802
|
"provider.text_delta",
|
|
5803
|
+
// Subagent extended-thinking output. Forwarded so the FleetPanel /
|
|
5804
|
+
// /fleet log can surface "the planner is thinking…" instead of a
|
|
5805
|
+
// silent gap between iteration.started and the first text_delta.
|
|
5806
|
+
"provider.thinking_delta",
|
|
5519
5807
|
"provider.response",
|
|
5520
5808
|
"provider.retry",
|
|
5521
5809
|
"provider.error",
|
|
@@ -5764,14 +6052,34 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5764
6052
|
completedResults = [];
|
|
5765
6053
|
totalIterations = 0;
|
|
5766
6054
|
inFlight = 0;
|
|
6055
|
+
/**
|
|
6056
|
+
* Subagents currently being stopped. Set on entry to `stop()`, cleared
|
|
6057
|
+
* once `recordCompletion` lands the terminal TaskResult. Used by
|
|
6058
|
+
* `runDispatched` and `findIdleSubagent` to refuse mid-flight dispatch
|
|
6059
|
+
* to a subagent the caller has already asked to terminate — closes the
|
|
6060
|
+
* assign+terminate race where a fresh task could land on a worker that
|
|
6061
|
+
* was about to be killed.
|
|
6062
|
+
*/
|
|
6063
|
+
terminating = /* @__PURE__ */ new Set();
|
|
5767
6064
|
constructor(config, options = {}) {
|
|
5768
6065
|
super();
|
|
5769
6066
|
this.coordinatorId = config.coordinatorId;
|
|
5770
6067
|
this.config = config;
|
|
5771
6068
|
this.runner = options.runner;
|
|
5772
6069
|
}
|
|
6070
|
+
/**
|
|
6071
|
+
* Replace the runner after construction. Used when the runner depends
|
|
6072
|
+
* on infrastructure (e.g. FleetBus) that isn't available until after
|
|
6073
|
+
* the coordinator's owning Director is built.
|
|
6074
|
+
*/
|
|
6075
|
+
setRunner(runner) {
|
|
6076
|
+
this.runner = runner;
|
|
6077
|
+
}
|
|
5773
6078
|
async spawn(subagent) {
|
|
5774
6079
|
const id = subagent.id || randomUUID();
|
|
6080
|
+
if (this.subagents.has(id)) {
|
|
6081
|
+
throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
|
|
6082
|
+
}
|
|
5775
6083
|
const context = {
|
|
5776
6084
|
subagentId: id,
|
|
5777
6085
|
tasks: [],
|
|
@@ -5815,6 +6123,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5815
6123
|
async stop(subagentId) {
|
|
5816
6124
|
const subagent = this.subagents.get(subagentId);
|
|
5817
6125
|
if (!subagent) return;
|
|
6126
|
+
this.terminating.add(subagentId);
|
|
5818
6127
|
subagent.abortController.abort();
|
|
5819
6128
|
subagent.status = "stopped";
|
|
5820
6129
|
subagent.currentTask = void 0;
|
|
@@ -5822,6 +6131,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5822
6131
|
this.emit("subagent.stopped", { subagentId, reason: "stopped by coordinator" });
|
|
5823
6132
|
}
|
|
5824
6133
|
async stopAll() {
|
|
6134
|
+
this.drainPendingAsAborted("Coordinator stopAll() drained the pending queue");
|
|
5825
6135
|
await Promise.allSettled([...this.subagents.keys()].map((id) => this.stop(id)));
|
|
5826
6136
|
}
|
|
5827
6137
|
getStatus() {
|
|
@@ -5855,7 +6165,14 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5855
6165
|
tryDispatchNext() {
|
|
5856
6166
|
while (this.canDispatch()) {
|
|
5857
6167
|
const subagentId = this.findIdleSubagent();
|
|
5858
|
-
if (!subagentId)
|
|
6168
|
+
if (!subagentId) {
|
|
6169
|
+
if (this.pendingTasks.length > 0 && !this.hasLiveSubagent()) {
|
|
6170
|
+
this.drainPendingAsAborted(
|
|
6171
|
+
"No live subagent available \u2014 all stopped or mid-termination"
|
|
6172
|
+
);
|
|
6173
|
+
}
|
|
6174
|
+
return;
|
|
6175
|
+
}
|
|
5859
6176
|
const task = this.pendingTasks.shift();
|
|
5860
6177
|
if (!task) return;
|
|
5861
6178
|
this.runDispatched(subagentId, task).catch((err) => {
|
|
@@ -5863,7 +6180,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5863
6180
|
subagentId,
|
|
5864
6181
|
taskId: task.id,
|
|
5865
6182
|
status: "failed",
|
|
5866
|
-
error:
|
|
6183
|
+
error: classifySubagentError(err),
|
|
5867
6184
|
iterations: 0,
|
|
5868
6185
|
toolCalls: 0,
|
|
5869
6186
|
durationMs: 0
|
|
@@ -5877,13 +6194,76 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5877
6194
|
}
|
|
5878
6195
|
findIdleSubagent() {
|
|
5879
6196
|
for (const [id, s] of this.subagents) {
|
|
5880
|
-
if (s.status === "idle") return id;
|
|
6197
|
+
if (s.status === "idle" && !this.terminating.has(id)) return id;
|
|
5881
6198
|
}
|
|
5882
6199
|
return null;
|
|
5883
6200
|
}
|
|
6201
|
+
/**
|
|
6202
|
+
* Returns true iff at least one spawned subagent could still
|
|
6203
|
+
* process a task. A "live" subagent is one that is not stopped
|
|
6204
|
+
* AND not mid-termination — `running` workers count because they
|
|
6205
|
+
* will eventually finish and become idle.
|
|
6206
|
+
*
|
|
6207
|
+
* When no subagent has ever been spawned, returns `true` so a
|
|
6208
|
+
* pre-spawn `assign()` simply queues (legacy behaviour). The
|
|
6209
|
+
* dead-end detection only fires after `stop()` has retired every
|
|
6210
|
+
* spawned worker.
|
|
6211
|
+
*
|
|
6212
|
+
* Used by `tryDispatchNext` to detect a dead-end pending queue.
|
|
6213
|
+
*/
|
|
6214
|
+
hasLiveSubagent() {
|
|
6215
|
+
if (this.subagents.size === 0) return true;
|
|
6216
|
+
for (const [id, s] of this.subagents) {
|
|
6217
|
+
if (s.status !== "stopped" && !this.terminating.has(id)) return true;
|
|
6218
|
+
}
|
|
6219
|
+
return false;
|
|
6220
|
+
}
|
|
6221
|
+
/**
|
|
6222
|
+
* Drain every pending task with a synthetic `aborted_by_parent`
|
|
6223
|
+
* completion event. Same shape as the `stopAll()` drain — we go
|
|
6224
|
+
* around `recordCompletion` because pending tasks were never
|
|
6225
|
+
* counted in `inFlight` and routing them through would trip the
|
|
6226
|
+
* underflow guard on every task after the first.
|
|
6227
|
+
*/
|
|
6228
|
+
drainPendingAsAborted(message) {
|
|
6229
|
+
const dropped = this.pendingTasks.splice(0, this.pendingTasks.length);
|
|
6230
|
+
for (const t2 of dropped) {
|
|
6231
|
+
const synthetic = {
|
|
6232
|
+
subagentId: t2.subagentId ?? "unassigned",
|
|
6233
|
+
taskId: t2.id,
|
|
6234
|
+
status: "stopped",
|
|
6235
|
+
error: {
|
|
6236
|
+
kind: "aborted_by_parent",
|
|
6237
|
+
message,
|
|
6238
|
+
retryable: false
|
|
6239
|
+
},
|
|
6240
|
+
iterations: 0,
|
|
6241
|
+
toolCalls: 0,
|
|
6242
|
+
durationMs: 0
|
|
6243
|
+
};
|
|
6244
|
+
this.completedResults.push(synthetic);
|
|
6245
|
+
this.emit("task.completed", { task: t2, result: synthetic });
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
5884
6248
|
async runDispatched(subagentId, task) {
|
|
5885
6249
|
const subagent = this.subagents.get(subagentId);
|
|
5886
6250
|
if (!subagent) return;
|
|
6251
|
+
if (this.terminating.has(subagentId) || subagent.status === "stopped") {
|
|
6252
|
+
this.recordCompletion({
|
|
6253
|
+
subagentId,
|
|
6254
|
+
taskId: task.id,
|
|
6255
|
+
status: "stopped",
|
|
6256
|
+
error: {
|
|
6257
|
+
kind: "aborted_by_parent",
|
|
6258
|
+
message: "Subagent was terminated before task could start",
|
|
6259
|
+
retryable: false
|
|
6260
|
+
},
|
|
6261
|
+
iterations: 0,
|
|
6262
|
+
toolCalls: 0,
|
|
6263
|
+
durationMs: 0
|
|
6264
|
+
});
|
|
6265
|
+
return;
|
|
6266
|
+
}
|
|
5887
6267
|
subagent.status = "running";
|
|
5888
6268
|
subagent.currentTask = task.id;
|
|
5889
6269
|
task.subagentId = subagentId;
|
|
@@ -5929,7 +6309,9 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5929
6309
|
subagentId,
|
|
5930
6310
|
taskId: task.id,
|
|
5931
6311
|
status,
|
|
5932
|
-
error:
|
|
6312
|
+
error: classifySubagentError(err, {
|
|
6313
|
+
parentAborted: subagent.abortController.signal.aborted
|
|
6314
|
+
}),
|
|
5933
6315
|
iterations: usage.iterations,
|
|
5934
6316
|
toolCalls: usage.toolCalls,
|
|
5935
6317
|
durationMs: Date.now() - startTime
|
|
@@ -5968,19 +6350,14 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
5968
6350
|
}
|
|
5969
6351
|
const subagent = this.subagents.get(result.subagentId);
|
|
5970
6352
|
if (subagent && subagent.status !== "stopped") {
|
|
5971
|
-
|
|
5972
|
-
subagent.status =
|
|
6353
|
+
result.status === "failed" || result.status === "timeout";
|
|
6354
|
+
subagent.status = "idle";
|
|
5973
6355
|
subagent.currentTask = void 0;
|
|
5974
6356
|
if (subagent.abortController.signal.aborted) {
|
|
5975
6357
|
subagent.abortController = new AbortController();
|
|
5976
6358
|
}
|
|
5977
|
-
if (subagent.status === "error") {
|
|
5978
|
-
queueMicrotask(() => {
|
|
5979
|
-
if (subagent.status === "error") subagent.status = "idle";
|
|
5980
|
-
this.tryDispatchNext();
|
|
5981
|
-
});
|
|
5982
|
-
}
|
|
5983
6359
|
}
|
|
6360
|
+
this.terminating.delete(result.subagentId);
|
|
5984
6361
|
this.emit("task.completed", {
|
|
5985
6362
|
task: subagent?.context.tasks.find((t2) => t2.id === result.taskId) ?? { id: result.taskId },
|
|
5986
6363
|
result
|
|
@@ -6003,6 +6380,99 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
6003
6380
|
return false;
|
|
6004
6381
|
}
|
|
6005
6382
|
};
|
|
6383
|
+
function classifySubagentError(err, hints = {}) {
|
|
6384
|
+
const cause = err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : void 0;
|
|
6385
|
+
const baseMessage = err instanceof Error ? err.message : String(err);
|
|
6386
|
+
if (err instanceof ProviderError) {
|
|
6387
|
+
return providerErrorToSubagentError(err, baseMessage, cause);
|
|
6388
|
+
}
|
|
6389
|
+
if (err instanceof BudgetExceededError) {
|
|
6390
|
+
const map = {
|
|
6391
|
+
iterations: "budget_iterations",
|
|
6392
|
+
tool_calls: "budget_tool_calls",
|
|
6393
|
+
tokens: "budget_tokens",
|
|
6394
|
+
cost: "budget_cost",
|
|
6395
|
+
timeout: "budget_timeout"
|
|
6396
|
+
};
|
|
6397
|
+
return {
|
|
6398
|
+
kind: map[err.kind],
|
|
6399
|
+
message: baseMessage,
|
|
6400
|
+
// Budgets are user-configured ceilings, not transient failures —
|
|
6401
|
+
// retrying with the same budget will hit the same ceiling. The
|
|
6402
|
+
// orchestrator must raise the budget or narrow the task first.
|
|
6403
|
+
retryable: false,
|
|
6404
|
+
cause
|
|
6405
|
+
};
|
|
6406
|
+
}
|
|
6407
|
+
if (hints.parentAborted) {
|
|
6408
|
+
return {
|
|
6409
|
+
kind: "aborted_by_parent",
|
|
6410
|
+
message: baseMessage,
|
|
6411
|
+
retryable: false,
|
|
6412
|
+
cause
|
|
6413
|
+
};
|
|
6414
|
+
}
|
|
6415
|
+
const lower = baseMessage.toLowerCase();
|
|
6416
|
+
if (/agent aborted$/i.test(baseMessage)) {
|
|
6417
|
+
return {
|
|
6418
|
+
kind: "aborted_by_parent",
|
|
6419
|
+
message: baseMessage,
|
|
6420
|
+
retryable: false,
|
|
6421
|
+
cause
|
|
6422
|
+
};
|
|
6423
|
+
}
|
|
6424
|
+
if (/agent exhausted iteration limit$/i.test(baseMessage)) {
|
|
6425
|
+
return { kind: "budget_iterations", message: baseMessage, retryable: false, cause };
|
|
6426
|
+
}
|
|
6427
|
+
if (/empty response$/i.test(baseMessage)) {
|
|
6428
|
+
return { kind: "empty_response", message: baseMessage, retryable: false, cause };
|
|
6429
|
+
}
|
|
6430
|
+
if (/^tool failed: /i.test(baseMessage)) {
|
|
6431
|
+
return { kind: "tool_failed", message: baseMessage, retryable: false, cause };
|
|
6432
|
+
}
|
|
6433
|
+
if (lower.includes("bridge transport") || /bridge.*(closed|disconnect)/i.test(baseMessage)) {
|
|
6434
|
+
return { kind: "bridge_failed", message: baseMessage, retryable: false, cause };
|
|
6435
|
+
}
|
|
6436
|
+
if (/context length|max.*tokens?.*exceeded|prompt is too long/i.test(baseMessage)) {
|
|
6437
|
+
return { kind: "context_overflow", message: baseMessage, retryable: false, cause };
|
|
6438
|
+
}
|
|
6439
|
+
return {
|
|
6440
|
+
kind: "unknown",
|
|
6441
|
+
message: baseMessage,
|
|
6442
|
+
retryable: false,
|
|
6443
|
+
cause
|
|
6444
|
+
};
|
|
6445
|
+
}
|
|
6446
|
+
function providerErrorToSubagentError(err, message, cause) {
|
|
6447
|
+
const status = err.status;
|
|
6448
|
+
if (status === 429 || err.body?.type === "rate_limit_error") {
|
|
6449
|
+
return {
|
|
6450
|
+
kind: "provider_rate_limit",
|
|
6451
|
+
message,
|
|
6452
|
+
retryable: true,
|
|
6453
|
+
// Conservative default: 5s. Provider-specific code can override
|
|
6454
|
+
// by emitting an error whose body carries an explicit hint.
|
|
6455
|
+
backoffMs: 5e3,
|
|
6456
|
+
cause
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
if (status === 401 || status === 403 || err.body?.type === "authentication_error") {
|
|
6460
|
+
return { kind: "provider_auth", message, retryable: false, cause };
|
|
6461
|
+
}
|
|
6462
|
+
if (status === 408 || status === 0) {
|
|
6463
|
+
return { kind: "provider_timeout", message, retryable: true, cause };
|
|
6464
|
+
}
|
|
6465
|
+
if (status >= 500 && status < 600) {
|
|
6466
|
+
return {
|
|
6467
|
+
kind: "provider_5xx",
|
|
6468
|
+
message,
|
|
6469
|
+
retryable: true,
|
|
6470
|
+
backoffMs: 3e3,
|
|
6471
|
+
cause
|
|
6472
|
+
};
|
|
6473
|
+
}
|
|
6474
|
+
return { kind: "unknown", message, retryable: err.retryable, cause };
|
|
6475
|
+
}
|
|
6006
6476
|
|
|
6007
6477
|
// src/coordination/director.ts
|
|
6008
6478
|
var DirectorBudgetError = class extends Error {
|
|
@@ -6066,6 +6536,27 @@ var Director = class {
|
|
|
6066
6536
|
spawnDepth;
|
|
6067
6537
|
/** Live spawn counter for `maxSpawns` enforcement. */
|
|
6068
6538
|
spawnCount = 0;
|
|
6539
|
+
/** Optional checkpoint mirror — writes the live task graph + roster to disk. */
|
|
6540
|
+
stateCheckpoint;
|
|
6541
|
+
/** Optional session writer for emitting task_* / agent_* lifecycle events. */
|
|
6542
|
+
sessionWriter;
|
|
6543
|
+
/** Debounce timer for periodic manifest writes. */
|
|
6544
|
+
manifestTimer = null;
|
|
6545
|
+
manifestDebounceMs;
|
|
6546
|
+
/** Resolves task descriptions back from `assign()` so completion events
|
|
6547
|
+
* can also carry a human-readable title. */
|
|
6548
|
+
taskDescriptions = /* @__PURE__ */ new Map();
|
|
6549
|
+
/** Snapshot of which subagent owns each task — drives state-checkpoint
|
|
6550
|
+
* status updates without re-walking the manifest. */
|
|
6551
|
+
taskOwners = /* @__PURE__ */ new Map();
|
|
6552
|
+
/**
|
|
6553
|
+
* Handle to the coordinator-side `task.completed` listener so we can
|
|
6554
|
+
* unsubscribe in `shutdown()`. Without this, repeated Director
|
|
6555
|
+
* construction (e.g. tests, hot reloads) accumulates listeners on a
|
|
6556
|
+
* cached coordinator and slowly drifts the EventEmitter past its
|
|
6557
|
+
* default cap.
|
|
6558
|
+
*/
|
|
6559
|
+
taskCompletedListener = null;
|
|
6069
6560
|
constructor(opts) {
|
|
6070
6561
|
this.id = opts.config.coordinatorId || randomUUID();
|
|
6071
6562
|
this.manifestPath = opts.manifestPath;
|
|
@@ -6076,6 +6567,14 @@ var Director = class {
|
|
|
6076
6567
|
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
6077
6568
|
this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
|
|
6078
6569
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
6570
|
+
this.sessionWriter = opts.sessionWriter ?? null;
|
|
6571
|
+
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
6572
|
+
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(opts.stateCheckpointPath, {
|
|
6573
|
+
directorRunId: this.id,
|
|
6574
|
+
maxSpawns: opts.maxSpawns,
|
|
6575
|
+
spawnDepth: this.spawnDepth,
|
|
6576
|
+
maxSpawnDepth: this.maxSpawnDepth
|
|
6577
|
+
}) : null;
|
|
6079
6578
|
if (this.sharedScratchpadPath) {
|
|
6080
6579
|
void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(() => void 0);
|
|
6081
6580
|
}
|
|
@@ -6094,7 +6593,7 @@ var Director = class {
|
|
|
6094
6593
|
{ ...opts.config, coordinatorId: this.id },
|
|
6095
6594
|
{ runner: opts.runner }
|
|
6096
6595
|
);
|
|
6097
|
-
this.
|
|
6596
|
+
this.taskCompletedListener = (payload) => {
|
|
6098
6597
|
const r = payload.result;
|
|
6099
6598
|
this.completed.set(r.taskId, r);
|
|
6100
6599
|
const waiter = this.taskWaiters.get(r.taskId);
|
|
@@ -6102,7 +6601,54 @@ var Director = class {
|
|
|
6102
6601
|
waiter.resolve(r);
|
|
6103
6602
|
this.taskWaiters.delete(r.taskId);
|
|
6104
6603
|
}
|
|
6105
|
-
|
|
6604
|
+
const title = this.taskDescriptions.get(r.taskId) ?? payload.task.description ?? r.taskId;
|
|
6605
|
+
const failed = r.status !== "success";
|
|
6606
|
+
const errorString = r.error ? `${r.error.kind}: ${r.error.message}` : void 0;
|
|
6607
|
+
this.stateCheckpoint?.recordTaskStatus(r.taskId, {
|
|
6608
|
+
status: failed ? r.status : "completed",
|
|
6609
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6610
|
+
iterations: r.iterations,
|
|
6611
|
+
toolCalls: r.toolCalls,
|
|
6612
|
+
durationMs: r.durationMs,
|
|
6613
|
+
error: errorString
|
|
6614
|
+
});
|
|
6615
|
+
this.stateCheckpoint?.setUsage(this.usage.snapshot());
|
|
6616
|
+
void this.appendSessionEvent(
|
|
6617
|
+
failed ? {
|
|
6618
|
+
type: "task_failed",
|
|
6619
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6620
|
+
taskId: r.taskId,
|
|
6621
|
+
title,
|
|
6622
|
+
error: errorString ?? r.status
|
|
6623
|
+
} : {
|
|
6624
|
+
type: "task_completed",
|
|
6625
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6626
|
+
taskId: r.taskId,
|
|
6627
|
+
title
|
|
6628
|
+
}
|
|
6629
|
+
);
|
|
6630
|
+
this.scheduleManifest();
|
|
6631
|
+
};
|
|
6632
|
+
this.coordinator.on("task.completed", this.taskCompletedListener);
|
|
6633
|
+
}
|
|
6634
|
+
/** Best-effort session-writer append. Swallows failures — the director
|
|
6635
|
+
* must not break a fleet run because the session JSONL handle closed. */
|
|
6636
|
+
async appendSessionEvent(event) {
|
|
6637
|
+
if (!this.sessionWriter) return;
|
|
6638
|
+
try {
|
|
6639
|
+
await this.sessionWriter.append(event);
|
|
6640
|
+
} catch {
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
/** Debounced manifest writer. A burst of spawn/assign/complete events
|
|
6644
|
+
* collapses into one write. Set `manifestDebounceMs` to 0 to disable. */
|
|
6645
|
+
scheduleManifest() {
|
|
6646
|
+
if (!this.manifestPath || this.manifestDebounceMs <= 0) return;
|
|
6647
|
+
if (this.manifestTimer) return;
|
|
6648
|
+
this.manifestTimer = setTimeout(() => {
|
|
6649
|
+
this.manifestTimer = null;
|
|
6650
|
+
void this.writeManifest().catch(() => void 0);
|
|
6651
|
+
}, this.manifestDebounceMs);
|
|
6106
6652
|
}
|
|
6107
6653
|
/**
|
|
6108
6654
|
* Spawn a subagent. Identical to the coordinator's `spawn()` but
|
|
@@ -6141,6 +6687,25 @@ var Director = class {
|
|
|
6141
6687
|
model: config.model,
|
|
6142
6688
|
taskIds: []
|
|
6143
6689
|
});
|
|
6690
|
+
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6691
|
+
this.stateCheckpoint?.recordSpawn(
|
|
6692
|
+
{
|
|
6693
|
+
id: result.subagentId,
|
|
6694
|
+
name: config.name,
|
|
6695
|
+
role: config.role,
|
|
6696
|
+
provider: config.provider,
|
|
6697
|
+
model: config.model,
|
|
6698
|
+
spawnedAt
|
|
6699
|
+
},
|
|
6700
|
+
this.spawnCount
|
|
6701
|
+
);
|
|
6702
|
+
void this.appendSessionEvent({
|
|
6703
|
+
type: "agent_spawned",
|
|
6704
|
+
ts: spawnedAt,
|
|
6705
|
+
agentId: result.subagentId,
|
|
6706
|
+
role: config.role ?? config.name
|
|
6707
|
+
});
|
|
6708
|
+
this.scheduleManifest();
|
|
6144
6709
|
return result.subagentId;
|
|
6145
6710
|
}
|
|
6146
6711
|
/**
|
|
@@ -6261,13 +6826,42 @@ var Director = class {
|
|
|
6261
6826
|
* — calling shutdown twice is a no-op on the second invocation.
|
|
6262
6827
|
*/
|
|
6263
6828
|
async shutdown() {
|
|
6829
|
+
if (this.manifestTimer) {
|
|
6830
|
+
clearTimeout(this.manifestTimer);
|
|
6831
|
+
this.manifestTimer = null;
|
|
6832
|
+
}
|
|
6833
|
+
if (this.taskCompletedListener) {
|
|
6834
|
+
this.coordinator.off("task.completed", this.taskCompletedListener);
|
|
6835
|
+
this.taskCompletedListener = null;
|
|
6836
|
+
}
|
|
6264
6837
|
await this.coordinator.stopAll();
|
|
6265
6838
|
for (const b of this.subagentBridges.values()) {
|
|
6266
|
-
await b.stop().catch(() =>
|
|
6839
|
+
await b.stop().catch((err) => this.logShutdownError("subagent_bridge_stop", err));
|
|
6267
6840
|
}
|
|
6268
6841
|
this.subagentBridges.clear();
|
|
6269
|
-
await this.bridge.stop().catch(() =>
|
|
6270
|
-
if (this.manifestPath)
|
|
6842
|
+
await this.bridge.stop().catch((err) => this.logShutdownError("director_bridge_stop", err));
|
|
6843
|
+
if (this.manifestPath)
|
|
6844
|
+
await this.writeManifest().catch((err) => this.logShutdownError("manifest_write", err));
|
|
6845
|
+
if (this.stateCheckpoint) {
|
|
6846
|
+
this.stateCheckpoint.setUsage(this.usage.snapshot());
|
|
6847
|
+
await this.stateCheckpoint.flush().catch((err) => this.logShutdownError("state_checkpoint_flush", err));
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
/**
|
|
6851
|
+
* Funnel for shutdown-phase errors. We can't throw — `shutdown()` is
|
|
6852
|
+
* called from process-exit paths where an uncaught throw would lose
|
|
6853
|
+
* the manifest write that comes after. But we MUST NOT silently
|
|
6854
|
+
* swallow either — a persistent bridge-close failure would otherwise
|
|
6855
|
+
* mask a real bug. `process.emitWarning` is the right tier:
|
|
6856
|
+
* surfaces on stderr by default, lets the host plug a warning
|
|
6857
|
+
* listener for structured collection, and never affects exit code.
|
|
6858
|
+
*/
|
|
6859
|
+
logShutdownError(phase, err) {
|
|
6860
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
6861
|
+
process.emitWarning(
|
|
6862
|
+
`Director shutdown phase "${phase}" failed: ${detail}`,
|
|
6863
|
+
"DirectorShutdownWarning"
|
|
6864
|
+
);
|
|
6271
6865
|
}
|
|
6272
6866
|
/**
|
|
6273
6867
|
* Hand a task to the coordinator. Returns the assigned task id so
|
|
@@ -6281,6 +6875,23 @@ var Director = class {
|
|
|
6281
6875
|
if (entry) entry.taskIds.push(taskWithId.id);
|
|
6282
6876
|
}
|
|
6283
6877
|
await this.coordinator.assign(taskWithId);
|
|
6878
|
+
this.taskDescriptions.set(taskWithId.id, taskWithId.description);
|
|
6879
|
+
if (taskWithId.subagentId) this.taskOwners.set(taskWithId.id, taskWithId.subagentId);
|
|
6880
|
+
const assignedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6881
|
+
this.stateCheckpoint?.recordTaskAssigned({
|
|
6882
|
+
taskId: taskWithId.id,
|
|
6883
|
+
subagentId: taskWithId.subagentId,
|
|
6884
|
+
description: taskWithId.description,
|
|
6885
|
+
status: "running",
|
|
6886
|
+
assignedAt
|
|
6887
|
+
});
|
|
6888
|
+
void this.appendSessionEvent({
|
|
6889
|
+
type: "task_created",
|
|
6890
|
+
ts: assignedAt,
|
|
6891
|
+
taskId: taskWithId.id,
|
|
6892
|
+
title: taskWithId.description
|
|
6893
|
+
});
|
|
6894
|
+
this.scheduleManifest();
|
|
6284
6895
|
return taskWithId.id;
|
|
6285
6896
|
}
|
|
6286
6897
|
/**
|
|
@@ -6340,6 +6951,23 @@ var Director = class {
|
|
|
6340
6951
|
snapshot() {
|
|
6341
6952
|
return this.usage.snapshot();
|
|
6342
6953
|
}
|
|
6954
|
+
/**
|
|
6955
|
+
* Look up provider/model metadata for a spawned subagent. Returns
|
|
6956
|
+
* undefined when the subagent id is unknown (not yet spawned, or
|
|
6957
|
+
* already torn down). Callers — notably the TUI fleet panel — use
|
|
6958
|
+
* this to render human-readable provider/model tags next to each
|
|
6959
|
+
* subagent row without reaching into private state.
|
|
6960
|
+
*/
|
|
6961
|
+
getSubagentMeta(id) {
|
|
6962
|
+
const usage = this.subagentMeta.get(id);
|
|
6963
|
+
const manifest = this.manifestEntries.get(id);
|
|
6964
|
+
if (!usage && !manifest) return void 0;
|
|
6965
|
+
return {
|
|
6966
|
+
provider: usage?.provider ?? manifest?.provider,
|
|
6967
|
+
model: usage?.model ?? manifest?.model,
|
|
6968
|
+
name: manifest?.name
|
|
6969
|
+
};
|
|
6970
|
+
}
|
|
6343
6971
|
/**
|
|
6344
6972
|
* Compose the leader/director-agent system prompt: fleet preamble +
|
|
6345
6973
|
* (optional) roster summary + user base prompt. Pass the result to your
|
|
@@ -6639,12 +7267,260 @@ function makeFleetUsageTool(director) {
|
|
|
6639
7267
|
}
|
|
6640
7268
|
};
|
|
6641
7269
|
}
|
|
7270
|
+
function createDelegateTool(opts) {
|
|
7271
|
+
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
7272
|
+
const rosterIds = opts.roster ? Object.keys(opts.roster) : [];
|
|
7273
|
+
const inputSchema = {
|
|
7274
|
+
type: "object",
|
|
7275
|
+
properties: {
|
|
7276
|
+
task: {
|
|
7277
|
+
type: "string",
|
|
7278
|
+
description: "What the subagent should do \u2014 natural language, complete sentence(s). The subagent has its own tool slice, its own LLM call, and returns when its task is done."
|
|
7279
|
+
},
|
|
7280
|
+
role: {
|
|
7281
|
+
type: "string",
|
|
7282
|
+
description: rosterIds.length > 0 ? `Roster role (preferred). One of: ${rosterIds.join(", ")}. Picks a pre-tuned config (prompt, budgets, tools) for that role.` : "No roster is configured \u2014 pass `name` instead.",
|
|
7283
|
+
enum: rosterIds.length > 0 ? rosterIds : void 0
|
|
7284
|
+
},
|
|
7285
|
+
name: {
|
|
7286
|
+
type: "string",
|
|
7287
|
+
description: "Display name for the subagent when not using a roster role. Required when `role` is omitted."
|
|
7288
|
+
},
|
|
7289
|
+
provider: {
|
|
7290
|
+
type: "string",
|
|
7291
|
+
description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the host provider when omitted.'
|
|
7292
|
+
},
|
|
7293
|
+
model: {
|
|
7294
|
+
type: "string",
|
|
7295
|
+
description: "Model id within the provider. Defaults to the host model when omitted."
|
|
7296
|
+
},
|
|
7297
|
+
systemPromptOverride: {
|
|
7298
|
+
type: "string",
|
|
7299
|
+
description: "Optional extra prompt text appended to the role baseline."
|
|
7300
|
+
},
|
|
7301
|
+
timeoutMs: {
|
|
7302
|
+
type: "number",
|
|
7303
|
+
description: `Wall-clock budget for this delegate in milliseconds. No hard cap \u2014 set as high as the task realistically needs (a monorepo audit can take hours, a single-file lint takes seconds). Default ${Math.round(defaultTimeoutMs / 1e3 / 60)} minutes.`
|
|
7304
|
+
},
|
|
7305
|
+
maxIterations: {
|
|
7306
|
+
type: "number",
|
|
7307
|
+
description: "Maximum LLM iterations the subagent may take. Unset = use the role/coordinator default. Raise this for tasks with many tool-think-tool cycles (deep code analysis, multi-file refactors)."
|
|
7308
|
+
},
|
|
7309
|
+
maxToolCalls: {
|
|
7310
|
+
type: "number",
|
|
7311
|
+
description: "Maximum number of tool invocations the subagent may make. Unset = use the role/coordinator default. Raise this for tasks that touch many files (large grep + read + report)."
|
|
7312
|
+
}
|
|
7313
|
+
},
|
|
7314
|
+
required: ["task"]
|
|
7315
|
+
};
|
|
7316
|
+
return {
|
|
7317
|
+
name: "delegate",
|
|
7318
|
+
description: "Hand a discrete piece of work to a dedicated subagent and wait for its result. The subagent has its own context, its own LLM call, and its own budget \u2014 use this when a task is self-contained, would otherwise blow up your context, or benefits from a specialized role (bug-hunter, security-scanner, refactor-planner, audit-log). YOU decide how big the budget is: pass `timeoutMs`, `maxIterations`, and `maxToolCalls` sized to the actual work. There is no hidden cap forcing a 3-minute / 80-iteration limit \u2014 if a monorepo audit needs 2 hours and 500 tool calls, ask for that. Call multiple delegates in parallel through the provider's parallel-tool-call surface to fan work out across roles.",
|
|
7319
|
+
usageHint: "Set `task` to a complete instruction. Either pick `role` from the roster or pass `name` + `provider` + `model`. For non-trivial work, also pass `timeoutMs` (the wall-clock budget you actually need), `maxIterations`, and `maxToolCalls` \u2014 defaults are intentionally generous (4 hours) but the right values depend on scope. Returns the subagent's `TaskResult` \u2014 including the textual `result`, iteration count, tool count, and duration. Auto-promotes the host into director mode on first call.",
|
|
7320
|
+
permission: "auto",
|
|
7321
|
+
mutating: false,
|
|
7322
|
+
inputSchema,
|
|
7323
|
+
async execute(input) {
|
|
7324
|
+
const i = input ?? {};
|
|
7325
|
+
if (typeof i.task !== "string" || !i.task.trim()) {
|
|
7326
|
+
return { ok: false, error: "`task` is required." };
|
|
7327
|
+
}
|
|
7328
|
+
let director = await opts.host.ensureDirector();
|
|
7329
|
+
if (!director) {
|
|
7330
|
+
director = await opts.host.promoteToDirector();
|
|
7331
|
+
}
|
|
7332
|
+
if (!director) {
|
|
7333
|
+
const reason = opts.host.getPromotionBlockReason?.();
|
|
7334
|
+
return {
|
|
7335
|
+
ok: false,
|
|
7336
|
+
error: reason ?? "Director could not be activated \u2014 multi-agent host already running in legacy non-director mode. Restart with `--director` for fleet support."
|
|
7337
|
+
};
|
|
7338
|
+
}
|
|
7339
|
+
const timeoutMs = i.timeoutMs ?? defaultTimeoutMs;
|
|
7340
|
+
let cfg;
|
|
7341
|
+
if (i.role) {
|
|
7342
|
+
const base = opts.roster?.[i.role];
|
|
7343
|
+
if (!base) {
|
|
7344
|
+
return {
|
|
7345
|
+
ok: false,
|
|
7346
|
+
error: `Unknown role "${i.role}". Available: ${rosterIds.join(", ") || "(no roster configured)"}.`
|
|
7347
|
+
};
|
|
7348
|
+
}
|
|
7349
|
+
cfg = { ...base };
|
|
7350
|
+
if (i.systemPromptOverride) cfg.systemPromptOverride = i.systemPromptOverride;
|
|
7351
|
+
if (i.provider) cfg.provider = i.provider;
|
|
7352
|
+
if (i.model) cfg.model = i.model;
|
|
7353
|
+
} else {
|
|
7354
|
+
if (!i.name) {
|
|
7355
|
+
return {
|
|
7356
|
+
ok: false,
|
|
7357
|
+
error: "Either `role` (from the roster) or `name` is required."
|
|
7358
|
+
};
|
|
7359
|
+
}
|
|
7360
|
+
cfg = {
|
|
7361
|
+
name: i.name,
|
|
7362
|
+
provider: i.provider,
|
|
7363
|
+
model: i.model,
|
|
7364
|
+
systemPromptOverride: i.systemPromptOverride
|
|
7365
|
+
};
|
|
7366
|
+
}
|
|
7367
|
+
if (typeof i.maxIterations === "number" && i.maxIterations > 0) {
|
|
7368
|
+
cfg.maxIterations = i.maxIterations;
|
|
7369
|
+
}
|
|
7370
|
+
if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
|
|
7371
|
+
cfg.maxToolCalls = i.maxToolCalls;
|
|
7372
|
+
}
|
|
7373
|
+
const SUBAGENT_TIMEOUT_BUFFER_MS = 3e4;
|
|
7374
|
+
const desiredSubTimeout = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
7375
|
+
if (!cfg.timeoutMs || cfg.timeoutMs > desiredSubTimeout) {
|
|
7376
|
+
cfg.timeoutMs = desiredSubTimeout;
|
|
7377
|
+
}
|
|
7378
|
+
try {
|
|
7379
|
+
const subagentId = await director.spawn(cfg);
|
|
7380
|
+
const taskId = await director.assign({
|
|
7381
|
+
id: "",
|
|
7382
|
+
description: i.task,
|
|
7383
|
+
subagentId
|
|
7384
|
+
});
|
|
7385
|
+
const result = await Promise.race([
|
|
7386
|
+
director.awaitTasks([taskId]).then((r) => r[0]),
|
|
7387
|
+
new Promise(
|
|
7388
|
+
(resolve4) => setTimeout(() => resolve4({ __timeout: true }), timeoutMs)
|
|
7389
|
+
)
|
|
7390
|
+
]);
|
|
7391
|
+
if ("__timeout" in result) {
|
|
7392
|
+
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
7393
|
+
return {
|
|
7394
|
+
ok: false,
|
|
7395
|
+
stopReason: "host_timeout",
|
|
7396
|
+
error: `Subagent did not finish within ${timeoutMs}ms.`,
|
|
7397
|
+
hint: "Reduce scope of the next delegate, raise timeoutMs, or use spawn_subagent + await_tasks for long-running work.",
|
|
7398
|
+
subagentId,
|
|
7399
|
+
taskId,
|
|
7400
|
+
partial: partial2
|
|
7401
|
+
};
|
|
7402
|
+
}
|
|
7403
|
+
const baseStopReason = result.status === "success" ? "end_turn" : result.status === "timeout" ? "subagent_timeout" : result.status === "stopped" ? "aborted" : "budget_exhausted";
|
|
7404
|
+
const partial = result.status === "success" ? void 0 : await readSubagentPartial(opts, subagentId);
|
|
7405
|
+
const errorKind = result.error?.kind;
|
|
7406
|
+
const retryable = result.error?.retryable;
|
|
7407
|
+
const backoffMs = result.error?.backoffMs;
|
|
7408
|
+
return {
|
|
7409
|
+
ok: result.status === "success",
|
|
7410
|
+
status: result.status,
|
|
7411
|
+
stopReason: baseStopReason,
|
|
7412
|
+
errorKind,
|
|
7413
|
+
retryable,
|
|
7414
|
+
backoffMs,
|
|
7415
|
+
subagentId: result.subagentId,
|
|
7416
|
+
taskId: result.taskId,
|
|
7417
|
+
result: result.result,
|
|
7418
|
+
error: result.error,
|
|
7419
|
+
iterations: result.iterations,
|
|
7420
|
+
toolCalls: result.toolCalls,
|
|
7421
|
+
durationMs: result.durationMs,
|
|
7422
|
+
...partial ? { partial } : {},
|
|
7423
|
+
...hintForKind(errorKind, retryable, backoffMs) ? { hint: hintForKind(errorKind, retryable, backoffMs) } : {}
|
|
7424
|
+
};
|
|
7425
|
+
} catch (err) {
|
|
7426
|
+
return {
|
|
7427
|
+
ok: false,
|
|
7428
|
+
stopReason: "error",
|
|
7429
|
+
error: err instanceof Error ? err.message : String(err)
|
|
7430
|
+
};
|
|
7431
|
+
}
|
|
7432
|
+
}
|
|
7433
|
+
};
|
|
7434
|
+
}
|
|
7435
|
+
function hintForKind(kind, retryable, backoffMs) {
|
|
7436
|
+
if (!kind) return void 0;
|
|
7437
|
+
switch (kind) {
|
|
7438
|
+
case "provider_rate_limit":
|
|
7439
|
+
return `Provider rate-limited. Retry safe after ${backoffMs ?? 5e3}ms backoff. Consider a smaller model or fewer parallel delegates.`;
|
|
7440
|
+
case "provider_5xx":
|
|
7441
|
+
return `Provider server error. Retry safe after ${backoffMs ?? 3e3}ms backoff \u2014 usually transient.`;
|
|
7442
|
+
case "provider_timeout":
|
|
7443
|
+
return "Provider network timeout. Retry safe; reduce input size if it persists.";
|
|
7444
|
+
case "provider_auth":
|
|
7445
|
+
return "Provider rejected credentials. Cannot retry \u2014 fix the API key / config and re-invoke.";
|
|
7446
|
+
case "context_overflow":
|
|
7447
|
+
return "Subagent context exceeded the model limit. Narrow the task, use a larger-context model, or split into multiple delegates.";
|
|
7448
|
+
case "budget_iterations":
|
|
7449
|
+
case "budget_tool_calls":
|
|
7450
|
+
case "budget_tokens":
|
|
7451
|
+
case "budget_cost":
|
|
7452
|
+
return "Subagent exhausted its budget. Raise the matching `max*` field on the next delegate or narrow task scope.";
|
|
7453
|
+
case "budget_timeout":
|
|
7454
|
+
return "Subagent hit its wall-clock budget. Raise `timeoutMs` on the next delegate or split the task.";
|
|
7455
|
+
case "aborted_by_parent":
|
|
7456
|
+
return "Subagent was aborted (user Ctrl+C, parent unwound, or sibling failure cascade). Not retryable until the abort condition is resolved.";
|
|
7457
|
+
case "empty_response":
|
|
7458
|
+
return "Subagent ended its turn with no text and no tool calls. Almost always a prompt / config issue \u2014 clarify the task or check the model.";
|
|
7459
|
+
case "tool_failed":
|
|
7460
|
+
return "A tool inside the subagent returned ok:false. Inspect `partial.lastAssistantText` for the agent reasoning, then retry with corrected inputs.";
|
|
7461
|
+
case "bridge_failed":
|
|
7462
|
+
return "Parent-child bridge transport failed. This is rare \u2014 restart the session and retry.";
|
|
7463
|
+
default:
|
|
7464
|
+
return retryable ? "Failure classified as retryable. Try again with the same input." : void 0;
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
async function readSubagentPartial(opts, subagentId) {
|
|
7468
|
+
if (!opts.sessionsRoot) return void 0;
|
|
7469
|
+
const candidates = [];
|
|
7470
|
+
if (opts.directorRunId) {
|
|
7471
|
+
candidates.push(path6.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
7472
|
+
} else {
|
|
7473
|
+
try {
|
|
7474
|
+
const runDirs = await fsp2.readdir(opts.sessionsRoot);
|
|
7475
|
+
for (const r of runDirs) {
|
|
7476
|
+
candidates.push(path6.join(opts.sessionsRoot, r, `${subagentId}.jsonl`));
|
|
7477
|
+
}
|
|
7478
|
+
} catch {
|
|
7479
|
+
return void 0;
|
|
7480
|
+
}
|
|
7481
|
+
}
|
|
7482
|
+
for (const file of candidates) {
|
|
7483
|
+
let raw;
|
|
7484
|
+
try {
|
|
7485
|
+
raw = await fsp2.readFile(file, "utf8");
|
|
7486
|
+
} catch {
|
|
7487
|
+
continue;
|
|
7488
|
+
}
|
|
7489
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
7490
|
+
let lastAssistantText;
|
|
7491
|
+
let lastStopReason;
|
|
7492
|
+
let toolUses = 0;
|
|
7493
|
+
for (const line of lines) {
|
|
7494
|
+
try {
|
|
7495
|
+
const ev = JSON.parse(line);
|
|
7496
|
+
if (ev.type === "tool_use") toolUses += 1;
|
|
7497
|
+
if (ev.type === "llm_response") {
|
|
7498
|
+
if (typeof ev.stopReason === "string") lastStopReason = ev.stopReason;
|
|
7499
|
+
if (Array.isArray(ev.content)) {
|
|
7500
|
+
const txt = ev.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n").trim();
|
|
7501
|
+
if (txt) lastAssistantText = txt;
|
|
7502
|
+
}
|
|
7503
|
+
}
|
|
7504
|
+
} catch {
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
return {
|
|
7508
|
+
lastAssistantText,
|
|
7509
|
+
lastStopReason,
|
|
7510
|
+
toolUsesObserved: toolUses,
|
|
7511
|
+
events: lines.length
|
|
7512
|
+
};
|
|
7513
|
+
}
|
|
7514
|
+
return void 0;
|
|
7515
|
+
}
|
|
6642
7516
|
|
|
6643
7517
|
// src/coordination/agent-subagent-runner.ts
|
|
6644
7518
|
function makeAgentSubagentRunner(opts) {
|
|
6645
7519
|
const format = opts.formatTaskInput ?? defaultFormatTaskInput;
|
|
6646
7520
|
return async (task, ctx) => {
|
|
6647
|
-
const
|
|
7521
|
+
const factoryResult = await opts.factory(ctx.config);
|
|
7522
|
+
const { agent, events } = factoryResult;
|
|
7523
|
+
const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
|
|
6648
7524
|
const aborter = new AbortController();
|
|
6649
7525
|
let budgetError = null;
|
|
6650
7526
|
const onBudgetError = (err) => {
|
|
@@ -6658,13 +7534,19 @@ function makeAgentSubagentRunner(opts) {
|
|
|
6658
7534
|
budgetError.message += ` (caused by: ${err.message})`;
|
|
6659
7535
|
}
|
|
6660
7536
|
};
|
|
7537
|
+
let lastToolFailed = null;
|
|
6661
7538
|
const unsub = [];
|
|
6662
7539
|
unsub.push(
|
|
6663
|
-
events.on("tool.
|
|
7540
|
+
events.on("tool.executed", (e) => {
|
|
6664
7541
|
try {
|
|
6665
7542
|
ctx.budget.recordToolCall();
|
|
6666
|
-
} catch (
|
|
6667
|
-
onBudgetError(
|
|
7543
|
+
} catch (eb) {
|
|
7544
|
+
onBudgetError(eb);
|
|
7545
|
+
}
|
|
7546
|
+
if (e.ok === false) {
|
|
7547
|
+
lastToolFailed = e.name;
|
|
7548
|
+
} else if (e.ok === true) {
|
|
7549
|
+
lastToolFailed = null;
|
|
6668
7550
|
}
|
|
6669
7551
|
}),
|
|
6670
7552
|
events.on("provider.response", (e) => {
|
|
@@ -6681,6 +7563,26 @@ function makeAgentSubagentRunner(opts) {
|
|
|
6681
7563
|
} catch (e) {
|
|
6682
7564
|
onBudgetError(e);
|
|
6683
7565
|
}
|
|
7566
|
+
}),
|
|
7567
|
+
// D3: cooperative timeout enforcement DURING a long tool call.
|
|
7568
|
+
// The iteration-loop checkTimeout() only fires between agent
|
|
7569
|
+
// iterations — a single `bash sleep 3600` call would otherwise
|
|
7570
|
+
// park inside one tool execution while the timeout silently
|
|
7571
|
+
// passes, relying solely on the coordinator's hard Promise.race
|
|
7572
|
+
// to interrupt. Tools that emit `tool.progress` (bash chunks,
|
|
7573
|
+
// fetch byte progress, spawn-stream stdout) give us a heartbeat
|
|
7574
|
+
// we can hang the check on. When the budget trips here:
|
|
7575
|
+
// 1. onBudgetError sets budgetError + aborter.abort()
|
|
7576
|
+
// 2. aborter signal propagates to agent.run → tool executor
|
|
7577
|
+
// 3. tool's own signal listener kills the child process
|
|
7578
|
+
// Cheap: O(1) per progress event, and the budget short-circuits
|
|
7579
|
+
// when timeoutMs is unset (most subagents have one set anyway).
|
|
7580
|
+
events.on("tool.progress", () => {
|
|
7581
|
+
try {
|
|
7582
|
+
ctx.budget.checkTimeout();
|
|
7583
|
+
} catch (e) {
|
|
7584
|
+
onBudgetError(e);
|
|
7585
|
+
}
|
|
6684
7586
|
})
|
|
6685
7587
|
);
|
|
6686
7588
|
const onParentAbort = () => aborter.abort();
|
|
@@ -6689,8 +7591,15 @@ function makeAgentSubagentRunner(opts) {
|
|
|
6689
7591
|
try {
|
|
6690
7592
|
result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
|
|
6691
7593
|
} finally {
|
|
7594
|
+
detachFleet?.();
|
|
6692
7595
|
ctx.signal.removeEventListener("abort", onParentAbort);
|
|
6693
7596
|
for (const u of unsub) u();
|
|
7597
|
+
if (factoryResult.dispose) {
|
|
7598
|
+
try {
|
|
7599
|
+
await factoryResult.dispose();
|
|
7600
|
+
} catch {
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
6694
7603
|
}
|
|
6695
7604
|
if (budgetError) throw budgetError;
|
|
6696
7605
|
if (result.status === "failed") {
|
|
@@ -6703,6 +7612,13 @@ function makeAgentSubagentRunner(opts) {
|
|
|
6703
7612
|
throw new Error("agent exhausted iteration limit");
|
|
6704
7613
|
}
|
|
6705
7614
|
const usage = ctx.budget.usage();
|
|
7615
|
+
const finalText = (result.finalText ?? "").trim();
|
|
7616
|
+
if (finalText.length === 0 && usage.toolCalls === 0) {
|
|
7617
|
+
throw new Error("empty response");
|
|
7618
|
+
}
|
|
7619
|
+
if (finalText.length === 0 && lastToolFailed !== null) {
|
|
7620
|
+
throw new Error(`tool failed: ${lastToolFailed}`);
|
|
7621
|
+
}
|
|
6706
7622
|
return {
|
|
6707
7623
|
result: result.finalText,
|
|
6708
7624
|
iterations: result.iterations,
|
|
@@ -6768,10 +7684,12 @@ Working rules:
|
|
|
6768
7684
|
- Never fabricate numbers \u2014 read the actual logs first
|
|
6769
7685
|
- Always include file:line references for errors
|
|
6770
7686
|
- If sessionPath is missing, ask the director to provide it
|
|
6771
|
-
- Report confidence level: high (>90% accuracy), medium, low
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
7687
|
+
- Report confidence level: high (>90% accuracy), medium, low`
|
|
7688
|
+
// No hardcoded budgets — the orchestrator (delegate tool or
|
|
7689
|
+
// spawn_subagent) decides per-task how much room a subagent gets.
|
|
7690
|
+
// A monorepo audit needs hours; a single-file lint check needs
|
|
7691
|
+
// seconds. Pinning a number here forces the orchestrator to fight
|
|
7692
|
+
// the role's default instead of just asking for what it needs.
|
|
6775
7693
|
};
|
|
6776
7694
|
var BUG_HUNTER_AGENT = {
|
|
6777
7695
|
id: "bug-hunter",
|
|
@@ -6811,10 +7729,8 @@ Working rules:
|
|
|
6811
7729
|
- Never scan node_modules \u2014 it's noise
|
|
6812
7730
|
- Always include file:line for every finding
|
|
6813
7731
|
- If >30% of findings are false positives, note the confidence level
|
|
6814
|
-
- Ask director for clarification if paths are ambiguous
|
|
6815
|
-
|
|
6816
|
-
maxToolCalls: 300,
|
|
6817
|
-
timeoutMs: 18e4
|
|
7732
|
+
- Ask director for clarification if paths are ambiguous`
|
|
7733
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
6818
7734
|
};
|
|
6819
7735
|
var REFACTOR_PLANNER_AGENT = {
|
|
6820
7736
|
id: "refactor-planner",
|
|
@@ -6854,10 +7770,8 @@ Working rules:
|
|
|
6854
7770
|
- Always include rollback strategy \u2014 every refactor can fail
|
|
6855
7771
|
- Merge tasks that take <1h into a single phase
|
|
6856
7772
|
- Respect team constraints (reviewer availability, parallelization)
|
|
6857
|
-
- Never plan without analyzing the actual code first
|
|
6858
|
-
|
|
6859
|
-
maxToolCalls: 250,
|
|
6860
|
-
timeoutMs: 15e4
|
|
7773
|
+
- Never plan without analyzing the actual code first`
|
|
7774
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
6861
7775
|
};
|
|
6862
7776
|
var SECURITY_SCANNER_AGENT = {
|
|
6863
7777
|
id: "security-scanner",
|
|
@@ -6905,10 +7819,8 @@ Working rules:
|
|
|
6905
7819
|
- Never scan node_modules \u2014 use npm audit instead
|
|
6906
7820
|
- Always provide remediation steps, not just findings
|
|
6907
7821
|
- Verify regex-based secrets before flagging (false positive risk)
|
|
6908
|
-
- When in doubt, flag as medium rather than ignoring potential issues
|
|
6909
|
-
|
|
6910
|
-
maxToolCalls: 280,
|
|
6911
|
-
timeoutMs: 16e4
|
|
7822
|
+
- When in doubt, flag as medium rather than ignoring potential issues`
|
|
7823
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
6912
7824
|
};
|
|
6913
7825
|
var FLEET_ROSTER = {
|
|
6914
7826
|
"audit-log": AUDIT_LOG_AGENT,
|
|
@@ -8177,7 +9089,7 @@ async function startMetricsServer(opts) {
|
|
|
8177
9089
|
const tls = opts.tls;
|
|
8178
9090
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
8179
9091
|
const host = opts.host ?? "127.0.0.1";
|
|
8180
|
-
const
|
|
9092
|
+
const path18 = opts.path ?? "/metrics";
|
|
8181
9093
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
8182
9094
|
const healthRegistry = opts.healthRegistry;
|
|
8183
9095
|
const listener = (req, res) => {
|
|
@@ -8187,7 +9099,7 @@ async function startMetricsServer(opts) {
|
|
|
8187
9099
|
return;
|
|
8188
9100
|
}
|
|
8189
9101
|
const url = req.url.split("?")[0];
|
|
8190
|
-
if (url ===
|
|
9102
|
+
if (url === path18) {
|
|
8191
9103
|
let body;
|
|
8192
9104
|
try {
|
|
8193
9105
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -8251,7 +9163,7 @@ async function startMetricsServer(opts) {
|
|
|
8251
9163
|
const protocol = useHttps ? "https" : "http";
|
|
8252
9164
|
return {
|
|
8253
9165
|
port: boundPort,
|
|
8254
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
9166
|
+
url: `${protocol}://${host}:${boundPort}${path18}`,
|
|
8255
9167
|
close: () => new Promise((resolve4, reject) => {
|
|
8256
9168
|
server.close((err) => err ? reject(err) : resolve4());
|
|
8257
9169
|
})
|
|
@@ -9177,7 +10089,7 @@ var Agent = class {
|
|
|
9177
10089
|
this.autoExtendLimit = init.autoExtendLimit ?? true;
|
|
9178
10090
|
this.tracer = init.tracer;
|
|
9179
10091
|
this.toolExecutor = new ToolExecutor(this.tools, {
|
|
9180
|
-
permissionPolicy: this.permission,
|
|
10092
|
+
permissionPolicy: init.permissionPolicy ?? this.permission,
|
|
9181
10093
|
secretScrubber: this.scrubber,
|
|
9182
10094
|
renderer: this.renderer,
|
|
9183
10095
|
events: this.events,
|
|
@@ -9931,6 +10843,7 @@ var DefaultSystemPromptBuilder = class {
|
|
|
9931
10843
|
const layer3 = await this.buildEnvironment(ctx);
|
|
9932
10844
|
const layer4 = await this.buildMemoryAndSkills();
|
|
9933
10845
|
const layer5 = await this.buildMode();
|
|
10846
|
+
const layer6 = ctx.subagent ? "" : await this.buildActivePlan();
|
|
9934
10847
|
const blocks = [
|
|
9935
10848
|
{ type: "text", text: layer1 },
|
|
9936
10849
|
{ type: "text", text: layer2 },
|
|
@@ -9950,8 +10863,52 @@ var DefaultSystemPromptBuilder = class {
|
|
|
9950
10863
|
cache_control: { type: "ephemeral" }
|
|
9951
10864
|
});
|
|
9952
10865
|
}
|
|
10866
|
+
if (layer6.trim()) {
|
|
10867
|
+
blocks.push({
|
|
10868
|
+
type: "text",
|
|
10869
|
+
text: layer6,
|
|
10870
|
+
cache_control: { type: "ephemeral" }
|
|
10871
|
+
});
|
|
10872
|
+
}
|
|
9953
10873
|
return blocks;
|
|
9954
10874
|
}
|
|
10875
|
+
/**
|
|
10876
|
+
* Reads `<sessionId>.plan.json` (when configured) and produces a short
|
|
10877
|
+
* "Active plan" block listing open items so the model is anchored to
|
|
10878
|
+
* the strategic roadmap every turn. Reads on every `build()` so a
|
|
10879
|
+
* plan edit (via `/plan` or the `plan` tool) reflects on the next
|
|
10880
|
+
* turn without restarting the session.
|
|
10881
|
+
*/
|
|
10882
|
+
async buildActivePlan() {
|
|
10883
|
+
const planPath = typeof this.opts.planPath === "function" ? this.opts.planPath() : this.opts.planPath;
|
|
10884
|
+
if (!planPath) return "";
|
|
10885
|
+
let raw;
|
|
10886
|
+
try {
|
|
10887
|
+
raw = await fsp2.readFile(planPath, "utf8");
|
|
10888
|
+
} catch {
|
|
10889
|
+
return "";
|
|
10890
|
+
}
|
|
10891
|
+
let parsed;
|
|
10892
|
+
try {
|
|
10893
|
+
parsed = JSON.parse(raw);
|
|
10894
|
+
} catch {
|
|
10895
|
+
return "";
|
|
10896
|
+
}
|
|
10897
|
+
if (!Array.isArray(parsed.items) || parsed.items.length === 0) return "";
|
|
10898
|
+
const open3 = parsed.items.filter((i) => i?.status !== "done");
|
|
10899
|
+
if (open3.length === 0) return "";
|
|
10900
|
+
const lines = ["## Active plan"];
|
|
10901
|
+
if (parsed.title) lines.push(`*${parsed.title}*`, "");
|
|
10902
|
+
parsed.items.forEach((it, idx) => {
|
|
10903
|
+
const mark = it?.status === "done" ? "[x]" : it?.status === "in_progress" ? "[~]" : "[ ]";
|
|
10904
|
+
lines.push(`${idx + 1}. ${mark} ${it?.title ?? "(untitled)"}`);
|
|
10905
|
+
});
|
|
10906
|
+
lines.push(
|
|
10907
|
+
"",
|
|
10908
|
+
"Use `/plan` (user) or the `plan` tool to update status as you progress. The roadmap survives session resume."
|
|
10909
|
+
);
|
|
10910
|
+
return lines.join("\n");
|
|
10911
|
+
}
|
|
9955
10912
|
buildToolUsage(tools) {
|
|
9956
10913
|
if (tools.length === 0) return "## Tool usage\n\nNo tools registered.";
|
|
9957
10914
|
const lines = ["## Tool usage"];
|
|
@@ -9971,6 +10928,78 @@ ${hint.trim()}`);
|
|
|
9971
10928
|
- **Batch ops:** Use \`replace\` with glob patterns for multi-file surgical changes
|
|
9972
10929
|
|
|
9973
10930
|
When unsure about a file's current state, read it first rather than assuming.`);
|
|
10931
|
+
const hasDelegate = tools.some((t2) => t2.name === "delegate");
|
|
10932
|
+
if (hasDelegate) {
|
|
10933
|
+
const delegateTool = tools.find((t2) => t2.name === "delegate");
|
|
10934
|
+
const enumValues = (() => {
|
|
10935
|
+
const role = delegateTool?.inputSchema?.properties?.role?.enum;
|
|
10936
|
+
return Array.isArray(role) ? role.filter((r) => typeof r === "string") : [];
|
|
10937
|
+
})();
|
|
10938
|
+
const roleList = enumValues.length > 0 ? enumValues.join(", ") : "(no roster configured)";
|
|
10939
|
+
lines.push(`
|
|
10940
|
+
## Delegation
|
|
10941
|
+
|
|
10942
|
+
You have a \`delegate\` tool that hands a discrete piece of work to a
|
|
10943
|
+
dedicated subagent (its own context, its own LLM call, its own budget
|
|
10944
|
+
cap) and waits for the result. Use it proactively when:
|
|
10945
|
+
|
|
10946
|
+
- **The task fans out naturally** \u2014 e.g. "audit these 5 files for
|
|
10947
|
+
security issues" splits cleanly into 5 parallel \`delegate\` calls,
|
|
10948
|
+
one per file or per role. Fire them through the provider's
|
|
10949
|
+
parallel-tool-call surface in the same turn.
|
|
10950
|
+
- **A specialized role exists** \u2014 the roster has tuned prompts and
|
|
10951
|
+
budgets for: ${roleList}. Reach for a role when the description
|
|
10952
|
+
matches your subtask; otherwise pass \`name\` + \`provider\` + \`model\`.
|
|
10953
|
+
- **A subtask would blow up your context** \u2014 long log analyses, large
|
|
10954
|
+
diff reviews, multi-file refactor plans. The subagent absorbs the
|
|
10955
|
+
reading cost and hands back a summary.
|
|
10956
|
+
- **You'd otherwise switch hats mid-turn** \u2014 instead of stopping a code
|
|
10957
|
+
fix to do a security pass, delegate the security pass.
|
|
10958
|
+
|
|
10959
|
+
### Scope it tight \u2014 narrow tasks succeed, broad tasks time out
|
|
10960
|
+
|
|
10961
|
+
A subagent has a finite iteration / tool-call budget (typically 50\u201380
|
|
10962
|
+
iterations, 200\u2013300 tool calls). Tasks that mention "ALL files" or "the
|
|
10963
|
+
entire codebase" reliably exhaust that budget without producing a clean
|
|
10964
|
+
answer \u2014 the delegate returns with \`stopReason: budget_exhausted\` and
|
|
10965
|
+
no useful output.
|
|
10966
|
+
|
|
10967
|
+
- \u274C BAD: \`"Analyze ALL .ts files in src/ for bugs"\`
|
|
10968
|
+
- \u274C BAD: \`"Audit the codebase for security issues"\`
|
|
10969
|
+
- \u274C BAD: \`"Plan a refactor of the whole project"\`
|
|
10970
|
+
- \u2705 GOOD: \`"Audit src/auth/session.ts for null-deref bugs in the login flow"\`
|
|
10971
|
+
- \u2705 GOOD: \`"Check packages/core/src/storage/*.ts for unhandled promise rejections (~6 files)"\`
|
|
10972
|
+
- \u2705 GOOD: \`"Plan a phased refactor of the InMemoryBridge transport (3 files in coordination/)"\`
|
|
10973
|
+
|
|
10974
|
+
If you need fleet-wide coverage, **fan out**: list the target files
|
|
10975
|
+
yourself first (one quick \`glob\` call), then fire one \`delegate\` per
|
|
10976
|
+
chunk of \u22645\u201310 files in parallel.
|
|
10977
|
+
|
|
10978
|
+
### Reading the result
|
|
10979
|
+
|
|
10980
|
+
\`delegate\` returns a structured object. Look at \`stopReason\`:
|
|
10981
|
+
|
|
10982
|
+
- \`end_turn\` \u2014 subagent finished cleanly, \`result\` has the answer.
|
|
10983
|
+
- \`budget_exhausted\` \u2014 task was too broad; \`partial.lastAssistantText\`
|
|
10984
|
+
has whatever it managed. Narrow the next try.
|
|
10985
|
+
- \`subagent_timeout\` / \`host_timeout\` \u2014 likewise partial; raise
|
|
10986
|
+
\`timeoutMs\` only if you have a reason to believe more time would help.
|
|
10987
|
+
- \`aborted\` \u2014 the user or another tool stopped this worker; don't retry
|
|
10988
|
+
silently.
|
|
10989
|
+
- \`error\` \u2014 infrastructure problem; surface it.
|
|
10990
|
+
|
|
10991
|
+
Stay in-process (no \`delegate\`) when:
|
|
10992
|
+
- The task is trivial or atomic.
|
|
10993
|
+
- The information needed is already in your context.
|
|
10994
|
+
- The user is mid-conversation and expects an immediate reply from you,
|
|
10995
|
+
not a research detour through a subagent.
|
|
10996
|
+
|
|
10997
|
+
\`delegate\` auto-promotes the host into director mode the first time
|
|
10998
|
+
it's called \u2014 you do not need to call any setup tool. For fine-grained
|
|
10999
|
+
control over a long-running fleet (spawn N workers, hand them tasks
|
|
11000
|
+
one by one, roll up results), use \`spawn_subagent\` + \`assign_task\` +
|
|
11001
|
+
\`await_tasks\` directly; \`delegate\` is the one-call shortcut.`);
|
|
11002
|
+
}
|
|
9974
11003
|
const hasContextManager = tools.some((t2) => t2.name === "context_manager");
|
|
9975
11004
|
if (hasContextManager) {
|
|
9976
11005
|
const maxCtx = this.opts.modelCapabilities?.maxContextTokens ?? 128e3;
|
|
@@ -10659,6 +11688,6 @@ function wrapApiForCapabilityCheck(plugin, api, log) {
|
|
|
10659
11688
|
});
|
|
10660
11689
|
}
|
|
10661
11690
|
|
|
10662
|
-
export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoCompactionMiddleware, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorBudgetError, DoneConditionChecker, EventBus, FLEET_ROSTER, FleetBus, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, RunController, SECURITY_SCANNER_AGENT, SelectiveCompactor, SessionAnalyzer, SessionError, SlashCommandRegistry, SpecDrivenDev, SpecParser, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskTracker, ToolError, ToolExecutor, ToolRegistry, WrongStackError, allServers, asBlocks, asText, atomicWrite, awsServer, blockServer, braveSearchServer, buildChildEnv, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, color, compileGlob, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createContextManagerTool, createDefaultPipelines, createMessage, createToolOutputSerializer, decryptConfigSecrets, detectNewlineStyle, encryptConfigSecrets, ensureDir, estimateTextTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatTodosList, githubServer, googleMapsServer, isAgentError, isConfigError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, loadPlugins, loadProjectModes, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, normalizeToLf, projectHash, renderPrometheus, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, sentinelServer, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState };
|
|
11691
|
+
export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorBudgetError, DirectorStateCheckpoint, DoneConditionChecker, EventBus, FLEET_ROSTER, FleetBus, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, RunController, SECURITY_SCANNER_AGENT, SelectiveCompactor, SessionAnalyzer, SessionError, SlashCommandRegistry, SpecDrivenDev, SpecParser, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskTracker, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMessage, createToolOutputSerializer, decryptConfigSecrets, detectNewlineStyle, emptyPlan, encryptConfigSecrets, ensureDir, estimateTextTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatPlan, formatTodosList, githubServer, googleMapsServer, isAgentError, isConfigError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, loadDirectorState, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, normalizeToLf, projectHash, removePlanItem, renderPrometheus, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, savePlan, saveTodosCheckpoint, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState };
|
|
10663
11692
|
//# sourceMappingURL=index.js.map
|
|
10664
11693
|
//# sourceMappingURL=index.js.map
|