@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/cli-adapters/provider-cli-adapter.d.ts +26 -0
- package/dist/index.js +156 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +156 -25
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-delivery-policy.d.ts +7 -0
- package/dist/mesh/mesh-runtime-store.d.ts +25 -0
- package/package.json +3 -3
- package/src/cli-adapters/provider-cli-adapter.ts +40 -3
- package/src/mesh/mesh-delivery-policy.ts +24 -4
- package/src/mesh/mesh-event-forwarding.ts +9 -11
- package/src/mesh/mesh-reconcile-loop.ts +42 -2
- package/src/mesh/mesh-runtime-store.ts +103 -1
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 ? "
|
|
418
|
-
const commitShort = readInjected(true ? "
|
|
419
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
420
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
26905
|
-
if (!startupModal &&
|
|
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.
|
|
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");
|