@wrongstack/core 0.260.0 → 0.264.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/{agent-bridge-BbskZ7HH.d.ts → agent-bridge-D8sa1vtv.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-c9DLkaas.d.ts} +11 -9
  3. package/dist/{brain-C2yDd7Lw.d.ts → brain-O1IdKPaK.d.ts} +2 -2
  4. package/dist/{compactor-t0R_AIt_.d.ts → compactor-BBy0rCtB.d.ts} +1 -1
  5. package/dist/{config-FG6As4H5.d.ts → config-Dz2F3H2K.d.ts} +7 -1
  6. package/dist/{context-JFOVvu6z.d.ts → context-BGSpZNSE.d.ts} +11 -0
  7. package/dist/coordination/index.d.ts +1681 -15
  8. package/dist/coordination/index.js +2648 -388
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +1414 -1387
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
  14. package/dist/execution/index.d.ts +15 -15
  15. package/dist/execution/index.js +410 -388
  16. package/dist/execution/index.js.map +1 -1
  17. package/dist/execution/prompt-enhancer.d.ts +2 -2
  18. package/dist/execution/prompt-enhancer.js +7 -1
  19. package/dist/execution/prompt-enhancer.js.map +1 -1
  20. package/dist/extension/index.d.ts +6 -6
  21. package/dist/extension/index.js.map +1 -1
  22. package/dist/{goal-preamble-B1IXJtLX.d.ts → goal-preamble-DzjFuN3p.d.ts} +21 -9
  23. package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-CxWmCGbH.d.ts} +1 -1
  24. package/dist/{index-CebbJB94.d.ts → index-CYIQrXVF.d.ts} +8 -8
  25. package/dist/{index-BPcg4N3M.d.ts → index-CbLSI66_.d.ts} +5 -5
  26. package/dist/index.d.ts +45 -91
  27. package/dist/index.js +14976 -12551
  28. package/dist/index.js.map +1 -1
  29. package/dist/infrastructure/index.d.ts +6 -6
  30. package/dist/kernel/index.d.ts +9 -9
  31. package/dist/kernel/index.js +6 -1
  32. package/dist/kernel/index.js.map +1 -1
  33. package/dist/{llm-selector-DXxI2tlu.d.ts → llm-selector-DzxuZnNz.d.ts} +2 -2
  34. package/dist/{mcp-servers-OwNHo43-.d.ts → mcp-servers-DC4QRPUI.d.ts} +3 -3
  35. package/dist/models/index.d.ts +5 -5
  36. package/dist/models/index.js +6 -1
  37. package/dist/models/index.js.map +1 -1
  38. package/dist/{models-registry-Djlmq4uB.d.ts → models-registry-B_siPxqN.d.ts} +1 -1
  39. package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-CK5Jdj9K.d.ts} +2 -2
  40. package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-DgvD4SCO.d.ts} +6 -6
  41. package/dist/observability/index.d.ts +2 -2
  42. package/dist/observability/index.js +8 -3
  43. package/dist/observability/index.js.map +1 -1
  44. package/dist/{parallel-eternal-engine-0SItuq5r.d.ts → parallel-eternal-engine-bK0JQBR_.d.ts} +9 -9
  45. package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-BPEDlN38.d.ts} +3 -3
  46. package/dist/{permission-BJ7eO9Vl.d.ts → permission-4yvGmMRB.d.ts} +1 -1
  47. package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-C6XpsBOy.d.ts} +2 -2
  48. package/dist/{pipeline-zflkI2dp.d.ts → pipeline-CXCeMz8J.d.ts} +58 -3
  49. package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-BvzRBkJc.d.ts} +5 -5
  50. package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-C5aQpDWE.d.ts} +3 -3
  51. package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CFhdtRzz.d.ts} +1 -1
  52. package/dist/sdd/index.d.ts +8 -8
  53. package/dist/sdd/index.js +39 -29
  54. package/dist/sdd/index.js.map +1 -1
  55. package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-CxiVLbt1.d.ts} +1 -1
  56. package/dist/security/index.d.ts +4 -4
  57. package/dist/security/index.js +208 -203
  58. package/dist/security/index.js.map +1 -1
  59. package/dist/{selector-B7AivHsu.d.ts → selector-gIuhRTkN.d.ts} +1 -1
  60. package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-DkvvrpDt.d.ts} +8 -2
  61. package/dist/{session-reader-DtofsB-2.d.ts → session-reader-KdfVwkKP.d.ts} +1 -1
  62. package/dist/skills/index.js +67 -64
  63. package/dist/skills/index.js.map +1 -1
  64. package/dist/storage/index.d.ts +31 -12
  65. package/dist/storage/index.js +441 -360
  66. package/dist/storage/index.js.map +1 -1
  67. package/dist/tools/index.d.ts +57 -0
  68. package/dist/tools/index.js +411 -0
  69. package/dist/tools/index.js.map +1 -0
  70. package/dist/types/index.d.ts +19 -19
  71. package/dist/types/index.js +703 -694
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/utils/error.d.ts +7 -0
  74. package/dist/utils/error.js +8 -0
  75. package/dist/utils/error.js.map +1 -0
  76. package/dist/utils/index.d.ts +7 -67
  77. package/dist/utils/index.js +17 -5
  78. package/dist/utils/index.js.map +1 -1
  79. package/package.json +5 -1
  80. package/dist/package-outdated-watcher-C70ag2G9.d.ts +0 -581
@@ -1,6 +1,6 @@
1
1
  import { randomUUID, createHash, randomBytes } from 'crypto';
2
2
  import * as fsp6 from 'fs/promises';
3
- import * as path4 from 'path';
3
+ import * as path5 from 'path';
4
4
  import { isAbsolute, resolve } from 'path';
5
5
  import * as os from 'os';
6
6
  import { hostname } from 'os';
@@ -152,9 +152,9 @@ function formatHumanPrompt(request) {
152
152
  return lines.join("\n");
153
153
  }
154
154
  async function atomicWrite(targetPath, content, opts = {}) {
155
- const dir = path4.dirname(targetPath);
155
+ const dir = path5.dirname(targetPath);
156
156
  await fsp6.mkdir(dir, { recursive: true });
157
- const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
157
+ const tmp = path5.join(dir, `.${path5.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
158
158
  try {
159
159
  if (typeof content === "string") {
160
160
  await fsp6.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
@@ -172,8 +172,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
172
172
  }
173
173
  let mode;
174
174
  try {
175
- const stat5 = await fsp6.stat(targetPath);
176
- mode = stat5.mode & 511;
175
+ const stat6 = await fsp6.stat(targetPath);
176
+ mode = stat6.mode & 511;
177
177
  } catch {
178
178
  mode = opts.mode;
179
179
  }
@@ -193,9 +193,9 @@ async function ensureDir(dir) {
193
193
  await fsp6.mkdir(dir, { recursive: true });
194
194
  }
195
195
  async function withFileLock(targetPath, fn, opts = {}) {
196
- const dir = path4.dirname(targetPath);
196
+ const dir = path5.dirname(targetPath);
197
197
  await fsp6.mkdir(dir, { recursive: true });
198
- const lockPath = path4.join(dir, `.${path4.basename(targetPath)}.lock`);
198
+ const lockPath = path5.join(dir, `.${path5.basename(targetPath)}.lock`);
199
199
  const timeoutMs = opts.timeoutMs ?? 5e3;
200
200
  const staleMs = opts.staleMs ?? 3e4;
201
201
  const started = Date.now();
@@ -206,10 +206,15 @@ async function withFileLock(targetPath, fn, opts = {}) {
206
206
  await handle.writeFile(`${process.pid}:${Date.now()}`);
207
207
  break;
208
208
  } catch (err) {
209
- if (err.code !== "EEXIST") throw err;
209
+ const code = err.code;
210
+ if (code === "ENOENT") {
211
+ await fsp6.mkdir(dir, { recursive: true });
212
+ continue;
213
+ }
214
+ if (code !== "EEXIST") throw err;
210
215
  try {
211
- const stat5 = await fsp6.stat(lockPath);
212
- if (Date.now() - stat5.mtimeMs > staleMs) {
216
+ const stat6 = await fsp6.stat(lockPath);
217
+ if (Date.now() - stat6.mtimeMs > staleMs) {
213
218
  await fsp6.unlink(lockPath);
214
219
  continue;
215
220
  }
@@ -259,6 +264,11 @@ async function renameWithRetry(from, to) {
259
264
  throw lastErr;
260
265
  }
261
266
 
267
+ // src/utils/error.ts
268
+ function toErrorMessage(err) {
269
+ return err instanceof Error ? err.message : String(err);
270
+ }
271
+
262
272
  // src/storage/director-state.ts
263
273
  async function acquireDirectorStateLock(lockPath, processId = process.pid) {
264
274
  let existing;
@@ -412,7 +422,7 @@ var DirectorStateCheckpoint = class {
412
422
  } catch (err) {
413
423
  console.warn(
414
424
  "[director-state] checkpoint write failed:",
415
- err instanceof Error ? err.message : String(err)
425
+ toErrorMessage(err)
416
426
  );
417
427
  } finally {
418
428
  this.writing = false;
@@ -434,11 +444,246 @@ function safeParse(input, maxBytes = 5e6) {
434
444
  } catch (err) {
435
445
  return {
436
446
  ok: false,
437
- error: err instanceof Error ? err.message : String(err)
447
+ error: toErrorMessage(err)
438
448
  };
439
449
  }
440
450
  }
441
451
 
452
+ // src/utils/string.ts
453
+ function truncate(s, max) {
454
+ return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
455
+ }
456
+
457
+ // src/utils/expect-defined.ts
458
+ function expectDefined(value, label) {
459
+ if (value === null || value === void 0) {
460
+ const err = new Error("Expected value to be defined");
461
+ err.name = "ExpectDefinedError";
462
+ throw err;
463
+ }
464
+ return value;
465
+ }
466
+ function projectSlug(absRoot) {
467
+ const base = slugify(path5.basename(absRoot));
468
+ const hash = createHash("sha256").update(path5.resolve(absRoot)).digest("hex").slice(0, 6);
469
+ return `${base}-${hash}`;
470
+ }
471
+ function slugify(name) {
472
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
473
+ }
474
+ function wstackGlobalRoot() {
475
+ const fromEnv = process.env["WRONGSTACK_HOME"];
476
+ if (fromEnv && fromEnv.trim().length > 0) return path5.resolve(fromEnv);
477
+ return path5.join(os.homedir(), ".wrongstack");
478
+ }
479
+
480
+ // src/utils/message-invariants.ts
481
+ function repairToolUseAdjacency(messages) {
482
+ const removedToolUses = [];
483
+ const removedToolResults = [];
484
+ let removedMessages = 0;
485
+ let changed = false;
486
+ const out = [];
487
+ for (let i = 0; i < messages.length; i++) {
488
+ const original = expectDefined(messages[i]);
489
+ let msg = original;
490
+ if (hasToolUse(msg)) {
491
+ const nextIds = toolResultIds(messages[i + 1]);
492
+ const filtered = mapContent(msg, (blocks) => {
493
+ const next = [];
494
+ for (const block of blocks) {
495
+ if (block.type === "tool_use" && !nextIds.has(block.id)) {
496
+ removedToolUses.push(block.id);
497
+ changed = true;
498
+ continue;
499
+ }
500
+ next.push(block);
501
+ }
502
+ return next;
503
+ });
504
+ msg = filtered ?? msg;
505
+ }
506
+ if (hasToolResult(msg)) {
507
+ const allowed = toolUseIds(out[out.length - 1]);
508
+ const filtered = mapContent(msg, (blocks) => {
509
+ const next = [];
510
+ for (const block of blocks) {
511
+ if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
512
+ removedToolResults.push(block.tool_use_id);
513
+ changed = true;
514
+ continue;
515
+ }
516
+ next.push(block);
517
+ }
518
+ return next;
519
+ });
520
+ msg = filtered ?? msg;
521
+ }
522
+ if (isEmptyMessage(msg)) {
523
+ removedMessages++;
524
+ changed = true;
525
+ continue;
526
+ }
527
+ out.push(msg);
528
+ }
529
+ return {
530
+ messages: changed ? out : messages,
531
+ report: { changed, removedToolUses, removedToolResults, removedMessages }
532
+ };
533
+ }
534
+ function hasToolUse(msg) {
535
+ return contentBlocks(msg).some((b) => b.type === "tool_use");
536
+ }
537
+ function hasToolResult(msg) {
538
+ return contentBlocks(msg).some((b) => b.type === "tool_result");
539
+ }
540
+ function toolUseIds(msg) {
541
+ const ids = /* @__PURE__ */ new Set();
542
+ if (!msg || msg.role !== "assistant") return ids;
543
+ for (const block of contentBlocks(msg)) {
544
+ if (block.type === "tool_use") ids.add(block.id);
545
+ }
546
+ return ids;
547
+ }
548
+ function toolResultIds(msg) {
549
+ const ids = /* @__PURE__ */ new Set();
550
+ if (!msg || msg.role !== "user") return ids;
551
+ for (const block of contentBlocks(msg)) {
552
+ if (block.type === "tool_result") ids.add(block.tool_use_id);
553
+ }
554
+ return ids;
555
+ }
556
+ function contentBlocks(msg) {
557
+ return msg && Array.isArray(msg.content) ? msg.content : [];
558
+ }
559
+ function mapContent(msg, fn) {
560
+ if (!Array.isArray(msg.content)) return msg;
561
+ const next = fn(msg.content);
562
+ if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
563
+ return msg;
564
+ }
565
+ return { ...msg, content: next };
566
+ }
567
+ function isEmptyMessage(msg) {
568
+ if (typeof msg.content === "string") return msg.content.trim().length === 0;
569
+ return msg.content.length === 0;
570
+ }
571
+ var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
572
+ var IS_WINDOWS = process.platform === "win32";
573
+ var SEP = IS_WINDOWS ? "\\" : "/";
574
+ function isGlob(p) {
575
+ for (const c of p) {
576
+ if (GLOB_CHARS.has(c)) return true;
577
+ }
578
+ return false;
579
+ }
580
+ function globToRegex(pat) {
581
+ let i = 0;
582
+ let re = "^";
583
+ while (i < pat.length) {
584
+ const c = expectDefined(pat[i]);
585
+ if (c === "*") {
586
+ if (pat[i + 1] === "*") {
587
+ re += ".*";
588
+ i += 2;
589
+ if (pat[i] === "/") i++;
590
+ } else {
591
+ re += "[^/\\\\]*";
592
+ i++;
593
+ }
594
+ } else if (c === "?") {
595
+ re += "[^/\\\\]";
596
+ i++;
597
+ } else if (c === "[") {
598
+ let cls = "[";
599
+ i++;
600
+ if (pat[i] === "!" || pat[i] === "^") {
601
+ cls += "^";
602
+ i++;
603
+ }
604
+ while (i < pat.length && pat[i] !== "]") {
605
+ const ch = pat[i] ?? "";
606
+ if (ch === "\\") cls += "\\\\";
607
+ else if (ch === "]" || ch === "^") cls += `\\${ch}`;
608
+ else cls += ch;
609
+ i++;
610
+ }
611
+ cls += "]";
612
+ re += cls;
613
+ i++;
614
+ } else {
615
+ re += c.replace(/[.+^${}()|\\]/g, "\\$&");
616
+ i++;
617
+ }
618
+ }
619
+ return new RegExp(re + "$");
620
+ }
621
+ function baseDir(pat) {
622
+ let i = pat.length - 1;
623
+ while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
624
+ const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
625
+ return cut < 0 ? "." : pat.slice(0, cut);
626
+ }
627
+ async function expandGlob(pattern) {
628
+ if (!isGlob(pattern)) return [pattern];
629
+ const results = /* @__PURE__ */ new Set();
630
+ const abs = isAbsolute(pattern);
631
+ const base = abs ? baseDir(pattern) : baseDir(pattern);
632
+ const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
633
+ async function walk(dir, pat) {
634
+ let entries;
635
+ try {
636
+ entries = await fsp6.readdir(dir);
637
+ } catch {
638
+ return;
639
+ }
640
+ const firstGlob = pat.search(/[*?[[]/);
641
+ if (firstGlob < 0) {
642
+ const re = globToRegex(pat);
643
+ for (const e of entries) {
644
+ if (re.test(e)) {
645
+ const full = `${dir}${SEP}${e}`;
646
+ results.add(abs ? resolve(full) : full);
647
+ }
648
+ }
649
+ return;
650
+ }
651
+ const before = pat.slice(0, firstGlob);
652
+ const rest = pat.slice(firstGlob);
653
+ if (before.endsWith("**")) {
654
+ await walk(dir, rest);
655
+ for (const e of entries) {
656
+ const full = `${dir}${SEP}${e}`;
657
+ try {
658
+ const stat6 = await fsp6.stat(full);
659
+ if (stat6.isDirectory()) await walk(full, rest);
660
+ } catch {
661
+ }
662
+ }
663
+ } else if (before === "") {
664
+ const re = globToRegex(rest);
665
+ for (const e of entries) {
666
+ if (re.test(e)) {
667
+ const full = `${dir}${SEP}${e}`;
668
+ results.add(abs ? resolve(full) : full);
669
+ }
670
+ }
671
+ } else {
672
+ const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
673
+ if (entries.includes(seg)) {
674
+ const full = `${dir}${SEP}${seg}`;
675
+ try {
676
+ const stat6 = await fsp6.stat(full);
677
+ if (stat6.isDirectory()) await walk(full, rest);
678
+ } catch {
679
+ }
680
+ }
681
+ }
682
+ }
683
+ await walk(base === "." ? "." : base, relPat);
684
+ return [...results];
685
+ }
686
+
442
687
  // src/types/errors.ts
443
688
  var ERROR_CODES = {
444
689
  // Provider
@@ -657,133 +902,6 @@ function createMessage(type, from, payload, to) {
657
902
  priority: "normal"
658
903
  };
659
904
  }
660
-
661
- // src/utils/expect-defined.ts
662
- function expectDefined(value, label) {
663
- if (value === null || value === void 0) {
664
- const err = new Error("Expected value to be defined");
665
- err.name = "ExpectDefinedError";
666
- throw err;
667
- }
668
- return value;
669
- }
670
- var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
671
- var IS_WINDOWS = process.platform === "win32";
672
- var SEP = IS_WINDOWS ? "\\" : "/";
673
- function isGlob(p) {
674
- for (const c of p) {
675
- if (GLOB_CHARS.has(c)) return true;
676
- }
677
- return false;
678
- }
679
- function globToRegex(pat) {
680
- let i = 0;
681
- let re = "^";
682
- while (i < pat.length) {
683
- const c = expectDefined(pat[i]);
684
- if (c === "*") {
685
- if (pat[i + 1] === "*") {
686
- re += ".*";
687
- i += 2;
688
- if (pat[i] === "/") i++;
689
- } else {
690
- re += "[^/\\\\]*";
691
- i++;
692
- }
693
- } else if (c === "?") {
694
- re += "[^/\\\\]";
695
- i++;
696
- } else if (c === "[") {
697
- let cls = "[";
698
- i++;
699
- if (pat[i] === "!" || pat[i] === "^") {
700
- cls += "^";
701
- i++;
702
- }
703
- while (i < pat.length && pat[i] !== "]") {
704
- const ch = pat[i] ?? "";
705
- if (ch === "\\") cls += "\\\\";
706
- else if (ch === "]" || ch === "^") cls += `\\${ch}`;
707
- else cls += ch;
708
- i++;
709
- }
710
- cls += "]";
711
- re += cls;
712
- i++;
713
- } else {
714
- re += c.replace(/[.+^${}()|\\]/g, "\\$&");
715
- i++;
716
- }
717
- }
718
- return new RegExp(re + "$");
719
- }
720
- function baseDir(pat) {
721
- let i = pat.length - 1;
722
- while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
723
- const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
724
- return cut < 0 ? "." : pat.slice(0, cut);
725
- }
726
- async function expandGlob(pattern) {
727
- if (!isGlob(pattern)) return [pattern];
728
- const results = /* @__PURE__ */ new Set();
729
- const abs = isAbsolute(pattern);
730
- const base = abs ? baseDir(pattern) : baseDir(pattern);
731
- const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
732
- async function walk(dir, pat) {
733
- let entries;
734
- try {
735
- entries = await fsp6.readdir(dir);
736
- } catch {
737
- return;
738
- }
739
- const firstGlob = pat.search(/[*?[[]/);
740
- if (firstGlob < 0) {
741
- const re = globToRegex(pat);
742
- for (const e of entries) {
743
- if (re.test(e)) {
744
- const full = `${dir}${SEP}${e}`;
745
- results.add(abs ? resolve(full) : full);
746
- }
747
- }
748
- return;
749
- }
750
- const before = pat.slice(0, firstGlob);
751
- const rest = pat.slice(firstGlob);
752
- if (before.endsWith("**")) {
753
- await walk(dir, rest);
754
- for (const e of entries) {
755
- const full = `${dir}${SEP}${e}`;
756
- try {
757
- const stat5 = await fsp6.stat(full);
758
- if (stat5.isDirectory()) await walk(full, rest);
759
- } catch {
760
- }
761
- }
762
- } else if (before === "") {
763
- const re = globToRegex(rest);
764
- for (const e of entries) {
765
- if (re.test(e)) {
766
- const full = `${dir}${SEP}${e}`;
767
- results.add(abs ? resolve(full) : full);
768
- }
769
- }
770
- } else {
771
- const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
772
- if (entries.includes(seg)) {
773
- const full = `${dir}${SEP}${seg}`;
774
- try {
775
- const stat5 = await fsp6.stat(full);
776
- if (stat5.isDirectory()) await walk(full, rest);
777
- } catch {
778
- }
779
- }
780
- }
781
- }
782
- await walk(base === "." ? "." : base, relPat);
783
- return [...results];
784
- }
785
-
786
- // src/coordination/collab-debug.ts
787
905
  var DEFAULT_MAX_TARGET_FILES = 30;
788
906
  var DirectorAlertLevel = /* @__PURE__ */ ((DirectorAlertLevel2) => {
789
907
  DirectorAlertLevel2["WARNING"] = "warning";
@@ -878,7 +996,7 @@ var CollabSession = class extends EventEmitter {
878
996
  }
879
997
  for (const filePath of allFiles) {
880
998
  try {
881
- const [content, stat5] = await Promise.all([
999
+ const [content, stat6] = await Promise.all([
882
1000
  fsp6.readFile(filePath, "utf8"),
883
1001
  fsp6.stat(filePath)
884
1002
  ]);
@@ -888,8 +1006,8 @@ var CollabSession = class extends EventEmitter {
888
1006
  path: filePath,
889
1007
  content,
890
1008
  language,
891
- snapshotMtimeMs: stat5.mtimeMs,
892
- snapshotSizeBytes: stat5.size
1009
+ snapshotMtimeMs: stat6.mtimeMs,
1010
+ snapshotSizeBytes: stat6.size
893
1011
  });
894
1012
  } catch {
895
1013
  this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
@@ -1302,9 +1420,9 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
1302
1420
  for (const file of this.snapshot.files) {
1303
1421
  if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
1304
1422
  try {
1305
- const stat5 = await fsp6.stat(file.path);
1306
- const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat5.mtimeMs > file.snapshotMtimeMs + 1;
1307
- const sizeChanged = file.snapshotSizeBytes !== void 0 && stat5.size !== file.snapshotSizeBytes;
1423
+ const stat6 = await fsp6.stat(file.path);
1424
+ const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat6.mtimeMs > file.snapshotMtimeMs + 1;
1425
+ const sizeChanged = file.snapshotSizeBytes !== void 0 && stat6.size !== file.snapshotSizeBytes;
1308
1426
  if (mtimeChanged || sizeChanged) {
1309
1427
  warnings.push(`${file.path} changed after the collab snapshot was captured.`);
1310
1428
  }
@@ -4329,7 +4447,7 @@ function makeSpawnTool(director, roster) {
4329
4447
  if (err instanceof FleetCostCapError) {
4330
4448
  return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
4331
4449
  }
4332
- return { error: err instanceof Error ? err.message : String(err) };
4450
+ return { error: toErrorMessage(err) };
4333
4451
  }
4334
4452
  }
4335
4453
  };
@@ -4411,7 +4529,7 @@ function makeAskTool(director) {
4411
4529
  _hint: "Response was large and stored. Use ask_result with the key to retrieve it."
4412
4530
  };
4413
4531
  } catch (err) {
4414
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
4532
+ return { ok: false, error: toErrorMessage(err) };
4415
4533
  }
4416
4534
  }
4417
4535
  };
@@ -4637,7 +4755,7 @@ function makeCollabDebugTool(director) {
4637
4755
  evaluations: report.evaluations
4638
4756
  };
4639
4757
  } catch (err) {
4640
- const msg = err instanceof Error ? err.message : String(err);
4758
+ const msg = toErrorMessage(err);
4641
4759
  return { error: "collab_debug failed: " + msg };
4642
4760
  }
4643
4761
  }
@@ -5313,11 +5431,6 @@ var SubagentBudget = class _SubagentBudget {
5313
5431
  }
5314
5432
  };
5315
5433
 
5316
- // src/utils/string.ts
5317
- function truncate(s, max) {
5318
- return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
5319
- }
5320
-
5321
5434
  // src/types/provider.ts
5322
5435
  var ProviderError = class extends WrongStackError {
5323
5436
  status;
@@ -5397,7 +5510,7 @@ function classifySubagentError(err, hints = {}) {
5397
5510
  const baseMessage2 = err.describe();
5398
5511
  return providerErrorToSubagentError(err, baseMessage2, cause);
5399
5512
  }
5400
- const baseMessage = err instanceof Error ? err.message : String(err);
5513
+ const baseMessage = toErrorMessage(err);
5401
5514
  if (err instanceof BudgetExceededError) {
5402
5515
  const map = {
5403
5516
  iterations: "budget_iterations",
@@ -7383,7 +7496,7 @@ var Director = class _Director {
7383
7496
  })),
7384
7497
  usage: this.usage.snapshot()
7385
7498
  };
7386
- await fsp6.mkdir(path4.dirname(this.manifestPath), { recursive: true });
7499
+ await fsp6.mkdir(path5.dirname(this.manifestPath), { recursive: true });
7387
7500
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
7388
7501
  return this.manifestPath;
7389
7502
  }
@@ -7433,7 +7546,7 @@ var Director = class _Director {
7433
7546
  * listener for structured collection, and never affects exit code.
7434
7547
  */
7435
7548
  logShutdownError(phase, err) {
7436
- const detail = err instanceof Error ? err.message : String(err);
7549
+ const detail = toErrorMessage(err);
7437
7550
  process.emitWarning(
7438
7551
  `Director shutdown phase "${phase}" failed: ${detail}`,
7439
7552
  "DirectorShutdownWarning"
@@ -7597,7 +7710,7 @@ var Director = class _Director {
7597
7710
  */
7598
7711
  async readSession(subagentId, tail) {
7599
7712
  if (!this.sessionsRoot) return null;
7600
- const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
7713
+ const filePath = path5.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
7601
7714
  let raw;
7602
7715
  try {
7603
7716
  raw = await fsp6.readFile(filePath, "utf8");
@@ -8026,7 +8139,7 @@ function createDelegateTool(opts) {
8026
8139
  summary
8027
8140
  };
8028
8141
  } catch (err) {
8029
- const message = err instanceof Error ? err.message : String(err);
8142
+ const message = toErrorMessage(err);
8030
8143
  opts.events?.emit("delegate.completed", {
8031
8144
  target,
8032
8145
  task: i.task,
@@ -8127,13 +8240,13 @@ async function readSubagentPartial(opts, subagentId) {
8127
8240
  if (!opts.sessionsRoot) return void 0;
8128
8241
  const candidates = [];
8129
8242
  if (opts.directorRunId) {
8130
- candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
8243
+ candidates.push(path5.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
8131
8244
  } else {
8132
8245
  try {
8133
8246
  const entries = await fsp6.readdir(opts.sessionsRoot, { withFileTypes: true });
8134
8247
  for (const entry of entries) {
8135
8248
  if (entry.isDirectory()) {
8136
- candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
8249
+ candidates.push(path5.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
8137
8250
  }
8138
8251
  }
8139
8252
  } catch {
@@ -8389,100 +8502,6 @@ function makeAgentSubagentRunner(opts) {
8389
8502
  function defaultFormatTaskInput(task) {
8390
8503
  return task.description ?? "";
8391
8504
  }
8392
-
8393
- // src/utils/message-invariants.ts
8394
- function repairToolUseAdjacency(messages) {
8395
- const removedToolUses = [];
8396
- const removedToolResults = [];
8397
- let removedMessages = 0;
8398
- let changed = false;
8399
- const out = [];
8400
- for (let i = 0; i < messages.length; i++) {
8401
- const original = expectDefined(messages[i]);
8402
- let msg = original;
8403
- if (hasToolUse(msg)) {
8404
- const nextIds = toolResultIds(messages[i + 1]);
8405
- const filtered = mapContent(msg, (blocks) => {
8406
- const next = [];
8407
- for (const block of blocks) {
8408
- if (block.type === "tool_use" && !nextIds.has(block.id)) {
8409
- removedToolUses.push(block.id);
8410
- changed = true;
8411
- continue;
8412
- }
8413
- next.push(block);
8414
- }
8415
- return next;
8416
- });
8417
- msg = filtered ?? msg;
8418
- }
8419
- if (hasToolResult(msg)) {
8420
- const allowed = toolUseIds(out[out.length - 1]);
8421
- const filtered = mapContent(msg, (blocks) => {
8422
- const next = [];
8423
- for (const block of blocks) {
8424
- if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
8425
- removedToolResults.push(block.tool_use_id);
8426
- changed = true;
8427
- continue;
8428
- }
8429
- next.push(block);
8430
- }
8431
- return next;
8432
- });
8433
- msg = filtered ?? msg;
8434
- }
8435
- if (isEmptyMessage(msg)) {
8436
- removedMessages++;
8437
- changed = true;
8438
- continue;
8439
- }
8440
- out.push(msg);
8441
- }
8442
- return {
8443
- messages: changed ? out : messages,
8444
- report: { changed, removedToolUses, removedToolResults, removedMessages }
8445
- };
8446
- }
8447
- function hasToolUse(msg) {
8448
- return contentBlocks(msg).some((b) => b.type === "tool_use");
8449
- }
8450
- function hasToolResult(msg) {
8451
- return contentBlocks(msg).some((b) => b.type === "tool_result");
8452
- }
8453
- function toolUseIds(msg) {
8454
- const ids = /* @__PURE__ */ new Set();
8455
- if (!msg || msg.role !== "assistant") return ids;
8456
- for (const block of contentBlocks(msg)) {
8457
- if (block.type === "tool_use") ids.add(block.id);
8458
- }
8459
- return ids;
8460
- }
8461
- function toolResultIds(msg) {
8462
- const ids = /* @__PURE__ */ new Set();
8463
- if (!msg || msg.role !== "user") return ids;
8464
- for (const block of contentBlocks(msg)) {
8465
- if (block.type === "tool_result") ids.add(block.tool_use_id);
8466
- }
8467
- return ids;
8468
- }
8469
- function contentBlocks(msg) {
8470
- return msg && Array.isArray(msg.content) ? msg.content : [];
8471
- }
8472
- function mapContent(msg, fn) {
8473
- if (!Array.isArray(msg.content)) return msg;
8474
- const next = fn(msg.content);
8475
- if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
8476
- return msg;
8477
- }
8478
- return { ...msg, content: next };
8479
- }
8480
- function isEmptyMessage(msg) {
8481
- if (typeof msg.content === "string") return msg.content.trim().length === 0;
8482
- return msg.content.length === 0;
8483
- }
8484
-
8485
- // src/storage/session-store.ts
8486
8505
  function sanitizeModel(model) {
8487
8506
  return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
8488
8507
  }
@@ -8538,11 +8557,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
8538
8557
  }
8539
8558
  /** Absolute path to the session index file. */
8540
8559
  get indexFile() {
8541
- return path4.join(this.dir, "_index.jsonl");
8560
+ return path5.join(this.dir, "_index.jsonl");
8542
8561
  }
8543
8562
  /** Join session ID to its absolute path within the store directory. */
8544
8563
  sessionPath(id, ext) {
8545
- return path4.join(this.dir, `${id}${ext}`);
8564
+ return path5.join(this.dir, `${id}${ext}`);
8546
8565
  }
8547
8566
  /**
8548
8567
  * Ensure the directory implied by the session ID exists. When the ID
@@ -8550,7 +8569,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8550
8569
  * subdirectory so sessions group naturally by day.
8551
8570
  */
8552
8571
  async ensureShardDir(id) {
8553
- const dirPath = path4.dirname(path4.join(this.dir, id));
8572
+ const dirPath = path5.dirname(path5.join(this.dir, id));
8554
8573
  await ensureDir(dirPath);
8555
8574
  return dirPath;
8556
8575
  }
@@ -8558,15 +8577,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
8558
8577
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
8559
8578
  const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
8560
8579
  const shardDir = await this.ensureShardDir(id);
8561
- const file = path4.join(shardDir, `${path4.basename(id)}.jsonl`);
8580
+ const file = path5.join(shardDir, `${path5.basename(id)}.jsonl`);
8562
8581
  const t0 = Date.now();
8563
8582
  let handle;
8564
8583
  try {
8565
8584
  handle = await fsp6.open(file, "a", 384);
8566
8585
  } catch (err) {
8567
- this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), false);
8586
+ this.emitError(id, file, "create", toErrorMessage(err), false);
8568
8587
  throw new Error(
8569
- `Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
8588
+ `Failed to open session file: ${toErrorMessage(err)}`,
8570
8589
  { cause: err }
8571
8590
  );
8572
8591
  }
