@adhdev/daemon-standalone 0.9.82-rc.374 → 0.9.82-rc.375

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
@@ -30036,10 +30036,10 @@ var require_dist3 = __commonJS({
30036
30036
  }
30037
30037
  function getDaemonBuildInfo() {
30038
30038
  if (cached2) return cached2;
30039
- const commit = readInjected(true ? "47e042dcb10c9ca07c30ac4bf8e0d5a4153a63c6" : void 0) ?? "unknown";
30040
- const commitShort = readInjected(true ? "47e042dc" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30041
- const version2 = readInjected(true ? "0.9.82-rc.374" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30042
- const builtAt = readInjected(true ? "2026-06-25T00:33:32.738Z" : void 0);
30039
+ const commit = readInjected(true ? "b17fb9165bd52c9e2ab8cccf2ce80734e2165680" : void 0) ?? "unknown";
30040
+ const commitShort = readInjected(true ? "b17fb916" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30041
+ const version2 = readInjected(true ? "0.9.82-rc.375" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30042
+ const builtAt = readInjected(true ? "2026-06-25T02:22:25.633Z" : void 0);
30043
30043
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
30044
30044
  return cached2;
30045
30045
  }
@@ -32771,6 +32771,10 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
32771
32771
  if (ctx.missionSection?.trim()) {
32772
32772
  sections.push(ctx.missionSection.trim());
32773
32773
  }
32774
+ const recentActivity = buildRecentActivitySection(ctx.recentActivity);
32775
+ if (recentActivity) sections.push(recentActivity);
32776
+ const operatingNotes = buildOperatingNotesSection(ctx.operatingNotes);
32777
+ if (operatingNotes) sections.push(operatingNotes);
32774
32778
  sections.push(buildPolicySection({ ...DEFAULT_MESH_POLICY, ...mesh.policy || {} }));
32775
32779
  sections.push(TOOLS_SECTION);
32776
32780
  sections.push(TOOL_EXPOSURE_PREFLIGHT_SECTION);
@@ -32802,6 +32806,8 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
32802
32806
  cliType: coordinatorCliType || "",
32803
32807
  nodes: nodesSection,
32804
32808
  mission: ctx.missionSection?.trim() || "",
32809
+ recentActivity: buildRecentActivitySection(ctx.recentActivity) || "",
32810
+ operatingNotes: buildOperatingNotesSection(ctx.operatingNotes) || "",
32805
32811
  policy: buildPolicySection({ ...DEFAULT_MESH_POLICY, ...mesh.policy || {} }),
32806
32812
  tools: TOOLS_SECTION,
32807
32813
  workflow: WORKFLOW_SECTION,
@@ -32873,6 +32879,56 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
32873
32879
  if (lines.length === 1) return lines[0];
32874
32880
  return [lines[0], ...lines.slice(1).map((l) => pad + l)].join("\n");
32875
32881
  }
32882
+ function buildRecentActivitySection(activity) {
32883
+ if (!activity) return "";
32884
+ const failures = Array.isArray(activity.recentFailures) ? activity.recentFailures : [];
32885
+ const pending = Number.isFinite(activity.pendingTasks) ? Number(activity.pendingTasks) : 0;
32886
+ const assigned = Number.isFinite(activity.assignedTasks) ? Number(activity.assignedTasks) : 0;
32887
+ const stalled = Number.isFinite(activity.stalledTasks) ? Number(activity.stalledTasks) : 0;
32888
+ const recentFailureCount = Number.isFinite(activity.recentFailureCount) ? Number(activity.recentFailureCount) : failures.length;
32889
+ if (failures.length === 0 && pending === 0 && assigned === 0 && stalled === 0 && recentFailureCount === 0) {
32890
+ return "";
32891
+ }
32892
+ const lines = ["## Recent Activity", ""];
32893
+ lines.push("A snapshot of this mesh's recent ledger/queue state at launch. Use it to decide what needs attention first; call `mesh_task_history` / `mesh_view_queue` for full detail.");
32894
+ lines.push("");
32895
+ const counts = [];
32896
+ if (pending > 0) counts.push(`**${pending}** pending`);
32897
+ if (assigned > 0) counts.push(`**${assigned}** assigned`);
32898
+ if (stalled > 0) counts.push(`**${stalled}** stalled`);
32899
+ if (recentFailureCount > 0) counts.push(`**${recentFailureCount}** failed in the last 30 min`);
32900
+ if (counts.length) lines.push(`- Queue/ledger: ${counts.join(", ")}.`);
32901
+ if (activity.lastActivityAt) lines.push(`- Last ledger activity: ${activity.lastActivityAt}.`);
32902
+ if (failures.length > 0) {
32903
+ const recent = failures.slice(-5).reverse();
32904
+ lines.push("", "Recent failures (newest first):");
32905
+ for (const f of recent) {
32906
+ const when = f.timestamp ? `${f.timestamp} ` : "";
32907
+ const node = f.nodeId ? `node \`${f.nodeId}\`` : "unknown node";
32908
+ const summary = (f.summary || "").trim();
32909
+ lines.push(`- ${when}${node}${summary ? ` \u2014 ${summary}` : ""}`);
32910
+ }
32911
+ lines.push("", "_Check `mesh_task_history` before retrying; repeated failures on the same node mean reassign or escalate, not retry._");
32912
+ }
32913
+ return lines.join("\n");
32914
+ }
32915
+ function buildOperatingNotesSection(notes) {
32916
+ const valid = Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
32917
+ if (valid.length === 0) return "";
32918
+ const categoryLabel = {
32919
+ provider_quirk: "provider quirk",
32920
+ pattern_to_avoid: "pattern to avoid",
32921
+ recovery_lesson: "recovery lesson"
32922
+ };
32923
+ const lines = ["## Operating Notes", ""];
32924
+ lines.push("Lessons earlier coordinators on this mesh recorded via `mesh_record_note`. Treat them as accumulated operating knowledge \u2014 apply them. When you learn a durable lesson (a provider quirk, a pattern to avoid, a recovery lesson), record it with `mesh_record_note` so future coordinators inherit it.");
32925
+ lines.push("");
32926
+ for (const n of valid) {
32927
+ const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
32928
+ lines.push(`- ${cat}${n.text.trim()}`);
32929
+ }
32930
+ return lines.join("\n");
32931
+ }
32876
32932
  function buildPolicySection(policy) {
32877
32933
  const rules = [];
32878
32934
  if (policy.requirePreTaskCheckpoint) rules.push("- Create a git checkpoint **before** starting each task");
@@ -32941,6 +32997,7 @@ ${rules.join("\n")}`;
32941
32997
  | \`mesh_read_chat\` | Read recent chat messages from a delegated agent session |
32942
32998
  | \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
32943
32999
  | \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
33000
+ | \`mesh_record_note\` | Record a durable, provider-neutral operating note (provider quirk / pattern to avoid / recovery lesson). Future coordinators see it under "## Operating Notes" at launch |
32944
33001
  | \`mesh_git_status\` | Check git status on a specific node |
32945
33002
  | \`mesh_read_node_logs\` | Fetch a remote node's daemon log tail directly over P2P (grep/since/byte-bounded, secrets redacted) \u2014 no session/PowerShell needed to debug a node's daemon |
32946
33003
  | \`mesh_fast_forward_node\` | Safely dry-run or explicitly execute an obvious clean fast-forward without launching an agent session |
@@ -42724,6 +42781,18 @@ ${cleanBody}`;
42724
42781
  const diag = readRecord4(payload.completionDiagnostic);
42725
42782
  return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
42726
42783
  }
42784
+ function supersedesTruncatedTerminalSummary(args) {
42785
+ if (!args.terminalTaskId || !args.eventTaskId || args.terminalTaskId !== args.eventTaskId) return false;
42786
+ if (!isGenuineCompletionEvidence(args.metadataEvent)) return false;
42787
+ const terminalSummary = readNonEmptyString2(args.terminalPayload.finalSummary);
42788
+ const eventSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
42789
+ if (!eventSummary) return false;
42790
+ if (terminalSummary === eventSummary) return false;
42791
+ if (isWeakTerminalLedgerPayload(args.terminalPayload)) return false;
42792
+ if (!terminalSummary) return true;
42793
+ if (eventSummary.startsWith(terminalSummary)) return true;
42794
+ return eventSummary.length > terminalSummary.length + 32;
42795
+ }
42727
42796
  function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
42728
42797
  try {
42729
42798
  const matches = getActiveDirectDispatches(meshId).filter((d) => d.sessionId === sessionId);
@@ -43640,7 +43709,13 @@ ${cleanBody}`;
43640
43709
  const terminalTaskId = readNonEmptyString2(terminal.payload.taskId);
43641
43710
  const eventTaskId = readNonEmptyString2(args.metadataEvent.taskId);
43642
43711
  const distinctTaskCompletion = !!eventTaskId && !!terminalTaskId && eventTaskId !== terminalTaskId;
43643
- if (!newDispatchAfterTerminal && !supersedesWeakTerminal && !distinctTaskCompletion) {
43712
+ const supersedesTruncatedTerminal = supersedesTruncatedTerminalSummary({
43713
+ terminalPayload: terminal.payload,
43714
+ metadataEvent: args.metadataEvent,
43715
+ terminalTaskId,
43716
+ eventTaskId
43717
+ });
43718
+ if (!newDispatchAfterTerminal && !supersedesWeakTerminal && !distinctTaskCompletion && !supersedesTruncatedTerminal) {
43644
43719
  const terminalProviderSessionId = readNonEmptyString2(terminal.payload.providerSessionId);
43645
43720
  const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
43646
43721
  const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
@@ -44435,6 +44510,16 @@ ${cleanBody}`;
44435
44510
  const status = readNonEmptyString2(state.status).toLowerCase();
44436
44511
  const modalParked = status === "waiting_choice" || status === "waiting_approval";
44437
44512
  const sessionId = readNonEmptyString2(state.instanceId);
44513
+ const stateKey = `${meshId}::${sessionId || "?"}`;
44514
+ const prevParked = coordinatorModalParkState.get(stateKey);
44515
+ if (prevParked !== modalParked) {
44516
+ coordinatorModalParkState.set(stateKey, modalParked);
44517
+ if (modalParked) {
44518
+ LOG2.info("MeshReconcile", `Coordinator ${sessionId || "?"} (mesh ${meshId}) entered modal-park (status=${status}) \u2014 terminal events for it will be held until the modal is answered`);
44519
+ } else if (prevParked === true) {
44520
+ LOG2.info("MeshReconcile", `Coordinator ${sessionId || "?"} (mesh ${meshId}) left modal-park (status=${status}) \u2014 held events will drain on this/next tick`);
44521
+ }
44522
+ }
44438
44523
  out.push({ meshId, instance: inst, sessionId, idle: status === "idle", modalParked });
44439
44524
  }
44440
44525
  return out;
@@ -44628,7 +44713,55 @@ ${cleanBody}`;
44628
44713
  const forceOnly = idleCoordinators.length === 0;
44629
44714
  if (targetCoordinators.length === 0) {
44630
44715
  if (modalParkedCoordinators.length > 0) {
44631
- LOG2.info("MeshReconcile", `Reconcile skip \u2192 modal-parked: holding pending event(s) for mesh ${meshId} (${modalParkedCoordinators.length} coordinator(s) awaiting a modal answer; events left queued)`);
44716
+ const liveSessionIds = new Set(
44717
+ meshCoordinators.map((c) => readNonEmptyString2(c.sessionId)).filter(Boolean)
44718
+ );
44719
+ let orphanEscaped = 0;
44720
+ const hasPendingForOrphanPeek = !store || (() => {
44721
+ try {
44722
+ return store.pendingEventCount(meshId) > 0;
44723
+ } catch {
44724
+ return true;
44725
+ }
44726
+ })();
44727
+ if (hasPendingForOrphanPeek) {
44728
+ let peeked = [];
44729
+ try {
44730
+ peeked = getPendingMeshCoordinatorEvents(meshId, drainDaemonIds.length > 0 ? drainDaemonIds : void 0);
44731
+ } catch {
44732
+ peeked = [];
44733
+ }
44734
+ const isOrphan = (e) => {
44735
+ const want = readNonEmptyString2(e.targetCoordinatorSessionId);
44736
+ return !!want && !liveSessionIds.has(want);
44737
+ };
44738
+ const orphanEventNames = new Set(peeked.filter(isOrphan).map((e) => e.event));
44739
+ if (orphanEventNames.size > 0) {
44740
+ let drained = [];
44741
+ try {
44742
+ drained = drainPendingMeshCoordinatorEvents(
44743
+ meshId,
44744
+ drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId,
44745
+ { onlyEvents: orphanEventNames }
44746
+ );
44747
+ } catch (e) {
44748
+ LOG2.warn("MeshReconcile", `Orphan-escape drain failed for mesh ${meshId}: ${e?.message || e}`);
44749
+ drained = [];
44750
+ }
44751
+ for (const pending of drained) {
44752
+ if (isOrphan(pending)) {
44753
+ holdOrExpireStrictUnmatchedEvent(pending, readNonEmptyString2(pending.targetCoordinatorSessionId), meshId);
44754
+ orphanEscaped++;
44755
+ } else {
44756
+ try {
44757
+ queuePendingMeshCoordinatorEvent(pending);
44758
+ } catch {
44759
+ }
44760
+ }
44761
+ }
44762
+ }
44763
+ }
44764
+ LOG2.info("MeshReconcile", `Reconcile skip \u2192 modal-parked: holding pending event(s) for mesh ${meshId} (${modalParkedCoordinators.length} coordinator(s) awaiting a modal answer; events left queued${orphanEscaped > 0 ? `; ${orphanEscaped} orphan-targeted event(s) routed to strict-route TTL` : ""})`);
44632
44765
  let hasPending = true;
44633
44766
  if (store) {
44634
44767
  try {
@@ -44899,6 +45032,19 @@ ${cleanBody}`;
44899
45032
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
44900
45033
  const evidence = extractFinalAssistantSummaryEvidence(messages);
44901
45034
  if (!evidence.finalSummary) continue;
45035
+ const dispatchedAtMs = Date.parse(readNonEmptyString2(dispatch.dispatchedAt));
45036
+ const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
45037
+ if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
45038
+ LOG2.info("MeshReconcile", `Stale-summary guard: skipping transcript reconcile for task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 final assistant message (${evidence.transcriptMessageAt}) predates this task's dispatch (${dispatch.dispatchedAt}); it is a prior task's summary`);
45039
+ traceMeshEventDrop("reconcile_stale_summary_before_dispatch", {
45040
+ taskId,
45041
+ sessionId,
45042
+ nodeId,
45043
+ meshId: mesh.id,
45044
+ event: "agent:generating_completed"
45045
+ }, `transcriptAt=${evidence.transcriptMessageAt} < dispatchedAt=${dispatch.dispatchedAt}`);
45046
+ continue;
45047
+ }
44902
45048
  const providerSessionId = readNonEmptyString2(payload.providerSessionId);
44903
45049
  const coordinatorDaemonId = selfIds.find((id) => !!id);
44904
45050
  try {
@@ -45028,6 +45174,7 @@ ${cleanBody}`;
45028
45174
  }
45029
45175
  var DEFAULT_RECONCILE_INTERVAL_MS;
45030
45176
  var DEFAULT_AUTO_PRUNE_MIN_AGE_MS;
45177
+ var coordinatorModalParkState;
45031
45178
  var heldEventLedgerRecorded;
45032
45179
  var ASSIGNED_STRANDED_DEADLINE_MS;
45033
45180
  var STRICT_SESSION_MATCH_TTL_MS;
@@ -45054,6 +45201,7 @@ ${cleanBody}`;
45054
45201
  init_chat_message_normalization();
45055
45202
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
45056
45203
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
45204
+ coordinatorModalParkState = /* @__PURE__ */ new Map();
45057
45205
  heldEventLedgerRecorded = /* @__PURE__ */ new Set();
45058
45206
  ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
45059
45207
  STRICT_SESSION_MATCH_TTL_MS = 6e4;
@@ -67800,6 +67948,8 @@ ${body}
67800
67948
  }
67801
67949
  var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
67802
67950
  var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
67951
+ var NATIVE_HISTORY_MESH_IDLE_SETTLE_MS = 1500;
67952
+ var USER_INPUT_ACK_DEDUP_WINDOW_MS = 6e4;
67803
67953
  var TERMINAL_MESH_EVENTS = /* @__PURE__ */ new Set([
67804
67954
  "agent:generating_completed",
67805
67955
  "agent:stopped",
@@ -68071,6 +68221,14 @@ ${body}
68071
68221
  runtimeMessages = [];
68072
68222
  lastPersistedHistoryMessages = [];
68073
68223
  lastAcknowledgedUserInputAt = 0;
68224
+ // TASKBUBBLE-DUP: per-content last-ack timestamps so the same dispatched
68225
+ // prompt acked twice in quick succession (the worker buffers the first
68226
+ // send during bootstrap/busy, then a redelivery — dispatch-confirm-timeout
68227
+ // requeue or a reconcile re-dispatch — fires a SECOND send_chat before the
68228
+ // outbound queue drains) collapses to ONE user bubble. Keyed on the trimmed
68229
+ // content; an entry older than USER_INPUT_ACK_DEDUP_WINDOW_MS is treated as
68230
+ // a fresh, intentional resend and is NOT suppressed.
68231
+ recentUserInputAcks = /* @__PURE__ */ new Map();
68074
68232
  lastNativeSourceCanonicalCheckAt = 0;
68075
68233
  lastNativeSourceCanonicalCacheKey = void 0;
68076
68234
  cachedSqliteDb = null;
@@ -68505,6 +68663,15 @@ ${body}
68505
68663
  const content = typeof input === "string" ? input.trim() : buildCliStructuredInputPrompt(input).trim();
68506
68664
  if (!content) return;
68507
68665
  const receivedAt = Date.now();
68666
+ const ackContentKey = shortHash(`${this.instanceId}:${content}`, 24);
68667
+ const lastAckAt = this.recentUserInputAcks.get(ackContentKey);
68668
+ if (lastAckAt !== void 0 && receivedAt - lastAckAt <= USER_INPUT_ACK_DEDUP_WINDOW_MS) {
68669
+ this.recentUserInputAcks.set(ackContentKey, receivedAt);
68670
+ this.pruneRecentUserInputAcks(receivedAt);
68671
+ return;
68672
+ }
68673
+ this.recentUserInputAcks.set(ackContentKey, receivedAt);
68674
+ this.pruneRecentUserInputAcks(receivedAt);
68508
68675
  this.lastAcknowledgedUserInputAt = receivedAt;
68509
68676
  const dedupKey = `user_input_ack:${shortHash(`${this.instanceId}:${content}:${receivedAt}`, 24)}`;
68510
68677
  this.appendRuntimeMessage(buildChatMessage({
@@ -68522,6 +68689,13 @@ ${body}
68522
68689
  }
68523
68690
  }), dedupKey);
68524
68691
  }
68692
+ /** Drop user-input ack entries older than the dedup window so the map can't grow unbounded. */
68693
+ pruneRecentUserInputAcks(now) {
68694
+ if (this.recentUserInputAcks.size <= 1) return;
68695
+ for (const [key, at] of this.recentUserInputAcks) {
68696
+ if (now - at > USER_INPUT_ACK_DEDUP_WINDOW_MS) this.recentUserInputAcks.delete(key);
68697
+ }
68698
+ }
68525
68699
  dispose() {
68526
68700
  this.adapter.shutdown();
68527
68701
  this.monitor.reset();
@@ -69211,8 +69385,9 @@ ${body}
69211
69385
  previousStatus: this.lastStatus
69212
69386
  };
69213
69387
  const ownsExternalHistory = !!this.adapter?.chatMessagesOwnedExternally;
69214
- const flushDelay = ownsExternalHistory ? 0 : 3e3;
69215
- LOG2.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration3}s ownsExternalHistory=${ownsExternalHistory} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
69388
+ const meshWorkerSession = this.isMeshWorkerSession();
69389
+ const flushDelay = ownsExternalHistory ? meshWorkerSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
69390
+ LOG2.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration3}s ownsExternalHistory=${ownsExternalHistory} meshWorker=${meshWorkerSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
69216
69391
  this.scheduleCompletedDebounceFlush(flushDelay);
69217
69392
  }
69218
69393
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
@@ -72257,11 +72432,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
72257
72432
  }
