adhdev 0.9.6 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -5813,6 +5813,18 @@ function validateRole(role, source, index) {
5813
5813
  }
5814
5814
  return role;
5815
5815
  }
5816
+ function validateBubbleState(state, source, index) {
5817
+ if (typeof state !== "string" || !VALID_BUBBLE_STATES.includes(state)) {
5818
+ throw new Error(`${source}: messages[${index}].bubbleState must be one of ${VALID_BUBBLE_STATES.join(", ")}`);
5819
+ }
5820
+ return state;
5821
+ }
5822
+ function validateTurnStatus(turnStatus, source) {
5823
+ if (typeof turnStatus !== "string" || !VALID_TURN_STATUSES.includes(turnStatus)) {
5824
+ throw new Error(`${source}: turnStatus must be one of ${VALID_TURN_STATUSES.join(", ")}`);
5825
+ }
5826
+ return turnStatus;
5827
+ }
5816
5828
  function validateMessageContent(content, source, index) {
5817
5829
  if (typeof content === "string") return content;
5818
5830
  if (Array.isArray(content)) return normalizeMessageParts(content);
@@ -5828,6 +5840,9 @@ function validateMessage(message, source, index) {
5828
5840
  };
5829
5841
  if (typeof message.kind === "string") normalized.kind = message.kind;
5830
5842
  if (typeof message.id === "string") normalized.id = message.id;
5843
+ if (typeof message.bubbleId === "string") normalized.bubbleId = message.bubbleId;
5844
+ if (typeof message.providerUnitKey === "string") normalized.providerUnitKey = message.providerUnitKey;
5845
+ if (message.bubbleState !== void 0) normalized.bubbleState = validateBubbleState(message.bubbleState, source, index);
5831
5846
  if (isFiniteNumber(message.index)) normalized.index = message.index;
5832
5847
  if (isFiniteNumber(message.timestamp)) normalized.timestamp = message.timestamp;
5833
5848
  if (isFiniteNumber(message.receivedAt)) normalized.receivedAt = message.receivedAt;
@@ -5895,6 +5910,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
5895
5910
  if (activeModal !== void 0) normalized.activeModal = activeModal;
5896
5911
  if (typeof raw.id === "string") normalized.id = raw.id;
5897
5912
  if (typeof raw.title === "string") normalized.title = raw.title;
5913
+ if (typeof raw.currentTurnId === "string") normalized.currentTurnId = raw.currentTurnId;
5914
+ if (raw.turnStatus !== void 0) normalized.turnStatus = validateTurnStatus(raw.turnStatus, source);
5898
5915
  if (typeof raw.agentType === "string") normalized.agentType = raw.agentType;
5899
5916
  if (typeof raw.agentName === "string") normalized.agentName = raw.agentName;
5900
5917
  if (typeof raw.extensionId === "string") normalized.extensionId = raw.extensionId;
@@ -5907,13 +5924,15 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
5907
5924
  if (typeof raw.providerSessionId === "string") normalized.providerSessionId = raw.providerSessionId;
5908
5925
  return normalized;
5909
5926
  }
5910
- var VALID_STATUSES, VALID_ROLES;
5927
+ var VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES;
5911
5928
  var init_read_chat_contract = __esm({
5912
5929
  "../../oss/packages/daemon-core/src/providers/read-chat-contract.ts"() {
5913
5930
  "use strict";
5914
5931
  init_contracts();
5915
5932
  VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "streaming", "long_generating"];
5916
5933
  VALID_ROLES = ["user", "assistant", "system", "human"];
5934
+ VALID_BUBBLE_STATES = ["draft", "streaming", "final", "removed"];
5935
+ VALID_TURN_STATUSES = ["open", "waiting_approval", "complete", "error"];
5917
5936
  }
5918
5937
  });
5919
5938
 
