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.
Files changed (40) hide show
  1. package/dist/{agent-registry-Ct2yWPW7.js → agent-registry-DWlx5BQ4.js} +3 -2
  2. package/dist/agent-registry-DWlx5BQ4.js.map +1 -0
  3. package/dist/agent-registry.js +1 -1
  4. package/dist/{cli-Bmki9cj5.js → cli-D-s_eTUy.js} +5 -5
  5. package/dist/{cli-Bmki9cj5.js.map → cli-D-s_eTUy.js.map} +1 -1
  6. package/dist/cli.js +31 -15
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{client-DzaUKazo.d.ts → client-BchvQRpz.d.ts} +8 -1
  9. package/dist/client-BchvQRpz.d.ts.map +1 -0
  10. package/dist/{flows--Ewxn_fQ.js → flows-DdxEaeZo.js} +33 -20
  11. package/dist/flows-DdxEaeZo.js.map +1 -0
  12. package/dist/flows.d.ts +2 -1
  13. package/dist/flows.d.ts.map +1 -1
  14. package/dist/flows.js +1 -1
  15. package/dist/{invocation-options-DRzxHZmr.js → invocation-options-HjxSBsK0.js} +5 -5
  16. package/dist/{invocation-options-DRzxHZmr.js.map → invocation-options-HjxSBsK0.js.map} +1 -1
  17. package/dist/{ipc-fMgj5vrO.js → ipc-D0ZMQ0q9.js} +1393 -86
  18. package/dist/ipc-D0ZMQ0q9.js.map +1 -0
  19. package/dist/{output-D6kDs8BN.js → output-GQnhkJMU.js} +20 -12
  20. package/dist/output-GQnhkJMU.js.map +1 -0
  21. package/dist/{queue-owner-runtime-CLwjMYNL.js → queue-owner-runtime-W0lly6ap.js} +344 -777
  22. package/dist/queue-owner-runtime-W0lly6ap.js.map +1 -0
  23. package/dist/runtime.d.ts +1 -1
  24. package/dist/runtime.d.ts.map +1 -1
  25. package/dist/runtime.js +5 -5
  26. package/dist/runtime.js.map +1 -1
  27. package/dist/{session-watch-BfdIo-n4.js → session-watch-_8yxQ6ba.js} +4 -4
  28. package/dist/{session-watch-BfdIo-n4.js.map → session-watch-_8yxQ6ba.js.map} +1 -1
  29. package/dist/watch-C0mwpJcN.js +85 -0
  30. package/dist/watch-C0mwpJcN.js.map +1 -0
  31. package/package.json +1 -1
  32. package/skills/acpx/SKILL.md +2 -0
  33. package/dist/agent-registry-Ct2yWPW7.js.map +0 -1
  34. package/dist/client-DzaUKazo.d.ts.map +0 -1
  35. package/dist/flows--Ewxn_fQ.js.map +0 -1
  36. package/dist/ipc-fMgj5vrO.js.map +0 -1
  37. package/dist/output-D6kDs8BN.js.map +0 -1
  38. package/dist/queue-owner-runtime-CLwjMYNL.js.map +0 -1
  39. package/dist/watch-DH-zXQH2.js +0 -32
  40. package/dist/watch-DH-zXQH2.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { $ as measurePerf, A as findGitRepositoryRoot, B as writeSessionRecord, Bt as PermissionDeniedError, C as getUnsupportedPromptContentMessage, D as textPrompt, Dt as OUTPUT_ERROR_ORIGINS, E as promptToDisplayText, Et as OUTPUT_ERROR_CODES, Ft as AgentStartupError, G as normalizeAgentSessionId, Gt as SessionModelReplayError, Ht as QueueConnectionError, I as normalizeName, It as AuthPolicyError, Jt as UnsupportedPromptContentError, K as SESSION_JOURNAL_SCHEMA, Lt as ClaudeAcpSessionCreateTimeoutError, M as findSessionByDirectoryWalk, Mt as AcpxOperationalError, N as isoNow$1, Nt as AgentDisconnectedError, Pt as AgentSpawnError, Q as incrementPerfCounter, Rt as CopilotAcpUnsupportedError, St as isAcpResourceNotFoundError, Ut as SessionConfigOptionReplayError, Vt as PermissionPromptUnavailableError, W as extractAgentSessionId, Wt as SessionModeReplayError, X as formatPerfMetric, Z as getPerfMetricsSnapshot, _ as waitMs$1, a as trySetModelOnRunningOwner, at as isAcpJsonRpcMessage, b as lockOwnerPid, bt as extractAcpError, c as queueRequestExceedsLimit, et as recordPerfDuration, f as refreshQueueOwnerLease, ft as defaultSessionEventLog, g as tryAcquireQueueOwnerLease, gt as sessionEventSegmentPath, h as terminateQueueOwnerForSession, ht as sessionEventLockPath, i as trySetModeOnRunningOwner, k as absolutePath, l as parseQueueRequest, m as terminateProcess, mt as sessionEventActivePath, n as tryCloseSessionOnRunningOwner, nt as setPerfGauge, o as trySubmitToRunningOwner, ot as isAcpMessageObject, p as releaseQueueOwnerLease, pt as sessionBaseDir, q as SessionJournalReader, qt as SessionResumeRequiredError, r as trySetConfigOptionOnRunningOwner, rt as startPerfTimer, s as queueRequestByteLimit, st as isSessionUpdateNotification, t as tryCancelOnRunningOwner, tt as resetPerfMetrics, v as QueueLeaseGuardSettlementError, wt as EXIT_CODES, x as readProcessTable, xt as formatUnknownErrorMessage, y as createLockOwner, yt as isProcessAlive, z as resolveSessionRecord, zt as GeminiAcpStartupTimeoutError } from "./ipc-fMgj5vrO.js";
