@adhdev/daemon-standalone 0.9.82-rc.19 → 0.9.82-rc.191

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.
@@ -35,9 +35,128 @@ __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
+ };
53
+ var WS_CONNECTING = 0;
54
+ var WS_OPEN = 1;
55
+ var POOL_IDLE_EVICT_MS = 5 * 6e4;
56
+ var POOL_MAX_AGE_MS = 10 * 6e4;
57
+ var connectionPool = /* @__PURE__ */ new Map();
58
+ function buildRequestId() {
59
+ return `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
60
+ }
61
+ function getTimeoutMs(type, nestedCommand) {
62
+ return Math.max(
63
+ IPC_COMMAND_TIMEOUTS_MS[type] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS,
64
+ IPC_COMMAND_TIMEOUTS_MS[nestedCommand] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS
65
+ );
66
+ }
67
+ function getOrCreateConnection(WebSocketCtor, url) {
68
+ const existing = connectionPool.get(url);
69
+ if (existing) {
70
+ const { readyState } = existing.ws;
71
+ const now2 = Date.now();
72
+ const isAlive = readyState === WS_CONNECTING || readyState === WS_OPEN;
73
+ const isIdle = now2 - existing.lastUsedAt > POOL_IDLE_EVICT_MS && existing.pending.size === 0;
74
+ const isTooOld = now2 - existing.createdAt > POOL_MAX_AGE_MS && existing.pending.size === 0;
75
+ if (isAlive && !isIdle && !isTooOld) {
76
+ return existing;
77
+ }
78
+ if (isAlive && (isIdle || isTooOld)) {
79
+ try {
80
+ existing.ws.close();
81
+ } catch {
82
+ }
83
+ connectionPool.delete(url);
84
+ }
85
+ connectionPool.delete(url);
86
+ }
87
+ const now = Date.now();
88
+ const conn = {
89
+ ws: new WebSocketCtor(url),
90
+ ready: false,
91
+ commandQueue: [],
92
+ pending: /* @__PURE__ */ new Map(),
93
+ lastUsedAt: now,
94
+ createdAt: now
95
+ };
96
+ connectionPool.set(url, conn);
97
+ const drainQueue = () => {
98
+ conn.ready = true;
99
+ for (const { type, args, requestId } of conn.commandQueue) {
100
+ conn.ws.send(JSON.stringify({ type: "ext:command", payload: { command: type, args, requestId } }));
101
+ }
102
+ conn.commandQueue = [];
103
+ };
104
+ let tornDown = false;
105
+ const teardown = (error) => {
106
+ if (tornDown) return;
107
+ tornDown = true;
108
+ connectionPool.delete(url);
109
+ conn.ready = false;
110
+ for (const [, req] of conn.pending) {
111
+ clearTimeout(req.timer);
112
+ req.reject(error);
113
+ }
114
+ conn.pending.clear();
115
+ conn.commandQueue = [];
116
+ };
117
+ conn.ws.addEventListener("open", () => {
118
+ conn.ws.send(JSON.stringify({
119
+ type: "ext:register",
120
+ payload: {
121
+ ideType: "mcp-server",
122
+ ideVersion: "1.0.0",
123
+ extensionVersion: "1.0.0",
124
+ instanceId: `mcp-server-${process.pid}`,
125
+ machineId: "mcp-server",
126
+ workspaceFolders: []
127
+ }
128
+ }));
129
+ });
130
+ conn.ws.addEventListener("message", (event) => {
131
+ try {
132
+ const raw = typeof event.data === "string" ? event.data : String(event.data);
133
+ const msg = JSON.parse(raw);
134
+ if (msg?.type === "daemon:welcome") {
135
+ drainQueue();
136
+ return;
137
+ }
138
+ if (msg?.type !== "ext:command_result") return;
139
+ const req = conn.pending.get(msg?.payload?.requestId);
140
+ if (!req) return;
141
+ conn.pending.delete(msg.payload.requestId);
142
+ clearTimeout(req.timer);
143
+ const payload = msg.payload;
144
+ if (payload?.success === false) {
145
+ req.reject(new Error(payload.error || "Daemon IPC command failed"));
146
+ } else {
147
+ req.resolve(payload?.result ?? payload);
148
+ }
149
+ } catch {
150
+ }
151
+ });
152
+ conn.ws.addEventListener("error", () => {
153
+ teardown(new Error(`Cannot connect to daemon IPC at ${url}`));
154
+ });
155
+ conn.ws.addEventListener("close", () => {
156
+ teardown(new Error(`Daemon IPC connection closed: ${url}`));
157
+ });
158
+ return conn;
159
+ }
41
160
  var IpcTransport = class {
42
161
  port;
43
162
  path;
@@ -66,73 +185,41 @@ var IpcTransport = class {
66
185
  args
67
186
  });
68
187
  }
69
- async sendIpcCommand(type, args) {
188
+ sendIpcCommand(type, args) {
70
189
  const WebSocketCtor = globalThis.WebSocket;
71
190
  if (!WebSocketCtor) {
72
- throw new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode");
191
+ return Promise.reject(new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode"));
73
192
  }
193
+ const requestId = buildRequestId();
194
+ const nestedCommand = typeof args?.command === "string" ? args.command : "";
195
+ const timeoutMs = getTimeoutMs(type, nestedCommand);
196
+ const targetDaemonId = typeof args?.targetDaemonId === "string" ? args.targetDaemonId : "";
197
+ const diagnosticParts = [
198
+ `command='${type}'`,
199
+ ...nestedCommand ? [`relayedCommand='${nestedCommand}'`] : [],
200
+ ...targetDaemonId ? [`targetDaemonId='${targetDaemonId.slice(0, 12)}'`] : [],
201
+ ...typeof args?.nodeId === "string" ? [`nodeId='${args.nodeId}'`] : [],
202
+ ...typeof args?.workspace === "string" ? [`workspace='${args.workspace}'`] : []
203
+ ];
204
+ const url = `ws://127.0.0.1:${this.port}${this.path}`;
74
205
  return new Promise((resolve, reject) => {
75
- const requestId = `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
76
- const ws = new WebSocketCtor(`ws://127.0.0.1:${this.port}${this.path}`);
77
- let settled = false;
78
- const finish = (fn) => {
79
- if (settled) return;
80
- settled = true;
81
- clearTimeout(timeout);
82
- try {
83
- ws.close();
84
- } catch {
85
- }
86
- fn();
87
- };
88
- const timeoutMs = type === "mesh_relay_command" ? 6e4 : 15e3;
89
- const timeout = setTimeout(() => {
90
- finish(() => reject(new Error(`Daemon IPC command '${type}' timed out after ${Math.round(timeoutMs / 1e3)}s`)));
206
+ let conn;
207
+ try {
208
+ conn = getOrCreateConnection(WebSocketCtor, url);
209
+ } catch (e) {
210
+ return reject(new Error(`Failed to create IPC connection: ${e?.message || e}`));
211
+ }
212
+ const timer = setTimeout(() => {
213
+ conn.pending.delete(requestId);
214
+ reject(new Error(`Daemon IPC ${diagnosticParts.join(" ")} timed out after ${Math.round(timeoutMs / 1e3)}s (requestId=${requestId})`));
91
215
  }, timeoutMs);
92
- let commandSent = false;
93
- const send = () => {
94
- if (commandSent) return;
95
- commandSent = true;
96
- ws.send(JSON.stringify({
97
- type: "ext:command",
98
- payload: { command: type, args, requestId }
99
- }));
100
- };
101
- ws.addEventListener("open", () => {
102
- ws.send(JSON.stringify({
103
- type: "ext:register",
104
- payload: {
105
- ideType: "mcp-server",
106
- ideVersion: "1.0.0",
107
- extensionVersion: "1.0.0",
108
- instanceId: `mcp-server-${process.pid}`,
109
- machineId: "mcp-server",
110
- workspaceFolders: []
111
- }
112
- }));
113
- });
114
- ws.addEventListener("message", (event) => {
115
- try {
116
- const raw = typeof event.data === "string" ? event.data : String(event.data);
117
- const msg = JSON.parse(raw);
118
- if (msg?.type === "daemon:welcome") {
119
- send();
120
- return;
121
- }
122
- if (msg?.type !== "ext:command_result") return;
123
- if (msg?.payload?.requestId !== requestId) return;
124
- const payload = msg.payload;
125
- if (payload?.success === false) {
126
- finish(() => reject(new Error(payload.error || `Daemon IPC command '${type}' failed`)));
127
- return;
128
- }
129
- finish(() => resolve(payload?.result ?? payload));
130
- } catch {
131
- }
132
- });
133
- ws.addEventListener("error", () => {
134
- finish(() => reject(new Error(`Cannot connect to daemon IPC at ws://127.0.0.1:${this.port}${this.path}`)));
135
- });
216
+ conn.pending.set(requestId, { resolve, reject, timer });
217
+ conn.lastUsedAt = Date.now();
218
+ if (conn.ready) {
219
+ conn.ws.send(JSON.stringify({ type: "ext:command", payload: { command: type, args, requestId } }));
220
+ } else {
221
+ conn.commandQueue.push({ type, args, requestId });
222
+ }
136
223
  });
137
224
  }
138
225
  };
@@ -143,10 +230,6 @@ function isLocalTransport(transport) {
143
230
  }
144
231
 
145
232
  // src/tools/chat-compact.ts
146
- function isAssistantLike(message) {
147
- const role = String(message?.role ?? "").toLowerCase();
148
- return role === "assistant" || role === "agent";
149
- }
150
233
  function messageContent(message) {
151
234
  const content = message?.content;
152
235
  if (typeof content === "string") return content;
@@ -166,10 +249,7 @@ function isCoordinatorVisibleMessage(message) {
166
249
  return role === "user" || role === "assistant" || role === "agent";
167
250
  }
168
251
  function buildCompactMessageTail(visibleMessages, opts) {
169
- const summary = typeof opts.summary === "string" ? opts.summary.trim() : "";
170
- const shouldOmitSummaryMessage = !!summary && !!opts.finalAssistant && isAssistantLike(opts.finalAssistant) && messageContent(opts.finalAssistant).trim() === summary;
171
- const sourceMessages = shouldOmitSummaryMessage ? visibleMessages.filter((message) => message !== opts.finalAssistant) : visibleMessages;
172
- return sourceMessages.slice(-opts.limit);
252
+ return visibleMessages.slice(-opts.limit);
173
253
  }
174
254
  function compactChatPayload(payload, opts = {}) {
175
255
  const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
@@ -241,17 +321,64 @@ function annotateRapidReadChatAdvisory(payload, options) {
241
321
 
242
322
  // src/tools/mesh-tools.ts
243
323
  var import_daemon_core = require("@adhdev/daemon-core");
324
+ var SESSION_PROVIDER_METADATA_TTL_MS = 30 * 6e4;
244
325
  var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
326
+ function getSessionMetadata(key) {
327
+ const entry = meshSessionProviderMetadata.get(key);
328
+ if (!entry) return void 0;
329
+ if (entry.expiresAt <= Date.now()) {
330
+ meshSessionProviderMetadata.delete(key);
331
+ return void 0;
332
+ }
333
+ return entry;
334
+ }
335
+ var ACTIVE_WORK_POLLING_BACKOFF_MS = 6e4;
336
+ function buildActiveWorkPollingGuidance(summary, now = Date.now()) {
337
+ if (!summary || summary.generatingCount <= 0) return void 0;
338
+ return {
339
+ activeGeneratingWork: true,
340
+ generatingCount: summary.generatingCount,
341
+ doNotPollBefore: new Date(now + ACTIVE_WORK_POLLING_BACKOFF_MS).toISOString(),
342
+ eventSurface: "pendingCoordinatorEvents",
343
+ nextRecommendedAction: "Wait for pendingCoordinatorEvents/completion events or an explicit user status request. If no terminal evidence appears and the user asks for status, make one bounded status check, then wait again.",
344
+ message: "Do not repeatedly poll mesh_status/mesh_view_queue/mesh_read_chat while delegated work is generating; terminal ledger or completion evidence will be surfaced through pendingCoordinatorEvents when available."
345
+ };
346
+ }
245
347
  function readString(value) {
246
348
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
247
349
  }
350
+ function summarizeTaskMessage(message) {
351
+ const taskSummary = message.replace(/\s+/g, " ").trim();
352
+ const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
353
+ return { taskTitle: taskTitle || "(untitled task)", taskSummary };
354
+ }
355
+ function buildDirectTaskPayload(message, via, opts) {
356
+ const descriptor = summarizeTaskMessage(message);
357
+ return {
358
+ source: "direct",
359
+ via,
360
+ taskId: opts.taskId,
361
+ message,
362
+ taskTitle: descriptor.taskTitle,
363
+ taskSummary: descriptor.taskSummary,
364
+ ...opts.taskMode ? { taskMode: opts.taskMode } : {},
365
+ ...opts.providerType ? { providerType: opts.providerType } : {},
366
+ ...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {},
367
+ ...opts.dispatchedToIdleSession !== void 0 ? { dispatchedToIdleSession: opts.dispatchedToIdleSession } : {}
368
+ };
369
+ }
370
+ function findNode(mesh, nodeId) {
371
+ const node = mesh.nodes.find((n) => n.id === nodeId);
372
+ if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
373
+ return node;
374
+ }
248
375
  var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
249
376
  var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
250
377
  var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
251
378
  var ACTIVE_QUEUE_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned"]);
252
379
  var HISTORICAL_QUEUE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
253
380
  async function refreshMeshFromDaemon(ctx) {
254
- if (!(ctx.transport instanceof IpcTransport)) return;
381
+ if (!isLocalTransport(ctx.transport)) return;
255
382
  try {
256
383
  const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
257
384
  if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
@@ -404,6 +531,33 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
404
531
  function readSessionRecordId(session) {
405
532
  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);
406
533
  }
534
+ function extractStatusMetadataSessions(value) {
535
+ const payload = unwrapCommandPayload(value);
536
+ const status = payload?.status && typeof payload.status === "object" ? payload.status : payload;
537
+ return Array.isArray(status?.sessions) ? status.sessions : [];
538
+ }
539
+ function resolveSessionProviderType(session) {
540
+ return readString(session?.providerType) || readString(session?.cliType) || readString(session?.agentType) || "";
541
+ }
542
+ function isMeshCoordinatorSessionRecord(session) {
543
+ return Boolean(
544
+ readString(session?.settings?.meshCoordinatorFor) || readString(session?.meta?.meshCoordinatorFor) || readString(session?.metadata?.meshCoordinatorFor) || readString(session?.meshCoordinatorFor)
545
+ );
546
+ }
547
+ function isUnmanagedSessionRecord(session) {
548
+ const hasMeshNodeFor = Boolean(
549
+ readString(session?.settings?.meshNodeFor) || readString(session?.meta?.meshNodeFor) || readString(session?.metadata?.meshNodeFor) || readString(session?.meshNodeFor)
550
+ );
551
+ if (hasMeshNodeFor) return false;
552
+ if (isMeshCoordinatorSessionRecord(session)) return false;
553
+ const launchedByCoordinator = Boolean(
554
+ session?.settings?.launchedByCoordinator === true || session?.meta?.launchedByCoordinator === true || session?.launchedByCoordinator === true
555
+ );
556
+ return !launchedByCoordinator;
557
+ }
558
+ function isWorkerTaskMode(taskMode) {
559
+ return taskMode !== "live_debug_readonly";
560
+ }
407
561
  function addSessionRecord(target, session) {
408
562
  if (!session || typeof session !== "object" || isTerminalSessionRecord(session)) return;
409
563
  const sessionId = readSessionRecordId(session);
@@ -472,18 +626,26 @@ function queueAssignmentStaleReason(task, liveness) {
472
626
  }
473
627
  function buildQueueStatusSummary(queue) {
474
628
  const counts = { pending: 0, assigned: 0, completed: 0, failed: 0, cancelled: 0 };
629
+ let staleAssigned = 0;
475
630
  for (const task of queue) {
476
631
  const status = typeof task?.status === "string" ? task.status : void 0;
477
632
  if (status && Object.prototype.hasOwnProperty.call(counts, status)) {
478
633
  counts[status] += 1;
479
634
  }
635
+ if (status === "assigned" && task?.staleAssigned === true) staleAssigned += 1;
480
636
  }
637
+ const liveAssigned = Math.max(0, counts.assigned - staleAssigned);
481
638
  return {
482
639
  totalCount: queue.length,
483
- activeCount: counts.pending + counts.assigned,
640
+ activeCount: counts.pending + liveAssigned,
484
641
  historicalCount: counts.completed + counts.failed + counts.cancelled,
485
642
  counts,
486
643
  activeCounts: {
644
+ pending: counts.pending,
645
+ assigned: liveAssigned
646
+ },
647
+ staleAssignedCount: staleAssigned,
648
+ rawActiveCounts: {
487
649
  pending: counts.pending,
488
650
  assigned: counts.assigned
489
651
  },
@@ -511,6 +673,18 @@ function filterQueueForView(queue, view, statuses) {
511
673
  if (view === "historical") return queue.filter((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
512
674
  return queue;
513
675
  }
676
+ function prioritizeActiveQueueRows(queue) {
677
+ const active = [];
678
+ const historical = [];
679
+ const other = [];
680
+ for (const task of queue) {
681
+ const status = String(task?.status || "");
682
+ if (ACTIVE_QUEUE_STATUSES.has(status)) active.push(task);
683
+ else if (HISTORICAL_QUEUE_STATUSES.has(status)) historical.push(task);
684
+ else other.push(task);
685
+ }
686
+ return [...active, ...other, ...historical];
687
+ }
514
688
  function slimQueueTask(task) {
515
689
  return {
516
690
  id: task?.id,
@@ -604,6 +778,172 @@ function unwrapCommandPayload(value) {
604
778
  }
605
779
  return current;
606
780
  }
781
+ function isDirectDispatchLedgerEntry(entry) {
782
+ if (entry?.kind !== "task_dispatched") return false;
783
+ const payload = entry.payload || {};
784
+ const via = readString(payload.via);
785
+ return payload.source === "direct" || via === "p2p_direct" || via === "local_direct" || via === "mesh_send_task";
786
+ }
787
+ function readMessageTimestampIso(message) {
788
+ for (const value of [message?.timestamp, message?.createdAt, message?.created_at, message?.updatedAt, message?.time]) {
789
+ if (typeof value === "number" && Number.isFinite(value)) {
790
+ const ms = value > 1e10 ? value : value * 1e3;
791
+ return new Date(ms).toISOString();
792
+ }
793
+ if (typeof value === "string" && value.trim()) {
794
+ const ms = new Date(value.trim()).getTime();
795
+ if (Number.isFinite(ms)) return new Date(ms).toISOString();
796
+ }
797
+ }
798
+ return void 0;
799
+ }
800
+ function readFinalAssistantTranscriptEvidence(payload) {
801
+ const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
802
+ const finalAssistant = [...rawMessages].reverse().filter(isCoordinatorVisibleMessage).find((message) => {
803
+ const role = String(message?.role ?? "").toLowerCase();
804
+ return (role === "assistant" || role === "agent") && messageContent(message).trim();
805
+ });
806
+ const finalSummary = messageContent(finalAssistant).trim() || (typeof payload?.summary === "string" && payload.summary.trim() ? payload.summary.trim() : void 0);
807
+ return {
808
+ finalSummary,
809
+ transcriptMessageAt: finalAssistant ? readMessageTimestampIso(finalAssistant) : void 0
810
+ };
811
+ }
812
+ function findNodeSession(nodes, nodeId, sessionId) {
813
+ if (!nodeId || !sessionId) return {};
814
+ const node = nodes.find((candidate) => readString(candidate?.id) === nodeId || readString(candidate?.nodeId) === nodeId);
815
+ if (!node) return {};
816
+ const sessions = Array.isArray(node.sessions) ? node.sessions : [];
817
+ const session = sessions.find((candidate) => readSessionRecordId(candidate) === sessionId);
818
+ return { node, session };
819
+ }
820
+ function buildDirectDispatchReconciliationCandidates(directDispatches, ledgerEntries) {
821
+ const candidates = [];
822
+ const seenTaskIds = /* @__PURE__ */ new Set();
823
+ for (const dispatch of directDispatches || []) {
824
+ const taskId = readString(dispatch?.taskId);
825
+ if (!taskId || seenTaskIds.has(taskId)) continue;
826
+ seenTaskIds.add(taskId);
827
+ candidates.push(dispatch);
828
+ }
829
+ for (const entry of ledgerEntries || []) {
830
+ if (!isDirectDispatchLedgerEntry(entry)) continue;
831
+ const taskId = readString(entry.payload?.taskId);
832
+ if (!taskId || seenTaskIds.has(taskId)) continue;
833
+ seenTaskIds.add(taskId);
834
+ candidates.push({
835
+ taskId,
836
+ nodeId: entry.nodeId,
837
+ sessionId: entry.sessionId,
838
+ providerType: entry.providerType || readString(entry.payload?.providerType),
839
+ message: readString(entry.payload?.message),
840
+ dispatchedAt: entry.timestamp,
841
+ via: readString(entry.payload?.via)
842
+ });
843
+ }
844
+ return candidates;
845
+ }
846
+ async function reconcileDirectDispatchesFromTranscriptEvidence(ctx, liveNodes, directDispatches, ledgerEntries) {
847
+ let attempted = 0;
848
+ let reconciled = 0;
849
+ let skipped = 0;
850
+ const candidates = buildDirectDispatchReconciliationCandidates(directDispatches, ledgerEntries);
851
+ for (const dispatch of candidates) {
852
+ const taskId = readString(dispatch?.taskId);
853
+ const nodeId = readString(dispatch?.nodeId);
854
+ const sessionId = readString(dispatch?.sessionId);
855
+ if (!taskId || !nodeId || !sessionId) {
856
+ skipped += 1;
857
+ continue;
858
+ }
859
+ const { session } = findNodeSession(liveNodes, nodeId, sessionId);
860
+ if (!session || !isIdleSessionRecord(session)) {
861
+ skipped += 1;
862
+ continue;
863
+ }
864
+ const node = await findOptionalNodeWithRefresh(ctx, nodeId).catch(() => null);
865
+ if (!node) {
866
+ skipped += 1;
867
+ continue;
868
+ }
869
+ const providerType = readString(dispatch?.providerType) || resolveSessionProviderType(session);
870
+ const providerSessionId = readString(session?.providerSessionId) || readString(session?.activeChat?.providerSessionId) || readString(session?.settings?.providerSessionId) || resolveMeshSessionProviderMetadata(ctx, nodeId, sessionId)?.providerSessionId;
871
+ attempted += 1;
872
+ try {
873
+ const readResult = await commandForNode(ctx, node, "read_chat", {
874
+ sessionId,
875
+ targetSessionId: sessionId,
876
+ workspace: node.workspace,
877
+ ...providerType ? { agentType: providerType, providerType } : {},
878
+ ...providerSessionId ? { providerSessionId } : {},
879
+ tailLimit: 10
880
+ });
881
+ const payload = unwrapCommandPayload(readResult);
882
+ if (payload?.success === false) continue;
883
+ const evidence = readFinalAssistantTranscriptEvidence(payload);
884
+ if (!evidence.finalSummary) continue;
885
+ const result = (0, import_daemon_core.reconcileDirectDispatchCompletionFromTranscript)({
886
+ meshId: ctx.mesh.id,
887
+ nodeId,
888
+ sessionId,
889
+ providerType,
890
+ providerSessionId: readString(payload?.providerSessionId) || providerSessionId,
891
+ taskId,
892
+ finalSummary: evidence.finalSummary,
893
+ transcriptMessageAt: evidence.transcriptMessageAt,
894
+ targetCoordinatorDaemonId: ctx.localDaemonId,
895
+ source: "mcp_mesh_status_transcript_reconciliation"
896
+ });
897
+ if (result.reconciled) reconciled += 1;
898
+ } catch {
899
+ skipped += 1;
900
+ }
901
+ }
902
+ return { attempted, reconciled, skipped };
903
+ }
904
+ async function triggerMeshQueueAndReport(ctx, node, opts) {
905
+ if (!(isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport)) return void 0;
906
+ try {
907
+ let raw;
908
+ if (ctx.transport instanceof IpcTransport && node?.daemonId && opts?.localNode === false) {
909
+ raw = await ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id });
910
+ } else if (isLocalTransport(ctx.transport)) {
911
+ raw = await ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id });
912
+ } else {
913
+ return void 0;
914
+ }
915
+ const payload = unwrapCommandPayload(raw);
916
+ const trigger = payload?.trigger && typeof payload.trigger === "object" ? payload.trigger : payload;
917
+ return trigger && typeof trigger === "object" ? trigger : { success: true };
918
+ } catch (e) {
919
+ return {
920
+ success: false,
921
+ error: e?.message || String(e)
922
+ };
923
+ }
924
+ }
925
+ function buildQueueTriggerGuidance(queueTrigger) {
926
+ if (!queueTrigger || queueTrigger.claimed === true) return void 0;
927
+ if (queueTrigger.success === false) {
928
+ return {
929
+ queueClaimed: false,
930
+ queueDispatchState: "trigger_failed",
931
+ nextAction: "Do not assume the queued task is running. Check mesh_view_queue and daemon connectivity before redispatching."
932
+ };
933
+ }
934
+ if (queueTrigger.noIdleMeshSessionAvailable === true) {
935
+ return {
936
+ queueClaimed: false,
937
+ queueDispatchState: "pending_no_idle_mesh_session",
938
+ nextAction: "The task is queued but not running. Launch a managed worker with mesh_launch_session, or wait for a delegated session to become ready and trigger the queue again."
939
+ };
940
+ }
941
+ return {
942
+ queueClaimed: false,
943
+ queueDispatchState: "pending_or_waiting_for_ready",
944
+ nextAction: "The task is queued but this trigger did not claim it. Use mesh_view_queue for the current active-work source of truth before retrying."
945
+ };
946
+ }
607
947
  function isTerminalSessionRecord(session) {
608
948
  const status = typeof session?.status === "string" ? session.status.toLowerCase() : "";
609
949
  const lifecycle = typeof session?.lifecycle === "string" ? session.lifecycle.toLowerCase() : "";
@@ -616,22 +956,67 @@ function isIdleSessionRecord(session) {
616
956
  const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
617
957
  return status === "idle" || chatStatus === "waiting_input";
618
958
  }
959
+ function isMeshOwnedDelegateSession(session, meshId, nodeId) {
960
+ const settings = session?.settings;
961
+ const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
962
+ const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
963
+ if (sessionMeshId !== meshId) return false;
964
+ return !sessionNodeId || sessionNodeId === nodeId;
965
+ }
966
+ function hasRemoteRelayMetadata(session) {
967
+ return Boolean(
968
+ readString(session?.settings?.meshCoordinatorDaemonId) || readString(session?.meta?.meshCoordinatorDaemonId) || readString(session?.metadata?.meshCoordinatorDaemonId) || readString(session?.meshCoordinatorDaemonId)
969
+ );
970
+ }
971
+ function isRelaySafeRemoteDelegateSession(session, meshId, nodeId) {
972
+ return isMeshOwnedDelegateSession(session, meshId, nodeId) && hasRemoteRelayMetadata(session);
973
+ }
619
974
  function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
620
975
  const live = sessions.filter((session) => !isTerminalSessionRecord(session));
621
976
  const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
622
- const isMeshOwnedDelegateSession = (session) => {
623
- const settings = session?.settings;
624
- const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
625
- const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
626
- const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
627
- if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
628
- return !sessionNodeId || sessionNodeId === nodeId;
629
- };
630
977
  const meshSessions = live.filter(
631
- (session) => isMeshOwnedDelegateSession(session)
978
+ (session) => isRelaySafeRemoteDelegateSession(session, meshId, nodeId)
632
979
  );
633
980
  return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || void 0;
634
981
  }
982
+ function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
983
+ return {
984
+ success: false,
985
+ recoverable: true,
986
+ code: "mesh_delegate_session_missing_relay_metadata",
987
+ reason: "mesh_delegate_session_missing_relay_metadata",
988
+ transport: "mesh_transport",
989
+ retryRecommended: true,
990
+ meshId: ctx.mesh.id,
991
+ nodeId: node.id,
992
+ daemonId: node.daemonId,
993
+ workspace: node.workspace,
994
+ sessionId,
995
+ unsafeTranscriptAlias: true,
996
+ ...providerType ? { resolvedProviderType: providerType } : {},
997
+ 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. This session may be the coordinator itself or an unrelated session (unsafe_transcript_alias risk).`,
998
+ 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.`,
999
+ noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
1000
+ };
1001
+ }
1002
+ function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
1003
+ return {
1004
+ success: false,
1005
+ recoverable: true,
1006
+ code: "mesh_coordinator_daemon_unknown",
1007
+ reason: "mesh_coordinator_daemon_unknown",
1008
+ transport: "mesh_transport",
1009
+ retryRecommended: true,
1010
+ meshId: ctx.mesh.id,
1011
+ nodeId: node.id,
1012
+ daemonId: node.daemonId,
1013
+ workspace: node.workspace,
1014
+ ...providerType ? { resolvedProviderType: providerType } : {},
1015
+ 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.`,
1016
+ 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.",
1017
+ noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
1018
+ };
1019
+ }
635
1020
  function findNestedPayload(value, predicate) {
636
1021
  const seen = /* @__PURE__ */ new Set();
637
1022
  const stack = [{ payload: value, depth: 0 }];
@@ -659,12 +1044,16 @@ function extractGitDiff(value) {
659
1044
  }
660
1045
  function extractSubmodules(value, ignorePaths) {
661
1046
  const payload = unwrapCommandPayload(value);
662
- const subs = payload?.submodules ?? value?.submodules;
1047
+ const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
663
1048
  if (!Array.isArray(subs)) return void 0;
664
1049
  if (ignorePaths.length === 0) return subs;
665
1050
  const ignoreSet = new Set(ignorePaths);
666
1051
  return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
667
1052
  }
1053
+ function assignFullGitSnapshot(entry, status) {
1054
+ if (!status || typeof status !== "object" || Array.isArray(status)) return;
1055
+ entry.git = status;
1056
+ }
668
1057
  function extractLaunchPayload(value) {
669
1058
  return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
670
1059
  }
@@ -789,20 +1178,76 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
789
1178
  let sessionId = args.session_id?.trim() || "";
790
1179
  const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
791
1180
  let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
792
- if (!sessionId) {
1181
+ if (sessionId && args.verifiedSession) {
1182
+ const explicitSession = args.verifiedSession;
1183
+ if (!isRelaySafeRemoteDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
1184
+ return buildRelayUnsafeRemoteSessionFailure(
1185
+ ctx,
1186
+ node,
1187
+ sessionId,
1188
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1189
+ );
1190
+ }
1191
+ if (!resolvedProviderType) {
1192
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
1193
+ }
1194
+ } else if (!sessionId || args.session_id) {
793
1195
  try {
794
1196
  const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
795
- const innerResult = relayResult?.result ?? relayResult;
796
- const statusObj = innerResult?.status ?? innerResult;
797
- const sessions = Array.isArray(statusObj?.sessions) ? statusObj.sessions : [];
798
- const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
799
- if (targetSession?.id || targetSession?.sessionId) {
800
- sessionId = targetSession.id || targetSession.sessionId;
1197
+ const sessions = extractStatusMetadataSessions(relayResult);
1198
+ if (sessionId) {
1199
+ const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
1200
+ if (!explicitSession) {
1201
+ return {
1202
+ success: false,
1203
+ recoverable: true,
1204
+ code: "mesh_target_session_not_found",
1205
+ reason: "mesh_target_session_not_found",
1206
+ transport: "mesh_transport",
1207
+ retryRecommended: true,
1208
+ meshId: ctx.mesh.id,
1209
+ nodeId: node.id,
1210
+ daemonId,
1211
+ workspace: node.workspace,
1212
+ sessionId,
1213
+ ...resolvedProviderType ? { resolvedProviderType } : {},
1214
+ error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
1215
+ 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.`
1216
+ };
1217
+ }
1218
+ if (!isRelaySafeRemoteDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
1219
+ return buildRelayUnsafeRemoteSessionFailure(
1220
+ ctx,
1221
+ node,
1222
+ sessionId,
1223
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1224
+ );
1225
+ }
801
1226
  if (!resolvedProviderType) {
802
- resolvedProviderType = targetSession.providerType || targetSession.cliType || "";
1227
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
1228
+ }
1229
+ } else {
1230
+ const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
1231
+ if (targetSession?.id || targetSession?.sessionId) {
1232
+ sessionId = targetSession.id || targetSession.sessionId;
1233
+ if (!resolvedProviderType) {
1234
+ resolvedProviderType = resolveSessionProviderType(targetSession);
1235
+ }
803
1236
  }
804
1237
  }
805
1238
  } catch (e) {
1239
+ if (sessionId) {
1240
+ return {
1241
+ ...buildCoordinatorP2pRelayFailure(e, {
1242
+ command: "get_status_metadata",
1243
+ targetDaemonId: daemonId,
1244
+ nodeId: node.id,
1245
+ sessionId
1246
+ }),
1247
+ success: false,
1248
+ error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
1249
+ };
1250
+ }
806
1251
  }
807
1252
  }
808
1253
  if (!resolvedProviderType) {
@@ -814,7 +1259,8 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
814
1259
  agentType: resolvedProviderType,
815
1260
  cliType: resolvedProviderType,
816
1261
  action: "send_chat",
817
- message: args.message
1262
+ message: args.message,
1263
+ ...args.meshContext ? { meshContext: args.meshContext } : {}
818
1264
  });
819
1265
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
820
1266
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
@@ -832,7 +1278,7 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
832
1278
  error: `P2P dispatch failed: ${errorMessage}`
833
1279
  };
