acpx 0.19.0 → 0.19.2
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/{agent-registry-Ct2yWPW7.js → agent-registry-DWlx5BQ4.js} +3 -2
- package/dist/agent-registry-DWlx5BQ4.js.map +1 -0
- package/dist/agent-registry.js +1 -1
- package/dist/{cli-Bmki9cj5.js → cli-D-s_eTUy.js} +5 -5
- package/dist/{cli-Bmki9cj5.js.map → cli-D-s_eTUy.js.map} +1 -1
- package/dist/cli.js +31 -15
- package/dist/cli.js.map +1 -1
- package/dist/{client-DzaUKazo.d.ts → client-BchvQRpz.d.ts} +8 -1
- package/dist/client-BchvQRpz.d.ts.map +1 -0
- package/dist/{flows--Ewxn_fQ.js → flows-DdxEaeZo.js} +33 -20
- package/dist/flows-DdxEaeZo.js.map +1 -0
- package/dist/flows.d.ts +2 -1
- package/dist/flows.d.ts.map +1 -1
- package/dist/flows.js +1 -1
- package/dist/{invocation-options-DRzxHZmr.js → invocation-options-HjxSBsK0.js} +5 -5
- package/dist/{invocation-options-DRzxHZmr.js.map → invocation-options-HjxSBsK0.js.map} +1 -1
- package/dist/{ipc-fMgj5vrO.js → ipc-D0ZMQ0q9.js} +1393 -86
- package/dist/ipc-D0ZMQ0q9.js.map +1 -0
- package/dist/{output-D6kDs8BN.js → output-GQnhkJMU.js} +20 -12
- package/dist/output-GQnhkJMU.js.map +1 -0
- package/dist/{queue-owner-runtime-CLwjMYNL.js → queue-owner-runtime-W0lly6ap.js} +344 -777
- package/dist/queue-owner-runtime-W0lly6ap.js.map +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +5 -5
- package/dist/runtime.js.map +1 -1
- package/dist/{session-watch-BfdIo-n4.js → session-watch-_8yxQ6ba.js} +4 -4
- package/dist/{session-watch-BfdIo-n4.js.map → session-watch-_8yxQ6ba.js.map} +1 -1
- package/dist/watch-C0mwpJcN.js +85 -0
- package/dist/watch-C0mwpJcN.js.map +1 -0
- package/package.json +1 -1
- package/skills/acpx/SKILL.md +2 -0
- package/dist/agent-registry-Ct2yWPW7.js.map +0 -1
- package/dist/client-DzaUKazo.d.ts.map +0 -1
- package/dist/flows--Ewxn_fQ.js.map +0 -1
- package/dist/ipc-fMgj5vrO.js.map +0 -1
- package/dist/output-D6kDs8BN.js.map +0 -1
- package/dist/queue-owner-runtime-CLwjMYNL.js.map +0 -1
- package/dist/watch-DH-zXQH2.js +0 -32
- package/dist/watch-DH-zXQH2.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { A as
|
|
1
|
+
import { $ as resetPerfMetrics, Bt as PermissionDeniedError, Ct as extractAcpError, E as textPrompt, F as normalizeName, Ft as AgentStartupError, G as SESSION_JOURNAL_SCHEMA, Gt as SessionModelReplayError, Ht as QueueConnectionError, It as AuthPolicyError, Jt as UnsupportedPromptContentError, K as SessionJournalReader, Lt as ClaudeAcpSessionCreateTimeoutError, M as isoNow$1, Mt as AcpxOperationalError, Nt as AgentDisconnectedError, O as absolutePath, Pt as AgentSpawnError, Q as measurePerf, R as resolveSessionRecord, Rt as CopilotAcpUnsupportedError, S as getUnsupportedPromptContentMessage, St as normalizeOutputError, T as promptToDisplayText, U as extractAgentSessionId, Ut as SessionConfigOptionReplayError, Vt as PermissionPromptUnavailableError, W as normalizeAgentSessionId, Wt as SessionModeReplayError, X as getPerfMetricsSnapshot, Y as formatPerfMetric, Z as incrementPerfCounter, _ as createLockOwner, _t as isProcessAlive, a as trySetModelOnRunningOwner, at as isSessionUpdateNotification, bt as isAcpQueryClosedBeforeResponseError, d as releaseQueueOwnerLease, dt as sessionBaseDir, et as setPerfGauge, f as terminateProcess, ft as sessionEventActivePath, g as QueueLeaseGuardSettlementError, h as waitMs$1, i as trySetModeOnRunningOwner, it as isAcpMessageObject, j as findSessionByDirectoryWalk, k as findGitRepositoryRoot, m as tryAcquireQueueOwnerLease, mt as sessionEventSegmentPath, n as tryCloseSessionOnRunningOwner, o as trySubmitToRunningOwner, p as terminateQueueOwnerForSession, pt as sessionEventLockPath, qt as SessionResumeRequiredError, r as trySetConfigOptionOnRunningOwner, rt as isAcpJsonRpcMessage, s as SessionQueueOwner, t as tryCancelOnRunningOwner, tt as startPerfTimer, u as refreshQueueOwnerLease, ut as defaultSessionEventLog, v as lockOwnerPid, wt as isAcpResourceNotFoundError, xt as isRetryablePromptError, y as ProcessDescendants, yt as formatErrorMessage, z as writeSessionRecord, zt as GeminiAcpStartupTimeoutError } from "./ipc-D0ZMQ0q9.js";
|
|
2
|
+
import { A as requireAgentStdio, C as PROCESS_HELPER_TIMEOUT_MS, D as isoNow$2, E as isChildProcessRunning, F as waitForChildExit, I as waitForSpawn, M as resolveAgentSessionCwd, N as runTimedExecFile, P as splitCommandLine, T as basenameToken, _ as buildSpawnCommandOptions, b as readWindowsEnvValue, f as resolveBuiltInAgentLaunch, g as buildAgentSpawnCommand, j as resolveAgentCommandParts, v as buildTerminalShellSpawnCommand, w as asAbsoluteCwd, x as resolveWindowsExecutablePath, y as buildTerminalSpawnCommand } from "./agent-registry-DWlx5BQ4.js";
|
|
3
3
|
import fs, { readFileSync, realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import path from "node:path";
|
|
@@ -16,137 +16,6 @@ import { root } from "@openclaw/fs-safe/root";
|
|
|
16
16
|
import readline from "node:readline/promises";
|
|
17
17
|
import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
|
|
18
18
|
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
19
|
-
import net from "node:net";
|
|
20
|
-
//#region src/acp/error-normalization.ts
|
|
21
|
-
const AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
|
|
22
|
-
const QUERY_CLOSED_BEFORE_RESPONSE_DETAIL = "query closed before response received";
|
|
23
|
-
function asRecord$3(value) {
|
|
24
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
|
-
function isAuthRequiredMessage(value) {
|
|
28
|
-
if (!value) return false;
|
|
29
|
-
const normalized = value.toLowerCase();
|
|
30
|
-
return [
|
|
31
|
-
"auth required",
|
|
32
|
-
"authentication required",
|
|
33
|
-
"authorization required",
|
|
34
|
-
"credential required",
|
|
35
|
-
"credentials required",
|
|
36
|
-
"token required",
|
|
37
|
-
"login required"
|
|
38
|
-
].some((needle) => normalized.includes(needle));
|
|
39
|
-
}
|
|
40
|
-
function isAcpAuthRequiredPayload(acp) {
|
|
41
|
-
if (!acp) return false;
|
|
42
|
-
if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) return false;
|
|
43
|
-
if (isAuthRequiredMessage(acp.message)) return true;
|
|
44
|
-
const data = asRecord$3(acp.data);
|
|
45
|
-
if (!data) return false;
|
|
46
|
-
return hasAuthRequiredData(data);
|
|
47
|
-
}
|
|
48
|
-
function hasAuthRequiredData(data) {
|
|
49
|
-
return data.authRequired === true || hasNonEmptyString(data.methodId) || hasNonEmptyArray(data.methods);
|
|
50
|
-
}
|
|
51
|
-
function hasNonEmptyString(value) {
|
|
52
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
53
|
-
}
|
|
54
|
-
function hasNonEmptyArray(value) {
|
|
55
|
-
return Array.isArray(value) && value.length > 0;
|
|
56
|
-
}
|
|
57
|
-
function isOutputErrorCode(value) {
|
|
58
|
-
return typeof value === "string" && OUTPUT_ERROR_CODES.includes(value);
|
|
59
|
-
}
|
|
60
|
-
function isOutputErrorOrigin(value) {
|
|
61
|
-
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
62
|
-
}
|
|
63
|
-
function readOutputErrorMeta(error) {
|
|
64
|
-
const record = asRecord$3(error);
|
|
65
|
-
if (!record) return {};
|
|
66
|
-
return {
|
|
67
|
-
outputCode: isOutputErrorCode(record.outputCode) ? record.outputCode : void 0,
|
|
68
|
-
detailCode: typeof record.detailCode === "string" && record.detailCode.trim().length > 0 ? record.detailCode : void 0,
|
|
69
|
-
origin: isOutputErrorOrigin(record.origin) ? record.origin : void 0,
|
|
70
|
-
retryable: typeof record.retryable === "boolean" ? record.retryable : void 0,
|
|
71
|
-
acp: extractAcpError(record.acp)
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function isTimeoutLike(error) {
|
|
75
|
-
return error instanceof Error && error.name === "TimeoutError";
|
|
76
|
-
}
|
|
77
|
-
function isNoSessionLike(error) {
|
|
78
|
-
return error instanceof Error && error.name === "NoSessionError";
|
|
79
|
-
}
|
|
80
|
-
function isUsageLike(error) {
|
|
81
|
-
if (!(error instanceof Error)) return false;
|
|
82
|
-
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord$3(error)?.code === "commander.invalidArgument";
|
|
83
|
-
}
|
|
84
|
-
function formatErrorMessage(error) {
|
|
85
|
-
return formatUnknownErrorMessage(error);
|
|
86
|
-
}
|
|
87
|
-
function isAcpQueryClosedBeforeResponseError(error) {
|
|
88
|
-
const acp = extractAcpError(error);
|
|
89
|
-
if (!acp || acp.code !== -32603) return false;
|
|
90
|
-
const details = asRecord$3(acp.data)?.details;
|
|
91
|
-
if (typeof details !== "string") return false;
|
|
92
|
-
return details.toLowerCase().includes(QUERY_CLOSED_BEFORE_RESPONSE_DETAIL);
|
|
93
|
-
}
|
|
94
|
-
function mapErrorCode(error) {
|
|
95
|
-
if (error instanceof PermissionPromptUnavailableError) return "PERMISSION_PROMPT_UNAVAILABLE";
|
|
96
|
-
if (error instanceof PermissionDeniedError) return "PERMISSION_DENIED";
|
|
97
|
-
if (isTimeoutLike(error)) return "TIMEOUT";
|
|
98
|
-
if (isNoSessionLike(error) || isAcpResourceNotFoundError(error)) return "NO_SESSION";
|
|
99
|
-
if (isUsageLike(error)) return "USAGE";
|
|
100
|
-
}
|
|
101
|
-
function normalizeOutputError(error, options = {}) {
|
|
102
|
-
const meta = readOutputErrorMeta(error);
|
|
103
|
-
const code = resolveOutputErrorCode(error, options, meta);
|
|
104
|
-
const acp = options.acp ?? meta.acp ?? extractAcpError(error);
|
|
105
|
-
return {
|
|
106
|
-
code,
|
|
107
|
-
message: formatErrorMessage(error),
|
|
108
|
-
detailCode: resolveDetailCode(error, acp, options, meta),
|
|
109
|
-
origin: meta.origin ?? options.origin,
|
|
110
|
-
retryable: meta.retryable ?? options.retryable,
|
|
111
|
-
acp
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function resolveOutputErrorCode(error, options, meta) {
|
|
115
|
-
const code = meta.outputCode ?? mapErrorCode(error) ?? options.defaultCode ?? "RUNTIME";
|
|
116
|
-
if (code === "RUNTIME" && isAcpResourceNotFoundError(error)) return "NO_SESSION";
|
|
117
|
-
return code;
|
|
118
|
-
}
|
|
119
|
-
function resolveDetailCode(error, acp, options, meta) {
|
|
120
|
-
return meta.detailCode ?? options.detailCode ?? (error instanceof AuthPolicyError || isAcpAuthRequiredPayload(acp) ? "AUTH_REQUIRED" : void 0);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Returns true when an error from `client.prompt()` looks transient and
|
|
124
|
-
* can reasonably be retried (e.g. model-API 400/500, network hiccups that
|
|
125
|
-
* surface as ACP internal errors).
|
|
126
|
-
*
|
|
127
|
-
* Errors that are definitively non-recoverable (auth, missing session,
|
|
128
|
-
* invalid params, timeout, permission) return false.
|
|
129
|
-
*/
|
|
130
|
-
function isRetryablePromptError(error) {
|
|
131
|
-
if (isNonRetryablePromptError(error)) return false;
|
|
132
|
-
const acp = extractAcpError(error);
|
|
133
|
-
if (!acp) return false;
|
|
134
|
-
return acp.code === -32603 || acp.code === -32700;
|
|
135
|
-
}
|
|
136
|
-
function isNonRetryablePromptError(error) {
|
|
137
|
-
return error instanceof PermissionDeniedError || error instanceof PermissionPromptUnavailableError || isTimeoutLike(error) || isNoSessionLike(error) || isUsageLike(error);
|
|
138
|
-
}
|
|
139
|
-
function exitCodeForOutputErrorCode(code) {
|
|
140
|
-
switch (code) {
|
|
141
|
-
case "USAGE": return EXIT_CODES.USAGE;
|
|
142
|
-
case "TIMEOUT": return EXIT_CODES.TIMEOUT;
|
|
143
|
-
case "NO_SESSION": return EXIT_CODES.NO_SESSION;
|
|
144
|
-
case "PERMISSION_DENIED":
|
|
145
|
-
case "PERMISSION_PROMPT_UNAVAILABLE": return EXIT_CODES.PERMISSION_DENIED;
|
|
146
|
-
default: return EXIT_CODES.ERROR;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
//#endregion
|
|
150
19
|
//#region src/async-control.ts
|
|
151
20
|
var TimeoutError = class extends Error {
|
|
152
21
|
constructor(timeoutMs) {
|
|
@@ -1080,63 +949,22 @@ function compareVersionParts(left, right) {
|
|
|
1080
949
|
}
|
|
1081
950
|
return 0;
|
|
1082
951
|
}
|
|
1083
|
-
async function detectGeminiVersion(command) {
|
|
1084
|
-
const versionLine = (await
|
|
952
|
+
async function detectGeminiVersion(command, context, diagnostic = false) {
|
|
953
|
+
const versionLine = (await context.readOutput(command, ["--version"], GEMINI_VERSION_TIMEOUT_MS, diagnostic))?.split(/\r?\n/).map((line) => line.trim()).find((line) => /\d+\.\d+\.\d+/.test(line));
|
|
1085
954
|
return parseGeminiVersion(versionLine);
|
|
1086
955
|
}
|
|
1087
|
-
async function resolveGeminiCommandArgs(command, args) {
|
|
956
|
+
async function resolveGeminiCommandArgs(command, args, context) {
|
|
1088
957
|
if (basenameToken(command) !== "gemini" || !args.includes("--acp")) return [...args];
|
|
1089
|
-
const version = await detectGeminiVersion(command);
|
|
958
|
+
const version = await detectGeminiVersion(command, context);
|
|
1090
959
|
if (version && compareVersionParts(version.parts, GEMINI_ACP_FLAG_VERSION) < 0) return args.map((arg) => arg === "--acp" ? "--experimental-acp" : arg);
|
|
1091
960
|
return [...args];
|
|
1092
961
|
}
|
|
1093
|
-
async function
|
|
1094
|
-
return await new Promise((resolve) => {
|
|
1095
|
-
const child = spawn(command, [...args], buildSpawnCommandOptions(command, {
|
|
1096
|
-
stdio: [
|
|
1097
|
-
"ignore",
|
|
1098
|
-
"pipe",
|
|
1099
|
-
"pipe"
|
|
1100
|
-
],
|
|
1101
|
-
windowsHide: true
|
|
1102
|
-
}));
|
|
1103
|
-
let stdout = "";
|
|
1104
|
-
let stderr = "";
|
|
1105
|
-
let settled = false;
|
|
1106
|
-
const finish = (value) => {
|
|
1107
|
-
if (settled) return;
|
|
1108
|
-
settled = true;
|
|
1109
|
-
clearTimeout(timer);
|
|
1110
|
-
child.removeAllListeners();
|
|
1111
|
-
child.stdout?.removeAllListeners();
|
|
1112
|
-
child.stderr?.removeAllListeners();
|
|
1113
|
-
resolve(value);
|
|
1114
|
-
};
|
|
1115
|
-
const timer = setTimeout(() => {
|
|
1116
|
-
child.kill("SIGKILL");
|
|
1117
|
-
finish(void 0);
|
|
1118
|
-
}, timeoutMs);
|
|
1119
|
-
child.stdout?.setEncoding("utf8");
|
|
1120
|
-
child.stderr?.setEncoding("utf8");
|
|
1121
|
-
child.stdout?.on("data", (chunk) => {
|
|
1122
|
-
stdout += chunk;
|
|
1123
|
-
});
|
|
1124
|
-
child.stderr?.on("data", (chunk) => {
|
|
1125
|
-
stderr += chunk;
|
|
1126
|
-
});
|
|
1127
|
-
child.once("error", () => {
|
|
1128
|
-
finish(void 0);
|
|
1129
|
-
});
|
|
1130
|
-
child.once("close", () => {
|
|
1131
|
-
finish(`${stdout}\n${stderr}`);
|
|
1132
|
-
});
|
|
1133
|
-
});
|
|
1134
|
-
}
|
|
1135
|
-
async function buildGeminiAcpStartupTimeoutMessage(command) {
|
|
962
|
+
async function buildGeminiAcpStartupTimeoutMessage(command, context) {
|
|
1136
963
|
const parts = ["Gemini CLI ACP startup timed out before initialize completed.", "This usually means the local Gemini CLI is waiting on interactive OAuth or has incompatible ACP subprocess behavior."];
|
|
1137
|
-
const version = await detectGeminiVersion(command);
|
|
964
|
+
const version = await detectGeminiVersion(command, context, true);
|
|
1138
965
|
if (version) parts.push(`Detected Gemini CLI version: ${version.raw}.`);
|
|
1139
|
-
|
|
966
|
+
const env = context.env;
|
|
967
|
+
if (!env.GEMINI_API_KEY && !env.GOOGLE_API_KEY) parts.push("No GEMINI_API_KEY or GOOGLE_API_KEY was set for non-interactive auth.");
|
|
1140
968
|
parts.push("Try upgrading Gemini CLI and using API-key-based auth for non-interactive ACP runs.");
|
|
1141
969
|
return parts.join(" ");
|
|
1142
970
|
}
|
|
@@ -1147,16 +975,17 @@ function buildClaudeAcpSessionCreateTimeoutMessage() {
|
|
|
1147
975
|
"In harnessed or non-interactive runs, prefer --approve-all with nonInteractivePermissions=deny, upgrade Claude Code and the Claude ACP adapter, or use acpx claude exec as a one-shot fallback."
|
|
1148
976
|
].join(" ");
|
|
1149
977
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
978
|
+
function buildCopilotAcpUnsupportedMessage() {
|
|
979
|
+
return [
|
|
980
|
+
"GitHub Copilot CLI ACP stdio mode is not available in the installed copilot binary.",
|
|
981
|
+
"acpx copilot expects a Copilot CLI release that supports --acp --stdio.",
|
|
982
|
+
"Detected copilot --help output without --acp support.",
|
|
983
|
+
"Upgrade GitHub Copilot CLI to a release with ACP stdio support, or use --agent with another ACP-compatible adapter in the meantime."
|
|
984
|
+
].join(" ");
|
|
1156
985
|
}
|
|
1157
|
-
async function ensureCopilotAcpSupport(command) {
|
|
1158
|
-
const helpOutput = await
|
|
1159
|
-
if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(
|
|
986
|
+
async function ensureCopilotAcpSupport(command, context) {
|
|
987
|
+
const helpOutput = await context.readOutput(command, ["--help"], COPILOT_HELP_TIMEOUT_MS);
|
|
988
|
+
if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(buildCopilotAcpUnsupportedMessage(), { retryable: false });
|
|
1160
989
|
}
|
|
1161
990
|
function buildClaudeCodeOptionsMeta(options, isolateUserSettings = false) {
|
|
1162
991
|
const claudeCodeOptions = {};
|
|
@@ -1377,6 +1206,73 @@ function resolveClientCapabilities(params) {
|
|
|
1377
1206
|
};
|
|
1378
1207
|
}
|
|
1379
1208
|
//#endregion
|
|
1209
|
+
//#region src/acp/command-probe.ts
|
|
1210
|
+
var CommandProbeAdmissionError = class extends Error {
|
|
1211
|
+
reason;
|
|
1212
|
+
constructor(reason) {
|
|
1213
|
+
super("Agent command admission rejected", { cause: reason });
|
|
1214
|
+
this.reason = reason;
|
|
1215
|
+
this.name = "CommandProbeAdmissionError";
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
async function admitCommandProbe(admit) {
|
|
1219
|
+
try {
|
|
1220
|
+
return await admit();
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
throw new CommandProbeAdmissionError(error);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
/** Collect only this short adapter command; its client owns process retirement. */
|
|
1226
|
+
function captureCommandProbeOutput(child, timeoutMs, retire) {
|
|
1227
|
+
let stdout = "";
|
|
1228
|
+
let stderr = "";
|
|
1229
|
+
let settled = false;
|
|
1230
|
+
let disposed = false;
|
|
1231
|
+
let timer;
|
|
1232
|
+
let resolveResult;
|
|
1233
|
+
const result = new Promise((resolve) => {
|
|
1234
|
+
resolveResult = resolve;
|
|
1235
|
+
});
|
|
1236
|
+
const finish = (value) => {
|
|
1237
|
+
if (settled) return;
|
|
1238
|
+
settled = true;
|
|
1239
|
+
clearTimeout(timer);
|
|
1240
|
+
resolveResult(value);
|
|
1241
|
+
};
|
|
1242
|
+
const fail = () => {
|
|
1243
|
+
if (disposed) return;
|
|
1244
|
+
finish(void 0);
|
|
1245
|
+
retire().catch(() => {});
|
|
1246
|
+
};
|
|
1247
|
+
const onStdout = (chunk) => {
|
|
1248
|
+
if (!settled) stdout += chunk;
|
|
1249
|
+
};
|
|
1250
|
+
const onStderr = (chunk) => {
|
|
1251
|
+
if (!settled) stderr += chunk;
|
|
1252
|
+
};
|
|
1253
|
+
const onClose = () => finish(`${stdout}\n${stderr}`);
|
|
1254
|
+
child.stdout?.setEncoding("utf8");
|
|
1255
|
+
child.stderr?.setEncoding("utf8");
|
|
1256
|
+
child.stdout?.on("data", onStdout);
|
|
1257
|
+
child.stderr?.on("data", onStderr);
|
|
1258
|
+
child.on("error", fail);
|
|
1259
|
+
child.stdout?.on("error", fail);
|
|
1260
|
+
child.stderr?.on("error", fail);
|
|
1261
|
+
child.once("close", onClose);
|
|
1262
|
+
timer = setTimeout(fail, timeoutMs);
|
|
1263
|
+
return {
|
|
1264
|
+
result,
|
|
1265
|
+
dispose: () => {
|
|
1266
|
+
disposed = true;
|
|
1267
|
+
finish(void 0);
|
|
1268
|
+
clearTimeout(timer);
|
|
1269
|
+
child.stdout?.off("data", onStdout);
|
|
1270
|
+
child.stderr?.off("data", onStderr);
|
|
1271
|
+
child.off("close", onClose);
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
//#endregion
|
|
1380
1276
|
//#region src/acp/model-support.ts
|
|
1381
1277
|
const REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE = "ACP_MODEL_UNSUPPORTED";
|
|
1382
1278
|
const REQUESTED_MODEL_UNSUPPORTED_REASONS = ["missing-capability", "unadvertised-model"];
|
|
@@ -1672,116 +1568,6 @@ function promptRequestOwner(message) {
|
|
|
1672
1568
|
} : void 0;
|
|
1673
1569
|
}
|
|
1674
1570
|
//#endregion
|
|
1675
|
-
//#region src/acp/process-descendants.ts
|
|
1676
|
-
function includeDescendants(table, owned) {
|
|
1677
|
-
let expanded;
|
|
1678
|
-
do {
|
|
1679
|
-
expanded = false;
|
|
1680
|
-
for (const identity of table.values()) {
|
|
1681
|
-
const parent = table.get(identity.parentPid);
|
|
1682
|
-
if (process.platform === "win32" && (!parent || parent.birth > identity.birth)) continue;
|
|
1683
|
-
if (owned.has(identity.parentPid) && !owned.has(identity.pid)) {
|
|
1684
|
-
owned.add(identity.pid);
|
|
1685
|
-
expanded = true;
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
} while (expanded);
|
|
1689
|
-
}
|
|
1690
|
-
/** Best-effort cleanup for descendants witnessed during this child launch. */
|
|
1691
|
-
var ProcessDescendants = class {
|
|
1692
|
-
child;
|
|
1693
|
-
identities = /* @__PURE__ */ new Map();
|
|
1694
|
-
rootBirth;
|
|
1695
|
-
pending;
|
|
1696
|
-
retired = false;
|
|
1697
|
-
ownProcessGroup;
|
|
1698
|
-
captureGroupAfterExit;
|
|
1699
|
-
groupExitedAt;
|
|
1700
|
-
onRootExit = () => {
|
|
1701
|
-
this.groupExitedAt = Date.now();
|
|
1702
|
-
};
|
|
1703
|
-
constructor(child, options = {}) {
|
|
1704
|
-
this.child = child;
|
|
1705
|
-
this.ownProcessGroup = process.platform !== "win32" && Boolean(options.ownProcessGroup);
|
|
1706
|
-
this.captureGroupAfterExit = this.ownProcessGroup && isChildProcessRunning(child);
|
|
1707
|
-
if (this.ownProcessGroup) child.once("exit", this.onRootExit);
|
|
1708
|
-
}
|
|
1709
|
-
capture(timeoutMs = PROCESS_HELPER_TIMEOUT_MS) {
|
|
1710
|
-
if (this.retired) return Promise.resolve(true);
|
|
1711
|
-
this.pending ??= this.readSnapshot(timeoutMs).finally(() => {
|
|
1712
|
-
this.pending = void 0;
|
|
1713
|
-
});
|
|
1714
|
-
return this.pending;
|
|
1715
|
-
}
|
|
1716
|
-
async readSnapshot(timeoutMs) {
|
|
1717
|
-
const deadline = performance.now() + timeoutMs;
|
|
1718
|
-
const rootWasRunning = isChildProcessRunning(this.child);
|
|
1719
|
-
const captureRootGroup = rootWasRunning || this.captureGroupAfterExit;
|
|
1720
|
-
if (!rootWasRunning) this.captureGroupAfterExit = false;
|
|
1721
|
-
try {
|
|
1722
|
-
const table = await readProcessTable(timeoutMs);
|
|
1723
|
-
if (!this.retired) {
|
|
1724
|
-
table.delete(1);
|
|
1725
|
-
table.delete(process.pid);
|
|
1726
|
-
this.refresh(table, captureRootGroup);
|
|
1727
|
-
if (this.captureGroupAfterExit && !isChildProcessRunning(this.child)) return await this.readSnapshot(Math.max(1, deadline - performance.now()));
|
|
1728
|
-
}
|
|
1729
|
-
return true;
|
|
1730
|
-
} catch {
|
|
1731
|
-
if (!isChildProcessRunning(this.child)) this.captureGroupAfterExit = false;
|
|
1732
|
-
return false;
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
refresh(table, rootWasRunning) {
|
|
1736
|
-
const owned = /* @__PURE__ */ new Set();
|
|
1737
|
-
for (const [pid, identity] of this.identities) if (table.get(pid)?.birth === identity.birth) owned.add(pid);
|
|
1738
|
-
this.includeRoot(table, owned);
|
|
1739
|
-
if (this.ownProcessGroup) this.includeProcessGroup(table, owned, rootWasRunning);
|
|
1740
|
-
includeDescendants(table, owned);
|
|
1741
|
-
owned.delete(this.child.pid ?? 0);
|
|
1742
|
-
this.identities = new Map([...table].filter(([pid]) => owned.has(pid)));
|
|
1743
|
-
}
|
|
1744
|
-
includeRoot(table, owned) {
|
|
1745
|
-
const root = this.child.pid;
|
|
1746
|
-
const rootIdentity = root && table.get(root);
|
|
1747
|
-
if (rootIdentity && isChildProcessRunning(this.child)) {
|
|
1748
|
-
this.rootBirth ??= rootIdentity.birth;
|
|
1749
|
-
if (rootIdentity.birth === this.rootBirth) owned.add(rootIdentity.pid);
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
includeProcessGroup(table, owned, rootWasRunning) {
|
|
1753
|
-
const root = this.child.pid;
|
|
1754
|
-
if (root && (rootWasRunning || [...owned].some((pid) => table.get(pid)?.groupPid === root))) {
|
|
1755
|
-
for (const identity of table.values()) if (identity.groupPid === root && (this.groupExitedAt === void 0 || Date.parse(identity.birth) <= this.groupExitedAt)) owned.add(identity.pid);
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
async signal(signal, timeoutMs) {
|
|
1759
|
-
if (!await this.capture(timeoutMs) || this.retired) return;
|
|
1760
|
-
for (const pid of this.identities.keys()) try {
|
|
1761
|
-
process.kill(pid, signal);
|
|
1762
|
-
} catch {
|
|
1763
|
-
this.identities.delete(pid);
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
hasTrackedProcesses() {
|
|
1767
|
-
return this.identities.size > 0;
|
|
1768
|
-
}
|
|
1769
|
-
async waitForExit(timeoutMs) {
|
|
1770
|
-
const deadline = Date.now() + timeoutMs;
|
|
1771
|
-
do {
|
|
1772
|
-
if (!await this.capture(Math.max(1, deadline - Date.now()))) return false;
|
|
1773
|
-
if (this.identities.size === 0) return true;
|
|
1774
|
-
await setTimeout$1(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
1775
|
-
} while (Date.now() < deadline);
|
|
1776
|
-
return false;
|
|
1777
|
-
}
|
|
1778
|
-
retire() {
|
|
1779
|
-
this.retired = true;
|
|
1780
|
-
this.child.off("exit", this.onRootExit);
|
|
1781
|
-
this.identities.clear();
|
|
1782
|
-
}
|
|
1783
|
-
};
|
|
1784
|
-
//#endregion
|
|
1785
1571
|
//#region src/acp/session-control-errors.ts
|
|
1786
1572
|
const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
1787
1573
|
function asRecord$1(value) {
|
|
@@ -2403,26 +2189,13 @@ function childProcessIsRunning(agent) {
|
|
|
2403
2189
|
function cancelledPermissionResponse() {
|
|
2404
2190
|
return { outcome: { outcome: "cancelled" } };
|
|
2405
2191
|
}
|
|
2406
|
-
function shouldSuppressSdkConsoleError(args) {
|
|
2407
|
-
if (args.length === 0) return false;
|
|
2408
|
-
return typeof args[0] === "string" && args[0] === "Error handling request";
|
|
2409
|
-
}
|
|
2410
|
-
function installSdkConsoleErrorSuppression() {
|
|
2411
|
-
const originalConsoleError = console.error;
|
|
2412
|
-
console.error = (...args) => {
|
|
2413
|
-
if (shouldSuppressSdkConsoleError(args)) return;
|
|
2414
|
-
originalConsoleError(...args);
|
|
2415
|
-
};
|
|
2416
|
-
return () => {
|
|
2417
|
-
console.error = originalConsoleError;
|
|
2418
|
-
};
|
|
2419
|
-
}
|
|
2420
2192
|
var AcpClient = class {
|
|
2421
2193
|
options;
|
|
2422
2194
|
connection;
|
|
2423
2195
|
agent;
|
|
2424
2196
|
agentDescendants = /* @__PURE__ */ new WeakMap();
|
|
2425
2197
|
agentCleanups = /* @__PURE__ */ new WeakMap();
|
|
2198
|
+
commandProbes = /* @__PURE__ */ new Set();
|
|
2426
2199
|
initResult;
|
|
2427
2200
|
loadedSessionId;
|
|
2428
2201
|
eventHandlers;
|
|
@@ -2569,11 +2342,11 @@ var AcpClient = class {
|
|
|
2569
2342
|
if (this.hasLiveConnection()) return;
|
|
2570
2343
|
const epoch = await this.waitForPreviousClose();
|
|
2571
2344
|
const maxMessageBytes = readMaxAcpMessageBytes();
|
|
2572
|
-
const launch = await this.resolveAgentLaunchPlan();
|
|
2573
|
-
|
|
2345
|
+
const launch = await this.resolveAgentLaunchPlan(epoch, authority);
|
|
2346
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2574
2347
|
this.logAgentLaunch(launch);
|
|
2575
2348
|
await this.ensureLaunchSupport(launch);
|
|
2576
|
-
|
|
2349
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2577
2350
|
const { child, process: startedProcess } = await this.spawnAgentProcess(launch, authority);
|
|
2578
2351
|
if (this.closeEpoch === epoch) {
|
|
2579
2352
|
this.agent = child;
|
|
@@ -2633,12 +2406,29 @@ var AcpClient = class {
|
|
|
2633
2406
|
if (this.closeEpoch !== epoch) throw new Error("ACP client was closed while the agent was starting");
|
|
2634
2407
|
return epoch;
|
|
2635
2408
|
}
|
|
2636
|
-
async resolveAgentLaunchPlan() {
|
|
2409
|
+
async resolveAgentLaunchPlan(epoch, authority) {
|
|
2637
2410
|
const configuredCommand = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
|
|
2638
2411
|
const resolvedBuiltInLaunch = resolveBuiltInAgentLaunch(this.options.agentCommand);
|
|
2639
2412
|
const spawnCommand = resolvedBuiltInLaunch?.command ?? configuredCommand.command;
|
|
2413
|
+
const spawnOptions = buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials, this.options.sessionOptions?.env, this.options.agentProcessEnv);
|
|
2414
|
+
const commandContext = {
|
|
2415
|
+
env: spawnOptions.env,
|
|
2416
|
+
readOutput: async (command, args, timeoutMs, diagnostic = false) => {
|
|
2417
|
+
try {
|
|
2418
|
+
const output = await this.readAgentCommandOutput(command, args, timeoutMs, spawnOptions, epoch, authority);
|
|
2419
|
+
await admitCommandProbe(() => this.assertCommandProbeActive(epoch, authority));
|
|
2420
|
+
return output;
|
|
2421
|
+
} catch (error) {
|
|
2422
|
+
if (error instanceof CommandProbeAdmissionError) {
|
|
2423
|
+
if (diagnostic) return;
|
|
2424
|
+
throw error.reason;
|
|
2425
|
+
}
|
|
2426
|
+
throw error;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2640
2430
|
let args = resolvedBuiltInLaunch?.args ?? configuredCommand.args;
|
|
2641
|
-
args = await resolveGeminiCommandArgs(spawnCommand, args);
|
|
2431
|
+
args = await resolveGeminiCommandArgs(spawnCommand, args, commandContext);
|
|
2642
2432
|
if (isQoderAcpCommand(spawnCommand, args)) args = buildQoderAcpCommandArgs(args, this.options);
|
|
2643
2433
|
return {
|
|
2644
2434
|
spawnCommand,
|
|
@@ -2648,7 +2438,8 @@ var AcpClient = class {
|
|
|
2648
2438
|
geminiAcp: isGeminiAcpCommand(spawnCommand, args),
|
|
2649
2439
|
copilotAcp: isCopilotAcpCommand(spawnCommand, args),
|
|
2650
2440
|
claudeAcp: isClaudeAcpCommand(spawnCommand, args),
|
|
2651
|
-
spawnOptions
|
|
2441
|
+
spawnOptions,
|
|
2442
|
+
commandContext
|
|
2652
2443
|
};
|
|
2653
2444
|
}
|
|
2654
2445
|
logAgentLaunch(plan) {
|
|
@@ -2664,7 +2455,7 @@ var AcpClient = class {
|
|
|
2664
2455
|
this.log(`spawning agent: ${plan.spawnCommand} ${plan.args.join(" ")}`);
|
|
2665
2456
|
}
|
|
2666
2457
|
async ensureLaunchSupport(plan) {
|
|
2667
|
-
if (plan.copilotAcp) await ensureCopilotAcpSupport(plan.spawnCommand);
|
|
2458
|
+
if (plan.copilotAcp) await ensureCopilotAcpSupport(plan.spawnCommand, plan.commandContext);
|
|
2668
2459
|
if (!plan.claudeAcp) return;
|
|
2669
2460
|
const claudeExe = resolveClaudeCodeExecutable(process.platform, plan.spawnOptions.env);
|
|
2670
2461
|
if (claudeExe) {
|
|
@@ -2672,22 +2463,36 @@ var AcpClient = class {
|
|
|
2672
2463
|
this.log(`resolved system Claude Code executable: ${claudeExe}`);
|
|
2673
2464
|
}
|
|
2674
2465
|
}
|
|
2675
|
-
|
|
2676
|
-
const
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2466
|
+
prepareProcessLaunch(command, args, options) {
|
|
2467
|
+
const resolved = buildAgentSpawnCommand(command, args, process.platform, options.env);
|
|
2468
|
+
return {
|
|
2469
|
+
launch: Object.freeze({
|
|
2470
|
+
launchId: randomUUID(),
|
|
2471
|
+
scope: snapshotProcessLaunchScope(this.options.processLaunchScope),
|
|
2472
|
+
command: resolved.command,
|
|
2473
|
+
args: Object.freeze([...resolved.args]),
|
|
2474
|
+
cwd: options.cwd
|
|
2475
|
+
}),
|
|
2476
|
+
windowsVerbatimArguments: resolved.windowsVerbatimArguments
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
startedProcess(launch, pid) {
|
|
2480
|
+
return Object.freeze({
|
|
2481
|
+
...launch,
|
|
2482
|
+
pid,
|
|
2483
|
+
startedAt: isoNow$2()
|
|
2683
2484
|
});
|
|
2485
|
+
}
|
|
2486
|
+
async spawnAgentProcess(plan, authority) {
|
|
2487
|
+
const prepared = this.prepareProcessLaunch(plan.spawnCommand, plan.args, plan.spawnOptions);
|
|
2488
|
+
const { launch } = prepared;
|
|
2684
2489
|
await this.options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
2685
2490
|
assertControlAuthority(authority);
|
|
2686
2491
|
let spawnedChild;
|
|
2687
2492
|
try {
|
|
2688
|
-
spawnedChild = spawn(
|
|
2493
|
+
spawnedChild = spawn(launch.command, [...launch.args], {
|
|
2689
2494
|
...plan.spawnOptions,
|
|
2690
|
-
windowsVerbatimArguments:
|
|
2495
|
+
windowsVerbatimArguments: prepared.windowsVerbatimArguments
|
|
2691
2496
|
});
|
|
2692
2497
|
await waitForSpawn(spawnedChild);
|
|
2693
2498
|
} catch (error) {
|
|
@@ -2706,21 +2511,86 @@ var AcpClient = class {
|
|
|
2706
2511
|
}
|
|
2707
2512
|
return {
|
|
2708
2513
|
child,
|
|
2709
|
-
process:
|
|
2710
|
-
...launch,
|
|
2711
|
-
pid,
|
|
2712
|
-
startedAt: isoNow$2()
|
|
2713
|
-
})
|
|
2514
|
+
process: this.startedProcess(launch, pid)
|
|
2714
2515
|
};
|
|
2715
2516
|
}
|
|
2517
|
+
assertCommandProbeActive(epoch, authority) {
|
|
2518
|
+
assertControlAuthority(authority);
|
|
2519
|
+
if (this.closeEpoch !== epoch) throw new Error("ACP client was closed while the agent was starting");
|
|
2520
|
+
}
|
|
2521
|
+
async readAgentCommandOutput(command, args, timeoutMs, options, epoch, authority) {
|
|
2522
|
+
const prepared = this.prepareProcessLaunch(command, args, options);
|
|
2523
|
+
const { launch } = prepared;
|
|
2524
|
+
await admitCommandProbe(async () => {
|
|
2525
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2526
|
+
await this.options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
2527
|
+
});
|
|
2528
|
+
try {
|
|
2529
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2530
|
+
} catch (error) {
|
|
2531
|
+
throw new CommandProbeAdmissionError(error);
|
|
2532
|
+
}
|
|
2533
|
+
let child;
|
|
2534
|
+
try {
|
|
2535
|
+
child = spawn(launch.command, [...launch.args], {
|
|
2536
|
+
cwd: options.cwd,
|
|
2537
|
+
env: options.env,
|
|
2538
|
+
stdio: [
|
|
2539
|
+
"ignore",
|
|
2540
|
+
"pipe",
|
|
2541
|
+
"pipe"
|
|
2542
|
+
],
|
|
2543
|
+
windowsHide: true,
|
|
2544
|
+
windowsVerbatimArguments: prepared.windowsVerbatimArguments,
|
|
2545
|
+
detached: process.platform !== "win32"
|
|
2546
|
+
});
|
|
2547
|
+
} catch (error) {
|
|
2548
|
+
this.notifyProcessSpawnFailure(launch, new AgentSpawnError(command, error));
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
this.commandProbes.add(child);
|
|
2552
|
+
this.agentDescendants.set(child, new ProcessDescendants(child, { ownProcessGroup: true }));
|
|
2553
|
+
const capture = captureCommandProbeOutput(child, timeoutMs, () => this.terminateAgentProcess(child));
|
|
2554
|
+
try {
|
|
2555
|
+
try {
|
|
2556
|
+
await waitForSpawn(child);
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
this.notifyProcessSpawnFailure(launch, new AgentSpawnError(command, error));
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
if (child.pid === void 0) {
|
|
2562
|
+
this.notifyProcessSpawnFailure(launch, new AgentSpawnError(command, /* @__PURE__ */ new Error("spawned probe did not expose a PID")));
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
const started = this.startedProcess(launch, child.pid);
|
|
2566
|
+
await this.admitSpawnedProcess(child, (barrier) => this.attachProcessExitObserver(child, started, barrier), () => admitCommandProbe(async () => {
|
|
2567
|
+
await this.options.processLifecycle?.onSpawned?.(started);
|
|
2568
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2569
|
+
}));
|
|
2570
|
+
const output = await capture.result;
|
|
2571
|
+
await admitCommandProbe(() => this.assertCommandProbeActive(epoch, authority));
|
|
2572
|
+
return output;
|
|
2573
|
+
} finally {
|
|
2574
|
+
try {
|
|
2575
|
+
await this.terminateAgentProcess(child);
|
|
2576
|
+
} finally {
|
|
2577
|
+
capture.dispose();
|
|
2578
|
+
this.commandProbes.delete(child);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2716
2582
|
async admitAndObserveSpawnedProcess(child, process) {
|
|
2583
|
+
await this.admitSpawnedProcess(child, (barrier) => this.attachAgentLifecycleObservers(child, process, barrier), async () => {
|
|
2584
|
+
await this.options.processLifecycle?.onSpawned?.(process);
|
|
2585
|
+
});
|
|
2586
|
+
}
|
|
2587
|
+
async admitSpawnedProcess(child, observe, admit) {
|
|
2717
2588
|
let releaseExitNotification = () => {};
|
|
2718
|
-
|
|
2589
|
+
observe(new Promise((resolve) => {
|
|
2719
2590
|
releaseExitNotification = resolve;
|
|
2720
|
-
});
|
|
2721
|
-
this.attachAgentLifecycleObservers(child, process, exitNotificationBarrier);
|
|
2591
|
+
}));
|
|
2722
2592
|
try {
|
|
2723
|
-
await
|
|
2593
|
+
await admit();
|
|
2724
2594
|
} catch (error) {
|
|
2725
2595
|
await this.terminateAgentProcess(child);
|
|
2726
2596
|
throw error;
|
|
@@ -2786,7 +2656,7 @@ var AcpClient = class {
|
|
|
2786
2656
|
await this.captureAgentDescendants(params.child);
|
|
2787
2657
|
const normalizedError = error instanceof AcpMessageLimitError ? error : await this.normalizeInitializeError(error, params.child, params.startupStderr);
|
|
2788
2658
|
await this.terminateAgentProcess(params.child);
|
|
2789
|
-
if (params.launch.geminiAcp && error instanceof TimeoutError) throw new GeminiAcpStartupTimeoutError(await buildGeminiAcpStartupTimeoutMessage(params.launch.spawnCommand), {
|
|
2659
|
+
if (params.launch.geminiAcp && error instanceof TimeoutError) throw new GeminiAcpStartupTimeoutError(await buildGeminiAcpStartupTimeoutMessage(params.launch.spawnCommand, params.launch.commandContext), {
|
|
2790
2660
|
cause: error,
|
|
2791
2661
|
retryable: true
|
|
2792
2662
|
});
|
|
@@ -2896,7 +2766,6 @@ var AcpClient = class {
|
|
|
2896
2766
|
async prompt(sessionId, prompt, onRequestWritten, onElicitation, onPermissionRequest, authority) {
|
|
2897
2767
|
const connection = this.getConnection();
|
|
2898
2768
|
const normalizedPrompt = this.normalizePromptForAgent(prompt);
|
|
2899
|
-
const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
|
|
2900
2769
|
const previousActivePrompt = this.activePrompt;
|
|
2901
2770
|
const replacedOwners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
2902
2771
|
const activePrompt = this.beginActivePrompt(sessionId, onRequestWritten, onElicitation, onPermissionRequest, authority);
|
|
@@ -2914,7 +2783,6 @@ var AcpClient = class {
|
|
|
2914
2783
|
this.throwPromptPermissionFailureIfPresent(sessionId);
|
|
2915
2784
|
throw error;
|
|
2916
2785
|
} finally {
|
|
2917
|
-
restoreConsoleError?.();
|
|
2918
2786
|
this.clearActivePrompt(activePrompt);
|
|
2919
2787
|
}
|
|
2920
2788
|
}
|
|
@@ -3133,18 +3001,15 @@ var AcpClient = class {
|
|
|
3133
3001
|
this.agent = void 0;
|
|
3134
3002
|
}
|
|
3135
3003
|
async retireNativeResources() {
|
|
3136
|
-
const
|
|
3004
|
+
const owned = [...this.commandProbes, ...this.agent ? [this.agent] : []];
|
|
3137
3005
|
const failures = [];
|
|
3138
3006
|
try {
|
|
3139
3007
|
await this.terminalManager.shutdown();
|
|
3140
3008
|
} catch (error) {
|
|
3141
3009
|
failures.push(error);
|
|
3142
3010
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
} catch (error) {
|
|
3146
|
-
failures.push(error);
|
|
3147
|
-
}
|
|
3011
|
+
const retired = await Promise.allSettled(owned.map((child) => this.terminateAgentProcess(child)));
|
|
3012
|
+
for (const result of retired) if (result.status === "rejected") failures.push(result.reason);
|
|
3148
3013
|
try {
|
|
3149
3014
|
this.closeConnection();
|
|
3150
3015
|
} finally {
|
|
@@ -3170,6 +3035,11 @@ var AcpClient = class {
|
|
|
3170
3035
|
if (descendants && !await descendants.capture()) this.log("could not verify agent descendants; skipping unverified process cleanup");
|
|
3171
3036
|
}
|
|
3172
3037
|
terminateAgentProcess(child) {
|
|
3038
|
+
if (child.pid === void 0) {
|
|
3039
|
+
this.agentDescendants.get(child)?.retire();
|
|
3040
|
+
this.detachAgentHandles(child, true);
|
|
3041
|
+
return Promise.resolve();
|
|
3042
|
+
}
|
|
3173
3043
|
let cleanup = this.agentCleanups.get(child);
|
|
3174
3044
|
if (!cleanup) {
|
|
3175
3045
|
cleanup = Promise.resolve().then(() => this.cleanupAgentProcess(child));
|
|
@@ -3179,12 +3049,12 @@ var AcpClient = class {
|
|
|
3179
3049
|
}
|
|
3180
3050
|
async cleanupAgentProcess(child) {
|
|
3181
3051
|
const descendants = this.agentDescendants.get(child);
|
|
3182
|
-
const deadline =
|
|
3052
|
+
const deadline = performance.now() + AGENT_CLEANUP_BUDGET_MS;
|
|
3183
3053
|
const stdinCloseGraceMs = resolveAgentCloseAfterStdinEndMs(this.options.agentCommand);
|
|
3184
3054
|
try {
|
|
3185
|
-
if (descendants) await descendants.capture(Math.max(1, deadline -
|
|
3055
|
+
if (descendants) await descendants.capture(Math.max(1, deadline - performance.now()));
|
|
3186
3056
|
this.endAgentStdin(child);
|
|
3187
|
-
await waitForChildExit(child, Math.min(stdinCloseGraceMs, Math.max(0, deadline -
|
|
3057
|
+
await waitForChildExit(child, Math.min(stdinCloseGraceMs, Math.max(0, deadline - performance.now())));
|
|
3188
3058
|
if (!await this.signalAgentAndDescendants(child, "SIGTERM", AGENT_CLOSE_TERM_GRACE_MS, deadline)) {
|
|
3189
3059
|
this.log("agent processes did not exit after SIGTERM; forcing SIGKILL");
|
|
3190
3060
|
await this.signalAgentAndDescendants(child, "SIGKILL", AGENT_CLOSE_KILL_GRACE_MS, deadline);
|
|
@@ -3195,18 +3065,18 @@ var AcpClient = class {
|
|
|
3195
3065
|
}
|
|
3196
3066
|
}
|
|
3197
3067
|
endAgentStdin(child) {
|
|
3198
|
-
if (child.stdin.destroyed) return;
|
|
3068
|
+
if (!child.stdin || child.stdin.destroyed) return;
|
|
3199
3069
|
try {
|
|
3200
3070
|
child.stdin.end();
|
|
3201
3071
|
} catch {}
|
|
3202
3072
|
}
|
|
3203
3073
|
async signalAgentAndDescendants(child, signal, waitMs, deadline) {
|
|
3204
3074
|
const descendants = this.agentDescendants.get(child);
|
|
3205
|
-
if (descendants &&
|
|
3075
|
+
if (descendants && performance.now() < deadline) await descendants.signal(signal, deadline - performance.now());
|
|
3206
3076
|
if (isChildProcessRunning(child)) try {
|
|
3207
3077
|
child.kill(signal);
|
|
3208
3078
|
} catch {}
|
|
3209
|
-
const remaining = Math.min(waitMs, Math.max(0, deadline -
|
|
3079
|
+
const remaining = Math.min(waitMs, Math.max(0, deadline - performance.now()));
|
|
3210
3080
|
const [exited, descendantsExited] = await Promise.all([waitForChildExit(child, remaining), descendants ? descendants.waitForExit(remaining) : Promise.resolve(true)]);
|
|
3211
3081
|
return exited && descendantsExited;
|
|
3212
3082
|
}
|
|
@@ -3516,19 +3386,10 @@ var AcpClient = class {
|
|
|
3516
3386
|
return cancelledPermissionResponse();
|
|
3517
3387
|
}
|
|
3518
3388
|
attachAgentLifecycleObservers(child, startedProcess, exitNotificationBarrier) {
|
|
3519
|
-
|
|
3520
|
-
const exitedAt = isoNow$2();
|
|
3389
|
+
this.attachProcessExitObserver(child, startedProcess, exitNotificationBarrier, (exitCode, signal) => {
|
|
3521
3390
|
this.handleAgentDisconnect(child, "process_exit", exitCode, signal);
|
|
3522
3391
|
this.terminateAgentProcess(child);
|
|
3523
|
-
|
|
3524
|
-
this.notifyProcessExit(startedProcess, exitCode, signal, exitedAt);
|
|
3525
|
-
});
|
|
3526
|
-
};
|
|
3527
|
-
child.once("exit", onExit);
|
|
3528
|
-
if (child.exitCode !== null || child.signalCode !== null) {
|
|
3529
|
-
child.off("exit", onExit);
|
|
3530
|
-
onExit(child.exitCode, child.signalCode);
|
|
3531
|
-
}
|
|
3392
|
+
});
|
|
3532
3393
|
child.once("close", (exitCode, signal) => {
|
|
3533
3394
|
this.handleAgentDisconnect(child, "process_close", exitCode, signal);
|
|
3534
3395
|
});
|
|
@@ -3536,6 +3397,18 @@ var AcpClient = class {
|
|
|
3536
3397
|
this.handleAgentDisconnect(child, "pipe_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
3537
3398
|
});
|
|
3538
3399
|
}
|
|
3400
|
+
attachProcessExitObserver(child, started, barrier, onProcessExit) {
|
|
3401
|
+
const onExit = (exitCode, signal) => {
|
|
3402
|
+
const exitedAt = isoNow$2();
|
|
3403
|
+
onProcessExit?.(exitCode, signal);
|
|
3404
|
+
barrier.then(() => this.notifyProcessExit(started, exitCode, signal, exitedAt));
|
|
3405
|
+
};
|
|
3406
|
+
child.once("exit", onExit);
|
|
3407
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
3408
|
+
child.off("exit", onExit);
|
|
3409
|
+
onExit(child.exitCode, child.signalCode);
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3539
3412
|
handleAgentDisconnect(child, reason, exitCode, signal) {
|
|
3540
3413
|
this.recordAgentExit(child, reason, exitCode, signal);
|
|
3541
3414
|
if (this.agent !== child || this.closing || !this.connection) return;
|
|
@@ -4353,6 +4226,11 @@ function mergeSessionOptions(preferred, fallback) {
|
|
|
4353
4226
|
assignDefinedOption(merged, "env", mergeEnvRecords(fallback?.env, preferred?.env));
|
|
4354
4227
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
4355
4228
|
}
|
|
4229
|
+
function normalizeSessionOptionShape(options) {
|
|
4230
|
+
const normalized = mergeSessionOptions(options, void 0);
|
|
4231
|
+
if (normalized?.env !== void 0 && Object.keys(normalized.env).length === 0) delete normalized.env;
|
|
4232
|
+
return normalized && Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
4233
|
+
}
|
|
4356
4234
|
function mergeEnvRecords(fallback, preferred) {
|
|
4357
4235
|
if (!fallback && !preferred) return;
|
|
4358
4236
|
return {
|
|
@@ -4804,406 +4682,6 @@ function directExecutionError(error, signal) {
|
|
|
4804
4682
|
return signal?.aborted && error instanceof AgentDisconnectedError ? signal.reason : error;
|
|
4805
4683
|
}
|
|
4806
4684
|
//#endregion
|
|
4807
|
-
//#region src/session/queue/ipc-server.ts
|
|
4808
|
-
const QUEUE_SOCKET_DRAIN_TIMEOUT_MS = 1e3;
|
|
4809
|
-
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
4810
|
-
return {
|
|
4811
|
-
type: "error",
|
|
4812
|
-
requestId,
|
|
4813
|
-
ownerGeneration: void 0,
|
|
4814
|
-
code: "RUNTIME",
|
|
4815
|
-
detailCode,
|
|
4816
|
-
origin: "queue",
|
|
4817
|
-
retryable: options.retryable,
|
|
4818
|
-
message
|
|
4819
|
-
};
|
|
4820
|
-
}
|
|
4821
|
-
function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
|
|
4822
|
-
const normalized = normalizeOutputError(error, {
|
|
4823
|
-
defaultCode: "RUNTIME",
|
|
4824
|
-
origin: "queue",
|
|
4825
|
-
detailCode,
|
|
4826
|
-
retryable: options.retryable
|
|
4827
|
-
});
|
|
4828
|
-
return {
|
|
4829
|
-
type: "error",
|
|
4830
|
-
requestId,
|
|
4831
|
-
code: normalized.code,
|
|
4832
|
-
detailCode: normalized.detailCode,
|
|
4833
|
-
origin: normalized.origin,
|
|
4834
|
-
message: normalized.message,
|
|
4835
|
-
retryable: normalized.retryable,
|
|
4836
|
-
acp: normalized.acp
|
|
4837
|
-
};
|
|
4838
|
-
}
|
|
4839
|
-
function armSocketDrainTimeout(socket) {
|
|
4840
|
-
if (process.platform !== "win32") socket.setTimeout(QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
|
|
4841
|
-
}
|
|
4842
|
-
function writeQueueMessage(socket, message) {
|
|
4843
|
-
if (socket.destroyed || !socket.writable) return;
|
|
4844
|
-
if (!socket.write(`${JSON.stringify(message)}\n`) && !socket.timeout) armSocketDrainTimeout(socket);
|
|
4845
|
-
}
|
|
4846
|
-
var SessionQueueOwner = class SessionQueueOwner {
|
|
4847
|
-
server;
|
|
4848
|
-
controlHandlers;
|
|
4849
|
-
ownerGeneration;
|
|
4850
|
-
maxQueueDepth;
|
|
4851
|
-
maxRequestBytes;
|
|
4852
|
-
onQueueDepthChanged;
|
|
4853
|
-
pending = [];
|
|
4854
|
-
activeTask;
|
|
4855
|
-
waiters = [];
|
|
4856
|
-
sockets = /* @__PURE__ */ new Set();
|
|
4857
|
-
taskSockets = /* @__PURE__ */ new Set();
|
|
4858
|
-
drainingSockets = /* @__PURE__ */ new Set();
|
|
4859
|
-
closed = false;
|
|
4860
|
-
constructor(server, controlHandlers, lease, options) {
|
|
4861
|
-
this.server = server;
|
|
4862
|
-
this.controlHandlers = controlHandlers;
|
|
4863
|
-
this.ownerGeneration = lease.ownerGeneration;
|
|
4864
|
-
this.maxQueueDepth = Math.max(1, Math.round(options.maxQueueDepth));
|
|
4865
|
-
this.maxRequestBytes = queueRequestByteLimit(options.maxRequestBytes?.toString());
|
|
4866
|
-
this.onQueueDepthChanged = options.onQueueDepthChanged;
|
|
4867
|
-
}
|
|
4868
|
-
static async start(lease, controlHandlers, options = { maxQueueDepth: 16 }) {
|
|
4869
|
-
const ownerRef = { current: void 0 };
|
|
4870
|
-
const server = net.createServer((socket) => {
|
|
4871
|
-
ownerRef.current?.handleConnection(socket);
|
|
4872
|
-
});
|
|
4873
|
-
ownerRef.current = new SessionQueueOwner(server, controlHandlers, lease, options);
|
|
4874
|
-
await new Promise((resolve, reject) => {
|
|
4875
|
-
const onListening = () => {
|
|
4876
|
-
server.off("error", onError);
|
|
4877
|
-
resolve();
|
|
4878
|
-
};
|
|
4879
|
-
const onError = (error) => {
|
|
4880
|
-
server.off("listening", onListening);
|
|
4881
|
-
reject(error);
|
|
4882
|
-
};
|
|
4883
|
-
server.once("listening", onListening);
|
|
4884
|
-
server.once("error", onError);
|
|
4885
|
-
server.listen(lease.socketPath);
|
|
4886
|
-
});
|
|
4887
|
-
return ownerRef.current;
|
|
4888
|
-
}
|
|
4889
|
-
beginShutdown() {
|
|
4890
|
-
if (this.closed) return;
|
|
4891
|
-
this.closed = true;
|
|
4892
|
-
for (const waiter of this.waiters.splice(0)) waiter(void 0);
|
|
4893
|
-
for (const task of this.pending.splice(0)) {
|
|
4894
|
-
if (task.waitForCompletion) task.send(makeQueueOwnerError(task.requestId, "Queue owner shutting down before prompt execution", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
|
|
4895
|
-
task.close();
|
|
4896
|
-
}
|
|
4897
|
-
for (const socket of this.sockets) if (!this.taskSockets.has(socket) && !this.drainingSockets.has(socket)) socket.destroy();
|
|
4898
|
-
this.emitQueueDepth();
|
|
4899
|
-
}
|
|
4900
|
-
async close() {
|
|
4901
|
-
this.beginShutdown();
|
|
4902
|
-
await new Promise((resolve) => {
|
|
4903
|
-
const forceCloseTimer = setTimeout(() => {
|
|
4904
|
-
for (const socket of this.sockets) socket.destroy();
|
|
4905
|
-
}, QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
|
|
4906
|
-
forceCloseTimer.unref();
|
|
4907
|
-
this.server.close(() => {
|
|
4908
|
-
clearTimeout(forceCloseTimer);
|
|
4909
|
-
resolve();
|
|
4910
|
-
});
|
|
4911
|
-
});
|
|
4912
|
-
}
|
|
4913
|
-
async nextTask(timeoutMs) {
|
|
4914
|
-
if (this.pending.length > 0) {
|
|
4915
|
-
const task = this.pending.shift();
|
|
4916
|
-
this.emitQueueDepth();
|
|
4917
|
-
if (task) {
|
|
4918
|
-
this.activeTask = task;
|
|
4919
|
-
recordPerfDuration("queue.owner.wait_ms", Date.now() - task.enqueuedAt);
|
|
4920
|
-
}
|
|
4921
|
-
return task;
|
|
4922
|
-
}
|
|
4923
|
-
if (this.closed) return;
|
|
4924
|
-
return await new Promise((resolve) => {
|
|
4925
|
-
const timer = timeoutMs != null && setTimeout(() => {
|
|
4926
|
-
const index = this.waiters.indexOf(waiter);
|
|
4927
|
-
if (index >= 0) this.waiters.splice(index, 1);
|
|
4928
|
-
resolve(void 0);
|
|
4929
|
-
}, Math.max(0, timeoutMs));
|
|
4930
|
-
const waiter = (task) => {
|
|
4931
|
-
if (timer) clearTimeout(timer);
|
|
4932
|
-
resolve(task);
|
|
4933
|
-
};
|
|
4934
|
-
this.waiters.push(waiter);
|
|
4935
|
-
});
|
|
4936
|
-
}
|
|
4937
|
-
queueDepth() {
|
|
4938
|
-
return this.pending.length;
|
|
4939
|
-
}
|
|
4940
|
-
completeTask(task) {
|
|
4941
|
-
if (this.activeTask === task) this.activeTask = void 0;
|
|
4942
|
-
}
|
|
4943
|
-
async cancelPrompt(targetRequestId) {
|
|
4944
|
-
if (targetRequestId === void 0) return await this.controlHandlers.cancelPrompt();
|
|
4945
|
-
const index = this.pending.findIndex((task) => task.requestId === targetRequestId);
|
|
4946
|
-
if (index >= 0) {
|
|
4947
|
-
const [task] = this.pending.splice(index, 1);
|
|
4948
|
-
task.send(makeQueueOwnerError(task.requestId, "Prompt cancelled before execution", "QUEUE_REQUEST_CANCELLED", { retryable: false }));
|
|
4949
|
-
task.close();
|
|
4950
|
-
this.emitQueueDepth();
|
|
4951
|
-
return true;
|
|
4952
|
-
}
|
|
4953
|
-
return this.activeTask?.requestId === targetRequestId ? await this.controlHandlers.cancelPrompt() : false;
|
|
4954
|
-
}
|
|
4955
|
-
emitQueueDepth() {
|
|
4956
|
-
this.onQueueDepthChanged?.(this.pending.length);
|
|
4957
|
-
}
|
|
4958
|
-
enqueue(task) {
|
|
4959
|
-
if (this.closed) {
|
|
4960
|
-
task.send(makeQueueOwnerError(task.requestId, "Queue owner is shutting down", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
|
|
4961
|
-
task.close();
|
|
4962
|
-
return false;
|
|
4963
|
-
}
|
|
4964
|
-
if (this.activeTask?.requestId === task.requestId || this.pending.some((queued) => queued.requestId === task.requestId)) {
|
|
4965
|
-
task.send(makeQueueOwnerError(task.requestId, "Request is already queued or active", "QUEUE_REQUEST_DUPLICATE", { retryable: false }));
|
|
4966
|
-
task.close();
|
|
4967
|
-
return false;
|
|
4968
|
-
}
|
|
4969
|
-
const waiter = this.waiters.shift();
|
|
4970
|
-
if (waiter) {
|
|
4971
|
-
this.activeTask = task;
|
|
4972
|
-
waiter(task);
|
|
4973
|
-
return true;
|
|
4974
|
-
}
|
|
4975
|
-
if (this.pending.length >= this.maxQueueDepth) {
|
|
4976
|
-
task.send({
|
|
4977
|
-
...makeQueueOwnerError(task.requestId, `Queue owner is overloaded (${this.pending.length}/${this.maxQueueDepth} queued)`, "QUEUE_OWNER_OVERLOADED", { retryable: true }),
|
|
4978
|
-
ownerGeneration: this.ownerGeneration
|
|
4979
|
-
});
|
|
4980
|
-
task.close();
|
|
4981
|
-
return false;
|
|
4982
|
-
}
|
|
4983
|
-
this.pending.push(task);
|
|
4984
|
-
this.emitQueueDepth();
|
|
4985
|
-
return true;
|
|
4986
|
-
}
|
|
4987
|
-
handleControlRequest(options) {
|
|
4988
|
-
this.taskSockets.add(options.socket);
|
|
4989
|
-
writeQueueMessage(options.socket, {
|
|
4990
|
-
type: "accepted",
|
|
4991
|
-
requestId: options.requestId,
|
|
4992
|
-
ownerGeneration: this.ownerGeneration
|
|
4993
|
-
});
|
|
4994
|
-
options.run().then((message) => {
|
|
4995
|
-
writeQueueMessage(options.socket, {
|
|
4996
|
-
...message,
|
|
4997
|
-
ownerGeneration: this.ownerGeneration
|
|
4998
|
-
});
|
|
4999
|
-
}).catch((error) => {
|
|
5000
|
-
writeQueueMessage(options.socket, {
|
|
5001
|
-
...makeQueueOwnerErrorFromUnknown(options.requestId, error, "QUEUE_CONTROL_REQUEST_FAILED"),
|
|
5002
|
-
ownerGeneration: this.ownerGeneration
|
|
5003
|
-
});
|
|
5004
|
-
}).finally(() => {
|
|
5005
|
-
this.endSocket(options.socket);
|
|
5006
|
-
});
|
|
5007
|
-
}
|
|
5008
|
-
endSocket(socket) {
|
|
5009
|
-
if (socket.destroyed || this.drainingSockets.has(socket)) return;
|
|
5010
|
-
this.taskSockets.delete(socket);
|
|
5011
|
-
this.drainingSockets.add(socket);
|
|
5012
|
-
armSocketDrainTimeout(socket);
|
|
5013
|
-
socket.end(() => {
|
|
5014
|
-
this.drainingSockets.delete(socket);
|
|
5015
|
-
socket.destroy();
|
|
5016
|
-
});
|
|
5017
|
-
}
|
|
5018
|
-
failRequest(socket, requestId, message, detailCode) {
|
|
5019
|
-
writeQueueMessage(socket, {
|
|
5020
|
-
...makeQueueOwnerError(requestId, message, detailCode, { retryable: false }),
|
|
5021
|
-
ownerGeneration: this.ownerGeneration
|
|
5022
|
-
});
|
|
5023
|
-
this.endSocket(socket);
|
|
5024
|
-
}
|
|
5025
|
-
parseRequestLine(socket, line) {
|
|
5026
|
-
let parsed;
|
|
5027
|
-
try {
|
|
5028
|
-
parsed = JSON.parse(line);
|
|
5029
|
-
} catch {
|
|
5030
|
-
this.failRequest(socket, "unknown", "Invalid queue request payload", "QUEUE_REQUEST_PAYLOAD_INVALID_JSON");
|
|
5031
|
-
return;
|
|
5032
|
-
}
|
|
5033
|
-
const request = parseQueueRequest(parsed);
|
|
5034
|
-
if (!request) this.failRequest(socket, "unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
5035
|
-
return request ?? void 0;
|
|
5036
|
-
}
|
|
5037
|
-
rejectStaleOwnerGeneration(socket, request) {
|
|
5038
|
-
if (request.ownerGeneration === void 0 || this.ownerGeneration === void 0 || request.ownerGeneration === this.ownerGeneration) return false;
|
|
5039
|
-
this.failRequest(socket, request.requestId, "Queue request targeted a stale queue owner generation", "QUEUE_OWNER_GENERATION_MISMATCH");
|
|
5040
|
-
return true;
|
|
5041
|
-
}
|
|
5042
|
-
rejectClosedOwner(socket, request) {
|
|
5043
|
-
if (!this.closed) return false;
|
|
5044
|
-
writeQueueMessage(socket, {
|
|
5045
|
-
...makeQueueOwnerError(request.requestId, "Queue owner is shutting down", "QUEUE_OWNER_CLOSED", { retryable: true }),
|
|
5046
|
-
ownerGeneration: this.ownerGeneration
|
|
5047
|
-
});
|
|
5048
|
-
this.endSocket(socket);
|
|
5049
|
-
return true;
|
|
5050
|
-
}
|
|
5051
|
-
handleControlQueueRequest(socket, request) {
|
|
5052
|
-
if (request.type === "cancel_prompt") {
|
|
5053
|
-
this.handleControlRequest({
|
|
5054
|
-
socket,
|
|
5055
|
-
requestId: request.requestId,
|
|
5056
|
-
run: async () => ({
|
|
5057
|
-
type: "cancel_result",
|
|
5058
|
-
requestId: request.requestId,
|
|
5059
|
-
cancelled: await this.cancelPrompt(request.targetRequestId)
|
|
5060
|
-
})
|
|
5061
|
-
});
|
|
5062
|
-
return true;
|
|
5063
|
-
}
|
|
5064
|
-
if (request.type === "close_session") {
|
|
5065
|
-
this.handleControlRequest({
|
|
5066
|
-
socket,
|
|
5067
|
-
requestId: request.requestId,
|
|
5068
|
-
run: async () => ({
|
|
5069
|
-
type: "close_session_result",
|
|
5070
|
-
requestId: request.requestId,
|
|
5071
|
-
closed: await this.controlHandlers.closeSession(request.timeoutMs)
|
|
5072
|
-
})
|
|
5073
|
-
});
|
|
5074
|
-
return true;
|
|
5075
|
-
}
|
|
5076
|
-
return this.handleSessionControlQueueRequest(socket, request);
|
|
5077
|
-
}
|
|
5078
|
-
handleSessionControlQueueRequest(socket, request) {
|
|
5079
|
-
if (request.type === "set_mode") {
|
|
5080
|
-
this.handleControlRequest({
|
|
5081
|
-
socket,
|
|
5082
|
-
requestId: request.requestId,
|
|
5083
|
-
run: async () => {
|
|
5084
|
-
await this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs);
|
|
5085
|
-
return {
|
|
5086
|
-
type: "set_mode_result",
|
|
5087
|
-
requestId: request.requestId,
|
|
5088
|
-
modeId: request.modeId
|
|
5089
|
-
};
|
|
5090
|
-
}
|
|
5091
|
-
});
|
|
5092
|
-
return true;
|
|
5093
|
-
}
|
|
5094
|
-
if (request.type === "set_model") {
|
|
5095
|
-
this.handleControlRequest({
|
|
5096
|
-
socket,
|
|
5097
|
-
requestId: request.requestId,
|
|
5098
|
-
run: async () => {
|
|
5099
|
-
const response = await this.controlHandlers.setSessionModel(request.modelId, request.timeoutMs);
|
|
5100
|
-
return {
|
|
5101
|
-
type: "set_model_result",
|
|
5102
|
-
requestId: request.requestId,
|
|
5103
|
-
modelId: request.modelId,
|
|
5104
|
-
response
|
|
5105
|
-
};
|
|
5106
|
-
}
|
|
5107
|
-
});
|
|
5108
|
-
return true;
|
|
5109
|
-
}
|
|
5110
|
-
if (request.type === "set_config_option") {
|
|
5111
|
-
this.handleControlRequest({
|
|
5112
|
-
socket,
|
|
5113
|
-
requestId: request.requestId,
|
|
5114
|
-
run: async () => ({
|
|
5115
|
-
type: "set_config_option_result",
|
|
5116
|
-
requestId: request.requestId,
|
|
5117
|
-
response: await this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs)
|
|
5118
|
-
})
|
|
5119
|
-
});
|
|
5120
|
-
return true;
|
|
5121
|
-
}
|
|
5122
|
-
return false;
|
|
5123
|
-
}
|
|
5124
|
-
enqueuePromptRequest(socket, request) {
|
|
5125
|
-
this.taskSockets.add(socket);
|
|
5126
|
-
const task = {
|
|
5127
|
-
requestId: request.requestId,
|
|
5128
|
-
message: request.message,
|
|
5129
|
-
prompt: request.prompt ?? textPrompt(request.message),
|
|
5130
|
-
permissionMode: request.permissionMode,
|
|
5131
|
-
resumePolicy: request.resumePolicy,
|
|
5132
|
-
nonInteractivePermissions: request.nonInteractivePermissions,
|
|
5133
|
-
permissionPolicy: request.permissionPolicy,
|
|
5134
|
-
timeoutMs: request.timeoutMs,
|
|
5135
|
-
suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
|
|
5136
|
-
promptRetries: request.promptRetries,
|
|
5137
|
-
sessionOptions: request.sessionOptions,
|
|
5138
|
-
waitForCompletion: request.waitForCompletion,
|
|
5139
|
-
reportPromptStarted: request.reportPromptStarted,
|
|
5140
|
-
enqueuedAt: Date.now(),
|
|
5141
|
-
send: (message) => {
|
|
5142
|
-
writeQueueMessage(socket, {
|
|
5143
|
-
...message,
|
|
5144
|
-
ownerGeneration: this.ownerGeneration
|
|
5145
|
-
});
|
|
5146
|
-
},
|
|
5147
|
-
close: () => {
|
|
5148
|
-
this.endSocket(socket);
|
|
5149
|
-
}
|
|
5150
|
-
};
|
|
5151
|
-
if (!this.enqueue(task)) return;
|
|
5152
|
-
writeQueueMessage(socket, {
|
|
5153
|
-
type: "accepted",
|
|
5154
|
-
requestId: request.requestId,
|
|
5155
|
-
ownerGeneration: this.ownerGeneration
|
|
5156
|
-
});
|
|
5157
|
-
if (!request.waitForCompletion) task.close();
|
|
5158
|
-
}
|
|
5159
|
-
handleConnection(socket) {
|
|
5160
|
-
this.sockets.add(socket);
|
|
5161
|
-
socket.setEncoding("utf8");
|
|
5162
|
-
socket.on("timeout", () => socket.destroy());
|
|
5163
|
-
socket.on("drain", () => {
|
|
5164
|
-
if (!socket.writableEnded) socket.setTimeout(0);
|
|
5165
|
-
});
|
|
5166
|
-
socket.once("close", () => {
|
|
5167
|
-
this.sockets.delete(socket);
|
|
5168
|
-
this.taskSockets.delete(socket);
|
|
5169
|
-
this.drainingSockets.delete(socket);
|
|
5170
|
-
});
|
|
5171
|
-
let buffer = "";
|
|
5172
|
-
let handled = false;
|
|
5173
|
-
const processLine = (line) => {
|
|
5174
|
-
if (handled) return;
|
|
5175
|
-
handled = true;
|
|
5176
|
-
const request = this.parseRequestLine(socket, line);
|
|
5177
|
-
if (!request || this.rejectClosedOwner(socket, request) || this.rejectStaleOwnerGeneration(socket, request)) return;
|
|
5178
|
-
if (this.handleControlQueueRequest(socket, request)) return;
|
|
5179
|
-
if (request.type !== "submit_prompt") {
|
|
5180
|
-
this.failRequest(socket, request.requestId, "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
5181
|
-
return;
|
|
5182
|
-
}
|
|
5183
|
-
this.enqueuePromptRequest(socket, request);
|
|
5184
|
-
};
|
|
5185
|
-
socket.on("data", (chunk) => {
|
|
5186
|
-
buffer += chunk;
|
|
5187
|
-
let index = buffer.indexOf("\n");
|
|
5188
|
-
while (index >= 0) {
|
|
5189
|
-
const rawLine = buffer.slice(0, index);
|
|
5190
|
-
if (this.rejectOversizedRequest(socket, rawLine)) return;
|
|
5191
|
-
const line = rawLine.trim();
|
|
5192
|
-
buffer = buffer.slice(index + 1);
|
|
5193
|
-
if (line.length > 0) processLine(line);
|
|
5194
|
-
index = buffer.indexOf("\n");
|
|
5195
|
-
}
|
|
5196
|
-
this.rejectOversizedRequest(socket, buffer);
|
|
5197
|
-
});
|
|
5198
|
-
socket.on("error", () => {});
|
|
5199
|
-
}
|
|
5200
|
-
rejectOversizedRequest(socket, line) {
|
|
5201
|
-
if (!queueRequestExceedsLimit(line, this.maxRequestBytes)) return false;
|
|
5202
|
-
socket.destroy();
|
|
5203
|
-
return true;
|
|
5204
|
-
}
|
|
5205
|
-
};
|
|
5206
|
-
//#endregion
|
|
5207
4685
|
//#region src/runtime/engine/reconnect.ts
|
|
5208
4686
|
const SESSION_LOAD_UNSUPPORTED_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
5209
4687
|
function createReplayState(authority) {
|
|
@@ -5789,6 +5267,89 @@ async function runSessionSetConfigOptionDirect(options) {
|
|
|
5789
5267
|
};
|
|
5790
5268
|
}
|
|
5791
5269
|
//#endregion
|
|
5270
|
+
//#region src/session/execution/windows-process-command.ts
|
|
5271
|
+
function skipSeparators(value) {
|
|
5272
|
+
return value.replace(/^[ \t]+/u, "");
|
|
5273
|
+
}
|
|
5274
|
+
function readQuote(commandLine, state, executable) {
|
|
5275
|
+
if (!executable && state.quoted && commandLine[state.index + 1] === "\"") {
|
|
5276
|
+
state.value += "\"";
|
|
5277
|
+
state.index += 2;
|
|
5278
|
+
} else {
|
|
5279
|
+
state.quoted = !state.quoted;
|
|
5280
|
+
state.index += 1;
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
function readBackslashes(commandLine, state, executable) {
|
|
5284
|
+
const start = state.index;
|
|
5285
|
+
while (commandLine[state.index] === "\\") state.index += 1;
|
|
5286
|
+
const count = state.index - start;
|
|
5287
|
+
if (executable || commandLine[state.index] !== "\"") state.value += "\\".repeat(count);
|
|
5288
|
+
else {
|
|
5289
|
+
state.value += "\\".repeat(Math.floor(count / 2));
|
|
5290
|
+
if (count % 2 === 1) {
|
|
5291
|
+
state.value += "\"";
|
|
5292
|
+
state.index += 1;
|
|
5293
|
+
} else readQuote(commandLine, state, false);
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
function readArgument(commandLine, start, executable) {
|
|
5297
|
+
const state = {
|
|
5298
|
+
value: "",
|
|
5299
|
+
index: start,
|
|
5300
|
+
quoted: false
|
|
5301
|
+
};
|
|
5302
|
+
while (state.index < commandLine.length) {
|
|
5303
|
+
const char = commandLine[state.index];
|
|
5304
|
+
if (!state.quoted && /[ \t]/u.test(char)) break;
|
|
5305
|
+
if (char === "\\") readBackslashes(commandLine, state, executable);
|
|
5306
|
+
else if (char === "\"") readQuote(commandLine, state, executable);
|
|
5307
|
+
else {
|
|
5308
|
+
state.value += char;
|
|
5309
|
+
state.index += 1;
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
return state.quoted ? void 0 : state;
|
|
5313
|
+
}
|
|
5314
|
+
function readBatchCommand(commandLine) {
|
|
5315
|
+
const body = commandLine.match(/^[ \t]+\/d[ \t]+\/s[ \t]+\/c[ \t]+"([\s\S]*)"$/iu)?.[1];
|
|
5316
|
+
if (!body) return;
|
|
5317
|
+
return readBatchExecutable(body);
|
|
5318
|
+
}
|
|
5319
|
+
function readBatchExecutable(body) {
|
|
5320
|
+
let command = "";
|
|
5321
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
5322
|
+
const char = body[index];
|
|
5323
|
+
if (char === "^") {
|
|
5324
|
+
index += 1;
|
|
5325
|
+
if (index === body.length) return;
|
|
5326
|
+
command += body[index];
|
|
5327
|
+
} else if (/[ \t]/u.test(char)) break;
|
|
5328
|
+
else if (/["\r\n]/u.test(char)) return;
|
|
5329
|
+
else command += char;
|
|
5330
|
+
}
|
|
5331
|
+
return /\.(?:cmd|bat)$/iu.test(command) ? command : void 0;
|
|
5332
|
+
}
|
|
5333
|
+
/** Decode process observations, not saved command identities or POSIX launch strings. */
|
|
5334
|
+
function splitWindowsProcessCommandLine(commandLine) {
|
|
5335
|
+
const line = skipSeparators(commandLine);
|
|
5336
|
+
const executable = readArgument(line, 0, true);
|
|
5337
|
+
if (!executable?.value) return [];
|
|
5338
|
+
const argv = [executable.value];
|
|
5339
|
+
if (path.win32.basename(executable.value).toLowerCase() === "cmd.exe") {
|
|
5340
|
+
const batch = readBatchCommand(line.slice(executable.index));
|
|
5341
|
+
return batch ? [...argv, batch] : argv;
|
|
5342
|
+
}
|
|
5343
|
+
let rest = skipSeparators(line.slice(executable.index));
|
|
5344
|
+
while (rest) {
|
|
5345
|
+
const argument = readArgument(rest, 0, false);
|
|
5346
|
+
if (!argument) return [];
|
|
5347
|
+
argv.push(argument.value);
|
|
5348
|
+
rest = skipSeparators(rest.slice(argument.index));
|
|
5349
|
+
}
|
|
5350
|
+
return argv;
|
|
5351
|
+
}
|
|
5352
|
+
//#endregion
|
|
5792
5353
|
//#region src/session/execution/session-control.ts
|
|
5793
5354
|
async function cancelSessionPrompt(options) {
|
|
5794
5355
|
const cancelled = await tryCancelOnRunningOwner(options);
|
|
@@ -5898,15 +5459,16 @@ async function setSessionConfigOption(options) {
|
|
|
5898
5459
|
verbose: options.verbose
|
|
5899
5460
|
});
|
|
5900
5461
|
}
|
|
5901
|
-
function firstAgentCommandToken(command) {
|
|
5462
|
+
function firstAgentCommandToken(command, argv) {
|
|
5463
|
+
if (argv) return argv[0] || void 0;
|
|
5902
5464
|
try {
|
|
5903
5465
|
return splitCommandLine(command).command || void 0;
|
|
5904
5466
|
} catch {
|
|
5905
5467
|
return;
|
|
5906
5468
|
}
|
|
5907
5469
|
}
|
|
5908
|
-
async function isLikelyMatchingProcess(pid,
|
|
5909
|
-
const expectedToken = firstAgentCommandToken(agentCommand);
|
|
5470
|
+
async function isLikelyMatchingProcess(pid, record) {
|
|
5471
|
+
const expectedToken = firstAgentCommandToken(record.agentCommand, record.agentArgv);
|
|
5910
5472
|
if (!expectedToken) return false;
|
|
5911
5473
|
const argv = await readProcessArgv(pid);
|
|
5912
5474
|
if (argv.length === 0) return false;
|
|
@@ -5945,13 +5507,14 @@ async function readWindowsCommandLine(pid) {
|
|
|
5945
5507
|
"-NonInteractive",
|
|
5946
5508
|
"-Command",
|
|
5947
5509
|
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`
|
|
5948
|
-
], { windowsHide: true })).
|
|
5510
|
+
], { windowsHide: true })).replace(/\r?\n$/u, "") || void 0;
|
|
5949
5511
|
} catch {
|
|
5950
5512
|
return;
|
|
5951
5513
|
}
|
|
5952
5514
|
}
|
|
5953
|
-
function splitCommandLineLike(commandLine) {
|
|
5515
|
+
function splitCommandLineLike(commandLine, platform = process.platform) {
|
|
5954
5516
|
if (!commandLine) return [];
|
|
5517
|
+
if (platform === "win32") return splitWindowsProcessCommandLine(commandLine);
|
|
5955
5518
|
try {
|
|
5956
5519
|
const parsed = splitCommandLine(commandLine);
|
|
5957
5520
|
return [parsed.command, ...parsed.args];
|
|
@@ -5973,7 +5536,7 @@ async function closeSession(sessionId) {
|
|
|
5973
5536
|
}
|
|
5974
5537
|
}).catch(() => {});
|
|
5975
5538
|
if (selectedOwner) await terminateQueueOwnerForSession(record.acpxRecordId, selectedOwner);
|
|
5976
|
-
if (record.pid != null && isProcessAlive(record.pid) && await isLikelyMatchingProcess(record.pid, record
|
|
5539
|
+
if (record.pid != null && isProcessAlive(record.pid) && await isLikelyMatchingProcess(record.pid, record)) await terminateProcess(record.pid);
|
|
5977
5540
|
const ownership = await acquireSessionTurn(record.acpxRecordId);
|
|
5978
5541
|
try {
|
|
5979
5542
|
const current = await resolveSessionRecord(record.acpxRecordId);
|
|
@@ -6964,10 +6527,11 @@ var QueueTaskOutputFormatter = class {
|
|
|
6964
6527
|
this.send = task.send;
|
|
6965
6528
|
}
|
|
6966
6529
|
setContext(_context) {}
|
|
6967
|
-
onAcpMessage(message) {
|
|
6530
|
+
onAcpMessage(message, direction) {
|
|
6968
6531
|
this.send({
|
|
6969
6532
|
type: "event",
|
|
6970
6533
|
requestId: this.requestId,
|
|
6534
|
+
...direction === void 0 ? {} : { direction },
|
|
6971
6535
|
message
|
|
6972
6536
|
});
|
|
6973
6537
|
}
|
|
@@ -7024,7 +6588,7 @@ var AcpErrorTracker = class {
|
|
|
7024
6588
|
this.outbound.length = 0;
|
|
7025
6589
|
}
|
|
7026
6590
|
observe(output, direction, message) {
|
|
7027
|
-
output.onAcpMessage(message);
|
|
6591
|
+
output.onAcpMessage(message, direction);
|
|
7028
6592
|
const acp = extractAcpError(message);
|
|
7029
6593
|
if (!acp) return;
|
|
7030
6594
|
if (direction === "inbound") {
|
|
@@ -7852,6 +7416,9 @@ async function runOnce(options, control) {
|
|
|
7852
7416
|
try {
|
|
7853
7417
|
await closeOwnedClient();
|
|
7854
7418
|
} finally {
|
|
7419
|
+
try {
|
|
7420
|
+
options.onPermissionStats?.(client.getPermissionStats());
|
|
7421
|
+
} catch {}
|
|
7855
7422
|
output.flush();
|
|
7856
7423
|
}
|
|
7857
7424
|
}
|
|
@@ -8291,6 +7858,6 @@ async function sendSession(options) {
|
|
|
8291
7858
|
}
|
|
8292
7859
|
const queueOwnerRuntimeTestInternals = { queueOwnerExitIsFatal };
|
|
8293
7860
|
//#endregion
|
|
8294
|
-
export {
|
|
7861
|
+
export { isRequestedModelUnsupportedError as $, applyInitialModelSelection as A, reconcileAgentSessionId as B, setSessionModeOnOwner as C, connectAndLoadSession as D, withConnectedSession as E, persistSessionOptions as F, recordPromptSubmission as G, cloneSessionConversation as H, sessionOptionsFromRecord as I, advertisedModelState as J, recordSessionUpdate as K, applyConversation as L, applyRequestedModelIfAdvertised as M, setDesiredModeId as N, applyConfigOptionSelection as O, normalizeSessionOptionShape as P, RequestedModelUnsupportedError as Q, applyLifecycleSnapshotToRecord as R, setSessionMode as S, setSessionModelOnOwner as T, createSessionConversation as U, cloneSessionAcpxState as V, recordClientOperation as W, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as X, AcpClient as Y, REQUESTED_MODEL_UNSUPPORTED_REASONS as Z, cancelSessionPrompt as _, runQueuedTask as a, isLegacyZedCodexAcpInvocation as at, setSessionConfigOption as b, LiveSessionCheckpoint as c, assertControlAuthority as ct, flushPerfMetricsCapture as d, getAcpxVersion as et, installPerfMetricsCapture as f, listAgentSessions as g, ensureSession as h, runOnce as i, listSessionEvents as it, applyModelSelection as j, applyConfigOptionsToRecord as k, runPromptTurn as l, withInterrupt as lt, createSessionWithClient as m, runSessionQueueOwner as n, DEFAULT_QUEUE_OWNER_TTL_MS as nt, sendSessionDirect as o, InterruptedError as ot, createSession as p, trimConversationForRuntime as q, sendSession as r, normalizeQueueOwnerTtlMs as rt, DISCARD_OUTPUT_FORMATTER as s, TimeoutError as st, queueOwnerRuntimeTestInternals as t, permissionModeSatisfies as tt, buildQueueOwnerArgOverride as u, withTimeout as ut, closeSession as v, setSessionModel as w, setSessionConfigOptionOnOwner as x, sessionControlTestInternals as y, createInitialSessionRecord as z };
|
|
8295
7862
|
|
|
8296
|
-
//# sourceMappingURL=queue-owner-runtime-
|
|
7863
|
+
//# sourceMappingURL=queue-owner-runtime-W0lly6ap.js.map
|