@adhdev/daemon-standalone 0.9.6 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -29378,8 +29378,35 @@ var require_dist2 = __commonJS({
29378
29378
  function normalizeScreenSnapshot(text) {
29379
29379
  return sanitizeTerminalText(String(text || "")).replace(/\s+/g, " ").trim();
29380
29380
  }
29381
+ function shouldReflowComparableMessageLines(lines) {
29382
+ 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));
29383
+ }
29384
+ function joinComparableMessageLines(lines) {
29385
+ return lines.reduce((acc, line) => {
29386
+ const next = String(line || "").trim();
29387
+ if (!next) return acc;
29388
+ if (!acc) return next;
29389
+ if (/[,\d]$/.test(acc) && /^\d/.test(next)) {
29390
+ return `${acc}${next}`;
29391
+ }
29392
+ if (/[A-Za-z]$/.test(acc) && /^\d/.test(next)) {
29393
+ return `${acc}${next}`;
29394
+ }
29395
+ const fragmentMatch = acc.match(/([A-Za-z]{1,4})$/);
29396
+ const fragment = fragmentMatch ? fragmentMatch[1].toLowerCase() : "";
29397
+ if (/^[a-z]/.test(next) && fragment && !COMMON_COMPARABLE_WRAP_WORDS.has(fragment)) {
29398
+ return `${acc}${next}`;
29399
+ }
29400
+ return `${acc} ${next}`;
29401
+ }, "").replace(/\s+([,.;:!?])/g, "$1").replace(/(\d)\s+,/g, "$1,").replace(/\s+/g, " ").trim();
29402
+ }
29381
29403
  function normalizeComparableMessageContent(text) {
29382
- return String(text || "").replace(/\s+/g, " ").trim();
29404
+ const lines = String(text || "").split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
29405
+ if (lines.length === 0) return "";
29406
+ if (shouldReflowComparableMessageLines(lines)) {
29407
+ return joinComparableMessageLines(lines);
29408
+ }
29409
+ return lines.join(" ").replace(/\s+/g, " ").trim();
29383
29410
  }
