@adhdev/daemon-standalone 0.9.82-rc.1 → 0.9.82-rc.100

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,21 @@ __export(index_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
+ // src/tools/mesh-tools.ts
39
+ var import_node_crypto = require("crypto");
40
+
38
41
  // src/transports/ipc.ts
39
42
  var DEFAULT_IPC_PORT = 19222;
40
43
  var DEFAULT_IPC_PATH = "/ipc";
44
+ var DEFAULT_IPC_COMMAND_TIMEOUT_MS = 15e3;
45
+ var IPC_COMMAND_TIMEOUTS_MS = {
46
+ mesh_relay_command: 12e4,
47
+ agent_command: 3e4,
48
+ git_status: 45e3,
49
+ git_diff_summary: 45e3,
50
+ fast_forward_mesh_node: 12e4,
51
+ mesh_status: 12e4
52
+ };
41
53
  var IpcTransport = class {
42
54
  port;
43
55
  path;
@@ -85,9 +97,22 @@ var IpcTransport = class {
85
97
  }
86
98
  fn();
87
99
  };
88
- const timeoutMs = type === "mesh_relay_command" ? 6e4 : 15e3;
100
+ const nestedCommand = typeof args?.command === "string" ? args.command : "";
101
+ const targetDaemonId = typeof args?.targetDaemonId === "string" ? args.targetDaemonId : "";
102
+ const effectiveType = type === "mesh_relay_command" && nestedCommand ? nestedCommand : type;
103
+ const timeoutMs = Math.max(
104
+ IPC_COMMAND_TIMEOUTS_MS[type] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS,
105
+ IPC_COMMAND_TIMEOUTS_MS[effectiveType] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS
106
+ );
107
+ const diagnosticParts = [
108
+ `command='${type}'`,
109
+ ...nestedCommand ? [`relayedCommand='${nestedCommand}'`] : [],
110
+ ...targetDaemonId ? [`targetDaemonId='${targetDaemonId.slice(0, 12)}'`] : [],
111
+ ...typeof args?.nodeId === "string" ? [`nodeId='${args.nodeId}'`] : [],
112
+ ...typeof args?.workspace === "string" ? [`workspace='${args.workspace}'`] : []
113
+ ];
89
114
  const timeout = setTimeout(() => {
90
- finish(() => reject(new Error(`Daemon IPC command '${type}' timed out after ${Math.round(timeoutMs / 1e3)}s`)));
115
+ finish(() => reject(new Error(`Daemon IPC ${diagnosticParts.join(" ")} timed out after ${Math.round(timeoutMs / 1e3)}s (requestId=${requestId})`)));
91
116
  }, timeoutMs);
92
117
  let commandSent = false;
93
118
  const send = () => {
@@ -143,6 +168,10 @@ function isLocalTransport(transport) {
143
168
  }
144
169
 
145
170
  // src/tools/chat-compact.ts
171
+ function isAssistantLike(message) {
172
+ const role = String(message?.role ?? "").toLowerCase();
173
+ return role === "assistant" || role === "agent";
174
+ }
146
175
  function messageContent(message) {
147
176
  const content = message?.content;
148
177
  if (typeof content === "string") return content;
@@ -161,16 +190,22 @@ function isCoordinatorVisibleMessage(message) {
161
190
  if (meta?.internal === true || meta?.debug === true || meta?.control === true || meta?.userVisible === false || meta?.user_visible === false) return false;
162
191
  return role === "user" || role === "assistant" || role === "agent";
163
192
  }
193
+ function buildCompactMessageTail(visibleMessages, opts) {
194
+ const summary = typeof opts.summary === "string" ? opts.summary.trim() : "";
195
+ const shouldOmitSummaryMessage = !!summary && !!opts.finalAssistant && isAssistantLike(opts.finalAssistant) && messageContent(opts.finalAssistant).trim() === summary;
196
+ const sourceMessages = shouldOmitSummaryMessage ? visibleMessages.filter((message) => message !== opts.finalAssistant) : visibleMessages;
197
+ return sourceMessages.slice(-opts.limit);
198
+ }
164
199
  function compactChatPayload(payload, opts = {}) {
165
200
  const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
166
201
  const visible = rawMessages.filter(isCoordinatorVisibleMessage);
167
202
  const limit = Math.max(1, Math.min(opts.limit ?? 10, 10));
168
- const messages = visible.slice(-limit);
169
203
  const finalAssistant = [...visible].reverse().find((message) => {
170
204
  const role = String(message?.role ?? "").toLowerCase();
171
205
  return (role === "assistant" || role === "agent") && messageContent(message).trim();
172
206
  });
173
207
  const summary = typeof payload?.summary === "string" && payload.summary.trim() ? payload.summary.trim() : messageContent(finalAssistant).trim();
208
+ const messages = buildCompactMessageTail(visible, { summary, finalAssistant, limit });
174
209
  return {
175
210
  success: payload?.success !== false,
176
211
  compact: true,
@@ -232,9 +267,45 @@ function annotateRapidReadChatAdvisory(payload, options) {
232
267
  // src/tools/mesh-tools.ts
233
268
  var import_daemon_core = require("@adhdev/daemon-core");
234
269
  var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
270
+ var ACTIVE_WORK_POLLING_BACKOFF_MS = 6e4;
271
+ function buildActiveWorkPollingGuidance(summary, now = Date.now()) {
272
+ if (!summary || summary.generatingCount <= 0) return void 0;
273
+ return {
274
+ activeGeneratingWork: true,
275
+ generatingCount: summary.generatingCount,
276
+ doNotPollBefore: new Date(now + ACTIVE_WORK_POLLING_BACKOFF_MS).toISOString(),
277
+ eventSurface: "pendingCoordinatorEvents",
278
+ nextRecommendedAction: "Wait for pendingCoordinatorEvents/completion events or an explicit user status request. After a terminal signal, call mesh_read_chat once with compact=true, then verify git state if repository changes were expected.",
279
+ message: "Do not repeatedly poll mesh_status/mesh_view_queue/mesh_read_chat while delegated work is generating; these snapshots rarely change until the worker emits a completion/status event."
280
+ };
281
+ }
235
282
  function readString(value) {
236
283
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
237
284
  }
285
+ function summarizeTaskMessage(message) {
286
+ const taskSummary = message.replace(/\s+/g, " ").trim();
287
+ const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
288
+ return { taskTitle: taskTitle || "(untitled task)", taskSummary };
289
+ }
290
+ function buildDirectTaskPayload(message, via, opts) {
291
+ const descriptor = summarizeTaskMessage(message);
292
+ return {
293
+ source: "direct",
294
+ via,
295
+ taskId: opts.taskId,
296
+ message,
297
+ taskTitle: descriptor.taskTitle,
298
+ taskSummary: descriptor.taskSummary,
299
+ ...opts.taskMode ? { taskMode: opts.taskMode } : {},
300
+ ...opts.providerType ? { providerType: opts.providerType } : {},
301
+ ...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {}
302
+ };
303
+ }
304
+ function findNode(mesh, nodeId) {
305
+ const node = mesh.nodes.find((n) => n.id === nodeId);
306
+ if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
307
+ return node;
308
+ }
238
309
  var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
239
310
  var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
240
311
  var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
@@ -246,15 +317,24 @@ async function refreshMeshFromDaemon(ctx) {
246
317
  const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
247
318
  if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
248
319
  const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
249
- if (!refreshedNodes.length) return;
250
320
  ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
251
321
  ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
252
322
  } catch {
253
323
  }
254
324
  }
325
+ async function syncCoordinatorDaemonMeshCache(ctx) {
326
+ if (!(ctx.transport instanceof IpcTransport)) return;
327
+ try {
328
+ await ctx.transport.command("get_mesh", {
329
+ meshId: ctx.mesh.id,
330
+ inlineMesh: ctx.mesh
331
+ });
332
+ } catch {
333
+ }
334
+ }
255
335
  async function findNodeWithRefresh(ctx, nodeId) {
256
336
  const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
257
- if (hit) return hit;
337
+ if (hit && !hit.isLocalWorktree) return hit;
258
338
  await refreshMeshFromDaemon(ctx);
259
339
  const refreshed = ctx.mesh.nodes.find((n) => n.id === nodeId);
260
340
  if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
@@ -262,7 +342,7 @@ async function findNodeWithRefresh(ctx, nodeId) {
262
342
  }
263
343
  async function findOptionalNodeWithRefresh(ctx, nodeId) {
264
344
  const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
265
- if (hit) return hit;
345
+ if (hit && !hit.isLocalWorktree) return hit;
266
346
  await refreshMeshFromDaemon(ctx);
267
347
  return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
268
348
  }
@@ -314,9 +394,26 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
314
394
  readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
315
395
  };
316
396
  if (finalSummary) {
397
+ if (args.compact === true) {
398
+ return {
399
+ ...compactChatPayload({
400
+ success: true,
401
+ status: "idle",
402
+ providerSessionId,
403
+ summary: finalSummary,
404
+ messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
405
+ }, {
406
+ nodeId: args.node_id,
407
+ sessionId: args.session_id,
408
+ limit: args.tail ?? 10
409
+ }),
410
+ recoveredFromLedger: true,
411
+ ledger
412
+ };
413
+ }
317
414
  return {
318
415
  success: true,
319
- compact: args.compact === true,
416
+ compact: false,
320
417
  recoveredFromLedger: true,
321
418
  nodeId: args.node_id,
322
419
  sessionId: args.session_id,
@@ -368,6 +465,22 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
368
465
  function readSessionRecordId(session) {
369
466
  return readString(session?.id) || readString(session?.sessionId) || readString(session?.session_id) || readString(session?.runtimeSessionId) || readString(session?.runtime_session_id) || readString(session?.instanceId) || readString(session?.instance_id);
370
467
  }
468
+ function extractStatusMetadataSessions(value) {
469
+ const payload = unwrapCommandPayload(value);
470
+ const status = payload?.status && typeof payload.status === "object" ? payload.status : payload;
471
+ return Array.isArray(status?.sessions) ? status.sessions : [];
472
+ }
473
+ function resolveSessionProviderType(session) {
474
+ return readString(session?.providerType) || readString(session?.cliType) || readString(session?.agentType) || "";
475
+ }
476
+ function isMeshCoordinatorSessionRecord(session) {
477
+ return Boolean(
478
+ readString(session?.settings?.meshCoordinatorFor) || readString(session?.meta?.meshCoordinatorFor) || readString(session?.metadata?.meshCoordinatorFor) || readString(session?.meshCoordinatorFor)
479
+ );
480
+ }
481
+ function isWorkerTaskMode(taskMode) {
482
+ return taskMode !== "live_debug_readonly";
483
+ }
371
484
  function addSessionRecord(target, session) {
372
485
  if (!session || typeof session !== "object" || isTerminalSessionRecord(session)) return;
373
486
  const sessionId = readSessionRecordId(session);
@@ -436,18 +549,26 @@ function queueAssignmentStaleReason(task, liveness) {
436
549
  }
437
550
  function buildQueueStatusSummary(queue) {
438
551
  const counts = { pending: 0, assigned: 0, completed: 0, failed: 0, cancelled: 0 };
552
+ let staleAssigned = 0;
439
553
  for (const task of queue) {
440
554
  const status = typeof task?.status === "string" ? task.status : void 0;
441
555
  if (status && Object.prototype.hasOwnProperty.call(counts, status)) {
442
556
  counts[status] += 1;
443
557
  }
558
+ if (status === "assigned" && task?.staleAssigned === true) staleAssigned += 1;
444
559
  }
560
+ const liveAssigned = Math.max(0, counts.assigned - staleAssigned);
445
561
  return {
446
562
  totalCount: queue.length,
447
- activeCount: counts.pending + counts.assigned,
563
+ activeCount: counts.pending + liveAssigned,
448
564
  historicalCount: counts.completed + counts.failed + counts.cancelled,
449
565
  counts,
450
566
  activeCounts: {
567
+ pending: counts.pending,
568
+ assigned: liveAssigned
569
+ },
570
+ staleAssignedCount: staleAssigned,
571
+ rawActiveCounts: {
451
572
  pending: counts.pending,
452
573
  assigned: counts.assigned
453
574
  },
@@ -475,6 +596,18 @@ function filterQueueForView(queue, view, statuses) {
475
596
  if (view === "historical") return queue.filter((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
476
597
  return queue;
477
598
  }
599
+ function prioritizeActiveQueueRows(queue) {
600
+ const active = [];
601
+ const historical = [];
602
+ const other = [];
603
+ for (const task of queue) {
604
+ const status = String(task?.status || "");
605
+ if (ACTIVE_QUEUE_STATUSES.has(status)) active.push(task);
606
+ else if (HISTORICAL_QUEUE_STATUSES.has(status)) historical.push(task);
607
+ else other.push(task);
608
+ }
609
+ return [...active, ...other, ...historical];
610
+ }
478
611
  function slimQueueTask(task) {
479
612
  return {
480
613
  id: task?.id,
@@ -580,13 +713,58 @@ function isIdleSessionRecord(session) {
580
713
  const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
581
714
  return status === "idle" || chatStatus === "waiting_input";
582
715
  }
716
+ function isMeshOwnedDelegateSession(session, meshId, nodeId) {
717
+ const settings = session?.settings;
718
+ const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
719
+ const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
720
+ const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
721
+ if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
722
+ return !sessionNodeId || sessionNodeId === nodeId;
723
+ }
583
724
  function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
584
725
  const live = sessions.filter((session) => !isTerminalSessionRecord(session));
585
726
  const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
586
727
  const meshSessions = live.filter(
587
- (session) => session?.settings?.meshNodeFor === meshId || session?.settings?.meshNodeId === nodeId
728
+ (session) => isMeshOwnedDelegateSession(session, meshId, nodeId)
588
729
  );
589
- return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || live.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || live.find(matchingProvider) || live.find(isIdleSessionRecord) || live[0];
730
+ return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || void 0;
731
+ }
732
+ function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
733
+ return {
734
+ success: false,
735
+ recoverable: true,
736
+ code: "mesh_delegate_session_missing_relay_metadata",
737
+ reason: "mesh_delegate_session_missing_relay_metadata",
738
+ transport: "mesh_transport",
739
+ retryRecommended: true,
740
+ meshId: ctx.mesh.id,
741
+ nodeId: node.id,
742
+ daemonId: node.daemonId,
743
+ workspace: node.workspace,
744
+ sessionId,
745
+ ...providerType ? { resolvedProviderType: providerType } : {},
746
+ error: `Remote session '${sessionId}' is not relay-safe for mesh '${ctx.mesh.id}': missing meshNodeFor/meshCoordinatorDaemonId metadata, so completion events would not reach the coordinator ledger.`,
747
+ nextAction: `Launch a fresh relay-safe session with mesh_launch_session(node_id: '${node.id}'${providerType ? `, type: '${providerType}'` : ""}) or dispatch without session_id so Repo Mesh can choose a valid delegate session.`,
748
+ noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
749
+ };
750
+ }
751
+ function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
752
+ return {
753
+ success: false,
754
+ recoverable: true,
755
+ code: "mesh_coordinator_daemon_unknown",
756
+ reason: "mesh_coordinator_daemon_unknown",
757
+ transport: "mesh_transport",
758
+ retryRecommended: true,
759
+ meshId: ctx.mesh.id,
760
+ nodeId: node.id,
761
+ daemonId: node.daemonId,
762
+ workspace: node.workspace,
763
+ ...providerType ? { resolvedProviderType: providerType } : {},
764
+ error: `Cannot launch a remote mesh delegate for node '${node.id}': coordinator daemon identity is unavailable, so the worker would be unable to relay completion events back to the coordinator.`,
765
+ nextAction: "Retry after the coordinator daemon identity is available (for example from an attached daemon-backed MCP session) so meshCoordinatorDaemonId can be stamped on the worker session.",
766
+ noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
767
+ };
590
768
  }
591
769
  function findNestedPayload(value, predicate) {
592
770
  const seen = /* @__PURE__ */ new Set();
@@ -615,12 +793,16 @@ function extractGitDiff(value) {
615
793
  }
616
794
  function extractSubmodules(value, ignorePaths) {
617
795
  const payload = unwrapCommandPayload(value);
618
- const subs = payload?.submodules ?? value?.submodules;
796
+ const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
619
797
  if (!Array.isArray(subs)) return void 0;
620
798
  if (ignorePaths.length === 0) return subs;
621
799
  const ignoreSet = new Set(ignorePaths);
622
800
  return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
623
801
  }
802
+ function assignFullGitSnapshot(entry, status) {
803
+ if (!status || typeof status !== "object" || Array.isArray(status)) return;
804
+ entry.git = status;
805
+ }
624
806
  function extractLaunchPayload(value) {
625
807
  return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
626
808
  }
@@ -745,20 +927,76 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
745
927
  let sessionId = args.session_id?.trim() || "";
746
928
  const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
747
929
  let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
748
- if (!sessionId) {
930
+ if (sessionId && args.verifiedSession) {
931
+ const explicitSession = args.verifiedSession;
932
+ if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
933
+ return buildRelayUnsafeRemoteSessionFailure(
934
+ ctx,
935
+ node,
936
+ sessionId,
937
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
938
+ );
939
+ }
940
+ if (!resolvedProviderType) {
941
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
942
+ }
943
+ } else if (!sessionId || args.session_id) {
749
944
  try {
750
945
  const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
751
- const innerResult = relayResult?.result ?? relayResult;
752
- const statusObj = innerResult?.status ?? innerResult;
753
- const sessions = Array.isArray(statusObj?.sessions) ? statusObj.sessions : [];
754
- const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
755
- if (targetSession?.id || targetSession?.sessionId) {
756
- sessionId = targetSession.id || targetSession.sessionId;
946
+ const sessions = extractStatusMetadataSessions(relayResult);
947
+ if (sessionId) {
948
+ const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
949
+ if (!explicitSession) {
950
+ return {
951
+ success: false,
952
+ recoverable: true,
953
+ code: "mesh_target_session_not_found",
954
+ reason: "mesh_target_session_not_found",
955
+ transport: "mesh_transport",
956
+ retryRecommended: true,
957
+ meshId: ctx.mesh.id,
958
+ nodeId: node.id,
959
+ daemonId,
960
+ workspace: node.workspace,
961
+ sessionId,
962
+ ...resolvedProviderType ? { resolvedProviderType } : {},
963
+ error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
964
+ nextAction: `Launch a fresh session with mesh_launch_session(node_id: '${node.id}'${resolvedProviderType ? `, type: '${resolvedProviderType}'` : ""}) or retry without session_id so Repo Mesh can target a live delegate session.`
965
+ };
966
+ }
967
+ if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
968
+ return buildRelayUnsafeRemoteSessionFailure(
969
+ ctx,
970
+ node,
971
+ sessionId,
972
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
973
+ );
974
+ }
757
975
  if (!resolvedProviderType) {
758
- resolvedProviderType = targetSession.providerType || targetSession.cliType || "";
976
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
977
+ }
978
+ } else {
979
+ const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
980
+ if (targetSession?.id || targetSession?.sessionId) {
981
+ sessionId = targetSession.id || targetSession.sessionId;
982
+ if (!resolvedProviderType) {
983
+ resolvedProviderType = resolveSessionProviderType(targetSession);
984
+ }
759
985
  }
760
986
  }
761
987
  } catch (e) {
988
+ if (sessionId) {
989
+ return {
990
+ ...buildCoordinatorP2pRelayFailure(e, {
991
+ command: "get_status_metadata",
992
+ targetDaemonId: daemonId,
993
+ nodeId: node.id,
994
+ sessionId
995
+ }),
996
+ success: false,
997
+ error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
998
+ };
999
+ }
762
1000
  }
763
1001
  }
764
1002
  if (!resolvedProviderType) {
@@ -788,7 +1026,7 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
788
1026
  error: `P2P dispatch failed: ${errorMessage}`
789
1027
  };
790
1028
  }
791
- return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
1029
+ return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType, providerType: resolvedProviderType };
792
1030
  } catch (e) {
793
1031
  const errorMessage = e?.message || String(e);
794
1032
  return {
@@ -818,10 +1056,60 @@ function resolveCoordinatorNode(ctx) {
818
1056
  return void 0;
819
1057
  }
820
1058
  function readNodeMachineId(node) {
821
- return readString(node.machineId) || readString(node.machine_id);
1059
+ return readString(node.machineId) || readString(node.machine_id) || readString(node.machine?.id) || readString(node.machine?.machineId) || readString(node.lastProbe?.machineId) || readString(node.last_probe?.machine_id) || readString(node.lastProbe?.machine?.id) || readString(node.lastProbe?.machine?.machineId) || readString(node.last_probe?.machine?.id) || readString(node.last_probe?.machine?.machine_id);
822
1060
  }
823
1061
  function readNodeDaemonId(node) {
824
- return readString(node.daemonId) || readString(node.daemon_id);
1062
+ return readString(node.daemonId) || readString(node.daemon_id) || readString(node.machine?.daemonId) || readString(node.machine?.daemon_id) || readString(node.lastProbe?.daemonId) || readString(node.last_probe?.daemon_id) || readString(node.lastProbe?.machine?.daemonId) || readString(node.lastProbe?.machine?.daemon_id) || readString(node.last_probe?.machine?.daemonId) || readString(node.last_probe?.machine?.daemon_id);
1063
+ }
1064
+ function normalizeHostname(value) {
1065
+ const hostname = readString(value);
1066
+ if (!hostname) return void 0;
1067
+ return hostname.toLowerCase().replace(/\.$/, "");
1068
+ }
1069
+ function readNodeHostname(node) {
1070
+ return readString(node.hostname) || readString(node.host) || readString(node.machineHostname) || readString(node.machine_hostname) || readString(node.machine?.hostname) || readString(node.machine?.host) || readString(node.lastProbe?.hostname) || readString(node.last_probe?.hostname) || readString(node.lastProbe?.machine?.hostname) || readString(node.last_probe?.machine?.hostname);
1071
+ }
1072
+ function readNodeDisplayMachineName(node) {
1073
+ return readString(node.machineName) || readString(node.machine_name) || readString(node.machineLabel) || readString(node.machine_label) || readString(node.machineNickname) || readString(node.machine_nickname) || readString(node.alias) || readString(node.machine?.name) || readString(node.machine?.displayName) || readString(node.machine?.display_name) || readString(node.lastProbe?.machineName) || readString(node.last_probe?.machine_name) || readString(node.lastProbe?.machine?.name) || readString(node.last_probe?.machine?.name) || readNodeHostname(node);
1074
+ }
1075
+ function compactIdentityEvidence(value) {
1076
+ if (!value) return void 0;
1077
+ return value.length > 24 ? `${value.slice(0, 12)}\u2026${value.slice(-8)}` : value;
1078
+ }
1079
+ function pushIdentityEvidence(evidence, label, value) {
1080
+ const compact = compactIdentityEvidence(value);
1081
+ if (compact) evidence.push(`${label}:${compact}`);
1082
+ }
1083
+ function buildNodeMachineIdentity(ctx, node) {
1084
+ const machineId = readNodeMachineId(node);
1085
+ const daemonId = readNodeDaemonId(node);
1086
+ const hostname = readNodeHostname(node);
1087
+ const machineName = readNodeDisplayMachineName(node);
1088
+ const coordinatorHostname = readString(ctx.coordinatorHostname);
1089
+ const directLocal = isLocalControlPlaneNode(ctx, node);
1090
+ const hostnameMatches = Boolean(
1091
+ normalizeHostname(hostname) && normalizeHostname(coordinatorHostname) && normalizeHostname(hostname) === normalizeHostname(coordinatorHostname)
1092
+ );
1093
+ const sameMachine = directLocal || hostnameMatches;
1094
+ const evidence = [];
1095
+ pushIdentityEvidence(evidence, "machineName", machineName);
1096
+ pushIdentityEvidence(evidence, "hostname", hostname);
1097
+ pushIdentityEvidence(evidence, "machineId", machineId);
1098
+ pushIdentityEvidence(evidence, "daemonId", daemonId);
1099
+ const locality = sameMachine ? "same_machine" : evidence.length > 0 ? "remote_known" : "remote_or_unknown";
1100
+ const localityReason = sameMachine ? directLocal ? "matched coordinator daemon or machine id" : "matched coordinator hostname" : evidence.length > 0 ? `known remote/other machine identity; no local coordinator match (${evidence.join(", ")})` : "no useful machine identity evidence available";
1101
+ return {
1102
+ daemonId,
1103
+ machineId,
1104
+ hostname,
1105
+ machineName,
1106
+ displayName: machineName || hostname || daemonId || machineId,
1107
+ coordinatorHostname,
1108
+ sameMachine,
1109
+ locality,
1110
+ localityReason,
1111
+ identityEvidence: evidence
1112
+ };
825
1113
  }
826
1114
  function isDirectLocalNode(ctx, node) {
827
1115
  const machineId = readNodeMachineId(node);
@@ -846,6 +1134,54 @@ function isLocalControlPlaneNode(ctx, node) {
846
1134
  function meshSessionCacheKey(nodeId, runtimeSessionId) {
847
1135
  return `${nodeId}:${runtimeSessionId}`;
848
1136
  }
1137
+ function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata) {
1138
+ const keyNodeId = readString(nodeId);
1139
+ const keySessionId = readString(runtimeSessionId);
1140
+ if (!keyNodeId || !keySessionId) return;
1141
+ const providerType = readString(metadata.providerType);
1142
+ const providerSessionId = readString(metadata.providerSessionId);
1143
+ if (!providerType && !providerSessionId) return;
1144
+ const existing = meshSessionProviderMetadata.get(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
1145
+ meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
1146
+ providerType: providerType || existing.providerType,
1147
+ providerSessionId: providerSessionId || existing.providerSessionId
1148
+ });
1149
+ }
1150
+ function rememberMeshSessionProviderMetadataFromEvent(event) {
1151
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : event && typeof event === "object" ? event : {};
1152
+ const nodeId = readString(event?.nodeId) || readString(metadataEvent.nodeId) || readString(metadataEvent.meshNodeId);
1153
+ const sessionId = readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId) || readString(event?.sessionId);
1154
+ rememberMeshSessionProviderMetadata(nodeId, sessionId, {
1155
+ providerType: readString(metadataEvent.providerType) || readString(event?.providerType) || "",
1156
+ providerSessionId: readString(metadataEvent.providerSessionId) || readString(event?.providerSessionId)
1157
+ });
1158
+ }
1159
+ function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
1160
+ const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 });
1161
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
1162
+ const entry = entries[i];
1163
+ const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
1164
+ const entryNodeId = readString(entry.nodeId) || readString(payload.nodeId) || readString(payload.meshNodeId);
1165
+ if (entryNodeId && entryNodeId !== nodeId) continue;
1166
+ const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
1167
+ if (entrySessionId !== runtimeSessionId) continue;
1168
+ const providerType = readString(entry.providerType) || readString(payload.providerType);
1169
+ const completionDiagnostic = payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? payload.completionDiagnostic : {};
1170
+ const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : {};
1171
+ const providerSessionId = readString(payload.providerSessionId) || readString(completionDiagnostic.providerSessionId) || readString(metadataEvent.providerSessionId);
1172
+ if (providerType || providerSessionId) {
1173
+ return { providerType: providerType || "", providerSessionId };
1174
+ }
1175
+ }
1176
+ return void 0;
1177
+ }
1178
+ function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
1179
+ const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(nodeId, runtimeSessionId));
1180
+ if (cached?.providerType || cached?.providerSessionId) return cached;
1181
+ const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
1182
+ if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
1183
+ return fromLedger;
1184
+ }
849
1185
  function countUncommittedChanges(status) {
850
1186
  if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
851
1187
  const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
@@ -872,6 +1208,10 @@ function summarizeRelatedRepoStatus(repo, status) {
872
1208
  workspace: repo.workspace,
873
1209
  isGitRepo: status?.isGitRepo === true,
874
1210
  branch: status?.branch ?? null,
1211
+ upstream: status?.upstream ?? null,
1212
+ upstreamStatus: typeof status?.upstreamStatus === "string" ? status.upstreamStatus : status?.upstream ? "unchecked" : "no_upstream",
1213
+ upstreamFetchedAt: Number.isFinite(Number(status?.upstreamFetchedAt)) ? Number(status.upstreamFetchedAt) : null,
1214
+ upstreamFetchError: typeof status?.upstreamFetchError === "string" ? status.upstreamFetchError : null,
875
1215
  ahead: Number.isFinite(Number(status?.ahead)) ? Number(status.ahead) : 0,
876
1216
  behind: Number.isFinite(Number(status?.behind)) ? Number(status.behind) : 0,
877
1217
  dirty,
@@ -888,7 +1228,7 @@ async function collectRelatedRepoStatuses(ctx, node) {
888
1228
  const results = [];
889
1229
  for (const repo of relatedRepos) {
890
1230
  try {
891
- const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace });
1231
+ const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false, true) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace, refreshUpstream: true });
892
1232
  const status = extractGitStatus(statusResult);
893
1233
  results.push(summarizeRelatedRepoStatus(repo, status));
894
1234
  } catch (e) {
@@ -926,6 +1266,14 @@ function getNodeLaunchReadiness(node) {
926
1266
  launchBlockedMessage: missingProviderPriorityMessage(node.id)
927
1267
  };
928
1268
  }
1269
+ async function collectLiveStatusSessions(ctx, node) {
1270
+ try {
1271
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
1272
+ return extractStatusMetadataSessions(statusResult);
1273
+ } catch {
1274
+ return [];
1275
+ }
1276
+ }
929
1277
  function readNumeric(value, fallback = 0) {
930
1278
  const parsed = Number(value);
931
1279
  return Number.isFinite(parsed) ? parsed : fallback;
@@ -936,11 +1284,13 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
936
1284
  const ahead = readNumeric(status?.ahead);
937
1285
  const behind = readNumeric(status?.behind);
938
1286
  const upstream = readString(status?.upstream) ?? null;
1287
+ const upstreamStatus = readString(status?.upstreamStatus) ?? (upstream ? "unchecked" : "no_upstream");
939
1288
  const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
940
1289
  const base = {
941
1290
  defaultBranch,
942
1291
  branch,
943
1292
  upstream,
1293
+ upstreamStatus,
944
1294
  ahead,
945
1295
  behind,
946
1296
  isWorktree: node.isLocalWorktree === true,
@@ -974,6 +1324,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
974
1324
  };
975
1325
  }
976
1326
  if (branch === defaultBranch) {
1327
+ if (upstream && upstreamStatus !== "fresh") {
1328
+ return {
1329
+ ...base,
1330
+ status: "blocked_review",
1331
+ needsConvergence: true,
1332
+ reason: "default_branch_upstream_unverified",
1333
+ nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${node.id}'.`
1334
+ };
1335
+ }
977
1336
  if (ahead > 0 || behind > 0) {
978
1337
  return {
979
1338
  ...base,
@@ -1000,6 +1359,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
1000
1359
  nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
1001
1360
  };
1002
1361
  }
1362
+ if (upstream && upstreamStatus !== "fresh") {
1363
+ return {
1364
+ ...base,
1365
+ status: "blocked_review",
1366
+ needsConvergence: true,
1367
+ reason: "feature_branch_upstream_unverified",
1368
+ nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`
1369
+ };
1370
+ }
1003
1371
  if (!upstream || ahead > 0 || behind > 0) {
1004
1372
  return {
1005
1373
  ...base,
@@ -1041,7 +1409,86 @@ async function commandForNode(ctx, node, command, args = {}) {
1041
1409
  if (isLocalTransport(ctx.transport)) {
1042
1410
  return ctx.transport.command(command, args);
1043
1411
  }
1044
- throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
1412
+ const identity = buildNodeMachineIdentity(ctx, node);
1413
+ 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})`);
1414
+ }
1415
+ function normalizePendingMeshCoordinatorEvents(value) {
1416
+ const payload = unwrapCommandPayload(value);
1417
+ const events = Array.isArray(payload?.events) ? payload.events : Array.isArray(value?.events) ? value.events : [];
1418
+ return events.filter((event) => event && typeof event === "object");
1419
+ }
1420
+ function buildMeshForwardPayloadFromPendingEvent(event) {
1421
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : {};
1422
+ return {
1423
+ event: readString(event?.event),
1424
+ meshId: readString(event?.meshId),
1425
+ nodeId: readString(event?.nodeId) || readString(metadataEvent.meshNodeId),
1426
+ workspace: readString(event?.workspace) || readString(metadataEvent.workspace),
1427
+ targetSessionId: readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId),
1428
+ providerType: readString(metadataEvent.providerType),
1429
+ providerSessionId: readString(metadataEvent.providerSessionId),
1430
+ finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
1431
+ jobId: readString(metadataEvent.jobId),
1432
+ interactionId: readString(metadataEvent.interactionId),
1433
+ status: readString(metadataEvent.status),
1434
+ targetDaemonId: readString(metadataEvent.targetDaemonId),
1435
+ startedAt: readString(metadataEvent.startedAt),
1436
+ completedAt: readString(metadataEvent.completedAt),
1437
+ retryOfJobId: readString(metadataEvent.retryOfJobId),
1438
+ ...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
1439
+ ...metadataEvent.intentional === true ? { intentional: true } : {},
1440
+ ...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
1441
+ ...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
1442
+ ...readString(metadataEvent.reason) ? { reason: readString(metadataEvent.reason) } : {},
1443
+ ...readString(metadataEvent.stopReason) ? { stopReason: readString(metadataEvent.stopReason) } : {},
1444
+ ...readString(metadataEvent.cleanupReason) ? { cleanupReason: readString(metadataEvent.cleanupReason) } : {},
1445
+ ...readString(metadataEvent.source) ? { source: readString(metadataEvent.source) } : {}
1446
+ };
1447
+ }
1448
+ async function drainCoordinatorPendingEvents(ctx, opts) {
1449
+ const requestedNodeIds = opts?.nodeIds?.length ? new Set(opts.nodeIds) : null;
1450
+ const matchesCurrentMesh = (event) => readString(event?.meshId) === ctx.mesh.id;
1451
+ if (ctx.transport instanceof IpcTransport) {
1452
+ const surfacedEvents = [];
1453
+ try {
1454
+ surfacedEvents.push(
1455
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1456
+ );
1457
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1458
+ } catch {
1459
+ }
1460
+ for (const node of ctx.mesh.nodes) {
1461
+ if (!node.daemonId || isLocalControlPlaneNode(ctx, node)) continue;
1462
+ if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
1463
+ try {
1464
+ const remoteEvents = normalizePendingMeshCoordinatorEvents(
1465
+ await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", { meshId: ctx.mesh.id })
1466
+ ).filter(matchesCurrentMesh);
1467
+ if (remoteEvents.length === 0) continue;
1468
+ for (const event of remoteEvents) {
1469
+ const payload = buildMeshForwardPayloadFromPendingEvent(event);
1470
+ if (!payload.event || !payload.meshId) continue;
1471
+ await ctx.transport.command("mesh_forward_event", payload);
1472
+ rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
1473
+ }
1474
+ } catch {
1475
+ }
1476
+ }
1477
+ try {
1478
+ surfacedEvents.push(
1479
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1480
+ );
1481
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1482
+ } catch {
1483
+ }
1484
+ return surfacedEvents;
1485
+ }
1486
+ if (isLocalTransport(ctx.transport)) {
1487
+ const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id).filter(matchesCurrentMesh);
1488
+ events.forEach(rememberMeshSessionProviderMetadataFromEvent);
1489
+ return events;
1490
+ }
1491
+ return [];
1045
1492
  }
1046
1493
  function isP2pTransportUnavailableError(error) {
1047
1494
  return (0, import_daemon_core.isP2pRelayTransportFailure)(error);
@@ -1056,7 +1503,7 @@ function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
1056
1503
  }
1057
1504
  var MESH_STATUS_TOOL = {
1058
1505
  name: "mesh_status",
1059
- 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.",
1506
+ 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.",
1060
1507
  inputSchema: {
1061
1508
  type: "object",
1062
1509
  properties: {
@@ -1080,14 +1527,16 @@ var MESH_ENQUEUE_TASK_TOOL = {
1080
1527
  inputSchema: {
1081
1528
  type: "object",
1082
1529
  properties: {
1083
- message: { type: "string", description: "The task instruction for the agent." }
1530
+ message: { type: "string", description: "The task instruction for the agent." },
1531
+ 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." },
1532
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
1084
1533
  },
1085
1534
  required: ["message"]
1086
1535
  }
1087
1536
  };
1088
1537
  var MESH_VIEW_QUEUE_TOOL = {
1089
1538
  name: "mesh_view_queue",
1090
- description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records.",
1539
+ 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.",
1091
1540
  inputSchema: {
1092
1541
  type: "object",
1093
1542
  properties: {
@@ -1140,7 +1589,9 @@ var MESH_SEND_TASK_TOOL = {
1140
1589
  properties: {
1141
1590
  node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
1142
1591
  session_id: { type: "string", description: "Agent session ID on the target node." },
1143
- message: { type: "string", description: "Natural-language task to send to the agent." }
1592
+ message: { type: "string", description: "Natural-language task to send to the agent." },
1593
+ 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." },
1594
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
1144
1595
  },
1145
1596
  required: ["node_id", "session_id", "message"]
1146
1597
  }
@@ -1198,6 +1649,21 @@ var MESH_GIT_STATUS_TOOL = {
1198
1649
  required: ["node_id"]
1199
1650
  }
1200
1651
  };
1652
+ var MESH_FAST_FORWARD_NODE_TOOL = {
1653
+ name: "mesh_fast_forward_node",
1654
+ 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.",
1655
+ inputSchema: {
1656
+ type: "object",
1657
+ properties: {
1658
+ node_id: { type: "string", description: "Target node ID." },
1659
+ branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
1660
+ execute: { type: "boolean", description: "When true, apply the fast-forward if all safety gates pass. Defaults false/dry-run." },
1661
+ dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
1662
+ 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." }
1663
+ },
1664
+ required: ["node_id"]
1665
+ }
1666
+ };
1201
1667
  var MESH_CHECKPOINT_TOOL = {
1202
1668
  name: "mesh_checkpoint",
1203
1669
  description: "Create a git checkpoint (commit) on a mesh node workspace.",
@@ -1281,7 +1747,7 @@ var MESH_TASK_HISTORY_TOOL = {
1281
1747
  type: "object",
1282
1748
  properties: {
1283
1749
  tail: { type: "number", description: "Number of recent entries to return (default: 20)." },
1284
- kind: { type: "string", description: "Filter by entry kind: task_dispatched, task_completed, task_failed, task_stalled, session_launched, checkpoint_created, node_cloned, node_removed." }
1750
+ 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." }
1285
1751
  }
1286
1752
  }
1287
1753
  };
@@ -1301,7 +1767,7 @@ var MESH_RECONCILE_LEDGER_TOOL = {
1301
1767
  };
1302
1768
  var MESH_REFINE_NODE_TOOL = {
1303
1769
  name: "mesh_refine_node",
1304
- 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.",
1770
+ 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.",
1305
1771
  inputSchema: {
1306
1772
  type: "object",
1307
1773
  properties: {
@@ -1310,6 +1776,43 @@ var MESH_REFINE_NODE_TOOL = {
1310
1776
  required: ["node_id"]
1311
1777
  }
1312
1778
  };
1779
+ var MESH_REFINE_CONFIG_SCHEMA_TOOL = {
1780
+ name: "mesh_refine_config_schema",
1781
+ 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.",
1782
+ inputSchema: { type: "object", properties: {} }
1783
+ };
1784
+ var MESH_VALIDATE_REFINE_CONFIG_TOOL = {
1785
+ name: "mesh_validate_refine_config",
1786
+ description: "Validate the repo mesh/refine config for a node/workspace without running validation commands or merging.",
1787
+ inputSchema: {
1788
+ type: "object",
1789
+ properties: {
1790
+ node_id: { type: "string", description: "Optional node/workspace whose refine config should be loaded. Defaults to the first mesh node." },
1791
+ config: { type: "object", description: "Optional inline config object to validate instead of loading from the repo." }
1792
+ }
1793
+ }
1794
+ };
1795
+ var MESH_SUGGEST_REFINE_CONFIG_TOOL = {
1796
+ name: "mesh_suggest_refine_config",
1797
+ description: "Suggest a repo mesh/refine config scaffold from project context/package scripts. Suggestions are never executed until saved as explicit refine config.",
1798
+ inputSchema: {
1799
+ type: "object",
1800
+ properties: {
1801
+ node_id: { type: "string", description: "Optional node/workspace used for suggestions. Defaults to the first mesh node." }
1802
+ }
1803
+ }
1804
+ };
1805
+ var MESH_REFINE_PLAN_TOOL = {
1806
+ name: "mesh_refine_plan",
1807
+ 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.",
1808
+ inputSchema: {
1809
+ type: "object",
1810
+ properties: {
1811
+ node_id: { type: "string", description: "Node ID of the worktree node to plan." }
1812
+ },
1813
+ required: ["node_id"]
1814
+ }
1815
+ };
1313
1816
  var ALL_MESH_TOOLS = [
1314
1817
  MESH_STATUS_TOOL,
1315
1818
  MESH_LIST_NODES_TOOL,
@@ -1322,11 +1825,16 @@ var ALL_MESH_TOOLS = [
1322
1825
  MESH_READ_DEBUG_TOOL,
1323
1826
  MESH_LAUNCH_SESSION_TOOL,
1324
1827
  MESH_GIT_STATUS_TOOL,
1828
+ MESH_FAST_FORWARD_NODE_TOOL,
1325
1829
  MESH_CHECKPOINT_TOOL,
1326
1830
  MESH_APPROVE_TOOL,
1327
1831
  MESH_CLONE_NODE_TOOL,
1328
1832
  MESH_REMOVE_NODE_TOOL,
1329
1833
  MESH_REFINE_NODE_TOOL,
1834
+ MESH_REFINE_CONFIG_SCHEMA_TOOL,
1835
+ MESH_VALIDATE_REFINE_CONFIG_TOOL,
1836
+ MESH_SUGGEST_REFINE_CONFIG_TOOL,
1837
+ MESH_REFINE_PLAN_TOOL,
1330
1838
  MESH_CLEANUP_SESSIONS_TOOL,
1331
1839
  MESH_TASK_HISTORY_TOOL,
1332
1840
  MESH_RECONCILE_LEDGER_TOOL
@@ -1340,15 +1848,19 @@ async function meshStatus(ctx) {
1340
1848
  const entry = {
1341
1849
  nodeId: node.id,
1342
1850
  workspace: node.workspace,
1851
+ machine: buildNodeMachineIdentity(ctx, node),
1852
+ daemonId: readNodeDaemonId(node),
1853
+ machineId: readNodeMachineId(node),
1343
1854
  ...getNodeLaunchReadiness(node)
1344
1855
  };
1345
1856
  try {
1346
1857
  if (!isLocalTransport(transport) && node.daemonId) {
1347
- const result = await transport.gitStatus(node.daemonId, node.workspace, false);
1858
+ const result = await transport.gitStatus(node.daemonId, node.workspace, false, true);
1348
1859
  const status = extractGitStatus(result);
1349
1860
  const uncommittedChanges = countUncommittedChanges(status);
1350
1861
  const dirty = isGitStatusDirty(status);
1351
1862
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
1863
+ assignFullGitSnapshot(entry, status);
1352
1864
  entry.branch = status?.branch;
1353
1865
  entry.isDirty = dirty;
1354
1866
  entry.uncommittedChanges = uncommittedChanges;
@@ -1362,6 +1874,7 @@ async function meshStatus(ctx) {
1362
1874
  const autoDiscover = node.policy?.autoDiscoverSubmodules !== false;
1363
1875
  const statusResult = await commandForNode(ctx, node, "git_status", {
1364
1876
  workspace: node.workspace,
1877
+ refreshUpstream: true,
1365
1878
  includeSubmodules: autoDiscover,
1366
1879
  submoduleIgnorePaths: node.policy?.submoduleIgnorePaths || void 0
1367
1880
  });
@@ -1369,6 +1882,7 @@ async function meshStatus(ctx) {
1369
1882
  const uncommittedChanges = countUncommittedChanges(status);
1370
1883
  const dirty = isGitStatusDirty(status);
1371
1884
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
1885
+ assignFullGitSnapshot(entry, status);
1372
1886
  entry.branch = status?.branch;
1373
1887
  entry.isDirty = dirty;
1374
1888
  entry.uncommittedChanges = uncommittedChanges;
@@ -1444,15 +1958,37 @@ async function meshStatus(ctx) {
1444
1958
  }
1445
1959
  const relatedRepos = await collectRelatedRepoStatuses(ctx, node);
1446
1960
  if (relatedRepos.length) entry.relatedRepos = relatedRepos;
1961
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
1962
+ if (liveSessions.length > 0) {
1963
+ entry.sessions = liveSessions;
1964
+ }
1447
1965
  results.push(entry);
1448
1966
  }
1967
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
1968
+ meshId: mesh.id,
1969
+ queue: (0, import_daemon_core.getQueue)(mesh.id),
1970
+ ledgerEntries: (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 500 }),
1971
+ nodes: results
1972
+ });
1973
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
1449
1974
  const response = {
1450
1975
  meshId: mesh.id,
1451
1976
  meshName: mesh.name,
1452
1977
  repoIdentity: mesh.repoIdentity,
1453
1978
  policy: mesh.policy,
1454
1979
  refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
1980
+ sourceOfTruth: {
1981
+ membership: "coordinator_daemon_live_mesh",
1982
+ currentStatus: "live_git_and_session_probes",
1983
+ activeWork: "mesh_queue_file_and_local_ledger",
1984
+ historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
1985
+ },
1455
1986
  nodes: results,
1987
+ activeWork: activeWorkEvidence.activeWork,
1988
+ staleDirectWork: activeWorkEvidence.staleDirectWork,
1989
+ terminalDirectWork: activeWorkEvidence.terminalDirectWork,
1990
+ activeWorkSummary: activeWorkEvidence.summary,
1991
+ ...pollingGuidance ? { pollingGuidance } : {},
1456
1992
  branchConvergenceSummary: summarizeBranchConvergence(results)
1457
1993
  };
1458
1994
  try {
@@ -1460,13 +1996,7 @@ async function meshStatus(ctx) {
1460
1996
  } catch {
1461
1997
  }
1462
1998
  try {
1463
- let pendingEvents = [];
1464
- if (ctx.transport instanceof IpcTransport) {
1465
- const eventsResult = await ctx.transport.command("get_pending_mesh_events", {});
1466
- pendingEvents = Array.isArray(eventsResult?.events) ? eventsResult.events : [];
1467
- } else if (isLocalTransport(ctx.transport)) {
1468
- pendingEvents = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)();
1469
- }
1999
+ const pendingEvents = await drainCoordinatorPendingEvents(ctx);
1470
2000
  if (pendingEvents.length > 0) {
1471
2001
  response.pendingCoordinatorEvents = pendingEvents;
1472
2002
  }
@@ -1476,11 +2006,17 @@ async function meshStatus(ctx) {
1476
2006
  }
1477
2007
  async function meshTaskHistory(ctx, args) {
1478
2008
  const { mesh } = ctx;
2009
+ const pendingEvents = await drainCoordinatorPendingEvents(ctx);
1479
2010
  const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
1480
2011
  const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
1481
2012
  const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
1482
2013
  const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1483
- return JSON.stringify({ meshId: mesh.id, entries, summary }, null, 2);
2014
+ return JSON.stringify({
2015
+ meshId: mesh.id,
2016
+ entries,
2017
+ summary,
2018
+ ...pendingEvents.length > 0 ? { pendingCoordinatorEvents: pendingEvents } : {}
2019
+ }, null, 2);
1484
2020
  }
1485
2021
  async function meshReconcileLedger(ctx, args) {
1486
2022
  await refreshMeshFromDaemon(ctx);
@@ -1570,6 +2106,9 @@ async function meshListNodes(ctx) {
1570
2106
  nodeId: n.id,
1571
2107
  workspace: n.workspace,
1572
2108
  repoRoot: n.repoRoot,
2109
+ daemonId: readNodeDaemonId(n),
2110
+ machineId: readNodeMachineId(n),
2111
+ machine: buildNodeMachineIdentity(ctx, n),
1573
2112
  isLocalWorktree: n.isLocalWorktree,
1574
2113
  policy: n.policy,
1575
2114
  relatedRepos: readRelatedRepos(n),
@@ -1579,12 +2118,13 @@ async function meshListNodes(ctx) {
1579
2118
  }, null, 2);
1580
2119
  }
1581
2120
  async function meshEnqueueTask(ctx, args) {
2121
+ const taskMode = readString(args.task_mode) || readString(args.taskMode);
1582
2122
  try {
1583
- const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message);
2123
+ const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode });
1584
2124
  if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
1585
2125
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1586
2126
  });
1587
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2127
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
1588
2128
  }
1589
2129
  if (ctx.transport instanceof IpcTransport) {
1590
2130
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
@@ -1597,11 +2137,24 @@ async function meshEnqueueTask(ctx, args) {
1597
2137
  ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
1598
2138
  if (result.success) {
1599
2139
  try {
2140
+ const providerType = result.providerType;
2141
+ const descriptor = summarizeTaskMessage(args.message);
1600
2142
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1601
2143
  kind: "task_dispatched",
1602
2144
  nodeId: node.id,
1603
2145
  sessionId: result.sessionId,
1604
- payload: { message: args.message, via: "p2p_direct", taskId: task.id }
2146
+ providerType,
2147
+ payload: {
2148
+ source: "queue",
2149
+ via: "p2p_direct",
2150
+ taskId: task.id,
2151
+ message: args.message,
2152
+ taskTitle: descriptor.taskTitle,
2153
+ taskSummary: descriptor.taskSummary,
2154
+ ...task.taskMode ? { taskMode: task.taskMode } : {},
2155
+ ...providerType ? { providerType } : {},
2156
+ targetSessionId: result.sessionId
2157
+ }
1605
2158
  });
1606
2159
  } catch {
1607
2160
  }
@@ -1612,24 +2165,36 @@ async function meshEnqueueTask(ctx, args) {
1612
2165
  }
1613
2166
  Promise.all(dispatchPromises).catch(() => {
1614
2167
  });
1615
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2168
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
1616
2169
  }
1617
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2170
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
1618
2171
  } catch (e) {
1619
- return JSON.stringify({ success: false, error: e.message });
2172
+ const message = e?.message || String(e);
2173
+ if (message.includes("live_debug_readonly_guardrail_violation")) {
2174
+ return JSON.stringify({ success: false, code: "live_debug_readonly_guardrail_violation", taskMode, error: message });
2175
+ }
2176
+ return JSON.stringify({ success: false, error: message });
1620
2177
  }
1621
2178
  }
1622
2179
  async function meshViewQueue(ctx, args) {
1623
2180
  try {
2181
+ await refreshMeshFromDaemon(ctx);
1624
2182
  const statusFilter = sanitizeQueueStatusFilter(args.status);
1625
2183
  const view = normalizeQueueViewMode(args.view);
1626
- const fullQueue = annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh);
2184
+ const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh));
1627
2185
  const queue = filterQueueForView(fullQueue, view, statusFilter);
1628
2186
  const summary = buildQueueStatusSummary(fullQueue);
1629
2187
  const visibleSummary = buildQueueStatusSummary(queue);
1630
2188
  const maintenance = buildQueueMaintenanceReport(fullQueue);
2189
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2190
+ meshId: ctx.mesh.id,
2191
+ queue: fullQueue,
2192
+ ledgerEntries: (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 }),
2193
+ nodes: ctx.mesh.nodes
2194
+ });
1631
2195
  const staleAssignedTasks = maintenance.staleAssignedTasks || [];
1632
2196
  const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
2197
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
1633
2198
  return JSON.stringify({
1634
2199
  success: true,
1635
2200
  sourceOfTruth: {
@@ -1645,6 +2210,10 @@ async function meshViewQueue(ctx, args) {
1645
2210
  },
1646
2211
  queue,
1647
2212
  visibleQueue: queue,
2213
+ activeWork: activeWorkEvidence.activeWork,
2214
+ staleDirectWork: activeWorkEvidence.staleDirectWork,
2215
+ activeWorkSummary: activeWorkEvidence.summary,
2216
+ ...pollingGuidance ? { pollingGuidance } : {},
1648
2217
  visibleSummary,
1649
2218
  summary,
1650
2219
  activeCounts: summary.activeCounts,
@@ -1678,6 +2247,10 @@ async function meshQueueCancel(ctx, args) {
1678
2247
  if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
1679
2248
  const task = (0, import_daemon_core.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
1680
2249
  if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
2250
+ if (isLocalTransport(ctx.transport)) {
2251
+ ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2252
+ });
2253
+ }
1681
2254
  return JSON.stringify({ success: true, task }, null, 2);
1682
2255
  } catch (e) {
1683
2256
  return JSON.stringify({ success: false, error: e.message });
@@ -1708,10 +2281,46 @@ async function meshQueueRequeue(ctx, args) {
1708
2281
  }
1709
2282
  }
1710
2283
  async function meshSendTask(ctx, args) {
2284
+ const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
2285
+ const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
2286
+ if (!modeValidation.valid) {
2287
+ return JSON.stringify({
2288
+ success: false,
2289
+ code: "live_debug_readonly_guardrail_violation",
2290
+ taskMode: modeValidation.taskMode || requestedTaskMode,
2291
+ violations: modeValidation.violations,
2292
+ allowedOperations: modeValidation.allowedOperations,
2293
+ error: `live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`
2294
+ });
2295
+ }
2296
+ const taskMode = modeValidation.taskMode;
1711
2297
  const node = await findNodeWithRefresh(ctx, args.node_id);
1712
2298
  if (node.policy?.readOnly) {
1713
2299
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
1714
2300
  }
2301
+ let explicitTargetSession;
2302
+ if (args.session_id && isWorkerTaskMode(taskMode) && (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport))) {
2303
+ try {
2304
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2305
+ const sessions = extractStatusMetadataSessions(statusResult);
2306
+ explicitTargetSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2307
+ if (explicitTargetSession && isMeshCoordinatorSessionRecord(explicitTargetSession)) {
2308
+ return JSON.stringify({
2309
+ success: false,
2310
+ recoverable: true,
2311
+ code: "mesh_target_session_is_coordinator",
2312
+ reason: "mesh_target_session_is_coordinator",
2313
+ nodeId: args.node_id,
2314
+ sessionId: args.session_id,
2315
+ taskMode: taskMode || "unspecified",
2316
+ 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.`,
2317
+ 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.`
2318
+ });
2319
+ }
2320
+ } catch {
2321
+ explicitTargetSession = void 0;
2322
+ }
2323
+ }
1715
2324
  const duplicate = hasRecentDuplicateDispatch(ctx, args);
1716
2325
  if (duplicate.duplicate) {
1717
2326
  return JSON.stringify({
@@ -1735,75 +2344,144 @@ async function meshSendTask(ctx, args) {
1735
2344
  const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
1736
2345
  meshId: ctx.mesh.id,
1737
2346
  message: args.message,
1738
- targetNodeId: args.node_id
2347
+ targetNodeId: args.node_id,
2348
+ ...taskMode ? { taskMode } : {}
1739
2349
  });
1740
2350
  return JSON.stringify(res);
1741
2351
  }
1742
2352
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1743
2353
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
1744
2354
  const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
2355
+ const taskId = (0, import_node_crypto.randomUUID)();
1745
2356
  const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
1746
2357
  session_id: args.session_id,
1747
2358
  message: args.message,
1748
- providerType: cached?.providerType
2359
+ providerType: cached?.providerType,
2360
+ verifiedSession: explicitTargetSession
1749
2361
  });
1750
2362
  if (result2.success) {
1751
2363
  const dispatchedSessionId = args.session_id || result2.sessionId;
1752
2364
  try {
2365
+ const providerType = result2.providerType || cached?.providerType;
1753
2366
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1754
2367
  kind: "task_dispatched",
1755
2368
  nodeId: args.node_id,
1756
2369
  sessionId: dispatchedSessionId,
1757
- payload: {
1758
- message: args.message,
1759
- via: "p2p_direct",
1760
- ...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
1761
- }
2370
+ providerType,
2371
+ payload: buildDirectTaskPayload(args.message, "p2p_direct", {
2372
+ taskId,
2373
+ taskMode,
2374
+ providerType,
2375
+ targetSessionId: dispatchedSessionId
2376
+ })
1762
2377
  });
1763
2378
  } catch {
1764
2379
  }
1765
2380
  }
1766
- return JSON.stringify({ ...result2, nodeId: args.node_id, dispatched: result2.success === true });
2381
+ return JSON.stringify({
2382
+ ...result2,
2383
+ nodeId: args.node_id,
2384
+ sessionId: result2.success ? args.session_id || result2.sessionId : args.session_id,
2385
+ ...result2.success ? { source: "direct", taskId } : {},
2386
+ taskMode,
2387
+ ...result2.success && result2.providerType ? { providerType: result2.providerType } : {},
2388
+ dispatched: result2.success === true
2389
+ });
1767
2390
  }
1768
2391
  if (args.session_id && isLocalTransport(ctx.transport)) {
1769
2392
  const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2393
+ let resolvedProviderType = cached?.providerType || "";
2394
+ if (!resolvedProviderType) {
2395
+ let explicitSession = explicitTargetSession;
2396
+ if (!explicitSession) {
2397
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2398
+ const sessions = extractStatusMetadataSessions(statusResult);
2399
+ explicitSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2400
+ }
2401
+ if (!explicitSession) {
2402
+ return JSON.stringify({
2403
+ success: false,
2404
+ recoverable: true,
2405
+ code: "mesh_target_session_not_found",
2406
+ reason: "mesh_target_session_not_found",
2407
+ transport: "local_ipc",
2408
+ retryRecommended: true,
2409
+ nodeId: args.node_id,
2410
+ sessionId: args.session_id,
2411
+ error: `Local session '${args.session_id}' is not present in live status for node '${args.node_id}'.`,
2412
+ 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.`
2413
+ });
2414
+ }
2415
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
2416
+ if (resolvedProviderType) {
2417
+ meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
2418
+ providerType: resolvedProviderType,
2419
+ providerSessionId: readString(explicitSession?.providerSessionId) || void 0
2420
+ });
2421
+ }
2422
+ }
2423
+ if (!resolvedProviderType) {
2424
+ return JSON.stringify({
2425
+ success: false,
2426
+ recoverable: true,
2427
+ code: "mesh_target_session_provider_unknown",
2428
+ reason: "mesh_target_session_provider_unknown",
2429
+ transport: "local_ipc",
2430
+ retryRecommended: false,
2431
+ nodeId: args.node_id,
2432
+ sessionId: args.session_id,
2433
+ error: `Local session '${args.session_id}' is live but does not expose providerType/cliType, so agent_command cannot be routed safely.`,
2434
+ 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.`
2435
+ });
2436
+ }
1770
2437
  const dispatchResult = await commandForNode(ctx, node, "agent_command", {
1771
2438
  targetSessionId: args.session_id,
1772
- ...cached?.providerType ? { agentType: cached.providerType, cliType: cached.providerType, providerType: cached.providerType } : {},
2439
+ agentType: resolvedProviderType,
2440
+ cliType: resolvedProviderType,
2441
+ providerType: resolvedProviderType,
1773
2442
  action: "send_chat",
1774
2443
  message: args.message
1775
2444
  });
1776
2445
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
1777
2446
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
2447
+ const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
1778
2448
  return JSON.stringify({
2449
+ ...source && typeof source === "object" ? source : {},
1779
2450
  success: false,
1780
2451
  nodeId: args.node_id,
1781
2452
  sessionId: args.session_id,
1782
2453
  error: dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"
1783
2454
  });
1784
2455
  }
2456
+ const taskId = (0, import_node_crypto.randomUUID)();
1785
2457
  try {
1786
2458
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1787
2459
  kind: "task_dispatched",
1788
2460
  nodeId: args.node_id,
1789
2461
  sessionId: args.session_id,
1790
- providerType: cached?.providerType,
1791
- payload: { message: args.message, via: "local_direct" }
2462
+ providerType: resolvedProviderType,
2463
+ payload: buildDirectTaskPayload(args.message, "local_direct", {
2464
+ taskId,
2465
+ taskMode,
2466
+ providerType: resolvedProviderType,
2467
+ targetSessionId: args.session_id
2468
+ })
1792
2469
  });
1793
2470
  } catch {
1794
2471
  }
