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/index.js CHANGED
@@ -5293,6 +5293,18 @@ function validateRole(role, source, index) {
5293
5293
  }
5294
5294
  return role;
5295
5295
  }
5296
+ function validateBubbleState(state, source, index) {
5297
+ if (typeof state !== "string" || !VALID_BUBBLE_STATES.includes(state)) {
5298
+ throw new Error(`${source}: messages[${index}].bubbleState must be one of ${VALID_BUBBLE_STATES.join(", ")}`);
5299
+ }
5300
+ return state;
5301
+ }
5302
+ function validateTurnStatus(turnStatus, source) {
5303
+ if (typeof turnStatus !== "string" || !VALID_TURN_STATUSES.includes(turnStatus)) {
5304
+ throw new Error(`${source}: turnStatus must be one of ${VALID_TURN_STATUSES.join(", ")}`);
5305
+ }
5306
+ return turnStatus;
5307
+ }
5296
5308
  function validateMessageContent(content, source, index) {
5297
5309
  if (typeof content === "string") return content;
5298
5310
  if (Array.isArray(content)) return normalizeMessageParts(content);
@@ -5308,6 +5320,9 @@ function validateMessage(message, source, index) {
5308
5320
  };
5309
5321
  if (typeof message.kind === "string") normalized.kind = message.kind;
5310
5322
  if (typeof message.id === "string") normalized.id = message.id;
5323
+ if (typeof message.bubbleId === "string") normalized.bubbleId = message.bubbleId;
5324
+ if (typeof message.providerUnitKey === "string") normalized.providerUnitKey = message.providerUnitKey;
5325
+ if (message.bubbleState !== void 0) normalized.bubbleState = validateBubbleState(message.bubbleState, source, index);
5311
5326
  if (isFiniteNumber(message.index)) normalized.index = message.index;
5312
5327
  if (isFiniteNumber(message.timestamp)) normalized.timestamp = message.timestamp;
5313
5328
  if (isFiniteNumber(message.receivedAt)) normalized.receivedAt = message.receivedAt;
@@ -5375,6 +5390,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
5375
5390
  if (activeModal !== void 0) normalized.activeModal = activeModal;
5376
5391
  if (typeof raw.id === "string") normalized.id = raw.id;
5377
5392
  if (typeof raw.title === "string") normalized.title = raw.title;
5393
+ if (typeof raw.currentTurnId === "string") normalized.currentTurnId = raw.currentTurnId;
5394
+ if (raw.turnStatus !== void 0) normalized.turnStatus = validateTurnStatus(raw.turnStatus, source);
5378
5395
  if (typeof raw.agentType === "string") normalized.agentType = raw.agentType;
5379
5396
  if (typeof raw.agentName === "string") normalized.agentName = raw.agentName;
5380
5397
  if (typeof raw.extensionId === "string") normalized.extensionId = raw.extensionId;
@@ -5387,13 +5404,15 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
5387
5404
  if (typeof raw.providerSessionId === "string") normalized.providerSessionId = raw.providerSessionId;
5388
5405
  return normalized;
5389
5406
  }
5390
- var VALID_STATUSES, VALID_ROLES;
5407
+ var VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES;
5391
5408
  var init_read_chat_contract = __esm({
5392
5409
  "../../oss/packages/daemon-core/src/providers/read-chat-contract.ts"() {
5393
5410
  "use strict";
5394
5411
  init_contracts();
5395
5412
  VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "streaming", "long_generating"];
5396
5413
  VALID_ROLES = ["user", "assistant", "system", "human"];
5414
+ VALID_BUBBLE_STATES = ["draft", "streaming", "final", "removed"];
5415
+ VALID_TURN_STATUSES = ["open", "waiting_approval", "complete", "error"];
5397
5416
  }
5398
5417
  });
5399
5418
 
@@ -8305,25 +8324,23 @@ async function handleResolveAction(h, args) {
8305
8324
  const effectiveModal = statusModal || surfacedModal;
8306
8325
  const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" ? "waiting_approval" : status?.status;
8307
8326
  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) {
8327
+ if (!effectiveModal) {
8309
8328
  return { success: false, error: "Not in approval state" };
8310
8329
  }
8311
- const buttons = effectiveModal?.buttons || ["Allow once", "Always allow", "Deny"];
8330
+ const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
8312
8331
  let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
8313
- if (buttonIndex < 0) {
8332
+ if (buttonIndex < 0 && button) {
8314
8333
  const btnLower = button.toLowerCase();
8315
8334
  buttonIndex = buttons.findIndex((b) => b.toLowerCase().includes(btnLower));
8316
8335
  }
8336
+ if (buttonIndex < 0 && (action === "reject" || action === "deny")) {
8337
+ buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
8338
+ }
8339
+ if (buttonIndex < 0 && (action === "always" || /always/i.test(button))) {
8340
+ buttonIndex = buttons.findIndex((b) => /always/i.test(b));
8341
+ }
8317
8342
  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
- }
8343
+ return { success: false, error: "Approval action did not match any visible button" };
8327
8344
  }
