@wrongstack/core 0.267.0 → 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 (74) hide show
  1. package/dist/{agent-bridge-STJ3JwwK.d.ts → agent-bridge-UhojbpWx.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-CzPGP3jA.d.ts → agent-subagent-runner-Bvtf1o9K.d.ts} +7 -7
  3. package/dist/{brain-Cdg77tVN.d.ts → brain-69wzMKp1.d.ts} +1 -1
  4. package/dist/{compactor-iMZ84CXq.d.ts → compactor-CBQAJoDc.d.ts} +1 -1
  5. package/dist/{config-Du3pYYln.d.ts → config-VKfOZ-6X.d.ts} +70 -2
  6. package/dist/{context-dT5Ueund.d.ts → context-C0U8B9NF.d.ts} +24 -1
  7. package/dist/coordination/index.d.ts +56 -160
  8. package/dist/coordination/index.js +333 -63
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +26 -25
  11. package/dist/defaults/index.js +94 -68
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +72 -16
  14. package/dist/execution/index.js +151 -36
  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-SulMTowG.d.ts → goal-preamble-CrYjmdw4.d.ts} +9 -9
  20. package/dist/{goal-store-CABDwdFE.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-IEuxQd-E.d.ts → index-BfaS-f_m.d.ts} +2 -2
  25. package/dist/{index-DtCVWel4.d.ts → index-CtQnmkaS.d.ts} +8 -8
  26. package/dist/{index-Bms0m4oy.d.ts → index-gCv830d7.d.ts} +5 -5
  27. package/dist/index.d.ts +46 -42
  28. package/dist/index.js +2955 -1498
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/infrastructure/index.js +45 -18
  32. package/dist/infrastructure/index.js.map +1 -1
  33. package/dist/kernel/index.d.ts +10 -9
  34. package/dist/{pipeline-BfD2k1rT.d.ts → mailbox-types-Ct2hJq0P.d.ts} +1 -244
  35. package/dist/{mcp-servers-C2cBTxUR.d.ts → mcp-servers-HT3Fi7Bl.d.ts} +10 -4
  36. package/dist/models/index.d.ts +5 -5
  37. package/dist/models/index.js +3 -2
  38. package/dist/models/index.js.map +1 -1
  39. package/dist/{models-registry-BqGZNJQ-.d.ts → models-registry-Bvcl3Vaa.d.ts} +1 -1
  40. package/dist/{multi-agent-coordinator-B8R43uPz.d.ts → multi-agent-coordinator-BACjsmkC.d.ts} +1 -1
  41. package/dist/{null-fleet-bus-CnXa5oTH.d.ts → null-fleet-bus-DA7fvhUg.d.ts} +6 -6
  42. package/dist/observability/index.d.ts +2 -2
  43. package/dist/{parallel-eternal-engine-DdNnw9BQ.d.ts → parallel-eternal-engine-Ci71gYu_.d.ts} +9 -15
  44. package/dist/{path-resolver-COIMLCQL.d.ts → path-resolver-O1IJnmKE.d.ts} +4 -3
  45. package/dist/{permission-B75JAi3-.d.ts → permission-Bd-57Lbl.d.ts} +1 -1
  46. package/dist/{permission-policy-DlR9eJAM.d.ts → permission-policy-uNXC6Kge.d.ts} +2 -3
  47. package/dist/pipeline-BDNvENyV.d.ts +245 -0
  48. package/dist/{plan-templates-DSIKCXZN.d.ts → plan-templates-EMsalEtN.d.ts} +5 -5
  49. package/dist/{provider-model-resolve-BNRsNuJx.d.ts → provider-model-resolve-CEb9x886.d.ts} +3 -3
  50. package/dist/{provider-runner-CX7iIvox.d.ts → provider-runner-DWJbpo70.d.ts} +3 -3
  51. package/dist/{retry-policy-BilV1ujH.d.ts → retry-policy-C3s_lvdK.d.ts} +1 -1
  52. package/dist/sdd/index.d.ts +9 -8
  53. package/dist/sdd/index.js +32 -2
  54. package/dist/sdd/index.js.map +1 -1
  55. package/dist/{secret-vault-gkvEZZfE.d.ts → secret-vault-Cgduf5xL.d.ts} +1 -1
  56. package/dist/security/index.d.ts +5 -5
  57. package/dist/security/index.js +39 -29
  58. package/dist/security/index.js.map +1 -1
  59. package/dist/{selector-Bc7eWtT3.d.ts → selector-47LBnBVk.d.ts} +1 -1
  60. package/dist/{session-event-bridge-D-araDEz.d.ts → session-event-bridge-Cw7oqmW2.d.ts} +1 -1
  61. package/dist/{session-reader-D7Dapswh.d.ts → session-reader-DD4v2Obw.d.ts} +1 -1
  62. package/dist/storage/index.d.ts +14 -12
  63. package/dist/storage/index.js +63 -36
  64. package/dist/storage/index.js.map +1 -1
  65. package/dist/tools/index.d.ts +2 -2
  66. package/dist/tools/index.js +166 -31
  67. package/dist/tools/index.js.map +1 -1
  68. package/dist/types/index.d.ts +20 -19
  69. package/dist/types/index.js +68 -51
  70. package/dist/types/index.js.map +1 -1
  71. package/dist/utils/index.d.ts +22 -3
  72. package/dist/utils/index.js +139 -1
  73. package/dist/utils/index.js.map +1 -1
  74. package/package.json +5 -1
