adhdev 0.9.5 → 0.9.8

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 CHANGED
@@ -8305,25 +8305,23 @@ async function handleResolveAction(h, args) {
8305
8305
  const effectiveModal = statusModal || surfacedModal;
8306
8306
  const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" ? "waiting_approval" : status?.status;
8307
8307
  LOG.info("Command", `[resolveAction] CLI PTY gate target=${String(args?.targetSessionId || "")} rawStatus=${String(status?.status || "")} effectiveStatus=${String(effectiveStatus || "")} statusModal=${statusModal ? "yes" : "no"} surfacedModal=${surfacedModal ? "yes" : "no"} instance=${targetInstance ? "yes" : "no"}`);
8308
- if (effectiveStatus !== "waiting_approval" && !effectiveModal) {
8308
+ if (!effectiveModal) {
8309
8309
  return { success: false, error: "Not in approval state" };
8310
8310
  }
8311
- const buttons = effectiveModal?.buttons || ["Allow once", "Always allow", "Deny"];
8311
+ const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
8312
8312
  let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
8313
- if (buttonIndex < 0) {
8313
+ if (buttonIndex < 0 && button) {
8314
8314
  const btnLower = button.toLowerCase();
8315
8315
  buttonIndex = buttons.findIndex((b) => b.toLowerCase().includes(btnLower));
8316
8316
  }
8317
+ if (buttonIndex < 0 && (action === "reject" || action === "deny")) {
8318
+ buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
8319
+ }
8320
+ if (buttonIndex < 0 && (action === "always" || /always/i.test(button))) {
8321
+ buttonIndex = buttons.findIndex((b) => /always/i.test(b));
8322
+ }
8317
8323
  if (buttonIndex < 0) {
8318
- if (action === "reject" || action === "deny") {
8319
- buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
8320
- if (buttonIndex < 0) buttonIndex = buttons.length - 1;
8321
- } else if (action === "always" || /always/i.test(button)) {
8322
- buttonIndex = buttons.findIndex((b) => /always/i.test(b));
8323
- if (buttonIndex < 0) buttonIndex = 1;
8324
- } else {
8325
- buttonIndex = 0;
8326
- }
8324
+ return { success: false, error: "Approval action did not match any visible button" };
8327
8325
  }
8328
8326
  if (typeof adapter.resolveModal === "function") {
8329
8327
  adapter.resolveModal(buttonIndex);
@@ -11132,8 +11130,35 @@ function promptLikelyVisible(screenText, promptSnippet) {
11132
11130
  function normalizeScreenSnapshot(text) {
11133
11131
  return sanitizeTerminalText(String(text || "")).replace(/\s+/g, " ").trim();
11134
11132
  }
11133
+ function shouldReflowComparableMessageLines(lines) {
11134
+ return Array.isArray(lines) && lines.length > 1 && lines.slice(0, -1).every((line) => String(line || "").trim().length >= 48) && !lines.some((line) => /^```/.test(line)) && !lines.some((line) => /^\|/.test(line)) && !lines.some((line) => /^\s*(?:[-*+] |\d+\.\s)/.test(line));
11135
+ }
11136
+ function joinComparableMessageLines(lines) {
11137
+ return lines.reduce((acc, line) => {
11138
+ const next = String(line || "").trim();
11139
+ if (!next) return acc;
11140
+ if (!acc) return next;
11141
+ if (/[,\d]$/.test(acc) && /^\d/.test(next)) {
11142
+ return `${acc}${next}`;
11143
+ }
11144
+ if (/[A-Za-z]$/.test(acc) && /^\d/.test(next)) {
11145
+ return `${acc}${next}`;
11146
+ }
11147
+ const fragmentMatch = acc.match(/([A-Za-z]{1,4})$/);
11148
+ const fragment = fragmentMatch ? fragmentMatch[1].toLowerCase() : "";
11149
+ if (/^[a-z]/.test(next) && fragment && !COMMON_COMPARABLE_WRAP_WORDS.has(fragment)) {
11150
+ return `${acc}${next}`;
11151
+ }
11152
+ return `${acc} ${next}`;
11153
+ }, "").replace(/\s+([,.;:!?])/g, "$1").replace(/(\d)\s+,/g, "$1,").replace(/\s+/g, " ").trim();
11154
+ }
11135
11155
  function normalizeComparableMessageContent(text) {
11136
- return String(text || "").replace(/\s+/g, " ").trim();
11156
+ const lines = String(text || "").split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
11157
+ if (lines.length === 0) return "";
11158
+ if (shouldReflowComparableMessageLines(lines)) {
11159
+ return joinComparableMessageLines(lines);
11160
+ }
11161
+ return lines.join(" ").replace(/\s+/g, " ").trim();
11137
11162
  }
11138
11163
  function trimPromptEchoPrefix(text, promptText) {
11139
11164
  const prompt2 = normalizeComparableMessageContent(String(promptText || ""));
@@ -11166,9 +11191,6 @@ function getLastUserPromptText(messages) {
11166
11191
  }
11167
11192
  return "";
11168
11193
  }
11169
- function looksLikeConfirmOnlyLabel(label) {
11170
- return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
11171
- }
11172
11194
  function parsePatternEntry(x) {
11173
11195
  if (x instanceof RegExp) return x;
11174
11196
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -11195,7 +11217,7 @@ function normalizeCliProviderForRuntime(raw) {
11195
11217
  }
11196
11218
  };
11197
11219
  }
11198
- var os10, path9, import_child_process4, buildCliSpawnEnv;
11220
+ var os10, path9, import_child_process4, buildCliSpawnEnv, COMMON_COMPARABLE_WRAP_WORDS;
11199
11221
  var init_provider_cli_shared = __esm({
11200
11222
  "../../oss/packages/daemon-core/src/cli-adapters/provider-cli-shared.ts"() {
11201
11223
  "use strict";
@@ -11204,6 +11226,32 @@ var init_provider_cli_shared = __esm({
11204
11226
  import_child_process4 = require("child_process");
11205
11227
  init_spawn_env();
11206
11228
  buildCliSpawnEnv = sanitizeSpawnEnv;
11229
+ COMMON_COMPARABLE_WRAP_WORDS = /* @__PURE__ */ new Set([
11230
+ "a",
11231
+ "an",
11232
+ "and",
11233
+ "as",
11234
+ "at",
11235
+ "but",
11236
+ "by",
11237
+ "for",
11238
+ "from",
11239
+ "in",
11240
+ "into",
11241
+ "is",
11242
+ "it",
11243
+ "of",
11244
+ "on",
11245
+ "or",
11246
+ "that",
11247
+ "the",
11248
+ "their",
11249
+ "then",
11250
+ "this",
11251
+ "to",
11252
+ "was",
11253
+ "with"
11254
+ ]);
11207
11255
  }
11208
11256
  });
11209
11257
 
@@ -11259,8 +11307,44 @@ function hydrateCliParsedMessages(parsedMessages, options) {
11259
11307
  };
11260
11308
  });
11261
11309
  }
11310
+ function chooseMoreComparableCliMessage(left2, right2) {
11311
+ const leftComparable = normalizeComparableMessageContent(left2.content || "");
11312
+ const rightComparable = normalizeComparableMessageContent(right2.content || "");
11313
+ if (leftComparable && leftComparable === rightComparable) {
11314
+ const leftNewlines = String(left2.content || "").split(/\r\n|\n|\r/g).length - 1;
11315
+ const rightNewlines = String(right2.content || "").split(/\r\n|\n|\r/g).length - 1;
11316
+ return rightNewlines < leftNewlines ? right2 : left2;
11317
+ }
11318
+ return rightComparable.length > leftComparable.length ? right2 : left2;
11319
+ }
11320
+ function dedupeConsecutiveComparableCliMessages(messages) {
11321
+ const deduped = [];
11322
+ for (const message of messages) {
11323
+ const current = {
11324
+ ...message,
11325
+ content: typeof message.content === "string" ? message.content : String(message.content || "")
11326
+ };
11327
+ const previous = deduped[deduped.length - 1];
11328
+ if (!previous) {
11329
+ deduped.push(current);
11330
+ continue;
11331
+ }
11332
+ const previousComparable = normalizeComparableMessageContent(previous.content || "");
11333
+ const currentComparable = normalizeComparableMessageContent(current.content || "");
11334
+ const sameRole = previous.role === current.role;
11335
+ const sameKind = (previous.kind || "standard") === (current.kind || "standard");
11336
+ const sameSender = (previous.senderName || "") === (current.senderName || "");
11337
+ const comparableMatch = previousComparable && previousComparable === currentComparable;
11338
+ if (sameRole && sameKind && sameSender && comparableMatch) {
11339
+ deduped[deduped.length - 1] = chooseMoreComparableCliMessage(previous, current);
11340
+ continue;
11341
+ }
11342
+ deduped.push(current);
11343
+ }
11344
+ return deduped;
11345
+ }
11262
11346
  function normalizeCliParsedMessages(parsedMessages, options) {
11263
- return hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
11347
+ return dedupeConsecutiveComparableCliMessages(hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
11264
11348
  role: message.role,
11265
11349
  content: message.content,
11266
11350
  timestamp: message.timestamp,
@@ -11270,7 +11354,7 @@ function normalizeCliParsedMessages(parsedMessages, options) {
11270
11354
  index: message.index,
11271
11355
  meta: message.meta,
11272
11356
  senderName: message.senderName
11273
- }));
11357
+ })));
11274
11358
  }
11275
11359
  function buildCliParseInput(options) {
11276
11360
  const {
@@ -11933,7 +12017,7 @@ var init_provider_cli_adapter = __esm({
11933
12017
  if (!hasStartupOutput) return;
11934
12018
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
11935
12019
  if (stableMs < 2e3) return;
11936
- const startupModal = this.getStartupConfirmationModal(screenText);
12020
+ const startupModal = this.runParseApproval(this.recentOutputBuffer);
11937
12021
  this.startupParseGate = false;
11938
12022
  if (this.startupSettleTimer) {
11939
12023
  clearTimeout(this.startupSettleTimer);
@@ -11990,11 +12074,17 @@ var init_provider_cli_adapter = __esm({
11990
12074
  if (this.currentStatus !== "waiting_approval") return;
11991
12075
  const tail = this.recentOutputBuffer;
11992
12076
  const screenText = this.terminalScreen.getText() || "";
11993
- const startupModal = this.getStartupConfirmationModal(screenText);
11994
- const modal = this.runParseApproval(tail) || startupModal;
12077
+ const modal = this.runParseApproval(tail);
11995
12078
  const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
11996
12079
  if (stillWaiting) {
11997
- this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
12080
+ if (!modal) {
12081
+ LOG.warn("CLI", `[${this.cliType}] approval timeout check found no actionable modal; keeping approval state fail-closed`);
12082
+ this.activeModal = null;
12083
+ this.onStatusChange?.();
12084
+ this.armApprovalExitTimeout();
12085
+ return;
12086
+ }
12087
+ this.activeModal = modal;
11998
12088
  this.onStatusChange?.();
11999
12089
  this.armApprovalExitTimeout();
12000
12090
  return;
@@ -12006,81 +12096,12 @@ var init_provider_cli_adapter = __esm({
12006
12096
  this.onStatusChange?.();
12007
12097
  }, 6e4);
12008
12098
  }
12009
- looksLikeVisibleIdlePrompt(screenText) {
12010
- const text = String(screenText || "");
12011
- if (!text.trim()) return false;
12012
- if (this.cliType === "codex-cli" && /(^|\n)\s*[❯›>]\s+(?:Find and fix a bug in @filename|Improve documentation in @filename|Use \/skills|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Run \/review on my current changes)(?:\n|$)/im.test(text)) {
12013
- return true;
12014
- }
12015
- return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text) || /⏎\s+send/i.test(text) || /\?\s*for\s*shortcuts/i.test(text) || /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text) || /workspace\s*\(\/directory\)/i.test(text) || /for\s*shortcuts/i.test(text);
12016
- }
12017
- findLastMatchingLineIndex(lines, predicate) {
12018
- for (let index = lines.length - 1; index >= 0; index -= 1) {
12019
- if (predicate(lines[index])) return index;
12020
- }
12021
- return -1;
12022
- }
12023
- looksLikeClaudeGeneratingLine(line) {
12024
- const trimmed = String(line || "").trim();
12025
- if (!trimmed) return false;
12026
- if (/^⏵⏵\s+accept edits on/i.test(trimmed)) return false;
12027
- if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) return true;
12028
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+\S+.*\b(?:thinking|thought for \d+s?)\b/i.test(trimmed)) return true;
12029
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+[A-Z][A-Za-z-]{3,}ing\b.*(?:…|\.{3})/u.test(trimmed)) return true;
12030
- if (/^[⏺•]\s+(?:Reading|Writing|Editing|Searching|Inspecting|Planning|Analyzing|Synthesizing|Drafting|Running|Listing|Scanning|Matching)\b.*(?:…|\.{3})/i.test(trimmed)) {
12031
- return /ctrl\+o to expand/i.test(trimmed) || /\b\d+\s+(?:file|files|pattern|patterns|director(?:y|ies)|match|matches|result|results)\b/i.test(trimmed);
12032
- }
12033
- return false;
12034
- }
12035
- detectClaudeGeneratingOverride(screenText, tail) {
12036
- if (this.cliType !== "claude-cli") return false;
12037
- const source = sanitizeTerminalText(screenText || tail || "");
12038
- if (!source.trim()) return false;
12039
- const allLines = source.split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
12040
- if (allLines.length === 0) return false;
12041
- const recentLines = allLines.slice(-12);
12042
- const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
12043
- const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
12044
- if (activeRegion.length === 0) return false;
12045
- return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
12046
- }
12047
- refineDetectedStatus(status, tail, screenText) {
12048
- if (this.startupParseGate) {
12049
- return this.getStartupConfirmationModal(screenText || "") ? "waiting_approval" : "starting";
12050
- }
12051
- if (status === "waiting_approval") return status;
12052
- if (this.detectClaudeGeneratingOverride(screenText || "", tail)) return "generating";
12053
- return status;
12054
- }
12055
- looksLikeVisibleAssistantCandidate(screenText) {
12056
- const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
12057
- for (const line of lines) {
12058
- const trimmed = String(line || "").trim();
12059
- if (!trimmed) continue;
12060
- if (/^➜\s+\S+/.test(trimmed)) continue;
12061
- if (/^Update available!/i.test(trimmed)) continue;
12062
- if (/Claude Code v\d/i.test(trimmed)) continue;
12063
- if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
12064
- if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
12065
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
12066
- if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
12067
- const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
12068
- if (!assistantMatch) continue;
12069
- const content = assistantMatch[1].trim();
12070
- if (!content) continue;
12071
- if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
12072
- if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
12073
- return true;
12074
- }
12075
- return false;
12076
- }
12077
12099
  shouldRetryFinishResponse(commitResult) {
12078
12100
  if (!this.currentTurnScope) return false;
12079
12101
  if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
12080
12102
  if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
12081
12103
  if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
12082
- const screenText = this.terminalScreen.getText() || "";
12083
- if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
12104
+ if (this.runDetectStatus(this.recentOutputBuffer) !== "idle") return false;
12084
12105
  const now = Date.now();
12085
12106
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
12086
12107
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
@@ -12104,45 +12125,21 @@ var init_provider_cli_adapter = __esm({
12104
12125
  }
12105
12126
  return false;
12106
12127
  }
12107
- getStartupConfirmationModal(screenText) {
12108
- const text = sanitizeTerminalText(String(screenText || ""));
12109
- if (!text.trim()) return null;
12110
- if (this.cliType === "claude-cli") {
12111
- const hasTrustPrompt = /Quick safety check/i.test(text) || /Is this a project you trust/i.test(text) || /Do you trust (?:this project|the contents of this directory|the files in this folder)/i.test(text);
12112
- const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
12113
- if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
12114
- return {
12115
- message: "Confirm Claude Code project trust",
12116
- buttons: ["Continue"]
12117
- };
12118
- }
12119
- }
12120
- return null;
12121
- }
12122
- shouldResolveModalWithEnter(modal, buttonIndex) {
12123
- if (!modal || buttonIndex !== 0) return false;
12124
- const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
12125
- if (buttons.length !== 1) return false;
12126
- const buttonLabel = String(buttons[0] || "").trim();
12127
- return looksLikeConfirmOnlyLabel(buttonLabel);
12128
- }
12129
12128
  async waitForInteractivePrompt(maxWaitMs = 5e3) {
12130
12129
  const startedAt = Date.now();
12131
12130
  let loggedWait = false;
12132
12131
  while (Date.now() - startedAt < maxWaitMs) {
12133
12132
  this.resolveStartupState("interactive_wait");
12134
12133
  const screenText = this.terminalScreen.getText() || "";
12135
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
12136
12134
  const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
12137
12135
  const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
12138
12136
  const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
12139
- const startupLikelyActive = /Welcome back|Tips for getting|Recent activity|Claude Code v\d/i.test(screenText);
12140
- const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "generating";
12137
+ const interactiveReady = status === "idle" && stableMs >= 700 && recentlyOutput >= 350;
12141
12138
  if (interactiveReady) {
12142
12139
  if (loggedWait) {
12143
12140
  LOG.info(
12144
12141
  "CLI",
12145
- `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput}, startup=${startupLikelyActive})`
12142
+ `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput})`
12146
12143
  );
12147
12144
  }
12148
12145
  return;
@@ -12151,7 +12148,7 @@ var init_provider_cli_adapter = __esm({
12151
12148
  loggedWait = true;
12152
12149
  LOG.info(
12153
12150
  "CLI",
12154
- `[${this.cliType}] Waiting for interactive prompt: hasPrompt=${hasPrompt} stableMs=${stableMs} recentOutputMs=${recentlyOutput} status=${status} startup=${startupLikelyActive} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
12151
+ `[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
12155
12152
  );
12156
12153
  }
12157
12154
  await new Promise((resolve16) => setTimeout(resolve16, 50));
@@ -12162,13 +12159,12 @@ var init_provider_cli_adapter = __esm({
12162
12159
  `[${this.cliType}] Interactive prompt wait timed out after ${maxWaitMs}ms; proceeding with screen=${JSON.stringify(summarizeCliTraceText(finalScreenText, 240)).slice(0, 280)}`
12163
12160
  );
12164
12161
  }
12165
- clearStaleIdleResponseGuard(reason) {
12166
- const screenText = this.terminalScreen.getText() || "";
12167
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
12168
- const blockingModal = this.activeModal || this.getStartupConfirmationModal(screenText);
12169
- if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !visibleIdlePrompt || !!blockingModal) {
12170
- return false;
12171
- }
12162
+ trimLastAssistantEcho(messages, prompt2) {
12163
+ if (!prompt2) return;
12164
+ const last = [...messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
12165
+ if (last) last.content = trimPromptEchoPrefix(last.content, prompt2);
12166
+ }
12167
+ clearAllTimers() {
12172
12168
  if (this.responseTimeout) {
12173
12169
  clearTimeout(this.responseTimeout);
12174
12170
  this.responseTimeout = null;
@@ -12181,10 +12177,38 @@ var init_provider_cli_adapter = __esm({
12181
12177
  clearTimeout(this.approvalExitTimeout);
12182
12178
  this.approvalExitTimeout = null;
12183
12179
  }
12180
+ if (this.submitRetryTimer) {
12181
+ clearTimeout(this.submitRetryTimer);
12182
+ this.submitRetryTimer = null;
12183
+ }
12184
12184
  if (this.finishRetryTimer) {
12185
12185
  clearTimeout(this.finishRetryTimer);
12186
12186
  this.finishRetryTimer = null;
12187
12187
  }
12188
+ if (this.settleTimer) {
12189
+ clearTimeout(this.settleTimer);
12190
+ this.settleTimer = null;
12191
+ }
12192
+ if (this.pendingScriptStatusTimer) {
12193
+ clearTimeout(this.pendingScriptStatusTimer);
12194
+ this.pendingScriptStatusTimer = null;
12195
+ }
12196
+ if (this.pendingOutputParseTimer) {
12197
+ clearTimeout(this.pendingOutputParseTimer);
12198
+ this.pendingOutputParseTimer = null;
12199
+ }
12200
+ if (this.ptyOutputFlushTimer) {
12201
+ clearTimeout(this.ptyOutputFlushTimer);
12202
+ this.ptyOutputFlushTimer = null;
12203
+ }
12204
+ }
12205
+ clearStaleIdleResponseGuard(reason) {
12206
+ const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
12207
+ const isIdle = this.runDetectStatus(this.recentOutputBuffer) === "idle";
12208
+ if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !isIdle || !!blockingModal) {
12209
+ return false;
12210
+ }
12211
+ this.clearAllTimers();
12188
12212
  this.clearIdleFinishCandidate(reason);
12189
12213
  this.responseBuffer = "";
12190
12214
  this.isWaitingForResponse = false;
@@ -12194,10 +12218,7 @@ var init_provider_cli_adapter = __esm({
12194
12218
  this.finishRetryCount = 0;
12195
12219
  this.currentTurnScope = null;
12196
12220
  this.activeModal = null;
12197
- this.recordTrace("stale_idle_response_cleared", {
12198
- reason,
12199
- screenText: summarizeCliTraceText(screenText, 240)
12200
- });
12221
+ this.recordTrace("stale_idle_response_cleared", { reason });
12201
12222
  return true;
12202
12223
  }
12203
12224
  hasMeaningfulResponseBuffer(promptSnippet) {
@@ -12232,16 +12253,15 @@ var init_provider_cli_adapter = __esm({
12232
12253
  if (this.startupParseGate) {
12233
12254
  return;
12234
12255
  }
12235
- const startupModal = this.getStartupConfirmationModal(screenText);
12236
12256
  const parsedTranscript = this.parseCurrentTranscript(
12237
12257
  this.committedMessages,
12238
12258
  this.responseBuffer,
12239
12259
  this.currentTurnScope
12240
12260
  );
12241
12261
  const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsedTranscript.activeModal : null;
12242
- const modal = this.runParseApproval(tail) || parsedModal || startupModal;
12262
+ const modal = this.runParseApproval(tail) || parsedModal;
12243
12263
  const rawScriptStatus = this.runDetectStatus(tail);
12244
- const scriptStatus = startupModal ? "waiting_approval" : parsedModal && parsedTranscript?.status === "waiting_approval" ? "waiting_approval" : rawScriptStatus;
12264
+ const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
12245
12265
  const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
12246
12266
  committedMessages: this.committedMessages,
12247
12267
  scope: this.currentTurnScope,
@@ -12296,15 +12316,44 @@ var init_provider_cli_adapter = __esm({
12296
12316
  }
12297
12317
  if (!scriptStatus) return;
12298
12318
  const prevStatus = this.currentStatus;
12299
- const clearPendingScriptStatus = () => {
12319
+ const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
12320
+ if (!this.applyPendingScriptStatusDebounce(ctx)) return;
12321
+ const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
12322
+ LOG.info(
12323
+ "CLI",
12324
+ `[${this.cliType}] settled diagnostics prompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 140)} scriptStatus=${String(scriptStatus || "")} parsedStatus=${String(parsedTranscript?.status || "")} parsedMsgCount=${parsedMessages.length} lastParsedAssistant=${JSON.stringify(summarizeCliTraceText(lastParsedAssistant?.content || "", 120)).slice(0, 160)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
12325
+ );
12326
+ const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
12327
+ if (shouldHoldGenerating) {
12328
+ this.applyHoldGenerating(ctx, recentInteractiveActivity);
12329
+ return;
12330
+ }
12331
+ if (scriptStatus === "waiting_approval") {
12332
+ this.applyWaitingApproval(ctx);
12333
+ return;
12334
+ }
12335
+ if (scriptStatus === "generating") {
12336
+ this.applyGenerating(ctx);
12337
+ return;
12338
+ }
12339
+ if (scriptStatus === "idle") {
12340
+ this.applyIdle(ctx, now);
12341
+ }
12342
+ }
12343
+ // Returns false if the caller should bail out (debounce pending).
12344
+ applyPendingScriptStatusDebounce(ctx) {
12345
+ const { now, scriptStatus, prevStatus } = ctx;
12346
+ const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
12347
+ if (!shouldDebounce) {
12300
12348
  this.pendingScriptStatus = null;
12301
12349
  this.pendingScriptStatusSince = 0;
12302
12350
  if (this.pendingScriptStatusTimer) {
12303
12351
  clearTimeout(this.pendingScriptStatusTimer);
12304
12352
  this.pendingScriptStatusTimer = null;
12305
12353
  }
12306
- };
12307
- const armPendingScriptStatus = (delayMs) => {
12354
+ return true;
12355
+ }
12356
+ const armPending = (delayMs) => {
12308
12357
  if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
12309
12358
  this.pendingScriptStatusTimer = setTimeout(() => {
12310
12359
  this.pendingScriptStatusTimer = null;
@@ -12312,200 +12361,187 @@ var init_provider_cli_adapter = __esm({
12312
12361
  this.evaluateSettled();
12313
12362
  }, delayMs);
12314
12363
  };
12315
- const shouldDebouncePromotion = (status) => prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
12316
- if (shouldDebouncePromotion(scriptStatus)) {
12317
- if (this.pendingScriptStatus !== scriptStatus) {
12318
- this.pendingScriptStatus = scriptStatus;
12319
- this.pendingScriptStatusSince = now;
12320
- armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
12321
- return;
12322
- }
12323
- const elapsed = now - this.pendingScriptStatusSince;
12324
- if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
12325
- armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
12326
- return;
12327
- }
12328
- } else {
12329
- clearPendingScriptStatus();
12330
- }
12331
- const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
12332
- const statusActivityHoldMs = this.getStatusActivityHoldMs();
12333
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
12334
- const visibleAssistantCandidate = this.looksLikeVisibleAssistantCandidate(screenText);
12335
- if (this.currentTurnScope && this.cliType === "claude-cli") {
12336
- LOG.info(
12337
- "CLI",
12338
- `[${this.cliType}] settled diagnostics prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} scriptStatus=${String(scriptStatus || "")} parsedStatus=${String(parsedTranscript?.status || "")} parsedMsgCount=${parsedMessages.length} lastParsedAssistant=${JSON.stringify(summarizeCliTraceText(lastParsedAssistant?.content || "", 120)).slice(0, 160)} visibleIdlePrompt=${String(visibleIdlePrompt)} visibleAssistantCandidate=${String(visibleAssistantCandidate)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
12339
- );
12364
+ if (this.pendingScriptStatus !== scriptStatus) {
12365
+ this.pendingScriptStatus = scriptStatus;
12366
+ this.pendingScriptStatusSince = now;
12367
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
12368
+ return false;
12340
12369
  }
12341
- const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(visibleIdlePrompt && visibleAssistantCandidate) && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
12342
- if (shouldHoldGenerating) {
12343
- this.clearIdleFinishCandidate("hold_generating_recent_activity");
12344
- this.setStatus("generating", "recent_activity_hold");
12345
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
12346
- this.idleTimeout = setTimeout(() => {
12347
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12348
- if (this.shouldDeferIdleTimeoutFinish()) return;
12349
- this.finishResponse();
12350
- }
12351
- }, this.timeouts.generatingIdle);
12352
- this.recordTrace("hold_generating_recent_activity", {
12353
- scriptStatus,
12354
- recentInteractiveActivity,
12355
- lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
12356
- lastScreenChangeAt: this.lastScreenChangeAt,
12357
- holdMs: statusActivityHoldMs,
12358
- ...buildCliTraceParseSnapshot({
12359
- accumulatedBuffer: this.accumulatedBuffer,
12360
- accumulatedRawBuffer: this.accumulatedRawBuffer,
12361
- responseBuffer: this.responseBuffer,
12362
- partialResponse: this.responseBuffer,
12363
- scope: this.currentTurnScope
12364
- })
12365
- });
12366
- this.onStatusChange?.();
12367
- return;
12370
+ const elapsed = now - this.pendingScriptStatusSince;
12371
+ if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
12372
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
12373
+ return false;
12368
12374
  }
12369
- if (scriptStatus === "waiting_approval") {
12370
- this.clearIdleFinishCandidate("waiting_approval");
12371
- const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
12372
- const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
12373
- if ((inCooldown || visibleIdlePrompt2) && !modal) {
12374
- if (this.approvalExitTimeout) {
12375
- clearTimeout(this.approvalExitTimeout);
12376
- this.approvalExitTimeout = null;
12377
- }
12378
- this.activeModal = null;
12379
- if (this.isWaitingForResponse) {
12380
- this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
12381
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
12382
- this.idleTimeout = setTimeout(() => {
12383
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12384
- if (this.shouldDeferIdleTimeoutFinish()) return;
12385
- this.finishResponse();
12386
- }
12387
- }, this.timeouts.generatingIdle);
12388
- } else {
12389
- this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
12390
- }
12391
- this.onStatusChange?.();
12392
- return;
12375
+ return true;
12376
+ }
12377
+ applyHoldGenerating(ctx, recentInteractiveActivity) {
12378
+ const { scriptStatus } = ctx;
12379
+ this.clearIdleFinishCandidate("hold_generating_recent_activity");
12380
+ this.setStatus("generating", "recent_activity_hold");
12381
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12382
+ this.idleTimeout = setTimeout(() => {
12383
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12384
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12385
+ this.finishResponse();
12386
+ }
12387
+ }, this.timeouts.generatingIdle);
12388
+ this.recordTrace("hold_generating_recent_activity", {
12389
+ scriptStatus,
12390
+ recentInteractiveActivity,
12391
+ lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
12392
+ lastScreenChangeAt: this.lastScreenChangeAt,
12393
+ holdMs: this.getStatusActivityHoldMs(),
12394
+ ...buildCliTraceParseSnapshot({
12395
+ accumulatedBuffer: this.accumulatedBuffer,
12396
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
12397
+ responseBuffer: this.responseBuffer,
12398
+ partialResponse: this.responseBuffer,
12399
+ scope: this.currentTurnScope
12400
+ })
12401
+ });
12402
+ this.onStatusChange?.();
12403
+ }
12404
+ applyWaitingApproval(ctx) {
12405
+ const { modal } = ctx;
12406
+ this.clearIdleFinishCandidate("waiting_approval");
12407
+ const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
12408
+ if (inCooldown && !modal) {
12409
+ if (this.approvalExitTimeout) {
12410
+ clearTimeout(this.approvalExitTimeout);
12411
+ this.approvalExitTimeout = null;
12393
12412
  }
12394
- if (!inCooldown) {
12395
- this.isWaitingForResponse = true;
12396
- this.setStatus("waiting_approval", "script_detect");
12397
- this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
12413
+ this.activeModal = null;
12414
+ if (this.isWaitingForResponse) {
12415
+ this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
12398
12416
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
12399
- this.armApprovalExitTimeout();
12400
- this.onStatusChange?.();
12401
- return;
12417
+ this.idleTimeout = setTimeout(() => {
12418
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12419
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12420
+ this.finishResponse();
12421
+ }
12422
+ }, this.timeouts.generatingIdle);
12423
+ } else {
12424
+ this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
12402
12425
  }
12426
+ this.onStatusChange?.();
12427
+ return;
12403
12428
  }
12404
- if (scriptStatus === "generating") {
12405
- this.clearIdleFinishCandidate("generating");
12406
- const effectiveScreenText = screenText || this.accumulatedBuffer;
12407
- const noActiveTurn = !this.currentTurnScope;
12408
- const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText) || /accept edits on/i.test(effectiveScreenText) && (/Update available!/i.test(screenText) || /\/effort/i.test(screenText) || /^.*➜\s+\S+/m.test(effectiveScreenText));
12409
- if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
12429
+ if (!inCooldown) {
12430
+ if (!modal) {
12431
+ LOG.warn("CLI", `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
12410
12432
  return;
12411
12433
  }
12412
- if (prevStatus === "waiting_approval") {
12413
- if (this.approvalExitTimeout) {
12414
- clearTimeout(this.approvalExitTimeout);
12415
- this.approvalExitTimeout = null;
12416
- }
12417
- this.activeModal = null;
12418
- this.lastApprovalResolvedAt = Date.now();
12419
- }
12420
- if (!this.isWaitingForResponse) {
12421
- this.isWaitingForResponse = true;
12422
- this.responseBuffer = "";
12423
- }
12424
- this.setStatus("generating", "script_detect");
12434
+ this.isWaitingForResponse = true;
12435
+ this.setStatus("waiting_approval", "script_detect");
12436
+ this.activeModal = modal;
12425
12437
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
12426
- this.idleTimeout = setTimeout(() => {
12427
- if (this.isWaitingForResponse) {
12428
- if (this.shouldDeferIdleTimeoutFinish()) return;
12429
- this.finishResponse();
12430
- }
12431
- }, this.timeouts.generatingIdle);
12438
+ this.armApprovalExitTimeout();
12432
12439
  this.onStatusChange?.();
12440
+ }
12441
+ }
12442
+ applyGenerating(ctx) {
12443
+ const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
12444
+ this.clearIdleFinishCandidate("generating");
12445
+ const effectiveScreenText = screenText || this.accumulatedBuffer;
12446
+ const noActiveTurn = !this.currentTurnScope;
12447
+ const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText) || /accept edits on/i.test(effectiveScreenText) && (/Update available!/i.test(screenText) || /\/effort/i.test(screenText) || /^.*➜\s+\S+/m.test(effectiveScreenText));
12448
+ if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
12433
12449
  return;
12434
12450
  }
