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

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) {
@@ -832,7 +1110,7 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
832
1110
  error: `P2P dispatch failed: ${errorMessage}`
833
1111
  };
834
1112
  }
835
- return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
1113
+ return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType, providerType: resolvedProviderType };
836
1114
  } catch (e) {
837
1115
  const errorMessage = e?.message || String(e);
838
1116
  return {
@@ -862,34 +1140,199 @@ function resolveCoordinatorNode(ctx) {
862
1140
  return void 0;
863
1141
  }
864
1142
  function readNodeMachineId(node) {
865
- return readString(node.machineId) || readString(node.machine_id);
1143
+ 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
1144
  }
867
1145
  function readNodeDaemonId(node) {
868
- return readString(node.daemonId) || readString(node.daemon_id);
1146
+ 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);
1147
+ }
1148
+ function normalizeHostname(value) {
1149
+ const hostname = readString(value);
1150
+ if (!hostname) return void 0;
1151
+ return hostname.toLowerCase().replace(/\.$/, "");
1152
+ }
1153
+ function readNodeHostname(node) {
1154
+ 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);
1155
+ }
1156
+ function readNodeDisplayMachineName(node) {
1157
+ 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);
1158
+ }
1159
+ function compactIdentityEvidence(value) {
1160
+ if (!value) return void 0;
1161
+ return value.length > 24 ? `${value.slice(0, 12)}\u2026${value.slice(-8)}` : value;
1162
+ }
1163
+ function pushIdentityEvidence(evidence, label, value) {
1164
+ const compact = compactIdentityEvidence(value);
1165
+ if (compact) evidence.push(`${label}:${compact}`);
1166
+ }
1167
+ function buildNodeMachineIdentity(ctx, node) {
1168
+ const machineId = readNodeMachineId(node);
1169
+ const daemonId = readNodeDaemonId(node);
1170
+ const hostname = readNodeHostname(node);
1171
+ const machineName = readNodeDisplayMachineName(node);
1172
+ const coordinatorHostname = readString(ctx.coordinatorHostname);
1173
+ const localControlPlaneReason = getLocalControlPlaneMatchReason(ctx, node);
1174
+ const directLocal = !!localControlPlaneReason;
1175
+ const hostnameMatches = Boolean(
1176
+ normalizeHostname(hostname) && normalizeHostname(coordinatorHostname) && normalizeHostname(hostname) === normalizeHostname(coordinatorHostname)
1177
+ );
1178
+ const sameMachine = directLocal || hostnameMatches;
1179
+ const evidence = [];
1180
+ pushIdentityEvidence(evidence, "machineName", machineName);
1181
+ pushIdentityEvidence(evidence, "hostname", hostname);
1182
+ pushIdentityEvidence(evidence, "machineId", machineId);
1183
+ pushIdentityEvidence(evidence, "daemonId", daemonId);
1184
+ if (localControlPlaneReason) {
1185
+ pushIdentityEvidence(evidence, "localMatch", localControlPlaneReason);
1186
+ pushIdentityEvidence(evidence, "localMachineId", ctx.localMachineId);
1187
+ pushIdentityEvidence(evidence, "localDaemonId", ctx.localDaemonId);
1188
+ }
1189
+ const locality = sameMachine ? "same_machine" : evidence.length > 0 ? "remote_known" : "remote_or_unknown";
1190
+ 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";
1191
+ return {
1192
+ daemonId,
1193
+ machineId,
1194
+ hostname,
1195
+ machineName,
1196
+ displayName: machineName || hostname || daemonId || machineId,
1197
+ coordinatorHostname,
1198
+ sameMachine,
1199
+ locality,
1200
+ localityReason,
1201
+ identityEvidence: evidence
1202
+ };
1203
+ }
1204
+ function nodeHasLocalDaemonEvidence(ctx, node) {
1205
+ const isLocal = (session) => {
1206
+ if (!session || typeof session !== "object") return false;
1207
+ if (ctx.localDaemonId && session.settings?.meshCoordinatorDaemonId === ctx.localDaemonId) return true;
1208
+ if (session.launchedByCoordinator === true) return true;
1209
+ if (ctx.localDaemonId && session.runtime?.owner === ctx.localDaemonId) return true;
1210
+ if (ctx.localDaemonId && session.daemonClient?.daemonId === ctx.localDaemonId) return true;
1211
+ return false;
1212
+ };
1213
+ const sessionArrays = [
1214
+ node?.sessions,
1215
+ node?.activeSessions,
1216
+ node?.active_sessions,
1217
+ node?.lastProbe?.sessions,
1218
+ node?.last_probe?.sessions,
1219
+ node?.lastProbe?.status?.sessions,
1220
+ node?.last_probe?.status?.sessions
1221
+ ];
1222
+ for (const arr of sessionArrays) {
1223
+ if (Array.isArray(arr) && arr.some(isLocal)) return true;
1224
+ }
1225
+ const sessionRecords = [
1226
+ node?.activeSession,
1227
+ node?.active_session,
1228
+ node?.currentSession,
1229
+ node?.current_session,
1230
+ node?.runtimeSession,
1231
+ node?.runtime_session,
1232
+ node?.session,
1233
+ node?.lastProbe?.activeSession,
1234
+ node?.last_probe?.active_session,
1235
+ node?.lastProbe?.currentSession,
1236
+ node?.last_probe?.current_session,
1237
+ node?.lastProbe?.session,
1238
+ node?.last_probe?.session
1239
+ ];
1240
+ for (const session of sessionRecords) {
1241
+ if (isLocal(session)) return true;
1242
+ }
1243
+ return false;
869
1244
  }
870
1245
  function isDirectLocalNode(ctx, node) {
871
1246
  const machineId = readNodeMachineId(node);
872
1247
  const daemonId = readNodeDaemonId(node);
873
1248
  return Boolean(
874
- ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId
1249
+ ctx.localMachineId && machineId === ctx.localMachineId || ctx.localDaemonId && daemonId === ctx.localDaemonId || nodeHasLocalDaemonEvidence(ctx, node)
875
1250
  );
876
1251
  }
1252
+ function isConfiguredCoordinatorNode(ctx, node) {
1253
+ if (!ctx.localMachineId && !ctx.localDaemonId) return false;
1254
+ const nodeId = readString(node.id) || readString(node.nodeId) || readString(node.node_id);
1255
+ if (!nodeId) return false;
1256
+ const nodeDaemonId = readNodeDaemonId(node);
1257
+ const nodeMachineId = readNodeMachineId(node);
1258
+ if (nodeDaemonId && ctx.localDaemonId && nodeDaemonId !== ctx.localDaemonId) return false;
1259
+ if (nodeMachineId && ctx.localMachineId && nodeMachineId !== ctx.localMachineId) return false;
1260
+ const preferredNodeId = readString(ctx.mesh.coordinator?.preferredNodeId) || readString(ctx.mesh.coordinator?.preferred_node_id);
1261
+ if (preferredNodeId) return nodeId === preferredNodeId;
1262
+ const first = ctx.mesh.nodes?.[0];
1263
+ const firstNodeId = readString(first?.id) || readString(first?.nodeId) || readString(first?.node_id);
1264
+ return !!firstNodeId && nodeId === firstNodeId;
1265
+ }
1266
+ function getLocalControlPlaneMatchReason(ctx, node) {
1267
+ if (isDirectLocalNode(ctx, node)) return "matched coordinator daemon or machine id";
1268
+ if (isConfiguredCoordinatorNode(ctx, node)) return "matched configured coordinator node";
1269
+ if (node.isLocalWorktree === true) {
1270
+ const sourceNode = findClonedFromNode(ctx, node);
1271
+ if (sourceNode && isDirectLocalNode(ctx, sourceNode)) return "matched local cloned-from node";
1272
+ if (sourceNode && isConfiguredCoordinatorNode(ctx, sourceNode)) return "matched configured coordinator source node";
1273
+ }
1274
+ return void 0;
1275
+ }
877
1276
  function findClonedFromNode(ctx, node) {
878
1277
  const clonedFromNodeId = readString(node.clonedFromNodeId) || readString(node.cloned_from_node_id);
879
1278
  if (!clonedFromNodeId) return void 0;
880
1279
  return ctx.mesh.nodes.find((n) => n.id === clonedFromNodeId || n.nodeId === clonedFromNodeId || n.node_id === clonedFromNodeId);
881
1280
  }
882
1281
  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;
1282
+ return !!getLocalControlPlaneMatchReason(ctx, node);
889
1283
  }
890
1284
  function meshSessionCacheKey(nodeId, runtimeSessionId) {
891
1285
  return `${nodeId}:${runtimeSessionId}`;
892
1286
  }
