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