@adhdev/daemon-standalone 0.9.82-rc.6 → 0.9.82-rc.60

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,6 +35,9 @@ __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";
@@ -143,6 +146,10 @@ function isLocalTransport(transport) {
143
146
  }
144
147
 
145
148
  // src/tools/chat-compact.ts
149
+ function isAssistantLike(message) {
150
+ const role = String(message?.role ?? "").toLowerCase();
151
+ return role === "assistant" || role === "agent";
152
+ }
146
153
  function messageContent(message) {
147
154
  const content = message?.content;
148
155
  if (typeof content === "string") return content;
@@ -161,16 +168,22 @@ function isCoordinatorVisibleMessage(message) {
161
168
  if (meta?.internal === true || meta?.debug === true || meta?.control === true || meta?.userVisible === false || meta?.user_visible === false) return false;
162
169
  return role === "user" || role === "assistant" || role === "agent";
163
170
  }
171
+ function buildCompactMessageTail(visibleMessages, opts) {
172
+ const summary = typeof opts.summary === "string" ? opts.summary.trim() : "";
173
+ const shouldOmitSummaryMessage = !!summary && !!opts.finalAssistant && isAssistantLike(opts.finalAssistant) && messageContent(opts.finalAssistant).trim() === summary;
174
+ const sourceMessages = shouldOmitSummaryMessage ? visibleMessages.filter((message) => message !== opts.finalAssistant) : visibleMessages;
175
+ return sourceMessages.slice(-opts.limit);
176
+ }
164
177
  function compactChatPayload(payload, opts = {}) {
165
178
  const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
166
179
  const visible = rawMessages.filter(isCoordinatorVisibleMessage);
167
180
  const limit = Math.max(1, Math.min(opts.limit ?? 10, 10));
168
- const messages = visible.slice(-limit);
169
181
  const finalAssistant = [...visible].reverse().find((message) => {
170
182
  const role = String(message?.role ?? "").toLowerCase();
171
183
  return (role === "assistant" || role === "agent") && messageContent(message).trim();
172
184
  });
173
185
  const summary = typeof payload?.summary === "string" && payload.summary.trim() ? payload.summary.trim() : messageContent(finalAssistant).trim();
186
+ const messages = buildCompactMessageTail(visible, { summary, finalAssistant, limit });
174
187
  return {
175
188
  success: payload?.success !== false,
176
189
  compact: true,
@@ -235,6 +248,30 @@ var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
235
248
  function readString(value) {
236
249
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
237
250
  }
251
+ function summarizeTaskMessage(message) {
252
+ const taskSummary = message.replace(/\s+/g, " ").trim();
253
+ const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
254
+ return { taskTitle: taskTitle || "(untitled task)", taskSummary };
255
+ }
256
+ function buildDirectTaskPayload(message, via, opts) {
257
+ const descriptor = summarizeTaskMessage(message);
258
+ return {
259
+ source: "direct",
260
+ via,
261
+ taskId: opts.taskId,
262
+ message,
263
+ taskTitle: descriptor.taskTitle,
264
+ taskSummary: descriptor.taskSummary,
265
+ ...opts.taskMode ? { taskMode: opts.taskMode } : {},
266
+ ...opts.providerType ? { providerType: opts.providerType } : {},
267
+ ...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {}
268
+ };
269
+ }
270
+ function findNode(mesh, nodeId) {
271
+ const node = mesh.nodes.find((n) => n.id === nodeId);
272
+ if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
273
+ return node;
274
+ }
238
275
  var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
239
276
  var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
240
277
  var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
@@ -246,15 +283,24 @@ async function refreshMeshFromDaemon(ctx) {
246
283
  const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
247
284
  if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
248
285
  const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
249
- if (!refreshedNodes.length) return;
250
286
  ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
251
287
  ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
252
288
  } catch {
253
289
  }
254
290
  }
291
+ async function syncCoordinatorDaemonMeshCache(ctx) {
292
+ if (!(ctx.transport instanceof IpcTransport)) return;
293
+ try {
294
+ await ctx.transport.command("get_mesh", {
295
+ meshId: ctx.mesh.id,
296
+ inlineMesh: ctx.mesh
297
+ });
298
+ } catch {
299
+ }
300
+ }
255
301
  async function findNodeWithRefresh(ctx, nodeId) {
256
302
  const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
257
- if (hit) return hit;
303
+ if (hit && !hit.isLocalWorktree) return hit;
258
304
  await refreshMeshFromDaemon(ctx);
259
305
  const refreshed = ctx.mesh.nodes.find((n) => n.id === nodeId);
260
306
  if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
@@ -262,7 +308,7 @@ async function findNodeWithRefresh(ctx, nodeId) {
262
308
  }
263
309
  async function findOptionalNodeWithRefresh(ctx, nodeId) {
264
310
  const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
265
- if (hit) return hit;
311
+ if (hit && !hit.isLocalWorktree) return hit;
266
312
  await refreshMeshFromDaemon(ctx);
267
313
  return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
268
314
  }
@@ -314,9 +360,26 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
314
360
  readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
315
361
  };
316
362
  if (finalSummary) {
363
+ if (args.compact === true) {
364
+ return {
365
+ ...compactChatPayload({
366
+ success: true,
367
+ status: "idle",
368
+ providerSessionId,
369
+ summary: finalSummary,
370
+ messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
371
+ }, {
372
+ nodeId: args.node_id,
373
+ sessionId: args.session_id,
374
+ limit: args.tail ?? 10
375
+ }),
376
+ recoveredFromLedger: true,
377
+ ledger
378
+ };
379
+ }
317
380
  return {
318
381
  success: true,
319
- compact: args.compact === true,
382
+ compact: false,
320
383
  recoveredFromLedger: true,
321
384
  nodeId: args.node_id,
322
385
  sessionId: args.session_id,
@@ -368,6 +431,14 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
368
431
  function readSessionRecordId(session) {
369
432
  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
433
  }
434
+ function extractStatusMetadataSessions(value) {
435
+ const payload = unwrapCommandPayload(value);
436
+ const status = payload?.status && typeof payload.status === "object" ? payload.status : payload;
437
+ return Array.isArray(status?.sessions) ? status.sessions : [];
438
+ }
439
+ function resolveSessionProviderType(session) {
440
+ return readString(session?.providerType) || readString(session?.cliType) || readString(session?.agentType) || "";
441
+ }
371
442
  function addSessionRecord(target, session) {
372
443
  if (!session || typeof session !== "object" || isTerminalSessionRecord(session)) return;
373
444
  const sessionId = readSessionRecordId(session);
@@ -580,22 +651,59 @@ function isIdleSessionRecord(session) {
580
651
  const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
581
652
  return status === "idle" || chatStatus === "waiting_input";
582
653
  }
654
+ function isMeshOwnedDelegateSession(session, meshId, nodeId) {
655
+ const settings = session?.settings;
656
+ const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
657
+ const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
658
+ const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
659
+ if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
660
+ return !sessionNodeId || sessionNodeId === nodeId;
661
+ }
583
662
  function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
584
663
  const live = sessions.filter((session) => !isTerminalSessionRecord(session));
585
664
  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
665
  const meshSessions = live.filter(
595
- (session) => isMeshOwnedDelegateSession(session)
666
+ (session) => isMeshOwnedDelegateSession(session, meshId, nodeId)
596
667
  );
597
668
  return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || void 0;
598
669
  }
670
+ function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
671
+ return {
672
+ success: false,
673
+ recoverable: true,
674
+ code: "mesh_delegate_session_missing_relay_metadata",
675
+ reason: "mesh_delegate_session_missing_relay_metadata",
676
+ transport: "mesh_transport",
677
+ retryRecommended: true,
678
+ meshId: ctx.mesh.id,
679
+ nodeId: node.id,
680
+ daemonId: node.daemonId,
681
+ workspace: node.workspace,
682
+ sessionId,
683
+ ...providerType ? { resolvedProviderType: providerType } : {},
684
+ 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.`,
685
+ 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.`,
686
+ noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
687
+ };
688
+ }
689
+ function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
690
+ return {
691
+ success: false,
692
+ recoverable: true,
693
+ code: "mesh_coordinator_daemon_unknown",
694
+ reason: "mesh_coordinator_daemon_unknown",
695
+ transport: "mesh_transport",
696
+ retryRecommended: true,
697
+ meshId: ctx.mesh.id,
698
+ nodeId: node.id,
699
+ daemonId: node.daemonId,
700
+ workspace: node.workspace,
701
+ ...providerType ? { resolvedProviderType: providerType } : {},
702
+ 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.`,
703
+ 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.",
704
+ noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
705
+ };
706
+ }
599
707
  function findNestedPayload(value, predicate) {
600
708
  const seen = /* @__PURE__ */ new Set();
601
709
  const stack = [{ payload: value, depth: 0 }];
@@ -623,12 +731,16 @@ function extractGitDiff(value) {
623
731
  }
624
732
  function extractSubmodules(value, ignorePaths) {
625
733
  const payload = unwrapCommandPayload(value);
626
- const subs = payload?.submodules ?? value?.submodules;
734
+ const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
627
735
  if (!Array.isArray(subs)) return void 0;
628
736
  if (ignorePaths.length === 0) return subs;
629
737
  const ignoreSet = new Set(ignorePaths);
630
738
  return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
631
739
  }
740
+ function assignFullGitSnapshot(entry, status) {
741
+ if (!status || typeof status !== "object" || Array.isArray(status)) return;
742
+ entry.git = status;
743
+ }
632
744
  function extractLaunchPayload(value) {
633
745
  return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
634
746
  }
@@ -753,20 +865,63 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
753
865
  let sessionId = args.session_id?.trim() || "";
754
866
  const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
755
867
  let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
756
- if (!sessionId) {
868
+ if (!sessionId || args.session_id) {
757
869
  try {
758
870
  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;
871
+ const sessions = extractStatusMetadataSessions(relayResult);
872
+ if (sessionId) {
873
+ const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
874
+ if (!explicitSession) {
875
+ return {
876
+ success: false,
877
+ recoverable: true,
878
+ code: "mesh_target_session_not_found",
879
+ reason: "mesh_target_session_not_found",
880
+ transport: "mesh_transport",
881
+ retryRecommended: true,
882
+ meshId: ctx.mesh.id,
883
+ nodeId: node.id,
884
+ daemonId,
885
+ workspace: node.workspace,
886
+ sessionId,
887
+ ...resolvedProviderType ? { resolvedProviderType } : {},
888
+ error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
889
+ 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.`
890
+ };
891
+ }
892
+ if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
893
+ return buildRelayUnsafeRemoteSessionFailure(
894
+ ctx,
895
+ node,
896
+ sessionId,
897
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
898
+ );
899
+ }
765
900
  if (!resolvedProviderType) {
766
- resolvedProviderType = targetSession.providerType || targetSession.cliType || "";
901
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
902
+ }
903
+ } else {
904
+ const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
905
+ if (targetSession?.id || targetSession?.sessionId) {
906
+ sessionId = targetSession.id || targetSession.sessionId;
907
+ if (!resolvedProviderType) {
908
+ resolvedProviderType = resolveSessionProviderType(targetSession);
909
+ }
767
910
  }
768
911
  }
