@adhdev/daemon-standalone 0.9.82-rc.18 → 0.9.82-rc.181

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,9 +35,128 @@ __export(index_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
+ // src/tools/mesh-tools.ts
39
+ var import_node_crypto = require("crypto");
40
+
38
41
  // src/transports/ipc.ts
39
42
  var DEFAULT_IPC_PORT = 19222;
40
43
  var DEFAULT_IPC_PATH = "/ipc";
44
+ var DEFAULT_IPC_COMMAND_TIMEOUT_MS = 15e3;
45
+ var IPC_COMMAND_TIMEOUTS_MS = {
46
+ mesh_relay_command: 12e4,
47
+ agent_command: 3e4,
48
+ git_status: 45e3,
49
+ git_diff_summary: 45e3,
50
+ fast_forward_mesh_node: 12e4,
51
+ mesh_status: 12e4
52
+ };
53
+ var WS_CONNECTING = 0;
54
+ var WS_OPEN = 1;
55
+ var POOL_IDLE_EVICT_MS = 5 * 6e4;
56
+ var POOL_MAX_AGE_MS = 10 * 6e4;
57
+ var connectionPool = /* @__PURE__ */ new Map();
58
+ function buildRequestId() {
59
+ return `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
60
+ }
61
+ function getTimeoutMs(type, nestedCommand) {
62
+ return Math.max(
63
+ IPC_COMMAND_TIMEOUTS_MS[type] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS,
64
+ IPC_COMMAND_TIMEOUTS_MS[nestedCommand] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS
65
+ );
66
+ }
67
+ function getOrCreateConnection(WebSocketCtor, url) {
68
+ const existing = connectionPool.get(url);
69
+ if (existing) {
70
+ const { readyState } = existing.ws;
71
+ const now2 = Date.now();
72
+ const isAlive = readyState === WS_CONNECTING || readyState === WS_OPEN;
73
+ const isIdle = now2 - existing.lastUsedAt > POOL_IDLE_EVICT_MS && existing.pending.size === 0;
74
+ const isTooOld = now2 - existing.createdAt > POOL_MAX_AGE_MS && existing.pending.size === 0;
75
+ if (isAlive && !isIdle && !isTooOld) {
76
+ return existing;
77
+ }
78
+ if (isAlive && (isIdle || isTooOld)) {
79
+ try {
80
+ existing.ws.close();
81
+ } catch {
82
+ }
83
+ connectionPool.delete(url);
84
+ }
85
+ connectionPool.delete(url);
86
+ }
87
+ const now = Date.now();
88
+ const conn = {
89
+ ws: new WebSocketCtor(url),
90
+ ready: false,
91
+ commandQueue: [],
92
+ pending: /* @__PURE__ */ new Map(),
93
+ lastUsedAt: now,
94
+ createdAt: now
95
+ };
96
+ connectionPool.set(url, conn);
97
+ const drainQueue = () => {
98
+ conn.ready = true;
99
+ for (const { type, args, requestId } of conn.commandQueue) {
100
+ conn.ws.send(JSON.stringify({ type: "ext:command", payload: { command: type, args, requestId } }));
101
+ }
102
+ conn.commandQueue = [];
103
+ };
104
+ let tornDown = false;
105
+ const teardown = (error) => {
106
+ if (tornDown) return;
107
+ tornDown = true;
108
+ connectionPool.delete(url);
109
+ conn.ready = false;
110
+ for (const [, req] of conn.pending) {
111
+ clearTimeout(req.timer);
112
+ req.reject(error);
113
+ }
114
+ conn.pending.clear();
115
+ conn.commandQueue = [];
116
+ };
117
+ conn.ws.addEventListener("open", () => {
118
+ conn.ws.send(JSON.stringify({
119
+ type: "ext:register",
120
+ payload: {
121
+ ideType: "mcp-server",
122
+ ideVersion: "1.0.0",
123
+ extensionVersion: "1.0.0",
124
+ instanceId: `mcp-server-${process.pid}`,
125
+ machineId: "mcp-server",
126
+ workspaceFolders: []
127
+ }
128
+ }));
129
+ });
130
+ conn.ws.addEventListener("message", (event) => {
131
+ try {
132
+ const raw = typeof event.data === "string" ? event.data : String(event.data);
133
+ const msg = JSON.parse(raw);
134
+ if (msg?.type === "daemon:welcome") {
135
+ drainQueue();
136
+ return;
137
+ }
138
+ if (msg?.type !== "ext:command_result") return;
139
+ const req = conn.pending.get(msg?.payload?.requestId);
140
+ if (!req) return;
141
+ conn.pending.delete(msg.payload.requestId);
142
+ clearTimeout(req.timer);
143
+ const payload = msg.payload;
144
+ if (payload?.success === false) {
145
+ req.reject(new Error(payload.error || "Daemon IPC command failed"));
146
+ } else {
147
+ req.resolve(payload?.result ?? payload);
148
+ }
149
+ } catch {
150
+ }
151
+ });
152
+ conn.ws.addEventListener("error", () => {
153
+ teardown(new Error(`Cannot connect to daemon IPC at ${url}`));
154
+ });
155
+ conn.ws.addEventListener("close", () => {
156
+ teardown(new Error(`Daemon IPC connection closed: ${url}`));
157
+ });
158
+ return conn;
159
+ }
41
160
  var IpcTransport = class {
42
161
  port;
43
162
  path;
@@ -66,73 +185,41 @@ var IpcTransport = class {
66
185
  args
67
186
  });
68
187
  }
69
- async sendIpcCommand(type, args) {
188
+ sendIpcCommand(type, args) {
70
189
  const WebSocketCtor = globalThis.WebSocket;
71
190
  if (!WebSocketCtor) {
72
- throw new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode");
191
+ return Promise.reject(new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode"));
73
192
  }
193
+ const requestId = buildRequestId();
194
+ const nestedCommand = typeof args?.command === "string" ? args.command : "";
195
+ const timeoutMs = getTimeoutMs(type, nestedCommand);
196
+ const targetDaemonId = typeof args?.targetDaemonId === "string" ? args.targetDaemonId : "";
197
+ const diagnosticParts = [
198
+ `command='${type}'`,
199
+ ...nestedCommand ? [`relayedCommand='${nestedCommand}'`] : [],
200
+ ...targetDaemonId ? [`targetDaemonId='${targetDaemonId.slice(0, 12)}'`] : [],
201
+ ...typeof args?.nodeId === "string" ? [`nodeId='${args.nodeId}'`] : [],
202
+ ...typeof args?.workspace === "string" ? [`workspace='${args.workspace}'`] : []
203
+ ];
204
+ const url = `ws://127.0.0.1:${this.port}${this.path}`;
74
205
  return new Promise((resolve, reject) => {
75
- const requestId = `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
76
- const ws = new WebSocketCtor(`ws://127.0.0.1:${this.port}${this.path}`);
77
- let settled = false;
78
- const finish = (fn) => {
79
- if (settled) return;
80
- settled = true;
81
- clearTimeout(timeout);
82
- try {
83
- ws.close();
84
- } catch {
85
- }
86
- fn();
87
- };
88
- const timeoutMs = type === "mesh_relay_command" ? 6e4 : 15e3;
89
- const timeout = setTimeout(() => {
90
- finish(() => reject(new Error(`Daemon IPC command '${type}' timed out after ${Math.round(timeoutMs / 1e3)}s`)));
206
+ let conn;
207
+ try {
208
+ conn = getOrCreateConnection(WebSocketCtor, url);
209
+ } catch (e) {
210
+ return reject(new Error(`Failed to create IPC connection: ${e?.message || e}`));
211
+ }
212
+ const timer = setTimeout(() => {
213
+ conn.pending.delete(requestId);
214
+ reject(new Error(`Daemon IPC ${diagnosticParts.join(" ")} timed out after ${Math.round(timeoutMs / 1e3)}s (requestId=${requestId})`));
91
215
  }, timeoutMs);
92
- let commandSent = false;
93
- const send = () => {
94
- if (commandSent) return;
95
- commandSent = true;
96
- ws.send(JSON.stringify({
97
- type: "ext:command",
98
- payload: { command: type, args, requestId }
99
- }));
100
- };
101
- ws.addEventListener("open", () => {
102
- ws.send(JSON.stringify({
103
- type: "ext:register",
104
- payload: {
105
- ideType: "mcp-server",
106
- ideVersion: "1.0.0",
107
- extensionVersion: "1.0.0",
108
- instanceId: `mcp-server-${process.pid}`,
109
- machineId: "mcp-server",
110
- workspaceFolders: []
111
- }
112
- }));
113
- });
114
- ws.addEventListener("message", (event) => {
115
- try {
116
- const raw = typeof event.data === "string" ? event.data : String(event.data);
117
- const msg = JSON.parse(raw);
118
- if (msg?.type === "daemon:welcome") {
119
- send();
120
- return;
121
- }
122
- if (msg?.type !== "ext:command_result") return;
123
- if (msg?.payload?.requestId !== requestId) return;
124
- const payload = msg.payload;
125
- if (payload?.success === false) {
126
- finish(() => reject(new Error(payload.error || `Daemon IPC command '${type}' failed`)));
127
- return;
128
- }
129
- finish(() => resolve(payload?.result ?? payload));
130
- } catch {
131
- }
132
- });
133
- ws.addEventListener("error", () => {
134
- finish(() => reject(new Error(`Cannot connect to daemon IPC at ws://127.0.0.1:${this.port}${this.path}`)));
135
- });
216
+ conn.pending.set(requestId, { resolve, reject, timer });
217
+ conn.lastUsedAt = Date.now();
218
+ if (conn.ready) {
219
+ conn.ws.send(JSON.stringify({ type: "ext:command", payload: { command: type, args, requestId } }));
220
+ } else {
221
+ conn.commandQueue.push({ type, args, requestId });
222
+ }
136
223
  });
137
224
  }
138
225
  };
@@ -241,17 +328,64 @@ function annotateRapidReadChatAdvisory(payload, options) {
241
328
 
242
329
  // src/tools/mesh-tools.ts
243
330
  var import_daemon_core = require("@adhdev/daemon-core");
331
+ var SESSION_PROVIDER_METADATA_TTL_MS = 30 * 6e4;
244
332
  var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
333
+ function getSessionMetadata(key) {
334
+ const entry = meshSessionProviderMetadata.get(key);
335
+ if (!entry) return void 0;
336
+ if (entry.expiresAt <= Date.now()) {
337
+ meshSessionProviderMetadata.delete(key);
338
+ return void 0;
339
+ }
340
+ return entry;
341
+ }
342
+ var ACTIVE_WORK_POLLING_BACKOFF_MS = 6e4;
343
+ function buildActiveWorkPollingGuidance(summary, now = Date.now()) {
344
+ if (!summary || summary.generatingCount <= 0) return void 0;
345
+ return {
346
+ activeGeneratingWork: true,
347
+ generatingCount: summary.generatingCount,
348
+ doNotPollBefore: new Date(now + ACTIVE_WORK_POLLING_BACKOFF_MS).toISOString(),
349
+ eventSurface: "pendingCoordinatorEvents",
350
+ nextRecommendedAction: "Wait for pendingCoordinatorEvents/completion events or an explicit user status request. If no terminal evidence appears and the user asks for status, make one bounded status check, then wait again.",
351
+ message: "Do not repeatedly poll mesh_status/mesh_view_queue/mesh_read_chat while delegated work is generating; terminal ledger or completion evidence will be surfaced through pendingCoordinatorEvents when available."
352
+ };
353
+ }
245
354
  function readString(value) {
246
355
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
247
356
  }
357
+ function summarizeTaskMessage(message) {
358
+ const taskSummary = message.replace(/\s+/g, " ").trim();
359
+ const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
360
+ return { taskTitle: taskTitle || "(untitled task)", taskSummary };
361
+ }
362
+ function buildDirectTaskPayload(message, via, opts) {
363
+ const descriptor = summarizeTaskMessage(message);
364
+ return {
365
+ source: "direct",
366
+ via,
367
+ taskId: opts.taskId,
368
+ message,
369
+ taskTitle: descriptor.taskTitle,
370
+ taskSummary: descriptor.taskSummary,
371
+ ...opts.taskMode ? { taskMode: opts.taskMode } : {},
372
+ ...opts.providerType ? { providerType: opts.providerType } : {},
373
+ ...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {},
374
+ ...opts.dispatchedToIdleSession !== void 0 ? { dispatchedToIdleSession: opts.dispatchedToIdleSession } : {}
375
+ };
376
+ }
377
+ function findNode(mesh, nodeId) {
378
+ const node = mesh.nodes.find((n) => n.id === nodeId);
379
+ if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
380
+ return node;
381
+ }
248
382
  var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
249
383
  var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
250
384
  var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
251
385
  var ACTIVE_QUEUE_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned"]);
252
386
  var HISTORICAL_QUEUE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
253
387
  async function refreshMeshFromDaemon(ctx) {
254
- if (!(ctx.transport instanceof IpcTransport)) return;
388
+ if (!isLocalTransport(ctx.transport)) return;
255
389
  try {
256
390
  const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
257
391
  if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
@@ -404,6 +538,33 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
404
538
  function readSessionRecordId(session) {
405
539
  return readString(session?.id) || readString(session?.sessionId) || readString(session?.session_id) || readString(session?.runtimeSessionId) || readString(session?.runtime_session_id) || readString(session?.instanceId) || readString(session?.instance_id);
406
540
  }
541
+ function extractStatusMetadataSessions(value) {
542
+ const payload = unwrapCommandPayload(value);
543
+ const status = payload?.status && typeof payload.status === "object" ? payload.status : payload;
544
+ return Array.isArray(status?.sessions) ? status.sessions : [];
545
+ }
546
+ function resolveSessionProviderType(session) {
547
+ return readString(session?.providerType) || readString(session?.cliType) || readString(session?.agentType) || "";
548
+ }
549
+ function isMeshCoordinatorSessionRecord(session) {
550
+ return Boolean(
551
+ readString(session?.settings?.meshCoordinatorFor) || readString(session?.meta?.meshCoordinatorFor) || readString(session?.metadata?.meshCoordinatorFor) || readString(session?.meshCoordinatorFor)
552
+ );
553
+ }
554
+ function isUnmanagedSessionRecord(session) {
555
+ const hasMeshNodeFor = Boolean(
556
+ readString(session?.settings?.meshNodeFor) || readString(session?.meta?.meshNodeFor) || readString(session?.metadata?.meshNodeFor) || readString(session?.meshNodeFor)
557
+ );
558
+ if (hasMeshNodeFor) return false;
559
+ if (isMeshCoordinatorSessionRecord(session)) return false;
560
+ const launchedByCoordinator = Boolean(
561
+ session?.settings?.launchedByCoordinator === true || session?.meta?.launchedByCoordinator === true || session?.launchedByCoordinator === true
562
+ );
563
+ return !launchedByCoordinator;
564
+ }
565
+ function isWorkerTaskMode(taskMode) {
566
+ return taskMode !== "live_debug_readonly";
567
+ }
407
568
  function addSessionRecord(target, session) {
408
569
  if (!session || typeof session !== "object" || isTerminalSessionRecord(session)) return;
409
570
  const sessionId = readSessionRecordId(session);
@@ -472,18 +633,26 @@ function queueAssignmentStaleReason(task, liveness) {
472
633
  }
473
634
  function buildQueueStatusSummary(queue) {
474
635
  const counts = { pending: 0, assigned: 0, completed: 0, failed: 0, cancelled: 0 };
636
+ let staleAssigned = 0;
475
637
  for (const task of queue) {
476
638
  const status = typeof task?.status === "string" ? task.status : void 0;
477
639
  if (status && Object.prototype.hasOwnProperty.call(counts, status)) {
478
640
  counts[status] += 1;
479
641
  }
642
+ if (status === "assigned" && task?.staleAssigned === true) staleAssigned += 1;
480
643
  }
644
+ const liveAssigned = Math.max(0, counts.assigned - staleAssigned);
481
645
  return {
482
646
  totalCount: queue.length,
483
- activeCount: counts.pending + counts.assigned,
647
+ activeCount: counts.pending + liveAssigned,
484
648
  historicalCount: counts.completed + counts.failed + counts.cancelled,
485
649
  counts,
486
650
  activeCounts: {
651
+ pending: counts.pending,
652
+ assigned: liveAssigned
653
+ },
654
+ staleAssignedCount: staleAssigned,
655
+ rawActiveCounts: {
487
656
  pending: counts.pending,
488
657
  assigned: counts.assigned
489
658
  },
@@ -511,6 +680,18 @@ function filterQueueForView(queue, view, statuses) {
511
680
  if (view === "historical") return queue.filter((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
512
681
  return queue;
513
682
  }
683
+ function prioritizeActiveQueueRows(queue) {
684
+ const active = [];
685
+ const historical = [];
686
+ const other = [];
687
+ for (const task of queue) {
688
+ const status = String(task?.status || "");
689
+ if (ACTIVE_QUEUE_STATUSES.has(status)) active.push(task);
690
+ else if (HISTORICAL_QUEUE_STATUSES.has(status)) historical.push(task);
691
+ else other.push(task);
692
+ }
693
+ return [...active, ...other, ...historical];
694
+ }
514
695
  function slimQueueTask(task) {
515
696
  return {
516
697
  id: task?.id,
@@ -616,22 +797,59 @@ function isIdleSessionRecord(session) {
616
797
  const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
617
798
  return status === "idle" || chatStatus === "waiting_input";
618
799
  }
800
+ function isMeshOwnedDelegateSession(session, meshId, nodeId) {
801
+ const settings = session?.settings;
802
+ const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
803
+ const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
804
+ if (sessionMeshId !== meshId) return false;
805
+ return !sessionNodeId || sessionNodeId === nodeId;
806
+ }
619
807
  function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
620
808
  const live = sessions.filter((session) => !isTerminalSessionRecord(session));
621
809
  const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
622
- const isMeshOwnedDelegateSession = (session) => {
623
- const settings = session?.settings;
624
- const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
625
- const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
626
- const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
627
- if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
628
- return !sessionNodeId || sessionNodeId === nodeId;
629
- };
630
810
  const meshSessions = live.filter(
631
- (session) => isMeshOwnedDelegateSession(session)
811
+ (session) => isMeshOwnedDelegateSession(session, meshId, nodeId)
632
812
  );
633
813
  return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || void 0;
634
814
  }
815
+ function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
816
+ return {
817
+ success: false,
818
+ recoverable: true,
819
+ code: "mesh_delegate_session_missing_relay_metadata",
820
+ reason: "mesh_delegate_session_missing_relay_metadata",
821
+ transport: "mesh_transport",
822
+ retryRecommended: true,
823
+ meshId: ctx.mesh.id,
824
+ nodeId: node.id,
825
+ daemonId: node.daemonId,
826
+ workspace: node.workspace,
827
+ sessionId,
828
+ unsafeTranscriptAlias: true,
829
+ ...providerType ? { resolvedProviderType: providerType } : {},
830
+ error: `Remote session '${sessionId}' is not relay-safe for mesh '${ctx.mesh.id}': missing meshNodeFor/meshCoordinatorDaemonId metadata, so completion events would not reach the coordinator ledger. This session may be the coordinator itself or an unrelated session (unsafe_transcript_alias risk).`,
831
+ 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.`,
832
+ noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
833
+ };
834
+ }
835
+ function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
836
+ return {
837
+ success: false,
838
+ recoverable: true,
839
+ code: "mesh_coordinator_daemon_unknown",
840
+ reason: "mesh_coordinator_daemon_unknown",
841
+ transport: "mesh_transport",
842
+ retryRecommended: true,
843
+ meshId: ctx.mesh.id,
844
+ nodeId: node.id,
845
+ daemonId: node.daemonId,
846
+ workspace: node.workspace,
847
+ ...providerType ? { resolvedProviderType: providerType } : {},
848
+ 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.`,
849
+ 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.",
850
+ noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
851
+ };
852
+ }
635
853
  function findNestedPayload(value, predicate) {
636
854
  const seen = /* @__PURE__ */ new Set();
637
855
  const stack = [{ payload: value, depth: 0 }];
@@ -659,12 +877,16 @@ function extractGitDiff(value) {
659
877
  }
660
878
  function extractSubmodules(value, ignorePaths) {
661
879
  const payload = unwrapCommandPayload(value);
662
- const subs = payload?.submodules ?? value?.submodules;
880
+ const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
663
881
  if (!Array.isArray(subs)) return void 0;
664
882
  if (ignorePaths.length === 0) return subs;
665
883
  const ignoreSet = new Set(ignorePaths);
666
884
  return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
667
885
  }
886
+ function assignFullGitSnapshot(entry, status) {
887
+ if (!status || typeof status !== "object" || Array.isArray(status)) return;
888
+ entry.git = status;
889
+ }
668
890
  function extractLaunchPayload(value) {
669
891
  return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
670
892
  }
@@ -789,20 +1011,76 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
789
1011
  let sessionId = args.session_id?.trim() || "";
790
1012
  const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
791
1013
  let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
792
- if (!sessionId) {
1014
+ if (sessionId && args.verifiedSession) {
1015
+ const explicitSession = args.verifiedSession;
1016
+ if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
1017
+ return buildRelayUnsafeRemoteSessionFailure(
1018
+ ctx,
1019
+ node,
1020
+ sessionId,
1021
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1022
+ );
1023
+ }
1024
+ if (!resolvedProviderType) {
1025
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
1026
+ }
1027
+ } else if (!sessionId || args.session_id) {
793
1028
  try {
794
1029
  const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
795
- const innerResult = relayResult?.result ?? relayResult;
796
- const statusObj = innerResult?.status ?? innerResult;
797
- const sessions = Array.isArray(statusObj?.sessions) ? statusObj.sessions : [];
798
- const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
799
- if (targetSession?.id || targetSession?.sessionId) {
800
- sessionId = targetSession.id || targetSession.sessionId;
1030
+ const sessions = extractStatusMetadataSessions(relayResult);
1031
+ if (sessionId) {
1032
+ const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
1033
+ if (!explicitSession) {
1034
+ return {
1035
+ success: false,
1036
+ recoverable: true,
1037
+ code: "mesh_target_session_not_found",
1038
+ reason: "mesh_target_session_not_found",
1039
+ transport: "mesh_transport",
1040
+ retryRecommended: true,
1041
+ meshId: ctx.mesh.id,
1042
+ nodeId: node.id,
1043
+ daemonId,
1044
+ workspace: node.workspace,
1045
+ sessionId,
1046
+ ...resolvedProviderType ? { resolvedProviderType } : {},
1047
+ error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
1048
+ 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.`
1049
+ };
1050
+ }
1051
+ if (!isMeshOwnedDelegateSession(explicitSession, ctx.mesh.id, node.id)) {
1052
+ return buildRelayUnsafeRemoteSessionFailure(
1053
+ ctx,
1054
+ node,
1055
+ sessionId,
1056
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1057
+ );
1058
+ }
801
1059
  if (!resolvedProviderType) {
802
- resolvedProviderType = targetSession.providerType || targetSession.cliType || "";
1060
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
1061
+ }
1062
+ } else {
1063
+ const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
1064
+ if (targetSession?.id || targetSession?.sessionId) {
1065
+ sessionId = targetSession.id || targetSession.sessionId;
1066
+ if (!resolvedProviderType) {
1067
+ resolvedProviderType = resolveSessionProviderType(targetSession);
1068
+ }
803
1069
  }
804
1070
  }
805
1071
  } catch (e) {
1072
+ if (sessionId) {
1073
+ return {
1074
+ ...buildCoordinatorP2pRelayFailure(e, {
1075
+ command: "get_status_metadata",
1076
+ targetDaemonId: daemonId,
1077
+ nodeId: node.id,
1078
+ sessionId
1079
+ }),
1080
+ success: false,
1081
+ error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
1082
+ };
1083
+ }
806
1084
  }
807
1085
  }
808
1086
  if (!resolvedProviderType) {
@@ -814,7 +1092,8 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
814
1092
  agentType: resolvedProviderType,
815
1093
  cliType: resolvedProviderType,
816
1094
  action: "send_chat",
817
- message: args.message
1095
+ message: args.message,
1096
+ ...args.meshContext ? { meshContext: args.meshContext } : {}
818
1097
  });
819
1098
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
820
1099
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
@@ -832,7 +1111,7 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
832
1111
  error: `P2P dispatch failed: ${errorMessage}`
833
1112
  };
834
1113
  }
835
- return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
1114
+ return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType, providerType: resolvedProviderType };
836
1115
  } catch (e) {
837
1116
  const errorMessage = e?.message || String(e);
838
1117
  return {
@@ -862,34 +1141,199 @@ function resolveCoordinatorNode(ctx) {
862
1141
  return void 0;
863
1142
  }
864
1143
  function readNodeMachineId(node) {
865
- return readString(node.machineId) || readString(node.machine_id);
1144
+ return readString(node.machineId) || readString(node.machine_id) || readString(node.machine?.id) || readString(node.machine?.machineId) || readString(node.lastProbe?.machineId) || readString(node.last_probe?.machine_id) || readString(node.lastProbe?.machine?.id) || readString(node.lastProbe?.machine?.machineId) || readString(node.last_probe?.machine?.id) || readString(node.last_probe?.machine?.machine_id);
866
1145
  }
867
1146
  function readNodeDaemonId(node) {
868
- return readString(node.daemonId) || readString(node.daemon_id);
1147
+ return readString(node.daemonId) || readString(node.daemon_id) || readString(node.machine?.daemonId) || readString(node.machine?.daemon_id) || readString(node.lastProbe?.daemonId) || readString(node.last_probe?.daemon_id) || readString(node.lastProbe?.machine?.daemonId) || readString(node.lastProbe?.machine?.daemon_id) || readString(node.last_probe?.machine?.daemonId) || readString(node.last_probe?.machine?.daemon_id);
1148
+ }
1149
+ function normalizeHostname(value) {
1150
+ const hostname = readString(value);
1151
+ if (!hostname) return void 0;
1152
+ return hostname.toLowerCase().replace(/\.$/, "");
1153
+ }
1154
+ function readNodeHostname(node) {
1155
+ return readString(node.hostname) || readString(node.host) || readString(node.machineHostname) || readString(node.machine_hostname) || readString(node.machine?.hostname) || readString(node.machine?.host) || readString(node.lastProbe?.hostname) || readString(node.last_probe?.hostname) || readString(node.lastProbe?.machine?.hostname) || readString(node.last_probe?.machine?.hostname);
1156
+ }
1157
+ function readNodeDisplayMachineName(node) {
1158
+ return readString(node.machineName) || readString(node.machine_name) || readString(node.machineLabel) || readString(node.machine_label) || readString(node.machineNickname) || readString(node.machine_nickname) || readString(node.alias) || readString(node.machine?.name) || readString(node.machine?.displayName) || readString(node.machine?.display_name) || readString(node.lastProbe?.machineName) || readString(node.last_probe?.machine_name) || readString(node.lastProbe?.machine?.name) || readString(node.last_probe?.machine?.name) || readNodeHostname(node);
1159
+ }
1160
+ function compactIdentityEvidence(value) {
1161
+ if (!value) return void 0;
1162
+ return value.length > 24 ? `${value.slice(0, 12)}\u2026${value.slice(-8)}` : value;
1163
+ }
1164
+ function pushIdentityEvidence(evidence, label, value) {
1165
+ const compact = compactIdentityEvidence(value);
1166
+ if (compact) evidence.push(`${label}:${compact}`);
1167
+ }
1168
+ function buildNodeMachineIdentity(ctx, node) {
1169
+ const machineId = readNodeMachineId(node);
1170
+ const daemonId = readNodeDaemonId(node);
1171
+ const hostname = readNodeHostname(node);
1172
+ const machineName = readNodeDisplayMachineName(node);
1173
+ const coordinatorHostname = readString(ctx.coordinatorHostname);
1174
+ const localControlPlaneReason = getLocalControlPlaneMatchReason(ctx, node);
1175
+ const directLocal = !!localControlPlaneReason;
1176
+ const hostnameMatches = Boolean(
1177
+ normalizeHostname(hostname) && normalizeHostname(coordinatorHostname) && normalizeHostname(hostname) === normalizeHostname(coordinatorHostname)
1178
+ );
1179
+ const sameMachine = directLocal || hostnameMatches;
1180
+ const evidence = [];
1181
+ pushIdentityEvidence(evidence, "machineName", machineName);
1182
+ pushIdentityEvidence(evidence, "hostname", hostname);
1183
+ pushIdentityEvidence(evidence, "machineId", machineId);
1184
+ pushIdentityEvidence(evidence, "daemonId", daemonId);
1185
+ if (localControlPlaneReason) {
1186
+ pushIdentityEvidence(evidence, "localMatch", localControlPlaneReason);
1187
+ pushIdentityEvidence(evidence, "localMachineId", ctx.localMachineId);
1188
+ pushIdentityEvidence(evidence, "localDaemonId", ctx.localDaemonId);
1189
+ }
1190
+ const locality = sameMachine ? "same_machine" : evidence.length > 0 ? "remote_known" : "remote_or_unknown";
1191
+ const localityReason = sameMachine ? localControlPlaneReason || "matched coordinator hostname" : evidence.length > 0 ? `known remote/other machine identity; no local coordinator match (${evidence.join(", ")})` : "no useful machine identity evidence available";
1192
+ return {
1193
+ daemonId,
1194
+ machineId,
1195
+ hostname,
1196
+ machineName,
1197
+ displayName: machineName || hostname || daemonId || machineId,
1198
+ coordinatorHostname,
1199
+ sameMachine,
1200
+ locality,
1201
+ localityReason,
1202
+ identityEvidence: evidence
1203
+ };
1204
+ }
1205
+ function nodeHasLocalDaemonEvidence(ctx, node) {
1206
+ const isLocal = (session) => {
1207
+ if (!session || typeof session !== "object") return false;
1208
+ if (ctx.localDaemonId && session.settings?.meshCoordinatorDaemonId === ctx.localDaemonId) return true;
1209
+ if (session.launchedByCoordinator === true) return true;
1210
+ if (ctx.localDaemonId && session.runtime?.owner === ctx.localDaemonId) return true;
1211
+ if (ctx.localDaemonId && session.daemonClient?.daemonId === ctx.localDaemonId) return true;
1212
+ return false;
1213
+ };
1214
+ const sessionArrays = [
1215
+ node?.sessions,
1216
+ node?.activeSessions,
1217
+ node?.active_sessions,
1218
+ node?.lastProbe?.sessions,
1219
+ node?.last_probe?.sessions,
1220
+ node?.lastProbe?.status?.sessions,
1221
+ node?.last_probe?.status?.sessions
1222
+ ];
1223
+ for (const arr of sessionArrays) {
1224
+ if (Array.isArray(arr) && arr.some(isLocal)) return true;
1225
+ }
1226
+ const sessionRecords = [
1227
+ node?.activeSession,
1228
+ node?.active_session,
1229
+ node?.currentSession,
1230
+ node?.current_session,
1231
+ node?.runtimeSession,
1232
+ node?.runtime_session,
1233
+ node?.session,
1234
+ node?.lastProbe?.activeSession,
1235
+ node?.last_probe?.active_session,
1236
+ node?.lastProbe?.currentSession,
1237
+ node?.last_probe?.current_session,
1238
+ node?.lastProbe?.session,
1239
+ node?.last_probe?.session
1240
+ ];
1241
+ for (const session of sessionRecords) {
1242
+ if (isLocal(session)) return true;
1243
+ }
1244
+ return false;
869
1245
  }
870
1246
  function isDirectLocalNode(ctx, node) {
871
1247
  const machineId = readNodeMachineId(node);
872
1248
  const daemonId = readNodeDaemonId(node);
873
1249
  return Boolean(
874
- ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId
1250
+ ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId || nodeHasLocalDaemonEvidence(ctx, node)
875
1251
  );
876
1252
  }
1253
+ function isConfiguredCoordinatorNode(ctx, node) {
1254
+ if (!ctx.localMachineId && !ctx.localDaemonId) return false;
1255
+ const nodeId = readString(node.id) || readString(node.nodeId) || readString(node.node_id);
1256
+ if (!nodeId) return false;
1257
+ const nodeDaemonId = readNodeDaemonId(node);
1258
+ const nodeMachineId = readNodeMachineId(node);
1259
+ if (nodeDaemonId && ctx.localDaemonId && nodeDaemonId !== ctx.localDaemonId) return false;
1260
+ if (nodeMachineId && ctx.localMachineId && nodeMachineId !== ctx.localMachineId) return false;
1261
+ const preferredNodeId = readString(ctx.mesh.coordinator?.preferredNodeId) || readString(ctx.mesh.coordinator?.preferred_node_id);
1262
+ if (preferredNodeId) return nodeId === preferredNodeId;
1263
+ const first = ctx.mesh.nodes?.[0];
1264
+ const firstNodeId = readString(first?.id) || readString(first?.nodeId) || readString(first?.node_id);
1265
+ return !!firstNodeId && nodeId === firstNodeId;
1266
+ }
1267
+ function getLocalControlPlaneMatchReason(ctx, node) {
1268
+ if (isDirectLocalNode(ctx, node)) return "matched coordinator daemon or machine id";
1269
+ if (isConfiguredCoordinatorNode(ctx, node)) return "matched configured coordinator node";
1270
+ if (node.isLocalWorktree === true) {
1271
+ const sourceNode = findClonedFromNode(ctx, node);
1272
+ if (sourceNode && isDirectLocalNode(ctx, sourceNode)) return "matched local cloned-from node";
1273
+ if (sourceNode && isConfiguredCoordinatorNode(ctx, sourceNode)) return "matched configured coordinator source node";
1274
+ }
1275
+ return void 0;
1276
+ }
877
1277
  function findClonedFromNode(ctx, node) {
878
1278
  const clonedFromNodeId = readString(node.clonedFromNodeId) || readString(node.cloned_from_node_id);
879
1279
  if (!clonedFromNodeId) return void 0;
880
1280
  return ctx.mesh.nodes.find((n) => n.id === clonedFromNodeId || n.nodeId === clonedFromNodeId || n.node_id === clonedFromNodeId);
881
1281
  }
882
1282
  function isLocalControlPlaneNode(ctx, node) {
883
- if (isDirectLocalNode(ctx, node)) return true;
884
- if (node.isLocalWorktree === true) {
885
- const sourceNode = findClonedFromNode(ctx, node);
886
- if (sourceNode && isDirectLocalNode(ctx, sourceNode)) return true;
887
- }
888
- return false;
1283
+ return !!getLocalControlPlaneMatchReason(ctx, node);
889
1284
  }
890
1285
  function meshSessionCacheKey(nodeId, runtimeSessionId) {
891
1286
  return `${nodeId}:${runtimeSessionId}`;
892
1287
  }
1288
+ function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata) {
1289
+ const keyNodeId = readString(nodeId);
1290
+ const keySessionId = readString(runtimeSessionId);
1291
+ if (!keyNodeId || !keySessionId) return;
1292
+ const providerType = readString(metadata.providerType);
1293
+ const providerSessionId = readString(metadata.providerSessionId);
1294
+ if (!providerType && !providerSessionId) return;
1295
+ const existing = getSessionMetadata(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
1296
+ meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
1297
+ providerType: providerType || existing.providerType,
1298
+ providerSessionId: providerSessionId || existing.providerSessionId,
1299
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
1300
+ });
1301
+ }
1302
+ function rememberMeshSessionProviderMetadataFromEvent(event) {
1303
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : event && typeof event === "object" ? event : {};
1304
+ const nodeId = readString(event?.nodeId) || readString(metadataEvent.nodeId) || readString(metadataEvent.meshNodeId);
1305
+ const sessionId = readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId) || readString(event?.sessionId);
1306
+ rememberMeshSessionProviderMetadata(nodeId, sessionId, {
1307
+ providerType: readString(metadataEvent.providerType) || readString(event?.providerType) || "",
1308
+ providerSessionId: readString(metadataEvent.providerSessionId) || readString(event?.providerSessionId)
1309
+ });
1310
+ }
1311
+ function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
1312
+ const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 50 });
1313
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
1314
+ const entry = entries[i];
1315
+ const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
1316
+ const entryNodeId = readString(entry.nodeId) || readString(payload.nodeId) || readString(payload.meshNodeId);
1317
+ if (entryNodeId && entryNodeId !== nodeId) continue;
1318
+ const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
1319
+ if (entrySessionId !== runtimeSessionId) continue;
1320
+ const providerType = readString(entry.providerType) || readString(payload.providerType);
1321
+ const completionDiagnostic = payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? payload.completionDiagnostic : {};
1322
+ const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : {};
1323
+ const providerSessionId = readString(payload.providerSessionId) || readString(completionDiagnostic.providerSessionId) || readString(metadataEvent.providerSessionId);
1324
+ if (providerType || providerSessionId) {
1325
+ return { providerType: providerType || "", providerSessionId };
1326
+ }
1327
+ }
1328
+ return void 0;
1329
+ }
1330
+ function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
1331
+ const cached = getSessionMetadata(meshSessionCacheKey(nodeId, runtimeSessionId));
1332
+ if (cached?.providerType || cached?.providerSessionId) return cached;
1333
+ const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
1334
+ if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
1335
+ return fromLedger;
1336
+ }
893
1337
  function countUncommittedChanges(status) {
894
1338
  if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
895
1339
  const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
@@ -902,6 +1346,20 @@ function isGitStatusDirty(status) {
902
1346
  if (typeof status?.dirty === "boolean") return status.dirty;
903
1347
  return countUncommittedChanges(status) > 0;
904
1348
  }
1349
+ function slimLedgerPayload(payload) {
1350
+ const slim = {};
1351
+ for (const [k, v] of Object.entries(payload)) {
1352
+ if (k === "message" || k === "taskSummary") {
1353
+ slim[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "\u2026" : v;
1354
+ } else if (k === "evidence" || k === "workerResult" || k === "gitStatus" || k === "validationResults") {
1355
+ } else if (k === "finalSummary") {
1356
+ slim[k] = typeof v === "string" && v.length > 300 ? v.slice(0, 300) + "\u2026" : v;
1357
+ } else {
1358
+ slim[k] = v;
1359
+ }
1360
+ }
1361
+ return slim;
1362
+ }
905
1363
  function readRelatedRepos(node) {
906
1364
  const raw = Array.isArray(node.relatedRepos) ? node.relatedRepos : Array.isArray(node.policy?.relatedRepos) ? node.policy.relatedRepos : [];
907
1365
  return raw.map((entry) => ({
@@ -960,6 +1418,16 @@ function missingProviderPriorityMessage(nodeId) {
960
1418
  return `Node '${nodeId}' has no providerPriority policy; pass type explicitly or configure node.policy.providerPriority`;
961
1419
  }
962
1420
  function getNodeLaunchReadiness(node) {
1421
+ const bootstrap = node.worktreeBootstrap;
1422
+ if (node.isLocalWorktree && bootstrap?.status === "failed" && bootstrap?.required !== false) {
1423
+ return {
1424
+ providerPriority: readProviderPriority(node.policy),
1425
+ launchReady: false,
1426
+ launchBlockedReason: "worktree_bootstrap_failed",
1427
+ launchBlockedMessage: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : "Required worktree bootstrap failed; resolve it before launching an agent into this node.",
1428
+ worktreeBootstrap: bootstrap
1429
+ };
1430
+ }
963
1431
  const providerPriority = readProviderPriority(node.policy);
964
1432
  if (providerPriority.length) {
965
1433
  return {
@@ -974,6 +1442,33 @@ function getNodeLaunchReadiness(node) {
974
1442
  launchBlockedMessage: missingProviderPriorityMessage(node.id)
975
1443
  };
976
1444
  }
1445
+ function getWorktreeBootstrapLaunchBlock(node) {
1446
+ const bootstrap = node.worktreeBootstrap;
1447
+ if (!node.isLocalWorktree || bootstrap?.status !== "failed" || bootstrap?.required === false) return void 0;
1448
+ return {
1449
+ success: false,
1450
+ code: "worktree_bootstrap_failed",
1451
+ error: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : `Node '${node.id}' has a failed required worktree bootstrap.`,
1452
+ nodeId: node.id,
1453
+ worktreeBootstrap: bootstrap,
1454
+ recoveryHint: "Fix the configured worktree bootstrap command or remove/recreate the worktree node before launching an agent."
1455
+ };
1456
+ }
1457
+ async function collectLiveStatusSessions(ctx, node) {
1458
+ try {
1459
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
1460
+ return extractStatusMetadataSessions(statusResult);
1461
+ } catch {
1462
+ return [];
1463
+ }
1464
+ }
1465
+ async function collectMeshViewQueueNodesWithLiveSessions(ctx) {
1466
+ const nodes = await Promise.all(ctx.mesh.nodes.map(async (node) => {
1467
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
1468
+ return liveSessions.length > 0 ? { ...node, sessions: liveSessions } : node;
1469
+ }));
1470
+ return nodes;
1471
+ }
977
1472
  function readNumeric(value, fallback = 0) {
978
1473
  const parsed = Number(value);
979
1474
  return Number.isFinite(parsed) ? parsed : fallback;
@@ -1109,7 +1604,8 @@ async function commandForNode(ctx, node, command, args = {}) {
1109
1604
  if (isLocalTransport(ctx.transport)) {
1110
1605
  return ctx.transport.command(command, args);
1111
1606
  }
1112
- throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
1607
+ const identity = buildNodeMachineIdentity(ctx, node);
1608
+ throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}' (hostname=${identity.hostname || "unknown"}, coordinatorHostname=${identity.coordinatorHostname || "unknown"}, sameMachine=${identity.sameMachine})`);
1113
1609
  }
1114
1610
  function normalizePendingMeshCoordinatorEvents(value) {
1115
1611
  const payload = unwrapCommandPayload(value);
@@ -1127,6 +1623,14 @@ function buildMeshForwardPayloadFromPendingEvent(event) {
1127
1623
  providerType: readString(metadataEvent.providerType),
1128
1624
  providerSessionId: readString(metadataEvent.providerSessionId),
1129
1625
  finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
1626
+ jobId: readString(metadataEvent.jobId),
1627
+ interactionId: readString(metadataEvent.interactionId),
1628
+ status: readString(metadataEvent.status),
1629
+ targetDaemonId: readString(metadataEvent.targetDaemonId),
1630
+ startedAt: readString(metadataEvent.startedAt),
1631
+ completedAt: readString(metadataEvent.completedAt),
1632
+ retryOfJobId: readString(metadataEvent.retryOfJobId),
1633
+ ...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
1130
1634
  ...metadataEvent.intentional === true ? { intentional: true } : {},
1131
1635
  ...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
1132
1636
  ...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
@@ -1143,8 +1647,9 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
1143
1647
  const surfacedEvents = [];
1144
1648
  try {
1145
1649
  surfacedEvents.push(
1146
- ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
1650
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1147
1651
  );
1652
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1148
1653
  } catch {
1149
1654
  }
1150
1655
  for (const node of ctx.mesh.nodes) {
@@ -1152,27 +1657,31 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
1152
1657
  if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
1153
1658
  try {
1154
1659
  const remoteEvents = normalizePendingMeshCoordinatorEvents(
1155
- await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", {})
1660
+ await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", { meshId: ctx.mesh.id })
1156
1661
  ).filter(matchesCurrentMesh);
1157
1662
  if (remoteEvents.length === 0) continue;
1158
1663
  for (const event of remoteEvents) {
1159
1664
  const payload = buildMeshForwardPayloadFromPendingEvent(event);
1160
1665
  if (!payload.event || !payload.meshId) continue;
1161
1666
  await ctx.transport.command("mesh_forward_event", payload);
1667
+ rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
1162
1668
  }
1163
1669
  } catch {
1164
1670
  }
1165
1671
  }
1166
1672
  try {
1167
1673
  surfacedEvents.push(
1168
- ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
1674
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1169
1675
  );
1676
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1170
1677
  } catch {
1171
1678
  }
1172
1679
  return surfacedEvents;
1173
1680
  }
1174
1681
  if (isLocalTransport(ctx.transport)) {
1175
- return (0, import_daemon_core.drainPendingMeshCoordinatorEvents)().filter(matchesCurrentMesh);
1682
+ const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId).filter(matchesCurrentMesh);
1683
+ events.forEach(rememberMeshSessionProviderMetadataFromEvent);
1684
+ return events;
1176
1685
  }
1177
1686
  return [];
1178
1687
  }
@@ -1189,11 +1698,12 @@ function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
1189
1698
  }
1190
1699
  var MESH_STATUS_TOOL = {
1191
1700
  name: "mesh_status",
1192
- description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures.",
1701
+ description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures. Do not repeatedly call this to wait for generating delegated work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
1193
1702
  inputSchema: {
1194
1703
  type: "object",
1195
1704
  properties: {
1196
- _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
1705
+ _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." },
1706
+ includeStaleDirectWorkDetails: { type: "boolean", description: "Opt in to the full staleDirectWork array. Defaults false; normal status returns compact staleDirectWorkSummary only." }
1197
1707
  }
1198
1708
  }
1199
1709
  };
@@ -1213,14 +1723,18 @@ var MESH_ENQUEUE_TASK_TOOL = {
1213
1723
  inputSchema: {
1214
1724
  type: "object",
1215
1725
  properties: {
1216
- message: { type: "string", description: "The task instruction for the agent." }
1726
+ message: { type: "string", description: "The task instruction for the agent." },
1727
+ 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." },
1728
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
1729
+ requiredTags: { type: "array", items: { type: "string" }, description: "Optional capability tags that every eligible node must have, e.g. os=darwin, provider=codex-cli, gpu." },
1730
+ required_tags: { type: "array", items: { type: "string" }, description: "Snake_case alias for requiredTags." }
1217
1731
  },
1218
1732
  required: ["message"]
1219
1733
  }
1220
1734
  };
1221
1735
  var MESH_VIEW_QUEUE_TOOL = {
1222
1736
  name: "mesh_view_queue",
1223
- description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records.",
1737
+ description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records. Do not repeatedly call this to wait for generating assigned work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
1224
1738
  inputSchema: {
1225
1739
  type: "object",
1226
1740
  properties: {
@@ -1273,7 +1787,9 @@ var MESH_SEND_TASK_TOOL = {
1273
1787
  properties: {
1274
1788
  node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
1275
1789
  session_id: { type: "string", description: "Agent session ID on the target node." },
1276
- message: { type: "string", description: "Natural-language task to send to the agent." }
1790
+ message: { type: "string", description: "Natural-language task to send to the agent." },
1791
+ 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." },
1792
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
1277
1793
  },
1278
1794
  required: ["node_id", "session_id", "message"]
1279
1795
  }
@@ -1331,6 +1847,21 @@ var MESH_GIT_STATUS_TOOL = {
1331
1847
  required: ["node_id"]
1332
1848
  }
1333
1849
  };
1850
+ var MESH_FAST_FORWARD_NODE_TOOL = {
1851
+ name: "mesh_fast_forward_node",
1852
+ 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.",
1853
+ inputSchema: {
1854
+ type: "object",
1855
+ properties: {
1856
+ node_id: { type: "string", description: "Target node ID." },
1857
+ branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
1858
+ execute: { type: "boolean", description: "When true, apply the fast-forward if all safety gates pass. Defaults false/dry-run." },
1859
+ dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
1860
+ 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." }
1861
+ },
1862
+ required: ["node_id"]
1863
+ }
1864
+ };
1334
1865
  var MESH_CHECKPOINT_TOOL = {
1335
1866
  name: "mesh_checkpoint",
1336
1867
  description: "Create a git checkpoint (commit) on a mesh node workspace.",
@@ -1414,7 +1945,7 @@ var MESH_TASK_HISTORY_TOOL = {
1414
1945
  type: "object",
1415
1946
  properties: {
1416
1947
  tail: { type: "number", description: "Number of recent entries to return (default: 20)." },
1417
- kind: { type: "string", description: "Filter by entry kind: task_dispatched, task_completed, task_failed, task_stalled, session_launched, checkpoint_created, node_cloned, node_removed." }
1948
+ kind: { type: "string", description: "Filter by entry kind: task_dispatched, task_completed, task_failed, task_stalled, session_launched, checkpoint_created, node_cloned, node_removed, direct_fast_forward." }
1418
1949
  }
1419
1950
  }
1420
1951
  };
@@ -1434,7 +1965,7 @@ var MESH_RECONCILE_LEDGER_TOOL = {
1434
1965
  };
1435
1966
  var MESH_REFINE_NODE_TOOL = {
1436
1967
  name: "mesh_refine_node",
1437
- description: "The Refinery: Automatically validate and merge a completed worktree node back into its base branch. This tool automates the validation gate and merge queue step. It will merge the node's branch into its base branch and cleanly remove the worktree node and its sessions.",
1968
+ description: "The Refinery: Accept an async validation/merge/cleanup job for a completed worktree node. The immediate response includes async:true, status:'accepted', jobId, interactionId, target node, and startedAt; completion/failure evidence is delivered through pending mesh events and the mesh task ledger.",
1438
1969
  inputSchema: {
1439
1970
  type: "object",
1440
1971
  properties: {
@@ -1443,6 +1974,43 @@ var MESH_REFINE_NODE_TOOL = {
1443
1974
  required: ["node_id"]
1444
1975
  }
1445
1976
  };
1977
+ var MESH_REFINE_CONFIG_SCHEMA_TOOL = {
1978
+ name: "mesh_refine_config_schema",
1979
+ 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.",
1980
+ inputSchema: { type: "object", properties: {} }
1981
+ };
1982
+ var MESH_VALIDATE_REFINE_CONFIG_TOOL = {
1983
+ name: "mesh_validate_refine_config",
1984
+ description: "Validate the repo mesh/refine config for a node/workspace without running validation commands or merging.",
1985
+ inputSchema: {
1986
+ type: "object",
1987
+ properties: {
1988
+ node_id: { type: "string", description: "Optional node/workspace whose refine config should be loaded. Defaults to the first mesh node." },
1989
+ config: { type: "object", description: "Optional inline config object to validate instead of loading from the repo." }
1990
+ }
1991
+ }
1992
+ };
1993
+ var MESH_SUGGEST_REFINE_CONFIG_TOOL = {
1994
+ name: "mesh_suggest_refine_config",
1995
+ description: "Suggest a repo mesh/refine config scaffold from project context/package scripts. Suggestions are never executed until saved as explicit refine config.",
1996
+ inputSchema: {
1997
+ type: "object",
1998
+ properties: {
1999
+ node_id: { type: "string", description: "Optional node/workspace used for suggestions. Defaults to the first mesh node." }
2000
+ }
2001
+ }
2002
+ };
2003
+ var MESH_REFINE_PLAN_TOOL = {
2004
+ name: "mesh_refine_plan",
2005
+ 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.",
2006
+ inputSchema: {
2007
+ type: "object",
2008
+ properties: {
2009
+ node_id: { type: "string", description: "Node ID of the worktree node to plan." }
2010
+ },
2011
+ required: ["node_id"]
2012
+ }
2013
+ };
1446
2014
  var ALL_MESH_TOOLS = [
1447
2015
  MESH_STATUS_TOOL,
1448
2016
  MESH_LIST_NODES_TOOL,
@@ -1455,24 +2023,31 @@ var ALL_MESH_TOOLS = [
1455
2023
  MESH_READ_DEBUG_TOOL,
1456
2024
  MESH_LAUNCH_SESSION_TOOL,
1457
2025
  MESH_GIT_STATUS_TOOL,
2026
+ MESH_FAST_FORWARD_NODE_TOOL,
1458
2027
  MESH_CHECKPOINT_TOOL,
1459
2028
  MESH_APPROVE_TOOL,
1460
2029
  MESH_CLONE_NODE_TOOL,
1461
2030
  MESH_REMOVE_NODE_TOOL,
1462
2031
  MESH_REFINE_NODE_TOOL,
2032
+ MESH_REFINE_CONFIG_SCHEMA_TOOL,
2033
+ MESH_VALIDATE_REFINE_CONFIG_TOOL,
2034
+ MESH_SUGGEST_REFINE_CONFIG_TOOL,
2035
+ MESH_REFINE_PLAN_TOOL,
1463
2036
  MESH_CLEANUP_SESSIONS_TOOL,
1464
2037
  MESH_TASK_HISTORY_TOOL,
1465
2038
  MESH_RECONCILE_LEDGER_TOOL
1466
2039
  ];
1467
- async function meshStatus(ctx) {
2040
+ async function meshStatus(ctx, args = {}) {
1468
2041
  await refreshMeshFromDaemon(ctx);
1469
2042
  const { mesh, transport } = ctx;
1470
- const results = [];
1471
2043
  const ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1472
- for (const node of mesh.nodes) {
2044
+ const results = await Promise.all(mesh.nodes.map(async (node) => {
1473
2045
  const entry = {
1474
2046
  nodeId: node.id,
1475
2047
  workspace: node.workspace,
2048
+ machine: buildNodeMachineIdentity(ctx, node),
2049
+ daemonId: readNodeDaemonId(node),
2050
+ machineId: readNodeMachineId(node),
1476
2051
  ...getNodeLaunchReadiness(node)
1477
2052
  };
1478
2053
  try {
@@ -1482,6 +2057,7 @@ async function meshStatus(ctx) {
1482
2057
  const uncommittedChanges = countUncommittedChanges(status);
1483
2058
  const dirty = isGitStatusDirty(status);
1484
2059
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
2060
+ assignFullGitSnapshot(entry, status);
1485
2061
  entry.branch = status?.branch;
1486
2062
  entry.isDirty = dirty;
1487
2063
  entry.uncommittedChanges = uncommittedChanges;
@@ -1503,6 +2079,7 @@ async function meshStatus(ctx) {
1503
2079
  const uncommittedChanges = countUncommittedChanges(status);
1504
2080
  const dirty = isGitStatusDirty(status);
1505
2081
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
2082
+ assignFullGitSnapshot(entry, status);
1506
2083
  entry.branch = status?.branch;
1507
2084
  entry.isDirty = dirty;
1508
2085
  entry.uncommittedChanges = uncommittedChanges;
@@ -1538,7 +2115,7 @@ async function meshStatus(ctx) {
1538
2115
  if (recoveryContext.consecutiveNodeFailures > 0) {
1539
2116
  entry.recoveryHints = {
1540
2117
  consecutiveFailures: recoveryContext.consecutiveNodeFailures,
1541
- lastTaskMessage: recoveryContext.lastTaskMessage,
2118
+ lastTaskMessage: typeof recoveryContext.lastTaskMessage === "string" ? recoveryContext.lastTaskMessage.slice(0, 100) + (recoveryContext.lastTaskMessage.length > 100 ? "\u2026" : "") : recoveryContext.lastTaskMessage,
1542
2119
  advice: recoveryContext.advice,
1543
2120
  retryRecommended: recoveryContext.retryRecommended
1544
2121
  };
@@ -1578,7 +2155,47 @@ async function meshStatus(ctx) {
1578
2155
  }
1579
2156
  const relatedRepos = await collectRelatedRepoStatuses(ctx, node);
1580
2157
  if (relatedRepos.length) entry.relatedRepos = relatedRepos;
1581
- results.push(entry);
2158
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
2159
+ if (liveSessions.length > 0) {
2160
+ entry.sessions = liveSessions.map((s) => {
2161
+ const coordinatorMeshId = typeof s.coordinator?.meshId === "string" ? s.coordinator.meshId : void 0;
2162
+ const isSelfCoordinator = coordinatorMeshId === mesh.id;
2163
+ return {
2164
+ id: s.instanceId ?? s.id ?? s.sessionId,
2165
+ status: s.status ?? s.lifecycle ?? s.state,
2166
+ providerType: s.providerType ?? s.cliType ?? s.type,
2167
+ ...s.activeChat?.status ? { chatStatus: s.activeChat.status } : {},
2168
+ ...isSelfCoordinator ? { isSelfCoordinator: true, role: "coordinator" } : {}
2169
+ };
2170
+ }).filter((s) => s.id);
2171
+ }
2172
+ return entry;
2173
+ }));
2174
+ const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 200 });
2175
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2176
+ meshId: mesh.id,
2177
+ queue: (0, import_daemon_core.getQueue)(mesh.id),
2178
+ ledgerEntries,
2179
+ nodes: results
2180
+ });
2181
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
2182
+ const staleDirectWorkSummary = (0, import_daemon_core.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
2183
+ note: activeWorkEvidence.staleDirectWorkNote,
2184
+ detailHint: "Full stale direct entries are omitted from mesh_status by default. Call mesh_status with includeStaleDirectWorkDetails=true or inspect mesh_task_history for ledger detail."
2185
+ });
2186
+ const coordinatorSessions = [];
2187
+ for (const nodeEntry of results) {
2188
+ const sessions = Array.isArray(nodeEntry.sessions) ? nodeEntry.sessions : [];
2189
+ for (const s of sessions) {
2190
+ if (s?.isSelfCoordinator === true && s.id) {
2191
+ coordinatorSessions.push({
2192
+ nodeId: nodeEntry.nodeId,
2193
+ sessionId: s.id,
2194
+ providerType: s.providerType,
2195
+ status: s.status
2196
+ });
2197
+ }
2198
+ }
1582
2199
  }
1583
2200
  const response = {
1584
2201
  meshId: mesh.id,
@@ -1586,8 +2203,29 @@ async function meshStatus(ctx) {
1586
2203
  repoIdentity: mesh.repoIdentity,
1587
2204
  policy: mesh.policy,
1588
2205
  refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
2206
+ sourceOfTruth: {
2207
+ membership: "coordinator_daemon_live_mesh",
2208
+ currentStatus: "live_git_and_session_probes",
2209
+ activeWork: "mesh_queue_file_and_local_ledger",
2210
+ historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
2211
+ },
1589
2212
  nodes: results,
1590
- branchConvergenceSummary: summarizeBranchConvergence(results)
2213
+ activeWork: activeWorkEvidence.activeWork,
2214
+ staleDirectWorkSummary,
2215
+ ...args.includeStaleDirectWorkDetails === true ? { staleDirectWork: activeWorkEvidence.staleDirectWork } : {},
2216
+ // terminalDirectWork is historical (completed/failed direct dispatches) — opt-in only.
2217
+ ...args.includeTerminalDirectWork === true ? { terminalDirectWork: activeWorkEvidence.terminalDirectWork } : {},
2218
+ activeWorkSummary: activeWorkEvidence.summary,
2219
+ ...pollingGuidance ? { pollingGuidance } : {},
2220
+ branchConvergenceSummary: summarizeBranchConvergence(results),
2221
+ ...coordinatorSessions.length > 0 ? {
2222
+ coordinatorSessions,
2223
+ selfIdentification: {
2224
+ meshId: mesh.id,
2225
+ coordinatorSessions,
2226
+ note: "Sessions listed here are coordinator sessions for this mesh. The calling coordinator IS one of these sessions \u2014 do not treat its own generating CLI session as a foreign delegated task. Per-session marker: sessions[].isSelfCoordinator === true."
2227
+ }
2228
+ } : {}
1591
2229
  };
1592
2230
  try {
1593
2231
  response.ledgerSummary = ledgerSummary;
@@ -1595,6 +2233,14 @@ async function meshStatus(ctx) {
1595
2233
  }
1596
2234
  try {
1597
2235
  const pendingEvents = await drainCoordinatorPendingEvents(ctx);
2236
+ const asyncRefineJobs = (0, import_daemon_core.buildMeshAsyncRefineJobs)({
2237
+ meshId: mesh.id,
2238
+ ledgerEntries,
2239
+ pendingEvents
2240
+ });
2241
+ if (asyncRefineJobs.length > 0) {
2242
+ response.asyncRefineJobs = asyncRefineJobs;
2243
+ }
1598
2244
  if (pendingEvents.length > 0) {
1599
2245
  response.pendingCoordinatorEvents = pendingEvents;
1600
2246
  }
@@ -1604,12 +2250,21 @@ async function meshStatus(ctx) {
1604
2250
  }
1605
2251
  async function meshTaskHistory(ctx, args) {
1606
2252
  const { mesh } = ctx;
1607
- await drainCoordinatorPendingEvents(ctx);
2253
+ const pendingEvents = await drainCoordinatorPendingEvents(ctx);
1608
2254
  const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
1609
2255
  const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
1610
- const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
2256
+ const rawEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
2257
+ const entries = rawEntries.map((e) => ({
2258
+ ...e,
2259
+ payload: e.payload ? slimLedgerPayload(e.payload) : e.payload
2260
+ }));
1611
2261
  const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1612
- return JSON.stringify({ meshId: mesh.id, entries, summary }, null, 2);
2262
+ return JSON.stringify({
2263
+ meshId: mesh.id,
2264
+ entries,
2265
+ summary,
2266
+ ...pendingEvents.length > 0 ? { pendingCoordinatorEvents: pendingEvents } : {}
2267
+ }, null, 2);
1613
2268
  }
1614
2269
  async function meshReconcileLedger(ctx, args) {
1615
2270
  await refreshMeshFromDaemon(ctx);
@@ -1699,6 +2354,9 @@ async function meshListNodes(ctx) {
1699
2354
  nodeId: n.id,
1700
2355
  workspace: n.workspace,
1701
2356
  repoRoot: n.repoRoot,
2357
+ daemonId: readNodeDaemonId(n),
2358
+ machineId: readNodeMachineId(n),
2359
+ machine: buildNodeMachineIdentity(ctx, n),
1702
2360
  isLocalWorktree: n.isLocalWorktree,
1703
2361
  policy: n.policy,
1704
2362
  relatedRepos: readRelatedRepos(n),
@@ -1708,12 +2366,14 @@ async function meshListNodes(ctx) {
1708
2366
  }, null, 2);
1709
2367
  }
1710
2368
  async function meshEnqueueTask(ctx, args) {
2369
+ const taskMode = readString(args.task_mode) || readString(args.taskMode);
2370
+ const requiredTags = (0, import_daemon_core.normalizeMeshCapabilityTags)(Array.isArray(args.requiredTags) ? args.requiredTags : args.required_tags);
1711
2371
  try {
1712
- const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message);
2372
+ const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode, requiredTags });
1713
2373
  if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
1714
2374
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1715
2375
  });
1716
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2376
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode, requiredTags: task.requiredTags });
1717
2377
  }
1718
2378
  if (ctx.transport instanceof IpcTransport) {
1719
2379
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
@@ -1722,43 +2382,97 @@ async function meshEnqueueTask(ctx, args) {
1722
2382
  for (const node of ctx.mesh.nodes) {
1723
2383
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1724
2384
  if (isLocalNode || !node.daemonId) continue;
2385
+ if (!(0, import_daemon_core.nodeSatisfiesRequiredTags)(requiredTags, (0, import_daemon_core.buildMeshNodeCapabilityTags)(node))) continue;
1725
2386
  dispatchPromises.push(
1726
2387
  ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
1727
2388
  if (result.success) {
1728
2389
  try {
2390
+ const providerType = result.providerType;
2391
+ const descriptor = summarizeTaskMessage(args.message);
1729
2392
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1730
2393
  kind: "task_dispatched",
1731
2394
  nodeId: node.id,
1732
2395
  sessionId: result.sessionId,
1733
- payload: { message: args.message, via: "p2p_direct", taskId: task.id }
2396
+ providerType,
2397
+ payload: {
2398
+ source: "queue",
2399
+ via: "p2p_direct",
2400
+ taskId: task.id,
2401
+ message: args.message,
2402
+ taskTitle: descriptor.taskTitle,
2403
+ taskSummary: descriptor.taskSummary,
2404
+ ...task.taskMode ? { taskMode: task.taskMode } : {},
2405
+ ...providerType ? { providerType } : {},
2406
+ targetSessionId: result.sessionId
2407
+ }
1734
2408
  });
1735
2409
  } catch {
1736
2410
  }
1737
2411
  }
1738
- }).catch(() => {
2412
+ }).catch((err) => {
2413
+ try {
2414
+ (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2415
+ kind: "p2p_dispatch_failed",
2416
+ nodeId: node.id,
2417
+ payload: {
2418
+ source: "queue",
2419
+ via: "p2p_direct",
2420
+ taskId: task.id,
2421
+ error: err?.message || String(err),
2422
+ dispatchFailedAt: (/* @__PURE__ */ new Date()).toISOString()
2423
+ }
2424
+ });
2425
+ } catch {
2426
+ }
1739
2427
  })
1740
2428
  );
1741
2429
  }
1742
2430
  Promise.all(dispatchPromises).catch(() => {
1743
2431
  });
1744
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2432
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode, requiredTags: task.requiredTags });
1745
2433
  }
1746
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2434
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode, requiredTags: task.requiredTags });
1747
2435
  } catch (e) {
1748
- return JSON.stringify({ success: false, error: e.message });
2436
+ const message = e?.message || String(e);
2437
+ if (message.includes("live_debug_readonly_guardrail_violation")) {
2438
+ return JSON.stringify({ success: false, code: "live_debug_readonly_guardrail_violation", taskMode, error: message });
2439
+ }
2440
+ return JSON.stringify({ success: false, error: message });
1749
2441
  }
1750
2442
  }
1751
2443
  async function meshViewQueue(ctx, args) {
1752
2444
  try {
2445
+ await refreshMeshFromDaemon(ctx);
1753
2446
  const statusFilter = sanitizeQueueStatusFilter(args.status);
1754
2447
  const view = normalizeQueueViewMode(args.view);
1755
- const fullQueue = annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh);
2448
+ const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh));
1756
2449
  const queue = filterQueueForView(fullQueue, view, statusFilter);
1757
2450
  const summary = buildQueueStatusSummary(fullQueue);
1758
2451
  const visibleSummary = buildQueueStatusSummary(queue);
1759
2452
  const maintenance = buildQueueMaintenanceReport(fullQueue);
2453
+ const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
2454
+ (0, import_daemon_core.markStaleDirectDispatches)(ctx.mesh.id);
2455
+ const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
2456
+ const directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
2457
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2458
+ meshId: ctx.mesh.id,
2459
+ queue: fullQueue,
2460
+ ledgerEntries,
2461
+ // Always pass BeadsDB records (may be empty). buildMeshActiveWork uses them for local
2462
+ // dispatches and falls through to ledger scan for remote P2P dispatches not in BeadsDB.
2463
+ directDispatches,
2464
+ nodes: liveNodes
2465
+ });
2466
+ const recentDispatchFailures = ledgerEntries.filter((e) => e.kind === "p2p_dispatch_failed").slice(-20).map((e) => ({
2467
+ nodeId: e.nodeId,
2468
+ taskId: e.payload?.taskId,
2469
+ error: e.payload?.error,
2470
+ via: e.payload?.via,
2471
+ failedAt: e.payload?.dispatchFailedAt || e.timestamp
2472
+ }));
1760
2473
  const staleAssignedTasks = maintenance.staleAssignedTasks || [];
1761
2474
  const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
2475
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
1762
2476
  return JSON.stringify({
1763
2477
  success: true,
1764
2478
  sourceOfTruth: {
@@ -1773,21 +2487,20 @@ async function meshViewQueue(ctx, args) {
1773
2487
  filtered: Boolean(statusFilter?.length) || view !== "all"
1774
2488
  },
1775
2489
  queue,
1776
- visibleQueue: queue,
1777
- visibleSummary,
2490
+ activeWork: activeWorkEvidence.activeWork,
2491
+ staleDirectWork: activeWorkEvidence.staleDirectWork,
2492
+ activeWorkSummary: activeWorkEvidence.summary,
2493
+ ...pollingGuidance ? { pollingGuidance } : {},
1778
2494
  summary,
1779
- activeCounts: summary.activeCounts,
1780
- historicalCounts: summary.historicalCounts,
1781
- activeCount: summary.activeCount,
1782
- historicalCount: summary.historicalCount,
1783
- visibleActiveCounts: visibleSummary.activeCounts,
1784
- visibleHistoricalCounts: visibleSummary.historicalCounts,
1785
- visibleActiveCount: visibleSummary.activeCount,
1786
- visibleHistoricalCount: visibleSummary.historicalCount,
1787
2495
  staleAssignedTasks,
1788
2496
  staleAssignedCount: maintenance.staleAssignedCount,
1789
2497
  queueMaintenance: maintenance,
1790
2498
  cleanupDryRun: maintenance,
2499
+ ...recentDispatchFailures.length > 0 ? {
2500
+ recentDispatchFailures,
2501
+ dispatchFailureCount: recentDispatchFailures.length,
2502
+ dispatchFailureNote: "Remote P2P dispatch attempts that failed. Affected tasks remain pending and may require mesh_queue_requeue if no idle session picks them up."
2503
+ } : {},
1791
2504
  ...view === "active" || statusFilter?.some((status) => ACTIVE_QUEUE_STATUSES.has(status)) ? {
1792
2505
  activeQueue: queue.filter((task) => ACTIVE_QUEUE_STATUSES.has(String(task?.status || "")))
1793
2506
  } : {},
@@ -1807,6 +2520,10 @@ async function meshQueueCancel(ctx, args) {
1807
2520
  if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
1808
2521
  const task = (0, import_daemon_core.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
1809
2522
  if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
2523
+ if (isLocalTransport(ctx.transport)) {
2524
+ ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2525
+ });
2526
+ }
1810
2527
  return JSON.stringify({ success: true, task }, null, 2);
1811
2528
  } catch (e) {
1812
2529
  return JSON.stringify({ success: false, error: e.message });
@@ -1837,10 +2554,60 @@ async function meshQueueRequeue(ctx, args) {
1837
2554
  }
1838
2555
  }
1839
2556
  async function meshSendTask(ctx, args) {
2557
+ const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
2558
+ const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
2559
+ if (!modeValidation.valid) {
2560
+ return JSON.stringify({
2561
+ success: false,
2562
+ code: "live_debug_readonly_guardrail_violation",
2563
+ taskMode: modeValidation.taskMode || requestedTaskMode,
2564
+ violations: modeValidation.violations,
2565
+ allowedOperations: modeValidation.allowedOperations,
2566
+ error: `live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`
2567
+ });
2568
+ }
2569
+ const taskMode = modeValidation.taskMode;
1840
2570
  const node = await findNodeWithRefresh(ctx, args.node_id);
1841
2571
  if (node.policy?.readOnly) {
1842
2572
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
1843
2573
  }
2574
+ let explicitTargetSession;
2575
+ if (args.session_id && isWorkerTaskMode(taskMode) && (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport))) {
2576
+ try {
2577
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2578
+ const sessions = extractStatusMetadataSessions(statusResult);
2579
+ explicitTargetSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2580
+ if (explicitTargetSession && isMeshCoordinatorSessionRecord(explicitTargetSession)) {
2581
+ return JSON.stringify({
2582
+ success: false,
2583
+ recoverable: true,
2584
+ code: "mesh_target_session_is_coordinator",
2585
+ reason: "mesh_target_session_is_coordinator",
2586
+ nodeId: args.node_id,
2587
+ sessionId: args.session_id,
2588
+ taskMode: taskMode || "unspecified",
2589
+ error: `Session '${args.session_id}' is a Repo Mesh coordinator session, not a visible worker session. Launch or use a visible worker session before dispatching this task.`,
2590
+ nextAction: `Call mesh_launch_session for node '${args.node_id}' and then retry mesh_send_task with that worker session_id, or use mesh_enqueue_task for queue-based worker assignment.`
2591
+ });
2592
+ }
2593
+ if (explicitTargetSession && isUnmanagedSessionRecord(explicitTargetSession)) {
2594
+ return JSON.stringify({
2595
+ success: false,
2596
+ recoverable: true,
2597
+ code: "mesh_target_session_unmanaged",
2598
+ reason: "mesh_target_session_unmanaged",
2599
+ nodeId: args.node_id,
2600
+ sessionId: args.session_id,
2601
+ taskMode: taskMode || "unspecified",
2602
+ unsafeTranscriptAlias: true,
2603
+ error: `Session '${args.session_id}' on node '${args.node_id}' has no Repo Mesh delegation metadata (missing meshNodeFor/meshCoordinatorFor/launchedByCoordinator). It may be the coordinator's own session or an unrelated session \u2014 dispatching risks self-send and orphaned completion events that never reach the coordinator ledger.`,
2604
+ nextAction: `Call mesh_launch_session for node '${args.node_id}' to start a fresh managed worker session, then retry mesh_send_task with the returned session_id. Alternatively use mesh_enqueue_task for queue-based assignment without specifying session_id.`
2605
+ });
2606
+ }
2607
+ } catch {
2608
+ explicitTargetSession = void 0;
2609
+ }
2610
+ }
1844
2611
  const duplicate = hasRecentDuplicateDispatch(ctx, args);
1845
2612
  if (duplicate.duplicate) {
1846
2613
  return JSON.stringify({
@@ -1864,47 +2631,162 @@ async function meshSendTask(ctx, args) {
1864
2631
  const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
1865
2632
  meshId: ctx.mesh.id,
1866
2633
  message: args.message,
1867
- targetNodeId: args.node_id
2634
+ targetNodeId: args.node_id,
2635
+ ...taskMode ? { taskMode } : {}
1868
2636
  });
1869
2637
  return JSON.stringify(res);
1870
2638
  }
1871
2639
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1872
2640
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
1873
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
2641
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id || ""));
2642
+ const taskId = (0, import_node_crypto.randomUUID)();
1874
2643
  const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
1875
2644
  session_id: args.session_id,
1876
2645
  message: args.message,
1877
- providerType: cached?.providerType
2646
+ providerType: cached?.providerType,
2647
+ verifiedSession: explicitTargetSession,
2648
+ meshContext: {
2649
+ meshId: ctx.mesh.id,
2650
+ nodeId: args.node_id,
2651
+ taskId
2652
+ }
1878
2653
  });
1879
2654
  if (result2.success) {
1880
2655
  const dispatchedSessionId = args.session_id || result2.sessionId;
2656
+ const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
1881
2657
  try {
2658
+ const providerType = result2.providerType || cached?.providerType;
1882
2659
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1883
2660
  kind: "task_dispatched",
1884
2661
  nodeId: args.node_id,
1885
2662
  sessionId: dispatchedSessionId,
1886
- payload: {
1887
- message: args.message,
1888
- via: "p2p_direct",
1889
- ...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
1890
- }
2663
+ providerType,
2664
+ payload: buildDirectTaskPayload(args.message, "p2p_direct", {
2665
+ taskId,
2666
+ taskMode,
2667
+ providerType,
2668
+ targetSessionId: dispatchedSessionId
2669
+ })
2670
+ });
2671
+ (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
2672
+ taskId,
2673
+ nodeId: args.node_id,
2674
+ sessionId: dispatchedSessionId,
2675
+ providerType: providerType || void 0,
2676
+ message: args.message,
2677
+ taskMode: taskMode || void 0,
2678
+ via: "p2p_direct",
2679
+ dispatchedAt
1891
2680
  });
1892
2681
  } catch {
1893
2682
  }
1894
2683
  }
1895
- return JSON.stringify({ ...result2, nodeId: args.node_id, dispatched: result2.success === true });
2684
+ return JSON.stringify({
2685
+ ...result2,
2686
+ nodeId: args.node_id,
2687
+ sessionId: result2.success ? args.session_id || result2.sessionId : args.session_id,
2688
+ ...result2.success ? { source: "direct", taskId } : {},
2689
+ taskMode,
2690
+ ...result2.success && result2.providerType ? { providerType: result2.providerType } : {},
2691
+ dispatched: result2.success === true
2692
+ });
1896
2693
  }