@@ -3765,7 +3765,27 @@ Working rules:
3765
3765
  id: "devops",
3766
3766
  name: "DevOps",
3767
3767
  role: "devops",
3768
- 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
+ ],
3769
3789
  prompt: `You are the DevOps agent. Your job is CI/CD, containerization, and
3770
3790
  deployment configuration: make builds reproducible and deploys safe.
3771
3791
 
@@ -3774,6 +3794,7 @@ Scope:
3774
3794
  - Write Dockerfiles/compose and optimize image size and layer caching
3775
3795
  - Configure deployment (env, secrets handling, health checks, rollback)
3776
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
3777
3798
 
3778
3799
  Input format you accept:
3779
3800
  { "task": "ci | container | deploy | fix-pipeline", "platform": "github-actions | gitlab | docker | k8s", "target": "<what>" }
@@ -3788,7 +3809,8 @@ Working rules:
3788
3809
  - Never hardcode secrets in config; reference the secret store
3789
3810
  - Pin versions for reproducible builds; avoid floating :latest
3790
3811
  - Every deploy path needs a rollback and a health check
3791
- - 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`
3792
3814
  },
3793
3815
  budget: MEDIUM_BUDGET,
3794
3816
  capability: {
@@ -3805,6 +3827,13 @@ Working rules:
3805
3827
  "kubernetes",
3806
3828
  "k8s",
3807
3829
  "deploy",
3830
+ "ssh",
3831
+ "remote ssh",
3832
+ "remote server",
3833
+ "sftp",
3834
+ "tunnel",
3835
+ "bastion",
3836
+ "jump host",
3808
3837
  "github actions",
3809
3838
  "container"
3810
3839
  ]
@@ -6800,6 +6829,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
6800
6829
  subagentId: result.subagentId,
6801
6830
  taskId: result.taskId,
6802
6831
  status: result.status,
6832
+ result: result.result,
6803
6833
  iterations: result.iterations,
6804
6834
  toolCalls: result.toolCalls,
6805
6835
  durationMs: result.durationMs
@@ -8904,6 +8934,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
8904
8934
  const cached = this._loadCache.get(id);
8905
8935
  if (cached && cached.mtimeMs === stat6.mtimeMs && cached.size === stat6.size) {
8906
8936
  cacheHit = true;
8937
+ this._loadCache.delete(id);
8938
+ this._loadCache.set(id, cached);
8907
8939
  return cached.data;
8908
8940
  }
8909
8941
  const raw = await fsp6.readFile(file, "utf8");
