@wrongstack/core 0.10.3 → 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{agent-subagent-runner-DuRtZmhl.d.ts → agent-subagent-runner-DpZTLdBe.d.ts} +1 -1
  2. package/dist/{config-CJXBka2r.d.ts → config-BUEGM4JP.d.ts} +2 -0
  3. package/dist/coordination/index.d.ts +6 -6
  4. package/dist/coordination/index.js +414 -71
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/defaults/index.d.ts +8 -8
  7. package/dist/defaults/index.js +388 -57
  8. package/dist/defaults/index.js.map +1 -1
  9. package/dist/execution/index.d.ts +5 -5
  10. package/dist/execution/index.js +47 -5
  11. package/dist/execution/index.js.map +1 -1
  12. package/dist/extension/index.d.ts +2 -2
  13. package/dist/{index-DnBHrz2y.d.ts → index-pXJdVLe0.d.ts} +1 -1
  14. package/dist/{index-DAHEM_II.d.ts → index-ysfO_DlX.d.ts} +1 -1
  15. package/dist/index.d.ts +13 -13
  16. package/dist/index.js +635 -242
  17. package/dist/index.js.map +1 -1
  18. package/dist/infrastructure/index.d.ts +2 -2
  19. package/dist/infrastructure/index.js +2 -1
  20. package/dist/infrastructure/index.js.map +1 -1
  21. package/dist/kernel/index.d.ts +2 -2
  22. package/dist/{mcp-servers-BgINZzuo.d.ts → mcp-servers-BzB3r7_c.d.ts} +1 -1
  23. package/dist/{multi-agent-coordinator-CjG-FZKj.d.ts → multi-agent-coordinator-DOXSgtom.d.ts} +5 -1
  24. package/dist/{null-fleet-bus-B3WdVY9i.d.ts → null-fleet-bus-CAQDGsKc.d.ts} +59 -3
  25. package/dist/{plan-templates-BUP4o-ds.d.ts → plan-templates-BZMi-VpU.d.ts} +1 -1
  26. package/dist/sdd/index.d.ts +3 -3
  27. package/dist/sdd/index.js +40 -3
  28. package/dist/sdd/index.js.map +1 -1
  29. package/dist/storage/index.d.ts +2 -2
  30. package/dist/{tool-executor-D87-_5Of.d.ts → tool-executor-BAi4WI2d.d.ts} +1 -1
  31. package/dist/types/index.d.ts +3 -3
  32. package/dist/types/index.js +2 -1
  33. package/dist/types/index.js.map +1 -1
  34. package/dist/utils/index.d.ts +76 -1
  35. package/dist/utils/index.js +171 -5
  36. package/dist/utils/index.js.map +1 -1
  37. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import { randomUUID, randomBytes } from 'crypto';
2
- import * as fsp5 from 'fs/promises';
2
+ import * as fsp6 from 'fs/promises';
3
3
  import * as path4 from 'path';
4
+ import { isAbsolute, resolve } from 'path';
4
5
  import { EventEmitter } from 'events';
5
6
 
6
7
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -11,16 +12,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
11
12
  });
