@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.
@@ -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 ? "bf805cc2b4d63e722d2e330fa10a9890db3b4668" : void 0) ?? "unknown";
269
- const commitShort = readInjected(true ? "bf805cc2" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
270
- const version = readInjected(true ? "0.9.82-rc.267" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
271
- const builtAt = readInjected(true ? "2026-06-14T20:01:43.506Z" : void 0);
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 raw = this.terminal.formatPlainText({ trim: false }) || "";
9531
- if (!raw) return "";
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
- let first = 0;
9535
- let last = viewport.length;
9536
- while (first < last && !viewport[first]) first += 1;
9537
- while (last > first && !viewport[last - 1]) last -= 1;
9538
- return viewport.slice(first, last).join("\n");
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 (freeformText) {
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, 200),
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
- 200
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 modal = this.deriveModal(state, sections, lines.join("\n"));
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, sections, lines.join("\n"));
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 (!this.autoApproveBusy || signature !== this.lastAutoApprovalSignature) {
32301
- this.autoApproveBusy = true;
32302
- this.lastAutoApprovalSignature = signature;
32303
- if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
32304
- this.autoApproveBusyTimer = setTimeout(() => {
32305
- this.autoApproveBusy = false;
32306
- this.autoApproveBusyTimer = null;
32307
- this.lastAutoApprovalSignature = "";
32308
- }, 5e3);
32309
- this.recordAutoApproval(modal?.message, buttonLabel, now);
32310
- setTimeout(() => {
32311
- this.adapter.resolveModal(buttonIndex);
32312
- }, 0);
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 });