acpx 0.19.1 → 0.19.3
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-DWlx5BQ4.js → agent-registry-DWnqUICY.js} +20 -17
- package/dist/agent-registry-DWnqUICY.js.map +1 -0
- package/dist/agent-registry.js +1 -1
- package/dist/{cli-BerngYEz.js → cli-C1uofM8S.js} +5 -5
- package/dist/{cli-BerngYEz.js.map → cli-C1uofM8S.js.map} +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +56 -40
- package/dist/cli.js.map +1 -1
- package/dist/{client-BchvQRpz.d.ts → client-CdG6nVDG.d.ts} +4 -3
- package/dist/client-CdG6nVDG.d.ts.map +1 -0
- package/dist/{flows-DimX0it5.js → flows-hz_Pfsp-.js} +9 -5
- package/dist/flows-hz_Pfsp-.js.map +1 -0
- package/dist/flows.d.ts +2 -2
- package/dist/flows.js +1 -1
- package/dist/{invocation-options-vobVApfO.js → invocation-options-DbZzMvKM.js} +5 -5
- package/dist/{invocation-options-vobVApfO.js.map → invocation-options-DbZzMvKM.js.map} +1 -1
- package/dist/{ipc-DAApJ5mi.js → ipc-BW06Pbb8.js} +1246 -369
- package/dist/ipc-BW06Pbb8.js.map +1 -0
- package/dist/{output-Cnuji8Bo.js → output-BjeeG-cj.js} +3 -3
- package/dist/{output-Cnuji8Bo.js.map → output-BjeeG-cj.js.map} +1 -1
- package/dist/{queue-owner-runtime-C4OOosbo.js → queue-owner-runtime-Dhr4gxNE.js} +542 -929
- package/dist/queue-owner-runtime-Dhr4gxNE.js.map +1 -0
- package/dist/runtime.d.ts +7 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +24 -17
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-Dp6mzNSy.d.ts → session-options-X7LhX9BF.d.ts} +2 -1
- package/dist/{session-options-Dp6mzNSy.d.ts.map → session-options-X7LhX9BF.d.ts.map} +1 -1
- package/dist/{session-watch-Bj9LoYUq.js → session-watch-CgfkUKLA.js} +4 -4
- package/dist/{session-watch-Bj9LoYUq.js.map → session-watch-CgfkUKLA.js.map} +1 -1
- package/dist/watch-BGdqBiNX.js +85 -0
- package/dist/watch-BGdqBiNX.js.map +1 -0
- package/package.json +11 -11
- package/skills/acpx/SKILL.md +7 -2
- package/dist/agent-registry-DWlx5BQ4.js.map +0 -1
- package/dist/client-BchvQRpz.d.ts.map +0 -1
- package/dist/flows-DimX0it5.js.map +0 -1
- package/dist/ipc-DAApJ5mi.js.map +0 -1
- package/dist/queue-owner-runtime-C4OOosbo.js.map +0 -1
- package/dist/watch-Cz6jbrRs.js +0 -32
- package/dist/watch-Cz6jbrRs.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
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-
|
|
1
|
+
import { $ as resetPerfMetrics, Bt as GeminiAcpStartupTimeoutError, C as textPrompt, Ct as normalizeOutputError, F as normalizeName, Ft as AgentSpawnError, G as SESSION_JOURNAL_SCHEMA, Gt as SessionModeReplayError, Ht as PermissionPromptUnavailableError, It as AgentStartupError, Jt as SessionResumeRequiredError, K as SessionJournalReader, Kt as SessionModelReplayError, L as readSessionRecord, Lt as AuthPolicyError, M as isoNow$1, Nt as AcpxOperationalError, O as absolutePath, Pt as AgentDisconnectedError, Q as measurePerf, R as resolveSessionRecord, Rt as ClaudeAcpSessionCreateTimeoutError, S as promptToDisplayText, St as isRetryablePromptError, T as lockOwnerPid, Tt as isAcpResourceNotFoundError, U as extractAgentSessionId, Ut as QueueConnectionError, Vt as PermissionDeniedError, W as normalizeAgentSessionId, Wt as SessionConfigOptionReplayError, X as getPerfMetricsSnapshot, Y as formatPerfMetric, Yt as UnsupportedPromptContentError, Z as incrementPerfCounter, _ as ProcessDescendants, a as trySetModelOnRunningOwner, at as isSessionUpdateNotification, bt as formatErrorMessage, d as releaseQueueOwnerLease, dt as sessionBaseDir, et as setPerfGauge, f as terminateProcess, ft as sessionEventActivePath, g as QueueLeaseGuardSettlementError, h as waitMs$1, i as trySetModeOnRunningOwner, it as isAcpMessageObject, j as findSessionByDirectoryWalk, k as findGitRepositoryRoot, m as tryAcquireQueueOwnerLease, mt as sessionEventSegmentPath, n as tryCloseSessionOnRunningOwner, o as trySubmitToRunningOwner, p as terminateQueueOwnerForSession, pt as sessionEventLockPath, r as trySetConfigOptionOnRunningOwner, rt as isAcpJsonRpcMessage, s as SessionQueueOwner, t as tryCancelOnRunningOwner, tt as startPerfTimer, u as refreshQueueOwnerLease, ut as defaultSessionEventLog, vt as isProcessAlive, w as createLockOwner, wt as extractAcpError, xt as isAcpQueryClosedBeforeResponseError, y as getUnsupportedPromptContentMessage, z as writeSessionRecord, zt as CopilotAcpUnsupportedError } from "./ipc-BW06Pbb8.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-DWnqUICY.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";
|
|
@@ -8,145 +8,14 @@ import fs$1 from "node:fs/promises";
|
|
|
8
8
|
import { appendRegularFile, appendRegularFileSync, withTempFile } from "@openclaw/fs-safe/advanced";
|
|
9
9
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
10
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
+
import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
|
|
12
|
+
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
11
13
|
import { Readable, Writable } from "node:stream";
|
|
12
14
|
import { PROTOCOL_VERSION, RequestError, client, methods } from "@agentclientprotocol/sdk";
|
|
13
15
|
import { FsSafeError } from "@openclaw/fs-safe/errors";
|
|
14
16
|
import { isPathInside } from "@openclaw/fs-safe/path";
|
|
15
17
|
import { root } from "@openclaw/fs-safe/root";
|
|
16
18
|
import readline from "node:readline/promises";
|
|
17
|
-
import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
|
|
18
|
-
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
19
|
-
import net from "node:net";
|
|
20
|
-
//#region src/acp/error-normalization.ts
|
|
21
|
-
const AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
|
|
22
|
-
const QUERY_CLOSED_BEFORE_RESPONSE_DETAIL = "query closed before response received";
|
|
23
|
-
function asRecord$3(value) {
|
|
24
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
|
-
function isAuthRequiredMessage(value) {
|
|
28
|
-
if (!value) return false;
|
|
29
|
-
const normalized = value.toLowerCase();
|
|
30
|
-
return [
|
|
31
|
-
"auth required",
|
|
32
|
-
"authentication required",
|
|
33
|
-
"authorization required",
|
|
34
|
-
"credential required",
|
|
35
|
-
"credentials required",
|
|
36
|
-
"token required",
|
|
37
|
-
"login required"
|
|
38
|
-
].some((needle) => normalized.includes(needle));
|
|
39
|
-
}
|
|
40
|
-
function isAcpAuthRequiredPayload(acp) {
|
|
41
|
-
if (!acp) return false;
|
|
42
|
-
if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) return false;
|
|
43
|
-
if (isAuthRequiredMessage(acp.message)) return true;
|
|
44
|
-
const data = asRecord$3(acp.data);
|
|
45
|
-
if (!data) return false;
|
|
46
|
-
return hasAuthRequiredData(data);
|
|
47
|
-
}
|
|
48
|
-
function hasAuthRequiredData(data) {
|
|
49
|
-
return data.authRequired === true || hasNonEmptyString(data.methodId) || hasNonEmptyArray(data.methods);
|
|
50
|
-
}
|
|
51
|
-
function hasNonEmptyString(value) {
|
|
52
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
53
|
-
}
|
|
54
|
-
function hasNonEmptyArray(value) {
|
|
55
|
-
return Array.isArray(value) && value.length > 0;
|
|
56
|
-
}
|
|
57
|
-
function isOutputErrorCode(value) {
|
|
58
|
-
return typeof value === "string" && OUTPUT_ERROR_CODES.includes(value);
|
|
59
|
-
}
|
|
60
|
-
function isOutputErrorOrigin(value) {
|
|
61
|
-
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
62
|
-
}
|
|
63
|
-
function readOutputErrorMeta(error) {
|
|
64
|
-
const record = asRecord$3(error);
|
|
65
|
-
if (!record) return {};
|
|
66
|
-
return {
|
|
67
|
-
outputCode: isOutputErrorCode(record.outputCode) ? record.outputCode : void 0,
|
|
68
|
-
detailCode: typeof record.detailCode === "string" && record.detailCode.trim().length > 0 ? record.detailCode : void 0,
|
|
69
|
-
origin: isOutputErrorOrigin(record.origin) ? record.origin : void 0,
|
|
70
|
-
retryable: typeof record.retryable === "boolean" ? record.retryable : void 0,
|
|
71
|
-
acp: extractAcpError(record.acp)
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function isTimeoutLike(error) {
|
|
75
|
-
return error instanceof Error && error.name === "TimeoutError";
|
|
76
|
-
}
|
|
77
|
-
function isNoSessionLike(error) {
|
|
78
|
-
return error instanceof Error && error.name === "NoSessionError";
|
|
79
|
-
}
|
|
80
|
-
function isUsageLike(error) {
|
|
81
|
-
if (!(error instanceof Error)) return false;
|
|
82
|
-
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord$3(error)?.code === "commander.invalidArgument";
|
|
83
|
-
}
|
|
84
|
-
function formatErrorMessage(error) {
|
|
85
|
-
return formatUnknownErrorMessage(error);
|
|
86
|
-
}
|
|
87
|
-
function isAcpQueryClosedBeforeResponseError(error) {
|
|
88
|
-
const acp = extractAcpError(error);
|
|
89
|
-
if (!acp || acp.code !== -32603) return false;
|
|
90
|
-
const details = asRecord$3(acp.data)?.details;
|
|
91
|
-
if (typeof details !== "string") return false;
|
|
92
|
-
return details.toLowerCase().includes(QUERY_CLOSED_BEFORE_RESPONSE_DETAIL);
|
|
93
|
-
}
|
|
94
|
-
function mapErrorCode(error) {
|
|
95
|
-
if (error instanceof PermissionPromptUnavailableError) return "PERMISSION_PROMPT_UNAVAILABLE";
|
|
96
|
-
if (error instanceof PermissionDeniedError) return "PERMISSION_DENIED";
|
|
97
|
-
if (isTimeoutLike(error)) return "TIMEOUT";
|
|
98
|
-
if (isNoSessionLike(error) || isAcpResourceNotFoundError(error)) return "NO_SESSION";
|
|
99
|
-
if (isUsageLike(error)) return "USAGE";
|
|
100
|
-
}
|
|
101
|
-
function normalizeOutputError(error, options = {}) {
|
|
102
|
-
const meta = readOutputErrorMeta(error);
|
|
103
|
-
const code = resolveOutputErrorCode(error, options, meta);
|
|
104
|
-
const acp = options.acp ?? meta.acp ?? extractAcpError(error);
|
|
105
|
-
return {
|
|
106
|
-
code,
|
|
107
|
-
message: formatErrorMessage(error),
|
|
108
|
-
detailCode: resolveDetailCode(error, acp, options, meta),
|
|
109
|
-
origin: meta.origin ?? options.origin,
|
|
110
|
-
retryable: meta.retryable ?? options.retryable,
|
|
111
|
-
acp
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function resolveOutputErrorCode(error, options, meta) {
|
|
115
|
-
const code = meta.outputCode ?? mapErrorCode(error) ?? options.defaultCode ?? "RUNTIME";
|
|
116
|
-
if (code === "RUNTIME" && isAcpResourceNotFoundError(error)) return "NO_SESSION";
|
|
117
|
-
return code;
|
|
118
|
-
}
|
|
119
|
-
function resolveDetailCode(error, acp, options, meta) {
|
|
120
|
-
return meta.detailCode ?? options.detailCode ?? (error instanceof AuthPolicyError || isAcpAuthRequiredPayload(acp) ? "AUTH_REQUIRED" : void 0);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Returns true when an error from `client.prompt()` looks transient and
|
|
124
|
-
* can reasonably be retried (e.g. model-API 400/500, network hiccups that
|
|
125
|
-
* surface as ACP internal errors).
|
|
126
|
-
*
|
|
127
|
-
* Errors that are definitively non-recoverable (auth, missing session,
|
|
128
|
-
* invalid params, timeout, permission) return false.
|
|
129
|
-
*/
|
|
130
|
-
function isRetryablePromptError(error) {
|
|
131
|
-
if (isNonRetryablePromptError(error)) return false;
|
|
132
|
-
const acp = extractAcpError(error);
|
|
133
|
-
if (!acp) return false;
|
|
134
|
-
return acp.code === -32603 || acp.code === -32700;
|
|
135
|
-
}
|
|
136
|
-
function isNonRetryablePromptError(error) {
|
|
137
|
-
return error instanceof PermissionDeniedError || error instanceof PermissionPromptUnavailableError || isTimeoutLike(error) || isNoSessionLike(error) || isUsageLike(error);
|
|
138
|
-
}
|
|
139
|
-
function exitCodeForOutputErrorCode(code) {
|
|
140
|
-
switch (code) {
|
|
141
|
-
case "USAGE": return EXIT_CODES.USAGE;
|
|
142
|
-
case "TIMEOUT": return EXIT_CODES.TIMEOUT;
|
|
143
|
-
case "NO_SESSION": return EXIT_CODES.NO_SESSION;
|
|
144
|
-
case "PERMISSION_DENIED":
|
|
145
|
-
case "PERMISSION_PROMPT_UNAVAILABLE": return EXIT_CODES.PERMISSION_DENIED;
|
|
146
|
-
default: return EXIT_CODES.ERROR;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
//#endregion
|
|
150
19
|
//#region src/async-control.ts
|
|
151
20
|
var TimeoutError = class extends Error {
|
|
152
21
|
constructor(timeoutMs) {
|
|
@@ -477,6 +346,246 @@ async function listSessionEvents(sessionId, maxSegments) {
|
|
|
477
346
|
}).readAcpMessages();
|
|
478
347
|
}
|
|
479
348
|
//#endregion
|
|
349
|
+
//#region src/session/turn-ownership.ts
|
|
350
|
+
const LOCK_RETRY_MS = 15;
|
|
351
|
+
const INCOMPLETE_RESERVATION_GRACE_MS = 15e3;
|
|
352
|
+
let lastCreatedAt = 0;
|
|
353
|
+
const localTurns = /* @__PURE__ */ new Map();
|
|
354
|
+
async function readLock(filePath) {
|
|
355
|
+
try {
|
|
356
|
+
const stat = await fs$1.lstat(filePath, { bigint: true });
|
|
357
|
+
if (!stat.isFile()) throw new Error(`Session turn lock is not a regular file: ${filePath}`);
|
|
358
|
+
return {
|
|
359
|
+
stat,
|
|
360
|
+
payload: await fs$1.readFile(filePath, "utf8")
|
|
361
|
+
};
|
|
362
|
+
} catch (error) {
|
|
363
|
+
if (error.code === "ENOENT") return;
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function parseLock(payload) {
|
|
368
|
+
try {
|
|
369
|
+
return JSON.parse(payload);
|
|
370
|
+
} catch {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async function tryAcquireGuard(filePath, owner, signal) {
|
|
375
|
+
try {
|
|
376
|
+
return await acquireFileLock(filePath, {
|
|
377
|
+
managerKey: "acpx.session-turn",
|
|
378
|
+
lockPath: `${filePath}.guard`,
|
|
379
|
+
staleMs: Infinity,
|
|
380
|
+
timeoutMs: LOCK_RETRY_MS,
|
|
381
|
+
retry: {
|
|
382
|
+
retries: 8,
|
|
383
|
+
minTimeout: 1,
|
|
384
|
+
maxTimeout: 2,
|
|
385
|
+
factor: 1,
|
|
386
|
+
randomize: false
|
|
387
|
+
},
|
|
388
|
+
staleRecovery: "remove-if-unchanged",
|
|
389
|
+
payload: () => owner.payload,
|
|
390
|
+
shouldReclaim: ({ payload }) => owner.hasExited(payload, false, signal),
|
|
391
|
+
shouldRemoveStaleLock: ({ payload }) => owner.hasExited(payload, true, signal)
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (error.code === "file_lock_timeout") return;
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function removeObservedLock(filePath, observed) {
|
|
399
|
+
const current = await readLock(filePath);
|
|
400
|
+
if (!current) return true;
|
|
401
|
+
if (current.payload !== observed.payload || current.stat.mtimeNs !== observed.stat.mtimeNs || current.stat.size !== observed.stat.size) return false;
|
|
402
|
+
await fs$1.unlink(filePath).catch((error) => {
|
|
403
|
+
if (error.code !== "ENOENT") throw error;
|
|
404
|
+
});
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
async function recoverAbandonedLock(filePath, owner, signal) {
|
|
408
|
+
const observed = await readLock(filePath);
|
|
409
|
+
if (!observed) return true;
|
|
410
|
+
const payload = parseLock(observed.payload);
|
|
411
|
+
const pid = lockOwnerPid(payload);
|
|
412
|
+
if (pid && (!await owner.hasExited(payload, false, signal) || !await owner.hasExited(payload, true, signal))) return false;
|
|
413
|
+
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return false;
|
|
414
|
+
if (await removeObservedLock(filePath, observed)) {
|
|
415
|
+
incrementPerfCounter("session.events.stale_lock_recovered");
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
async function publishLock(filePath, payload, signal) {
|
|
421
|
+
await withTempFile({
|
|
422
|
+
rootDir: path.dirname(filePath),
|
|
423
|
+
prefix: "session-turn",
|
|
424
|
+
fileName: "lock"
|
|
425
|
+
}, async (temporaryPath) => {
|
|
426
|
+
await fs$1.writeFile(temporaryPath, payload, {
|
|
427
|
+
encoding: "utf8",
|
|
428
|
+
flag: "wx",
|
|
429
|
+
mode: 384
|
|
430
|
+
});
|
|
431
|
+
signal?.throwIfAborted();
|
|
432
|
+
try {
|
|
433
|
+
await fs$1.link(temporaryPath, filePath);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
if (!isHardlinkFallbackError(error)) throw error;
|
|
436
|
+
await fs$1.writeFile(filePath, payload, {
|
|
437
|
+
encoding: "utf8",
|
|
438
|
+
flag: "wx",
|
|
439
|
+
mode: 384
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
async function removePublishedLock(filePath, payload) {
|
|
445
|
+
const current = await readLock(filePath);
|
|
446
|
+
if (current?.payload === payload) await removeObservedLock(filePath, current);
|
|
447
|
+
}
|
|
448
|
+
async function rejectAcquisition(filePath, receipt, error) {
|
|
449
|
+
const failures = [error];
|
|
450
|
+
await disposeTurn(filePath, receipt).catch((cleanupError) => {
|
|
451
|
+
failures.push(...cleanupError instanceof AggregateError ? cleanupError.errors : [cleanupError]);
|
|
452
|
+
});
|
|
453
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn acquisition and cleanup failed");
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
async function releaseTurnGuard(receipt) {
|
|
457
|
+
await receipt.guard?.release();
|
|
458
|
+
receipt.guard = void 0;
|
|
459
|
+
}
|
|
460
|
+
async function removeTurnMarker(filePath, receipt) {
|
|
461
|
+
if (!receipt.removeMarker) return true;
|
|
462
|
+
if (receipt.guard && !await receipt.guard.verifyStillHeld()) await releaseTurnGuard(receipt);
|
|
463
|
+
receipt.guard ??= await tryAcquireGuard(filePath, receipt.owner);
|
|
464
|
+
if (!receipt.guard) return false;
|
|
465
|
+
await receipt.removeMarker();
|
|
466
|
+
receipt.removeMarker = void 0;
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
async function settleTurn(filePath, receipt) {
|
|
470
|
+
if (receipt.settled) return true;
|
|
471
|
+
const pending = receipt.pending ??= (async () => {
|
|
472
|
+
receipt.failed = false;
|
|
473
|
+
const failures = [];
|
|
474
|
+
try {
|
|
475
|
+
if (!await removeTurnMarker(filePath, receipt)) return false;
|
|
476
|
+
} catch (error) {
|
|
477
|
+
failures.push(error);
|
|
478
|
+
}
|
|
479
|
+
if (failures.length === 0 || !receipt.admitted) try {
|
|
480
|
+
await releaseTurnGuard(receipt);
|
|
481
|
+
} catch (error) {
|
|
482
|
+
failures.push(error);
|
|
483
|
+
}
|
|
484
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn cleanup failed");
|
|
485
|
+
if (failures.length === 1) throw failures[0];
|
|
486
|
+
receipt.settled = true;
|
|
487
|
+
localTurns.delete(filePath);
|
|
488
|
+
return true;
|
|
489
|
+
})();
|
|
490
|
+
try {
|
|
491
|
+
return await pending;
|
|
492
|
+
} finally {
|
|
493
|
+
if (receipt.pending === pending) {
|
|
494
|
+
receipt.pending = void 0;
|
|
495
|
+
receipt.failed = !receipt.settled;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
async function disposeTurn(filePath, receipt) {
|
|
500
|
+
if (!await settleTurn(filePath, receipt)) throw new Error(`Session turn cleanup is waiting for its guard: ${filePath}`);
|
|
501
|
+
}
|
|
502
|
+
async function tryPublishLock(filePath, payload, owner, signal) {
|
|
503
|
+
for (;;) try {
|
|
504
|
+
await publishLock(filePath, payload, signal);
|
|
505
|
+
return true;
|
|
506
|
+
} catch (error) {
|
|
507
|
+
if (error.code !== "EEXIST") throw error;
|
|
508
|
+
if (!await recoverAbandonedLock(filePath, owner, signal)) return false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function tryAcquireTurnCleanup(filePath, payload, owner, signal) {
|
|
512
|
+
const previous = localTurns.get(filePath);
|
|
513
|
+
if (previous) {
|
|
514
|
+
if (previous.failed) await settleTurn(filePath, previous);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const receipt = {
|
|
518
|
+
owner,
|
|
519
|
+
removeMarker: async () => await removePublishedLock(filePath, payload),
|
|
520
|
+
admitted: false,
|
|
521
|
+
failed: false,
|
|
522
|
+
settled: false
|
|
523
|
+
};
|
|
524
|
+
localTurns.set(filePath, receipt);
|
|
525
|
+
try {
|
|
526
|
+
receipt.guard = await tryAcquireGuard(filePath, owner, signal);
|
|
527
|
+
} catch (error) {
|
|
528
|
+
localTurns.delete(filePath);
|
|
529
|
+
throw error;
|
|
530
|
+
}
|
|
531
|
+
if (!receipt.guard) {
|
|
532
|
+
localTurns.delete(filePath);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
return receipt;
|
|
536
|
+
}
|
|
537
|
+
async function tryAcquireTurn(filePath, payload, owner, signal) {
|
|
538
|
+
const receipt = await tryAcquireTurnCleanup(filePath, payload, owner, signal);
|
|
539
|
+
if (!receipt) return;
|
|
540
|
+
try {
|
|
541
|
+
signal?.throwIfAborted();
|
|
542
|
+
if (!await tryPublishLock(filePath, payload, owner, signal)) {
|
|
543
|
+
await disposeTurn(filePath, receipt);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const observed = await readLock(filePath);
|
|
547
|
+
if (observed?.payload !== payload) throw new Error(`Session turn ownership changed before admission: ${filePath}`);
|
|
548
|
+
signal?.throwIfAborted();
|
|
549
|
+
receipt.admitted = true;
|
|
550
|
+
receipt.removeMarker = async () => await removeObservedLock(filePath, observed);
|
|
551
|
+
return { [Symbol.asyncDispose]: async () => await disposeTurn(filePath, receipt) };
|
|
552
|
+
} catch (error) {
|
|
553
|
+
return await rejectAcquisition(filePath, receipt, error);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function acquireSessionTurn(recordId, signal) {
|
|
557
|
+
return await acquireSessionOwnership(sessionEventLockPath(recordId), signal);
|
|
558
|
+
}
|
|
559
|
+
async function acquireSessionScope(scope, signal) {
|
|
560
|
+
return await acquireSessionTurn(`ensure:${createHash("sha256").update(JSON.stringify([
|
|
561
|
+
scope.agentCommand,
|
|
562
|
+
scope.cwd,
|
|
563
|
+
scope.name
|
|
564
|
+
])).digest("hex")}`, signal);
|
|
565
|
+
}
|
|
566
|
+
async function acquireSessionImport(signal) {
|
|
567
|
+
return await acquireSessionOwnership(path.join(sessionBaseDir(), ".import-admission.lock"), signal);
|
|
568
|
+
}
|
|
569
|
+
async function acquireSessionOwnership(requestedPath, signal) {
|
|
570
|
+
await fs$1.mkdir(path.dirname(requestedPath), {
|
|
571
|
+
recursive: true,
|
|
572
|
+
mode: 448
|
|
573
|
+
});
|
|
574
|
+
const filePath = path.join(await fs$1.realpath(path.dirname(requestedPath)), path.basename(requestedPath));
|
|
575
|
+
const owner = await createLockOwner({ signal });
|
|
576
|
+
lastCreatedAt = Math.max(Date.now(), lastCreatedAt + 1);
|
|
577
|
+
const payload = `${JSON.stringify({
|
|
578
|
+
...owner.payload,
|
|
579
|
+
created_at: new Date(lastCreatedAt).toISOString()
|
|
580
|
+
}, null, 2)}\n`;
|
|
581
|
+
for (;;) {
|
|
582
|
+
signal?.throwIfAborted();
|
|
583
|
+
const turn = await tryAcquireTurn(filePath, payload, owner, signal);
|
|
584
|
+
if (turn) return turn;
|
|
585
|
+
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
//#endregion
|
|
480
589
|
//#region src/session/execution/contracts.ts
|
|
481
590
|
const DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
482
591
|
function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
@@ -1147,10 +1256,10 @@ function assignClaudeCodeSystemPrompt(target, systemPrompt) {
|
|
|
1147
1256
|
function isAppendSystemPrompt(value) {
|
|
1148
1257
|
return !!value && typeof value === "object" && typeof value.append === "string" && value.append.length > 0;
|
|
1149
1258
|
}
|
|
1150
|
-
function resolveClaudeCodeExecutable(platform = process.platform, env = process.env) {
|
|
1259
|
+
function resolveClaudeCodeExecutable(platform = process.platform, env = process.env, cwd) {
|
|
1151
1260
|
if (platform !== "win32") return;
|
|
1152
1261
|
if (readWindowsEnvValue(env, "CLAUDE_CODE_EXECUTABLE")) return;
|
|
1153
|
-
return resolveWindowsExecutablePath("claude", env);
|
|
1262
|
+
return resolveWindowsExecutablePath("claude", env, cwd);
|
|
1154
1263
|
}
|
|
1155
1264
|
//#endregion
|
|
1156
1265
|
//#region src/acp/auth-env.ts
|
|
@@ -1197,7 +1306,7 @@ function promotePrefixedAuthEnvironment(env) {
|
|
|
1197
1306
|
if (!normalized) continue;
|
|
1198
1307
|
protectEnvKey(protectedKeys, key);
|
|
1199
1308
|
protectEnvKey(protectedKeys, normalized);
|
|
1200
|
-
|
|
1309
|
+
assignIfMissing(env, normalized, value);
|
|
1201
1310
|
}
|
|
1202
1311
|
return protectedKeys;
|
|
1203
1312
|
}
|
|
@@ -1234,7 +1343,7 @@ function addAuthCredentialEnvKeys(protectedKeys, methodId, credential) {
|
|
|
1234
1343
|
}
|
|
1235
1344
|
function assignAuthCredentialEnv(env, methodId, credential) {
|
|
1236
1345
|
if (typeof credential !== "string" || credential.trim().length === 0) return;
|
|
1237
|
-
if (!methodId.includes("=") && !methodId.includes("\0")
|
|
1346
|
+
if (!methodId.includes("=") && !methodId.includes("\0")) assignIfMissing(env, methodId, credential);
|
|
1238
1347
|
const normalized = toEnvToken(methodId);
|
|
1239
1348
|
if (normalized) {
|
|
1240
1349
|
assignIfMissing(env, `${AUTH_ENV_PREFIX}${normalized}`, credential);
|
|
@@ -1242,7 +1351,10 @@ function assignAuthCredentialEnv(env, methodId, credential) {
|
|
|
1242
1351
|
}
|
|
1243
1352
|
}
|
|
1244
1353
|
function assignIfMissing(env, key, value) {
|
|
1245
|
-
if (env[key]
|
|
1354
|
+
if (env[key] != null) return;
|
|
1355
|
+
const normalizedKey = protectedEnvKey(key);
|
|
1356
|
+
if (process.platform === "win32" && Object.entries(env).some(([existingKey, existingValue]) => existingValue != null && protectedEnvKey(existingKey) === normalizedKey)) return;
|
|
1357
|
+
env[key] = value;
|
|
1246
1358
|
}
|
|
1247
1359
|
function resolveConfiguredAuthCredential(methodId, authCredentials) {
|
|
1248
1360
|
const configCredentials = authCredentials ?? {};
|
|
@@ -1753,7 +1865,7 @@ function buildTerminalSpawnOptions(command, cwd, env, platform = process.platfor
|
|
|
1753
1865
|
"pipe"
|
|
1754
1866
|
],
|
|
1755
1867
|
windowsHide: true
|
|
1756
|
-
}, platform, resolvedEnv ?? process.env);
|
|
1868
|
+
}, platform, resolvedEnv ?? process.env, cwd);
|
|
1757
1869
|
}
|
|
1758
1870
|
function readTerminalOutputCeiling() {
|
|
1759
1871
|
const raw = process.env.ACPX_TERMINAL_MAX_OUTPUT_BYTES?.trim();
|
|
@@ -1804,6 +1916,7 @@ var TerminalManager = class {
|
|
|
1804
1916
|
processHelperTimeoutMs;
|
|
1805
1917
|
outputCeilingBytes;
|
|
1806
1918
|
terminals = /* @__PURE__ */ new Map();
|
|
1919
|
+
creations = /* @__PURE__ */ new Map();
|
|
1807
1920
|
constructor(options) {
|
|
1808
1921
|
this.outputCeilingBytes = readTerminalOutputCeiling();
|
|
1809
1922
|
this.cwd = options.cwd;
|
|
@@ -1820,7 +1933,15 @@ var TerminalManager = class {
|
|
|
1820
1933
|
this.permissionMode = permissionMode;
|
|
1821
1934
|
this.nonInteractivePermissions = nonInteractivePermissions ?? "deny";
|
|
1822
1935
|
}
|
|
1823
|
-
|
|
1936
|
+
createTerminal(params, authority) {
|
|
1937
|
+
const creation = {};
|
|
1938
|
+
const pending = Promise.resolve().then(() => this.create(params, authority, creation));
|
|
1939
|
+
this.creations.set(pending, creation);
|
|
1940
|
+
const remove = () => this.creations.delete(pending);
|
|
1941
|
+
pending.then(remove, remove);
|
|
1942
|
+
return pending;
|
|
1943
|
+
}
|
|
1944
|
+
async create(params, authority, creation) {
|
|
1824
1945
|
assertControlAuthority(authority);
|
|
1825
1946
|
const commandLine = toCommandLine(params.command, params.args);
|
|
1826
1947
|
const summary = `terminal/create: ${commandLine}`;
|
|
@@ -1884,10 +2005,15 @@ var TerminalManager = class {
|
|
|
1884
2005
|
await terminal.descendants?.capture(terminal.processHelperTimeoutMs);
|
|
1885
2006
|
assertControlAuthority(authority);
|
|
1886
2007
|
} catch (error) {
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2008
|
+
try {
|
|
2009
|
+
await this.releaseTerminal({
|
|
2010
|
+
terminalId,
|
|
2011
|
+
sessionId: params.sessionId
|
|
2012
|
+
});
|
|
2013
|
+
} catch (cleanupError) {
|
|
2014
|
+
creation.cleanupFailure = { error: cleanupError };
|
|
2015
|
+
throw cleanupError;
|
|
2016
|
+
}
|
|
1891
2017
|
throw error;
|
|
1892
2018
|
}
|
|
1893
2019
|
this.emitOperation({
|
|
@@ -2020,11 +2146,13 @@ var TerminalManager = class {
|
|
|
2020
2146
|
}
|
|
2021
2147
|
}
|
|
2022
2148
|
async shutdown() {
|
|
2149
|
+
const creations = [...this.creations];
|
|
2150
|
+
await Promise.allSettled(creations.map(([pending]) => pending));
|
|
2023
2151
|
const results = await Promise.allSettled(Array.from(this.terminals.keys(), (terminalId) => this.releaseTerminal({
|
|
2024
2152
|
terminalId,
|
|
2025
2153
|
sessionId: "shutdown"
|
|
2026
2154
|
})));
|
|
2027
|
-
const failures = [];
|
|
2155
|
+
const failures = creations.flatMap(([, creation]) => creation.cleanupFailure ? [creation.cleanupFailure.error] : []);
|
|
2028
2156
|
for (const result of results) if (result.status === "rejected") failures.push(result.reason);
|
|
2029
2157
|
if (failures.length > 0) throw new AggregateError(failures, "Terminal shutdown failed", { cause: failures[0] });
|
|
2030
2158
|
}
|
|
@@ -2468,8 +2596,9 @@ var AcpClient = class {
|
|
|
2468
2596
|
}
|
|
2469
2597
|
fallback?.abort();
|
|
2470
2598
|
}
|
|
2471
|
-
async start(authority) {
|
|
2599
|
+
async start(authority, options) {
|
|
2472
2600
|
assertControlAuthority(authority);
|
|
2601
|
+
if (options) this.options.sessionOptions = structuredClone(options.sessionOptions);
|
|
2473
2602
|
if (this.hasLiveConnection()) return;
|
|
2474
2603
|
const epoch = await this.waitForPreviousClose();
|
|
2475
2604
|
const maxMessageBytes = readMaxAcpMessageBytes();
|
|
@@ -2588,14 +2717,14 @@ var AcpClient = class {
|
|
|
2588
2717
|
async ensureLaunchSupport(plan) {
|
|
2589
2718
|
if (plan.copilotAcp) await ensureCopilotAcpSupport(plan.spawnCommand, plan.commandContext);
|
|
2590
2719
|
if (!plan.claudeAcp) return;
|
|
2591
|
-
const claudeExe = resolveClaudeCodeExecutable(process.platform, plan.spawnOptions.env);
|
|
2720
|
+
const claudeExe = resolveClaudeCodeExecutable(process.platform, plan.spawnOptions.env, plan.spawnOptions.cwd);
|
|
2592
2721
|
if (claudeExe) {
|
|
2593
2722
|
plan.spawnOptions.env.CLAUDE_CODE_EXECUTABLE = claudeExe;
|
|
2594
2723
|
this.log(`resolved system Claude Code executable: ${claudeExe}`);
|
|
2595
2724
|
}
|
|
2596
2725
|
}
|
|
2597
2726
|
prepareProcessLaunch(command, args, options) {
|
|
2598
|
-
const resolved = buildAgentSpawnCommand(command, args, process.platform, options.env);
|
|
2727
|
+
const resolved = buildAgentSpawnCommand(command, args, process.platform, options.env, options.cwd);
|
|
2599
2728
|
return {
|
|
2600
2729
|
launch: Object.freeze({
|
|
2601
2730
|
launchId: randomUUID(),
|
|
@@ -2805,6 +2934,10 @@ var AcpClient = class {
|
|
|
2805
2934
|
onPromptRequestWritten: (active, owner) => this.onPromptRequestWritten(active, owner)
|
|
2806
2935
|
});
|
|
2807
2936
|
}
|
|
2937
|
+
sessionMeta() {
|
|
2938
|
+
const { command, args } = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
|
|
2939
|
+
return buildClaudeCodeOptionsMeta(this.options.sessionOptions, isClaudeAcpCommand(command, args));
|
|
2940
|
+
}
|
|
2808
2941
|
async createSession(cwd = this.options.cwd, authority) {
|
|
2809
2942
|
assertControlAuthority(authority);
|
|
2810
2943
|
const connection = this.getConnection();
|
|
@@ -2816,7 +2949,7 @@ var AcpClient = class {
|
|
|
2816
2949
|
const createPromise = this.runConnectionRequest(() => connection.agent.request(methods.agent.session.new, {
|
|
2817
2950
|
cwd: sessionCwd,
|
|
2818
2951
|
mcpServers: this.options.mcpServers ?? [],
|
|
2819
|
-
_meta:
|
|
2952
|
+
_meta: this.sessionMeta()
|
|
2820
2953
|
}), authority);
|
|
2821
2954
|
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
|
2822
2955
|
} catch (error) {
|
|
@@ -2856,7 +2989,8 @@ var AcpClient = class {
|
|
|
2856
2989
|
response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.load, {
|
|
2857
2990
|
sessionId,
|
|
2858
2991
|
cwd: sessionCwd,
|
|
2859
|
-
mcpServers: this.options.mcpServers ?? []
|
|
2992
|
+
mcpServers: this.options.mcpServers ?? [],
|
|
2993
|
+
_meta: this.sessionMeta()
|
|
2860
2994
|
}), options.authority);
|
|
2861
2995
|
await this.waitForSessionUpdateDrain(options.replayIdleMs ?? REPLAY_IDLE_MS, options.replayDrainTimeoutMs ?? REPLAY_DRAIN_TIMEOUT_MS);
|
|
2862
2996
|
} finally {
|
|
@@ -2874,7 +3008,8 @@ var AcpClient = class {
|
|
|
2874
3008
|
const response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.resume, {
|
|
2875
3009
|
sessionId,
|
|
2876
3010
|
cwd: sessionCwd,
|
|
2877
|
-
mcpServers: this.options.mcpServers ?? []
|
|
3011
|
+
mcpServers: this.options.mcpServers ?? [],
|
|
3012
|
+
_meta: this.sessionMeta()
|
|
2878
3013
|
}), authority);
|
|
2879
3014
|
this.loadedSessionId = sessionId;
|
|
2880
3015
|
const result = toReconnectedSessionResult(response);
|
|
@@ -3862,6 +3997,7 @@ function contentToUserContent(content) {
|
|
|
3862
3997
|
if (content.type === "resource") return resourceToUserContent(content);
|
|
3863
3998
|
if (content.type === "image") return { Image: {
|
|
3864
3999
|
source: content.data,
|
|
4000
|
+
mime_type: content.mimeType,
|
|
3865
4001
|
size: null
|
|
3866
4002
|
} };
|
|
3867
4003
|
if (content.type === "audio") return { Audio: {
|
|
@@ -3930,7 +4066,10 @@ function appendAgentThinking(agent, text) {
|
|
|
3930
4066
|
}
|
|
3931
4067
|
function trimRuntimeText(value, maxChars) {
|
|
3932
4068
|
if (value.length <= maxChars) return value;
|
|
3933
|
-
|
|
4069
|
+
let end = Math.max(0, maxChars - 3);
|
|
4070
|
+
const last = value.charCodeAt(end - 1);
|
|
4071
|
+
if (last >= 55296 && last <= 56319) end -= 1;
|
|
4072
|
+
return `${value.slice(0, end)}...`;
|
|
3934
4073
|
}
|
|
3935
4074
|
function statusIndicatesComplete(status) {
|
|
3936
4075
|
if (typeof status !== "string") return false;
|
|
@@ -4477,13 +4616,6 @@ function clearDesiredConfigOption(state, configId) {
|
|
|
4477
4616
|
function getDesiredModelId(state) {
|
|
4478
4617
|
return normalizeModelId(state?.session_options?.model);
|
|
4479
4618
|
}
|
|
4480
|
-
function setCurrentModelId(record, modelId) {
|
|
4481
|
-
const acpx = ensureAcpxState(record.acpx);
|
|
4482
|
-
const normalized = normalizeModelId(modelId);
|
|
4483
|
-
if (normalized) acpx.current_model_id = normalized;
|
|
4484
|
-
else delete acpx.current_model_id;
|
|
4485
|
-
record.acpx = acpx;
|
|
4486
|
-
}
|
|
4487
4619
|
function syncAdvertisedModelState(record, models) {
|
|
4488
4620
|
if (!models) return;
|
|
4489
4621
|
const acpx = ensureAcpxState(record.acpx);
|
|
@@ -4506,9 +4638,13 @@ async function applyRequestedModelIfAdvertised(params) {
|
|
|
4506
4638
|
});
|
|
4507
4639
|
if (warning) params.onWarning?.(warning);
|
|
4508
4640
|
if (!params.models) return { applied: false };
|
|
4509
|
-
if (params.models.currentModelId === requestedModel) return {
|
|
4641
|
+
if (params.models.currentModelId === requestedModel) return {
|
|
4642
|
+
applied: true,
|
|
4643
|
+
modelId: requestedModel
|
|
4644
|
+
};
|
|
4510
4645
|
return {
|
|
4511
4646
|
applied: true,
|
|
4647
|
+
modelId: requestedModel,
|
|
4512
4648
|
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models, params.authority), params.timeoutMs)
|
|
4513
4649
|
};
|
|
4514
4650
|
}
|
|
@@ -4517,276 +4653,61 @@ async function applyRequestedModelIfAdvertised(params) {
|
|
|
4517
4653
|
function applyConfigOptionsToState(state, configOptions) {
|
|
4518
4654
|
const acpxState = cloneSessionAcpxState(state) ?? {};
|
|
4519
4655
|
applyConfigOptionsModelState(acpxState, configOptions);
|
|
4520
|
-
return acpxState;
|
|
4521
|
-
}
|
|
4522
|
-
function applyConfigOptionsToRecord(record, result) {
|
|
4523
|
-
const configOptions = result?.configOptions;
|
|
4524
|
-
if (!configOptions) return;
|
|
4525
|
-
record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
|
|
4526
|
-
}
|
|
4527
|
-
function applyInitialModelSelection(record, originalModels, requestedModel, modelApplication) {
|
|
4528
|
-
applyConfigOptionsToRecord(record, modelApplication.response);
|
|
4529
|
-
syncAdvertisedModelState(record, modelApplication.response ? modelStateFromConfigOptions(modelApplication.response.configOptions) : originalModels);
|
|
4530
|
-
if (modelApplication.applied) setCurrentModelId(record, currentModelIdFromSetModelResponse(modelApplication.response, requestedModel));
|
|
4531
|
-
}
|
|
4532
|
-
function applyAcceptedConfigOptions(state, response) {
|
|
4533
|
-
const next = cloneSessionAcpxState(state) ?? {};
|
|
4534
|
-
if (!response) return next;
|
|
4535
|
-
applyConfigOptionsModelState(next, response.configOptions);
|
|
4536
|
-
if (!next.desired_config_options) return next;
|
|
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);
|
|
4540
|
-
else delete next.desired_config_options;
|
|
4541
|
-
return next;
|
|
4542
|
-
}
|
|
4543
|
-
function applyModelSelection(state, modelId, response) {
|
|
4544
|
-
const modelConfigId = advertisedModelState(state)?.configId;
|
|
4545
|
-
const next = applyAcceptedConfigOptions(state, response);
|
|
4546
|
-
next.session_options = {
|
|
4547
|
-
...next.session_options,
|
|
4548
|
-
model: modelId
|
|
4549
|
-
};
|
|
4550
|
-
next.current_model_id = currentModelIdFromSetModelResponse(response, modelId);
|
|
4551
|
-
clearDesiredConfigOption(next, modelConfigId ?? advertisedModelState(next)?.configId);
|
|
4552
|
-
return next;
|
|
4553
|
-
}
|
|
4554
|
-
function applyConfigOptionSelection(state, configId, value, response, modelConfigId = advertisedModelState(state)?.configId) {
|
|
4555
|
-
if (configId === modelConfigId || configId === modelStateFromConfigOptions(response.configOptions)?.configId) return applyModelSelection(state, value, response);
|
|
4556
|
-
const next = cloneSessionAcpxState(state) ?? {};
|
|
4557
|
-
next.desired_config_options = {
|
|
4558
|
-
...next.desired_config_options,
|
|
4559
|
-
[configId]: value
|
|
4560
|
-
};
|
|
4561
|
-
return applyAcceptedConfigOptions(next, response);
|
|
4562
|
-
}
|
|
4563
|
-
//#endregion
|
|
4564
|
-
//#region src/session/turn-ownership.ts
|
|
4565
|
-
const LOCK_RETRY_MS = 15;
|
|
4566
|
-
const INCOMPLETE_RESERVATION_GRACE_MS = 15e3;
|
|
4567
|
-
let lastCreatedAt = 0;
|
|
4568
|
-
const localTurns = /* @__PURE__ */ new Map();
|
|
4569
|
-
async function readLock(filePath) {
|
|
4570
|
-
try {
|
|
4571
|
-
const stat = await fs$1.lstat(filePath, { bigint: true });
|
|
4572
|
-
if (!stat.isFile()) throw new Error(`Session turn lock is not a regular file: ${filePath}`);
|
|
4573
|
-
return {
|
|
4574
|
-
stat,
|
|
4575
|
-
payload: await fs$1.readFile(filePath, "utf8")
|
|
4576
|
-
};
|
|
4577
|
-
} catch (error) {
|
|
4578
|
-
if (error.code === "ENOENT") return;
|
|
4579
|
-
throw error;
|
|
4580
|
-
}
|
|
4581
|
-
}
|
|
4582
|
-
function parseLock(payload) {
|
|
4583
|
-
try {
|
|
4584
|
-
return JSON.parse(payload);
|
|
4585
|
-
} catch {
|
|
4586
|
-
return;
|
|
4587
|
-
}
|
|
4588
|
-
}
|
|
4589
|
-
async function tryAcquireGuard(filePath, owner, signal) {
|
|
4590
|
-
try {
|
|
4591
|
-
return await acquireFileLock(filePath, {
|
|
4592
|
-
managerKey: "acpx.session-turn",
|
|
4593
|
-
lockPath: `${filePath}.guard`,
|
|
4594
|
-
staleMs: Infinity,
|
|
4595
|
-
timeoutMs: LOCK_RETRY_MS,
|
|
4596
|
-
retry: {
|
|
4597
|
-
retries: 8,
|
|
4598
|
-
minTimeout: 1,
|
|
4599
|
-
maxTimeout: 2,
|
|
4600
|
-
factor: 1,
|
|
4601
|
-
randomize: false
|
|
4602
|
-
},
|
|
4603
|
-
staleRecovery: "remove-if-unchanged",
|
|
4604
|
-
payload: () => owner.payload,
|
|
4605
|
-
shouldReclaim: ({ payload }) => owner.hasExited(payload, false, signal),
|
|
4606
|
-
shouldRemoveStaleLock: ({ payload }) => owner.hasExited(payload, true, signal)
|
|
4607
|
-
});
|
|
4608
|
-
} catch (error) {
|
|
4609
|
-
if (error.code === "file_lock_timeout") return;
|
|
4610
|
-
throw error;
|
|
4611
|
-
}
|
|
4612
|
-
}
|
|
4613
|
-
async function removeObservedLock(filePath, observed) {
|
|
4614
|
-
const current = await readLock(filePath);
|
|
4615
|
-
if (!current) return true;
|
|
4616
|
-
if (current.payload !== observed.payload || current.stat.mtimeNs !== observed.stat.mtimeNs || current.stat.size !== observed.stat.size) return false;
|
|
4617
|
-
await fs$1.unlink(filePath).catch((error) => {
|
|
4618
|
-
if (error.code !== "ENOENT") throw error;
|
|
4619
|
-
});
|
|
4620
|
-
return true;
|
|
4621
|
-
}
|
|
4622
|
-
async function recoverAbandonedLock(filePath, owner, signal) {
|
|
4623
|
-
const observed = await readLock(filePath);
|
|
4624
|
-
if (!observed) return true;
|
|
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;
|
|
4628
|
-
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return false;
|
|
4629
|
-
if (await removeObservedLock(filePath, observed)) {
|
|
4630
|
-
incrementPerfCounter("session.events.stale_lock_recovered");
|
|
4631
|
-
return true;
|
|
4632
|
-
}
|
|
4633
|
-
return false;
|
|
4634
|
-
}
|
|
4635
|
-
async function publishLock(filePath, payload, signal) {
|
|
4636
|
-
await withTempFile({
|
|
4637
|
-
rootDir: path.dirname(filePath),
|
|
4638
|
-
prefix: "session-turn",
|
|
4639
|
-
fileName: "lock"
|
|
4640
|
-
}, async (temporaryPath) => {
|
|
4641
|
-
await fs$1.writeFile(temporaryPath, payload, {
|
|
4642
|
-
encoding: "utf8",
|
|
4643
|
-
flag: "wx",
|
|
4644
|
-
mode: 384
|
|
4645
|
-
});
|
|
4646
|
-
signal?.throwIfAborted();
|
|
4647
|
-
try {
|
|
4648
|
-
await fs$1.link(temporaryPath, filePath);
|
|
4649
|
-
} catch (error) {
|
|
4650
|
-
if (!isHardlinkFallbackError(error)) throw error;
|
|
4651
|
-
await fs$1.writeFile(filePath, payload, {
|
|
4652
|
-
encoding: "utf8",
|
|
4653
|
-
flag: "wx",
|
|
4654
|
-
mode: 384
|
|
4655
|
-
});
|
|
4656
|
-
}
|
|
4657
|
-
});
|
|
4658
|
-
}
|
|
4659
|
-
async function removePublishedLock(filePath, payload) {
|
|
4660
|
-
const current = await readLock(filePath);
|
|
4661
|
-
if (current?.payload === payload) await removeObservedLock(filePath, current);
|
|
4662
|
-
}
|
|
4663
|
-
async function rejectAcquisition(filePath, receipt, error) {
|
|
4664
|
-
const failures = [error];
|
|
4665
|
-
await disposeTurn(filePath, receipt).catch((cleanupError) => {
|
|
4666
|
-
failures.push(...cleanupError instanceof AggregateError ? cleanupError.errors : [cleanupError]);
|
|
4667
|
-
});
|
|
4668
|
-
if (failures.length > 1) throw new AggregateError(failures, "Session turn acquisition and cleanup failed");
|
|
4669
|
-
throw error;
|
|
4670
|
-
}
|
|
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 = [];
|
|
4689
|
-
try {
|
|
4690
|
-
if (!await removeTurnMarker(filePath, receipt)) return false;
|
|
4691
|
-
} catch (error) {
|
|
4692
|
-
failures.push(error);
|
|
4693
|
-
}
|
|
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
|
-
}
|
|
4656
|
+
return acpxState;
|
|
4713
4657
|
}
|
|
4714
|
-
|
|
4715
|
-
|
|
4658
|
+
function applyConfigOptionsToRecord(record, result) {
|
|
4659
|
+
const configOptions = result?.configOptions;
|
|
4660
|
+
if (!configOptions) return;
|
|
4661
|
+
record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
|
|
4716
4662
|
}
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
} catch (error) {
|
|
4722
|
-
if (error.code !== "EEXIST") throw error;
|
|
4723
|
-
if (!await recoverAbandonedLock(filePath, owner, signal)) return false;
|
|
4724
|
-
}
|
|
4663
|
+
function applyInitialModelSelection(record, originalModels, modelApplication) {
|
|
4664
|
+
applyConfigOptionsToRecord(record, modelApplication.response);
|
|
4665
|
+
syncAdvertisedModelState(record, modelApplication.response?.configOptions !== void 0 ? modelStateFromConfigOptions(modelApplication.response.configOptions) : originalModels);
|
|
4666
|
+
if (modelApplication.applied) record.acpx = applyModelSelection(record.acpx, modelApplication.modelId, modelApplication.response);
|
|
4725
4667
|
}
|
|
4726
|
-
|
|
4727
|
-
const
|
|
4728
|
-
if (
|
|
4729
|
-
|
|
4730
|
-
|
|
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;
|
|
4668
|
+
function applyAcceptedConfigOptions(state, response, selection) {
|
|
4669
|
+
const next = cloneSessionAcpxState(state) ?? {};
|
|
4670
|
+
if (response?.configOptions === void 0) {
|
|
4671
|
+
const option = next.config_options?.find((entry) => entry.id === selection.configId);
|
|
4672
|
+
if (option) option.currentValue = selection.value;
|
|
4673
|
+
return next;
|
|
4749
4674
|
}
|
|
4750
|
-
|
|
4675
|
+
applyConfigOptionsModelState(next, response.configOptions);
|
|
4676
|
+
reconcileDesiredConfigOptions(next, response.configOptions);
|
|
4677
|
+
return next;
|
|
4751
4678
|
}
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
await disposeTurn(filePath, receipt);
|
|
4759
|
-
return;
|
|
4760
|
-
}
|
|
4761
|
-
const observed = await readLock(filePath);
|
|
4762
|
-
if (observed?.payload !== payload) throw new Error(`Session turn ownership changed before admission: ${filePath}`);
|
|
4763
|
-
signal?.throwIfAborted();
|
|
4764
|
-
receipt.admitted = true;
|
|
4765
|
-
receipt.removeMarker = async () => await removeObservedLock(filePath, observed);
|
|
4766
|
-
return { [Symbol.asyncDispose]: async () => await disposeTurn(filePath, receipt) };
|
|
4767
|
-
} catch (error) {
|
|
4768
|
-
return await rejectAcquisition(filePath, receipt, error);
|
|
4769
|
-
}
|
|
4679
|
+
function reconcileDesiredConfigOptions(state, configOptions) {
|
|
4680
|
+
if (!state.desired_config_options) return;
|
|
4681
|
+
const desiredEntries = [];
|
|
4682
|
+
for (const option of configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(state.desired_config_options, option.id)) desiredEntries.push([option.id, option.currentValue]);
|
|
4683
|
+
if (desiredEntries.length > 0) state.desired_config_options = Object.fromEntries(desiredEntries);
|
|
4684
|
+
else delete state.desired_config_options;
|
|
4770
4685
|
}
|
|
4771
|
-
|
|
4772
|
-
const
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4686
|
+
function applyModelSelection(state, modelId, response) {
|
|
4687
|
+
const modelConfigId = advertisedModelState(state)?.configId;
|
|
4688
|
+
const next = applyAcceptedConfigOptions(state, response, {
|
|
4689
|
+
configId: modelConfigId,
|
|
4690
|
+
value: modelId
|
|
4691
|
+
});
|
|
4692
|
+
next.session_options = {
|
|
4693
|
+
...next.session_options,
|
|
4694
|
+
model: modelId
|
|
4695
|
+
};
|
|
4696
|
+
next.current_model_id = currentModelIdFromSetModelResponse(response, modelId);
|
|
4697
|
+
clearDesiredConfigOption(next, modelConfigId ?? advertisedModelState(next)?.configId);
|
|
4698
|
+
return next;
|
|
4699
|
+
}
|
|
4700
|
+
function applyConfigOptionSelection(state, configId, value, response, modelConfigId = advertisedModelState(state)?.configId) {
|
|
4701
|
+
if (configId === modelConfigId || configId === modelStateFromConfigOptions(response.configOptions)?.configId) return applyModelSelection(state, value, response);
|
|
4702
|
+
const next = cloneSessionAcpxState(state) ?? {};
|
|
4703
|
+
next.desired_config_options = {
|
|
4704
|
+
...next.desired_config_options,
|
|
4705
|
+
[configId]: value
|
|
4706
|
+
};
|
|
4707
|
+
return applyAcceptedConfigOptions(next, response, {
|
|
4708
|
+
configId,
|
|
4709
|
+
value
|
|
4776
4710
|
});
|
|
4777
|
-
const filePath = path.join(await fs$1.realpath(path.dirname(requestedPath)), path.basename(requestedPath));
|
|
4778
|
-
const owner = await createLockOwner({ signal });
|
|
4779
|
-
lastCreatedAt = Math.max(Date.now(), lastCreatedAt + 1);
|
|
4780
|
-
const payload = `${JSON.stringify({
|
|
4781
|
-
...owner.payload,
|
|
4782
|
-
created_at: new Date(lastCreatedAt).toISOString()
|
|
4783
|
-
}, null, 2)}\n`;
|
|
4784
|
-
for (;;) {
|
|
4785
|
-
signal?.throwIfAborted();
|
|
4786
|
-
const turn = await tryAcquireTurn(filePath, payload, owner, signal);
|
|
4787
|
-
if (turn) return turn;
|
|
4788
|
-
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
4789
|
-
}
|
|
4790
4711
|
}
|
|
4791
4712
|
//#endregion
|
|
4792
4713
|
//#region src/session/execution/direct-lifetime.ts
|
|
@@ -4813,406 +4734,6 @@ function directExecutionError(error, signal) {
|
|
|
4813
4734
|
return signal?.aborted && error instanceof AgentDisconnectedError ? signal.reason : error;
|
|
4814
4735
|
}
|
|
4815
4736
|
//#endregion
|
|
4816
|
-
//#region src/session/queue/ipc-server.ts
|
|
4817
|
-
const QUEUE_SOCKET_DRAIN_TIMEOUT_MS = 1e3;
|
|
4818
|
-
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
4819
|
-
return {
|
|
4820
|
-
type: "error",
|
|
4821
|
-
requestId,
|
|
4822
|
-
ownerGeneration: void 0,
|
|
4823
|
-
code: "RUNTIME",
|
|
4824
|
-
detailCode,
|
|
4825
|
-
origin: "queue",
|
|
4826
|
-
retryable: options.retryable,
|
|
4827
|
-
message
|
|
4828
|
-
};
|
|
4829
|
-
}
|
|
4830
|
-
function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
|
|
4831
|
-
const normalized = normalizeOutputError(error, {
|
|
4832
|
-
defaultCode: "RUNTIME",
|
|
4833
|
-
origin: "queue",
|
|
4834
|
-
detailCode,
|
|
4835
|
-
retryable: options.retryable
|
|
4836
|
-
});
|
|
4837
|
-
return {
|
|
4838
|
-
type: "error",
|
|
4839
|
-
requestId,
|
|
4840
|
-
code: normalized.code,
|
|
4841
|
-
detailCode: normalized.detailCode,
|
|
4842
|
-
origin: normalized.origin,
|
|
4843
|
-
message: normalized.message,
|
|
4844
|
-
retryable: normalized.retryable,
|
|
4845
|
-
acp: normalized.acp
|
|
4846
|
-
};
|
|
4847
|
-
}
|
|
4848
|
-
function armSocketDrainTimeout(socket) {
|
|
4849
|
-
if (process.platform !== "win32") socket.setTimeout(QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
|
|
4850
|
-
}
|
|
4851
|
-
function writeQueueMessage(socket, message) {
|
|
4852
|
-
if (socket.destroyed || !socket.writable) return;
|
|
4853
|
-
if (!socket.write(`${JSON.stringify(message)}\n`) && !socket.timeout) armSocketDrainTimeout(socket);
|
|
4854
|
-
}
|
|
4855
|
-
var SessionQueueOwner = class SessionQueueOwner {
|
|
4856
|
-
server;
|
|
4857
|
-
controlHandlers;
|
|
4858
|
-
ownerGeneration;
|
|
4859
|
-
maxQueueDepth;
|
|
4860
|
-
maxRequestBytes;
|
|
4861
|
-
onQueueDepthChanged;
|
|
4862
|
-
pending = [];
|
|
4863
|
-
activeTask;
|
|
4864
|
-
waiters = [];
|
|
4865
|
-
sockets = /* @__PURE__ */ new Set();
|
|
4866
|
-
taskSockets = /* @__PURE__ */ new Set();
|
|
4867
|
-
drainingSockets = /* @__PURE__ */ new Set();
|
|
4868
|
-
closed = false;
|
|
4869
|
-
constructor(server, controlHandlers, lease, options) {
|
|
4870
|
-
this.server = server;
|
|
4871
|
-
this.controlHandlers = controlHandlers;
|
|
4872
|
-
this.ownerGeneration = lease.ownerGeneration;
|
|
4873
|
-
this.maxQueueDepth = Math.max(1, Math.round(options.maxQueueDepth));
|
|
4874
|
-
this.maxRequestBytes = queueRequestByteLimit(options.maxRequestBytes?.toString());
|
|
4875
|
-
this.onQueueDepthChanged = options.onQueueDepthChanged;
|
|
4876
|
-
}
|
|
4877
|
-
static async start(lease, controlHandlers, options = { maxQueueDepth: 16 }) {
|
|
4878
|
-
const ownerRef = { current: void 0 };
|
|
4879
|
-
const server = net.createServer((socket) => {
|
|
4880
|
-
ownerRef.current?.handleConnection(socket);
|
|
4881
|
-
});
|
|
4882
|
-
ownerRef.current = new SessionQueueOwner(server, controlHandlers, lease, options);
|
|
4883
|
-
await new Promise((resolve, reject) => {
|
|
4884
|
-
const onListening = () => {
|
|
4885
|
-
server.off("error", onError);
|
|
4886
|
-
resolve();
|
|
4887
|
-
};
|
|
4888
|
-
const onError = (error) => {
|
|
4889
|
-
server.off("listening", onListening);
|
|
4890
|
-
reject(error);
|
|
4891
|
-
};
|
|
4892
|
-
server.once("listening", onListening);
|
|
4893
|
-
server.once("error", onError);
|
|
4894
|
-
server.listen(lease.socketPath);
|
|
4895
|
-
});
|
|
4896
|
-
return ownerRef.current;
|
|
4897
|
-
}
|
|
4898
|
-
beginShutdown() {
|
|
4899
|
-
if (this.closed) return;
|
|
4900
|
-
this.closed = true;
|
|
4901
|
-
for (const waiter of this.waiters.splice(0)) waiter(void 0);
|
|
4902
|
-
for (const task of this.pending.splice(0)) {
|
|
4903
|
-
if (task.waitForCompletion) task.send(makeQueueOwnerError(task.requestId, "Queue owner shutting down before prompt execution", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
|
|
4904
|
-
task.close();
|
|
4905
|
-
}
|
|
4906
|
-
for (const socket of this.sockets) if (!this.taskSockets.has(socket) && !this.drainingSockets.has(socket)) socket.destroy();
|
|
4907
|
-
this.emitQueueDepth();
|
|
4908
|
-
}
|
|
4909
|
-
async close() {
|
|
4910
|
-
this.beginShutdown();
|
|
4911
|
-
await new Promise((resolve) => {
|
|
4912
|
-
const forceCloseTimer = setTimeout(() => {
|
|
4913
|
-
for (const socket of this.sockets) socket.destroy();
|
|
4914
|
-
}, QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
|
|
4915
|
-
forceCloseTimer.unref();
|
|
4916
|
-
this.server.close(() => {
|
|
4917
|
-
clearTimeout(forceCloseTimer);
|
|
4918
|
-
resolve();
|
|
4919
|
-
});
|
|
4920
|
-
});
|
|
4921
|
-
}
|
|
4922
|
-
async nextTask(timeoutMs) {
|
|
4923
|
-
if (this.pending.length > 0) {
|
|
4924
|
-
const task = this.pending.shift();
|
|
4925
|
-
this.emitQueueDepth();
|
|
4926
|
-
if (task) {
|
|
4927
|
-
this.activeTask = task;
|
|
4928
|
-
recordPerfDuration("queue.owner.wait_ms", Date.now() - task.enqueuedAt);
|
|
4929
|
-
}
|
|
4930
|
-
return task;
|
|
4931
|
-
}
|
|
4932
|
-
if (this.closed) return;
|
|
4933
|
-
return await new Promise((resolve) => {
|
|
4934
|
-
const timer = timeoutMs != null && setTimeout(() => {
|
|
4935
|
-
const index = this.waiters.indexOf(waiter);
|
|
4936
|
-
if (index >= 0) this.waiters.splice(index, 1);
|
|
4937
|
-
resolve(void 0);
|
|
4938
|
-
}, Math.max(0, timeoutMs));
|
|
4939
|
-
const waiter = (task) => {
|
|
4940
|
-
if (timer) clearTimeout(timer);
|
|
4941
|
-
resolve(task);
|
|
4942
|
-
};
|
|
4943
|
-
this.waiters.push(waiter);
|
|
4944
|
-
});
|
|
4945
|
-
}
|
|
4946
|
-
queueDepth() {
|
|
4947
|
-
return this.pending.length;
|
|
4948
|
-
}
|
|
4949
|
-
completeTask(task) {
|
|
4950
|
-
if (this.activeTask === task) this.activeTask = void 0;
|
|
4951
|
-
}
|
|
4952
|
-
async cancelPrompt(targetRequestId) {
|
|
4953
|
-
if (targetRequestId === void 0) return await this.controlHandlers.cancelPrompt();
|
|
4954
|
-
const index = this.pending.findIndex((task) => task.requestId === targetRequestId);
|
|
4955
|
-
if (index >= 0) {
|
|
4956
|
-
const [task] = this.pending.splice(index, 1);
|
|
4957
|
-
task.send(makeQueueOwnerError(task.requestId, "Prompt cancelled before execution", "QUEUE_REQUEST_CANCELLED", { retryable: false }));
|
|
4958
|
-
task.close();
|
|
4959
|
-
this.emitQueueDepth();
|
|
4960
|
-
return true;
|
|
4961
|
-
}
|
|
4962
|
-
return this.activeTask?.requestId === targetRequestId ? await this.controlHandlers.cancelPrompt() : false;
|
|
4963
|
-
}
|
|
4964
|
-
emitQueueDepth() {
|
|
4965
|
-
this.onQueueDepthChanged?.(this.pending.length);
|
|
4966
|
-
}
|
|
4967
|
-
enqueue(task) {
|
|
4968
|
-
if (this.closed) {
|
|
4969
|
-
task.send(makeQueueOwnerError(task.requestId, "Queue owner is shutting down", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
|
|
4970
|
-
task.close();
|
|
4971
|
-
return false;
|
|
4972
|
-
}
|
|
4973
|
-
if (this.activeTask?.requestId === task.requestId || this.pending.some((queued) => queued.requestId === task.requestId)) {
|
|
4974
|
-
task.send(makeQueueOwnerError(task.requestId, "Request is already queued or active", "QUEUE_REQUEST_DUPLICATE", { retryable: false }));
|
|
4975
|
-
task.close();
|
|
4976
|
-
return false;
|
|
4977
|
-
}
|
|
4978
|
-
const waiter = this.waiters.shift();
|
|
4979
|
-
if (waiter) {
|
|
4980
|
-
this.activeTask = task;
|
|
4981
|
-
waiter(task);
|
|
4982
|
-
return true;
|
|
4983
|
-
}
|
|
4984
|
-
if (this.pending.length >= this.maxQueueDepth) {
|
|
4985
|
-
task.send({
|
|
4986
|
-
...makeQueueOwnerError(task.requestId, `Queue owner is overloaded (${this.pending.length}/${this.maxQueueDepth} queued)`, "QUEUE_OWNER_OVERLOADED", { retryable: true }),
|
|
4987
|
-
ownerGeneration: this.ownerGeneration
|
|
4988
|
-
});
|
|
4989
|
-
task.close();
|
|
4990
|
-
return false;
|
|
4991
|
-
}
|
|
4992
|
-
this.pending.push(task);
|
|
4993
|
-
this.emitQueueDepth();
|
|
4994
|
-
return true;
|
|
4995
|
-
}
|
|
4996
|
-
handleControlRequest(options) {
|
|
4997
|
-
this.taskSockets.add(options.socket);
|
|
4998
|
-
writeQueueMessage(options.socket, {
|
|
4999
|
-
type: "accepted",
|
|
5000
|
-
requestId: options.requestId,
|
|
5001
|
-
ownerGeneration: this.ownerGeneration
|
|
5002
|
-
});
|
|
5003
|
-
options.run().then((message) => {
|
|
5004
|
-
writeQueueMessage(options.socket, {
|
|
5005
|
-
...message,
|
|
5006
|
-
ownerGeneration: this.ownerGeneration
|
|
5007
|
-
});
|
|
5008
|
-
}).catch((error) => {
|
|
5009
|
-
writeQueueMessage(options.socket, {
|
|
5010
|
-
...makeQueueOwnerErrorFromUnknown(options.requestId, error, "QUEUE_CONTROL_REQUEST_FAILED"),
|
|
5011
|
-
ownerGeneration: this.ownerGeneration
|
|
5012
|
-
});
|
|
5013
|
-
}).finally(() => {
|
|
5014
|
-
this.endSocket(options.socket);
|
|
5015
|
-
});
|
|
5016
|
-
}
|
|
5017
|
-
endSocket(socket) {
|
|
5018
|
-
if (socket.destroyed || this.drainingSockets.has(socket)) return;
|
|
5019
|
-
this.taskSockets.delete(socket);
|
|
5020
|
-
this.drainingSockets.add(socket);
|
|
5021
|
-
armSocketDrainTimeout(socket);
|
|
5022
|
-
socket.end(() => {
|
|
5023
|
-
this.drainingSockets.delete(socket);
|
|
5024
|
-
socket.destroy();
|
|
5025
|
-
});
|
|
5026
|
-
}
|
|
5027
|
-
failRequest(socket, requestId, message, detailCode) {
|
|
5028
|
-
writeQueueMessage(socket, {
|
|
5029
|
-
...makeQueueOwnerError(requestId, message, detailCode, { retryable: false }),
|
|
5030
|
-
ownerGeneration: this.ownerGeneration
|
|
5031
|
-
});
|
|
5032
|
-
this.endSocket(socket);
|
|
5033
|
-
}
|
|
5034
|
-
parseRequestLine(socket, line) {
|
|
5035
|
-
let parsed;
|
|
5036
|
-
try {
|
|
5037
|
-
parsed = JSON.parse(line);
|
|
5038
|
-
} catch {
|
|
5039
|
-
this.failRequest(socket, "unknown", "Invalid queue request payload", "QUEUE_REQUEST_PAYLOAD_INVALID_JSON");
|
|
5040
|
-
return;
|
|
5041
|
-
}
|
|
5042
|
-
const request = parseQueueRequest(parsed);
|
|
5043
|
-
if (!request) this.failRequest(socket, "unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
5044
|
-
return request ?? void 0;
|
|
5045
|
-
}
|
|
5046
|
-
rejectStaleOwnerGeneration(socket, request) {
|
|
5047
|
-
if (request.ownerGeneration === void 0 || this.ownerGeneration === void 0 || request.ownerGeneration === this.ownerGeneration) return false;
|
|
5048
|
-
this.failRequest(socket, request.requestId, "Queue request targeted a stale queue owner generation", "QUEUE_OWNER_GENERATION_MISMATCH");
|
|
5049
|
-
return true;
|
|
5050
|
-
}
|
|
5051
|
-
rejectClosedOwner(socket, request) {
|
|
5052
|
-
if (!this.closed) return false;
|
|
5053
|
-
writeQueueMessage(socket, {
|
|
5054
|
-
...makeQueueOwnerError(request.requestId, "Queue owner is shutting down", "QUEUE_OWNER_CLOSED", { retryable: true }),
|
|
5055
|
-
ownerGeneration: this.ownerGeneration
|
|
5056
|
-
});
|
|
5057
|
-
this.endSocket(socket);
|
|
5058
|
-
return true;
|
|
5059
|
-
}
|
|
5060
|
-
handleControlQueueRequest(socket, request) {
|
|
5061
|
-
if (request.type === "cancel_prompt") {
|
|
5062
|
-
this.handleControlRequest({
|
|
5063
|
-
socket,
|
|
5064
|
-
requestId: request.requestId,
|
|
5065
|
-
run: async () => ({
|
|
5066
|
-
type: "cancel_result",
|
|
5067
|
-
requestId: request.requestId,
|
|
5068
|
-
cancelled: await this.cancelPrompt(request.targetRequestId)
|
|
5069
|
-
})
|
|
5070
|
-
});
|
|
5071
|
-
return true;
|
|
5072
|
-
}
|
|
5073
|
-
if (request.type === "close_session") {
|
|
5074
|
-
this.handleControlRequest({
|
|
5075
|
-
socket,
|
|
5076
|
-
requestId: request.requestId,
|
|
5077
|
-
run: async () => ({
|
|
5078
|
-
type: "close_session_result",
|
|
5079
|
-
requestId: request.requestId,
|
|
5080
|
-
closed: await this.controlHandlers.closeSession(request.timeoutMs)
|
|
5081
|
-
})
|
|
5082
|
-
});
|
|
5083
|
-
return true;
|
|
5084
|
-
}
|
|
5085
|
-
return this.handleSessionControlQueueRequest(socket, request);
|
|
5086
|
-
}
|
|
5087
|
-
handleSessionControlQueueRequest(socket, request) {
|
|
5088
|
-
if (request.type === "set_mode") {
|
|
5089
|
-
this.handleControlRequest({
|
|
5090
|
-
socket,
|
|
5091
|
-
requestId: request.requestId,
|
|
5092
|
-
run: async () => {
|
|
5093
|
-
await this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs);
|
|
5094
|
-
return {
|
|
5095
|
-
type: "set_mode_result",
|
|
5096
|
-
requestId: request.requestId,
|
|
5097
|
-
modeId: request.modeId
|
|
5098
|
-
};
|
|
5099
|
-
}
|
|
5100
|
-
});
|
|
5101
|
-
return true;
|
|
5102
|
-
}
|
|
5103
|
-
if (request.type === "set_model") {
|
|
5104
|
-
this.handleControlRequest({
|
|
5105
|
-
socket,
|
|
5106
|
-
requestId: request.requestId,
|
|
5107
|
-
run: async () => {
|
|
5108
|
-
const response = await this.controlHandlers.setSessionModel(request.modelId, request.timeoutMs);
|
|
5109
|
-
return {
|
|
5110
|
-
type: "set_model_result",
|
|
5111
|
-
requestId: request.requestId,
|
|
5112
|
-
modelId: request.modelId,
|
|
5113
|
-
response
|
|
5114
|
-
};
|
|
5115
|
-
}
|
|
5116
|
-
});
|
|
5117
|
-
return true;
|
|
5118
|
-
}
|
|
5119
|
-
if (request.type === "set_config_option") {
|
|
5120
|
-
this.handleControlRequest({
|
|
5121
|
-
socket,
|
|
5122
|
-
requestId: request.requestId,
|
|
5123
|
-
run: async () => ({
|
|
5124
|
-
type: "set_config_option_result",
|
|
5125
|
-
requestId: request.requestId,
|
|
5126
|
-
response: await this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs)
|
|
5127
|
-
})
|
|
5128
|
-
});
|
|
5129
|
-
return true;
|
|
5130
|
-
}
|
|
5131
|
-
return false;
|
|
5132
|
-
}
|
|
5133
|
-
enqueuePromptRequest(socket, request) {
|
|
5134
|
-
this.taskSockets.add(socket);
|
|
5135
|
-
const task = {
|
|
5136
|
-
requestId: request.requestId,
|
|
5137
|
-
message: request.message,
|
|
5138
|
-
prompt: request.prompt ?? textPrompt(request.message),
|
|
5139
|
-
permissionMode: request.permissionMode,
|
|
5140
|
-
resumePolicy: request.resumePolicy,
|
|
5141
|
-
nonInteractivePermissions: request.nonInteractivePermissions,
|
|
5142
|
-
permissionPolicy: request.permissionPolicy,
|
|
5143
|
-
timeoutMs: request.timeoutMs,
|
|
5144
|
-
suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
|
|
5145
|
-
promptRetries: request.promptRetries,
|
|
5146
|
-
sessionOptions: request.sessionOptions,
|
|
5147
|
-
waitForCompletion: request.waitForCompletion,
|
|
5148
|
-
reportPromptStarted: request.reportPromptStarted,
|
|
5149
|
-
enqueuedAt: Date.now(),
|
|
5150
|
-
send: (message) => {
|
|
5151
|
-
writeQueueMessage(socket, {
|
|
5152
|
-
...message,
|
|
5153
|
-
ownerGeneration: this.ownerGeneration
|
|
5154
|
-
});
|
|
5155
|
-
},
|
|
5156
|
-
close: () => {
|
|
5157
|
-
this.endSocket(socket);
|
|
5158
|
-
}
|
|
5159
|
-
};
|
|
5160
|
-
if (!this.enqueue(task)) return;
|
|
5161
|
-
writeQueueMessage(socket, {
|
|
5162
|
-
type: "accepted",
|
|
5163
|
-
requestId: request.requestId,
|
|
5164
|
-
ownerGeneration: this.ownerGeneration
|
|
5165
|
-
});
|
|
5166
|
-
if (!request.waitForCompletion) task.close();
|
|
5167
|
-
}
|
|
5168
|
-
handleConnection(socket) {
|
|
5169
|
-
this.sockets.add(socket);
|
|
5170
|
-
socket.setEncoding("utf8");
|
|
5171
|
-
socket.on("timeout", () => socket.destroy());
|
|
5172
|
-
socket.on("drain", () => {
|
|
5173
|
-
if (!socket.writableEnded) socket.setTimeout(0);
|
|
5174
|
-
});
|
|
5175
|
-
socket.once("close", () => {
|
|
5176
|
-
this.sockets.delete(socket);
|
|
5177
|
-
this.taskSockets.delete(socket);
|
|
5178
|
-
this.drainingSockets.delete(socket);
|
|
5179
|
-
});
|
|
5180
|
-
let buffer = "";
|
|
5181
|
-
let handled = false;
|
|
5182
|
-
const processLine = (line) => {
|
|
5183
|
-
if (handled) return;
|
|
5184
|
-
handled = true;
|
|
5185
|
-
const request = this.parseRequestLine(socket, line);
|
|
5186
|
-
if (!request || this.rejectClosedOwner(socket, request) || this.rejectStaleOwnerGeneration(socket, request)) return;
|
|
5187
|
-
if (this.handleControlQueueRequest(socket, request)) return;
|
|
5188
|
-
if (request.type !== "submit_prompt") {
|
|
5189
|
-
this.failRequest(socket, request.requestId, "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
5190
|
-
return;
|
|
5191
|
-
}
|
|
5192
|
-
this.enqueuePromptRequest(socket, request);
|
|
5193
|
-
};
|
|
5194
|
-
socket.on("data", (chunk) => {
|
|
5195
|
-
buffer += chunk;
|
|
5196
|
-
let index = buffer.indexOf("\n");
|
|
5197
|
-
while (index >= 0) {
|
|
5198
|
-
const rawLine = buffer.slice(0, index);
|
|
5199
|
-
if (this.rejectOversizedRequest(socket, rawLine)) return;
|
|
5200
|
-
const line = rawLine.trim();
|
|
5201
|
-
buffer = buffer.slice(index + 1);
|
|
5202
|
-
if (line.length > 0) processLine(line);
|
|
5203
|
-
index = buffer.indexOf("\n");
|
|
5204
|
-
}
|
|
5205
|
-
this.rejectOversizedRequest(socket, buffer);
|
|
5206
|
-
});
|
|
5207
|
-
socket.on("error", () => {});
|
|
5208
|
-
}
|
|
5209
|
-
rejectOversizedRequest(socket, line) {
|
|
5210
|
-
if (!queueRequestExceedsLimit(line, this.maxRequestBytes)) return false;
|
|
5211
|
-
socket.destroy();
|
|
5212
|
-
return true;
|
|
5213
|
-
}
|
|
5214
|
-
};
|
|
5215
|
-
//#endregion
|
|
5216
4737
|
//#region src/runtime/engine/reconnect.ts
|
|
5217
4738
|
const SESSION_LOAD_UNSUPPORTED_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
5218
4739
|
function createReplayState(authority) {
|
|
@@ -5288,10 +4809,7 @@ async function replayDesiredModel(params) {
|
|
|
5288
4809
|
const response = await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId, params.models, params.replay.authority), params.timeoutMs);
|
|
5289
4810
|
params.replay.acknowledged = true;
|
|
5290
4811
|
params.record.acpx = applyModelSelection(params.record.acpx, params.desiredModelId, response);
|
|
5291
|
-
const models =
|
|
5292
|
-
...params.models,
|
|
5293
|
-
currentModelId: params.desiredModelId
|
|
5294
|
-
};
|
|
4812
|
+
const models = advertisedModelState(params.record.acpx);
|
|
5295
4813
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5296
4814
|
return {
|
|
5297
4815
|
replayed: true,
|
|
@@ -5319,10 +4837,10 @@ async function replayDesiredConfigOptions(params) {
|
|
|
5319
4837
|
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value, advertisedModelState(params.record.acpx), params.replay.authority), params.timeoutMs);
|
|
5320
4838
|
params.replay.acknowledged = true;
|
|
5321
4839
|
params.record.acpx = applyConfigOptionSelection(params.record.acpx, configId, value, response);
|
|
5322
|
-
acceptedConfigOptions =
|
|
4840
|
+
acceptedConfigOptions = params.record.acpx.config_options;
|
|
5323
4841
|
result = {
|
|
5324
4842
|
replayed: true,
|
|
5325
|
-
models:
|
|
4843
|
+
models: advertisedModelState(params.record.acpx)
|
|
5326
4844
|
};
|
|
5327
4845
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5328
4846
|
} catch (error) {
|
|
@@ -5354,7 +4872,7 @@ async function connectAndLoadSession(options) {
|
|
|
5354
4872
|
logReconnectAttempt(record, storedProcessAlive, Boolean(record.pid) && !storedProcessAlive, options.verbose);
|
|
5355
4873
|
const reusingLoadedSession = client.hasReusableSession(record.acpSessionId);
|
|
5356
4874
|
if (reusingLoadedSession) incrementPerfCounter("runtime.connect_and_load.reused_session");
|
|
5357
|
-
else await withTimeout(client.start(options.authority), options.timeoutMs);
|
|
4875
|
+
else await withTimeout(client.start(options.authority, { sessionOptions: mergeSessionOptions(options.sessionOptions, sessionOptionsFromRecord(record)) }), options.timeoutMs);
|
|
5358
4876
|
assertControlAuthority(options.authority);
|
|
5359
4877
|
options.onClientAvailable?.(options.activeController);
|
|
5360
4878
|
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
@@ -5798,6 +5316,89 @@ async function runSessionSetConfigOptionDirect(options) {
|
|
|
5798
5316
|
};
|
|
5799
5317
|
}
|
|
5800
5318
|
//#endregion
|
|
5319
|
+
//#region src/session/execution/windows-process-command.ts
|
|
5320
|
+
function skipSeparators(value) {
|
|
5321
|
+
return value.replace(/^[ \t]+/u, "");
|
|
5322
|
+
}
|
|
5323
|
+
function readQuote(commandLine, state, executable) {
|
|
5324
|
+
if (!executable && state.quoted && commandLine[state.index + 1] === "\"") {
|
|
5325
|
+
state.value += "\"";
|
|
5326
|
+
state.index += 2;
|
|
5327
|
+
} else {
|
|
5328
|
+
state.quoted = !state.quoted;
|
|
5329
|
+
state.index += 1;
|
|
5330
|
+
}
|
|
5331
|
+
}
|
|
5332
|
+
function readBackslashes(commandLine, state, executable) {
|
|
5333
|
+
const start = state.index;
|
|
5334
|
+
while (commandLine[state.index] === "\\") state.index += 1;
|
|
5335
|
+
const count = state.index - start;
|
|
5336
|
+
if (executable || commandLine[state.index] !== "\"") state.value += "\\".repeat(count);
|
|
5337
|
+
else {
|
|
5338
|
+
state.value += "\\".repeat(Math.floor(count / 2));
|
|
5339
|
+
if (count % 2 === 1) {
|
|
5340
|
+
state.value += "\"";
|
|
5341
|
+
state.index += 1;
|
|
5342
|
+
} else readQuote(commandLine, state, false);
|
|
5343
|
+
}
|
|
5344
|
+
}
|
|
5345
|
+
function readArgument(commandLine, start, executable) {
|
|
5346
|
+
const state = {
|
|
5347
|
+
value: "",
|
|
5348
|
+
index: start,
|
|
5349
|
+
quoted: false
|
|
5350
|
+
};
|
|
5351
|
+
while (state.index < commandLine.length) {
|
|
5352
|
+
const char = commandLine[state.index];
|
|
5353
|
+
if (!state.quoted && /[ \t]/u.test(char)) break;
|
|
5354
|
+
if (char === "\\") readBackslashes(commandLine, state, executable);
|
|
5355
|
+
else if (char === "\"") readQuote(commandLine, state, executable);
|
|
5356
|
+
else {
|
|
5357
|
+
state.value += char;
|
|
5358
|
+
state.index += 1;
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
return state.quoted ? void 0 : state;
|
|
5362
|
+
}
|
|
5363
|
+
function readBatchCommand(commandLine) {
|
|
5364
|
+
const body = commandLine.match(/^[ \t]+\/d[ \t]+\/s[ \t]+\/c[ \t]+"([\s\S]*)"$/iu)?.[1];
|
|
5365
|
+
if (!body) return;
|
|
5366
|
+
return readBatchExecutable(body);
|
|
5367
|
+
}
|
|
5368
|
+
function readBatchExecutable(body) {
|
|
5369
|
+
let command = "";
|
|
5370
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
5371
|
+
const char = body[index];
|
|
5372
|
+
if (char === "^") {
|
|
5373
|
+
index += 1;
|
|
5374
|
+
if (index === body.length) return;
|
|
5375
|
+
command += body[index];
|
|
5376
|
+
} else if (/[ \t]/u.test(char)) break;
|
|
5377
|
+
else if (/["\r\n]/u.test(char)) return;
|
|
5378
|
+
else command += char;
|
|
5379
|
+
}
|
|
5380
|
+
return /\.(?:cmd|bat)$/iu.test(command) ? command : void 0;
|
|
5381
|
+
}
|
|
5382
|
+
/** Decode process observations, not saved command identities or POSIX launch strings. */
|
|
5383
|
+
function splitWindowsProcessCommandLine(commandLine) {
|
|
5384
|
+
const line = skipSeparators(commandLine);
|
|
5385
|
+
const executable = readArgument(line, 0, true);
|
|
5386
|
+
if (!executable?.value) return [];
|
|
5387
|
+
const argv = [executable.value];
|
|
5388
|
+
if (path.win32.basename(executable.value).toLowerCase() === "cmd.exe") {
|
|
5389
|
+
const batch = readBatchCommand(line.slice(executable.index));
|
|
5390
|
+
return batch ? [...argv, batch] : argv;
|
|
5391
|
+
}
|
|
5392
|
+
let rest = skipSeparators(line.slice(executable.index));
|
|
5393
|
+
while (rest) {
|
|
5394
|
+
const argument = readArgument(rest, 0, false);
|
|
5395
|
+
if (!argument) return [];
|
|
5396
|
+
argv.push(argument.value);
|
|
5397
|
+
rest = skipSeparators(rest.slice(argument.index));
|
|
5398
|
+
}
|
|
5399
|
+
return argv;
|
|
5400
|
+
}
|
|
5401
|
+
//#endregion
|
|
5801
5402
|
//#region src/session/execution/session-control.ts
|
|
5802
5403
|
async function cancelSessionPrompt(options) {
|
|
5803
5404
|
const cancelled = await tryCancelOnRunningOwner(options);
|
|
@@ -5907,15 +5508,16 @@ async function setSessionConfigOption(options) {
|
|
|
5907
5508
|
verbose: options.verbose
|
|
5908
5509
|
});
|
|
5909
5510
|
}
|
|
5910
|
-
function firstAgentCommandToken(command) {
|
|
5511
|
+
function firstAgentCommandToken(command, argv) {
|
|
5512
|
+
if (argv) return argv[0] || void 0;
|
|
5911
5513
|
try {
|
|
5912
5514
|
return splitCommandLine(command).command || void 0;
|
|
5913
5515
|
} catch {
|
|
5914
5516
|
return;
|
|
5915
5517
|
}
|
|
5916
5518
|
}
|
|
5917
|
-
async function isLikelyMatchingProcess(pid,
|
|
5918
|
-
const expectedToken = firstAgentCommandToken(agentCommand);
|
|
5519
|
+
async function isLikelyMatchingProcess(pid, record) {
|
|
5520
|
+
const expectedToken = firstAgentCommandToken(record.agentCommand, record.agentArgv);
|
|
5919
5521
|
if (!expectedToken) return false;
|
|
5920
5522
|
const argv = await readProcessArgv(pid);
|
|
5921
5523
|
if (argv.length === 0) return false;
|
|
@@ -5954,13 +5556,14 @@ async function readWindowsCommandLine(pid) {
|
|
|
5954
5556
|
"-NonInteractive",
|
|
5955
5557
|
"-Command",
|
|
5956
5558
|
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`
|
|
5957
|
-
], { windowsHide: true })).
|
|
5559
|
+
], { windowsHide: true })).replace(/\r?\n$/u, "") || void 0;
|
|
5958
5560
|
} catch {
|
|
5959
5561
|
return;
|
|
5960
5562
|
}
|
|
5961
5563
|
}
|
|
5962
|
-
function splitCommandLineLike(commandLine) {
|
|
5564
|
+
function splitCommandLineLike(commandLine, platform = process.platform) {
|
|
5963
5565
|
if (!commandLine) return [];
|
|
5566
|
+
if (platform === "win32") return splitWindowsProcessCommandLine(commandLine);
|
|
5964
5567
|
try {
|
|
5965
5568
|
const parsed = splitCommandLine(commandLine);
|
|
5966
5569
|
return [parsed.command, ...parsed.args];
|
|
@@ -5982,7 +5585,7 @@ async function closeSession(sessionId) {
|
|
|
5982
5585
|
}
|
|
5983
5586
|
}).catch(() => {});
|
|
5984
5587
|
if (selectedOwner) await terminateQueueOwnerForSession(record.acpxRecordId, selectedOwner);
|
|
5985
|
-
if (record.pid != null && isProcessAlive(record.pid) && await isLikelyMatchingProcess(record.pid, record
|
|
5588
|
+
if (record.pid != null && isProcessAlive(record.pid) && await isLikelyMatchingProcess(record.pid, record)) await terminateProcess(record.pid);
|
|
5986
5589
|
const ownership = await acquireSessionTurn(record.acpxRecordId);
|
|
5987
5590
|
try {
|
|
5988
5591
|
const current = await resolveSessionRecord(record.acpxRecordId);
|
|
@@ -6023,7 +5626,7 @@ async function createSessionRecordWithClient(client, options) {
|
|
|
6023
5626
|
};
|
|
6024
5627
|
persistSessionOptions(record, options.sessionOptions);
|
|
6025
5628
|
applyConfigOptionsToRecord(record, createdState.sessionResult);
|
|
6026
|
-
applyInitialModelSelection(record, createdState.sessionResult.models,
|
|
5629
|
+
applyInitialModelSelection(record, createdState.sessionResult.models, createdState.modelApplication);
|
|
6027
5630
|
await writeSessionRecord(record);
|
|
6028
5631
|
assertControlAuthority({ signal: options.signal });
|
|
6029
5632
|
return record;
|
|
@@ -6078,6 +5681,14 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
6078
5681
|
}
|
|
6079
5682
|
async function createSessionWithClient(options) {
|
|
6080
5683
|
assertControlAuthority({ signal: options.signal });
|
|
5684
|
+
if (options.resumeSessionId) {
|
|
5685
|
+
const resumedRecord = await readSessionRecord(options.resumeSessionId);
|
|
5686
|
+
assertControlAuthority({ signal: options.signal });
|
|
5687
|
+
if (resumedRecord && !resumedRecord.closed && resumedRecord.agentCommand === options.agentCommand) {
|
|
5688
|
+
await closeSession(resumedRecord.acpxRecordId);
|
|
5689
|
+
assertControlAuthority({ signal: options.signal });
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
6081
5692
|
const client = new AcpClient({
|
|
6082
5693
|
agentCommand: options.agentCommand,
|
|
6083
5694
|
agentArgv: options.agentArgv,
|
|
@@ -6165,11 +5776,11 @@ async function listAgentSessions(options) {
|
|
|
6165
5776
|
}
|
|
6166
5777
|
async function ensureSession(options) {
|
|
6167
5778
|
const cwd = absolutePath(options.cwd);
|
|
6168
|
-
const ownership = await
|
|
6169
|
-
options.agentCommand,
|
|
5779
|
+
const ownership = await acquireSessionScope({
|
|
5780
|
+
agentCommand: options.agentCommand,
|
|
6170
5781
|
cwd,
|
|
6171
|
-
normalizeName(options.name)
|
|
6172
|
-
|
|
5782
|
+
name: normalizeName(options.name)
|
|
5783
|
+
}, options.signal);
|
|
6173
5784
|
try {
|
|
6174
5785
|
return await ensureSessionWithOwnership(options, cwd);
|
|
6175
5786
|
} finally {
|
|
@@ -6949,7 +6560,12 @@ var LiveSessionCheckpoint = class {
|
|
|
6949
6560
|
async flushDirty() {
|
|
6950
6561
|
while (this.dirty) {
|
|
6951
6562
|
this.dirty = false;
|
|
6952
|
-
|
|
6563
|
+
try {
|
|
6564
|
+
await this.save();
|
|
6565
|
+
} catch (error) {
|
|
6566
|
+
this.dirty = true;
|
|
6567
|
+
throw error;
|
|
6568
|
+
}
|
|
6953
6569
|
}
|
|
6954
6570
|
}
|
|
6955
6571
|
};
|
|
@@ -7003,8 +6619,8 @@ var QueueTaskOutputFormatter = class {
|
|
|
7003
6619
|
flush() {}
|
|
7004
6620
|
};
|
|
7005
6621
|
function markOutputAlreadyEmitted(error, outputAlreadyEmitted) {
|
|
7006
|
-
if (!
|
|
7007
|
-
error.outputAlreadyEmitted =
|
|
6622
|
+
if (!error || typeof error !== "object") return;
|
|
6623
|
+
error.outputAlreadyEmitted = outputAlreadyEmitted;
|
|
7008
6624
|
}
|
|
7009
6625
|
async function runWithOptionalInterrupt(options) {
|
|
7010
6626
|
if (options.handleProcessInterrupts === false) return await options.run();
|
|
@@ -7047,11 +6663,17 @@ var AcpErrorTracker = class {
|
|
|
7047
6663
|
return this.latestInbound ?? this.outbound.findLast((acp) => outboundAcpErrorMatches(error, acp));
|
|
7048
6664
|
}
|
|
7049
6665
|
};
|
|
7050
|
-
function toPromptResult(stopReason, sessionId, client, meta) {
|
|
6666
|
+
function toPromptResult(stopReason, sessionId, client, before, meta) {
|
|
6667
|
+
const after = client.getPermissionStats();
|
|
7051
6668
|
return {
|
|
7052
6669
|
stopReason,
|
|
7053
6670
|
sessionId,
|
|
7054
|
-
permissionStats:
|
|
6671
|
+
permissionStats: {
|
|
6672
|
+
requested: after.requested - before.requested,
|
|
6673
|
+
approved: after.approved - before.approved,
|
|
6674
|
+
denied: after.denied - before.denied,
|
|
6675
|
+
cancelled: after.cancelled - before.cancelled
|
|
6676
|
+
},
|
|
7055
6677
|
...meta === void 0 ? {} : { _meta: meta }
|
|
7056
6678
|
};
|
|
7057
6679
|
}
|
|
@@ -7073,60 +6695,37 @@ async function applyPromptModelIfAdvertised(params) {
|
|
|
7073
6695
|
});
|
|
7074
6696
|
if (result.applied) params.record.acpx = applyModelSelection(params.record.acpx, requestedModel, result.response);
|
|
7075
6697
|
}
|
|
7076
|
-
function jsonRpcIdKey(
|
|
6698
|
+
function jsonRpcIdKey(message) {
|
|
6699
|
+
if (!("id" in message)) return;
|
|
6700
|
+
const value = message.id;
|
|
7077
6701
|
if (typeof value === "string") return `s:${value}`;
|
|
7078
6702
|
if (typeof value === "number" && Number.isFinite(value)) return `n:${value}`;
|
|
7079
6703
|
}
|
|
7080
|
-
function extractJsonRpcRequestInfo(message) {
|
|
7081
|
-
const candidate = message;
|
|
7082
|
-
if (typeof candidate.method !== "string") return;
|
|
7083
|
-
const idKey = jsonRpcIdKey(candidate.id);
|
|
7084
|
-
if (!idKey) return;
|
|
7085
|
-
return {
|
|
7086
|
-
idKey,
|
|
7087
|
-
method: candidate.method
|
|
7088
|
-
};
|
|
7089
|
-
}
|
|
7090
|
-
function extractJsonRpcResponseInfo(message) {
|
|
7091
|
-
const candidate = message;
|
|
7092
|
-
const idKey = jsonRpcIdKey(candidate.id);
|
|
7093
|
-
if (!idKey) return;
|
|
7094
|
-
const hasError = Object.hasOwn(candidate, "error");
|
|
7095
|
-
const hasResult = Object.hasOwn(candidate, "result");
|
|
7096
|
-
if (!hasError && !hasResult) return;
|
|
7097
|
-
return {
|
|
7098
|
-
idKey,
|
|
7099
|
-
hasError
|
|
7100
|
-
};
|
|
7101
|
-
}
|
|
7102
6704
|
const SESSION_RECONNECT_METHODS = /* @__PURE__ */ new Set(["session/load", "session/resume"]);
|
|
7103
|
-
function
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
if (request) {
|
|
7109
|
-
requestMethodById.set(request.idKey, request.method);
|
|
7110
|
-
continue;
|
|
7111
|
-
}
|
|
7112
|
-
const response = extractJsonRpcResponseInfo(message);
|
|
7113
|
-
if (!response || !response.hasError) continue;
|
|
7114
|
-
const requestMethod = requestMethodById.get(response.idKey);
|
|
7115
|
-
if (requestMethod && SESSION_RECONNECT_METHODS.has(requestMethod)) failedLoadRequestIds.add(response.idKey);
|
|
7116
|
-
}
|
|
7117
|
-
if (failedLoadRequestIds.size === 0) return messages;
|
|
7118
|
-
return messages.filter((message) => {
|
|
7119
|
-
const request = extractJsonRpcRequestInfo(message);
|
|
7120
|
-
if (request && SESSION_RECONNECT_METHODS.has(request.method) && failedLoadRequestIds.has(request.idKey)) return false;
|
|
7121
|
-
const response = extractJsonRpcResponseInfo(message);
|
|
7122
|
-
if (response && failedLoadRequestIds.has(response.idKey)) return false;
|
|
7123
|
-
return true;
|
|
7124
|
-
});
|
|
6705
|
+
function isReconnectRequest(direction, message) {
|
|
6706
|
+
return direction === "outbound" && "method" in message && typeof message.method === "string" && SESSION_RECONNECT_METHODS.has(message.method);
|
|
6707
|
+
}
|
|
6708
|
+
function isInboundResponse(direction, message) {
|
|
6709
|
+
return direction === "inbound" && ("result" in message || "error" in message);
|
|
7125
6710
|
}
|
|
7126
6711
|
function filterBufferedConnectOutput(messages, loadError) {
|
|
7127
6712
|
if (loadError == null) return messages;
|
|
7128
|
-
const
|
|
7129
|
-
|
|
6713
|
+
const pending = /* @__PURE__ */ new Map();
|
|
6714
|
+
const suppressed = /* @__PURE__ */ new Set();
|
|
6715
|
+
for (const [index, { direction, message }] of messages.entries()) {
|
|
6716
|
+
const id = jsonRpcIdKey(message);
|
|
6717
|
+
if (!id) continue;
|
|
6718
|
+
if (isReconnectRequest(direction, message)) pending.set(id, index);
|
|
6719
|
+
else if (isInboundResponse(direction, message)) {
|
|
6720
|
+
const requestIndex = pending.get(id);
|
|
6721
|
+
if (requestIndex !== void 0 && "error" in message) {
|
|
6722
|
+
suppressed.add(requestIndex);
|
|
6723
|
+
suppressed.add(index);
|
|
6724
|
+
}
|
|
6725
|
+
pending.delete(id);
|
|
6726
|
+
}
|
|
6727
|
+
}
|
|
6728
|
+
return messages.filter((_message, index) => !suppressed.has(index));
|
|
7130
6729
|
}
|
|
7131
6730
|
function emitPromptRetryNotice(params) {
|
|
7132
6731
|
if (params.suppressSdkConsoleErrors) return;
|
|
@@ -7190,7 +6789,7 @@ function buildQueuedTaskRunOptions(sessionRecordId, task, options, outputFormatt
|
|
|
7190
6789
|
suppressSdkConsoleErrors: task.suppressSdkConsoleErrors ?? options.suppressSdkConsoleErrors,
|
|
7191
6790
|
verbose: options.verbose,
|
|
7192
6791
|
promptRetries: task.promptRetries ?? options.promptRetries ?? 0,
|
|
7193
|
-
sessionOptions:
|
|
6792
|
+
sessionOptions: task.sessionOptions,
|
|
7194
6793
|
onClientAvailable: options.onClientAvailable,
|
|
7195
6794
|
onClientClosed: options.onClientClosed,
|
|
7196
6795
|
onClientCloseFailure: options.onClientCloseFailure,
|
|
@@ -7462,6 +7061,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7462
7061
|
verbose: options.verbose,
|
|
7463
7062
|
sessionOptions
|
|
7464
7063
|
});
|
|
7064
|
+
const permissionStatsBefore = client.getPermissionStats();
|
|
7465
7065
|
client.updateRuntimeOptions({
|
|
7466
7066
|
permissionMode: options.permissionMode,
|
|
7467
7067
|
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
@@ -7540,6 +7140,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7540
7140
|
client,
|
|
7541
7141
|
record,
|
|
7542
7142
|
resumePolicy: options.resumePolicy,
|
|
7143
|
+
sessionOptions: options.sessionOptions,
|
|
7543
7144
|
replacingConfigOption: requestedModelId(options.sessionOptions?.model) ? { key: "model" } : void 0,
|
|
7544
7145
|
timeoutMs: options.timeoutMs,
|
|
7545
7146
|
verbose: options.verbose,
|
|
@@ -7617,7 +7218,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7617
7218
|
applyConversation(record, conversation);
|
|
7618
7219
|
const propagated = error instanceof Error ? error : new Error(formatErrorMessage(error));
|
|
7619
7220
|
attachAcpErrorPayload(propagated, normalizedError.acp);
|
|
7620
|
-
propagated
|
|
7221
|
+
markOutputAlreadyEmitted(propagated, matchedAcpError !== void 0 && shouldMarkAcpErrorsEmitted);
|
|
7621
7222
|
propagated.normalizedOutputError = normalizedError;
|
|
7622
7223
|
throw propagated;
|
|
7623
7224
|
};
|
|
@@ -7670,7 +7271,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7670
7271
|
const response = await savePromptSuccess(await runPromptWithRetries(activeSessionId));
|
|
7671
7272
|
promptTurnActive = false;
|
|
7672
7273
|
return {
|
|
7673
|
-
...toPromptResult(response.stopReason, record.acpxRecordId, client, response._meta),
|
|
7274
|
+
...toPromptResult(response.stopReason, record.acpxRecordId, client, permissionStatsBefore, response._meta),
|
|
7674
7275
|
record,
|
|
7675
7276
|
resumed,
|
|
7676
7277
|
loadError
|
|
@@ -7790,6 +7391,7 @@ async function runOnce(options, control) {
|
|
|
7790
7391
|
},
|
|
7791
7392
|
sessionOptions: options.sessionOptions
|
|
7792
7393
|
});
|
|
7394
|
+
const permissionStatsBefore = client.getPermissionStats();
|
|
7793
7395
|
const closeOwnedClient = ownDirectClient(client, control?.signal);
|
|
7794
7396
|
const runExecPromptAttempt = async (sessionId) => {
|
|
7795
7397
|
assertControlAuthority(authority);
|
|
@@ -7835,17 +7437,17 @@ async function runOnce(options, control) {
|
|
|
7835
7437
|
authority,
|
|
7836
7438
|
onWarning: options.suppressSdkConsoleErrors ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
|
|
7837
7439
|
});
|
|
7838
|
-
if (modelApplication.
|
|
7440
|
+
if (modelApplication.applied) controlState = applyModelSelection(controlState, modelApplication.modelId, modelApplication.response);
|
|
7839
7441
|
for (const configOption of options.configOptions ?? []) {
|
|
7840
7442
|
assertControlAuthority(authority);
|
|
7841
7443
|
const response = await withTimeout(client.setSessionConfigOption(sessionId, configOption.configId, configOption.value, advertisedModelState(controlState), authority), options.timeoutMs);
|
|
7842
|
-
controlState =
|
|
7444
|
+
controlState = applyConfigOptionSelection(controlState, configOption.configId, configOption.value, response);
|
|
7843
7445
|
}
|
|
7844
7446
|
assertControlAuthority(authority);
|
|
7845
7447
|
output.setContext({ sessionId });
|
|
7846
7448
|
const response = await runExecPromptWithRetries(sessionId);
|
|
7847
7449
|
promptTurnActive = false;
|
|
7848
|
-
return toPromptResult(response.stopReason, sessionId, client, response._meta);
|
|
7450
|
+
return toPromptResult(response.stopReason, sessionId, client, permissionStatsBefore, response._meta);
|
|
7849
7451
|
},
|
|
7850
7452
|
handleInterrupt: async () => {
|
|
7851
7453
|
await client.cancelActivePrompt(INTERRUPT_CANCEL_WAIT_MS);
|
|
@@ -8072,31 +7674,50 @@ function createLeaseHeartbeat(lease, shutdown) {
|
|
|
8072
7674
|
};
|
|
8073
7675
|
}
|
|
8074
7676
|
async function runSessionQueueOwner(options) {
|
|
8075
|
-
const
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
let
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
}
|
|
7677
|
+
const signals = [
|
|
7678
|
+
"SIGINT",
|
|
7679
|
+
"SIGTERM",
|
|
7680
|
+
"SIGHUP"
|
|
7681
|
+
];
|
|
7682
|
+
let stopRequested = false;
|
|
7683
|
+
let requestRuntimeStop;
|
|
7684
|
+
const onSignal = () => {
|
|
7685
|
+
stopRequested = true;
|
|
7686
|
+
requestRuntimeStop?.();
|
|
7687
|
+
};
|
|
8086
7688
|
try {
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
7689
|
+
for (const signal of signals) process.on(signal, onSignal);
|
|
7690
|
+
const lease = await tryAcquireQueueOwnerLease(options.sessionId, {
|
|
7691
|
+
path: options.mcpConfigPath,
|
|
7692
|
+
fingerprint: options.mcpConfigFingerprint
|
|
7693
|
+
});
|
|
7694
|
+
if (!lease) return;
|
|
7695
|
+
let runtimeFailure;
|
|
7696
|
+
try {
|
|
7697
|
+
await runQueueOwnerRuntime(options, lease, (requestStop) => {
|
|
7698
|
+
requestRuntimeStop = requestStop;
|
|
7699
|
+
if (stopRequested) requestStop();
|
|
7700
|
+
});
|
|
7701
|
+
} catch (error) {
|
|
7702
|
+
runtimeFailure = { error };
|
|
7703
|
+
}
|
|
7704
|
+
try {
|
|
7705
|
+
await releaseQueueOwnerLease(lease);
|
|
7706
|
+
} catch (error) {
|
|
7707
|
+
if (runtimeFailure) throw new AggregateError([runtimeFailure.error, error], "Queue owner shutdown failed", { cause: error });
|
|
7708
|
+
throw error;
|
|
7709
|
+
}
|
|
7710
|
+
if (runtimeFailure) throw runtimeFailure.error;
|
|
7711
|
+
} finally {
|
|
7712
|
+
for (const signal of signals) process.off(signal, onSignal);
|
|
8091
7713
|
}
|
|
8092
|
-
if (runtimeFailure) throw runtimeFailure.error;
|
|
8093
7714
|
}
|
|
8094
7715
|
async function keepOwnerForPendingControls(controls, shutdown) {
|
|
8095
7716
|
if (shutdown.requested || !controls.hasPending) return false;
|
|
8096
7717
|
await controls.drainAdmitted();
|
|
8097
7718
|
return !shutdown.requested;
|
|
8098
7719
|
}
|
|
8099
|
-
async function runQueueOwnerRuntime(options, lease) {
|
|
7720
|
+
async function runQueueOwnerRuntime(options, lease, bindStopRequest) {
|
|
8100
7721
|
const sessionRecord = await resolveSessionRecord(options.sessionId);
|
|
8101
7722
|
let owner;
|
|
8102
7723
|
let heartbeatTimer;
|
|
@@ -8170,13 +7791,8 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
8170
7791
|
sessionId: options.sessionId,
|
|
8171
7792
|
verbose: options.verbose
|
|
8172
7793
|
});
|
|
8173
|
-
|
|
8174
|
-
shutdown.request();
|
|
8175
|
-
};
|
|
7794
|
+
bindStopRequest(shutdown.request);
|
|
8176
7795
|
const heartbeat = createLeaseHeartbeat(lease, shutdown);
|
|
8177
|
-
process.once("SIGINT", onSignal);
|
|
8178
|
-
process.once("SIGTERM", onSignal);
|
|
8179
|
-
process.once("SIGHUP", onSignal);
|
|
8180
7796
|
try {
|
|
8181
7797
|
owner = await SessionQueueOwner.start(lease, {
|
|
8182
7798
|
cancelPrompt: async () => {
|
|
@@ -8258,9 +7874,6 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
8258
7874
|
}
|
|
8259
7875
|
}
|
|
8260
7876
|
} finally {
|
|
8261
|
-
process.off("SIGINT", onSignal);
|
|
8262
|
-
process.off("SIGTERM", onSignal);
|
|
8263
|
-
process.off("SIGHUP", onSignal);
|
|
8264
7877
|
await shutdown.shutdown();
|
|
8265
7878
|
await lease.updates;
|
|
8266
7879
|
}
|
|
@@ -8304,6 +7917,6 @@ async function sendSession(options) {
|
|
|
8304
7917
|
}
|
|
8305
7918
|
const queueOwnerRuntimeTestInternals = { queueOwnerExitIsFatal };
|
|
8306
7919
|
//#endregion
|
|
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,
|
|
7920
|
+
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, acquireSessionScope as at, setSessionConfigOption as b, LiveSessionCheckpoint as c, InterruptedError as ct, flushPerfMetricsCapture as d, withInterrupt as dt, getAcpxVersion as et, installPerfMetricsCapture as f, withTimeout as ft, listAgentSessions as g, ensureSession as h, runOnce as i, acquireSessionImport as it, applyModelSelection as j, applyConfigOptionsToRecord as k, runPromptTurn as l, TimeoutError as lt, createSessionWithClient as m, runSessionQueueOwner as n, DEFAULT_QUEUE_OWNER_TTL_MS as nt, sendSessionDirect as o, listSessionEvents as ot, createSession as p, trimConversationForRuntime as q, sendSession as r, normalizeQueueOwnerTtlMs as rt, DISCARD_OUTPUT_FORMATTER as s, isLegacyZedCodexAcpInvocation as st, queueOwnerRuntimeTestInternals as t, permissionModeSatisfies as tt, buildQueueOwnerArgOverride as u, assertControlAuthority as ut, closeSession as v, setSessionModel as w, setSessionConfigOptionOnOwner as x, sessionControlTestInternals as y, createInitialSessionRecord as z };
|
|
8308
7921
|
|
|
8309
|
-
//# sourceMappingURL=queue-owner-runtime-
|
|
7922
|
+
//# sourceMappingURL=queue-owner-runtime-Dhr4gxNE.js.map
|