29384
29411
  function trimPromptEchoPrefix(text, promptText) {
29385
29412
  const prompt = normalizeComparableMessageContent(String(promptText || ""));
@@ -29412,9 +29439,6 @@ var require_dist2 = __commonJS({
29412
29439
  }
29413
29440
  return "";
29414
29441
  }
29415
- function looksLikeConfirmOnlyLabel(label) {
29416
- return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
29417
- }
29418
29442
  function parsePatternEntry(x) {
29419
29443
  if (x instanceof RegExp) return x;
29420
29444
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -29445,6 +29469,7 @@ var require_dist2 = __commonJS({
29445
29469
  var path9;
29446
29470
  var import_child_process4;
29447
29471
  var buildCliSpawnEnv;
29472
+ var COMMON_COMPARABLE_WRAP_WORDS;
29448
29473
  var init_provider_cli_shared = __esm2({
29449
29474
  "src/cli-adapters/provider-cli-shared.ts"() {
29450
29475
  "use strict";
@@ -29453,6 +29478,32 @@ var require_dist2 = __commonJS({
29453
29478
  import_child_process4 = require("child_process");
29454
29479
  init_spawn_env();
29455
29480
  buildCliSpawnEnv = import_session_host_core3.sanitizeSpawnEnv;
29481
+ COMMON_COMPARABLE_WRAP_WORDS = /* @__PURE__ */ new Set([
29482
+ "a",
29483
+ "an",
29484
+ "and",
29485
+ "as",
29486
+ "at",
29487
+ "but",
29488
+ "by",
29489
+ "for",
29490
+ "from",
29491
+ "in",
29492
+ "into",
29493
+ "is",
29494
+ "it",
29495
+ "of",
29496
+ "on",
29497
+ "or",
29498
+ "that",
29499
+ "the",
29500
+ "their",
29501
+ "then",
29502
+ "this",
29503
+ "to",
29504
+ "was",
29505
+ "with"
29506
+ ]);
29456
29507
  }
29457
29508
  });
29458
29509
  function sliceFromOffset(text, start) {
@@ -29506,8 +29557,44 @@ var require_dist2 = __commonJS({
29506
29557
  };
29507
29558
  });
29508
29559
  }
29560
+ function chooseMoreComparableCliMessage(left, right) {
29561
+ const leftComparable = normalizeComparableMessageContent(left.content || "");
29562
+ const rightComparable = normalizeComparableMessageContent(right.content || "");
29563
+ if (leftComparable && leftComparable === rightComparable) {
29564
+ const leftNewlines = String(left.content || "").split(/\r\n|\n|\r/g).length - 1;
29565
+ const rightNewlines = String(right.content || "").split(/\r\n|\n|\r/g).length - 1;
29566
+ return rightNewlines < leftNewlines ? right : left;
29567
+ }
29568
+ return rightComparable.length > leftComparable.length ? right : left;
29569
+ }
29570
+ function dedupeConsecutiveComparableCliMessages(messages) {
29571
+ const deduped = [];
29572
+ for (const message of messages) {
29573
+ const current = {
29574
+ ...message,
29575
+ content: typeof message.content === "string" ? message.content : String(message.content || "")
29576
+ };
29577
+ const previous = deduped[deduped.length - 1];
29578
+ if (!previous) {
29579
+ deduped.push(current);
29580
+ continue;
29581
+ }
29582
+ const previousComparable = normalizeComparableMessageContent(previous.content || "");
29583
+ const currentComparable = normalizeComparableMessageContent(current.content || "");
29584
+ const sameRole = previous.role === current.role;
29585
+ const sameKind = (previous.kind || "standard") === (current.kind || "standard");
29586
+ const sameSender = (previous.senderName || "") === (current.senderName || "");
29587
+ const comparableMatch = previousComparable && previousComparable === currentComparable;
29588
+ if (sameRole && sameKind && sameSender && comparableMatch) {
29589
+ deduped[deduped.length - 1] = chooseMoreComparableCliMessage(previous, current);
29590
+ continue;
29591
+ }
29592
+ deduped.push(current);
29593
+ }
29594
+ return deduped;
29595
+ }
29509
29596
  function normalizeCliParsedMessages(parsedMessages, options) {
29510
- return hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
29597
+ return dedupeConsecutiveComparableCliMessages(hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
29511
29598
  role: message.role,
29512
29599
  content: message.content,
29513
29600
  timestamp: message.timestamp,
@@ -29517,7 +29604,7 @@ var require_dist2 = __commonJS({
29517
29604
  index: message.index,
29518
29605
  meta: message.meta,
29519
29606
  senderName: message.senderName
29520
- }));
29607
+ })));
29521
29608
  }
29522
29609
  function buildCliParseInput(options) {
29523
29610
  const {
@@ -30177,7 +30264,7 @@ var require_dist2 = __commonJS({
30177
30264
  if (!hasStartupOutput) return;
30178
30265
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
30179
30266
  if (stableMs < 2e3) return;
30180
- const startupModal = this.getStartupConfirmationModal(screenText);
30267
+ const startupModal = this.runParseApproval(this.recentOutputBuffer);
30181
30268
  this.startupParseGate = false;
30182
30269
  if (this.startupSettleTimer) {
30183
30270
  clearTimeout(this.startupSettleTimer);
@@ -30234,11 +30321,17 @@ var require_dist2 = __commonJS({
30234
30321
  if (this.currentStatus !== "waiting_approval") return;
30235
30322
  const tail = this.recentOutputBuffer;
30236
30323
  const screenText = this.terminalScreen.getText() || "";
30237
- const startupModal = this.getStartupConfirmationModal(screenText);
30238
- const modal = this.runParseApproval(tail) || startupModal;
30324
+ const modal = this.runParseApproval(tail);
30239
30325
  const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
30240
30326
  if (stillWaiting) {
30241
- this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
30327
+ if (!modal) {
30328
+ LOG2.warn("CLI", `[${this.cliType}] approval timeout check found no actionable modal; keeping approval state fail-closed`);
30329
+ this.activeModal = null;
30330
+ this.onStatusChange?.();
30331
+ this.armApprovalExitTimeout();
30332
+ return;
30333
+ }
30334
+ this.activeModal = modal;
30242
30335
  this.onStatusChange?.();
30243
30336
  this.armApprovalExitTimeout();
30244
30337
  return;
@@ -30250,81 +30343,12 @@ var require_dist2 = __commonJS({
30250
30343
  this.onStatusChange?.();
30251
30344
  }, 6e4);
30252
30345
  }
30253
- looksLikeVisibleIdlePrompt(screenText) {
30254
- const text = String(screenText || "");
30255
- if (!text.trim()) return false;
30256
- 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)) {
30257
- return true;
30258
- }
30259
- 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);
30260
- }
30261
- findLastMatchingLineIndex(lines, predicate) {
30262
- for (let index = lines.length - 1; index >= 0; index -= 1) {
30263
- if (predicate(lines[index])) return index;
30264
- }
30265
- return -1;
30266
- }
30267
- looksLikeClaudeGeneratingLine(line) {
30268
- const trimmed = String(line || "").trim();
30269
- if (!trimmed) return false;
30270
- if (/^⏵⏵\s+accept edits on/i.test(trimmed)) return false;
30271
- if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) return true;
30272
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+\S+.*\b(?:thinking|thought for \d+s?)\b/i.test(trimmed)) return true;
30273
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+[A-Z][A-Za-z-]{3,}ing\b.*(?:…|\.{3})/u.test(trimmed)) return true;
30274
- if (/^[⏺•]\s+(?:Reading|Writing|Editing|Searching|Inspecting|Planning|Analyzing|Synthesizing|Drafting|Running|Listing|Scanning|Matching)\b.*(?:…|\.{3})/i.test(trimmed)) {
30275
- 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);
30276
- }
30277
- return false;
30278
- }
30279
- detectClaudeGeneratingOverride(screenText, tail) {
30280
- if (this.cliType !== "claude-cli") return false;
30281
- const source = sanitizeTerminalText(screenText || tail || "");
30282
- if (!source.trim()) return false;
30283
- const allLines = source.split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
30284
- if (allLines.length === 0) return false;
30285
- const recentLines = allLines.slice(-12);
30286
- const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
30287
- const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
30288
- if (activeRegion.length === 0) return false;
30289
- return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
30290
- }
30291
- refineDetectedStatus(status, tail, screenText) {
30292
- if (this.startupParseGate) {
30293
- return this.getStartupConfirmationModal(screenText || "") ? "waiting_approval" : "starting";
30294
- }
30295
- if (status === "waiting_approval") return status;
30296
- if (this.detectClaudeGeneratingOverride(screenText || "", tail)) return "generating";
30297
- return status;
30298
- }
30299
- looksLikeVisibleAssistantCandidate(screenText) {
30300
- const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
30301
- for (const line of lines) {
30302
- const trimmed = String(line || "").trim();
30303
- if (!trimmed) continue;
30304
- if (/^➜\s+\S+/.test(trimmed)) continue;
30305
- if (/^Update available!/i.test(trimmed)) continue;
30306
- if (/Claude Code v\d/i.test(trimmed)) continue;
30307
- if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
30308
- if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
30309
- if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
30310
- if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
30311
- const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
30312
- if (!assistantMatch) continue;
30313
- const content = assistantMatch[1].trim();
30314
- if (!content) continue;
30315
- if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
30316
- if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
30317
- return true;
30318
- }
30319
- return false;
30320
- }
30321
30346
  shouldRetryFinishResponse(commitResult) {
30322
30347
  if (!this.currentTurnScope) return false;
30323
30348
  if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
30324
30349
  if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
30325
30350
  if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
30326
- const screenText = this.terminalScreen.getText() || "";
30327
- if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
30351
+ if (this.runDetectStatus(this.recentOutputBuffer) !== "idle") return false;
30328
30352
  const now = Date.now();
30329
30353
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
30330
30354
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
@@ -30348,45 +30372,21 @@ var require_dist2 = __commonJS({
30348
30372
  }
30349
30373
  return false;
30350
30374
  }
30351
- getStartupConfirmationModal(screenText) {
30352
- const text = sanitizeTerminalText(String(screenText || ""));
30353
- if (!text.trim()) return null;
30354
- if (this.cliType === "claude-cli") {
30355
- 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);
30356
- const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
30357
- if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
30358
- return {
30359
- message: "Confirm Claude Code project trust",
30360
- buttons: ["Continue"]
30361
- };
30362
- }
30363
- }
30364
- return null;
30365
- }
30366
- shouldResolveModalWithEnter(modal, buttonIndex) {
30367
- if (!modal || buttonIndex !== 0) return false;
30368
- const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
30369
- if (buttons.length !== 1) return false;
30370
- const buttonLabel = String(buttons[0] || "").trim();
30371
- return looksLikeConfirmOnlyLabel(buttonLabel);
30372
- }
30373
30375
  async waitForInteractivePrompt(maxWaitMs = 5e3) {
30374
30376
  const startedAt = Date.now();
30375
30377
  let loggedWait = false;
30376
30378
  while (Date.now() - startedAt < maxWaitMs) {
30377
30379
  this.resolveStartupState("interactive_wait");
30378
30380
  const screenText = this.terminalScreen.getText() || "";
30379
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
30380
30381
  const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
30381
30382
  const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
30382
30383
  const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
30383
- const startupLikelyActive = /Welcome back|Tips for getting|Recent activity|Claude Code v\d/i.test(screenText);
30384
- const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "generating";
30384
+ const interactiveReady = status === "idle" && stableMs >= 700 && recentlyOutput >= 350;
30385
30385
  if (interactiveReady) {
30386
30386
  if (loggedWait) {
30387
30387
  LOG2.info(
30388
30388
  "CLI",
30389
- `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput}, startup=${startupLikelyActive})`
30389
+ `[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput})`
30390
30390
  );
30391
30391
  }
30392
30392
  return;
@@ -30395,7 +30395,7 @@ var require_dist2 = __commonJS({
30395
30395
  loggedWait = true;
30396
30396
  LOG2.info(
30397
30397
  "CLI",
30398
- `[${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)}`
30398
+ `[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
30399
30399
  );
30400
30400
  }
30401
30401
  await new Promise((resolve12) => setTimeout(resolve12, 50));
@@ -30406,13 +30406,12 @@ var require_dist2 = __commonJS({
30406
30406
  `[${this.cliType}] Interactive prompt wait timed out after ${maxWaitMs}ms; proceeding with screen=${JSON.stringify(summarizeCliTraceText(finalScreenText, 240)).slice(0, 280)}`
30407
30407
  );
30408
30408
  }
30409
- clearStaleIdleResponseGuard(reason) {
30410
- const screenText = this.terminalScreen.getText() || "";
30411
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
30412
- const blockingModal = this.activeModal || this.getStartupConfirmationModal(screenText);
30413
- if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !visibleIdlePrompt || !!blockingModal) {
30414
- return false;
30415
- }
30409
+ trimLastAssistantEcho(messages, prompt) {
30410
+ if (!prompt) return;
30411
+ const last = [...messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
30412
+ if (last) last.content = trimPromptEchoPrefix(last.content, prompt);
30413
+ }
30414
+ clearAllTimers() {
30416
30415
  if (this.responseTimeout) {
30417
30416
  clearTimeout(this.responseTimeout);
30418
30417
  this.responseTimeout = null;
@@ -30425,10 +30424,38 @@ var require_dist2 = __commonJS({
30425
30424
  clearTimeout(this.approvalExitTimeout);
30426
30425
  this.approvalExitTimeout = null;
30427
30426
  }
30427
+ if (this.submitRetryTimer) {
30428
+ clearTimeout(this.submitRetryTimer);
30429
+ this.submitRetryTimer = null;
30430
+ }
30428
30431
  if (this.finishRetryTimer) {
30429
30432
  clearTimeout(this.finishRetryTimer);
30430
30433
  this.finishRetryTimer = null;
30431
30434
  }
30435
+ if (this.settleTimer) {
30436
+ clearTimeout(this.settleTimer);
30437
+ this.settleTimer = null;
30438
+ }
30439
+ if (this.pendingScriptStatusTimer) {
30440
+ clearTimeout(this.pendingScriptStatusTimer);
30441
+ this.pendingScriptStatusTimer = null;
30442
+ }
30443
+ if (this.pendingOutputParseTimer) {
30444
+ clearTimeout(this.pendingOutputParseTimer);
30445
+ this.pendingOutputParseTimer = null;
30446
+ }
30447
+ if (this.ptyOutputFlushTimer) {
30448
+ clearTimeout(this.ptyOutputFlushTimer);
30449
+ this.ptyOutputFlushTimer = null;
30450
+ }
30451
+ }
30452
+ clearStaleIdleResponseGuard(reason) {
30453
+ const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
30454
+ const isIdle = this.runDetectStatus(this.recentOutputBuffer) === "idle";
30455
+ if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !isIdle || !!blockingModal) {
30456
+ return false;
30457
+ }
30458
+ this.clearAllTimers();
30432
30459
  this.clearIdleFinishCandidate(reason);
30433
30460
  this.responseBuffer = "";
30434
30461
  this.isWaitingForResponse = false;
@@ -30438,10 +30465,7 @@ var require_dist2 = __commonJS({
30438
30465
  this.finishRetryCount = 0;
30439
30466
  this.currentTurnScope = null;
30440
30467
  this.activeModal = null;
30441
- this.recordTrace("stale_idle_response_cleared", {
30442
- reason,
30443
- screenText: summarizeCliTraceText(screenText, 240)
30444
- });
30468
+ this.recordTrace("stale_idle_response_cleared", { reason });
30445
30469
  return true;
30446
30470
  }
30447
30471
  hasMeaningfulResponseBuffer(promptSnippet) {
@@ -30476,16 +30500,15 @@ var require_dist2 = __commonJS({
30476
30500
  if (this.startupParseGate) {
30477
30501
  return;
30478
30502
  }
30479
- const startupModal = this.getStartupConfirmationModal(screenText);
30480
30503
  const parsedTranscript = this.parseCurrentTranscript(
30481
30504
  this.committedMessages,
30482
30505
  this.responseBuffer,
30483
30506
  this.currentTurnScope
30484
30507
  );
30485
30508
  const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsedTranscript.activeModal : null;
30486
- const modal = this.runParseApproval(tail) || parsedModal || startupModal;
30509
+ const modal = this.runParseApproval(tail) || parsedModal;
30487
30510
  const rawScriptStatus = this.runDetectStatus(tail);
30488
- const scriptStatus = startupModal ? "waiting_approval" : parsedModal && parsedTranscript?.status === "waiting_approval" ? "waiting_approval" : rawScriptStatus;
30511
+ const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
30489
30512
  const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
30490
30513
  committedMessages: this.committedMessages,
30491
30514
  scope: this.currentTurnScope,
@@ -30540,15 +30563,44 @@ var require_dist2 = __commonJS({
30540
30563
  }
30541
30564
  if (!scriptStatus) return;
30542
30565
  const prevStatus = this.currentStatus;
30543
- const clearPendingScriptStatus = () => {
30566
+ const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
30567
+ if (!this.applyPendingScriptStatusDebounce(ctx)) return;
30568
+ const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
30569
+ LOG2.info(
30570
+ "CLI",
30571
+ `[${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)}`
30572
+ );
30573
+ const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
30574
+ if (shouldHoldGenerating) {
30575
+ this.applyHoldGenerating(ctx, recentInteractiveActivity);
30576
+ return;
30577
+ }
30578
+ if (scriptStatus === "waiting_approval") {
30579
+ this.applyWaitingApproval(ctx);
30580
+ return;
30581
+ }
30582
+ if (scriptStatus === "generating") {
30583
+ this.applyGenerating(ctx);
30584
+ return;
30585
+ }
30586
+ if (scriptStatus === "idle") {
30587
+ this.applyIdle(ctx, now);
30588
+ }
30589
+ }
30590
+ // Returns false if the caller should bail out (debounce pending).
30591
+ applyPendingScriptStatusDebounce(ctx) {
30592
+ const { now, scriptStatus, prevStatus } = ctx;
30593
+ const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
30594
+ if (!shouldDebounce) {
30544
30595
  this.pendingScriptStatus = null;
30545
30596
  this.pendingScriptStatusSince = 0;
30546
30597
  if (this.pendingScriptStatusTimer) {
30547
30598
  clearTimeout(this.pendingScriptStatusTimer);
30548
30599
  this.pendingScriptStatusTimer = null;
30549
30600
  }
30550
- };
30551
- const armPendingScriptStatus = (delayMs) => {
30601
+ return true;
30602
+ }
30603
+ const armPending = (delayMs) => {
30552
30604
  if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
30553
30605
  this.pendingScriptStatusTimer = setTimeout(() => {
30554
30606
  this.pendingScriptStatusTimer = null;
@@ -30556,200 +30608,187 @@ var require_dist2 = __commonJS({
30556
30608
  this.evaluateSettled();
30557
30609
  }, delayMs);
30558
30610
  };
30559
- const shouldDebouncePromotion = (status) => prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
30560
- if (shouldDebouncePromotion(scriptStatus)) {
30561
- if (this.pendingScriptStatus !== scriptStatus) {
30562
- this.pendingScriptStatus = scriptStatus;
30563
- this.pendingScriptStatusSince = now;
30564
- armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
30565
- return;
30566
- }
30567
- const elapsed = now - this.pendingScriptStatusSince;
30568
- if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
30569
- armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
30570
- return;
30571
- }
30572
- } else {
30573
- clearPendingScriptStatus();
30574
- }
30575
- const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
30576
- const statusActivityHoldMs = this.getStatusActivityHoldMs();
30577
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
30578
- const visibleAssistantCandidate = this.looksLikeVisibleAssistantCandidate(screenText);
30579
- if (this.currentTurnScope && this.cliType === "claude-cli") {
30580
- LOG2.info(
30581
- "CLI",
30582
- `[${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)}`
30583
- );
30611
+ if (this.pendingScriptStatus !== scriptStatus) {
30612
+ this.pendingScriptStatus = scriptStatus;
30613
+ this.pendingScriptStatusSince = now;
30614
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
30615
+ return false;
30584
30616
  }
30585
- const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(visibleIdlePrompt && visibleAssistantCandidate) && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
30586
- if (shouldHoldGenerating) {
30587
- this.clearIdleFinishCandidate("hold_generating_recent_activity");
30588
- this.setStatus("generating", "recent_activity_hold");
30589
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
30590
- this.idleTimeout = setTimeout(() => {
30591
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
30592
- if (this.shouldDeferIdleTimeoutFinish()) return;
30593
- this.finishResponse();
30594
- }
30595
- }, this.timeouts.generatingIdle);
30596
- this.recordTrace("hold_generating_recent_activity", {
30597
- scriptStatus,
30598
- recentInteractiveActivity,
30599
- lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
30600
- lastScreenChangeAt: this.lastScreenChangeAt,
30601
- holdMs: statusActivityHoldMs,
30602
- ...buildCliTraceParseSnapshot({
30603
- accumulatedBuffer: this.accumulatedBuffer,
30604
- accumulatedRawBuffer: this.accumulatedRawBuffer,
30605
- responseBuffer: this.responseBuffer,
30606
- partialResponse: this.responseBuffer,
30607
- scope: this.currentTurnScope
30608
- })
30609
- });
30610
- this.onStatusChange?.();
30611
- return;
30617
+ const elapsed = now - this.pendingScriptStatusSince;
30618
+ if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
30619
+ armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
30620
+ return false;
30612
30621
  }
30613
- if (scriptStatus === "waiting_approval") {
30614
- this.clearIdleFinishCandidate("waiting_approval");
30615
- const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
30616
- const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
30617
- if ((inCooldown || visibleIdlePrompt2) && !modal) {
30618
- if (this.approvalExitTimeout) {
30619
- clearTimeout(this.approvalExitTimeout);
30620
- this.approvalExitTimeout = null;
30621
- }
30622
- this.activeModal = null;
30623
- if (this.isWaitingForResponse) {
30624
- this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
30625
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
30626
- this.idleTimeout = setTimeout(() => {
30627
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
30628
- if (this.shouldDeferIdleTimeoutFinish()) return;
30629
- this.finishResponse();
30630
- }
30631
- }, this.timeouts.generatingIdle);
30632
- } else {
30633
- this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
30634
- }
30635
- this.onStatusChange?.();
30636
- return;
30622
+ return true;
30623
+ }
30624
+ applyHoldGenerating(ctx, recentInteractiveActivity) {
30625
+ const { scriptStatus } = ctx;
30626
+ this.clearIdleFinishCandidate("hold_generating_recent_activity");
30627
+ this.setStatus("generating", "recent_activity_hold");
30628
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
30629
+ this.idleTimeout = setTimeout(() => {
30630
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
30631
+ if (this.shouldDeferIdleTimeoutFinish()) return;
30632
+ this.finishResponse();
30633
+ }
30634
+ }, this.timeouts.generatingIdle);
30635
+ this.recordTrace("hold_generating_recent_activity", {
30636
+ scriptStatus,
30637
+ recentInteractiveActivity,
30638
+ lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
30639
+ lastScreenChangeAt: this.lastScreenChangeAt,
30640
+ holdMs: this.getStatusActivityHoldMs(),
30641
+ ...buildCliTraceParseSnapshot({
30642
+ accumulatedBuffer: this.accumulatedBuffer,
30643
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
30644
+ responseBuffer: this.responseBuffer,
30645
+ partialResponse: this.responseBuffer,
30646
+ scope: this.currentTurnScope
30647
+ })
30648
+ });
30649
+ this.onStatusChange?.();
30650
+ }
30651
+ applyWaitingApproval(ctx) {
30652
+ const { modal } = ctx;
30653
+ this.clearIdleFinishCandidate("waiting_approval");
30654
+ const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
30655
+ if (inCooldown && !modal) {
30656
+ if (this.approvalExitTimeout) {
30657
+ clearTimeout(this.approvalExitTimeout);
30658
+ this.approvalExitTimeout = null;
30637
30659
  }
30638
- if (!inCooldown) {
30639
- this.isWaitingForResponse = true;
30640
- this.setStatus("waiting_approval", "script_detect");
30641
- this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
30660
+ this.activeModal = null;
30661
+ if (this.isWaitingForResponse) {
30662
+ this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
30642
30663
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
30643
- this.armApprovalExitTimeout();
30644
- this.onStatusChange?.();
30645
- return;
30664
+ this.idleTimeout = setTimeout(() => {
30665
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
30666
+ if (this.shouldDeferIdleTimeoutFinish()) return;
30667
+ this.finishResponse();
30668
+ }
30669
+ }, this.timeouts.generatingIdle);
30670
+ } else {
30671
+ this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
30646
30672
  }
30673
+ this.onStatusChange?.();
30674
+ return;
30647
30675
  }
30648
- if (scriptStatus === "generating") {
30649
- this.clearIdleFinishCandidate("generating");
30650
- const effectiveScreenText = screenText || this.accumulatedBuffer;
30651
- const noActiveTurn = !this.currentTurnScope;
30652
- 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));
30653
- if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
30676
+ if (!inCooldown) {
30677
+ if (!modal) {
30678
+ LOG2.warn("CLI", `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
30654
30679
  return;
30655
30680
  }
30656
- if (prevStatus === "waiting_approval") {
30657
- if (this.approvalExitTimeout) {
30658
- clearTimeout(this.approvalExitTimeout);
30659
- this.approvalExitTimeout = null;
30660
- }
30661
- this.activeModal = null;
30662
- this.lastApprovalResolvedAt = Date.now();
30663
- }
30664
- if (!this.isWaitingForResponse) {
30665
- this.isWaitingForResponse = true;
30666
- this.responseBuffer = "";
30667
- }
30668
- this.setStatus("generating", "script_detect");
30681
+ this.isWaitingForResponse = true;
30682
+ this.setStatus("waiting_approval", "script_detect");
30683
+ this.activeModal = modal;
30669
30684
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
30670
- this.idleTimeout = setTimeout(() => {
30671
- if (this.isWaitingForResponse) {
30672
- if (this.shouldDeferIdleTimeoutFinish()) return;
30673
- this.finishResponse();
30674
- }
30675
- }, this.timeouts.generatingIdle);
30685
+ this.armApprovalExitTimeout();
30676
30686
  this.onStatusChange?.();
30687
+ }
30688
+ }
30689
+ applyGenerating(ctx) {
30690
+ const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
30691
+ this.clearIdleFinishCandidate("generating");
30692
+ const effectiveScreenText = screenText || this.accumulatedBuffer;
30693
+ const noActiveTurn = !this.currentTurnScope;
30694
+ 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));
30695
+ if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
30677
30696
  return;
30678
30697
  }
30679
- if (scriptStatus === "idle") {
30680
- if (prevStatus === "waiting_approval") {
30681
- if (this.approvalExitTimeout) {
30682
- clearTimeout(this.approvalExitTimeout);
30683
- this.approvalExitTimeout = null;
30684
- }
30685
- this.activeModal = null;
30686
- this.lastApprovalResolvedAt = Date.now();
30698
+ if (prevStatus === "waiting_approval") {
30699
+ if (this.approvalExitTimeout) {
30700
+ clearTimeout(this.approvalExitTimeout);
30701
+ this.approvalExitTimeout = null;
30687
30702
  }
30703
+ this.activeModal = null;
30704
+ this.lastApprovalResolvedAt = Date.now();
30705
+ }
30706
+ if (!this.isWaitingForResponse) {
30707
+ this.isWaitingForResponse = true;
30708
+ this.responseBuffer = "";
30709
+ }
30710
+ this.setStatus("generating", "script_detect");
30711
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
30712
+ this.idleTimeout = setTimeout(() => {
30688
30713
  if (this.isWaitingForResponse) {
30689
- const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
30690
- const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
30691
- const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
30692
- const hasAssistantTurn = !!lastParsedAssistant;
30693
- const assistantLength = lastParsedAssistant?.content?.length || 0;
30694
- const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
30695
- const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
30696
- const idleStableThresholdMs = idleFinishConfirmMs;
30697
- const idleReady = visibleIdlePrompt2 && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
30698
- const candidate = this.idleFinishCandidate;
30699
- const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
30700
- const canFinishImmediately = idleReady && candidateQuiet;
30701
- this.recordTrace("idle_decision", {
30702
- visibleIdlePrompt: visibleIdlePrompt2,
30703
- quietForMs,
30704
- screenStableMs,
30705
- hasAssistantTurn,
30706
- assistantLength,
30707
- hasModal: !!modal,
30708
- idleQuietThresholdMs,
30709
- idleStableThresholdMs,
30710
- idleReady,
30711
- idleFinishConfirmMs,
30712
- idleFinishCandidate: candidate,
30713
- candidateQuiet,
30714
- canFinishImmediately,
30715
- submitPendingUntil: this.submitPendingUntil,
30716
- responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
30717
- ...buildCliTraceParseSnapshot({
30718
- accumulatedBuffer: this.accumulatedBuffer,
30719
- accumulatedRawBuffer: this.accumulatedRawBuffer,
30720
- responseBuffer: this.responseBuffer,
30721
- partialResponse: this.responseBuffer,
30722
- scope: this.currentTurnScope
30723
- })
30724
- });
30725
- if (canFinishImmediately) {
30726
- this.clearIdleFinishCandidate("finish_response");
30727
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
30728
- this.finishResponse();
30729
- return;
30730
- }
30731
- if (idleReady) {
30732
- if (!candidate) {
30733
- this.armIdleFinishCandidate(assistantLength);
30734
- return;
30735
- }
30736
- } else {
30737
- this.clearIdleFinishCandidate("idle_not_ready");
30738
- }
30739
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
30740
- this.idleTimeout = setTimeout(() => {
30741
- if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
30742
- if (this.shouldDeferIdleTimeoutFinish()) return;
30743
- this.clearIdleFinishCandidate("idle_timeout_finish");
30744
- this.finishResponse();
30745
- }
30746
- }, this.timeouts.idleFinish);
30747
- } else if (prevStatus !== "idle") {
30714
+ if (this.shouldDeferIdleTimeoutFinish()) return;
30715
+ this.finishResponse();
30716
+ }
30717
+ }, this.timeouts.generatingIdle);
30718
+ this.onStatusChange?.();
30719
+ }
30720
+ applyIdle(ctx, now) {
30721
+ const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
30722
+ if (prevStatus === "waiting_approval") {
30723
+ if (this.approvalExitTimeout) {
30724
+ clearTimeout(this.approvalExitTimeout);
30725
+ this.approvalExitTimeout = null;
30726
+ }
30727
+ this.activeModal = null;
30728
+ this.lastApprovalResolvedAt = Date.now();
30729
+ }
30730
+ if (!this.isWaitingForResponse) {
30731
+ if (prevStatus !== "idle") {
30748
30732
  this.clearIdleFinishCandidate("idle_without_response");
30749
30733
  this.setStatus("idle", "script_detect");
30750
30734
  this.onStatusChange?.();
30751
30735
  }
30736
+ return;
30737
+ }
30738
+ const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
30739
+ const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
30740
+ const hasAssistantTurn = !!lastParsedAssistant;
30741
+ const assistantLength = lastParsedAssistant?.content?.length || 0;
30742
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
30743
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
30744
+ const idleReady = !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleFinishConfirmMs;
30745
+ const candidate = this.idleFinishCandidate;
30746
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
30747
+ this.recordTrace("idle_decision", {
30748
+ quietForMs,
30749
+ screenStableMs,
30750
+ hasAssistantTurn,
30751
+ assistantLength,
30752
+ hasModal: !!modal,
30753
+ idleQuietThresholdMs,
30754
+ idleStableThresholdMs: idleFinishConfirmMs,
30755
+ idleReady,
30756
+ idleFinishConfirmMs,
30757
+ idleFinishCandidate: candidate,
30758
+ candidateQuiet,
30759
+ canFinishImmediately: idleReady && candidateQuiet,
30760
+ submitPendingUntil: this.submitPendingUntil,
30761
+ responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
30762
+ ...buildCliTraceParseSnapshot({
30763
+ accumulatedBuffer: this.accumulatedBuffer,
30764
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
30765
+ responseBuffer: this.responseBuffer,
30766
+ partialResponse: this.responseBuffer,
30767
+ scope: this.currentTurnScope
30768
+ })
30769
+ });
30770
+ if (idleReady && candidateQuiet) {
30771
+ this.clearIdleFinishCandidate("finish_response");
30772
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
30773
+ this.finishResponse();
30774
+ return;
30752
30775
  }
30776
+ if (idleReady) {
30777
+ if (!candidate) {
30778
+ this.armIdleFinishCandidate(assistantLength);
30779
+ return;
30780
+ }
30781
+ } else {
30782
+ this.clearIdleFinishCandidate("idle_not_ready");
30783
+ }
30784
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
30785
+ this.idleTimeout = setTimeout(() => {
30786
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
30787
+ if (this.shouldDeferIdleTimeoutFinish()) return;
30788
+ this.clearIdleFinishCandidate("idle_timeout_finish");
30789
+ this.finishResponse();
30790
+ }
30791
+ }, this.timeouts.idleFinish);
30753
30792
  }
