@adhdev/daemon-core 0.9.76 → 0.9.77-rc.10

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.
package/dist/index.mjs CHANGED
@@ -38,7 +38,8 @@ var init_repo_mesh_types = __esm({
38
38
  dirtyWorkspaceBehavior: "warn",
39
39
  maxParallelTasks: 2,
40
40
  spawnedSessionVisibility: "visible",
41
- sessionCleanupOnNodeRemove: "preserve"
41
+ sessionCleanupOnNodeRemove: "preserve",
42
+ maxTaskRetries: 1
42
43
  };
43
44
  }
44
45
  });
@@ -668,12 +669,13 @@ function buildRulesSection(coordinatorCliType) {
668
669
  return `## Rules
669
670
 
670
671
  - **Minimize coordinator context.** The coordinator's job is routing, not implementing. Do not read source files, run commands, or analyze code directly \u2014 delegate all of that to node agents. Your context should stay lean.
671
- - **Delegate analysis too.** If you need to understand a bug or explore the codebase, send that investigation as a task to a node. Do not do it yourself.
672
+ - **Delegate analysis too.** If you need to understand a bug or explore the codebase, send that investigation as a task to the queue or a node. Do not do it yourself.
672
673
  - **Respect explicit provider requests.** If the user names an agent/provider, pass the matching provider type to \`mesh_launch_session\`: Hermes \u2192 \`hermes-cli\`, Claude Code/Claude \u2192 \`claude-cli\`, Codex \u2192 \`codex-cli\`, Gemini \u2192 \`gemini-cli\`. Never substitute \`claude-cli\` just because the coordinator itself is Claude Code.
673
- - **Front-load the task message.** When calling \`mesh_send_task\`, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
674
+ - **Front-load the task message.** When calling \`mesh_enqueue_task\` or \`mesh_send_task\`, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
674
675
  - **Don't inspect code.** Treat delegated agent summaries as self-reports, not verification. Verify side effects via \`mesh_git_status\` (including related repo freshness when configured), not by reading source files.
675
676
  - **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed. Never launch a duplicate session or second worker solely because \`mesh_read_chat\` has no final assistant message while the delegated session is still showing tool/terminal activity.
676
- - **Handle failures gracefully.** If a task fails, read the chat to understand why, then retry or reassign.
677
+ - **Handle failures with context.** If a task fails, check \`mesh_task_history\` first to see if this task was attempted before and how it failed. Read the chat to understand why, then decide: retry on the same node, reassign to a different node, or escalate to the user.
678
+ - **Check history before starting.** At the beginning of a coordination session, call \`mesh_task_history\` to understand what was previously delegated and its outcomes. This prevents duplicate work and informs recovery decisions.
677
679
  - **Keep the user informed.** Report progress after each delegation round \u2014 one or two sentences, not a narration.
678
680
  - **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
679
681
  - **Never fabricate tool results.** Always call the actual tool; never pretend you did.
@@ -694,6 +696,7 @@ var init_coordinator_prompt = __esm({
694
696
  | \`mesh_launch_session\` | Start a new agent session on a node |
695
697
  | \`mesh_send_task\` | Send a task (natural language) to a running agent |
696
698
  | \`mesh_read_chat\` | Read an agent's recent messages to check progress |
699
+ | \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
697
700
  | \`mesh_git_status\` | Check git status on a specific node |
698
701
  | \`mesh_checkpoint\` | Create a git checkpoint on a node |
699
702
  | \`mesh_approve\` | Approve/reject a pending agent action |
@@ -704,25 +707,372 @@ var init_coordinator_prompt = __esm({
704
707
  Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
705
708
  WORKFLOW_SECTION = `## Orchestration Workflow
706
709
 
707
- 1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available.
708
- 2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist.
709
- 3. **Delegate** \u2014 For each task:
710
- a. Pick the best node (consider: health, dirty state, current workload).
711
- b. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
712
- c. If no session exists, call \`mesh_launch_session\` to start one.
713
- d. Call \`mesh_send_task\` with a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
714
- 4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly just because the delegated session has not produced a final assistant message yet; tool/terminal activity means work may still be in progress. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session; wait for the completion callback/status event instead unless you are debugging a real stall. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal, an explicit user status request, or a real timeout/stall. Handle approvals via \`mesh_approve\`.
710
+ 1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available. Check \`mesh_task_history\` to understand what has already been done in this mesh \u2014 previous delegations, completions, and failures.
711
+ 2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign.
712
+ 3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
713
+ a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
714
+ b. **Node Preparation**: Call \`mesh_launch_session\` to ensure enough agent sessions are active to handle the queue. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
715
+ c. **Targeted Tasks**: Use \`mesh_send_task\` only when you need to bypass the queue and force a specific node to execute a task immediately.
716
+ d. Always provide a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
717
+ 4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Use \`mesh_view_queue\` to see the status of all pending, assigned, completed, and failed tasks. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal. Handle approvals via \`mesh_approve\`.
715
718
  5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
716
719
  6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
717
720
  7. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
718
- 8. **Report** \u2014 Summarize what was done, what changed, and any issues.`;
721
+ 8. **Report** \u2014 Summarize what was done, what changed, and any issues.
722
+
723
+ ## Failure Recovery
724
+
725
+ When a node agent stops unexpectedly, the daemon automatically enriches the system message with **Recovery Context** that includes:
726
+ - The number of consecutive failures on that node
727
+ - The original task message (if recorded in the ledger)
728
+ - A recommendation: **retry**, **reassign**, or **escalate**
729
+
730
+ Follow these recovery rules:
731
+ 1. **If "Retry recommended"**: Re-launch the session on the same node (\`mesh_launch_session\`), then resend the original task (\`mesh_send_task\`). The system message includes the original task text.
732
+ 2. **If "Max retries exceeded"**: Do NOT retry on the same node. Either reassign the task to a different node, or inform the user that the task requires manual intervention.
733
+ 3. **If no recovery context**: The stop may be intentional (normal completion). Use \`mesh_read_chat\` once to verify, then move on.
734
+ 4. **Always record what happened**: After handling a failure, briefly note the outcome in your report to the user.`;
735
+ }
736
+ });
737
+
738
+ // src/mesh/mesh-ledger.ts
739
+ var mesh_ledger_exports = {};
740
+ __export(mesh_ledger_exports, {
741
+ appendLedgerEntry: () => appendLedgerEntry,
742
+ appendRemoteLedgerEntries: () => appendRemoteLedgerEntries,
743
+ getLedgerDir: () => getLedgerDir,
744
+ getLedgerSummary: () => getLedgerSummary,
745
+ getSessionRecoveryContext: () => getSessionRecoveryContext,
746
+ meshLedgerEvents: () => meshLedgerEvents,
747
+ readLedgerEntries: () => readLedgerEntries
748
+ });
749
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, appendFileSync, statSync as statSync2, renameSync } from "fs";
750
+ import { join as join5 } from "path";
751
+ import { randomUUID as randomUUID4 } from "crypto";
752
+ import { EventEmitter } from "events";
753
+ function getLedgerDir() {
754
+ const dir = join5(getConfigDir(), LEDGER_DIR_NAME);
755
+ if (!existsSync5(dir)) {
756
+ mkdirSync3(dir, { recursive: true, mode: 448 });
757
+ }
758
+ return dir;
759
+ }
760
+ function getLedgerPath(meshId) {
761
+ const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
762
+ return join5(getLedgerDir(), `${safe}.jsonl`);
763
+ }
764
+ function getRotatedPath(meshId, index) {
765
+ const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
766
+ return join5(getLedgerDir(), `${safe}.${index}.jsonl`);
767
+ }
768
+ function appendLedgerEntry(meshId, partial) {
769
+ const entry = {
770
+ id: randomUUID4(),
771
+ meshId,
772
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
773
+ ...partial
774
+ };
775
+ const filePath = getLedgerPath(meshId);
776
+ if (existsSync5(filePath)) {
777
+ try {
778
+ const stat2 = statSync2(filePath);
779
+ if (stat2.size >= MAX_FILE_SIZE_BYTES) {
780
+ rotateLedgerFile(meshId, filePath);
781
+ }
782
+ } catch {
783
+ }
784
+ }
785
+ try {
786
+ const line = JSON.stringify(entry) + "\n";
787
+ appendFileSync(filePath, line, { encoding: "utf-8", mode: 384 });
788
+ meshLedgerEvents.emit("append", meshId, entry);
789
+ return entry;
790
+ } catch (e) {
791
+ throw new Error(`Failed to append to ledger for mesh ${meshId}: ${e.message}`);
792
+ }
793
+ }
794
+ function appendRemoteLedgerEntries(meshId, entries) {
795
+ if (entries.length === 0) return;
796
+ const ledgerPath = getLedgerPath(meshId);
797
+ const existing = new Set(readLedgerEntries(meshId).map((e) => e.id));
798
+ const newEntries = entries.filter((e) => !existing.has(e.id));
799
+ if (newEntries.length === 0) return;
800
+ try {
801
+ const lines = newEntries.map((e) => JSON.stringify(e)).join("\n") + "\n";
802
+ appendFileSync(ledgerPath, lines, { encoding: "utf-8", mode: 384 });
803
+ } catch (e) {
804
+ throw new Error(`Failed to append remote ledger entries for mesh ${meshId}: ${e.message}`);
805
+ }
806
+ }
807
+ function readLedgerEntries(meshId, opts) {
808
+ const filePath = getLedgerPath(meshId);
809
+ if (!existsSync5(filePath)) return [];
810
+ let content;
811
+ try {
812
+ content = readFileSync3(filePath, "utf-8");
813
+ } catch {
814
+ return [];
815
+ }
816
+ const lines = content.split("\n").filter((line) => line.trim());
817
+ let entries = [];
818
+ for (const line of lines) {
819
+ try {
820
+ const entry = JSON.parse(line);
821
+ if (!entry.id || !entry.kind) continue;
822
+ entries.push(entry);
823
+ } catch {
824
+ }
825
+ }
826
+ if (opts?.since) {
827
+ const sinceDate = new Date(opts.since).getTime();
828
+ if (!isNaN(sinceDate)) {
829
+ entries = entries.filter((e) => new Date(e.timestamp).getTime() >= sinceDate);
830
+ }
831
+ }
832
+ if (opts?.kind?.length) {
833
+ const kindSet = new Set(opts.kind);
834
+ entries = entries.filter((e) => kindSet.has(e.kind));
835
+ }
836
+ if (opts?.tail && opts.tail > 0 && entries.length > opts.tail) {
837
+ entries = entries.slice(-opts.tail);
838
+ }
839
+ return entries;
840
+ }
841
+ function getLedgerSummary(meshId) {
842
+ const entries = readLedgerEntries(meshId);
843
+ const now = Date.now();
844
+ const recentFailureCutoff = now - RECENT_FAILURE_WINDOW_MS;
845
+ const summary = {
846
+ meshId,
847
+ totalEntries: entries.length,
848
+ taskDispatched: 0,
849
+ taskCompleted: 0,
850
+ taskFailed: 0,
851
+ taskStalled: 0,
852
+ sessionLaunched: 0,
853
+ checkpointCreated: 0,
854
+ lastActivityAt: null,
855
+ recentFailures: 0
856
+ };
857
+ for (const entry of entries) {
858
+ switch (entry.kind) {
859
+ case "task_dispatched":
860
+ summary.taskDispatched++;
861
+ break;
862
+ case "task_completed":
863
+ summary.taskCompleted++;
864
+ break;
865
+ case "task_failed": {
866
+ summary.taskFailed++;
867
+ if (new Date(entry.timestamp).getTime() >= recentFailureCutoff) {
868
+ summary.recentFailures++;
869
+ }
870
+ break;
871
+ }
872
+ case "task_stalled":
873
+ summary.taskStalled++;
874
+ break;
875
+ case "session_launched":
876
+ summary.sessionLaunched++;
877
+ break;
878
+ case "checkpoint_created":
879
+ summary.checkpointCreated++;
880
+ break;
881
+ }
882
+ }
883
+ if (entries.length > 0) {
884
+ summary.lastActivityAt = entries[entries.length - 1].timestamp;
885
+ }
886
+ return summary;
887
+ }
888
+ function getSessionRecoveryContext(meshId, opts) {
889
+ const maxRetries = opts.maxRetries ?? 1;
890
+ const entries = readLedgerEntries(meshId);
891
+ let lastDispatch = null;
892
+ for (let i = entries.length - 1; i >= 0; i--) {
893
+ const e = entries[i];
894
+ if (e.kind !== "task_dispatched") continue;
895
+ if (opts.sessionId && e.sessionId === opts.sessionId) {
896
+ lastDispatch = e;
897
+ break;
898
+ }
899
+ if (opts.nodeId && e.nodeId === opts.nodeId) {
900
+ lastDispatch = e;
901
+ break;
902
+ }
903
+ }
904
+ const lastTaskMessage = typeof lastDispatch?.payload?.message === "string" ? lastDispatch.payload.message : null;
905
+ const now = Date.now();
906
+ const recentWindow = now - RECENT_FAILURE_WINDOW_MS;
907
+ let consecutiveNodeFailures = 0;
908
+ for (let i = entries.length - 1; i >= 0; i--) {
909
+ const e = entries[i];
910
+ if (new Date(e.timestamp).getTime() < recentWindow) break;
911
+ if (opts.nodeId && e.nodeId !== opts.nodeId) continue;
912
+ if (e.kind === "task_failed") {
913
+ consecutiveNodeFailures++;
914
+ } else if (e.kind === "task_completed" || e.kind === "task_dispatched") {
915
+ break;
916
+ }
917
+ }
918
+ let taskAttemptCount = 0;
919
+ if (lastTaskMessage) {
920
+ const prefix = lastTaskMessage.slice(0, 200);
921
+ for (const e of entries) {
922
+ if (e.kind === "task_dispatched" && typeof e.payload?.message === "string") {
923
+ if (e.payload.message.startsWith(prefix)) {
924
+ taskAttemptCount++;
925
+ }
926
+ }
927
+ }
928
+ }
929
+ const retryRecommended = consecutiveNodeFailures <= maxRetries;
930
+ let advice;
931
+ if (consecutiveNodeFailures === 0) {
932
+ advice = "No recent failures detected. This may be a normal stop.";
933
+ } else if (retryRecommended) {
934
+ const remaining = maxRetries - consecutiveNodeFailures + 1;
935
+ advice = `Retry recommended (${consecutiveNodeFailures}/${maxRetries + 1} attempts used, ${remaining} remaining). ` + (lastTaskMessage ? `Re-launch the session and resend the original task.` : `Re-launch the session. Original task message not found in ledger.`);
936
+ } else {
937
+ advice = `Max retries exceeded (${consecutiveNodeFailures} consecutive failures). Consider: (1) reassigning to a different node, (2) simplifying the task, or (3) escalating to the user.`;
938
+ }
939
+ return {
940
+ lastTaskMessage,
941
+ failedNodeId: opts.nodeId || null,
942
+ failedSessionId: opts.sessionId || null,
943
+ failedProviderType: null,
944
+ // filled by caller if available
945
+ consecutiveNodeFailures,
946
+ taskAttemptCount,
947
+ retryRecommended,
948
+ advice
949
+ };
950
+ }
951
+ function rotateLedgerFile(meshId, currentPath) {
952
+ let index = 1;
953
+ while (existsSync5(getRotatedPath(meshId, index))) {
954
+ index++;
955
+ if (index > 10) break;
956
+ }
957
+ if (index > 10) index = 10;
958
+ try {
959
+ renameSync(currentPath, getRotatedPath(meshId, index));
960
+ } catch {
961
+ }
962
+ }
963
+ var LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents;
964
+ var init_mesh_ledger = __esm({
965
+ "src/mesh/mesh-ledger.ts"() {
966
+ "use strict";
967
+ init_config();
968
+ LEDGER_DIR_NAME = "mesh-ledger";
969
+ MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
970
+ RECENT_FAILURE_WINDOW_MS = 30 * 60 * 1e3;
971
+ meshLedgerEvents = new EventEmitter();
972
+ }
973
+ });
974
+
975
+ // src/mesh/mesh-work-queue.ts
976
+ import { existsSync as existsSync6, writeFileSync as writeFileSync3, readFileSync as readFileSync4 } from "fs";
977
+ import { join as join6 } from "path";
978
+ import { randomUUID as randomUUID5 } from "crypto";
979
+ function getQueuePath(meshId) {
980
+ const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
981
+ return join6(getLedgerDir(), `${safe}.queue.json`);
982
+ }
983
+ function readQueue(meshId) {
984
+ const path28 = getQueuePath(meshId);
985
+ if (!existsSync6(path28)) return [];
986
+ try {
987
+ const content = readFileSync4(path28, "utf-8");
988
+ return JSON.parse(content);
989
+ } catch {
990
+ return [];
991
+ }
992
+ }
993
+ function writeQueue(meshId, queue) {
994
+ const path28 = getQueuePath(meshId);
995
+ writeFileSync3(path28, JSON.stringify(queue, null, 2), "utf-8");
996
+ }
997
+ function enqueueTask(meshId, message, opts) {
998
+ const queue = readQueue(meshId);
999
+ const entry = {
1000
+ id: randomUUID5(),
1001
+ meshId,
1002
+ message,
1003
+ status: "pending",
1004
+ targetNodeId: opts?.targetNodeId,
1005
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1006
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1007
+ };
1008
+ queue.push(entry);
1009
+ writeQueue(meshId, queue);
1010
+ return entry;
1011
+ }
1012
+ function getQueue(meshId, opts) {
1013
+ let queue = readQueue(meshId);
1014
+ if (opts?.status?.length) {
1015
+ const statuses = new Set(opts.status);
1016
+ queue = queue.filter((q) => statuses.has(q.status));
1017
+ }
1018
+ return queue;
1019
+ }
1020
+ function claimNextTask(meshId, nodeId, sessionId) {
1021
+ const queue = readQueue(meshId);
1022
+ let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId);
1023
+ if (targetIdx === -1) {
1024
+ targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId);
1025
+ }
1026
+ if (targetIdx === -1) return null;
1027
+ const entry = queue[targetIdx];
1028
+ entry.status = "assigned";
1029
+ entry.assignedNodeId = nodeId;
1030
+ entry.assignedSessionId = sessionId;
1031
+ entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1032
+ writeQueue(meshId, queue);
1033
+ return entry;
1034
+ }
1035
+ function updateTaskStatus(meshId, taskId, status) {
1036
+ const queue = readQueue(meshId);
1037
+ const idx = queue.findIndex((q) => q.id === taskId);
1038
+ if (idx === -1) return null;
1039
+ queue[idx].status = status;
1040
+ queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1041
+ writeQueue(meshId, queue);
1042
+ return queue[idx];
1043
+ }
1044
+ function updateSessionTaskStatus(meshId, sessionId, status) {
1045
+ const queue = readQueue(meshId);
1046
+ for (let i = queue.length - 1; i >= 0; i--) {
1047
+ if (queue[i].assignedSessionId === sessionId && queue[i].status === "assigned") {
1048
+ queue[i].status = status;
1049
+ queue[i].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1050
+ writeQueue(meshId, queue);
1051
+ return queue[i];
1052
+ }
1053
+ }
1054
+ return null;
1055
+ }
1056
+ function getMeshQueueStats(meshId) {
1057
+ const queue = readQueue(meshId);
1058
+ return {
1059
+ pending: queue.filter((q) => q.status === "pending").length,
1060
+ assigned: queue.filter((q) => q.status === "assigned").length,
1061
+ completed: queue.filter((q) => q.status === "completed").length,
1062
+ failed: queue.filter((q) => q.status === "failed").length
1063
+ };
1064
+ }
1065
+ var init_mesh_work_queue = __esm({
1066
+ "src/mesh/mesh-work-queue.ts"() {
1067
+ "use strict";
1068
+ init_mesh_ledger();
719
1069
  }
720
1070
  });