8328
8345
  if (typeof adapter.resolveModal === "function") {
8329
8346
  adapter.resolveModal(buttonIndex);
@@ -11132,8 +11149,35 @@ function promptLikelyVisible(screenText, promptSnippet) {
11132
11149
  function normalizeScreenSnapshot(text) {
11133
11150
  return sanitizeTerminalText(String(text || "")).replace(/\s+/g, " ").trim();
11134
11151
  }
11152
+ function shouldReflowComparableMessageLines(lines) {
11153
+ 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));
11154
+ }
11155
+ function joinComparableMessageLines(lines) {
11156
+ return lines.reduce((acc, line) => {
11157
+ const next = String(line || "").trim();
11158
+ if (!next) return acc;
11159
+ if (!acc) return next;
11160
+ if (/[,\d]$/.test(acc) && /^\d/.test(next)) {
11161
+ return `${acc}${next}`;
11162
+ }
11163
+ if (/[A-Za-z]$/.test(acc) && /^\d/.test(next)) {
11164
+ return `${acc}${next}`;
11165
+ }
11166
+ const fragmentMatch = acc.match(/([A-Za-z]{1,4})$/);
11167
+ const fragment = fragmentMatch ? fragmentMatch[1].toLowerCase() : "";
11168
+ if (/^[a-z]/.test(next) && fragment && !COMMON_COMPARABLE_WRAP_WORDS.has(fragment)) {
11169
+ return `${acc}${next}`;
11170
+ }
11171
+ return `${acc} ${next}`;
11172
+ }, "").replace(/\s+([,.;:!?])/g, "$1").replace(/(\d)\s+,/g, "$1,").replace(/\s+/g, " ").trim();
11173
+ }
11135
11174
  function normalizeComparableMessageContent(text) {
11136
- return String(text || "").replace(/\s+/g, " ").trim();
11175
+ const lines = String(text || "").split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
11176
+ if (lines.length === 0) return "";
11177
+ if (shouldReflowComparableMessageLines(lines)) {
11178
+ return joinComparableMessageLines(lines);
11179
+ }
11180
+ return lines.join(" ").replace(/\s+/g, " ").trim();
11137
11181
  }
11138
11182
  function trimPromptEchoPrefix(text, promptText) {
11139
11183
  const prompt2 = normalizeComparableMessageContent(String(promptText || ""));
@@ -11166,9 +11210,6 @@ function getLastUserPromptText(messages) {
11166
11210
  }
11167
11211
  return "";
11168
11212
  }
11169
- function looksLikeConfirmOnlyLabel(label) {
11170
- return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
11171
- }
11172
11213
  function parsePatternEntry(x) {
11173
11214
  if (x instanceof RegExp) return x;
11174
11215
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -11195,7 +11236,7 @@ function normalizeCliProviderForRuntime(raw) {
11195
11236
  }
11196
11237
  };
11197
11238
  }
11198
- var os10, path9, import_child_process4, buildCliSpawnEnv;
11239
+ var os10, path9, import_child_process4, buildCliSpawnEnv, COMMON_COMPARABLE_WRAP_WORDS;
11199
11240
  var init_provider_cli_shared = __esm({
11200
11241
  "../../oss/packages/daemon-core/src/cli-adapters/provider-cli-shared.ts"() {
11201
11242
  "use strict";
@@ -11204,6 +11245,32 @@ var init_provider_cli_shared = __esm({
11204
11245
  import_child_process4 = require("child_process");
11205
11246
  init_spawn_env();
11206
11247
  buildCliSpawnEnv = sanitizeSpawnEnv;
11248
+ COMMON_COMPARABLE_WRAP_WORDS = /* @__PURE__ */ new Set([
11249
+ "a",
11250
+ "an",
11251
+ "and",
11252
+ "as",
11253
+ "at",
11254
+ "but",
11255
+ "by",
11256
+ "for",
11257
+ "from",
11258
+ "in",
11259
+ "into",
11260
+ "is",
11261
+ "it",
11262
+ "of",
11263
+ "on",
11264
+ "or",
11265
+ "that",
11266
+ "the",
11267
+ "their",
11268
+ "then",
11269
+ "this",
11270
+ "to",
11271
+ "was",
11272
+ "with"
11273
+ ]);
11207
11274
  }
11208
11275
  });
11209
11276
 
@@ -11259,8 +11326,44 @@ function hydrateCliParsedMessages(parsedMessages, options) {
11259
11326
  };
11260
11327
  });
11261
11328
  }
