@adhdev/daemon-core 0.8.15 → 0.8.16
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 +9 -0
- package/dist/daemon/dev-auto-implement.d.ts +1 -0
- package/dist/index.js +253 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +253 -35
- 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 +188 -9
- package/src/commands/upgrade-helper.ts +9 -3
- package/src/daemon/dev-auto-implement.ts +9 -0
- package/src/daemon/dev-cli-debug.ts +58 -21
package/dist/index.mjs
CHANGED
|
@@ -906,6 +906,40 @@ function normalizeScreenSnapshot(text) {
|
|
|
906
906
|
function normalizeComparableMessageContent(text) {
|
|
907
907
|
return String(text || "").replace(/\s+/g, " ").trim();
|
|
908
908
|
}
|
|
909
|
+
function trimPromptEchoPrefix(text, promptText) {
|
|
910
|
+
const prompt = normalizeComparableMessageContent(String(promptText || ""));
|
|
911
|
+
if (!prompt) return String(text || "");
|
|
912
|
+
const lines = String(text || "").split(/\r\n|\n|\r/g);
|
|
913
|
+
let dropCount = 0;
|
|
914
|
+
for (let index = 0; index < Math.min(lines.length, 6); index += 1) {
|
|
915
|
+
const fragment = normalizeComparableMessageContent(lines[index].replace(/^[.…]+\s*/, ""));
|
|
916
|
+
if (!fragment) {
|
|
917
|
+
if (dropCount === index) dropCount = index + 1;
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
const fragmentWordCount = fragment ? fragment.split(/\s+/).filter(Boolean).length : 0;
|
|
921
|
+
const canBePromptEcho = fragment.length >= 16 || fragmentWordCount >= 4;
|
|
922
|
+
if (canBePromptEcho && prompt.includes(fragment)) {
|
|
923
|
+
dropCount = index + 1;
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
break;
|
|
927
|
+
}
|
|
928
|
+
return lines.slice(dropCount).join("\n").trim();
|
|
929
|
+
}
|
|
930
|
+
function getLastUserPromptText(messages) {
|
|
931
|
+
const items = Array.isArray(messages) ? messages : [];
|
|
932
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
933
|
+
const message = items[index];
|
|
934
|
+
if (message?.role === "user" && typeof message.content === "string" && message.content.trim()) {
|
|
935
|
+
return message.content;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return "";
|
|
939
|
+
}
|
|
940
|
+
function looksLikeConfirmOnlyLabel(label) {
|
|
941
|
+
return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
|
|
942
|
+
}
|
|
909
943
|
function parsePatternEntry(x) {
|
|
910
944
|
if (x instanceof RegExp) return x;
|
|
911
945
|
if (x && typeof x === "object" && typeof x.source === "string") {
|
|
@@ -1039,6 +1073,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1039
1073
|
submitRetryUsed = false;
|
|
1040
1074
|
submitRetryPromptSnippet = "";
|
|
1041
1075
|
idleFinishCandidate = null;
|
|
1076
|
+
finishRetryTimer = null;
|
|
1077
|
+
finishRetryCount = 0;
|
|
1042
1078
|
// Resize redraw suppression
|
|
1043
1079
|
resizeSuppressUntil = 0;
|
|
1044
1080
|
// Debug: status transition history
|
|
@@ -1060,6 +1096,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1060
1096
|
static MAX_TRACE_ENTRIES = 250;
|
|
1061
1097
|
providerResolutionMeta;
|
|
1062
1098
|
static IDLE_FINISH_CONFIRM_MS = 900;
|
|
1099
|
+
static FINISH_RETRY_DELAY_MS = 300;
|
|
1100
|
+
static MAX_FINISH_RETRIES = 2;
|
|
1063
1101
|
syncMessageViews() {
|
|
1064
1102
|
this.messages = [...this.committedMessages];
|
|
1065
1103
|
this.structuredMessages = [...this.committedMessages];
|
|
@@ -1119,7 +1157,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1119
1157
|
recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
|
|
1120
1158
|
screenText: this.terminalScreen.getText(),
|
|
1121
1159
|
messages: [...baseMessages],
|
|
1122
|
-
partialResponse
|
|
1160
|
+
partialResponse,
|
|
1161
|
+
promptText: scope?.prompt || ""
|
|
1123
1162
|
};
|
|
1124
1163
|
}
|
|
1125
1164
|
setStatus(status, trigger) {
|
|
@@ -1362,6 +1401,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
1362
1401
|
this.terminalScreen.reset(24, 80);
|
|
1363
1402
|
this.pendingTerminalQueryTail = "";
|
|
1364
1403
|
this.currentTurnScope = null;
|
|
1404
|
+
this.finishRetryCount = 0;
|
|
1405
|
+
if (this.finishRetryTimer) {
|
|
1406
|
+
clearTimeout(this.finishRetryTimer);
|
|
1407
|
+
this.finishRetryTimer = null;
|
|
1408
|
+
}
|
|
1365
1409
|
this.ready = false;
|
|
1366
1410
|
await this.ptyProcess.ready;
|
|
1367
1411
|
this.recordTrace("ready", {
|
|
@@ -1408,11 +1452,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
1408
1452
|
if (this.startupParseGate) {
|
|
1409
1453
|
this.startupBuffer += cleanData;
|
|
1410
1454
|
const elapsed = Date.now() - this.spawnAt;
|
|
1411
|
-
const scriptStatus = this.runDetectStatus(this.startupBuffer);
|
|
1412
1455
|
const screenText = this.terminalScreen.getText() || "";
|
|
1456
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1457
|
+
const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
|
|
1413
1458
|
const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1414
1459
|
const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1415
|
-
const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
|
|
1460
|
+
const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || !!startupModal && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
|
|
1416
1461
|
if (isReady) {
|
|
1417
1462
|
this.startupParseGate = false;
|
|
1418
1463
|
this.ready = true;
|
|
@@ -1444,7 +1489,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1444
1489
|
this.approvalExitTimeout = setTimeout(() => {
|
|
1445
1490
|
if (this.currentStatus !== "waiting_approval") return;
|
|
1446
1491
|
const tail = this.recentOutputBuffer;
|
|
1447
|
-
const
|
|
1492
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
1493
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1494
|
+
const modal = this.runParseApproval(tail) || startupModal;
|
|
1448
1495
|
const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
|
|
1449
1496
|
if (stillWaiting) {
|
|
1450
1497
|
this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
|
|
@@ -1464,6 +1511,63 @@ var init_provider_cli_adapter = __esm({
|
|
|
1464
1511
|
if (!text.trim()) return false;
|
|
1465
1512
|
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);
|
|
1466
1513
|
}
|
|
1514
|
+
looksLikeVisibleAssistantCandidate(screenText) {
|
|
1515
|
+
const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
|
|
1516
|
+
for (const line of lines) {
|
|
1517
|
+
const trimmed = String(line || "").trim();
|
|
1518
|
+
if (!trimmed) continue;
|
|
1519
|
+
if (/^➜\s+\S+/.test(trimmed)) continue;
|
|
1520
|
+
if (/^Update available!/i.test(trimmed)) continue;
|
|
1521
|
+
if (/Claude Code v\d/i.test(trimmed)) continue;
|
|
1522
|
+
if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
|
|
1523
|
+
if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
|
|
1524
|
+
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
|
|
1525
|
+
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
|
|
1526
|
+
const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
|
|
1527
|
+
if (!assistantMatch) continue;
|
|
1528
|
+
const content = assistantMatch[1].trim();
|
|
1529
|
+
if (!content) continue;
|
|
1530
|
+
if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
|
|
1531
|
+
if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
|
|
1532
|
+
return true;
|
|
1533
|
+
}
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
shouldRetryFinishResponse(commitResult) {
|
|
1537
|
+
if (!this.currentTurnScope) return false;
|
|
1538
|
+
if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
|
|
1539
|
+
if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
|
|
1540
|
+
if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
|
|
1541
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
1542
|
+
if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
|
|
1543
|
+
const now = Date.now();
|
|
1544
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
1545
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1546
|
+
return quietForMs < 1200 || screenStableMs < 1200 || !commitResult.hasAssistant;
|
|
1547
|
+
}
|
|
1548
|
+
getStartupConfirmationModal(screenText) {
|
|
1549
|
+
const text = sanitizeTerminalText(String(screenText || ""));
|
|
1550
|
+
if (!text.trim()) return null;
|
|
1551
|
+
if (this.cliType === "claude-cli") {
|
|
1552
|
+
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);
|
|
1553
|
+
const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
|
|
1554
|
+
if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
|
|
1555
|
+
return {
|
|
1556
|
+
message: "Confirm Claude Code project trust",
|
|
1557
|
+
buttons: ["Continue"]
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
shouldResolveModalWithEnter(modal, buttonIndex) {
|
|
1564
|
+
if (!modal || buttonIndex !== 0) return false;
|
|
1565
|
+
const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
|
|
1566
|
+
if (buttons.length !== 1) return false;
|
|
1567
|
+
const buttonLabel = String(buttons[0] || "").trim();
|
|
1568
|
+
const modalText = `${modal.message || ""} ${buttonLabel}`.trim();
|
|
1569
|
+
return looksLikeConfirmOnlyLabel(buttonLabel) || /Quick safety check|project trust|trust (?:this project|the contents of this directory|the files in this folder)|Enter to confirm/i.test(modalText);
|
|
1570
|
+
}
|
|
1467
1571
|
async waitForInteractivePrompt(maxWaitMs = 5e3) {
|
|
1468
1572
|
const startedAt = Date.now();
|
|
1469
1573
|
let loggedWait = false;
|
|
@@ -1513,9 +1617,10 @@ var init_provider_cli_adapter = __esm({
|
|
|
1513
1617
|
}
|
|
1514
1618
|
const tail = this.settledBuffer;
|
|
1515
1619
|
const screenText = this.terminalScreen.getText() || "";
|
|
1516
|
-
const
|
|
1620
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1621
|
+
const modal = this.runParseApproval(tail) || startupModal;
|
|
1517
1622
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
1518
|
-
const scriptStatus = rawScriptStatus;
|
|
1623
|
+
const scriptStatus = startupModal ? "waiting_approval" : rawScriptStatus;
|
|
1519
1624
|
const parsedTranscript = this.parseCurrentTranscript(
|
|
1520
1625
|
this.committedMessages,
|
|
1521
1626
|
this.responseBuffer,
|
|
@@ -1710,7 +1815,24 @@ var init_provider_cli_adapter = __esm({
|
|
|
1710
1815
|
this.recordTrace("finish_response", {
|
|
1711
1816
|
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
1712
1817
|
});
|
|
1713
|
-
this.commitCurrentTranscript();
|
|
1818
|
+
const commitResult = this.commitCurrentTranscript();
|
|
1819
|
+
if (this.shouldRetryFinishResponse(commitResult)) {
|
|
1820
|
+
this.finishRetryCount += 1;
|
|
1821
|
+
this.recordTrace("finish_response_retry", {
|
|
1822
|
+
retryCount: this.finishRetryCount,
|
|
1823
|
+
retryDelayMs: _ProviderCliAdapter.FINISH_RETRY_DELAY_MS,
|
|
1824
|
+
assistantContent: this.summarizeTraceText(commitResult.assistantContent, 220),
|
|
1825
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
1826
|
+
});
|
|
1827
|
+
if (this.finishRetryTimer) clearTimeout(this.finishRetryTimer);
|
|
1828
|
+
this.finishRetryTimer = setTimeout(() => {
|
|
1829
|
+
this.finishRetryTimer = null;
|
|
1830
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
1831
|
+
this.finishResponse();
|
|
1832
|
+
}
|
|
1833
|
+
}, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1714
1836
|
if (this.responseTimeout) {
|
|
1715
1837
|
clearTimeout(this.responseTimeout);
|
|
1716
1838
|
this.responseTimeout = null;
|
|
@@ -1727,11 +1849,16 @@ var init_provider_cli_adapter = __esm({
|
|
|
1727
1849
|
clearTimeout(this.submitRetryTimer);
|
|
1728
1850
|
this.submitRetryTimer = null;
|
|
1729
1851
|
}
|
|
1852
|
+
if (this.finishRetryTimer) {
|
|
1853
|
+
clearTimeout(this.finishRetryTimer);
|
|
1854
|
+
this.finishRetryTimer = null;
|
|
1855
|
+
}
|
|
1730
1856
|
this.responseBuffer = "";
|
|
1731
1857
|
this.isWaitingForResponse = false;
|
|
1732
1858
|
this.responseSettleIgnoreUntil = 0;
|
|
1733
1859
|
this.submitRetryUsed = false;
|
|
1734
1860
|
this.submitRetryPromptSnippet = "";
|
|
1861
|
+
this.finishRetryCount = 0;
|
|
1735
1862
|
this.currentTurnScope = null;
|
|
1736
1863
|
this.activeModal = null;
|
|
1737
1864
|
this.setStatus("idle", "response_finished");
|
|
@@ -1745,6 +1872,13 @@ var init_provider_cli_adapter = __esm({
|
|
|
1745
1872
|
);
|
|
1746
1873
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
1747
1874
|
this.committedMessages = this.normalizeParsedMessages(parsed.messages);
|
|
1875
|
+
const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
|
|
1876
|
+
if (promptForTrim) {
|
|
1877
|
+
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
1878
|
+
if (lastAssistantForTrim) {
|
|
1879
|
+
lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1748
1882
|
this.syncMessageViews();
|
|
1749
1883
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
1750
1884
|
this.recordTrace("commit_transcript", {
|
|
@@ -1760,7 +1894,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1760
1894
|
`[${this.cliType}] Commit without assistant turn: prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} responseBuffer=${JSON.stringify(this.summarizeTraceText(this.responseBuffer, 220)).slice(0, 260)} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
|
|
1761
1895
|
);
|
|
1762
1896
|
}
|
|
1897
|
+
return {
|
|
1898
|
+
hasAssistant: !!lastAssistant,
|
|
1899
|
+
assistantContent: lastAssistant?.content || ""
|
|
1900
|
+
};
|
|
1763
1901
|
}
|
|
1902
|
+
return {
|
|
1903
|
+
hasAssistant: false,
|
|
1904
|
+
assistantContent: ""
|
|
1905
|
+
};
|
|
1764
1906
|
}
|
|
1765
1907
|
// ─── Script Execution ──────────────────────────
|
|
1766
1908
|
runDetectStatus(text) {
|
|
@@ -1839,7 +1981,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1839
1981
|
if (!this.cliScripts?.parseOutput) return null;
|
|
1840
1982
|
try {
|
|
1841
1983
|
const input = this.buildParseInput(baseMessages, partialResponse, scope);
|
|
1842
|
-
|
|
1984
|
+
const parsed = this.cliScripts.parseOutput(input);
|
|
1985
|
+
const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
|
|
1986
|
+
if (parsed && Array.isArray(parsed.messages) && promptForTrim) {
|
|
1987
|
+
const lastAssistant = [...parsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
|
|
1988
|
+
if (lastAssistant) {
|
|
1989
|
+
lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return parsed;
|
|
1843
1993
|
} catch (e) {
|
|
1844
1994
|
LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
|
|
1845
1995
|
return null;
|
|
@@ -1884,10 +2034,19 @@ ${data.message || ""}`.trim();
|
|
|
1884
2034
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
1885
2035
|
if (this.isWaitingForResponse) return;
|
|
1886
2036
|
await this.waitForInteractivePrompt();
|
|
2037
|
+
const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
|
|
2038
|
+
if (blockingModal || this.currentStatus === "waiting_approval") {
|
|
2039
|
+
throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
|
|
2040
|
+
}
|
|
1887
2041
|
this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
|
|
1888
2042
|
this.syncMessageViews();
|
|
1889
2043
|
this.isWaitingForResponse = true;
|
|
1890
2044
|
this.responseBuffer = "";
|
|
2045
|
+
this.finishRetryCount = 0;
|
|
2046
|
+
if (this.finishRetryTimer) {
|
|
2047
|
+
clearTimeout(this.finishRetryTimer);
|
|
2048
|
+
this.finishRetryTimer = null;
|
|
2049
|
+
}
|
|
1891
2050
|
this.clearIdleFinishCandidate("send_message");
|
|
1892
2051
|
this.currentTurnScope = {
|
|
1893
2052
|
prompt: text,
|
|
@@ -2115,6 +2274,10 @@ ${data.message || ""}`.trim();
|
|
|
2115
2274
|
clearTimeout(this.submitRetryTimer);
|
|
2116
2275
|
this.submitRetryTimer = null;
|
|
2117
2276
|
}
|
|
2277
|
+
if (this.finishRetryTimer) {
|
|
2278
|
+
clearTimeout(this.finishRetryTimer);
|
|
2279
|
+
this.finishRetryTimer = null;
|
|
2280
|
+
}
|
|
2118
2281
|
if (this.responseTimeout) {
|
|
2119
2282
|
clearTimeout(this.responseTimeout);
|
|
2120
2283
|
this.responseTimeout = null;
|
|
@@ -2138,6 +2301,7 @@ ${data.message || ""}`.trim();
|
|
|
2138
2301
|
this.ptyOutputFlushTimer = null;
|
|
2139
2302
|
}
|
|
2140
2303
|
this.ptyOutputBuffer = "";
|
|
2304
|
+
this.finishRetryCount = 0;
|
|
2141
2305
|
if (this.ptyProcess) {
|
|
2142
2306
|
this.ptyProcess.write("");
|
|
2143
2307
|
setTimeout(() => {
|
|
@@ -2168,6 +2332,10 @@ ${data.message || ""}`.trim();
|
|
|
2168
2332
|
clearTimeout(this.submitRetryTimer);
|
|
2169
2333
|
this.submitRetryTimer = null;
|
|
2170
2334
|
}
|
|
2335
|
+
if (this.finishRetryTimer) {
|
|
2336
|
+
clearTimeout(this.finishRetryTimer);
|
|
2337
|
+
this.finishRetryTimer = null;
|
|
2338
|
+
}
|
|
2171
2339
|
if (this.responseTimeout) {
|
|
2172
2340
|
clearTimeout(this.responseTimeout);
|
|
2173
2341
|
this.responseTimeout = null;
|
|
@@ -2191,6 +2359,7 @@ ${data.message || ""}`.trim();
|
|
|
2191
2359
|
this.ptyOutputFlushTimer = null;
|
|
2192
2360
|
}
|
|
2193
2361
|
this.ptyOutputBuffer = "";
|
|
2362
|
+
this.finishRetryCount = 0;
|
|
2194
2363
|
if (this.ptyProcess) {
|
|
2195
2364
|
try {
|
|
2196
2365
|
if (typeof this.ptyProcess.detach === "function") {
|
|
@@ -2227,6 +2396,11 @@ ${data.message || ""}`.trim();
|
|
|
2227
2396
|
this.ptyOutputFlushTimer = null;
|
|
2228
2397
|
}
|
|
2229
2398
|
this.ptyOutputBuffer = "";
|
|
2399
|
+
if (this.finishRetryTimer) {
|
|
2400
|
+
clearTimeout(this.finishRetryTimer);
|
|
2401
|
+
this.finishRetryTimer = null;
|
|
2402
|
+
}
|
|
2403
|
+
this.finishRetryCount = 0;
|
|
2230
2404
|
this.terminalScreen.reset();
|
|
2231
2405
|
this.ptyProcess?.clearBuffer?.();
|
|
2232
2406
|
this.onStatusChange?.();
|
|
@@ -2246,10 +2420,11 @@ ${data.message || ""}`.trim();
|
|
|
2246
2420
|
}
|
|
2247
2421
|
resolveModal(buttonIndex) {
|
|
2248
2422
|
if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
|
|
2423
|
+
const modal = this.activeModal;
|
|
2249
2424
|
this.clearIdleFinishCandidate("resolve_modal");
|
|
2250
2425
|
this.recordTrace("resolve_modal", {
|
|
2251
2426
|
buttonIndex,
|
|
2252
|
-
activeModal:
|
|
2427
|
+
activeModal: modal
|
|
2253
2428
|
});
|
|
2254
2429
|
this.activeModal = null;
|
|
2255
2430
|
this.lastApprovalResolvedAt = Date.now();
|
|
@@ -2260,7 +2435,9 @@ ${data.message || ""}`.trim();
|
|
|
2260
2435
|
}
|
|
2261
2436
|
this.setStatus("generating", "approval_resolved");
|
|
2262
2437
|
this.onStatusChange?.();
|
|
2263
|
-
if (
|
|
2438
|
+
if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
|
|
2439
|
+
this.ptyProcess.write("\r");
|
|
2440
|
+
} else if (buttonIndex in this.approvalKeys) {
|
|
2264
2441
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
2265
2442
|
} else {
|
|
2266
2443
|
const DOWN = "\x1B[B";
|
|
@@ -11598,7 +11775,10 @@ function appendUpgradeLog(message) {
|
|
|
11598
11775
|
}
|
|
11599
11776
|
}
|
|
11600
11777
|
function getNpmExecutable() {
|
|
11601
|
-
return
|
|
11778
|
+
return "npm";
|
|
11779
|
+
}
|
|
11780
|
+
function getNpmExecOptions() {
|
|
11781
|
+
return { shell: process.platform === "win32" };
|
|
11602
11782
|
}
|
|
11603
11783
|
function killPid(pid) {
|
|
11604
11784
|
try {
|
|
@@ -11660,9 +11840,10 @@ function removeDaemonPidFile() {
|
|
|
11660
11840
|
}
|
|
11661
11841
|
}
|
|
11662
11842
|
function cleanupStaleGlobalInstallDirs(pkgName) {
|
|
11663
|
-
const
|
|
11843
|
+
const npmExecOpts = getNpmExecOptions();
|
|
11844
|
+
const npmRoot = execFileSync(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
11664
11845
|
if (!npmRoot) return;
|
|
11665
|
-
const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8" }).trim();
|
|
11846
|
+
const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
11666
11847
|
const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
|
|
11667
11848
|
const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
|
|
11668
11849
|
const binNames = /* @__PURE__ */ new Set([packageBaseName]);
|
|
@@ -11723,7 +11904,8 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
11723
11904
|
{
|
|
11724
11905
|
encoding: "utf8",
|
|
11725
11906
|
stdio: "pipe",
|
|
11726
|
-
maxBuffer: 20 * 1024 * 1024
|
|
11907
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
11908
|
+
...getNpmExecOptions()
|
|
11727
11909
|
}
|
|
11728
11910
|
);
|
|
11729
11911
|
if (installOutput.trim()) {
|
|
@@ -14945,6 +15127,53 @@ async function runCliExerciseInternal(ctx, body) {
|
|
|
14945
15127
|
let lastModalKey = "";
|
|
14946
15128
|
let idleSince = 0;
|
|
14947
15129
|
let sawBusy = false;
|
|
15130
|
+
const noteStatus = (status) => {
|
|
15131
|
+
if (status !== lastStatus) {
|
|
15132
|
+
statusesSeen.push(status);
|
|
15133
|
+
lastStatus = status;
|
|
15134
|
+
}
|
|
15135
|
+
};
|
|
15136
|
+
const resolveActiveModalIfNeeded = (status, modal) => {
|
|
15137
|
+
if (!autoResolveApprovals || status !== "waiting_approval" || !modal || !Array.isArray(modal.buttons) || modal.buttons.length === 0) {
|
|
15138
|
+
return false;
|
|
15139
|
+
}
|
|
15140
|
+
const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
|
|
15141
|
+
const modalKey = JSON.stringify({
|
|
15142
|
+
message: modal.message || "",
|
|
15143
|
+
buttons: modal.buttons,
|
|
15144
|
+
index: clampedIndex
|
|
15145
|
+
});
|
|
15146
|
+
if (modalKey === lastModalKey || typeof bundle?.adapter?.resolveModal !== "function") {
|
|
15147
|
+
return false;
|
|
15148
|
+
}
|
|
15149
|
+
lastModalKey = modalKey;
|
|
15150
|
+
approvalsResolved.push({
|
|
15151
|
+
at: Date.now(),
|
|
15152
|
+
buttonIndex: clampedIndex,
|
|
15153
|
+
label: modal.buttons[clampedIndex] || null
|
|
15154
|
+
});
|
|
15155
|
+
bundle.adapter.resolveModal(clampedIndex);
|
|
15156
|
+
return true;
|
|
15157
|
+
};
|
|
15158
|
+
const preflightStartedAt = Date.now();
|
|
15159
|
+
while (Date.now() - preflightStartedAt < Math.max(1e3, readyTimeoutMs)) {
|
|
15160
|
+
bundle = getCliTargetBundle(ctx, type, bundle.target.instanceId);
|
|
15161
|
+
if (!bundle) {
|
|
15162
|
+
throw new Error("CLI instance disappeared before exercise send");
|
|
15163
|
+
}
|
|
15164
|
+
const debug = typeof bundle.adapter.getDebugState === "function" ? bundle.adapter.getDebugState() : null;
|
|
15165
|
+
const trace = typeof bundle.adapter.getTraceState === "function" ? bundle.adapter.getTraceState(traceLimit) : null;
|
|
15166
|
+
const status = String(debug?.status || bundle.target.status || "unknown");
|
|
15167
|
+
const modal = debug?.activeModal || trace?.activeModal || null;
|
|
15168
|
+
noteStatus(status);
|
|
15169
|
+
if (resolveActiveModalIfNeeded(status, modal)) {
|
|
15170
|
+
await sleep(150);
|
|
15171
|
+
continue;
|
|
15172
|
+
}
|
|
15173
|
+
const startupParseGate = !!debug?.startupParseGate;
|
|
15174
|
+
if (status === "idle" && !startupParseGate) break;
|
|
15175
|
+
await sleep(150);
|
|
15176
|
+
}
|
|
14948
15177
|
ctx.instanceManager.sendEvent(bundle.target.instanceId, "send_message", { text });
|
|
14949
15178
|
while (Date.now() - startAt < Math.max(1e3, timeoutMs)) {
|
|
14950
15179
|
await sleep(150);
|
|
@@ -14959,32 +15188,14 @@ async function runCliExerciseInternal(ctx, body) {
|
|
|
14959
15188
|
const sawSendMessage = traceEntries.some((entry) => entry?.type === "send_message");
|
|
14960
15189
|
const sawSubmitWrite = traceEntries.some((entry) => entry?.type === "submit_write");
|
|
14961
15190
|
const hasTurnStarted = sawSendMessage || sawSubmitWrite || !!debug?.currentTurnScope;
|
|
14962
|
-
|
|
14963
|
-
statusesSeen.push(status);
|
|
14964
|
-
lastStatus = status;
|
|
14965
|
-
}
|
|
15191
|
+
noteStatus(status);
|
|
14966
15192
|
if (status === "generating" || status === "waiting_approval") {
|
|
14967
15193
|
sawBusy = true;
|
|
14968
15194
|
idleSince = 0;
|
|
14969
15195
|
}
|
|
14970
15196
|
const modal = debug?.activeModal || trace?.activeModal || null;
|
|
14971
|
-
if (
|
|
14972
|
-
|
|
14973
|
-
const modalKey = JSON.stringify({
|
|
14974
|
-
message: modal.message || "",
|
|
14975
|
-
buttons: modal.buttons,
|
|
14976
|
-
index: clampedIndex
|
|
14977
|
-
});
|
|
14978
|
-
if (modalKey !== lastModalKey && typeof bundle.adapter.resolveModal === "function") {
|
|
14979
|
-
lastModalKey = modalKey;
|
|
14980
|
-
approvalsResolved.push({
|
|
14981
|
-
at: Date.now(),
|
|
14982
|
-
buttonIndex: clampedIndex,
|
|
14983
|
-
label: modal.buttons[clampedIndex] || null
|
|
14984
|
-
});
|
|
14985
|
-
bundle.adapter.resolveModal(clampedIndex);
|
|
14986
|
-
continue;
|
|
14987
|
-
}
|
|
15197
|
+
if (resolveActiveModalIfNeeded(status, modal)) {
|
|
15198
|
+
continue;
|
|
14988
15199
|
}
|
|
14989
15200
|
const traceCount = Number(trace?.entryCount || 0);
|
|
14990
15201
|
const hasProgress = hasTurnStarted && (traceCount > preTraceCount || statusesSeen.length > 1 || approvalsResolved.length > 0);
|
|
@@ -16639,6 +16850,13 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
16639
16850
|
lines.push("19. Literal string checks are allowed only for stable proper nouns or exact product chrome that cannot be expressed safely as a broader pattern. Everything else should generalize.");
|
|
16640
16851
|
lines.push("20. When a bug comes from noisy PTY text, first normalize and classify the line family; do NOT just append another special-case substring to the parser.");
|
|
16641
16852
|
lines.push("");
|
|
16853
|
+
if (verification?.focusAreas?.length) {
|
|
16854
|
+
lines.push("## Provider-Specific Focus Areas");
|
|
16855
|
+
for (const area of verification.focusAreas) {
|
|
16856
|
+
lines.push(`- ${area}`);
|
|
16857
|
+
}
|
|
16858
|
+
lines.push("");
|
|
16859
|
+
}
|
|
16642
16860
|
lines.push("## Task");
|
|
16643
16861
|
lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
|
|
16644
16862
|
lines.push("");
|