1287
+ function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata) {
1288
+ const keyNodeId = readString(nodeId);
1289
+ const keySessionId = readString(runtimeSessionId);
1290
+ if (!keyNodeId || !keySessionId) return;
1291
+ const providerType = readString(metadata.providerType);
1292
+ const providerSessionId = readString(metadata.providerSessionId);
1293
+ if (!providerType && !providerSessionId) return;
1294
+ const existing = getSessionMetadata(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
1295
+ meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
1296
+ providerType: providerType || existing.providerType,
1297
+ providerSessionId: providerSessionId || existing.providerSessionId,
1298
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
1299
+ });
1300
+ }
1301
+ function rememberMeshSessionProviderMetadataFromEvent(event) {
1302
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : event && typeof event === "object" ? event : {};
1303
+ const nodeId = readString(event?.nodeId) || readString(metadataEvent.nodeId) || readString(metadataEvent.meshNodeId);
1304
+ const sessionId = readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId) || readString(event?.sessionId);
1305
+ rememberMeshSessionProviderMetadata(nodeId, sessionId, {
1306
+ providerType: readString(metadataEvent.providerType) || readString(event?.providerType) || "",
1307
+ providerSessionId: readString(metadataEvent.providerSessionId) || readString(event?.providerSessionId)
1308
+ });
1309
+ }
1310
+ function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
1311
+ const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 50 });
1312
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
1313
+ const entry = entries[i];
1314
+ const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
1315
+ const entryNodeId = readString(entry.nodeId) || readString(payload.nodeId) || readString(payload.meshNodeId);
1316
+ if (entryNodeId && entryNodeId !== nodeId) continue;
1317
+ const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
1318
+ if (entrySessionId !== runtimeSessionId) continue;
1319
+ const providerType = readString(entry.providerType) || readString(payload.providerType);
1320
+ const completionDiagnostic = payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? payload.completionDiagnostic : {};
1321
+ const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : {};
1322
+ const providerSessionId = readString(payload.providerSessionId) || readString(completionDiagnostic.providerSessionId) || readString(metadataEvent.providerSessionId);
1323
+ if (providerType || providerSessionId) {
1324
+ return { providerType: providerType || "", providerSessionId };
1325
+ }
1326
+ }
1327
+ return void 0;
1328
+ }
1329
+ function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
1330
+ const cached = getSessionMetadata(meshSessionCacheKey(nodeId, runtimeSessionId));
1331
+ if (cached?.providerType || cached?.providerSessionId) return cached;
1332
+ const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
1333
+ if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
1334
+ return fromLedger;
1335
+ }
893
1336
  function countUncommittedChanges(status) {
894
1337
  if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
895
1338
  const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
@@ -902,6 +1345,20 @@ function isGitStatusDirty(status) {
902
1345
  if (typeof status?.dirty === "boolean") return status.dirty;
903
1346
  return countUncommittedChanges(status) > 0;
904
1347
  }
1348
+ function slimLedgerPayload(payload) {
1349
+ const slim = {};
1350
+ for (const [k, v] of Object.entries(payload)) {
1351
+ if (k === "message" || k === "taskSummary") {
1352
+ slim[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "\u2026" : v;
1353
+ } else if (k === "evidence" || k === "workerResult" || k === "gitStatus" || k === "validationResults") {
1354
+ } else if (k === "finalSummary") {
1355
+ slim[k] = typeof v === "string" && v.length > 300 ? v.slice(0, 300) + "\u2026" : v;
1356
+ } else {
1357
+ slim[k] = v;
1358
+ }
1359
+ }
1360
+ return slim;
1361
+ }
905
1362
  function readRelatedRepos(node) {
906
1363
  const raw = Array.isArray(node.relatedRepos) ? node.relatedRepos : Array.isArray(node.policy?.relatedRepos) ? node.policy.relatedRepos : [];
907
1364
  return raw.map((entry) => ({
@@ -960,6 +1417,16 @@ function missingProviderPriorityMessage(nodeId) {
960
1417
  return `Node '${nodeId}' has no providerPriority policy; pass type explicitly or configure node.policy.providerPriority`;
961
1418
  }
962
1419
  function getNodeLaunchReadiness(node) {
1420
+ const bootstrap = node.worktreeBootstrap;
1421
+ if (node.isLocalWorktree && bootstrap?.status === "failed" && bootstrap?.required !== false) {
1422
+ return {
1423
+ providerPriority: readProviderPriority(node.policy),
1424
+ launchReady: false,
1425
+ launchBlockedReason: "worktree_bootstrap_failed",
1426
+ launchBlockedMessage: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : "Required worktree bootstrap failed; resolve it before launching an agent into this node.",
1427
+ worktreeBootstrap: bootstrap
1428
+ };
1429
+ }
963
1430
  const providerPriority = readProviderPriority(node.policy);
964
1431
  if (providerPriority.length) {
965
1432
  return {
@@ -974,6 +1441,33 @@ function getNodeLaunchReadiness(node) {
974
1441
  launchBlockedMessage: missingProviderPriorityMessage(node.id)
975
1442
  };
976
1443
  }
1444
+ function getWorktreeBootstrapLaunchBlock(node) {
1445
+ const bootstrap = node.worktreeBootstrap;
1446
+ if (!node.isLocalWorktree || bootstrap?.status !== "failed" || bootstrap?.required === false) return void 0;
1447
+ return {
1448
+ success: false,
1449
+ code: "worktree_bootstrap_failed",
1450
+ error: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : `Node '${node.id}' has a failed required worktree bootstrap.`,
1451
+ nodeId: node.id,
1452
+ worktreeBootstrap: bootstrap,
1453
+ recoveryHint: "Fix the configured worktree bootstrap command or remove/recreate the worktree node before launching an agent."
1454
+ };
1455
+ }
1456
+ async function collectLiveStatusSessions(ctx, node) {
1457
+ try {
1458
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
1459
+ return extractStatusMetadataSessions(statusResult);
1460
+ } catch {
1461
+ return [];
1462
+ }
1463
+ }
1464
+ async function collectMeshViewQueueNodesWithLiveSessions(ctx) {
1465
+ const nodes = await Promise.all(ctx.mesh.nodes.map(async (node) => {
1466
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
1467
+ return liveSessions.length > 0 ? { ...node, sessions: liveSessions } : node;
1468
+ }));
1469
+ return nodes;
1470
+ }
977
1471
  function readNumeric(value, fallback = 0) {
978
1472
  const parsed = Number(value);
979
1473
  return Number.isFinite(parsed) ? parsed : fallback;
@@ -1109,7 +1603,8 @@ async function commandForNode(ctx, node, command, args = {}) {
1109
1603
  if (isLocalTransport(ctx.transport)) {
1110
1604
  return ctx.transport.command(command, args);
1111
1605
  }
1112
- throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
1606
+ const identity = buildNodeMachineIdentity(ctx, node);
1607
+ 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
1608
  }
1114
1609
  function normalizePendingMeshCoordinatorEvents(value) {
1115
1610
  const payload = unwrapCommandPayload(value);
@@ -1127,6 +1622,14 @@ function buildMeshForwardPayloadFromPendingEvent(event) {
1127
1622
  providerType: readString(metadataEvent.providerType),
1128
1623
  providerSessionId: readString(metadataEvent.providerSessionId),
1129
1624
  finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
1625
+ jobId: readString(metadataEvent.jobId),
1626
+ interactionId: readString(metadataEvent.interactionId),
1627
+ status: readString(metadataEvent.status),
1628
+ targetDaemonId: readString(metadataEvent.targetDaemonId),
1629
+ startedAt: readString(metadataEvent.startedAt),
1630
+ completedAt: readString(metadataEvent.completedAt),
1631
+ retryOfJobId: readString(metadataEvent.retryOfJobId),
1632
+ ...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
1130
1633
  ...metadataEvent.intentional === true ? { intentional: true } : {},
1131
1634
  ...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
1132
1635
  ...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
@@ -1143,8 +1646,9 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
1143
1646
  const surfacedEvents = [];
1144
1647
  try {
1145
1648
  surfacedEvents.push(
1146
- ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
1649
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1147
1650
  );
1651
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1148
1652
  } catch {
1149
1653
  }
1150
1654
  for (const node of ctx.mesh.nodes) {
@@ -1152,27 +1656,31 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
1152
1656
  if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
1153
1657
  try {
1154
1658
  const remoteEvents = normalizePendingMeshCoordinatorEvents(
1155
- await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", {})
1659
+ await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", { meshId: ctx.mesh.id })
1156
1660
  ).filter(matchesCurrentMesh);
1157
1661
  if (remoteEvents.length === 0) continue;
1158
1662
  for (const event of remoteEvents) {
1159
1663
  const payload = buildMeshForwardPayloadFromPendingEvent(event);
1160
1664
  if (!payload.event || !payload.meshId) continue;
1161
1665
  await ctx.transport.command("mesh_forward_event", payload);
1666
+ rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
1162
1667
  }
1163
1668
  } catch {
1164
1669
  }
1165
1670
  }
1166
1671
  try {
1167
1672
  surfacedEvents.push(
1168
- ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
1673
+ ...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", { meshId: ctx.mesh.id })).filter(matchesCurrentMesh)
1169
1674
  );
1675
+ surfacedEvents.forEach(rememberMeshSessionProviderMetadataFromEvent);
1170
1676
  } catch {
1171
1677
  }
1172
1678
  return surfacedEvents;
1173
1679
  }
1174
1680
  if (isLocalTransport(ctx.transport)) {
1175
- return (0, import_daemon_core.drainPendingMeshCoordinatorEvents)().filter(matchesCurrentMesh);
1681
+ const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId).filter(matchesCurrentMesh);
1682
+ events.forEach(rememberMeshSessionProviderMetadataFromEvent);
1683
+ return events;
1176
1684
  }
1177
1685
  return [];
1178
1686
  }
@@ -1189,11 +1697,12 @@ function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
1189
1697
  }
1190
1698
  var MESH_STATUS_TOOL = {
1191
1699
  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.",
1700
+ 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
1701
  inputSchema: {
1194
1702
  type: "object",
1195
1703
  properties: {
1196
- _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
1704
+ _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." },
1705
+ includeStaleDirectWorkDetails: { type: "boolean", description: "Opt in to the full staleDirectWork array. Defaults false; normal status returns compact staleDirectWorkSummary only." }
1197
1706
  }
1198
1707
  }
1199
1708
  };
@@ -1213,14 +1722,18 @@ var MESH_ENQUEUE_TASK_TOOL = {
1213
1722
  inputSchema: {
1214
1723
  type: "object",
1215
1724
  properties: {
1216
- message: { type: "string", description: "The task instruction for the agent." }
1725
+ message: { type: "string", description: "The task instruction for the agent." },
1726
+ 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." },
1727
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
1728
+ requiredTags: { type: "array", items: { type: "string" }, description: "Optional capability tags that every eligible node must have, e.g. os=darwin, provider=codex-cli, gpu." },
1729
+ required_tags: { type: "array", items: { type: "string" }, description: "Snake_case alias for requiredTags." }
1217
1730
  },
1218
1731
  required: ["message"]
1219
1732
  }
1220
1733
  };
1221
1734
  var MESH_VIEW_QUEUE_TOOL = {
1222
1735
  name: "mesh_view_queue",
1223
- description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records.",
1736
+ 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
1737
  inputSchema: {
1225
1738
  type: "object",
1226
1739
  properties: {
@@ -1273,7 +1786,9 @@ var MESH_SEND_TASK_TOOL = {
1273
1786
  properties: {
1274
1787
  node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
1275
1788
  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." }
1789
+ message: { type: "string", description: "Natural-language task to send to the agent." },
1790
+ 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." },
1791
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." }
1277
1792
  },
1278
1793
  required: ["node_id", "session_id", "message"]
1279
1794
  }
@@ -1331,6 +1846,21 @@ var MESH_GIT_STATUS_TOOL = {
1331
1846
  required: ["node_id"]
1332
1847
  }
1333
1848
  };
1849
+ var MESH_FAST_FORWARD_NODE_TOOL = {
1850
+ name: "mesh_fast_forward_node",
1851
+ 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.",
1852
+ inputSchema: {
1853
+ type: "object",
1854
+ properties: {
1855
+ node_id: { type: "string", description: "Target node ID." },
1856
+ branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
1857
+ execute: { type: "boolean", description: "When true, apply the fast-forward if all safety gates pass. Defaults false/dry-run." },
1858
+ dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
1859
+ 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." }
1860
+ },
1861
+ required: ["node_id"]
1862
+ }
1863
+ };
1334
1864
  var MESH_CHECKPOINT_TOOL = {
1335
1865
  name: "mesh_checkpoint",
1336
1866
  description: "Create a git checkpoint (commit) on a mesh node workspace.",
@@ -1414,7 +1944,7 @@ var MESH_TASK_HISTORY_TOOL = {
1414
1944
  type: "object",
1415
1945
  properties: {
1416
1946
  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." }
1947
+ 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
1948
  }
1419
1949
  }
1420
1950
  };
@@ -1434,7 +1964,7 @@ var MESH_RECONCILE_LEDGER_TOOL = {
1434
1964
  };
1435
1965
  var MESH_REFINE_NODE_TOOL = {
1436
1966
  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.",
1967
+ 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
1968
  inputSchema: {
1439
1969
  type: "object",
1440
1970
  properties: {
@@ -1443,6 +1973,43 @@ var MESH_REFINE_NODE_TOOL = {
1443
1973
  required: ["node_id"]
1444
1974
  }
1445
1975
  };
1976
+ var MESH_REFINE_CONFIG_SCHEMA_TOOL = {
1977
+ name: "mesh_refine_config_schema",
1978
+ 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.",
1979
+ inputSchema: { type: "object", properties: {} }
1980
+ };
1981
+ var MESH_VALIDATE_REFINE_CONFIG_TOOL = {
1982
+ name: "mesh_validate_refine_config",
1983
+ description: "Validate the repo mesh/refine config for a node/workspace without running validation commands or merging.",
1984
+ inputSchema: {
1985
+ type: "object",
1986
+ properties: {
1987
+ node_id: { type: "string", description: "Optional node/workspace whose refine config should be loaded. Defaults to the first mesh node." },
1988
+ config: { type: "object", description: "Optional inline config object to validate instead of loading from the repo." }
1989
+ }
1990
+ }
1991
+ };
1992
+ var MESH_SUGGEST_REFINE_CONFIG_TOOL = {
1993
+ name: "mesh_suggest_refine_config",
1994
+ description: "Suggest a repo mesh/refine config scaffold from project context/package scripts. Suggestions are never executed until saved as explicit refine config.",
1995
+ inputSchema: {
1996
+ type: "object",
1997
+ properties: {
1998
+ node_id: { type: "string", description: "Optional node/workspace used for suggestions. Defaults to the first mesh node." }
1999
+ }
2000
+ }
2001
+ };
2002
+ var MESH_REFINE_PLAN_TOOL = {
2003
+ name: "mesh_refine_plan",
2004
+ 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.",
2005
+ inputSchema: {
2006
+ type: "object",
2007
+ properties: {
2008
+ node_id: { type: "string", description: "Node ID of the worktree node to plan." }
2009
+ },
2010
+ required: ["node_id"]
2011
+ }
2012
+ };
1446
2013
  var ALL_MESH_TOOLS = [
1447
2014
  MESH_STATUS_TOOL,
1448
2015
  MESH_LIST_NODES_TOOL,
@@ -1455,24 +2022,31 @@ var ALL_MESH_TOOLS = [
1455
2022
  MESH_READ_DEBUG_TOOL,
1456
2023
  MESH_LAUNCH_SESSION_TOOL,
1457
2024
  MESH_GIT_STATUS_TOOL,
2025
+ MESH_FAST_FORWARD_NODE_TOOL,
1458
2026
  MESH_CHECKPOINT_TOOL,
1459
2027
  MESH_APPROVE_TOOL,
1460
2028
  MESH_CLONE_NODE_TOOL,
1461
2029
  MESH_REMOVE_NODE_TOOL,
1462
2030
  MESH_REFINE_NODE_TOOL,
2031
+ MESH_REFINE_CONFIG_SCHEMA_TOOL,
2032
+ MESH_VALIDATE_REFINE_CONFIG_TOOL,
2033
+ MESH_SUGGEST_REFINE_CONFIG_TOOL,
2034
+ MESH_REFINE_PLAN_TOOL,
1463
2035
  MESH_CLEANUP_SESSIONS_TOOL,
1464
2036
  MESH_TASK_HISTORY_TOOL,
1465
2037
  MESH_RECONCILE_LEDGER_TOOL
1466
2038
  ];
1467
- async function meshStatus(ctx) {
2039
+ async function meshStatus(ctx, args = {}) {
1468
2040
  await refreshMeshFromDaemon(ctx);
1469
2041
  const { mesh, transport } = ctx;
1470
- const results = [];
1471
2042
  const ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1472
- for (const node of mesh.nodes) {
2043
+ const results = await Promise.all(mesh.nodes.map(async (node) => {
1473
2044
  const entry = {
1474
2045
  nodeId: node.id,
1475
2046
  workspace: node.workspace,
2047
+ machine: buildNodeMachineIdentity(ctx, node),
2048
+ daemonId: readNodeDaemonId(node),
2049
+ machineId: readNodeMachineId(node),
1476
2050
  ...getNodeLaunchReadiness(node)
1477
2051
  };
1478
2052
  try {
@@ -1482,6 +2056,7 @@ async function meshStatus(ctx) {
1482
2056
  const uncommittedChanges = countUncommittedChanges(status);
1483
2057
  const dirty = isGitStatusDirty(status);
1484
2058
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
2059
+ assignFullGitSnapshot(entry, status);
1485
2060
  entry.branch = status?.branch;
1486
2061
  entry.isDirty = dirty;
1487
2062
  entry.uncommittedChanges = uncommittedChanges;
@@ -1503,6 +2078,7 @@ async function meshStatus(ctx) {
1503
2078
  const uncommittedChanges = countUncommittedChanges(status);
1504
2079
  const dirty = isGitStatusDirty(status);
1505
2080
  entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
2081
+ assignFullGitSnapshot(entry, status);
1506
2082
  entry.branch = status?.branch;
1507
2083
  entry.isDirty = dirty;
1508
2084
  entry.uncommittedChanges = uncommittedChanges;
@@ -1538,7 +2114,7 @@ async function meshStatus(ctx) {
1538
2114
  if (recoveryContext.consecutiveNodeFailures > 0) {
1539
2115
  entry.recoveryHints = {
1540
2116
  consecutiveFailures: recoveryContext.consecutiveNodeFailures,
1541
- lastTaskMessage: recoveryContext.lastTaskMessage,
2117
+ lastTaskMessage: typeof recoveryContext.lastTaskMessage === "string" ? recoveryContext.lastTaskMessage.slice(0, 100) + (recoveryContext.lastTaskMessage.length > 100 ? "\u2026" : "") : recoveryContext.lastTaskMessage,
1542
2118
  advice: recoveryContext.advice,
1543
2119
  retryRecommended: recoveryContext.retryRecommended
1544
2120
  };
@@ -1578,7 +2154,47 @@ async function meshStatus(ctx) {
1578
2154
  }
1579
2155
  const relatedRepos = await collectRelatedRepoStatuses(ctx, node);
1580
2156
  if (relatedRepos.length) entry.relatedRepos = relatedRepos;
1581
- results.push(entry);
2157
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
2158
+ if (liveSessions.length > 0) {
2159
+ entry.sessions = liveSessions.map((s) => {
2160
+ const coordinatorMeshId = typeof s.coordinator?.meshId === "string" ? s.coordinator.meshId : void 0;
2161
+ const isSelfCoordinator = coordinatorMeshId === mesh.id;
2162
+ return {
2163
+ id: s.instanceId ?? s.id ?? s.sessionId,
2164
+ status: s.status ?? s.lifecycle ?? s.state,
2165
+ providerType: s.providerType ?? s.cliType ?? s.type,
2166
+ ...s.activeChat?.status ? { chatStatus: s.activeChat.status } : {},
2167
+ ...isSelfCoordinator ? { isSelfCoordinator: true, role: "coordinator" } : {}
2168
+ };
2169
+ }).filter((s) => s.id);
2170
+ }
2171
+ return entry;
2172
+ }));
2173
+ const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 200 });
2174
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2175
+ meshId: mesh.id,
2176
+ queue: (0, import_daemon_core.getQueue)(mesh.id),
2177
+ ledgerEntries,
2178
+ nodes: results
2179
+ });
2180
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
2181
+ const staleDirectWorkSummary = (0, import_daemon_core.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
2182
+ note: activeWorkEvidence.staleDirectWorkNote,
2183
+ 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."
2184
+ });
2185
+ const coordinatorSessions = [];
2186
+ for (const nodeEntry of results) {
2187
+ const sessions = Array.isArray(nodeEntry.sessions) ? nodeEntry.sessions : [];
2188
+ for (const s of sessions) {
2189
+ if (s?.isSelfCoordinator === true && s.id) {
2190
+ coordinatorSessions.push({
2191
+ nodeId: nodeEntry.nodeId,
2192
+ sessionId: s.id,
2193
+ providerType: s.providerType,
2194
+ status: s.status
2195
+ });
2196
+ }
2197
+ }
1582
2198
  }
1583
2199
  const response = {
1584
2200
  meshId: mesh.id,
@@ -1586,8 +2202,29 @@ async function meshStatus(ctx) {
1586
2202
  repoIdentity: mesh.repoIdentity,
1587
2203
  policy: mesh.policy,
1588
2204
  refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
2205
+ sourceOfTruth: {
2206
+ membership: "coordinator_daemon_live_mesh",
2207
+ currentStatus: "live_git_and_session_probes",
2208
+ activeWork: "mesh_queue_file_and_local_ledger",
2209
+ historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
2210
+ },
1589
2211
  nodes: results,
1590
- branchConvergenceSummary: summarizeBranchConvergence(results)
2212
+ activeWork: activeWorkEvidence.activeWork,
2213
+ staleDirectWorkSummary,
2214
+ ...args.includeStaleDirectWorkDetails === true ? { staleDirectWork: activeWorkEvidence.staleDirectWork } : {},
2215
+ // terminalDirectWork is historical (completed/failed direct dispatches) — opt-in only.
2216
+ ...args.includeTerminalDirectWork === true ? { terminalDirectWork: activeWorkEvidence.terminalDirectWork } : {},
2217
+ activeWorkSummary: activeWorkEvidence.summary,
2218
+ ...pollingGuidance ? { pollingGuidance } : {},
2219
+ branchConvergenceSummary: summarizeBranchConvergence(results),
2220
+ ...coordinatorSessions.length > 0 ? {
2221
+ coordinatorSessions,
2222
+ selfIdentification: {
2223
+ meshId: mesh.id,
2224
+ coordinatorSessions,
2225
+ 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."
2226
+ }
2227
+ } : {}
1591
2228
  };
1592
2229
  try {
1593
2230
  response.ledgerSummary = ledgerSummary;
@@ -1595,6 +2232,14 @@ async function meshStatus(ctx) {
1595
2232
  }
1596
2233
  try {
1597
2234
  const pendingEvents = await drainCoordinatorPendingEvents(ctx);
2235
+ const asyncRefineJobs = (0, import_daemon_core.buildMeshAsyncRefineJobs)({
2236
+ meshId: mesh.id,
2237
+ ledgerEntries,
2238
+ pendingEvents
2239
+ });
2240
+ if (asyncRefineJobs.length > 0) {
2241
+ response.asyncRefineJobs = asyncRefineJobs;
2242
+ }
1598
2243
  if (pendingEvents.length > 0) {
1599
2244
  response.pendingCoordinatorEvents = pendingEvents;
1600
2245
  }
@@ -1604,12 +2249,21 @@ async function meshStatus(ctx) {
1604
2249
  }
1605
2250
  async function meshTaskHistory(ctx, args) {
1606
2251
  const { mesh } = ctx;
1607
- await drainCoordinatorPendingEvents(ctx);
2252
+ const pendingEvents = await drainCoordinatorPendingEvents(ctx);
1608
2253
  const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
1609
2254
  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 });
2255
+ const rawEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
2256
+ const entries = rawEntries.map((e) => ({
2257
+ ...e,
2258
+ payload: e.payload ? slimLedgerPayload(e.payload) : e.payload
2259
+ }));
1611
2260
  const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1612
- return JSON.stringify({ meshId: mesh.id, entries, summary }, null, 2);
2261
+ return JSON.stringify({
2262
+ meshId: mesh.id,
2263
+ entries,
2264
+ summary,
2265
+ ...pendingEvents.length > 0 ? { pendingCoordinatorEvents: pendingEvents } : {}
2266
+ }, null, 2);
1613
2267
  }
1614
2268
  async function meshReconcileLedger(ctx, args) {
1615
2269
  await refreshMeshFromDaemon(ctx);
@@ -1699,6 +2353,9 @@ async function meshListNodes(ctx) {
1699
2353
  nodeId: n.id,
1700
2354
  workspace: n.workspace,
1701
2355
  repoRoot: n.repoRoot,
2356
+ daemonId: readNodeDaemonId(n),
2357
+ machineId: readNodeMachineId(n),
2358
+ machine: buildNodeMachineIdentity(ctx, n),
1702
2359
  isLocalWorktree: n.isLocalWorktree,
1703
2360
  policy: n.policy,
1704
2361
  relatedRepos: readRelatedRepos(n),
@@ -1708,12 +2365,14 @@ async function meshListNodes(ctx) {
1708
2365
  }, null, 2);
1709
2366
  }
1710
2367
  async function meshEnqueueTask(ctx, args) {
2368
+ const taskMode = readString(args.task_mode) || readString(args.taskMode);
2369
+ const requiredTags = (0, import_daemon_core.normalizeMeshCapabilityTags)(Array.isArray(args.requiredTags) ? args.requiredTags : args.required_tags);
1711
2370
  try {
1712
- const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message);
2371
+ const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode, requiredTags });
1713
2372
  if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
1714
2373
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1715
2374
  });
1716
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2375
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode, requiredTags: task.requiredTags });
1717
2376
  }
1718
2377
  if (ctx.transport instanceof IpcTransport) {
1719
2378
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
@@ -1722,43 +2381,97 @@ async function meshEnqueueTask(ctx, args) {
1722
2381
  for (const node of ctx.mesh.nodes) {
1723
2382
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1724
2383
  if (isLocalNode || !node.daemonId) continue;
2384
+ if (!(0, import_daemon_core.nodeSatisfiesRequiredTags)(requiredTags, (0, import_daemon_core.buildMeshNodeCapabilityTags)(node))) continue;
1725
2385
  dispatchPromises.push(
1726
2386
  ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
1727
2387
  if (result.success) {
1728
2388
  try {
2389
+ const providerType = result.providerType;
2390
+ const descriptor = summarizeTaskMessage(args.message);
1729
2391
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1730
2392
  kind: "task_dispatched",
1731
2393
  nodeId: node.id,
1732
2394
  sessionId: result.sessionId,
1733
- payload: { message: args.message, via: "p2p_direct", taskId: task.id }
2395
+ providerType,
2396
+ payload: {
2397
+ source: "queue",
2398
+ via: "p2p_direct",
2399
+ taskId: task.id,
2400
+ message: args.message,
2401
+ taskTitle: descriptor.taskTitle,
2402
+ taskSummary: descriptor.taskSummary,
2403
+ ...task.taskMode ? { taskMode: task.taskMode } : {},
2404
+ ...providerType ? { providerType } : {},
2405
+ targetSessionId: result.sessionId
2406
+ }
1734
2407
  });
1735
2408
  } catch {
1736
2409
  }
1737
2410
  }
1738
- }).catch(() => {
2411
+ }).catch((err) => {
2412
+ try {
2413
+ (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2414
+ kind: "p2p_dispatch_failed",
2415
+ nodeId: node.id,
2416
+ payload: {
2417
+ source: "queue",
2418
+ via: "p2p_direct",
2419
+ taskId: task.id,
2420
+ error: err?.message || String(err),
2421
+ dispatchFailedAt: (/* @__PURE__ */ new Date()).toISOString()
2422
+ }
2423
+ });
2424
+ } catch {
2425
+ }
1739
2426
  })
1740
2427
  );
1741
2428
  }
1742
2429
  Promise.all(dispatchPromises).catch(() => {
1743
2430
  });
1744
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2431
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode, requiredTags: task.requiredTags });
1745
2432
  }
1746
- return JSON.stringify({ success: true, taskId: task.id, status: task.status });
2433
+ return JSON.stringify({ success: true, source: "queue", taskId: task.id, status: task.status, taskMode: task.taskMode, requiredTags: task.requiredTags });
1747
2434
  } catch (e) {
1748
- return JSON.stringify({ success: false, error: e.message });
2435
+ const message = e?.message || String(e);
2436
+ if (message.includes("live_debug_readonly_guardrail_violation")) {
2437
+ return JSON.stringify({ success: false, code: "live_debug_readonly_guardrail_violation", taskMode, error: message });
2438
+ }
2439
+ return JSON.stringify({ success: false, error: message });
1749
2440
  }
1750
2441
  }
1751
2442
  async function meshViewQueue(ctx, args) {
1752
2443
  try {
2444
+ await refreshMeshFromDaemon(ctx);
1753
2445
  const statusFilter = sanitizeQueueStatusFilter(args.status);
1754
2446
  const view = normalizeQueueViewMode(args.view);
1755
- const fullQueue = annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh);
2447
+ const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness((0, import_daemon_core.getQueue)(ctx.mesh.id), ctx.mesh));
1756
2448
  const queue = filterQueueForView(fullQueue, view, statusFilter);
1757
2449
  const summary = buildQueueStatusSummary(fullQueue);
1758
2450
  const visibleSummary = buildQueueStatusSummary(queue);
1759
2451
  const maintenance = buildQueueMaintenanceReport(fullQueue);
2452
+ const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
2453
+ (0, import_daemon_core.markStaleDirectDispatches)(ctx.mesh.id);
2454
+ const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
2455
+ const directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
2456
+ const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2457
+ meshId: ctx.mesh.id,
2458
+ queue: fullQueue,
2459
+ ledgerEntries,
2460
+ // Always pass BeadsDB records (may be empty). buildMeshActiveWork uses them for local
2461
+ // dispatches and falls through to ledger scan for remote P2P dispatches not in BeadsDB.
2462
+ directDispatches,
2463
+ nodes: liveNodes
2464
+ });
2465
+ const recentDispatchFailures = ledgerEntries.filter((e) => e.kind === "p2p_dispatch_failed").slice(-20).map((e) => ({
2466
+ nodeId: e.nodeId,
2467
+ taskId: e.payload?.taskId,
2468
+ error: e.payload?.error,
2469
+ via: e.payload?.via,
2470
+ failedAt: e.payload?.dispatchFailedAt || e.timestamp
2471
+ }));
1760
2472
  const staleAssignedTasks = maintenance.staleAssignedTasks || [];