30754
30793
  finishResponse() {
30755
30794
  if (this.submitPendingUntil > Date.now()) return;
@@ -30788,26 +30827,7 @@ var require_dist2 = __commonJS({
30788
30827
  }, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
30789
30828
  return;
30790
30829
  }
30791
- if (this.responseTimeout) {
30792
- clearTimeout(this.responseTimeout);
30793
- this.responseTimeout = null;
30794
- }
30795
- if (this.idleTimeout) {
30796
- clearTimeout(this.idleTimeout);
30797
- this.idleTimeout = null;
30798
- }
30799
- if (this.approvalExitTimeout) {
30800
- clearTimeout(this.approvalExitTimeout);
30801
- this.approvalExitTimeout = null;
30802
- }
30803
- if (this.submitRetryTimer) {
30804
- clearTimeout(this.submitRetryTimer);
30805
- this.submitRetryTimer = null;
30806
- }
30807
- if (this.finishRetryTimer) {
30808
- clearTimeout(this.finishRetryTimer);
30809
- this.finishRetryTimer = null;
30810
- }
30830
+ this.clearAllTimers();
30811
30831
  this.responseBuffer = "";
30812
30832
  this.isWaitingForResponse = false;
30813
30833
  this.responseSettleIgnoreUntil = 0;
@@ -30819,18 +30839,12 @@ var require_dist2 = __commonJS({
30819
30839
  this.setStatus("idle", "response_finished");
30820
30840
  this.onStatusChange?.();
30821
30841
  }
30822
- maybeCommitVisibleIdleTranscript(parsed, options) {
30842
+ maybeCommitVisibleIdleTranscript(parsed) {
30823
30843
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
30824
30844
  if (!allowImmediateScriptIdleCommit) return false;
30825
30845
  if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
30826
30846
  return false;
30827
30847
  }
30828
- if (options?.requireVisibleAssistantCandidate) {
30829
- const candidateText = options.screenText || this.terminalScreen.getText() || "";
30830
- if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
30831
- return false;
30832
- }
30833
- }
30834
30848
  const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
30835
30849
  committedMessages: this.committedMessages,
30836
30850
  scope: this.currentTurnScope,
@@ -30839,33 +30853,8 @@ var require_dist2 = __commonJS({
30839
30853
  const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
30840
30854
  if (!visibleAssistant) return false;
30841
30855
  this.committedMessages = hydratedForIdleCommit;
30842
- const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
30843
- if (promptForTrim) {
30844
- const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
30845
- if (lastAssistantForTrim) {
30846
- lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
30847
- }
30848
- }
30849
- if (this.responseTimeout) {
30850
- clearTimeout(this.responseTimeout);
30851
- this.responseTimeout = null;
30852
- }
30853
- if (this.idleTimeout) {
30854
- clearTimeout(this.idleTimeout);
30855
- this.idleTimeout = null;
30856
- }
30857
- if (this.approvalExitTimeout) {
30858
- clearTimeout(this.approvalExitTimeout);
30859
- this.approvalExitTimeout = null;
30860
- }
30861
- if (this.submitRetryTimer) {
30862
- clearTimeout(this.submitRetryTimer);
30863
- this.submitRetryTimer = null;
30864
- }
30865
- if (this.finishRetryTimer) {
30866
- clearTimeout(this.finishRetryTimer);
30867
- this.finishRetryTimer = null;
30868
- }
30856
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
30857
+ this.clearAllTimers();
30869
30858
  this.syncMessageViews();
30870
30859
  this.responseBuffer = "";
30871
30860
  this.isWaitingForResponse = false;
@@ -30895,13 +30884,7 @@ var require_dist2 = __commonJS({
30895
30884
  scope: this.currentTurnScope,
30896
30885
  lastOutputAt: this.lastOutputAt
30897
30886
  });
30898
- const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
30899
- if (promptForTrim) {
30900
- const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
30901
- if (lastAssistantForTrim) {
30902
- lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
30903
- }
30904
- }
30887
+ this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
30905
30888
  this.syncMessageViews();
30906
30889
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
30907
30890
  if (this.currentTurnScope) {
@@ -30959,7 +30942,7 @@ var require_dist2 = __commonJS({
30959
30942
  screen: buildCliScreenSnapshot(screenText),
30960
30943
  tailScreen: buildCliScreenSnapshot(text.slice(-500))
30961
30944
  });
30962
- return this.refineDetectedStatus(status, text, screenText || "");
30945
+ return status;
30963
30946
  } catch (e) {
30964
30947
  LOG2.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
30965
30948
  return null;
@@ -30999,23 +30982,21 @@ var require_dist2 = __commonJS({
30999
30982
  if (!inApprovalCooldown) {
31000
30983
  return parsed;
31001
30984
  }
31002
- const startupModal = this.getStartupConfirmationModal(screenText || "");
31003
- const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
30985
+ const visibleModal = this.runParseApproval(recentBuffer);
31004
30986
  if (visibleModal) {
31005
30987
  return parsed;
31006
30988
  }
31007
30989
  const detectedStatus = this.runDetectStatus(recentBuffer);
31008
- const fallbackStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
30990
+ const resolvedStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
31009
30991
  return {
31010
30992
  ...parsed,
31011
- status: fallbackStatus,
30993
+ status: resolvedStatus,
31012
30994
  activeModal: null
31013
30995
  };
31014
30996
  }
31015
30997
  // ─── Public API (CliAdapter) ───────────────────
31016
30998
  getStatus() {
31017
- const screenText = this.terminalScreen.getText() || "";
31018
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
30999
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
31019
31000
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
31020
31001
  let effectiveModal = startupModal || this.activeModal;
31021
31002
  if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
@@ -31096,8 +31077,7 @@ var require_dist2 = __commonJS({
31096
31077
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
31097
31078
  }));
31098
31079
  const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === "assistant" && typeof message.content === "string" && message.content.trim());
31099
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
31100
- const shouldAdoptParsedIdleReplay = !this.currentTurnScope && !this.activeModal && !!parsedLastAssistant && parsedTranscriptIsRicherThanCommitted(parsedHydratedMessages, committedHydratedMessages) && (this.currentStatus === "idle" || this.currentStatus === "generating" && this.isWaitingForResponse && parsed.status === "idle" && visibleIdlePrompt);
31080
+ 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");
31101
31081
  if (shouldAdoptParsedIdleReplay) {
31102
31082
  this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
31103
31083
  committedMessages: this.committedMessages,
@@ -31226,17 +31206,9 @@ var require_dist2 = __commonJS({
31226
31206
  if (parsed && typeof parsed === "object") {
31227
31207
  Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
31228
31208
  }
31229
- const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
31230
- if (parsed && refinedStatus && parsed.status !== refinedStatus) {
31231
- parsed.status = refinedStatus;
31232
- }
31233
31209
  const normalizedParsed = this.suppressStaleParsedApproval(parsed, input.recentBuffer, input.screenText);
31234
- const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
31235
- if (normalizedParsed && Array.isArray(normalizedParsed.messages) && promptForTrim) {
31236
- const lastAssistant = [...normalizedParsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
31237
- if (lastAssistant) {
31238
- lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
31239
- }
31210
+ if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
31211
+ this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
31240
31212
  }
31241
31213
  this.parseErrorMessage = null;
31242
31214
  return normalizedParsed;
@@ -31264,16 +31236,11 @@ var require_dist2 = __commonJS({
31264
31236
  LOG2.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
31265
31237
  }
31266
31238
  }
31267
- if (!promptText && data) {
31268
- promptText = `Please fix the following issue:
31269
- ${data.title || ""}
31270
- ${data.explanation || ""}
31271
-
31272
- ${data.message || ""}`.trim();
31273
- }
31274
- if (promptText) {
31275
- await this.sendMessage(promptText);
31239
+ if (!promptText) {
31240
+ LOG2.warn("CLI", `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
31241
+ return;
31276
31242
  }
31243
+ await this.sendMessage(promptText);
31277
31244
  }
31278
31245
  async sendMessage(text) {
31279
31246
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
@@ -31291,9 +31258,7 @@ ${data.message || ""}`.trim();
31291
31258
  }
31292
31259
  if (!this.ready) {
31293
31260
  this.resolveStartupState("send_precheck");
31294
- const screenText = this.terminalScreen.getText() || "";
31295
- const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
31296
- if (hasPrompt && this.currentStatus === "idle") {
31261
+ if (this.runDetectStatus(this.recentOutputBuffer) === "idle" && this.currentStatus === "idle") {
31297
31262
  this.ready = true;
31298
31263
  this.startupParseGate = false;
31299
31264
  LOG2.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
@@ -31399,7 +31364,10 @@ ${data.message || ""}`.trim();
31399
31364
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
31400
31365
  const screenText2 = this.terminalScreen.getText();
31401
31366
  if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
31402
- 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;
31367
+ const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
31368
+ if (liveApproval) return;
31369
+ const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
31370
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
31403
31371
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
31404
31372
  LOG2.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
31405
31373
  this.recordTrace("submit_write", {
@@ -31436,6 +31404,10 @@ ${data.message || ""}`.trim();
31436
31404
  if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
31437
31405
  const screenText = this.terminalScreen.getText();
31438
31406
  if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
31407
+ const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
31408
+ if (liveApproval) return;
31409
+ const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
31410
+ if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
31439
31411
  LOG2.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
31440
31412
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
31441
31413
  this.recordTrace("submit_write", {
@@ -31567,44 +31539,9 @@ ${data.message || ""}`.trim();
31567
31539
  }
31568
31540
  shutdown() {
31569
31541
  this.clearIdleFinishCandidate("shutdown");
31570
- if (this.settleTimer) {
31571
- clearTimeout(this.settleTimer);
31572
- this.settleTimer = null;
31573
- }
31574
- if (this.approvalExitTimeout) {
31575
- clearTimeout(this.approvalExitTimeout);
31576
- this.approvalExitTimeout = null;
31577
- }
31578
- if (this.submitRetryTimer) {
31579
- clearTimeout(this.submitRetryTimer);
31580
- this.submitRetryTimer = null;
31581
- }
31582
- if (this.finishRetryTimer) {
31583
- clearTimeout(this.finishRetryTimer);
31584
- this.finishRetryTimer = null;
31585
- }
31586
- if (this.responseTimeout) {
31587
- clearTimeout(this.responseTimeout);
31588
- this.responseTimeout = null;
31589
- }
31590
- if (this.idleTimeout) {
31591
- clearTimeout(this.idleTimeout);
31592
- this.idleTimeout = null;
31593
- }
31594
- if (this.pendingScriptStatusTimer) {
31595
- clearTimeout(this.pendingScriptStatusTimer);
31596
- this.pendingScriptStatusTimer = null;
31597
- }
31598
- if (this.pendingOutputParseTimer) {
31599
- clearTimeout(this.pendingOutputParseTimer);
31600
- this.pendingOutputParseTimer = null;
31601
- }
31542
+ this.clearAllTimers();
31602
31543
  this.pendingOutputParseBuffer = "";
31603
31544
  this.pendingTerminalQueryTail = "";
31604
- if (this.ptyOutputFlushTimer) {
31605
- clearTimeout(this.ptyOutputFlushTimer);
31606
- this.ptyOutputFlushTimer = null;
31607
- }
31608
31545
  this.ptyOutputBuffer = "";
31609
31546
  this.finishRetryCount = 0;
31610
31547
  if (this.ptyProcess) {
@@ -31625,44 +31562,9 @@ ${data.message || ""}`.trim();
31625
31562
  }
31626
31563
  detach() {
31627
31564
  this.clearIdleFinishCandidate("detach");
31628
- if (this.settleTimer) {
31629
- clearTimeout(this.settleTimer);
31630
- this.settleTimer = null;
31631
- }
31632
- if (this.approvalExitTimeout) {
31633
- clearTimeout(this.approvalExitTimeout);
31634
- this.approvalExitTimeout = null;
31635
- }
31636
- if (this.submitRetryTimer) {
31637
- clearTimeout(this.submitRetryTimer);
31638
- this.submitRetryTimer = null;
31639
- }
31640
- if (this.finishRetryTimer) {
31641
- clearTimeout(this.finishRetryTimer);
31642
- this.finishRetryTimer = null;
31643
- }
31644
- if (this.responseTimeout) {
31645
- clearTimeout(this.responseTimeout);
31646
- this.responseTimeout = null;
31647
- }
31648
- if (this.idleTimeout) {
31649
- clearTimeout(this.idleTimeout);
31650
- this.idleTimeout = null;
31651
- }
31652
- if (this.pendingScriptStatusTimer) {
31653
- clearTimeout(this.pendingScriptStatusTimer);
31654
- this.pendingScriptStatusTimer = null;
31655
- }
31656
- if (this.pendingOutputParseTimer) {
31657
- clearTimeout(this.pendingOutputParseTimer);
31658
- this.pendingOutputParseTimer = null;
31659
- }
31565
+ this.clearAllTimers();
31660
31566
  this.pendingOutputParseBuffer = "";
31661
31567
  this.pendingTerminalQueryTail = "";
31662
- if (this.ptyOutputFlushTimer) {
31663
- clearTimeout(this.ptyOutputFlushTimer);
31664
- this.ptyOutputFlushTimer = null;
31665
- }
31666
31568
  this.ptyOutputBuffer = "";
31667
31569
  this.finishRetryCount = 0;
31668
31570
  if (this.ptyProcess) {
@@ -31724,8 +31626,7 @@ ${data.message || ""}`.trim();
31724
31626
  this.ptyProcess?.write(data);
31725
31627
  }
31726
31628
  resolveModal(buttonIndex) {
31727
- const screenText = this.terminalScreen.getText() || "";
31728
- let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
31629
+ let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
31729
31630
  if (!modal && typeof this.cliScripts?.parseOutput === "function") {
31730
31631
  try {
31731
31632
  const parsed = this.getScriptParsedStatus();
@@ -31756,12 +31657,7 @@ ${data.message || ""}`.trim();
31756
31657
  }
31757
31658
  this.setStatus("generating", "approval_resolved");
31758
31659
  this.onStatusChange?.();
31759
- 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 || ""));
31760
- if (startupTrustModal && buttonIndex in this.approvalKeys) {
31761
- this.ptyProcess.write(`${this.approvalKeys[buttonIndex]}\r`);
31762
- } else if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
31763
- this.ptyProcess.write("\r");
31764
- } else if (buttonIndex in this.approvalKeys) {
31660
+ if (buttonIndex in this.approvalKeys) {
31765
31661
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
31766
31662
  } else {
31767
31663
  const DOWN = "\x1B[B";
@@ -31781,7 +31677,7 @@ ${data.message || ""}`.trim();
31781
31677
  }
31782
31678
  getDebugState() {
31783
31679
  const screenText = sanitizeTerminalText(this.terminalScreen.getText());
31784
- const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
31680
+ const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
31785
31681
  const effectiveStatus = this.projectEffectiveStatus(startupModal);
31786
31682
  const effectiveReady = this.ready || !!startupModal;
31787
31683
  return {
@@ -39030,25 +38926,23 @@ ${effect.notification.body || ""}`.trim();
39030
38926
  const effectiveModal = statusModal || surfacedModal;
39031
38927
  const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" ? "waiting_approval" : status?.status;
39032
38928
  LOG2.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"}`);
39033
- if (effectiveStatus !== "waiting_approval" && !effectiveModal) {
38929
+ if (!effectiveModal) {
39034
38930
  return { success: false, error: "Not in approval state" };
39035
38931
  }
39036
- const buttons = effectiveModal?.buttons || ["Allow once", "Always allow", "Deny"];
38932
+ const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
39037
38933
  let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
39038
- if (buttonIndex < 0) {
38934
+ if (buttonIndex < 0 && button) {
39039
38935
  const btnLower = button.toLowerCase();
39040
38936
  buttonIndex = buttons.findIndex((b2) => b2.toLowerCase().includes(btnLower));
39041
38937
  }
38938
+ if (buttonIndex < 0 && (action === "reject" || action === "deny")) {
38939
+ buttonIndex = buttons.findIndex((b2) => /deny|reject|no/i.test(b2));
38940
+ }
38941
+ if (buttonIndex < 0 && (action === "always" || /always/i.test(button))) {
38942
+ buttonIndex = buttons.findIndex((b2) => /always/i.test(b2));
38943
+ }
39042
38944
  if (buttonIndex < 0) {
39043
- if (action === "reject" || action === "deny") {
39044
- buttonIndex = buttons.findIndex((b2) => /deny|reject|no/i.test(b2));
39045
- if (buttonIndex < 0) buttonIndex = buttons.length - 1;
39046
- } else if (action === "always" || /always/i.test(button)) {
39047
- buttonIndex = buttons.findIndex((b2) => /always/i.test(b2));
39048
- if (buttonIndex < 0) buttonIndex = 1;
39049
- } else {
39050
- buttonIndex = 0;
39051
- }
38945
+ return { success: false, error: "Approval action did not match any visible button" };
39052
38946
  }
39053
38947
  if (typeof adapter.resolveModal === "function") {
39054
38948
  adapter.resolveModal(buttonIndex);