721
1071
 
722
1072
  // src/logging/logger.ts
723
1073
  import * as fs2 from "fs";
724
- import * as path10 from "path";
725
- import * as os4 from "os";
1074
+ import * as path8 from "path";
1075
+ import * as os2 from "os";
726
1076
  function setLogLevel(level) {
727
1077
  currentLevel = level;
728
1078
  daemonLog("Logger", `Log level set to: ${level}`, "info");
@@ -737,13 +1087,13 @@ function getDaemonLogDir() {
737
1087
  return LOG_DIR;
738
1088
  }
739
1089
  function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
740
- return path10.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
1090
+ return path8.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
741
1091
  }
742
1092
  function checkDateRotation() {
743
1093
  const today = getDateStr();
744
1094
  if (today !== currentDate) {
745
1095
  currentDate = today;
746
- currentLogFile = path10.join(LOG_DIR, `daemon-${currentDate}.log`);
1096
+ currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
747
1097
  cleanOldLogs();
748
1098
  }
749
1099
  }
@@ -757,7 +1107,7 @@ function cleanOldLogs() {
757
1107
  const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
758
1108
  if (dateMatch && dateMatch[1] < cutoffStr) {
759
1109
  try {
760
- fs2.unlinkSync(path10.join(LOG_DIR, file));
1110
+ fs2.unlinkSync(path8.join(LOG_DIR, file));
761
1111
  } catch {
762
1112
  }
763
1113
  }
@@ -880,7 +1230,7 @@ var init_logger = __esm({
880
1230
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
881
1231
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
882
1232
  currentLevel = "info";
883
- LOG_DIR = process.platform === "win32" ? path10.join(process.env.LOCALAPPDATA || process.env.APPDATA || path10.join(os4.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path10.join(os4.homedir(), "Library", "Logs", "adhdev") : path10.join(os4.homedir(), ".local", "share", "adhdev", "logs");
1233
+ LOG_DIR = process.platform === "win32" ? path8.join(process.env.LOCALAPPDATA || process.env.APPDATA || path8.join(os2.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path8.join(os2.homedir(), "Library", "Logs", "adhdev") : path8.join(os2.homedir(), ".local", "share", "adhdev", "logs");
884
1234
  MAX_LOG_SIZE = 5 * 1024 * 1024;
885
1235
  MAX_LOG_DAYS = 7;
886
1236
  try {
@@ -888,16 +1238,16 @@ var init_logger = __esm({
888
1238
  } catch {
889
1239
  }
890
1240
  currentDate = getDateStr();
891
- currentLogFile = path10.join(LOG_DIR, `daemon-${currentDate}.log`);
1241
+ currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
892
1242
  cleanOldLogs();
893
1243
  try {
894
- const oldLog = path10.join(LOG_DIR, "daemon.log");
1244
+ const oldLog = path8.join(LOG_DIR, "daemon.log");
895
1245
  if (fs2.existsSync(oldLog)) {
896
1246
  const stat2 = fs2.statSync(oldLog);
897
1247
  const oldDate = stat2.mtime.toISOString().slice(0, 10);
898
- fs2.renameSync(oldLog, path10.join(LOG_DIR, `daemon-${oldDate}.log`));
1248
+ fs2.renameSync(oldLog, path8.join(LOG_DIR, `daemon-${oldDate}.log`));
899
1249
  }
900
- const oldLogBackup = path10.join(LOG_DIR, "daemon.log.old");
1250
+ const oldLogBackup = path8.join(LOG_DIR, "daemon.log.old");
901
1251
  if (fs2.existsSync(oldLogBackup)) {
902
1252
  fs2.unlinkSync(oldLogBackup);
903
1253
  }
@@ -929,7 +1279,313 @@ var init_logger = __esm({
929
1279
  }
930
1280
  };
931
1281
  interceptorInstalled = false;
932
- LOG_PATH = path10.join(LOG_DIR, `daemon-${getDateStr()}.log`);
1282
+ LOG_PATH = path8.join(LOG_DIR, `daemon-${getDateStr()}.log`);
1283
+ }
1284
+ });
1285
+
1286
+ // src/mesh/mesh-events.ts
1287
+ var mesh_events_exports = {};
1288
+ __export(mesh_events_exports, {
1289
+ drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
1290
+ handleMeshForwardEvent: () => handleMeshForwardEvent,
1291
+ setupMeshEventForwarding: () => setupMeshEventForwarding,
1292
+ triggerMeshQueue: () => triggerMeshQueue,
1293
+ tryAssignQueueTask: () => tryAssignQueueTask
1294
+ });
1295
+ function drainPendingMeshCoordinatorEvents() {
1296
+ return pendingMeshCoordinatorEvents.splice(0);
1297
+ }
1298
+ function readNonEmptyString(value) {
1299
+ return typeof value === "string" && value.trim() ? value.trim() : "";
1300
+ }
1301
+ function isMeshCoordinatorEvent(eventName) {
1302
+ return typeof eventName === "string" && MESH_COORDINATOR_EVENTS.has(eventName);
1303
+ }
1304
+ function formatCompletionMetadata(event) {
1305
+ const parts = [
1306
+ readNonEmptyString(event.targetSessionId) ? `session_id=${readNonEmptyString(event.targetSessionId)}` : "",
1307
+ readNonEmptyString(event.providerType) ? `provider=${readNonEmptyString(event.providerType)}` : "",
1308
+ readNonEmptyString(event.providerSessionId) ? `provider_session_id=${readNonEmptyString(event.providerSessionId)}` : ""
1309
+ ].filter(Boolean);
1310
+ return parts.length > 0 ? ` (${parts.join("; ")})` : "";
1311
+ }
1312
+ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
1313
+ const task = claimNextTask(meshId, nodeId, sessionId);
1314
+ if (!task) return false;
1315
+ LOG.info("MeshQueue", `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
1316
+ components.cliManager.handleCliCommand("agent_command", {
1317
+ targetSessionId: sessionId,
1318
+ cliType: providerType,
1319
+ action: "send_chat",
1320
+ input: task.message
1321
+ }).catch((e) => {
1322
+ LOG.error("MeshQueue", `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
1323
+ });
1324
+ return true;
1325
+ }
1326
+ function triggerMeshQueue(components, meshId) {
1327
+ const mesh = getMesh(meshId);
1328
+ if (!mesh) return;
1329
+ const cliInstances = components.instanceManager.getByCategory("cli");
1330
+ for (const inst of cliInstances) {
1331
+ const state = inst.getState();
1332
+ const settings = state.settings || {};
1333
+ const instMeshId = readNonEmptyString(settings.meshNodeFor);
1334
+ if (instMeshId !== meshId && !settings.launchedByCoordinator) continue;
1335
+ const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
1336
+ if (!nodeId) continue;
1337
+ if (state.status !== "idle" && state.status !== "stopped" && state.activeChat?.status !== "waiting_input") continue;
1338
+ const sessionId = state.instanceId;
1339
+ const providerType = state.type || readNonEmptyString(settings.providerType);
1340
+ if (providerType) {
1341
+ tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
1342
+ }
1343
+ }
1344
+ }
1345
+ function buildMeshSystemMessage(args) {
1346
+ const metadata = formatCompletionMetadata(args.metadataEvent);
1347
+ if (args.event === "agent:generating_completed") {
1348
+ return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path; use mesh_read_chat once to review its final progress, but do not poll repeatedly.`;
1349
+ }
1350
+ if (args.event === "agent:waiting_approval") {
1351
+ return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
1352
+ }
1353
+ if (args.event === "agent:stopped") {
1354
+ const rc = args.recoveryContext;
1355
+ if (rc && rc.consecutiveNodeFailures > 0) {
1356
+ const parts = [
1357
+ `[System] ${args.nodeLabel} has stopped unexpectedly${metadata}.`,
1358
+ `
1359
+
1360
+ **Recovery Context:**`,
1361
+ `- Consecutive failures on this node: ${rc.consecutiveNodeFailures}`,
1362
+ rc.taskAttemptCount > 0 ? `- This task has been attempted ${rc.taskAttemptCount} time(s)` : "",
1363
+ `- Recommendation: ${rc.advice}`
1364
+ ];
1365
+ if (rc.retryRecommended && rc.lastTaskMessage) {
1366
+ parts.push(
1367
+ `
1368
+
1369
+ **Original task to retry:**`,
1370
+ `> ${rc.lastTaskMessage.length > 300 ? rc.lastTaskMessage.slice(0, 300) + "..." : rc.lastTaskMessage}`,
1371
+ `
1372
+ To retry: call \`mesh_launch_session\` for this node, then \`mesh_send_task\` with the original task.`
1373
+ );
1374
+ } else if (!rc.retryRecommended) {
1375
+ parts.push(
1376
+ `
1377
+ Do NOT retry on this node. Consider reassigning to a different node or asking the user for guidance.`
1378
+ );
1379
+ }
1380
+ return parts.filter(Boolean).join("\n");
1381
+ }
1382
+ return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
1383
+ }
1384
+ if (args.event === "monitor:long_generating") {
1385
+ return `[System] ${args.nodeLabel} has been generating for a long time${metadata}. Use mesh_read_chat once for a status check, but do not poll repeatedly.`;
1386
+ }
1387
+ return "";
1388
+ }
1389
+ function injectMeshSystemMessage(components, args) {
1390
+ if (args.event === "agent:generating_completed") {
1391
+ const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1392
+ const nodeId = readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1393
+ const providerType = readNonEmptyString(args.metadataEvent.providerType);
1394
+ if (sessionId) {
1395
+ updateSessionTaskStatus(args.meshId, sessionId, "completed");
1396
+ if (nodeId && providerType) {
1397
+ setTimeout(() => {
1398
+ tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
1399
+ }, 500);
1400
+ }
1401
+ }
1402
+ } else if (args.event === "agent:stopped") {
1403
+ const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1404
+ if (sessionId) {
1405
+ updateSessionTaskStatus(args.meshId, sessionId, "failed");
1406
+ }
1407
+ }
1408
+ const ledgerKind = EVENT_TO_LEDGER_KIND[args.event];
1409
+ if (ledgerKind) {
1410
+ try {
1411
+ appendLedgerEntry(args.meshId, {
1412
+ kind: ledgerKind,
1413
+ nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1414
+ sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1415
+ providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
1416
+ payload: {
1417
+ event: args.event,
1418
+ nodeLabel: args.nodeLabel,
1419
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
1420
+ }
1421
+ });
1422
+ } catch (e) {
1423
+ LOG.warn("MeshLedger", `Failed to record ${ledgerKind}: ${e?.message || e}`);
1424
+ }
1425
+ }
1426
+ let recoveryContext = null;
1427
+ if (args.event === "agent:stopped") {
1428
+ try {
1429
+ const mesh = getMesh(args.meshId);
1430
+ const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
1431
+ recoveryContext = getSessionRecoveryContext(args.meshId, {
1432
+ sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1433
+ nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1434
+ maxRetries
1435
+ });
1436
+ recoveryContext.failedProviderType = readNonEmptyString(args.metadataEvent.providerType) || null;
1437
+ if (recoveryContext.retryRecommended && recoveryContext.consecutiveNodeFailures > 0) {
1438
+ appendLedgerEntry(args.meshId, {
1439
+ kind: "recovery_attempted",
1440
+ nodeId: recoveryContext.failedNodeId || void 0,
1441
+ sessionId: recoveryContext.failedSessionId || void 0,
1442
+ providerType: recoveryContext.failedProviderType || void 0,
1443
+ payload: {
1444
+ consecutiveFailures: recoveryContext.consecutiveNodeFailures,
1445
+ taskAttemptCount: recoveryContext.taskAttemptCount,
1446
+ retryRecommended: recoveryContext.retryRecommended,
1447
+ advice: recoveryContext.advice
1448
+ }
1449
+ });
1450
+ if (recoveryContext.lastTaskMessage && recoveryContext.failedNodeId && recoveryContext.failedProviderType) {
1451
+ const autoNodeId = recoveryContext.failedNodeId;
1452
+ try {
1453
+ const task = enqueueTask(args.meshId, recoveryContext.lastTaskMessage, {
1454
+ targetNodeId: autoNodeId
1455
+ });
1456
+ LOG.info("MeshRecovery", `Auto-requeued failed task: ${task.id} for node ${autoNodeId}`);
1457
+ const node = mesh?.nodes.find((n) => n.id === autoNodeId);
1458
+ if (node) {
1459
+ components.cliManager.handleCliCommand("launch_cli", {
1460
+ cliType: recoveryContext.failedProviderType,
1461
+ dir: node.workspace,
1462
+ settings: {
1463
+ meshNodeFor: args.meshId,
1464
+ meshNodeId: node.id,
1465
+ spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
1466
+ launchedByCoordinator: true
1467
+ }
1468
+ }).catch((e) => LOG.error("MeshRecovery", `Failed to auto-relaunch session for ${node.id}: ${e?.message}`));
1469
+ }
1470
+ } catch (e) {
1471
+ LOG.warn("MeshRecovery", `Failed to execute auto-recovery: ${e?.message}`);
1472
+ }
1473
+ }
1474
+ }
1475
+ LOG.info("MeshRecovery", `Recovery context for ${args.nodeLabel}: ${recoveryContext.advice}`);
1476
+ } catch (e) {
1477
+ LOG.warn("MeshRecovery", `Failed to build recovery context: ${e?.message || e}`);
1478
+ }
1479
+ }
1480
+ const coordinatorInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
1481
+ const instState = inst.getState();
1482
+ if (instState.settings?.meshCoordinatorFor !== args.meshId) return false;
1483
+ if (args.sourceInstanceId && instState.instanceId === args.sourceInstanceId) return false;
1484
+ return true;
1485
+ });
1486
+ if (coordinatorInstances.length === 0) {
1487
+ if (pendingMeshCoordinatorEvents.length < MAX_PENDING_EVENTS) {
1488
+ pendingMeshCoordinatorEvents.push({
1489
+ event: args.event,
1490
+ meshId: args.meshId,
1491
+ nodeLabel: args.nodeLabel,
1492
+ metadataEvent: {
1493
+ ...args.metadataEvent,
1494
+ ...recoveryContext ? { recoveryContext } : {}
1495
+ },
1496
+ queuedAt: Date.now()
1497
+ });
1498
+ LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
1499
+ }
1500
+ return { success: true, forwarded: 0 };
1501
+ }
1502
+ const messageText = buildMeshSystemMessage({
1503
+ event: args.event,
1504
+ nodeLabel: args.nodeLabel,
1505
+ metadataEvent: args.metadataEvent,
1506
+ recoveryContext
1507
+ });
1508
+ if (!messageText) return { success: false, error: "unsupported mesh event" };
1509
+ for (const coord of coordinatorInstances) {
1510
+ const coordState = coord.getState();
1511
+ LOG.info("MeshEvents", `Forwarding mesh event to coordinator ${coordState.instanceId}`);
1512
+ coord.onEvent("send_message", { input: { text: messageText, textFallback: messageText } });
1513
+ }
1514
+ return { success: true, forwarded: coordinatorInstances.length };
1515
+ }
1516
+ function handleMeshForwardEvent(components, payload) {
1517
+ const eventName = readNonEmptyString(payload.event);
1518
+ if (!isMeshCoordinatorEvent(eventName)) {
1519
+ return { success: false, error: "unsupported mesh event" };
1520
+ }
1521
+ const meshId = readNonEmptyString(payload.meshId);
1522
+ if (!meshId) return { success: false, error: "meshId required" };
1523
+ const nodeId = readNonEmptyString(payload.nodeId);
1524
+ const workspace = readNonEmptyString(payload.workspace);
1525
+ const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
1526
+ return injectMeshSystemMessage(components, {
1527
+ meshId,
1528
+ nodeLabel,
1529
+ event: eventName,
1530
+ metadataEvent: {
1531
+ targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
1532
+ providerType: readNonEmptyString(payload.providerType),
1533
+ providerSessionId: readNonEmptyString(payload.providerSessionId)
1534
+ }
1535
+ });
1536
+ }
1537
+ function setupMeshEventForwarding(components) {
1538
+ components.instanceManager.onEvent((event) => {
1539
+ if (!isMeshCoordinatorEvent(event.event)) return;
1540
+ const instanceId = readNonEmptyString(event.instanceId);
1541
+ if (!instanceId) return;
1542
+ const sourceInstance = components.instanceManager.getInstance(instanceId);
1543
+ if (!sourceInstance || sourceInstance.category !== "cli") return;
1544
+ const state = sourceInstance.getState();
1545
+ const workspace = readNonEmptyString(state.workspace);
1546
+ if (!workspace) return;
1547
+ const settings = state.settings && typeof state.settings === "object" ? state.settings : {};
1548
+ if (readNonEmptyString(settings.meshCoordinatorFor)) return;
1549
+ const meshIdFromRuntime = readNonEmptyString(settings.meshNodeFor);
1550
+ const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
1551
+ if (!isMeshDelegate) return;
1552
+ const mesh = meshIdFromRuntime ? getMesh(meshIdFromRuntime) : getMeshByRepo(workspace);
1553
+ const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
1554
+ if (!meshId) return;
1555
+ const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
1556
+ const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
1557
+ const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
1558
+ injectMeshSystemMessage(components, {
1559
+ meshId,
1560
+ sourceInstanceId: instanceId,
1561
+ nodeLabel,
1562
+ event: event.event,
1563
+ metadataEvent: event
1564
+ });
1565
+ });
1566
+ }
1567
+ var MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
1568
+ var init_mesh_events = __esm({
1569
+ "src/mesh/mesh-events.ts"() {
1570
+ "use strict";
1571
+ init_mesh_config();
1572
+ init_logger();
1573
+ init_mesh_ledger();
1574
+ init_mesh_work_queue();
1575
+ MAX_PENDING_EVENTS = 50;
1576
+ pendingMeshCoordinatorEvents = [];
1577
+ MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
1578
+ "agent:generating_completed",
1579
+ "agent:waiting_approval",
1580
+ "agent:stopped",
1581
+ "monitor:long_generating"
1582
+ ]);
1583
+ EVENT_TO_LEDGER_KIND = {
1584
+ "agent:generating_completed": "task_completed",
1585
+ "agent:waiting_approval": "task_approval_needed",
1586
+ "agent:stopped": "task_failed",
1587
+ "monitor:long_generating": "task_stalled"
1588
+ };
933
1589
  }