@@ -10400,30 +10432,18 @@ var DefaultMailbox = class {
10400
10432
  async query(q) {
10401
10433
  const all = await this._readAll();
10402
10434
  const limit = q.limit ?? 50;
10403
- let filtered = all;
10404
- if (q.to !== void 0) {
10405
- filtered = filtered.filter((m) => m.to === q.to || m.to === "*");
10406
- }
10407
- if (q.from !== void 0) {
10408
- filtered = filtered.filter((m) => m.from === q.from);
10409
- }
10410
- if (q.unreadBy !== void 0) {
10411
- filtered = filtered.filter((m) => !(q.unreadBy in m.readBy));
10412
- }
10413
- if (q.incompleteOnly) {
10414
- filtered = filtered.filter((m) => !m.completed);
10415
- }
10416
- if (q.type !== void 0) {
10417
- filtered = filtered.filter((m) => m.type === q.type);
10418
- }
10419
- if (q.minPriority !== void 0) {
10420
- const order = { low: 0, normal: 1, high: 2 };
10421
- const min = order[q.minPriority];
10422
- filtered = filtered.filter((m) => (order[m.priority] ?? 1) >= min);
10423
- }
10424
- if (q.since !== void 0) {
10425
- const since = q.since;
10426
- 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);
10427
10447
  }
10428
10448
  filtered.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10429
10449
  return filtered.slice(0, limit);
@@ -10744,6 +10764,8 @@ var GlobalMailbox = class {
10744
10764
  clientRegistryPath;
10745
10765
  /** Optional event bus for emitting agent registration/heartbeat events. */
10746
10766
  _events;
10767
+ /** Optional HQ publisher for cross-project command-center telemetry. */
10768
+ _hqPublisher;
10747
10769
  /**
10748
10770
  * Local cache of the agent registry to avoid re-reading on every call.
10749
10771
  * Time-bounded: the registry file is shared ACROSS PROCESSES (that's the
@@ -10781,12 +10803,28 @@ var GlobalMailbox = class {
10781
10803
  /**
10782
10804
  * @param projectDir — `~/.wrongstack/projects/<slug>/`
10783
10805
  * @param events — optional EventBus for real-time TUI/WebUI notifications
10806
+ * @param hqPublisher — optional HQ publisher for cross-project telemetry
10784
10807
  */
10785
- constructor(projectDir, events) {
10808
+ constructor(projectDir, events, hqPublisher) {
10786
10809
  this.messagePath = path5.join(projectDir, MAILBOX_FILE2);
10787
10810
  this.registryPath = path5.join(projectDir, "_mailbox.registry.json");
10788
10811
  this.clientRegistryPath = path5.join(projectDir, CLIENT_REGISTRY_FILE);
10789
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
+ });
10790
10828
  }
10791
10829
  // ── Messages ────────────────────────────────────────────────────────────
10792
10830
  async send(input) {
@@ -10813,6 +10851,8 @@ var GlobalMailbox = class {
10813
10851
  await fsp6.appendFile(this.messagePath, line, "utf8");
10814
10852
  this._pushToCache(msg);
10815
10853
  });
10854
+ this.publishHqMailboxEvent({ mailboxId: this.hqMailboxId, action: "message.sent", message: msg });
10855
+ this.publishHqMailboxSnapshot();
10816
10856
  return msg;
10817
10857
  }
10818
10858
  async query(q) {
@@ -10879,6 +10919,14 @@ var GlobalMailbox = class {
10879
10919
  cacheSnapshot = all;
10880
10920
  });
10881
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();
10882
10930
  return updated;
10883
10931
  }
10884
10932
  async unreadCount(forAgentId) {
@@ -10926,6 +10974,25 @@ var GlobalMailbox = class {
10926
10974
  role: input.role,
10927
10975
  source: input.source
10928
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();
10929
10996
  }
10930
10997
  async heartbeat(input) {
10931
10998
  const last = this._lastHeartbeat.get(input.agentId) ?? 0;
@@ -10956,6 +11023,12 @@ var GlobalMailbox = class {
10956
11023
  currentTool: input.currentTool,
10957
11024
  currentTask: input.currentTask
10958
11025
  });
11026
+ this.publishHqMailboxEvent({
11027
+ mailboxId: this.hqMailboxId,
11028
+ action: "agent.heartbeat",
11029
+ summary: input.agentId
11030
+ });
11031
+ this.publishHqMailboxSnapshot();
10959
11032
  }
