acpx 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-registry-DdKB-zia.js → agent-registry-DWlx5BQ4.js} +5 -3
- package/dist/agent-registry-DWlx5BQ4.js.map +1 -0
- package/dist/agent-registry.js +1 -1
- package/dist/{cli-8mVHeUXh.js → cli-BerngYEz.js} +5 -5
- package/dist/{cli-8mVHeUXh.js.map → cli-BerngYEz.js.map} +1 -1
- package/dist/cli.js +77 -53
- package/dist/cli.js.map +1 -1
- package/dist/{client-DeNKZZcu.d.ts → client-BchvQRpz.d.ts} +17 -4
- package/dist/client-BchvQRpz.d.ts.map +1 -0
- package/dist/{flows-WOQNzCMC.js → flows-DimX0it5.js} +693 -475
- package/dist/flows-DimX0it5.js.map +1 -0
- package/dist/flows.d.ts +20 -7
- package/dist/flows.d.ts.map +1 -1
- package/dist/flows.js +1 -1
- package/dist/{invocation-options-B6d7TI-y.js → invocation-options-vobVApfO.js} +32 -11
- package/dist/invocation-options-vobVApfO.js.map +1 -0
- package/dist/{ipc-BJrvfNXr.js → ipc-DAApJ5mi.js} +1041 -263
- package/dist/ipc-DAApJ5mi.js.map +1 -0
- package/dist/{output-CL18YmsG.js → output-Cnuji8Bo.js} +41 -20
- package/dist/output-Cnuji8Bo.js.map +1 -0
- package/dist/{queue-owner-runtime-Cr0dWJuL.js → queue-owner-runtime-C4OOosbo.js} +732 -507
- package/dist/queue-owner-runtime-C4OOosbo.js.map +1 -0
- package/dist/runtime.d.ts +19 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +48 -29
- package/dist/runtime.js.map +1 -1
- package/dist/session-options-Dp6mzNSy.d.ts.map +1 -1
- package/dist/{session-watch-B8Q_TsGf.js → session-watch-Bj9LoYUq.js} +5 -5
- package/dist/session-watch-Bj9LoYUq.js.map +1 -0
- package/dist/{watch-Dp73ph8h.js → watch-Cz6jbrRs.js} +2 -2
- package/dist/{watch-Dp73ph8h.js.map → watch-Cz6jbrRs.js.map} +1 -1
- package/package.json +1 -1
- package/skills/acpx/SKILL.md +45 -23
- package/dist/agent-registry-DdKB-zia.js.map +0 -1
- package/dist/client-DeNKZZcu.d.ts.map +0 -1
- package/dist/flows-WOQNzCMC.js.map +0 -1
- package/dist/invocation-options-B6d7TI-y.js.map +0 -1
- package/dist/ipc-BJrvfNXr.js.map +0 -1
- package/dist/output-CL18YmsG.js.map +0 -1
- package/dist/queue-owner-runtime-Cr0dWJuL.js.map +0 -1
- package/dist/session-watch-B8Q_TsGf.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { A as
|
|
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 ProcessDescendants, xt as formatUnknownErrorMessage, y as createLockOwner, yt as isProcessAlive, z as resolveSessionRecord, zt as GeminiAcpStartupTimeoutError } from "./ipc-DAApJ5mi.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";
|
|
@@ -485,6 +485,17 @@ function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
|
485
485
|
return Math.round(ttlMs);
|
|
486
486
|
}
|
|
487
487
|
//#endregion
|
|
488
|
+
//#region src/file-read-window.ts
|
|
489
|
+
function sliceReadWindow(content, line, limit) {
|
|
490
|
+
if (line == null && limit == null) return content;
|
|
491
|
+
const lines = content.split("\n");
|
|
492
|
+
const startIndex = Math.max(0, (line == null ? 1 : Math.max(1, Math.trunc(line))) - 1);
|
|
493
|
+
const maxLines = limit == null ? void 0 : Math.max(0, Math.trunc(limit));
|
|
494
|
+
if (maxLines === 0) return "";
|
|
495
|
+
const endIndex = maxLines == null ? lines.length : Math.min(lines.length, startIndex + maxLines);
|
|
496
|
+
return lines.slice(startIndex, endIndex).join("\n");
|
|
497
|
+
}
|
|
498
|
+
//#endregion
|
|
488
499
|
//#region src/permission-prompt.ts
|
|
489
500
|
let promptQueue = Promise.resolve();
|
|
490
501
|
async function promptForPermission(options) {
|
|
@@ -598,7 +609,7 @@ var FileSystemHandlers = class {
|
|
|
598
609
|
if (this.permissionMode === "deny-all") throw new PermissionDeniedError("Permission denied for fs/read_text_file (--deny-all)");
|
|
599
610
|
const content = await (await this.getWorkspace()).readText(filePath);
|
|
600
611
|
assertControlAuthority(authority);
|
|
601
|
-
const sliced =
|
|
612
|
+
const sliced = sliceReadWindow(content, params.line, params.limit);
|
|
602
613
|
this.emitOperation({
|
|
603
614
|
method: "fs/read_text_file",
|
|
604
615
|
status: "completed",
|
|
@@ -686,15 +697,6 @@ var FileSystemHandlers = class {
|
|
|
686
697
|
maxBytes: Infinity
|
|
687
698
|
});
|
|
688
699
|
}
|
|
689
|
-
sliceContent(content, line, limit) {
|
|
690
|
-
if (line == null && limit == null) return content;
|
|
691
|
-
const lines = content.split("\n");
|
|
692
|
-
const startIndex = Math.max(0, (line == null ? 1 : Math.max(1, Math.trunc(line))) - 1);
|
|
693
|
-
const maxLines = limit == null ? void 0 : Math.max(0, Math.trunc(limit));
|
|
694
|
-
if (maxLines === 0) return "";
|
|
695
|
-
const endIndex = maxLines == null ? lines.length : Math.min(lines.length, startIndex + maxLines);
|
|
696
|
-
return lines.slice(startIndex, endIndex).join("\n");
|
|
697
|
-
}
|
|
698
700
|
readWindowDetails(line, limit) {
|
|
699
701
|
if (line == null && limit == null) return;
|
|
700
702
|
return `line=${line == null ? 1 : Math.max(1, Math.trunc(line))}, limit=${limit == null ? "all" : Math.max(0, Math.trunc(limit))}`;
|
|
@@ -1078,63 +1080,22 @@ function compareVersionParts(left, right) {
|
|
|
1078
1080
|
}
|
|
1079
1081
|
return 0;
|
|
1080
1082
|
}
|
|
1081
|
-
async function detectGeminiVersion(command) {
|
|
1082
|
-
const versionLine = (await
|
|
1083
|
+
async function detectGeminiVersion(command, context, diagnostic = false) {
|
|
1084
|
+
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));
|
|
1083
1085
|
return parseGeminiVersion(versionLine);
|
|
1084
1086
|
}
|
|
1085
|
-
async function resolveGeminiCommandArgs(command, args) {
|
|
1087
|
+
async function resolveGeminiCommandArgs(command, args, context) {
|
|
1086
1088
|
if (basenameToken(command) !== "gemini" || !args.includes("--acp")) return [...args];
|
|
1087
|
-
const version = await detectGeminiVersion(command);
|
|
1089
|
+
const version = await detectGeminiVersion(command, context);
|
|
1088
1090
|
if (version && compareVersionParts(version.parts, GEMINI_ACP_FLAG_VERSION) < 0) return args.map((arg) => arg === "--acp" ? "--experimental-acp" : arg);
|
|
1089
1091
|
return [...args];
|
|
1090
1092
|
}
|
|
1091
|
-
async function
|
|
1092
|
-
return await new Promise((resolve) => {
|
|
1093
|
-
const child = spawn(command, [...args], buildSpawnCommandOptions(command, {
|
|
1094
|
-
stdio: [
|
|
1095
|
-
"ignore",
|
|
1096
|
-
"pipe",
|
|
1097
|
-
"pipe"
|
|
1098
|
-
],
|
|
1099
|
-
windowsHide: true
|
|
1100
|
-
}));
|
|
1101
|
-
let stdout = "";
|
|
1102
|
-
let stderr = "";
|
|
1103
|
-
let settled = false;
|
|
1104
|
-
const finish = (value) => {
|
|
1105
|
-
if (settled) return;
|
|
1106
|
-
settled = true;
|
|
1107
|
-
clearTimeout(timer);
|
|
1108
|
-
child.removeAllListeners();
|
|
1109
|
-
child.stdout?.removeAllListeners();
|
|
1110
|
-
child.stderr?.removeAllListeners();
|
|
1111
|
-
resolve(value);
|
|
1112
|
-
};
|
|
1113
|
-
const timer = setTimeout(() => {
|
|
1114
|
-
child.kill("SIGKILL");
|
|
1115
|
-
finish(void 0);
|
|
1116
|
-
}, timeoutMs);
|
|
1117
|
-
child.stdout?.setEncoding("utf8");
|
|
1118
|
-
child.stderr?.setEncoding("utf8");
|
|
1119
|
-
child.stdout?.on("data", (chunk) => {
|
|
1120
|
-
stdout += chunk;
|
|
1121
|
-
});
|
|
1122
|
-
child.stderr?.on("data", (chunk) => {
|
|
1123
|
-
stderr += chunk;
|
|
1124
|
-
});
|
|
1125
|
-
child.once("error", () => {
|
|
1126
|
-
finish(void 0);
|
|
1127
|
-
});
|
|
1128
|
-
child.once("close", () => {
|
|
1129
|
-
finish(`${stdout}\n${stderr}`);
|
|
1130
|
-
});
|
|
1131
|
-
});
|
|
1132
|
-
}
|
|
1133
|
-
async function buildGeminiAcpStartupTimeoutMessage(command) {
|
|
1093
|
+
async function buildGeminiAcpStartupTimeoutMessage(command, context) {
|
|
1134
1094
|
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."];
|
|
1135
|
-
const version = await detectGeminiVersion(command);
|
|
1095
|
+
const version = await detectGeminiVersion(command, context, true);
|
|
1136
1096
|
if (version) parts.push(`Detected Gemini CLI version: ${version.raw}.`);
|
|
1137
|
-
|
|
1097
|
+
const env = context.env;
|
|
1098
|
+
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.");
|
|
1138
1099
|
parts.push("Try upgrading Gemini CLI and using API-key-based auth for non-interactive ACP runs.");
|
|
1139
1100
|
return parts.join(" ");
|
|
1140
1101
|
}
|
|
@@ -1145,16 +1106,17 @@ function buildClaudeAcpSessionCreateTimeoutMessage() {
|
|
|
1145
1106
|
"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."
|
|
1146
1107
|
].join(" ");
|
|
1147
1108
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1109
|
+
function buildCopilotAcpUnsupportedMessage() {
|
|
1110
|
+
return [
|
|
1111
|
+
"GitHub Copilot CLI ACP stdio mode is not available in the installed copilot binary.",
|
|
1112
|
+
"acpx copilot expects a Copilot CLI release that supports --acp --stdio.",
|
|
1113
|
+
"Detected copilot --help output without --acp support.",
|
|
1114
|
+
"Upgrade GitHub Copilot CLI to a release with ACP stdio support, or use --agent with another ACP-compatible adapter in the meantime."
|
|
1115
|
+
].join(" ");
|
|
1154
1116
|
}
|
|
1155
|
-
async function ensureCopilotAcpSupport(command) {
|
|
1156
|
-
const helpOutput = await
|
|
1157
|
-
if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(
|
|
1117
|
+
async function ensureCopilotAcpSupport(command, context) {
|
|
1118
|
+
const helpOutput = await context.readOutput(command, ["--help"], COPILOT_HELP_TIMEOUT_MS);
|
|
1119
|
+
if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(buildCopilotAcpUnsupportedMessage(), { retryable: false });
|
|
1158
1120
|
}
|
|
1159
1121
|
function buildClaudeCodeOptionsMeta(options, isolateUserSettings = false) {
|
|
1160
1122
|
const claudeCodeOptions = {};
|
|
@@ -1375,6 +1337,73 @@ function resolveClientCapabilities(params) {
|
|
|
1375
1337
|
};
|
|
1376
1338
|
}
|
|
1377
1339
|
//#endregion
|
|
1340
|
+
//#region src/acp/command-probe.ts
|
|
1341
|
+
var CommandProbeAdmissionError = class extends Error {
|
|
1342
|
+
reason;
|
|
1343
|
+
constructor(reason) {
|
|
1344
|
+
super("Agent command admission rejected", { cause: reason });
|
|
1345
|
+
this.reason = reason;
|
|
1346
|
+
this.name = "CommandProbeAdmissionError";
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
async function admitCommandProbe(admit) {
|
|
1350
|
+
try {
|
|
1351
|
+
return await admit();
|
|
1352
|
+
} catch (error) {
|
|
1353
|
+
throw new CommandProbeAdmissionError(error);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
/** Collect only this short adapter command; its client owns process retirement. */
|
|
1357
|
+
function captureCommandProbeOutput(child, timeoutMs, retire) {
|
|
1358
|
+
let stdout = "";
|
|
1359
|
+
let stderr = "";
|
|
1360
|
+
let settled = false;
|
|
1361
|
+
let disposed = false;
|
|
1362
|
+
let timer;
|
|
1363
|
+
let resolveResult;
|
|
1364
|
+
const result = new Promise((resolve) => {
|
|
1365
|
+
resolveResult = resolve;
|
|
1366
|
+
});
|
|
1367
|
+
const finish = (value) => {
|
|
1368
|
+
if (settled) return;
|
|
1369
|
+
settled = true;
|
|
1370
|
+
clearTimeout(timer);
|
|
1371
|
+
resolveResult(value);
|
|
1372
|
+
};
|
|
1373
|
+
const fail = () => {
|
|
1374
|
+
if (disposed) return;
|
|
1375
|
+
finish(void 0);
|
|
1376
|
+
retire().catch(() => {});
|
|
1377
|
+
};
|
|
1378
|
+
const onStdout = (chunk) => {
|
|
1379
|
+
if (!settled) stdout += chunk;
|
|
1380
|
+
};
|
|
1381
|
+
const onStderr = (chunk) => {
|
|
1382
|
+
if (!settled) stderr += chunk;
|
|
1383
|
+
};
|
|
1384
|
+
const onClose = () => finish(`${stdout}\n${stderr}`);
|
|
1385
|
+
child.stdout?.setEncoding("utf8");
|
|
1386
|
+
child.stderr?.setEncoding("utf8");
|
|
1387
|
+
child.stdout?.on("data", onStdout);
|
|
1388
|
+
child.stderr?.on("data", onStderr);
|
|
1389
|
+
child.on("error", fail);
|
|
1390
|
+
child.stdout?.on("error", fail);
|
|
1391
|
+
child.stderr?.on("error", fail);
|
|
1392
|
+
child.once("close", onClose);
|
|
1393
|
+
timer = setTimeout(fail, timeoutMs);
|
|
1394
|
+
return {
|
|
1395
|
+
result,
|
|
1396
|
+
dispose: () => {
|
|
1397
|
+
disposed = true;
|
|
1398
|
+
finish(void 0);
|
|
1399
|
+
clearTimeout(timer);
|
|
1400
|
+
child.stdout?.off("data", onStdout);
|
|
1401
|
+
child.stderr?.off("data", onStderr);
|
|
1402
|
+
child.off("close", onClose);
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
//#endregion
|
|
1378
1407
|
//#region src/acp/model-support.ts
|
|
1379
1408
|
const REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE = "ACP_MODEL_UNSUPPORTED";
|
|
1380
1409
|
const REQUESTED_MODEL_UNSUPPORTED_REASONS = ["missing-capability", "unadvertised-model"];
|
|
@@ -1556,15 +1585,22 @@ function enqueueNdJsonLine(agentCommand, line, controller) {
|
|
|
1556
1585
|
console.error("Failed to parse JSON message:", trimmedLine, err);
|
|
1557
1586
|
}
|
|
1558
1587
|
}
|
|
1559
|
-
function
|
|
1588
|
+
function enqueueNdJsonChunk(agentCommand, chunk, fragments, controller) {
|
|
1589
|
+
const lines = chunk.split("\n");
|
|
1590
|
+
const suffix = lines.pop() || "";
|
|
1591
|
+
if (lines.length > 0 && fragments.length > 0) {
|
|
1592
|
+
lines[0] = fragments.join("") + lines[0];
|
|
1593
|
+
fragments.length = 0;
|
|
1594
|
+
}
|
|
1560
1595
|
for (const line of lines) enqueueNdJsonLine(agentCommand, line, controller);
|
|
1596
|
+
if (suffix) fragments.push(suffix);
|
|
1561
1597
|
}
|
|
1562
1598
|
function createNdJsonMessageStream(agentCommand, output, input, maxMessageBytes, onReadError) {
|
|
1563
1599
|
const textEncoder = new TextEncoder();
|
|
1564
1600
|
const textDecoder = new TextDecoder();
|
|
1565
1601
|
return {
|
|
1566
1602
|
readable: new ReadableStream({ async start(controller) {
|
|
1567
|
-
|
|
1603
|
+
const fragments = [];
|
|
1568
1604
|
let retainedBytes = 0;
|
|
1569
1605
|
const reader = input.getReader();
|
|
1570
1606
|
try {
|
|
@@ -1572,10 +1608,7 @@ function createNdJsonMessageStream(agentCommand, output, input, maxMessageBytes,
|
|
|
1572
1608
|
const { value, done } = await reader.read();
|
|
1573
1609
|
if (done) break;
|
|
1574
1610
|
if (maxMessageBytes !== void 0) retainedBytes = countLineBytes(value, retainedBytes, maxMessageBytes);
|
|
1575
|
-
|
|
1576
|
-
const lines = content.split("\n");
|
|
1577
|
-
content = lines.pop() || "";
|
|
1578
|
-
enqueueNdJsonLines(agentCommand, lines, controller);
|
|
1611
|
+
enqueueNdJsonChunk(agentCommand, textDecoder.decode(value, { stream: true }), fragments, controller);
|
|
1579
1612
|
}
|
|
1580
1613
|
controller.close();
|
|
1581
1614
|
} catch (err) {
|
|
@@ -1666,89 +1699,6 @@ function promptRequestOwner(message) {
|
|
|
1666
1699
|
} : void 0;
|
|
1667
1700
|
}
|
|
1668
1701
|
//#endregion
|
|
1669
|
-
//#region src/acp/process-descendants.ts
|
|
1670
|
-
function parseProcessTable(output) {
|
|
1671
|
-
const table = /* @__PURE__ */ new Map();
|
|
1672
|
-
for (const line of output.split("\n")) {
|
|
1673
|
-
const match = /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.+?)\s*$/.exec(line);
|
|
1674
|
-
if (match && !match[3].startsWith("Z")) {
|
|
1675
|
-
const pid = Number(match[1]);
|
|
1676
|
-
if (Number.isSafeInteger(pid) && pid > 1 && pid !== process.pid) table.set(pid, {
|
|
1677
|
-
pid,
|
|
1678
|
-
parentPid: Number(match[2]),
|
|
1679
|
-
birth: match[4]
|
|
1680
|
-
});
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
return table;
|
|
1684
|
-
}
|
|
1685
|
-
function includeDescendants(table, owned) {
|
|
1686
|
-
let expanded;
|
|
1687
|
-
do {
|
|
1688
|
-
expanded = false;
|
|
1689
|
-
for (const identity of table.values()) if (owned.has(identity.parentPid) && !owned.has(identity.pid)) {
|
|
1690
|
-
owned.add(identity.pid);
|
|
1691
|
-
expanded = true;
|
|
1692
|
-
}
|
|
1693
|
-
} while (expanded);
|
|
1694
|
-
}
|
|
1695
|
-
/** Best-effort POSIX cleanup for descendants witnessed during this child launch. */
|
|
1696
|
-
var ProcessDescendants = class {
|
|
1697
|
-
child;
|
|
1698
|
-
identities = /* @__PURE__ */ new Map();
|
|
1699
|
-
pending;
|
|
1700
|
-
retired = false;
|
|
1701
|
-
constructor(child) {
|
|
1702
|
-
this.child = child;
|
|
1703
|
-
}
|
|
1704
|
-
capture(timeoutMs = PROCESS_HELPER_TIMEOUT_MS) {
|
|
1705
|
-
if (this.retired || process.platform === "win32") return Promise.resolve(true);
|
|
1706
|
-
this.pending ??= this.readSnapshot(timeoutMs).finally(() => {
|
|
1707
|
-
this.pending = void 0;
|
|
1708
|
-
});
|
|
1709
|
-
return this.pending;
|
|
1710
|
-
}
|
|
1711
|
-
async readSnapshot(timeoutMs) {
|
|
1712
|
-
try {
|
|
1713
|
-
const output = await runTimedExecFile("ps", ["-eo", "pid=,ppid=,stat=,lstart="], { timeoutMs });
|
|
1714
|
-
if (!this.retired) this.refresh(parseProcessTable(output));
|
|
1715
|
-
return true;
|
|
1716
|
-
} catch {
|
|
1717
|
-
return false;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
refresh(table) {
|
|
1721
|
-
const owned = /* @__PURE__ */ new Set();
|
|
1722
|
-
for (const [pid, identity] of this.identities) if (table.get(pid)?.birth === identity.birth) owned.add(pid);
|
|
1723
|
-
const root = this.child.pid;
|
|
1724
|
-
if (root && isChildProcessRunning(this.child)) owned.add(root);
|
|
1725
|
-
includeDescendants(table, owned);
|
|
1726
|
-
owned.delete(root ?? 0);
|
|
1727
|
-
this.identities = new Map([...table].filter(([pid]) => owned.has(pid)));
|
|
1728
|
-
}
|
|
1729
|
-
async signal(signal, timeoutMs) {
|
|
1730
|
-
if (!await this.capture(timeoutMs) || this.retired) return;
|
|
1731
|
-
for (const pid of this.identities.keys()) try {
|
|
1732
|
-
process.kill(pid, signal);
|
|
1733
|
-
} catch {
|
|
1734
|
-
this.identities.delete(pid);
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
async waitForExit(timeoutMs) {
|
|
1738
|
-
const deadline = Date.now() + timeoutMs;
|
|
1739
|
-
do {
|
|
1740
|
-
if (!await this.capture(Math.max(1, deadline - Date.now()))) return false;
|
|
1741
|
-
if (this.identities.size === 0) return true;
|
|
1742
|
-
await setTimeout$1(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
1743
|
-
} while (Date.now() < deadline);
|
|
1744
|
-
return false;
|
|
1745
|
-
}
|
|
1746
|
-
retire() {
|
|
1747
|
-
this.retired = true;
|
|
1748
|
-
this.identities.clear();
|
|
1749
|
-
}
|
|
1750
|
-
};
|
|
1751
|
-
//#endregion
|
|
1752
1702
|
//#region src/acp/session-control-errors.ts
|
|
1753
1703
|
const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
1754
1704
|
function asRecord$1(value) {
|
|
@@ -1812,12 +1762,20 @@ function readTerminalOutputCeiling() {
|
|
|
1812
1762
|
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(bytes)) throw new Error("ACPX_TERMINAL_MAX_OUTPUT_BYTES must be a non-negative safe integer; zero disables the host ceiling");
|
|
1813
1763
|
return bytes === 0 ? void 0 : bytes;
|
|
1814
1764
|
}
|
|
1765
|
+
function resolveTerminalOutputLimit(requested, ceiling) {
|
|
1766
|
+
return Math.min(Math.max(0, Math.round(requested ?? DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES)), ceiling ?? Number.POSITIVE_INFINITY);
|
|
1767
|
+
}
|
|
1768
|
+
function tracksTerminalDescendants(terminal) {
|
|
1769
|
+
return terminal.killProcessGroup || terminal.descendants !== void 0;
|
|
1770
|
+
}
|
|
1771
|
+
function createTerminalDescendants(proc, killProcessGroup) {
|
|
1772
|
+
return process.platform === "win32" && killProcessGroup ? void 0 : new ProcessDescendants(proc, { ownProcessGroup: true });
|
|
1773
|
+
}
|
|
1815
1774
|
function trimToUtf8Boundary(buffer, limit) {
|
|
1816
1775
|
if (limit <= 0) return Buffer.alloc(0);
|
|
1817
|
-
|
|
1818
|
-
let start = buffer.length - limit;
|
|
1776
|
+
let start = Math.max(0, buffer.length - limit);
|
|
1819
1777
|
while (start < buffer.length && (buffer[start] & 192) === 128) start += 1;
|
|
1820
|
-
if (start >= buffer.length)
|
|
1778
|
+
if (start >= buffer.length) return Buffer.alloc(0);
|
|
1821
1779
|
return buffer.subarray(start);
|
|
1822
1780
|
}
|
|
1823
1781
|
async function defaultConfirmExecute(commandLine, signal) {
|
|
@@ -1854,7 +1812,8 @@ var TerminalManager = class {
|
|
|
1854
1812
|
this.onOperation = options.onOperation;
|
|
1855
1813
|
this.usesDefaultConfirmExecute = options.confirmExecute == null;
|
|
1856
1814
|
this.confirmExecute = options.confirmExecute ?? defaultConfirmExecute;
|
|
1857
|
-
|
|
1815
|
+
const killGraceMs = Math.max(0, Math.round(options.killGraceMs ?? DEFAULT_KILL_GRACE_MS));
|
|
1816
|
+
this.killGraceMs = Number.isFinite(killGraceMs) && killGraceMs <= 2147483647 ? killGraceMs : 1;
|
|
1858
1817
|
this.processHelperTimeoutMs = Math.max(1, Math.round(options.processHelperTimeoutMs ?? 8e3));
|
|
1859
1818
|
}
|
|
1860
1819
|
updatePermissionPolicy(permissionMode, nonInteractivePermissions) {
|
|
@@ -1875,8 +1834,7 @@ var TerminalManager = class {
|
|
|
1875
1834
|
const approved = await this.isExecuteApproved(commandLine, authority?.signal);
|
|
1876
1835
|
assertControlAuthority(authority);
|
|
1877
1836
|
if (!approved) throw new PermissionDeniedError("Permission denied for terminal/create");
|
|
1878
|
-
const
|
|
1879
|
-
const outputByteLimit = Math.min(requestedLimit, this.outputCeilingBytes ?? Number.POSITIVE_INFINITY);
|
|
1837
|
+
const outputByteLimit = resolveTerminalOutputLimit(params.outputByteLimit, this.outputCeilingBytes);
|
|
1880
1838
|
const { proc, spawnCommand } = await spawnTerminalProcess(params, this.cwd, authority);
|
|
1881
1839
|
let resolveExit = () => {};
|
|
1882
1840
|
const exitPromise = new Promise((resolve) => {
|
|
@@ -1886,6 +1844,7 @@ var TerminalManager = class {
|
|
|
1886
1844
|
process: proc,
|
|
1887
1845
|
killProcessGroup: spawnCommand.killProcessGroup,
|
|
1888
1846
|
descendantPids: /* @__PURE__ */ new Set(),
|
|
1847
|
+
descendants: createTerminalDescendants(proc, spawnCommand.killProcessGroup),
|
|
1889
1848
|
processHelperTimeoutMs: this.processHelperTimeoutMs,
|
|
1890
1849
|
output: Buffer.alloc(0),
|
|
1891
1850
|
truncated: false,
|
|
@@ -1899,10 +1858,8 @@ var TerminalManager = class {
|
|
|
1899
1858
|
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1900
1859
|
if (bytes.length === 0) return;
|
|
1901
1860
|
terminal.output = Buffer.concat([terminal.output, bytes]);
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
terminal.truncated = true;
|
|
1905
|
-
}
|
|
1861
|
+
terminal.truncated ||= terminal.output.length > terminal.outputByteLimit;
|
|
1862
|
+
if (terminal.truncated) terminal.output = trimToUtf8Boundary(terminal.output, terminal.outputByteLimit);
|
|
1906
1863
|
};
|
|
1907
1864
|
proc.stdout.on("data", appendOutput);
|
|
1908
1865
|
proc.stderr.on("data", appendOutput);
|
|
@@ -1911,9 +1868,10 @@ var TerminalManager = class {
|
|
|
1911
1868
|
proc.once("exit", (exitCode, signal) => {
|
|
1912
1869
|
terminal.exitCode = exitCode;
|
|
1913
1870
|
terminal.signal = signal;
|
|
1914
|
-
terminal.processGroupSnapshotPromise = rememberProcessGroupPids(terminal);
|
|
1871
|
+
terminal.processGroupSnapshotPromise = terminal.descendants ? terminal.descendants.capture(terminal.processHelperTimeoutMs).then(() => {}) : rememberProcessGroupPids(terminal);
|
|
1915
1872
|
(async () => {
|
|
1916
1873
|
await terminal.processGroupSnapshotPromise;
|
|
1874
|
+
terminal.processGroupSnapshotPromise = void 0;
|
|
1917
1875
|
terminal.resolveExit({
|
|
1918
1876
|
exitCode: exitCode ?? null,
|
|
1919
1877
|
signal: signal ?? null
|
|
@@ -1923,6 +1881,7 @@ var TerminalManager = class {
|
|
|
1923
1881
|
const terminalId = randomUUID();
|
|
1924
1882
|
this.terminals.set(terminalId, terminal);
|
|
1925
1883
|
try {
|
|
1884
|
+
await terminal.descendants?.capture(terminal.processHelperTimeoutMs);
|
|
1926
1885
|
assertControlAuthority(authority);
|
|
1927
1886
|
} catch (error) {
|
|
1928
1887
|
await this.releaseTerminal({
|
|
@@ -2036,6 +1995,9 @@ var TerminalManager = class {
|
|
|
2036
1995
|
try {
|
|
2037
1996
|
await this.killProcess(terminal);
|
|
2038
1997
|
await terminal.exitPromise.catch(() => {});
|
|
1998
|
+
terminal.descendants?.retire();
|
|
1999
|
+
terminal.process.stdout.destroy();
|
|
2000
|
+
terminal.process.stderr.destroy();
|
|
2039
2001
|
terminal.output = Buffer.alloc(0);
|
|
2040
2002
|
this.terminals.delete(params.terminalId);
|
|
2041
2003
|
this.emitOperation({
|
|
@@ -2058,10 +2020,13 @@ var TerminalManager = class {
|
|
|
2058
2020
|
}
|
|
2059
2021
|
}
|
|
2060
2022
|
async shutdown() {
|
|
2061
|
-
|
|
2023
|
+
const results = await Promise.allSettled(Array.from(this.terminals.keys(), (terminalId) => this.releaseTerminal({
|
|
2062
2024
|
terminalId,
|
|
2063
2025
|
sessionId: "shutdown"
|
|
2064
|
-
});
|
|
2026
|
+
})));
|
|
2027
|
+
const failures = [];
|
|
2028
|
+
for (const result of results) if (result.status === "rejected") failures.push(result.reason);
|
|
2029
|
+
if (failures.length > 0) throw new AggregateError(failures, "Terminal shutdown failed", { cause: failures[0] });
|
|
2065
2030
|
}
|
|
2066
2031
|
getTerminal(terminalId) {
|
|
2067
2032
|
return this.terminals.get(terminalId);
|
|
@@ -2079,13 +2044,13 @@ var TerminalManager = class {
|
|
|
2079
2044
|
return terminal.exitCode === void 0 && terminal.signal === void 0;
|
|
2080
2045
|
}
|
|
2081
2046
|
async killProcess(terminal) {
|
|
2082
|
-
if (!this.isRunning(terminal) && !terminal
|
|
2047
|
+
if (!this.isRunning(terminal) && !tracksTerminalDescendants(terminal)) return;
|
|
2083
2048
|
try {
|
|
2084
2049
|
await this.signalProcess(terminal, "SIGTERM");
|
|
2085
2050
|
} catch {
|
|
2086
2051
|
return;
|
|
2087
2052
|
}
|
|
2088
|
-
if (await this.waitForCleanupAfterSignal(terminal) && !terminal.killProcessGroup) return;
|
|
2053
|
+
if (await this.waitForCleanupAfterSignal(terminal) && (!terminal.killProcessGroup || terminal.descendants)) return;
|
|
2089
2054
|
try {
|
|
2090
2055
|
await this.signalProcess(terminal, "SIGKILL");
|
|
2091
2056
|
} catch {
|
|
@@ -2094,15 +2059,16 @@ var TerminalManager = class {
|
|
|
2094
2059
|
await this.waitForFinalCleanup(terminal);
|
|
2095
2060
|
}
|
|
2096
2061
|
async signalProcess(terminal, signal) {
|
|
2062
|
+
if (terminal.descendants) {
|
|
2063
|
+
await terminal.descendants.signal(signal, terminal.processHelperTimeoutMs);
|
|
2064
|
+
if (this.isRunning(terminal)) terminal.process.kill(signal);
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2097
2067
|
const pid = terminal.process.pid;
|
|
2098
2068
|
if (terminal.killProcessGroup && pid && process.platform === "win32") {
|
|
2099
2069
|
await this.signalWindowsProcessGroup(terminal, pid, signal);
|
|
2100
2070
|
return;
|
|
2101
2071
|
}
|
|
2102
|
-
if (terminal.killProcessGroup && pid) {
|
|
2103
|
-
await this.signalPosixProcessGroup(terminal, pid, signal);
|
|
2104
|
-
return;
|
|
2105
|
-
}
|
|
2106
2072
|
terminal.process.kill(signal);
|
|
2107
2073
|
}
|
|
2108
2074
|
async signalWindowsProcessGroup(terminal, pid, signal) {
|
|
@@ -2113,14 +2079,6 @@ var TerminalManager = class {
|
|
|
2113
2079
|
}
|
|
2114
2080
|
for (const descendantPid of terminal.descendantPids) await killWindowsProcessTree(descendantPid, signal, terminal.processHelperTimeoutMs);
|
|
2115
2081
|
}
|
|
2116
|
-
async signalPosixProcessGroup(terminal, pid, signal) {
|
|
2117
|
-
await this.captureDescendantPids(terminal, pid);
|
|
2118
|
-
if (hasLiveProcessGroup(pid)) {
|
|
2119
|
-
sendSignal(-pid, signal);
|
|
2120
|
-
return;
|
|
2121
|
-
}
|
|
2122
|
-
for (const descendantPid of terminal.descendantPids) sendSignal(descendantPid, signal);
|
|
2123
|
-
}
|
|
2124
2082
|
async captureDescendantPids(terminal, pid) {
|
|
2125
2083
|
if (!this.isRunning(terminal)) await terminal.processGroupSnapshotPromise?.catch(() => {});
|
|
2126
2084
|
for (const descendantPid of await listDescendantPids(pid, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(descendantPid);
|
|
@@ -2129,12 +2087,13 @@ var TerminalManager = class {
|
|
|
2129
2087
|
if (!await this.waitForCleanupAfterSignal(terminal) && process.platform === "win32") throw new Error("Terminal process cleanup did not finish after SIGKILL");
|
|
2130
2088
|
}
|
|
2131
2089
|
async waitForCleanupAfterSignal(terminal) {
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2090
|
+
const deadline = performance.now() + this.killGraceMs;
|
|
2091
|
+
while (this.isRunning(terminal) || terminal.processGroupSnapshotPromise || await hasLiveTerminalDescendants(terminal, deadline - performance.now()) || hasLivePid(terminal.descendantPids)) {
|
|
2092
|
+
const remaining = deadline - performance.now();
|
|
2093
|
+
if (remaining <= 0) return false;
|
|
2094
|
+
await waitMs(Math.min(25, remaining));
|
|
2095
|
+
}
|
|
2096
|
+
return true;
|
|
2138
2097
|
}
|
|
2139
2098
|
};
|
|
2140
2099
|
async function spawnTerminalProcess(params, defaultCwd, authority) {
|
|
@@ -2155,7 +2114,7 @@ async function spawnTerminalProcess(params, defaultCwd, authority) {
|
|
|
2155
2114
|
}
|
|
2156
2115
|
async function spawnAndWait(spawnCommand, params, defaultCwd, authority) {
|
|
2157
2116
|
const spawnOptions = buildTerminalSpawnOptions(spawnCommand.command, params.cwd ?? defaultCwd, params.env);
|
|
2158
|
-
if (spawnCommand.killProcessGroup) spawnOptions.detached = true;
|
|
2117
|
+
if (spawnCommand.killProcessGroup || process.platform !== "win32") spawnOptions.detached = true;
|
|
2159
2118
|
assertControlAuthority(authority);
|
|
2160
2119
|
const proc = spawn(spawnCommand.command, spawnCommand.args, spawnOptions);
|
|
2161
2120
|
await waitForSpawn(proc);
|
|
@@ -2183,7 +2142,7 @@ function commandPathExists(command, cwd) {
|
|
|
2183
2142
|
async function listDescendantPids(rootPid, timeoutMs) {
|
|
2184
2143
|
let output;
|
|
2185
2144
|
try {
|
|
2186
|
-
output = await
|
|
2145
|
+
output = await runWindowsProcessListCommand(timeoutMs);
|
|
2187
2146
|
} catch {
|
|
2188
2147
|
return [];
|
|
2189
2148
|
}
|
|
@@ -2216,38 +2175,10 @@ function parseProcessListLine(line) {
|
|
|
2216
2175
|
parentPid
|
|
2217
2176
|
};
|
|
2218
2177
|
}
|
|
2219
|
-
async function runProcessListCommand(timeoutMs) {
|
|
2220
|
-
if (process.platform === "win32") return await runWindowsProcessListCommand(timeoutMs);
|
|
2221
|
-
return await runTimedExecFile("ps", ["-eo", "pid=,ppid="], { timeoutMs });
|
|
2222
|
-
}
|
|
2223
2178
|
async function rememberProcessGroupPids(terminal) {
|
|
2224
2179
|
const processGroupId = terminal.process.pid;
|
|
2225
2180
|
if (!terminal.killProcessGroup || !processGroupId) return;
|
|
2226
|
-
|
|
2227
|
-
for (const pid of await listDescendantPids(processGroupId, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(pid);
|
|
2228
|
-
return;
|
|
2229
|
-
}
|
|
2230
|
-
for (const pid of await listProcessGroupPids(processGroupId, terminal.processHelperTimeoutMs)) if (pid !== processGroupId) terminal.descendantPids.add(pid);
|
|
2231
|
-
}
|
|
2232
|
-
async function listProcessGroupPids(processGroupId, timeoutMs) {
|
|
2233
|
-
let output;
|
|
2234
|
-
try {
|
|
2235
|
-
output = await runProcessGroupListCommand(timeoutMs);
|
|
2236
|
-
} catch {
|
|
2237
|
-
return [];
|
|
2238
|
-
}
|
|
2239
|
-
const pids = [];
|
|
2240
|
-
for (const line of output.split("\n")) {
|
|
2241
|
-
const match = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
2242
|
-
if (!match) continue;
|
|
2243
|
-
const pid = Number(match[1]);
|
|
2244
|
-
const pgid = Number(match[2]);
|
|
2245
|
-
if (Number.isInteger(pid) && Number.isInteger(pgid) && pid > 0 && pgid === processGroupId) pids.push(pid);
|
|
2246
|
-
}
|
|
2247
|
-
return pids;
|
|
2248
|
-
}
|
|
2249
|
-
async function runProcessGroupListCommand(timeoutMs) {
|
|
2250
|
-
return await runTimedExecFile("ps", ["-eo", "pid=,pgid="], { timeoutMs });
|
|
2181
|
+
for (const pid of await listDescendantPids(processGroupId, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(pid);
|
|
2251
2182
|
}
|
|
2252
2183
|
async function runWindowsProcessListCommand(timeoutMs) {
|
|
2253
2184
|
const command = ["Get-CimInstance Win32_Process |", "ForEach-Object { \"$($_.ProcessId) $($_.ParentProcessId)\" }"].join(" ");
|
|
@@ -2275,22 +2206,11 @@ async function killWindowsProcessTree(pid, signal, timeoutMs = PROCESS_HELPER_TI
|
|
|
2275
2206
|
});
|
|
2276
2207
|
} catch {}
|
|
2277
2208
|
}
|
|
2278
|
-
function
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
function hasLiveProcessGroup(processGroupId) {
|
|
2284
|
-
try {
|
|
2285
|
-
process.kill(-processGroupId, 0);
|
|
2286
|
-
return true;
|
|
2287
|
-
} catch {
|
|
2288
|
-
return false;
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
function hasLiveTerminalProcessGroup(terminal) {
|
|
2292
|
-
const pid = terminal.process.pid;
|
|
2293
|
-
return Boolean(terminal.killProcessGroup && pid && process.platform !== "win32" && hasLiveProcessGroup(pid));
|
|
2209
|
+
async function hasLiveTerminalDescendants(terminal, timeoutMs) {
|
|
2210
|
+
const descendants = terminal.descendants;
|
|
2211
|
+
if (!descendants) return false;
|
|
2212
|
+
if (timeoutMs > 0) await descendants.capture(Math.min(terminal.processHelperTimeoutMs, timeoutMs));
|
|
2213
|
+
return descendants.hasTrackedProcesses();
|
|
2294
2214
|
}
|
|
2295
2215
|
function hasLivePid(pids) {
|
|
2296
2216
|
for (const pid of pids) try {
|
|
@@ -2400,26 +2320,13 @@ function childProcessIsRunning(agent) {
|
|
|
2400
2320
|
function cancelledPermissionResponse() {
|
|
2401
2321
|
return { outcome: { outcome: "cancelled" } };
|
|
2402
2322
|
}
|
|
2403
|
-
function shouldSuppressSdkConsoleError(args) {
|
|
2404
|
-
if (args.length === 0) return false;
|
|
2405
|
-
return typeof args[0] === "string" && args[0] === "Error handling request";
|
|
2406
|
-
}
|
|
2407
|
-
function installSdkConsoleErrorSuppression() {
|
|
2408
|
-
const originalConsoleError = console.error;
|
|
2409
|
-
console.error = (...args) => {
|
|
2410
|
-
if (shouldSuppressSdkConsoleError(args)) return;
|
|
2411
|
-
originalConsoleError(...args);
|
|
2412
|
-
};
|
|
2413
|
-
return () => {
|
|
2414
|
-
console.error = originalConsoleError;
|
|
2415
|
-
};
|
|
2416
|
-
}
|
|
2417
2323
|
var AcpClient = class {
|
|
2418
2324
|
options;
|
|
2419
2325
|
connection;
|
|
2420
2326
|
agent;
|
|
2421
2327
|
agentDescendants = /* @__PURE__ */ new WeakMap();
|
|
2422
2328
|
agentCleanups = /* @__PURE__ */ new WeakMap();
|
|
2329
|
+
commandProbes = /* @__PURE__ */ new Set();
|
|
2423
2330
|
initResult;
|
|
2424
2331
|
loadedSessionId;
|
|
2425
2332
|
eventHandlers;
|
|
@@ -2441,6 +2348,7 @@ var AcpClient = class {
|
|
|
2441
2348
|
cancellingSessionIds = /* @__PURE__ */ new Set();
|
|
2442
2349
|
permissionAbortControllers = /* @__PURE__ */ new Map();
|
|
2443
2350
|
closing = false;
|
|
2351
|
+
closeTask;
|
|
2444
2352
|
closeEpoch = 0;
|
|
2445
2353
|
agentStartedAt;
|
|
2446
2354
|
lastAgentExit;
|
|
@@ -2537,7 +2445,7 @@ var AcpClient = class {
|
|
|
2537
2445
|
this.terminalManager.updatePermissionPolicy(this.options.permissionMode, this.options.nonInteractivePermissions);
|
|
2538
2446
|
}
|
|
2539
2447
|
hasLiveConnection() {
|
|
2540
|
-
return this.connection != null && !this.connection.signal.aborted && this.agent != null && isChildProcessRunning(this.agent);
|
|
2448
|
+
return !this.closing && this.connection != null && !this.connection.signal.aborted && this.agent != null && isChildProcessRunning(this.agent);
|
|
2541
2449
|
}
|
|
2542
2450
|
hasReusableSession(sessionId) {
|
|
2543
2451
|
return this.hasLiveConnection() && this.loadedSessionId === sessionId;
|
|
@@ -2560,15 +2468,17 @@ var AcpClient = class {
|
|
|
2560
2468
|
}
|
|
2561
2469
|
fallback?.abort();
|
|
2562
2470
|
}
|
|
2563
|
-
async start() {
|
|
2471
|
+
async start(authority) {
|
|
2472
|
+
assertControlAuthority(authority);
|
|
2564
2473
|
if (this.hasLiveConnection()) return;
|
|
2565
|
-
|
|
2566
|
-
const epoch = this.closeEpoch;
|
|
2474
|
+
const epoch = await this.waitForPreviousClose();
|
|
2567
2475
|
const maxMessageBytes = readMaxAcpMessageBytes();
|
|
2568
|
-
const launch = await this.resolveAgentLaunchPlan();
|
|
2476
|
+
const launch = await this.resolveAgentLaunchPlan(epoch, authority);
|
|
2477
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2569
2478
|
this.logAgentLaunch(launch);
|
|
2570
2479
|
await this.ensureLaunchSupport(launch);
|
|
2571
|
-
|
|
2480
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2481
|
+
const { child, process: startedProcess } = await this.spawnAgentProcess(launch, authority);
|
|
2572
2482
|
if (this.closeEpoch === epoch) {
|
|
2573
2483
|
this.agent = child;
|
|
2574
2484
|
this.closing = false;
|
|
@@ -2591,7 +2501,7 @@ var AcpClient = class {
|
|
|
2591
2501
|
startupFailure.dispose();
|
|
2592
2502
|
throw error;
|
|
2593
2503
|
}
|
|
2594
|
-
await this.stopIfClosedDuringLaunch(epoch, child, startupFailure);
|
|
2504
|
+
await this.stopIfClosedDuringLaunch(epoch, child, startupFailure, authority);
|
|
2595
2505
|
const input = Writable.toWeb(child.stdin);
|
|
2596
2506
|
const output = Readable.toWeb(child.stdout);
|
|
2597
2507
|
let connection;
|
|
@@ -2606,9 +2516,10 @@ var AcpClient = class {
|
|
|
2606
2516
|
elicitationModes: this.options.elicitationModes
|
|
2607
2517
|
});
|
|
2608
2518
|
connection = this.createConnection(stream, launch, capabilities);
|
|
2609
|
-
|
|
2610
|
-
this.
|
|
2611
|
-
}
|
|
2519
|
+
const onConnectionClose = () => {
|
|
2520
|
+
this.handleAgentDisconnect(child, "connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
2521
|
+
};
|
|
2522
|
+
connection.signal.addEventListener("abort", onConnectionClose, { once: true });
|
|
2612
2523
|
await this.initializeAgentConnection({
|
|
2613
2524
|
child,
|
|
2614
2525
|
connection,
|
|
@@ -2617,13 +2528,38 @@ var AcpClient = class {
|
|
|
2617
2528
|
launch,
|
|
2618
2529
|
capabilities
|
|
2619
2530
|
});
|
|
2531
|
+
if (connection.signal.aborted) onConnectionClose();
|
|
2620
2532
|
}
|
|
2621
|
-
async
|
|
2533
|
+
async waitForPreviousClose() {
|
|
2534
|
+
const cleanup = this.closeTask ?? (this.connection || this.agent ? this.close() : void 0);
|
|
2535
|
+
const epoch = this.closeEpoch;
|
|
2536
|
+
await cleanup;
|
|
2537
|
+
if (this.closeEpoch !== epoch) throw new Error("ACP client was closed while the agent was starting");
|
|
2538
|
+
return epoch;
|
|
2539
|
+
}
|
|
2540
|
+
async resolveAgentLaunchPlan(epoch, authority) {
|
|
2622
2541
|
const configuredCommand = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
|
|
2623
2542
|
const resolvedBuiltInLaunch = resolveBuiltInAgentLaunch(this.options.agentCommand);
|
|
2624
2543
|
const spawnCommand = resolvedBuiltInLaunch?.command ?? configuredCommand.command;
|
|
2544
|
+
const spawnOptions = buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials, this.options.sessionOptions?.env, this.options.agentProcessEnv);
|
|
2545
|
+
const commandContext = {
|
|
2546
|
+
env: spawnOptions.env,
|
|
2547
|
+
readOutput: async (command, args, timeoutMs, diagnostic = false) => {
|
|
2548
|
+
try {
|
|
2549
|
+
const output = await this.readAgentCommandOutput(command, args, timeoutMs, spawnOptions, epoch, authority);
|
|
2550
|
+
await admitCommandProbe(() => this.assertCommandProbeActive(epoch, authority));
|
|
2551
|
+
return output;
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
if (error instanceof CommandProbeAdmissionError) {
|
|
2554
|
+
if (diagnostic) return;
|
|
2555
|
+
throw error.reason;
|
|
2556
|
+
}
|
|
2557
|
+
throw error;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
};
|
|
2625
2561
|
let args = resolvedBuiltInLaunch?.args ?? configuredCommand.args;
|
|
2626
|
-
args = await resolveGeminiCommandArgs(spawnCommand, args);
|
|
2562
|
+
args = await resolveGeminiCommandArgs(spawnCommand, args, commandContext);
|
|
2627
2563
|
if (isQoderAcpCommand(spawnCommand, args)) args = buildQoderAcpCommandArgs(args, this.options);
|
|
2628
2564
|
return {
|
|
2629
2565
|
spawnCommand,
|
|
@@ -2633,7 +2569,8 @@ var AcpClient = class {
|
|
|
2633
2569
|
geminiAcp: isGeminiAcpCommand(spawnCommand, args),
|
|
2634
2570
|
copilotAcp: isCopilotAcpCommand(spawnCommand, args),
|
|
2635
2571
|
claudeAcp: isClaudeAcpCommand(spawnCommand, args),
|
|
2636
|
-
spawnOptions
|
|
2572
|
+
spawnOptions,
|
|
2573
|
+
commandContext
|
|
2637
2574
|
};
|
|
2638
2575
|
}
|
|
2639
2576
|
logAgentLaunch(plan) {
|
|
@@ -2649,7 +2586,7 @@ var AcpClient = class {
|
|
|
2649
2586
|
this.log(`spawning agent: ${plan.spawnCommand} ${plan.args.join(" ")}`);
|
|
2650
2587
|
}
|
|
2651
2588
|
async ensureLaunchSupport(plan) {
|
|
2652
|
-
if (plan.copilotAcp) await ensureCopilotAcpSupport(plan.spawnCommand);
|
|
2589
|
+
if (plan.copilotAcp) await ensureCopilotAcpSupport(plan.spawnCommand, plan.commandContext);
|
|
2653
2590
|
if (!plan.claudeAcp) return;
|
|
2654
2591
|
const claudeExe = resolveClaudeCodeExecutable(process.platform, plan.spawnOptions.env);
|
|
2655
2592
|
if (claudeExe) {
|
|
@@ -2657,21 +2594,36 @@ var AcpClient = class {
|
|
|
2657
2594
|
this.log(`resolved system Claude Code executable: ${claudeExe}`);
|
|
2658
2595
|
}
|
|
2659
2596
|
}
|
|
2660
|
-
|
|
2661
|
-
const
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2597
|
+
prepareProcessLaunch(command, args, options) {
|
|
2598
|
+
const resolved = buildAgentSpawnCommand(command, args, process.platform, options.env);
|
|
2599
|
+
return {
|
|
2600
|
+
launch: Object.freeze({
|
|
2601
|
+
launchId: randomUUID(),
|
|
2602
|
+
scope: snapshotProcessLaunchScope(this.options.processLaunchScope),
|
|
2603
|
+
command: resolved.command,
|
|
2604
|
+
args: Object.freeze([...resolved.args]),
|
|
2605
|
+
cwd: options.cwd
|
|
2606
|
+
}),
|
|
2607
|
+
windowsVerbatimArguments: resolved.windowsVerbatimArguments
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
startedProcess(launch, pid) {
|
|
2611
|
+
return Object.freeze({
|
|
2612
|
+
...launch,
|
|
2613
|
+
pid,
|
|
2614
|
+
startedAt: isoNow$2()
|
|
2668
2615
|
});
|
|
2616
|
+
}
|
|
2617
|
+
async spawnAgentProcess(plan, authority) {
|
|
2618
|
+
const prepared = this.prepareProcessLaunch(plan.spawnCommand, plan.args, plan.spawnOptions);
|
|
2619
|
+
const { launch } = prepared;
|
|
2669
2620
|
await this.options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
2621
|
+
assertControlAuthority(authority);
|
|
2670
2622
|
let spawnedChild;
|
|
2671
2623
|
try {
|
|
2672
|
-
spawnedChild = spawn(
|
|
2624
|
+
spawnedChild = spawn(launch.command, [...launch.args], {
|
|
2673
2625
|
...plan.spawnOptions,
|
|
2674
|
-
windowsVerbatimArguments:
|
|
2626
|
+
windowsVerbatimArguments: prepared.windowsVerbatimArguments
|
|
2675
2627
|
});
|
|
2676
2628
|
await waitForSpawn(spawnedChild);
|
|
2677
2629
|
} catch (error) {
|
|
@@ -2690,21 +2642,86 @@ var AcpClient = class {
|
|
|
2690
2642
|
}
|
|
2691
2643
|
return {
|
|
2692
2644
|
child,
|
|
2693
|
-
process:
|
|
2694
|
-
...launch,
|
|
2695
|
-
pid,
|
|
2696
|
-
startedAt: isoNow$2()
|
|
2697
|
-
})
|
|
2645
|
+
process: this.startedProcess(launch, pid)
|
|
2698
2646
|
};
|
|
2699
2647
|
}
|
|
2648
|
+
assertCommandProbeActive(epoch, authority) {
|
|
2649
|
+
assertControlAuthority(authority);
|
|
2650
|
+
if (this.closeEpoch !== epoch) throw new Error("ACP client was closed while the agent was starting");
|
|
2651
|
+
}
|
|
2652
|
+
async readAgentCommandOutput(command, args, timeoutMs, options, epoch, authority) {
|
|
2653
|
+
const prepared = this.prepareProcessLaunch(command, args, options);
|
|
2654
|
+
const { launch } = prepared;
|
|
2655
|
+
await admitCommandProbe(async () => {
|
|
2656
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2657
|
+
await this.options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
2658
|
+
});
|
|
2659
|
+
try {
|
|
2660
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2661
|
+
} catch (error) {
|
|
2662
|
+
throw new CommandProbeAdmissionError(error);
|
|
2663
|
+
}
|
|
2664
|
+
let child;
|
|
2665
|
+
try {
|
|
2666
|
+
child = spawn(launch.command, [...launch.args], {
|
|
2667
|
+
cwd: options.cwd,
|
|
2668
|
+
env: options.env,
|
|
2669
|
+
stdio: [
|
|
2670
|
+
"ignore",
|
|
2671
|
+
"pipe",
|
|
2672
|
+
"pipe"
|
|
2673
|
+
],
|
|
2674
|
+
windowsHide: true,
|
|
2675
|
+
windowsVerbatimArguments: prepared.windowsVerbatimArguments,
|
|
2676
|
+
detached: process.platform !== "win32"
|
|
2677
|
+
});
|
|
2678
|
+
} catch (error) {
|
|
2679
|
+
this.notifyProcessSpawnFailure(launch, new AgentSpawnError(command, error));
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
this.commandProbes.add(child);
|
|
2683
|
+
this.agentDescendants.set(child, new ProcessDescendants(child, { ownProcessGroup: true }));
|
|
2684
|
+
const capture = captureCommandProbeOutput(child, timeoutMs, () => this.terminateAgentProcess(child));
|
|
2685
|
+
try {
|
|
2686
|
+
try {
|
|
2687
|
+
await waitForSpawn(child);
|
|
2688
|
+
} catch (error) {
|
|
2689
|
+
this.notifyProcessSpawnFailure(launch, new AgentSpawnError(command, error));
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2692
|
+
if (child.pid === void 0) {
|
|
2693
|
+
this.notifyProcessSpawnFailure(launch, new AgentSpawnError(command, /* @__PURE__ */ new Error("spawned probe did not expose a PID")));
|
|
2694
|
+
return;
|
|
2695
|
+
}
|
|
2696
|
+
const started = this.startedProcess(launch, child.pid);
|
|
2697
|
+
await this.admitSpawnedProcess(child, (barrier) => this.attachProcessExitObserver(child, started, barrier), () => admitCommandProbe(async () => {
|
|
2698
|
+
await this.options.processLifecycle?.onSpawned?.(started);
|
|
2699
|
+
this.assertCommandProbeActive(epoch, authority);
|
|
2700
|
+
}));
|
|
2701
|
+
const output = await capture.result;
|
|
2702
|
+
await admitCommandProbe(() => this.assertCommandProbeActive(epoch, authority));
|
|
2703
|
+
return output;
|
|
2704
|
+
} finally {
|
|
2705
|
+
try {
|
|
2706
|
+
await this.terminateAgentProcess(child);
|
|
2707
|
+
} finally {
|
|
2708
|
+
capture.dispose();
|
|
2709
|
+
this.commandProbes.delete(child);
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2700
2713
|
async admitAndObserveSpawnedProcess(child, process) {
|
|
2714
|
+
await this.admitSpawnedProcess(child, (barrier) => this.attachAgentLifecycleObservers(child, process, barrier), async () => {
|
|
2715
|
+
await this.options.processLifecycle?.onSpawned?.(process);
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
async admitSpawnedProcess(child, observe, admit) {
|
|
2701
2719
|
let releaseExitNotification = () => {};
|
|
2702
|
-
|
|
2720
|
+
observe(new Promise((resolve) => {
|
|
2703
2721
|
releaseExitNotification = resolve;
|
|
2704
|
-
});
|
|
2705
|
-
this.attachAgentLifecycleObservers(child, process, exitNotificationBarrier);
|
|
2722
|
+
}));
|
|
2706
2723
|
try {
|
|
2707
|
-
await
|
|
2724
|
+
await admit();
|
|
2708
2725
|
} catch (error) {
|
|
2709
2726
|
await this.terminateAgentProcess(child);
|
|
2710
2727
|
throw error;
|
|
@@ -2770,7 +2787,7 @@ var AcpClient = class {
|
|
|
2770
2787
|
await this.captureAgentDescendants(params.child);
|
|
2771
2788
|
const normalizedError = error instanceof AcpMessageLimitError ? error : await this.normalizeInitializeError(error, params.child, params.startupStderr);
|
|
2772
2789
|
await this.terminateAgentProcess(params.child);
|
|
2773
|
-
if (params.launch.geminiAcp && error instanceof TimeoutError) throw new GeminiAcpStartupTimeoutError(await buildGeminiAcpStartupTimeoutMessage(params.launch.spawnCommand), {
|
|
2790
|
+
if (params.launch.geminiAcp && error instanceof TimeoutError) throw new GeminiAcpStartupTimeoutError(await buildGeminiAcpStartupTimeoutMessage(params.launch.spawnCommand, params.launch.commandContext), {
|
|
2774
2791
|
cause: error,
|
|
2775
2792
|
retryable: true
|
|
2776
2793
|
});
|
|
@@ -2788,7 +2805,8 @@ var AcpClient = class {
|
|
|
2788
2805
|
onPromptRequestWritten: (active, owner) => this.onPromptRequestWritten(active, owner)
|
|
2789
2806
|
});
|
|
2790
2807
|
}
|
|
2791
|
-
async createSession(cwd = this.options.cwd) {
|
|
2808
|
+
async createSession(cwd = this.options.cwd, authority) {
|
|
2809
|
+
assertControlAuthority(authority);
|
|
2792
2810
|
const connection = this.getConnection();
|
|
2793
2811
|
const { command, args } = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
|
|
2794
2812
|
const claudeAcp = isClaudeAcpCommand(command, args);
|
|
@@ -2799,7 +2817,7 @@ var AcpClient = class {
|
|
|
2799
2817
|
cwd: sessionCwd,
|
|
2800
2818
|
mcpServers: this.options.mcpServers ?? [],
|
|
2801
2819
|
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions, claudeAcp)
|
|
2802
|
-
}));
|
|
2820
|
+
}), authority);
|
|
2803
2821
|
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
|
2804
2822
|
} catch (error) {
|
|
2805
2823
|
if (claudeAcp && error instanceof TimeoutError) throw new ClaudeAcpSessionCreateTimeoutError(buildClaudeAcpSessionCreateTimeoutMessage(), {
|
|
@@ -2831,6 +2849,7 @@ var AcpClient = class {
|
|
|
2831
2849
|
async loadSessionWithOptions(sessionId, cwd = this.options.cwd, options = {}) {
|
|
2832
2850
|
const connection = this.getConnection();
|
|
2833
2851
|
const sessionCwd = await resolveAgentSessionCwd(cwd, this.options.agentCommand);
|
|
2852
|
+
assertControlAuthority(options.authority);
|
|
2834
2853
|
const previousSuppression = this.applySessionUpdateSuppression(Boolean(options.suppressReplayUpdates));
|
|
2835
2854
|
let response;
|
|
2836
2855
|
try {
|
|
@@ -2838,7 +2857,7 @@ var AcpClient = class {
|
|
|
2838
2857
|
sessionId,
|
|
2839
2858
|
cwd: sessionCwd,
|
|
2840
2859
|
mcpServers: this.options.mcpServers ?? []
|
|
2841
|
-
}));
|
|
2860
|
+
}), options.authority);
|
|
2842
2861
|
await this.waitForSessionUpdateDrain(options.replayIdleMs ?? REPLAY_IDLE_MS, options.replayDrainTimeoutMs ?? REPLAY_DRAIN_TIMEOUT_MS);
|
|
2843
2862
|
} finally {
|
|
2844
2863
|
this.restoreSessionUpdateSuppression(previousSuppression);
|
|
@@ -2848,14 +2867,15 @@ var AcpClient = class {
|
|
|
2848
2867
|
await this.captureAgentDescendants(this.agent);
|
|
2849
2868
|
return result;
|
|
2850
2869
|
}
|
|
2851
|
-
async resumeSession(sessionId, cwd = this.options.cwd) {
|
|
2870
|
+
async resumeSession(sessionId, cwd = this.options.cwd, authority) {
|
|
2871
|
+
assertControlAuthority(authority);
|
|
2852
2872
|
const connection = this.getConnection();
|
|
2853
2873
|
const sessionCwd = await resolveAgentSessionCwd(cwd, this.options.agentCommand);
|
|
2854
2874
|
const response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.resume, {
|
|
2855
2875
|
sessionId,
|
|
2856
2876
|
cwd: sessionCwd,
|
|
2857
2877
|
mcpServers: this.options.mcpServers ?? []
|
|
2858
|
-
}));
|
|
2878
|
+
}), authority);
|
|
2859
2879
|
this.loadedSessionId = sessionId;
|
|
2860
2880
|
const result = toReconnectedSessionResult(response);
|
|
2861
2881
|
await this.captureAgentDescendants(this.agent);
|
|
@@ -2877,7 +2897,6 @@ var AcpClient = class {
|
|
|
2877
2897
|
async prompt(sessionId, prompt, onRequestWritten, onElicitation, onPermissionRequest, authority) {
|
|
2878
2898
|
const connection = this.getConnection();
|
|
2879
2899
|
const normalizedPrompt = this.normalizePromptForAgent(prompt);
|
|
2880
|
-
const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
|
|
2881
2900
|
const previousActivePrompt = this.activePrompt;
|
|
2882
2901
|
const replacedOwners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
2883
2902
|
const activePrompt = this.beginActivePrompt(sessionId, onRequestWritten, onElicitation, onPermissionRequest, authority);
|
|
@@ -2895,7 +2914,6 @@ var AcpClient = class {
|
|
|
2895
2914
|
this.throwPromptPermissionFailureIfPresent(sessionId);
|
|
2896
2915
|
throw error;
|
|
2897
2916
|
} finally {
|
|
2898
|
-
restoreConsoleError?.();
|
|
2899
2917
|
this.clearActivePrompt(activePrompt);
|
|
2900
2918
|
}
|
|
2901
2919
|
}
|
|
@@ -3080,20 +3098,23 @@ var AcpClient = class {
|
|
|
3080
3098
|
if (timer) clearTimeout(timer);
|
|
3081
3099
|
}
|
|
3082
3100
|
}
|
|
3083
|
-
|
|
3101
|
+
close() {
|
|
3084
3102
|
this.closing = true;
|
|
3085
3103
|
this.closeEpoch += 1;
|
|
3104
|
+
if (this.closeTask) return this.closeTask;
|
|
3105
|
+
this.closeTask = Promise.resolve().then(() => this.finishClose()).finally(() => {
|
|
3106
|
+
this.closeTask = void 0;
|
|
3107
|
+
});
|
|
3108
|
+
return this.closeTask;
|
|
3109
|
+
}
|
|
3110
|
+
async finishClose() {
|
|
3086
3111
|
const permissionControllers = [...this.permissionAbortControllers.values()];
|
|
3087
3112
|
this.permissionAbortControllers.clear();
|
|
3088
3113
|
const owners = [...this.pendingPromptOwners];
|
|
3089
3114
|
this.abortActiveElicitation();
|
|
3090
3115
|
for (const owner of owners) owner.requestController.abort();
|
|
3091
3116
|
for (const controller of permissionControllers) controller.abort();
|
|
3092
|
-
await this.
|
|
3093
|
-
const agent = this.agent;
|
|
3094
|
-
if (agent) await this.terminateAgentProcess(agent);
|
|
3095
|
-
this.closeConnection();
|
|
3096
|
-
if (this.pendingConnectionRequests.size > 0) this.rejectPendingConnectionRequests(this.lastAgentExit ? new AgentDisconnectedError(this.lastAgentExit.reason, this.lastAgentExit.exitCode, this.lastAgentExit.signal, { outputAlreadyEmitted: Boolean(this.activePrompt) }) : new AgentDisconnectedError("connection_close", null, null, { outputAlreadyEmitted: Boolean(this.activePrompt) }));
|
|
3117
|
+
await this.retireNativeResources();
|
|
3097
3118
|
this.sessionUpdateChain = Promise.resolve();
|
|
3098
3119
|
this.observedSessionUpdates = 0;
|
|
3099
3120
|
this.processedSessionUpdates = 0;
|
|
@@ -3110,10 +3131,28 @@ var AcpClient = class {
|
|
|
3110
3131
|
this.connection = void 0;
|
|
3111
3132
|
this.agent = void 0;
|
|
3112
3133
|
}
|
|
3113
|
-
async
|
|
3134
|
+
async retireNativeResources() {
|
|
3135
|
+
const owned = [...this.commandProbes, ...this.agent ? [this.agent] : []];
|
|
3136
|
+
const failures = [];
|
|
3137
|
+
try {
|
|
3138
|
+
await this.terminalManager.shutdown();
|
|
3139
|
+
} catch (error) {
|
|
3140
|
+
failures.push(error);
|
|
3141
|
+
}
|
|
3142
|
+
const retired = await Promise.allSettled(owned.map((child) => this.terminateAgentProcess(child)));
|
|
3143
|
+
for (const result of retired) if (result.status === "rejected") failures.push(result.reason);
|
|
3144
|
+
try {
|
|
3145
|
+
this.closeConnection();
|
|
3146
|
+
} finally {
|
|
3147
|
+
this.rejectPendingConnectionRequests(this.lastAgentExit ? new AgentDisconnectedError(this.lastAgentExit.reason, this.lastAgentExit.exitCode, this.lastAgentExit.signal, { outputAlreadyEmitted: Boolean(this.activePrompt) }) : new AgentDisconnectedError("connection_close", null, null, { outputAlreadyEmitted: Boolean(this.activePrompt) }));
|
|
3148
|
+
}
|
|
3149
|
+
if (failures.length > 0) throw new AggregateError(failures, "ACP client cleanup failed", { cause: failures[0] });
|
|
3150
|
+
}
|
|
3151
|
+
async stopIfClosedDuringLaunch(epoch, child, startupFailure, authority) {
|
|
3114
3152
|
if (this.closeEpoch === epoch) return;
|
|
3115
3153
|
startupFailure.dispose();
|
|
3116
3154
|
await this.terminateAgentProcess(child);
|
|
3155
|
+
assertControlAuthority(authority);
|
|
3117
3156
|
throw new Error("ACP client was closed while the agent was starting");
|
|
3118
3157
|
}
|
|
3119
3158
|
abortActiveElicitation() {
|
|
@@ -3127,6 +3166,11 @@ var AcpClient = class {
|
|
|
3127
3166
|
if (descendants && !await descendants.capture()) this.log("could not verify agent descendants; skipping unverified process cleanup");
|
|
3128
3167
|
}
|
|
3129
3168
|
terminateAgentProcess(child) {
|
|
3169
|
+
if (child.pid === void 0) {
|
|
3170
|
+
this.agentDescendants.get(child)?.retire();
|
|
3171
|
+
this.detachAgentHandles(child, true);
|
|
3172
|
+
return Promise.resolve();
|
|
3173
|
+
}
|
|
3130
3174
|
let cleanup = this.agentCleanups.get(child);
|
|
3131
3175
|
if (!cleanup) {
|
|
3132
3176
|
cleanup = Promise.resolve().then(() => this.cleanupAgentProcess(child));
|
|
@@ -3136,12 +3180,12 @@ var AcpClient = class {
|
|
|
3136
3180
|
}
|
|
3137
3181
|
async cleanupAgentProcess(child) {
|
|
3138
3182
|
const descendants = this.agentDescendants.get(child);
|
|
3139
|
-
const deadline =
|
|
3183
|
+
const deadline = performance.now() + AGENT_CLEANUP_BUDGET_MS;
|
|
3140
3184
|
const stdinCloseGraceMs = resolveAgentCloseAfterStdinEndMs(this.options.agentCommand);
|
|
3141
3185
|
try {
|
|
3142
|
-
if (descendants) await descendants.capture(Math.max(1, deadline -
|
|
3186
|
+
if (descendants) await descendants.capture(Math.max(1, deadline - performance.now()));
|
|
3143
3187
|
this.endAgentStdin(child);
|
|
3144
|
-
await waitForChildExit(child, Math.min(stdinCloseGraceMs, Math.max(0, deadline -
|
|
3188
|
+
await waitForChildExit(child, Math.min(stdinCloseGraceMs, Math.max(0, deadline - performance.now())));
|
|
3145
3189
|
if (!await this.signalAgentAndDescendants(child, "SIGTERM", AGENT_CLOSE_TERM_GRACE_MS, deadline)) {
|
|
3146
3190
|
this.log("agent processes did not exit after SIGTERM; forcing SIGKILL");
|
|
3147
3191
|
await this.signalAgentAndDescendants(child, "SIGKILL", AGENT_CLOSE_KILL_GRACE_MS, deadline);
|
|
@@ -3152,18 +3196,18 @@ var AcpClient = class {
|
|
|
3152
3196
|
}
|
|
3153
3197
|
}
|
|
3154
3198
|
endAgentStdin(child) {
|
|
3155
|
-
if (child.stdin.destroyed) return;
|
|
3199
|
+
if (!child.stdin || child.stdin.destroyed) return;
|
|
3156
3200
|
try {
|
|
3157
3201
|
child.stdin.end();
|
|
3158
3202
|
} catch {}
|
|
3159
3203
|
}
|
|
3160
3204
|
async signalAgentAndDescendants(child, signal, waitMs, deadline) {
|
|
3161
3205
|
const descendants = this.agentDescendants.get(child);
|
|
3162
|
-
if (descendants &&
|
|
3206
|
+
if (descendants && performance.now() < deadline) await descendants.signal(signal, deadline - performance.now());
|
|
3163
3207
|
if (isChildProcessRunning(child)) try {
|
|
3164
3208
|
child.kill(signal);
|
|
3165
3209
|
} catch {}
|
|
3166
|
-
const remaining = Math.min(waitMs, Math.max(0, deadline -
|
|
3210
|
+
const remaining = Math.min(waitMs, Math.max(0, deadline - performance.now()));
|
|
3167
3211
|
const [exited, descendantsExited] = await Promise.all([waitForChildExit(child, remaining), descendants ? descendants.waitForExit(remaining) : Promise.resolve(true)]);
|
|
3168
3212
|
return exited && descendantsExited;
|
|
3169
3213
|
}
|
|
@@ -3473,24 +3517,34 @@ var AcpClient = class {
|
|
|
3473
3517
|
return cancelledPermissionResponse();
|
|
3474
3518
|
}
|
|
3475
3519
|
attachAgentLifecycleObservers(child, startedProcess, exitNotificationBarrier) {
|
|
3520
|
+
this.attachProcessExitObserver(child, startedProcess, exitNotificationBarrier, (exitCode, signal) => {
|
|
3521
|
+
this.handleAgentDisconnect(child, "process_exit", exitCode, signal);
|
|
3522
|
+
this.terminateAgentProcess(child);
|
|
3523
|
+
});
|
|
3524
|
+
child.once("close", (exitCode, signal) => {
|
|
3525
|
+
this.handleAgentDisconnect(child, "process_close", exitCode, signal);
|
|
3526
|
+
});
|
|
3527
|
+
child.stdout.once("close", () => {
|
|
3528
|
+
this.handleAgentDisconnect(child, "pipe_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
3529
|
+
});
|
|
3530
|
+
}
|
|
3531
|
+
attachProcessExitObserver(child, started, barrier, onProcessExit) {
|
|
3476
3532
|
const onExit = (exitCode, signal) => {
|
|
3477
3533
|
const exitedAt = isoNow$2();
|
|
3478
|
-
|
|
3479
|
-
this.
|
|
3480
|
-
exitNotificationBarrier.then(() => {
|
|
3481
|
-
this.notifyProcessExit(startedProcess, exitCode, signal, exitedAt);
|
|
3482
|
-
});
|
|
3534
|
+
onProcessExit?.(exitCode, signal);
|
|
3535
|
+
barrier.then(() => this.notifyProcessExit(started, exitCode, signal, exitedAt));
|
|
3483
3536
|
};
|
|
3484
3537
|
child.once("exit", onExit);
|
|
3485
3538
|
if (child.exitCode !== null || child.signalCode !== null) {
|
|
3486
3539
|
child.off("exit", onExit);
|
|
3487
3540
|
onExit(child.exitCode, child.signalCode);
|
|
3488
3541
|
}
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
child.
|
|
3493
|
-
|
|
3542
|
+
}
|
|
3543
|
+
handleAgentDisconnect(child, reason, exitCode, signal) {
|
|
3544
|
+
this.recordAgentExit(child, reason, exitCode, signal);
|
|
3545
|
+
if (this.agent !== child || this.closing || !this.connection) return;
|
|
3546
|
+
this.close().catch((error) => {
|
|
3547
|
+
this.log(`disconnected agent cleanup failed: ${String(error)}`);
|
|
3494
3548
|
});
|
|
3495
3549
|
}
|
|
3496
3550
|
notifyProcessSpawnFailure(launch, error) {
|
|
@@ -3854,7 +3908,6 @@ function ensureAgentMessage(conversation) {
|
|
|
3854
3908
|
return created;
|
|
3855
3909
|
}
|
|
3856
3910
|
function appendAgentText(agent, text) {
|
|
3857
|
-
if (!text.trim()) return;
|
|
3858
3911
|
const last = agent.content.at(-1);
|
|
3859
3912
|
if (last && isAgentTextContent(last)) {
|
|
3860
3913
|
last.Text = trimRuntimeText(`${last.Text}${text}`, MAX_RUNTIME_AGENT_TEXT_CHARS);
|
|
@@ -3864,7 +3917,6 @@ function appendAgentText(agent, text) {
|
|
|
3864
3917
|
agent.content.push(next);
|
|
3865
3918
|
}
|
|
3866
3919
|
function appendAgentThinking(agent, text) {
|
|
3867
|
-
if (!text.trim()) return;
|
|
3868
3920
|
const last = agent.content.at(-1);
|
|
3869
3921
|
if (last && isAgentThinkingContent(last)) {
|
|
3870
3922
|
last.Thinking.text = trimRuntimeText(`${last.Thinking.text}${text}`, MAX_RUNTIME_THINKING_CHARS);
|
|
@@ -3921,8 +3973,7 @@ function ensureToolUseContent(agent, toolCallId) {
|
|
|
3921
3973
|
return created;
|
|
3922
3974
|
}
|
|
3923
3975
|
function upsertToolResult(agent, toolCallId, patch) {
|
|
3924
|
-
const
|
|
3925
|
-
const fallback = existingToolResultValues(existing);
|
|
3976
|
+
const fallback = existingToolResultValues(hasOwn(agent.tool_results, toolCallId) ? agent.tool_results[toolCallId] : void 0);
|
|
3926
3977
|
const next = {
|
|
3927
3978
|
tool_use_id: toolCallId,
|
|
3928
3979
|
tool_name: patch.tool_name ?? fallback.tool_name,
|
|
@@ -3930,7 +3981,12 @@ function upsertToolResult(agent, toolCallId, patch) {
|
|
|
3930
3981
|
content: patch.content ?? fallback.content,
|
|
3931
3982
|
output: patch.output ?? fallback.output
|
|
3932
3983
|
};
|
|
3933
|
-
agent.tool_results
|
|
3984
|
+
Object.defineProperty(agent.tool_results, toolCallId, {
|
|
3985
|
+
value: next,
|
|
3986
|
+
writable: true,
|
|
3987
|
+
enumerable: true,
|
|
3988
|
+
configurable: true
|
|
3989
|
+
});
|
|
3934
3990
|
}
|
|
3935
3991
|
function existingToolResultValues(existing) {
|
|
3936
3992
|
if (existing) return existing;
|
|
@@ -3971,7 +4027,7 @@ function applyToolResultUpdate(agent, tool, update) {
|
|
|
3971
4027
|
const output = hasOwn(update, "rawOutput") ? deepClone(update.rawOutput) : void 0;
|
|
3972
4028
|
upsertToolResult(agent, update.toolCallId, {
|
|
3973
4029
|
tool_name: tool.name,
|
|
3974
|
-
is_error: statusIndicatesError(status),
|
|
4030
|
+
is_error: status === void 0 ? void 0 : statusIndicatesError(status),
|
|
3975
4031
|
content: output === void 0 ? void 0 : toToolResultContent(output),
|
|
3976
4032
|
output
|
|
3977
4033
|
});
|
|
@@ -4301,6 +4357,11 @@ function mergeSessionOptions(preferred, fallback) {
|
|
|
4301
4357
|
assignDefinedOption(merged, "env", mergeEnvRecords(fallback?.env, preferred?.env));
|
|
4302
4358
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
4303
4359
|
}
|
|
4360
|
+
function normalizeSessionOptionShape(options) {
|
|
4361
|
+
const normalized = mergeSessionOptions(options, void 0);
|
|
4362
|
+
if (normalized?.env !== void 0 && Object.keys(normalized.env).length === 0) delete normalized.env;
|
|
4363
|
+
return normalized && Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
4364
|
+
}
|
|
4304
4365
|
function mergeEnvRecords(fallback, preferred) {
|
|
4305
4366
|
if (!fallback && !preferred) return;
|
|
4306
4367
|
return {
|
|
@@ -4397,10 +4458,7 @@ function getDesiredModeId(state) {
|
|
|
4397
4458
|
function getDesiredConfigOptions(state) {
|
|
4398
4459
|
const desired = state?.desired_config_options;
|
|
4399
4460
|
if (!desired) return {};
|
|
4400
|
-
return Object.fromEntries(Object.entries(desired).
|
|
4401
|
-
const normalizedConfigId = normalizeModeId(configId);
|
|
4402
|
-
return normalizedConfigId && typeof value === "string" ? [[normalizedConfigId, value]] : [];
|
|
4403
|
-
}));
|
|
4461
|
+
return Object.fromEntries(Object.entries(desired).filter(([, value]) => typeof value === "string"));
|
|
4404
4462
|
}
|
|
4405
4463
|
function setDesiredModeId(record, modeId) {
|
|
4406
4464
|
const acpx = ensureAcpxState(record.acpx);
|
|
@@ -4410,10 +4468,9 @@ function setDesiredModeId(record, modeId) {
|
|
|
4410
4468
|
record.acpx = acpx;
|
|
4411
4469
|
}
|
|
4412
4470
|
function clearDesiredConfigOption(state, configId) {
|
|
4413
|
-
|
|
4414
|
-
if (!normalizedConfigId || !state.desired_config_options) return;
|
|
4471
|
+
if (typeof configId !== "string" || !state.desired_config_options) return;
|
|
4415
4472
|
const desired = { ...state.desired_config_options };
|
|
4416
|
-
delete desired[
|
|
4473
|
+
delete desired[configId];
|
|
4417
4474
|
if (Object.keys(desired).length > 0) state.desired_config_options = desired;
|
|
4418
4475
|
else delete state.desired_config_options;
|
|
4419
4476
|
}
|
|
@@ -4452,7 +4509,7 @@ async function applyRequestedModelIfAdvertised(params) {
|
|
|
4452
4509
|
if (params.models.currentModelId === requestedModel) return { applied: true };
|
|
4453
4510
|
return {
|
|
4454
4511
|
applied: true,
|
|
4455
|
-
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models), params.timeoutMs)
|
|
4512
|
+
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models, params.authority), params.timeoutMs)
|
|
4456
4513
|
};
|
|
4457
4514
|
}
|
|
4458
4515
|
//#endregion
|
|
@@ -4477,9 +4534,9 @@ function applyAcceptedConfigOptions(state, response) {
|
|
|
4477
4534
|
if (!response) return next;
|
|
4478
4535
|
applyConfigOptionsModelState(next, response.configOptions);
|
|
4479
4536
|
if (!next.desired_config_options) return next;
|
|
4480
|
-
const
|
|
4481
|
-
for (const option of response.configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(next.desired_config_options, option.id))
|
|
4482
|
-
if (
|
|
4537
|
+
const desiredEntries = [];
|
|
4538
|
+
for (const option of response.configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(next.desired_config_options, option.id)) desiredEntries.push([option.id, option.currentValue]);
|
|
4539
|
+
if (desiredEntries.length > 0) next.desired_config_options = Object.fromEntries(desiredEntries);
|
|
4483
4540
|
else delete next.desired_config_options;
|
|
4484
4541
|
return next;
|
|
4485
4542
|
}
|
|
@@ -4497,8 +4554,7 @@ function applyModelSelection(state, modelId, response) {
|
|
|
4497
4554
|
function applyConfigOptionSelection(state, configId, value, response, modelConfigId = advertisedModelState(state)?.configId) {
|
|
4498
4555
|
if (configId === modelConfigId || configId === modelStateFromConfigOptions(response.configOptions)?.configId) return applyModelSelection(state, value, response);
|
|
4499
4556
|
const next = cloneSessionAcpxState(state) ?? {};
|
|
4500
|
-
|
|
4501
|
-
else next.desired_config_options = {
|
|
4557
|
+
next.desired_config_options = {
|
|
4502
4558
|
...next.desired_config_options,
|
|
4503
4559
|
[configId]: value
|
|
4504
4560
|
};
|
|
@@ -4509,6 +4565,7 @@ function applyConfigOptionSelection(state, configId, value, response, modelConfi
|
|
|
4509
4565
|
const LOCK_RETRY_MS = 15;
|
|
4510
4566
|
const INCOMPLETE_RESERVATION_GRACE_MS = 15e3;
|
|
4511
4567
|
let lastCreatedAt = 0;
|
|
4568
|
+
const localTurns = /* @__PURE__ */ new Map();
|
|
4512
4569
|
async function readLock(filePath) {
|
|
4513
4570
|
try {
|
|
4514
4571
|
const stat = await fs$1.lstat(filePath, { bigint: true });
|
|
@@ -4522,11 +4579,6 @@ async function readLock(filePath) {
|
|
|
4522
4579
|
throw error;
|
|
4523
4580
|
}
|
|
4524
4581
|
}
|
|
4525
|
-
function lockPid(value) {
|
|
4526
|
-
if (!value || typeof value !== "object" || !("pid" in value)) return;
|
|
4527
|
-
const pid = value.pid;
|
|
4528
|
-
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
4529
|
-
}
|
|
4530
4582
|
function parseLock(payload) {
|
|
4531
4583
|
try {
|
|
4532
4584
|
return JSON.parse(payload);
|
|
@@ -4534,7 +4586,7 @@ function parseLock(payload) {
|
|
|
4534
4586
|
return;
|
|
4535
4587
|
}
|
|
4536
4588
|
}
|
|
4537
|
-
async function tryAcquireGuard(filePath) {
|
|
4589
|
+
async function tryAcquireGuard(filePath, owner, signal) {
|
|
4538
4590
|
try {
|
|
4539
4591
|
return await acquireFileLock(filePath, {
|
|
4540
4592
|
managerKey: "acpx.session-turn",
|
|
@@ -4549,9 +4601,9 @@ async function tryAcquireGuard(filePath) {
|
|
|
4549
4601
|
randomize: false
|
|
4550
4602
|
},
|
|
4551
4603
|
staleRecovery: "remove-if-unchanged",
|
|
4552
|
-
payload: () =>
|
|
4553
|
-
shouldReclaim: ({ payload }) =>
|
|
4554
|
-
shouldRemoveStaleLock: ({ payload }) =>
|
|
4604
|
+
payload: () => owner.payload,
|
|
4605
|
+
shouldReclaim: ({ payload }) => owner.hasExited(payload, false, signal),
|
|
4606
|
+
shouldRemoveStaleLock: ({ payload }) => owner.hasExited(payload, true, signal)
|
|
4555
4607
|
});
|
|
4556
4608
|
} catch (error) {
|
|
4557
4609
|
if (error.code === "file_lock_timeout") return;
|
|
@@ -4567,11 +4619,12 @@ async function removeObservedLock(filePath, observed) {
|
|
|
4567
4619
|
});
|
|
4568
4620
|
return true;
|
|
4569
4621
|
}
|
|
4570
|
-
async function recoverAbandonedLock(filePath) {
|
|
4622
|
+
async function recoverAbandonedLock(filePath, owner, signal) {
|
|
4571
4623
|
const observed = await readLock(filePath);
|
|
4572
4624
|
if (!observed) return true;
|
|
4573
|
-
const
|
|
4574
|
-
|
|
4625
|
+
const payload = parseLock(observed.payload);
|
|
4626
|
+
const pid = lockOwnerPid(payload);
|
|
4627
|
+
if (pid && (!await owner.hasExited(payload, false, signal) || !await owner.hasExited(payload, true, signal))) return false;
|
|
4575
4628
|
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return false;
|
|
4576
4629
|
if (await removeObservedLock(filePath, observed)) {
|
|
4577
4630
|
incrementPerfCounter("session.events.stale_lock_recovered");
|
|
@@ -4607,58 +4660,112 @@ async function removePublishedLock(filePath, payload) {
|
|
|
4607
4660
|
const current = await readLock(filePath);
|
|
4608
4661
|
if (current?.payload === payload) await removeObservedLock(filePath, current);
|
|
4609
4662
|
}
|
|
4610
|
-
async function rejectAcquisition(filePath,
|
|
4663
|
+
async function rejectAcquisition(filePath, receipt, error) {
|
|
4611
4664
|
const failures = [error];
|
|
4612
|
-
await
|
|
4613
|
-
failures.push(cleanupError);
|
|
4614
|
-
});
|
|
4615
|
-
await guard.release().catch((releaseError) => {
|
|
4616
|
-
failures.push(releaseError);
|
|
4665
|
+
await disposeTurn(filePath, receipt).catch((cleanupError) => {
|
|
4666
|
+
failures.push(...cleanupError instanceof AggregateError ? cleanupError.errors : [cleanupError]);
|
|
4617
4667
|
});
|
|
4618
4668
|
if (failures.length > 1) throw new AggregateError(failures, "Session turn acquisition and cleanup failed");
|
|
4619
4669
|
throw error;
|
|
4620
4670
|
}
|
|
4621
|
-
function
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4671
|
+
async function releaseTurnGuard(receipt) {
|
|
4672
|
+
await receipt.guard?.release();
|
|
4673
|
+
receipt.guard = void 0;
|
|
4674
|
+
}
|
|
4675
|
+
async function removeTurnMarker(filePath, receipt) {
|
|
4676
|
+
if (!receipt.removeMarker) return true;
|
|
4677
|
+
if (receipt.guard && !await receipt.guard.verifyStillHeld()) await releaseTurnGuard(receipt);
|
|
4678
|
+
receipt.guard ??= await tryAcquireGuard(filePath, receipt.owner);
|
|
4679
|
+
if (!receipt.guard) return false;
|
|
4680
|
+
await receipt.removeMarker();
|
|
4681
|
+
receipt.removeMarker = void 0;
|
|
4682
|
+
return true;
|
|
4683
|
+
}
|
|
4684
|
+
async function settleTurn(filePath, receipt) {
|
|
4685
|
+
if (receipt.settled) return true;
|
|
4686
|
+
const pending = receipt.pending ??= (async () => {
|
|
4687
|
+
receipt.failed = false;
|
|
4688
|
+
const failures = [];
|
|
4631
4689
|
try {
|
|
4632
|
-
await
|
|
4633
|
-
}
|
|
4634
|
-
|
|
4690
|
+
if (!await removeTurnMarker(filePath, receipt)) return false;
|
|
4691
|
+
} catch (error) {
|
|
4692
|
+
failures.push(error);
|
|
4635
4693
|
}
|
|
4636
|
-
|
|
4694
|
+
if (failures.length === 0 || !receipt.admitted) try {
|
|
4695
|
+
await releaseTurnGuard(receipt);
|
|
4696
|
+
} catch (error) {
|
|
4697
|
+
failures.push(error);
|
|
4698
|
+
}
|
|
4699
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn cleanup failed");
|
|
4700
|
+
if (failures.length === 1) throw failures[0];
|
|
4701
|
+
receipt.settled = true;
|
|
4702
|
+
localTurns.delete(filePath);
|
|
4703
|
+
return true;
|
|
4704
|
+
})();
|
|
4705
|
+
try {
|
|
4706
|
+
return await pending;
|
|
4707
|
+
} finally {
|
|
4708
|
+
if (receipt.pending === pending) {
|
|
4709
|
+
receipt.pending = void 0;
|
|
4710
|
+
receipt.failed = !receipt.settled;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4637
4713
|
}
|
|
4638
|
-
async function
|
|
4714
|
+
async function disposeTurn(filePath, receipt) {
|
|
4715
|
+
if (!await settleTurn(filePath, receipt)) throw new Error(`Session turn cleanup is waiting for its guard: ${filePath}`);
|
|
4716
|
+
}
|
|
4717
|
+
async function tryPublishLock(filePath, payload, owner, signal) {
|
|
4639
4718
|
for (;;) try {
|
|
4640
4719
|
await publishLock(filePath, payload, signal);
|
|
4641
4720
|
return true;
|
|
4642
4721
|
} catch (error) {
|
|
4643
4722
|
if (error.code !== "EEXIST") throw error;
|
|
4644
|
-
if (!await recoverAbandonedLock(filePath)) return false;
|
|
4723
|
+
if (!await recoverAbandonedLock(filePath, owner, signal)) return false;
|
|
4645
4724
|
}
|
|
4646
4725
|
}
|
|
4647
|
-
async function
|
|
4648
|
-
const
|
|
4649
|
-
if (
|
|
4726
|
+
async function tryAcquireTurnCleanup(filePath, payload, owner, signal) {
|
|
4727
|
+
const previous = localTurns.get(filePath);
|
|
4728
|
+
if (previous) {
|
|
4729
|
+
if (previous.failed) await settleTurn(filePath, previous);
|
|
4730
|
+
return;
|
|
4731
|
+
}
|
|
4732
|
+
const receipt = {
|
|
4733
|
+
owner,
|
|
4734
|
+
removeMarker: async () => await removePublishedLock(filePath, payload),
|
|
4735
|
+
admitted: false,
|
|
4736
|
+
failed: false,
|
|
4737
|
+
settled: false
|
|
4738
|
+
};
|
|
4739
|
+
localTurns.set(filePath, receipt);
|
|
4740
|
+
try {
|
|
4741
|
+
receipt.guard = await tryAcquireGuard(filePath, owner, signal);
|
|
4742
|
+
} catch (error) {
|
|
4743
|
+
localTurns.delete(filePath);
|
|
4744
|
+
throw error;
|
|
4745
|
+
}
|
|
4746
|
+
if (!receipt.guard) {
|
|
4747
|
+
localTurns.delete(filePath);
|
|
4748
|
+
return;
|
|
4749
|
+
}
|
|
4750
|
+
return receipt;
|
|
4751
|
+
}
|
|
4752
|
+
async function tryAcquireTurn(filePath, payload, owner, signal) {
|
|
4753
|
+
const receipt = await tryAcquireTurnCleanup(filePath, payload, owner, signal);
|
|
4754
|
+
if (!receipt) return;
|
|
4650
4755
|
try {
|
|
4651
4756
|
signal?.throwIfAborted();
|
|
4652
|
-
if (!await tryPublishLock(filePath, payload, signal)) {
|
|
4653
|
-
await
|
|
4757
|
+
if (!await tryPublishLock(filePath, payload, owner, signal)) {
|
|
4758
|
+
await disposeTurn(filePath, receipt);
|
|
4654
4759
|
return;
|
|
4655
4760
|
}
|
|
4656
4761
|
const observed = await readLock(filePath);
|
|
4657
4762
|
if (observed?.payload !== payload) throw new Error(`Session turn ownership changed before admission: ${filePath}`);
|
|
4658
4763
|
signal?.throwIfAborted();
|
|
4659
|
-
|
|
4764
|
+
receipt.admitted = true;
|
|
4765
|
+
receipt.removeMarker = async () => await removeObservedLock(filePath, observed);
|
|
4766
|
+
return { [Symbol.asyncDispose]: async () => await disposeTurn(filePath, receipt) };
|
|
4660
4767
|
} catch (error) {
|
|
4661
|
-
return await rejectAcquisition(filePath,
|
|
4768
|
+
return await rejectAcquisition(filePath, receipt, error);
|
|
4662
4769
|
}
|
|
4663
4770
|
}
|
|
4664
4771
|
async function acquireSessionTurn(recordId, signal) {
|
|
@@ -4668,19 +4775,44 @@ async function acquireSessionTurn(recordId, signal) {
|
|
|
4668
4775
|
mode: 448
|
|
4669
4776
|
});
|
|
4670
4777
|
const filePath = path.join(await fs$1.realpath(path.dirname(requestedPath)), path.basename(requestedPath));
|
|
4778
|
+
const owner = await createLockOwner({ signal });
|
|
4671
4779
|
lastCreatedAt = Math.max(Date.now(), lastCreatedAt + 1);
|
|
4672
4780
|
const payload = `${JSON.stringify({
|
|
4673
|
-
|
|
4781
|
+
...owner.payload,
|
|
4674
4782
|
created_at: new Date(lastCreatedAt).toISOString()
|
|
4675
4783
|
}, null, 2)}\n`;
|
|
4676
4784
|
for (;;) {
|
|
4677
4785
|
signal?.throwIfAborted();
|
|
4678
|
-
const turn = await tryAcquireTurn(filePath, payload, signal);
|
|
4786
|
+
const turn = await tryAcquireTurn(filePath, payload, owner, signal);
|
|
4679
4787
|
if (turn) return turn;
|
|
4680
4788
|
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
4681
4789
|
}
|
|
4682
4790
|
}
|
|
4683
4791
|
//#endregion
|
|
4792
|
+
//#region src/session/execution/direct-lifetime.ts
|
|
4793
|
+
function ownDirectClient(client, signal) {
|
|
4794
|
+
let closing;
|
|
4795
|
+
const cancel = async () => {
|
|
4796
|
+
await withTimeout(client.cancelActivePrompt(2500), 2500).catch(() => {});
|
|
4797
|
+
await client.close();
|
|
4798
|
+
};
|
|
4799
|
+
const onAbort = () => {
|
|
4800
|
+
closing ??= cancel();
|
|
4801
|
+
closing.catch(() => {});
|
|
4802
|
+
};
|
|
4803
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4804
|
+
if (signal?.aborted) onAbort();
|
|
4805
|
+
const release = () => signal?.removeEventListener("abort", onAbort);
|
|
4806
|
+
return Object.assign(() => {
|
|
4807
|
+
release();
|
|
4808
|
+
closing ??= client.close();
|
|
4809
|
+
return closing;
|
|
4810
|
+
}, { release });
|
|
4811
|
+
}
|
|
4812
|
+
function directExecutionError(error, signal) {
|
|
4813
|
+
return signal?.aborted && error instanceof AgentDisconnectedError ? signal.reason : error;
|
|
4814
|
+
}
|
|
4815
|
+
//#endregion
|
|
4684
4816
|
//#region src/session/queue/ipc-server.ts
|
|
4685
4817
|
const QUEUE_SOCKET_DRAIN_TIMEOUT_MS = 1e3;
|
|
4686
4818
|
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
@@ -4713,9 +4845,12 @@ function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options =
|
|
|
4713
4845
|
acp: normalized.acp
|
|
4714
4846
|
};
|
|
4715
4847
|
}
|
|
4848
|
+
function armSocketDrainTimeout(socket) {
|
|
4849
|
+
if (process.platform !== "win32") socket.setTimeout(QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
|
|
4850
|
+
}
|
|
4716
4851
|
function writeQueueMessage(socket, message) {
|
|
4717
4852
|
if (socket.destroyed || !socket.writable) return;
|
|
4718
|
-
socket.write(`${JSON.stringify(message)}\n`);
|
|
4853
|
+
if (!socket.write(`${JSON.stringify(message)}\n`) && !socket.timeout) armSocketDrainTimeout(socket);
|
|
4719
4854
|
}
|
|
4720
4855
|
var SessionQueueOwner = class SessionQueueOwner {
|
|
4721
4856
|
server;
|
|
@@ -4883,6 +5018,7 @@ var SessionQueueOwner = class SessionQueueOwner {
|
|
|
4883
5018
|
if (socket.destroyed || this.drainingSockets.has(socket)) return;
|
|
4884
5019
|
this.taskSockets.delete(socket);
|
|
4885
5020
|
this.drainingSockets.add(socket);
|
|
5021
|
+
armSocketDrainTimeout(socket);
|
|
4886
5022
|
socket.end(() => {
|
|
4887
5023
|
this.drainingSockets.delete(socket);
|
|
4888
5024
|
socket.destroy();
|
|
@@ -5032,6 +5168,10 @@ var SessionQueueOwner = class SessionQueueOwner {
|
|
|
5032
5168
|
handleConnection(socket) {
|
|
5033
5169
|
this.sockets.add(socket);
|
|
5034
5170
|
socket.setEncoding("utf8");
|
|
5171
|
+
socket.on("timeout", () => socket.destroy());
|
|
5172
|
+
socket.on("drain", () => {
|
|
5173
|
+
if (!socket.writableEnded) socket.setTimeout(0);
|
|
5174
|
+
});
|
|
5035
5175
|
socket.once("close", () => {
|
|
5036
5176
|
this.sockets.delete(socket);
|
|
5037
5177
|
this.taskSockets.delete(socket);
|
|
@@ -5120,6 +5260,10 @@ async function replayDesiredMode(params) {
|
|
|
5120
5260
|
await withTimeout(params.client.setSessionMode(params.sessionId, params.desiredModeId, params.replay.authority), params.timeoutMs);
|
|
5121
5261
|
params.replay.acknowledged = true;
|
|
5122
5262
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired mode ${params.desiredModeId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5263
|
+
return {
|
|
5264
|
+
models: advertisedModelState(params.record.acpx),
|
|
5265
|
+
configOptionsPresent: params.record.acpx?.config_options !== void 0
|
|
5266
|
+
};
|
|
5123
5267
|
} catch (error) {
|
|
5124
5268
|
rethrowAuthorityFailure(params.replay, error);
|
|
5125
5269
|
throw new SessionModeReplayError(`Failed to replay saved session mode ${params.desiredModeId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
@@ -5198,18 +5342,20 @@ function restoreOriginalSessionState(params) {
|
|
|
5198
5342
|
async function connectAndLoadSession(options) {
|
|
5199
5343
|
const record = options.record;
|
|
5200
5344
|
const client = options.client;
|
|
5345
|
+
assertControlAuthority(options.authority);
|
|
5201
5346
|
const sameSessionOnly = requiresSameSession(options.resumePolicy) || Boolean(record.importedFrom);
|
|
5202
5347
|
const originalSessionId = record.acpSessionId;
|
|
5203
5348
|
const originalAgentSessionId = record.agentSessionId;
|
|
5204
5349
|
const originalAcpx = cloneSessionAcpxState(record.acpx);
|
|
5205
|
-
const desiredModeId = getDesiredModeId(record.acpx);
|
|
5350
|
+
const desiredModeId = options.replacingMode ? void 0 : getDesiredModeId(record.acpx);
|
|
5206
5351
|
const desiredModelId = getDesiredModelId(record.acpx);
|
|
5207
5352
|
const desiredConfigOptions = getDesiredConfigOptions(record.acpx);
|
|
5208
5353
|
const storedProcessAlive = isProcessAlive(record.pid);
|
|
5209
5354
|
logReconnectAttempt(record, storedProcessAlive, Boolean(record.pid) && !storedProcessAlive, options.verbose);
|
|
5210
5355
|
const reusingLoadedSession = client.hasReusableSession(record.acpSessionId);
|
|
5211
5356
|
if (reusingLoadedSession) incrementPerfCounter("runtime.connect_and_load.reused_session");
|
|
5212
|
-
else await withTimeout(client.start(), options.timeoutMs);
|
|
5357
|
+
else await withTimeout(client.start(options.authority), options.timeoutMs);
|
|
5358
|
+
assertControlAuthority(options.authority);
|
|
5213
5359
|
options.onClientAvailable?.(options.activeController);
|
|
5214
5360
|
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
5215
5361
|
record.closed = false;
|
|
@@ -5220,7 +5366,8 @@ async function connectAndLoadSession(options) {
|
|
|
5220
5366
|
record,
|
|
5221
5367
|
reusingLoadedSession,
|
|
5222
5368
|
sameSessionOnly,
|
|
5223
|
-
timeoutMs: options.timeoutMs
|
|
5369
|
+
timeoutMs: options.timeoutMs,
|
|
5370
|
+
authority: options.authority
|
|
5224
5371
|
});
|
|
5225
5372
|
const { resumed, loadError, sessionId, createdFreshSession, pendingAgentSessionId, sessionModels } = loadState;
|
|
5226
5373
|
applyReconnectedModelState(record, sessionModels, loadState.configOptionsPresent, loadState.legacyModelMetadataPresent, createdFreshSession);
|
|
@@ -5254,14 +5401,18 @@ async function connectAndLoadSession(options) {
|
|
|
5254
5401
|
};
|
|
5255
5402
|
}
|
|
5256
5403
|
function resolveModelsAfterReplay(replay, initialModels) {
|
|
5257
|
-
|
|
5258
|
-
return replay.modelReplay.replayed ? replay.modelReplay.models :
|
|
5404
|
+
const models = resolveModeReplayModels(replay.modeMetadata, initialModels);
|
|
5405
|
+
if (replay.configReplay.replayed) return replay.configReplay.models ?? preserveLegacyModels(replay.modelReplay.replayed ? replay.modelReplay.models : models);
|
|
5406
|
+
return replay.modelReplay.replayed ? replay.modelReplay.models : models;
|
|
5407
|
+
}
|
|
5408
|
+
function resolveModeReplayModels(modeMetadata, initialModels) {
|
|
5409
|
+
return modeMetadata ? modeMetadata.models : initialModels;
|
|
5259
5410
|
}
|
|
5260
5411
|
function preserveLegacyModels(models) {
|
|
5261
5412
|
return models && !models.configId ? models : void 0;
|
|
5262
5413
|
}
|
|
5263
5414
|
function resolveConfigOptionsPresenceAfterReplay(replay, initiallyPresent) {
|
|
5264
|
-
return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.configOptions !== void 0;
|
|
5415
|
+
return initiallyPresent || replay.modeMetadata?.configOptionsPresent === true || replay.configReplay.replayed || replay.modelReplay.configOptions !== void 0;
|
|
5265
5416
|
}
|
|
5266
5417
|
function applyReconnectedModelState(record, sessionModels, configOptionsPresent, legacyModelMetadataPresent, createdFreshSession) {
|
|
5267
5418
|
clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent);
|
|
@@ -5302,12 +5453,13 @@ async function replaySessionPreferences(params) {
|
|
|
5302
5453
|
};
|
|
5303
5454
|
let modelReplay = { replayed: false };
|
|
5304
5455
|
let configReplay = { replayed: false };
|
|
5456
|
+
let modeMetadata;
|
|
5305
5457
|
const replay = createReplayState(params.authority);
|
|
5306
|
-
const replacingModel = replacesModel(params.replacingConfigOption, params.sessionModels, params.originalAcpx);
|
|
5307
5458
|
try {
|
|
5308
5459
|
assertControlAuthority(replay.authority);
|
|
5309
|
-
await replayDesiredMode({
|
|
5460
|
+
modeMetadata = await replayDesiredMode({
|
|
5310
5461
|
client: params.client,
|
|
5462
|
+
record: params.record,
|
|
5311
5463
|
sessionId: params.sessionId,
|
|
5312
5464
|
desiredModeId: params.createdFreshSession ? params.desiredModeId : void 0,
|
|
5313
5465
|
replay,
|
|
@@ -5315,6 +5467,8 @@ async function replaySessionPreferences(params) {
|
|
|
5315
5467
|
timeoutMs: params.timeoutMs,
|
|
5316
5468
|
verbose: params.verbose
|
|
5317
5469
|
});
|
|
5470
|
+
const effectiveModels = resolveModeReplayModels(modeMetadata, params.sessionModels);
|
|
5471
|
+
const replacingModel = replacesModel(params.replacingConfigOption, effectiveModels, params.originalAcpx);
|
|
5318
5472
|
modelReplay = await replayDesiredModel({
|
|
5319
5473
|
client: params.client,
|
|
5320
5474
|
sessionId: params.sessionId,
|
|
@@ -5322,7 +5476,7 @@ async function replaySessionPreferences(params) {
|
|
|
5322
5476
|
replay,
|
|
5323
5477
|
previousSessionId: params.originalSessionId,
|
|
5324
5478
|
record: params.record,
|
|
5325
|
-
models:
|
|
5479
|
+
models: effectiveModels,
|
|
5326
5480
|
createdFreshSession: params.createdFreshSession,
|
|
5327
5481
|
timeoutMs: params.timeoutMs,
|
|
5328
5482
|
verbose: params.verbose,
|
|
@@ -5349,7 +5503,8 @@ async function replaySessionPreferences(params) {
|
|
|
5349
5503
|
reconcileAgentSessionId(params.record, params.pendingAgentSessionId);
|
|
5350
5504
|
return {
|
|
5351
5505
|
modelReplay,
|
|
5352
|
-
configReplay
|
|
5506
|
+
configReplay,
|
|
5507
|
+
modeMetadata
|
|
5353
5508
|
};
|
|
5354
5509
|
}
|
|
5355
5510
|
function settleFailedReplay(params, replay, error) {
|
|
@@ -5381,11 +5536,14 @@ async function loadOrCreateRuntimeSession(params) {
|
|
|
5381
5536
|
record: params.record,
|
|
5382
5537
|
reason: "agent does not support session/resume or session/load"
|
|
5383
5538
|
});
|
|
5384
|
-
return await createFreshRuntimeSession(params.client, params.record, params.timeoutMs);
|
|
5539
|
+
return await createFreshRuntimeSession(params.client, params.record, params.timeoutMs, params.authority);
|
|
5385
5540
|
}
|
|
5386
5541
|
async function loadRuntimeSession(params, resume) {
|
|
5387
5542
|
try {
|
|
5388
|
-
const loadResult = await withTimeout(resume ? params.client.resumeSession(params.record.acpSessionId, params.record.cwd) : params.client.loadSessionWithOptions(params.record.acpSessionId, params.record.cwd, {
|
|
5543
|
+
const loadResult = await withTimeout(resume ? params.client.resumeSession(params.record.acpSessionId, params.record.cwd, params.authority) : params.client.loadSessionWithOptions(params.record.acpSessionId, params.record.cwd, {
|
|
5544
|
+
suppressReplayUpdates: true,
|
|
5545
|
+
...params.authority ? { authority: params.authority } : {}
|
|
5546
|
+
}), params.timeoutMs);
|
|
5389
5547
|
reconcileAgentSessionId(params.record, loadResult.agentSessionId);
|
|
5390
5548
|
applyConfigOptionsToRecord(params.record, loadResult);
|
|
5391
5549
|
return {
|
|
@@ -5398,9 +5556,14 @@ async function loadRuntimeSession(params, resume) {
|
|
|
5398
5556
|
createdFreshSession: false
|
|
5399
5557
|
};
|
|
5400
5558
|
} catch (error) {
|
|
5559
|
+
rethrowCancelledLoad(error, params.authority);
|
|
5401
5560
|
return await recoverRuntimeSessionLoadFailure(params, error);
|
|
5402
5561
|
}
|
|
5403
5562
|
}
|
|
5563
|
+
function rethrowCancelledLoad(error, authority) {
|
|
5564
|
+
const signal = authority?.signal;
|
|
5565
|
+
if (signal?.aborted && (error instanceof AgentDisconnectedError || error === signal.reason)) throw signal.reason;
|
|
5566
|
+
}
|
|
5404
5567
|
async function recoverRuntimeSessionLoadFailure(params, error) {
|
|
5405
5568
|
const loadError = formatErrorMessage(error);
|
|
5406
5569
|
if (params.sameSessionOnly) throw makeSessionResumeRequiredError({
|
|
@@ -5410,12 +5573,12 @@ async function recoverRuntimeSessionLoadFailure(params, error) {
|
|
|
5410
5573
|
});
|
|
5411
5574
|
if (!shouldFallbackToNewSession(error, params.record)) throw error;
|
|
5412
5575
|
return {
|
|
5413
|
-
...await createFreshRuntimeSession(params.client, params.record, params.timeoutMs),
|
|
5576
|
+
...await createFreshRuntimeSession(params.client, params.record, params.timeoutMs, params.authority),
|
|
5414
5577
|
loadError
|
|
5415
5578
|
};
|
|
5416
5579
|
}
|
|
5417
|
-
async function createFreshRuntimeSession(client, record, timeoutMs) {
|
|
5418
|
-
const createdSession = await withTimeout(client.createSession(record.cwd), timeoutMs);
|
|
5580
|
+
async function createFreshRuntimeSession(client, record, timeoutMs, authority) {
|
|
5581
|
+
const createdSession = await withTimeout(client.createSession(record.cwd, authority), timeoutMs);
|
|
5419
5582
|
applyConfigOptionsToRecord(record, createdSession);
|
|
5420
5583
|
return {
|
|
5421
5584
|
sessionId: createdSession.sessionId,
|
|
@@ -5482,6 +5645,7 @@ async function withConnectedSession(options) {
|
|
|
5482
5645
|
client,
|
|
5483
5646
|
record,
|
|
5484
5647
|
resumePolicy: options.resumePolicy,
|
|
5648
|
+
replacingMode: options.replacingMode,
|
|
5485
5649
|
replacingConfigOption: options.replacingConfigOption,
|
|
5486
5650
|
authority: options.authority,
|
|
5487
5651
|
timeoutMs: options.timeoutMs,
|
|
@@ -5542,6 +5706,7 @@ async function withConnectedSession(options) {
|
|
|
5542
5706
|
function buildDirectConnectedSessionOptions(options, run) {
|
|
5543
5707
|
return {
|
|
5544
5708
|
sessionRecordId: options.sessionRecordId,
|
|
5709
|
+
replacingMode: options.replacingMode,
|
|
5545
5710
|
replacingConfigOption: options.replacingConfigOption,
|
|
5546
5711
|
loadRecord: resolveSessionRecord,
|
|
5547
5712
|
saveRecord: writeSessionRecord,
|
|
@@ -5592,7 +5757,10 @@ async function withOwnedDirectSession(options) {
|
|
|
5592
5757
|
}
|
|
5593
5758
|
}
|
|
5594
5759
|
async function runSessionSetModeDirect(options) {
|
|
5595
|
-
return toSessionMutationResult(await withOwnedDirectSession(buildDirectConnectedSessionOptions(
|
|
5760
|
+
return toSessionMutationResult(await withOwnedDirectSession(buildDirectConnectedSessionOptions({
|
|
5761
|
+
...options,
|
|
5762
|
+
replacingMode: true
|
|
5763
|
+
}, async ({ client, sessionId, record }) => {
|
|
5596
5764
|
await withTimeout(client.setSessionMode(sessionId, options.modeId), options.timeoutMs);
|
|
5597
5765
|
setDesiredModeId(record, options.modeId);
|
|
5598
5766
|
})));
|
|
@@ -5806,8 +5974,14 @@ const sessionControlTestInternals = {
|
|
|
5806
5974
|
};
|
|
5807
5975
|
async function closeSession(sessionId) {
|
|
5808
5976
|
const record = await resolveSessionRecord(sessionId);
|
|
5809
|
-
|
|
5810
|
-
await
|
|
5977
|
+
let selectedOwner;
|
|
5978
|
+
await tryCloseSessionOnRunningOwner({
|
|
5979
|
+
sessionId: record.acpxRecordId,
|
|
5980
|
+
onOwnerSelected: (owner) => {
|
|
5981
|
+
selectedOwner = owner;
|
|
5982
|
+
}
|
|
5983
|
+
}).catch(() => {});
|
|
5984
|
+
if (selectedOwner) await terminateQueueOwnerForSession(record.acpxRecordId, selectedOwner);
|
|
5811
5985
|
if (record.pid != null && isProcessAlive(record.pid) && await isLikelyMatchingProcess(record.pid, record.agentCommand)) await terminateProcess(record.pid);
|
|
5812
5986
|
const ownership = await acquireSessionTurn(record.acpxRecordId);
|
|
5813
5987
|
try {
|
|
@@ -5825,9 +5999,11 @@ async function closeSession(sessionId) {
|
|
|
5825
5999
|
//#region src/session/execution/session-management.ts
|
|
5826
6000
|
async function createSessionRecordWithClient(client, options) {
|
|
5827
6001
|
const cwd = absolutePath(options.cwd);
|
|
5828
|
-
await withTimeout(client.start(), options.timeoutMs);
|
|
6002
|
+
await withTimeout(client.start({ signal: options.signal }), options.timeoutMs);
|
|
6003
|
+
assertControlAuthority({ signal: options.signal });
|
|
5829
6004
|
const createdState = options.resumeSessionId ? await resumeSessionRecordWithClient(client, options, cwd) : await createFreshSessionState(client, options, cwd);
|
|
5830
6005
|
const { sessionId, agentSessionId } = createdState;
|
|
6006
|
+
assertControlAuthority({ signal: options.signal });
|
|
5831
6007
|
const lifecycle = client.getAgentLifecycleSnapshot();
|
|
5832
6008
|
const record = {
|
|
5833
6009
|
...createInitialSessionRecord({
|
|
@@ -5849,10 +6025,12 @@ async function createSessionRecordWithClient(client, options) {
|
|
|
5849
6025
|
applyConfigOptionsToRecord(record, createdState.sessionResult);
|
|
5850
6026
|
applyInitialModelSelection(record, createdState.sessionResult.models, options.sessionOptions?.model, createdState.modelApplication);
|
|
5851
6027
|
await writeSessionRecord(record);
|
|
6028
|
+
assertControlAuthority({ signal: options.signal });
|
|
5852
6029
|
return record;
|
|
5853
6030
|
}
|
|
5854
6031
|
async function createFreshSessionState(client, options, cwd) {
|
|
5855
|
-
const createdSession = await withTimeout(client.createSession(cwd), options.timeoutMs);
|
|
6032
|
+
const createdSession = await withTimeout(client.createSession(cwd, { signal: options.signal }), options.timeoutMs);
|
|
6033
|
+
assertControlAuthority({ signal: options.signal });
|
|
5856
6034
|
const modelApplication = await applyRequestedModelIfAdvertised({
|
|
5857
6035
|
client,
|
|
5858
6036
|
sessionId: createdSession.sessionId,
|
|
@@ -5860,7 +6038,8 @@ async function createFreshSessionState(client, options, cwd) {
|
|
|
5860
6038
|
models: createdSession.models,
|
|
5861
6039
|
agentCommand: options.agentCommand,
|
|
5862
6040
|
timeoutMs: options.timeoutMs,
|
|
5863
|
-
onWarning: options.onModelWarning
|
|
6041
|
+
onWarning: options.onModelWarning,
|
|
6042
|
+
authority: { signal: options.signal }
|
|
5864
6043
|
});
|
|
5865
6044
|
return {
|
|
5866
6045
|
sessionId: createdSession.sessionId,
|
|
@@ -5874,8 +6053,9 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
5874
6053
|
const resumeMethod = client.supportsResumeSession() ? "session/resume" : client.supportsLoadSession() ? "session/load" : void 0;
|
|
5875
6054
|
if (!resumeMethod) throw new Error(`Agent command "${options.agentCommand}" does not support session/resume or session/load; cannot resume session ${options.resumeSessionId}`);
|
|
5876
6055
|
try {
|
|
5877
|
-
const resumedSession = await withTimeout(resumeMethod === "session/resume" ? client.resumeSession(options.resumeSessionId, cwd) : client.
|
|
6056
|
+
const resumedSession = await withTimeout(resumeMethod === "session/resume" ? client.resumeSession(options.resumeSessionId, cwd, { signal: options.signal }) : client.loadSessionWithOptions(options.resumeSessionId, cwd, { authority: { signal: options.signal } }), options.timeoutMs);
|
|
5878
6057
|
const sessionModels = resumedSession.models;
|
|
6058
|
+
assertControlAuthority({ signal: options.signal });
|
|
5879
6059
|
const modelApplication = await applyRequestedModelIfAdvertised({
|
|
5880
6060
|
client,
|
|
5881
6061
|
sessionId: options.resumeSessionId,
|
|
@@ -5883,7 +6063,8 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
5883
6063
|
models: sessionModels,
|
|
5884
6064
|
agentCommand: options.agentCommand,
|
|
5885
6065
|
timeoutMs: options.timeoutMs,
|
|
5886
|
-
onWarning: options.onModelWarning
|
|
6066
|
+
onWarning: options.onModelWarning,
|
|
6067
|
+
authority: { signal: options.signal }
|
|
5887
6068
|
});
|
|
5888
6069
|
return {
|
|
5889
6070
|
sessionId: options.resumeSessionId,
|
|
@@ -5896,7 +6077,7 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
5896
6077
|
}
|
|
5897
6078
|
}
|
|
5898
6079
|
async function createSessionWithClient(options) {
|
|
5899
|
-
options.signal
|
|
6080
|
+
assertControlAuthority({ signal: options.signal });
|
|
5900
6081
|
const client = new AcpClient({
|
|
5901
6082
|
agentCommand: options.agentCommand,
|
|
5902
6083
|
agentArgv: options.agentArgv,
|
|
@@ -5912,28 +6093,25 @@ async function createSessionWithClient(options) {
|
|
|
5912
6093
|
verbose: options.verbose,
|
|
5913
6094
|
sessionOptions: options.sessionOptions
|
|
5914
6095
|
});
|
|
5915
|
-
const
|
|
5916
|
-
client.close().catch(() => {});
|
|
5917
|
-
};
|
|
5918
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
6096
|
+
const closeOwnedClient = ownDirectClient(client, options.signal);
|
|
5919
6097
|
try {
|
|
5920
6098
|
const create = async () => {
|
|
5921
|
-
options.signal
|
|
6099
|
+
assertControlAuthority({ signal: options.signal });
|
|
5922
6100
|
const record = await createSessionRecordWithClient(client, options);
|
|
5923
|
-
options.signal
|
|
6101
|
+
assertControlAuthority({ signal: options.signal });
|
|
5924
6102
|
return record;
|
|
5925
6103
|
};
|
|
5926
6104
|
return {
|
|
5927
6105
|
record: options.handleProcessInterrupts === false ? await create() : await withInterrupt(create, async () => {
|
|
5928
|
-
await
|
|
6106
|
+
await closeOwnedClient();
|
|
5929
6107
|
}),
|
|
5930
6108
|
client
|
|
5931
6109
|
};
|
|
5932
6110
|
} catch (error) {
|
|
5933
|
-
await
|
|
5934
|
-
throw error;
|
|
6111
|
+
await closeOwnedClient();
|
|
6112
|
+
throw directExecutionError(error, options.signal);
|
|
5935
6113
|
} finally {
|
|
5936
|
-
|
|
6114
|
+
closeOwnedClient.release();
|
|
5937
6115
|
}
|
|
5938
6116
|
}
|
|
5939
6117
|
async function createSession(options) {
|
|
@@ -5999,7 +6177,7 @@ async function ensureSession(options) {
|
|
|
5999
6177
|
}
|
|
6000
6178
|
}
|
|
6001
6179
|
async function ensureSessionWithOwnership(options, cwd) {
|
|
6002
|
-
options.signal
|
|
6180
|
+
assertControlAuthority({ signal: options.signal });
|
|
6003
6181
|
const gitRoot = findGitRepositoryRoot(cwd);
|
|
6004
6182
|
const walkBoundary = options.walkBoundary ?? gitRoot ?? cwd;
|
|
6005
6183
|
const existing = await findSessionByDirectoryWalk({
|
|
@@ -6509,27 +6687,27 @@ const NODE_TEST_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
|
6509
6687
|
"--test-reporter",
|
|
6510
6688
|
"--test-reporter-destination"
|
|
6511
6689
|
]);
|
|
6512
|
-
const
|
|
6690
|
+
const INSPECTOR_FLAGS = /* @__PURE__ */ new Set([
|
|
6513
6691
|
"--inspect",
|
|
6514
6692
|
"--inspect-brk",
|
|
6693
|
+
"--inspect-wait"
|
|
6694
|
+
]);
|
|
6695
|
+
const INSPECTOR_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
6515
6696
|
"--inspect-port",
|
|
6516
6697
|
"--inspect-publish-uid",
|
|
6517
6698
|
"--debug-port"
|
|
6518
6699
|
]);
|
|
6519
|
-
const INSPECTOR_FLAG_PREFIXES = [
|
|
6520
|
-
"--inspect=",
|
|
6521
|
-
"--inspect-brk=",
|
|
6522
|
-
"--inspect-port=",
|
|
6523
|
-
"--inspect-publish-uid=",
|
|
6524
|
-
"--debug-port="
|
|
6525
|
-
];
|
|
6526
6700
|
function classifyExecArgv(value) {
|
|
6527
|
-
if (value
|
|
6528
|
-
if (
|
|
6529
|
-
return
|
|
6701
|
+
if (NODE_TEST_FLAGS_WITH_VALUE.has(value)) return "drop-with-value";
|
|
6702
|
+
if (NODE_TEST_FLAGS.has(value) || value.startsWith("--test-")) return "drop";
|
|
6703
|
+
return classifyInspectorArgv(value);
|
|
6530
6704
|
}
|
|
6531
|
-
function
|
|
6532
|
-
|
|
6705
|
+
function classifyInspectorArgv(value) {
|
|
6706
|
+
const [name] = value.split("=", 1);
|
|
6707
|
+
if (!name?.startsWith("--")) return "keep";
|
|
6708
|
+
const flag = name.replaceAll("_", "-");
|
|
6709
|
+
if (INSPECTOR_FLAGS_WITH_VALUE.has(flag)) return value.includes("=") ? "drop" : "drop-with-value";
|
|
6710
|
+
return INSPECTOR_FLAGS.has(flag) ? "drop" : "keep";
|
|
6533
6711
|
}
|
|
6534
6712
|
function sanitizeQueueOwnerExecArgv(execArgv = process.execArgv) {
|
|
6535
6713
|
const sanitized = [];
|
|
@@ -6795,10 +6973,11 @@ var QueueTaskOutputFormatter = class {
|
|
|
6795
6973
|
this.send = task.send;
|
|
6796
6974
|
}
|
|
6797
6975
|
setContext(_context) {}
|
|
6798
|
-
onAcpMessage(message) {
|
|
6976
|
+
onAcpMessage(message, direction) {
|
|
6799
6977
|
this.send({
|
|
6800
6978
|
type: "event",
|
|
6801
6979
|
requestId: this.requestId,
|
|
6980
|
+
...direction === void 0 ? {} : { direction },
|
|
6802
6981
|
message
|
|
6803
6982
|
});
|
|
6804
6983
|
}
|
|
@@ -6855,7 +7034,7 @@ var AcpErrorTracker = class {
|
|
|
6855
7034
|
this.outbound.length = 0;
|
|
6856
7035
|
}
|
|
6857
7036
|
observe(output, direction, message) {
|
|
6858
|
-
output.onAcpMessage(message);
|
|
7037
|
+
output.onAcpMessage(message, direction);
|
|
6859
7038
|
const acp = extractAcpError(message);
|
|
6860
7039
|
if (!acp) return;
|
|
6861
7040
|
if (direction === "inbound") {
|
|
@@ -6889,6 +7068,7 @@ async function applyPromptModelIfAdvertised(params) {
|
|
|
6889
7068
|
models: advertisedModelState(params.record.acpx),
|
|
6890
7069
|
agentCommand: params.record.agentCommand,
|
|
6891
7070
|
timeoutMs: params.timeoutMs,
|
|
7071
|
+
authority: { signal: params.signal },
|
|
6892
7072
|
onWarning: params.suppressWarnings ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
|
|
6893
7073
|
});
|
|
6894
7074
|
if (result.applied) params.record.acpx = applyModelSelection(params.record.acpx, requestedModel, result.response);
|
|
@@ -7073,7 +7253,7 @@ async function waitForSessionTurn(options) {
|
|
|
7073
7253
|
"SIGTERM",
|
|
7074
7254
|
"SIGHUP"
|
|
7075
7255
|
];
|
|
7076
|
-
for (const signal of signals) process.once(signal, onInterrupt);
|
|
7256
|
+
if (options.handleProcessInterrupts !== false) for (const signal of signals) process.once(signal, onInterrupt);
|
|
7077
7257
|
const timeoutMs = options.timeoutMs;
|
|
7078
7258
|
const timeout = timeoutMs != null && timeoutMs > 0 ? setTimeout(() => waiting.abort(new TimeoutError(timeoutMs)), timeoutMs) : void 0;
|
|
7079
7259
|
const signal = options.waitSignal ? AbortSignal.any([waiting.signal, options.waitSignal]) : waiting.signal;
|
|
@@ -7084,18 +7264,22 @@ async function waitForSessionTurn(options) {
|
|
|
7084
7264
|
ownership: await acquireSessionTurn(acpxRecordId, signal)
|
|
7085
7265
|
};
|
|
7086
7266
|
} catch (error) {
|
|
7087
|
-
signal
|
|
7088
|
-
throw error;
|
|
7267
|
+
return throwTurnAcquisitionError(error, signal);
|
|
7089
7268
|
} finally {
|
|
7090
7269
|
clearTimeout(timeout);
|
|
7091
7270
|
for (const signal of signals) process.off(signal, onInterrupt);
|
|
7092
7271
|
}
|
|
7093
7272
|
}
|
|
7273
|
+
function throwTurnAcquisitionError(error, signal) {
|
|
7274
|
+
if (signal.aborted && error instanceof Error && error.name === "AbortError" && error.cause === signal.reason) throw signal.reason;
|
|
7275
|
+
throw error;
|
|
7276
|
+
}
|
|
7094
7277
|
async function runSessionPrompt(options) {
|
|
7095
7278
|
let turn;
|
|
7096
7279
|
try {
|
|
7097
7280
|
turn = await waitForSessionTurn(options);
|
|
7098
7281
|
} catch (error) {
|
|
7282
|
+
if (options.ownedSignal) throw error;
|
|
7099
7283
|
if (options.waitSignal?.aborted && error === options.waitSignal.reason) {
|
|
7100
7284
|
const record = await resolveSessionRecord(options.sessionRecordId);
|
|
7101
7285
|
return {
|
|
@@ -7215,8 +7399,10 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7215
7399
|
const record = await measurePerf("session.resolve_prompt_record", async () => {
|
|
7216
7400
|
return await resolveSessionRecord(options.sessionRecordId);
|
|
7217
7401
|
});
|
|
7402
|
+
options.ownedSignal?.throwIfAborted();
|
|
7218
7403
|
const { conversation, promptMessageId } = preparePromptConversation(record, options);
|
|
7219
7404
|
await writeSessionRecord(record);
|
|
7405
|
+
options.ownedSignal?.throwIfAborted();
|
|
7220
7406
|
output.setContext({ sessionId: record.acpxRecordId });
|
|
7221
7407
|
const eventWriter = await measurePerf("session.events.open", async () => {
|
|
7222
7408
|
return await SessionEventWriter.open(record);
|
|
@@ -7259,7 +7445,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7259
7445
|
if (options.verbose) process.stderr.write("[acpx] live session checkpoint failed: " + formatErrorMessage(error) + "\n");
|
|
7260
7446
|
}
|
|
7261
7447
|
});
|
|
7262
|
-
let closeClientOnExit = options.client == null;
|
|
7448
|
+
let closeClientOnExit = options.client == null || options.ownedSignal !== void 0;
|
|
7263
7449
|
const client = options.client ?? new AcpClient({
|
|
7264
7450
|
agentCommand: record.agentCommand,
|
|
7265
7451
|
agentArgv: record.agentArgv,
|
|
@@ -7285,6 +7471,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7285
7471
|
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
7286
7472
|
verbose: options.verbose
|
|
7287
7473
|
});
|
|
7474
|
+
const closeOwnedClient = options.closeProvidedClient ?? ownDirectClient(client, options.ownedSignal);
|
|
7288
7475
|
client.setEventHandlers({
|
|
7289
7476
|
onAcpMessage: (direction, message) => {
|
|
7290
7477
|
pendingMessages.push(message);
|
|
@@ -7358,6 +7545,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7358
7545
|
verbose: options.verbose,
|
|
7359
7546
|
suppressWarnings: options.suppressSdkConsoleErrors,
|
|
7360
7547
|
activeController,
|
|
7548
|
+
authority: { signal: options.ownedSignal },
|
|
7361
7549
|
onClientAvailable: (controller) => {
|
|
7362
7550
|
options.onClientAvailable?.(controller);
|
|
7363
7551
|
notifiedClientAvailable = true;
|
|
@@ -7464,15 +7652,19 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7464
7652
|
return response;
|
|
7465
7653
|
};
|
|
7466
7654
|
const runPrompt = async () => {
|
|
7655
|
+
options.ownedSignal?.throwIfAborted();
|
|
7467
7656
|
const { sessionId: activeSessionId, resumed, loadError } = await connectForPrompt();
|
|
7657
|
+
options.ownedSignal?.throwIfAborted();
|
|
7468
7658
|
await applyPromptModelIfAdvertised({
|
|
7469
7659
|
client,
|
|
7470
7660
|
sessionId: activeSessionId,
|
|
7471
7661
|
requestedModel: options.sessionOptions?.model,
|
|
7472
7662
|
record,
|
|
7473
7663
|
timeoutMs: options.timeoutMs,
|
|
7474
|
-
suppressWarnings: options.suppressSdkConsoleErrors
|
|
7664
|
+
suppressWarnings: options.suppressSdkConsoleErrors,
|
|
7665
|
+
signal: options.ownedSignal
|
|
7475
7666
|
});
|
|
7667
|
+
options.ownedSignal?.throwIfAborted();
|
|
7476
7668
|
output.setContext({ sessionId: record.acpxRecordId });
|
|
7477
7669
|
await liveCheckpoint.checkpoint();
|
|
7478
7670
|
const response = await savePromptSuccess(await runPromptWithRetries(activeSessionId));
|
|
@@ -7490,20 +7682,23 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7490
7682
|
record.lastUsedAt = isoNow$1();
|
|
7491
7683
|
applyConversation(record, conversation);
|
|
7492
7684
|
await flushPendingMessages(false).catch(() => {});
|
|
7493
|
-
if (closeClientOnExit) await
|
|
7685
|
+
if (closeClientOnExit) await closeOwnedClient();
|
|
7494
7686
|
};
|
|
7495
7687
|
const runObservedPrompt = async () => {
|
|
7496
7688
|
try {
|
|
7497
|
-
|
|
7689
|
+
const result = await runWithOptionalInterrupt({
|
|
7498
7690
|
handleProcessInterrupts: options.handleProcessInterrupts,
|
|
7499
7691
|
run: runPrompt,
|
|
7500
7692
|
handleInterrupt
|
|
7501
7693
|
});
|
|
7694
|
+
options.ownedSignal?.throwIfAborted();
|
|
7695
|
+
return result;
|
|
7502
7696
|
} catch (error) {
|
|
7503
|
-
const
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7697
|
+
const failure = directExecutionError(error, options.ownedSignal);
|
|
7698
|
+
const matchedAcpError = acpErrors.match(failure);
|
|
7699
|
+
attachAcpErrorPayload(failure, matchedAcpError);
|
|
7700
|
+
markOutputAlreadyEmitted(failure, matchedAcpError !== void 0 && shouldMarkAcpErrorsEmitted);
|
|
7701
|
+
throw failure;
|
|
7507
7702
|
}
|
|
7508
7703
|
};
|
|
7509
7704
|
const cleanupPrompt = async (attempt) => {
|
|
@@ -7516,7 +7711,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7516
7711
|
async () => {
|
|
7517
7712
|
const journalFailed = attempt.status === "rejected" && attempt.error instanceof AcpxOperationalError && attempt.error.detailCode === "SESSION_JOURNAL_WRITE_FAILED";
|
|
7518
7713
|
if (unresolvedPrompt || closeClientOnExit || journalFailed) try {
|
|
7519
|
-
await
|
|
7714
|
+
await closeOwnedClient();
|
|
7520
7715
|
} catch (error) {
|
|
7521
7716
|
options.onClientCloseFailure?.();
|
|
7522
7717
|
throw error;
|
|
@@ -7554,7 +7749,8 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7554
7749
|
cleanup: cleanupPrompt
|
|
7555
7750
|
});
|
|
7556
7751
|
}
|
|
7557
|
-
async function runOnce(options) {
|
|
7752
|
+
async function runOnce(options, control) {
|
|
7753
|
+
const authority = { signal: control?.signal };
|
|
7558
7754
|
const output = options.outputFormatter;
|
|
7559
7755
|
const shouldMarkAcpErrorsEmitted = rendersAcpErrors(options.errorEmissionPolicy);
|
|
7560
7756
|
let promptTurnActive = false;
|
|
@@ -7594,10 +7790,12 @@ async function runOnce(options) {
|
|
|
7594
7790
|
},
|
|
7595
7791
|
sessionOptions: options.sessionOptions
|
|
7596
7792
|
});
|
|
7793
|
+
const closeOwnedClient = ownDirectClient(client, control?.signal);
|
|
7597
7794
|
const runExecPromptAttempt = async (sessionId) => {
|
|
7795
|
+
assertControlAuthority(authority);
|
|
7598
7796
|
acpErrors.reset();
|
|
7599
7797
|
return await measurePerf("runtime.exec.prompt", async () => {
|
|
7600
|
-
return await withTimeout(client.prompt(sessionId, options.prompt), options.timeoutMs);
|
|
7798
|
+
return await withTimeout(client.prompt(sessionId, options.prompt, void 0, void 0, void 0, { signal: control?.signal }), options.timeoutMs);
|
|
7601
7799
|
});
|
|
7602
7800
|
};
|
|
7603
7801
|
const runExecPromptWithRetries = async (sessionId) => {
|
|
@@ -7606,78 +7804,105 @@ async function runOnce(options) {
|
|
|
7606
7804
|
for (let attempt = 0;; attempt++) try {
|
|
7607
7805
|
return await runExecPromptAttempt(sessionId);
|
|
7608
7806
|
} catch (error) {
|
|
7609
|
-
if (await preparePromptRetry(error, attempt, maxRetries, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors)) continue;
|
|
7807
|
+
if (await preparePromptRetry(error, attempt, maxRetries, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors, control?.signal)) continue;
|
|
7610
7808
|
promptTurnActive = false;
|
|
7611
7809
|
throw error;
|
|
7612
7810
|
}
|
|
7613
7811
|
};
|
|
7614
7812
|
try {
|
|
7615
|
-
return await
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
sessionId
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7813
|
+
return await runWithOptionalInterrupt({
|
|
7814
|
+
handleProcessInterrupts: control?.handleProcessInterrupts,
|
|
7815
|
+
run: async () => {
|
|
7816
|
+
assertControlAuthority(authority);
|
|
7817
|
+
await measurePerf("runtime.exec.start", async () => {
|
|
7818
|
+
await withTimeout(client.start(authority), options.timeoutMs);
|
|
7819
|
+
});
|
|
7820
|
+
assertControlAuthority(authority);
|
|
7821
|
+
const createdSession = await measurePerf("runtime.exec.create_session", async () => {
|
|
7822
|
+
return await withTimeout(client.createSession(absolutePath(options.cwd), authority), options.timeoutMs);
|
|
7823
|
+
});
|
|
7824
|
+
assertControlAuthority(authority);
|
|
7825
|
+
const sessionId = createdSession.sessionId;
|
|
7826
|
+
if (createdSession.models) applyAdvertisedModelState(controlState, createdSession.models);
|
|
7827
|
+
controlState = applyConfigOptionsToState(controlState, createdSession.configOptions ?? []);
|
|
7828
|
+
const modelApplication = await applyRequestedModelIfAdvertised({
|
|
7829
|
+
client,
|
|
7830
|
+
sessionId,
|
|
7831
|
+
requestedModel: options.sessionOptions?.model,
|
|
7832
|
+
models: createdSession.models,
|
|
7833
|
+
agentCommand: options.agentCommand,
|
|
7834
|
+
timeoutMs: options.timeoutMs,
|
|
7835
|
+
authority,
|
|
7836
|
+
onWarning: options.suppressSdkConsoleErrors ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
|
|
7837
|
+
});
|
|
7838
|
+
if (modelApplication.response) controlState = applyConfigOptionsToState(controlState, modelApplication.response.configOptions);
|
|
7839
|
+
for (const configOption of options.configOptions ?? []) {
|
|
7840
|
+
assertControlAuthority(authority);
|
|
7841
|
+
const response = await withTimeout(client.setSessionConfigOption(sessionId, configOption.configId, configOption.value, advertisedModelState(controlState), authority), options.timeoutMs);
|
|
7842
|
+
controlState = applyConfigOptionsToState(controlState, response.configOptions);
|
|
7843
|
+
}
|
|
7844
|
+
assertControlAuthority(authority);
|
|
7845
|
+
output.setContext({ sessionId });
|
|
7846
|
+
const response = await runExecPromptWithRetries(sessionId);
|
|
7847
|
+
promptTurnActive = false;
|
|
7848
|
+
return toPromptResult(response.stopReason, sessionId, client, response._meta);
|
|
7849
|
+
},
|
|
7850
|
+
handleInterrupt: async () => {
|
|
7851
|
+
await client.cancelActivePrompt(INTERRUPT_CANCEL_WAIT_MS);
|
|
7852
|
+
await closeOwnedClient();
|
|
7638
7853
|
}
|
|
7639
|
-
output.setContext({ sessionId });
|
|
7640
|
-
const response = await runExecPromptWithRetries(sessionId);
|
|
7641
|
-
promptTurnActive = false;
|
|
7642
|
-
output.flush();
|
|
7643
|
-
return toPromptResult(response.stopReason, sessionId, client, response._meta);
|
|
7644
|
-
}, async () => {
|
|
7645
|
-
await client.cancelActivePrompt(INTERRUPT_CANCEL_WAIT_MS);
|
|
7646
|
-
await client.close();
|
|
7647
7854
|
});
|
|
7648
7855
|
} catch (error) {
|
|
7649
|
-
const
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7856
|
+
const failure = directExecutionError(error, authority.signal);
|
|
7857
|
+
const matchedAcpError = acpErrors.match(failure);
|
|
7858
|
+
attachAcpErrorPayload(failure, matchedAcpError);
|
|
7859
|
+
markOutputAlreadyEmitted(failure, matchedAcpError !== void 0 && shouldMarkAcpErrorsEmitted);
|
|
7860
|
+
throw failure;
|
|
7653
7861
|
} finally {
|
|
7654
|
-
|
|
7862
|
+
try {
|
|
7863
|
+
await closeOwnedClient();
|
|
7864
|
+
} finally {
|
|
7865
|
+
try {
|
|
7866
|
+
options.onPermissionStats?.(client.getPermissionStats());
|
|
7867
|
+
} catch {}
|
|
7868
|
+
output.flush();
|
|
7869
|
+
}
|
|
7655
7870
|
}
|
|
7656
7871
|
}
|
|
7657
|
-
async function sendSessionDirect(options) {
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7872
|
+
async function sendSessionDirect(options, control) {
|
|
7873
|
+
const closeProvidedClient = control && options.client ? ownDirectClient(options.client, control.signal) : void 0;
|
|
7874
|
+
try {
|
|
7875
|
+
control?.signal.throwIfAborted();
|
|
7876
|
+
return await runSessionPrompt({
|
|
7877
|
+
ownedSignal: control?.signal,
|
|
7878
|
+
closeProvidedClient,
|
|
7879
|
+
waitSignal: control?.signal,
|
|
7880
|
+
handleProcessInterrupts: control?.handleProcessInterrupts,
|
|
7881
|
+
sessionRecordId: options.sessionId,
|
|
7882
|
+
prompt: options.prompt,
|
|
7883
|
+
mcpServers: options.mcpServers,
|
|
7884
|
+
permissionMode: options.permissionMode,
|
|
7885
|
+
resumePolicy: options.resumePolicy,
|
|
7886
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
7887
|
+
permissionPolicy: options.permissionPolicy,
|
|
7888
|
+
authCredentials: options.authCredentials,
|
|
7889
|
+
authPolicy: options.authPolicy,
|
|
7890
|
+
fs: options.fs,
|
|
7891
|
+
terminal: options.terminal,
|
|
7892
|
+
outputFormatter: options.outputFormatter,
|
|
7893
|
+
errorEmissionPolicy: options.errorEmissionPolicy,
|
|
7894
|
+
onAcpMessage: options.onAcpMessage,
|
|
7895
|
+
onSessionUpdate: options.onSessionUpdate,
|
|
7896
|
+
onClientOperation: options.onClientOperation,
|
|
7897
|
+
onPermissionEscalation: options.onPermissionEscalation,
|
|
7898
|
+
timeoutMs: options.timeoutMs,
|
|
7899
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
7900
|
+
verbose: options.verbose,
|
|
7901
|
+
client: options.client
|
|
7902
|
+
});
|
|
7903
|
+
} finally {
|
|
7904
|
+
await closeProvidedClient?.();
|
|
7905
|
+
}
|
|
7681
7906
|
}
|
|
7682
7907
|
//#endregion
|
|
7683
7908
|
//#region src/session/execution/queue-owner-runtime.ts
|
|
@@ -8079,6 +8304,6 @@ async function sendSession(options) {
|
|
|
8079
8304
|
}
|
|
8080
8305
|
const queueOwnerRuntimeTestInternals = { queueOwnerExitIsFatal };
|
|
8081
8306
|
//#endregion
|
|
8082
|
-
export {
|
|
8307
|
+
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, exitCodeForOutputErrorCode as dt, getAcpxVersion as et, installPerfMetricsCapture as f, normalizeOutputError as ft, 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 };
|
|
8083
8308
|
|
|
8084
|
-
//# sourceMappingURL=queue-owner-runtime-
|
|
8309
|
+
//# sourceMappingURL=queue-owner-runtime-C4OOosbo.js.map
|