@adhdev/daemon-core 0.9.82-rc.267 → 0.9.82-rc.269
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/terminal-backends/ghostty-vt-backend.d.ts +3 -0
- package/dist/cli-adapters/terminal-backends/types.d.ts +9 -0
- package/dist/cli-adapters/terminal-screen.d.ts +2 -0
- package/dist/index.js +234 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +234 -33
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +24 -0
- package/dist/providers/spec/adapter.d.ts +7 -0
- package/dist/providers/spec/fsm-driver.d.ts +4 -0
- package/package.json +2 -2
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +28 -10
- package/src/cli-adapters/terminal-backends/types.ts +9 -0
- package/src/cli-adapters/terminal-screen.ts +5 -0
- package/src/commands/chat-commands.ts +9 -2
- package/src/config/chat-history.ts +134 -0
- package/src/providers/cli-provider-instance.ts +95 -13
- package/src/providers/spec/adapter.ts +10 -0
- package/src/providers/spec/fsm-driver.ts +31 -2
- package/src/providers/types/interactive-prompt.ts +23 -2
|
@@ -7,7 +7,10 @@ export declare class GhosttyVtTerminalBackend implements TerminalViewportBackend
|
|
|
7
7
|
constructor(options: TerminalViewportBackendOptions);
|
|
8
8
|
resize(rows: number, cols: number): void;
|
|
9
9
|
write(data: string): void;
|
|
10
|
+
private formatLines;
|
|
11
|
+
private static trimBlankEnds;
|
|
10
12
|
getText(): string;
|
|
13
|
+
getTextWithScrollback(): string;
|
|
11
14
|
getCursorPosition(): {
|
|
12
15
|
col: number;
|
|
13
16
|
row: number;
|
|
@@ -9,6 +9,15 @@ export interface TerminalViewportBackend {
|
|
|
9
9
|
resize(rows: number, cols: number): void;
|
|
10
10
|
write(data: string): void;
|
|
11
11
|
getText(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Like getText() but includes scrollback history (the lines that have
|
|
14
|
+
* scrolled above the visible viewport). Used by content-pattern matching
|
|
15
|
+
* (e.g. approval/modal button extraction) that must stay correct when a
|
|
16
|
+
* tall prompt — a big diff or long explanation — pushes part of the
|
|
17
|
+
* prompt box above the viewport. NOT for cursor-relative / stable_ms
|
|
18
|
+
* conditions, whose row indices are viewport-relative.
|
|
19
|
+
*/
|
|
20
|
+
getTextWithScrollback(): string;
|
|
12
21
|
getCursorPosition(): {
|
|
13
22
|
col: number;
|
|
14
23
|
row: number;
|
|
@@ -15,6 +15,8 @@ export declare class TerminalScreen {
|
|
|
15
15
|
resize(rows: number, cols: number): void;
|
|
16
16
|
write(data: string): void;
|
|
17
17
|
getText(): string;
|
|
18
|
+
/** Full buffer including scrollback history (see backend doc). */
|
|
19
|
+
getTextWithScrollback(): string;
|
|
18
20
|
getCursorPosition(): {
|
|
19
21
|
col: number;
|
|
20
22
|
row: number;
|
package/dist/index.js
CHANGED
|
@@ -265,10 +265,10 @@ function readInjected(value) {
|
|
|
265
265
|
}
|
|
266
266
|
function getDaemonBuildInfo() {
|
|
267
267
|
if (cached) return cached;
|
|
268
|
-
const commit = readInjected(true ? "
|
|
269
|
-
const commitShort = readInjected(true ? "
|
|
270
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
271
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
268
|
+
const commit = readInjected(true ? "025d8bef81a66e7b44a5ba5d5a04e063db46db4e" : void 0) ?? "unknown";
|
|
269
|
+
const commitShort = readInjected(true ? "025d8bef" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
270
|
+
const version = readInjected(true ? "0.9.82-rc.269" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
271
|
+
const builtAt = readInjected(true ? "2026-06-14T22:11:38.134Z" : void 0);
|
|
272
272
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
273
273
|
return cached;
|
|
274
274
|
}
|
|
@@ -9503,7 +9503,7 @@ var init_ghostty_vt_backend = __esm({
|
|
|
9503
9503
|
"@adhdev/ghostty-vt-node"
|
|
9504
9504
|
];
|
|
9505
9505
|
cachedBindingError = null;
|
|
9506
|
-
GhosttyVtTerminalBackend = class {
|
|
9506
|
+
GhosttyVtTerminalBackend = class _GhosttyVtTerminalBackend {
|
|
9507
9507
|
kind = "ghostty-vt";
|
|
9508
9508
|
terminal;
|
|
9509
9509
|
rows;
|
|
@@ -9525,17 +9525,30 @@ var init_ghostty_vt_backend = __esm({
|
|
|
9525
9525
|
if (!data || this.disposed) return;
|
|
9526
9526
|
this.terminal.write(data);
|
|
9527
9527
|
}
|
|
9528
|
+
formatLines() {
|
|
9529
|
+
const raw = this.terminal.formatPlainText({ trim: false }) || "";
|
|
9530
|
+
if (!raw) return [];
|
|
9531
|
+
return raw.split("\n").map((row) => row.replace(/\s+$/, ""));
|
|
9532
|
+
}
|
|
9533
|
+
static trimBlankEnds(lines) {
|
|
9534
|
+
let first = 0;
|
|
9535
|
+
let last = lines.length;
|
|
9536
|
+
while (first < last && !lines[first]) first += 1;
|
|
9537
|
+
while (last > first && !lines[last - 1]) last -= 1;
|
|
9538
|
+
return lines.slice(first, last).join("\n");
|
|
9539
|
+
}
|
|
9528
9540
|
getText() {
|
|
9529
9541
|
if (this.disposed) return "";
|
|
9530
|
-
const
|
|
9531
|
-
if (
|
|
9532
|
-
const lines = raw.split("\n").map((row) => row.replace(/\s+$/, ""));
|
|
9542
|
+
const lines = this.formatLines();
|
|
9543
|
+
if (lines.length === 0) return "";
|
|
9533
9544
|
const viewport = lines.length > this.rows ? lines.slice(-this.rows) : lines;
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9545
|
+
return _GhosttyVtTerminalBackend.trimBlankEnds(viewport);
|
|
9546
|
+
}
|
|
9547
|
+
getTextWithScrollback() {
|
|
9548
|
+
if (this.disposed) return "";
|
|
9549
|
+
const lines = this.formatLines();
|
|
9550
|
+
if (lines.length === 0) return "";
|
|
9551
|
+
return _GhosttyVtTerminalBackend.trimBlankEnds(lines);
|
|
9539
9552
|
}
|
|
9540
9553
|
getCursorPosition() {
|
|
9541
9554
|
if (this.disposed) return { col: 0, row: 0 };
|
|
@@ -9588,6 +9601,10 @@ var init_terminal_screen = __esm({
|
|
|
9588
9601
|
getText() {
|
|
9589
9602
|
return this.terminal.getText();
|
|
9590
9603
|
}
|
|
9604
|
+
/** Full buffer including scrollback history (see backend doc). */
|
|
9605
|
+
getTextWithScrollback() {
|
|
9606
|
+
return this.terminal.getTextWithScrollback();
|
|
9607
|
+
}
|
|
9591
9608
|
getCursorPosition() {
|
|
9592
9609
|
return this.terminal.getCursorPosition();
|
|
9593
9610
|
}
|
|
@@ -15056,11 +15073,22 @@ function buildClaudeInteractiveTuiAnswerSteps(prompt, response) {
|
|
|
15056
15073
|
if (response.promptId !== prompt.promptId) throw new Error("Interactive prompt response does not match active prompt");
|
|
15057
15074
|
const steps = [];
|
|
15058
15075
|
for (const question of prompt.questions) {
|
|
15059
|
-
if (question.multiSelect) throw new Error("Claude TUI multi-select prompts are not supported yet");
|
|
15060
15076
|
const answer = response.answers[question.questionId];
|
|
15061
15077
|
if (!answer) throw new Error(`Missing answer for ${question.questionId}`);
|
|
15062
15078
|
const freeformText = answer.freeformText?.trim() ?? "";
|
|
15063
|
-
if (
|
|
15079
|
+
if (question.multiSelect) {
|
|
15080
|
+
const labels = answer.selectedLabels;
|
|
15081
|
+
if (labels.length === 0) {
|
|
15082
|
+
throw new Error(`Expected at least one selected label for ${question.questionId}`);
|
|
15083
|
+
}
|
|
15084
|
+
for (const label of labels) {
|
|
15085
|
+
const selectedIndex = question.options.findIndex((option) => option.label === label);
|
|
15086
|
+
if (selectedIndex < 0) throw new Error(`Unknown option for ${question.questionId}: ${label}`);
|
|
15087
|
+
steps.push(String(selectedIndex + 1));
|
|
15088
|
+
steps.push(" ");
|
|
15089
|
+
}
|
|
15090
|
+
steps.push("\r");
|
|
15091
|
+
} else if (freeformText) {
|
|
15064
15092
|
const typeOptionIndex = question.options.findIndex((o) => /^Type something\.?$/i.test(o.label));
|
|
15065
15093
|
const optionNumber = typeOptionIndex >= 0 ? typeOptionIndex + 1 : question.options.length;
|
|
15066
15094
|
steps.push(String(optionNumber));
|
|
@@ -20237,6 +20265,24 @@ var savedHistorySessionCache = /* @__PURE__ */ new Map();
|
|
|
20237
20265
|
var savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
|
|
20238
20266
|
var savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
|
|
20239
20267
|
var savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
|
|
20268
|
+
var BOUNDED_TAIL_CACHE_MAX_ENTRIES = 64;
|
|
20269
|
+
var boundedTailReadCache = /* @__PURE__ */ new Map();
|
|
20270
|
+
function readBoundedTailCache(key, signature) {
|
|
20271
|
+
const cached2 = boundedTailReadCache.get(key);
|
|
20272
|
+
if (!cached2 || cached2.signature !== signature) return null;
|
|
20273
|
+
boundedTailReadCache.delete(key);
|
|
20274
|
+
boundedTailReadCache.set(key, cached2);
|
|
20275
|
+
return cached2.result;
|
|
20276
|
+
}
|
|
20277
|
+
function writeBoundedTailCache(key, signature, result) {
|
|
20278
|
+
boundedTailReadCache.delete(key);
|
|
20279
|
+
boundedTailReadCache.set(key, { signature, result });
|
|
20280
|
+
while (boundedTailReadCache.size > BOUNDED_TAIL_CACHE_MAX_ENTRIES) {
|
|
20281
|
+
const oldest = boundedTailReadCache.keys().next().value;
|
|
20282
|
+
if (oldest === void 0) break;
|
|
20283
|
+
boundedTailReadCache.delete(oldest);
|
|
20284
|
+
}
|
|
20285
|
+
}
|
|
20240
20286
|
function normalizeHistoryComparable(text) {
|
|
20241
20287
|
return String(text || "").replace(/\s+/g, " ").trim();
|
|
20242
20288
|
}
|
|
@@ -21121,12 +21167,73 @@ function pageHistoryRecords(agentType, records, offset = 0, limit = 30, excludeR
|
|
|
21121
21167
|
const sliced = collapsed.slice(startInclusive, endExclusive);
|
|
21122
21168
|
return { messages: sliced, hasMore: startInclusive > 0 };
|
|
21123
21169
|
}
|
|
21170
|
+
var BOUNDED_TAIL_MAX_LIMIT = 5e3;
|
|
21171
|
+
var BOUNDED_TAIL_SLACK = 50;
|
|
21172
|
+
function isBoundedTailRequest(limit, offset, excludeRecentCount) {
|
|
21173
|
+
const numericLimit = Number(limit);
|
|
21174
|
+
if (!Number.isFinite(numericLimit) || numericLimit <= 0) return false;
|
|
21175
|
+
if (numericLimit > BOUNDED_TAIL_MAX_LIMIT) return false;
|
|
21176
|
+
const numericOffset = Number(offset);
|
|
21177
|
+
const numericExclude = Number(excludeRecentCount);
|
|
21178
|
+
if (!Number.isFinite(numericOffset) || !Number.isFinite(numericExclude)) return false;
|
|
21179
|
+
return true;
|
|
21180
|
+
}
|
|
21181
|
+
function readBoundedTailRecords(agentType, dir, files, needed) {
|
|
21182
|
+
const collected = [];
|
|
21183
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21184
|
+
let readAllFiles = true;
|
|
21185
|
+
for (let f = 0; f < files.length; f++) {
|
|
21186
|
+
const filePath = path12.join(dir, files[f]);
|
|
21187
|
+
let content;
|
|
21188
|
+
try {
|
|
21189
|
+
content = fs5.readFileSync(filePath, "utf-8");
|
|
21190
|
+
} catch {
|
|
21191
|
+
continue;
|
|
21192
|
+
}
|
|
21193
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
21194
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
21195
|
+
try {
|
|
21196
|
+
const parsed = JSON.parse(lines[i]);
|
|
21197
|
+
const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
|
|
21198
|
+
if (!sanitizedMessage) continue;
|
|
21199
|
+
const hash = buildHistoryMessageHash(agentType, sanitizedMessage);
|
|
21200
|
+
if (seen.has(hash)) continue;
|
|
21201
|
+
seen.add(hash);
|
|
21202
|
+
collected.push(sanitizedMessage);
|
|
21203
|
+
} catch {
|
|
21204
|
+
}
|
|
21205
|
+
}
|
|
21206
|
+
if (collected.length >= needed && f < files.length - 1) {
|
|
21207
|
+
readAllFiles = false;
|
|
21208
|
+
break;
|
|
21209
|
+
}
|
|
21210
|
+
}
|
|
21211
|
+
collected.reverse();
|
|
21212
|
+
return { records: collected, readAllFiles };
|
|
21213
|
+
}
|
|
21124
21214
|
function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, excludeRecentCount = 0, historyBehavior) {
|
|
21125
21215
|
try {
|
|
21126
21216
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
21127
21217
|
const dir = path12.join(HISTORY_DIR, sanitized);
|
|
21128
21218
|
if (!fs5.existsSync(dir)) return { messages: [], hasMore: false };
|
|
21129
21219
|
const files = listHistoryFiles(dir, historySessionId);
|
|
21220
|
+
const bounded = isBoundedTailRequest(limit, offset, excludeRecentCount);
|
|
21221
|
+
if (bounded) {
|
|
21222
|
+
const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
|
|
21223
|
+
const cacheKey = `${sanitized}\0${historySessionId || ""}\0${offset}\0${limit}\0${excludeRecentCount}\0${historyBehavior?.collapseConsecutiveAssistantTurns ? "1" : "0"}`;
|
|
21224
|
+
const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
|
|
21225
|
+
const cached2 = readBoundedTailCache(cacheKey, signature);
|
|
21226
|
+
if (cached2) return cached2;
|
|
21227
|
+
const numericLimit = Math.max(1, Number(limit));
|
|
21228
|
+
const numericOffset = Math.max(0, Number(offset));
|
|
21229
|
+
const numericExclude = Math.max(0, Number(excludeRecentCount));
|
|
21230
|
+
const needed = numericLimit + numericOffset + numericExclude + Math.max(BOUNDED_TAIL_SLACK, numericLimit);
|
|
21231
|
+
const { records, readAllFiles } = readBoundedTailRecords(agentType, dir, files, needed);
|
|
21232
|
+
const result = pageHistoryRecords(agentType, records, offset, limit, excludeRecentCount, historyBehavior);
|
|
21233
|
+
const boundedResult = readAllFiles ? result : { messages: result.messages, hasMore: true };
|
|
21234
|
+
writeBoundedTailCache(cacheKey, signature, boundedResult);
|
|
21235
|
+
return boundedResult;
|
|
21236
|
+
}
|
|
21130
21237
|
const allMessages = [];
|
|
21131
21238
|
const seen = /* @__PURE__ */ new Set();
|
|
21132
21239
|
for (const file of files) {
|
|
@@ -24336,6 +24443,7 @@ var CHAT_SOURCE_REGISTRY = new ChatSourceRegistry();
|
|
|
24336
24443
|
// src/commands/chat-commands.ts
|
|
24337
24444
|
var RECENT_SEND_WINDOW_MS = 1200;
|
|
24338
24445
|
var READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25e3;
|
|
24446
|
+
var HOT_TAIL_MIN_LIMIT = 60;
|
|
24339
24447
|
var HERMES_CLI_STARTING_SEND_SETTLE_MS = 2e3;
|
|
24340
24448
|
var CLI_NATIVE_TRANSCRIPT_PROVIDERS = /* @__PURE__ */ new Set(["codex-cli", "claude-cli", "hermes-cli", "antigravity-cli"]);
|
|
24341
24449
|
var warnedLegacyNativeAllowlistHits = /* @__PURE__ */ new Set();
|
|
@@ -24878,7 +24986,7 @@ function readExactRuntimeMirrorMessages(args) {
|
|
|
24878
24986
|
const history = readChatHistory(
|
|
24879
24987
|
args.providerType,
|
|
24880
24988
|
0,
|
|
24881
|
-
Math.max(args.tailLimit || 0,
|
|
24989
|
+
Math.max(args.tailLimit || 0, HOT_TAIL_MIN_LIMIT),
|
|
24882
24990
|
targetSessionId,
|
|
24883
24991
|
0,
|
|
24884
24992
|
args.historyBehavior
|
|
@@ -25771,7 +25879,7 @@ async function handleReadChat(h, args) {
|
|
|
25771
25879
|
const nativeHistoryLimit = Math.max(
|
|
25772
25880
|
normalizeReadChatTailLimit(args) || 0,
|
|
25773
25881
|
returnedMessages.length,
|
|
25774
|
-
|
|
25882
|
+
HOT_TAIL_MIN_LIMIT
|
|
25775
25883
|
);
|
|
25776
25884
|
const nativeHistorySessionId = supportsNative ? resolveCliNativeHistorySessionId(args, historySessionId, providerSessionId) : void 0;
|
|
25777
25885
|
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
@@ -29234,6 +29342,15 @@ var TerminalAdapter = class {
|
|
|
29234
29342
|
snapshot() {
|
|
29235
29343
|
return this.lastScreen || this.computeScreen();
|
|
29236
29344
|
}
|
|
29345
|
+
/**
|
|
29346
|
+
* Full-buffer snapshot including scrollback history. Unlike snapshot()
|
|
29347
|
+
* (visible viewport only), this survives a tall prompt whose top has
|
|
29348
|
+
* scrolled off-screen — used for content-pattern extraction (modal
|
|
29349
|
+
* buttons / approval anchors), NOT for cursor-relative conditions.
|
|
29350
|
+
*/
|
|
29351
|
+
snapshotWithScrollback() {
|
|
29352
|
+
return this.screen.getTextWithScrollback();
|
|
29353
|
+
}
|
|
29237
29354
|
getCursorPosition() {
|
|
29238
29355
|
const pos = this.screen.getCursorPosition();
|
|
29239
29356
|
return { row: pos.row, col: pos.col };
|
|
@@ -29408,6 +29525,18 @@ var FsmDriver = class {
|
|
|
29408
29525
|
getScreen() {
|
|
29409
29526
|
return this.adapter.snapshot();
|
|
29410
29527
|
}
|
|
29528
|
+
/** Scrollback-inclusive screen as line array — used only for modal/button
|
|
29529
|
+
* content extraction so a tall prompt's off-screen anchors stay matchable.
|
|
29530
|
+
* Falls back to the viewport snapshot if scrollback read is unavailable. */
|
|
29531
|
+
scrollbackLines() {
|
|
29532
|
+
let screen = "";
|
|
29533
|
+
try {
|
|
29534
|
+
screen = this.adapter.snapshotWithScrollback();
|
|
29535
|
+
} catch {
|
|
29536
|
+
}
|
|
29537
|
+
if (!screen) screen = this.adapter.snapshot();
|
|
29538
|
+
return screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
29539
|
+
}
|
|
29411
29540
|
getSpecPath() {
|
|
29412
29541
|
return this.opts.specPath;
|
|
29413
29542
|
}
|
|
@@ -29589,9 +29718,12 @@ var FsmDriver = class {
|
|
|
29589
29718
|
const screen = this.adapter.snapshot();
|
|
29590
29719
|
const lines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
29591
29720
|
const sections = resolveSections(this.spec.sections ?? {}, lines);
|
|
29592
|
-
const
|
|
29721
|
+
const modalLines = state.modal ? this.scrollbackLines() : lines;
|
|
29722
|
+
const modalSections = state.modal ? resolveSections(this.spec.sections ?? {}, modalLines) : sections;
|
|
29723
|
+
const modalFullScreen = modalLines.join("\n");
|
|
29724
|
+
const modal = this.deriveModal(state, modalSections, modalFullScreen);
|
|
29593
29725
|
const controls = this.deriveControls(state.id);
|
|
29594
|
-
const title = modal?.title ?? this.deriveTitle(state,
|
|
29726
|
+
const title = modal?.title ?? this.deriveTitle(state, modalSections, modalFullScreen);
|
|
29595
29727
|
const next = {
|
|
29596
29728
|
// status is derived from the FSM state itself (statusForState), NOT from
|
|
29597
29729
|
// whether a modal was parsed this frame. A modal state whose buttons briefly
|
|
@@ -31462,7 +31594,7 @@ async function waitForCliAdapterReady(adapter, options) {
|
|
|
31462
31594
|
}
|
|
31463
31595
|
throw new Error(`CLI runtime did not become ready within ${timeoutMs}ms`);
|
|
31464
31596
|
}
|
|
31465
|
-
var CliProviderInstance = class {
|
|
31597
|
+
var CliProviderInstance = class _CliProviderInstance {
|
|
31466
31598
|
constructor(provider, workingDir, cliArgs = [], instanceId, transportFactory, options) {
|
|
31467
31599
|
this.provider = provider;
|
|
31468
31600
|
this.workingDir = workingDir;
|
|
@@ -31482,6 +31614,18 @@ var CliProviderInstance = class {
|
|
|
31482
31614
|
}
|
|
31483
31615
|
type;
|
|
31484
31616
|
category = "cli";
|
|
31617
|
+
/**
|
|
31618
|
+
* Quiet period an approval modal's signature must be stable before
|
|
31619
|
+
* auto-approve sends the approve key. Guards against firing on a prompt
|
|
31620
|
+
* that is still streaming into the PTY (the "resolves too fast" symptom):
|
|
31621
|
+
* while the modal text/buttons are still changing, every frame yields a
|
|
31622
|
+
* new signature and the settle clock restarts. Once the prompt finishes
|
|
31623
|
+
* rendering the signature holds and the key is sent after this window.
|
|
31624
|
+
* Bounded + small so genuine approvals stay timely. The FSM is already
|
|
31625
|
+
* authoritative over the `waiting_approval` state; this only delays the
|
|
31626
|
+
* keystroke until the modal *content* has settled.
|
|
31627
|
+
*/
|
|
31628
|
+
static AUTO_APPROVE_SETTLE_MS = 600;
|
|
31485
31629
|
adapter;
|
|
31486
31630
|
context = null;
|
|
31487
31631
|
events = [];
|
|
@@ -31495,6 +31639,14 @@ var CliProviderInstance = class {
|
|
|
31495
31639
|
autoApproveBusy = false;
|
|
31496
31640
|
autoApproveBusyTimer = null;
|
|
31497
31641
|
lastAutoApprovalSignature = "";
|
|
31642
|
+
// Settle gate: the approval modal's signature + the wall-clock when this
|
|
31643
|
+
// exact signature was first observed. Auto-approve only fires once the
|
|
31644
|
+
// SAME signature has been stable for AUTO_APPROVE_SETTLE_MS, so a prompt
|
|
31645
|
+
// still streaming into the PTY (its buttons/message changing frame to
|
|
31646
|
+
// frame) keeps resetting the timer and is never approved half-rendered.
|
|
31647
|
+
pendingAutoApprovalSignature = "";
|
|
31648
|
+
pendingAutoApprovalSince = 0;
|
|
31649
|
+
autoApproveSettleTimer = null;
|
|
31498
31650
|
controlValues = {};
|
|
31499
31651
|
summaryMetadata = void 0;
|
|
31500
31652
|
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
@@ -31929,6 +32081,14 @@ var CliProviderInstance = class {
|
|
|
31929
32081
|
dispose() {
|
|
31930
32082
|
this.adapter.shutdown();
|
|
31931
32083
|
this.monitor.reset();
|
|
32084
|
+
if (this.autoApproveSettleTimer) {
|
|
32085
|
+
clearTimeout(this.autoApproveSettleTimer);
|
|
32086
|
+
this.autoApproveSettleTimer = null;
|
|
32087
|
+
}
|
|
32088
|
+
if (this.autoApproveBusyTimer) {
|
|
32089
|
+
clearTimeout(this.autoApproveBusyTimer);
|
|
32090
|
+
this.autoApproveBusyTimer = null;
|
|
32091
|
+
}
|
|
31932
32092
|
this.appliedEffectKeys.clear();
|
|
31933
32093
|
try {
|
|
31934
32094
|
this.cachedSqliteDb?.close();
|
|
@@ -32279,6 +32439,12 @@ var CliProviderInstance = class {
|
|
|
32279
32439
|
const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
|
|
32280
32440
|
if (!autoApproveActive) {
|
|
32281
32441
|
this.lastAutoApprovalSignature = "";
|
|
32442
|
+
this.pendingAutoApprovalSignature = "";
|
|
32443
|
+
this.pendingAutoApprovalSince = 0;
|
|
32444
|
+
if (this.autoApproveSettleTimer) {
|
|
32445
|
+
clearTimeout(this.autoApproveSettleTimer);
|
|
32446
|
+
this.autoApproveSettleTimer = null;
|
|
32447
|
+
}
|
|
32282
32448
|
return autoApproveActive;
|
|
32283
32449
|
}
|
|
32284
32450
|
const modal = adapterStatus.activeModal;
|
|
@@ -32297,22 +32463,57 @@ var CliProviderInstance = class {
|
|
|
32297
32463
|
buttons.join("|"),
|
|
32298
32464
|
buttonIndex
|
|
32299
32465
|
].join("::");
|
|
32300
|
-
if (
|
|
32301
|
-
|
|
32302
|
-
|
|
32303
|
-
|
|
32304
|
-
this.
|
|
32305
|
-
|
|
32306
|
-
|
|
32307
|
-
|
|
32308
|
-
|
|
32309
|
-
this.
|
|
32310
|
-
setTimeout(() => {
|
|
32311
|
-
this.
|
|
32312
|
-
|
|
32466
|
+
if (this.autoApproveBusy && signature === this.lastAutoApprovalSignature) {
|
|
32467
|
+
return autoApproveActive;
|
|
32468
|
+
}
|
|
32469
|
+
if (signature !== this.pendingAutoApprovalSignature) {
|
|
32470
|
+
this.pendingAutoApprovalSignature = signature;
|
|
32471
|
+
this.pendingAutoApprovalSince = now;
|
|
32472
|
+
}
|
|
32473
|
+
const settledForMs = now - this.pendingAutoApprovalSince;
|
|
32474
|
+
if (settledForMs < _CliProviderInstance.AUTO_APPROVE_SETTLE_MS) {
|
|
32475
|
+
if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
|
|
32476
|
+
this.autoApproveSettleTimer = setTimeout(() => {
|
|
32477
|
+
this.autoApproveSettleTimer = null;
|
|
32478
|
+
this.recheckAutoApproveSettled();
|
|
32479
|
+
}, _CliProviderInstance.AUTO_APPROVE_SETTLE_MS - settledForMs + 20);
|
|
32480
|
+
return autoApproveActive;
|
|
32481
|
+
}
|
|
32482
|
+
if (this.autoApproveSettleTimer) {
|
|
32483
|
+
clearTimeout(this.autoApproveSettleTimer);
|
|
32484
|
+
this.autoApproveSettleTimer = null;
|
|
32313
32485
|
}
|
|
32486
|
+
this.autoApproveBusy = true;
|
|
32487
|
+
this.lastAutoApprovalSignature = signature;
|
|
32488
|
+
this.pendingAutoApprovalSignature = "";
|
|
32489
|
+
this.pendingAutoApprovalSince = 0;
|
|
32490
|
+
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
32491
|
+
this.autoApproveBusyTimer = setTimeout(() => {
|
|
32492
|
+
this.autoApproveBusy = false;
|
|
32493
|
+
this.autoApproveBusyTimer = null;
|
|
32494
|
+
this.lastAutoApprovalSignature = "";
|
|
32495
|
+
}, 5e3);
|
|
32496
|
+
this.recordAutoApproval(modal?.message, buttonLabel, now);
|
|
32497
|
+
setTimeout(() => {
|
|
32498
|
+
this.adapter.resolveModal(buttonIndex);
|
|
32499
|
+
}, 0);
|
|
32314
32500
|
return autoApproveActive;
|
|
32315
32501
|
}
|
|
32502
|
+
/**
|
|
32503
|
+
* Re-drive the auto-approve check after the settle quiet window elapses.
|
|
32504
|
+
* The PTY may have gone silent once the approval prompt finished painting,
|
|
32505
|
+
* so no status-change frame is guaranteed to re-enter maybeAutoApproveStatus
|
|
32506
|
+
* — this timer-driven re-check picks up the now-settled modal and fires.
|
|
32507
|
+
* Deliberately lighter than detectStatusTransition(): it only re-evaluates
|
|
32508
|
+
* the approval decision; the next real PTY frame refreshes visible status.
|
|
32509
|
+
*/
|
|
32510
|
+
recheckAutoApproveSettled() {
|
|
32511
|
+
try {
|
|
32512
|
+
const adapterStatus = this.adapter.getStatus({ allowParse: false });
|
|
32513
|
+
this.maybeAutoApproveStatus(adapterStatus, Date.now());
|
|
32514
|
+
} catch {
|
|
32515
|
+
}
|
|
32516
|
+
}
|
|
32316
32517
|
detectStatusTransition() {
|
|
32317
32518
|
const now = Date.now();
|
|
32318
32519
|
const adapterStatus = this.adapter.getStatus({ allowParse: false });
|