834
1280
  }
835
- return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
1281
+ return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType, providerType: resolvedProviderType };
836
1282
  } catch (e) {
837
1283
  const errorMessage = e?.message || String(e);
838
1284
  return {
@@ -862,34 +1308,197 @@ function resolveCoordinatorNode(ctx) {
862
1308
  return void 0;
863
1309
  }
864
1310
  function readNodeMachineId(node) {
865
- return readString(node.machineId) || readString(node.machine_id);
1311
+ 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);
866
1312
  }
867
1313
  function readNodeDaemonId(node) {
868
- return readString(node.daemonId) || readString(node.daemon_id);
1314
+ 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);
1315
+ }
1316
+ function normalizeHostname(value) {
1317
+ const hostname = readString(value);
1318
+ if (!hostname) return void 0;
1319
+ return hostname.toLowerCase().replace(/\.$/, "");
1320
+ }
1321
+ function readNodeHostname(node) {
1322
+ 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);
1323
+ }
1324
+ function readNodeDisplayMachineName(node) {
1325
+ 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);
1326
+ }
1327
+ function compactIdentityEvidence(value) {
1328
+ if (!value) return void 0;
1329
+ return value.length > 24 ? `${value.slice(0, 12)}\u2026${value.slice(-8)}` : value;
1330
+ }
1331
+ function pushIdentityEvidence(evidence, label, value) {
1332
+ const compact = compactIdentityEvidence(value);
1333
+ if (compact) evidence.push(`${label}:${compact}`);
1334
+ }
1335
+ function buildNodeMachineIdentity(ctx, node) {
1336
+ const machineId = readNodeMachineId(node);
1337
+ const daemonId = readNodeDaemonId(node);
1338
+ const hostname = readNodeHostname(node);
1339
+ const machineName = readNodeDisplayMachineName(node);
1340
+ const coordinatorHostname = readString(ctx.coordinatorHostname);
1341
+ const localControlPlaneReason = getLocalControlPlaneMatchReason(ctx, node);
1342
+ const directLocal = !!localControlPlaneReason;
1343
+ const hostnameMatches = Boolean(
1344
+ normalizeHostname(hostname) && normalizeHostname(coordinatorHostname) && normalizeHostname(hostname) === normalizeHostname(coordinatorHostname)
1345
+ );
1346
+ const sameMachine = directLocal || hostnameMatches;
1347
+ const evidence = [];
1348
+ pushIdentityEvidence(evidence, "machineName", machineName);
1349
+ pushIdentityEvidence(evidence, "hostname", hostname);
1350
+ pushIdentityEvidence(evidence, "machineId", machineId);
1351
+ pushIdentityEvidence(evidence, "daemonId", daemonId);
1352
+ if (localControlPlaneReason) {
1353
+ pushIdentityEvidence(evidence, "localMatch", localControlPlaneReason);
1354
+ pushIdentityEvidence(evidence, "localMachineId", ctx.localMachineId);
1355
+ pushIdentityEvidence(evidence, "localDaemonId", ctx.localDaemonId);
1356
+ }
1357
+ const locality = sameMachine ? "same_machine" : evidence.length > 0 ? "remote_known" : "remote_or_unknown";
1358
+ 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";
1359
+ return {
1360
+ daemonId,
1361
+ machineId,
1362
+ hostname,
1363
+ machineName,
1364
+ displayName: machineName || hostname || daemonId || machineId,
1365
+ coordinatorHostname,
1366
+ sameMachine,
1367
+ locality,
1368
+ localityReason,
1369
+ identityEvidence: evidence
1370
+ };
1371
+ }
1372
+ function nodeHasLocalDaemonEvidence(ctx, node) {
1373
+ const isLocal = (session) => {
1374
+ if (!session || typeof session !== "object") return false;
1375
+ if (ctx.localDaemonId && session.runtime?.owner === ctx.localDaemonId) return true;
1376
+ if (ctx.localDaemonId && session.daemonClient?.daemonId === ctx.localDaemonId) return true;
1377
+ return false;
1378
+ };
1379
+ const sessionArrays = [
1380
+ node?.sessions,
1381
+ node?.activeSessions,
1382
+ node?.active_sessions,
1383
+ node?.lastProbe?.sessions,
1384
+ node?.last_probe?.sessions,
1385
+ node?.lastProbe?.status?.sessions,
1386
+ node?.last_probe?.status?.sessions
1387
+ ];
1388
+ for (const arr of sessionArrays) {
1389
+ if (Array.isArray(arr) && arr.some(isLocal)) return true;
1390
+ }
1391
+ const sessionRecords = [
1392
+ node?.activeSession,
1393
+ node?.active_session,
1394
+ node?.currentSession,
1395
+ node?.current_session,
1396
+ node?.runtimeSession,
1397
+ node?.runtime_session,
1398
+ node?.session,
1399
+ node?.lastProbe?.activeSession,
1400
+ node?.last_probe?.active_session,
1401
+ node?.lastProbe?.currentSession,
1402
+ node?.last_probe?.current_session,
1403
+ node?.lastProbe?.session,
1404
+ node?.last_probe?.session
1405
+ ];
1406
+ for (const session of sessionRecords) {
1407
+ if (isLocal(session)) return true;
1408
+ }
1409
+ return false;
869
1410
  }