2
- import { A as resolveAgentCommandParts, C as asAbsoluteCwd, E as isoNow$2, F as waitForSpawn, M as runTimedExecFile, N as splitCommandLine, P as waitForChildExit, S as PROCESS_HELPER_TIMEOUT_MS, T as isChildProcessRunning, _ as buildSpawnCommandOptions, b as readWindowsEnvValue, f as resolveBuiltInAgentLaunch, g as buildAgentSpawnCommand, j as resolveAgentSessionCwd, k as requireAgentStdio, v as buildTerminalShellSpawnCommand, w as basenameToken, x as resolveWindowsExecutablePath, y as buildTerminalSpawnCommand } from "./agent-registry-Ct2yWPW7.js";
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 readCommandOutput(command, ["--version"], GEMINI_VERSION_TIMEOUT_MS))?.split(/\r?\n/).map((line) => line.trim()).find((line) => /\d+\.\d+\.\d+/.test(line));
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 readCommandOutput(command, args, timeoutMs) {
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
- if (!process.env.GEMINI_API_KEY && !process.env.GOOGLE_API_KEY) parts.push("No GEMINI_API_KEY or GOOGLE_API_KEY was set for non-interactive auth.");
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
- async function buildCopilotAcpUnsupportedMessage(command) {
1151
- const parts = ["GitHub Copilot CLI ACP stdio mode is not available in the installed copilot binary.", "acpx copilot expects a Copilot CLI release that supports --acp --stdio."];
1152
- const helpOutput = await readCommandOutput(command, ["--help"], COPILOT_HELP_TIMEOUT_MS);
1153
- if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) parts.push("Detected copilot --help output without --acp support.");
1154
- parts.push("Upgrade GitHub Copilot CLI to a release with ACP stdio support, or use --agent with another ACP-compatible adapter in the meantime.");
1155
- return parts.join(" ");
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 readCommandOutput(command, ["--help"], COPILOT_HELP_TIMEOUT_MS);
1159
- if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(await buildCopilotAcpUnsupportedMessage(command), { retryable: false });
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
- assertControlAuthority(authority);
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
- assertControlAuthority(authority);
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: buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials, this.options.sessionOptions?.env, this.options.agentProcessEnv)
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
- async spawnAgentProcess(plan, authority) {
2676
- const spawnCommand = buildAgentSpawnCommand(plan.spawnCommand, plan.args, process.platform, plan.spawnOptions.env);
2677
- const launch = Object.freeze({
2678
- launchId: randomUUID(),
2679
- scope: snapshotProcessLaunchScope(this.options.processLaunchScope),
2680
- command: spawnCommand.command,
2681
- args: Object.freeze([...spawnCommand.args]),
2682
- cwd: this.options.cwd
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(spawnCommand.command, spawnCommand.args, {
2493
+ spawnedChild = spawn(launch.command, [...launch.args], {
2689
2494
  ...plan.spawnOptions,
2690
- windowsVerbatimArguments: spawnCommand.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: Object.freeze({
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
- const exitNotificationBarrier = new Promise((resolve) => {
2589
+ observe(new Promise((resolve) => {
2719
2590
  releaseExitNotification = resolve;
2720
- });
2721
- this.attachAgentLifecycleObservers(child, process, exitNotificationBarrier);
2591
+ }));
2722
2592
  try {
2723
- await this.options.processLifecycle?.onSpawned?.(process);
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 agent = this.agent;
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
- try {
3144
- if (agent) await this.terminateAgentProcess(agent);
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 = Date.now() + AGENT_CLEANUP_BUDGET_MS;
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 - Date.now()));
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 - Date.now())));
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 && Date.now() < deadline) await descendants.signal(signal, deadline - Date.now());
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 - Date.now()));
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
- const onExit = (exitCode, signal) => {
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
- exitNotificationBarrier.then(() => {
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, agentCommand) {
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 })).trim() || void 0;
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.agentCommand)) await terminateProcess(record.pid);
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 { getAcpxVersion as $, applyInitialModelSelection as A, cloneSessionAcpxState as B, setSessionModeOnOwner as C, connectAndLoadSession as D, withConnectedSession as E, sessionOptionsFromRecord as F, recordSessionUpdate as G, createSessionConversation as H, applyConversation as I, AcpClient as J, trimConversationForRuntime as K, applyLifecycleSnapshotToRecord as L, applyRequestedModelIfAdvertised as M, setDesiredModeId as N, applyConfigOptionSelection as O, persistSessionOptions as P, isRequestedModelUnsupportedError as Q, createInitialSessionRecord as R, setSessionMode as S, setSessionModelOnOwner as T, recordClientOperation as U, cloneSessionConversation as V, recordPromptSubmission as W, REQUESTED_MODEL_UNSUPPORTED_REASONS as X, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as Y, RequestedModelUnsupportedError as Z, cancelSessionPrompt as _, runQueuedTask as a, InterruptedError as at, setSessionConfigOption as b, LiveSessionCheckpoint as c, withInterrupt as ct, flushPerfMetricsCapture as d, normalizeOutputError as dt, permissionModeSatisfies as et, installPerfMetricsCapture as f, listAgentSessions as g, ensureSession as h, runOnce as i, isLegacyZedCodexAcpInvocation as it, applyModelSelection as j, applyConfigOptionsToRecord as k, runPromptTurn as l, withTimeout as lt, createSessionWithClient as m, runSessionQueueOwner as n, normalizeQueueOwnerTtlMs as nt, sendSessionDirect as o, TimeoutError as ot, createSession as p, advertisedModelState as q, sendSession as r, listSessionEvents as rt, DISCARD_OUTPUT_FORMATTER as s, assertControlAuthority as st, queueOwnerRuntimeTestInternals as t, DEFAULT_QUEUE_OWNER_TTL_MS as tt, buildQueueOwnerArgOverride as u, exitCodeForOutputErrorCode as ut, closeSession as v, setSessionModel as w, setSessionConfigOptionOnOwner as x, sessionControlTestInternals as y, reconcileAgentSessionId as z };
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-CLwjMYNL.js.map
7863
+ //# sourceMappingURL=queue-owner-runtime-W0lly6ap.js.map