@adhdev/daemon-standalone 0.9.77-rc.4 → 0.9.77-rc.41

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.
@@ -25120,6 +25120,7 @@ __export(dist_exports, {
25120
25120
  buildThoughtChatMessage: () => buildThoughtChatMessage,
25121
25121
  buildToolChatMessage: () => buildToolChatMessage,
25122
25122
  buildUserChatMessage: () => buildUserChatMessage,
25123
+ cancelTask: () => cancelTask,
25123
25124
  claimNextTask: () => claimNextTask,
25124
25125
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
25125
25126
  classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush,
@@ -25163,6 +25164,7 @@ __export(dist_exports, {
25163
25164
  getLogLevel: () => getLogLevel,
25164
25165
  getMesh: () => getMesh,
25165
25166
  getMeshByRepo: () => getMeshByRepo,
25167
+ getMeshQueueStats: () => getMeshQueueStats,
25166
25168
  getNpmExecOptions: () => getNpmExecOptions,
25167
25169
  getQueue: () => getQueue,
25168
25170
  getRecentActivity: () => getRecentActivity,
@@ -25231,6 +25233,7 @@ __export(dist_exports, {
25231
25233
  registerExtensionProviders: () => registerExtensionProviders,
25232
25234
  removeNode: () => removeNode,
25233
25235
  removeWorktree: () => removeWorktree,
25236
+ requeueTask: () => requeueTask,
25234
25237
  resetConfig: () => resetConfig,
25235
25238
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
25236
25239
  resetState: () => resetState,
@@ -26029,6 +26032,7 @@ function enqueueTask(meshId, message, opts) {
26029
26032
  message,
26030
26033
  status: "pending",
26031
26034
  targetNodeId: opts?.targetNodeId,
26035
+ targetSessionId: opts?.targetSessionId,
26032
26036
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
26033
26037
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
26034
26038
  };
@@ -26046,9 +26050,14 @@ function getQueue(meshId, opts) {
26046
26050
  }
26047
26051
  function claimNextTask(meshId, nodeId, sessionId) {
26048
26052
  const queue = readQueue(meshId);
26049
- let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId);
26053
+ const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
26054
+ if (hasActiveAssignment) return null;
26055
+ let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
26050
26056
  if (targetIdx === -1) {
26051
- targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId);
26057
+ targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
26058
+ }
26059
+ if (targetIdx === -1) {
26060
+ targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
26052
26061
  }
26053
26062
  if (targetIdx === -1) return null;
26054
26063
  const entry = queue[targetIdx];
@@ -26068,6 +26077,40 @@ function updateTaskStatus(meshId, taskId, status) {
26068
26077
  writeQueue(meshId, queue);
26069
26078
  return queue[idx];
26070
26079
  }
26080
+ function cancelTask(meshId, taskId, opts) {
26081
+ const queue = readQueue(meshId);
26082
+ const idx = queue.findIndex((q) => q.id === taskId);
26083
+ if (idx === -1) return null;
26084
+ const now = (/* @__PURE__ */ new Date()).toISOString();
26085
+ queue[idx].status = "cancelled";
26086
+ queue[idx].updatedAt = now;
26087
+ queue[idx].cancelledAt = now;
26088
+ if (opts?.reason) queue[idx].cancelReason = opts.reason;
26089
+ writeQueue(meshId, queue);
26090
+ return queue[idx];
26091
+ }
26092
+ function requeueTask(meshId, taskId, opts) {
26093
+ const queue = readQueue(meshId);
26094
+ const idx = queue.findIndex((q) => q.id === taskId);
26095
+ if (idx === -1) return null;
26096
+ const entry = queue[idx];
26097
+ const now = (/* @__PURE__ */ new Date()).toISOString();
26098
+ entry.status = "pending";
26099
+ delete entry.assignedNodeId;
26100
+ delete entry.assignedSessionId;
26101
+ delete entry.cancelledAt;
26102
+ delete entry.cancelReason;
26103
+ if (opts?.clearTargetNode) delete entry.targetNodeId;
26104
+ if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
26105
+ if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
26106
+ if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
26107
+ entry.updatedAt = now;
26108
+ entry.requeuedAt = now;
26109
+ entry.requeueCount = (entry.requeueCount || 0) + 1;
26110
+ if (opts?.reason) entry.requeueReason = opts.reason;
26111
+ writeQueue(meshId, queue);
26112
+ return entry;
26113
+ }
26071
26114
  function updateSessionTaskStatus(meshId, sessionId, status) {
26072
26115
  const queue = readQueue(meshId);
26073
26116
  for (let i = queue.length - 1; i >= 0; i--) {
@@ -26086,7 +26129,14 @@ function getMeshQueueStats(meshId) {
26086
26129
  pending: queue.filter((q) => q.status === "pending").length,
26087
26130
  assigned: queue.filter((q) => q.status === "assigned").length,
26088
26131
  completed: queue.filter((q) => q.status === "completed").length,
26089
- failed: queue.filter((q) => q.status === "failed").length
26132
+ failed: queue.filter((q) => q.status === "failed").length,
26133
+ cancelled: queue.filter((q) => q.status === "cancelled").length,
26134
+ activeAssignments: queue.filter((q) => q.status === "assigned").map((q) => ({
26135
+ id: q.id,
26136
+ nodeId: q.assignedNodeId,
26137
+ sessionId: q.assignedSessionId,
26138
+ message: q.message
26139
+ }))
26090
26140
  };
26091
26141
  }
26092
26142
  function setLogLevel(level) {
@@ -26245,6 +26295,9 @@ function drainPendingMeshCoordinatorEvents() {
26245
26295
  function readNonEmptyString(value) {
26246
26296
  return typeof value === "string" && value.trim() ? value.trim() : "";
26247
26297
  }
26298
+ function resolveEventSessionId(event, fallback) {
26299
+ return readNonEmptyString(event.targetSessionId) || readNonEmptyString(event.sessionId) || readNonEmptyString(event.instanceId) || readNonEmptyString(fallback);
26300
+ }
26248
26301
  function isMeshCoordinatorEvent(eventName) {
26249
26302
  return typeof eventName === "string" && MESH_COORDINATOR_EVENTS.has(eventName);
26250
26303
  }
@@ -26256,38 +26309,74 @@ function formatCompletionMetadata(event) {
26256
26309
  ].filter(Boolean);
26257
26310
  return parts.length > 0 ? ` (${parts.join("; ")})` : "";
26258
26311
  }
26312
+ function getMeshWithCache(components, meshId) {
26313
+ const localMesh = getMesh(meshId);
26314
+ if (localMesh) return localMesh;
26315
+ return components.router?.getCachedInlineMesh(meshId);
26316
+ }
26259
26317
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
26260
26318
  const task = claimNextTask(meshId, nodeId, sessionId);
26261
- if (!task) return false;
26319
+ if (!task) {
26320
+ return false;
26321
+ }
26262
26322
  LOG.info("MeshQueue", `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
26323
+ const mesh = getMeshWithCache(components, meshId);
26324
+ const node = mesh?.nodes.find((n) => n.id === nodeId);
26325
+ if (node?.daemonId && components.dispatchMeshCommand) {
26326
+ const isLocalNode = components.cliManager.adapters.has(sessionId);
26327
+ if (!isLocalNode) {
26328
+ components.dispatchMeshCommand(node.daemonId, "agent_command", {
26329
+ targetSessionId: sessionId,
26330
+ cliType: providerType,
26331
+ action: "send_chat",
26332
+ message: task.message
26333
+ }).catch((e) => {
26334
+ LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
26335
+ updateTaskStatus(meshId, task.id, "failed");
26336
+ });
26337
+ return true;
26338
+ }
26339
+ }
26263
26340
  components.cliManager.handleCliCommand("agent_command", {
26264
26341
  targetSessionId: sessionId,
26265
26342
  cliType: providerType,
26266
26343
  action: "send_chat",
26267
- input: task.message
26344
+ message: task.message
26268
26345
  }).catch((e) => {
26269
- LOG.error("MeshQueue", `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
26346
+ LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
26347
+ updateTaskStatus(meshId, task.id, "failed");
26270
26348
  });
26271
26349
  return true;
26272
26350
  }
26273
26351
  function triggerMeshQueue(components, meshId) {
26274
- const mesh = getMesh(meshId);
26352
+ const mesh = getMeshWithCache(components, meshId);
26275
26353
  if (!mesh) return;
26276
26354
  const cliInstances = components.instanceManager.getByCategory("cli");
26277
26355
  for (const inst of cliInstances) {
26278
26356
  const state = inst.getState();
26279
26357
  const settings = state.settings || {};
26280
26358
  const instMeshId = readNonEmptyString(settings.meshNodeFor);
26281
- if (instMeshId !== meshId && !settings.launchedByCoordinator) continue;
26359
+ if (instMeshId !== meshId) continue;
26282
26360
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
26283
26361
  if (!nodeId) continue;
26284
- if (state.status !== "idle" && state.status !== "stopped" && state.activeChat?.status !== "waiting_input") continue;
26362
+ const status = readNonEmptyString(state.status).toLowerCase();
26363
+ if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
26364
+ if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
26285
26365
  const sessionId = state.instanceId;
26286
26366
  const providerType = state.type || readNonEmptyString(settings.providerType);
26287
26367
  if (providerType) {
26288
26368
  tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
26289
26369
  }
26290
26370
  }
26371
+ for (const [key, idle] of remoteIdleSessions.entries()) {
26372
+ const node = mesh.nodes.find((n) => n.id === idle.nodeId);
26373
+ if (node) {
26374
+ const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
26375
+ if (assigned) {
26376
+ remoteIdleSessions.delete(key);
26377
+ }
26378
+ }
26379
+ }
26291
26380
  }
26292
26381
  function buildMeshSystemMessage(args) {
26293
26382
  const metadata = formatCompletionMetadata(args.metadataEvent);
@@ -26334,20 +26423,67 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
26334
26423
  return "";
26335
26424
  }
26336
26425
  function injectMeshSystemMessage(components, args) {
26426
+ let completedTaskForLedger = null;
26337
26427
  if (args.event === "agent:generating_completed") {
26338
- const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
26339
- const nodeId = readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
26428
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
26429
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
26340
26430
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
26341
26431
  if (sessionId) {
26342
- updateSessionTaskStatus(args.meshId, sessionId, "completed");
26432
+ const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed");
26433
+ completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
26343
26434
  if (nodeId && providerType) {
26344
26435
  setTimeout(() => {
26345
26436
  tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
26346
26437
  }, 500);
26347
26438
  }
26348
26439
  }
26440
+ } else if (args.event === "agent:ready") {
26441
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
26442
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
26443
+ const providerType = readNonEmptyString(args.metadataEvent.providerType);
26444
+ const completedTask = sessionId ? updateSessionTaskStatus(args.meshId, sessionId, "completed") : null;
26445
+ if (completedTask) {
26446
+ completedTaskForLedger = { id: completedTask.id };
26447
+ try {
26448
+ appendLedgerEntry(args.meshId, {
26449
+ kind: "task_completed",
26450
+ nodeId: nodeId || void 0,
26451
+ sessionId,
26452
+ providerType: providerType || void 0,
26453
+ payload: {
26454
+ event: args.event,
26455
+ nodeLabel: args.nodeLabel,
26456
+ taskId: completedTask.id,
26457
+ completedViaReady: true,
26458
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
26459
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
26460
+ }
26461
+ });
26462
+ } catch (e) {
26463
+ LOG.warn("MeshLedger", `Failed to record task_completed from ready: ${e?.message || e}`);
26464
+ }
26465
+ }
26466
+ if (sessionId && nodeId && providerType) {
26467
+ remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
26468
+ setTimeout(() => {
26469
+ const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
26470
+ if (assigned) {
26471
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
26472
+ }
26473
+ }, 500);
26474
+ }
26475
+ } else if (args.event === "agent:generating_started") {
26476
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
26477
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
26478
+ if (sessionId && nodeId) {
26479
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
26480
+ }
26349
26481
  } else if (args.event === "agent:stopped") {
26350
- const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
26482
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
26483
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
26484
+ if (sessionId && nodeId) {
26485
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
26486
+ }
26351
26487
  if (sessionId) {
26352
26488
  updateSessionTaskStatus(args.meshId, sessionId, "failed");
26353
26489
  }
@@ -26357,13 +26493,15 @@ function injectMeshSystemMessage(components, args) {
26357
26493
  try {
26358
26494
  appendLedgerEntry(args.meshId, {
26359
26495
  kind: ledgerKind,
26360
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
26361
- sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
26496
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
26497
+ sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || void 0,
26362
26498
  providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
26363
26499
  payload: {
26364
26500
  event: args.event,
26365
26501
  nodeLabel: args.nodeLabel,
26366
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
26502
+ taskId: completedTaskForLedger?.id || void 0,
26503
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
26504
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
26367
26505
  }
26368
26506
  });
26369
26507
  } catch (e) {
@@ -26376,8 +26514,8 @@ function injectMeshSystemMessage(components, args) {
26376
26514
  const mesh = getMesh(args.meshId);
26377
26515
  const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
26378
26516
  recoveryContext = getSessionRecoveryContext(args.meshId, {
26379
- sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
26380
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
26517
+ sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || void 0,
26518
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
26381
26519
  maxRetries
26382
26520
  });
26383
26521
  recoveryContext.failedProviderType = readNonEmptyString(args.metadataEvent.providerType) || null;
@@ -26472,12 +26610,14 @@ function handleMeshForwardEvent(components, payload) {
26472
26610
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
26473
26611
  return injectMeshSystemMessage(components, {
26474
26612
  meshId,
26613
+ nodeId,
26475
26614
  nodeLabel,
26476
26615
  event: eventName,
26477
26616
  metadataEvent: {
26478
- targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
26617
+ targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
26479
26618
  providerType: readNonEmptyString(payload.providerType),
26480
- providerSessionId: readNonEmptyString(payload.providerSessionId)
26619
+ providerSessionId: readNonEmptyString(payload.providerSessionId),
26620
+ finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary)
26481
26621
  }
26482
26622
  });
26483
26623
  }
@@ -26496,15 +26636,17 @@ function setupMeshEventForwarding(components) {
26496
26636
  const meshIdFromRuntime = readNonEmptyString(settings.meshNodeFor);
26497
26637
  const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
26498
26638
  if (!isMeshDelegate) return;
26499
- const mesh = meshIdFromRuntime ? getMesh(meshIdFromRuntime) : getMeshByRepo(workspace);
26639
+ const mesh = meshIdFromRuntime ? getMeshWithCache(components, meshIdFromRuntime) : getMeshByRepo(workspace);
26500
26640
  const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
26501
26641
  if (!meshId) return;
26502
26642
  const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
26503
26643
  const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
26644
+ const resolvedNodeId = targetNode?.id || runtimeNodeId;
26504
26645
  const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
26505
26646
  injectMeshSystemMessage(components, {
26506
26647
  meshId,
26507
26648
  sourceInstanceId: instanceId,
26649
+ nodeId: resolvedNodeId,
26508
26650
  nodeLabel,
26509
26651
  event: event.event,
26510
26652
  metadataEvent: event
@@ -33701,11 +33843,13 @@ async function handleOpenPanel(h, args) {
33701
33843
  async function handlePtyInput(h, args) {
33702
33844
  const { cliType, data, targetSessionId } = args || {};
33703
33845
  if (!data) return { success: false, error: "data required" };
33846
+ const cleanData = typeof data === "string" ? data.replace(/\x1b\[[?>][0-9;]*c/g, "") : data;
33847
+ if (!cleanData) return { success: true };
33704
33848
  const adapter = h.getCliAdapter(targetSessionId || cliType);
33705
33849
  if (!adapter || typeof adapter.writeRaw !== "function") {
33706
33850
  return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
33707
33851
  }
33708
- await adapter.writeRaw(data);
33852
+ await adapter.writeRaw(cleanData);
33709
33853
  return { success: true };
33710
33854
  }
33711
33855
  function handlePtyResize(_h, args) {
@@ -34532,9 +34676,6 @@ function buildCoordinatorDelegatedCliLaunchOptions(input) {
34532
34676
  const cliType = String(input.cliType || "").trim();
34533
34677
  const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
34534
34678
  const env2 = { ...input.env || {}, ...COORDINATOR_DELEGATED_ENV_UNSETS };
34535
- if (cliType === "hermes-cli" && !hasCliArg(cliArgs, "--ignore-user-config")) {
34536
- cliArgs.unshift("--ignore-user-config");
34537
- }
34538
34679
  if (cliType === "claude-cli" && !hasCliArg(cliArgs, "--mcp-config")) {
34539
34680
  cliArgs.unshift("--mcp-config", ensureEmptyDelegatedMcpConfig(input.workspace));
34540
34681
  }
@@ -35478,7 +35619,9 @@ function resolveHermesMeshCoordinatorSetup(options) {
35478
35619
  const mcpServer = resolveAdhdevMcpServerLaunch({
35479
35620
  meshId: options.meshId,
35480
35621
  nodeExecutable: options.nodeExecutable,
35481
- adhdevMcpEntryPath: options.adhdevMcpEntryPath
35622
+ adhdevMcpEntryPath: options.adhdevMcpEntryPath,
35623
+ adhdevMcpTransport: options.adhdevMcpTransport,
35624
+ adhdevMcpPort: options.adhdevMcpPort
35482
35625
  });
35483
35626
  if (!mcpServer) {
35484
35627
  return {
@@ -35545,7 +35688,9 @@ function resolveMeshCoordinatorSetup(options) {
35545
35688
  const mcpServer = resolveAdhdevMcpServerLaunch({
35546
35689
  meshId,
35547
35690
  nodeExecutable: options.nodeExecutable,
35548
- adhdevMcpEntryPath: options.adhdevMcpEntryPath
35691
+ adhdevMcpEntryPath: options.adhdevMcpEntryPath,
35692
+ adhdevMcpTransport: options.adhdevMcpTransport,
35693
+ adhdevMcpPort: options.adhdevMcpPort
35549
35694
  });
35550
35695
  if (!mcpServer) {
35551
35696
  return {
@@ -35567,6 +35712,22 @@ function resolveMeshCoordinatorSetup(options) {
35567
35712
  if (!instructions || !template?.trim()) {
35568
35713
  return { kind: "unsupported", reason: "Provider manual MCP setup is missing instructions or template" };
35569
35714
  }
35715
+ const renderedTemplate = renderMeshCoordinatorTemplate(template, {
35716
+ meshId,
35717
+ workspace,
35718
+ serverName,
35719
+ adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
35720
+ });
35721
+ const isCliCommand = !renderedTemplate.trim().includes("\n") && !renderedTemplate.trim().startsWith("{");
35722
+ if (isCliCommand) {
35723
+ return {
35724
+ kind: "cli_command",
35725
+ serverName,
35726
+ command: renderedTemplate.trim(),
35727
+ requiresRestart: mcpConfig.requiresRestart === true,
35728
+ instructions
35729
+ };
35730
+ }
35570
35731
  return {
35571
35732
  kind: "manual",
35572
35733
  serverName,
@@ -35574,12 +35735,7 @@ function resolveMeshCoordinatorSetup(options) {
35574
35735
  configPathCommand: mcpConfig.configPathCommand,
35575
35736
  requiresRestart: mcpConfig.requiresRestart === true,
35576
35737
  instructions,
35577
- template: renderMeshCoordinatorTemplate(template, {
35578
- meshId,
35579
- workspace,
35580
- serverName,
35581
- adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
35582
- })
35738
+ template: renderedTemplate
35583
35739
  };
35584
35740
  }
35585
35741
  return {
@@ -35608,11 +35764,27 @@ function resolveAdhdevMcpServerLaunch(options) {
35608
35764
  if (!entryPath) return null;
35609
35765
  const nodeExecutable = resolveMcpNodeExecutable(options.nodeExecutable);
35610
35766
  if (!nodeExecutable) return null;
35767
+ const transport = resolveMcpTransport(options.adhdevMcpTransport);
35768
+ const args = [entryPath, "--mode", transport, "--repo-mesh", options.meshId];
35769
+ const port = resolveMcpPort(options.adhdevMcpPort);
35770
+ if (port !== void 0) args.push("--port", String(port));
35611
35771
  return {
35612
35772
  command: nodeExecutable,
35613
- args: [entryPath, "--mode", "ipc", "--repo-mesh", options.meshId]
35773
+ args
35614
35774
  };
35615
35775
  }
35776
+ function resolveMcpTransport(explicitTransport) {
35777
+ if (explicitTransport === "local" || explicitTransport === "ipc") return explicitTransport;
35778
+ const envTransport = process.env.ADHDEV_COORDINATOR_MCP_TRANSPORT?.trim();
35779
+ return envTransport === "local" ? "local" : "ipc";
35780
+ }
35781
+ function resolveMcpPort(explicitPort) {
35782
+ if (typeof explicitPort === "number" && Number.isInteger(explicitPort) && explicitPort > 0) return explicitPort;
35783
+ const raw = process.env.ADHDEV_COORDINATOR_MCP_PORT?.trim();
35784
+ if (!raw) return void 0;
35785
+ const parsed = Number(raw);
35786
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
35787
+ }
35616
35788
  function resolveMcpNodeExecutable(explicitExecutable) {
35617
35789
  const explicit = explicitExecutable?.trim();
35618
35790
  if (explicit) return explicit;
@@ -36424,6 +36596,34 @@ function loadHermesCoordinatorBaseConfig(targetConfigPath) {
36424
36596
  const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
36425
36597
  return { config: baseConfig, sourceHome, sourceConfigPath };
36426
36598
  }
36599
+ function stripHermesCoordinatorTempModelProviderOverrides(config2) {
36600
+ const {
36601
+ model: _model,
36602
+ provider: _provider,
36603
+ default_model: _defaultModel,
36604
+ defaultProvider: _defaultProvider,
36605
+ default_provider: _defaultProviderSnake,
36606
+ modelProvider: _modelProvider,
36607
+ model_provider: _modelProviderSnake,
36608
+ ...sanitized
36609
+ } = config2;
36610
+ const delegation = sanitized.delegation;
36611
+ if (delegation && typeof delegation === "object" && !Array.isArray(delegation)) {
36612
+ const {
36613
+ model: _delegationModel,
36614
+ provider: _delegationProvider,
36615
+ modelProvider: _delegationModelProvider,
36616
+ model_provider: _delegationModelProviderSnake,
36617
+ ...delegationRest
36618
+ } = delegation;
36619
+ if (Object.keys(delegationRest).length > 0) {
36620
+ sanitized.delegation = delegationRest;
36621
+ } else {
36622
+ delete sanitized.delegation;
36623
+ }
36624
+ }
36625
+ return sanitized;
36626
+ }
36427
36627
  function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
36428
36628
  if ((0, import_path7.resolve)(sourceHome) === (0, import_path7.resolve)(targetHome)) return;
36429
36629
  for (const fileName of [".env", "auth.json"]) {
@@ -40855,7 +41055,8 @@ async function initDaemonComponents(config2) {
40855
41055
  cdpManagers,
40856
41056
  sessionRegistry,
40857
41057
  detectedIdes: detectedIdesRef,
40858
- refreshProviderAvailability
41058
+ refreshProviderAvailability,
41059
+ dispatchMeshCommand: config2.dispatchMeshCommand
40859
41060
  };
40860
41061
  setupMeshEventForwarding(components);
40861
41062
  return components;
@@ -40913,7 +41114,7 @@ async function shutdownDaemonComponents(components) {
40913
41114
  }
40914
41115
  cdpManagers.clear();
40915
41116
  }
40916
- var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, fs2, path8, os22, os8, os9, path14, import_child_process2, os10, path15, os11, import_child_process3, import_fs8, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs9, import_path5, import_child_process4, import_fs10, import_os3, path9, import_child_process5, os32, path10, import_fs11, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, init_mesh_work_queue, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
41117
+ var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, fs2, path8, os22, os8, os9, path14, import_child_process2, os10, path15, os11, import_child_process3, import_fs8, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs9, import_path5, import_child_process4, import_fs10, import_os3, path9, import_child_process5, os32, path10, import_fs11, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, mesh_work_queue_exports, init_mesh_work_queue, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
40917
41118
  var init_dist2 = __esm({
40918
41119
  "../daemon-core/dist/index.mjs"() {
40919
41120
  "use strict";
@@ -41246,6 +41447,17 @@ Follow these recovery rules:
41246
41447
  meshLedgerEvents = new import_events2.EventEmitter();
41247
41448
  }
41248
41449
  });
41450
+ mesh_work_queue_exports = {};
41451
+ __export2(mesh_work_queue_exports, {
41452
+ cancelTask: () => cancelTask,
41453
+ claimNextTask: () => claimNextTask,
41454
+ enqueueTask: () => enqueueTask,
41455
+ getMeshQueueStats: () => getMeshQueueStats,
41456
+ getQueue: () => getQueue,
41457
+ requeueTask: () => requeueTask,
41458
+ updateSessionTaskStatus: () => updateSessionTaskStatus,
41459
+ updateTaskStatus: () => updateTaskStatus
41460
+ });
41249
41461
  init_mesh_work_queue = __esm2({
41250
41462
  "src/mesh/mesh-work-queue.ts"() {
41251
41463
  "use strict";
@@ -41325,12 +41537,15 @@ Follow these recovery rules:
41325
41537
  init_logger();
41326
41538
  init_mesh_ledger();
41327
41539
  init_mesh_work_queue();
41540
+ remoteIdleSessions = /* @__PURE__ */ new Map();
41328
41541
  MAX_PENDING_EVENTS = 50;
41329
41542
  pendingMeshCoordinatorEvents = [];
41330
41543
  MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
41544
+ "agent:generating_started",
41331
41545
  "agent:generating_completed",
41332
41546
  "agent:waiting_approval",
41333
41547
  "agent:stopped",
41548
+ "agent:ready",
41334
41549
  "monitor:long_generating"
41335
41550
  ]);
41336
41551
  EVENT_TO_LEDGER_KIND = {
@@ -41865,6 +42080,8 @@ Follow these recovery rules:
41865
42080
  statusHistory = [];
41866
42081
  // ─── CLI Scripts (script-based parsing) ───
41867
42082
  cliScripts;
42083
+ /** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
42084
+ scriptState = null;
41868
42085
  runtimeSettings = {};
41869
42086
  /** Full accumulated rendered PTY transcript for parser/readback use */
41870
42087
  accumulatedBuffer = "";
@@ -41941,9 +42158,13 @@ ${lastSnapshot}`;
41941
42158
  this.lastScreenChangeAt = 0;
41942
42159
  this.lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
41943
42160
  }
42161
+ getAccumulatedRawBufferCacheKey() {
42162
+ return this.accumulatedRawBuffer.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
42163
+ }
41944
42164
  getFreshParsedStatusCache() {
41945
42165
  const cached2 = this.parsedStatusCache;
41946
- if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
42166
+ const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
42167
+ if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
41947
42168
  return cached2.result;
41948
42169
  }
41949
42170
  return null;
@@ -42046,6 +42267,7 @@ ${lastSnapshot}`;
42046
42267
  this.cliScripts = scripts;
42047
42268
  this.parsedStatusCache = null;
42048
42269
  this.parseErrorMessage = null;
42270
+ this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
42049
42271
  const scriptNames = listCliScriptNames(scripts);
42050
42272
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
42051
42273
  }
@@ -42163,6 +42385,7 @@ ${lastSnapshot}`;
42163
42385
  this.ready = false;
42164
42386
  this.startupParseGate = false;
42165
42387
  this.spawnAt = 0;
42388
+ this.scriptState = null;
42166
42389
  this.onStatusChange?.();
42167
42390
  });
42168
42391
  this.spawnAt = Date.now();
@@ -42916,6 +43139,11 @@ ${lastSnapshot}`;
42916
43139
  };
42917
43140
  }
42918
43141
  // ─── Script Execution ──────────────────────────
43142
+ invokeCliScript(script, input) {
43143
+ const hasStateFactory = typeof this.cliScripts?.createState === "function";
43144
+ const expectsStateArgument = hasStateFactory || this.scriptState !== null || script.length >= 2;
43145
+ return expectsStateArgument ? script(this.scriptState, input) : script(input);
43146
+ }
42919
43147
  runParseSession() {
42920
43148
  if (typeof this.cliScripts?.parseSession !== "function") {
42921
43149
  this.parseErrorMessage = `${this.cliType} parseSession unavailable`;
@@ -42936,7 +43164,10 @@ ${lastSnapshot}`;
42936
43164
  scope: this.currentTurnScope,
42937
43165
  runtimeSettings: this.runtimeSettings
42938
43166
  });
42939
- const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
43167
+ const session = this.invokeCliScript(
43168
+ this.cliScripts.parseSession,
43169
+ { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) }
43170
+ );
42940
43171
  this.parseErrorMessage = null;
42941
43172
  return session && typeof session === "object" ? session : null;
42942
43173
  } catch (e) {
@@ -42950,7 +43181,7 @@ ${lastSnapshot}`;
42950
43181
  if (!this.cliScripts?.detectStatus) return null;
42951
43182
  try {
42952
43183
  const screenText = this.terminalScreen.getText();
42953
- const status = this.cliScripts.detectStatus({
43184
+ const status = this.invokeCliScript(this.cliScripts.detectStatus, {
42954
43185
  tail: text.slice(-500),
42955
43186
  screenText,
42956
43187
  rawBuffer: this.accumulatedRawBuffer,
@@ -42969,7 +43200,7 @@ ${lastSnapshot}`;
42969
43200
  try {
42970
43201
  const screenText = this.terminalScreen.getText();
42971
43202
  const buffer = screenText || this.accumulatedBuffer;
42972
- return this.cliScripts.parseApproval({
43203
+ return this.invokeCliScript(this.cliScripts.parseApproval, {
42973
43204
  buffer,
42974
43205
  screenText,
42975
43206
  rawBuffer: this.accumulatedRawBuffer,
@@ -43025,7 +43256,8 @@ ${lastSnapshot}`;
43025
43256
  const screenText = this.readTerminalScreenText();
43026
43257
  const parseScreenText = this.getParseScreenText(screenText);
43027
43258
  const cached2 = this.parsedStatusCache;
43028
- if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.screenText === parseScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
43259
+ const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
43260
+ if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === parseScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
43029
43261
  return cached2.result;
43030
43262
  }
43031
43263
  const parsed = this.runParseSession();
@@ -43053,6 +43285,7 @@ ${lastSnapshot}`;
43053
43285
  currentTurnScope: this.currentTurnScope,
43054
43286
  recentOutputBuffer: this.recentOutputBuffer,
43055
43287
  accumulatedBuffer: this.accumulatedBuffer,
43288
+ accumulatedRawBufferKey,
43056
43289
  screenText: parseScreenText,
43057
43290
  currentStatus: this.currentStatus,
43058
43291
  activeModal: this.activeModal,
@@ -43077,7 +43310,7 @@ ${lastSnapshot}`;
43077
43310
  scope: this.currentTurnScope,
43078
43311
  runtimeSettings: this.runtimeSettings
43079
43312
  });
43080
- return await Promise.resolve(fn({
43313
+ return await Promise.resolve(fn(this.scriptState, {
43081
43314
  ...input,
43082
43315
  args: args && typeof args === "object" ? { ...args } : {}
43083
43316
  }));
@@ -48296,6 +48529,8 @@ ${effect.notification.body || ""}`.trim();
48296
48529
  this.completedDebounceTimer = null;
48297
48530
  }, 3e3);
48298
48531
  }
48532
+ } else if (newStatus === "idle" && this.lastStatus === "starting") {
48533
+ this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
48299
48534
  } else if (newStatus === "stopped") {
48300
48535
  if (this.generatingDebounceTimer) {
48301
48536
  clearTimeout(this.generatingDebounceTimer);
@@ -53014,6 +53249,51 @@ Run 'adhdev doctor' for detailed diagnostics.`
53014
53249
  return { success: false, error: e.message };
53015
53250
  }
53016
53251
  }
53252
+ case "get_mesh_queue": {
53253
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
53254
+ if (!meshId) return { success: false, error: "meshId required" };
53255
+ try {
53256
+ const { getQueue: getQueue2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
53257
+ const status = Array.isArray(args?.status) ? args.status.map((s) => typeof s === "string" ? s.trim() : "").filter(Boolean) : void 0;
53258
+ const queue = getQueue2(meshId, { status });
53259
+ return { success: true, queue };
53260
+ } catch (e) {
53261
+ return { success: false, error: e.message };
53262
+ }
53263
+ }
53264
+ case "cancel_mesh_queue_task": {
53265
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
53266
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
53267
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
53268
+ try {
53269
+ const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
53270
+ const reason = typeof args?.reason === "string" ? args.reason : void 0;
53271
+ const task = cancelTask2(meshId, taskId, { reason });
53272
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
53273
+ return { success: true, task };
53274
+ } catch (e) {
53275
+ return { success: false, error: e.message };
53276
+ }
53277
+ }
53278
+ case "requeue_mesh_queue_task": {
53279
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
53280
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
53281
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
53282
+ try {
53283
+ const { requeueTask: requeueTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
53284
+ const task = requeueTask2(meshId, taskId, {
53285
+ reason: typeof args?.reason === "string" ? args.reason : void 0,
53286
+ targetNodeId: typeof args?.targetNodeId === "string" ? args.targetNodeId.trim() : void 0,
53287
+ targetSessionId: typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : void 0,
53288
+ clearTargetNode: args?.clearTargetNode === true,
53289
+ clearTargetSession: args?.clearTargetSession !== false
53290
+ });
53291
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
53292
+ return { success: true, task };
53293
+ } catch (e) {
53294
+ return { success: false, error: e.message };
53295
+ }
53296
+ }
53017
53297
  case "add_mesh_node": {
53018
53298
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
53019
53299
  const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
@@ -53171,7 +53451,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
53171
53451
  appendLedgerEntry2(meshId, {
53172
53452
  kind: "node_removed",
53173
53453
  nodeId,
53174
- payload: { worktree: !!node?.isLocalWorktree, sessionCleanupMode }
53454
+ payload: {
53455
+ worktree: !!node?.isLocalWorktree,
53456
+ sessionCleanupMode,
53457
+ workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
53458
+ daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
53459
+ worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
53460
+ }
53175
53461
  });
53176
53462
  } catch {
53177
53463
  }
@@ -53342,6 +53628,93 @@ Run 'adhdev doctor' for detailed diagnostics.`
53342
53628
  meshCoordinatorSetup: coordinatorSetup
53343
53629
  };
53344
53630
  }
53631
+ if (coordinatorSetup.kind === "cli_command") {
53632
+ let cliCmdSystemPrompt = "";
53633
+ try {
53634
+ cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType });
53635
+ } catch (error48) {
53636
+ const message = error48?.message || String(error48);
53637
+ LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
53638
+ return {
53639
+ success: false,
53640
+ code: "mesh_coordinator_prompt_failed",
53641
+ error: `Failed to build Repo Mesh coordinator prompt: ${message}`,
53642
+ meshId,
53643
+ cliType,
53644
+ workspace
53645
+ };
53646
+ }
53647
+ try {
53648
+ const { execFileSync: execCmdSync } = await import("child_process");
53649
+ const cmdParts = coordinatorSetup.command.trim().split(/\s+/);
53650
+ const [regCmd, ...regArgs] = cmdParts;
53651
+ LOG.info("MeshCoordinator", `Running MCP registration: ${coordinatorSetup.command}`);
53652
+ execCmdSync(regCmd, regArgs, { stdio: "pipe", timeout: 15e3 });
53653
+ } catch (error48) {
53654
+ LOG.warn("MeshCoordinator", `MCP registration command failed (may be pre-registered): ${error48?.message || error48}`);
53655
+ }
53656
+ const cliCmdArgs = [];
53657
+ const cliCmdEnv = {};
53658
+ if (cliCmdSystemPrompt) {
53659
+ if (cliType === "codex-cli") {
53660
+ cliCmdArgs.push("-c", `developer_instructions=${JSON.stringify(cliCmdSystemPrompt)}`);
53661
+ } else if (cliType === "gemini-cli") {
53662
+ try {
53663
+ const { writeFileSync: wfs, existsSync: efs, readFileSync: rfs } = await import("fs");
53664
+ const geminiMdPath = `${workspace}/GEMINI.md`;
53665
+ const marker = "<!-- adhdev-mesh-coordinator-prompt -->";
53666
+ const markerEnd = "<!-- /adhdev-mesh-coordinator-prompt -->";
53667
+ const block = `${marker}
53668
+ ${cliCmdSystemPrompt}
53669
+ ${markerEnd}`;
53670
+ if (efs(geminiMdPath)) {
53671
+ const existing = rfs(geminiMdPath, "utf-8");
53672
+ const replaced = existing.replace(
53673
+ new RegExp(`${marker}[\\s\\S]*?${markerEnd}`, "g"),
53674
+ block
53675
+ );
53676
+ wfs(geminiMdPath, replaced.includes(marker) ? replaced : `${existing}
53677
+
53678
+ ${block}`);
53679
+ } else {
53680
+ wfs(geminiMdPath, block);
53681
+ }
53682
+ LOG.info("MeshCoordinator", `Wrote coordinator prompt to ${workspace}/GEMINI.md`);
53683
+ } catch (e) {
53684
+ LOG.warn("MeshCoordinator", `Could not write GEMINI.md: ${e?.message || e}`);
53685
+ }
53686
+ }
53687
+ }
53688
+ const cliCmdLaunch = await this.deps.cliManager.handleCliCommand("launch_cli", {
53689
+ cliType,
53690
+ dir: workspace,
53691
+ cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : void 0,
53692
+ env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : void 0,
53693
+ settings: { meshCoordinatorFor: meshId }
53694
+ });
53695
+ if (!cliCmdLaunch?.success) {
53696
+ return { success: false, error: cliCmdLaunch?.error || "Failed to launch CLI session" };
53697
+ }
53698
+ LOG.info("MeshCoordinator", `Launched ${cliType} coordinator (cli_command) for mesh ${meshId}`);
53699
+ try {
53700
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
53701
+ appendLedgerEntry2(meshId, {
53702
+ kind: "coordinator_started",
53703
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
53704
+ providerType: cliType,
53705
+ payload: { workspace }
53706
+ });
53707
+ } catch {
53708
+ }
53709
+ return {
53710
+ success: true,
53711
+ meshId,
53712
+ cliType,
53713
+ workspace,
53714
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
53715
+ mcpRegistered: true
53716
+ };
53717
+ }
53345
53718
  const configFormat = coordinatorSetup.configFormat;
53346
53719
  if (configFormat !== "claude_mcp_json" && configFormat !== "hermes_config_yaml") {
53347
53720
  return {
@@ -53396,9 +53769,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
53396
53769
  args: coordinatorSetup.mcpServer.args
53397
53770
  };
53398
53771
  if (args?.inlineMesh) {
53772
+ const modeArgIndex = coordinatorSetup.mcpServer.args.findIndex((value) => value === "--mode");
53773
+ const mcpTransport = modeArgIndex >= 0 ? coordinatorSetup.mcpServer.args[modeArgIndex + 1] : "ipc";
53399
53774
  mcpServerEntry.env = {
53400
53775
  ADHDEV_INLINE_MESH: JSON.stringify(mesh),
53401
- ADHDEV_MCP_TRANSPORT: "ipc"
53776
+ ADHDEV_MCP_TRANSPORT: mcpTransport === "local" ? "local" : "ipc"
53402
53777
  };
53403
53778
  }
53404
53779
  try {
@@ -53417,7 +53792,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
53417
53792
  if (hadExistingMcpConfig) {
53418
53793
  try {
53419
53794
  const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync17(mcpConfigPath, "utf-8"), configFormat);
53420
- existingMcpConfig = { ...existingMcpConfig, ...parsedExistingMcpConfig };
53795
+ const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
53796
+ existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
53421
53797
  copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
53422
53798
  } catch (error48) {
53423
53799
  LOG.error("MeshCoordinator", `Failed to parse existing MCP config ${mcpConfigPath}: ${error48?.message || error48}`);
@@ -57240,6 +57616,11 @@ function annotateRapidReadChatAdvisory(payload, options) {
57240
57616
  // src/tools/mesh-tools.ts
57241
57617
  init_dist2();
57242
57618
  var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
57619
+ function readString(value) {
57620
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
57621
+ }
57622
+ var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
57623
+ var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
57243
57624
  async function refreshMeshFromDaemon(ctx) {
57244
57625
  if (!(ctx.transport instanceof IpcTransport)) return;
57245
57626
  try {
@@ -57260,6 +57641,138 @@ async function findNodeWithRefresh(ctx, nodeId) {
57260
57641
  if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
57261
57642
  return refreshed;
57262
57643
  }
57644
+ async function findOptionalNodeWithRefresh(ctx, nodeId) {
57645
+ const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
57646
+ if (hit) return hit;
57647
+ await refreshMeshFromDaemon(ctx);
57648
+ return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
57649
+ }
57650
+ function hasRecentDuplicateDispatch(ctx, args) {
57651
+ const now = Date.now();
57652
+ const normalizedMessage = args.message.trim();
57653
+ for (const task of getQueue(ctx.mesh.id)) {
57654
+ const timestamp2 = new Date(task.updatedAt || task.createdAt).getTime();
57655
+ if (!Number.isFinite(timestamp2) || now - timestamp2 > DUPLICATE_DISPATCH_WINDOW_MS) continue;
57656
+ if (task.targetNodeId && task.targetNodeId !== args.node_id) continue;
57657
+ if (task.assignedNodeId && task.assignedNodeId !== args.node_id) continue;
57658
+ if (args.session_id && task.targetSessionId !== args.session_id && task.assignedSessionId !== args.session_id) continue;
57659
+ if (task.message?.trim() === normalizedMessage) {
57660
+ return { duplicate: true, entry: task, source: "queue" };
57661
+ }
57662
+ }
57663
+ const entries = readLedgerEntries(ctx.mesh.id, { tail: 200 });
57664
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
57665
+ const entry = entries[i];
57666
+ const timestamp2 = new Date(entry.timestamp).getTime();
57667
+ if (Number.isFinite(timestamp2) && now - timestamp2 > DUPLICATE_DISPATCH_WINDOW_MS) break;
57668
+ if (entry.kind !== "task_dispatched") continue;
57669
+ if (entry.nodeId !== args.node_id) continue;
57670
+ if (args.session_id && entry.sessionId !== args.session_id) continue;
57671
+ if (typeof entry.payload?.message !== "string") continue;
57672
+ if (entry.payload.message.trim() === normalizedMessage) {
57673
+ return { duplicate: true, entry, source: "ledger" };
57674
+ }
57675
+ }
57676
+ return { duplicate: false };
57677
+ }
57678
+ function buildMissingNodeReadChatRecovery(ctx, args) {
57679
+ const entries = readLedgerEntries(ctx.mesh.id, { tail: 300 });
57680
+ const relatedEntries = entries.filter((entry) => entry.nodeId === args.node_id || entry.sessionId === args.session_id);
57681
+ const completedEntries = relatedEntries.filter((entry) => entry.kind === "task_completed");
57682
+ const lastDispatch = [...relatedEntries].reverse().find((entry) => entry.kind === "task_dispatched");
57683
+ const lastTerminal = [...relatedEntries].reverse().find((entry) => entry.kind === "task_completed" || entry.kind === "task_failed" || entry.kind === "task_stalled");
57684
+ const lastRemoved = [...relatedEntries].reverse().find((entry) => entry.kind === "node_removed");
57685
+ const lastLaunch = [...relatedEntries].reverse().find((entry) => entry.kind === "session_launched");
57686
+ const providerSessionId = args.provider_session_id || readString(lastTerminal?.payload?.providerSessionId) || readString(lastLaunch?.payload?.providerSessionId) || readString(lastDispatch?.payload?.providerSessionId);
57687
+ const finalSummary = readString(lastTerminal?.payload?.finalSummary) || readString(lastTerminal?.payload?.compactSummary) || readString(lastTerminal?.payload?.summary);
57688
+ const ledger = {
57689
+ taskCompletedFound: completedEntries.length > 0,
57690
+ nodeRemovedFound: !!lastRemoved,
57691
+ providerType: lastTerminal?.providerType || lastLaunch?.providerType || lastDispatch?.providerType,
57692
+ providerSessionId,
57693
+ nodeRemovedAt: lastRemoved?.timestamp,
57694
+ sessionCleanupMode: readString(lastRemoved?.payload?.sessionCleanupMode),
57695
+ readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
57696
+ };
57697
+ if (finalSummary) {
57698
+ return {
57699
+ success: true,
57700
+ compact: args.compact === true,
57701
+ recoveredFromLedger: true,
57702
+ nodeId: args.node_id,
57703
+ sessionId: args.session_id,
57704
+ summary: finalSummary,
57705
+ ledger,
57706
+ messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
57707
+ };
57708
+ }
57709
+ return {
57710
+ success: false,
57711
+ recoverable: true,
57712
+ code: "mesh_removed_node_transcript_unavailable",
57713
+ error: `Node '${args.node_id}' is not a current member of mesh '${ctx.mesh.name}'.`,
57714
+ nodeId: args.node_id,
57715
+ sessionId: args.session_id,
57716
+ providerSessionId,
57717
+ reason: "node_not_in_current_mesh_snapshot",
57718
+ ledger,
57719
+ completedSessionSeenInLedger: ledger.taskCompletedFound,
57720
+ lastDispatch: lastDispatch ? {
57721
+ timestamp: lastDispatch.timestamp,
57722
+ sessionId: lastDispatch.sessionId,
57723
+ providerType: lastDispatch.providerType,
57724
+ taskId: typeof lastDispatch.payload?.taskId === "string" ? lastDispatch.payload.taskId : void 0,
57725
+ messagePreview: typeof lastDispatch.payload?.message === "string" ? lastDispatch.payload.message.slice(0, 500) : void 0
57726
+ } : null,
57727
+ lastTerminalEvent: lastTerminal ? {
57728
+ kind: lastTerminal.kind,
57729
+ timestamp: lastTerminal.timestamp,
57730
+ sessionId: lastTerminal.sessionId,
57731
+ providerType: lastTerminal.providerType,
57732
+ taskId: typeof lastTerminal.payload?.taskId === "string" ? lastTerminal.payload.taskId : void 0,
57733
+ payload: lastTerminal.payload
57734
+ } : null,
57735
+ nextSteps: [
57736
+ providerSessionId ? `Retry mesh_read_chat with provider_session_id='${providerSessionId}' on a current live node for the same daemon if one exists.` : "If the node UI shows a provider transcript id, retry mesh_read_chat/mesh_read_debug with provider_session_id.",
57737
+ "Use mesh_read_debug with the provider_session_id or daemon-side debug bundle locator if available.",
57738
+ "Check mesh_task_history for task_completed and node_removed entries before redispatching; do not resend solely because transcript recovery failed.",
57739
+ "If this node was removed with stop_and_delete, the runtime transcript may be gone; rely on the ledger summary/locator or ask the operator for the saved UI output."
57740
+ ],
57741
+ recoveryHints: [
57742
+ "The worktree/node may have been removed or the mesh snapshot may be stale after task completion.",
57743
+ "If you have a provider_session_id, retry mesh_read_chat with that value while targeting a live node for the same daemon if available.",
57744
+ "Use mesh_read_debug with provider_session_id, or inspect the daemon/session-host history locator if the transcript has already been archived.",
57745
+ "Avoid redispatching the same task solely because read_chat could not recover the transcript; check task_history and git status first."
57746
+ ]
57747
+ };
57748
+ }
57749
+ function annotateQueueStaleness(queue) {
57750
+ const now = Date.now();
57751
+ return queue.map((task) => {
57752
+ const taskStatus = typeof task?.status === "string" ? task.status : void 0;
57753
+ const annotated = {
57754
+ ...task,
57755
+ taskStatus,
57756
+ dispatchedAt: task?.createdAt,
57757
+ ...taskStatus === "assigned" ? { activeTaskId: task.id } : {},
57758
+ ...taskStatus === "completed" || taskStatus === "failed" ? {
57759
+ isHistorical: true,
57760
+ completedAt: task.updatedAt
57761
+ } : {}
57762
+ };
57763
+ if (taskStatus !== "assigned") return annotated;
57764
+ const updatedAt = new Date(task.updatedAt).getTime();
57765
+ const ageMs = Number.isFinite(updatedAt) ? now - updatedAt : null;
57766
+ if (ageMs === null || ageMs < STALE_ASSIGNED_QUEUE_MS) return annotated;
57767
+ return {
57768
+ ...annotated,
57769
+ stale: true,
57770
+ staleAssigned: true,
57771
+ staleReason: "assigned task has not reached a terminal state within 30 minutes",
57772
+ assignedAgeMs: ageMs
57773
+ };
57774
+ });
57775
+ }
57263
57776
  function unwrapCommandPayload(value) {
57264
57777
  let current = value;
57265
57778
  const seen = /* @__PURE__ */ new Set();
@@ -57272,6 +57785,26 @@ function unwrapCommandPayload(value) {
57272
57785
  }
57273
57786
  return current;
57274
57787
  }
57788
+ function isTerminalSessionRecord(session) {
57789
+ const status = typeof session?.status === "string" ? session.status.toLowerCase() : "";
57790
+ const lifecycle = typeof session?.lifecycle === "string" ? session.lifecycle.toLowerCase() : "";
57791
+ const state = typeof session?.state === "string" ? session.state.toLowerCase() : "";
57792
+ return [status, lifecycle, state].some((value) => ["stopped", "failed", "terminated", "exited", "closed"].includes(value));
57793
+ }
57794
+ function isIdleSessionRecord(session) {
57795
+ if (isTerminalSessionRecord(session)) return false;
57796
+ const status = typeof session?.status === "string" ? session.status.toLowerCase() : "";
57797
+ const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
57798
+ return status === "idle" || chatStatus === "waiting_input";
57799
+ }
57800
+ function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
57801
+ const live = sessions.filter((session) => !isTerminalSessionRecord(session));
57802
+ const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
57803
+ const meshSessions = live.filter(
57804
+ (session) => session?.settings?.meshNodeFor === meshId || session?.settings?.meshNodeId === nodeId
57805
+ );
57806
+ return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || live.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || live.find(matchingProvider) || live.find(isIdleSessionRecord) || live[0];
57807
+ }
57275
57808
  function findNestedPayload(value, predicate) {
57276
57809
  const seen = /* @__PURE__ */ new Set();
57277
57810
  const stack = [{ payload: value, depth: 0 }];
@@ -57300,12 +57833,136 @@ function extractGitDiff(value) {
57300
57833
  function extractLaunchPayload(value) {
57301
57834
  return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
57302
57835
  }
57836
+ function classifyMeshLaunchFailure(error48) {
57837
+ const message = error48 instanceof Error ? error48.message : String(error48 || "launch failed");
57838
+ const lower = message.toLowerCase();
57839
+ if (lower.includes("p2p") || lower.includes("datachannel") || lower.includes("node-datachannel")) {
57840
+ return { code: "p2p_unavailable", reason: "daemon_mesh_p2p_transport_unavailable", transport: "daemon_mesh_p2p" };
57841
+ }
57842
+ if (lower.includes("cannot connect to daemon ipc") || lower.includes("daemon ipc command")) {
57843
+ return { code: "local_ipc_unavailable", reason: "local_daemon_ipc_unavailable", transport: "local_ipc" };
57844
+ }
57845
+ if (lower.includes("timed out") || lower.includes("timeout")) {
57846
+ return { code: "mesh_transport_timeout", reason: "mesh_transport_timeout", transport: "mesh_transport" };
57847
+ }
57848
+ return { code: "mesh_launch_failed", reason: "provider_launch_failed", transport: "mesh_transport" };
57849
+ }
57850
+ function buildWorktreeCleanupHint(node) {
57851
+ if (!node.isLocalWorktree) return void 0;
57852
+ return {
57853
+ tool: "mesh_remove_node",
57854
+ args: { node_id: node.id, session_cleanup_mode: "preserve" },
57855
+ hint: `If the worktree is no longer needed, remove the orphan worktree node with mesh_remove_node(node_id: "${node.id}").`
57856
+ };
57857
+ }
57858
+ function buildRecoverableLaunchFailure(ctx, node, providerType, error48) {
57859
+ const message = error48 instanceof Error ? error48.message : String(error48 || "launch failed");
57860
+ const classified = classifyMeshLaunchFailure(error48);
57861
+ const cleanup = buildWorktreeCleanupHint(node);
57862
+ return {
57863
+ success: false,
57864
+ recoverable: true,
57865
+ code: classified.code,
57866
+ reason: classified.reason,
57867
+ transport: classified.transport,
57868
+ error: message,
57869
+ meshId: ctx.mesh.id,
57870
+ nodeId: node.id,
57871
+ daemonId: node.daemonId,
57872
+ workspace: node.workspace,
57873
+ isLocalWorktree: node.isLocalWorktree === true,
57874
+ worktreeBranch: node.worktreeBranch,
57875
+ clonedFromNodeId: node.clonedFromNodeId,
57876
+ ...providerType ? { resolvedProviderType: providerType } : {},
57877
+ retryHint: `Retry mesh_launch_session(node_id: "${node.id}"${providerType ? `, type: "${providerType}"` : ""}) after daemon mesh transport/P2P is healthy.`,
57878
+ ...cleanup ? { cleanup } : {},
57879
+ nextStepHints: [
57880
+ `Retry mesh_launch_session(node_id: "${node.id}"${providerType ? `, type: "${providerType}"` : ""}) after checking daemon/P2P health.`,
57881
+ ...cleanup ? [`Cleanup orphan worktree node with mesh_remove_node(node_id: "${node.id}") if retry is not desired.`] : [],
57882
+ "Run mesh_status to see the degraded reason and recovery hints before redispatching work."
57883
+ ]
57884
+ };
57885
+ }
57886
+ function recordRecoverableLaunchFailure(ctx, node, providerType, error48) {
57887
+ const failure2 = buildRecoverableLaunchFailure(ctx, node, providerType, error48);
57888
+ try {
57889
+ appendLedgerEntry(ctx.mesh.id, {
57890
+ kind: "recovery_attempted",
57891
+ nodeId: node.id,
57892
+ providerType,
57893
+ payload: {
57894
+ event: "session_launch_failed",
57895
+ ...failure2
57896
+ }
57897
+ });
57898
+ } catch {
57899
+ }
57900
+ return failure2;
57901
+ }
57902
+ function getLatestActiveLaunchFailure(meshId, nodeId) {
57903
+ const entries = readLedgerEntries(meshId, { tail: 200 });
57904
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
57905
+ const entry = entries[i];
57906
+ if (entry.nodeId !== nodeId) continue;
57907
+ if (entry.kind === "session_launched" || entry.kind === "node_removed") return null;
57908
+ if (entry.kind === "recovery_attempted" && entry.payload?.event === "session_launch_failed") {
57909
+ return { timestamp: entry.timestamp, ...entry.payload };
57910
+ }
57911
+ }
57912
+ return null;
57913
+ }
57914
+ async function ipcDispatchToRemoteAgent(ctx, node, args) {
57915
+ const transport = ctx.transport;
57916
+ const daemonId = node.daemonId;
57917
+ let sessionId = args.session_id?.trim() || "";
57918
+ const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
57919
+ let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
57920
+ if (!sessionId) {
57921
+ try {
57922
+ const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
57923
+ const innerResult = relayResult?.result ?? relayResult;
57924
+ const statusObj = innerResult?.status ?? innerResult;
57925
+ const sessions = Array.isArray(statusObj?.sessions) ? statusObj.sessions : [];
57926
+ const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id);
57927
+ if (targetSession?.id || targetSession?.sessionId) {
57928
+ sessionId = targetSession.id || targetSession.sessionId;
57929
+ if (!resolvedProviderType) {
57930
+ resolvedProviderType = targetSession.providerType || targetSession.cliType || "";
57931
+ }
57932
+ }
57933
+ } catch (e) {
57934
+ }
57935
+ }
57936
+ if (!resolvedProviderType) {
57937
+ return { success: false, error: `Cannot dispatch to remote node '${node.id}': providerType unknown. Set providerPriority on the node policy or call mesh_launch_session first.` };
57938
+ }
57939
+ try {
57940
+ const dispatchResult = await transport.meshCommand(daemonId, "agent_command", {
57941
+ ...sessionId ? { targetSessionId: sessionId } : {},
57942
+ agentType: resolvedProviderType,
57943
+ cliType: resolvedProviderType,
57944
+ action: "send_chat",
57945
+ message: args.message
57946
+ });
57947
+ const dispatchPayload = unwrapCommandPayload(dispatchResult);
57948
+ if (dispatchPayload?.success === false || dispatchResult?.success === false) {
57949
+ return { success: false, error: `P2P dispatch failed: ${dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"}` };
57950
+ }
57951
+ return { success: true, dispatched: true, sessionId: sessionId || resolvedProviderType };
57952
+ } catch (e) {
57953
+ return { success: false, error: `P2P dispatch failed: ${e?.message || String(e)}` };
57954
+ }
57955
+ }
57303
57956
  function resolveCoordinatorNode(ctx) {
57304
57957
  const preferredNodeId = typeof ctx.mesh.coordinator?.preferredNodeId === "string" ? ctx.mesh.coordinator.preferredNodeId.trim() : "";
57305
57958
  if (preferredNodeId) {
57306
57959
  const preferred = ctx.mesh.nodes.find((n) => n.id === preferredNodeId && typeof n.daemonId === "string" && n.daemonId.trim());
57307
57960
  if (preferred) return preferred;
57308
57961
  }
57962
+ if (ctx.localMachineId) {
57963
+ const byMachine = ctx.mesh.nodes.find((n) => n.machineId === ctx.localMachineId);
57964
+ if (byMachine) return byMachine;
57965
+ }
57309
57966
  if (ctx.localDaemonId) {
57310
57967
  return ctx.mesh.nodes.find((n) => n.daemonId === ctx.localDaemonId);
57311
57968
  }
@@ -57395,7 +58052,8 @@ function getNodeLaunchReadiness(node) {
57395
58052
  };
57396
58053
  }
57397
58054
  async function commandForNode(ctx, node, command, args = {}) {
57398
- if (ctx.transport instanceof IpcTransport && node.daemonId && node.daemonId !== ctx.localDaemonId) {
58055
+ const isLocalNode = ctx.localMachineId && node.machineId === ctx.localMachineId || ctx.localDaemonId && node.daemonId === ctx.localDaemonId;
58056
+ if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
57399
58057
  return ctx.transport.meshCommand(node.daemonId, command, args);
57400
58058
  }
57401
58059
  if (isLocalTransport(ctx.transport)) {
@@ -57405,10 +58063,12 @@ async function commandForNode(ctx, node, command, args = {}) {
57405
58063
  }
57406
58064
  var MESH_STATUS_TOOL = {
57407
58065
  name: "mesh_status",
57408
- description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions. Use this to decide which node to send work to.",
58066
+ 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.",
57409
58067
  inputSchema: {
57410
58068
  type: "object",
57411
- properties: {}
58069
+ properties: {
58070
+ _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
58071
+ }
57412
58072
  }
57413
58073
  };
57414
58074
  var MESH_LIST_NODES_TOOL = {
@@ -57416,7 +58076,9 @@ var MESH_LIST_NODES_TOOL = {
57416
58076
  description: "List all nodes in the mesh with their capabilities, platform, and workspace paths.",
57417
58077
  inputSchema: {
57418
58078
  type: "object",
57419
- properties: {}
58079
+ properties: {
58080
+ _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
58081
+ }
57420
58082
  }
57421
58083
  };
57422
58084
  var MESH_ENQUEUE_TASK_TOOL = {
@@ -57432,18 +58094,46 @@ var MESH_ENQUEUE_TASK_TOOL = {
57432
58094
  };
57433
58095
  var MESH_VIEW_QUEUE_TOOL = {
57434
58096
  name: "mesh_view_queue",
57435
- description: "View the current status of the mesh work queue (pending, assigned, completed, failed tasks).",
58097
+ description: "View the current status of the mesh work queue (pending, assigned, completed, failed, cancelled tasks).",
57436
58098
  inputSchema: {
57437
58099
  type: "object",
57438
58100
  properties: {
57439
58101
  status: {
57440
58102
  type: "array",
57441
58103
  items: { type: "string" },
57442
- description: "Filter by task status: pending, assigned, completed, failed. Returns all if omitted."
58104
+ description: "Filter by task status: pending, assigned, completed, failed, cancelled. Returns all if omitted."
57443
58105
  }
57444
58106
  }
57445
58107
  }
57446
58108
  };
58109
+ var MESH_QUEUE_CANCEL_TOOL = {
58110
+ name: "mesh_queue_cancel",
58111
+ description: "Cancel a pending/assigned/completed/failed mesh queue task without deleting audit history. Use this to retire stale queue items that target dead sessions.",
58112
+ inputSchema: {
58113
+ type: "object",
58114
+ properties: {
58115
+ task_id: { type: "string", description: "Queue task ID to cancel." },
58116
+ reason: { type: "string", description: "Optional operator-visible reason for cancellation." }
58117
+ },
58118
+ required: ["task_id"]
58119
+ }
58120
+ };
58121
+ var MESH_QUEUE_REQUEUE_TOOL = {
58122
+ name: "mesh_queue_requeue",
58123
+ description: "Return a mesh queue task to pending for retry. By default clears stale assigned owner and target session so another live session can claim it.",
58124
+ inputSchema: {
58125
+ type: "object",
58126
+ properties: {
58127
+ task_id: { type: "string", description: "Queue task ID to requeue." },
58128
+ reason: { type: "string", description: "Optional operator-visible reason for requeueing." },
58129
+ target_node_id: { type: "string", description: "Optional replacement target node ID." },
58130
+ target_session_id: { type: "string", description: "Optional replacement target runtime session ID." },
58131
+ clear_target_node: { type: "boolean", description: "When true, remove any existing target node constraint." },
58132
+ keep_target_session: { type: "boolean", description: "When true, preserve an existing target session if target_session_id is not provided. Defaults false to avoid stale session targets." }
58133
+ },
58134
+ required: ["task_id"]
58135
+ }
58136
+ };
57447
58137
  var MESH_SEND_TASK_TOOL = {
57448
58138
  name: "mesh_send_task",
57449
58139
  description: "Legacy push-based task assignment. Enqueues a task specifically targeted at a given node. The node will pull it immediately if idle.",
@@ -57613,6 +58303,8 @@ var ALL_MESH_TOOLS = [
57613
58303
  MESH_LIST_NODES_TOOL,
57614
58304
  MESH_ENQUEUE_TASK_TOOL,
57615
58305
  MESH_VIEW_QUEUE_TOOL,
58306
+ MESH_QUEUE_CANCEL_TOOL,
58307
+ MESH_QUEUE_REQUEUE_TOOL,
57616
58308
  MESH_SEND_TASK_TOOL,
57617
58309
  MESH_READ_CHAT_TOOL,
57618
58310
  MESH_READ_DEBUG_TOOL,
@@ -57630,6 +58322,7 @@ async function meshStatus(ctx) {
57630
58322
  await refreshMeshFromDaemon(ctx);
57631
58323
  const { mesh, transport } = ctx;
57632
58324
  const results = [];
58325
+ const ledgerSummary = getLedgerSummary(mesh.id);
57633
58326
  for (const node of mesh.nodes) {
57634
58327
  const entry = {
57635
58328
  nodeId: node.id,
@@ -57663,6 +58356,45 @@ async function meshStatus(ctx) {
57663
58356
  entry.health = "degraded";
57664
58357
  entry.error = e.message;
57665
58358
  }
58359
+ const recoveryContext = getSessionRecoveryContext(mesh.id, { nodeId: node.id });
58360
+ if (recoveryContext.consecutiveNodeFailures > 0) {
58361
+ entry.recoveryHints = {
58362
+ consecutiveFailures: recoveryContext.consecutiveNodeFailures,
58363
+ lastTaskMessage: recoveryContext.lastTaskMessage,
58364
+ advice: recoveryContext.advice,
58365
+ retryRecommended: recoveryContext.retryRecommended
58366
+ };
58367
+ }
58368
+ const activeLaunchFailure = getLatestActiveLaunchFailure(mesh.id, node.id);
58369
+ if (activeLaunchFailure && node.isLocalWorktree) {
58370
+ entry.health = "degraded";
58371
+ entry.degradedReason = "worktree_launch_failed";
58372
+ entry.launchReady = false;
58373
+ entry.launchBlockedReason = activeLaunchFailure.code || "mesh_launch_failed";
58374
+ entry.launchBlockedMessage = activeLaunchFailure.error || "Previous worktree session launch failed";
58375
+ entry.lastLaunchFailure = activeLaunchFailure;
58376
+ }
58377
+ const nextStepHints = [];
58378
+ if (entry.degradedReason === "worktree_launch_failed") {
58379
+ nextStepHints.push(`Retry mesh_launch_session(node_id: "${node.id}") after daemon mesh transport/P2P is healthy.`);
58380
+ nextStepHints.push(`If retry is not desired, cleanup the orphan worktree node with mesh_remove_node(node_id: "${node.id}").`);
58381
+ } else if (entry.health === "online" && node.isLocalWorktree) {
58382
+ nextStepHints.push(`Merge worktree to base via mesh_refine_node(node_id: "${node.id}")`);
58383
+ } else if (entry.health === "dirty") {
58384
+ nextStepHints.push(`Commit changes via mesh_checkpoint(node_id: "${node.id}", message: "...")`);
58385
+ } else if (entry.health === "degraded" && entry.error?.includes("git")) {
58386
+ nextStepHints.push("Initialize git repository or check workspace path.");
58387
+ }
58388
+ if (recoveryContext.consecutiveNodeFailures > 0) {
58389
+ if (recoveryContext.retryRecommended) {
58390
+ nextStepHints.push(`Retry task on this node or launch a fresh session.`);
58391
+ } else {
58392
+ nextStepHints.push(`Consider reassigning work to a different node.`);
58393
+ }
58394
+ }
58395
+ if (nextStepHints.length > 0) {
58396
+ entry.nextStepHints = nextStepHints;
58397
+ }
57666
58398
  const relatedRepos = await collectRelatedRepoStatuses(ctx, node);
57667
58399
  if (relatedRepos.length) entry.relatedRepos = relatedRepos;
57668
58400
  results.push(entry);
@@ -57676,7 +58408,7 @@ async function meshStatus(ctx) {
57676
58408
  nodes: results
57677
58409
  };
57678
58410
  try {
57679
- response.ledgerSummary = getLedgerSummary(mesh.id);
58411
+ response.ledgerSummary = ledgerSummary;
57680
58412
  } catch {
57681
58413
  }
57682
58414
  if (ctx.transport instanceof IpcTransport) {
@@ -57720,12 +58452,38 @@ async function meshListNodes(ctx) {
57720
58452
  async function meshEnqueueTask(ctx, args) {
57721
58453
  try {
57722
58454
  const task = enqueueTask(ctx.mesh.id, args.message);
57723
- if (ctx.transport instanceof IpcTransport && ctx.localDaemonId) {
57724
- ctx.transport.meshCommand(ctx.localDaemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
58455
+ if (isLocalTransport(ctx.transport) && !(ctx.transport instanceof IpcTransport)) {
58456
+ ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
57725
58457
  });
57726
- } else if (isLocalTransport(ctx.transport)) {
58458
+ return JSON.stringify({ success: true, taskId: task.id, status: task.status });
58459
+ }
58460
+ if (ctx.transport instanceof IpcTransport) {
57727
58461
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
57728
58462
  });
58463
+ const dispatchPromises = [];
58464
+ for (const node of ctx.mesh.nodes) {
58465
+ const isLocalNode = ctx.localMachineId && node.machineId === ctx.localMachineId || ctx.localDaemonId && node.daemonId === ctx.localDaemonId;
58466
+ if (isLocalNode || !node.daemonId) continue;
58467
+ dispatchPromises.push(
58468
+ ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
58469
+ if (result.success) {
58470
+ try {
58471
+ appendLedgerEntry(ctx.mesh.id, {
58472
+ kind: "task_dispatched",
58473
+ nodeId: node.id,
58474
+ sessionId: result.sessionId,
58475
+ payload: { message: args.message, via: "p2p_direct", taskId: task.id }
58476
+ });
58477
+ } catch {
58478
+ }
58479
+ }
58480
+ }).catch(() => {
58481
+ })
58482
+ );
58483
+ }
58484
+ Promise.all(dispatchPromises).catch(() => {
58485
+ });
58486
+ return JSON.stringify({ success: true, taskId: task.id, status: task.status });
57729
58487
  }
57730
58488
  return JSON.stringify({ success: true, taskId: task.id, status: task.status });
57731
58489
  } catch (e) {
@@ -57734,8 +58492,51 @@ async function meshEnqueueTask(ctx, args) {
57734
58492
  }
57735
58493
  async function meshViewQueue(ctx, args) {
57736
58494
  try {
57737
- const queue = getQueue(ctx.mesh.id, { status: args.status });
57738
- return JSON.stringify({ success: true, queue }, null, 2);
58495
+ const queue = annotateQueueStaleness(getQueue(ctx.mesh.id, { status: args.status }));
58496
+ const staleAssignedTasks = queue.filter((task) => task?.status === "assigned" && task?.staleAssigned);
58497
+ return JSON.stringify({
58498
+ success: true,
58499
+ queue,
58500
+ staleAssignedTasks,
58501
+ staleAssignedCount: staleAssignedTasks.length,
58502
+ // Back-compat alias for callers already reading the first hardening payload.
58503
+ staleAssignments: staleAssignedTasks
58504
+ }, null, 2);
58505
+ } catch (e) {
58506
+ return JSON.stringify({ success: false, error: e.message });
58507
+ }
58508
+ }
58509
+ async function meshQueueCancel(ctx, args) {
58510
+ try {
58511
+ const taskId = (args.task_id || args.taskId || "").trim();
58512
+ if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
58513
+ const task = cancelTask(ctx.mesh.id, taskId, { reason: args.reason });
58514
+ if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
58515
+ return JSON.stringify({ success: true, task }, null, 2);
58516
+ } catch (e) {
58517
+ return JSON.stringify({ success: false, error: e.message });
58518
+ }
58519
+ }
58520
+ async function meshQueueRequeue(ctx, args) {
58521
+ try {
58522
+ const taskId = (args.task_id || args.taskId || "").trim();
58523
+ if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
58524
+ const targetNodeId = (args.target_node_id || args.targetNodeId || "").trim() || void 0;
58525
+ const targetSessionId = (args.target_session_id || args.targetSessionId || "").trim() || void 0;
58526
+ const keepTargetSession = args.keep_target_session === true || args.keepTargetSession === true;
58527
+ const task = requeueTask(ctx.mesh.id, taskId, {
58528
+ reason: args.reason,
58529
+ targetNodeId,
58530
+ targetSessionId,
58531
+ clearTargetNode: args.clear_target_node === true || args.clearTargetNode === true,
58532
+ clearTargetSession: targetSessionId ? false : !keepTargetSession
58533
+ });
58534
+ if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
58535
+ if (isLocalTransport(ctx.transport)) {
58536
+ ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
58537
+ });
58538
+ }
58539
+ return JSON.stringify({ success: true, task }, null, 2);
57739
58540
  } catch (e) {
57740
58541
  return JSON.stringify({ success: false, error: e.message });
57741
58542
  }
@@ -57745,6 +58546,24 @@ async function meshSendTask(ctx, args) {
57745
58546
  if (node.policy?.readOnly) {
57746
58547
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
57747
58548
  }
58549
+ const duplicate = hasRecentDuplicateDispatch(ctx, args);
58550
+ if (duplicate.duplicate) {
58551
+ return JSON.stringify({
58552
+ success: true,
58553
+ duplicate: true,
58554
+ dispatched: false,
58555
+ warning: "Duplicate mesh_send_task suppressed: the same node/session/message was dispatched recently.",
58556
+ nodeId: args.node_id,
58557
+ sessionId: args.session_id,
58558
+ source: duplicate.source,
58559
+ previousDispatch: duplicate.entry ? {
58560
+ id: duplicate.entry.id,
58561
+ timestamp: duplicate.entry.timestamp || duplicate.entry.updatedAt || duplicate.entry.createdAt,
58562
+ nodeId: duplicate.entry.nodeId || duplicate.entry.targetNodeId || duplicate.entry.assignedNodeId,
58563
+ sessionId: duplicate.entry.sessionId || duplicate.entry.targetSessionId || duplicate.entry.assignedSessionId
58564
+ } : void 0
58565
+ });
58566
+ }
57748
58567
  try {
57749
58568
  if (!isLocalTransport(ctx.transport) && node.daemonId) {
57750
58569
  const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
@@ -57754,11 +58573,66 @@ async function meshSendTask(ctx, args) {
57754
58573
  });
57755
58574
  return JSON.stringify(res);
57756
58575
  }
57757
- const task = enqueueTask(ctx.mesh.id, args.message, { targetNodeId: args.node_id });
57758
- if (ctx.transport instanceof IpcTransport && node.daemonId && node.daemonId !== ctx.localDaemonId) {
57759
- ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
58576
+ const isLocalNode = ctx.localMachineId && node.machineId === ctx.localMachineId || ctx.localDaemonId && node.daemonId === ctx.localDaemonId;
58577
+ if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
58578
+ const cached2 = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
58579
+ const result = await ipcDispatchToRemoteAgent(ctx, node, {
58580
+ session_id: args.session_id,
58581
+ message: args.message,
58582
+ providerType: cached2?.providerType
57760
58583
  });
57761
- } else if (isLocalTransport(ctx.transport)) {
58584
+ if (result.success) {
58585
+ const dispatchedSessionId = args.session_id || result.sessionId;
58586
+ try {
58587
+ appendLedgerEntry(ctx.mesh.id, {
58588
+ kind: "task_dispatched",
58589
+ nodeId: args.node_id,
58590
+ sessionId: dispatchedSessionId,
58591
+ payload: {
58592
+ message: args.message,
58593
+ via: "p2p_direct",
58594
+ ...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
58595
+ }
58596
+ });
58597
+ } catch {
58598
+ }
58599
+ }
58600
+ return JSON.stringify({ ...result, nodeId: args.node_id, dispatched: result.success === true });
58601
+ }
58602
+ if (args.session_id && isLocalTransport(ctx.transport)) {
58603
+ const cached2 = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
58604
+ const dispatchResult = await commandForNode(ctx, node, "agent_command", {
58605
+ targetSessionId: args.session_id,
58606
+ ...cached2?.providerType ? { agentType: cached2.providerType, cliType: cached2.providerType, providerType: cached2.providerType } : {},
58607
+ action: "send_chat",
58608
+ message: args.message
58609
+ });
58610
+ const dispatchPayload = unwrapCommandPayload(dispatchResult);
58611
+ if (dispatchPayload?.success === false || dispatchResult?.success === false) {
58612
+ return JSON.stringify({
58613
+ success: false,
58614
+ nodeId: args.node_id,
58615
+ sessionId: args.session_id,
58616
+ error: dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task"
58617
+ });
58618
+ }
58619
+ try {
58620
+ appendLedgerEntry(ctx.mesh.id, {
58621
+ kind: "task_dispatched",
58622
+ nodeId: args.node_id,
58623
+ sessionId: args.session_id,
58624
+ providerType: cached2?.providerType,
58625
+ payload: { message: args.message, via: "local_direct" }
58626
+ });
58627
+ } catch {
58628
+ }
58629
+ return JSON.stringify({ success: true, dispatched: true, nodeId: args.node_id, sessionId: args.session_id });
58630
+ }
58631
+ const task = enqueueTask(ctx.mesh.id, args.message, {
58632
+ targetNodeId: args.node_id,
58633
+ targetSessionId: args.session_id
58634
+ });
58635
+ if (isLocalTransport(ctx.transport) || ctx.transport instanceof IpcTransport) {
57762
58636
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
57763
58637
  });
57764
58638
  }
@@ -57768,7 +58642,10 @@ async function meshSendTask(ctx, args) {
57768
58642
  }
57769
58643
  }
57770
58644
  async function meshReadChat(ctx, args) {
57771
- const node = await findNodeWithRefresh(ctx, args.node_id);
58645
+ const node = await findOptionalNodeWithRefresh(ctx, args.node_id);
58646
+ if (!node) {
58647
+ return JSON.stringify(buildMissingNodeReadChatRecovery(ctx, args), null, 2);
58648
+ }
57772
58649
  if (isLocalTransport(ctx.transport)) {
57773
58650
  const cached2 = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
57774
58651
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached2?.providerSessionId;
@@ -57871,19 +58748,28 @@ async function meshLaunchSession(ctx, args) {
57871
58748
  const coordinatorNode = resolveCoordinatorNode(ctx);
57872
58749
  const coordinatorDaemonId = coordinatorNode?.daemonId || ctx.localDaemonId;
57873
58750
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
57874
- const result = await commandForNode(ctx, node, "launch_cli", {
57875
- cliType: resolvedProviderType,
57876
- dir: node.workspace,
57877
- settings: {
57878
- meshNodeFor: ctx.mesh.id,
57879
- meshNodeId: args.node_id,
57880
- spawnedSessionVisibility,
57881
- ...coordinatorDaemonId ? { meshCoordinatorDaemonId: coordinatorDaemonId } : {},
57882
- ...coordinatorNode?.id ? { meshCoordinatorNodeId: coordinatorNode.id } : {},
57883
- launchedByCoordinator: true
57884
- }
57885
- });
58751
+ let result;
58752
+ try {
58753
+ result = await commandForNode(ctx, node, "launch_cli", {
58754
+ cliType: resolvedProviderType,
58755
+ dir: node.workspace,
58756
+ settings: {
58757
+ meshNodeFor: ctx.mesh.id,
58758
+ meshNodeId: args.node_id,
58759
+ spawnedSessionVisibility,
58760
+ ...coordinatorDaemonId ? { meshCoordinatorDaemonId: coordinatorDaemonId } : {},
58761
+ ...coordinatorNode?.id ? { meshCoordinatorNodeId: coordinatorNode.id } : {},
58762
+ launchedByCoordinator: true
58763
+ }
58764
+ });
58765
+ } catch (e) {
58766
+ return JSON.stringify(recordRecoverableLaunchFailure(ctx, node, resolvedProviderType, e), null, 2);
58767
+ }
57886
58768
  const launchPayload = extractLaunchPayload(result);
58769
+ if (launchPayload?.success === false || result?.success === false) {
58770
+ const launchError = new Error(launchPayload?.error || result?.error || "launch_cli rejected the session launch");
58771
+ return JSON.stringify(recordRecoverableLaunchFailure(ctx, node, resolvedProviderType, launchError), null, 2);
58772
+ }
57887
58773
  const runtimeSessionId = typeof launchPayload?.sessionId === "string" ? launchPayload.sessionId : typeof launchPayload?.id === "string" ? launchPayload.id : typeof launchPayload?.runtimeSessionId === "string" ? launchPayload.runtimeSessionId : "";
57888
58774
  const providerSessionId = typeof launchPayload?.providerSessionId === "string" && launchPayload.providerSessionId.trim() ? launchPayload.providerSessionId.trim() : void 0;
57889
58775
  if (runtimeSessionId) {
@@ -57902,7 +58788,8 @@ async function meshLaunchSession(ctx, args) {
57902
58788
  });
57903
58789
  } catch {
57904
58790
  }
57905
- if (ctx.transport instanceof IpcTransport && node.daemonId && node.daemonId !== ctx.localDaemonId) {
58791
+ const isLocalNode = ctx.localMachineId && node.machineId === ctx.localMachineId || ctx.localDaemonId && node.daemonId === ctx.localDaemonId;
58792
+ if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
57906
58793
  ctx.transport.meshCommand(node.daemonId, "trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
57907
58794
  });
57908
58795
  } else if (isLocalTransport(ctx.transport)) {
@@ -57952,7 +58839,7 @@ async function meshLaunchSession(ctx, args) {
57952
58839
  }
57953
58840
  return JSON.stringify({ ...res, resolvedProviderType }, null, 2);
57954
58841
  } catch (e) {
57955
- return JSON.stringify({ success: false, error: e.message });
58842
+ return JSON.stringify(recordRecoverableLaunchFailure(ctx, node, resolvedProviderType, e), null, 2);
57956
58843
  }
57957
58844
  } else {
57958
58845
  return JSON.stringify({ error: "Cloud mesh launch_session requires node daemonId" });
@@ -59840,6 +60727,15 @@ async function startMcpServer(opts) {
59840
60727
  process.exit(1);
59841
60728
  }
59842
60729
  let localDaemonId;
60730
+ let localMachineId;
60731
+ if (transport instanceof LocalTransport || transport instanceof IpcTransport) {
60732
+ try {
60733
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
60734
+ const cfg = loadConfig2();
60735
+ if (cfg.registeredMachineId) localMachineId = cfg.registeredMachineId;
60736
+ } catch {
60737
+ }
60738
+ }
59843
60739
  if (transport instanceof IpcTransport) {
59844
60740
  try {
59845
60741
  const statusResult = await transport.getStatus();
@@ -59848,7 +60744,7 @@ async function startMcpServer(opts) {
59848
60744
  } catch {
59849
60745
  }
59850
60746
  }
59851
- const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {} };
60747
+ const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {} };
59852
60748
  const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
59853
60749
  const server2 = new import_server.Server(
59854
60750
  { name: "adhdev-mcp-server", version: "0.9.76" },
@@ -59888,6 +60784,12 @@ async function startMcpServer(opts) {
59888
60784
  case "mesh_view_queue":
59889
60785
  text = await meshViewQueue(meshCtx, a);
59890
60786
  break;
60787
+ case "mesh_queue_cancel":
60788
+ text = await meshQueueCancel(meshCtx, a);
60789
+ break;
60790
+ case "mesh_queue_requeue":
60791
+ text = await meshQueueRequeue(meshCtx, a);
60792
+ break;
59891
60793
  case "mesh_send_task":
59892
60794
  text = await meshSendTask(meshCtx, a);
59893
60795
  break;