72258
72433
  }
72259
72434
  }
72260
- const agentResult = await ctx.deps.cliManager.handleCliCommand("agent_command", args);
72261
72435
  const meshCtx = args?.meshContext;
72262
72436
  const dispatchNodeId = readStringValue(meshCtx?.nodeId);
72263
72437
  const dispatchMeshId = readStringValue(meshCtx?.meshId);
72264
- if (dispatchNodeId && dispatchMeshId && agentResult?.success !== false) {
72438
+ const isSendChat = args?.action === "send_chat";
72439
+ if (isSendChat && dispatchNodeId && dispatchMeshId) {
72265
72440
  try {
72266
72441
  const { getMesh: getMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
72267
72442
  const meshObj = getMesh2(dispatchMeshId) ?? ctx.getCachedInlineMesh(dispatchMeshId);
@@ -72269,17 +72444,22 @@ Run 'adhdev doctor' for detailed diagnostics.`
72269
72444
  const bootstrapStatus = readStringValue(nodeObj?.worktreeBootstrap?.status);
72270
72445
  if (bootstrapStatus === "running") {
72271
72446
  return {
72272
- success: true,
72273
- ...agentResult,
72274
- dispatchAcknowledgementRisk: true,
72275
- dispatchAcknowledgementRiskReason: "bootstrap_still_running",
72276
- nextAction: "Wait for worktree_bootstrap_complete event before dispatching work to this node."
72447
+ success: false,
72448
+ recoverable: true,
72449
+ dispatched: false,
72450
+ code: "mesh_node_bootstrap_pending",
72451
+ reason: "bootstrap_still_running",
72452
+ nodeId: dispatchNodeId,
72453
+ meshId: dispatchMeshId,
72454
+ ...readStringValue(meshCtx?.taskId) ? { taskId: readStringValue(meshCtx?.taskId) } : {},
72455
+ error: `Node '${dispatchNodeId}' worktree bootstrap is still running; a task injected now would land in the session input buffer before the provider is ready to consume it and be silently lost. Dispatch deferred.`,
72456
+ nextAction: "Wait for the worktree_bootstrap_complete event (or poll mesh_status until the node session is ready), then re-send the task with mesh_send_task. Alternatively use mesh_enqueue_task so the queue auto-assigns it once a ready session is available."
72277
72457
  };
72278
72458
  }
72279
72459
  } catch {
72280
72460
  }
72281
72461
  }
72282
- return agentResult;
72462
+ return ctx.deps.cliManager.handleCliCommand("agent_command", args);
72283
72463
  },
72284
72464
  // ─── Logs ───
72285
72465
  list_saved_sessions: async (ctx, args) => {
@@ -77361,49 +77541,72 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
77361
77541
  };
77362
77542
  },
77363
77543
  fast_forward_mesh_node: async (ctx, args) => {
77364
- const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
77365
- const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
77366
- let workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
77367
- let submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
77368
- let nodeDaemonId;
77369
- let allowAutoPublishSubmoduleMainCommits = false;
77370
- if (meshId && nodeId) {
77371
- const meshRecord = await ctx.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
77372
- const mesh = meshRecord?.mesh;
77373
- const node = mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
77374
- if (!workspace) {
77375
- workspace = typeof node?.workspace === "string" ? node.workspace.trim() : "";
77544
+ const workspaceForError = typeof args?.workspace === "string" ? args.workspace.trim() : "";
77545
+ const meshIdForError = typeof args?.meshId === "string" ? args.meshId.trim() : "";
77546
+ const nodeIdForError = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
77547
+ try {
77548
+ const meshId = meshIdForError;
77549
+ const nodeId = nodeIdForError;
77550
+ let workspace = workspaceForError;
77551
+ let submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
77552
+ let nodeDaemonId;
77553
+ let allowAutoPublishSubmoduleMainCommits = false;
77554
+ if (meshId && nodeId) {
77555
+ const meshRecord = await ctx.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
77556
+ const mesh = meshRecord?.mesh;
77557
+ const node = mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
77558
+ if (!workspace) {
77559
+ workspace = typeof node?.workspace === "string" ? node.workspace.trim() : "";
77560
+ }
77561
+ if (!submoduleIgnorePaths && Array.isArray(node?.policy?.submoduleIgnorePaths)) {
77562
+ submoduleIgnorePaths = node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string");
77563
+ }
77564
+ allowAutoPublishSubmoduleMainCommits = mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true;
77565
+ nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
77376
77566
  }
77377
- if (!submoduleIgnorePaths && Array.isArray(node?.policy?.submoduleIgnorePaths)) {
77378
- submoduleIgnorePaths = node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string");
77567
+ const selfDaemonId = ctx.deps.statusInstanceId;
77568
+ const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
77569
+ if (isRemote && ctx.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
77570
+ const forwarded = await ctx.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
77571
+ ...typeof args === "object" && args !== null ? args : {},
77572
+ workspace,
77573
+ _meshDirectDispatch: true
77574
+ });
77575
+ return forwarded ?? { success: false, error: "no response from remote node" };
77379
77576
  }
77380
- allowAutoPublishSubmoduleMainCommits = mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true;
77381
- nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
77382
- }
77383
- const selfDaemonId = ctx.deps.statusInstanceId;
77384
- const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
77385
- if (isRemote && ctx.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
77386
- const forwarded = await ctx.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
77387
- ...typeof args === "object" && args !== null ? args : {},
77577
+ const result = await fastForwardMeshNode({
77578
+ meshId: meshId || void 0,
77579
+ nodeId: nodeId || void 0,
77388
77580
  workspace,
77389
- _meshDirectDispatch: true
77581
+ branch: typeof args?.branch === "string" ? args.branch : void 0,
77582
+ execute: args?.execute === true,
77583
+ dryRun: args?.dryRun === true,
77584
+ updateSubmodules: args?.updateSubmodules === true,
77585
+ submoduleIgnorePaths,
77586
+ mode: args?.mode === "push" ? "push" : "merge",
77587
+ pushSubmodules: args?.pushSubmodules === true,
77588
+ allowAutoPublishSubmoduleMainCommits
77390
77589
  });
77391
- return forwarded ?? { success: false, error: "no response from remote node" };
77590
+ return result;
77591
+ } catch (e) {
77592
+ const errorMessage = e?.message || String(e);
77593
+ return {
77594
+ success: false,
77595
+ code: "fast_forward_safety_gate_error",
77596
+ ...meshIdForError ? { meshId: meshIdForError } : {},
77597
+ ...nodeIdForError ? { nodeId: nodeIdForError } : {},
77598
+ workspace: workspaceForError,
77599
+ mode: args?.mode === "push" ? "push" : "merge",
77600
+ allowed: false,
77601
+ willRun: false,
77602
+ executed: false,
77603
+ // Surface the throw as a blocking reason instead of an opaque IPC crash
77604
+ // so the coordinator gets the same structured shape a clean node returns.
77605
+ blockingReasons: ["fast_forward_safety_gate_error"],
77606
+ operationError: errorMessage,
77607
+ error: errorMessage
77608
+ };
77392
77609
  }
77393
- const result = await fastForwardMeshNode({
77394
- meshId: meshId || void 0,
77395
- nodeId: nodeId || void 0,
77396
- workspace,
77397
- branch: typeof args?.branch === "string" ? args.branch : void 0,
77398
- execute: args?.execute === true,
77399
- dryRun: args?.dryRun === true,
77400
- updateSubmodules: args?.updateSubmodules === true,
77401
- submoduleIgnorePaths,
77402
- mode: args?.mode === "push" ? "push" : "merge",
77403
- pushSubmodules: args?.pushSubmodules === true,
77404
- allowAutoPublishSubmoduleMainCommits
77405
- });
77406
- return result;
77407
77610
  },
77408
77611
  refine_mesh_node: async (ctx, args) => {
77409
77612
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
@@ -77552,6 +77755,56 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
77552
77755
  return "";
77553
77756
  }
77554
77757
  };
77758
+ const buildRecentActivityBestEffort = async (id) => {
77759
+ try {
77760
+ const { getLedgerSummary: getLedgerSummary2, readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
77761
+ const { getMeshQueueStats: getMeshQueueStats2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
77762
+ const summary = getLedgerSummary2(id);
77763
+ const queue = getMeshQueueStats2(id);
77764
+ const failureEntries = readLedgerEntries2(id, { kind: ["task_failed"], tail: 5 });
77765
+ const recentFailures = failureEntries.map((e) => {
77766
+ const p = e.payload || {};
77767
+ const raw = typeof p.taskSummary === "string" ? p.taskSummary : typeof p.message === "string" ? p.message : typeof p.error === "string" ? p.error : "";
77768
+ const summaryText = raw.length > 160 ? `${raw.slice(0, 160)}\u2026` : raw;
77769
+ return {
77770
+ timestamp: e.timestamp,
77771
+ nodeId: e.nodeId,
77772
+ summary: summaryText
77773
+ };
77774
+ });
77775
+ return {
77776
+ recentFailures,
77777
+ recentFailureCount: summary.recentFailures,
77778
+ pendingTasks: queue.pending,
77779
+ assignedTasks: queue.assigned,
77780
+ stalledTasks: summary.taskStalled,
77781
+ lastActivityAt: summary.lastActivityAt
77782
+ };
77783
+ } catch {
77784
+ return void 0;
77785
+ }
77786
+ };
77787
+ const buildOperatingNotesBestEffort = async (id) => {
77788
+ try {
77789
+ const { readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
77790
+ const noteEntries = readLedgerEntries2(id, { kind: ["coordinator_operating_note"], tail: 20 });
77791
+ const notes = noteEntries.map((e) => {
77792
+ const p = e.payload || {};
77793
+ const text = typeof p.text === "string" ? p.text.trim() : "";
77794
+ if (!text) return null;
77795
+ const category = p.category === "provider_quirk" || p.category === "pattern_to_avoid" || p.category === "recovery_lesson" ? p.category : void 0;
77796
+ return {
77797
+ text,
77798
+ category,
77799
+ createdAt: typeof p.createdAt === "string" ? p.createdAt : e.timestamp,
77800
+ sourceCoordinator: typeof p.sourceCoordinator === "string" ? p.sourceCoordinator : void 0
77801
+ };
77802
+ }).filter((n) => n !== null);
77803
+ return notes.length ? notes : void 0;
77804
+ } catch {
77805
+ return void 0;
77806
+ }
77807
+ };
77555
77808
  let mesh;
77556
77809
  if (args?.inlineMesh && typeof args.inlineMesh === "object") {
77557
77810
  mesh = args.inlineMesh;
@@ -77642,7 +77895,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
77642
77895
  if (coordinatorSetup.kind === "cli_command") {
77643
77896
  let cliCmdSystemPrompt = "";
77644
77897
  try {
77645
- cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id) });
77898
+ cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildOperatingNotesBestEffort(mesh.id) });
77646
77899
  } catch (error48) {
77647
77900
  const message = error48?.message || String(error48);
77648
77901
  LOG2.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
@@ -77819,7 +78072,7 @@ ${ptyResult.output.slice(-2e3)}`);
77819
78072
  }
77820
78073
  let systemPrompt = "";
77821
78074
  try {
77822
- systemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id) });
78075
+ systemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildOperatingNotesBestEffort(mesh.id) });
77823
78076
  } catch (error48) {
77824
78077
  const message = error48?.message || String(error48);
77825
78078
  LOG2.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);