12435
- if (scriptStatus === "idle") {
12436
- if (prevStatus === "waiting_approval") {
12437
- if (this.approvalExitTimeout) {
12438
- clearTimeout(this.approvalExitTimeout);
12439
- this.approvalExitTimeout = null;
12440
- }
12441
- this.activeModal = null;
12442
- this.lastApprovalResolvedAt = Date.now();
12451
+ if (prevStatus === "waiting_approval") {
12452
+ if (this.approvalExitTimeout) {
12453
+ clearTimeout(this.approvalExitTimeout);
12454
+ this.approvalExitTimeout = null;
12443
12455
  }
12456
+ this.activeModal = null;
12457
+ this.lastApprovalResolvedAt = Date.now();
12458
+ }
12459
+ if (!this.isWaitingForResponse) {
12460
+ this.isWaitingForResponse = true;
12461
+ this.responseBuffer = "";
12462
+ }
12463
+ this.setStatus("generating", "script_detect");
12464
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12465
+ this.idleTimeout = setTimeout(() => {
12444
12466
  if (this.isWaitingForResponse) {
12445
- const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
12446
- const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
12447
- const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
12448
- const hasAssistantTurn = !!lastParsedAssistant;
12449
- const assistantLength = lastParsedAssistant?.content?.length || 0;
12450
- const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
12451
- const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
12452
- const idleStableThresholdMs = idleFinishConfirmMs;
12453
- const idleReady = visibleIdlePrompt2 && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
12454
- const candidate = this.idleFinishCandidate;
12455
- const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
12456
- const canFinishImmediately = idleReady && candidateQuiet;
12457
- this.recordTrace("idle_decision", {
12458
- visibleIdlePrompt: visibleIdlePrompt2,
12459
- quietForMs,
12460
- screenStableMs,
12461
- hasAssistantTurn,
12462
- assistantLength,
12463
- hasModal: !!modal,
12464
- idleQuietThresholdMs,
12465
- idleStableThresholdMs,
12466
- idleReady,
12467
- idleFinishConfirmMs,
12468
- idleFinishCandidate: candidate,
12469
- candidateQuiet,
12470
- canFinishImmediately,
12471
- submitPendingUntil: this.submitPendingUntil,
12472
- responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
12473
- ...buildCliTraceParseSnapshot({
12474
- accumulatedBuffer: this.accumulatedBuffer,
12475
- accumulatedRawBuffer: this.accumulatedRawBuffer,
12476
- responseBuffer: this.responseBuffer,
12477
- partialResponse: this.responseBuffer,
12478
- scope: this.currentTurnScope
12479
- })
12480
- });
12481
- if (canFinishImmediately) {
12482
- this.clearIdleFinishCandidate("finish_response");
12483
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
12484
- this.finishResponse();
12485
- return;
12486
- }
12487
- if (idleReady) {
12488
- if (!candidate) {
12489
- this.armIdleFinishCandidate(assistantLength);
12490
- return;
12491
- }
12492
- } else {
12493
- this.clearIdleFinishCandidate("idle_not_ready");
12494
- }
12495
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
12496
- this.idleTimeout = setTimeout(() => {
12497
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12498
- if (this.shouldDeferIdleTimeoutFinish()) return;
12499
- this.clearIdleFinishCandidate("idle_timeout_finish");
12500
- this.finishResponse();
12501
- }
12502
- }, this.timeouts.idleFinish);
12503
- } else if (prevStatus !== "idle") {
12467
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12468
+ this.finishResponse();
12469
+ }
12470
+ }, this.timeouts.generatingIdle);
12471
+ this.onStatusChange?.();
12472
+ }
12473
+ applyIdle(ctx, now) {
12474
+ const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
12475
+ if (prevStatus === "waiting_approval") {
12476
+ if (this.approvalExitTimeout) {
12477
+ clearTimeout(this.approvalExitTimeout);
12478
+ this.approvalExitTimeout = null;
12479
+ }
12480
+ this.activeModal = null;
12481
+ this.lastApprovalResolvedAt = Date.now();
12482
+ }
12483
+ if (!this.isWaitingForResponse) {
12484
+ if (prevStatus !== "idle") {
12504
12485
  this.clearIdleFinishCandidate("idle_without_response");
12505
12486
  this.setStatus("idle", "script_detect");
12506
12487
  this.onStatusChange?.();
12507
12488
  }
12489
+ return;
12508
12490
  }
12491
+ const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
12492
+ const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
12493
+ const hasAssistantTurn = !!lastParsedAssistant;
12494
+ const assistantLength = lastParsedAssistant?.content?.length || 0;
12495
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
12496
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
12497
+ const idleReady = !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleFinishConfirmMs;
12498
+ const candidate = this.idleFinishCandidate;
12499
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
12500
+ this.recordTrace("idle_decision", {
12501
+ quietForMs,
12502
+ screenStableMs,
12503
+ hasAssistantTurn,
12504
+ assistantLength,
12505
+ hasModal: !!modal,
12506
+ idleQuietThresholdMs,
12507
+ idleStableThresholdMs: idleFinishConfirmMs,
12508
+ idleReady,
12509
+ idleFinishConfirmMs,
12510
+ idleFinishCandidate: candidate,
12511
+ candidateQuiet,
12512
+ canFinishImmediately: idleReady && candidateQuiet,
12513
+ submitPendingUntil: this.submitPendingUntil,
12514
+ responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
12515
+ ...buildCliTraceParseSnapshot({
12516
+ accumulatedBuffer: this.accumulatedBuffer,
12517
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
12518
+ responseBuffer: this.responseBuffer,
12519
+ partialResponse: this.responseBuffer,
12520
+ scope: this.currentTurnScope
12521
+ })
12522
+ });
12523
+ if (idleReady && candidateQuiet) {
12524
+ this.clearIdleFinishCandidate("finish_response");
12525
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12526
+ this.finishResponse();
12527
+ return;
12528
+ }
12529
+ if (idleReady) {
12530
+ if (!candidate) {
12531
+ this.armIdleFinishCandidate(assistantLength);
12532
+ return;
12533
+ }
12534
+ } else {
12535
+ this.clearIdleFinishCandidate("idle_not_ready");
12536
+ }
12537
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12538
+ this.idleTimeout = setTimeout(() => {
12539
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12540
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12541
+ this.clearIdleFinishCandidate("idle_timeout_finish");
12542
+ this.finishResponse();
12543
+ }
12544
+ }, this.timeouts.idleFinish);
12509
12545
  }
