@wrongstack/core 0.265.1 → 0.268.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 (75) hide show
  1. package/dist/{agent-bridge-DrkBxszZ.d.ts → agent-bridge-UhojbpWx.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-Bvtf1o9K.d.ts} +25 -7
  3. package/dist/{brain-BXd_61kQ.d.ts → brain-69wzMKp1.d.ts} +73 -1
  4. package/dist/{compactor-B8pOf45Y.d.ts → compactor-CBQAJoDc.d.ts} +19 -1
  5. package/dist/{config-BMCj_XDs.d.ts → config-VKfOZ-6X.d.ts} +122 -3
  6. package/dist/{context-MRk5PhNv.d.ts → context-C0U8B9NF.d.ts} +88 -1
  7. package/dist/coordination/index.d.ts +57 -161
  8. package/dist/coordination/index.js +471 -177
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +26 -25
  11. package/dist/defaults/index.js +1818 -844
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +72 -16
  14. package/dist/execution/index.js +1270 -265
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +7 -6
  18. package/dist/global-mailbox-KByEFFBa.d.ts +663 -0
  19. package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-CrYjmdw4.d.ts} +28 -11
  20. package/dist/{goal-store-DtLMySNb.d.ts → goal-store-Y_zdLZ3q.d.ts} +1 -1
  21. package/dist/hq/index.d.ts +195 -0
  22. package/dist/hq/index.js +1884 -0
  23. package/dist/hq/index.js.map +1 -0
  24. package/dist/index-BfaS-f_m.d.ts +82 -0
  25. package/dist/{index-B-ch8K9C.d.ts → index-CtQnmkaS.d.ts} +8 -8
  26. package/dist/{index-CEDeNodM.d.ts → index-gCv830d7.d.ts} +5 -5
  27. package/dist/index.d.ts +124 -47
  28. package/dist/index.js +5600 -2662
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/infrastructure/index.js +117 -19
  32. package/dist/infrastructure/index.js.map +1 -1
  33. package/dist/kernel/index.d.ts +10 -9
  34. package/dist/kernel/index.js.map +1 -1
  35. package/dist/{pipeline-DPDxH_7m.d.ts → mailbox-types-Ct2hJq0P.d.ts} +1 -244
  36. package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-HT3Fi7Bl.d.ts} +10 -4
  37. package/dist/models/index.d.ts +5 -5
  38. package/dist/models/index.js +33 -3
  39. package/dist/models/index.js.map +1 -1
  40. package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-Bvcl3Vaa.d.ts} +1 -1
  41. package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-BACjsmkC.d.ts} +1 -1
  42. package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-DA7fvhUg.d.ts} +14 -9
  43. package/dist/observability/index.d.ts +2 -2
  44. package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-Ci71gYu_.d.ts} +11 -15
  45. package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-O1IJnmKE.d.ts} +4 -3
  46. package/dist/{permission-B9SB45lp.d.ts → permission-Bd-57Lbl.d.ts} +1 -1
  47. package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-uNXC6Kge.d.ts} +2 -3
  48. package/dist/pipeline-BDNvENyV.d.ts +245 -0
  49. package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-EMsalEtN.d.ts} +5 -5
  50. package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-CEb9x886.d.ts} +40 -3
  51. package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-DWJbpo70.d.ts} +3 -3
  52. package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-C3s_lvdK.d.ts} +1 -1
  53. package/dist/sdd/index.d.ts +9 -8
  54. package/dist/sdd/index.js +44 -14
  55. package/dist/sdd/index.js.map +1 -1
  56. package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-Cgduf5xL.d.ts} +2 -2
  57. package/dist/security/index.d.ts +5 -67
  58. package/dist/security/index.js +129 -99
  59. package/dist/security/index.js.map +1 -1
  60. package/dist/{selector-CzHh_igB.d.ts → selector-47LBnBVk.d.ts} +1 -1
  61. package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-Cw7oqmW2.d.ts} +1 -1
  62. package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-DD4v2Obw.d.ts} +1 -1
  63. package/dist/storage/index.d.ts +14 -12
  64. package/dist/storage/index.js +144 -120
  65. package/dist/storage/index.js.map +1 -1
  66. package/dist/tools/index.d.ts +4 -2
  67. package/dist/tools/index.js +166 -31
  68. package/dist/tools/index.js.map +1 -1
  69. package/dist/types/index.d.ts +20 -19
  70. package/dist/types/index.js +1358 -476
  71. package/dist/types/index.js.map +1 -1
  72. package/dist/utils/index.d.ts +472 -405
  73. package/dist/utils/index.js +2321 -1193
  74. package/dist/utils/index.js.map +1 -1
  75. package/package.json +5 -1
@@ -449,11 +449,6 @@ function safeParse(input, maxBytes = 5e6) {
449
449
  }
450
450
  }
451
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
452
  // src/utils/expect-defined.ts
