@adhdev/daemon-core 0.9.82-rc.260 → 0.9.82-rc.261
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 +104 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +104 -5
- 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/providers/spec/adapter.ts +10 -3
- package/src/providers/spec/cli-adapter.ts +80 -0
- package/src/providers/spec/fsm-driver.ts +60 -3
package/dist/index.js
CHANGED
|
@@ -28744,8 +28744,12 @@ var TerminalAdapter = class {
|
|
|
28744
28744
|
this.rows = opts.rows ?? import_session_host_core6.DEFAULT_SESSION_HOST_ROWS;
|
|
28745
28745
|
this.screenDebounceMs = opts.screenChangeDebounceMs ?? 80;
|
|
28746
28746
|
this.tickIntervalMs = opts.tickIntervalMs ?? 0;
|
|
28747
|
-
|
|
28748
|
-
|
|
28747
|
+
if (opts.transportFactory) {
|
|
28748
|
+
this.factory = opts.transportFactory;
|
|
28749
|
+
} else {
|
|
28750
|
+
const { NodePtyTransportFactory: NodePtyTransportFactory2 } = (init_pty_transport(), __toCommonJS(pty_transport_exports));
|
|
28751
|
+
this.factory = new NodePtyTransportFactory2();
|
|
28752
|
+
}
|
|
28749
28753
|
this.screen = new TerminalScreen(this.rows, this.cols);
|
|
28750
28754
|
}
|
|
28751
28755
|
rows;
|
|
@@ -28902,6 +28906,10 @@ var FsmDriver = class {
|
|
|
28902
28906
|
specWatcher = null;
|
|
28903
28907
|
/** Last full FSM evaluation, kept for the debugger. */
|
|
28904
28908
|
lastFsmEval = null;
|
|
28909
|
+
/** Ring buffer (max 20) of the full FSM evaluation captured at each
|
|
28910
|
+
* transition — the rich pre-transition table that lastFsmEval only keeps
|
|
28911
|
+
* for the single most recent evaluation. Separate from stateHistory. */
|
|
28912
|
+
fsmSnapshotHistory = [];
|
|
28905
28913
|
subscribe(listener) {
|
|
28906
28914
|
this.listeners.add(listener);
|
|
28907
28915
|
return () => {
|
|
@@ -28992,6 +29000,9 @@ var FsmDriver = class {
|
|
|
28992
29000
|
getStateHistory() {
|
|
28993
29001
|
return this.stateHistory;
|
|
28994
29002
|
}
|
|
29003
|
+
getFsmSnapshotHistory() {
|
|
29004
|
+
return this.fsmSnapshotHistory;
|
|
29005
|
+
}
|
|
28995
29006
|
getSections() {
|
|
28996
29007
|
try {
|
|
28997
29008
|
const screen = this.adapter.snapshot();
|
|
@@ -29087,7 +29098,7 @@ var FsmDriver = class {
|
|
|
29087
29098
|
this.lastFsmEval = ev;
|
|
29088
29099
|
this.prevScreenLines = currentLines;
|
|
29089
29100
|
if (ev.fired) {
|
|
29090
|
-
this.commitTransition(ev.fired, now);
|
|
29101
|
+
this.commitTransition(ev.fired, now, ev);
|
|
29091
29102
|
this.emitStateChanged(forceEmit);
|
|
29092
29103
|
this.scheduleWakeForState();
|
|
29093
29104
|
return;
|
|
@@ -29096,8 +29107,9 @@ var FsmDriver = class {
|
|
|
29096
29107
|
this.scheduleWakeForState();
|
|
29097
29108
|
this.maybeMarkReady();
|
|
29098
29109
|
}
|
|
29099
|
-
commitTransition(fired, now) {
|
|
29110
|
+
commitTransition(fired, now, ev) {
|
|
29100
29111
|
const from = this.currentStateId;
|
|
29112
|
+
this.pushFsmSnapshot(from, fired, now, ev);
|
|
29101
29113
|
this.currentStateId = fired.to;
|
|
29102
29114
|
this.stateEnteredAt = now;
|
|
29103
29115
|
this.regionLastChangedAt.clear();
|
|
@@ -29108,6 +29120,22 @@ var FsmDriver = class {
|
|
|
29108
29120
|
});
|
|
29109
29121
|
LOG.info("FsmDriver", `[${this.specTag()}] ${from} \u2192 ${fired.to} (${fired.label})`);
|
|
29110
29122
|
}
|
|
29123
|
+
/** Snapshot the full FSM evaluation that produced a transition into the
|
|
29124
|
+
* separate fsmSnapshotHistory ring buffer (max 20). The transitions[]
|
|
29125
|
+
* table is captured by reference — it is freshly built per evaluation in
|
|
29126
|
+
* evaluateFsm and never mutated after, so no clone is needed. */
|
|
29127
|
+
pushFsmSnapshot(from, fired, now, ev) {
|
|
29128
|
+
this.fsmSnapshotHistory.push({
|
|
29129
|
+
stateFrom: from,
|
|
29130
|
+
stateTo: fired.to,
|
|
29131
|
+
at: now,
|
|
29132
|
+
firedTo: fired.to,
|
|
29133
|
+
firedLabel: fired.label,
|
|
29134
|
+
reason: summarizeTransition(fired),
|
|
29135
|
+
transitions: ev.transitions
|
|
29136
|
+
});
|
|
29137
|
+
if (this.fsmSnapshotHistory.length > 20) this.fsmSnapshotHistory.shift();
|
|
29138
|
+
}
|
|
29111
29139
|
/** Re-derive the visible modal + controls for the current state and emit a
|
|
29112
29140
|
* state_changed if anything differs from the last emit. */
|
|
29113
29141
|
emitStateChanged(forceEmit) {
|
|
@@ -30140,7 +30168,7 @@ init_logger();
|
|
|
30140
30168
|
function stripAnsi3(text) {
|
|
30141
30169
|
return String(text || "").replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
30142
30170
|
}
|
|
30143
|
-
var SpecCliAdapter = class {
|
|
30171
|
+
var SpecCliAdapter = class _SpecCliAdapter {
|
|
30144
30172
|
cliType;
|
|
30145
30173
|
cliName;
|
|
30146
30174
|
workingDir;
|
|
@@ -30165,6 +30193,23 @@ var SpecCliAdapter = class {
|
|
|
30165
30193
|
activeInteractivePrompt = null;
|
|
30166
30194
|
interactivePromptTransport = null;
|
|
30167
30195
|
claudeTuiPromptCaptureInFlight = false;
|
|
30196
|
+
/**
|
|
30197
|
+
* Wall clock of the first frame on which a held interactive prompt was
|
|
30198
|
+
* observed to have left the screen. Mirrors the approval FSM's
|
|
30199
|
+
* `modalLostAt` hysteresis (see cli-state-engine.ts): claude-cli's TUI
|
|
30200
|
+
* repaints the choice picker as several PTY chunks, so a single frame
|
|
30201
|
+
* with no "Enter to select" footer is not proof the prompt is gone — it
|
|
30202
|
+
* may just be mid-repaint. We only clear the held prompt once it has
|
|
30203
|
+
* been absent across a short grace window. Reset to null the moment the
|
|
30204
|
+
* prompt footer reappears.
|
|
30205
|
+
*
|
|
30206
|
+
* Without this, a choice prompt resolved *directly in the terminal* (the
|
|
30207
|
+
* user picked an option without going through ADHDev's
|
|
30208
|
+
* setInteractivePromptResponse) was never cleared from
|
|
30209
|
+
* `activeInteractivePrompt`, so getStatus() re-emitted the same prompt
|
|
30210
|
+
* forever — the choice-resolve-stuck bug.
|
|
30211
|
+
*/
|
|
30212
|
+
interactivePromptLostAt = null;
|
|
30168
30213
|
jsonLineTail = "";
|
|
30169
30214
|
exited = false;
|
|
30170
30215
|
spawned = false;
|
|
@@ -30425,6 +30470,11 @@ var SpecCliAdapter = class {
|
|
|
30425
30470
|
// transition from the current state with its per-condition match
|
|
30426
30471
|
// result + countdown — the canonical "why isn't it moving" answer.
|
|
30427
30472
|
fsm: this.driver.getFsmDebug?.() ?? null,
|
|
30473
|
+
// v4 FSM transition snapshot history (null for v3 specs). The full
|
|
30474
|
+
// pre-transition evaluation table captured at each transition —
|
|
30475
|
+
// answers "why did this rule fire" after the fact, unlike the live
|
|
30476
|
+
// `fsm` field which only reflects the current instant.
|
|
30477
|
+
fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
|
|
30428
30478
|
// Extended fields
|
|
30429
30479
|
name: this.cliName,
|
|
30430
30480
|
status: this.getStatus().status,
|
|
@@ -30462,11 +30512,13 @@ var SpecCliAdapter = class {
|
|
|
30462
30512
|
if (ev.state.title) {
|
|
30463
30513
|
LOG.debug("SpecAdapter", `[${this.cliType}] state.title=${JSON.stringify(ev.state.title)}`);
|
|
30464
30514
|
}
|
|
30515
|
+
this.maybeClearResolvedClaudeTuiPrompt();
|
|
30465
30516
|
this.maybeCaptureClaudeTuiPrompt();
|
|
30466
30517
|
this.statusCallback?.();
|
|
30467
30518
|
return;
|
|
30468
30519
|
case "pty_data":
|
|
30469
30520
|
this.detectInteractivePromptFromPtyChunk(ev.chunk);
|
|
30521
|
+
this.maybeClearResolvedClaudeTuiPrompt();
|
|
30470
30522
|
this.maybeCaptureClaudeTuiPrompt();
|
|
30471
30523
|
try {
|
|
30472
30524
|
this.ptyDataCallback?.(ev.chunk);
|
|
@@ -30499,6 +30551,7 @@ var SpecCliAdapter = class {
|
|
|
30499
30551
|
if (!prompt) continue;
|
|
30500
30552
|
this.activeInteractivePrompt = prompt;
|
|
30501
30553
|
this.interactivePromptTransport = "stream-json";
|
|
30554
|
+
this.interactivePromptLostAt = null;
|
|
30502
30555
|
this.statusCallback?.();
|
|
30503
30556
|
} catch {
|
|
30504
30557
|
}
|
|
@@ -30552,6 +30605,47 @@ var SpecCliAdapter = class {
|
|
|
30552
30605
|
}
|
|
30553
30606
|
return messages;
|
|
30554
30607
|
}
|
|
30608
|
+
/**
|
|
30609
|
+
* Grace window a held interactive prompt must be absent from the screen
|
|
30610
|
+
* before we treat it as resolved-in-terminal and clear it. claude-cli
|
|
30611
|
+
* repaints the picker across multiple PTY chunks, so a single
|
|
30612
|
+
* footer-less frame is not proof the prompt is gone. Sized in the same
|
|
30613
|
+
* spirit as the approval FSM's `approvalCooldown` modal-lost hysteresis.
|
|
30614
|
+
*/
|
|
30615
|
+
static INTERACTIVE_PROMPT_LOST_GRACE_MS = 1500;
|
|
30616
|
+
/**
|
|
30617
|
+
* Clear a held interactive prompt once the user has resolved it directly
|
|
30618
|
+
* in the terminal (the choice picker leaves the screen without going
|
|
30619
|
+
* through setInteractivePromptResponse). The approval path already does
|
|
30620
|
+
* this via the FSM's modal-lost hysteresis; the interactive-prompt path
|
|
30621
|
+
* had no equivalent, so a terminal-side answer left activeInteractivePrompt
|
|
30622
|
+
* set and getStatus() re-emitted the same choice modal forever.
|
|
30623
|
+
*
|
|
30624
|
+
* Detection mirrors capture: the claude TUI picker is on-screen exactly
|
|
30625
|
+
* while its "Enter to select" footer is rendered. When the footer is gone
|
|
30626
|
+
* for INTERACTIVE_PROMPT_LOST_GRACE_MS the prompt is genuinely resolved.
|
|
30627
|
+
*/
|
|
30628
|
+
maybeClearResolvedClaudeTuiPrompt() {
|
|
30629
|
+
if (this.cliType !== "claude-cli" || !this.activeInteractivePrompt) return;
|
|
30630
|
+
let screenText = "";
|
|
30631
|
+
try {
|
|
30632
|
+
screenText = this.driver.snapshot();
|
|
30633
|
+
} catch {
|
|
30634
|
+
return;
|
|
30635
|
+
}
|
|
30636
|
+
const stillOnScreen = screenText.includes("Enter to select");
|
|
30637
|
+
if (stillOnScreen) {
|
|
30638
|
+
this.interactivePromptLostAt = null;
|
|
30639
|
+
return;
|
|
30640
|
+
}
|
|
30641
|
+
const lostAt = this.interactivePromptLostAt ?? Date.now();
|
|
30642
|
+
if (this.interactivePromptLostAt === null) this.interactivePromptLostAt = lostAt;
|
|
30643
|
+
if (Date.now() - lostAt < _SpecCliAdapter.INTERACTIVE_PROMPT_LOST_GRACE_MS) return;
|
|
30644
|
+
this.activeInteractivePrompt = null;
|
|
30645
|
+
this.interactivePromptTransport = null;
|
|
30646
|
+
this.interactivePromptLostAt = null;
|
|
30647
|
+
this.statusCallback?.();
|
|
30648
|
+
}
|
|
30555
30649
|
maybeCaptureClaudeTuiPrompt() {
|
|
30556
30650
|
if (this.cliType !== "claude-cli" || this.activeInteractivePrompt || this.claudeTuiPromptCaptureInFlight) return;
|
|
30557
30651
|
const screenText = this.driver.snapshot();
|
|
@@ -30565,6 +30659,7 @@ var SpecCliAdapter = class {
|
|
|
30565
30659
|
if (!prompt) return;
|
|
30566
30660
|
this.activeInteractivePrompt = prompt;
|
|
30567
30661
|
this.interactivePromptTransport = "tui";
|
|
30662
|
+
this.interactivePromptLostAt = null;
|
|
30568
30663
|
this.statusCallback?.();
|
|
30569
30664
|
return;
|
|
30570
30665
|
}
|
|
@@ -30601,6 +30696,7 @@ var SpecCliAdapter = class {
|
|
|
30601
30696
|
if (!prompt) return;
|
|
30602
30697
|
this.activeInteractivePrompt = prompt;
|
|
30603
30698
|
this.interactivePromptTransport = "tui";
|
|
30699
|
+
this.interactivePromptLostAt = null;
|
|
30604
30700
|
this.statusCallback?.();
|
|
30605
30701
|
}
|
|
30606
30702
|
getDebugState() {
|
|
@@ -30655,6 +30751,9 @@ var SpecCliAdapter = class {
|
|
|
30655
30751
|
// and countdown. This is the canonical "why isn't it transitioning"
|
|
30656
30752
|
// answer — no screenshots needed.
|
|
30657
30753
|
fsm: this.driver.getFsmDebug?.() ?? null,
|
|
30754
|
+
// v4 FSM transition snapshot history — the captured pre-transition
|
|
30755
|
+
// evaluation table at each transition (null for v3 specs).
|
|
30756
|
+
fsmHistory: this.driver.getFsmSnapshotHistory?.() ?? null,
|
|
30658
30757
|
messages,
|
|
30659
30758
|
committedMessages: messages
|
|
30660
30759
|
};
|