1761
2473
  const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
2474
+ const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
1762
2475
  return JSON.stringify({
1763
2476
  success: true,
1764
2477
  sourceOfTruth: {
@@ -1773,21 +2486,20 @@ async function meshViewQueue(ctx, args) {
1773
2486
  filtered: Boolean(statusFilter?.length) || view !== "all"
1774
2487
  },
1775
2488
  queue,
1776
- visibleQueue: queue,
1777
- visibleSummary,
2489
+ activeWork: activeWorkEvidence.activeWork,
2490
+ staleDirectWork: activeWorkEvidence.staleDirectWork,
2491
+ activeWorkSummary: activeWorkEvidence.summary,
2492
+ ...pollingGuidance ? { pollingGuidance } : {},
1778
2493
  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
2494
  staleAssignedTasks,
1788
2495
  staleAssignedCount: maintenance.staleAssignedCount,
1789
2496
  queueMaintenance: maintenance,
1790
2497
  cleanupDryRun: maintenance,
2498
+ ...recentDispatchFailures.length > 0 ? {
2499
+ recentDispatchFailures,
2500
+ dispatchFailureCount: recentDispatchFailures.length,
2501
+ dispatchFailureNote: "Remote P2P dispatch attempts that failed. Affected tasks remain pending and may require mesh_queue_requeue if no idle session picks them up."
2502
+ } : {},
1791
2503
  ...view === "active" || statusFilter?.some((status) => ACTIVE_QUEUE_STATUSES.has(status)) ? {
1792
2504
  activeQueue: queue.filter((task) => ACTIVE_QUEUE_STATUSES.has(String(task?.status || "")))
1793
2505
  } : {},
@@ -1807,6 +2519,10 @@ async function meshQueueCancel(ctx, args) {
1807
2519
  if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
1808
2520
  const task = (0, import_daemon_core.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
1809
2521
  if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
2522
+ if (isLocalTransport(ctx.transport)) {
2523
+ ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2524
+ });
2525
+ }
1810
2526
  return JSON.stringify({ success: true, task }, null, 2);
1811
2527
  } catch (e) {
1812
2528
  return JSON.stringify({ success: false, error: e.message });
@@ -1837,10 +2553,60 @@ async function meshQueueRequeue(ctx, args) {
1837
2553
  }
1838
2554
  }
1839
2555
  async function meshSendTask(ctx, args) {
2556
+ const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
2557
+ const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
2558
+ if (!modeValidation.valid) {
2559
+ return JSON.stringify({
2560
+ success: false,
2561
+ code: "live_debug_readonly_guardrail_violation",
2562
+ taskMode: modeValidation.taskMode || requestedTaskMode,
2563
+ violations: modeValidation.violations,
2564
+ allowedOperations: modeValidation.allowedOperations,
2565
+ error: `live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`
2566
+ });
2567
+ }
2568
+ const taskMode = modeValidation.taskMode;
1840
2569
  const node = await findNodeWithRefresh(ctx, args.node_id);
1841
2570
  if (node.policy?.readOnly) {
1842
2571
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
1843
2572
  }
2573
+ let explicitTargetSession;
2574
+ if (args.session_id && isWorkerTaskMode(taskMode) && (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport))) {
2575
+ try {
2576
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2577
+ const sessions = extractStatusMetadataSessions(statusResult);
2578
+ explicitTargetSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2579
+ if (explicitTargetSession && isMeshCoordinatorSessionRecord(explicitTargetSession)) {
2580
+ return JSON.stringify({
2581
+ success: false,
2582
+ recoverable: true,
2583
+ code: "mesh_target_session_is_coordinator",
2584
+ reason: "mesh_target_session_is_coordinator",
2585
+ nodeId: args.node_id,
2586
+ sessionId: args.session_id,
2587
+ taskMode: taskMode || "unspecified",
2588
+ 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.`,
2589
+ 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.`
2590
+ });
2591
+ }
2592
+ if (explicitTargetSession && isUnmanagedSessionRecord(explicitTargetSession)) {
2593
+ return JSON.stringify({
2594
+ success: false,
2595
+ recoverable: true,
2596
+ code: "mesh_target_session_unmanaged",
2597
+ reason: "mesh_target_session_unmanaged",
2598
+ nodeId: args.node_id,
2599
+ sessionId: args.session_id,
2600
+ taskMode: taskMode || "unspecified",
2601
+ unsafeTranscriptAlias: true,
2602
+ 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.`,
2603
+ 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.`
2604
+ });
2605
+ }
2606
+ } catch {
2607
+ explicitTargetSession = void 0;
2608
+ }
2609
+ }
1844
2610
  const duplicate = hasRecentDuplicateDispatch(ctx, args);
1845
2611
  if (duplicate.duplicate) {
1846
2612
  return JSON.stringify({
@@ -1864,75 +2630,212 @@ async function meshSendTask(ctx, args) {
1864
2630
  const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
1865
2631
  meshId: ctx.mesh.id,
1866
2632
  message: args.message,
1867
- targetNodeId: args.node_id
2633
+ targetNodeId: args.node_id,
2634
+ ...taskMode ? { taskMode } : {}
1868
2635
  });
1869
2636
  return JSON.stringify(res);
1870
2637
  }
1871
2638
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
1872
2639
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
1873
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
2640
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id || ""));
2641
+ const taskId = (0, import_node_crypto.randomUUID)();
1874
2642
  const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
1875
2643
  session_id: args.session_id,
1876
2644
  message: args.message,
1877
- providerType: cached?.providerType
2645
+ providerType: cached?.providerType,
2646
+ verifiedSession: explicitTargetSession
1878
2647
  });
