@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
@@ -1,5 +1,5 @@
1
1
  import { randomUUID, randomBytes } from 'crypto';
2
- import * as fsp4 from 'fs/promises';
2
+ import * as fsp5 from 'fs/promises';
3
3
  import * as path4 from 'path';
4
4
  import { EventEmitter } from 'events';
5
5
 
@@ -11,16 +11,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
11
11
  });
12
12
  async function atomicWrite(targetPath, content, opts = {}) {
13
13
  const dir = path4.dirname(targetPath);
14
- await fsp4.mkdir(dir, { recursive: true });
14
+ await fsp5.mkdir(dir, { recursive: true });
15
15
  const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
16
16
  try {
17
17
  if (typeof content === "string") {
18
- await fsp4.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
18
+ await fsp5.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
19
19
  } else {
20
- await fsp4.writeFile(tmp, content, { flag: "wx" });
20
+ await fsp5.writeFile(tmp, content, { flag: "wx" });
21
21
  }
22
22
  try {
23
- const fh = await fsp4.open(tmp, "r+");
23
+ const fh = await fsp5.open(tmp, "r+");
24
24
  try {
25
25
  await fh.sync();
26
26
  } finally {
@@ -30,37 +30,37 @@ async function atomicWrite(targetPath, content, opts = {}) {
30
30
  }
31
31
  let mode;
32
32
  try {
33
- const stat3 = await fsp4.stat(targetPath);
33
+ const stat3 = await fsp5.stat(targetPath);
34
34
  mode = stat3.mode & 511;
35
35
  } catch {
36
36
  mode = opts.mode;
37
37
  }
38
38
  if (mode !== void 0) {
39
- await fsp4.chmod(tmp, mode);
39
+ await fsp5.chmod(tmp, mode);
40
40
  }
41
41
  await renameWithRetry(tmp, targetPath);
42
42
  } catch (err) {
43
43
  try {
44
- await fsp4.unlink(tmp);
44
+ await fsp5.unlink(tmp);
45
45
  } catch {
46
46
  }
47
47
  throw err;
48
48
  }
49
49
  }
50
50
  async function ensureDir(dir) {
51
- await fsp4.mkdir(dir, { recursive: true });
51
+ await fsp5.mkdir(dir, { recursive: true });
52
52
  }
53
53
  var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
54
54
  async function renameWithRetry(from, to) {
55
55
  if (process.platform !== "win32") {
56
- await fsp4.rename(from, to);
56
+ await fsp5.rename(from, to);
57
57
  return;
58
58
  }
59
59
  const delays = [10, 25, 60, 120, 250];
60
60
  let lastErr;
61
61
  for (let i = 0; i <= delays.length; i++) {
62
62
  try {
63
- await fsp4.rename(from, to);
63
+ await fsp5.rename(from, to);
64
64
  return;
65
65
  } catch (err) {
66
66
  lastErr = err;
@@ -76,7 +76,7 @@ async function renameWithRetry(from, to) {
76
76
  async function acquireDirectorStateLock(lockPath, processId = process.pid) {
77
77
  let existing;
78
78
  try {
79
- existing = await fsp4.readFile(lockPath, "utf8");
79
+ existing = await fsp5.readFile(lockPath, "utf8");
80
80
  } catch {
81
81
  }
82
82
  if (existing) {
@@ -100,7 +100,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
100
100
  }
101
101
  async function releaseDirectorStateLock(lockPath) {
102
102
  try {
103
- await fsp4.unlink(lockPath);
103
+ await fsp5.unlink(lockPath);
104
104
  } catch {
105
105
  }
106
106
  }
@@ -631,6 +631,106 @@ var FleetUsageAggregator = class {
631
631
  }
632
632
  };
633
633
 
634
+ // src/coordination/subagent-nicknames.ts
635
+ var NICKNAME_POOL = {
636
+ // Physics & fundamental sciences
637
+ "einstein": { name: "Einstein", domain: "physics" },
638
+ "newton": { name: "Newton", domain: "physics" },
639
+ "feynman": { name: "Feynman", domain: "physics" },
640
+ "dirac": { name: "Dirac", domain: "physics" },
641
+ "bohr": { name: "Bohr", domain: "physics" },
642
+ "planck": { name: "Planck", domain: "physics" },
643
+ "curie": { name: "Curie", domain: "physics" },
644
+ "fermi": { name: "Fermi", domain: "physics" },
645
+ "heisenberg": { name: "Heisenberg", domain: "physics" },
646
+ "schrodinger": { name: "Schr\xF6dinger", domain: "physics" },
647
+ // Mathematics
648
+ "euclid": { name: "Euclid", domain: "math" },
649
+ "gauss": { name: "Gauss", domain: "math" },
650
+ "turing": { name: "Turing", domain: "math" },
651
+ "poincare": { name: "Poincar\xE9", domain: "math" },
652
+ "riemann": { name: "Riemann", domain: "math" },
653
+ "hilbert": { name: "Hilbert", domain: "math" },
654
+ "pythagoras": { name: "Pythagoras", domain: "math" },
655
+ // Computing & information theory
656
+ "von-neumann": { name: "Von Neumann", domain: "computing" },
657
+ "shannon": { name: "Shannon", domain: "computing" },
658
+ "hopper": { name: "Hopper", domain: "computing" },
659
+ "backus": { name: "Backus", domain: "computing" },
660
+ "knuth": { name: "Knuth", domain: "computing" },
661
+ "torvalds": { name: "Torvalds", domain: "computing" },
662
+ "stallman": { name: "Stallman", domain: "computing" },
663
+ "berners-lee": { name: "Berners-Lee", domain: "computing" },
664
+ "babbage": { name: "Babbage", domain: "computing" },
665
+ "lovelace": { name: "Lovelace", domain: "computing" },
666
+ "klein": { name: "Klein", domain: "computing" },
667
+ // Electronics & electrical engineering
668
+ "edison": { name: "Edison", domain: "ee" },
669
+ "tesla": { name: "Tesla", domain: "ee" },
670
+ "faraday": { name: "Faraday", domain: "ee" },
671
+ "maxwell": { name: "Maxwell", domain: "ee" },
672
+ "ohm": { name: "Ohm", domain: "ee" },
673
+ "bell": { name: "Bell", domain: "ee" },
674
+ "marconi": { name: "Marconi", domain: "ee" },
675
+ "lamarr": { name: "Lamarr", domain: "ee" },
676
+ // General science / multi-disciplinary
677
+ "darwin": { name: "Darwin", domain: "biology" },
678
+ "mendel": { name: "Mendel", domain: "biology" },
679
+ "pasteur": { name: "Pasteur", domain: "biology" },
680
+ "hawking": { name: "Hawking", domain: "cosmology" },
681
+ "sagan": { name: "Sagan", domain: "cosmology" },
682
+ // Chemistry / materials
683
+ "lavoisier": { name: "Lavoisier", domain: "chemistry" },
684
+ "mendeleev": { name: "Mendeleev", domain: "chemistry" }
685
+ };
686
+ var ALL_NICKNAMES = Object.values(NICKNAME_POOL);
687
+ var DOMAIN_PREFERENCES = {
688
+ "security": ["shannon", "turing", "lamarr", "stallman"],
689
+ "bug-hunter": ["darwin", "curie", "feynman", "fermi"],
690
+ "refactor": ["gauss", "hilbert", "euclid", "planck"],
691
+ "audit-log": ["sagan", "hawking", "poincare", "newton"],
692
+ "planner": ["hilbert", "gauss", "turing", "euclid"],
693
+ "researcher": ["sagan", "hawking", "darwin", "pasteur"],
694
+ "explorer": ["marconi", "bell", "columbus", "polo"],
695
+ "testing": ["pasteur", "curie", "fermi", "bohr"],
696
+ "frontend": ["lovelace", "hopper", "babbage", "backus"],
697
+ "backend": ["torvalds", "stallman", "von-neumann", "backus"],
698
+ "database": ["turing", "shannon", "backus", "knuth"],
699
+ "devops": ["tesla", "edison", "faraday", "bell"],
700
+ "security-scanner": ["shannon", "turing", "lamarr", "stallman"],
701
+ "refactor-planner": ["gauss", "hilbert", "planck", "newton"],
702
+ "architect": ["von-neumann", "turing", "gauss", "hilbert"],
703
+ "critic": ["einstein", "feynman", "dirac", "bohr"],
704
+ "e2e": ["hopper", "bell", "marconi", "tesla"],
705
+ "performance": ["knuth", "gauss", "planck", "feynman"],
706
+ "chaos": ["tesla", "edison", "curie", "fermi"],
707
+ "cost": ["oh", "bell", "marconi", "tesla"],
708
+ // default fallback
709
+ "default": ["einstein", "newton", "curie", "tesla", "edison", "turing", "shannon", "hopper", "knuth", "stallman"]
710
+ };
711
+ function assignNickname(role, used) {
712
+ const preferences = [
713
+ ...DOMAIN_PREFERENCES[role] ?? [],
714
+ ...DOMAIN_PREFERENCES["default"] ?? []
715
+ ];
716
+ for (const key of preferences) {
717
+ if (!used.has(key)) {
718
+ return `${NICKNAME_POOL[key].name} (${formatRole(role)})`;
719
+ }
720
+ }
721
+ for (const entry of ALL_NICKNAMES) {
722
+ const key = Object.entries(NICKNAME_POOL).find(([, v]) => v.name === entry.name)?.[0];
723
+ if (key && !used.has(key)) {
724
+ return `${entry.name} (${formatRole(role)})`;
725
+ }
726
+ }
727
+ const counter = used.size + 1;
728
+ return `Scientist #${counter} (${formatRole(role)})`;
729
+ }
730
+ function formatRole(role) {
731
+ return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
732
+ }
733
+
634
734
  // src/types/errors.ts
635
735
  var ERROR_CODES = {
636
736
  // Provider
@@ -3341,11 +3441,46 @@ Working rules:
3341
3441
  - When in doubt, flag as medium rather than ignoring potential issues`
3342
3442
  // Budgets are set by the orchestrator per task — see fleet.ts header.
3343
3443
  };
3444
+ var CRITIC_AGENT = {
3445
+ id: "critic",
3446
+ name: "Critic",
3447
+ role: "critic",
3448
+ prompt: `You are the Critic agent. Your job is to evaluate code quality,
3449
+ architectural decisions, and proposed changes against project conventions,
3450
+ engineering standards, and known quality gates. You do not write code \u2014
3451
+ you judge it.
3452
+
3453
+ Scope:
3454
+ - Evaluate bug severity and fix quality from Bug Hunter reports
3455
+ - Score refactoring plans from Refactor Planner (risk, completeness, trade-offs)
3456
+ - Flag gaps in test coverage, error handling, and edge case coverage
3457
+ - Assess whether a proposed change aligns with existing project patterns
3458
+ - Detect over-engineering or under-engineering relative to the problem scope
3459
+
3460
+ Input format you accept:
3461
+ { "task": "evaluate | score | review", "subject": "bug_report | refactor_plan | diff", "focus": "correctness | maintainability | risk | all" }
3462
+
3463
+ Output: Markdown critic report:
3464
+ - ## Overall Score (0-10 with rationale)
3465
+ - ## Strengths (what's solid)
3466
+ - ## Weaknesses (what needs work)
3467
+ - ## Specific Concerns (with file:line when applicable)
3468
+ - ## Verdict: **Approve / Needs Revision / Reject**
3469
+
3470
+ Working rules:
3471
+ - Be specific \u2014 "looks fine" is not a review. Cite concrete evidence.
3472
+ - When scoring, explain the delta from a perfect score.
3473
+ - If you have no basis to evaluate a concern, say so rather than speculating.
3474
+ - Prioritise correctness over style; correctness issues block approval.
3475
+ - Score thresholds: \u22657 = Approve, 4-6 = Needs Revision, <4 = Reject`
3476
+ // Budgets are set by the orchestrator per task — see fleet.ts header.
3477
+ };
3344
3478
  var FLEET_ROSTER = {
3345
3479
  "audit-log": AUDIT_LOG_AGENT,
3346
3480
  "bug-hunter": BUG_HUNTER_AGENT,
3347
3481
  "refactor-planner": REFACTOR_PLANNER_AGENT,
3348
3482
  "security-scanner": SECURITY_SCANNER_AGENT,
3483
+ "critic": CRITIC_AGENT,
3349
3484
  ...Object.fromEntries(
3350
3485
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
3351
3486
  )
@@ -3355,6 +3490,7 @@ var FLEET_ROSTER_BUDGETS = {
3355
3490
  "bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
3356
3491
  "refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
3357
3492
  "security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
3493
+ "critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
3358
3494
  ...Object.fromEntries(
3359
3495
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
3360
3496
  )
@@ -4428,7 +4564,6 @@ function makeFleetSessionTool(director) {
4428
4564
  type: "object",
4429
4565
  properties: {
4430
4566
  subagentId: { type: "string", description: "Subagent id to read the transcript of." },
4431
- /** Number of trailing lines to return (last N JSONL lines). Default: all. */
4432
4567
  tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
4433
4568
  },
4434
4569
  required: ["subagentId"]
@@ -4464,8 +4599,6 @@ function makeFleetHealthTool(director) {
4464
4599
  id: s.id,
4465
4600
  status: s.status,
4466
4601
  lastEventAt: usage?.lastEventAt,
4467
- // Budget pressure: fraction of each limit consumed if we have it.
4468
- // BudgetWarning events carry used/limit ratios; surface them here.
4469
4602
  budgetPressure: {
4470
4603
  iterations: usage?.iterations,
4471
4604
  toolCalls: usage?.toolCalls,
@@ -4477,6 +4610,415 @@ function makeFleetHealthTool(director) {
4477
4610
  }
4478
4611
  };
4479
4612
  }
4613
+ function makeCollabDebugTool(director) {
4614
+ return {
4615
+ name: "collab_debug",
4616
+ 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).",
4617
+ permission: "auto",
4618
+ mutating: false,
4619
+ inputSchema: {
4620
+ type: "object",
4621
+ properties: {
4622
+ targetPaths: {
4623
+ type: "array",
4624
+ items: { type: "string" },
4625
+ description: "File paths / glob patterns to scan for bugs."
4626
+ },
4627
+ timeoutMs: {
4628
+ type: "number",
4629
+ description: "Timeout in ms. Default: 600000 (10 minutes)."
4630
+ }
4631
+ },
4632
+ required: ["targetPaths"]
4633
+ },
4634
+ async execute(input) {
4635
+ const i = input;
4636
+ if (!i.targetPaths?.length) {
4637
+ return { error: "collab_debug: targetPaths is required and must be non-empty." };
4638
+ }
4639
+ const options = {
4640
+ targetPaths: i.targetPaths,
4641
+ timeoutMs: i.timeoutMs
4642
+ };
4643
+ try {
4644
+ const report = await director.spawnCollab(options);
4645
+ return {
4646
+ sessionId: report.sessionId,
4647
+ overallVerdict: report.overallVerdict,
4648
+ bugCount: report.bugs.length,
4649
+ planCount: report.refactorPlans.length,
4650
+ evaluationCount: report.evaluations.length,
4651
+ summary: report.summary,
4652
+ bugs: report.bugs,
4653
+ refactorPlans: report.refactorPlans,
4654
+ evaluations: report.evaluations
4655
+ };
4656
+ } catch (err) {
4657
+ const msg = err instanceof Error ? err.message : String(err);
4658
+ return { error: "collab_debug failed: " + msg };
4659
+ }
4660
+ }
4661
+ };
4662
+ }
4663
+ function makeFleetEmitTool(director) {
4664
+ return {
4665
+ name: "fleet_emit",
4666
+ 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.",
4667
+ permission: "auto",
4668
+ mutating: false,
4669
+ inputSchema: {
4670
+ type: "object",
4671
+ properties: {
4672
+ type: {
4673
+ type: "string",
4674
+ description: "Event type: bug.found | refactor.plan | critic.evaluation"
4675
+ },
4676
+ payload: {
4677
+ type: "object",
4678
+ description: "Event payload matching the event type."
4679
+ }
4680
+ },
4681
+ required: ["type", "payload"]
4682
+ },
4683
+ async execute(input) {
4684
+ const i = input;
4685
+ director.fleet.emit({
4686
+ subagentId: director.id,
4687
+ ts: Date.now(),
4688
+ type: i.type,
4689
+ payload: i.payload
4690
+ });
4691
+ return { ok: true, event: i.type };
4692
+ }
4693
+ };
4694
+ }
4695
+ var CollabSession = class extends EventEmitter {
4696
+ sessionId;
4697
+ options;
4698
+ snapshot;
4699
+ director;
4700
+ fleetBus;
4701
+ subagentIds = /* @__PURE__ */ new Map();
4702
+ // role → subagentId
4703
+ bugs = /* @__PURE__ */ new Map();
4704
+ plans = /* @__PURE__ */ new Map();
4705
+ evaluations = /* @__PURE__ */ new Map();
4706
+ disposers = new Array();
4707
+ settled = false;
4708
+ timeoutMs;
4709
+ constructor(director, fleetBus, options) {
4710
+ super();
4711
+ this.sessionId = randomUUID();
4712
+ this.options = options;
4713
+ this.director = director;
4714
+ this.fleetBus = fleetBus;
4715
+ this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
4716
+ if (options.prebuiltSnapshot) {
4717
+ this.snapshot = options.prebuiltSnapshot;
4718
+ } else {
4719
+ this.snapshot = {
4720
+ id: this.sessionId,
4721
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4722
+ files: []
4723
+ };
4724
+ }
4725
+ }
4726
+ /**
4727
+ * Read the target files from disk and populate the snapshot.
4728
+ * Call this after construction if you did not provide a prebuiltSnapshot
4729
+ * and want the session to operate on real file contents.
4730
+ */
4731
+ async buildSnapshot() {
4732
+ if (this.snapshot.files.length > 0) return this.snapshot;
4733
+ for (const filePath of this.options.targetPaths) {
4734
+ try {
4735
+ const content = await fsp5.readFile(filePath, "utf8");
4736
+ const ext = filePath.split(".").pop() ?? "";
4737
+ const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
4738
+ this.snapshot.files.push({ path: filePath, content, language });
4739
+ } catch {
4740
+ this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
4741
+ }
4742
+ }
4743
+ return this.snapshot;
4744
+ }
4745
+ /**
4746
+ * Start the collaborative session: snapshot files, spawn all three agents,
4747
+ * wire up event routing, and wait for completion.
4748
+ */
4749
+ async start() {
4750
+ if (this.settled) throw new Error("session already settled");
4751
+ this.settled = true;
4752
+ await this.buildSnapshot();
4753
+ this.wireFleetBus();
4754
+ const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
4755
+ this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
4756
+ this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
4757
+ this.spawnAgent("critic", this.buildCriticTask())
4758
+ ]);
4759
+ this.subagentIds.set("bug-hunter", bugHunterId);
4760
+ this.subagentIds.set("refactor-planner", refactorPlannerId);
4761
+ this.subagentIds.set("critic", criticId);
4762
+ const timeout = new Promise((_, reject) => {
4763
+ setTimeout(
4764
+ () => reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`)),
4765
+ this.timeoutMs
4766
+ );
4767
+ });
4768
+ let results;
4769
+ try {
4770
+ results = await Promise.race([
4771
+ Promise.all([
4772
+ this.director.awaitTasks([bugHunterId]),
4773
+ this.director.awaitTasks([refactorPlannerId]),
4774
+ this.director.awaitTasks([criticId])
4775
+ ]),
4776
+ timeout
4777
+ ]);
4778
+ } catch (err) {
4779
+ this.cleanup();
4780
+ const error = err instanceof Error ? err : new Error(String(err));
4781
+ this.emit("session.error", error);
4782
+ throw error;
4783
+ }
4784
+ for (const result of results.flat()) {
4785
+ await this.parseAndEmit(result);
4786
+ }
4787
+ const report = this.assembleReport();
4788
+ this.cleanup();
4789
+ this.emit("session.done", report);
4790
+ return report;
4791
+ }
4792
+ /**
4793
+ * Parse a completed agent's final text output and route the structured
4794
+ * findings onto the FleetBus. Agents are instructed to emit one JSON object
4795
+ * per finding / plan / evaluation; we scan the output for balanced JSON
4796
+ * objects (tolerating prose, markdown, and ```json fences around them) and
4797
+ * dispatch each by its discriminating key. The wired FleetBus listeners then
4798
+ * collect them into the bugs / plans / evaluations maps.
4799
+ *
4800
+ * Failed or empty results are ignored, and a single malformed object never
4801
+ * aborts the collation pass — see {@link extractJsonObjects}.
4802
+ */
4803
+ async parseAndEmit(result) {
4804
+ if (result.status !== "success" || result.result == null) return;
4805
+ const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
4806
+ for (const obj of this.extractJsonObjects(text)) {
4807
+ const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
4808
+ if (!type) continue;
4809
+ this.fleetBus.emit({
4810
+ subagentId: result.subagentId,
4811
+ taskId: result.taskId,
4812
+ ts: Date.now(),
4813
+ type,
4814
+ payload: obj
4815
+ });
4816
+ }
4817
+ }
4818
+ /**
4819
+ * Extract every top-level JSON object embedded in free-form agent text.
4820
+ * Scans for balanced braces while respecting string literals and escape
4821
+ * sequences, so prose, markdown headers, and code fences around the JSON
4822
+ * are skipped. Malformed spans are dropped silently rather than thrown.
4823
+ */
4824
+ extractJsonObjects(text) {
4825
+ const objects = [];
4826
+ let depth = 0;
4827
+ let start = -1;
4828
+ let inString = false;
4829
+ let escaped = false;
4830
+ for (let i = 0; i < text.length; i++) {
4831
+ const ch = text[i];
4832
+ if (inString) {
4833
+ if (escaped) escaped = false;
4834
+ else if (ch === "\\") escaped = true;
4835
+ else if (ch === '"') inString = false;
4836
+ continue;
4837
+ }
4838
+ if (ch === '"') {
4839
+ inString = true;
4840
+ } else if (ch === "{") {
4841
+ if (depth === 0) start = i;
4842
+ depth++;
4843
+ } else if (ch === "}" && depth > 0) {
4844
+ depth--;
4845
+ if (depth === 0 && start >= 0) {
4846
+ const candidate = text.slice(start, i + 1);
4847
+ try {
4848
+ const parsed = JSON.parse(candidate);
4849
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4850
+ objects.push(parsed);
4851
+ }
4852
+ } catch {
4853
+ }
4854
+ start = -1;
4855
+ }
4856
+ }
4857
+ }
4858
+ return objects;
4859
+ }
4860
+ async spawnAgent(role, taskBrief) {
4861
+ const cfg = {
4862
+ id: `${role}-${this.sessionId}`,
4863
+ name: role,
4864
+ role
4865
+ // No fleet_emit tool — agents output structured JSON, the Director parses
4866
+ // results and emits FleetBus events. This keeps the agent simple and the
4867
+ // routing deterministic.
4868
+ };
4869
+ const subagentId = await this.director.spawn(cfg);
4870
+ await this.director.assign({
4871
+ id: randomUUID(),
4872
+ subagentId,
4873
+ description: taskBrief
4874
+ });
4875
+ return subagentId;
4876
+ }
4877
+ buildBugHunterTask() {
4878
+ this.options.targetPaths.join(", ");
4879
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
4880
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
4881
+ ${f.content}`).join("\n\n");
4882
+ return `You are BugHunter. Scan the following files for bugs and code smells.
4883
+
4884
+ Target files:
4885
+ ${fileContents}
4886
+
4887
+ For each bug found, write ONE valid JSON line to the scratchpad with this exact structure:
4888
+ { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } }.
4889
+ After scanning all files, write your full markdown bug report to the shared scratchpad at:
4890
+ ${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
4891
+ }
4892
+ buildRefactorPlannerTask() {
4893
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
4894
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
4895
+ ${f.content}`).join("\n\n");
4896
+ return `You are RefactorPlanner. Plan refactorings for the following files.
4897
+
4898
+ Target files:
4899
+ ${fileContents}
4900
+
4901
+ 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:
4902
+ { "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [...], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } }.
4903
+ After planning, write your full markdown plan to:
4904
+ ${scratchpad}/refactor-plan-${this.sessionId}.md`;
4905
+ }
4906
+ buildCriticTask() {
4907
+ const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
4908
+ const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
4909
+ ${f.content}`).join("\n\n");
4910
+ return `You are Critic. Evaluate bug findings and refactor plans as they arrive via fleet bus events.
4911
+
4912
+ Target files:
4913
+ ${fileContents}
4914
+
4915
+ Subscribe to bug.found events (from BugHunter) and refactor.plan events (from RefactorPlanner). For each subject, write one JSON line per evaluation:
4916
+ { "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": [...], "weaknesses": [...], "concerns": [...] } }.
4917
+ After all evaluations, write your markdown critic report to:
4918
+ ${scratchpad}/critic-report-${this.sessionId}.md`;
4919
+ }
4920
+ wireFleetBus() {
4921
+ const d1 = this.fleetBus.filter("bug.found", (e) => {
4922
+ const payload = e.payload;
4923
+ if (payload?.finding) {
4924
+ this.bugs.set(payload.finding.id, payload.finding);
4925
+ this.emit("bug.found", payload);
4926
+ }
4927
+ });
4928
+ this.disposers.push(d1);
4929
+ const d2 = this.fleetBus.filter("refactor.plan", (e) => {
4930
+ const payload = e.payload;
4931
+ if (payload?.plan) {
4932
+ this.plans.set(payload.plan.id, payload.plan);
4933
+ this.emit("refactor.plan", payload);
4934
+ }
4935
+ });
4936
+ this.disposers.push(d2);
4937
+ const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
4938
+ const payload = e.payload;
4939
+ if (payload?.evaluation) {
4940
+ this.evaluations.set(payload.evaluation.id, payload.evaluation);
4941
+ this.emit("critic.evaluation", payload);
4942
+ }
4943
+ });
4944
+ this.disposers.push(d3);
4945
+ }
4946
+ assembleReport() {
4947
+ const bugList = Array.from(this.bugs.values());
4948
+ const planList = Array.from(this.plans.values());
4949
+ const evalList = Array.from(this.evaluations.values());
4950
+ const verdictOrder = {
4951
+ approve: 0,
4952
+ needs_revision: 1,
4953
+ reject: 2
4954
+ };
4955
+ const overallVerdict = evalList.reduce(
4956
+ (worst, eval_) => {
4957
+ const w = verdictOrder[worst];
4958
+ const c = verdictOrder[eval_.verdict];
4959
+ return c > w ? eval_.verdict : worst;
4960
+ },
4961
+ "approve"
4962
+ );
4963
+ const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict);
4964
+ return {
4965
+ sessionId: this.sessionId,
4966
+ startedAt: this.snapshot.createdAt,
4967
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
4968
+ targetPaths: this.options.targetPaths,
4969
+ bugs: bugList,
4970
+ refactorPlans: planList,
4971
+ evaluations: evalList,
4972
+ overallVerdict,
4973
+ summary
4974
+ };
4975
+ }
4976
+ buildMarkdownSummary(bugs, plans, evals, overallVerdict) {
4977
+ const lines = [
4978
+ `## Collaborative Debugging Report \u2014 ${this.sessionId}`,
4979
+ "",
4980
+ `**Target:** ${this.options.targetPaths.join(", ")}`,
4981
+ `**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
4982
+ ""
4983
+ ];
4984
+ if (bugs.length > 0) {
4985
+ lines.push("### Bugs Found", "");
4986
+ for (const b of bugs) {
4987
+ lines.push(
4988
+ `- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
4989
+ );
4990
+ }
4991
+ lines.push("");
4992
+ }
4993
+ if (plans.length > 0) {
4994
+ lines.push("### Refactor Plans", "");
4995
+ for (const p of plans) {
4996
+ lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
4997
+ for (const phase of p.phases) {
4998
+ lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
4999
+ }
5000
+ }
5001
+ lines.push("");
5002
+ }
5003
+ if (evals.length > 0) {
5004
+ lines.push("### Critic Evaluations", "");
5005
+ for (const e of evals) {
5006
+ lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
5007
+ for (const c of e.concerns) {
5008
+ if (c.severity === "blocking") {
5009
+ lines.push(` - ${c.description}`);
5010
+ }
5011
+ }
5012
+ }
5013
+ lines.push("");
5014
+ }
5015
+ return lines.join("\n");
5016
+ }
5017
+ cleanup() {
5018
+ for (const dispose of this.disposers) dispose();
5019
+ this.disposers.length = 0;
5020
+ }
5021
+ };
4480
5022
 