11329
+ function chooseMoreComparableCliMessage(left2, right2) {
11330
+ const leftComparable = normalizeComparableMessageContent(left2.content || "");
11331
+ const rightComparable = normalizeComparableMessageContent(right2.content || "");
11332
+ if (leftComparable && leftComparable === rightComparable) {
11333
+ const leftNewlines = String(left2.content || "").split(/\r\n|\n|\r/g).length - 1;
11334
+ const rightNewlines = String(right2.content || "").split(/\r\n|\n|\r/g).length - 1;
11335
+ return rightNewlines < leftNewlines ? right2 : left2;
11336
+ }
11337
+ return rightComparable.length > leftComparable.length ? right2 : left2;
11338
+ }
11339
+ function dedupeConsecutiveComparableCliMessages(messages) {
11340
+ const deduped = [];
11341
+ for (const message of messages) {
11342
+ const current = {
11343
+ ...message,
11344
+ content: typeof message.content === "string" ? message.content : String(message.content || "")
11345
+ };
11346
+ const previous = deduped[deduped.length - 1];
11347
+ if (!previous) {
11348
+ deduped.push(current);
11349
+ continue;
11350
+ }
11351
+ const previousComparable = normalizeComparableMessageContent(previous.content || "");
11352
+ const currentComparable = normalizeComparableMessageContent(current.content || "");
11353
+ const sameRole = previous.role === current.role;
11354
+ const sameKind = (previous.kind || "standard") === (current.kind || "standard");
11355
+ const sameSender = (previous.senderName || "") === (current.senderName || "");
11356
+ const comparableMatch = previousComparable && previousComparable === currentComparable;
11357
+ if (sameRole && sameKind && sameSender && comparableMatch) {
11358
+ deduped[deduped.length - 1] = chooseMoreComparableCliMessage(previous, current);
11359
+ continue;
11360
+ }
11361
+ deduped.push(current);
11362
+ }
11363
+ return deduped;
11364
+ }
11262
11365
  function normalizeCliParsedMessages(parsedMessages, options) {
11263
- return hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
11366
+ return dedupeConsecutiveComparableCliMessages(hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
11264
11367
  role: message.role,
11265
11368
  content: message.content,
11266
11369
  timestamp: message.timestamp,
@@ -11270,7 +11373,7 @@ function normalizeCliParsedMessages(parsedMessages, options) {
11270
11373
  index: message.index,
11271
11374
  meta: message.meta,
11272
11375
  senderName: message.senderName
11273
- }));
11376
+ })));
11274
11377
  }