870
1411
  function isDirectLocalNode(ctx, node) {
871
1412
  const machineId = readNodeMachineId(node);
872
1413
  const daemonId = readNodeDaemonId(node);
873
1414
  return Boolean(
874
- ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId
1415
+ ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId || nodeHasLocalDaemonEvidence(ctx, node)
875
1416
  );
876
1417
  }
1418
+ function isConfiguredCoordinatorNode(ctx, node) {
1419
+ if (!ctx.localMachineId && !ctx.localDaemonId) return false;
1420
+ const nodeId = readString(node.id) || readString(node.nodeId) || readString(node.node_id);
1421
+ if (!nodeId) return false;
1422
+ const nodeDaemonId = readNodeDaemonId(node);
1423
+ const nodeMachineId = readNodeMachineId(node);
1424
+ if (nodeDaemonId && ctx.localDaemonId && nodeDaemonId !== ctx.localDaemonId) return false;
1425
+ if (nodeMachineId && ctx.localMachineId && nodeMachineId !== ctx.localMachineId) return false;
1426
+ const preferredNodeId = readString(ctx.mesh.coordinator?.preferredNodeId) || readString(ctx.mesh.coordinator?.preferred_node_id);
1427
+ if (preferredNodeId) return nodeId === preferredNodeId;
1428
+ const first = ctx.mesh.nodes?.[0];
1429
+ const firstNodeId = readString(first?.id) || readString(first?.nodeId) || readString(first?.node_id);
1430
+ return !!firstNodeId && nodeId === firstNodeId;
1431
+ }
1432
+ function getLocalControlPlaneMatchReason(ctx, node) {
1433
+ if (isDirectLocalNode(ctx, node)) return "matched coordinator daemon or machine id";
1434
+ if (isConfiguredCoordinatorNode(ctx, node)) return "matched configured coordinator node";
1435
+ if (node.isLocalWorktree === true) {
1436
+ const sourceNode = findClonedFromNode(ctx, node);
1437
+ if (sourceNode && isDirectLocalNode(ctx, sourceNode)) return "matched local cloned-from node";
1438
+ if (sourceNode && isConfiguredCoordinatorNode(ctx, sourceNode)) return "matched configured coordinator source node";
1439
+ }
1440
+ return void 0;
1441
+ }
877
1442
  function findClonedFromNode(ctx, node) {
878
1443
  const clonedFromNodeId = readString(node.clonedFromNodeId) || readString(node.cloned_from_node_id);
879
1444
  if (!clonedFromNodeId) return void 0;
880
1445
  return ctx.mesh.nodes.find((n) => n.id === clonedFromNodeId || n.nodeId === clonedFromNodeId || n.node_id === clonedFromNodeId);
881
1446
  }
882
1447
  function isLocalControlPlaneNode(ctx, node) {
883
- if (isDirectLocalNode(ctx, node)) return true;
884
- if (node.isLocalWorktree === true) {
885
- const sourceNode = findClonedFromNode(ctx, node);
886
- if (sourceNode && isDirectLocalNode(ctx, sourceNode)) return true;
887
- }
888
- return false;
1448
+ return !!getLocalControlPlaneMatchReason(ctx, node);
889
1449
  }