@@ -8586,7 +8605,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8586
8605
  message: e instanceof Error ? e.message : String(e),
8587
8606
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8588
8607
  })));
8589
- this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), true);
8608
+ this.emitError(id, file, "create", toErrorMessage(err), true);
8590
8609
  throw err;
8591
8610
  }
8592
8611
  }
@@ -8598,9 +8617,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
8598
8617
  try {
8599
8618
  handle = await fsp6.open(file, "a", 384);
8600
8619
  } catch (err) {
8601
- this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), false);
8620
+ this.emitError(id, file, "resume", toErrorMessage(err), false);
8602
8621
  throw new Error(
8603
- `Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
8622
+ `Failed to open session "${id}" for append: ${toErrorMessage(err)}`,
8604
8623
  { cause: err }
8605
8624
  );
8606
8625
  }
@@ -8620,7 +8639,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8620
8639
  // Shard directory (sessions/<date>/) — must match create() so the
8621
8640
  // .summary.json sidecar lands next to the JSONL instead of the
8622
8641
  // sessions root (where summaryFor() would never find it).
8623
- dir: path4.dirname(file),
8642
+ dir: path5.dirname(file),
8624
8643
  filePath: file,
8625
8644
  secretScrubber: this.secretScrubber,
8626
8645
  onClose: (s) => this.appendToIndex(s)
@@ -8635,7 +8654,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8635
8654
  message: e instanceof Error ? e.message : String(e),
8636
8655
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8637
8656
  })));
8638
- this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), true);
8657
+ this.emitError(id, file, "resume", toErrorMessage(err), true);
8639
8658
  throw err;
8640
8659
  }
8641
8660
  }
@@ -8663,7 +8682,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8663
8682
  return { metadata: meta, events, messages, usage, toolCallEnds };
8664
8683
  } catch (err) {
8665
8684
  outcome = "failure";
8666
- errorMsg = err instanceof Error ? err.message : String(err);
8685
+ errorMsg = toErrorMessage(err);
8667
8686
  throw err;
8668
8687
  } finally {
8669
8688
  this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
@@ -8746,7 +8765,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8746
8765
  await fsp6.rename(tmp, this.indexFile);
8747
8766
  } catch (err) {
8748
8767
  outcome = "failure";
8749
- errorMsg = err instanceof Error ? err.message : String(err);
8768
+ errorMsg = toErrorMessage(err);
8750
8769
  } finally {
8751
8770
  this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
8752
8771
  }
@@ -8814,7 +8833,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8814
8833
  continue;
8815
8834
  if (entry.isDirectory()) {
8816
8835
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
8817
- ids.push(...await this.collectSessionIds(path4.join(dir, entry.name), childPrefix, depth + 1));
8836
+ ids.push(...await this.collectSessionIds(path5.join(dir, entry.name), childPrefix, depth + 1));
8818
8837
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
8819
8838
  if (entry.name === "_index.jsonl") continue;
8820
8839
  const base = entry.name.replace(/\.jsonl$/, "");
@@ -8834,10 +8853,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
8834
8853
  return JSON.parse(raw);
8835
8854
  } catch {
8836
8855
  const full = this.sessionPath(id, ".jsonl");
8837
- const stat5 = await fsp6.stat(full);
8838
- const summary = await this.summarize(id, stat5.mtime.toISOString());
8856
+ const stat6 = await fsp6.stat(full);
8857
+ const summary = await this.summarize(id, stat6.mtime.toISOString());
8839
8858
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
8840
- const msg = err instanceof Error ? err.message : String(err);
8859
+ const msg = toErrorMessage(err);
8841
8860
  this.emitError(id, manifest, "summary_fallback", msg, true);
8842
8861
  console.warn(JSON.stringify({
8843
8862
  level: "warn",
@@ -8865,14 +8884,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
8865
8884
  async deleteSession(id) {
8866
8885
  const jsonlPath = this.sessionPath(id, ".jsonl");
8867
8886
  const summaryPath = this.sessionPath(id, ".summary.json");
8868
- const shardDir = path4.dirname(path4.join(this.dir, id));
8869
- const base = path4.basename(id);
8870
- const sessDir = path4.join(shardDir, base);
8887
+ const shardDir = path5.dirname(path5.join(this.dir, id));
8888
+ const base = path5.basename(id);
8889
+ const sessDir = path5.join(shardDir, base);
8871
8890
  const deletions = [
8872
8891
  fsp6.unlink(jsonlPath),
8873
8892
  fsp6.unlink(summaryPath),
8874
- fsp6.unlink(path4.join(shardDir, `${base}.plan.json`)),
8875
- fsp6.unlink(path4.join(shardDir, `${base}.todos.json`))
8893
+ fsp6.unlink(path5.join(shardDir, `${base}.plan.json`)),
8894
+ fsp6.unlink(path5.join(shardDir, `${base}.todos.json`))
8876
8895
  ];
8877
8896
  const results = await Promise.allSettled(deletions);
8878
8897
  for (const r of results) {
@@ -8894,7 +8913,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8894
8913
  level: "warn",
8895
8914
  event: "session_store.rmdir_failed",
8896
8915
  sessionId: id,
8897
- message: err instanceof Error ? err.message : String(err),
8916
+ message: toErrorMessage(err),
8898
8917
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8899
8918
  }));
8900
8919
  });
@@ -8908,17 +8927,17 @@ var DefaultSessionStore = class _DefaultSessionStore {
8908
8927
  let deleted = 0;
8909
8928
  let activeSessionId = null;
8910
8929
  try {
8911
- const raw = await fsp6.readFile(path4.join(this.dir, "active.json"), "utf8");
8930
+ const raw = await fsp6.readFile(path5.join(this.dir, "active.json"), "utf8");
8912
8931
  const active = JSON.parse(raw);
8913
8932
  activeSessionId = active.sessionId ?? null;
8914
8933
  } catch {
8915
8934
  }
8916
8935
  const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
8917
8936
  const pruneFile = async (dir, name, prefix) => {
8918
- const jsonlPath = path4.join(dir, name);
8937
+ const jsonlPath = path5.join(dir, name);
8919
8938
  try {
8920
- const stat5 = await fsp6.stat(jsonlPath);
8921
- if (stat5.mtimeMs >= cutoff) return;
8939
+ const stat6 = await fsp6.stat(jsonlPath);
8940
+ if (stat6.mtimeMs >= cutoff) return;
8922
8941
  } catch {
8923
8942
  return;
8924
8943
  }
@@ -8935,7 +8954,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8935
8954
  continue;
8936
8955
  }
8937
8956
  if (!entry.isDirectory()) continue;
8938
- const dateDir = path4.join(this.dir, entry.name);
8957
+ const dateDir = path5.join(this.dir, entry.name);
8939
8958
  const files = await fsp6.readdir(dateDir, { withFileTypes: true }).catch(() => []);
8940
8959
  for (const file of files) {
8941
8960
  if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
@@ -8947,7 +8966,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
8947
8966
  }
8948
8967
  for (const entry of entries) {
8949
8968
  if (!entry.isDirectory()) continue;
8950
- const dateDir = path4.join(this.dir, entry.name);
8969
+ const dateDir = path5.join(this.dir, entry.name);
8951
8970
  try {
8952
8971
  const remaining = await fsp6.readdir(dateDir);
8953
8972
  if (remaining.length === 0) {
@@ -9122,7 +9141,7 @@ var FileSessionWriter = class _FileSessionWriter {
9122
9141
  this.meta = meta;
9123
9142
  this.events = events;
9124
9143
  this.resumed = opts.resumed ?? false;
9125
- this.manifestFile = opts.dir ? path4.join(opts.dir, `${path4.basename(id)}.summary.json`) : "";
9144
+ this.manifestFile = opts.dir ? path5.join(opts.dir, `${path5.basename(id)}.summary.json`) : "";
9126
9145
  this.filePath = opts.filePath ?? "";
9127
9146
  this.secretScrubber = opts.secretScrubber;
9128
9147
  this.onCloseCb = opts.onClose;
@@ -9329,7 +9348,7 @@ var FileSessionWriter = class _FileSessionWriter {
9329
9348
  await this.enqueueWrite(batch);
9330
9349
  } catch (err) {
9331
9350
  outcome = "failure";
9332
- errorMsg = err instanceof Error ? err.message : String(err);
9351
+ errorMsg = toErrorMessage(err);
9333
9352
  this.appendFailCount += eventCount;
9334
9353
  const now = Date.now();
9335
9354
  if (now - this.lastAppendWarnAt > 5e3) {
@@ -9337,7 +9356,7 @@ var FileSessionWriter = class _FileSessionWriter {
9337
9356
  const tail = suppressed > 0 ? ` (+${suppressed} suppressed)` : "";
9338
9357
  console.warn(
9339
9358
  "[session] flush failed:",
9340
- err instanceof Error ? err.message : String(err),
9359
+ toErrorMessage(err),
9341
9360
  tail
9342
9361
  );
9343
9362
  this.lastAppendWarnAt = now;
@@ -9427,7 +9446,7 @@ var FileSessionWriter = class _FileSessionWriter {
9427
9446
  await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
9428
9447
  } catch (err) {
9429
9448
  outcome = "failure";
9430
- errorMsg = err instanceof Error ? err.message : String(err);
9449
+ errorMsg = toErrorMessage(err);
9431
9450
  } finally {
9432
9451
  this.events?.emit("storage.write", {
9433
9452
  sessionId: this.id,
@@ -9448,7 +9467,7 @@ var FileSessionWriter = class _FileSessionWriter {
9448
9467
  await this.onCloseCb?.(this.summary);
9449
9468
  } catch (err) {
9450
9469
  idxOutcome = "failure";
9451
- idxError = err instanceof Error ? err.message : String(err);
9470
+ idxError = toErrorMessage(err);
9452
9471
  } finally {
9453
9472
  this.events?.emit("storage.write", {
9454
9473
  sessionId: this.summary.id,
@@ -9625,9 +9644,9 @@ function makeDirectorSessionFactory(opts) {
9625
9644
  let dir;
9626
9645
  if (opts.store) {
9627
9646
  store = opts.store;
9628
- dir = opts.sessionsRoot ? path4.join(opts.sessionsRoot, runId) : "(caller-managed)";
9647
+ dir = opts.sessionsRoot ? path5.join(opts.sessionsRoot, runId) : "(caller-managed)";
9629
9648
  } else if (opts.sessionsRoot) {
9630
- dir = path4.join(opts.sessionsRoot, runId);
9649
+ dir = path5.join(opts.sessionsRoot, runId);
9631
9650
  store = new DefaultSessionStore({ dir });
9632
9651
  } else {
9633
9652
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -9935,7 +9954,7 @@ var FleetManager = class {
9935
9954
  })),
9936
9955
  usage: this.usage.snapshot()
9937
9956
  };
9938
- await fsp6.mkdir(path4.dirname(this.manifestPath), { recursive: true });
9957
+ await fsp6.mkdir(path5.dirname(this.manifestPath), { recursive: true });
9939
9958
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
9940
9959
  return this.manifestPath;
9941
9960
  }
@@ -9956,7 +9975,7 @@ var FleetManager = class {
9956
9975
  if (!this.manifestPath) return;
9957
9976
  if (this.manifestDebounceMs === 0) {
9958
9977
  void this.writeManifest().catch((err) => {
9959
- const detail = err instanceof Error ? err.message : String(err);
9978
+ const detail = toErrorMessage(err);
9960
9979
  process.emitWarning(
9961
9980
  `FleetManager manifest write failed: ${detail}`,
9962
9981
  "FleetManagerWarning"
@@ -9969,7 +9988,7 @@ var FleetManager = class {
9969
9988
  this.manifestTimer = setTimeout(() => {
9970
9989
  this.manifestTimer = null;
9971
9990
  void this.writeManifest().catch((err) => {
9972
- const detail = err instanceof Error ? err.message : String(err);
9991
+ const detail = toErrorMessage(err);
9973
9992
  process.emitWarning(
9974
9993
  `FleetManager manifest write failed: ${detail}`,
9975
9994
  "FleetManagerWarning"
@@ -9988,7 +10007,7 @@ var FleetManager = class {
9988
10007
  this.manifestTimer = null;
9989
10008
  }
9990
10009
  await this.writeManifest().catch((err) => {
9991
- const detail = err instanceof Error ? err.message : String(err);
10010
+ const detail = toErrorMessage(err);
9992
10011
  process.emitWarning(
9993
10012
  `FleetManager manifest write failed: ${detail}`,
9994
10013
  "FleetManagerWarning"
@@ -10083,7 +10102,7 @@ var LINE_SEPARATOR = "\n";
10083
10102
  var DefaultMailbox = class {
10084
10103
  filePath;
10085
10104
  constructor(sessionDir) {
10086
- this.filePath = path4.join(sessionDir, MAILBOX_FILE);
10105
+ this.filePath = path5.join(sessionDir, MAILBOX_FILE);
10087
10106
  }
10088
10107
  get mailboxPath() {
10089
10108
  return this.filePath;
@@ -10108,7 +10127,7 @@ var DefaultMailbox = class {
10108
10127
  taskContext: input.taskContext
10109
10128
  };
10110
10129
  const line = JSON.stringify(msg) + LINE_SEPARATOR;
10111
- await fsp6.mkdir(path4.dirname(this.filePath), { recursive: true });
10130
+ await fsp6.mkdir(path5.dirname(this.filePath), { recursive: true });
10112
10131
  await withFileLock(this.filePath, async () => {
10113
10132
  await fsp6.appendFile(this.filePath, line, "utf8");
10114
10133
  });
@@ -10148,29 +10167,43 @@ var DefaultMailbox = class {
10148
10167
  }
10149
10168
  // ── Ack ───────────────────────────────────────────────────────────────
10150
10169
  async ack(input) {
10151
- let result = null;
10170
+ const updated = await this.ackMany({ acks: [input] });
10171
+ return updated.length > 0 ? updated[0] : null;
10172
+ }
10173
+ async ackMany(input) {
10174
+ if (input.acks.length === 0) return [];
10175
+ const updated = [];
10176
+ const byId = /* @__PURE__ */ new Map();
10177
+ for (const a of input.acks) byId.set(a.messageId, a);
10152
10178
  await withFileLock(this.filePath, async () => {
10153
10179
  const all = await this._readAll();
10154
- const idx = all.findIndex((m) => m.id === input.messageId);
10155
- if (idx === -1) return;
10156
- const msg = all[idx];
10157
10180
  const now = (/* @__PURE__ */ new Date()).toISOString();
10158
- if (input.read !== false) {
10159
- msg.readBy[input.readerId] = now;
10160
- }
10161
- if (input.completed) {
10162
- msg.completed = true;
10163
- msg.completedBy = input.readerId;
10164
- msg.completedAt = now;
10181
+ let changed = false;
10182
+ for (const msg of all) {
10183
+ const a = byId.get(msg.id);
10184
+ if (!a) continue;
10185
+ updated.push(msg);
10186
+ if (a.read !== false && !(a.readerId in msg.readBy)) {
10187
+ msg.readBy[a.readerId] = now;
10188
+ changed = true;
10189
+ }
10190
+ if (a.completed && !msg.completed) {
10191
+ msg.completed = true;
10192
+ msg.completedBy = a.readerId;
10193
+ msg.completedAt = now;
10194
+ changed = true;
10195
+ }
10196
+ if (a.outcome !== void 0 && msg.outcome !== a.outcome) {
10197
+ msg.outcome = a.outcome;
10198
+ changed = true;
10199
+ }
10165
10200
  }
10166
- if (input.outcome !== void 0) {
10167
- msg.outcome = input.outcome;
10201
+ if (changed) {
10202
+ const serialized = all.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR) + LINE_SEPARATOR;
10203
+ await fsp6.writeFile(this.filePath, serialized, "utf8");
10168
10204
  }
10169
- const serialized = all.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR) + LINE_SEPARATOR;
10170
- await fsp6.writeFile(this.filePath, serialized, "utf8");
10171
- result = msg;
10172
10205
  });
10173
- return result;
10206
+ return updated;
10174
10207
  }
10175
10208
  // ── Agent statuses ────────────────────────────────────────────────────
10176
10209
  async getAgentStatuses() {
@@ -10222,6 +10255,43 @@ var DefaultMailbox = class {
10222
10255
  await fsp6.writeFile(this.filePath, "", "utf8");
10223
10256
  });
10224
10257
  }
10258
+ async purgeStale(opts) {
10259
+ const COMPLETED_MAX_AGE_MS = opts?.completedMaxAgeMs ?? 864e5;
10260
+ const INCOMPLETE_MAX_AGE_MS = opts?.incompleteMaxAgeMs ?? 6048e5;
10261
+ let completedPurged = 0;
10262
+ let incompletePurged = 0;
10263
+ await withFileLock(this.filePath, async () => {
10264
+ const all2 = await this._readAll();
10265
+ const now = Date.now();
10266
+ const cutoffCompleted = now - COMPLETED_MAX_AGE_MS;
10267
+ const cutoffIncomplete = now - INCOMPLETE_MAX_AGE_MS;
10268
+ const kept = [];
10269
+ for (const msg of all2) {
10270
+ const msgTime = new Date(msg.timestamp).getTime();
10271
+ const completedTime = msg.completedAt ? new Date(msg.completedAt).getTime() : 0;
10272
+ if (msg.completed && completedTime < cutoffCompleted) {
10273
+ completedPurged++;
10274
+ continue;
10275
+ }
10276
+ if (!msg.completed && msgTime < cutoffIncomplete) {
10277
+ incompletePurged++;
10278
+ continue;
10279
+ }
10280
+ kept.push(msg);
10281
+ }
10282
+ if (kept.length < all2.length) {
10283
+ const content = kept.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR) + LINE_SEPARATOR;
10284
+ await fsp6.writeFile(this.filePath, content, "utf8");
10285
+ }
10286
+ });
10287
+ const all = await this._readAll();
10288
+ return {
10289
+ completedPurged,
10290
+ incompletePurged,
10291
+ totalPurged: completedPurged + incompletePurged,
10292
+ remaining: all.length
10293
+ };
10294
+ }
10225
10295
  // ── Client registry stubs (not applicable per-session) ─────────────────
10226
10296
  async registerClient(_input) {
10227
10297
  }
@@ -10390,21 +10460,6 @@ var BrainMonitor = class {
10390
10460
  }
10391
10461
  }
10392
10462
  };
10393
- function projectSlug(absRoot) {
10394
- const base = slugify(path4.basename(absRoot));
10395
- const hash = createHash("sha256").update(path4.resolve(absRoot)).digest("hex").slice(0, 6);
10396
- return `${base}-${hash}`;
10397
- }
10398
- function slugify(name) {
10399
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
10400
- }
10401
- function wstackGlobalRoot() {
10402
- const fromEnv = process.env["WRONGSTACK_HOME"];
10403
- if (fromEnv && fromEnv.trim().length > 0) return path4.resolve(fromEnv);
10404
- return path4.join(os.homedir(), ".wrongstack");
10405
- }
10406
-
10407
- // src/coordination/global-mailbox.ts
10408
10463
  var MAILBOX_FILE2 = "_mailbox.jsonl";
10409
10464
  var CLIENT_REGISTRY_FILE = "_mailbox.clients.json";
10410
10465
  var AGENT_STALE_MS = 6e4;
@@ -10412,8 +10467,9 @@ var CLIENT_STALE_MS = 6e4;
10412
10467
  var HEARTBEAT_THROTTLE_MS = 5e3;
10413
10468
  var REGISTRY_CACHE_TTL_MS = 2e3;
10414
10469
  var LINE_SEPARATOR2 = "\n";
10470
+ var MESSAGE_CACHE_MAX_ENTRIES = 1e4;
10415
10471
  function resolveProjectDir(projectRoot, globalRoot) {
10416
- return path4.join(globalRoot, "projects", projectSlug(projectRoot));
10472
+ return path5.join(globalRoot, "projects", projectSlug(projectRoot));
10417
10473
  }
10418
10474
  var GlobalMailbox = class {
10419
10475
  /** Path to the JSONL message file. */
@@ -10444,14 +10500,28 @@ var GlobalMailbox = class {
10444
10500
  _lastHeartbeat = /* @__PURE__ */ new Map();
10445
10501
  /** Last time each local client sent a heartbeat (throttle). */
10446
10502
  _lastClientHeartbeat = /* @__PURE__ */ new Map();
10503
+ /**
10504
+ * In-memory mirror of the JSONL message file. The mailbox is shared
10505
+ * ACROSS PROCESSES, so reads cannot trust the cache blindly — we pair it
10506
+ * with an mtime check. The file lock serializes every write, so a
10507
+ * changed mtimeMs is a definitive signal that another process (or this
10508
+ * one) wrote; an unchanged mtimeMs guarantees no write happened and the
10509
+ * cache is current. This collapses the per-iteration `query()` cost from
10510
+ * O(file_size) disk + parse to O(messages) in memory.
10511
+ */
10512
+ _messageCache = null;
10513
+ /** mtimeMs of the file when `_messageCache` was populated. */
10514
+ _messageCacheMtime = -1;
10515
+ /** Size of the file when `_messageCache` was populated (extra guard). */
10516
+ _messageCacheSize = -1;
10447
10517
  /**
10448
10518
  * @param projectDir — `~/.wrongstack/projects/<slug>/`
10449
10519
  * @param events — optional EventBus for real-time TUI/WebUI notifications
10450
10520
  */
10451
10521
  constructor(projectDir, events) {
10452
- this.messagePath = path4.join(projectDir, MAILBOX_FILE2);
10453
- this.registryPath = path4.join(projectDir, "_mailbox.registry.json");
10454
- this.clientRegistryPath = path4.join(projectDir, CLIENT_REGISTRY_FILE);
10522
+ this.messagePath = path5.join(projectDir, MAILBOX_FILE2);
10523
+ this.registryPath = path5.join(projectDir, "_mailbox.registry.json");
10524
+ this.clientRegistryPath = path5.join(projectDir, CLIENT_REGISTRY_FILE);
10455
10525
  this._events = events;
10456
10526
  }
10457
10527
  // ── Messages ────────────────────────────────────────────────────────────
@@ -10474,73 +10544,89 @@ var GlobalMailbox = class {
10474
10544
  taskContext: input.taskContext
10475
10545
  };
10476
10546
  const line = JSON.stringify(msg) + LINE_SEPARATOR2;
10477
- await fsp6.mkdir(path4.dirname(this.messagePath), { recursive: true });
10547
+ await fsp6.mkdir(path5.dirname(this.messagePath), { recursive: true });
10478
10548
  await withFileLock(this.messagePath, async () => {
10479
10549
  await fsp6.appendFile(this.messagePath, line, "utf8");
10550
+ this._pushToCache(msg);
10480
10551
  });
10481
10552
  return msg;
10482
10553
  }
10483
10554
  async query(q) {
10484
- const all = await this._readMessages();
10555
+ const all = await this._readMessagesCached();
10485
10556
  const limit = q.limit ?? 50;
10486
- let filtered = all;
10487
- if (q.to !== void 0) {
10488
- filtered = filtered.filter((m) => m.to === q.to || m.to === "*");
10489
- }
10490
- if (q.from !== void 0) {
10491
- filtered = filtered.filter((m) => m.from === q.from);
10492
- }
10493
- if (q.unreadBy !== void 0) {
10494
- filtered = filtered.filter((m) => !(q.unreadBy in m.readBy));
10495
- }
10496
- if (q.incompleteOnly) {
10497
- filtered = filtered.filter((m) => !m.completed);
10498
- }
10499
- if (q.type !== void 0) {
10500
- filtered = filtered.filter((m) => m.type === q.type);
10501
- }
10502
- if (q.minPriority !== void 0) {
10503
- const order = { low: 0, normal: 1, high: 2 };
10504
- const min = order[q.minPriority];
10505
- filtered = filtered.filter((m) => (order[m.priority] ?? 1) >= min);
10506
- }
10507
- if (q.since !== void 0) {
10508
- const since = q.since;
10509
- filtered = filtered.filter((m) => m.timestamp > since);
10557
+ const order = q.minPriority !== void 0 ? { low: 0, normal: 1, high: 2 } : null;
10558
+ const minPriorityRank = order && q.minPriority !== void 0 ? order[q.minPriority] : 0;
10559
+ const out = [];
10560
+ for (let i = 0; i < all.length; i++) {
10561
+ const m = all[i];
10562
+ if (q.to !== void 0 && m.to !== q.to && m.to !== "*") continue;
10563
+ if (q.from !== void 0 && m.from !== q.from) continue;
10564
+ if (q.unreadBy !== void 0 && q.unreadBy in m.readBy) continue;
10565
+ if (q.incompleteOnly && m.completed) continue;
10566
+ if (q.type !== void 0 && m.type !== q.type) continue;
10567
+ if (order !== null && (order[m.priority] ?? 1) < minPriorityRank) {
10568
+ continue;
10569
+ }
10570
+ if (q.since !== void 0 && m.timestamp <= q.since) continue;
10571
+ out.push(m);
10510
10572
  }
10511
- filtered.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10512
- return filtered.slice(0, limit);
10573
+ out.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10574
+ return out.slice(0, limit).map((m) => ({ ...m, readBy: { ...m.readBy } }));
10513
10575
  }
10514
10576
  async ack(input) {
10515
- let result = null;
10577
+ const updated = await this.ackMany({ acks: [input] });
10578
+ return updated.length > 0 ? updated[0] : null;
10579
+ }
10580
+ async ackMany(input) {
10581
+ if (input.acks.length === 0) return [];
10582
+ const updated = [];
10583
+ const byId = /* @__PURE__ */ new Map();
10584
+ for (const a of input.acks) {
10585
+ byId.set(a.messageId, a);
10586
+ }
10587
+ let cacheSnapshot = null;
10516
10588
  await withFileLock(this.messagePath, async () => {
10517
- const all = await this._readMessages();
10518
- const idx = all.findIndex((m) => m.id === input.messageId);
10519
- if (idx === -1) return;
10520
- const msg = all[idx];
10589
+ const all = await this._readMessagesFresh();
10521
10590
  const now = (/* @__PURE__ */ new Date()).toISOString();
10522
- if (input.read !== false) {
10523
- msg.readBy[input.readerId] = now;
10524
- }
10525
- if (input.completed) {
10526
- msg.completed = true;
10527
- msg.completedBy = input.readerId;
10528
- msg.completedAt = now;
10591
+ let changed = false;
10592
+ for (const msg of all) {
10593
+ const a = byId.get(msg.id);
10594
+ if (!a) continue;
10595
+ updated.push(msg);
10596
+ if (a.read !== false && !(a.readerId in msg.readBy)) {
10597
+ msg.readBy[a.readerId] = now;
10598
+ changed = true;
10599
+ }
10600
+ if (a.completed && !msg.completed) {
10601
+ msg.completed = true;
10602
+ msg.completedBy = a.readerId;
10603
+ msg.completedAt = now;
10604
+ changed = true;
10605
+ }
10606
+ if (a.outcome !== void 0 && msg.outcome !== a.outcome) {
10607
+ msg.outcome = a.outcome;
10608
+ changed = true;
10609
+ }
10529
10610
  }
10530
- if (input.outcome !== void 0) {
10531
- msg.outcome = input.outcome;
10611
+ if (changed) {
10612
+ const serialized = all.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR2) + LINE_SEPARATOR2;
10613
+ await fsp6.writeFile(this.messagePath, serialized, "utf8");
10532
10614
  }
10533
- const serialized = all.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR2) + LINE_SEPARATOR2;
10534
- await fsp6.writeFile(this.messagePath, serialized, "utf8");
10535
- result = msg;
10615
+ cacheSnapshot = all;
10536
10616
  });
10537
- return result;
10617
+ if (cacheSnapshot) this._setMessageCache(cacheSnapshot);
10618
+ return updated;
10538
10619
  }
10539
10620
  async unreadCount(forAgentId) {
10540
- const all = await this._readMessages();
10541
- return all.filter(
10542
- (m) => (m.to === forAgentId || m.to === "*") && !(forAgentId in m.readBy) && !m.completed
10543
- ).length;
10621
+ const all = await this._readMessagesCached();
10622
+ let count = 0;
10623
+ for (let i = 0; i < all.length; i++) {
10624
+ const m = all[i];
10625
+ if ((m.to === forAgentId || m.to === "*") && !(forAgentId in m.readBy) && !m.completed) {
10626
+ count++;
10627
+ }
10628
+ }
10629
+ return count;
10544
10630
  }
10545
10631
  // ── Agent registry ──────────────────────────────────────────────────────
10546
10632
  async registerAgent(input) {
@@ -10701,13 +10787,62 @@ var GlobalMailbox = class {
10701
10787
  async close() {
10702
10788
  this._registryCache = null;
10703
10789
  this._clientRegistryCache = null;
10790
+ this._messageCache = null;
10791
+ this._messageCacheMtime = -1;
10792
+ this._messageCacheSize = -1;
10704
10793
  }
10705
10794
  async clearAll() {
10706
10795
  await withFileLock(this.messagePath, async () => {
10707
10796
  await fsp6.writeFile(this.messagePath, "", "utf8");
10708
10797
  });
10798
+ this._setMessageCache([]);
10799
+ }
10800
+ async purgeStale(opts) {
10801
+ const COMPLETED_MAX_AGE_MS = opts?.completedMaxAgeMs ?? 864e5;
10802
+ const INCOMPLETE_MAX_AGE_MS = opts?.incompleteMaxAgeMs ?? 6048e5;
10803
+ let completedPurged = 0;
10804
+ let incompletePurged = 0;
10805
+ let remaining = 0;
10806
+ await withFileLock(this.messagePath, async () => {
10807
+ const all = await this._readMessagesFresh();
10808
+ const now = Date.now();
10809
+ const cutoffCompleted = now - COMPLETED_MAX_AGE_MS;
10810
+ const cutoffIncomplete = now - INCOMPLETE_MAX_AGE_MS;
10811
+ const kept = [];
10812
+ for (const msg of all) {
10813
+ const msgTime = new Date(msg.timestamp).getTime();
10814
+ const completedTime = msg.completedAt ? new Date(msg.completedAt).getTime() : 0;
10815
+ if (msg.completed && completedTime < cutoffCompleted) {
10816
+ completedPurged++;
10817
+ continue;
10818
+ }
10819
+ if (!msg.completed && msgTime < cutoffIncomplete) {
10820
+ incompletePurged++;
10821
+ continue;
10822
+ }
10823
+ kept.push(msg);
10824
+ }
10825
+ remaining = kept.length;
10826
+ if (kept.length < all.length) {
10827
+ const content = kept.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR2) + LINE_SEPARATOR2;
10828
+ await fsp6.writeFile(this.messagePath, content, "utf8");
10829
+ }
10830
+ this._setMessageCache(kept);
10831
+ });
10832
+ return {
10833
+ completedPurged,
10834
+ incompletePurged,
10835
+ totalPurged: completedPurged + incompletePurged,
10836
+ remaining
10837
+ };
10709
10838
  }
10710
10839
  // ── Internal ────────────────────────────────────────────────────────────
10840
+ /**
10841
+ * Read all messages from the JSONL file. Always reads + parses the file.
10842
+ * Callers that can tolerate a stale-by-mtime view should use
10843
+ * {@link _readMessagesCached}; writers that need the post-lock truth
10844
+ * should call this directly (it's what {@link _readMessagesFresh} aliases).
10845
+ */
10711
10846
  async _readMessages() {
10712
10847
  try {
10713
10848
  const raw = await fsp6.readFile(this.messagePath, "utf8");
@@ -10735,8 +10870,85 @@ var GlobalMailbox = class {
10735
10870
  throw err;
10736
10871
  }
10737
10872
  }
10873
+ /**
10874
+ * Read messages, then adopt the result as the in-memory cache. Use this
10875
+ * from writers that just took the file lock — the read reflects the
10876
+ * authoritative post-lock state and should be served to subsequent
10877
+ * queries without re-reading.
10878
+ */
10879
+ async _readMessagesFresh() {
10880
+ const all = await this._readMessages();
10881
+ this._setMessageCache(all);
10882
+ return all;
10883
+ }
10884
+ /**
10885
+ * Read messages, consulting the mtime-bounded in-memory cache first.
10886
+ * The mailbox file is shared across processes; every `send`/`ack`/
10887
+ * `clearAll`/`purgeStale` takes the file lock, so writes are serialized
10888
+ * and a changed mtimeMs is a definitive freshness signal. When the
10889
+ * stat matches the cached mtime+size we return the cached array — no
10890
+ * file read and no JSON.parse — collapsing the per-iteration query
10891
+ * cost on the mailbox-loop hot path.
10892
+ */
10893
+ async _readMessagesCached() {
10894
+ try {
10895
+ const st = await fsp6.stat(this.messagePath);
10896
+ if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
10897
+ return this._messageCache;
10898
+ }
10899
+ const all = await this._readMessages();
10900
+ this._setMessageCache(all, st.mtimeMs, st.size);
10901
+ return all;
10902
+ } catch (err) {
10903
+ if (err.code === "ENOENT") {
10904
+ this._setMessageCache([], -1, -1);
10905
+ return [];
10906
+ }
10907
+ throw err;
10908
+ }
10909
+ }
10910
+ /**
10911
+ * Replace the in-memory cache. Caller is responsible for guaranteeing
10912
+ * that `messages` reflects the current on-disk state (e.g. they just
10913
+ * read or wrote it under the file lock).
10914
+ */
10915
+ _setMessageCache(messages, mtime, size) {
10916
+ if (messages.length > MESSAGE_CACHE_MAX_ENTRIES) {
10917
+ this._messageCache = null;
10918
+ this._messageCacheMtime = -1;
10919
+ this._messageCacheSize = -1;
10920
+ return;
10921
+ }
10922
+ this._messageCache = messages;
10923
+ if (mtime !== void 0 && size !== void 0) {
10924
+ this._messageCacheMtime = mtime;
10925
+ this._messageCacheSize = size;
10926
+ } else {
10927
+ void fsp6.stat(this.messagePath).then((st) => {
10928
+ this._messageCacheMtime = st.mtimeMs;
10929
+ this._messageCacheSize = st.size;
10930
+ }).catch(() => {
10931
+ });
10932
+ }
10933
+ }
10934
+ /**
10935
+ * Append a single just-sent message to the in-memory cache without
10936
+ * re-reading the file. The caller must hold the file lock (or have
10937
+ * just released it after a successful append) so the cache stays
10938
+ * consistent with on-disk state.
10939
+ */
10940
+ _pushToCache(msg) {
10941
+ if (this._messageCache === null) return;
10942
+ if (this._messageCache.length >= MESSAGE_CACHE_MAX_ENTRIES) {
10943
+ this._messageCache = null;
10944
+ this._messageCacheMtime = -1;
10945
+ this._messageCacheSize = -1;
10946
+ return;
10947
+ }
10948
+ this._messageCache.push(msg);
10949
+ }
10738
10950
  async _ensureRegistry() {
10739
- await fsp6.mkdir(path4.dirname(this.registryPath), { recursive: true });
10951
+ await fsp6.mkdir(path5.dirname(this.registryPath), { recursive: true });
10740
10952
  }
10741
10953
  async _readRegistry(opts) {
10742
10954
  if (!opts?.fresh && this._registryCache && Date.now() - this._registryCacheAt < REGISTRY_CACHE_TTL_MS) {
@@ -10781,7 +10993,7 @@ var GlobalMailbox = class {
10781
10993
  }
10782
10994
  // ── Client registry internals ───────────────────────────────────────────
10783
10995
  async _ensureClientRegistry() {
10784
- await fsp6.mkdir(path4.dirname(this.clientRegistryPath), { recursive: true });
10996
+ await fsp6.mkdir(path5.dirname(this.clientRegistryPath), { recursive: true });
10785
10997
  }
10786
10998
  async _readClientRegistry(opts) {
10787
10999
  if (!opts?.fresh && this._clientRegistryCache && Date.now() - this._clientRegistryCacheAt < REGISTRY_CACHE_TTL_MS) {
@@ -11421,7 +11633,7 @@ function createMailboxHooks(opts) {
11421
11633
  var DEFAULT_MAX_ENTRIES = 1e4;
11422
11634
  var LOG_FILENAME = "package-authors.json";
11423
11635
  function logPath(storageDir) {
11424
- return path4.join(storageDir, LOG_FILENAME);
11636
+ return path5.join(storageDir, LOG_FILENAME);
11425
11637
  }
11426
11638
  async function loadLog(storageDir, projectRoot) {
11427
11639
  try {
@@ -11445,7 +11657,7 @@ async function saveLog(storageDir, log) {
11445
11657
  await fsp6.rename(tmp, logPath(storageDir));
11446
11658
  }
11447
11659
  function detectEcosystem(manifestPath) {
11448
- const name = path4.basename(manifestPath).toLowerCase();
11660
+ const name = path5.basename(manifestPath).toLowerCase();
11449
11661
  if (name === "package.json") return "npm";
11450
11662
  if (name === "go.mod") return "go";
11451
11663
  if (name === "cargo.toml") return "cargo";
@@ -11642,7 +11854,7 @@ function startPackageOutdatedWatcher(opts) {
11642
11854
  );
11643
11855
  } catch (err) {
11644
11856
  handleError(err);
11645
- log(`[pkg-outdated-watcher] Failed to notify for ${entry.name}: ${err instanceof Error ? err.message : String(err)}`);
11857
+ log(`[pkg-outdated-watcher] Failed to notify for ${entry.name}: ${toErrorMessage(err)}`);
11646
11858
  }
11647
11859
  }
11648
11860
  }
@@ -11715,7 +11927,2055 @@ mvn versions:use-latest-versions`;
11715
11927
  return `# Update ${entry.name} to ${entry.latestVersion} using your package manager`;
11716
11928
  }
