@adhdev/daemon-core 0.9.6 → 0.9.7
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 -487
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +379 -487
- 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 +302 -448
- 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,14 @@ 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
|
|
2623
|
-
const
|
|
2624
|
-
const scriptStatus = startupModal ? "waiting_approval" : parsedModal && parsedTranscript?.status === "waiting_approval" ? "waiting_approval" : rawScriptStatus;
|
|
2644
|
+
const modal = this.runParseApproval(tail) || parsedModal;
|
|
2645
|
+
const scriptStatus = this.runDetectStatus(tail);
|
|
2625
2646
|
const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
|
|
2626
2647
|
committedMessages: this.committedMessages,
|
|
2627
2648
|
scope: this.currentTurnScope,
|
|
@@ -2676,15 +2697,44 @@ var init_provider_cli_adapter = __esm({
|
|
|
2676
2697
|
}
|
|
2677
2698
|
if (!scriptStatus) return;
|
|
2678
2699
|
const prevStatus = this.currentStatus;
|
|
2679
|
-
const
|
|
2700
|
+
const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
|
|
2701
|
+
if (!this.applyPendingScriptStatusDebounce(ctx)) return;
|
|
2702
|
+
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
2703
|
+
LOG.info(
|
|
2704
|
+
"CLI",
|
|
2705
|
+
`[${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)}`
|
|
2706
|
+
);
|
|
2707
|
+
const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
|
|
2708
|
+
if (shouldHoldGenerating) {
|
|
2709
|
+
this.applyHoldGenerating(ctx, recentInteractiveActivity);
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
if (scriptStatus === "waiting_approval") {
|
|
2713
|
+
this.applyWaitingApproval(ctx);
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
if (scriptStatus === "generating") {
|
|
2717
|
+
this.applyGenerating(ctx);
|
|
2718
|
+
return;
|
|
2719
|
+
}
|
|
2720
|
+
if (scriptStatus === "idle") {
|
|
2721
|
+
this.applyIdle(ctx, now);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
// Returns false if the caller should bail out (debounce pending).
|
|
2725
|
+
applyPendingScriptStatusDebounce(ctx) {
|
|
2726
|
+
const { now, scriptStatus, prevStatus } = ctx;
|
|
2727
|
+
const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
|
|
2728
|
+
if (!shouldDebounce) {
|
|
2680
2729
|
this.pendingScriptStatus = null;
|
|
2681
2730
|
this.pendingScriptStatusSince = 0;
|
|
2682
2731
|
if (this.pendingScriptStatusTimer) {
|
|
2683
2732
|
clearTimeout(this.pendingScriptStatusTimer);
|
|
2684
2733
|
this.pendingScriptStatusTimer = null;
|
|
2685
2734
|
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2735
|
+
return true;
|
|
2736
|
+
}
|
|
2737
|
+
const armPending = (delayMs) => {
|
|
2688
2738
|
if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
|
|
2689
2739
|
this.pendingScriptStatusTimer = setTimeout(() => {
|
|
2690
2740
|
this.pendingScriptStatusTimer = null;
|
|
@@ -2692,200 +2742,187 @@ var init_provider_cli_adapter = __esm({
|
|
|
2692
2742
|
this.evaluateSettled();
|
|
2693
2743
|
}, delayMs);
|
|
2694
2744
|
};
|
|
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
|
-
);
|
|
2745
|
+
if (this.pendingScriptStatus !== scriptStatus) {
|
|
2746
|
+
this.pendingScriptStatus = scriptStatus;
|
|
2747
|
+
this.pendingScriptStatusSince = now;
|
|
2748
|
+
armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
|
|
2749
|
+
return false;
|
|
2720
2750
|
}
|
|
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;
|
|
2751
|
+
const elapsed = now - this.pendingScriptStatusSince;
|
|
2752
|
+
if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
|
|
2753
|
+
armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
|
|
2754
|
+
return false;
|
|
2748
2755
|
}
|
|
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
|
-
|
|
2756
|
+
return true;
|
|
2757
|
+
}
|
|
2758
|
+
applyHoldGenerating(ctx, recentInteractiveActivity) {
|
|
2759
|
+
const { scriptStatus } = ctx;
|
|
2760
|
+
this.clearIdleFinishCandidate("hold_generating_recent_activity");
|
|
2761
|
+
this.setStatus("generating", "recent_activity_hold");
|
|
2762
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2763
|
+
this.idleTimeout = setTimeout(() => {
|
|
2764
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2765
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2766
|
+
this.finishResponse();
|
|
2767
|
+
}
|
|
2768
|
+
}, this.timeouts.generatingIdle);
|
|
2769
|
+
this.recordTrace("hold_generating_recent_activity", {
|
|
2770
|
+
scriptStatus,
|
|
2771
|
+
recentInteractiveActivity,
|
|
2772
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
2773
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
2774
|
+
holdMs: this.getStatusActivityHoldMs(),
|
|
2775
|
+
...buildCliTraceParseSnapshot({
|
|
2776
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
2777
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2778
|
+
responseBuffer: this.responseBuffer,
|
|
2779
|
+
partialResponse: this.responseBuffer,
|
|
2780
|
+
scope: this.currentTurnScope
|
|
2781
|
+
})
|
|
2782
|
+
});
|
|
2783
|
+
this.onStatusChange?.();
|
|
2784
|
+
}
|
|
2785
|
+
applyWaitingApproval(ctx) {
|
|
2786
|
+
const { modal } = ctx;
|
|
2787
|
+
this.clearIdleFinishCandidate("waiting_approval");
|
|
2788
|
+
const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
2789
|
+
if (inCooldown && !modal) {
|
|
2790
|
+
if (this.approvalExitTimeout) {
|
|
2791
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2792
|
+
this.approvalExitTimeout = null;
|
|
2773
2793
|
}
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
this.setStatus("
|
|
2777
|
-
this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
|
|
2794
|
+
this.activeModal = null;
|
|
2795
|
+
if (this.isWaitingForResponse) {
|
|
2796
|
+
this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
|
|
2778
2797
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2779
|
-
this.
|
|
2780
|
-
|
|
2781
|
-
|
|
2798
|
+
this.idleTimeout = setTimeout(() => {
|
|
2799
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2800
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2801
|
+
this.finishResponse();
|
|
2802
|
+
}
|
|
2803
|
+
}, this.timeouts.generatingIdle);
|
|
2804
|
+
} else {
|
|
2805
|
+
this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
|
|
2782
2806
|
}
|
|
2807
|
+
this.onStatusChange?.();
|
|
2808
|
+
return;
|
|
2783
2809
|
}
|
|
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) {
|
|
2810
|
+
if (!inCooldown) {
|
|
2811
|
+
if (!modal) {
|
|
2812
|
+
LOG.warn("CLI", `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
|
|
2790
2813
|
return;
|
|
2791
2814
|
}
|
|
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");
|
|
2815
|
+
this.isWaitingForResponse = true;
|
|
2816
|
+
this.setStatus("waiting_approval", "script_detect");
|
|
2817
|
+
this.activeModal = modal;
|
|
2805
2818
|
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);
|
|
2819
|
+
this.armApprovalExitTimeout();
|
|
2812
2820
|
this.onStatusChange?.();
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
applyGenerating(ctx) {
|
|
2824
|
+
const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
|
|
2825
|
+
this.clearIdleFinishCandidate("generating");
|
|
2826
|
+
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
2827
|
+
const noActiveTurn = !this.currentTurnScope;
|
|
2828
|
+
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));
|
|
2829
|
+
if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
|
|
2813
2830
|
return;
|
|
2814
2831
|
}
|
|
2815
|
-
if (
|
|
2816
|
-
if (
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
this.approvalExitTimeout = null;
|
|
2820
|
-
}
|
|
2821
|
-
this.activeModal = null;
|
|
2822
|
-
this.lastApprovalResolvedAt = Date.now();
|
|
2832
|
+
if (prevStatus === "waiting_approval") {
|
|
2833
|
+
if (this.approvalExitTimeout) {
|
|
2834
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2835
|
+
this.approvalExitTimeout = null;
|
|
2823
2836
|
}
|
|
2837
|
+
this.activeModal = null;
|
|
2838
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
2839
|
+
}
|
|
2840
|
+
if (!this.isWaitingForResponse) {
|
|
2841
|
+
this.isWaitingForResponse = true;
|
|
2842
|
+
this.responseBuffer = "";
|
|
2843
|
+
}
|
|
2844
|
+
this.setStatus("generating", "script_detect");
|
|
2845
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2846
|
+
this.idleTimeout = setTimeout(() => {
|
|
2824
2847
|
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") {
|
|
2848
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2849
|
+
this.finishResponse();
|
|
2850
|
+
}
|
|
2851
|
+
}, this.timeouts.generatingIdle);
|
|
2852
|
+
this.onStatusChange?.();
|
|
2853
|
+
}
|
|
2854
|
+
applyIdle(ctx, now) {
|
|
2855
|
+
const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
|
|
2856
|
+
if (prevStatus === "waiting_approval") {
|
|
2857
|
+
if (this.approvalExitTimeout) {
|
|
2858
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2859
|
+
this.approvalExitTimeout = null;
|
|
2860
|
+
}
|
|
2861
|
+
this.activeModal = null;
|
|
2862
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
2863
|
+
}
|
|
2864
|
+
if (!this.isWaitingForResponse) {
|
|
2865
|
+
if (prevStatus !== "idle") {
|
|
2884
2866
|
this.clearIdleFinishCandidate("idle_without_response");
|
|
2885
2867
|
this.setStatus("idle", "script_detect");
|
|
2886
2868
|
this.onStatusChange?.();
|
|
2887
2869
|
}
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
2873
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
2874
|
+
const hasAssistantTurn = !!lastParsedAssistant;
|
|
2875
|
+
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
2876
|
+
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
2877
|
+
const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
|
|
2878
|
+
const idleReady = !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleFinishConfirmMs;
|
|
2879
|
+
const candidate = this.idleFinishCandidate;
|
|
2880
|
+
const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
|
|
2881
|
+
this.recordTrace("idle_decision", {
|
|
2882
|
+
quietForMs,
|
|
2883
|
+
screenStableMs,
|
|
2884
|
+
hasAssistantTurn,
|
|
2885
|
+
assistantLength,
|
|
2886
|
+
hasModal: !!modal,
|
|
2887
|
+
idleQuietThresholdMs,
|
|
2888
|
+
idleStableThresholdMs: idleFinishConfirmMs,
|
|
2889
|
+
idleReady,
|
|
2890
|
+
idleFinishConfirmMs,
|
|
2891
|
+
idleFinishCandidate: candidate,
|
|
2892
|
+
candidateQuiet,
|
|
2893
|
+
canFinishImmediately: idleReady && candidateQuiet,
|
|
2894
|
+
submitPendingUntil: this.submitPendingUntil,
|
|
2895
|
+
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
2896
|
+
...buildCliTraceParseSnapshot({
|
|
2897
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
2898
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2899
|
+
responseBuffer: this.responseBuffer,
|
|
2900
|
+
partialResponse: this.responseBuffer,
|
|
2901
|
+
scope: this.currentTurnScope
|
|
2902
|
+
})
|
|
2903
|
+
});
|
|
2904
|
+
if (idleReady && candidateQuiet) {
|
|
2905
|
+
this.clearIdleFinishCandidate("finish_response");
|
|
2906
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2907
|
+
this.finishResponse();
|
|
2908
|
+
return;
|
|
2888
2909
|
}
|
|
2910
|
+
if (idleReady) {
|
|
2911
|
+
if (!candidate) {
|
|
2912
|
+
this.armIdleFinishCandidate(assistantLength);
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
} else {
|
|
2916
|
+
this.clearIdleFinishCandidate("idle_not_ready");
|
|
2917
|
+
}
|
|
2918
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2919
|
+
this.idleTimeout = setTimeout(() => {
|
|
2920
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2921
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2922
|
+
this.clearIdleFinishCandidate("idle_timeout_finish");
|
|
2923
|
+
this.finishResponse();
|
|
2924
|
+
}
|
|
2925
|
+
}, this.timeouts.idleFinish);
|
|
2889
2926
|
}
|
|
2890
2927
|
finishResponse() {
|
|
2891
2928
|
if (this.submitPendingUntil > Date.now()) return;
|
|
@@ -2924,26 +2961,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2924
2961
|
}, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
|
|
2925
2962
|
return;
|
|
2926
2963
|
}
|
|
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
|
-
}
|
|
2964
|
+
this.clearAllTimers();
|
|
2947
2965
|
this.responseBuffer = "";
|
|
2948
2966
|
this.isWaitingForResponse = false;
|
|
2949
2967
|
this.responseSettleIgnoreUntil = 0;
|
|
@@ -2955,18 +2973,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
2955
2973
|
this.setStatus("idle", "response_finished");
|
|
2956
2974
|
this.onStatusChange?.();
|
|
2957
2975
|
}
|
|
2958
|
-
maybeCommitVisibleIdleTranscript(parsed
|
|
2976
|
+
maybeCommitVisibleIdleTranscript(parsed) {
|
|
2959
2977
|
const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
|
|
2960
2978
|
if (!allowImmediateScriptIdleCommit) return false;
|
|
2961
2979
|
if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
|
|
2962
2980
|
return false;
|
|
2963
2981
|
}
|
|
2964
|
-
if (options?.requireVisibleAssistantCandidate) {
|
|
2965
|
-
const candidateText = options.screenText || this.terminalScreen.getText() || "";
|
|
2966
|
-
if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
|
|
2967
|
-
return false;
|
|
2968
|
-
}
|
|
2969
|
-
}
|
|
2970
2982
|
const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
|
|
2971
2983
|
committedMessages: this.committedMessages,
|
|
2972
2984
|
scope: this.currentTurnScope,
|
|
@@ -2975,33 +2987,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
2975
2987
|
const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
|
|
2976
2988
|
if (!visibleAssistant) return false;
|
|
2977
2989
|
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
|
-
}
|
|
2990
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
2991
|
+
this.clearAllTimers();
|
|
3005
2992
|
this.syncMessageViews();
|
|
3006
2993
|
this.responseBuffer = "";
|
|
3007
2994
|
this.isWaitingForResponse = false;
|
|
@@ -3031,13 +3018,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3031
3018
|
scope: this.currentTurnScope,
|
|
3032
3019
|
lastOutputAt: this.lastOutputAt
|
|
3033
3020
|
});
|
|
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
|
-
}
|
|
3021
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
3041
3022
|
this.syncMessageViews();
|
|
3042
3023
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
3043
3024
|
if (this.currentTurnScope) {
|
|
@@ -3095,7 +3076,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3095
3076
|
screen: buildCliScreenSnapshot(screenText),
|
|
3096
3077
|
tailScreen: buildCliScreenSnapshot(text.slice(-500))
|
|
3097
3078
|
});
|
|
3098
|
-
return
|
|
3079
|
+
return status;
|
|
3099
3080
|
} catch (e) {
|
|
3100
3081
|
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
3101
3082
|
return null;
|
|
@@ -3135,23 +3116,21 @@ var init_provider_cli_adapter = __esm({
|
|
|
3135
3116
|
if (!inApprovalCooldown) {
|
|
3136
3117
|
return parsed;
|
|
3137
3118
|
}
|
|
3138
|
-
const
|
|
3139
|
-
const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
|
|
3119
|
+
const visibleModal = this.runParseApproval(recentBuffer);
|
|
3140
3120
|
if (visibleModal) {
|
|
3141
3121
|
return parsed;
|
|
3142
3122
|
}
|
|
3143
3123
|
const detectedStatus = this.runDetectStatus(recentBuffer);
|
|
3144
|
-
const
|
|
3124
|
+
const resolvedStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
|
|
3145
3125
|
return {
|
|
3146
3126
|
...parsed,
|
|
3147
|
-
status:
|
|
3127
|
+
status: resolvedStatus,
|
|
3148
3128
|
activeModal: null
|
|
3149
3129
|
};
|
|
3150
3130
|
}
|
|
3151
3131
|
// ─── Public API (CliAdapter) ───────────────────
|
|
3152
3132
|
getStatus() {
|
|
3153
|
-
const
|
|
3154
|
-
const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
|
|
3133
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
3155
3134
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
3156
3135
|
let effectiveModal = startupModal || this.activeModal;
|
|
3157
3136
|
if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
|
|
@@ -3232,8 +3211,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3232
3211
|
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
3233
3212
|
}));
|
|
3234
3213
|
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);
|
|
3214
|
+
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
3215
|
if (shouldAdoptParsedIdleReplay) {
|
|
3238
3216
|
this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
|
|
3239
3217
|
committedMessages: this.committedMessages,
|
|
@@ -3362,17 +3340,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
3362
3340
|
if (parsed && typeof parsed === "object") {
|
|
3363
3341
|
Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
|
|
3364
3342
|
}
|
|
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
3343
|
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
|
-
}
|
|
3344
|
+
if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
|
|
3345
|
+
this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
|
|
3376
3346
|
}
|
|
3377
3347
|
this.parseErrorMessage = null;
|
|
3378
3348
|
return normalizedParsed;
|
|
@@ -3400,16 +3370,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
3400
3370
|
LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
|
|
3401
3371
|
}
|
|
3402
3372
|
}
|
|
3403
|
-
if (!promptText
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
${data.explanation || ""}
|
|
3407
|
-
|
|
3408
|
-
${data.message || ""}`.trim();
|
|
3409
|
-
}
|
|
3410
|
-
if (promptText) {
|
|
3411
|
-
await this.sendMessage(promptText);
|
|
3373
|
+
if (!promptText) {
|
|
3374
|
+
LOG.warn("CLI", `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
|
|
3375
|
+
return;
|
|
3412
3376
|
}
|
|
3377
|
+
await this.sendMessage(promptText);
|
|
3413
3378
|
}
|
|
3414
3379
|
async sendMessage(text) {
|
|
3415
3380
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
@@ -3427,9 +3392,7 @@ ${data.message || ""}`.trim();
|
|
|
3427
3392
|
}
|
|
3428
3393
|
if (!this.ready) {
|
|
3429
3394
|
this.resolveStartupState("send_precheck");
|
|
3430
|
-
|
|
3431
|
-
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
3432
|
-
if (hasPrompt && this.currentStatus === "idle") {
|
|
3395
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === "idle" && this.currentStatus === "idle") {
|
|
3433
3396
|
this.ready = true;
|
|
3434
3397
|
this.startupParseGate = false;
|
|
3435
3398
|
LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
@@ -3535,7 +3498,10 @@ ${data.message || ""}`.trim();
|
|
|
3535
3498
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
3536
3499
|
const screenText2 = this.terminalScreen.getText();
|
|
3537
3500
|
if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
|
|
3538
|
-
|
|
3501
|
+
const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
|
|
3502
|
+
if (liveApproval) return;
|
|
3503
|
+
const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
|
|
3504
|
+
if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
|
|
3539
3505
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
3540
3506
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
3541
3507
|
this.recordTrace("submit_write", {
|
|
@@ -3572,6 +3538,10 @@ ${data.message || ""}`.trim();
|
|
|
3572
3538
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
3573
3539
|
const screenText = this.terminalScreen.getText();
|
|
3574
3540
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
3541
|
+
const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
|
|
3542
|
+
if (liveApproval) return;
|
|
3543
|
+
const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
|
|
3544
|
+
if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
|
|
3575
3545
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
3576
3546
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
3577
3547
|
this.recordTrace("submit_write", {
|
|
@@ -3703,44 +3673,9 @@ ${data.message || ""}`.trim();
|
|
|
3703
3673
|
}
|
|
3704
3674
|
shutdown() {
|
|
3705
3675
|
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
|
-
}
|
|
3676
|
+
this.clearAllTimers();
|
|
3738
3677
|
this.pendingOutputParseBuffer = "";
|
|
3739
3678
|
this.pendingTerminalQueryTail = "";
|
|
3740
|
-
if (this.ptyOutputFlushTimer) {
|
|
3741
|
-
clearTimeout(this.ptyOutputFlushTimer);
|
|
3742
|
-
this.ptyOutputFlushTimer = null;
|
|
3743
|
-
}
|
|
3744
3679
|
this.ptyOutputBuffer = "";
|
|
3745
3680
|
this.finishRetryCount = 0;
|
|
3746
3681
|
if (this.ptyProcess) {
|
|
@@ -3761,44 +3696,9 @@ ${data.message || ""}`.trim();
|
|
|
3761
3696
|
}
|
|
3762
3697
|
detach() {
|
|
3763
3698
|
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
|
-
}
|
|
3699
|
+
this.clearAllTimers();
|
|
3796
3700
|
this.pendingOutputParseBuffer = "";
|
|
3797
3701
|
this.pendingTerminalQueryTail = "";
|
|
3798
|
-
if (this.ptyOutputFlushTimer) {
|
|
3799
|
-
clearTimeout(this.ptyOutputFlushTimer);
|
|
3800
|
-
this.ptyOutputFlushTimer = null;
|
|
3801
|
-
}
|
|
3802
3702
|
this.ptyOutputBuffer = "";
|
|
3803
3703
|
this.finishRetryCount = 0;
|
|
3804
3704
|
if (this.ptyProcess) {
|
|
@@ -3860,8 +3760,7 @@ ${data.message || ""}`.trim();
|
|
|
3860
3760
|
this.ptyProcess?.write(data);
|
|
3861
3761
|
}
|
|
3862
3762
|
resolveModal(buttonIndex) {
|
|
3863
|
-
|
|
3864
|
-
let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
|
|
3763
|
+
let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
3865
3764
|
if (!modal && typeof this.cliScripts?.parseOutput === "function") {
|
|
3866
3765
|
try {
|
|
3867
3766
|
const parsed = this.getScriptParsedStatus();
|
|
@@ -3892,12 +3791,7 @@ ${data.message || ""}`.trim();
|
|
|
3892
3791
|
}
|
|
3893
3792
|
this.setStatus("generating", "approval_resolved");
|
|
3894
3793
|
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) {
|
|
3794
|
+
if (buttonIndex in this.approvalKeys) {
|
|
3901
3795
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
3902
3796
|
} else {
|
|
3903
3797
|
const DOWN = "\x1B[B";
|
|
@@ -3917,7 +3811,7 @@ ${data.message || ""}`.trim();
|
|
|
3917
3811
|
}
|
|
3918
3812
|
getDebugState() {
|
|
3919
3813
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
3920
|
-
const startupModal = this.startupParseGate ? this.
|
|
3814
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
3921
3815
|
const effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
3922
3816
|
const effectiveReady = this.ready || !!startupModal;
|
|
3923
3817
|
return {
|
|
@@ -11250,25 +11144,23 @@ async function handleResolveAction(h, args) {
|
|
|
11250
11144
|
const effectiveModal = statusModal || surfacedModal;
|
|
11251
11145
|
const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" ? "waiting_approval" : status?.status;
|
|
11252
11146
|
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 (
|
|
11147
|
+
if (!effectiveModal) {
|
|
11254
11148
|
return { success: false, error: "Not in approval state" };
|
|
11255
11149
|
}
|
|
11256
|
-
const buttons = effectiveModal
|
|
11150
|
+
const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
|
|
11257
11151
|
let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
|
|
11258
|
-
if (buttonIndex < 0) {
|
|
11152
|
+
if (buttonIndex < 0 && button) {
|
|
11259
11153
|
const btnLower = button.toLowerCase();
|
|
11260
11154
|
buttonIndex = buttons.findIndex((b) => b.toLowerCase().includes(btnLower));
|
|
11261
11155
|
}
|
|
11156
|
+
if (buttonIndex < 0 && (action === "reject" || action === "deny")) {
|
|
11157
|
+
buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
|
|
11158
|
+
}
|
|
11159
|
+
if (buttonIndex < 0 && (action === "always" || /always/i.test(button))) {
|
|
11160
|
+
buttonIndex = buttons.findIndex((b) => /always/i.test(b));
|
|
11161
|
+
}
|
|
11262
11162
|
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
|
-
}
|
|
11163
|
+
return { success: false, error: "Approval action did not match any visible button" };
|
|
11272
11164
|
}
|
|
11273
11165
|
if (typeof adapter.resolveModal === "function") {
|
|
11274
11166
|
adapter.resolveModal(buttonIndex);
|