@adhdev/daemon-core 0.9.82-rc.389 → 0.9.82-rc.390
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 +54 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +54 -10
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +20 -0
- package/package.json +2 -2
- package/src/commands/high-family/mesh-events.ts +23 -2
- package/src/providers/cli-provider-instance.ts +71 -5
|
@@ -62,6 +62,24 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
62
62
|
* from scratch rather than firing on a stale timestamp.
|
|
63
63
|
*/
|
|
64
64
|
private static readonly AUTO_APPROVE_GATE_HYSTERESIS_MS;
|
|
65
|
+
/**
|
|
66
|
+
* STATUS-MISMATCH: upper bound on how long the auto-approve→`generating` SURFACE
|
|
67
|
+
* mask may hide a worker's `waiting_approval` (status + activeModal) before we give
|
|
68
|
+
* up and surface the real prompt. The mask exists because auto-approve is expected
|
|
69
|
+
* to resolve the modal momentarily; but if it STALLS without ever calling
|
|
70
|
+
* resolveModal — the modal signature never settles for AUTO_APPROVE_SETTLE_MS (a
|
|
71
|
+
* perpetually-flapping/streaming prompt), no concrete modal is ever captured, or the
|
|
72
|
+
* modal is a picker/non-affirmative we never auto-pick — the mask would persist
|
|
73
|
+
* forever and read_chat / mesh_status / the dashboard would NEVER see the pending
|
|
74
|
+
* approval (the coordinator cannot mesh_approve what it cannot see). Once an episode
|
|
75
|
+
* exceeds this bound we stop masking. Generously larger than
|
|
76
|
+
* AUTO_APPROVE_SETTLE_MS (600) + AUTO_APPROVE_GATE_HYSTERESIS_MS (1500) so a
|
|
77
|
+
* legitimately slow-settling / blip-flapping prompt is never unmasked early; a
|
|
78
|
+
* genuine never-resolving stall surfaces within this window. The settle gate keeps
|
|
79
|
+
* running underneath, so a prompt that finally stabilises still auto-approves, and
|
|
80
|
+
* mesh_approve (raw FSM, unmasked) works throughout.
|
|
81
|
+
*/
|
|
82
|
+
private static readonly AUTO_APPROVE_MASK_STALL_MS;
|
|
65
83
|
private adapter;
|
|
66
84
|
private context;
|
|
67
85
|
private events;
|
|
@@ -80,6 +98,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
80
98
|
private pendingAutoApprovalSince;
|
|
81
99
|
private autoApproveSettleTimer;
|
|
82
100
|
private autoApproveInactiveSince;
|
|
101
|
+
private autoApproveMaskSince;
|
|
83
102
|
private readonly manualAttendance;
|
|
84
103
|
private controlValues;
|
|
85
104
|
private summaryMetadata;
|
|
@@ -249,6 +268,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
249
268
|
* CLI-specific modal text.
|
|
250
269
|
*/
|
|
251
270
|
private autoApproveEffectivelyActive;
|
|
271
|
+
private autoApproveMaskStalled;
|
|
252
272
|
private recordAutoApproval;
|
|
253
273
|
recordApprovalSelection(buttonText: string): void;
|
|
254
274
|
private formatMarkerTimestamp;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.390",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.390",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
handleMeshForwardEvent,
|
|
12
12
|
drainPendingMeshCoordinatorEvents,
|
|
13
13
|
shouldHoldPendingDrainForBusyLocalCoordinator,
|
|
14
|
+
resolveCoordinatorDrainDeliverability,
|
|
14
15
|
} from '../../mesh/mesh-events.js';
|
|
15
16
|
import { normalizeInteractivePromptResponse } from '../../providers/types/interactive-prompt.js';
|
|
16
17
|
import type { HighFamilyContext, HighFamilyHandler } from './types.js';
|
|
@@ -36,11 +37,31 @@ export const meshEventsHandlers: Record<string, HighFamilyHandler> = {
|
|
|
36
37
|
// loop: return nothing, leaving the rows undrained for its idle-tick delivery. A
|
|
37
38
|
// remote pull (foreign coordinatorDaemonId) or a pure stdio MCP coordinator (no live
|
|
38
39
|
// CLI session) is NOT held — see shouldHoldPendingDrainForBusyLocalCoordinator.
|
|
40
|
+
// Surface whether THIS daemon has a live CLI coordinator for the mesh. The MCP
|
|
41
|
+
// (LLM) coordinator's pull (drainCoordinatorPendingEvents) needs this to decide its
|
|
42
|
+
// delivery surface: when there is NO live CLI coordinator (pure stdio MCP/LLM), the
|
|
43
|
+
// MCP tool result is the ONLY surface, so the puller must return the drained events
|
|
44
|
+
// to the LLM rather than re-forwarding them (a re-forward just re-queues with no PTY
|
|
45
|
+
// to inject into — the NOTIF-DROP transcript-reconcile drain-without-inject loop).
|
|
46
|
+
// When a live CLI coordinator exists, the reconcile loop owns PTY delivery and the
|
|
47
|
+
// puller keeps forwarding (unchanged). NOTE: this is observational only — it does not
|
|
48
|
+
// change what is drained here, so the idle full-drain and remote-pull paths are intact.
|
|
49
|
+
const hasLiveCliCoordinator = meshId
|
|
50
|
+
? resolveCoordinatorDrainDeliverability(ctx.deps, meshId).hasLiveCliCoordinator
|
|
51
|
+
: false;
|
|
52
|
+
// DRAIN-WITHOUT-INJECT guard: when a LOCAL live CLI coordinator for this mesh is
|
|
53
|
+
// busy (generating / modal-parked), the reconcile loop is HOLDING its terminal
|
|
54
|
+
// events (drained=0) for the coordinator's next idle tick. Draining here would
|
|
55
|
+
// consume those held rows (drained=1) into an MCP tool result the busy coordinator
|
|
56
|
+
// never surfaces as a turn — losing the completion forever. Defer to the reconcile
|
|
57
|
+
// loop: return nothing, leaving the rows undrained for its idle-tick delivery. A
|
|
58
|
+
// remote pull (foreign coordinatorDaemonId) or a pure stdio MCP coordinator (no live
|
|
59
|
+
// CLI session) is NOT held — see shouldHoldPendingDrainForBusyLocalCoordinator.
|
|
39
60
|
if (meshId && shouldHoldPendingDrainForBusyLocalCoordinator(ctx.deps, meshId, coordinatorDaemonId)) {
|
|
40
|
-
return { success: true, events: [], heldForBusyLocalCoordinator: true };
|
|
61
|
+
return { success: true, events: [], heldForBusyLocalCoordinator: true, hasLiveCliCoordinator };
|
|
41
62
|
}
|
|
42
63
|
const events = drainPendingMeshCoordinatorEvents(meshId || undefined, coordinatorDaemonId);
|
|
43
|
-
return { success: true, events };
|
|
64
|
+
return { success: true, events, hasLiveCliCoordinator };
|
|
44
65
|
},
|
|
45
66
|
|
|
46
67
|
interactive_prompt_response: async (ctx: HighFamilyContext, args: any) => {
|
|
@@ -426,6 +426,25 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
426
426
|
*/
|
|
427
427
|
private static readonly AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
|
|
428
428
|
|
|
429
|
+
/**
|
|
430
|
+
* STATUS-MISMATCH: upper bound on how long the auto-approve→`generating` SURFACE
|
|
431
|
+
* mask may hide a worker's `waiting_approval` (status + activeModal) before we give
|
|
432
|
+
* up and surface the real prompt. The mask exists because auto-approve is expected
|
|
433
|
+
* to resolve the modal momentarily; but if it STALLS without ever calling
|
|
434
|
+
* resolveModal — the modal signature never settles for AUTO_APPROVE_SETTLE_MS (a
|
|
435
|
+
* perpetually-flapping/streaming prompt), no concrete modal is ever captured, or the
|
|
436
|
+
* modal is a picker/non-affirmative we never auto-pick — the mask would persist
|
|
437
|
+
* forever and read_chat / mesh_status / the dashboard would NEVER see the pending
|
|
438
|
+
* approval (the coordinator cannot mesh_approve what it cannot see). Once an episode
|
|
439
|
+
* exceeds this bound we stop masking. Generously larger than
|
|
440
|
+
* AUTO_APPROVE_SETTLE_MS (600) + AUTO_APPROVE_GATE_HYSTERESIS_MS (1500) so a
|
|
441
|
+
* legitimately slow-settling / blip-flapping prompt is never unmasked early; a
|
|
442
|
+
* genuine never-resolving stall surfaces within this window. The settle gate keeps
|
|
443
|
+
* running underneath, so a prompt that finally stabilises still auto-approves, and
|
|
444
|
+
* mesh_approve (raw FSM, unmasked) works throughout.
|
|
445
|
+
*/
|
|
446
|
+
private static readonly AUTO_APPROVE_MASK_STALL_MS = 4500;
|
|
447
|
+
|
|
429
448
|
private adapter: ProviderCliAdapter;
|
|
430
449
|
private context: InstanceContext | null = null;
|
|
431
450
|
private events: ProviderEvent[] = [];
|
|
@@ -459,6 +478,14 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
459
478
|
// a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
|
|
460
479
|
// brief generating flip does not immediately wipe the settle clock.
|
|
461
480
|
private autoApproveInactiveSince = 0;
|
|
481
|
+
// STATUS-MISMATCH: wall-clock when the CURRENT auto-approve episode (waiting_approval
|
|
482
|
+
// + shouldAutoApprove) first began wanting to mask. Unlike pendingAutoApprovalSince it
|
|
483
|
+
// is NOT reset when the modal signature changes (a still-streaming/flapping prompt) and
|
|
484
|
+
// survives the same hysteresis blips the settle gate does, so it measures the TRUE age
|
|
485
|
+
// of an unresolved auto-approve. Once it exceeds AUTO_APPROVE_MASK_STALL_MS the surface
|
|
486
|
+
// mask is dropped so the real waiting_approval surfaces. Cleared when the episode ends
|
|
487
|
+
// (modal genuinely gone, manual attendance takes over, or auto-approve fires).
|
|
488
|
+
private autoApproveMaskSince = 0;
|
|
462
489
|
// Provider-common manual-attendance signal: while a human is actively driving
|
|
463
490
|
// this session from the dashboard, auto-approve holds so they can take manual
|
|
464
491
|
// control. Background mesh workers are never attended → delegated auto-approve
|
|
@@ -696,7 +723,13 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
696
723
|
this.provider,
|
|
697
724
|
typeof adapterStatus?.providerSessionId === 'string' ? adapterStatus.providerSessionId : '',
|
|
698
725
|
);
|
|
699
|
-
const
|
|
726
|
+
const nowMs = Date.now();
|
|
727
|
+
// STATUS-MISMATCH: maybeAutoApproveStatus still runs for its side effects (settle gate,
|
|
728
|
+
// resolveModal fire), but the SURFACE mask is dropped once the episode has stalled past
|
|
729
|
+
// AUTO_APPROVE_MASK_STALL_MS — otherwise a never-settling auto-approve hides the worker's
|
|
730
|
+
// waiting_approval + modal from read_chat/mesh_status/dashboard forever.
|
|
731
|
+
const autoApproveActive = this.maybeAutoApproveStatus(adapterStatus, nowMs)
|
|
732
|
+
&& !this.autoApproveMaskStalled(nowMs);
|
|
700
733
|
const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === 'idle';
|
|
701
734
|
let visibleStatus = parseErrorMessage || parsedStatus?.status === 'error'
|
|
702
735
|
? 'error'
|
|
@@ -884,7 +917,10 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
884
917
|
|
|
885
918
|
getHotChatSessionState(): HotChatSessionState {
|
|
886
919
|
const adapterStatus = this.adapter.getStatus({ allowParse: false });
|
|
887
|
-
const
|
|
920
|
+
const nowMs = Date.now();
|
|
921
|
+
// STATUS-MISMATCH: drop the mask once the auto-approve episode has stalled (see getState).
|
|
922
|
+
const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status, nowMs)
|
|
923
|
+
&& !this.autoApproveMaskStalled(nowMs);
|
|
888
924
|
const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === 'idle';
|
|
889
925
|
const visibleStatus = autoApproveActive || autoApproveHoldIdle ? 'generating' : adapterStatus.status;
|
|
890
926
|
const runtime = this.adapter.getRuntimeMetadata();
|
|
@@ -900,7 +936,10 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
900
936
|
|
|
901
937
|
getSessionModalState(sessionId?: string): SessionModalState {
|
|
902
938
|
const adapterStatus = this.adapter.getStatus({ allowParse: true });
|
|
903
|
-
const
|
|
939
|
+
const nowMs = Date.now();
|
|
940
|
+
// STATUS-MISMATCH: drop the mask once the auto-approve episode has stalled (see getState).
|
|
941
|
+
const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status, nowMs)
|
|
942
|
+
&& !this.autoApproveMaskStalled(nowMs);
|
|
904
943
|
const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === 'idle';
|
|
905
944
|
const visibleStatus = autoApproveActive || autoApproveHoldIdle ? 'generating' : adapterStatus.status;
|
|
906
945
|
const dirName = workingDirBasename(this.workingDir);
|
|
@@ -1045,8 +1084,11 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1045
1084
|
}
|
|
1046
1085
|
// A session whose auto-approve is held by manual attendance IS parked on a
|
|
1047
1086
|
// modal awaiting the human — autoApproveEffectivelyActive folds that in, so
|
|
1048
|
-
// the mesh force-inject guard correctly treats it as modal-parked.
|
|
1049
|
-
|
|
1087
|
+
// the mesh force-inject guard correctly treats it as modal-parked. STATUS-MISMATCH:
|
|
1088
|
+
// a STALLED auto-approve (never resolving) is likewise effectively parked — treat it
|
|
1089
|
+
// as modal-parked so its events are held/surfaced rather than masked behind generating.
|
|
1090
|
+
if (adapterStatus.status === 'waiting_approval'
|
|
1091
|
+
&& (!this.autoApproveEffectivelyActive(adapterStatus.status) || this.autoApproveMaskStalled())) {
|
|
1050
1092
|
return 'waiting_approval';
|
|
1051
1093
|
}
|
|
1052
1094
|
return null;
|
|
@@ -1750,6 +1792,9 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1750
1792
|
this.pendingAutoApprovalSignature = '';
|
|
1751
1793
|
this.pendingAutoApprovalSince = 0;
|
|
1752
1794
|
this.autoApproveInactiveSince = 0;
|
|
1795
|
+
// Manual attendance takes over — the modal stays surfaced (maybeAutoApproveStatus
|
|
1796
|
+
// returns false), so end the mask episode.
|
|
1797
|
+
this.autoApproveMaskSince = 0;
|
|
1753
1798
|
if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
|
|
1754
1799
|
this.autoApproveSettleTimer = setTimeout(() => {
|
|
1755
1800
|
this.autoApproveSettleTimer = null;
|
|
@@ -1791,12 +1836,19 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1791
1836
|
this.pendingAutoApprovalSignature = '';
|
|
1792
1837
|
this.pendingAutoApprovalSince = 0;
|
|
1793
1838
|
this.autoApproveInactiveSince = 0;
|
|
1839
|
+
// Modal has genuinely been gone past the hysteresis window → the episode ended;
|
|
1840
|
+
// end the mask episode too (a later approval starts a fresh stall clock).
|
|
1841
|
+
this.autoApproveMaskSince = 0;
|
|
1794
1842
|
if (this.autoApproveSettleTimer) { clearTimeout(this.autoApproveSettleTimer); this.autoApproveSettleTimer = null; }
|
|
1795
1843
|
return autoApproveActive;
|
|
1796
1844
|
}
|
|
1797
1845
|
// Active approval observed — reset the inactivity tracker so a later
|
|
1798
1846
|
// blip starts its hysteresis window fresh.
|
|
1799
1847
|
this.autoApproveInactiveSince = 0;
|
|
1848
|
+
// STATUS-MISMATCH: start (or keep) the mask-stall clock for this episode. Set ONLY
|
|
1849
|
+
// when zero so it survives modal-signature changes and hysteresis blips — it measures
|
|
1850
|
+
// the true age of the unresolved auto-approve, not the per-signature settle window.
|
|
1851
|
+
if (!this.autoApproveMaskSince) this.autoApproveMaskSince = now;
|
|
1800
1852
|
const modal = adapterStatus.activeModal;
|
|
1801
1853
|
// (fix) Do not auto-approve when no concrete modal/buttons are present.
|
|
1802
1854
|
// Claude TUI flaps between paints; without this guard adapterStatus
|
|
@@ -1900,6 +1952,8 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1900
1952
|
this.pendingAutoApprovalSignature = '';
|
|
1901
1953
|
this.pendingAutoApprovalSince = 0;
|
|
1902
1954
|
this.autoApproveInactiveSince = 0;
|
|
1955
|
+
// Fired (resolveModal in flight) — the episode resolved; end the mask-stall clock.
|
|
1956
|
+
this.autoApproveMaskSince = 0;
|
|
1903
1957
|
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
1904
1958
|
this.autoApproveBusyTimer = setTimeout(() => {
|
|
1905
1959
|
this.autoApproveBusy = false;
|
|
@@ -2484,6 +2538,18 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
2484
2538
|
&& !this.manualAttendance.isAttended(now);
|
|
2485
2539
|
}
|
|
2486
2540
|
|
|
2541
|
+
// STATUS-MISMATCH: true once the current auto-approve episode has been masking
|
|
2542
|
+
// waiting_approval behind `generating` for longer than AUTO_APPROVE_MASK_STALL_MS without
|
|
2543
|
+
// resolving (the settle gate never fired). When stalled, the surface mask must be dropped
|
|
2544
|
+
// so read_chat / mesh_status / the dashboard see the real waiting_approval + modal (and a
|
|
2545
|
+
// coordinator can mesh_approve it). autoApproveMaskSince is maintained by
|
|
2546
|
+
// maybeAutoApproveStatus (driven by getState + the recheck timer during a waiting episode);
|
|
2547
|
+
// this read is side-effect-free so getStatusMetadata can consult it too.
|
|
2548
|
+
private autoApproveMaskStalled(now = Date.now()): boolean {
|
|
2549
|
+
return this.autoApproveMaskSince > 0
|
|
2550
|
+
&& now - this.autoApproveMaskSince > CliProviderInstance.AUTO_APPROVE_MASK_STALL_MS;
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2487
2553
|
private recordAutoApproval(modalMessage?: string, buttonLabel?: string, now = Date.now()): void {
|
|
2488
2554
|
this.appendRuntimeSystemMessage(
|
|
2489
2555
|
formatAutoApprovalMessage(modalMessage, buttonLabel),
|