12510
12546
  finishResponse() {
12511
12547
  if (this.submitPendingUntil > Date.now()) return;
@@ -12544,26 +12580,7 @@ var init_provider_cli_adapter = __esm({
12544
12580
  }, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
12545
12581
  return;
12546
12582
  }
12547
- if (this.responseTimeout) {
12548
- clearTimeout(this.responseTimeout);
12549
- this.responseTimeout = null;
12550
- }
12551
- if (this.idleTimeout) {
12552
- clearTimeout(this.idleTimeout);
12553
- this.idleTimeout = null;
12554
- }
12555
- if (this.approvalExitTimeout) {
12556
- clearTimeout(this.approvalExitTimeout);
12557
- this.approvalExitTimeout = null;
12558
- }
12559
- if (this.submitRetryTimer) {
12560
- clearTimeout(this.submitRetryTimer);
12561
- this.submitRetryTimer = null;
12562
- }
12563
- if (this.finishRetryTimer) {
12564
- clearTimeout(this.finishRetryTimer);
12565
- this.finishRetryTimer = null;
12566
- }
12583
+ this.clearAllTimers();
12567
12584
  this.responseBuffer = "";
12568
12585
  this.isWaitingForResponse = false;
12569
12586
  this.responseSettleIgnoreUntil = 0;
@@ -12575,18 +12592,12 @@ var init_provider_cli_adapter = __esm({
12575
12592
  this.setStatus("idle", "response_finished");
12576
12593
  this.onStatusChange?.();
12577
12594
  }
12578
- maybeCommitVisibleIdleTranscript(parsed, options) {
12595
+ maybeCommitVisibleIdleTranscript(parsed) {
12579
12596
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
12580
12597
  if (!allowImmediateScriptIdleCommit) return false;
12581
12598
  if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
12582
12599
  return false;
12583
12600
  }
12584
- if (options?.requireVisibleAssistantCandidate) {
12585
- const candidateText = options.screenText || this.terminalScreen.getText() || "";
12586
- if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
12587
- return false;
12588
- }
12589
- }
12590
12601
  const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
12591
12602
  committedMessages: this.committedMessages,
12592
12603
  scope: this.currentTurnScope,
@@ -12595,33 +12606,8 @@ var init_provider_cli_adapter = __esm({
12595
12606
  const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
12596
12607
  if (!visibleAssistant) return false;
12597
12608
  this.committedMessages = hydratedForIdleCommit;
12598
- const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
12599
- if (promptForTrim) {
12600
- const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
12601
- if (lastAssistantForTrim) {
12602
- lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
12603
- }
12604
- }
12605
- if (this.responseTimeout) {
12606
- clearTimeout(this.responseTimeout);
12607
- this.responseTimeout = null;
12608
- }
12609
- if (this.idleTimeout) {
12610
- clearTimeout(this.idleTimeout);
12611
- this.idleTimeout = null;
12612
- }
12613
- if (this.approvalExitTimeout) {
12614
- clearTimeout(this.approvalExitTimeout);
12615
- this.approvalExitTimeout = null;
12616
- }
12617
- if (this.submitRetryTimer) {
12618
- clearTimeout(this.submitRetryTimer);
12619
- this.submitRetryTimer = null;
12620
- }
12621
- if (this.finishRetryTimer) {
12622
- clearTimeout(this.finishRetryTimer);
12623
- this.finishRetryTimer = null;
12624
- }
12609
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
12610
+ this.clearAllTimers();
12625
12611
  this.syncMessageViews();
12626
12612
  this.responseBuffer = "";
12627
12613
  this.isWaitingForResponse = false;
@@ -12651,13 +12637,7 @@ var init_provider_cli_adapter = __esm({
12651
12637
  scope: this.currentTurnScope,
12652
12638
  lastOutputAt: this.lastOutputAt
12653
12639
  });
12654
- const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
12655
- if (promptForTrim) {
12656
- const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
12657
- if (lastAssistantForTrim) {
12658
- lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
12659
- }
12660
- }
12640
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
12661
12641
  this.syncMessageViews();
12662
12642
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
12663
12643
  if (this.currentTurnScope) {
@@ -12715,7 +12695,7 @@ var init_provider_cli_adapter = __esm({
12715
12695
  screen: buildCliScreenSnapshot(screenText),
12716
12696
  tailScreen: buildCliScreenSnapshot(text.slice(-500))
12717
12697
  });
12718
- return this.refineDetectedStatus(status, text, screenText || "");
12698
+ return status;
12719
12699
  } catch (e) {
12720
12700
  LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
12721
12701
  return null;
@@ -12755,23 +12735,21 @@ var init_provider_cli_adapter = __esm({
12755
12735
  if (!inApprovalCooldown) {
12756
12736
  return parsed;
12757
12737
  }
12758
- const startupModal = this.getStartupConfirmationModal(screenText || "");
12759
- const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
12738
+ const visibleModal = this.runParseApproval(recentBuffer);
12760
12739
  if (visibleModal) {
12761
12740
  return parsed;
12762
12741
  }
12763
12742
  const detectedStatus = this.runDetectStatus(recentBuffer);
12764
- const fallbackStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
12743
+ const resolvedStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
12765
12744
  return {
12766
12745
  ...parsed,
12767
- status: fallbackStatus,
12746
+ status: resolvedStatus,
12768
12747
  activeModal: null
12769
12748
  };
12770
12749
  }
12771
12750
  // ─── Public API (CliAdapter) ───────────────────
12772
12751
  getStatus() {
12773
- const screenText = this.terminalScreen.getText() || "";
12774
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
12752
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
12775
12753
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
12776
12754
  let effectiveModal = startupModal || this.activeModal;
12777
12755
  if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
@@ -12852,8 +12830,7 @@ var init_provider_cli_adapter = __esm({
12852
12830
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
12853
12831
  }));
12854
12832
  const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === "assistant" && typeof message.content === "string" && message.content.trim());
12855
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
12856
- const shouldAdoptParsedIdleReplay = !this.currentTurnScope && !this.activeModal && !!parsedLastAssistant && parsedTranscriptIsRicherThanCommitted(parsedHydratedMessages, committedHydratedMessages) && (this.currentStatus === "idle" || this.currentStatus === "generating" && this.isWaitingForResponse && parsed.status === "idle" && visibleIdlePrompt);
12833
+ const shouldAdoptParsedIdleReplay = !this.currentTurnScope && !this.activeModal && !!parsedLastAssistant && parsedTranscriptIsRicherThanCommitted(parsedHydratedMessages, committedHydratedMessages) && (this.currentStatus === "idle" || this.currentStatus === "generating" && this.isWaitingForResponse && parsed.status === "idle" && this.runDetectStatus(this.recentOutputBuffer) === "idle");
12857
12834
  if (shouldAdoptParsedIdleReplay) {
12858
12835
  this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
12859
12836
  committedMessages: this.committedMessages,
@@ -12982,17 +12959,9 @@ var init_provider_cli_adapter = __esm({
12982
12959
  if (parsed && typeof parsed === "object") {
12983
12960
  Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
12984
12961
  }
12985
- const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
12986
- if (parsed && refinedStatus && parsed.status !== refinedStatus) {
12987
- parsed.status = refinedStatus;
12988
- }
12989
12962
  const normalizedParsed = this.suppressStaleParsedApproval(parsed, input.recentBuffer, input.screenText);
12990
- const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
12991
- if (normalizedParsed && Array.isArray(normalizedParsed.messages) && promptForTrim) {
12992
- const lastAssistant = [...normalizedParsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
12993
- if (lastAssistant) {
12994
- lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
12995
- }
12963
+ if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
12964
+ this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
12996
12965
  }
12997
12966
  this.parseErrorMessage = null;
12998
12967
  return normalizedParsed;
@@ -13020,16 +12989,11 @@ var init_provider_cli_adapter = __esm({
13020
12989
  LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
13021
12990
  }
13022
12991
  }
13023
- if (!promptText && data) {
13024
- promptText = `Please fix the following issue:
13025
- ${data.title || ""}
13026
- ${data.explanation || ""}
13027
-
13028
- ${data.message || ""}`.trim();
13029
- }
13030
- if (promptText) {
13031
- await this.sendMessage(promptText);
12992
+ if (!promptText) {
12993
+ LOG.warn("CLI", `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
12994
+ return;
13032
12995
  }
12996
+ await this.sendMessage(promptText);
13033
12997
  }
13034
12998
  async sendMessage(text) {
13035
12999
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
@@ -13047,9 +13011,7 @@ ${data.message || ""}`.trim();
13047
13011
  }
13048
13012
  if (!this.ready) {
13049
13013
  this.resolveStartupState("send_precheck");
13050
- const screenText = this.terminalScreen.getText() || "";
13051
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
13052
- if (hasPrompt && this.currentStatus === "idle") {
13014
+ if (this.runDetectStatus(this.recentOutputBuffer) === "idle" && this.currentStatus === "idle") {
13053
13015
  this.ready = true;
13054
13016
  this.startupParseGate = false;
13055
13017
  LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
@@ -13155,7 +13117,10 @@ ${data.message || ""}`.trim();
13155
13117
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
13156
13118
  const screenText2 = this.terminalScreen.getText();
13157
13119
  if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
13158
- if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText2)) return;
13120
+ const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
13121
+ if (liveApproval) return;
13122
+ const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
13123
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
13159
13124
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
13160
13125
  LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
13161
13126
  this.recordTrace("submit_write", {
@@ -13192,6 +13157,10 @@ ${data.message || ""}`.trim();
13192
13157
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
13193
13158
  const screenText = this.terminalScreen.getText();
13194
13159
  if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
13160
+ const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
13161
+ if (liveApproval) return;
13162
+ const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
13163
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
13195
13164
  LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
13196
13165
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
13197
13166
  this.recordTrace("submit_write", {
@@ -13323,44 +13292,9 @@ ${data.message || ""}`.trim();
13323
13292
  }
13324
13293
  shutdown() {
13325
13294
  this.clearIdleFinishCandidate("shutdown");
13326
- if (this.settleTimer) {
13327
- clearTimeout(this.settleTimer);
13328
- this.settleTimer = null;
13329
- }
13330
- if (this.approvalExitTimeout) {
13331
- clearTimeout(this.approvalExitTimeout);
13332
- this.approvalExitTimeout = null;
13333
- }
13334
- if (this.submitRetryTimer) {
13335
- clearTimeout(this.submitRetryTimer);
13336
- this.submitRetryTimer = null;
13337
- }
13338
- if (this.finishRetryTimer) {
13339
- clearTimeout(this.finishRetryTimer);
13340
- this.finishRetryTimer = null;
13341
- }
13342
- if (this.responseTimeout) {
13343
- clearTimeout(this.responseTimeout);
13344
- this.responseTimeout = null;
13345
- }
13346
- if (this.idleTimeout) {
13347
- clearTimeout(this.idleTimeout);
13348
- this.idleTimeout = null;
13349
- }
13350
- if (this.pendingScriptStatusTimer) {
13351
- clearTimeout(this.pendingScriptStatusTimer);
13352
- this.pendingScriptStatusTimer = null;
13353
- }
13354
- if (this.pendingOutputParseTimer) {
13355
- clearTimeout(this.pendingOutputParseTimer);
13356
- this.pendingOutputParseTimer = null;
13357
- }
13295
+ this.clearAllTimers();
13358
13296
  this.pendingOutputParseBuffer = "";
13359
13297
  this.pendingTerminalQueryTail = "";
13360
- if (this.ptyOutputFlushTimer) {
13361
- clearTimeout(this.ptyOutputFlushTimer);
13362
- this.ptyOutputFlushTimer = null;
13363
- }
13364
13298
  this.ptyOutputBuffer = "";
13365
13299
  this.finishRetryCount = 0;
13366
13300
  if (this.ptyProcess) {
@@ -13381,44 +13315,9 @@ ${data.message || ""}`.trim();
13381
13315
  }
13382
13316
  detach() {
13383
13317
  this.clearIdleFinishCandidate("detach");
13384
- if (this.settleTimer) {
13385
- clearTimeout(this.settleTimer);
13386
- this.settleTimer = null;
13387
- }
13388
- if (this.approvalExitTimeout) {
13389
- clearTimeout(this.approvalExitTimeout);
13390
- this.approvalExitTimeout = null;
13391
- }
13392
- if (this.submitRetryTimer) {
13393
- clearTimeout(this.submitRetryTimer);
13394
- this.submitRetryTimer = null;
13395
- }
13396
- if (this.finishRetryTimer) {
13397
- clearTimeout(this.finishRetryTimer);
13398
- this.finishRetryTimer = null;
13399
- }
13400
- if (this.responseTimeout) {
13401
- clearTimeout(this.responseTimeout);
13402
- this.responseTimeout = null;
13403
- }
13404
- if (this.idleTimeout) {
13405
- clearTimeout(this.idleTimeout);
13406
- this.idleTimeout = null;
13407
- }
13408
- if (this.pendingScriptStatusTimer) {
13409
- clearTimeout(this.pendingScriptStatusTimer);
13410
- this.pendingScriptStatusTimer = null;
13411
- }
13412
- if (this.pendingOutputParseTimer) {
13413
- clearTimeout(this.pendingOutputParseTimer);
13414
- this.pendingOutputParseTimer = null;
13415
- }
13318
+ this.clearAllTimers();
13416
13319
  this.pendingOutputParseBuffer = "";
13417
13320
  this.pendingTerminalQueryTail = "";
13418
- if (this.ptyOutputFlushTimer) {
13419
- clearTimeout(this.ptyOutputFlushTimer);
13420
- this.ptyOutputFlushTimer = null;
13421
- }
13422
13321
  this.ptyOutputBuffer = "";
13423
13322
  this.finishRetryCount = 0;
13424
13323
  if (this.ptyProcess) {
@@ -13480,8 +13379,7 @@ ${data.message || ""}`.trim();
13480
13379
  this.ptyProcess?.write(data);
13481
13380
  }
13482
13381
  resolveModal(buttonIndex) {
13483
- const screenText = this.terminalScreen.getText() || "";
13484
- let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
13382
+ let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
13485
13383
  if (!modal && typeof this.cliScripts?.parseOutput === "function") {
13486
13384
  try {
13487
13385
  const parsed = this.getScriptParsedStatus();
@@ -13512,12 +13410,7 @@ ${data.message || ""}`.trim();
13512
13410
  }
13513
13411
  this.setStatus("generating", "approval_resolved");
13514
13412
  this.onStatusChange?.();
13515
- const startupTrustModal = /Quick safety check|project trust|Confirm Claude Code project trust|trust (?:this project|the contents of this directory|the files in this folder)/i.test(String(modal?.message || ""));
13516
- if (startupTrustModal && buttonIndex in this.approvalKeys) {
13517
- this.ptyProcess.write(`${this.approvalKeys[buttonIndex]}\r`);
13518
- } else if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
13519
- this.ptyProcess.write("\r");
13520
- } else if (buttonIndex in this.approvalKeys) {
13413
+ if (buttonIndex in this.approvalKeys) {
13521
13414
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
13522
13415
  } else {
13523
13416
  const DOWN = "\x1B[B";
@@ -13537,7 +13430,7 @@ ${data.message || ""}`.trim();
13537
13430
  }
13538
13431
  getDebugState() {
13539
13432
  const screenText = sanitizeTerminalText(this.terminalScreen.getText());
13540
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
13433
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
13541
13434
  const effectiveStatus = this.projectEffectiveStatus(startupModal);
13542
13435
  const effectiveReady = this.ready || !!startupModal;
13543
13436
  return {
@@ -13759,6 +13652,13 @@ var init_cli_provider_instance = __esm({
13759
13652
  runtimeMessages = [];
13760
13653
  lastPersistedHistoryMessages = [];
13761
13654
  lastCanonicalHermesSyncMtimeMs = 0;
13655
+ lastCanonicalHermesExistCheckAt = 0;
13656
+ lastCanonicalHermesWatchPath = void 0;
13657
+ lastCanonicalClaudeRebuildMtimeMs = 0;
13658
+ lastCanonicalClaudeCheckAt = 0;
13659
+ cachedSqliteDb = null;
13660
+ cachedSqliteDbPath = null;
13661
+ cachedSqliteDbMissingUntil = 0;
13762
13662
  instanceId;
13763
13663
  suppressIdleHistoryReplay = false;
13764
13664
  errorMessage = void 0;
@@ -13823,7 +13723,12 @@ var init_cli_provider_instance = __esm({
13823
13723
  */
13824
13724
  probeSessionIdFromConfig(probe) {
13825
13725
  const resolvedDbPath = probe.dbPath.replace(/^~/, os13.homedir());
13826
- if (!fs5.existsSync(resolvedDbPath)) return null;
13726
+ const now = Date.now();
13727
+ if (this.cachedSqliteDbMissingUntil > now) return null;
13728
+ if (!fs5.existsSync(resolvedDbPath)) {
13729
+ this.cachedSqliteDbMissingUntil = now + 1e4;
13730
+ return null;
13731
+ }
13827
13732
  const directories = this.getProbeDirectories();
13828
13733
  const minCreatedAt = Math.max(0, this.startedAt - 6e4);
13829
13734
  const tsFormat = probe.timestampFormat || "unix_ms";
@@ -13994,6 +13899,12 @@ var init_cli_provider_instance = __esm({
13994
13899
  this.adapter.shutdown();
13995
13900
  this.monitor.reset();
13996
13901
  this.appliedEffectKeys.clear();
13902
+ try {
13903
+ this.cachedSqliteDb?.close();
13904
+ } catch {
13905
+ }
13906
+ this.cachedSqliteDb = null;
13907
+ this.cachedSqliteDbPath = null;
13997
13908
  }
13998
13909
  completedDebounceTimer = null;
13999
13910
  completedDebouncePending = null;
@@ -14398,13 +14309,35 @@ ${effect.notification.body || ""}`.trim();
14398
14309
  let rebuilt = false;
14399
14310
  if (canonicalHistory.format === "hermes-json") {
14400
14311
  const watchPath = canonicalHistory.watchPath.replace(/^~/, os13.homedir()).replace("{{sessionId}}", this.providerSessionId);
14401
- if (!fs5.existsSync(watchPath)) return false;
14312
+ const now = Date.now();
14313
+ if (watchPath !== this.lastCanonicalHermesWatchPath || now - this.lastCanonicalHermesExistCheckAt >= 2e3) {
14314
+ this.lastCanonicalHermesWatchPath = watchPath;
14315
+ this.lastCanonicalHermesExistCheckAt = now;
14316
+ if (!fs5.existsSync(watchPath)) return false;
14317
+ } else if (this.lastCanonicalHermesSyncMtimeMs === 0) {
14318
+ if (!fs5.existsSync(watchPath)) return false;
14319
+ }
14402
14320
  const stat4 = fs5.statSync(watchPath);
14403
14321
  if (stat4.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
14404
14322
  rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
14405
14323
  if (rebuilt) this.lastCanonicalHermesSyncMtimeMs = stat4.mtimeMs;
14406
14324
  } else if (canonicalHistory.format === "claude-jsonl") {
14325
+ const now = Date.now();
14326
+ if (now - this.lastCanonicalClaudeCheckAt < 2e3 && this.lastCanonicalClaudeRebuildMtimeMs !== 0) {
14327
+ return true;
14328
+ }
14329
+ this.lastCanonicalClaudeCheckAt = now;
14330
+ const claudeProjectsDir = path11.join(os13.homedir(), ".claude", "projects");
14331
+ const workspaceSegment = typeof this.workingDir === "string" ? this.workingDir.replace(/[\\/]/g, "-").replace(/^-+/, "") : "";
14332
+ const transcriptFile = path11.join(claudeProjectsDir, workspaceSegment, `${this.providerSessionId}.jsonl`);
14333
+ let transcriptMtime = 0;
14334
+ try {
14335
+ transcriptMtime = fs5.statSync(transcriptFile).mtimeMs;
14336
+ } catch {
14337
+ }
14338
+ if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalClaudeRebuildMtimeMs) return true;
14407
14339
  rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
14340
+ if (rebuilt) this.lastCanonicalClaudeRebuildMtimeMs = transcriptMtime || Date.now();
14408
14341
  }
14409
14342
  if (!rebuilt) return false;
14410
14343
  const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
@@ -14469,20 +14402,29 @@ ${effect.notification.body || ""}`.trim();
14469
14402
  return Array.from({ length: count }, () => "?").join(", ");
14470
14403
  }
14471
14404
  querySqliteText(dbPath, query, params) {
14472
- let db = null;
14473
14405
  try {
14474
- const DatabaseSync = getDatabaseSync();
14475
- db = new DatabaseSync(dbPath, { readOnly: true });
14476
- const row = db.prepare(query).get(...params);
14406
+ if (this.cachedSqliteDb === null || this.cachedSqliteDbPath !== dbPath) {
14407
+ try {
14408
+ this.cachedSqliteDb?.close();
14409
+ } catch {
14410
+ }
14411
+ this.cachedSqliteDb = null;
14412
+ this.cachedSqliteDbPath = null;
14413
+ const DatabaseSync = getDatabaseSync();
14414
+ this.cachedSqliteDb = new DatabaseSync(dbPath, { readOnly: true });
14415
+ this.cachedSqliteDbPath = dbPath;
14416
+ }
14417
+ const row = this.cachedSqliteDb.prepare(query).get(...params);
14477
14418
  const sessionId = typeof row?.id === "string" ? row.id.trim() : "";
14478
14419
  return sessionId || null;
14479
14420
  } catch {
14480
- return null;
14481
- } finally {
14482
14421
  try {
14483
- db?.close();
14422
+ this.cachedSqliteDb?.close();
14484
14423
  } catch {
14485
14424
  }
14425
+ this.cachedSqliteDb = null;
14426
+ this.cachedSqliteDbPath = null;
14427
+ return null;
14486
14428
  }
14487
14429
  }
14488
14430
  };
@@ -34649,6 +34591,7 @@ var init_provider_schema = __esm({
34649
34591
  "canonicalHistory",
34650
34592
  "autoFixProfile",
34651
34593
  "ideLevelScripts",
34594
+ "allowInputDuringGeneration",
34652
34595
  "scripts",
34653
34596
  "vscodeCommands",
34654
34597
  "inputMethod",
@@ -55410,7 +55353,7 @@ var init_adhdev_daemon = __esm({
55410
55353
  init_version();
55411
55354
  init_src();
55412
55355
  init_runtime_defaults();
55413
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.5" });
55356
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.8" });
55414
55357
  AdhdevDaemon = class _AdhdevDaemon {
55415
55358
  localHttpServer = null;
55416
55359
  localWss = null;