4481
5023
  // src/coordination/director.ts
4482
5024
  var FleetSpawnBudgetError = class extends Error {
@@ -4555,6 +5097,8 @@ var Director = class {
4555
5097
  subagentBridges = /* @__PURE__ */ new Map();
4556
5098
  /** Tracks per-spawn config + assigned task ids for manifest writing. */
4557
5099
  manifestEntries = /* @__PURE__ */ new Map();
5100
+ /** Tracks assigned nicknames so the same name is never reused in one fleet. */
5101
+ _usedNicknames = /* @__PURE__ */ new Set();
4558
5102
  manifestPath;
4559
5103
  roster;
4560
5104
  directorPreamble;
@@ -4632,7 +5176,7 @@ var Director = class {
4632
5176
  }, opts.checkpointDebounceMs ?? 250) : null;
4633
5177
  this.fleetManager = opts.fleetManager;
4634
5178
  if (this.sharedScratchpadPath) {
4635
- void fsp4.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
5179
+ void fsp5.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
4636
5180
  (err) => this.logShutdownError("shared_scratchpad_mkdir", err)
4637
5181
  );
4638
5182
  }
@@ -4848,9 +5392,21 @@ var Director = class {
4848
5392
  }
4849
5393
  }
4850
5394
  let result;
5395
+ const needsNickname = config.name === config.role || !config.name || config.name === "subagent";
5396
+ if (needsNickname) {
5397
+ const role = config.role ?? "subagent";
5398
+ if (this.fleetManager) {
5399
+ this.fleetManager.assignNicknameAndRecord("", config, priceLookup);
5400
+ } else {
5401
+ config.name = assignNickname(role, this._usedNicknames);
5402
+ this._usedNicknames.add(config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-"));
5403
+ }
5404
+ }
4851
5405
  result = await this.coordinator.spawn(config);
4852
5406
  if (this.fleetManager) {
4853
- this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
5407
+ if (!needsNickname) {
5408
+ this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
5409
+ }
4854
5410
  } else {
4855
5411
  this.spawnCount += 1;
4856
5412
  this.subagentMeta.set(result.subagentId, {
@@ -5006,7 +5562,7 @@ var Director = class {
5006
5562
  })),
5007
5563
  usage: this.usage.snapshot()
5008
5564
  };
5009
- await fsp4.mkdir(path4.dirname(this.manifestPath), { recursive: true });
5565
+ await fsp5.mkdir(path4.dirname(this.manifestPath), { recursive: true });
5010
5566
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
5011
5567
  return this.manifestPath;
5012
5568
  }
