@adhdev/daemon-core 0.8.15 → 0.8.17
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.d.ts +2 -1
- package/dist/index.js +255 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +254 -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/src/index.ts +2 -1
|
@@ -66,6 +66,7 @@ export interface CliScriptInput {
|
|
|
66
66
|
screenText: string;
|
|
67
67
|
messages: CliChatMessage[];
|
|
68
68
|
partialResponse: string;
|
|
69
|
+
promptText?: string;
|
|
69
70
|
}
|
|
70
71
|
export interface CliTraceEntry {
|
|
71
72
|
id: number;
|
|
@@ -172,6 +173,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
172
173
|
private submitRetryUsed;
|
|
173
174
|
private submitRetryPromptSnippet;
|
|
174
175
|
private idleFinishCandidate;
|
|
176
|
+
private finishRetryTimer;
|
|
177
|
+
private finishRetryCount;
|
|
175
178
|
private resizeSuppressUntil;
|
|
176
179
|
private statusHistory;
|
|
177
180
|
private cliScripts;
|
|
@@ -190,6 +193,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
190
193
|
private static readonly MAX_TRACE_ENTRIES;
|
|
191
194
|
private readonly providerResolutionMeta;
|
|
192
195
|
private static readonly IDLE_FINISH_CONFIRM_MS;
|
|
196
|
+
private static readonly FINISH_RETRY_DELAY_MS;
|
|
197
|
+
private static readonly MAX_FINISH_RETRIES;
|
|
193
198
|
private syncMessageViews;
|
|
194
199
|
private normalizeParsedMessages;
|
|
195
200
|
private sliceFromOffset;
|
|
@@ -220,6 +225,10 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
220
225
|
private scheduleSettle;
|
|
221
226
|
private armApprovalExitTimeout;
|
|
222
227
|
private looksLikeVisibleIdlePrompt;
|
|
228
|
+
private looksLikeVisibleAssistantCandidate;
|
|
229
|
+
private shouldRetryFinishResponse;
|
|
230
|
+
private getStartupConfirmationModal;
|
|
231
|
+
private shouldResolveModalWithEnter;
|
|
223
232
|
private waitForInteractivePrompt;
|
|
224
233
|
private evaluateSettled;
|
|
225
234
|
private finishResponse;
|
package/dist/index.d.ts
CHANGED
|
@@ -39,7 +39,8 @@ export { DaemonCommandHandler } from './commands/handler.js';
|
|
|
39
39
|
export type { CommandResult, CommandContext } from './commands/handler.js';
|
|
40
40
|
export { DaemonCommandRouter } from './commands/router.js';
|
|
41
41
|
export type { CommandRouterDeps, CommandRouterResult } from './commands/router.js';
|
|
42
|
-
export { maybeRunDaemonUpgradeHelperFromEnv } from './commands/upgrade-helper.js';
|
|
42
|
+
export { maybeRunDaemonUpgradeHelperFromEnv, spawnDetachedDaemonUpgradeHelper } from './commands/upgrade-helper.js';
|
|
43
|
+
export type { DaemonUpgradeHelperPayload } from './commands/upgrade-helper.js';
|
|
43
44
|
export { DaemonStatusReporter } from './status/reporter.js';
|
|
44
45
|
export { buildSessionEntries, findCdpManager, hasCdpManager, isCdpConnected } from './status/builders.js';
|
|
45
46
|
export { buildStatusSnapshot } from './status/snapshot.js';
|
package/dist/index.js
CHANGED
|
@@ -905,6 +905,40 @@ function normalizeScreenSnapshot(text) {
|
|
|
905
905
|
function normalizeComparableMessageContent(text) {
|
|
906
906
|
return String(text || "").replace(/\s+/g, " ").trim();
|
|
907
907
|
}
|
|
908
|
+
function trimPromptEchoPrefix(text, promptText) {
|
|
909
|
+
const prompt = normalizeComparableMessageContent(String(promptText || ""));
|
|
910
|
+
if (!prompt) return String(text || "");
|
|
911
|
+
const lines = String(text || "").split(/\r\n|\n|\r/g);
|
|
912
|
+
let dropCount = 0;
|
|
913
|
+
for (let index = 0; index < Math.min(lines.length, 6); index += 1) {
|
|
914
|
+
const fragment = normalizeComparableMessageContent(lines[index].replace(/^[.…]+\s*/, ""));
|
|
915
|
+
if (!fragment) {
|
|
916
|
+
if (dropCount === index) dropCount = index + 1;
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
const fragmentWordCount = fragment ? fragment.split(/\s+/).filter(Boolean).length : 0;
|
|
920
|
+
const canBePromptEcho = fragment.length >= 16 || fragmentWordCount >= 4;
|
|
921
|
+
if (canBePromptEcho && prompt.includes(fragment)) {
|
|
922
|
+
dropCount = index + 1;
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
return lines.slice(dropCount).join("\n").trim();
|
|
928
|
+
}
|
|
929
|
+
function getLastUserPromptText(messages) {
|
|
930
|
+
const items = Array.isArray(messages) ? messages : [];
|
|
931
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
932
|
+
const message = items[index];
|
|
933
|
+
if (message?.role === "user" && typeof message.content === "string" && message.content.trim()) {
|
|
934
|
+
return message.content;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return "";
|
|
938
|
+
}
|
|
939
|
+
function looksLikeConfirmOnlyLabel(label) {
|
|
940
|
+
return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
|
|
941
|
+
}
|
|
908
942
|
function parsePatternEntry(x) {
|
|
909
943
|
if (x instanceof RegExp) return x;
|
|
910
944
|
if (x && typeof x === "object" && typeof x.source === "string") {
|
|
@@ -1041,6 +1075,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1041
1075
|
submitRetryUsed = false;
|
|
1042
1076
|
submitRetryPromptSnippet = "";
|
|
1043
1077
|
idleFinishCandidate = null;
|
|
1078
|
+
finishRetryTimer = null;
|
|
1079
|
+
finishRetryCount = 0;
|
|
1044
1080
|
// Resize redraw suppression
|
|
1045
1081
|
resizeSuppressUntil = 0;
|
|
1046
1082
|
// Debug: status transition history
|
|
@@ -1062,6 +1098,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1062
1098
|
static MAX_TRACE_ENTRIES = 250;
|
|
1063
1099
|
providerResolutionMeta;
|
|
1064
1100
|
static IDLE_FINISH_CONFIRM_MS = 900;
|
|
1101
|
+
static FINISH_RETRY_DELAY_MS = 300;
|
|
1102
|
+
static MAX_FINISH_RETRIES = 2;
|
|
1065
1103
|
syncMessageViews() {
|
|
1066
1104
|
this.messages = [...this.committedMessages];
|
|
1067
1105
|
this.structuredMessages = [...this.committedMessages];
|
|
@@ -1121,7 +1159,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1121
1159
|
recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
|
|
1122
1160
|
screenText: this.terminalScreen.getText(),
|
|
1123
1161
|
messages: [...baseMessages],
|
|
1124
|
-
partialResponse
|
|
1162
|
+
partialResponse,
|
|
1163
|
+
promptText: scope?.prompt || ""
|
|
1125
1164
|
};
|
|
1126
1165
|
}
|
|
1127
1166
|
setStatus(status, trigger) {
|
|
@@ -1364,6 +1403,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
1364
1403
|
this.terminalScreen.reset(24, 80);
|
|
1365
1404
|
this.pendingTerminalQueryTail = "";
|
|
1366
1405
|
this.currentTurnScope = null;
|
|
1406
|
+
this.finishRetryCount = 0;
|
|
1407
|
+
if (this.finishRetryTimer) {
|
|
1408
|
+
clearTimeout(this.finishRetryTimer);
|
|
1409
|
+
this.finishRetryTimer = null;
|
|
1410
|
+
}
|
|
1367
1411
|
this.ready = false;
|
|
1368
1412
|
await this.ptyProcess.ready;
|
|
1369
1413
|
this.recordTrace("ready", {
|
|
@@ -1410,11 +1454,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
1410
1454
|
if (this.startupParseGate) {
|
|
1411
1455
|
this.startupBuffer += cleanData;
|
|
1412
1456
|
const elapsed = Date.now() - this.spawnAt;
|
|
1413
|
-
const scriptStatus = this.runDetectStatus(this.startupBuffer);
|
|
1414
1457
|
const screenText = this.terminalScreen.getText() || "";
|
|
1458
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1459
|
+
const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
|
|
1415
1460
|
const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1416
1461
|
const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1417
|
-
const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
|
|
1462
|
+
const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || !!startupModal && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
|
|
1418
1463
|
if (isReady) {
|
|
1419
1464
|
this.startupParseGate = false;
|
|
1420
1465
|
this.ready = true;
|
|
@@ -1446,7 +1491,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1446
1491
|
this.approvalExitTimeout = setTimeout(() => {
|
|
1447
1492
|
if (this.currentStatus !== "waiting_approval") return;
|
|
1448
1493
|
const tail = this.recentOutputBuffer;
|
|
1449
|
-
const
|
|
1494
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
1495
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1496
|
+
const modal = this.runParseApproval(tail) || startupModal;
|
|
1450
1497
|
const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
|
|
1451
1498
|
if (stillWaiting) {
|
|
1452
1499
|
this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
|
|
@@ -1466,6 +1513,63 @@ var init_provider_cli_adapter = __esm({
|
|
|
1466
1513
|
if (!text.trim()) return false;
|
|
1467
1514
|
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);
|
|
1468
1515
|
}
|
|
1516
|
+
looksLikeVisibleAssistantCandidate(screenText) {
|
|
1517
|
+
const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
|
|
1518
|
+
for (const line of lines) {
|
|
1519
|
+
const trimmed = String(line || "").trim();
|
|
1520
|
+
if (!trimmed) continue;
|
|
1521
|
+
if (/^➜\s+\S+/.test(trimmed)) continue;
|
|
1522
|
+
if (/^Update available!/i.test(trimmed)) continue;
|
|
1523
|
+
if (/Claude Code v\d/i.test(trimmed)) continue;
|
|
1524
|
+
if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
|
|
1525
|
+
if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
|
|
1526
|
+
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
|
|
1527
|
+
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
|
|
1528
|
+
const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
|
|
1529
|
+
if (!assistantMatch) continue;
|
|
1530
|
+
const content = assistantMatch[1].trim();
|
|
1531
|
+
if (!content) continue;
|
|
1532
|
+
if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
|
|
1533
|
+
if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
|
|
1534
|
+
return true;
|
|
1535
|
+
}
|
|
1536
|
+
return false;
|
|
1537
|
+
}
|
|
1538
|
+
shouldRetryFinishResponse(commitResult) {
|
|
1539
|
+
if (!this.currentTurnScope) return false;
|
|
1540
|
+
if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
|
|
1541
|
+
if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
|
|
1542
|
+
if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
|
|
1543
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
1544
|
+
if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
|
|
1545
|
+
const now = Date.now();
|
|
1546
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
1547
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1548
|
+
return quietForMs < 1200 || screenStableMs < 1200 || !commitResult.hasAssistant;
|
|
1549
|
+
}
|
|
1550
|
+
getStartupConfirmationModal(screenText) {
|
|
1551
|
+
const text = sanitizeTerminalText(String(screenText || ""));
|
|
1552
|
+
if (!text.trim()) return null;
|
|
1553
|
+
if (this.cliType === "claude-cli") {
|
|
1554
|
+
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);
|
|
1555
|
+
const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
|
|
1556
|
+
if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
|
|
1557
|
+
return {
|
|
1558
|
+
message: "Confirm Claude Code project trust",
|
|
1559
|
+
buttons: ["Continue"]
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
return null;
|
|
1564
|
+
}
|
|
1565
|
+
shouldResolveModalWithEnter(modal, buttonIndex) {
|
|
1566
|
+
if (!modal || buttonIndex !== 0) return false;
|
|
1567
|
+
const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
|
|
1568
|
+
if (buttons.length !== 1) return false;
|
|
1569
|
+
const buttonLabel = String(buttons[0] || "").trim();
|
|
1570
|
+
const modalText = `${modal.message || ""} ${buttonLabel}`.trim();
|
|
1571
|
+
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);
|
|
1572
|
+
}
|
|
1469
1573
|
async waitForInteractivePrompt(maxWaitMs = 5e3) {
|
|
1470
1574
|
const startedAt = Date.now();
|
|
1471
1575
|
let loggedWait = false;
|
|
@@ -1515,9 +1619,10 @@ var init_provider_cli_adapter = __esm({
|
|
|
1515
1619
|
}
|
|
1516
1620
|
const tail = this.settledBuffer;
|
|
1517
1621
|
const screenText = this.terminalScreen.getText() || "";
|
|
1518
|
-
const
|
|
1622
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1623
|
+
const modal = this.runParseApproval(tail) || startupModal;
|
|
1519
1624
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
1520
|
-
const scriptStatus = rawScriptStatus;
|
|
1625
|
+
const scriptStatus = startupModal ? "waiting_approval" : rawScriptStatus;
|
|
1521
1626
|
const parsedTranscript = this.parseCurrentTranscript(
|
|
1522
1627
|
this.committedMessages,
|
|
1523
1628
|
this.responseBuffer,
|
|
@@ -1712,7 +1817,24 @@ var init_provider_cli_adapter = __esm({
|
|
|
1712
1817
|
this.recordTrace("finish_response", {
|
|
1713
1818
|
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
1714
1819
|
});
|
|
1715
|
-
this.commitCurrentTranscript();
|
|
1820
|
+
const commitResult = this.commitCurrentTranscript();
|
|
1821
|
+
if (this.shouldRetryFinishResponse(commitResult)) {
|
|
1822
|
+
this.finishRetryCount += 1;
|
|
1823
|
+
this.recordTrace("finish_response_retry", {
|
|
1824
|
+
retryCount: this.finishRetryCount,
|
|
1825
|
+
retryDelayMs: _ProviderCliAdapter.FINISH_RETRY_DELAY_MS,
|
|
1826
|
+
assistantContent: this.summarizeTraceText(commitResult.assistantContent, 220),
|
|
1827
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
1828
|
+
});
|
|
1829
|
+
if (this.finishRetryTimer) clearTimeout(this.finishRetryTimer);
|
|
1830
|
+
this.finishRetryTimer = setTimeout(() => {
|
|
1831
|
+
this.finishRetryTimer = null;
|
|
1832
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
1833
|
+
this.finishResponse();
|
|
1834
|
+
}
|
|
1835
|
+
}, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1716
1838
|
if (this.responseTimeout) {
|
|
1717
1839
|
clearTimeout(this.responseTimeout);
|
|
1718
1840
|
this.responseTimeout = null;
|
|
@@ -1729,11 +1851,16 @@ var init_provider_cli_adapter = __esm({
|
|
|
1729
1851
|
clearTimeout(this.submitRetryTimer);
|
|
1730
1852
|
this.submitRetryTimer = null;
|
|
1731
1853
|
}
|
|
1854
|
+
if (this.finishRetryTimer) {
|
|
1855
|
+
clearTimeout(this.finishRetryTimer);
|
|
1856
|
+
this.finishRetryTimer = null;
|
|
1857
|
+
}
|
|
1732
1858
|
this.responseBuffer = "";
|
|
1733
1859
|
this.isWaitingForResponse = false;
|
|
1734
1860
|
this.responseSettleIgnoreUntil = 0;
|
|
1735
1861
|
this.submitRetryUsed = false;
|
|
1736
1862
|
this.submitRetryPromptSnippet = "";
|
|
1863
|
+
this.finishRetryCount = 0;
|
|
1737
1864
|
this.currentTurnScope = null;
|
|
1738
1865
|
this.activeModal = null;
|
|
1739
1866
|
this.setStatus("idle", "response_finished");
|
|
@@ -1747,6 +1874,13 @@ var init_provider_cli_adapter = __esm({
|
|
|
1747
1874
|
);
|
|
1748
1875
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
1749
1876
|
this.committedMessages = this.normalizeParsedMessages(parsed.messages);
|
|
1877
|
+
const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
|
|
1878
|
+
if (promptForTrim) {
|
|
1879
|
+
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
1880
|
+
if (lastAssistantForTrim) {
|
|
1881
|
+
lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1750
1884
|
this.syncMessageViews();
|
|
1751
1885
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
1752
1886
|
this.recordTrace("commit_transcript", {
|
|
@@ -1762,7 +1896,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1762
1896
|
`[${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 || "-"}`
|
|
1763
1897
|
);
|
|
1764
1898
|
}
|
|
1899
|
+
return {
|
|
1900
|
+
hasAssistant: !!lastAssistant,
|
|
1901
|
+
assistantContent: lastAssistant?.content || ""
|
|
1902
|
+
};
|
|
1765
1903
|
}
|
|
1904
|
+
return {
|
|
1905
|
+
hasAssistant: false,
|
|
1906
|
+
assistantContent: ""
|
|
1907
|
+
};
|
|
1766
1908
|
}
|
|
1767
1909
|
// ─── Script Execution ──────────────────────────
|
|
1768
1910
|
runDetectStatus(text) {
|
|
@@ -1841,7 +1983,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1841
1983
|
if (!this.cliScripts?.parseOutput) return null;
|
|
1842
1984
|
try {
|
|
1843
1985
|
const input = this.buildParseInput(baseMessages, partialResponse, scope);
|
|
1844
|
-
|
|
1986
|
+
const parsed = this.cliScripts.parseOutput(input);
|
|
1987
|
+
const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
|
|
1988
|
+
if (parsed && Array.isArray(parsed.messages) && promptForTrim) {
|
|
1989
|
+
const lastAssistant = [...parsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
|
|
1990
|
+
if (lastAssistant) {
|
|
1991
|
+
lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
return parsed;
|
|
1845
1995
|
} catch (e) {
|
|
1846
1996
|
LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
|
|
1847
1997
|
return null;
|
|
@@ -1886,10 +2036,19 @@ ${data.message || ""}`.trim();
|
|
|
1886
2036
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
1887
2037
|
if (this.isWaitingForResponse) return;
|
|
1888
2038
|
await this.waitForInteractivePrompt();
|
|
2039
|
+
const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
|
|
2040
|
+
if (blockingModal || this.currentStatus === "waiting_approval") {
|
|
2041
|
+
throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
|
|
2042
|
+
}
|
|
1889
2043
|
this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
|
|
1890
2044
|
this.syncMessageViews();
|
|
1891
2045
|
this.isWaitingForResponse = true;
|
|
1892
2046
|
this.responseBuffer = "";
|
|
2047
|
+
this.finishRetryCount = 0;
|
|
2048
|
+
if (this.finishRetryTimer) {
|
|
2049
|
+
clearTimeout(this.finishRetryTimer);
|
|
2050
|
+
this.finishRetryTimer = null;
|
|
2051
|
+
}
|
|
1893
2052
|
this.clearIdleFinishCandidate("send_message");
|
|
1894
2053
|
this.currentTurnScope = {
|
|
1895
2054
|
prompt: text,
|
|
@@ -2117,6 +2276,10 @@ ${data.message || ""}`.trim();
|
|
|
2117
2276
|
clearTimeout(this.submitRetryTimer);
|
|
2118
2277
|
this.submitRetryTimer = null;
|
|
2119
2278
|
}
|
|
2279
|
+
if (this.finishRetryTimer) {
|
|
2280
|
+
clearTimeout(this.finishRetryTimer);
|
|
2281
|
+
this.finishRetryTimer = null;
|
|
2282
|
+
}
|
|
2120
2283
|
if (this.responseTimeout) {
|
|
2121
2284
|
clearTimeout(this.responseTimeout);
|
|
2122
2285
|
this.responseTimeout = null;
|
|
@@ -2140,6 +2303,7 @@ ${data.message || ""}`.trim();
|
|
|
2140
2303
|
this.ptyOutputFlushTimer = null;
|
|
2141
2304
|
}
|
|
2142
2305
|
this.ptyOutputBuffer = "";
|
|
2306
|
+
this.finishRetryCount = 0;
|
|
2143
2307
|
if (this.ptyProcess) {
|
|
2144
2308
|
this.ptyProcess.write("");
|
|
2145
2309
|
setTimeout(() => {
|
|
@@ -2170,6 +2334,10 @@ ${data.message || ""}`.trim();
|
|
|
2170
2334
|
clearTimeout(this.submitRetryTimer);
|
|
2171
2335
|
this.submitRetryTimer = null;
|
|
2172
2336
|
}
|
|
2337
|
+
if (this.finishRetryTimer) {
|
|
2338
|
+
clearTimeout(this.finishRetryTimer);
|
|
2339
|
+
this.finishRetryTimer = null;
|
|
2340
|
+
}
|
|
2173
2341
|
if (this.responseTimeout) {
|
|
2174
2342
|
clearTimeout(this.responseTimeout);
|
|
2175
2343
|
this.responseTimeout = null;
|
|
@@ -2193,6 +2361,7 @@ ${data.message || ""}`.trim();
|
|
|
2193
2361
|
this.ptyOutputFlushTimer = null;
|
|
2194
2362
|
}
|
|
2195
2363
|
this.ptyOutputBuffer = "";
|
|
2364
|
+
this.finishRetryCount = 0;
|
|
2196
2365
|
if (this.ptyProcess) {
|
|
2197
2366
|
try {
|
|
2198
2367
|
if (typeof this.ptyProcess.detach === "function") {
|
|
@@ -2229,6 +2398,11 @@ ${data.message || ""}`.trim();
|
|
|
2229
2398
|
this.ptyOutputFlushTimer = null;
|
|
2230
2399
|
}
|
|
2231
2400
|
this.ptyOutputBuffer = "";
|
|
2401
|
+
if (this.finishRetryTimer) {
|
|
2402
|
+
clearTimeout(this.finishRetryTimer);
|
|
2403
|
+
this.finishRetryTimer = null;
|
|
2404
|
+
}
|
|
2405
|
+
this.finishRetryCount = 0;
|
|
2232
2406
|
this.terminalScreen.reset();
|
|
2233
2407
|
this.ptyProcess?.clearBuffer?.();
|
|
2234
2408
|
this.onStatusChange?.();
|
|
@@ -2248,10 +2422,11 @@ ${data.message || ""}`.trim();
|
|
|
2248
2422
|
}
|
|
2249
2423
|
resolveModal(buttonIndex) {
|
|
2250
2424
|
if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
|
|
2425
|
+
const modal = this.activeModal;
|
|
2251
2426
|
this.clearIdleFinishCandidate("resolve_modal");
|
|
2252
2427
|
this.recordTrace("resolve_modal", {
|
|
2253
2428
|
buttonIndex,
|
|
2254
|
-
activeModal:
|
|
2429
|
+
activeModal: modal
|
|
2255
2430
|
});
|
|
2256
2431
|
this.activeModal = null;
|
|
2257
2432
|
this.lastApprovalResolvedAt = Date.now();
|
|
@@ -2262,7 +2437,9 @@ ${data.message || ""}`.trim();
|
|
|
2262
2437
|
}
|
|
2263
2438
|
this.setStatus("generating", "approval_resolved");
|
|
2264
2439
|
this.onStatusChange?.();
|
|
2265
|
-
if (
|
|
2440
|
+
if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
|
|
2441
|
+
this.ptyProcess.write("\r");
|
|
2442
|
+
} else if (buttonIndex in this.approvalKeys) {
|
|
2266
2443
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
2267
2444
|
} else {
|
|
2268
2445
|
const DOWN = "\x1B[B";
|
|
@@ -2440,6 +2617,7 @@ __export(index_exports, {
|
|
|
2440
2617
|
setLogLevel: () => setLogLevel,
|
|
2441
2618
|
setupIdeInstance: () => setupIdeInstance,
|
|
2442
2619
|
shutdownDaemonComponents: () => shutdownDaemonComponents,
|
|
2620
|
+
spawnDetachedDaemonUpgradeHelper: () => spawnDetachedDaemonUpgradeHelper,
|
|
2443
2621
|
startDaemonDevSupport: () => startDaemonDevSupport,
|
|
2444
2622
|
updateConfig: () => updateConfig,
|
|
2445
2623
|
upsertSavedProviderSession: () => upsertSavedProviderSession
|
|
@@ -11674,7 +11852,10 @@ function appendUpgradeLog(message) {
|
|
|
11674
11852
|
}
|
|
11675
11853
|
}
|
|
11676
11854
|
function getNpmExecutable() {
|
|
11677
|
-
return
|
|
11855
|
+
return "npm";
|
|
11856
|
+
}
|
|
11857
|
+
function getNpmExecOptions() {
|
|
11858
|
+
return { shell: process.platform === "win32" };
|
|
11678
11859
|
}
|
|
11679
11860
|
function killPid(pid) {
|
|
11680
11861
|
try {
|
|
@@ -11736,9 +11917,10 @@ function removeDaemonPidFile() {
|
|
|
11736
11917
|
}
|
|
11737
11918
|
}
|
|
11738
11919
|
function cleanupStaleGlobalInstallDirs(pkgName) {
|
|
11739
|
-
const
|
|
11920
|
+
const npmExecOpts = getNpmExecOptions();
|
|
11921
|
+
const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
11740
11922
|
if (!npmRoot) return;
|
|
11741
|
-
const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8" }).trim();
|
|
11923
|
+
const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
11742
11924
|
const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
|
|
11743
11925
|
const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
|
|
11744
11926
|
const binNames = /* @__PURE__ */ new Set([packageBaseName]);
|
|
@@ -11799,7 +11981,8 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
11799
11981
|
{
|
|
11800
11982
|
encoding: "utf8",
|
|
11801
11983
|
stdio: "pipe",
|
|
11802
|
-
maxBuffer: 20 * 1024 * 1024
|
|
11984
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
11985
|
+
...getNpmExecOptions()
|
|
11803
11986
|
}
|
|
11804
11987
|
);
|
|
11805
11988
|
if (installOutput.trim()) {
|
|
@@ -15021,6 +15204,53 @@ async function runCliExerciseInternal(ctx, body) {
|
|
|
15021
15204
|
let lastModalKey = "";
|
|
15022
15205
|
let idleSince = 0;
|
|
15023
15206
|
let sawBusy = false;
|
|
15207
|
+
const noteStatus = (status) => {
|
|
15208
|
+
if (status !== lastStatus) {
|
|
15209
|
+
statusesSeen.push(status);
|
|
15210
|
+
lastStatus = status;
|
|
15211
|
+
}
|
|
15212
|
+
};
|
|
15213
|
+
const resolveActiveModalIfNeeded = (status, modal) => {
|
|
15214
|
+
if (!autoResolveApprovals || status !== "waiting_approval" || !modal || !Array.isArray(modal.buttons) || modal.buttons.length === 0) {
|
|
15215
|
+
return false;
|
|
15216
|
+
}
|
|
15217
|
+
const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
|
|
15218
|
+
const modalKey = JSON.stringify({
|
|
15219
|
+
message: modal.message || "",
|
|
15220
|
+
buttons: modal.buttons,
|
|
15221
|
+
index: clampedIndex
|
|
15222
|
+
});
|
|
15223
|
+
if (modalKey === lastModalKey || typeof bundle?.adapter?.resolveModal !== "function") {
|
|
15224
|
+
return false;
|
|
15225
|
+
}
|
|
15226
|
+
lastModalKey = modalKey;
|
|
15227
|
+
approvalsResolved.push({
|
|
15228
|
+
at: Date.now(),
|
|
15229
|
+
buttonIndex: clampedIndex,
|
|
15230
|
+
label: modal.buttons[clampedIndex] || null
|
|
15231
|
+
});
|
|
15232
|
+
bundle.adapter.resolveModal(clampedIndex);
|
|
15233
|
+
return true;
|
|
15234
|
+
};
|
|
15235
|
+
const preflightStartedAt = Date.now();
|
|
15236
|
+
while (Date.now() - preflightStartedAt < Math.max(1e3, readyTimeoutMs)) {
|
|
15237
|
+
bundle = getCliTargetBundle(ctx, type, bundle.target.instanceId);
|
|
15238
|
+
if (!bundle) {
|
|
15239
|
+
throw new Error("CLI instance disappeared before exercise send");
|
|
15240
|
+
}
|
|
15241
|
+
const debug = typeof bundle.adapter.getDebugState === "function" ? bundle.adapter.getDebugState() : null;
|
|
15242
|
+
const trace = typeof bundle.adapter.getTraceState === "function" ? bundle.adapter.getTraceState(traceLimit) : null;
|
|
15243
|
+
const status = String(debug?.status || bundle.target.status || "unknown");
|
|
15244
|
+
const modal = debug?.activeModal || trace?.activeModal || null;
|
|
15245
|
+
noteStatus(status);
|
|
15246
|
+
if (resolveActiveModalIfNeeded(status, modal)) {
|
|
15247
|
+
await sleep(150);
|
|
15248
|
+
continue;
|
|
15249
|
+
}
|
|
15250
|
+
const startupParseGate = !!debug?.startupParseGate;
|
|
15251
|
+
if (status === "idle" && !startupParseGate) break;
|
|
15252
|
+
await sleep(150);
|
|
15253
|
+
}
|
|
15024
15254
|
ctx.instanceManager.sendEvent(bundle.target.instanceId, "send_message", { text });
|
|
15025
15255
|
while (Date.now() - startAt < Math.max(1e3, timeoutMs)) {
|
|
15026
15256
|
await sleep(150);
|
|
@@ -15035,32 +15265,14 @@ async function runCliExerciseInternal(ctx, body) {
|
|
|
15035
15265
|
const sawSendMessage = traceEntries.some((entry) => entry?.type === "send_message");
|
|
15036
15266
|
const sawSubmitWrite = traceEntries.some((entry) => entry?.type === "submit_write");
|
|
15037
15267
|
const hasTurnStarted = sawSendMessage || sawSubmitWrite || !!debug?.currentTurnScope;
|
|
15038
|
-
|
|
15039
|
-
statusesSeen.push(status);
|
|
15040
|
-
lastStatus = status;
|
|
15041
|
-
}
|
|
15268
|
+
noteStatus(status);
|
|
15042
15269
|
if (status === "generating" || status === "waiting_approval") {
|
|
15043
15270
|
sawBusy = true;
|
|
15044
15271
|
idleSince = 0;
|
|
15045
15272
|
}
|
|
15046
15273
|
const modal = debug?.activeModal || trace?.activeModal || null;
|
|
15047
|
-
if (
|
|
15048
|
-
|
|
15049
|
-
const modalKey = JSON.stringify({
|
|
15050
|
-
message: modal.message || "",
|
|
15051
|
-
buttons: modal.buttons,
|
|
15052
|
-
index: clampedIndex
|
|
15053
|
-
});
|
|
15054
|
-
if (modalKey !== lastModalKey && typeof bundle.adapter.resolveModal === "function") {
|
|
15055
|
-
lastModalKey = modalKey;
|
|
15056
|
-
approvalsResolved.push({
|
|
15057
|
-
at: Date.now(),
|
|
15058
|
-
buttonIndex: clampedIndex,
|
|
15059
|
-
label: modal.buttons[clampedIndex] || null
|
|
15060
|
-
});
|
|
15061
|
-
bundle.adapter.resolveModal(clampedIndex);
|
|
15062
|
-
continue;
|
|
15063
|
-
}
|
|
15274
|
+
if (resolveActiveModalIfNeeded(status, modal)) {
|
|
15275
|
+
continue;
|
|
15064
15276
|
}
|
|
15065
15277
|
const traceCount = Number(trace?.entryCount || 0);
|
|
15066
15278
|
const hasProgress = hasTurnStarted && (traceCount > preTraceCount || statusesSeen.length > 1 || approvalsResolved.length > 0);
|
|
@@ -16715,6 +16927,13 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
16715
16927
|
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.");
|
|
16716
16928
|
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.");
|
|
16717
16929
|
lines.push("");
|
|
16930
|
+
if (verification?.focusAreas?.length) {
|
|
16931
|
+
lines.push("## Provider-Specific Focus Areas");
|
|
16932
|
+
for (const area of verification.focusAreas) {
|
|
16933
|
+
lines.push(`- ${area}`);
|
|
16934
|
+
}
|
|
16935
|
+
lines.push("");
|
|
16936
|
+
}
|
|
16718
16937
|
lines.push("## Task");
|
|
16719
16938
|
lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
|
|
16720
16939
|
lines.push("");
|
|
@@ -19401,6 +19620,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
19401
19620
|
setLogLevel,
|
|
19402
19621
|
setupIdeInstance,
|
|
19403
19622
|
shutdownDaemonComponents,
|
|
19623
|
+
spawnDetachedDaemonUpgradeHelper,
|
|
19404
19624
|
startDaemonDevSupport,
|
|
19405
19625
|
updateConfig,
|
|
19406
19626
|
upsertSavedProviderSession
|