11275
11378
  function buildCliParseInput(options) {
11276
11379
  const {
@@ -11933,7 +12036,7 @@ var init_provider_cli_adapter = __esm({
11933
12036
  if (!hasStartupOutput) return;
11934
12037
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
11935
12038
  if (stableMs < 2e3) return;
11936
- const startupModal = this.getStartupConfirmationModal(screenText);
12039
+ const startupModal = this.runParseApproval(this.recentOutputBuffer);
11937
12040
  this.startupParseGate = false;
11938
12041
  if (this.startupSettleTimer) {
11939
12042
  clearTimeout(this.startupSettleTimer);
@@ -11990,11 +12093,17 @@ var init_provider_cli_adapter = __esm({
11990
12093
  if (this.currentStatus !== "waiting_approval") return;
11991
12094
  const tail = this.recentOutputBuffer;
11992
12095
  const screenText = this.terminalScreen.getText() || "";
11993
- const startupModal = this.getStartupConfirmationModal(screenText);
11994
- const modal = this.runParseApproval(tail) || startupModal;
12096
+ const modal = this.runParseApproval(tail);
11995
12097
  const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
11996
12098
  if (stillWaiting) {
11997
- this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
12099
+ if (!modal) {
12100
+ LOG.warn("CLI", `[${this.cliType}] approval timeout check found no actionable modal; keeping approval state fail-closed`);
12101
+ this.activeModal = null;
12102
+ this.onStatusChange?.();
12103
+ this.armApprovalExitTimeout();
12104
+ return;
12105
+ }
12106
+ this.activeModal = modal;
11998
12107
  this.onStatusChange?.();
11999
12108
  this.armApprovalExitTimeout();
12000
12109
  return;
@@ -12006,81 +12115,12 @@ var init_provider_cli_adapter = __esm({
12006
12115
  this.onStatusChange?.();
12007
12116
  }, 6e4);
12008
12117
  }
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
12118
  shouldRetryFinishResponse(commitResult) {
12078
12119
  if (!this.currentTurnScope) return false;
12079
12120
  if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
12080
12121
  if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
12081
12122
  if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
12082
- const screenText = this.terminalScreen.getText() || "";
12083
- if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
12123
+ if (this.runDetectStatus(this.recentOutputBuffer) !== "idle") return false;
12084
12124
  const now = Date.now();
12085
12125
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
12086
12126
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
@@ -12104,45 +12144,21 @@ var init_provider_cli_adapter = __esm({
12104
12144
  }
12105
12145
  return false;
12106
12146
  }
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
12147
  async waitForInteractivePrompt(maxWaitMs = 5e3) {
12130
12148
  const startedAt = Date.now();
12131
12149
  let loggedWait = false;
12132
12150
  while (Date.now() - startedAt < maxWaitMs) {
12133
12151
  this.resolveStartupState("interactive_wait");
12134
12152
  const screenText = this.terminalScreen.getText() || "";
12135
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
12136
12153
  const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
12137
12154
  const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
12138
12155
  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";
12156
+ const interactiveReady = status === "idle" && stableMs >= 700 && recentlyOutput >= 350;
12141
12157
  if (interactiveReady) {
12142
12158
  if (loggedWait) {
12143
12159
  LOG.info(
12144
12160
  "CLI",
12145
- `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput}, startup=${startupLikelyActive})`
12161
+ `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput})`
12146
12162
  );
12147
12163
  }
12148
12164
  return;
@@ -12151,7 +12167,7 @@ var init_provider_cli_adapter = __esm({
12151
12167
  loggedWait = true;
12152
12168
  LOG.info(
12153
12169
  "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)}`
12170
+ `[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
12155
12171
  );
12156
12172
  }
12157
12173
  await new Promise((resolve16) => setTimeout(resolve16, 50));
@@ -12162,13 +12178,12 @@ var init_provider_cli_adapter = __esm({
12162
12178
  `[${this.cliType}] Interactive prompt wait timed out after ${maxWaitMs}ms; proceeding with screen=${JSON.stringify(summarizeCliTraceText(finalScreenText, 240)).slice(0, 280)}`
12163
12179
  );
12164
12180
  }
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
- }
12181
+ trimLastAssistantEcho(messages, prompt2) {
12182
+ if (!prompt2) return;
12183
+ const last = [...messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
12184
+ if (last) last.content = trimPromptEchoPrefix(last.content, prompt2);
12185
+ }
12186
+ clearAllTimers() {
12172
12187
  if (this.responseTimeout) {
12173
12188
  clearTimeout(this.responseTimeout);
12174
12189
  this.responseTimeout = null;
@@ -12181,10 +12196,38 @@ var init_provider_cli_adapter = __esm({
12181
12196
  clearTimeout(this.approvalExitTimeout);
12182
12197
  this.approvalExitTimeout = null;
12183
12198
  }
12199
+ if (this.submitRetryTimer) {
12200
+ clearTimeout(this.submitRetryTimer);
12201
+ this.submitRetryTimer = null;
12202
+ }
12184
12203
  if (this.finishRetryTimer) {
12185
12204
  clearTimeout(this.finishRetryTimer);
12186
12205
  this.finishRetryTimer = null;
12187
12206
  }
12207
+ if (this.settleTimer) {
12208
+ clearTimeout(this.settleTimer);
12209
+ this.settleTimer = null;
12210
+ }
12211
+ if (this.pendingScriptStatusTimer) {
12212
+ clearTimeout(this.pendingScriptStatusTimer);
12213
+ this.pendingScriptStatusTimer = null;
12214
+ }
12215
+ if (this.pendingOutputParseTimer) {
12216
+ clearTimeout(this.pendingOutputParseTimer);
12217
+ this.pendingOutputParseTimer = null;
12218
+ }
12219
+ if (this.ptyOutputFlushTimer) {
12220
+ clearTimeout(this.ptyOutputFlushTimer);
12221
+ this.ptyOutputFlushTimer = null;
12222
+ }
12223
+ }
12224
+ clearStaleIdleResponseGuard(reason) {
12225
+ const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
12226
+ const isIdle = this.runDetectStatus(this.recentOutputBuffer) === "idle";
12227
+ if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !isIdle || !!blockingModal) {
12228
+ return false;
12229
+ }
12230
+ this.clearAllTimers();
12188
12231
  this.clearIdleFinishCandidate(reason);
12189
12232
  this.responseBuffer = "";
12190
12233
  this.isWaitingForResponse = false;
@@ -12194,10 +12237,7 @@ var init_provider_cli_adapter = __esm({
12194
12237
  this.finishRetryCount = 0;
12195
12238
  this.currentTurnScope = null;
12196
12239
  this.activeModal = null;
12197
- this.recordTrace("stale_idle_response_cleared", {
12198
- reason,
12199
- screenText: summarizeCliTraceText(screenText, 240)
12200
- });
12240
+ this.recordTrace("stale_idle_response_cleared", { reason });
12201
12241
  return true;
12202
12242
  }
12203
12243
  hasMeaningfulResponseBuffer(promptSnippet) {
@@ -12232,16 +12272,15 @@ var init_provider_cli_adapter = __esm({
12232
12272
  if (this.startupParseGate) {
12233
12273
  return;
12234
12274
  }
12235
- const startupModal = this.getStartupConfirmationModal(screenText);
12236
12275
  const parsedTranscript = this.parseCurrentTranscript(
12237
12276
  this.committedMessages,
12238
12277
  this.responseBuffer,
12239
12278
  this.currentTurnScope
12240
12279
  );
12241
12280
  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;
12281
+ const modal = this.runParseApproval(tail) || parsedModal;
12243
12282
  const rawScriptStatus = this.runDetectStatus(tail);
12244
- const scriptStatus = startupModal ? "waiting_approval" : parsedModal && parsedTranscript?.status === "waiting_approval" ? "waiting_approval" : rawScriptStatus;
12283
+ const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
12245
12284
  const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
12246
12285
  committedMessages: this.committedMessages,
12247
12286
  scope: this.currentTurnScope,
@@ -12296,15 +12335,44 @@ var init_provider_cli_adapter = __esm({
12296
12335
  }
12297
12336
  if (!scriptStatus) return;
12298
12337
  const prevStatus = this.currentStatus;
12299
- const clearPendingScriptStatus = () => {
12338
+ const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
12339
+ if (!this.applyPendingScriptStatusDebounce(ctx)) return;
12340
+ const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
12341
+ LOG.info(
12342
+ "CLI",
12343
+ `[${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)}`
12344
+ );
12345
+ const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
12346
+ if (shouldHoldGenerating) {
12347
+ this.applyHoldGenerating(ctx, recentInteractiveActivity);
12348
+ return;
12349
+ }
12350
+ if (scriptStatus === "waiting_approval") {
12351
+ this.applyWaitingApproval(ctx);
12352
+ return;
12353
+ }
12354
+ if (scriptStatus === "generating") {
12355
+ this.applyGenerating(ctx);
12356
+ return;
12357
+ }
12358
+ if (scriptStatus === "idle") {
12359
+ this.applyIdle(ctx, now);
12360
+ }
12361
+ }
12362
+ // Returns false if the caller should bail out (debounce pending).
12363
+ applyPendingScriptStatusDebounce(ctx) {
12364
+ const { now, scriptStatus, prevStatus } = ctx;
12365
+ const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
12366
+ if (!shouldDebounce) {
12300
12367
  this.pendingScriptStatus = null;
12301
12368
  this.pendingScriptStatusSince = 0;
12302
12369
  if (this.pendingScriptStatusTimer) {
12303
12370
  clearTimeout(this.pendingScriptStatusTimer);
12304
12371
  this.pendingScriptStatusTimer = null;
12305
12372
  }
12306
- };
12307
- const armPendingScriptStatus = (delayMs) => {
12373
+ return true;
12374
+ }
12375
+ const armPending = (delayMs) => {
12308
12376
  if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
12309
12377
  this.pendingScriptStatusTimer = setTimeout(() => {
12310
12378
  this.pendingScriptStatusTimer = null;
@@ -12312,200 +12380,187 @@ var init_provider_cli_adapter = __esm({
12312
12380
  this.evaluateSettled();
12313
12381
  }, delayMs);
12314
12382
  };
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
- );
12383
+ if (this.pendingScriptStatus !== scriptStatus) {
12384
+ this.pendingScriptStatus = scriptStatus;
12385
+ this.pendingScriptStatusSince = now;
12386
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
12387
+ return false;
12340
12388
  }
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;
12389
+ const elapsed = now - this.pendingScriptStatusSince;
12390
+ if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
12391
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
12392
+ return false;
12368
12393
  }
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;
12394
+ return true;
12395
+ }
12396
+ applyHoldGenerating(ctx, recentInteractiveActivity) {
12397
+ const { scriptStatus } = ctx;
12398
+ this.clearIdleFinishCandidate("hold_generating_recent_activity");
12399
+ this.setStatus("generating", "recent_activity_hold");
12400
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12401
+ this.idleTimeout = setTimeout(() => {
12402
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12403
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12404
+ this.finishResponse();
12405
+ }
12406
+ }, this.timeouts.generatingIdle);
12407
+ this.recordTrace("hold_generating_recent_activity", {
12408
+ scriptStatus,
12409
+ recentInteractiveActivity,
12410
+ lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
12411
+ lastScreenChangeAt: this.lastScreenChangeAt,
12412
+ holdMs: this.getStatusActivityHoldMs(),
12413
+ ...buildCliTraceParseSnapshot({
12414
+ accumulatedBuffer: this.accumulatedBuffer,
12415
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
12416
+ responseBuffer: this.responseBuffer,
12417
+ partialResponse: this.responseBuffer,
12418
+ scope: this.currentTurnScope
12419
+ })
12420
+ });
12421
+ this.onStatusChange?.();
12422
+ }
12423
+ applyWaitingApproval(ctx) {
12424
+ const { modal } = ctx;
12425
+ this.clearIdleFinishCandidate("waiting_approval");
12426
+ const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
12427
+ if (inCooldown && !modal) {
12428
+ if (this.approvalExitTimeout) {
12429
+ clearTimeout(this.approvalExitTimeout);
12430
+ this.approvalExitTimeout = null;
12393
12431
  }
12394
- if (!inCooldown) {
12395
- this.isWaitingForResponse = true;
12396
- this.setStatus("waiting_approval", "script_detect");
12397
- this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
12432
+ this.activeModal = null;
12433
+ if (this.isWaitingForResponse) {
12434
+ this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
12398
12435
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
12399
- this.armApprovalExitTimeout();
12400
- this.onStatusChange?.();
12401
- return;
12436
+ this.idleTimeout = setTimeout(() => {
12437
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12438
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12439
+ this.finishResponse();
12440
+ }
12441
+ }, this.timeouts.generatingIdle);
12442
+ } else {
12443
+ this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
12402
12444
  }
12445
+ this.onStatusChange?.();
12446
+ return;
12403
12447
  }
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) {
12448
+ if (!inCooldown) {
12449
+ if (!modal) {
12450
+ LOG.warn("CLI", `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
12410
12451
  return;
12411
12452
  }
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");
12453
+ this.isWaitingForResponse = true;
12454
+ this.setStatus("waiting_approval", "script_detect");
12455
+ this.activeModal = modal;
12425
12456
  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);
12457
+ this.armApprovalExitTimeout();
12432
12458
  this.onStatusChange?.();
12459
+ }
12460
+ }
12461
+ applyGenerating(ctx) {
12462
+ const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
12463
+ this.clearIdleFinishCandidate("generating");
12464
+ const effectiveScreenText = screenText || this.accumulatedBuffer;
12465
+ const noActiveTurn = !this.currentTurnScope;
12466
+ 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));
12467
+ if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
12433
12468
  return;
12434
12469
  }
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();
12470
+ if (prevStatus === "waiting_approval") {
12471
+ if (this.approvalExitTimeout) {
12472
+ clearTimeout(this.approvalExitTimeout);
12473
+ this.approvalExitTimeout = null;
12443
12474
  }
12475
+ this.activeModal = null;
12476
+ this.lastApprovalResolvedAt = Date.now();
12477
+ }
12478
+ if (!this.isWaitingForResponse) {
12479
+ this.isWaitingForResponse = true;
12480
+ this.responseBuffer = "";
12481
+ }
12482
+ this.setStatus("generating", "script_detect");
12483
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12484
+ this.idleTimeout = setTimeout(() => {
12444
12485
  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") {
12486
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12487
+ this.finishResponse();
12488
+ }
12489
+ }, this.timeouts.generatingIdle);
12490
+ this.onStatusChange?.();
12491
+ }
12492
+ applyIdle(ctx, now) {
12493
+ const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
12494
+ if (prevStatus === "waiting_approval") {
12495
+ if (this.approvalExitTimeout) {
12496
+ clearTimeout(this.approvalExitTimeout);
12497
+ this.approvalExitTimeout = null;
12498
+ }
12499
+ this.activeModal = null;
12500
+ this.lastApprovalResolvedAt = Date.now();
12501
+ }
12502
+ if (!this.isWaitingForResponse) {
12503
+ if (prevStatus !== "idle") {
12504
12504
  this.clearIdleFinishCandidate("idle_without_response");
12505
12505
  this.setStatus("idle", "script_detect");
12506
12506
  this.onStatusChange?.();
12507
12507
  }
12508
+ return;
12508
12509
  }
12510
+ const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
12511
+ const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
12512
+ const hasAssistantTurn = !!lastParsedAssistant;
12513
+ const assistantLength = lastParsedAssistant?.content?.length || 0;
12514
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
12515
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
12516
+ const idleReady = !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleFinishConfirmMs;
12517
+ const candidate = this.idleFinishCandidate;
12518
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
12519
+ this.recordTrace("idle_decision", {
12520
+ quietForMs,
12521
+ screenStableMs,
12522
+ hasAssistantTurn,
12523
+ assistantLength,
12524
+ hasModal: !!modal,
12525
+ idleQuietThresholdMs,
12526
+ idleStableThresholdMs: idleFinishConfirmMs,
12527
+ idleReady,
12528
+ idleFinishConfirmMs,
12529
+ idleFinishCandidate: candidate,
12530
+ candidateQuiet,
12531
+ canFinishImmediately: idleReady && candidateQuiet,
12532
+ submitPendingUntil: this.submitPendingUntil,
12533
+ responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
12534
+ ...buildCliTraceParseSnapshot({
12535
+ accumulatedBuffer: this.accumulatedBuffer,
12536
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
12537
+ responseBuffer: this.responseBuffer,
12538
+ partialResponse: this.responseBuffer,
12539
+ scope: this.currentTurnScope
12540
+ })
12541
+ });
12542
+ if (idleReady && candidateQuiet) {
12543
+ this.clearIdleFinishCandidate("finish_response");
12544
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12545
+ this.finishResponse();
12546
+ return;
12547
+ }
12548
+ if (idleReady) {
12549
+ if (!candidate) {
12550
+ this.armIdleFinishCandidate(assistantLength);
12551
+ return;
12552
+ }
12553
+ } else {
12554
+ this.clearIdleFinishCandidate("idle_not_ready");
12555
+ }
12556
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
12557
+ this.idleTimeout = setTimeout(() => {
12558
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
12559
+ if (this.shouldDeferIdleTimeoutFinish()) return;
12560
+ this.clearIdleFinishCandidate("idle_timeout_finish");
12561
+ this.finishResponse();
12562
+ }
12563
+ }, this.timeouts.idleFinish);
12509
12564
  }
12510
12565
  finishResponse() {
12511
12566
  if (this.submitPendingUntil > Date.now()) return;
@@ -12544,26 +12599,7 @@ var init_provider_cli_adapter = __esm({
12544
12599
  }, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
12545
12600
  return;
12546
12601
  }
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
- }
12602
+ this.clearAllTimers();
12567
12603
  this.responseBuffer = "";
12568
12604
  this.isWaitingForResponse = false;
12569
12605
  this.responseSettleIgnoreUntil = 0;
@@ -12575,18 +12611,12 @@ var init_provider_cli_adapter = __esm({
12575
12611
  this.setStatus("idle", "response_finished");
12576
12612
  this.onStatusChange?.();
12577
12613
  }
12578
- maybeCommitVisibleIdleTranscript(parsed, options) {
12614
+ maybeCommitVisibleIdleTranscript(parsed) {
12579
12615
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
12580
12616
  if (!allowImmediateScriptIdleCommit) return false;
12581
12617
  if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
12582
12618
  return false;
12583
12619
  }
12584
- if (options?.requireVisibleAssistantCandidate) {
12585
- const candidateText = options.screenText || this.terminalScreen.getText() || "";
12586
- if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
12587
- return false;
12588
- }
12589
- }
12590
12620
  const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
12591
12621
  committedMessages: this.committedMessages,
12592
12622
  scope: this.currentTurnScope,
@@ -12595,33 +12625,8 @@ var init_provider_cli_adapter = __esm({
12595
12625
  const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
12596
12626
  if (!visibleAssistant) return false;
12597
12627
  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
- }
12628
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
12629
+ this.clearAllTimers();
12625
12630
  this.syncMessageViews();
12626
12631
  this.responseBuffer = "";
12627
12632
  this.isWaitingForResponse = false;
@@ -12651,13 +12656,7 @@ var init_provider_cli_adapter = __esm({
12651
12656
  scope: this.currentTurnScope,
12652
12657
  lastOutputAt: this.lastOutputAt
12653
12658
  });
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
- }
12659
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
12661
12660
  this.syncMessageViews();
12662
12661
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
12663
12662
  if (this.currentTurnScope) {
@@ -12715,7 +12714,7 @@ var init_provider_cli_adapter = __esm({
12715
12714
  screen: buildCliScreenSnapshot(screenText),
12716
12715
  tailScreen: buildCliScreenSnapshot(text.slice(-500))
12717
12716
  });
12718
- return this.refineDetectedStatus(status, text, screenText || "");
12717
+ return status;
12719
12718
  } catch (e) {
12720
12719
  LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
12721
12720
  return null;
@@ -12755,23 +12754,21 @@ var init_provider_cli_adapter = __esm({
12755
12754
  if (!inApprovalCooldown) {
12756
12755
  return parsed;
12757
12756
  }
12758
- const startupModal = this.getStartupConfirmationModal(screenText || "");
12759
- const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
12757
+ const visibleModal = this.runParseApproval(recentBuffer);
12760
12758
  if (visibleModal) {
12761
12759
  return parsed;
12762
12760
  }
12763
12761
  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;
12762
+ const resolvedStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
12765
12763
  return {
12766
12764
  ...parsed,
12767
- status: fallbackStatus,
12765
+ status: resolvedStatus,
12768
12766
  activeModal: null
12769
12767
  };
12770
12768
  }
12771
12769
  // ─── Public API (CliAdapter) ───────────────────
12772
12770
  getStatus() {
12773
- const screenText = this.terminalScreen.getText() || "";
12774
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
12771
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
12775
12772
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
12776
12773
  let effectiveModal = startupModal || this.activeModal;
12777
12774
  if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
@@ -12852,8 +12849,7 @@ var init_provider_cli_adapter = __esm({
12852
12849
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
12853
12850
  }));
12854
12851
  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);
12852
+ 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
12853
  if (shouldAdoptParsedIdleReplay) {
12858
12854
  this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
12859
12855
  committedMessages: this.committedMessages,
@@ -12982,17 +12978,9 @@ var init_provider_cli_adapter = __esm({
12982
12978
  if (parsed && typeof parsed === "object") {
12983
12979
  Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
12984
12980
  }
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
12981
  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
- }
12982
+ if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
12983
+ this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
12996
12984
  }
12997
12985
  this.parseErrorMessage = null;
12998
12986
  return normalizedParsed;
@@ -13020,16 +13008,11 @@ var init_provider_cli_adapter = __esm({
13020
13008
  LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
13021
13009
  }
13022
13010
  }
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);
13011
+ if (!promptText) {
13012
+ LOG.warn("CLI", `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
13013
+ return;
13032
13014
  }
13015
+ await this.sendMessage(promptText);
13033
13016
  }
13034
13017
  async sendMessage(text) {
13035
13018
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
@@ -13047,9 +13030,7 @@ ${data.message || ""}`.trim();
13047
13030
  }
13048
13031
  if (!this.ready) {
13049
13032
  this.resolveStartupState("send_precheck");
13050
- const screenText = this.terminalScreen.getText() || "";
13051
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
13052
- if (hasPrompt && this.currentStatus === "idle") {
13033
+ if (this.runDetectStatus(this.recentOutputBuffer) === "idle" && this.currentStatus === "idle") {
13053
13034
  this.ready = true;
13054
13035
  this.startupParseGate = false;
13055
13036
  LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
@@ -13155,7 +13136,10 @@ ${data.message || ""}`.trim();
13155
13136
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
13156
13137
  const screenText2 = this.terminalScreen.getText();
13157
13138
  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;
13139
+ const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
13140
+ if (liveApproval) return;
13141
+ const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
13142
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
13159
13143
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
13160
13144
  LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
13161
13145
  this.recordTrace("submit_write", {
@@ -13192,6 +13176,10 @@ ${data.message || ""}`.trim();
13192
13176
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
13193
13177
  const screenText = this.terminalScreen.getText();
13194
13178
  if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
13179
+ const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
13180
+ if (liveApproval) return;
13181
+ const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
13182
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
13195
13183
  LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
13196
13184
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
13197
13185
  this.recordTrace("submit_write", {
@@ -13323,44 +13311,9 @@ ${data.message || ""}`.trim();
13323
13311
  }
13324
13312
  shutdown() {
13325
13313
  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
- }
13314
+ this.clearAllTimers();
13358
13315
  this.pendingOutputParseBuffer = "";
13359
13316
  this.pendingTerminalQueryTail = "";
13360
- if (this.ptyOutputFlushTimer) {
13361
- clearTimeout(this.ptyOutputFlushTimer);
13362
- this.ptyOutputFlushTimer = null;
13363
- }
13364
13317
  this.ptyOutputBuffer = "";
13365
13318
  this.finishRetryCount = 0;
13366
13319
  if (this.ptyProcess) {
@@ -13381,44 +13334,9 @@ ${data.message || ""}`.trim();
13381
13334
  }
13382
13335
  detach() {
13383
13336
  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
- }
13337
+ this.clearAllTimers();
13416
13338
  this.pendingOutputParseBuffer = "";
13417
13339
  this.pendingTerminalQueryTail = "";
13418
- if (this.ptyOutputFlushTimer) {
13419
- clearTimeout(this.ptyOutputFlushTimer);
13420
- this.ptyOutputFlushTimer = null;
13421
- }
13422
13340
  this.ptyOutputBuffer = "";
13423
13341
  this.finishRetryCount = 0;
13424
13342
  if (this.ptyProcess) {
@@ -13480,8 +13398,7 @@ ${data.message || ""}`.trim();
13480
13398
  this.ptyProcess?.write(data);
13481
13399
  }
13482
13400
  resolveModal(buttonIndex) {
13483
- const screenText = this.terminalScreen.getText() || "";
13484
- let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
13401
+ let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
13485
13402
  if (!modal && typeof this.cliScripts?.parseOutput === "function") {
13486
13403
  try {
13487
13404
  const parsed = this.getScriptParsedStatus();
@@ -13512,12 +13429,7 @@ ${data.message || ""}`.trim();
13512
13429
  }
13513
13430
  this.setStatus("generating", "approval_resolved");
13514
13431
  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) {
13432
+ if (buttonIndex in this.approvalKeys) {
13521
13433
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
13522
13434
  } else {
13523
13435
  const DOWN = "\x1B[B";
@@ -13537,7 +13449,7 @@ ${data.message || ""}`.trim();
13537
13449
  }
13538
13450
  getDebugState() {
13539
13451
  const screenText = sanitizeTerminalText(this.terminalScreen.getText());
13540
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
13452
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
13541
13453
  const effectiveStatus = this.projectEffectiveStatus(startupModal);
13542
13454
  const effectiveReady = this.ready || !!startupModal;
13543
13455
  return {
@@ -55460,7 +55372,7 @@ var init_adhdev_daemon = __esm({
55460
55372
  init_version();
55461
55373
  init_src();
55462
55374
  init_runtime_defaults();
55463
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.6" });
55375
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.9" });
55464
55376
  AdhdevDaemon = class _AdhdevDaemon {
55465
55377
  localHttpServer = null;
55466
55378
  localWss = null;