@@ -8825,25 +8844,23 @@ async function handleResolveAction(h, args) {
8825
8844
  const effectiveModal = statusModal || surfacedModal;
8826
8845
  const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" ? "waiting_approval" : status?.status;
8827
8846
  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"}`);
8828
- if (effectiveStatus !== "waiting_approval" && !effectiveModal) {
8847
+ if (!effectiveModal) {
8829
8848
  return { success: false, error: "Not in approval state" };
8830
8849
  }
8831
- const buttons = effectiveModal?.buttons || ["Allow once", "Always allow", "Deny"];
8850
+ const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
8832
8851
  let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
8833
- if (buttonIndex < 0) {
8852
+ if (buttonIndex < 0 && button) {
8834
8853
  const btnLower = button.toLowerCase();
8835
8854
  buttonIndex = buttons.findIndex((b) => b.toLowerCase().includes(btnLower));
8836
8855
  }
8856
+ if (buttonIndex < 0 && (action === "reject" || action === "deny")) {
8857
+ buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
8858
+ }
8859
+ if (buttonIndex < 0 && (action === "always" || /always/i.test(button))) {
8860
+ buttonIndex = buttons.findIndex((b) => /always/i.test(b));
8861
+ }
8837
8862
  if (buttonIndex < 0) {
8838
- if (action === "reject" || action === "deny") {
8839
- buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
8840
- if (buttonIndex < 0) buttonIndex = buttons.length - 1;
8841
- } else if (action === "always" || /always/i.test(button)) {
8842
- buttonIndex = buttons.findIndex((b) => /always/i.test(b));
8843
- if (buttonIndex < 0) buttonIndex = 1;
8844
- } else {
8845
- buttonIndex = 0;
8846
- }
8863
+ return { success: false, error: "Approval action did not match any visible button" };
8847
8864
  }
8848
8865
  if (typeof adapter.resolveModal === "function") {
8849
8866
  adapter.resolveModal(buttonIndex);
@@ -12088,8 +12105,35 @@ function promptLikelyVisible(screenText, promptSnippet) {
12088
12105
  function normalizeScreenSnapshot(text) {
12089
12106
  return sanitizeTerminalText(String(text || "")).replace(/\s+/g, " ").trim();
12090
12107
  }
12108
+ function shouldReflowComparableMessageLines(lines) {
12109
+ 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));
12110
+ }
12111
+ function joinComparableMessageLines(lines) {
12112
+ return lines.reduce((acc, line) => {
12113
+ const next = String(line || "").trim();
12114
+ if (!next) return acc;
12115
+ if (!acc) return next;
12116
+ if (/[,\d]$/.test(acc) && /^\d/.test(next)) {
12117
+ return `${acc}${next}`;
12118
+ }
12119
+ if (/[A-Za-z]$/.test(acc) && /^\d/.test(next)) {
12120
+ return `${acc}${next}`;
12121
+ }
12122
+ const fragmentMatch = acc.match(/([A-Za-z]{1,4})$/);
12123
+ const fragment = fragmentMatch ? fragmentMatch[1].toLowerCase() : "";
12124
+ if (/^[a-z]/.test(next) && fragment && !COMMON_COMPARABLE_WRAP_WORDS.has(fragment)) {
12125
+ return `${acc}${next}`;
12126
+ }
12127
+ return `${acc} ${next}`;
12128
+ }, "").replace(/\s+([,.;:!?])/g, "$1").replace(/(\d)\s+,/g, "$1,").replace(/\s+/g, " ").trim();
12129
+ }
12091
12130
  function normalizeComparableMessageContent(text) {
12092
- return String(text || "").replace(/\s+/g, " ").trim();
12131
+ const lines = String(text || "").split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
12132
+ if (lines.length === 0) return "";
12133
+ if (shouldReflowComparableMessageLines(lines)) {
12134
+ return joinComparableMessageLines(lines);
12135
+ }
12136
+ return lines.join(" ").replace(/\s+/g, " ").trim();
12093
12137
  }
12094
12138
  function trimPromptEchoPrefix(text, promptText) {
12095
12139
  const prompt2 = normalizeComparableMessageContent(String(promptText || ""));
@@ -12122,9 +12166,6 @@ function getLastUserPromptText(messages) {
12122
12166
  }
12123
12167
  return "";
12124
12168
  }
12125
- function looksLikeConfirmOnlyLabel(label) {
12126
- return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
12127
- }
12128
12169
  function parsePatternEntry(x) {
12129
12170
  if (x instanceof RegExp) return x;
12130
12171
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -12151,7 +12192,7 @@ function normalizeCliProviderForRuntime(raw) {
12151
12192
  }
12152
12193
  };
12153
12194
  }
12154
- var os11, path10, import_child_process4, buildCliSpawnEnv;
12195
+ var os11, path10, import_child_process4, buildCliSpawnEnv, COMMON_COMPARABLE_WRAP_WORDS;
12155
12196
  var init_provider_cli_shared = __esm({
12156
12197
  "../../oss/packages/daemon-core/src/cli-adapters/provider-cli-shared.ts"() {
12157
12198
  "use strict";
@@ -12160,6 +12201,32 @@ var init_provider_cli_shared = __esm({
12160
12201
  import_child_process4 = require("child_process");
12161
12202
  init_spawn_env();
12162
12203
  buildCliSpawnEnv = sanitizeSpawnEnv;
12204
+ COMMON_COMPARABLE_WRAP_WORDS = /* @__PURE__ */ new Set([
12205
+ "a",
12206
+ "an",
12207
+ "and",
12208
+ "as",
12209
+ "at",
12210
+ "but",
12211
+ "by",
12212
+ "for",
12213
+ "from",
12214
+ "in",
12215
+ "into",
12216
+ "is",
12217
+ "it",
12218
+ "of",
12219
+ "on",
12220
+ "or",
12221
+ "that",
12222
+ "the",
12223
+ "their",
12224
+ "then",
12225
+ "this",
12226
+ "to",
12227
+ "was",
12228
+ "with"
12229
+ ]);
12163
12230
  }
12164
12231
  });
12165
12232
 
@@ -12215,8 +12282,44 @@ function hydrateCliParsedMessages(parsedMessages, options) {
12215
12282
  };
12216
12283
  });
12217
12284
  }
12285
+ function chooseMoreComparableCliMessage(left2, right2) {
12286
+ const leftComparable = normalizeComparableMessageContent(left2.content || "");
12287
+ const rightComparable = normalizeComparableMessageContent(right2.content || "");
12288
+ if (leftComparable && leftComparable === rightComparable) {
12289
+ const leftNewlines = String(left2.content || "").split(/\r\n|\n|\r/g).length - 1;
12290
+ const rightNewlines = String(right2.content || "").split(/\r\n|\n|\r/g).length - 1;
12291
+ return rightNewlines < leftNewlines ? right2 : left2;
12292
+ }
12293
+ return rightComparable.length > leftComparable.length ? right2 : left2;
12294
+ }
12295
+ function dedupeConsecutiveComparableCliMessages(messages) {
12296
+ const deduped = [];
12297
+ for (const message of messages) {
12298
+ const current = {
12299
+ ...message,
12300
+ content: typeof message.content === "string" ? message.content : String(message.content || "")
12301
+ };
12302
+ const previous = deduped[deduped.length - 1];
12303
+ if (!previous) {
12304
+ deduped.push(current);
12305
+ continue;
12306
+ }
12307
+ const previousComparable = normalizeComparableMessageContent(previous.content || "");
12308
+ const currentComparable = normalizeComparableMessageContent(current.content || "");
12309
+ const sameRole = previous.role === current.role;
12310
+ const sameKind = (previous.kind || "standard") === (current.kind || "standard");
12311
+ const sameSender = (previous.senderName || "") === (current.senderName || "");
12312
+ const comparableMatch = previousComparable && previousComparable === currentComparable;
12313
+ if (sameRole && sameKind && sameSender && comparableMatch) {
12314
+ deduped[deduped.length - 1] = chooseMoreComparableCliMessage(previous, current);
12315
+ continue;
12316
+ }
12317
+ deduped.push(current);
12318
+ }
12319
+ return deduped;
12320
+ }
12218
12321
  function normalizeCliParsedMessages(parsedMessages, options) {
12219
- return hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
12322
+ return dedupeConsecutiveComparableCliMessages(hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
12220
12323
  role: message.role,
12221
12324
  content: message.content,
12222
12325
  timestamp: message.timestamp,
@@ -12226,7 +12329,7 @@ function normalizeCliParsedMessages(parsedMessages, options) {
12226
12329
  index: message.index,
12227
12330
  meta: message.meta,
12228
12331
  senderName: message.senderName
12229
- }));
12332
+ })));
12230
12333
  }