1879
2648
  if (result2.success) {
1880
2649
  const dispatchedSessionId = args.session_id || result2.sessionId;
2650
+ const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
1881
2651
  try {
2652
+ const providerType = result2.providerType || cached?.providerType;
1882
2653
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1883
2654
  kind: "task_dispatched",
1884
2655
  nodeId: args.node_id,
1885
2656
  sessionId: dispatchedSessionId,
1886
- payload: {
1887
- message: args.message,
1888
- via: "p2p_direct",
1889
- ...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
1890
- }
2657
+ providerType,
2658
+ payload: buildDirectTaskPayload(args.message, "p2p_direct", {
2659
+ taskId,
2660
+ taskMode,
2661
+ providerType,
2662
+ targetSessionId: dispatchedSessionId
2663
+ })
2664
+ });
2665
+ (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
2666
+ taskId,
2667
+ nodeId: args.node_id,
2668
+ sessionId: dispatchedSessionId,
2669
+ providerType: providerType || void 0,
2670
+ message: args.message,
2671
+ taskMode: taskMode || void 0,
2672
+ via: "p2p_direct",
2673
+ dispatchedAt
1891
2674
  });
1892
2675
  } catch {
1893
2676
  }
1894
2677
  }
1895
- return JSON.stringify({ ...result2, nodeId: args.node_id, dispatched: result2.success === true });
2678
+ return JSON.stringify({
2679
+ ...result2,
2680
+ nodeId: args.node_id,
2681
+ sessionId: result2.success ? args.session_id || result2.sessionId : args.session_id,
2682
+ ...result2.success ? { source: "direct", taskId } : {},
2683
+ taskMode,
2684
+ ...result2.success && result2.providerType ? { providerType: result2.providerType } : {},
2685
+ dispatched: result2.success === true
2686
+ });
1896
2687
  }
