@adhdev/daemon-core 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/cli-adapters/provider-cli-adapter.d.ts +7 -8
- package/dist/cli-adapters/provider-cli-shared.d.ts +0 -1
- package/dist/index.js +379 -486
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +379 -486
- package/dist/index.mjs.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +303 -446
- package/src/cli-adapters/provider-cli-parse.ts +47 -2
- package/src/cli-adapters/provider-cli-shared.ts +51 -5
- package/src/commands/chat-commands.ts +11 -13
package/dist/index.js
CHANGED
|
@@ -1512,8 +1512,35 @@ function promptLikelyVisible(screenText, promptSnippet) {
|
|
|
1512
1512
|
function normalizeScreenSnapshot(text) {
|
|
1513
1513
|
return sanitizeTerminalText(String(text || "")).replace(/\s+/g, " ").trim();
|
|
1514
1514
|
}
|
|
1515
|
+
function shouldReflowComparableMessageLines(lines) {
|
|
1516
|
+
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));
|
|
1517
|
+
}
|
|
1518
|
+
function joinComparableMessageLines(lines) {
|
|
1519
|
+
return lines.reduce((acc, line) => {
|
|
1520
|
+
const next = String(line || "").trim();
|
|
1521
|
+
if (!next) return acc;
|
|
1522
|
+
if (!acc) return next;
|
|
1523
|
+
if (/[,\d]$/.test(acc) && /^\d/.test(next)) {
|
|
1524
|
+
return `${acc}${next}`;
|
|
1525
|
+
}
|
|
1526
|
+
if (/[A-Za-z]$/.test(acc) && /^\d/.test(next)) {
|
|
1527
|
+
return `${acc}${next}`;
|
|
1528
|
+
}
|
|
1529
|
+
const fragmentMatch = acc.match(/([A-Za-z]{1,4})$/);
|
|
1530
|
+
const fragment = fragmentMatch ? fragmentMatch[1].toLowerCase() : "";
|
|
1531
|
+
if (/^[a-z]/.test(next) && fragment && !COMMON_COMPARABLE_WRAP_WORDS.has(fragment)) {
|
|
1532
|
+
return `${acc}${next}`;
|
|
1533
|
+
}
|
|
1534
|
+
return `${acc} ${next}`;
|
|
1535
|
+
}, "").replace(/\s+([,.;:!?])/g, "$1").replace(/(\d)\s+,/g, "$1,").replace(/\s+/g, " ").trim();
|
|
1536
|
+
}
|
|
1515
1537
|
function normalizeComparableMessageContent(text) {
|
|
1516
|
-
|
|
1538
|
+
const lines = String(text || "").split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
|
|
1539
|
+
if (lines.length === 0) return "";
|
|
1540
|
+
if (shouldReflowComparableMessageLines(lines)) {
|
|
1541
|
+
return joinComparableMessageLines(lines);
|
|
1542
|
+
}
|
|
1543
|
+
return lines.join(" ").replace(/\s+/g, " ").trim();
|
|
1517
1544
|
}
|
|
1518
1545
|
function trimPromptEchoPrefix(text, promptText) {
|
|
1519
1546
|
const prompt = normalizeComparableMessageContent(String(promptText || ""));
|
|
@@ -1546,9 +1573,6 @@ function getLastUserPromptText(messages) {
|
|
|
1546
1573
|
}
|
|
1547
1574
|
return "";
|
|
1548
1575
|
}
|
|
1549
|
-
function looksLikeConfirmOnlyLabel(label) {
|
|
1550
|
-
return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
|
|
1551
|
-
}
|
|
1552
1576
|
function parsePatternEntry(x) {
|
|
1553
1577
|
if (x instanceof RegExp) return x;
|
|
1554
1578
|
if (x && typeof x === "object" && typeof x.source === "string") {
|
|
@@ -1575,7 +1599,7 @@ function normalizeCliProviderForRuntime(raw) {
|
|
|
1575
1599
|
}
|
|
1576
1600
|
};
|
|
1577
1601
|
}
|
|
1578
|
-
var os8, path9, import_child_process4, buildCliSpawnEnv;
|
|
1602
|
+
var os8, path9, import_child_process4, buildCliSpawnEnv, COMMON_COMPARABLE_WRAP_WORDS;
|
|
1579
1603
|
var init_provider_cli_shared = __esm({
|
|
1580
1604
|
"src/cli-adapters/provider-cli-shared.ts"() {
|
|
1581
1605
|
"use strict";
|
|
@@ -1584,6 +1608,32 @@ var init_provider_cli_shared = __esm({
|
|
|
1584
1608
|
import_child_process4 = require("child_process");
|
|
1585
1609
|
init_spawn_env();
|
|
1586
1610
|
buildCliSpawnEnv = import_session_host_core.sanitizeSpawnEnv;
|
|
1611
|
+
COMMON_COMPARABLE_WRAP_WORDS = /* @__PURE__ */ new Set([
|
|
1612
|
+
"a",
|
|
1613
|
+
"an",
|
|
1614
|
+
"and",
|
|
1615
|
+
"as",
|
|
1616
|
+
"at",
|
|
1617
|
+
"but",
|
|
1618
|
+
"by",
|
|
1619
|
+
"for",
|
|
1620
|
+
"from",
|
|
1621
|
+
"in",
|
|
1622
|
+
"into",
|
|
1623
|
+
"is",
|
|
1624
|
+
"it",
|
|
1625
|
+
"of",
|
|
1626
|
+
"on",
|
|
1627
|
+
"or",
|
|
1628
|
+
"that",
|
|
1629
|
+
"the",
|
|
1630
|
+
"their",
|
|
1631
|
+
"then",
|
|
1632
|
+
"this",
|
|
1633
|
+
"to",
|
|
1634
|
+
"was",
|
|
1635
|
+
"with"
|
|
1636
|
+
]);
|
|
1587
1637
|
}
|
|
1588
1638
|
});
|
|
1589
1639
|
|
|
@@ -1639,8 +1689,44 @@ function hydrateCliParsedMessages(parsedMessages, options) {
|
|
|
1639
1689
|
};
|
|
1640
1690
|
});
|
|
1641
1691
|
}
|
|
1692
|
+
function chooseMoreComparableCliMessage(left, right) {
|
|
1693
|
+
const leftComparable = normalizeComparableMessageContent(left.content || "");
|
|
1694
|
+
const rightComparable = normalizeComparableMessageContent(right.content || "");
|
|
1695
|
+
if (leftComparable && leftComparable === rightComparable) {
|
|
1696
|
+
const leftNewlines = String(left.content || "").split(/\r\n|\n|\r/g).length - 1;
|
|
1697
|
+
const rightNewlines = String(right.content || "").split(/\r\n|\n|\r/g).length - 1;
|
|
1698
|
+
return rightNewlines < leftNewlines ? right : left;
|
|
1699
|
+
}
|
|
1700
|
+
return rightComparable.length > leftComparable.length ? right : left;
|
|
1701
|
+
}
|
|
1702
|
+
function dedupeConsecutiveComparableCliMessages(messages) {
|
|
1703
|
+
const deduped = [];
|
|
1704
|
+
for (const message of messages) {
|
|
1705
|
+
const current = {
|
|
1706
|
+
...message,
|
|
1707
|
+
content: typeof message.content === "string" ? message.content : String(message.content || "")
|
|
1708
|
+
};
|
|
1709
|
+
const previous = deduped[deduped.length - 1];
|
|
1710
|
+
if (!previous) {
|
|
1711
|
+
deduped.push(current);
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1714
|
+
const previousComparable = normalizeComparableMessageContent(previous.content || "");
|
|
1715
|
+
const currentComparable = normalizeComparableMessageContent(current.content || "");
|
|
1716
|
+
const sameRole = previous.role === current.role;
|
|
1717
|
+
const sameKind = (previous.kind || "standard") === (current.kind || "standard");
|
|
1718
|
+
const sameSender = (previous.senderName || "") === (current.senderName || "");
|
|
1719
|
+
const comparableMatch = previousComparable && previousComparable === currentComparable;
|
|
1720
|
+
if (sameRole && sameKind && sameSender && comparableMatch) {
|
|
1721
|
+
deduped[deduped.length - 1] = chooseMoreComparableCliMessage(previous, current);
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
deduped.push(current);
|
|
1725
|
+
}
|
|
1726
|
+
return deduped;
|
|
1727
|
+
}
|
|
1642
1728
|
function normalizeCliParsedMessages(parsedMessages, options) {
|
|
1643
|
-
return hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
|
|
1729
|
+
return dedupeConsecutiveComparableCliMessages(hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
|
|
1644
1730
|
role: message.role,
|
|
1645
1731
|
content: message.content,
|
|
1646
1732
|
timestamp: message.timestamp,
|
|
@@ -1650,7 +1736,7 @@ function normalizeCliParsedMessages(parsedMessages, options) {
|
|
|
1650
1736
|
index: message.index,
|
|
1651
1737
|
meta: message.meta,
|
|
1652
1738
|
senderName: message.senderName
|
|
1653
|
-
}));
|
|
1739
|
+
})));
|
|
1654
1740
|
}
|
|
1655
1741
|
function buildCliParseInput(options) {
|
|
1656
1742
|
const {
|
|
@@ -2313,7 +2399,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2313
2399
|
if (!hasStartupOutput) return;
|
|
2314
2400
|
const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
2315
2401
|
if (stableMs < 2e3) return;
|
|
2316
|
-
const startupModal = this.
|
|
2402
|
+
const startupModal = this.runParseApproval(this.recentOutputBuffer);
|
|
2317
2403
|
this.startupParseGate = false;
|
|
2318
2404
|
if (this.startupSettleTimer) {
|
|
2319
2405
|
clearTimeout(this.startupSettleTimer);
|
|
@@ -2370,11 +2456,17 @@ var init_provider_cli_adapter = __esm({
|
|
|
2370
2456
|
if (this.currentStatus !== "waiting_approval") return;
|
|
2371
2457
|
const tail = this.recentOutputBuffer;
|
|
2372
2458
|
const screenText = this.terminalScreen.getText() || "";
|
|
2373
|
-
const
|
|
2374
|
-
const modal = this.runParseApproval(tail) || startupModal;
|
|
2459
|
+
const modal = this.runParseApproval(tail);
|
|
2375
2460
|
const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
|
|
2376
2461
|
if (stillWaiting) {
|
|
2377
|
-
|
|
2462
|
+
if (!modal) {
|
|
2463
|
+
LOG.warn("CLI", `[${this.cliType}] approval timeout check found no actionable modal; keeping approval state fail-closed`);
|
|
2464
|
+
this.activeModal = null;
|
|
2465
|
+
this.onStatusChange?.();
|
|
2466
|
+
this.armApprovalExitTimeout();
|
|
2467
|
+
return;
|
|
2468
|
+
}
|
|
2469
|
+
this.activeModal = modal;
|
|
2378
2470
|
this.onStatusChange?.();
|
|
2379
2471
|
this.armApprovalExitTimeout();
|
|
2380
2472
|
return;
|
|
@@ -2386,81 +2478,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
2386
2478
|
this.onStatusChange?.();
|
|
2387
2479
|
}, 6e4);
|
|
2388
2480
|
}
|
|
2389
|
-
looksLikeVisibleIdlePrompt(screenText) {
|
|
2390
|
-
const text = String(screenText || "");
|
|
2391
|
-
if (!text.trim()) return false;
|
|
2392
|
-
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)) {
|
|
2393
|
-
return true;
|
|
2394
|
-
}
|
|
2395
|
-
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);
|
|
2396
|
-
}
|
|
2397
|
-
findLastMatchingLineIndex(lines, predicate) {
|
|
2398
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
2399
|
-
if (predicate(lines[index])) return index;
|
|
2400
|
-
}
|
|
2401
|
-
return -1;
|
|
2402
|
-
}
|
|
2403
|
-
looksLikeClaudeGeneratingLine(line) {
|
|
2404
|
-
const trimmed = String(line || "").trim();
|
|
2405
|
-
if (!trimmed) return false;
|
|
2406
|
-
if (/^⏵⏵\s+accept edits on/i.test(trimmed)) return false;
|
|
2407
|
-
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) return true;
|
|
2408
|
-
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+\S+.*\b(?:thinking|thought for \d+s?)\b/i.test(trimmed)) return true;
|
|
2409
|
-
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+[A-Z][A-Za-z-]{3,}ing\b.*(?:…|\.{3})/u.test(trimmed)) return true;
|
|
2410
|
-
if (/^[⏺•]\s+(?:Reading|Writing|Editing|Searching|Inspecting|Planning|Analyzing|Synthesizing|Drafting|Running|Listing|Scanning|Matching)\b.*(?:…|\.{3})/i.test(trimmed)) {
|
|
2411
|
-
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);
|
|
2412
|
-
}
|
|
2413
|
-
return false;
|
|
2414
|
-
}
|
|
2415
|
-
detectClaudeGeneratingOverride(screenText, tail) {
|
|
2416
|
-
if (this.cliType !== "claude-cli") return false;
|
|
2417
|
-
const source = sanitizeTerminalText(screenText || tail || "");
|
|
2418
|
-
if (!source.trim()) return false;
|
|
2419
|
-
const allLines = source.split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
|
|
2420
|
-
if (allLines.length === 0) return false;
|
|
2421
|
-
const recentLines = allLines.slice(-12);
|
|
2422
|
-
const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
|
|
2423
|
-
const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
|
|
2424
|
-
if (activeRegion.length === 0) return false;
|
|
2425
|
-
return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
|
|
2426
|
-
}
|
|
2427
|
-
refineDetectedStatus(status, tail, screenText) {
|
|
2428
|
-
if (this.startupParseGate) {
|
|
2429
|
-
return this.getStartupConfirmationModal(screenText || "") ? "waiting_approval" : "starting";
|
|
2430
|
-
}
|
|
2431
|
-
if (status === "waiting_approval") return status;
|
|
2432
|
-
if (this.detectClaudeGeneratingOverride(screenText || "", tail)) return "generating";
|
|
2433
|
-
return status;
|
|
2434
|
-
}
|
|
2435
|
-
looksLikeVisibleAssistantCandidate(screenText) {
|
|
2436
|
-
const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
|
|
2437
|
-
for (const line of lines) {
|
|
2438
|
-
const trimmed = String(line || "").trim();
|
|
2439
|
-
if (!trimmed) continue;
|
|
2440
|
-
if (/^➜\s+\S+/.test(trimmed)) continue;
|
|
2441
|
-
if (/^Update available!/i.test(trimmed)) continue;
|
|
2442
|
-
if (/Claude Code v\d/i.test(trimmed)) continue;
|
|
2443
|
-
if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
|
|
2444
|
-
if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
|
|
2445
|
-
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
|
|
2446
|
-
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
|
|
2447
|
-
const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
|
|
2448
|
-
if (!assistantMatch) continue;
|
|
2449
|
-
const content = assistantMatch[1].trim();
|
|
2450
|
-
if (!content) continue;
|
|
2451
|
-
if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
|
|
2452
|
-
if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
|
|
2453
|
-
return true;
|
|
2454
|
-
}
|
|
2455
|
-
return false;
|
|
2456
|
-
}
|
|
2457
2481
|
shouldRetryFinishResponse(commitResult) {
|
|
2458
2482
|
if (!this.currentTurnScope) return false;
|
|
2459
2483
|
if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
|
|
2460
2484
|
if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
|
|
2461
2485
|
if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
|
|
2462
|
-
|
|
2463
|
-
if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
|
|
2486
|
+
if (this.runDetectStatus(this.recentOutputBuffer) !== "idle") return false;
|
|
2464
2487
|
const now = Date.now();
|
|
2465
2488
|
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
2466
2489
|
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
@@ -2484,45 +2507,21 @@ var init_provider_cli_adapter = __esm({
|
|
|
2484
2507
|
}
|
|
2485
2508
|
return false;
|
|
2486
2509
|
}
|
|
2487
|
-
getStartupConfirmationModal(screenText) {
|
|
2488
|
-
const text = sanitizeTerminalText(String(screenText || ""));
|
|
2489
|
-
if (!text.trim()) return null;
|
|
2490
|
-
if (this.cliType === "claude-cli") {
|
|
2491
|
-
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);
|
|
2492
|
-
const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
|
|
2493
|
-
if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
|
|
2494
|
-
return {
|
|
2495
|
-
message: "Confirm Claude Code project trust",
|
|
2496
|
-
buttons: ["Continue"]
|
|
2497
|
-
};
|
|
2498
|
-
}
|
|
2499
|
-
}
|
|
2500
|
-
return null;
|
|
2501
|
-
}
|
|
2502
|
-
shouldResolveModalWithEnter(modal, buttonIndex) {
|
|
2503
|
-
if (!modal || buttonIndex !== 0) return false;
|
|
2504
|
-
const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
|
|
2505
|
-
if (buttons.length !== 1) return false;
|
|
2506
|
-
const buttonLabel = String(buttons[0] || "").trim();
|
|
2507
|
-
return looksLikeConfirmOnlyLabel(buttonLabel);
|
|
2508
|
-
}
|
|
2509
2510
|
async waitForInteractivePrompt(maxWaitMs = 5e3) {
|
|
2510
2511
|
const startedAt = Date.now();
|
|
2511
2512
|
let loggedWait = false;
|
|
2512
2513
|
while (Date.now() - startedAt < maxWaitMs) {
|
|
2513
2514
|
this.resolveStartupState("interactive_wait");
|
|
2514
2515
|
const screenText = this.terminalScreen.getText() || "";
|
|
2515
|
-
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
2516
2516
|
const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
|
|
2517
2517
|
const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
2518
2518
|
const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
|
|
2519
|
-
const
|
|
2520
|
-
const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "generating";
|
|
2519
|
+
const interactiveReady = status === "idle" && stableMs >= 700 && recentlyOutput >= 350;
|
|
2521
2520
|
if (interactiveReady) {
|
|
2522
2521
|
if (loggedWait) {
|
|
2523
2522
|
LOG.info(
|
|
2524
2523
|
"CLI",
|
|
2525
|
-
`[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput}
|
|
2524
|
+
`[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput})`
|
|
2526
2525
|
);
|
|
2527
2526
|
}
|
|
2528
2527
|
return;
|
|
@@ -2531,7 +2530,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2531
2530
|
loggedWait = true;
|
|
2532
2531
|
LOG.info(
|
|
2533
2532
|
"CLI",
|
|
2534
|
-
`[${this.cliType}] Waiting for interactive prompt:
|
|
2533
|
+
`[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
|
|
2535
2534
|
);
|
|
2536
2535
|
}
|
|
2537
2536
|
await new Promise((resolve12) => setTimeout(resolve12, 50));
|
|
@@ -2542,13 +2541,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
2542
2541
|
`[${this.cliType}] Interactive prompt wait timed out after ${maxWaitMs}ms; proceeding with screen=${JSON.stringify(summarizeCliTraceText(finalScreenText, 240)).slice(0, 280)}`
|
|
2543
2542
|
);
|
|
2544
2543
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
const
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
}
|
|
2544
|
+
trimLastAssistantEcho(messages, prompt) {
|
|
2545
|
+
if (!prompt) return;
|
|
2546
|
+
const last = [...messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
|
|
2547
|
+
if (last) last.content = trimPromptEchoPrefix(last.content, prompt);
|
|
2548
|
+
}
|
|
2549
|
+
clearAllTimers() {
|
|
2552
2550
|
if (this.responseTimeout) {
|
|
2553
2551
|
clearTimeout(this.responseTimeout);
|
|
2554
2552
|
this.responseTimeout = null;
|
|
@@ -2561,10 +2559,38 @@ var init_provider_cli_adapter = __esm({
|
|
|
2561
2559
|
clearTimeout(this.approvalExitTimeout);
|
|
2562
2560
|
this.approvalExitTimeout = null;
|
|
2563
2561
|
}
|
|
2562
|
+
if (this.submitRetryTimer) {
|
|
2563
|
+
clearTimeout(this.submitRetryTimer);
|
|
2564
|
+
this.submitRetryTimer = null;
|
|
2565
|
+
}
|
|
2564
2566
|
if (this.finishRetryTimer) {
|
|
2565
2567
|
clearTimeout(this.finishRetryTimer);
|
|
2566
2568
|
this.finishRetryTimer = null;
|
|
2567
2569
|
}
|
|
2570
|
+
if (this.settleTimer) {
|
|
2571
|
+
clearTimeout(this.settleTimer);
|
|
2572
|
+
this.settleTimer = null;
|
|
2573
|
+
}
|
|
2574
|
+
if (this.pendingScriptStatusTimer) {
|
|
2575
|
+
clearTimeout(this.pendingScriptStatusTimer);
|
|
2576
|
+
this.pendingScriptStatusTimer = null;
|
|
2577
|
+
}
|
|
2578
|
+
if (this.pendingOutputParseTimer) {
|
|
2579
|
+
clearTimeout(this.pendingOutputParseTimer);
|
|
2580
|
+
this.pendingOutputParseTimer = null;
|
|
2581
|
+
}
|
|
2582
|
+
if (this.ptyOutputFlushTimer) {
|
|
2583
|
+
clearTimeout(this.ptyOutputFlushTimer);
|
|
2584
|
+
this.ptyOutputFlushTimer = null;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
clearStaleIdleResponseGuard(reason) {
|
|
2588
|
+
const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
2589
|
+
const isIdle = this.runDetectStatus(this.recentOutputBuffer) === "idle";
|
|
2590
|
+
if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !isIdle || !!blockingModal) {
|
|
2591
|
+
return false;
|
|
2592
|
+
}
|
|
2593
|
+
this.clearAllTimers();
|
|
2568
2594
|
this.clearIdleFinishCandidate(reason);
|
|
2569
2595
|
this.responseBuffer = "";
|
|
2570
2596
|
this.isWaitingForResponse = false;
|
|
@@ -2574,10 +2600,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2574
2600
|
this.finishRetryCount = 0;
|
|
2575
2601
|
this.currentTurnScope = null;
|
|
2576
2602
|
this.activeModal = null;
|
|
2577
|
-
this.recordTrace("stale_idle_response_cleared", {
|
|
2578
|
-
reason,
|
|
2579
|
-
screenText: summarizeCliTraceText(screenText, 240)
|
|
2580
|
-
});
|
|
2603
|
+
this.recordTrace("stale_idle_response_cleared", { reason });
|
|
2581
2604
|
return true;
|
|
2582
2605
|
}
|
|
2583
2606
|
hasMeaningfulResponseBuffer(promptSnippet) {
|
|
@@ -2612,16 +2635,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
2612
2635
|
if (this.startupParseGate) {
|
|
2613
2636
|
return;
|
|
2614
2637
|
}
|
|
2615
|
-
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
2616
2638
|
const parsedTranscript = this.parseCurrentTranscript(
|
|
2617
2639
|
this.committedMessages,
|
|
2618
2640
|
this.responseBuffer,
|
|
2619
2641
|
this.currentTurnScope
|
|
2620
2642
|
);
|
|
2621
2643
|
const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsedTranscript.activeModal : null;
|
|
2622
|
-
const modal = this.runParseApproval(tail) || parsedModal
|
|
2644
|
+
const modal = this.runParseApproval(tail) || parsedModal;
|
|
2623
2645
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
2624
|
-
const scriptStatus =
|
|
2646
|
+
const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
|
|
2625
2647
|
const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
|
|
2626
2648
|
committedMessages: this.committedMessages,
|
|
2627
2649
|
scope: this.currentTurnScope,
|
|
@@ -2676,15 +2698,44 @@ var init_provider_cli_adapter = __esm({
|
|
|
2676
2698
|
}
|
|
2677
2699
|
if (!scriptStatus) return;
|
|
2678
2700
|
const prevStatus = this.currentStatus;
|
|
2679
|
-
const
|
|
2701
|
+
const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
|
|
2702
|
+
if (!this.applyPendingScriptStatusDebounce(ctx)) return;
|
|
2703
|
+
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
2704
|
+
LOG.info(
|
|
2705
|
+
"CLI",
|
|
2706
|
+
`[${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)}`
|
|
2707
|
+
);
|
|
2708
|
+
const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
|
|
2709
|
+
if (shouldHoldGenerating) {
|
|
2710
|
+
this.applyHoldGenerating(ctx, recentInteractiveActivity);
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
if (scriptStatus === "waiting_approval") {
|
|
2714
|
+
this.applyWaitingApproval(ctx);
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
if (scriptStatus === "generating") {
|
|
2718
|
+
this.applyGenerating(ctx);
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
if (scriptStatus === "idle") {
|
|
2722
|
+
this.applyIdle(ctx, now);
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
// Returns false if the caller should bail out (debounce pending).
|
|
2726
|
+
applyPendingScriptStatusDebounce(ctx) {
|
|
2727
|
+
const { now, scriptStatus, prevStatus } = ctx;
|
|
2728
|
+
const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
|
|
2729
|
+
if (!shouldDebounce) {
|
|
2680
2730
|
this.pendingScriptStatus = null;
|
|
2681
2731
|
this.pendingScriptStatusSince = 0;
|
|
2682
2732
|
if (this.pendingScriptStatusTimer) {
|
|
2683
2733
|
clearTimeout(this.pendingScriptStatusTimer);
|
|
2684
2734
|
this.pendingScriptStatusTimer = null;
|
|
2685
2735
|
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2736
|
+
return true;
|
|
2737
|
+
}
|
|
2738
|
+
const armPending = (delayMs) => {
|
|
2688
2739
|
if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
|
|
2689
2740
|
this.pendingScriptStatusTimer = setTimeout(() => {
|
|
2690
2741
|
this.pendingScriptStatusTimer = null;
|
|
@@ -2692,200 +2743,187 @@ var init_provider_cli_adapter = __esm({
|
|
|
2692
2743
|
this.evaluateSettled();
|
|
2693
2744
|
}, delayMs);
|
|
2694
2745
|
};
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
|
|
2701
|
-
return;
|
|
2702
|
-
}
|
|
2703
|
-
const elapsed = now - this.pendingScriptStatusSince;
|
|
2704
|
-
if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
|
|
2705
|
-
armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
|
|
2706
|
-
return;
|
|
2707
|
-
}
|
|
2708
|
-
} else {
|
|
2709
|
-
clearPendingScriptStatus();
|
|
2710
|
-
}
|
|
2711
|
-
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
2712
|
-
const statusActivityHoldMs = this.getStatusActivityHoldMs();
|
|
2713
|
-
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
2714
|
-
const visibleAssistantCandidate = this.looksLikeVisibleAssistantCandidate(screenText);
|
|
2715
|
-
if (this.currentTurnScope && this.cliType === "claude-cli") {
|
|
2716
|
-
LOG.info(
|
|
2717
|
-
"CLI",
|
|
2718
|
-
`[${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)}`
|
|
2719
|
-
);
|
|
2746
|
+
if (this.pendingScriptStatus !== scriptStatus) {
|
|
2747
|
+
this.pendingScriptStatus = scriptStatus;
|
|
2748
|
+
this.pendingScriptStatusSince = now;
|
|
2749
|
+
armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
|
|
2750
|
+
return false;
|
|
2720
2751
|
}
|
|
2721
|
-
const
|
|
2722
|
-
if (
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2726
|
-
this.idleTimeout = setTimeout(() => {
|
|
2727
|
-
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2728
|
-
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2729
|
-
this.finishResponse();
|
|
2730
|
-
}
|
|
2731
|
-
}, this.timeouts.generatingIdle);
|
|
2732
|
-
this.recordTrace("hold_generating_recent_activity", {
|
|
2733
|
-
scriptStatus,
|
|
2734
|
-
recentInteractiveActivity,
|
|
2735
|
-
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
2736
|
-
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
2737
|
-
holdMs: statusActivityHoldMs,
|
|
2738
|
-
...buildCliTraceParseSnapshot({
|
|
2739
|
-
accumulatedBuffer: this.accumulatedBuffer,
|
|
2740
|
-
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2741
|
-
responseBuffer: this.responseBuffer,
|
|
2742
|
-
partialResponse: this.responseBuffer,
|
|
2743
|
-
scope: this.currentTurnScope
|
|
2744
|
-
})
|
|
2745
|
-
});
|
|
2746
|
-
this.onStatusChange?.();
|
|
2747
|
-
return;
|
|
2752
|
+
const elapsed = now - this.pendingScriptStatusSince;
|
|
2753
|
+
if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
|
|
2754
|
+
armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
|
|
2755
|
+
return false;
|
|
2748
2756
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
this.
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
this.
|
|
2772
|
-
|
|
2757
|
+
return true;
|
|
2758
|
+
}
|
|
2759
|
+
applyHoldGenerating(ctx, recentInteractiveActivity) {
|
|
2760
|
+
const { scriptStatus } = ctx;
|
|
2761
|
+
this.clearIdleFinishCandidate("hold_generating_recent_activity");
|
|
2762
|
+
this.setStatus("generating", "recent_activity_hold");
|
|
2763
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2764
|
+
this.idleTimeout = setTimeout(() => {
|
|
2765
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2766
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2767
|
+
this.finishResponse();
|
|
2768
|
+
}
|
|
2769
|
+
}, this.timeouts.generatingIdle);
|
|
2770
|
+
this.recordTrace("hold_generating_recent_activity", {
|
|
2771
|
+
scriptStatus,
|
|
2772
|
+
recentInteractiveActivity,
|
|
2773
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
2774
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
2775
|
+
holdMs: this.getStatusActivityHoldMs(),
|
|
2776
|
+
...buildCliTraceParseSnapshot({
|
|
2777
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
2778
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2779
|
+
responseBuffer: this.responseBuffer,
|
|
2780
|
+
partialResponse: this.responseBuffer,
|
|
2781
|
+
scope: this.currentTurnScope
|
|
2782
|
+
})
|
|
2783
|
+
});
|
|
2784
|
+
this.onStatusChange?.();
|
|
2785
|
+
}
|
|
2786
|
+
applyWaitingApproval(ctx) {
|
|
2787
|
+
const { modal } = ctx;
|
|
2788
|
+
this.clearIdleFinishCandidate("waiting_approval");
|
|
2789
|
+
const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
2790
|
+
if (inCooldown && !modal) {
|
|
2791
|
+
if (this.approvalExitTimeout) {
|
|
2792
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2793
|
+
this.approvalExitTimeout = null;
|
|
2773
2794
|
}
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
this.setStatus("
|
|
2777
|
-
this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
|
|
2795
|
+
this.activeModal = null;
|
|
2796
|
+
if (this.isWaitingForResponse) {
|
|
2797
|
+
this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
|
|
2778
2798
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2779
|
-
this.
|
|
2780
|
-
|
|
2781
|
-
|
|
2799
|
+
this.idleTimeout = setTimeout(() => {
|
|
2800
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2801
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2802
|
+
this.finishResponse();
|
|
2803
|
+
}
|
|
2804
|
+
}, this.timeouts.generatingIdle);
|
|
2805
|
+
} else {
|
|
2806
|
+
this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
|
|
2782
2807
|
}
|
|
2808
|
+
this.onStatusChange?.();
|
|
2809
|
+
return;
|
|
2783
2810
|
}
|
|
2784
|
-
if (
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
const noActiveTurn = !this.currentTurnScope;
|
|
2788
|
-
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));
|
|
2789
|
-
if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
|
|
2811
|
+
if (!inCooldown) {
|
|
2812
|
+
if (!modal) {
|
|
2813
|
+
LOG.warn("CLI", `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
|
|
2790
2814
|
return;
|
|
2791
2815
|
}
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
this.approvalExitTimeout = null;
|
|
2796
|
-
}
|
|
2797
|
-
this.activeModal = null;
|
|
2798
|
-
this.lastApprovalResolvedAt = Date.now();
|
|
2799
|
-
}
|
|
2800
|
-
if (!this.isWaitingForResponse) {
|
|
2801
|
-
this.isWaitingForResponse = true;
|
|
2802
|
-
this.responseBuffer = "";
|
|
2803
|
-
}
|
|
2804
|
-
this.setStatus("generating", "script_detect");
|
|
2816
|
+
this.isWaitingForResponse = true;
|
|
2817
|
+
this.setStatus("waiting_approval", "script_detect");
|
|
2818
|
+
this.activeModal = modal;
|
|
2805
2819
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2806
|
-
this.
|
|
2807
|
-
if (this.isWaitingForResponse) {
|
|
2808
|
-
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2809
|
-
this.finishResponse();
|
|
2810
|
-
}
|
|
2811
|
-
}, this.timeouts.generatingIdle);
|
|
2820
|
+
this.armApprovalExitTimeout();
|
|
2812
2821
|
this.onStatusChange?.();
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
applyGenerating(ctx) {
|
|
2825
|
+
const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
|
|
2826
|
+
this.clearIdleFinishCandidate("generating");
|
|
2827
|
+
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
2828
|
+
const noActiveTurn = !this.currentTurnScope;
|
|
2829
|
+
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));
|
|
2830
|
+
if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
|
|
2813
2831
|
return;
|
|
2814
2832
|
}
|
|
2815
|
-
if (
|
|
2816
|
-
if (
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
this.approvalExitTimeout = null;
|
|
2820
|
-
}
|
|
2821
|
-
this.activeModal = null;
|
|
2822
|
-
this.lastApprovalResolvedAt = Date.now();
|
|
2833
|
+
if (prevStatus === "waiting_approval") {
|
|
2834
|
+
if (this.approvalExitTimeout) {
|
|
2835
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2836
|
+
this.approvalExitTimeout = null;
|
|
2823
2837
|
}
|
|
2838
|
+
this.activeModal = null;
|
|
2839
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
2840
|
+
}
|
|
2841
|
+
if (!this.isWaitingForResponse) {
|
|
2842
|
+
this.isWaitingForResponse = true;
|
|
2843
|
+
this.responseBuffer = "";
|
|
2844
|
+
}
|
|
2845
|
+
this.setStatus("generating", "script_detect");
|
|
2846
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2847
|
+
this.idleTimeout = setTimeout(() => {
|
|
2824
2848
|
if (this.isWaitingForResponse) {
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
hasModal: !!modal,
|
|
2844
|
-
idleQuietThresholdMs,
|
|
2845
|
-
idleStableThresholdMs,
|
|
2846
|
-
idleReady,
|
|
2847
|
-
idleFinishConfirmMs,
|
|
2848
|
-
idleFinishCandidate: candidate,
|
|
2849
|
-
candidateQuiet,
|
|
2850
|
-
canFinishImmediately,
|
|
2851
|
-
submitPendingUntil: this.submitPendingUntil,
|
|
2852
|
-
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
2853
|
-
...buildCliTraceParseSnapshot({
|
|
2854
|
-
accumulatedBuffer: this.accumulatedBuffer,
|
|
2855
|
-
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2856
|
-
responseBuffer: this.responseBuffer,
|
|
2857
|
-
partialResponse: this.responseBuffer,
|
|
2858
|
-
scope: this.currentTurnScope
|
|
2859
|
-
})
|
|
2860
|
-
});
|
|
2861
|
-
if (canFinishImmediately) {
|
|
2862
|
-
this.clearIdleFinishCandidate("finish_response");
|
|
2863
|
-
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2864
|
-
this.finishResponse();
|
|
2865
|
-
return;
|
|
2866
|
-
}
|
|
2867
|
-
if (idleReady) {
|
|
2868
|
-
if (!candidate) {
|
|
2869
|
-
this.armIdleFinishCandidate(assistantLength);
|
|
2870
|
-
return;
|
|
2871
|
-
}
|
|
2872
|
-
} else {
|
|
2873
|
-
this.clearIdleFinishCandidate("idle_not_ready");
|
|
2874
|
-
}
|
|
2875
|
-
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2876
|
-
this.idleTimeout = setTimeout(() => {
|
|
2877
|
-
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2878
|
-
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2879
|
-
this.clearIdleFinishCandidate("idle_timeout_finish");
|
|
2880
|
-
this.finishResponse();
|
|
2881
|
-
}
|
|
2882
|
-
}, this.timeouts.idleFinish);
|
|
2883
|
-
} else if (prevStatus !== "idle") {
|
|
2849
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2850
|
+
this.finishResponse();
|
|
2851
|
+
}
|
|
2852
|
+
}, this.timeouts.generatingIdle);
|
|
2853
|
+
this.onStatusChange?.();
|
|
2854
|
+
}
|
|
2855
|
+
applyIdle(ctx, now) {
|
|
2856
|
+
const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
|
|
2857
|
+
if (prevStatus === "waiting_approval") {
|
|
2858
|
+
if (this.approvalExitTimeout) {
|
|
2859
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2860
|
+
this.approvalExitTimeout = null;
|
|
2861
|
+
}
|
|
2862
|
+
this.activeModal = null;
|
|
2863
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
2864
|
+
}
|
|
2865
|
+
if (!this.isWaitingForResponse) {
|
|
2866
|
+
if (prevStatus !== "idle") {
|
|
2884
2867
|
this.clearIdleFinishCandidate("idle_without_response");
|
|
2885
2868
|
this.setStatus("idle", "script_detect");
|
|
2886
2869
|
this.onStatusChange?.();
|
|
2887
2870
|
}
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
2874
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
2875
|
+
const hasAssistantTurn = !!lastParsedAssistant;
|
|
2876
|
+
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
2877
|
+
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
2878
|
+
const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
|
|
2879
|
+
const idleReady = !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleFinishConfirmMs;
|
|
2880
|
+
const candidate = this.idleFinishCandidate;
|
|
2881
|
+
const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
|
|
2882
|
+
this.recordTrace("idle_decision", {
|
|
2883
|
+
quietForMs,
|
|
2884
|
+
screenStableMs,
|
|
2885
|
+
hasAssistantTurn,
|
|
2886
|
+
assistantLength,
|
|
2887
|
+
hasModal: !!modal,
|
|
2888
|
+
idleQuietThresholdMs,
|
|
2889
|
+
idleStableThresholdMs: idleFinishConfirmMs,
|
|
2890
|
+
idleReady,
|
|
2891
|
+
idleFinishConfirmMs,
|
|
2892
|
+
idleFinishCandidate: candidate,
|
|
2893
|
+
candidateQuiet,
|
|
2894
|
+
canFinishImmediately: idleReady && candidateQuiet,
|
|
2895
|
+
submitPendingUntil: this.submitPendingUntil,
|
|
2896
|
+
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
2897
|
+
...buildCliTraceParseSnapshot({
|
|
2898
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
2899
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2900
|
+
responseBuffer: this.responseBuffer,
|
|
2901
|
+
partialResponse: this.responseBuffer,
|
|
2902
|
+
scope: this.currentTurnScope
|
|
2903
|
+
})
|
|
2904
|
+
});
|
|
2905
|
+
if (idleReady && candidateQuiet) {
|
|
2906
|
+
this.clearIdleFinishCandidate("finish_response");
|
|
2907
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2908
|
+
this.finishResponse();
|
|
2909
|
+
return;
|
|
2888
2910
|
}
|
|
2911
|
+
if (idleReady) {
|
|
2912
|
+
if (!candidate) {
|
|
2913
|
+
this.armIdleFinishCandidate(assistantLength);
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
} else {
|
|
2917
|
+
this.clearIdleFinishCandidate("idle_not_ready");
|
|
2918
|
+
}
|
|
2919
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2920
|
+
this.idleTimeout = setTimeout(() => {
|
|
2921
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2922
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2923
|
+
this.clearIdleFinishCandidate("idle_timeout_finish");
|
|
2924
|
+
this.finishResponse();
|
|
2925
|
+
}
|
|
2926
|
+
}, this.timeouts.idleFinish);
|
|
2889
2927
|
}
|
|
2890
2928
|
finishResponse() {
|
|
2891
2929
|
if (this.submitPendingUntil > Date.now()) return;
|
|
@@ -2924,26 +2962,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2924
2962
|
}, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
|
|
2925
2963
|
return;
|
|
2926
2964
|
}
|
|
2927
|
-
|
|
2928
|
-
clearTimeout(this.responseTimeout);
|
|
2929
|
-
this.responseTimeout = null;
|
|
2930
|
-
}
|
|
2931
|
-
if (this.idleTimeout) {
|
|
2932
|
-
clearTimeout(this.idleTimeout);
|
|
2933
|
-
this.idleTimeout = null;
|
|
2934
|
-
}
|
|
2935
|
-
if (this.approvalExitTimeout) {
|
|
2936
|
-
clearTimeout(this.approvalExitTimeout);
|
|
2937
|
-
this.approvalExitTimeout = null;
|
|
2938
|
-
}
|
|
2939
|
-
if (this.submitRetryTimer) {
|
|
2940
|
-
clearTimeout(this.submitRetryTimer);
|
|
2941
|
-
this.submitRetryTimer = null;
|
|
2942
|
-
}
|
|
2943
|
-
if (this.finishRetryTimer) {
|
|
2944
|
-
clearTimeout(this.finishRetryTimer);
|
|
2945
|
-
this.finishRetryTimer = null;
|
|
2946
|
-
}
|
|
2965
|
+
this.clearAllTimers();
|
|
2947
2966
|
this.responseBuffer = "";
|
|
2948
2967
|
this.isWaitingForResponse = false;
|
|
2949
2968
|
this.responseSettleIgnoreUntil = 0;
|
|
@@ -2955,18 +2974,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
2955
2974
|
this.setStatus("idle", "response_finished");
|
|
2956
2975
|
this.onStatusChange?.();
|
|
2957
2976
|
}
|
|
2958
|
-
maybeCommitVisibleIdleTranscript(parsed
|
|
2977
|
+
maybeCommitVisibleIdleTranscript(parsed) {
|
|
2959
2978
|
const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
|
|
2960
2979
|
if (!allowImmediateScriptIdleCommit) return false;
|
|
2961
2980
|
if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
|
|
2962
2981
|
return false;
|
|
2963
2982
|
}
|
|
2964
|
-
if (options?.requireVisibleAssistantCandidate) {
|
|
2965
|
-
const candidateText = options.screenText || this.terminalScreen.getText() || "";
|
|
2966
|
-
if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
|
|
2967
|
-
return false;
|
|
2968
|
-
}
|
|
2969
|
-
}
|
|
2970
2983
|
const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
|
|
2971
2984
|
committedMessages: this.committedMessages,
|
|
2972
2985
|
scope: this.currentTurnScope,
|
|
@@ -2975,33 +2988,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
2975
2988
|
const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
|
|
2976
2989
|
if (!visibleAssistant) return false;
|
|
2977
2990
|
this.committedMessages = hydratedForIdleCommit;
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
2981
|
-
if (lastAssistantForTrim) {
|
|
2982
|
-
lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
|
|
2983
|
-
}
|
|
2984
|
-
}
|
|
2985
|
-
if (this.responseTimeout) {
|
|
2986
|
-
clearTimeout(this.responseTimeout);
|
|
2987
|
-
this.responseTimeout = null;
|
|
2988
|
-
}
|
|
2989
|
-
if (this.idleTimeout) {
|
|
2990
|
-
clearTimeout(this.idleTimeout);
|
|
2991
|
-
this.idleTimeout = null;
|
|
2992
|
-
}
|
|
2993
|
-
if (this.approvalExitTimeout) {
|
|
2994
|
-
clearTimeout(this.approvalExitTimeout);
|
|
2995
|
-
this.approvalExitTimeout = null;
|
|
2996
|
-
}
|
|
2997
|
-
if (this.submitRetryTimer) {
|
|
2998
|
-
clearTimeout(this.submitRetryTimer);
|
|
2999
|
-
this.submitRetryTimer = null;
|
|
3000
|
-
}
|
|
3001
|
-
if (this.finishRetryTimer) {
|
|
3002
|
-
clearTimeout(this.finishRetryTimer);
|
|
3003
|
-
this.finishRetryTimer = null;
|
|
3004
|
-
}
|
|
2991
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
2992
|
+
this.clearAllTimers();
|
|
3005
2993
|
this.syncMessageViews();
|
|
3006
2994
|
this.responseBuffer = "";
|
|
3007
2995
|
this.isWaitingForResponse = false;
|
|
@@ -3031,13 +3019,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3031
3019
|
scope: this.currentTurnScope,
|
|
3032
3020
|
lastOutputAt: this.lastOutputAt
|
|
3033
3021
|
});
|
|
3034
|
-
|
|
3035
|
-
if (promptForTrim) {
|
|
3036
|
-
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
3037
|
-
if (lastAssistantForTrim) {
|
|
3038
|
-
lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
|
|
3039
|
-
}
|
|
3040
|
-
}
|
|
3022
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
3041
3023
|
this.syncMessageViews();
|
|
3042
3024
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
3043
3025
|
if (this.currentTurnScope) {
|
|
@@ -3095,7 +3077,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3095
3077
|
screen: buildCliScreenSnapshot(screenText),
|
|
3096
3078
|
tailScreen: buildCliScreenSnapshot(text.slice(-500))
|
|
3097
3079
|
});
|
|
3098
|
-
return
|
|
3080
|
+
return status;
|
|
3099
3081
|
} catch (e) {
|
|
3100
3082
|
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
3101
3083
|
return null;
|
|
@@ -3135,23 +3117,21 @@ var init_provider_cli_adapter = __esm({
|
|
|
3135
3117
|
if (!inApprovalCooldown) {
|
|
3136
3118
|
return parsed;
|
|
3137
3119
|
}
|
|
3138
|
-
const
|
|
3139
|
-
const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
|
|
3120
|
+
const visibleModal = this.runParseApproval(recentBuffer);
|
|
3140
3121
|
if (visibleModal) {
|
|
3141
3122
|
return parsed;
|
|
3142
3123
|
}
|
|
3143
3124
|
const detectedStatus = this.runDetectStatus(recentBuffer);
|
|
3144
|
-
const
|
|
3125
|
+
const resolvedStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
|
|
3145
3126
|
return {
|
|
3146
3127
|
...parsed,
|
|
3147
|
-
status:
|
|
3128
|
+
status: resolvedStatus,
|
|
3148
3129
|
activeModal: null
|
|
3149
3130
|
};
|
|
3150
3131
|
}
|
|
3151
3132
|
// ─── Public API (CliAdapter) ───────────────────
|
|
3152
3133
|
getStatus() {
|
|
3153
|
-
const
|
|
3154
|
-
const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
|
|
3134
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
3155
3135
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
3156
3136
|
let effectiveModal = startupModal || this.activeModal;
|
|
3157
3137
|
if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
|
|
@@ -3232,8 +3212,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3232
3212
|
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
3233
3213
|
}));
|
|
3234
3214
|
const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === "assistant" && typeof message.content === "string" && message.content.trim());
|
|
3235
|
-
const
|
|
3236
|
-
const shouldAdoptParsedIdleReplay = !this.currentTurnScope && !this.activeModal && !!parsedLastAssistant && parsedTranscriptIsRicherThanCommitted(parsedHydratedMessages, committedHydratedMessages) && (this.currentStatus === "idle" || this.currentStatus === "generating" && this.isWaitingForResponse && parsed.status === "idle" && visibleIdlePrompt);
|
|
3215
|
+
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");
|
|
3237
3216
|
if (shouldAdoptParsedIdleReplay) {
|
|
3238
3217
|
this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
|
|
3239
3218
|
committedMessages: this.committedMessages,
|
|
@@ -3362,17 +3341,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
3362
3341
|
if (parsed && typeof parsed === "object") {
|
|
3363
3342
|
Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
|
|
3364
3343
|
}
|
|
3365
|
-
const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
|
|
3366
|
-
if (parsed && refinedStatus && parsed.status !== refinedStatus) {
|
|
3367
|
-
parsed.status = refinedStatus;
|
|
3368
|
-
}
|
|
3369
3344
|
const normalizedParsed = this.suppressStaleParsedApproval(parsed, input.recentBuffer, input.screenText);
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
const lastAssistant = [...normalizedParsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
|
|
3373
|
-
if (lastAssistant) {
|
|
3374
|
-
lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
|
|
3375
|
-
}
|
|
3345
|
+
if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
|
|
3346
|
+
this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
|
|
3376
3347
|
}
|
|
3377
3348
|
this.parseErrorMessage = null;
|
|
3378
3349
|
return normalizedParsed;
|
|
@@ -3400,16 +3371,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
3400
3371
|
LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
|
|
3401
3372
|
}
|
|
3402
3373
|
}
|
|
3403
|
-
if (!promptText
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
${data.explanation || ""}
|
|
3407
|
-
|
|
3408
|
-
${data.message || ""}`.trim();
|
|
3409
|
-
}
|
|
3410
|
-
if (promptText) {
|
|
3411
|
-
await this.sendMessage(promptText);
|
|
3374
|
+
if (!promptText) {
|
|
3375
|
+
LOG.warn("CLI", `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
|
|
3376
|
+
return;
|
|
3412
3377
|
}
|
|
3378
|
+
await this.sendMessage(promptText);
|
|
3413
3379
|
}
|
|
3414
3380
|
async sendMessage(text) {
|
|
3415
3381
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
@@ -3427,9 +3393,7 @@ ${data.message || ""}`.trim();
|
|
|
3427
3393
|
}
|
|
3428
3394
|
if (!this.ready) {
|
|
3429
3395
|
this.resolveStartupState("send_precheck");
|
|
3430
|
-
|
|
3431
|
-
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
3432
|
-
if (hasPrompt && this.currentStatus === "idle") {
|
|
3396
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === "idle" && this.currentStatus === "idle") {
|
|
3433
3397
|
this.ready = true;
|
|
3434
3398
|
this.startupParseGate = false;
|
|
3435
3399
|
LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
@@ -3535,7 +3499,10 @@ ${data.message || ""}`.trim();
|
|
|
3535
3499
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
3536
3500
|
const screenText2 = this.terminalScreen.getText();
|
|
3537
3501
|
if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
|
|
3538
|
-
|
|
3502
|
+
const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
|
|
3503
|
+
if (liveApproval) return;
|
|
3504
|
+
const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
|
|
3505
|
+
if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
|
|
3539
3506
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
3540
3507
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
3541
3508
|
this.recordTrace("submit_write", {
|
|
@@ -3572,6 +3539,10 @@ ${data.message || ""}`.trim();
|
|
|
3572
3539
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
3573
3540
|
const screenText = this.terminalScreen.getText();
|
|
3574
3541
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
3542
|
+
const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
|
|
3543
|
+
if (liveApproval) return;
|
|
3544
|
+
const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
|
|
3545
|
+
if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
|
|
3575
3546
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
3576
3547
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
3577
3548
|
this.recordTrace("submit_write", {
|
|
@@ -3703,44 +3674,9 @@ ${data.message || ""}`.trim();
|
|
|
3703
3674
|
}
|
|
3704
3675
|
shutdown() {
|
|
3705
3676
|
this.clearIdleFinishCandidate("shutdown");
|
|
3706
|
-
|
|
3707
|
-
clearTimeout(this.settleTimer);
|
|
3708
|
-
this.settleTimer = null;
|
|
3709
|
-
}
|
|
3710
|
-
if (this.approvalExitTimeout) {
|
|
3711
|
-
clearTimeout(this.approvalExitTimeout);
|
|
3712
|
-
this.approvalExitTimeout = null;
|
|
3713
|
-
}
|
|
3714
|
-
if (this.submitRetryTimer) {
|
|
3715
|
-
clearTimeout(this.submitRetryTimer);
|
|
3716
|
-
this.submitRetryTimer = null;
|
|
3717
|
-
}
|
|
3718
|
-
if (this.finishRetryTimer) {
|
|
3719
|
-
clearTimeout(this.finishRetryTimer);
|
|
3720
|
-
this.finishRetryTimer = null;
|
|
3721
|
-
}
|
|
3722
|
-
if (this.responseTimeout) {
|
|
3723
|
-
clearTimeout(this.responseTimeout);
|
|
3724
|
-
this.responseTimeout = null;
|
|
3725
|
-
}
|
|
3726
|
-
if (this.idleTimeout) {
|
|
3727
|
-
clearTimeout(this.idleTimeout);
|
|
3728
|
-
this.idleTimeout = null;
|
|
3729
|
-
}
|
|
3730
|
-
if (this.pendingScriptStatusTimer) {
|
|
3731
|
-
clearTimeout(this.pendingScriptStatusTimer);
|
|
3732
|
-
this.pendingScriptStatusTimer = null;
|
|
3733
|
-
}
|
|
3734
|
-
if (this.pendingOutputParseTimer) {
|
|
3735
|
-
clearTimeout(this.pendingOutputParseTimer);
|
|
3736
|
-
this.pendingOutputParseTimer = null;
|
|
3737
|
-
}
|
|
3677
|
+
this.clearAllTimers();
|
|
3738
3678
|
this.pendingOutputParseBuffer = "";
|
|
3739
3679
|
this.pendingTerminalQueryTail = "";
|
|
3740
|
-
if (this.ptyOutputFlushTimer) {
|
|
3741
|
-
clearTimeout(this.ptyOutputFlushTimer);
|
|
3742
|
-
this.ptyOutputFlushTimer = null;
|
|
3743
|
-
}
|
|
3744
3680
|
this.ptyOutputBuffer = "";
|
|
3745
3681
|
this.finishRetryCount = 0;
|
|
3746
3682
|
if (this.ptyProcess) {
|
|
@@ -3761,44 +3697,9 @@ ${data.message || ""}`.trim();
|
|
|
3761
3697
|
}
|
|
3762
3698
|
detach() {
|
|
3763
3699
|
this.clearIdleFinishCandidate("detach");
|
|
3764
|
-
|
|
3765
|
-
clearTimeout(this.settleTimer);
|
|
3766
|
-
this.settleTimer = null;
|
|
3767
|
-
}
|
|
3768
|
-
if (this.approvalExitTimeout) {
|
|
3769
|
-
clearTimeout(this.approvalExitTimeout);
|
|
3770
|
-
this.approvalExitTimeout = null;
|
|
3771
|
-
}
|
|
3772
|
-
if (this.submitRetryTimer) {
|
|
3773
|
-
clearTimeout(this.submitRetryTimer);
|
|
3774
|
-
this.submitRetryTimer = null;
|
|
3775
|
-
}
|
|
3776
|
-
if (this.finishRetryTimer) {
|
|
3777
|
-
clearTimeout(this.finishRetryTimer);
|
|
3778
|
-
this.finishRetryTimer = null;
|
|
3779
|
-
}
|
|
3780
|
-
if (this.responseTimeout) {
|
|
3781
|
-
clearTimeout(this.responseTimeout);
|
|
3782
|
-
this.responseTimeout = null;
|
|
3783
|
-
}
|
|
3784
|
-
if (this.idleTimeout) {
|
|
3785
|
-
clearTimeout(this.idleTimeout);
|
|
3786
|
-
this.idleTimeout = null;
|
|
3787
|
-
}
|
|
3788
|
-
if (this.pendingScriptStatusTimer) {
|
|
3789
|
-
clearTimeout(this.pendingScriptStatusTimer);
|
|
3790
|
-
this.pendingScriptStatusTimer = null;
|
|
3791
|
-
}
|
|
3792
|
-
if (this.pendingOutputParseTimer) {
|
|
3793
|
-
clearTimeout(this.pendingOutputParseTimer);
|
|
3794
|
-
this.pendingOutputParseTimer = null;
|
|
3795
|
-
}
|
|
3700
|
+
this.clearAllTimers();
|
|
3796
3701
|
this.pendingOutputParseBuffer = "";
|
|
3797
3702
|
this.pendingTerminalQueryTail = "";
|
|
3798
|
-
if (this.ptyOutputFlushTimer) {
|
|
3799
|
-
clearTimeout(this.ptyOutputFlushTimer);
|
|
3800
|
-
this.ptyOutputFlushTimer = null;
|
|
3801
|
-
}
|
|
3802
3703
|
this.ptyOutputBuffer = "";
|
|
3803
3704
|
this.finishRetryCount = 0;
|
|
3804
3705
|
if (this.ptyProcess) {
|
|
@@ -3860,8 +3761,7 @@ ${data.message || ""}`.trim();
|
|
|
3860
3761
|
this.ptyProcess?.write(data);
|
|
3861
3762
|
}
|
|
3862
3763
|
resolveModal(buttonIndex) {
|
|
3863
|
-
|
|
3864
|
-
let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
|
|
3764
|
+
let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
3865
3765
|
if (!modal && typeof this.cliScripts?.parseOutput === "function") {
|
|
3866
3766
|
try {
|
|
3867
3767
|
const parsed = this.getScriptParsedStatus();
|
|
@@ -3892,12 +3792,7 @@ ${data.message || ""}`.trim();
|
|
|
3892
3792
|
}
|
|
3893
3793
|
this.setStatus("generating", "approval_resolved");
|
|
3894
3794
|
this.onStatusChange?.();
|
|
3895
|
-
|
|
3896
|
-
if (startupTrustModal && buttonIndex in this.approvalKeys) {
|
|
3897
|
-
this.ptyProcess.write(`${this.approvalKeys[buttonIndex]}\r`);
|
|
3898
|
-
} else if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
|
|
3899
|
-
this.ptyProcess.write("\r");
|
|
3900
|
-
} else if (buttonIndex in this.approvalKeys) {
|
|
3795
|
+
if (buttonIndex in this.approvalKeys) {
|
|
3901
3796
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
3902
3797
|
} else {
|
|
3903
3798
|
const DOWN = "\x1B[B";
|
|
@@ -3917,7 +3812,7 @@ ${data.message || ""}`.trim();
|
|
|
3917
3812
|
}
|
|
3918
3813
|
getDebugState() {
|
|
3919
3814
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
3920
|
-
const startupModal = this.startupParseGate ? this.
|
|
3815
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
3921
3816
|
const effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
3922
3817
|
const effectiveReady = this.ready || !!startupModal;
|
|
3923
3818
|
return {
|
|
@@ -11250,25 +11145,23 @@ async function handleResolveAction(h, args) {
|
|
|
11250
11145
|
const effectiveModal = statusModal || surfacedModal;
|
|
11251
11146
|
const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" ? "waiting_approval" : status?.status;
|
|
11252
11147
|
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"}`);
|
|
11253
|
-
if (
|
|
11148
|
+
if (!effectiveModal) {
|
|
11254
11149
|
return { success: false, error: "Not in approval state" };
|
|
11255
11150
|
}
|
|
11256
|
-
const buttons = effectiveModal
|
|
11151
|
+
const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
|
|
11257
11152
|
let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
|
|
11258
|
-
if (buttonIndex < 0) {
|
|
11153
|
+
if (buttonIndex < 0 && button) {
|
|
11259
11154
|
const btnLower = button.toLowerCase();
|
|
11260
11155
|
buttonIndex = buttons.findIndex((b) => b.toLowerCase().includes(btnLower));
|
|
11261
11156
|
}
|
|
11157
|
+
if (buttonIndex < 0 && (action === "reject" || action === "deny")) {
|
|
11158
|
+
buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
|
|
11159
|
+
}
|
|
11160
|
+
if (buttonIndex < 0 && (action === "always" || /always/i.test(button))) {
|
|
11161
|
+
buttonIndex = buttons.findIndex((b) => /always/i.test(b));
|
|
11162
|
+
}
|
|
11262
11163
|
if (buttonIndex < 0) {
|
|
11263
|
-
|
|
11264
|
-
buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
|
|
11265
|
-
if (buttonIndex < 0) buttonIndex = buttons.length - 1;
|
|
11266
|
-
} else if (action === "always" || /always/i.test(button)) {
|
|
11267
|
-
buttonIndex = buttons.findIndex((b) => /always/i.test(b));
|
|
11268
|
-
if (buttonIndex < 0) buttonIndex = 1;
|
|
11269
|
-
} else {
|
|
11270
|
-
buttonIndex = 0;
|
|
11271
|
-
}
|
|
11164
|
+
return { success: false, error: "Approval action did not match any visible button" };
|
|
11272
11165
|
}
|
|
11273
11166
|
if (typeof adapter.resolveModal === "function") {
|
|
11274
11167
|
adapter.resolveModal(buttonIndex);
|