@@ -5175,7 +5731,7 @@ var Director = class {
5175
5731
  const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
5176
5732
  let raw;
5177
5733
  try {
5178
- raw = await fsp4.readFile(filePath, "utf8");
5734
+ raw = await fsp5.readFile(filePath, "utf8");
5179
5735
  } catch {
5180
5736
  return null;
5181
5737
  }
@@ -5286,7 +5842,9 @@ var Director = class {
5286
5842
  makeFleetStatusTool(this),
5287
5843
  makeFleetUsageTool(this),
5288
5844
  makeFleetSessionTool(this),
5289
- makeFleetHealthTool(this)
5845
+ makeFleetHealthTool(this),
5846
+ makeCollabDebugTool(this),
5847
+ makeFleetEmitTool(this)
5290
5848
  ];
5291
5849
  return t;
5292
5850
  }
@@ -5298,6 +5856,17 @@ var Director = class {
5298
5856
  async acquireCheckpointLock() {
5299
5857
  return this.stateCheckpoint ? this.stateCheckpoint.acquireLock() : true;
5300
5858
  }
5859
+ /**
5860
+ * Start a collaborative debugging session: BugHunter, RefactorPlanner,
5861
+ * and Critic run in parallel on the same target files, with findings
5862
+ * flowing through the FleetBus (bug.found → refactor.plan →
5863
+ * critic.evaluation). Returns a structured CollabDebugReport when all
5864
+ * three agents complete or the session times out.
5865
+ */
5866
+ async spawnCollab(options) {
5867
+ const session = new CollabSession(this, this.fleet, options);
5868
+ return session.start();
5869
+ }
5301
5870
  /**
5302
5871
  * Resume from a prior checkpoint snapshot (loaded via
5303
5872
  * `loadDirectorState()`). Re-attach to the fleet mid-flight so
@@ -5578,7 +6147,7 @@ async function readSubagentPartial(opts, subagentId) {
5578
6147
  candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
5579
6148
  } else {
5580
6149
  try {
5581
- const entries = await fsp4.readdir(opts.sessionsRoot, { withFileTypes: true });
6150
+ const entries = await fsp5.readdir(opts.sessionsRoot, { withFileTypes: true });
5582
6151
  for (const entry of entries) {
5583
6152
  if (entry.isDirectory()) {
5584
6153
  candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
@@ -5591,7 +6160,7 @@ async function readSubagentPartial(opts, subagentId) {
5591
6160
  for (const file of candidates) {
5592
6161
  let raw;
5593
6162
  try {
5594
- raw = await fsp4.readFile(file, "utf8");
6163
+ raw = await fsp5.readFile(file, "utf8");
5595
6164
  } catch {
5596
6165
  continue;
5597
6166
  }
@@ -5887,9 +6456,11 @@ function isEmptyMessage(msg) {
5887
6456
  var DefaultSessionStore = class {
5888
6457
  dir;
5889
6458
  events;
6459
+ secretScrubber;
5890
6460
  constructor(opts) {
5891
6461
  this.dir = opts.dir;
5892
6462
  this.events = opts.events;
6463
+ this.secretScrubber = opts.secretScrubber;
5893
6464
  }
5894
6465
  /** Join session ID to its absolute path within the store directory. */
5895
6466
  sessionPath(id, ext) {
@@ -5906,7 +6477,7 @@ var DefaultSessionStore = class {
5906
6477
  const file = path4.join(shardDir, `${id}.jsonl`);
5907
6478
  let handle;
5908
6479
  try {
5909
- handle = await fsp4.open(file, "a", 384);
6480
+ handle = await fsp5.open(file, "a", 384);
5910
6481
  } catch (err) {
5911
6482
  throw new Error(
5912
6483
  `Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
@@ -5914,7 +6485,11 @@ var DefaultSessionStore = class {
5914
6485
  );
5915
6486
  }
5916
6487
  try {
5917
- return new FileSessionWriter(id, handle, startedAt, meta, this.events, { dir: shardDir, filePath: file });
6488
+ return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
6489
+ dir: shardDir,
6490
+ filePath: file,
6491
+ secretScrubber: this.secretScrubber
6492
+ });
5918
6493
  } catch (err) {
5919
6494
  await handle.close().catch(() => {
5920
6495
  });
@@ -5926,7 +6501,7 @@ var DefaultSessionStore = class {
5926
6501
  const data = await this.load(id);
5927
6502
  let handle;
5928
6503
  try {
5929
- handle = await fsp4.open(file, "a", 384);
6504
+ handle = await fsp5.open(file, "a", 384);
5930
6505
  } catch (err) {
5931
6506
  throw new Error(
5932
6507
  `Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
@@ -5944,7 +6519,7 @@ var DefaultSessionStore = class {
5944
6519
  provider: data.metadata.provider
5945
6520
  },
5946
6521
  this.events,
5947
- { resumed: true, dir: this.dir, filePath: file }
6522
+ { resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber }
5948
6523
  );
5949
6524
  return { writer, data };
5950
6525
  } catch (err) {
@@ -5955,7 +6530,7 @@ var DefaultSessionStore = class {
5955
6530
  }
5956
6531
  async load(id) {
5957
6532
  const file = this.sessionPath(id, ".jsonl");
5958
- const raw = await fsp4.readFile(file, "utf8");
6533
+ const raw = await fsp5.readFile(file, "utf8");
5959
6534
  const lines = raw.split("\n").filter((l) => l.trim());
5960
6535
  const events = [];
5961
6536
  for (const line of lines) {
@@ -5990,7 +6565,7 @@ var DefaultSessionStore = class {
5990
6565
  /** Recursively collect all session IDs from shard subdirectories. */
5991
6566
  async collectSessionIds(dir) {
5992
6567
  const ids = [];
5993
- const entries = await fsp4.readdir(dir, { withFileTypes: true });
6568
+ const entries = await fsp5.readdir(dir, { withFileTypes: true });
5994
6569
  for (const entry of entries) {
5995
6570
  const full = path4.join(dir, entry.name);
5996
6571
  if (entry.isDirectory()) {
@@ -6004,11 +6579,11 @@ var DefaultSessionStore = class {
6004
6579
  async summaryFor(id) {
6005
6580
  const manifest = this.sessionPath(id, ".summary.json");
6006
6581
  try {
6007
- const raw = await fsp4.readFile(manifest, "utf8");
6582
+ const raw = await fsp5.readFile(manifest, "utf8");
6008
6583
  return JSON.parse(raw);
6009
6584
  } catch {
6010
6585
  const full = this.sessionPath(id, ".jsonl");
6011
- const stat3 = await fsp4.stat(full);
6586
+ const stat3 = await fsp5.stat(full);
6012
6587
  const summary = await this.summarize(id, stat3.mtime.toISOString());
6013
6588
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
6014
6589
  console.warn(
@@ -6020,8 +6595,8 @@ var DefaultSessionStore = class {
6020
6595
  }
6021
6596
  }
6022
6597
  async delete(id) {
6023
- await fsp4.unlink(this.sessionPath(id, ".jsonl"));
6024
- await fsp4.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
6598
+ await fsp5.unlink(this.sessionPath(id, ".jsonl"));
6599
+ await fsp5.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
6025
6600
  }
6026
6601
  async clearHistory(id) {
6027
6602
  await this.ensureShardDir(id);
@@ -6035,8 +6610,8 @@ var DefaultSessionStore = class {
6035
6610
  provider: "unknown"
6036
6611
  })}
6037
6612
  `;
6038
- await fsp4.writeFile(file, record, "utf8");
6039
- await fsp4.unlink(meta).catch(() => void 0);
6613
+ await fsp5.writeFile(file, record, "utf8");
6614
+ await fsp5.unlink(meta).catch(() => void 0);
6040
6615
  }
6041
6616
  async summarize(id, mtime) {
6042
6617
  try {
@@ -6150,6 +6725,7 @@ var FileSessionWriter = class {
6150
6725
  this.resumed = opts.resumed ?? false;
6151
6726
  this.manifestFile = opts.dir ? path4.join(opts.dir, `${id}.summary.json`) : "";
6152
6727
  this.filePath = opts.filePath ?? "";
6728
+ this.secretScrubber = opts.secretScrubber;
6153
6729
  this.summary = {
6154
6730
  id,
6155
6731
  title: "(empty session)",
@@ -6178,6 +6754,29 @@ var FileSessionWriter = class {
6178
6754
  resumed;
6179
6755
  appendFailCount = 0;
6180
6756
  lastAppendWarnAt = 0;
6757
+ secretScrubber;
6758
+ /**
6759
+ * Scrub secrets out of conversation-turn events before they are observed
6760
+ * for the summary, written to the JSONL log, or surfaced on resume. Only
6761
+ * `user_input` / `llm_response` carry free-form user/model text; other event
6762
+ * types either have no secret-bearing content or are already scrubbed
6763
+ * upstream (tool results). Returns the event unchanged when no scrubber is
6764
+ * configured.
6765
+ */
6766
+ scrubEvent(event) {
6767
+ const s = this.secretScrubber;
6768
+ if (!s) return event;
6769
+ if (event.type === "user_input") {
6770
+ return {
6771
+ ...event,
6772
+ content: typeof event.content === "string" ? s.scrub(event.content) : s.scrubObject(event.content)
6773
+ };
6774
+ }
6775
+ if (event.type === "llm_response") {
6776
+ return { ...event, content: s.scrubObject(event.content) };
6777
+ }
6778
+ return event;
6779
+ }
6181
6780
  promptIndex = 0;
6182
6781
  pendingFileSnapshots = [];
6183
6782
  /** Tracks open tool_use IDs during the current run to serialize on close for resume. */
@@ -6199,7 +6798,7 @@ var FileSessionWriter = class {
6199
6798
  `;
6200
6799
  try {
6201
6800
  if (this.filePath) {
6202
- await fsp4.writeFile(this.filePath, record, { flag: "a", mode: 384 });
6801
+ await fsp5.writeFile(this.filePath, record, { flag: "a", mode: 384 });
6203
6802
  }
6204
6803
  } catch {
6205
6804
  }
@@ -6210,9 +6809,10 @@ var FileSessionWriter = class {
6210
6809
  this.initDone = true;
6211
6810
  await this.writeSessionStartLazy();
6212
6811
  }
6213
- this.observeForSummary(event);
6812
+ const scrubbed = this.scrubEvent(event);
6813
+ this.observeForSummary(scrubbed);
6214
6814
  try {
6215
- await this.handle.appendFile(`${JSON.stringify(event)}
6815
+ await this.handle.appendFile(`${JSON.stringify(scrubbed)}
6216
6816
  `, "utf8");
6217
6817
  } catch (err) {
6218
6818
  this.appendFailCount++;
@@ -6292,7 +6892,7 @@ var FileSessionWriter = class {
6292
6892
  }
6293
6893
  async truncateToCheckpoint(targetPromptIndex) {
6294
6894
  if (!this.filePath) return 0;
6295
- const raw = await fsp4.readFile(this.filePath, "utf8");
6895
+ const raw = await fsp5.readFile(this.filePath, "utf8");
6296
6896
  const lines = raw.split("\n");
6297
6897
  const kept = [];
6298
6898
  let removedCount = 0;
@@ -6330,13 +6930,13 @@ var FileSessionWriter = class {
6330
6930
  }
6331
6931
  const truncated = kept.join("\n");
6332
6932
  const tmpPath = `${this.filePath}.rewind.tmp`;
6333
- await fsp4.writeFile(tmpPath, truncated + "\n", "utf8");
6933
+ await fsp5.writeFile(tmpPath, truncated + "\n", "utf8");
6334
6934
  try {
6335
6935
  await this.handle.close();
6336
- await fsp4.rename(tmpPath, this.filePath);
6337
- this.handle = await fsp4.open(this.filePath, "a", 384);
6936
+ await fsp5.rename(tmpPath, this.filePath);
6937
+ this.handle = await fsp5.open(this.filePath, "a", 384);
6338
6938
  } catch (err) {
6339
- await fsp4.unlink(tmpPath).catch(() => void 0);
6939
+ await fsp5.unlink(tmpPath).catch(() => void 0);
6340
6940
  throw err;
6341
6941
  }
6342
6942
  await this.append({
@@ -6362,7 +6962,7 @@ var FileSessionWriter = class {
6362
6962
  provider: this.meta.provider ?? "unknown"
6363
6963
  })}
6364
6964
  `;
6365
- await fsp4.writeFile(this.filePath, record, "utf8");
6965
+ await fsp5.writeFile(this.filePath, record, "utf8");
6366
6966
  }
6367
6967
  };
6368
6968
  function userInputTitle(content) {
@@ -6488,6 +7088,8 @@ var FleetManager = class {
6488
7088
  pendingTasks = /* @__PURE__ */ new Map();
6489
7089
  subagentMeta = /* @__PURE__ */ new Map();
6490
7090
  priceLookups = /* @__PURE__ */ new Map();
7091
+ /** Tracks which nickname keys are already assigned — prevents collisions. */
7092
+ _usedNicknames = /* @__PURE__ */ new Set();
6491
7093
  /** The coordinator (wired via setCoordinator by Director after construction). */
6492
7094
  coordinator = null;
6493
7095
  constructor(opts = {}) {
@@ -6568,6 +7170,30 @@ var FleetManager = class {
6568
7170
  }
6569
7171
  return null;
6570
7172
  }
7173
+ /**
7174
+ * Assign a memorable nickname (e.g. "Einstein (Bug Hunter)") to the config,
7175
+ * record it so the same name is never reused, then record the spawn.
7176
+ *
7177
+ * Call this INSTEAD of `recordSpawn` when you want automatic nicknames.
7178
+ * The nickname is written back to `config.name` BEFORE the coordinator
7179
+ * sees the config, so the manifest, logs, and fleet UI all show it.
7180
+ */
7181
+ assignNicknameAndRecord(subagentId, config, priceLookup) {
7182
+ const role = config.role ?? "subagent";
7183
+ const nickname = assignNickname(role, this._usedNicknames);
7184
+ const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
7185
+ this._usedNicknames.add(baseKey);
7186
+ config.name = nickname;
7187
+ this.recordSpawn(subagentId, config, priceLookup);
7188
+ return nickname;
7189
+ }
7190
+ /**
7191
+ * Returns the set of already-assigned nickname keys — useful for debugging
7192
+ * and testing.
7193
+ */
7194
+ get usedNicknames() {
7195
+ return this._usedNicknames;
7196
+ }
6571
7197
  /**
6572
7198
  * Records a spawn: increments counter, stores metadata, updates state checkpoint,
6573
7199
  * and schedules a debounced manifest write. Call AFTER the coordinator
@@ -6626,7 +7252,7 @@ var FleetManager = class {
6626
7252
  })),
6627
7253
  usage: this.usage.snapshot()
6628
7254
  };
6629
- await fsp4.mkdir(path4.dirname(this.manifestPath), { recursive: true });
7255
+ await fsp5.mkdir(path4.dirname(this.manifestPath), { recursive: true });
6630
7256
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
6631
7257
  return this.manifestPath;
6632
7258
  }
@@ -6738,6 +7364,6 @@ var FleetManager = class {
6738
7364
  }
6739
7365
  };
6740
7366
 
6741
- 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, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, 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, makeDirectorSessionFactory, makeLLMClassifier, rosterSummaryFromConfigs, scoreAgents };
7367
+ 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, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, 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, rosterSummaryFromConfigs, scoreAgents };
6742
7368
  //# sourceMappingURL=index.js.map
6743
7369
  //# sourceMappingURL=index.js.map