@adhdev/daemon-core 0.9.77-rc.4 → 0.9.77-rc.40

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.
package/dist/index.js CHANGED
@@ -978,6 +978,17 @@ var init_mesh_ledger = __esm({
978
978
  });
979
979
 
980
980
  // src/mesh/mesh-work-queue.ts
981
+ var mesh_work_queue_exports = {};
982
+ __export(mesh_work_queue_exports, {
983
+ cancelTask: () => cancelTask,
984
+ claimNextTask: () => claimNextTask,
985
+ enqueueTask: () => enqueueTask,
986
+ getMeshQueueStats: () => getMeshQueueStats,
987
+ getQueue: () => getQueue,
988
+ requeueTask: () => requeueTask,
989
+ updateSessionTaskStatus: () => updateSessionTaskStatus,
990
+ updateTaskStatus: () => updateTaskStatus
991
+ });
981
992
  function getQueuePath(meshId) {
982
993
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
983
994
  return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.json`);
@@ -1004,6 +1015,7 @@ function enqueueTask(meshId, message, opts) {
1004
1015
  message,
1005
1016
  status: "pending",
1006
1017
  targetNodeId: opts?.targetNodeId,
1018
+ targetSessionId: opts?.targetSessionId,
1007
1019
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1008
1020
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1009
1021
  };
@@ -1021,9 +1033,14 @@ function getQueue(meshId, opts) {
1021
1033
  }
1022
1034
  function claimNextTask(meshId, nodeId, sessionId) {
1023
1035
  const queue = readQueue(meshId);
1024
- let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId);
1036
+ const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
1037
+ if (hasActiveAssignment) return null;
1038
+ let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
1039
+ if (targetIdx === -1) {
1040
+ targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
1041
+ }
1025
1042
  if (targetIdx === -1) {
1026
- targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId);
1043
+ targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
1027
1044
  }
1028
1045
  if (targetIdx === -1) return null;
1029
1046
  const entry = queue[targetIdx];
@@ -1043,6 +1060,40 @@ function updateTaskStatus(meshId, taskId, status) {
1043
1060
  writeQueue(meshId, queue);
1044
1061
  return queue[idx];
1045
1062
  }
1063
+ function cancelTask(meshId, taskId, opts) {
1064
+ const queue = readQueue(meshId);
1065
+ const idx = queue.findIndex((q) => q.id === taskId);
1066
+ if (idx === -1) return null;
1067
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1068
+ queue[idx].status = "cancelled";
1069
+ queue[idx].updatedAt = now;
1070
+ queue[idx].cancelledAt = now;
1071
+ if (opts?.reason) queue[idx].cancelReason = opts.reason;
1072
+ writeQueue(meshId, queue);
1073
+ return queue[idx];
1074
+ }
1075
+ function requeueTask(meshId, taskId, opts) {
1076
+ const queue = readQueue(meshId);
1077
+ const idx = queue.findIndex((q) => q.id === taskId);
1078
+ if (idx === -1) return null;
1079
+ const entry = queue[idx];
1080
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1081
+ entry.status = "pending";
1082
+ delete entry.assignedNodeId;
1083
+ delete entry.assignedSessionId;
1084
+ delete entry.cancelledAt;
1085
+ delete entry.cancelReason;
1086
+ if (opts?.clearTargetNode) delete entry.targetNodeId;
1087
+ if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
1088
+ if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
1089
+ if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
1090
+ entry.updatedAt = now;
1091
+ entry.requeuedAt = now;
1092
+ entry.requeueCount = (entry.requeueCount || 0) + 1;
1093
+ if (opts?.reason) entry.requeueReason = opts.reason;
1094
+ writeQueue(meshId, queue);
1095
+ return entry;
1096
+ }
1046
1097
  function updateSessionTaskStatus(meshId, sessionId, status) {
1047
1098
  const queue = readQueue(meshId);
1048
1099
  for (let i = queue.length - 1; i >= 0; i--) {
@@ -1061,7 +1112,14 @@ function getMeshQueueStats(meshId) {
1061
1112
  pending: queue.filter((q) => q.status === "pending").length,
1062
1113
  assigned: queue.filter((q) => q.status === "assigned").length,
1063
1114
  completed: queue.filter((q) => q.status === "completed").length,
1064
- failed: queue.filter((q) => q.status === "failed").length
1115
+ failed: queue.filter((q) => q.status === "failed").length,
1116
+ cancelled: queue.filter((q) => q.status === "cancelled").length,
1117
+ activeAssignments: queue.filter((q) => q.status === "assigned").map((q) => ({
1118
+ id: q.id,
1119
+ nodeId: q.assignedNodeId,
1120
+ sessionId: q.assignedSessionId,
1121
+ message: q.message
1122
+ }))
1065
1123
  };
1066
1124
  }
1067
1125
  var import_fs4, import_path4, import_crypto5;
@@ -1304,6 +1362,9 @@ function drainPendingMeshCoordinatorEvents() {
1304
1362
  function readNonEmptyString(value) {
1305
1363
  return typeof value === "string" && value.trim() ? value.trim() : "";
1306
1364
  }
1365
+ function resolveEventSessionId(event, fallback) {
1366
+ return readNonEmptyString(event.targetSessionId) || readNonEmptyString(event.sessionId) || readNonEmptyString(event.instanceId) || readNonEmptyString(fallback);
1367
+ }
1307
1368
  function isMeshCoordinatorEvent(eventName) {
1308
1369
  return typeof eventName === "string" && MESH_COORDINATOR_EVENTS.has(eventName);
1309
1370
  }
@@ -1315,38 +1376,74 @@ function formatCompletionMetadata(event) {
1315
1376
  ].filter(Boolean);
1316
1377
  return parts.length > 0 ? ` (${parts.join("; ")})` : "";
1317
1378
  }
1379
+ function getMeshWithCache(components, meshId) {
1380
+ const localMesh = getMesh(meshId);
1381
+ if (localMesh) return localMesh;
1382
+ return components.router?.getCachedInlineMesh(meshId);
1383
+ }
1318
1384
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
1319
1385
  const task = claimNextTask(meshId, nodeId, sessionId);
1320
- if (!task) return false;
1386
+ if (!task) {
1387
+ return false;
1388
+ }
1321
1389
  LOG.info("MeshQueue", `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
1390
+ const mesh = getMeshWithCache(components, meshId);
1391
+ const node = mesh?.nodes.find((n) => n.id === nodeId);
1392
+ if (node?.daemonId && components.dispatchMeshCommand) {
1393
+ const isLocalNode = components.cliManager.adapters.has(sessionId);
1394
+ if (!isLocalNode) {
1395
+ components.dispatchMeshCommand(node.daemonId, "agent_command", {
1396
+ targetSessionId: sessionId,
1397
+ cliType: providerType,
1398
+ action: "send_chat",
1399
+ message: task.message
1400
+ }).catch((e) => {
1401
+ LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
1402
+ updateTaskStatus(meshId, task.id, "failed");
1403
+ });
1404
+ return true;
1405
+ }
1406
+ }
1322
1407
  components.cliManager.handleCliCommand("agent_command", {
1323
1408
  targetSessionId: sessionId,
1324
1409
  cliType: providerType,
1325
1410
  action: "send_chat",
1326
- input: task.message
1411
+ message: task.message
1327
1412
  }).catch((e) => {
1328
- LOG.error("MeshQueue", `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
1413
+ LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
1414
+ updateTaskStatus(meshId, task.id, "failed");
1329
1415
  });
1330
1416
  return true;
1331
1417
  }
1332
1418
  function triggerMeshQueue(components, meshId) {
1333
- const mesh = getMesh(meshId);
1419
+ const mesh = getMeshWithCache(components, meshId);
1334
1420
  if (!mesh) return;
1335
1421
  const cliInstances = components.instanceManager.getByCategory("cli");
1336
1422
  for (const inst of cliInstances) {
1337
1423
  const state = inst.getState();
1338
1424
  const settings = state.settings || {};
1339
1425
  const instMeshId = readNonEmptyString(settings.meshNodeFor);
1340
- if (instMeshId !== meshId && !settings.launchedByCoordinator) continue;
1426
+ if (instMeshId !== meshId) continue;
1341
1427
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
1342
1428
  if (!nodeId) continue;
1343
- if (state.status !== "idle" && state.status !== "stopped" && state.activeChat?.status !== "waiting_input") continue;
1429
+ const status = readNonEmptyString(state.status).toLowerCase();
1430
+ if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
1431
+ if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
1344
1432
  const sessionId = state.instanceId;
1345
1433
  const providerType = state.type || readNonEmptyString(settings.providerType);
1346
1434
  if (providerType) {
1347
1435
  tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
1348
1436
  }
1349
1437
  }
1438
+ for (const [key, idle] of remoteIdleSessions.entries()) {
1439
+ const node = mesh.nodes.find((n) => n.id === idle.nodeId);
1440
+ if (node) {
1441
+ const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
1442
+ if (assigned) {
1443
+ remoteIdleSessions.delete(key);
1444
+ }
1445
+ }
1446
+ }
1350
1447
  }
1351
1448
  function buildMeshSystemMessage(args) {
1352
1449
  const metadata = formatCompletionMetadata(args.metadataEvent);
@@ -1393,20 +1490,67 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
1393
1490
  return "";
1394
1491
  }
1395
1492
  function injectMeshSystemMessage(components, args) {
1493
+ let completedTaskForLedger = null;
1396
1494
  if (args.event === "agent:generating_completed") {
1397
- const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1398
- const nodeId = readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1495
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1496
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1399
1497
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
1400
1498
  if (sessionId) {
1401
- updateSessionTaskStatus(args.meshId, sessionId, "completed");
1499
+ const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed");
1500
+ completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
1402
1501
  if (nodeId && providerType) {
1403
1502
  setTimeout(() => {
1404
1503
  tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
1405
1504
  }, 500);
1406
1505
  }
1407
1506
  }
1507
+ } else if (args.event === "agent:ready") {
1508
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1509
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1510
+ const providerType = readNonEmptyString(args.metadataEvent.providerType);
1511
+ const completedTask = sessionId ? updateSessionTaskStatus(args.meshId, sessionId, "completed") : null;
1512
+ if (completedTask) {
1513
+ completedTaskForLedger = { id: completedTask.id };
1514
+ try {
1515
+ appendLedgerEntry(args.meshId, {
1516
+ kind: "task_completed",
1517
+ nodeId: nodeId || void 0,
1518
+ sessionId,
1519
+ providerType: providerType || void 0,
1520
+ payload: {
1521
+ event: args.event,
1522
+ nodeLabel: args.nodeLabel,
1523
+ taskId: completedTask.id,
1524
+ completedViaReady: true,
1525
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
1526
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
1527
+ }
1528
+ });
1529
+ } catch (e) {
1530
+ LOG.warn("MeshLedger", `Failed to record task_completed from ready: ${e?.message || e}`);
1531
+ }
1532
+ }
1533
+ if (sessionId && nodeId && providerType) {
1534
+ remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
1535
+ setTimeout(() => {
1536
+ const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
1537
+ if (assigned) {
1538
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
1539
+ }
1540
+ }, 500);
1541
+ }
1542
+ } else if (args.event === "agent:generating_started") {
1543
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1544
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1545
+ if (sessionId && nodeId) {
1546
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
1547
+ }
1408
1548
  } else if (args.event === "agent:stopped") {
1409
- const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1549
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1550
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1551
+ if (sessionId && nodeId) {
1552
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
1553
+ }
1410
1554
  if (sessionId) {
1411
1555
  updateSessionTaskStatus(args.meshId, sessionId, "failed");
1412
1556
  }
@@ -1416,13 +1560,15 @@ function injectMeshSystemMessage(components, args) {
1416
1560
  try {
1417
1561
  appendLedgerEntry(args.meshId, {
1418
1562
  kind: ledgerKind,
1419
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1420
- sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1563
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1564
+ sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || void 0,
1421
1565
  providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
1422
1566
  payload: {
1423
1567
  event: args.event,
1424
1568
  nodeLabel: args.nodeLabel,
1425
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
1569
+ taskId: completedTaskForLedger?.id || void 0,
1570
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
1571
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
1426
1572
  }
1427
1573
  });
1428
1574
  } catch (e) {
@@ -1435,8 +1581,8 @@ function injectMeshSystemMessage(components, args) {
1435
1581
  const mesh = getMesh(args.meshId);
1436
1582
  const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
1437
1583
  recoveryContext = getSessionRecoveryContext(args.meshId, {
1438
- sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1439
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1584
+ sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || void 0,
1585
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1440
1586
  maxRetries
1441
1587
  });
1442
1588
  recoveryContext.failedProviderType = readNonEmptyString(args.metadataEvent.providerType) || null;
@@ -1531,12 +1677,14 @@ function handleMeshForwardEvent(components, payload) {
1531
1677
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
1532
1678
  return injectMeshSystemMessage(components, {
1533
1679
  meshId,
1680
+ nodeId,
1534
1681
  nodeLabel,
1535
1682
  event: eventName,
1536
1683
  metadataEvent: {
1537
- targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
1684
+ targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
1538
1685
  providerType: readNonEmptyString(payload.providerType),
1539
- providerSessionId: readNonEmptyString(payload.providerSessionId)
1686
+ providerSessionId: readNonEmptyString(payload.providerSessionId),
1687
+ finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary)
1540
1688
  }
1541
1689
  });
1542
1690
  }
@@ -1555,22 +1703,24 @@ function setupMeshEventForwarding(components) {
1555
1703
  const meshIdFromRuntime = readNonEmptyString(settings.meshNodeFor);
1556
1704
  const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
1557
1705
  if (!isMeshDelegate) return;
1558
- const mesh = meshIdFromRuntime ? getMesh(meshIdFromRuntime) : getMeshByRepo(workspace);
1706
+ const mesh = meshIdFromRuntime ? getMeshWithCache(components, meshIdFromRuntime) : getMeshByRepo(workspace);
1559
1707
  const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
1560
1708
  if (!meshId) return;
1561
1709
  const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
1562
1710
  const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
1711
+ const resolvedNodeId = targetNode?.id || runtimeNodeId;
1563
1712
  const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
1564
1713
  injectMeshSystemMessage(components, {
1565
1714
  meshId,
1566
1715
  sourceInstanceId: instanceId,
1716
+ nodeId: resolvedNodeId,
1567
1717
  nodeLabel,
1568
1718
  event: event.event,
1569
1719
  metadataEvent: event
1570
1720
  });
1571
1721
  });
1572
1722
  }
1573
- var MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
1723
+ var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
1574
1724
  var init_mesh_events = __esm({
1575
1725
  "src/mesh/mesh-events.ts"() {
1576
1726
  "use strict";
@@ -1578,12 +1728,15 @@ var init_mesh_events = __esm({
1578
1728
  init_logger();
1579
1729
  init_mesh_ledger();
1580
1730
  init_mesh_work_queue();
1731
+ remoteIdleSessions = /* @__PURE__ */ new Map();
1581
1732
  MAX_PENDING_EVENTS = 50;
1582
1733
  pendingMeshCoordinatorEvents = [];
1583
1734
  MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
1735
+ "agent:generating_started",
1584
1736
  "agent:generating_completed",
1585
1737
  "agent:waiting_approval",
1586
1738
  "agent:stopped",
1739
+ "agent:ready",
1587
1740
  "monitor:long_generating"
1588
1741
  ]);
1589
1742
  EVENT_TO_LEDGER_KIND = {
@@ -2699,6 +2852,8 @@ var init_provider_cli_adapter = __esm({
2699
2852
  statusHistory = [];
2700
2853
  // ─── CLI Scripts (script-based parsing) ───
2701
2854
  cliScripts;
2855
+ /** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
2856
+ scriptState = null;
2702
2857
  runtimeSettings = {};
2703
2858
  /** Full accumulated rendered PTY transcript for parser/readback use */
2704
2859
  accumulatedBuffer = "";
@@ -2777,7 +2932,7 @@ ${lastSnapshot}`;
2777
2932
  }
2778
2933
  getFreshParsedStatusCache() {
2779
2934
  const cached = this.parsedStatusCache;
2780
- if (cached && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.screenText === this.lastScreenText && cached.currentStatus === this.currentStatus && cached.activeModal === this.activeModal && cached.cliName === this.cliName) {
2935
+ if (cached && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.accumulatedRawBuffer === this.accumulatedRawBuffer && cached.screenText === this.lastScreenText && cached.currentStatus === this.currentStatus && cached.activeModal === this.activeModal && cached.cliName === this.cliName) {
2781
2936
  return cached.result;
2782
2937
  }
2783
2938
  return null;
@@ -2880,6 +3035,7 @@ ${lastSnapshot}`;
2880
3035
  this.cliScripts = scripts;
2881
3036
  this.parsedStatusCache = null;
2882
3037
  this.parseErrorMessage = null;
3038
+ this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
2883
3039
  const scriptNames = listCliScriptNames(scripts);
2884
3040
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
2885
3041
  }
@@ -2997,6 +3153,7 @@ ${lastSnapshot}`;
2997
3153
  this.ready = false;
2998
3154
  this.startupParseGate = false;
2999
3155
  this.spawnAt = 0;
3156
+ this.scriptState = null;
3000
3157
  this.onStatusChange?.();
3001
3158
  });
3002
3159
  this.spawnAt = Date.now();
@@ -3750,6 +3907,11 @@ ${lastSnapshot}`;
3750
3907
  };
3751
3908
  }
3752
3909
  // ─── Script Execution ──────────────────────────
3910
+ invokeCliScript(script, input) {
3911
+ const hasStateFactory = typeof this.cliScripts?.createState === "function";
3912
+ const expectsStateArgument = hasStateFactory || this.scriptState !== null || script.length >= 2;
3913
+ return expectsStateArgument ? script(this.scriptState, input) : script(input);
3914
+ }
3753
3915
  runParseSession() {
3754
3916
  if (typeof this.cliScripts?.parseSession !== "function") {
3755
3917
  this.parseErrorMessage = `${this.cliType} parseSession unavailable`;
@@ -3770,7 +3932,10 @@ ${lastSnapshot}`;
3770
3932
  scope: this.currentTurnScope,
3771
3933
  runtimeSettings: this.runtimeSettings
3772
3934
  });
3773
- const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3935
+ const session = this.invokeCliScript(
3936
+ this.cliScripts.parseSession,
3937
+ { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) }
3938
+ );
3774
3939
  this.parseErrorMessage = null;