769
912
  } catch (e) {
913
+ if (sessionId) {
914
+ return {
915
+ ...buildCoordinatorP2pRelayFailure(e, {
916
+ command: "get_status_metadata",
917
+ targetDaemonId: daemonId,
918
+ nodeId: node.id,
919
+ sessionId
920
+ }),
921
+ success: false,
922
+ error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
923
+ };
924
+ }
770
925
  }
771
926
  }
772
927
  if (!resolvedProviderType) {
@@ -796,7 +951,7 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
796
951
  error: `P2P dispatch failed: ${errorMessage}`
797
952
  };
798
953
  }
799
- return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
954
+ return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType, providerType: resolvedProviderType };
800
955
  } catch (e) {
801
956
  const errorMessage = e?.message || String(e);
802
957
  return {
@@ -880,6 +1035,10 @@ function summarizeRelatedRepoStatus(repo, status) {
880
1035
  workspace: repo.workspace,
881
1036
  isGitRepo: status?.isGitRepo === true,
882
1037
  branch: status?.branch ?? null,
1038
+ upstream: status?.upstream ?? null,
1039
+ upstreamStatus: typeof status?.upstreamStatus === "string" ? status.upstreamStatus : status?.upstream ? "unchecked" : "no_upstream",
1040
+ upstreamFetchedAt: Number.isFinite(Number(status?.upstreamFetchedAt)) ? Number(status.upstreamFetchedAt) : null,
1041
+ upstreamFetchError: typeof status?.upstreamFetchError === "string" ? status.upstreamFetchError : null,
883
1042
  ahead: Number.isFinite(Number(status?.ahead)) ? Number(status.ahead) : 0,
884
1043
  behind: Number.isFinite(Number(status?.behind)) ? Number(status.behind) : 0,
885
1044
  dirty,
@@ -896,7 +1055,7 @@ async function collectRelatedRepoStatuses(ctx, node) {
896
1055
  const results = [];
897
1056
  for (const repo of relatedRepos) {
898
1057
  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 });
1058
+ 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
1059
  const status = extractGitStatus(statusResult);
901
1060
  results.push(summarizeRelatedRepoStatus(repo, status));
902
1061
  } catch (e) {
@@ -944,11 +1103,13 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
944
1103
  const ahead = readNumeric(status?.ahead);
945
1104
  const behind = readNumeric(status?.behind);
946
1105
  const upstream = readString(status?.upstream) ?? null;
1106
+ const upstreamStatus = readString(status?.upstreamStatus) ?? (upstream ? "unchecked" : "no_upstream");
947
1107
  const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
948
1108
  const base = {
949
1109
  defaultBranch,
950
1110
  branch,
951
1111
  upstream,
1112
+ upstreamStatus,
952
1113
  ahead,
953
1114
  behind,
954
1115
  isWorktree: node.isLocalWorktree === true,
@@ -982,6 +1143,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
982
1143
  };
983
1144
  }
984
1145
  if (branch === defaultBranch) {
1146
+ if (upstream && upstreamStatus !== "fresh") {
1147
+ return {
1148
+ ...base,
1149
+ status: "blocked_review",
1150
+ needsConvergence: true,
1151
+ reason: "default_branch_upstream_unverified",
1152
+ nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${node.id}'.`
1153
+ };
1154
+ }
985
1155
  if (ahead > 0 || behind > 0) {
986
1156
  return {
987
1157
  ...base,
@@ -1008,6 +1178,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
1008
1178
  nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
1009
1179
  };
1010
1180
  }
1181
+ if (upstream && upstreamStatus !== "fresh") {
1182
+ return {
1183
+ ...base,
1184
+ status: "blocked_review",
1185
+ needsConvergence: true,
1186
+ reason: "feature_branch_upstream_unverified",
1187
+ nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`
1188
+ };
1189
+ }
1011
1190
  if (!upstream || ahead > 0 || behind > 0) {
1012
1191
  return {
1013
1192
  ...base,
@@ -1051,6 +1230,71 @@ async function commandForNode(ctx, node, command, args = {}) {
1051
1230
  }
1052
1231
  throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
1053
1232
  }
1233
+ function normalizePendingMeshCoordinatorEvents(value) {
1234
+ const payload = unwrapCommandPayload(value);
1235
+ const events = Array.isArray(payload?.events) ? payload.events : Array.isArray(value?.events) ? value.events : [];
1236
+ return events.filter((event) => event && typeof event === "object");
1237
+ }
1238
+ function buildMeshForwardPayloadFromPendingEvent(event) {
1239
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : {};
1240
+ return {
1241
+ event: readString(event?.event),
1242
+ meshId: readString(event?.meshId),
1243
+ nodeId: readString(event?.nodeId) || readString(metadataEvent.meshNodeId),
1244
+ workspace: readString(event?.workspace) || readString(metadataEvent.workspace),
1245
+ targetSessionId: readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId),
1246
+ providerType: readString(metadataEvent.providerType),
1247
+ providerSessionId: readString(metadataEvent.providerSessionId),
1248
+ finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
1249
+ ...metadataEvent.intentional === true ? { intentional: true } : {},
1250
+ ...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
1251
+ ...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
1252
+ ...readString(metadataEvent.reason) ? { reason: readString(metadataEvent.reason) } : {},
1253
+ ...readString(metadataEvent.stopReason) ? { stopReason: readString(metadataEvent.stopReason) } : {},
1254
+ ...readString(metadataEvent.cleanupReason) ? { cleanupReason: readString(metadataEvent.cleanupReason) } : {},
1255
+ ...readString(metadataEvent.source) ? { source: readString(metadataEvent.source) } : {}
1256
+ };
1257
+ }
1258
+ async function drainCoordinatorPendingEvents(ctx, opts) {
1259
+ const requestedNodeIds = opts?.nodeIds?.length ? new Set(opts.nodeIds) : null;
1260
+ const matchesCurrentMesh = (event) => readString(event?.meshId) === ctx.mesh.id;
1261
+ if (ctx.transport instanceof IpcTransport) {
1262
+ const surfacedEvents = [];
1263
+ try {
1264
+ surfacedEvents.push(
1265
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1266
+ );
1267
+ } catch {
1268
+ }
1269
+ for (const node of ctx.mesh.nodes) {
1270
+ if (!node.daemonId || isLocalControlPlaneNode(ctx, node)) continue;
1271
+ if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
1272
+ try {
1273
+ const remoteEvents = normalizePendingMeshCoordinatorEvents(
1274
+ await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", { meshId: ctx.mesh.id })
1275
+ ).filter(matchesCurrentMesh);
1276
+ if (remoteEvents.length === 0) continue;
1277
+ for (const event of remoteEvents) {
1278
+ const payload = buildMeshForwardPayloadFromPendingEvent(event);
1279
+ if (!payload.event || !payload.meshId) continue;
1280
+ await ctx.transport.command("mesh_forward_event", payload);
1281
+ }
1282
+ } catch {
1283
+ }
1284
+ }
1285
+ try {
1286
+ surfacedEvents.push(
1287
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1288
+ );
1289
+ } catch {
1290
+ }
1291
+ return surfacedEvents;
1292
+ }
1293
+ if (isLocalTransport(ctx.transport)) {
1294
+ return (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id).filter(matchesCurrentMesh);
1295
+ }
1296
+ return [];
1297
+ }
1054
1298
  function isP2pTransportUnavailableError(error) {
1055
1299
  return (0, import_daemon_core.isP2pRelayTransportFailure)(error);
1056
1300
  }
@@ -1088,7 +1332,9 @@ var MESH_ENQUEUE_TASK_TOOL = {
1088
1332
  inputSchema: {
1089
1333
  type: "object",
1090
1334
  properties: {
1091
- message: { type: "string", description: "The task instruction for the agent." }
1335
+ message: { type: "string", description: "The task instruction for the agent." },
1336
+ 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." },
1337
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
1092
1338
  },
1093
1339
  required: ["message"]
1094
1340
  }
@@ -1148,7 +1394,9 @@ var MESH_SEND_TASK_TOOL = {
1148
1394
  properties: {
1149
1395
  node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
1150
1396
  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." }
1397
+ message: { type: "string", description: "Natural-language task to send to the agent." },
1398
+ 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." },
1399
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
1152
1400
  },
1153
1401
  required: ["node_id", "session_id", "message"]
1154
1402
  }
@@ -1206,6 +1454,21 @@ var MESH_GIT_STATUS_TOOL = {
1206
1454
  required: ["node_id"]
1207
1455
  }
1208
1456
  };
1457
+ var MESH_FAST_FORWARD_NODE_TOOL = {
1458
+ name: "mesh_fast_forward_node",
1459
+ 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.",
1460
+ inputSchema: {
1461
+ type: "object",
1462
+ properties: {
1463
+ node_id: { type: "string", description: "Target node ID." },
1464
+ branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
1465
+ execute: { type: "boolean", description: "When true, apply the fast-forward if all safety gates pass. Defaults false/dry-run." },
1466
+ dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
1467
+ 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." }
1468
+ },
1469
+ required: ["node_id"]
1470
+ }
1471
+ };
1209
1472
  var MESH_CHECKPOINT_TOOL = {
1210
1473
  name: "mesh_checkpoint",
1211
1474
  description: "Create a git checkpoint (commit) on a mesh node workspace.",
@@ -1289,7 +1552,7 @@ var MESH_TASK_HISTORY_TOOL = {
1289
1552
  type: "object",
1290
1553
  properties: {
1291
1554
  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." }
1555
+ 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
1556
  }
1294
1557
  }
1295
1558
  };
@@ -1318,6 +1581,43 @@ var MESH_REFINE_NODE_TOOL = {
1318
1581
  required: ["node_id"]
1319
1582
  }
1320
1583
  };
1584
+ var MESH_REFINE_CONFIG_SCHEMA_TOOL = {
1585
+ name: "mesh_refine_config_schema",
1586
+ 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.",
1587
+ inputSchema: { type: "object", properties: {} }
1588
+ };
1589
+ var MESH_VALIDATE_REFINE_CONFIG_TOOL = {
1590
+ name: "mesh_validate_refine_config",
1591
+ description: "Validate the repo mesh/refine config for a node/workspace without running validation commands or merging.",
1592
+ inputSchema: {
1593
+ type: "object",
1594
+ properties: {
1595
+ node_id: { type: "string", description: "Optional node/workspace whose refine config should be loaded. Defaults to the first mesh node." },
1596
+ config: { type: "object", description: "Optional inline config object to validate instead of loading from the repo." }
1597
+ }
1598
+ }
1599
+ };
1600
+ var MESH_SUGGEST_REFINE_CONFIG_TOOL = {
1601
+ name: "mesh_suggest_refine_config",
1602
+ description: "Suggest a repo mesh/refine config scaffold from project context/package scripts. Suggestions are never executed until saved as explicit refine config.",
1603
+ inputSchema: {
1604
+ type: "object",
1605
+ properties: {
1606
+ node_id: { type: "string", description: "Optional node/workspace used for suggestions. Defaults to the first mesh node." }
1607
+ }
1608
+ }
1609
+ };
1610
+ var MESH_REFINE_PLAN_TOOL = {
1611
+ name: "mesh_refine_plan",
1612
+ 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.",
1613
+ inputSchema: {
1614
+ type: "object",
1615
+ properties: {
1616
+ node_id: { type: "string", description: "Node ID of the worktree node to plan." }
1617
+ },
1618
+ required: ["node_id"]
1619
+ }
1620
+ };
1321
1621
  var ALL_MESH_TOOLS = [
1322
1622
  MESH_STATUS_TOOL,
1323
1623
  MESH_LIST_NODES_TOOL,
@@ -1330,11 +1630,16 @@ var ALL_MESH_TOOLS = [
1330
1630
  MESH_READ_DEBUG_TOOL,
1331
1631
  MESH_LAUNCH_SESSION_TOOL,
1332
1632
  MESH_GIT_STATUS_TOOL,
1633
+ MESH_FAST_FORWARD_NODE_TOOL,
1333
1634
  MESH_CHECKPOINT_TOOL,
1334
1635
  MESH_APPROVE_TOOL,
1335
1636
  MESH_CLONE_NODE_TOOL,
1336
1637
  MESH_REMOVE_NODE_TOOL,
1337
1638
  MESH_REFINE_NODE_TOOL,
1639
+ MESH_REFINE_CONFIG_SCHEMA_TOOL,
1640
+ MESH_VALIDATE_REFINE_CONFIG_TOOL,
1641
+ MESH_SUGGEST_REFINE_CONFIG_TOOL,
1642
+ MESH_REFINE_PLAN_TOOL,
1338
1643
  MESH_CLEANUP_SESSIONS_TOOL,
1339
1644
  MESH_TASK_HISTORY_TOOL,
1340
1645
  MESH_RECONCILE_LEDGER_TOOL
@@ -1352,11 +1657,12 @@ async function meshStatus(ctx) {
1352
1657
  };
1353
1658
  try {
1354
1659
  if (!isLocalTransport(transport) && node.daemonId) {
1355
- const result = await transport.gitStatus(node.daemonId, node.workspace, false);
1660
+ const result = await transport.gitStatus(node.daemonId, node.workspace, false, true);
1356
1661
  const status = extractGitStatus(result);
1357
1662
  const uncommittedChanges = countUncommittedChanges(status);
1358
1663
  const dirty = isGitStatusDirty(status);
1359
1664
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
1665
+ assignFullGitSnapshot(entry, status);
1360
1666
  entry.branch = status?.branch;
1361
1667
  entry.isDirty = dirty;
1362
1668
  entry.uncommittedChanges = uncommittedChanges;
@@ -1370,6 +1676,7 @@ async function meshStatus(ctx) {
1370
1676
  const autoDiscover = node.policy?.autoDiscoverSubmodules !== false;
1371
1677
  const statusResult = await commandForNode(ctx, node, "git_status", {
1372
1678
  workspace: node.workspace,
1679
+ refreshUpstream: true,
1373
1680
  includeSubmodules: autoDiscover,
1374
1681
  submoduleIgnorePaths: node.policy?.submoduleIgnorePaths || void 0
1375
1682
  });
@@ -1377,6 +1684,7 @@ async function meshStatus(ctx) {
1377
1684
  const uncommittedChanges = countUncommittedChanges(status);
1378
1685
  const dirty = isGitStatusDirty(status);
1379
1686
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
1687
+ assignFullGitSnapshot(entry, status);
1380
1688
  entry.branch = status?.branch;
1381
1689
  entry.isDirty = dirty;
1382
1690
  entry.uncommittedChanges = uncommittedChanges;
@@ -1454,13 +1762,27 @@ async function meshStatus(ctx) {
1454
1762
  if (relatedRepos.length) entry.relatedRepos = relatedRepos;
1455
1763
  results.push(entry);
1456
1764
  }
1765
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
1766
+ meshId: mesh.id,
1767
+ queue: (0, import_daemon_core.getQueue)(mesh.id),
1768
+ ledgerEntries: (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 500 }),
1769
+ nodes: mesh.nodes
1770
+ });
1457
1771
  const response = {
1458
1772
  meshId: mesh.id,
1459
1773
  meshName: mesh.name,
1460
1774
  repoIdentity: mesh.repoIdentity,
1461
1775
  policy: mesh.policy,
1462
1776
  refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
1777
+ sourceOfTruth: {
1778
+ membership: "coordinator_daemon_live_mesh",
1779
+ currentStatus: "live_git_and_session_probes",
1780
+ activeWork: "mesh_queue_file_and_local_ledger",
1781
+ historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
1782
+ },
1463
1783
  nodes: results,
1784
+ activeWork: activeWorkEvidence.activeWork,
1785
+ activeWorkSummary: activeWorkEvidence.summary,
1464
1786
  branchConvergenceSummary: summarizeBranchConvergence(results)
1465
1787
  };
1466
1788
  try {
@@ -1468,13 +1790,7 @@ async function meshStatus(ctx) {
1468
1790
  } catch {
1469
1791
  }
1470
1792
  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
- }
1793
+ const pendingEvents = await drainCoordinatorPendingEvents(ctx);
1478
1794
  if (pendingEvents.length > 0) {
1479
1795
  response.pendingCoordinatorEvents = pendingEvents;
1480
1796
  }
@@ -1484,6 +1800,7 @@ async function meshStatus(ctx) {
1484
1800
  }
1485
1801
  async function meshTaskHistory(ctx, args) {
1486
1802
  const { mesh } = ctx;
1803
+ await drainCoordinatorPendingEvents(ctx);
1487
1804
  const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
1488
1805
  const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
1489
1806
  const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
@@ -1587,12 +1904,13 @@ async function meshListNodes(ctx) {
1587
1904
  }, null, 2);
1588
1905
  }
1589
1906
  async function meshEnqueueTask(ctx, args) {
1907
+ const taskMode = readString(args.task_mode) || readString(args.taskMode);
1590
1908
  try {
1591
- const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message);
1909
+ const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode });
1592
1910
  if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
1593
1911
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1594
1912
  });
1595
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
1913
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
1596
1914
  }
1597
1915
  if (ctx.transport instanceof IpcTransport) {
1598
1916
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
@@ -1605,11 +1923,24 @@ async function meshEnqueueTask(ctx, args) {
1605
1923
  ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
1606
1924
  if (result.success) {
1607
1925
  try {
1926
+ const providerType = result.providerType;
1927
+ const descriptor = summarizeTaskMessage(args.message);
1608
1928
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1609
1929
  kind: "task_dispatched",
1610
1930
  nodeId: node.id,
1611
1931
  sessionId: result.sessionId,
1612
- payload: { message: args.message, via: "p2p_direct", taskId: task.id }
1932
+ providerType,
1933
+ payload: {
1934
+ source: "queue",
1935
+ via: "p2p_direct",
1936
+ taskId: task.id,
1937
+ message: args.message,
1938
+ taskTitle: descriptor.taskTitle,
1939
+ taskSummary: descriptor.taskSummary,
1940
+ ...task.taskMode ? { taskMode: task.taskMode } : {},
1941
+ ...providerType ? { providerType } : {},
1942
+ targetSessionId: result.sessionId
1943
+ }
1613
1944
  });
1614
1945
  } catch {
1615
1946
  }
@@ -1620,11 +1951,15 @@ async function meshEnqueueTask(ctx, args) {
1620
1951
  }
1621
1952
  Promise.all(dispatchPromises).catch(() => {
1622
1953
  });
1623
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
1954
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
1624
1955
  }
1625
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
1956
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode });
1626
1957
  } catch (e) {
1627
- return JSON.stringify({ success: false, error: e.message });
1958
+ const message = e?.message || String(e);
1959
+ if (message.includes("live_debug_readonly_guardrail_violation")) {
1960
+ return JSON.stringify({ success: false, code: "live_debug_readonly_guardrail_violation", taskMode, error: message });
1961
+ }
1962
+ return JSON.stringify({ success: false, error: message });
1628
1963
  }
1629
1964
  }
1630
1965
  async function meshViewQueue(ctx, args) {
@@ -1636,6 +1971,12 @@ async function meshViewQueue(ctx, args) {
1636
1971
  const summary = buildQueueStatusSummary(fullQueue);
1637
1972
  const visibleSummary = buildQueueStatusSummary(queue);
1638
1973
  const maintenance = buildQueueMaintenanceReport(fullQueue);
1974
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
1975
+ meshId: ctx.mesh.id,
1976
+ queue: fullQueue,
1977
+ ledgerEntries: (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 }),
1978
+ nodes: ctx.mesh.nodes
1979
+ });
1639
1980
  const staleAssignedTasks = maintenance.staleAssignedTasks || [];
1640
1981
  const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
1641
1982
  return JSON.stringify({
@@ -1653,6 +1994,8 @@ async function meshViewQueue(ctx, args) {
1653
1994
  },
1654
1995
  queue,
1655
1996
  visibleQueue: queue,
1997
+ activeWork: activeWorkEvidence.activeWork,
1998
+ activeWorkSummary: activeWorkEvidence.summary,
1656
1999
  visibleSummary,
1657
2000
  summary,
1658
2001
  activeCounts: summary.activeCounts,
@@ -1716,6 +2059,19 @@ async function meshQueueRequeue(ctx, args) {
1716
2059
  }
1717
2060
  }
1718
2061
  async function meshSendTask(ctx, args) {
2062
+ const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
2063
+ const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
2064
+ if (!modeValidation.valid) {
2065
+ return JSON.stringify({
2066
+ success: false,
2067
+ code: "live_debug_readonly_guardrail_violation",
2068
+ taskMode: modeValidation.taskMode || requestedTaskMode,
2069
+ violations: modeValidation.violations,
2070
+ allowedOperations: modeValidation.allowedOperations,
2071
+ error: `live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`
2072
+ });
2073
+ }
2074
+ const taskMode = modeValidation.taskMode;
1719
2075
  const node = await findNodeWithRefresh(ctx, args.node_id);
1720
2076
  if (node.policy?.readOnly) {
1721
2077
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
@@ -1743,13 +2099,15 @@ async function meshSendTask(ctx, args) {
1743
2099
  const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
1744
2100
  meshId: ctx.mesh.id,
1745
2101
  message: args.message,
1746
- targetNodeId: args.node_id
2102
+ targetNodeId: args.node_id,
2103
+ ...taskMode ? { taskMode } : {}
1747
2104
  });
1748
2105
  return JSON.stringify(res);
1749
2106
  }
1750
2107
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1751
2108
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
1752
2109
  const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
2110
+ const taskId = (0, import_node_crypto.randomUUID)();
1753
2111
  const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
1754
2112
  session_id: args.session_id,
1755
2113
  message: args.message,
@@ -1758,26 +2116,80 @@ async function meshSendTask(ctx, args) {
1758
2116
  if (result2.success) {
1759
2117
  const dispatchedSessionId = args.session_id || result2.sessionId;
1760
2118
  try {
2119
+ const providerType = result2.providerType || cached?.providerType;
1761
2120
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1762
2121
  kind: "task_dispatched",
1763
2122
  nodeId: args.node_id,
1764
2123
  sessionId: dispatchedSessionId,
1765
- payload: {
1766
- message: args.message,
1767
- via: "p2p_direct",
1768
- ...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
1769
- }
2124
+ providerType,
2125
+ payload: buildDirectTaskPayload(args.message, "p2p_direct", {
2126
+ taskId,
2127
+ taskMode,
2128
+ providerType,
2129
+ targetSessionId: dispatchedSessionId
2130
+ })
1770
2131
  });
1771
2132
  } catch {
1772
2133
  }
1773
2134
  }
1774
- return JSON.stringify({ ...result2, nodeId: args.node_id, dispatched: result2.success === true });
2135
+ return JSON.stringify({
2136
+ ...result2,
2137
+ nodeId: args.node_id,
2138
+ sessionId: result2.success ? args.session_id || result2.sessionId : args.session_id,
2139
+ ...result2.success ? { source: "direct", taskId } : {},
2140
+ taskMode,
2141
+ ...result2.success && result2.providerType ? { providerType: result2.providerType } : {},
2142
+ dispatched: result2.success === true
2143
+ });
1775
2144
  }
1776
2145
  if (args.session_id && isLocalTransport(ctx.transport)) {
1777
2146
  const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2147
+ let resolvedProviderType = cached?.providerType || "";
2148
+ if (!resolvedProviderType) {
2149
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2150
+ const sessions = extractStatusMetadataSessions(statusResult);
2151
+ const explicitSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2152
+ if (!explicitSession) {
2153
+ return JSON.stringify({
2154
+ success: false,
2155
+ recoverable: true,
2156
+ code: "mesh_target_session_not_found",
2157
+ reason: "mesh_target_session_not_found",
2158
+ transport: "local_ipc",
2159
+ retryRecommended: true,
2160
+ nodeId: args.node_id,
2161
+ sessionId: args.session_id,
2162
+ error: `Local session '${args.session_id}' is not present in live status for node '${args.node_id}'.`,
2163
+ 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.`
2164
+ });
2165
+ }
2166
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
2167
+ if (resolvedProviderType) {
2168
+ meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
2169
+ providerType: resolvedProviderType,
2170
+ providerSessionId: readString(explicitSession?.providerSessionId) || void 0
2171
+ });
2172
+ }
2173
+ }
2174
+ if (!resolvedProviderType) {
2175
+ return JSON.stringify({
2176
+ success: false,
2177
+ recoverable: true,
2178
+ code: "mesh_target_session_provider_unknown",
2179
+ reason: "mesh_target_session_provider_unknown",
2180
+ transport: "local_ipc",
2181
+ retryRecommended: false,
2182
+ nodeId: args.node_id,
2183
+ sessionId: args.session_id,
2184
+ error: `Local session '${args.session_id}' is live but does not expose providerType/cliType, so agent_command cannot be routed safely.`,
2185
+ 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.`
2186
+ });
2187
+ }
1778
2188
  const dispatchResult = await commandForNode(ctx, node, "agent_command", {
1779
2189
  targetSessionId: args.session_id,
1780
- ...cached?.providerType ? { agentType: cached.providerType, cliType: cached.providerType, providerType: cached.providerType } : {},
2190
+ agentType: resolvedProviderType,
2191
+ cliType: resolvedProviderType,
2192
+ providerType: resolvedProviderType,
1781
2193
  action: "send_chat",
1782
2194
  message: args.message
1783
2195
  });
@@ -1790,28 +2202,35 @@ async function meshSendTask(ctx, args) {
1790
2202
  error: dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"
1791
2203
  });
1792
2204
  }
2205
+ const taskId = (0, import_node_crypto.randomUUID)();
1793
2206
  try {
1794
2207
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1795
2208
  kind: "task_dispatched",
1796
2209
  nodeId: args.node_id,
1797
2210
  sessionId: args.session_id,
1798
- providerType: cached?.providerType,
1799
- payload: { message: args.message, via: "local_direct" }
2211
+ providerType: resolvedProviderType,
2212
+ payload: buildDirectTaskPayload(args.message, "local_direct", {
2213
+ taskId,
2214
+ taskMode,
2215
+ providerType: resolvedProviderType,
2216
+ targetSessionId: args.session_id
2217
+ })
1800
2218
  });
1801
2219
  } catch {
1802
2220
  }
1803
- return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
2221
+ return JSON.stringify({ success: true, dispatched: true, source: "direct", taskId, taskMode, providerType: resolvedProviderType, nodeId: args.node_id, sessionId: args.session_id });
1804
2222
  }
1805
2223
  const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
1806
2224
  targetNodeId: args.node_id,
1807
- targetSessionId: args.session_id
2225
+ targetSessionId: args.session_id,
2226
+ taskMode
1808
2227
  });
1809
2228
  if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
1810
2229
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1811
2230
  });
1812
2231
  }
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 };
2232
+ const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id) : [];
2233
+ const result = { success: true, source: "queue", nodeId: args.node_id, taskId: task.id, status: task.status, taskMode: task.taskMode };
1815
2234
  if (pendingEvents.length > 0) {
1816
2235
  result.pendingCoordinatorEvents = pendingEvents;
1817
2236
  }
@@ -1831,6 +2250,9 @@ async function meshReadChat(ctx, args) {
1831
2250
  if (!node) {
1832
2251
  return JSON.stringify(buildMissingNodeReadChatRecovery(ctx, args), null, 2);
1833
2252
  }
2253
+ if (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport)) {
2254
+ await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
2255
+ }
1834
2256
  if (isLocalTransport(ctx.transport)) {
1835
2257
  const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
1836
2258
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
@@ -1933,6 +2355,10 @@ async function meshLaunchSession(ctx, args) {
1933
2355
  const coordinatorNode = resolveCoordinatorNode(ctx);
1934
2356
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
1935
2357
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
2358
+ const isLocalNode = isLocalControlPlaneNode(ctx, node);
2359
+ if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
2360
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
2361
+ }
1936
2362
  let result;
1937
2363
  try {
1938
2364
  result = await commandForNode(ctx, node, "launch_cli", {
@@ -1973,7 +2399,6 @@ async function meshLaunchSession(ctx, args) {
1973
2399
  });
1974
2400
  } catch {
1975
2401
  }
1976
- const isLocalNode = isLocalControlPlaneNode(ctx, node);
1977
2402
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
1978
2403
  ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1979
2404
  });
@@ -1998,6 +2423,9 @@ async function meshLaunchSession(ctx, args) {
1998
2423
  const coordinatorNode = resolveCoordinatorNode(ctx);
1999
2424
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
2000
2425
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
2426
+ if (!coordinatorDaemonId) {
2427
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
2428
+ }
2001
2429
  try {
2002
2430
  const res = await ctx.transport.launch(node.daemonId, {
2003
2431
  type: resolvedProviderType,
@@ -2036,7 +2464,7 @@ async function meshGitStatus(ctx, args) {
2036
2464
  const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
2037
2465
  try {
2038
2466
  if (!isLocalTransport(ctx.transport) && node.daemonId) {
2039
- const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true);
2467
+ const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true, true);
2040
2468
  return JSON.stringify({
2041
2469
  nodeId: args.node_id,
2042
2470
  workspace: node.workspace,
@@ -2048,6 +2476,7 @@ async function meshGitStatus(ctx, args) {
2048
2476
  } else if (isLocalTransport(ctx.transport)) {
2049
2477
  const statusResult = await commandForNode(ctx, node, "git_status", {
2050
2478
  workspace: node.workspace,
2479
+ refreshUpstream: true,
2051
2480
  includeSubmodules: autoDiscoverSubmodules,
2052
2481
  submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
2053
2482
  });
@@ -2077,6 +2506,51 @@ async function meshGitStatus(ctx, args) {
2077
2506
  }, null, 2);
2078
2507
  }
2079
2508
  }
2509
+ async function meshFastForwardNode(ctx, args) {
2510
+ await refreshMeshFromDaemon(ctx);
2511
+ const node = await findNodeWithRefresh(ctx, args.node_id);
2512
+ const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
2513
+ if (node.policy?.readOnly) {
2514
+ return JSON.stringify({
2515
+ success: false,
2516
+ code: "node_read_only",
2517
+ nodeId: args.node_id,
2518
+ workspace: node.workspace,
2519
+ allowed: false,
2520
+ willRun: false,
2521
+ executed: false,
2522
+ blockingReasons: ["node_read_only"]
2523
+ }, null, 2);
2524
+ }
2525
+ try {
2526
+ const dryRun = args.dry_run === true || args.execute !== true;
2527
+ const result = await commandForNode(ctx, node, "fast_forward_mesh_node", {
2528
+ meshId: ctx.mesh.id,
2529
+ nodeId: node.id,
2530
+ workspace: node.workspace,
2531
+ branch: typeof args.branch === "string" ? args.branch : void 0,
2532
+ execute: args.execute === true && args.dry_run !== true,
2533
+ dryRun,
2534
+ updateSubmodules: args.update_submodules === true,
2535
+ submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
2536
+ });
2537
+ return JSON.stringify(unwrapCommandPayload(result), null, 2);
2538
+ } catch (e) {
2539
+ const failure = buildCoordinatorP2pRelayFailure(e, {
2540
+ command: "fast_forward_mesh_node",
2541
+ targetDaemonId: node.daemonId,
2542
+ nodeId: args.node_id
2543
+ });
2544
+ return JSON.stringify({
2545
+ ...failure,
2546
+ workspace: node.workspace,
2547
+ allowed: false,
2548
+ willRun: false,
2549
+ executed: false,
2550
+ blockingReasons: [failure.code || "mesh_fast_forward_unavailable"]
2551
+ }, null, 2);
2552
+ }
2553
+ }
2080
2554
  async function meshCheckpoint(ctx, args) {
2081
2555
  const node = await findNodeWithRefresh(ctx, args.node_id);
2082
2556
  if (node.policy?.readOnly) {
@@ -2162,6 +2636,7 @@ async function meshCloneNode(ctx, args) {
2162
2636
  if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
2163
2637
  else ctx.mesh.nodes.push(clonePayload.node);
2164
2638
  ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2639
+ await syncCoordinatorDaemonMeshCache(ctx);
2165
2640
  }
2166
2641
  return JSON.stringify(result, null, 2);
2167
2642
  } else if (!isLocalTransport(ctx.transport) && sourceNode.daemonId) {
@@ -2179,6 +2654,7 @@ async function meshCloneNode(ctx, args) {
2179
2654
  if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
2180
2655
  else ctx.mesh.nodes.push(clonePayload.node);
2181
2656
  ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2657
+ await syncCoordinatorDaemonMeshCache(ctx);
2182
2658
  }
2183
2659
  return JSON.stringify(res, null, 2);
2184
2660
  } catch (e) {
@@ -2274,6 +2750,43 @@ async function meshRemoveNode(ctx, args) {
2274
2750
  return JSON.stringify({ error: "Cloud mesh remove_node requires node daemonId" });
2275
2751
  }
2276
2752
  }
2753
+ function resolveRefineConfigNode(ctx, nodeId) {
2754
+ if (nodeId) return findNode(ctx.mesh, nodeId);
2755
+ const node = ctx.mesh.nodes.find((entry) => !!entry.workspace);
2756
+ if (!node) throw new Error("No mesh node with a workspace is available");
2757
+ return node;
2758
+ }
2759
+ async function meshRefineConfigSchema(ctx) {
2760
+ const node = resolveRefineConfigNode(ctx);
2761
+ const result = await commandForNode(ctx, node, "get_mesh_refine_config_schema", {});
2762
+ return JSON.stringify(result, null, 2);
2763
+ }
2764
+ async function meshValidateRefineConfig(ctx, args) {
2765
+ const node = resolveRefineConfigNode(ctx, args.node_id);
2766
+ const result = await commandForNode(ctx, node, "validate_mesh_refine_config", {
2767
+ workspace: node.workspace,
2768
+ inlineMesh: ctx.mesh,
2769
+ ...args.config ? { config: args.config } : {}
2770
+ });
2771
+ return JSON.stringify(result, null, 2);
2772
+ }
2773
+ async function meshSuggestRefineConfig(ctx, args) {
2774
+ const node = resolveRefineConfigNode(ctx, args.node_id);
2775
+ const result = await commandForNode(ctx, node, "suggest_mesh_refine_config", {
2776
+ workspace: node.workspace,
2777
+ inlineMesh: ctx.mesh
2778
+ });
2779
+ return JSON.stringify(result, null, 2);
2780
+ }
2781
+ async function meshRefinePlan(ctx, args) {
2782
+ const node = await findNodeWithRefresh(ctx, args.node_id);
2783
+ const result = await commandForNode(ctx, node, "plan_mesh_refine_node", {
2784
+ meshId: ctx.mesh.id,
2785
+ nodeId: args.node_id,
2786
+ inlineMesh: ctx.mesh
2787
+ });
2788
+ return JSON.stringify(result, null, 2);
2789
+ }
2277
2790
  async function meshRefineNode(ctx, args) {
2278
2791
  const node = await findNodeWithRefresh(ctx, args.node_id);
2279
2792
  if (isLocalTransport(ctx.transport)) {
@@ -2519,8 +3032,8 @@ var CloudTransport = class {
2519
3032
  if (!res.ok) throw new Error(`Approve failed: ${res.status}`);
2520
3033
  return res.json();
2521
3034
  }
2522
- async gitStatus(daemonId, workspace, includeDiff = true) {
2523
- const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff) });
3035
+ async gitStatus(daemonId, workspace, includeDiff = true, refreshUpstream = false) {
3036
+ const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff), refreshUpstream: String(refreshUpstream) });
2524
3037
  const res = await fetch(
2525
3038
  `${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-status?${params}`,
2526
3039
  { headers: this.headers() }
@@ -2915,6 +3428,22 @@ function formatChatResult(result, sessionId, format, limit = 50, compact = false
2915
3428
  }))
2916
3429
  }, null, 2);
2917
3430
  }
3431
+ if ((format === "text" || format === void 0) && compact && compactPayload) {
3432
+ const lines2 = outputMessages.slice(-limit).map((m) => {
3433
+ const role = m.role === "user" ? "User" : m.role === "assistant" ? "Agent" : m.role;
3434
+ const content = messageContent(m);
3435
+ const truncated = content.length > 500 ? `${content.slice(0, 500)}\u2026` : content;
3436
+ return `[${role}] ${truncated}`;
3437
+ });
3438
+ if (compactPayload.summary) {
3439
+ const truncatedSummary = compactPayload.summary.length > 500 ? `${compactPayload.summary.slice(0, 500)}\u2026` : compactPayload.summary;
3440
+ lines2.push(`[Summary] ${truncatedSummary}`);
3441
+ }
3442
+ if (result?.pollingAdvisory) {
3443
+ lines2.push(`Advisory: ${result.pollingAdvisory.message}`);
3444
+ }
3445
+ return lines2.length > 0 ? lines2.join("\n\n") : "No messages in chat.";
3446
+ }
2918
3447
  if (outputMessages.length === 0) {
2919
3448
  return result?.pollingAdvisory ? `No messages in chat.
2920
3449
 
@@ -4015,6 +4544,9 @@ async function startMcpServer(opts) {
4015
4544
  case "mesh_git_status":
4016
4545
  text = await meshGitStatus(meshCtx, a);
4017
4546
  break;
4547
+ case "mesh_fast_forward_node":
4548
+ text = await meshFastForwardNode(meshCtx, a);
4549
+ break;
4018
4550
  case "mesh_checkpoint":
4019
4551
  text = await meshCheckpoint(meshCtx, a);
4020
4552
  break;
@@ -4030,6 +4562,18 @@ async function startMcpServer(opts) {
4030
4562
  case "mesh_refine_node":
4031
4563
  text = await meshRefineNode(meshCtx, a);
4032
4564
  break;
4565
+ case "mesh_refine_config_schema":
4566
+ text = await meshRefineConfigSchema(meshCtx);
4567
+ break;
4568
+ case "mesh_validate_refine_config":
4569
+ text = await meshValidateRefineConfig(meshCtx, a);
4570
+ break;
4571
+ case "mesh_suggest_refine_config":
4572
+ text = await meshSuggestRefineConfig(meshCtx, a);
4573
+ break;
4574
+ case "mesh_refine_plan":
4575
+ text = await meshRefinePlan(meshCtx, a);
4576
+ break;
4033
4577
  case "mesh_cleanup_sessions":
4034
4578
  text = await meshCleanupSessions(meshCtx, a);
4035
4579
  break;