@adhdev/daemon-core 0.9.82-rc.362 → 0.9.82-rc.363
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/cli-state-engine.d.ts +1 -1
- package/dist/cli-adapters/provider-cli-shared.d.ts +10 -0
- package/dist/index.js +77 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -8
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-utils.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -0
- package/package.json +2 -2
- package/src/cli-adapters/cli-state-engine.ts +7 -1
- package/src/cli-adapters/provider-cli-adapter.ts +1 -0
- package/src/cli-adapters/provider-cli-shared.ts +10 -0
- package/src/mesh/mesh-events-coordinator.ts +37 -0
- package/src/mesh/mesh-events-utils.ts +28 -2
- package/src/providers/cli-provider-instance.ts +46 -0
|
@@ -83,7 +83,7 @@ export declare class CliStateEngine {
|
|
|
83
83
|
* seq) is always a real, distinct approval and must be written.
|
|
84
84
|
*/
|
|
85
85
|
approvalEntrySeq: number;
|
|
86
|
-
|
|
86
|
+
lastResolvedEntrySeq: number;
|
|
87
87
|
/**
|
|
88
88
|
* When the engine previously held a modal but the latest parse failed
|
|
89
89
|
* to extract one, we record the timestamp here and only drop the modal
|
|
@@ -36,6 +36,16 @@ export interface CliSessionStatus {
|
|
|
36
36
|
* seq is the only reliable discriminator.
|
|
37
37
|
*/
|
|
38
38
|
approvalEntrySeq?: number;
|
|
39
|
+
/**
|
|
40
|
+
* The approval entry seq that the engine's last resolveModal() handled. When
|
|
41
|
+
* `lastResolvedEntrySeq >= approvalEntrySeq` (and approvalEntrySeq > 0) it is positive
|
|
42
|
+
* evidence that the current/latest approval entry was resolved through ADHDev (auto-approve
|
|
43
|
+
* fire, dashboard / mesh_approve, or dev-cli-debug — all route through resolveModal). The
|
|
44
|
+
* completion finalization gate uses this to avoid confirming a waiting_approval→idle
|
|
45
|
+
* transition for which no resolution actually occurred (a false idle: the spec's text-based
|
|
46
|
+
* approval→idle rule tripped while the modal is still on screen).
|
|
47
|
+
*/
|
|
48
|
+
lastResolvedEntrySeq?: number;
|
|
39
49
|
activeInteractivePrompt?: InteractivePrompt | null;
|
|
40
50
|
pendingOutboundCount?: number;
|
|
41
51
|
pendingOutboundMessages?: Array<{
|
package/dist/index.js
CHANGED
|
@@ -316,10 +316,10 @@ function readInjected(value) {
|
|
|
316
316
|
}
|
|
317
317
|
function getDaemonBuildInfo() {
|
|
318
318
|
if (cached) return cached;
|
|
319
|
-
const commit = readInjected(true ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
319
|
+
const commit = readInjected(true ? "4c5b30b1b0527ec5234c037893ba4fc24b88a8a0" : void 0) ?? "unknown";
|
|
320
|
+
const commitShort = readInjected(true ? "4c5b30b1" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
321
|
+
const version = readInjected(true ? "0.9.82-rc.363" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-23T16:13:33.075Z" : void 0);
|
|
323
323
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
324
324
|
return cached;
|
|
325
325
|
}
|
|
@@ -8594,6 +8594,13 @@ function readEventTimestampValue(value) {
|
|
|
8594
8594
|
}
|
|
8595
8595
|
return 0;
|
|
8596
8596
|
}
|
|
8597
|
+
function isWeakCompletionMetadata(metadataEvent) {
|
|
8598
|
+
const evidenceLevel = readNonEmptyString2(metadataEvent.evidenceLevel);
|
|
8599
|
+
if (evidenceLevel === "insufficient" || evidenceLevel === "weak") return true;
|
|
8600
|
+
if (metadataEvent.reviewRecommended === true) return true;
|
|
8601
|
+
const diag = readRecord4(metadataEvent.completionDiagnostic);
|
|
8602
|
+
return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
|
|
8603
|
+
}
|
|
8597
8604
|
function formatCompletionMetadata(event) {
|
|
8598
8605
|
const completionDiagnostic = event.completionDiagnostic && typeof event.completionDiagnostic === "object" ? event.completionDiagnostic : null;
|
|
8599
8606
|
const diagnosticReason = completionDiagnostic ? readNonEmptyString2(completionDiagnostic.blockReason) || "present" : "";
|
|
@@ -8616,17 +8623,19 @@ function buildMeshSystemMessage(args) {
|
|
|
8616
8623
|
return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The no-progress monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
|
|
8617
8624
|
}
|
|
8618
8625
|
const reviewRecommended = args.metadataEvent.reviewRecommended === true;
|
|
8626
|
+
const weakCompletion = isWeakCompletionMetadata(args.metadataEvent);
|
|
8627
|
+
const verifyTextNote = " Completion evidence is weak \u2014 verify via mesh_read_chat or git status before declaring the task done; the worker may still be parked on an approval/modal.";
|
|
8619
8628
|
const completionSummary = readMeshCompletionSummary(args.metadataEvent);
|
|
8620
8629
|
if (completionSummary) {
|
|
8621
8630
|
const truncationSuffix = "\n\u2026[truncated \u2014 call mesh_read_chat once for the full transcript]";
|
|
8622
8631
|
const surfaced = completionSummary.length > MESH_COMPLETION_SURFACE_MAX_CHARS ? `${completionSummary.slice(0, MESH_COMPLETION_SURFACE_MAX_CHARS - truncationSuffix.length)}${truncationSuffix}` : completionSummary;
|
|
8623
|
-
const verifyNote = reviewRecommended ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done." : "";
|
|
8632
|
+
const verifyNote = reviewRecommended ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done." : weakCompletion ? verifyTextNote : "";
|
|
8624
8633
|
return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. Its final summary is included below \u2014 read it directly and only call mesh_read_chat if you need the full transcript.${verifyNote}
|
|
8625
8634
|
|
|
8626
8635
|
--- ${args.nodeLabel} final summary ---
|
|
8627
8636
|
${surfaced}`;
|
|
8628
8637
|
}
|
|
8629
|
-
const reviewNote = reviewRecommended ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly." : " Use mesh_read_chat once to review its final progress, but do not poll repeatedly.";
|
|
8638
|
+
const reviewNote = reviewRecommended ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly." : weakCompletion ? `${verifyTextNote} Use mesh_read_chat once if needed, but do not poll repeatedly.` : " Use mesh_read_chat once to review its final progress, but do not poll repeatedly.";
|
|
8630
8639
|
return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
|
|
8631
8640
|
}
|
|
8632
8641
|
if (args.event === "agent:waiting_approval") {
|
|
@@ -8761,7 +8770,7 @@ function hasPendingRefineTerminalEventDuplicate(event) {
|
|
|
8761
8770
|
(pending) => pending.event === event.event && readRefineJobId2(pending) === jobId
|
|
8762
8771
|
);
|
|
8763
8772
|
}
|
|
8764
|
-
function
|
|
8773
|
+
function isWeakCompletionMetadata2(metadata) {
|
|
8765
8774
|
const evidenceLevel = readNonEmptyString2(metadata.evidenceLevel);
|
|
8766
8775
|
if (evidenceLevel === "insufficient" || evidenceLevel === "weak") return true;
|
|
8767
8776
|
if (metadata.reviewRecommended === true) return true;
|
|
@@ -8780,7 +8789,7 @@ function buildPendingEventFingerprint(event) {
|
|
|
8780
8789
|
event.meshId,
|
|
8781
8790
|
event.event,
|
|
8782
8791
|
terminalTaskId,
|
|
8783
|
-
|
|
8792
|
+
isWeakCompletionMetadata2(metadata) ? "weak" : "genuine"
|
|
8784
8793
|
].join("::");
|
|
8785
8794
|
}
|
|
8786
8795
|
}
|
|
@@ -12863,9 +12872,22 @@ function deliverTaskToSession(dispatchThunk, ctx) {
|
|
|
12863
12872
|
}
|
|
12864
12873
|
});
|
|
12865
12874
|
}
|
|
12875
|
+
function normalizeMeshWorkspaceForCompare(dir) {
|
|
12876
|
+
if (typeof dir !== "string") return "";
|
|
12877
|
+
return dir.trim().replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
12878
|
+
}
|
|
12866
12879
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
12867
12880
|
const mesh = getMeshWithCache(components, meshId);
|
|
12868
12881
|
const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
|
|
12882
|
+
const localClaimAdapter = components.cliManager?.adapters?.get(sessionId);
|
|
12883
|
+
if (localClaimAdapter) {
|
|
12884
|
+
const sessionWorkspace = normalizeMeshWorkspaceForCompare(localClaimAdapter.workingDir);
|
|
12885
|
+
const nodeWorkspace = normalizeMeshWorkspaceForCompare(readNonEmptyString2(node?.workspace));
|
|
12886
|
+
if (sessionWorkspace && nodeWorkspace && sessionWorkspace !== nodeWorkspace) {
|
|
12887
|
+
LOG.info("MeshQueue", `WTCLAIM: refusing claim for node ${nodeId} (${sessionId}) \u2014 session workspace "${sessionWorkspace}" \u2260 node workspace "${nodeWorkspace}" (cross-workspace dispatch blocked)`);
|
|
12888
|
+
return false;
|
|
12889
|
+
}
|
|
12890
|
+
}
|
|
12869
12891
|
const capabilityTags = buildMeshNodeCapabilityTags(node, providerType);
|
|
12870
12892
|
const providerMaxParallel = resolveProviderMaxParallel(node?.policy, providerType);
|
|
12871
12893
|
const task = claimNextTask(meshId, nodeId, sessionId, capabilityTags, {
|
|
@@ -17378,6 +17400,12 @@ var init_cli_state_engine = __esm({
|
|
|
17378
17400
|
* seq) is always a real, distinct approval and must be written.
|
|
17379
17401
|
*/
|
|
17380
17402
|
approvalEntrySeq = 0;
|
|
17403
|
+
// Records which approval entry the last resolveModal() handled. Set unconditionally on
|
|
17404
|
+
// every resolveModal (auto-approve fire, dashboard/mesh_approve, dev-cli-debug), so
|
|
17405
|
+
// `lastResolvedEntrySeq >= approvalEntrySeq` is positive, ADHDev-side proof that the
|
|
17406
|
+
// current/latest approval entry was actually resolved. Exposed via getStatus so the
|
|
17407
|
+
// completion finalization gate (FALSEIDLE-a) can require resolution evidence before
|
|
17408
|
+
// confirming a waiting_approval→idle transition. Public (read-only by convention).
|
|
17381
17409
|
lastResolvedEntrySeq = -1;
|
|
17382
17410
|
/**
|
|
17383
17411
|
* When the engine previously held a modal but the latest parse failed
|
|
@@ -19095,6 +19123,7 @@ ${lastSnapshot}`;
|
|
|
19095
19123
|
workingDir: this.workingDir,
|
|
19096
19124
|
activeModal: effectiveModal,
|
|
19097
19125
|
approvalEntrySeq: this.engine.approvalEntrySeq,
|
|
19126
|
+
lastResolvedEntrySeq: this.engine.lastResolvedEntrySeq,
|
|
19098
19127
|
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
19099
19128
|
pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
|
|
19100
19129
|
id: message.id,
|
|
@@ -38652,6 +38681,8 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38652
38681
|
};
|
|
38653
38682
|
}
|
|
38654
38683
|
}
|
|
38684
|
+
const approvalResolutionBlock = this.approvalResolutionFinalizationBlock(pending);
|
|
38685
|
+
if (approvalResolutionBlock) return approvalResolutionBlock;
|
|
38655
38686
|
try {
|
|
38656
38687
|
const screenText = typeof this.adapter.getScreenText === "function" ? String(this.adapter.getScreenText() || "") : "";
|
|
38657
38688
|
if (screenText) {
|
|
@@ -38664,6 +38695,44 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38664
38695
|
}
|
|
38665
38696
|
return null;
|
|
38666
38697
|
}
|
|
38698
|
+
// (FALSEIDLE-a) Positive, structural proof that the latest approval entry was resolved
|
|
38699
|
+
// through ADHDev. resolveModal() — driven by auto-approve, dashboard/mesh_approve, and
|
|
38700
|
+
// dev-cli-debug alike — advances the engine's lastResolvedEntrySeq to the current
|
|
38701
|
+
// approvalEntrySeq. So `lastResolvedEntrySeq >= approvalEntrySeq` (with a real entry,
|
|
38702
|
+
// approvalEntrySeq > 0) means the modal we last saw was actually answered. Absence of this
|
|
38703
|
+
// evidence after a waiting_approval→idle transition means the idle is suspect: the spec's
|
|
38704
|
+
// text-based approval→idle rule false-tripped while the modal is still unresolved.
|
|
38705
|
+
// Fails OPEN (returns true) when the seq fields are unavailable, so the gate can never wedge
|
|
38706
|
+
// a session on a provider/adapter that does not surface the counters.
|
|
38707
|
+
hasApprovalResolutionEvidence() {
|
|
38708
|
+
try {
|
|
38709
|
+
const status = this.adapter.getStatus({ allowParse: false });
|
|
38710
|
+
const entrySeq = typeof status?.approvalEntrySeq === "number" ? status.approvalEntrySeq : 0;
|
|
38711
|
+
if (entrySeq <= 0) return true;
|
|
38712
|
+
const resolvedSeq = typeof status?.lastResolvedEntrySeq === "number" ? status.lastResolvedEntrySeq : void 0;
|
|
38713
|
+
if (resolvedSeq === void 0) return true;
|
|
38714
|
+
return resolvedSeq >= entrySeq;
|
|
38715
|
+
} catch {
|
|
38716
|
+
return true;
|
|
38717
|
+
}
|
|
38718
|
+
}
|
|
38719
|
+
// (FALSEIDLE-a) Hold a completion that is the anomalous DIRECT waiting_approval→idle
|
|
38720
|
+
// transition with no positive resolution evidence. A genuinely resolved approval routes
|
|
38721
|
+
// through resolveModal → setStatus('generating'), so its completion's previousStatus is
|
|
38722
|
+
// 'generating' (not 'waiting_approval') and this gate never fires for it. Scoped to
|
|
38723
|
+
// delegated mesh/coordinator sessions — whose only modal-resolution path is auto-approve /
|
|
38724
|
+
// mesh_approve (both advance lastResolvedEntrySeq) — so an interactive local session, where
|
|
38725
|
+
// a human may answer the PTY prompt directly and leave no resolveModal record, is untouched.
|
|
38726
|
+
// Non-terminal: the hold is bounded by COMPLETED_FINALIZATION_MAX_WAIT_MS (30s), giving a
|
|
38727
|
+
// settling auto-approve time to fire and advance the seq, and guaranteeing no permanent wedge
|
|
38728
|
+
// if resolution ever happens via a path that does not record evidence.
|
|
38729
|
+
approvalResolutionFinalizationBlock(pending) {
|
|
38730
|
+
if (pending.previousStatus !== "waiting_approval") return null;
|
|
38731
|
+
const meshContext = !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.launchedByCoordinator);
|
|
38732
|
+
if (!meshContext) return null;
|
|
38733
|
+
if (this.hasApprovalResolutionEvidence()) return null;
|
|
38734
|
+
return { reason: "approval_resolution_unconfirmed", terminal: false };
|
|
38735
|
+
}
|
|
38667
38736
|
scheduleCompletedDebounceFlush(delayMs) {
|
|
38668
38737
|
if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
|
|
38669
38738
|
this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);
|