1897
2694
  if (args.session_id && isLocalTransport(ctx.transport)) {
1898
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2695
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
2696
+ let resolvedProviderType = cached?.providerType || "";
2697
+ if (!resolvedProviderType) {
2698
+ let explicitSession = explicitTargetSession;
2699
+ if (!explicitSession) {
2700
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2701
+ const sessions = extractStatusMetadataSessions(statusResult);
2702
+ explicitSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2703
+ }
2704
+ if (!explicitSession) {
2705
+ return JSON.stringify({
2706
+ success: false,
2707
+ recoverable: true,
2708
+ code: "mesh_target_session_not_found",
2709
+ reason: "mesh_target_session_not_found",
2710
+ transport: "local_ipc",
2711
+ retryRecommended: true,
2712
+ nodeId: args.node_id,
2713
+ sessionId: args.session_id,
2714
+ error: `Local session '${args.session_id}' is not present in live status for node '${args.node_id}'.`,
2715
+ 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.`
2716
+ });
2717
+ }
2718
+ if (isMeshCoordinatorSessionRecord(explicitSession)) {
2719
+ return JSON.stringify({
2720
+ success: false,
2721
+ recoverable: true,
2722
+ code: "mesh_target_session_is_coordinator",
2723
+ reason: "mesh_target_session_is_coordinator",
2724
+ nodeId: args.node_id,
2725
+ sessionId: args.session_id,
2726
+ taskMode: taskMode || "unspecified",
2727
+ error: `Session '${args.session_id}' is a Repo Mesh coordinator session, not a visible worker session. Launch or use a visible worker session before dispatching this task.`,
2728
+ nextAction: `Call mesh_launch_session for node '${args.node_id}' and then retry mesh_send_task with that worker session_id, or use mesh_enqueue_task for queue-based worker assignment.`
2729
+ });
2730
+ }
2731
+ if (isUnmanagedSessionRecord(explicitSession)) {
2732
+ return JSON.stringify({
2733
+ success: false,
2734
+ recoverable: true,
2735
+ code: "mesh_target_session_unmanaged",
2736
+ reason: "mesh_target_session_unmanaged",
2737
+ nodeId: args.node_id,
2738
+ sessionId: args.session_id,
2739
+ taskMode: taskMode || "unspecified",
2740
+ unsafeTranscriptAlias: true,
2741
+ unsafeDelegateTarget: true,
2742
+ error: `Session '${args.session_id}' on node '${args.node_id}' has no Repo Mesh delegation metadata (missing meshNodeFor/meshCoordinatorFor/launchedByCoordinator). It may be the coordinator's own session or an unrelated session \u2014 dispatching risks self-send and orphaned completion events that never reach the coordinator ledger.`,
2743
+ nextAction: `Call mesh_launch_session for node '${args.node_id}' to start a fresh managed worker session, then retry mesh_send_task with the returned session_id. Alternatively use mesh_enqueue_task for queue-based assignment without specifying session_id.`
2744
+ });
2745
+ }
2746
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
2747
+ if (resolvedProviderType) {
2748
+ meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
2749
+ providerType: resolvedProviderType,
2750
+ providerSessionId: readString(explicitSession?.providerSessionId) || void 0,
2751
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2752
+ });
2753
+ }
2754
+ }
2755
+ if (!resolvedProviderType) {
2756
+ return JSON.stringify({
2757
+ success: false,
2758
+ recoverable: true,
2759
+ code: "mesh_target_session_provider_unknown",
2760
+ reason: "mesh_target_session_provider_unknown",
2761
+ transport: "local_ipc",
2762
+ retryRecommended: false,
2763
+ nodeId: args.node_id,
2764
+ sessionId: args.session_id,
2765
+ error: `Local session '${args.session_id}' is live but does not expose providerType/cliType, so agent_command cannot be routed safely.`,
2766
+ 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.`
2767
+ });
2768
+ }
2769
+ const sessionWasIdle = explicitTargetSession ? isIdleSessionRecord(explicitTargetSession) : false;
2770
+ const taskId = (0, import_node_crypto.randomUUID)();
2771
+ const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
1899
2772
  const dispatchResult = await commandForNode(ctx, node, "agent_command", {
1900
2773
  targetSessionId: args.session_id,
1901
- ...cached?.providerType ? { agentType: cached.providerType, cliType: cached.providerType, providerType: cached.providerType } : {},
2774
+ agentType: resolvedProviderType,
2775
+ cliType: resolvedProviderType,
2776
+ providerType: resolvedProviderType,
1902
2777
  action: "send_chat",
1903
- message: args.message
2778
+ message: args.message,
2779
+ meshContext: {
2780
+ meshId: ctx.mesh.id,
2781
+ nodeId: args.node_id,
2782
+ taskId
2783
+ }
1904
2784
  });
1905
2785
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
1906
2786
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
2787
+ const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
1907
2788
  return JSON.stringify({
2789
+ ...source && typeof source === "object" ? source : {},
1908
2790
  success: false,
1909
2791
  nodeId: args.node_id,
1910
2792
  sessionId: args.session_id,
@@ -1916,23 +2798,55 @@ async function meshSendTask(ctx, args) {
1916
2798
  kind: "task_dispatched",
1917
2799
  nodeId: args.node_id,
1918
2800
  sessionId: args.session_id,
1919
- providerType: cached?.providerType,
1920
- payload: { message: args.message, via: "local_direct" }
2801
+ providerType: resolvedProviderType,
2802
+ payload: buildDirectTaskPayload(args.message, "local_direct", {
2803
+ taskId,
2804
+ taskMode,
2805
+ providerType: resolvedProviderType,
2806
+ targetSessionId: args.session_id,
2807
+ dispatchedToIdleSession: sessionWasIdle
2808
+ })
1921
2809
  });
1922
2810
  } catch {
1923
2811
  }
1924
- return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
2812
+ (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
2813
+ taskId,
2814
+ nodeId: args.node_id,
2815
+ sessionId: args.session_id,
2816
+ providerType: resolvedProviderType || void 0,
2817
+ message: args.message,
2818
+ taskMode: taskMode || void 0,
2819
+ via: "local_direct",
2820
+ dispatchedToIdleSession: sessionWasIdle,
2821
+ dispatchedAt
2822
+ });
2823
+ return JSON.stringify({
2824
+ success: true,
2825
+ dispatched: true,
2826
+ source: "direct",
2827
+ taskId,
2828
+ taskMode,
2829
+ providerType: resolvedProviderType,
2830
+ nodeId: args.node_id,
2831
+ sessionId: args.session_id,
2832
+ ...sessionWasIdle ? {
2833
+ dispatchAcknowledgementRisk: true,
2834
+ dispatchAcknowledgementRiskReason: "session_was_idle_at_dispatch",
2835
+ dispatchAcknowledgementNote: `Session '${args.session_id}' was idle at dispatch time. If it does not transition to generating, this direct task was not acknowledged. Use mesh_status to verify; if the session remains idle, it may appear as stale direct work \u2014 launch a fresh session and retry.`
2836
+ } : {}
2837
+ });
1925
2838
  }
1926
2839
  const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
1927
2840
  targetNodeId: args.node_id,
1928
- targetSessionId: args.session_id
2841
+ targetSessionId: args.session_id,
2842
+ taskMode
1929
2843
  });
1930
2844
  if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
1931
2845
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1932
2846
  });
1933
2847
  }
1934
- const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)() : [];
1935
- const result = { success: true, nodeId: args.node_id, taskId: task.id, status: task.status };
2848
+ const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId) : [];
2849
+ const result = { success: true, source: "queue", nodeId: args.node_id, taskId: task.id, status: task.status, taskMode: task.taskMode };
1936
2850
  if (pendingEvents.length > 0) {
1937
2851
  result.pendingCoordinatorEvents = pendingEvents;
1938
2852
  }
@@ -1956,7 +2870,7 @@ async function meshReadChat(ctx, args) {
1956
2870
  await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
1957
2871
  }
1958
2872
  if (isLocalTransport(ctx.transport)) {
1959
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2873
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
1960
2874
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
1961
2875
  const result = await commandForNode(ctx, node, "read_chat", {
1962
2876
  sessionId: args.session_id,
@@ -1964,18 +2878,19 @@ async function meshReadChat(ctx, args) {
1964
2878
  workspace: node.workspace,
1965
2879
  ...cached?.providerType ? { agentType: cached.providerType, providerType: cached.providerType } : {},
1966
2880
  ...providerSessionId ? { providerSessionId } : {},
1967
- tailLimit: args.tail ?? 10
2881
+ tailLimit: args.tail ?? 3
1968
2882
  });
1969
2883
  const payload = annotateRapidReadChatAdvisory(unwrapCommandPayload(result), {
1970
2884
  key: `mesh:${args.node_id}:${args.session_id}`,
1971
2885
  toolName: "mesh_read_chat",
1972
2886
  completionCallbackExpected: true
1973
2887
  });
1974
- if (args.compact) {
2888
+ const useCompact = args.compact !== false;
2889
+ if (useCompact) {
1975
2890
  const compactPayload = compactChatPayload(payload, {
1976
2891
  nodeId: args.node_id,
1977
2892
  sessionId: args.session_id,
1978
- limit: args.tail ?? 10
2893
+ limit: args.tail ?? 3
1979
2894
  });
1980
2895
  return JSON.stringify(
1981
2896
  payload.pollingAdvisory ? { ...compactPayload, pollingAdvisory: payload.pollingAdvisory } : compactPayload,
@@ -1988,7 +2903,7 @@ async function meshReadChat(ctx, args) {
1988
2903
  try {
1989
2904
  const targetId = `${node.daemonId}:session:${args.session_id}`;
1990
2905
  const res = await ctx.transport.readChat(targetId, {
1991
- limit: args.tail ?? 10,
2906
+ limit: args.tail ?? 3,
1992
2907
  sessionId: args.session_id
1993
2908
  });
1994
2909
  return JSON.stringify(res, null, 2);
@@ -2002,7 +2917,7 @@ async function meshReadChat(ctx, args) {
2002
2917
  async function meshReadDebug(ctx, args) {
2003
2918
  const node = await findNodeWithRefresh(ctx, args.node_id);
2004
2919
  if (isLocalTransport(ctx.transport)) {
2005
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2920
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
2006
2921
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
2007
2922
  const delivery = args.delivery === "inline" ? void 0 : "daemon_file";
2008
2923
  const result = await commandForNode(ctx, node, "get_chat_debug_bundle", {
@@ -2033,6 +2948,8 @@ async function meshReadDebug(ctx, args) {
2033
2948
  }
2034
2949
  async function meshLaunchSession(ctx, args) {
2035
2950
  const node = await findNodeWithRefresh(ctx, args.node_id);
2951
+ const bootstrapBlock = getWorktreeBootstrapLaunchBlock(node);
2952
+ if (bootstrapBlock) return JSON.stringify(bootstrapBlock, null, 2);
2036
2953
  if (isLocalTransport(ctx.transport)) {
2037
2954
  let resolvedProviderType = typeof args.type === "string" && args.type.trim() ? args.type : "";
2038
2955
  if (!resolvedProviderType) {
@@ -2057,6 +2974,10 @@ async function meshLaunchSession(ctx, args) {
2057
2974
  const coordinatorNode = resolveCoordinatorNode(ctx);
2058
2975
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
2059
2976
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
2977
+ const isLocalNode = isLocalControlPlaneNode(ctx, node);
2978
+ if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
2979
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
2980
+ }
2060
2981
  let result;
2061
2982
  try {
2062
2983
  result = await commandForNode(ctx, node, "launch_cli", {
@@ -2084,7 +3005,8 @@ async function meshLaunchSession(ctx, args) {
2084
3005
  if (runtimeSessionId) {
2085
3006
  meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, runtimeSessionId), {
2086
3007
  providerType: resolvedProviderType,
2087
- ...providerSessionId ? { providerSessionId } : {}
3008
+ ...providerSessionId ? { providerSessionId } : {},
3009
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2088
3010
  });
2089
3011
  }
2090
3012
  try {
@@ -2097,7 +3019,6 @@ async function meshLaunchSession(ctx, args) {
2097
3019
  });
2098
3020
  } catch {
2099
3021
  }
2100
- const isLocalNode = isLocalControlPlaneNode(ctx, node);
2101
3022
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
2102
3023
  ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2103
3024
  });
@@ -2122,6 +3043,9 @@ async function meshLaunchSession(ctx, args) {
2122
3043
  const coordinatorNode = resolveCoordinatorNode(ctx);
2123
3044
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
2124
3045
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
3046
+ if (!coordinatorDaemonId) {
3047
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
3048
+ }
2125
3049
  try {
2126
3050
  const res = await ctx.transport.launch(node.daemonId, {
2127
3051
  type: resolvedProviderType,
@@ -2202,6 +3126,51 @@ async function meshGitStatus(ctx, args) {
2202
3126
  }, null, 2);
2203
3127
  }
2204
3128
  }
3129
+ async function meshFastForwardNode(ctx, args) {
3130
+ await refreshMeshFromDaemon(ctx);
3131
+ const node = await findNodeWithRefresh(ctx, args.node_id);
3132
+ const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
3133
+ if (node.policy?.readOnly) {
3134
+ return JSON.stringify({
3135
+ success: false,
3136
+ code: "node_read_only",
3137
+ nodeId: args.node_id,
3138
+ workspace: node.workspace,
3139
+ allowed: false,
3140
+ willRun: false,
3141
+ executed: false,
3142
+ blockingReasons: ["node_read_only"]
3143
+ }, null, 2);
3144
+ }
3145
+ try {
3146
+ const dryRun = args.dry_run === true || args.execute !== true;
3147
+ const result = await commandForNode(ctx, node, "fast_forward_mesh_node", {
3148
+ meshId: ctx.mesh.id,
3149
+ nodeId: node.id,
3150
+ workspace: node.workspace,
3151
+ branch: typeof args.branch === "string" ? args.branch : void 0,
3152
+ execute: args.execute === true && args.dry_run !== true,
3153
+ dryRun,
3154
+ updateSubmodules: args.update_submodules === true,
3155
+ submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
3156
+ });
3157
+ return JSON.stringify(unwrapCommandPayload(result), null, 2);
3158
+ } catch (e) {
3159
+ const failure = buildCoordinatorP2pRelayFailure(e, {
3160
+ command: "fast_forward_mesh_node",
3161
+ targetDaemonId: node.daemonId,
3162
+ nodeId: args.node_id
3163
+ });
3164
+ return JSON.stringify({
3165
+ ...failure,
3166
+ workspace: node.workspace,
3167
+ allowed: false,
3168
+ willRun: false,
3169
+ executed: false,
3170
+ blockingReasons: [failure.code || "mesh_fast_forward_unavailable"]
3171
+ }, null, 2);
3172
+ }
3173
+ }
2205
3174
  async function meshCheckpoint(ctx, args) {
2206
3175
  const node = await findNodeWithRefresh(ctx, args.node_id);
2207
3176
  if (node.policy?.readOnly) {
@@ -2217,7 +3186,13 @@ async function meshCheckpoint(ctx, args) {
2217
3186
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2218
3187
  kind: "checkpoint_created",
2219
3188
  nodeId: args.node_id,
2220
- payload: { message: args.message, commit: result?.checkpoint?.commit }
3189
+ payload: {
3190
+ message: args.message,
3191
+ commit: result?.checkpoint?.commit,
3192
+ outcome: result?.checkpoint?.status || (result?.checkpoint?.noop ? "skipped" : void 0),
3193
+ noop: result?.checkpoint?.noop === true,
3194
+ reason: result?.checkpoint?.reason
3195
+ }
2221
3196
  });
2222
3197
  } catch {
2223
3198
  }
@@ -2233,7 +3208,13 @@ async function meshCheckpoint(ctx, args) {
2233
3208
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2234
3209
  kind: "checkpoint_created",
2235
3210
  nodeId: args.node_id,
2236
- payload: { message: args.message, commit: res?.checkpoint?.commit }
3211
+ payload: {
3212
+ message: args.message,
3213
+ commit: res?.checkpoint?.commit,
3214
+ outcome: res?.checkpoint?.status || (res?.checkpoint?.noop ? "skipped" : void 0),
3215
+ noop: res?.checkpoint?.noop === true,
3216
+ reason: res?.checkpoint?.reason
3217
+ }
2237
3218
  });
2238
3219
  } catch {
2239
3220
  }
@@ -2248,7 +3229,7 @@ async function meshCheckpoint(ctx, args) {
2248
3229
  async function meshApprove(ctx, args) {
2249
3230
  const node = await findNodeWithRefresh(ctx, args.node_id);
2250
3231
  if (isLocalTransport(ctx.transport)) {
2251
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
3232
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
2252
3233
  const providerSessionId = cached?.providerSessionId;
2253
3234
  const result = await commandForNode(ctx, node, "resolve_action", {
2254
3235
  sessionId: args.session_id,
@@ -2401,6 +3382,43 @@ async function meshRemoveNode(ctx, args) {
2401
3382
  return JSON.stringify({ error: "Cloud mesh remove_node requires node daemonId" });
2402
3383
  }
2403
3384
  }
3385
+ function resolveRefineConfigNode(ctx, nodeId) {
3386
+ if (nodeId) return findNode(ctx.mesh, nodeId);
3387
+ const node = ctx.mesh.nodes.find((entry) => !!entry.workspace);
3388
+ if (!node) throw new Error("No mesh node with a workspace is available");
3389
+ return node;
3390
+ }
3391
+ async function meshRefineConfigSchema(ctx) {
3392
+ const node = resolveRefineConfigNode(ctx);
3393
+ const result = await commandForNode(ctx, node, "get_mesh_refine_config_schema", {});
3394
+ return JSON.stringify(result, null, 2);
3395
+ }
3396
+ async function meshValidateRefineConfig(ctx, args) {
3397
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3398
+ const result = await commandForNode(ctx, node, "validate_mesh_refine_config", {
3399
+ workspace: node.workspace,
3400
+ inlineMesh: ctx.mesh,
3401
+ ...args.config ? { config: args.config } : {}
3402
+ });
3403
+ return JSON.stringify(result, null, 2);
3404
+ }
3405
+ async function meshSuggestRefineConfig(ctx, args) {
3406
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3407
+ const result = await commandForNode(ctx, node, "suggest_mesh_refine_config", {
3408
+ workspace: node.workspace,
3409
+ inlineMesh: ctx.mesh
3410
+ });
3411
+ return JSON.stringify(result, null, 2);
3412
+ }
3413
+ async function meshRefinePlan(ctx, args) {
3414
+ const node = await findNodeWithRefresh(ctx, args.node_id);
3415
+ const result = await commandForNode(ctx, node, "plan_mesh_refine_node", {
3416
+ meshId: ctx.mesh.id,
3417
+ nodeId: args.node_id,
3418
+ inlineMesh: ctx.mesh
3419
+ });
3420
+ return JSON.stringify(result, null, 2);
3421
+ }
2404
3422
  async function meshRefineNode(ctx, args) {
2405
3423
  const node = await findNodeWithRefresh(ctx, args.node_id);
2406
3424
  if (isLocalTransport(ctx.transport)) {
@@ -2409,7 +3427,7 @@ async function meshRefineNode(ctx, args) {
2409
3427
  nodeId: args.node_id,
2410
3428
  inlineMesh: ctx.mesh
2411
3429
  });
2412
- if (result?.success && result.removeResult?.removed !== false) {
3430
+ if (result?.success && result.async !== true && result.removeResult?.removed !== false) {
2413
3431
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2414
3432
  if (idx >= 0) {
2415
3433
  ctx.mesh.nodes.splice(idx, 1);
@@ -2424,7 +3442,7 @@ async function meshRefineNode(ctx, args) {
2424
3442
  nodeId: args.node_id,
2425
3443
  inlineMesh: ctx.mesh
2426
3444
  });
2427
- if (res?.success && res.removeResult?.removed !== false) {
3445
+ if (res?.success && res.async !== true && res.removeResult?.removed !== false) {
2428
3446
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2429
3447
  if (idx >= 0) {
2430
3448
  ctx.mesh.nodes.splice(idx, 1);
@@ -2461,13 +3479,13 @@ var STANDARD_TOOLS = [
2461
3479
  function buildMcpHelpText() {
2462
3480
  const meshTools = ALL_MESH_TOOLS.map((tool) => tool.name);
2463
3481
  return `
2464
- adhdev-mcp \u2014 ADHDev MCP Server
3482
+ ADHDev MCP Server
2465
3483
 
2466
3484
  Usage:
2467
- adhdev-mcp Local mode (requires standalone daemon)
2468
- adhdev-mcp --api-key <key> Cloud mode (ADHDev cloud API)
2469
- adhdev-mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
2470
- adhdev-mcp --repo-mesh <mesh_id> Mesh mode (coordinator-scoped tools)
3485
+ adhdev mcp Local mode (requires standalone daemon)
3486
+ adhdev mcp --api-key <key> Cloud mode (ADHDev cloud API)
3487
+ adhdev mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
3488
+ adhdev-mcp --help Compatibility bin (same server, legacy package entrypoint)
2471
3489
 
2472
3490
  Options:
2473
3491
  --mode <mode> Transport: local, cloud, or ipc
@@ -2492,6 +3510,7 @@ Mesh tools: ${meshTools.join(", ")}
2492
3510
  // src/server.ts
2493
3511
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
2494
3512
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
3513
+ var import_node_os = __toESM(require("os"));
2495
3514
  var import_types = require("@modelcontextprotocol/sdk/types.js");
2496
3515
 
2497
3516
  // src/transports/local.ts
@@ -4025,6 +5044,7 @@ async function startMcpServer(opts) {
4025
5044
  requirePreTaskCheckpoint: false,
4026
5045
  requirePostTaskCheckpoint: true,
4027
5046
  requireApprovalForPush: true,
5047
+ allowAutoPublishSubmoduleMainCommits: false,
4028
5048
  requireApprovalForDestructiveGit: true,
4029
5049
  dirtyWorkspaceBehavior: "warn",
4030
5050
  maxParallelTasks: 2,
@@ -4081,11 +5101,13 @@ async function startMcpServer(opts) {
4081
5101
  }
4082
5102
  let localDaemonId;
4083
5103
  let localMachineId;
5104
+ let coordinatorHostname = import_node_os.default.hostname();
4084
5105
  if (transport instanceof LocalTransport || transport instanceof IpcTransport) {
4085
5106
  try {
4086
5107
  const { loadConfig } = await import("@adhdev/daemon-core");
4087
5108
  const cfg = loadConfig();
4088
- if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
5109
+ if (cfg.machineId) localMachineId = cfg.machineId;
5110
+ else if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
4089
5111
  } catch {
4090
5112
  }
4091
5113
  }
@@ -4093,14 +5115,16 @@ async function startMcpServer(opts) {
4093
5115
  try {
4094
5116
  const statusResult = await transport.getStatus();
4095
5117
  const instanceId = typeof statusResult?.status?.instanceId === "string" ? statusResult.status.instanceId.trim() : "";
5118
+ const hostname = typeof statusResult?.status?.hostname === "string" ? statusResult.status.hostname.trim() : typeof statusResult?.status?.machine?.hostname === "string" ? statusResult.status.machine.hostname.trim() : "";
4096
5119
  if (instanceId) localDaemonId = instanceId;
5120
+ if (hostname) coordinatorHostname = hostname;
4097
5121
  } catch {
4098
5122
  }
4099
5123
  }
4100
- const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {} };
5124
+ const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {}, ...coordinatorHostname ? { coordinatorHostname } : {} };
4101
5125
  const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
4102
5126
  const server2 = new import_server.Server(
4103
- { name: "adhdev-mcp-server", version: "0.9.81" },
5127
+ { name: "adhdev-mcp-server", version: "0.9.82" },
4104
5128
  { capabilities: { tools: {}, resources: {} } }
4105
5129
  );
4106
5130
  const { ListResourcesRequestSchema, ReadResourceRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
@@ -4126,7 +5150,7 @@ async function startMcpServer(opts) {
4126
5150
  let text;
4127
5151
  switch (name) {
4128
5152
  case "mesh_status":
4129
- text = await meshStatus(meshCtx);
5153
+ text = await meshStatus(meshCtx, a);
4130
5154
  break;
4131
5155
  case "mesh_list_nodes":
4132
5156
  text = await meshListNodes(meshCtx);
@@ -4158,6 +5182,9 @@ async function startMcpServer(opts) {
4158
5182
  case "mesh_git_status":
4159
5183
  text = await meshGitStatus(meshCtx, a);
4160
5184
  break;
5185
+ case "mesh_fast_forward_node":
5186
+ text = await meshFastForwardNode(meshCtx, a);
5187
+ break;
4161
5188
  case "mesh_checkpoint":
4162
5189
  text = await meshCheckpoint(meshCtx, a);
4163
5190
  break;
@@ -4173,6 +5200,18 @@ async function startMcpServer(opts) {
4173
5200
  case "mesh_refine_node":
4174
5201
  text = await meshRefineNode(meshCtx, a);
4175
5202
  break;
5203
+ case "mesh_refine_config_schema":
5204
+ text = await meshRefineConfigSchema(meshCtx);
5205
+ break;
5206
+ case "mesh_validate_refine_config":
5207
+ text = await meshValidateRefineConfig(meshCtx, a);
5208
+ break;
5209
+ case "mesh_suggest_refine_config":
5210
+ text = await meshSuggestRefineConfig(meshCtx, a);
5211
+ break;
5212
+ case "mesh_refine_plan":
5213
+ text = await meshRefinePlan(meshCtx, a);
5214
+ break;
4176
5215
  case "mesh_cleanup_sessions":
4177
5216
  text = await meshCleanupSessions(meshCtx, a);
4178
5217
  break;