@wrongstack/core 0.9.4 → 0.9.19

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 (59) hide show
  1. package/dist/{agent-subagent-runner-DaF_EgRG.d.ts → agent-subagent-runner-C4qt9e5Y.d.ts} +1 -1
  2. package/dist/{config-SkMIDN9L.d.ts → config-CWva0qoL.d.ts} +4 -0
  3. package/dist/coordination/index.d.ts +8 -7
  4. package/dist/coordination/index.js +672 -46
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/defaults/index.d.ts +12 -11
  7. package/dist/defaults/index.js +780 -23
  8. package/dist/defaults/index.js.map +1 -1
  9. package/dist/execution/index.d.ts +7 -6
  10. package/dist/execution/index.js +1 -0
  11. package/dist/execution/index.js.map +1 -1
  12. package/dist/extension/index.d.ts +4 -3
  13. package/dist/{index-CP8638Wm.d.ts → index-aizK8olO.d.ts} +3 -2
  14. package/dist/{index-Bsha5K4D.d.ts → index-p95HQ22A.d.ts} +3 -2
  15. package/dist/index.d.ts +22 -16
  16. package/dist/index.js +861 -35
  17. package/dist/index.js.map +1 -1
  18. package/dist/infrastructure/index.d.ts +2 -2
  19. package/dist/kernel/index.d.ts +4 -3
  20. package/dist/{mcp-servers-BouUWYW6.d.ts → mcp-servers-BkVEqkRe.d.ts} +1 -1
  21. package/dist/{multi-agent-coordinator-DTXF2aAl.d.ts → multi-agent-coordinator-bRaI_aD1.d.ts} +1 -1
  22. package/dist/{null-fleet-bus-Chrc_3Pp.d.ts → null-fleet-bus-DKM3Iy9d.d.ts} +183 -3
  23. package/dist/{secret-scrubber-DttNiGYA.d.ts → permission-bPuzAy4x.d.ts} +1 -6
  24. package/dist/{permission-policy-BpCGYBud.d.ts → permission-policy-BUQSutpl.d.ts} +8 -1
  25. package/dist/{plan-templates-envSmNlZ.d.ts → plan-templates-fkQTyz3U.d.ts} +25 -1
  26. package/dist/sdd/index.d.ts +5 -4
  27. package/dist/sdd/index.js +1 -0
  28. package/dist/sdd/index.js.map +1 -1
  29. package/dist/secret-scrubber-3MHDDAtm.d.ts +6 -0
  30. package/dist/{secret-scrubber-QSeI0ADi.d.ts → secret-scrubber-7rSC_emZ.d.ts} +1 -1
  31. package/dist/security/index.d.ts +4 -3
  32. package/dist/security/index.js +37 -6
  33. package/dist/security/index.js.map +1 -1
  34. package/dist/storage/index.d.ts +3 -2
  35. package/dist/storage/index.js +88 -5
  36. package/dist/storage/index.js.map +1 -1
  37. package/dist/{tool-executor-CsktM3h9.d.ts → tool-executor-Boo3dekH.d.ts} +1 -1
  38. package/dist/types/index.d.ts +6 -5
  39. package/dist/types/index.js.map +1 -1
  40. package/dist/utils/index.d.ts +12 -1
  41. package/dist/utils/index.js +85 -1
  42. package/dist/utils/index.js.map +1 -1
  43. package/package.json +1 -1
  44. package/skills/api-design/SKILL.md +139 -0
  45. package/skills/audit-log/SKILL.md +87 -14
  46. package/skills/bug-hunter/SKILL.md +42 -19
  47. package/skills/docker-deploy/SKILL.md +155 -0
  48. package/skills/git-flow/SKILL.md +53 -1
  49. package/skills/multi-agent/SKILL.md +42 -0
  50. package/skills/node-modern/SKILL.md +57 -1
  51. package/skills/observability/SKILL.md +134 -0
  52. package/skills/prompt-engineering/SKILL.md +46 -19
  53. package/skills/react-modern/SKILL.md +92 -1
  54. package/skills/refactor-planner/SKILL.md +49 -1
  55. package/skills/sdd/SKILL.md +12 -1
  56. package/skills/security-scanner/SKILL.md +46 -1
  57. package/skills/skill-creator/SKILL.md +49 -52
  58. package/skills/testing/SKILL.md +170 -0
  59. package/skills/typescript-strict/SKILL.md +98 -1
@@ -319,9 +319,11 @@ function isEmptyMessage(msg) {
319
319
  var DefaultSessionStore = class {
320
320
  dir;
321
321
  events;
322
+ secretScrubber;
322
323
  constructor(opts) {
323
324
  this.dir = opts.dir;
324
325
  this.events = opts.events;
326
+ this.secretScrubber = opts.secretScrubber;
325
327
  }
326
328
  /** Join session ID to its absolute path within the store directory. */
327
329
  sessionPath(id, ext) {
@@ -346,7 +348,11 @@ var DefaultSessionStore = class {
346
348
  );
347
349
  }
348
350
  try {
349
- return new FileSessionWriter(id, handle, startedAt, meta, this.events, { dir: shardDir, filePath: file });
351
+ return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
352
+ dir: shardDir,
353
+ filePath: file,
354
+ secretScrubber: this.secretScrubber
355
+ });
350
356
  } catch (err) {
351
357
  await handle.close().catch(() => {
352
358
  });
@@ -376,7 +382,7 @@ var DefaultSessionStore = class {
376
382
  provider: data.metadata.provider
377
383
  },
378
384
  this.events,
379
- { resumed: true, dir: this.dir, filePath: file }
385
+ { resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber }
380
386
  );
381
387
  return { writer, data };