458
453
  function expectDefined(value, label) {
459
454
  if (value === null || value === void 0) {
@@ -463,111 +458,6 @@ function expectDefined(value, label) {
463
458
  }
464
459
  return value;
465
460
  }
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
461
  var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
572
462
  var IS_WINDOWS = process.platform === "win32";
573
463
  var SEP = IS_WINDOWS ? "\\" : "/";
@@ -684,6 +574,116 @@ async function expandGlob(pattern) {
684
574
  return [...results];
685
575
  }
686
576
 
577
+ // src/utils/message-invariants.ts
578
+ function repairToolUseAdjacency(messages) {
579
+ const removedToolUses = [];
580
+ const removedToolResults = [];
581
+ let removedMessages = 0;
582
+ let changed = false;
583
+ const out = [];
584
+ for (let i = 0; i < messages.length; i++) {
585
+ const original = expectDefined(messages[i]);
586
+ let msg = original;
587
+ if (hasToolUse(msg)) {
588
+ const nextIds = toolResultIds(messages[i + 1]);
589
+ const filtered = mapContent(msg, (blocks) => {
590
+ const next = [];
591
+ for (const block of blocks) {
592
+ if (block.type === "tool_use" && !nextIds.has(block.id)) {
593
+ removedToolUses.push(block.id);
594
+ changed = true;
595
+ continue;
596
+ }
597
+ next.push(block);
598
+ }
599
+ return next;
600
+ });
601
+ msg = filtered ?? msg;
602
+ }
603
+ if (hasToolResult(msg)) {
604
+ const allowed = toolUseIds(out[out.length - 1]);
605
+ const filtered = mapContent(msg, (blocks) => {
606
+ const next = [];
607
+ for (const block of blocks) {
608
+ if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
609
+ removedToolResults.push(block.tool_use_id);
610
+ changed = true;
611
+ continue;
612
+ }
613
+ next.push(block);
614
+ }
615
+ return next;
616
+ });
617
+ msg = filtered ?? msg;
618
+ }
619
+ if (isEmptyMessage(msg)) {
620
+ removedMessages++;
621
+ changed = true;
622
+ continue;
623
+ }
624
+ out.push(msg);
625
+ }
626
+ return {
627
+ messages: changed ? out : messages,
628
+ report: { changed, removedToolUses, removedToolResults, removedMessages }
629
+ };
630
+ }
631
+ function hasToolUse(msg) {
632
+ return contentBlocks(msg).some((b) => b.type === "tool_use");
633
+ }
634
+ function hasToolResult(msg) {
635
+ return contentBlocks(msg).some((b) => b.type === "tool_result");
636
+ }
637
+ function toolUseIds(msg) {
638
+ const ids = /* @__PURE__ */ new Set();
639
+ if (!msg || msg.role !== "assistant") return ids;
640
+ for (const block of contentBlocks(msg)) {
641
+ if (block.type === "tool_use") ids.add(block.id);
642
+ }
643
+ return ids;
644
+ }
645
+ function toolResultIds(msg) {
646
+ const ids = /* @__PURE__ */ new Set();
647
+ if (!msg || msg.role !== "user") return ids;
648
+ for (const block of contentBlocks(msg)) {
649
+ if (block.type === "tool_result") ids.add(block.tool_use_id);
650
+ }
651
+ return ids;
652
+ }
653
+ function contentBlocks(msg) {
654
+ return msg && Array.isArray(msg.content) ? msg.content : [];
655
+ }
656
+ function mapContent(msg, fn) {
657
+ if (!Array.isArray(msg.content)) return msg;
658
+ const next = fn(msg.content);
659
+ if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
660
+ return msg;
661
+ }
662
+ return { ...msg, content: next };
663
+ }
664
+ function isEmptyMessage(msg) {
665
+ if (typeof msg.content === "string") return msg.content.trim().length === 0;
666
+ return msg.content.length === 0;
667
+ }
668
+
669
+ // src/utils/string.ts
670
+ function truncate(s, max) {
671
+ return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
672
+ }
673
+ function projectSlug(absRoot) {
674
+ const base = slugify(path5.basename(absRoot));
675
+ const hash = createHash("sha256").update(path5.resolve(absRoot)).digest("hex").slice(0, 6);
676
+ return `${base}-${hash}`;
677
+ }
678
+ function slugify(name) {
679
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
680
+ }
681
+ function wstackGlobalRoot() {
682
+ const fromEnv = process.env["WRONGSTACK_HOME"];
683
+ if (fromEnv && fromEnv.trim().length > 0) return path5.resolve(fromEnv);
684
+ return path5.join(os.homedir(), ".wrongstack");
685
+ }
686
+
687
687
  // src/types/errors.ts
