@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.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,14 @@ 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
|
|
2620
|
-
const
|
|
2621
|
-
const scriptStatus = startupModal ? "waiting_approval" : parsedModal && parsedTranscript?.status === "waiting_approval" ? "waiting_approval" : rawScriptStatus;
|
|
2641
|
+
const modal = this.runParseApproval(tail) || parsedModal;
|
|
2642
|
+
const scriptStatus = this.runDetectStatus(tail);
|
|
2622
2643
|
const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
|
|
2623
2644
|
committedMessages: this.committedMessages,
|
|
2624
2645
|
scope: this.currentTurnScope,
|
|
@@ -2673,15 +2694,44 @@ var init_provider_cli_adapter = __esm({
|
|
|
2673
2694
|
}
|
|
2674
2695
|
if (!scriptStatus) return;
|
|
2675
2696
|
const prevStatus = this.currentStatus;
|
|
2676
|
-
const
|
|
2697
|
+
const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
|
|
2698
|
+
if (!this.applyPendingScriptStatusDebounce(ctx)) return;
|
|
2699
|
+
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
2700
|
+
LOG.info(
|
|
2701
|
+
"CLI",
|
|
2702
|
+
`[${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)}`
|
|
2703
|
+
);
|
|
2704
|
+
const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
|
|
2705
|
+
if (shouldHoldGenerating) {
|
|
2706
|
+
this.applyHoldGenerating(ctx, recentInteractiveActivity);
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2709
|
+
if (scriptStatus === "waiting_approval") {
|
|
2710
|
+
this.applyWaitingApproval(ctx);
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
if (scriptStatus === "generating") {
|
|
2714
|
+
this.applyGenerating(ctx);
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
if (scriptStatus === "idle") {
|
|
2718
|
+
this.applyIdle(ctx, now);
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
// Returns false if the caller should bail out (debounce pending).
|
|
2722
|
+
applyPendingScriptStatusDebounce(ctx) {
|
|
2723
|
+
const { now, scriptStatus, prevStatus } = ctx;
|
|
2724
|
+
const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
|
|
2725
|
+
if (!shouldDebounce) {
|
|
2677
2726
|
this.pendingScriptStatus = null;
|
|
2678
2727
|
this.pendingScriptStatusSince = 0;
|
|
2679
2728
|
if (this.pendingScriptStatusTimer) {
|
|
2680
2729
|
clearTimeout(this.pendingScriptStatusTimer);
|
|
2681
2730
|
this.pendingScriptStatusTimer = null;
|
|
2682
2731
|
}
|
|
2683
|
-
|
|
2684
|
-
|
|
2732
|
+
return true;
|
|
2733
|
+
}
|
|
2734
|
+
const armPending = (delayMs) => {
|
|
2685
2735
|
if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
|
|
2686
2736
|
this.pendingScriptStatusTimer = setTimeout(() => {
|
|
2687
2737
|
this.pendingScriptStatusTimer = null;
|
|
@@ -2689,200 +2739,187 @@ var init_provider_cli_adapter = __esm({
|
|
|
2689
2739
|
this.evaluateSettled();
|
|
2690
2740
|
}, delayMs);
|
|
2691
2741
|
};
|
|
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
|
-
);
|
|
2742
|
+
if (this.pendingScriptStatus !== scriptStatus) {
|
|
2743
|
+
this.pendingScriptStatus = scriptStatus;
|
|
2744
|
+
this.pendingScriptStatusSince = now;
|
|
2745
|
+
armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
|
|
2746
|
+
return false;
|
|
2717
2747
|
}
|
|
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;
|
|
2748
|
+
const elapsed = now - this.pendingScriptStatusSince;
|
|
2749
|
+
if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
|
|
2750
|
+
armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
|
|
2751
|
+
return false;
|
|
2745
2752
|
}
|
|
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
|
-
|
|
2753
|
+
return true;
|
|
2754
|
+
}
|
|
2755
|
+
applyHoldGenerating(ctx, recentInteractiveActivity) {
|
|
2756
|
+
const { scriptStatus } = ctx;
|
|
2757
|
+
this.clearIdleFinishCandidate("hold_generating_recent_activity");
|
|
2758
|
+
this.setStatus("generating", "recent_activity_hold");
|
|
2759
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2760
|
+
this.idleTimeout = setTimeout(() => {
|
|
2761
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2762
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2763
|
+
this.finishResponse();
|
|
2764
|
+
}
|
|
2765
|
+
}, this.timeouts.generatingIdle);
|
|
2766
|
+
this.recordTrace("hold_generating_recent_activity", {
|
|
2767
|
+
scriptStatus,
|
|
2768
|
+
recentInteractiveActivity,
|
|
2769
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
2770
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
2771
|
+
holdMs: this.getStatusActivityHoldMs(),
|
|
2772
|
+
...buildCliTraceParseSnapshot({
|
|
2773
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
2774
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2775
|
+
responseBuffer: this.responseBuffer,
|
|
2776
|
+
partialResponse: this.responseBuffer,
|
|
2777
|
+
scope: this.currentTurnScope
|
|
2778
|
+
})
|
|
2779
|
+
});
|
|
2780
|
+
this.onStatusChange?.();
|
|
2781
|
+
}
|
|
2782
|
+
applyWaitingApproval(ctx) {
|
|
2783
|
+
const { modal } = ctx;
|
|
2784
|
+
this.clearIdleFinishCandidate("waiting_approval");
|
|
2785
|
+
const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
2786
|
+
if (inCooldown && !modal) {
|
|
2787
|
+
if (this.approvalExitTimeout) {
|
|
2788
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2789
|
+
this.approvalExitTimeout = null;
|
|
2770
2790
|
}
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
this.setStatus("
|
|
2774
|
-
this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
|
|
2791
|
+
this.activeModal = null;
|
|
2792
|
+
if (this.isWaitingForResponse) {
|
|
2793
|
+
this.setStatus("generating", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
|
|
2775
2794
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2776
|
-
this.
|
|
2777
|
-
|
|
2778
|
-
|
|
2795
|
+
this.idleTimeout = setTimeout(() => {
|
|
2796
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2797
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2798
|
+
this.finishResponse();
|
|
2799
|
+
}
|
|
2800
|
+
}, this.timeouts.generatingIdle);
|
|
2801
|
+
} else {
|
|
2802
|
+
this.setStatus("idle", inCooldown ? "approval_cooldown_ignore" : "approval_prompt_gone");
|
|
2779
2803
|
}
|
|
2804
|
+
this.onStatusChange?.();
|
|
2805
|
+
return;
|
|
2780
2806
|
}
|
|
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) {
|
|
2807
|
+
if (!inCooldown) {
|
|
2808
|
+
if (!modal) {
|
|
2809
|
+
LOG.warn("CLI", `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
|
|
2787
2810
|
return;
|
|
2788
2811
|
}
|
|
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");
|
|
2812
|
+
this.isWaitingForResponse = true;
|
|
2813
|
+
this.setStatus("waiting_approval", "script_detect");
|
|
2814
|
+
this.activeModal = modal;
|
|
2802
2815
|
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);
|
|
2816
|
+
this.armApprovalExitTimeout();
|
|
2809
2817
|
this.onStatusChange?.();
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
applyGenerating(ctx) {
|
|
2821
|
+
const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
|
|
2822
|
+
this.clearIdleFinishCandidate("generating");
|
|
2823
|
+
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
2824
|
+
const noActiveTurn = !this.currentTurnScope;
|
|
2825
|
+
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));
|
|
2826
|
+
if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
|
|
2810
2827
|
return;
|
|
2811
2828
|
}
|
|
2812
|
-
if (
|
|
2813
|
-
if (
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
this.approvalExitTimeout = null;
|
|
2817
|
-
}
|
|
2818
|
-
this.activeModal = null;
|
|
2819
|
-
this.lastApprovalResolvedAt = Date.now();
|
|
2829
|
+
if (prevStatus === "waiting_approval") {
|
|
2830
|
+
if (this.approvalExitTimeout) {
|
|
2831
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2832
|
+
this.approvalExitTimeout = null;
|
|
2820
2833
|
}
|
|
2834
|
+
this.activeModal = null;
|
|
2835
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
2836
|
+
}
|
|
2837
|
+
if (!this.isWaitingForResponse) {
|
|
2838
|
+
this.isWaitingForResponse = true;
|
|
2839
|
+
this.responseBuffer = "";
|
|
2840
|
+
}
|
|
2841
|
+
this.setStatus("generating", "script_detect");
|
|
2842
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2843
|
+
this.idleTimeout = setTimeout(() => {
|
|
2821
2844
|
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") {
|
|
2845
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2846
|
+
this.finishResponse();
|
|
2847
|
+
}
|
|
2848
|
+
}, this.timeouts.generatingIdle);
|
|
2849
|
+
this.onStatusChange?.();
|
|
2850
|
+
}
|
|
2851
|
+
applyIdle(ctx, now) {
|
|
2852
|
+
const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
|
|
2853
|
+
if (prevStatus === "waiting_approval") {
|
|
2854
|
+
if (this.approvalExitTimeout) {
|
|
2855
|
+
clearTimeout(this.approvalExitTimeout);
|
|
2856
|
+
this.approvalExitTimeout = null;
|
|
2857
|
+
}
|
|
2858
|
+
this.activeModal = null;
|
|
2859
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
2860
|
+
}
|
|
2861
|
+
if (!this.isWaitingForResponse) {
|
|
2862
|
+
if (prevStatus !== "idle") {
|
|
2881
2863
|
this.clearIdleFinishCandidate("idle_without_response");
|
|
2882
2864
|
this.setStatus("idle", "script_detect");
|
|
2883
2865
|
this.onStatusChange?.();
|
|
2884
2866
|
}
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
2870
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
2871
|
+
const hasAssistantTurn = !!lastParsedAssistant;
|
|
2872
|
+
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
2873
|
+
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
2874
|
+
const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
|
|
2875
|
+
const idleReady = !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleFinishConfirmMs;
|
|
2876
|
+
const candidate = this.idleFinishCandidate;
|
|
2877
|
+
const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
|
|
2878
|
+
this.recordTrace("idle_decision", {
|
|
2879
|
+
quietForMs,
|
|
2880
|
+
screenStableMs,
|
|
2881
|
+
hasAssistantTurn,
|
|
2882
|
+
assistantLength,
|
|
2883
|
+
hasModal: !!modal,
|
|
2884
|
+
idleQuietThresholdMs,
|
|
2885
|
+
idleStableThresholdMs: idleFinishConfirmMs,
|
|
2886
|
+
idleReady,
|
|
2887
|
+
idleFinishConfirmMs,
|
|
2888
|
+
idleFinishCandidate: candidate,
|
|
2889
|
+
candidateQuiet,
|
|
2890
|
+
canFinishImmediately: idleReady && candidateQuiet,
|
|
2891
|
+
submitPendingUntil: this.submitPendingUntil,
|
|
2892
|
+
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
2893
|
+
...buildCliTraceParseSnapshot({
|
|
2894
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
2895
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2896
|
+
responseBuffer: this.responseBuffer,
|
|
2897
|
+
partialResponse: this.responseBuffer,
|
|
2898
|
+
scope: this.currentTurnScope
|
|
2899
|
+
})
|
|
2900
|
+
});
|
|
2901
|
+
if (idleReady && candidateQuiet) {
|
|
2902
|
+
this.clearIdleFinishCandidate("finish_response");
|
|
2903
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2904
|
+
this.finishResponse();
|
|
2905
|
+
return;
|
|
2885
2906
|
}
|
|
2907
|
+
if (idleReady) {
|
|
2908
|
+
if (!candidate) {
|
|
2909
|
+
this.armIdleFinishCandidate(assistantLength);
|
|
2910
|
+
return;
|
|
2911
|
+
}
|
|
2912
|
+
} else {
|
|
2913
|
+
this.clearIdleFinishCandidate("idle_not_ready");
|
|
2914
|
+
}
|
|
2915
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
2916
|
+
this.idleTimeout = setTimeout(() => {
|
|
2917
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
2918
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
2919
|
+
this.clearIdleFinishCandidate("idle_timeout_finish");
|
|
2920
|
+
this.finishResponse();
|
|
2921
|
+
}
|
|
2922
|
+
}, this.timeouts.idleFinish);
|
|
2886
2923
|
}
|
|
2887
2924
|
finishResponse() {
|
|
2888
2925
|
if (this.submitPendingUntil > Date.now()) return;
|
|
@@ -2921,26 +2958,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2921
2958
|
}, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
|
|
2922
2959
|
return;
|
|
2923
2960
|
}
|
|
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
|
-
}
|
|
2961
|
+
this.clearAllTimers();
|
|
2944
2962
|
this.responseBuffer = "";
|
|
2945
2963
|
this.isWaitingForResponse = false;
|
|
2946
2964
|
this.responseSettleIgnoreUntil = 0;
|
|
@@ -2952,18 +2970,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
2952
2970
|
this.setStatus("idle", "response_finished");
|
|
2953
2971
|
this.onStatusChange?.();
|
|
2954
2972
|
}
|
|
2955
|
-
maybeCommitVisibleIdleTranscript(parsed
|
|
2973
|
+
maybeCommitVisibleIdleTranscript(parsed) {
|
|
2956
2974
|
const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
|
|
2957
2975
|
if (!allowImmediateScriptIdleCommit) return false;
|
|
2958
2976
|
if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
|
|
2959
2977
|
return false;
|
|
2960
2978
|
}
|
|
2961
|
-
if (options?.requireVisibleAssistantCandidate) {
|
|
2962
|
-
const candidateText = options.screenText || this.terminalScreen.getText() || "";
|
|
2963
|
-
if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
|
|
2964
|
-
return false;
|
|
2965
|
-
}
|
|
2966
|
-
}
|
|
2967
2979
|
const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
|
|
2968
2980
|
committedMessages: this.committedMessages,
|
|
2969
2981
|
scope: this.currentTurnScope,
|
|
@@ -2972,33 +2984,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
2972
2984
|
const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
|
|
2973
2985
|
if (!visibleAssistant) return false;
|
|
2974
2986
|
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
|
-
}
|
|
2987
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
2988
|
+
this.clearAllTimers();
|
|
3002
2989
|
this.syncMessageViews();
|
|
3003
2990
|
this.responseBuffer = "";
|
|
3004
2991
|
this.isWaitingForResponse = false;
|
|
@@ -3028,13 +3015,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3028
3015
|
scope: this.currentTurnScope,
|
|
3029
3016
|
lastOutputAt: this.lastOutputAt
|
|
3030
3017
|
});
|
|
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
|
-
}
|
|
3018
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
3038
3019
|
this.syncMessageViews();
|
|
3039
3020
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
3040
3021
|
if (this.currentTurnScope) {
|
|
@@ -3092,7 +3073,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3092
3073
|
screen: buildCliScreenSnapshot(screenText),
|
|
3093
3074
|
tailScreen: buildCliScreenSnapshot(text.slice(-500))
|
|
3094
3075
|
});
|
|
3095
|
-
return
|
|
3076
|
+
return status;
|
|
3096
3077
|
} catch (e) {
|
|
3097
3078
|
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
3098
3079
|
return null;
|
|
@@ -3132,23 +3113,21 @@ var init_provider_cli_adapter = __esm({
|
|
|
3132
3113
|
if (!inApprovalCooldown) {
|
|
3133
3114
|
return parsed;
|
|
3134
3115
|
}
|
|
3135
|
-
const
|
|
3136
|
-
const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
|
|
3116
|
+
const visibleModal = this.runParseApproval(recentBuffer);
|
|
3137
3117
|
if (visibleModal) {
|
|
3138
3118
|
return parsed;
|
|
3139
3119
|
}
|
|
3140
3120
|
const detectedStatus = this.runDetectStatus(recentBuffer);
|
|
3141
|
-
const
|
|
3121
|
+
const resolvedStatus = detectedStatus && detectedStatus !== "waiting_approval" ? detectedStatus : this.isWaitingForResponse || this.currentTurnScope ? "generating" : this.currentStatus === "waiting_approval" ? "idle" : this.currentStatus;
|
|
3142
3122
|
return {
|
|
3143
3123
|
...parsed,
|
|
3144
|
-
status:
|
|
3124
|
+
status: resolvedStatus,
|
|
3145
3125
|
activeModal: null
|
|
3146
3126
|
};
|
|
3147
3127
|
}
|
|
3148
3128
|
// ─── Public API (CliAdapter) ───────────────────
|
|
3149
3129
|
getStatus() {
|
|
3150
|
-
const
|
|
3151
|
-
const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
|
|
3130
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
3152
3131
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
3153
3132
|
let effectiveModal = startupModal || this.activeModal;
|
|
3154
3133
|
if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
|
|
@@ -3229,8 +3208,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
3229
3208
|
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
3230
3209
|
}));
|
|
3231
3210
|
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);
|
|
3211
|
+
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
3212
|
if (shouldAdoptParsedIdleReplay) {
|
|
3235
3213
|
this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
|
|
3236
3214
|
committedMessages: this.committedMessages,
|
|
@@ -3359,17 +3337,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
3359
3337
|
if (parsed && typeof parsed === "object") {
|
|
3360
3338
|
Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
|
|
3361
3339
|
}
|
|
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
3340
|
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
|
-
}
|
|
3341
|
+
if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
|
|
3342
|
+
this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
|
|
3373
3343
|
}
|
|
3374
3344
|
this.parseErrorMessage = null;
|
|
3375
3345
|
return normalizedParsed;
|
|
@@ -3397,16 +3367,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
3397
3367
|
LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
|
|
3398
3368
|
}
|
|
3399
3369
|
}
|
|
3400
|
-
if (!promptText
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
${data.explanation || ""}
|
|
3404
|
-
|
|
3405
|
-
${data.message || ""}`.trim();
|
|
3406
|
-
}
|
|
3407
|
-
if (promptText) {
|
|
3408
|
-
await this.sendMessage(promptText);
|
|
3370
|
+
if (!promptText) {
|
|
3371
|
+
LOG.warn("CLI", `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
|
|
3372
|
+
return;
|
|
3409
3373
|
}
|
|
3374
|
+
await this.sendMessage(promptText);
|
|
3410
3375
|
}
|
|
3411
3376
|
async sendMessage(text) {
|
|
3412
3377
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
@@ -3424,9 +3389,7 @@ ${data.message || ""}`.trim();
|
|
|
3424
3389
|
}
|
|
3425
3390
|
if (!this.ready) {
|
|
3426
3391
|
this.resolveStartupState("send_precheck");
|
|
3427
|
-
|
|
3428
|
-
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
3429
|
-
if (hasPrompt && this.currentStatus === "idle") {
|
|
3392
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === "idle" && this.currentStatus === "idle") {
|
|
3430
3393
|
this.ready = true;
|
|
3431
3394
|
this.startupParseGate = false;
|
|
3432
3395
|
LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
@@ -3532,7 +3495,10 @@ ${data.message || ""}`.trim();
|
|
|
3532
3495
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
3533
3496
|
const screenText2 = this.terminalScreen.getText();
|
|
3534
3497
|
if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
|
|
3535
|
-
|
|
3498
|
+
const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
|
|
3499
|
+
if (liveApproval) return;
|
|
3500
|
+
const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
|
|
3501
|
+
if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
|
|
3536
3502
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
3537
3503
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
3538
3504
|
this.recordTrace("submit_write", {
|
|
@@ -3569,6 +3535,10 @@ ${data.message || ""}`.trim();
|
|
|
3569
3535
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
3570
3536
|
const screenText = this.terminalScreen.getText();
|
|
3571
3537
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
3538
|
+
const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
|
|
3539
|
+
if (liveApproval) return;
|
|
3540
|
+
const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
|
|
3541
|
+
if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
|
|
3572
3542
|
LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
3573
3543
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
3574
3544
|
this.recordTrace("submit_write", {
|
|
@@ -3700,44 +3670,9 @@ ${data.message || ""}`.trim();
|
|
|
3700
3670
|
}
|
|
3701
3671
|
shutdown() {
|
|
3702
3672
|
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
|
-
}
|
|
3673
|
+
this.clearAllTimers();
|
|
3735
3674
|
this.pendingOutputParseBuffer = "";
|
|
3736
3675
|
this.pendingTerminalQueryTail = "";
|
|
3737
|
-
if (this.ptyOutputFlushTimer) {
|
|
3738
|
-
clearTimeout(this.ptyOutputFlushTimer);
|
|
3739
|
-
this.ptyOutputFlushTimer = null;
|
|
3740
|
-
}
|
|
3741
3676
|
this.ptyOutputBuffer = "";
|
|
3742
3677
|
this.finishRetryCount = 0;
|
|
3743
3678
|
if (this.ptyProcess) {
|
|
@@ -3758,44 +3693,9 @@ ${data.message || ""}`.trim();
|
|
|
3758
3693
|
}
|
|
3759
3694
|
detach() {
|
|
3760
3695
|
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
|
-
}
|
|
3696
|
+
this.clearAllTimers();
|
|
3793
3697
|
this.pendingOutputParseBuffer = "";
|
|
3794
3698
|
this.pendingTerminalQueryTail = "";
|
|
3795
|
-
if (this.ptyOutputFlushTimer) {
|
|
3796
|
-
clearTimeout(this.ptyOutputFlushTimer);
|
|
3797
|
-
this.ptyOutputFlushTimer = null;
|
|
3798
|
-
}
|
|
3799
3699
|
this.ptyOutputBuffer = "";
|
|
3800
3700
|
this.finishRetryCount = 0;
|
|
3801
3701
|
if (this.ptyProcess) {
|
|
@@ -3857,8 +3757,7 @@ ${data.message || ""}`.trim();
|
|
|
3857
3757
|
this.ptyProcess?.write(data);
|
|
3858
3758
|
}
|
|
3859
3759
|
resolveModal(buttonIndex) {
|
|
3860
|
-
|
|
3861
|
-
let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
|
|
3760
|
+
let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
3862
3761
|
if (!modal && typeof this.cliScripts?.parseOutput === "function") {
|
|
3863
3762
|
try {
|
|
3864
3763
|
const parsed = this.getScriptParsedStatus();
|
|
@@ -3889,12 +3788,7 @@ ${data.message || ""}`.trim();
|
|
|
3889
3788
|
}
|
|
3890
3789
|
this.setStatus("generating", "approval_resolved");
|
|
3891
3790
|
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) {
|
|
3791
|
+
if (buttonIndex in this.approvalKeys) {
|
|
3898
3792
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
3899
3793
|
} else {
|
|
3900
3794
|
const DOWN = "\x1B[B";
|
|
@@ -3914,7 +3808,7 @@ ${data.message || ""}`.trim();
|
|
|
3914
3808
|
}
|
|
3915
3809
|
getDebugState() {
|
|
3916
3810
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
3917
|
-
const startupModal = this.startupParseGate ? this.
|
|
3811
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
3918
3812
|
const effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
3919
3813
|
const effectiveReady = this.ready || !!startupModal;
|
|
3920
3814
|
return {
|
|
@@ -11097,25 +10991,23 @@ async function handleResolveAction(h, args) {
|
|
|
11097
10991
|
const effectiveModal = statusModal || surfacedModal;
|
|
11098
10992
|
const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" ? "waiting_approval" : status?.status;
|
|
11099
10993
|
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 (
|
|
10994
|
+
if (!effectiveModal) {
|
|
11101
10995
|
return { success: false, error: "Not in approval state" };
|
|
11102
10996
|
}
|
|
11103
|
-
const buttons = effectiveModal
|
|
10997
|
+
const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
|
|
11104
10998
|
let buttonIndex = typeof args?.buttonIndex === "number" ? args.buttonIndex : -1;
|
|
11105
|
-
if (buttonIndex < 0) {
|
|
10999
|
+
if (buttonIndex < 0 && button) {
|
|
11106
11000
|
const btnLower = button.toLowerCase();
|
|
11107
11001
|
buttonIndex = buttons.findIndex((b) => b.toLowerCase().includes(btnLower));
|
|
11108
11002
|
}
|
|
11003
|
+
if (buttonIndex < 0 && (action === "reject" || action === "deny")) {
|
|
11004
|
+
buttonIndex = buttons.findIndex((b) => /deny|reject|no/i.test(b));
|
|
11005
|
+
}
|
|
11006
|
+
if (buttonIndex < 0 && (action === "always" || /always/i.test(button))) {
|
|
11007
|
+
buttonIndex = buttons.findIndex((b) => /always/i.test(b));
|
|
11008
|
+
}
|
|
11109
11009
|
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
|
-
}
|
|
11010
|
+
return { success: false, error: "Approval action did not match any visible button" };
|
|
11119
11011
|
}
|
|
11120
11012
|
if (typeof adapter.resolveModal === "function") {
|
|
11121
11013
|
adapter.resolveModal(buttonIndex);
|