1897
2688
  if (args.session_id && isLocalTransport(ctx.transport)) {
1898
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2689
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
2690
+ let resolvedProviderType = cached?.providerType || "";
2691
+ if (!resolvedProviderType) {
2692
+ let explicitSession = explicitTargetSession;
2693
+ if (!explicitSession) {
2694
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2695
+ const sessions = extractStatusMetadataSessions(statusResult);
2696
+ explicitSession = sessions.find((session) => readSessionRecordId(session) === args.session_id);
2697
+ }
2698
+ if (!explicitSession) {
2699
+ return JSON.stringify({
2700
+ success: false,
2701
+ recoverable: true,
2702
+ code: "mesh_target_session_not_found",
2703
+ reason: "mesh_target_session_not_found",
2704
+ transport: "local_ipc",
2705
+ retryRecommended: true,
2706
+ nodeId: args.node_id,
2707
+ sessionId: args.session_id,
2708
+ error: `Local session '${args.session_id}' is not present in live status for node '${args.node_id}'.`,
2709
+ 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.`
2710
+ });
2711
+ }
2712
+ if (isMeshCoordinatorSessionRecord(explicitSession)) {
2713
+ return JSON.stringify({
2714
+ success: false,
2715
+ recoverable: true,
2716
+ code: "mesh_target_session_is_coordinator",
2717
+ reason: "mesh_target_session_is_coordinator",
2718
+ nodeId: args.node_id,
2719
+ sessionId: args.session_id,
2720
+ taskMode: taskMode || "unspecified",
2721
+ 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.`,
2722
+ 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.`
2723
+ });
2724
+ }
2725
+ if (isUnmanagedSessionRecord(explicitSession)) {
2726
+ return JSON.stringify({
2727
+ success: false,
2728
+ recoverable: true,
2729
+ code: "mesh_target_session_unmanaged",
2730
+ reason: "mesh_target_session_unmanaged",
2731
+ nodeId: args.node_id,
2732
+ sessionId: args.session_id,
2733
+ taskMode: taskMode || "unspecified",
2734
+ unsafeTranscriptAlias: true,
2735
+ unsafeDelegateTarget: true,
2736
+ 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.`,
2737
+ 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.`
2738
+ });
2739
+ }
2740
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
2741
+ if (resolvedProviderType) {
2742
+ meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
2743
+ providerType: resolvedProviderType,
2744
+ providerSessionId: readString(explicitSession?.providerSessionId) || void 0,
2745
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2746
+ });
2747
+ }
2748
+ }
2749
+ if (!resolvedProviderType) {
2750
+ return JSON.stringify({
2751
+ success: false,
2752
+ recoverable: true,
2753
+ code: "mesh_target_session_provider_unknown",
2754
+ reason: "mesh_target_session_provider_unknown",
2755
+ transport: "local_ipc",
2756
+ retryRecommended: false,
2757
+ nodeId: args.node_id,
2758
+ sessionId: args.session_id,
2759
+ error: `Local session '${args.session_id}' is live but does not expose providerType/cliType, so agent_command cannot be routed safely.`,
2760
+ 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.`
2761
+ });
2762
+ }
2763
+ const sessionWasIdle = explicitTargetSession ? isIdleSessionRecord(explicitTargetSession) : false;
1899
2764
  const dispatchResult = await commandForNode(ctx, node, "agent_command", {
1900
2765
  targetSessionId: args.session_id,
1901
- ...cached?.providerType ? { agentType: cached.providerType, cliType: cached.providerType, providerType: cached.providerType } : {},
2766
+ agentType: resolvedProviderType,
2767
+ cliType: resolvedProviderType,
2768
+ providerType: resolvedProviderType,
1902
2769
  action: "send_chat",
1903
2770
  message: args.message
1904
2771
  });
1905
2772
  const dispatchPayload = unwrapCommandPayload(dispatchResult);
1906
2773
  if (dispatchPayload?.success === false || dispatchResult?.success === false) {
2774
+ const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
1907
2775
  return JSON.stringify({
2776
+ ...source && typeof source === "object" ? source : {},
1908
2777
  success: false,
1909
2778
  nodeId: args.node_id,
1910
2779
  sessionId: args.session_id,
1911
2780
  error: dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"
1912
2781
  });
1913
2782
  }
2783
+ const taskId = (0, import_node_crypto.randomUUID)();
2784
+ const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
1914
2785
  try {
1915
2786
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1916
2787
  kind: "task_dispatched",
1917
2788
  nodeId: args.node_id,
1918
2789
  sessionId: args.session_id,
1919
- providerType: cached?.providerType,
1920
- payload: { message: args.message, via: "local_direct" }
2790
+ providerType: resolvedProviderType,
2791
+ payload: buildDirectTaskPayload(args.message, "local_direct", {
2792
+ taskId,
2793
+ taskMode,
2794
+ providerType: resolvedProviderType,
2795
+ targetSessionId: args.session_id,
2796
+ dispatchedToIdleSession: sessionWasIdle
2797
+ })
1921
2798
  });
1922
2799
  } catch {
1923
2800
  }
