acpx 0.17.1 → 0.19.0
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-evUoJINz.js → agent-registry-Ct2yWPW7.js} +18 -18
- package/dist/agent-registry-Ct2yWPW7.js.map +1 -0
- package/dist/agent-registry.d.ts.map +1 -1
- package/dist/agent-registry.js +1 -1
- package/dist/{cli-B2ycKzaO.js → cli-Bmki9cj5.js} +5 -5
- package/dist/{cli-B2ycKzaO.js.map → cli-Bmki9cj5.js.map} +1 -1
- package/dist/cli.js +167 -131
- package/dist/cli.js.map +1 -1
- package/dist/{client-CkbBoSGd.d.ts → client-DzaUKazo.d.ts} +23 -15
- package/dist/client-DzaUKazo.d.ts.map +1 -0
- package/dist/{flows-xGyhmoHO.js → flows--Ewxn_fQ.js} +665 -460
- package/dist/flows--Ewxn_fQ.js.map +1 -0
- package/dist/flows.d.ts +19 -7
- package/dist/flows.d.ts.map +1 -1
- package/dist/flows.js +1 -1
- package/dist/{invocation-options-eZEBh4vd.js → invocation-options-DRzxHZmr.js} +34 -12
- package/dist/invocation-options-DRzxHZmr.js.map +1 -0
- package/dist/{ipc-D2rstexT.js → ipc-fMgj5vrO.js} +818 -302
- package/dist/ipc-fMgj5vrO.js.map +1 -0
- package/dist/{output-D2xL6wdp.js → output-D6kDs8BN.js} +30 -14
- package/dist/output-D6kDs8BN.js.map +1 -0
- package/dist/{queue-owner-runtime-Dgv3e2Tp.js → queue-owner-runtime-CLwjMYNL.js} +1448 -717
- package/dist/queue-owner-runtime-CLwjMYNL.js.map +1 -0
- package/dist/runtime.d.ts +57 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +303 -166
- package/dist/runtime.js.map +1 -1
- package/dist/session-options-Dp6mzNSy.d.ts.map +1 -1
- package/dist/{session-watch-CTe9Bnbc.js → session-watch-BfdIo-n4.js} +5 -5
- package/dist/session-watch-BfdIo-n4.js.map +1 -0
- package/dist/{watch-DxuXEc8n.js → watch-DH-zXQH2.js} +2 -2
- package/dist/{watch-DxuXEc8n.js.map → watch-DH-zXQH2.js.map} +1 -1
- package/package.json +1 -1
- package/skills/acpx/SKILL.md +69 -30
- package/dist/agent-registry-evUoJINz.js.map +0 -1
- package/dist/client-CkbBoSGd.d.ts.map +0 -1
- package/dist/flows-xGyhmoHO.js.map +0 -1
- package/dist/invocation-options-eZEBh4vd.js.map +0 -1
- package/dist/ipc-D2rstexT.js.map +0 -1
- package/dist/output-D2xL6wdp.js.map +0 -1
- package/dist/queue-owner-runtime-Dgv3e2Tp.js.map +0 -1
- package/dist/session-watch-CTe9Bnbc.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { A as resolveAgentCommandParts, C as asAbsoluteCwd, E as isoNow$2, F as waitForSpawn, M as runTimedExecFile, N as splitCommandLine, P as waitForChildExit, S as PROCESS_HELPER_TIMEOUT_MS, T as isChildProcessRunning, _ as buildSpawnCommandOptions, b as readWindowsEnvValue, f as resolveBuiltInAgentLaunch, g as buildAgentSpawnCommand, j as resolveAgentSessionCwd, k as requireAgentStdio, v as buildTerminalShellSpawnCommand, w as basenameToken, x as resolveWindowsExecutablePath, y as buildTerminalSpawnCommand } from "./agent-registry-
|
|
1
|
+
import { $ as measurePerf, A as findGitRepositoryRoot, B as writeSessionRecord, Bt as PermissionDeniedError, C as getUnsupportedPromptContentMessage, D as textPrompt, Dt as OUTPUT_ERROR_ORIGINS, E as promptToDisplayText, Et as OUTPUT_ERROR_CODES, Ft as AgentStartupError, G as normalizeAgentSessionId, Gt as SessionModelReplayError, Ht as QueueConnectionError, I as normalizeName, It as AuthPolicyError, Jt as UnsupportedPromptContentError, K as SESSION_JOURNAL_SCHEMA, Lt as ClaudeAcpSessionCreateTimeoutError, M as findSessionByDirectoryWalk, Mt as AcpxOperationalError, N as isoNow$1, Nt as AgentDisconnectedError, Pt as AgentSpawnError, Q as incrementPerfCounter, Rt as CopilotAcpUnsupportedError, St as isAcpResourceNotFoundError, Ut as SessionConfigOptionReplayError, Vt as PermissionPromptUnavailableError, W as extractAgentSessionId, Wt as SessionModeReplayError, X as formatPerfMetric, Z as getPerfMetricsSnapshot, _ as waitMs$1, a as trySetModelOnRunningOwner, at as isAcpJsonRpcMessage, b as lockOwnerPid, bt as extractAcpError, c as queueRequestExceedsLimit, et as recordPerfDuration, f as refreshQueueOwnerLease, ft as defaultSessionEventLog, g as tryAcquireQueueOwnerLease, gt as sessionEventSegmentPath, h as terminateQueueOwnerForSession, ht as sessionEventLockPath, i as trySetModeOnRunningOwner, k as absolutePath, l as parseQueueRequest, m as terminateProcess, mt as sessionEventActivePath, n as tryCloseSessionOnRunningOwner, nt as setPerfGauge, o as trySubmitToRunningOwner, ot as isAcpMessageObject, p as releaseQueueOwnerLease, pt as sessionBaseDir, q as SessionJournalReader, qt as SessionResumeRequiredError, r as trySetConfigOptionOnRunningOwner, rt as startPerfTimer, s as queueRequestByteLimit, st as isSessionUpdateNotification, t as tryCancelOnRunningOwner, tt as resetPerfMetrics, v as QueueLeaseGuardSettlementError, wt as EXIT_CODES, x as readProcessTable, xt as formatUnknownErrorMessage, y as createLockOwner, yt as isProcessAlive, z as resolveSessionRecord, zt as GeminiAcpStartupTimeoutError } from "./ipc-fMgj5vrO.js";
|
|
2
|
+
import { A as resolveAgentCommandParts, C as asAbsoluteCwd, E as isoNow$2, F as waitForSpawn, M as runTimedExecFile, N as splitCommandLine, P as waitForChildExit, S as PROCESS_HELPER_TIMEOUT_MS, T as isChildProcessRunning, _ as buildSpawnCommandOptions, b as readWindowsEnvValue, f as resolveBuiltInAgentLaunch, g as buildAgentSpawnCommand, j as resolveAgentSessionCwd, k as requireAgentStdio, v as buildTerminalShellSpawnCommand, w as basenameToken, x as resolveWindowsExecutablePath, y as buildTerminalSpawnCommand } from "./agent-registry-Ct2yWPW7.js";
|
|
3
3
|
import fs, { readFileSync, realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import path from "node:path";
|
|
@@ -15,6 +15,7 @@ import { isPathInside } from "@openclaw/fs-safe/path";
|
|
|
15
15
|
import { root } from "@openclaw/fs-safe/root";
|
|
16
16
|
import readline from "node:readline/promises";
|
|
17
17
|
import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
|
|
18
|
+
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
18
19
|
import net from "node:net";
|
|
19
20
|
//#region src/acp/error-normalization.ts
|
|
20
21
|
const AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
|
|
@@ -484,22 +485,69 @@ function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
|
484
485
|
return Math.round(ttlMs);
|
|
485
486
|
}
|
|
486
487
|
//#endregion
|
|
488
|
+
//#region src/file-read-window.ts
|
|
489
|
+
function sliceReadWindow(content, line, limit) {
|
|
490
|
+
if (line == null && limit == null) return content;
|
|
491
|
+
const lines = content.split("\n");
|
|
492
|
+
const startIndex = Math.max(0, (line == null ? 1 : Math.max(1, Math.trunc(line))) - 1);
|
|
493
|
+
const maxLines = limit == null ? void 0 : Math.max(0, Math.trunc(limit));
|
|
494
|
+
if (maxLines === 0) return "";
|
|
495
|
+
const endIndex = maxLines == null ? lines.length : Math.min(lines.length, startIndex + maxLines);
|
|
496
|
+
return lines.slice(startIndex, endIndex).join("\n");
|
|
497
|
+
}
|
|
498
|
+
//#endregion
|
|
487
499
|
//#region src/permission-prompt.ts
|
|
500
|
+
let promptQueue = Promise.resolve();
|
|
488
501
|
async function promptForPermission(options) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
502
|
+
options.signal?.throwIfAborted();
|
|
503
|
+
const result = promptQueue.then(() => askPermission(options));
|
|
504
|
+
promptQueue = result.then(() => {}, () => {});
|
|
505
|
+
const signal = options.signal;
|
|
506
|
+
if (!signal) return await result;
|
|
507
|
+
let onAbort = () => {};
|
|
508
|
+
const aborted = new Promise((_, reject) => {
|
|
509
|
+
onAbort = () => reject(signal.reason);
|
|
510
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
511
|
+
});
|
|
512
|
+
try {
|
|
513
|
+
return await Promise.race([result, aborted]);
|
|
514
|
+
} finally {
|
|
515
|
+
signal.removeEventListener("abort", onAbort);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async function askPermission(options) {
|
|
519
|
+
options.signal?.throwIfAborted();
|
|
520
|
+
if (!canPrompt()) return false;
|
|
521
|
+
writePromptDetails(options);
|
|
492
522
|
const rl = readline.createInterface({
|
|
493
523
|
input: process.stdin,
|
|
494
524
|
output: process.stderr
|
|
495
525
|
});
|
|
526
|
+
const questionController = new AbortController();
|
|
527
|
+
const signal = options.signal ? AbortSignal.any([options.signal, questionController.signal]) : questionController.signal;
|
|
528
|
+
let onClose = () => {};
|
|
529
|
+
const closed = new Promise((resolve) => {
|
|
530
|
+
onClose = () => resolve(void 0);
|
|
531
|
+
rl.once("close", onClose);
|
|
532
|
+
});
|
|
496
533
|
try {
|
|
497
|
-
const
|
|
534
|
+
const answer = await Promise.race([rl.question(options.prompt, { signal }), closed]);
|
|
535
|
+
options.signal?.throwIfAborted();
|
|
536
|
+
const normalized = answer?.trim().toLowerCase();
|
|
498
537
|
return normalized === "y" || normalized === "yes";
|
|
499
538
|
} finally {
|
|
539
|
+
rl.removeListener("close", onClose);
|
|
540
|
+
questionController.abort();
|
|
500
541
|
rl.close();
|
|
501
542
|
}
|
|
502
543
|
}
|
|
544
|
+
function canPrompt() {
|
|
545
|
+
return process.stdin.isTTY && process.stderr.isTTY && !process.stdin.readableEnded && !process.stdin.destroyed;
|
|
546
|
+
}
|
|
547
|
+
function writePromptDetails(options) {
|
|
548
|
+
if (options.header) process.stderr.write(`\n${options.header}\n`);
|
|
549
|
+
if (options.details && options.details.trim().length > 0) process.stderr.write(`${options.details}\n`);
|
|
550
|
+
}
|
|
503
551
|
//#endregion
|
|
504
552
|
//#region src/filesystem.ts
|
|
505
553
|
const WRITE_PREVIEW_MAX_LINES = 16;
|
|
@@ -515,11 +563,12 @@ function toWritePreview(content) {
|
|
|
515
563
|
if (preview.length > WRITE_PREVIEW_MAX_CHARS) preview = `${preview.slice(0, 1197)}...`;
|
|
516
564
|
return preview;
|
|
517
565
|
}
|
|
518
|
-
async function defaultConfirmWrite(filePath, preview) {
|
|
566
|
+
async function defaultConfirmWrite(filePath, preview, signal) {
|
|
519
567
|
return await promptForPermission({
|
|
520
568
|
header: `[permission] Allow write to ${filePath}?`,
|
|
521
569
|
details: preview,
|
|
522
|
-
prompt: "Allow write? (y/N) "
|
|
570
|
+
prompt: "Allow write? (y/N) ",
|
|
571
|
+
signal
|
|
523
572
|
});
|
|
524
573
|
}
|
|
525
574
|
function canPromptForPermission$2() {
|
|
@@ -545,7 +594,8 @@ var FileSystemHandlers = class {
|
|
|
545
594
|
this.permissionMode = permissionMode;
|
|
546
595
|
this.nonInteractivePermissions = nonInteractivePermissions ?? "deny";
|
|
547
596
|
}
|
|
548
|
-
async readTextFile(params) {
|
|
597
|
+
async readTextFile(params, authority) {
|
|
598
|
+
assertControlAuthority(authority);
|
|
549
599
|
const filePath = this.resolvePathWithinRoot(params.path);
|
|
550
600
|
const summary = `read_text_file: ${filePath}`;
|
|
551
601
|
this.emitOperation({
|
|
@@ -557,8 +607,9 @@ var FileSystemHandlers = class {
|
|
|
557
607
|
});
|
|
558
608
|
try {
|
|
559
609
|
if (this.permissionMode === "deny-all") throw new PermissionDeniedError("Permission denied for fs/read_text_file (--deny-all)");
|
|
560
|
-
const content = await (await this.getWorkspace()).readText(
|
|
561
|
-
|
|
610
|
+
const content = await (await this.getWorkspace()).readText(filePath);
|
|
611
|
+
assertControlAuthority(authority);
|
|
612
|
+
const sliced = sliceReadWindow(content, params.line, params.limit);
|
|
562
613
|
this.emitOperation({
|
|
563
614
|
method: "fs/read_text_file",
|
|
564
615
|
status: "completed",
|
|
@@ -579,7 +630,8 @@ var FileSystemHandlers = class {
|
|
|
579
630
|
throw error;
|
|
580
631
|
}
|
|
581
632
|
}
|
|
582
|
-
async writeTextFile(params) {
|
|
633
|
+
async writeTextFile(params, authority) {
|
|
634
|
+
assertControlAuthority(authority);
|
|
583
635
|
const filePath = this.resolvePathWithinRoot(params.path);
|
|
584
636
|
const preview = toWritePreview(params.content);
|
|
585
637
|
const summary = `write_text_file: ${filePath}`;
|
|
@@ -591,9 +643,17 @@ var FileSystemHandlers = class {
|
|
|
591
643
|
timestamp: nowIso$1()
|
|
592
644
|
});
|
|
593
645
|
try {
|
|
594
|
-
|
|
595
|
-
|
|
646
|
+
const approved = await this.isWriteApproved(filePath, preview, authority?.signal);
|
|
647
|
+
assertControlAuthority(authority);
|
|
648
|
+
if (!approved) throw new PermissionDeniedError("Permission denied for fs/write_text_file");
|
|
649
|
+
const workspace = await this.getWorkspace();
|
|
650
|
+
const target = await workspace.resolve(filePath);
|
|
651
|
+
const file = await workspace.openWritable(target, {
|
|
652
|
+
mode: 438,
|
|
653
|
+
assertBeforeMutation: () => assertControlAuthority(authority)
|
|
654
|
+
});
|
|
596
655
|
try {
|
|
656
|
+
assertControlAuthority(authority);
|
|
597
657
|
await file.handle.writeFile(params.content, "utf8");
|
|
598
658
|
} finally {
|
|
599
659
|
await file.handle.close();
|
|
@@ -618,17 +678,17 @@ var FileSystemHandlers = class {
|
|
|
618
678
|
throw error;
|
|
619
679
|
}
|
|
620
680
|
}
|
|
621
|
-
async isWriteApproved(filePath, preview) {
|
|
681
|
+
async isWriteApproved(filePath, preview, signal) {
|
|
622
682
|
if (this.permissionMode === "approve-all") return true;
|
|
623
683
|
if (this.permissionMode === "deny-all") return false;
|
|
624
684
|
if (this.usesDefaultConfirmWrite && this.nonInteractivePermissions === "fail" && !canPromptForPermission$2()) throw new PermissionPromptUnavailableError();
|
|
625
|
-
return await this.confirmWrite(filePath, preview);
|
|
685
|
+
return await this.confirmWrite(filePath, preview, signal);
|
|
626
686
|
}
|
|
627
687
|
resolvePathWithinRoot(rawPath) {
|
|
628
688
|
if (!path.isAbsolute(rawPath)) throw new Error(`Path must be absolute: ${rawPath}`);
|
|
629
689
|
const resolved = path.resolve(rawPath);
|
|
630
690
|
if (!isPathInside(this.rootDir, resolved)) throw new Error(`Path is outside allowed cwd subtree: ${resolved}`);
|
|
631
|
-
return
|
|
691
|
+
return rawPath;
|
|
632
692
|
}
|
|
633
693
|
getWorkspace() {
|
|
634
694
|
return this.workspace ??= root(this.rootDir, {
|
|
@@ -637,15 +697,6 @@ var FileSystemHandlers = class {
|
|
|
637
697
|
maxBytes: Infinity
|
|
638
698
|
});
|
|
639
699
|
}
|
|
640
|
-
sliceContent(content, line, limit) {
|
|
641
|
-
if (line == null && limit == null) return content;
|
|
642
|
-
const lines = content.split("\n");
|
|
643
|
-
const startIndex = Math.max(0, (line == null ? 1 : Math.max(1, Math.trunc(line))) - 1);
|
|
644
|
-
const maxLines = limit == null ? void 0 : Math.max(0, Math.trunc(limit));
|
|
645
|
-
if (maxLines === 0) return "";
|
|
646
|
-
const endIndex = maxLines == null ? lines.length : Math.min(lines.length, startIndex + maxLines);
|
|
647
|
-
return lines.slice(startIndex, endIndex).join("\n");
|
|
648
|
-
}
|
|
649
700
|
readWindowDetails(line, limit) {
|
|
650
701
|
if (line == null && limit == null) return;
|
|
651
702
|
return `line=${line == null ? 1 : Math.max(1, Math.trunc(line))}, limit=${limit == null ? "all" : Math.max(0, Math.trunc(limit))}`;
|
|
@@ -691,11 +742,11 @@ function pickOption(options, kinds) {
|
|
|
691
742
|
const TOOL_KIND_TITLE_MATCHERS = [
|
|
692
743
|
{
|
|
693
744
|
kind: "read",
|
|
694
|
-
|
|
745
|
+
names: ["read", "cat"]
|
|
695
746
|
},
|
|
696
747
|
{
|
|
697
748
|
kind: "search",
|
|
698
|
-
|
|
749
|
+
names: [
|
|
699
750
|
"search",
|
|
700
751
|
"find",
|
|
701
752
|
"grep"
|
|
@@ -703,7 +754,7 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
703
754
|
},
|
|
704
755
|
{
|
|
705
756
|
kind: "edit",
|
|
706
|
-
|
|
757
|
+
names: [
|
|
707
758
|
"write",
|
|
708
759
|
"edit",
|
|
709
760
|
"patch"
|
|
@@ -711,15 +762,15 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
711
762
|
},
|
|
712
763
|
{
|
|
713
764
|
kind: "delete",
|
|
714
|
-
|
|
765
|
+
names: ["delete", "remove"]
|
|
715
766
|
},
|
|
716
767
|
{
|
|
717
768
|
kind: "move",
|
|
718
|
-
|
|
769
|
+
names: ["move", "rename"]
|
|
719
770
|
},
|
|
720
771
|
{
|
|
721
772
|
kind: "execute",
|
|
722
|
-
|
|
773
|
+
names: [
|
|
723
774
|
"run",
|
|
724
775
|
"execute",
|
|
725
776
|
"bash"
|
|
@@ -727,7 +778,7 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
727
778
|
},
|
|
728
779
|
{
|
|
729
780
|
kind: "fetch",
|
|
730
|
-
|
|
781
|
+
names: [
|
|
731
782
|
"fetch",
|
|
732
783
|
"http",
|
|
733
784
|
"url"
|
|
@@ -735,25 +786,25 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
735
786
|
},
|
|
736
787
|
{
|
|
737
788
|
kind: "think",
|
|
738
|
-
|
|
789
|
+
names: ["think"]
|
|
739
790
|
}
|
|
740
791
|
];
|
|
741
792
|
function inferToolKind(params) {
|
|
742
793
|
if (params.toolCall.kind) return params.toolCall.kind;
|
|
743
794
|
const title = params.toolCall.title?.trim().toLowerCase();
|
|
744
795
|
if (!title) return;
|
|
745
|
-
const head = title.split(
|
|
796
|
+
const head = title.split(/[:\s]/, 1)[0];
|
|
746
797
|
if (!head) return;
|
|
747
|
-
return
|
|
748
|
-
}
|
|
749
|
-
function titleHeadToolKind(head) {
|
|
750
|
-
return TOOL_KIND_TITLE_MATCHERS.find(({ needles }) => needles.some((needle) => head.includes(needle)))?.kind;
|
|
798
|
+
return TOOL_KIND_TITLE_MATCHERS.find(({ names }) => names.includes(head))?.kind ?? "other";
|
|
751
799
|
}
|
|
752
800
|
function isAutoApprovedReadKind(kind) {
|
|
753
801
|
return kind === "read" || kind === "search";
|
|
754
802
|
}
|
|
755
|
-
async function promptForToolPermission(params) {
|
|
756
|
-
return await promptForPermission({
|
|
803
|
+
async function promptForToolPermission(params, signal) {
|
|
804
|
+
return await promptForPermission({
|
|
805
|
+
prompt: `\n[permission] Allow ${params.toolCall.title ?? "tool"} [${inferToolKind(params) ?? "other"}]? (y/N) `,
|
|
806
|
+
signal
|
|
807
|
+
});
|
|
757
808
|
}
|
|
758
809
|
function canPromptForPermission$1() {
|
|
759
810
|
return process.stdin.isTTY && process.stderr.isTTY;
|
|
@@ -848,24 +899,24 @@ function selectedOrFirst(options, allowOption) {
|
|
|
848
899
|
function selectedOrCancelled(option) {
|
|
849
900
|
return { response: option ? selected(option.optionId) : cancelled() };
|
|
850
901
|
}
|
|
851
|
-
async function resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption) {
|
|
852
|
-
if (canPromptForPermission$1()) return resolveInteractivePromptResult(params, allowOption, rejectOption);
|
|
902
|
+
async function resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption, signal) {
|
|
903
|
+
if (canPromptForPermission$1()) return resolveInteractivePromptResult(params, allowOption, rejectOption, signal);
|
|
853
904
|
const escalation = buildEscalationEvent(params, policyMatch.matchedRule);
|
|
854
905
|
return {
|
|
855
906
|
response: withPermissionMetadata(rejectOption ? selected(rejectOption.optionId) : cancelled(), { permissionEscalation: escalation }),
|
|
856
907
|
escalation
|
|
857
908
|
};
|
|
858
909
|
}
|
|
859
|
-
async function resolveInteractivePromptResult(params, allowOption, rejectOption) {
|
|
860
|
-
const approved = await promptForToolPermission(params);
|
|
910
|
+
async function resolveInteractivePromptResult(params, allowOption, rejectOption, signal) {
|
|
911
|
+
const approved = await promptForToolPermission(params, signal);
|
|
861
912
|
if (approved && allowOption) return { response: selected(allowOption.optionId) };
|
|
862
913
|
if (!approved && rejectOption) return { response: selected(rejectOption.optionId) };
|
|
863
914
|
return { response: cancelled() };
|
|
864
915
|
}
|
|
865
|
-
function resolvePolicyMatch(params, policyMatch, options, allowOption, rejectOption) {
|
|
916
|
+
function resolvePolicyMatch(params, policyMatch, options, allowOption, rejectOption, signal) {
|
|
866
917
|
if (policyMatch?.action === "approve") return selectedOrFirst(options, allowOption);
|
|
867
918
|
if (policyMatch?.action === "deny") return selectedOrCancelled(rejectOption);
|
|
868
|
-
if (policyMatch?.action === "escalate") return resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption);
|
|
919
|
+
if (policyMatch?.action === "escalate") return resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption, signal);
|
|
869
920
|
}
|
|
870
921
|
function resolveModeMatch(options, mode, allowOption, rejectOption) {
|
|
871
922
|
if (mode === "approve-all") return selectedOrFirst(options, allowOption);
|
|
@@ -875,24 +926,35 @@ function resolveNonInteractivePermission(nonInteractivePolicy, rejectOption) {
|
|
|
875
926
|
if (nonInteractivePolicy === "fail") throw new PermissionPromptUnavailableError();
|
|
876
927
|
return selectedOrCancelled(rejectOption);
|
|
877
928
|
}
|
|
878
|
-
async function resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption) {
|
|
929
|
+
async function resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption, signal) {
|
|
879
930
|
if (isAutoApprovedReadKind(inferToolKind(params)) && allowOption) return { response: selected(allowOption.optionId) };
|
|
880
931
|
if (!canPromptForPermission$1()) return resolveNonInteractivePermission(nonInteractivePolicy, rejectOption);
|
|
881
|
-
return resolveInteractivePromptResult(params, allowOption, rejectOption);
|
|
932
|
+
return resolveInteractivePromptResult(params, allowOption, rejectOption, signal);
|
|
882
933
|
}
|
|
883
934
|
function permissionModeSatisfies(actual, required) {
|
|
884
935
|
return PERMISSION_MODE_RANK[actual] >= PERMISSION_MODE_RANK[required];
|
|
885
936
|
}
|
|
886
|
-
async function resolvePermissionRequestWithDetails(params, mode, nonInteractivePolicy = "deny", policy) {
|
|
937
|
+
async function resolvePermissionRequestWithDetails(params, mode, nonInteractivePolicy = "deny", policy, signal) {
|
|
938
|
+
try {
|
|
939
|
+
signal?.throwIfAborted();
|
|
940
|
+
const result = await resolvePermissionByPolicy(params, mode, nonInteractivePolicy, policy, signal);
|
|
941
|
+
signal?.throwIfAborted();
|
|
942
|
+
return result;
|
|
943
|
+
} catch (error) {
|
|
944
|
+
if (signal?.aborted) return { response: cancelled() };
|
|
945
|
+
throw error;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
async function resolvePermissionByPolicy(params, mode, nonInteractivePolicy, policy, signal) {
|
|
887
949
|
const options = params.options ?? [];
|
|
888
950
|
if (options.length === 0) return { response: cancelled() };
|
|
889
951
|
const allowOption = pickOption(options, ["allow_once", "allow_always"]);
|
|
890
952
|
const rejectOption = pickOption(options, ["reject_once", "reject_always"]);
|
|
891
|
-
const resolvedByPolicy = await resolvePolicyMatch(params, matchPermissionPolicy(params, policy), options, allowOption, rejectOption);
|
|
953
|
+
const resolvedByPolicy = await resolvePolicyMatch(params, matchPermissionPolicy(params, policy), options, allowOption, rejectOption, signal);
|
|
892
954
|
if (resolvedByPolicy) return resolvedByPolicy;
|
|
893
955
|
const resolvedByMode = resolveModeMatch(options, mode, allowOption, rejectOption);
|
|
894
956
|
if (resolvedByMode) return resolvedByMode;
|
|
895
|
-
return resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption);
|
|
957
|
+
return resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption, signal);
|
|
896
958
|
}
|
|
897
959
|
const DECISION_FALLBACK_ORDER = {
|
|
898
960
|
allow_once: ["allow_once", "allow_always"],
|
|
@@ -1321,10 +1383,12 @@ const REQUESTED_MODEL_UNSUPPORTED_REASONS = ["missing-capability", "unadvertised
|
|
|
1321
1383
|
var RequestedModelUnsupportedError = class extends Error {
|
|
1322
1384
|
code = REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE;
|
|
1323
1385
|
reason;
|
|
1324
|
-
|
|
1386
|
+
ambiguous;
|
|
1387
|
+
constructor(message, reason, details) {
|
|
1325
1388
|
super(message);
|
|
1326
1389
|
this.name = "RequestedModelUnsupportedError";
|
|
1327
1390
|
this.reason = reason;
|
|
1391
|
+
if (details?.ambiguous) this.ambiguous = true;
|
|
1328
1392
|
}
|
|
1329
1393
|
};
|
|
1330
1394
|
function isRequestedModelUnsupportedReason(value) {
|
|
@@ -1427,6 +1491,7 @@ function resolveRequestedModelId(params) {
|
|
|
1427
1491
|
if (!params.models || !isCursorAcpCommandForModelAlias(params.agentCommand)) return params.requestedModel;
|
|
1428
1492
|
if (params.models.availableModels.some((model) => model.modelId === params.requestedModel)) return params.requestedModel;
|
|
1429
1493
|
const candidates = params.models.availableModels.map((model) => model.modelId).filter((modelId) => modelId.startsWith(`${params.requestedModel}[`));
|
|
1494
|
+
if (candidates.length > 1) throw new RequestedModelUnsupportedError(`Cannot select model "${params.requestedModel}": multiple advertised Cursor models match (${candidates.join(", ")}). Use an exact advertised model ID.`, "unadvertised-model", { ambiguous: true });
|
|
1430
1495
|
return candidates.length === 1 ? candidates[0] : params.requestedModel;
|
|
1431
1496
|
}
|
|
1432
1497
|
function isCursorAcpCommandForModelAlias(agentCommand) {
|
|
@@ -1493,15 +1558,22 @@ function enqueueNdJsonLine(agentCommand, line, controller) {
|
|
|
1493
1558
|
console.error("Failed to parse JSON message:", trimmedLine, err);
|
|
1494
1559
|
}
|
|
1495
1560
|
}
|
|
1496
|
-
function
|
|
1561
|
+
function enqueueNdJsonChunk(agentCommand, chunk, fragments, controller) {
|
|
1562
|
+
const lines = chunk.split("\n");
|
|
1563
|
+
const suffix = lines.pop() || "";
|
|
1564
|
+
if (lines.length > 0 && fragments.length > 0) {
|
|
1565
|
+
lines[0] = fragments.join("") + lines[0];
|
|
1566
|
+
fragments.length = 0;
|
|
1567
|
+
}
|
|
1497
1568
|
for (const line of lines) enqueueNdJsonLine(agentCommand, line, controller);
|
|
1569
|
+
if (suffix) fragments.push(suffix);
|
|
1498
1570
|
}
|
|
1499
1571
|
function createNdJsonMessageStream(agentCommand, output, input, maxMessageBytes, onReadError) {
|
|
1500
1572
|
const textEncoder = new TextEncoder();
|
|
1501
1573
|
const textDecoder = new TextDecoder();
|
|
1502
1574
|
return {
|
|
1503
1575
|
readable: new ReadableStream({ async start(controller) {
|
|
1504
|
-
|
|
1576
|
+
const fragments = [];
|
|
1505
1577
|
let retainedBytes = 0;
|
|
1506
1578
|
const reader = input.getReader();
|
|
1507
1579
|
try {
|
|
@@ -1509,10 +1581,7 @@ function createNdJsonMessageStream(agentCommand, output, input, maxMessageBytes,
|
|
|
1509
1581
|
const { value, done } = await reader.read();
|
|
1510
1582
|
if (done) break;
|
|
1511
1583
|
if (maxMessageBytes !== void 0) retainedBytes = countLineBytes(value, retainedBytes, maxMessageBytes);
|
|
1512
|
-
|
|
1513
|
-
const lines = content.split("\n");
|
|
1514
|
-
content = lines.pop() || "";
|
|
1515
|
-
enqueueNdJsonLines(agentCommand, lines, controller);
|
|
1584
|
+
enqueueNdJsonChunk(agentCommand, textDecoder.decode(value, { stream: true }), fragments, controller);
|
|
1516
1585
|
}
|
|
1517
1586
|
controller.close();
|
|
1518
1587
|
} catch (err) {
|
|
@@ -1604,65 +1673,88 @@ function promptRequestOwner(message) {
|
|
|
1604
1673
|
}
|
|
1605
1674
|
//#endregion
|
|
1606
1675
|
//#region src/acp/process-descendants.ts
|
|
1607
|
-
function parseProcessTable(output) {
|
|
1608
|
-
const table = /* @__PURE__ */ new Map();
|
|
1609
|
-
for (const line of output.split("\n")) {
|
|
1610
|
-
const match = /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.+?)\s*$/.exec(line);
|
|
1611
|
-
if (match && !match[3].startsWith("Z")) {
|
|
1612
|
-
const pid = Number(match[1]);
|
|
1613
|
-
if (Number.isSafeInteger(pid) && pid > 1 && pid !== process.pid) table.set(pid, {
|
|
1614
|
-
pid,
|
|
1615
|
-
parentPid: Number(match[2]),
|
|
1616
|
-
birth: match[4]
|
|
1617
|
-
});
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
return table;
|
|
1621
|
-
}
|
|
1622
1676
|
function includeDescendants(table, owned) {
|
|
1623
1677
|
let expanded;
|
|
1624
1678
|
do {
|
|
1625
1679
|
expanded = false;
|
|
1626
|
-
for (const identity of table.values())
|
|
1627
|
-
|
|
1628
|
-
|
|
1680
|
+
for (const identity of table.values()) {
|
|
1681
|
+
const parent = table.get(identity.parentPid);
|
|
1682
|
+
if (process.platform === "win32" && (!parent || parent.birth > identity.birth)) continue;
|
|
1683
|
+
if (owned.has(identity.parentPid) && !owned.has(identity.pid)) {
|
|
1684
|
+
owned.add(identity.pid);
|
|
1685
|
+
expanded = true;
|
|
1686
|
+
}
|
|
1629
1687
|
}
|
|
1630
1688
|
} while (expanded);
|
|
1631
1689
|
}
|
|
1632
|
-
/** Best-effort
|
|
1690
|
+
/** Best-effort cleanup for descendants witnessed during this child launch. */
|
|
1633
1691
|
var ProcessDescendants = class {
|
|
1634
1692
|
child;
|
|
1635
1693
|
identities = /* @__PURE__ */ new Map();
|
|
1694
|
+
rootBirth;
|
|
1636
1695
|
pending;
|
|
1637
1696
|
retired = false;
|
|
1638
|
-
|
|
1697
|
+
ownProcessGroup;
|
|
1698
|
+
captureGroupAfterExit;
|
|
1699
|
+
groupExitedAt;
|
|
1700
|
+
onRootExit = () => {
|
|
1701
|
+
this.groupExitedAt = Date.now();
|
|
1702
|
+
};
|
|
1703
|
+
constructor(child, options = {}) {
|
|
1639
1704
|
this.child = child;
|
|
1705
|
+
this.ownProcessGroup = process.platform !== "win32" && Boolean(options.ownProcessGroup);
|
|
1706
|
+
this.captureGroupAfterExit = this.ownProcessGroup && isChildProcessRunning(child);
|
|
1707
|
+
if (this.ownProcessGroup) child.once("exit", this.onRootExit);
|
|
1640
1708
|
}
|
|
1641
1709
|
capture(timeoutMs = PROCESS_HELPER_TIMEOUT_MS) {
|
|
1642
|
-
if (this.retired
|
|
1710
|
+
if (this.retired) return Promise.resolve(true);
|
|
1643
1711
|
this.pending ??= this.readSnapshot(timeoutMs).finally(() => {
|
|
1644
1712
|
this.pending = void 0;
|
|
1645
1713
|
});
|
|
1646
1714
|
return this.pending;
|
|
1647
1715
|
}
|
|
1648
1716
|
async readSnapshot(timeoutMs) {
|
|
1717
|
+
const deadline = performance.now() + timeoutMs;
|
|
1718
|
+
const rootWasRunning = isChildProcessRunning(this.child);
|
|
1719
|
+
const captureRootGroup = rootWasRunning || this.captureGroupAfterExit;
|
|
1720
|
+
if (!rootWasRunning) this.captureGroupAfterExit = false;
|
|
1649
1721
|
try {
|
|
1650
|
-
const
|
|
1651
|
-
if (!this.retired)
|
|
1722
|
+
const table = await readProcessTable(timeoutMs);
|
|
1723
|
+
if (!this.retired) {
|
|
1724
|
+
table.delete(1);
|
|
1725
|
+
table.delete(process.pid);
|
|
1726
|
+
this.refresh(table, captureRootGroup);
|
|
1727
|
+
if (this.captureGroupAfterExit && !isChildProcessRunning(this.child)) return await this.readSnapshot(Math.max(1, deadline - performance.now()));
|
|
1728
|
+
}
|
|
1652
1729
|
return true;
|
|
1653
1730
|
} catch {
|
|
1731
|
+
if (!isChildProcessRunning(this.child)) this.captureGroupAfterExit = false;
|
|
1654
1732
|
return false;
|
|
1655
1733
|
}
|
|
1656
1734
|
}
|
|
1657
|
-
refresh(table) {
|
|
1735
|
+
refresh(table, rootWasRunning) {
|
|
1658
1736
|
const owned = /* @__PURE__ */ new Set();
|
|
1659
1737
|
for (const [pid, identity] of this.identities) if (table.get(pid)?.birth === identity.birth) owned.add(pid);
|
|
1660
|
-
|
|
1661
|
-
if (
|
|
1738
|
+
this.includeRoot(table, owned);
|
|
1739
|
+
if (this.ownProcessGroup) this.includeProcessGroup(table, owned, rootWasRunning);
|
|
1662
1740
|
includeDescendants(table, owned);
|
|
1663
|
-
owned.delete(
|
|
1741
|
+
owned.delete(this.child.pid ?? 0);
|
|
1664
1742
|
this.identities = new Map([...table].filter(([pid]) => owned.has(pid)));
|
|
1665
1743
|
}
|
|
1744
|
+
includeRoot(table, owned) {
|
|
1745
|
+
const root = this.child.pid;
|
|
1746
|
+
const rootIdentity = root && table.get(root);
|
|
1747
|
+
if (rootIdentity && isChildProcessRunning(this.child)) {
|
|
1748
|
+
this.rootBirth ??= rootIdentity.birth;
|
|
1749
|
+
if (rootIdentity.birth === this.rootBirth) owned.add(rootIdentity.pid);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
includeProcessGroup(table, owned, rootWasRunning) {
|
|
1753
|
+
const root = this.child.pid;
|
|
1754
|
+
if (root && (rootWasRunning || [...owned].some((pid) => table.get(pid)?.groupPid === root))) {
|
|
1755
|
+
for (const identity of table.values()) if (identity.groupPid === root && (this.groupExitedAt === void 0 || Date.parse(identity.birth) <= this.groupExitedAt)) owned.add(identity.pid);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1666
1758
|
async signal(signal, timeoutMs) {
|
|
1667
1759
|
if (!await this.capture(timeoutMs) || this.retired) return;
|
|
1668
1760
|
for (const pid of this.identities.keys()) try {
|
|
@@ -1671,6 +1763,9 @@ var ProcessDescendants = class {
|
|
|
1671
1763
|
this.identities.delete(pid);
|
|
1672
1764
|
}
|
|
1673
1765
|
}
|
|
1766
|
+
hasTrackedProcesses() {
|
|
1767
|
+
return this.identities.size > 0;
|
|
1768
|
+
}
|
|
1674
1769
|
async waitForExit(timeoutMs) {
|
|
1675
1770
|
const deadline = Date.now() + timeoutMs;
|
|
1676
1771
|
do {
|
|
@@ -1682,6 +1777,7 @@ var ProcessDescendants = class {
|
|
|
1682
1777
|
}
|
|
1683
1778
|
retire() {
|
|
1684
1779
|
this.retired = true;
|
|
1780
|
+
this.child.off("exit", this.onRootExit);
|
|
1685
1781
|
this.identities.clear();
|
|
1686
1782
|
}
|
|
1687
1783
|
};
|
|
@@ -1749,16 +1845,27 @@ function readTerminalOutputCeiling() {
|
|
|
1749
1845
|
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(bytes)) throw new Error("ACPX_TERMINAL_MAX_OUTPUT_BYTES must be a non-negative safe integer; zero disables the host ceiling");
|
|
1750
1846
|
return bytes === 0 ? void 0 : bytes;
|
|
1751
1847
|
}
|
|
1848
|
+
function resolveTerminalOutputLimit(requested, ceiling) {
|
|
1849
|
+
return Math.min(Math.max(0, Math.round(requested ?? DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES)), ceiling ?? Number.POSITIVE_INFINITY);
|
|
1850
|
+
}
|
|
1851
|
+
function tracksTerminalDescendants(terminal) {
|
|
1852
|
+
return terminal.killProcessGroup || terminal.descendants !== void 0;
|
|
1853
|
+
}
|
|
1854
|
+
function createTerminalDescendants(proc, killProcessGroup) {
|
|
1855
|
+
return process.platform === "win32" && killProcessGroup ? void 0 : new ProcessDescendants(proc, { ownProcessGroup: true });
|
|
1856
|
+
}
|
|
1752
1857
|
function trimToUtf8Boundary(buffer, limit) {
|
|
1753
1858
|
if (limit <= 0) return Buffer.alloc(0);
|
|
1754
|
-
|
|
1755
|
-
let start = buffer.length - limit;
|
|
1859
|
+
let start = Math.max(0, buffer.length - limit);
|
|
1756
1860
|
while (start < buffer.length && (buffer[start] & 192) === 128) start += 1;
|
|
1757
|
-
if (start >= buffer.length)
|
|
1861
|
+
if (start >= buffer.length) return Buffer.alloc(0);
|
|
1758
1862
|
return buffer.subarray(start);
|
|
1759
1863
|
}
|
|
1760
|
-
async function defaultConfirmExecute(commandLine) {
|
|
1761
|
-
return await promptForPermission({
|
|
1864
|
+
async function defaultConfirmExecute(commandLine, signal) {
|
|
1865
|
+
return await promptForPermission({
|
|
1866
|
+
prompt: `\n[permission] Allow terminal command "${commandLine}"? (y/N) `,
|
|
1867
|
+
signal
|
|
1868
|
+
});
|
|
1762
1869
|
}
|
|
1763
1870
|
function canPromptForPermission() {
|
|
1764
1871
|
return process.stdin.isTTY && process.stderr.isTTY;
|
|
@@ -1788,14 +1895,16 @@ var TerminalManager = class {
|
|
|
1788
1895
|
this.onOperation = options.onOperation;
|
|
1789
1896
|
this.usesDefaultConfirmExecute = options.confirmExecute == null;
|
|
1790
1897
|
this.confirmExecute = options.confirmExecute ?? defaultConfirmExecute;
|
|
1791
|
-
|
|
1898
|
+
const killGraceMs = Math.max(0, Math.round(options.killGraceMs ?? DEFAULT_KILL_GRACE_MS));
|
|
1899
|
+
this.killGraceMs = Number.isFinite(killGraceMs) && killGraceMs <= 2147483647 ? killGraceMs : 1;
|
|
1792
1900
|
this.processHelperTimeoutMs = Math.max(1, Math.round(options.processHelperTimeoutMs ?? 8e3));
|
|
1793
1901
|
}
|
|
1794
1902
|
updatePermissionPolicy(permissionMode, nonInteractivePermissions) {
|
|
1795
1903
|
this.permissionMode = permissionMode;
|
|
1796
1904
|
this.nonInteractivePermissions = nonInteractivePermissions ?? "deny";
|
|
1797
1905
|
}
|
|
1798
|
-
async createTerminal(params) {
|
|
1906
|
+
async createTerminal(params, authority) {
|
|
1907
|
+
assertControlAuthority(authority);
|
|
1799
1908
|
const commandLine = toCommandLine(params.command, params.args);
|
|
1800
1909
|
const summary = `terminal/create: ${commandLine}`;
|
|
1801
1910
|
this.emitOperation({
|
|
@@ -1805,10 +1914,11 @@ var TerminalManager = class {
|
|
|
1805
1914
|
timestamp: nowIso()
|
|
1806
1915
|
});
|
|
1807
1916
|
try {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
const
|
|
1917
|
+
const approved = await this.isExecuteApproved(commandLine, authority?.signal);
|
|
1918
|
+
assertControlAuthority(authority);
|
|
1919
|
+
if (!approved) throw new PermissionDeniedError("Permission denied for terminal/create");
|
|
1920
|
+
const outputByteLimit = resolveTerminalOutputLimit(params.outputByteLimit, this.outputCeilingBytes);
|
|
1921
|
+
const { proc, spawnCommand } = await spawnTerminalProcess(params, this.cwd, authority);
|
|
1812
1922
|
let resolveExit = () => {};
|
|
1813
1923
|
const exitPromise = new Promise((resolve) => {
|
|
1814
1924
|
resolveExit = resolve;
|
|
@@ -1817,6 +1927,7 @@ var TerminalManager = class {
|
|
|
1817
1927
|
process: proc,
|
|
1818
1928
|
killProcessGroup: spawnCommand.killProcessGroup,
|
|
1819
1929
|
descendantPids: /* @__PURE__ */ new Set(),
|
|
1930
|
+
descendants: createTerminalDescendants(proc, spawnCommand.killProcessGroup),
|
|
1820
1931
|
processHelperTimeoutMs: this.processHelperTimeoutMs,
|
|
1821
1932
|
output: Buffer.alloc(0),
|
|
1822
1933
|
truncated: false,
|
|
@@ -1830,10 +1941,8 @@ var TerminalManager = class {
|
|
|
1830
1941
|
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1831
1942
|
if (bytes.length === 0) return;
|
|
1832
1943
|
terminal.output = Buffer.concat([terminal.output, bytes]);
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
terminal.truncated = true;
|
|
1836
|
-
}
|
|
1944
|
+
terminal.truncated ||= terminal.output.length > terminal.outputByteLimit;
|
|
1945
|
+
if (terminal.truncated) terminal.output = trimToUtf8Boundary(terminal.output, terminal.outputByteLimit);
|
|
1837
1946
|
};
|
|
1838
1947
|
proc.stdout.on("data", appendOutput);
|
|
1839
1948
|
proc.stderr.on("data", appendOutput);
|
|
@@ -1842,9 +1951,10 @@ var TerminalManager = class {
|
|
|
1842
1951
|
proc.once("exit", (exitCode, signal) => {
|
|
1843
1952
|
terminal.exitCode = exitCode;
|
|
1844
1953
|
terminal.signal = signal;
|
|
1845
|
-
terminal.processGroupSnapshotPromise = rememberProcessGroupPids(terminal);
|
|
1954
|
+
terminal.processGroupSnapshotPromise = terminal.descendants ? terminal.descendants.capture(terminal.processHelperTimeoutMs).then(() => {}) : rememberProcessGroupPids(terminal);
|
|
1846
1955
|
(async () => {
|
|
1847
1956
|
await terminal.processGroupSnapshotPromise;
|
|
1957
|
+
terminal.processGroupSnapshotPromise = void 0;
|
|
1848
1958
|
terminal.resolveExit({
|
|
1849
1959
|
exitCode: exitCode ?? null,
|
|
1850
1960
|
signal: signal ?? null
|
|
@@ -1853,6 +1963,16 @@ var TerminalManager = class {
|
|
|
1853
1963
|
});
|
|
1854
1964
|
const terminalId = randomUUID();
|
|
1855
1965
|
this.terminals.set(terminalId, terminal);
|
|
1966
|
+
try {
|
|
1967
|
+
await terminal.descendants?.capture(terminal.processHelperTimeoutMs);
|
|
1968
|
+
assertControlAuthority(authority);
|
|
1969
|
+
} catch (error) {
|
|
1970
|
+
await this.releaseTerminal({
|
|
1971
|
+
terminalId,
|
|
1972
|
+
sessionId: params.sessionId
|
|
1973
|
+
});
|
|
1974
|
+
throw error;
|
|
1975
|
+
}
|
|
1856
1976
|
this.emitOperation({
|
|
1857
1977
|
method: "terminal/create",
|
|
1858
1978
|
status: "completed",
|
|
@@ -1958,6 +2078,9 @@ var TerminalManager = class {
|
|
|
1958
2078
|
try {
|
|
1959
2079
|
await this.killProcess(terminal);
|
|
1960
2080
|
await terminal.exitPromise.catch(() => {});
|
|
2081
|
+
terminal.descendants?.retire();
|
|
2082
|
+
terminal.process.stdout.destroy();
|
|
2083
|
+
terminal.process.stderr.destroy();
|
|
1961
2084
|
terminal.output = Buffer.alloc(0);
|
|
1962
2085
|
this.terminals.delete(params.terminalId);
|
|
1963
2086
|
this.emitOperation({
|
|
@@ -1980,10 +2103,13 @@ var TerminalManager = class {
|
|
|
1980
2103
|
}
|
|
1981
2104
|
}
|
|
1982
2105
|
async shutdown() {
|
|
1983
|
-
|
|
2106
|
+
const results = await Promise.allSettled(Array.from(this.terminals.keys(), (terminalId) => this.releaseTerminal({
|
|
1984
2107
|
terminalId,
|
|
1985
2108
|
sessionId: "shutdown"
|
|
1986
|
-
});
|
|
2109
|
+
})));
|
|
2110
|
+
const failures = [];
|
|
2111
|
+
for (const result of results) if (result.status === "rejected") failures.push(result.reason);
|
|
2112
|
+
if (failures.length > 0) throw new AggregateError(failures, "Terminal shutdown failed", { cause: failures[0] });
|
|
1987
2113
|
}
|
|
1988
2114
|
getTerminal(terminalId) {
|
|
1989
2115
|
return this.terminals.get(terminalId);
|
|
@@ -1991,23 +2117,23 @@ var TerminalManager = class {
|
|
|
1991
2117
|
emitOperation(operation) {
|
|
1992
2118
|
this.onOperation?.(operation);
|
|
1993
2119
|
}
|
|
1994
|
-
async isExecuteApproved(commandLine) {
|
|
2120
|
+
async isExecuteApproved(commandLine, signal) {
|
|
1995
2121
|
if (this.permissionMode === "approve-all") return true;
|
|
1996
2122
|
if (this.permissionMode === "deny-all") return false;
|
|
1997
2123
|
if (this.usesDefaultConfirmExecute && this.nonInteractivePermissions === "fail" && !canPromptForPermission()) throw new PermissionPromptUnavailableError();
|
|
1998
|
-
return await this.confirmExecute(commandLine);
|
|
2124
|
+
return await this.confirmExecute(commandLine, signal);
|
|
1999
2125
|
}
|
|
2000
2126
|
isRunning(terminal) {
|
|
2001
2127
|
return terminal.exitCode === void 0 && terminal.signal === void 0;
|
|
2002
2128
|
}
|
|
2003
2129
|
async killProcess(terminal) {
|
|
2004
|
-
if (!this.isRunning(terminal) && !terminal
|
|
2130
|
+
if (!this.isRunning(terminal) && !tracksTerminalDescendants(terminal)) return;
|
|
2005
2131
|
try {
|
|
2006
2132
|
await this.signalProcess(terminal, "SIGTERM");
|
|
2007
2133
|
} catch {
|
|
2008
2134
|
return;
|
|
2009
2135
|
}
|
|
2010
|
-
if (await this.waitForCleanupAfterSignal(terminal) && !terminal.killProcessGroup) return;
|
|
2136
|
+
if (await this.waitForCleanupAfterSignal(terminal) && (!terminal.killProcessGroup || terminal.descendants)) return;
|
|
2011
2137
|
try {
|
|
2012
2138
|
await this.signalProcess(terminal, "SIGKILL");
|
|
2013
2139
|
} catch {
|
|
@@ -2016,15 +2142,16 @@ var TerminalManager = class {
|
|
|
2016
2142
|
await this.waitForFinalCleanup(terminal);
|
|
2017
2143
|
}
|
|
2018
2144
|
async signalProcess(terminal, signal) {
|
|
2145
|
+
if (terminal.descendants) {
|
|
2146
|
+
await terminal.descendants.signal(signal, terminal.processHelperTimeoutMs);
|
|
2147
|
+
if (this.isRunning(terminal)) terminal.process.kill(signal);
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2019
2150
|
const pid = terminal.process.pid;
|
|
2020
2151
|
if (terminal.killProcessGroup && pid && process.platform === "win32") {
|
|
2021
2152
|
await this.signalWindowsProcessGroup(terminal, pid, signal);
|
|
2022
2153
|
return;
|
|
2023
2154
|
}
|
|
2024
|
-
if (terminal.killProcessGroup && pid) {
|
|
2025
|
-
await this.signalPosixProcessGroup(terminal, pid, signal);
|
|
2026
|
-
return;
|
|
2027
|
-
}
|
|
2028
2155
|
terminal.process.kill(signal);
|
|
2029
2156
|
}
|
|
2030
2157
|
async signalWindowsProcessGroup(terminal, pid, signal) {
|
|
@@ -2035,14 +2162,6 @@ var TerminalManager = class {
|
|
|
2035
2162
|
}
|
|
2036
2163
|
for (const descendantPid of terminal.descendantPids) await killWindowsProcessTree(descendantPid, signal, terminal.processHelperTimeoutMs);
|
|
2037
2164
|
}
|
|
2038
|
-
async signalPosixProcessGroup(terminal, pid, signal) {
|
|
2039
|
-
await this.captureDescendantPids(terminal, pid);
|
|
2040
|
-
if (hasLiveProcessGroup(pid)) {
|
|
2041
|
-
sendSignal(-pid, signal);
|
|
2042
|
-
return;
|
|
2043
|
-
}
|
|
2044
|
-
for (const descendantPid of terminal.descendantPids) sendSignal(descendantPid, signal);
|
|
2045
|
-
}
|
|
2046
2165
|
async captureDescendantPids(terminal, pid) {
|
|
2047
2166
|
if (!this.isRunning(terminal)) await terminal.processGroupSnapshotPromise?.catch(() => {});
|
|
2048
2167
|
for (const descendantPid of await listDescendantPids(pid, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(descendantPid);
|
|
@@ -2051,33 +2170,35 @@ var TerminalManager = class {
|
|
|
2051
2170
|
if (!await this.waitForCleanupAfterSignal(terminal) && process.platform === "win32") throw new Error("Terminal process cleanup did not finish after SIGKILL");
|
|
2052
2171
|
}
|
|
2053
2172
|
async waitForCleanupAfterSignal(terminal) {
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2173
|
+
const deadline = performance.now() + this.killGraceMs;
|
|
2174
|
+
while (this.isRunning(terminal) || terminal.processGroupSnapshotPromise || await hasLiveTerminalDescendants(terminal, deadline - performance.now()) || hasLivePid(terminal.descendantPids)) {
|
|
2175
|
+
const remaining = deadline - performance.now();
|
|
2176
|
+
if (remaining <= 0) return false;
|
|
2177
|
+
await waitMs(Math.min(25, remaining));
|
|
2178
|
+
}
|
|
2179
|
+
return true;
|
|
2060
2180
|
}
|
|
2061
2181
|
};
|
|
2062
|
-
async function spawnTerminalProcess(params, defaultCwd) {
|
|
2182
|
+
async function spawnTerminalProcess(params, defaultCwd, authority) {
|
|
2063
2183
|
const directCommand = buildTerminalSpawnCommand(params.command, params.args);
|
|
2064
2184
|
try {
|
|
2065
2185
|
return {
|
|
2066
|
-
proc: await spawnAndWait(directCommand, params, defaultCwd),
|
|
2186
|
+
proc: await spawnAndWait(directCommand, params, defaultCwd, authority),
|
|
2067
2187
|
spawnCommand: directCommand
|
|
2068
2188
|
};
|
|
2069
2189
|
} catch (error) {
|
|
2070
2190
|
const fallbackCommand = params.args === void 0 && isNotFoundSpawnError(error) ? buildTerminalFallbackSpawnCommand(params.command, params.cwd ?? defaultCwd) : void 0;
|
|
2071
2191
|
if (!fallbackCommand) throw error;
|
|
2072
2192
|
return {
|
|
2073
|
-
proc: await spawnAndWait(fallbackCommand, params, defaultCwd),
|
|
2193
|
+
proc: await spawnAndWait(fallbackCommand, params, defaultCwd, authority),
|
|
2074
2194
|
spawnCommand: fallbackCommand
|
|
2075
2195
|
};
|
|
2076
2196
|
}
|
|
2077
2197
|
}
|
|
2078
|
-
async function spawnAndWait(spawnCommand, params, defaultCwd) {
|
|
2198
|
+
async function spawnAndWait(spawnCommand, params, defaultCwd, authority) {
|
|
2079
2199
|
const spawnOptions = buildTerminalSpawnOptions(spawnCommand.command, params.cwd ?? defaultCwd, params.env);
|
|
2080
|
-
if (spawnCommand.killProcessGroup) spawnOptions.detached = true;
|
|
2200
|
+
if (spawnCommand.killProcessGroup || process.platform !== "win32") spawnOptions.detached = true;
|
|
2201
|
+
assertControlAuthority(authority);
|
|
2081
2202
|
const proc = spawn(spawnCommand.command, spawnCommand.args, spawnOptions);
|
|
2082
2203
|
await waitForSpawn(proc);
|
|
2083
2204
|
return proc;
|
|
@@ -2104,7 +2225,7 @@ function commandPathExists(command, cwd) {
|
|
|
2104
2225
|
async function listDescendantPids(rootPid, timeoutMs) {
|
|
2105
2226
|
let output;
|
|
2106
2227
|
try {
|
|
2107
|
-
output = await
|
|
2228
|
+
output = await runWindowsProcessListCommand(timeoutMs);
|
|
2108
2229
|
} catch {
|
|
2109
2230
|
return [];
|
|
2110
2231
|
}
|
|
@@ -2137,38 +2258,10 @@ function parseProcessListLine(line) {
|
|
|
2137
2258
|
parentPid
|
|
2138
2259
|
};
|
|
2139
2260
|
}
|
|
2140
|
-
async function runProcessListCommand(timeoutMs) {
|
|
2141
|
-
if (process.platform === "win32") return await runWindowsProcessListCommand(timeoutMs);
|
|
2142
|
-
return await runTimedExecFile("ps", ["-eo", "pid=,ppid="], { timeoutMs });
|
|
2143
|
-
}
|
|
2144
2261
|
async function rememberProcessGroupPids(terminal) {
|
|
2145
2262
|
const processGroupId = terminal.process.pid;
|
|
2146
2263
|
if (!terminal.killProcessGroup || !processGroupId) return;
|
|
2147
|
-
|
|
2148
|
-
for (const pid of await listDescendantPids(processGroupId, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(pid);
|
|
2149
|
-
return;
|
|
2150
|
-
}
|
|
2151
|
-
for (const pid of await listProcessGroupPids(processGroupId, terminal.processHelperTimeoutMs)) if (pid !== processGroupId) terminal.descendantPids.add(pid);
|
|
2152
|
-
}
|
|
2153
|
-
async function listProcessGroupPids(processGroupId, timeoutMs) {
|
|
2154
|
-
let output;
|
|
2155
|
-
try {
|
|
2156
|
-
output = await runProcessGroupListCommand(timeoutMs);
|
|
2157
|
-
} catch {
|
|
2158
|
-
return [];
|
|
2159
|
-
}
|
|
2160
|
-
const pids = [];
|
|
2161
|
-
for (const line of output.split("\n")) {
|
|
2162
|
-
const match = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
2163
|
-
if (!match) continue;
|
|
2164
|
-
const pid = Number(match[1]);
|
|
2165
|
-
const pgid = Number(match[2]);
|
|
2166
|
-
if (Number.isInteger(pid) && Number.isInteger(pgid) && pid > 0 && pgid === processGroupId) pids.push(pid);
|
|
2167
|
-
}
|
|
2168
|
-
return pids;
|
|
2169
|
-
}
|
|
2170
|
-
async function runProcessGroupListCommand(timeoutMs) {
|
|
2171
|
-
return await runTimedExecFile("ps", ["-eo", "pid=,pgid="], { timeoutMs });
|
|
2264
|
+
for (const pid of await listDescendantPids(processGroupId, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(pid);
|
|
2172
2265
|
}
|
|
2173
2266
|
async function runWindowsProcessListCommand(timeoutMs) {
|
|
2174
2267
|
const command = ["Get-CimInstance Win32_Process |", "ForEach-Object { \"$($_.ProcessId) $($_.ParentProcessId)\" }"].join(" ");
|
|
@@ -2196,22 +2289,11 @@ async function killWindowsProcessTree(pid, signal, timeoutMs = PROCESS_HELPER_TI
|
|
|
2196
2289
|
});
|
|
2197
2290
|
} catch {}
|
|
2198
2291
|
}
|
|
2199
|
-
function
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
function hasLiveProcessGroup(processGroupId) {
|
|
2205
|
-
try {
|
|
2206
|
-
process.kill(-processGroupId, 0);
|
|
2207
|
-
return true;
|
|
2208
|
-
} catch {
|
|
2209
|
-
return false;
|
|
2210
|
-
}
|
|
2211
|
-
}
|
|
2212
|
-
function hasLiveTerminalProcessGroup(terminal) {
|
|
2213
|
-
const pid = terminal.process.pid;
|
|
2214
|
-
return Boolean(terminal.killProcessGroup && pid && process.platform !== "win32" && hasLiveProcessGroup(pid));
|
|
2292
|
+
async function hasLiveTerminalDescendants(terminal, timeoutMs) {
|
|
2293
|
+
const descendants = terminal.descendants;
|
|
2294
|
+
if (!descendants) return false;
|
|
2295
|
+
if (timeoutMs > 0) await descendants.capture(Math.min(terminal.processHelperTimeoutMs, timeoutMs));
|
|
2296
|
+
return descendants.hasTrackedProcesses();
|
|
2215
2297
|
}
|
|
2216
2298
|
function hasLivePid(pids) {
|
|
2217
2299
|
for (const pid of pids) try {
|
|
@@ -2362,14 +2444,13 @@ var AcpClient = class {
|
|
|
2362
2444
|
cancellingSessionIds = /* @__PURE__ */ new Set();
|
|
2363
2445
|
permissionAbortControllers = /* @__PURE__ */ new Map();
|
|
2364
2446
|
closing = false;
|
|
2447
|
+
closeTask;
|
|
2365
2448
|
closeEpoch = 0;
|
|
2366
2449
|
agentStartedAt;
|
|
2367
2450
|
lastAgentExit;
|
|
2368
2451
|
lastKnownPid;
|
|
2369
2452
|
promptPermissionFailures = /* @__PURE__ */ new Map();
|
|
2370
2453
|
pendingConnectionRequests = /* @__PURE__ */ new Set();
|
|
2371
|
-
modelConfigIds = /* @__PURE__ */ new Map();
|
|
2372
|
-
legacyModelSessionIds = /* @__PURE__ */ new Set();
|
|
2373
2454
|
constructor(options) {
|
|
2374
2455
|
this.options = {
|
|
2375
2456
|
...options,
|
|
@@ -2460,7 +2541,7 @@ var AcpClient = class {
|
|
|
2460
2541
|
this.terminalManager.updatePermissionPolicy(this.options.permissionMode, this.options.nonInteractivePermissions);
|
|
2461
2542
|
}
|
|
2462
2543
|
hasLiveConnection() {
|
|
2463
|
-
return this.connection != null && !this.connection.signal.aborted && this.agent != null && isChildProcessRunning(this.agent);
|
|
2544
|
+
return !this.closing && this.connection != null && !this.connection.signal.aborted && this.agent != null && isChildProcessRunning(this.agent);
|
|
2464
2545
|
}
|
|
2465
2546
|
hasReusableSession(sessionId) {
|
|
2466
2547
|
return this.hasLiveConnection() && this.loadedSessionId === sessionId;
|
|
@@ -2473,21 +2554,27 @@ var AcpClient = class {
|
|
|
2473
2554
|
hasUnresolvedPrompt() {
|
|
2474
2555
|
return this.activePrompt !== void 0;
|
|
2475
2556
|
}
|
|
2476
|
-
|
|
2477
|
-
const
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2557
|
+
abortSessionRequests(sessionId) {
|
|
2558
|
+
const owners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
2559
|
+
const fallback = this.takePermissionAbortController(sessionId);
|
|
2560
|
+
for (const owner of owners) {
|
|
2561
|
+
owner.elicitationHandler = void 0;
|
|
2562
|
+
owner.elicitationController.abort();
|
|
2563
|
+
owner.requestController.abort();
|
|
2564
|
+
}
|
|
2565
|
+
fallback?.abort();
|
|
2481
2566
|
}
|
|
2482
|
-
async start() {
|
|
2567
|
+
async start(authority) {
|
|
2568
|
+
assertControlAuthority(authority);
|
|
2483
2569
|
if (this.hasLiveConnection()) return;
|
|
2484
|
-
|
|
2485
|
-
const epoch = this.closeEpoch;
|
|
2570
|
+
const epoch = await this.waitForPreviousClose();
|
|
2486
2571
|
const maxMessageBytes = readMaxAcpMessageBytes();
|
|
2487
2572
|
const launch = await this.resolveAgentLaunchPlan();
|
|
2573
|
+
assertControlAuthority(authority);
|
|
2488
2574
|
this.logAgentLaunch(launch);
|
|
2489
2575
|
await this.ensureLaunchSupport(launch);
|
|
2490
|
-
|
|
2576
|
+
assertControlAuthority(authority);
|
|
2577
|
+
const { child, process: startedProcess } = await this.spawnAgentProcess(launch, authority);
|
|
2491
2578
|
if (this.closeEpoch === epoch) {
|
|
2492
2579
|
this.agent = child;
|
|
2493
2580
|
this.closing = false;
|
|
@@ -2510,7 +2597,7 @@ var AcpClient = class {
|
|
|
2510
2597
|
startupFailure.dispose();
|
|
2511
2598
|
throw error;
|
|
2512
2599
|
}
|
|
2513
|
-
await this.stopIfClosedDuringLaunch(epoch, child, startupFailure);
|
|
2600
|
+
await this.stopIfClosedDuringLaunch(epoch, child, startupFailure, authority);
|
|
2514
2601
|
const input = Writable.toWeb(child.stdin);
|
|
2515
2602
|
const output = Readable.toWeb(child.stdout);
|
|
2516
2603
|
let connection;
|
|
@@ -2525,9 +2612,10 @@ var AcpClient = class {
|
|
|
2525
2612
|
elicitationModes: this.options.elicitationModes
|
|
2526
2613
|
});
|
|
2527
2614
|
connection = this.createConnection(stream, launch, capabilities);
|
|
2528
|
-
|
|
2529
|
-
this.
|
|
2530
|
-
}
|
|
2615
|
+
const onConnectionClose = () => {
|
|
2616
|
+
this.handleAgentDisconnect(child, "connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
2617
|
+
};
|
|
2618
|
+
connection.signal.addEventListener("abort", onConnectionClose, { once: true });
|
|
2531
2619
|
await this.initializeAgentConnection({
|
|
2532
2620
|
child,
|
|
2533
2621
|
connection,
|
|
@@ -2536,6 +2624,14 @@ var AcpClient = class {
|
|
|
2536
2624
|
launch,
|
|
2537
2625
|
capabilities
|
|
2538
2626
|
});
|
|
2627
|
+
if (connection.signal.aborted) onConnectionClose();
|
|
2628
|
+
}
|
|
2629
|
+
async waitForPreviousClose() {
|
|
2630
|
+
const cleanup = this.closeTask ?? (this.connection || this.agent ? this.close() : void 0);
|
|
2631
|
+
const epoch = this.closeEpoch;
|
|
2632
|
+
await cleanup;
|
|
2633
|
+
if (this.closeEpoch !== epoch) throw new Error("ACP client was closed while the agent was starting");
|
|
2634
|
+
return epoch;
|
|
2539
2635
|
}
|
|
2540
2636
|
async resolveAgentLaunchPlan() {
|
|
2541
2637
|
const configuredCommand = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
|
|
@@ -2576,7 +2672,7 @@ var AcpClient = class {
|
|
|
2576
2672
|
this.log(`resolved system Claude Code executable: ${claudeExe}`);
|
|
2577
2673
|
}
|
|
2578
2674
|
}
|
|
2579
|
-
async spawnAgentProcess(plan) {
|
|
2675
|
+
async spawnAgentProcess(plan, authority) {
|
|
2580
2676
|
const spawnCommand = buildAgentSpawnCommand(plan.spawnCommand, plan.args, process.platform, plan.spawnOptions.env);
|
|
2581
2677
|
const launch = Object.freeze({
|
|
2582
2678
|
launchId: randomUUID(),
|
|
@@ -2586,6 +2682,7 @@ var AcpClient = class {
|
|
|
2586
2682
|
cwd: this.options.cwd
|
|
2587
2683
|
});
|
|
2588
2684
|
await this.options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
2685
|
+
assertControlAuthority(authority);
|
|
2589
2686
|
let spawnedChild;
|
|
2590
2687
|
try {
|
|
2591
2688
|
spawnedChild = spawn(spawnCommand.command, spawnCommand.args, {
|
|
@@ -2634,19 +2731,19 @@ var AcpClient = class {
|
|
|
2634
2731
|
createConnection(stream, launch, capabilities) {
|
|
2635
2732
|
const app = client({ name: "acpx" }).onNotification(methods.client.session.update, async ({ params }) => {
|
|
2636
2733
|
await this.handleSessionUpdate(params);
|
|
2637
|
-
}).onNotification(methods.client.elicitation.complete, async () => {}).onRequest(methods.client.session.requestPermission, async ({ params }) => {
|
|
2638
|
-
return await this.handlePermissionRequest(params);
|
|
2734
|
+
}).onNotification(methods.client.elicitation.complete, async () => {}).onRequest(methods.client.session.requestPermission, async ({ params, signal }) => {
|
|
2735
|
+
return await this.handlePermissionRequest(params, signal);
|
|
2639
2736
|
}).onRequest(methods.client.elicitation.create, async ({ params, requestId, signal }) => {
|
|
2640
2737
|
return await this.handleElicitationRequest(params, requestId, signal);
|
|
2641
2738
|
});
|
|
2642
|
-
if (capabilities.fs?.readTextFile) app.onRequest(methods.client.fs.readTextFile, async ({ params }) => {
|
|
2643
|
-
return await this.handleReadTextFile(params);
|
|
2739
|
+
if (capabilities.fs?.readTextFile) app.onRequest(methods.client.fs.readTextFile, async ({ params, signal }) => {
|
|
2740
|
+
return await this.handleReadTextFile(params, signal);
|
|
2644
2741
|
});
|
|
2645
|
-
if (capabilities.fs?.writeTextFile) app.onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
|
|
2646
|
-
return await this.handleWriteTextFile(params);
|
|
2742
|
+
if (capabilities.fs?.writeTextFile) app.onRequest(methods.client.fs.writeTextFile, async ({ params, signal }) => {
|
|
2743
|
+
return await this.handleWriteTextFile(params, signal);
|
|
2647
2744
|
});
|
|
2648
|
-
if (capabilities.terminal) app.onRequest(methods.client.terminal.create, async ({ params }) => {
|
|
2649
|
-
return await this.handleCreateTerminal(params);
|
|
2745
|
+
if (capabilities.terminal) app.onRequest(methods.client.terminal.create, async ({ params, signal }) => {
|
|
2746
|
+
return await this.handleCreateTerminal(params, signal);
|
|
2650
2747
|
}).onRequest(methods.client.terminal.output, async ({ params }) => {
|
|
2651
2748
|
return await this.terminalManager.terminalOutput(params);
|
|
2652
2749
|
}).onRequest(methods.client.terminal.waitForExit, async ({ params }) => {
|
|
@@ -2707,7 +2804,8 @@ var AcpClient = class {
|
|
|
2707
2804
|
onPromptRequestWritten: (active, owner) => this.onPromptRequestWritten(active, owner)
|
|
2708
2805
|
});
|
|
2709
2806
|
}
|
|
2710
|
-
async createSession(cwd = this.options.cwd) {
|
|
2807
|
+
async createSession(cwd = this.options.cwd, authority) {
|
|
2808
|
+
assertControlAuthority(authority);
|
|
2711
2809
|
const connection = this.getConnection();
|
|
2712
2810
|
const { command, args } = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
|
|
2713
2811
|
const claudeAcp = isClaudeAcpCommand(command, args);
|
|
@@ -2718,7 +2816,7 @@ var AcpClient = class {
|
|
|
2718
2816
|
cwd: sessionCwd,
|
|
2719
2817
|
mcpServers: this.options.mcpServers ?? [],
|
|
2720
2818
|
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions, claudeAcp)
|
|
2721
|
-
}));
|
|
2819
|
+
}), authority);
|
|
2722
2820
|
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
|
2723
2821
|
} catch (error) {
|
|
2724
2822
|
if (claudeAcp && error instanceof TimeoutError) throw new ClaudeAcpSessionCreateTimeoutError(buildClaudeAcpSessionCreateTimeoutMessage(), {
|
|
@@ -2733,7 +2831,6 @@ var AcpClient = class {
|
|
|
2733
2831
|
configOptions,
|
|
2734
2832
|
response: result
|
|
2735
2833
|
});
|
|
2736
|
-
this.rememberSessionModels(result.sessionId, models);
|
|
2737
2834
|
await this.captureAgentDescendants(this.agent);
|
|
2738
2835
|
return {
|
|
2739
2836
|
sessionId: result.sessionId,
|
|
@@ -2751,6 +2848,7 @@ var AcpClient = class {
|
|
|
2751
2848
|
async loadSessionWithOptions(sessionId, cwd = this.options.cwd, options = {}) {
|
|
2752
2849
|
const connection = this.getConnection();
|
|
2753
2850
|
const sessionCwd = await resolveAgentSessionCwd(cwd, this.options.agentCommand);
|
|
2851
|
+
assertControlAuthority(options.authority);
|
|
2754
2852
|
const previousSuppression = this.applySessionUpdateSuppression(Boolean(options.suppressReplayUpdates));
|
|
2755
2853
|
let response;
|
|
2756
2854
|
try {
|
|
@@ -2758,28 +2856,27 @@ var AcpClient = class {
|
|
|
2758
2856
|
sessionId,
|
|
2759
2857
|
cwd: sessionCwd,
|
|
2760
2858
|
mcpServers: this.options.mcpServers ?? []
|
|
2761
|
-
}));
|
|
2859
|
+
}), options.authority);
|
|
2762
2860
|
await this.waitForSessionUpdateDrain(options.replayIdleMs ?? REPLAY_IDLE_MS, options.replayDrainTimeoutMs ?? REPLAY_DRAIN_TIMEOUT_MS);
|
|
2763
2861
|
} finally {
|
|
2764
2862
|
this.restoreSessionUpdateSuppression(previousSuppression);
|
|
2765
2863
|
}
|
|
2766
2864
|
this.loadedSessionId = sessionId;
|
|
2767
2865
|
const result = toReconnectedSessionResult(response);
|
|
2768
|
-
this.updateRememberedSessionModels(sessionId, result);
|
|
2769
2866
|
await this.captureAgentDescendants(this.agent);
|
|
2770
2867
|
return result;
|
|
2771
2868
|
}
|
|
2772
|
-
async resumeSession(sessionId, cwd = this.options.cwd) {
|
|
2869
|
+
async resumeSession(sessionId, cwd = this.options.cwd, authority) {
|
|
2870
|
+
assertControlAuthority(authority);
|
|
2773
2871
|
const connection = this.getConnection();
|
|
2774
2872
|
const sessionCwd = await resolveAgentSessionCwd(cwd, this.options.agentCommand);
|
|
2775
2873
|
const response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.resume, {
|
|
2776
2874
|
sessionId,
|
|
2777
2875
|
cwd: sessionCwd,
|
|
2778
2876
|
mcpServers: this.options.mcpServers ?? []
|
|
2779
|
-
}));
|
|
2877
|
+
}), authority);
|
|
2780
2878
|
this.loadedSessionId = sessionId;
|
|
2781
2879
|
const result = toReconnectedSessionResult(response);
|
|
2782
|
-
this.updateRememberedSessionModels(sessionId, result);
|
|
2783
2880
|
await this.captureAgentDescendants(this.agent);
|
|
2784
2881
|
return result;
|
|
2785
2882
|
}
|
|
@@ -2801,6 +2898,7 @@ var AcpClient = class {
|
|
|
2801
2898
|
const normalizedPrompt = this.normalizePromptForAgent(prompt);
|
|
2802
2899
|
const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
|
|
2803
2900
|
const previousActivePrompt = this.activePrompt;
|
|
2901
|
+
const replacedOwners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
2804
2902
|
const activePrompt = this.beginActivePrompt(sessionId, onRequestWritten, onElicitation, onPermissionRequest, authority);
|
|
2805
2903
|
try {
|
|
2806
2904
|
const promptPromise = this.runConnectionRequest(() => connection.agent.request(methods.agent.session.prompt, {
|
|
@@ -2809,6 +2907,7 @@ var AcpClient = class {
|
|
|
2809
2907
|
}), authority);
|
|
2810
2908
|
activePrompt.promise = promptPromise;
|
|
2811
2909
|
previousActivePrompt?.elicitationController?.abort();
|
|
2910
|
+
for (const owner of replacedOwners) owner.requestController.abort();
|
|
2812
2911
|
return this.returnPromptResponseOrPermissionFailure(sessionId, await promptPromise);
|
|
2813
2912
|
} catch (error) {
|
|
2814
2913
|
if (activePrompt.admissionFailure) throw activePrompt.admissionFailure.error;
|
|
@@ -2827,7 +2926,8 @@ var AcpClient = class {
|
|
|
2827
2926
|
authority,
|
|
2828
2927
|
elicitationHandler,
|
|
2829
2928
|
permissionHandler,
|
|
2830
|
-
elicitationController: new AbortController()
|
|
2929
|
+
elicitationController: new AbortController(),
|
|
2930
|
+
requestController: new AbortController()
|
|
2831
2931
|
};
|
|
2832
2932
|
this.activePrompt = active;
|
|
2833
2933
|
this.pendingPromptOwners.push(active);
|
|
@@ -2866,6 +2966,7 @@ var AcpClient = class {
|
|
|
2866
2966
|
this.promptPermissionFailures.delete(active.sessionId);
|
|
2867
2967
|
}
|
|
2868
2968
|
active.elicitationController.abort();
|
|
2969
|
+
active.requestController.abort();
|
|
2869
2970
|
permissionController?.abort();
|
|
2870
2971
|
}
|
|
2871
2972
|
normalizePromptForAgent(prompt) {
|
|
@@ -2889,33 +2990,41 @@ var AcpClient = class {
|
|
|
2889
2990
|
modeId
|
|
2890
2991
|
}), authority, (error) => maybeWrapSessionControlError("session/set_mode", error, `for mode "${modeId}"`));
|
|
2891
2992
|
}
|
|
2892
|
-
async setSessionConfigOption(sessionId, configId, value, authority) {
|
|
2993
|
+
async setSessionConfigOption(sessionId, configId, value, models, authority) {
|
|
2994
|
+
assertControlAuthority(authority);
|
|
2995
|
+
const resolvedValue = models?.configId === configId ? this.resolveSessionModelId(value, models) : value;
|
|
2893
2996
|
const connection = this.getConnection();
|
|
2894
2997
|
return await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.setConfigOption, {
|
|
2895
2998
|
sessionId,
|
|
2896
2999
|
configId,
|
|
2897
|
-
value
|
|
3000
|
+
value: resolvedValue
|
|
2898
3001
|
}), authority, (error) => maybeWrapSessionControlError("session/set_config_option", error, `for "${configId}"="${value}"`));
|
|
2899
3002
|
}
|
|
2900
|
-
async setSessionModel(sessionId, modelId,
|
|
2901
|
-
|
|
2902
|
-
if (!
|
|
2903
|
-
const resolvedModelId =
|
|
3003
|
+
async setSessionModel(sessionId, modelId, models, authority) {
|
|
3004
|
+
assertControlAuthority(authority);
|
|
3005
|
+
if (!models) throw new RequestedModelUnsupportedError(`Cannot set model "${modelId}": the ACP session did not advertise a model config option or legacy session/set_model support.`, "missing-capability");
|
|
3006
|
+
const resolvedModelId = this.resolveSessionModelId(modelId, models);
|
|
3007
|
+
return models.configId ? await this.setSessionModelThroughConfig(sessionId, resolvedModelId, models.configId, authority) : await this.setSessionModelThroughLegacyMethod(sessionId, resolvedModelId, authority);
|
|
3008
|
+
}
|
|
3009
|
+
resolveSessionModelId(modelId, models) {
|
|
3010
|
+
const params = {
|
|
2904
3011
|
requestedModel: modelId,
|
|
2905
|
-
models
|
|
3012
|
+
models,
|
|
2906
3013
|
agentCommand: this.options.agentCommand
|
|
3014
|
+
};
|
|
3015
|
+
assertRequestedModelSupported({
|
|
3016
|
+
...params,
|
|
3017
|
+
context: "apply"
|
|
2907
3018
|
});
|
|
2908
|
-
return
|
|
3019
|
+
return resolveRequestedModelId(params);
|
|
2909
3020
|
}
|
|
2910
3021
|
async setSessionModelThroughConfig(sessionId, modelId, configId, authority) {
|
|
2911
3022
|
const connection = this.getConnection();
|
|
2912
|
-
|
|
3023
|
+
return await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.setConfigOption, {
|
|
2913
3024
|
sessionId,
|
|
2914
3025
|
configId,
|
|
2915
3026
|
value: modelId
|
|
2916
3027
|
}), authority, (error) => this.throwSessionModelError("session/set_config_option", modelId, error));
|
|
2917
|
-
this.rememberSessionModels(sessionId, modelStateFromConfigOptions(response.configOptions));
|
|
2918
|
-
return response;
|
|
2919
3028
|
}
|
|
2920
3029
|
async setSessionModelThroughLegacyMethod(sessionId, modelId, authority) {
|
|
2921
3030
|
const connection = this.getConnection();
|
|
@@ -2931,39 +3040,10 @@ var AcpClient = class {
|
|
|
2931
3040
|
const summary = acp ? formatSessionControlAcpSummary(acp) : error instanceof Error ? error.message : String(error);
|
|
2932
3041
|
throw new Error(`Failed ${method} for model "${modelId}": ${summary}`, { cause: error });
|
|
2933
3042
|
}
|
|
2934
|
-
resolveModelControl(sessionId, controlOverride) {
|
|
2935
|
-
if (controlOverride) return controlOverride.configId ? {
|
|
2936
|
-
kind: "config_option",
|
|
2937
|
-
configId: controlOverride.configId
|
|
2938
|
-
} : { kind: "legacy_set_model" };
|
|
2939
|
-
const configId = this.modelConfigIds.get(sessionId);
|
|
2940
|
-
if (configId) return {
|
|
2941
|
-
kind: "config_option",
|
|
2942
|
-
configId
|
|
2943
|
-
};
|
|
2944
|
-
return this.legacyModelSessionIds.has(sessionId) ? { kind: "legacy_set_model" } : void 0;
|
|
2945
|
-
}
|
|
2946
|
-
rememberSessionModels(sessionId, models) {
|
|
2947
|
-
if (!models) {
|
|
2948
|
-
this.modelConfigIds.delete(sessionId);
|
|
2949
|
-
this.legacyModelSessionIds.delete(sessionId);
|
|
2950
|
-
return;
|
|
2951
|
-
}
|
|
2952
|
-
if (models.configId) {
|
|
2953
|
-
this.modelConfigIds.set(sessionId, models.configId);
|
|
2954
|
-
this.legacyModelSessionIds.delete(sessionId);
|
|
2955
|
-
return;
|
|
2956
|
-
}
|
|
2957
|
-
this.modelConfigIds.delete(sessionId);
|
|
2958
|
-
this.legacyModelSessionIds.add(sessionId);
|
|
2959
|
-
}
|
|
2960
|
-
updateRememberedSessionModels(sessionId, result) {
|
|
2961
|
-
const explicitConfigRemoval = result.configOptionsPresent && this.modelConfigIds.has(sessionId);
|
|
2962
|
-
if (result.models || result.legacyModelMetadataPresent || explicitConfigRemoval) this.rememberSessionModels(sessionId, result.models);
|
|
2963
|
-
}
|
|
2964
3043
|
async cancel(sessionId) {
|
|
2965
3044
|
const connection = this.getConnection();
|
|
2966
|
-
const
|
|
3045
|
+
const owners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
3046
|
+
const active = owners.at(-1);
|
|
2967
3047
|
const cancellation = active?.cancelPromise ?? this.runConnectionRequest(() => connection.agent.notify(methods.agent.session.cancel, { sessionId })).catch((error) => {
|
|
2968
3048
|
if (active?.cancelPromise === cancellation) active.cancelPromise = void 0;
|
|
2969
3049
|
throw error;
|
|
@@ -2972,19 +3052,20 @@ var AcpClient = class {
|
|
|
2972
3052
|
if (active) {
|
|
2973
3053
|
active.cancelPromise = cancellation;
|
|
2974
3054
|
this.cancellingSessionIds.add(sessionId);
|
|
2975
|
-
active.elicitationController?.abort();
|
|
2976
3055
|
} else this.cancellingSessionIds.delete(sessionId);
|
|
3056
|
+
for (const owner of owners) {
|
|
3057
|
+
owner.elicitationController.abort();
|
|
3058
|
+
owner.requestController.abort();
|
|
3059
|
+
}
|
|
2977
3060
|
permissionController?.abort();
|
|
2978
3061
|
await cancellation;
|
|
2979
3062
|
}
|
|
2980
3063
|
async closeSession(sessionId) {
|
|
2981
3064
|
const connection = this.getConnection();
|
|
2982
3065
|
this.cancellingSessionIds.add(sessionId);
|
|
2983
|
-
|
|
3066
|
+
this.abortSessionRequests(sessionId);
|
|
2984
3067
|
await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.close, { sessionId }));
|
|
2985
3068
|
if (this.loadedSessionId === sessionId) this.loadedSessionId = void 0;
|
|
2986
|
-
this.modelConfigIds.delete(sessionId);
|
|
2987
|
-
this.legacyModelSessionIds.delete(sessionId);
|
|
2988
3069
|
}
|
|
2989
3070
|
async listSessions(params = {}) {
|
|
2990
3071
|
const connection = this.getConnection();
|
|
@@ -3018,15 +3099,23 @@ var AcpClient = class {
|
|
|
3018
3099
|
if (timer) clearTimeout(timer);
|
|
3019
3100
|
}
|
|
3020
3101
|
}
|
|
3021
|
-
|
|
3102
|
+
close() {
|
|
3022
3103
|
this.closing = true;
|
|
3023
3104
|
this.closeEpoch += 1;
|
|
3105
|
+
if (this.closeTask) return this.closeTask;
|
|
3106
|
+
this.closeTask = Promise.resolve().then(() => this.finishClose()).finally(() => {
|
|
3107
|
+
this.closeTask = void 0;
|
|
3108
|
+
});
|
|
3109
|
+
return this.closeTask;
|
|
3110
|
+
}
|
|
3111
|
+
async finishClose() {
|
|
3112
|
+
const permissionControllers = [...this.permissionAbortControllers.values()];
|
|
3113
|
+
this.permissionAbortControllers.clear();
|
|
3114
|
+
const owners = [...this.pendingPromptOwners];
|
|
3024
3115
|
this.abortActiveElicitation();
|
|
3025
|
-
|
|
3026
|
-
const
|
|
3027
|
-
|
|
3028
|
-
this.closeConnection();
|
|
3029
|
-
if (this.pendingConnectionRequests.size > 0) this.rejectPendingConnectionRequests(this.lastAgentExit ? new AgentDisconnectedError(this.lastAgentExit.reason, this.lastAgentExit.exitCode, this.lastAgentExit.signal, { outputAlreadyEmitted: Boolean(this.activePrompt) }) : new AgentDisconnectedError("connection_close", null, null, { outputAlreadyEmitted: Boolean(this.activePrompt) }));
|
|
3116
|
+
for (const owner of owners) owner.requestController.abort();
|
|
3117
|
+
for (const controller of permissionControllers) controller.abort();
|
|
3118
|
+
await this.retireNativeResources();
|
|
3030
3119
|
this.sessionUpdateChain = Promise.resolve();
|
|
3031
3120
|
this.observedSessionUpdates = 0;
|
|
3032
3121
|
this.processedSessionUpdates = 0;
|
|
@@ -3039,16 +3128,35 @@ var AcpClient = class {
|
|
|
3039
3128
|
this.permissionAbortControllers.clear();
|
|
3040
3129
|
this.promptPermissionFailures.clear();
|
|
3041
3130
|
this.loadedSessionId = void 0;
|
|
3042
|
-
this.modelConfigIds.clear();
|
|
3043
|
-
this.legacyModelSessionIds.clear();
|
|
3044
3131
|
this.initResult = void 0;
|
|
3045
3132
|
this.connection = void 0;
|
|
3046
3133
|
this.agent = void 0;
|
|
3047
3134
|
}
|
|
3048
|
-
async
|
|
3135
|
+
async retireNativeResources() {
|
|
3136
|
+
const agent = this.agent;
|
|
3137
|
+
const failures = [];
|
|
3138
|
+
try {
|
|
3139
|
+
await this.terminalManager.shutdown();
|
|
3140
|
+
} catch (error) {
|
|
3141
|
+
failures.push(error);
|
|
3142
|
+
}
|
|
3143
|
+
try {
|
|
3144
|
+
if (agent) await this.terminateAgentProcess(agent);
|
|
3145
|
+
} catch (error) {
|
|
3146
|
+
failures.push(error);
|
|
3147
|
+
}
|
|
3148
|
+
try {
|
|
3149
|
+
this.closeConnection();
|
|
3150
|
+
} finally {
|
|
3151
|
+
this.rejectPendingConnectionRequests(this.lastAgentExit ? new AgentDisconnectedError(this.lastAgentExit.reason, this.lastAgentExit.exitCode, this.lastAgentExit.signal, { outputAlreadyEmitted: Boolean(this.activePrompt) }) : new AgentDisconnectedError("connection_close", null, null, { outputAlreadyEmitted: Boolean(this.activePrompt) }));
|
|
3152
|
+
}
|
|
3153
|
+
if (failures.length > 0) throw new AggregateError(failures, "ACP client cleanup failed", { cause: failures[0] });
|
|
3154
|
+
}
|
|
3155
|
+
async stopIfClosedDuringLaunch(epoch, child, startupFailure, authority) {
|
|
3049
3156
|
if (this.closeEpoch === epoch) return;
|
|
3050
3157
|
startupFailure.dispose();
|
|
3051
3158
|
await this.terminateAgentProcess(child);
|
|
3159
|
+
assertControlAuthority(authority);
|
|
3052
3160
|
throw new Error("ACP client was closed while the agent was starting");
|
|
3053
3161
|
}
|
|
3054
3162
|
abortActiveElicitation() {
|
|
@@ -3255,27 +3363,41 @@ var AcpClient = class {
|
|
|
3255
3363
|
await connection.agent.request(methods.agent.authenticate, { methodId: selected.methodId });
|
|
3256
3364
|
this.log(`authenticated with method ${selected.methodId} (${selected.source})`);
|
|
3257
3365
|
}
|
|
3258
|
-
async handlePermissionRequest(params) {
|
|
3259
|
-
|
|
3260
|
-
if (this.
|
|
3261
|
-
|
|
3262
|
-
if (
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
const decision = classifyPermissionDecision(params, response);
|
|
3266
|
-
this.recordPermissionDecision(decision);
|
|
3366
|
+
async handlePermissionRequest(params, requestSignal) {
|
|
3367
|
+
const owner = this.captureDelegatedRequestOwner(params.sessionId, requestSignal);
|
|
3368
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3369
|
+
if (!this.hasPermissionOwner(params.sessionId)) return this.finishPermissionRequest(params, owner, cancelledPermissionResponse());
|
|
3370
|
+
if (this.initResult?.agentInfo?.name === "antigravity-acp" && params.toolCall.toolCallId.startsWith("interaction_")) {
|
|
3371
|
+
const response = this.handleModePermissionError(params.sessionId, new PermissionPromptUnavailableError("Antigravity requested a user answer. acpx cannot answer Antigravity interaction questions; continue in an interactive client."));
|
|
3372
|
+
return this.finishPermissionRequest(params, owner, response);
|
|
3267
3373
|
}
|
|
3268
|
-
|
|
3374
|
+
const response = await this.tryHandlePermissionRequestWithHost(params, owner) ?? await this.resolvePermissionRequestFromMode(params, owner);
|
|
3375
|
+
return this.finishPermissionRequest(params, owner, response);
|
|
3376
|
+
}
|
|
3377
|
+
finishPermissionRequest(params, owner, response) {
|
|
3378
|
+
const result = this.isDelegatedRequestActive(owner) ? response : cancelledPermissionResponse();
|
|
3379
|
+
this.recordPermissionDecision(classifyPermissionDecision(params, result));
|
|
3380
|
+
return result;
|
|
3381
|
+
}
|
|
3382
|
+
hasPermissionOwner(sessionId) {
|
|
3383
|
+
return !this.pendingPromptOwners.some((owner) => owner.permissionHandler !== void 0) || this.pendingPromptOwners.some((owner) => owner.sessionId === sessionId);
|
|
3269
3384
|
}
|
|
3270
3385
|
async handleElicitationRequest(request, requestId, requestSignal) {
|
|
3271
3386
|
const resolved = this.resolveElicitationOwner(request);
|
|
3272
3387
|
if ("response" in resolved) return resolved.response;
|
|
3273
3388
|
const { active, handler } = resolved.owner;
|
|
3274
|
-
const signal = AbortSignal.any([
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
signal
|
|
3278
|
-
|
|
3389
|
+
const signal = AbortSignal.any([
|
|
3390
|
+
requestSignal,
|
|
3391
|
+
active.elicitationController.signal,
|
|
3392
|
+
active.authority?.signal
|
|
3393
|
+
].filter((candidate) => candidate !== void 0));
|
|
3394
|
+
const outcome = await raceWithAbort(signal, Promise.resolve().then(async () => {
|
|
3395
|
+
signal.throwIfAborted();
|
|
3396
|
+
return await handler(request, {
|
|
3397
|
+
requestId,
|
|
3398
|
+
signal
|
|
3399
|
+
});
|
|
3400
|
+
}).then((response) => ({
|
|
3279
3401
|
kind: "response",
|
|
3280
3402
|
response
|
|
3281
3403
|
}), (error) => ({
|
|
@@ -3310,62 +3432,65 @@ var AcpClient = class {
|
|
|
3310
3432
|
isElicitationSessionCancelling(sessionId) {
|
|
3311
3433
|
return this.closing || this.cancellingSessionIds.has(sessionId);
|
|
3312
3434
|
}
|
|
3313
|
-
|
|
3314
|
-
const active = this.
|
|
3315
|
-
const
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
};
|
|
3435
|
+
captureDelegatedRequestOwner(sessionId, requestSignal) {
|
|
3436
|
+
const active = this.pendingPromptOwners.findLast((owner) => owner.sessionId === sessionId);
|
|
3437
|
+
const signal = AbortSignal.any([
|
|
3438
|
+
active?.requestController.signal ?? this.cancellationSignalForSession(sessionId),
|
|
3439
|
+
active?.authority?.signal,
|
|
3440
|
+
requestSignal
|
|
3441
|
+
].filter((candidate) => candidate !== void 0));
|
|
3442
|
+
const epoch = this.closeEpoch;
|
|
3322
3443
|
return {
|
|
3323
|
-
|
|
3324
|
-
|
|
3444
|
+
signal,
|
|
3445
|
+
permissionHandler: active?.permissionHandler ?? this.options.onPermissionRequest,
|
|
3446
|
+
assertActive: () => {
|
|
3447
|
+
if (this.closing || this.closeEpoch !== epoch || this.cancellingSessionIds.has(sessionId)) throw RequestError.requestCancelled();
|
|
3448
|
+
}
|
|
3325
3449
|
};
|
|
3326
3450
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3451
|
+
isDelegatedRequestActive(owner) {
|
|
3452
|
+
try {
|
|
3453
|
+
assertControlAuthority(owner);
|
|
3454
|
+
return true;
|
|
3455
|
+
} catch {
|
|
3456
|
+
return false;
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
async tryHandlePermissionRequestWithHost(params, owner) {
|
|
3460
|
+
const handler = owner.permissionHandler;
|
|
3461
|
+
if (!handler) return;
|
|
3462
|
+
const { signal } = owner;
|
|
3463
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3332
3464
|
try {
|
|
3333
3465
|
const decision = await raceWithAbort(signal, handler({
|
|
3334
3466
|
sessionId: params.sessionId,
|
|
3335
3467
|
raw: params,
|
|
3336
3468
|
inferredKind: inferToolKind(params)
|
|
3337
3469
|
}, { signal }));
|
|
3338
|
-
return this.hostPermissionDecisionResponse(params,
|
|
3470
|
+
return this.hostPermissionDecisionResponse(params, owner, decision);
|
|
3339
3471
|
} catch (error) {
|
|
3340
|
-
return this.hostPermissionErrorResponse(
|
|
3472
|
+
return this.hostPermissionErrorResponse(owner, error);
|
|
3341
3473
|
}
|
|
3342
3474
|
}
|
|
3343
|
-
hostPermissionDecisionResponse(params,
|
|
3344
|
-
if (
|
|
3345
|
-
this.recordPermissionDecision("cancelled");
|
|
3346
|
-
return cancelledPermissionResponse();
|
|
3347
|
-
}
|
|
3475
|
+
hostPermissionDecisionResponse(params, owner, decision) {
|
|
3476
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3348
3477
|
if (!decision) return;
|
|
3349
3478
|
const response = decisionToResponse(preferCodexPermissionRefusal(params, this.initResult?.agentInfo?.name), decision);
|
|
3350
|
-
this.recordPermissionDecision(classifyPermissionDecision(params, response));
|
|
3351
3479
|
return decision.outcome === "cancel" ? response : this.explainPermissionRefusal(params, response);
|
|
3352
3480
|
}
|
|
3353
|
-
hostPermissionErrorResponse(
|
|
3354
|
-
if (
|
|
3355
|
-
this.recordPermissionDecision("cancelled");
|
|
3356
|
-
return cancelledPermissionResponse();
|
|
3357
|
-
}
|
|
3481
|
+
hostPermissionErrorResponse(owner, error) {
|
|
3482
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3358
3483
|
this.log(`onPermissionRequest threw, falling through to mode-based resolver: ${error instanceof Error ? error.message : String(error)}`);
|
|
3359
3484
|
}
|
|
3360
|
-
async resolvePermissionRequestFromMode(params) {
|
|
3485
|
+
async resolvePermissionRequestFromMode(params, owner) {
|
|
3361
3486
|
try {
|
|
3362
|
-
|
|
3487
|
+
assertControlAuthority(owner);
|
|
3488
|
+
const result = await resolvePermissionRequestWithDetails(preferCodexPermissionRefusal(params, this.initResult?.agentInfo?.name), this.options.permissionMode, this.options.nonInteractivePermissions ?? "deny", this.options.permissionPolicy, owner.signal);
|
|
3489
|
+
assertControlAuthority(owner);
|
|
3363
3490
|
this.emitPermissionEscalation(result.escalation);
|
|
3364
|
-
return
|
|
3365
|
-
response: this.explainPermissionRefusal(params, result.response),
|
|
3366
|
-
recorded: false
|
|
3367
|
-
};
|
|
3491
|
+
return this.explainPermissionRefusal(params, result.response);
|
|
3368
3492
|
} catch (error) {
|
|
3493
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3369
3494
|
return this.handleModePermissionError(params.sessionId, error);
|
|
3370
3495
|
}
|
|
3371
3496
|
}
|
|
@@ -3388,16 +3513,12 @@ var AcpClient = class {
|
|
|
3388
3513
|
handleModePermissionError(sessionId, error) {
|
|
3389
3514
|
if (!(error instanceof PermissionPromptUnavailableError)) throw error;
|
|
3390
3515
|
this.notePromptPermissionFailure(sessionId, error);
|
|
3391
|
-
|
|
3392
|
-
return {
|
|
3393
|
-
response: cancelledPermissionResponse(),
|
|
3394
|
-
recorded: true
|
|
3395
|
-
};
|
|
3516
|
+
return cancelledPermissionResponse();
|
|
3396
3517
|
}
|
|
3397
3518
|
attachAgentLifecycleObservers(child, startedProcess, exitNotificationBarrier) {
|
|
3398
3519
|
const onExit = (exitCode, signal) => {
|
|
3399
3520
|
const exitedAt = isoNow$2();
|
|
3400
|
-
this.
|
|
3521
|
+
this.handleAgentDisconnect(child, "process_exit", exitCode, signal);
|
|
3401
3522
|
this.terminateAgentProcess(child);
|
|
3402
3523
|
exitNotificationBarrier.then(() => {
|
|
3403
3524
|
this.notifyProcessExit(startedProcess, exitCode, signal, exitedAt);
|
|
@@ -3409,10 +3530,17 @@ var AcpClient = class {
|
|
|
3409
3530
|
onExit(child.exitCode, child.signalCode);
|
|
3410
3531
|
}
|
|
3411
3532
|
child.once("close", (exitCode, signal) => {
|
|
3412
|
-
this.
|
|
3533
|
+
this.handleAgentDisconnect(child, "process_close", exitCode, signal);
|
|
3413
3534
|
});
|
|
3414
3535
|
child.stdout.once("close", () => {
|
|
3415
|
-
this.
|
|
3536
|
+
this.handleAgentDisconnect(child, "pipe_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
3537
|
+
});
|
|
3538
|
+
}
|
|
3539
|
+
handleAgentDisconnect(child, reason, exitCode, signal) {
|
|
3540
|
+
this.recordAgentExit(child, reason, exitCode, signal);
|
|
3541
|
+
if (this.agent !== child || this.closing || !this.connection) return;
|
|
3542
|
+
this.close().catch((error) => {
|
|
3543
|
+
this.log(`disconnected agent cleanup failed: ${String(error)}`);
|
|
3416
3544
|
});
|
|
3417
3545
|
}
|
|
3418
3546
|
notifyProcessSpawnFailure(launch, error) {
|
|
@@ -3519,28 +3647,30 @@ var AcpClient = class {
|
|
|
3519
3647
|
pending.reject(error);
|
|
3520
3648
|
}
|
|
3521
3649
|
}
|
|
3522
|
-
async handleReadTextFile(params) {
|
|
3650
|
+
async handleReadTextFile(params, requestSignal) {
|
|
3523
3651
|
try {
|
|
3524
|
-
return await this.filesystem.readTextFile(params);
|
|
3652
|
+
return await this.runDelegatedOperation(params.sessionId, requestSignal, (owner) => this.filesystem.readTextFile(params, owner));
|
|
3525
3653
|
} catch (error) {
|
|
3526
|
-
this.recordPermissionError(params.sessionId, error);
|
|
3527
3654
|
if (error instanceof FsSafeError && error.code === "not-found" || error instanceof Error && "code" in error && error.code === "ENOENT") throw RequestError.resourceNotFound(pathToFileURL(params.path).href);
|
|
3528
3655
|
throw error;
|
|
3529
3656
|
}
|
|
3530
3657
|
}
|
|
3531
|
-
async handleWriteTextFile(params) {
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
throw error;
|
|
3537
|
-
}
|
|
3658
|
+
async handleWriteTextFile(params, requestSignal) {
|
|
3659
|
+
return await this.runDelegatedOperation(params.sessionId, requestSignal, (owner) => this.filesystem.writeTextFile(params, owner));
|
|
3660
|
+
}
|
|
3661
|
+
async handleCreateTerminal(params, requestSignal) {
|
|
3662
|
+
return await this.runDelegatedOperation(params.sessionId, requestSignal, (owner) => this.terminalManager.createTerminal(params, owner));
|
|
3538
3663
|
}
|
|
3539
|
-
async
|
|
3664
|
+
async runDelegatedOperation(sessionId, requestSignal, run) {
|
|
3665
|
+
const owner = this.captureDelegatedRequestOwner(sessionId, requestSignal);
|
|
3540
3666
|
try {
|
|
3541
|
-
|
|
3667
|
+
assertControlAuthority(owner);
|
|
3668
|
+
const result = await run(owner);
|
|
3669
|
+
assertControlAuthority(owner);
|
|
3670
|
+
return result;
|
|
3542
3671
|
} catch (error) {
|
|
3543
|
-
this.
|
|
3672
|
+
if (!this.isDelegatedRequestActive(owner)) throw RequestError.requestCancelled();
|
|
3673
|
+
this.recordPermissionError(sessionId, error);
|
|
3544
3674
|
throw error;
|
|
3545
3675
|
}
|
|
3546
3676
|
}
|
|
@@ -3774,7 +3904,6 @@ function ensureAgentMessage(conversation) {
|
|
|
3774
3904
|
return created;
|
|
3775
3905
|
}
|
|
3776
3906
|
function appendAgentText(agent, text) {
|
|
3777
|
-
if (!text.trim()) return;
|
|
3778
3907
|
const last = agent.content.at(-1);
|
|
3779
3908
|
if (last && isAgentTextContent(last)) {
|
|
3780
3909
|
last.Text = trimRuntimeText(`${last.Text}${text}`, MAX_RUNTIME_AGENT_TEXT_CHARS);
|
|
@@ -3784,7 +3913,6 @@ function appendAgentText(agent, text) {
|
|
|
3784
3913
|
agent.content.push(next);
|
|
3785
3914
|
}
|
|
3786
3915
|
function appendAgentThinking(agent, text) {
|
|
3787
|
-
if (!text.trim()) return;
|
|
3788
3916
|
const last = agent.content.at(-1);
|
|
3789
3917
|
if (last && isAgentThinkingContent(last)) {
|
|
3790
3918
|
last.Thinking.text = trimRuntimeText(`${last.Thinking.text}${text}`, MAX_RUNTIME_THINKING_CHARS);
|
|
@@ -3841,8 +3969,7 @@ function ensureToolUseContent(agent, toolCallId) {
|
|
|
3841
3969
|
return created;
|
|
3842
3970
|
}
|
|
3843
3971
|
function upsertToolResult(agent, toolCallId, patch) {
|
|
3844
|
-
const
|
|
3845
|
-
const fallback = existingToolResultValues(existing);
|
|
3972
|
+
const fallback = existingToolResultValues(hasOwn(agent.tool_results, toolCallId) ? agent.tool_results[toolCallId] : void 0);
|
|
3846
3973
|
const next = {
|
|
3847
3974
|
tool_use_id: toolCallId,
|
|
3848
3975
|
tool_name: patch.tool_name ?? fallback.tool_name,
|
|
@@ -3850,7 +3977,12 @@ function upsertToolResult(agent, toolCallId, patch) {
|
|
|
3850
3977
|
content: patch.content ?? fallback.content,
|
|
3851
3978
|
output: patch.output ?? fallback.output
|
|
3852
3979
|
};
|
|
3853
|
-
agent.tool_results
|
|
3980
|
+
Object.defineProperty(agent.tool_results, toolCallId, {
|
|
3981
|
+
value: next,
|
|
3982
|
+
writable: true,
|
|
3983
|
+
enumerable: true,
|
|
3984
|
+
configurable: true
|
|
3985
|
+
});
|
|
3854
3986
|
}
|
|
3855
3987
|
function existingToolResultValues(existing) {
|
|
3856
3988
|
if (existing) return existing;
|
|
@@ -3891,7 +4023,7 @@ function applyToolResultUpdate(agent, tool, update) {
|
|
|
3891
4023
|
const output = hasOwn(update, "rawOutput") ? deepClone(update.rawOutput) : void 0;
|
|
3892
4024
|
upsertToolResult(agent, update.toolCallId, {
|
|
3893
4025
|
tool_name: tool.name,
|
|
3894
|
-
is_error: statusIndicatesError(status),
|
|
4026
|
+
is_error: status === void 0 ? void 0 : statusIndicatesError(status),
|
|
3895
4027
|
content: output === void 0 ? void 0 : toToolResultContent(output),
|
|
3896
4028
|
output
|
|
3897
4029
|
});
|
|
@@ -4037,20 +4169,6 @@ function recordPromptSubmission(conversation, prompt, timestamp = isoNow(), mess
|
|
|
4037
4169
|
trimConversationForRuntime(conversation);
|
|
4038
4170
|
return promptMessageId;
|
|
4039
4171
|
}
|
|
4040
|
-
function agentMessageHasObservedReply(message) {
|
|
4041
|
-
return message.content.length > 0 || Object.keys(message.tool_results).length > 0;
|
|
4042
|
-
}
|
|
4043
|
-
function hasAgentReplyAfterPrompt(conversation, promptMessageId) {
|
|
4044
|
-
let sawPrompt = false;
|
|
4045
|
-
for (const message of conversation.messages) {
|
|
4046
|
-
if (!sawPrompt) {
|
|
4047
|
-
if (isUserMessage(message) && message.User.id === promptMessageId) sawPrompt = true;
|
|
4048
|
-
continue;
|
|
4049
|
-
}
|
|
4050
|
-
if (isAgentMessage(message) && agentMessageHasObservedReply(message.Agent)) return true;
|
|
4051
|
-
}
|
|
4052
|
-
return false;
|
|
4053
|
-
}
|
|
4054
4172
|
function recordSessionUpdate(conversation, state, notification, timestamp = isoNow(), userMessageId) {
|
|
4055
4173
|
const acpx = ensureAcpxState$1(state);
|
|
4056
4174
|
const update = notification.update;
|
|
@@ -4331,10 +4449,7 @@ function getDesiredModeId(state) {
|
|
|
4331
4449
|
function getDesiredConfigOptions(state) {
|
|
4332
4450
|
const desired = state?.desired_config_options;
|
|
4333
4451
|
if (!desired) return {};
|
|
4334
|
-
return Object.fromEntries(Object.entries(desired).
|
|
4335
|
-
const normalizedConfigId = normalizeModeId(configId);
|
|
4336
|
-
return normalizedConfigId && typeof value === "string" ? [[normalizedConfigId, value]] : [];
|
|
4337
|
-
}));
|
|
4452
|
+
return Object.fromEntries(Object.entries(desired).filter(([, value]) => typeof value === "string"));
|
|
4338
4453
|
}
|
|
4339
4454
|
function setDesiredModeId(record, modeId) {
|
|
4340
4455
|
const acpx = ensureAcpxState(record.acpx);
|
|
@@ -4344,10 +4459,9 @@ function setDesiredModeId(record, modeId) {
|
|
|
4344
4459
|
record.acpx = acpx;
|
|
4345
4460
|
}
|
|
4346
4461
|
function clearDesiredConfigOption(state, configId) {
|
|
4347
|
-
|
|
4348
|
-
if (!normalizedConfigId || !state.desired_config_options) return;
|
|
4462
|
+
if (typeof configId !== "string" || !state.desired_config_options) return;
|
|
4349
4463
|
const desired = { ...state.desired_config_options };
|
|
4350
|
-
delete desired[
|
|
4464
|
+
delete desired[configId];
|
|
4351
4465
|
if (Object.keys(desired).length > 0) state.desired_config_options = desired;
|
|
4352
4466
|
else delete state.desired_config_options;
|
|
4353
4467
|
}
|
|
@@ -4386,7 +4500,7 @@ async function applyRequestedModelIfAdvertised(params) {
|
|
|
4386
4500
|
if (params.models.currentModelId === requestedModel) return { applied: true };
|
|
4387
4501
|
return {
|
|
4388
4502
|
applied: true,
|
|
4389
|
-
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models), params.timeoutMs)
|
|
4503
|
+
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models, params.authority), params.timeoutMs)
|
|
4390
4504
|
};
|
|
4391
4505
|
}
|
|
4392
4506
|
//#endregion
|
|
@@ -4411,9 +4525,9 @@ function applyAcceptedConfigOptions(state, response) {
|
|
|
4411
4525
|
if (!response) return next;
|
|
4412
4526
|
applyConfigOptionsModelState(next, response.configOptions);
|
|
4413
4527
|
if (!next.desired_config_options) return next;
|
|
4414
|
-
const
|
|
4415
|
-
for (const option of response.configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(next.desired_config_options, option.id))
|
|
4416
|
-
if (
|
|
4528
|
+
const desiredEntries = [];
|
|
4529
|
+
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]);
|
|
4530
|
+
if (desiredEntries.length > 0) next.desired_config_options = Object.fromEntries(desiredEntries);
|
|
4417
4531
|
else delete next.desired_config_options;
|
|
4418
4532
|
return next;
|
|
4419
4533
|
}
|
|
@@ -4431,8 +4545,7 @@ function applyModelSelection(state, modelId, response) {
|
|
|
4431
4545
|
function applyConfigOptionSelection(state, configId, value, response, modelConfigId = advertisedModelState(state)?.configId) {
|
|
4432
4546
|
if (configId === modelConfigId || configId === modelStateFromConfigOptions(response.configOptions)?.configId) return applyModelSelection(state, value, response);
|
|
4433
4547
|
const next = cloneSessionAcpxState(state) ?? {};
|
|
4434
|
-
|
|
4435
|
-
else next.desired_config_options = {
|
|
4548
|
+
next.desired_config_options = {
|
|
4436
4549
|
...next.desired_config_options,
|
|
4437
4550
|
[configId]: value
|
|
4438
4551
|
};
|
|
@@ -4443,6 +4556,7 @@ function applyConfigOptionSelection(state, configId, value, response, modelConfi
|
|
|
4443
4556
|
const LOCK_RETRY_MS = 15;
|
|
4444
4557
|
const INCOMPLETE_RESERVATION_GRACE_MS = 15e3;
|
|
4445
4558
|
let lastCreatedAt = 0;
|
|
4559
|
+
const localTurns = /* @__PURE__ */ new Map();
|
|
4446
4560
|
async function readLock(filePath) {
|
|
4447
4561
|
try {
|
|
4448
4562
|
const stat = await fs$1.lstat(filePath, { bigint: true });
|
|
@@ -4456,14 +4570,37 @@ async function readLock(filePath) {
|
|
|
4456
4570
|
throw error;
|
|
4457
4571
|
}
|
|
4458
4572
|
}
|
|
4459
|
-
function
|
|
4573
|
+
function parseLock(payload) {
|
|
4460
4574
|
try {
|
|
4461
|
-
|
|
4462
|
-
return typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4575
|
+
return JSON.parse(payload);
|
|
4463
4576
|
} catch {
|
|
4464
4577
|
return;
|
|
4465
4578
|
}
|
|
4466
4579
|
}
|
|
4580
|
+
async function tryAcquireGuard(filePath, owner, signal) {
|
|
4581
|
+
try {
|
|
4582
|
+
return await acquireFileLock(filePath, {
|
|
4583
|
+
managerKey: "acpx.session-turn",
|
|
4584
|
+
lockPath: `${filePath}.guard`,
|
|
4585
|
+
staleMs: Infinity,
|
|
4586
|
+
timeoutMs: LOCK_RETRY_MS,
|
|
4587
|
+
retry: {
|
|
4588
|
+
retries: 8,
|
|
4589
|
+
minTimeout: 1,
|
|
4590
|
+
maxTimeout: 2,
|
|
4591
|
+
factor: 1,
|
|
4592
|
+
randomize: false
|
|
4593
|
+
},
|
|
4594
|
+
staleRecovery: "remove-if-unchanged",
|
|
4595
|
+
payload: () => owner.payload,
|
|
4596
|
+
shouldReclaim: ({ payload }) => owner.hasExited(payload, false, signal),
|
|
4597
|
+
shouldRemoveStaleLock: ({ payload }) => owner.hasExited(payload, true, signal)
|
|
4598
|
+
});
|
|
4599
|
+
} catch (error) {
|
|
4600
|
+
if (error.code === "file_lock_timeout") return;
|
|
4601
|
+
throw error;
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4467
4604
|
async function removeObservedLock(filePath, observed) {
|
|
4468
4605
|
const current = await readLock(filePath);
|
|
4469
4606
|
if (!current) return true;
|
|
@@ -4473,83 +4610,198 @@ async function removeObservedLock(filePath, observed) {
|
|
|
4473
4610
|
});
|
|
4474
4611
|
return true;
|
|
4475
4612
|
}
|
|
4476
|
-
async function recoverAbandonedLock(filePath) {
|
|
4613
|
+
async function recoverAbandonedLock(filePath, owner, signal) {
|
|
4477
4614
|
const observed = await readLock(filePath);
|
|
4478
|
-
if (!observed) return;
|
|
4479
|
-
const
|
|
4480
|
-
|
|
4481
|
-
if (
|
|
4482
|
-
if (
|
|
4615
|
+
if (!observed) return true;
|
|
4616
|
+
const payload = parseLock(observed.payload);
|
|
4617
|
+
const pid = lockOwnerPid(payload);
|
|
4618
|
+
if (pid && (!await owner.hasExited(payload, false, signal) || !await owner.hasExited(payload, true, signal))) return false;
|
|
4619
|
+
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return false;
|
|
4620
|
+
if (await removeObservedLock(filePath, observed)) {
|
|
4621
|
+
incrementPerfCounter("session.events.stale_lock_recovered");
|
|
4622
|
+
return true;
|
|
4623
|
+
}
|
|
4624
|
+
return false;
|
|
4483
4625
|
}
|
|
4484
4626
|
async function publishLock(filePath, payload, signal) {
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4627
|
+
await withTempFile({
|
|
4628
|
+
rootDir: path.dirname(filePath),
|
|
4629
|
+
prefix: "session-turn",
|
|
4630
|
+
fileName: "lock"
|
|
4631
|
+
}, async (temporaryPath) => {
|
|
4632
|
+
await fs$1.writeFile(temporaryPath, payload, {
|
|
4633
|
+
encoding: "utf8",
|
|
4634
|
+
flag: "wx",
|
|
4635
|
+
mode: 384
|
|
4636
|
+
});
|
|
4637
|
+
signal?.throwIfAborted();
|
|
4638
|
+
try {
|
|
4639
|
+
await fs$1.link(temporaryPath, filePath);
|
|
4640
|
+
} catch (error) {
|
|
4641
|
+
if (!isHardlinkFallbackError(error)) throw error;
|
|
4642
|
+
await fs$1.writeFile(filePath, payload, {
|
|
4492
4643
|
encoding: "utf8",
|
|
4493
4644
|
flag: "wx",
|
|
4494
4645
|
mode: 384
|
|
4495
4646
|
});
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
await fs$1.link(temporaryPath, filePath);
|
|
4499
|
-
} catch (error) {
|
|
4500
|
-
if (!isHardlinkFallbackError(error)) throw error;
|
|
4501
|
-
await fs$1.writeFile(filePath, payload, {
|
|
4502
|
-
encoding: "utf8",
|
|
4503
|
-
flag: "wx",
|
|
4504
|
-
mode: 384
|
|
4505
|
-
});
|
|
4506
|
-
}
|
|
4507
|
-
});
|
|
4508
|
-
} catch (error) {
|
|
4509
|
-
await removePublishedLock(filePath, payload).catch(() => {});
|
|
4510
|
-
throw error;
|
|
4511
|
-
}
|
|
4647
|
+
}
|
|
4648
|
+
});
|
|
4512
4649
|
}
|
|
4513
4650
|
async function removePublishedLock(filePath, payload) {
|
|
4514
4651
|
const current = await readLock(filePath);
|
|
4515
4652
|
if (current?.payload === payload) await removeObservedLock(filePath, current);
|
|
4516
4653
|
}
|
|
4517
|
-
async function
|
|
4654
|
+
async function rejectAcquisition(filePath, receipt, error) {
|
|
4655
|
+
const failures = [error];
|
|
4656
|
+
await disposeTurn(filePath, receipt).catch((cleanupError) => {
|
|
4657
|
+
failures.push(...cleanupError instanceof AggregateError ? cleanupError.errors : [cleanupError]);
|
|
4658
|
+
});
|
|
4659
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn acquisition and cleanup failed");
|
|
4660
|
+
throw error;
|
|
4661
|
+
}
|
|
4662
|
+
async function releaseTurnGuard(receipt) {
|
|
4663
|
+
await receipt.guard?.release();
|
|
4664
|
+
receipt.guard = void 0;
|
|
4665
|
+
}
|
|
4666
|
+
async function removeTurnMarker(filePath, receipt) {
|
|
4667
|
+
if (!receipt.removeMarker) return true;
|
|
4668
|
+
if (receipt.guard && !await receipt.guard.verifyStillHeld()) await releaseTurnGuard(receipt);
|
|
4669
|
+
receipt.guard ??= await tryAcquireGuard(filePath, receipt.owner);
|
|
4670
|
+
if (!receipt.guard) return false;
|
|
4671
|
+
await receipt.removeMarker();
|
|
4672
|
+
receipt.removeMarker = void 0;
|
|
4673
|
+
return true;
|
|
4674
|
+
}
|
|
4675
|
+
async function settleTurn(filePath, receipt) {
|
|
4676
|
+
if (receipt.settled) return true;
|
|
4677
|
+
const pending = receipt.pending ??= (async () => {
|
|
4678
|
+
receipt.failed = false;
|
|
4679
|
+
const failures = [];
|
|
4680
|
+
try {
|
|
4681
|
+
if (!await removeTurnMarker(filePath, receipt)) return false;
|
|
4682
|
+
} catch (error) {
|
|
4683
|
+
failures.push(error);
|
|
4684
|
+
}
|
|
4685
|
+
if (failures.length === 0 || !receipt.admitted) try {
|
|
4686
|
+
await releaseTurnGuard(receipt);
|
|
4687
|
+
} catch (error) {
|
|
4688
|
+
failures.push(error);
|
|
4689
|
+
}
|
|
4690
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn cleanup failed");
|
|
4691
|
+
if (failures.length === 1) throw failures[0];
|
|
4692
|
+
receipt.settled = true;
|
|
4693
|
+
localTurns.delete(filePath);
|
|
4694
|
+
return true;
|
|
4695
|
+
})();
|
|
4696
|
+
try {
|
|
4697
|
+
return await pending;
|
|
4698
|
+
} finally {
|
|
4699
|
+
if (receipt.pending === pending) {
|
|
4700
|
+
receipt.pending = void 0;
|
|
4701
|
+
receipt.failed = !receipt.settled;
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
async function disposeTurn(filePath, receipt) {
|
|
4706
|
+
if (!await settleTurn(filePath, receipt)) throw new Error(`Session turn cleanup is waiting for its guard: ${filePath}`);
|
|
4707
|
+
}
|
|
4708
|
+
async function tryPublishLock(filePath, payload, owner, signal) {
|
|
4709
|
+
for (;;) try {
|
|
4710
|
+
await publishLock(filePath, payload, signal);
|
|
4711
|
+
return true;
|
|
4712
|
+
} catch (error) {
|
|
4713
|
+
if (error.code !== "EEXIST") throw error;
|
|
4714
|
+
if (!await recoverAbandonedLock(filePath, owner, signal)) return false;
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
async function tryAcquireTurnCleanup(filePath, payload, owner, signal) {
|
|
4718
|
+
const previous = localTurns.get(filePath);
|
|
4719
|
+
if (previous) {
|
|
4720
|
+
if (previous.failed) await settleTurn(filePath, previous);
|
|
4721
|
+
return;
|
|
4722
|
+
}
|
|
4723
|
+
const receipt = {
|
|
4724
|
+
owner,
|
|
4725
|
+
removeMarker: async () => await removePublishedLock(filePath, payload),
|
|
4726
|
+
admitted: false,
|
|
4727
|
+
failed: false,
|
|
4728
|
+
settled: false
|
|
4729
|
+
};
|
|
4730
|
+
localTurns.set(filePath, receipt);
|
|
4731
|
+
try {
|
|
4732
|
+
receipt.guard = await tryAcquireGuard(filePath, owner, signal);
|
|
4733
|
+
} catch (error) {
|
|
4734
|
+
localTurns.delete(filePath);
|
|
4735
|
+
throw error;
|
|
4736
|
+
}
|
|
4737
|
+
if (!receipt.guard) {
|
|
4738
|
+
localTurns.delete(filePath);
|
|
4739
|
+
return;
|
|
4740
|
+
}
|
|
4741
|
+
return receipt;
|
|
4742
|
+
}
|
|
4743
|
+
async function tryAcquireTurn(filePath, payload, owner, signal) {
|
|
4744
|
+
const receipt = await tryAcquireTurnCleanup(filePath, payload, owner, signal);
|
|
4745
|
+
if (!receipt) return;
|
|
4518
4746
|
try {
|
|
4747
|
+
signal?.throwIfAborted();
|
|
4748
|
+
if (!await tryPublishLock(filePath, payload, owner, signal)) {
|
|
4749
|
+
await disposeTurn(filePath, receipt);
|
|
4750
|
+
return;
|
|
4751
|
+
}
|
|
4519
4752
|
const observed = await readLock(filePath);
|
|
4520
4753
|
if (observed?.payload !== payload) throw new Error(`Session turn ownership changed before admission: ${filePath}`);
|
|
4521
4754
|
signal?.throwIfAborted();
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4755
|
+
receipt.admitted = true;
|
|
4756
|
+
receipt.removeMarker = async () => await removeObservedLock(filePath, observed);
|
|
4757
|
+
return { [Symbol.asyncDispose]: async () => await disposeTurn(filePath, receipt) };
|
|
4525
4758
|
} catch (error) {
|
|
4526
|
-
await
|
|
4527
|
-
throw error;
|
|
4759
|
+
return await rejectAcquisition(filePath, receipt, error);
|
|
4528
4760
|
}
|
|
4529
4761
|
}
|
|
4530
4762
|
async function acquireSessionTurn(recordId, signal) {
|
|
4531
|
-
const
|
|
4532
|
-
await fs$1.mkdir(path.dirname(
|
|
4763
|
+
const requestedPath = sessionEventLockPath(recordId);
|
|
4764
|
+
await fs$1.mkdir(path.dirname(requestedPath), {
|
|
4533
4765
|
recursive: true,
|
|
4534
4766
|
mode: 448
|
|
4535
4767
|
});
|
|
4768
|
+
const filePath = path.join(await fs$1.realpath(path.dirname(requestedPath)), path.basename(requestedPath));
|
|
4769
|
+
const owner = await createLockOwner({ signal });
|
|
4536
4770
|
lastCreatedAt = Math.max(Date.now(), lastCreatedAt + 1);
|
|
4537
4771
|
const payload = `${JSON.stringify({
|
|
4538
|
-
|
|
4772
|
+
...owner.payload,
|
|
4539
4773
|
created_at: new Date(lastCreatedAt).toISOString()
|
|
4540
4774
|
}, null, 2)}\n`;
|
|
4541
4775
|
for (;;) {
|
|
4542
4776
|
signal?.throwIfAborted();
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
} catch (error) {
|
|
4547
|
-
if (error.code !== "EEXIST") throw error;
|
|
4548
|
-
await recoverAbandonedLock(filePath);
|
|
4549
|
-
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
4550
|
-
}
|
|
4777
|
+
const turn = await tryAcquireTurn(filePath, payload, owner, signal);
|
|
4778
|
+
if (turn) return turn;
|
|
4779
|
+
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
4551
4780
|
}
|
|
4552
|
-
|
|
4781
|
+
}
|
|
4782
|
+
//#endregion
|
|
4783
|
+
//#region src/session/execution/direct-lifetime.ts
|
|
4784
|
+
function ownDirectClient(client, signal) {
|
|
4785
|
+
let closing;
|
|
4786
|
+
const cancel = async () => {
|
|
4787
|
+
await withTimeout(client.cancelActivePrompt(2500), 2500).catch(() => {});
|
|
4788
|
+
await client.close();
|
|
4789
|
+
};
|
|
4790
|
+
const onAbort = () => {
|
|
4791
|
+
closing ??= cancel();
|
|
4792
|
+
closing.catch(() => {});
|
|
4793
|
+
};
|
|
4794
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4795
|
+
if (signal?.aborted) onAbort();
|
|
4796
|
+
const release = () => signal?.removeEventListener("abort", onAbort);
|
|
4797
|
+
return Object.assign(() => {
|
|
4798
|
+
release();
|
|
4799
|
+
closing ??= client.close();
|
|
4800
|
+
return closing;
|
|
4801
|
+
}, { release });
|
|
4802
|
+
}
|
|
4803
|
+
function directExecutionError(error, signal) {
|
|
4804
|
+
return signal?.aborted && error instanceof AgentDisconnectedError ? signal.reason : error;
|
|
4553
4805
|
}
|
|
4554
4806
|
//#endregion
|
|
4555
4807
|
//#region src/session/queue/ipc-server.ts
|
|
@@ -4584,9 +4836,12 @@ function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options =
|
|
|
4584
4836
|
acp: normalized.acp
|
|
4585
4837
|
};
|
|
4586
4838
|
}
|
|
4839
|
+
function armSocketDrainTimeout(socket) {
|
|
4840
|
+
if (process.platform !== "win32") socket.setTimeout(QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
|
|
4841
|
+
}
|
|
4587
4842
|
function writeQueueMessage(socket, message) {
|
|
4588
4843
|
if (socket.destroyed || !socket.writable) return;
|
|
4589
|
-
socket.write(`${JSON.stringify(message)}\n`);
|
|
4844
|
+
if (!socket.write(`${JSON.stringify(message)}\n`) && !socket.timeout) armSocketDrainTimeout(socket);
|
|
4590
4845
|
}
|
|
4591
4846
|
var SessionQueueOwner = class SessionQueueOwner {
|
|
4592
4847
|
server;
|
|
@@ -4730,6 +4985,7 @@ var SessionQueueOwner = class SessionQueueOwner {
|
|
|
4730
4985
|
return true;
|
|
4731
4986
|
}
|
|
4732
4987
|
handleControlRequest(options) {
|
|
4988
|
+
this.taskSockets.add(options.socket);
|
|
4733
4989
|
writeQueueMessage(options.socket, {
|
|
4734
4990
|
type: "accepted",
|
|
4735
4991
|
requestId: options.requestId,
|
|
@@ -4753,6 +5009,7 @@ var SessionQueueOwner = class SessionQueueOwner {
|
|
|
4753
5009
|
if (socket.destroyed || this.drainingSockets.has(socket)) return;
|
|
4754
5010
|
this.taskSockets.delete(socket);
|
|
4755
5011
|
this.drainingSockets.add(socket);
|
|
5012
|
+
armSocketDrainTimeout(socket);
|
|
4756
5013
|
socket.end(() => {
|
|
4757
5014
|
this.drainingSockets.delete(socket);
|
|
4758
5015
|
socket.destroy();
|
|
@@ -4902,6 +5159,10 @@ var SessionQueueOwner = class SessionQueueOwner {
|
|
|
4902
5159
|
handleConnection(socket) {
|
|
4903
5160
|
this.sockets.add(socket);
|
|
4904
5161
|
socket.setEncoding("utf8");
|
|
5162
|
+
socket.on("timeout", () => socket.destroy());
|
|
5163
|
+
socket.on("drain", () => {
|
|
5164
|
+
if (!socket.writableEnded) socket.setTimeout(0);
|
|
5165
|
+
});
|
|
4905
5166
|
socket.once("close", () => {
|
|
4906
5167
|
this.sockets.delete(socket);
|
|
4907
5168
|
this.taskSockets.delete(socket);
|
|
@@ -4990,6 +5251,10 @@ async function replayDesiredMode(params) {
|
|
|
4990
5251
|
await withTimeout(params.client.setSessionMode(params.sessionId, params.desiredModeId, params.replay.authority), params.timeoutMs);
|
|
4991
5252
|
params.replay.acknowledged = true;
|
|
4992
5253
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired mode ${params.desiredModeId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5254
|
+
return {
|
|
5255
|
+
models: advertisedModelState(params.record.acpx),
|
|
5256
|
+
configOptionsPresent: params.record.acpx?.config_options !== void 0
|
|
5257
|
+
};
|
|
4993
5258
|
} catch (error) {
|
|
4994
5259
|
rethrowAuthorityFailure(params.replay, error);
|
|
4995
5260
|
throw new SessionModeReplayError(`Failed to replay saved session mode ${params.desiredModeId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
@@ -5042,7 +5307,7 @@ async function replayDesiredConfigOptions(params) {
|
|
|
5042
5307
|
for (const [configId, value] of Object.entries(params.desiredConfigOptions)) {
|
|
5043
5308
|
if (configId === replacingKey || acceptedConfigOptions && !acceptsSavedConfigValue(acceptedConfigOptions.find((option) => option.id === configId), value)) continue;
|
|
5044
5309
|
try {
|
|
5045
|
-
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value, params.replay.authority), params.timeoutMs);
|
|
5310
|
+
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value, advertisedModelState(params.record.acpx), params.replay.authority), params.timeoutMs);
|
|
5046
5311
|
params.replay.acknowledged = true;
|
|
5047
5312
|
params.record.acpx = applyConfigOptionSelection(params.record.acpx, configId, value, response);
|
|
5048
5313
|
acceptedConfigOptions = response.configOptions;
|
|
@@ -5068,18 +5333,20 @@ function restoreOriginalSessionState(params) {
|
|
|
5068
5333
|
async function connectAndLoadSession(options) {
|
|
5069
5334
|
const record = options.record;
|
|
5070
5335
|
const client = options.client;
|
|
5336
|
+
assertControlAuthority(options.authority);
|
|
5071
5337
|
const sameSessionOnly = requiresSameSession(options.resumePolicy) || Boolean(record.importedFrom);
|
|
5072
5338
|
const originalSessionId = record.acpSessionId;
|
|
5073
5339
|
const originalAgentSessionId = record.agentSessionId;
|
|
5074
5340
|
const originalAcpx = cloneSessionAcpxState(record.acpx);
|
|
5075
|
-
const desiredModeId = getDesiredModeId(record.acpx);
|
|
5341
|
+
const desiredModeId = options.replacingMode ? void 0 : getDesiredModeId(record.acpx);
|
|
5076
5342
|
const desiredModelId = getDesiredModelId(record.acpx);
|
|
5077
5343
|
const desiredConfigOptions = getDesiredConfigOptions(record.acpx);
|
|
5078
5344
|
const storedProcessAlive = isProcessAlive(record.pid);
|
|
5079
5345
|
logReconnectAttempt(record, storedProcessAlive, Boolean(record.pid) && !storedProcessAlive, options.verbose);
|
|
5080
5346
|
const reusingLoadedSession = client.hasReusableSession(record.acpSessionId);
|
|
5081
5347
|
if (reusingLoadedSession) incrementPerfCounter("runtime.connect_and_load.reused_session");
|
|
5082
|
-
else await withTimeout(client.start(), options.timeoutMs);
|
|
5348
|
+
else await withTimeout(client.start(options.authority), options.timeoutMs);
|
|
5349
|
+
assertControlAuthority(options.authority);
|
|
5083
5350
|
options.onClientAvailable?.(options.activeController);
|
|
5084
5351
|
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
5085
5352
|
record.closed = false;
|
|
@@ -5090,9 +5357,11 @@ async function connectAndLoadSession(options) {
|
|
|
5090
5357
|
record,
|
|
5091
5358
|
reusingLoadedSession,
|
|
5092
5359
|
sameSessionOnly,
|
|
5093
|
-
timeoutMs: options.timeoutMs
|
|
5360
|
+
timeoutMs: options.timeoutMs,
|
|
5361
|
+
authority: options.authority
|
|
5094
5362
|
});
|
|
5095
5363
|
const { resumed, loadError, sessionId, createdFreshSession, pendingAgentSessionId, sessionModels } = loadState;
|
|
5364
|
+
applyReconnectedModelState(record, sessionModels, loadState.configOptionsPresent, loadState.legacyModelMetadataPresent, createdFreshSession);
|
|
5096
5365
|
const preferenceReplay = await replaySessionPreferences({
|
|
5097
5366
|
client,
|
|
5098
5367
|
record,
|
|
@@ -5123,14 +5392,18 @@ async function connectAndLoadSession(options) {
|
|
|
5123
5392
|
};
|
|
5124
5393
|
}
|
|
5125
5394
|
function resolveModelsAfterReplay(replay, initialModels) {
|
|
5126
|
-
|
|
5127
|
-
return replay.modelReplay.replayed ? replay.modelReplay.models :
|
|
5395
|
+
const models = resolveModeReplayModels(replay.modeMetadata, initialModels);
|
|
5396
|
+
if (replay.configReplay.replayed) return replay.configReplay.models ?? preserveLegacyModels(replay.modelReplay.replayed ? replay.modelReplay.models : models);
|
|
5397
|
+
return replay.modelReplay.replayed ? replay.modelReplay.models : models;
|
|
5398
|
+
}
|
|
5399
|
+
function resolveModeReplayModels(modeMetadata, initialModels) {
|
|
5400
|
+
return modeMetadata ? modeMetadata.models : initialModels;
|
|
5128
5401
|
}
|
|
5129
5402
|
function preserveLegacyModels(models) {
|
|
5130
5403
|
return models && !models.configId ? models : void 0;
|
|
5131
5404
|
}
|
|
5132
5405
|
function resolveConfigOptionsPresenceAfterReplay(replay, initiallyPresent) {
|
|
5133
|
-
return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.configOptions !== void 0;
|
|
5406
|
+
return initiallyPresent || replay.modeMetadata?.configOptionsPresent === true || replay.configReplay.replayed || replay.modelReplay.configOptions !== void 0;
|
|
5134
5407
|
}
|
|
5135
5408
|
function applyReconnectedModelState(record, sessionModels, configOptionsPresent, legacyModelMetadataPresent, createdFreshSession) {
|
|
5136
5409
|
clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent);
|
|
@@ -5171,12 +5444,13 @@ async function replaySessionPreferences(params) {
|
|
|
5171
5444
|
};
|
|
5172
5445
|
let modelReplay = { replayed: false };
|
|
5173
5446
|
let configReplay = { replayed: false };
|
|
5447
|
+
let modeMetadata;
|
|
5174
5448
|
const replay = createReplayState(params.authority);
|
|
5175
|
-
const replacingModel = replacesModel(params.replacingConfigOption, params.sessionModels, params.originalAcpx);
|
|
5176
5449
|
try {
|
|
5177
5450
|
assertControlAuthority(replay.authority);
|
|
5178
|
-
await replayDesiredMode({
|
|
5451
|
+
modeMetadata = await replayDesiredMode({
|
|
5179
5452
|
client: params.client,
|
|
5453
|
+
record: params.record,
|
|
5180
5454
|
sessionId: params.sessionId,
|
|
5181
5455
|
desiredModeId: params.createdFreshSession ? params.desiredModeId : void 0,
|
|
5182
5456
|
replay,
|
|
@@ -5184,6 +5458,8 @@ async function replaySessionPreferences(params) {
|
|
|
5184
5458
|
timeoutMs: params.timeoutMs,
|
|
5185
5459
|
verbose: params.verbose
|
|
5186
5460
|
});
|
|
5461
|
+
const effectiveModels = resolveModeReplayModels(modeMetadata, params.sessionModels);
|
|
5462
|
+
const replacingModel = replacesModel(params.replacingConfigOption, effectiveModels, params.originalAcpx);
|
|
5187
5463
|
modelReplay = await replayDesiredModel({
|
|
5188
5464
|
client: params.client,
|
|
5189
5465
|
sessionId: params.sessionId,
|
|
@@ -5191,7 +5467,7 @@ async function replaySessionPreferences(params) {
|
|
|
5191
5467
|
replay,
|
|
5192
5468
|
previousSessionId: params.originalSessionId,
|
|
5193
5469
|
record: params.record,
|
|
5194
|
-
models:
|
|
5470
|
+
models: effectiveModels,
|
|
5195
5471
|
createdFreshSession: params.createdFreshSession,
|
|
5196
5472
|
timeoutMs: params.timeoutMs,
|
|
5197
5473
|
verbose: params.verbose,
|
|
@@ -5218,7 +5494,8 @@ async function replaySessionPreferences(params) {
|
|
|
5218
5494
|
reconcileAgentSessionId(params.record, params.pendingAgentSessionId);
|
|
5219
5495
|
return {
|
|
5220
5496
|
modelReplay,
|
|
5221
|
-
configReplay
|
|
5497
|
+
configReplay,
|
|
5498
|
+
modeMetadata
|
|
5222
5499
|
};
|
|
5223
5500
|
}
|
|
5224
5501
|
function settleFailedReplay(params, replay, error) {
|
|
@@ -5250,11 +5527,14 @@ async function loadOrCreateRuntimeSession(params) {
|
|
|
5250
5527
|
record: params.record,
|
|
5251
5528
|
reason: "agent does not support session/resume or session/load"
|
|
5252
5529
|
});
|
|
5253
|
-
return await createFreshRuntimeSession(params.client, params.record, params.timeoutMs);
|
|
5530
|
+
return await createFreshRuntimeSession(params.client, params.record, params.timeoutMs, params.authority);
|
|
5254
5531
|
}
|
|
5255
5532
|
async function loadRuntimeSession(params, resume) {
|
|
5256
5533
|
try {
|
|
5257
|
-
const loadResult = await withTimeout(resume ? params.client.resumeSession(params.record.acpSessionId, params.record.cwd) : params.client.loadSessionWithOptions(params.record.acpSessionId, params.record.cwd, {
|
|
5534
|
+
const loadResult = await withTimeout(resume ? params.client.resumeSession(params.record.acpSessionId, params.record.cwd, params.authority) : params.client.loadSessionWithOptions(params.record.acpSessionId, params.record.cwd, {
|
|
5535
|
+
suppressReplayUpdates: true,
|
|
5536
|
+
...params.authority ? { authority: params.authority } : {}
|
|
5537
|
+
}), params.timeoutMs);
|
|
5258
5538
|
reconcileAgentSessionId(params.record, loadResult.agentSessionId);
|
|
5259
5539
|
applyConfigOptionsToRecord(params.record, loadResult);
|
|
5260
5540
|
return {
|
|
@@ -5267,9 +5547,14 @@ async function loadRuntimeSession(params, resume) {
|
|
|
5267
5547
|
createdFreshSession: false
|
|
5268
5548
|
};
|
|
5269
5549
|
} catch (error) {
|
|
5550
|
+
rethrowCancelledLoad(error, params.authority);
|
|
5270
5551
|
return await recoverRuntimeSessionLoadFailure(params, error);
|
|
5271
5552
|
}
|
|
5272
5553
|
}
|
|
5554
|
+
function rethrowCancelledLoad(error, authority) {
|
|
5555
|
+
const signal = authority?.signal;
|
|
5556
|
+
if (signal?.aborted && (error instanceof AgentDisconnectedError || error === signal.reason)) throw signal.reason;
|
|
5557
|
+
}
|
|
5273
5558
|
async function recoverRuntimeSessionLoadFailure(params, error) {
|
|
5274
5559
|
const loadError = formatErrorMessage(error);
|
|
5275
5560
|
if (params.sameSessionOnly) throw makeSessionResumeRequiredError({
|
|
@@ -5279,12 +5564,12 @@ async function recoverRuntimeSessionLoadFailure(params, error) {
|
|
|
5279
5564
|
});
|
|
5280
5565
|
if (!shouldFallbackToNewSession(error, params.record)) throw error;
|
|
5281
5566
|
return {
|
|
5282
|
-
...await createFreshRuntimeSession(params.client, params.record, params.timeoutMs),
|
|
5567
|
+
...await createFreshRuntimeSession(params.client, params.record, params.timeoutMs, params.authority),
|
|
5283
5568
|
loadError
|
|
5284
5569
|
};
|
|
5285
5570
|
}
|
|
5286
|
-
async function createFreshRuntimeSession(client, record, timeoutMs) {
|
|
5287
|
-
const createdSession = await withTimeout(client.createSession(record.cwd), timeoutMs);
|
|
5571
|
+
async function createFreshRuntimeSession(client, record, timeoutMs, authority) {
|
|
5572
|
+
const createdSession = await withTimeout(client.createSession(record.cwd, authority), timeoutMs);
|
|
5288
5573
|
applyConfigOptionsToRecord(record, createdSession);
|
|
5289
5574
|
return {
|
|
5290
5575
|
sessionId: createdSession.sessionId,
|
|
@@ -5313,7 +5598,7 @@ function createActiveSessionController(params) {
|
|
|
5313
5598
|
return response;
|
|
5314
5599
|
},
|
|
5315
5600
|
setSessionConfigOption: async (configId, value) => {
|
|
5316
|
-
return await params.client.setSessionConfigOption(getActiveSessionId(), configId, value);
|
|
5601
|
+
return await params.client.setSessionConfigOption(getActiveSessionId(), configId, value, advertisedModelState(params.record.acpx));
|
|
5317
5602
|
}
|
|
5318
5603
|
};
|
|
5319
5604
|
}
|
|
@@ -5351,6 +5636,7 @@ async function withConnectedSession(options) {
|
|
|
5351
5636
|
client,
|
|
5352
5637
|
record,
|
|
5353
5638
|
resumePolicy: options.resumePolicy,
|
|
5639
|
+
replacingMode: options.replacingMode,
|
|
5354
5640
|
replacingConfigOption: options.replacingConfigOption,
|
|
5355
5641
|
authority: options.authority,
|
|
5356
5642
|
timeoutMs: options.timeoutMs,
|
|
@@ -5411,6 +5697,7 @@ async function withConnectedSession(options) {
|
|
|
5411
5697
|
function buildDirectConnectedSessionOptions(options, run) {
|
|
5412
5698
|
return {
|
|
5413
5699
|
sessionRecordId: options.sessionRecordId,
|
|
5700
|
+
replacingMode: options.replacingMode,
|
|
5414
5701
|
replacingConfigOption: options.replacingConfigOption,
|
|
5415
5702
|
loadRecord: resolveSessionRecord,
|
|
5416
5703
|
saveRecord: writeSessionRecord,
|
|
@@ -5436,14 +5723,41 @@ function toSessionMutationResult(result) {
|
|
|
5436
5723
|
loadError: result.loadError
|
|
5437
5724
|
};
|
|
5438
5725
|
}
|
|
5726
|
+
async function withOwnedDirectSession(options) {
|
|
5727
|
+
const waiting = new AbortController();
|
|
5728
|
+
const timeoutMs = options.timeoutMs;
|
|
5729
|
+
const timer = timeoutMs != null && timeoutMs > 0 ? setTimeout(() => waiting.abort(new TimeoutError(timeoutMs)), timeoutMs) : void 0;
|
|
5730
|
+
let ownership;
|
|
5731
|
+
let recordId;
|
|
5732
|
+
try {
|
|
5733
|
+
recordId = (await options.loadRecord(options.sessionRecordId)).acpxRecordId;
|
|
5734
|
+
ownership = await acquireSessionTurn(recordId, waiting.signal);
|
|
5735
|
+
} catch (error) {
|
|
5736
|
+
waiting.signal.throwIfAborted();
|
|
5737
|
+
throw error;
|
|
5738
|
+
} finally {
|
|
5739
|
+
clearTimeout(timer);
|
|
5740
|
+
}
|
|
5741
|
+
try {
|
|
5742
|
+
return await withConnectedSession({
|
|
5743
|
+
...options,
|
|
5744
|
+
sessionRecordId: recordId
|
|
5745
|
+
});
|
|
5746
|
+
} finally {
|
|
5747
|
+
await ownership[Symbol.asyncDispose]();
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5439
5750
|
async function runSessionSetModeDirect(options) {
|
|
5440
|
-
return toSessionMutationResult(await
|
|
5751
|
+
return toSessionMutationResult(await withOwnedDirectSession(buildDirectConnectedSessionOptions({
|
|
5752
|
+
...options,
|
|
5753
|
+
replacingMode: true
|
|
5754
|
+
}, async ({ client, sessionId, record }) => {
|
|
5441
5755
|
await withTimeout(client.setSessionMode(sessionId, options.modeId), options.timeoutMs);
|
|
5442
5756
|
setDesiredModeId(record, options.modeId);
|
|
5443
5757
|
})));
|
|
5444
5758
|
}
|
|
5445
5759
|
async function runSessionSetModelDirect(options) {
|
|
5446
|
-
const result = await
|
|
5760
|
+
const result = await withOwnedDirectSession(buildDirectConnectedSessionOptions({
|
|
5447
5761
|
...options,
|
|
5448
5762
|
replacingConfigOption: { key: "model" }
|
|
5449
5763
|
}, async ({ client, sessionId, record }) => {
|
|
@@ -5458,12 +5772,13 @@ async function runSessionSetModelDirect(options) {
|
|
|
5458
5772
|
};
|
|
5459
5773
|
}
|
|
5460
5774
|
async function runSessionSetConfigOptionDirect(options) {
|
|
5461
|
-
const result = await
|
|
5775
|
+
const result = await withOwnedDirectSession(buildDirectConnectedSessionOptions({
|
|
5462
5776
|
...options,
|
|
5463
5777
|
replacingConfigOption: { key: options.configId }
|
|
5464
5778
|
}, async ({ client, sessionId, record }) => {
|
|
5465
|
-
const
|
|
5466
|
-
|
|
5779
|
+
const models = advertisedModelState(record.acpx);
|
|
5780
|
+
const response = await withTimeout(client.setSessionConfigOption(sessionId, options.configId, options.value, models), options.timeoutMs);
|
|
5781
|
+
record.acpx = applyConfigOptionSelection(record.acpx, options.configId, options.value, response, models?.configId);
|
|
5467
5782
|
return response;
|
|
5468
5783
|
}));
|
|
5469
5784
|
return {
|
|
@@ -5482,16 +5797,31 @@ async function cancelSessionPrompt(options) {
|
|
|
5482
5797
|
cancelled: cancelled === true
|
|
5483
5798
|
};
|
|
5484
5799
|
}
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5800
|
+
/**
|
|
5801
|
+
* Dispatches the mode change to a running queue owner. Returns `undefined`
|
|
5802
|
+
* when no owner holds the session, leaving the direct-connection decision to
|
|
5803
|
+
* the caller.
|
|
5804
|
+
*
|
|
5805
|
+
* An owner advertising `persistsControlState` saves the accepted state itself.
|
|
5806
|
+
* Only the older caller-save path still reloads and rewrites the record here,
|
|
5807
|
+
* which is not serialized against another process doing the same.
|
|
5808
|
+
*/
|
|
5809
|
+
async function setSessionModeOnOwner(options) {
|
|
5810
|
+
const submittedToOwner = await trySetModeOnRunningOwner(options.sessionId, options.modeId, options.timeoutMs, options.verbose, options.assertDispatch);
|
|
5811
|
+
if (!submittedToOwner?.value) return;
|
|
5812
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5813
|
+
if (!submittedToOwner.persistsControlState) {
|
|
5488
5814
|
setDesiredModeId(record, options.modeId);
|
|
5489
5815
|
await writeSessionRecord(record);
|
|
5490
|
-
return {
|
|
5491
|
-
record,
|
|
5492
|
-
resumed: false
|
|
5493
|
-
};
|
|
5494
5816
|
}
|
|
5817
|
+
return {
|
|
5818
|
+
record,
|
|
5819
|
+
resumed: false
|
|
5820
|
+
};
|
|
5821
|
+
}
|
|
5822
|
+
async function setSessionMode(options) {
|
|
5823
|
+
const onOwner = await setSessionModeOnOwner(options);
|
|
5824
|
+
if (onOwner) return onOwner;
|
|
5495
5825
|
return await runSessionSetModeDirect({
|
|
5496
5826
|
sessionRecordId: options.sessionId,
|
|
5497
5827
|
modeId: options.modeId,
|
|
@@ -5505,18 +5835,24 @@ async function setSessionMode(options) {
|
|
|
5505
5835
|
verbose: options.verbose
|
|
5506
5836
|
});
|
|
5507
5837
|
}
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5838
|
+
/** Owner-only counterpart of {@link setSessionMode} for the model control. */
|
|
5839
|
+
async function setSessionModelOnOwner(options) {
|
|
5840
|
+
const submittedToOwner = await trySetModelOnRunningOwner(options.sessionId, options.modelId, options.timeoutMs, options.verbose, options.assertDispatch);
|
|
5841
|
+
if (!submittedToOwner) return;
|
|
5842
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5843
|
+
if (!submittedToOwner.persistsControlState) {
|
|
5844
|
+
record.acpx = applyModelSelection(record.acpx, options.modelId, submittedToOwner.value.response);
|
|
5513
5845
|
await writeSessionRecord(record);
|
|
5514
|
-
return {
|
|
5515
|
-
record,
|
|
5516
|
-
response: submittedToOwner.response,
|
|
5517
|
-
resumed: false
|
|
5518
|
-
};
|
|
5519
5846
|
}
|
|
5847
|
+
return {
|
|
5848
|
+
record,
|
|
5849
|
+
response: submittedToOwner.value.response,
|
|
5850
|
+
resumed: false
|
|
5851
|
+
};
|
|
5852
|
+
}
|
|
5853
|
+
async function setSessionModel(options) {
|
|
5854
|
+
const onOwner = await setSessionModelOnOwner(options);
|
|
5855
|
+
if (onOwner) return onOwner;
|
|
5520
5856
|
return await runSessionSetModelDirect({
|
|
5521
5857
|
sessionRecordId: options.sessionId,
|
|
5522
5858
|
modelId: options.modelId,
|
|
@@ -5530,18 +5866,24 @@ async function setSessionModel(options) {
|
|
|
5530
5866
|
verbose: options.verbose
|
|
5531
5867
|
});
|
|
5532
5868
|
}
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5869
|
+
/** Owner-only counterpart of {@link setSessionMode} for the config option control. */
|
|
5870
|
+
async function setSessionConfigOptionOnOwner(options) {
|
|
5871
|
+
const ownerResponse = await trySetConfigOptionOnRunningOwner(options.sessionId, options.configId, options.value, options.timeoutMs, options.verbose, options.assertDispatch);
|
|
5872
|
+
if (!ownerResponse) return;
|
|
5873
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5874
|
+
if (!ownerResponse.persistsControlState) {
|
|
5875
|
+
record.acpx = applyConfigOptionSelection(record.acpx, options.configId, options.value, ownerResponse.value);
|
|
5538
5876
|
await writeSessionRecord(record);
|
|
5539
|
-
return {
|
|
5540
|
-
record,
|
|
5541
|
-
response: ownerResponse,
|
|
5542
|
-
resumed: false
|
|
5543
|
-
};
|
|
5544
5877
|
}
|
|
5878
|
+
return {
|
|
5879
|
+
record,
|
|
5880
|
+
response: ownerResponse.value,
|
|
5881
|
+
resumed: false
|
|
5882
|
+
};
|
|
5883
|
+
}
|
|
5884
|
+
async function setSessionConfigOption(options) {
|
|
5885
|
+
const onOwner = await setSessionConfigOptionOnOwner(options);
|
|
5886
|
+
if (onOwner) return onOwner;
|
|
5545
5887
|
return await runSessionSetConfigOptionDirect({
|
|
5546
5888
|
sessionRecordId: options.sessionId,
|
|
5547
5889
|
configId: options.configId,
|
|
@@ -5623,22 +5965,36 @@ const sessionControlTestInternals = {
|
|
|
5623
5965
|
};
|
|
5624
5966
|
async function closeSession(sessionId) {
|
|
5625
5967
|
const record = await resolveSessionRecord(sessionId);
|
|
5626
|
-
|
|
5627
|
-
await
|
|
5968
|
+
let selectedOwner;
|
|
5969
|
+
await tryCloseSessionOnRunningOwner({
|
|
5970
|
+
sessionId: record.acpxRecordId,
|
|
5971
|
+
onOwnerSelected: (owner) => {
|
|
5972
|
+
selectedOwner = owner;
|
|
5973
|
+
}
|
|
5974
|
+
}).catch(() => {});
|
|
5975
|
+
if (selectedOwner) await terminateQueueOwnerForSession(record.acpxRecordId, selectedOwner);
|
|
5628
5976
|
if (record.pid != null && isProcessAlive(record.pid) && await isLikelyMatchingProcess(record.pid, record.agentCommand)) await terminateProcess(record.pid);
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5977
|
+
const ownership = await acquireSessionTurn(record.acpxRecordId);
|
|
5978
|
+
try {
|
|
5979
|
+
const current = await resolveSessionRecord(record.acpxRecordId);
|
|
5980
|
+
current.pid = void 0;
|
|
5981
|
+
current.closed = true;
|
|
5982
|
+
current.closedAt = isoNow$1();
|
|
5983
|
+
await writeSessionRecord(current);
|
|
5984
|
+
return current;
|
|
5985
|
+
} finally {
|
|
5986
|
+
await ownership[Symbol.asyncDispose]();
|
|
5987
|
+
}
|
|
5634
5988
|
}
|
|
5635
5989
|
//#endregion
|
|
5636
5990
|
//#region src/session/execution/session-management.ts
|
|
5637
5991
|
async function createSessionRecordWithClient(client, options) {
|
|
5638
5992
|
const cwd = absolutePath(options.cwd);
|
|
5639
|
-
await withTimeout(client.start(), options.timeoutMs);
|
|
5993
|
+
await withTimeout(client.start({ signal: options.signal }), options.timeoutMs);
|
|
5994
|
+
assertControlAuthority({ signal: options.signal });
|
|
5640
5995
|
const createdState = options.resumeSessionId ? await resumeSessionRecordWithClient(client, options, cwd) : await createFreshSessionState(client, options, cwd);
|
|
5641
5996
|
const { sessionId, agentSessionId } = createdState;
|
|
5997
|
+
assertControlAuthority({ signal: options.signal });
|
|
5642
5998
|
const lifecycle = client.getAgentLifecycleSnapshot();
|
|
5643
5999
|
const record = {
|
|
5644
6000
|
...createInitialSessionRecord({
|
|
@@ -5660,10 +6016,12 @@ async function createSessionRecordWithClient(client, options) {
|
|
|
5660
6016
|
applyConfigOptionsToRecord(record, createdState.sessionResult);
|
|
5661
6017
|
applyInitialModelSelection(record, createdState.sessionResult.models, options.sessionOptions?.model, createdState.modelApplication);
|
|
5662
6018
|
await writeSessionRecord(record);
|
|
6019
|
+
assertControlAuthority({ signal: options.signal });
|
|
5663
6020
|
return record;
|
|
5664
6021
|
}
|
|
5665
6022
|
async function createFreshSessionState(client, options, cwd) {
|
|
5666
|
-
const createdSession = await withTimeout(client.createSession(cwd), options.timeoutMs);
|
|
6023
|
+
const createdSession = await withTimeout(client.createSession(cwd, { signal: options.signal }), options.timeoutMs);
|
|
6024
|
+
assertControlAuthority({ signal: options.signal });
|
|
5667
6025
|
const modelApplication = await applyRequestedModelIfAdvertised({
|
|
5668
6026
|
client,
|
|
5669
6027
|
sessionId: createdSession.sessionId,
|
|
@@ -5671,7 +6029,8 @@ async function createFreshSessionState(client, options, cwd) {
|
|
|
5671
6029
|
models: createdSession.models,
|
|
5672
6030
|
agentCommand: options.agentCommand,
|
|
5673
6031
|
timeoutMs: options.timeoutMs,
|
|
5674
|
-
onWarning: options.onModelWarning
|
|
6032
|
+
onWarning: options.onModelWarning,
|
|
6033
|
+
authority: { signal: options.signal }
|
|
5675
6034
|
});
|
|
5676
6035
|
return {
|
|
5677
6036
|
sessionId: createdSession.sessionId,
|
|
@@ -5685,8 +6044,9 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
5685
6044
|
const resumeMethod = client.supportsResumeSession() ? "session/resume" : client.supportsLoadSession() ? "session/load" : void 0;
|
|
5686
6045
|
if (!resumeMethod) throw new Error(`Agent command "${options.agentCommand}" does not support session/resume or session/load; cannot resume session ${options.resumeSessionId}`);
|
|
5687
6046
|
try {
|
|
5688
|
-
const resumedSession = await withTimeout(resumeMethod === "session/resume" ? client.resumeSession(options.resumeSessionId, cwd) : client.
|
|
6047
|
+
const resumedSession = await withTimeout(resumeMethod === "session/resume" ? client.resumeSession(options.resumeSessionId, cwd, { signal: options.signal }) : client.loadSessionWithOptions(options.resumeSessionId, cwd, { authority: { signal: options.signal } }), options.timeoutMs);
|
|
5689
6048
|
const sessionModels = resumedSession.models;
|
|
6049
|
+
assertControlAuthority({ signal: options.signal });
|
|
5690
6050
|
const modelApplication = await applyRequestedModelIfAdvertised({
|
|
5691
6051
|
client,
|
|
5692
6052
|
sessionId: options.resumeSessionId,
|
|
@@ -5694,7 +6054,8 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
5694
6054
|
models: sessionModels,
|
|
5695
6055
|
agentCommand: options.agentCommand,
|
|
5696
6056
|
timeoutMs: options.timeoutMs,
|
|
5697
|
-
onWarning: options.onModelWarning
|
|
6057
|
+
onWarning: options.onModelWarning,
|
|
6058
|
+
authority: { signal: options.signal }
|
|
5698
6059
|
});
|
|
5699
6060
|
return {
|
|
5700
6061
|
sessionId: options.resumeSessionId,
|
|
@@ -5707,7 +6068,7 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
5707
6068
|
}
|
|
5708
6069
|
}
|
|
5709
6070
|
async function createSessionWithClient(options) {
|
|
5710
|
-
options.signal
|
|
6071
|
+
assertControlAuthority({ signal: options.signal });
|
|
5711
6072
|
const client = new AcpClient({
|
|
5712
6073
|
agentCommand: options.agentCommand,
|
|
5713
6074
|
agentArgv: options.agentArgv,
|
|
@@ -5723,28 +6084,25 @@ async function createSessionWithClient(options) {
|
|
|
5723
6084
|
verbose: options.verbose,
|
|
5724
6085
|
sessionOptions: options.sessionOptions
|
|
5725
6086
|
});
|
|
5726
|
-
const
|
|
5727
|
-
client.close().catch(() => {});
|
|
5728
|
-
};
|
|
5729
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
6087
|
+
const closeOwnedClient = ownDirectClient(client, options.signal);
|
|
5730
6088
|
try {
|
|
5731
6089
|
const create = async () => {
|
|
5732
|
-
options.signal
|
|
6090
|
+
assertControlAuthority({ signal: options.signal });
|
|
5733
6091
|
const record = await createSessionRecordWithClient(client, options);
|
|
5734
|
-
options.signal
|
|
6092
|
+
assertControlAuthority({ signal: options.signal });
|
|
5735
6093
|
return record;
|
|
5736
6094
|
};
|
|
5737
6095
|
return {
|
|
5738
6096
|
record: options.handleProcessInterrupts === false ? await create() : await withInterrupt(create, async () => {
|
|
5739
|
-
await
|
|
6097
|
+
await closeOwnedClient();
|
|
5740
6098
|
}),
|
|
5741
6099
|
client
|
|
5742
6100
|
};
|
|
5743
6101
|
} catch (error) {
|
|
5744
|
-
await
|
|
5745
|
-
throw error;
|
|
6102
|
+
await closeOwnedClient();
|
|
6103
|
+
throw directExecutionError(error, options.signal);
|
|
5746
6104
|
} finally {
|
|
5747
|
-
|
|
6105
|
+
closeOwnedClient.release();
|
|
5748
6106
|
}
|
|
5749
6107
|
}
|
|
5750
6108
|
async function createSession(options) {
|
|
@@ -5810,7 +6168,7 @@ async function ensureSession(options) {
|
|
|
5810
6168
|
}
|
|
5811
6169
|
}
|
|
5812
6170
|
async function ensureSessionWithOwnership(options, cwd) {
|
|
5813
|
-
options.signal
|
|
6171
|
+
assertControlAuthority({ signal: options.signal });
|
|
5814
6172
|
const gitRoot = findGitRepositoryRoot(cwd);
|
|
5815
6173
|
const walkBoundary = options.walkBoundary ?? gitRoot ?? cwd;
|
|
5816
6174
|
const existing = await findSessionByDirectoryWalk({
|
|
@@ -5951,16 +6309,182 @@ function installPerfMetricsCapture(options = {}) {
|
|
|
5951
6309
|
}
|
|
5952
6310
|
}
|
|
5953
6311
|
//#endregion
|
|
6312
|
+
//#region src/session/queue/control-admission.ts
|
|
6313
|
+
var QueueControlDeadline = class {
|
|
6314
|
+
controller = new AbortController();
|
|
6315
|
+
admissionSignal;
|
|
6316
|
+
expiresAt;
|
|
6317
|
+
expired;
|
|
6318
|
+
timeoutError;
|
|
6319
|
+
rejectExpiry;
|
|
6320
|
+
timer;
|
|
6321
|
+
nativeSettled = false;
|
|
6322
|
+
admitted = false;
|
|
6323
|
+
constructor(timeoutMs, ownerSignal) {
|
|
6324
|
+
this.admissionSignal = ownerSignal ? AbortSignal.any([this.controller.signal, ownerSignal]) : this.controller.signal;
|
|
6325
|
+
this.expiresAt = timeoutMs != null && timeoutMs > 0 ? performance.now() + timeoutMs : void 0;
|
|
6326
|
+
this.timeoutError = this.expiresAt === void 0 ? void 0 : new TimeoutError(timeoutMs);
|
|
6327
|
+
this.expired = new Promise((_resolve, reject) => {
|
|
6328
|
+
this.rejectExpiry = reject;
|
|
6329
|
+
if (this.expiresAt !== void 0) this.timer = setTimeout(() => this.expire(), timeoutMs);
|
|
6330
|
+
});
|
|
6331
|
+
this.expired.catch(() => {});
|
|
6332
|
+
}
|
|
6333
|
+
get signal() {
|
|
6334
|
+
return this.admissionSignal;
|
|
6335
|
+
}
|
|
6336
|
+
get timeoutSignal() {
|
|
6337
|
+
return this.controller.signal;
|
|
6338
|
+
}
|
|
6339
|
+
get remainingMs() {
|
|
6340
|
+
this.assertActive();
|
|
6341
|
+
return this.expiresAt === void 0 || this.nativeSettled ? void 0 : Math.max(1, this.expiresAt - performance.now());
|
|
6342
|
+
}
|
|
6343
|
+
get nativeAdmitted() {
|
|
6344
|
+
return this.admitted;
|
|
6345
|
+
}
|
|
6346
|
+
get authority() {
|
|
6347
|
+
return {
|
|
6348
|
+
signal: this.signal,
|
|
6349
|
+
assertActive: () => {
|
|
6350
|
+
this.assertActive();
|
|
6351
|
+
this.admitted = true;
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6354
|
+
}
|
|
6355
|
+
assertActive() {
|
|
6356
|
+
if (this.expiresAt !== void 0 && performance.now() >= this.expiresAt) this.expire();
|
|
6357
|
+
this.signal.throwIfAborted();
|
|
6358
|
+
}
|
|
6359
|
+
expire() {
|
|
6360
|
+
if (this.nativeSettled || this.timeoutSignal.aborted || !this.timeoutError) return;
|
|
6361
|
+
this.controller.abort(this.timeoutError);
|
|
6362
|
+
this.rejectExpiry(this.timeoutError);
|
|
6363
|
+
}
|
|
6364
|
+
responseSettled() {
|
|
6365
|
+
this.nativeSettled = true;
|
|
6366
|
+
clearTimeout(this.timer);
|
|
6367
|
+
this.timer = void 0;
|
|
6368
|
+
}
|
|
6369
|
+
isExpired(error) {
|
|
6370
|
+
return this.timeoutSignal.aborted && error === this.timeoutSignal.reason;
|
|
6371
|
+
}
|
|
6372
|
+
async wait(operation) {
|
|
6373
|
+
return await Promise.race([operation, this.expired]);
|
|
6374
|
+
}
|
|
6375
|
+
};
|
|
6376
|
+
function deferred() {
|
|
6377
|
+
let resolve;
|
|
6378
|
+
let reject;
|
|
6379
|
+
const promise = new Promise((accept, fail) => {
|
|
6380
|
+
resolve = accept;
|
|
6381
|
+
reject = fail;
|
|
6382
|
+
});
|
|
6383
|
+
promise.catch(() => {});
|
|
6384
|
+
return {
|
|
6385
|
+
promise,
|
|
6386
|
+
resolve,
|
|
6387
|
+
reject
|
|
6388
|
+
};
|
|
6389
|
+
}
|
|
6390
|
+
function drained(operation) {
|
|
6391
|
+
return operation.then(() => {}, () => {});
|
|
6392
|
+
}
|
|
6393
|
+
var QueueOwnerControlAdmission = class {
|
|
6394
|
+
options;
|
|
6395
|
+
idleTail = Promise.resolve();
|
|
6396
|
+
ticket;
|
|
6397
|
+
stopped = new AbortController();
|
|
6398
|
+
pending = 0;
|
|
6399
|
+
constructor(options) {
|
|
6400
|
+
this.options = options;
|
|
6401
|
+
}
|
|
6402
|
+
get hasPending() {
|
|
6403
|
+
return this.pending > 0;
|
|
6404
|
+
}
|
|
6405
|
+
beginPrompt() {
|
|
6406
|
+
const ticket = {
|
|
6407
|
+
ready: deferred(),
|
|
6408
|
+
released: deferred(),
|
|
6409
|
+
rawTail: Promise.resolve(),
|
|
6410
|
+
sealed: false
|
|
6411
|
+
};
|
|
6412
|
+
this.ticket = ticket;
|
|
6413
|
+
return {
|
|
6414
|
+
ticket,
|
|
6415
|
+
priorIdle: this.idleTail
|
|
6416
|
+
};
|
|
6417
|
+
}
|
|
6418
|
+
publish(ticket, controls) {
|
|
6419
|
+
if (!ticket.sealed) ticket.ready.resolve(controls);
|
|
6420
|
+
}
|
|
6421
|
+
seal(ticket) {
|
|
6422
|
+
ticket.sealed = true;
|
|
6423
|
+
ticket.ready.reject(new QueueConnectionError("Prompt ended before controls became ready", {
|
|
6424
|
+
detailCode: "QUEUE_CONTROL_REQUEST_FAILED",
|
|
6425
|
+
origin: "queue",
|
|
6426
|
+
retryable: true
|
|
6427
|
+
}));
|
|
6428
|
+
return ticket.rawTail;
|
|
6429
|
+
}
|
|
6430
|
+
release(ticket) {
|
|
6431
|
+
if (this.ticket === ticket) this.ticket = void 0;
|
|
6432
|
+
ticket.released.resolve();
|
|
6433
|
+
}
|
|
6434
|
+
stopAdmission() {
|
|
6435
|
+
this.stopped.abort(new QueueConnectionError("Queue owner is closing", {
|
|
6436
|
+
detailCode: "QUEUE_OWNER_SHUTTING_DOWN",
|
|
6437
|
+
origin: "queue",
|
|
6438
|
+
retryable: true
|
|
6439
|
+
}));
|
|
6440
|
+
}
|
|
6441
|
+
async drainAdmitted() {
|
|
6442
|
+
await Promise.all([this.idleTail, this.ticket?.rawTail]);
|
|
6443
|
+
}
|
|
6444
|
+
async run(invoke, timeoutMs, replacingConfigOption) {
|
|
6445
|
+
const deadline = new QueueControlDeadline(timeoutMs, this.stopped.signal);
|
|
6446
|
+
this.pending += 1;
|
|
6447
|
+
const operation = this.enqueue(invoke, {
|
|
6448
|
+
deadline,
|
|
6449
|
+
replacingConfigOption
|
|
6450
|
+
});
|
|
6451
|
+
operation.finally(() => {
|
|
6452
|
+
this.pending -= 1;
|
|
6453
|
+
deadline.responseSettled();
|
|
6454
|
+
}).catch(() => {});
|
|
6455
|
+
return await deadline.wait(operation);
|
|
6456
|
+
}
|
|
6457
|
+
async enqueue(invoke, options) {
|
|
6458
|
+
options.deadline.assertActive();
|
|
6459
|
+
const ticket = this.ticket;
|
|
6460
|
+
if (ticket?.sealed) return ticket.released.promise.then(() => this.enqueue(invoke, options));
|
|
6461
|
+
if (ticket) {
|
|
6462
|
+
const operation = ticket.rawTail.then(async () => {
|
|
6463
|
+
const controls = await ticket.ready.promise.catch((error) => {
|
|
6464
|
+
options.deadline.assertActive();
|
|
6465
|
+
throw error;
|
|
6466
|
+
});
|
|
6467
|
+
options.deadline.assertActive();
|
|
6468
|
+
return await invoke(controls, options.deadline);
|
|
6469
|
+
});
|
|
6470
|
+
ticket.rawTail = drained(operation);
|
|
6471
|
+
return operation;
|
|
6472
|
+
}
|
|
6473
|
+
const operation = this.idleTail.then(async () => {
|
|
6474
|
+
options.deadline.assertActive();
|
|
6475
|
+
return await this.options.runIdle(invoke, options);
|
|
6476
|
+
});
|
|
6477
|
+
this.idleTail = drained(operation);
|
|
6478
|
+
return operation;
|
|
6479
|
+
}
|
|
6480
|
+
};
|
|
6481
|
+
//#endregion
|
|
5954
6482
|
//#region src/session/queue/owner-turn-controller.ts
|
|
5955
6483
|
var QueueOwnerTurnController = class {
|
|
5956
|
-
options;
|
|
5957
6484
|
state = "idle";
|
|
5958
6485
|
pendingCancel = false;
|
|
5959
6486
|
activeController;
|
|
5960
6487
|
waitingTurn;
|
|
5961
|
-
constructor(options) {
|
|
5962
|
-
this.options = options;
|
|
5963
|
-
}
|
|
5964
6488
|
get lifecycleState() {
|
|
5965
6489
|
return this.state;
|
|
5966
6490
|
}
|
|
@@ -5992,20 +6516,9 @@ var QueueOwnerTurnController = class {
|
|
|
5992
6516
|
clearActiveController() {
|
|
5993
6517
|
this.activeController = void 0;
|
|
5994
6518
|
}
|
|
5995
|
-
assertCanHandleControlRequest() {
|
|
5996
|
-
if (this.state === "closing") throw new QueueConnectionError("Queue owner is closing", {
|
|
5997
|
-
detailCode: "QUEUE_OWNER_SHUTTING_DOWN",
|
|
5998
|
-
origin: "queue",
|
|
5999
|
-
retryable: true
|
|
6000
|
-
});
|
|
6001
|
-
}
|
|
6002
6519
|
async requestCancel() {
|
|
6003
6520
|
const activeController = this.activeController;
|
|
6004
|
-
if (activeController?.hasActivePrompt())
|
|
6005
|
-
const cancelled = await activeController.requestCancelActivePrompt();
|
|
6006
|
-
if (cancelled) this.pendingCancel = false;
|
|
6007
|
-
return cancelled;
|
|
6008
|
-
}
|
|
6521
|
+
if (activeController?.hasActivePrompt()) return await this.cancelActivePrompt(activeController);
|
|
6009
6522
|
if (this.state === "starting" || this.state === "active") {
|
|
6010
6523
|
this.pendingCancel = true;
|
|
6011
6524
|
this.waitingTurn?.abort();
|
|
@@ -6016,32 +6529,138 @@ var QueueOwnerTurnController = class {
|
|
|
6016
6529
|
async applyPendingCancel() {
|
|
6017
6530
|
const activeController = this.activeController;
|
|
6018
6531
|
if (!this.pendingCancel || !activeController || !activeController.hasActivePrompt()) return false;
|
|
6019
|
-
|
|
6020
|
-
|
|
6532
|
+
return await this.cancelActivePrompt(activeController);
|
|
6533
|
+
}
|
|
6534
|
+
async cancelActivePrompt(activeController) {
|
|
6535
|
+
const turn = this.waitingTurn;
|
|
6536
|
+
const cancellation = activeController.requestCancelActivePrompt();
|
|
6537
|
+
turn?.abort();
|
|
6538
|
+
const cancelled = await cancellation;
|
|
6539
|
+
if (cancelled && this.waitingTurn === turn) this.pendingCancel = false;
|
|
6021
6540
|
return cancelled;
|
|
6022
6541
|
}
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6542
|
+
};
|
|
6543
|
+
//#endregion
|
|
6544
|
+
//#region src/session/execution/owned-controls.ts
|
|
6545
|
+
async function acceptControl(options, invoke, apply, deadline = new QueueControlDeadline()) {
|
|
6546
|
+
deadline.assertActive();
|
|
6547
|
+
const raw = invoke(deadline.authority).then(async (response) => {
|
|
6548
|
+
deadline.responseSettled();
|
|
6549
|
+
apply(response);
|
|
6550
|
+
await options.checkpoint();
|
|
6551
|
+
return response;
|
|
6552
|
+
}, (error) => {
|
|
6553
|
+
deadline.responseSettled();
|
|
6554
|
+
throw error;
|
|
6555
|
+
});
|
|
6556
|
+
try {
|
|
6557
|
+
return await deadline.wait(raw);
|
|
6558
|
+
} catch (error) {
|
|
6559
|
+
if (deadline.isExpired(error) && deadline.nativeAdmitted) {
|
|
6560
|
+
const [retired] = await Promise.allSettled([options.retire(), raw]);
|
|
6561
|
+
if (retired.status === "rejected") throw retired.reason;
|
|
6029
6562
|
}
|
|
6030
|
-
|
|
6563
|
+
throw error;
|
|
6031
6564
|
}
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6565
|
+
}
|
|
6566
|
+
function createOwnedSessionControls(options) {
|
|
6567
|
+
return {
|
|
6568
|
+
setSessionMode: async (modeId, deadline) => {
|
|
6569
|
+
await acceptControl(options, (authority) => options.client.setSessionMode(options.sessionId(), modeId, authority), () => setDesiredModeId(options.record, modeId), deadline);
|
|
6570
|
+
},
|
|
6571
|
+
setSessionModel: async (modelId, deadline) => {
|
|
6572
|
+
const models = advertisedModelState(options.record.acpx);
|
|
6573
|
+
return await acceptControl(options, (authority) => options.client.setSessionModel(options.sessionId(), modelId, models, authority), (response) => {
|
|
6574
|
+
options.record.acpx = applyModelSelection(options.record.acpx, modelId, response);
|
|
6575
|
+
}, deadline);
|
|
6576
|
+
},
|
|
6577
|
+
setSessionConfigOption: async (configId, value, deadline) => {
|
|
6578
|
+
const models = advertisedModelState(options.record.acpx);
|
|
6579
|
+
return await acceptControl(options, (authority) => options.client.setSessionConfigOption(options.sessionId(), configId, value, models, authority), (response) => {
|
|
6580
|
+
options.record.acpx = applyConfigOptionSelection(options.record.acpx, configId, value, response, models?.configId);
|
|
6581
|
+
}, deadline);
|
|
6582
|
+
}
|
|
6583
|
+
};
|
|
6584
|
+
}
|
|
6585
|
+
async function runIdleControlWithRecord(options, record, invoke) {
|
|
6586
|
+
const { client, deadline } = options;
|
|
6587
|
+
deadline.assertActive();
|
|
6588
|
+
let retirement;
|
|
6589
|
+
const retire = () => {
|
|
6590
|
+
retirement ??= client.close().catch((error) => {
|
|
6591
|
+
options.onRetirementFailure();
|
|
6592
|
+
throw error;
|
|
6593
|
+
});
|
|
6594
|
+
return retirement;
|
|
6595
|
+
};
|
|
6596
|
+
const onAbort = () => {
|
|
6597
|
+
retire().catch(() => {});
|
|
6598
|
+
};
|
|
6599
|
+
const checkpoint = async () => {
|
|
6600
|
+
record.lastUsedAt = isoNow$1();
|
|
6601
|
+
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
6602
|
+
await writeSessionRecord(record);
|
|
6603
|
+
};
|
|
6604
|
+
let sessionId = record.acpSessionId;
|
|
6605
|
+
const controls = createOwnedSessionControls({
|
|
6606
|
+
client,
|
|
6607
|
+
record,
|
|
6608
|
+
sessionId: () => sessionId,
|
|
6609
|
+
checkpoint,
|
|
6610
|
+
retire
|
|
6611
|
+
});
|
|
6612
|
+
client.setEventHandlers({ onSessionUpdate: (notification) => {
|
|
6613
|
+
record.acpx = recordSessionUpdate(record, record.acpx, notification);
|
|
6614
|
+
} });
|
|
6615
|
+
deadline.timeoutSignal.addEventListener("abort", onAbort, { once: true });
|
|
6616
|
+
let connected = false;
|
|
6617
|
+
let completed = false;
|
|
6618
|
+
try {
|
|
6619
|
+
await connectAndLoadSession({
|
|
6620
|
+
client,
|
|
6621
|
+
record,
|
|
6622
|
+
resumePolicy: "same-session-only",
|
|
6623
|
+
replacingConfigOption: options.replacingConfigOption,
|
|
6624
|
+
timeoutMs: deadline.remainingMs,
|
|
6625
|
+
authority: deadline.authority,
|
|
6626
|
+
verbose: options.verbose,
|
|
6627
|
+
activeController: {
|
|
6628
|
+
...controls,
|
|
6629
|
+
hasActivePrompt: () => client.hasActivePrompt(),
|
|
6630
|
+
requestCancelActivePrompt: () => client.requestCancelActivePrompt()
|
|
6631
|
+
},
|
|
6632
|
+
onSessionIdResolved: (value) => {
|
|
6633
|
+
sessionId = value;
|
|
6634
|
+
}
|
|
6635
|
+
});
|
|
6636
|
+
connected = true;
|
|
6637
|
+
deadline.assertActive();
|
|
6638
|
+
const value = await invoke(controls, deadline);
|
|
6639
|
+
completed = true;
|
|
6640
|
+
return value;
|
|
6641
|
+
} finally {
|
|
6642
|
+
deadline.timeoutSignal.removeEventListener("abort", onAbort);
|
|
6643
|
+
try {
|
|
6644
|
+
if (!connected) await retire();
|
|
6645
|
+
await retirement;
|
|
6646
|
+
if (!completed || retirement) await checkpoint().catch(() => {});
|
|
6647
|
+
} finally {
|
|
6648
|
+
client.clearEventHandlers();
|
|
6649
|
+
}
|
|
6037
6650
|
}
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6651
|
+
}
|
|
6652
|
+
async function runIdleOwnerControl(options, invoke) {
|
|
6653
|
+
const { acpxRecordId } = await resolveSessionRecord(options.sessionId);
|
|
6654
|
+
const ownership = await acquireSessionTurn(acpxRecordId, options.deadline.signal).catch((error) => {
|
|
6655
|
+
options.deadline.assertActive();
|
|
6656
|
+
throw error;
|
|
6657
|
+
});
|
|
6658
|
+
try {
|
|
6659
|
+
return await runIdleControlWithRecord(options, await resolveSessionRecord(acpxRecordId), invoke);
|
|
6660
|
+
} finally {
|
|
6661
|
+
await ownership[Symbol.asyncDispose]();
|
|
6043
6662
|
}
|
|
6044
|
-
}
|
|
6663
|
+
}
|
|
6045
6664
|
//#endregion
|
|
6046
6665
|
//#region src/session/execution/queue-owner-process.ts
|
|
6047
6666
|
function isNonEmptyStringArray(value) {
|
|
@@ -6059,27 +6678,27 @@ const NODE_TEST_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
|
6059
6678
|
"--test-reporter",
|
|
6060
6679
|
"--test-reporter-destination"
|
|
6061
6680
|
]);
|
|
6062
|
-
const
|
|
6681
|
+
const INSPECTOR_FLAGS = /* @__PURE__ */ new Set([
|
|
6063
6682
|
"--inspect",
|
|
6064
6683
|
"--inspect-brk",
|
|
6684
|
+
"--inspect-wait"
|
|
6685
|
+
]);
|
|
6686
|
+
const INSPECTOR_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
6065
6687
|
"--inspect-port",
|
|
6066
6688
|
"--inspect-publish-uid",
|
|
6067
6689
|
"--debug-port"
|
|
6068
6690
|
]);
|
|
6069
|
-
const INSPECTOR_FLAG_PREFIXES = [
|
|
6070
|
-
"--inspect=",
|
|
6071
|
-
"--inspect-brk=",
|
|
6072
|
-
"--inspect-port=",
|
|
6073
|
-
"--inspect-publish-uid=",
|
|
6074
|
-
"--debug-port="
|
|
6075
|
-
];
|
|
6076
6691
|
function classifyExecArgv(value) {
|
|
6077
|
-
if (value
|
|
6078
|
-
if (
|
|
6079
|
-
return
|
|
6692
|
+
if (NODE_TEST_FLAGS_WITH_VALUE.has(value)) return "drop-with-value";
|
|
6693
|
+
if (NODE_TEST_FLAGS.has(value) || value.startsWith("--test-")) return "drop";
|
|
6694
|
+
return classifyInspectorArgv(value);
|
|
6080
6695
|
}
|
|
6081
|
-
function
|
|
6082
|
-
|
|
6696
|
+
function classifyInspectorArgv(value) {
|
|
6697
|
+
const [name] = value.split("=", 1);
|
|
6698
|
+
if (!name?.startsWith("--")) return "keep";
|
|
6699
|
+
const flag = name.replaceAll("_", "-");
|
|
6700
|
+
if (INSPECTOR_FLAGS_WITH_VALUE.has(flag)) return value.includes("=") ? "drop" : "drop-with-value";
|
|
6701
|
+
return INSPECTOR_FLAGS.has(flag) ? "drop" : "keep";
|
|
6083
6702
|
}
|
|
6084
6703
|
function sanitizeQueueOwnerExecArgv(execArgv = process.execArgv) {
|
|
6085
6704
|
const sanitized = [];
|
|
@@ -6223,20 +6842,27 @@ function responseMetaField(meta) {
|
|
|
6223
6842
|
return meta === void 0 ? {} : { _meta: meta };
|
|
6224
6843
|
}
|
|
6225
6844
|
function recoveredSessionResult(response, conversation, promptMessageId) {
|
|
6226
|
-
recordPromptResponseUsage(conversation, response
|
|
6845
|
+
recordPromptResponseUsage(conversation, response.usage, promptMessageId);
|
|
6227
6846
|
return {
|
|
6228
|
-
stopReason:
|
|
6847
|
+
stopReason: response.stopReason,
|
|
6229
6848
|
source: "session",
|
|
6230
|
-
...responseMetaField(response
|
|
6849
|
+
...responseMetaField(response._meta)
|
|
6231
6850
|
};
|
|
6232
6851
|
}
|
|
6233
|
-
function abortTimedOutRequests(error,
|
|
6234
|
-
if (error instanceof TimeoutError)
|
|
6852
|
+
function abortTimedOutRequests(error, lifetime) {
|
|
6853
|
+
if (error instanceof TimeoutError) lifetime.abort(error);
|
|
6854
|
+
}
|
|
6855
|
+
function promptRequestAuthority(lifetime, authority) {
|
|
6856
|
+
return {
|
|
6857
|
+
signal: authority?.signal ? AbortSignal.any([authority.signal, lifetime.signal]) : lifetime.signal,
|
|
6858
|
+
assertActive: () => assertControlAuthority(authority)
|
|
6859
|
+
};
|
|
6235
6860
|
}
|
|
6236
6861
|
async function runPromptTurn(params) {
|
|
6237
6862
|
let settledResponse;
|
|
6863
|
+
const lifetime = new AbortController();
|
|
6238
6864
|
try {
|
|
6239
|
-
const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation, params.onPermissionRequest, params.authority);
|
|
6865
|
+
const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation, params.onPermissionRequest, promptRequestAuthority(lifetime, params.authority));
|
|
6240
6866
|
promptPromise.then((response) => {
|
|
6241
6867
|
settledResponse = response;
|
|
6242
6868
|
}, () => {});
|
|
@@ -6253,14 +6879,16 @@ async function runPromptTurn(params) {
|
|
|
6253
6879
|
...responseMetaField(response._meta)
|
|
6254
6880
|
};
|
|
6255
6881
|
} catch (error) {
|
|
6256
|
-
abortTimedOutRequests(error,
|
|
6882
|
+
abortTimedOutRequests(error, lifetime);
|
|
6257
6883
|
if (!(error instanceof TimeoutError) || !params.promptMessageId) throw error;
|
|
6258
6884
|
await params.client.waitForSessionUpdatesIdle?.({
|
|
6259
6885
|
idleMs: SESSION_REPLY_IDLE_MS,
|
|
6260
6886
|
timeoutMs: SESSION_REPLY_DRAIN_TIMEOUT_MS
|
|
6261
6887
|
}).catch(() => {});
|
|
6262
|
-
if (
|
|
6888
|
+
if (settledResponse) return recoveredSessionResult(settledResponse, params.conversation, params.promptMessageId);
|
|
6263
6889
|
throw error;
|
|
6890
|
+
} finally {
|
|
6891
|
+
lifetime.abort();
|
|
6264
6892
|
}
|
|
6265
6893
|
}
|
|
6266
6894
|
//#endregion
|
|
@@ -6430,6 +7058,7 @@ async function applyPromptModelIfAdvertised(params) {
|
|
|
6430
7058
|
models: advertisedModelState(params.record.acpx),
|
|
6431
7059
|
agentCommand: params.record.agentCommand,
|
|
6432
7060
|
timeoutMs: params.timeoutMs,
|
|
7061
|
+
authority: { signal: params.signal },
|
|
6433
7062
|
onWarning: params.suppressWarnings ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
|
|
6434
7063
|
});
|
|
6435
7064
|
if (result.applied) params.record.acpx = applyModelSelection(params.record.acpx, requestedModel, result.response);
|
|
@@ -6510,8 +7139,9 @@ function emitPromptDisconnectNotice(snapshot, verbose) {
|
|
|
6510
7139
|
if (!lastExit?.unexpectedDuringPrompt || !verbose) return;
|
|
6511
7140
|
process.stderr.write("[acpx] agent disconnected during prompt (" + lastExit.reason + ", exit=" + lastExit.exitCode + ", signal=" + (lastExit.signal ?? "none") + ")\n");
|
|
6512
7141
|
}
|
|
6513
|
-
async function preparePromptRetry(error, attempt, maxRetries, hasSideEffects, suppressSdkConsoleErrors) {
|
|
7142
|
+
async function preparePromptRetry(error, attempt, maxRetries, hasSideEffects, suppressSdkConsoleErrors, signal) {
|
|
6514
7143
|
if (attempt < maxRetries && !hasSideEffects() && isRetryablePromptError(error)) {
|
|
7144
|
+
signal?.throwIfAborted();
|
|
6515
7145
|
const delayMs = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
6516
7146
|
emitPromptRetryNotice({
|
|
6517
7147
|
error,
|
|
@@ -6520,11 +7150,18 @@ async function preparePromptRetry(error, attempt, maxRetries, hasSideEffects, su
|
|
|
6520
7150
|
maxRetries,
|
|
6521
7151
|
suppressSdkConsoleErrors
|
|
6522
7152
|
});
|
|
6523
|
-
await
|
|
7153
|
+
await setTimeout$1(delayMs, void 0, { signal }).catch((error) => {
|
|
7154
|
+
signal?.throwIfAborted();
|
|
7155
|
+
throw error;
|
|
7156
|
+
});
|
|
7157
|
+
signal?.throwIfAborted();
|
|
6524
7158
|
return !hasSideEffects();
|
|
6525
7159
|
}
|
|
6526
7160
|
return false;
|
|
6527
7161
|
}
|
|
7162
|
+
function isTurnCancellation(error, signal) {
|
|
7163
|
+
return signal?.aborted === true && error === signal.reason;
|
|
7164
|
+
}
|
|
6528
7165
|
function buildQueuedTaskRunOptions(sessionRecordId, task, options, outputFormatter) {
|
|
6529
7166
|
return {
|
|
6530
7167
|
sessionRecordId,
|
|
@@ -6546,7 +7183,9 @@ function buildQueuedTaskRunOptions(sessionRecordId, task, options, outputFormatt
|
|
|
6546
7183
|
sessionOptions: mergeSessionOptions(task.sessionOptions, options.sessionOptions),
|
|
6547
7184
|
onClientAvailable: options.onClientAvailable,
|
|
6548
7185
|
onClientClosed: options.onClientClosed,
|
|
7186
|
+
onClientCloseFailure: options.onClientCloseFailure,
|
|
6549
7187
|
onPromptActive: options.onPromptActive,
|
|
7188
|
+
onPromptFinalizing: options.onPromptFinalizing,
|
|
6550
7189
|
onPromptRequestWritten: () => {
|
|
6551
7190
|
if (task.reportPromptStarted) task.send({
|
|
6552
7191
|
type: "prompt_started",
|
|
@@ -6604,7 +7243,7 @@ async function waitForSessionTurn(options) {
|
|
|
6604
7243
|
"SIGTERM",
|
|
6605
7244
|
"SIGHUP"
|
|
6606
7245
|
];
|
|
6607
|
-
for (const signal of signals) process.once(signal, onInterrupt);
|
|
7246
|
+
if (options.handleProcessInterrupts !== false) for (const signal of signals) process.once(signal, onInterrupt);
|
|
6608
7247
|
const timeoutMs = options.timeoutMs;
|
|
6609
7248
|
const timeout = timeoutMs != null && timeoutMs > 0 ? setTimeout(() => waiting.abort(new TimeoutError(timeoutMs)), timeoutMs) : void 0;
|
|
6610
7249
|
const signal = options.waitSignal ? AbortSignal.any([waiting.signal, options.waitSignal]) : waiting.signal;
|
|
@@ -6615,18 +7254,22 @@ async function waitForSessionTurn(options) {
|
|
|
6615
7254
|
ownership: await acquireSessionTurn(acpxRecordId, signal)
|
|
6616
7255
|
};
|
|
6617
7256
|
} catch (error) {
|
|
6618
|
-
signal
|
|
6619
|
-
throw error;
|
|
7257
|
+
return throwTurnAcquisitionError(error, signal);
|
|
6620
7258
|
} finally {
|
|
6621
7259
|
clearTimeout(timeout);
|
|
6622
7260
|
for (const signal of signals) process.off(signal, onInterrupt);
|
|
6623
7261
|
}
|
|
6624
7262
|
}
|
|
7263
|
+
function throwTurnAcquisitionError(error, signal) {
|
|
7264
|
+
if (signal.aborted && error instanceof Error && error.name === "AbortError" && error.cause === signal.reason) throw signal.reason;
|
|
7265
|
+
throw error;
|
|
7266
|
+
}
|
|
6625
7267
|
async function runSessionPrompt(options) {
|
|
6626
7268
|
let turn;
|
|
6627
7269
|
try {
|
|
6628
7270
|
turn = await waitForSessionTurn(options);
|
|
6629
7271
|
} catch (error) {
|
|
7272
|
+
if (options.ownedSignal) throw error;
|
|
6630
7273
|
if (options.waitSignal?.aborted && error === options.waitSignal.reason) {
|
|
6631
7274
|
const record = await resolveSessionRecord(options.sessionRecordId);
|
|
6632
7275
|
return {
|
|
@@ -6746,8 +7389,10 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6746
7389
|
const record = await measurePerf("session.resolve_prompt_record", async () => {
|
|
6747
7390
|
return await resolveSessionRecord(options.sessionRecordId);
|
|
6748
7391
|
});
|
|
7392
|
+
options.ownedSignal?.throwIfAborted();
|
|
6749
7393
|
const { conversation, promptMessageId } = preparePromptConversation(record, options);
|
|
6750
7394
|
await writeSessionRecord(record);
|
|
7395
|
+
options.ownedSignal?.throwIfAborted();
|
|
6751
7396
|
output.setContext({ sessionId: record.acpxRecordId });
|
|
6752
7397
|
const eventWriter = await measurePerf("session.events.open", async () => {
|
|
6753
7398
|
return await SessionEventWriter.open(record);
|
|
@@ -6790,7 +7435,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6790
7435
|
if (options.verbose) process.stderr.write("[acpx] live session checkpoint failed: " + formatErrorMessage(error) + "\n");
|
|
6791
7436
|
}
|
|
6792
7437
|
});
|
|
6793
|
-
let closeClientOnExit = options.client == null;
|
|
7438
|
+
let closeClientOnExit = options.client == null || options.ownedSignal !== void 0;
|
|
6794
7439
|
const client = options.client ?? new AcpClient({
|
|
6795
7440
|
agentCommand: record.agentCommand,
|
|
6796
7441
|
agentArgv: record.agentArgv,
|
|
@@ -6816,6 +7461,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6816
7461
|
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
6817
7462
|
verbose: options.verbose
|
|
6818
7463
|
});
|
|
7464
|
+
const closeOwnedClient = options.closeProvidedClient ?? ownDirectClient(client, options.ownedSignal);
|
|
6819
7465
|
client.setEventHandlers({
|
|
6820
7466
|
onAcpMessage: (direction, message) => {
|
|
6821
7467
|
pendingMessages.push(message);
|
|
@@ -6852,24 +7498,23 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6852
7498
|
});
|
|
6853
7499
|
let activeSessionIdForControl = record.acpSessionId;
|
|
6854
7500
|
let notifiedClientAvailable = false;
|
|
7501
|
+
let controlRetirement;
|
|
6855
7502
|
const activeController = {
|
|
6856
7503
|
hasActivePrompt: () => client.hasActivePrompt(),
|
|
6857
7504
|
requestCancelActivePrompt: async () => await client.requestCancelActivePrompt(),
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
return response;
|
|
6872
|
-
}
|
|
7505
|
+
...createOwnedSessionControls({
|
|
7506
|
+
client,
|
|
7507
|
+
record,
|
|
7508
|
+
sessionId: () => activeSessionIdForControl,
|
|
7509
|
+
checkpoint: () => liveCheckpoint.checkpoint(),
|
|
7510
|
+
retire: () => {
|
|
7511
|
+
controlRetirement ??= client.close().catch((error) => {
|
|
7512
|
+
options.onClientCloseFailure?.();
|
|
7513
|
+
throw error;
|
|
7514
|
+
});
|
|
7515
|
+
return controlRetirement;
|
|
7516
|
+
}
|
|
7517
|
+
})
|
|
6873
7518
|
};
|
|
6874
7519
|
const flushConnectOutput = (loadError) => {
|
|
6875
7520
|
bufferingConnectOutput = false;
|
|
@@ -6890,6 +7535,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6890
7535
|
verbose: options.verbose,
|
|
6891
7536
|
suppressWarnings: options.suppressSdkConsoleErrors,
|
|
6892
7537
|
activeController,
|
|
7538
|
+
authority: { signal: options.ownedSignal },
|
|
6893
7539
|
onClientAvailable: (controller) => {
|
|
6894
7540
|
options.onClientAvailable?.(controller);
|
|
6895
7541
|
notifiedClientAvailable = true;
|
|
@@ -6933,15 +7579,17 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6933
7579
|
conversation,
|
|
6934
7580
|
promptMessageId,
|
|
6935
7581
|
onPromptRequestWritten: options.onPromptRequestWritten,
|
|
6936
|
-
onPromptStarted: buildPromptStartedHook(attempt)
|
|
7582
|
+
onPromptStarted: buildPromptStartedHook(attempt),
|
|
7583
|
+
authority: { signal: options.waitSignal }
|
|
6937
7584
|
});
|
|
6938
7585
|
});
|
|
6939
7586
|
emitPromptPerfMetric(promptStartedAt, options.verbose);
|
|
6940
7587
|
return response;
|
|
6941
7588
|
};
|
|
6942
7589
|
const handlePromptFailure = async (error, attempt) => {
|
|
7590
|
+
if (isTurnCancellation(error, options.waitSignal)) throw error;
|
|
6943
7591
|
const snapshot = client.getAgentLifecycleSnapshot();
|
|
6944
|
-
if (snapshot.lastExit?.unexpectedDuringPrompt !== true && await preparePromptRetry(error, attempt, options.promptRetries ?? 0, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors)) return;
|
|
7592
|
+
if (snapshot.lastExit?.unexpectedDuringPrompt !== true && await preparePromptRetry(error, attempt, options.promptRetries ?? 0, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors, options.waitSignal)) return;
|
|
6945
7593
|
await failRuntimePrompt(error, snapshot);
|
|
6946
7594
|
};
|
|
6947
7595
|
const failRuntimePrompt = async (error, snapshot) => {
|
|
@@ -6965,10 +7613,18 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6965
7613
|
};
|
|
6966
7614
|
const runPromptWithRetries = async (sessionId) => {
|
|
6967
7615
|
promptTurnActive = true;
|
|
6968
|
-
|
|
6969
|
-
|
|
7616
|
+
try {
|
|
7617
|
+
for (let attempt = 0;; attempt++) {
|
|
7618
|
+
options.waitSignal?.throwIfAborted();
|
|
7619
|
+
try {
|
|
7620
|
+
return await runPromptAttempt(sessionId, attempt);
|
|
7621
|
+
} catch (error) {
|
|
7622
|
+
await handlePromptFailure(error, attempt);
|
|
7623
|
+
}
|
|
7624
|
+
}
|
|
6970
7625
|
} catch (error) {
|
|
6971
|
-
|
|
7626
|
+
if (isTurnCancellation(error, options.waitSignal)) return { stopReason: "cancelled" };
|
|
7627
|
+
throw error;
|
|
6972
7628
|
}
|
|
6973
7629
|
};
|
|
6974
7630
|
const savePromptSuccess = async (response) => {
|
|
@@ -6986,15 +7642,19 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6986
7642
|
return response;
|
|
6987
7643
|
};
|
|
6988
7644
|
const runPrompt = async () => {
|
|
7645
|
+
options.ownedSignal?.throwIfAborted();
|
|
6989
7646
|
const { sessionId: activeSessionId, resumed, loadError } = await connectForPrompt();
|
|
7647
|
+
options.ownedSignal?.throwIfAborted();
|
|
6990
7648
|
await applyPromptModelIfAdvertised({
|
|
6991
7649
|
client,
|
|
6992
7650
|
sessionId: activeSessionId,
|
|
6993
7651
|
requestedModel: options.sessionOptions?.model,
|
|
6994
7652
|
record,
|
|
6995
7653
|
timeoutMs: options.timeoutMs,
|
|
6996
|
-
suppressWarnings: options.suppressSdkConsoleErrors
|
|
7654
|
+
suppressWarnings: options.suppressSdkConsoleErrors,
|
|
7655
|
+
signal: options.ownedSignal
|
|
6997
7656
|
});
|
|
7657
|
+
options.ownedSignal?.throwIfAborted();
|
|
6998
7658
|
output.setContext({ sessionId: record.acpxRecordId });
|
|
6999
7659
|
await liveCheckpoint.checkpoint();
|
|
7000
7660
|
const response = await savePromptSuccess(await runPromptWithRetries(activeSessionId));
|
|
@@ -7012,24 +7672,44 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7012
7672
|
record.lastUsedAt = isoNow$1();
|
|
7013
7673
|
applyConversation(record, conversation);
|
|
7014
7674
|
await flushPendingMessages(false).catch(() => {});
|
|
7015
|
-
if (closeClientOnExit) await
|
|
7675
|
+
if (closeClientOnExit) await closeOwnedClient();
|
|
7016
7676
|
};
|
|
7017
7677
|
const runObservedPrompt = async () => {
|
|
7018
7678
|
try {
|
|
7019
|
-
|
|
7679
|
+
const result = await runWithOptionalInterrupt({
|
|
7020
7680
|
handleProcessInterrupts: options.handleProcessInterrupts,
|
|
7021
7681
|
run: runPrompt,
|
|
7022
7682
|
handleInterrupt
|
|
7023
7683
|
});
|
|
7684
|
+
options.ownedSignal?.throwIfAborted();
|
|
7685
|
+
return result;
|
|
7024
7686
|
} catch (error) {
|
|
7025
|
-
const
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7687
|
+
const failure = directExecutionError(error, options.ownedSignal);
|
|
7688
|
+
const matchedAcpError = acpErrors.match(failure);
|
|
7689
|
+
attachAcpErrorPayload(failure, matchedAcpError);
|
|
7690
|
+
markOutputAlreadyEmitted(failure, matchedAcpError !== void 0 && shouldMarkAcpErrorsEmitted);
|
|
7691
|
+
throw failure;
|
|
7029
7692
|
}
|
|
7030
7693
|
};
|
|
7031
7694
|
const cleanupPrompt = async (attempt) => {
|
|
7695
|
+
const controlDrain = options.onPromptFinalizing?.();
|
|
7696
|
+
const unresolvedPrompt = client.hasUnresolvedPrompt();
|
|
7032
7697
|
const steps = [
|
|
7698
|
+
async () => {
|
|
7699
|
+
if (unresolvedPrompt) await withTimeout(client.cancelActivePrompt(INTERRUPT_CANCEL_WAIT_MS), INTERRUPT_CANCEL_WAIT_MS).catch(() => {});
|
|
7700
|
+
},
|
|
7701
|
+
async () => {
|
|
7702
|
+
const journalFailed = attempt.status === "rejected" && attempt.error instanceof AcpxOperationalError && attempt.error.detailCode === "SESSION_JOURNAL_WRITE_FAILED";
|
|
7703
|
+
if (unresolvedPrompt || closeClientOnExit || journalFailed) try {
|
|
7704
|
+
await closeOwnedClient();
|
|
7705
|
+
} catch (error) {
|
|
7706
|
+
options.onClientCloseFailure?.();
|
|
7707
|
+
throw error;
|
|
7708
|
+
}
|
|
7709
|
+
},
|
|
7710
|
+
async () => {
|
|
7711
|
+
await controlDrain;
|
|
7712
|
+
},
|
|
7033
7713
|
() => {
|
|
7034
7714
|
const duration = stopTotalTimer();
|
|
7035
7715
|
if (options.verbose) process.stderr.write(`[acpx] ${formatPerfMetric("prompt.total", duration)}\n`);
|
|
@@ -7038,10 +7718,6 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7038
7718
|
if (notifiedClientAvailable) options.onClientClosed?.();
|
|
7039
7719
|
},
|
|
7040
7720
|
() => client.clearEventHandlers(),
|
|
7041
|
-
async () => {
|
|
7042
|
-
const journalFailed = attempt.status === "rejected" && attempt.error instanceof AcpxOperationalError && attempt.error.detailCode === "SESSION_JOURNAL_WRITE_FAILED";
|
|
7043
|
-
if (closeClientOnExit || journalFailed) await client.close();
|
|
7044
|
-
},
|
|
7045
7721
|
() => applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot()),
|
|
7046
7722
|
() => applyConversation(record, conversation)
|
|
7047
7723
|
];
|
|
@@ -7063,11 +7739,13 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7063
7739
|
cleanup: cleanupPrompt
|
|
7064
7740
|
});
|
|
7065
7741
|
}
|
|
7066
|
-
async function runOnce(options) {
|
|
7742
|
+
async function runOnce(options, control) {
|
|
7743
|
+
const authority = { signal: control?.signal };
|
|
7067
7744
|
const output = options.outputFormatter;
|
|
7068
7745
|
const shouldMarkAcpErrorsEmitted = rendersAcpErrors(options.errorEmissionPolicy);
|
|
7069
7746
|
let promptTurnActive = false;
|
|
7070
7747
|
let promptTurnHadSideEffects = false;
|
|
7748
|
+
let controlState = {};
|
|
7071
7749
|
const acpErrors = new AcpErrorTracker();
|
|
7072
7750
|
const client = new AcpClient({
|
|
7073
7751
|
agentCommand: options.agentCommand,
|
|
@@ -7088,6 +7766,7 @@ async function runOnce(options) {
|
|
|
7088
7766
|
acpErrors.observe(output, direction, message);
|
|
7089
7767
|
},
|
|
7090
7768
|
onSessionUpdate: (notification) => {
|
|
7769
|
+
if (notification.update.sessionUpdate === "config_option_update") controlState = applyConfigOptionsToState(controlState, notification.update.configOptions);
|
|
7091
7770
|
if (promptTurnActive) promptTurnHadSideEffects = true;
|
|
7092
7771
|
options.onSessionUpdate?.(notification);
|
|
7093
7772
|
},
|
|
@@ -7101,10 +7780,12 @@ async function runOnce(options) {
|
|
|
7101
7780
|
},
|
|
7102
7781
|
sessionOptions: options.sessionOptions
|
|
7103
7782
|
});
|
|
7783
|
+
const closeOwnedClient = ownDirectClient(client, control?.signal);
|
|
7104
7784
|
const runExecPromptAttempt = async (sessionId) => {
|
|
7785
|
+
assertControlAuthority(authority);
|
|
7105
7786
|
acpErrors.reset();
|
|
7106
7787
|
return await measurePerf("runtime.exec.prompt", async () => {
|
|
7107
|
-
return await withTimeout(client.prompt(sessionId, options.prompt), options.timeoutMs);
|
|
7788
|
+
return await withTimeout(client.prompt(sessionId, options.prompt, void 0, void 0, void 0, { signal: control?.signal }), options.timeoutMs);
|
|
7108
7789
|
});
|
|
7109
7790
|
};
|
|
7110
7791
|
const runExecPromptWithRetries = async (sessionId) => {
|
|
@@ -7113,72 +7794,102 @@ async function runOnce(options) {
|
|
|
7113
7794
|
for (let attempt = 0;; attempt++) try {
|
|
7114
7795
|
return await runExecPromptAttempt(sessionId);
|
|
7115
7796
|
} catch (error) {
|
|
7116
|
-
if (await preparePromptRetry(error, attempt, maxRetries, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors)) continue;
|
|
7797
|
+
if (await preparePromptRetry(error, attempt, maxRetries, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors, control?.signal)) continue;
|
|
7117
7798
|
promptTurnActive = false;
|
|
7118
7799
|
throw error;
|
|
7119
7800
|
}
|
|
7120
7801
|
};
|
|
7121
7802
|
try {
|
|
7122
|
-
return await
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7803
|
+
return await runWithOptionalInterrupt({
|
|
7804
|
+
handleProcessInterrupts: control?.handleProcessInterrupts,
|
|
7805
|
+
run: async () => {
|
|
7806
|
+
assertControlAuthority(authority);
|
|
7807
|
+
await measurePerf("runtime.exec.start", async () => {
|
|
7808
|
+
await withTimeout(client.start(authority), options.timeoutMs);
|
|
7809
|
+
});
|
|
7810
|
+
assertControlAuthority(authority);
|
|
7811
|
+
const createdSession = await measurePerf("runtime.exec.create_session", async () => {
|
|
7812
|
+
return await withTimeout(client.createSession(absolutePath(options.cwd), authority), options.timeoutMs);
|
|
7813
|
+
});
|
|
7814
|
+
assertControlAuthority(authority);
|
|
7815
|
+
const sessionId = createdSession.sessionId;
|
|
7816
|
+
if (createdSession.models) applyAdvertisedModelState(controlState, createdSession.models);
|
|
7817
|
+
controlState = applyConfigOptionsToState(controlState, createdSession.configOptions ?? []);
|
|
7818
|
+
const modelApplication = await applyRequestedModelIfAdvertised({
|
|
7819
|
+
client,
|
|
7820
|
+
sessionId,
|
|
7821
|
+
requestedModel: options.sessionOptions?.model,
|
|
7822
|
+
models: createdSession.models,
|
|
7823
|
+
agentCommand: options.agentCommand,
|
|
7824
|
+
timeoutMs: options.timeoutMs,
|
|
7825
|
+
authority,
|
|
7826
|
+
onWarning: options.suppressSdkConsoleErrors ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
|
|
7827
|
+
});
|
|
7828
|
+
if (modelApplication.response) controlState = applyConfigOptionsToState(controlState, modelApplication.response.configOptions);
|
|
7829
|
+
for (const configOption of options.configOptions ?? []) {
|
|
7830
|
+
assertControlAuthority(authority);
|
|
7831
|
+
const response = await withTimeout(client.setSessionConfigOption(sessionId, configOption.configId, configOption.value, advertisedModelState(controlState), authority), options.timeoutMs);
|
|
7832
|
+
controlState = applyConfigOptionsToState(controlState, response.configOptions);
|
|
7833
|
+
}
|
|
7834
|
+
assertControlAuthority(authority);
|
|
7835
|
+
output.setContext({ sessionId });
|
|
7836
|
+
const response = await runExecPromptWithRetries(sessionId);
|
|
7837
|
+
promptTurnActive = false;
|
|
7838
|
+
return toPromptResult(response.stopReason, sessionId, client, response._meta);
|
|
7839
|
+
},
|
|
7840
|
+
handleInterrupt: async () => {
|
|
7841
|
+
await client.cancelActivePrompt(INTERRUPT_CANCEL_WAIT_MS);
|
|
7842
|
+
await closeOwnedClient();
|
|
7843
|
+
}
|
|
7148
7844
|
});
|
|
7149
7845
|
} catch (error) {
|
|
7150
|
-
const
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7846
|
+
const failure = directExecutionError(error, authority.signal);
|
|
7847
|
+
const matchedAcpError = acpErrors.match(failure);
|
|
7848
|
+
attachAcpErrorPayload(failure, matchedAcpError);
|
|
7849
|
+
markOutputAlreadyEmitted(failure, matchedAcpError !== void 0 && shouldMarkAcpErrorsEmitted);
|
|
7850
|
+
throw failure;
|
|
7154
7851
|
} finally {
|
|
7155
|
-
|
|
7852
|
+
try {
|
|
7853
|
+
await closeOwnedClient();
|
|
7854
|
+
} finally {
|
|
7855
|
+
output.flush();
|
|
7856
|
+
}
|
|
7156
7857
|
}
|
|
7157
7858
|
}
|
|
7158
|
-
async function sendSessionDirect(options) {
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7859
|
+
async function sendSessionDirect(options, control) {
|
|
7860
|
+
const closeProvidedClient = control && options.client ? ownDirectClient(options.client, control.signal) : void 0;
|
|
7861
|
+
try {
|
|
7862
|
+
control?.signal.throwIfAborted();
|
|
7863
|
+
return await runSessionPrompt({
|
|
7864
|
+
ownedSignal: control?.signal,
|
|
7865
|
+
closeProvidedClient,
|
|
7866
|
+
waitSignal: control?.signal,
|
|
7867
|
+
handleProcessInterrupts: control?.handleProcessInterrupts,
|
|
7868
|
+
sessionRecordId: options.sessionId,
|
|
7869
|
+
prompt: options.prompt,
|
|
7870
|
+
mcpServers: options.mcpServers,
|
|
7871
|
+
permissionMode: options.permissionMode,
|
|
7872
|
+
resumePolicy: options.resumePolicy,
|
|
7873
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
7874
|
+
permissionPolicy: options.permissionPolicy,
|
|
7875
|
+
authCredentials: options.authCredentials,
|
|
7876
|
+
authPolicy: options.authPolicy,
|
|
7877
|
+
fs: options.fs,
|
|
7878
|
+
terminal: options.terminal,
|
|
7879
|
+
outputFormatter: options.outputFormatter,
|
|
7880
|
+
errorEmissionPolicy: options.errorEmissionPolicy,
|
|
7881
|
+
onAcpMessage: options.onAcpMessage,
|
|
7882
|
+
onSessionUpdate: options.onSessionUpdate,
|
|
7883
|
+
onClientOperation: options.onClientOperation,
|
|
7884
|
+
onPermissionEscalation: options.onPermissionEscalation,
|
|
7885
|
+
timeoutMs: options.timeoutMs,
|
|
7886
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
7887
|
+
verbose: options.verbose,
|
|
7888
|
+
client: options.client
|
|
7889
|
+
});
|
|
7890
|
+
} finally {
|
|
7891
|
+
await closeProvidedClient?.();
|
|
7892
|
+
}
|
|
7182
7893
|
}
|
|
7183
7894
|
//#endregion
|
|
7184
7895
|
//#region src/session/execution/queue-owner-runtime.ts
|
|
@@ -7231,54 +7942,6 @@ function createQueueOwnerSharedClient(options, sessionRecord) {
|
|
|
7231
7942
|
sessionOptions: mergeSessionOptions(options.sessionOptions, sessionOptionsFromRecord(sessionRecord))
|
|
7232
7943
|
});
|
|
7233
7944
|
}
|
|
7234
|
-
function createQueueOwnerTurnController(options) {
|
|
7235
|
-
return new QueueOwnerTurnController({
|
|
7236
|
-
withTimeout: async (run, timeoutMs) => await withTimeout(run(), timeoutMs),
|
|
7237
|
-
setSessionModeFallback: async (modeId, timeoutMs) => {
|
|
7238
|
-
await runSessionSetModeDirect({
|
|
7239
|
-
sessionRecordId: options.sessionId,
|
|
7240
|
-
modeId,
|
|
7241
|
-
mcpServers: options.mcpServers,
|
|
7242
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
7243
|
-
authCredentials: options.authCredentials,
|
|
7244
|
-
authPolicy: options.authPolicy,
|
|
7245
|
-
fs: options.fs,
|
|
7246
|
-
terminal: options.terminal,
|
|
7247
|
-
timeoutMs,
|
|
7248
|
-
verbose: options.verbose
|
|
7249
|
-
});
|
|
7250
|
-
},
|
|
7251
|
-
setSessionModelFallback: async (modelId, timeoutMs) => {
|
|
7252
|
-
return (await runSessionSetModelDirect({
|
|
7253
|
-
sessionRecordId: options.sessionId,
|
|
7254
|
-
modelId,
|
|
7255
|
-
mcpServers: options.mcpServers,
|
|
7256
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
7257
|
-
authCredentials: options.authCredentials,
|
|
7258
|
-
authPolicy: options.authPolicy,
|
|
7259
|
-
fs: options.fs,
|
|
7260
|
-
terminal: options.terminal,
|
|
7261
|
-
timeoutMs,
|
|
7262
|
-
verbose: options.verbose
|
|
7263
|
-
})).response;
|
|
7264
|
-
},
|
|
7265
|
-
setSessionConfigOptionFallback: async (configId, value, timeoutMs) => {
|
|
7266
|
-
return (await runSessionSetConfigOptionDirect({
|
|
7267
|
-
sessionRecordId: options.sessionId,
|
|
7268
|
-
configId,
|
|
7269
|
-
value,
|
|
7270
|
-
mcpServers: options.mcpServers,
|
|
7271
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
7272
|
-
authCredentials: options.authCredentials,
|
|
7273
|
-
authPolicy: options.authPolicy,
|
|
7274
|
-
fs: options.fs,
|
|
7275
|
-
terminal: options.terminal,
|
|
7276
|
-
timeoutMs,
|
|
7277
|
-
verbose: options.verbose
|
|
7278
|
-
})).response;
|
|
7279
|
-
}
|
|
7280
|
-
});
|
|
7281
|
-
}
|
|
7282
7945
|
function logDeferredCancelFailure(error, verbose) {
|
|
7283
7946
|
if (!verbose) return;
|
|
7284
7947
|
process.stderr.write(`[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}\n`);
|
|
@@ -7322,16 +7985,17 @@ function createQueueOwnerShutdownController(params) {
|
|
|
7322
7985
|
let shutdownPromise;
|
|
7323
7986
|
const drainActiveTurn = async () => {
|
|
7324
7987
|
const turn = activeTurn;
|
|
7325
|
-
|
|
7326
|
-
params.turnController.requestCancel().catch((error) => {
|
|
7988
|
+
const pending = Promise.allSettled([turn, params.controls.drainAdmitted()]);
|
|
7989
|
+
if (turn) params.turnController.requestCancel().catch((error) => {
|
|
7327
7990
|
logDeferredCancelFailure(error, params.verbose);
|
|
7328
7991
|
});
|
|
7329
|
-
if (!await settlesWithin(
|
|
7330
|
-
await
|
|
7992
|
+
if (!await settlesWithin(pending, QUEUE_OWNER_ACTIVE_TURN_CANCEL_GRACE_MS)) await params.sharedClient.close().catch(() => {});
|
|
7993
|
+
await pending;
|
|
7331
7994
|
};
|
|
7332
7995
|
const request = () => {
|
|
7333
7996
|
requested = true;
|
|
7334
7997
|
params.stopHeartbeat();
|
|
7998
|
+
params.controls.stopAdmission();
|
|
7335
7999
|
params.getOwner()?.beginShutdown();
|
|
7336
8000
|
activeTurnShutdown ??= drainActiveTurn();
|
|
7337
8001
|
};
|
|
@@ -7342,14 +8006,20 @@ function createQueueOwnerShutdownController(params) {
|
|
|
7342
8006
|
request,
|
|
7343
8007
|
setActiveTurn: (turn) => {
|
|
7344
8008
|
activeTurn = turn;
|
|
8009
|
+
if (requested) activeTurnShutdown = Promise.all([activeTurnShutdown, drainActiveTurn()]).then(() => {});
|
|
7345
8010
|
},
|
|
7346
8011
|
clearActiveTurn: (turn) => {
|
|
7347
8012
|
if (activeTurn === turn) activeTurn = void 0;
|
|
7348
8013
|
},
|
|
8014
|
+
drain: () => {
|
|
8015
|
+
request();
|
|
8016
|
+
return activeTurnShutdown;
|
|
8017
|
+
},
|
|
7349
8018
|
shutdown: () => {
|
|
7350
8019
|
request();
|
|
7351
8020
|
shutdownPromise ??= (async () => {
|
|
7352
8021
|
await activeTurnShutdown;
|
|
8022
|
+
await params.getBackendClose()?.catch(() => {});
|
|
7353
8023
|
await closeQueueOwnerRuntime({
|
|
7354
8024
|
owner: params.getOwner(),
|
|
7355
8025
|
turnController: params.turnController,
|
|
@@ -7368,20 +8038,50 @@ function applyRequestedShutdown(owner, shutdown) {
|
|
|
7368
8038
|
function startQueueOwnerHeartbeat(params) {
|
|
7369
8039
|
if (!params.enabled) return;
|
|
7370
8040
|
return setInterval(() => {
|
|
7371
|
-
|
|
8041
|
+
params.refresh(params.owner.queueDepth());
|
|
7372
8042
|
}, QUEUE_OWNER_HEARTBEAT_INTERVAL_MS);
|
|
7373
8043
|
}
|
|
8044
|
+
function createLeaseHeartbeat(lease, shutdown) {
|
|
8045
|
+
let guardFailure;
|
|
8046
|
+
return {
|
|
8047
|
+
refresh: async (queueDepth) => {
|
|
8048
|
+
if (shutdown.requested) return;
|
|
8049
|
+
await refreshQueueOwnerLease(lease, { queueDepth }).catch((error) => {
|
|
8050
|
+
if (error instanceof QueueLeaseGuardSettlementError) {
|
|
8051
|
+
guardFailure ??= error;
|
|
8052
|
+
shutdown.request();
|
|
8053
|
+
}
|
|
8054
|
+
});
|
|
8055
|
+
},
|
|
8056
|
+
throwIfFailed: () => {
|
|
8057
|
+
if (guardFailure) throw guardFailure;
|
|
8058
|
+
}
|
|
8059
|
+
};
|
|
8060
|
+
}
|
|
7374
8061
|
async function runSessionQueueOwner(options) {
|
|
7375
8062
|
const lease = await tryAcquireQueueOwnerLease(options.sessionId, {
|
|
7376
8063
|
path: options.mcpConfigPath,
|
|
7377
8064
|
fingerprint: options.mcpConfigFingerprint
|
|
7378
8065
|
});
|
|
7379
8066
|
if (!lease) return;
|
|
8067
|
+
let runtimeFailure;
|
|
7380
8068
|
try {
|
|
7381
8069
|
await runQueueOwnerRuntime(options, lease);
|
|
7382
|
-
}
|
|
8070
|
+
} catch (error) {
|
|
8071
|
+
runtimeFailure = { error };
|
|
8072
|
+
}
|
|
8073
|
+
try {
|
|
7383
8074
|
await releaseQueueOwnerLease(lease);
|
|
8075
|
+
} catch (error) {
|
|
8076
|
+
if (runtimeFailure) throw new AggregateError([runtimeFailure.error, error], "Queue owner shutdown failed", { cause: error });
|
|
8077
|
+
throw error;
|
|
7384
8078
|
}
|
|
8079
|
+
if (runtimeFailure) throw runtimeFailure.error;
|
|
8080
|
+
}
|
|
8081
|
+
async function keepOwnerForPendingControls(controls, shutdown) {
|
|
8082
|
+
if (shutdown.requested || !controls.hasPending) return false;
|
|
8083
|
+
await controls.drainAdmitted();
|
|
8084
|
+
return !shutdown.requested;
|
|
7385
8085
|
}
|
|
7386
8086
|
async function runQueueOwnerRuntime(options, lease) {
|
|
7387
8087
|
const sessionRecord = await resolveSessionRecord(options.sessionId);
|
|
@@ -7392,7 +8092,16 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7392
8092
|
const maxQueueDepth = Math.max(1, Math.round(options.maxQueueDepth ?? 16));
|
|
7393
8093
|
const taskPollTimeoutMs = ttlMs === 0 ? void 0 : ttlMs;
|
|
7394
8094
|
const initialTaskPollTimeoutMs = taskPollTimeoutMs == null ? void 0 : Math.max(taskPollTimeoutMs, 1e3);
|
|
7395
|
-
const turnController =
|
|
8095
|
+
const turnController = new QueueOwnerTurnController();
|
|
8096
|
+
let activeControls;
|
|
8097
|
+
let backendCloseRequest;
|
|
8098
|
+
const controls = new QueueOwnerControlAdmission({ runIdle: async (invoke, controlOptions) => await runIdleOwnerControl({
|
|
8099
|
+
...controlOptions,
|
|
8100
|
+
client: sharedClient,
|
|
8101
|
+
sessionId: options.sessionId,
|
|
8102
|
+
verbose: options.verbose,
|
|
8103
|
+
onRetirementFailure: () => shutdown.request()
|
|
8104
|
+
}, invoke) });
|
|
7396
8105
|
const applyPendingCancel = async () => {
|
|
7397
8106
|
return await turnController.applyPendingCancel();
|
|
7398
8107
|
};
|
|
@@ -7402,23 +8111,34 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7402
8111
|
});
|
|
7403
8112
|
};
|
|
7404
8113
|
const setActiveController = (controller) => {
|
|
8114
|
+
activeControls = controller;
|
|
7405
8115
|
turnController.setActiveController(controller);
|
|
7406
8116
|
scheduleApplyPendingCancel();
|
|
7407
8117
|
};
|
|
7408
8118
|
const clearActiveController = () => {
|
|
8119
|
+
activeControls = void 0;
|
|
7409
8120
|
turnController.clearActiveController();
|
|
7410
8121
|
};
|
|
7411
|
-
const closeActiveBackendSession =
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
8122
|
+
const closeActiveBackendSession = (timeoutMs) => {
|
|
8123
|
+
backendCloseRequest ??= Promise.resolve().then(async () => {
|
|
8124
|
+
await shutdown.drain();
|
|
8125
|
+
const latestRecord = await resolveSessionRecord(options.sessionId);
|
|
8126
|
+
if (!sharedClient.supportsCloseSession()) return false;
|
|
8127
|
+
await withTimeout(sharedClient.closeSession(latestRecord.acpSessionId), timeoutMs);
|
|
8128
|
+
return true;
|
|
8129
|
+
});
|
|
8130
|
+
return backendCloseRequest;
|
|
7416
8131
|
};
|
|
7417
8132
|
const runPromptTurn = async (run) => {
|
|
7418
8133
|
const waitSignal = turnController.beginTurn();
|
|
8134
|
+
const { ticket, priorIdle } = controls.beginPrompt();
|
|
8135
|
+
if (shutdown.requested) turnController.requestCancel().catch(() => {});
|
|
7419
8136
|
try {
|
|
7420
|
-
|
|
8137
|
+
await priorIdle;
|
|
8138
|
+
return await run(waitSignal, ticket);
|
|
7421
8139
|
} finally {
|
|
8140
|
+
await controls.seal(ticket);
|
|
8141
|
+
controls.release(ticket);
|
|
7422
8142
|
turnController.endTurn();
|
|
7423
8143
|
}
|
|
7424
8144
|
};
|
|
@@ -7431,6 +8151,8 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7431
8151
|
}
|
|
7432
8152
|
},
|
|
7433
8153
|
turnController,
|
|
8154
|
+
controls,
|
|
8155
|
+
getBackendClose: () => backendCloseRequest,
|
|
7434
8156
|
sharedClient,
|
|
7435
8157
|
sessionId: options.sessionId,
|
|
7436
8158
|
verbose: options.verbose
|
|
@@ -7438,6 +8160,7 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7438
8160
|
const onSignal = () => {
|
|
7439
8161
|
shutdown.request();
|
|
7440
8162
|
};
|
|
8163
|
+
const heartbeat = createLeaseHeartbeat(lease, shutdown);
|
|
7441
8164
|
process.once("SIGINT", onSignal);
|
|
7442
8165
|
process.once("SIGTERM", onSignal);
|
|
7443
8166
|
process.once("SIGHUP", onSignal);
|
|
@@ -7450,17 +8173,17 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7450
8173
|
},
|
|
7451
8174
|
closeSession: async (timeoutMs) => await closeActiveBackendSession(timeoutMs),
|
|
7452
8175
|
setSessionMode: async (modeId, timeoutMs) => {
|
|
7453
|
-
await
|
|
8176
|
+
await controls.run((context, deadline) => context.setSessionMode(modeId, deadline), timeoutMs);
|
|
7454
8177
|
},
|
|
7455
|
-
setSessionModel: async (modelId, timeoutMs) => await
|
|
8178
|
+
setSessionModel: async (modelId, timeoutMs) => await controls.run((context, deadline) => context.setSessionModel(modelId, deadline), timeoutMs, { key: "model" }),
|
|
7456
8179
|
setSessionConfigOption: async (configId, value, timeoutMs) => {
|
|
7457
|
-
return await
|
|
8180
|
+
return await controls.run((context, deadline) => context.setSessionConfigOption(configId, value, deadline), timeoutMs, { key: configId });
|
|
7458
8181
|
}
|
|
7459
8182
|
}, {
|
|
7460
8183
|
maxQueueDepth,
|
|
7461
8184
|
onQueueDepthChanged: (queueDepth) => {
|
|
7462
8185
|
setPerfGauge("queue.owner.depth", queueDepth);
|
|
7463
|
-
|
|
8186
|
+
heartbeat.refresh(queueDepth);
|
|
7464
8187
|
}
|
|
7465
8188
|
});
|
|
7466
8189
|
applyRequestedShutdown(owner, shutdown);
|
|
@@ -7470,19 +8193,22 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7470
8193
|
maxQueueDepth,
|
|
7471
8194
|
verbose: options.verbose
|
|
7472
8195
|
});
|
|
7473
|
-
await
|
|
8196
|
+
await heartbeat.refresh(owner.queueDepth());
|
|
7474
8197
|
heartbeatTimer = startQueueOwnerHeartbeat({
|
|
7475
8198
|
enabled: !shutdown.requested,
|
|
7476
|
-
|
|
8199
|
+
refresh: heartbeat.refresh,
|
|
7477
8200
|
owner
|
|
7478
8201
|
});
|
|
7479
8202
|
let isFirstTask = true;
|
|
7480
8203
|
while (true) {
|
|
7481
8204
|
const pollTimeoutMs = isFirstTask ? initialTaskPollTimeoutMs : taskPollTimeoutMs;
|
|
7482
8205
|
const task = await owner.nextTask(pollTimeoutMs);
|
|
7483
|
-
if (!task)
|
|
8206
|
+
if (!task) {
|
|
8207
|
+
if (await keepOwnerForPendingControls(controls, shutdown)) continue;
|
|
8208
|
+
break;
|
|
8209
|
+
}
|
|
7484
8210
|
isFirstTask = false;
|
|
7485
|
-
const turnPromise = runPromptTurn(async (waitSignal) => {
|
|
8211
|
+
const turnPromise = runPromptTurn(async (waitSignal, ticket) => {
|
|
7486
8212
|
try {
|
|
7487
8213
|
await runQueuedTask(options.sessionId, task, {
|
|
7488
8214
|
sharedClient,
|
|
@@ -7496,7 +8222,10 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7496
8222
|
sessionOptions: options.sessionOptions,
|
|
7497
8223
|
onClientAvailable: setActiveController,
|
|
7498
8224
|
onClientClosed: clearActiveController,
|
|
8225
|
+
onClientCloseFailure: shutdown.request,
|
|
8226
|
+
onPromptFinalizing: () => controls.seal(ticket),
|
|
7499
8227
|
onPromptActive: async () => {
|
|
8228
|
+
if (activeControls) controls.publish(ticket, activeControls);
|
|
7500
8229
|
turnController.markPromptActive();
|
|
7501
8230
|
await applyPendingCancel();
|
|
7502
8231
|
},
|
|
@@ -7520,7 +8249,9 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7520
8249
|
process.off("SIGTERM", onSignal);
|
|
7521
8250
|
process.off("SIGHUP", onSignal);
|
|
7522
8251
|
await shutdown.shutdown();
|
|
8252
|
+
await lease.updates;
|
|
7523
8253
|
}
|
|
8254
|
+
heartbeat.throwIfFailed();
|
|
7524
8255
|
}
|
|
7525
8256
|
async function sendSession(options) {
|
|
7526
8257
|
options.signal?.throwIfAborted();
|
|
@@ -7560,6 +8291,6 @@ async function sendSession(options) {
|
|
|
7560
8291
|
}
|
|
7561
8292
|
const queueOwnerRuntimeTestInternals = { queueOwnerExitIsFatal };
|
|
7562
8293
|
//#endregion
|
|
7563
|
-
export {
|
|
8294
|
+
export { getAcpxVersion as $, applyInitialModelSelection as A, cloneSessionAcpxState as B, setSessionModeOnOwner as C, connectAndLoadSession as D, withConnectedSession as E, sessionOptionsFromRecord as F, recordSessionUpdate as G, createSessionConversation as H, applyConversation as I, AcpClient as J, trimConversationForRuntime as K, applyLifecycleSnapshotToRecord as L, applyRequestedModelIfAdvertised as M, setDesiredModeId as N, applyConfigOptionSelection as O, persistSessionOptions as P, isRequestedModelUnsupportedError as Q, createInitialSessionRecord as R, setSessionMode as S, setSessionModelOnOwner as T, recordClientOperation as U, cloneSessionConversation as V, recordPromptSubmission as W, REQUESTED_MODEL_UNSUPPORTED_REASONS as X, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as Y, RequestedModelUnsupportedError as Z, cancelSessionPrompt as _, runQueuedTask as a, InterruptedError as at, setSessionConfigOption as b, LiveSessionCheckpoint as c, withInterrupt as ct, flushPerfMetricsCapture as d, normalizeOutputError as dt, permissionModeSatisfies as et, installPerfMetricsCapture as f, listAgentSessions as g, ensureSession as h, runOnce as i, isLegacyZedCodexAcpInvocation as it, applyModelSelection as j, applyConfigOptionsToRecord as k, runPromptTurn as l, withTimeout as lt, createSessionWithClient as m, runSessionQueueOwner as n, normalizeQueueOwnerTtlMs as nt, sendSessionDirect as o, TimeoutError as ot, createSession as p, advertisedModelState as q, sendSession as r, listSessionEvents as rt, DISCARD_OUTPUT_FORMATTER as s, assertControlAuthority as st, queueOwnerRuntimeTestInternals as t, DEFAULT_QUEUE_OWNER_TTL_MS as tt, buildQueueOwnerArgOverride as u, exitCodeForOutputErrorCode as ut, closeSession as v, setSessionModel as w, setSessionConfigOptionOnOwner as x, sessionControlTestInternals as y, reconcileAgentSessionId as z };
|
|
7564
8295
|
|
|
7565
|
-
//# sourceMappingURL=queue-owner-runtime-
|
|
8296
|
+
//# sourceMappingURL=queue-owner-runtime-CLwjMYNL.js.map
|