@adhdev/daemon-standalone 0.9.77-rc.33 → 0.9.77-rc.35

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/public/index.html CHANGED
@@ -7,7 +7,7 @@
7
7
  <meta name="description" content="ADHDev self-hosted dashboard for controlling AI agents" />
8
8
  <link rel="icon" href="/otter-logo.png" />
9
9
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-CWWeDAva.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-Crr3Vqz7.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-CLec0455.js">
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-DftJ2WZr.css">
13
13
  </head>
@@ -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,
@@ -25231,6 +25232,7 @@ __export(dist_exports, {
25231
25232
  registerExtensionProviders: () => registerExtensionProviders,
25232
25233
  removeNode: () => removeNode,
25233
25234
  removeWorktree: () => removeWorktree,
25235
+ requeueTask: () => requeueTask,
25234
25236
  resetConfig: () => resetConfig,
25235
25237
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
25236
25238
  resetState: () => resetState,
@@ -26074,6 +26076,40 @@ function updateTaskStatus(meshId, taskId, status) {
26074
26076
  writeQueue(meshId, queue);
26075
26077
  return queue[idx];
26076
26078
  }
26079
+ function cancelTask(meshId, taskId, opts) {
26080
+ const queue = readQueue(meshId);
26081
+ const idx = queue.findIndex((q) => q.id === taskId);
26082
+ if (idx === -1) return null;
26083
+ const now = (/* @__PURE__ */ new Date()).toISOString();
26084
+ queue[idx].status = "cancelled";
26085
+ queue[idx].updatedAt = now;
26086
+ queue[idx].cancelledAt = now;
26087
+ if (opts?.reason) queue[idx].cancelReason = opts.reason;
26088
+ writeQueue(meshId, queue);
26089
+ return queue[idx];
26090
+ }
26091
+ function requeueTask(meshId, taskId, opts) {
26092
+ const queue = readQueue(meshId);
26093
+ const idx = queue.findIndex((q) => q.id === taskId);
26094
+ if (idx === -1) return null;
26095
+ const entry = queue[idx];
26096
+ const now = (/* @__PURE__ */ new Date()).toISOString();
26097
+ entry.status = "pending";
26098
+ delete entry.assignedNodeId;
26099
+ delete entry.assignedSessionId;
26100
+ delete entry.cancelledAt;
26101
+ delete entry.cancelReason;
26102
+ if (opts?.clearTargetNode) delete entry.targetNodeId;
26103
+ if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
26104
+ if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
26105
+ if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
26106
+ entry.updatedAt = now;
26107
+ entry.requeuedAt = now;
26108
+ entry.requeueCount = (entry.requeueCount || 0) + 1;
26109
+ if (opts?.reason) entry.requeueReason = opts.reason;
26110
+ writeQueue(meshId, queue);
26111
+ return entry;
26112
+ }
26077
26113
  function updateSessionTaskStatus(meshId, sessionId, status) {
26078
26114
  const queue = readQueue(meshId);
26079
26115
  for (let i = queue.length - 1; i >= 0; i--) {
@@ -26092,7 +26128,8 @@ function getMeshQueueStats(meshId) {
26092
26128
  pending: queue.filter((q) => q.status === "pending").length,
26093
26129
  assigned: queue.filter((q) => q.status === "assigned").length,
26094
26130
  completed: queue.filter((q) => q.status === "completed").length,
26095
- failed: queue.filter((q) => q.status === "failed").length
26131
+ failed: queue.filter((q) => q.status === "failed").length,
26132
+ cancelled: queue.filter((q) => q.status === "cancelled").length
26096
26133
  };
26097
26134
  }
26098
26135
  function setLogLevel(level) {
@@ -26379,12 +26416,14 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
26379
26416
  return "";
26380
26417
  }
26381
26418
  function injectMeshSystemMessage(components, args) {
26419
+ let completedTaskForLedger = null;
26382
26420
  if (args.event === "agent:generating_completed") {
26383
26421
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
26384
26422
  const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
26385
26423
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
26386
26424
  if (sessionId) {
26387
- updateSessionTaskStatus(args.meshId, sessionId, "completed");
26425
+ const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed");
26426
+ completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
26388
26427
  if (nodeId && providerType) {
26389
26428
  setTimeout(() => {
26390
26429
  tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
@@ -26397,6 +26436,7 @@ function injectMeshSystemMessage(components, args) {
26397
26436
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
26398
26437
  const completedTask = sessionId ? updateSessionTaskStatus(args.meshId, sessionId, "completed") : null;
26399
26438
  if (completedTask) {
26439
+ completedTaskForLedger = { id: completedTask.id };
26400
26440
  try {
26401
26441
  appendLedgerEntry(args.meshId, {
26402
26442
  kind: "task_completed",
@@ -26408,7 +26448,8 @@ function injectMeshSystemMessage(components, args) {
26408
26448
  nodeLabel: args.nodeLabel,
26409
26449
  taskId: completedTask.id,
26410
26450
  completedViaReady: true,
26411
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
26451
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
26452
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
26412
26453
  }
26413
26454
  });
26414
26455
  } catch (e) {
@@ -26451,7 +26492,9 @@ function injectMeshSystemMessage(components, args) {
26451
26492
  payload: {
26452
26493
  event: args.event,
26453
26494
  nodeLabel: args.nodeLabel,
26454
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
26495
+ taskId: completedTaskForLedger?.id || void 0,
26496
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
26497
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
26455
26498
  }
26456
26499
  });
26457
26500
  } catch (e) {
@@ -26566,7 +26609,8 @@ function handleMeshForwardEvent(components, payload) {
26566
26609
  metadataEvent: {
26567
26610
  targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
26568
26611
  providerType: readNonEmptyString(payload.providerType),
26569
- providerSessionId: readNonEmptyString(payload.providerSessionId)
26612
+ providerSessionId: readNonEmptyString(payload.providerSessionId),
26613
+ finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary)
26570
26614
  }
26571
26615
  });
26572
26616
  }
@@ -41350,10 +41394,12 @@ Follow these recovery rules:
41350
41394
  });
41351
41395
  mesh_work_queue_exports = {};
41352
41396
  __export2(mesh_work_queue_exports, {
41397
+ cancelTask: () => cancelTask,
41353
41398
  claimNextTask: () => claimNextTask,
41354
41399
  enqueueTask: () => enqueueTask,
41355
41400
  getMeshQueueStats: () => getMeshQueueStats,
41356
41401
  getQueue: () => getQueue,
41402
+ requeueTask: () => requeueTask,
41357
41403
  updateSessionTaskStatus: () => updateSessionTaskStatus,
41358
41404
  updateTaskStatus: () => updateTaskStatus
41359
41405
  });
@@ -43034,6 +43080,11 @@ ${lastSnapshot}`;
43034
43080
  };
43035
43081
  }
43036
43082
  // ─── Script Execution ──────────────────────────
43083
+ invokeCliScript(script, input) {
43084
+ const hasStateFactory = typeof this.cliScripts?.createState === "function";
43085
+ const expectsStateArgument = hasStateFactory || this.scriptState !== null || script.length >= 2;
43086
+ return expectsStateArgument ? script(this.scriptState, input) : script(input);
43087
+ }
43037
43088
  runParseSession() {
43038
43089
  if (typeof this.cliScripts?.parseSession !== "function") {
43039
43090
  this.parseErrorMessage = `${this.cliType} parseSession unavailable`;
@@ -43054,7 +43105,10 @@ ${lastSnapshot}`;
43054
43105
  scope: this.currentTurnScope,
43055
43106
  runtimeSettings: this.runtimeSettings
43056
43107
  });
43057
- const session = this.cliScripts.parseSession(this.scriptState, { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
43108
+ const session = this.invokeCliScript(
43109
+ this.cliScripts.parseSession,
43110
+ { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) }
43111
+ );
43058
43112
  this.parseErrorMessage = null;
43059
43113
  return session && typeof session === "object" ? session : null;
43060
43114
  } catch (e) {
@@ -43068,7 +43122,7 @@ ${lastSnapshot}`;
43068
43122
  if (!this.cliScripts?.detectStatus) return null;
43069
43123
  try {
43070
43124
  const screenText = this.terminalScreen.getText();
43071
- const status = this.cliScripts.detectStatus(this.scriptState, {
43125
+ const status = this.invokeCliScript(this.cliScripts.detectStatus, {
43072
43126
  tail: text.slice(-500),
43073
43127
  screenText,
43074
43128
  rawBuffer: this.accumulatedRawBuffer,
@@ -43087,7 +43141,7 @@ ${lastSnapshot}`;
43087
43141
  try {
43088
43142
  const screenText = this.terminalScreen.getText();
43089
43143
  const buffer = screenText || this.accumulatedBuffer;
43090
- return this.cliScripts.parseApproval(this.scriptState, {
43144
+ return this.invokeCliScript(this.cliScripts.parseApproval, {
43091
43145
  buffer,
43092
43146
  screenText,
43093
43147
  rawBuffer: this.accumulatedRawBuffer,
@@ -53146,6 +53200,39 @@ Run 'adhdev doctor' for detailed diagnostics.`
53146
53200
  return { success: false, error: e.message };
53147
53201
  }
53148
53202
  }
53203
+ case "cancel_mesh_queue_task": {
53204
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
53205
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
53206
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
53207
+ try {
53208
+ const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
53209
+ const reason = typeof args?.reason === "string" ? args.reason : void 0;
53210
+ const task = cancelTask2(meshId, taskId, { reason });
53211
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
53212
+ return { success: true, task };
53213
+ } catch (e) {
53214
+ return { success: false, error: e.message };
53215
+ }
53216
+ }
53217
+ case "requeue_mesh_queue_task": {
53218
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
53219
+ const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
53220
+ if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
53221
+ try {
53222
+ const { requeueTask: requeueTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
53223
+ const task = requeueTask2(meshId, taskId, {
53224
+ reason: typeof args?.reason === "string" ? args.reason : void 0,
53225
+ targetNodeId: typeof args?.targetNodeId === "string" ? args.targetNodeId.trim() : void 0,
53226
+ targetSessionId: typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : void 0,
53227
+ clearTargetNode: args?.clearTargetNode === true,
53228
+ clearTargetSession: args?.clearTargetSession !== false
53229
+ });
53230
+ if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
53231
+ return { success: true, task };
53232
+ } catch (e) {
53233
+ return { success: false, error: e.message };
53234
+ }
53235
+ }
53149
53236
  case "add_mesh_node": {
53150
53237
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
53151
53238
  const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
@@ -53303,7 +53390,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
53303
53390
  appendLedgerEntry2(meshId, {
53304
53391
  kind: "node_removed",
53305
53392
  nodeId,
53306
- payload: { worktree: !!node?.isLocalWorktree, sessionCleanupMode }
53393
+ payload: {
53394
+ worktree: !!node?.isLocalWorktree,
53395
+ sessionCleanupMode,
53396
+ workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
53397
+ daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
53398
+ worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
53399
+ }
53307
53400
  });
53308
53401
  } catch {
53309
53402
  }
@@ -57459,6 +57552,11 @@ function annotateRapidReadChatAdvisory(payload, options) {
57459
57552
  // src/tools/mesh-tools.ts
57460
57553
  init_dist2();
57461
57554
  var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
57555
+ function readString(value) {
57556
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
57557
+ }
57558
+ var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
57559
+ var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
57462
57560
  async function refreshMeshFromDaemon(ctx) {
57463
57561
  if (!(ctx.transport instanceof IpcTransport)) return;
57464
57562
  try {
@@ -57479,6 +57577,138 @@ async function findNodeWithRefresh(ctx, nodeId) {
57479
57577
  if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
57480
57578
  return refreshed;
57481
57579
  }
57580
+ async function findOptionalNodeWithRefresh(ctx, nodeId) {
57581
+ const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
57582
+ if (hit) return hit;
57583
+ await refreshMeshFromDaemon(ctx);
57584
+ return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
57585
+ }
57586
+ function hasRecentDuplicateDispatch(ctx, args) {
57587
+ const now = Date.now();
57588
+ const normalizedMessage = args.message.trim();
57589
+ for (const task of getQueue(ctx.mesh.id)) {
57590
+ const timestamp2 = new Date(task.updatedAt || task.createdAt).getTime();
57591
+ if (!Number.isFinite(timestamp2) || now - timestamp2 > DUPLICATE_DISPATCH_WINDOW_MS) continue;
57592
+ if (task.targetNodeId && task.targetNodeId !== args.node_id) continue;
57593
+ if (task.assignedNodeId && task.assignedNodeId !== args.node_id) continue;
57594
+ if (args.session_id && task.targetSessionId !== args.session_id && task.assignedSessionId !== args.session_id) continue;
57595
+ if (task.message?.trim() === normalizedMessage) {
57596
+ return { duplicate: true, entry: task, source: "queue" };
57597
+ }
57598
+ }
57599
+ const entries = readLedgerEntries(ctx.mesh.id, { tail: 200 });
57600
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
57601
+ const entry = entries[i];
57602
+ const timestamp2 = new Date(entry.timestamp).getTime();
57603
+ if (Number.isFinite(timestamp2) && now - timestamp2 > DUPLICATE_DISPATCH_WINDOW_MS) break;
57604
+ if (entry.kind !== "task_dispatched") continue;
57605
+ if (entry.nodeId !== args.node_id) continue;
57606
+ if (args.session_id && entry.sessionId !== args.session_id) continue;
57607
+ if (typeof entry.payload?.message !== "string") continue;
57608
+ if (entry.payload.message.trim() === normalizedMessage) {
57609
+ return { duplicate: true, entry, source: "ledger" };
57610
+ }
57611
+ }
57612
+ return { duplicate: false };
57613
+ }
57614
+ function buildMissingNodeReadChatRecovery(ctx, args) {
57615
+ const entries = readLedgerEntries(ctx.mesh.id, { tail: 300 });
57616
+ const relatedEntries = entries.filter((entry) => entry.nodeId === args.node_id || entry.sessionId === args.session_id);
57617
+ const completedEntries = relatedEntries.filter((entry) => entry.kind === "task_completed");
57618
+ const lastDispatch = [...relatedEntries].reverse().find((entry) => entry.kind === "task_dispatched");
57619
+ const lastTerminal = [...relatedEntries].reverse().find((entry) => entry.kind === "task_completed" || entry.kind === "task_failed" || entry.kind === "task_stalled");
57620
+ const lastRemoved = [...relatedEntries].reverse().find((entry) => entry.kind === "node_removed");
57621
+ const lastLaunch = [...relatedEntries].reverse().find((entry) => entry.kind === "session_launched");
57622
+ const providerSessionId = args.provider_session_id || readString(lastTerminal?.payload?.providerSessionId) || readString(lastLaunch?.payload?.providerSessionId) || readString(lastDispatch?.payload?.providerSessionId);
57623
+ const finalSummary = readString(lastTerminal?.payload?.finalSummary) || readString(lastTerminal?.payload?.compactSummary) || readString(lastTerminal?.payload?.summary);
57624
+ const ledger = {
57625
+ taskCompletedFound: completedEntries.length > 0,
57626
+ nodeRemovedFound: !!lastRemoved,
57627
+ providerType: lastTerminal?.providerType || lastLaunch?.providerType || lastDispatch?.providerType,
57628
+ providerSessionId,
57629
+ nodeRemovedAt: lastRemoved?.timestamp,
57630
+ sessionCleanupMode: readString(lastRemoved?.payload?.sessionCleanupMode),
57631
+ readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
57632
+ };
57633
+ if (finalSummary) {
57634
+ return {
57635
+ success: true,
57636
+ compact: args.compact === true,
57637
+ recoveredFromLedger: true,
57638
+ nodeId: args.node_id,
57639
+ sessionId: args.session_id,
57640
+ summary: finalSummary,
57641
+ ledger,
57642
+ messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
57643
+ };
57644
+ }
57645
+ return {
57646
+ success: false,
57647
+ recoverable: true,
57648
+ code: "mesh_removed_node_transcript_unavailable",
57649
+ error: `Node '${args.node_id}' is not a current member of mesh '${ctx.mesh.name}'.`,
57650
+ nodeId: args.node_id,
57651
+ sessionId: args.session_id,
57652
+ providerSessionId,
57653
+ reason: "node_not_in_current_mesh_snapshot",
57654
+ ledger,
57655
+ completedSessionSeenInLedger: ledger.taskCompletedFound,
57656
+ lastDispatch: lastDispatch ? {
57657
+ timestamp: lastDispatch.timestamp,
57658
+ sessionId: lastDispatch.sessionId,
57659
+ providerType: lastDispatch.providerType,
57660
+ taskId: typeof lastDispatch.payload?.taskId === "string" ? lastDispatch.payload.taskId : void 0,
57661
+ messagePreview: typeof lastDispatch.payload?.message === "string" ? lastDispatch.payload.message.slice(0, 500) : void 0
57662
+ } : null,
57663
+ lastTerminalEvent: lastTerminal ? {
57664
+ kind: lastTerminal.kind,
57665
+ timestamp: lastTerminal.timestamp,
57666
+ sessionId: lastTerminal.sessionId,
57667
+ providerType: lastTerminal.providerType,
57668
+ taskId: typeof lastTerminal.payload?.taskId === "string" ? lastTerminal.payload.taskId : void 0,
57669
+ payload: lastTerminal.payload
57670
+ } : null,
57671
+ nextSteps: [
57672
+ 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.",
57673
+ "Use mesh_read_debug with the provider_session_id or daemon-side debug bundle locator if available.",
57674
+ "Check mesh_task_history for task_completed and node_removed entries before redispatching; do not resend solely because transcript recovery failed.",
57675
+ "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."
57676
+ ],
57677
+ recoveryHints: [
57678
+ "The worktree/node may have been removed or the mesh snapshot may be stale after task completion.",
57679
+ "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.",
57680
+ "Use mesh_read_debug with provider_session_id, or inspect the daemon/session-host history locator if the transcript has already been archived.",
57681
+ "Avoid redispatching the same task solely because read_chat could not recover the transcript; check task_history and git status first."
57682
+ ]
57683
+ };
57684
+ }
57685
+ function annotateQueueStaleness(queue) {
57686
+ const now = Date.now();
57687
+ return queue.map((task) => {
57688
+ const taskStatus = typeof task?.status === "string" ? task.status : void 0;
57689
+ const annotated = {
57690
+ ...task,
57691
+ taskStatus,
57692
+ dispatchedAt: task?.createdAt,
57693
+ ...taskStatus === "assigned" ? { activeTaskId: task.id } : {},
57694
+ ...taskStatus === "completed" || taskStatus === "failed" ? {
57695
+ isHistorical: true,
57696
+ completedAt: task.updatedAt
57697
+ } : {}
57698
+ };
57699
+ if (taskStatus !== "assigned") return annotated;
57700
+ const updatedAt = new Date(task.updatedAt).getTime();
57701
+ const ageMs = Number.isFinite(updatedAt) ? now - updatedAt : null;
57702
+ if (ageMs === null || ageMs < STALE_ASSIGNED_QUEUE_MS) return annotated;
57703
+ return {
57704
+ ...annotated,
57705
+ stale: true,
57706
+ staleAssigned: true,
57707
+ staleReason: "assigned task has not reached a terminal state within 30 minutes",
57708
+ assignedAgeMs: ageMs
57709
+ };
57710
+ });
57711
+ }
57482
57712
  function unwrapCommandPayload(value) {
57483
57713
  let current = value;
57484
57714
  const seen = /* @__PURE__ */ new Set();
@@ -57722,18 +57952,46 @@ var MESH_ENQUEUE_TASK_TOOL = {
57722
57952
  };
57723
57953
  var MESH_VIEW_QUEUE_TOOL = {
57724
57954
  name: "mesh_view_queue",
57725
- description: "View the current status of the mesh work queue (pending, assigned, completed, failed tasks).",
57955
+ description: "View the current status of the mesh work queue (pending, assigned, completed, failed, cancelled tasks).",
57726
57956
  inputSchema: {
57727
57957
  type: "object",
57728
57958
  properties: {
57729
57959
  status: {
57730
57960
  type: "array",
57731
57961
  items: { type: "string" },
57732
- description: "Filter by task status: pending, assigned, completed, failed. Returns all if omitted."
57962
+ description: "Filter by task status: pending, assigned, completed, failed, cancelled. Returns all if omitted."
57733
57963
  }
57734
57964
  }
57735
57965
  }
57736
57966
  };
57967
+ var MESH_QUEUE_CANCEL_TOOL = {
57968
+ name: "mesh_queue_cancel",
57969
+ 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.",
57970
+ inputSchema: {
57971
+ type: "object",
57972
+ properties: {
57973
+ task_id: { type: "string", description: "Queue task ID to cancel." },
57974
+ reason: { type: "string", description: "Optional operator-visible reason for cancellation." }
57975
+ },
57976
+ required: ["task_id"]
57977
+ }
57978
+ };
57979
+ var MESH_QUEUE_REQUEUE_TOOL = {
57980
+ name: "mesh_queue_requeue",
57981
+ 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.",
57982
+ inputSchema: {
57983
+ type: "object",
57984
+ properties: {
57985
+ task_id: { type: "string", description: "Queue task ID to requeue." },
57986
+ reason: { type: "string", description: "Optional operator-visible reason for requeueing." },
57987
+ target_node_id: { type: "string", description: "Optional replacement target node ID." },
57988
+ target_session_id: { type: "string", description: "Optional replacement target runtime session ID." },
57989
+ clear_target_node: { type: "boolean", description: "When true, remove any existing target node constraint." },
57990
+ 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." }
57991
+ },
57992
+ required: ["task_id"]
57993
+ }
57994
+ };
57737
57995
  var MESH_SEND_TASK_TOOL = {
57738
57996
  name: "mesh_send_task",
57739
57997
  description: "Legacy push-based task assignment. Enqueues a task specifically targeted at a given node. The node will pull it immediately if idle.",
@@ -57903,6 +58161,8 @@ var ALL_MESH_TOOLS = [
57903
58161
  MESH_LIST_NODES_TOOL,
57904
58162
  MESH_ENQUEUE_TASK_TOOL,
57905
58163
  MESH_VIEW_QUEUE_TOOL,
58164
+ MESH_QUEUE_CANCEL_TOOL,
58165
+ MESH_QUEUE_REQUEUE_TOOL,
57906
58166
  MESH_SEND_TASK_TOOL,
57907
58167
  MESH_READ_CHAT_TOOL,
57908
58168
  MESH_READ_DEBUG_TOOL,
@@ -58078,8 +58338,51 @@ async function meshEnqueueTask(ctx, args) {
58078
58338
  }
58079
58339
  async function meshViewQueue(ctx, args) {
58080
58340
  try {
58081
- const queue = getQueue(ctx.mesh.id, { status: args.status });
58082
- return JSON.stringify({ success: true, queue }, null, 2);
58341
+ const queue = annotateQueueStaleness(getQueue(ctx.mesh.id, { status: args.status }));
58342
+ const staleAssignedTasks = queue.filter((task) => task?.status === "assigned" && task?.staleAssigned);
58343
+ return JSON.stringify({
58344
+ success: true,
58345
+ queue,
58346
+ staleAssignedTasks,
58347
+ staleAssignedCount: staleAssignedTasks.length,
58348
+ // Back-compat alias for callers already reading the first hardening payload.
58349
+ staleAssignments: staleAssignedTasks
58350
+ }, null, 2);
58351
+ } catch (e) {
58352
+ return JSON.stringify({ success: false, error: e.message });
58353
+ }
58354
+ }
58355
+ async function meshQueueCancel(ctx, args) {
58356
+ try {
58357
+ const taskId = (args.task_id || args.taskId || "").trim();
58358
+ if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
58359
+ const task = cancelTask(ctx.mesh.id, taskId, { reason: args.reason });
58360
+ if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
58361
+ return JSON.stringify({ success: true, task }, null, 2);
58362
+ } catch (e) {
58363
+ return JSON.stringify({ success: false, error: e.message });
58364
+ }
58365
+ }
58366
+ async function meshQueueRequeue(ctx, args) {
58367
+ try {
58368
+ const taskId = (args.task_id || args.taskId || "").trim();
58369
+ if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
58370
+ const targetNodeId = (args.target_node_id || args.targetNodeId || "").trim() || void 0;
58371
+ const targetSessionId = (args.target_session_id || args.targetSessionId || "").trim() || void 0;
58372
+ const keepTargetSession = args.keep_target_session === true || args.keepTargetSession === true;
58373
+ const task = requeueTask(ctx.mesh.id, taskId, {
58374
+ reason: args.reason,
58375
+ targetNodeId,
58376
+ targetSessionId,
58377
+ clearTargetNode: args.clear_target_node === true || args.clearTargetNode === true,
58378
+ clearTargetSession: targetSessionId ? false : !keepTargetSession
58379
+ });
58380
+ if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
58381
+ if (isLocalTransport(ctx.transport)) {
58382
+ ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
58383
+ });
58384
+ }
58385
+ return JSON.stringify({ success: true, task }, null, 2);
58083
58386
  } catch (e) {
58084
58387
  return JSON.stringify({ success: false, error: e.message });
58085
58388
  }
@@ -58089,6 +58392,24 @@ async function meshSendTask(ctx, args) {
58089
58392
  if (node.policy?.readOnly) {
58090
58393
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
58091
58394
  }
58395
+ const duplicate = hasRecentDuplicateDispatch(ctx, args);
58396
+ if (duplicate.duplicate) {
58397
+ return JSON.stringify({
58398
+ success: true,
58399
+ duplicate: true,
58400
+ dispatched: false,
58401
+ warning: "Duplicate mesh_send_task suppressed: the same node/session/message was dispatched recently.",
58402
+ nodeId: args.node_id,
58403
+ sessionId: args.session_id,
58404
+ source: duplicate.source,
58405
+ previousDispatch: duplicate.entry ? {
58406
+ id: duplicate.entry.id,
58407
+ timestamp: duplicate.entry.timestamp || duplicate.entry.updatedAt || duplicate.entry.createdAt,
58408
+ nodeId: duplicate.entry.nodeId || duplicate.entry.targetNodeId || duplicate.entry.assignedNodeId,
58409
+ sessionId: duplicate.entry.sessionId || duplicate.entry.targetSessionId || duplicate.entry.assignedSessionId
58410
+ } : void 0
58411
+ });
58412
+ }
58092
58413
  try {
58093
58414
  if (!isLocalTransport(ctx.transport) && node.daemonId) {
58094
58415
  const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
@@ -58107,17 +58428,22 @@ async function meshSendTask(ctx, args) {
58107
58428
  providerType: cached2?.providerType
58108
58429
  });
58109
58430
  if (result.success) {
58431
+ const dispatchedSessionId = args.session_id || result.sessionId;
58110
58432
  try {
58111
58433
  appendLedgerEntry(ctx.mesh.id, {
58112
58434
  kind: "task_dispatched",
58113
58435
  nodeId: args.node_id,
58114
- sessionId: result.sessionId,
58115
- payload: { message: args.message, via: "p2p_direct" }
58436
+ sessionId: dispatchedSessionId,
58437
+ payload: {
58438
+ message: args.message,
58439
+ via: "p2p_direct",
58440
+ ...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
58441
+ }
58116
58442
  });
58117
58443
  } catch {
58118
58444
  }
58119
58445
  }
58120
- return JSON.stringify({ ...result, nodeId: args.node_id });
58446
+ return JSON.stringify({ ...result, nodeId: args.node_id, dispatched: result.success === true });
58121
58447
  }
58122
58448
  if (args.session_id && isLocalTransport(ctx.transport)) {
58123
58449
  const cached2 = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
@@ -58162,7 +58488,10 @@ async function meshSendTask(ctx, args) {
58162
58488
  }
58163
58489
  }
58164
58490
  async function meshReadChat(ctx, args) {
58165
- const node = await findNodeWithRefresh(ctx, args.node_id);
58491
+ const node = await findOptionalNodeWithRefresh(ctx, args.node_id);
58492
+ if (!node) {
58493
+ return JSON.stringify(buildMissingNodeReadChatRecovery(ctx, args), null, 2);
58494
+ }
58166
58495
  if (isLocalTransport(ctx.transport)) {
58167
58496
  const cached2 = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
58168
58497
  const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached2?.providerSessionId;
@@ -60292,6 +60621,12 @@ async function startMcpServer(opts) {
60292
60621
  case "mesh_view_queue":
60293
60622
  text = await meshViewQueue(meshCtx, a);
60294
60623
  break;
60624
+ case "mesh_queue_cancel":
60625
+ text = await meshQueueCancel(meshCtx, a);
60626
+ break;
60627
+ case "mesh_queue_requeue":
60628
+ text = await meshQueueRequeue(meshCtx, a);
60629
+ break;
60295
60630
  case "mesh_send_task":
60296
60631
  text = await meshSendTask(meshCtx, a);
60297
60632
  break;