@adhdev/daemon-standalone 0.9.82-rc.8 → 0.9.82-rc.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +25049 -21178
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/index-Bso1b8Lh.css +1 -0
- package/public/assets/index-DW3PmABW.js +99 -0
- package/public/assets/{terminal-DCVDkSIp.js → terminal-D46M5EWH.js} +4 -4
- package/public/assets/{vendor-piMzuE8Y.js → vendor-CgiI0UIA.js} +29 -29
- package/public/index.html +3 -3
- package/vendor/mcp-server/index.js +725 -74
- package/vendor/mcp-server/index.js.map +1 -1
- package/public/assets/index-UQ7bnB6f.css +0 -1
- package/public/assets/index-rt5QK8HH.js +0 -98
|
@@ -35,9 +35,21 @@ __export(index_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
|
+
// src/tools/mesh-tools.ts
|
|
39
|
+
var import_node_crypto = require("crypto");
|
|
40
|
+
|
|
38
41
|
// src/transports/ipc.ts
|
|
39
42
|
var DEFAULT_IPC_PORT = 19222;
|
|
40
43
|
var DEFAULT_IPC_PATH = "/ipc";
|
|
44
|
+
var DEFAULT_IPC_COMMAND_TIMEOUT_MS = 15e3;
|
|
45
|
+
var IPC_COMMAND_TIMEOUTS_MS = {
|
|
46
|
+
mesh_relay_command: 12e4,
|
|
47
|
+
agent_command: 3e4,
|
|
48
|
+
git_status: 45e3,
|
|
49
|
+
git_diff_summary: 45e3,
|
|
50
|
+
fast_forward_mesh_node: 12e4,
|
|
51
|
+
mesh_status: 12e4
|
|
52
|
+
};
|
|
41
53
|
var IpcTransport = class {
|
|
42
54
|
port;
|
|
43
55
|
path;
|
|
@@ -85,9 +97,22 @@ var IpcTransport = class {
|
|
|
85
97
|
}
|
|
86
98
|
fn();
|
|
87
99
|
};
|
|
88
|
-
const
|
|
100
|
+
const nestedCommand = typeof args?.command === "string" ? args.command : "";
|
|
101
|
+
const targetDaemonId = typeof args?.targetDaemonId === "string" ? args.targetDaemonId : "";
|
|
102
|
+
const effectiveType = type === "mesh_relay_command" && nestedCommand ? nestedCommand : type;
|
|
103
|
+
const timeoutMs = Math.max(
|
|
104
|
+
IPC_COMMAND_TIMEOUTS_MS[type] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS,
|
|
105
|
+
IPC_COMMAND_TIMEOUTS_MS[effectiveType] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS
|
|
106
|
+
);
|
|
107
|
+
const diagnosticParts = [
|
|
108
|
+
`command='${type}'`,
|
|
109
|
+
...nestedCommand ? [`relayedCommand='${nestedCommand}'`] : [],
|
|
110
|
+
...targetDaemonId ? [`targetDaemonId='${targetDaemonId.slice(0, 12)}'`] : [],
|
|
111
|
+
...typeof args?.nodeId === "string" ? [`nodeId='${args.nodeId}'`] : [],
|
|
112
|
+
...typeof args?.workspace === "string" ? [`workspace='${args.workspace}'`] : []
|
|
113
|
+
];
|
|
89
114
|
const timeout = setTimeout(() => {
|
|
90
|
-
finish(() => reject(new Error(`Daemon IPC
|
|
115
|
+
finish(() => reject(new Error(`Daemon IPC ${diagnosticParts.join(" ")} timed out after ${Math.round(timeoutMs / 1e3)}s (requestId=${requestId})`)));
|
|
91
116
|
}, timeoutMs);
|
|
92
117
|
let commandSent = false;
|
|
93
118
|
const send = () => {
|
|
@@ -143,6 +168,10 @@ function isLocalTransport(transport) {
|
|
|
143
168
|
}
|
|
144
169
|
|
|
145
170
|
// src/tools/chat-compact.ts
|
|
171
|
+
function isAssistantLike(message) {
|
|
172
|
+
const role = String(message?.role ?? "").toLowerCase();
|
|
173
|
+
return role === "assistant" || role === "agent";
|
|
174
|
+
}
|
|
146
175
|
function messageContent(message) {
|
|
147
176
|
const content = message?.content;
|
|
148
177
|
if (typeof content === "string") return content;
|
|
@@ -161,16 +190,22 @@ function isCoordinatorVisibleMessage(message) {
|
|
|
161
190
|
if (meta?.internal === true || meta?.debug === true || meta?.control === true || meta?.userVisible === false || meta?.user_visible === false) return false;
|
|
162
191
|
return role === "user" || role === "assistant" || role === "agent";
|
|
163
192
|
}
|
|
193
|
+
function buildCompactMessageTail(visibleMessages, opts) {
|
|
194
|
+
const summary = typeof opts.summary === "string" ? opts.summary.trim() : "";
|
|
195
|
+
const shouldOmitSummaryMessage = !!summary && !!opts.finalAssistant && isAssistantLike(opts.finalAssistant) && messageContent(opts.finalAssistant).trim() === summary;
|
|
196
|
+
const sourceMessages = shouldOmitSummaryMessage ? visibleMessages.filter((message) => message !== opts.finalAssistant) : visibleMessages;
|
|
197
|
+
return sourceMessages.slice(-opts.limit);
|
|
198
|
+
}
|
|
164
199
|
function compactChatPayload(payload, opts = {}) {
|
|
165
200
|
const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
|
|
166
201
|
const visible = rawMessages.filter(isCoordinatorVisibleMessage);
|
|
167
202
|
const limit = Math.max(1, Math.min(opts.limit ?? 10, 10));
|
|
168
|
-
const messages = visible.slice(-limit);
|
|
169
203
|
const finalAssistant = [...visible].reverse().find((message) => {
|
|
170
204
|
const role = String(message?.role ?? "").toLowerCase();
|
|
171
205
|
return (role === "assistant" || role === "agent") && messageContent(message).trim();
|
|
172
206
|
});
|
|
173
207
|
const summary = typeof payload?.summary === "string" && payload.summary.trim() ? payload.summary.trim() : messageContent(finalAssistant).trim();
|
|
208
|
+
const messages = buildCompactMessageTail(visible, { summary, finalAssistant, limit });
|
|
174
209
|
return {
|
|
175
210
|
success: payload?.success !== false,
|
|
176
211
|
compact: true,
|
|
@@ -235,6 +270,30 @@ var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
|
|
|
235
270
|
function readString(value) {
|
|
236
271
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
237
272
|
}
|
|
273
|
+
function summarizeTaskMessage(message) {
|
|
274
|
+
const taskSummary = message.replace(/\s+/g, " ").trim();
|
|
275
|
+
const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
|
|
276
|
+
return { taskTitle: taskTitle || "(untitled task)", taskSummary };
|
|
277
|
+
}
|
|
278
|
+
function buildDirectTaskPayload(message, via, opts) {
|
|
279
|
+
const descriptor = summarizeTaskMessage(message);
|
|
280
|
+
return {
|
|
281
|
+
source: "direct",
|
|
282
|
+
via,
|
|
283
|
+
taskId: opts.taskId,
|
|
284
|
+
message,
|
|
285
|
+
taskTitle: descriptor.taskTitle,
|
|
286
|
+
taskSummary: descriptor.taskSummary,
|
|
287
|
+
...opts.taskMode ? { taskMode: opts.taskMode } : {},
|
|
288
|
+
...opts.providerType ? { providerType: opts.providerType } : {},
|
|
289
|
+
...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function findNode(mesh, nodeId) {
|
|
293
|
+
const node = mesh.nodes.find((n) => n.id === nodeId);
|
|
294
|
+
if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
|
|
295
|
+
return node;
|
|
296
|
+
}
|
|
238
297
|
var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
|
|
239
298
|
var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
|
|
240
299
|
var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
|
|
@@ -246,15 +305,24 @@ async function refreshMeshFromDaemon(ctx) {
|
|
|
246
305
|
const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
|
|
247
306
|
if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
|
|
248
307
|
const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
|
|
249
|
-
if (!refreshedNodes.length) return;
|
|
250
308
|
ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
|
|
251
309
|
ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
|
|
252
310
|
} catch {
|
|
253
311
|
}
|
|
254
312
|
}
|
|
313
|
+
async function syncCoordinatorDaemonMeshCache(ctx) {
|
|
314
|
+
if (!(ctx.transport instanceof IpcTransport)) return;
|
|
315
|
+
try {
|
|
316
|
+
await ctx.transport.command("get_mesh", {
|
|
317
|
+
meshId: ctx.mesh.id,
|
|
318
|
+
inlineMesh: ctx.mesh
|
|
319
|
+
});
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
}
|
|
255
323
|
async function findNodeWithRefresh(ctx, nodeId) {
|
|
256
324
|
const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
257
|
-
if (hit) return hit;
|
|
325
|
+
if (hit && !hit.isLocalWorktree) return hit;
|
|
258
326
|
await refreshMeshFromDaemon(ctx);
|
|
259
327
|
const refreshed = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
260
328
|
if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
|
|
@@ -262,7 +330,7 @@ async function findNodeWithRefresh(ctx, nodeId) {
|
|
|
262
330
|
}
|
|
263
331
|
async function findOptionalNodeWithRefresh(ctx, nodeId) {
|
|
264
332
|
const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
265
|
-
if (hit) return hit;
|
|
333
|
+
if (hit && !hit.isLocalWorktree) return hit;
|
|
266
334
|
await refreshMeshFromDaemon(ctx);
|
|
267
335
|
return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
|
|
268
336
|
}
|
|
@@ -314,9 +382,26 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
|
|
|
314
382
|
readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
|
|
315
383
|
};
|
|
316
384
|
if (finalSummary) {
|
|
385
|
+
if (args.compact === true) {
|
|
386
|
+
return {
|
|
387
|
+
...compactChatPayload({
|
|
388
|
+
success: true,
|
|
389
|
+
status: "idle",
|
|
390
|
+
providerSessionId,
|
|
391
|
+
summary: finalSummary,
|
|
392
|
+
messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
|
|
393
|
+
}, {
|
|
394
|
+
nodeId: args.node_id,
|
|
395
|
+
sessionId: args.session_id,
|
|
396
|
+
limit: args.tail ?? 10
|
|
397
|
+
}),
|
|
398
|
+
recoveredFromLedger: true,
|
|
399
|
+
ledger
|
|
400
|
+
};
|
|
401
|
+
}
|
|
317
402
|
return {
|
|
318
403
|
success: true,
|
|
319
|
-
compact:
|
|
404
|
+
compact: false,
|
|
320
405
|
recoveredFromLedger: true,
|
|
321
406
|
nodeId: args.node_id,
|
|
322
407
|
sessionId: args.session_id,
|
|
@@ -368,6 +453,14 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
|
|
|
368
453
|
function readSessionRecordId(session) {
|
|
369
454
|
return readString(session?.id) || readString(session?.sessionId) || readString(session?.session_id) || readString(session?.runtimeSessionId) || readString(session?.runtime_session_id) || readString(session?.instanceId) || readString(session?.instance_id);
|
|
370
455
|
}
|
|
456
|
+
function extractStatusMetadataSessions(value) {
|
|
457
|
+
const payload = unwrapCommandPayload(value);
|
|
458
|
+
const status = payload?.status && typeof payload.status === "object" ? payload.status : payload;
|
|
459
|
+
return Array.isArray(status?.sessions) ? status.sessions : [];
|
|
460
|
+
}
|
|
461
|
+
function resolveSessionProviderType(session) {
|
|
462
|
+
return readString(session?.providerType) || readString(session?.cliType) || readString(session?.agentType) || "";
|
|
463
|
+
}
|
|
371
464
|
function addSessionRecord(target, session) {
|
|
372
465
|
if (!session || typeof session !== "object" || isTerminalSessionRecord(session)) return;
|
|
373
466
|
const sessionId = readSessionRecordId(session);
|
|
@@ -475,6 +568,18 @@ function filterQueueForView(queue, view, statuses) {
|
|
|
475
568
|
if (view === "historical") return queue.filter((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
|
|
476
569
|
return queue;
|
|
477
570
|
}
|
|
571
|
+
function prioritizeActiveQueueRows(queue) {
|
|
572
|
+
const active = [];
|
|
573
|
+
const historical = [];
|
|
574
|
+
const other = [];
|
|
575
|
+
for (const task of queue) {
|
|
576
|
+
const status = String(task?.status || "");
|
|
577
|
+
if (ACTIVE_QUEUE_STATUSES.has(status)) active.push(task);
|
|
578
|
+
else if (HISTORICAL_QUEUE_STATUSES.has(status)) historical.push(task);
|
|
579
|
+
else other.push(task);
|
|
580
|
+
}
|
|
581
|
+
return [...active, ...other, ...historical];
|
|
582
|
+
}
|
|
478
583
|
function slimQueueTask(task) {
|
|
479
584
|
return {
|
|
480
585
|
id: task?.id,
|
|
@@ -580,22 +685,59 @@ function isIdleSessionRecord(session) {
|
|
|
580
685
|
const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
|
|
581
686
|
return status === "idle" || chatStatus === "waiting_input";
|
|
582
687
|
}
|
|
688
|
+
function isMeshOwnedDelegateSession(session, meshId, nodeId) {
|
|
689
|
+
const settings = session?.settings;
|
|
690
|
+
const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
|
|
691
|
+
const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
|
|
692
|
+
const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
|
|
693
|
+
if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
|
|
694
|
+
return !sessionNodeId || sessionNodeId === nodeId;
|
|
695
|
+
}
|
|
583
696
|
function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
|
|
584
697
|
const live = sessions.filter((session) => !isTerminalSessionRecord(session));
|
|
585
698
|
const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
|
|
586
|
-
const isMeshOwnedDelegateSession = (session) => {
|
|
587
|
-
const settings = session?.settings;
|
|
588
|
-
const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
|
|
589
|
-
const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
|
|
590
|
-
const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
|
|
591
|
-
if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
|
|
592
|
-
return !sessionNodeId || sessionNodeId === nodeId;
|
|
593
|
-
};
|
|
594
699
|
const meshSessions = live.filter(
|
|
595
|
-
(session) => isMeshOwnedDelegateSession(session)
|
|
700
|
+
(session) => isMeshOwnedDelegateSession(session, meshId, nodeId)
|
|
596
701
|
);
|
|
597
702
|
return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || void 0;
|
|
598
703
|
}
|
|
704
|
+
function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
|
|
705
|
+
return {
|
|
706
|
+
success: false,
|
|
707
|
+
recoverable: true,
|
|
708
|
+
code: "mesh_delegate_session_missing_relay_metadata",
|
|
709
|
+
reason: "mesh_delegate_session_missing_relay_metadata",
|
|
710
|
+
transport: "mesh_transport",
|
|
711
|
+
retryRecommended: true,
|
|
712
|
+
meshId: ctx.mesh.id,
|
|
713
|
+
nodeId: node.id,
|
|
714
|
+
daemonId: node.daemonId,
|
|
715
|
+
workspace: node.workspace,
|
|
716
|
+
sessionId,
|
|
717
|
+
...providerType ? { resolvedProviderType: providerType } : {},
|
|
718
|
+
error: `Remote session '${sessionId}' is not relay-safe for mesh '${ctx.mesh.id}': missing meshNodeFor/meshCoordinatorDaemonId metadata, so completion events would not reach the coordinator ledger.`,
|
|
719
|
+
nextAction: `Launch a fresh relay-safe session with mesh_launch_session(node_id: '${node.id}'${providerType ? `, type: '${providerType}'` : ""}) or dispatch without session_id so Repo Mesh can choose a valid delegate session.`,
|
|
720
|
+
noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
|
|
724
|
+
return {
|
|
725
|
+
success: false,
|
|
726
|
+
recoverable: true,
|
|
727
|
+
code: "mesh_coordinator_daemon_unknown",
|
|
728
|
+
reason: "mesh_coordinator_daemon_unknown",
|
|
729
|
+
transport: "mesh_transport",
|
|
730
|
+
retryRecommended: true,
|
|
731
|
+
meshId: ctx.mesh.id,
|
|
732
|
+
nodeId: node.id,
|
|
733
|
+
daemonId: node.daemonId,
|
|
734
|
+
workspace: node.workspace,
|
|
735
|
+
...providerType ? { resolvedProviderType: providerType } : {},
|
|
736
|
+
error: `Cannot launch a remote mesh delegate for node '${node.id}': coordinator daemon identity is unavailable, so the worker would be unable to relay completion events back to the coordinator.`,
|
|
737
|
+
nextAction: "Retry after the coordinator daemon identity is available (for example from an attached daemon-backed MCP session) so meshCoordinatorDaemonId can be stamped on the worker session.",
|
|
738
|
+
noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
|
|
739
|
+
};
|
|
740
|
+
}
|
|
599
741
|
function findNestedPayload(value, predicate) {
|
|
600
742
|
const seen = /* @__PURE__ */ new Set();
|
|
601
743
|
const stack = [{ payload: value, depth: 0 }];
|
|
@@ -623,12 +765,16 @@ function extractGitDiff(value) {
|
|
|
623
765
|
}
|
|
624
766
|
function extractSubmodules(value, ignorePaths) {
|
|
625
767
|
const payload = unwrapCommandPayload(value);
|
|
626
|
-
const subs = payload?.submodules ?? value?.submodules;
|
|
768
|
+
const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
|
|
627
769
|
if (!Array.isArray(subs)) return void 0;
|
|
628
770
|
if (ignorePaths.length === 0) return subs;
|
|
629
771
|
const ignoreSet = new Set(ignorePaths);
|
|
630
772
|
return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
|
|
631
773
|
}
|
|
774
|
+
function assignFullGitSnapshot(entry, status) {
|
|
775
|
+
if (!status || typeof status !== "object" || Array.isArray(status)) return;
|
|
776
|
+
entry.git = status;
|
|
777
|
+
}
|
|
632
778
|
function extractLaunchPayload(value) {
|
|
633
779
|
return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
|
|
634
780
|
}
|
|
@@ -753,20 +899,63 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
|
|
|
753
899
|
let sessionId = args.session_id?.trim() || "";
|
|
754
900
|
const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
|
|
755
901
|
let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
|
|
756
|
-
if (!sessionId) {
|
|
902
|
+
if (!sessionId || args.session_id) {
|
|
757
903
|
try {
|
|
758
904
|
const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
905
|
+
const sessions = extractStatusMetadataSessions(relayResult);
|
|
906
|
+
if (sessionId) {
|
|
907
|
+
const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
|
|
908
|
+
if (!explicitSession) {
|
|
909
|
+
return {
|
|
910
|
+
success: false,
|
|
911
|
+
recoverable: true,
|
|
912
|
+
code: "mesh_target_session_not_found",
|
|
913
|
+
reason: "mesh_target_session_not_found",
|
|
914
|
+
transport: "mesh_transport",
|
|
915
|
+
retryRecommended: true,
|
|
916
|
+
meshId: ctx.mesh.id,
|
|
917
|
+
nodeId: node.id,
|
|
918
|
+
daemonId,
|
|
919
|
+
workspace: node.workspace,
|
|
920
|
+
sessionId,
|
|
921
|
+
...resolvedProviderType ? { resolvedProviderType } : {},
|
|
922
|
+
error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
|
|
923
|
+
nextAction: `Launch a fresh session with mesh_launch_session(node_id: '${node.id}'${resolvedProviderType ? `, type: '${resolvedProviderType}'` : ""}) or retry without session_id so Repo Mesh can target a live delegate session.`
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
|
|
927
|
+
return buildRelayUnsafeRemoteSessionFailure(
|
|
928
|
+
ctx,
|
|
929
|
+
node,
|
|
930
|
+
sessionId,
|
|
931
|
+
resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
|
|
932
|
+
);
|
|
933
|
+
}
|
|
765
934
|
if (!resolvedProviderType) {
|
|
766
|
-
resolvedProviderType =
|
|
935
|
+
resolvedProviderType = resolveSessionProviderType(explicitSession);
|
|
936
|
+
}
|
|
937
|
+
} else {
|
|
938
|
+
const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
|
|
939
|
+
if (targetSession?.id || targetSession?.sessionId) {
|
|
940
|
+
sessionId = targetSession.id || targetSession.sessionId;
|
|
941
|
+
if (!resolvedProviderType) {
|
|
942
|
+
resolvedProviderType = resolveSessionProviderType(targetSession);
|
|
943
|
+
}
|
|
767
944
|
}
|
|
768
945
|
}
|
|
769
946
|
} catch (e) {
|
|
947
|
+
if (sessionId) {
|
|
948
|
+
return {
|
|
949
|
+
...buildCoordinatorP2pRelayFailure(e, {
|
|
950
|
+
command: "get_status_metadata",
|
|
951
|
+
targetDaemonId: daemonId,
|
|
952
|
+
nodeId: node.id,
|
|
953
|
+
sessionId
|
|
954
|
+
}),
|
|
955
|
+
success: false,
|
|
956
|
+
error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
|
|
957
|
+
};
|
|
958
|
+
}
|
|
770
959
|
}
|
|
771
960
|
}
|
|
772
961
|
if (!resolvedProviderType) {
|
|
@@ -796,7 +985,7 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
|
|
|
796
985
|
error: `P2P dispatch failed: ${errorMessage}`
|
|
797
986
|
};
|
|
798
987
|
}
|
|
799
|
-
return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
|
|
988
|
+
return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType, providerType: resolvedProviderType };
|
|
800
989
|
} catch (e) {
|
|
801
990
|
const errorMessage = e?.message || String(e);
|
|
802
991
|
return {
|
|
@@ -831,6 +1020,34 @@ function readNodeMachineId(node) {
|
|
|
831
1020
|
function readNodeDaemonId(node) {
|
|
832
1021
|
return readString(node.daemonId) || readString(node.daemon_id);
|
|
833
1022
|
}
|
|
1023
|
+
function normalizeHostname(value) {
|
|
1024
|
+
const hostname = readString(value);
|
|
1025
|
+
if (!hostname) return void 0;
|
|
1026
|
+
return hostname.toLowerCase().replace(/\.$/, "");
|
|
1027
|
+
}
|
|
1028
|
+
function readNodeHostname(node) {
|
|
1029
|
+
return readString(node.hostname) || readString(node.machineHostname) || readString(node.machine_hostname) || readString(node.machineName) || readString(node.machine_name) || readString(node.lastProbe?.hostname) || readString(node.last_probe?.hostname) || readString(node.lastProbe?.machine?.hostname) || readString(node.last_probe?.machine?.hostname);
|
|
1030
|
+
}
|
|
1031
|
+
function buildNodeMachineIdentity(ctx, node) {
|
|
1032
|
+
const machineId = readNodeMachineId(node);
|
|
1033
|
+
const daemonId = readNodeDaemonId(node);
|
|
1034
|
+
const hostname = readNodeHostname(node);
|
|
1035
|
+
const coordinatorHostname = readString(ctx.coordinatorHostname);
|
|
1036
|
+
const hostnameMatches = Boolean(
|
|
1037
|
+
normalizeHostname(hostname) && normalizeHostname(coordinatorHostname) && normalizeHostname(hostname) === normalizeHostname(coordinatorHostname)
|
|
1038
|
+
);
|
|
1039
|
+
const sameMachine = isLocalControlPlaneNode(ctx, node) || hostnameMatches;
|
|
1040
|
+
return {
|
|
1041
|
+
daemonId,
|
|
1042
|
+
machineId,
|
|
1043
|
+
hostname,
|
|
1044
|
+
machineName: hostname,
|
|
1045
|
+
coordinatorHostname,
|
|
1046
|
+
sameMachine,
|
|
1047
|
+
locality: sameMachine ? "same_machine" : "remote_or_unknown",
|
|
1048
|
+
localityReason: sameMachine ? isLocalControlPlaneNode(ctx, node) ? "matched coordinator daemon or machine id" : "matched coordinator hostname" : "no coordinator daemon, machine id, or hostname match"
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
834
1051
|
function isDirectLocalNode(ctx, node) {
|
|
835
1052
|
const machineId = readNodeMachineId(node);
|
|
836
1053
|
const daemonId = readNodeDaemonId(node);
|
|
@@ -880,6 +1097,10 @@ function summarizeRelatedRepoStatus(repo, status) {
|
|
|
880
1097
|
workspace: repo.workspace,
|
|
881
1098
|
isGitRepo: status?.isGitRepo === true,
|
|
882
1099
|
branch: status?.branch ?? null,
|
|
1100
|
+
upstream: status?.upstream ?? null,
|
|
1101
|
+
upstreamStatus: typeof status?.upstreamStatus === "string" ? status.upstreamStatus : status?.upstream ? "unchecked" : "no_upstream",
|
|
1102
|
+
upstreamFetchedAt: Number.isFinite(Number(status?.upstreamFetchedAt)) ? Number(status.upstreamFetchedAt) : null,
|
|
1103
|
+
upstreamFetchError: typeof status?.upstreamFetchError === "string" ? status.upstreamFetchError : null,
|
|
883
1104
|
ahead: Number.isFinite(Number(status?.ahead)) ? Number(status.ahead) : 0,
|
|
884
1105
|
behind: Number.isFinite(Number(status?.behind)) ? Number(status.behind) : 0,
|
|
885
1106
|
dirty,
|
|
@@ -896,7 +1117,7 @@ async function collectRelatedRepoStatuses(ctx, node) {
|
|
|
896
1117
|
const results = [];
|
|
897
1118
|
for (const repo of relatedRepos) {
|
|
898
1119
|
try {
|
|
899
|
-
const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace });
|
|
1120
|
+
const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false, true) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace, refreshUpstream: true });
|
|
900
1121
|
const status = extractGitStatus(statusResult);
|
|
901
1122
|
results.push(summarizeRelatedRepoStatus(repo, status));
|
|
902
1123
|
} catch (e) {
|
|
@@ -934,6 +1155,14 @@ function getNodeLaunchReadiness(node) {
|
|
|
934
1155
|
launchBlockedMessage: missingProviderPriorityMessage(node.id)
|
|
935
1156
|
};
|
|
936
1157
|
}
|
|
1158
|
+
async function collectLiveStatusSessions(ctx, node) {
|
|
1159
|
+
try {
|
|
1160
|
+
const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
|
|
1161
|
+
return extractStatusMetadataSessions(statusResult);
|
|
1162
|
+
} catch {
|
|
1163
|
+
return [];
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
937
1166
|
function readNumeric(value, fallback = 0) {
|
|
938
1167
|
const parsed = Number(value);
|
|
939
1168
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
@@ -944,11 +1173,13 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
|
|
|
944
1173
|
const ahead = readNumeric(status?.ahead);
|
|
945
1174
|
const behind = readNumeric(status?.behind);
|
|
946
1175
|
const upstream = readString(status?.upstream) ?? null;
|
|
1176
|
+
const upstreamStatus = readString(status?.upstreamStatus) ?? (upstream ? "unchecked" : "no_upstream");
|
|
947
1177
|
const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
|
|
948
1178
|
const base = {
|
|
949
1179
|
defaultBranch,
|
|
950
1180
|
branch,
|
|
951
1181
|
upstream,
|
|
1182
|
+
upstreamStatus,
|
|
952
1183
|
ahead,
|
|
953
1184
|
behind,
|
|
954
1185
|
isWorktree: node.isLocalWorktree === true,
|
|
@@ -982,6 +1213,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
|
|
|
982
1213
|
};
|
|
983
1214
|
}
|
|
984
1215
|
if (branch === defaultBranch) {
|
|
1216
|
+
if (upstream && upstreamStatus !== "fresh") {
|
|
1217
|
+
return {
|
|
1218
|
+
...base,
|
|
1219
|
+
status: "blocked_review",
|
|
1220
|
+
needsConvergence: true,
|
|
1221
|
+
reason: "default_branch_upstream_unverified",
|
|
1222
|
+
nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${node.id}'.`
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
985
1225
|
if (ahead > 0 || behind > 0) {
|
|
986
1226
|
return {
|
|
987
1227
|
...base,
|
|
@@ -1008,6 +1248,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
|
|
|
1008
1248
|
nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
|
|
1009
1249
|
};
|
|
1010
1250
|
}
|
|
1251
|
+
if (upstream && upstreamStatus !== "fresh") {
|
|
1252
|
+
return {
|
|
1253
|
+
...base,
|
|
1254
|
+
status: "blocked_review",
|
|
1255
|
+
needsConvergence: true,
|
|
1256
|
+
reason: "feature_branch_upstream_unverified",
|
|
1257
|
+
nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1011
1260
|
if (!upstream || ahead > 0 || behind > 0) {
|
|
1012
1261
|
return {
|
|
1013
1262
|
...base,
|
|
@@ -1049,7 +1298,81 @@ async function commandForNode(ctx, node, command, args = {}) {
|
|
|
1049
1298
|
if (isLocalTransport(ctx.transport)) {
|
|
1050
1299
|
return ctx.transport.command(command, args);
|
|
1051
1300
|
}
|
|
1052
|
-
|
|
1301
|
+
const identity = buildNodeMachineIdentity(ctx, node);
|
|
1302
|
+
throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}' (hostname=${identity.hostname || "unknown"}, coordinatorHostname=${identity.coordinatorHostname || "unknown"}, sameMachine=${identity.sameMachine})`);
|
|
1303
|
+
}
|
|
1304
|
+
function normalizePendingMeshCoordinatorEvents(value) {
|
|
1305
|
+
const payload = unwrapCommandPayload(value);
|
|
1306
|
+
const events = Array.isArray(payload?.events) ? payload.events : Array.isArray(value?.events) ? value.events : [];
|
|
1307
|
+
return events.filter((event) => event && typeof event === "object");
|
|
1308
|
+
}
|
|
1309
|
+
function buildMeshForwardPayloadFromPendingEvent(event) {
|
|
1310
|
+
const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : {};
|
|
1311
|
+
return {
|
|
1312
|
+
event: readString(event?.event),
|
|
1313
|
+
meshId: readString(event?.meshId),
|
|
1314
|
+
nodeId: readString(event?.nodeId) || readString(metadataEvent.meshNodeId),
|
|
1315
|
+
workspace: readString(event?.workspace) || readString(metadataEvent.workspace),
|
|
1316
|
+
targetSessionId: readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId),
|
|
1317
|
+
providerType: readString(metadataEvent.providerType),
|
|
1318
|
+
providerSessionId: readString(metadataEvent.providerSessionId),
|
|
1319
|
+
finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
|
|
1320
|
+
jobId: readString(metadataEvent.jobId),
|
|
1321
|
+
interactionId: readString(metadataEvent.interactionId),
|
|
1322
|
+
status: readString(metadataEvent.status),
|
|
1323
|
+
targetDaemonId: readString(metadataEvent.targetDaemonId),
|
|
1324
|
+
startedAt: readString(metadataEvent.startedAt),
|
|
1325
|
+
completedAt: readString(metadataEvent.completedAt),
|
|
1326
|
+
retryOfJobId: readString(metadataEvent.retryOfJobId),
|
|
1327
|
+
...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
|
|
1328
|
+
...metadataEvent.intentional === true ? { intentional: true } : {},
|
|
1329
|
+
...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
|
|
1330
|
+
...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
|
|
1331
|
+
...readString(metadataEvent.reason) ? { reason: readString(metadataEvent.reason) } : {},
|
|
1332
|
+
...readString(metadataEvent.stopReason) ? { stopReason: readString(metadataEvent.stopReason) } : {},
|
|
1333
|
+
...readString(metadataEvent.cleanupReason) ? { cleanupReason: readString(metadataEvent.cleanupReason) } : {},
|
|
1334
|
+
...readString(metadataEvent.source) ? { source: readString(metadataEvent.source) } : {}
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
async function drainCoordinatorPendingEvents(ctx, opts) {
|
|
1338
|
+
const requestedNodeIds = opts?.nodeIds?.length ? new Set(opts.nodeIds) : null;
|
|
1339
|
+
const matchesCurrentMesh = (event) => readString(event?.meshId) === ctx.mesh.id;
|
|
1340
|
+
if (ctx.transport instanceof IpcTransport) {
|
|
1341
|
+
const surfacedEvents = [];
|
|
1342
|
+
try {
|
|
1343
|
+
surfacedEvents.push(
|
|
1344
|
+
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
|
|
1345
|
+
);
|
|
1346
|
+
} catch {
|
|
1347
|
+
}
|
|
1348
|
+
for (const node of ctx.mesh.nodes) {
|
|
1349
|
+
if (!node.daemonId || isLocalControlPlaneNode(ctx, node)) continue;
|
|
1350
|
+
if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
|
|
1351
|
+
try {
|
|
1352
|
+
const remoteEvents = normalizePendingMeshCoordinatorEvents(
|
|
1353
|
+
await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", { meshId: ctx.mesh.id })
|
|
1354
|
+
).filter(matchesCurrentMesh);
|
|
1355
|
+
if (remoteEvents.length === 0) continue;
|
|
1356
|
+
for (const event of remoteEvents) {
|
|
1357
|
+
const payload = buildMeshForwardPayloadFromPendingEvent(event);
|
|
1358
|
+
if (!payload.event || !payload.meshId) continue;
|
|
1359
|
+
await ctx.transport.command("mesh_forward_event", payload);
|
|
1360
|
+
}
|
|
1361
|
+
} catch {
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
try {
|
|
1365
|
+
surfacedEvents.push(
|
|
1366
|
+
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
|
|
1367
|
+
);
|
|
1368
|
+
} catch {
|
|
1369
|
+
}
|
|
1370
|
+
return surfacedEvents;
|
|
1371
|
+
}
|
|
1372
|
+
if (isLocalTransport(ctx.transport)) {
|
|
1373
|
+
return (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id).filter(matchesCurrentMesh);
|
|
1374
|
+
}
|
|
1375
|
+
return [];
|
|
1053
1376
|
}
|
|
1054
1377
|
function isP2pTransportUnavailableError(error) {
|
|
1055
1378
|
return (0, import_daemon_core.isP2pRelayTransportFailure)(error);
|
|
@@ -1088,7 +1411,9 @@ var MESH_ENQUEUE_TASK_TOOL = {
|
|
|
1088
1411
|
inputSchema: {
|
|
1089
1412
|
type: "object",
|
|
1090
1413
|
properties: {
|
|
1091
|
-
message: { type: "string", description: "The task instruction for the agent." }
|
|
1414
|
+
message: { type: "string", description: "The task instruction for the agent." },
|
|
1415
|
+
task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before dispatch." },
|
|
1416
|
+
taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
|
|
1092
1417
|
},
|
|
1093
1418
|
required: ["message"]
|
|
1094
1419
|
}
|
|
@@ -1148,7 +1473,9 @@ var MESH_SEND_TASK_TOOL = {
|
|
|
1148
1473
|
properties: {
|
|
1149
1474
|
node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
|
|
1150
1475
|
session_id: { type: "string", description: "Agent session ID on the target node." },
|
|
1151
|
-
message: { type: "string", description: "Natural-language task to send to the agent." }
|
|
1476
|
+
message: { type: "string", description: "Natural-language task to send to the agent." },
|
|
1477
|
+
task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before local or remote direct dispatch." },
|
|
1478
|
+
taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
|
|
1152
1479
|
},
|
|
1153
1480
|
required: ["node_id", "session_id", "message"]
|
|
1154
1481
|
}
|
|
@@ -1206,6 +1533,21 @@ var MESH_GIT_STATUS_TOOL = {
|
|
|
1206
1533
|
required: ["node_id"]
|
|
1207
1534
|
}
|
|
1208
1535
|
};
|
|
1536
|
+
var MESH_FAST_FORWARD_NODE_TOOL = {
|
|
1537
|
+
name: "mesh_fast_forward_node",
|
|
1538
|
+
description: "Safely dry-run or execute an obvious direct fast-forward for a mesh node without launching an agent session. Defaults to dry-run; execution requires execute=true. Never pushes, rebases, resets, cleans, or checks out arbitrary revisions.",
|
|
1539
|
+
inputSchema: {
|
|
1540
|
+
type: "object",
|
|
1541
|
+
properties: {
|
|
1542
|
+
node_id: { type: "string", description: "Target node ID." },
|
|
1543
|
+
branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
|
|
1544
|
+
execute: { type: "boolean", description: "When true, apply the fast-forward if all safety gates pass. Defaults false/dry-run." },
|
|
1545
|
+
dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
|
|
1546
|
+
update_submodules: { type: "boolean", description: "When true, if the root fast-forward changes gitlinks, run only git submodule update --init --recursive and verify submodules clean." }
|
|
1547
|
+
},
|
|
1548
|
+
required: ["node_id"]
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1209
1551
|
var MESH_CHECKPOINT_TOOL = {
|
|
1210
1552
|
name: "mesh_checkpoint",
|
|
1211
1553
|
description: "Create a git checkpoint (commit) on a mesh node workspace.",
|
|
@@ -1289,7 +1631,7 @@ var MESH_TASK_HISTORY_TOOL = {
|
|
|
1289
1631
|
type: "object",
|
|
1290
1632
|
properties: {
|
|
1291
1633
|
tail: { type: "number", description: "Number of recent entries to return (default: 20)." },
|
|
1292
|
-
kind: { type: "string", description: "Filter by entry kind: task_dispatched, task_completed, task_failed, task_stalled, session_launched, checkpoint_created, node_cloned, node_removed." }
|
|
1634
|
+
kind: { type: "string", description: "Filter by entry kind: task_dispatched, task_completed, task_failed, task_stalled, session_launched, checkpoint_created, node_cloned, node_removed, direct_fast_forward." }
|
|
1293
1635
|
}
|
|
1294
1636
|
}
|
|
1295
1637
|
};
|
|
@@ -1309,7 +1651,7 @@ var MESH_RECONCILE_LEDGER_TOOL = {
|
|
|
1309
1651
|
};
|
|
1310
1652
|
var MESH_REFINE_NODE_TOOL = {
|
|
1311
1653
|
name: "mesh_refine_node",
|
|
1312
|
-
description: "The Refinery:
|
|
1654
|
+
description: "The Refinery: Accept an async validation/merge/cleanup job for a completed worktree node. The immediate response includes async:true, status:'accepted', jobId, interactionId, target node, and startedAt; completion/failure evidence is delivered through pending mesh events and the mesh task ledger.",
|
|
1313
1655
|
inputSchema: {
|
|
1314
1656
|
type: "object",
|
|
1315
1657
|
properties: {
|
|
@@ -1318,6 +1660,43 @@ var MESH_REFINE_NODE_TOOL = {
|
|
|
1318
1660
|
required: ["node_id"]
|
|
1319
1661
|
}
|
|
1320
1662
|
};
|
|
1663
|
+
var MESH_REFINE_CONFIG_SCHEMA_TOOL = {
|
|
1664
|
+
name: "mesh_refine_config_schema",
|
|
1665
|
+
description: "Return the Repo Mesh Refinery config JSON schema and supported repo-local config locations. This is the validation source of truth; heuristic command detection is suggestions-only.",
|
|
1666
|
+
inputSchema: { type: "object", properties: {} }
|
|
1667
|
+
};
|
|
1668
|
+
var MESH_VALIDATE_REFINE_CONFIG_TOOL = {
|
|
1669
|
+
name: "mesh_validate_refine_config",
|
|
1670
|
+
description: "Validate the repo mesh/refine config for a node/workspace without running validation commands or merging.",
|
|
1671
|
+
inputSchema: {
|
|
1672
|
+
type: "object",
|
|
1673
|
+
properties: {
|
|
1674
|
+
node_id: { type: "string", description: "Optional node/workspace whose refine config should be loaded. Defaults to the first mesh node." },
|
|
1675
|
+
config: { type: "object", description: "Optional inline config object to validate instead of loading from the repo." }
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
var MESH_SUGGEST_REFINE_CONFIG_TOOL = {
|
|
1680
|
+
name: "mesh_suggest_refine_config",
|
|
1681
|
+
description: "Suggest a repo mesh/refine config scaffold from project context/package scripts. Suggestions are never executed until saved as explicit refine config.",
|
|
1682
|
+
inputSchema: {
|
|
1683
|
+
type: "object",
|
|
1684
|
+
properties: {
|
|
1685
|
+
node_id: { type: "string", description: "Optional node/workspace used for suggestions. Defaults to the first mesh node." }
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
var MESH_REFINE_PLAN_TOOL = {
|
|
1690
|
+
name: "mesh_refine_plan",
|
|
1691
|
+
description: "Dry-run Refinery plan for a worktree node: reports config source, validation commands, suggestions/unavailable reason, and merge/cleanup intent without executing validation or git merge.",
|
|
1692
|
+
inputSchema: {
|
|
1693
|
+
type: "object",
|
|
1694
|
+
properties: {
|
|
1695
|
+
node_id: { type: "string", description: "Node ID of the worktree node to plan." }
|
|
1696
|
+
},
|
|
1697
|
+
required: ["node_id"]
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1321
1700
|
var ALL_MESH_TOOLS = [
|
|
1322
1701
|
MESH_STATUS_TOOL,
|
|
1323
1702
|
MESH_LIST_NODES_TOOL,
|
|
@@ -1330,11 +1709,16 @@ var ALL_MESH_TOOLS = [
|
|
|
1330
1709
|
MESH_READ_DEBUG_TOOL,
|
|
1331
1710
|
MESH_LAUNCH_SESSION_TOOL,
|
|
1332
1711
|
MESH_GIT_STATUS_TOOL,
|
|
1712
|
+
MESH_FAST_FORWARD_NODE_TOOL,
|
|
1333
1713
|
MESH_CHECKPOINT_TOOL,
|
|
1334
1714
|
MESH_APPROVE_TOOL,
|
|
1335
1715
|
MESH_CLONE_NODE_TOOL,
|
|
1336
1716
|
MESH_REMOVE_NODE_TOOL,
|
|
1337
1717
|
MESH_REFINE_NODE_TOOL,
|
|
1718
|
+
MESH_REFINE_CONFIG_SCHEMA_TOOL,
|
|
1719
|
+
MESH_VALIDATE_REFINE_CONFIG_TOOL,
|
|
1720
|
+
MESH_SUGGEST_REFINE_CONFIG_TOOL,
|
|
1721
|
+
MESH_REFINE_PLAN_TOOL,
|
|
1338
1722
|
MESH_CLEANUP_SESSIONS_TOOL,
|
|
1339
1723
|
MESH_TASK_HISTORY_TOOL,
|
|
1340
1724
|
MESH_RECONCILE_LEDGER_TOOL
|
|
@@ -1348,15 +1732,19 @@ async function meshStatus(ctx) {
|
|
|
1348
1732
|
const entry = {
|
|
1349
1733
|
nodeId: node.id,
|
|
1350
1734
|
workspace: node.workspace,
|
|
1735
|
+
machine: buildNodeMachineIdentity(ctx, node),
|
|
1736
|
+
daemonId: readNodeDaemonId(node),
|
|
1737
|
+
machineId: readNodeMachineId(node),
|
|
1351
1738
|
...getNodeLaunchReadiness(node)
|
|
1352
1739
|
};
|
|
1353
1740
|
try {
|
|
1354
1741
|
if (!isLocalTransport(transport) && node.daemonId) {
|
|
1355
|
-
const result = await transport.gitStatus(node.daemonId, node.workspace, false);
|
|
1742
|
+
const result = await transport.gitStatus(node.daemonId, node.workspace, false, true);
|
|
1356
1743
|
const status = extractGitStatus(result);
|
|
1357
1744
|
const uncommittedChanges = countUncommittedChanges(status);
|
|
1358
1745
|
const dirty = isGitStatusDirty(status);
|
|
1359
1746
|
entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
|
|
1747
|
+
assignFullGitSnapshot(entry, status);
|
|
1360
1748
|
entry.branch = status?.branch;
|
|
1361
1749
|
entry.isDirty = dirty;
|
|
1362
1750
|
entry.uncommittedChanges = uncommittedChanges;
|
|
@@ -1370,6 +1758,7 @@ async function meshStatus(ctx) {
|
|
|
1370
1758
|
const autoDiscover = node.policy?.autoDiscoverSubmodules !== false;
|
|
1371
1759
|
const statusResult = await commandForNode(ctx, node, "git_status", {
|
|
1372
1760
|
workspace: node.workspace,
|
|
1761
|
+
refreshUpstream: true,
|
|
1373
1762
|
includeSubmodules: autoDiscover,
|
|
1374
1763
|
submoduleIgnorePaths: node.policy?.submoduleIgnorePaths || void 0
|
|
1375
1764
|
});
|
|
@@ -1377,6 +1766,7 @@ async function meshStatus(ctx) {
|
|
|
1377
1766
|
const uncommittedChanges = countUncommittedChanges(status);
|
|
1378
1767
|
const dirty = isGitStatusDirty(status);
|
|
1379
1768
|
entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
|
|
1769
|
+
assignFullGitSnapshot(entry, status);
|
|
1380
1770
|
entry.branch = status?.branch;
|
|
1381
1771
|
entry.isDirty = dirty;
|
|
1382
1772
|
entry.uncommittedChanges = uncommittedChanges;
|
|
@@ -1452,15 +1842,35 @@ async function meshStatus(ctx) {
|
|
|
1452
1842
|
}
|
|
1453
1843
|
const relatedRepos = await collectRelatedRepoStatuses(ctx, node);
|
|
1454
1844
|
if (relatedRepos.length) entry.relatedRepos = relatedRepos;
|
|
1845
|
+
const liveSessions = await collectLiveStatusSessions(ctx, node);
|
|
1846
|
+
if (liveSessions.length > 0) {
|
|
1847
|
+
entry.sessions = liveSessions;
|
|
1848
|
+
}
|
|
1455
1849
|
results.push(entry);
|
|
1456
1850
|
}
|
|
1851
|
+
const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
|
|
1852
|
+
meshId: mesh.id,
|
|
1853
|
+
queue: (0, import_daemon_core.getQueue)(mesh.id),
|
|
1854
|
+
ledgerEntries: (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 500 }),
|
|
1855
|
+
nodes: results
|
|
1856
|
+
});
|
|
1457
1857
|
const response = {
|
|
1458
1858
|
meshId: mesh.id,
|
|
1459
1859
|
meshName: mesh.name,
|
|
1460
1860
|
repoIdentity: mesh.repoIdentity,
|
|
1461
1861
|
policy: mesh.policy,
|
|
1462
1862
|
refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1863
|
+
sourceOfTruth: {
|
|
1864
|
+
membership: "coordinator_daemon_live_mesh",
|
|
1865
|
+
currentStatus: "live_git_and_session_probes",
|
|
1866
|
+
activeWork: "mesh_queue_file_and_local_ledger",
|
|
1867
|
+
historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
|
|
1868
|
+
},
|
|
1463
1869
|
nodes: results,
|
|
1870
|
+
activeWork: activeWorkEvidence.activeWork,
|
|
1871
|
+
staleDirectWork: activeWorkEvidence.staleDirectWork,
|
|
1872
|
+
terminalDirectWork: activeWorkEvidence.terminalDirectWork,
|
|
1873
|
+
activeWorkSummary: activeWorkEvidence.summary,
|
|
1464
1874
|
branchConvergenceSummary: summarizeBranchConvergence(results)
|
|
1465
1875
|
};
|
|
1466
1876
|
try {
|
|
@@ -1468,13 +1878,7 @@ async function meshStatus(ctx) {
|
|
|
1468
1878
|
} catch {
|
|
1469
1879
|
}
|
|
1470
1880
|
try {
|
|
1471
|
-
|
|
1472
|
-
if (ctx.transport instanceof IpcTransport) {
|
|
1473
|
-
const eventsResult = await ctx.transport.command("get_pending_mesh_events", {});
|
|
1474
|
-
pendingEvents = Array.isArray(eventsResult?.events) ? eventsResult.events : [];
|
|
1475
|
-
} else if (isLocalTransport(ctx.transport)) {
|
|
1476
|
-
pendingEvents = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)();
|
|
1477
|
-
}
|
|
1881
|
+
const pendingEvents = await drainCoordinatorPendingEvents(ctx);
|
|
1478
1882
|
if (pendingEvents.length > 0) {
|
|
1479
1883
|
response.pendingCoordinatorEvents = pendingEvents;
|
|
1480
1884
|
}
|
|
@@ -1484,11 +1888,17 @@ async function meshStatus(ctx) {
|
|
|
1484
1888
|
}
|
|
1485
1889
|
async function meshTaskHistory(ctx, args) {
|
|
1486
1890
|
const { mesh } = ctx;
|
|
1891
|
+
const pendingEvents = await drainCoordinatorPendingEvents(ctx);
|
|
1487
1892
|
const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
|
|
1488
1893
|
const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
|
|
1489
1894
|
const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
|
|
1490
1895
|
const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
|
|
1491
|
-
return JSON.stringify({
|
|
1896
|
+
return JSON.stringify({
|
|
1897
|
+
meshId: mesh.id,
|
|
1898
|
+
entries,
|
|
1899
|
+
summary,
|
|
1900
|
+
...pendingEvents.length > 0 ? { pendingCoordinatorEvents: pendingEvents } : {}
|
|
1901
|
+
}, null, 2);
|
|
1492
1902
|
}
|
|
1493
1903
|
async function meshReconcileLedger(ctx, args) {
|
|
1494
1904
|
await refreshMeshFromDaemon(ctx);
|
|
@@ -1578,6 +1988,9 @@ async function meshListNodes(ctx) {
|
|
|
1578
1988
|
nodeId: n.id,
|
|
1579
1989
|
workspace: n.workspace,
|
|
1580
1990
|
repoRoot: n.repoRoot,
|
|
1991
|
+
daemonId: readNodeDaemonId(n),
|
|
1992
|
+
machineId: readNodeMachineId(n),
|
|
1993
|
+
machine: buildNodeMachineIdentity(ctx, n),
|
|
1581
1994
|
isLocalWorktree: n.isLocalWorktree,
|
|
1582
1995
|
policy: n.policy,
|
|
1583
1996
|
relatedRepos: readRelatedRepos(n),
|
|
@@ -1587,12 +2000,13 @@ async function meshListNodes(ctx) {
|
|
|
1587
2000
|
}, null, 2);
|
|
1588
2001
|
}
|
|
1589
2002
|
async function meshEnqueueTask(ctx, args) {
|
|
2003
|
+
const taskMode = readString(args.task_mode) || readString(args.taskMode);
|
|
1590
2004
|
try {
|
|
1591
|
-
const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message);
|
|
2005
|
+
const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode });
|
|
1592
2006
|
if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
|
|
1593
2007
|
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
1594
2008
|
});
|
|
1595
|
-
return JSON.stringify({ success: true, taskId: task.id, status: task.status });
|
|
2009
|
+
return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
|
|
1596
2010
|
}
|
|
1597
2011
|
if (ctx.transport instanceof IpcTransport) {
|
|
1598
2012
|
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
@@ -1605,11 +2019,24 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
1605
2019
|
ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
|
|
1606
2020
|
if (result.success) {
|
|
1607
2021
|
try {
|
|
2022
|
+
const providerType = result.providerType;
|
|
2023
|
+
const descriptor = summarizeTaskMessage(args.message);
|
|
1608
2024
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
1609
2025
|
kind: "task_dispatched",
|
|
1610
2026
|
nodeId: node.id,
|
|
1611
2027
|
sessionId: result.sessionId,
|
|
1612
|
-
|
|
2028
|
+
providerType,
|
|
2029
|
+
payload: {
|
|
2030
|
+
source: "queue",
|
|
2031
|
+
via: "p2p_direct",
|
|
2032
|
+
taskId: task.id,
|
|
2033
|
+
message: args.message,
|
|
2034
|
+
taskTitle: descriptor.taskTitle,
|
|
2035
|
+
taskSummary: descriptor.taskSummary,
|
|
2036
|
+
...task.taskMode ? { taskMode: task.taskMode } : {},
|
|
2037
|
+
...providerType ? { providerType } : {},
|
|
2038
|
+
targetSessionId: result.sessionId
|
|
2039
|
+
}
|
|
1613
2040
|
});
|
|
1614
2041
|
} catch {
|
|
1615
2042
|
}
|
|
@@ -1620,22 +2047,32 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
1620
2047
|
}
|
|
1621
2048
|
Promise.all(dispatchPromises).catch(() => {
|
|
1622
2049
|
});
|
|
1623
|
-
return JSON.stringify({ success: true, taskId: task.id, status: task.status });
|
|
2050
|
+
return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
|
|
1624
2051
|
}
|
|
1625
|
-
return JSON.stringify({ success: true, taskId: task.id, status: task.status });
|
|
2052
|
+
return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
|
|
1626
2053
|
} catch (e) {
|
|
1627
|
-
|
|
2054
|
+
const message = e?.message || String(e);
|
|
2055
|
+
if (message.includes("live_debug_readonly_guardrail_violation")) {
|
|
2056
|
+
return JSON.stringify({ success: false, code: "live_debug_readonly_guardrail_violation", taskMode, error: message });
|
|
2057
|
+
}
|
|
2058
|
+
return JSON.stringify({ success: false, error: message });
|
|
1628
2059
|
}
|
|
1629
2060
|
}
|
|
1630
2061
|
async function meshViewQueue(ctx, args) {
|
|
1631
2062
|
try {
|
|
1632
2063
|
const statusFilter = sanitizeQueueStatusFilter(args.status);
|
|
1633
2064
|
const view = normalizeQueueViewMode(args.view);
|
|
1634
|
-
const fullQueue = annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh);
|
|
2065
|
+
const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh));
|
|
1635
2066
|
const queue = filterQueueForView(fullQueue, view, statusFilter);
|
|
1636
2067
|
const summary = buildQueueStatusSummary(fullQueue);
|
|
1637
2068
|
const visibleSummary = buildQueueStatusSummary(queue);
|
|
1638
2069
|
const maintenance = buildQueueMaintenanceReport(fullQueue);
|
|
2070
|
+
const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
|
|
2071
|
+
meshId: ctx.mesh.id,
|
|
2072
|
+
queue: fullQueue,
|
|
2073
|
+
ledgerEntries: (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 }),
|
|
2074
|
+
nodes: ctx.mesh.nodes
|
|
2075
|
+
});
|
|
1639
2076
|
const staleAssignedTasks = maintenance.staleAssignedTasks || [];
|
|
1640
2077
|
const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
|
|
1641
2078
|
return JSON.stringify({
|
|
@@ -1653,6 +2090,9 @@ async function meshViewQueue(ctx, args) {
|
|
|
1653
2090
|
},
|
|
1654
2091
|
queue,
|
|
1655
2092
|
visibleQueue: queue,
|
|
2093
|
+
activeWork: activeWorkEvidence.activeWork,
|
|
2094
|
+
staleDirectWork: activeWorkEvidence.staleDirectWork,
|
|
2095
|
+
activeWorkSummary: activeWorkEvidence.summary,
|
|
1656
2096
|
visibleSummary,
|
|
1657
2097
|
summary,
|
|
1658
2098
|
activeCounts: summary.activeCounts,
|
|
@@ -1686,6 +2126,10 @@ async function meshQueueCancel(ctx, args) {
|
|
|
1686
2126
|
if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
|
|
1687
2127
|
const task = (0, import_daemon_core.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
|
|
1688
2128
|
if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
|
|
2129
|
+
if (isLocalTransport(ctx.transport)) {
|
|
2130
|
+
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
1689
2133
|
return JSON.stringify({ success: true, task }, null, 2);
|
|
1690
2134
|
} catch (e) {
|
|
1691
2135
|
return JSON.stringify({ success: false, error: e.message });
|
|
@@ -1716,6 +2160,19 @@ async function meshQueueRequeue(ctx, args) {
|
|
|
1716
2160
|
}
|
|
1717
2161
|
}
|
|
1718
2162
|
async function meshSendTask(ctx, args) {
|
|
2163
|
+
const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
|
|
2164
|
+
const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
|
|
2165
|
+
if (!modeValidation.valid) {
|
|
2166
|
+
return JSON.stringify({
|
|
2167
|
+
success: false,
|
|
2168
|
+
code: "live_debug_readonly_guardrail_violation",
|
|
2169
|
+
taskMode: modeValidation.taskMode || requestedTaskMode,
|
|
2170
|
+
violations: modeValidation.violations,
|
|
2171
|
+
allowedOperations: modeValidation.allowedOperations,
|
|
2172
|
+
error: `live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
const taskMode = modeValidation.taskMode;
|
|
1719
2176
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
1720
2177
|
if (node.policy?.readOnly) {
|
|
1721
2178
|
return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
|
|
@@ -1743,13 +2200,15 @@ async function meshSendTask(ctx, args) {
|
|
|
1743
2200
|
const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
|
|
1744
2201
|
meshId: ctx.mesh.id,
|
|
1745
2202
|
message: args.message,
|
|
1746
|
-
targetNodeId: args.node_id
|
|
2203
|
+
targetNodeId: args.node_id,
|
|
2204
|
+
...taskMode ? { taskMode } : {}
|
|
1747
2205
|
});
|
|
1748
2206
|
return JSON.stringify(res);
|
|
1749
2207
|
}
|
|
1750
2208
|
const isLocalNode = isLocalControlPlaneNode(ctx, node);
|
|
1751
2209
|
if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
|
|
1752
2210
|
const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
|
|
2211
|
+
const taskId = (0, import_node_crypto.randomUUID)();
|
|
1753
2212
|
const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
|
|
1754
2213
|
session_id: args.session_id,
|
|
1755
2214
|
message: args.message,
|
|
@@ -1758,60 +2217,123 @@ async function meshSendTask(ctx, args) {
|
|
|
1758
2217
|
if (result2.success) {
|
|
1759
2218
|
const dispatchedSessionId = args.session_id || result2.sessionId;
|
|
1760
2219
|
try {
|
|
2220
|
+
const providerType = result2.providerType || cached?.providerType;
|
|
1761
2221
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
1762
2222
|
kind: "task_dispatched",
|
|
1763
2223
|
nodeId: args.node_id,
|
|
1764
2224
|
sessionId: dispatchedSessionId,
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
2225
|
+
providerType,
|
|
2226
|
+
payload: buildDirectTaskPayload(args.message, "p2p_direct", {
|
|
2227
|
+
taskId,
|
|
2228
|
+
taskMode,
|
|
2229
|
+
providerType,
|
|
2230
|
+
targetSessionId: dispatchedSessionId
|
|
2231
|
+
})
|
|
1770
2232
|
});
|
|
1771
2233
|
} catch {
|
|
1772
2234
|
}
|
|
1773
2235
|
}
|
|
1774
|
-
return JSON.stringify({
|
|
2236
|
+
return JSON.stringify({
|
|
2237
|
+
...result2,
|
|
2238
|
+
nodeId: args.node_id,
|
|
2239
|
+
sessionId: result2.success ? args.session_id || result2.sessionId : args.session_id,
|
|
2240
|
+
...result2.success ? { source: "direct", taskId } : {},
|
|
2241
|
+
taskMode,
|
|
2242
|
+
...result2.success && result2.providerType ? { providerType: result2.providerType } : {},
|
|
2243
|
+
dispatched: result2.success === true
|
|
2244
|
+
});
|
|
1775
2245
|
}
|
|
1776
2246
|
if (args.session_id && isLocalTransport(ctx.transport)) {
|
|
1777
2247
|
const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
|
|
2248
|
+
let resolvedProviderType = cached?.providerType || "";
|
|
2249
|
+
if (!resolvedProviderType) {
|
|
2250
|
+
const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
|
|
2251
|
+
const sessions = extractStatusMetadataSessions(statusResult);
|
|
2252
|
+
const explicitSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
|
|
2253
|
+
if (!explicitSession) {
|
|
2254
|
+
return JSON.stringify({
|
|
2255
|
+
success: false,
|
|
2256
|
+
recoverable: true,
|
|
2257
|
+
code: "mesh_target_session_not_found",
|
|
2258
|
+
reason: "mesh_target_session_not_found",
|
|
2259
|
+
transport: "local_ipc",
|
|
2260
|
+
retryRecommended: true,
|
|
2261
|
+
nodeId: args.node_id,
|
|
2262
|
+
sessionId: args.session_id,
|
|
2263
|
+
error: `Local session '${args.session_id}' is not present in live status for node '${args.node_id}'.`,
|
|
2264
|
+
nextAction: `Launch a fresh session with mesh_launch_session(node_id: '${args.node_id}') or retry without session_id so Repo Mesh can target a live delegate session.`
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
resolvedProviderType = resolveSessionProviderType(explicitSession);
|
|
2268
|
+
if (resolvedProviderType) {
|
|
2269
|
+
meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
|
|
2270
|
+
providerType: resolvedProviderType,
|
|
2271
|
+
providerSessionId: readString(explicitSession?.providerSessionId) || void 0
|
|
2272
|
+
});
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
if (!resolvedProviderType) {
|
|
2276
|
+
return JSON.stringify({
|
|
2277
|
+
success: false,
|
|
2278
|
+
recoverable: true,
|
|
2279
|
+
code: "mesh_target_session_provider_unknown",
|
|
2280
|
+
reason: "mesh_target_session_provider_unknown",
|
|
2281
|
+
transport: "local_ipc",
|
|
2282
|
+
retryRecommended: false,
|
|
2283
|
+
nodeId: args.node_id,
|
|
2284
|
+
sessionId: args.session_id,
|
|
2285
|
+
error: `Local session '${args.session_id}' is live but does not expose providerType/cliType, so agent_command cannot be routed safely.`,
|
|
2286
|
+
nextAction: `Relaunch the target session on node '${args.node_id}' or retry without session_id so Repo Mesh can pick a session with provider metadata.`
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
1778
2289
|
const dispatchResult = await commandForNode(ctx, node, "agent_command", {
|
|
1779
2290
|
targetSessionId: args.session_id,
|
|
1780
|
-
|
|
2291
|
+
agentType: resolvedProviderType,
|
|
2292
|
+
cliType: resolvedProviderType,
|
|
2293
|
+
providerType: resolvedProviderType,
|
|
1781
2294
|
action: "send_chat",
|
|
1782
2295
|
message: args.message
|
|
1783
2296
|
});
|
|
1784
2297
|
const dispatchPayload = unwrapCommandPayload(dispatchResult);
|
|
1785
2298
|
if (dispatchPayload?.success === false || dispatchResult?.success === false) {
|
|
2299
|
+
const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
|
|
1786
2300
|
return JSON.stringify({
|
|
2301
|
+
...source && typeof source === "object" ? source : {},
|
|
1787
2302
|
success: false,
|
|
1788
2303
|
nodeId: args.node_id,
|
|
1789
2304
|
sessionId: args.session_id,
|
|
1790
2305
|
error: dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"
|
|
1791
2306
|
});
|
|
1792
2307
|
}
|
|
2308
|
+
const taskId = (0, import_node_crypto.randomUUID)();
|
|
1793
2309
|
try {
|
|
1794
2310
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
1795
2311
|
kind: "task_dispatched",
|
|
1796
2312
|
nodeId: args.node_id,
|
|
1797
2313
|
sessionId: args.session_id,
|
|
1798
|
-
providerType:
|
|
1799
|
-
payload:
|
|
2314
|
+
providerType: resolvedProviderType,
|
|
2315
|
+
payload: buildDirectTaskPayload(args.message, "local_direct", {
|
|
2316
|
+
taskId,
|
|
2317
|
+
taskMode,
|
|
2318
|
+
providerType: resolvedProviderType,
|
|
2319
|
+
targetSessionId: args.session_id
|
|
2320
|
+
})
|
|
1800
2321
|
});
|
|
1801
2322
|
} catch {
|
|
1802
2323
|
}
|
|
1803
|
-
return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
|
|
2324
|
+
return JSON.stringify({ success: true, dispatched: true, source: "direct", taskId, taskMode, providerType: resolvedProviderType, nodeId: args.node_id, sessionId: args.session_id });
|
|
1804
2325
|
}
|
|
1805
2326
|
const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
|
|
1806
2327
|
targetNodeId: args.node_id,
|
|
1807
|
-
targetSessionId: args.session_id
|
|
2328
|
+
targetSessionId: args.session_id,
|
|
2329
|
+
taskMode
|
|
1808
2330
|
});
|
|
1809
2331
|
if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
|
|
1810
2332
|
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
1811
2333
|
});
|
|
1812
2334
|
}
|
|
1813
|
-
const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)() : [];
|
|
1814
|
-
const result = { success: true, nodeId: args.node_id, taskId: task.id, status: task.status };
|
|
2335
|
+
const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id) : [];
|
|
2336
|
+
const result = { success: true, source: "queue", nodeId: args.node_id, taskId: task.id, status: task.status, taskMode: task.taskMode };
|
|
1815
2337
|
if (pendingEvents.length > 0) {
|
|
1816
2338
|
result.pendingCoordinatorEvents = pendingEvents;
|
|
1817
2339
|
}
|
|
@@ -1831,6 +2353,9 @@ async function meshReadChat(ctx, args) {
|
|
|
1831
2353
|
if (!node) {
|
|
1832
2354
|
return JSON.stringify(buildMissingNodeReadChatRecovery(ctx, args), null, 2);
|
|
1833
2355
|
}
|
|
2356
|
+
if (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport)) {
|
|
2357
|
+
await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
|
|
2358
|
+
}
|
|
1834
2359
|
if (isLocalTransport(ctx.transport)) {
|
|
1835
2360
|
const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
|
|
1836
2361
|
const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
|
|
@@ -1933,6 +2458,10 @@ async function meshLaunchSession(ctx, args) {
|
|
|
1933
2458
|
const coordinatorNode = resolveCoordinatorNode(ctx);
|
|
1934
2459
|
const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
|
|
1935
2460
|
const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
|
|
2461
|
+
const isLocalNode = isLocalControlPlaneNode(ctx, node);
|
|
2462
|
+
if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
|
|
2463
|
+
return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
|
|
2464
|
+
}
|
|
1936
2465
|
let result;
|
|
1937
2466
|
try {
|
|
1938
2467
|
result = await commandForNode(ctx, node, "launch_cli", {
|
|
@@ -1973,7 +2502,6 @@ async function meshLaunchSession(ctx, args) {
|
|
|
1973
2502
|
});
|
|
1974
2503
|
} catch {
|
|
1975
2504
|
}
|
|
1976
|
-
const isLocalNode = isLocalControlPlaneNode(ctx, node);
|
|
1977
2505
|
if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
|
|
1978
2506
|
ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
1979
2507
|
});
|
|
@@ -1998,6 +2526,9 @@ async function meshLaunchSession(ctx, args) {
|
|
|
1998
2526
|
const coordinatorNode = resolveCoordinatorNode(ctx);
|
|
1999
2527
|
const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
|
|
2000
2528
|
const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
|
|
2529
|
+
if (!coordinatorDaemonId) {
|
|
2530
|
+
return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
|
|
2531
|
+
}
|
|
2001
2532
|
try {
|
|
2002
2533
|
const res = await ctx.transport.launch(node.daemonId, {
|
|
2003
2534
|
type: resolvedProviderType,
|
|
@@ -2036,7 +2567,7 @@ async function meshGitStatus(ctx, args) {
|
|
|
2036
2567
|
const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
|
|
2037
2568
|
try {
|
|
2038
2569
|
if (!isLocalTransport(ctx.transport) && node.daemonId) {
|
|
2039
|
-
const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true);
|
|
2570
|
+
const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true, true);
|
|
2040
2571
|
return JSON.stringify({
|
|
2041
2572
|
nodeId: args.node_id,
|
|
2042
2573
|
workspace: node.workspace,
|
|
@@ -2048,6 +2579,7 @@ async function meshGitStatus(ctx, args) {
|
|
|
2048
2579
|
} else if (isLocalTransport(ctx.transport)) {
|
|
2049
2580
|
const statusResult = await commandForNode(ctx, node, "git_status", {
|
|
2050
2581
|
workspace: node.workspace,
|
|
2582
|
+
refreshUpstream: true,
|
|
2051
2583
|
includeSubmodules: autoDiscoverSubmodules,
|
|
2052
2584
|
submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
|
|
2053
2585
|
});
|
|
@@ -2077,6 +2609,51 @@ async function meshGitStatus(ctx, args) {
|
|
|
2077
2609
|
}, null, 2);
|
|
2078
2610
|
}
|
|
2079
2611
|
}
|
|
2612
|
+
async function meshFastForwardNode(ctx, args) {
|
|
2613
|
+
await refreshMeshFromDaemon(ctx);
|
|
2614
|
+
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2615
|
+
const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
|
|
2616
|
+
if (node.policy?.readOnly) {
|
|
2617
|
+
return JSON.stringify({
|
|
2618
|
+
success: false,
|
|
2619
|
+
code: "node_read_only",
|
|
2620
|
+
nodeId: args.node_id,
|
|
2621
|
+
workspace: node.workspace,
|
|
2622
|
+
allowed: false,
|
|
2623
|
+
willRun: false,
|
|
2624
|
+
executed: false,
|
|
2625
|
+
blockingReasons: ["node_read_only"]
|
|
2626
|
+
}, null, 2);
|
|
2627
|
+
}
|
|
2628
|
+
try {
|
|
2629
|
+
const dryRun = args.dry_run === true || args.execute !== true;
|
|
2630
|
+
const result = await commandForNode(ctx, node, "fast_forward_mesh_node", {
|
|
2631
|
+
meshId: ctx.mesh.id,
|
|
2632
|
+
nodeId: node.id,
|
|
2633
|
+
workspace: node.workspace,
|
|
2634
|
+
branch: typeof args.branch === "string" ? args.branch : void 0,
|
|
2635
|
+
execute: args.execute === true && args.dry_run !== true,
|
|
2636
|
+
dryRun,
|
|
2637
|
+
updateSubmodules: args.update_submodules === true,
|
|
2638
|
+
submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
|
|
2639
|
+
});
|
|
2640
|
+
return JSON.stringify(unwrapCommandPayload(result), null, 2);
|
|
2641
|
+
} catch (e) {
|
|
2642
|
+
const failure = buildCoordinatorP2pRelayFailure(e, {
|
|
2643
|
+
command: "fast_forward_mesh_node",
|
|
2644
|
+
targetDaemonId: node.daemonId,
|
|
2645
|
+
nodeId: args.node_id
|
|
2646
|
+
});
|
|
2647
|
+
return JSON.stringify({
|
|
2648
|
+
...failure,
|
|
2649
|
+
workspace: node.workspace,
|
|
2650
|
+
allowed: false,
|
|
2651
|
+
willRun: false,
|
|
2652
|
+
executed: false,
|
|
2653
|
+
blockingReasons: [failure.code || "mesh_fast_forward_unavailable"]
|
|
2654
|
+
}, null, 2);
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2080
2657
|
async function meshCheckpoint(ctx, args) {
|
|
2081
2658
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2082
2659
|
if (node.policy?.readOnly) {
|
|
@@ -2162,6 +2739,7 @@ async function meshCloneNode(ctx, args) {
|
|
|
2162
2739
|
if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
|
|
2163
2740
|
else ctx.mesh.nodes.push(clonePayload.node);
|
|
2164
2741
|
ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2742
|
+
await syncCoordinatorDaemonMeshCache(ctx);
|
|
2165
2743
|
}
|
|
2166
2744
|
return JSON.stringify(result, null, 2);
|
|
2167
2745
|
} else if (!isLocalTransport(ctx.transport) && sourceNode.daemonId) {
|
|
@@ -2179,6 +2757,7 @@ async function meshCloneNode(ctx, args) {
|
|
|
2179
2757
|
if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
|
|
2180
2758
|
else ctx.mesh.nodes.push(clonePayload.node);
|
|
2181
2759
|
ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2760
|
+
await syncCoordinatorDaemonMeshCache(ctx);
|
|
2182
2761
|
}
|
|
2183
2762
|
return JSON.stringify(res, null, 2);
|
|
2184
2763
|
} catch (e) {
|
|
@@ -2274,6 +2853,43 @@ async function meshRemoveNode(ctx, args) {
|
|
|
2274
2853
|
return JSON.stringify({ error: "Cloud mesh remove_node requires node daemonId" });
|
|
2275
2854
|
}
|
|
2276
2855
|
}
|
|
2856
|
+
function resolveRefineConfigNode(ctx, nodeId) {
|
|
2857
|
+
if (nodeId) return findNode(ctx.mesh, nodeId);
|
|
2858
|
+
const node = ctx.mesh.nodes.find((entry) => !!entry.workspace);
|
|
2859
|
+
if (!node) throw new Error("No mesh node with a workspace is available");
|
|
2860
|
+
return node;
|
|
2861
|
+
}
|
|
2862
|
+
async function meshRefineConfigSchema(ctx) {
|
|
2863
|
+
const node = resolveRefineConfigNode(ctx);
|
|
2864
|
+
const result = await commandForNode(ctx, node, "get_mesh_refine_config_schema", {});
|
|
2865
|
+
return JSON.stringify(result, null, 2);
|
|
2866
|
+
}
|
|
2867
|
+
async function meshValidateRefineConfig(ctx, args) {
|
|
2868
|
+
const node = resolveRefineConfigNode(ctx, args.node_id);
|
|
2869
|
+
const result = await commandForNode(ctx, node, "validate_mesh_refine_config", {
|
|
2870
|
+
workspace: node.workspace,
|
|
2871
|
+
inlineMesh: ctx.mesh,
|
|
2872
|
+
...args.config ? { config: args.config } : {}
|
|
2873
|
+
});
|
|
2874
|
+
return JSON.stringify(result, null, 2);
|
|
2875
|
+
}
|
|
2876
|
+
async function meshSuggestRefineConfig(ctx, args) {
|
|
2877
|
+
const node = resolveRefineConfigNode(ctx, args.node_id);
|
|
2878
|
+
const result = await commandForNode(ctx, node, "suggest_mesh_refine_config", {
|
|
2879
|
+
workspace: node.workspace,
|
|
2880
|
+
inlineMesh: ctx.mesh
|
|
2881
|
+
});
|
|
2882
|
+
return JSON.stringify(result, null, 2);
|
|
2883
|
+
}
|
|
2884
|
+
async function meshRefinePlan(ctx, args) {
|
|
2885
|
+
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2886
|
+
const result = await commandForNode(ctx, node, "plan_mesh_refine_node", {
|
|
2887
|
+
meshId: ctx.mesh.id,
|
|
2888
|
+
nodeId: args.node_id,
|
|
2889
|
+
inlineMesh: ctx.mesh
|
|
2890
|
+
});
|
|
2891
|
+
return JSON.stringify(result, null, 2);
|
|
2892
|
+
}
|
|
2277
2893
|
async function meshRefineNode(ctx, args) {
|
|
2278
2894
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2279
2895
|
if (isLocalTransport(ctx.transport)) {
|
|
@@ -2282,7 +2898,7 @@ async function meshRefineNode(ctx, args) {
|
|
|
2282
2898
|
nodeId: args.node_id,
|
|
2283
2899
|
inlineMesh: ctx.mesh
|
|
2284
2900
|
});
|
|
2285
|
-
if (result?.success && result.removeResult?.removed !== false) {
|
|
2901
|
+
if (result?.success && result.async !== true && result.removeResult?.removed !== false) {
|
|
2286
2902
|
const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
|
|
2287
2903
|
if (idx >= 0) {
|
|
2288
2904
|
ctx.mesh.nodes.splice(idx, 1);
|
|
@@ -2297,7 +2913,7 @@ async function meshRefineNode(ctx, args) {
|
|
|
2297
2913
|
nodeId: args.node_id,
|
|
2298
2914
|
inlineMesh: ctx.mesh
|
|
2299
2915
|
});
|
|
2300
|
-
if (res?.success && res.removeResult?.removed !== false) {
|
|
2916
|
+
if (res?.success && res.async !== true && res.removeResult?.removed !== false) {
|
|
2301
2917
|
const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
|
|
2302
2918
|
if (idx >= 0) {
|
|
2303
2919
|
ctx.mesh.nodes.splice(idx, 1);
|
|
@@ -2334,13 +2950,13 @@ var STANDARD_TOOLS = [
|
|
|
2334
2950
|
function buildMcpHelpText() {
|
|
2335
2951
|
const meshTools = ALL_MESH_TOOLS.map((tool) => tool.name);
|
|
2336
2952
|
return `
|
|
2337
|
-
|
|
2953
|
+
ADHDev MCP Server
|
|
2338
2954
|
|
|
2339
2955
|
Usage:
|
|
2340
|
-
adhdev
|
|
2341
|
-
adhdev
|
|
2342
|
-
adhdev
|
|
2343
|
-
adhdev-mcp --
|
|
2956
|
+
adhdev mcp Local mode (requires standalone daemon)
|
|
2957
|
+
adhdev mcp --api-key <key> Cloud mode (ADHDev cloud API)
|
|
2958
|
+
adhdev mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
|
|
2959
|
+
adhdev-mcp --help Compatibility bin (same server, legacy package entrypoint)
|
|
2344
2960
|
|
|
2345
2961
|
Options:
|
|
2346
2962
|
--mode <mode> Transport: local, cloud, or ipc
|
|
@@ -2365,6 +2981,7 @@ Mesh tools: ${meshTools.join(", ")}
|
|
|
2365
2981
|
// src/server.ts
|
|
2366
2982
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
2367
2983
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
2984
|
+
var import_node_os = __toESM(require("os"));
|
|
2368
2985
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
2369
2986
|
|
|
2370
2987
|
// src/transports/local.ts
|
|
@@ -2519,8 +3136,8 @@ var CloudTransport = class {
|
|
|
2519
3136
|
if (!res.ok) throw new Error(`Approve failed: ${res.status}`);
|
|
2520
3137
|
return res.json();
|
|
2521
3138
|
}
|
|
2522
|
-
async gitStatus(daemonId, workspace, includeDiff = true) {
|
|
2523
|
-
const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff) });
|
|
3139
|
+
async gitStatus(daemonId, workspace, includeDiff = true, refreshUpstream = false) {
|
|
3140
|
+
const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff), refreshUpstream: String(refreshUpstream) });
|
|
2524
3141
|
const res = await fetch(
|
|
2525
3142
|
`${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-status?${params}`,
|
|
2526
3143
|
{ headers: this.headers() }
|
|
@@ -2915,6 +3532,22 @@ function formatChatResult(result, sessionId, format, limit = 50, compact = false
|
|
|
2915
3532
|
}))
|
|
2916
3533
|
}, null, 2);
|
|
2917
3534
|
}
|
|
3535
|
+
if ((format === "text" || format === void 0) && compact && compactPayload) {
|
|
3536
|
+
const lines2 = outputMessages.slice(-limit).map((m) => {
|
|
3537
|
+
const role = m.role === "user" ? "User" : m.role === "assistant" ? "Agent" : m.role;
|
|
3538
|
+
const content = messageContent(m);
|
|
3539
|
+
const truncated = content.length > 500 ? `${content.slice(0, 500)}\u2026` : content;
|
|
3540
|
+
return `[${role}] ${truncated}`;
|
|
3541
|
+
});
|
|
3542
|
+
if (compactPayload.summary) {
|
|
3543
|
+
const truncatedSummary = compactPayload.summary.length > 500 ? `${compactPayload.summary.slice(0, 500)}\u2026` : compactPayload.summary;
|
|
3544
|
+
lines2.push(`[Summary] ${truncatedSummary}`);
|
|
3545
|
+
}
|
|
3546
|
+
if (result?.pollingAdvisory) {
|
|
3547
|
+
lines2.push(`Advisory: ${result.pollingAdvisory.message}`);
|
|
3548
|
+
}
|
|
3549
|
+
return lines2.length > 0 ? lines2.join("\n\n") : "No messages in chat.";
|
|
3550
|
+
}
|
|
2918
3551
|
if (outputMessages.length === 0) {
|
|
2919
3552
|
return result?.pollingAdvisory ? `No messages in chat.
|
|
2920
3553
|
|
|
@@ -3938,6 +4571,7 @@ async function startMcpServer(opts) {
|
|
|
3938
4571
|
}
|
|
3939
4572
|
let localDaemonId;
|
|
3940
4573
|
let localMachineId;
|
|
4574
|
+
let coordinatorHostname = import_node_os.default.hostname();
|
|
3941
4575
|
if (transport instanceof LocalTransport || transport instanceof IpcTransport) {
|
|
3942
4576
|
try {
|
|
3943
4577
|
const { loadConfig } = await import("@adhdev/daemon-core");
|
|
@@ -3950,11 +4584,13 @@ async function startMcpServer(opts) {
|
|
|
3950
4584
|
try {
|
|
3951
4585
|
const statusResult = await transport.getStatus();
|
|
3952
4586
|
const instanceId = typeof statusResult?.status?.instanceId === "string" ? statusResult.status.instanceId.trim() : "";
|
|
4587
|
+
const hostname = typeof statusResult?.status?.hostname === "string" ? statusResult.status.hostname.trim() : typeof statusResult?.status?.machine?.hostname === "string" ? statusResult.status.machine.hostname.trim() : "";
|
|
3953
4588
|
if (instanceId) localDaemonId = instanceId;
|
|
4589
|
+
if (hostname) coordinatorHostname = hostname;
|
|
3954
4590
|
} catch {
|
|
3955
4591
|
}
|
|
3956
4592
|
}
|
|
3957
|
-
const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {} };
|
|
4593
|
+
const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {}, ...coordinatorHostname ? { coordinatorHostname } : {} };
|
|
3958
4594
|
const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
|
|
3959
4595
|
const server2 = new import_server.Server(
|
|
3960
4596
|
{ name: "adhdev-mcp-server", version: "0.9.81" },
|
|
@@ -4015,6 +4651,9 @@ async function startMcpServer(opts) {
|
|
|
4015
4651
|
case "mesh_git_status":
|
|
4016
4652
|
text = await meshGitStatus(meshCtx, a);
|
|
4017
4653
|
break;
|
|
4654
|
+
case "mesh_fast_forward_node":
|
|
4655
|
+
text = await meshFastForwardNode(meshCtx, a);
|
|
4656
|
+
break;
|
|
4018
4657
|
case "mesh_checkpoint":
|
|
4019
4658
|
text = await meshCheckpoint(meshCtx, a);
|
|
4020
4659
|
break;
|
|
@@ -4030,6 +4669,18 @@ async function startMcpServer(opts) {
|
|
|
4030
4669
|
case "mesh_refine_node":
|
|
4031
4670
|
text = await meshRefineNode(meshCtx, a);
|
|
4032
4671
|
break;
|
|
4672
|
+
case "mesh_refine_config_schema":
|
|
4673
|
+
text = await meshRefineConfigSchema(meshCtx);
|
|
4674
|
+
break;
|
|
4675
|
+
case "mesh_validate_refine_config":
|
|
4676
|
+
text = await meshValidateRefineConfig(meshCtx, a);
|
|
4677
|
+
break;
|
|
4678
|
+
case "mesh_suggest_refine_config":
|
|
4679
|
+
text = await meshSuggestRefineConfig(meshCtx, a);
|
|
4680
|
+
break;
|
|
4681
|
+
case "mesh_refine_plan":
|
|
4682
|
+
text = await meshRefinePlan(meshCtx, a);
|
|
4683
|
+
break;
|
|
4033
4684
|
case "mesh_cleanup_sessions":
|
|
4034
4685
|
text = await meshCleanupSessions(meshCtx, a);
|
|
4035
4686
|
break;
|