@adhdev/daemon-core 0.9.82-rc.260 → 0.9.82-rc.262
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/commands/router.d.ts +116 -0
- package/dist/index.js +585 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +585 -10
- package/dist/index.mjs.map +1 -1
- package/dist/providers/spec/cli-adapter.d.ts +38 -0
- package/dist/providers/spec/fsm-driver.d.ts +41 -0
- package/package.json +1 -1
- package/src/commands/router.ts +756 -7
- package/src/providers/spec/adapter.ts +10 -3
- package/src/providers/spec/cli-adapter.ts +80 -0
- package/src/providers/spec/fsm-driver.ts +60 -3
|
@@ -26,6 +26,23 @@ export declare class SpecCliAdapter implements CliAdapter {
|
|
|
26
26
|
private activeInteractivePrompt;
|
|
27
27
|
private interactivePromptTransport;
|
|
28
28
|
private claudeTuiPromptCaptureInFlight;
|
|
29
|
+
/**
|
|
30
|
+
* Wall clock of the first frame on which a held interactive prompt was
|
|
31
|
+
* observed to have left the screen. Mirrors the approval FSM's
|
|
32
|
+
* `modalLostAt` hysteresis (see cli-state-engine.ts): claude-cli's TUI
|
|
33
|
+
* repaints the choice picker as several PTY chunks, so a single frame
|
|
34
|
+
* with no "Enter to select" footer is not proof the prompt is gone — it
|
|
35
|
+
* may just be mid-repaint. We only clear the held prompt once it has
|
|
36
|
+
* been absent across a short grace window. Reset to null the moment the
|
|
37
|
+
* prompt footer reappears.
|
|
38
|
+
*
|
|
39
|
+
* Without this, a choice prompt resolved *directly in the terminal* (the
|
|
40
|
+
* user picked an option without going through ADHDev's
|
|
41
|
+
* setInteractivePromptResponse) was never cleared from
|
|
42
|
+
* `activeInteractivePrompt`, so getStatus() re-emitted the same prompt
|
|
43
|
+
* forever — the choice-resolve-stuck bug.
|
|
44
|
+
*/
|
|
45
|
+
private interactivePromptLostAt;
|
|
29
46
|
private jsonLineTail;
|
|
30
47
|
private exited;
|
|
31
48
|
private spawned;
|
|
@@ -93,6 +110,27 @@ export declare class SpecCliAdapter implements CliAdapter {
|
|
|
93
110
|
private readCurrentScreenSections;
|
|
94
111
|
private extractProviderSessionIdFromScreen;
|
|
95
112
|
private readClaudeScreenAssistantMessages;
|
|
113
|
+
/**
|
|
114
|
+
* Grace window a held interactive prompt must be absent from the screen
|
|
115
|
+
* before we treat it as resolved-in-terminal and clear it. claude-cli
|
|
116
|
+
* repaints the picker across multiple PTY chunks, so a single
|
|
117
|
+
* footer-less frame is not proof the prompt is gone. Sized in the same
|
|
118
|
+
* spirit as the approval FSM's `approvalCooldown` modal-lost hysteresis.
|
|
119
|
+
*/
|
|
120
|
+
private static readonly INTERACTIVE_PROMPT_LOST_GRACE_MS;
|
|
121
|
+
/**
|
|
122
|
+
* Clear a held interactive prompt once the user has resolved it directly
|
|
123
|
+
* in the terminal (the choice picker leaves the screen without going
|
|
124
|
+
* through setInteractivePromptResponse). The approval path already does
|
|
125
|
+
* this via the FSM's modal-lost hysteresis; the interactive-prompt path
|
|
126
|
+
* had no equivalent, so a terminal-side answer left activeInteractivePrompt
|
|
127
|
+
* set and getStatus() re-emitted the same choice modal forever.
|
|
128
|
+
*
|
|
129
|
+
* Detection mirrors capture: the claude TUI picker is on-screen exactly
|
|
130
|
+
* while its "Enter to select" footer is rendered. When the footer is gone
|
|
131
|
+
* for INTERACTIVE_PROMPT_LOST_GRACE_MS the prompt is genuinely resolved.
|
|
132
|
+
*/
|
|
133
|
+
private maybeClearResolvedClaudeTuiPrompt;
|
|
96
134
|
private maybeCaptureClaudeTuiPrompt;
|
|
97
135
|
private readClaudeTuiHeaders;
|
|
98
136
|
private captureClaudeTuiPrompt;
|
|
@@ -82,6 +82,36 @@ export interface DriverHistoryEntry {
|
|
|
82
82
|
busyHoldMs?: number;
|
|
83
83
|
via?: string;
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* A frozen snapshot of the FULL FSM evaluation captured at the instant a
|
|
87
|
+
* transition fired — the rich `transitions[]` table (per-transition eligible /
|
|
88
|
+
* hold countdown / per-condition CondResult + remainingMs) that `getFsmDebug()`
|
|
89
|
+
* otherwise only computes live for the current instant. Kept in a separate ring
|
|
90
|
+
* buffer from `stateHistory` (which stays intentionally lightweight) so the
|
|
91
|
+
* "why did this rule fire just before the transition" question is answerable
|
|
92
|
+
* after the fact. before-only: this is the evaluation that PRODUCED the
|
|
93
|
+
* transition, not the post-transition state.
|
|
94
|
+
*/
|
|
95
|
+
export interface FsmSnapshotEntry {
|
|
96
|
+
/** State we transitioned out of. */
|
|
97
|
+
stateFrom: string;
|
|
98
|
+
/** State we transitioned into (the fired transition's destination). */
|
|
99
|
+
stateTo: string;
|
|
100
|
+
/** Wall-clock time the transition committed (ms). */
|
|
101
|
+
at: number;
|
|
102
|
+
/** The fired transition's destination state id (== stateTo; kept explicit
|
|
103
|
+
* to mirror the rule that fired). */
|
|
104
|
+
firedTo: string;
|
|
105
|
+
/** Human label of the fired transition (e.g. "approval→busy"). */
|
|
106
|
+
firedLabel: string;
|
|
107
|
+
/** Why-it-fired summary, same shape produced for stateHistory.matchedRules. */
|
|
108
|
+
reason: string[];
|
|
109
|
+
/** Every outgoing transition from `stateFrom` as evaluated at `at`, each
|
|
110
|
+
* with its eligible / hold / per-condition CondResult + remainingMs. This
|
|
111
|
+
* is the full pre-transition evaluation table — the whole point of the
|
|
112
|
+
* snapshot. */
|
|
113
|
+
transitions: TransitionEval[];
|
|
114
|
+
}
|
|
85
115
|
export interface ISpecDriver {
|
|
86
116
|
subscribe(listener: (ev: DashboardEvent) => void): () => void;
|
|
87
117
|
start(): void;
|
|
@@ -108,6 +138,7 @@ export interface ISpecDriver {
|
|
|
108
138
|
forceAfterMs: number;
|
|
109
139
|
} | null;
|
|
110
140
|
getFsmDebug?(): unknown;
|
|
141
|
+
getFsmSnapshotHistory?(): ReadonlyArray<FsmSnapshotEntry>;
|
|
111
142
|
}
|
|
112
143
|
export interface SpecDriverOpts {
|
|
113
144
|
specPath: string;
|
|
@@ -148,6 +179,10 @@ export declare class FsmDriver implements ISpecDriver {
|
|
|
148
179
|
private specWatcher;
|
|
149
180
|
/** Last full FSM evaluation, kept for the debugger. */
|
|
150
181
|
private lastFsmEval;
|
|
182
|
+
/** Ring buffer (max 20) of the full FSM evaluation captured at each
|
|
183
|
+
* transition — the rich pre-transition table that lastFsmEval only keeps
|
|
184
|
+
* for the single most recent evaluation. Separate from stateHistory. */
|
|
185
|
+
private fsmSnapshotHistory;
|
|
151
186
|
constructor(opts: SpecDriverOpts);
|
|
152
187
|
subscribe(listener: (ev: DashboardEvent) => void): () => void;
|
|
153
188
|
start(): void;
|
|
@@ -172,6 +207,7 @@ export declare class FsmDriver implements ISpecDriver {
|
|
|
172
207
|
transitions: TransitionEval[];
|
|
173
208
|
};
|
|
174
209
|
getStateHistory(): ReadonlyArray<HistoryEntry>;
|
|
210
|
+
getFsmSnapshotHistory(): ReadonlyArray<FsmSnapshotEntry>;
|
|
175
211
|
getSections(): Array<{
|
|
176
212
|
id: string;
|
|
177
213
|
text: string;
|
|
@@ -194,6 +230,11 @@ export declare class FsmDriver implements ISpecDriver {
|
|
|
194
230
|
private evalFsmNow;
|
|
195
231
|
private reevaluate;
|
|
196
232
|
private commitTransition;
|
|
233
|
+
/** Snapshot the full FSM evaluation that produced a transition into the
|
|
234
|
+
* separate fsmSnapshotHistory ring buffer (max 20). The transitions[]
|
|
235
|
+
* table is captured by reference — it is freshly built per evaluation in
|
|
236
|
+
* evaluateFsm and never mutated after, so no clone is needed. */
|
|
237
|
+
private pushFsmSnapshot;
|
|
197
238
|
/** Re-derive the visible modal + controls for the current state and emit a
|
|
198
239
|
* state_changed if anything differs from the last emit. */
|
|
199
240
|
private emitStateChanged;
|
package/package.json
CHANGED