3775
3940
  return session && typeof session === "object" ? session : null;
3776
3941
  } catch (e) {
@@ -3784,7 +3949,7 @@ ${lastSnapshot}`;
3784
3949
  if (!this.cliScripts?.detectStatus) return null;
3785
3950
  try {
3786
3951
  const screenText = this.terminalScreen.getText();
3787
- const status = this.cliScripts.detectStatus({
3952
+ const status = this.invokeCliScript(this.cliScripts.detectStatus, {
3788
3953
  tail: text.slice(-500),
3789
3954
  screenText,
3790
3955
  rawBuffer: this.accumulatedRawBuffer,
@@ -3803,7 +3968,7 @@ ${lastSnapshot}`;
3803
3968
  try {
3804
3969
  const screenText = this.terminalScreen.getText();
3805
3970
  const buffer = screenText || this.accumulatedBuffer;
3806
- return this.cliScripts.parseApproval({
3971
+ return this.invokeCliScript(this.cliScripts.parseApproval, {
3807
3972
  buffer,
3808
3973
  screenText,
3809
3974
  rawBuffer: this.accumulatedRawBuffer,
@@ -3859,7 +4024,7 @@ ${lastSnapshot}`;
3859
4024
  const screenText = this.readTerminalScreenText();
3860
4025
  const parseScreenText = this.getParseScreenText(screenText);
3861
4026
  const cached = this.parsedStatusCache;
3862
- if (cached && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.screenText === parseScreenText && cached.currentStatus === this.currentStatus && cached.activeModal === this.activeModal && cached.cliName === this.cliName) {
4027
+ if (cached && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.accumulatedRawBuffer === this.accumulatedRawBuffer && cached.screenText === parseScreenText && cached.currentStatus === this.currentStatus && cached.activeModal === this.activeModal && cached.cliName === this.cliName) {
3863
4028
  return cached.result;
3864
4029
  }
3865
4030
  const parsed = this.runParseSession();
@@ -3887,6 +4052,7 @@ ${lastSnapshot}`;
3887
4052
  currentTurnScope: this.currentTurnScope,
3888
4053
  recentOutputBuffer: this.recentOutputBuffer,
3889
4054
  accumulatedBuffer: this.accumulatedBuffer,
4055
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
3890
4056
  screenText: parseScreenText,
3891
4057
  currentStatus: this.currentStatus,
3892
4058
  activeModal: this.activeModal,
@@ -3911,7 +4077,7 @@ ${lastSnapshot}`;
3911
4077
  scope: this.currentTurnScope,
3912
4078
  runtimeSettings: this.runtimeSettings
3913
4079
  });
3914
- return await Promise.resolve(fn({
4080
+ return await Promise.resolve(fn(this.scriptState, {
3915
4081
  ...input,
3916
4082
  args: args && typeof args === "object" ? { ...args } : {}
3917
4083
  }));
@@ -4722,6 +4888,7 @@ __export(index_exports, {
4722
4888
  buildThoughtChatMessage: () => buildThoughtChatMessage,
4723
4889
  buildToolChatMessage: () => buildToolChatMessage,
4724
4890
  buildUserChatMessage: () => buildUserChatMessage,
4891
+ cancelTask: () => cancelTask,
4725
4892
  claimNextTask: () => claimNextTask,
4726
4893
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
4727
4894
  classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush,
@@ -4765,6 +4932,7 @@ __export(index_exports, {
4765
4932
  getLogLevel: () => getLogLevel,
4766
4933
  getMesh: () => getMesh,
4767
4934
  getMeshByRepo: () => getMeshByRepo,
4935
+ getMeshQueueStats: () => getMeshQueueStats,
4768
4936
  getNpmExecOptions: () => getNpmExecOptions,
4769
4937
  getQueue: () => getQueue,
4770
4938
  getRecentActivity: () => getRecentActivity,
@@ -4833,6 +5001,7 @@ __export(index_exports, {
4833
5001
  registerExtensionProviders: () => registerExtensionProviders,
4834
5002
  removeNode: () => removeNode,
4835
5003
  removeWorktree: () => removeWorktree,
5004
+ requeueTask: () => requeueTask,
4836
5005
  resetConfig: () => resetConfig,
4837
5006
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
4838
5007
  resetState: () => resetState,
@@ -15205,11 +15374,13 @@ async function handleOpenPanel(h, args) {
15205
15374
  async function handlePtyInput(h, args) {
15206
15375
  const { cliType, data, targetSessionId } = args || {};
15207
15376
  if (!data) return { success: false, error: "data required" };
15377
+ const cleanData = typeof data === "string" ? data.replace(/\x1b\[[?>][0-9;]*c/g, "") : data;
15378
+ if (!cleanData) return { success: true };
15208
15379
  const adapter = h.getCliAdapter(targetSessionId || cliType);
15209
15380
  if (!adapter || typeof adapter.writeRaw !== "function") {
15210
15381
  return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
15211
15382
  }
15212
- await adapter.writeRaw(data);
15383
+ await adapter.writeRaw(cleanData);
15213
15384
  return { success: true };
15214
15385
  }
15215
15386
  function handlePtyResize(_h, args) {
@@ -16839,6 +17010,8 @@ var CliProviderInstance = class {
16839
17010
  this.completedDebounceTimer = null;
16840
17011
  }, 3e3);
16841
17012
  }
17013
+ } else if (newStatus === "idle" && this.lastStatus === "starting") {
17014
+ this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
16842
17015
  } else if (newStatus === "stopped") {
16843
17016
  if (this.generatingDebounceTimer) {
16844
17017
  clearTimeout(this.generatingDebounceTimer);
@@ -18582,9 +18755,6 @@ function buildCoordinatorDelegatedCliLaunchOptions(input) {
18582
18755
  const cliType = String(input.cliType || "").trim();
18583
18756
  const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
18584
18757
  const env = { ...input.env || {}, ...COORDINATOR_DELEGATED_ENV_UNSETS };
18585
- if (cliType === "hermes-cli" && !hasCliArg(cliArgs, "--ignore-user-config")) {
18586
- cliArgs.unshift("--ignore-user-config");
18587
- }
18588
18758
  if (cliType === "claude-cli" && !hasCliArg(cliArgs, "--mcp-config")) {
18589
18759
  cliArgs.unshift("--mcp-config", ensureEmptyDelegatedMcpConfig(input.workspace));
18590
18760
  }
@@ -21830,7 +22000,9 @@ function resolveHermesMeshCoordinatorSetup(options) {
21830
22000
  const mcpServer = resolveAdhdevMcpServerLaunch({
21831
22001
  meshId: options.meshId,
21832
22002
  nodeExecutable: options.nodeExecutable,
21833
- adhdevMcpEntryPath: options.adhdevMcpEntryPath
22003
+ adhdevMcpEntryPath: options.adhdevMcpEntryPath,
22004
+ adhdevMcpTransport: options.adhdevMcpTransport,
22005
+ adhdevMcpPort: options.adhdevMcpPort
21834
22006
  });
21835
22007
  if (!mcpServer) {
21836
22008
  return {
@@ -21897,7 +22069,9 @@ function resolveMeshCoordinatorSetup(options) {
21897
22069
  const mcpServer = resolveAdhdevMcpServerLaunch({
21898
22070
  meshId,
21899
22071
  nodeExecutable: options.nodeExecutable,
21900
- adhdevMcpEntryPath: options.adhdevMcpEntryPath
22072
+ adhdevMcpEntryPath: options.adhdevMcpEntryPath,
22073
+ adhdevMcpTransport: options.adhdevMcpTransport,
22074
+ adhdevMcpPort: options.adhdevMcpPort
21901
22075
  });
21902
22076
  if (!mcpServer) {
21903
22077
  return {
@@ -21919,6 +22093,22 @@ function resolveMeshCoordinatorSetup(options) {
21919
22093
  if (!instructions || !template?.trim()) {
21920
22094
  return { kind: "unsupported", reason: "Provider manual MCP setup is missing instructions or template" };
21921
22095
  }
22096
+ const renderedTemplate = renderMeshCoordinatorTemplate(template, {
22097
+ meshId,
22098
+ workspace,
22099
+ serverName,
22100
+ adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
22101
+ });
22102
+ const isCliCommand = !renderedTemplate.trim().includes("\n") && !renderedTemplate.trim().startsWith("{");
22103
+ if (isCliCommand) {
22104
+ return {
22105
+ kind: "cli_command",
22106
+ serverName,
22107
+ command: renderedTemplate.trim(),
22108
+ requiresRestart: mcpConfig.requiresRestart === true,
22109
+ instructions
22110
+ };
22111
+ }
21922
22112
  return {
21923
22113
  kind: "manual",
21924
22114
  serverName,
@@ -21926,12 +22116,7 @@ function resolveMeshCoordinatorSetup(options) {
21926
22116
  configPathCommand: mcpConfig.configPathCommand,
21927
22117
  requiresRestart: mcpConfig.requiresRestart === true,
21928
22118
  instructions,
21929
- template: renderMeshCoordinatorTemplate(template, {
21930
- meshId,
21931
- workspace,
21932
- serverName,
21933
- adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
21934
- })
22119
+ template: renderedTemplate
21935
22120
  };
21936
22121
  }
21937
22122
  return {
@@ -21960,11 +22145,27 @@ function resolveAdhdevMcpServerLaunch(options) {
21960
22145
  if (!entryPath) return null;
21961
22146
  const nodeExecutable = resolveMcpNodeExecutable(options.nodeExecutable);
21962
22147
  if (!nodeExecutable) return null;
22148
+ const transport = resolveMcpTransport(options.adhdevMcpTransport);
22149
+ const args = [entryPath, "--mode", transport, "--repo-mesh", options.meshId];
22150
+ const port = resolveMcpPort(options.adhdevMcpPort);
22151
+ if (port !== void 0) args.push("--port", String(port));
21963
22152
  return {
21964
22153
  command: nodeExecutable,
21965
- args: [entryPath, "--mode", "ipc", "--repo-mesh", options.meshId]
22154
+ args
21966
22155
  };
21967
22156
  }
22157
+ function resolveMcpTransport(explicitTransport) {
22158
+ if (explicitTransport === "local" || explicitTransport === "ipc") return explicitTransport;
22159
+ const envTransport = process.env.ADHDEV_COORDINATOR_MCP_TRANSPORT?.trim();
22160
+ return envTransport === "local" ? "local" : "ipc";
22161
+ }
22162
+ function resolveMcpPort(explicitPort) {
22163
+ if (typeof explicitPort === "number" && Number.isInteger(explicitPort) && explicitPort > 0) return explicitPort;
22164
+ const raw = process.env.ADHDEV_COORDINATOR_MCP_PORT?.trim();
22165
+ if (!raw) return void 0;
22166
+ const parsed = Number(raw);
22167
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
22168
+ }
21968
22169
  function resolveMcpNodeExecutable(explicitExecutable) {
21969
22170
  const explicit = explicitExecutable?.trim();
21970
22171
  if (explicit) return explicit;
@@ -23785,6 +23986,51 @@ var DaemonCommandRouter = class {
23785
23986
  return { success: false, error: e.message };
23786
23987
  }
23787
23988
  }
23989
+ case "get_mesh_queue": {
23990
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23991
+ if (!meshId) return { success: false, error: "meshId required" };
23992
+ try {
23993
+ const { getQueue: getQueue2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
23994
+ const status = Array.isArray(args?.status) ? args.status.map((s) => typeof s === "string" ? s.trim() : "").filter(Boolean) : void 0;
23995
+ const queue = getQueue2(meshId, { status });
23996
+ return { success: true, queue };
23997
+ } catch (e) {
23998
+ return { success: false, error: e.message };
23999
+ }
24000
+ }
24001
+ case "cancel_mesh_queue_task": {
24002
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
24003
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
24004
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
24005
+ try {
24006
+ const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
24007
+ const reason = typeof args?.reason === "string" ? args.reason : void 0;
24008
+ const task = cancelTask2(meshId, taskId, { reason });
24009
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
24010
+ return { success: true, task };
24011
+ } catch (e) {
24012
+ return { success: false, error: e.message };
24013
+ }
24014
+ }
24015
+ case "requeue_mesh_queue_task": {
24016
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
24017
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
24018
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
24019
+ try {
24020
+ const { requeueTask: requeueTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
24021
+ const task = requeueTask2(meshId, taskId, {
24022
+ reason: typeof args?.reason === "string" ? args.reason : void 0,
24023
+ targetNodeId: typeof args?.targetNodeId === "string" ? args.targetNodeId.trim() : void 0,
24024
+ targetSessionId: typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : void 0,
24025
+ clearTargetNode: args?.clearTargetNode === true,
24026
+ clearTargetSession: args?.clearTargetSession !== false
24027
+ });
24028
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
24029
+ return { success: true, task };
24030
+ } catch (e) {
24031
+ return { success: false, error: e.message };
24032
+ }
24033
+ }
23788
24034
  case "add_mesh_node": {
23789
24035
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23790
24036
  const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
@@ -23942,7 +24188,13 @@ var DaemonCommandRouter = class {
23942
24188
  appendLedgerEntry2(meshId, {
23943
24189
  kind: "node_removed",
23944
24190
  nodeId,
23945
- payload: { worktree: !!node?.isLocalWorktree, sessionCleanupMode }
24191
+ payload: {
24192
+ worktree: !!node?.isLocalWorktree,
24193
+ sessionCleanupMode,
24194
+ workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
24195
+ daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
24196
+ worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
24197
+ }
23946
24198
  });
23947
24199
  } catch {
23948
24200
  }
@@ -24113,6 +24365,93 @@ var DaemonCommandRouter = class {
24113
24365
  meshCoordinatorSetup: coordinatorSetup
24114
24366
  };
24115
24367
  }
24368
+ if (coordinatorSetup.kind === "cli_command") {
24369
+ let cliCmdSystemPrompt = "";
24370
+ try {
24371
+ cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType });
24372
+ } catch (error) {
24373
+ const message = error?.message || String(error);
24374
+ LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
24375
+ return {
24376
+ success: false,
24377
+ code: "mesh_coordinator_prompt_failed",
24378
+ error: `Failed to build Repo Mesh coordinator prompt: ${message}`,
24379
+ meshId,
24380
+ cliType,
24381
+ workspace
24382
+ };
24383
+ }
24384
+ try {
24385
+ const { execFileSync: execCmdSync } = await import("child_process");
24386
+ const cmdParts = coordinatorSetup.command.trim().split(/\s+/);
24387
+ const [regCmd, ...regArgs] = cmdParts;
24388
+ LOG.info("MeshCoordinator", `Running MCP registration: ${coordinatorSetup.command}`);
24389
+ execCmdSync(regCmd, regArgs, { stdio: "pipe", timeout: 15e3 });
24390
+ } catch (error) {
24391
+ LOG.warn("MeshCoordinator", `MCP registration command failed (may be pre-registered): ${error?.message || error}`);
24392
+ }
24393
+ const cliCmdArgs = [];
24394
+ const cliCmdEnv = {};
24395
+ if (cliCmdSystemPrompt) {
24396
+ if (cliType === "codex-cli") {
24397
+ cliCmdArgs.push("-c", `developer_instructions=${JSON.stringify(cliCmdSystemPrompt)}`);
24398
+ } else if (cliType === "gemini-cli") {
24399
+ try {
24400
+ const { writeFileSync: wfs, existsSync: efs, readFileSync: rfs } = await import("fs");
24401
+ const geminiMdPath = `${workspace}/GEMINI.md`;
24402
+ const marker = "<!-- adhdev-mesh-coordinator-prompt -->";
24403
+ const markerEnd = "<!-- /adhdev-mesh-coordinator-prompt -->";
24404
+ const block = `${marker}
24405
+ ${cliCmdSystemPrompt}
24406
+ ${markerEnd}`;
24407
+ if (efs(geminiMdPath)) {
24408
+ const existing = rfs(geminiMdPath, "utf-8");
24409
+ const replaced = existing.replace(
24410
+ new RegExp(`${marker}[\\s\\S]*?${markerEnd}`, "g"),
24411
+ block
24412
+ );
24413
+ wfs(geminiMdPath, replaced.includes(marker) ? replaced : `${existing}
24414
+
24415
+ ${block}`);
24416
+ } else {
24417
+ wfs(geminiMdPath, block);
24418
+ }
24419
+ LOG.info("MeshCoordinator", `Wrote coordinator prompt to ${workspace}/GEMINI.md`);
24420
+ } catch (e) {
24421
+ LOG.warn("MeshCoordinator", `Could not write GEMINI.md: ${e?.message || e}`);
24422
+ }
24423
+ }
24424
+ }
24425
+ const cliCmdLaunch = await this.deps.cliManager.handleCliCommand("launch_cli", {
24426
+ cliType,
24427
+ dir: workspace,
24428
+ cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : void 0,
24429
+ env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : void 0,
24430
+ settings: { meshCoordinatorFor: meshId }
24431
+ });
24432
+ if (!cliCmdLaunch?.success) {
24433
+ return { success: false, error: cliCmdLaunch?.error || "Failed to launch CLI session" };
24434
+ }
24435
+ LOG.info("MeshCoordinator", `Launched ${cliType} coordinator (cli_command) for mesh ${meshId}`);
24436
+ try {
24437
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
24438
+ appendLedgerEntry2(meshId, {
24439
+ kind: "coordinator_started",
24440
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
24441
+ providerType: cliType,
24442
+ payload: { workspace }
24443
+ });
24444
+ } catch {
24445
+ }
24446
+ return {
24447
+ success: true,
24448
+ meshId,
24449
+ cliType,
24450
+ workspace,
24451
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
24452
+ mcpRegistered: true
24453
+ };
24454
+ }
24116
24455
  const configFormat = coordinatorSetup.configFormat;
24117
24456
  if (configFormat !== "claude_mcp_json" && configFormat !== "hermes_config_yaml") {
24118
24457
  return {
@@ -24167,9 +24506,11 @@ var DaemonCommandRouter = class {
24167
24506
  args: coordinatorSetup.mcpServer.args
24168
24507
  };
24169
24508
  if (args?.inlineMesh) {
24509
+ const modeArgIndex = coordinatorSetup.mcpServer.args.findIndex((value) => value === "--mode");
24510
+ const mcpTransport = modeArgIndex >= 0 ? coordinatorSetup.mcpServer.args[modeArgIndex + 1] : "ipc";
24170
24511
  mcpServerEntry.env = {
24171
24512
  ADHDEV_INLINE_MESH: JSON.stringify(mesh),
24172
- ADHDEV_MCP_TRANSPORT: "ipc"
24513
+ ADHDEV_MCP_TRANSPORT: mcpTransport === "local" ? "local" : "ipc"
24173
24514
  };
24174
24515
  }
24175
24516
  try {
@@ -32211,7 +32552,8 @@ async function initDaemonComponents(config) {
32211
32552
  cdpManagers,
32212
32553
  sessionRegistry,
32213
32554
  detectedIdes: detectedIdesRef,
32214
- refreshProviderAvailability
32555
+ refreshProviderAvailability,
32556
+ dispatchMeshCommand: config.dispatchMeshCommand
32215
32557
  };
32216
32558
  setupMeshEventForwarding(components);
32217
32559
  return components;
@@ -32343,6 +32685,7 @@ async function shutdownDaemonComponents(components) {
32343
32685
  buildThoughtChatMessage,
32344
32686
  buildToolChatMessage,
32345
32687
  buildUserChatMessage,
32688
+ cancelTask,
32346
32689
  claimNextTask,
32347
32690
  classifyChatMessageVisibility,
32348
32691
  classifyHotChatSessionsForSubscriptionFlush,
@@ -32386,6 +32729,7 @@ async function shutdownDaemonComponents(components) {
32386
32729
  getLogLevel,
32387
32730
  getMesh,
32388
32731
  getMeshByRepo,
32732
+ getMeshQueueStats,
32389
32733
  getNpmExecOptions,
32390
32734
  getQueue,
32391
32735
  getRecentActivity,
@@ -32454,6 +32798,7 @@ async function shutdownDaemonComponents(components) {
32454
32798
  registerExtensionProviders,
32455
32799
  removeNode,
32456
32800
  removeWorktree,
32801
+ requeueTask,
32457
32802
  resetConfig,
32458
32803
  resetDebugRuntimeConfig,
32459
32804
  resetState,