382
388
  } catch (err) {
@@ -582,6 +588,7 @@ var FileSessionWriter = class {
582
588
  this.resumed = opts.resumed ?? false;
583
589
  this.manifestFile = opts.dir ? path15.join(opts.dir, `${id}.summary.json`) : "";
584
590
  this.filePath = opts.filePath ?? "";
591
+ this.secretScrubber = opts.secretScrubber;
585
592
  this.summary = {
586
593
  id,
587
594
  title: "(empty session)",
@@ -610,6 +617,29 @@ var FileSessionWriter = class {
610
617
  resumed;
611
618
  appendFailCount = 0;
612
619
  lastAppendWarnAt = 0;
620
+ secretScrubber;
621
+ /**
622
+ * Scrub secrets out of conversation-turn events before they are observed
623
+ * for the summary, written to the JSONL log, or surfaced on resume. Only
624
+ * `user_input` / `llm_response` carry free-form user/model text; other event
625
+ * types either have no secret-bearing content or are already scrubbed
626
+ * upstream (tool results). Returns the event unchanged when no scrubber is
627
+ * configured.
628
+ */
629
+ scrubEvent(event) {
630
+ const s = this.secretScrubber;
631
+ if (!s) return event;
632
+ if (event.type === "user_input") {
633
+ return {
634
+ ...event,
635
+ content: typeof event.content === "string" ? s.scrub(event.content) : s.scrubObject(event.content)
636
+ };
637
+ }
638
+ if (event.type === "llm_response") {
639
+ return { ...event, content: s.scrubObject(event.content) };
640
+ }
641
+ return event;
642
+ }
613
643
  promptIndex = 0;
614
644
  pendingFileSnapshots = [];
615
645
  /** Tracks open tool_use IDs during the current run to serialize on close for resume. */
@@ -642,9 +672,10 @@ var FileSessionWriter = class {
642
672
  this.initDone = true;
643
673
  await this.writeSessionStartLazy();
644
674
  }
645
- this.observeForSummary(event);
675
+ const scrubbed = this.scrubEvent(event);
676
+ this.observeForSummary(scrubbed);
646
677
  try {
647
- await this.handle.appendFile(`${JSON.stringify(event)}
678
+ await this.handle.appendFile(`${JSON.stringify(scrubbed)}
648
679
  `, "utf8");
649
680
  } catch (err) {
650
681
  this.appendFailCount++;
@@ -1266,6 +1297,9 @@ function isSecretField(name) {
1266
1297
  return SECRET_KEY_PATTERN.test(lc);
1267
1298
  }
1268
1299
 
1300
+ // src/storage/config-loader.ts
1301
+ init_atomic_write();
1302
+
1269
1303
  // src/types/context-window.ts
1270
1304
  var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
1271
1305
  var CONTEXT_WINDOW_MODES = Object.freeze([
@@ -1510,6 +1544,40 @@ var DefaultConfigLoader = class {
1510
1544
  }
1511
1545
  return Object.freeze(cfg);
1512
1546
  }
1547
+ /**
1548
+ * Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
1549
+ * by the vault (if provided). The file is isolated from the main config
1550
+ * hierarchy to prevent accidental commits.
1551
+ */
1552
+ async persistSyncConfig(cfg) {
1553
+ let toWrite = { ...cfg };
1554
+ if (this.vault && toWrite.githubToken && !toWrite.githubToken.startsWith("enc:")) {
1555
+ toWrite = { ...toWrite, githubToken: this.vault.encrypt(toWrite.githubToken) };
1556
+ }
1557
+ await atomicWrite(this.paths.syncConfig, JSON.stringify(toWrite, null, 2), { mode: 384 });
1558
+ }
1559
+ /**
1560
+ * Read ~/.wrongstack/sync.json (encrypted GitHub token storage) and decrypt
1561
+ * the token if a vault is available. Returns null if the file doesn't exist.
1562
+ * This is separate from main config loading because sync.json is intentionally
1563
+ * isolated — it should never be part of project-local or env-driven config.
1564
+ */
1565
+ async loadSyncConfig() {
1566
+ try {
1567
+ const raw = await fsp.readFile(this.paths.syncConfig, "utf8");
1568
+ const parsed = safeParse(raw);
1569
+ if (!parsed.ok || !parsed.value) return null;
1570
+ if (this.vault) {
1571
+ const decrypted = decryptConfigSecrets({ sync: parsed.value }, this.vault);
1572
+ return decrypted.sync ?? null;
1573
+ }
1574
+ return parsed.value;
1575
+ } catch (err) {
1576
+ if (err.code === "ENOENT") return null;
1577
+ console.warn("[config] Failed to load sync config:", err);
1578
+ return null;
1579
+ }
1580
+ }
1513
1581
  async readJson(file) {
1514
1582
  let raw;
1515
1583
  try {
@@ -3126,7 +3194,11 @@ var DefaultPermissionPolicy = class {
3126
3194
  return { permission: "deny", source: "deny", reason: "session soft deny (user pressed no)" };
3127
3195
  }
3128
3196
  if (this.sessionAllowed.has(subjectKey)) {
3129
- return { permission: "auto", source: "trust", reason: "session soft allow (user pressed yes)" };
3197
+ return {
3198
+ permission: "auto",
3199
+ source: "trust",
3200
+ reason: "session soft allow (user pressed yes)"
3201
+ };
3130
3202
  }
3131
3203
  if (entry?.deny && subject && matchAny(entry.deny, subject)) {
3132
3204
  return { permission: "deny", source: "deny", reason: "matched deny pattern" };
@@ -3146,7 +3218,11 @@ var DefaultPermissionPolicy = class {
3146
3218
  const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
3147
3219
  if (decision === "always") {
3148
3220
  await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
3149
- return { permission: "auto", source: "user", reason: "destructive yolo always-allowed" };
3221
+ return {
3222
+ permission: "auto",
3223
+ source: "user",
3224
+ reason: "destructive yolo always-allowed"
3225
+ };
3150
3226
  }
3151
3227
  if (decision === "deny") {
3152
3228
  await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
@@ -3154,13 +3230,22 @@ var DefaultPermissionPolicy = class {
3154
3230
  }
3155
3231
  return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
3156
3232
  }
3157
- return { permission: "confirm", source: "yolo_destructive", riskTier: "destructive", reason: "destructive tool needs explicit approval even in yolo mode" };
3233
+ return {
3234
+ permission: "confirm",
3235
+ source: "yolo_destructive",
3236
+ riskTier: "destructive",
3237
+ reason: "destructive tool needs explicit approval even in yolo mode"
3238
+ };
3158
3239
  }
3159
3240
  return { permission: "auto", source: "yolo" };
3160
3241
  }
3161
3242
  if (tool.name === "write" && subject) {
3162
3243
  if (ctx.hasRead(subject)) {
3163
- return { permission: "auto", source: "context", reason: "file already read in this session" };
3244
+ return {
3245
+ permission: "auto",
3246
+ source: "context",
3247
+ reason: "file already read in this session"
3248
+ };
3164
3249
  }
3165
3250
  }
3166
3251
  if (tool.permission === "auto" && !tool.mutating) {
@@ -3268,6 +3353,10 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
3268
3353
  // arbitrary shell — use exec for constrained shell
3269
3354
  "write",
3270
3355
  // arbitrary file write
3356
+ "edit",
3357
+ // arbitrary in-project file modification (equivalent to write)
3358
+ "replace",
3359
+ // arbitrary multi-file find/replace (equivalent to write)
3271
3360
  "scaffold",
3272
3361
  // arbitrary file generation outside project root
3273
3362
  "patch",
@@ -3277,12 +3366,22 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
3277
3366
  "exec"
3278
3367
  // restricted shell but with arbitrary command args
3279
3368
  ]);
3369
+ /**
3370
+ * Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
3371
+ * unknown capability — they may wrap a shell or filesystem. They are
3372
+ * fail-closed here: not auto-approved for subagents by default, so the
3373
+ * leader must allow them explicitly per-spawn.
3374
+ */
3375
+ static isMcpTool(name) {
3376
+ return name.startsWith("mcp__");
3377
+ }
3280
3378
  async evaluate(tool) {
3281
- if (tool.permission === "deny" || _AutoApprovePermissionPolicy.DENY.has(tool.name)) {
3379
+ const blocked = _AutoApprovePermissionPolicy.DENY.has(tool.name) || _AutoApprovePermissionPolicy.isMcpTool(tool.name);
3380
+ if (tool.permission === "deny" || blocked) {
3282
3381
  return {
3283
3382
  permission: "deny",
3284
3383
  source: "subagent_guard",
3285
- reason: _AutoApprovePermissionPolicy.DENY.has(tool.name) ? `tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny"
3384
+ reason: blocked ? `tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny"
3286
3385
  };
3287
3386
  }
3288
3387
  return { permission: "auto", source: "yolo" };
@@ -3697,6 +3796,90 @@ function parseDescription(raw) {
3697
3796
  return { trigger, scope };
3698
3797
  }
3699
3798
 
3799
+ // src/utils/json-repair.ts
3800
+ function completePartialObject(s) {
3801
+ let result = s;
3802
+ const trimmed = result.trim();
3803
+ if (!trimmed.startsWith("{")) return s;
3804
+ for (let pass = 0; pass < 3; pass++) {
3805
+ let braceDepth = 0;
3806
+ let inString2 = false;
3807
+ let escaped2 = false;
3808
+ let foundClose = false;
3809
+ for (const ch of result) {
3810
+ if (escaped2) {
3811
+ escaped2 = false;
3812
+ continue;
3813
+ }
3814
+ if (ch === "\\") {
3815
+ escaped2 = true;
3816
+ continue;
3817
+ }
3818
+ if (ch === '"') {
3819
+ inString2 = !inString2;
3820
+ continue;
3821
+ }
3822
+ if (inString2) continue;
3823
+ if (ch === "{") {
3824
+ braceDepth++;
3825
+ foundClose = false;
3826
+ } else if (ch === "}") {
3827
+ braceDepth--;
3828
+ if (braceDepth === 0) foundClose = true;
3829
+ }
3830
+ }
3831
+ if (foundClose || braceDepth <= 0) break;
3832
+ result += "}".repeat(braceDepth);
3833
+ }
3834
+ if (tryParse(result).ok) return result;
3835
+ let inString = false;
3836
+ let escaped = false;
3837
+ for (let i = result.length - 1; i >= 0; i--) {
3838
+ const ch = result[i];
3839
+ if (escaped) {
3840
+ escaped = false;
3841
+ continue;
3842
+ }
3843
+ if (ch === "\\") {
3844
+ escaped = true;
3845
+ continue;
3846
+ }
3847
+ if (ch === '"') {
3848
+ let nextNonWs;
3849
+ for (let j = i + 1; j < result.length; j++) {
3850
+ const nc = result[j];
3851
+ if (nc === " " || nc === " " || nc === "\n" || nc === "\r") continue;
3852
+ nextNonWs = result[j];
3853
+ break;
3854
+ }
3855
+ if (nextNonWs === ":") {
3856
+ continue;
3857
+ } else {
3858
+ inString = !inString;
3859
+ continue;
3860
+ }
3861
+ }
3862
+ }
3863
+ if (inString) {
3864
+ result = result.trimEnd();
3865
+ if (result.endsWith("\\")) result = result.slice(0, -1);
3866
+ let depth = 0;
3867
+ for (const ch of result) {
3868
+ if (ch === "{") depth++;
3869
+ else if (ch === "}") depth = Math.max(0, depth - 1);
3870
+ }
3871
+ result += '"' + "}".repeat(Math.max(1, depth));
3872
+ }
3873
+ return result;
3874
+ }
3875
+ function tryParse(s) {
3876
+ try {
3877
+ return { ok: true, value: JSON.parse(s) };
3878
+ } catch {
3879
+ return { ok: false };
3880
+ }
3881
+ }
3882
+
3700
3883
  // src/core/streaming-response-builder.ts
3701
3884
  function buildResponse(state) {
3702
3885
  const content = [];
@@ -3794,7 +3977,12 @@ function safeJsonOrRaw(s) {
3794
3977
  try {
3795
3978
  return JSON.parse(s);
3796
3979
  } catch {
3797
- return { _raw: s };
3980
+ const repaired = completePartialObject(s);
3981
+ try {
3982
+ return JSON.parse(repaired);
3983
+ } catch {
3984
+ return { _raw: repaired };
3985
+ }
3798
3986
  }
3799
3987
  }
3800
3988
  function handleToolUseStop(state, ev) {
@@ -9069,11 +9257,46 @@ Working rules:
9069
9257
  - When in doubt, flag as medium rather than ignoring potential issues`
9070
9258
  // Budgets are set by the orchestrator per task — see fleet.ts header.
9071
9259
  };
9260
+ var CRITIC_AGENT = {
9261
+ id: "critic",
9262
+ name: "Critic",
9263
+ role: "critic",
9264
+ prompt: `You are the Critic agent. Your job is to evaluate code quality,
9265
+ architectural decisions, and proposed changes against project conventions,
9266
+ engineering standards, and known quality gates. You do not write code \u2014
9267
+ you judge it.
9268
+
9269
+ Scope:
9270
+ - Evaluate bug severity and fix quality from Bug Hunter reports
9271
+ - Score refactoring plans from Refactor Planner (risk, completeness, trade-offs)
9272
+ - Flag gaps in test coverage, error handling, and edge case coverage
9273
+ - Assess whether a proposed change aligns with existing project patterns
9274
+ - Detect over-engineering or under-engineering relative to the problem scope
9275
+
9276
+ Input format you accept:
9277
+ { "task": "evaluate | score | review", "subject": "bug_report | refactor_plan | diff", "focus": "correctness | maintainability | risk | all" }
9278
+
9279
+ Output: Markdown critic report:
9280
+ - ## Overall Score (0-10 with rationale)
9281
+ - ## Strengths (what's solid)
9282
+ - ## Weaknesses (what needs work)
9283
+ - ## Specific Concerns (with file:line when applicable)
9284
+ - ## Verdict: **Approve / Needs Revision / Reject**
9285
+
9286
+ Working rules:
9287
+ - Be specific \u2014 "looks fine" is not a review. Cite concrete evidence.
9288
+ - When scoring, explain the delta from a perfect score.
9289
+ - If you have no basis to evaluate a concern, say so rather than speculating.
9290
+ - Prioritise correctness over style; correctness issues block approval.
9291
+ - Score thresholds: \u22657 = Approve, 4-6 = Needs Revision, <4 = Reject`
9292
+ // Budgets are set by the orchestrator per task — see fleet.ts header.
9293
+ };
9072
9294
  var FLEET_ROSTER = {
9073
9295
  "audit-log": AUDIT_LOG_AGENT,
9074
9296
  "bug-hunter": BUG_HUNTER_AGENT,
9075
9297
  "refactor-planner": REFACTOR_PLANNER_AGENT,
9076
9298
  "security-scanner": SECURITY_SCANNER_AGENT,
9299
+ "critic": CRITIC_AGENT,
9077
9300
  ...Object.fromEntries(
9078
9301
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
9079
9302
  )
@@ -9083,6 +9306,7 @@ var FLEET_ROSTER_BUDGETS = {
9083
9306
  "bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
9084
9307
  "refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
9085
9308
  "security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
9309
+ "critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
9086
9310
  ...Object.fromEntries(
9087
9311
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
9088
9312
  )
@@ -10698,6 +10922,106 @@ var FleetUsageAggregator = class {
10698
10922
  snap.lastEventAt = e.ts;
10699
10923
  }
10700
10924
  };
10925
+
10926
+ // src/coordination/subagent-nicknames.ts
10927
+ var NICKNAME_POOL = {
10928
+ // Physics & fundamental sciences
10929
+ "einstein": { name: "Einstein", domain: "physics" },
10930
+ "newton": { name: "Newton", domain: "physics" },
10931
+ "feynman": { name: "Feynman", domain: "physics" },
10932
+ "dirac": { name: "Dirac", domain: "physics" },
10933
+ "bohr": { name: "Bohr", domain: "physics" },
10934
+ "planck": { name: "Planck", domain: "physics" },
10935
+ "curie": { name: "Curie", domain: "physics" },
10936
+ "fermi": { name: "Fermi", domain: "physics" },
10937
+ "heisenberg": { name: "Heisenberg", domain: "physics" },
10938
+ "schrodinger": { name: "Schr\xF6dinger", domain: "physics" },
10939
+ // Mathematics
10940
+ "euclid": { name: "Euclid", domain: "math" },
10941
+ "gauss": { name: "Gauss", domain: "math" },
10942
+ "turing": { name: "Turing", domain: "math" },
10943
+ "poincare": { name: "Poincar\xE9", domain: "math" },
10944
+ "riemann": { name: "Riemann", domain: "math" },
10945
+ "hilbert": { name: "Hilbert", domain: "math" },
10946
+ "pythagoras": { name: "Pythagoras", domain: "math" },
10947
+ // Computing & information theory
10948
+ "von-neumann": { name: "Von Neumann", domain: "computing" },
10949
+ "shannon": { name: "Shannon", domain: "computing" },
10950
+ "hopper": { name: "Hopper", domain: "computing" },
10951
+ "backus": { name: "Backus", domain: "computing" },
10952
+ "knuth": { name: "Knuth", domain: "computing" },
10953
+ "torvalds": { name: "Torvalds", domain: "computing" },
10954
+ "stallman": { name: "Stallman", domain: "computing" },
10955
+ "berners-lee": { name: "Berners-Lee", domain: "computing" },
10956
+ "babbage": { name: "Babbage", domain: "computing" },
10957
+ "lovelace": { name: "Lovelace", domain: "computing" },
10958
+ "klein": { name: "Klein", domain: "computing" },
10959
+ // Electronics & electrical engineering
10960
+ "edison": { name: "Edison", domain: "ee" },
10961
+ "tesla": { name: "Tesla", domain: "ee" },
10962
+ "faraday": { name: "Faraday", domain: "ee" },
10963
+ "maxwell": { name: "Maxwell", domain: "ee" },
10964
+ "ohm": { name: "Ohm", domain: "ee" },
10965
+ "bell": { name: "Bell", domain: "ee" },
10966
+ "marconi": { name: "Marconi", domain: "ee" },
10967
+ "lamarr": { name: "Lamarr", domain: "ee" },
10968
+ // General science / multi-disciplinary
10969
+ "darwin": { name: "Darwin", domain: "biology" },
10970
+ "mendel": { name: "Mendel", domain: "biology" },
10971
+ "pasteur": { name: "Pasteur", domain: "biology" },
10972
+ "hawking": { name: "Hawking", domain: "cosmology" },
10973
+ "sagan": { name: "Sagan", domain: "cosmology" },
10974
+ // Chemistry / materials
10975
+ "lavoisier": { name: "Lavoisier", domain: "chemistry" },
10976
+ "mendeleev": { name: "Mendeleev", domain: "chemistry" }
10977
+ };
10978
+ var ALL_NICKNAMES = Object.values(NICKNAME_POOL);
10979
+ var DOMAIN_PREFERENCES = {
10980
+ "security": ["shannon", "turing", "lamarr", "stallman"],
10981
+ "bug-hunter": ["darwin", "curie", "feynman", "fermi"],
10982
+ "refactor": ["gauss", "hilbert", "euclid", "planck"],
10983
+ "audit-log": ["sagan", "hawking", "poincare", "newton"],
10984
+ "planner": ["hilbert", "gauss", "turing", "euclid"],
10985
+ "researcher": ["sagan", "hawking", "darwin", "pasteur"],
10986
+ "explorer": ["marconi", "bell", "columbus", "polo"],
10987
+ "testing": ["pasteur", "curie", "fermi", "bohr"],
10988
+ "frontend": ["lovelace", "hopper", "babbage", "backus"],
10989
+ "backend": ["torvalds", "stallman", "von-neumann", "backus"],
10990
+ "database": ["turing", "shannon", "backus", "knuth"],
10991
+ "devops": ["tesla", "edison", "faraday", "bell"],
10992
+ "security-scanner": ["shannon", "turing", "lamarr", "stallman"],
10993
+ "refactor-planner": ["gauss", "hilbert", "planck", "newton"],
10994
+ "architect": ["von-neumann", "turing", "gauss", "hilbert"],
10995
+ "critic": ["einstein", "feynman", "dirac", "bohr"],
10996
+ "e2e": ["hopper", "bell", "marconi", "tesla"],
10997
+ "performance": ["knuth", "gauss", "planck", "feynman"],
10998
+ "chaos": ["tesla", "edison", "curie", "fermi"],
10999
+ "cost": ["oh", "bell", "marconi", "tesla"],
11000
+ // default fallback
11001
+ "default": ["einstein", "newton", "curie", "tesla", "edison", "turing", "shannon", "hopper", "knuth", "stallman"]
11002
+ };
11003
+ function assignNickname(role, used) {
11004
+ const preferences = [
11005
+ ...DOMAIN_PREFERENCES[role] ?? [],
11006
+ ...DOMAIN_PREFERENCES["default"] ?? []
11007
+ ];
11008
+ for (const key of preferences) {
11009
+ if (!used.has(key)) {
11010
+ return `${NICKNAME_POOL[key].name} (${formatRole(role)})`;
11011
+ }
11012
+ }
11013
+ for (const entry of ALL_NICKNAMES) {
11014
+ const key = Object.entries(NICKNAME_POOL).find(([, v]) => v.name === entry.name)?.[0];
11015
+ if (key && !used.has(key)) {
11016
+ return `${entry.name} (${formatRole(role)})`;
11017
+ }
11018
+ }
11019
+ const counter = used.size + 1;
11020
+ return `Scientist #${counter} (${formatRole(role)})`;
11021
+ }
11022
+ function formatRole(role) {
11023
+ return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
11024
+ }
10701
11025
  function makeSpawnTool(director, roster) {
10702
11026
  const inputSchema = {
10703
11027
  type: "object",
@@ -10922,7 +11246,6 @@ function makeFleetSessionTool(director) {
10922
11246
  type: "object",
10923
11247
  properties: {
10924
11248
  subagentId: { type: "string", description: "Subagent id to read the transcript of." },
10925
- /** Number of trailing lines to return (last N JSONL lines). Default: all. */
10926
11249
  tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
10927
11250
  },
10928
11251
  required: ["subagentId"]
@@ -10958,8 +11281,6 @@ function makeFleetHealthTool(director) {
10958
11281
  id: s.id,
10959
11282
  status: s.status,
10960
11283
  lastEventAt: usage?.lastEventAt,
10961
- // Budget pressure: fraction of each limit consumed if we have it.
10962
- // BudgetWarning events carry used/limit ratios; surface them here.
10963
11284
  budgetPressure: {
10964
11285
  iterations: usage?.iterations,
10965
11286
  toolCalls: usage?.toolCalls,
@@ -10971,6 +11292,415 @@ function makeFleetHealthTool(director) {
10971
11292
  }
10972
11293
  };
10973
11294
  }
11295
+ function makeCollabDebugTool(director) {
11296
+ return {
11297
+ name: "collab_debug",
11298
+ description: "Start a collaborative debugging session: BugHunter, RefactorPlanner, and Critic run in parallel on the same target files. BugHunter finds bugs and emits bug.found events. RefactorPlanner listens for bug.found and emits refactor.plan events. Critic evaluates both and emits critic.evaluation events. Returns a structured report with overall verdict (approve / needs_revision / reject).",
11299
+ permission: "auto",
11300
+ mutating: false,
11301
+ inputSchema: {
11302
+ type: "object",
11303
+ properties: {
11304
+ targetPaths: {
11305
+ type: "array",
11306
+ items: { type: "string" },
11307
+ description: "File paths / glob patterns to scan for bugs."
11308
+ },
11309
+ timeoutMs: {
11310
+ type: "number",
11311
+ description: "Timeout in ms. Default: 600000 (10 minutes)."
11312
+ }
11313
+ },
11314
+ required: ["targetPaths"]
11315
+ },
11316
+ async execute(input) {
11317
+ const i = input;
11318
+ if (!i.targetPaths?.length) {
11319
+ return { error: "collab_debug: targetPaths is required and must be non-empty." };
11320
+ }
11321
+ const options = {
11322
+ targetPaths: i.targetPaths,
11323
+ timeoutMs: i.timeoutMs
11324
+ };
11325
+ try {
11326
+ const report = await director.spawnCollab(options);
11327
+ return {
11328
+ sessionId: report.sessionId,
11329
+ overallVerdict: report.overallVerdict,
11330
+ bugCount: report.bugs.length,
11331
+ planCount: report.refactorPlans.length,
11332
+ evaluationCount: report.evaluations.length,
11333
+ summary: report.summary,
11334
+ bugs: report.bugs,
11335
+ refactorPlans: report.refactorPlans,
11336
+ evaluations: report.evaluations
11337
+ };
11338
+ } catch (err) {
11339
+ const msg = err instanceof Error ? err.message : String(err);
11340
+ return { error: "collab_debug failed: " + msg };
11341
+ }
11342
+ }
11343
+ };
11344
+ }
11345
+ function makeFleetEmitTool(director) {
11346
+ return {
11347
+ name: "fleet_emit",
11348
+ description: "Emit a structured event on the FleetBus. Use this when you find a bug (bug.found), complete a refactor plan (refactor.plan), or finish an evaluation (critic.evaluation). Events are routed to other agents in the collab session.",
11349
+ permission: "auto",
11350
+ mutating: false,
11351
+ inputSchema: {
11352
+ type: "object",
11353
+ properties: {
11354
+ type: {
11355
+ type: "string",
11356
+ description: "Event type: bug.found | refactor.plan | critic.evaluation"
11357
+ },
11358
+ payload: {
11359
+ type: "object",
11360
+ description: "Event payload matching the event type."
11361
+ }
11362
+ },
11363
+ required: ["type", "payload"]
11364
+ },
11365
+ async execute(input) {
11366
+ const i = input;
11367
+ director.fleet.emit({
11368
+ subagentId: director.id,
11369
+ ts: Date.now(),
11370
+ type: i.type,
11371
+ payload: i.payload
11372
+ });
11373
+ return { ok: true, event: i.type };
11374
+ }
11375
+ };
11376
+ }
11377
+ var CollabSession = class extends EventEmitter {
11378
+ sessionId;
11379
+ options;
11380
+ snapshot;
11381
+ director;
11382
+ fleetBus;
11383
+ subagentIds = /* @__PURE__ */ new Map();
11384
+ // role → subagentId
11385
+ bugs = /* @__PURE__ */ new Map();
11386
+ plans = /* @__PURE__ */ new Map();
11387
+ evaluations = /* @__PURE__ */ new Map();
11388
+ disposers = new Array();
11389
+ settled = false;
11390
+ timeoutMs;
11391
+ constructor(director, fleetBus, options) {
11392
+ super();
11393
+ this.sessionId = randomUUID();
11394
+ this.options = options;
11395
+ this.director = director;
11396
+ this.fleetBus = fleetBus;
11397
+ this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
11398
+ if (options.prebuiltSnapshot) {
11399
+ this.snapshot = options.prebuiltSnapshot;
11400
+ } else {
11401
+ this.snapshot = {
11402
+ id: this.sessionId,
11403
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
11404
+ files: []
11405
+ };
11406
+ }
11407
+ }
11408
+ /**
11409
+ * Read the target files from disk and populate the snapshot.
11410
+ * Call this after construction if you did not provide a prebuiltSnapshot
11411
+ * and want the session to operate on real file contents.
11412
+ */
11413
+ async buildSnapshot() {
11414
+ if (this.snapshot.files.length > 0) return this.snapshot;
11415
+ for (const filePath of this.options.targetPaths) {
11416
+ try {
11417
+ const content = await fsp.readFile(filePath, "utf8");
11418
+ const ext = filePath.split(".").pop() ?? "";
11419
+ const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
11420
+ this.snapshot.files.push({ path: filePath, content, language });
11421
+ } catch {
11422
+ this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
11423
+ }
11424
+ }
11425
+ return this.snapshot;
11426
+ }
11427
+ /**
11428
+ * Start the collaborative session: snapshot files, spawn all three agents,
11429
+ * wire up event routing, and wait for completion.
11430
+ */
11431
+ async start() {
11432
+ if (this.settled) throw new Error("session already settled");
11433
+ this.settled = true;
11434
+ await this.buildSnapshot();
11435
+ this.wireFleetBus();
11436
+ const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
11437
+ this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
11438
+ this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
11439
+ this.spawnAgent("critic", this.buildCriticTask())
11440
+ ]);
11441
+ this.subagentIds.set("bug-hunter", bugHunterId);
11442
+ this.subagentIds.set("refactor-planner", refactorPlannerId);
11443
+ this.subagentIds.set("critic", criticId);
11444
+ const timeout = new Promise((_, reject) => {
11445
+ setTimeout(
11446
+ () => reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`)),
11447
+ this.timeoutMs
11448
+ );
11449
+ });
11450
+ let results;
11451
+ try {
11452
+ results = await Promise.race([
11453
+ Promise.all([
11454
+ this.director.awaitTasks([bugHunterId]),
11455
+ this.director.awaitTasks([refactorPlannerId]),
11456
+ this.director.awaitTasks([criticId])
11457
+ ]),
11458
+ timeout
11459
+ ]);
11460
+ } catch (err) {
11461
+ this.cleanup();
11462
+ const error = err instanceof Error ? err : new Error(String(err));
11463
+ this.emit("session.error", error);
11464
+ throw error;
11465
+ }
11466
+ for (const result of results.flat()) {
11467
+ await this.parseAndEmit(result);
11468
+ }
11469
+ const report = this.assembleReport();
11470
+ this.cleanup();
11471
+ this.emit("session.done", report);
11472
+ return report;
11473
+ }
11474
+ /**
11475
+ * Parse a completed agent's final text output and route the structured
11476
+ * findings onto the FleetBus. Agents are instructed to emit one JSON object
11477
+ * per finding / plan / evaluation; we scan the output for balanced JSON
11478
+ * objects (tolerating prose, markdown, and ```json fences around them) and
11479
+ * dispatch each by its discriminating key. The wired FleetBus listeners then
11480
+ * collect them into the bugs / plans / evaluations maps.
11481
+ *
11482
+ * Failed or empty results are ignored, and a single malformed object never
11483
+ * aborts the collation pass — see {@link extractJsonObjects}.
11484
+ */
11485
+ async parseAndEmit(result) {
11486
+ if (result.status !== "success" || result.result == null) return;
11487
+ const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
11488
+ for (const obj of this.extractJsonObjects(text)) {
11489
+ const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
11490
+ if (!type) continue;
11491
+ this.fleetBus.emit({
11492
+ subagentId: result.subagentId,
11493
+ taskId: result.taskId,
11494
+ ts: Date.now(),
11495
+ type,
11496
+ payload: obj
11497
+ });
11498
+ }
11499
+ }
11500
+ /**
11501
+ * Extract every top-level JSON object embedded in free-form agent text.
11502
+ * Scans for balanced braces while respecting string literals and escape
11503
+ * sequences, so prose, markdown headers, and code fences around the JSON
11504
+ * are skipped. Malformed spans are dropped silently rather than thrown.
11505
+ */
11506
+ extractJsonObjects(text) {
11507
+ const objects = [];
11508
+ let depth = 0;
11509
+ let start = -1;
11510
+ let inString = false;
11511
+ let escaped = false;
11512
+ for (let i = 0; i < text.length; i++) {
11513
+ const ch = text[i];
11514
+ if (inString) {
11515
+ if (escaped) escaped = false;
11516
+ else if (ch === "\\") escaped = true;
11517
+ else if (ch === '"') inString = false;
11518
+ continue;
11519
+ }
11520
+ if (ch === '"') {
11521
+ inString = true;
11522
+ } else if (ch === "{") {
11523
+ if (depth === 0) start = i;
11524
+ depth++;
11525
+ } else if (ch === "}" && depth > 0) {
11526
+ depth--;
11527
+ if (depth === 0 && start >= 0) {
11528
+ const candidate = text.slice(start, i + 1);
11529
+ try {
11530
+ const parsed = JSON.parse(candidate);
11531
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
11532
+ objects.push(parsed);
11533
+ }
11534
+ } catch {
11535
+ }
11536
+ start = -1;
11537
+ }
11538
+ }
11539
+ }
11540
+ return objects;
11541
+ }
11542
+ async spawnAgent(role, taskBrief) {
11543
+ const cfg = {
11544
+ id: `${role}-${this.sessionId}`,
11545
+ name: role,
11546
+ role
11547
+ // No fleet_emit tool — agents output structured JSON, the Director parses
11548
+ // results and emits FleetBus events. This keeps the agent simple and the
11549
+ // routing deterministic.
11550
+ };
11551
+ const subagentId = await this.director.spawn(cfg);
11552
+ await this.director.assign({
11553
+ id: randomUUID(),
11554
+ subagentId,
11555
+ description: taskBrief
11556
+ });
11557
+ return subagentId;
11558
+ }
11559
+ buildBugHunterTask() {
11560
+ this.options.targetPaths.join(", ");
11561
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
11562
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
11563
+ ${f.content}`).join("\n\n");
11564
+ return `You are BugHunter. Scan the following files for bugs and code smells.
11565
+
11566
+ Target files:
11567
+ ${fileContents}
11568
+
11569
+ For each bug found, write ONE valid JSON line to the scratchpad with this exact structure:
11570
+ { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } }.
11571
+ After scanning all files, write your full markdown bug report to the shared scratchpad at:
11572
+ ${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
11573
+ }
11574
+ buildRefactorPlannerTask() {
11575
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
11576
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
11577
+ ${f.content}`).join("\n\n");
11578
+ return `You are RefactorPlanner. Plan refactorings for the following files.
11579
+
11580
+ Target files:
11581
+ ${fileContents}
11582
+
11583
+ Listen for bug.found events from BugHunter on the fleet bus. For each bug, create a refactor plan and write one JSON line per plan:
11584
+ { "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [...], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } }.
11585
+ After planning, write your full markdown plan to:
11586
+ ${scratchpad}/refactor-plan-${this.sessionId}.md`;
11587
+ }
11588
+ buildCriticTask() {
11589
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
11590
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
11591
+ ${f.content}`).join("\n\n");
11592
+ return `You are Critic. Evaluate bug findings and refactor plans as they arrive via fleet bus events.
11593
+
11594
+ Target files:
11595
+ ${fileContents}
11596
+
11597
+ Subscribe to bug.found events (from BugHunter) and refactor.plan events (from RefactorPlanner). For each subject, write one JSON line per evaluation:
11598
+ { "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": [...], "weaknesses": [...], "concerns": [...] } }.
11599
+ After all evaluations, write your markdown critic report to:
11600
+ ${scratchpad}/critic-report-${this.sessionId}.md`;
11601
+ }
11602
+ wireFleetBus() {
11603
+ const d1 = this.fleetBus.filter("bug.found", (e) => {
11604
+ const payload = e.payload;
11605
+ if (payload?.finding) {
11606
+ this.bugs.set(payload.finding.id, payload.finding);
11607
+ this.emit("bug.found", payload);
11608
+ }
11609
+ });
11610
+ this.disposers.push(d1);
11611
+ const d2 = this.fleetBus.filter("refactor.plan", (e) => {
11612
+ const payload = e.payload;
11613
+ if (payload?.plan) {
11614
+ this.plans.set(payload.plan.id, payload.plan);
11615
+ this.emit("refactor.plan", payload);
11616
+ }
11617
+ });
11618
+ this.disposers.push(d2);
11619
+ const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
11620
+ const payload = e.payload;
11621
+ if (payload?.evaluation) {
11622
+ this.evaluations.set(payload.evaluation.id, payload.evaluation);
11623
+ this.emit("critic.evaluation", payload);
11624
+ }
11625
+ });
11626
+ this.disposers.push(d3);
11627
+ }
11628
+ assembleReport() {
11629
+ const bugList = Array.from(this.bugs.values());
11630
+ const planList = Array.from(this.plans.values());
11631
+ const evalList = Array.from(this.evaluations.values());
11632
+ const verdictOrder = {
11633
+ approve: 0,
11634
+ needs_revision: 1,
11635
+ reject: 2
11636
+ };
11637
+ const overallVerdict = evalList.reduce(
11638
+ (worst, eval_) => {
11639
+ const w = verdictOrder[worst];
11640
+ const c = verdictOrder[eval_.verdict];
11641
+ return c > w ? eval_.verdict : worst;
11642
+ },
11643
+ "approve"
11644
+ );
11645
+ const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict);
11646
+ return {
11647
+ sessionId: this.sessionId,
11648
+ startedAt: this.snapshot.createdAt,
11649
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
11650
+ targetPaths: this.options.targetPaths,
11651
+ bugs: bugList,
11652
+ refactorPlans: planList,
11653
+ evaluations: evalList,
11654
+ overallVerdict,
11655
+ summary
11656
+ };
11657
+ }
11658
+ buildMarkdownSummary(bugs, plans, evals, overallVerdict) {
11659
+ const lines = [
11660
+ `## Collaborative Debugging Report \u2014 ${this.sessionId}`,
11661
+ "",
11662
+ `**Target:** ${this.options.targetPaths.join(", ")}`,
11663
+ `**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
11664
+ ""
11665
+ ];
11666
+ if (bugs.length > 0) {
11667
+ lines.push("### Bugs Found", "");
11668
+ for (const b of bugs) {
11669
+ lines.push(
11670
+ `- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
11671
+ );
11672
+ }
11673
+ lines.push("");
11674
+ }
11675
+ if (plans.length > 0) {
11676
+ lines.push("### Refactor Plans", "");
11677
+ for (const p of plans) {
11678
+ lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
11679
+ for (const phase of p.phases) {
11680
+ lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
11681
+ }
11682
+ }
11683
+ lines.push("");
11684
+ }
11685
+ if (evals.length > 0) {
11686
+ lines.push("### Critic Evaluations", "");
11687
+ for (const e of evals) {
11688
+ lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
11689
+ for (const c of e.concerns) {
11690
+ if (c.severity === "blocking") {
11691
+ lines.push(` - ${c.description}`);
11692
+ }
11693
+ }
11694
+ }
11695
+ lines.push("");
11696
+ }
11697
+ return lines.join("\n");
11698
+ }
11699
+ cleanup() {
11700
+ for (const dispose of this.disposers) dispose();
11701
+ this.disposers.length = 0;
11702
+ }
11703
+ };
10974
11704
 
10975
11705
  // src/coordination/director.ts
10976
11706
  var FleetSpawnBudgetError = class extends Error {
@@ -11049,6 +11779,8 @@ var Director = class {
11049
11779
  subagentBridges = /* @__PURE__ */ new Map();
11050
11780
  /** Tracks per-spawn config + assigned task ids for manifest writing. */
11051
11781
  manifestEntries = /* @__PURE__ */ new Map();
11782
+ /** Tracks assigned nicknames so the same name is never reused in one fleet. */
11783
+ _usedNicknames = /* @__PURE__ */ new Set();
11052
11784
  manifestPath;
11053
11785
  roster;
11054
11786
  directorPreamble;
@@ -11342,9 +12074,21 @@ var Director = class {
11342
12074
  }
11343
12075
  }
11344
12076
  let result;
12077
+ const needsNickname = config.name === config.role || !config.name || config.name === "subagent";
12078
+ if (needsNickname) {
12079
+ const role = config.role ?? "subagent";
12080
+ if (this.fleetManager) {
12081
+ this.fleetManager.assignNicknameAndRecord("", config, priceLookup);
12082
+ } else {
12083
+ config.name = assignNickname(role, this._usedNicknames);
12084
+ this._usedNicknames.add(config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-"));
12085
+ }
12086
+ }
11345
12087
  result = await this.coordinator.spawn(config);
11346
12088
  if (this.fleetManager) {
11347
- this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
12089
+ if (!needsNickname) {
12090
+ this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
12091
+ }
11348
12092
  } else {
11349
12093
  this.spawnCount += 1;
11350
12094
  this.subagentMeta.set(result.subagentId, {
@@ -11780,7 +12524,9 @@ var Director = class {
11780
12524
  makeFleetStatusTool(this),
11781
12525
  makeFleetUsageTool(this),
11782
12526
  makeFleetSessionTool(this),
11783
- makeFleetHealthTool(this)
12527
+ makeFleetHealthTool(this),
12528
+ makeCollabDebugTool(this),
12529
+ makeFleetEmitTool(this)
11784
12530
  ];
11785
12531
  return t;
11786
12532
  }
@@ -11792,6 +12538,17 @@ var Director = class {
11792
12538
  async acquireCheckpointLock() {
11793
12539
  return this.stateCheckpoint ? this.stateCheckpoint.acquireLock() : true;
11794
12540
  }
12541
+ /**
12542
+ * Start a collaborative debugging session: BugHunter, RefactorPlanner,
12543
+ * and Critic run in parallel on the same target files, with findings
12544
+ * flowing through the FleetBus (bug.found → refactor.plan →
12545
+ * critic.evaluation). Returns a structured CollabDebugReport when all
12546
+ * three agents complete or the session times out.
12547
+ */
12548
+ async spawnCollab(options) {
12549
+ const session = new CollabSession(this, this.fleet, options);
12550
+ return session.start();
12551
+ }
11795
12552
  /**
11796
12553
  * Resume from a prior checkpoint snapshot (loaded via
11797
12554
  * `loadDirectorState()`). Re-attach to the fleet mid-flight so
@@ -14025,10 +14782,10 @@ var AISpecBuilder = class {
14025
14782
  async saveSession() {
14026
14783
  if (!this.sessionPath) return;
14027
14784
  try {
14028
- const fsp14 = await import('fs/promises');
14785
+ const fsp15 = await import('fs/promises');
14029
14786
  const path18 = await import('path');
14030
14787
  const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
14031
- await fsp14.mkdir(path18.dirname(this.sessionPath), { recursive: true });
14788
+ await fsp15.mkdir(path18.dirname(this.sessionPath), { recursive: true });
14032
14789
  await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
14033
14790
  } catch {
14034
14791
  }
@@ -14037,8 +14794,8 @@ var AISpecBuilder = class {
14037
14794
  async loadSession() {
14038
14795
  if (!this.sessionPath) return false;
14039
14796
  try {
14040
- const fsp14 = await import('fs/promises');
14041
- const raw = await fsp14.readFile(this.sessionPath, "utf8");
14797
+ const fsp15 = await import('fs/promises');
14798
+ const raw = await fsp15.readFile(this.sessionPath, "utf8");
14042
14799
  const loaded = JSON.parse(raw);
14043
14800
  if (loaded?.id && loaded?.phase && loaded?.title) {
14044
14801
  this.session = loaded;
@@ -14052,8 +14809,8 @@ var AISpecBuilder = class {
14052
14809
  async deleteSession() {
14053
14810
  if (!this.sessionPath) return;
14054
14811
  try {
14055
- const fsp14 = await import('fs/promises');
14056
- await fsp14.unlink(this.sessionPath);
14812
+ const fsp15 = await import('fs/promises');
14813
+ await fsp15.unlink(this.sessionPath);
14057
14814
  } catch {
14058
14815
  }
14059
14816
  }
@@ -16420,6 +17177,6 @@ var allServers = () => ({
16420
17177
  "minimax-vision": { ...miniMaxVisionServer(), enabled: false }
16421
17178
  });
16422
17179
 
16423
- export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, decryptConfigSecrets2 as decryptConfigSecrets, deriveTodosFromPlanItem, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeDirectorSessionFactory, makeLLMClassifier, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
17180
+ export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, decryptConfigSecrets2 as decryptConfigSecrets, deriveTodosFromPlanItem, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
16424
17181
  //# sourceMappingURL=index.js.map
16425
17182
  //# sourceMappingURL=index.js.map