@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.mjs CHANGED
@@ -973,6 +973,17 @@ var init_mesh_ledger = __esm({
973
973
  });
974
974
 
975
975
  // src/mesh/mesh-work-queue.ts
976
+ var mesh_work_queue_exports = {};
977
+ __export(mesh_work_queue_exports, {
978
+ cancelTask: () => cancelTask,
979
+ claimNextTask: () => claimNextTask,
980
+ enqueueTask: () => enqueueTask,
981
+ getMeshQueueStats: () => getMeshQueueStats,
982
+ getQueue: () => getQueue,
983
+ requeueTask: () => requeueTask,
984
+ updateSessionTaskStatus: () => updateSessionTaskStatus,
985
+ updateTaskStatus: () => updateTaskStatus
986
+ });
976
987
  import { existsSync as existsSync6, writeFileSync as writeFileSync3, readFileSync as readFileSync4 } from "fs";
977
988
  import { join as join6 } from "path";
978
989
  import { randomUUID as randomUUID5 } from "crypto";
@@ -1002,6 +1013,7 @@ function enqueueTask(meshId, message, opts) {
1002
1013
  message,
1003
1014
  status: "pending",
1004
1015
  targetNodeId: opts?.targetNodeId,
1016
+ targetSessionId: opts?.targetSessionId,
1005
1017
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1006
1018
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1007
1019
  };
@@ -1019,9 +1031,14 @@ function getQueue(meshId, opts) {
1019
1031
  }
1020
1032
  function claimNextTask(meshId, nodeId, sessionId) {
1021
1033
  const queue = readQueue(meshId);
1022
- let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId);
1034
+ const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
1035
+ if (hasActiveAssignment) return null;
1036
+ let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
1037
+ if (targetIdx === -1) {
1038
+ targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
1039
+ }
1023
1040
  if (targetIdx === -1) {
1024
- targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId);
1041
+ targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
1025
1042
  }
1026
1043
  if (targetIdx === -1) return null;
1027
1044
  const entry = queue[targetIdx];
@@ -1041,6 +1058,40 @@ function updateTaskStatus(meshId, taskId, status) {
1041
1058
  writeQueue(meshId, queue);
1042
1059
  return queue[idx];
1043
1060
  }
