@adhdev/daemon-standalone 0.9.82-rc.8 → 0.9.82-rc.82

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