890
1450
  function meshSessionCacheKey(nodeId, runtimeSessionId) {
891
1451
  return `${nodeId}:${runtimeSessionId}`;
892
1452
  }
1453
+ function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata) {
1454
+ const keyNodeId = readString(nodeId);
1455
+ const keySessionId = readString(runtimeSessionId);
1456
+ if (!keyNodeId || !keySessionId) return;
1457
+ const providerType = readString(metadata.providerType);
1458
+ const providerSessionId = readString(metadata.providerSessionId);
1459
+ if (!providerType && !providerSessionId) return;
1460
+ const existing = getSessionMetadata(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
1461
+ meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
1462
+ providerType: providerType || existing.providerType,
1463
+ providerSessionId: providerSessionId || existing.providerSessionId,
1464
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
1465
+ });
1466
+ }
1467
+ function rememberMeshSessionProviderMetadataFromEvent(event) {
1468
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : event && typeof event === "object" ? event : {};
1469
+ const nodeId = readString(event?.nodeId) || readString(metadataEvent.nodeId) || readString(metadataEvent.meshNodeId);
1470
+ const sessionId = readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId) || readString(event?.sessionId);
1471
+ rememberMeshSessionProviderMetadata(nodeId, sessionId, {
1472
+ providerType: readString(metadataEvent.providerType) || readString(event?.providerType) || "",
1473
+ providerSessionId: readString(metadataEvent.providerSessionId) || readString(event?.providerSessionId)
1474
+ });
1475
+ }
1476
+ function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
1477
+ const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 50 });
1478
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
1479
+ const entry = entries[i];
1480
+ const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
1481
+ const entryNodeId = readString(entry.nodeId) || readString(payload.nodeId) || readString(payload.meshNodeId);
1482
+ if (entryNodeId && entryNodeId !== nodeId) continue;
1483
+ const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
1484
+ if (entrySessionId !== runtimeSessionId) continue;
1485
+ const providerType = readString(entry.providerType) || readString(payload.providerType);
1486
+ const completionDiagnostic = payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? payload.completionDiagnostic : {};
1487
+ const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : {};
1488
+ const providerSessionId = readString(payload.providerSessionId) || readString(completionDiagnostic.providerSessionId) || readString(metadataEvent.providerSessionId);
1489
+ if (providerType || providerSessionId) {
1490
+ return { providerType: providerType || "", providerSessionId };
1491
+ }
1492
+ }
1493
+ return void 0;
1494
+ }
1495
+ function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
1496
+ const cached = getSessionMetadata(meshSessionCacheKey(nodeId, runtimeSessionId));
1497
+ if (cached?.providerType || cached?.providerSessionId) return cached;
1498
+ const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
1499
+ if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
1500
+ return fromLedger;
1501
+ }
893
1502
  function countUncommittedChanges(status) {
894
1503
  if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
895
1504
  const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
@@ -900,8 +1509,23 @@ function countUncommittedChanges(status) {
900
1509
  function isGitStatusDirty(status) {
901
1510
  if (typeof status?.isDirty === "boolean") return status.isDirty;
902
1511
  if (typeof status?.dirty === "boolean") return status.dirty;
1512
+ if (Array.isArray(status?.submodules) && status.submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return true;
903
1513
  return countUncommittedChanges(status) > 0;
904
1514
  }
1515
+ function slimLedgerPayload(payload) {
1516
+ const slim = {};
1517
+ for (const [k, v] of Object.entries(payload)) {
1518
+ if (k === "message" || k === "taskSummary") {
1519
+ slim[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "\u2026" : v;
1520
+ } else if (k === "evidence" || k === "workerResult" || k === "gitStatus" || k === "validationResults") {
1521
+ } else if (k === "finalSummary") {
1522
+ slim[k] = typeof v === "string" && v.length > 300 ? v.slice(0, 300) + "\u2026" : v;
1523
+ } else {
1524
+ slim[k] = v;
1525
+ }
1526
+ }
1527
+ return slim;
1528
+ }
905
1529
  function readRelatedRepos(node) {
906
1530
  const raw = Array.isArray(node.relatedRepos) ? node.relatedRepos : Array.isArray(node.policy?.relatedRepos) ? node.policy.relatedRepos : [];
907
1531
  return raw.map((entry) => ({
@@ -960,6 +1584,16 @@ function missingProviderPriorityMessage(nodeId) {
960
1584
  return `Node '${nodeId}' has no providerPriority policy; pass type explicitly or configure node.policy.providerPriority`;
961
1585
  }
962
1586
  function getNodeLaunchReadiness(node) {
1587
+ const bootstrap = node.worktreeBootstrap;
1588
+ if (node.isLocalWorktree && bootstrap?.status === "failed" && bootstrap?.required !== false) {
1589
+ return {
1590
+ providerPriority: readProviderPriority(node.policy),
1591
+ launchReady: false,
1592
+ launchBlockedReason: "worktree_bootstrap_failed",
1593
+ launchBlockedMessage: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : "Required worktree bootstrap failed; resolve it before launching an agent into this node.",
1594
+ worktreeBootstrap: bootstrap
1595
+ };
1596
+ }
963
1597
  const providerPriority = readProviderPriority(node.policy);
964
1598
  if (providerPriority.length) {
965
1599
  return {
@@ -974,6 +1608,33 @@ function getNodeLaunchReadiness(node) {
974
1608
  launchBlockedMessage: missingProviderPriorityMessage(node.id)
975
1609
  };
976
1610
  }
1611
+ function getWorktreeBootstrapLaunchBlock(node) {
1612
+ const bootstrap = node.worktreeBootstrap;
1613
+ if (!node.isLocalWorktree || bootstrap?.status !== "failed" || bootstrap?.required === false) return void 0;
1614
+ return {
1615
+ success: false,
1616
+ code: "worktree_bootstrap_failed",
1617
+ error: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : `Node '${node.id}' has a failed required worktree bootstrap.`,
1618
+ nodeId: node.id,
1619
+ worktreeBootstrap: bootstrap,
1620
+ recoveryHint: "Fix the configured worktree bootstrap command or remove/recreate the worktree node before launching an agent."
1621
+ };
1622
+ }
1623
+ async function collectLiveStatusSessions(ctx, node) {
1624
+ try {
1625
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
1626
+ return extractStatusMetadataSessions(statusResult);
1627
+ } catch {
1628
+ return [];
1629
+ }
1630
+ }
1631
+ async function collectMeshViewQueueNodesWithLiveSessions(ctx) {
1632
+ const nodes = await Promise.all(ctx.mesh.nodes.map(async (node) => {
1633
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
1634
+ return liveSessions.length > 0 ? { ...node, sessions: liveSessions } : node;
1635
+ }));
1636
+ return nodes;
1637
+ }
977
1638
  function readNumeric(value, fallback = 0) {
978
1639
  const parsed = Number(value);
979
1640
  return Number.isFinite(parsed) ? parsed : fallback;
@@ -1109,7 +1770,8 @@ async function commandForNode(ctx, node, command, args = {}) {
1109
1770
  if (isLocalTransport(ctx.transport)) {
1110
1771
  return ctx.transport.command(command, args);
1111
1772
  }
1112
- throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
1773
+ const identity = buildNodeMachineIdentity(ctx, node);
1774
+ 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})`);
1113
1775
  }
1114
1776
  function normalizePendingMeshCoordinatorEvents(value) {
1115
1777
  const payload = unwrapCommandPayload(value);
@@ -1127,6 +1789,14 @@ function buildMeshForwardPayloadFromPendingEvent(event) {
1127
1789
  providerType: readString(metadataEvent.providerType),
1128
1790
  providerSessionId: readString(metadataEvent.providerSessionId),
1129
1791
  finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
1792
+ jobId: readString(metadataEvent.jobId),
1793
+ interactionId: readString(metadataEvent.interactionId),
1794
+ status: readString(metadataEvent.status),
1795
+ targetDaemonId: readString(metadataEvent.targetDaemonId),
1796
+ startedAt: readString(metadataEvent.startedAt),
1797
+ completedAt: readString(metadataEvent.completedAt),
1798
+ retryOfJobId: readString(metadataEvent.retryOfJobId),
1799
+ ...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
1130
1800
  ...metadataEvent.intentional === true ? { intentional: true } : {},
1131
1801
  ...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
1132
1802
  ...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
@@ -1141,10 +1811,16 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
1141
1811
  const matchesCurrentMesh = (event) => readString(event?.meshId) === ctx.mesh.id;
1142
1812
  if (ctx.transport instanceof IpcTransport) {
1143
1813
  const surfacedEvents = [];
1814
+ const coordinatorDaemonId = readString(ctx.localDaemonId);
1815
+ const pendingEventArgs = {
1816
+ meshId: ctx.mesh.id,
1817
+ ...coordinatorDaemonId ? { coordinatorDaemonId } : {}
1818
+ };
1144
1819
  try {
1145
1820
  surfacedEvents.push(
1146
- ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
1821
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", pendingEventArgs)).filter(matchesCurrentMesh)
1147
1822
  );
1823
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1148
1824
  } catch {
1149
1825
  }
1150
1826
  for (const node of ctx.mesh.nodes) {
@@ -1152,27 +1828,31 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
1152
1828
  if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
1153
1829
  try {
1154
1830
  const remoteEvents = normalizePendingMeshCoordinatorEvents(
1155
- await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", {})
1831
+ await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", pendingEventArgs)
1156
1832
  ).filter(matchesCurrentMesh);
1157
1833
  if (remoteEvents.length === 0) continue;
1158
1834
  for (const event of remoteEvents) {
1159
1835
  const payload = buildMeshForwardPayloadFromPendingEvent(event);
1160
1836
  if (!payload.event || !payload.meshId) continue;
1161
1837
  await ctx.transport.command("mesh_forward_event", payload);
1838
+ rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
1162
1839
  }
1163
1840
  } catch {
1164
1841
  }
1165
1842
  }
1166
1843
  try {
1167
1844
  surfacedEvents.push(
1168
- ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
1845
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", pendingEventArgs)).filter(matchesCurrentMesh)
1169
1846
  );
1847
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1170
1848
  } catch {
1171
1849
  }
1172
1850
  return surfacedEvents;
1173
1851
  }
1174
1852
  if (isLocalTransport(ctx.transport)) {
1175
- return (0, import_daemon_core.drainPendingMeshCoordinatorEvents)().filter(matchesCurrentMesh);
1853
+ const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId).filter(matchesCurrentMesh);
1854
+ events.forEach(rememberMeshSessionProviderMetadataFromEvent);
1855
+ return events;
1176
1856
  }
1177
1857
  return [];
1178
1858
  }
@@ -1189,11 +1869,12 @@ function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
1189
1869
  }
1190
1870
  var MESH_STATUS_TOOL = {
1191
1871
  name: "mesh_status",
1192
- 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.",
1872
+ 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.",
1193
1873
  inputSchema: {
1194
1874
  type: "object",
1195
1875
  properties: {
1196
- _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
1876
+ _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." },
1877
+ includeStaleDirectWorkDetails: { type: "boolean", description: "Opt in to the full staleDirectWork array. Defaults false; normal status returns compact staleDirectWorkSummary only." }
1197
1878
  }
1198
1879
  }
1199
1880
  };
@@ -1213,14 +1894,18 @@ var MESH_ENQUEUE_TASK_TOOL = {
1213
1894
  inputSchema: {
1214
1895
  type: "object",
1215
1896
  properties: {
1216
- message: { type: "string", description: "The task instruction for the agent." }
1897
+ message: { type: "string", description: "The task instruction for the agent." },
1898
+ 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." },
1899
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
1900
+ requiredTags: { type: "array", items: { type: "string" }, description: "Optional capability tags that every eligible node must have, e.g. os=darwin, provider=codex-cli, gpu." },
1901
+ required_tags: { type: "array", items: { type: "string" }, description: "Snake_case alias for requiredTags." }
1217
1902
  },
1218
1903
  required: ["message"]
1219
1904
  }
1220
1905
  };
1221
1906
  var MESH_VIEW_QUEUE_TOOL = {
1222
1907
  name: "mesh_view_queue",
1223
- description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records.",
1908
+ 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.",
1224
1909
  inputSchema: {
1225
1910
  type: "object",
1226
1911
  properties: {
@@ -1273,7 +1958,9 @@ var MESH_SEND_TASK_TOOL = {
1273
1958
  properties: {
1274
1959
  node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
1275
1960
  session_id: { type: "string", description: "Agent session ID on the target node." },
1276
- message: { type: "string", description: "Natural-language task to send to the agent." }
1961
+ message: { type: "string", description: "Natural-language task to send to the agent." },
1962
+ 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." },
1963
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
1277
1964
  },
1278
1965
  required: ["node_id", "session_id", "message"]
1279
1966
  }
@@ -1331,6 +2018,21 @@ var MESH_GIT_STATUS_TOOL = {
1331
2018
  required: ["node_id"]
1332
2019
  }
1333
2020
  };
2021
+ var MESH_FAST_FORWARD_NODE_TOOL = {
2022
+ name: "mesh_fast_forward_node",
2023
+ 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.",
2024
+ inputSchema: {
2025
+ type: "object",
2026
+ properties: {
2027
+ node_id: { type: "string", description: "Target node ID." },
2028
+ branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
2029
+ execute: { type: "boolean", description: "When true, apply the fast-forward if all safety gates pass. Defaults false/dry-run." },
2030
+ dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
2031
+ 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." }
2032
+ },
2033
+ required: ["node_id"]
2034
+ }
2035
+ };
1334
2036
  var MESH_CHECKPOINT_TOOL = {
1335
2037
  name: "mesh_checkpoint",
1336
2038
  description: "Create a git checkpoint (commit) on a mesh node workspace.",
@@ -1414,7 +2116,7 @@ var MESH_TASK_HISTORY_TOOL = {
1414
2116
  type: "object",
1415
2117
  properties: {
1416
2118
  tail: { type: "number", description: "Number of recent entries to return (default: 20)." },
1417
- kind: { type: "string", description: "Filter by entry kind: task_dispatched, task_completed, task_failed, task_stalled, session_launched, checkpoint_created, node_cloned, node_removed." }
2119
+ 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." }
1418
2120
  }
1419
2121
  }
1420
2122
  };
@@ -1434,7 +2136,7 @@ var MESH_RECONCILE_LEDGER_TOOL = {
1434
2136
  };
1435
2137
  var MESH_REFINE_NODE_TOOL = {
1436
2138
  name: "mesh_refine_node",
1437
- description: "The Refinery: Automatically validate and merge a completed worktree node back into its base branch. This tool automates the validation gate and merge queue step. It will merge the node's branch into its base branch and cleanly remove the worktree node and its sessions.",
2139
+ 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.",
1438
2140
  inputSchema: {
1439
2141
  type: "object",
1440
2142
  properties: {
@@ -1443,6 +2145,43 @@ var MESH_REFINE_NODE_TOOL = {
1443
2145
  required: ["node_id"]
1444
2146
  }
1445
2147
  };
2148
+ var MESH_REFINE_CONFIG_SCHEMA_TOOL = {
2149
+ name: "mesh_refine_config_schema",
2150
+ 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.",
2151
+ inputSchema: { type: "object", properties: {} }
2152
+ };
2153
+ var MESH_VALIDATE_REFINE_CONFIG_TOOL = {
2154
+ name: "mesh_validate_refine_config",
2155
+ description: "Validate the repo mesh/refine config for a node/workspace without running validation commands or merging.",
2156
+ inputSchema: {
2157
+ type: "object",
2158
+ properties: {
2159
+ node_id: { type: "string", description: "Optional node/workspace whose refine config should be loaded. Defaults to the first mesh node." },
2160
+ config: { type: "object", description: "Optional inline config object to validate instead of loading from the repo." }
2161
+ }
2162
+ }
2163
+ };
2164
+ var MESH_SUGGEST_REFINE_CONFIG_TOOL = {
2165
+ name: "mesh_suggest_refine_config",
2166
+ description: "Suggest a repo mesh/refine config scaffold from project context/package scripts. Suggestions are never executed until saved as explicit refine config.",
2167
+ inputSchema: {
2168
+ type: "object",
2169
+ properties: {
2170
+ node_id: { type: "string", description: "Optional node/workspace used for suggestions. Defaults to the first mesh node." }
2171
+ }
2172
+ }
2173
+ };
2174
+ var MESH_REFINE_PLAN_TOOL = {
2175
+ name: "mesh_refine_plan",
2176
+ 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.",
2177
+ inputSchema: {
2178
+ type: "object",
2179
+ properties: {
2180
+ node_id: { type: "string", description: "Node ID of the worktree node to plan." }
2181
+ },
2182
+ required: ["node_id"]
2183
+ }
2184
+ };
1446
2185
  var ALL_MESH_TOOLS = [
1447
2186
  MESH_STATUS_TOOL,
1448
2187
  MESH_LIST_NODES_TOOL,
@@ -1455,24 +2194,31 @@ var ALL_MESH_TOOLS = [
1455
2194
  MESH_READ_DEBUG_TOOL,
1456
2195
  MESH_LAUNCH_SESSION_TOOL,
1457
2196
  MESH_GIT_STATUS_TOOL,
2197
+ MESH_FAST_FORWARD_NODE_TOOL,
1458
2198
  MESH_CHECKPOINT_TOOL,
1459
2199
  MESH_APPROVE_TOOL,
1460
2200
  MESH_CLONE_NODE_TOOL,
1461
2201
  MESH_REMOVE_NODE_TOOL,
1462
2202
  MESH_REFINE_NODE_TOOL,
2203
+ MESH_REFINE_CONFIG_SCHEMA_TOOL,
2204
+ MESH_VALIDATE_REFINE_CONFIG_TOOL,
2205
+ MESH_SUGGEST_REFINE_CONFIG_TOOL,
2206
+ MESH_REFINE_PLAN_TOOL,
1463
2207
  MESH_CLEANUP_SESSIONS_TOOL,
1464
2208
  MESH_TASK_HISTORY_TOOL,
1465
2209
  MESH_RECONCILE_LEDGER_TOOL
1466
2210
  ];
1467
- async function meshStatus(ctx) {
2211
+ async function meshStatus(ctx, args = {}) {
1468
2212
  await refreshMeshFromDaemon(ctx);
1469
2213
  const { mesh, transport } = ctx;
1470
- const results = [];
1471
- const ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1472
- for (const node of mesh.nodes) {
2214
+ let ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
2215
+ const results = await Promise.all(mesh.nodes.map(async (node) => {
1473
2216
  const entry = {
1474
2217
  nodeId: node.id,
1475
2218
  workspace: node.workspace,
2219
+ machine: buildNodeMachineIdentity(ctx, node),
2220
+ daemonId: readNodeDaemonId(node),
2221
+ machineId: readNodeMachineId(node),
1476
2222
  ...getNodeLaunchReadiness(node)
1477
2223
  };
1478
2224
  try {
@@ -1482,6 +2228,7 @@ async function meshStatus(ctx) {
1482
2228
  const uncommittedChanges = countUncommittedChanges(status);
1483
2229
  const dirty = isGitStatusDirty(status);
1484
2230
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
2231
+ assignFullGitSnapshot(entry, status);
1485
2232
  entry.branch = status?.branch;
1486
2233
  entry.isDirty = dirty;
1487
2234
  entry.uncommittedChanges = uncommittedChanges;
@@ -1503,6 +2250,7 @@ async function meshStatus(ctx) {
1503
2250
  const uncommittedChanges = countUncommittedChanges(status);
1504
2251
  const dirty = isGitStatusDirty(status);
1505
2252
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
2253
+ assignFullGitSnapshot(entry, status);
1506
2254
  entry.branch = status?.branch;
1507
2255
  entry.isDirty = dirty;
1508
2256
  entry.uncommittedChanges = uncommittedChanges;
@@ -1538,7 +2286,7 @@ async function meshStatus(ctx) {
1538
2286
  if (recoveryContext.consecutiveNodeFailures > 0) {
1539
2287
  entry.recoveryHints = {
1540
2288
  consecutiveFailures: recoveryContext.consecutiveNodeFailures,
1541
- lastTaskMessage: recoveryContext.lastTaskMessage,
2289
+ lastTaskMessage: typeof recoveryContext.lastTaskMessage === "string" ? recoveryContext.lastTaskMessage.slice(0, 100) + (recoveryContext.lastTaskMessage.length > 100 ? "\u2026" : "") : recoveryContext.lastTaskMessage,
1542
2290
  advice: recoveryContext.advice,
1543
2291
  retryRecommended: recoveryContext.retryRecommended
1544
2292
  };
@@ -1578,7 +2326,55 @@ async function meshStatus(ctx) {
1578
2326
  }
1579
2327
  const relatedRepos = await collectRelatedRepoStatuses(ctx, node);
1580
2328
  if (relatedRepos.length) entry.relatedRepos = relatedRepos;
1581
- results.push(entry);
2329
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
2330
+ if (liveSessions.length > 0) {
2331
+ entry.sessions = liveSessions.map((s) => {
2332
+ const coordinatorMeshId = typeof s.coordinator?.meshId === "string" ? s.coordinator.meshId : void 0;
2333
+ const isSelfCoordinator = coordinatorMeshId === mesh.id;
2334
+ return {
2335
+ id: s.instanceId ?? s.id ?? s.sessionId,
2336
+ status: s.status ?? s.lifecycle ?? s.state,
2337
+ providerType: s.providerType ?? s.cliType ?? s.type,
2338
+ ...s.activeChat?.status ? { chatStatus: s.activeChat.status } : {},
2339
+ ...isSelfCoordinator ? { isSelfCoordinator: true, role: "coordinator" } : {}
2340
+ };
2341
+ }).filter((s) => s.id);
2342
+ }
2343
+ return entry;
2344
+ }));
2345
+ let ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 200 });
2346
+ let directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(mesh.id);
2347
+ const directReconciliation = await reconcileDirectDispatchesFromTranscriptEvidence(ctx, results, directDispatches, ledgerEntries);
2348
+ if (directReconciliation.reconciled > 0) {
2349
+ ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 200 });
2350
+ directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(mesh.id);
2351
+ ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
2352
+ }
2353
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2354
+ meshId: mesh.id,
2355
+ queue: (0, import_daemon_core.getQueue)(mesh.id),
2356
+ ledgerEntries,
2357
+ directDispatches,
2358
+ nodes: results
2359
+ });
2360
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
2361
+ const staleDirectWorkSummary = (0, import_daemon_core.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
2362
+ note: activeWorkEvidence.staleDirectWorkNote,
2363
+ 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."
2364
+ });
2365
+ const coordinatorSessions = [];
2366
+ for (const nodeEntry of results) {
2367
+ const sessions = Array.isArray(nodeEntry.sessions) ? nodeEntry.sessions : [];
2368
+ for (const s of sessions) {
2369
+ if (s?.isSelfCoordinator === true && s.id) {
2370
+ coordinatorSessions.push({
2371
+ nodeId: nodeEntry.nodeId,
2372
+ sessionId: s.id,
2373
+ providerType: s.providerType,
2374
+ status: s.status
2375
+ });
2376
+ }
2377
+ }
1582
2378
  }
1583
2379
  const response = {
1584
2380
  meshId: mesh.id,
@@ -1589,10 +2385,26 @@ async function meshStatus(ctx) {
1589
2385
  sourceOfTruth: {
1590
2386
  membership: "coordinator_daemon_live_mesh",
1591
2387
  currentStatus: "live_git_and_session_probes",
2388
+ activeWork: "mesh_queue_file_and_local_ledger",
1592
2389
  historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
1593
2390
  },
1594
2391
  nodes: results,
1595
- branchConvergenceSummary: summarizeBranchConvergence(results)
2392
+ activeWork: activeWorkEvidence.activeWork,
2393
+ staleDirectWorkSummary,
2394
+ ...args.includeStaleDirectWorkDetails === true ? { staleDirectWork: activeWorkEvidence.staleDirectWork } : {},
2395
+ // terminalDirectWork is historical (completed/failed direct dispatches) — opt-in only.
2396
+ ...args.includeTerminalDirectWork === true ? { terminalDirectWork: activeWorkEvidence.terminalDirectWork } : {},
2397
+ activeWorkSummary: activeWorkEvidence.summary,
2398
+ ...pollingGuidance ? { pollingGuidance } : {},
2399
+ branchConvergenceSummary: summarizeBranchConvergence(results),
2400
+ ...coordinatorSessions.length > 0 ? {
2401
+ coordinatorSessions,
2402
+ selfIdentification: {
2403
+ meshId: mesh.id,
2404
+ coordinatorSessions,
2405
+ note: "Sessions listed here are coordinator sessions for this mesh. The calling coordinator IS one of these sessions \u2014 do not treat its own generating CLI session as a foreign delegated task. Per-session marker: sessions[].isSelfCoordinator === true."
2406
+ }
2407
+ } : {}
1596
2408
  };
1597
2409
  try {
1598
2410
  response.ledgerSummary = ledgerSummary;
@@ -1600,6 +2412,14 @@ async function meshStatus(ctx) {
1600
2412
  }
1601
2413
  try {
1602
2414
  const pendingEvents = await drainCoordinatorPendingEvents(ctx);
2415
+ const asyncRefineJobs = (0, import_daemon_core.buildMeshAsyncRefineJobs)({
2416
+ meshId: mesh.id,
2417
+ ledgerEntries,
2418
+ pendingEvents
2419
+ });
2420
+ if (asyncRefineJobs.length > 0) {
2421
+ response.asyncRefineJobs = asyncRefineJobs;
2422
+ }
1603
2423
  if (pendingEvents.length > 0) {
1604
2424
  response.pendingCoordinatorEvents = pendingEvents;
1605
2425
  }
@@ -1609,12 +2429,21 @@ async function meshStatus(ctx) {
1609
2429
  }
1610
2430
  async function meshTaskHistory(ctx, args) {
1611
2431
  const { mesh } = ctx;
1612
- await drainCoordinatorPendingEvents(ctx);
2432
+ const pendingEvents = await drainCoordinatorPendingEvents(ctx);
1613
2433
  const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
1614
2434
  const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
1615
- const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
2435
+ const rawEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
2436
+ const entries = rawEntries.map((e) => ({
2437
+ ...e,
2438
+ payload: e.payload ? slimLedgerPayload(e.payload) : e.payload
2439
+ }));
1616
2440
  const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1617
- return JSON.stringify({ meshId: mesh.id, entries, summary }, null, 2);
2441
+ return JSON.stringify({
2442
+ meshId: mesh.id,
2443
+ entries,
2444
+ summary,
2445
+ ...pendingEvents.length > 0 ? { pendingCoordinatorEvents: pendingEvents } : {}
2446
+ }, null, 2);
1618
2447
  }
1619
2448
  async function meshReconcileLedger(ctx, args) {
1620
2449
  await refreshMeshFromDaemon(ctx);
@@ -1704,6 +2533,9 @@ async function meshListNodes(ctx) {
1704
2533
  nodeId: n.id,
1705
2534
  workspace: n.workspace,
1706
2535
  repoRoot: n.repoRoot,
2536
+ daemonId: readNodeDaemonId(n),
2537
+ machineId: readNodeMachineId(n),
2538
+ machine: buildNodeMachineIdentity(ctx, n),
1707
2539
  isLocalWorktree: n.isLocalWorktree,
1708
2540
  policy: n.policy,
1709
2541
  relatedRepos: readRelatedRepos(n),
@@ -1713,57 +2545,135 @@ async function meshListNodes(ctx) {
1713
2545
  }, null, 2);
1714
2546
  }
1715
2547
  async function meshEnqueueTask(ctx, args) {
2548
+ const taskMode = readString(args.task_mode) || readString(args.taskMode);
2549
+ const requiredTags = (0, import_daemon_core.normalizeMeshCapabilityTags)(Array.isArray(args.requiredTags) ? args.requiredTags : args.required_tags);
1716
2550
  try {
1717
- const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message);
2551
+ const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode, requiredTags });
1718
2552
  if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
1719
- ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2553
+ const queueTrigger = await triggerMeshQueueAndReport(ctx);
2554
+ return JSON.stringify({
2555
+ success: true,
2556
+ source: "queue",
2557
+ taskId: task.id,
2558
+ status: task.status,
2559
+ taskMode: task.taskMode,
2560
+ requiredTags: task.requiredTags,
2561
+ queueTrigger,
2562
+ ...buildQueueTriggerGuidance(queueTrigger)
1720
2563
  });
1721
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
1722
2564
  }
1723
2565
  if (ctx.transport instanceof IpcTransport) {
1724
- ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1725
- });
2566
+ const queueTrigger = await triggerMeshQueueAndReport(ctx);
1726
2567
  const dispatchPromises = [];
1727
2568
  for (const node of ctx.mesh.nodes) {
1728
2569
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1729
2570
  if (isLocalNode || !node.daemonId) continue;
2571
+ if (!(0, import_daemon_core.nodeSatisfiesRequiredTags)(requiredTags, (0, import_daemon_core.buildMeshNodeCapabilityTags)(node))) continue;
1730
2572
  dispatchPromises.push(
1731
2573
  ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
1732
2574
  if (result.success) {
1733
2575
  try {
2576
+ const providerType = result.providerType;
2577
+ const descriptor = summarizeTaskMessage(args.message);
1734
2578
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1735
2579
  kind: "task_dispatched",
1736
2580
  nodeId: node.id,
1737
2581
  sessionId: result.sessionId,
1738
- payload: { message: args.message, via: "p2p_direct", taskId: task.id }
2582
+ providerType,
2583
+ payload: {
2584
+ source: "queue",
2585
+ via: "p2p_direct",
2586
+ taskId: task.id,
2587
+ message: args.message,
2588
+ taskTitle: descriptor.taskTitle,
2589
+ taskSummary: descriptor.taskSummary,
2590
+ ...task.taskMode ? { taskMode: task.taskMode } : {},
2591
+ ...providerType ? { providerType } : {},
2592
+ targetSessionId: result.sessionId
2593
+ }
1739
2594
  });
1740
2595
  } catch {
1741
2596
  }
1742
2597
  }
1743
- }).catch(() => {
2598
+ }).catch((err) => {
2599
+ try {
2600
+ (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2601
+ kind: "p2p_dispatch_failed",
2602
+ nodeId: node.id,
2603
+ payload: {
2604
+ source: "queue",
2605
+ via: "p2p_direct",
2606
+ taskId: task.id,
2607
+ error: err?.message || String(err),
2608
+ dispatchFailedAt: (/* @__PURE__ */ new Date()).toISOString()
2609
+ }
2610
+ });
2611
+ } catch {
2612
+ }
1744
2613
  })
1745
2614
  );
1746
2615
  }
1747
2616
  Promise.all(dispatchPromises).catch(() => {
1748
2617
  });
1749
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2618
+ return JSON.stringify({
2619
+ success: true,
2620
+ source: "queue",
2621
+ taskId: task.id,
2622
+ status: task.status,
2623
+ taskMode: task.taskMode,
2624
+ requiredTags: task.requiredTags,
2625
+ queueTrigger,
2626
+ ...buildQueueTriggerGuidance(queueTrigger)
2627
+ });
1750
2628
  }
1751
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2629
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode, requiredTags: task.requiredTags });
1752
2630
  } catch (e) {
1753
- return JSON.stringify({ success: false, error: e.message });
2631
+ const message = e?.message || String(e);
2632
+ if (message.includes("live_debug_readonly_guardrail_violation")) {
2633
+ return JSON.stringify({ success: false, code: "live_debug_readonly_guardrail_violation", taskMode, error: message });
2634
+ }
2635
+ return JSON.stringify({ success: false, error: message });
1754
2636
  }
1755
2637
  }
1756
2638
  async function meshViewQueue(ctx, args) {
1757
2639
  try {
2640
+ await refreshMeshFromDaemon(ctx);
1758
2641
  const statusFilter = sanitizeQueueStatusFilter(args.status);
1759
2642
  const view = normalizeQueueViewMode(args.view);
1760
- const fullQueue = annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh);
2643
+ const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh));
1761
2644
  const queue = filterQueueForView(fullQueue, view, statusFilter);
1762
2645
  const summary = buildQueueStatusSummary(fullQueue);
1763
2646
  const visibleSummary = buildQueueStatusSummary(queue);
1764
2647
  const maintenance = buildQueueMaintenanceReport(fullQueue);
2648
+ const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
2649
+ let ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
2650
+ let directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
2651
+ const directReconciliation = await reconcileDirectDispatchesFromTranscriptEvidence(ctx, liveNodes, directDispatches, ledgerEntries);
2652
+ if (directReconciliation.reconciled > 0) {
2653
+ ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
2654
+ directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
2655
+ }
2656
+ (0, import_daemon_core.markStaleDirectDispatches)(ctx.mesh.id);
2657
+ directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
2658
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2659
+ meshId: ctx.mesh.id,
2660
+ queue: fullQueue,
2661
+ ledgerEntries,
2662
+ // Always pass BeadsDB records (may be empty). buildMeshActiveWork uses them for local
2663
+ // dispatches and falls through to ledger scan for remote P2P dispatches not in BeadsDB.
2664
+ directDispatches,
2665
+ nodes: liveNodes
2666
+ });
2667
+ const recentDispatchFailures = ledgerEntries.filter((e) => e.kind === "p2p_dispatch_failed").slice(-20).map((e) => ({
2668
+ nodeId: e.nodeId,
2669
+ taskId: e.payload?.taskId,
2670
+ error: e.payload?.error,
2671
+ via: e.payload?.via,
2672
+ failedAt: e.payload?.dispatchFailedAt || e.timestamp
2673
+ }));
1765
2674
  const staleAssignedTasks = maintenance.staleAssignedTasks || [];
1766
2675
  const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
2676
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
1767
2677
  return JSON.stringify({
1768
2678
  success: true,
1769
2679
  sourceOfTruth: {
@@ -1778,21 +2688,29 @@ async function meshViewQueue(ctx, args) {
1778
2688
  filtered: Boolean(statusFilter?.length) || view !== "all"
1779
2689
  },
1780
2690
  queue,
1781
- visibleQueue: queue,
1782
- visibleSummary,
2691
+ activeWork: activeWorkEvidence.activeWork,
2692
+ staleDirectWork: activeWorkEvidence.staleDirectWork,
2693
+ activeWorkSummary: activeWorkEvidence.summary,
2694
+ ...pollingGuidance ? { pollingGuidance } : {},
1783
2695
  summary,
2696
+ visibleSummary,
1784
2697
  activeCounts: summary.activeCounts,
1785
2698
  historicalCounts: summary.historicalCounts,
1786
- activeCount: summary.activeCount,
1787
- historicalCount: summary.historicalCount,
1788
2699
  visibleActiveCounts: visibleSummary.activeCounts,
1789
2700
  visibleHistoricalCounts: visibleSummary.historicalCounts,
2701
+ activeCount: summary.activeCount,
2702
+ historicalCount: summary.historicalCount,
1790
2703
  visibleActiveCount: visibleSummary.activeCount,
1791
2704
  visibleHistoricalCount: visibleSummary.historicalCount,
1792
2705
  staleAssignedTasks,
1793
2706
  staleAssignedCount: maintenance.staleAssignedCount,
1794
2707
  queueMaintenance: maintenance,
1795
2708
  cleanupDryRun: maintenance,
2709
+ ...recentDispatchFailures.length > 0 ? {
2710
+ recentDispatchFailures,
2711
+ dispatchFailureCount: recentDispatchFailures.length,
2712
+ dispatchFailureNote: "Remote P2P dispatch attempts that failed. Affected tasks remain pending and may require mesh_queue_requeue if no idle session picks them up."
2713
+ } : {},
1796
2714
  ...view === "active" || statusFilter?.some((status) => ACTIVE_QUEUE_STATUSES.has(status)) ? {
1797
2715
  activeQueue: queue.filter((task) => ACTIVE_QUEUE_STATUSES.has(String(task?.status || "")))
1798
2716
  } : {},
@@ -1812,6 +2730,10 @@ async function meshQueueCancel(ctx, args) {
1812
2730
  if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
1813
2731
  const task = (0, import_daemon_core.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
1814
2732
  if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
2733
+ if (isLocalTransport(ctx.transport)) {
2734
+ ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2735
+ });
2736
+ }
1815
2737
  return JSON.stringify({ success: true, task }, null, 2);
1816
2738
  } catch (e) {
1817
2739
  return JSON.stringify({ success: false, error: e.message });
@@ -1842,10 +2764,60 @@ async function meshQueueRequeue(ctx, args) {
1842
2764
  }
1843
2765
  }
1844
2766
  async function meshSendTask(ctx, args) {
2767
+ const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
2768
+ const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
2769
+ if (!modeValidation.valid) {
2770
+ return JSON.stringify({
2771
+ success: false,
2772
+ code: "live_debug_readonly_guardrail_violation",
2773
+ taskMode: modeValidation.taskMode || requestedTaskMode,
2774
+ violations: modeValidation.violations,
2775
+ allowedOperations: modeValidation.allowedOperations,
2776
+ error: `live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`
2777
+ });
2778
+ }
2779
+ const taskMode = modeValidation.taskMode;
1845
2780
  const node = await findNodeWithRefresh(ctx, args.node_id);
1846
2781
  if (node.policy?.readOnly) {
1847
2782
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
1848
2783
  }
2784
+ let explicitTargetSession;
2785
+ if (args.session_id && isWorkerTaskMode(taskMode) && (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport))) {
2786
+ try {
2787
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2788
+ const sessions = extractStatusMetadataSessions(statusResult);
2789
+ explicitTargetSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2790
+ if (explicitTargetSession && isMeshCoordinatorSessionRecord(explicitTargetSession)) {
2791
+ return JSON.stringify({
2792
+ success: false,
2793
+ recoverable: true,
2794
+ code: "mesh_target_session_is_coordinator",
2795
+ reason: "mesh_target_session_is_coordinator",
2796
+ nodeId: args.node_id,
2797
+ sessionId: args.session_id,
2798
+ taskMode: taskMode || "unspecified",
2799
+ 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.`,
2800
+ 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.`
2801
+ });
2802
+ }
2803
+ if (explicitTargetSession && isUnmanagedSessionRecord(explicitTargetSession)) {
2804
+ return JSON.stringify({
2805
+ success: false,
2806
+ recoverable: true,
2807
+ code: "mesh_target_session_unmanaged",
2808
+ reason: "mesh_target_session_unmanaged",
2809
+ nodeId: args.node_id,
2810
+ sessionId: args.session_id,
2811
+ taskMode: taskMode || "unspecified",
2812
+ unsafeTranscriptAlias: true,
2813
+ error: `Session '${args.session_id}' on node '${args.node_id}' has no Repo Mesh delegation metadata (missing meshNodeFor/meshCoordinatorFor/launchedByCoordinator). It may be the coordinator's own session or an unrelated session \u2014 dispatching risks self-send and orphaned completion events that never reach the coordinator ledger.`,
2814
+ nextAction: `Call mesh_launch_session for node '${args.node_id}' to start a fresh managed worker session, then retry mesh_send_task with the returned session_id. Alternatively use mesh_enqueue_task for queue-based assignment without specifying session_id.`
2815
+ });
2816
+ }
2817
+ } catch {
2818
+ explicitTargetSession = void 0;
2819
+ }
2820
+ }
1849
2821
  const duplicate = hasRecentDuplicateDispatch(ctx, args);
1850
2822
  if (duplicate.duplicate) {
1851
2823
  return JSON.stringify({
@@ -1869,47 +2841,162 @@ async function meshSendTask(ctx, args) {
1869
2841
  const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
1870
2842
  meshId: ctx.mesh.id,
1871
2843
  message: args.message,
1872
- targetNodeId: args.node_id
2844
+ targetNodeId: args.node_id,
2845
+ ...taskMode ? { taskMode } : {}
1873
2846
  });
1874
2847
  return JSON.stringify(res);
1875
2848
  }
1876
2849
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1877
2850
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
1878
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
2851
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id || ""));
2852
+ const taskId = (0, import_node_crypto.randomUUID)();
1879
2853
  const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
1880
2854
  session_id: args.session_id,
1881
2855
  message: args.message,
1882
- providerType: cached?.providerType
2856
+ providerType: cached?.providerType,
2857
+ verifiedSession: explicitTargetSession,
2858
+ meshContext: {
2859
+ meshId: ctx.mesh.id,
2860
+ nodeId: args.node_id,
2861
+ taskId
2862
+ }
1883
2863
  });
1884
2864
  if (result2.success) {
1885
2865
  const dispatchedSessionId = args.session_id || result2.sessionId;
2866
+ const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
1886
2867
  try {
2868
+ const providerType = result2.providerType || cached?.providerType;
1887
2869
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1888
2870
  kind: "task_dispatched",
1889
2871
  nodeId: args.node_id,
1890
2872
  sessionId: dispatchedSessionId,
1891
- payload: {
1892
- message: args.message,
1893
- via: "p2p_direct",
1894
- ...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
1895
- }
2873
+ providerType,
2874
+ payload: buildDirectTaskPayload(args.message, "p2p_direct", {
2875
+ taskId,
2876
+ taskMode,
2877
+ providerType,
2878
+ targetSessionId: dispatchedSessionId
2879
+ })
2880
+ });
2881
+ (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
2882
+ taskId,
2883
+ nodeId: args.node_id,
2884
+ sessionId: dispatchedSessionId,
2885
+ providerType: providerType || void 0,
2886
+ message: args.message,
2887
+ taskMode: taskMode || void 0,
2888
+ via: "p2p_direct",
2889
+ dispatchedAt
1896
2890
  });
1897
2891
  } catch {
1898
2892
  }