10960
11033
  async getAgentStatuses() {
10961
11034
  await this._ensureRegistry();
@@ -11768,13 +11841,13 @@ function makeDependencyWatcherConfig(opts) {
11768
11841
  const globPatterns = patterns.filter((p) => p.includes("*"));
11769
11842
  const plainPatterns = patterns.filter((p) => !p.includes("*"));
11770
11843
  function matchesPattern(filePath) {
11771
- const basename5 = filePath.split("/").pop()?.split("\\").pop() ?? "";
11772
- if (plainPatterns.includes(basename5)) return true;
11844
+ const basename6 = filePath.split("/").pop()?.split("\\").pop() ?? "";
11845
+ if (plainPatterns.includes(basename6)) return true;
11773
11846
  for (const gp of globPatterns) {
11774
11847
  const regex = new RegExp(
11775
11848
  "^" + gp.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$"
11776
11849
  );
11777
- if (regex.test(basename5)) return true;
11850
+ if (regex.test(basename6)) return true;
11778
11851
  }
11779
11852
  return false;
11780
11853
  }
@@ -12750,12 +12823,12 @@ var ConsensusProtocol = class {
12750
12823
  * Initiate a vote on a proposed change. Updates the change node's status
12751
12824
  * to 'proposed' and notifies eligible voters via FleetBus.
12752
12825
  */
12753
- initiateVote(changeId) {
12826
+ async initiateVote(changeId) {
12754
12827
  const change = this.graph.get(changeId);
12755
12828
  if (!change || change.type !== "change") {
12756
12829
  throw new Error(`ConsensusProtocol: no change found with id "${changeId}"`);
12757
12830
  }
12758
- this.graph.update(changeId, { status: "proposed", votes: [] });
12831
+ await this.graph.update(changeId, { status: "proposed", votes: [] });
12759
12832
  const eligible = this._eligibleVoters(change);
12760
12833
  this._notifyVoters(change, eligible, "vote_initiated");
12761
12834
  }
@@ -12763,7 +12836,7 @@ var ConsensusProtocol = class {
12763
12836
  * Cast a vote. Updates the change node in the graph and re-evaluates
12764
12837
  * consensus. If the vote triggers a resolution, updates the change status.
12765
12838
  */
12766
- castVote(changeId, voterId, value, rationale) {
12839
+ async castVote(changeId, voterId, value, rationale) {
12767
12840
  const change = this.graph.get(changeId);
12768
12841
  if (!change || change.type !== "change") {
12769
12842
  throw new Error(`ConsensusProtocol: no change found for "${changeId}"`);
@@ -12786,7 +12859,7 @@ var ConsensusProtocol = class {
12786
12859
  const existingIdx = change.votes.findIndex((v) => v.agentId === voterId);
12787
12860
  const newVotes = existingIdx >= 0 ? change.votes.with(existingIdx, vote) : [...change.votes, vote];
12788
12861
  const result = this._resolve(changeId, newVotes, eligible);
12789
- this.graph.update(changeId, {
12862
+ await this.graph.update(changeId, {
12790
12863
  votes: newVotes,
12791
12864
  ...result.outcome !== "pending" ? { status: this._toChangeStatus(result.outcome) } : {}
12792
12865
  });
@@ -12797,13 +12870,13 @@ var ConsensusProtocol = class {
12797
12870
  * Resolve the current vote without waiting for all eligible voters.
12798
12871
  * Useful when a timeout fires or an agent decides to finalize early.
12799
12872
  */
12800
- resolveNow(changeId) {
12873
+ async resolveNow(changeId) {
12801
12874
  const change = this.graph.get(changeId);
12802
12875
  if (!change) throw new Error(`ConsensusProtocol: unknown change "${changeId}"`);
12803
12876
  const eligible = this._eligibleVoters(change);
12804
12877
  const result = this._resolve(changeId, change.votes, eligible);
12805
12878
  if (result.outcome !== "pending") {
12806
- this.graph.update(changeId, { status: this._toChangeStatus(result.outcome) });
12879
+ await this.graph.update(changeId, { status: this._toChangeStatus(result.outcome) });
12807
12880
  this._notifyVoters(change, eligible, "vote_resolved", { result });
12808
12881
  }
12809
12882
  return result;
@@ -13726,7 +13799,8 @@ Priority: ${goal.priority}`,
13726
13799
  this.agentTaskCount(agentId, -1);
13727
13800
  await this.graph.update(taskId, {
13728
13801
  status: "done",
13729
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13802
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13803
+ ..._result !== void 0 ? { result: _result } : {}
13730
13804
  });
13731
13805
  this.bidRetryCounts.delete(taskId);
13732
13806
  this.pendingBids.delete(taskId);
@@ -13971,7 +14045,7 @@ ${goal.description}`,
13971
14045
  this.agentTaskCounts.set(agentId, next);
13972
14046
  }
13973
14047
  };
13974
- var AutonomousCoordinator = class {
14048
+ var AutonomousCoordinator = class _AutonomousCoordinator {
13975
14049
  graph;
13976
14050
  dag;
13977
14051
  auction;
@@ -13987,6 +14061,8 @@ var AutonomousCoordinator = class {
13987
14061
  onCoordinatorEvent;
13988
14062
  running = false;
13989
14063
  iterationCount = 0;
14064
+ lastSyncAt = 0;
14065
+ static SYNC_INTERVAL_MS = 5e3;
13990
14066
  /** Tasks already handled by _onSubagentTerminated (to avoid double goal:failed on fleet event). */
13991
14067
  _handledBySubagent = /* @__PURE__ */ new Set();
13992
14068
  /** FleetBus subscription disposers, detached in dispose(). */
@@ -14043,7 +14119,7 @@ var AutonomousCoordinator = class {
14043
14119
  const taskId = payload?.taskId;
14044
14120
  if (!taskId || this._handledBySubagent.has(taskId)) return;
14045
14121
  this._handledBySubagent.add(taskId);
14046
- this._emit({ type: "goal:failed", goalId: taskId, text: payload?.error ?? "Task failed" });
14122
+ this._recordTaskFailed(taskId, payload?.error ?? "Task failed");
14047
14123
  });
14048
14124
  if (offFailed) this.unsubs.push(offFailed);
14049
14125
  this._emit({ type: "coordinator:mode", mode: this.fleet ? "fleet" : "standalone" });
@@ -14062,6 +14138,7 @@ var AutonomousCoordinator = class {
14062
14138
  const maxCost = opts.maxCostUsd;
14063
14139
  try {
14064
14140
  await this.graph.load();
14141
+ this._rebuildDagFromGraph();
14065
14142
  const goalConfigs = await this._decomposeGoal(goal);
14066
14143
  for (const g of goalConfigs) {
14067
14144
  const goalId = await this.auction.publishTask(g);
@@ -14069,23 +14146,49 @@ var AutonomousCoordinator = class {
14069
14146
  this._emit({ type: "goal:added", goalId, title: g.title, text: g.description });
14070
14147
  }
14071
14148
  while (this.running) {
14149
+ if (this.dag.getRunning().length > 0 && this.auction.getPendingTasks().length === 0) {
14150
+ await this._waitForDagProgress(1e3);
14151
+ continue;
14152
+ }
14072
14153
  this.iterationCount++;
14154
+ await this._maybeSyncFromGraph();
14073
14155
  if (this.iterationCount >= maxIterations) break;
14074
14156
  if (maxCost !== void 0) {
14075
14157
  const cost = this.fleetManager?.snapshot()?.total?.cost ?? 0;
14076
14158
  if (cost >= maxCost) break;
14077
14159
  }
14078
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
+ }
14079
14182
  const decision = await this.brain.decideAuto({
14080
14183
  id: randomUUID(),
14081
14184
  source: "system",
14082
14185
  decisionType: "prioritize_goals",
14083
- 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(", ")}`,
14084
14187
  context: {
14085
- goals: this.auction.getPendingTasks(),
14188
+ goals: dispatchable,
14086
14189
  fleetStatus: this._fleetStatus()
14087
14190
  },
14088
- options: this._goalToOptions(this.auction.getPendingTasks()),
14191
+ options: this._goalToOptions(dispatchable),
14089
14192
  risk: "medium",
14090
14193
  requiresConsensus: false
14091
14194
  });
@@ -14132,6 +14235,55 @@ var AutonomousCoordinator = class {
14132
14235
  this.running = false;
14133
14236
  console.error(`[AutonomousCoordinator] stop signal received \u2014 shutting down (iteration ${this.iterationCount})`);
14134
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
+ }
14135
14287
  /**
14136
14288
  * Tear down the coordinator for good: stop the loop and detach all FleetBus
14137
14289
  * subscriptions (this coordinator's + the auctioneer's) plus any open bid
@@ -14228,6 +14380,65 @@ ${input.detail}`
14228
14380
  return goal;
14229
14381
  }
14230
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
+ }
14231
14442
  async _decomposeGoal(goalText) {
14232
14443
  const category = this._inferCategory(goalText);
14233
14444
  const subGoals = [];
@@ -14264,13 +14475,7 @@ ${input.detail}`
14264
14475
  const goalNode = this.graph.get(goalId);
14265
14476
  if (!goalNode) return;
14266
14477
  const title = goalNode.title || dagNode.description;
14267
- const taskId = await this.auction.publishTask({
14268
- title,
14269
- description: goalNode.description,
14270
- priority: this._dagPriorityToGoal(dagNode.priority),
14271
- tags: dagNode.tags
14272
- });
14273
- this._emit({ type: "task:ready", goalId, taskId, title });
14478
+ this._emit({ type: "task:ready", goalId, taskId: goalId, title });
14274
14479
  if (this.director) {
14275
14480
  const config = {
14276
14481
  name: `worker-${goalId.slice(0, 8)}`,
@@ -14280,7 +14485,7 @@ ${input.detail}`
14280
14485
  // 10 minutes per goal
14281
14486
  };
14282
14487
  const subagentId = await this.director.spawn(config);
14283
- await this.auction.claim(taskId, subagentId, config.name);
14488
+ await this.auction.claim(goalId, subagentId, config.name);
14284
14489
  await this.director.assign({
14285
14490
  id: goalId,
14286
14491
  subagentId,
@@ -14288,6 +14493,78 @@ ${input.detail}`
14288
14493
  });
14289
14494
  }
14290
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
+ }
14291
14568
  async _handlePendingChange(change) {
14292
14569
  const result = this.consensus.getStatus(change.id);
14293
14570
  if (result?.outcome !== "pending") return;
@@ -14328,16 +14605,15 @@ ${input.detail}`
14328
14605
  _onSubagentTerminated(e) {
14329
14606
  const payload = e.payload;
14330
14607
  const subagentId = payload?.subagentId ?? e.subagentId;
14331
- const stopReason = payload?.stopReason ?? (payload?.status === "ok" ? "end_turn" : payload?.status ?? "unknown");
14332
- 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);
14333
14611
  for (const task of tasks) {
14334
14612
  this._handledBySubagent.add(task.id);
14335
- if (stopReason === "end_turn") {
14336
- void this.auction.complete(task.id, "Subagent completed successfully");
14337
- 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));
14338
14615
  } else {
14339
- void this.auction.fail(task.id, `Subagent terminated: ${stopReason}`);
14340
- this._emit({ type: "goal:failed", goalId: task.id, text: `Subagent terminated: ${stopReason}` });
14616
+ void this._failTask(task.id, `Subagent terminated: ${rawStatus}`);
14341
14617
  }
14342
14618
  }
14343
14619
  }
@@ -14372,12 +14648,6 @@ ${input.detail}`
14372
14648
  _optionToGoal(optionId) {
14373
14649
  return this.graph.get(optionId);
14374
14650
  }
14375
- _dagPriorityToGoal(p) {
14376
- if (p <= 1) return "critical";
14377
- if (p <= 2) return "high";
14378
- if (p <= 4) return "medium";
14379
- return "low";
14380
- }
14381
14651
  async _mailboxBroadcast(msg) {
14382
14652
  if (!this.mailbox) return;
14383
14653
  try {