688
688
  var ERROR_CODES = {
689
689
  // Provider
@@ -1539,6 +1539,24 @@ Working rules:
1539
1539
  var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
1540
1540
  a specific slice of a larger plan \u2014 do that slice well and report back.
1541
1541
 
1542
+ Capabilities & operating rules:
1543
+ - You have full developer tools for your task: read, write/edit, search,
1544
+ shell + build (lint, format, typecheck, test), and dependency install.
1545
+ Use them directly to finish the task end-to-end. You run non-interactively
1546
+ \u2014 there is no human to approve individual tool calls, so routine work is
1547
+ pre-authorized; do not stop to ask for permission to read, edit, or build.
1548
+ - Stay inside the project root. Do not write files outside the repository,
1549
+ and do not touch machine config, credentials, or global state \u2014 those
1550
+ require an explicit grant you do not have.
1551
+ - Prefer the least-destructive path. Do not run irreversible or destructive
1552
+ commands (e.g. \`rm -rf\`, \`git push --force\`, history rewrites, dropping
1553
+ databases, mass deletes) unless the task explicitly requires it and names
1554
+ the target.
1555
+ - When you change code, verify it: run the relevant build / typecheck / tests
1556
+ and fix what you broke before reporting done.
1557
+ - Make only the changes the task calls for \u2014 don't refactor or reformat
1558
+ unrelated code.
1559
+
1542
1560
  Bridge contract:
1543
1561
  - You have a parent (the Director). You may call \`request\` on the
1544
1562
  parent bridge to ask a clarifying question. Use this sparingly; the
@@ -3747,7 +3765,27 @@ Working rules:
3747
3765
  id: "devops",
3748
3766
  name: "DevOps",
3749
3767
  role: "devops",
3750
- tools: [...TOOLS.build],
3768
+ tools: [
3769
+ ...TOOLS.build,
3770
+ "mcp__ssh__ssh_list_servers",
3771
+ "mcp__ssh__ssh_connection_status",
3772
+ "mcp__ssh__ssh_execute",
3773
+ "mcp__ssh__ssh_execute_sudo",
3774
+ "mcp__ssh__ssh_upload",
3775
+ "mcp__ssh__ssh_download",
3776
+ "mcp__ssh__ssh_sync",
3777
+ "mcp__ssh__ssh_deploy",
3778
+ "mcp__ssh__ssh_health_check",
3779
+ "mcp__ssh__ssh_service_status",
3780
+ "mcp__ssh__ssh_process_manager",
3781
+ "mcp__ssh__ssh_tunnel",
3782
+ "mcp__ssh__ssh_backup_create",
3783
+ "mcp__ssh__ssh_backup_list",
3784
+ "mcp__ssh__ssh_backup_restore",
3785
+ "mcp__ssh__ssh_db_list",
3786
+ "mcp__ssh__ssh_db_query",
3787
+ "mcp__ssh__ssh_profile"
3788
+ ],
3751
3789
  prompt: `You are the DevOps agent. Your job is CI/CD, containerization, and
3752
3790
  deployment configuration: make builds reproducible and deploys safe.
3753
3791
 
@@ -3756,6 +3794,7 @@ Scope:
3756
3794
  - Write Dockerfiles/compose and optimize image size and layer caching
3757
3795
  - Configure deployment (env, secrets handling, health checks, rollback)
3758
3796
  - Diagnose flaky/broken pipelines
3797
+ - Use optional SSH MCP tools for remote hosts when the 'ssh' MCP server is enabled: list servers, run health checks, inspect services, transfer/deploy files, and open tunnels
3759
3798
 
3760
3799
  Input format you accept:
3761
3800
  { "task": "ci | container | deploy | fix-pipeline", "platform": "github-actions | gitlab | docker | k8s", "target": "<what>" }
@@ -3770,7 +3809,8 @@ Working rules:
3770
3809
  - Never hardcode secrets in config; reference the secret store
3771
3810
  - Pin versions for reproducible builds; avoid floating :latest
3772
3811
  - Every deploy path needs a rollback and a health check
3773
- - Treat CI/CD changes as high-risk \u2014 explain blast radius before applying`
3812
+ - Treat CI/CD changes as high-risk \u2014 explain blast radius before applying
3813
+ - For remote SSH work, start with ssh_list_servers / ssh_connection_status, prefer read-only checks first, and do not run destructive commands without explicit user approval`
3774
3814
  },
3775
3815
  budget: MEDIUM_BUDGET,
3776
3816
  capability: {
@@ -3787,6 +3827,13 @@ Working rules:
3787
3827
  "kubernetes",
3788
3828
  "k8s",
3789
3829
  "deploy",
3830
+ "ssh",
3831
+ "remote ssh",
3832
+ "remote server",
3833
+ "sftp",
3834
+ "tunnel",
3835
+ "bastion",
3836
+ "jump host",
3790
3837
  "github actions",
3791
3838
  "container"
3792
3839
  ]
@@ -6782,6 +6829,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
6782
6829
  subagentId: result.subagentId,
6783
6830
  taskId: result.taskId,
6784
6831
  status: result.status,
6832
+ result: result.result,
6785
6833
  iterations: result.iterations,
6786
6834
  toolCalls: result.toolCalls,
6787
6835
  durationMs: result.durationMs
@@ -6996,6 +7044,8 @@ var Director = class _Director {
6996
7044
  sessionsRoot;
6997
7045
  /** Director run id for JSONL path resolution. */
6998
7046
  directorRunId;
7047
+ /** Optional logger for structured logging. Falls back to noop when omitted. */
7048
+ logger;
6999
7049
  /** Resolves task descriptions back from `assign()` so completion events
7000
7050
  * can also carry a human-readable title. */
7001
7051
  taskDescriptions = /* @__PURE__ */ new Map();
@@ -7084,6 +7134,7 @@ var Director = class _Director {
7084
7134
  opts.checkpointDebounceMs ?? 250
7085
7135
  ) : null;
7086
7136
  this.fleetManager = opts.fleetManager;
7137
+ this.logger = opts.logger;
7087
7138
  if (this.sharedScratchpadPath) {
7088
7139
  void fsp6.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
7089
7140
  }
@@ -7383,7 +7434,10 @@ var Director = class _Director {
7383
7434
  entry.session.cancel(reason);
7384
7435
  for (const [_role, subagentId] of entry.session.getSubagentIds()) {
7385
7436
  this.coordinator.stop(subagentId).catch((err) => {
7386
- console.debug(`[director] stop subagent ${subagentId} failed (may have already completed): ${err}`);
7437
+ this.logger?.debug(`stop subagent ${subagentId} failed (may have already completed)`, {
7438
+ subagentId,
7439
+ err: err instanceof Error ? err.message : String(err)
7440
+ });
7387
7441
  });
7388
7442
  }
7389
7443
  }
@@ -7443,7 +7497,7 @@ var Director = class _Director {
7443
7497
  * Caller-supplied `priceLookup` is optional but recommended — without
7444
7498
  * it the `cost` column in `usage.snapshot()` stays at 0.
7445
7499
  */
7446
- async spawn(config, priceLookup) {
7500
+ async spawn(callerConfig, priceLookup) {
7447
7501
  if (this.workCompleteFlag) {
7448
7502
  throw new FleetSpawnBudgetError(
7449
7503
  "max_spawns",
@@ -7452,6 +7506,7 @@ var Director = class _Director {
7452
7506
  "workComplete() has been called \u2014 director closed further spawning"
7453
7507
  );
7454
7508
  }
7509
+ const config = { ...callerConfig };
7455
7510
  if (!config.model && this.modelMatrix) {
7456
7511
  const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
7457
7512
  const entry = resolveModelMatrix(matrix, config.role);
@@ -8879,6 +8934,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
8879
8934
  const cached = this._loadCache.get(id);
8880
8935
  if (cached && cached.mtimeMs === stat6.mtimeMs && cached.size === stat6.size) {
8881
8936
  cacheHit = true;
8937
+ this._loadCache.delete(id);
8938
+ this._loadCache.set(id, cached);
8882
8939
  return cached.data;
8883
8940
  }
8884
8941
  const raw = await fsp6.readFile(file, "utf8");
@@ -10375,30 +10432,18 @@ var DefaultMailbox = class {
10375
10432
  async query(q) {
10376
10433
  const all = await this._readAll();
10377
10434
  const limit = q.limit ?? 50;
10378
- let filtered = all;
10379
- if (q.to !== void 0) {
10380
- filtered = filtered.filter((m) => m.to === q.to || m.to === "*");
10381
- }
10382
- if (q.from !== void 0) {
10383
- filtered = filtered.filter((m) => m.from === q.from);
10384
- }
10385
- if (q.unreadBy !== void 0) {
10386
- filtered = filtered.filter((m) => !(q.unreadBy in m.readBy));
10387
- }
10388
- if (q.incompleteOnly) {
10389
- filtered = filtered.filter((m) => !m.completed);
10390
- }
10391
- if (q.type !== void 0) {
10392
- filtered = filtered.filter((m) => m.type === q.type);
10393
- }
10394
- if (q.minPriority !== void 0) {
10395
- const order = { low: 0, normal: 1, high: 2 };
10396
- const min = order[q.minPriority];
10397
- filtered = filtered.filter((m) => (order[m.priority] ?? 1) >= min);
10398
- }
10399
- if (q.since !== void 0) {
10400
- const since = q.since;
10401
- filtered = filtered.filter((m) => m.timestamp > since);
10435
+ const order = q.minPriority !== void 0 ? { low: 0, normal: 1, high: 2 } : null;
10436
+ const minPriorityRank = order && q.minPriority !== void 0 ? order[q.minPriority] : 0;
10437
+ const filtered = [];
10438
+ for (const msg of all) {
10439
+ if (q.to !== void 0 && msg.to !== q.to && msg.to !== "*") continue;
10440
+ if (q.from !== void 0 && msg.from !== q.from) continue;
10441
+ if (q.unreadBy !== void 0 && q.unreadBy in msg.readBy) continue;
10442
+ if (q.incompleteOnly && msg.completed) continue;
10443
+ if (q.type !== void 0 && msg.type !== q.type) continue;
10444
+ if (order !== null && (order[msg.priority] ?? 1) < minPriorityRank) continue;
10445
+ if (q.since !== void 0 && msg.timestamp <= q.since) continue;
10446
+ filtered.push(msg);
10402
10447
  }
10403
10448
  filtered.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10404
10449
  return filtered.slice(0, limit);
@@ -10719,6 +10764,8 @@ var GlobalMailbox = class {
10719
10764
  clientRegistryPath;
10720
10765
  /** Optional event bus for emitting agent registration/heartbeat events. */
10721
10766
  _events;
10767
+ /** Optional HQ publisher for cross-project command-center telemetry. */
10768
+ _hqPublisher;
10722
10769
  /**
10723
10770
  * Local cache of the agent registry to avoid re-reading on every call.
10724
10771
  * Time-bounded: the registry file is shared ACROSS PROCESSES (that's the
@@ -10756,12 +10803,28 @@ var GlobalMailbox = class {
10756
10803
  /**
10757
10804
  * @param projectDir — `~/.wrongstack/projects/<slug>/`
10758
10805
  * @param events — optional EventBus for real-time TUI/WebUI notifications
10806
+ * @param hqPublisher — optional HQ publisher for cross-project telemetry
10759
10807
  */
10760
- constructor(projectDir, events) {
10808
+ constructor(projectDir, events, hqPublisher) {
10761
10809
  this.messagePath = path5.join(projectDir, MAILBOX_FILE2);
10762
10810
  this.registryPath = path5.join(projectDir, "_mailbox.registry.json");
10763
10811
  this.clientRegistryPath = path5.join(projectDir, CLIENT_REGISTRY_FILE);
10764
10812
  this._events = events;
10813
+ this._hqPublisher = hqPublisher;
10814
+ }
10815
+ get hqMailboxId() {
10816
+ return `${path5.basename(path5.dirname(this.messagePath))}:mailbox`;
10817
+ }
10818
+ publishHqMailboxEvent(input) {
10819
+ try {
10820
+ this._hqPublisher?.publishMailboxEvent(input);
10821
+ } catch {
10822
+ }
10823
+ }
10824
+ publishHqMailboxSnapshot() {
10825
+ if (this._hqPublisher === void 0) return;
10826
+ void this._hqPublisher.publishMailboxSnapshot(this, { mailboxId: this.hqMailboxId }).catch(() => {
10827
+ });
10765
10828
  }
10766
10829
  // ── Messages ────────────────────────────────────────────────────────────
10767
10830
  async send(input) {
@@ -10788,6 +10851,8 @@ var GlobalMailbox = class {
10788
10851
  await fsp6.appendFile(this.messagePath, line, "utf8");
10789
10852
  this._pushToCache(msg);
10790
10853
  });
10854
+ this.publishHqMailboxEvent({ mailboxId: this.hqMailboxId, action: "message.sent", message: msg });
10855
+ this.publishHqMailboxSnapshot();
10791
10856
  return msg;
10792
10857
  }
10793
10858
  async query(q) {
@@ -10854,6 +10919,14 @@ var GlobalMailbox = class {
10854
10919
  cacheSnapshot = all;
10855
10920
  });
10856
10921
  if (cacheSnapshot) this._setMessageCache(cacheSnapshot);
10922
+ for (const message of updated) {
10923
+ this.publishHqMailboxEvent({
10924
+ mailboxId: this.hqMailboxId,
10925
+ action: message.completed ? "message.completed" : "message.read",
10926
+ message
10927
+ });
10928
+ }
10929
+ if (updated.length > 0) this.publishHqMailboxSnapshot();
10857
10930
  return updated;
10858
10931
  }
10859
10932
  async unreadCount(forAgentId) {
@@ -10901,6 +10974,25 @@ var GlobalMailbox = class {
10901
10974
  role: input.role,
10902
10975
  source: input.source
10903
10976
  });
10977
+ this.publishHqMailboxEvent({
10978
+ mailboxId: this.hqMailboxId,
10979
+ action: "agent.registered",
10980
+ agent: {
10981
+ agentId: input.agentId,
10982
+ name: input.name,
10983
+ ...input.role !== void 0 ? { role: input.role } : {},
10984
+ sessionId: input.sessionId,
10985
+ status: "idle",
10986
+ iterations: 0,
10987
+ toolCalls: 0,
10988
+ lastActivityAt: now,
10989
+ lastSeenAt: now,
10990
+ online: true,
10991
+ pid: input.pid,
10992
+ ...input.source !== void 0 ? { source: input.source } : {}
10993
+ }
10994
+ });
10995
+ this.publishHqMailboxSnapshot();
10904
10996
  }
10905
10997
  async heartbeat(input) {
10906
10998
  const last = this._lastHeartbeat.get(input.agentId) ?? 0;
@@ -10931,6 +11023,12 @@ var GlobalMailbox = class {
10931
11023
  currentTool: input.currentTool,
10932
11024
  currentTask: input.currentTask
10933
11025
  });
11026
+ this.publishHqMailboxEvent({
11027
+ mailboxId: this.hqMailboxId,
11028
+ action: "agent.heartbeat",
11029
+ summary: input.agentId
11030
+ });
11031
+ this.publishHqMailboxSnapshot();
10934
11032
  }
10935
11033
  async getAgentStatuses() {
10936
11034
  await this._ensureRegistry();
@@ -11743,13 +11841,13 @@ function makeDependencyWatcherConfig(opts) {
11743
11841
  const globPatterns = patterns.filter((p) => p.includes("*"));
11744
11842
  const plainPatterns = patterns.filter((p) => !p.includes("*"));
11745
11843
  function matchesPattern(filePath) {
11746
- const basename5 = filePath.split("/").pop()?.split("\\").pop() ?? "";
11747
- if (plainPatterns.includes(basename5)) return true;
11844
+ const basename6 = filePath.split("/").pop()?.split("\\").pop() ?? "";
11845
+ if (plainPatterns.includes(basename6)) return true;
11748
11846
  for (const gp of globPatterns) {
11749
11847
  const regex = new RegExp(
11750
11848
  "^" + gp.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$"
11751
11849
  );
11752
- if (regex.test(basename5)) return true;
11850
+ if (regex.test(basename6)) return true;
11753
11851
  }
11754
11852
  return false;
11755
11853
  }
@@ -12725,12 +12823,12 @@ var ConsensusProtocol = class {
12725
12823
  * Initiate a vote on a proposed change. Updates the change node's status
12726
12824
  * to 'proposed' and notifies eligible voters via FleetBus.
12727
12825
  */
12728
- initiateVote(changeId) {
12826
+ async initiateVote(changeId) {
12729
12827
  const change = this.graph.get(changeId);
12730
12828
  if (!change || change.type !== "change") {
12731
12829
  throw new Error(`ConsensusProtocol: no change found with id "${changeId}"`);
12732
12830
  }
12733
- this.graph.update(changeId, { status: "proposed", votes: [] });
12831
+ await this.graph.update(changeId, { status: "proposed", votes: [] });
12734
12832
  const eligible = this._eligibleVoters(change);
12735
12833
  this._notifyVoters(change, eligible, "vote_initiated");
12736
12834
  }
@@ -12738,7 +12836,7 @@ var ConsensusProtocol = class {
12738
12836
  * Cast a vote. Updates the change node in the graph and re-evaluates
12739
12837
  * consensus. If the vote triggers a resolution, updates the change status.
12740
12838
  */
12741
- castVote(changeId, voterId, value, rationale) {
12839
+ async castVote(changeId, voterId, value, rationale) {
12742
12840
  const change = this.graph.get(changeId);
12743
12841
  if (!change || change.type !== "change") {
12744
12842
  throw new Error(`ConsensusProtocol: no change found for "${changeId}"`);
@@ -12761,7 +12859,7 @@ var ConsensusProtocol = class {
12761
12859
  const existingIdx = change.votes.findIndex((v) => v.agentId === voterId);
12762
12860
  const newVotes = existingIdx >= 0 ? change.votes.with(existingIdx, vote) : [...change.votes, vote];
12763
12861
  const result = this._resolve(changeId, newVotes, eligible);
12764
- this.graph.update(changeId, {
12862
+ await this.graph.update(changeId, {
12765
12863
  votes: newVotes,
12766
12864
  ...result.outcome !== "pending" ? { status: this._toChangeStatus(result.outcome) } : {}
12767
12865
  });
@@ -12772,13 +12870,13 @@ var ConsensusProtocol = class {
12772
12870
  * Resolve the current vote without waiting for all eligible voters.
12773
12871
  * Useful when a timeout fires or an agent decides to finalize early.
12774
12872
  */
12775
- resolveNow(changeId) {
12873
+ async resolveNow(changeId) {
12776
12874
  const change = this.graph.get(changeId);
12777
12875
  if (!change) throw new Error(`ConsensusProtocol: unknown change "${changeId}"`);
12778
12876
  const eligible = this._eligibleVoters(change);
12779
12877
  const result = this._resolve(changeId, change.votes, eligible);
12780
12878
  if (result.outcome !== "pending") {
12781
- this.graph.update(changeId, { status: this._toChangeStatus(result.outcome) });
12879
+ await this.graph.update(changeId, { status: this._toChangeStatus(result.outcome) });
12782
12880
  this._notifyVoters(change, eligible, "vote_resolved", { result });
12783
12881
  }
12784
12882
  return result;
@@ -12985,8 +13083,7 @@ var ChangeManager = class {
12985
13083
  // filled after quality gate
12986
13084
  satisfiesGoals: input.satisfiesGoals
12987
13085
  });
12988
- void this._runQualityGate(node.id, input.files).then((gate) => {
12989
- void this.graph.update(node.id, { qualityGate: gate });
13086
+ void this._runQualityGate(node.id, input.files).then((gate) => this.graph.update(node.id, { qualityGate: gate })).catch(() => {
12990
13087
  });
12991
13088
  this._emit("change:proposed", { changeId: node.id, title: node.title });
12992
13089
  return node;
@@ -13702,7 +13799,8 @@ Priority: ${goal.priority}`,
13702
13799
  this.agentTaskCount(agentId, -1);
13703
13800
  await this.graph.update(taskId, {
13704
13801
  status: "done",
13705
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13802
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13803
+ ..._result !== void 0 ? { result: _result } : {}
13706
13804
  });
13707
13805
  this.bidRetryCounts.delete(taskId);
13708
13806
  this.pendingBids.delete(taskId);
@@ -13947,7 +14045,7 @@ ${goal.description}`,
13947
14045
  this.agentTaskCounts.set(agentId, next);
13948
14046
  }
13949
14047
  };
13950
- var AutonomousCoordinator = class {
14048
+ var AutonomousCoordinator = class _AutonomousCoordinator {
13951
14049
  graph;
13952
14050
  dag;
13953
14051
  auction;
@@ -13963,6 +14061,8 @@ var AutonomousCoordinator = class {
13963
14061
  onCoordinatorEvent;
13964
14062
  running = false;
13965
14063
  iterationCount = 0;
14064
+ lastSyncAt = 0;
14065
+ static SYNC_INTERVAL_MS = 5e3;
13966
14066
  /** Tasks already handled by _onSubagentTerminated (to avoid double goal:failed on fleet event). */
13967
14067
  _handledBySubagent = /* @__PURE__ */ new Set();
13968
14068
  /** FleetBus subscription disposers, detached in dispose(). */
@@ -14019,7 +14119,7 @@ var AutonomousCoordinator = class {
14019
14119
  const taskId = payload?.taskId;
14020
14120
  if (!taskId || this._handledBySubagent.has(taskId)) return;
14021
14121
  this._handledBySubagent.add(taskId);
14022
- this._emit({ type: "goal:failed", goalId: taskId, text: payload?.error ?? "Task failed" });
14122
+ this._recordTaskFailed(taskId, payload?.error ?? "Task failed");
14023
14123
  });
14024
14124
  if (offFailed) this.unsubs.push(offFailed);
14025
14125
  this._emit({ type: "coordinator:mode", mode: this.fleet ? "fleet" : "standalone" });
@@ -14038,6 +14138,7 @@ var AutonomousCoordinator = class {
14038
14138
  const maxCost = opts.maxCostUsd;
14039
14139
  try {
14040
14140
  await this.graph.load();
14141
+ this._rebuildDagFromGraph();
14041
14142
  const goalConfigs = await this._decomposeGoal(goal);
14042
14143
  for (const g of goalConfigs) {
14043
14144
  const goalId = await this.auction.publishTask(g);
@@ -14045,23 +14146,49 @@ var AutonomousCoordinator = class {
14045
14146
  this._emit({ type: "goal:added", goalId, title: g.title, text: g.description });
14046
14147
  }
14047
14148
  while (this.running) {
14149
+ if (this.dag.getRunning().length > 0 && this.auction.getPendingTasks().length === 0) {
14150
+ await this._waitForDagProgress(1e3);
14151
+ continue;
14152
+ }
14048
14153
  this.iterationCount++;
14154
+ await this._maybeSyncFromGraph();
14049
14155
  if (this.iterationCount >= maxIterations) break;
14050
14156
  if (maxCost !== void 0) {
14051
14157
  const cost = this.fleetManager?.snapshot()?.total?.cost ?? 0;
14052
14158
  if (cost >= maxCost) break;
14053
14159
  }
14054
14160
  if (opts.runUntilComplete && this.dag.isDone()) break;
14161
+ const pendingTasks = this.auction.getPendingTasks();
14162
+ const dispatchable = pendingTasks.filter((task) => {
14163
+ const dagNode = this.dag.getNode(task.id);
14164
+ return !dagNode || dagNode.status === "ready";
14165
+ });
14166
+ if (dispatchable.length === 0) {
14167
+ if (this.dag.getRunning().length > 0 || this.dag.getReady().length > 0) {
14168
+ await this._waitForDagProgress(1e3);
14169
+ continue;
14170
+ }
14171
+ if (pendingTasks.length > 0) {
14172
+ await this._waitForDagProgress(2e3);
14173
+ continue;
14174
+ }
14175
+ if (this.dag.hasDeadlock()) {
14176
+ const blocked = this.dag.getBlocked();
14177
+ (this.events?.emit)("autonomous:deadlock", { blocked });
14178
+ this._emit({ type: "deadlock:detected", goalId: blocked[0]?.id ?? "", text: `Deadlock detected: ${blocked.map((n) => n.id).join(", ")}` });
14179
+ }
14180
+ break;
14181
+ }
14055
14182
  const decision = await this.brain.decideAuto({
14056
14183
  id: randomUUID(),
14057
14184
  source: "system",
14058
14185
  decisionType: "prioritize_goals",
14059
- question: `What should we work on next? Open goals: ${this.auction.getPendingTasks().map((g) => g.title).join(", ") || "none"}`,
14186
+ question: `What should we work on next? Open goals: ${dispatchable.map((g) => g.title).join(", ")}`,
14060
14187
  context: {
14061
- goals: this.auction.getPendingTasks(),
14188
+ goals: dispatchable,
14062
14189
  fleetStatus: this._fleetStatus()
14063
14190
  },
14064
- options: this._goalToOptions(this.auction.getPendingTasks()),
14191
+ options: this._goalToOptions(dispatchable),
14065
14192
  risk: "medium",
14066
14193
  requiresConsensus: false
14067
14194
  });
@@ -14108,6 +14235,55 @@ var AutonomousCoordinator = class {
14108
14235
  this.running = false;
14109
14236
  console.error(`[AutonomousCoordinator] stop signal received \u2014 shutting down (iteration ${this.iterationCount})`);
14110
14237
  }
14238
+ /**
14239
+ * Report that a terminal worker (not a Director subagent) completed a claimed
14240
+ * task. This updates the auction, DAG, publishes a task-result fact, and
14241
+ * extracts follow-up goals — the same path as subagent completion.
14242
+ */
14243
+ async reportTaskCompletion(taskId, result) {
14244
+ this._handledBySubagent.add(taskId);
14245
+ await this._completeTask(taskId, result);
14246
+ }
14247
+ /**
14248
+ * Report that a terminal worker failed a claimed task.
14249
+ */
14250
+ async reportTaskFailure(taskId, error) {
14251
+ this._handledBySubagent.add(taskId);
14252
+ await this._failTask(taskId, error);
14253
+ }
14254
+ /**
14255
+ * Reload the KnowledgeGraph from disk and sync the in-memory DAG with any
14256
+ * changes published by other terminal sessions. New goals are added to the
14257
+ * DAG; existing goals whose status changed (e.g. completed by another
14258
+ * terminal) are transitioned accordingly.
14259
+ *
14260
+ * Safe to call at any time — also used internally by the run loop.
14261
+ */
14262
+ async syncFromGraph() {
14263
+ await this.graph.load();
14264
+ this._rebuildDagFromGraph();
14265
+ this._syncDagStatuses();
14266
+ }
14267
+ _syncDagStatuses() {
14268
+ const goals = this.graph.getGoals({});
14269
+ for (const goal of goals) {
14270
+ const dagNode = this.dag.getNode(goal.id);
14271
+ if (!dagNode) continue;
14272
+ if (goal.status === "done" && dagNode.status !== "done" && dagNode.status !== "failed") {
14273
+ this.dag.complete(goal.id, goal.result ?? "Completed by another session");
14274
+ } else if (goal.status === "failed" && dagNode.status !== "failed" && dagNode.status !== "done") {
14275
+ this.dag.fail(goal.id, goal.result ?? "Failed by another session");
14276
+ } else if (goal.status === "in_progress" && (dagNode.status === "ready" || dagNode.status === "pending")) {
14277
+ this.dag.start(goal.id, goal.assignee ?? "another-session");
14278
+ }
14279
+ }
14280
+ }
14281
+ async _maybeSyncFromGraph() {
14282
+ const now = Date.now();
14283
+ if (now - this.lastSyncAt < _AutonomousCoordinator.SYNC_INTERVAL_MS) return;
14284
+ this.lastSyncAt = now;
14285
+ await this.syncFromGraph();
14286
+ }
14111
14287
  /**
14112
14288
  * Tear down the coordinator for good: stop the loop and detach all FleetBus
14113
14289
  * subscriptions (this coordinator's + the auctioneer's) plus any open bid
@@ -14204,6 +14380,65 @@ ${input.detail}`
14204
14380
  return goal;
14205
14381
  }
14206
14382
  // ── Private ───────────────────────────────────────────────────────────
14383
+ _waitForDagProgress(timeoutMs) {
14384
+ const before = this._dagProgressKey();
14385
+ if (this.dag.isDone()) return Promise.resolve();
14386
+ return new Promise((resolve3) => {
14387
+ let off;
14388
+ const timer = setTimeout(() => {
14389
+ off?.();
14390
+ resolve3();
14391
+ }, timeoutMs);
14392
+ off = this.dag.onEvent(() => {
14393
+ if (this._dagProgressKey() === before) return;
14394
+ clearTimeout(timer);
14395
+ off?.();
14396
+ resolve3();
14397
+ });
14398
+ });
14399
+ }
14400
+ _dagProgressKey() {
14401
+ const s = this.dag.stats();
14402
+ return `${s.pending}:${s.ready}:${s.running}:${s.done}:${s.failed}:${s.skipped}`;
14403
+ }
14404
+ _rebuildDagFromGraph() {
14405
+ const goals = this.graph.getGoals({});
14406
+ const knownGoalIds = new Set(goals.map((goal) => goal.id));
14407
+ const added = /* @__PURE__ */ new Set();
14408
+ const remaining = new Map(goals.map((goal) => [goal.id, goal]));
14409
+ while (remaining.size > 0) {
14410
+ let progressed = false;
14411
+ for (const [id, goal] of Array.from(remaining.entries())) {
14412
+ const deps = goal.blockedBy.filter((depId) => knownGoalIds.has(depId));
14413
+ if (!deps.every((depId) => added.has(depId))) continue;
14414
+ this._rebuildDagNode(goal, deps);
14415
+ added.add(id);
14416
+ remaining.delete(id);
14417
+ progressed = true;
14418
+ }
14419
+ if (!progressed) {
14420
+ for (const [id, goal] of Array.from(remaining.entries())) {
14421
+ this._rebuildDagNode(goal, []);
14422
+ added.add(id);
14423
+ remaining.delete(id);
14424
+ }
14425
+ }
14426
+ }
14427
+ }
14428
+ _rebuildDagNode(goal, deps) {
14429
+ this.dag.addNode(goal.id, goal.description, deps, { tags: goal.tags });
14430
+ if (goal.status === "in_progress") {
14431
+ this.dag.start(goal.id, goal.assignee ?? "unknown");
14432
+ return;
14433
+ }
14434
+ if (goal.status === "done") {
14435
+ this.dag.complete(goal.id, goal.result ?? "Persisted completion");
14436
+ return;
14437
+ }
14438
+ if (goal.status === "failed") {
14439
+ this.dag.fail(goal.id, goal.result ?? "Persisted failure");
14440
+ }
14441
+ }
14207
14442
  async _decomposeGoal(goalText) {
14208
14443
  const category = this._inferCategory(goalText);
14209
14444
  const subGoals = [];
@@ -14240,13 +14475,7 @@ ${input.detail}`
14240
14475
  const goalNode = this.graph.get(goalId);
14241
14476
  if (!goalNode) return;
14242
14477
  const title = goalNode.title || dagNode.description;
14243
- const taskId = await this.auction.publishTask({
14244
- title,
14245
- description: goalNode.description,
14246
- priority: this._dagPriorityToGoal(dagNode.priority),
14247
- tags: dagNode.tags
14248
- });
14249
- this._emit({ type: "task:ready", goalId, taskId, title });
14478
+ this._emit({ type: "task:ready", goalId, taskId: goalId, title });
14250
14479
  if (this.director) {
14251
14480
  const config = {
14252
14481
  name: `worker-${goalId.slice(0, 8)}`,
@@ -14256,7 +14485,7 @@ ${input.detail}`
14256
14485
  // 10 minutes per goal
14257
14486
  };
14258
14487
  const subagentId = await this.director.spawn(config);
14259
- await this.auction.claim(taskId, subagentId, config.name);
14488
+ await this.auction.claim(goalId, subagentId, config.name);
14260
14489
  await this.director.assign({
14261
14490
  id: goalId,
14262
14491
  subagentId,
@@ -14264,6 +14493,78 @@ ${input.detail}`
14264
14493
  });
14265
14494
  }
14266
14495
  }
14496
+ _stringifyTaskResult(result) {
14497
+ if (typeof result === "string" && result.trim()) return result.trim();
14498
+ if (result === void 0 || result === null) return "Subagent completed successfully";
14499
+ try {
14500
+ return JSON.stringify(result);
14501
+ } catch {
14502
+ return String(result);
14503
+ }
14504
+ }
14505
+ async _completeTask(taskId, result) {
14506
+ await this.auction.complete(taskId, result);
14507
+ if (this.dag.getNode(taskId)) {
14508
+ this.dag.complete(taskId, result);
14509
+ }
14510
+ await this._publishTaskResultFact(taskId, result);
14511
+ await this._createFollowUpGoalsFromResult(taskId, result);
14512
+ this._emit({ type: "task:completed", goalId: taskId, taskId, text: result });
14513
+ }
14514
+ async _publishTaskResultFact(taskId, result) {
14515
+ const key = `task-result:${taskId}`;
14516
+ if (this.graph.getFacts({ category: "quality" }).some((fact2) => fact2.key === key)) return;
14517
+ const goal = this.graph.get(taskId);
14518
+ const subject = goal?.type === "goal" ? `Task completed: ${goal.title}` : `Task completed: ${taskId}`;
14519
+ const fact = await this.graph.add({
14520
+ type: "fact",
14521
+ category: "quality",
14522
+ subject,
14523
+ detail: result,
14524
+ discoveredBy: this.selfAgentId,
14525
+ discoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
14526
+ tags: ["task-result", "autonomous-coordinator"],
14527
+ key,
14528
+ related: [taskId]
14529
+ });
14530
+ this._emit({ type: "knowledge:added", knowledgeId: fact.id, title: subject, text: result });
14531
+ }
14532
+ async _createFollowUpGoalsFromResult(taskId, result) {
14533
+ const followUps = this._extractFollowUps(result);
14534
+ if (followUps.length === 0) return;
14535
+ const existing = this.graph.getGoals({});
14536
+ for (const title of followUps) {
14537
+ if (existing.some((goal2) => goal2.title === title && goal2.tags.includes("follow-up"))) continue;
14538
+ const goal = await this.createGoal({
14539
+ title,
14540
+ description: title,
14541
+ priority: "medium",
14542
+ tags: ["follow-up", "task-result", taskId]
14543
+ });
14544
+ this._emit({ type: "goal:added", goalId: goal.id, title: goal.title, text: goal.description });
14545
+ }
14546
+ }
14547
+ _extractFollowUps(result) {
14548
+ const found = [];
14549
+ for (const line of result.split(/\r?\n/)) {
14550
+ const match = /^\s*(?:[-*]\s*)?(?:NEXT|TODO|FOLLOW-?UP):\s*(.+)$/i.exec(line);
14551
+ const text = match?.[1]?.trim();
14552
+ if (!text || found.includes(text)) continue;
14553
+ found.push(text);
14554
+ if (found.length >= 5) break;
14555
+ }
14556
+ return found;
14557
+ }
14558
+ async _failTask(taskId, error) {
14559
+ await this.auction.fail(taskId, error);
14560
+ this._recordTaskFailed(taskId, error);
14561
+ }
14562
+ _recordTaskFailed(taskId, error) {
14563
+ if (this.dag.getNode(taskId)) {
14564
+ this.dag.fail(taskId, error);
14565
+ }
14566
+ this._emit({ type: "goal:failed", goalId: taskId, text: error });
14567
+ }
14267
14568
  async _handlePendingChange(change) {
14268
14569
  const result = this.consensus.getStatus(change.id);
14269
14570
  if (result?.outcome !== "pending") return;
@@ -14304,16 +14605,15 @@ ${input.detail}`
14304
14605
  _onSubagentTerminated(e) {
14305
14606
  const payload = e.payload;
14306
14607
  const subagentId = payload?.subagentId ?? e.subagentId;
14307
- const stopReason = payload?.stopReason ?? (payload?.status === "ok" ? "end_turn" : payload?.status ?? "unknown");
14308
- const tasks = this.auction.getTasksForAgent(subagentId);
14608
+ const rawStatus = payload?.stopReason ?? payload?.status ?? "unknown";
14609
+ const succeeded = rawStatus === "end_turn" || rawStatus === "ok" || rawStatus === "success";
14610
+ const tasks = payload?.taskId ? this.auction.getTasksForAgent(subagentId).filter((task) => task.id === payload.taskId) : this.auction.getTasksForAgent(subagentId);
14309
14611
  for (const task of tasks) {
14310
14612
  this._handledBySubagent.add(task.id);
14311
- if (stopReason === "end_turn") {
14312
- void this.auction.complete(task.id, "Subagent completed successfully");
14313
- this._emit({ type: "task:completed", goalId: task.id, taskId: task.id, text: "Subagent completed successfully" });
14613
+ if (succeeded) {
14614
+ void this._completeTask(task.id, this._stringifyTaskResult(payload?.result));
14314
14615
  } else {
14315
- void this.auction.fail(task.id, `Subagent terminated: ${stopReason}`);
14316
- this._emit({ type: "goal:failed", goalId: task.id, text: `Subagent terminated: ${stopReason}` });
14616
+ void this._failTask(task.id, `Subagent terminated: ${rawStatus}`);
14317
14617
  }
14318
14618
  }
14319
14619
  }
@@ -14348,12 +14648,6 @@ ${input.detail}`
14348
14648
  _optionToGoal(optionId) {
14349
14649
  return this.graph.get(optionId);
14350
14650
  }
14351
- _dagPriorityToGoal(p) {
14352
- if (p <= 1) return "critical";
14353
- if (p <= 2) return "high";
14354
- if (p <= 4) return "medium";
14355
- return "low";
14356
- }
14357
14651
  async _mailboxBroadcast(msg) {
14358
14652
  if (!this.mailbox) return;
14359
14653
  try {