1899
2893
  }
1900
- return JSON.stringify({ ...result2, nodeId: args.node_id, dispatched: result2.success === true });
2894
+ return JSON.stringify({
2895
+ ...result2,
2896
+ nodeId: args.node_id,
2897
+ sessionId: result2.success ? args.session_id || result2.sessionId : args.session_id,
2898
+ ...result2.success ? { source: "direct", taskId } : {},
2899
+ taskMode,
2900
+ ...result2.success && result2.providerType ? { providerType: result2.providerType } : {},
2901
+ dispatched: result2.success === true
2902
+ });
1901
2903
  }
1902
2904
  if (args.session_id && isLocalTransport(ctx.transport)) {
1903
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2905
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
2906
+ let resolvedProviderType = cached?.providerType || "";
2907
+ if (!resolvedProviderType) {
2908
+ let explicitSession = explicitTargetSession;
2909
+ if (!explicitSession) {
2910
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2911
+ const sessions = extractStatusMetadataSessions(statusResult);
2912
+ explicitSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2913
+ }
2914
+ if (!explicitSession) {
2915
+ return JSON.stringify({
2916
+ success: false,
2917
+ recoverable: true,
2918
+ code: "mesh_target_session_not_found",
2919
+ reason: "mesh_target_session_not_found",
2920
+ transport: "local_ipc",
2921
+ retryRecommended: true,
2922
+ nodeId: args.node_id,
2923
+ sessionId: args.session_id,
2924
+ error: `Local session '${args.session_id}' is not present in live status for node '${args.node_id}'.`,
2925
+ 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.`
2926
+ });
2927
+ }
2928
+ if (isMeshCoordinatorSessionRecord(explicitSession)) {
2929
+ return JSON.stringify({
2930
+ success: false,
2931
+ recoverable: true,
2932
+ code: "mesh_target_session_is_coordinator",
2933
+ reason: "mesh_target_session_is_coordinator",
2934
+ nodeId: args.node_id,
2935
+ sessionId: args.session_id,
2936
+ taskMode: taskMode || "unspecified",
2937
+ 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.`,
2938
+ 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.`
2939
+ });
2940
+ }
2941
+ if (isUnmanagedSessionRecord(explicitSession)) {
2942
+ return JSON.stringify({
2943
+ success: false,
2944
+ recoverable: true,
2945
+ code: "mesh_target_session_unmanaged",
2946
+ reason: "mesh_target_session_unmanaged",
2947
+ nodeId: args.node_id,
2948
+ sessionId: args.session_id,
2949
+ taskMode: taskMode || "unspecified",
2950
+ unsafeTranscriptAlias: true,
2951
+ unsafeDelegateTarget: true,
2952
+ error: `Session '${args.session_id}' on node '${args.node_id}' has no Repo Mesh delegation metadata (missing meshNodeFor/meshCoordinatorFor/launchedByCoordinator). It may be the coordinator's own session or an unrelated session \u2014 dispatching risks self-send and orphaned completion events that never reach the coordinator ledger.`,
2953
+ nextAction: `Call mesh_launch_session for node '${args.node_id}' to start a fresh managed worker session, then retry mesh_send_task with the returned session_id. Alternatively use mesh_enqueue_task for queue-based assignment without specifying session_id.`
2954
+ });
2955
+ }
2956
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
2957
+ if (resolvedProviderType) {
2958
+ meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
2959
+ providerType: resolvedProviderType,
2960
+ providerSessionId: readString(explicitSession?.providerSessionId) || void 0,
2961
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2962
+ });
2963
+ }
2964
+ }
2965
+ if (!resolvedProviderType) {
2966
+ return JSON.stringify({
2967
+ success: false,
2968
+ recoverable: true,
2969
+ code: "mesh_target_session_provider_unknown",
2970
+ reason: "mesh_target_session_provider_unknown",
2971
+ transport: "local_ipc",
2972
+ retryRecommended: false,
2973
+ nodeId: args.node_id,
2974
+ sessionId: args.session_id,
2975
+ error: `Local session '${args.session_id}' is live but does not expose providerType/cliType, so agent_command cannot be routed safely.`,
2976
+ 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.`
2977
+ });
2978
+ }
2979
+ const sessionWasIdle = explicitTargetSession ? isIdleSessionRecord(explicitTargetSession) : false;
2980
+ const taskId = (0, import_node_crypto.randomUUID)();
2981
+ const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
1904
2982
  const dispatchResult = await commandForNode(ctx, node, "agent_command", {
1905
2983
  targetSessionId: args.session_id,
1906
- ...cached?.providerType ? { agentType: cached.providerType, cliType: cached.providerType, providerType: cached.providerType } : {},
2984
+ agentType: resolvedProviderType,
2985
+ cliType: resolvedProviderType,
2986
+ providerType: resolvedProviderType,
1907
2987
  action: "send_chat",
1908
- message: args.message
2988
+ message: args.message,
2989
+ meshContext: {
2990
+ meshId: ctx.mesh.id,
2991
+ nodeId: args.node_id,
2992
+ taskId
2993
+ }
1909
2994
  });
1910
2995
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
1911
2996
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
2997
+ const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
1912
2998
  return JSON.stringify({
2999
+ ...source && typeof source === "object" ? source : {},
1913
3000
  success: false,
1914
3001
  nodeId: args.node_id,
1915
3002
  sessionId: args.session_id,
@@ -1921,23 +3008,61 @@ async function meshSendTask(ctx, args) {
1921
3008
  kind: "task_dispatched",
1922
3009
  nodeId: args.node_id,
1923
3010
  sessionId: args.session_id,
1924
- providerType: cached?.providerType,
1925
- payload: { message: args.message, via: "local_direct" }
3011
+ providerType: resolvedProviderType,
3012
+ payload: buildDirectTaskPayload(args.message, "local_direct", {
3013
+ taskId,
3014
+ taskMode,
3015
+ providerType: resolvedProviderType,
3016
+ targetSessionId: args.session_id,
3017
+ dispatchedToIdleSession: sessionWasIdle
3018
+ })
1926
3019
  });
1927
3020
  } catch {
1928
3021
  }
1929
- return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
3022
+ (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
3023
+ taskId,
3024
+ nodeId: args.node_id,
3025
+ sessionId: args.session_id,
3026
+ providerType: resolvedProviderType || void 0,
3027
+ message: args.message,
3028
+ taskMode: taskMode || void 0,
3029
+ via: "local_direct",
3030
+ dispatchedToIdleSession: sessionWasIdle,
3031
+ dispatchedAt
3032
+ });
3033
+ return JSON.stringify({
3034
+ success: true,
3035
+ dispatched: true,
3036
+ source: "direct",
3037
+ taskId,
3038
+ taskMode,
3039
+ providerType: resolvedProviderType,
3040
+ nodeId: args.node_id,
3041
+ sessionId: args.session_id,
3042
+ ...sessionWasIdle ? {
3043
+ dispatchAcknowledgementRisk: true,
3044
+ dispatchAcknowledgementRiskReason: "session_was_idle_at_dispatch",
3045
+ dispatchAcknowledgementNote: `Session '${args.session_id}' was idle at dispatch time. If it does not transition to generating, this direct task was not acknowledged. Use mesh_status to verify; if the session remains idle, it may appear as stale direct work \u2014 launch a fresh session and retry.`
3046
+ } : {}
3047
+ });
1930
3048
  }
1931
3049
  const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
1932
3050
  targetNodeId: args.node_id,
1933
- targetSessionId: args.session_id
3051
+ targetSessionId: args.session_id,
3052
+ taskMode
1934
3053
  });
1935
- if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
1936
- ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1937
- });
1938
- }
1939
- const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)() : [];
1940
- const result = { success: true, nodeId: args.node_id, taskId: task.id, status: task.status };
3054
+ const queueTrigger = isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport ? await triggerMeshQueueAndReport(ctx) : void 0;
3055
+ const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId) : [];
3056
+ const result = {
3057
+ success: true,
3058
+ source: "queue",
3059
+ nodeId: args.node_id,
3060
+ taskId: task.id,
3061
+ status: task.status,
3062
+ taskMode: task.taskMode,
3063
+ queueTrigger,
3064
+ ...buildQueueTriggerGuidance(queueTrigger)
3065
+ };
1941
3066
  if (pendingEvents.length > 0) {
1942
3067
  result.pendingCoordinatorEvents = pendingEvents;
1943
3068
  }
@@ -1961,7 +3086,7 @@ async function meshReadChat(ctx, args) {
1961
3086
  await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
1962
3087
  }
1963
3088
  if (isLocalTransport(ctx.transport)) {
1964
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
3089
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
1965
3090
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
1966
3091
  const result = await commandForNode(ctx, node, "read_chat", {
1967
3092
  sessionId: args.session_id,
@@ -1969,18 +3094,19 @@ async function meshReadChat(ctx, args) {
1969
3094
  workspace: node.workspace,
1970
3095
  ...cached?.providerType ? { agentType: cached.providerType, providerType: cached.providerType } : {},
1971
3096
  ...providerSessionId ? { providerSessionId } : {},
1972
- tailLimit: args.tail ?? 10
3097
+ tailLimit: args.tail ?? 3
1973
3098
  });
1974
3099
  const payload = annotateRapidReadChatAdvisory(unwrapCommandPayload(result), {
1975
3100
  key: `mesh:${args.node_id}:${args.session_id}`,
1976
3101
  toolName: "mesh_read_chat",
1977
3102
  completionCallbackExpected: true
1978
3103
  });
1979
- if (args.compact) {
3104
+ const useCompact = args.compact !== false;
3105
+ if (useCompact) {
1980
3106
  const compactPayload = compactChatPayload(payload, {
1981
3107
  nodeId: args.node_id,
1982
3108
  sessionId: args.session_id,
1983
- limit: args.tail ?? 10
3109
+ limit: args.tail ?? 3
1984
3110
  });
1985
3111
  return JSON.stringify(
1986
3112
  payload.pollingAdvisory ? { ...compactPayload, pollingAdvisory: payload.pollingAdvisory } : compactPayload,
@@ -1993,7 +3119,7 @@ async function meshReadChat(ctx, args) {
1993
3119
  try {
1994
3120
  const targetId = `${node.daemonId}:session:${args.session_id}`;
1995
3121
  const res = await ctx.transport.readChat(targetId, {
1996
- limit: args.tail ?? 10,
3122
+ limit: args.tail ?? 3,
1997
3123
  sessionId: args.session_id
1998
3124
  });
1999
3125
  return JSON.stringify(res, null, 2);
@@ -2007,7 +3133,7 @@ async function meshReadChat(ctx, args) {
2007
3133
  async function meshReadDebug(ctx, args) {
2008
3134
  const node = await findNodeWithRefresh(ctx, args.node_id);
2009
3135
  if (isLocalTransport(ctx.transport)) {
2010
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
3136
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
2011
3137
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
2012
3138
  const delivery = args.delivery === "inline" ? void 0 : "daemon_file";
2013
3139
  const result = await commandForNode(ctx, node, "get_chat_debug_bundle", {
@@ -2038,6 +3164,8 @@ async function meshReadDebug(ctx, args) {
2038
3164
  }
2039
3165
  async function meshLaunchSession(ctx, args) {
2040
3166
  const node = await findNodeWithRefresh(ctx, args.node_id);
3167
+ const bootstrapBlock = getWorktreeBootstrapLaunchBlock(node);
3168
+ if (bootstrapBlock) return JSON.stringify(bootstrapBlock, null, 2);
2041
3169
  if (isLocalTransport(ctx.transport)) {
2042
3170
  let resolvedProviderType = typeof args.type === "string" && args.type.trim() ? args.type : "";
2043
3171
  if (!resolvedProviderType) {
@@ -2062,6 +3190,10 @@ async function meshLaunchSession(ctx, args) {
2062
3190
  const coordinatorNode = resolveCoordinatorNode(ctx);
2063
3191
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
2064
3192
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
3193
+ const isLocalNode = isLocalControlPlaneNode(ctx, node);
3194
+ if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
3195
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
3196
+ }
2065
3197
  let result;
2066
3198
  try {
2067
3199
  result = await commandForNode(ctx, node, "launch_cli", {
@@ -2089,7 +3221,8 @@ async function meshLaunchSession(ctx, args) {
2089
3221
  if (runtimeSessionId) {
2090
3222
  meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, runtimeSessionId), {
2091
3223
  providerType: resolvedProviderType,
2092
- ...providerSessionId ? { providerSessionId } : {}
3224
+ ...providerSessionId ? { providerSessionId } : {},
3225
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2093
3226
  });
2094
3227
  }
2095
3228
  try {
@@ -2102,18 +3235,13 @@ async function meshLaunchSession(ctx, args) {
2102
3235
  });
2103
3236
  } catch {
2104
3237
  }
2105
- const isLocalNode = isLocalControlPlaneNode(ctx, node);
2106
- if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
2107
- ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2108
- });
2109
- } else if (isLocalTransport(ctx.transport)) {
2110
- ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2111
- });
2112
- }
3238
+ const queueTrigger = await triggerMeshQueueAndReport(ctx, node, { localNode: isLocalNode });
2113
3239
  return JSON.stringify({
2114
3240
  ...launchPayload,
2115
3241
  resolvedProviderType,
2116
- ...providerSessionId ? { providerSessionId } : {}
3242
+ ...providerSessionId ? { providerSessionId } : {},
3243
+ queueTrigger,
3244
+ ...buildQueueTriggerGuidance(queueTrigger)
2117
3245
  }, null, 2);
2118
3246
  } else if (!isLocalTransport(ctx.transport) && node.daemonId) {
2119
3247
  let resolvedProviderType = typeof args.type === "string" && args.type.trim() ? args.type : "";
@@ -2127,6 +3255,9 @@ async function meshLaunchSession(ctx, args) {
2127
3255
  const coordinatorNode = resolveCoordinatorNode(ctx);
2128
3256
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
2129
3257
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
3258
+ if (!coordinatorDaemonId) {
3259
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
3260
+ }
2130
3261
  try {
2131
3262
  const res = await ctx.transport.launch(node.daemonId, {
2132
3263
  type: resolvedProviderType,
@@ -2207,6 +3338,51 @@ async function meshGitStatus(ctx, args) {
2207
3338
  }, null, 2);
2208
3339
  }
2209
3340
  }
3341
+ async function meshFastForwardNode(ctx, args) {
3342
+ await refreshMeshFromDaemon(ctx);
3343
+ const node = await findNodeWithRefresh(ctx, args.node_id);
3344
+ const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
3345
+ if (node.policy?.readOnly) {
3346
+ return JSON.stringify({
3347
+ success: false,
3348
+ code: "node_read_only",
3349
+ nodeId: args.node_id,
3350
+ workspace: node.workspace,
3351
+ allowed: false,
3352
+ willRun: false,
3353
+ executed: false,
3354
+ blockingReasons: ["node_read_only"]
3355
+ }, null, 2);
3356
+ }
3357
+ try {
3358
+ const dryRun = args.dry_run === true || args.execute !== true;
3359
+ const result = await commandForNode(ctx, node, "fast_forward_mesh_node", {
3360
+ meshId: ctx.mesh.id,
3361
+ nodeId: node.id,
3362
+ workspace: node.workspace,
3363
+ branch: typeof args.branch === "string" ? args.branch : void 0,
3364
+ execute: args.execute === true && args.dry_run !== true,
3365
+ dryRun,
3366
+ updateSubmodules: args.update_submodules === true,
3367
+ submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
3368
+ });
3369
+ return JSON.stringify(unwrapCommandPayload(result), null, 2);
3370
+ } catch (e) {
3371
+ const failure = buildCoordinatorP2pRelayFailure(e, {
3372
+ command: "fast_forward_mesh_node",
3373
+ targetDaemonId: node.daemonId,
3374
+ nodeId: args.node_id
3375
+ });
3376
+ return JSON.stringify({
3377
+ ...failure,
3378
+ workspace: node.workspace,
3379
+ allowed: false,
3380
+ willRun: false,
3381
+ executed: false,
3382
+ blockingReasons: [failure.code || "mesh_fast_forward_unavailable"]
3383
+ }, null, 2);
3384
+ }
3385
+ }
2210
3386
  async function meshCheckpoint(ctx, args) {
2211
3387
  const node = await findNodeWithRefresh(ctx, args.node_id);
2212
3388
  if (node.policy?.readOnly) {
@@ -2222,7 +3398,13 @@ async function meshCheckpoint(ctx, args) {
2222
3398
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2223
3399
  kind: "checkpoint_created",
2224
3400
  nodeId: args.node_id,
2225
- payload: { message: args.message, commit: result?.checkpoint?.commit }
3401
+ payload: {
3402
+ message: args.message,
3403
+ commit: result?.checkpoint?.commit,
3404
+ outcome: result?.checkpoint?.status || (result?.checkpoint?.noop ? "skipped" : void 0),
3405
+ noop: result?.checkpoint?.noop === true,
3406
+ reason: result?.checkpoint?.reason
3407
+ }
2226
3408
  });
2227
3409
  } catch {
2228
3410
  }
@@ -2238,7 +3420,13 @@ async function meshCheckpoint(ctx, args) {
2238
3420
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2239
3421
  kind: "checkpoint_created",
2240
3422
  nodeId: args.node_id,
2241
- payload: { message: args.message, commit: res?.checkpoint?.commit }
3423
+ payload: {
3424
+ message: args.message,
3425
+ commit: res?.checkpoint?.commit,
3426
+ outcome: res?.checkpoint?.status || (res?.checkpoint?.noop ? "skipped" : void 0),
3427
+ noop: res?.checkpoint?.noop === true,
3428
+ reason: res?.checkpoint?.reason
3429
+ }
2242
3430
  });
2243
3431
  } catch {
2244
3432
  }
@@ -2253,7 +3441,7 @@ async function meshCheckpoint(ctx, args) {
2253
3441
  async function meshApprove(ctx, args) {
2254
3442
  const node = await findNodeWithRefresh(ctx, args.node_id);
2255
3443
  if (isLocalTransport(ctx.transport)) {
2256
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
3444
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
2257
3445
  const providerSessionId = cached?.providerSessionId;
2258
3446
  const result = await commandForNode(ctx, node, "resolve_action", {
2259
3447
  sessionId: args.session_id,
@@ -2406,6 +3594,43 @@ async function meshRemoveNode(ctx, args) {
2406
3594
  return JSON.stringify({ error: "Cloud mesh remove_node requires node daemonId" });
2407
3595
  }
2408
3596
  }
3597
+ function resolveRefineConfigNode(ctx, nodeId) {
3598
+ if (nodeId) return findNode(ctx.mesh, nodeId);
3599
+ const node = ctx.mesh.nodes.find((entry) => !!entry.workspace);
3600
+ if (!node) throw new Error("No mesh node with a workspace is available");
3601
+ return node;
3602
+ }
3603
+ async function meshRefineConfigSchema(ctx) {
3604
+ const node = resolveRefineConfigNode(ctx);
3605
+ const result = await commandForNode(ctx, node, "get_mesh_refine_config_schema", {});
3606
+ return JSON.stringify(result, null, 2);
3607
+ }
3608
+ async function meshValidateRefineConfig(ctx, args) {
3609
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3610
+ const result = await commandForNode(ctx, node, "validate_mesh_refine_config", {
3611
+ workspace: node.workspace,
3612
+ inlineMesh: ctx.mesh,
3613
+ ...args.config ? { config: args.config } : {}
3614
+ });
3615
+ return JSON.stringify(result, null, 2);
3616
+ }
3617
+ async function meshSuggestRefineConfig(ctx, args) {
3618
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3619
+ const result = await commandForNode(ctx, node, "suggest_mesh_refine_config", {
3620
+ workspace: node.workspace,
3621
+ inlineMesh: ctx.mesh
3622
+ });
3623
+ return JSON.stringify(result, null, 2);
3624
+ }
3625
+ async function meshRefinePlan(ctx, args) {
3626
+ const node = await findNodeWithRefresh(ctx, args.node_id);
3627
+ const result = await commandForNode(ctx, node, "plan_mesh_refine_node", {
3628
+ meshId: ctx.mesh.id,
3629
+ nodeId: args.node_id,
3630
+ inlineMesh: ctx.mesh
3631
+ });
3632
+ return JSON.stringify(result, null, 2);
3633
+ }
2409
3634
  async function meshRefineNode(ctx, args) {
2410
3635
  const node = await findNodeWithRefresh(ctx, args.node_id);
2411
3636
  if (isLocalTransport(ctx.transport)) {
@@ -2414,7 +3639,7 @@ async function meshRefineNode(ctx, args) {
2414
3639
  nodeId: args.node_id,
2415
3640
  inlineMesh: ctx.mesh
2416
3641
  });
2417
- if (result?.success && result.removeResult?.removed !== false) {
3642
+ if (result?.success && result.async !== true && result.removeResult?.removed !== false) {
2418
3643
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2419
3644
  if (idx >= 0) {
2420
3645
  ctx.mesh.nodes.splice(idx, 1);
@@ -2429,7 +3654,7 @@ async function meshRefineNode(ctx, args) {
2429
3654
  nodeId: args.node_id,
2430
3655
  inlineMesh: ctx.mesh
2431
3656
  });
2432
- if (res?.success && res.removeResult?.removed !== false) {
3657
+ if (res?.success && res.async !== true && res.removeResult?.removed !== false) {
2433
3658
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2434
3659
  if (idx >= 0) {
2435
3660
  ctx.mesh.nodes.splice(idx, 1);
@@ -2466,13 +3691,13 @@ var STANDARD_TOOLS = [
2466
3691
  function buildMcpHelpText() {
2467
3692
  const meshTools = ALL_MESH_TOOLS.map((tool) => tool.name);
2468
3693
  return `
2469
- adhdev-mcp \u2014 ADHDev MCP Server
3694
+ ADHDev MCP Server
2470
3695
 
2471
3696
  Usage:
2472
- adhdev-mcp Local mode (requires standalone daemon)
2473
- adhdev-mcp --api-key <key> Cloud mode (ADHDev cloud API)
2474
- adhdev-mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
2475
- adhdev-mcp --repo-mesh <mesh_id> Mesh mode (coordinator-scoped tools)
3697
+ adhdev mcp Local mode (requires standalone daemon)
3698
+ adhdev mcp --api-key <key> Cloud mode (ADHDev cloud API)
3699
+ adhdev mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
3700
+ adhdev-mcp --help Compatibility bin (same server, legacy package entrypoint)
2476
3701
 
2477
3702
  Options:
2478
3703
  --mode <mode> Transport: local, cloud, or ipc
@@ -2497,6 +3722,7 @@ Mesh tools: ${meshTools.join(", ")}
2497
3722
  // src/server.ts
2498
3723
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
2499
3724
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
3725
+ var import_node_os = __toESM(require("os"));
2500
3726
  var import_types = require("@modelcontextprotocol/sdk/types.js");
2501
3727
 
2502
3728
  // src/transports/local.ts
@@ -3048,11 +4274,17 @@ function formatChatResult(result, sessionId, format, limit = 50, compact = false
3048
4274
  }, null, 2);
3049
4275
  }
3050
4276
  if ((format === "text" || format === void 0) && compact && compactPayload) {
3051
- const lines2 = outputMessages.slice(-limit).map((m) => {
4277
+ const summaryText = typeof compactPayload.summary === "string" ? compactPayload.summary.trim() : "";
4278
+ const tail = outputMessages.slice(-limit);
4279
+ const lastIndex = tail.length - 1;
4280
+ const lines2 = tail.flatMap((m, idx) => {
3052
4281
  const role = m.role === "user" ? "User" : m.role === "assistant" ? "Agent" : m.role;
3053
4282
  const content = messageContent(m);
4283
+ if (idx === lastIndex && (role === "Agent" || m.role === "agent") && summaryText && content.trim() === summaryText) {
4284
+ return [];
4285
+ }
3054
4286
  const truncated = content.length > 500 ? `${content.slice(0, 500)}\u2026` : content;
3055
- return `[${role}] ${truncated}`;
4287
+ return [`[${role}] ${truncated}`];
3056
4288
  });
3057
4289
  if (compactPayload.summary) {
3058
4290
  const truncatedSummary = compactPayload.summary.length > 500 ? `${compactPayload.summary.slice(0, 500)}\u2026` : compactPayload.summary;
@@ -4030,6 +5262,7 @@ async function startMcpServer(opts) {
4030
5262
  requirePreTaskCheckpoint: false,
4031
5263
  requirePostTaskCheckpoint: true,
4032
5264
  requireApprovalForPush: true,
5265
+ allowAutoPublishSubmoduleMainCommits: false,
4033
5266
  requireApprovalForDestructiveGit: true,
4034
5267
  dirtyWorkspaceBehavior: "warn",
4035
5268
  maxParallelTasks: 2,
@@ -4086,11 +5319,13 @@ async function startMcpServer(opts) {
4086
5319
  }
4087
5320
  let localDaemonId;
4088
5321
  let localMachineId;
5322
+ let coordinatorHostname = import_node_os.default.hostname();
4089
5323
  if (transport instanceof LocalTransport || transport instanceof IpcTransport) {
4090
5324
  try {
4091
5325
  const { loadConfig } = await import("@adhdev/daemon-core");
4092
5326
  const cfg = loadConfig();
4093
- if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
5327
+ if (cfg.machineId) localMachineId = cfg.machineId;
5328
+ else if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
4094
5329
  } catch {
4095
5330
  }
4096
5331
  }
@@ -4098,14 +5333,16 @@ async function startMcpServer(opts) {
4098
5333
  try {
4099
5334
  const statusResult = await transport.getStatus();
4100
5335
  const instanceId = typeof statusResult?.status?.instanceId === "string" ? statusResult.status.instanceId.trim() : "";
5336
+ const hostname = typeof statusResult?.status?.hostname === "string" ? statusResult.status.hostname.trim() : typeof statusResult?.status?.machine?.hostname === "string" ? statusResult.status.machine.hostname.trim() : "";
4101
5337
  if (instanceId) localDaemonId = instanceId;
5338
+ if (hostname) coordinatorHostname = hostname;
4102
5339
  } catch {
4103
5340
  }
4104
5341
  }
4105
- const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {} };
5342
+ const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {}, ...coordinatorHostname ? { coordinatorHostname } : {} };
4106
5343
  const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
4107
5344
  const server2 = new import_server.Server(
4108
- { name: "adhdev-mcp-server", version: "0.9.81" },
5345
+ { name: "adhdev-mcp-server", version: "0.9.82" },
4109
5346
  { capabilities: { tools: {}, resources: {} } }
4110
5347
  );
4111
5348
  const { ListResourcesRequestSchema, ReadResourceRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
@@ -4131,7 +5368,7 @@ async function startMcpServer(opts) {
4131
5368
  let text;
4132
5369
  switch (name) {
4133
5370
  case "mesh_status":
4134
- text = await meshStatus(meshCtx);
5371
+ text = await meshStatus(meshCtx, a);
4135
5372
  break;
4136
5373
  case "mesh_list_nodes":
4137
5374
  text = await meshListNodes(meshCtx);
@@ -4163,6 +5400,9 @@ async function startMcpServer(opts) {
4163
5400
  case "mesh_git_status":
4164
5401
  text = await meshGitStatus(meshCtx, a);
4165
5402
  break;
5403
+ case "mesh_fast_forward_node":
5404
+ text = await meshFastForwardNode(meshCtx, a);
5405
+ break;
4166
5406
  case "mesh_checkpoint":
4167
5407
  text = await meshCheckpoint(meshCtx, a);
4168
5408
  break;
@@ -4178,6 +5418,18 @@ async function startMcpServer(opts) {
4178
5418
  case "mesh_refine_node":
4179
5419
  text = await meshRefineNode(meshCtx, a);
4180
5420
  break;
5421
+ case "mesh_refine_config_schema":
5422
+ text = await meshRefineConfigSchema(meshCtx);
5423
+ break;
5424
+ case "mesh_validate_refine_config":
5425
+ text = await meshValidateRefineConfig(meshCtx, a);
5426
+ break;
5427
+ case "mesh_suggest_refine_config":
5428
+ text = await meshSuggestRefineConfig(meshCtx, a);
5429
+ break;
5430
+ case "mesh_refine_plan":
5431
+ text = await meshRefinePlan(meshCtx, a);
5432
+ break;
4181
5433
  case "mesh_cleanup_sessions":
4182
5434
  text = await meshCleanupSessions(meshCtx, a);
4183
5435
  break;