@adhdev/daemon-standalone 0.7.46 → 0.8.1
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/index.js +1748 -237
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/index-Pa7-dpip.css +1 -0
- package/public/assets/index-W_zXnDHp.js +62 -0
- package/public/index.html +2 -2
- package/vendor/session-host-daemon/index.js +14 -3
- package/vendor/session-host-daemon/index.js.map +1 -1
- package/vendor/session-host-daemon/index.mjs +14 -3
- package/vendor/session-host-daemon/index.mjs.map +1 -1
- package/public/assets/index-B7gMWsoX.js +0 -68
- package/public/assets/index-DiWCXoGV.css +0 -1
package/dist/index.js
CHANGED
|
@@ -27570,7 +27570,7 @@ var require_dist = __commonJS({
|
|
|
27570
27570
|
};
|
|
27571
27571
|
}
|
|
27572
27572
|
};
|
|
27573
|
-
var
|
|
27573
|
+
var os6 = __toESM2(require("os"));
|
|
27574
27574
|
var path22 = __toESM2(require("path"));
|
|
27575
27575
|
var net3 = __toESM2(require("net"));
|
|
27576
27576
|
var import_crypto22 = require("crypto");
|
|
@@ -27583,7 +27583,7 @@ var require_dist = __commonJS({
|
|
|
27583
27583
|
}
|
|
27584
27584
|
return {
|
|
27585
27585
|
kind: "unix",
|
|
27586
|
-
path: path22.join(
|
|
27586
|
+
path: path22.join(os6.tmpdir(), `${appName}-session-host.sock`)
|
|
27587
27587
|
};
|
|
27588
27588
|
}
|
|
27589
27589
|
function serializeEnvelope3(envelope) {
|
|
@@ -28488,10 +28488,10 @@ var require_dist2 = __commonJS({
|
|
|
28488
28488
|
normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
|
|
28489
28489
|
});
|
|
28490
28490
|
function stripAnsi(str) {
|
|
28491
|
-
return str.replace(/\x1B\[
|
|
28491
|
+
return str.replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][\s\S]*?\x1B\\/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B\[\d*[A-HJKSTfG]/g, " ").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/ +/g, " ");
|
|
28492
28492
|
}
|
|
28493
28493
|
function stripTerminalNoise(str) {
|
|
28494
|
-
return String(str || "").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, "").replace(/(^|[\s([])(?:\??\d{1,4}(?:;\d{1,4})*[A-Za-z])(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\[\??\d{1,4}(?:;\d{1,4})*[A-Za-z])(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\d{1,4};\?)(?=$|[\s)\]])/g, "$1").replace(/\r+/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/ {2,}/g, " ");
|
|
28494
|
+
return String(str || "").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, "").replace(/(^|[\s([])(?:\??\d{1,4}(?:;\d{1,4})*[A-Za-z])(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\[\??\d{1,4}(?:;\d{1,4})*[A-Za-z])(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\d{1,4};\?)(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\d+\$r[0-9;\" ]*[A-Za-z]?)(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:>\|[A-Za-z0-9_.:-]+(?:\([^)]*\))?)(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:[A-Z]\d(?:\s+[A-Z]\d)+)(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\d+;[^\s)\]]+)(?=$|[\s)\]])/g, "$1").replace(/\r+/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/ {2,}/g, " ");
|
|
28495
28495
|
}
|
|
28496
28496
|
function sanitizeTerminalText(str) {
|
|
28497
28497
|
return stripTerminalNoise(stripAnsi(str));
|
|
@@ -28534,12 +28534,12 @@ var require_dist2 = __commonJS({
|
|
|
28534
28534
|
function isScriptBinary(binaryPath) {
|
|
28535
28535
|
if (!path7.isAbsolute(binaryPath)) return false;
|
|
28536
28536
|
try {
|
|
28537
|
-
const
|
|
28538
|
-
const resolved =
|
|
28537
|
+
const fs15 = require("fs");
|
|
28538
|
+
const resolved = fs15.realpathSync(binaryPath);
|
|
28539
28539
|
const head = Buffer.alloc(8);
|
|
28540
|
-
const fd =
|
|
28541
|
-
|
|
28542
|
-
|
|
28540
|
+
const fd = fs15.openSync(resolved, "r");
|
|
28541
|
+
fs15.readSync(fd, head, 0, 8, 0);
|
|
28542
|
+
fs15.closeSync(fd);
|
|
28543
28543
|
let i = 0;
|
|
28544
28544
|
if (head[0] === 239 && head[1] === 187 && head[2] === 191) i = 3;
|
|
28545
28545
|
return head[i] === 35 && head[i + 1] === 33;
|
|
@@ -28550,12 +28550,12 @@ var require_dist2 = __commonJS({
|
|
|
28550
28550
|
function looksLikeMachOOrElf(filePath) {
|
|
28551
28551
|
if (!path7.isAbsolute(filePath)) return false;
|
|
28552
28552
|
try {
|
|
28553
|
-
const
|
|
28554
|
-
const resolved =
|
|
28553
|
+
const fs15 = require("fs");
|
|
28554
|
+
const resolved = fs15.realpathSync(filePath);
|
|
28555
28555
|
const buf = Buffer.alloc(8);
|
|
28556
|
-
const fd =
|
|
28557
|
-
|
|
28558
|
-
|
|
28556
|
+
const fd = fs15.openSync(resolved, "r");
|
|
28557
|
+
fs15.readSync(fd, buf, 0, 8, 0);
|
|
28558
|
+
fs15.closeSync(fd);
|
|
28559
28559
|
let i = 0;
|
|
28560
28560
|
if (buf[0] === 239 && buf[1] === 187 && buf[2] === 191) i = 3;
|
|
28561
28561
|
const b2 = buf.subarray(i);
|
|
@@ -28608,6 +28608,9 @@ var require_dist2 = __commonJS({
|
|
|
28608
28608
|
).length;
|
|
28609
28609
|
return matched >= required2;
|
|
28610
28610
|
}
|
|
28611
|
+
function normalizeScreenSnapshot(text) {
|
|
28612
|
+
return sanitizeTerminalText(String(text || "")).replace(/\s+/g, " ").trim();
|
|
28613
|
+
}
|
|
28611
28614
|
function parsePatternEntry(x) {
|
|
28612
28615
|
if (x instanceof RegExp) return x;
|
|
28613
28616
|
if (x && typeof x === "object" && typeof x.source === "string") {
|
|
@@ -28650,14 +28653,14 @@ var require_dist2 = __commonJS({
|
|
|
28650
28653
|
pty2 = require("node-pty");
|
|
28651
28654
|
if (os8.platform() !== "win32") {
|
|
28652
28655
|
try {
|
|
28653
|
-
const
|
|
28656
|
+
const fs15 = require("fs");
|
|
28654
28657
|
const ptyDir = path7.resolve(path7.dirname(require.resolve("node-pty")), "..");
|
|
28655
28658
|
const platformArch = `${os8.platform()}-${os8.arch()}`;
|
|
28656
28659
|
const helper = path7.join(ptyDir, "prebuilds", platformArch, "spawn-helper");
|
|
28657
|
-
if (
|
|
28658
|
-
const stat4 =
|
|
28660
|
+
if (fs15.existsSync(helper)) {
|
|
28661
|
+
const stat4 = fs15.statSync(helper);
|
|
28659
28662
|
if (!(stat4.mode & 73)) {
|
|
28660
|
-
|
|
28663
|
+
fs15.chmodSync(helper, stat4.mode | 493);
|
|
28661
28664
|
LOG2.info("CLI", "[node-pty] Fixed spawn-helper permissions");
|
|
28662
28665
|
}
|
|
28663
28666
|
}
|
|
@@ -28691,10 +28694,25 @@ var require_dist2 = __commonJS({
|
|
|
28691
28694
|
this.sendDelayMs = typeof provider.sendDelayMs === "number" ? Math.max(0, provider.sendDelayMs) : 0;
|
|
28692
28695
|
this.sendKey = typeof provider.sendKey === "string" && provider.sendKey.length > 0 ? provider.sendKey : "\r";
|
|
28693
28696
|
this.submitStrategy = provider.submitStrategy === "immediate" ? "immediate" : "wait_for_echo";
|
|
28697
|
+
this.providerResolutionMeta = {
|
|
28698
|
+
type: provider.type,
|
|
28699
|
+
name: provider.name,
|
|
28700
|
+
resolvedVersion: provider._resolvedVersion || null,
|
|
28701
|
+
resolvedOs: provider._resolvedOs || null,
|
|
28702
|
+
providerDir: provider._resolvedProviderDir || null,
|
|
28703
|
+
scriptDir: provider._resolvedScriptDir || null,
|
|
28704
|
+
scriptsPath: provider._resolvedScriptsPath || null,
|
|
28705
|
+
scriptsSource: provider._resolvedScriptsSource || null,
|
|
28706
|
+
versionWarning: provider._versionWarning || null
|
|
28707
|
+
};
|
|
28694
28708
|
this.cliScripts = provider.scripts || {};
|
|
28695
28709
|
const scriptNames = Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function");
|
|
28696
28710
|
if (scriptNames.length > 0) {
|
|
28697
28711
|
LOG2.info("CLI", `[${this.cliType}] CLI scripts: [${scriptNames.join(", ")}]`);
|
|
28712
|
+
LOG2.info(
|
|
28713
|
+
"CLI",
|
|
28714
|
+
`[${this.cliType}] Provider resolution: providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"} source=${this.providerResolutionMeta.scriptsSource || "-"} version=${this.providerResolutionMeta.resolvedVersion || "-"}`
|
|
28715
|
+
);
|
|
28698
28716
|
} else {
|
|
28699
28717
|
LOG2.warn("CLI", `[${this.cliType}] \u26A0 No CLI scripts loaded! Provider needs scripts/{version}/scripts.js`);
|
|
28700
28718
|
}
|
|
@@ -28727,6 +28745,10 @@ var require_dist2 = __commonJS({
|
|
|
28727
28745
|
ptyOutputBuffer = "";
|
|
28728
28746
|
ptyOutputFlushTimer = null;
|
|
28729
28747
|
pendingTerminalQueryTail = "";
|
|
28748
|
+
lastOutputAt = 0;
|
|
28749
|
+
lastNonEmptyOutputAt = 0;
|
|
28750
|
+
lastScreenChangeAt = 0;
|
|
28751
|
+
lastScreenSnapshot = "";
|
|
28730
28752
|
// Server log forwarding
|
|
28731
28753
|
serverConn = null;
|
|
28732
28754
|
logBuffer = [];
|
|
@@ -28747,6 +28769,7 @@ var require_dist2 = __commonJS({
|
|
|
28747
28769
|
submitRetryTimer = null;
|
|
28748
28770
|
submitRetryUsed = false;
|
|
28749
28771
|
submitRetryPromptSnippet = "";
|
|
28772
|
+
idleFinishCandidate = null;
|
|
28750
28773
|
// Resize redraw suppression
|
|
28751
28774
|
resizeSuppressUntil = 0;
|
|
28752
28775
|
// Debug: status transition history
|
|
@@ -28762,6 +28785,12 @@ var require_dist2 = __commonJS({
|
|
|
28762
28785
|
/** Max accumulated buffer size (last 50KB) */
|
|
28763
28786
|
static MAX_ACCUMULATED_BUFFER = 5e4;
|
|
28764
28787
|
currentTurnScope = null;
|
|
28788
|
+
traceEntries = [];
|
|
28789
|
+
traceSeq = 0;
|
|
28790
|
+
traceSessionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
28791
|
+
static MAX_TRACE_ENTRIES = 250;
|
|
28792
|
+
providerResolutionMeta;
|
|
28793
|
+
static IDLE_FINISH_CONFIRM_MS = 900;
|
|
28765
28794
|
syncMessageViews() {
|
|
28766
28795
|
this.messages = [...this.committedMessages];
|
|
28767
28796
|
this.structuredMessages = [...this.committedMessages];
|
|
@@ -28797,8 +28826,89 @@ var require_dist2 = __commonJS({
|
|
|
28797
28826
|
this.currentStatus = status;
|
|
28798
28827
|
this.statusHistory.push({ status, at: Date.now(), trigger });
|
|
28799
28828
|
if (this.statusHistory.length > 50) this.statusHistory.shift();
|
|
28829
|
+
this.recordTrace("status", {
|
|
28830
|
+
previousStatus: prev,
|
|
28831
|
+
trigger: trigger || null
|
|
28832
|
+
});
|
|
28800
28833
|
LOG2.info("CLI", `[${this.cliType}] status: ${prev} \u2192 ${status}${trigger ? ` (${trigger})` : ""}`);
|
|
28801
28834
|
}
|
|
28835
|
+
clearIdleFinishCandidate(reason) {
|
|
28836
|
+
if (!this.idleFinishCandidate) return;
|
|
28837
|
+
this.recordTrace("idle_candidate_reset", {
|
|
28838
|
+
reason,
|
|
28839
|
+
candidate: this.idleFinishCandidate
|
|
28840
|
+
});
|
|
28841
|
+
this.idleFinishCandidate = null;
|
|
28842
|
+
}
|
|
28843
|
+
armIdleFinishCandidate(assistantLength) {
|
|
28844
|
+
const now = Date.now();
|
|
28845
|
+
this.idleFinishCandidate = {
|
|
28846
|
+
armedAt: now,
|
|
28847
|
+
lastOutputAt: this.lastOutputAt,
|
|
28848
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
28849
|
+
responseEpoch: this.responseEpoch,
|
|
28850
|
+
assistantLength
|
|
28851
|
+
};
|
|
28852
|
+
this.recordTrace("idle_candidate_armed", {
|
|
28853
|
+
confirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
|
|
28854
|
+
candidate: this.idleFinishCandidate,
|
|
28855
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
28856
|
+
});
|
|
28857
|
+
if (this.settleTimer) clearTimeout(this.settleTimer);
|
|
28858
|
+
this.settleTimer = setTimeout(() => {
|
|
28859
|
+
this.settleTimer = null;
|
|
28860
|
+
this.settledBuffer = this.recentOutputBuffer;
|
|
28861
|
+
this.evaluateSettled();
|
|
28862
|
+
}, _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS);
|
|
28863
|
+
}
|
|
28864
|
+
summarizeTraceText(text, max = 800) {
|
|
28865
|
+
const value = sanitizeTerminalText(String(text || ""));
|
|
28866
|
+
if (value.length <= max) return value;
|
|
28867
|
+
return `\u2026${value.slice(-max)}`;
|
|
28868
|
+
}
|
|
28869
|
+
summarizeTraceMessages(messages, limit = 3) {
|
|
28870
|
+
return messages.slice(-limit).map((message) => ({
|
|
28871
|
+
role: message.role,
|
|
28872
|
+
content: this.summarizeTraceText(message.content, 240),
|
|
28873
|
+
timestamp: message.timestamp
|
|
28874
|
+
}));
|
|
28875
|
+
}
|
|
28876
|
+
buildTraceParseSnapshot(scope, partialResponse = "") {
|
|
28877
|
+
const scopedBuffer = scope ? this.sliceFromOffset(this.accumulatedBuffer, scope.bufferStart) || this.accumulatedBuffer : this.accumulatedBuffer;
|
|
28878
|
+
const scopedRawBuffer = scope ? this.sliceFromOffset(this.accumulatedRawBuffer, scope.rawBufferStart) || this.accumulatedRawBuffer : this.accumulatedRawBuffer;
|
|
28879
|
+
return {
|
|
28880
|
+
currentTurnScope: scope || null,
|
|
28881
|
+
responseBuffer: this.summarizeTraceText(this.responseBuffer, 1200),
|
|
28882
|
+
partialResponse: this.summarizeTraceText(partialResponse || this.responseBuffer, 1200),
|
|
28883
|
+
turnBuffer: this.summarizeTraceText(scopedBuffer, 1600),
|
|
28884
|
+
turnRawPreview: this.summarizeTraceText(scopedRawBuffer, 1600),
|
|
28885
|
+
turnSanitizedRawPreview: this.summarizeTraceText(sanitizeTerminalText(scopedRawBuffer), 1600)
|
|
28886
|
+
};
|
|
28887
|
+
}
|
|
28888
|
+
recordTrace(type, payload = {}) {
|
|
28889
|
+
const entry = {
|
|
28890
|
+
id: ++this.traceSeq,
|
|
28891
|
+
at: Date.now(),
|
|
28892
|
+
type,
|
|
28893
|
+
status: this.currentStatus,
|
|
28894
|
+
isWaitingForResponse: this.isWaitingForResponse,
|
|
28895
|
+
activeModal: this.activeModal ? { message: this.activeModal.message, buttons: [...this.activeModal.buttons] } : null,
|
|
28896
|
+
payload
|
|
28897
|
+
};
|
|
28898
|
+
this.traceEntries.push(entry);
|
|
28899
|
+
if (this.traceEntries.length > _ProviderCliAdapter.MAX_TRACE_ENTRIES) {
|
|
28900
|
+
this.traceEntries.splice(0, this.traceEntries.length - _ProviderCliAdapter.MAX_TRACE_ENTRIES);
|
|
28901
|
+
}
|
|
28902
|
+
}
|
|
28903
|
+
resetTraceSession() {
|
|
28904
|
+
this.traceEntries = [];
|
|
28905
|
+
this.traceSeq = 0;
|
|
28906
|
+
this.traceSessionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
28907
|
+
this.recordTrace("session_start", {
|
|
28908
|
+
providerType: this.cliType,
|
|
28909
|
+
workingDir: this.workingDir
|
|
28910
|
+
});
|
|
28911
|
+
}
|
|
28802
28912
|
// Resolved timeouts
|
|
28803
28913
|
timeouts;
|
|
28804
28914
|
// Provider approval key mapping
|
|
@@ -28844,6 +28954,7 @@ var require_dist2 = __commonJS({
|
|
|
28844
28954
|
const isWin = os8.platform() === "win32";
|
|
28845
28955
|
const allArgs = [...spawnConfig.args, ...this.extraArgs];
|
|
28846
28956
|
LOG2.info("CLI", `[${this.cliType}] Spawning in ${this.workingDir}`);
|
|
28957
|
+
this.resetTraceSession();
|
|
28847
28958
|
let shellCmd;
|
|
28848
28959
|
let shellArgs;
|
|
28849
28960
|
const useShellUnix = !isWin && (!!spawnConfig.shell || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
|
|
@@ -28869,6 +28980,14 @@ var require_dist2 = __commonJS({
|
|
|
28869
28980
|
cwd: this.workingDir,
|
|
28870
28981
|
env: buildCliSpawnEnv(process.env, spawnConfig.env)
|
|
28871
28982
|
};
|
|
28983
|
+
this.recordTrace("spawn", {
|
|
28984
|
+
shellCommand: shellCmd,
|
|
28985
|
+
shellArgs,
|
|
28986
|
+
cwd: ptyOpts.cwd,
|
|
28987
|
+
cols: ptyOpts.cols,
|
|
28988
|
+
rows: ptyOpts.rows,
|
|
28989
|
+
providerResolution: this.providerResolutionMeta
|
|
28990
|
+
});
|
|
28872
28991
|
try {
|
|
28873
28992
|
this.ptyProcess = this.transportFactory.spawn(shellCmd, shellArgs, ptyOpts);
|
|
28874
28993
|
} catch (err) {
|
|
@@ -28911,6 +29030,7 @@ var require_dist2 = __commonJS({
|
|
|
28911
29030
|
this.ptyProcess.onExit(({ exitCode }) => {
|
|
28912
29031
|
LOG2.info("CLI", `[${this.cliType}] Exit code ${exitCode}`);
|
|
28913
29032
|
this.flushPendingOutputParse();
|
|
29033
|
+
this.recordTrace("exit", { exitCode });
|
|
28914
29034
|
this.ptyProcess = null;
|
|
28915
29035
|
this.setStatus("stopped", "pty_exit");
|
|
28916
29036
|
this.ready = false;
|
|
@@ -28926,6 +29046,9 @@ var require_dist2 = __commonJS({
|
|
|
28926
29046
|
this.currentTurnScope = null;
|
|
28927
29047
|
this.ready = false;
|
|
28928
29048
|
await this.ptyProcess.ready;
|
|
29049
|
+
this.recordTrace("ready", {
|
|
29050
|
+
runtimeMeta: this.getRuntimeMetadata()
|
|
29051
|
+
});
|
|
28929
29052
|
this.setStatus("idle", "pty_ready");
|
|
28930
29053
|
this.onStatusChange?.();
|
|
28931
29054
|
}
|
|
@@ -28933,6 +29056,24 @@ var require_dist2 = __commonJS({
|
|
|
28933
29056
|
handleOutput(rawData) {
|
|
28934
29057
|
this.terminalScreen.write(rawData);
|
|
28935
29058
|
const cleanData = sanitizeTerminalText(rawData);
|
|
29059
|
+
const now = Date.now();
|
|
29060
|
+
const normalizedScreenSnapshot = normalizeScreenSnapshot(this.terminalScreen.getText());
|
|
29061
|
+
this.lastOutputAt = now;
|
|
29062
|
+
if (cleanData.trim()) this.lastNonEmptyOutputAt = now;
|
|
29063
|
+
if (normalizedScreenSnapshot !== this.lastScreenSnapshot) {
|
|
29064
|
+
this.lastScreenSnapshot = normalizedScreenSnapshot;
|
|
29065
|
+
this.lastScreenChangeAt = now;
|
|
29066
|
+
}
|
|
29067
|
+
if (this.idleFinishCandidate && (rawData.length > 0 || cleanData.length > 0)) {
|
|
29068
|
+
this.clearIdleFinishCandidate("new_output");
|
|
29069
|
+
}
|
|
29070
|
+
this.recordTrace("output", {
|
|
29071
|
+
rawLength: rawData.length,
|
|
29072
|
+
cleanLength: cleanData.length,
|
|
29073
|
+
rawPreview: this.summarizeTraceText(rawData, 300),
|
|
29074
|
+
cleanPreview: this.summarizeTraceText(cleanData, 300),
|
|
29075
|
+
screenText: this.summarizeTraceText(this.terminalScreen.getText(), 1200)
|
|
29076
|
+
});
|
|
28936
29077
|
if (this.isWaitingForResponse && cleanData) {
|
|
28937
29078
|
this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
|
|
28938
29079
|
}
|
|
@@ -28950,11 +29091,17 @@ var require_dist2 = __commonJS({
|
|
|
28950
29091
|
this.startupBuffer += cleanData;
|
|
28951
29092
|
const elapsed = Date.now() - this.spawnAt;
|
|
28952
29093
|
const scriptStatus = this.runDetectStatus(this.startupBuffer);
|
|
28953
|
-
const
|
|
29094
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
29095
|
+
const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
29096
|
+
const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
29097
|
+
const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
|
|
28954
29098
|
if (isReady) {
|
|
28955
29099
|
this.startupParseGate = false;
|
|
28956
29100
|
this.ready = true;
|
|
28957
|
-
LOG2.info(
|
|
29101
|
+
LOG2.info(
|
|
29102
|
+
"CLI",
|
|
29103
|
+
`[${this.cliType}] Startup ready (${elapsed}ms, scriptStatus=${scriptStatus}, prompt=${hasInteractivePrompt}, stableMs=${startupStableMs}) providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
|
|
29104
|
+
);
|
|
28958
29105
|
this.onStatusChange?.();
|
|
28959
29106
|
}
|
|
28960
29107
|
}
|
|
@@ -28999,6 +29146,41 @@ var require_dist2 = __commonJS({
|
|
|
28999
29146
|
if (!text.trim()) return false;
|
|
29000
29147
|
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);
|
|
29001
29148
|
}
|
|
29149
|
+
async waitForInteractivePrompt(maxWaitMs = 5e3) {
|
|
29150
|
+
const startedAt = Date.now();
|
|
29151
|
+
let loggedWait = false;
|
|
29152
|
+
while (Date.now() - startedAt < maxWaitMs) {
|
|
29153
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
29154
|
+
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
29155
|
+
const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
|
|
29156
|
+
const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
29157
|
+
const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
|
|
29158
|
+
const startupLikelyActive = /Welcome back|Tips for getting|Recent activity|Claude Code v\d/i.test(screenText);
|
|
29159
|
+
const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "starting" && status !== "generating";
|
|
29160
|
+
if (interactiveReady) {
|
|
29161
|
+
if (loggedWait) {
|
|
29162
|
+
LOG2.info(
|
|
29163
|
+
"CLI",
|
|
29164
|
+
`[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput}, startup=${startupLikelyActive})`
|
|
29165
|
+
);
|
|
29166
|
+
}
|
|
29167
|
+
return;
|
|
29168
|
+
}
|
|
29169
|
+
if (!loggedWait && Date.now() - startedAt >= 400) {
|
|
29170
|
+
loggedWait = true;
|
|
29171
|
+
LOG2.info(
|
|
29172
|
+
"CLI",
|
|
29173
|
+
`[${this.cliType}] Waiting for interactive prompt: hasPrompt=${hasPrompt} stableMs=${stableMs} recentOutputMs=${recentlyOutput} status=${status} startup=${startupLikelyActive} screen=${JSON.stringify(this.summarizeTraceText(screenText, 220)).slice(0, 260)}`
|
|
29174
|
+
);
|
|
29175
|
+
}
|
|
29176
|
+
await new Promise((resolve10) => setTimeout(resolve10, 50));
|
|
29177
|
+
}
|
|
29178
|
+
const finalScreenText = this.terminalScreen.getText() || "";
|
|
29179
|
+
LOG2.warn(
|
|
29180
|
+
"CLI",
|
|
29181
|
+
`[${this.cliType}] Interactive prompt wait timed out after ${maxWaitMs}ms; proceeding with screen=${JSON.stringify(this.summarizeTraceText(finalScreenText, 240)).slice(0, 280)}`
|
|
29182
|
+
);
|
|
29183
|
+
}
|
|
29002
29184
|
evaluateSettled() {
|
|
29003
29185
|
const now = Date.now();
|
|
29004
29186
|
if (this.submitPendingUntil > now || this.responseSettleIgnoreUntil > now) {
|
|
@@ -29016,6 +29198,30 @@ var require_dist2 = __commonJS({
|
|
|
29016
29198
|
const modal = this.runParseApproval(tail);
|
|
29017
29199
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
29018
29200
|
const scriptStatus = rawScriptStatus;
|
|
29201
|
+
const parsedTranscript = this.parseCurrentTranscript(
|
|
29202
|
+
this.committedMessages,
|
|
29203
|
+
this.responseBuffer,
|
|
29204
|
+
this.currentTurnScope
|
|
29205
|
+
);
|
|
29206
|
+
const parsedMessages = Array.isArray(parsedTranscript?.messages) ? this.normalizeParsedMessages(parsedTranscript.messages) : [];
|
|
29207
|
+
const lastParsedAssistant = [...parsedMessages].reverse().find((message) => message.role === "assistant");
|
|
29208
|
+
this.recordTrace("settled", {
|
|
29209
|
+
tail: this.summarizeTraceText(tail, 500),
|
|
29210
|
+
screenText: this.summarizeTraceText(screenText, 1200),
|
|
29211
|
+
detectStatus: scriptStatus,
|
|
29212
|
+
parsedStatus: parsedTranscript?.status || null,
|
|
29213
|
+
parsedMessageCount: parsedMessages.length,
|
|
29214
|
+
parsedLastAssistant: lastParsedAssistant ? this.summarizeTraceText(lastParsedAssistant.content, 280) : "",
|
|
29215
|
+
parsedActiveModal: parsedTranscript?.activeModal ?? null,
|
|
29216
|
+
approval: modal,
|
|
29217
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
29218
|
+
});
|
|
29219
|
+
if (this.currentTurnScope && !lastParsedAssistant) {
|
|
29220
|
+
LOG2.info(
|
|
29221
|
+
"CLI",
|
|
29222
|
+
`[${this.cliType}] Settled without assistant: prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} responseBuffer=${JSON.stringify(this.summarizeTraceText(this.responseBuffer, 220)).slice(0, 260)} screen=${JSON.stringify(this.summarizeTraceText(screenText, 220)).slice(0, 260)} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"}`
|
|
29223
|
+
);
|
|
29224
|
+
}
|
|
29019
29225
|
if (!scriptStatus) return;
|
|
29020
29226
|
const prevStatus = this.currentStatus;
|
|
29021
29227
|
const clearPendingScriptStatus = () => {
|
|
@@ -29051,6 +29257,7 @@ var require_dist2 = __commonJS({
|
|
|
29051
29257
|
clearPendingScriptStatus();
|
|
29052
29258
|
}
|
|
29053
29259
|
if (scriptStatus === "waiting_approval") {
|
|
29260
|
+
this.clearIdleFinishCandidate("waiting_approval");
|
|
29054
29261
|
const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
29055
29262
|
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
29056
29263
|
if ((inCooldown || visibleIdlePrompt) && !modal) {
|
|
@@ -29084,6 +29291,7 @@ var require_dist2 = __commonJS({
|
|
|
29084
29291
|
}
|
|
29085
29292
|
}
|
|
29086
29293
|
if (scriptStatus === "generating") {
|
|
29294
|
+
this.clearIdleFinishCandidate("generating");
|
|
29087
29295
|
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
29088
29296
|
const noActiveTurn = !this.currentTurnScope;
|
|
29089
29297
|
const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText) || /accept edits on/i.test(effectiveScreenText) && (/Update available!/i.test(screenText) || /\/effort/i.test(screenText) || /^.*➜\s+\S+/m.test(effectiveScreenText));
|
|
@@ -29120,13 +29328,58 @@ var require_dist2 = __commonJS({
|
|
|
29120
29328
|
this.lastApprovalResolvedAt = Date.now();
|
|
29121
29329
|
}
|
|
29122
29330
|
if (this.isWaitingForResponse) {
|
|
29331
|
+
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
29332
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
29333
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
29334
|
+
const hasAssistantTurn = !!lastParsedAssistant;
|
|
29335
|
+
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
29336
|
+
const idleQuietThresholdMs = Math.max(220, this.timeouts.outputSettle);
|
|
29337
|
+
const idleStableThresholdMs = Math.max(120, Math.min(220, this.timeouts.outputSettle));
|
|
29338
|
+
const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
|
|
29339
|
+
const candidate = this.idleFinishCandidate;
|
|
29340
|
+
const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
|
|
29341
|
+
const canFinishImmediately = idleReady && candidateQuiet;
|
|
29342
|
+
this.recordTrace("idle_decision", {
|
|
29343
|
+
visibleIdlePrompt,
|
|
29344
|
+
quietForMs,
|
|
29345
|
+
screenStableMs,
|
|
29346
|
+
hasAssistantTurn,
|
|
29347
|
+
assistantLength,
|
|
29348
|
+
hasModal: !!modal,
|
|
29349
|
+
idleQuietThresholdMs,
|
|
29350
|
+
idleStableThresholdMs,
|
|
29351
|
+
idleReady,
|
|
29352
|
+
idleFinishConfirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
|
|
29353
|
+
idleFinishCandidate: candidate,
|
|
29354
|
+
candidateQuiet,
|
|
29355
|
+
canFinishImmediately,
|
|
29356
|
+
submitPendingUntil: this.submitPendingUntil,
|
|
29357
|
+
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
29358
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
29359
|
+
});
|
|
29360
|
+
if (canFinishImmediately) {
|
|
29361
|
+
this.clearIdleFinishCandidate("finish_response");
|
|
29362
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
29363
|
+
this.finishResponse();
|
|
29364
|
+
return;
|
|
29365
|
+
}
|
|
29366
|
+
if (idleReady) {
|
|
29367
|
+
if (!candidate) {
|
|
29368
|
+
this.armIdleFinishCandidate(assistantLength);
|
|
29369
|
+
return;
|
|
29370
|
+
}
|
|
29371
|
+
} else {
|
|
29372
|
+
this.clearIdleFinishCandidate("idle_not_ready");
|
|
29373
|
+
}
|
|
29123
29374
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
29124
29375
|
this.idleTimeout = setTimeout(() => {
|
|
29125
29376
|
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
29377
|
+
this.clearIdleFinishCandidate("idle_timeout_finish");
|
|
29126
29378
|
this.finishResponse();
|
|
29127
29379
|
}
|
|
29128
29380
|
}, this.timeouts.idleFinish);
|
|
29129
29381
|
} else if (prevStatus !== "idle") {
|
|
29382
|
+
this.clearIdleFinishCandidate("idle_without_response");
|
|
29130
29383
|
this.setStatus("idle", "script_detect");
|
|
29131
29384
|
this.onStatusChange?.();
|
|
29132
29385
|
}
|
|
@@ -29135,6 +29388,10 @@ var require_dist2 = __commonJS({
|
|
|
29135
29388
|
finishResponse() {
|
|
29136
29389
|
if (this.submitPendingUntil > Date.now()) return;
|
|
29137
29390
|
if (this.responseSettleIgnoreUntil > Date.now()) return;
|
|
29391
|
+
this.clearIdleFinishCandidate("finish_response_enter");
|
|
29392
|
+
this.recordTrace("finish_response", {
|
|
29393
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
29394
|
+
});
|
|
29138
29395
|
this.commitCurrentTranscript();
|
|
29139
29396
|
if (this.responseTimeout) {
|
|
29140
29397
|
clearTimeout(this.responseTimeout);
|
|
@@ -29171,6 +29428,20 @@ var require_dist2 = __commonJS({
|
|
|
29171
29428
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
29172
29429
|
this.committedMessages = this.normalizeParsedMessages(parsed.messages);
|
|
29173
29430
|
this.syncMessageViews();
|
|
29431
|
+
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
29432
|
+
this.recordTrace("commit_transcript", {
|
|
29433
|
+
parsedStatus: parsed.status || null,
|
|
29434
|
+
messageCount: this.committedMessages.length,
|
|
29435
|
+
lastAssistant: lastAssistant ? this.summarizeTraceText(lastAssistant.content, 320) : "",
|
|
29436
|
+
messages: this.summarizeTraceMessages(this.committedMessages),
|
|
29437
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
29438
|
+
});
|
|
29439
|
+
if (!lastAssistant && this.currentTurnScope) {
|
|
29440
|
+
LOG2.warn(
|
|
29441
|
+
"CLI",
|
|
29442
|
+
`[${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 || "-"}`
|
|
29443
|
+
);
|
|
29444
|
+
}
|
|
29174
29445
|
}
|
|
29175
29446
|
}
|
|
29176
29447
|
// ─── Script Execution ──────────────────────────
|
|
@@ -29294,16 +29565,23 @@ ${data.message || ""}`.trim();
|
|
|
29294
29565
|
}
|
|
29295
29566
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
29296
29567
|
if (this.isWaitingForResponse) return;
|
|
29568
|
+
await this.waitForInteractivePrompt();
|
|
29297
29569
|
this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
|
|
29298
29570
|
this.syncMessageViews();
|
|
29299
29571
|
this.isWaitingForResponse = true;
|
|
29300
29572
|
this.responseBuffer = "";
|
|
29573
|
+
this.clearIdleFinishCandidate("send_message");
|
|
29301
29574
|
this.currentTurnScope = {
|
|
29302
29575
|
prompt: text,
|
|
29303
29576
|
startedAt: Date.now(),
|
|
29304
29577
|
bufferStart: this.accumulatedBuffer.length,
|
|
29305
29578
|
rawBufferStart: this.accumulatedRawBuffer.length
|
|
29306
29579
|
};
|
|
29580
|
+
this.recordTrace("send_message", {
|
|
29581
|
+
text: this.summarizeTraceText(text, 500),
|
|
29582
|
+
estimatedLines: estimatePromptDisplayLines(text),
|
|
29583
|
+
turnScope: this.currentTurnScope
|
|
29584
|
+
});
|
|
29307
29585
|
LOG2.info("CLI", `[${this.cliType}] sendMessage turn scope buffer=${this.currentTurnScope.bufferStart} raw=${this.currentTurnScope.rawBufferStart} prompt=${JSON.stringify(text).slice(0, 120)}`);
|
|
29308
29586
|
this.submitRetryUsed = false;
|
|
29309
29587
|
this.submitRetryPromptSnippet = extractPromptRetrySnippet(text);
|
|
@@ -29333,6 +29611,11 @@ ${data.message || ""}`.trim();
|
|
|
29333
29611
|
const submit = () => {
|
|
29334
29612
|
if (!this.ptyProcess) return;
|
|
29335
29613
|
this.submitPendingUntil = 0;
|
|
29614
|
+
this.recordTrace("submit_write", {
|
|
29615
|
+
mode: "submit_key",
|
|
29616
|
+
sendKey: this.sendKey,
|
|
29617
|
+
screenText: this.summarizeTraceText(this.terminalScreen.getText(), 500)
|
|
29618
|
+
});
|
|
29336
29619
|
this.ptyProcess.write(this.sendKey);
|
|
29337
29620
|
const retrySubmitIfStuck = (attempt) => {
|
|
29338
29621
|
this.submitRetryTimer = null;
|
|
@@ -29344,6 +29627,12 @@ ${data.message || ""}`.trim();
|
|
|
29344
29627
|
if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText)) return;
|
|
29345
29628
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
29346
29629
|
LOG2.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
29630
|
+
this.recordTrace("submit_write", {
|
|
29631
|
+
mode: "submit_retry",
|
|
29632
|
+
attempt,
|
|
29633
|
+
sendKey: this.sendKey,
|
|
29634
|
+
screenText: this.summarizeTraceText(screenText, 500)
|
|
29635
|
+
});
|
|
29347
29636
|
this.ptyProcess.write(this.sendKey);
|
|
29348
29637
|
if (attempt >= 3) {
|
|
29349
29638
|
this.submitRetryUsed = true;
|
|
@@ -29356,6 +29645,12 @@ ${data.message || ""}`.trim();
|
|
|
29356
29645
|
};
|
|
29357
29646
|
if (this.submitStrategy === "immediate") {
|
|
29358
29647
|
this.submitPendingUntil = 0;
|
|
29648
|
+
this.recordTrace("submit_write", {
|
|
29649
|
+
mode: "immediate",
|
|
29650
|
+
text: this.summarizeTraceText(text, 500),
|
|
29651
|
+
sendKey: this.sendKey,
|
|
29652
|
+
screenText: this.summarizeTraceText(this.terminalScreen.getText(), 500)
|
|
29653
|
+
});
|
|
29359
29654
|
this.ptyProcess.write(text + this.sendKey);
|
|
29360
29655
|
this.submitRetryTimer = setTimeout(() => {
|
|
29361
29656
|
this.submitRetryTimer = null;
|
|
@@ -29366,6 +29661,12 @@ ${data.message || ""}`.trim();
|
|
|
29366
29661
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
29367
29662
|
LOG2.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
29368
29663
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
29664
|
+
this.recordTrace("submit_write", {
|
|
29665
|
+
mode: "immediate_retry",
|
|
29666
|
+
attempt: 1,
|
|
29667
|
+
sendKey: this.sendKey,
|
|
29668
|
+
screenText: this.summarizeTraceText(screenText, 500)
|
|
29669
|
+
});
|
|
29369
29670
|
this.ptyProcess.write(this.sendKey);
|
|
29370
29671
|
this.submitRetryUsed = true;
|
|
29371
29672
|
}, retryDelayMs);
|
|
@@ -29376,6 +29677,12 @@ ${data.message || ""}`.trim();
|
|
|
29376
29677
|
this.submitPendingUntil = Date.now() + submitDelayMs;
|
|
29377
29678
|
}
|
|
29378
29679
|
this.ptyProcess.write(text);
|
|
29680
|
+
this.recordTrace("submit_write", {
|
|
29681
|
+
mode: "type_then_submit",
|
|
29682
|
+
text: this.summarizeTraceText(text, 500),
|
|
29683
|
+
sendKey: this.sendKey,
|
|
29684
|
+
screenText: this.summarizeTraceText(this.terminalScreen.getText(), 500)
|
|
29685
|
+
});
|
|
29379
29686
|
const submitStartedAt = Date.now();
|
|
29380
29687
|
let lastNormalizedScreen = "";
|
|
29381
29688
|
let lastScreenChangeAt = submitStartedAt;
|
|
@@ -29476,6 +29783,7 @@ ${data.message || ""}`.trim();
|
|
|
29476
29783
|
});
|
|
29477
29784
|
}
|
|
29478
29785
|
shutdown() {
|
|
29786
|
+
this.clearIdleFinishCandidate("shutdown");
|
|
29479
29787
|
if (this.settleTimer) {
|
|
29480
29788
|
clearTimeout(this.settleTimer);
|
|
29481
29789
|
this.settleTimer = null;
|
|
@@ -29516,6 +29824,7 @@ ${data.message || ""}`.trim();
|
|
|
29516
29824
|
}
|
|
29517
29825
|
}
|
|
29518
29826
|
detach() {
|
|
29827
|
+
this.clearIdleFinishCandidate("detach");
|
|
29519
29828
|
if (this.settleTimer) {
|
|
29520
29829
|
clearTimeout(this.settleTimer);
|
|
29521
29830
|
this.settleTimer = null;
|
|
@@ -29556,6 +29865,7 @@ ${data.message || ""}`.trim();
|
|
|
29556
29865
|
this.onStatusChange?.();
|
|
29557
29866
|
}
|
|
29558
29867
|
clearHistory() {
|
|
29868
|
+
this.clearIdleFinishCandidate("clear_history");
|
|
29559
29869
|
this.committedMessages = [];
|
|
29560
29870
|
this.syncMessageViews();
|
|
29561
29871
|
this.accumulatedBuffer = "";
|
|
@@ -29585,10 +29895,19 @@ ${data.message || ""}`.trim();
|
|
|
29585
29895
|
return this.ready;
|
|
29586
29896
|
}
|
|
29587
29897
|
writeRaw(data) {
|
|
29898
|
+
this.recordTrace("write_raw", {
|
|
29899
|
+
keys: JSON.stringify(data),
|
|
29900
|
+
length: data.length
|
|
29901
|
+
});
|
|
29588
29902
|
this.ptyProcess?.write(data);
|
|
29589
29903
|
}
|
|
29590
29904
|
resolveModal(buttonIndex) {
|
|
29591
29905
|
if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
|
|
29906
|
+
this.clearIdleFinishCandidate("resolve_modal");
|
|
29907
|
+
this.recordTrace("resolve_modal", {
|
|
29908
|
+
buttonIndex,
|
|
29909
|
+
activeModal: this.activeModal
|
|
29910
|
+
});
|
|
29592
29911
|
this.activeModal = null;
|
|
29593
29912
|
this.lastApprovalResolvedAt = Date.now();
|
|
29594
29913
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
@@ -29620,6 +29939,7 @@ ${data.message || ""}`.trim();
|
|
|
29620
29939
|
return {
|
|
29621
29940
|
type: this.cliType,
|
|
29622
29941
|
name: this.cliName,
|
|
29942
|
+
providerResolution: this.providerResolutionMeta,
|
|
29623
29943
|
status: this.currentStatus,
|
|
29624
29944
|
ready: this.ready,
|
|
29625
29945
|
startupParseGate: this.startupParseGate,
|
|
@@ -29639,6 +29959,10 @@ ${data.message || ""}`.trim();
|
|
|
29639
29959
|
rawBufferPreview: this.accumulatedRawBuffer.slice(-1e3),
|
|
29640
29960
|
sanitizedRawPreview: sanitizeTerminalText(this.accumulatedRawBuffer).slice(-1e3),
|
|
29641
29961
|
responseBuffer: this.responseBuffer.slice(-1e3),
|
|
29962
|
+
lastOutputAt: this.lastOutputAt,
|
|
29963
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
29964
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
29965
|
+
lastScreenSnapshot: this.lastScreenSnapshot.slice(-500),
|
|
29642
29966
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
29643
29967
|
activeModal: this.activeModal,
|
|
29644
29968
|
lastApprovalResolvedAt: this.lastApprovalResolvedAt,
|
|
@@ -29650,6 +29974,8 @@ ${data.message || ""}`.trim();
|
|
|
29650
29974
|
resizeSuppressUntil: this.resizeSuppressUntil,
|
|
29651
29975
|
hasCliScripts: this.hasCliScripts(),
|
|
29652
29976
|
scriptNames: Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function"),
|
|
29977
|
+
traceSessionId: this.traceSessionId,
|
|
29978
|
+
traceEntryCount: this.traceEntries.length,
|
|
29653
29979
|
statusHistory: this.statusHistory.slice(-30),
|
|
29654
29980
|
timeouts: this.timeouts,
|
|
29655
29981
|
pendingOutputParseBufferLength: this.pendingOutputParseBuffer.length,
|
|
@@ -29657,6 +29983,25 @@ ${data.message || ""}`.trim();
|
|
|
29657
29983
|
ptyAlive: !!this.ptyProcess
|
|
29658
29984
|
};
|
|
29659
29985
|
}
|
|
29986
|
+
getTraceState(limit = 120) {
|
|
29987
|
+
const cappedLimit = Math.max(1, Math.min(500, Number.isFinite(limit) ? Math.floor(limit) : 120));
|
|
29988
|
+
return {
|
|
29989
|
+
sessionId: this.traceSessionId,
|
|
29990
|
+
providerResolution: this.providerResolutionMeta,
|
|
29991
|
+
entryCount: this.traceEntries.length,
|
|
29992
|
+
entries: this.traceEntries.slice(-cappedLimit),
|
|
29993
|
+
screenText: this.summarizeTraceText(this.terminalScreen.getText(), 4e3),
|
|
29994
|
+
recentOutputBuffer: this.summarizeTraceText(this.recentOutputBuffer, 1e3),
|
|
29995
|
+
responseBuffer: this.summarizeTraceText(this.responseBuffer, 1200),
|
|
29996
|
+
status: this.currentStatus,
|
|
29997
|
+
activeModal: this.activeModal,
|
|
29998
|
+
currentTurnScope: this.currentTurnScope,
|
|
29999
|
+
messages: this.summarizeTraceMessages(this.committedMessages, 5)
|
|
30000
|
+
};
|
|
30001
|
+
}
|
|
30002
|
+
getProviderResolutionMeta() {
|
|
30003
|
+
return { ...this.providerResolutionMeta };
|
|
30004
|
+
}
|
|
29660
30005
|
respondToTerminalQueries(data) {
|
|
29661
30006
|
if (!this.ptyProcess || !data) return;
|
|
29662
30007
|
const combined = this.pendingTerminalQueryTail + data;
|
|
@@ -29752,22 +30097,22 @@ ${data.message || ""}`.trim();
|
|
|
29752
30097
|
});
|
|
29753
30098
|
module2.exports = __toCommonJS2(index_exports);
|
|
29754
30099
|
init_config();
|
|
29755
|
-
var
|
|
29756
|
-
var
|
|
30100
|
+
var fs4 = __toESM2(require("fs"));
|
|
30101
|
+
var os6 = __toESM2(require("os"));
|
|
29757
30102
|
var path5 = __toESM2(require("path"));
|
|
29758
30103
|
var import_crypto22 = require("crypto");
|
|
29759
30104
|
var MAX_WORKSPACES = 50;
|
|
29760
30105
|
function expandPath(p) {
|
|
29761
30106
|
const t = (p || "").trim();
|
|
29762
30107
|
if (!t) return "";
|
|
29763
|
-
if (t.startsWith("~")) return path5.join(
|
|
30108
|
+
if (t.startsWith("~")) return path5.join(os6.homedir(), t.slice(1).replace(/^\//, ""));
|
|
29764
30109
|
return path5.resolve(t);
|
|
29765
30110
|
}
|
|
29766
30111
|
function validateWorkspacePath(absPath) {
|
|
29767
30112
|
try {
|
|
29768
30113
|
if (!absPath) return { ok: false, error: "Path required" };
|
|
29769
|
-
if (!
|
|
29770
|
-
const st2 =
|
|
30114
|
+
if (!fs4.existsSync(absPath)) return { ok: false, error: "Path does not exist" };
|
|
30115
|
+
const st2 = fs4.statSync(absPath);
|
|
29771
30116
|
if (!st2.isDirectory()) return { ok: false, error: "Not a directory" };
|
|
29772
30117
|
return { ok: true };
|
|
29773
30118
|
} catch (e) {
|
|
@@ -29833,7 +30178,7 @@ ${data.message || ""}`.trim();
|
|
|
29833
30178
|
};
|
|
29834
30179
|
}
|
|
29835
30180
|
if (a.useHome === true) {
|
|
29836
|
-
return { ok: true, path:
|
|
30181
|
+
return { ok: true, path: os6.homedir(), source: "home" };
|
|
29837
30182
|
}
|
|
29838
30183
|
return {
|
|
29839
30184
|
ok: false,
|
|
@@ -29874,9 +30219,9 @@ ${data.message || ""}`.trim();
|
|
|
29874
30219
|
const abs = expandPath(rawPath);
|
|
29875
30220
|
const createIfMissing = options?.createIfMissing === true;
|
|
29876
30221
|
if (!abs) return { error: "Path required" };
|
|
29877
|
-
if (!
|
|
30222
|
+
if (!fs4.existsSync(abs) && createIfMissing) {
|
|
29878
30223
|
try {
|
|
29879
|
-
|
|
30224
|
+
fs4.mkdirSync(abs, { recursive: true });
|
|
29880
30225
|
} catch (e) {
|
|
29881
30226
|
return { error: e?.message || "Could not create directory" };
|
|
29882
30227
|
}
|
|
@@ -31824,6 +32169,7 @@ ${data.message || ""}`.trim();
|
|
|
31824
32169
|
role: msg.role,
|
|
31825
32170
|
content: msg.content || "",
|
|
31826
32171
|
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
32172
|
+
senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
|
|
31827
32173
|
agent: agentType,
|
|
31828
32174
|
instanceId,
|
|
31829
32175
|
historySessionId: effectiveHistoryKey,
|
|
@@ -31862,6 +32208,7 @@ ${data.message || ""}`.trim();
|
|
|
31862
32208
|
kind: "system",
|
|
31863
32209
|
content,
|
|
31864
32210
|
receivedAt: options.receivedAt,
|
|
32211
|
+
senderName: options.senderName,
|
|
31865
32212
|
historyDedupKey: options.dedupKey
|
|
31866
32213
|
}],
|
|
31867
32214
|
options.sessionTitle,
|
|
@@ -33192,6 +33539,12 @@ ${data.message || ""}`.trim();
|
|
|
33192
33539
|
function getTargetedCliAdapter(h, args, providerType) {
|
|
33193
33540
|
return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
|
|
33194
33541
|
}
|
|
33542
|
+
function getTargetInstance(h, args) {
|
|
33543
|
+
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
33544
|
+
const sessionId = targetSessionId || h.currentSession?.sessionId || "";
|
|
33545
|
+
if (!sessionId) return null;
|
|
33546
|
+
return h.ctx.instanceManager?.getInstance(sessionId);
|
|
33547
|
+
}
|
|
33195
33548
|
function getTargetTransport(h, provider) {
|
|
33196
33549
|
if (h.currentSession?.transport) return h.currentSession.transport;
|
|
33197
33550
|
switch (provider?.category) {
|
|
@@ -33934,6 +34287,7 @@ ${data.message || ""}`.trim();
|
|
|
33934
34287
|
adapter.writeRaw?.(keys);
|
|
33935
34288
|
}
|
|
33936
34289
|
LOG2.info("Command", `[resolveAction] CLI PTY \u2192 buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? "?"}"`);
|
|
34290
|
+
getTargetInstance(h, args)?.recordApprovalSelection?.(buttons[buttonIndex] ?? button);
|
|
33937
34291
|
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
33938
34292
|
}
|
|
33939
34293
|
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
@@ -34015,9 +34369,9 @@ ${data.message || ""}`.trim();
|
|
|
34015
34369
|
}
|
|
34016
34370
|
return { success: false, error: "resolveAction script not available for this provider" };
|
|
34017
34371
|
}
|
|
34018
|
-
var
|
|
34372
|
+
var fs42 = __toESM2(require("fs"));
|
|
34019
34373
|
var path6 = __toESM2(require("path"));
|
|
34020
|
-
var
|
|
34374
|
+
var os62 = __toESM2(require("os"));
|
|
34021
34375
|
var KEY_TO_VK = {
|
|
34022
34376
|
Backspace: 8,
|
|
34023
34377
|
Tab: 9,
|
|
@@ -34244,7 +34598,7 @@ ${data.message || ""}`.trim();
|
|
|
34244
34598
|
return { success: true, agents };
|
|
34245
34599
|
}
|
|
34246
34600
|
function resolveSafePath(requestedPath) {
|
|
34247
|
-
const home =
|
|
34601
|
+
const home = os62.homedir();
|
|
34248
34602
|
let resolved;
|
|
34249
34603
|
if (requestedPath.startsWith("~")) {
|
|
34250
34604
|
resolved = path6.join(home, requestedPath.slice(1));
|
|
@@ -34258,7 +34612,7 @@ ${data.message || ""}`.trim();
|
|
|
34258
34612
|
async function handleFileRead(h, args) {
|
|
34259
34613
|
try {
|
|
34260
34614
|
const filePath = resolveSafePath(args?.path);
|
|
34261
|
-
const content =
|
|
34615
|
+
const content = fs42.readFileSync(filePath, "utf-8");
|
|
34262
34616
|
return { success: true, content, path: filePath };
|
|
34263
34617
|
} catch (e) {
|
|
34264
34618
|
return { success: false, error: e.message };
|
|
@@ -34267,8 +34621,8 @@ ${data.message || ""}`.trim();
|
|
|
34267
34621
|
async function handleFileWrite(h, args) {
|
|
34268
34622
|
try {
|
|
34269
34623
|
const filePath = resolveSafePath(args?.path);
|
|
34270
|
-
|
|
34271
|
-
|
|
34624
|
+
fs42.mkdirSync(path6.dirname(filePath), { recursive: true });
|
|
34625
|
+
fs42.writeFileSync(filePath, args?.content || "", "utf-8");
|
|
34272
34626
|
return { success: true, path: filePath };
|
|
34273
34627
|
} catch (e) {
|
|
34274
34628
|
return { success: false, error: e.message };
|
|
@@ -34277,11 +34631,11 @@ ${data.message || ""}`.trim();
|
|
|
34277
34631
|
async function handleFileList(h, args) {
|
|
34278
34632
|
try {
|
|
34279
34633
|
const dirPath = resolveSafePath(args?.path || ".");
|
|
34280
|
-
const entries =
|
|
34634
|
+
const entries = fs42.readdirSync(dirPath, { withFileTypes: true });
|
|
34281
34635
|
const files = entries.map((e) => ({
|
|
34282
34636
|
name: e.name,
|
|
34283
34637
|
type: e.isDirectory() ? "directory" : "file",
|
|
34284
|
-
size: e.isFile() ?
|
|
34638
|
+
size: e.isFile() ? fs42.statSync(path6.join(dirPath, e.name)).size : void 0
|
|
34285
34639
|
}));
|
|
34286
34640
|
return { success: true, files, path: dirPath };
|
|
34287
34641
|
} catch (e) {
|
|
@@ -35095,6 +35449,7 @@ ${data.message || ""}`.trim();
|
|
|
35095
35449
|
generatingDebouncePending = null;
|
|
35096
35450
|
lastApprovalEventAt = 0;
|
|
35097
35451
|
historyWriter;
|
|
35452
|
+
runtimeMessages = [];
|
|
35098
35453
|
instanceId;
|
|
35099
35454
|
presentationMode;
|
|
35100
35455
|
providerSessionId;
|
|
@@ -35157,6 +35512,7 @@ ${data.message || ""}`.trim();
|
|
|
35157
35512
|
}
|
|
35158
35513
|
const runtime = this.adapter.getRuntimeMetadata();
|
|
35159
35514
|
const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
|
|
35515
|
+
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
35160
35516
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
35161
35517
|
if (parsedMessages.length > 0) {
|
|
35162
35518
|
let messagesToSave = parsedMessages;
|
|
@@ -35186,7 +35542,7 @@ ${data.message || ""}`.trim();
|
|
|
35186
35542
|
id: `${this.type}_${this.workingDir}`,
|
|
35187
35543
|
title: parsedStatus?.title || dirName,
|
|
35188
35544
|
status: parsedStatus?.status || adapterStatus.status,
|
|
35189
|
-
messages:
|
|
35545
|
+
messages: mergedMessages,
|
|
35190
35546
|
activeModal: parsedStatus?.activeModal ?? adapterStatus.activeModal,
|
|
35191
35547
|
inputContent: ""
|
|
35192
35548
|
},
|
|
@@ -35285,6 +35641,11 @@ ${data.message || ""}`.trim();
|
|
|
35285
35641
|
const approvalCooldown = 5e3;
|
|
35286
35642
|
if (this.lastStatus !== "waiting_approval" && (!this.lastApprovalEventAt || now - this.lastApprovalEventAt > approvalCooldown)) {
|
|
35287
35643
|
this.lastApprovalEventAt = now;
|
|
35644
|
+
this.appendRuntimeSystemMessage(
|
|
35645
|
+
this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
|
|
35646
|
+
`approval_request:${now}`,
|
|
35647
|
+
now
|
|
35648
|
+
);
|
|
35288
35649
|
this.pushEvent({
|
|
35289
35650
|
event: "agent:waiting_approval",
|
|
35290
35651
|
chatTitle,
|
|
@@ -35356,11 +35717,71 @@ ${data.message || ""}`.trim();
|
|
|
35356
35717
|
get cliName() {
|
|
35357
35718
|
return this.provider.name;
|
|
35358
35719
|
}
|
|
35720
|
+
recordApprovalSelection(buttonText) {
|
|
35721
|
+
const cleanButton = String(buttonText || "").trim();
|
|
35722
|
+
if (!cleanButton) return;
|
|
35723
|
+
const now = Date.now();
|
|
35724
|
+
this.appendRuntimeSystemMessage(
|
|
35725
|
+
`Approval selected: ${cleanButton}`,
|
|
35726
|
+
`approval_selection:${now}:${cleanButton}`,
|
|
35727
|
+
now
|
|
35728
|
+
);
|
|
35729
|
+
}
|
|
35359
35730
|
formatMarkerTimestamp(timestamp) {
|
|
35360
35731
|
const date5 = new Date(timestamp);
|
|
35361
35732
|
const pad = (value) => String(value).padStart(2, "0");
|
|
35362
35733
|
return `${date5.getFullYear()}-${pad(date5.getMonth() + 1)}-${pad(date5.getDate())} ${pad(date5.getHours())}:${pad(date5.getMinutes())}:${pad(date5.getSeconds())}`;
|
|
35363
35734
|
}
|
|
35735
|
+
appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
|
|
35736
|
+
const normalizedContent = String(content || "").trim();
|
|
35737
|
+
if (!normalizedContent) return;
|
|
35738
|
+
if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
|
|
35739
|
+
this.runtimeMessages.push({
|
|
35740
|
+
key: dedupKey,
|
|
35741
|
+
message: {
|
|
35742
|
+
role: "system",
|
|
35743
|
+
senderName: "System",
|
|
35744
|
+
content: normalizedContent,
|
|
35745
|
+
receivedAt,
|
|
35746
|
+
timestamp: receivedAt
|
|
35747
|
+
}
|
|
35748
|
+
});
|
|
35749
|
+
if (this.runtimeMessages.length > 50) {
|
|
35750
|
+
this.runtimeMessages = this.runtimeMessages.slice(-50);
|
|
35751
|
+
}
|
|
35752
|
+
this.historyWriter.appendNewMessages(
|
|
35753
|
+
this.type,
|
|
35754
|
+
[{
|
|
35755
|
+
role: "system",
|
|
35756
|
+
senderName: "System",
|
|
35757
|
+
content: normalizedContent,
|
|
35758
|
+
receivedAt,
|
|
35759
|
+
historyDedupKey: dedupKey
|
|
35760
|
+
}],
|
|
35761
|
+
this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
|
|
35762
|
+
this.instanceId,
|
|
35763
|
+
this.providerSessionId
|
|
35764
|
+
);
|
|
35765
|
+
}
|
|
35766
|
+
mergeConversationMessages(parsedMessages) {
|
|
35767
|
+
if (this.runtimeMessages.length === 0) return parsedMessages;
|
|
35768
|
+
return [...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b2) => {
|
|
35769
|
+
const aTime = a.message.receivedAt || a.message.timestamp || 0;
|
|
35770
|
+
const bTime = b2.message.receivedAt || b2.message.timestamp || 0;
|
|
35771
|
+
if (aTime !== bTime) return aTime - bTime;
|
|
35772
|
+
return a.index - b2.index;
|
|
35773
|
+
}).map((entry) => entry.message);
|
|
35774
|
+
}
|
|
35775
|
+
formatApprovalRequestMessage(modalMessage, buttons) {
|
|
35776
|
+
const lines = ["Approval requested"];
|
|
35777
|
+
const cleanMessage = String(modalMessage || "").trim();
|
|
35778
|
+
if (cleanMessage) lines.push(cleanMessage);
|
|
35779
|
+
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
35780
|
+
if (labels.length > 0) {
|
|
35781
|
+
lines.push(labels.map((label) => `[${label}]`).join(" "));
|
|
35782
|
+
}
|
|
35783
|
+
return lines.join("\n");
|
|
35784
|
+
}
|
|
35364
35785
|
promoteProviderSessionId(sessionId) {
|
|
35365
35786
|
const nextSessionId = String(sessionId || "").trim();
|
|
35366
35787
|
if (!nextSessionId || nextSessionId === this.providerSessionId) return;
|
|
@@ -37364,6 +37785,7 @@ ${installInfo}`
|
|
|
37364
37785
|
resolve(type, context) {
|
|
37365
37786
|
const base = this.providers.get(type);
|
|
37366
37787
|
if (!base) return void 0;
|
|
37788
|
+
const providerDir = this.findProviderDirInternal(type) || void 0;
|
|
37367
37789
|
const currentOs = context?.os || process.platform;
|
|
37368
37790
|
const currentVersion = context?.version ?? this.versionArchive?.getLatest(type) ?? void 0;
|
|
37369
37791
|
const resolved = JSON.parse(JSON.stringify(base));
|
|
@@ -37373,6 +37795,9 @@ ${installInfo}`
|
|
|
37373
37795
|
if (base.scripts) {
|
|
37374
37796
|
resolved.scripts = { ...base.scripts };
|
|
37375
37797
|
}
|
|
37798
|
+
if (providerDir) {
|
|
37799
|
+
resolved._resolvedProviderDir = providerDir;
|
|
37800
|
+
}
|
|
37376
37801
|
if (base.os?.[currentOs]) {
|
|
37377
37802
|
const osOverride = base.os[currentOs];
|
|
37378
37803
|
if (osOverride.scripts) {
|
|
@@ -37393,6 +37818,12 @@ ${installInfo}`
|
|
|
37393
37818
|
if (loaded) {
|
|
37394
37819
|
resolved.scripts = loaded;
|
|
37395
37820
|
this.log(` [compatibility] ${type} v${currentVersion} \u2192 ${entry.scriptDir}`);
|
|
37821
|
+
resolved._resolvedScriptDir = entry.scriptDir;
|
|
37822
|
+
resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
|
|
37823
|
+
if (providerDir) {
|
|
37824
|
+
const fullDir = path10.join(providerDir, entry.scriptDir);
|
|
37825
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
|
|
37826
|
+
}
|
|
37396
37827
|
matched = true;
|
|
37397
37828
|
}
|
|
37398
37829
|
break;
|
|
@@ -37403,6 +37834,12 @@ ${installInfo}`
|
|
|
37403
37834
|
if (loaded) {
|
|
37404
37835
|
resolved.scripts = loaded;
|
|
37405
37836
|
this.log(` [compatibility] ${type} v${currentVersion} \u2192 default: ${base.defaultScriptDir}`);
|
|
37837
|
+
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
37838
|
+
resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
|
|
37839
|
+
if (providerDir) {
|
|
37840
|
+
const fullDir = path10.join(providerDir, base.defaultScriptDir);
|
|
37841
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
|
|
37842
|
+
}
|
|
37406
37843
|
}
|
|
37407
37844
|
resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
|
|
37408
37845
|
}
|
|
@@ -37415,6 +37852,12 @@ ${installInfo}`
|
|
|
37415
37852
|
if (loaded) {
|
|
37416
37853
|
resolved.scripts = loaded;
|
|
37417
37854
|
this.log(` [version override] ${type} ${range} \u2192 ${dirOverride}`);
|
|
37855
|
+
resolved._resolvedScriptDir = dirOverride;
|
|
37856
|
+
resolved._resolvedScriptsSource = `versions:${range}`;
|
|
37857
|
+
if (providerDir) {
|
|
37858
|
+
const fullDir = path10.join(providerDir, dirOverride);
|
|
37859
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
|
|
37860
|
+
}
|
|
37418
37861
|
}
|
|
37419
37862
|
} else if (override.scripts) {
|
|
37420
37863
|
resolved.scripts = { ...resolved.scripts, ...override.scripts };
|
|
@@ -37426,6 +37869,12 @@ ${installInfo}`
|
|
|
37426
37869
|
if (loaded) {
|
|
37427
37870
|
resolved.scripts = loaded;
|
|
37428
37871
|
this.log(` [compatibility] ${type} no version detected \u2192 default: ${base.defaultScriptDir}`);
|
|
37872
|
+
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
37873
|
+
resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
|
|
37874
|
+
if (providerDir) {
|
|
37875
|
+
const fullDir = path10.join(providerDir, base.defaultScriptDir);
|
|
37876
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
|
|
37877
|
+
}
|
|
37429
37878
|
}
|
|
37430
37879
|
}
|
|
37431
37880
|
if (base.overrides) {
|
|
@@ -37494,6 +37943,9 @@ ${installInfo}`
|
|
|
37494
37943
|
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
37495
37944
|
});
|
|
37496
37945
|
const handleChange = (filePath) => {
|
|
37946
|
+
if (/[\/\\]fixtures[\/\\]/.test(filePath)) {
|
|
37947
|
+
return;
|
|
37948
|
+
}
|
|
37497
37949
|
if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
|
|
37498
37950
|
this.log(`File changed: ${path10.basename(filePath)}, reloading...`);
|
|
37499
37951
|
this.reload();
|
|
@@ -38184,7 +38636,7 @@ ${installInfo}`
|
|
|
38184
38636
|
}
|
|
38185
38637
|
} else if (plat === "win32") {
|
|
38186
38638
|
try {
|
|
38187
|
-
const
|
|
38639
|
+
const fs15 = require("fs");
|
|
38188
38640
|
const appNameMap = getMacAppIdentifiers();
|
|
38189
38641
|
const appName = appNameMap[ideId];
|
|
38190
38642
|
if (appName) {
|
|
@@ -38193,8 +38645,8 @@ ${installInfo}`
|
|
|
38193
38645
|
appName,
|
|
38194
38646
|
"storage.json"
|
|
38195
38647
|
);
|
|
38196
|
-
if (
|
|
38197
|
-
const data = JSON.parse(
|
|
38648
|
+
if (fs15.existsSync(storagePath)) {
|
|
38649
|
+
const data = JSON.parse(fs15.readFileSync(storagePath, "utf-8"));
|
|
38198
38650
|
const workspaces = data?.openedPathsList?.workspaces3 || data?.openedPathsList?.entries || [];
|
|
38199
38651
|
if (workspaces.length > 0) {
|
|
38200
38652
|
const recent = workspaces[0];
|
|
@@ -38654,7 +39106,7 @@ ${installInfo}`
|
|
|
38654
39106
|
function getNpmExecutable() {
|
|
38655
39107
|
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
38656
39108
|
}
|
|
38657
|
-
function
|
|
39109
|
+
function killPid2(pid) {
|
|
38658
39110
|
try {
|
|
38659
39111
|
if (process.platform === "win32") {
|
|
38660
39112
|
(0, import_child_process7.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
|
|
@@ -38683,7 +39135,7 @@ ${installInfo}`
|
|
|
38683
39135
|
if (fs8.existsSync(pidFile)) {
|
|
38684
39136
|
const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
|
|
38685
39137
|
if (Number.isFinite(pid)) {
|
|
38686
|
-
|
|
39138
|
+
killPid2(pid);
|
|
38687
39139
|
}
|
|
38688
39140
|
}
|
|
38689
39141
|
} catch {
|
|
@@ -38699,7 +39151,7 @@ ${installInfo}`
|
|
|
38699
39151
|
for (const line of raw.split("\n")) {
|
|
38700
39152
|
const pid = Number.parseInt(line.trim(), 10);
|
|
38701
39153
|
if (Number.isFinite(pid)) {
|
|
38702
|
-
|
|
39154
|
+
killPid2(pid);
|
|
38703
39155
|
}
|
|
38704
39156
|
}
|
|
38705
39157
|
} catch {
|
|
@@ -39904,6 +40356,15 @@ ${installInfo}`
|
|
|
39904
40356
|
cdpManagerKey: ideType,
|
|
39905
40357
|
instanceKey: `ide:${ideType}`
|
|
39906
40358
|
});
|
|
40359
|
+
const activeSessionId2 = agentStreamManager.getActiveSessionId(parentSessionId);
|
|
40360
|
+
if (!activeSessionId2 || enabledExtTypes.size === 1) {
|
|
40361
|
+
await agentStreamManager.setActiveSession(
|
|
40362
|
+
cdp,
|
|
40363
|
+
parentSessionId,
|
|
40364
|
+
extInstance.getInstanceId()
|
|
40365
|
+
);
|
|
40366
|
+
LOG2.info("AgentStream", `Auto-activated enabled extension: ${extType} (${ideType})`);
|
|
40367
|
+
}
|
|
39907
40368
|
}
|
|
39908
40369
|
LOG2.info("AgentStream", `Extension added: ${extType} (enabled for ${ideType})`);
|
|
39909
40370
|
}
|
|
@@ -40354,8 +40815,8 @@ ${installInfo}`
|
|
|
40354
40815
|
return results;
|
|
40355
40816
|
}
|
|
40356
40817
|
var http2 = __toESM2(require("http"));
|
|
40357
|
-
var
|
|
40358
|
-
var
|
|
40818
|
+
var fs14 = __toESM2(require("fs"));
|
|
40819
|
+
var path18 = __toESM2(require("path"));
|
|
40359
40820
|
function generateFiles(type, name, category, opts = {}) {
|
|
40360
40821
|
const { cdpPorts, cli, processName, installPath, binary, extensionId, version: version2 = "0.1" } = opts;
|
|
40361
40822
|
if (category === "cli" || category === "acp") {
|
|
@@ -41690,6 +42151,162 @@ async (params) => {
|
|
|
41690
42151
|
ctx.json(res, 500, { error: `DOM context collection failed: ${e.message}` });
|
|
41691
42152
|
}
|
|
41692
42153
|
}
|
|
42154
|
+
var fs12 = __toESM2(require("fs"));
|
|
42155
|
+
var path16 = __toESM2(require("path"));
|
|
42156
|
+
function slugifyFixtureName(value) {
|
|
42157
|
+
const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
42158
|
+
return normalized || `fixture-${Date.now()}`;
|
|
42159
|
+
}
|
|
42160
|
+
function getCliFixtureDir(ctx, type) {
|
|
42161
|
+
const providerDir = ctx.providerLoader.findProviderDir(type);
|
|
42162
|
+
if (!providerDir) {
|
|
42163
|
+
throw new Error(`Provider directory not found for '${type}'`);
|
|
42164
|
+
}
|
|
42165
|
+
return path16.join(providerDir, "fixtures");
|
|
42166
|
+
}
|
|
42167
|
+
function readCliFixture(ctx, type, name) {
|
|
42168
|
+
const fixtureDir = getCliFixtureDir(ctx, type);
|
|
42169
|
+
const filePath = path16.join(fixtureDir, `${name}.json`);
|
|
42170
|
+
if (!fs12.existsSync(filePath)) {
|
|
42171
|
+
throw new Error(`Fixture not found: ${filePath}`);
|
|
42172
|
+
}
|
|
42173
|
+
return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
|
|
42174
|
+
}
|
|
42175
|
+
function getExerciseTranscriptText(result) {
|
|
42176
|
+
const parts = [];
|
|
42177
|
+
const debugMessages = Array.isArray(result?.debug?.messages) ? result.debug.messages : [];
|
|
42178
|
+
const traceMessages = Array.isArray(result?.trace?.messages) ? result.trace.messages : [];
|
|
42179
|
+
for (const message of [...debugMessages, ...traceMessages]) {
|
|
42180
|
+
if (!message || typeof message.content !== "string") continue;
|
|
42181
|
+
parts.push(message.content);
|
|
42182
|
+
}
|
|
42183
|
+
if (typeof result?.debug?.partialResponse === "string") parts.push(result.debug.partialResponse);
|
|
42184
|
+
if (typeof result?.trace?.responseBuffer === "string") parts.push(result.trace.responseBuffer);
|
|
42185
|
+
return parts.join("\n");
|
|
42186
|
+
}
|
|
42187
|
+
function getExerciseLastAssistant(result) {
|
|
42188
|
+
const debugMessages = Array.isArray(result?.debug?.messages) ? result.debug.messages : [];
|
|
42189
|
+
const traceMessages = Array.isArray(result?.trace?.messages) ? result.trace.messages : [];
|
|
42190
|
+
for (const messages of [debugMessages, traceMessages]) {
|
|
42191
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
42192
|
+
const message = messages[i];
|
|
42193
|
+
if (message?.role === "assistant" && typeof message.content === "string" && message.content.trim()) {
|
|
42194
|
+
return message.content;
|
|
42195
|
+
}
|
|
42196
|
+
}
|
|
42197
|
+
}
|
|
42198
|
+
return "";
|
|
42199
|
+
}
|
|
42200
|
+
function getExerciseMessageCount(result) {
|
|
42201
|
+
const debugMessages = Array.isArray(result?.debug?.messages) ? result.debug.messages : [];
|
|
42202
|
+
const traceMessages = Array.isArray(result?.trace?.messages) ? result.trace.messages : [];
|
|
42203
|
+
return Math.max(debugMessages.length, traceMessages.length);
|
|
42204
|
+
}
|
|
42205
|
+
function compileFixtureRegex(source) {
|
|
42206
|
+
const value = String(source || "").trim();
|
|
42207
|
+
if (!value) return null;
|
|
42208
|
+
const delimited = value.match(/^\/([\s\S]+)\/([dgimsuvy]*)$/);
|
|
42209
|
+
try {
|
|
42210
|
+
if (delimited) {
|
|
42211
|
+
return new RegExp(delimited[1], delimited[2]);
|
|
42212
|
+
}
|
|
42213
|
+
return new RegExp(value, "m");
|
|
42214
|
+
} catch {
|
|
42215
|
+
return null;
|
|
42216
|
+
}
|
|
42217
|
+
}
|
|
42218
|
+
function statusesContainSequence(actual, expected) {
|
|
42219
|
+
if (!expected.length) return true;
|
|
42220
|
+
let index = 0;
|
|
42221
|
+
for (const status of actual) {
|
|
42222
|
+
if (status === expected[index]) index += 1;
|
|
42223
|
+
if (index >= expected.length) return true;
|
|
42224
|
+
}
|
|
42225
|
+
return false;
|
|
42226
|
+
}
|
|
42227
|
+
function validateCliFixtureResult(result, assertions) {
|
|
42228
|
+
const failures = [];
|
|
42229
|
+
const transcriptText = getExerciseTranscriptText(result);
|
|
42230
|
+
const lastAssistant = getExerciseLastAssistant(result);
|
|
42231
|
+
const mustContainAny = assertions.mustContainAny || [];
|
|
42232
|
+
const mustNotContainAny = assertions.mustNotContainAny || [];
|
|
42233
|
+
const mustMatchAny = assertions.mustMatchAny || [];
|
|
42234
|
+
const mustNotMatchAny = assertions.mustNotMatchAny || [];
|
|
42235
|
+
const lastAssistantMustContainAny = assertions.lastAssistantMustContainAny || [];
|
|
42236
|
+
const lastAssistantMustNotContainAny = assertions.lastAssistantMustNotContainAny || [];
|
|
42237
|
+
const lastAssistantMustMatchAny = assertions.lastAssistantMustMatchAny || [];
|
|
42238
|
+
const lastAssistantMustNotMatchAny = assertions.lastAssistantMustNotMatchAny || [];
|
|
42239
|
+
const statusesSeen = Array.isArray(result?.statusesSeen) ? result.statusesSeen.map((value) => String(value)) : [];
|
|
42240
|
+
if (assertions.requireNotTimedOut !== false && result?.timedOut) {
|
|
42241
|
+
failures.push("Exercise timed out");
|
|
42242
|
+
}
|
|
42243
|
+
const missingRequired = mustContainAny.filter((value) => !transcriptText.includes(value));
|
|
42244
|
+
if (missingRequired.length > 0) {
|
|
42245
|
+
failures.push(`Missing required substrings: ${missingRequired.join(", ")}`);
|
|
42246
|
+
}
|
|
42247
|
+
const presentBanned = mustNotContainAny.filter((value) => transcriptText.includes(value));
|
|
42248
|
+
if (presentBanned.length > 0) {
|
|
42249
|
+
failures.push(`Found banned substrings: ${presentBanned.join(", ")}`);
|
|
42250
|
+
}
|
|
42251
|
+
const missingRegex = mustMatchAny.filter((value) => {
|
|
42252
|
+
const regex = compileFixtureRegex(value);
|
|
42253
|
+
return !regex || !regex.test(transcriptText);
|
|
42254
|
+
});
|
|
42255
|
+
if (missingRegex.length > 0) {
|
|
42256
|
+
failures.push(`Missing required regex matches: ${missingRegex.join(", ")}`);
|
|
42257
|
+
}
|
|
42258
|
+
const presentBannedRegex = mustNotMatchAny.filter((value) => {
|
|
42259
|
+
const regex = compileFixtureRegex(value);
|
|
42260
|
+
return !!regex && regex.test(transcriptText);
|
|
42261
|
+
});
|
|
42262
|
+
if (presentBannedRegex.length > 0) {
|
|
42263
|
+
failures.push(`Found banned regex matches: ${presentBannedRegex.join(", ")}`);
|
|
42264
|
+
}
|
|
42265
|
+
const missingLastAssistant = lastAssistantMustContainAny.filter((value) => !lastAssistant.includes(value));
|
|
42266
|
+
if (missingLastAssistant.length > 0) {
|
|
42267
|
+
failures.push(`Missing required lastAssistant substrings: ${missingLastAssistant.join(", ")}`);
|
|
42268
|
+
}
|
|
42269
|
+
const presentBannedLastAssistant = lastAssistantMustNotContainAny.filter((value) => lastAssistant.includes(value));
|
|
42270
|
+
if (presentBannedLastAssistant.length > 0) {
|
|
42271
|
+
failures.push(`Found banned lastAssistant substrings: ${presentBannedLastAssistant.join(", ")}`);
|
|
42272
|
+
}
|
|
42273
|
+
const missingLastAssistantRegex = lastAssistantMustMatchAny.filter((value) => {
|
|
42274
|
+
const regex = compileFixtureRegex(value);
|
|
42275
|
+
return !regex || !regex.test(lastAssistant);
|
|
42276
|
+
});
|
|
42277
|
+
if (missingLastAssistantRegex.length > 0) {
|
|
42278
|
+
failures.push(`Missing required lastAssistant regex matches: ${missingLastAssistantRegex.join(", ")}`);
|
|
42279
|
+
}
|
|
42280
|
+
const presentBannedLastAssistantRegex = lastAssistantMustNotMatchAny.filter((value) => {
|
|
42281
|
+
const regex = compileFixtureRegex(value);
|
|
42282
|
+
return !!regex && regex.test(lastAssistant);
|
|
42283
|
+
});
|
|
42284
|
+
if (presentBannedLastAssistantRegex.length > 0) {
|
|
42285
|
+
failures.push(`Found banned lastAssistant regex matches: ${presentBannedLastAssistantRegex.join(", ")}`);
|
|
42286
|
+
}
|
|
42287
|
+
if (assertions.statusesSeen?.length && !statusesContainSequence(statusesSeen, assertions.statusesSeen)) {
|
|
42288
|
+
failures.push(`Expected statuses sequence not observed: ${assertions.statusesSeen.join(" -> ")}`);
|
|
42289
|
+
}
|
|
42290
|
+
if (result && typeof result === "object") {
|
|
42291
|
+
result.lastAssistant = lastAssistant;
|
|
42292
|
+
}
|
|
42293
|
+
return failures;
|
|
42294
|
+
}
|
|
42295
|
+
function getCliProviderResolutionMeta(ctx, type, adapter) {
|
|
42296
|
+
const adapterMeta = typeof adapter?.getProviderResolutionMeta === "function" ? adapter.getProviderResolutionMeta() : adapter?.getDebugState?.()?.providerResolution || null;
|
|
42297
|
+
const resolvedProvider = ctx.providerLoader.resolve(type);
|
|
42298
|
+
if (!adapterMeta && !resolvedProvider) return null;
|
|
42299
|
+
return {
|
|
42300
|
+
type,
|
|
42301
|
+
providerDir: adapterMeta?.providerDir || resolvedProvider?._resolvedProviderDir || ctx.providerLoader.findProviderDir(type),
|
|
42302
|
+
scriptDir: adapterMeta?.scriptDir || resolvedProvider?._resolvedScriptDir || null,
|
|
42303
|
+
scriptsPath: adapterMeta?.scriptsPath || resolvedProvider?._resolvedScriptsPath || null,
|
|
42304
|
+
scriptsSource: adapterMeta?.scriptsSource || resolvedProvider?._resolvedScriptsSource || null,
|
|
42305
|
+
resolvedVersion: adapterMeta?.resolvedVersion || resolvedProvider?._resolvedVersion || null,
|
|
42306
|
+
resolvedOs: adapterMeta?.resolvedOs || resolvedProvider?._resolvedOs || null,
|
|
42307
|
+
versionWarning: adapterMeta?.versionWarning || resolvedProvider?._versionWarning || null
|
|
42308
|
+
};
|
|
42309
|
+
}
|
|
41693
42310
|
function findCliTarget(ctx, type, instanceId) {
|
|
41694
42311
|
if (!ctx.instanceManager) return null;
|
|
41695
42312
|
const cliStates = ctx.instanceManager.collectAllStates().filter((s15) => s15.category === "cli" || s15.category === "acp");
|
|
@@ -41698,6 +42315,331 @@ async (params) => {
|
|
|
41698
42315
|
const matches = cliStates.filter((s15) => s15.type === type);
|
|
41699
42316
|
return matches[matches.length - 1] || null;
|
|
41700
42317
|
}
|
|
42318
|
+
function getCliTargetBundle(ctx, type, instanceId) {
|
|
42319
|
+
if (!ctx.instanceManager) return null;
|
|
42320
|
+
const target = findCliTarget(ctx, type, instanceId);
|
|
42321
|
+
if (!target) return null;
|
|
42322
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId);
|
|
42323
|
+
if (!instance) return null;
|
|
42324
|
+
const adapter = instance.getAdapter?.() || instance.adapter;
|
|
42325
|
+
if (!adapter) return null;
|
|
42326
|
+
return { target, instance, adapter };
|
|
42327
|
+
}
|
|
42328
|
+
function sleep(ms2) {
|
|
42329
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms2));
|
|
42330
|
+
}
|
|
42331
|
+
async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
|
|
42332
|
+
const startedAt = Date.now();
|
|
42333
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
42334
|
+
const bundle = getCliTargetBundle(ctx, type, instanceId);
|
|
42335
|
+
if (bundle) {
|
|
42336
|
+
const debug = typeof bundle.adapter.getDebugState === "function" ? bundle.adapter.getDebugState() : null;
|
|
42337
|
+
const startupParseGate = !!debug?.startupParseGate;
|
|
42338
|
+
const adapterReady = !!debug?.ready;
|
|
42339
|
+
const visibleStatusReady = bundle.target.status === "generating" || bundle.target.status === "waiting_approval";
|
|
42340
|
+
const idleReady = bundle.target.status === "idle" && !startupParseGate;
|
|
42341
|
+
if (adapterReady || visibleStatusReady || idleReady) {
|
|
42342
|
+
return bundle;
|
|
42343
|
+
}
|
|
42344
|
+
}
|
|
42345
|
+
await sleep(100);
|
|
42346
|
+
}
|
|
42347
|
+
return getCliTargetBundle(ctx, type, instanceId);
|
|
42348
|
+
}
|
|
42349
|
+
async function runCliExerciseInternal(ctx, body) {
|
|
42350
|
+
if (!ctx.cliManager) {
|
|
42351
|
+
throw new Error("CliManager not available");
|
|
42352
|
+
}
|
|
42353
|
+
if (!ctx.instanceManager) {
|
|
42354
|
+
throw new Error("InstanceManager not available");
|
|
42355
|
+
}
|
|
42356
|
+
const {
|
|
42357
|
+
type,
|
|
42358
|
+
text,
|
|
42359
|
+
instanceId: requestedInstanceId,
|
|
42360
|
+
workingDir,
|
|
42361
|
+
args,
|
|
42362
|
+
autoLaunch = true,
|
|
42363
|
+
freshSession = true,
|
|
42364
|
+
autoResolveApprovals = true,
|
|
42365
|
+
approvalButtonIndex = 0,
|
|
42366
|
+
timeoutMs = 45e3,
|
|
42367
|
+
readyTimeoutMs = 15e3,
|
|
42368
|
+
idleSettledMs = 1200,
|
|
42369
|
+
traceLimit = 160,
|
|
42370
|
+
stopWhenDone = false
|
|
42371
|
+
} = body || {};
|
|
42372
|
+
if (!type) {
|
|
42373
|
+
throw new Error("type required (e.g. claude-cli, codex-cli)");
|
|
42374
|
+
}
|
|
42375
|
+
if (!text || typeof text !== "string") {
|
|
42376
|
+
throw new Error("text required (prompt to send to the CLI)");
|
|
42377
|
+
}
|
|
42378
|
+
let resolvedInstanceId = requestedInstanceId;
|
|
42379
|
+
if (freshSession) {
|
|
42380
|
+
const staleTargets = ctx.instanceManager.collectAllStates().filter((state) => (state.category === "cli" || state.category === "acp") && state.type === type).map((state) => state.instanceId);
|
|
42381
|
+
for (const staleId of staleTargets) {
|
|
42382
|
+
ctx.instanceManager.removeInstance(staleId);
|
|
42383
|
+
}
|
|
42384
|
+
resolvedInstanceId = void 0;
|
|
42385
|
+
}
|
|
42386
|
+
let bundle = getCliTargetBundle(ctx, type, resolvedInstanceId);
|
|
42387
|
+
if (!bundle && autoLaunch) {
|
|
42388
|
+
const launchArgs = [type, workingDir || process.cwd(), Array.isArray(args) ? args : []];
|
|
42389
|
+
let launched = null;
|
|
42390
|
+
let lastLaunchError = null;
|
|
42391
|
+
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
|
42392
|
+
try {
|
|
42393
|
+
launched = await ctx.cliManager.startSession(...launchArgs);
|
|
42394
|
+
lastLaunchError = null;
|
|
42395
|
+
break;
|
|
42396
|
+
} catch (error48) {
|
|
42397
|
+
lastLaunchError = error48 instanceof Error ? error48 : new Error(String(error48?.message || error48));
|
|
42398
|
+
const message = String(lastLaunchError.message || "");
|
|
42399
|
+
const retryable = /ECONNREFUSED|session-host|Session host/i.test(message);
|
|
42400
|
+
if (!retryable || attempt === 2) break;
|
|
42401
|
+
await sleep(1e3);
|
|
42402
|
+
}
|
|
42403
|
+
}
|
|
42404
|
+
if (!launched) {
|
|
42405
|
+
throw lastLaunchError || new Error(`Failed to start ${type}`);
|
|
42406
|
+
}
|
|
42407
|
+
resolvedInstanceId = launched.runtimeSessionId;
|
|
42408
|
+
bundle = await waitForCliReady(ctx, type, resolvedInstanceId, Math.max(1e3, readyTimeoutMs));
|
|
42409
|
+
}
|
|
42410
|
+
if (!bundle) {
|
|
42411
|
+
throw new Error(`No running instance found for: ${resolvedInstanceId || type}`);
|
|
42412
|
+
}
|
|
42413
|
+
const initialDebug = typeof bundle.adapter.getDebugState === "function" ? bundle.adapter.getDebugState() : null;
|
|
42414
|
+
const initialTrace = typeof bundle.adapter.getTraceState === "function" ? bundle.adapter.getTraceState(traceLimit) : null;
|
|
42415
|
+
const providerResolution = getCliProviderResolutionMeta(ctx, bundle.target.type, bundle.adapter);
|
|
42416
|
+
const preTraceCount = Number(initialTrace?.entryCount || 0);
|
|
42417
|
+
const startAt = Date.now();
|
|
42418
|
+
const statusesSeen = [];
|
|
42419
|
+
const approvalsResolved = [];
|
|
42420
|
+
let lastStatus = "";
|
|
42421
|
+
let lastModalKey = "";
|
|
42422
|
+
let idleSince = 0;
|
|
42423
|
+
let sawBusy = false;
|
|
42424
|
+
ctx.instanceManager.sendEvent(bundle.target.instanceId, "send_message", { text });
|
|
42425
|
+
while (Date.now() - startAt < Math.max(1e3, timeoutMs)) {
|
|
42426
|
+
await sleep(150);
|
|
42427
|
+
bundle = getCliTargetBundle(ctx, type, bundle.target.instanceId);
|
|
42428
|
+
if (!bundle) {
|
|
42429
|
+
throw new Error("CLI instance disappeared during exercise");
|
|
42430
|
+
}
|
|
42431
|
+
const debug = typeof bundle.adapter.getDebugState === "function" ? bundle.adapter.getDebugState() : null;
|
|
42432
|
+
const trace = typeof bundle.adapter.getTraceState === "function" ? bundle.adapter.getTraceState(traceLimit) : null;
|
|
42433
|
+
const status = String(debug?.status || bundle.target.status || "unknown");
|
|
42434
|
+
const traceEntries = Array.isArray(trace?.entries) ? trace.entries : [];
|
|
42435
|
+
const sawSendMessage = traceEntries.some((entry) => entry?.type === "send_message");
|
|
42436
|
+
const sawSubmitWrite = traceEntries.some((entry) => entry?.type === "submit_write");
|
|
42437
|
+
const hasTurnStarted = sawSendMessage || sawSubmitWrite || !!debug?.currentTurnScope;
|
|
42438
|
+
if (status !== lastStatus) {
|
|
42439
|
+
statusesSeen.push(status);
|
|
42440
|
+
lastStatus = status;
|
|
42441
|
+
}
|
|
42442
|
+
if (status === "generating" || status === "waiting_approval") {
|
|
42443
|
+
sawBusy = true;
|
|
42444
|
+
idleSince = 0;
|
|
42445
|
+
}
|
|
42446
|
+
const modal = debug?.activeModal || trace?.activeModal || null;
|
|
42447
|
+
if (autoResolveApprovals && status === "waiting_approval" && modal && Array.isArray(modal.buttons) && modal.buttons.length > 0) {
|
|
42448
|
+
const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
|
|
42449
|
+
const modalKey = JSON.stringify({
|
|
42450
|
+
message: modal.message || "",
|
|
42451
|
+
buttons: modal.buttons,
|
|
42452
|
+
index: clampedIndex
|
|
42453
|
+
});
|
|
42454
|
+
if (modalKey !== lastModalKey && typeof bundle.adapter.resolveModal === "function") {
|
|
42455
|
+
lastModalKey = modalKey;
|
|
42456
|
+
approvalsResolved.push({
|
|
42457
|
+
at: Date.now(),
|
|
42458
|
+
buttonIndex: clampedIndex,
|
|
42459
|
+
label: modal.buttons[clampedIndex] || null
|
|
42460
|
+
});
|
|
42461
|
+
bundle.adapter.resolveModal(clampedIndex);
|
|
42462
|
+
continue;
|
|
42463
|
+
}
|
|
42464
|
+
}
|
|
42465
|
+
const traceCount = Number(trace?.entryCount || 0);
|
|
42466
|
+
const hasProgress = hasTurnStarted && (traceCount > preTraceCount || statusesSeen.length > 1 || approvalsResolved.length > 0);
|
|
42467
|
+
if (status === "idle" && hasProgress && sawBusy) {
|
|
42468
|
+
if (!idleSince) idleSince = Date.now();
|
|
42469
|
+
if (Date.now() - idleSince >= Math.max(200, idleSettledMs)) {
|
|
42470
|
+
const payload2 = {
|
|
42471
|
+
exercised: true,
|
|
42472
|
+
instanceId: bundle.target.instanceId,
|
|
42473
|
+
providerState: {
|
|
42474
|
+
type: bundle.target.type,
|
|
42475
|
+
name: bundle.target.name,
|
|
42476
|
+
status: bundle.target.status,
|
|
42477
|
+
mode: "mode" in bundle.target ? bundle.target.mode : void 0
|
|
42478
|
+
},
|
|
42479
|
+
providerResolution,
|
|
42480
|
+
initialDebug,
|
|
42481
|
+
initialTrace,
|
|
42482
|
+
debug,
|
|
42483
|
+
trace,
|
|
42484
|
+
statusesSeen,
|
|
42485
|
+
approvalsResolved,
|
|
42486
|
+
elapsedMs: Date.now() - startAt,
|
|
42487
|
+
timedOut: false
|
|
42488
|
+
};
|
|
42489
|
+
payload2.lastAssistant = getExerciseLastAssistant(payload2);
|
|
42490
|
+
payload2.messageCount = getExerciseMessageCount(payload2);
|
|
42491
|
+
if (stopWhenDone) {
|
|
42492
|
+
ctx.instanceManager.removeInstance(bundle.target.instanceId);
|
|
42493
|
+
}
|
|
42494
|
+
return payload2;
|
|
42495
|
+
}
|
|
42496
|
+
} else if (status === "idle" && hasProgress) {
|
|
42497
|
+
if (!idleSince) idleSince = Date.now();
|
|
42498
|
+
if (Date.now() - idleSince >= Math.max(500, idleSettledMs) && Date.now() - startAt >= 750) {
|
|
42499
|
+
const payload2 = {
|
|
42500
|
+
exercised: true,
|
|
42501
|
+
instanceId: bundle.target.instanceId,
|
|
42502
|
+
providerState: {
|
|
42503
|
+
type: bundle.target.type,
|
|
42504
|
+
name: bundle.target.name,
|
|
42505
|
+
status: bundle.target.status,
|
|
42506
|
+
mode: "mode" in bundle.target ? bundle.target.mode : void 0
|
|
42507
|
+
},
|
|
42508
|
+
providerResolution,
|
|
42509
|
+
initialDebug,
|
|
42510
|
+
initialTrace,
|
|
42511
|
+
debug,
|
|
42512
|
+
trace,
|
|
42513
|
+
statusesSeen,
|
|
42514
|
+
approvalsResolved,
|
|
42515
|
+
elapsedMs: Date.now() - startAt,
|
|
42516
|
+
timedOut: false
|
|
42517
|
+
};
|
|
42518
|
+
payload2.lastAssistant = getExerciseLastAssistant(payload2);
|
|
42519
|
+
payload2.messageCount = getExerciseMessageCount(payload2);
|
|
42520
|
+
if (stopWhenDone) {
|
|
42521
|
+
ctx.instanceManager.removeInstance(bundle.target.instanceId);
|
|
42522
|
+
}
|
|
42523
|
+
return payload2;
|
|
42524
|
+
}
|
|
42525
|
+
} else {
|
|
42526
|
+
idleSince = 0;
|
|
42527
|
+
}
|
|
42528
|
+
}
|
|
42529
|
+
const finalBundle = getCliTargetBundle(ctx, type, bundle.target.instanceId) || bundle;
|
|
42530
|
+
const finalDebug = typeof finalBundle.adapter.getDebugState === "function" ? finalBundle.adapter.getDebugState() : null;
|
|
42531
|
+
const finalTrace = typeof finalBundle.adapter.getTraceState === "function" ? finalBundle.adapter.getTraceState(traceLimit) : null;
|
|
42532
|
+
if (stopWhenDone) {
|
|
42533
|
+
ctx.instanceManager.removeInstance(finalBundle.target.instanceId);
|
|
42534
|
+
}
|
|
42535
|
+
const payload = {
|
|
42536
|
+
exercised: true,
|
|
42537
|
+
instanceId: finalBundle.target.instanceId,
|
|
42538
|
+
providerState: {
|
|
42539
|
+
type: finalBundle.target.type,
|
|
42540
|
+
name: finalBundle.target.name,
|
|
42541
|
+
status: finalBundle.target.status,
|
|
42542
|
+
mode: "mode" in finalBundle.target ? finalBundle.target.mode : void 0
|
|
42543
|
+
},
|
|
42544
|
+
providerResolution: getCliProviderResolutionMeta(ctx, finalBundle.target.type, finalBundle.adapter),
|
|
42545
|
+
initialDebug,
|
|
42546
|
+
initialTrace,
|
|
42547
|
+
debug: finalDebug,
|
|
42548
|
+
trace: finalTrace,
|
|
42549
|
+
statusesSeen,
|
|
42550
|
+
approvalsResolved,
|
|
42551
|
+
elapsedMs: Date.now() - startAt,
|
|
42552
|
+
timedOut: true
|
|
42553
|
+
};
|
|
42554
|
+
payload.lastAssistant = getExerciseLastAssistant(payload);
|
|
42555
|
+
payload.messageCount = getExerciseMessageCount(payload);
|
|
42556
|
+
return payload;
|
|
42557
|
+
}
|
|
42558
|
+
async function runCliAutoImplVerification(ctx, type, verification) {
|
|
42559
|
+
const assertions = {
|
|
42560
|
+
mustContainAny: verification?.mustContainAny || [],
|
|
42561
|
+
mustNotContainAny: verification?.mustNotContainAny || [],
|
|
42562
|
+
mustMatchAny: verification?.mustMatchAny || [],
|
|
42563
|
+
mustNotMatchAny: verification?.mustNotMatchAny || [],
|
|
42564
|
+
lastAssistantMustContainAny: verification?.lastAssistantMustContainAny || [],
|
|
42565
|
+
lastAssistantMustNotContainAny: verification?.lastAssistantMustNotContainAny || [],
|
|
42566
|
+
lastAssistantMustMatchAny: verification?.lastAssistantMustMatchAny || [],
|
|
42567
|
+
lastAssistantMustNotMatchAny: verification?.lastAssistantMustNotMatchAny || [],
|
|
42568
|
+
requireNotTimedOut: true
|
|
42569
|
+
};
|
|
42570
|
+
const rawFixtureNames = Array.isArray(verification?.fixtureNames) ? verification.fixtureNames.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
42571
|
+
if (rawFixtureNames.length > 0) {
|
|
42572
|
+
const results = [];
|
|
42573
|
+
for (const rawFixtureName2 of rawFixtureNames) {
|
|
42574
|
+
const name = slugifyFixtureName(rawFixtureName2);
|
|
42575
|
+
const fixture = readCliFixture(ctx, type, name);
|
|
42576
|
+
const mergedAssertions = {
|
|
42577
|
+
...fixture.assertions,
|
|
42578
|
+
...assertions
|
|
42579
|
+
};
|
|
42580
|
+
const result2 = await runCliExerciseInternal(ctx, {
|
|
42581
|
+
...fixture.request,
|
|
42582
|
+
type
|
|
42583
|
+
});
|
|
42584
|
+
const failures2 = validateCliFixtureResult(result2, mergedAssertions);
|
|
42585
|
+
results.push({
|
|
42586
|
+
fixtureName: name,
|
|
42587
|
+
pass: failures2.length === 0,
|
|
42588
|
+
failures: failures2,
|
|
42589
|
+
result: result2,
|
|
42590
|
+
assertions: mergedAssertions,
|
|
42591
|
+
fixture
|
|
42592
|
+
});
|
|
42593
|
+
}
|
|
42594
|
+
const firstFailure = results.find((item) => !item.pass) || results[results.length - 1];
|
|
42595
|
+
return {
|
|
42596
|
+
mode: "fixture_replay_suite",
|
|
42597
|
+
pass: results.every((item) => item.pass),
|
|
42598
|
+
failures: results.flatMap((item) => item.failures.map((failure) => `${item.fixtureName}: ${failure}`)),
|
|
42599
|
+
result: firstFailure.result,
|
|
42600
|
+
assertions: firstFailure.assertions,
|
|
42601
|
+
fixture: firstFailure.fixture,
|
|
42602
|
+
results
|
|
42603
|
+
};
|
|
42604
|
+
}
|
|
42605
|
+
const rawFixtureName = String(verification?.fixtureName || "").trim();
|
|
42606
|
+
if (rawFixtureName) {
|
|
42607
|
+
const name = slugifyFixtureName(rawFixtureName);
|
|
42608
|
+
try {
|
|
42609
|
+
const fixture = readCliFixture(ctx, type, name);
|
|
42610
|
+
const mergedAssertions = {
|
|
42611
|
+
...fixture.assertions,
|
|
42612
|
+
...assertions
|
|
42613
|
+
};
|
|
42614
|
+
const result2 = await runCliExerciseInternal(ctx, {
|
|
42615
|
+
...fixture.request,
|
|
42616
|
+
type
|
|
42617
|
+
});
|
|
42618
|
+
const failures2 = validateCliFixtureResult(result2, mergedAssertions);
|
|
42619
|
+
return {
|
|
42620
|
+
mode: "fixture_replay",
|
|
42621
|
+
pass: failures2.length === 0,
|
|
42622
|
+
failures: failures2,
|
|
42623
|
+
result: result2,
|
|
42624
|
+
assertions: mergedAssertions,
|
|
42625
|
+
fixture
|
|
42626
|
+
};
|
|
42627
|
+
} catch {
|
|
42628
|
+
}
|
|
42629
|
+
}
|
|
42630
|
+
const result = await runCliExerciseInternal(ctx, {
|
|
42631
|
+
...verification?.request || {},
|
|
42632
|
+
type
|
|
42633
|
+
});
|
|
42634
|
+
const failures = validateCliFixtureResult(result, assertions);
|
|
42635
|
+
return {
|
|
42636
|
+
mode: "exercise",
|
|
42637
|
+
pass: failures.length === 0,
|
|
42638
|
+
failures,
|
|
42639
|
+
result,
|
|
42640
|
+
assertions
|
|
42641
|
+
};
|
|
42642
|
+
}
|
|
41701
42643
|
async function handleCliStatus(ctx, _req, res) {
|
|
41702
42644
|
if (!ctx.instanceManager) {
|
|
41703
42645
|
ctx.json(res, 503, { error: "InstanceManager not available (daemon not fully initialized)" });
|
|
@@ -41836,12 +42778,14 @@ async (params) => {
|
|
|
41836
42778
|
status: target.status,
|
|
41837
42779
|
mode: "mode" in target ? target.mode : void 0
|
|
41838
42780
|
},
|
|
42781
|
+
providerResolution: getCliProviderResolutionMeta(ctx, target.type, adapter),
|
|
41839
42782
|
debug: debugState
|
|
41840
42783
|
});
|
|
41841
42784
|
} else {
|
|
41842
42785
|
ctx.json(res, 200, {
|
|
41843
42786
|
instanceId: target.instanceId,
|
|
41844
42787
|
providerState: target,
|
|
42788
|
+
providerResolution: getCliProviderResolutionMeta(ctx, target.type, adapter),
|
|
41845
42789
|
debug: null,
|
|
41846
42790
|
message: "No debug state available (adapter.getDebugState not found)"
|
|
41847
42791
|
});
|
|
@@ -41850,6 +42794,191 @@ async (params) => {
|
|
|
41850
42794
|
ctx.json(res, 500, { error: `Debug state failed: ${e.message}` });
|
|
41851
42795
|
}
|
|
41852
42796
|
}
|
|
42797
|
+
async function handleCliTrace(ctx, type, req, res) {
|
|
42798
|
+
if (!ctx.instanceManager) {
|
|
42799
|
+
ctx.json(res, 503, { error: "InstanceManager not available" });
|
|
42800
|
+
return;
|
|
42801
|
+
}
|
|
42802
|
+
const target = findCliTarget(ctx, type);
|
|
42803
|
+
if (!target) {
|
|
42804
|
+
const allStates = ctx.instanceManager.collectAllStates();
|
|
42805
|
+
ctx.json(res, 404, {
|
|
42806
|
+
error: `No running instance for: ${type}`,
|
|
42807
|
+
available: allStates.filter((s15) => s15.category === "cli" || s15.category === "acp").map((s15) => s15.type)
|
|
42808
|
+
});
|
|
42809
|
+
return;
|
|
42810
|
+
}
|
|
42811
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId);
|
|
42812
|
+
if (!instance) {
|
|
42813
|
+
ctx.json(res, 404, { error: `Instance not found: ${target.instanceId}` });
|
|
42814
|
+
return;
|
|
42815
|
+
}
|
|
42816
|
+
try {
|
|
42817
|
+
const adapter = instance.getAdapter?.() || instance.adapter;
|
|
42818
|
+
const url2 = new URL(req.url || "/", "http://127.0.0.1");
|
|
42819
|
+
const limit = parseInt(url2.searchParams.get("limit") || "120", 10);
|
|
42820
|
+
if (adapter && typeof adapter.getTraceState === "function") {
|
|
42821
|
+
const trace = adapter.getTraceState(limit);
|
|
42822
|
+
const debug = typeof adapter.getDebugState === "function" ? adapter.getDebugState() : null;
|
|
42823
|
+
ctx.json(res, 200, {
|
|
42824
|
+
instanceId: target.instanceId,
|
|
42825
|
+
providerState: {
|
|
42826
|
+
type: target.type,
|
|
42827
|
+
name: target.name,
|
|
42828
|
+
status: target.status,
|
|
42829
|
+
mode: "mode" in target ? target.mode : void 0
|
|
42830
|
+
},
|
|
42831
|
+
providerResolution: getCliProviderResolutionMeta(ctx, target.type, adapter),
|
|
42832
|
+
debug,
|
|
42833
|
+
trace
|
|
42834
|
+
});
|
|
42835
|
+
} else {
|
|
42836
|
+
ctx.json(res, 200, {
|
|
42837
|
+
instanceId: target.instanceId,
|
|
42838
|
+
providerState: target,
|
|
42839
|
+
providerResolution: getCliProviderResolutionMeta(ctx, target.type, adapter),
|
|
42840
|
+
debug: typeof adapter?.getDebugState === "function" ? adapter.getDebugState() : null,
|
|
42841
|
+
trace: null,
|
|
42842
|
+
message: "No trace state available (adapter.getTraceState not found)"
|
|
42843
|
+
});
|
|
42844
|
+
}
|
|
42845
|
+
} catch (e) {
|
|
42846
|
+
ctx.json(res, 500, { error: `Trace state failed: ${e.message}` });
|
|
42847
|
+
}
|
|
42848
|
+
}
|
|
42849
|
+
async function handleCliExercise(ctx, req, res) {
|
|
42850
|
+
try {
|
|
42851
|
+
const body = await ctx.readBody(req);
|
|
42852
|
+
const result = await runCliExerciseInternal(ctx, body || {});
|
|
42853
|
+
ctx.json(res, 200, result);
|
|
42854
|
+
} catch (e) {
|
|
42855
|
+
ctx.json(res, 500, { error: `Exercise failed: ${e.message}` });
|
|
42856
|
+
}
|
|
42857
|
+
}
|
|
42858
|
+
async function handleCliFixtureCapture(ctx, req, res) {
|
|
42859
|
+
try {
|
|
42860
|
+
const body = await ctx.readBody(req);
|
|
42861
|
+
const type = String(body?.type || "");
|
|
42862
|
+
const request = body?.request || {};
|
|
42863
|
+
if (!type) {
|
|
42864
|
+
ctx.json(res, 400, { error: "type required" });
|
|
42865
|
+
return;
|
|
42866
|
+
}
|
|
42867
|
+
if (!request?.text) {
|
|
42868
|
+
ctx.json(res, 400, { error: "request.text required" });
|
|
42869
|
+
return;
|
|
42870
|
+
}
|
|
42871
|
+
const fixtureDir = getCliFixtureDir(ctx, type);
|
|
42872
|
+
fs12.mkdirSync(fixtureDir, { recursive: true });
|
|
42873
|
+
const name = slugifyFixtureName(String(body?.name || `${type}-${Date.now()}`));
|
|
42874
|
+
const result = await runCliExerciseInternal(ctx, { ...request, type });
|
|
42875
|
+
const fixture = {
|
|
42876
|
+
version: 1,
|
|
42877
|
+
kind: "cli-exercise-fixture",
|
|
42878
|
+
name,
|
|
42879
|
+
type,
|
|
42880
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
42881
|
+
providerDir: ctx.providerLoader.findProviderDir(type),
|
|
42882
|
+
providerResolution: result?.providerResolution || null,
|
|
42883
|
+
request: { ...request, type },
|
|
42884
|
+
result,
|
|
42885
|
+
assertions: {
|
|
42886
|
+
mustContainAny: Array.isArray(body?.assertions?.mustContainAny) ? body.assertions.mustContainAny : [],
|
|
42887
|
+
mustNotContainAny: Array.isArray(body?.assertions?.mustNotContainAny) ? body.assertions.mustNotContainAny : [],
|
|
42888
|
+
mustMatchAny: Array.isArray(body?.assertions?.mustMatchAny) ? body.assertions.mustMatchAny : [],
|
|
42889
|
+
mustNotMatchAny: Array.isArray(body?.assertions?.mustNotMatchAny) ? body.assertions.mustNotMatchAny : [],
|
|
42890
|
+
lastAssistantMustContainAny: Array.isArray(body?.assertions?.lastAssistantMustContainAny) ? body.assertions.lastAssistantMustContainAny : [],
|
|
42891
|
+
lastAssistantMustNotContainAny: Array.isArray(body?.assertions?.lastAssistantMustNotContainAny) ? body.assertions.lastAssistantMustNotContainAny : [],
|
|
42892
|
+
lastAssistantMustMatchAny: Array.isArray(body?.assertions?.lastAssistantMustMatchAny) ? body.assertions.lastAssistantMustMatchAny : [],
|
|
42893
|
+
lastAssistantMustNotMatchAny: Array.isArray(body?.assertions?.lastAssistantMustNotMatchAny) ? body.assertions.lastAssistantMustNotMatchAny : [],
|
|
42894
|
+
statusesSeen: Array.isArray(body?.assertions?.statusesSeen) ? body.assertions.statusesSeen : void 0,
|
|
42895
|
+
requireNotTimedOut: body?.assertions?.requireNotTimedOut !== false
|
|
42896
|
+
},
|
|
42897
|
+
notes: typeof body?.notes === "string" ? body.notes : void 0
|
|
42898
|
+
};
|
|
42899
|
+
const filePath = path16.join(fixtureDir, `${name}.json`);
|
|
42900
|
+
fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
|
|
42901
|
+
ctx.json(res, 200, {
|
|
42902
|
+
saved: true,
|
|
42903
|
+
name,
|
|
42904
|
+
path: filePath,
|
|
42905
|
+
fixture,
|
|
42906
|
+
verification: {
|
|
42907
|
+
pass: validateCliFixtureResult(result, fixture.assertions).length === 0,
|
|
42908
|
+
failures: validateCliFixtureResult(result, fixture.assertions)
|
|
42909
|
+
}
|
|
42910
|
+
});
|
|
42911
|
+
} catch (e) {
|
|
42912
|
+
ctx.json(res, 500, { error: `Fixture capture failed: ${e.message}` });
|
|
42913
|
+
}
|
|
42914
|
+
}
|
|
42915
|
+
async function handleCliFixtureList(ctx, type, _req, res) {
|
|
42916
|
+
try {
|
|
42917
|
+
const fixtureDir = getCliFixtureDir(ctx, type);
|
|
42918
|
+
if (!fs12.existsSync(fixtureDir)) {
|
|
42919
|
+
ctx.json(res, 200, { fixtures: [], count: 0 });
|
|
42920
|
+
return;
|
|
42921
|
+
}
|
|
42922
|
+
const fixtures = fs12.readdirSync(fixtureDir).filter((file2) => file2.endsWith(".json")).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file2) => {
|
|
42923
|
+
const fullPath = path16.join(fixtureDir, file2);
|
|
42924
|
+
try {
|
|
42925
|
+
const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
|
|
42926
|
+
return {
|
|
42927
|
+
name: raw.name || file2.replace(/\.json$/i, ""),
|
|
42928
|
+
path: fullPath,
|
|
42929
|
+
createdAt: raw.createdAt || null,
|
|
42930
|
+
notes: raw.notes || null,
|
|
42931
|
+
requestText: raw.request?.text || "",
|
|
42932
|
+
assertions: raw.assertions || {}
|
|
42933
|
+
};
|
|
42934
|
+
} catch {
|
|
42935
|
+
return {
|
|
42936
|
+
name: file2.replace(/\.json$/i, ""),
|
|
42937
|
+
path: fullPath,
|
|
42938
|
+
createdAt: null,
|
|
42939
|
+
notes: "Unreadable fixture",
|
|
42940
|
+
requestText: "",
|
|
42941
|
+
assertions: {}
|
|
42942
|
+
};
|
|
42943
|
+
}
|
|
42944
|
+
});
|
|
42945
|
+
ctx.json(res, 200, { fixtures, count: fixtures.length });
|
|
42946
|
+
} catch (e) {
|
|
42947
|
+
ctx.json(res, 500, { error: `Fixture list failed: ${e.message}` });
|
|
42948
|
+
}
|
|
42949
|
+
}
|
|
42950
|
+
async function handleCliFixtureReplay(ctx, req, res) {
|
|
42951
|
+
try {
|
|
42952
|
+
const body = await ctx.readBody(req);
|
|
42953
|
+
const type = String(body?.type || "");
|
|
42954
|
+
const rawName = String(body?.name || "").trim();
|
|
42955
|
+
if (!type || !rawName) {
|
|
42956
|
+
ctx.json(res, 400, { error: "type and name required" });
|
|
42957
|
+
return;
|
|
42958
|
+
}
|
|
42959
|
+
const name = slugifyFixtureName(rawName);
|
|
42960
|
+
const fixture = readCliFixture(ctx, type, name);
|
|
42961
|
+
const result = await runCliExerciseInternal(ctx, {
|
|
42962
|
+
...fixture.request,
|
|
42963
|
+
type
|
|
42964
|
+
});
|
|
42965
|
+
const assertions = {
|
|
42966
|
+
...fixture.assertions,
|
|
42967
|
+
...body?.assertions || {}
|
|
42968
|
+
};
|
|
42969
|
+
const failures = validateCliFixtureResult(result, assertions);
|
|
42970
|
+
ctx.json(res, 200, {
|
|
42971
|
+
replayed: true,
|
|
42972
|
+
pass: failures.length === 0,
|
|
42973
|
+
failures,
|
|
42974
|
+
fixture,
|
|
42975
|
+
result,
|
|
42976
|
+
assertions
|
|
42977
|
+
});
|
|
42978
|
+
} catch (e) {
|
|
42979
|
+
ctx.json(res, 500, { error: `Fixture replay failed: ${e.message}` });
|
|
42980
|
+
}
|
|
42981
|
+
}
|
|
41853
42982
|
async function handleCliResolve(ctx, req, res) {
|
|
41854
42983
|
const body = await ctx.readBody(req);
|
|
41855
42984
|
const { type, buttonIndex, instanceId } = body;
|
|
@@ -41924,9 +43053,29 @@ async (params) => {
|
|
|
41924
43053
|
ctx.json(res, 500, { error: `Raw send failed: ${e.message}` });
|
|
41925
43054
|
}
|
|
41926
43055
|
}
|
|
41927
|
-
var
|
|
41928
|
-
var
|
|
43056
|
+
var fs13 = __toESM2(require("fs"));
|
|
43057
|
+
var path17 = __toESM2(require("path"));
|
|
41929
43058
|
var os17 = __toESM2(require("os"));
|
|
43059
|
+
function getAutoImplPid(ctx) {
|
|
43060
|
+
const proc = ctx.autoImplProcess;
|
|
43061
|
+
return proc && typeof proc.pid === "number" && proc.pid > 0 ? proc.pid : null;
|
|
43062
|
+
}
|
|
43063
|
+
function isPidAlive(pid) {
|
|
43064
|
+
try {
|
|
43065
|
+
process.kill(pid, 0);
|
|
43066
|
+
return true;
|
|
43067
|
+
} catch (error48) {
|
|
43068
|
+
return error48?.code === "EPERM";
|
|
43069
|
+
}
|
|
43070
|
+
}
|
|
43071
|
+
function clearStaleAutoImplState(ctx, reason) {
|
|
43072
|
+
if (!ctx.autoImplStatus.running && !ctx.autoImplProcess) return;
|
|
43073
|
+
const pid = getAutoImplPid(ctx);
|
|
43074
|
+
if (pid && isPidAlive(pid)) return;
|
|
43075
|
+
ctx.log(`Clearing stale auto-implement state: ${reason}${pid ? ` (pid ${pid})` : ""}`);
|
|
43076
|
+
ctx.autoImplProcess = null;
|
|
43077
|
+
ctx.autoImplStatus.running = false;
|
|
43078
|
+
}
|
|
41930
43079
|
function getDefaultAutoImplReference(ctx, category, type) {
|
|
41931
43080
|
if (category === "cli") {
|
|
41932
43081
|
return type === "codex-cli" ? "claude-cli" : "codex-cli";
|
|
@@ -41942,45 +43091,45 @@ async (params) => {
|
|
|
41942
43091
|
return fallback?.type || null;
|
|
41943
43092
|
}
|
|
41944
43093
|
function getLatestScriptVersionDir(scriptsDir) {
|
|
41945
|
-
if (!
|
|
41946
|
-
const versions =
|
|
43094
|
+
if (!fs13.existsSync(scriptsDir)) return null;
|
|
43095
|
+
const versions = fs13.readdirSync(scriptsDir).filter((d) => {
|
|
41947
43096
|
try {
|
|
41948
|
-
return
|
|
43097
|
+
return fs13.statSync(path17.join(scriptsDir, d)).isDirectory();
|
|
41949
43098
|
} catch {
|
|
41950
43099
|
return false;
|
|
41951
43100
|
}
|
|
41952
43101
|
}).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
41953
43102
|
if (versions.length === 0) return null;
|
|
41954
|
-
return
|
|
43103
|
+
return path17.join(scriptsDir, versions[0]);
|
|
41955
43104
|
}
|
|
41956
43105
|
function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
|
|
41957
|
-
const canonicalUserDir =
|
|
41958
|
-
const desiredDir = requestedDir ?
|
|
41959
|
-
const upstreamRoot =
|
|
41960
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
43106
|
+
const canonicalUserDir = path17.resolve(ctx.providerLoader.getUserProviderDir(category, type));
|
|
43107
|
+
const desiredDir = requestedDir ? path17.resolve(requestedDir) : canonicalUserDir;
|
|
43108
|
+
const upstreamRoot = path17.resolve(ctx.providerLoader.getUpstreamDir());
|
|
43109
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path17.sep}`)) {
|
|
41961
43110
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
41962
43111
|
}
|
|
41963
|
-
if (
|
|
43112
|
+
if (path17.basename(desiredDir) !== type) {
|
|
41964
43113
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
41965
43114
|
}
|
|
41966
43115
|
const sourceDir = ctx.findProviderDir(type);
|
|
41967
43116
|
if (!sourceDir) {
|
|
41968
43117
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
41969
43118
|
}
|
|
41970
|
-
if (!
|
|
41971
|
-
|
|
41972
|
-
|
|
43119
|
+
if (!fs13.existsSync(desiredDir)) {
|
|
43120
|
+
fs13.mkdirSync(path17.dirname(desiredDir), { recursive: true });
|
|
43121
|
+
fs13.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
41973
43122
|
ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
41974
43123
|
}
|
|
41975
|
-
const providerJson =
|
|
41976
|
-
if (!
|
|
43124
|
+
const providerJson = path17.join(desiredDir, "provider.json");
|
|
43125
|
+
if (!fs13.existsSync(providerJson)) {
|
|
41977
43126
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
41978
43127
|
}
|
|
41979
43128
|
try {
|
|
41980
|
-
const providerData = JSON.parse(
|
|
43129
|
+
const providerData = JSON.parse(fs13.readFileSync(providerJson, "utf-8"));
|
|
41981
43130
|
if (providerData.disableUpstream !== true) {
|
|
41982
43131
|
providerData.disableUpstream = true;
|
|
41983
|
-
|
|
43132
|
+
fs13.writeFileSync(providerJson, JSON.stringify(providerData, null, 2));
|
|
41984
43133
|
}
|
|
41985
43134
|
} catch (error48) {
|
|
41986
43135
|
return {
|
|
@@ -41993,15 +43142,15 @@ async (params) => {
|
|
|
41993
43142
|
function loadAutoImplReferenceScripts(ctx, referenceType) {
|
|
41994
43143
|
if (!referenceType) return {};
|
|
41995
43144
|
const refDir = ctx.findProviderDir(referenceType);
|
|
41996
|
-
if (!refDir || !
|
|
43145
|
+
if (!refDir || !fs13.existsSync(refDir)) return {};
|
|
41997
43146
|
const referenceScripts = {};
|
|
41998
|
-
const scriptsDir =
|
|
43147
|
+
const scriptsDir = path17.join(refDir, "scripts");
|
|
41999
43148
|
const latestDir = getLatestScriptVersionDir(scriptsDir);
|
|
42000
43149
|
if (!latestDir) return referenceScripts;
|
|
42001
|
-
for (const file2 of
|
|
43150
|
+
for (const file2 of fs13.readdirSync(latestDir)) {
|
|
42002
43151
|
if (!file2.endsWith(".js")) continue;
|
|
42003
43152
|
try {
|
|
42004
|
-
referenceScripts[file2] =
|
|
43153
|
+
referenceScripts[file2] = fs13.readFileSync(path17.join(latestDir, file2), "utf-8");
|
|
42005
43154
|
} catch {
|
|
42006
43155
|
}
|
|
42007
43156
|
}
|
|
@@ -42009,11 +43158,20 @@ async (params) => {
|
|
|
42009
43158
|
}
|
|
42010
43159
|
async function handleAutoImplement(ctx, type, req, res) {
|
|
42011
43160
|
const body = await ctx.readBody(req);
|
|
42012
|
-
const {
|
|
43161
|
+
const {
|
|
43162
|
+
agent = "claude-cli",
|
|
43163
|
+
functions,
|
|
43164
|
+
reference,
|
|
43165
|
+
model,
|
|
43166
|
+
comment,
|
|
43167
|
+
providerDir: requestedProviderDir,
|
|
43168
|
+
verification
|
|
43169
|
+
} = body;
|
|
42013
43170
|
if (!functions || !Array.isArray(functions) || functions.length === 0) {
|
|
42014
43171
|
ctx.json(res, 400, { error: 'functions[] is required (e.g. ["readChat", "sendMessage"])' });
|
|
42015
43172
|
return;
|
|
42016
43173
|
}
|
|
43174
|
+
clearStaleAutoImplState(ctx, "new auto-implement request");
|
|
42017
43175
|
if (ctx.autoImplStatus.running) {
|
|
42018
43176
|
ctx.json(res, 409, { error: "Auto-implement already in progress", type: ctx.autoImplStatus.type });
|
|
42019
43177
|
return;
|
|
@@ -42031,7 +43189,55 @@ async (params) => {
|
|
|
42031
43189
|
return;
|
|
42032
43190
|
}
|
|
42033
43191
|
const providerDir = writableProvider.dir;
|
|
43192
|
+
ctx.autoImplStatus = { running: false, type, progress: [] };
|
|
43193
|
+
if (provider.category === "cli" && verification && (verification.fixtureName || verification.fixtureNames && verification.fixtureNames.length > 0)) {
|
|
43194
|
+
sendAutoImplSSE(ctx, {
|
|
43195
|
+
event: "progress",
|
|
43196
|
+
data: {
|
|
43197
|
+
function: "_preflight",
|
|
43198
|
+
status: "verifying",
|
|
43199
|
+
message: "Running preflight verification before spawning agent..."
|
|
43200
|
+
}
|
|
43201
|
+
});
|
|
43202
|
+
try {
|
|
43203
|
+
const preflight = await runCliAutoImplVerification(ctx, type, verification);
|
|
43204
|
+
sendAutoImplSSE(ctx, { event: "verification", data: preflight });
|
|
43205
|
+
if (preflight.pass) {
|
|
43206
|
+
sendAutoImplSSE(ctx, {
|
|
43207
|
+
event: "complete",
|
|
43208
|
+
data: {
|
|
43209
|
+
success: true,
|
|
43210
|
+
exitCode: 0,
|
|
43211
|
+
functions,
|
|
43212
|
+
message: `\u2705 No-op: exact ${preflight.mode} already passes`,
|
|
43213
|
+
verification: preflight,
|
|
43214
|
+
skipped: true
|
|
43215
|
+
}
|
|
43216
|
+
});
|
|
43217
|
+
ctx.json(res, 200, {
|
|
43218
|
+
started: false,
|
|
43219
|
+
skipped: true,
|
|
43220
|
+
type,
|
|
43221
|
+
functions,
|
|
43222
|
+
providerDir,
|
|
43223
|
+
verification: preflight,
|
|
43224
|
+
message: "Preflight verification already passes. No auto-implement run needed."
|
|
43225
|
+
});
|
|
43226
|
+
return;
|
|
43227
|
+
}
|
|
43228
|
+
} catch (error48) {
|
|
43229
|
+
sendAutoImplSSE(ctx, {
|
|
43230
|
+
event: "progress",
|
|
43231
|
+
data: {
|
|
43232
|
+
function: "_preflight",
|
|
43233
|
+
status: "verify_failed",
|
|
43234
|
+
message: `Preflight verification errored, continuing to agent run: ${error48?.message || error48}`
|
|
43235
|
+
}
|
|
43236
|
+
});
|
|
43237
|
+
}
|
|
43238
|
+
}
|
|
42034
43239
|
try {
|
|
43240
|
+
ctx.autoImplStatus = { running: true, type, progress: ctx.autoImplStatus.progress };
|
|
42035
43241
|
const resolvedReference = resolveAutoImplReference(ctx, provider.category, reference, type);
|
|
42036
43242
|
sendAutoImplSSE(ctx, {
|
|
42037
43243
|
event: "progress",
|
|
@@ -42051,17 +43257,17 @@ async (params) => {
|
|
|
42051
43257
|
}
|
|
42052
43258
|
});
|
|
42053
43259
|
const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
|
|
42054
|
-
const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference);
|
|
42055
|
-
const tmpDir =
|
|
42056
|
-
if (!
|
|
42057
|
-
const promptFile =
|
|
42058
|
-
|
|
43260
|
+
const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
|
|
43261
|
+
const tmpDir = path17.join(os17.tmpdir(), "adhdev-autoimpl");
|
|
43262
|
+
if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
|
|
43263
|
+
const promptFile = path17.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
|
|
43264
|
+
fs13.writeFileSync(promptFile, prompt, "utf-8");
|
|
42059
43265
|
ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
|
|
42060
43266
|
const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
|
|
42061
43267
|
const spawn4 = agentProvider?.spawn;
|
|
42062
43268
|
if (!spawn4?.command) {
|
|
42063
43269
|
try {
|
|
42064
|
-
|
|
43270
|
+
fs13.unlinkSync(promptFile);
|
|
42065
43271
|
} catch {
|
|
42066
43272
|
}
|
|
42067
43273
|
ctx.json(res, 400, { error: `Agent '${agent}' has no spawn config. Select a CLI provider with a spawn configuration.` });
|
|
@@ -42070,7 +43276,8 @@ async (params) => {
|
|
|
42070
43276
|
const agentCategory = agentProvider?.category;
|
|
42071
43277
|
if (agentCategory === "acp") {
|
|
42072
43278
|
sendAutoImplSSE(ctx, { event: "progress", data: { function: "_init", status: "spawning", message: `Spawning ACP agent: ${spawn4.command} ${(spawn4.args || []).join(" ")}` } });
|
|
42073
|
-
ctx.autoImplStatus =
|
|
43279
|
+
ctx.autoImplStatus.running = true;
|
|
43280
|
+
ctx.autoImplStatus.type = type;
|
|
42074
43281
|
const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
|
|
42075
43282
|
const { Readable: Readable2, Writable: Writable2 } = await import("stream");
|
|
42076
43283
|
const { spawn: spawnFn2 } = await import("child_process");
|
|
@@ -42162,7 +43369,7 @@ async (params) => {
|
|
|
42162
43369
|
} catch {
|
|
42163
43370
|
}
|
|
42164
43371
|
try {
|
|
42165
|
-
|
|
43372
|
+
fs13.unlinkSync(promptFile);
|
|
42166
43373
|
} catch {
|
|
42167
43374
|
}
|
|
42168
43375
|
ctx.log(`Auto-implement (ACP) ${success2 ? "completed" : "failed"}: ${type} (exit: ${code})`);
|
|
@@ -42240,7 +43447,8 @@ async (params) => {
|
|
|
42240
43447
|
}
|
|
42241
43448
|
}
|
|
42242
43449
|
sendAutoImplSSE(ctx, { event: "progress", data: { function: "_init", status: "spawning", message: `Spawning agent: ${shellCmd.substring(0, 200)}... (prompt: ${prompt.length} chars)` } });
|
|
42243
|
-
ctx.autoImplStatus =
|
|
43450
|
+
ctx.autoImplStatus.running = true;
|
|
43451
|
+
ctx.autoImplStatus.type = type;
|
|
42244
43452
|
const spawnedAt = Date.now();
|
|
42245
43453
|
let child;
|
|
42246
43454
|
let isPty = false;
|
|
@@ -42283,6 +43491,7 @@ async (params) => {
|
|
|
42283
43491
|
let approvalKeys = { 0: "y\r" };
|
|
42284
43492
|
let approvalBuffer = "";
|
|
42285
43493
|
let lastApprovalTime = 0;
|
|
43494
|
+
let completionSignalSeen = false;
|
|
42286
43495
|
try {
|
|
42287
43496
|
const { normalizeCliProviderForRuntime: normalizeCliProviderForRuntime2 } = await Promise.resolve().then(() => (init_provider_cli_adapter(), provider_cli_adapter_exports));
|
|
42288
43497
|
const normalized = normalizeCliProviderForRuntime2(agentProvider);
|
|
@@ -42296,6 +43505,7 @@ async (params) => {
|
|
|
42296
43505
|
approvalBuffer = (approvalBuffer + cleanData).slice(-1500);
|
|
42297
43506
|
const elapsed = Date.now() - spawnedAt;
|
|
42298
43507
|
if (elapsed > 15e3 && cleanData.includes("_PIPELINE_COMPLETE_SIGNAL_")) {
|
|
43508
|
+
completionSignalSeen = true;
|
|
42299
43509
|
ctx.log(`Agent finished task after ${Math.round(elapsed / 1e3)}s. Terminating interactive CLI session to unblock pipeline.`);
|
|
42300
43510
|
sendAutoImplSSE(ctx, { event: "output", data: { chunk: `
|
|
42301
43511
|
[\u{1F916} ADHDev Pipeline] Completion token detected. Proceeding...
|
|
@@ -42319,6 +43529,55 @@ async (params) => {
|
|
|
42319
43529
|
lastApprovalTime = Date.now();
|
|
42320
43530
|
}
|
|
42321
43531
|
};
|
|
43532
|
+
const finalizeCliAutoImpl = async (code) => {
|
|
43533
|
+
ctx.autoImplProcess = null;
|
|
43534
|
+
let success2 = completionSignalSeen || code === 0;
|
|
43535
|
+
let message = success2 ? completionSignalSeen && code !== 0 ? "\u2705 Auto-implement complete (completion signal)" : "\u2705 Auto-implement complete" : `\u274C Agent exited (code: ${code})`;
|
|
43536
|
+
let verificationSummary = null;
|
|
43537
|
+
try {
|
|
43538
|
+
ctx.providerLoader.reload();
|
|
43539
|
+
} catch {
|
|
43540
|
+
}
|
|
43541
|
+
if (provider.category === "cli" && verification) {
|
|
43542
|
+
sendAutoImplSSE(ctx, {
|
|
43543
|
+
event: "progress",
|
|
43544
|
+
data: {
|
|
43545
|
+
function: "_verify",
|
|
43546
|
+
status: "running",
|
|
43547
|
+
message: "Running exact post-patch verification..."
|
|
43548
|
+
}
|
|
43549
|
+
});
|
|
43550
|
+
try {
|
|
43551
|
+
verificationSummary = await runCliAutoImplVerification(ctx, type, verification);
|
|
43552
|
+
sendAutoImplSSE(ctx, { event: "verification", data: verificationSummary });
|
|
43553
|
+
success2 = verificationSummary.pass;
|
|
43554
|
+
message = verificationSummary.pass ? `\u2705 Auto-implement complete (${verificationSummary.mode})` : `\u274C Post-patch verification failed (${verificationSummary.mode}): ${verificationSummary.failures.join("; ") || "unknown failure"}`;
|
|
43555
|
+
} catch (error48) {
|
|
43556
|
+
success2 = false;
|
|
43557
|
+
message = `\u274C Post-patch verification error: ${error48?.message || error48}`;
|
|
43558
|
+
sendAutoImplSSE(ctx, {
|
|
43559
|
+
event: "verification",
|
|
43560
|
+
data: { pass: false, error: error48?.message || String(error48) }
|
|
43561
|
+
});
|
|
43562
|
+
}
|
|
43563
|
+
}
|
|
43564
|
+
ctx.autoImplStatus.running = false;
|
|
43565
|
+
sendAutoImplSSE(ctx, {
|
|
43566
|
+
event: "complete",
|
|
43567
|
+
data: {
|
|
43568
|
+
success: success2,
|
|
43569
|
+
exitCode: code,
|
|
43570
|
+
functions,
|
|
43571
|
+
message,
|
|
43572
|
+
verification: verificationSummary
|
|
43573
|
+
}
|
|
43574
|
+
});
|
|
43575
|
+
try {
|
|
43576
|
+
fs13.unlinkSync(promptFile);
|
|
43577
|
+
} catch {
|
|
43578
|
+
}
|
|
43579
|
+
ctx.log(`Auto-implement ${success2 ? "completed" : "failed"}: ${type} (exit: ${code})${verificationSummary ? ` verify=${verificationSummary.pass ? "pass" : "fail"}` : ""}`);
|
|
43580
|
+
};
|
|
42322
43581
|
if (isPty) {
|
|
42323
43582
|
child.onData((data) => {
|
|
42324
43583
|
stdout += data;
|
|
@@ -42330,21 +43589,7 @@ async (params) => {
|
|
|
42330
43589
|
sendAutoImplSSE(ctx, { event: "output", data: { chunk: data, stream: "stdout" } });
|
|
42331
43590
|
});
|
|
42332
43591
|
child.onExit(({ exitCode: code }) => {
|
|
42333
|
-
|
|
42334
|
-
ctx.autoImplStatus.running = false;
|
|
42335
|
-
const success2 = code === 0;
|
|
42336
|
-
sendAutoImplSSE(ctx, {
|
|
42337
|
-
event: "complete",
|
|
42338
|
-
data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 Auto-implement complete" : `\u274C Agent exited (code: ${code})` }
|
|
42339
|
-
});
|
|
42340
|
-
try {
|
|
42341
|
-
ctx.providerLoader.reload();
|
|
42342
|
-
} catch {
|
|
42343
|
-
}
|
|
42344
|
-
try {
|
|
42345
|
-
fs12.unlinkSync(promptFile);
|
|
42346
|
-
} catch {
|
|
42347
|
-
}
|
|
43592
|
+
void finalizeCliAutoImpl(code);
|
|
42348
43593
|
});
|
|
42349
43594
|
} else {
|
|
42350
43595
|
child.stdout?.on("data", (d) => {
|
|
@@ -42361,27 +43606,7 @@ async (params) => {
|
|
|
42361
43606
|
sendAutoImplSSE(ctx, { event: "output", data: { chunk, stream: "stderr" } });
|
|
42362
43607
|
});
|
|
42363
43608
|
child.on("exit", (code) => {
|
|
42364
|
-
|
|
42365
|
-
ctx.autoImplStatus.running = false;
|
|
42366
|
-
const success2 = code === 0;
|
|
42367
|
-
sendAutoImplSSE(ctx, {
|
|
42368
|
-
event: "complete",
|
|
42369
|
-
data: {
|
|
42370
|
-
success: success2,
|
|
42371
|
-
exitCode: code,
|
|
42372
|
-
functions,
|
|
42373
|
-
message: success2 ? "\u2705 Auto-implement complete" : `\u274C Agent exited (code: ${code})`
|
|
42374
|
-
}
|
|
42375
|
-
});
|
|
42376
|
-
try {
|
|
42377
|
-
ctx.providerLoader.reload();
|
|
42378
|
-
} catch {
|
|
42379
|
-
}
|
|
42380
|
-
try {
|
|
42381
|
-
fs12.unlinkSync(promptFile);
|
|
42382
|
-
} catch {
|
|
42383
|
-
}
|
|
42384
|
-
ctx.log(`Auto-implement ${success2 ? "completed" : "failed"}: ${type} (exit: ${code})`);
|
|
43609
|
+
void finalizeCliAutoImpl(code);
|
|
42385
43610
|
});
|
|
42386
43611
|
}
|
|
42387
43612
|
ctx.json(res, 202, {
|
|
@@ -42398,9 +43623,9 @@ async (params) => {
|
|
|
42398
43623
|
ctx.json(res, 500, { error: `Auto-implement failed: ${e.message}` });
|
|
42399
43624
|
}
|
|
42400
43625
|
}
|
|
42401
|
-
function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, userComment, referenceType) {
|
|
43626
|
+
function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, userComment, referenceType, verification) {
|
|
42402
43627
|
if (provider.category === "cli") {
|
|
42403
|
-
return buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, referenceScripts, userComment, referenceType);
|
|
43628
|
+
return buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, referenceScripts, userComment, referenceType, verification);
|
|
42404
43629
|
}
|
|
42405
43630
|
const lines = [];
|
|
42406
43631
|
lines.push("You are implementing browser automation scripts for an IDE provider.");
|
|
@@ -42425,7 +43650,7 @@ async (params) => {
|
|
|
42425
43650
|
setMode: "set_mode.js"
|
|
42426
43651
|
};
|
|
42427
43652
|
const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
|
|
42428
|
-
const scriptsDir =
|
|
43653
|
+
const scriptsDir = path17.join(providerDir, "scripts");
|
|
42429
43654
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
42430
43655
|
if (latestScriptsDir) {
|
|
42431
43656
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -42433,10 +43658,10 @@ async (params) => {
|
|
|
42433
43658
|
lines.push("## \u270F\uFE0F Target Files (EDIT THESE)");
|
|
42434
43659
|
lines.push("These are the ONLY files you are allowed to modify. Replace the TODO stubs with working implementations.");
|
|
42435
43660
|
lines.push("");
|
|
42436
|
-
for (const file2 of
|
|
43661
|
+
for (const file2 of fs13.readdirSync(latestScriptsDir)) {
|
|
42437
43662
|
if (file2.endsWith(".js") && targetFileNames.has(file2)) {
|
|
42438
43663
|
try {
|
|
42439
|
-
const content =
|
|
43664
|
+
const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
|
|
42440
43665
|
lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
|
|
42441
43666
|
lines.push("```javascript");
|
|
42442
43667
|
lines.push(content);
|
|
@@ -42446,14 +43671,14 @@ async (params) => {
|
|
|
42446
43671
|
}
|
|
42447
43672
|
}
|
|
42448
43673
|
}
|
|
42449
|
-
const refFiles =
|
|
43674
|
+
const refFiles = fs13.readdirSync(latestScriptsDir).filter((f) => f.endsWith(".js") && !targetFileNames.has(f));
|
|
42450
43675
|
if (refFiles.length > 0) {
|
|
42451
43676
|
lines.push("## \u{1F512} Other Scripts (REFERENCE ONLY \u2014 DO NOT EDIT)");
|
|
42452
43677
|
lines.push("These files are shown for context only. Do NOT modify them under any circumstances.");
|
|
42453
43678
|
lines.push("");
|
|
42454
43679
|
for (const file2 of refFiles) {
|
|
42455
43680
|
try {
|
|
42456
|
-
const content =
|
|
43681
|
+
const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
|
|
42457
43682
|
lines.push(`### \`${file2}\` \u{1F512}`);
|
|
42458
43683
|
lines.push("```javascript");
|
|
42459
43684
|
lines.push(content);
|
|
@@ -42494,11 +43719,11 @@ async (params) => {
|
|
|
42494
43719
|
lines.push("");
|
|
42495
43720
|
}
|
|
42496
43721
|
}
|
|
42497
|
-
const docsDir =
|
|
43722
|
+
const docsDir = path17.join(providerDir, "../../docs");
|
|
42498
43723
|
const loadGuide = (name) => {
|
|
42499
43724
|
try {
|
|
42500
|
-
const p =
|
|
42501
|
-
if (
|
|
43725
|
+
const p = path17.join(docsDir, name);
|
|
43726
|
+
if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
|
|
42502
43727
|
} catch {
|
|
42503
43728
|
}
|
|
42504
43729
|
return null;
|
|
@@ -42656,8 +43881,69 @@ async (params) => {
|
|
|
42656
43881
|
lines.push("Start NOW. Do not ask for permission. Explore the DOM -> Code -> Test.");
|
|
42657
43882
|
return lines.join("\n");
|
|
42658
43883
|
}
|
|
42659
|
-
function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, referenceScripts, userComment, referenceType) {
|
|
43884
|
+
function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, referenceScripts, userComment, referenceType, verification) {
|
|
42660
43885
|
const lines = [];
|
|
43886
|
+
const defaultExercisePayload = {
|
|
43887
|
+
type,
|
|
43888
|
+
workingDir: providerDir,
|
|
43889
|
+
freshSession: true,
|
|
43890
|
+
autoLaunch: true,
|
|
43891
|
+
autoResolveApprovals: true,
|
|
43892
|
+
approvalButtonIndex: 0,
|
|
43893
|
+
timeoutMs: 45e3,
|
|
43894
|
+
traceLimit: 200,
|
|
43895
|
+
text: "Create a file at tmp/adhdev_provider_fix_test.py that prints the current working directory and the squares of 1 through 5, then run python3 tmp/adhdev_provider_fix_test.py and tell me the exact output."
|
|
43896
|
+
};
|
|
43897
|
+
const exercisePayload = {
|
|
43898
|
+
...defaultExercisePayload,
|
|
43899
|
+
...verification?.request || {},
|
|
43900
|
+
type,
|
|
43901
|
+
workingDir: providerDir
|
|
43902
|
+
};
|
|
43903
|
+
const exerciseJson = JSON.stringify(exercisePayload).replace(/\\/g, "\\\\").replace(/'/g, `'\\''`);
|
|
43904
|
+
const verificationInspectFields = verification?.inspectFields?.length ? verification.inspectFields : [
|
|
43905
|
+
"debug.messages",
|
|
43906
|
+
"trace.entries[].payload.parsedLastAssistant",
|
|
43907
|
+
"trace.entries[].payload.lastAssistant"
|
|
43908
|
+
];
|
|
43909
|
+
const verificationMustContainAny = verification?.mustContainAny || [];
|
|
43910
|
+
const verificationMustNotContainAny = verification?.mustNotContainAny || [];
|
|
43911
|
+
const verificationMustMatchAny = verification?.mustMatchAny || [];
|
|
43912
|
+
const verificationMustNotMatchAny = verification?.mustNotMatchAny || [];
|
|
43913
|
+
const verificationLastAssistantMustContainAny = verification?.lastAssistantMustContainAny || [];
|
|
43914
|
+
const verificationLastAssistantMustNotContainAny = verification?.lastAssistantMustNotContainAny || [];
|
|
43915
|
+
const verificationLastAssistantMustMatchAny = verification?.lastAssistantMustMatchAny || [];
|
|
43916
|
+
const verificationLastAssistantMustNotMatchAny = verification?.lastAssistantMustNotMatchAny || [];
|
|
43917
|
+
const quotedMustContain = verificationMustContainAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43918
|
+
const quotedMustNotContain = verificationMustNotContainAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43919
|
+
const quotedMustMatch = verificationMustMatchAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43920
|
+
const quotedMustNotMatch = verificationMustNotMatchAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43921
|
+
const quotedLastAssistantMustContain = verificationLastAssistantMustContainAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43922
|
+
const quotedLastAssistantMustNotContain = verificationLastAssistantMustNotContainAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43923
|
+
const quotedLastAssistantMustMatch = verificationLastAssistantMustMatchAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43924
|
+
const quotedLastAssistantMustNotMatch = verificationLastAssistantMustNotMatchAny.map((value) => JSON.stringify(value)).join(", ");
|
|
43925
|
+
const fixtureName = verification?.fixtureName || `${type}-provider-fix`;
|
|
43926
|
+
const fixtureNames = Array.isArray(verification?.fixtureNames) ? verification.fixtureNames.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
43927
|
+
const fixtureCaptureJson = JSON.stringify({
|
|
43928
|
+
type,
|
|
43929
|
+
name: fixtureName,
|
|
43930
|
+
request: exercisePayload,
|
|
43931
|
+
assertions: {
|
|
43932
|
+
mustContainAny: verificationMustContainAny,
|
|
43933
|
+
mustNotContainAny: verificationMustNotContainAny,
|
|
43934
|
+
mustMatchAny: verificationMustMatchAny,
|
|
43935
|
+
mustNotMatchAny: verificationMustNotMatchAny,
|
|
43936
|
+
lastAssistantMustContainAny: verificationLastAssistantMustContainAny,
|
|
43937
|
+
lastAssistantMustNotContainAny: verificationLastAssistantMustNotContainAny,
|
|
43938
|
+
lastAssistantMustMatchAny: verificationLastAssistantMustMatchAny,
|
|
43939
|
+
lastAssistantMustNotMatchAny: verificationLastAssistantMustNotMatchAny,
|
|
43940
|
+
requireNotTimedOut: true
|
|
43941
|
+
}
|
|
43942
|
+
}).replace(/\\/g, "\\\\").replace(/'/g, `'\\''`);
|
|
43943
|
+
const fixtureReplayJson = JSON.stringify({
|
|
43944
|
+
type,
|
|
43945
|
+
name: fixtureName
|
|
43946
|
+
}).replace(/\\/g, "\\\\").replace(/'/g, `'\\''`);
|
|
42661
43947
|
lines.push("You are implementing PTY parsing scripts for a CLI provider.");
|
|
42662
43948
|
lines.push("Be concise. Do NOT explain your reasoning. Edit files directly and verify with the local DevServer.");
|
|
42663
43949
|
lines.push("");
|
|
@@ -42671,7 +43957,7 @@ async (params) => {
|
|
|
42671
43957
|
parseApproval: "parse_approval.js"
|
|
42672
43958
|
};
|
|
42673
43959
|
const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
|
|
42674
|
-
const scriptsDir =
|
|
43960
|
+
const scriptsDir = path17.join(providerDir, "scripts");
|
|
42675
43961
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
42676
43962
|
if (latestScriptsDir) {
|
|
42677
43963
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -42679,11 +43965,11 @@ async (params) => {
|
|
|
42679
43965
|
lines.push("## \u270F\uFE0F Target Files (EDIT THESE)");
|
|
42680
43966
|
lines.push("These are the ONLY files you are allowed to modify. Replace TODO or heuristic-only logic with working PTY-aware implementations.");
|
|
42681
43967
|
lines.push("");
|
|
42682
|
-
for (const file2 of
|
|
43968
|
+
for (const file2 of fs13.readdirSync(latestScriptsDir)) {
|
|
42683
43969
|
if (!file2.endsWith(".js")) continue;
|
|
42684
43970
|
if (!targetFileNames.has(file2)) continue;
|
|
42685
43971
|
try {
|
|
42686
|
-
const content =
|
|
43972
|
+
const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
|
|
42687
43973
|
lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
|
|
42688
43974
|
lines.push("```javascript");
|
|
42689
43975
|
lines.push(content);
|
|
@@ -42692,14 +43978,14 @@ async (params) => {
|
|
|
42692
43978
|
} catch {
|
|
42693
43979
|
}
|
|
42694
43980
|
}
|
|
42695
|
-
const refFiles =
|
|
43981
|
+
const refFiles = fs13.readdirSync(latestScriptsDir).filter((f) => f.endsWith(".js") && !targetFileNames.has(f));
|
|
42696
43982
|
if (refFiles.length > 0) {
|
|
42697
43983
|
lines.push("## \u{1F512} Other Scripts (REFERENCE ONLY \u2014 DO NOT EDIT)");
|
|
42698
43984
|
lines.push("These files are shown for context only. Do NOT modify them under any circumstances.");
|
|
42699
43985
|
lines.push("");
|
|
42700
43986
|
for (const file2 of refFiles) {
|
|
42701
43987
|
try {
|
|
42702
|
-
const content =
|
|
43988
|
+
const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
|
|
42703
43989
|
lines.push(`### \`${file2}\` \u{1F512}`);
|
|
42704
43990
|
lines.push("```javascript");
|
|
42705
43991
|
lines.push(content);
|
|
@@ -42732,17 +44018,17 @@ async (params) => {
|
|
|
42732
44018
|
lines.push("");
|
|
42733
44019
|
}
|
|
42734
44020
|
}
|
|
42735
|
-
const docsDir =
|
|
44021
|
+
const docsDir = path17.join(providerDir, "../../docs");
|
|
42736
44022
|
const loadGuide = (name) => {
|
|
42737
44023
|
try {
|
|
42738
|
-
const p =
|
|
42739
|
-
if (
|
|
44024
|
+
const p = path17.join(docsDir, name);
|
|
44025
|
+
if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
|
|
42740
44026
|
} catch {
|
|
42741
44027
|
}
|
|
42742
44028
|
return null;
|
|
42743
44029
|
};
|
|
42744
44030
|
const providerGuide = loadGuide("PROVIDER_GUIDE.md");
|
|
42745
|
-
if (providerGuide) {
|
|
44031
|
+
if (providerGuide && provider.category !== "cli") {
|
|
42746
44032
|
lines.push("## Documentation: PROVIDER_GUIDE.md");
|
|
42747
44033
|
lines.push("```markdown");
|
|
42748
44034
|
lines.push(providerGuide);
|
|
@@ -42784,6 +44070,11 @@ async (params) => {
|
|
|
42784
44070
|
lines.push("13. After the first successful live repro, stop broad diagnosis. Edit the scripts, reload, and verify. Do not burn tokens on repeated re-inspection without code changes.");
|
|
42785
44071
|
lines.push("14. If the visible current screen is clean and sufficient, do NOT fall back to complex buffer heuristics. Simpler current-screen parsing is preferred.");
|
|
42786
44072
|
lines.push("15. Before changing parser logic, verify whether `provider.json` submit/approval behavior (`sendDelayMs`, `approvalKeys`, submit strategy) is the simpler and more correct fix.");
|
|
44073
|
+
lines.push('16. Do NOT patch transcript bugs by piling up one-off literal string exceptions (`includes("foo")`, `=== "bar"`, ad hoc allowlists/denylists) for every observed variant. Model the UI as PATTERN FAMILIES using reusable regex classifiers and normalization first.');
|
|
44074
|
+
lines.push("17. If you find yourself adding a second or third near-duplicate literal check for spinner words, tool headers, approval prompts, footer chrome, or OSC residue, STOP and replace them with a broader regex or helper classifier.");
|
|
44075
|
+
lines.push('18. Prefer a small number of named classifiers such as "status line", "tool header", "tool detail", "footer chrome", "approval cue", "prompt line", and "OSC residue" over a long chain of unrelated string checks.');
|
|
44076
|
+
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.");
|
|
44077
|
+
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.");
|
|
42787
44078
|
lines.push("");
|
|
42788
44079
|
lines.push("## Task");
|
|
42789
44080
|
lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
|
|
@@ -42791,35 +44082,145 @@ async (params) => {
|
|
|
42791
44082
|
lines.push("## Verification API");
|
|
42792
44083
|
lines.push("Use the DevServer CLI debug endpoints, not DOM/CDP routes.");
|
|
42793
44084
|
lines.push("");
|
|
42794
|
-
lines.push("### 1.
|
|
44085
|
+
lines.push("### 1. Preferred: run a full autonomous repro");
|
|
44086
|
+
lines.push("Use the exercise endpoint first. It launches a fresh CLI session, sends the repro prompt, auto-resolves approvals, waits for the session to settle, and returns the final debug + trace payload in one response.");
|
|
42795
44087
|
lines.push("```bash");
|
|
42796
|
-
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/
|
|
44088
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/exercise \\`);
|
|
42797
44089
|
lines.push(' -H "Content-Type: application/json" \\');
|
|
42798
|
-
lines.push(` -d '
|
|
44090
|
+
lines.push(` -d '${exerciseJson}'`);
|
|
42799
44091
|
lines.push("```");
|
|
42800
44092
|
lines.push("");
|
|
44093
|
+
if (verification?.description) {
|
|
44094
|
+
lines.push("Verification intent:");
|
|
44095
|
+
lines.push(verification.description);
|
|
44096
|
+
lines.push("");
|
|
44097
|
+
}
|
|
44098
|
+
lines.push("Read the JSON response carefully. It already includes:");
|
|
44099
|
+
lines.push("1. `instanceId`");
|
|
44100
|
+
lines.push("2. `statusesSeen` and `approvalsResolved`");
|
|
44101
|
+
lines.push("3. `debug` for the final settled state");
|
|
44102
|
+
lines.push("4. `trace.entries` for the repro turn");
|
|
44103
|
+
lines.push("");
|
|
44104
|
+
lines.push("Save the response to a temp file and inspect the exact parsed transcript fields before editing:");
|
|
44105
|
+
lines.push("```bash");
|
|
44106
|
+
lines.push(`EXERCISE_JSON=$(mktemp)`);
|
|
44107
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/exercise \\`);
|
|
44108
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
44109
|
+
lines.push(` -d '${exerciseJson}' > "$EXERCISE_JSON"`);
|
|
44110
|
+
lines.push(`jq '{timedOut,statusesSeen,approvalsResolved,inspect:{${verificationInspectFields.map((field, index) => `f${index + 1}: .${field}`).join(", ")}}}' "$EXERCISE_JSON"`);
|
|
44111
|
+
lines.push("```");
|
|
44112
|
+
lines.push("");
|
|
44113
|
+
if (verificationMustContainAny.length > 0 || verificationMustNotContainAny.length > 0 || verificationMustMatchAny.length > 0 || verificationMustNotMatchAny.length > 0 || verificationLastAssistantMustContainAny.length > 0 || verificationLastAssistantMustNotContainAny.length > 0 || verificationLastAssistantMustMatchAny.length > 0 || verificationLastAssistantMustNotMatchAny.length > 0) {
|
|
44114
|
+
lines.push("The exact repro below is mandatory. Do NOT declare success unless these transcript assertions pass on the exercise JSON from the PATCHED provider.");
|
|
44115
|
+
lines.push("```bash");
|
|
44116
|
+
if (verificationMustContainAny.length > 0) {
|
|
44117
|
+
lines.push(`node -e 'const fs=require("fs");const text=fs.readFileSync(process.argv[1],"utf8");const required=[${quotedMustContain}];const missing=required.filter(v=>!text.includes(v));if(missing.length){console.error("Missing required substrings:\\n"+missing.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44118
|
+
}
|
|
44119
|
+
if (verificationMustNotContainAny.length > 0) {
|
|
44120
|
+
lines.push(`node -e 'const fs=require("fs");const text=fs.readFileSync(process.argv[1],"utf8");const banned=[${quotedMustNotContain}];const hits=banned.filter(v=>text.includes(v));if(hits.length){console.error("Found banned substrings:\\n"+hits.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44121
|
+
}
|
|
44122
|
+
if (verificationMustMatchAny.length > 0) {
|
|
44123
|
+
lines.push(`node -e 'const fs=require("fs");const text=fs.readFileSync(process.argv[1],"utf8");const required=[${quotedMustMatch}].map(v=>new RegExp(v,"m"));const missing=required.filter(v=>!v.test(text)).map(v=>String(v));if(missing.length){console.error("Missing required regex matches:\\n"+missing.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44124
|
+
}
|
|
44125
|
+
if (verificationMustNotMatchAny.length > 0) {
|
|
44126
|
+
lines.push(`node -e 'const fs=require("fs");const text=fs.readFileSync(process.argv[1],"utf8");const banned=[${quotedMustNotMatch}].map(v=>new RegExp(v,"m"));const hits=banned.filter(v=>v.test(text)).map(v=>String(v));if(hits.length){console.error("Found banned regex matches:\\n"+hits.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44127
|
+
}
|
|
44128
|
+
if (verificationLastAssistantMustContainAny.length > 0) {
|
|
44129
|
+
lines.push(`node -e 'const fs=require("fs");const payload=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));const text=String(payload.lastAssistant||"");const required=[${quotedLastAssistantMustContain}];const missing=required.filter(v=>!text.includes(v));if(missing.length){console.error("Missing required lastAssistant substrings:\\n"+missing.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44130
|
+
}
|
|
44131
|
+
if (verificationLastAssistantMustNotContainAny.length > 0) {
|
|
44132
|
+
lines.push(`node -e 'const fs=require("fs");const payload=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));const text=String(payload.lastAssistant||"");const banned=[${quotedLastAssistantMustNotContain}];const hits=banned.filter(v=>text.includes(v));if(hits.length){console.error("Found banned lastAssistant substrings:\\n"+hits.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44133
|
+
}
|
|
44134
|
+
if (verificationLastAssistantMustMatchAny.length > 0) {
|
|
44135
|
+
lines.push(`node -e 'const fs=require("fs");const payload=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));const text=String(payload.lastAssistant||"");const required=[${quotedLastAssistantMustMatch}].map(v=>new RegExp(v,"m"));const missing=required.filter(v=>!v.test(text)).map(v=>String(v));if(missing.length){console.error("Missing required lastAssistant regex matches:\\n"+missing.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44136
|
+
}
|
|
44137
|
+
if (verificationLastAssistantMustNotMatchAny.length > 0) {
|
|
44138
|
+
lines.push(`node -e 'const fs=require("fs");const payload=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));const text=String(payload.lastAssistant||"");const banned=[${quotedLastAssistantMustNotMatch}].map(v=>new RegExp(v,"m"));const hits=banned.filter(v=>v.test(text)).map(v=>String(v));if(hits.length){console.error("Found banned lastAssistant regex matches:\\n"+hits.join("\\n"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
44139
|
+
}
|
|
44140
|
+
lines.push("```");
|
|
44141
|
+
lines.push("");
|
|
44142
|
+
}
|
|
44143
|
+
lines.push("If you need a manual follow-up repro after patching, use the SAME endpoint again with the SAME prompt and compare the new trace to the previous one.");
|
|
44144
|
+
lines.push("");
|
|
44145
|
+
lines.push("### 1b. Persist or replay the exact repro as a reusable fixture");
|
|
44146
|
+
if (fixtureNames.length > 0) {
|
|
44147
|
+
lines.push(`Replay this exact fixture suite before editing, and replay the SAME suite again after patching. Do not declare success unless EVERY fixture passes: ${fixtureNames.map((name) => `\`${name}\``).join(", ")}.`);
|
|
44148
|
+
for (const name of fixtureNames) {
|
|
44149
|
+
const replayJson = JSON.stringify({ type, name }).replace(/\\/g, "\\\\").replace(/'/g, `'\\''`);
|
|
44150
|
+
lines.push("```bash");
|
|
44151
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/replay \\`);
|
|
44152
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
44153
|
+
lines.push(` -d '${replayJson}'`);
|
|
44154
|
+
lines.push("```");
|
|
44155
|
+
lines.push("");
|
|
44156
|
+
}
|
|
44157
|
+
lines.push("Do not create new fixtures unless one of the listed fixtures is missing or stale.");
|
|
44158
|
+
} else if (verification?.fixtureName) {
|
|
44159
|
+
lines.push(`Replay the EXISTING saved fixture \`${fixtureName}\` before editing, and replay the SAME fixture again after patching. Do not declare success unless that exact fixture passes.`);
|
|
44160
|
+
lines.push("```bash");
|
|
44161
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/replay \\`);
|
|
44162
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
44163
|
+
lines.push(` -d '${fixtureReplayJson}'`);
|
|
44164
|
+
lines.push("```");
|
|
44165
|
+
lines.push("");
|
|
44166
|
+
lines.push("Only if the named fixture is missing or outdated should you recapture it. Prefer replaying the existing failing fixture over creating a new one.");
|
|
44167
|
+
lines.push("```bash");
|
|
44168
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/capture \\`);
|
|
44169
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
44170
|
+
lines.push(` -d '${fixtureCaptureJson}'`);
|
|
44171
|
+
lines.push("```");
|
|
44172
|
+
} else {
|
|
44173
|
+
lines.push("Capture the exact exercise once before editing. After patching, replay THIS fixture and do not declare success unless replay passes.");
|
|
44174
|
+
lines.push("```bash");
|
|
44175
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/capture \\`);
|
|
44176
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
44177
|
+
lines.push(` -d '${fixtureCaptureJson}'`);
|
|
44178
|
+
lines.push("");
|
|
44179
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/replay \\`);
|
|
44180
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
44181
|
+
lines.push(` -d '${fixtureReplayJson}'`);
|
|
44182
|
+
lines.push("```");
|
|
44183
|
+
}
|
|
44184
|
+
lines.push("");
|
|
44185
|
+
lines.push("The capture endpoint saves the exact request, initial result, and transcript assertions into the provider directory. The replay endpoint reruns the SAME exercise against your patched scripts and returns pass/fail.");
|
|
44186
|
+
lines.push("");
|
|
42801
44187
|
lines.push("### 2. Inspect parsed + raw adapter state");
|
|
42802
44188
|
lines.push("```bash");
|
|
44189
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/launch \\`);
|
|
44190
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
44191
|
+
lines.push(` -d '{"type":"${type}","workingDir":"${providerDir.replace(/\\/g, "\\\\")}"}'`);
|
|
42803
44192
|
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/debug/${type}`);
|
|
44193
|
+
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/trace/${type}`);
|
|
42804
44194
|
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/status`);
|
|
42805
44195
|
lines.push("```");
|
|
42806
44196
|
lines.push("");
|
|
44197
|
+
lines.push("The CLI trace endpoint is the primary debugging source. Read it BEFORE editing any parser code.");
|
|
44198
|
+
lines.push("Use the trace timeline to find the latest `settled` or `commit_transcript` frame for the repro turn and inspect these fields first:");
|
|
44199
|
+
lines.push("1. `payload.screenText`");
|
|
44200
|
+
lines.push("2. `payload.detectStatus` and `payload.parsedStatus`");
|
|
44201
|
+
lines.push("3. `payload.parsedLastAssistant`");
|
|
44202
|
+
lines.push("4. `payload.approval` / `payload.parsedActiveModal`");
|
|
44203
|
+
lines.push("5. `payload.rawPreview` only when control-sequence residue matters");
|
|
44204
|
+
lines.push("");
|
|
42807
44205
|
lines.push("The debug payload should be read in this priority order:");
|
|
42808
44206
|
lines.push("1. `screenText` / current visible state");
|
|
42809
44207
|
lines.push("2. parsed `status`, `messages`, `activeModal`");
|
|
42810
44208
|
lines.push("3. `rawBuffer` only for style/control-sequence cues");
|
|
42811
44209
|
lines.push("4. `buffer` only when the current screen is insufficient");
|
|
42812
44210
|
lines.push("");
|
|
42813
|
-
lines.push("
|
|
44211
|
+
lines.push("If the bug is transcript corruption, quote the exact bad `parsedLastAssistant` or bad committed assistant message from the trace and patch against that concrete failure.");
|
|
44212
|
+
lines.push("Do NOT guess based only on the final chat bubble or a truncated UI preview.");
|
|
42814
44213
|
lines.push("");
|
|
42815
|
-
lines.push("
|
|
44214
|
+
lines.push("Extract the current `instanceId` from the exercise, launch, or status response and keep using it below.");
|
|
44215
|
+
lines.push("");
|
|
44216
|
+
lines.push("### 3. Manual fallback only: send a realistic approval-triggering prompt");
|
|
42816
44217
|
lines.push("```bash");
|
|
42817
44218
|
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/send \\`);
|
|
42818
44219
|
lines.push(' -H "Content-Type: application/json" \\');
|
|
42819
44220
|
lines.push(` -d '{"type":"${type}","instanceId":"<INSTANCE_ID>","text":"Create a file at tmp/adhdev_provider_fix_test.py that prints the current working directory and the squares of 1 through 5, then run python3 tmp/adhdev_provider_fix_test.py and tell me the exact output."}'`);
|
|
42820
44221
|
lines.push("```");
|
|
42821
44222
|
lines.push("");
|
|
42822
|
-
lines.push("### 4.
|
|
44223
|
+
lines.push("### 4. Manual fallback only: if approval appears, resolve it until the CLI reaches idle");
|
|
42823
44224
|
lines.push("```bash");
|
|
42824
44225
|
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/resolve \\`);
|
|
42825
44226
|
lines.push(' -H "Content-Type: application/json" \\');
|
|
@@ -42829,10 +44230,14 @@ async (params) => {
|
|
|
42829
44230
|
lines.push(` -d '{"type":"${type}","instanceId":"<INSTANCE_ID>","keys":"1"}'`);
|
|
42830
44231
|
lines.push("```");
|
|
42831
44232
|
lines.push("");
|
|
42832
|
-
lines.push("Use `resolve` when the parsed modal buttons are correct. Use `raw` when the CLI expects a literal keystroke like `1`, `y`, or Enter. Repeat until idle.");
|
|
44233
|
+
lines.push("Use `resolve` when the parsed modal buttons are correct. Use `raw` when the CLI expects a literal keystroke like `1`, `y`, or Enter. Repeat until idle. Prefer the exercise endpoint instead of doing this by hand.");
|
|
42833
44234
|
lines.push("");
|
|
42834
44235
|
lines.push("### Patch Discipline");
|
|
42835
44236
|
lines.push("Once the repro is confirmed, immediately edit the target files. Avoid loops where you keep re-reading long files or re-running the same debug commands without changing code.");
|
|
44237
|
+
lines.push("For CLI transcript bugs, reproduce once with the exercise endpoint, inspect the returned trace once, patch immediately, then re-run the SAME exercise and compare the new `commit_transcript` frame.");
|
|
44238
|
+
lines.push("If the patched run still fails the exact required/banned substring checks above, the task is NOT complete even if the CLI exits normally.");
|
|
44239
|
+
lines.push("When you patch, write down the pattern family you are fixing: e.g. spinner/status, tool block, approval modal, footer chrome, OSC/control residue, prompt echo, or long-output continuation. Patch that family once instead of adding case-by-case literals.");
|
|
44240
|
+
lines.push('Bad fix pattern: add another `includes("Drizzling")` or `includes("Show more (")` check. Good fix pattern: broaden the regex/helper that recognizes spinner words, collapsed tool overflow lines, or footer chrome as a family.');
|
|
42836
44241
|
lines.push("");
|
|
42837
44242
|
lines.push("### 5. Verify the side effects outside the CLI");
|
|
42838
44243
|
lines.push("```bash");
|
|
@@ -42857,6 +44262,8 @@ async (params) => {
|
|
|
42857
44262
|
lines.push("7. Re-run the debug endpoints after edits. Do NOT finish until the parsed result looks correct.");
|
|
42858
44263
|
lines.push("8. Confirm the parser still works after a redraw or scroll change without duplicating transcript history.");
|
|
42859
44264
|
lines.push("9. Confirm the implementation prefers current-screen signals over stale history when both are present.");
|
|
44265
|
+
lines.push("10. For transcript-cleanliness bugs, confirm the latest `commit_transcript` trace frame no longer contains tool headers, approval prompts, OSC residue like `0;`, or footer chrome unless they are truly user-facing answer content.");
|
|
44266
|
+
lines.push("11. Confirm the implementation uses generalized pattern classifiers or regexes for noisy UI families instead of accumulating one-off literal string exceptions for each observed sample.");
|
|
42860
44267
|
lines.push("");
|
|
42861
44268
|
if (userComment) {
|
|
42862
44269
|
lines.push("## \u26A0\uFE0F User Instructions (HIGH PRIORITY)");
|
|
@@ -42865,10 +44272,11 @@ async (params) => {
|
|
|
42865
44272
|
lines.push(userComment);
|
|
42866
44273
|
lines.push("");
|
|
42867
44274
|
}
|
|
42868
|
-
lines.push("Start NOW. Launch the CLI, inspect PTY state, edit the scripts, and verify via the CLI debug endpoints.");
|
|
44275
|
+
lines.push("Start NOW. Launch the CLI, inspect the trace and PTY state, edit the scripts, and verify via the CLI debug + trace endpoints.");
|
|
42869
44276
|
return lines.join("\n");
|
|
42870
44277
|
}
|
|
42871
44278
|
function handleAutoImplSSE(ctx, type, req, res) {
|
|
44279
|
+
clearStaleAutoImplState(ctx, "SSE connection opened");
|
|
42872
44280
|
res.writeHead(200, {
|
|
42873
44281
|
"Content-Type": "text/event-stream",
|
|
42874
44282
|
"Cache-Control": "no-cache",
|
|
@@ -42890,6 +44298,7 @@ data: ${JSON.stringify(p.data)}
|
|
|
42890
44298
|
});
|
|
42891
44299
|
}
|
|
42892
44300
|
function handleAutoImplCancel(ctx, _type, _req, res) {
|
|
44301
|
+
clearStaleAutoImplState(ctx, "cancel request");
|
|
42893
44302
|
if (ctx.autoImplProcess) {
|
|
42894
44303
|
ctx.autoImplProcess.kill("SIGTERM");
|
|
42895
44304
|
setTimeout(() => {
|
|
@@ -42971,11 +44380,16 @@ data: ${JSON.stringify(msg.data)}
|
|
|
42971
44380
|
{ method: "GET", pattern: "/api/cli/status", handler: (q2, s15) => this.handleCliStatus(q2, s15) },
|
|
42972
44381
|
{ method: "POST", pattern: "/api/cli/launch", handler: (q2, s15) => this.handleCliLaunch(q2, s15) },
|
|
42973
44382
|
{ method: "POST", pattern: "/api/cli/send", handler: (q2, s15) => this.handleCliSend(q2, s15) },
|
|
44383
|
+
{ method: "POST", pattern: "/api/cli/exercise", handler: (q2, s15) => this.handleCliExercise(q2, s15) },
|
|
44384
|
+
{ method: "POST", pattern: "/api/cli/fixture/capture", handler: (q2, s15) => this.handleCliFixtureCapture(q2, s15) },
|
|
44385
|
+
{ method: "POST", pattern: "/api/cli/fixture/replay", handler: (q2, s15) => this.handleCliFixtureReplay(q2, s15) },
|
|
42974
44386
|
{ method: "POST", pattern: "/api/cli/resolve", handler: (q2, s15) => this.handleCliResolve(q2, s15) },
|
|
42975
44387
|
{ method: "POST", pattern: "/api/cli/raw", handler: (q2, s15) => this.handleCliRaw(q2, s15) },
|
|
42976
44388
|
{ method: "POST", pattern: "/api/cli/stop", handler: (q2, s15) => this.handleCliStop(q2, s15) },
|
|
42977
44389
|
{ method: "GET", pattern: "/api/cli/events", handler: (q2, s15) => this.handleCliSSE(q2, s15) },
|
|
42978
44390
|
{ method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q2, s15, p) => this.handleCliDebug(p[0], q2, s15) },
|
|
44391
|
+
{ method: "GET", pattern: /^\/api\/cli\/trace\/([^/]+)$/, handler: (q2, s15, p) => this.handleCliTrace(p[0], q2, s15) },
|
|
44392
|
+
{ method: "GET", pattern: /^\/api\/cli\/fixtures\/([^/]+)$/, handler: (q2, s15, p) => this.handleCliFixtureList(p[0], q2, s15) },
|
|
42979
44393
|
// Dynamic routes (provider :type param)
|
|
42980
44394
|
{ method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q2, s15, p) => this.handleRunScript(p[0], q2, s15) },
|
|
42981
44395
|
{ method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q2, s15, p) => this.handleListFiles(p[0], q2, s15) },
|
|
@@ -43009,8 +44423,8 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43009
44423
|
}
|
|
43010
44424
|
getEndpointList() {
|
|
43011
44425
|
return this.routes.map((r) => {
|
|
43012
|
-
const
|
|
43013
|
-
return `${r.method.padEnd(5)} ${
|
|
44426
|
+
const path19 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
|
|
44427
|
+
return `${r.method.padEnd(5)} ${path19}`;
|
|
43014
44428
|
});
|
|
43015
44429
|
}
|
|
43016
44430
|
async start(port = DEV_SERVER_PORT) {
|
|
@@ -43292,12 +44706,12 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43292
44706
|
// ─── DevConsole SPA ───
|
|
43293
44707
|
getConsoleDistDir() {
|
|
43294
44708
|
const candidates = [
|
|
43295
|
-
|
|
43296
|
-
|
|
43297
|
-
|
|
44709
|
+
path18.resolve(__dirname, "../../web-devconsole/dist"),
|
|
44710
|
+
path18.resolve(__dirname, "../../../web-devconsole/dist"),
|
|
44711
|
+
path18.join(process.cwd(), "packages/web-devconsole/dist")
|
|
43298
44712
|
];
|
|
43299
44713
|
for (const dir of candidates) {
|
|
43300
|
-
if (
|
|
44714
|
+
if (fs14.existsSync(path18.join(dir, "index.html"))) return dir;
|
|
43301
44715
|
}
|
|
43302
44716
|
return null;
|
|
43303
44717
|
}
|
|
@@ -43307,9 +44721,9 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43307
44721
|
this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
|
|
43308
44722
|
return;
|
|
43309
44723
|
}
|
|
43310
|
-
const htmlPath =
|
|
44724
|
+
const htmlPath = path18.join(distDir, "index.html");
|
|
43311
44725
|
try {
|
|
43312
|
-
const html =
|
|
44726
|
+
const html = fs14.readFileSync(htmlPath, "utf-8");
|
|
43313
44727
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
43314
44728
|
res.end(html);
|
|
43315
44729
|
} catch (e) {
|
|
@@ -43332,15 +44746,15 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43332
44746
|
this.json(res, 404, { error: "Not found" });
|
|
43333
44747
|
return;
|
|
43334
44748
|
}
|
|
43335
|
-
const safePath =
|
|
43336
|
-
const filePath =
|
|
44749
|
+
const safePath = path18.normalize(pathname).replace(/^\.\.\//, "");
|
|
44750
|
+
const filePath = path18.join(distDir, safePath);
|
|
43337
44751
|
if (!filePath.startsWith(distDir)) {
|
|
43338
44752
|
this.json(res, 403, { error: "Forbidden" });
|
|
43339
44753
|
return;
|
|
43340
44754
|
}
|
|
43341
44755
|
try {
|
|
43342
|
-
const content =
|
|
43343
|
-
const ext =
|
|
44756
|
+
const content = fs14.readFileSync(filePath);
|
|
44757
|
+
const ext = path18.extname(filePath);
|
|
43344
44758
|
const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
|
|
43345
44759
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
|
|
43346
44760
|
res.end(content);
|
|
@@ -43448,14 +44862,14 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43448
44862
|
const files = [];
|
|
43449
44863
|
const scan = (d, prefix) => {
|
|
43450
44864
|
try {
|
|
43451
|
-
for (const entry of
|
|
44865
|
+
for (const entry of fs14.readdirSync(d, { withFileTypes: true })) {
|
|
43452
44866
|
if (entry.name.startsWith(".") || entry.name.endsWith(".bak")) continue;
|
|
43453
44867
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
43454
44868
|
if (entry.isDirectory()) {
|
|
43455
44869
|
files.push({ path: rel, size: 0, type: "dir" });
|
|
43456
|
-
scan(
|
|
44870
|
+
scan(path18.join(d, entry.name), rel);
|
|
43457
44871
|
} else {
|
|
43458
|
-
const stat4 =
|
|
44872
|
+
const stat4 = fs14.statSync(path18.join(d, entry.name));
|
|
43459
44873
|
files.push({ path: rel, size: stat4.size, type: "file" });
|
|
43460
44874
|
}
|
|
43461
44875
|
}
|
|
@@ -43478,16 +44892,16 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43478
44892
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
43479
44893
|
return;
|
|
43480
44894
|
}
|
|
43481
|
-
const fullPath =
|
|
44895
|
+
const fullPath = path18.resolve(dir, path18.normalize(filePath));
|
|
43482
44896
|
if (!fullPath.startsWith(dir)) {
|
|
43483
44897
|
this.json(res, 403, { error: "Forbidden" });
|
|
43484
44898
|
return;
|
|
43485
44899
|
}
|
|
43486
|
-
if (!
|
|
44900
|
+
if (!fs14.existsSync(fullPath) || fs14.statSync(fullPath).isDirectory()) {
|
|
43487
44901
|
this.json(res, 404, { error: `File not found: ${filePath}` });
|
|
43488
44902
|
return;
|
|
43489
44903
|
}
|
|
43490
|
-
const content =
|
|
44904
|
+
const content = fs14.readFileSync(fullPath, "utf-8");
|
|
43491
44905
|
this.json(res, 200, { type, path: filePath, content, lines: content.split("\n").length });
|
|
43492
44906
|
}
|
|
43493
44907
|
/** POST /api/providers/:type/file — write a file { path, content } */
|
|
@@ -43503,15 +44917,15 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43503
44917
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
43504
44918
|
return;
|
|
43505
44919
|
}
|
|
43506
|
-
const fullPath =
|
|
44920
|
+
const fullPath = path18.resolve(dir, path18.normalize(filePath));
|
|
43507
44921
|
if (!fullPath.startsWith(dir)) {
|
|
43508
44922
|
this.json(res, 403, { error: "Forbidden" });
|
|
43509
44923
|
return;
|
|
43510
44924
|
}
|
|
43511
44925
|
try {
|
|
43512
|
-
if (
|
|
43513
|
-
|
|
43514
|
-
|
|
44926
|
+
if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
|
|
44927
|
+
fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
|
|
44928
|
+
fs14.writeFileSync(fullPath, content, "utf-8");
|
|
43515
44929
|
this.log(`File saved: ${fullPath} (${content.length} chars)`);
|
|
43516
44930
|
this.providerLoader.reload();
|
|
43517
44931
|
this.json(res, 200, { saved: true, path: filePath, chars: content.length });
|
|
@@ -43527,9 +44941,9 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43527
44941
|
return;
|
|
43528
44942
|
}
|
|
43529
44943
|
for (const name of ["scripts.js", "provider.json"]) {
|
|
43530
|
-
const p =
|
|
43531
|
-
if (
|
|
43532
|
-
const source =
|
|
44944
|
+
const p = path18.join(dir, name);
|
|
44945
|
+
if (fs14.existsSync(p)) {
|
|
44946
|
+
const source = fs14.readFileSync(p, "utf-8");
|
|
43533
44947
|
this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
|
|
43534
44948
|
return;
|
|
43535
44949
|
}
|
|
@@ -43548,11 +44962,11 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43548
44962
|
this.json(res, 404, { error: `Provider not found: ${type}` });
|
|
43549
44963
|
return;
|
|
43550
44964
|
}
|
|
43551
|
-
const target =
|
|
43552
|
-
const targetPath =
|
|
44965
|
+
const target = fs14.existsSync(path18.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
|
|
44966
|
+
const targetPath = path18.join(dir, target);
|
|
43553
44967
|
try {
|
|
43554
|
-
if (
|
|
43555
|
-
|
|
44968
|
+
if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
|
|
44969
|
+
fs14.writeFileSync(targetPath, source, "utf-8");
|
|
43556
44970
|
this.log(`Saved provider: ${targetPath} (${source.length} chars)`);
|
|
43557
44971
|
this.providerLoader.reload();
|
|
43558
44972
|
this.json(res, 200, { saved: true, path: targetPath, chars: source.length });
|
|
@@ -43709,21 +45123,21 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43709
45123
|
}
|
|
43710
45124
|
let targetDir;
|
|
43711
45125
|
targetDir = this.providerLoader.getUserProviderDir(category, type);
|
|
43712
|
-
const jsonPath =
|
|
43713
|
-
if (
|
|
45126
|
+
const jsonPath = path18.join(targetDir, "provider.json");
|
|
45127
|
+
if (fs14.existsSync(jsonPath)) {
|
|
43714
45128
|
this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
|
|
43715
45129
|
return;
|
|
43716
45130
|
}
|
|
43717
45131
|
try {
|
|
43718
45132
|
const result = generateFiles(type, name, category, { cdpPorts, cli, processName, installPath, binary, extensionId, version: version2, osPaths, processNames });
|
|
43719
|
-
|
|
43720
|
-
|
|
45133
|
+
fs14.mkdirSync(targetDir, { recursive: true });
|
|
45134
|
+
fs14.writeFileSync(jsonPath, result["provider.json"], "utf-8");
|
|
43721
45135
|
const createdFiles = ["provider.json"];
|
|
43722
45136
|
if (result.files) {
|
|
43723
45137
|
for (const [relPath, content] of Object.entries(result.files)) {
|
|
43724
|
-
const fullPath =
|
|
43725
|
-
|
|
43726
|
-
|
|
45138
|
+
const fullPath = path18.join(targetDir, relPath);
|
|
45139
|
+
fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
|
|
45140
|
+
fs14.writeFileSync(fullPath, content, "utf-8");
|
|
43727
45141
|
createdFiles.push(relPath);
|
|
43728
45142
|
}
|
|
43729
45143
|
}
|
|
@@ -43772,45 +45186,45 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43772
45186
|
}
|
|
43773
45187
|
// ─── Phase 2: Auto-Implement Backend ───
|
|
43774
45188
|
getLatestScriptVersionDir(scriptsDir) {
|
|
43775
|
-
if (!
|
|
43776
|
-
const versions =
|
|
45189
|
+
if (!fs14.existsSync(scriptsDir)) return null;
|
|
45190
|
+
const versions = fs14.readdirSync(scriptsDir).filter((d) => {
|
|
43777
45191
|
try {
|
|
43778
|
-
return
|
|
45192
|
+
return fs14.statSync(path18.join(scriptsDir, d)).isDirectory();
|
|
43779
45193
|
} catch {
|
|
43780
45194
|
return false;
|
|
43781
45195
|
}
|
|
43782
45196
|
}).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
43783
45197
|
if (versions.length === 0) return null;
|
|
43784
|
-
return
|
|
45198
|
+
return path18.join(scriptsDir, versions[0]);
|
|
43785
45199
|
}
|
|
43786
45200
|
resolveAutoImplWritableProviderDir(category, type, requestedDir) {
|
|
43787
|
-
const canonicalUserDir =
|
|
43788
|
-
const desiredDir = requestedDir ?
|
|
43789
|
-
const upstreamRoot =
|
|
43790
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
45201
|
+
const canonicalUserDir = path18.resolve(this.providerLoader.getUserProviderDir(category, type));
|
|
45202
|
+
const desiredDir = requestedDir ? path18.resolve(requestedDir) : canonicalUserDir;
|
|
45203
|
+
const upstreamRoot = path18.resolve(this.providerLoader.getUpstreamDir());
|
|
45204
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path18.sep}`)) {
|
|
43791
45205
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
43792
45206
|
}
|
|
43793
|
-
if (
|
|
45207
|
+
if (path18.basename(desiredDir) !== type) {
|
|
43794
45208
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
43795
45209
|
}
|
|
43796
45210
|
const sourceDir = this.findProviderDir(type);
|
|
43797
45211
|
if (!sourceDir) {
|
|
43798
45212
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
43799
45213
|
}
|
|
43800
|
-
if (!
|
|
43801
|
-
|
|
43802
|
-
|
|
45214
|
+
if (!fs14.existsSync(desiredDir)) {
|
|
45215
|
+
fs14.mkdirSync(path18.dirname(desiredDir), { recursive: true });
|
|
45216
|
+
fs14.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
43803
45217
|
this.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
43804
45218
|
}
|
|
43805
|
-
const providerJson =
|
|
43806
|
-
if (!
|
|
45219
|
+
const providerJson = path18.join(desiredDir, "provider.json");
|
|
45220
|
+
if (!fs14.existsSync(providerJson)) {
|
|
43807
45221
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
43808
45222
|
}
|
|
43809
45223
|
try {
|
|
43810
|
-
const providerData = JSON.parse(
|
|
45224
|
+
const providerData = JSON.parse(fs14.readFileSync(providerJson, "utf-8"));
|
|
43811
45225
|
if (providerData.disableUpstream !== true) {
|
|
43812
45226
|
providerData.disableUpstream = true;
|
|
43813
|
-
|
|
45227
|
+
fs14.writeFileSync(providerJson, JSON.stringify(providerData, null, 2));
|
|
43814
45228
|
}
|
|
43815
45229
|
} catch (error48) {
|
|
43816
45230
|
return {
|
|
@@ -43850,7 +45264,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43850
45264
|
setMode: "set_mode.js"
|
|
43851
45265
|
};
|
|
43852
45266
|
const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
|
|
43853
|
-
const scriptsDir =
|
|
45267
|
+
const scriptsDir = path18.join(providerDir, "scripts");
|
|
43854
45268
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
43855
45269
|
if (latestScriptsDir) {
|
|
43856
45270
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -43858,10 +45272,10 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43858
45272
|
lines.push("## \u270F\uFE0F Target Files (EDIT THESE)");
|
|
43859
45273
|
lines.push("These are the ONLY files you are allowed to modify. Replace the TODO stubs with working implementations.");
|
|
43860
45274
|
lines.push("");
|
|
43861
|
-
for (const file2 of
|
|
45275
|
+
for (const file2 of fs14.readdirSync(latestScriptsDir)) {
|
|
43862
45276
|
if (file2.endsWith(".js") && targetFileNames.has(file2)) {
|
|
43863
45277
|
try {
|
|
43864
|
-
const content =
|
|
45278
|
+
const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
|
|
43865
45279
|
lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
|
|
43866
45280
|
lines.push("```javascript");
|
|
43867
45281
|
lines.push(content);
|
|
@@ -43871,14 +45285,14 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43871
45285
|
}
|
|
43872
45286
|
}
|
|
43873
45287
|
}
|
|
43874
|
-
const refFiles =
|
|
45288
|
+
const refFiles = fs14.readdirSync(latestScriptsDir).filter((f) => f.endsWith(".js") && !targetFileNames.has(f));
|
|
43875
45289
|
if (refFiles.length > 0) {
|
|
43876
45290
|
lines.push("## \u{1F512} Other Scripts (REFERENCE ONLY \u2014 DO NOT EDIT)");
|
|
43877
45291
|
lines.push("These files are shown for context only. Do NOT modify them under any circumstances.");
|
|
43878
45292
|
lines.push("");
|
|
43879
45293
|
for (const file2 of refFiles) {
|
|
43880
45294
|
try {
|
|
43881
|
-
const content =
|
|
45295
|
+
const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
|
|
43882
45296
|
lines.push(`### \`${file2}\` \u{1F512}`);
|
|
43883
45297
|
lines.push("```javascript");
|
|
43884
45298
|
lines.push(content);
|
|
@@ -43919,11 +45333,11 @@ data: ${JSON.stringify(msg.data)}
|
|
|
43919
45333
|
lines.push("");
|
|
43920
45334
|
}
|
|
43921
45335
|
}
|
|
43922
|
-
const docsDir =
|
|
45336
|
+
const docsDir = path18.join(providerDir, "../../docs");
|
|
43923
45337
|
const loadGuide = (name) => {
|
|
43924
45338
|
try {
|
|
43925
|
-
const p =
|
|
43926
|
-
if (
|
|
45339
|
+
const p = path18.join(docsDir, name);
|
|
45340
|
+
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
43927
45341
|
} catch {
|
|
43928
45342
|
}
|
|
43929
45343
|
return null;
|
|
@@ -44096,7 +45510,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44096
45510
|
parseApproval: "parse_approval.js"
|
|
44097
45511
|
};
|
|
44098
45512
|
const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
|
|
44099
|
-
const scriptsDir =
|
|
45513
|
+
const scriptsDir = path18.join(providerDir, "scripts");
|
|
44100
45514
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
44101
45515
|
if (latestScriptsDir) {
|
|
44102
45516
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -44104,11 +45518,11 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44104
45518
|
lines.push("## \u270F\uFE0F Target Files (EDIT THESE)");
|
|
44105
45519
|
lines.push("These are the ONLY files you are allowed to modify. Replace TODO or heuristic-only logic with working PTY-aware implementations.");
|
|
44106
45520
|
lines.push("");
|
|
44107
|
-
for (const file2 of
|
|
45521
|
+
for (const file2 of fs14.readdirSync(latestScriptsDir)) {
|
|
44108
45522
|
if (!file2.endsWith(".js")) continue;
|
|
44109
45523
|
if (!targetFileNames.has(file2)) continue;
|
|
44110
45524
|
try {
|
|
44111
|
-
const content =
|
|
45525
|
+
const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
|
|
44112
45526
|
lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
|
|
44113
45527
|
lines.push("```javascript");
|
|
44114
45528
|
lines.push(content);
|
|
@@ -44117,14 +45531,14 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44117
45531
|
} catch {
|
|
44118
45532
|
}
|
|
44119
45533
|
}
|
|
44120
|
-
const refFiles =
|
|
45534
|
+
const refFiles = fs14.readdirSync(latestScriptsDir).filter((f) => f.endsWith(".js") && !targetFileNames.has(f));
|
|
44121
45535
|
if (refFiles.length > 0) {
|
|
44122
45536
|
lines.push("## \u{1F512} Other Scripts (REFERENCE ONLY \u2014 DO NOT EDIT)");
|
|
44123
45537
|
lines.push("These files are shown for context only. Do NOT modify them under any circumstances.");
|
|
44124
45538
|
lines.push("");
|
|
44125
45539
|
for (const file2 of refFiles) {
|
|
44126
45540
|
try {
|
|
44127
|
-
const content =
|
|
45541
|
+
const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
|
|
44128
45542
|
lines.push(`### \`${file2}\` \u{1F512}`);
|
|
44129
45543
|
lines.push("```javascript");
|
|
44130
45544
|
lines.push(content);
|
|
@@ -44157,11 +45571,11 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44157
45571
|
lines.push("");
|
|
44158
45572
|
}
|
|
44159
45573
|
}
|
|
44160
|
-
const docsDir =
|
|
45574
|
+
const docsDir = path18.join(providerDir, "../../docs");
|
|
44161
45575
|
const loadGuide = (name) => {
|
|
44162
45576
|
try {
|
|
44163
|
-
const p =
|
|
44164
|
-
if (
|
|
45577
|
+
const p = path18.join(docsDir, name);
|
|
45578
|
+
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
44165
45579
|
} catch {
|
|
44166
45580
|
}
|
|
44167
45581
|
return null;
|
|
@@ -44226,6 +45640,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44226
45640
|
lines.push("### 2. Inspect parsed + raw adapter state");
|
|
44227
45641
|
lines.push("```bash");
|
|
44228
45642
|
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/debug/${type}`);
|
|
45643
|
+
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/trace/${type}`);
|
|
44229
45644
|
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/status`);
|
|
44230
45645
|
lines.push("```");
|
|
44231
45646
|
lines.push("");
|
|
@@ -44360,6 +45775,16 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44360
45775
|
async handleCliSend(req, res) {
|
|
44361
45776
|
return handleCliSend(this, req, res);
|
|
44362
45777
|
}
|
|
45778
|
+
/** POST /api/cli/exercise — launch/send/approve/wait helper for provider-fix loops */
|
|
45779
|
+
async handleCliExercise(req, res) {
|
|
45780
|
+
return handleCliExercise(this, req, res);
|
|
45781
|
+
}
|
|
45782
|
+
async handleCliFixtureCapture(req, res) {
|
|
45783
|
+
return handleCliFixtureCapture(this, req, res);
|
|
45784
|
+
}
|
|
45785
|
+
async handleCliFixtureReplay(req, res) {
|
|
45786
|
+
return handleCliFixtureReplay(this, req, res);
|
|
45787
|
+
}
|
|
44363
45788
|
/** POST /api/cli/stop — stop a running CLI { type } */
|
|
44364
45789
|
async handleCliStop(req, res) {
|
|
44365
45790
|
return handleCliStop(this, req, res);
|
|
@@ -44383,6 +45808,13 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44383
45808
|
async handleCliDebug(type, _req, res) {
|
|
44384
45809
|
return handleCliDebug(this, type, _req, res);
|
|
44385
45810
|
}
|
|
45811
|
+
/** GET /api/cli/trace/:type — recent CLI trace timeline plus current debug snapshot */
|
|
45812
|
+
async handleCliTrace(type, _req, res) {
|
|
45813
|
+
return handleCliTrace(this, type, _req, res);
|
|
45814
|
+
}
|
|
45815
|
+
async handleCliFixtureList(type, _req, res) {
|
|
45816
|
+
return handleCliFixtureList(this, type, _req, res);
|
|
45817
|
+
}
|
|
44386
45818
|
/** POST /api/cli/resolve — resolve an approval modal { type, buttonIndex } */
|
|
44387
45819
|
async handleCliResolve(req, res) {
|
|
44388
45820
|
return handleCliResolve(this, req, res);
|
|
@@ -44549,7 +45981,18 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44549
45981
|
});
|
|
44550
45982
|
}
|
|
44551
45983
|
async boot() {
|
|
44552
|
-
|
|
45984
|
+
if (typeof this.options.ensureReady === "function") {
|
|
45985
|
+
await this.options.ensureReady();
|
|
45986
|
+
}
|
|
45987
|
+
try {
|
|
45988
|
+
await this.client.connect();
|
|
45989
|
+
} catch (error48) {
|
|
45990
|
+
if (typeof this.options.ensureReady !== "function") {
|
|
45991
|
+
throw error48;
|
|
45992
|
+
}
|
|
45993
|
+
await this.options.ensureReady();
|
|
45994
|
+
await this.client.connect();
|
|
45995
|
+
}
|
|
44553
45996
|
this.unsubscribe = this.client.onEvent((event) => this.handleEvent(event));
|
|
44554
45997
|
let record2 = null;
|
|
44555
45998
|
if (this.options.attachExisting) {
|
|
@@ -44924,8 +46367,8 @@ data: ${JSON.stringify(msg.data)}
|
|
|
44924
46367
|
const res = await fetch(extension.vsixUrl);
|
|
44925
46368
|
if (res.ok) {
|
|
44926
46369
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
44927
|
-
const
|
|
44928
|
-
|
|
46370
|
+
const fs15 = await import("fs");
|
|
46371
|
+
fs15.writeFileSync(vsixPath, buffer);
|
|
44929
46372
|
return new Promise((resolve10) => {
|
|
44930
46373
|
const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
|
|
44931
46374
|
(0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error48, _stdout, stderr) => {
|
|
@@ -45238,15 +46681,18 @@ data: ${JSON.stringify(msg.data)}
|
|
|
45238
46681
|
var import_http = require("http");
|
|
45239
46682
|
var import_ws = require("ws");
|
|
45240
46683
|
var path4 = __toESM(require("path"));
|
|
45241
|
-
var
|
|
45242
|
-
var
|
|
46684
|
+
var fs3 = __toESM(require("fs"));
|
|
46685
|
+
var os5 = __toESM(require("os"));
|
|
45243
46686
|
var import_daemon_core2 = __toESM(require_dist2());
|
|
45244
46687
|
|
|
45245
46688
|
// src/session-host.ts
|
|
45246
46689
|
var import_child_process = require("child_process");
|
|
46690
|
+
var fs2 = __toESM(require("fs"));
|
|
46691
|
+
var os2 = __toESM(require("os"));
|
|
45247
46692
|
var path = __toESM(require("path"));
|
|
45248
46693
|
var import_daemon_core = __toESM(require_dist2());
|
|
45249
46694
|
var SESSION_HOST_APP_NAME = process.env.ADHDEV_SESSION_HOST_NAME || "adhdev";
|
|
46695
|
+
var SESSION_HOST_START_TIMEOUT_MS = 15e3;
|
|
45250
46696
|
function buildSessionHostEnv(baseEnv) {
|
|
45251
46697
|
const env = {};
|
|
45252
46698
|
for (const [key, value] of Object.entries(baseEnv)) {
|
|
@@ -45267,12 +46713,58 @@ function resolveSessionHostEntry() {
|
|
|
45267
46713
|
path.resolve(__dirname, "../../vendor/session-host-daemon/index.js")
|
|
45268
46714
|
];
|
|
45269
46715
|
for (const candidate of localCandidates) {
|
|
45270
|
-
if (
|
|
46716
|
+
if (fs2.existsSync(candidate)) {
|
|
45271
46717
|
return candidate;
|
|
45272
46718
|
}
|
|
45273
46719
|
}
|
|
45274
46720
|
return require.resolve("@adhdev/session-host-daemon");
|
|
45275
46721
|
}
|
|
46722
|
+
function getSessionHostPidFile() {
|
|
46723
|
+
return path.join(os2.homedir(), ".adhdev", `${SESSION_HOST_APP_NAME}-session-host.pid`);
|
|
46724
|
+
}
|
|
46725
|
+
function killPid(pid) {
|
|
46726
|
+
try {
|
|
46727
|
+
if (process.platform === "win32") {
|
|
46728
|
+
(0, import_child_process.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
|
|
46729
|
+
} else {
|
|
46730
|
+
process.kill(pid, "SIGTERM");
|
|
46731
|
+
}
|
|
46732
|
+
return true;
|
|
46733
|
+
} catch {
|
|
46734
|
+
return false;
|
|
46735
|
+
}
|
|
46736
|
+
}
|
|
46737
|
+
function stopSessionHost() {
|
|
46738
|
+
let stopped = false;
|
|
46739
|
+
const pidFile = getSessionHostPidFile();
|
|
46740
|
+
try {
|
|
46741
|
+
if (fs2.existsSync(pidFile)) {
|
|
46742
|
+
const pid = Number.parseInt(fs2.readFileSync(pidFile, "utf8").trim(), 10);
|
|
46743
|
+
if (Number.isFinite(pid)) {
|
|
46744
|
+
stopped = killPid(pid) || stopped;
|
|
46745
|
+
}
|
|
46746
|
+
}
|
|
46747
|
+
} catch {
|
|
46748
|
+
} finally {
|
|
46749
|
+
try {
|
|
46750
|
+
fs2.unlinkSync(pidFile);
|
|
46751
|
+
} catch {
|
|
46752
|
+
}
|
|
46753
|
+
}
|
|
46754
|
+
if (process.platform !== "win32") {
|
|
46755
|
+
try {
|
|
46756
|
+
const raw = (0, import_child_process.execFileSync)("pgrep", ["-f", "session-host-daemon"], { encoding: "utf8" }).trim();
|
|
46757
|
+
for (const line of raw.split("\n")) {
|
|
46758
|
+
const pid = Number.parseInt(line.trim(), 10);
|
|
46759
|
+
if (Number.isFinite(pid)) {
|
|
46760
|
+
stopped = killPid(pid) || stopped;
|
|
46761
|
+
}
|
|
46762
|
+
}
|
|
46763
|
+
} catch {
|
|
46764
|
+
}
|
|
46765
|
+
}
|
|
46766
|
+
return stopped;
|
|
46767
|
+
}
|
|
45276
46768
|
async function runSessionHostCli(args) {
|
|
45277
46769
|
const entry = resolveSessionHostEntry();
|
|
45278
46770
|
const child = (0, import_child_process.spawn)(process.execPath, [entry, ...args], {
|
|
@@ -45285,19 +46777,34 @@ async function runSessionHostCli(args) {
|
|
|
45285
46777
|
});
|
|
45286
46778
|
}
|
|
45287
46779
|
async function ensureSessionHostReady() {
|
|
45288
|
-
|
|
45289
|
-
|
|
45290
|
-
|
|
45291
|
-
|
|
45292
|
-
|
|
45293
|
-
|
|
45294
|
-
|
|
45295
|
-
|
|
45296
|
-
|
|
45297
|
-
|
|
45298
|
-
|
|
45299
|
-
|
|
45300
|
-
|
|
46780
|
+
const spawnHost = () => {
|
|
46781
|
+
const entry = resolveSessionHostEntry();
|
|
46782
|
+
const child = (0, import_child_process.spawn)(process.execPath, [entry], {
|
|
46783
|
+
detached: true,
|
|
46784
|
+
stdio: "ignore",
|
|
46785
|
+
windowsHide: true,
|
|
46786
|
+
env: buildSessionHostEnv(process.env)
|
|
46787
|
+
});
|
|
46788
|
+
child.unref();
|
|
46789
|
+
};
|
|
46790
|
+
try {
|
|
46791
|
+
return await (0, import_daemon_core.ensureSessionHostReady)({
|
|
46792
|
+
appName: SESSION_HOST_APP_NAME,
|
|
46793
|
+
spawnHost,
|
|
46794
|
+
timeoutMs: SESSION_HOST_START_TIMEOUT_MS
|
|
46795
|
+
});
|
|
46796
|
+
} catch (error48) {
|
|
46797
|
+
stopSessionHost();
|
|
46798
|
+
return (0, import_daemon_core.ensureSessionHostReady)({
|
|
46799
|
+
appName: SESSION_HOST_APP_NAME,
|
|
46800
|
+
spawnHost,
|
|
46801
|
+
timeoutMs: SESSION_HOST_START_TIMEOUT_MS
|
|
46802
|
+
}).catch((retryError) => {
|
|
46803
|
+
const initialMessage = error48 instanceof Error ? error48.message : String(error48);
|
|
46804
|
+
const retryMessage = retryError instanceof Error ? retryError.message : String(retryError);
|
|
46805
|
+
throw new Error(`Session host failed to start after retry (${initialMessage}; retry: ${retryMessage})`);
|
|
46806
|
+
});
|
|
46807
|
+
}
|
|
45301
46808
|
}
|
|
45302
46809
|
async function listHostedCliRuntimes(endpoint) {
|
|
45303
46810
|
return (0, import_daemon_core.listHostedCliRuntimes)(endpoint);
|
|
@@ -45315,7 +46822,7 @@ async function proxySessionHostAttach(target, options = {}) {
|
|
|
45315
46822
|
}
|
|
45316
46823
|
|
|
45317
46824
|
// ../session-host-core/dist/index.mjs
|
|
45318
|
-
var
|
|
46825
|
+
var os3 = __toESM(require("os"), 1);
|
|
45319
46826
|
var path2 = __toESM(require("path"), 1);
|
|
45320
46827
|
var net = __toESM(require("net"), 1);
|
|
45321
46828
|
var import_crypto = require("crypto");
|
|
@@ -45328,7 +46835,7 @@ function getDefaultSessionHostEndpoint(appName = "adhdev") {
|
|
|
45328
46835
|
}
|
|
45329
46836
|
return {
|
|
45330
46837
|
kind: "unix",
|
|
45331
|
-
path: path2.join(
|
|
46838
|
+
path: path2.join(os3.tmpdir(), `${appName}-session-host.sock`)
|
|
45332
46839
|
};
|
|
45333
46840
|
}
|
|
45334
46841
|
function serializeEnvelope(envelope) {
|
|
@@ -45612,7 +47119,7 @@ if (pkgVersion === "unknown") {
|
|
|
45612
47119
|
];
|
|
45613
47120
|
for (const candidate of possiblePaths) {
|
|
45614
47121
|
try {
|
|
45615
|
-
const data = JSON.parse(
|
|
47122
|
+
const data = JSON.parse(fs3.readFileSync(candidate, "utf-8"));
|
|
45616
47123
|
if (data.version) {
|
|
45617
47124
|
pkgVersion = data.version;
|
|
45618
47125
|
break;
|
|
@@ -45714,6 +47221,10 @@ var StandaloneServer = class {
|
|
|
45714
47221
|
},
|
|
45715
47222
|
createPtyTransportFactory: ({ runtimeId, providerType, workspace, cliArgs, providerSessionId, attachExisting }) => new import_daemon_core2.SessionHostPtyTransportFactory({
|
|
45716
47223
|
endpoint: sessionHostEndpoint,
|
|
47224
|
+
ensureReady: async () => {
|
|
47225
|
+
const activeEndpoint = await this.ensureActiveSessionHostEndpoint();
|
|
47226
|
+
this.sessionHostEndpoint = activeEndpoint;
|
|
47227
|
+
},
|
|
45717
47228
|
clientId: `daemon-${process.pid}`,
|
|
45718
47229
|
runtimeId,
|
|
45719
47230
|
providerType,
|
|
@@ -45972,7 +47483,7 @@ var StandaloneServer = class {
|
|
|
45972
47483
|
if (publicDir) {
|
|
45973
47484
|
const filePath = url2 === "/" ? "/index.html" : url2;
|
|
45974
47485
|
const fullPath = path4.join(publicDir, filePath);
|
|
45975
|
-
if (
|
|
47486
|
+
if (fs3.existsSync(fullPath) && fs3.statSync(fullPath).isFile()) {
|
|
45976
47487
|
const ext = path4.extname(fullPath);
|
|
45977
47488
|
const mimeTypes = {
|
|
45978
47489
|
".html": "text/html",
|
|
@@ -45985,13 +47496,13 @@ var StandaloneServer = class {
|
|
|
45985
47496
|
".woff2": "font/woff2"
|
|
45986
47497
|
};
|
|
45987
47498
|
res.writeHead(200, { "Content-Type": mimeTypes[ext] || "application/octet-stream" });
|
|
45988
|
-
|
|
47499
|
+
fs3.createReadStream(fullPath).pipe(res);
|
|
45989
47500
|
return;
|
|
45990
47501
|
}
|
|
45991
47502
|
const indexPath = path4.join(publicDir, "index.html");
|
|
45992
|
-
if (
|
|
47503
|
+
if (fs3.existsSync(indexPath) && !url2.startsWith("/api/")) {
|
|
45993
47504
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
45994
|
-
|
|
47505
|
+
fs3.createReadStream(indexPath).pipe(res);
|
|
45995
47506
|
return;
|
|
45996
47507
|
}
|
|
45997
47508
|
}
|
|
@@ -46251,7 +47762,7 @@ var StandaloneServer = class {
|
|
|
46251
47762
|
}
|
|
46252
47763
|
// ─── Network ───
|
|
46253
47764
|
getLanIPs() {
|
|
46254
|
-
const interfaces =
|
|
47765
|
+
const interfaces = os5.networkInterfaces();
|
|
46255
47766
|
const ips = [];
|
|
46256
47767
|
for (const iface of Object.values(interfaces)) {
|
|
46257
47768
|
if (!iface) continue;
|
|
@@ -46370,7 +47881,7 @@ Runtime commands:
|
|
|
46370
47881
|
path4.join(process.cwd(), "public")
|
|
46371
47882
|
];
|
|
46372
47883
|
for (const candidate of candidates) {
|
|
46373
|
-
if (
|
|
47884
|
+
if (fs3.existsSync(path4.join(candidate, "index.html"))) {
|
|
46374
47885
|
options.publicDir = candidate;
|
|
46375
47886
|
break;
|
|
46376
47887
|
}
|