11717
11929
  }
11930
+ var KnowledgeGraph = class {
11931
+ nodes = /* @__PURE__ */ new Map();
11932
+ index = /* @__PURE__ */ new Map();
11933
+ // tag/field → node ids
11934
+ subs = /* @__PURE__ */ new Map();
11935
+ pendingDeliveries = /* @__PURE__ */ new Map();
11936
+ filePath;
11937
+ graphFilePath;
11938
+ constructor(sessionDir) {
11939
+ this.filePath = path5.join(sessionDir, "_knowledge_graph");
11940
+ this.graphFilePath = path5.join(this.filePath, "graph.jsonl");
11941
+ }
11942
+ // ── Write ──────────────────────────────────────────────────────────────
11943
+ /**
11944
+ * Add a node. Fires to all matching subscriptions synchronously.
11945
+ * Returns the node with its assigned id.
11946
+ */
11947
+ async add(node) {
11948
+ const full = { id: randomUUID(), ...node };
11949
+ this.nodes.set(full.id, full);
11950
+ this._index(full);
11951
+ await this._persist(full);
11952
+ this._deliver(full);
11953
+ return full;
11954
+ }
11955
+ /** Update an existing node by id. Returns updated node or null if not found. */
11956
+ async update(id, patch) {
11957
+ const existing = this.nodes.get(id);
11958
+ if (!existing) return null;
11959
+ const updated = { ...existing, ...patch };
11960
+ this.nodes.set(id, updated);
11961
+ this._deliver(updated);
11962
+ await this._append(updated);
11963
+ return updated;
11964
+ }
11965
+ // ── Read ───────────────────────────────────────────────────────────────
11966
+ get(id) {
11967
+ return this.nodes.get(id);
11968
+ }
11969
+ getAll(filter) {
11970
+ return Array.from(this.nodes.values()).filter((n) => this._matches(n, filter ?? {}));
11971
+ }
11972
+ getGoals(filter) {
11973
+ return this.getAll({ type: "goal", ...filter });
11974
+ }
11975
+ getFacts(filter) {
11976
+ return this.getAll({ type: "fact", ...filter });
11977
+ }
11978
+ getChanges(filter) {
11979
+ return this.getAll({ type: "change", ...filter });
11980
+ }
11981
+ getOpenGoals() {
11982
+ return this.getGoals({ status: "pending" }).concat(
11983
+ this.getGoals({ status: "in_progress" })
11984
+ );
11985
+ }
11986
+ getTopLevelGoals() {
11987
+ return this.getGoals({}).filter((g) => !g.parentGoal);
11988
+ }
11989
+ getBlockedGoals() {
11990
+ return this.getGoals({ status: "blocked" });
11991
+ }
11992
+ getPendingChanges() {
11993
+ return this.getChanges({ status: "proposed" });
11994
+ }
11995
+ getDecisions(since) {
11996
+ return this.getAll({ type: "decision", since });
11997
+ }
11998
+ // ── Search ─────────────────────────────────────────────────────────────
11999
+ searchFacts(query) {
12000
+ const q = query.toLowerCase();
12001
+ return this.getFacts().filter(
12002
+ (f) => f.subject.toLowerCase().includes(q) || f.detail.toLowerCase().includes(q) || f.file?.toLowerCase().includes(q) || f.tags.some((t) => t.toLowerCase().includes(q))
12003
+ );
12004
+ }
12005
+ getRelatedFacts(factId) {
12006
+ const fact = this.nodes.get(factId);
12007
+ if (!fact) return [];
12008
+ return fact.related.map((id) => this.nodes.get(id)).filter((n) => n?.type === "fact");
12009
+ }
12010
+ // ── Subscriptions ──────────────────────────────────────────────────────
12011
+ /**
12012
+ * Subscribe to nodes matching a filter. Returns a channel id that can be
12013
+ * used to poll for new nodes since the last check.
12014
+ */
12015
+ subscribe(agentId, filter) {
12016
+ const channel = randomUUID();
12017
+ const sub = { id: randomUUID(), agentId, filter, channel };
12018
+ this.subs.set(channel, sub);
12019
+ this.pendingDeliveries.set(channel, []);
12020
+ return channel;
12021
+ }
12022
+ /**
12023
+ * Poll for new nodes delivered to a channel since last check.
12024
+ * Clears the delivery buffer after reading.
12025
+ */
12026
+ poll(channel) {
12027
+ const pending = this.pendingDeliveries.get(channel);
12028
+ if (!pending) return [];
12029
+ const delivered = [...pending];
12030
+ pending.length = 0;
12031
+ return delivered;
12032
+ }
12033
+ unsubscribe(channel) {
12034
+ this.subs.delete(channel);
12035
+ this.pendingDeliveries.delete(channel);
12036
+ }
12037
+ // ── Quality gate helpers ───────────────────────────────────────────────
12038
+ /**
12039
+ * Create a quality gate result. Call this when a change is being proposed
12040
+ * so the change node carries the gate result.
12041
+ */
12042
+ static makeQualityGate(checks) {
12043
+ return { passed: checks.every((c) => c.passed), checks };
12044
+ }
12045
+ // ── Private ────────────────────────────────────────────────────────────
12046
+ _index(node) {
12047
+ const add = (key) => {
12048
+ let set = this.index.get(key);
12049
+ if (!set) {
12050
+ set = /* @__PURE__ */ new Set();
12051
+ this.index.set(key, set);
12052
+ }
12053
+ set.add(node.id);
12054
+ };
12055
+ add(`type:${node.type}`);
12056
+ if (node.type === "fact") {
12057
+ const f = node;
12058
+ add(`cat:${f.category}`);
12059
+ if (f.severity) add(`sev:${f.severity}`);
12060
+ add(`by:${f.discoveredBy}`);
12061
+ for (const tag of f.tags) add(`tag:${tag}`);
12062
+ add(`key:${f.key}`);
12063
+ }
12064
+ if (node.type === "goal") {
12065
+ const g = node;
12066
+ add(`status:${g.status}`);
12067
+ add(`prio:${g.priority}`);
12068
+ if (g.assignee) add(`assign:${g.assignee}`);
12069
+ for (const tag of g.tags) add(`tag:${tag}`);
12070
+ }
12071
+ if (node.type === "change") {
12072
+ const c = node;
12073
+ add(`change:${c.status}`);
12074
+ add(`by:${c.proposedBy}`);
12075
+ for (const g of c.satisfiesGoals) add(`goal:${g}`);
12076
+ }
12077
+ }
12078
+ _matches(node, f) {
12079
+ if (f.type && node.type !== f.type) return false;
12080
+ if (f.category && node.category !== f.category) return false;
12081
+ if (f.status) {
12082
+ if (node.type === "goal" && node.status !== f.status) return false;
12083
+ if (node.type === "change" && node.status !== f.status) return false;
12084
+ }
12085
+ if (f.assignee && node.assignee !== f.assignee) return false;
12086
+ if (f.discoveredBy && node.discoveredBy !== f.discoveredBy) return false;
12087
+ if (f.proposedBy && node.proposedBy !== f.proposedBy) return false;
12088
+ if (f.tags?.length) {
12089
+ const nodeTags = node.tags ?? node.tags ?? [];
12090
+ if (!f.tags.some((t) => nodeTags.includes(t))) return false;
12091
+ }
12092
+ if (f.since && node.id > f.since) ;
12093
+ return true;
12094
+ }
12095
+ _deliver(node) {
12096
+ for (const sub of this.subs.values()) {
12097
+ if (this._matches(node, sub.filter)) {
12098
+ const pending = this.pendingDeliveries.get(sub.channel);
12099
+ if (pending) pending.push(node);
12100
+ }
12101
+ }
12102
+ }
12103
+ async _persist(node) {
12104
+ await fsp6.mkdir(this.filePath, { recursive: true });
12105
+ const line = JSON.stringify(node) + "\n";
12106
+ await withFileLock(this.graphFilePath, async () => {
12107
+ await fsp6.appendFile(this.graphFilePath, line, "utf8");
12108
+ });
12109
+ }
12110
+ async _append(node) {
12111
+ await fsp6.mkdir(this.filePath, { recursive: true });
12112
+ const line = JSON.stringify({ op: "update", node }) + "\n";
12113
+ await withFileLock(this.graphFilePath, async () => {
12114
+ await fsp6.appendFile(this.graphFilePath, line, "utf8");
12115
+ });
12116
+ }
12117
+ /** Rebuild in-memory state from the log file. Call on startup. */
12118
+ async load() {
12119
+ try {
12120
+ const content = await fsp6.readFile(this.graphFilePath, "utf8");
12121
+ const lines = content.split("\n").filter(Boolean);
12122
+ for (const line of lines) {
12123
+ try {
12124
+ const parsed = JSON.parse(line);
12125
+ if (parsed.op === "update") {
12126
+ this.nodes.set(parsed.node.id, parsed.node);
12127
+ this._index(parsed.node);
12128
+ } else {
12129
+ this.nodes.set(parsed.id, parsed);
12130
+ this._index(parsed);
12131
+ }
12132
+ } catch {
12133
+ }
12134
+ }
12135
+ } catch {
12136
+ }
12137
+ }
12138
+ /** Snapshot for serialization. */
12139
+ snapshot() {
12140
+ return {
12141
+ nodes: Array.from(this.nodes.values()),
12142
+ subs: this.subs.size
12143
+ };
12144
+ }
12145
+ };
12146
+
12147
+ // src/coordination/task-dag.ts
12148
+ var TaskDAG = class {
12149
+ nodes = /* @__PURE__ */ new Map();
12150
+ handlers = /* @__PURE__ */ new Set();
12151
+ runnablesHandlers = /* @__PURE__ */ new Set();
12152
+ runnableCache = null;
12153
+ // ── Node management ───────────────────────────────────────────────────
12154
+ /**
12155
+ * Add a task node. Dependencies are validated for cycles.
12156
+ * Throws if adding a dep would create a cycle.
12157
+ */
12158
+ addNode(id, description, deps = [], opts = {}) {
12159
+ if (this.nodes.has(id)) return;
12160
+ for (const depId of deps) {
12161
+ if (!this.nodes.has(depId)) {
12162
+ throw new Error(`TaskDAG.addNode: unknown dependency "${depId}" for task "${id}". Add the dep first.`);
12163
+ }
12164
+ }
12165
+ if (this._wouldCycle(id, deps)) {
12166
+ throw new Error(`TaskDAG.addNode: adding deps [${deps.join(", ")}] to "${id}" would create a cycle.`);
12167
+ }
12168
+ const node = {
12169
+ id,
12170
+ description,
12171
+ deps: [...deps],
12172
+ status: deps.length === 0 ? "ready" : "pending",
12173
+ role: opts.role,
12174
+ priority: opts.priority ?? 5,
12175
+ dependents: [],
12176
+ tags: opts.tags ?? []
12177
+ };
12178
+ this.nodes.set(id, node);
12179
+ for (const depId of deps) {
12180
+ this.nodes.get(depId).dependents.push(id);
12181
+ }
12182
+ this.invalidateCache();
12183
+ this._emitReady();
12184
+ }
12185
+ /**
12186
+ * Remove a node and all edges to/from it.
12187
+ * Skips any dependents that would become dangling.
12188
+ */
12189
+ removeNode(id) {
12190
+ const node = this.nodes.get(id);
12191
+ if (!node) return;
12192
+ for (const depId of node.deps) {
12193
+ const dep = this.nodes.get(depId);
12194
+ if (dep) {
12195
+ dep.dependents = dep.dependents.filter((d) => d !== id);
12196
+ }
12197
+ }
12198
+ for (const depId of node.dependents) {
12199
+ const dep = this.nodes.get(depId);
12200
+ if (dep && dep.deps.every((d) => !this.nodes.has(d) || this.nodes.get(d).status === "done")) {
12201
+ this._transition(depId, "pending", "ready");
12202
+ }
12203
+ }
12204
+ this.nodes.delete(id);
12205
+ this.invalidateCache();
12206
+ }
12207
+ // ── State transitions ──────────────────────────────────────────────────
12208
+ /**
12209
+ * Mark a task as running. Returns true if the transition was valid
12210
+ * (task was in 'ready' state), false otherwise.
12211
+ */
12212
+ start(id, assignedTo) {
12213
+ const node = this.nodes.get(id);
12214
+ if (!node) return false;
12215
+ if (node.status !== "ready") return false;
12216
+ node.status = "running";
12217
+ node.assignedTo = assignedTo;
12218
+ node.spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
12219
+ this.invalidateCache();
12220
+ this._emit({ type: "node:started", nodeId: id, assignedTo });
12221
+ return true;
12222
+ }
12223
+ /**
12224
+ * Mark a task as completed. Unblocks all dependents; they become 'ready'
12225
+ * if all their deps are done.
12226
+ */
12227
+ complete(id, result) {
12228
+ const node = this.nodes.get(id);
12229
+ if (!node) return;
12230
+ node.status = "done";
12231
+ node.result = result;
12232
+ node.completedAt = (/* @__PURE__ */ new Date()).toISOString();
12233
+ this.invalidateCache();
12234
+ const blocked = [];
12235
+ for (const depId of node.dependents) {
12236
+ const dep = this.nodes.get(depId);
12237
+ if (!dep) continue;
12238
+ const allDone = dep.deps.filter((d) => this.nodes.has(d)).every((d) => this.nodes.get(d).status === "done");
12239
+ if (allDone) {
12240
+ this._transition(depId, "pending", "ready");
12241
+ } else {
12242
+ blocked.push(depId);
12243
+ }
12244
+ }
12245
+ this._emit({ type: "node:completed", nodeId: id, result, blockers: blocked });
12246
+ if (blocked.length === 0) this._emitReady();
12247
+ }
12248
+ /**
12249
+ * Mark a task as failed. Unblocks dependents but they remain 'pending'
12250
+ * (they may still be runnable if other deps succeeded).
12251
+ */
12252
+ fail(id, error) {
12253
+ const node = this.nodes.get(id);
12254
+ if (!node) return;
12255
+ node.status = "failed";
12256
+ node.error = error;
12257
+ node.completedAt = (/* @__PURE__ */ new Date()).toISOString();
12258
+ this.invalidateCache();
12259
+ const blocked = [];
12260
+ for (const depId of node.dependents) {
12261
+ const dep = this.nodes.get(depId);
12262
+ if (!dep) continue;
12263
+ const allDone = dep.deps.filter((d) => this.nodes.has(d)).every((d) => {
12264
+ const s = this.nodes.get(d).status;
12265
+ return s === "done" || s === "skipped";
12266
+ });
12267
+ if (allDone) {
12268
+ this._transition(depId, "pending", "ready");
12269
+ } else {
12270
+ blocked.push(depId);
12271
+ }
12272
+ }
12273
+ this._emit({ type: "node:failed", nodeId: id, error, blockers: blocked });
12274
+ if (blocked.length === 0) this._emitReady();
12275
+ }
12276
+ /**
12277
+ * Skip a task (e.g., it was deemed unnecessary by an earlier step).
12278
+ * Treats it as done for dependency purposes.
12279
+ */
12280
+ skip(id, reason) {
12281
+ const node = this.nodes.get(id);
12282
+ if (!node) return;
12283
+ node.status = "skipped";
12284
+ node.completedAt = (/* @__PURE__ */ new Date()).toISOString();
12285
+ this.invalidateCache();
12286
+ for (const depId of node.dependents) {
12287
+ const dep = this.nodes.get(depId);
12288
+ if (!dep) continue;
12289
+ const allDone = dep.deps.filter((d) => this.nodes.has(d)).every((d) => {
12290
+ const s = this.nodes.get(d).status;
12291
+ return s === "done" || s === "skipped";
12292
+ });
12293
+ if (allDone) this._transition(depId, "pending", "ready");
12294
+ }
12295
+ this._emit({ type: "node:skipped", nodeId: id, reason });
12296
+ }
12297
+ // ── Queries ────────────────────────────────────────────────────────────
12298
+ getNode(id) {
12299
+ return this.nodes.get(id);
12300
+ }
12301
+ getAll() {
12302
+ return Array.from(this.nodes.values());
12303
+ }
12304
+ getReady() {
12305
+ if (this.runnableCache) return this.runnableCache;
12306
+ const runnable = Array.from(this.nodes.values()).filter((n) => n.status === "ready").sort((a, b) => a.priority - b.priority);
12307
+ this.runnableCache = runnable;
12308
+ return runnable;
12309
+ }
12310
+ getRunning() {
12311
+ return Array.from(this.nodes.values()).filter((n) => n.status === "running");
12312
+ }
12313
+ getPending() {
12314
+ return Array.from(this.nodes.values()).filter((n) => n.status === "pending");
12315
+ }
12316
+ getDone() {
12317
+ return Array.from(this.nodes.values()).filter((n) => n.status === "done");
12318
+ }
12319
+ getFailed() {
12320
+ return Array.from(this.nodes.values()).filter((n) => n.status === "failed");
12321
+ }
12322
+ getCompleted() {
12323
+ return Array.from(this.nodes.values()).filter((n) => n.status === "done" || n.status === "skipped");
12324
+ }
12325
+ isDone() {
12326
+ return Array.from(this.nodes.values()).every(
12327
+ (n) => n.status === "done" || n.status === "failed" || n.status === "skipped"
12328
+ );
12329
+ }
12330
+ isFailed() {
12331
+ return Array.from(this.nodes.values()).some((n) => n.status === "failed");
12332
+ }
12333
+ /** All tasks that are currently blocked (pending but not ready). */
12334
+ getBlocked() {
12335
+ return Array.from(this.nodes.values()).filter((n) => n.status === "pending");
12336
+ }
12337
+ /** Topological sort — tasks in dependency order. */
12338
+ getTopologicalOrder() {
12339
+ const visited = /* @__PURE__ */ new Set();
12340
+ const result = [];
12341
+ const visit = (id) => {
12342
+ if (visited.has(id)) return;
12343
+ visited.add(id);
12344
+ const node = this.nodes.get(id);
12345
+ if (!node) return;
12346
+ for (const depId of node.deps) visit(depId);
12347
+ result.push(node);
12348
+ };
12349
+ for (const id of this.nodes.keys()) visit(id);
12350
+ return result;
12351
+ }
12352
+ /** Check for deadlock: no runnable tasks but not done. */
12353
+ hasDeadlock() {
12354
+ if (this.isDone()) return false;
12355
+ return this.getReady().length === 0 && this.getRunning().length === 0;
12356
+ }
12357
+ /** Stats snapshot for reporting. */
12358
+ stats() {
12359
+ const all = Array.from(this.nodes.values());
12360
+ const done = all.filter((n) => n.status === "done" || n.status === "skipped").length;
12361
+ return {
12362
+ total: all.length,
12363
+ pending: all.filter((n) => n.status === "pending").length,
12364
+ ready: all.filter((n) => n.status === "ready").length,
12365
+ running: all.filter((n) => n.status === "running").length,
12366
+ done: all.filter((n) => n.status === "done").length,
12367
+ failed: all.filter((n) => n.status === "failed").length,
12368
+ skipped: all.filter((n) => n.status === "skipped").length,
12369
+ progress: all.length ? done / all.length : 0
12370
+ };
12371
+ }
12372
+ // ── Events ────────────────────────────────────────────────────────────
12373
+ onEvent(handler) {
12374
+ this.handlers.add(handler);
12375
+ return () => void this.handlers.delete(handler);
12376
+ }
12377
+ onRunnable(handler) {
12378
+ this.runnablesHandlers.add(handler);
12379
+ return () => void this.runnablesHandlers.delete(handler);
12380
+ }
12381
+ // ── Private ───────────────────────────────────────────────────────────
12382
+ _transition(id, from, to) {
12383
+ const node = this.nodes.get(id);
12384
+ if (!node || node.status !== from) return;
12385
+ node.status = to;
12386
+ this.invalidateCache();
12387
+ if (to === "ready") {
12388
+ this._emit({ type: "node:ready", nodeId: id, deps: node.deps });
12389
+ }
12390
+ }
12391
+ _emit(event) {
12392
+ for (const h of this.handlers) {
12393
+ try {
12394
+ h(event);
12395
+ } catch {
12396
+ }
12397
+ }
12398
+ }
12399
+ _emitReady() {
12400
+ const runnable = this.getReady();
12401
+ if (this.hasDeadlock()) {
12402
+ this._emit({ type: "deadlock", blocked: this.getBlocked().map((n) => n.id) });
12403
+ } else {
12404
+ this._emit({ type: "graph:done", allDone: this.isDone() });
12405
+ }
12406
+ if (runnable.length > 0) {
12407
+ for (const h of this.runnablesHandlers) {
12408
+ try {
12409
+ h(runnable);
12410
+ } catch {
12411
+ }
12412
+ }
12413
+ }
12414
+ }
12415
+ invalidateCache() {
12416
+ this.runnableCache = null;
12417
+ }
12418
+ /**
12419
+ * DFS cycle detection. Adding edge (id → dep) creates a cycle if
12420
+ * there already exists a path from dep to id.
12421
+ */
12422
+ _wouldCycle(id, newDeps) {
12423
+ const visited = /* @__PURE__ */ new Set();
12424
+ const stack = [...newDeps];
12425
+ while (stack.length > 0) {
12426
+ const current = stack.pop();
12427
+ if (current === id) return true;
12428
+ if (visited.has(current)) continue;
12429
+ visited.add(current);
12430
+ const node = this.nodes.get(current);
12431
+ if (node) stack.push(...node.dependents);
12432
+ }
12433
+ return false;
12434
+ }
12435
+ };
12436
+
12437
+ // src/coordination/consensus-protocol.ts
12438
+ var ConsensusProtocol = class {
12439
+ graph;
12440
+ fleet;
12441
+ rules;
12442
+ voters;
12443
+ // agentId → config
12444
+ constructor(opts) {
12445
+ this.graph = opts.graph;
12446
+ this.fleet = opts.fleet ?? void 0;
12447
+ this.voters = new Map(opts.voters.map((v) => [v.agentId, v]));
12448
+ this.rules = {
12449
+ quorumFraction: opts.rules?.quorumFraction ?? 0.5,
12450
+ approvalFraction: opts.rules?.approvalFraction ?? 0.6,
12451
+ vetoRoles: opts.rules?.vetoRoles ?? [],
12452
+ approvalWeightFraction: opts.rules?.approvalWeightFraction
12453
+ };
12454
+ }
12455
+ // ── Vote lifecycle ────────────────────────────────────────────────────
12456
+ /**
12457
+ * Initiate a vote on a proposed change. Updates the change node's status
12458
+ * to 'proposed' and notifies eligible voters via FleetBus.
12459
+ */
12460
+ initiateVote(changeId) {
12461
+ const change = this.graph.get(changeId);
12462
+ if (!change || change.type !== "change") {
12463
+ throw new Error(`ConsensusProtocol: no change found with id "${changeId}"`);
12464
+ }
12465
+ this.graph.update(changeId, { status: "proposed", votes: [] });
12466
+ const eligible = this._eligibleVoters(change);
12467
+ this._notifyVoters(change, eligible, "vote_initiated");
12468
+ }
12469
+ /**
12470
+ * Cast a vote. Updates the change node in the graph and re-evaluates
12471
+ * consensus. If the vote triggers a resolution, updates the change status.
12472
+ */
12473
+ castVote(changeId, voterId, value, rationale) {
12474
+ const change = this.graph.get(changeId);
12475
+ if (!change || change.type !== "change") {
12476
+ throw new Error(`ConsensusProtocol: no change found for "${changeId}"`);
12477
+ }
12478
+ const voter = this.voters.get(voterId);
12479
+ if (!voter) {
12480
+ throw new Error(`ConsensusProtocol: unknown voter "${voterId}"`);
12481
+ }
12482
+ const eligible = this._eligibleVoters(change);
12483
+ if (!eligible.includes(voterId)) {
12484
+ throw new Error(`ConsensusProtocol: voter "${voterId}" is not eligible for this vote`);
12485
+ }
12486
+ const vote = {
12487
+ agentId: voterId,
12488
+ agentName: voter.agentName,
12489
+ value,
12490
+ rationale,
12491
+ votedAt: (/* @__PURE__ */ new Date()).toISOString()
12492
+ };
12493
+ const existingIdx = change.votes.findIndex((v) => v.agentId === voterId);
12494
+ const newVotes = existingIdx >= 0 ? change.votes.with(existingIdx, vote) : [...change.votes, vote];
12495
+ const result = this._resolve(changeId, newVotes, eligible);
12496
+ this.graph.update(changeId, {
12497
+ votes: newVotes,
12498
+ ...result.outcome !== "pending" ? { status: this._toChangeStatus(result.outcome) } : {}
12499
+ });
12500
+ this._notifyVoters(change, eligible, "vote_cast", { voterId, value, result });
12501
+ return result;
12502
+ }
12503
+ /**
12504
+ * Resolve the current vote without waiting for all eligible voters.
12505
+ * Useful when a timeout fires or an agent decides to finalize early.
12506
+ */
12507
+ resolveNow(changeId) {
12508
+ const change = this.graph.get(changeId);
12509
+ if (!change) throw new Error(`ConsensusProtocol: unknown change "${changeId}"`);
12510
+ const eligible = this._eligibleVoters(change);
12511
+ const result = this._resolve(changeId, change.votes, eligible);
12512
+ if (result.outcome !== "pending") {
12513
+ this.graph.update(changeId, { status: this._toChangeStatus(result.outcome) });
12514
+ this._notifyVoters(change, eligible, "vote_resolved", { result });
12515
+ }
12516
+ return result;
12517
+ }
12518
+ /**
12519
+ * Register or update a voter's configuration.
12520
+ */
12521
+ registerVoter(config) {
12522
+ this.voters.set(config.agentId, config);
12523
+ }
12524
+ /**
12525
+ * Get the current vote status for a change.
12526
+ */
12527
+ getStatus(changeId) {
12528
+ const change = this.graph.get(changeId);
12529
+ if (!change || change.type !== "change") return null;
12530
+ const eligible = this._eligibleVoters(change);
12531
+ return this._resolve(changeId, change.votes, eligible);
12532
+ }
12533
+ // ── Private ───────────────────────────────────────────────────────────
12534
+ _eligibleVoters(_change) {
12535
+ return Array.from(this.voters.keys());
12536
+ }
12537
+ _resolve(changeId, votes, eligible) {
12538
+ const totalEligible = eligible.length;
12539
+ const approve = votes.filter((v) => v.value === "approve");
12540
+ const reject = votes.filter((v) => v.value === "reject");
12541
+ const abstain = votes.filter((v) => v.value === "abstain");
12542
+ const totalWeightApprove = approve.reduce(
12543
+ (sum, v) => sum + (this.voters.get(v.agentId)?.weight ?? 1),
12544
+ 0
12545
+ );
12546
+ const totalWeightReject = reject.reduce(
12547
+ (sum, v) => sum + (this.voters.get(v.agentId)?.weight ?? 1),
12548
+ 0
12549
+ );
12550
+ const totalWeight = Array.from(this.voters.values()).reduce(
12551
+ (sum, v) => sum + v.weight,
12552
+ 0
12553
+ );
12554
+ const castCount = votes.length;
12555
+ const quorumRequired = Math.ceil(totalEligible * this.rules.quorumFraction);
12556
+ const quorumMet = castCount >= quorumRequired;
12557
+ for (const v of reject) {
12558
+ const config = this.voters.get(v.agentId);
12559
+ if (config?.veto && this.rules.vetoRoles.includes(config.role)) {
12560
+ return {
12561
+ changeId,
12562
+ outcome: "vetoed",
12563
+ votes,
12564
+ approveCount: approve.length,
12565
+ rejectCount: reject.length,
12566
+ abstainCount: abstain.length,
12567
+ totalWeightApprove,
12568
+ totalWeightReject,
12569
+ eligibleVoters: eligible,
12570
+ quorumMet,
12571
+ approvalMet: false,
12572
+ vetoedBy: config.agentId,
12573
+ rationale: `Hard veto from role "${config.role}" (${config.agentName}).`
12574
+ };
12575
+ }
12576
+ }
12577
+ if (!quorumMet) {
12578
+ return {
12579
+ changeId,
12580
+ outcome: "pending",
12581
+ votes,
12582
+ approveCount: approve.length,
12583
+ rejectCount: reject.length,
12584
+ abstainCount: abstain.length,
12585
+ totalWeightApprove,
12586
+ totalWeightReject,
12587
+ eligibleVoters: eligible,
12588
+ quorumMet: false,
12589
+ approvalMet: false,
12590
+ rationale: `Quorum not met: ${castCount}/${quorumRequired} required.`
12591
+ };
12592
+ }
12593
+ const approvalRequired = Math.ceil(castCount * this.rules.approvalFraction);
12594
+ const approvalMet = approve.length >= approvalRequired;
12595
+ if (this.rules.approvalWeightFraction !== void 0 && approvalMet) {
12596
+ const weightRequired = totalWeight * this.rules.approvalWeightFraction;
12597
+ if (totalWeightApprove < weightRequired) {
12598
+ return {
12599
+ changeId,
12600
+ outcome: "rejected",
12601
+ votes,
12602
+ approveCount: approve.length,
12603
+ rejectCount: reject.length,
12604
+ abstainCount: abstain.length,
12605
+ totalWeightApprove,
12606
+ totalWeightReject,
12607
+ eligibleVoters: eligible,
12608
+ quorumMet: true,
12609
+ approvalMet: false,
12610
+ rationale: `Weight threshold not met: ${totalWeightApprove.toFixed(2)}/${weightRequired.toFixed(2)} required.`
12611
+ };
12612
+ }
12613
+ }
12614
+ if (approvalMet) {
12615
+ return {
12616
+ changeId,
12617
+ outcome: "approved",
12618
+ votes,
12619
+ approveCount: approve.length,
12620
+ rejectCount: reject.length,
12621
+ abstainCount: abstain.length,
12622
+ totalWeightApprove,
12623
+ totalWeightReject,
12624
+ eligibleVoters: eligible,
12625
+ quorumMet: true,
12626
+ approvalMet: true,
12627
+ rationale: `Approved: ${approve.length}/${castCount} votes (threshold: ${approvalRequired}).`
12628
+ };
12629
+ }
12630
+ return {
12631
+ changeId,
12632
+ outcome: "rejected",
12633
+ votes,
12634
+ approveCount: approve.length,
12635
+ rejectCount: reject.length,
12636
+ abstainCount: abstain.length,
12637
+ totalWeightApprove,
12638
+ totalWeightReject,
12639
+ eligibleVoters: eligible,
12640
+ quorumMet: true,
12641
+ approvalMet: false,
12642
+ rationale: `Rejected: ${approve.length}/${castCount} approve votes (threshold: ${approvalRequired}).`
12643
+ };
12644
+ }
12645
+ _toChangeStatus(outcome) {
12646
+ switch (outcome) {
12647
+ case "approved":
12648
+ return "approved";
12649
+ case "rejected":
12650
+ return "rejected";
12651
+ case "vetoed":
12652
+ return "rejected";
12653
+ case "quorum_not_met":
12654
+ return "proposed";
12655
+ default:
12656
+ return "proposed";
12657
+ }
12658
+ }
12659
+ _notifyVoters(change, eligible, event, extra) {
12660
+ if (!this.fleet) return;
12661
+ this.fleet.emit({
12662
+ subagentId: "consensus",
12663
+ ts: Date.now(),
12664
+ type: `consensus:${event}`,
12665
+ payload: { changeId: change.id, changeTitle: change.title, eligible, ...extra }
12666
+ });
12667
+ }
12668
+ };
12669
+
12670
+ // src/coordination/change-manager.ts
12671
+ var DEFAULT_QUALITY_CHECKS = {
12672
+ runTests: true,
12673
+ runTypecheck: true,
12674
+ runLint: false,
12675
+ // warning only
12676
+ runSecurityScan: true,
12677
+ checkTestCoverage: false,
12678
+ minCoveragePercent: 70
12679
+ };
12680
+ var ChangeManager = class {
12681
+ graph;
12682
+ consensus;
12683
+ fleet;
12684
+ checks;
12685
+ /** Track applied changes for rollback lookup. */
12686
+ appliedChanges = /* @__PURE__ */ new Map();
12687
+ // changeId → rollbackId
12688
+ constructor(opts) {
12689
+ this.graph = opts.graph;
12690
+ this.consensus = opts.consensus;
12691
+ this.fleet = opts.fleet ?? void 0;
12692
+ this.checks = { ...DEFAULT_QUALITY_CHECKS, ...opts.checks };
12693
+ }
12694
+ // ── Lifecycle ────────────────────────────────────────────────────────
12695
+ /**
12696
+ * Propose a new code change. Creates a ChangeNode in the knowledge graph.
12697
+ * Does NOT automatically initiate voting — call `submitForReview()` for that.
12698
+ */
12699
+ async propose(input) {
12700
+ const node = await this.graph.add({
12701
+ type: "change",
12702
+ title: input.title,
12703
+ description: input.description,
12704
+ files: input.files.map((f) => ({
12705
+ path: f.path,
12706
+ action: f.action
12707
+ })),
12708
+ status: "proposed",
12709
+ proposedBy: input.proposedBy,
12710
+ proposedAt: (/* @__PURE__ */ new Date()).toISOString(),
12711
+ approvedBy: [],
12712
+ rejectedBy: [],
12713
+ votes: [],
12714
+ qualityGate: { passed: false, checks: [] },
12715
+ // filled after quality gate
12716
+ satisfiesGoals: input.satisfiesGoals
12717
+ });
12718
+ void this._runQualityGate(node.id, input.files).then((gate) => {
12719
+ void this.graph.update(node.id, { qualityGate: gate });
12720
+ });
12721
+ this._emit("change:proposed", { changeId: node.id, title: node.title });
12722
+ return node;
12723
+ }
12724
+ /**
12725
+ * Submit an approved change for application.
12726
+ * Returns the change node — actual file mutations are performed by agents
12727
+ * acting on this node's data from the knowledge graph.
12728
+ */
12729
+ async submitForReview(changeId) {
12730
+ const change = this.graph.get(changeId);
12731
+ if (!change || change.type !== "change") {
12732
+ throw new Error(`ChangeManager: no change found "${changeId}"`);
12733
+ }
12734
+ if (change.status !== "proposed") {
12735
+ throw new Error(`ChangeManager: change "${changeId}" is not in 'proposed' state`);
12736
+ }
12737
+ this.consensus.initiateVote(changeId);
12738
+ this._emit("change:submitted_for_review", { changeId, title: change.title });
12739
+ }
12740
+ /**
12741
+ * Apply an approved change. Updates the change node to 'applied'.
12742
+ * Agents should watch for 'applied' status and perform the actual file mutations.
12743
+ */
12744
+ async markApplied(changeId, appliedAt) {
12745
+ const change = this.graph.get(changeId);
12746
+ if (!change) return null;
12747
+ const updated = await this.graph.update(changeId, {
12748
+ status: "applied",
12749
+ appliedAt
12750
+ });
12751
+ if (updated) {
12752
+ this.appliedChanges.set(changeId, "");
12753
+ this._emit("change:applied", { changeId, title: updated.title, files: updated.files });
12754
+ }
12755
+ return updated;
12756
+ }
12757
+ /**
12758
+ * Mark a change as applied and trigger rollback for any satisfied goal
12759
+ * that turns out to be broken.
12760
+ */
12761
+ async markAppliedWithVerification(changeId, verify) {
12762
+ const change = this.graph.get(changeId);
12763
+ if (!change) throw new Error(`ChangeManager: unknown change "${changeId}"`);
12764
+ const appliedAt = (/* @__PURE__ */ new Date()).toISOString();
12765
+ await this.markApplied(changeId, appliedAt);
12766
+ const verificationResult = await verify();
12767
+ if (!verificationResult.passed) {
12768
+ const rollbackResult = await this.proposeRollback(changeId, "Quality gate failed after apply");
12769
+ return {
12770
+ changeId,
12771
+ success: false,
12772
+ appliedAt,
12773
+ filesTouched: change.files.map((f) => f.path),
12774
+ verificationResult,
12775
+ rollbackChangeId: rollbackResult?.id,
12776
+ error: `Quality gate failed: ${verificationResult.checks.filter((c) => !c.passed).map((c) => c.name).join(", ")}`
12777
+ };
12778
+ }
12779
+ return {
12780
+ changeId,
12781
+ success: true,
12782
+ appliedAt,
12783
+ filesTouched: change.files.map((f) => f.path),
12784
+ verificationResult
12785
+ };
12786
+ }
12787
+ /**
12788
+ * Propose a rollback for an applied change. Creates a new change that
12789
+ * reverses the original. Goes through full consensus.
12790
+ */
12791
+ async proposeRollback(appliedChangeId, reason) {
12792
+ const original = this.graph.get(appliedChangeId);
12793
+ if (!original || original.type !== "change") return null;
12794
+ const rollbackFiles = original.files.map((f) => ({
12795
+ path: f.path,
12796
+ action: f.action === "create" ? "delete" : f.action === "delete" ? "create" : "modify"
12797
+ }));
12798
+ const rollback = await this.propose({
12799
+ title: `Rollback: ${original.title}`,
12800
+ description: `Rollback of "${original.title}" applied at ${original.appliedAt}. Reason: ${reason}`,
12801
+ files: rollbackFiles,
12802
+ proposedBy: "change-manager",
12803
+ satisfiesGoals: [],
12804
+ tags: ["rollback", `original:${appliedChangeId}`]
12805
+ });
12806
+ this.appliedChanges.set(appliedChangeId, rollback.id);
12807
+ await this.graph.update(appliedChangeId, {
12808
+ rolledBackAt: (/* @__PURE__ */ new Date()).toISOString(),
12809
+ rollbackReason: reason
12810
+ });
12811
+ this._emit("change:rollback_proposed", {
12812
+ originalChangeId: appliedChangeId,
12813
+ rollbackChangeId: rollback.id,
12814
+ reason
12815
+ });
12816
+ return rollback;
12817
+ }
12818
+ /**
12819
+ * Mark a change as rolled back.
12820
+ */
12821
+ async markRolledBack(changeId, rolledBackAt) {
12822
+ const updated = await this.graph.update(changeId, {
12823
+ status: "rolled_back",
12824
+ rolledBackAt
12825
+ });
12826
+ if (updated) {
12827
+ this._emit("change:rolled_back", { changeId, title: updated.title });
12828
+ }
12829
+ return updated;
12830
+ }
12831
+ // ── Queries ───────────────────────────────────────────────────────────
12832
+ getPendingReviews() {
12833
+ return this.graph.getChanges({ status: "proposed" });
12834
+ }
12835
+ getAppliedChanges() {
12836
+ return this.graph.getChanges({ status: "applied" });
12837
+ }
12838
+ getChange(id) {
12839
+ return this.graph.get(id);
12840
+ }
12841
+ getChangesForGoal(goalId) {
12842
+ return this.graph.getChanges({}).filter(
12843
+ (c) => c.satisfiesGoals.includes(goalId)
12844
+ );
12845
+ }
12846
+ // ── Quality gate ──────────────────────────────────────────────────────
12847
+ /**
12848
+ * Run quality gate checks. This is informational — actual test/lint/typecheck
12849
+ * execution is done by agents spawned for this purpose. This method stores
12850
+ * the result in the change node.
12851
+ */
12852
+ async _runQualityGate(_changeId, _files) {
12853
+ const checks = [];
12854
+ if (this.checks.runTests) {
12855
+ checks.push({ name: "tests", passed: false, detail: "Tests must be run by a verify agent" });
12856
+ }
12857
+ if (this.checks.runTypecheck) {
12858
+ checks.push({ name: "typecheck", passed: false, detail: "TypeScript must compile" });
12859
+ }
12860
+ if (this.checks.runSecurityScan) {
12861
+ checks.push({ name: "security", passed: false, detail: "Security scan must pass" });
12862
+ }
12863
+ if (this.checks.runLint) {
12864
+ checks.push({ name: "lint", passed: false, detail: "Lint check" });
12865
+ }
12866
+ const result = {
12867
+ passed: checks.length === 0,
12868
+ checks
12869
+ };
12870
+ await this.graph.update(_changeId, { qualityGate: result });
12871
+ return result;
12872
+ }
12873
+ /**
12874
+ * Update quality gate result for a change. Called by verify agents
12875
+ * after running their checks.
12876
+ */
12877
+ async updateQualityGate(changeId, checkName, result) {
12878
+ const change = this.graph.get(changeId);
12879
+ if (!change) return;
12880
+ const checks = change.qualityGate.checks.map(
12881
+ (c) => c.name === checkName ? { ...c, ...result } : c
12882
+ );
12883
+ const allPassed = checks.every((c) => c.passed);
12884
+ await this.graph.update(changeId, {
12885
+ qualityGate: { passed: allPassed, checks }
12886
+ });
12887
+ this._emit("quality_gate:updated", { changeId, checkName, passed: result.passed });
12888
+ }
12889
+ // ── Helpers ──────────────────────────────────────────────────────────
12890
+ _emit(type, payload) {
12891
+ if (!this.fleet) return;
12892
+ this.fleet.emit({ subagentId: "change-manager", ts: Date.now(), type, payload });
12893
+ }
12894
+ };
12895
+ var AutonomousBrain = class {
12896
+ graph;
12897
+ // Fleet bus for emitting decisions — null-safe, no-op if not provided
12898
+ fleetBus;
12899
+ llmProvider;
12900
+ maxRetries;
12901
+ consensusRiskThreshold;
12902
+ selfImprove;
12903
+ /** Decision history for self-improvement and audit. */
12904
+ decisionHistory = [];
12905
+ /** Tracks failure patterns for self-improvement. */
12906
+ failurePatterns = /* @__PURE__ */ new Map();
12907
+ RISK_ORDER = ["low", "medium", "high", "critical"];
12908
+ // ── Fleet bus integration ─────────────────────────────────────────────
12909
+ _emit(type, payload) {
12910
+ if (!this.fleetBus) return;
12911
+ this.fleetBus.emit({ subagentId: "brain", ts: Date.now(), type, payload });
12912
+ }
12913
+ constructor(opts) {
12914
+ this.graph = opts.graph;
12915
+ this.fleetBus = opts.fleet ?? void 0;
12916
+ this.llmProvider = opts.llmProvider;
12917
+ this.maxRetries = opts.maxRetries ?? 3;
12918
+ this.consensusRiskThreshold = opts.consensusRiskThreshold ?? "high";
12919
+ this.selfImprove = opts.selfImprove ?? true;
12920
+ }
12921
+ // ── BrainArbiter interface ────────────────────────────────────────────
12922
+ /** Implements BrainArbiter — bridges standard brain.ts interface to autonomous engine. */
12923
+ async decide(request) {
12924
+ return this.decideAuto(this._toAutonomous(request));
12925
+ }
12926
+ // ── Main entry point ──────────────────────────────────────────────────
12927
+ /**
12928
+ * Primary autonomous decision engine — receives AutonomousDecisionRequest,
12929
+ * queries the LLM, records the decision, and returns a BrainDecision.
12930
+ *
12931
+ * Specialized methods (decideSpawn, decideApproval, etc.) should call this
12932
+ * directly with a pre-built AutonomousDecisionRequest.
12933
+ */
12934
+ async decideAuto(request) {
12935
+ const { id, decisionType, question, context, options, risk, requiresConsensus } = request;
12936
+ const history = this.selfImprove ? this._loadHistory(decisionType, risk) : [];
12937
+ const hints = this.selfImprove ? this._getSelfImproveHints(decisionType) : [];
12938
+ const prompt = {
12939
+ decisionType,
12940
+ question,
12941
+ context: this._serializeContext(context),
12942
+ options,
12943
+ risk,
12944
+ decisionHistory: history,
12945
+ selfImproveHints: hints
12946
+ };
12947
+ let result;
12948
+ try {
12949
+ result = await this.llmProvider.decide(prompt);
12950
+ } catch (err) {
12951
+ const recommended = options.find((o) => o.recommended);
12952
+ if (recommended && risk === "low") {
12953
+ return { type: "answer", optionId: recommended.id, text: recommended.label };
12954
+ }
12955
+ return { type: "deny", reason: `Brain LLM failed: ${String(err)}` };
12956
+ }
12957
+ void this._recordDecision({
12958
+ id,
12959
+ decisionType,
12960
+ question,
12961
+ options,
12962
+ chosen: result.optionId,
12963
+ rationale: result.rationale,
12964
+ madeBy: "autonomous-brain",
12965
+ context: JSON.stringify(context)
12966
+ });
12967
+ if (requiresConsensus) {
12968
+ this._emit("brain.decision", { id, decisionType, optionId: result.optionId, rationale: result.rationale, consensusRequired: true });
12969
+ return {
12970
+ type: "answer",
12971
+ optionId: result.optionId,
12972
+ text: options.find((o) => o.id === result.optionId)?.label ?? result.optionId,
12973
+ rationale: `${result.rationale}
12974
+
12975
+ \u26A0\uFE0F This decision requires consensus approval before execution.`
12976
+ };
12977
+ }
12978
+ this._emit("brain.decision", { id, decisionType, optionId: result.optionId, rationale: result.rationale, consensusRequired: false });
12979
+ return {
12980
+ type: "answer",
12981
+ optionId: result.optionId,
12982
+ text: options.find((o) => o.id === result.optionId)?.label ?? result.optionId,
12983
+ rationale: result.rationale
12984
+ };
12985
+ }
12986
+ // ── Specialized decision methods ────────────────────────────────────
12987
+ /**
12988
+ * Decide whether to spawn a subagent, which role to use, and what budget.
12989
+ */
12990
+ async decideSpawn(source, taskDescription, availableFacts, fleetStatus) {
12991
+ const roleHints = this._inferRoles(taskDescription);
12992
+ const risk = roleHints.length > 1 ? "medium" : "low";
12993
+ const options = roleHints.map((role, i) => ({
12994
+ id: `spawn:${role}`,
12995
+ label: `Spawn ${role} agent`,
12996
+ risk: i === 0 ? "low" : "medium",
12997
+ recommended: i === 0,
12998
+ consequence: i === 0 ? `Spawn the most appropriate agent for: ${taskDescription.slice(0, 80)}` : `Spawn an alternative agent for the same task`
12999
+ }));
13000
+ return this.decideAuto({
13001
+ id: randomUUID(),
13002
+ source,
13003
+ decisionType: "spawn",
13004
+ question: `Should we spawn a subagent for this task?`,
13005
+ context: {
13006
+ facts: availableFacts,
13007
+ fleetStatus,
13008
+ taskDescription
13009
+ },
13010
+ options,
13011
+ risk,
13012
+ requiresConsensus: false
13013
+ });
13014
+ }
13015
+ /**
13016
+ * Decide whether to approve a proposed change.
13017
+ */
13018
+ async decideApproval(source, change, relevantFacts) {
13019
+ const risk = this._changeRisk(change);
13020
+ const options = [
13021
+ {
13022
+ id: "approve",
13023
+ label: "Approve change",
13024
+ recommended: change.qualityGate.passed && relevantFacts.filter((f) => f.severity === "critical").length === 0,
13025
+ risk,
13026
+ consequence: `Apply changes to: ${change.files.map((f) => f.path).join(", ")}`
13027
+ },
13028
+ {
13029
+ id: "reject",
13030
+ label: "Reject change",
13031
+ recommended: false,
13032
+ risk: "medium",
13033
+ consequence: "Return change to proposer with feedback"
13034
+ },
13035
+ {
13036
+ id: "request_changes",
13037
+ label: "Request specific changes",
13038
+ recommended: false,
13039
+ risk: "low",
13040
+ consequence: "Send back for revision with conditions"
13041
+ }
13042
+ ];
13043
+ return this.decideAuto({
13044
+ id: randomUUID(),
13045
+ source,
13046
+ decisionType: "approve_change",
13047
+ question: `Should we approve the change "${change.title}"?`,
13048
+ context: {
13049
+ facts: relevantFacts,
13050
+ change
13051
+ },
13052
+ options,
13053
+ risk,
13054
+ requiresConsensus: risk === "critical" || risk === "high"
13055
+ });
13056
+ }
13057
+ /**
13058
+ * Decide how to handle a failed task.
13059
+ */
13060
+ async decideEscalation(source, taskId, error, attempts) {
13061
+ const retryCount = this.failurePatterns.get(taskId)?.failures ?? attempts;
13062
+ const options = [];
13063
+ if (retryCount < this.maxRetries) {
13064
+ options.push({
13065
+ id: "retry",
13066
+ label: `Retry task (attempt ${retryCount + 1}/${this.maxRetries})`,
13067
+ recommended: retryCount < 2,
13068
+ risk: "medium",
13069
+ consequence: `Restart the task with same or adjusted budget`
13070
+ });
13071
+ options.push({
13072
+ id: "retry_with_adjustment",
13073
+ label: `Retry with more budget`,
13074
+ recommended: retryCount >= 1,
13075
+ risk: "medium",
13076
+ consequence: `Increase timeout or iterations before retrying`
13077
+ });
13078
+ }
13079
+ options.push({
13080
+ id: "delegate",
13081
+ label: "Delegate to different role",
13082
+ recommended: retryCount >= 1,
13083
+ risk: "medium",
13084
+ consequence: "Try a different agent role for the same task"
13085
+ });
13086
+ if (retryCount >= this.maxRetries) {
13087
+ options.push({
13088
+ id: "mark_failed",
13089
+ label: "Mark task as failed",
13090
+ recommended: true,
13091
+ risk: "high",
13092
+ consequence: "Stop retrying, propagate failure upward"
13093
+ });
13094
+ }
13095
+ options.push({
13096
+ id: "decompose",
13097
+ label: "Decompose and retry in parts",
13098
+ recommended: false,
13099
+ risk: "low",
13100
+ consequence: "Break the task into smaller sub-tasks"
13101
+ });
13102
+ return this.decideAuto({
13103
+ id: randomUUID(),
13104
+ source,
13105
+ decisionType: "escalate_task",
13106
+ question: `Task failed: ${error.slice(0, 100)}. How should we proceed?`,
13107
+ context: { error, attempts: retryCount },
13108
+ options,
13109
+ risk: retryCount >= this.maxRetries ? "critical" : "medium",
13110
+ requiresConsensus: false
13111
+ });
13112
+ }
13113
+ // ── Self-improvement ─────────────────────────────────────────────────
13114
+ /**
13115
+ * Record the outcome of a decision for self-improvement.
13116
+ * Call this after a spawned agent completes or a change is applied.
13117
+ */
13118
+ recordOutcome(decisionId, outcome, _detail) {
13119
+ const node = this.graph.get(decisionId);
13120
+ if (!node) return;
13121
+ const key = `decision:${node.decisionType}`;
13122
+ if (outcome === "failure") {
13123
+ const existing = this.failurePatterns.get(key) ?? { failures: 0, lastFailure: "" };
13124
+ existing.failures += 1;
13125
+ existing.lastFailure = (/* @__PURE__ */ new Date()).toISOString();
13126
+ this.failurePatterns.set(key, existing);
13127
+ } else {
13128
+ this.failurePatterns.delete(key);
13129
+ }
13130
+ void this.graph.update(decisionId, { decisionType: node.decisionType });
13131
+ }
13132
+ _getSelfImproveHints(decisionType) {
13133
+ const pattern = this.failurePatterns.get(`decision:${decisionType}`);
13134
+ if (!pattern || pattern.failures < 2) return [];
13135
+ return [
13136
+ `\u26A0\uFE0F ${decisionType} decisions have failed ${pattern.failures} times recently.`,
13137
+ "Consider alternative approaches before defaulting to this pattern."
13138
+ ];
13139
+ }
13140
+ // ── Private ───────────────────────────────────────────────────────────
13141
+ _toAutonomous(req) {
13142
+ const decisionType = this._inferDecisionType(req);
13143
+ return {
13144
+ id: req.id,
13145
+ source: req.source,
13146
+ decisionType,
13147
+ question: req.question,
13148
+ context: {
13149
+ taskDescription: req.context ?? ""
13150
+ },
13151
+ options: req.options ?? [],
13152
+ risk: req.risk,
13153
+ requiresConsensus: this.RISK_ORDER.indexOf(req.risk) >= this.RISK_ORDER.indexOf(this.consensusRiskThreshold)
13154
+ };
13155
+ }
13156
+ _inferDecisionType(req) {
13157
+ if (req.question.toLowerCase().includes("spawn")) return "spawn";
13158
+ if (req.question.toLowerCase().includes("approve") || req.question.toLowerCase().includes("change")) return "approve_change";
13159
+ if (req.question.toLowerCase().includes("retry") || req.question.toLowerCase().includes("fail")) return "retry_task";
13160
+ if (req.question.toLowerCase().includes("priorit")) return "prioritize_goals";
13161
+ if (req.question.toLowerCase().includes("decompos")) return "decompose_goal";
13162
+ return "assign_task";
13163
+ }
13164
+ _serializeContext(ctx) {
13165
+ const parts = [];
13166
+ if (ctx.facts?.length) {
13167
+ parts.push(`## Relevant Facts
13168
+ ${ctx.facts.map((f) => `- [${f.severity ?? "info"}] ${f.subject}: ${f.detail}`).join("\n")}`);
13169
+ }
13170
+ if (ctx.goals?.length) {
13171
+ parts.push(`## Active Goals
13172
+ ${ctx.goals.map((g) => `- [${g.status}] ${g.priority}: ${g.title}`).join("\n")}`);
13173
+ }
13174
+ if (ctx.change) {
13175
+ const c = ctx.change;
13176
+ parts.push(`## Change Under Review
13177
+ - Title: ${c.title}
13178
+ - Status: ${c.status}
13179
+ - Files: ${c.files.map((f) => `${f.action} ${f.path}`).join(", ")}
13180
+ - Quality gate: ${c.qualityGate.passed ? "PASSED" : "FAILED"}
13181
+ Checks: ${c.qualityGate.checks.map((ch) => `${ch.name}:${ch.passed ? "\u2705" : "\u274C"}`).join(", ")}`);
13182
+ }
13183
+ if (ctx.fleetStatus) {
13184
+ parts.push(`## Fleet Status
13185
+ - Running: ${ctx.fleetStatus.running}, Idle: ${ctx.fleetStatus.idle}, Total: ${ctx.fleetStatus.total}
13186
+ - Cost so far: $${ctx.fleetStatus.costSoFar.toFixed(4)}`);
13187
+ }
13188
+ if (ctx.taskDescription) {
13189
+ parts.push(`## Task
13190
+ ${ctx.taskDescription}`);
13191
+ }
13192
+ if (ctx.error) {
13193
+ parts.push(`## Error
13194
+ ${ctx.error}`);
13195
+ }
13196
+ return parts.join("\n\n");
13197
+ }
13198
+ _loadHistory(type, _risk) {
13199
+ const all = this.graph.getDecisions().filter((d) => d.decisionType === type);
13200
+ return all.slice(-10);
13201
+ }
13202
+ async _recordDecision(input) {
13203
+ const node = await this.graph.add({
13204
+ type: "decision",
13205
+ decisionType: input.decisionType,
13206
+ question: input.question,
13207
+ options: input.options,
13208
+ chosen: input.chosen,
13209
+ rationale: input.rationale,
13210
+ madeBy: input.madeBy,
13211
+ madeAt: (/* @__PURE__ */ new Date()).toISOString(),
13212
+ context: input.context
13213
+ });
13214
+ this.decisionHistory.push(node);
13215
+ return node;
13216
+ }
13217
+ _inferRoles(task) {
13218
+ const t = task.toLowerCase();
13219
+ if (t.includes("bug") || t.includes("error") || t.includes("crash")) return ["bug-hunter", "fixer"];
13220
+ if (t.includes("security") || t.includes("secret") || t.includes("injection")) return ["security-scanner"];
13221
+ if (t.includes("refactor") || t.includes("architecture") || t.includes("debt")) return ["refactor-planner", "critic"];
13222
+ if (t.includes("audit") || t.includes("log") || t.includes("analyze")) return ["audit-log"];
13223
+ if (t.includes("test") || t.includes("coverage")) return ["tester", "bug-hunter"];
13224
+ return ["bug-hunter", "refactor-planner"];
13225
+ }
13226
+ _changeRisk(change) {
13227
+ const criticalFiles = change.files.filter(
13228
+ (f) => f.path.includes("auth") || f.path.includes("config") || f.path.includes("schema")
13229
+ );
13230
+ if (criticalFiles.length > 0) return "high";
13231
+ if (change.files.length > 10) return "medium";
13232
+ return "low";
13233
+ }
13234
+ };
13235
+ var TaskAuctioneer = class {
13236
+ graph;
13237
+ fleet;
13238
+ mailbox;
13239
+ selfAgentId;
13240
+ bidWindowMs;
13241
+ maxTasksPerAgent;
13242
+ minConfidence;
13243
+ // minimum dispatcher confidence to accept a bid
13244
+ /** Pending bids keyed by taskId. */
13245
+ pendingBids = /* @__PURE__ */ new Map();
13246
+ /** Active bid windows keyed by taskId. */
13247
+ bidTimers = /* @__PURE__ */ new Map();
13248
+ /** Agent → current task count (from graph + in-flight). */
13249
+ agentTaskCounts = /* @__PURE__ */ new Map();
13250
+ constructor(opts) {
13251
+ this.graph = opts.graph;
13252
+ this.fleet = opts.fleet;
13253
+ this.mailbox = opts.mailbox;
13254
+ this.selfAgentId = opts.selfAgentId ?? "auctioneer";
13255
+ this.bidWindowMs = opts.bidWindowMs ?? 3e4;
13256
+ this.maxTasksPerAgent = opts.maxTasksPerAgent ?? 3;
13257
+ this.minConfidence = opts.minConfidence ?? 0.3;
13258
+ this.fleet?.filter("task:bid", (e) => this._onBidEvent(e));
13259
+ this.fleet?.filter("task:claimed", (e) => this._onClaimedEvent(e));
13260
+ }
13261
+ // ── Publish a task ────────────────────────────────────────────────────
13262
+ /**
13263
+ * Publish a new task to the auction. Creates a GoalNode and broadcasts
13264
+ * it to all online agents. Returns the goal id.
13265
+ *
13266
+ * If `targetAgent` is specified, the task is assigned directly without auction.
13267
+ */
13268
+ async publishTask(input) {
13269
+ const goal = await this.graph.add({
13270
+ type: "goal",
13271
+ title: input.title,
13272
+ description: input.description,
13273
+ status: input.targetAgent ? "in_progress" : "pending",
13274
+ priority: input.priority ?? "medium",
13275
+ assignee: input.targetAgent,
13276
+ blockedBy: [],
13277
+ dependsOn: input.satisfiesGoals ?? [],
13278
+ createdBy: this.selfAgentId,
13279
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
13280
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13281
+ tags: input.tags ?? [],
13282
+ children: [],
13283
+ parentGoal: input.parentGoal
13284
+ });
13285
+ if (input.parentGoal) {
13286
+ const parent = this.graph.get(input.parentGoal);
13287
+ if (parent) {
13288
+ await this.graph.update(input.parentGoal, {
13289
+ children: [...parent.children, goal.id]
13290
+ });
13291
+ }
13292
+ }
13293
+ if (input.targetAgent) {
13294
+ await this._assignDirect(goal.id, input.targetAgent);
13295
+ } else {
13296
+ await this._broadcastTask(goal);
13297
+ this._startBidWindow(goal.id);
13298
+ }
13299
+ this._emit("task:published", {
13300
+ taskId: goal.id,
13301
+ title: goal.title,
13302
+ priority: goal.priority,
13303
+ tags: goal.tags
13304
+ });
13305
+ return goal.id;
13306
+ }
13307
+ // ── Bid on a task ─────────────────────────────────────────────────────
13308
+ /**
13309
+ * Submit a bid for a task. Called by agents who want to work on it.
13310
+ * Returns true if the bid was accepted, false if the task was already claimed.
13311
+ */
13312
+ async bid(taskId, agent, rationale) {
13313
+ const goal = this.graph.get(taskId);
13314
+ if (!goal || goal.type !== "goal") return false;
13315
+ if (goal.status !== "pending") return false;
13316
+ const currentCount = this._getAgentTaskCount(agent.agentId);
13317
+ if (currentCount >= this.maxTasksPerAgent) return false;
13318
+ const dispatchResult = await dispatchAgent(goal.description);
13319
+ const score = dispatchResult.confidence * (dispatchResult.role === agent.agentRole ? 1.2 : 1);
13320
+ if (score < this.minConfidence) return false;
13321
+ const bid = {
13322
+ id: randomUUID(),
13323
+ taskId,
13324
+ agentId: agent.agentId,
13325
+ agentName: agent.agentName,
13326
+ agentRole: agent.agentRole,
13327
+ score,
13328
+ rationale,
13329
+ submittedAt: (/* @__PURE__ */ new Date()).toISOString()
13330
+ };
13331
+ let bids = this.pendingBids.get(taskId);
13332
+ if (!bids) {
13333
+ bids = [];
13334
+ this.pendingBids.set(taskId, bids);
13335
+ }
13336
+ const existingIdx = bids.findIndex((b) => b.agentId === agent.agentId);
13337
+ if (existingIdx >= 0) {
13338
+ bids[existingIdx] = bid;
13339
+ } else {
13340
+ bids.push(bid);
13341
+ }
13342
+ this._emit("task:bid", {
13343
+ taskId,
13344
+ bid: { ...bid, score: Math.round(score * 100) / 100 },
13345
+ agentName: agent.agentName
13346
+ });
13347
+ await this._mailboxPublish({
13348
+ type: "note",
13349
+ subject: `[bid] ${agent.agentName} \u2192 ${goal.title}`,
13350
+ body: `${agent.agentName} (${agent.agentRole}) bidded on task "${goal.title}" (${goal.id})
13351
+ Rationale: ${rationale}
13352
+ Score: ${score.toFixed(2)}`
13353
+ });
13354
+ return true;
13355
+ }
13356
+ // ── Claim (award) a task ───────────────────────────────────────────────
13357
+ /**
13358
+ * Award a task to a specific agent. Called internally by the bid window
13359
+ * expiry, or can be called directly to force an award.
13360
+ */
13361
+ async claim(taskId, agentId, agentName) {
13362
+ const goal = this.graph.get(taskId);
13363
+ if (!goal || goal.type !== "goal") return false;
13364
+ if (goal.status !== "pending") return false;
13365
+ this._cancelBidWindow(taskId);
13366
+ await this.graph.update(taskId, {
13367
+ status: "in_progress",
13368
+ assignee: agentId,
13369
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13370
+ });
13371
+ this.pendingBids.delete(taskId);
13372
+ this.agentTaskCount(agentId, 1);
13373
+ await this._notifyAgent(agentId, {
13374
+ type: "assign",
13375
+ subject: `[assigned] ${goal.title}`,
13376
+ body: `You have been assigned: "${goal.title}"
13377
+
13378
+ ${goal.description}
13379
+
13380
+ Task ID: ${taskId}
13381
+ Priority: ${goal.priority}`,
13382
+ taskContext: {
13383
+ agentRole: goal.tags[0],
13384
+ taskId,
13385
+ status: "in_progress"
13386
+ }
13387
+ });
13388
+ this._emit("task:claimed", { taskId, agentId, agentName });
13389
+ return true;
13390
+ }
13391
+ // ── Complete a task ────────────────────────────────────────────────────
13392
+ /**
13393
+ * Mark a task as done. Called by the agent when it finishes.
13394
+ */
13395
+ async complete(taskId, _result) {
13396
+ const goal = this.graph.get(taskId);
13397
+ if (!goal) return;
13398
+ const agentId = goal.assignee ?? "unknown";
13399
+ this.agentTaskCount(agentId, -1);
13400
+ await this.graph.update(taskId, {
13401
+ status: "done",
13402
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13403
+ });
13404
+ for (const childId of goal.children) {
13405
+ const child = this.graph.get(childId);
13406
+ if (child) {
13407
+ const allUnblocked = child.blockedBy.every((blockedId) => {
13408
+ const blocked = this.graph.get(blockedId);
13409
+ return blocked?.status === "done";
13410
+ });
13411
+ if (allUnblocked) {
13412
+ await this.graph.update(childId, { status: "pending", updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
13413
+ const unblockedGoal = this.graph.get(childId);
13414
+ await this._broadcastTask(unblockedGoal);
13415
+ }
13416
+ }
13417
+ }
13418
+ this._emit("task:completed", { taskId, agentId, result: _result });
13419
+ await this._mailboxPublish({
13420
+ type: "result",
13421
+ subject: `[done] ${goal.title}`,
13422
+ body: `Task completed by ${agentId}: "${goal.title}"
13423
+
13424
+ ${_result ?? "No result provided."}`
13425
+ });
13426
+ }
13427
+ /**
13428
+ * Mark a task as failed. Optionally spawn a retry.
13429
+ */
13430
+ async fail(taskId, error) {
13431
+ const goal = this.graph.get(taskId);
13432
+ if (!goal) return;
13433
+ const agentId = goal.assignee ?? "unknown";
13434
+ this.agentTaskCount(agentId, -1);
13435
+ await this.graph.update(taskId, {
13436
+ status: "failed",
13437
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13438
+ result: error
13439
+ });
13440
+ this._emit("task:failed", { taskId, agentId, error });
13441
+ await this._mailboxPublish({
13442
+ type: "note",
13443
+ subject: `[failed] ${goal.title}`,
13444
+ body: `Task failed: "${goal.title}"
13445
+ Error: ${error}
13446
+ Assignee: ${agentId}`
13447
+ });
13448
+ }
13449
+ // ── Work finding ──────────────────────────────────────────────────────
13450
+ /**
13451
+ * Find the best available tasks for an agent based on its capabilities.
13452
+ * Returns tasks sorted by match score (best first).
13453
+ */
13454
+ async findWork(_agentId, agentRole, limit = 5) {
13455
+ const pending = this.graph.getGoals({ status: "pending" });
13456
+ const scored = [];
13457
+ for (const goal of pending) {
13458
+ if (goal.blockedBy.length > 0) continue;
13459
+ const dispatchResult = await dispatchAgent(goal.description);
13460
+ const roleBonus = agentRole && goal.tags.includes(agentRole) ? 1.3 : 1;
13461
+ const priorityBonus = goal.priority === "critical" ? 1.5 : goal.priority === "high" ? 1.2 : 1;
13462
+ const score = dispatchResult.confidence * roleBonus * priorityBonus;
13463
+ const bids = this.pendingBids.get(goal.id)?.length ?? 0;
13464
+ scored.push({ task: goal, score, bids });
13465
+ }
13466
+ scored.sort((a, b) => b.score - a.score);
13467
+ return scored.slice(0, limit);
13468
+ }
13469
+ // ── Queries ──────────────────────────────────────────────────────────
13470
+ /** Get all pending tasks (available for bidding). */
13471
+ getPendingTasks() {
13472
+ return this.graph.getGoals({ status: "pending" }).filter((g) => g.blockedBy.length === 0);
13473
+ }
13474
+ /** Get tasks assigned to a specific agent. */
13475
+ getTasksForAgent(agentId) {
13476
+ return this.graph.getGoals({}).filter((g) => g.assignee === agentId);
13477
+ }
13478
+ /** Get the current bid count for a task. */
13479
+ getBidCount(taskId) {
13480
+ return this.pendingBids.get(taskId)?.length ?? 0;
13481
+ }
13482
+ /** Get bids for a task. */
13483
+ getBids(taskId) {
13484
+ return this.pendingBids.get(taskId) ?? [];
13485
+ }
13486
+ /** Get task stats for a project-wide dashboard. */
13487
+ getStats() {
13488
+ const all = this.graph.getGoals({});
13489
+ const pending = all.filter((g) => g.status === "pending" && g.blockedBy.length === 0);
13490
+ const inProgress = all.filter((g) => g.status === "in_progress");
13491
+ const done = all.filter((g) => g.status === "done");
13492
+ const failed = all.filter((g) => g.status === "failed");
13493
+ let totalBids = 0;
13494
+ for (const bids of this.pendingBids.values()) totalBids += bids.length;
13495
+ return {
13496
+ total: all.length,
13497
+ pending: pending.length,
13498
+ in_progress: inProgress.length,
13499
+ done: done.length,
13500
+ failed: failed.length,
13501
+ totalBids,
13502
+ avgBidsPerTask: pending.length > 0 ? totalBids / pending.length : 0
13503
+ };
13504
+ }
13505
+ // ── Private ───────────────────────────────────────────────────────────
13506
+ _emit(type, payload) {
13507
+ if (!this.fleet) return;
13508
+ this.fleet.emit({ subagentId: this.selfAgentId, ts: Date.now(), type, payload });
13509
+ }
13510
+ _broadcastTask(goal) {
13511
+ this._emit("task:available", {
13512
+ taskId: goal.id,
13513
+ title: goal.title,
13514
+ description: goal.description,
13515
+ priority: goal.priority,
13516
+ tags: goal.tags
13517
+ });
13518
+ void this._mailboxPublish({
13519
+ type: "broadcast",
13520
+ subject: `[task] ${goal.title} (${goal.priority})`,
13521
+ body: `New task available: "${goal.title}"
13522
+ Priority: ${goal.priority}
13523
+ Description: ${goal.description.slice(0, 200)}${goal.description.length > 200 ? "..." : ""}
13524
+
13525
+ Task ID: ${goal.id}
13526
+ Tags: ${goal.tags.join(", ") || "none"}
13527
+
13528
+ Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
13529
+ });
13530
+ }
13531
+ async _mailboxPublish(msg) {
13532
+ if (!this.mailbox) return;
13533
+ try {
13534
+ await this.mailbox.send({
13535
+ from: this.selfAgentId,
13536
+ to: "*",
13537
+ type: msg.type,
13538
+ subject: msg.subject,
13539
+ body: msg.body,
13540
+ priority: "normal"
13541
+ });
13542
+ } catch {
13543
+ }
13544
+ }
13545
+ async _notifyAgent(agentId, msg) {
13546
+ if (!this.mailbox) return;
13547
+ try {
13548
+ await this.mailbox.send({
13549
+ from: this.selfAgentId,
13550
+ to: agentId,
13551
+ type: msg.type,
13552
+ subject: msg.subject,
13553
+ body: msg.body,
13554
+ priority: "high",
13555
+ taskContext: msg.taskContext
13556
+ });
13557
+ } catch {
13558
+ }
13559
+ }
13560
+ _startBidWindow(taskId) {
13561
+ this._cancelBidWindow(taskId);
13562
+ const timer = setTimeout(async () => {
13563
+ this.bidTimers.delete(taskId);
13564
+ await this._evaluateBids(taskId);
13565
+ }, this.bidWindowMs);
13566
+ this.bidTimers.set(taskId, timer);
13567
+ }
13568
+ _cancelBidWindow(taskId) {
13569
+ const timer = this.bidTimers.get(taskId);
13570
+ if (timer) {
13571
+ clearTimeout(timer);
13572
+ this.bidTimers.delete(taskId);
13573
+ }
13574
+ }
13575
+ async _evaluateBids(taskId) {
13576
+ const bids = this.pendingBids.get(taskId);
13577
+ if (!bids || bids.length === 0) {
13578
+ const goal = this.graph.get(taskId);
13579
+ if (goal) {
13580
+ await this._broadcastTask(goal);
13581
+ this._startBidWindow(taskId);
13582
+ }
13583
+ return;
13584
+ }
13585
+ bids.sort((a, b) => b.score - a.score);
13586
+ const winner = bids[0];
13587
+ await this.claim(taskId, winner.agentId, winner.agentName);
13588
+ }
13589
+ async _assignDirect(taskId, agentId) {
13590
+ const goal = this.graph.get(taskId);
13591
+ if (!goal) return;
13592
+ await this.graph.update(taskId, {
13593
+ status: "in_progress",
13594
+ assignee: agentId,
13595
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13596
+ });
13597
+ this.agentTaskCount(agentId, 1);
13598
+ await this._notifyAgent(agentId, {
13599
+ type: "assign",
13600
+ subject: `[assigned] ${goal.title}`,
13601
+ body: `You have been directly assigned: "${goal.title}"
13602
+
13603
+ ${goal.description}`,
13604
+ taskContext: { taskId, status: "in_progress" }
13605
+ });
13606
+ }
13607
+ _onBidEvent(_e) {
13608
+ }
13609
+ _onClaimedEvent(e) {
13610
+ const { taskId } = e.payload;
13611
+ this.pendingBids.delete(taskId);
13612
+ this._cancelBidWindow(taskId);
13613
+ }
13614
+ _getAgentTaskCount(agentId) {
13615
+ return this.agentTaskCounts.get(agentId) ?? 0;
13616
+ }
13617
+ agentTaskCount(agentId, delta) {
13618
+ const current = this._getAgentTaskCount(agentId);
13619
+ const next = Math.max(0, current + delta);
13620
+ this.agentTaskCounts.set(agentId, next);
13621
+ }
13622
+ };
13623
+ var AutonomousCoordinator = class {
13624
+ graph;
13625
+ dag;
13626
+ auction;
13627
+ consensus;
13628
+ changes;
13629
+ brain;
13630
+ selfAgentId;
13631
+ fleet;
13632
+ fleetManager;
13633
+ mailbox;
13634
+ events;
13635
+ running = false;
13636
+ iterationCount = 0;
13637
+ constructor(opts) {
13638
+ this.selfAgentId = opts.selfAgentId;
13639
+ this.fleet = opts.fleet ?? void 0;
13640
+ this.fleetManager = opts.fleetManager ?? void 0;
13641
+ this.mailbox = opts.mailbox ?? void 0;
13642
+ this.events = opts.events ?? void 0;
13643
+ this.graph = new KnowledgeGraph(opts.sessionDir);
13644
+ this.dag = new TaskDAG();
13645
+ this.auction = new TaskAuctioneer({
13646
+ graph: this.graph,
13647
+ fleet: this.fleet ?? void 0,
13648
+ mailbox: this.mailbox ?? void 0,
13649
+ selfAgentId: this.selfAgentId
13650
+ });
13651
+ this.consensus = new ConsensusProtocol({
13652
+ graph: this.graph,
13653
+ fleet: this.fleet ?? void 0,
13654
+ voters: this._buildVoters(),
13655
+ rules: {
13656
+ quorumFraction: 0.5,
13657
+ approvalFraction: 0.6,
13658
+ vetoRoles: ["critic"],
13659
+ // Critic has veto power
13660
+ approvalWeightFraction: 0.5
13661
+ }
13662
+ });
13663
+ this.changes = new ChangeManager({
13664
+ graph: this.graph,
13665
+ consensus: this.consensus,
13666
+ fleet: this.fleet ?? void 0,
13667
+ checks: DEFAULT_QUALITY_CHECKS
13668
+ });
13669
+ this.brain = new AutonomousBrain({
13670
+ llmProvider: opts.llmProvider,
13671
+ graph: this.graph,
13672
+ fleet: this.fleet ?? void 0,
13673
+ selfImprove: !opts.disableSelfImprove
13674
+ });
13675
+ this.dag.onEvent((event) => {
13676
+ this._onDagEvent(event);
13677
+ });
13678
+ this.fleet?.filter("subagent.terminated", (e) => {
13679
+ this._onSubagentTerminated(e);
13680
+ });
13681
+ }
13682
+ // ── Public API ───────────────────────────────────────────────────────
13683
+ /**
13684
+ * Run the autonomous loop until the goal is satisfied or max iterations reached.
13685
+ * This is the main entry point for a fully autonomous session.
13686
+ */
13687
+ async run(opts = {}) {
13688
+ if (this.running) throw new Error("AutonomousCoordinator: already running");
13689
+ this.running = true;
13690
+ this.iterationCount = 0;
13691
+ const maxIterations = opts.maxIterations ?? 100;
13692
+ const goal = opts.goal ?? "Improve the codebase";
13693
+ const maxCost = opts.maxCostUsd;
13694
+ await this.graph.load();
13695
+ try {
13696
+ const goalConfigs = await this._decomposeGoal(goal);
13697
+ for (const g of goalConfigs) {
13698
+ await this.auction.publishTask(g);
13699
+ }
13700
+ while (this.running) {
13701
+ this.iterationCount++;
13702
+ if (this.iterationCount >= maxIterations) break;
13703
+ if (maxCost !== void 0) {
13704
+ const cost = this.fleetManager?.snapshot()?.total?.cost ?? 0;
13705
+ if (cost >= maxCost) break;
13706
+ }
13707
+ if (opts.runUntilComplete && this.dag.isDone()) break;
13708
+ const decision = await this.brain.decideAuto({
13709
+ id: randomUUID(),
13710
+ source: "system",
13711
+ decisionType: "prioritize_goals",
13712
+ question: `What should we work on next? Open goals: ${this.auction.getPendingTasks().map((g) => g.title).join(", ") || "none"}`,
13713
+ context: {
13714
+ goals: this.auction.getPendingTasks(),
13715
+ fleetStatus: this._fleetStatus()
13716
+ },
13717
+ options: this._goalToOptions(this.auction.getPendingTasks()),
13718
+ risk: "medium",
13719
+ requiresConsensus: false
13720
+ });
13721
+ if (decision.type === "deny") {
13722
+ const blocked = this.dag.getBlocked();
13723
+ if (blocked.length > 0 && this.dag.hasDeadlock()) {
13724
+ (this.events?.emit)("autonomous:deadlock", { blocked });
13725
+ this.running = false;
13726
+ }
13727
+ break;
13728
+ }
13729
+ if (decision.type === "ask_human") {
13730
+ (this.events?.emit)("autonomous:ask_human", { prompt: decision.prompt });
13731
+ break;
13732
+ }
13733
+ if (decision.optionId) {
13734
+ const goalNode = this._optionToGoal(decision.optionId);
13735
+ if (goalNode) {
13736
+ await this._processGoal(goalNode.id);
13737
+ }
13738
+ }
13739
+ const pendingChanges = this.changes.getPendingReviews();
13740
+ for (const change of pendingChanges) {
13741
+ await this._handlePendingChange(change);
13742
+ }
13743
+ }
13744
+ } finally {
13745
+ this.running = false;
13746
+ }
13747
+ return this.getStats();
13748
+ }
13749
+ /** Stop the autonomous loop. */
13750
+ stop() {
13751
+ this.running = false;
13752
+ }
13753
+ /** Get a stats snapshot. */
13754
+ getStats() {
13755
+ const dagStats = this.dag.stats();
13756
+ const auctionStats = this.auction.getStats();
13757
+ const allGoals = this.graph.getGoals({});
13758
+ const allChanges = this.graph.getChanges({});
13759
+ const allDecisions = this.graph.getDecisions();
13760
+ return {
13761
+ goals: {
13762
+ total: allGoals.length,
13763
+ done: allGoals.filter((g) => g.status === "done").length,
13764
+ pending: allGoals.filter((g) => g.status === "pending").length,
13765
+ failed: allGoals.filter((g) => g.status === "failed").length,
13766
+ progress: allGoals.length > 0 ? allGoals.filter((g) => g.status === "done").length / allGoals.length : 0
13767
+ },
13768
+ dag: dagStats,
13769
+ auction: auctionStats,
13770
+ changes: {
13771
+ proposed: allChanges.filter((c) => c.status === "proposed").length,
13772
+ approved: allChanges.filter((c) => c.status === "approved").length,
13773
+ applied: allChanges.filter((c) => c.status === "applied").length,
13774
+ rejected: allChanges.filter((c) => c.status === "rejected").length
13775
+ },
13776
+ decisions: allDecisions.length,
13777
+ costSoFar: this.fleetManager?.snapshot()?.total?.cost
13778
+ };
13779
+ }
13780
+ // ── Fact publishing ──────────────────────────────────────────────────
13781
+ /**
13782
+ * Publish a fact discovered by an agent. Facts are immutable and form
13783
+ * the basis for other agents' decisions.
13784
+ */
13785
+ async publishFact(input) {
13786
+ const fact = await this.graph.add({
13787
+ type: "fact",
13788
+ category: input.category,
13789
+ subject: input.subject,
13790
+ detail: input.detail,
13791
+ file: input.file,
13792
+ line: input.line,
13793
+ severity: input.severity,
13794
+ discoveredBy: this.selfAgentId,
13795
+ discoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
13796
+ tags: input.tags ?? [],
13797
+ key: `${input.category}:${input.subject}:${input.file ?? ""}:${input.line ?? ""}`,
13798
+ related: []
13799
+ });
13800
+ await this._mailboxBroadcast({
13801
+ type: "note",
13802
+ subject: `[${input.severity ?? "info"}] ${input.category}: ${input.subject}`,
13803
+ body: `**${input.category}**${input.file ? ` in ${input.file}${input.line ? `:${input.line}` : ""}` : ""}
13804
+ ${input.detail}`
13805
+ });
13806
+ return fact;
13807
+ }
13808
+ // ── Goal creation helpers ────────────────────────────────────────────
13809
+ /**
13810
+ * Publish a goal and add it to the DAG.
13811
+ */
13812
+ async createGoal(input) {
13813
+ const resolvedPriority = input.priority ?? "medium";
13814
+ const goalId = await this.auction.publishTask({
13815
+ title: input.title,
13816
+ description: input.description,
13817
+ priority: resolvedPriority,
13818
+ ...input.tags ? { tags: input.tags } : {}
13819
+ });
13820
+ const goal = this.graph.get(goalId);
13821
+ for (const depId of input.deps ?? []) {
13822
+ this.dag.addNode(depId, this.graph.get(depId)?.type === "goal" ? this.graph.get(depId).title : depId);
13823
+ }
13824
+ this.dag.addNode(goalId, input.description, input.deps ?? []);
13825
+ return goal;
13826
+ }
13827
+ // ── Private ───────────────────────────────────────────────────────────
13828
+ async _decomposeGoal(goalText) {
13829
+ const category = this._inferCategory(goalText);
13830
+ const subGoals = [];
13831
+ if (category === "security") {
13832
+ subGoals.push({ title: "Audit for secrets", description: "Scan codebase for hardcoded secrets and API keys", priority: "critical", tags: ["security"] });
13833
+ subGoals.push({ title: "Check injection vectors", description: "Find eval, innerHTML, SQL concat, shell injection patterns", priority: "critical", tags: ["security", "injection"] });
13834
+ subGoals.push({ title: "Dependency audit", description: "Run npm/pnpm audit for known CVEs", priority: "high", tags: ["security", "deps"] });
13835
+ } else if (category === "bug") {
13836
+ subGoals.push({ title: "Find bugs", description: `Scan for bugs related to: ${goalText}`, priority: "high", tags: ["bug"] });
13837
+ subGoals.push({ title: "Fix bugs", description: "Fix discovered bugs with tests", priority: "high", tags: ["fix"] });
13838
+ } else if (category === "refactor") {
13839
+ subGoals.push({ title: "Plan refactor", description: `Analyze code structure for: ${goalText}`, priority: "medium", tags: ["refactor", "planning"] });
13840
+ subGoals.push({ title: "Implement refactor", description: "Apply the refactoring plan", priority: "medium", tags: ["refactor", "implementation"] });
13841
+ } else {
13842
+ subGoals.push({ title: goalText, description: goalText, priority: "medium", tags: [category] });
13843
+ }
13844
+ return subGoals;
13845
+ }
13846
+ _inferCategory(goal) {
13847
+ const g = goal.toLowerCase();
13848
+ if (g.includes("security") || g.includes("secret") || g.includes("injection")) return "security";
13849
+ if (g.includes("bug") || g.includes("fix") || g.includes("error")) return "bug";
13850
+ if (g.includes("refactor") || g.includes("debt") || g.includes("architecture")) return "architecture";
13851
+ if (g.includes("test") || g.includes("coverage")) return "test";
13852
+ if (g.includes("perf") || g.includes("speed") || g.includes("optimize")) return "perf";
13853
+ if (g.includes("deps") || g.includes("package") || g.includes("update")) return "deps";
13854
+ return "quality";
13855
+ }
13856
+ async _processGoal(goalId) {
13857
+ const ready = this.dag.getReady();
13858
+ if (ready.length === 0) return;
13859
+ const next = ready.find((n) => n.id === goalId) ?? ready[0];
13860
+ this.dag.start(next.id, "auctioneer");
13861
+ const existingAgent = this.auction.getTasksForAgent(this.selfAgentId).find((g) => g.id === next.id);
13862
+ if (!existingAgent) {
13863
+ await this.auction.publishTask({
13864
+ title: next.description,
13865
+ description: next.description,
13866
+ priority: this._dagPriorityToGoal(next.priority),
13867
+ tags: next.tags
13868
+ });
13869
+ }
13870
+ await this._waitForClaim(next.id);
13871
+ }
13872
+ async _waitForClaim(taskId) {
13873
+ const maxWait = 6e4;
13874
+ const pollInterval = 2e3;
13875
+ const start = Date.now();
13876
+ while (Date.now() - start < maxWait) {
13877
+ const goal = this.graph.get(taskId);
13878
+ if (goal?.status === "in_progress" || goal?.status === "done") {
13879
+ return;
13880
+ }
13881
+ await this._sleep(pollInterval);
13882
+ }
13883
+ }
13884
+ async _handlePendingChange(change) {
13885
+ const result = this.consensus.getStatus(change.id);
13886
+ if (result?.outcome !== "pending") return;
13887
+ if (change.qualityGate.passed) {
13888
+ const voteResult = await this.consensus.castVote(
13889
+ change.id,
13890
+ this.selfAgentId,
13891
+ "approve",
13892
+ `Quality gate passed: ${change.qualityGate.checks.map((c) => c.name).join(", ")}`
13893
+ );
13894
+ if (voteResult.outcome === "approved") {
13895
+ await this.changes.markApplied(change.id, (/* @__PURE__ */ new Date()).toISOString());
13896
+ }
13897
+ }
13898
+ }
13899
+ _onDagEvent(event) {
13900
+ if (event.type === "node:ready") {
13901
+ const node = this.dag.getNode(event.nodeId);
13902
+ if (node) {
13903
+ (this.events?.emit)("autonomous:task_ready", { taskId: event.nodeId, description: node.description });
13904
+ }
13905
+ }
13906
+ if (event.type === "deadlock") {
13907
+ (this.events?.emit)("autonomous:deadlock", { blocked: event.blocked });
13908
+ this.running = false;
13909
+ }
13910
+ if (event.type === "graph:done") {
13911
+ (this.events?.emit)("autonomous:all_done", this.getStats());
13912
+ }
13913
+ }
13914
+ _onSubagentTerminated(e) {
13915
+ const payload = e.payload;
13916
+ const subagentId = payload?.subagentId ?? e.subagentId;
13917
+ const stopReason = payload?.stopReason ?? "unknown";
13918
+ const tasks = this.auction.getTasksForAgent(subagentId);
13919
+ for (const task of tasks) {
13920
+ if (stopReason === "end_turn") {
13921
+ void this.auction.complete(task.id, "Subagent completed successfully");
13922
+ } else {
13923
+ void this.auction.fail(task.id, `Subagent terminated: ${stopReason}`);
13924
+ }
13925
+ }
13926
+ }
13927
+ _fleetStatus() {
13928
+ return {
13929
+ running: this.fleetManager?.getFleetStats().running ?? 0,
13930
+ idle: this.fleetManager?.getFleetStats().idle ?? 0,
13931
+ total: this.fleetManager?.getFleetStats().total ?? 0,
13932
+ costSoFar: this.fleetManager?.snapshot()?.total?.cost ?? 0
13933
+ };
13934
+ }
13935
+ _buildVoters() {
13936
+ return [
13937
+ { agentId: "critic", agentName: "Critic", role: "critic", weight: 2, veto: true },
13938
+ { agentId: "bug-hunter", agentName: "Bug Hunter", role: "bug-hunter", weight: 1.5 },
13939
+ { agentId: "security-scanner", agentName: "Security Scanner", role: "security-scanner", weight: 1.5 },
13940
+ { agentId: "audit-log", agentName: "Audit Log", role: "audit-log", weight: 1 },
13941
+ { agentId: "refactor-planner", agentName: "Refactor Planner", role: "refactor-planner", weight: 1 }
13942
+ ];
13943
+ }
13944
+ _goalToOptions(goals) {
13945
+ return goals.slice(0, 5).map((g, i) => ({
13946
+ id: g.id,
13947
+ label: `[${g.priority}] ${g.title}`,
13948
+ recommended: i === 0
13949
+ }));
13950
+ }
13951
+ _optionToGoal(optionId) {
13952
+ return this.graph.get(optionId);
13953
+ }
13954
+ _dagPriorityToGoal(p) {
13955
+ if (p <= 1) return "critical";
13956
+ if (p <= 2) return "high";
13957
+ if (p <= 4) return "medium";
13958
+ return "low";
13959
+ }
13960
+ async _mailboxBroadcast(msg) {
13961
+ if (!this.mailbox) return;
13962
+ try {
13963
+ await this.mailbox.send({
13964
+ from: this.selfAgentId,
13965
+ to: "*",
13966
+ type: msg.type,
13967
+ subject: msg.subject,
13968
+ body: msg.body,
13969
+ priority: "normal"
13970
+ });
13971
+ } catch {
13972
+ }
13973
+ }
13974
+ _sleep(ms) {
13975
+ return new Promise((r) => setTimeout(r, ms));
13976
+ }
13977
+ };
11718
13978
 
11719
- 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, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, CollabSession, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
13979
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
11720
13980
  //# sourceMappingURL=index.js.map
11721
13981
  //# sourceMappingURL=index.js.map