1795
- return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
2472
+ return JSON.stringify({ success: true, dispatched: true, source: "direct", taskId, taskMode, providerType: resolvedProviderType, nodeId: args.node_id, sessionId: args.session_id });
1796
2473
  }
1797
2474
  const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
1798
2475
  targetNodeId: args.node_id,
1799
- targetSessionId: args.session_id
2476
+ targetSessionId: args.session_id,
2477
+ taskMode
1800
2478
  });
1801
2479
  if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
1802
2480
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1803
2481
  });
1804
2482
  }
1805
- const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)() : [];
1806
- const result = { success: true, nodeId: args.node_id, taskId: task.id, status: task.status };
2483
+ const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id) : [];
2484
+ const result = { success: true, source: "queue", nodeId: args.node_id, taskId: task.id, status: task.status, taskMode: task.taskMode };
1807
2485
  if (pendingEvents.length > 0) {
1808
2486
  result.pendingCoordinatorEvents = pendingEvents;
1809
2487
  }
@@ -1823,8 +2501,11 @@ async function meshReadChat(ctx, args) {
1823
2501
  if (!node) {
1824
2502
  return JSON.stringify(buildMissingNodeReadChatRecovery(ctx, args), null, 2);
1825
2503
  }
2504
+ if (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport)) {
2505
+ await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
2506
+ }
1826
2507
  if (isLocalTransport(ctx.transport)) {
1827
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2508
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
1828
2509
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
1829
2510
  const result = await commandForNode(ctx, node, "read_chat", {
1830
2511
  sessionId: args.session_id,
@@ -1870,7 +2551,7 @@ async function meshReadChat(ctx, args) {
1870
2551
  async function meshReadDebug(ctx, args) {
1871
2552
  const node = await findNodeWithRefresh(ctx, args.node_id);
1872
2553
  if (isLocalTransport(ctx.transport)) {
1873
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2554
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
1874
2555
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
1875
2556
  const delivery = args.delivery === "inline" ? void 0 : "daemon_file";
1876
2557
  const result = await commandForNode(ctx, node, "get_chat_debug_bundle", {
@@ -1925,6 +2606,10 @@ async function meshLaunchSession(ctx, args) {
1925
2606
  const coordinatorNode = resolveCoordinatorNode(ctx);
1926
2607
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
1927
2608
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
2609
+ const isLocalNode = isLocalControlPlaneNode(ctx, node);
2610
+ if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
2611
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
2612
+ }
1928
2613
  let result;
1929
2614
  try {
1930
2615
  result = await commandForNode(ctx, node, "launch_cli", {
@@ -1965,7 +2650,6 @@ async function meshLaunchSession(ctx, args) {
1965
2650
  });
1966
2651
  } catch {
1967
2652
  }
1968
- const isLocalNode = isLocalControlPlaneNode(ctx, node);
1969
2653
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
1970
2654
  ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1971
2655
  });
@@ -1990,6 +2674,9 @@ async function meshLaunchSession(ctx, args) {
1990
2674
  const coordinatorNode = resolveCoordinatorNode(ctx);
1991
2675
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
1992
2676
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
2677
+ if (!coordinatorDaemonId) {
2678
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
2679
+ }
1993
2680
  try {
1994
2681
  const res = await ctx.transport.launch(node.daemonId, {
1995
2682
  type: resolvedProviderType,
@@ -2028,7 +2715,7 @@ async function meshGitStatus(ctx, args) {
2028
2715
  const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
2029
2716
  try {
2030
2717
  if (!isLocalTransport(ctx.transport) && node.daemonId) {
2031
- const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true);
2718
+ const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true, true);
2032
2719
  return JSON.stringify({
2033
2720
  nodeId: args.node_id,
2034
2721
  workspace: node.workspace,
@@ -2040,6 +2727,7 @@ async function meshGitStatus(ctx, args) {
2040
2727
  } else if (isLocalTransport(ctx.transport)) {
2041
2728
  const statusResult = await commandForNode(ctx, node, "git_status", {
2042
2729
  workspace: node.workspace,
2730
+ refreshUpstream: true,
2043
2731
  includeSubmodules: autoDiscoverSubmodules,
2044
2732
  submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
2045
2733
  });
@@ -2069,6 +2757,51 @@ async function meshGitStatus(ctx, args) {
2069
2757
  }, null, 2);
2070
2758
  }
2071
2759
  }
2760
+ async function meshFastForwardNode(ctx, args) {
2761
+ await refreshMeshFromDaemon(ctx);
2762
+ const node = await findNodeWithRefresh(ctx, args.node_id);
2763
+ const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
2764
+ if (node.policy?.readOnly) {
2765
+ return JSON.stringify({
2766
+ success: false,
2767
+ code: "node_read_only",
2768
+ nodeId: args.node_id,
2769
+ workspace: node.workspace,
2770
+ allowed: false,
2771
+ willRun: false,
2772
+ executed: false,
2773
+ blockingReasons: ["node_read_only"]
2774
+ }, null, 2);
2775
+ }
2776
+ try {
2777
+ const dryRun = args.dry_run === true || args.execute !== true;
2778
+ const result = await commandForNode(ctx, node, "fast_forward_mesh_node", {
2779
+ meshId: ctx.mesh.id,
2780
+ nodeId: node.id,
2781
+ workspace: node.workspace,
2782
+ branch: typeof args.branch === "string" ? args.branch : void 0,
2783
+ execute: args.execute === true && args.dry_run !== true,
2784
+ dryRun,
2785
+ updateSubmodules: args.update_submodules === true,
2786
+ submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
2787
+ });
2788
+ return JSON.stringify(unwrapCommandPayload(result), null, 2);
2789
+ } catch (e) {
2790
+ const failure = buildCoordinatorP2pRelayFailure(e, {
2791
+ command: "fast_forward_mesh_node",
2792
+ targetDaemonId: node.daemonId,
2793
+ nodeId: args.node_id
2794
+ });
2795
+ return JSON.stringify({
2796
+ ...failure,
2797
+ workspace: node.workspace,
2798
+ allowed: false,
2799
+ willRun: false,
2800
+ executed: false,
2801
+ blockingReasons: [failure.code || "mesh_fast_forward_unavailable"]
2802
+ }, null, 2);
2803
+ }
2804
+ }
2072
2805
  async function meshCheckpoint(ctx, args) {
2073
2806
  const node = await findNodeWithRefresh(ctx, args.node_id);
2074
2807
  if (node.policy?.readOnly) {
@@ -2154,6 +2887,7 @@ async function meshCloneNode(ctx, args) {
2154
2887
  if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
2155
2888
  else ctx.mesh.nodes.push(clonePayload.node);
2156
2889
  ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2890
+ await syncCoordinatorDaemonMeshCache(ctx);
2157
2891
  }
2158
2892
  return JSON.stringify(result, null, 2);
2159
2893
  } else if (!isLocalTransport(ctx.transport) && sourceNode.daemonId) {
@@ -2171,6 +2905,7 @@ async function meshCloneNode(ctx, args) {
2171
2905
  if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
2172
2906
  else ctx.mesh.nodes.push(clonePayload.node);
2173
2907
  ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2908
+ await syncCoordinatorDaemonMeshCache(ctx);
2174
2909
  }
2175
2910
  return JSON.stringify(res, null, 2);
2176
2911
  } catch (e) {
@@ -2266,6 +3001,43 @@ async function meshRemoveNode(ctx, args) {
2266
3001
  return JSON.stringify({ error: "Cloud mesh remove_node requires node daemonId" });
2267
3002
  }
2268
3003
  }
3004
+ function resolveRefineConfigNode(ctx, nodeId) {
3005
+ if (nodeId) return findNode(ctx.mesh, nodeId);
3006
+ const node = ctx.mesh.nodes.find((entry) => !!entry.workspace);
3007
+ if (!node) throw new Error("No mesh node with a workspace is available");
3008
+ return node;
3009
+ }
3010
+ async function meshRefineConfigSchema(ctx) {
3011
+ const node = resolveRefineConfigNode(ctx);
3012
+ const result = await commandForNode(ctx, node, "get_mesh_refine_config_schema", {});
3013
+ return JSON.stringify(result, null, 2);
3014
+ }
3015
+ async function meshValidateRefineConfig(ctx, args) {
3016
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3017
+ const result = await commandForNode(ctx, node, "validate_mesh_refine_config", {
3018
+ workspace: node.workspace,
3019
+ inlineMesh: ctx.mesh,
3020
+ ...args.config ? { config: args.config } : {}
3021
+ });
3022
+ return JSON.stringify(result, null, 2);
3023
+ }
3024
+ async function meshSuggestRefineConfig(ctx, args) {
3025
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3026
+ const result = await commandForNode(ctx, node, "suggest_mesh_refine_config", {
3027
+ workspace: node.workspace,
3028
+ inlineMesh: ctx.mesh
3029
+ });
3030
+ return JSON.stringify(result, null, 2);
3031
+ }
3032
+ async function meshRefinePlan(ctx, args) {
3033
+ const node = await findNodeWithRefresh(ctx, args.node_id);
3034
+ const result = await commandForNode(ctx, node, "plan_mesh_refine_node", {
3035
+ meshId: ctx.mesh.id,
3036
+ nodeId: args.node_id,
3037
+ inlineMesh: ctx.mesh
3038
+ });
3039
+ return JSON.stringify(result, null, 2);
3040
+ }
2269
3041
  async function meshRefineNode(ctx, args) {
2270
3042
  const node = await findNodeWithRefresh(ctx, args.node_id);
2271
3043
  if (isLocalTransport(ctx.transport)) {
@@ -2274,7 +3046,7 @@ async function meshRefineNode(ctx, args) {
2274
3046
  nodeId: args.node_id,
2275
3047
  inlineMesh: ctx.mesh
2276
3048
  });
2277
- if (result?.success && result.removeResult?.removed !== false) {
3049
+ if (result?.success && result.async !== true && result.removeResult?.removed !== false) {
2278
3050
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2279
3051
  if (idx >= 0) {
2280
3052
  ctx.mesh.nodes.splice(idx, 1);
@@ -2289,7 +3061,7 @@ async function meshRefineNode(ctx, args) {
2289
3061
  nodeId: args.node_id,
2290
3062
  inlineMesh: ctx.mesh
2291
3063
  });
2292
- if (res?.success && res.removeResult?.removed !== false) {
3064
+ if (res?.success && res.async !== true && res.removeResult?.removed !== false) {
2293
3065
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2294
3066
  if (idx >= 0) {
2295
3067
  ctx.mesh.nodes.splice(idx, 1);
@@ -2326,13 +3098,13 @@ var STANDARD_TOOLS = [
2326
3098
  function buildMcpHelpText() {
2327
3099
  const meshTools = ALL_MESH_TOOLS.map((tool) => tool.name);
2328
3100
  return `
2329
- adhdev-mcp \u2014 ADHDev MCP Server
3101
+ ADHDev MCP Server
2330
3102
 
2331
3103
  Usage:
2332
- adhdev-mcp Local mode (requires standalone daemon)
2333
- adhdev-mcp --api-key <key> Cloud mode (ADHDev cloud API)
2334
- adhdev-mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
2335
- adhdev-mcp --repo-mesh <mesh_id> Mesh mode (coordinator-scoped tools)
3104
+ adhdev mcp Local mode (requires standalone daemon)
3105
+ adhdev mcp --api-key <key> Cloud mode (ADHDev cloud API)
3106
+ adhdev mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
3107
+ adhdev-mcp --help Compatibility bin (same server, legacy package entrypoint)
2336
3108
 
2337
3109
  Options:
2338
3110
  --mode <mode> Transport: local, cloud, or ipc
@@ -2357,6 +3129,7 @@ Mesh tools: ${meshTools.join(", ")}
2357
3129
  // src/server.ts
2358
3130
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
2359
3131
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
3132
+ var import_node_os = __toESM(require("os"));
2360
3133
  var import_types = require("@modelcontextprotocol/sdk/types.js");
2361
3134
 
2362
3135
  // src/transports/local.ts
@@ -2511,8 +3284,8 @@ var CloudTransport = class {
2511
3284
  if (!res.ok) throw new Error(`Approve failed: ${res.status}`);
2512
3285
  return res.json();
2513
3286
  }
2514
- async gitStatus(daemonId, workspace, includeDiff = true) {
2515
- const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff) });
3287
+ async gitStatus(daemonId, workspace, includeDiff = true, refreshUpstream = false) {
3288
+ const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff), refreshUpstream: String(refreshUpstream) });
2516
3289
  const res = await fetch(
2517
3290
  `${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-status?${params}`,
2518
3291
  { headers: this.headers() }
@@ -2907,6 +3680,22 @@ function formatChatResult(result, sessionId, format, limit = 50, compact = false
2907
3680
  }))
2908
3681
  }, null, 2);
2909
3682
  }
3683
+ if ((format === "text" || format === void 0) && compact && compactPayload) {
3684
+ const lines2 = outputMessages.slice(-limit).map((m) => {
3685
+ const role = m.role === "user" ? "User" : m.role === "assistant" ? "Agent" : m.role;
3686
+ const content = messageContent(m);
3687
+ const truncated = content.length > 500 ? `${content.slice(0, 500)}\u2026` : content;
3688
+ return `[${role}] ${truncated}`;
3689
+ });
3690
+ if (compactPayload.summary) {
3691
+ const truncatedSummary = compactPayload.summary.length > 500 ? `${compactPayload.summary.slice(0, 500)}\u2026` : compactPayload.summary;
3692
+ lines2.push(`[Summary] ${truncatedSummary}`);
3693
+ }
3694
+ if (result?.pollingAdvisory) {
3695
+ lines2.push(`Advisory: ${result.pollingAdvisory.message}`);
3696
+ }
3697
+ return lines2.length > 0 ? lines2.join("\n\n") : "No messages in chat.";
3698
+ }
2910
3699
  if (outputMessages.length === 0) {
2911
3700
  return result?.pollingAdvisory ? `No messages in chat.
2912
3701
 
@@ -3874,6 +4663,7 @@ async function startMcpServer(opts) {
3874
4663
  requirePreTaskCheckpoint: false,
3875
4664
  requirePostTaskCheckpoint: true,
3876
4665
  requireApprovalForPush: true,
4666
+ allowAutoPublishSubmoduleMainCommits: false,
3877
4667
  requireApprovalForDestructiveGit: true,
3878
4668
  dirtyWorkspaceBehavior: "warn",
3879
4669
  maxParallelTasks: 2,
@@ -3930,6 +4720,7 @@ async function startMcpServer(opts) {
3930
4720
  }
3931
4721
  let localDaemonId;
3932
4722
  let localMachineId;
4723
+ let coordinatorHostname = import_node_os.default.hostname();
3933
4724
  if (transport instanceof LocalTransport || transport instanceof IpcTransport) {
3934
4725
  try {
3935
4726
  const { loadConfig } = await import("@adhdev/daemon-core");
@@ -3942,11 +4733,13 @@ async function startMcpServer(opts) {
3942
4733
  try {
3943
4734
  const statusResult = await transport.getStatus();
3944
4735
  const instanceId = typeof statusResult?.status?.instanceId === "string" ? statusResult.status.instanceId.trim() : "";
4736
+ const hostname = typeof statusResult?.status?.hostname === "string" ? statusResult.status.hostname.trim() : typeof statusResult?.status?.machine?.hostname === "string" ? statusResult.status.machine.hostname.trim() : "";
3945
4737
  if (instanceId) localDaemonId = instanceId;
4738
+ if (hostname) coordinatorHostname = hostname;
3946
4739
  } catch {
3947
4740
  }
3948
4741
  }
3949
- const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {} };
4742
+ const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {}, ...coordinatorHostname ? { coordinatorHostname } : {} };
3950
4743
  const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
3951
4744
  const server2 = new import_server.Server(
3952
4745
  { name: "adhdev-mcp-server", version: "0.9.81" },
@@ -4007,6 +4800,9 @@ async function startMcpServer(opts) {
4007
4800
  case "mesh_git_status":
4008
4801
  text = await meshGitStatus(meshCtx, a);
4009
4802
  break;
4803
+ case "mesh_fast_forward_node":
4804
+ text = await meshFastForwardNode(meshCtx, a);
4805
+ break;
4010
4806
  case "mesh_checkpoint":
4011
4807
  text = await meshCheckpoint(meshCtx, a);
4012
4808
  break;
@@ -4022,6 +4818,18 @@ async function startMcpServer(opts) {
4022
4818
  case "mesh_refine_node":
4023
4819
  text = await meshRefineNode(meshCtx, a);
4024
4820
  break;
4821
+ case "mesh_refine_config_schema":
4822
+ text = await meshRefineConfigSchema(meshCtx);
4823
+ break;
4824
+ case "mesh_validate_refine_config":
4825
+ text = await meshValidateRefineConfig(meshCtx, a);
4826
+ break;
4827
+ case "mesh_suggest_refine_config":
4828
+ text = await meshSuggestRefineConfig(meshCtx, a);
4829
+ break;
4830
+ case "mesh_refine_plan":
4831
+ text = await meshRefinePlan(meshCtx, a);
4832
+ break;
4025
4833
  case "mesh_cleanup_sessions":
4026
4834
  text = await meshCleanupSessions(meshCtx, a);
4027
4835
  break;