934
1590
  });
935
1591
 
@@ -2039,6 +2695,8 @@ var init_provider_cli_adapter = __esm({
2039
2695
  statusHistory = [];
2040
2696
  // ─── CLI Scripts (script-based parsing) ───
2041
2697
  cliScripts;
2698
+ /** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
2699
+ scriptState = null;
2042
2700
  runtimeSettings = {};
2043
2701
  /** Full accumulated rendered PTY transcript for parser/readback use */
2044
2702
  accumulatedBuffer = "";
@@ -2220,6 +2878,7 @@ ${lastSnapshot}`;
2220
2878
  this.cliScripts = scripts;
2221
2879
  this.parsedStatusCache = null;
2222
2880
  this.parseErrorMessage = null;
2881
+ this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
2223
2882
  const scriptNames = listCliScriptNames(scripts);
2224
2883
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
2225
2884
  }
@@ -2337,6 +2996,7 @@ ${lastSnapshot}`;
2337
2996
  this.ready = false;
2338
2997
  this.startupParseGate = false;
2339
2998
  this.spawnAt = 0;
2999
+ this.scriptState = null;
2340
3000
  this.onStatusChange?.();
2341
3001
  });
2342
3002
  this.spawnAt = Date.now();
@@ -3110,7 +3770,7 @@ ${lastSnapshot}`;
3110
3770
  scope: this.currentTurnScope,
3111
3771
  runtimeSettings: this.runtimeSettings
3112
3772
  });
3113
- const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3773
+ const session = this.cliScripts.parseSession(this.scriptState, { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3114
3774
  this.parseErrorMessage = null;
3115
3775
  return session && typeof session === "object" ? session : null;
3116
3776
  } catch (e) {
@@ -3124,7 +3784,7 @@ ${lastSnapshot}`;
3124
3784
  if (!this.cliScripts?.detectStatus) return null;