12231
12334
  function buildCliParseInput(options) {
12232
12335
  const {
@@ -12889,7 +12992,7 @@ var init_provider_cli_adapter = __esm({
12889
12992
  if (!hasStartupOutput) return;
12890
12993
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
12891
12994
  if (stableMs < 2e3) return;
12892
- const startupModal = this.getStartupConfirmationModal(screenText);
12995
+ const startupModal = this.runParseApproval(this.recentOutputBuffer);
12893
12996
  this.startupParseGate = false;
12894
12997
  if (this.startupSettleTimer) {
12895
12998
  clearTimeout(this.startupSettleTimer);
@@ -12946,11 +13049,17 @@ var init_provider_cli_adapter = __esm({
12946
13049
  if (this.currentStatus !== "waiting_approval") return;
12947
13050
  const tail = this.recentOutputBuffer;
12948
13051
  const screenText = this.terminalScreen.getText() || "";
12949
- const startupModal = this.getStartupConfirmationModal(screenText);
12950
- const modal = this.runParseApproval(tail) || startupModal;
13052
+ const modal = this.runParseApproval(tail);
12951
13053
  const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
12952
13054
  if (stillWaiting) {
12953
- this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
13055
+ if (!modal) {
13056
+ LOG.warn("CLI", `[${this.cliType}] approval timeout check found no actionable modal; keeping approval state fail-closed`);
13057
+ this.activeModal = null;
13058
+ this.onStatusChange?.();
13059
+ this.armApprovalExitTimeout();
13060
+ return;
13061
+ }
13062
+ this.activeModal = modal;
12954
13063
  this.onStatusChange?.();
12955
13064
  this.armApprovalExitTimeout();
12956
13065
  return;
@@ -12962,81 +13071,12 @@ var init_provider_cli_adapter = __esm({
12962
13071
  this.onStatusChange?.();
12963
13072
  }, 6e4);
12964
13073
  }
12965
- looksLikeVisibleIdlePrompt(screenText) {
12966
- const text = String(screenText || "");
12967
- if (!text.trim()) return false;
12968
- 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)) {
12969
- return true;
12970
- }
12971
- 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);
12972
- }
12973
- findLastMatchingLineIndex(lines, predicate) {
12974
- for (let index = lines.length - 1; index >= 0; index -= 1) {
12975
- if (predicate(lines[index])) return index;
12976
- }
12977
- return -1;
12978
- }
12979
- looksLikeClaudeGeneratingLine(line) {
12980
- const trimmed = String(line || "").trim();
12981
- if (!trimmed) return false;
12982
- if (/^⏵⏵\s+accept edits on/i.test(trimmed)) return false;
12983
- if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) return true;
12984
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+\S+.*\b(?:thinking|thought for \d+s?)\b/i.test(trimmed)) return true;
12985
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+[A-Z][A-Za-z-]{3,}ing\b.*(?:…|\.{3})/u.test(trimmed)) return true;
12986
- if (/^[⏺•]\s+(?:Reading|Writing|Editing|Searching|Inspecting|Planning|Analyzing|Synthesizing|Drafting|Running|Listing|Scanning|Matching)\b.*(?:…|\.{3})/i.test(trimmed)) {
12987
- 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);
12988
- }
12989
- return false;
12990
- }
12991
- detectClaudeGeneratingOverride(screenText, tail) {
12992
- if (this.cliType !== "claude-cli") return false;
12993
- const source = sanitizeTerminalText(screenText || tail || "");
12994
- if (!source.trim()) return false;
12995
- const allLines = source.split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
12996
- if (allLines.length === 0) return false;
12997
- const recentLines = allLines.slice(-12);
12998
- const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
12999
- const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
13000
- if (activeRegion.length === 0) return false;
13001
- return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
13002
- }
13003
- refineDetectedStatus(status, tail, screenText) {
13004
- if (this.startupParseGate) {
13005
- return this.getStartupConfirmationModal(screenText || "") ? "waiting_approval" : "starting";
13006
- }
13007
- if (status === "waiting_approval") return status;
13008
- if (this.detectClaudeGeneratingOverride(screenText || "", tail)) return "generating";
13009
- return status;
13010
- }
13011
- looksLikeVisibleAssistantCandidate(screenText) {
13012
- const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
13013
- for (const line of lines) {
13014
- const trimmed = String(line || "").trim();
13015
- if (!trimmed) continue;
13016
- if (/^➜\s+\S+/.test(trimmed)) continue;
13017
- if (/^Update available!/i.test(trimmed)) continue;
13018
- if (/Claude Code v\d/i.test(trimmed)) continue;
13019
- if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
13020
- if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
13021
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
13022
- if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
13023
- const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
13024
- if (!assistantMatch) continue;
13025
- const content = assistantMatch[1].trim();
13026
- if (!content) continue;
13027
- if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
13028
- if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
13029
- return true;
13030
- }
13031
- return false;
13032
- }
13033
13074
  shouldRetryFinishResponse(commitResult) {
13034
13075
  if (!this.currentTurnScope) return false;
13035
13076
  if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
13036
13077
  if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
13037
13078
  if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
13038
- const screenText = this.terminalScreen.getText() || "";
13039
- if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
13079
+ if (this.runDetectStatus(this.recentOutputBuffer) !== "idle") return false;
13040
13080
  const now = Date.now();
13041
13081
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
13042
13082
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
@@ -13060,45 +13100,21 @@ var init_provider_cli_adapter = __esm({
13060
13100
  }
13061
13101
  return false;
13062
13102
  }
13063
- getStartupConfirmationModal(screenText) {
13064
- const text = sanitizeTerminalText(String(screenText || ""));
13065
- if (!text.trim()) return null;
13066
- if (this.cliType === "claude-cli") {
13067
- 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);
13068
- const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
13069
- if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
13070
- return {
13071
- message: "Confirm Claude Code project trust",
13072
- buttons: ["Continue"]
13073
- };
13074
- }
13075
- }
13076
- return null;
13077
- }
13078
- shouldResolveModalWithEnter(modal, buttonIndex) {
13079
- if (!modal || buttonIndex !== 0) return false;
13080
- const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
13081
- if (buttons.length !== 1) return false;
13082
- const buttonLabel = String(buttons[0] || "").trim();
13083
- return looksLikeConfirmOnlyLabel(buttonLabel);
13084
- }
13085
13103
  async waitForInteractivePrompt(maxWaitMs = 5e3) {
13086
13104
  const startedAt = Date.now();
13087
13105
  let loggedWait = false;
13088
13106
  while (Date.now() - startedAt < maxWaitMs) {
13089
13107
  this.resolveStartupState("interactive_wait");
13090
13108
  const screenText = this.terminalScreen.getText() || "";
13091
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
13092
13109
  const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
13093
13110
  const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
13094
13111
  const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
13095
- const startupLikelyActive = /Welcome back|Tips for getting|Recent activity|Claude Code v\d/i.test(screenText);
13096
- const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "generating";
13112
+ const interactiveReady = status === "idle" && stableMs >= 700 && recentlyOutput >= 350;
13097
13113
  if (interactiveReady) {
13098
13114
  if (loggedWait) {
13099
13115
  LOG.info(
13100
13116
  "CLI",
13101
- `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput}, startup=${startupLikelyActive})`
13117
+ `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput})`
13102
13118
  );
13103
13119
  }
13104
13120
  return;
@@ -13107,7 +13123,7 @@ var init_provider_cli_adapter = __esm({
13107
13123
  loggedWait = true;
13108
13124
  LOG.info(
13109
13125
  "CLI",
13110
- `[${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)}`
13126
+ `[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
13111
13127
  );
13112
13128
  }
13113
13129
  await new Promise((resolve18) => setTimeout(resolve18, 50));
@@ -13118,13 +13134,12 @@ var init_provider_cli_adapter = __esm({
13118
13134
  `[${this.cliType}] Interactive prompt wait timed out after ${maxWaitMs}ms; proceeding with screen=${JSON.stringify(summarizeCliTraceText(finalScreenText, 240)).slice(0, 280)}`
13119
13135
  );
13120
13136
  }
13121
- clearStaleIdleResponseGuard(reason) {
13122
- const screenText = this.terminalScreen.getText() || "";
13123
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
13124
- const blockingModal = this.activeModal || this.getStartupConfirmationModal(screenText);
13125
- if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !visibleIdlePrompt || !!blockingModal) {
13126
- return false;
13127
- }
13137
+ trimLastAssistantEcho(messages, prompt2) {
13138
+ if (!prompt2) return;
13139
+ const last = [...messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
13140
+ if (last) last.content = trimPromptEchoPrefix(last.content, prompt2);
13141
+ }
13142
+ clearAllTimers() {
13128
13143
  if (this.responseTimeout) {
13129
13144
  clearTimeout(this.responseTimeout);
13130
13145
  this.responseTimeout = null;
@@ -13137,10 +13152,38 @@ var init_provider_cli_adapter = __esm({
13137
13152
  clearTimeout(this.approvalExitTimeout);
13138
13153
  this.approvalExitTimeout = null;
13139
13154
  }
13155
+ if (this.submitRetryTimer) {
13156
+ clearTimeout(this.submitRetryTimer);
13157
+ this.submitRetryTimer = null;
13158
+ }
13140
13159
  if (this.finishRetryTimer) {
13141
13160
  clearTimeout(this.finishRetryTimer);
13142
13161
  this.finishRetryTimer = null;
13143
13162
  }
13163
+ if (this.settleTimer) {
13164
+ clearTimeout(this.settleTimer);
13165
+ this.settleTimer = null;
13166
+ }
13167
+ if (this.pendingScriptStatusTimer) {
13168
+ clearTimeout(this.pendingScriptStatusTimer);
13169
+ this.pendingScriptStatusTimer = null;
13170
+ }
13171
+ if (this.pendingOutputParseTimer) {
13172
+ clearTimeout(this.pendingOutputParseTimer);
13173
+ this.pendingOutputParseTimer = null;
13174
+ }
13175
+ if (this.ptyOutputFlushTimer) {
13176
+ clearTimeout(this.ptyOutputFlushTimer);
13177
+ this.ptyOutputFlushTimer = null;
13178
+ }
13179
+ }
13180
+ clearStaleIdleResponseGuard(reason) {
13181
+ const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
13182
+ const isIdle = this.runDetectStatus(this.recentOutputBuffer) === "idle";
13183
+ if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !isIdle || !!blockingModal) {
13184
+ return false;
13185
+ }
13186
+ this.clearAllTimers();
13144
13187
  this.clearIdleFinishCandidate(reason);
13145
13188
  this.responseBuffer = "";
13146
13189
  this.isWaitingForResponse = false;
@@ -13150,10 +13193,7 @@ var init_provider_cli_adapter = __esm({
13150
13193
  this.finishRetryCount = 0;
13151
13194
  this.currentTurnScope = null;
13152
13195
  this.activeModal = null;
13153
- this.recordTrace("stale_idle_response_cleared", {
13154
- reason,
13155
- screenText: summarizeCliTraceText(screenText, 240)
13156
- });
13196
+ this.recordTrace("stale_idle_response_cleared", { reason });
13157
13197
  return true;
13158
13198
  }
13159
13199
  hasMeaningfulResponseBuffer(promptSnippet) {
@@ -13188,16 +13228,15 @@ var init_provider_cli_adapter = __esm({
13188
13228
  if (this.startupParseGate) {
13189
13229
  return;
13190
13230
  }
13191
- const startupModal = this.getStartupConfirmationModal(screenText);
13192
13231
  const parsedTranscript = this.parseCurrentTranscript(
13193
13232
  this.committedMessages,
13194
13233
  this.responseBuffer,
13195
13234
  this.currentTurnScope
13196
13235
  );
13197
13236
  const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsedTranscript.activeModal : null;
13198
- const modal = this.runParseApproval(tail) || parsedModal || startupModal;
13237
+ const modal = this.runParseApproval(tail) || parsedModal;
13199
13238
  const rawScriptStatus = this.runDetectStatus(tail);
13200
- const scriptStatus = startupModal ? "waiting_approval" : parsedModal && parsedTranscript?.status === "waiting_approval" ? "waiting_approval" : rawScriptStatus;
13239
+ const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
13201
13240
  const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
13202
13241
  committedMessages: this.committedMessages,
13203
13242
  scope: this.currentTurnScope,
@@ -13252,15 +13291,44 @@ var init_provider_cli_adapter = __esm({
13252
13291
  }
13253
13292
  if (!scriptStatus) return;
13254
13293
  const prevStatus = this.currentStatus;
13255
- const clearPendingScriptStatus = () => {
13294
+ const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
13295
+ if (!this.applyPendingScriptStatusDebounce(ctx)) return;
13296
+ const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
13297
+ LOG.info(
13298
+ "CLI",
13299
+ `[${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)}`
13300
+ );
13301
+ const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
13302
+ if (shouldHoldGenerating) {
13303
+ this.applyHoldGenerating(ctx, recentInteractiveActivity);
13304
+ return;
13305
+ }
13306
+ if (scriptStatus === "waiting_approval") {
13307
+ this.applyWaitingApproval(ctx);
13308
+ return;
13309
+ }
13310
+ if (scriptStatus === "generating") {
13311
+ this.applyGenerating(ctx);
13312
+ return;
13313
+ }
13314
+ if (scriptStatus === "idle") {
13315
+ this.applyIdle(ctx, now);
13316
+ }
13317
+ }
13318
+ // Returns false if the caller should bail out (debounce pending).
13319
+ applyPendingScriptStatusDebounce(ctx) {
13320
+ const { now, scriptStatus, prevStatus } = ctx;
13321
+ const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
13322
+ if (!shouldDebounce) {
13256
13323
  this.pendingScriptStatus = null;
13257
13324
  this.pendingScriptStatusSince = 0;
13258
13325
  if (this.pendingScriptStatusTimer) {
13259
13326
  clearTimeout(this.pendingScriptStatusTimer);
13260
13327
  this.pendingScriptStatusTimer = null;
13261
13328
  }
13262
- };
13263
- const armPendingScriptStatus = (delayMs) => {
13329
+ return true;
13330
+ }
13331
+ const armPending = (delayMs) => {
13264
13332
  if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
13265
13333
  this.pendingScriptStatusTimer = setTimeout(() => {
13266
13334
  this.pendingScriptStatusTimer = null;
@@ -13268,200 +13336,187 @@ var init_provider_cli_adapter = __esm({
13268
13336
  this.evaluateSettled();
13269
13337
  }, delayMs);
13270
13338
  };
13271
- const shouldDebouncePromotion = (status) => prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
13272
- if (shouldDebouncePromotion(scriptStatus)) {
13273
- if (this.pendingScriptStatus !== scriptStatus) {
13274
- this.pendingScriptStatus = scriptStatus;
13275
- this.pendingScriptStatusSince = now;
13276
- armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
13277
- return;
13278
- }
13279
- const elapsed = now - this.pendingScriptStatusSince;
13280
- if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
13281
- armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
13282
- return;
13283
- }
13284
- } else {
13285
- clearPendingScriptStatus();
13286
- }
13287
- const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
13288
- const statusActivityHoldMs = this.getStatusActivityHoldMs();
13289
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
13290
- const visibleAssistantCandidate = this.looksLikeVisibleAssistantCandidate(screenText);
13291
- if (this.currentTurnScope && this.cliType === "claude-cli") {
13292
- LOG.info(
13293
- "CLI",
13294
- `[${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)}`
13295
- );
13339
+ if (this.pendingScriptStatus !== scriptStatus) {
13340
+ this.pendingScriptStatus = scriptStatus;
13341
+ this.pendingScriptStatusSince = now;
13342
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
13343
+ return false;
13296
13344
  }
13297
- const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(visibleIdlePrompt && visibleAssistantCandidate) && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
13298
- if (shouldHoldGenerating) {
13299
- this.clearIdleFinishCandidate("hold_generating_recent_activity");
13300
- this.setStatus("generating", "recent_activity_hold");
13301
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
13302
- this.idleTimeout = setTimeout(() => {
13303
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
13304
- if (this.shouldDeferIdleTimeoutFinish()) return;
13305
- this.finishResponse();
13306
- }
13307
- }, this.timeouts.generatingIdle);
13308
- this.recordTrace("hold_generating_recent_activity", {
13309
- scriptStatus,
13310
- recentInteractiveActivity,
13311
- lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
13312
- lastScreenChangeAt: this.lastScreenChangeAt,
13313
- holdMs: statusActivityHoldMs,
13314
- ...buildCliTraceParseSnapshot({
13315
- accumulatedBuffer: this.accumulatedBuffer,
13316
- accumulatedRawBuffer: this.accumulatedRawBuffer,
13317
- responseBuffer: this.responseBuffer,
13318
- partialResponse: this.responseBuffer,
13319
- scope: this.currentTurnScope
13320
- })
13321
- });
13322
- this.onStatusChange?.();
13323
- return;
13345
+ const elapsed = now - this.pendingScriptStatusSince;
13346
+ if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
13347
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
13348
+ return false;
13324
13349
  }
13325
- if (scriptStatus === "waiting_approval") {
13326
- this.clearIdleFinishCandidate("waiting_approval");
13327
- const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
13328
- const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
13329
- if ((inCooldown || visibleIdlePrompt2) && !modal) {
13330
- if (this.approvalExitTimeout) {
13331
- clearTimeout(this.approvalExitTimeout);
13332
- this.approvalExitTimeout = null;
13333
- }
13334
- this.activeModal = null;
13335
- if (this.isWaitingForResponse) {
13336
- this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
13337
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
13338
- this.idleTimeout = setTimeout(() => {
13339
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
13340
- if (this.shouldDeferIdleTimeoutFinish()) return;
13341
- this.finishResponse();
13342
- }
13343
- }, this.timeouts.generatingIdle);
13344
- } else {
13345
- this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
13346
- }
13347
- this.onStatusChange?.();
13348
- return;
13350
+ return true;
13351
+ }
13352
+ applyHoldGenerating(ctx, recentInteractiveActivity) {
13353
+ const { scriptStatus } = ctx;
13354
+ this.clearIdleFinishCandidate("hold_generating_recent_activity");
13355
+ this.setStatus("generating", "recent_activity_hold");
13356
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
13357
+ this.idleTimeout = setTimeout(() => {
13358
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
13359
+ if (this.shouldDeferIdleTimeoutFinish()) return;
13360
+ this.finishResponse();
13361
+ }
13362
+ }, this.timeouts.generatingIdle);
13363
+ this.recordTrace("hold_generating_recent_activity", {
13364
+ scriptStatus,
13365
+ recentInteractiveActivity,
13366
+ lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
13367
+ lastScreenChangeAt: this.lastScreenChangeAt,
13368
+ holdMs: this.getStatusActivityHoldMs(),
13369
+ ...buildCliTraceParseSnapshot({
13370
+ accumulatedBuffer: this.accumulatedBuffer,
13371
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
13372
+ responseBuffer: this.responseBuffer,
13373
+ partialResponse: this.responseBuffer,
13374
+ scope: this.currentTurnScope
13375
+ })
13376
+ });
13377
+ this.onStatusChange?.();
13378
+ }
13379
+ applyWaitingApproval(ctx) {
13380
+ const { modal } = ctx;
13381
+ this.clearIdleFinishCandidate("waiting_approval");
13382
+ const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
13383
+ if (inCooldown && !modal) {
13384
+ if (this.approvalExitTimeout) {
13385
+ clearTimeout(this.approvalExitTimeout);
13386
+ this.approvalExitTimeout = null;
13349
13387
  }
13350
- if (!inCooldown) {
13351
- this.isWaitingForResponse = true;
13352
- this.setStatus("waiting_approval", "script_detect");
13353
- this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
13388
+ this.activeModal = null;
13389
+ if (this.isWaitingForResponse) {
13390
+ this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
13354
13391
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
13355
- this.armApprovalExitTimeout();
13356
- this.onStatusChange?.();
13357
- return;
13392
+ this.idleTimeout = setTimeout(() => {
13393
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
13394
+ if (this.shouldDeferIdleTimeoutFinish()) return;
13395
+ this.finishResponse();
13396
+ }
13397
+ }, this.timeouts.generatingIdle);
13398
+ } else {
13399
+ this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
13358
13400
  }
13401
+ this.onStatusChange?.();
13402
+ return;
13359
13403
  }
13360
- if (scriptStatus === "generating") {
13361
- this.clearIdleFinishCandidate("generating");
13362
- const effectiveScreenText = screenText || this.accumulatedBuffer;
13363
- const noActiveTurn = !this.currentTurnScope;
13364
- 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));
13365
- if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
13404
+ if (!inCooldown) {
13405
+ if (!modal) {
13406
+ LOG.warn("CLI", `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
13366
13407
  return;
13367
13408
  }
13368
- if (prevStatus === "waiting_approval") {
13369
- if (this.approvalExitTimeout) {
13370
- clearTimeout(this.approvalExitTimeout);
13371
- this.approvalExitTimeout = null;
13372
- }
13373
- this.activeModal = null;
13374
- this.lastApprovalResolvedAt = Date.now();
13375
- }
13376
- if (!this.isWaitingForResponse) {
13377
- this.isWaitingForResponse = true;
13378
- this.responseBuffer = "";
13379
- }
13380
- this.setStatus("generating", "script_detect");
13409
+ this.isWaitingForResponse = true;
13410
+ this.setStatus("waiting_approval", "script_detect");
13411
+ this.activeModal = modal;
13381
13412
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
13382
- this.idleTimeout = setTimeout(() => {
13383
- if (this.isWaitingForResponse) {
13384
- if (this.shouldDeferIdleTimeoutFinish()) return;
13385
- this.finishResponse();
13386
- }
13387
- }, this.timeouts.generatingIdle);
13413
+ this.armApprovalExitTimeout();
13388
13414
  this.onStatusChange?.();
13415
+ }
13416
+ }
13417
+ applyGenerating(ctx) {
13418
+ const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
13419
+ this.clearIdleFinishCandidate("generating");
13420
+ const effectiveScreenText = screenText || this.accumulatedBuffer;
13421
+ const noActiveTurn = !this.currentTurnScope;
13422
+ 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));
13423
+ if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
13389
13424
  return;
13390
13425
  }
13391
- if (scriptStatus === "idle") {
13392
- if (prevStatus === "waiting_approval") {
13393
- if (this.approvalExitTimeout) {
13394
- clearTimeout(this.approvalExitTimeout);
13395
- this.approvalExitTimeout = null;
13396
- }
13397
- this.activeModal = null;
13398
- this.lastApprovalResolvedAt = Date.now();
13426
+ if (prevStatus === "waiting_approval") {
13427
+ if (this.approvalExitTimeout) {
13428
+ clearTimeout(this.approvalExitTimeout);
13429
+ this.approvalExitTimeout = null;
13399
13430
  }
13431
+ this.activeModal = null;
13432
+ this.lastApprovalResolvedAt = Date.now();
13433
+ }
13434
+ if (!this.isWaitingForResponse) {
13435
+ this.isWaitingForResponse = true;
13436
+ this.responseBuffer = "";
13437
+ }
13438
+ this.setStatus("generating", "script_detect");
13439
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
13440
+ this.idleTimeout = setTimeout(() => {
13400
13441
  if (this.isWaitingForResponse) {
13401
- const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
13402
- const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
13403
- const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
13404
- const hasAssistantTurn = !!lastParsedAssistant;
13405
- const assistantLength = lastParsedAssistant?.content?.length || 0;
13406
- const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
13407
- const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
13408
- const idleStableThresholdMs = idleFinishConfirmMs;
13409
- const idleReady = visibleIdlePrompt2 && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
13410
- const candidate = this.idleFinishCandidate;
13411
- const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
13412
- const canFinishImmediately = idleReady && candidateQuiet;
13413
- this.recordTrace("idle_decision", {
13414
- visibleIdlePrompt: visibleIdlePrompt2,
13415
- quietForMs,
13416
- screenStableMs,
13417
- hasAssistantTurn,
13418
- assistantLength,
13419
- hasModal: !!modal,
13420
- idleQuietThresholdMs,
13421
- idleStableThresholdMs,
13422
- idleReady,
13423
- idleFinishConfirmMs,
13424
- idleFinishCandidate: candidate,
13425
- candidateQuiet,
13426
- canFinishImmediately,
13427
- submitPendingUntil: this.submitPendingUntil,
13428
- responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
13429
- ...buildCliTraceParseSnapshot({
13430
- accumulatedBuffer: this.accumulatedBuffer,
13431
- accumulatedRawBuffer: this.accumulatedRawBuffer,
13432
- responseBuffer: this.responseBuffer,
13433
- partialResponse: this.responseBuffer,
13434
- scope: this.currentTurnScope
13435
- })
13436
- });
13437
- if (canFinishImmediately) {
13438
- this.clearIdleFinishCandidate("finish_response");
13439
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
13440
- this.finishResponse();
13441
- return;
13442
- }
13443
- if (idleReady) {
13444
- if (!candidate) {
13445
- this.armIdleFinishCandidate(assistantLength);
13446
- return;
13447
- }
13448
- } else {
13449
- this.clearIdleFinishCandidate("idle_not_ready");
13450
- }
13451
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
13452
- this.idleTimeout = setTimeout(() => {
13453
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
13454
- if (this.shouldDeferIdleTimeoutFinish()) return;
13455
- this.clearIdleFinishCandidate("idle_timeout_finish");
13456
- this.finishResponse();
13457
- }
13458
- }, this.timeouts.idleFinish);
13459
- } else if (prevStatus !== "idle") {
13442
+ if (this.shouldDeferIdleTimeoutFinish()) return;
13443
+ this.finishResponse();
13444
+ }
13445
+ }, this.timeouts.generatingIdle);
13446
+ this.onStatusChange?.();
13447
+ }
13448
+ applyIdle(ctx, now) {
13449
+ const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
13450
+ if (prevStatus === "waiting_approval") {
13451
+ if (this.approvalExitTimeout) {
13452
+ clearTimeout(this.approvalExitTimeout);
13453
+ this.approvalExitTimeout = null;
13454
+ }
13455
+ this.activeModal = null;
13456
+ this.lastApprovalResolvedAt = Date.now();
13457
+ }
13458
+ if (!this.isWaitingForResponse) {
13459
+ if (prevStatus !== "idle") {
13460
13460
  this.clearIdleFinishCandidate("idle_without_response");
13461
13461
  this.setStatus("idle", "script_detect");
13462
13462
  this.onStatusChange?.();
13463
13463
  }
13464
+ return;
13464
13465
  }
13466
+ const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
13467
+ const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
13468
+ const hasAssistantTurn = !!lastParsedAssistant;
13469
+ const assistantLength = lastParsedAssistant?.content?.length || 0;
13470
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
13471
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
13472
+ const idleReady = !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleFinishConfirmMs;
13473
+ const candidate = this.idleFinishCandidate;
13474
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
13475
+ this.recordTrace("idle_decision", {
13476
+ quietForMs,
13477
+ screenStableMs,
13478
+ hasAssistantTurn,
13479
+ assistantLength,
13480
+ hasModal: !!modal,
13481
+ idleQuietThresholdMs,
13482
+ idleStableThresholdMs: idleFinishConfirmMs,
13483
+ idleReady,
13484
+ idleFinishConfirmMs,
13485
+ idleFinishCandidate: candidate,
13486
+ candidateQuiet,
13487
+ canFinishImmediately: idleReady && candidateQuiet,
13488
+ submitPendingUntil: this.submitPendingUntil,
13489
+ responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
13490
+ ...buildCliTraceParseSnapshot({
13491
+ accumulatedBuffer: this.accumulatedBuffer,
13492
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
13493
+ responseBuffer: this.responseBuffer,
13494
+ partialResponse: this.responseBuffer,
13495
+ scope: this.currentTurnScope
13496
+ })
13497
+ });
13498
+ if (idleReady && candidateQuiet) {
13499
+ this.clearIdleFinishCandidate("finish_response");
13500
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
13501
+ this.finishResponse();
13502
+ return;
13503
+ }
13504
+ if (idleReady) {
13505
+ if (!candidate) {
13506
+ this.armIdleFinishCandidate(assistantLength);
13507
+ return;
13508
+ }
13509
+ } else {
13510
+ this.clearIdleFinishCandidate("idle_not_ready");
13511
+ }
13512
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
13513
+ this.idleTimeout = setTimeout(() => {
13514
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
13515
+ if (this.shouldDeferIdleTimeoutFinish()) return;
13516
+ this.clearIdleFinishCandidate("idle_timeout_finish");
13517
+ this.finishResponse();
13518
+ }
13519
+ }, this.timeouts.idleFinish);
13465
13520
  }
