@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/cli-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +14 -4
- package/dist/index.d.ts +5 -0
- package/dist/index.js +911 -209
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +906 -216
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +11 -1
- package/dist/mesh/mesh-ledger.d.ts +90 -0
- package/dist/mesh/mesh-sync.d.ts +10 -0
- package/dist/mesh/mesh-work-queue.d.ts +50 -0
- package/dist/repo-mesh-types.d.ts +6 -0
- package/dist/shared-types.d.ts +12 -0
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +10 -4
- package/src/cli-adapters/provider-cli-shared.ts +14 -4
- package/src/commands/router.ts +123 -0
- package/src/index.ts +11 -0
- package/src/mesh/coordinator-prompt.ts +27 -12
- package/src/mesh/mesh-events.ts +200 -1
- package/src/mesh/mesh-ledger.ts +378 -0
- package/src/mesh/mesh-sync.ts +32 -0
- package/src/mesh/mesh-work-queue.ts +164 -0
- package/src/repo-mesh-types.ts +7 -0
- package/src/shared-types.ts +12 -0
- package/src/status/builders.ts +13 -0
package/dist/index.js
CHANGED
|
@@ -43,7 +43,8 @@ var init_repo_mesh_types = __esm({
|
|
|
43
43
|
dirtyWorkspaceBehavior: "warn",
|
|
44
44
|
maxParallelTasks: 2,
|
|
45
45
|
spawnedSessionVisibility: "visible",
|
|
46
|
-
sessionCleanupOnNodeRemove: "preserve"
|
|
46
|
+
sessionCleanupOnNodeRemove: "preserve",
|
|
47
|
+
maxTaskRetries: 1
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
});
|
|
@@ -673,12 +674,13 @@ function buildRulesSection(coordinatorCliType) {
|
|
|
673
674
|
return `## Rules
|
|
674
675
|
|
|
675
676
|
- **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.
|
|
676
|
-
- **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.
|
|
677
|
+
- **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.
|
|
677
678
|
- **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.
|
|
678
|
-
- **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.
|
|
679
|
+
- **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.
|
|
679
680
|
- **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.
|
|
680
681
|
- **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.
|
|
681
|
-
- **Handle failures
|
|
682
|
+
- **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.
|
|
683
|
+
- **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.
|
|
682
684
|
- **Keep the user informed.** Report progress after each delegation round \u2014 one or two sentences, not a narration.
|
|
683
685
|
- **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
|
|
684
686
|
- **Never fabricate tool results.** Always call the actual tool; never pretend you did.
|
|
@@ -699,6 +701,7 @@ var init_coordinator_prompt = __esm({
|
|
|
699
701
|
| \`mesh_launch_session\` | Start a new agent session on a node |
|
|
700
702
|
| \`mesh_send_task\` | Send a task (natural language) to a running agent |
|
|
701
703
|
| \`mesh_read_chat\` | Read an agent's recent messages to check progress |
|
|
704
|
+
| \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
|
|
702
705
|
| \`mesh_git_status\` | Check git status on a specific node |
|
|
703
706
|
| \`mesh_checkpoint\` | Create a git checkpoint on a node |
|
|
704
707
|
| \`mesh_approve\` | Approve/reject a pending agent action |
|
|
@@ -709,18 +712,366 @@ var init_coordinator_prompt = __esm({
|
|
|
709
712
|
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\`.`;
|
|
710
713
|
WORKFLOW_SECTION = `## Orchestration Workflow
|
|
711
714
|
|
|
712
|
-
1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available.
|
|
713
|
-
2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist.
|
|
714
|
-
3. **Delegate** \u2014
|
|
715
|
-
a.
|
|
716
|
-
b. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
|
|
717
|
-
c.
|
|
718
|
-
d.
|
|
719
|
-
4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly
|
|
715
|
+
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.
|
|
716
|
+
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.
|
|
717
|
+
3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
|
|
718
|
+
a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
|
|
719
|
+
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.
|
|
720
|
+
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.
|
|
721
|
+
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.
|
|
722
|
+
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\`.
|
|
720
723
|
5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
|
|
721
724
|
6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
|
|
722
725
|
7. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
|
|
723
|
-
8. **Report** \u2014 Summarize what was done, what changed, and any issues
|
|
726
|
+
8. **Report** \u2014 Summarize what was done, what changed, and any issues.
|
|
727
|
+
|
|
728
|
+
## Failure Recovery
|
|
729
|
+
|
|
730
|
+
When a node agent stops unexpectedly, the daemon automatically enriches the system message with **Recovery Context** that includes:
|
|
731
|
+
- The number of consecutive failures on that node
|
|
732
|
+
- The original task message (if recorded in the ledger)
|
|
733
|
+
- A recommendation: **retry**, **reassign**, or **escalate**
|
|
734
|
+
|
|
735
|
+
Follow these recovery rules:
|
|
736
|
+
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.
|
|
737
|
+
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.
|
|
738
|
+
3. **If no recovery context**: The stop may be intentional (normal completion). Use \`mesh_read_chat\` once to verify, then move on.
|
|
739
|
+
4. **Always record what happened**: After handling a failure, briefly note the outcome in your report to the user.`;
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
// src/mesh/mesh-ledger.ts
|
|
744
|
+
var mesh_ledger_exports = {};
|
|
745
|
+
__export(mesh_ledger_exports, {
|
|
746
|
+
appendLedgerEntry: () => appendLedgerEntry,
|
|
747
|
+
appendRemoteLedgerEntries: () => appendRemoteLedgerEntries,
|
|
748
|
+
getLedgerDir: () => getLedgerDir,
|
|
749
|
+
getLedgerSummary: () => getLedgerSummary,
|
|
750
|
+
getSessionRecoveryContext: () => getSessionRecoveryContext,
|
|
751
|
+
meshLedgerEvents: () => meshLedgerEvents,
|
|
752
|
+
readLedgerEntries: () => readLedgerEntries
|
|
753
|
+
});
|
|
754
|
+
function getLedgerDir() {
|
|
755
|
+
const dir = (0, import_path3.join)(getConfigDir(), LEDGER_DIR_NAME);
|
|
756
|
+
if (!(0, import_fs3.existsSync)(dir)) {
|
|
757
|
+
(0, import_fs3.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
758
|
+
}
|
|
759
|
+
return dir;
|
|
760
|
+
}
|
|
761
|
+
function getLedgerPath(meshId) {
|
|
762
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
763
|
+
return (0, import_path3.join)(getLedgerDir(), `${safe}.jsonl`);
|
|
764
|
+
}
|
|
765
|
+
function getRotatedPath(meshId, index) {
|
|
766
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
767
|
+
return (0, import_path3.join)(getLedgerDir(), `${safe}.${index}.jsonl`);
|
|
768
|
+
}
|
|
769
|
+
function appendLedgerEntry(meshId, partial) {
|
|
770
|
+
const entry = {
|
|
771
|
+
id: (0, import_crypto4.randomUUID)(),
|
|
772
|
+
meshId,
|
|
773
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
774
|
+
...partial
|
|
775
|
+
};
|
|
776
|
+
const filePath = getLedgerPath(meshId);
|
|
777
|
+
if ((0, import_fs3.existsSync)(filePath)) {
|
|
778
|
+
try {
|
|
779
|
+
const stat2 = (0, import_fs3.statSync)(filePath);
|
|
780
|
+
if (stat2.size >= MAX_FILE_SIZE_BYTES) {
|
|
781
|
+
rotateLedgerFile(meshId, filePath);
|
|
782
|
+
}
|
|
783
|
+
} catch {
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
try {
|
|
787
|
+
const line = JSON.stringify(entry) + "\n";
|
|
788
|
+
(0, import_fs3.appendFileSync)(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
789
|
+
meshLedgerEvents.emit("append", meshId, entry);
|
|
790
|
+
return entry;
|
|
791
|
+
} catch (e) {
|
|
792
|
+
throw new Error(`Failed to append to ledger for mesh ${meshId}: ${e.message}`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
function appendRemoteLedgerEntries(meshId, entries) {
|
|
796
|
+
if (entries.length === 0) return;
|
|
797
|
+
const ledgerPath = getLedgerPath(meshId);
|
|
798
|
+
const existing = new Set(readLedgerEntries(meshId).map((e) => e.id));
|
|
799
|
+
const newEntries = entries.filter((e) => !existing.has(e.id));
|
|
800
|
+
if (newEntries.length === 0) return;
|
|
801
|
+
try {
|
|
802
|
+
const lines = newEntries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
803
|
+
(0, import_fs3.appendFileSync)(ledgerPath, lines, { encoding: "utf-8", mode: 384 });
|
|
804
|
+
} catch (e) {
|
|
805
|
+
throw new Error(`Failed to append remote ledger entries for mesh ${meshId}: ${e.message}`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function readLedgerEntries(meshId, opts) {
|
|
809
|
+
const filePath = getLedgerPath(meshId);
|
|
810
|
+
if (!(0, import_fs3.existsSync)(filePath)) return [];
|
|
811
|
+
let content;
|
|
812
|
+
try {
|
|
813
|
+
content = (0, import_fs3.readFileSync)(filePath, "utf-8");
|
|
814
|
+
} catch {
|
|
815
|
+
return [];
|
|
816
|
+
}
|
|
817
|
+
const lines = content.split("\n").filter((line) => line.trim());
|
|
818
|
+
let entries = [];
|
|
819
|
+
for (const line of lines) {
|
|
820
|
+
try {
|
|
821
|
+
const entry = JSON.parse(line);
|
|
822
|
+
if (!entry.id || !entry.kind) continue;
|
|
823
|
+
entries.push(entry);
|
|
824
|
+
} catch {
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
if (opts?.since) {
|
|
828
|
+
const sinceDate = new Date(opts.since).getTime();
|
|
829
|
+
if (!isNaN(sinceDate)) {
|
|
830
|
+
entries = entries.filter((e) => new Date(e.timestamp).getTime() >= sinceDate);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
if (opts?.kind?.length) {
|
|
834
|
+
const kindSet = new Set(opts.kind);
|
|
835
|
+
entries = entries.filter((e) => kindSet.has(e.kind));
|
|
836
|
+
}
|
|
837
|
+
if (opts?.tail && opts.tail > 0 && entries.length > opts.tail) {
|
|
838
|
+
entries = entries.slice(-opts.tail);
|
|
839
|
+
}
|
|
840
|
+
return entries;
|
|
841
|
+
}
|
|
842
|
+
function getLedgerSummary(meshId) {
|
|
843
|
+
const entries = readLedgerEntries(meshId);
|
|
844
|
+
const now = Date.now();
|
|
845
|
+
const recentFailureCutoff = now - RECENT_FAILURE_WINDOW_MS;
|
|
846
|
+
const summary = {
|
|
847
|
+
meshId,
|
|
848
|
+
totalEntries: entries.length,
|
|
849
|
+
taskDispatched: 0,
|
|
850
|
+
taskCompleted: 0,
|
|
851
|
+
taskFailed: 0,
|
|
852
|
+
taskStalled: 0,
|
|
853
|
+
sessionLaunched: 0,
|
|
854
|
+
checkpointCreated: 0,
|
|
855
|
+
lastActivityAt: null,
|
|
856
|
+
recentFailures: 0
|
|
857
|
+
};
|
|
858
|
+
for (const entry of entries) {
|
|
859
|
+
switch (entry.kind) {
|
|
860
|
+
case "task_dispatched":
|
|
861
|
+
summary.taskDispatched++;
|
|
862
|
+
break;
|
|
863
|
+
case "task_completed":
|
|
864
|
+
summary.taskCompleted++;
|
|
865
|
+
break;
|
|
866
|
+
case "task_failed": {
|
|
867
|
+
summary.taskFailed++;
|
|
868
|
+
if (new Date(entry.timestamp).getTime() >= recentFailureCutoff) {
|
|
869
|
+
summary.recentFailures++;
|
|
870
|
+
}
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
case "task_stalled":
|
|
874
|
+
summary.taskStalled++;
|
|
875
|
+
break;
|
|
876
|
+
case "session_launched":
|
|
877
|
+
summary.sessionLaunched++;
|
|
878
|
+
break;
|
|
879
|
+
case "checkpoint_created":
|
|
880
|
+
summary.checkpointCreated++;
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (entries.length > 0) {
|
|
885
|
+
summary.lastActivityAt = entries[entries.length - 1].timestamp;
|
|
886
|
+
}
|
|
887
|
+
return summary;
|
|
888
|
+
}
|
|
889
|
+
function getSessionRecoveryContext(meshId, opts) {
|
|
890
|
+
const maxRetries = opts.maxRetries ?? 1;
|
|
891
|
+
const entries = readLedgerEntries(meshId);
|
|
892
|
+
let lastDispatch = null;
|
|
893
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
894
|
+
const e = entries[i];
|
|
895
|
+
if (e.kind !== "task_dispatched") continue;
|
|
896
|
+
if (opts.sessionId && e.sessionId === opts.sessionId) {
|
|
897
|
+
lastDispatch = e;
|
|
898
|
+
break;
|
|
899
|
+
}
|
|
900
|
+
if (opts.nodeId && e.nodeId === opts.nodeId) {
|
|
901
|
+
lastDispatch = e;
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
const lastTaskMessage = typeof lastDispatch?.payload?.message === "string" ? lastDispatch.payload.message : null;
|
|
906
|
+
const now = Date.now();
|
|
907
|
+
const recentWindow = now - RECENT_FAILURE_WINDOW_MS;
|
|
908
|
+
let consecutiveNodeFailures = 0;
|
|
909
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
910
|
+
const e = entries[i];
|
|
911
|
+
if (new Date(e.timestamp).getTime() < recentWindow) break;
|
|
912
|
+
if (opts.nodeId && e.nodeId !== opts.nodeId) continue;
|
|
913
|
+
if (e.kind === "task_failed") {
|
|
914
|
+
consecutiveNodeFailures++;
|
|
915
|
+
} else if (e.kind === "task_completed" || e.kind === "task_dispatched") {
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
let taskAttemptCount = 0;
|
|
920
|
+
if (lastTaskMessage) {
|
|
921
|
+
const prefix = lastTaskMessage.slice(0, 200);
|
|
922
|
+
for (const e of entries) {
|
|
923
|
+
if (e.kind === "task_dispatched" && typeof e.payload?.message === "string") {
|
|
924
|
+
if (e.payload.message.startsWith(prefix)) {
|
|
925
|
+
taskAttemptCount++;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const retryRecommended = consecutiveNodeFailures <= maxRetries;
|
|
931
|
+
let advice;
|
|
932
|
+
if (consecutiveNodeFailures === 0) {
|
|
933
|
+
advice = "No recent failures detected. This may be a normal stop.";
|
|
934
|
+
} else if (retryRecommended) {
|
|
935
|
+
const remaining = maxRetries - consecutiveNodeFailures + 1;
|
|
936
|
+
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.`);
|
|
937
|
+
} else {
|
|
938
|
+
advice = `Max retries exceeded (${consecutiveNodeFailures} consecutive failures). Consider: (1) reassigning to a different node, (2) simplifying the task, or (3) escalating to the user.`;
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
lastTaskMessage,
|
|
942
|
+
failedNodeId: opts.nodeId || null,
|
|
943
|
+
failedSessionId: opts.sessionId || null,
|
|
944
|
+
failedProviderType: null,
|
|
945
|
+
// filled by caller if available
|
|
946
|
+
consecutiveNodeFailures,
|
|
947
|
+
taskAttemptCount,
|
|
948
|
+
retryRecommended,
|
|
949
|
+
advice
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
function rotateLedgerFile(meshId, currentPath) {
|
|
953
|
+
let index = 1;
|
|
954
|
+
while ((0, import_fs3.existsSync)(getRotatedPath(meshId, index))) {
|
|
955
|
+
index++;
|
|
956
|
+
if (index > 10) break;
|
|
957
|
+
}
|
|
958
|
+
if (index > 10) index = 10;
|
|
959
|
+
try {
|
|
960
|
+
(0, import_fs3.renameSync)(currentPath, getRotatedPath(meshId, index));
|
|
961
|
+
} catch {
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
var import_fs3, import_path3, import_crypto4, import_events, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents;
|
|
965
|
+
var init_mesh_ledger = __esm({
|
|
966
|
+
"src/mesh/mesh-ledger.ts"() {
|
|
967
|
+
"use strict";
|
|
968
|
+
import_fs3 = require("fs");
|
|
969
|
+
import_path3 = require("path");
|
|
970
|
+
import_crypto4 = require("crypto");
|
|
971
|
+
init_config();
|
|
972
|
+
import_events = require("events");
|
|
973
|
+
LEDGER_DIR_NAME = "mesh-ledger";
|
|
974
|
+
MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
|
|
975
|
+
RECENT_FAILURE_WINDOW_MS = 30 * 60 * 1e3;
|
|
976
|
+
meshLedgerEvents = new import_events.EventEmitter();
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
// src/mesh/mesh-work-queue.ts
|
|
981
|
+
function getQueuePath(meshId) {
|
|
982
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
983
|
+
return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.json`);
|
|
984
|
+
}
|
|
985
|
+
function readQueue(meshId) {
|
|
986
|
+
const path28 = getQueuePath(meshId);
|
|
987
|
+
if (!(0, import_fs4.existsSync)(path28)) return [];
|
|
988
|
+
try {
|
|
989
|
+
const content = (0, import_fs4.readFileSync)(path28, "utf-8");
|
|
990
|
+
return JSON.parse(content);
|
|
991
|
+
} catch {
|
|
992
|
+
return [];
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
function writeQueue(meshId, queue) {
|
|
996
|
+
const path28 = getQueuePath(meshId);
|
|
997
|
+
(0, import_fs4.writeFileSync)(path28, JSON.stringify(queue, null, 2), "utf-8");
|
|
998
|
+
}
|
|
999
|
+
function enqueueTask(meshId, message, opts) {
|
|
1000
|
+
const queue = readQueue(meshId);
|
|
1001
|
+
const entry = {
|
|
1002
|
+
id: (0, import_crypto5.randomUUID)(),
|
|
1003
|
+
meshId,
|
|
1004
|
+
message,
|
|
1005
|
+
status: "pending",
|
|
1006
|
+
targetNodeId: opts?.targetNodeId,
|
|
1007
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1008
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1009
|
+
};
|
|
1010
|
+
queue.push(entry);
|
|
1011
|
+
writeQueue(meshId, queue);
|
|
1012
|
+
return entry;
|
|
1013
|
+
}
|
|
1014
|
+
function getQueue(meshId, opts) {
|
|
1015
|
+
let queue = readQueue(meshId);
|
|
1016
|
+
if (opts?.status?.length) {
|
|
1017
|
+
const statuses = new Set(opts.status);
|
|
1018
|
+
queue = queue.filter((q) => statuses.has(q.status));
|
|
1019
|
+
}
|
|
1020
|
+
return queue;
|
|
1021
|
+
}
|
|
1022
|
+
function claimNextTask(meshId, nodeId, sessionId) {
|
|
1023
|
+
const queue = readQueue(meshId);
|
|
1024
|
+
let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId);
|
|
1025
|
+
if (targetIdx === -1) {
|
|
1026
|
+
targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId);
|
|
1027
|
+
}
|
|
1028
|
+
if (targetIdx === -1) return null;
|
|
1029
|
+
const entry = queue[targetIdx];
|
|
1030
|
+
entry.status = "assigned";
|
|
1031
|
+
entry.assignedNodeId = nodeId;
|
|
1032
|
+
entry.assignedSessionId = sessionId;
|
|
1033
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1034
|
+
writeQueue(meshId, queue);
|
|
1035
|
+
return entry;
|
|
1036
|
+
}
|
|
1037
|
+
function updateTaskStatus(meshId, taskId, status) {
|
|
1038
|
+
const queue = readQueue(meshId);
|
|
1039
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1040
|
+
if (idx === -1) return null;
|
|
1041
|
+
queue[idx].status = status;
|
|
1042
|
+
queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1043
|
+
writeQueue(meshId, queue);
|
|
1044
|
+
return queue[idx];
|
|
1045
|
+
}
|
|
1046
|
+
function updateSessionTaskStatus(meshId, sessionId, status) {
|
|
1047
|
+
const queue = readQueue(meshId);
|
|
1048
|
+
for (let i = queue.length - 1; i >= 0; i--) {
|
|
1049
|
+
if (queue[i].assignedSessionId === sessionId && queue[i].status === "assigned") {
|
|
1050
|
+
queue[i].status = status;
|
|
1051
|
+
queue[i].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1052
|
+
writeQueue(meshId, queue);
|
|
1053
|
+
return queue[i];
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
function getMeshQueueStats(meshId) {
|
|
1059
|
+
const queue = readQueue(meshId);
|
|
1060
|
+
return {
|
|
1061
|
+
pending: queue.filter((q) => q.status === "pending").length,
|
|
1062
|
+
assigned: queue.filter((q) => q.status === "assigned").length,
|
|
1063
|
+
completed: queue.filter((q) => q.status === "completed").length,
|
|
1064
|
+
failed: queue.filter((q) => q.status === "failed").length
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
var import_fs4, import_path4, import_crypto5;
|
|
1068
|
+
var init_mesh_work_queue = __esm({
|
|
1069
|
+
"src/mesh/mesh-work-queue.ts"() {
|
|
1070
|
+
"use strict";
|
|
1071
|
+
import_fs4 = require("fs");
|
|
1072
|
+
import_path4 = require("path");
|
|
1073
|
+
import_crypto5 = require("crypto");
|
|
1074
|
+
init_mesh_ledger();
|
|
724
1075
|
}
|
|
725
1076
|
});
|
|
726
1077
|
|
|
@@ -739,13 +1090,13 @@ function getDaemonLogDir() {
|
|
|
739
1090
|
return LOG_DIR;
|
|
740
1091
|
}
|
|
741
1092
|
function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
|
|
742
|
-
return
|
|
1093
|
+
return path8.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
|
|
743
1094
|
}
|
|
744
1095
|
function checkDateRotation() {
|
|
745
1096
|
const today = getDateStr();
|
|
746
1097
|
if (today !== currentDate) {
|
|
747
1098
|
currentDate = today;
|
|
748
|
-
currentLogFile =
|
|
1099
|
+
currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
749
1100
|
cleanOldLogs();
|
|
750
1101
|
}
|
|
751
1102
|
}
|
|
@@ -759,7 +1110,7 @@ function cleanOldLogs() {
|
|
|
759
1110
|
const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
|
|
760
1111
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
761
1112
|
try {
|
|
762
|
-
fs2.unlinkSync(
|
|
1113
|
+
fs2.unlinkSync(path8.join(LOG_DIR, file));
|
|
763
1114
|
} catch {
|
|
764
1115
|
}
|
|
765
1116
|
}
|
|
@@ -875,17 +1226,17 @@ function installGlobalInterceptor() {
|
|
|
875
1226
|
writeToFile(`Log file: ${currentLogFile}`);
|
|
876
1227
|
writeToFile(`Log level: ${currentLevel}`);
|
|
877
1228
|
}
|
|
878
|
-
var fs2,
|
|
1229
|
+
var fs2, path8, os2, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH;
|
|
879
1230
|
var init_logger = __esm({
|
|
880
1231
|
"src/logging/logger.ts"() {
|
|
881
1232
|
"use strict";
|
|
882
1233
|
fs2 = __toESM(require("fs"));
|
|
883
|
-
|
|
884
|
-
|
|
1234
|
+
path8 = __toESM(require("path"));
|
|
1235
|
+
os2 = __toESM(require("os"));
|
|
885
1236
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
886
1237
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
887
1238
|
currentLevel = "info";
|
|
888
|
-
LOG_DIR = process.platform === "win32" ?
|
|
1239
|
+
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");
|
|
889
1240
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
890
1241
|
MAX_LOG_DAYS = 7;
|
|
891
1242
|
try {
|
|
@@ -893,16 +1244,16 @@ var init_logger = __esm({
|
|
|
893
1244
|
} catch {
|
|
894
1245
|
}
|
|
895
1246
|
currentDate = getDateStr();
|
|
896
|
-
currentLogFile =
|
|
1247
|
+
currentLogFile = path8.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
897
1248
|
cleanOldLogs();
|
|
898
1249
|
try {
|
|
899
|
-
const oldLog =
|
|
1250
|
+
const oldLog = path8.join(LOG_DIR, "daemon.log");
|
|
900
1251
|
if (fs2.existsSync(oldLog)) {
|
|
901
1252
|
const stat2 = fs2.statSync(oldLog);
|
|
902
1253
|
const oldDate = stat2.mtime.toISOString().slice(0, 10);
|
|
903
|
-
fs2.renameSync(oldLog,
|
|
1254
|
+
fs2.renameSync(oldLog, path8.join(LOG_DIR, `daemon-${oldDate}.log`));
|
|
904
1255
|
}
|
|
905
|
-
const oldLogBackup =
|
|
1256
|
+
const oldLogBackup = path8.join(LOG_DIR, "daemon.log.old");
|
|
906
1257
|
if (fs2.existsSync(oldLogBackup)) {
|
|
907
1258
|
fs2.unlinkSync(oldLogBackup);
|
|
908
1259
|
}
|
|
@@ -934,7 +1285,313 @@ var init_logger = __esm({
|
|
|
934
1285
|
}
|
|
935
1286
|
};
|
|
936
1287
|
interceptorInstalled = false;
|
|
937
|
-
LOG_PATH =
|
|
1288
|
+
LOG_PATH = path8.join(LOG_DIR, `daemon-${getDateStr()}.log`);
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
// src/mesh/mesh-events.ts
|
|
1293
|
+
var mesh_events_exports = {};
|
|
1294
|
+
__export(mesh_events_exports, {
|
|
1295
|
+
drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
|
|
1296
|
+
handleMeshForwardEvent: () => handleMeshForwardEvent,
|
|
1297
|
+
setupMeshEventForwarding: () => setupMeshEventForwarding,
|
|
1298
|
+
triggerMeshQueue: () => triggerMeshQueue,
|
|
1299
|
+
tryAssignQueueTask: () => tryAssignQueueTask
|
|
1300
|
+
});
|
|
1301
|
+
function drainPendingMeshCoordinatorEvents() {
|
|
1302
|
+
return pendingMeshCoordinatorEvents.splice(0);
|
|
1303
|
+
}
|
|
1304
|
+
function readNonEmptyString(value) {
|
|
1305
|
+
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
1306
|
+
}
|
|
1307
|
+
function isMeshCoordinatorEvent(eventName) {
|
|
1308
|
+
return typeof eventName === "string" && MESH_COORDINATOR_EVENTS.has(eventName);
|
|
1309
|
+
}
|
|
1310
|
+
function formatCompletionMetadata(event) {
|
|
1311
|
+
const parts = [
|
|
1312
|
+
readNonEmptyString(event.targetSessionId) ? `session_id=${readNonEmptyString(event.targetSessionId)}` : "",
|
|
1313
|
+
readNonEmptyString(event.providerType) ? `provider=${readNonEmptyString(event.providerType)}` : "",
|
|
1314
|
+
readNonEmptyString(event.providerSessionId) ? `provider_session_id=${readNonEmptyString(event.providerSessionId)}` : ""
|
|
1315
|
+
].filter(Boolean);
|
|
1316
|
+
return parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
|
1317
|
+
}
|
|
1318
|
+
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
1319
|
+
const task = claimNextTask(meshId, nodeId, sessionId);
|
|
1320
|
+
if (!task) return false;
|
|
1321
|
+
LOG.info("MeshQueue", `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
|
|
1322
|
+
components.cliManager.handleCliCommand("agent_command", {
|
|
1323
|
+
targetSessionId: sessionId,
|
|
1324
|
+
cliType: providerType,
|
|
1325
|
+
action: "send_chat",
|
|
1326
|
+
input: task.message
|
|
1327
|
+
}).catch((e) => {
|
|
1328
|
+
LOG.error("MeshQueue", `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
|
|
1329
|
+
});
|
|
1330
|
+
return true;
|
|
1331
|
+
}
|
|
1332
|
+
function triggerMeshQueue(components, meshId) {
|
|
1333
|
+
const mesh = getMesh(meshId);
|
|
1334
|
+
if (!mesh) return;
|
|
1335
|
+
const cliInstances = components.instanceManager.getByCategory("cli");
|
|
1336
|
+
for (const inst of cliInstances) {
|
|
1337
|
+
const state = inst.getState();
|
|
1338
|
+
const settings = state.settings || {};
|
|
1339
|
+
const instMeshId = readNonEmptyString(settings.meshNodeFor);
|
|
1340
|
+
if (instMeshId !== meshId && !settings.launchedByCoordinator) continue;
|
|
1341
|
+
const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
1342
|
+
if (!nodeId) continue;
|
|
1343
|
+
if (state.status !== "idle" && state.status !== "stopped" && state.activeChat?.status !== "waiting_input") continue;
|
|
1344
|
+
const sessionId = state.instanceId;
|
|
1345
|
+
const providerType = state.type || readNonEmptyString(settings.providerType);
|
|
1346
|
+
if (providerType) {
|
|
1347
|
+
tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function buildMeshSystemMessage(args) {
|
|
1352
|
+
const metadata = formatCompletionMetadata(args.metadataEvent);
|
|
1353
|
+
if (args.event === "agent:generating_completed") {
|
|
1354
|
+
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.`;
|
|
1355
|
+
}
|
|
1356
|
+
if (args.event === "agent:waiting_approval") {
|
|
1357
|
+
return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
|
|
1358
|
+
}
|
|
1359
|
+
if (args.event === "agent:stopped") {
|
|
1360
|
+
const rc = args.recoveryContext;
|
|
1361
|
+
if (rc && rc.consecutiveNodeFailures > 0) {
|
|
1362
|
+
const parts = [
|
|
1363
|
+
`[System] ${args.nodeLabel} has stopped unexpectedly${metadata}.`,
|
|
1364
|
+
`
|
|
1365
|
+
|
|
1366
|
+
**Recovery Context:**`,
|
|
1367
|
+
`- Consecutive failures on this node: ${rc.consecutiveNodeFailures}`,
|
|
1368
|
+
rc.taskAttemptCount > 0 ? `- This task has been attempted ${rc.taskAttemptCount} time(s)` : "",
|
|
1369
|
+
`- Recommendation: ${rc.advice}`
|
|
1370
|
+
];
|
|
1371
|
+
if (rc.retryRecommended && rc.lastTaskMessage) {
|
|
1372
|
+
parts.push(
|
|
1373
|
+
`
|
|
1374
|
+
|
|
1375
|
+
**Original task to retry:**`,
|
|
1376
|
+
`> ${rc.lastTaskMessage.length > 300 ? rc.lastTaskMessage.slice(0, 300) + "..." : rc.lastTaskMessage}`,
|
|
1377
|
+
`
|
|
1378
|
+
To retry: call \`mesh_launch_session\` for this node, then \`mesh_send_task\` with the original task.`
|
|
1379
|
+
);
|
|
1380
|
+
} else if (!rc.retryRecommended) {
|
|
1381
|
+
parts.push(
|
|
1382
|
+
`
|
|
1383
|
+
Do NOT retry on this node. Consider reassigning to a different node or asking the user for guidance.`
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
return parts.filter(Boolean).join("\n");
|
|
1387
|
+
}
|
|
1388
|
+
return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
|
|
1389
|
+
}
|
|
1390
|
+
if (args.event === "monitor:long_generating") {
|
|
1391
|
+
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.`;
|
|
1392
|
+
}
|
|
1393
|
+
return "";
|
|
1394
|
+
}
|
|
1395
|
+
function injectMeshSystemMessage(components, args) {
|
|
1396
|
+
if (args.event === "agent:generating_completed") {
|
|
1397
|
+
const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
|
|
1398
|
+
const nodeId = readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
1399
|
+
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
1400
|
+
if (sessionId) {
|
|
1401
|
+
updateSessionTaskStatus(args.meshId, sessionId, "completed");
|
|
1402
|
+
if (nodeId && providerType) {
|
|
1403
|
+
setTimeout(() => {
|
|
1404
|
+
tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
1405
|
+
}, 500);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
} else if (args.event === "agent:stopped") {
|
|
1409
|
+
const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
|
|
1410
|
+
if (sessionId) {
|
|
1411
|
+
updateSessionTaskStatus(args.meshId, sessionId, "failed");
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
const ledgerKind = EVENT_TO_LEDGER_KIND[args.event];
|
|
1415
|
+
if (ledgerKind) {
|
|
1416
|
+
try {
|
|
1417
|
+
appendLedgerEntry(args.meshId, {
|
|
1418
|
+
kind: ledgerKind,
|
|
1419
|
+
nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
|
|
1420
|
+
sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
|
|
1421
|
+
providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
|
|
1422
|
+
payload: {
|
|
1423
|
+
event: args.event,
|
|
1424
|
+
nodeLabel: args.nodeLabel,
|
|
1425
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
} catch (e) {
|
|
1429
|
+
LOG.warn("MeshLedger", `Failed to record ${ledgerKind}: ${e?.message || e}`);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
let recoveryContext = null;
|
|
1433
|
+
if (args.event === "agent:stopped") {
|
|
1434
|
+
try {
|
|
1435
|
+
const mesh = getMesh(args.meshId);
|
|
1436
|
+
const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
|
|
1437
|
+
recoveryContext = getSessionRecoveryContext(args.meshId, {
|
|
1438
|
+
sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
|
|
1439
|
+
nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
|
|
1440
|
+
maxRetries
|
|
1441
|
+
});
|
|
1442
|
+
recoveryContext.failedProviderType = readNonEmptyString(args.metadataEvent.providerType) || null;
|
|
1443
|
+
if (recoveryContext.retryRecommended && recoveryContext.consecutiveNodeFailures > 0) {
|
|
1444
|
+
appendLedgerEntry(args.meshId, {
|
|
1445
|
+
kind: "recovery_attempted",
|
|
1446
|
+
nodeId: recoveryContext.failedNodeId || void 0,
|
|
1447
|
+
sessionId: recoveryContext.failedSessionId || void 0,
|
|
1448
|
+
providerType: recoveryContext.failedProviderType || void 0,
|
|
1449
|
+
payload: {
|
|
1450
|
+
consecutiveFailures: recoveryContext.consecutiveNodeFailures,
|
|
1451
|
+
taskAttemptCount: recoveryContext.taskAttemptCount,
|
|
1452
|
+
retryRecommended: recoveryContext.retryRecommended,
|
|
1453
|
+
advice: recoveryContext.advice
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
if (recoveryContext.lastTaskMessage && recoveryContext.failedNodeId && recoveryContext.failedProviderType) {
|
|
1457
|
+
const autoNodeId = recoveryContext.failedNodeId;
|
|
1458
|
+
try {
|
|
1459
|
+
const task = enqueueTask(args.meshId, recoveryContext.lastTaskMessage, {
|
|
1460
|
+
targetNodeId: autoNodeId
|
|
1461
|
+
});
|
|
1462
|
+
LOG.info("MeshRecovery", `Auto-requeued failed task: ${task.id} for node ${autoNodeId}`);
|
|
1463
|
+
const node = mesh?.nodes.find((n) => n.id === autoNodeId);
|
|
1464
|
+
if (node) {
|
|
1465
|
+
components.cliManager.handleCliCommand("launch_cli", {
|
|
1466
|
+
cliType: recoveryContext.failedProviderType,
|
|
1467
|
+
dir: node.workspace,
|
|
1468
|
+
settings: {
|
|
1469
|
+
meshNodeFor: args.meshId,
|
|
1470
|
+
meshNodeId: node.id,
|
|
1471
|
+
spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
|
|
1472
|
+
launchedByCoordinator: true
|
|
1473
|
+
}
|
|
1474
|
+
}).catch((e) => LOG.error("MeshRecovery", `Failed to auto-relaunch session for ${node.id}: ${e?.message}`));
|
|
1475
|
+
}
|
|
1476
|
+
} catch (e) {
|
|
1477
|
+
LOG.warn("MeshRecovery", `Failed to execute auto-recovery: ${e?.message}`);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
LOG.info("MeshRecovery", `Recovery context for ${args.nodeLabel}: ${recoveryContext.advice}`);
|
|
1482
|
+
} catch (e) {
|
|
1483
|
+
LOG.warn("MeshRecovery", `Failed to build recovery context: ${e?.message || e}`);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
const coordinatorInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
|
|
1487
|
+
const instState = inst.getState();
|
|
1488
|
+
if (instState.settings?.meshCoordinatorFor !== args.meshId) return false;
|
|
1489
|
+
if (args.sourceInstanceId && instState.instanceId === args.sourceInstanceId) return false;
|
|
1490
|
+
return true;
|
|
1491
|
+
});
|
|
1492
|
+
if (coordinatorInstances.length === 0) {
|
|
1493
|
+
if (pendingMeshCoordinatorEvents.length < MAX_PENDING_EVENTS) {
|
|
1494
|
+
pendingMeshCoordinatorEvents.push({
|
|
1495
|
+
event: args.event,
|
|
1496
|
+
meshId: args.meshId,
|
|
1497
|
+
nodeLabel: args.nodeLabel,
|
|
1498
|
+
metadataEvent: {
|
|
1499
|
+
...args.metadataEvent,
|
|
1500
|
+
...recoveryContext ? { recoveryContext } : {}
|
|
1501
|
+
},
|
|
1502
|
+
queuedAt: Date.now()
|
|
1503
|
+
});
|
|
1504
|
+
LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
|
|
1505
|
+
}
|
|
1506
|
+
return { success: true, forwarded: 0 };
|
|
1507
|
+
}
|
|
1508
|
+
const messageText = buildMeshSystemMessage({
|
|
1509
|
+
event: args.event,
|
|
1510
|
+
nodeLabel: args.nodeLabel,
|
|
1511
|
+
metadataEvent: args.metadataEvent,
|
|
1512
|
+
recoveryContext
|
|
1513
|
+
});
|
|
1514
|
+
if (!messageText) return { success: false, error: "unsupported mesh event" };
|
|
1515
|
+
for (const coord of coordinatorInstances) {
|
|
1516
|
+
const coordState = coord.getState();
|
|
1517
|
+
LOG.info("MeshEvents", `Forwarding mesh event to coordinator ${coordState.instanceId}`);
|
|
1518
|
+
coord.onEvent("send_message", { input: { text: messageText, textFallback: messageText } });
|
|
1519
|
+
}
|
|
1520
|
+
return { success: true, forwarded: coordinatorInstances.length };
|
|
1521
|
+
}
|
|
1522
|
+
function handleMeshForwardEvent(components, payload) {
|
|
1523
|
+
const eventName = readNonEmptyString(payload.event);
|
|
1524
|
+
if (!isMeshCoordinatorEvent(eventName)) {
|
|
1525
|
+
return { success: false, error: "unsupported mesh event" };
|
|
1526
|
+
}
|
|
1527
|
+
const meshId = readNonEmptyString(payload.meshId);
|
|
1528
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
1529
|
+
const nodeId = readNonEmptyString(payload.nodeId);
|
|
1530
|
+
const workspace = readNonEmptyString(payload.workspace);
|
|
1531
|
+
const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
|
|
1532
|
+
return injectMeshSystemMessage(components, {
|
|
1533
|
+
meshId,
|
|
1534
|
+
nodeLabel,
|
|
1535
|
+
event: eventName,
|
|
1536
|
+
metadataEvent: {
|
|
1537
|
+
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
|
|
1538
|
+
providerType: readNonEmptyString(payload.providerType),
|
|
1539
|
+
providerSessionId: readNonEmptyString(payload.providerSessionId)
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
function setupMeshEventForwarding(components) {
|
|
1544
|
+
components.instanceManager.onEvent((event) => {
|
|
1545
|
+
if (!isMeshCoordinatorEvent(event.event)) return;
|
|
1546
|
+
const instanceId = readNonEmptyString(event.instanceId);
|
|
1547
|
+
if (!instanceId) return;
|
|
1548
|
+
const sourceInstance = components.instanceManager.getInstance(instanceId);
|
|
1549
|
+
if (!sourceInstance || sourceInstance.category !== "cli") return;
|
|
1550
|
+
const state = sourceInstance.getState();
|
|
1551
|
+
const workspace = readNonEmptyString(state.workspace);
|
|
1552
|
+
if (!workspace) return;
|
|
1553
|
+
const settings = state.settings && typeof state.settings === "object" ? state.settings : {};
|
|
1554
|
+
if (readNonEmptyString(settings.meshCoordinatorFor)) return;
|
|
1555
|
+
const meshIdFromRuntime = readNonEmptyString(settings.meshNodeFor);
|
|
1556
|
+
const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
|
|
1557
|
+
if (!isMeshDelegate) return;
|
|
1558
|
+
const mesh = meshIdFromRuntime ? getMesh(meshIdFromRuntime) : getMeshByRepo(workspace);
|
|
1559
|
+
const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
|
|
1560
|
+
if (!meshId) return;
|
|
1561
|
+
const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
|
|
1562
|
+
const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
|
|
1563
|
+
const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
|
|
1564
|
+
injectMeshSystemMessage(components, {
|
|
1565
|
+
meshId,
|
|
1566
|
+
sourceInstanceId: instanceId,
|
|
1567
|
+
nodeLabel,
|
|
1568
|
+
event: event.event,
|
|
1569
|
+
metadataEvent: event
|
|
1570
|
+
});
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
var MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
|
|
1574
|
+
var init_mesh_events = __esm({
|
|
1575
|
+
"src/mesh/mesh-events.ts"() {
|
|
1576
|
+
"use strict";
|
|
1577
|
+
init_mesh_config();
|
|
1578
|
+
init_logger();
|
|
1579
|
+
init_mesh_ledger();
|
|
1580
|
+
init_mesh_work_queue();
|
|
1581
|
+
MAX_PENDING_EVENTS = 50;
|
|
1582
|
+
pendingMeshCoordinatorEvents = [];
|
|
1583
|
+
MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
|
|
1584
|
+
"agent:generating_completed",
|
|
1585
|
+
"agent:waiting_approval",
|
|
1586
|
+
"agent:stopped",
|
|
1587
|
+
"monitor:long_generating"
|
|
1588
|
+
]);
|
|
1589
|
+
EVENT_TO_LEDGER_KIND = {
|
|
1590
|
+
"agent:generating_completed": "task_completed",
|
|
1591
|
+
"agent:waiting_approval": "task_approval_needed",
|
|
1592
|
+
"agent:stopped": "task_failed",
|
|
1593
|
+
"monitor:long_generating": "task_stalled"
|
|
1594
|
+
};
|
|
938
1595
|
}
|
|
939
1596
|
});
|
|
940
1597
|
|
|
@@ -2042,6 +2699,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
2042
2699
|
statusHistory = [];
|
|
2043
2700
|
// ─── CLI Scripts (script-based parsing) ───
|
|
2044
2701
|
cliScripts;
|
|
2702
|
+
/** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
|
|
2703
|
+
scriptState = null;
|
|
2045
2704
|
runtimeSettings = {};
|
|
2046
2705
|
/** Full accumulated rendered PTY transcript for parser/readback use */
|
|
2047
2706
|
accumulatedBuffer = "";
|
|
@@ -2223,6 +2882,7 @@ ${lastSnapshot}`;
|
|
|
2223
2882
|
this.cliScripts = scripts;
|
|
2224
2883
|
this.parsedStatusCache = null;
|
|
2225
2884
|
this.parseErrorMessage = null;
|
|
2885
|
+
this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
|
|
2226
2886
|
const scriptNames = listCliScriptNames(scripts);
|
|
2227
2887
|
LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
|
|
2228
2888
|
}
|
|
@@ -2340,6 +3000,7 @@ ${lastSnapshot}`;
|
|
|
2340
3000
|
this.ready = false;
|
|
2341
3001
|
this.startupParseGate = false;
|
|
2342
3002
|
this.spawnAt = 0;
|
|
3003
|
+
this.scriptState = null;
|
|
2343
3004
|
this.onStatusChange?.();
|
|
2344
3005
|
});
|
|
2345
3006
|
this.spawnAt = Date.now();
|
|
@@ -3113,7 +3774,7 @@ ${lastSnapshot}`;
|
|
|
3113
3774
|
scope: this.currentTurnScope,
|
|
3114
3775
|
runtimeSettings: this.runtimeSettings
|
|
3115
3776
|
});
|
|
3116
|
-
const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
|
|
3777
|
+
const session = this.cliScripts.parseSession(this.scriptState, { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
|
|
3117
3778
|
this.parseErrorMessage = null;
|
|
3118
3779
|
return session && typeof session === "object" ? session : null;
|
|
3119
3780
|
} catch (e) {
|
|
@@ -3127,7 +3788,7 @@ ${lastSnapshot}`;
|
|
|
3127
3788
|
if (!this.cliScripts?.detectStatus) return null;
|
|
3128
3789
|
try {
|
|
3129
3790
|
const screenText = this.terminalScreen.getText();
|
|
3130
|
-
const status = this.cliScripts.detectStatus({
|
|
3791
|
+
const status = this.cliScripts.detectStatus(this.scriptState, {
|
|
3131
3792
|
tail: text.slice(-500),
|
|
3132
3793
|
screenText,
|
|
3133
3794
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -3146,7 +3807,7 @@ ${lastSnapshot}`;
|
|
|
3146
3807
|
try {
|
|
3147
3808
|
const screenText = this.terminalScreen.getText();
|
|
3148
3809
|
const buffer = screenText || this.accumulatedBuffer;
|
|
3149
|
-
return this.cliScripts.parseApproval({
|
|
3810
|
+
return this.cliScripts.parseApproval(this.scriptState, {
|
|
3150
3811
|
buffer,
|
|
3151
3812
|
screenText,
|
|
3152
3813
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -3254,7 +3915,7 @@ ${lastSnapshot}`;
|
|
|
3254
3915
|
scope: this.currentTurnScope,
|
|
3255
3916
|
runtimeSettings: this.runtimeSettings
|
|
3256
3917
|
});
|
|
3257
|
-
return await Promise.resolve(fn({
|
|
3918
|
+
return await Promise.resolve(fn(this.scriptState, {
|
|
3258
3919
|
...input,
|
|
3259
3920
|
args: args && typeof args === "object" ? { ...args } : {}
|
|
3260
3921
|
}));
|
|
@@ -4047,6 +4708,7 @@ __export(index_exports, {
|
|
|
4047
4708
|
TurnSnapshotTracker: () => TurnSnapshotTracker,
|
|
4048
4709
|
VersionArchive: () => VersionArchive,
|
|
4049
4710
|
addNode: () => addNode,
|
|
4711
|
+
appendLedgerEntry: () => appendLedgerEntry,
|
|
4050
4712
|
appendRecentActivity: () => appendRecentActivity,
|
|
4051
4713
|
buildAssistantChatMessage: () => buildAssistantChatMessage,
|
|
4052
4714
|
buildChatMessage: () => buildChatMessage,
|
|
@@ -4064,6 +4726,7 @@ __export(index_exports, {
|
|
|
4064
4726
|
buildThoughtChatMessage: () => buildThoughtChatMessage,
|
|
4065
4727
|
buildToolChatMessage: () => buildToolChatMessage,
|
|
4066
4728
|
buildUserChatMessage: () => buildUserChatMessage,
|
|
4729
|
+
claimNextTask: () => claimNextTask,
|
|
4067
4730
|
classifyChatMessageVisibility: () => classifyChatMessageVisibility,
|
|
4068
4731
|
classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush,
|
|
4069
4732
|
clearDebugTrace: () => clearDebugTrace,
|
|
@@ -4082,6 +4745,7 @@ __export(index_exports, {
|
|
|
4082
4745
|
detectAllVersions: () => detectAllVersions,
|
|
4083
4746
|
detectCLIs: () => detectCLIs,
|
|
4084
4747
|
detectIDEs: () => detectIDEs,
|
|
4748
|
+
enqueueTask: () => enqueueTask,
|
|
4085
4749
|
ensureSessionHostReady: () => ensureSessionHostReady,
|
|
4086
4750
|
execNpmCommandSync: () => execNpmCommandSync,
|
|
4087
4751
|
filterActivityChatMessages: () => filterActivityChatMessages,
|
|
@@ -4100,10 +4764,13 @@ __export(index_exports, {
|
|
|
4100
4764
|
getGitFileDiff: () => getGitFileDiff,
|
|
4101
4765
|
getGitRepoStatus: () => getGitRepoStatus,
|
|
4102
4766
|
getHostMemorySnapshot: () => getHostMemorySnapshot,
|
|
4767
|
+
getLedgerDir: () => getLedgerDir,
|
|
4768
|
+
getLedgerSummary: () => getLedgerSummary,
|
|
4103
4769
|
getLogLevel: () => getLogLevel,
|
|
4104
4770
|
getMesh: () => getMesh,
|
|
4105
4771
|
getMeshByRepo: () => getMeshByRepo,
|
|
4106
4772
|
getNpmExecOptions: () => getNpmExecOptions,
|
|
4773
|
+
getQueue: () => getQueue,
|
|
4107
4774
|
getRecentActivity: () => getRecentActivity,
|
|
4108
4775
|
getRecentCommands: () => getRecentCommands,
|
|
4109
4776
|
getRecentDebugTrace: () => getRecentDebugTrace,
|
|
@@ -4111,6 +4778,7 @@ __export(index_exports, {
|
|
|
4111
4778
|
getSavedProviderSessions: () => getSavedProviderSessions,
|
|
4112
4779
|
getSessionHostRecoveryLabel: () => getSessionHostRecoveryLabel,
|
|
4113
4780
|
getSessionHostSurfaceKind: () => getSessionHostSurfaceKind,
|
|
4781
|
+
getSessionRecoveryContext: () => getSessionRecoveryContext,
|
|
4114
4782
|
getWorkspaceState: () => getWorkspaceState,
|
|
4115
4783
|
handleGitCommand: () => handleGitCommand,
|
|
4116
4784
|
hasCdpManager: () => hasCdpManager,
|
|
@@ -4164,6 +4832,7 @@ __export(index_exports, {
|
|
|
4164
4832
|
prepareSessionModalUpdate: () => prepareSessionModalUpdate,
|
|
4165
4833
|
probeCdpPort: () => probeCdpPort,
|
|
4166
4834
|
readChatHistory: () => readChatHistory,
|
|
4835
|
+
readLedgerEntries: () => readLedgerEntries,
|
|
4167
4836
|
recordDebugTrace: () => recordDebugTrace,
|
|
4168
4837
|
registerExtensionProviders: () => registerExtensionProviders,
|
|
4169
4838
|
removeNode: () => removeNode,
|
|
@@ -4192,9 +4861,12 @@ __export(index_exports, {
|
|
|
4192
4861
|
startDaemonDevSupport: () => startDaemonDevSupport,
|
|
4193
4862
|
summarizeGitStatus: () => summarizeGitStatus,
|
|
4194
4863
|
syncMeshes: () => syncMeshes,
|
|
4864
|
+
triggerMeshQueue: () => triggerMeshQueue,
|
|
4195
4865
|
updateConfig: () => updateConfig,
|
|
4196
4866
|
updateMesh: () => updateMesh,
|
|
4197
4867
|
updateNode: () => updateNode,
|
|
4868
|
+
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
4869
|
+
updateTaskStatus: () => updateTaskStatus,
|
|
4198
4870
|
upsertSavedProviderSession: () => upsertSavedProviderSession
|
|
4199
4871
|
});
|
|
4200
4872
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -6061,12 +6733,35 @@ async function syncMeshes(transport) {
|
|
|
6061
6733
|
}
|
|
6062
6734
|
}
|
|
6063
6735
|
}
|
|
6736
|
+
if (transport.syncMeshLedger) {
|
|
6737
|
+
for (const local of localMeshes) {
|
|
6738
|
+
try {
|
|
6739
|
+
await syncMeshLedger(local.id, transport);
|
|
6740
|
+
} catch (e) {
|
|
6741
|
+
result.errors.push(`Ledger sync failed for "${local.name}": ${e.message}`);
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6064
6745
|
return result;
|
|
6065
6746
|
}
|
|
6747
|
+
async function syncMeshLedger(meshId, transport) {
|
|
6748
|
+
if (!transport.syncMeshLedger) return;
|
|
6749
|
+
const { readLedgerEntries: readLedgerEntries2, appendRemoteLedgerEntries: appendRemoteLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
6750
|
+
const localEntries = readLedgerEntries2(meshId);
|
|
6751
|
+
const res = await transport.syncMeshLedger(meshId, { newEntries: localEntries });
|
|
6752
|
+
if (res.missingEntries && res.missingEntries.length > 0) {
|
|
6753
|
+
appendRemoteLedgerEntries2(meshId, res.missingEntries);
|
|
6754
|
+
}
|
|
6755
|
+
}
|
|
6756
|
+
|
|
6757
|
+
// src/index.ts
|
|
6758
|
+
init_mesh_ledger();
|
|
6759
|
+
init_mesh_work_queue();
|
|
6760
|
+
init_mesh_events();
|
|
6066
6761
|
|
|
6067
6762
|
// src/config/state-store.ts
|
|
6068
|
-
var
|
|
6069
|
-
var
|
|
6763
|
+
var import_fs5 = require("fs");
|
|
6764
|
+
var import_path5 = require("path");
|
|
6070
6765
|
init_config();
|
|
6071
6766
|
var DEFAULT_STATE = {
|
|
6072
6767
|
recentActivity: [],
|
|
@@ -6080,7 +6775,7 @@ function isPlainObject2(value) {
|
|
|
6080
6775
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
6081
6776
|
}
|
|
6082
6777
|
function getStatePath() {
|
|
6083
|
-
return (0,
|
|
6778
|
+
return (0, import_path5.join)(getConfigDir(), "state.json");
|
|
6084
6779
|
}
|
|
6085
6780
|
function normalizeState(raw) {
|
|
6086
6781
|
const parsed = isPlainObject2(raw) ? raw : {};
|
|
@@ -6116,11 +6811,11 @@ function normalizeState(raw) {
|
|
|
6116
6811
|
}
|
|
6117
6812
|
function loadState() {
|
|
6118
6813
|
const statePath = getStatePath();
|
|
6119
|
-
if (!(0,
|
|
6814
|
+
if (!(0, import_fs5.existsSync)(statePath)) {
|
|
6120
6815
|
return { ...DEFAULT_STATE };
|
|
6121
6816
|
}
|
|
6122
6817
|
try {
|
|
6123
|
-
const raw = (0,
|
|
6818
|
+
const raw = (0, import_fs5.readFileSync)(statePath, "utf-8");
|
|
6124
6819
|
return normalizeState(JSON.parse(raw));
|
|
6125
6820
|
} catch {
|
|
6126
6821
|
return { ...DEFAULT_STATE };
|
|
@@ -6129,7 +6824,7 @@ function loadState() {
|
|
|
6129
6824
|
function saveState(state) {
|
|
6130
6825
|
const statePath = getStatePath();
|
|
6131
6826
|
const normalized = normalizeState(state);
|
|
6132
|
-
(0,
|
|
6827
|
+
(0, import_fs5.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
|
|
6133
6828
|
}
|
|
6134
6829
|
function resetState() {
|
|
6135
6830
|
saveState({ ...DEFAULT_STATE });
|
|
@@ -6137,9 +6832,9 @@ function resetState() {
|
|
|
6137
6832
|
|
|
6138
6833
|
// src/detection/ide-detector.ts
|
|
6139
6834
|
var import_child_process = require("child_process");
|
|
6140
|
-
var
|
|
6835
|
+
var import_fs6 = require("fs");
|
|
6141
6836
|
var import_os2 = require("os");
|
|
6142
|
-
var
|
|
6837
|
+
var path9 = __toESM(require("path"));
|
|
6143
6838
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
6144
6839
|
var registeredIDEs = /* @__PURE__ */ new Map();
|
|
6145
6840
|
function registerIDEDefinition(def) {
|
|
@@ -6158,10 +6853,10 @@ function getMergedDefinitions() {
|
|
|
6158
6853
|
function findCliCommand(command) {
|
|
6159
6854
|
const trimmed = String(command || "").trim();
|
|
6160
6855
|
if (!trimmed) return null;
|
|
6161
|
-
if (
|
|
6162
|
-
const candidate = trimmed.startsWith("~") ?
|
|
6163
|
-
const resolved =
|
|
6164
|
-
return (0,
|
|
6856
|
+
if (path9.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
6857
|
+
const candidate = trimmed.startsWith("~") ? path9.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
|
|
6858
|
+
const resolved = path9.isAbsolute(candidate) ? candidate : path9.resolve(candidate);
|
|
6859
|
+
return (0, import_fs6.existsSync)(resolved) ? resolved : null;
|
|
6165
6860
|
}
|
|
6166
6861
|
try {
|
|
6167
6862
|
const result = (0, import_child_process.execSync)(
|
|
@@ -6188,13 +6883,13 @@ function getIdeVersion(cliCommand) {
|
|
|
6188
6883
|
function checkPathExists(paths) {
|
|
6189
6884
|
const home = (0, import_os2.homedir)();
|
|
6190
6885
|
for (const p of paths) {
|
|
6191
|
-
const normalized = p.startsWith("~") ?
|
|
6886
|
+
const normalized = p.startsWith("~") ? path9.join(home, p.slice(1)) : p;
|
|
6192
6887
|
if (normalized.includes("*")) {
|
|
6193
6888
|
const username = home.split(/[\\/]/).pop() || "";
|
|
6194
6889
|
const resolved = normalized.replace("*", username);
|
|
6195
|
-
if ((0,
|
|
6890
|
+
if ((0, import_fs6.existsSync)(resolved)) return resolved;
|
|
6196
6891
|
} else {
|
|
6197
|
-
if ((0,
|
|
6892
|
+
if ((0, import_fs6.existsSync)(normalized)) return normalized;
|
|
6198
6893
|
}
|
|
6199
6894
|
}
|
|
6200
6895
|
return null;
|
|
@@ -6208,7 +6903,7 @@ async function detectIDEs(providerLoader) {
|
|
|
6208
6903
|
let resolvedCli = cliPath;
|
|
6209
6904
|
if (!resolvedCli && appPath && os22 === "darwin") {
|
|
6210
6905
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
6211
|
-
if ((0,
|
|
6906
|
+
if ((0, import_fs6.existsSync)(bundledCli)) resolvedCli = bundledCli;
|
|
6212
6907
|
}
|
|
6213
6908
|
if (!resolvedCli && appPath && os22 === "win32") {
|
|
6214
6909
|
const { dirname: dirname9 } = await import("path");
|
|
@@ -6221,7 +6916,7 @@ async function detectIDEs(providerLoader) {
|
|
|
6221
6916
|
`${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
|
|
6222
6917
|
];
|
|
6223
6918
|
for (const c of candidates) {
|
|
6224
|
-
if ((0,
|
|
6919
|
+
if ((0, import_fs6.existsSync)(c)) {
|
|
6225
6920
|
resolvedCli = c;
|
|
6226
6921
|
break;
|
|
6227
6922
|
}
|
|
@@ -6245,9 +6940,9 @@ async function detectIDEs(providerLoader) {
|
|
|
6245
6940
|
|
|
6246
6941
|
// src/detection/cli-detector.ts
|
|
6247
6942
|
var import_child_process2 = require("child_process");
|
|
6248
|
-
var
|
|
6249
|
-
var
|
|
6250
|
-
var
|
|
6943
|
+
var os3 = __toESM(require("os"));
|
|
6944
|
+
var path10 = __toESM(require("path"));
|
|
6945
|
+
var import_fs7 = require("fs");
|
|
6251
6946
|
function parseVersion(raw) {
|
|
6252
6947
|
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
6253
6948
|
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
@@ -6259,19 +6954,19 @@ function shellQuote(value) {
|
|
|
6259
6954
|
function expandHome(value) {
|
|
6260
6955
|
const trimmed = value.trim();
|
|
6261
6956
|
if (!trimmed.startsWith("~")) return trimmed;
|
|
6262
|
-
return
|
|
6957
|
+
return path10.join(os3.homedir(), trimmed.slice(1));
|
|
6263
6958
|
}
|
|
6264
6959
|
function isExplicitCommandPath(command) {
|
|
6265
6960
|
const trimmed = command.trim();
|
|
6266
|
-
return
|
|
6961
|
+
return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
6267
6962
|
}
|
|
6268
6963
|
function resolveCommandPath(command) {
|
|
6269
6964
|
const trimmed = command.trim();
|
|
6270
6965
|
if (!trimmed) return null;
|
|
6271
6966
|
if (isExplicitCommandPath(trimmed)) {
|
|
6272
6967
|
const expanded = expandHome(trimmed);
|
|
6273
|
-
const candidate =
|
|
6274
|
-
return (0,
|
|
6968
|
+
const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
|
|
6969
|
+
return (0, import_fs7.existsSync)(candidate) ? candidate : null;
|
|
6275
6970
|
}
|
|
6276
6971
|
return null;
|
|
6277
6972
|
}
|
|
@@ -6292,7 +6987,7 @@ function execAsync(cmd, timeoutMs = 5e3) {
|
|
|
6292
6987
|
});
|
|
6293
6988
|
}
|
|
6294
6989
|
async function detectCLIs(providerLoader, options) {
|
|
6295
|
-
const platform10 =
|
|
6990
|
+
const platform10 = os3.platform();
|
|
6296
6991
|
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
6297
6992
|
const includeVersion = options?.includeVersion !== false;
|
|
6298
6993
|
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
@@ -6336,7 +7031,7 @@ async function detectCLI(cliId, providerLoader, options) {
|
|
|
6336
7031
|
const cliList = providerLoader.getCliDetectionList();
|
|
6337
7032
|
const target = cliList.find((c) => c.id === resolvedId);
|
|
6338
7033
|
if (target) {
|
|
6339
|
-
const platform10 =
|
|
7034
|
+
const platform10 = os3.platform();
|
|
6340
7035
|
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
6341
7036
|
try {
|
|
6342
7037
|
const explicitPath = resolveCommandPath(target.command);
|
|
@@ -6373,10 +7068,10 @@ async function detectCLI(cliId, providerLoader, options) {
|
|
|
6373
7068
|
}
|
|
6374
7069
|
|
|
6375
7070
|
// src/system/host-memory.ts
|
|
6376
|
-
var
|
|
7071
|
+
var os4 = __toESM(require("os"));
|
|
6377
7072
|
var import_child_process3 = require("child_process");
|
|
6378
7073
|
function parseDarwinAvailableBytes(totalMem) {
|
|
6379
|
-
if (
|
|
7074
|
+
if (os4.platform() !== "darwin") return null;
|
|
6380
7075
|
try {
|
|
6381
7076
|
const out = (0, import_child_process3.execSync)("vm_stat", {
|
|
6382
7077
|
encoding: "utf-8",
|
|
@@ -6407,8 +7102,8 @@ function parseDarwinAvailableBytes(totalMem) {
|
|
|
6407
7102
|
}
|
|
6408
7103
|
}
|
|
6409
7104
|
function getHostMemorySnapshot() {
|
|
6410
|
-
const totalMem =
|
|
6411
|
-
const freeMem =
|
|
7105
|
+
const totalMem = os4.totalmem();
|
|
7106
|
+
const freeMem = os4.freemem();
|
|
6412
7107
|
const darwinAvail = parseDarwinAvailableBytes(totalMem);
|
|
6413
7108
|
const availableMem = darwinAvail != null ? darwinAvail : freeMem;
|
|
6414
7109
|
return { totalMem, freeMem, availableMem };
|
|
@@ -11778,6 +12473,9 @@ function normalizeActiveChatData(activeChat, options = FULL_STATUS_ACTIVE_CHAT_O
|
|
|
11778
12473
|
return normalized;
|
|
11779
12474
|
}
|
|
11780
12475
|
|
|
12476
|
+
// src/status/builders.ts
|
|
12477
|
+
init_mesh_work_queue();
|
|
12478
|
+
|
|
11781
12479
|
// src/providers/provider-input-support.ts
|
|
11782
12480
|
var VALID_INPUT_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
|
|
11783
12481
|
var VALID_INPUT_STRATEGIES = /* @__PURE__ */ new Set(["native", "native_acp", "resource_link", "text_fallback", "paste", "upload"]);
|
|
@@ -12003,6 +12701,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
|
|
|
12003
12701
|
const workspace = state.workspace || null;
|
|
12004
12702
|
const git = getGitSummaryForWorkspace(workspace, options);
|
|
12005
12703
|
const title = activeChat?.title || state.name;
|
|
12704
|
+
const meshCoordinatorFor = state.settings?.meshCoordinatorFor;
|
|
12705
|
+
const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
|
|
12006
12706
|
return {
|
|
12007
12707
|
id: state.instanceId || state.type,
|
|
12008
12708
|
parentId: null,
|
|
@@ -12025,7 +12725,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
|
|
|
12025
12725
|
errorMessage: state.errorMessage,
|
|
12026
12726
|
errorReason: state.errorReason,
|
|
12027
12727
|
lastUpdated: state.lastUpdated,
|
|
12028
|
-
settings: state.settings
|
|
12728
|
+
settings: state.settings,
|
|
12729
|
+
...meshQueueStats && { meshQueueStats }
|
|
12029
12730
|
};
|
|
12030
12731
|
}
|
|
12031
12732
|
function buildExtensionAgentSession(parent, ext, options) {
|
|
@@ -12037,6 +12738,8 @@ function buildExtensionAgentSession(parent, ext, options) {
|
|
|
12037
12738
|
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
12038
12739
|
const workspace = parent.workspace || null;
|
|
12039
12740
|
const git = getGitSummaryForWorkspace(workspace, options);
|
|
12741
|
+
const meshCoordinatorFor = ext.settings?.meshCoordinatorFor;
|
|
12742
|
+
const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
|
|
12040
12743
|
return {
|
|
12041
12744
|
id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
|
|
12042
12745
|
parentId: parent.instanceId || parent.type,
|
|
@@ -12059,7 +12762,8 @@ function buildExtensionAgentSession(parent, ext, options) {
|
|
|
12059
12762
|
errorMessage: ext.errorMessage,
|
|
12060
12763
|
errorReason: ext.errorReason,
|
|
12061
12764
|
lastUpdated: ext.lastUpdated,
|
|
12062
|
-
settings: ext.settings
|
|
12765
|
+
settings: ext.settings,
|
|
12766
|
+
...meshQueueStats && { meshQueueStats }
|
|
12063
12767
|
};
|
|
12064
12768
|
}
|
|
12065
12769
|
function shouldIncludeExtensionSession(ext) {
|
|
@@ -12087,6 +12791,8 @@ function buildCliSession(state, options) {
|
|
|
12087
12791
|
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
12088
12792
|
const workspace = state.workspace || null;
|
|
12089
12793
|
const git = getGitSummaryForWorkspace(workspace, options);
|
|
12794
|
+
const meshCoordinatorFor = state.settings?.meshCoordinatorFor;
|
|
12795
|
+
const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
|
|
12090
12796
|
return {
|
|
12091
12797
|
id: state.instanceId,
|
|
12092
12798
|
parentId: null,
|
|
@@ -12125,7 +12831,8 @@ function buildCliSession(state, options) {
|
|
|
12125
12831
|
errorMessage: state.errorMessage,
|
|
12126
12832
|
errorReason: state.errorReason,
|
|
12127
12833
|
lastUpdated: state.lastUpdated,
|
|
12128
|
-
settings: state.settings
|
|
12834
|
+
settings: state.settings,
|
|
12835
|
+
...meshQueueStats && { meshQueueStats }
|
|
12129
12836
|
};
|
|
12130
12837
|
}
|
|
12131
12838
|
function buildAcpSession(state, options) {
|
|
@@ -12137,6 +12844,8 @@ function buildAcpSession(state, options) {
|
|
|
12137
12844
|
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
12138
12845
|
const workspace = state.workspace || null;
|
|
12139
12846
|
const git = getGitSummaryForWorkspace(workspace, options);
|
|
12847
|
+
const meshCoordinatorFor = state.settings?.meshCoordinatorFor;
|
|
12848
|
+
const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : void 0;
|
|
12140
12849
|
return {
|
|
12141
12850
|
id: state.instanceId,
|
|
12142
12851
|
parentId: null,
|
|
@@ -12158,7 +12867,8 @@ function buildAcpSession(state, options) {
|
|
|
12158
12867
|
errorMessage: state.errorMessage,
|
|
12159
12868
|
errorReason: state.errorReason,
|
|
12160
12869
|
lastUpdated: state.lastUpdated,
|
|
12161
|
-
settings: state.settings
|
|
12870
|
+
settings: state.settings,
|
|
12871
|
+
...meshQueueStats && { meshQueueStats }
|
|
12162
12872
|
};
|
|
12163
12873
|
}
|
|
12164
12874
|
function buildSessionEntries(allStates, cdpManagers, options = {}) {
|
|
@@ -15459,7 +16169,7 @@ var DaemonCommandHandler = class {
|
|
|
15459
16169
|
var os13 = __toESM(require("os"));
|
|
15460
16170
|
var path18 = __toESM(require("path"));
|
|
15461
16171
|
var crypto4 = __toESM(require("crypto"));
|
|
15462
|
-
var
|
|
16172
|
+
var import_fs8 = require("fs");
|
|
15463
16173
|
var import_child_process6 = require("child_process");
|
|
15464
16174
|
var import_chalk = __toESM(require("chalk"));
|
|
15465
16175
|
init_provider_cli_adapter();
|
|
@@ -17837,7 +18547,7 @@ function commandExists(command) {
|
|
|
17837
18547
|
const trimmed = command.trim();
|
|
17838
18548
|
if (!trimmed) return false;
|
|
17839
18549
|
if (isExplicitCommand(trimmed)) {
|
|
17840
|
-
return (0,
|
|
18550
|
+
return (0, import_fs8.existsSync)(expandExecutable(trimmed));
|
|
17841
18551
|
}
|
|
17842
18552
|
try {
|
|
17843
18553
|
(0, import_child_process6.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
|
|
@@ -17866,10 +18576,10 @@ function hasCliArg(args, flag) {
|
|
|
17866
18576
|
}
|
|
17867
18577
|
function ensureEmptyDelegatedMcpConfig(workspace) {
|
|
17868
18578
|
const baseDir = path18.join(os13.tmpdir(), "adhdev-delegated-agent-empty-mcp");
|
|
17869
|
-
(0,
|
|
18579
|
+
(0, import_fs8.mkdirSync)(baseDir, { recursive: true });
|
|
17870
18580
|
const workspaceHash = crypto4.createHash("sha256").update(path18.resolve(workspace || os13.tmpdir())).digest("hex").slice(0, 16);
|
|
17871
18581
|
const filePath = path18.join(baseDir, `${workspaceHash}.json`);
|
|
17872
|
-
(0,
|
|
18582
|
+
(0, import_fs8.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
|
|
17873
18583
|
return filePath;
|
|
17874
18584
|
}
|
|
17875
18585
|
function buildCoordinatorDelegatedCliLaunchOptions(input) {
|
|
@@ -21363,134 +22073,8 @@ function normalizeExistingPath(filePath) {
|
|
|
21363
22073
|
}
|
|
21364
22074
|
}
|
|
21365
22075
|
|
|
21366
|
-
// src/
|
|
21367
|
-
|
|
21368
|
-
init_logger();
|
|
21369
|
-
var MAX_PENDING_EVENTS = 50;
|
|
21370
|
-
var pendingMeshCoordinatorEvents = [];
|
|
21371
|
-
function drainPendingMeshCoordinatorEvents() {
|
|
21372
|
-
return pendingMeshCoordinatorEvents.splice(0);
|
|
21373
|
-
}
|
|
21374
|
-
function readNonEmptyString(value) {
|
|
21375
|
-
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
21376
|
-
}
|
|
21377
|
-
var MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
|
|
21378
|
-
"agent:generating_completed",
|
|
21379
|
-
"agent:waiting_approval",
|
|
21380
|
-
"agent:stopped",
|
|
21381
|
-
"monitor:long_generating"
|
|
21382
|
-
]);
|
|
21383
|
-
function isMeshCoordinatorEvent(eventName) {
|
|
21384
|
-
return typeof eventName === "string" && MESH_COORDINATOR_EVENTS.has(eventName);
|
|
21385
|
-
}
|
|
21386
|
-
function formatCompletionMetadata(event) {
|
|
21387
|
-
const parts = [
|
|
21388
|
-
readNonEmptyString(event.targetSessionId) ? `session_id=${readNonEmptyString(event.targetSessionId)}` : "",
|
|
21389
|
-
readNonEmptyString(event.providerType) ? `provider=${readNonEmptyString(event.providerType)}` : "",
|
|
21390
|
-
readNonEmptyString(event.providerSessionId) ? `provider_session_id=${readNonEmptyString(event.providerSessionId)}` : ""
|
|
21391
|
-
].filter(Boolean);
|
|
21392
|
-
return parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
|
21393
|
-
}
|
|
21394
|
-
function buildMeshSystemMessage(args) {
|
|
21395
|
-
const metadata = formatCompletionMetadata(args.metadataEvent);
|
|
21396
|
-
if (args.event === "agent:generating_completed") {
|
|
21397
|
-
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.`;
|
|
21398
|
-
}
|
|
21399
|
-
if (args.event === "agent:waiting_approval") {
|
|
21400
|
-
return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
|
|
21401
|
-
}
|
|
21402
|
-
if (args.event === "agent:stopped") {
|
|
21403
|
-
return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
|
|
21404
|
-
}
|
|
21405
|
-
if (args.event === "monitor:long_generating") {
|
|
21406
|
-
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.`;
|
|
21407
|
-
}
|
|
21408
|
-
return "";
|
|
21409
|
-
}
|
|
21410
|
-
function injectMeshSystemMessage(components, args) {
|
|
21411
|
-
const coordinatorInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
|
|
21412
|
-
const instState = inst.getState();
|
|
21413
|
-
if (instState.settings?.meshCoordinatorFor !== args.meshId) return false;
|
|
21414
|
-
if (args.sourceInstanceId && instState.instanceId === args.sourceInstanceId) return false;
|
|
21415
|
-
return true;
|
|
21416
|
-
});
|
|
21417
|
-
if (coordinatorInstances.length === 0) {
|
|
21418
|
-
if (pendingMeshCoordinatorEvents.length < MAX_PENDING_EVENTS) {
|
|
21419
|
-
pendingMeshCoordinatorEvents.push({
|
|
21420
|
-
event: args.event,
|
|
21421
|
-
meshId: args.meshId,
|
|
21422
|
-
nodeLabel: args.nodeLabel,
|
|
21423
|
-
metadataEvent: args.metadataEvent,
|
|
21424
|
-
queuedAt: Date.now()
|
|
21425
|
-
});
|
|
21426
|
-
LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
|
|
21427
|
-
}
|
|
21428
|
-
return { success: true, forwarded: 0 };
|
|
21429
|
-
}
|
|
21430
|
-
const messageText = buildMeshSystemMessage({
|
|
21431
|
-
event: args.event,
|
|
21432
|
-
nodeLabel: args.nodeLabel,
|
|
21433
|
-
metadataEvent: args.metadataEvent
|
|
21434
|
-
});
|
|
21435
|
-
if (!messageText) return { success: false, error: "unsupported mesh event" };
|
|
21436
|
-
for (const coord of coordinatorInstances) {
|
|
21437
|
-
const coordState = coord.getState();
|
|
21438
|
-
LOG.info("MeshEvents", `Forwarding mesh event to coordinator ${coordState.instanceId}`);
|
|
21439
|
-
coord.onEvent("send_message", { input: { text: messageText, textFallback: messageText } });
|
|
21440
|
-
}
|
|
21441
|
-
return { success: true, forwarded: coordinatorInstances.length };
|
|
21442
|
-
}
|
|
21443
|
-
function handleMeshForwardEvent(components, payload) {
|
|
21444
|
-
const eventName = readNonEmptyString(payload.event);
|
|
21445
|
-
if (!isMeshCoordinatorEvent(eventName)) {
|
|
21446
|
-
return { success: false, error: "unsupported mesh event" };
|
|
21447
|
-
}
|
|
21448
|
-
const meshId = readNonEmptyString(payload.meshId);
|
|
21449
|
-
if (!meshId) return { success: false, error: "meshId required" };
|
|
21450
|
-
const nodeId = readNonEmptyString(payload.nodeId);
|
|
21451
|
-
const workspace = readNonEmptyString(payload.workspace);
|
|
21452
|
-
const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
|
|
21453
|
-
return injectMeshSystemMessage(components, {
|
|
21454
|
-
meshId,
|
|
21455
|
-
nodeLabel,
|
|
21456
|
-
event: eventName,
|
|
21457
|
-
metadataEvent: {
|
|
21458
|
-
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
|
|
21459
|
-
providerType: readNonEmptyString(payload.providerType),
|
|
21460
|
-
providerSessionId: readNonEmptyString(payload.providerSessionId)
|
|
21461
|
-
}
|
|
21462
|
-
});
|
|
21463
|
-
}
|
|
21464
|
-
function setupMeshEventForwarding(components) {
|
|
21465
|
-
components.instanceManager.onEvent((event) => {
|
|
21466
|
-
if (!isMeshCoordinatorEvent(event.event)) return;
|
|
21467
|
-
const instanceId = readNonEmptyString(event.instanceId);
|
|
21468
|
-
if (!instanceId) return;
|
|
21469
|
-
const sourceInstance = components.instanceManager.getInstance(instanceId);
|
|
21470
|
-
if (!sourceInstance || sourceInstance.category !== "cli") return;
|
|
21471
|
-
const state = sourceInstance.getState();
|
|
21472
|
-
const workspace = readNonEmptyString(state.workspace);
|
|
21473
|
-
if (!workspace) return;
|
|
21474
|
-
const settings = state.settings && typeof state.settings === "object" ? state.settings : {};
|
|
21475
|
-
if (readNonEmptyString(settings.meshCoordinatorFor)) return;
|
|
21476
|
-
const meshIdFromRuntime = readNonEmptyString(settings.meshNodeFor);
|
|
21477
|
-
const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
|
|
21478
|
-
if (!isMeshDelegate) return;
|
|
21479
|
-
const mesh = meshIdFromRuntime ? getMesh(meshIdFromRuntime) : getMeshByRepo(workspace);
|
|
21480
|
-
const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
|
|
21481
|
-
if (!meshId) return;
|
|
21482
|
-
const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
|
|
21483
|
-
const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
|
|
21484
|
-
const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
|
|
21485
|
-
injectMeshSystemMessage(components, {
|
|
21486
|
-
meshId,
|
|
21487
|
-
sourceInstanceId: instanceId,
|
|
21488
|
-
nodeLabel,
|
|
21489
|
-
event: event.event,
|
|
21490
|
-
metadataEvent: event
|
|
21491
|
-
});
|
|
21492
|
-
});
|
|
21493
|
-
}
|
|
22076
|
+
// src/commands/router.ts
|
|
22077
|
+
init_mesh_events();
|
|
21494
22078
|
|
|
21495
22079
|
// src/status/snapshot.ts
|
|
21496
22080
|
var os18 = __toESM(require("os"));
|
|
@@ -22145,7 +22729,7 @@ async function maybeRunDaemonUpgradeHelperFromEnv() {
|
|
|
22145
22729
|
|
|
22146
22730
|
// src/commands/router.ts
|
|
22147
22731
|
var import_os3 = require("os");
|
|
22148
|
-
var
|
|
22732
|
+
var import_path6 = require("path");
|
|
22149
22733
|
var fs10 = __toESM(require("fs"));
|
|
22150
22734
|
var CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
|
|
22151
22735
|
var CHANNEL_SERVER_URL = {
|
|
@@ -22214,22 +22798,22 @@ function serializeMeshCoordinatorMcpConfig(config, format) {
|
|
|
22214
22798
|
}
|
|
22215
22799
|
function resolveHermesUserHome() {
|
|
22216
22800
|
const explicitHome = process.env.HERMES_HOME?.trim();
|
|
22217
|
-
return explicitHome || (0,
|
|
22801
|
+
return explicitHome || (0, import_path6.join)((0, import_os3.homedir)(), ".hermes");
|
|
22218
22802
|
}
|
|
22219
22803
|
function loadHermesCoordinatorBaseConfig(targetConfigPath) {
|
|
22220
22804
|
const sourceHome = resolveHermesUserHome();
|
|
22221
|
-
const sourceConfigPath = (0,
|
|
22805
|
+
const sourceConfigPath = (0, import_path6.join)(sourceHome, "config.yaml");
|
|
22222
22806
|
if (!fs10.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
|
|
22223
|
-
if ((0,
|
|
22807
|
+
if ((0, import_path6.resolve)(sourceConfigPath) === (0, import_path6.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
|
|
22224
22808
|
const parsed = parseMeshCoordinatorMcpConfig(fs10.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
|
|
22225
22809
|
const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
|
|
22226
22810
|
return { config: baseConfig, sourceHome, sourceConfigPath };
|
|
22227
22811
|
}
|
|
22228
22812
|
function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
|
|
22229
|
-
if ((0,
|
|
22813
|
+
if ((0, import_path6.resolve)(sourceHome) === (0, import_path6.resolve)(targetHome)) return;
|
|
22230
22814
|
for (const fileName of [".env", "auth.json"]) {
|
|
22231
|
-
const sourcePath = (0,
|
|
22232
|
-
const targetPath = (0,
|
|
22815
|
+
const sourcePath = (0, import_path6.join)(sourceHome, fileName);
|
|
22816
|
+
const targetPath = (0, import_path6.join)(targetHome, fileName);
|
|
22233
22817
|
if (!fs10.existsSync(sourcePath)) continue;
|
|
22234
22818
|
try {
|
|
22235
22819
|
fs10.copyFileSync(sourcePath, targetPath);
|
|
@@ -23190,6 +23774,21 @@ var DaemonCommandRouter = class {
|
|
|
23190
23774
|
return { success: false, error: e.message };
|
|
23191
23775
|
}
|
|
23192
23776
|
}
|
|
23777
|
+
case "get_mesh_ledger": {
|
|
23778
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
23779
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
23780
|
+
try {
|
|
23781
|
+
const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
23782
|
+
const tail = typeof args?.tail === "number" ? args.tail : 20;
|
|
23783
|
+
const since = typeof args?.since === "string" ? args.since : void 0;
|
|
23784
|
+
const kind = Array.isArray(args?.kind) ? args.kind.filter((k) => typeof k === "string") : void 0;
|
|
23785
|
+
const entries = readLedgerEntries2(meshId, { tail, since, kind });
|
|
23786
|
+
const summary = getLedgerSummary2(meshId);
|
|
23787
|
+
return { success: true, entries, summary };
|
|
23788
|
+
} catch (e) {
|
|
23789
|
+
return { success: false, error: e.message };
|
|
23790
|
+
}
|
|
23791
|
+
}
|
|
23193
23792
|
case "add_mesh_node": {
|
|
23194
23793
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
23195
23794
|
const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
|
|
@@ -23258,6 +23857,54 @@ var DaemonCommandRouter = class {
|
|
|
23258
23857
|
return { success: false, error: e.message };
|
|
23259
23858
|
}
|
|
23260
23859
|
}
|
|
23860
|
+
case "refine_mesh_node": {
|
|
23861
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
23862
|
+
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
23863
|
+
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
23864
|
+
try {
|
|
23865
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
23866
|
+
const mesh = meshRecord?.mesh;
|
|
23867
|
+
const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
23868
|
+
if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
|
|
23869
|
+
if (!node.isLocalWorktree || !node.workspace) {
|
|
23870
|
+
return { success: false, error: `Refinery requires a local worktree node` };
|
|
23871
|
+
}
|
|
23872
|
+
const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
|
|
23873
|
+
const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
|
|
23874
|
+
if (!repoRoot) return { success: false, error: "Source node repoRoot not found" };
|
|
23875
|
+
const { execFile: execFile3 } = await import("child_process");
|
|
23876
|
+
const { promisify: promisify3 } = await import("util");
|
|
23877
|
+
const execFileAsync3 = promisify3(execFile3);
|
|
23878
|
+
const { stdout: branchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
23879
|
+
const branch = branchStdout.trim();
|
|
23880
|
+
if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
|
|
23881
|
+
const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
23882
|
+
const baseBranch = baseBranchStdout.trim();
|
|
23883
|
+
try {
|
|
23884
|
+
await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
23885
|
+
} catch (e) {
|
|
23886
|
+
return { success: false, error: `Merge failed (conflicts?): ${e.message}` };
|
|
23887
|
+
}
|
|
23888
|
+
const removeResult = await this.execute("remove_mesh_node", {
|
|
23889
|
+
meshId,
|
|
23890
|
+
nodeId,
|
|
23891
|
+
sessionCleanupMode: "kill",
|
|
23892
|
+
inlineMesh: args?.inlineMesh
|
|
23893
|
+
});
|
|
23894
|
+
try {
|
|
23895
|
+
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
23896
|
+
appendLedgerEntry2(meshId, {
|
|
23897
|
+
kind: "node_removed",
|
|
23898
|
+
nodeId,
|
|
23899
|
+
payload: { refined: true, mergedBranch: branch, into: baseBranch }
|
|
23900
|
+
});
|
|
23901
|
+
} catch {
|
|
23902
|
+
}
|
|
23903
|
+
return { success: true, merged: true, branch, into: baseBranch, removeResult };
|
|
23904
|
+
} catch (e) {
|
|
23905
|
+
return { success: false, error: e.message };
|
|
23906
|
+
}
|
|
23907
|
+
}
|
|
23261
23908
|
case "remove_mesh_node": {
|
|
23262
23909
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
23263
23910
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
@@ -23293,6 +23940,17 @@ var DaemonCommandRouter = class {
|
|
|
23293
23940
|
const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
23294
23941
|
removed = removeNode3(meshId, nodeId);
|
|
23295
23942
|
}
|
|
23943
|
+
if (removed) {
|
|
23944
|
+
try {
|
|
23945
|
+
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
23946
|
+
appendLedgerEntry2(meshId, {
|
|
23947
|
+
kind: "node_removed",
|
|
23948
|
+
nodeId,
|
|
23949
|
+
payload: { worktree: !!node?.isLocalWorktree, sessionCleanupMode }
|
|
23950
|
+
});
|
|
23951
|
+
} catch {
|
|
23952
|
+
}
|
|
23953
|
+
}
|
|
23296
23954
|
return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {} };
|
|
23297
23955
|
} catch (e) {
|
|
23298
23956
|
return { success: false, error: e.message };
|
|
@@ -23322,9 +23980,9 @@ var DaemonCommandRouter = class {
|
|
|
23322
23980
|
});
|
|
23323
23981
|
let node;
|
|
23324
23982
|
if (meshRecord.inline) {
|
|
23325
|
-
const { randomUUID:
|
|
23983
|
+
const { randomUUID: randomUUID10 } = await import("crypto");
|
|
23326
23984
|
node = {
|
|
23327
|
-
id: `node_${
|
|
23985
|
+
id: `node_${randomUUID10().replace(/-/g, "")}`,
|
|
23328
23986
|
workspace: result.worktreePath,
|
|
23329
23987
|
repoRoot: result.worktreePath,
|
|
23330
23988
|
daemonId: sourceNode.daemonId,
|
|
@@ -23349,6 +24007,15 @@ var DaemonCommandRouter = class {
|
|
|
23349
24007
|
});
|
|
23350
24008
|
if (!node) return { success: false, error: "Failed to register worktree node" };
|
|
23351
24009
|
}
|
|
24010
|
+
try {
|
|
24011
|
+
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
24012
|
+
appendLedgerEntry2(meshId, {
|
|
24013
|
+
kind: "node_cloned",
|
|
24014
|
+
nodeId: node.id,
|
|
24015
|
+
payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath }
|
|
24016
|
+
});
|
|
24017
|
+
} catch {
|
|
24018
|
+
}
|
|
23352
24019
|
return {
|
|
23353
24020
|
success: true,
|
|
23354
24021
|
node,
|
|
@@ -23359,6 +24026,19 @@ var DaemonCommandRouter = class {
|
|
|
23359
24026
|
return { success: false, error: e.message };
|
|
23360
24027
|
}
|
|
23361
24028
|
}
|
|
24029
|
+
case "trigger_mesh_queue": {
|
|
24030
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
24031
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
24032
|
+
try {
|
|
24033
|
+
const { triggerMeshQueue: triggerMeshQueue2 } = await Promise.resolve().then(() => (init_mesh_events(), mesh_events_exports));
|
|
24034
|
+
if (meshId) {
|
|
24035
|
+
triggerMeshQueue2(this.deps, meshId);
|
|
24036
|
+
}
|
|
24037
|
+
return { success: true };
|
|
24038
|
+
} catch (e) {
|
|
24039
|
+
return { success: false, error: e.message };
|
|
24040
|
+
}
|
|
24041
|
+
}
|
|
23362
24042
|
// ─── Mesh Coordinator Launch ───
|
|
23363
24043
|
case "launch_mesh_coordinator": {
|
|
23364
24044
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
@@ -23463,7 +24143,7 @@ var DaemonCommandRouter = class {
|
|
|
23463
24143
|
workspace
|
|
23464
24144
|
};
|
|
23465
24145
|
}
|
|
23466
|
-
const { existsSync:
|
|
24146
|
+
const { existsSync: existsSync25, readFileSync: readFileSync17, writeFileSync: writeFileSync15, copyFileSync: copyFileSync4, mkdirSync: mkdirSync17 } = await import("fs");
|
|
23467
24147
|
const { dirname: dirname9 } = await import("path");
|
|
23468
24148
|
const mcpConfigPath = coordinatorSetup.configPath;
|
|
23469
24149
|
const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
|
|
@@ -23497,21 +24177,21 @@ var DaemonCommandRouter = class {
|
|
|
23497
24177
|
};
|
|
23498
24178
|
}
|
|
23499
24179
|
try {
|
|
23500
|
-
|
|
24180
|
+
mkdirSync17(dirname9(mcpConfigPath), { recursive: true });
|
|
23501
24181
|
} catch (error) {
|
|
23502
24182
|
const message = `Could not prepare MCP config path for automatic setup: ${error?.message || error}`;
|
|
23503
24183
|
LOG.error("MeshCoordinator", message);
|
|
23504
24184
|
if (hermesManualFallback) return returnManualFallback(message);
|
|
23505
24185
|
return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
|
|
23506
24186
|
}
|
|
23507
|
-
const hadExistingMcpConfig =
|
|
24187
|
+
const hadExistingMcpConfig = existsSync25(mcpConfigPath);
|
|
23508
24188
|
let existingMcpConfig = hermesBaseConfig?.config || {};
|
|
23509
24189
|
if (hermesBaseConfig) {
|
|
23510
24190
|
copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname9(mcpConfigPath));
|
|
23511
24191
|
}
|
|
23512
24192
|
if (hadExistingMcpConfig) {
|
|
23513
24193
|
try {
|
|
23514
|
-
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(
|
|
24194
|
+
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync17(mcpConfigPath, "utf-8"), configFormat);
|
|
23515
24195
|
existingMcpConfig = { ...existingMcpConfig, ...parsedExistingMcpConfig };
|
|
23516
24196
|
copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
|
|
23517
24197
|
} catch (error) {
|
|
@@ -23533,7 +24213,7 @@ var DaemonCommandRouter = class {
|
|
|
23533
24213
|
}
|
|
23534
24214
|
};
|
|
23535
24215
|
try {
|
|
23536
|
-
|
|
24216
|
+
writeFileSync15(mcpConfigPath, serializeMeshCoordinatorMcpConfig(mcpConfig, configFormat), "utf-8");
|
|
23537
24217
|
} catch (error) {
|
|
23538
24218
|
const message = `Could not write MCP config for automatic setup: ${error?.message || error}`;
|
|
23539
24219
|
LOG.error("MeshCoordinator", message);
|
|
@@ -23570,6 +24250,16 @@ var DaemonCommandRouter = class {
|
|
|
23570
24250
|
return { success: false, error: launchResult?.error || "Failed to launch CLI session" };
|
|
23571
24251
|
}
|
|
23572
24252
|
LOG.info("MeshCoordinator", `Launched ${cliType} coordinator for mesh ${meshId} in ${workspace}`);
|
|
24253
|
+
try {
|
|
24254
|
+
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
24255
|
+
appendLedgerEntry2(meshId, {
|
|
24256
|
+
kind: "coordinator_started",
|
|
24257
|
+
sessionId: launchResult.sessionId || launchResult.id,
|
|
24258
|
+
providerType: cliType,
|
|
24259
|
+
payload: { workspace }
|
|
24260
|
+
});
|
|
24261
|
+
} catch {
|
|
24262
|
+
}
|
|
23573
24263
|
return {
|
|
23574
24264
|
success: true,
|
|
23575
24265
|
meshId,
|
|
@@ -31355,6 +32045,7 @@ var SessionRegistry = class {
|
|
|
31355
32045
|
// src/boot/daemon-lifecycle.ts
|
|
31356
32046
|
init_logger();
|
|
31357
32047
|
init_config();
|
|
32048
|
+
init_mesh_events();
|
|
31358
32049
|
async function initDaemonComponents(config) {
|
|
31359
32050
|
installGlobalInterceptor();
|
|
31360
32051
|
const appConfig = loadConfig();
|
|
@@ -31638,6 +32329,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
31638
32329
|
TurnSnapshotTracker,
|
|
31639
32330
|
VersionArchive,
|
|
31640
32331
|
addNode,
|
|
32332
|
+
appendLedgerEntry,
|
|
31641
32333
|
appendRecentActivity,
|
|
31642
32334
|
buildAssistantChatMessage,
|
|
31643
32335
|
buildChatMessage,
|
|
@@ -31655,6 +32347,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
31655
32347
|
buildThoughtChatMessage,
|
|
31656
32348
|
buildToolChatMessage,
|
|
31657
32349
|
buildUserChatMessage,
|
|
32350
|
+
claimNextTask,
|
|
31658
32351
|
classifyChatMessageVisibility,
|
|
31659
32352
|
classifyHotChatSessionsForSubscriptionFlush,
|
|
31660
32353
|
clearDebugTrace,
|
|
@@ -31673,6 +32366,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
31673
32366
|
detectAllVersions,
|
|
31674
32367
|
detectCLIs,
|
|
31675
32368
|
detectIDEs,
|
|
32369
|
+
enqueueTask,
|
|
31676
32370
|
ensureSessionHostReady,
|
|
31677
32371
|
execNpmCommandSync,
|
|
31678
32372
|
filterActivityChatMessages,
|
|
@@ -31691,10 +32385,13 @@ async function shutdownDaemonComponents(components) {
|
|
|
31691
32385
|
getGitFileDiff,
|
|
31692
32386
|
getGitRepoStatus,
|
|
31693
32387
|
getHostMemorySnapshot,
|
|
32388
|
+
getLedgerDir,
|
|
32389
|
+
getLedgerSummary,
|
|
31694
32390
|
getLogLevel,
|
|
31695
32391
|
getMesh,
|
|
31696
32392
|
getMeshByRepo,
|
|
31697
32393
|
getNpmExecOptions,
|
|
32394
|
+
getQueue,
|
|
31698
32395
|
getRecentActivity,
|
|
31699
32396
|
getRecentCommands,
|
|
31700
32397
|
getRecentDebugTrace,
|
|
@@ -31702,6 +32399,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
31702
32399
|
getSavedProviderSessions,
|
|
31703
32400
|
getSessionHostRecoveryLabel,
|
|
31704
32401
|
getSessionHostSurfaceKind,
|
|
32402
|
+
getSessionRecoveryContext,
|
|
31705
32403
|
getWorkspaceState,
|
|
31706
32404
|
handleGitCommand,
|
|
31707
32405
|
hasCdpManager,
|
|
@@ -31755,6 +32453,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
31755
32453
|
prepareSessionModalUpdate,
|
|
31756
32454
|
probeCdpPort,
|
|
31757
32455
|
readChatHistory,
|
|
32456
|
+
readLedgerEntries,
|
|
31758
32457
|
recordDebugTrace,
|
|
31759
32458
|
registerExtensionProviders,
|
|
31760
32459
|
removeNode,
|
|
@@ -31783,9 +32482,12 @@ async function shutdownDaemonComponents(components) {
|
|
|
31783
32482
|
startDaemonDevSupport,
|
|
31784
32483
|
summarizeGitStatus,
|
|
31785
32484
|
syncMeshes,
|
|
32485
|
+
triggerMeshQueue,
|
|
31786
32486
|
updateConfig,
|
|
31787
32487
|
updateMesh,
|
|
31788
32488
|
updateNode,
|
|
32489
|
+
updateSessionTaskStatus,
|
|
32490
|
+
updateTaskStatus,
|
|
31789
32491
|
upsertSavedProviderSession
|
|
31790
32492
|
});
|
|
31791
32493
|
//# sourceMappingURL=index.js.map
|