1924
- return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
2801
+ (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
2802
+ taskId,
2803
+ nodeId: args.node_id,
2804
+ sessionId: args.session_id,
2805
+ providerType: resolvedProviderType || void 0,
2806
+ message: args.message,
2807
+ taskMode: taskMode || void 0,
2808
+ via: "local_direct",
2809
+ dispatchedToIdleSession: sessionWasIdle,
2810
+ dispatchedAt
2811
+ });
2812
+ return JSON.stringify({
2813
+ success: true,
2814
+ dispatched: true,
2815
+ source: "direct",
2816
+ taskId,
2817
+ taskMode,
2818
+ providerType: resolvedProviderType,
2819
+ nodeId: args.node_id,
2820
+ sessionId: args.session_id,
2821
+ ...sessionWasIdle ? {
2822
+ dispatchAcknowledgementRisk: true,
2823
+ dispatchAcknowledgementRiskReason: "session_was_idle_at_dispatch",
2824
+ 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.`
2825
+ } : {}
2826
+ });
1925
2827
  }
1926
2828
  const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
1927
2829
  targetNodeId: args.node_id,
1928
- targetSessionId: args.session_id
2830
+ targetSessionId: args.session_id,
2831
+ taskMode
1929
2832
  });
1930
2833
  if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
1931
2834
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
1932
2835
  });
1933
2836
  }
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 };
2837
+ const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId) : [];
2838
+ const result = { success: true, source: "queue", nodeId: args.node_id, taskId: task.id, status: task.status, taskMode: task.taskMode };
1936
2839
  if (pendingEvents.length > 0) {
1937
2840
  result.pendingCoordinatorEvents = pendingEvents;
1938
2841
  }
@@ -1956,7 +2859,7 @@ async function meshReadChat(ctx, args) {
1956
2859
  await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
1957
2860
  }
1958
2861
  if (isLocalTransport(ctx.transport)) {
1959
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2862
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
1960
2863
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
1961
2864
  const result = await commandForNode(ctx, node, "read_chat", {
1962
2865
  sessionId: args.session_id,
@@ -1964,18 +2867,19 @@ async function meshReadChat(ctx, args) {
1964
2867
  workspace: node.workspace,
1965
2868
  ...cached?.providerType ? { agentType: cached.providerType, providerType: cached.providerType } : {},
1966
2869
  ...providerSessionId ? { providerSessionId } : {},
1967
- tailLimit: args.tail ?? 10
2870
+ tailLimit: args.tail ?? 3
1968
2871
  });
1969
2872
  const payload = annotateRapidReadChatAdvisory(unwrapCommandPayload(result), {
1970
2873
  key: `mesh:${args.node_id}:${args.session_id}`,
1971
2874
  toolName: "mesh_read_chat",
1972
2875
  completionCallbackExpected: true
1973
2876
  });
1974
- if (args.compact) {
2877
+ const useCompact = args.compact !== false;
2878
+ if (useCompact) {
1975
2879
  const compactPayload = compactChatPayload(payload, {
1976
2880
  nodeId: args.node_id,
1977
2881
  sessionId: args.session_id,
1978
- limit: args.tail ?? 10
2882
+ limit: args.tail ?? 3
1979
2883
  });
1980
2884
  return JSON.stringify(
1981
2885
  payload.pollingAdvisory ? { ...compactPayload, pollingAdvisory: payload.pollingAdvisory } : compactPayload,
@@ -1988,7 +2892,7 @@ async function meshReadChat(ctx, args) {
1988
2892
  try {
1989
2893
  const targetId = `${node.daemonId}:session:${args.session_id}`;
1990
2894
  const res = await ctx.transport.readChat(targetId, {
1991
- limit: args.tail ?? 10,
2895
+ limit: args.tail ?? 3,
1992
2896
  sessionId: args.session_id
1993
2897
  });
1994
2898
  return JSON.stringify(res, null, 2);
@@ -2002,7 +2906,7 @@ async function meshReadChat(ctx, args) {
2002
2906
  async function meshReadDebug(ctx, args) {
2003
2907
  const node = await findNodeWithRefresh(ctx, args.node_id);
2004
2908
  if (isLocalTransport(ctx.transport)) {
2005
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2909
+ const cached = resolveMeshSessionProviderMetadata(ctx, args.node_id, args.session_id);
2006
2910
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
2007
2911
  const delivery = args.delivery === "inline" ? void 0 : "daemon_file";
2008
2912
  const result = await commandForNode(ctx, node, "get_chat_debug_bundle", {
@@ -2033,6 +2937,8 @@ async function meshReadDebug(ctx, args) {
2033
2937
  }
2034
2938
  async function meshLaunchSession(ctx, args) {
2035
2939
  const node = await findNodeWithRefresh(ctx, args.node_id);
2940
+ const bootstrapBlock = getWorktreeBootstrapLaunchBlock(node);
2941
+ if (bootstrapBlock) return JSON.stringify(bootstrapBlock, null, 2);
2036
2942
  if (isLocalTransport(ctx.transport)) {
2037
2943
  let resolvedProviderType = typeof args.type === "string" && args.type.trim() ? args.type : "";
2038
2944
  if (!resolvedProviderType) {
@@ -2057,6 +2963,10 @@ async function meshLaunchSession(ctx, args) {
2057
2963
  const coordinatorNode = resolveCoordinatorNode(ctx);
2058
2964
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
2059
2965
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
2966
+ const isLocalNode = isLocalControlPlaneNode(ctx, node);
2967
+ if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
2968
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
2969
+ }
2060
2970
  let result;
2061
2971
  try {
2062
2972
  result = await commandForNode(ctx, node, "launch_cli", {
@@ -2084,7 +2994,8 @@ async function meshLaunchSession(ctx, args) {
2084
2994
  if (runtimeSessionId) {
2085
2995
  meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, runtimeSessionId), {
2086
2996
  providerType: resolvedProviderType,
2087
- ...providerSessionId ? { providerSessionId } : {}
2997
+ ...providerSessionId ? { providerSessionId } : {},
2998
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2088
2999
  });
2089
3000
  }
2090
3001
  try {
@@ -2097,7 +3008,6 @@ async function meshLaunchSession(ctx, args) {
2097
3008
  });
2098
3009
  } catch {
2099
3010
  }
2100
- const isLocalNode = isLocalControlPlaneNode(ctx, node);
2101
3011
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
2102
3012
  ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2103
3013
  });
@@ -2122,6 +3032,9 @@ async function meshLaunchSession(ctx, args) {
2122
3032
  const coordinatorNode = resolveCoordinatorNode(ctx);
2123
3033
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
2124
3034
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
3035
+ if (!coordinatorDaemonId) {
3036
+ return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
3037
+ }
2125
3038
  try {
2126
3039
  const res = await ctx.transport.launch(node.daemonId, {
2127
3040
  type: resolvedProviderType,
@@ -2202,6 +3115,51 @@ async function meshGitStatus(ctx, args) {
2202
3115
  }, null, 2);
2203
3116
  }
2204
3117
  }
3118
+ async function meshFastForwardNode(ctx, args) {
3119
+ await refreshMeshFromDaemon(ctx);
3120
+ const node = await findNodeWithRefresh(ctx, args.node_id);
3121
+ const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
3122
+ if (node.policy?.readOnly) {
3123
+ return JSON.stringify({
3124
+ success: false,
3125
+ code: "node_read_only",
3126
+ nodeId: args.node_id,
3127
+ workspace: node.workspace,
3128
+ allowed: false,
3129
+ willRun: false,
3130
+ executed: false,
3131
+ blockingReasons: ["node_read_only"]
3132
+ }, null, 2);
3133
+ }
3134
+ try {
3135
+ const dryRun = args.dry_run === true || args.execute !== true;
3136
+ const result = await commandForNode(ctx, node, "fast_forward_mesh_node", {
3137
+ meshId: ctx.mesh.id,
3138
+ nodeId: node.id,
3139
+ workspace: node.workspace,
3140
+ branch: typeof args.branch === "string" ? args.branch : void 0,
3141
+ execute: args.execute === true && args.dry_run !== true,
3142
+ dryRun,
3143
+ updateSubmodules: args.update_submodules === true,
3144
+ submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
3145
+ });
3146
+ return JSON.stringify(unwrapCommandPayload(result), null, 2);
3147
+ } catch (e) {
3148
+ const failure = buildCoordinatorP2pRelayFailure(e, {
3149
+ command: "fast_forward_mesh_node",
3150
+ targetDaemonId: node.daemonId,
3151
+ nodeId: args.node_id
3152
+ });
3153
+ return JSON.stringify({
3154
+ ...failure,
3155
+ workspace: node.workspace,
3156
+ allowed: false,
3157
+ willRun: false,
3158
+ executed: false,
3159
+ blockingReasons: [failure.code || "mesh_fast_forward_unavailable"]
3160
+ }, null, 2);
3161
+ }
3162
+ }
2205
3163
  async function meshCheckpoint(ctx, args) {
2206
3164
  const node = await findNodeWithRefresh(ctx, args.node_id);
2207
3165
  if (node.policy?.readOnly) {
@@ -2217,7 +3175,13 @@ async function meshCheckpoint(ctx, args) {
2217
3175
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2218
3176
  kind: "checkpoint_created",
2219
3177
  nodeId: args.node_id,
2220
- payload: { message: args.message, commit: result?.checkpoint?.commit }
3178
+ payload: {
3179
+ message: args.message,
3180
+ commit: result?.checkpoint?.commit,
3181
+ outcome: result?.checkpoint?.status || (result?.checkpoint?.noop ? "skipped" : void 0),
3182
+ noop: result?.checkpoint?.noop === true,
3183
+ reason: result?.checkpoint?.reason
3184
+ }
2221
3185
  });
2222
3186
  } catch {
2223
3187
  }
@@ -2233,7 +3197,13 @@ async function meshCheckpoint(ctx, args) {
2233
3197
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2234
3198
  kind: "checkpoint_created",
2235
3199
  nodeId: args.node_id,
2236
- payload: { message: args.message, commit: res?.checkpoint?.commit }
3200
+ payload: {
3201
+ message: args.message,
3202
+ commit: res?.checkpoint?.commit,
3203
+ outcome: res?.checkpoint?.status || (res?.checkpoint?.noop ? "skipped" : void 0),
3204
+ noop: res?.checkpoint?.noop === true,
3205
+ reason: res?.checkpoint?.reason
3206
+ }
2237
3207
  });
2238
3208
  } catch {
2239
3209
  }
@@ -2248,7 +3218,7 @@ async function meshCheckpoint(ctx, args) {
2248
3218
  async function meshApprove(ctx, args) {
2249
3219
  const node = await findNodeWithRefresh(ctx, args.node_id);
2250
3220
  if (isLocalTransport(ctx.transport)) {
2251
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
3221
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
2252
3222
  const providerSessionId = cached?.providerSessionId;
2253
3223
  const result = await commandForNode(ctx, node, "resolve_action", {
2254
3224
  sessionId: args.session_id,
@@ -2401,6 +3371,43 @@ async function meshRemoveNode(ctx, args) {
2401
3371
  return JSON.stringify({ error: "Cloud mesh remove_node requires node daemonId" });
2402
3372
  }
2403
3373
  }
3374
+ function resolveRefineConfigNode(ctx, nodeId) {
3375
+ if (nodeId) return findNode(ctx.mesh, nodeId);
3376
+ const node = ctx.mesh.nodes.find((entry) => !!entry.workspace);
3377
+ if (!node) throw new Error("No mesh node with a workspace is available");
3378
+ return node;
3379
+ }
3380
+ async function meshRefineConfigSchema(ctx) {
3381
+ const node = resolveRefineConfigNode(ctx);
3382
+ const result = await commandForNode(ctx, node, "get_mesh_refine_config_schema", {});
3383
+ return JSON.stringify(result, null, 2);
3384
+ }
3385
+ async function meshValidateRefineConfig(ctx, args) {
3386
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3387
+ const result = await commandForNode(ctx, node, "validate_mesh_refine_config", {
3388
+ workspace: node.workspace,
3389
+ inlineMesh: ctx.mesh,
3390
+ ...args.config ? { config: args.config } : {}
3391
+ });
3392
+ return JSON.stringify(result, null, 2);
3393
+ }
3394
+ async function meshSuggestRefineConfig(ctx, args) {
3395
+ const node = resolveRefineConfigNode(ctx, args.node_id);
3396
+ const result = await commandForNode(ctx, node, "suggest_mesh_refine_config", {
3397
+ workspace: node.workspace,
3398
+ inlineMesh: ctx.mesh
3399
+ });
3400
+ return JSON.stringify(result, null, 2);
3401
+ }
3402
+ async function meshRefinePlan(ctx, args) {
3403
+ const node = await findNodeWithRefresh(ctx, args.node_id);
3404
+ const result = await commandForNode(ctx, node, "plan_mesh_refine_node", {
3405
+ meshId: ctx.mesh.id,
3406
+ nodeId: args.node_id,
3407
+ inlineMesh: ctx.mesh
3408
+ });
3409
+ return JSON.stringify(result, null, 2);
3410
+ }
2404
3411
  async function meshRefineNode(ctx, args) {
2405
3412
  const node = await findNodeWithRefresh(ctx, args.node_id);
2406
3413
  if (isLocalTransport(ctx.transport)) {
@@ -2409,7 +3416,7 @@ async function meshRefineNode(ctx, args) {
2409
3416
  nodeId: args.node_id,
2410
3417
  inlineMesh: ctx.mesh
2411
3418
  });
2412
- if (result?.success && result.removeResult?.removed !== false) {
3419
+ if (result?.success && result.async !== true && result.removeResult?.removed !== false) {
2413
3420
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2414
3421
  if (idx >= 0) {
2415
3422
  ctx.mesh.nodes.splice(idx, 1);
@@ -2424,7 +3431,7 @@ async function meshRefineNode(ctx, args) {
2424
3431
  nodeId: args.node_id,
2425
3432
  inlineMesh: ctx.mesh
2426
3433
  });
2427
- if (res?.success && res.removeResult?.removed !== false) {
3434
+ if (res?.success && res.async !== true && res.removeResult?.removed !== false) {
2428
3435
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
2429
3436
  if (idx >= 0) {
2430
3437
  ctx.mesh.nodes.splice(idx, 1);
@@ -2461,13 +3468,13 @@ var STANDARD_TOOLS = [
2461
3468
  function buildMcpHelpText() {
2462
3469
  const meshTools = ALL_MESH_TOOLS.map((tool) => tool.name);
2463
3470
  return `
2464
- adhdev-mcp \u2014 ADHDev MCP Server
3471
+ ADHDev MCP Server
2465
3472
 
2466
3473
  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)
3474
+ adhdev mcp Local mode (requires standalone daemon)
3475
+ adhdev mcp --api-key <key> Cloud mode (ADHDev cloud API)
3476
+ adhdev mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
3477
+ adhdev-mcp --help Compatibility bin (same server, legacy package entrypoint)
2471
3478
 
2472
3479
  Options:
2473
3480
  --mode <mode> Transport: local, cloud, or ipc
@@ -2492,6 +3499,7 @@ Mesh tools: ${meshTools.join(", ")}
2492
3499
  // src/server.ts
2493
3500
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
2494
3501
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
3502
+ var import_node_os = __toESM(require("os"));
2495
3503
  var import_types = require("@modelcontextprotocol/sdk/types.js");
2496
3504
 
2497
3505
  // src/transports/local.ts
@@ -4025,6 +5033,7 @@ async function startMcpServer(opts) {
4025
5033
  requirePreTaskCheckpoint: false,
4026
5034
  requirePostTaskCheckpoint: true,
4027
5035
  requireApprovalForPush: true,
5036
+ allowAutoPublishSubmoduleMainCommits: false,
4028
5037
  requireApprovalForDestructiveGit: true,
4029
5038
  dirtyWorkspaceBehavior: "warn",
4030
5039
  maxParallelTasks: 2,
@@ -4081,11 +5090,13 @@ async function startMcpServer(opts) {
4081
5090
  }
4082
5091
  let localDaemonId;
4083
5092
  let localMachineId;
5093
+ let coordinatorHostname = import_node_os.default.hostname();
4084
5094
  if (transport instanceof LocalTransport || transport instanceof IpcTransport) {
4085
5095
  try {
4086
5096
  const { loadConfig } = await import("@adhdev/daemon-core");
4087
5097
  const cfg = loadConfig();
4088
- if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
5098
+ if (cfg.machineId) localMachineId = cfg.machineId;
5099
+ else if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
4089
5100
  } catch {
4090
5101
  }
4091
5102
  }
@@ -4093,14 +5104,16 @@ async function startMcpServer(opts) {
4093
5104
  try {
4094
5105
  const statusResult = await transport.getStatus();
4095
5106
  const instanceId = typeof statusResult?.status?.instanceId === "string" ? statusResult.status.instanceId.trim() : "";
5107
+ const hostname = typeof statusResult?.status?.hostname === "string" ? statusResult.status.hostname.trim() : typeof statusResult?.status?.machine?.hostname === "string" ? statusResult.status.machine.hostname.trim() : "";
4096
5108
  if (instanceId) localDaemonId = instanceId;
5109
+ if (hostname) coordinatorHostname = hostname;
4097
5110
  } catch {
4098
5111
  }
4099
5112
  }
4100
- const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {} };
5113
+ const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {}, ...coordinatorHostname ? { coordinatorHostname } : {} };
4101
5114
  const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
4102
5115
  const server2 = new import_server.Server(
4103
- { name: "adhdev-mcp-server", version: "0.9.81" },
5116
+ { name: "adhdev-mcp-server", version: "0.9.82" },
4104
5117
  { capabilities: { tools: {}, resources: {} } }
4105
5118
  );
4106
5119
  const { ListResourcesRequestSchema, ReadResourceRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
@@ -4126,7 +5139,7 @@ async function startMcpServer(opts) {
4126
5139
  let text;
4127
5140
  switch (name) {
4128
5141
  case "mesh_status":
4129
- text = await meshStatus(meshCtx);
5142
+ text = await meshStatus(meshCtx, a);
4130
5143
  break;
4131
5144
  case "mesh_list_nodes":
4132
5145
  text = await meshListNodes(meshCtx);
@@ -4158,6 +5171,9 @@ async function startMcpServer(opts) {
4158
5171
  case "mesh_git_status":
4159
5172
  text = await meshGitStatus(meshCtx, a);
4160
5173
  break;
5174
+ case "mesh_fast_forward_node":
5175
+ text = await meshFastForwardNode(meshCtx, a);
5176
+ break;
4161
5177
  case "mesh_checkpoint":
4162
5178
  text = await meshCheckpoint(meshCtx, a);
4163
5179
  break;
@@ -4173,6 +5189,18 @@ async function startMcpServer(opts) {
4173
5189
  case "mesh_refine_node":
4174
5190
  text = await meshRefineNode(meshCtx, a);
4175
5191
  break;
5192
+ case "mesh_refine_config_schema":
5193
+ text = await meshRefineConfigSchema(meshCtx);
5194
+ break;
5195
+ case "mesh_validate_refine_config":
5196
+ text = await meshValidateRefineConfig(meshCtx, a);
5197
+ break;
5198
+ case "mesh_suggest_refine_config":
5199
+ text = await meshSuggestRefineConfig(meshCtx, a);
5200
+ break;
5201
+ case "mesh_refine_plan":
5202
+ text = await meshRefinePlan(meshCtx, a);
5203
+ break;
4176
5204
  case "mesh_cleanup_sessions":
4177
5205
  text = await meshCleanupSessions(meshCtx, a);
4178
5206
  break;