@adhdev/daemon-core 0.9.82-rc.404 → 0.9.82-rc.406
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 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +1 -0
- package/dist/index.js +35 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +35 -5
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/package.json +2 -2
- package/src/cli-adapters/cli-state-engine.ts +10 -0
- package/src/cli-adapters/provider-cli-adapter.ts +5 -0
- package/src/providers/cli-provider-instance.ts +55 -10
|
@@ -87,6 +87,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
87
87
|
private agentReadyEmitted;
|
|
88
88
|
private generatingStartedAt;
|
|
89
89
|
private fastCollapseSynthesizedTaskId;
|
|
90
|
+
private startupGraceCollapseAt;
|
|
90
91
|
private settings;
|
|
91
92
|
private monitor;
|
|
92
93
|
private generatingDebounceTimer;
|
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.406",
|
|
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.406",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -111,6 +111,13 @@ export class CliStateEngine {
|
|
|
111
111
|
// queued in pendingOutbound and only flushed asynchronously after idle), so the
|
|
112
112
|
// completion event carries the correct id instead of the racy session scalar.
|
|
113
113
|
currentTurnTaskId: string | null = null;
|
|
114
|
+
// GENERATING-BOUNDARY (R4d): wall-clock when the most recently STARTED turn began
|
|
115
|
+
// (set by onTurnStarted, persists past completion until the next turn starts).
|
|
116
|
+
// The startup-grace idle-stayed synthesis anchors its window on when the FIRST turn
|
|
117
|
+
// STARTED — not on when it finished — so a turn dispatched a few seconds after the
|
|
118
|
+
// grace collapse and then running for a non-trivial duration is still attributed to
|
|
119
|
+
// the startup collapse even though its COMPLETION lands past a now-anchored window.
|
|
120
|
+
currentTurnStartedAt = 0;
|
|
114
121
|
activeModal: { message: string; buttons: string[] } | null = null;
|
|
115
122
|
|
|
116
123
|
// ── Approval ─────────────────────────────────────
|
|
@@ -247,6 +254,9 @@ export class CliStateEngine {
|
|
|
247
254
|
this.currentTurnTaskId = typeof turnScope.taskId === 'string' && turnScope.taskId.trim()
|
|
248
255
|
? turnScope.taskId
|
|
249
256
|
: null;
|
|
257
|
+
// R4d: stamp the turn-start moment so the startup-grace idle-stayed synthesis can
|
|
258
|
+
// anchor its window on dispatch time rather than completion time.
|
|
259
|
+
this.currentTurnStartedAt = Date.now();
|
|
250
260
|
this.responseEpoch += 1;
|
|
251
261
|
}
|
|
252
262
|
|
|
@@ -1988,6 +1988,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1988
1988
|
// reads this when stamping completion events so they carry the completing turn's
|
|
1989
1989
|
// task rather than the racy last-write-wins session scalar.
|
|
1990
1990
|
get currentTurnTaskId(): string | null { return this.engine.currentTurnTaskId; }
|
|
1991
|
+
// R4d: wall-clock when the most recently started turn began (persists past settle).
|
|
1992
|
+
// The provider instance's startup-grace idle-stayed synthesis anchors its window on
|
|
1993
|
+
// this so a delayed-dispatch first turn whose duration overruns the now-anchored
|
|
1994
|
+
// window is still attributed to the startup collapse.
|
|
1995
|
+
get currentTurnStartedAt(): number { return this.engine.currentTurnStartedAt; }
|
|
1991
1996
|
|
|
1992
1997
|
get responseEpoch(): number { return this.engine.responseEpoch; }
|
|
1993
1998
|
set responseEpoch(v: number) { this.engine.responseEpoch = v; }
|
|
@@ -140,15 +140,20 @@ const NATIVE_HISTORY_MESH_IDLE_SETTLE_MS = 4000;
|
|
|
140
140
|
// DUPLICATE_DISPATCH_WINDOW_MS (mesh-tools) so the daemon's bubble-level guard
|
|
141
141
|
// covers the same retry horizon as the MCP-level dispatch dedup.
|
|
142
142
|
const USER_INPUT_ACK_DEDUP_WINDOW_MS = 60_000;
|
|
143
|
-
// GENERATING-BOUNDARY (
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
143
|
+
// GENERATING-BOUNDARY (R4c): window — measured from the startup-grace COLLAPSE moment
|
|
144
|
+
// (startupGraceCollapseAt), not boot — inside which a "first turn that ran+completed
|
|
145
|
+
// without the FSM ever observing a 'generating' frame" is attributed to the startup-grace
|
|
146
|
+
// collapse and synthesized (reason 'startup_grace_idle_turn_collapse').
|
|
147
|
+
// The spec's startup-grace exit is elapsed_ms=8000, so the FSM spends ~8s in 'starting'
|
|
148
|
+
// before collapsing to idle; a turn can be dispatched a few seconds AFTER that collapse
|
|
149
|
+
// (the live R4b miss: collapse at boot+8s, dispatch at boot+12.4s — already past a 12s
|
|
150
|
+
// boot-anchored window before the turn even started). Anchoring on the collapse moment
|
|
151
|
+
// covers dispatch-delay; R4d additionally anchors on the turn-START moment
|
|
152
|
+
// (engine.currentTurnStartedAt) so a non-trivial turn-DURATION cannot push the completion
|
|
153
|
+
// past a now-anchored window (the live rc.405 Probe2 miss). The strong discriminator is
|
|
154
|
+
// generatingStartedAt===0 (generating was never observed) AND a started-but-finished turn
|
|
155
|
+
// — the window only keeps the synthesized reason honest and scopes the synthesis to the
|
|
156
|
+
// boot collapse, so a much-later unobservably-fast turn is not mislabelled a startup collapse.
|
|
152
157
|
const STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS = 12_000;
|
|
153
158
|
|
|
154
159
|
/** Events that signal a dispatched mesh task has reached a terminal state.
|
|
@@ -481,6 +486,16 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
481
486
|
// steadily while the session sits idle, so this guard makes the synthesis fire
|
|
482
487
|
// AT MOST ONCE per collapsed turn.
|
|
483
488
|
private fastCollapseSynthesizedTaskId: string | null = null;
|
|
489
|
+
// GENERATING-BOUNDARY (R4c): the wall-clock moment the FSM's starting→idle
|
|
490
|
+
// startup-grace collapse was observed (set ONCE, on the first starting→idle
|
|
491
|
+
// transition this boot). The idle-stayed collapse window is anchored on THIS,
|
|
492
|
+
// not on instance/boot time (this.startedAt): the FSM spends the full 8s
|
|
493
|
+
// startup-grace sitting in 'starting' before collapsing, and a turn can be
|
|
494
|
+
// dispatched a few seconds AFTER the collapse — measuring the window from boot
|
|
495
|
+
// would have it already closed by the time that turn lands+completes (the live
|
|
496
|
+
// R4b miss: collapse at boot+8s, dispatch at boot+12.4s > the 12s boot window).
|
|
497
|
+
// Anchoring on the collapse moment makes the window cover dispatch-delay+turn.
|
|
498
|
+
private startupGraceCollapseAt: number | null = null;
|
|
484
499
|
private settings: Record<string, any> = {};
|
|
485
500
|
private monitor: StatusMonitor;
|
|
486
501
|
private generatingDebounceTimer: NodeJS.Timeout | null = null;
|
|
@@ -2382,6 +2397,11 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
2382
2397
|
}
|
|
2383
2398
|
} else if (newStatus === 'idle' && this.lastStatus === 'starting') {
|
|
2384
2399
|
this.emitAgentReadyOnce(chatTitle, now);
|
|
2400
|
+
// GENERATING-BOUNDARY (R4c): stamp the collapse moment ONCE so the
|
|
2401
|
+
// idle-stayed window below is anchored on the startup-grace collapse,
|
|
2402
|
+
// not on boot. Set only the first time so a later starting re-entry
|
|
2403
|
+
// cannot slide the window forward.
|
|
2404
|
+
if (this.startupGraceCollapseAt === null) this.startupGraceCollapseAt = now;
|
|
2385
2405
|
// GENERATING-BOUNDARY fast-collapse (R4, win32 startup-grace first turn):
|
|
2386
2406
|
// a turn dispatched into the startup-grace window can START and FINISH while
|
|
2387
2407
|
// the FSM is still in 'starting'. On a daemon whose claude-cli spec has NOT yet
|
|
@@ -2450,10 +2470,35 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
2450
2470
|
// idle. Normal turns that DO reach 'busy' set generatingStartedAt and are
|
|
2451
2471
|
// excluded; a queued-pending first turn that only runs after grace falls
|
|
2452
2472
|
// outside the window and completes normally via idle→busy→idle.
|
|
2473
|
+
//
|
|
2474
|
+
// R4d (the live rc.405 Probe2 miss): R4c anchored the window on the collapse
|
|
2475
|
+
// moment but still measured its END against `now` (the poll/completion time). The
|
|
2476
|
+
// helper only fires once the turn has FINISHED (!hasAdapterPendingResponse()), so
|
|
2477
|
+
// the first eligible poll happens at completion. When the first turn is dispatched
|
|
2478
|
+
// a few seconds after the collapse AND runs for a non-trivial duration, that
|
|
2479
|
+
// completion lands PAST the 12s now-anchored window even though the turn was a
|
|
2480
|
+
// genuine startup-grace first turn (live: collapse→dispatch +5.2s, turn ~11s →
|
|
2481
|
+
// completion at collapse+16.2s > 12s). Anchor the window on when the first turn
|
|
2482
|
+
// STARTED (engine.currentTurnStartedAt, set by onTurnStarted) instead: a turn that
|
|
2483
|
+
// STARTED within the collapse window is a startup-grace first turn no matter how
|
|
2484
|
+
// long it then ran. The now-anchored check is retained as a union so a fast turn
|
|
2485
|
+
// (dispatched+completed quickly within 12s of collapse) keeps firing too; both
|
|
2486
|
+
// close for a much-later turn, preserving the "don't mislabel a late fast turn"
|
|
2487
|
+
// honesty the window exists for.
|
|
2488
|
+
const firstTurnStartedAt = typeof (this.adapter as any)?.currentTurnStartedAt === 'number'
|
|
2489
|
+
? (this.adapter as any).currentTurnStartedAt as number
|
|
2490
|
+
: 0;
|
|
2491
|
+
const collapsedAt = this.startupGraceCollapseAt;
|
|
2492
|
+
const turnStartedWithinCollapseWindow = collapsedAt !== null
|
|
2493
|
+
&& firstTurnStartedAt > 0
|
|
2494
|
+
&& firstTurnStartedAt >= collapsedAt
|
|
2495
|
+
&& (firstTurnStartedAt - collapsedAt) < STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS;
|
|
2496
|
+
const nowWithinCollapseWindow = collapsedAt !== null
|
|
2497
|
+
&& (now - collapsedAt) < STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS;
|
|
2453
2498
|
if (
|
|
2454
2499
|
newStatus === 'idle'
|
|
2455
2500
|
&& previousStatus === 'idle'
|
|
2456
|
-
&& (
|
|
2501
|
+
&& (turnStartedWithinCollapseWindow || nowWithinCollapseWindow)
|
|
2457
2502
|
) {
|
|
2458
2503
|
this.maybeSynthesizeStartupGraceCollapse(chatTitle, now, 'startup_grace_idle_turn_collapse');
|
|
2459
2504
|
}
|