13466
13521
  finishResponse() {
13467
13522
  if (this.submitPendingUntil > Date.now()) return;
@@ -13500,26 +13555,7 @@ var init_provider_cli_adapter = __esm({
13500
13555
  }, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
13501
13556
  return;
13502
13557
  }
13503
- if (this.responseTimeout) {
13504
- clearTimeout(this.responseTimeout);
13505
- this.responseTimeout = null;
13506
- }
13507
- if (this.idleTimeout) {
13508
- clearTimeout(this.idleTimeout);
13509
- this.idleTimeout = null;
13510
- }
13511
- if (this.approvalExitTimeout) {
13512
- clearTimeout(this.approvalExitTimeout);
13513
- this.approvalExitTimeout = null;
13514
- }
13515
- if (this.submitRetryTimer) {
13516
- clearTimeout(this.submitRetryTimer);
13517
- this.submitRetryTimer = null;
13518
- }
13519
- if (this.finishRetryTimer) {
13520
- clearTimeout(this.finishRetryTimer);
13521
- this.finishRetryTimer = null;
13522
- }
13558
+ this.clearAllTimers();
13523
13559
  this.responseBuffer = "";
13524
13560
  this.isWaitingForResponse = false;
13525
13561
  this.responseSettleIgnoreUntil = 0;
@@ -13531,18 +13567,12 @@ var init_provider_cli_adapter = __esm({
13531
13567
  this.setStatus("idle", "response_finished");
13532
13568
  this.onStatusChange?.();
13533
13569
  }
13534
- maybeCommitVisibleIdleTranscript(parsed, options) {
13570
+ maybeCommitVisibleIdleTranscript(parsed) {
13535
13571
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
13536
13572
  if (!allowImmediateScriptIdleCommit) return false;
13537
13573
  if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
13538
13574
  return false;
13539
13575
  }
13540
- if (options?.requireVisibleAssistantCandidate) {
13541
- const candidateText = options.screenText || this.terminalScreen.getText() || "";
13542
- if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
13543
- return false;
13544
- }
13545
- }
13546
13576
  const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
13547
13577
  committedMessages: this.committedMessages,
13548
13578
  scope: this.currentTurnScope,
@@ -13551,33 +13581,8 @@ var init_provider_cli_adapter = __esm({
13551
13581
  const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
13552
13582
  if (!visibleAssistant) return false;
13553
13583
  this.committedMessages = hydratedForIdleCommit;
13554
- const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
13555
- if (promptForTrim) {
13556
- const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
13557
- if (lastAssistantForTrim) {
13558
- lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
13559
- }
13560
- }
13561
- if (this.responseTimeout) {
13562
- clearTimeout(this.responseTimeout);
13563
- this.responseTimeout = null;
13564
- }
13565
- if (this.idleTimeout) {
13566
- clearTimeout(this.idleTimeout);
13567
- this.idleTimeout = null;
13568
- }
13569
- if (this.approvalExitTimeout) {
13570
- clearTimeout(this.approvalExitTimeout);
13571
- this.approvalExitTimeout = null;
13572
- }
13573
- if (this.submitRetryTimer) {
13574
- clearTimeout(this.submitRetryTimer);
13575
- this.submitRetryTimer = null;
13576
- }
13577
- if (this.finishRetryTimer) {
13578
- clearTimeout(this.finishRetryTimer);
13579
- this.finishRetryTimer = null;
13580
- }
13584
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
13585
+ this.clearAllTimers();
13581
13586
  this.syncMessageViews();
13582
13587
  this.responseBuffer = "";
13583
13588
  this.isWaitingForResponse = false;
@@ -13607,13 +13612,7 @@ var init_provider_cli_adapter = __esm({
13607
13612
  scope: this.currentTurnScope,
13608
13613
  lastOutputAt: this.lastOutputAt
13609
13614
  });
13610
- const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
13611
- if (promptForTrim) {
13612
- const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
13613
- if (lastAssistantForTrim) {
13614
- lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
13615
- }
13616
- }
13615
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
13617
13616
  this.syncMessageViews();
13618
13617
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
13619
13618
  if (this.currentTurnScope) {
@@ -13671,7 +13670,7 @@ var init_provider_cli_adapter = __esm({
13671
13670
  screen: buildCliScreenSnapshot(screenText),
13672
13671
  tailScreen: buildCliScreenSnapshot(text.slice(-500))
13673
13672
  });
13674
- return this.refineDetectedStatus(status, text, screenText || "");
13673
+ return status;
13675
13674
  } catch (e) {
13676
13675
  LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
13677
13676
  return null;
@@ -13711,23 +13710,21 @@ var init_provider_cli_adapter = __esm({
13711
13710
  if (!inApprovalCooldown) {
13712
13711
  return parsed;
13713
13712
  }
13714
- const startupModal = this.getStartupConfirmationModal(screenText || "");
13715
- const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
13713
+ const visibleModal = this.runParseApproval(recentBuffer);
13716
13714
  if (visibleModal) {
13717
13715
  return parsed;
13718
13716
  }
13719
13717
  const detectedStatus = this.runDetectStatus(recentBuffer);
13720
- const fallbackStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
13718
+ const resolvedStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
13721
13719
  return {
13722
13720
  ...parsed,
13723
- status: fallbackStatus,
13721
+ status: resolvedStatus,
13724
13722
  activeModal: null
13725
13723
  };
13726
13724
  }
13727
13725
  // ─── Public API (CliAdapter) ───────────────────
13728
13726
  getStatus() {
13729
- const screenText = this.terminalScreen.getText() || "";
13730
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
13727
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
13731
13728
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
13732
13729
  let effectiveModal = startupModal || this.activeModal;
13733
13730
  if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
@@ -13808,8 +13805,7 @@ var init_provider_cli_adapter = __esm({
13808
13805
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
13809
13806
  }));
13810
13807
  const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === "assistant" && typeof message.content === "string" && message.content.trim());
13811
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
13812
- const shouldAdoptParsedIdleReplay = !this.currentTurnScope && !this.activeModal && !!parsedLastAssistant && parsedTranscriptIsRicherThanCommitted(parsedHydratedMessages, committedHydratedMessages) && (this.currentStatus === "idle" || this.currentStatus === "generating" && this.isWaitingForResponse && parsed.status === "idle" && visibleIdlePrompt);
13808
+ 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");
13813
13809
  if (shouldAdoptParsedIdleReplay) {
13814
13810
  this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
13815
13811
  committedMessages: this.committedMessages,
@@ -13938,17 +13934,9 @@ var init_provider_cli_adapter = __esm({
13938
13934
  if (parsed && typeof parsed === "object") {
13939
13935
  Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
13940
13936
  }
13941
- const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
13942
- if (parsed && refinedStatus && parsed.status !== refinedStatus) {
13943
- parsed.status = refinedStatus;
13944
- }
13945
13937
  const normalizedParsed = this.suppressStaleParsedApproval(parsed, input.recentBuffer, input.screenText);
13946
- const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
13947
- if (normalizedParsed && Array.isArray(normalizedParsed.messages) && promptForTrim) {
13948
- const lastAssistant = [...normalizedParsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
13949
- if (lastAssistant) {
13950
- lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
13951
- }
13938
+ if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
13939
+ this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
13952
13940
  }
13953
13941
  this.parseErrorMessage = null;
13954
13942
  return normalizedParsed;
@@ -13976,16 +13964,11 @@ var init_provider_cli_adapter = __esm({
13976
13964
  LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
13977
13965
  }
13978
13966
  }
13979
- if (!promptText && data) {
13980
- promptText = `Please fix the following issue:
13981
- ${data.title || ""}
13982
- ${data.explanation || ""}
13983
-
13984
- ${data.message || ""}`.trim();
13985
- }
13986
- if (promptText) {
13987
- await this.sendMessage(promptText);
13967
+ if (!promptText) {
13968
+ LOG.warn("CLI", `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
13969
+ return;
13988
13970
  }
13971
+ await this.sendMessage(promptText);
13989
13972
  }
13990
13973
  async sendMessage(text) {
13991
13974
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
@@ -14003,9 +13986,7 @@ ${data.message || ""}`.trim();
14003
13986
  }
14004
13987
  if (!this.ready) {
14005
13988
  this.resolveStartupState("send_precheck");
14006
- const screenText = this.terminalScreen.getText() || "";
14007
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
14008
- if (hasPrompt && this.currentStatus === "idle") {
13989
+ if (this.runDetectStatus(this.recentOutputBuffer) === "idle" && this.currentStatus === "idle") {
14009
13990
  this.ready = true;
14010
13991
  this.startupParseGate = false;
14011
13992
  LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
@@ -14111,7 +14092,10 @@ ${data.message || ""}`.trim();
14111
14092
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
14112
14093
  const screenText2 = this.terminalScreen.getText();
14113
14094
  if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
14114
- 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;
14095
+ const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
14096
+ if (liveApproval) return;
14097
+ const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
14098
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
14115
14099
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
14116
14100
  LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
14117
14101
  this.recordTrace("submit_write", {
@@ -14148,6 +14132,10 @@ ${data.message || ""}`.trim();
14148
14132
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
14149
14133
  const screenText = this.terminalScreen.getText();
14150
14134
  if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
14135
+ const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
14136
+ if (liveApproval) return;
14137
+ const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
14138
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
14151
14139
  LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
14152
14140
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
14153
14141
  this.recordTrace("submit_write", {
@@ -14279,44 +14267,9 @@ ${data.message || ""}`.trim();
14279
14267
  }
14280
14268
  shutdown() {
14281
14269
  this.clearIdleFinishCandidate("shutdown");
14282
- if (this.settleTimer) {
14283
- clearTimeout(this.settleTimer);
14284
- this.settleTimer = null;
14285
- }
14286
- if (this.approvalExitTimeout) {
14287
- clearTimeout(this.approvalExitTimeout);
14288
- this.approvalExitTimeout = null;
14289
- }
14290
- if (this.submitRetryTimer) {
14291
- clearTimeout(this.submitRetryTimer);
14292
- this.submitRetryTimer = null;
14293
- }
14294
- if (this.finishRetryTimer) {
14295
- clearTimeout(this.finishRetryTimer);
14296
- this.finishRetryTimer = null;
14297
- }
14298
- if (this.responseTimeout) {
14299
- clearTimeout(this.responseTimeout);
14300
- this.responseTimeout = null;
14301
- }
14302
- if (this.idleTimeout) {
14303
- clearTimeout(this.idleTimeout);
14304
- this.idleTimeout = null;
14305
- }
14306
- if (this.pendingScriptStatusTimer) {
14307
- clearTimeout(this.pendingScriptStatusTimer);
14308
- this.pendingScriptStatusTimer = null;
14309
- }
14310
- if (this.pendingOutputParseTimer) {
14311
- clearTimeout(this.pendingOutputParseTimer);
14312
- this.pendingOutputParseTimer = null;
14313
- }
14270
+ this.clearAllTimers();
14314
14271
  this.pendingOutputParseBuffer = "";
14315
14272
  this.pendingTerminalQueryTail = "";
14316
- if (this.ptyOutputFlushTimer) {
14317
- clearTimeout(this.ptyOutputFlushTimer);
14318
- this.ptyOutputFlushTimer = null;
14319
- }
14320
14273
  this.ptyOutputBuffer = "";
14321
14274
  this.finishRetryCount = 0;
14322
14275
  if (this.ptyProcess) {
@@ -14337,44 +14290,9 @@ ${data.message || ""}`.trim();
14337
14290
  }
14338
14291
  detach() {
14339
14292
  this.clearIdleFinishCandidate("detach");
14340
- if (this.settleTimer) {
14341
- clearTimeout(this.settleTimer);
14342
- this.settleTimer = null;
14343
- }
14344
- if (this.approvalExitTimeout) {
14345
- clearTimeout(this.approvalExitTimeout);
14346
- this.approvalExitTimeout = null;
14347
- }
14348
- if (this.submitRetryTimer) {
14349
- clearTimeout(this.submitRetryTimer);
14350
- this.submitRetryTimer = null;
14351
- }
14352
- if (this.finishRetryTimer) {
14353
- clearTimeout(this.finishRetryTimer);
14354
- this.finishRetryTimer = null;
14355
- }
14356
- if (this.responseTimeout) {
14357
- clearTimeout(this.responseTimeout);
14358
- this.responseTimeout = null;
14359
- }
14360
- if (this.idleTimeout) {
14361
- clearTimeout(this.idleTimeout);
14362
- this.idleTimeout = null;
14363
- }
14364
- if (this.pendingScriptStatusTimer) {
14365
- clearTimeout(this.pendingScriptStatusTimer);
14366
- this.pendingScriptStatusTimer = null;
14367
- }
14368
- if (this.pendingOutputParseTimer) {
14369
- clearTimeout(this.pendingOutputParseTimer);
14370
- this.pendingOutputParseTimer = null;
14371
- }
14293
+ this.clearAllTimers();
14372
14294
  this.pendingOutputParseBuffer = "";
14373
14295
  this.pendingTerminalQueryTail = "";
14374
- if (this.ptyOutputFlushTimer) {
14375
- clearTimeout(this.ptyOutputFlushTimer);
14376
- this.ptyOutputFlushTimer = null;
14377
- }
14378
14296
  this.ptyOutputBuffer = "";
14379
14297
  this.finishRetryCount = 0;
14380
14298
  if (this.ptyProcess) {
@@ -14436,8 +14354,7 @@ ${data.message || ""}`.trim();
14436
14354
  this.ptyProcess?.write(data);
14437
14355
  }
14438
14356
  resolveModal(buttonIndex) {
14439
- const screenText = this.terminalScreen.getText() || "";
14440
- let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
14357
+ let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
14441
14358
  if (!modal && typeof this.cliScripts?.parseOutput === "function") {
14442
14359
  try {
14443
14360
  const parsed = this.getScriptParsedStatus();
@@ -14468,12 +14385,7 @@ ${data.message || ""}`.trim();
14468
14385
  }
14469
14386
  this.setStatus("generating", "approval_resolved");
14470
14387
  this.onStatusChange?.();
14471
- 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 || ""));
14472
- if (startupTrustModal && buttonIndex in this.approvalKeys) {
14473
- this.ptyProcess.write(`${this.approvalKeys[buttonIndex]}\r`);
14474
- } else if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
14475
- this.ptyProcess.write("\r");
14476
- } else if (buttonIndex in this.approvalKeys) {
14388
+ if (buttonIndex in this.approvalKeys) {
14477
14389
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
14478
14390
  } else {
14479
14391
  const DOWN = "\x1B[B";
@@ -14493,7 +14405,7 @@ ${data.message || ""}`.trim();
14493
14405
  }
14494
14406
  getDebugState() {
14495
14407
  const screenText = sanitizeTerminalText(this.terminalScreen.getText());
14496
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
14408
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
14497
14409
  const effectiveStatus = this.projectEffectiveStatus(startupModal);
14498
14410
  const effectiveReady = this.ready || !!startupModal;
14499
14411
  return {
@@ -87180,7 +87092,7 @@ var init_adhdev_daemon = __esm({
87180
87092
  init_version();
87181
87093
  init_src();
87182
87094
  init_runtime_defaults();
87183
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.6" });
87095
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.9" });
87184
87096
  AdhdevDaemon = class _AdhdevDaemon {
87185
87097
  localHttpServer = null;
87186
87098
  localWss = null;