@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.
Files changed (42) hide show
  1. package/dist/{agent-bridge-6KPqsFx6.d.ts → agent-bridge-DmBiCipY.d.ts} +1 -1
  2. package/dist/{compactor-B4mQZXf2.d.ts → compactor-DSl2FK7a.d.ts} +1 -1
  3. package/dist/{config-BU9f_5yH.d.ts → config-DXrqb41m.d.ts} +1 -1
  4. package/dist/{context-BmM2xGUZ.d.ts → context-u0bryklF.d.ts} +8 -0
  5. package/dist/coordination/index.d.ts +210 -12
  6. package/dist/coordination/index.js +941 -67
  7. package/dist/coordination/index.js.map +1 -1
  8. package/dist/defaults/index.d.ts +18 -18
  9. package/dist/defaults/index.js +953 -41
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/{events-BMNaEFZl.d.ts → events-B6Q03pTu.d.ts} +73 -1
  12. package/dist/execution/index.d.ts +11 -11
  13. package/dist/index.d.ts +61 -28
  14. package/dist/index.js +1077 -48
  15. package/dist/index.js.map +1 -1
  16. package/dist/infrastructure/index.d.ts +6 -6
  17. package/dist/kernel/index.d.ts +9 -9
  18. package/dist/kernel/index.js.map +1 -1
  19. package/dist/{mcp-servers-Dzgg4x1w.d.ts → mcp-servers-BA1Ofmfj.d.ts} +3 -3
  20. package/dist/models/index.d.ts +2 -2
  21. package/dist/{multi-agent-fmkRHtof.d.ts → multi-agent-BDfkxL5C.d.ts} +71 -3
  22. package/dist/observability/index.d.ts +2 -2
  23. package/dist/{path-resolver-DBjaoXFq.d.ts → path-resolver-Crkt8wTQ.d.ts} +2 -2
  24. package/dist/{plugin-DJk6LL8B.d.ts → plugin-CoYYZKdn.d.ts} +19 -6
  25. package/dist/{renderer-rk_1Swwc.d.ts → renderer-0A2ZEtca.d.ts} +1 -1
  26. package/dist/sdd/index.d.ts +3 -3
  27. package/dist/{secret-scrubber-CicHLN4G.d.ts → secret-scrubber-3TLUkiCV.d.ts} +1 -1
  28. package/dist/{secret-scrubber-DF88luOe.d.ts → secret-scrubber-CwYliRWd.d.ts} +1 -1
  29. package/dist/security/index.d.ts +20 -4
  30. package/dist/security/index.js +13 -1
  31. package/dist/security/index.js.map +1 -1
  32. package/dist/{selector-BbJqiEP4.d.ts → selector-BRqzvugb.d.ts} +1 -1
  33. package/dist/{session-reader-Drq8RvJu.d.ts → session-reader-C3x96CDR.d.ts} +1 -1
  34. package/dist/{skill-DhfSizKv.d.ts → skill-Bx8jxznf.d.ts} +1 -1
  35. package/dist/storage/index.d.ts +164 -6
  36. package/dist/storage/index.js +273 -1
  37. package/dist/storage/index.js.map +1 -1
  38. package/dist/{system-prompt-BC_8ypCG.d.ts → system-prompt-CG9jU5-5.d.ts} +9 -1
  39. package/dist/{tool-executor-CpuJPYm9.d.ts → tool-executor-CYdZdtno.d.ts} +4 -4
  40. package/dist/types/index.d.ts +15 -15
  41. package/dist/utils/index.d.ts +1 -1
  42. 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, path17, errors) {
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: path17 || "<root>",
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: path17 || "<root>",
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(path17, req), message: "required property missing" });
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(path17, key), errors);
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, `${path17}[${i}]`, errors));
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) return;
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: err instanceof Error ? err.message : String(err),
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: err instanceof Error ? err.message : String(err),
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
- const failed = result.status === "failed" || result.status === "timeout";
5972
- subagent.status = failed ? "error" : "idle";
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.coordinator.on("task.completed", (payload) => {
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(() => void 0);
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(() => void 0);
6270
- if (this.manifestPath) await this.writeManifest().catch(() => void 0);
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 { agent, events } = await opts.factory(ctx.config);
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.started", () => {
7540
+ events.on("tool.executed", (e) => {
6664
7541
  try {
6665
7542
  ctx.budget.recordToolCall();
6666
- } catch (e) {
6667
- onBudgetError(e);
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
- maxIterations: 50,
6773
- maxToolCalls: 200,
6774
- timeoutMs: 12e4
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
- maxIterations: 80,
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
- maxIterations: 60,
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
- maxIterations: 70,
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 path17 = opts.path ?? "/metrics";
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 === path17) {
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}${path17}`,
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