1061
+ function cancelTask(meshId, taskId, opts) {
1062
+ const queue = readQueue(meshId);
1063
+ const idx = queue.findIndex((q) => q.id === taskId);
1064
+ if (idx === -1) return null;
1065
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1066
+ queue[idx].status = "cancelled";
1067
+ queue[idx].updatedAt = now;
1068
+ queue[idx].cancelledAt = now;
1069
+ if (opts?.reason) queue[idx].cancelReason = opts.reason;
1070
+ writeQueue(meshId, queue);
1071
+ return queue[idx];
1072
+ }
1073
+ function requeueTask(meshId, taskId, opts) {
1074
+ const queue = readQueue(meshId);
1075
+ const idx = queue.findIndex((q) => q.id === taskId);
1076
+ if (idx === -1) return null;
1077
+ const entry = queue[idx];
1078
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1079
+ entry.status = "pending";
1080
+ delete entry.assignedNodeId;
1081
+ delete entry.assignedSessionId;
1082
+ delete entry.cancelledAt;
1083
+ delete entry.cancelReason;
1084
+ if (opts?.clearTargetNode) delete entry.targetNodeId;
1085
+ if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
1086
+ if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
1087
+ if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
1088
+ entry.updatedAt = now;
1089
+ entry.requeuedAt = now;
1090
+ entry.requeueCount = (entry.requeueCount || 0) + 1;
1091
+ if (opts?.reason) entry.requeueReason = opts.reason;
1092
+ writeQueue(meshId, queue);
1093
+ return entry;
1094
+ }
1044
1095
  function updateSessionTaskStatus(meshId, sessionId, status) {
1045
1096
  const queue = readQueue(meshId);
1046
1097
  for (let i = queue.length - 1; i >= 0; i--) {
@@ -1059,7 +1110,14 @@ function getMeshQueueStats(meshId) {
1059
1110
  pending: queue.filter((q) => q.status === "pending").length,
1060
1111
  assigned: queue.filter((q) => q.status === "assigned").length,
1061
1112
  completed: queue.filter((q) => q.status === "completed").length,
1062
- failed: queue.filter((q) => q.status === "failed").length
1113
+ failed: queue.filter((q) => q.status === "failed").length,
1114
+ cancelled: queue.filter((q) => q.status === "cancelled").length,
1115
+ activeAssignments: queue.filter((q) => q.status === "assigned").map((q) => ({
1116
+ id: q.id,
1117
+ nodeId: q.assignedNodeId,
1118
+ sessionId: q.assignedSessionId,
1119
+ message: q.message
1120
+ }))
1063
1121
  };
1064
1122
  }
1065
1123
  var init_mesh_work_queue = __esm({
@@ -1298,6 +1356,9 @@ function drainPendingMeshCoordinatorEvents() {
1298
1356
  function readNonEmptyString(value) {
1299
1357
  return typeof value === "string" && value.trim() ? value.trim() : "";
1300
1358
  }
1359
+ function resolveEventSessionId(event, fallback) {
1360
+ return readNonEmptyString(event.targetSessionId) || readNonEmptyString(event.sessionId) || readNonEmptyString(event.instanceId) || readNonEmptyString(fallback);
1361
+ }
1301
1362
  function isMeshCoordinatorEvent(eventName) {
1302
1363
  return typeof eventName === "string" && MESH_COORDINATOR_EVENTS.has(eventName);
1303
1364
  }
@@ -1309,38 +1370,74 @@ function formatCompletionMetadata(event) {
1309
1370
  ].filter(Boolean);
1310
1371
  return parts.length > 0 ? ` (${parts.join("; ")})` : "";
1311
1372
  }
1373
+ function getMeshWithCache(components, meshId) {
1374
+ const localMesh = getMesh(meshId);
1375
+ if (localMesh) return localMesh;
1376
+ return components.router?.getCachedInlineMesh(meshId);
1377
+ }
1312
1378
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
1313
1379
  const task = claimNextTask(meshId, nodeId, sessionId);
1314
- if (!task) return false;
1380
+ if (!task) {
1381
+ return false;
1382
+ }
1315
1383
  LOG.info("MeshQueue", `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
1384
+ const mesh = getMeshWithCache(components, meshId);
1385
+ const node = mesh?.nodes.find((n) => n.id === nodeId);
1386
+ if (node?.daemonId && components.dispatchMeshCommand) {
1387
+ const isLocalNode = components.cliManager.adapters.has(sessionId);
1388
+ if (!isLocalNode) {
1389
+ components.dispatchMeshCommand(node.daemonId, "agent_command", {
1390
+ targetSessionId: sessionId,
1391
+ cliType: providerType,
1392
+ action: "send_chat",
1393
+ message: task.message
1394
+ }).catch((e) => {
1395
+ LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
1396
+ updateTaskStatus(meshId, task.id, "failed");
1397
+ });
1398
+ return true;
1399
+ }
1400
+ }
1316
1401
  components.cliManager.handleCliCommand("agent_command", {
1317
1402
  targetSessionId: sessionId,
1318
1403
  cliType: providerType,
1319
1404
  action: "send_chat",
1320
- input: task.message
1405
+ message: task.message
1321
1406
  }).catch((e) => {
1322
- LOG.error("MeshQueue", `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
1407
+ LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
1408
+ updateTaskStatus(meshId, task.id, "failed");
1323
1409
  });
1324
1410
  return true;
1325
1411
  }
1326
1412
  function triggerMeshQueue(components, meshId) {
1327
- const mesh = getMesh(meshId);
1413
+ const mesh = getMeshWithCache(components, meshId);
1328
1414
  if (!mesh) return;
1329
1415
  const cliInstances = components.instanceManager.getByCategory("cli");
1330
1416
  for (const inst of cliInstances) {
1331
1417
  const state = inst.getState();
1332
1418
  const settings = state.settings || {};
1333
1419
  const instMeshId = readNonEmptyString(settings.meshNodeFor);
1334
- if (instMeshId !== meshId && !settings.launchedByCoordinator) continue;
1420
+ if (instMeshId !== meshId) continue;
1335
1421
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
1336
1422
  if (!nodeId) continue;
1337
- if (state.status !== "idle" && state.status !== "stopped" && state.activeChat?.status !== "waiting_input") continue;
1423
+ const status = readNonEmptyString(state.status).toLowerCase();
1424
+ if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
1425
+ if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
1338
1426
  const sessionId = state.instanceId;
1339
1427
  const providerType = state.type || readNonEmptyString(settings.providerType);
1340
1428
  if (providerType) {
1341
1429
  tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
1342
1430
  }
1343
1431
  }
1432
+ for (const [key, idle] of remoteIdleSessions.entries()) {
1433
+ const node = mesh.nodes.find((n) => n.id === idle.nodeId);
1434
+ if (node) {
1435
+ const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
1436
+ if (assigned) {
1437
+ remoteIdleSessions.delete(key);
1438
+ }
1439
+ }
1440
+ }
1344
1441
  }
1345
1442
  function buildMeshSystemMessage(args) {
1346
1443
  const metadata = formatCompletionMetadata(args.metadataEvent);
@@ -1387,20 +1484,67 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
1387
1484
  return "";
1388
1485
  }
1389
1486
  function injectMeshSystemMessage(components, args) {
1487
+ let completedTaskForLedger = null;
1390
1488
  if (args.event === "agent:generating_completed") {
1391
- const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1392
- const nodeId = readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1489
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1490
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1393
1491
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
1394
1492
  if (sessionId) {
1395
- updateSessionTaskStatus(args.meshId, sessionId, "completed");
1493
+ const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed");
1494
+ completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
1396
1495
  if (nodeId && providerType) {
1397
1496
  setTimeout(() => {
1398
1497
  tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
1399
1498
  }, 500);
1400
1499
  }
1401
1500
  }
1501
+ } else if (args.event === "agent:ready") {
1502
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1503
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1504
+ const providerType = readNonEmptyString(args.metadataEvent.providerType);
1505
+ const completedTask = sessionId ? updateSessionTaskStatus(args.meshId, sessionId, "completed") : null;
1506
+ if (completedTask) {
1507
+ completedTaskForLedger = { id: completedTask.id };
1508
+ try {
1509
+ appendLedgerEntry(args.meshId, {
1510
+ kind: "task_completed",
1511
+ nodeId: nodeId || void 0,
1512
+ sessionId,
1513
+ providerType: providerType || void 0,
1514
+ payload: {
1515
+ event: args.event,
1516
+ nodeLabel: args.nodeLabel,
1517
+ taskId: completedTask.id,
1518
+ completedViaReady: true,
1519
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
1520
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
1521
+ }
1522
+ });
1523
+ } catch (e) {
1524
+ LOG.warn("MeshLedger", `Failed to record task_completed from ready: ${e?.message || e}`);
1525
+ }
1526
+ }
1527
+ if (sessionId && nodeId && providerType) {
1528
+ remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
1529
+ setTimeout(() => {
1530
+ const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
1531
+ if (assigned) {
1532
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
1533
+ }
1534
+ }, 500);
1535
+ }
1536
+ } else if (args.event === "agent:generating_started") {
1537
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1538
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1539
+ if (sessionId && nodeId) {
1540
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
1541
+ }
1402
1542
  } else if (args.event === "agent:stopped") {
1403
- const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
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
+ }
1404
1548
  if (sessionId) {
1405
1549
  updateSessionTaskStatus(args.meshId, sessionId, "failed");
1406
1550
  }
@@ -1410,13 +1554,15 @@ function injectMeshSystemMessage(components, args) {
1410
1554
  try {
1411
1555
  appendLedgerEntry(args.meshId, {
1412
1556
  kind: ledgerKind,
1413
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1414
- sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1557
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1558
+ sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || void 0,
1415
1559
  providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
1416
1560
  payload: {
1417
1561
  event: args.event,
1418
1562
  nodeLabel: args.nodeLabel,
1419
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
1563
+ taskId: completedTaskForLedger?.id || void 0,
1564
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
1565
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
1420
1566
  }
1421
1567
  });
1422
1568
  } catch (e) {
@@ -1429,8 +1575,8 @@ function injectMeshSystemMessage(components, args) {
1429
1575
  const mesh = getMesh(args.meshId);
1430
1576
  const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
1431
1577
  recoveryContext = getSessionRecoveryContext(args.meshId, {
1432
- sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1433
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1578
+ sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || void 0,
1579
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1434
1580
  maxRetries
1435
1581
  });
1436
1582
  recoveryContext.failedProviderType = readNonEmptyString(args.metadataEvent.providerType) || null;
@@ -1525,12 +1671,14 @@ function handleMeshForwardEvent(components, payload) {
1525
1671
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
1526
1672
  return injectMeshSystemMessage(components, {
1527
1673
  meshId,
1674
+ nodeId,
1528
1675
  nodeLabel,
1529
1676
  event: eventName,
1530
1677
  metadataEvent: {
1531
- targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
1678
+ targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
1532
1679
  providerType: readNonEmptyString(payload.providerType),
1533
- providerSessionId: readNonEmptyString(payload.providerSessionId)
1680
+ providerSessionId: readNonEmptyString(payload.providerSessionId),
1681
+ finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary)
1534
1682
  }
1535
1683
  });
1536
1684
  }
@@ -1549,22 +1697,24 @@ function setupMeshEventForwarding(components) {
1549
1697
  const meshIdFromRuntime = readNonEmptyString(settings.meshNodeFor);
1550
1698
  const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
1551
1699
  if (!isMeshDelegate) return;
1552
- const mesh = meshIdFromRuntime ? getMesh(meshIdFromRuntime) : getMeshByRepo(workspace);
1700
+ const mesh = meshIdFromRuntime ? getMeshWithCache(components, meshIdFromRuntime) : getMeshByRepo(workspace);
1553
1701
  const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
1554
1702
  if (!meshId) return;
1555
1703
  const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
1556
1704
  const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
1705
+ const resolvedNodeId = targetNode?.id || runtimeNodeId;
1557
1706
  const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
1558
1707
  injectMeshSystemMessage(components, {
1559
1708
  meshId,
1560
1709
  sourceInstanceId: instanceId,
1710
+ nodeId: resolvedNodeId,
1561
1711
  nodeLabel,
1562
1712
  event: event.event,
1563
1713
  metadataEvent: event
1564
1714
  });
1565
1715
  });
1566
1716
  }
1567
- var MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
1717
+ var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
1568
1718
  var init_mesh_events = __esm({
1569
1719
  "src/mesh/mesh-events.ts"() {
1570
1720
  "use strict";
@@ -1572,12 +1722,15 @@ var init_mesh_events = __esm({
1572
1722
  init_logger();
1573
1723
  init_mesh_ledger();
1574
1724
  init_mesh_work_queue();
1725
+ remoteIdleSessions = /* @__PURE__ */ new Map();
1575
1726
  MAX_PENDING_EVENTS = 50;
1576
1727
  pendingMeshCoordinatorEvents = [];
1577
1728
  MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
1729
+ "agent:generating_started",
1578
1730
  "agent:generating_completed",
1579
1731
  "agent:waiting_approval",
1580
1732
  "agent:stopped",
1733
+ "agent:ready",
1581
1734
  "monitor:long_generating"
1582
1735
  ]);
1583
1736
  EVENT_TO_LEDGER_KIND = {
@@ -2695,6 +2848,8 @@ var init_provider_cli_adapter = __esm({
2695
2848
  statusHistory = [];
2696
2849
  // ─── CLI Scripts (script-based parsing) ───
2697
2850
  cliScripts;
2851
+ /** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
2852
+ scriptState = null;
2698
2853
  runtimeSettings = {};
2699
2854
  /** Full accumulated rendered PTY transcript for parser/readback use */
2700
2855
  accumulatedBuffer = "";
@@ -2773,7 +2928,7 @@ ${lastSnapshot}`;
2773
2928
  }
2774
2929
  getFreshParsedStatusCache() {
2775
2930
  const cached = this.parsedStatusCache;
2776
- 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) {
2931
+ 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) {
2777
2932
  return cached.result;
2778
2933
  }
2779
2934
  return null;
@@ -2876,6 +3031,7 @@ ${lastSnapshot}`;
2876
3031
  this.cliScripts = scripts;
2877
3032
  this.parsedStatusCache = null;
2878
3033
  this.parseErrorMessage = null;
3034
+ this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
2879
3035
  const scriptNames = listCliScriptNames(scripts);
2880
3036
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
2881
3037
  }
@@ -2993,6 +3149,7 @@ ${lastSnapshot}`;
2993
3149
  this.ready = false;
2994
3150
  this.startupParseGate = false;
2995
3151
  this.spawnAt = 0;
3152
+ this.scriptState = null;
2996
3153
  this.onStatusChange?.();
2997
3154
  });
2998
3155
  this.spawnAt = Date.now();
@@ -3746,6 +3903,11 @@ ${lastSnapshot}`;
3746
3903
  };
3747
3904
  }
3748
3905
  // ─── Script Execution ──────────────────────────
3906
+ invokeCliScript(script, input) {
3907
+ const hasStateFactory = typeof this.cliScripts?.createState === "function";
3908
+ const expectsStateArgument = hasStateFactory || this.scriptState !== null || script.length >= 2;
3909
+ return expectsStateArgument ? script(this.scriptState, input) : script(input);
3910
+ }
3749
3911
  runParseSession() {
3750
3912
  if (typeof this.cliScripts?.parseSession !== "function") {
3751
3913
  this.parseErrorMessage = `${this.cliType} parseSession unavailable`;
@@ -3766,7 +3928,10 @@ ${lastSnapshot}`;
3766
3928
  scope: this.currentTurnScope,
3767
3929
  runtimeSettings: this.runtimeSettings
3768
3930
  });
3769
- const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3931
+ const session = this.invokeCliScript(
3932
+ this.cliScripts.parseSession,
3933
+ { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) }
3934
+ );
3770
3935
  this.parseErrorMessage = null;
3771
3936
  return session && typeof session === "object" ? session : null;
3772
3937
  } catch (e) {
@@ -3780,7 +3945,7 @@ ${lastSnapshot}`;
3780
3945
  if (!this.cliScripts?.detectStatus) return null;
3781
3946
  try {
3782
3947
  const screenText = this.terminalScreen.getText();
3783
- const status = this.cliScripts.detectStatus({
3948
+ const status = this.invokeCliScript(this.cliScripts.detectStatus, {
3784
3949
  tail: text.slice(-500),
3785
3950
  screenText,
3786
3951
  rawBuffer: this.accumulatedRawBuffer,
@@ -3799,7 +3964,7 @@ ${lastSnapshot}`;
3799
3964
  try {
3800
3965
  const screenText = this.terminalScreen.getText();
3801
3966
  const buffer = screenText || this.accumulatedBuffer;
3802
- return this.cliScripts.parseApproval({
3967
+ return this.invokeCliScript(this.cliScripts.parseApproval, {
3803
3968
  buffer,
3804
3969
  screenText,
3805
3970
  rawBuffer: this.accumulatedRawBuffer,
@@ -3855,7 +4020,7 @@ ${lastSnapshot}`;
3855
4020
  const screenText = this.readTerminalScreenText();
3856
4021
  const parseScreenText = this.getParseScreenText(screenText);
3857
4022
  const cached = this.parsedStatusCache;
3858
- 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) {
4023
+ 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) {
3859
4024
  return cached.result;
3860
4025
  }
3861
4026
  const parsed = this.runParseSession();
@@ -3883,6 +4048,7 @@ ${lastSnapshot}`;
3883
4048
  currentTurnScope: this.currentTurnScope,
3884
4049
  recentOutputBuffer: this.recentOutputBuffer,
3885
4050
  accumulatedBuffer: this.accumulatedBuffer,
4051
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
3886
4052
  screenText: parseScreenText,
3887
4053
  currentStatus: this.currentStatus,
3888
4054
  activeModal: this.activeModal,
@@ -3907,7 +4073,7 @@ ${lastSnapshot}`;
3907
4073
  scope: this.currentTurnScope,
3908
4074
  runtimeSettings: this.runtimeSettings
3909
4075
  });
3910
- return await Promise.resolve(fn({
4076
+ return await Promise.resolve(fn(this.scriptState, {
3911
4077
  ...input,
3912
4078
  args: args && typeof args === "object" ? { ...args } : {}
3913
4079
  }));
@@ -14983,11 +15149,13 @@ async function handleOpenPanel(h, args) {
14983
15149
  async function handlePtyInput(h, args) {
14984
15150
  const { cliType, data, targetSessionId } = args || {};
14985
15151
  if (!data) return { success: false, error: "data required" };
15152
+ const cleanData = typeof data === "string" ? data.replace(/\x1b\[[?>][0-9;]*c/g, "") : data;
15153
+ if (!cleanData) return { success: true };
14986
15154
  const adapter = h.getCliAdapter(targetSessionId || cliType);
14987
15155
  if (!adapter || typeof adapter.writeRaw !== "function") {
14988
15156
  return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
14989
15157
  }
14990
- await adapter.writeRaw(data);
15158
+ await adapter.writeRaw(cleanData);
14991
15159
  return { success: true };
14992
15160
  }
14993
15161
  function handlePtyResize(_h, args) {
@@ -16617,6 +16785,8 @@ var CliProviderInstance = class {
16617
16785
  this.completedDebounceTimer = null;
16618
16786
  }, 3e3);
16619
16787
  }
16788
+ } else if (newStatus === "idle" && this.lastStatus === "starting") {
16789
+ this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
16620
16790
  } else if (newStatus === "stopped") {
16621
16791
  if (this.generatingDebounceTimer) {
16622
16792
  clearTimeout(this.generatingDebounceTimer);
@@ -18365,9 +18535,6 @@ function buildCoordinatorDelegatedCliLaunchOptions(input) {
18365
18535
  const cliType = String(input.cliType || "").trim();
18366
18536
  const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
18367
18537
  const env = { ...input.env || {}, ...COORDINATOR_DELEGATED_ENV_UNSETS };
18368
- if (cliType === "hermes-cli" && !hasCliArg(cliArgs, "--ignore-user-config")) {
18369
- cliArgs.unshift("--ignore-user-config");
18370
- }
18371
18538
  if (cliType === "claude-cli" && !hasCliArg(cliArgs, "--mcp-config")) {
18372
18539
  cliArgs.unshift("--mcp-config", ensureEmptyDelegatedMcpConfig(input.workspace));
18373
18540
  }
@@ -21613,7 +21780,9 @@ function resolveHermesMeshCoordinatorSetup(options) {
21613
21780
  const mcpServer = resolveAdhdevMcpServerLaunch({
21614
21781
  meshId: options.meshId,
21615
21782
  nodeExecutable: options.nodeExecutable,
21616
- adhdevMcpEntryPath: options.adhdevMcpEntryPath
21783
+ adhdevMcpEntryPath: options.adhdevMcpEntryPath,
21784
+ adhdevMcpTransport: options.adhdevMcpTransport,
21785
+ adhdevMcpPort: options.adhdevMcpPort
21617
21786
  });
21618
21787
  if (!mcpServer) {
21619
21788
  return {
@@ -21680,7 +21849,9 @@ function resolveMeshCoordinatorSetup(options) {
21680
21849
  const mcpServer = resolveAdhdevMcpServerLaunch({
21681
21850
  meshId,
21682
21851
  nodeExecutable: options.nodeExecutable,
21683
- adhdevMcpEntryPath: options.adhdevMcpEntryPath
21852
+ adhdevMcpEntryPath: options.adhdevMcpEntryPath,
21853
+ adhdevMcpTransport: options.adhdevMcpTransport,
21854
+ adhdevMcpPort: options.adhdevMcpPort
21684
21855
  });
21685
21856
  if (!mcpServer) {
21686
21857
  return {
@@ -21702,6 +21873,22 @@ function resolveMeshCoordinatorSetup(options) {
21702
21873
  if (!instructions || !template?.trim()) {
21703
21874
  return { kind: "unsupported", reason: "Provider manual MCP setup is missing instructions or template" };
21704
21875
  }
21876
+ const renderedTemplate = renderMeshCoordinatorTemplate(template, {
21877
+ meshId,
21878
+ workspace,
21879
+ serverName,
21880
+ adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
21881
+ });
21882
+ const isCliCommand = !renderedTemplate.trim().includes("\n") && !renderedTemplate.trim().startsWith("{");
21883
+ if (isCliCommand) {
21884
+ return {
21885
+ kind: "cli_command",
21886
+ serverName,
21887
+ command: renderedTemplate.trim(),
21888
+ requiresRestart: mcpConfig.requiresRestart === true,
21889
+ instructions
21890
+ };
21891
+ }
21705
21892
  return {
21706
21893
  kind: "manual",
21707
21894
  serverName,
@@ -21709,12 +21896,7 @@ function resolveMeshCoordinatorSetup(options) {
21709
21896
  configPathCommand: mcpConfig.configPathCommand,
21710
21897
  requiresRestart: mcpConfig.requiresRestart === true,
21711
21898
  instructions,
21712
- template: renderMeshCoordinatorTemplate(template, {
21713
- meshId,
21714
- workspace,
21715
- serverName,
21716
- adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
21717
- })
21899
+ template: renderedTemplate
21718
21900
  };
21719
21901
  }
21720
21902
  return {
@@ -21743,11 +21925,27 @@ function resolveAdhdevMcpServerLaunch(options) {
21743
21925
  if (!entryPath) return null;
21744
21926
  const nodeExecutable = resolveMcpNodeExecutable(options.nodeExecutable);
21745
21927
  if (!nodeExecutable) return null;
21928
+ const transport = resolveMcpTransport(options.adhdevMcpTransport);
21929
+ const args = [entryPath, "--mode", transport, "--repo-mesh", options.meshId];
21930
+ const port = resolveMcpPort(options.adhdevMcpPort);
21931
+ if (port !== void 0) args.push("--port", String(port));
21746
21932
  return {
21747
21933
  command: nodeExecutable,
21748
- args: [entryPath, "--mode", "ipc", "--repo-mesh", options.meshId]
21934
+ args
21749
21935
  };
21750
21936
  }
21937
+ function resolveMcpTransport(explicitTransport) {
21938
+ if (explicitTransport === "local" || explicitTransport === "ipc") return explicitTransport;
21939
+ const envTransport = process.env.ADHDEV_COORDINATOR_MCP_TRANSPORT?.trim();
21940
+ return envTransport === "local" ? "local" : "ipc";
21941
+ }
21942
+ function resolveMcpPort(explicitPort) {
21943
+ if (typeof explicitPort === "number" && Number.isInteger(explicitPort) && explicitPort > 0) return explicitPort;
21944
+ const raw = process.env.ADHDEV_COORDINATOR_MCP_PORT?.trim();
21945
+ if (!raw) return void 0;
21946
+ const parsed = Number(raw);
21947
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
21948
+ }
21751
21949
  function resolveMcpNodeExecutable(explicitExecutable) {
21752
21950
  const explicit = explicitExecutable?.trim();
21753
21951
  if (explicit) return explicit;
@@ -23568,6 +23766,51 @@ var DaemonCommandRouter = class {
23568
23766
  return { success: false, error: e.message };
23569
23767
  }
23570
23768
  }
23769
+ case "get_mesh_queue": {
23770
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23771
+ if (!meshId) return { success: false, error: "meshId required" };
23772
+ try {
23773
+ const { getQueue: getQueue2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
23774
+ const status = Array.isArray(args?.status) ? args.status.map((s) => typeof s === "string" ? s.trim() : "").filter(Boolean) : void 0;
23775
+ const queue = getQueue2(meshId, { status });
23776
+ return { success: true, queue };
23777
+ } catch (e) {
23778
+ return { success: false, error: e.message };
23779
+ }
23780
+ }
23781
+ case "cancel_mesh_queue_task": {
23782
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23783
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
23784
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
23785
+ try {
23786
+ const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
23787
+ const reason = typeof args?.reason === "string" ? args.reason : void 0;
23788
+ const task = cancelTask2(meshId, taskId, { reason });
23789
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
23790
+ return { success: true, task };
23791
+ } catch (e) {
23792
+ return { success: false, error: e.message };
23793
+ }
23794
+ }
23795
+ case "requeue_mesh_queue_task": {
23796
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23797
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
23798
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
23799
+ try {
23800
+ const { requeueTask: requeueTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
23801
+ const task = requeueTask2(meshId, taskId, {
23802
+ reason: typeof args?.reason === "string" ? args.reason : void 0,
23803
+ targetNodeId: typeof args?.targetNodeId === "string" ? args.targetNodeId.trim() : void 0,
23804
+ targetSessionId: typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : void 0,
23805
+ clearTargetNode: args?.clearTargetNode === true,
23806
+ clearTargetSession: args?.clearTargetSession !== false
23807
+ });
23808
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
23809
+ return { success: true, task };
23810
+ } catch (e) {
23811
+ return { success: false, error: e.message };
23812
+ }
23813
+ }
23571
23814
  case "add_mesh_node": {
23572
23815
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
23573
23816
  const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
@@ -23725,7 +23968,13 @@ var DaemonCommandRouter = class {
23725
23968
  appendLedgerEntry2(meshId, {
23726
23969
  kind: "node_removed",
23727
23970
  nodeId,
23728
- payload: { worktree: !!node?.isLocalWorktree, sessionCleanupMode }
23971
+ payload: {
23972
+ worktree: !!node?.isLocalWorktree,
23973
+ sessionCleanupMode,
23974
+ workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
23975
+ daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
23976
+ worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
23977
+ }
23729
23978
  });
23730
23979
  } catch {
23731
23980
  }
@@ -23896,6 +24145,93 @@ var DaemonCommandRouter = class {
23896
24145
  meshCoordinatorSetup: coordinatorSetup
23897
24146
  };
23898
24147
  }
24148
+ if (coordinatorSetup.kind === "cli_command") {
24149
+ let cliCmdSystemPrompt = "";
24150
+ try {
24151
+ cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType });
24152
+ } catch (error) {
24153
+ const message = error?.message || String(error);
24154
+ LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
24155
+ return {
24156
+ success: false,
24157
+ code: "mesh_coordinator_prompt_failed",
24158
+ error: `Failed to build Repo Mesh coordinator prompt: ${message}`,
24159
+ meshId,
24160
+ cliType,
24161
+ workspace
24162
+ };
24163
+ }
24164
+ try {
24165
+ const { execFileSync: execCmdSync } = await import("child_process");
24166
+ const cmdParts = coordinatorSetup.command.trim().split(/\s+/);
24167
+ const [regCmd, ...regArgs] = cmdParts;
24168
+ LOG.info("MeshCoordinator", `Running MCP registration: ${coordinatorSetup.command}`);
24169
+ execCmdSync(regCmd, regArgs, { stdio: "pipe", timeout: 15e3 });
24170
+ } catch (error) {
24171
+ LOG.warn("MeshCoordinator", `MCP registration command failed (may be pre-registered): ${error?.message || error}`);
24172
+ }
24173
+ const cliCmdArgs = [];
24174
+ const cliCmdEnv = {};
24175
+ if (cliCmdSystemPrompt) {
24176
+ if (cliType === "codex-cli") {
24177
+ cliCmdArgs.push("-c", `developer_instructions=${JSON.stringify(cliCmdSystemPrompt)}`);
24178
+ } else if (cliType === "gemini-cli") {
24179
+ try {
24180
+ const { writeFileSync: wfs, existsSync: efs, readFileSync: rfs } = await import("fs");
24181
+ const geminiMdPath = `${workspace}/GEMINI.md`;
24182
+ const marker = "<!-- adhdev-mesh-coordinator-prompt -->";
24183
+ const markerEnd = "<!-- /adhdev-mesh-coordinator-prompt -->";
24184
+ const block = `${marker}
24185
+ ${cliCmdSystemPrompt}
24186
+ ${markerEnd}`;
24187
+ if (efs(geminiMdPath)) {
24188
+ const existing = rfs(geminiMdPath, "utf-8");
24189
+ const replaced = existing.replace(
24190
+ new RegExp(`${marker}[\\s\\S]*?${markerEnd}`, "g"),
24191
+ block
24192
+ );
24193
+ wfs(geminiMdPath, replaced.includes(marker) ? replaced : `${existing}
24194
+
24195
+ ${block}`);
24196
+ } else {
24197
+ wfs(geminiMdPath, block);
24198
+ }
24199
+ LOG.info("MeshCoordinator", `Wrote coordinator prompt to ${workspace}/GEMINI.md`);
24200
+ } catch (e) {
24201
+ LOG.warn("MeshCoordinator", `Could not write GEMINI.md: ${e?.message || e}`);
24202
+ }
24203
+ }
24204
+ }
24205
+ const cliCmdLaunch = await this.deps.cliManager.handleCliCommand("launch_cli", {
24206
+ cliType,
24207
+ dir: workspace,
24208
+ cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : void 0,
24209
+ env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : void 0,
24210
+ settings: { meshCoordinatorFor: meshId }
24211
+ });
24212
+ if (!cliCmdLaunch?.success) {
24213
+ return { success: false, error: cliCmdLaunch?.error || "Failed to launch CLI session" };
24214
+ }
24215
+ LOG.info("MeshCoordinator", `Launched ${cliType} coordinator (cli_command) for mesh ${meshId}`);
24216
+ try {
24217
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
24218
+ appendLedgerEntry2(meshId, {
24219
+ kind: "coordinator_started",
24220
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
24221
+ providerType: cliType,
24222
+ payload: { workspace }
24223
+ });
24224
+ } catch {
24225
+ }
24226
+ return {
24227
+ success: true,
24228
+ meshId,
24229
+ cliType,
24230
+ workspace,
24231
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
24232
+ mcpRegistered: true
24233
+ };
24234
+ }
23899
24235
  const configFormat = coordinatorSetup.configFormat;
23900
24236
  if (configFormat !== "claude_mcp_json" && configFormat !== "hermes_config_yaml") {
23901
24237
  return {
@@ -23950,9 +24286,11 @@ var DaemonCommandRouter = class {
23950
24286
  args: coordinatorSetup.mcpServer.args
23951
24287
  };
23952
24288
  if (args?.inlineMesh) {
24289
+ const modeArgIndex = coordinatorSetup.mcpServer.args.findIndex((value) => value === "--mode");
24290
+ const mcpTransport = modeArgIndex >= 0 ? coordinatorSetup.mcpServer.args[modeArgIndex + 1] : "ipc";
23953
24291
  mcpServerEntry.env = {
23954
24292
  ADHDEV_INLINE_MESH: JSON.stringify(mesh),
23955
- ADHDEV_MCP_TRANSPORT: "ipc"
24293
+ ADHDEV_MCP_TRANSPORT: mcpTransport === "local" ? "local" : "ipc"
23956
24294
  };
23957
24295
  }
23958
24296
  try {
@@ -31999,7 +32337,8 @@ async function initDaemonComponents(config) {
31999
32337
  cdpManagers,
32000
32338
  sessionRegistry,
32001
32339
  detectedIdes: detectedIdesRef,
32002
- refreshProviderAvailability
32340
+ refreshProviderAvailability,
32341
+ dispatchMeshCommand: config.dispatchMeshCommand
32003
32342
  };
32004
32343
  setupMeshEventForwarding(components);
32005
32344
  return components;
@@ -32130,6 +32469,7 @@ export {
32130
32469
  buildThoughtChatMessage,
32131
32470
  buildToolChatMessage,
32132
32471
  buildUserChatMessage,
32472
+ cancelTask,
32133
32473
  claimNextTask,
32134
32474
  classifyChatMessageVisibility,
32135
32475
  classifyHotChatSessionsForSubscriptionFlush,
@@ -32173,6 +32513,7 @@ export {
32173
32513
  getLogLevel,
32174
32514
  getMesh,
32175
32515
  getMeshByRepo,
32516
+ getMeshQueueStats,
32176
32517
  getNpmExecOptions,
32177
32518
  getQueue,
32178
32519
  getRecentActivity,
@@ -32241,6 +32582,7 @@ export {
32241
32582
  registerExtensionProviders,
32242
32583
  removeNode,
32243
32584
  removeWorktree,
32585
+ requeueTask,
32244
32586
  resetConfig,
32245
32587
  resetDebugRuntimeConfig,
32246
32588
  resetState,