@adhdev/daemon-standalone 0.9.82-rc.12 → 0.9.82-rc.121
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 +26981 -21358
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/index-BJ05ceSl.js +98 -0
- package/public/assets/index-BKsjvBcH.js +100 -0
- package/public/assets/index-BNKDsz6G.css +1 -0
- package/public/assets/index-BfLqOvWH.js +98 -0
- package/public/assets/index-BggZArlj.js +99 -0
- package/public/assets/index-Bso1b8Lh.css +1 -0
- package/public/assets/index-BzfqwAXl.js +98 -0
- package/public/assets/index-CAip3He8.js +100 -0
- package/public/assets/index-CCQPbtl4.css +1 -0
- package/public/assets/index-CYwXUKol.js +98 -0
- package/public/assets/index-CiB8XPwO.css +1 -0
- package/public/assets/index-CsEmn20l.css +1 -0
- package/public/assets/index-CsR4qRd4.js +98 -0
- package/public/assets/index-DDB8O9lr.js +100 -0
- package/public/assets/index-DJmCzwTk.js +98 -0
- package/public/assets/index-DW3PmABW.js +99 -0
- package/public/assets/index-DaIkPFUd.js +99 -0
- package/public/assets/index-Dc9o_onl.js +98 -0
- package/public/assets/index-DcAeIiVq.css +1 -0
- package/public/assets/index-Deb1Bk0M.js +98 -0
- package/public/assets/index-DhQzhjNN.js +98 -0
- package/public/assets/index-Dt2Xvr87.js +98 -0
- package/public/assets/index-R9GDvGJ8.js +98 -0
- package/public/assets/index-kt4CV7OM.js +100 -0
- package/public/assets/index-phY3RKNC.js +98 -0
- package/public/assets/index-xSlc1ntZ.css +1 -0
- package/public/assets/index-yF4m-swI.js +100 -0
- package/public/assets/terminal-D46M5EWH.js +143 -0
- package/public/assets/terminal-NZFQPQy6.js +143 -0
- package/public/assets/vendor-C7br1-G2.js +2745 -0
- package/public/index.html +2 -2
- package/vendor/mcp-server/index.js +921 -88
- package/vendor/mcp-server/index.js.map +1 -1
|
@@ -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,
|
|
@@ -232,9 +267,45 @@ function annotateRapidReadChatAdvisory(payload, options) {
|
|
|
232
267
|
// src/tools/mesh-tools.ts
|
|
233
268
|
var import_daemon_core = require("@adhdev/daemon-core");
|
|
234
269
|
var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
|
|
270
|
+
var ACTIVE_WORK_POLLING_BACKOFF_MS = 6e4;
|
|
271
|
+
function buildActiveWorkPollingGuidance(summary, now = Date.now()) {
|
|
272
|
+
if (!summary || summary.generatingCount <= 0) return void 0;
|
|
273
|
+
return {
|
|
274
|
+
activeGeneratingWork: true,
|
|
275
|
+
generatingCount: summary.generatingCount,
|
|
276
|
+
doNotPollBefore: new Date(now + ACTIVE_WORK_POLLING_BACKOFF_MS).toISOString(),
|
|
277
|
+
eventSurface: "pendingCoordinatorEvents",
|
|
278
|
+
nextRecommendedAction: "Wait for pendingCoordinatorEvents/completion events or an explicit user status request. After a terminal signal, call mesh_read_chat once with compact=true, then verify git state if repository changes were expected.",
|
|
279
|
+
message: "Do not repeatedly poll mesh_status/mesh_view_queue/mesh_read_chat while delegated work is generating; these snapshots rarely change until the worker emits a completion/status event."
|
|
280
|
+
};
|
|
281
|
+
}
|
|
235
282
|
function readString(value) {
|
|
236
283
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
237
284
|
}
|
|
285
|
+
function summarizeTaskMessage(message) {
|
|
286
|
+
const taskSummary = message.replace(/\s+/g, " ").trim();
|
|
287
|
+
const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
|
|
288
|
+
return { taskTitle: taskTitle || "(untitled task)", taskSummary };
|
|
289
|
+
}
|
|
290
|
+
function buildDirectTaskPayload(message, via, opts) {
|
|
291
|
+
const descriptor = summarizeTaskMessage(message);
|
|
292
|
+
return {
|
|
293
|
+
source: "direct",
|
|
294
|
+
via,
|
|
295
|
+
taskId: opts.taskId,
|
|
296
|
+
message,
|
|
297
|
+
taskTitle: descriptor.taskTitle,
|
|
298
|
+
taskSummary: descriptor.taskSummary,
|
|
299
|
+
...opts.taskMode ? { taskMode: opts.taskMode } : {},
|
|
300
|
+
...opts.providerType ? { providerType: opts.providerType } : {},
|
|
301
|
+
...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function findNode(mesh, nodeId) {
|
|
305
|
+
const node = mesh.nodes.find((n) => n.id === nodeId);
|
|
306
|
+
if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
|
|
307
|
+
return node;
|
|
308
|
+
}
|
|
238
309
|
var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
|
|
239
310
|
var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
|
|
240
311
|
var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
|
|
@@ -246,15 +317,24 @@ async function refreshMeshFromDaemon(ctx) {
|
|
|
246
317
|
const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
|
|
247
318
|
if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
|
|
248
319
|
const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
|
|
249
|
-
if (!refreshedNodes.length) return;
|
|
250
320
|
ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
|
|
251
321
|
ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
|
|
252
322
|
} catch {
|
|
253
323
|
}
|
|
254
324
|
}
|
|
325
|
+
async function syncCoordinatorDaemonMeshCache(ctx) {
|
|
326
|
+
if (!(ctx.transport instanceof IpcTransport)) return;
|
|
327
|
+
try {
|
|
328
|
+
await ctx.transport.command("get_mesh", {
|
|
329
|
+
meshId: ctx.mesh.id,
|
|
330
|
+
inlineMesh: ctx.mesh
|
|
331
|
+
});
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
}
|
|
255
335
|
async function findNodeWithRefresh(ctx, nodeId) {
|
|
256
336
|
const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
257
|
-
if (hit) return hit;
|
|
337
|
+
if (hit && !hit.isLocalWorktree) return hit;
|
|
258
338
|
await refreshMeshFromDaemon(ctx);
|
|
259
339
|
const refreshed = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
260
340
|
if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
|
|
@@ -262,7 +342,7 @@ async function findNodeWithRefresh(ctx, nodeId) {
|
|
|
262
342
|
}
|
|
263
343
|
async function findOptionalNodeWithRefresh(ctx, nodeId) {
|
|
264
344
|
const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
265
|
-
if (hit) return hit;
|
|
345
|
+
if (hit && !hit.isLocalWorktree) return hit;
|
|
266
346
|
await refreshMeshFromDaemon(ctx);
|
|
267
347
|
return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
|
|
268
348
|
}
|
|
@@ -314,9 +394,26 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
|
|
|
314
394
|
readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
|
|
315
395
|
};
|
|
316
396
|
if (finalSummary) {
|
|
397
|
+
if (args.compact === true) {
|
|
398
|
+
return {
|
|
399
|
+
...compactChatPayload({
|
|
400
|
+
success: true,
|
|
401
|
+
status: "idle",
|
|
402
|
+
providerSessionId,
|
|
403
|
+
summary: finalSummary,
|
|
404
|
+
messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
|
|
405
|
+
}, {
|
|
406
|
+
nodeId: args.node_id,
|
|
407
|
+
sessionId: args.session_id,
|
|
408
|
+
limit: args.tail ?? 10
|
|
409
|
+
}),
|
|
410
|
+
recoveredFromLedger: true,
|
|
411
|
+
ledger
|
|
412
|
+
};
|
|
413
|
+
}
|
|
317
414
|
return {
|
|
318
415
|
success: true,
|
|
319
|
-
compact:
|
|
416
|
+
compact: false,
|
|
320
417
|
recoveredFromLedger: true,
|
|
321
418
|
nodeId: args.node_id,
|
|
322
419
|
sessionId: args.session_id,
|
|
@@ -368,6 +465,22 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
|
|
|
368
465
|
function readSessionRecordId(session) {
|
|
369
466
|
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
467
|
}
|
|
468
|
+
function extractStatusMetadataSessions(value) {
|
|
469
|
+
const payload = unwrapCommandPayload(value);
|
|
470
|
+
const status = payload?.status && typeof payload.status === "object" ? payload.status : payload;
|
|
471
|
+
return Array.isArray(status?.sessions) ? status.sessions : [];
|
|
472
|
+
}
|
|
473
|
+
function resolveSessionProviderType(session) {
|
|
474
|
+
return readString(session?.providerType) || readString(session?.cliType) || readString(session?.agentType) || "";
|
|
475
|
+
}
|
|
476
|
+
function isMeshCoordinatorSessionRecord(session) {
|
|
477
|
+
return Boolean(
|
|
478
|
+
readString(session?.settings?.meshCoordinatorFor) || readString(session?.meta?.meshCoordinatorFor) || readString(session?.metadata?.meshCoordinatorFor) || readString(session?.meshCoordinatorFor)
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
function isWorkerTaskMode(taskMode) {
|
|
482
|
+
return taskMode !== "live_debug_readonly";
|
|
483
|
+
}
|
|
371
484
|
function addSessionRecord(target, session) {
|
|
372
485
|
if (!session || typeof session !== "object" || isTerminalSessionRecord(session)) return;
|
|
373
486
|
const sessionId = readSessionRecordId(session);
|
|
@@ -436,18 +549,26 @@ function queueAssignmentStaleReason(task, liveness) {
|
|
|
436
549
|
}
|
|
437
550
|
function buildQueueStatusSummary(queue) {
|
|
438
551
|
const counts = { pending: 0, assigned: 0, completed: 0, failed: 0, cancelled: 0 };
|
|
552
|
+
let staleAssigned = 0;
|
|
439
553
|
for (const task of queue) {
|
|
440
554
|
const status = typeof task?.status === "string" ? task.status : void 0;
|
|
441
555
|
if (status && Object.prototype.hasOwnProperty.call(counts, status)) {
|
|
442
556
|
counts[status] += 1;
|
|
443
557
|
}
|
|
558
|
+
if (status === "assigned" && task?.staleAssigned === true) staleAssigned += 1;
|
|
444
559
|
}
|
|
560
|
+
const liveAssigned = Math.max(0, counts.assigned - staleAssigned);
|
|
445
561
|
return {
|
|
446
562
|
totalCount: queue.length,
|
|
447
|
-
activeCount: counts.pending +
|
|
563
|
+
activeCount: counts.pending + liveAssigned,
|
|
448
564
|
historicalCount: counts.completed + counts.failed + counts.cancelled,
|
|
449
565
|
counts,
|
|
450
566
|
activeCounts: {
|
|
567
|
+
pending: counts.pending,
|
|
568
|
+
assigned: liveAssigned
|
|
569
|
+
},
|
|
570
|
+
staleAssignedCount: staleAssigned,
|
|
571
|
+
rawActiveCounts: {
|
|
451
572
|
pending: counts.pending,
|
|
452
573
|
assigned: counts.assigned
|
|
453
574
|
},
|
|
@@ -475,6 +596,18 @@ function filterQueueForView(queue, view, statuses) {
|
|
|
475
596
|
if (view === "historical") return queue.filter((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
|
|
476
597
|
return queue;
|
|
477
598
|
}
|
|
599
|
+
function prioritizeActiveQueueRows(queue) {
|
|
600
|
+
const active = [];
|
|
601
|
+
const historical = [];
|
|
602
|
+
const other = [];
|
|
603
|
+
for (const task of queue) {
|
|
604
|
+
const status = String(task?.status || "");
|
|
605
|
+
if (ACTIVE_QUEUE_STATUSES.has(status)) active.push(task);
|
|
606
|
+
else if (HISTORICAL_QUEUE_STATUSES.has(status)) historical.push(task);
|
|
607
|
+
else other.push(task);
|
|
608
|
+
}
|
|
609
|
+
return [...active, ...other, ...historical];
|
|
610
|
+
}
|
|
478
611
|
function slimQueueTask(task) {
|
|
479
612
|
return {
|
|
480
613
|
id: task?.id,
|
|
@@ -580,22 +713,59 @@ function isIdleSessionRecord(session) {
|
|
|
580
713
|
const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
|
|
581
714
|
return status === "idle" || chatStatus === "waiting_input";
|
|
582
715
|
}
|
|
716
|
+
function isMeshOwnedDelegateSession(session, meshId, nodeId) {
|
|
717
|
+
const settings = session?.settings;
|
|
718
|
+
const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
|
|
719
|
+
const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
|
|
720
|
+
const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
|
|
721
|
+
if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
|
|
722
|
+
return !sessionNodeId || sessionNodeId === nodeId;
|
|
723
|
+
}
|
|
583
724
|
function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
|
|
584
725
|
const live = sessions.filter((session) => !isTerminalSessionRecord(session));
|
|
585
726
|
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
727
|
const meshSessions = live.filter(
|
|
595
|
-
(session) => isMeshOwnedDelegateSession(session)
|
|
728
|
+
(session) => isMeshOwnedDelegateSession(session, meshId, nodeId)
|
|
596
729
|
);
|
|
597
730
|
return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || void 0;
|
|
598
731
|
}
|
|
732
|
+
function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
|
|
733
|
+
return {
|
|
734
|
+
success: false,
|
|
735
|
+
recoverable: true,
|
|
736
|
+
code: "mesh_delegate_session_missing_relay_metadata",
|
|
737
|
+
reason: "mesh_delegate_session_missing_relay_metadata",
|
|
738
|
+
transport: "mesh_transport",
|
|
739
|
+
retryRecommended: true,
|
|
740
|
+
meshId: ctx.mesh.id,
|
|
741
|
+
nodeId: node.id,
|
|
742
|
+
daemonId: node.daemonId,
|
|
743
|
+
workspace: node.workspace,
|
|
744
|
+
sessionId,
|
|
745
|
+
...providerType ? { resolvedProviderType: providerType } : {},
|
|
746
|
+
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.`,
|
|
747
|
+
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.`,
|
|
748
|
+
noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
|
|
752
|
+
return {
|
|
753
|
+
success: false,
|
|
754
|
+
recoverable: true,
|
|
755
|
+
code: "mesh_coordinator_daemon_unknown",
|
|
756
|
+
reason: "mesh_coordinator_daemon_unknown",
|
|
757
|
+
transport: "mesh_transport",
|
|
758
|
+
retryRecommended: true,
|
|
759
|
+
meshId: ctx.mesh.id,
|
|
760
|
+
nodeId: node.id,
|
|
761
|
+
daemonId: node.daemonId,
|
|
762
|
+
workspace: node.workspace,
|
|
763
|
+
...providerType ? { resolvedProviderType: providerType } : {},
|
|
764
|
+
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.`,
|
|
765
|
+
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.",
|
|
766
|
+
noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
|
|
767
|
+
};
|
|
768
|
+
}
|
|
599
769
|
function findNestedPayload(value, predicate) {
|
|
600
770
|
const seen = /* @__PURE__ */ new Set();
|
|
601
771
|
const stack = [{ payload: value, depth: 0 }];
|
|
@@ -623,12 +793,16 @@ function extractGitDiff(value) {
|
|
|
623
793
|
}
|
|
624
794
|
function extractSubmodules(value, ignorePaths) {
|
|
625
795
|
const payload = unwrapCommandPayload(value);
|
|
626
|
-
const subs = payload?.submodules ?? value?.submodules;
|
|
796
|
+
const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
|
|
627
797
|
if (!Array.isArray(subs)) return void 0;
|
|
628
798
|
if (ignorePaths.length === 0) return subs;
|
|
629
799
|
const ignoreSet = new Set(ignorePaths);
|
|
630
800
|
return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
|
|
631
801
|
}
|
|
802
|
+
function assignFullGitSnapshot(entry, status) {
|
|
803
|
+
if (!status || typeof status !== "object" || Array.isArray(status)) return;
|
|
804
|
+
entry.git = status;
|
|
805
|
+
}
|
|
632
806
|
function extractLaunchPayload(value) {
|
|
633
807
|
return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
|
|
634
808
|
}
|
|
@@ -753,20 +927,76 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
|
|
|
753
927
|
let sessionId = args.session_id?.trim() || "";
|
|
754
928
|
const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
|
|
755
929
|
let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
|
|
756
|
-
if (
|
|
930
|
+
if (sessionId && args.verifiedSession) {
|
|
931
|
+
const explicitSession = args.verifiedSession;
|
|
932
|
+
if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
|
|
933
|
+
return buildRelayUnsafeRemoteSessionFailure(
|
|
934
|
+
ctx,
|
|
935
|
+
node,
|
|
936
|
+
sessionId,
|
|
937
|
+
resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
if (!resolvedProviderType) {
|
|
941
|
+
resolvedProviderType = resolveSessionProviderType(explicitSession);
|
|
942
|
+
}
|
|
943
|
+
} else if (!sessionId || args.session_id) {
|
|
757
944
|
try {
|
|
758
945
|
const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
946
|
+
const sessions = extractStatusMetadataSessions(relayResult);
|
|
947
|
+
if (sessionId) {
|
|
948
|
+
const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
|
|
949
|
+
if (!explicitSession) {
|
|
950
|
+
return {
|
|
951
|
+
success: false,
|
|
952
|
+
recoverable: true,
|
|
953
|
+
code: "mesh_target_session_not_found",
|
|
954
|
+
reason: "mesh_target_session_not_found",
|
|
955
|
+
transport: "mesh_transport",
|
|
956
|
+
retryRecommended: true,
|
|
957
|
+
meshId: ctx.mesh.id,
|
|
958
|
+
nodeId: node.id,
|
|
959
|
+
daemonId,
|
|
960
|
+
workspace: node.workspace,
|
|
961
|
+
sessionId,
|
|
962
|
+
...resolvedProviderType ? { resolvedProviderType } : {},
|
|
963
|
+
error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
|
|
964
|
+
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.`
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
|
|
968
|
+
return buildRelayUnsafeRemoteSessionFailure(
|
|
969
|
+
ctx,
|
|
970
|
+
node,
|
|
971
|
+
sessionId,
|
|
972
|
+
resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
|
|
973
|
+
);
|
|
974
|
+
}
|
|
765
975
|
if (!resolvedProviderType) {
|
|
766
|
-
resolvedProviderType =
|
|
976
|
+
resolvedProviderType = resolveSessionProviderType(explicitSession);
|
|
977
|
+
}
|
|
978
|
+
} else {
|
|
979
|
+
const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
|
|
980
|
+
if (targetSession?.id || targetSession?.sessionId) {
|
|
981
|
+
sessionId = targetSession.id || targetSession.sessionId;
|
|
982
|
+
if (!resolvedProviderType) {
|
|
983
|
+
resolvedProviderType = resolveSessionProviderType(targetSession);
|
|
984
|
+
}
|
|
767
985
|
}
|
|
768
986
|
}
|
|
769
987
|
} catch (e) {
|
|
988
|
+
if (sessionId) {
|
|
989
|
+
return {
|
|
990
|
+
...buildCoordinatorP2pRelayFailure(e, {
|
|
991
|
+
command: "get_status_metadata",
|
|
992
|
+
targetDaemonId: daemonId,
|
|
993
|
+
nodeId: node.id,
|
|
994
|
+
sessionId
|
|
995
|
+
}),
|
|
996
|
+
success: false,
|
|
997
|
+
error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
|
|
998
|
+
};
|
|
999
|
+
}
|
|
770
1000
|
}
|
|
771
1001
|
}
|
|
772
1002
|
if (!resolvedProviderType) {
|
|
@@ -796,7 +1026,7 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
|
|
|
796
1026
|
error: `P2P dispatch failed: ${errorMessage}`
|
|
797
1027
|
};
|
|
798
1028
|
}
|
|
799
|
-
return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
|
|
1029
|
+
return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType, providerType: resolvedProviderType };
|
|
800
1030
|
} catch (e) {
|
|
801
1031
|
const errorMessage = e?.message || String(e);
|
|
802
1032
|
return {
|
|
@@ -826,34 +1056,194 @@ function resolveCoordinatorNode(ctx) {
|
|
|
826
1056
|
return void 0;
|
|
827
1057
|
}
|
|
828
1058
|
function readNodeMachineId(node) {
|
|
829
|
-
return readString(node.machineId) || readString(node.machine_id);
|
|
1059
|
+
return readString(node.machineId) || readString(node.machine_id) || readString(node.machine?.id) || readString(node.machine?.machineId) || readString(node.lastProbe?.machineId) || readString(node.last_probe?.machine_id) || readString(node.lastProbe?.machine?.id) || readString(node.lastProbe?.machine?.machineId) || readString(node.last_probe?.machine?.id) || readString(node.last_probe?.machine?.machine_id);
|
|
830
1060
|
}
|
|
831
1061
|
function readNodeDaemonId(node) {
|
|
832
|
-
return readString(node.daemonId) || readString(node.daemon_id);
|
|
1062
|
+
return readString(node.daemonId) || readString(node.daemon_id) || readString(node.machine?.daemonId) || readString(node.machine?.daemon_id) || readString(node.lastProbe?.daemonId) || readString(node.last_probe?.daemon_id) || readString(node.lastProbe?.machine?.daemonId) || readString(node.lastProbe?.machine?.daemon_id) || readString(node.last_probe?.machine?.daemonId) || readString(node.last_probe?.machine?.daemon_id);
|
|
1063
|
+
}
|
|
1064
|
+
function normalizeHostname(value) {
|
|
1065
|
+
const hostname = readString(value);
|
|
1066
|
+
if (!hostname) return void 0;
|
|
1067
|
+
return hostname.toLowerCase().replace(/\.$/, "");
|
|
1068
|
+
}
|
|
1069
|
+
function readNodeHostname(node) {
|
|
1070
|
+
return readString(node.hostname) || readString(node.host) || readString(node.machineHostname) || readString(node.machine_hostname) || readString(node.machine?.hostname) || readString(node.machine?.host) || readString(node.lastProbe?.hostname) || readString(node.last_probe?.hostname) || readString(node.lastProbe?.machine?.hostname) || readString(node.last_probe?.machine?.hostname);
|
|
1071
|
+
}
|
|
1072
|
+
function readNodeDisplayMachineName(node) {
|
|
1073
|
+
return readString(node.machineName) || readString(node.machine_name) || readString(node.machineLabel) || readString(node.machine_label) || readString(node.machineNickname) || readString(node.machine_nickname) || readString(node.alias) || readString(node.machine?.name) || readString(node.machine?.displayName) || readString(node.machine?.display_name) || readString(node.lastProbe?.machineName) || readString(node.last_probe?.machine_name) || readString(node.lastProbe?.machine?.name) || readString(node.last_probe?.machine?.name) || readNodeHostname(node);
|
|
1074
|
+
}
|
|
1075
|
+
function compactIdentityEvidence(value) {
|
|
1076
|
+
if (!value) return void 0;
|
|
1077
|
+
return value.length > 24 ? `${value.slice(0, 12)}\u2026${value.slice(-8)}` : value;
|
|
1078
|
+
}
|
|
1079
|
+
function pushIdentityEvidence(evidence, label, value) {
|
|
1080
|
+
const compact = compactIdentityEvidence(value);
|
|
1081
|
+
if (compact) evidence.push(`${label}:${compact}`);
|
|
1082
|
+
}
|
|
1083
|
+
function buildNodeMachineIdentity(ctx, node) {
|
|
1084
|
+
const machineId = readNodeMachineId(node);
|
|
1085
|
+
const daemonId = readNodeDaemonId(node);
|
|
1086
|
+
const hostname = readNodeHostname(node);
|
|
1087
|
+
const machineName = readNodeDisplayMachineName(node);
|
|
1088
|
+
const coordinatorHostname = readString(ctx.coordinatorHostname);
|
|
1089
|
+
const localControlPlaneReason = getLocalControlPlaneMatchReason(ctx, node);
|
|
1090
|
+
const directLocal = !!localControlPlaneReason;
|
|
1091
|
+
const hostnameMatches = Boolean(
|
|
1092
|
+
normalizeHostname(hostname) && normalizeHostname(coordinatorHostname) && normalizeHostname(hostname) === normalizeHostname(coordinatorHostname)
|
|
1093
|
+
);
|
|
1094
|
+
const sameMachine = directLocal || hostnameMatches;
|
|
1095
|
+
const evidence = [];
|
|
1096
|
+
pushIdentityEvidence(evidence, "machineName", machineName);
|
|
1097
|
+
pushIdentityEvidence(evidence, "hostname", hostname);
|
|
1098
|
+
pushIdentityEvidence(evidence, "machineId", machineId);
|
|
1099
|
+
pushIdentityEvidence(evidence, "daemonId", daemonId);
|
|
1100
|
+
if (localControlPlaneReason) {
|
|
1101
|
+
pushIdentityEvidence(evidence, "localMatch", localControlPlaneReason);
|
|
1102
|
+
pushIdentityEvidence(evidence, "localMachineId", ctx.localMachineId);
|
|
1103
|
+
pushIdentityEvidence(evidence, "localDaemonId", ctx.localDaemonId);
|
|
1104
|
+
}
|
|
1105
|
+
const locality = sameMachine ? "same_machine" : evidence.length > 0 ? "remote_known" : "remote_or_unknown";
|
|
1106
|
+
const localityReason = sameMachine ? localControlPlaneReason || "matched coordinator hostname" : evidence.length > 0 ? `known remote/other machine identity; no local coordinator match (${evidence.join(", ")})` : "no useful machine identity evidence available";
|
|
1107
|
+
return {
|
|
1108
|
+
daemonId,
|
|
1109
|
+
machineId,
|
|
1110
|
+
hostname,
|
|
1111
|
+
machineName,
|
|
1112
|
+
displayName: machineName || hostname || daemonId || machineId,
|
|
1113
|
+
coordinatorHostname,
|
|
1114
|
+
sameMachine,
|
|
1115
|
+
locality,
|
|
1116
|
+
localityReason,
|
|
1117
|
+
identityEvidence: evidence
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
function nodeHasLocalDaemonEvidence(ctx, node) {
|
|
1121
|
+
const isLocal = (session) => {
|
|
1122
|
+
if (!session || typeof session !== "object") return false;
|
|
1123
|
+
if (ctx.localDaemonId && session.settings?.meshCoordinatorDaemonId === ctx.localDaemonId) return true;
|
|
1124
|
+
if (session.launchedByCoordinator === true) return true;
|
|
1125
|
+
if (ctx.localDaemonId && session.runtime?.owner === ctx.localDaemonId) return true;
|
|
1126
|
+
if (ctx.localDaemonId && session.daemonClient?.daemonId === ctx.localDaemonId) return true;
|
|
1127
|
+
return false;
|
|
1128
|
+
};
|
|
1129
|
+
const sessionArrays = [
|
|
1130
|
+
node?.sessions,
|
|
1131
|
+
node?.activeSessions,
|
|
1132
|
+
node?.active_sessions,
|
|
1133
|
+
node?.lastProbe?.sessions,
|
|
1134
|
+
node?.last_probe?.sessions,
|
|
1135
|
+
node?.lastProbe?.status?.sessions,
|
|
1136
|
+
node?.last_probe?.status?.sessions
|
|
1137
|
+
];
|
|
1138
|
+
for (const arr of sessionArrays) {
|
|
1139
|
+
if (Array.isArray(arr) && arr.some(isLocal)) return true;
|
|
1140
|
+
}
|
|
1141
|
+
const sessionRecords = [
|
|
1142
|
+
node?.activeSession,
|
|
1143
|
+
node?.active_session,
|
|
1144
|
+
node?.currentSession,
|
|
1145
|
+
node?.current_session,
|
|
1146
|
+
node?.runtimeSession,
|
|
1147
|
+
node?.runtime_session,
|
|
1148
|
+
node?.session,
|
|
1149
|
+
node?.lastProbe?.activeSession,
|
|
1150
|
+
node?.last_probe?.active_session,
|
|
1151
|
+
node?.lastProbe?.currentSession,
|
|
1152
|
+
node?.last_probe?.current_session,
|
|
1153
|
+
node?.lastProbe?.session,
|
|
1154
|
+
node?.last_probe?.session
|
|
1155
|
+
];
|
|
1156
|
+
for (const session of sessionRecords) {
|
|
1157
|
+
if (isLocal(session)) return true;
|
|
1158
|
+
}
|
|
1159
|
+
return false;
|
|
833
1160
|
}
|
|
834
1161
|
function isDirectLocalNode(ctx, node) {
|
|
835
1162
|
const machineId = readNodeMachineId(node);
|
|
836
1163
|
const daemonId = readNodeDaemonId(node);
|
|
837
1164
|
return Boolean(
|
|
838
|
-
ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId
|
|
1165
|
+
ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId || nodeHasLocalDaemonEvidence(ctx, node)
|
|
839
1166
|
);
|
|
840
1167
|
}
|
|
1168
|
+
function isConfiguredCoordinatorNode(ctx, node) {
|
|
1169
|
+
if (!ctx.localMachineId && !ctx.localDaemonId) return false;
|
|
1170
|
+
const nodeId = readString(node.id) || readString(node.nodeId) || readString(node.node_id);
|
|
1171
|
+
if (!nodeId) return false;
|
|
1172
|
+
const preferredNodeId = readString(ctx.mesh.coordinator?.preferredNodeId) || readString(ctx.mesh.coordinator?.preferred_node_id);
|
|
1173
|
+
if (preferredNodeId) return nodeId === preferredNodeId;
|
|
1174
|
+
const first = ctx.mesh.nodes?.[0];
|
|
1175
|
+
const firstNodeId = readString(first?.id) || readString(first?.nodeId) || readString(first?.node_id);
|
|
1176
|
+
return !!firstNodeId && nodeId === firstNodeId;
|
|
1177
|
+
}
|
|
1178
|
+
function getLocalControlPlaneMatchReason(ctx, node) {
|
|
1179
|
+
if (isDirectLocalNode(ctx, node)) return "matched coordinator daemon or machine id";
|
|
1180
|
+
if (isConfiguredCoordinatorNode(ctx, node)) return "matched configured coordinator node";
|
|
1181
|
+
if (node.isLocalWorktree === true) {
|
|
1182
|
+
const sourceNode = findClonedFromNode(ctx, node);
|
|
1183
|
+
if (sourceNode && isDirectLocalNode(ctx, sourceNode)) return "matched local cloned-from node";
|
|
1184
|
+
if (sourceNode && isConfiguredCoordinatorNode(ctx, sourceNode)) return "matched configured coordinator source node";
|
|
1185
|
+
}
|
|
1186
|
+
return void 0;
|
|
1187
|
+
}
|
|
841
1188
|
function findClonedFromNode(ctx, node) {
|
|
842
1189
|
const clonedFromNodeId = readString(node.clonedFromNodeId) || readString(node.cloned_from_node_id);
|
|
843
1190
|
if (!clonedFromNodeId) return void 0;
|
|
844
1191
|
return ctx.mesh.nodes.find((n) => n.id === clonedFromNodeId || n.nodeId === clonedFromNodeId || n.node_id === clonedFromNodeId);
|
|
845
1192
|
}
|
|
846
1193
|
function isLocalControlPlaneNode(ctx, node) {
|
|
847
|
-
|
|
848
|
-
if (node.isLocalWorktree === true) {
|
|
849
|
-
const sourceNode = findClonedFromNode(ctx, node);
|
|
850
|
-
if (sourceNode && isDirectLocalNode(ctx, sourceNode)) return true;
|
|
851
|
-
}
|
|
852
|
-
return false;
|
|
1194
|
+
return !!getLocalControlPlaneMatchReason(ctx, node);
|
|
853
1195
|
}
|
|
854
1196
|
function meshSessionCacheKey(nodeId, runtimeSessionId) {
|
|
855
1197
|
return `${nodeId}:${runtimeSessionId}`;
|
|
856
1198
|
}
|
|
1199
|
+
function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata) {
|
|
1200
|
+
const keyNodeId = readString(nodeId);
|
|
1201
|
+
const keySessionId = readString(runtimeSessionId);
|
|
1202
|
+
if (!keyNodeId || !keySessionId) return;
|
|
1203
|
+
const providerType = readString(metadata.providerType);
|
|
1204
|
+
const providerSessionId = readString(metadata.providerSessionId);
|
|
1205
|
+
if (!providerType && !providerSessionId) return;
|
|
1206
|
+
const existing = meshSessionProviderMetadata.get(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
|
|
1207
|
+
meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
|
|
1208
|
+
providerType: providerType || existing.providerType,
|
|
1209
|
+
providerSessionId: providerSessionId || existing.providerSessionId
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
function rememberMeshSessionProviderMetadataFromEvent(event) {
|
|
1213
|
+
const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : event && typeof event === "object" ? event : {};
|
|
1214
|
+
const nodeId = readString(event?.nodeId) || readString(metadataEvent.nodeId) || readString(metadataEvent.meshNodeId);
|
|
1215
|
+
const sessionId = readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId) || readString(event?.sessionId);
|
|
1216
|
+
rememberMeshSessionProviderMetadata(nodeId, sessionId, {
|
|
1217
|
+
providerType: readString(metadataEvent.providerType) || readString(event?.providerType) || "",
|
|
1218
|
+
providerSessionId: readString(metadataEvent.providerSessionId) || readString(event?.providerSessionId)
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
|
|
1222
|
+
const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 });
|
|
1223
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
1224
|
+
const entry = entries[i];
|
|
1225
|
+
const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
|
|
1226
|
+
const entryNodeId = readString(entry.nodeId) || readString(payload.nodeId) || readString(payload.meshNodeId);
|
|
1227
|
+
if (entryNodeId && entryNodeId !== nodeId) continue;
|
|
1228
|
+
const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
|
|
1229
|
+
if (entrySessionId !== runtimeSessionId) continue;
|
|
1230
|
+
const providerType = readString(entry.providerType) || readString(payload.providerType);
|
|
1231
|
+
const completionDiagnostic = payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? payload.completionDiagnostic : {};
|
|
1232
|
+
const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : {};
|
|
1233
|
+
const providerSessionId = readString(payload.providerSessionId) || readString(completionDiagnostic.providerSessionId) || readString(metadataEvent.providerSessionId);
|
|
1234
|
+
if (providerType || providerSessionId) {
|
|
1235
|
+
return { providerType: providerType || "", providerSessionId };
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return void 0;
|
|
1239
|
+
}
|
|
1240
|
+
function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
|
|
1241
|
+
const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(nodeId, runtimeSessionId));
|
|
1242
|
+
if (cached?.providerType || cached?.providerSessionId) return cached;
|
|
1243
|
+
const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
|
|
1244
|
+
if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
|
|
1245
|
+
return fromLedger;
|
|
1246
|
+
}
|
|
857
1247
|
function countUncommittedChanges(status) {
|
|
858
1248
|
if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
|
|
859
1249
|
const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
|
|
@@ -924,6 +1314,16 @@ function missingProviderPriorityMessage(nodeId) {
|
|
|
924
1314
|
return `Node '${nodeId}' has no providerPriority policy; pass type explicitly or configure node.policy.providerPriority`;
|
|
925
1315
|
}
|
|
926
1316
|
function getNodeLaunchReadiness(node) {
|
|
1317
|
+
const bootstrap = node.worktreeBootstrap;
|
|
1318
|
+
if (node.isLocalWorktree && bootstrap?.status === "failed" && bootstrap?.required !== false) {
|
|
1319
|
+
return {
|
|
1320
|
+
providerPriority: readProviderPriority(node.policy),
|
|
1321
|
+
launchReady: false,
|
|
1322
|
+
launchBlockedReason: "worktree_bootstrap_failed",
|
|
1323
|
+
launchBlockedMessage: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : "Required worktree bootstrap failed; resolve it before launching an agent into this node.",
|
|
1324
|
+
worktreeBootstrap: bootstrap
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
927
1327
|
const providerPriority = readProviderPriority(node.policy);
|
|
928
1328
|
if (providerPriority.length) {
|
|
929
1329
|
return {
|
|
@@ -938,6 +1338,33 @@ function getNodeLaunchReadiness(node) {
|
|
|
938
1338
|
launchBlockedMessage: missingProviderPriorityMessage(node.id)
|
|
939
1339
|
};
|
|
940
1340
|
}
|
|
1341
|
+
function getWorktreeBootstrapLaunchBlock(node) {
|
|
1342
|
+
const bootstrap = node.worktreeBootstrap;
|
|
1343
|
+
if (!node.isLocalWorktree || bootstrap?.status !== "failed" || bootstrap?.required === false) return void 0;
|
|
1344
|
+
return {
|
|
1345
|
+
success: false,
|
|
1346
|
+
code: "worktree_bootstrap_failed",
|
|
1347
|
+
error: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : `Node '${node.id}' has a failed required worktree bootstrap.`,
|
|
1348
|
+
nodeId: node.id,
|
|
1349
|
+
worktreeBootstrap: bootstrap,
|
|
1350
|
+
recoveryHint: "Fix the configured worktree bootstrap command or remove/recreate the worktree node before launching an agent."
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
async function collectLiveStatusSessions(ctx, node) {
|
|
1354
|
+
try {
|
|
1355
|
+
const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
|
|
1356
|
+
return extractStatusMetadataSessions(statusResult);
|
|
1357
|
+
} catch {
|
|
1358
|
+
return [];
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
async function collectMeshViewQueueNodesWithLiveSessions(ctx) {
|
|
1362
|
+
const nodes = await Promise.all(ctx.mesh.nodes.map(async (node) => {
|
|
1363
|
+
const liveSessions = await collectLiveStatusSessions(ctx, node);
|
|
1364
|
+
return liveSessions.length > 0 ? { ...node, sessions: liveSessions } : node;
|
|
1365
|
+
}));
|
|
1366
|
+
return nodes;
|
|
1367
|
+
}
|
|
941
1368
|
function readNumeric(value, fallback = 0) {
|
|
942
1369
|
const parsed = Number(value);
|
|
943
1370
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
@@ -1073,7 +1500,8 @@ async function commandForNode(ctx, node, command, args = {}) {
|
|
|
1073
1500
|
if (isLocalTransport(ctx.transport)) {
|
|
1074
1501
|
return ctx.transport.command(command, args);
|
|
1075
1502
|
}
|
|
1076
|
-
|
|
1503
|
+
const identity = buildNodeMachineIdentity(ctx, node);
|
|
1504
|
+
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})`);
|
|
1077
1505
|
}
|
|
1078
1506
|
function normalizePendingMeshCoordinatorEvents(value) {
|
|
1079
1507
|
const payload = unwrapCommandPayload(value);
|
|
@@ -1091,6 +1519,14 @@ function buildMeshForwardPayloadFromPendingEvent(event) {
|
|
|
1091
1519
|
providerType: readString(metadataEvent.providerType),
|
|
1092
1520
|
providerSessionId: readString(metadataEvent.providerSessionId),
|
|
1093
1521
|
finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
|
|
1522
|
+
jobId: readString(metadataEvent.jobId),
|
|
1523
|
+
interactionId: readString(metadataEvent.interactionId),
|
|
1524
|
+
status: readString(metadataEvent.status),
|
|
1525
|
+
targetDaemonId: readString(metadataEvent.targetDaemonId),
|
|
1526
|
+
startedAt: readString(metadataEvent.startedAt),
|
|
1527
|
+
completedAt: readString(metadataEvent.completedAt),
|
|
1528
|
+
retryOfJobId: readString(metadataEvent.retryOfJobId),
|
|
1529
|
+
...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
|
|
1094
1530
|
...metadataEvent.intentional === true ? { intentional: true } : {},
|
|
1095
1531
|
...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
|
|
1096
1532
|
...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
|
|
@@ -1107,8 +1543,9 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
|
|
|
1107
1543
|
const surfacedEvents = [];
|
|
1108
1544
|
try {
|
|
1109
1545
|
surfacedEvents.push(
|
|
1110
|
-
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
|
|
1546
|
+
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
|
|
1111
1547
|
);
|
|
1548
|
+
surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
|
|
1112
1549
|
} catch {
|
|
1113
1550
|
}
|
|
1114
1551
|
for (const node of ctx.mesh.nodes) {
|
|
@@ -1116,27 +1553,31 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
|
|
|
1116
1553
|
if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
|
|
1117
1554
|
try {
|
|
1118
1555
|
const remoteEvents = normalizePendingMeshCoordinatorEvents(
|
|
1119
|
-
await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", {})
|
|
1556
|
+
await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", { meshId: ctx.mesh.id })
|
|
1120
1557
|
).filter(matchesCurrentMesh);
|
|
1121
1558
|
if (remoteEvents.length === 0) continue;
|
|
1122
1559
|
for (const event of remoteEvents) {
|
|
1123
1560
|
const payload = buildMeshForwardPayloadFromPendingEvent(event);
|
|
1124
1561
|
if (!payload.event || !payload.meshId) continue;
|
|
1125
1562
|
await ctx.transport.command("mesh_forward_event", payload);
|
|
1563
|
+
rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
|
|
1126
1564
|
}
|
|
1127
1565
|
} catch {
|
|
1128
1566
|
}
|
|
1129
1567
|
}
|
|
1130
1568
|
try {
|
|
1131
1569
|
surfacedEvents.push(
|
|
1132
|
-
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
|
|
1570
|
+
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
|
|
1133
1571
|
);
|
|
1572
|
+
surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
|
|
1134
1573
|
} catch {
|
|
1135
1574
|
}
|
|
1136
1575
|
return surfacedEvents;
|
|
1137
1576
|
}
|
|
1138
1577
|
if (isLocalTransport(ctx.transport)) {
|
|
1139
|
-
|
|
1578
|
+
const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id).filter(matchesCurrentMesh);
|
|
1579
|
+
events.forEach(rememberMeshSessionProviderMetadataFromEvent);
|
|
1580
|
+
return events;
|
|
1140
1581
|
}
|
|
1141
1582
|
return [];
|
|
1142
1583
|
}
|
|
@@ -1153,11 +1594,12 @@ function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
|
|
|
1153
1594
|
}
|
|
1154
1595
|
var MESH_STATUS_TOOL = {
|
|
1155
1596
|
name: "mesh_status",
|
|
1156
|
-
description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures.",
|
|
1597
|
+
description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures. Do not repeatedly call this to wait for generating delegated work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
|
|
1157
1598
|
inputSchema: {
|
|
1158
1599
|
type: "object",
|
|
1159
1600
|
properties: {
|
|
1160
|
-
_gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
|
|
1601
|
+
_gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." },
|
|
1602
|
+
includeStaleDirectWorkDetails: { type: "boolean", description: "Opt in to the full staleDirectWork array. Defaults false; normal status returns compact staleDirectWorkSummary only." }
|
|
1161
1603
|
}
|
|
1162
1604
|
}
|
|
1163
1605
|
};
|
|
@@ -1177,14 +1619,16 @@ var MESH_ENQUEUE_TASK_TOOL = {
|
|
|
1177
1619
|
inputSchema: {
|
|
1178
1620
|
type: "object",
|
|
1179
1621
|
properties: {
|
|
1180
|
-
message: { type: "string", description: "The task instruction for the agent." }
|
|
1622
|
+
message: { type: "string", description: "The task instruction for the agent." },
|
|
1623
|
+
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." },
|
|
1624
|
+
taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
|
|
1181
1625
|
},
|
|
1182
1626
|
required: ["message"]
|
|
1183
1627
|
}
|
|
1184
1628
|
};
|
|
1185
1629
|
var MESH_VIEW_QUEUE_TOOL = {
|
|
1186
1630
|
name: "mesh_view_queue",
|
|
1187
|
-
description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records.",
|
|
1631
|
+
description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records. Do not repeatedly call this to wait for generating assigned work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
|
|
1188
1632
|
inputSchema: {
|
|
1189
1633
|
type: "object",
|
|
1190
1634
|
properties: {
|
|
@@ -1237,7 +1681,9 @@ var MESH_SEND_TASK_TOOL = {
|
|
|
1237
1681
|
properties: {
|
|
1238
1682
|
node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
|
|
1239
1683
|
session_id: { type: "string", description: "Agent session ID on the target node." },
|
|
1240
|
-
message: { type: "string", description: "Natural-language task to send to the agent." }
|
|
1684
|
+
message: { type: "string", description: "Natural-language task to send to the agent." },
|
|
1685
|
+
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." },
|
|
1686
|
+
taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
|
|
1241
1687
|
},
|
|
1242
1688
|
required: ["node_id", "session_id", "message"]
|
|
1243
1689
|
}
|
|
@@ -1295,6 +1741,21 @@ var MESH_GIT_STATUS_TOOL = {
|
|
|
1295
1741
|
required: ["node_id"]
|
|
1296
1742
|
}
|
|
1297
1743
|
};
|
|
1744
|
+
var MESH_FAST_FORWARD_NODE_TOOL = {
|
|
1745
|
+
name: "mesh_fast_forward_node",
|
|
1746
|
+
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.",
|
|
1747
|
+
inputSchema: {
|
|
1748
|
+
type: "object",
|
|
1749
|
+
properties: {
|
|
1750
|
+
node_id: { type: "string", description: "Target node ID." },
|
|
1751
|
+
branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
|
|
1752
|
+
execute: { type: "boolean", description: "When true, apply the fast-forward if all safety gates pass. Defaults false/dry-run." },
|
|
1753
|
+
dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
|
|
1754
|
+
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." }
|
|
1755
|
+
},
|
|
1756
|
+
required: ["node_id"]
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1298
1759
|
var MESH_CHECKPOINT_TOOL = {
|
|
1299
1760
|
name: "mesh_checkpoint",
|
|
1300
1761
|
description: "Create a git checkpoint (commit) on a mesh node workspace.",
|
|
@@ -1378,7 +1839,7 @@ var MESH_TASK_HISTORY_TOOL = {
|
|
|
1378
1839
|
type: "object",
|
|
1379
1840
|
properties: {
|
|
1380
1841
|
tail: { type: "number", description: "Number of recent entries to return (default: 20)." },
|
|
1381
|
-
kind: { type: "string", description: "Filter by entry kind: task_dispatched, task_completed, task_failed, task_stalled, session_launched, checkpoint_created, node_cloned, node_removed." }
|
|
1842
|
+
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." }
|
|
1382
1843
|
}
|
|
1383
1844
|
}
|
|
1384
1845
|
};
|
|
@@ -1398,7 +1859,7 @@ var MESH_RECONCILE_LEDGER_TOOL = {
|
|
|
1398
1859
|
};
|
|
1399
1860
|
var MESH_REFINE_NODE_TOOL = {
|
|
1400
1861
|
name: "mesh_refine_node",
|
|
1401
|
-
description: "The Refinery:
|
|
1862
|
+
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.",
|
|
1402
1863
|
inputSchema: {
|
|
1403
1864
|
type: "object",
|
|
1404
1865
|
properties: {
|
|
@@ -1407,6 +1868,43 @@ var MESH_REFINE_NODE_TOOL = {
|
|
|
1407
1868
|
required: ["node_id"]
|
|
1408
1869
|
}
|
|
1409
1870
|
};
|
|
1871
|
+
var MESH_REFINE_CONFIG_SCHEMA_TOOL = {
|
|
1872
|
+
name: "mesh_refine_config_schema",
|
|
1873
|
+
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.",
|
|
1874
|
+
inputSchema: { type: "object", properties: {} }
|
|
1875
|
+
};
|
|
1876
|
+
var MESH_VALIDATE_REFINE_CONFIG_TOOL = {
|
|
1877
|
+
name: "mesh_validate_refine_config",
|
|
1878
|
+
description: "Validate the repo mesh/refine config for a node/workspace without running validation commands or merging.",
|
|
1879
|
+
inputSchema: {
|
|
1880
|
+
type: "object",
|
|
1881
|
+
properties: {
|
|
1882
|
+
node_id: { type: "string", description: "Optional node/workspace whose refine config should be loaded. Defaults to the first mesh node." },
|
|
1883
|
+
config: { type: "object", description: "Optional inline config object to validate instead of loading from the repo." }
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
var MESH_SUGGEST_REFINE_CONFIG_TOOL = {
|
|
1888
|
+
name: "mesh_suggest_refine_config",
|
|
1889
|
+
description: "Suggest a repo mesh/refine config scaffold from project context/package scripts. Suggestions are never executed until saved as explicit refine config.",
|
|
1890
|
+
inputSchema: {
|
|
1891
|
+
type: "object",
|
|
1892
|
+
properties: {
|
|
1893
|
+
node_id: { type: "string", description: "Optional node/workspace used for suggestions. Defaults to the first mesh node." }
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
};
|
|
1897
|
+
var MESH_REFINE_PLAN_TOOL = {
|
|
1898
|
+
name: "mesh_refine_plan",
|
|
1899
|
+
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.",
|
|
1900
|
+
inputSchema: {
|
|
1901
|
+
type: "object",
|
|
1902
|
+
properties: {
|
|
1903
|
+
node_id: { type: "string", description: "Node ID of the worktree node to plan." }
|
|
1904
|
+
},
|
|
1905
|
+
required: ["node_id"]
|
|
1906
|
+
}
|
|
1907
|
+
};
|
|
1410
1908
|
var ALL_MESH_TOOLS = [
|
|
1411
1909
|
MESH_STATUS_TOOL,
|
|
1412
1910
|
MESH_LIST_NODES_TOOL,
|
|
@@ -1419,16 +1917,21 @@ var ALL_MESH_TOOLS = [
|
|
|
1419
1917
|
MESH_READ_DEBUG_TOOL,
|
|
1420
1918
|
MESH_LAUNCH_SESSION_TOOL,
|
|
1421
1919
|
MESH_GIT_STATUS_TOOL,
|
|
1920
|
+
MESH_FAST_FORWARD_NODE_TOOL,
|
|
1422
1921
|
MESH_CHECKPOINT_TOOL,
|
|
1423
1922
|
MESH_APPROVE_TOOL,
|
|
1424
1923
|
MESH_CLONE_NODE_TOOL,
|
|
1425
1924
|
MESH_REMOVE_NODE_TOOL,
|
|
1426
1925
|
MESH_REFINE_NODE_TOOL,
|
|
1926
|
+
MESH_REFINE_CONFIG_SCHEMA_TOOL,
|
|
1927
|
+
MESH_VALIDATE_REFINE_CONFIG_TOOL,
|
|
1928
|
+
MESH_SUGGEST_REFINE_CONFIG_TOOL,
|
|
1929
|
+
MESH_REFINE_PLAN_TOOL,
|
|
1427
1930
|
MESH_CLEANUP_SESSIONS_TOOL,
|
|
1428
1931
|
MESH_TASK_HISTORY_TOOL,
|
|
1429
1932
|
MESH_RECONCILE_LEDGER_TOOL
|
|
1430
1933
|
];
|
|
1431
|
-
async function meshStatus(ctx) {
|
|
1934
|
+
async function meshStatus(ctx, args = {}) {
|
|
1432
1935
|
await refreshMeshFromDaemon(ctx);
|
|
1433
1936
|
const { mesh, transport } = ctx;
|
|
1434
1937
|
const results = [];
|
|
@@ -1437,6 +1940,9 @@ async function meshStatus(ctx) {
|
|
|
1437
1940
|
const entry = {
|
|
1438
1941
|
nodeId: node.id,
|
|
1439
1942
|
workspace: node.workspace,
|
|
1943
|
+
machine: buildNodeMachineIdentity(ctx, node),
|
|
1944
|
+
daemonId: readNodeDaemonId(node),
|
|
1945
|
+
machineId: readNodeMachineId(node),
|
|
1440
1946
|
...getNodeLaunchReadiness(node)
|
|
1441
1947
|
};
|
|
1442
1948
|
try {
|
|
@@ -1446,6 +1952,7 @@ async function meshStatus(ctx) {
|
|
|
1446
1952
|
const uncommittedChanges = countUncommittedChanges(status);
|
|
1447
1953
|
const dirty = isGitStatusDirty(status);
|
|
1448
1954
|
entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
|
|
1955
|
+
assignFullGitSnapshot(entry, status);
|
|
1449
1956
|
entry.branch = status?.branch;
|
|
1450
1957
|
entry.isDirty = dirty;
|
|
1451
1958
|
entry.uncommittedChanges = uncommittedChanges;
|
|
@@ -1467,6 +1974,7 @@ async function meshStatus(ctx) {
|
|
|
1467
1974
|
const uncommittedChanges = countUncommittedChanges(status);
|
|
1468
1975
|
const dirty = isGitStatusDirty(status);
|
|
1469
1976
|
entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
|
|
1977
|
+
assignFullGitSnapshot(entry, status);
|
|
1470
1978
|
entry.branch = status?.branch;
|
|
1471
1979
|
entry.isDirty = dirty;
|
|
1472
1980
|
entry.uncommittedChanges = uncommittedChanges;
|
|
@@ -1542,15 +2050,43 @@ async function meshStatus(ctx) {
|
|
|
1542
2050
|
}
|
|
1543
2051
|
const relatedRepos = await collectRelatedRepoStatuses(ctx, node);
|
|
1544
2052
|
if (relatedRepos.length) entry.relatedRepos = relatedRepos;
|
|
2053
|
+
const liveSessions = await collectLiveStatusSessions(ctx, node);
|
|
2054
|
+
if (liveSessions.length > 0) {
|
|
2055
|
+
entry.sessions = liveSessions;
|
|
2056
|
+
}
|
|
1545
2057
|
results.push(entry);
|
|
1546
2058
|
}
|
|
2059
|
+
const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 500 });
|
|
2060
|
+
const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
|
|
2061
|
+
meshId: mesh.id,
|
|
2062
|
+
queue: (0, import_daemon_core.getQueue)(mesh.id),
|
|
2063
|
+
ledgerEntries,
|
|
2064
|
+
nodes: results
|
|
2065
|
+
});
|
|
2066
|
+
const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
|
|
2067
|
+
const staleDirectWorkSummary = (0, import_daemon_core.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
|
|
2068
|
+
note: activeWorkEvidence.staleDirectWorkNote,
|
|
2069
|
+
detailHint: "Full stale direct entries are omitted from mesh_status by default. Call mesh_status with includeStaleDirectWorkDetails=true or inspect mesh_task_history for ledger detail."
|
|
2070
|
+
});
|
|
1547
2071
|
const response = {
|
|
1548
2072
|
meshId: mesh.id,
|
|
1549
2073
|
meshName: mesh.name,
|
|
1550
2074
|
repoIdentity: mesh.repoIdentity,
|
|
1551
2075
|
policy: mesh.policy,
|
|
1552
2076
|
refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2077
|
+
sourceOfTruth: {
|
|
2078
|
+
membership: "coordinator_daemon_live_mesh",
|
|
2079
|
+
currentStatus: "live_git_and_session_probes",
|
|
2080
|
+
activeWork: "mesh_queue_file_and_local_ledger",
|
|
2081
|
+
historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
|
|
2082
|
+
},
|
|
1553
2083
|
nodes: results,
|
|
2084
|
+
activeWork: activeWorkEvidence.activeWork,
|
|
2085
|
+
staleDirectWorkSummary,
|
|
2086
|
+
...args.includeStaleDirectWorkDetails === true ? { staleDirectWork: activeWorkEvidence.staleDirectWork } : {},
|
|
2087
|
+
terminalDirectWork: activeWorkEvidence.terminalDirectWork,
|
|
2088
|
+
activeWorkSummary: activeWorkEvidence.summary,
|
|
2089
|
+
...pollingGuidance ? { pollingGuidance } : {},
|
|
1554
2090
|
branchConvergenceSummary: summarizeBranchConvergence(results)
|
|
1555
2091
|
};
|
|
1556
2092
|
try {
|
|
@@ -1559,6 +2095,14 @@ async function meshStatus(ctx) {
|
|
|
1559
2095
|
}
|
|
1560
2096
|
try {
|
|
1561
2097
|
const pendingEvents = await drainCoordinatorPendingEvents(ctx);
|
|
2098
|
+
const asyncRefineJobs = (0, import_daemon_core.buildMeshAsyncRefineJobs)({
|
|
2099
|
+
meshId: mesh.id,
|
|
2100
|
+
ledgerEntries,
|
|
2101
|
+
pendingEvents
|
|
2102
|
+
});
|
|
2103
|
+
if (asyncRefineJobs.length > 0) {
|
|
2104
|
+
response.asyncRefineJobs = asyncRefineJobs;
|
|
2105
|
+
}
|
|
1562
2106
|
if (pendingEvents.length > 0) {
|
|
1563
2107
|
response.pendingCoordinatorEvents = pendingEvents;
|
|
1564
2108
|
}
|
|
@@ -1568,12 +2112,17 @@ async function meshStatus(ctx) {
|
|
|
1568
2112
|
}
|
|
1569
2113
|
async function meshTaskHistory(ctx, args) {
|
|
1570
2114
|
const { mesh } = ctx;
|
|
1571
|
-
await drainCoordinatorPendingEvents(ctx);
|
|
2115
|
+
const pendingEvents = await drainCoordinatorPendingEvents(ctx);
|
|
1572
2116
|
const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
|
|
1573
2117
|
const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
|
|
1574
2118
|
const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
|
|
1575
2119
|
const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
|
|
1576
|
-
return JSON.stringify({
|
|
2120
|
+
return JSON.stringify({
|
|
2121
|
+
meshId: mesh.id,
|
|
2122
|
+
entries,
|
|
2123
|
+
summary,
|
|
2124
|
+
...pendingEvents.length > 0 ? { pendingCoordinatorEvents: pendingEvents } : {}
|
|
2125
|
+
}, null, 2);
|
|
1577
2126
|
}
|
|
1578
2127
|
async function meshReconcileLedger(ctx, args) {
|
|
1579
2128
|
await refreshMeshFromDaemon(ctx);
|
|
@@ -1663,6 +2212,9 @@ async function meshListNodes(ctx) {
|
|
|
1663
2212
|
nodeId: n.id,
|
|
1664
2213
|
workspace: n.workspace,
|
|
1665
2214
|
repoRoot: n.repoRoot,
|
|
2215
|
+
daemonId: readNodeDaemonId(n),
|
|
2216
|
+
machineId: readNodeMachineId(n),
|
|
2217
|
+
machine: buildNodeMachineIdentity(ctx, n),
|
|
1666
2218
|
isLocalWorktree: n.isLocalWorktree,
|
|
1667
2219
|
policy: n.policy,
|
|
1668
2220
|
relatedRepos: readRelatedRepos(n),
|
|
@@ -1672,12 +2224,13 @@ async function meshListNodes(ctx) {
|
|
|
1672
2224
|
}, null, 2);
|
|
1673
2225
|
}
|
|
1674
2226
|
async function meshEnqueueTask(ctx, args) {
|
|
2227
|
+
const taskMode = readString(args.task_mode) || readString(args.taskMode);
|
|
1675
2228
|
try {
|
|
1676
|
-
const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message);
|
|
2229
|
+
const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode });
|
|
1677
2230
|
if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
|
|
1678
2231
|
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
1679
2232
|
});
|
|
1680
|
-
return JSON.stringify({ success: true, taskId: task.id, status: task.status });
|
|
2233
|
+
return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
|
|
1681
2234
|
}
|
|
1682
2235
|
if (ctx.transport instanceof IpcTransport) {
|
|
1683
2236
|
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
@@ -1690,11 +2243,24 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
1690
2243
|
ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
|
|
1691
2244
|
if (result.success) {
|
|
1692
2245
|
try {
|
|
2246
|
+
const providerType = result.providerType;
|
|
2247
|
+
const descriptor = summarizeTaskMessage(args.message);
|
|
1693
2248
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
1694
2249
|
kind: "task_dispatched",
|
|
1695
2250
|
nodeId: node.id,
|
|
1696
2251
|
sessionId: result.sessionId,
|
|
1697
|
-
|
|
2252
|
+
providerType,
|
|
2253
|
+
payload: {
|
|
2254
|
+
source: "queue",
|
|
2255
|
+
via: "p2p_direct",
|
|
2256
|
+
taskId: task.id,
|
|
2257
|
+
message: args.message,
|
|
2258
|
+
taskTitle: descriptor.taskTitle,
|
|
2259
|
+
taskSummary: descriptor.taskSummary,
|
|
2260
|
+
...task.taskMode ? { taskMode: task.taskMode } : {},
|
|
2261
|
+
...providerType ? { providerType } : {},
|
|
2262
|
+
targetSessionId: result.sessionId
|
|
2263
|
+
}
|
|
1698
2264
|
});
|
|
1699
2265
|
} catch {
|
|
1700
2266
|
}
|
|
@@ -1705,24 +2271,37 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
1705
2271
|
}
|
|
1706
2272
|
Promise.all(dispatchPromises).catch(() => {
|
|
1707
2273
|
});
|
|
1708
|
-
return JSON.stringify({ success: true, taskId: task.id, status: task.status });
|
|
2274
|
+
return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
|
|
1709
2275
|
}
|
|
1710
|
-
return JSON.stringify({ success: true, taskId: task.id, status: task.status });
|
|
2276
|
+
return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
|
|
1711
2277
|
} catch (e) {
|
|
1712
|
-
|
|
2278
|
+
const message = e?.message || String(e);
|
|
2279
|
+
if (message.includes("live_debug_readonly_guardrail_violation")) {
|
|
2280
|
+
return JSON.stringify({ success: false, code: "live_debug_readonly_guardrail_violation", taskMode, error: message });
|
|
2281
|
+
}
|
|
2282
|
+
return JSON.stringify({ success: false, error: message });
|
|
1713
2283
|
}
|
|
1714
2284
|
}
|
|
1715
2285
|
async function meshViewQueue(ctx, args) {
|
|
1716
2286
|
try {
|
|
2287
|
+
await refreshMeshFromDaemon(ctx);
|
|
1717
2288
|
const statusFilter = sanitizeQueueStatusFilter(args.status);
|
|
1718
2289
|
const view = normalizeQueueViewMode(args.view);
|
|
1719
|
-
const fullQueue = annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh);
|
|
2290
|
+
const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh));
|
|
1720
2291
|
const queue = filterQueueForView(fullQueue, view, statusFilter);
|
|
1721
2292
|
const summary = buildQueueStatusSummary(fullQueue);
|
|
1722
2293
|
const visibleSummary = buildQueueStatusSummary(queue);
|
|
1723
2294
|
const maintenance = buildQueueMaintenanceReport(fullQueue);
|
|
2295
|
+
const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
|
|
2296
|
+
const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
|
|
2297
|
+
meshId: ctx.mesh.id,
|
|
2298
|
+
queue: fullQueue,
|
|
2299
|
+
ledgerEntries: (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 }),
|
|
2300
|
+
nodes: liveNodes
|
|
2301
|
+
});
|
|
1724
2302
|
const staleAssignedTasks = maintenance.staleAssignedTasks || [];
|
|
1725
2303
|
const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
|
|
2304
|
+
const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
|
|
1726
2305
|
return JSON.stringify({
|
|
1727
2306
|
success: true,
|
|
1728
2307
|
sourceOfTruth: {
|
|
@@ -1738,6 +2317,10 @@ async function meshViewQueue(ctx, args) {
|
|
|
1738
2317
|
},
|
|
1739
2318
|
queue,
|
|
1740
2319
|
visibleQueue: queue,
|
|
2320
|
+
activeWork: activeWorkEvidence.activeWork,
|
|
2321
|
+
staleDirectWork: activeWorkEvidence.staleDirectWork,
|
|
2322
|
+
activeWorkSummary: activeWorkEvidence.summary,
|
|
2323
|
+
...pollingGuidance ? { pollingGuidance } : {},
|
|
1741
2324
|
visibleSummary,
|
|
1742
2325
|
summary,
|
|
1743
2326
|
activeCounts: summary.activeCounts,
|
|
@@ -1771,6 +2354,10 @@ async function meshQueueCancel(ctx, args) {
|
|
|
1771
2354
|
if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
|
|
1772
2355
|
const task = (0, import_daemon_core.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
|
|
1773
2356
|
if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
|
|
2357
|
+
if (isLocalTransport(ctx.transport)) {
|
|
2358
|
+
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
1774
2361
|
return JSON.stringify({ success: true, task }, null, 2);
|
|
1775
2362
|
} catch (e) {
|
|
1776
2363
|
return JSON.stringify({ success: false, error: e.message });
|
|
@@ -1801,10 +2388,46 @@ async function meshQueueRequeue(ctx, args) {
|
|
|
1801
2388
|
}
|
|
1802
2389
|
}
|
|
1803
2390
|
async function meshSendTask(ctx, args) {
|
|
2391
|
+
const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
|
|
2392
|
+
const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
|
|
2393
|
+
if (!modeValidation.valid) {
|
|
2394
|
+
return JSON.stringify({
|
|
2395
|
+
success: false,
|
|
2396
|
+
code: "live_debug_readonly_guardrail_violation",
|
|
2397
|
+
taskMode: modeValidation.taskMode || requestedTaskMode,
|
|
2398
|
+
violations: modeValidation.violations,
|
|
2399
|
+
allowedOperations: modeValidation.allowedOperations,
|
|
2400
|
+
error: `live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
const taskMode = modeValidation.taskMode;
|
|
1804
2404
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
1805
2405
|
if (node.policy?.readOnly) {
|
|
1806
2406
|
return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
|
|
1807
2407
|
}
|
|
2408
|
+
let explicitTargetSession;
|
|
2409
|
+
if (args.session_id && isWorkerTaskMode(taskMode) && (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport))) {
|
|
2410
|
+
try {
|
|
2411
|
+
const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
|
|
2412
|
+
const sessions = extractStatusMetadataSessions(statusResult);
|
|
2413
|
+
explicitTargetSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
|
|
2414
|
+
if (explicitTargetSession && isMeshCoordinatorSessionRecord(explicitTargetSession)) {
|
|
2415
|
+
return JSON.stringify({
|
|
2416
|
+
success: false,
|
|
2417
|
+
recoverable: true,
|
|
2418
|
+
code: "mesh_target_session_is_coordinator",
|
|
2419
|
+
reason: "mesh_target_session_is_coordinator",
|
|
2420
|
+
nodeId: args.node_id,
|
|
2421
|
+
sessionId: args.session_id,
|
|
2422
|
+
taskMode: taskMode || "unspecified",
|
|
2423
|
+
error: `Session '${args.session_id}' is a Repo Mesh coordinator session, not a visible worker session. Launch or use a visible worker session before dispatching this task.`,
|
|
2424
|
+
nextAction: `Call mesh_launch_session for node '${args.node_id}' and then retry mesh_send_task with that worker session_id, or use mesh_enqueue_task for queue-based worker assignment.`
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
} catch {
|
|
2428
|
+
explicitTargetSession = void 0;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
1808
2431
|
const duplicate = hasRecentDuplicateDispatch(ctx, args);
|
|
1809
2432
|
if (duplicate.duplicate) {
|
|
1810
2433
|
return JSON.stringify({
|
|
@@ -1828,75 +2451,144 @@ async function meshSendTask(ctx, args) {
|
|
|
1828
2451
|
const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
|
|
1829
2452
|
meshId: ctx.mesh.id,
|
|
1830
2453
|
message: args.message,
|
|
1831
|
-
targetNodeId: args.node_id
|
|
2454
|
+
targetNodeId: args.node_id,
|
|
2455
|
+
...taskMode ? { taskMode } : {}
|
|
1832
2456
|
});
|
|
1833
2457
|
return JSON.stringify(res);
|
|
1834
2458
|
}
|
|
1835
2459
|
const isLocalNode = isLocalControlPlaneNode(ctx, node);
|
|
1836
2460
|
if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
|
|
1837
2461
|
const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
|
|
2462
|
+
const taskId = (0, import_node_crypto.randomUUID)();
|
|
1838
2463
|
const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
|
|
1839
2464
|
session_id: args.session_id,
|
|
1840
2465
|
message: args.message,
|
|
1841
|
-
providerType: cached?.providerType
|
|
2466
|
+
providerType: cached?.providerType,
|
|
2467
|
+
verifiedSession: explicitTargetSession
|
|
1842
2468
|
});
|
|
1843
2469
|
if (result2.success) {
|
|
1844
2470
|
const dispatchedSessionId = args.session_id || result2.sessionId;
|
|
1845
2471
|
try {
|
|
2472
|
+
const providerType = result2.providerType || cached?.providerType;
|
|
1846
2473
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
1847
2474
|
kind: "task_dispatched",
|
|
1848
2475
|
nodeId: args.node_id,
|
|
1849
2476
|
sessionId: dispatchedSessionId,
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
2477
|
+
providerType,
|
|
2478
|
+
payload: buildDirectTaskPayload(args.message, "p2p_direct", {
|
|
2479
|
+
taskId,
|
|
2480
|
+
taskMode,
|
|
2481
|
+
providerType,
|
|
2482
|
+
targetSessionId: dispatchedSessionId
|
|
2483
|
+
})
|
|
1855
2484
|
});
|
|
1856
2485
|
} catch {
|
|
1857
2486
|
}
|
|
1858
2487
|
}
|
|
1859
|
-
return JSON.stringify({
|
|
2488
|
+
return JSON.stringify({
|
|
2489
|
+
...result2,
|
|
2490
|
+
nodeId: args.node_id,
|
|
2491
|
+
sessionId: result2.success ? args.session_id || result2.sessionId : args.session_id,
|
|
2492
|
+
...result2.success ? { source: "direct", taskId } : {},
|
|
2493
|
+
taskMode,
|
|
2494
|
+
...result2.success && result2.providerType ? { providerType: result2.providerType } : {},
|
|
2495
|
+
dispatched: result2.success === true
|
|
2496
|
+
});
|
|
1860
2497
|
}
|
|
1861
2498
|
if (args.session_id && isLocalTransport(ctx.transport)) {
|
|
1862
2499
|
const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
|
|
2500
|
+
let resolvedProviderType = cached?.providerType || "";
|
|
2501
|
+
if (!resolvedProviderType) {
|
|
2502
|
+
let explicitSession = explicitTargetSession;
|
|
2503
|
+
if (!explicitSession) {
|
|
2504
|
+
const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
|
|
2505
|
+
const sessions = extractStatusMetadataSessions(statusResult);
|
|
2506
|
+
explicitSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
|
|
2507
|
+
}
|
|
2508
|
+
if (!explicitSession) {
|
|
2509
|
+
return JSON.stringify({
|
|
2510
|
+
success: false,
|
|
2511
|
+
recoverable: true,
|
|
2512
|
+
code: "mesh_target_session_not_found",
|
|
2513
|
+
reason: "mesh_target_session_not_found",
|
|
2514
|
+
transport: "local_ipc",
|
|
2515
|
+
retryRecommended: true,
|
|
2516
|
+
nodeId: args.node_id,
|
|
2517
|
+
sessionId: args.session_id,
|
|
2518
|
+
error: `Local session '${args.session_id}' is not present in live status for node '${args.node_id}'.`,
|
|
2519
|
+
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.`
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
resolvedProviderType = resolveSessionProviderType(explicitSession);
|
|
2523
|
+
if (resolvedProviderType) {
|
|
2524
|
+
meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
|
|
2525
|
+
providerType: resolvedProviderType,
|
|
2526
|
+
providerSessionId: readString(explicitSession?.providerSessionId) || void 0
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
if (!resolvedProviderType) {
|
|
2531
|
+
return JSON.stringify({
|
|
2532
|
+
success: false,
|
|
2533
|
+
recoverable: true,
|
|
2534
|
+
code: "mesh_target_session_provider_unknown",
|
|
2535
|
+
reason: "mesh_target_session_provider_unknown",
|
|
2536
|
+
transport: "local_ipc",
|
|
2537
|
+
retryRecommended: false,
|
|
2538
|
+
nodeId: args.node_id,
|
|
2539
|
+
sessionId: args.session_id,
|
|
2540
|
+
error: `Local session '${args.session_id}' is live but does not expose providerType/cliType, so agent_command cannot be routed safely.`,
|
|
2541
|
+
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.`
|
|
2542
|
+
});
|
|
2543
|
+
}
|
|
1863
2544
|
const dispatchResult = await commandForNode(ctx, node, "agent_command", {
|
|
1864
2545
|
targetSessionId: args.session_id,
|
|
1865
|
-
|
|
2546
|
+
agentType: resolvedProviderType,
|
|
2547
|
+
cliType: resolvedProviderType,
|
|
2548
|
+
providerType: resolvedProviderType,
|
|
1866
2549
|
action: "send_chat",
|
|
1867
2550
|
message: args.message
|
|
1868
2551
|
});
|
|
1869
2552
|
const dispatchPayload = unwrapCommandPayload(dispatchResult);
|
|
1870
2553
|
if (dispatchPayload?.success === false || dispatchResult?.success === false) {
|
|
2554
|
+
const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
|
|
1871
2555
|
return JSON.stringify({
|
|
2556
|
+
...source && typeof source === "object" ? source : {},
|
|
1872
2557
|
success: false,
|
|
1873
2558
|
nodeId: args.node_id,
|
|
1874
2559
|
sessionId: args.session_id,
|
|
1875
2560
|
error: dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"
|
|
1876
2561
|
});
|
|
1877
2562
|
}
|
|
2563
|
+
const taskId = (0, import_node_crypto.randomUUID)();
|
|
1878
2564
|
try {
|
|
1879
2565
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
1880
2566
|
kind: "task_dispatched",
|
|
1881
2567
|
nodeId: args.node_id,
|
|
1882
2568
|
sessionId: args.session_id,
|
|
1883
|
-
providerType:
|
|
1884
|
-
payload:
|
|
2569
|
+
providerType: resolvedProviderType,
|
|
2570
|
+
payload: buildDirectTaskPayload(args.message, "local_direct", {
|
|
2571
|
+
taskId,
|
|
2572
|
+
taskMode,
|
|
2573
|
+
providerType: resolvedProviderType,
|
|
2574
|
+
targetSessionId: args.session_id
|
|
2575
|
+
})
|
|
1885
2576
|
});
|
|
1886
2577
|
} catch {
|
|
1887
2578
|
}
|
|
1888
|
-
return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
|
|
2579
|
+
return JSON.stringify({ success: true, dispatched: true, source: "direct", taskId, taskMode, providerType: resolvedProviderType, nodeId: args.node_id, sessionId: args.session_id });
|
|
1889
2580
|
}
|
|
1890
2581
|
const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
|
|
1891
2582
|
targetNodeId: args.node_id,
|
|
1892
|
-
targetSessionId: args.session_id
|
|
2583
|
+
targetSessionId: args.session_id,
|
|
2584
|
+
taskMode
|
|
1893
2585
|
});
|
|
1894
2586
|
if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
|
|
1895
2587
|
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
1896
2588
|
});
|
|
1897
2589
|
}
|
|
1898
|
-
const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)() : [];
|
|
1899
|
-
const result = { success: true, nodeId: args.node_id, taskId: task.id, status: task.status };
|
|
2590
|
+
const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id) : [];
|
|
2591
|
+
const result = { success: true, source: "queue", nodeId: args.node_id, taskId: task.id, status: task.status, taskMode: task.taskMode };
|
|
1900
2592
|
if (pendingEvents.length > 0) {
|
|
1901
2593
|
result.pendingCoordinatorEvents = pendingEvents;
|
|
1902
2594
|
}
|
|
@@ -1920,7 +2612,7 @@ async function meshReadChat(ctx, args) {
|
|
|
1920
2612
|
await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
|
|
1921
2613
|
}
|
|
1922
2614
|
if (isLocalTransport(ctx.transport)) {
|
|
1923
|
-
const cached =
|
|
2615
|
+
const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
|
|
1924
2616
|
const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
|
|
1925
2617
|
const result = await commandForNode(ctx, node, "read_chat", {
|
|
1926
2618
|
sessionId: args.session_id,
|
|
@@ -1966,7 +2658,7 @@ async function meshReadChat(ctx, args) {
|
|
|
1966
2658
|
async function meshReadDebug(ctx, args) {
|
|
1967
2659
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
1968
2660
|
if (isLocalTransport(ctx.transport)) {
|
|
1969
|
-
const cached =
|
|
2661
|
+
const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
|
|
1970
2662
|
const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
|
|
1971
2663
|
const delivery = args.delivery === "inline" ? void 0 : "daemon_file";
|
|
1972
2664
|
const result = await commandForNode(ctx, node, "get_chat_debug_bundle", {
|
|
@@ -1997,6 +2689,8 @@ async function meshReadDebug(ctx, args) {
|
|
|
1997
2689
|
}
|
|
1998
2690
|
async function meshLaunchSession(ctx, args) {
|
|
1999
2691
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2692
|
+
const bootstrapBlock = getWorktreeBootstrapLaunchBlock(node);
|
|
2693
|
+
if (bootstrapBlock) return JSON.stringify(bootstrapBlock, null, 2);
|
|
2000
2694
|
if (isLocalTransport(ctx.transport)) {
|
|
2001
2695
|
let resolvedProviderType = typeof args.type === "string" && args.type.trim() ? args.type : "";
|
|
2002
2696
|
if (!resolvedProviderType) {
|
|
@@ -2021,6 +2715,10 @@ async function meshLaunchSession(ctx, args) {
|
|
|
2021
2715
|
const coordinatorNode = resolveCoordinatorNode(ctx);
|
|
2022
2716
|
const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
|
|
2023
2717
|
const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
|
|
2718
|
+
const isLocalNode = isLocalControlPlaneNode(ctx, node);
|
|
2719
|
+
if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
|
|
2720
|
+
return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
|
|
2721
|
+
}
|
|
2024
2722
|
let result;
|
|
2025
2723
|
try {
|
|
2026
2724
|
result = await commandForNode(ctx, node, "launch_cli", {
|
|
@@ -2061,7 +2759,6 @@ async function meshLaunchSession(ctx, args) {
|
|
|
2061
2759
|
});
|
|
2062
2760
|
} catch {
|
|
2063
2761
|
}
|
|
2064
|
-
const isLocalNode = isLocalControlPlaneNode(ctx, node);
|
|
2065
2762
|
if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
|
|
2066
2763
|
ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
2067
2764
|
});
|
|
@@ -2086,6 +2783,9 @@ async function meshLaunchSession(ctx, args) {
|
|
|
2086
2783
|
const coordinatorNode = resolveCoordinatorNode(ctx);
|
|
2087
2784
|
const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
|
|
2088
2785
|
const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
|
|
2786
|
+
if (!coordinatorDaemonId) {
|
|
2787
|
+
return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
|
|
2788
|
+
}
|
|
2089
2789
|
try {
|
|
2090
2790
|
const res = await ctx.transport.launch(node.daemonId, {
|
|
2091
2791
|
type: resolvedProviderType,
|
|
@@ -2166,6 +2866,51 @@ async function meshGitStatus(ctx, args) {
|
|
|
2166
2866
|
}, null, 2);
|
|
2167
2867
|
}
|
|
2168
2868
|
}
|
|
2869
|
+
async function meshFastForwardNode(ctx, args) {
|
|
2870
|
+
await refreshMeshFromDaemon(ctx);
|
|
2871
|
+
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2872
|
+
const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
|
|
2873
|
+
if (node.policy?.readOnly) {
|
|
2874
|
+
return JSON.stringify({
|
|
2875
|
+
success: false,
|
|
2876
|
+
code: "node_read_only",
|
|
2877
|
+
nodeId: args.node_id,
|
|
2878
|
+
workspace: node.workspace,
|
|
2879
|
+
allowed: false,
|
|
2880
|
+
willRun: false,
|
|
2881
|
+
executed: false,
|
|
2882
|
+
blockingReasons: ["node_read_only"]
|
|
2883
|
+
}, null, 2);
|
|
2884
|
+
}
|
|
2885
|
+
try {
|
|
2886
|
+
const dryRun = args.dry_run === true || args.execute !== true;
|
|
2887
|
+
const result = await commandForNode(ctx, node, "fast_forward_mesh_node", {
|
|
2888
|
+
meshId: ctx.mesh.id,
|
|
2889
|
+
nodeId: node.id,
|
|
2890
|
+
workspace: node.workspace,
|
|
2891
|
+
branch: typeof args.branch === "string" ? args.branch : void 0,
|
|
2892
|
+
execute: args.execute === true && args.dry_run !== true,
|
|
2893
|
+
dryRun,
|
|
2894
|
+
updateSubmodules: args.update_submodules === true,
|
|
2895
|
+
submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
|
|
2896
|
+
});
|
|
2897
|
+
return JSON.stringify(unwrapCommandPayload(result), null, 2);
|
|
2898
|
+
} catch (e) {
|
|
2899
|
+
const failure = buildCoordinatorP2pRelayFailure(e, {
|
|
2900
|
+
command: "fast_forward_mesh_node",
|
|
2901
|
+
targetDaemonId: node.daemonId,
|
|
2902
|
+
nodeId: args.node_id
|
|
2903
|
+
});
|
|
2904
|
+
return JSON.stringify({
|
|
2905
|
+
...failure,
|
|
2906
|
+
workspace: node.workspace,
|
|
2907
|
+
allowed: false,
|
|
2908
|
+
willRun: false,
|
|
2909
|
+
executed: false,
|
|
2910
|
+
blockingReasons: [failure.code || "mesh_fast_forward_unavailable"]
|
|
2911
|
+
}, null, 2);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2169
2914
|
async function meshCheckpoint(ctx, args) {
|
|
2170
2915
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2171
2916
|
if (node.policy?.readOnly) {
|
|
@@ -2181,7 +2926,13 @@ async function meshCheckpoint(ctx, args) {
|
|
|
2181
2926
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
2182
2927
|
kind: "checkpoint_created",
|
|
2183
2928
|
nodeId: args.node_id,
|
|
2184
|
-
payload: {
|
|
2929
|
+
payload: {
|
|
2930
|
+
message: args.message,
|
|
2931
|
+
commit: result?.checkpoint?.commit,
|
|
2932
|
+
outcome: result?.checkpoint?.status || (result?.checkpoint?.noop ? "skipped" : void 0),
|
|
2933
|
+
noop: result?.checkpoint?.noop === true,
|
|
2934
|
+
reason: result?.checkpoint?.reason
|
|
2935
|
+
}
|
|
2185
2936
|
});
|
|
2186
2937
|
} catch {
|
|
2187
2938
|
}
|
|
@@ -2197,7 +2948,13 @@ async function meshCheckpoint(ctx, args) {
|
|
|
2197
2948
|
(0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
|
|
2198
2949
|
kind: "checkpoint_created",
|
|
2199
2950
|
nodeId: args.node_id,
|
|
2200
|
-
payload: {
|
|
2951
|
+
payload: {
|
|
2952
|
+
message: args.message,
|
|
2953
|
+
commit: res?.checkpoint?.commit,
|
|
2954
|
+
outcome: res?.checkpoint?.status || (res?.checkpoint?.noop ? "skipped" : void 0),
|
|
2955
|
+
noop: res?.checkpoint?.noop === true,
|
|
2956
|
+
reason: res?.checkpoint?.reason
|
|
2957
|
+
}
|
|
2201
2958
|
});
|
|
2202
2959
|
} catch {
|
|
2203
2960
|
}
|
|
@@ -2251,6 +3008,7 @@ async function meshCloneNode(ctx, args) {
|
|
|
2251
3008
|
if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
|
|
2252
3009
|
else ctx.mesh.nodes.push(clonePayload.node);
|
|
2253
3010
|
ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3011
|
+
await syncCoordinatorDaemonMeshCache(ctx);
|
|
2254
3012
|
}
|
|
2255
3013
|
return JSON.stringify(result, null, 2);
|
|
2256
3014
|
} else if (!isLocalTransport(ctx.transport) && sourceNode.daemonId) {
|
|
@@ -2268,6 +3026,7 @@ async function meshCloneNode(ctx, args) {
|
|
|
2268
3026
|
if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
|
|
2269
3027
|
else ctx.mesh.nodes.push(clonePayload.node);
|
|
2270
3028
|
ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3029
|
+
await syncCoordinatorDaemonMeshCache(ctx);
|
|
2271
3030
|
}
|
|
2272
3031
|
return JSON.stringify(res, null, 2);
|
|
2273
3032
|
} catch (e) {
|
|
@@ -2363,6 +3122,43 @@ async function meshRemoveNode(ctx, args) {
|
|
|
2363
3122
|
return JSON.stringify({ error: "Cloud mesh remove_node requires node daemonId" });
|
|
2364
3123
|
}
|
|
2365
3124
|
}
|
|
3125
|
+
function resolveRefineConfigNode(ctx, nodeId) {
|
|
3126
|
+
if (nodeId) return findNode(ctx.mesh, nodeId);
|
|
3127
|
+
const node = ctx.mesh.nodes.find((entry) => !!entry.workspace);
|
|
3128
|
+
if (!node) throw new Error("No mesh node with a workspace is available");
|
|
3129
|
+
return node;
|
|
3130
|
+
}
|
|
3131
|
+
async function meshRefineConfigSchema(ctx) {
|
|
3132
|
+
const node = resolveRefineConfigNode(ctx);
|
|
3133
|
+
const result = await commandForNode(ctx, node, "get_mesh_refine_config_schema", {});
|
|
3134
|
+
return JSON.stringify(result, null, 2);
|
|
3135
|
+
}
|
|
3136
|
+
async function meshValidateRefineConfig(ctx, args) {
|
|
3137
|
+
const node = resolveRefineConfigNode(ctx, args.node_id);
|
|
3138
|
+
const result = await commandForNode(ctx, node, "validate_mesh_refine_config", {
|
|
3139
|
+
workspace: node.workspace,
|
|
3140
|
+
inlineMesh: ctx.mesh,
|
|
3141
|
+
...args.config ? { config: args.config } : {}
|
|
3142
|
+
});
|
|
3143
|
+
return JSON.stringify(result, null, 2);
|
|
3144
|
+
}
|
|
3145
|
+
async function meshSuggestRefineConfig(ctx, args) {
|
|
3146
|
+
const node = resolveRefineConfigNode(ctx, args.node_id);
|
|
3147
|
+
const result = await commandForNode(ctx, node, "suggest_mesh_refine_config", {
|
|
3148
|
+
workspace: node.workspace,
|
|
3149
|
+
inlineMesh: ctx.mesh
|
|
3150
|
+
});
|
|
3151
|
+
return JSON.stringify(result, null, 2);
|
|
3152
|
+
}
|
|
3153
|
+
async function meshRefinePlan(ctx, args) {
|
|
3154
|
+
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
3155
|
+
const result = await commandForNode(ctx, node, "plan_mesh_refine_node", {
|
|
3156
|
+
meshId: ctx.mesh.id,
|
|
3157
|
+
nodeId: args.node_id,
|
|
3158
|
+
inlineMesh: ctx.mesh
|
|
3159
|
+
});
|
|
3160
|
+
return JSON.stringify(result, null, 2);
|
|
3161
|
+
}
|
|
2366
3162
|
async function meshRefineNode(ctx, args) {
|
|
2367
3163
|
const node = await findNodeWithRefresh(ctx, args.node_id);
|
|
2368
3164
|
if (isLocalTransport(ctx.transport)) {
|
|
@@ -2371,7 +3167,7 @@ async function meshRefineNode(ctx, args) {
|
|
|
2371
3167
|
nodeId: args.node_id,
|
|
2372
3168
|
inlineMesh: ctx.mesh
|
|
2373
3169
|
});
|
|
2374
|
-
if (result?.success && result.removeResult?.removed !== false) {
|
|
3170
|
+
if (result?.success && result.async !== true && result.removeResult?.removed !== false) {
|
|
2375
3171
|
const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
|
|
2376
3172
|
if (idx >= 0) {
|
|
2377
3173
|
ctx.mesh.nodes.splice(idx, 1);
|
|
@@ -2386,7 +3182,7 @@ async function meshRefineNode(ctx, args) {
|
|
|
2386
3182
|
nodeId: args.node_id,
|
|
2387
3183
|
inlineMesh: ctx.mesh
|
|
2388
3184
|
});
|
|
2389
|
-
if (res?.success && res.removeResult?.removed !== false) {
|
|
3185
|
+
if (res?.success && res.async !== true && res.removeResult?.removed !== false) {
|
|
2390
3186
|
const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
|
|
2391
3187
|
if (idx >= 0) {
|
|
2392
3188
|
ctx.mesh.nodes.splice(idx, 1);
|
|
@@ -2423,13 +3219,13 @@ var STANDARD_TOOLS = [
|
|
|
2423
3219
|
function buildMcpHelpText() {
|
|
2424
3220
|
const meshTools = ALL_MESH_TOOLS.map((tool) => tool.name);
|
|
2425
3221
|
return `
|
|
2426
|
-
|
|
3222
|
+
ADHDev MCP Server
|
|
2427
3223
|
|
|
2428
3224
|
Usage:
|
|
2429
|
-
adhdev
|
|
2430
|
-
adhdev
|
|
2431
|
-
adhdev
|
|
2432
|
-
adhdev-mcp --
|
|
3225
|
+
adhdev mcp Local mode (requires standalone daemon)
|
|
3226
|
+
adhdev mcp --api-key <key> Cloud mode (ADHDev cloud API)
|
|
3227
|
+
adhdev mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
|
|
3228
|
+
adhdev-mcp --help Compatibility bin (same server, legacy package entrypoint)
|
|
2433
3229
|
|
|
2434
3230
|
Options:
|
|
2435
3231
|
--mode <mode> Transport: local, cloud, or ipc
|
|
@@ -2454,6 +3250,7 @@ Mesh tools: ${meshTools.join(", ")}
|
|
|
2454
3250
|
// src/server.ts
|
|
2455
3251
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
2456
3252
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
3253
|
+
var import_node_os = __toESM(require("os"));
|
|
2457
3254
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
2458
3255
|
|
|
2459
3256
|
// src/transports/local.ts
|
|
@@ -3004,6 +3801,22 @@ function formatChatResult(result, sessionId, format, limit = 50, compact = false
|
|
|
3004
3801
|
}))
|
|
3005
3802
|
}, null, 2);
|
|
3006
3803
|
}
|
|
3804
|
+
if ((format === "text" || format === void 0) && compact && compactPayload) {
|
|
3805
|
+
const lines2 = outputMessages.slice(-limit).map((m) => {
|
|
3806
|
+
const role = m.role === "user" ? "User" : m.role === "assistant" ? "Agent" : m.role;
|
|
3807
|
+
const content = messageContent(m);
|
|
3808
|
+
const truncated = content.length > 500 ? `${content.slice(0, 500)}\u2026` : content;
|
|
3809
|
+
return `[${role}] ${truncated}`;
|
|
3810
|
+
});
|
|
3811
|
+
if (compactPayload.summary) {
|
|
3812
|
+
const truncatedSummary = compactPayload.summary.length > 500 ? `${compactPayload.summary.slice(0, 500)}\u2026` : compactPayload.summary;
|
|
3813
|
+
lines2.push(`[Summary] ${truncatedSummary}`);
|
|
3814
|
+
}
|
|
3815
|
+
if (result?.pollingAdvisory) {
|
|
3816
|
+
lines2.push(`Advisory: ${result.pollingAdvisory.message}`);
|
|
3817
|
+
}
|
|
3818
|
+
return lines2.length > 0 ? lines2.join("\n\n") : "No messages in chat.";
|
|
3819
|
+
}
|
|
3007
3820
|
if (outputMessages.length === 0) {
|
|
3008
3821
|
return result?.pollingAdvisory ? `No messages in chat.
|
|
3009
3822
|
|
|
@@ -3971,6 +4784,7 @@ async function startMcpServer(opts) {
|
|
|
3971
4784
|
requirePreTaskCheckpoint: false,
|
|
3972
4785
|
requirePostTaskCheckpoint: true,
|
|
3973
4786
|
requireApprovalForPush: true,
|
|
4787
|
+
allowAutoPublishSubmoduleMainCommits: false,
|
|
3974
4788
|
requireApprovalForDestructiveGit: true,
|
|
3975
4789
|
dirtyWorkspaceBehavior: "warn",
|
|
3976
4790
|
maxParallelTasks: 2,
|
|
@@ -4027,11 +4841,13 @@ async function startMcpServer(opts) {
|
|
|
4027
4841
|
}
|
|
4028
4842
|
let localDaemonId;
|
|
4029
4843
|
let localMachineId;
|
|
4844
|
+
let coordinatorHostname = import_node_os.default.hostname();
|
|
4030
4845
|
if (transport instanceof LocalTransport || transport instanceof IpcTransport) {
|
|
4031
4846
|
try {
|
|
4032
4847
|
const { loadConfig } = await import("@adhdev/daemon-core");
|
|
4033
4848
|
const cfg = loadConfig();
|
|
4034
|
-
if (cfg.
|
|
4849
|
+
if (cfg.machineId) localMachineId = cfg.machineId;
|
|
4850
|
+
else if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
|
|
4035
4851
|
} catch {
|
|
4036
4852
|
}
|
|
4037
4853
|
}
|
|
@@ -4039,11 +4855,13 @@ async function startMcpServer(opts) {
|
|
|
4039
4855
|
try {
|
|
4040
4856
|
const statusResult = await transport.getStatus();
|
|
4041
4857
|
const instanceId = typeof statusResult?.status?.instanceId === "string" ? statusResult.status.instanceId.trim() : "";
|
|
4858
|
+
const hostname = typeof statusResult?.status?.hostname === "string" ? statusResult.status.hostname.trim() : typeof statusResult?.status?.machine?.hostname === "string" ? statusResult.status.machine.hostname.trim() : "";
|
|
4042
4859
|
if (instanceId) localDaemonId = instanceId;
|
|
4860
|
+
if (hostname) coordinatorHostname = hostname;
|
|
4043
4861
|
} catch {
|
|
4044
4862
|
}
|
|
4045
4863
|
}
|
|
4046
|
-
const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {} };
|
|
4864
|
+
const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {}, ...coordinatorHostname ? { coordinatorHostname } : {} };
|
|
4047
4865
|
const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
|
|
4048
4866
|
const server2 = new import_server.Server(
|
|
4049
4867
|
{ name: "adhdev-mcp-server", version: "0.9.81" },
|
|
@@ -4072,7 +4890,7 @@ async function startMcpServer(opts) {
|
|
|
4072
4890
|
let text;
|
|
4073
4891
|
switch (name) {
|
|
4074
4892
|
case "mesh_status":
|
|
4075
|
-
text = await meshStatus(meshCtx);
|
|
4893
|
+
text = await meshStatus(meshCtx, a);
|
|
4076
4894
|
break;
|
|
4077
4895
|
case "mesh_list_nodes":
|
|
4078
4896
|
text = await meshListNodes(meshCtx);
|
|
@@ -4104,6 +4922,9 @@ async function startMcpServer(opts) {
|
|
|
4104
4922
|
case "mesh_git_status":
|
|
4105
4923
|
text = await meshGitStatus(meshCtx, a);
|
|
4106
4924
|
break;
|
|
4925
|
+
case "mesh_fast_forward_node":
|
|
4926
|
+
text = await meshFastForwardNode(meshCtx, a);
|
|
4927
|
+
break;
|
|
4107
4928
|
case "mesh_checkpoint":
|
|
4108
4929
|
text = await meshCheckpoint(meshCtx, a);
|
|
4109
4930
|
break;
|
|
@@ -4119,6 +4940,18 @@ async function startMcpServer(opts) {
|
|
|
4119
4940
|
case "mesh_refine_node":
|
|
4120
4941
|
text = await meshRefineNode(meshCtx, a);
|
|
4121
4942
|
break;
|
|
4943
|
+
case "mesh_refine_config_schema":
|
|
4944
|
+
text = await meshRefineConfigSchema(meshCtx);
|
|
4945
|
+
break;
|
|
4946
|
+
case "mesh_validate_refine_config":
|
|
4947
|
+
text = await meshValidateRefineConfig(meshCtx, a);
|
|
4948
|
+
break;
|
|
4949
|
+
case "mesh_suggest_refine_config":
|
|
4950
|
+
text = await meshSuggestRefineConfig(meshCtx, a);
|
|
4951
|
+
break;
|
|
4952
|
+
case "mesh_refine_plan":
|
|
4953
|
+
text = await meshRefinePlan(meshCtx, a);
|
|
4954
|
+
break;
|
|
4122
4955
|
case "mesh_cleanup_sessions":
|
|
4123
4956
|
text = await meshCleanupSessions(meshCtx, a);
|
|
4124
4957
|
break;
|