@adhdev/daemon-core 0.8.14 → 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 +278 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +281 -62
- 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 +189 -18
- package/src/cli-adapters/pty-transport.ts +17 -6
- 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
|
@@ -700,17 +700,35 @@ var init_terminal_screen = __esm({
|
|
|
700
700
|
}
|
|
701
701
|
});
|
|
702
702
|
|
|
703
|
+
// src/cli-adapters/spawn-env.ts
|
|
704
|
+
import {
|
|
705
|
+
sanitizeSpawnEnv,
|
|
706
|
+
applyTerminalColorEnv,
|
|
707
|
+
ensureNodePtySpawnHelperPermissions
|
|
708
|
+
} from "@adhdev/session-host-core";
|
|
709
|
+
var init_spawn_env = __esm({
|
|
710
|
+
"src/cli-adapters/spawn-env.ts"() {
|
|
711
|
+
"use strict";
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
703
715
|
// src/cli-adapters/pty-transport.ts
|
|
704
716
|
import * as os7 from "os";
|
|
705
|
-
|
|
717
|
+
function loadNodePty() {
|
|
718
|
+
if (cachedPty !== void 0) return cachedPty;
|
|
719
|
+
try {
|
|
720
|
+
cachedPty = __require("node-pty");
|
|
721
|
+
ensureNodePtySpawnHelperPermissions();
|
|
722
|
+
} catch {
|
|
723
|
+
cachedPty = null;
|
|
724
|
+
}
|
|
725
|
+
return cachedPty;
|
|
726
|
+
}
|
|
727
|
+
var cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory;
|
|
706
728
|
var init_pty_transport = __esm({
|
|
707
729
|
"src/cli-adapters/pty-transport.ts"() {
|
|
708
730
|
"use strict";
|
|
709
|
-
|
|
710
|
-
pty = __require("node-pty");
|
|
711
|
-
} catch {
|
|
712
|
-
pty = null;
|
|
713
|
-
}
|
|
731
|
+
init_spawn_env();
|
|
714
732
|
NodePtyRuntimeTransport = class {
|
|
715
733
|
constructor(handle) {
|
|
716
734
|
this.handle = handle;
|
|
@@ -741,6 +759,7 @@ var init_pty_transport = __esm({
|
|
|
741
759
|
};
|
|
742
760
|
NodePtyTransportFactory = class {
|
|
743
761
|
spawn(command, args, options) {
|
|
762
|
+
const pty = loadNodePty();
|
|
744
763
|
if (!pty) throw new Error("node-pty is not installed");
|
|
745
764
|
let cwd = options.cwd;
|
|
746
765
|
if (cwd) {
|
|
@@ -765,18 +784,6 @@ var init_pty_transport = __esm({
|
|
|
765
784
|
}
|
|
766
785
|
});
|
|
767
786
|
|
|
768
|
-
// src/cli-adapters/spawn-env.ts
|
|
769
|
-
import {
|
|
770
|
-
sanitizeSpawnEnv,
|
|
771
|
-
applyTerminalColorEnv,
|
|
772
|
-
ensureNodePtySpawnHelperPermissions
|
|
773
|
-
} from "@adhdev/session-host-core";
|
|
774
|
-
var init_spawn_env = __esm({
|
|
775
|
-
"src/cli-adapters/spawn-env.ts"() {
|
|
776
|
-
"use strict";
|
|
777
|
-
}
|
|
778
|
-
});
|
|
779
|
-
|
|
780
787
|
// src/cli-adapters/provider-cli-adapter.ts
|
|
781
788
|
var provider_cli_adapter_exports = {};
|
|
782
789
|
__export(provider_cli_adapter_exports, {
|
|
@@ -899,6 +906,40 @@ function normalizeScreenSnapshot(text) {
|
|
|
899
906
|
function normalizeComparableMessageContent(text) {
|
|
900
907
|
return String(text || "").replace(/\s+/g, " ").trim();
|
|
901
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
|
+
}
|
|
902
943
|
function parsePatternEntry(x) {
|
|
903
944
|
if (x instanceof RegExp) return x;
|
|
904
945
|
if (x && typeof x === "object" && typeof x.source === "string") {
|
|
@@ -923,7 +964,7 @@ function normalizeCliProviderForRuntime(raw) {
|
|
|
923
964
|
}
|
|
924
965
|
};
|
|
925
966
|
}
|
|
926
|
-
var
|
|
967
|
+
var buildCliSpawnEnv, ProviderCliAdapter;
|
|
927
968
|
var init_provider_cli_adapter = __esm({
|
|
928
969
|
"src/cli-adapters/provider-cli-adapter.ts"() {
|
|
929
970
|
"use strict";
|
|
@@ -931,12 +972,6 @@ var init_provider_cli_adapter = __esm({
|
|
|
931
972
|
init_terminal_screen();
|
|
932
973
|
init_pty_transport();
|
|
933
974
|
init_spawn_env();
|
|
934
|
-
try {
|
|
935
|
-
pty2 = __require("node-pty");
|
|
936
|
-
ensureNodePtySpawnHelperPermissions((msg) => LOG.info("CLI", msg));
|
|
937
|
-
} catch {
|
|
938
|
-
LOG.error("CLI", "[ProviderCliAdapter] node-pty not found. Terminal features disabled.");
|
|
939
|
-
}
|
|
940
975
|
buildCliSpawnEnv = sanitizeSpawnEnv;
|
|
941
976
|
ProviderCliAdapter = class _ProviderCliAdapter {
|
|
942
977
|
constructor(provider, workingDir, extraArgs = [], transportFactory = new NodePtyTransportFactory()) {
|
|
@@ -1038,6 +1073,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1038
1073
|
submitRetryUsed = false;
|
|
1039
1074
|
submitRetryPromptSnippet = "";
|
|
1040
1075
|
idleFinishCandidate = null;
|
|
1076
|
+
finishRetryTimer = null;
|
|
1077
|
+
finishRetryCount = 0;
|
|
1041
1078
|
// Resize redraw suppression
|
|
1042
1079
|
resizeSuppressUntil = 0;
|
|
1043
1080
|
// Debug: status transition history
|
|
@@ -1059,6 +1096,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1059
1096
|
static MAX_TRACE_ENTRIES = 250;
|
|
1060
1097
|
providerResolutionMeta;
|
|
1061
1098
|
static IDLE_FINISH_CONFIRM_MS = 900;
|
|
1099
|
+
static FINISH_RETRY_DELAY_MS = 300;
|
|
1100
|
+
static MAX_FINISH_RETRIES = 2;
|
|
1062
1101
|
syncMessageViews() {
|
|
1063
1102
|
this.messages = [...this.committedMessages];
|
|
1064
1103
|
this.structuredMessages = [...this.committedMessages];
|
|
@@ -1118,7 +1157,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1118
1157
|
recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
|
|
1119
1158
|
screenText: this.terminalScreen.getText(),
|
|
1120
1159
|
messages: [...baseMessages],
|
|
1121
|
-
partialResponse
|
|
1160
|
+
partialResponse,
|
|
1161
|
+
promptText: scope?.prompt || ""
|
|
1122
1162
|
};
|
|
1123
1163
|
}
|
|
1124
1164
|
setStatus(status, trigger) {
|
|
@@ -1361,6 +1401,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
1361
1401
|
this.terminalScreen.reset(24, 80);
|
|
1362
1402
|
this.pendingTerminalQueryTail = "";
|
|
1363
1403
|
this.currentTurnScope = null;
|
|
1404
|
+
this.finishRetryCount = 0;
|
|
1405
|
+
if (this.finishRetryTimer) {
|
|
1406
|
+
clearTimeout(this.finishRetryTimer);
|
|
1407
|
+
this.finishRetryTimer = null;
|
|
1408
|
+
}
|
|
1364
1409
|
this.ready = false;
|
|
1365
1410
|
await this.ptyProcess.ready;
|
|
1366
1411
|
this.recordTrace("ready", {
|
|
@@ -1407,11 +1452,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
1407
1452
|
if (this.startupParseGate) {
|
|
1408
1453
|
this.startupBuffer += cleanData;
|
|
1409
1454
|
const elapsed = Date.now() - this.spawnAt;
|
|
1410
|
-
const scriptStatus = this.runDetectStatus(this.startupBuffer);
|
|
1411
1455
|
const screenText = this.terminalScreen.getText() || "";
|
|
1456
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1457
|
+
const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
|
|
1412
1458
|
const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1413
1459
|
const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1414
|
-
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;
|
|
1415
1461
|
if (isReady) {
|
|
1416
1462
|
this.startupParseGate = false;
|
|
1417
1463
|
this.ready = true;
|
|
@@ -1443,7 +1489,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1443
1489
|
this.approvalExitTimeout = setTimeout(() => {
|
|
1444
1490
|
if (this.currentStatus !== "waiting_approval") return;
|
|
1445
1491
|
const tail = this.recentOutputBuffer;
|
|
1446
|
-
const
|
|
1492
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
1493
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1494
|
+
const modal = this.runParseApproval(tail) || startupModal;
|
|
1447
1495
|
const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
|
|
1448
1496
|
if (stillWaiting) {
|
|
1449
1497
|
this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
|
|
@@ -1463,6 +1511,63 @@ var init_provider_cli_adapter = __esm({
|
|
|
1463
1511
|
if (!text.trim()) return false;
|
|
1464
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);
|
|
1465
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
|
+
}
|
|
1466
1571
|
async waitForInteractivePrompt(maxWaitMs = 5e3) {
|
|
1467
1572
|
const startedAt = Date.now();
|
|
1468
1573
|
let loggedWait = false;
|
|
@@ -1512,9 +1617,10 @@ var init_provider_cli_adapter = __esm({
|
|
|
1512
1617
|
}
|
|
1513
1618
|
const tail = this.settledBuffer;
|
|
1514
1619
|
const screenText = this.terminalScreen.getText() || "";
|
|
1515
|
-
const
|
|
1620
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1621
|
+
const modal = this.runParseApproval(tail) || startupModal;
|
|
1516
1622
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
1517
|
-
const scriptStatus = rawScriptStatus;
|
|
1623
|
+
const scriptStatus = startupModal ? "waiting_approval" : rawScriptStatus;
|
|
1518
1624
|
const parsedTranscript = this.parseCurrentTranscript(
|
|
1519
1625
|
this.committedMessages,
|
|
1520
1626
|
this.responseBuffer,
|
|
@@ -1709,7 +1815,24 @@ var init_provider_cli_adapter = __esm({
|
|
|
1709
1815
|
this.recordTrace("finish_response", {
|
|
1710
1816
|
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
1711
1817
|
});
|
|
1712
|
-
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
|
+
}
|
|
1713
1836
|
if (this.responseTimeout) {
|
|
1714
1837
|
clearTimeout(this.responseTimeout);
|
|
1715
1838
|
this.responseTimeout = null;
|
|
@@ -1726,11 +1849,16 @@ var init_provider_cli_adapter = __esm({
|
|
|
1726
1849
|
clearTimeout(this.submitRetryTimer);
|
|
1727
1850
|
this.submitRetryTimer = null;
|
|
1728
1851
|
}
|
|
1852
|
+
if (this.finishRetryTimer) {
|
|
1853
|
+
clearTimeout(this.finishRetryTimer);
|
|
1854
|
+
this.finishRetryTimer = null;
|
|
1855
|
+
}
|
|
1729
1856
|
this.responseBuffer = "";
|
|
1730
1857
|
this.isWaitingForResponse = false;
|
|
1731
1858
|
this.responseSettleIgnoreUntil = 0;
|
|
1732
1859
|
this.submitRetryUsed = false;
|
|
1733
1860
|
this.submitRetryPromptSnippet = "";
|
|
1861
|
+
this.finishRetryCount = 0;
|
|
1734
1862
|
this.currentTurnScope = null;
|
|
1735
1863
|
this.activeModal = null;
|
|
1736
1864
|
this.setStatus("idle", "response_finished");
|
|
@@ -1744,6 +1872,13 @@ var init_provider_cli_adapter = __esm({
|
|
|
1744
1872
|
);
|
|
1745
1873
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
1746
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
|
+
}
|
|
1747
1882
|
this.syncMessageViews();
|
|
1748
1883
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
1749
1884
|
this.recordTrace("commit_transcript", {
|
|
@@ -1759,7 +1894,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1759
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 || "-"}`
|
|
1760
1895
|
);
|
|
1761
1896
|
}
|
|
1897
|
+
return {
|
|
1898
|
+
hasAssistant: !!lastAssistant,
|
|
1899
|
+
assistantContent: lastAssistant?.content || ""
|
|
1900
|
+
};
|
|
1762
1901
|
}
|
|
1902
|
+
return {
|
|
1903
|
+
hasAssistant: false,
|
|
1904
|
+
assistantContent: ""
|
|
1905
|
+
};
|
|
1763
1906
|
}
|
|
1764
1907
|
// ─── Script Execution ──────────────────────────
|
|
1765
1908
|
runDetectStatus(text) {
|
|
@@ -1838,7 +1981,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1838
1981
|
if (!this.cliScripts?.parseOutput) return null;
|
|
1839
1982
|
try {
|
|
1840
1983
|
const input = this.buildParseInput(baseMessages, partialResponse, scope);
|
|
1841
|
-
|
|
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;
|
|
1842
1993
|
} catch (e) {
|
|
1843
1994
|
LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
|
|
1844
1995
|
return null;
|
|
@@ -1883,10 +2034,19 @@ ${data.message || ""}`.trim();
|
|
|
1883
2034
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
1884
2035
|
if (this.isWaitingForResponse) return;
|
|
1885
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
|
+
}
|
|
1886
2041
|
this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
|
|
1887
2042
|
this.syncMessageViews();
|
|
1888
2043
|
this.isWaitingForResponse = true;
|
|
1889
2044
|
this.responseBuffer = "";
|
|
2045
|
+
this.finishRetryCount = 0;
|
|
2046
|
+
if (this.finishRetryTimer) {
|
|
2047
|
+
clearTimeout(this.finishRetryTimer);
|
|
2048
|
+
this.finishRetryTimer = null;
|
|
2049
|
+
}
|
|
1890
2050
|
this.clearIdleFinishCandidate("send_message");
|
|
1891
2051
|
this.currentTurnScope = {
|
|
1892
2052
|
prompt: text,
|
|
@@ -2114,6 +2274,10 @@ ${data.message || ""}`.trim();
|
|
|
2114
2274
|
clearTimeout(this.submitRetryTimer);
|
|
2115
2275
|
this.submitRetryTimer = null;
|
|
2116
2276
|
}
|
|
2277
|
+
if (this.finishRetryTimer) {
|
|
2278
|
+
clearTimeout(this.finishRetryTimer);
|
|
2279
|
+
this.finishRetryTimer = null;
|
|
2280
|
+
}
|
|
2117
2281
|
if (this.responseTimeout) {
|
|
2118
2282
|
clearTimeout(this.responseTimeout);
|
|
2119
2283
|
this.responseTimeout = null;
|
|
@@ -2137,6 +2301,7 @@ ${data.message || ""}`.trim();
|
|
|
2137
2301
|
this.ptyOutputFlushTimer = null;
|
|
2138
2302
|
}
|
|
2139
2303
|
this.ptyOutputBuffer = "";
|
|
2304
|
+
this.finishRetryCount = 0;
|
|
2140
2305
|
if (this.ptyProcess) {
|
|
2141
2306
|
this.ptyProcess.write("");
|
|
2142
2307
|
setTimeout(() => {
|
|
@@ -2167,6 +2332,10 @@ ${data.message || ""}`.trim();
|
|
|
2167
2332
|
clearTimeout(this.submitRetryTimer);
|
|
2168
2333
|
this.submitRetryTimer = null;
|
|
2169
2334
|
}
|
|
2335
|
+
if (this.finishRetryTimer) {
|
|
2336
|
+
clearTimeout(this.finishRetryTimer);
|
|
2337
|
+
this.finishRetryTimer = null;
|
|
2338
|
+
}
|
|
2170
2339
|
if (this.responseTimeout) {
|
|
2171
2340
|
clearTimeout(this.responseTimeout);
|
|
2172
2341
|
this.responseTimeout = null;
|
|
@@ -2190,6 +2359,7 @@ ${data.message || ""}`.trim();
|
|
|
2190
2359
|
this.ptyOutputFlushTimer = null;
|
|
2191
2360
|
}
|
|
2192
2361
|
this.ptyOutputBuffer = "";
|
|
2362
|
+
this.finishRetryCount = 0;
|
|
2193
2363
|
if (this.ptyProcess) {
|
|
2194
2364
|
try {
|
|
2195
2365
|
if (typeof this.ptyProcess.detach === "function") {
|
|
@@ -2226,6 +2396,11 @@ ${data.message || ""}`.trim();
|
|
|
2226
2396
|
this.ptyOutputFlushTimer = null;
|
|
2227
2397
|
}
|
|
2228
2398
|
this.ptyOutputBuffer = "";
|
|
2399
|
+
if (this.finishRetryTimer) {
|
|
2400
|
+
clearTimeout(this.finishRetryTimer);
|
|
2401
|
+
this.finishRetryTimer = null;
|
|
2402
|
+
}
|
|
2403
|
+
this.finishRetryCount = 0;
|
|
2229
2404
|
this.terminalScreen.reset();
|
|
2230
2405
|
this.ptyProcess?.clearBuffer?.();
|
|
2231
2406
|
this.onStatusChange?.();
|
|
@@ -2245,10 +2420,11 @@ ${data.message || ""}`.trim();
|
|
|
2245
2420
|
}
|
|
2246
2421
|
resolveModal(buttonIndex) {
|
|
2247
2422
|
if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
|
|
2423
|
+
const modal = this.activeModal;
|
|
2248
2424
|
this.clearIdleFinishCandidate("resolve_modal");
|
|
2249
2425
|
this.recordTrace("resolve_modal", {
|
|
2250
2426
|
buttonIndex,
|
|
2251
|
-
activeModal:
|
|
2427
|
+
activeModal: modal
|
|
2252
2428
|
});
|
|
2253
2429
|
this.activeModal = null;
|
|
2254
2430
|
this.lastApprovalResolvedAt = Date.now();
|
|
@@ -2259,7 +2435,9 @@ ${data.message || ""}`.trim();
|
|
|
2259
2435
|
}
|
|
2260
2436
|
this.setStatus("generating", "approval_resolved");
|
|
2261
2437
|
this.onStatusChange?.();
|
|
2262
|
-
if (
|
|
2438
|
+
if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
|
|
2439
|
+
this.ptyProcess.write("\r");
|
|
2440
|
+
} else if (buttonIndex in this.approvalKeys) {
|
|
2263
2441
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
2264
2442
|
} else {
|
|
2265
2443
|
const DOWN = "\x1B[B";
|
|
@@ -11597,7 +11775,10 @@ function appendUpgradeLog(message) {
|
|
|
11597
11775
|
}
|
|
11598
11776
|
}
|
|
11599
11777
|
function getNpmExecutable() {
|
|
11600
|
-
return
|
|
11778
|
+
return "npm";
|
|
11779
|
+
}
|
|
11780
|
+
function getNpmExecOptions() {
|
|
11781
|
+
return { shell: process.platform === "win32" };
|
|
11601
11782
|
}
|
|
11602
11783
|
function killPid(pid) {
|
|
11603
11784
|
try {
|
|
@@ -11659,9 +11840,10 @@ function removeDaemonPidFile() {
|
|
|
11659
11840
|
}
|
|
11660
11841
|
}
|
|
11661
11842
|
function cleanupStaleGlobalInstallDirs(pkgName) {
|
|
11662
|
-
const
|
|
11843
|
+
const npmExecOpts = getNpmExecOptions();
|
|
11844
|
+
const npmRoot = execFileSync(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
11663
11845
|
if (!npmRoot) return;
|
|
11664
|
-
const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8" }).trim();
|
|
11846
|
+
const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
11665
11847
|
const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
|
|
11666
11848
|
const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
|
|
11667
11849
|
const binNames = /* @__PURE__ */ new Set([packageBaseName]);
|
|
@@ -11722,7 +11904,8 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
11722
11904
|
{
|
|
11723
11905
|
encoding: "utf8",
|
|
11724
11906
|
stdio: "pipe",
|
|
11725
|
-
maxBuffer: 20 * 1024 * 1024
|
|
11907
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
11908
|
+
...getNpmExecOptions()
|
|
11726
11909
|
}
|
|
11727
11910
|
);
|
|
11728
11911
|
if (installOutput.trim()) {
|
|
@@ -14944,6 +15127,53 @@ async function runCliExerciseInternal(ctx, body) {
|
|
|
14944
15127
|
let lastModalKey = "";
|
|
14945
15128
|
let idleSince = 0;
|
|
14946
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
|
+
}
|
|
14947
15177
|
ctx.instanceManager.sendEvent(bundle.target.instanceId, "send_message", { text });
|
|
14948
15178
|
while (Date.now() - startAt < Math.max(1e3, timeoutMs)) {
|
|
14949
15179
|
await sleep(150);
|
|
@@ -14958,32 +15188,14 @@ async function runCliExerciseInternal(ctx, body) {
|
|
|
14958
15188
|
const sawSendMessage = traceEntries.some((entry) => entry?.type === "send_message");
|
|
14959
15189
|
const sawSubmitWrite = traceEntries.some((entry) => entry?.type === "submit_write");
|
|
14960
15190
|
const hasTurnStarted = sawSendMessage || sawSubmitWrite || !!debug?.currentTurnScope;
|
|
14961
|
-
|
|
14962
|
-
statusesSeen.push(status);
|
|
14963
|
-
lastStatus = status;
|
|
14964
|
-
}
|
|
15191
|
+
noteStatus(status);
|
|
14965
15192
|
if (status === "generating" || status === "waiting_approval") {
|
|
14966
15193
|
sawBusy = true;
|
|
14967
15194
|
idleSince = 0;
|
|
14968
15195
|
}
|
|
14969
15196
|
const modal = debug?.activeModal || trace?.activeModal || null;
|
|
14970
|
-
if (
|
|
14971
|
-
|
|
14972
|
-
const modalKey = JSON.stringify({
|
|
14973
|
-
message: modal.message || "",
|
|
14974
|
-
buttons: modal.buttons,
|
|
14975
|
-
index: clampedIndex
|
|
14976
|
-
});
|
|
14977
|
-
if (modalKey !== lastModalKey && typeof bundle.adapter.resolveModal === "function") {
|
|
14978
|
-
lastModalKey = modalKey;
|
|
14979
|
-
approvalsResolved.push({
|
|
14980
|
-
at: Date.now(),
|
|
14981
|
-
buttonIndex: clampedIndex,
|
|
14982
|
-
label: modal.buttons[clampedIndex] || null
|
|
14983
|
-
});
|
|
14984
|
-
bundle.adapter.resolveModal(clampedIndex);
|
|
14985
|
-
continue;
|
|
14986
|
-
}
|
|
15197
|
+
if (resolveActiveModalIfNeeded(status, modal)) {
|
|
15198
|
+
continue;
|
|
14987
15199
|
}
|
|
14988
15200
|
const traceCount = Number(trace?.entryCount || 0);
|
|
14989
15201
|
const hasProgress = hasTurnStarted && (traceCount > preTraceCount || statusesSeen.length > 1 || approvalsResolved.length > 0);
|
|
@@ -15979,10 +16191,10 @@ async function handleAutoImplement(ctx, type, req, res) {
|
|
|
15979
16191
|
let isPty = false;
|
|
15980
16192
|
const { spawn: spawnFn } = await import("child_process");
|
|
15981
16193
|
try {
|
|
15982
|
-
const
|
|
16194
|
+
const pty = __require("node-pty");
|
|
15983
16195
|
ctx.log(`Auto-implement spawn (PTY): ${shellCmd}`);
|
|
15984
16196
|
const isWin2 = os17.platform() === "win32";
|
|
15985
|
-
child =
|
|
16197
|
+
child = pty.spawn(isWin2 ? "cmd.exe" : process.env.SHELL || "/bin/zsh", [isWin2 ? "/c" : "-c", shellCmd], {
|
|
15986
16198
|
name: "xterm-256color",
|
|
15987
16199
|
cols: 120,
|
|
15988
16200
|
rows: 40,
|
|
@@ -16638,6 +16850,13 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
16638
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.");
|
|
16639
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.");
|
|
16640
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
|
+
}
|
|
16641
16860
|
lines.push("## Task");
|
|
16642
16861
|
lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
|
|
16643
16862
|
lines.push("");
|