3125
3785
  try {
3126
3786
  const screenText = this.terminalScreen.getText();
3127
- const status = this.cliScripts.detectStatus({
3787
+ const status = this.cliScripts.detectStatus(this.scriptState, {
3128
3788
  tail: text.slice(-500),
3129
3789
  screenText,
3130
3790
  rawBuffer: this.accumulatedRawBuffer,
@@ -3143,7 +3803,7 @@ ${lastSnapshot}`;
3143
3803
  try {
3144
3804
  const screenText = this.terminalScreen.getText();
3145
3805
  const buffer = screenText || this.accumulatedBuffer;
3146
- return this.cliScripts.parseApproval({
3806
+ return this.cliScripts.parseApproval(this.scriptState, {
3147
3807
  buffer,
3148
3808
  screenText,
3149
3809
  rawBuffer: this.accumulatedRawBuffer,
@@ -3251,7 +3911,7 @@ ${lastSnapshot}`;
3251
3911
  scope: this.currentTurnScope,
3252
3912
  runtimeSettings: this.runtimeSettings
3253
3913
  });
3254
- return await Promise.resolve(fn({
3914
+ return await Promise.resolve(fn(this.scriptState, {
3255
3915
  ...input,
3256
3916
  args: args && typeof args === "object" ? { ...args } : {}
3257
3917
  }));
@@ -5851,13 +6511,36 @@ async function syncMeshes(transport) {
5851
6511
  }
5852
6512
  }
5853
6513
  }
6514
+ if (transport.syncMeshLedger) {
6515
+ for (const local of localMeshes) {
6516
+ try {
6517
+ await syncMeshLedger(local.id, transport);
6518
+ } catch (e) {
6519
+ result.errors.push(`Ledger sync failed for "${local.name}": ${e.message}`);
6520
+ }
6521
+ }
6522
+ }
5854
6523
  return result;
5855
6524
  }
6525
+ async function syncMeshLedger(meshId, transport) {
6526
+ if (!transport.syncMeshLedger) return;
6527
+ const { readLedgerEntries: readLedgerEntries2, appendRemoteLedgerEntries: appendRemoteLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
6528
+ const localEntries = readLedgerEntries2(meshId);
6529
+ const res = await transport.syncMeshLedger(meshId, { newEntries: localEntries });
6530
+ if (res.missingEntries && res.missingEntries.length > 0) {
6531
+ appendRemoteLedgerEntries2(meshId, res.missingEntries);
6532
+ }
6533
+ }
6534
+
6535
+ // src/index.ts
6536
+ init_mesh_ledger();
6537
+ init_mesh_work_queue();
6538
+ init_mesh_events();
5856
6539
 
5857
6540
  // src/config/state-store.ts
5858
6541
  init_config();
5859
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5860
- import { join as join5 } from "path";
6542
+ import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
6543
+ import { join as join8 } from "path";
5861
6544
  var DEFAULT_STATE = {
5862
6545
  recentActivity: [],
5863
6546
  savedProviderSessions: [],
@@ -5870,7 +6553,7 @@ function isPlainObject2(value) {
5870
6553
  return !!value && typeof value === "object" && !Array.isArray(value);
5871
6554
  }
5872
6555
  function getStatePath() {
5873
- return join5(getConfigDir(), "state.json");
6556
+ return join8(getConfigDir(), "state.json");
5874
6557
  }
5875
6558
  function normalizeState(raw) {
5876
6559
  const parsed = isPlainObject2(raw) ? raw : {};
@@ -5906,11 +6589,11 @@ function normalizeState(raw) {
5906
6589
  }
5907
6590
  function loadState() {
5908
6591
  const statePath = getStatePath();
5909
- if (!existsSync5(statePath)) {
6592
+ if (!existsSync8(statePath)) {
5910
6593
  return { ...DEFAULT_STATE };
5911
6594
  }
5912
6595
  try {
5913
- const raw = readFileSync3(statePath, "utf-8");
6596
+ const raw = readFileSync5(statePath, "utf-8");
5914
6597
  return normalizeState(JSON.parse(raw));
5915
6598
  } catch {
5916
6599
  return { ...DEFAULT_STATE };
@@ -5919,7 +6602,7 @@ function loadState() {
5919
6602
  function saveState(state) {
5920
6603
  const statePath = getStatePath();
5921
6604
  const normalized = normalizeState(state);
5922
- writeFileSync3(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
6605
+ writeFileSync4(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
5923
6606
  }
5924
6607
  function resetState() {
5925
6608
  saveState({ ...DEFAULT_STATE });
@@ -5927,9 +6610,9 @@ function resetState() {
5927
6610
 
5928
6611
  // src/detection/ide-detector.ts
5929
6612
  import { execSync } from "child_process";
5930
- import { existsSync as existsSync6 } from "fs";
5931
- import { platform, homedir as homedir3 } from "os";
5932
- import * as path8 from "path";
6613
+ import { existsSync as existsSync9 } from "fs";
6614
+ import { platform, homedir as homedir4 } from "os";
6615
+ import * as path9 from "path";
5933
6616
  var BUILTIN_IDE_DEFINITIONS = [];
5934
6617
  var registeredIDEs = /* @__PURE__ */ new Map();
5935
6618
  function registerIDEDefinition(def) {
@@ -5948,10 +6631,10 @@ function getMergedDefinitions() {
5948
6631
  function findCliCommand(command) {
5949
6632
  const trimmed = String(command || "").trim();
5950
6633
  if (!trimmed) return null;
5951
- if (path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
5952
- const candidate = trimmed.startsWith("~") ? path8.join(homedir3(), trimmed.slice(1)) : trimmed;
5953
- const resolved = path8.isAbsolute(candidate) ? candidate : path8.resolve(candidate);
5954
- return existsSync6(resolved) ? resolved : null;
6634
+ if (path9.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
6635
+ const candidate = trimmed.startsWith("~") ? path9.join(homedir4(), trimmed.slice(1)) : trimmed;
6636
+ const resolved = path9.isAbsolute(candidate) ? candidate : path9.resolve(candidate);
6637
+ return existsSync9(resolved) ? resolved : null;
5955
6638
  }
5956
6639
  try {
5957
6640
  const result = execSync(
@@ -5976,15 +6659,15 @@ function getIdeVersion(cliCommand) {
5976
6659
  }
5977
6660
  }
5978
6661
  function checkPathExists(paths) {
5979
- const home = homedir3();
6662
+ const home = homedir4();
5980
6663
  for (const p of paths) {
5981
- const normalized = p.startsWith("~") ? path8.join(home, p.slice(1)) : p;
6664
+ const normalized = p.startsWith("~") ? path9.join(home, p.slice(1)) : p;
5982
6665
  if (normalized.includes("*")) {
5983
6666
  const username = home.split(/[\\/]/).pop() || "";
5984
6667
  const resolved = normalized.replace("*", username);
5985
- if (existsSync6(resolved)) return resolved;
6668
+ if (existsSync9(resolved)) return resolved;
5986
6669
  } else {
5987
- if (existsSync6(normalized)) return normalized;
6670
+ if (existsSync9(normalized)) return normalized;
5988
6671
  }
5989
6672
  }
5990
6673
  return null;
@@ -5998,7 +6681,7 @@ async function detectIDEs(providerLoader) {
5998
6681
  let resolvedCli = cliPath;
5999
6682
  if (!resolvedCli && appPath && os22 === "darwin") {
6000
6683
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
6001
- if (existsSync6(bundledCli)) resolvedCli = bundledCli;
6684
+ if (existsSync9(bundledCli)) resolvedCli = bundledCli;
6002
6685
  }
6003
6686
  if (!resolvedCli && appPath && os22 === "win32") {
6004
6687
  const { dirname: dirname9 } = await import("path");
@@ -6011,7 +6694,7 @@ async function detectIDEs(providerLoader) {
6011
6694
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
6012
6695
  ];
6013
6696
  for (const c of candidates) {
6014
- if (existsSync6(c)) {
6697
+ if (existsSync9(c)) {
6015
6698
  resolvedCli = c;
6016
6699
  break;
6017
6700
  }
@@ -6035,9 +6718,9 @@ async function detectIDEs(providerLoader) {
6035
6718
 
6036
6719
  // src/detection/cli-detector.ts
6037
6720
  import { exec } from "child_process";
6038
- import * as os2 from "os";
6039
- import * as path9 from "path";
6040
- import { existsSync as existsSync7 } from "fs";
6721
+ import * as os3 from "os";
6722
+ import * as path10 from "path";
6723
+ import { existsSync as existsSync10 } from "fs";
6041
6724
  function parseVersion(raw) {
6042
6725
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
6043
6726
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
@@ -6049,19 +6732,19 @@ function shellQuote(value) {
6049
6732
  function expandHome(value) {
6050
6733
  const trimmed = value.trim();
6051
6734
  if (!trimmed.startsWith("~")) return trimmed;
6052
- return path9.join(os2.homedir(), trimmed.slice(1));
6735
+ return path10.join(os3.homedir(), trimmed.slice(1));
6053
6736
  }
6054
6737
  function isExplicitCommandPath(command) {
6055
6738
  const trimmed = command.trim();
6056
- return path9.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
6739
+ return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
6057
6740
  }
6058
6741
  function resolveCommandPath(command) {
6059
6742
  const trimmed = command.trim();
6060
6743
  if (!trimmed) return null;
6061
6744
  if (isExplicitCommandPath(trimmed)) {
6062
6745
  const expanded = expandHome(trimmed);
6063
- const candidate = path9.isAbsolute(expanded) ? expanded : path9.resolve(expanded);
6064
- return existsSync7(candidate) ? candidate : null;
6746
+ const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
6747
+ return existsSync10(candidate) ? candidate : null;
6065
6748
  }
6066
6749
  return null;
6067
6750
  }
@@ -6082,7 +6765,7 @@ function execAsync(cmd, timeoutMs = 5e3) {
6082
6765
  });
6083
6766
  }
6084
6767
  async function detectCLIs(providerLoader, options) {
6085
- const platform10 = os2.platform();
6768
+ const platform10 = os3.platform();
6086
6769
  const whichCmd = platform10 === "win32" ? "where" : "which";
6087
6770
  const includeVersion = options?.includeVersion !== false;
6088
6771
  const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
@@ -6126,7 +6809,7 @@ async function detectCLI(cliId, providerLoader, options) {
6126
6809
  const cliList = providerLoader.getCliDetectionList();
6127
6810
  const target = cliList.find((c) => c.id === resolvedId);
6128
6811
  if (target) {
6129
- const platform10 = os2.platform();
6812
+ const platform10 = os3.platform();
6130
6813
  const whichCmd = platform10 === "win32" ? "where" : "which";
6131
6814
  try {
6132
6815
  const explicitPath = resolveCommandPath(target.command);
@@ -6163,10 +6846,10 @@ async function detectCLI(cliId, providerLoader, options) {
6163
6846
  }
6164
6847
 
6165
6848
  // src/system/host-memory.ts
6166
- import * as os3 from "os";
6849
+ import * as os4 from "os";
6167
6850
  import { execSync as execSync2 } from "child_process";
6168
6851
  function parseDarwinAvailableBytes(totalMem) {
6169
- if (os3.platform() !== "darwin") return null;
6852
+ if (os4.platform() !== "darwin") return null;
6170
6853
  try {
6171
6854
  const out = execSync2("vm_stat", {
6172
6855
  encoding: "utf-8",
@@ -6197,8 +6880,8 @@ function parseDarwinAvailableBytes(totalMem) {
6197
6880
  }
6198
6881
  }
6199
6882
  function getHostMemorySnapshot() {
6200
- const totalMem = os3.totalmem();
6201
- const freeMem = os3.freemem();
6883
+ const totalMem = os4.totalmem();
6884
+ const freeMem = os4.freemem();
6202
6885
  const darwinAvail = parseDarwinAvailableBytes(totalMem);
6203
6886
  const availableMem = darwinAvail != null ? darwinAvail : freeMem;
6204
6887
  return { totalMem, freeMem, availableMem };
@@ -11568,6 +12251,9 @@ function normalizeActiveChatData(activeChat, options = FULL_STATUS_ACTIVE_CHAT_O
11568
12251
  return normalized;
11569
12252
  }
11570
12253
 
12254
+ // src/status/builders.ts
12255
+ init_mesh_work_queue();
12256
+
11571
12257
  // src/providers/provider-input-support.ts
11572
12258
  var VALID_INPUT_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
11573
12259
  var VALID_INPUT_STRATEGIES = /* @__PURE__ */ new Set(["native", "native_acp", "resource_link", "text_fallback", "paste", "upload"]);
@@ -11793,6 +12479,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
11793
12479
  const workspace = state.workspace || null;
11794
12480
  const git = getGitSummaryForWorkspace(workspace, options);
11795
12481
  const title = activeChat?.title || state.name;
12482
+ const meshCoordinatorFor = state.settings?.meshCoordinatorFor;
12483
+ const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
11796
12484
  return {
11797
12485
  id: state.instanceId || state.type,
11798
12486
  parentId: null,
@@ -11815,7 +12503,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
11815
12503
  errorMessage: state.errorMessage,
11816
12504
  errorReason: state.errorReason,
11817
12505
  lastUpdated: state.lastUpdated,
11818
- settings: state.settings
12506
+ settings: state.settings,
12507
+ ...meshQueueStats && { meshQueueStats }
11819
12508
  };
11820
12509
  }
11821
12510
  function buildExtensionAgentSession(parent, ext, options) {
@@ -11827,6 +12516,8 @@ function buildExtensionAgentSession(parent, ext, options) {
11827
12516
  const includeSessionControls = shouldIncludeSessionControls(profile);
11828
12517
  const workspace = parent.workspace || null;
11829
12518
  const git = getGitSummaryForWorkspace(workspace, options);
12519
+ const meshCoordinatorFor = ext.settings?.meshCoordinatorFor;
12520
+ const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
11830
12521
  return {
11831
12522
  id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
11832
12523
  parentId: parent.instanceId || parent.type,
@@ -11849,7 +12540,8 @@ function buildExtensionAgentSession(parent, ext, options) {
11849
12540
  errorMessage: ext.errorMessage,
11850
12541
  errorReason: ext.errorReason,
11851
12542
  lastUpdated: ext.lastUpdated,
11852
- settings: ext.settings
12543
+ settings: ext.settings,
12544
+ ...meshQueueStats && { meshQueueStats }
11853
12545
  };
11854
12546
  }
11855
12547
  function shouldIncludeExtensionSession(ext) {
@@ -11877,6 +12569,8 @@ function buildCliSession(state, options) {
11877
12569
  const includeSessionControls = shouldIncludeSessionControls(profile);
11878
12570
  const workspace = state.workspace || null;
11879
12571
  const git = getGitSummaryForWorkspace(workspace, options);
12572
+ const meshCoordinatorFor = state.settings?.meshCoordinatorFor;
12573
+ const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
11880
12574
  return {
11881
12575
  id: state.instanceId,
11882
12576
  parentId: null,
@@ -11915,7 +12609,8 @@ function buildCliSession(state, options) {
11915
12609
  errorMessage: state.errorMessage,
11916
12610
  errorReason: state.errorReason,
11917
12611
  lastUpdated: state.lastUpdated,
11918
- settings: state.settings
12612
+ settings: state.settings,
12613
+ ...meshQueueStats && { meshQueueStats }
11919
12614
  };
11920
12615
  }
11921
12616
  function buildAcpSession(state, options) {
@@ -11927,6 +12622,8 @@ function buildAcpSession(state, options) {
11927
12622
  const includeSessionControls = shouldIncludeSessionControls(profile);
11928
12623
  const workspace = state.workspace || null;
11929
12624
  const git = getGitSummaryForWorkspace(workspace, options);
12625
+ const meshCoordinatorFor = state.settings?.meshCoordinatorFor;
12626
+ const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
11930
12627
  return {
11931
12628
  id: state.instanceId,
11932
12629
  parentId: null,
@@ -11948,7 +12645,8 @@ function buildAcpSession(state, options) {
11948
12645
  errorMessage: state.errorMessage,
11949
12646
  errorReason: state.errorReason,
11950
12647
  lastUpdated: state.lastUpdated,
11951
- settings: state.settings
12648
+ settings: state.settings,
12649
+ ...meshQueueStats && { meshQueueStats }
11952
12650
  };
11953
12651
  }
11954
12652
  function buildSessionEntries(allStates, cdpManagers, options = {}) {
@@ -12058,7 +12756,7 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
12058
12756
  import * as fs4 from "fs";
12059
12757
  import * as os6 from "os";
12060
12758
  import * as path12 from "path";
12061
- import { randomUUID as randomUUID5 } from "crypto";
12759
+ import { randomUUID as randomUUID7 } from "crypto";
12062
12760
  init_logger();
12063
12761
 
12064
12762
  // src/logging/debug-trace.ts
@@ -12583,7 +13281,7 @@ function safeBundleIdSegment(value, fallback) {
12583
13281
  function createChatDebugBundleId(targetSessionId) {
12584
13282
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.]/g, "").replace("T", "T").replace("Z", "Z");
12585
13283
  const sessionSegment = safeBundleIdSegment(targetSessionId, "unknown-session");
12586
- return `chat-debug-${timestamp}-${sessionSegment}-${randomUUID5().slice(0, 8)}`;
13284
+ return `chat-debug-${timestamp}-${sessionSegment}-${randomUUID7().slice(0, 8)}`;
12587
13285
  }
12588
13286
  function buildChatDebugBundleSummary(bundle) {
12589
13287
  const target = bundle.target && typeof bundle.target === "object" ? bundle.target : {};
@@ -15250,7 +15948,7 @@ init_provider_cli_adapter();
15250
15948
  import * as os13 from "os";
15251
15949
  import * as path18 from "path";
15252
15950
  import * as crypto4 from "crypto";
15253
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
15951
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
15254
15952
  import { execFileSync } from "child_process";
15255
15953
  import chalk from "chalk";
15256
15954
  init_config();
@@ -17632,7 +18330,7 @@ function commandExists(command) {
17632
18330
  const trimmed = command.trim();
17633
18331
  if (!trimmed) return false;
17634
18332
  if (isExplicitCommand(trimmed)) {
17635
- return existsSync12(expandExecutable(trimmed));
18333
+ return existsSync14(expandExecutable(trimmed));
17636
18334
  }
17637
18335
  try {
17638
18336
  execFileSync(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -17661,10 +18359,10 @@ function hasCliArg(args, flag) {
17661
18359
  }
17662
18360
  function ensureEmptyDelegatedMcpConfig(workspace) {
17663
18361
  const baseDir = path18.join(os13.tmpdir(), "adhdev-delegated-agent-empty-mcp");
17664
- mkdirSync8(baseDir, { recursive: true });
18362
+ mkdirSync9(baseDir, { recursive: true });
17665
18363
  const workspaceHash = crypto4.createHash("sha256").update(path18.resolve(workspace || os13.tmpdir())).digest("hex").slice(0, 16);
17666
18364
  const filePath = path18.join(baseDir, `${workspaceHash}.json`);
17667
- writeFileSync8(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
18365
+ writeFileSync9(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
17668
18366
  return filePath;
17669
18367
  }
17670
18368
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
@@ -20903,10 +21601,10 @@ import * as yaml from "js-yaml";
20903
21601
  // src/commands/mesh-coordinator.ts
20904
21602
  import { execFileSync as execFileSync2 } from "child_process";
20905
21603
  import { createHash as createHash2 } from "crypto";
20906
- import { existsSync as existsSync15, readdirSync as readdirSync7, realpathSync as realpathSync2 } from "fs";
21604
+ import { existsSync as existsSync17, readdirSync as readdirSync7, realpathSync as realpathSync2 } from "fs";
20907
21605
  import { createRequire as createRequire2 } from "module";
20908
21606
  import * as os17 from "os";
20909
- import { dirname as dirname4, isAbsolute as isAbsolute11, join as join18, resolve as resolve13 } from "path";
21607
+ import { dirname as dirname4, isAbsolute as isAbsolute11, join as join20, resolve as resolve13 } from "path";
20910
21608
  var DEFAULT_SERVER_NAME = "adhdev-mesh";
20911
21609
  var DEFAULT_ADHDEV_MCP_COMMAND = "adhdev-mcp";
20912
21610
  var HERMES_CLI_TYPE = "hermes-cli";
@@ -20927,7 +21625,7 @@ function resolveHermesMeshCoordinatorSetup(options) {
20927
21625
  reason: "Could not resolve the ADHDev MCP server entrypoint and a Node runtime with WebSocket support for daemon IPC mode"
20928
21626
  };
20929
21627
  }
20930
- const configPath = join18(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
21628
+ const configPath = join20(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
20931
21629
  if (!configPath.trim()) {
20932
21630
  return createHermesManualMeshCoordinatorSetup(options.meshId, options.workspace);
20933
21631
  }
@@ -21035,14 +21733,14 @@ function resolveHermesCoordinatorHome(meshId, workspace) {
21035
21733
  const key = `${meshId || "mesh"}
21036
21734
  ${resolve13(workspace || os17.tmpdir())}`;
21037
21735
  const hash = createHash2("sha256").update(key).digest("hex").slice(0, 16);
21038
- return join18(os17.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
21736
+ return join20(os17.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
21039
21737
  }
21040
21738
  function resolveMcpConfigPath(configPath, workspace) {
21041
21739
  const trimmed = configPath.trim();
21042
21740
  if (trimmed === "~") return os17.homedir();
21043
- if (trimmed.startsWith("~/")) return join18(os17.homedir(), trimmed.slice(2));
21741
+ if (trimmed.startsWith("~/")) return join20(os17.homedir(), trimmed.slice(2));
21044
21742
  if (isAbsolute11(trimmed)) return trimmed;
21045
- return join18(workspace, trimmed);
21743
+ return join20(workspace, trimmed);
21046
21744
  }
21047
21745
  function resolveAdhdevMcpServerLaunch(options) {
21048
21746
  const entryPath = resolveAdhdevMcpEntryPath(options.adhdevMcpEntryPath);
@@ -21082,15 +21780,15 @@ function addNodeCandidatesFromPath(pathValue, addCandidate) {
21082
21780
  for (const entry of (pathValue || "").split(":")) {
21083
21781
  const dir = entry.trim();
21084
21782
  if (!dir) continue;
21085
- addCandidate(join18(dir, "node"));
21783
+ addCandidate(join20(dir, "node"));
21086
21784
  }
21087
21785
  }
21088
21786
  function addNodeCandidatesFromNvm(homeDir, addCandidate) {
21089
- const versionsDir = join18(homeDir, ".nvm", "versions", "node");
21787
+ const versionsDir = join20(homeDir, ".nvm", "versions", "node");
21090
21788
  try {
21091
21789
  const versionDirs = readdirSync7(versionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort(compareNodeVersionNamesDescending);
21092
21790
  for (const versionDir of versionDirs) {
21093
- addCandidate(join18(versionsDir, versionDir, "bin", "node"));
21791
+ addCandidate(join20(versionsDir, versionDir, "bin", "node"));
21094
21792
  }
21095
21793
  } catch {
21096
21794
  }
@@ -21141,7 +21839,7 @@ function resolveAdhdevMcpEntryPath(explicitPath) {
21141
21839
  if (normalized) return normalized;
21142
21840
  }
21143
21841
  try {
21144
- const requireBase = process.argv[1] ? normalizeExistingPath(process.argv[1]) || process.argv[1] : join18(process.cwd(), "adhdev-daemon.js");
21842
+ const requireBase = process.argv[1] ? normalizeExistingPath(process.argv[1]) || process.argv[1] : join20(process.cwd(), "adhdev-daemon.js");
21145
21843
  const req = createRequire2(requireBase);
21146
21844
  const resolvedModule = req.resolve("@adhdev/mcp-server");
21147
21845
  return normalizeExistingPath(resolvedModule) || resolvedModule;
@@ -21151,141 +21849,15 @@ function resolveAdhdevMcpEntryPath(explicitPath) {
21151
21849
  }
21152
21850
  function normalizeExistingPath(filePath) {
21153
21851
  try {
21154
- if (!existsSync15(filePath)) return null;
21852
+ if (!existsSync17(filePath)) return null;
21155
21853
  return realpathSync2.native(filePath);
21156
21854
  } catch {
21157
21855
  return null;
21158
21856
  }
21159
21857
  }
21160
21858
 
21161
- // src/mesh/mesh-events.ts
21162
- init_mesh_config();
21163
- init_logger();
21164
- var MAX_PENDING_EVENTS = 50;
21165
- var pendingMeshCoordinatorEvents = [];
21166
- function drainPendingMeshCoordinatorEvents() {
21167
- return pendingMeshCoordinatorEvents.splice(0);
21168
- }
21169
- function readNonEmptyString(value) {
21170
- return typeof value === "string" && value.trim() ? value.trim() : "";
21171
- }
21172
- var MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
21173
- "agent:generating_completed",
21174
- "agent:waiting_approval",
21175
- "agent:stopped",
21176
- "monitor:long_generating"
21177
- ]);
21178
- function isMeshCoordinatorEvent(eventName) {
21179
- return typeof eventName === "string" && MESH_COORDINATOR_EVENTS.has(eventName);
21180
- }
21181
- function formatCompletionMetadata(event) {
21182
- const parts = [
21183
- readNonEmptyString(event.targetSessionId) ? `session_id=${readNonEmptyString(event.targetSessionId)}` : "",
21184
- readNonEmptyString(event.providerType) ? `provider=${readNonEmptyString(event.providerType)}` : "",
21185
- readNonEmptyString(event.providerSessionId) ? `provider_session_id=${readNonEmptyString(event.providerSessionId)}` : ""
21186
- ].filter(Boolean);
21187
- return parts.length > 0 ? ` (${parts.join("; ")})` : "";
21188
- }
21189
- function buildMeshSystemMessage(args) {
21190
- const metadata = formatCompletionMetadata(args.metadataEvent);
21191
- if (args.event === "agent:generating_completed") {
21192
- return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path; use mesh_read_chat once to review its final progress, but do not poll repeatedly.`;
21193
- }
21194
- if (args.event === "agent:waiting_approval") {
21195
- return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
21196
- }
21197
- if (args.event === "agent:stopped") {
21198
- return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
21199
- }
21200
- if (args.event === "monitor:long_generating") {
21201
- return `[System] ${args.nodeLabel} has been generating for a long time${metadata}. Use mesh_read_chat once for a status check, but do not poll repeatedly.`;
21202
- }
21203
- return "";
21204
- }
21205
- function injectMeshSystemMessage(components, args) {
21206
- const coordinatorInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
21207
- const instState = inst.getState();
21208
- if (instState.settings?.meshCoordinatorFor !== args.meshId) return false;
21209
- if (args.sourceInstanceId && instState.instanceId === args.sourceInstanceId) return false;
21210
- return true;
21211
- });
21212
- if (coordinatorInstances.length === 0) {
21213
- if (pendingMeshCoordinatorEvents.length < MAX_PENDING_EVENTS) {
21214
- pendingMeshCoordinatorEvents.push({
21215
- event: args.event,
21216
- meshId: args.meshId,
21217
- nodeLabel: args.nodeLabel,
21218
- metadataEvent: args.metadataEvent,
21219
- queuedAt: Date.now()
21220
- });
21221
- LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
21222
- }
21223
- return { success: true, forwarded: 0 };
21224
- }
21225
- const messageText = buildMeshSystemMessage({
21226
- event: args.event,
21227
- nodeLabel: args.nodeLabel,
21228
- metadataEvent: args.metadataEvent
21229
- });
21230
- if (!messageText) return { success: false, error: "unsupported mesh event" };
21231
- for (const coord of coordinatorInstances) {
21232
- const coordState = coord.getState();
21233
- LOG.info("MeshEvents", `Forwarding mesh event to coordinator ${coordState.instanceId}`);
21234
- coord.onEvent("send_message", { input: { text: messageText, textFallback: messageText } });
21235
- }
21236
- return { success: true, forwarded: coordinatorInstances.length };
21237
- }
21238
- function handleMeshForwardEvent(components, payload) {
21239
- const eventName = readNonEmptyString(payload.event);
21240
- if (!isMeshCoordinatorEvent(eventName)) {
21241
- return { success: false, error: "unsupported mesh event" };
21242
- }
21243
- const meshId = readNonEmptyString(payload.meshId);
21244
- if (!meshId) return { success: false, error: "meshId required" };
21245
- const nodeId = readNonEmptyString(payload.nodeId);
21246
- const workspace = readNonEmptyString(payload.workspace);
21247
- const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
21248
- return injectMeshSystemMessage(components, {
21249
- meshId,
21250
- nodeLabel,
21251
- event: eventName,
21252
- metadataEvent: {
21253
- targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
21254
- providerType: readNonEmptyString(payload.providerType),
21255
- providerSessionId: readNonEmptyString(payload.providerSessionId)
21256
- }
21257
- });
21258
- }
21259
- function setupMeshEventForwarding(components) {
21260
- components.instanceManager.onEvent((event) => {
21261
- if (!isMeshCoordinatorEvent(event.event)) return;
21262
- const instanceId = readNonEmptyString(event.instanceId);
21263
- if (!instanceId) return;
21264
- const sourceInstance = components.instanceManager.getInstance(instanceId);
21265
- if (!sourceInstance || sourceInstance.category !== "cli") return;
21266
- const state = sourceInstance.getState();
21267
- const workspace = readNonEmptyString(state.workspace);
21268
- if (!workspace) return;
21269
- const settings = state.settings && typeof state.settings === "object" ? state.settings : {};
21270
- if (readNonEmptyString(settings.meshCoordinatorFor)) return;
21271
- const meshIdFromRuntime = readNonEmptyString(settings.meshNodeFor);
21272
- const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
21273
- if (!isMeshDelegate) return;
21274
- const mesh = meshIdFromRuntime ? getMesh(meshIdFromRuntime) : getMeshByRepo(workspace);
21275
- const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
21276
- if (!meshId) return;
21277
- const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
21278
- const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
21279
- const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
21280
- injectMeshSystemMessage(components, {
21281
- meshId,
21282
- sourceInstanceId: instanceId,
21283
- nodeLabel,
21284
- event: event.event,
21285
- metadataEvent: event
21286
- });
21287
- });
21288
- }
21859
+ // src/commands/router.ts
21860
+ init_mesh_events();
21289
21861
 
21290
21862
  // src/status/snapshot.ts
21291
21863
  init_config();
@@ -22985,6 +23557,21 @@ var DaemonCommandRouter = class {
22985
23557
  return { success: false, error: e.message };
22986
23558
  }
22987
23559
  }
23560
+ case "get_mesh_ledger": {
23561
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23562
+ if (!meshId) return { success: false, error: "meshId required" };
23563
+ try {
23564
+ const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
23565
+ const tail = typeof args?.tail === "number" ? args.tail : 20;
23566
+ const since = typeof args?.since === "string" ? args.since : void 0;
23567
+ const kind = Array.isArray(args?.kind) ? args.kind.filter((k) => typeof k === "string") : void 0;
23568
+ const entries = readLedgerEntries2(meshId, { tail, since, kind });
23569
+ const summary = getLedgerSummary2(meshId);
23570
+ return { success: true, entries, summary };
23571
+ } catch (e) {
23572
+ return { success: false, error: e.message };
23573
+ }
23574
+ }
22988
23575
  case "add_mesh_node": {
22989
23576
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
22990
23577
  const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
@@ -23053,6 +23640,54 @@ var DaemonCommandRouter = class {
23053
23640
  return { success: false, error: e.message };
23054
23641
  }
23055
23642
  }
23643
+ case "refine_mesh_node": {
23644
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23645
+ const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
23646
+ if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
23647
+ try {
23648
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
23649
+ const mesh = meshRecord?.mesh;
23650
+ const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
23651
+ if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
23652
+ if (!node.isLocalWorktree || !node.workspace) {
23653
+ return { success: false, error: `Refinery requires a local worktree node` };
23654
+ }
23655
+ const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
23656
+ const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
23657
+ if (!repoRoot) return { success: false, error: "Source node repoRoot not found" };
23658
+ const { execFile: execFile3 } = await import("child_process");
23659
+ const { promisify: promisify3 } = await import("util");
23660
+ const execFileAsync3 = promisify3(execFile3);
23661
+ const { stdout: branchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
23662
+ const branch = branchStdout.trim();
23663
+ if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
23664
+ const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
23665
+ const baseBranch = baseBranchStdout.trim();
23666
+ try {
23667
+ await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
23668
+ } catch (e) {
23669
+ return { success: false, error: `Merge failed (conflicts?): ${e.message}` };
23670
+ }
23671
+ const removeResult = await this.execute("remove_mesh_node", {
23672
+ meshId,
23673
+ nodeId,
23674
+ sessionCleanupMode: "kill",
23675
+ inlineMesh: args?.inlineMesh
23676
+ });
23677
+ try {
23678
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
23679
+ appendLedgerEntry2(meshId, {
23680
+ kind: "node_removed",
23681
+ nodeId,
23682
+ payload: { refined: true, mergedBranch: branch, into: baseBranch }
23683
+ });
23684
+ } catch {
23685
+ }
23686
+ return { success: true, merged: true, branch, into: baseBranch, removeResult };
23687
+ } catch (e) {
23688
+ return { success: false, error: e.message };
23689
+ }
23690
+ }
23056
23691
  case "remove_mesh_node": {
23057
23692
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23058
23693
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
@@ -23088,6 +23723,17 @@ var DaemonCommandRouter = class {
23088
23723
  const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
23089
23724
  removed = removeNode3(meshId, nodeId);
23090
23725
  }
23726
+ if (removed) {
23727
+ try {
23728
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
23729
+ appendLedgerEntry2(meshId, {
23730
+ kind: "node_removed",
23731
+ nodeId,
23732
+ payload: { worktree: !!node?.isLocalWorktree, sessionCleanupMode }
23733
+ });
23734
+ } catch {
23735
+ }
23736
+ }
23091
23737
  return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {} };
23092
23738
  } catch (e) {
23093
23739
  return { success: false, error: e.message };
@@ -23117,9 +23763,9 @@ var DaemonCommandRouter = class {
23117
23763
  });
23118
23764
  let node;
23119
23765
  if (meshRecord.inline) {
23120
- const { randomUUID: randomUUID8 } = await import("crypto");
23766
+ const { randomUUID: randomUUID10 } = await import("crypto");
23121
23767
  node = {
23122
- id: `node_${randomUUID8().replace(/-/g, "")}`,
23768
+ id: `node_${randomUUID10().replace(/-/g, "")}`,
23123
23769
  workspace: result.worktreePath,
23124
23770
  repoRoot: result.worktreePath,
23125
23771
  daemonId: sourceNode.daemonId,
@@ -23144,6 +23790,15 @@ var DaemonCommandRouter = class {
23144
23790
  });
23145
23791
  if (!node) return { success: false, error: "Failed to register worktree node" };
23146
23792
  }
23793
+ try {
23794
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
23795
+ appendLedgerEntry2(meshId, {
23796
+ kind: "node_cloned",
23797
+ nodeId: node.id,
23798
+ payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath }
23799
+ });
23800
+ } catch {
23801
+ }
23147
23802
  return {
23148
23803
  success: true,
23149
23804
  node,
@@ -23154,6 +23809,19 @@ var DaemonCommandRouter = class {
23154
23809
  return { success: false, error: e.message };
23155
23810
  }
23156
23811
  }
23812
+ case "trigger_mesh_queue": {
23813
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23814
+ if (!meshId) return { success: false, error: "meshId required" };
23815
+ try {
23816
+ const { triggerMeshQueue: triggerMeshQueue2 } = await Promise.resolve().then(() => (init_mesh_events(), mesh_events_exports));
23817
+ if (meshId) {
23818
+ triggerMeshQueue2(this.deps, meshId);
23819
+ }
23820
+ return { success: true };
23821
+ } catch (e) {
23822
+ return { success: false, error: e.message };
23823
+ }
23824
+ }
23157
23825
  // ─── Mesh Coordinator Launch ───
23158
23826
  case "launch_mesh_coordinator": {
23159
23827
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
@@ -23258,7 +23926,7 @@ var DaemonCommandRouter = class {
23258
23926
  workspace
23259
23927
  };
23260
23928
  }
23261
- const { existsSync: existsSync23, readFileSync: readFileSync15, writeFileSync: writeFileSync14, copyFileSync: copyFileSync4, mkdirSync: mkdirSync16 } = await import("fs");
23929
+ const { existsSync: existsSync25, readFileSync: readFileSync17, writeFileSync: writeFileSync15, copyFileSync: copyFileSync4, mkdirSync: mkdirSync17 } = await import("fs");
23262
23930
  const { dirname: dirname9 } = await import("path");
23263
23931
  const mcpConfigPath = coordinatorSetup.configPath;
23264
23932
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -23292,21 +23960,21 @@ var DaemonCommandRouter = class {
23292
23960
  };
23293
23961
  }
23294
23962
  try {
23295
- mkdirSync16(dirname9(mcpConfigPath), { recursive: true });
23963
+ mkdirSync17(dirname9(mcpConfigPath), { recursive: true });
23296
23964
  } catch (error) {
23297
23965
  const message = `Could not prepare MCP config path for automatic setup: ${error?.message || error}`;
23298
23966
  LOG.error("MeshCoordinator", message);
23299
23967
  if (hermesManualFallback) return returnManualFallback(message);
23300
23968
  return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
23301
23969
  }
23302
- const hadExistingMcpConfig = existsSync23(mcpConfigPath);
23970
+ const hadExistingMcpConfig = existsSync25(mcpConfigPath);
23303
23971
  let existingMcpConfig = hermesBaseConfig?.config || {};
23304
23972
  if (hermesBaseConfig) {
23305
23973
  copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname9(mcpConfigPath));
23306
23974
  }
23307
23975
  if (hadExistingMcpConfig) {
23308
23976
  try {
23309
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync15(mcpConfigPath, "utf-8"), configFormat);
23977
+ const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync17(mcpConfigPath, "utf-8"), configFormat);
23310
23978
  existingMcpConfig = { ...existingMcpConfig, ...parsedExistingMcpConfig };
23311
23979
  copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
23312
23980
  } catch (error) {
@@ -23328,7 +23996,7 @@ var DaemonCommandRouter = class {
23328
23996
  }
23329
23997
  };
23330
23998
  try {
23331
- writeFileSync14(mcpConfigPath, serializeMeshCoordinatorMcpConfig(mcpConfig, configFormat), "utf-8");
23999
+ writeFileSync15(mcpConfigPath, serializeMeshCoordinatorMcpConfig(mcpConfig, configFormat), "utf-8");
23332
24000
  } catch (error) {
23333
24001
  const message = `Could not write MCP config for automatic setup: ${error?.message || error}`;
23334
24002
  LOG.error("MeshCoordinator", message);
@@ -23365,6 +24033,16 @@ var DaemonCommandRouter = class {
23365
24033
  return { success: false, error: launchResult?.error || "Failed to launch CLI session" };
23366
24034
  }
23367
24035
  LOG.info("MeshCoordinator", `Launched ${cliType} coordinator for mesh ${meshId} in ${workspace}`);
24036
+ try {
24037
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
24038
+ appendLedgerEntry2(meshId, {
24039
+ kind: "coordinator_started",
24040
+ sessionId: launchResult.sessionId || launchResult.id,
24041
+ providerType: cliType,
24042
+ payload: { workspace }
24043
+ });
24044
+ } catch {
24045
+ }
23368
24046
  return {
23369
24047
  success: true,
23370
24048
  meshId,
@@ -31155,6 +31833,7 @@ var SessionRegistry = class {
31155
31833
  // src/boot/daemon-lifecycle.ts
31156
31834
  init_logger();
31157
31835
  init_config();
31836
+ init_mesh_events();
31158
31837
  async function initDaemonComponents(config) {
31159
31838
  installGlobalInterceptor();
31160
31839
  const appConfig = loadConfig();
@@ -31437,6 +32116,7 @@ export {
31437
32116
  TurnSnapshotTracker,
31438
32117
  VersionArchive,
31439
32118
  addNode,
32119
+ appendLedgerEntry,
31440
32120
  appendRecentActivity,
31441
32121
  buildAssistantChatMessage,
31442
32122
  buildChatMessage,
@@ -31454,6 +32134,7 @@ export {
31454
32134
  buildThoughtChatMessage,
31455
32135
  buildToolChatMessage,
31456
32136
  buildUserChatMessage,
32137
+ claimNextTask,
31457
32138
  classifyChatMessageVisibility,
31458
32139
  classifyHotChatSessionsForSubscriptionFlush,
31459
32140
  clearDebugTrace,
@@ -31472,6 +32153,7 @@ export {
31472
32153
  detectAllVersions,
31473
32154
  detectCLIs,
31474
32155
  detectIDEs,
32156
+ enqueueTask,
31475
32157
  ensureSessionHostReady,
31476
32158
  execNpmCommandSync,
31477
32159
  filterActivityChatMessages,
@@ -31490,10 +32172,13 @@ export {
31490
32172
  getGitFileDiff,
31491
32173
  getGitRepoStatus,
31492
32174
  getHostMemorySnapshot,
32175
+ getLedgerDir,
32176
+ getLedgerSummary,
31493
32177
  getLogLevel,
31494
32178
  getMesh,
31495
32179
  getMeshByRepo,
31496
32180
  getNpmExecOptions,
32181
+ getQueue,
31497
32182
  getRecentActivity,
31498
32183
  getRecentCommands,
31499
32184
  getRecentDebugTrace,
@@ -31501,6 +32186,7 @@ export {
31501
32186
  getSavedProviderSessions,
31502
32187
  getSessionHostRecoveryLabel,
31503
32188
  getSessionHostSurfaceKind,
32189
+ getSessionRecoveryContext,
31504
32190
  getWorkspaceState,
31505
32191
  handleGitCommand,
31506
32192
  hasCdpManager,
@@ -31554,6 +32240,7 @@ export {
31554
32240
  prepareSessionModalUpdate,
31555
32241
  probeCdpPort,
31556
32242
  readChatHistory,
32243
+ readLedgerEntries,
31557
32244
  recordDebugTrace,
31558
32245
  registerExtensionProviders,
31559
32246
  removeNode,
@@ -31582,9 +32269,12 @@ export {
31582
32269
  startDaemonDevSupport,
31583
32270
  summarizeGitStatus,
31584
32271
  syncMeshes,
32272
+ triggerMeshQueue,
31585
32273
  updateConfig,
31586
32274
  updateMesh,
31587
32275
  updateNode,
32276
+ updateSessionTaskStatus,
32277
+ updateTaskStatus,
31588
32278
  upsertSavedProviderSession
31589
32279
  };
31590
32280
  //# sourceMappingURL=index.mjs.map