12
13
  async function atomicWrite(targetPath, content, opts = {}) {
13
14
  const dir = path4.dirname(targetPath);
14
- await fsp5.mkdir(dir, { recursive: true });
15
+ await fsp6.mkdir(dir, { recursive: true });
15
16
  const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
16
17
  try {
17
18
  if (typeof content === "string") {
18
- await fsp5.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
19
+ await fsp6.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
19
20
  } else {
20
- await fsp5.writeFile(tmp, content, { flag: "wx" });
21
+ await fsp6.writeFile(tmp, content, { flag: "wx" });
21
22
  }
22
23
  try {
23
- const fh = await fsp5.open(tmp, "r+");
24
+ const fh = await fsp6.open(tmp, "r+");
24
25
  try {
25
26
  await fh.sync();
26
27
  } finally {
@@ -30,37 +31,37 @@ async function atomicWrite(targetPath, content, opts = {}) {
30
31
  }
31
32
  let mode;
32
33
  try {
33
- const stat3 = await fsp5.stat(targetPath);
34
- mode = stat3.mode & 511;
34
+ const stat4 = await fsp6.stat(targetPath);
35
+ mode = stat4.mode & 511;
35
36
  } catch {
36
37
  mode = opts.mode;
37
38
  }
38
39
  if (mode !== void 0) {
39
- await fsp5.chmod(tmp, mode);
40
+ await fsp6.chmod(tmp, mode);
40
41
  }
41
42
  await renameWithRetry(tmp, targetPath);
42
43
  } catch (err) {
43
44
  try {
44
- await fsp5.unlink(tmp);
45
+ await fsp6.unlink(tmp);
45
46
  } catch {
46
47
  }
47
48
  throw err;
48
49
  }
49
50
  }
50
51
  async function ensureDir(dir) {
51
- await fsp5.mkdir(dir, { recursive: true });
52
+ await fsp6.mkdir(dir, { recursive: true });
52
53
  }
53
54
  var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
54
55
  async function renameWithRetry(from, to) {
55
56
  if (process.platform !== "win32") {
56
- await fsp5.rename(from, to);
57
+ await fsp6.rename(from, to);
57
58
  return;
58
59
  }
59
60
  const delays = [10, 25, 60, 120, 250];
60
61
  let lastErr;
61
62
  for (let i = 0; i <= delays.length; i++) {
62
63
  try {
63
- await fsp5.rename(from, to);
64
+ await fsp6.rename(from, to);
64
65
  return;
65
66
  } catch (err) {
66
67
  lastErr = err;
@@ -68,15 +69,89 @@ async function renameWithRetry(from, to) {
68
69
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
69
70
  throw err;
70
71
  }
71
- await new Promise((resolve) => setTimeout(resolve, delays[i]));
72
+ await new Promise((resolve2) => setTimeout(resolve2, delays[i]));
72
73
  }
73
74
  }
74
75
  throw lastErr;
75
76
  }
77
+
78
+ // src/coordination/large-answer-store.ts
79
+ var LargeAnswerStore = class {
80
+ /**
81
+ * Responses above this size (in characters) are stored out-of-context.
82
+ * Below this, the full answer is returned inline (no overhead).
83
+ * Default: 2000 chars ≈ 400-600 tokens.
84
+ */
85
+ sizeThreshold;
86
+ store = /* @__PURE__ */ new Map();
87
+ constructor(sizeThreshold = 2e3) {
88
+ this.sizeThreshold = sizeThreshold;
89
+ }
90
+ /**
91
+ * Store a value, returning a summary + key for inline use.
92
+ * If the value is below sizeThreshold, returns it as-is (no store entry).
93
+ */
94
+ storeAnswer(value) {
95
+ if (value === void 0 || value === null) {
96
+ return { summary: String(value), inline: true };
97
+ }
98
+ const serialized = typeof value === "string" ? value : JSON.stringify(value);
99
+ const size = serialized.length;
100
+ if (size <= this.sizeThreshold) {
101
+ return { summary: serialized.slice(0, 500), inline: true };
102
+ }
103
+ const key = `a-${hashStr(serialized)}`;
104
+ this.store.set(key, {
105
+ key,
106
+ value,
107
+ size,
108
+ storedAt: Date.now()
109
+ });
110
+ return {
111
+ key,
112
+ summary: `[stored: ${size} chars \u2014 use roll_up or ask_result tool to retrieve, key=${key}]`,
113
+ inline: false
114
+ };
115
+ }
116
+ /**
117
+ * Retrieve a previously stored answer by its key.
118
+ * Returns undefined if the key is unknown or the store was cleared.
119
+ */
120
+ retrieveAnswer(key) {
121
+ return this.store.get(key)?.value;
122
+ }
123
+ /**
124
+ * Check if a key exists in the store.
125
+ */
126
+ hasAnswer(key) {
127
+ return this.store.has(key);
128
+ }
129
+ /** Number of stored entries. */
130
+ get size() {
131
+ return this.store.size;
132
+ }
133
+ /** Total characters stored. */
134
+ get totalChars() {
135
+ let total = 0;
136
+ for (const e of this.store.values()) total += e.size;
137
+ return total;
138
+ }
139
+ /** Clear all stored entries. Call at the end of a director run. */
140
+ clear() {
141
+ this.store.clear();
142
+ }
143
+ };
144
+ function hashStr(s) {
145
+ let h = 5381;
146
+ for (let i = 0; i < s.length; i++) {
147
+ h = h * 33 ^ s.charCodeAt(i);
148
+ }
149
+ return (h >>> 0).toString(36);
150
+ }
76
151
  async function acquireDirectorStateLock(lockPath, processId = process.pid) {
77
152
  let existing;
78
153
  try {
79
- existing = await fsp5.readFile(lockPath, "utf8");
154
+ existing = await fsp6.readFile(lockPath, "utf8");
80
155
  } catch {
81
156
  }
82
157
  if (existing) {
@@ -100,7 +175,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
100
175
  }
101
176
  async function releaseDirectorStateLock(lockPath) {
102
177
  try {
103
- await fsp5.unlink(lockPath);
178
+ await fsp6.unlink(lockPath);
104
179
  } catch {
105
180
  }
106
181
  }
@@ -334,7 +409,7 @@ var InMemoryAgentBridge = class {
334
409
  );
335
410
  }
336
411
  this.inflightGuards.add(correlationId);
337
- return new Promise((resolve, reject) => {
412
+ return new Promise((resolve2, reject) => {
338
413
  const timer = setTimeout(() => {
339
414
  this.inflightGuards.delete(correlationId);
340
415
  this.pendingRequests.delete(correlationId);
@@ -346,7 +421,7 @@ var InMemoryAgentBridge = class {
346
421
  return;
347
422
  }
348
423
  this.pendingRequests.set(correlationId, {
349
- resolve,
424
+ resolve: resolve2,
350
425
  reject,
351
426
  timer
352
427
  });
@@ -707,6 +782,10 @@ var NICKNAME_POOL = {
707
782
  "pasteur": { name: "Pasteur", domain: "biology" },
708
783
  "hawking": { name: "Hawking", domain: "cosmology" },
709
784
  "sagan": { name: "Sagan", domain: "cosmology" },
785
+ // Exploration & navigation
786
+ "columbus": { name: "Columbus", domain: "exploration" },
787
+ "polo": { name: "Polo", domain: "exploration" },
788
+ "magellan": { name: "Magellan", domain: "exploration" },
710
789
  // Chemistry / materials
711
790
  "lavoisier": { name: "Lavoisier", domain: "chemistry" },
712
791
  "mendeleev": { name: "Mendeleev", domain: "chemistry" }
@@ -1083,12 +1162,12 @@ var SubagentBudget = class _SubagentBudget {
1083
1162
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
1084
1163
  return Promise.resolve("stop");
1085
1164
  }
1086
- return new Promise((resolve) => {
1165
+ return new Promise((resolve2) => {
1087
1166
  let resolved = false;
1088
1167
  const respond = (d) => {
1089
1168
  if (resolved) return;
1090
1169
  resolved = true;
1091
- resolve(d);
1170
+ resolve2(d);
1092
1171
  };
1093
1172
  const fallback = setTimeout(
1094
1173
  () => respond("stop"),
@@ -3686,7 +3765,7 @@ var FLEET_ROSTER_WITHACP = {
3686
3765
  };
3687
3766
 
3688
3767
  // src/coordination/multi-agent-coordinator.ts
3689
- var DefaultMultiAgentCoordinator = class extends EventEmitter {
3768
+ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends EventEmitter {
3690
3769
  coordinatorId;
3691
3770
  config;
3692
3771
  runner;
@@ -3701,8 +3780,12 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
3701
3780
  * names) to memorable ones here — that's what surfaces in the fleet monitor.
3702
3781
  */
3703
3782
  usedNicknames = /* @__PURE__ */ new Set();
3783
+ /** Maps subagentId → nickname key (e.g. 'einstein'). Used to free the slot on remove(). */
3784
+ subagentNicknames = /* @__PURE__ */ new Map();
3704
3785
  pendingTasks = [];
3705
3786
  completedResults = [];
3787
+ /** Prevents completedResults from growing unbounded in long-running coordinators. */
3788
+ static MAX_COMPLETED_RESULTS = 1e4;
3706
3789
  totalIterations = 0;
3707
3790
  inFlight = 0;
3708
3791
  /**
@@ -3757,7 +3840,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
3757
3840
  * Explicit, human-chosen names — including nicknames already assigned by
3758
3841
  * `Director.spawn()` — are left untouched, so this never double-assigns.
3759
3842
  */
3760
- withNickname(subagent) {
3843
+ withNickname(subagent, subagentId) {
3761
3844
  const role = subagent.role ?? "subagent";
3762
3845
  const name = subagent.name?.trim() ?? "";
3763
3846
  const isPlaceholder = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
@@ -3765,11 +3848,12 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
3765
3848
  const nickname = assignNickname(role, this.usedNicknames);
3766
3849
  const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
3767
3850
  this.usedNicknames.add(baseKey);
3851
+ this.subagentNicknames.set(subagentId, baseKey);
3768
3852
  return { ...subagent, name: nickname };
3769
3853
  }
3770
3854
  async spawn(subagent) {
3771
- subagent = this.withNickname(subagent);
3772
3855
  const id = subagent.id || randomUUID();
3856
+ subagent = this.withNickname(subagent, id);
3773
3857
  if (this.subagents.has(id)) {
3774
3858
  throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
3775
3859
  }
@@ -3913,7 +3997,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
3913
3997
  taskIds.map((id) => {
3914
3998
  const cached = this.completedResults.find((r) => r.taskId === id);
3915
3999
  if (cached) return cached;
3916
- return new Promise((resolve, reject) => {
4000
+ return new Promise((resolve2, reject) => {
3917
4001
  const timeout = setTimeout(() => {
3918
4002
  this.off("task.completed", handler);
3919
4003
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -3922,7 +4006,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
3922
4006
  if (result.taskId === id) {
3923
4007
  clearTimeout(timeout);
3924
4008
  this.off("task.completed", handler);
3925
- resolve(result);
4009
+ resolve2(result);
3926
4010
  }
3927
4011
  };
3928
4012
  this.on("task.completed", handler);
@@ -4211,6 +4295,12 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
4211
4295
  }
4212
4296
  recordCompletion(result) {
4213
4297
  this.completedResults.push(result);
4298
+ if (this.completedResults.length > _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS) {
4299
+ this.completedResults.splice(
4300
+ 0,
4301
+ this.completedResults.length - _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS
4302
+ );
4303
+ }
4214
4304
  this.totalIterations += result.iterations;
4215
4305
  if (this.inFlight > 0) {
4216
4306
  this.inFlight--;
@@ -4280,7 +4370,29 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
4280
4370
  }
4281
4371
  this.subagents.delete(subagentId);
4282
4372
  this.terminating.delete(subagentId);
4373
+ const nicknameKey = this.subagentNicknames.get(subagentId);
4374
+ if (nicknameKey) {
4375
+ this.usedNicknames.delete(nicknameKey);
4376
+ this.subagentNicknames.delete(subagentId);
4377
+ }
4378
+ const orphaned = this.pendingTasks.filter((t) => t.subagentId === subagentId);
4283
4379
  this.pendingTasks = this.pendingTasks.filter((t) => t.subagentId !== subagentId);
4380
+ for (const t of orphaned) {
4381
+ const synthetic = {
4382
+ subagentId,
4383
+ taskId: t.id,
4384
+ status: "stopped",
4385
+ error: {
4386
+ kind: "aborted_by_parent",
4387
+ message: `Subagent "${subagentId}" was removed while task "${t.id}" was pending`,
4388
+ retryable: false
4389
+ },
4390
+ iterations: 0,
4391
+ toolCalls: 0,
4392
+ durationMs: 0
4393
+ };
4394
+ this.recordCompletion(synthetic);
4395
+ }
4284
4396
  this.fleetBus?.emit({
4285
4397
  subagentId,
4286
4398
  ts: Date.now(),
@@ -4528,9 +4640,12 @@ function makeSpawnTool(director, roster) {
4528
4640
  provider: { type: "string", description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.' },
4529
4641
  model: { type: "string", description: "Model id within the provider. Defaults to the leader model when omitted." },
4530
4642
  systemPromptOverride: { type: "string", description: "Extra prompt text appended after the role-base prompt." },
4531
- maxIterations: { type: "number" },
4532
- maxToolCalls: { type: "number" },
4533
- maxCostUsd: { type: "number" }
4643
+ maxIterations: { type: "number", minimum: 1 },
4644
+ maxToolCalls: { type: "number", minimum: 1 },
4645
+ maxCostUsd: { type: "number", minimum: 0 },
4646
+ timeoutMs: { type: "number", minimum: 1, description: "Hard wall-clock cap in milliseconds. Defaults to none (idle timeout is the default reaper)." },
4647
+ idleTimeoutMs: { type: "number", minimum: 1, description: "Idle timeout in ms: reap the subagent after this long with no activity. Resets on every iteration/tool call. Default is role/coordinator-specific." },
4648
+ maxTokens: { type: "number", minimum: 1, description: "Maximum total tokens (input + output) the subagent may use." }
4534
4649
  },
4535
4650
  required: []
4536
4651
  };
@@ -4576,6 +4691,9 @@ function makeSpawnTool(director, roster) {
4576
4691
  if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
4577
4692
  if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
4578
4693
  if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
4694
+ if (typeof i.timeoutMs === "number") cfg.timeoutMs = i.timeoutMs;
4695
+ if (typeof i.idleTimeoutMs === "number") cfg.idleTimeoutMs = i.idleTimeoutMs;
4696
+ if (typeof i.maxTokens === "number") cfg.maxTokens = i.maxTokens;
4579
4697
  try {
4580
4698
  const subagentId = await director.spawn(cfg);
4581
4699
  return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name, role: cfg.role };
@@ -4603,10 +4721,10 @@ function makeAssignTool(director) {
4603
4721
  const inputSchema = {
4604
4722
  type: "object",
4605
4723
  properties: {
4606
- subagentId: { type: "string", description: "Target subagent id. Required." },
4607
- description: { type: "string", description: "The task in natural language \u2014 what you want this subagent to do." },
4608
- maxToolCalls: { type: "number", description: "Optional per-task tool-call budget override." },
4609
- timeoutMs: { type: "number", description: "Optional per-task timeout in ms." }
4724
+ subagentId: { type: "string", minLength: 1, description: "Target subagent id. Required." },
4725
+ description: { type: "string", minLength: 1, description: "The task in natural language \u2014 what you want this subagent to do." },
4726
+ maxToolCalls: { type: "number", minimum: 1, description: "Optional per-task tool-call budget override." },
4727
+ timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
4610
4728
  },
4611
4729
  required: ["subagentId", "description"]
4612
4730
  };
@@ -4647,9 +4765,9 @@ function makeAskTool(director) {
4647
4765
  inputSchema: {
4648
4766
  type: "object",
4649
4767
  properties: {
4650
- subagentId: { type: "string", description: "Subagent to ask. Must be a previously spawned id." },
4651
- question: { type: "string", description: "The question or instruction." },
4652
- timeoutMs: { type: "number", description: "Optional timeout in ms (default 30s)." }
4768
+ subagentId: { type: "string", minLength: 1, description: "Subagent to ask. Must be a previously spawned id." },
4769
+ question: { type: "string", minLength: 1, description: "The question or instruction." },
4770
+ timeoutMs: { type: "number", minimum: 1, description: "Optional timeout in ms (default 30s)." }
4653
4771
  },
4654
4772
  required: ["subagentId", "question"]
4655
4773
  },
@@ -4657,13 +4775,49 @@ function makeAskTool(director) {
4657
4775
  const i = input;
4658
4776
  try {
4659
4777
  const answer = await director.ask(i.subagentId, { question: i.question }, i.timeoutMs);
4660
- return { ok: true, answer };
4778
+ const stored = director.largeAnswerStore.storeAnswer(answer);
4779
+ if (stored.inline) {
4780
+ return { ok: true, answer: stored.summary };
4781
+ }
4782
+ return {
4783
+ ok: true,
4784
+ answer: stored.summary,
4785
+ _answerKey: stored.key,
4786
+ _hint: "Response was large and stored. Use ask_result with the key to retrieve it."
4787
+ };
4661
4788
  } catch (err) {
4662
4789
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
4663
4790
  }
4664
4791
  }
4665
4792
  };
4666
4793
  }
4794
+ function makeAskResultTool(director) {
4795
+ return {
4796
+ name: "ask_result",
4797
+ description: "Retrieve a large `ask_subagent` response that was stored out-of-context (>2K chars). Returns the full stored value.",
4798
+ permission: "auto",
4799
+ mutating: false,
4800
+ inputSchema: {
4801
+ type: "object",
4802
+ properties: {
4803
+ key: {
4804
+ type: "string",
4805
+ minLength: 1,
4806
+ description: "The `_answerKey` returned by `ask_subagent` for a large response."
4807
+ }
4808
+ },
4809
+ required: ["key"]
4810
+ },
4811
+ async execute(input) {
4812
+ const i = input;
4813
+ const value = director.largeAnswerStore.retrieveAnswer(i.key);
4814
+ if (value === void 0) {
4815
+ return { ok: false, error: `No stored answer found for key "${i.key}" \u2014 it may have been cleared or the key is invalid.` };
4816
+ }
4817
+ return { ok: true, value };
4818
+ }
4819
+ };
4820
+ }
4667
4821
  function makeRollUpTool(director) {
4668
4822
  return {
4669
4823
  name: "roll_up",
@@ -4817,6 +4971,7 @@ function makeCollabDebugTool(director) {
4817
4971
  },
4818
4972
  timeoutMs: {
4819
4973
  type: "number",
4974
+ minimum: 1,
4820
4975
  description: "Timeout in ms. Default: 600000 (10 minutes)."
4821
4976
  }
4822
4977
  },
@@ -4896,6 +5051,122 @@ function makeWorkCompleteTool(director) {
4896
5051
  }
4897
5052
  };
4898
5053
  }
5054
+ var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
5055
+ var IS_WINDOWS = process.platform === "win32";
5056
+ var SEP = IS_WINDOWS ? "\\" : "/";
5057
+ function isGlob(p) {
5058
+ for (const c of p) {
5059
+ if (GLOB_CHARS.has(c)) return true;
5060
+ }
5061
+ return false;
5062
+ }
5063
+ function globToRegex(pat) {
5064
+ let i = 0, re = "^";
5065
+ while (i < pat.length) {
5066
+ const c = pat[i];
5067
+ if (c === "*") {
5068
+ if (pat[i + 1] === "*") {
5069
+ re += ".*";
5070
+ i += 2;
5071
+ if (pat[i] === "/") i++;
5072
+ } else {
5073
+ re += "[^/\\\\]*";
5074
+ i++;
5075
+ }
5076
+ } else if (c === "?") {
5077
+ re += "[^/\\\\]";
5078
+ i++;
5079
+ } else if (c === "[") {
5080
+ let cls = "[";
5081
+ i++;
5082
+ if (pat[i] === "!" || pat[i] === "^") {
5083
+ cls += "^";
5084
+ i++;
5085
+ }
5086
+ while (i < pat.length && pat[i] !== "]") {
5087
+ const ch = pat[i] ?? "";
5088
+ if (ch === "\\") cls += "\\\\";
5089
+ else if (ch === "]" || ch === "^") cls += `\\${ch}`;
5090
+ else cls += ch;
5091
+ i++;
5092
+ }
5093
+ cls += "]";
5094
+ re += cls;
5095
+ i++;
5096
+ } else {
5097
+ re += c.replace(/[.+^${}()|\\]/g, "\\$&");
5098
+ i++;
5099
+ }
5100
+ }
5101
+ return new RegExp(re + "$");
5102
+ }
5103
+ function baseDir(pat) {
5104
+ let i = pat.length - 1;
5105
+ while (i >= 0 && !GLOB_CHARS.has(pat[i]) && pat[i] !== SEP && pat[i] !== "/") i--;
5106
+ const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
5107
+ return cut < 0 ? "." : pat.slice(0, cut);
5108
+ }
5109
+ async function expandGlob(pattern) {
5110
+ if (!isGlob(pattern)) return [pattern];
5111
+ const results = /* @__PURE__ */ new Set();
5112
+ const abs = isAbsolute(pattern);
5113
+ const base = abs ? baseDir(pattern) : baseDir(pattern);
5114
+ const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
5115
+ async function walk(dir, pat) {
5116
+ let entries;
5117
+ try {
5118
+ entries = await fsp6.readdir(dir);
5119
+ } catch {
5120
+ return;
5121
+ }
5122
+ const firstGlob = pat.search(/[*?[\[]/);
5123
+ if (firstGlob < 0) {
5124
+ const re = globToRegex(pat);
5125
+ for (const e of entries) {
5126
+ if (re.test(e)) {
5127
+ const full = `${dir}${SEP}${e}`;
5128
+ results.add(abs ? resolve(full) : full);
5129
+ }
5130
+ }
5131
+ return;
5132
+ }
5133
+ const before = pat.slice(0, firstGlob);
5134
+ const rest = pat.slice(firstGlob);
5135
+ if (before.endsWith("**")) {
5136
+ await walk(dir, rest);
5137
+ for (const e of entries) {
5138
+ const full = `${dir}${SEP}${e}`;
5139
+ try {
5140
+ const stat4 = await fsp6.stat(full);
5141
+ if (stat4.isDirectory()) await walk(full, rest);
5142
+ } catch {
5143
+ }
5144
+ }
5145
+ } else if (before === "") {
5146
+ const re = globToRegex(rest);
5147
+ for (const e of entries) {
5148
+ if (re.test(e)) {
5149
+ const full = `${dir}${SEP}${e}`;
5150
+ results.add(abs ? resolve(full) : full);
5151
+ }
5152
+ }
5153
+ } else {
5154
+ const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
5155
+ if (entries.includes(seg)) {
5156
+ const full = `${dir}${SEP}${seg}`;
5157
+ try {
5158
+ const stat4 = await fsp6.stat(full);
5159
+ if (stat4.isDirectory()) await walk(full, rest);
5160
+ } catch {
5161
+ }
5162
+ }
5163
+ }
5164
+ }
5165
+ await walk(base === "." ? "." : base, relPat);
5166
+ return [...results];
5167
+ }
5168
+
5169
+ // src/coordination/collab-debug.ts
4899
5170
  var DirectorAlertLevel = /* @__PURE__ */ ((DirectorAlertLevel2) => {
4900
5171
  DirectorAlertLevel2["WARNING"] = "warning";
4901
5172
  DirectorAlertLevel2["CRITICAL"] = "critical";
@@ -4960,9 +5231,9 @@ var CollabSession = class extends EventEmitter {
4960
5231
  }
4961
5232
  async buildSnapshot() {
4962
5233
  if (this.snapshot.files.length > 0) return this.snapshot;
4963
- for (const filePath of this.options.targetPaths) {
5234
+ for (const filePath of (await Promise.all(this.options.targetPaths.map((p) => expandGlob(p)))).flat()) {
4964
5235
  try {
4965
- const content = await fsp5.readFile(filePath, "utf8");
5236
+ const content = await fsp6.readFile(filePath, "utf8");
4966
5237
  const ext = filePath.split(".").pop() ?? "";
4967
5238
  const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
4968
5239
  this.snapshot.files.push({ path: filePath, content, language });
@@ -5016,7 +5287,7 @@ var CollabSession = class extends EventEmitter {
5016
5287
  reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
5017
5288
  }, this.timeoutMs);
5018
5289
  });
5019
- let results;
5290
+ let results = null;
5020
5291
  try {
5021
5292
  results = await Promise.race([
5022
5293
  Promise.all([
@@ -5447,7 +5718,7 @@ var FleetContextOverflowError = class extends Error {
5447
5718
  this.observed = observed;
5448
5719
  }
5449
5720
  };
5450
- var Director = class {
5721
+ var Director = class _Director {
5451
5722
  /** Alias for the ICoordinator contract. `id` is retained for backward compatibility. */
5452
5723
  get coordinatorId() {
5453
5724
  return this.id;
@@ -5505,6 +5776,8 @@ var Director = class {
5505
5776
  * coordinator already fired the event — `awaitTasks(['t-1'])` after
5506
5777
  * t-1 finished should resolve immediately, not hang. */
5507
5778
  completed = /* @__PURE__ */ new Map();
5779
+ /** Prevents the completed Map from growing unbounded in long-running directors. */
5780
+ static MAX_COMPLETED = 1e4;
5508
5781
  /** Per-subagent provider/model metadata, captured at spawn time so the
5509
5782
  * FleetUsageAggregator's metaLookup can surface readable rows. */
5510
5783
  subagentMeta = /* @__PURE__ */ new Map();
@@ -5584,6 +5857,8 @@ var Director = class {
5584
5857
  _leaderBtwNotes = [];
5585
5858
  /** Active collab sessions tracked by sessionId (see spawnCollab). */
5586
5859
  _activeCollabSessions = /* @__PURE__ */ new Map();
5860
+ /** Prevents large `ask_subagent` answers from bloating the leader's context window. */
5861
+ largeAnswerStore;
5587
5862
  constructor(opts) {
5588
5863
  this.id = opts.config.coordinatorId || randomUUID();
5589
5864
  this.manifestPath = opts.manifestPath;
@@ -5612,7 +5887,7 @@ var Director = class {
5612
5887
  }, opts.checkpointDebounceMs ?? 250) : null;
5613
5888
  this.fleetManager = opts.fleetManager;
5614
5889
  if (this.sharedScratchpadPath) {
5615
- void fsp5.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
5890
+ void fsp6.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
5616
5891
  (err) => this.logShutdownError("shared_scratchpad_mkdir", err)
5617
5892
  );
5618
5893
  }
@@ -5644,6 +5919,11 @@ var Director = class {
5644
5919
  this.taskCompletedListener = (payload) => {
5645
5920
  const r = payload.result;
5646
5921
  this.completed.set(r.taskId, r);
5922
+ if (this.completed.size > _Director.MAX_COMPLETED) {
5923
+ const toDelete = this.completed.size - _Director.MAX_COMPLETED;
5924
+ const keys = [...this.completed.keys()].slice(0, toDelete);
5925
+ for (const k of keys) this.completed.delete(k);
5926
+ }
5647
5927
  const waiter = this.taskWaiters.get(r.taskId);
5648
5928
  if (waiter) {
5649
5929
  waiter.resolve(r);
@@ -5750,6 +6030,7 @@ var Director = class {
5750
6030
  payload.extend(extra);
5751
6031
  });
5752
6032
  });
6033
+ this.largeAnswerStore = new LargeAnswerStore(2e3);
5753
6034
  }
5754
6035
  /**
5755
6036
  * Record a granted budget extension and broadcast it on the FleetBus so
@@ -6115,7 +6396,7 @@ var Director = class {
6115
6396
  })),
6116
6397
  usage: this.usage.snapshot()
6117
6398
  };
6118
- await fsp5.mkdir(path4.dirname(this.manifestPath), { recursive: true });
6399
+ await fsp6.mkdir(path4.dirname(this.manifestPath), { recursive: true });
6119
6400
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
6120
6401
  return this.manifestPath;
6121
6402
  }
@@ -6230,11 +6511,11 @@ var Director = class {
6230
6511
  if (cached) return cached;
6231
6512
  const existing = this.taskWaiters.get(id);
6232
6513
  if (existing) return existing.promise;
6233
- let resolve;
6514
+ let resolve2;
6234
6515
  const promise = new Promise((res) => {
6235
- resolve = res;
6516
+ resolve2 = res;
6236
6517
  });
6237
- this.taskWaiters.set(id, { promise, resolve });
6518
+ this.taskWaiters.set(id, { promise, resolve: resolve2 });
6238
6519
  return promise;
6239
6520
  })
6240
6521
  );
@@ -6247,7 +6528,24 @@ var Director = class {
6247
6528
  }
6248
6529
  async remove(subagentId) {
6249
6530
  await this.coordinator.remove(subagentId);
6531
+ const bridge = this.subagentBridges.get(subagentId);
6532
+ if (bridge) {
6533
+ await bridge.stop();
6534
+ this.subagentBridges.delete(subagentId);
6535
+ }
6250
6536
  this.usage.removeSubagent(subagentId);
6537
+ if (this.fleetManager) {
6538
+ this.fleetManager.removeSubagent(subagentId);
6539
+ } else {
6540
+ const entry = this.manifestEntries.get(subagentId);
6541
+ if (entry?.name) {
6542
+ const nicknameKey = entry.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
6543
+ this._usedNicknames.delete(nicknameKey);
6544
+ }
6545
+ }
6546
+ this.manifestEntries.delete(subagentId);
6547
+ this.taskOwners.delete(subagentId);
6548
+ this.taskDescriptions.delete(subagentId);
6251
6549
  }
6252
6550
  status() {
6253
6551
  const base = this.coordinator.getStatus();
@@ -6303,7 +6601,7 @@ var Director = class {
6303
6601
  const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
6304
6602
  let raw;
6305
6603
  try {
6306
- raw = await fsp5.readFile(filePath, "utf8");
6604
+ raw = await fsp6.readFile(filePath, "utf8");
6307
6605
  } catch {
6308
6606
  return null;
6309
6607
  }
@@ -6409,6 +6707,7 @@ var Director = class {
6409
6707
  makeAssignTool(this),
6410
6708
  makeAwaitTasksTool(this),
6411
6709
  makeAskTool(this),
6710
+ makeAskResultTool(this),
6412
6711
  makeRollUpTool(this),
6413
6712
  makeTerminateTool(this),
6414
6713
  makeTerminateAllTool(this),
@@ -6491,15 +6790,33 @@ function createDelegateTool(opts) {
6491
6790
  },
6492
6791
  timeoutMs: {
6493
6792
  type: "number",
6793
+ minimum: 1,
6494
6794
  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.`
6495
6795
  },
6496
6796
  maxIterations: {
6497
6797
  type: "number",
6798
+ minimum: 1,
6498
6799
  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)."
6499
6800
  },
6500
6801
  maxToolCalls: {
6501
6802
  type: "number",
6803
+ minimum: 1,
6502
6804
  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)."
6805
+ },
6806
+ idleTimeoutMs: {
6807
+ type: "number",
6808
+ minimum: 1,
6809
+ description: "Idle timeout in ms: reap the subagent after this long with no activity. Resets on every iteration/tool call. Unset = use the role/coordinator default."
6810
+ },
6811
+ maxTokens: {
6812
+ type: "number",
6813
+ minimum: 1,
6814
+ description: "Maximum total tokens (input + output) the subagent may use. Unset = use the role/coordinator default."
6815
+ },
6816
+ maxCostUsd: {
6817
+ type: "number",
6818
+ minimum: 0,
6819
+ description: "Maximum estimated USD cost the subagent may incur. Unset = use the role/coordinator default."
6503
6820
  }
6504
6821
  },
6505
6822
  required: ["task"]
@@ -6557,12 +6874,21 @@ function createDelegateTool(opts) {
6557
6874
  };
6558
6875
  cfg = applyRosterBudget({ ...cfg, name: i.name });
6559
6876
  }
6560
- if (typeof i.maxIterations === "number" && i.maxIterations > 0) {
6877
+ if (typeof i.maxIterations === "number") {
6561
6878
  cfg.maxIterations = i.maxIterations;
6562
6879
  }
6563
- if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
6880
+ if (typeof i.maxToolCalls === "number") {
6564
6881
  cfg.maxToolCalls = i.maxToolCalls;
6565
6882
  }
6883
+ if (typeof i.idleTimeoutMs === "number") {
6884
+ cfg.idleTimeoutMs = i.idleTimeoutMs;
6885
+ }
6886
+ if (typeof i.maxTokens === "number") {
6887
+ cfg.maxTokens = i.maxTokens;
6888
+ }
6889
+ if (typeof i.maxCostUsd === "number") {
6890
+ cfg.maxCostUsd = i.maxCostUsd;
6891
+ }
6566
6892
  const SUBAGENT_TIMEOUT_BUFFER_MS = opts.subagentTimeoutBufferMs ?? 6e4;
6567
6893
  if (!cfg.timeoutMs) {
6568
6894
  cfg.timeoutMs = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
@@ -6574,7 +6900,7 @@ function createDelegateTool(opts) {
6574
6900
  subagentId
6575
6901
  });
6576
6902
  const dir = director;
6577
- const result = await new Promise((resolve) => {
6903
+ const result = await new Promise((resolve2) => {
6578
6904
  let settled = false;
6579
6905
  let timer;
6580
6906
  const finish = (value) => {
@@ -6584,7 +6910,7 @@ function createDelegateTool(opts) {
6584
6910
  offTool();
6585
6911
  offIter();
6586
6912
  offProgress();
6587
- resolve(value);
6913
+ resolve2(value);
6588
6914
  };
6589
6915
  const arm = () => {
6590
6916
  if (timer) clearTimeout(timer);
@@ -6731,7 +7057,7 @@ async function readSubagentPartial(opts, subagentId) {
6731
7057
  candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
6732
7058
  } else {
6733
7059
  try {
6734
- const entries = await fsp5.readdir(opts.sessionsRoot, { withFileTypes: true });
7060
+ const entries = await fsp6.readdir(opts.sessionsRoot, { withFileTypes: true });
6735
7061
  for (const entry of entries) {
6736
7062
  if (entry.isDirectory()) {
6737
7063
  candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
@@ -6744,7 +7070,7 @@ async function readSubagentPartial(opts, subagentId) {
6744
7070
  for (const file of candidates) {
6745
7071
  let raw;
6746
7072
  try {
6747
- raw = await fsp5.readFile(file, "utf8");
7073
+ raw = await fsp6.readFile(file, "utf8");
6748
7074
  } catch {
6749
7075
  continue;
6750
7076
  }
@@ -7063,7 +7389,7 @@ var DefaultSessionStore = class {
7063
7389
  const file = path4.join(shardDir, `${id}.jsonl`);
7064
7390
  let handle;
7065
7391
  try {
7066
- handle = await fsp5.open(file, "a", 384);
7392
+ handle = await fsp6.open(file, "a", 384);
7067
7393
  } catch (err) {
7068
7394
  throw new Error(
7069
7395
  `Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
@@ -7087,7 +7413,7 @@ var DefaultSessionStore = class {
7087
7413
  const data = await this.load(id);
7088
7414
  let handle;
7089
7415
  try {
7090
- handle = await fsp5.open(file, "a", 384);
7416
+ handle = await fsp6.open(file, "a", 384);
7091
7417
  } catch (err) {
7092
7418
  throw new Error(
7093
7419
  `Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
@@ -7116,7 +7442,7 @@ var DefaultSessionStore = class {
7116
7442
  }
7117
7443
  async load(id) {
7118
7444
  const file = this.sessionPath(id, ".jsonl");
7119
- const raw = await fsp5.readFile(file, "utf8");
7445
+ const raw = await fsp6.readFile(file, "utf8");
7120
7446
  const lines = raw.split("\n").filter((l) => l.trim());
7121
7447
  const events = [];
7122
7448
  for (const line of lines) {
@@ -7151,7 +7477,7 @@ var DefaultSessionStore = class {
7151
7477
  /** Recursively collect all session IDs from shard subdirectories. */
7152
7478
  async collectSessionIds(dir) {
7153
7479
  const ids = [];
7154
- const entries = await fsp5.readdir(dir, { withFileTypes: true });
7480
+ const entries = await fsp6.readdir(dir, { withFileTypes: true });
7155
7481
  for (const entry of entries) {
7156
7482
  const full = path4.join(dir, entry.name);
7157
7483
  if (entry.isDirectory()) {
@@ -7165,12 +7491,12 @@ var DefaultSessionStore = class {
7165
7491
  async summaryFor(id) {
7166
7492
  const manifest = this.sessionPath(id, ".summary.json");
7167
7493
  try {
7168
- const raw = await fsp5.readFile(manifest, "utf8");
7494
+ const raw = await fsp6.readFile(manifest, "utf8");
7169
7495
  return JSON.parse(raw);
7170
7496
  } catch {
7171
7497
  const full = this.sessionPath(id, ".jsonl");
7172
- const stat3 = await fsp5.stat(full);
7173
- const summary = await this.summarize(id, stat3.mtime.toISOString());
7498
+ const stat4 = await fsp6.stat(full);
7499
+ const summary = await this.summarize(id, stat4.mtime.toISOString());
7174
7500
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
7175
7501
  console.warn(
7176
7502
  `[session-store] Failed to write manifest for "${id}":`,
@@ -7181,8 +7507,8 @@ var DefaultSessionStore = class {
7181
7507
  }
7182
7508
  }
7183
7509
  async delete(id) {
7184
- await fsp5.unlink(this.sessionPath(id, ".jsonl"));
7185
- await fsp5.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
7510
+ await fsp6.unlink(this.sessionPath(id, ".jsonl"));
7511
+ await fsp6.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
7186
7512
  }
7187
7513
  async clearHistory(id) {
7188
7514
  await this.ensureShardDir(id);
@@ -7196,8 +7522,8 @@ var DefaultSessionStore = class {
7196
7522
  provider: "unknown"
7197
7523
  })}
7198
7524
  `;
7199
- await fsp5.writeFile(file, record, "utf8");
7200
- await fsp5.unlink(meta).catch(() => void 0);
7525
+ await fsp6.writeFile(file, record, "utf8");
7526
+ await fsp6.unlink(meta).catch(() => void 0);
7201
7527
  }
7202
7528
  async summarize(id, mtime) {
7203
7529
  try {
@@ -7383,7 +7709,7 @@ var FileSessionWriter = class {
7383
7709
  `;
7384
7710
  try {
7385
7711
  if (this.filePath) {
7386
- await fsp5.writeFile(this.filePath, record, { flag: "a", mode: 384 });
7712
+ await fsp6.writeFile(this.filePath, record, { flag: "a", mode: 384 });
7387
7713
  }
7388
7714
  } catch {
7389
7715
  }
@@ -7476,7 +7802,7 @@ var FileSessionWriter = class {
7476
7802
  }
7477
7803
  async truncateToCheckpoint(targetPromptIndex) {
7478
7804
  if (!this.filePath) return 0;
7479
- const raw = await fsp5.readFile(this.filePath, "utf8");
7805
+ const raw = await fsp6.readFile(this.filePath, "utf8");
7480
7806
  const lines = raw.split("\n");
7481
7807
  const kept = [];
7482
7808
  let removedCount = 0;
@@ -7514,13 +7840,13 @@ var FileSessionWriter = class {
7514
7840
  }
7515
7841
  const truncated = kept.join("\n");
7516
7842
  const tmpPath = `${this.filePath}.rewind.tmp`;
7517
- await fsp5.writeFile(tmpPath, truncated + "\n", "utf8");
7843
+ await fsp6.writeFile(tmpPath, truncated + "\n", "utf8");
7518
7844
  try {
7519
7845
  await this.handle.close();
7520
- await fsp5.rename(tmpPath, this.filePath);
7521
- this.handle = await fsp5.open(this.filePath, "a", 384);
7846
+ await fsp6.rename(tmpPath, this.filePath);
7847
+ this.handle = await fsp6.open(this.filePath, "a", 384);
7522
7848
  } catch (err) {
7523
- await fsp5.unlink(tmpPath).catch(() => void 0);
7849
+ await fsp6.unlink(tmpPath).catch(() => void 0);
7524
7850
  throw err;
7525
7851
  }
7526
7852
  await this.append({
@@ -7546,7 +7872,7 @@ var FileSessionWriter = class {
7546
7872
  provider: this.meta.provider ?? "unknown"
7547
7873
  })}
7548
7874
  `;
7549
- await fsp5.writeFile(this.filePath, record, "utf8");
7875
+ await fsp6.writeFile(this.filePath, record, "utf8");
7550
7876
  }
7551
7877
  /**
7552
7878
  * Idea #1 — write an in-flight marker. The agent loop should call
@@ -7892,7 +8218,7 @@ var FleetManager = class {
7892
8218
  })),
7893
8219
  usage: this.usage.snapshot()
7894
8220
  };
7895
- await fsp5.mkdir(path4.dirname(this.manifestPath), { recursive: true });
8221
+ await fsp6.mkdir(path4.dirname(this.manifestPath), { recursive: true });
7896
8222
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
7897
8223
  return this.manifestPath;
7898
8224
  }
@@ -8002,6 +8328,23 @@ var FleetManager = class {
8002
8328
  }));
8003
8329
  return { pending, live: [] };
8004
8330
  }
8331
+ /**
8332
+ * Clean up all fleet-manager state associated with a removed subagent:
8333
+ * - Frees the nickname slot so the same name can be reused
8334
+ * - Removes any pending tasks for this subagent
8335
+ */
8336
+ removeSubagent(subagentId) {
8337
+ const entry = this.manifestEntries.get(subagentId);
8338
+ if (entry?.name) {
8339
+ const nicknameKey = entry.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
8340
+ this._usedNicknames.delete(nicknameKey);
8341
+ }
8342
+ for (const [taskId, task] of this.pendingTasks) {
8343
+ if (task.subagentId === subagentId) {
8344
+ this.pendingTasks.delete(taskId);
8345
+ }
8346
+ }
8347
+ }
8005
8348
  /** Release all resources: clear the manifest debounce timer and dispose the usage aggregator. */
8006
8349
  dispose() {
8007
8350
  if (this.manifestTimer) {
@@ -8012,6 +8355,6 @@ var FleetManager = class {
8012
8355
  }
8013
8356
  };
8014
8357
 
8015
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BUILD_AGENTS, BudgetExceededError, BudgetThresholdSignal, CollabSession, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, HEAVY_BUDGET, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, LIGHT_BUDGET, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, dispatchAgent, getAgentDefinition, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, rosterSummaryFromConfigs, scoreAgents };
8358
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BUILD_AGENTS, BudgetExceededError, BudgetThresholdSignal, CollabSession, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, HEAVY_BUDGET, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, dispatchAgent, getAgentDefinition, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, rosterSummaryFromConfigs, scoreAgents };
8016
8359
  //# sourceMappingURL=index.js.map
8017
8360
  //# sourceMappingURL=index.js.map