@adhdev/daemon-core 0.9.82-rc.532 → 0.9.82-rc.534

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
@@ -414,10 +414,10 @@ function readInjected(value) {
414
414
  }
415
415
  function getDaemonBuildInfo() {
416
416
  if (cached) return cached;
417
- const commit = readInjected(true ? "2dcb1909a41f2358986e408601c8a1284c6f3685" : void 0) ?? "unknown";
418
- const commitShort = readInjected(true ? "2dcb1909" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
419
- const version = readInjected(true ? "0.9.82-rc.532" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
420
- const builtAt = readInjected(true ? "2026-07-15T07:14:52.163Z" : void 0);
417
+ const commit = readInjected(true ? "7b6411ebc267c71484a3f7d16c392b5bd3e71bd8" : void 0) ?? "unknown";
418
+ const commitShort = readInjected(true ? "7b6411eb" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
419
+ const version = readInjected(true ? "0.9.82-rc.534" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
420
+ const builtAt = readInjected(true ? "2026-07-15T08:30:18.262Z" : void 0);
421
421
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
422
422
  return cached;
423
423
  }
@@ -5626,12 +5626,16 @@ function getActiveSessionDeliveries(meshId, sessionId) {
5626
5626
  return [];
5627
5627
  }
5628
5628
  }
5629
+ function consumeSessionDelivery(meshId, sessionId, status, taskId) {
5630
+ try {
5631
+ return MeshRuntimeStore.getInstance().consumeSessionDelivery(meshId, sessionId, status, taskId);
5632
+ } catch {
5633
+ return 0;
5634
+ }
5635
+ }
5629
5636
  function markSessionDeliveriesTerminal(meshId, sessionId, terminalStatus) {
5630
5637
  try {
5631
- const active = MeshRuntimeStore.getInstance().getActiveSessionDeliveries(meshId, sessionId);
5632
- for (const delivery of active) {
5633
- MeshRuntimeStore.getInstance().updateSessionDeliveryStatus(delivery.id, terminalStatus);
5634
- }
5638
+ MeshRuntimeStore.getInstance().markOpenSessionDeliveriesTerminal(meshId, sessionId, terminalStatus);
5635
5639
  } catch {
5636
5640
  }
5637
5641
  }
@@ -7678,6 +7682,25 @@ var init_mesh_runtime_store = __esm({
7678
7682
  });
7679
7683
  this.maybeCheckpointWal();
7680
7684
  }
7685
+ // DELIVERED-NOT-CONSUMED-REDRIVE monotonic FSM: the forward-progress lifecycle of a
7686
+ // delivery is a strictly increasing rank — a status may only advance, never regress.
7687
+ // The redrive bug was a NON-monotonic FSM: the transport-confirm callback
7688
+ // (mesh-queue-assignment :384) writes 'delivered' unconditionally by PK, so when the
7689
+ // worker's agent:generating_started raced AHEAD of the confirm and already flipped the
7690
+ // row 'delivering'→'acked', the late confirm CLOBBERED 'acked' back to 'delivered'.
7691
+ // taskDeliveryConsumed() (which keys on 'acked'/'completed') then read false forever,
7692
+ // and the short-grace re-drive re-opened an already-consumed task. Enforcing the rank
7693
+ // ordering here makes the two event orders converge on the same monotone terminal state
7694
+ // regardless of arrival order, so a late confirm can never demote a consumed delivery.
7695
+ // 'failed'/'expired'/'cancelled' are absorbing OUTCOMES, not progress ranks — they are
7696
+ // always allowed (a genuine dispatch failure must be recordable even from 'acked').
7697
+ static DELIVERY_PROGRESS_RANK = {
7698
+ queued: 0,
7699
+ delivering: 1,
7700
+ delivered: 2,
7701
+ acked: 3,
7702
+ completed: 4
7703
+ };
7681
7704
  updateSessionDeliveryStatus(id, status, opts) {
7682
7705
  const now = (/* @__PURE__ */ new Date()).toISOString();
7683
7706
  if (opts?.incrementAttempt) {
@@ -7686,13 +7709,75 @@ var init_mesh_runtime_store = __esm({
7686
7709
  SET status = @status, last_error = @lastError, attempt_count = attempt_count + 1, updated_at = @updatedAt
7687
7710
  WHERE id = @id
7688
7711
  `).run({ id, status, lastError: opts?.lastError ?? null, updatedAt: now });
7689
- } else {
7712
+ return;
7713
+ }
7714
+ const targetRank = _MeshRuntimeStore.DELIVERY_PROGRESS_RANK[status];
7715
+ if (targetRank === void 0) {
7690
7716
  this.db.prepare(`
7691
7717
  UPDATE mesh_session_delivery
7692
7718
  SET status = @status, last_error = @lastError, updated_at = @updatedAt
7693
7719
  WHERE id = @id
7694
7720
  `).run({ id, status, lastError: opts?.lastError ?? null, updatedAt: now });
7721
+ return;
7695
7722
  }
7723
+ this.db.prepare(`
7724
+ UPDATE mesh_session_delivery
7725
+ SET status = @status, last_error = @lastError, updated_at = @updatedAt
7726
+ WHERE id = @id AND (@targetRank >= CASE status
7727
+ WHEN 'queued' THEN 0 WHEN 'delivering' THEN 1 WHEN 'delivered' THEN 2
7728
+ WHEN 'acked' THEN 3 WHEN 'completed' THEN 4 ELSE 99 END)
7729
+ `).run({ id, status, lastError: opts?.lastError ?? null, updatedAt: now, targetRank });
7730
+ }
7731
+ /**
7732
+ * DELIVERED-NOT-CONSUMED-REDRIVE consume path. Advance a task's delivery record(s) to a
7733
+ * CONSUMED status ('acked' or 'completed'), matching on mesh + session (+ taskId when the
7734
+ * event names one) and INCLUDING rows already in 'delivered'/'acked'/'delivering'.
7735
+ *
7736
+ * The ack/terminal callers previously routed through getActiveSessionDeliveries(), whose SQL
7737
+ * EXCLUDES 'delivered' — so in the normal event order (transport confirm flips 'delivered'
7738
+ * BEFORE the worker's generating_started fires) the ack matched zero rows and the delivery
7739
+ * was stranded 'delivered', never 'acked'. This finds the row by (mesh, session[, task])
7740
+ * directly and relies on updateSessionDeliveryStatus's monotonic guard to only advance it.
7741
+ * Returns the number of rows advanced.
7742
+ */
7743
+ consumeSessionDelivery(meshId, sessionId, status, taskId) {
7744
+ const rows = this.db.prepare(
7745
+ taskId ? `SELECT id, session_id FROM mesh_session_delivery
7746
+ WHERE mesh_id = ? AND task_id = ?
7747
+ AND status IN ('queued','delivering','delivered','acked')` : `SELECT id, session_id FROM mesh_session_delivery
7748
+ WHERE mesh_id = ? AND session_id = ?
7749
+ AND status IN ('queued','delivering','delivered','acked')`
7750
+ ).all(meshId, taskId ?? sessionId);
7751
+ let advanced = 0;
7752
+ for (const r of rows) {
7753
+ if (!sessionIdsEquivalent(r.session_id ?? void 0, sessionId)) continue;
7754
+ this.updateSessionDeliveryStatus(r.id, status);
7755
+ advanced++;
7756
+ }
7757
+ return advanced;
7758
+ }
7759
+ /**
7760
+ * DELIVERED-NOT-CONSUMED-REDRIVE terminal path. Mark every OPEN delivery for a session
7761
+ * (queued/delivering/delivered/acked) terminal on task completion/failure. The prior
7762
+ * markSessionDeliveriesTerminal() routed through getActiveSessionDeliveries(), whose SQL
7763
+ * EXCLUDES 'delivered'/'completed' — so a 'delivered' row (the common case, since the
7764
+ * transport confirm flips it before the completion event) was never marked terminal and
7765
+ * stayed 'delivered', keeping taskDeliveryConsumed() false and feeding the false re-drive.
7766
+ * We match rows in OPEN states directly here. 'completed' advances monotonically (it is the
7767
+ * top progress rank); 'failed' is an absorbing outcome written unconditionally.
7768
+ */
7769
+ markOpenSessionDeliveriesTerminal(meshId, sessionId, terminalStatus) {
7770
+ const rows = this.db.prepare(
7771
+ `SELECT id, session_id FROM mesh_session_delivery
7772
+ WHERE mesh_id = ? AND status IN ('queued','delivering','delivered','acked')`
7773
+ ).all(meshId);
7774
+ let marked = 0;
7775
+ for (const r of rows) {
7776
+ if (!sessionIdsEquivalent(r.session_id ?? void 0, sessionId)) continue;
7777
+ this.updateSessionDeliveryStatus(r.id, terminalStatus);
7778
+ marked++;
7779
+ }
7780
+ return marked;
7696
7781
  }
7697
7782
  getActiveSessionDeliveries(meshId, sessionId) {
7698
7783
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -20629,17 +20714,7 @@ function injectMeshSystemMessage(components, args) {
20629
20714
  updateDirectDispatchStatus(args.meshId, sessionId, "acked", soleTaskId);
20630
20715
  }
20631
20716
  }
20632
- const activeDeliveries = (() => {
20633
- try {
20634
- return MeshRuntimeStore.getInstance().getActiveSessionDeliveries(args.meshId, sessionId);
20635
- } catch {
20636
- return [];
20637
- }
20638
- })();
20639
- const deliveriesToAck = startedTaskId ? activeDeliveries.filter((d) => d.taskId === startedTaskId) : activeDeliveries;
20640
- for (const d of deliveriesToAck) {
20641
- updateSessionDeliveryStatus(d.id, "acked");
20642
- }
20717
+ consumeSessionDelivery(args.meshId, sessionId, "acked", startedTaskId);
20643
20718
  }
20644
20719
  } else if (args.event === "agent:stopped") {
20645
20720
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
@@ -22079,6 +22154,9 @@ function recoverStrandedAssignedDispatches(components, meshId, store) {
22079
22154
  for (const key2 of [...deliveredNoTurnUnknownStreak.keys()]) {
22080
22155
  if (key2.startsWith(meshKeyPrefix) && !assignedKeys.has(key2)) deliveredNoTurnUnknownStreak.delete(key2);
22081
22156
  }
22157
+ for (const key2 of [...deliveredUnconsumedUnknownStreak.keys()]) {
22158
+ if (key2.startsWith(meshKeyPrefix) && !assignedKeys.has(key2)) deliveredUnconsumedUnknownStreak.delete(key2);
22159
+ }
22082
22160
  for (const row of assigned) {
22083
22161
  const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
22084
22162
  if (!Number.isFinite(dispatchedAtMs)) continue;
@@ -22090,13 +22168,33 @@ function recoverStrandedAssignedDispatches(components, meshId, store) {
22090
22168
  updateTaskStatus(meshId, row.id, status);
22091
22169
  continue;
22092
22170
  }
22171
+ const shortStreakKey = `${meshId}::${row.id}`;
22093
22172
  const verdict = row.assignedSessionId ? resolveSessionBusyVerdict(components, row.assignedSessionId) : "IDLE_CONFIRMED";
22094
- if (verdict !== "GENERATING") {
22173
+ if (verdict === "GENERATING") {
22174
+ deliveredUnconsumedUnknownStreak.delete(shortStreakKey);
22175
+ } else {
22176
+ if (verdict === "IDLE_CONFIRMED") {
22177
+ deliveredUnconsumedUnknownStreak.delete(shortStreakKey);
22178
+ } else {
22179
+ const streak = (deliveredUnconsumedUnknownStreak.get(shortStreakKey) ?? 0) + 1;
22180
+ deliveredUnconsumedUnknownStreak.set(shortStreakKey, streak);
22181
+ if (streak < RECLAIM_UNKNOWN_GRACE_TICKS) {
22182
+ traceMeshEventDrop("short_redrive_deferred_unknown_verdict", {
22183
+ taskId: row.id,
22184
+ sessionId: row.assignedSessionId,
22185
+ nodeId: row.assignedNodeId,
22186
+ meshId,
22187
+ event: "agent:generating_started"
22188
+ }, `unknown ${streak}/${RECLAIM_UNKNOWN_GRACE_TICKS}`);
22189
+ continue;
22190
+ }
22191
+ }
22095
22192
  const redriven = reclaimStrandedAssignedTask(meshId, row.id, {
22096
22193
  reason: "delivered_not_consumed_redrive",
22097
22194
  ageMs
22098
22195
  });
22099
22196
  if (redriven) {
22197
+ deliveredUnconsumedUnknownStreak.delete(shortStreakKey);
22100
22198
  LOG.warn("MeshReconcile", `Re-drove delivered-but-unconsumed task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}, delivered but no generating_started in ${Math.round(ageMs / 1e3)}s, verdict ${verdict} \u2192 ${redriven.status})`);
22101
22199
  traceMeshEventDrop("assigned_delivered_not_consumed_redrive", {
22102
22200
  taskId: row.id,
@@ -22651,7 +22749,7 @@ function setupMeshReconcileLoop(components) {
22651
22749
  }
22652
22750
  };
22653
22751
  }
22654
- var coordinatorModalParkState, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, DELIVERED_NO_TURN_DEADLINE_MS, ASSIGNED_DELIVERED_UNCONSUMED_REDRIVE_MS, RECLAIM_UNKNOWN_GRACE_TICKS, deliveredNoTurnUnknownStreak, ZOMBIE_ASSIGNED_MIN_AGE_MS, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS, UNRESOLVED_FORWARD_NUDGE_DELAY_MS, unresolvedForwardNudgeTimer, unresolvedForwardNudgeRunning;
22752
+ var coordinatorModalParkState, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, DELIVERED_NO_TURN_DEADLINE_MS, ASSIGNED_DELIVERED_UNCONSUMED_REDRIVE_MS, RECLAIM_UNKNOWN_GRACE_TICKS, deliveredNoTurnUnknownStreak, deliveredUnconsumedUnknownStreak, ZOMBIE_ASSIGNED_MIN_AGE_MS, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS, UNRESOLVED_FORWARD_NUDGE_DELAY_MS, unresolvedForwardNudgeTimer, unresolvedForwardNudgeRunning;
22655
22753
  var init_mesh_reconcile_loop = __esm({
22656
22754
  "src/mesh/mesh-reconcile-loop.ts"() {
22657
22755
  "use strict";
@@ -22685,6 +22783,7 @@ var init_mesh_reconcile_loop = __esm({
22685
22783
  ASSIGNED_DELIVERED_UNCONSUMED_REDRIVE_MS = 25e3;
22686
22784
  RECLAIM_UNKNOWN_GRACE_TICKS = 3;
22687
22785
  deliveredNoTurnUnknownStreak = /* @__PURE__ */ new Map();
22786
+ deliveredUnconsumedUnknownStreak = /* @__PURE__ */ new Map();
22688
22787
  ZOMBIE_ASSIGNED_MIN_AGE_MS = 30 * 60 * 1e3;
22689
22788
  STRICT_SESSION_MATCH_TTL_MS = 6e4;
22690
22789
  unresolvedForwardRejectionCounts = /* @__PURE__ */ new Map();
@@ -26901,8 +27000,8 @@ ${lastSnapshot}`;
26901
27000
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
26902
27001
  if (stableMs < 2e3) return;
26903
27002
  const startupModal = this.runParseApproval(this.recentOutputBuffer);
26904
- const startupStatus = this.runDetectStatus(screenText || this.recentOutputBuffer);
26905
- if (!startupModal && startupStatus !== "idle") {
27003
+ const startupIdle = this.detectIdleHonoringOnNoMatch(screenText || this.recentOutputBuffer);
27004
+ if (!startupModal && !startupIdle) {
26906
27005
  this.scheduleStartupSettleCheck();
26907
27006
  return;
26908
27007
  }
@@ -27038,6 +27137,38 @@ ${lastSnapshot}`;
27038
27137
  tailScreen: buildCliScreenSnapshot(tail)
27039
27138
  });
27040
27139
  }
27140
+ /**
27141
+ * WRITE-READINESS ONNOMATCH (opencode): resolve whether the session is
27142
+ * genuinely idle *for the purpose of opening the PTY write gate*, honoring
27143
+ * the manifest's `dispatchOrder.onNoMatch` policy.
27144
+ *
27145
+ * The split-brain this closes: the engine's settled evaluation runs
27146
+ * detectStatus through parseSession, whose builder collapses a null verdict
27147
+ * to `'idle'` (buildParseSessionFromTui: `detectStatus(input) ?? 'idle'`),
27148
+ * so the dashboard status flips to idle via `script_detect`. But the
27149
+ * write-readiness gates (resolveStartupState / sendMessage recovery) call
27150
+ * `runDetectStatus` *directly* and require the literal `=== 'idle'` return —
27151
+ * they never apply the `onNoMatch` policy. opencode's only idle cue is the
27152
+ * `Ask anything` composer placeholder in the last-8-lines scope with
27153
+ * `onNoMatch: preserve-last`; when that placeholder is momentarily out of
27154
+ * frame the raw detector returns null, the gate stays shut, `this.ready`
27155
+ * never flips, and the first queued prompt sits in `not_ready_pending_prompt`
27156
+ * forever (no turn ever starts, so no turn-completion drain fires).
27157
+ *
27158
+ * The fix keeps the raw detector as the primary signal (unchanged behavior
27159
+ * for providers whose detector returns a literal idle) and only falls back
27160
+ * when BOTH (a) the manifest policy is idle-preserving (`preserve-last` or
27161
+ * `idle`) AND (b) the engine has *durably* settled to idle (no in-flight
27162
+ * turn, no modal, no parse error). That guard makes the fallback safe: it
27163
+ * cannot open the gate mid-turn or while a modal is up.
27164
+ */
27165
+ detectIdleHonoringOnNoMatch(text) {
27166
+ if (this.runDetectStatus(text) === "idle") return true;
27167
+ const dispatchOrder = this.provider.tui?.dispatchOrder;
27168
+ const onNoMatch = dispatchOrder?.onNoMatch;
27169
+ if (onNoMatch !== "preserve-last" && onNoMatch !== "idle") return false;
27170
+ return this.engine.currentStatus === "idle" && !this.engine.isWaitingForResponse && !this.engine.currentTurnScope && !this.engine.activeModal && !this.parseErrorMessage;
27171
+ }
27041
27172
  runParseApproval(tail) {
27042
27173
  const screenText = this.terminalScreen.getText();
27043
27174
  const buffer = screenText || this.accumulatedBuffer;
@@ -27570,7 +27701,7 @@ ${lastSnapshot}`;
27570
27701
  }
27571
27702
  if (!this.ready) {
27572
27703
  this.resolveStartupState("send_precheck");
27573
- if (this.runDetectStatus(this.recentOutputBuffer) === "idle") {
27704
+ if (!this.ready && this.detectIdleHonoringOnNoMatch(this.recentOutputBuffer)) {
27574
27705
  this.ready = true;
27575
27706
  this.startupParseGate = false;
27576
27707
  this.engine.setStatus("idle", "send_message_idle_prompt_recovery");