acpx 0.17.1 → 0.18.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-DdKB-zia.js} +16 -17
- package/dist/{agent-registry-evUoJINz.js.map → agent-registry-DdKB-zia.js.map} +1 -1
- package/dist/agent-registry.d.ts.map +1 -1
- package/dist/agent-registry.js +1 -1
- package/dist/{cli-B2ycKzaO.js → cli-8mVHeUXh.js} +5 -5
- package/dist/{cli-B2ycKzaO.js.map → cli-8mVHeUXh.js.map} +1 -1
- package/dist/cli.js +122 -94
- package/dist/cli.js.map +1 -1
- package/dist/{client-CkbBoSGd.d.ts → client-DeNKZZcu.d.ts} +14 -12
- package/dist/client-DeNKZZcu.d.ts.map +1 -0
- package/dist/{flows-xGyhmoHO.js → flows-WOQNzCMC.js} +5 -5
- package/dist/{flows-xGyhmoHO.js.map → flows-WOQNzCMC.js.map} +1 -1
- package/dist/flows.d.ts +1 -1
- package/dist/flows.js +1 -1
- package/dist/{invocation-options-eZEBh4vd.js → invocation-options-B6d7TI-y.js} +7 -6
- package/dist/{invocation-options-eZEBh4vd.js.map → invocation-options-B6d7TI-y.js.map} +1 -1
- package/dist/{ipc-D2rstexT.js → ipc-BJrvfNXr.js} +305 -88
- package/dist/ipc-BJrvfNXr.js.map +1 -0
- package/dist/{output-D2xL6wdp.js → output-CL18YmsG.js} +9 -6
- package/dist/output-CL18YmsG.js.map +1 -0
- package/dist/{queue-owner-runtime-Dgv3e2Tp.js → queue-owner-runtime-Cr0dWJuL.js} +952 -433
- package/dist/queue-owner-runtime-Cr0dWJuL.js.map +1 -0
- package/dist/runtime.d.ts +39 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +272 -154
- package/dist/runtime.js.map +1 -1
- package/dist/{session-watch-CTe9Bnbc.js → session-watch-B8Q_TsGf.js} +4 -4
- package/dist/{session-watch-CTe9Bnbc.js.map → session-watch-B8Q_TsGf.js.map} +1 -1
- package/dist/{watch-DxuXEc8n.js → watch-Dp73ph8h.js} +2 -2
- package/dist/{watch-DxuXEc8n.js.map → watch-Dp73ph8h.js.map} +1 -1
- package/package.json +1 -1
- package/skills/acpx/SKILL.md +26 -7
- package/dist/client-CkbBoSGd.d.ts.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
|
@@ -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 setPerfGauge, A as isoNow$1, At as AcpxOperationalError, Bt as QueueConnectionError, C as promptToDisplayText, D as findGitRepositoryRoot, E as absolutePath, Ft as ClaudeAcpSessionCreateTimeoutError, Gt as SessionResumeRequiredError, H as normalizeAgentSessionId, Ht as SessionModeReplayError, I as resolveSessionRecord, It as CopilotAcpUnsupportedError, J as getPerfMetricsSnapshot, Kt as UnsupportedPromptContentError, L as writeSessionRecord, Lt as GeminiAcpStartupTimeoutError, Mt as AgentSpawnError, N as normalizeName, Nt as AgentStartupError, Pt as AuthPolicyError, Q as resetPerfMetrics, Rt as PermissionDeniedError, St as EXIT_CODES, Tt as OUTPUT_ERROR_ORIGINS, U as SESSION_JOURNAL_SCHEMA, Ut as SessionModelReplayError, V as extractAgentSessionId, Vt as SessionConfigOptionReplayError, W as SessionJournalReader, X as measurePerf, Y as incrementPerfCounter, Z as recordPerfDuration, _ as waitMs$1, _t as isProcessDefinitelyDead, a as trySetModelOnRunningOwner, b as getUnsupportedPromptContentMessage, bt as isAcpResourceNotFoundError, c as queueRequestExceedsLimit, dt as sessionEventActivePath, et as startPerfTimer, f as refreshQueueOwnerLease, ft as sessionEventLockPath, g as tryAcquireQueueOwnerLease, gt as isProcessAlive, h as terminateQueueOwnerForSession, i as trySetModeOnRunningOwner, it as isSessionUpdateNotification, jt as AgentDisconnectedError, k as findSessionByDirectoryWalk, l as parseQueueRequest, lt as defaultSessionEventLog, m as terminateProcess, n as tryCloseSessionOnRunningOwner, nt as isAcpJsonRpcMessage, o as trySubmitToRunningOwner, p as releaseQueueOwnerLease, pt as sessionEventSegmentPath, q as formatPerfMetric, r as trySetConfigOptionOnRunningOwner, rt as isAcpMessageObject, s as queueRequestByteLimit, t as tryCancelOnRunningOwner, ut as sessionBaseDir, v as QueueLeaseGuardSettlementError, vt as extractAcpError, w as textPrompt, wt as OUTPUT_ERROR_CODES, yt as formatUnknownErrorMessage, zt as PermissionPromptUnavailableError } from "./ipc-BJrvfNXr.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-DdKB-zia.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]);
|
|
@@ -485,21 +486,57 @@ function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
|
485
486
|
}
|
|
486
487
|
//#endregion
|
|
487
488
|
//#region src/permission-prompt.ts
|
|
489
|
+
let promptQueue = Promise.resolve();
|
|
488
490
|
async function promptForPermission(options) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
491
|
+
options.signal?.throwIfAborted();
|
|
492
|
+
const result = promptQueue.then(() => askPermission(options));
|
|
493
|
+
promptQueue = result.then(() => {}, () => {});
|
|
494
|
+
const signal = options.signal;
|
|
495
|
+
if (!signal) return await result;
|
|
496
|
+
let onAbort = () => {};
|
|
497
|
+
const aborted = new Promise((_, reject) => {
|
|
498
|
+
onAbort = () => reject(signal.reason);
|
|
499
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
500
|
+
});
|
|
501
|
+
try {
|
|
502
|
+
return await Promise.race([result, aborted]);
|
|
503
|
+
} finally {
|
|
504
|
+
signal.removeEventListener("abort", onAbort);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async function askPermission(options) {
|
|
508
|
+
options.signal?.throwIfAborted();
|
|
509
|
+
if (!canPrompt()) return false;
|
|
510
|
+
writePromptDetails(options);
|
|
492
511
|
const rl = readline.createInterface({
|
|
493
512
|
input: process.stdin,
|
|
494
513
|
output: process.stderr
|
|
495
514
|
});
|
|
515
|
+
const questionController = new AbortController();
|
|
516
|
+
const signal = options.signal ? AbortSignal.any([options.signal, questionController.signal]) : questionController.signal;
|
|
517
|
+
let onClose = () => {};
|
|
518
|
+
const closed = new Promise((resolve) => {
|
|
519
|
+
onClose = () => resolve(void 0);
|
|
520
|
+
rl.once("close", onClose);
|
|
521
|
+
});
|
|
496
522
|
try {
|
|
497
|
-
const
|
|
523
|
+
const answer = await Promise.race([rl.question(options.prompt, { signal }), closed]);
|
|
524
|
+
options.signal?.throwIfAborted();
|
|
525
|
+
const normalized = answer?.trim().toLowerCase();
|
|
498
526
|
return normalized === "y" || normalized === "yes";
|
|
499
527
|
} finally {
|
|
528
|
+
rl.removeListener("close", onClose);
|
|
529
|
+
questionController.abort();
|
|
500
530
|
rl.close();
|
|
501
531
|
}
|
|
502
532
|
}
|
|
533
|
+
function canPrompt() {
|
|
534
|
+
return process.stdin.isTTY && process.stderr.isTTY && !process.stdin.readableEnded && !process.stdin.destroyed;
|
|
535
|
+
}
|
|
536
|
+
function writePromptDetails(options) {
|
|
537
|
+
if (options.header) process.stderr.write(`\n${options.header}\n`);
|
|
538
|
+
if (options.details && options.details.trim().length > 0) process.stderr.write(`${options.details}\n`);
|
|
539
|
+
}
|
|
503
540
|
//#endregion
|
|
504
541
|
//#region src/filesystem.ts
|
|
505
542
|
const WRITE_PREVIEW_MAX_LINES = 16;
|
|
@@ -515,11 +552,12 @@ function toWritePreview(content) {
|
|
|
515
552
|
if (preview.length > WRITE_PREVIEW_MAX_CHARS) preview = `${preview.slice(0, 1197)}...`;
|
|
516
553
|
return preview;
|
|
517
554
|
}
|
|
518
|
-
async function defaultConfirmWrite(filePath, preview) {
|
|
555
|
+
async function defaultConfirmWrite(filePath, preview, signal) {
|
|
519
556
|
return await promptForPermission({
|
|
520
557
|
header: `[permission] Allow write to ${filePath}?`,
|
|
521
558
|
details: preview,
|
|
522
|
-
prompt: "Allow write? (y/N) "
|
|
559
|
+
prompt: "Allow write? (y/N) ",
|
|
560
|
+
signal
|
|
523
561
|
});
|
|
524
562
|
}
|
|
525
563
|
function canPromptForPermission$2() {
|
|
@@ -545,7 +583,8 @@ var FileSystemHandlers = class {
|
|
|
545
583
|
this.permissionMode = permissionMode;
|
|
546
584
|
this.nonInteractivePermissions = nonInteractivePermissions ?? "deny";
|
|
547
585
|
}
|
|
548
|
-
async readTextFile(params) {
|
|
586
|
+
async readTextFile(params, authority) {
|
|
587
|
+
assertControlAuthority(authority);
|
|
549
588
|
const filePath = this.resolvePathWithinRoot(params.path);
|
|
550
589
|
const summary = `read_text_file: ${filePath}`;
|
|
551
590
|
this.emitOperation({
|
|
@@ -557,7 +596,8 @@ var FileSystemHandlers = class {
|
|
|
557
596
|
});
|
|
558
597
|
try {
|
|
559
598
|
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(
|
|
599
|
+
const content = await (await this.getWorkspace()).readText(filePath);
|
|
600
|
+
assertControlAuthority(authority);
|
|
561
601
|
const sliced = this.sliceContent(content, params.line, params.limit);
|
|
562
602
|
this.emitOperation({
|
|
563
603
|
method: "fs/read_text_file",
|
|
@@ -579,7 +619,8 @@ var FileSystemHandlers = class {
|
|
|
579
619
|
throw error;
|
|
580
620
|
}
|
|
581
621
|
}
|
|
582
|
-
async writeTextFile(params) {
|
|
622
|
+
async writeTextFile(params, authority) {
|
|
623
|
+
assertControlAuthority(authority);
|
|
583
624
|
const filePath = this.resolvePathWithinRoot(params.path);
|
|
584
625
|
const preview = toWritePreview(params.content);
|
|
585
626
|
const summary = `write_text_file: ${filePath}`;
|
|
@@ -591,9 +632,17 @@ var FileSystemHandlers = class {
|
|
|
591
632
|
timestamp: nowIso$1()
|
|
592
633
|
});
|
|
593
634
|
try {
|
|
594
|
-
|
|
595
|
-
|
|
635
|
+
const approved = await this.isWriteApproved(filePath, preview, authority?.signal);
|
|
636
|
+
assertControlAuthority(authority);
|
|
637
|
+
if (!approved) throw new PermissionDeniedError("Permission denied for fs/write_text_file");
|
|
638
|
+
const workspace = await this.getWorkspace();
|
|
639
|
+
const target = await workspace.resolve(filePath);
|
|
640
|
+
const file = await workspace.openWritable(target, {
|
|
641
|
+
mode: 438,
|
|
642
|
+
assertBeforeMutation: () => assertControlAuthority(authority)
|
|
643
|
+
});
|
|
596
644
|
try {
|
|
645
|
+
assertControlAuthority(authority);
|
|
597
646
|
await file.handle.writeFile(params.content, "utf8");
|
|
598
647
|
} finally {
|
|
599
648
|
await file.handle.close();
|
|
@@ -618,17 +667,17 @@ var FileSystemHandlers = class {
|
|
|
618
667
|
throw error;
|
|
619
668
|
}
|
|
620
669
|
}
|
|
621
|
-
async isWriteApproved(filePath, preview) {
|
|
670
|
+
async isWriteApproved(filePath, preview, signal) {
|
|
622
671
|
if (this.permissionMode === "approve-all") return true;
|
|
623
672
|
if (this.permissionMode === "deny-all") return false;
|
|
624
673
|
if (this.usesDefaultConfirmWrite && this.nonInteractivePermissions === "fail" && !canPromptForPermission$2()) throw new PermissionPromptUnavailableError();
|
|
625
|
-
return await this.confirmWrite(filePath, preview);
|
|
674
|
+
return await this.confirmWrite(filePath, preview, signal);
|
|
626
675
|
}
|
|
627
676
|
resolvePathWithinRoot(rawPath) {
|
|
628
677
|
if (!path.isAbsolute(rawPath)) throw new Error(`Path must be absolute: ${rawPath}`);
|
|
629
678
|
const resolved = path.resolve(rawPath);
|
|
630
679
|
if (!isPathInside(this.rootDir, resolved)) throw new Error(`Path is outside allowed cwd subtree: ${resolved}`);
|
|
631
|
-
return
|
|
680
|
+
return rawPath;
|
|
632
681
|
}
|
|
633
682
|
getWorkspace() {
|
|
634
683
|
return this.workspace ??= root(this.rootDir, {
|
|
@@ -691,11 +740,11 @@ function pickOption(options, kinds) {
|
|
|
691
740
|
const TOOL_KIND_TITLE_MATCHERS = [
|
|
692
741
|
{
|
|
693
742
|
kind: "read",
|
|
694
|
-
|
|
743
|
+
names: ["read", "cat"]
|
|
695
744
|
},
|
|
696
745
|
{
|
|
697
746
|
kind: "search",
|
|
698
|
-
|
|
747
|
+
names: [
|
|
699
748
|
"search",
|
|
700
749
|
"find",
|
|
701
750
|
"grep"
|
|
@@ -703,7 +752,7 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
703
752
|
},
|
|
704
753
|
{
|
|
705
754
|
kind: "edit",
|
|
706
|
-
|
|
755
|
+
names: [
|
|
707
756
|
"write",
|
|
708
757
|
"edit",
|
|
709
758
|
"patch"
|
|
@@ -711,15 +760,15 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
711
760
|
},
|
|
712
761
|
{
|
|
713
762
|
kind: "delete",
|
|
714
|
-
|
|
763
|
+
names: ["delete", "remove"]
|
|
715
764
|
},
|
|
716
765
|
{
|
|
717
766
|
kind: "move",
|
|
718
|
-
|
|
767
|
+
names: ["move", "rename"]
|
|
719
768
|
},
|
|
720
769
|
{
|
|
721
770
|
kind: "execute",
|
|
722
|
-
|
|
771
|
+
names: [
|
|
723
772
|
"run",
|
|
724
773
|
"execute",
|
|
725
774
|
"bash"
|
|
@@ -727,7 +776,7 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
727
776
|
},
|
|
728
777
|
{
|
|
729
778
|
kind: "fetch",
|
|
730
|
-
|
|
779
|
+
names: [
|
|
731
780
|
"fetch",
|
|
732
781
|
"http",
|
|
733
782
|
"url"
|
|
@@ -735,25 +784,25 @@ const TOOL_KIND_TITLE_MATCHERS = [
|
|
|
735
784
|
},
|
|
736
785
|
{
|
|
737
786
|
kind: "think",
|
|
738
|
-
|
|
787
|
+
names: ["think"]
|
|
739
788
|
}
|
|
740
789
|
];
|
|
741
790
|
function inferToolKind(params) {
|
|
742
791
|
if (params.toolCall.kind) return params.toolCall.kind;
|
|
743
792
|
const title = params.toolCall.title?.trim().toLowerCase();
|
|
744
793
|
if (!title) return;
|
|
745
|
-
const head = title.split(
|
|
794
|
+
const head = title.split(/[:\s]/, 1)[0];
|
|
746
795
|
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;
|
|
796
|
+
return TOOL_KIND_TITLE_MATCHERS.find(({ names }) => names.includes(head))?.kind ?? "other";
|
|
751
797
|
}
|
|
752
798
|
function isAutoApprovedReadKind(kind) {
|
|
753
799
|
return kind === "read" || kind === "search";
|
|
754
800
|
}
|
|
755
|
-
async function promptForToolPermission(params) {
|
|
756
|
-
return await promptForPermission({
|
|
801
|
+
async function promptForToolPermission(params, signal) {
|
|
802
|
+
return await promptForPermission({
|
|
803
|
+
prompt: `\n[permission] Allow ${params.toolCall.title ?? "tool"} [${inferToolKind(params) ?? "other"}]? (y/N) `,
|
|
804
|
+
signal
|
|
805
|
+
});
|
|
757
806
|
}
|
|
758
807
|
function canPromptForPermission$1() {
|
|
759
808
|
return process.stdin.isTTY && process.stderr.isTTY;
|
|
@@ -848,24 +897,24 @@ function selectedOrFirst(options, allowOption) {
|
|
|
848
897
|
function selectedOrCancelled(option) {
|
|
849
898
|
return { response: option ? selected(option.optionId) : cancelled() };
|
|
850
899
|
}
|
|
851
|
-
async function resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption) {
|
|
852
|
-
if (canPromptForPermission$1()) return resolveInteractivePromptResult(params, allowOption, rejectOption);
|
|
900
|
+
async function resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption, signal) {
|
|
901
|
+
if (canPromptForPermission$1()) return resolveInteractivePromptResult(params, allowOption, rejectOption, signal);
|
|
853
902
|
const escalation = buildEscalationEvent(params, policyMatch.matchedRule);
|
|
854
903
|
return {
|
|
855
904
|
response: withPermissionMetadata(rejectOption ? selected(rejectOption.optionId) : cancelled(), { permissionEscalation: escalation }),
|
|
856
905
|
escalation
|
|
857
906
|
};
|
|
858
907
|
}
|
|
859
|
-
async function resolveInteractivePromptResult(params, allowOption, rejectOption) {
|
|
860
|
-
const approved = await promptForToolPermission(params);
|
|
908
|
+
async function resolveInteractivePromptResult(params, allowOption, rejectOption, signal) {
|
|
909
|
+
const approved = await promptForToolPermission(params, signal);
|
|
861
910
|
if (approved && allowOption) return { response: selected(allowOption.optionId) };
|
|
862
911
|
if (!approved && rejectOption) return { response: selected(rejectOption.optionId) };
|
|
863
912
|
return { response: cancelled() };
|
|
864
913
|
}
|
|
865
|
-
function resolvePolicyMatch(params, policyMatch, options, allowOption, rejectOption) {
|
|
914
|
+
function resolvePolicyMatch(params, policyMatch, options, allowOption, rejectOption, signal) {
|
|
866
915
|
if (policyMatch?.action === "approve") return selectedOrFirst(options, allowOption);
|
|
867
916
|
if (policyMatch?.action === "deny") return selectedOrCancelled(rejectOption);
|
|
868
|
-
if (policyMatch?.action === "escalate") return resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption);
|
|
917
|
+
if (policyMatch?.action === "escalate") return resolveEscalatingPermissionRequest(params, policyMatch, allowOption, rejectOption, signal);
|
|
869
918
|
}
|
|
870
919
|
function resolveModeMatch(options, mode, allowOption, rejectOption) {
|
|
871
920
|
if (mode === "approve-all") return selectedOrFirst(options, allowOption);
|
|
@@ -875,24 +924,35 @@ function resolveNonInteractivePermission(nonInteractivePolicy, rejectOption) {
|
|
|
875
924
|
if (nonInteractivePolicy === "fail") throw new PermissionPromptUnavailableError();
|
|
876
925
|
return selectedOrCancelled(rejectOption);
|
|
877
926
|
}
|
|
878
|
-
async function resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption) {
|
|
927
|
+
async function resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption, signal) {
|
|
879
928
|
if (isAutoApprovedReadKind(inferToolKind(params)) && allowOption) return { response: selected(allowOption.optionId) };
|
|
880
929
|
if (!canPromptForPermission$1()) return resolveNonInteractivePermission(nonInteractivePolicy, rejectOption);
|
|
881
|
-
return resolveInteractivePromptResult(params, allowOption, rejectOption);
|
|
930
|
+
return resolveInteractivePromptResult(params, allowOption, rejectOption, signal);
|
|
882
931
|
}
|
|
883
932
|
function permissionModeSatisfies(actual, required) {
|
|
884
933
|
return PERMISSION_MODE_RANK[actual] >= PERMISSION_MODE_RANK[required];
|
|
885
934
|
}
|
|
886
|
-
async function resolvePermissionRequestWithDetails(params, mode, nonInteractivePolicy = "deny", policy) {
|
|
935
|
+
async function resolvePermissionRequestWithDetails(params, mode, nonInteractivePolicy = "deny", policy, signal) {
|
|
936
|
+
try {
|
|
937
|
+
signal?.throwIfAborted();
|
|
938
|
+
const result = await resolvePermissionByPolicy(params, mode, nonInteractivePolicy, policy, signal);
|
|
939
|
+
signal?.throwIfAborted();
|
|
940
|
+
return result;
|
|
941
|
+
} catch (error) {
|
|
942
|
+
if (signal?.aborted) return { response: cancelled() };
|
|
943
|
+
throw error;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
async function resolvePermissionByPolicy(params, mode, nonInteractivePolicy, policy, signal) {
|
|
887
947
|
const options = params.options ?? [];
|
|
888
948
|
if (options.length === 0) return { response: cancelled() };
|
|
889
949
|
const allowOption = pickOption(options, ["allow_once", "allow_always"]);
|
|
890
950
|
const rejectOption = pickOption(options, ["reject_once", "reject_always"]);
|
|
891
|
-
const resolvedByPolicy = await resolvePolicyMatch(params, matchPermissionPolicy(params, policy), options, allowOption, rejectOption);
|
|
951
|
+
const resolvedByPolicy = await resolvePolicyMatch(params, matchPermissionPolicy(params, policy), options, allowOption, rejectOption, signal);
|
|
892
952
|
if (resolvedByPolicy) return resolvedByPolicy;
|
|
893
953
|
const resolvedByMode = resolveModeMatch(options, mode, allowOption, rejectOption);
|
|
894
954
|
if (resolvedByMode) return resolvedByMode;
|
|
895
|
-
return resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption);
|
|
955
|
+
return resolveReadOrPromptPermission(params, nonInteractivePolicy, allowOption, rejectOption, signal);
|
|
896
956
|
}
|
|
897
957
|
const DECISION_FALLBACK_ORDER = {
|
|
898
958
|
allow_once: ["allow_once", "allow_always"],
|
|
@@ -1321,10 +1381,12 @@ const REQUESTED_MODEL_UNSUPPORTED_REASONS = ["missing-capability", "unadvertised
|
|
|
1321
1381
|
var RequestedModelUnsupportedError = class extends Error {
|
|
1322
1382
|
code = REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE;
|
|
1323
1383
|
reason;
|
|
1324
|
-
|
|
1384
|
+
ambiguous;
|
|
1385
|
+
constructor(message, reason, details) {
|
|
1325
1386
|
super(message);
|
|
1326
1387
|
this.name = "RequestedModelUnsupportedError";
|
|
1327
1388
|
this.reason = reason;
|
|
1389
|
+
if (details?.ambiguous) this.ambiguous = true;
|
|
1328
1390
|
}
|
|
1329
1391
|
};
|
|
1330
1392
|
function isRequestedModelUnsupportedReason(value) {
|
|
@@ -1427,6 +1489,7 @@ function resolveRequestedModelId(params) {
|
|
|
1427
1489
|
if (!params.models || !isCursorAcpCommandForModelAlias(params.agentCommand)) return params.requestedModel;
|
|
1428
1490
|
if (params.models.availableModels.some((model) => model.modelId === params.requestedModel)) return params.requestedModel;
|
|
1429
1491
|
const candidates = params.models.availableModels.map((model) => model.modelId).filter((modelId) => modelId.startsWith(`${params.requestedModel}[`));
|
|
1492
|
+
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
1493
|
return candidates.length === 1 ? candidates[0] : params.requestedModel;
|
|
1431
1494
|
}
|
|
1432
1495
|
function isCursorAcpCommandForModelAlias(agentCommand) {
|
|
@@ -1757,8 +1820,11 @@ function trimToUtf8Boundary(buffer, limit) {
|
|
|
1757
1820
|
if (start >= buffer.length) start = buffer.length - limit;
|
|
1758
1821
|
return buffer.subarray(start);
|
|
1759
1822
|
}
|
|
1760
|
-
async function defaultConfirmExecute(commandLine) {
|
|
1761
|
-
return await promptForPermission({
|
|
1823
|
+
async function defaultConfirmExecute(commandLine, signal) {
|
|
1824
|
+
return await promptForPermission({
|
|
1825
|
+
prompt: `\n[permission] Allow terminal command "${commandLine}"? (y/N) `,
|
|
1826
|
+
signal
|
|
1827
|
+
});
|
|
1762
1828
|
}
|
|
1763
1829
|
function canPromptForPermission() {
|
|
1764
1830
|
return process.stdin.isTTY && process.stderr.isTTY;
|
|
@@ -1795,7 +1861,8 @@ var TerminalManager = class {
|
|
|
1795
1861
|
this.permissionMode = permissionMode;
|
|
1796
1862
|
this.nonInteractivePermissions = nonInteractivePermissions ?? "deny";
|
|
1797
1863
|
}
|
|
1798
|
-
async createTerminal(params) {
|
|
1864
|
+
async createTerminal(params, authority) {
|
|
1865
|
+
assertControlAuthority(authority);
|
|
1799
1866
|
const commandLine = toCommandLine(params.command, params.args);
|
|
1800
1867
|
const summary = `terminal/create: ${commandLine}`;
|
|
1801
1868
|
this.emitOperation({
|
|
@@ -1805,10 +1872,12 @@ var TerminalManager = class {
|
|
|
1805
1872
|
timestamp: nowIso()
|
|
1806
1873
|
});
|
|
1807
1874
|
try {
|
|
1808
|
-
|
|
1875
|
+
const approved = await this.isExecuteApproved(commandLine, authority?.signal);
|
|
1876
|
+
assertControlAuthority(authority);
|
|
1877
|
+
if (!approved) throw new PermissionDeniedError("Permission denied for terminal/create");
|
|
1809
1878
|
const requestedLimit = Math.max(0, Math.round(params.outputByteLimit ?? DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES));
|
|
1810
1879
|
const outputByteLimit = Math.min(requestedLimit, this.outputCeilingBytes ?? Number.POSITIVE_INFINITY);
|
|
1811
|
-
const { proc, spawnCommand } = await spawnTerminalProcess(params, this.cwd);
|
|
1880
|
+
const { proc, spawnCommand } = await spawnTerminalProcess(params, this.cwd, authority);
|
|
1812
1881
|
let resolveExit = () => {};
|
|
1813
1882
|
const exitPromise = new Promise((resolve) => {
|
|
1814
1883
|
resolveExit = resolve;
|
|
@@ -1853,6 +1922,15 @@ var TerminalManager = class {
|
|
|
1853
1922
|
});
|
|
1854
1923
|
const terminalId = randomUUID();
|
|
1855
1924
|
this.terminals.set(terminalId, terminal);
|
|
1925
|
+
try {
|
|
1926
|
+
assertControlAuthority(authority);
|
|
1927
|
+
} catch (error) {
|
|
1928
|
+
await this.releaseTerminal({
|
|
1929
|
+
terminalId,
|
|
1930
|
+
sessionId: params.sessionId
|
|
1931
|
+
});
|
|
1932
|
+
throw error;
|
|
1933
|
+
}
|
|
1856
1934
|
this.emitOperation({
|
|
1857
1935
|
method: "terminal/create",
|
|
1858
1936
|
status: "completed",
|
|
@@ -1991,11 +2069,11 @@ var TerminalManager = class {
|
|
|
1991
2069
|
emitOperation(operation) {
|
|
1992
2070
|
this.onOperation?.(operation);
|
|
1993
2071
|
}
|
|
1994
|
-
async isExecuteApproved(commandLine) {
|
|
2072
|
+
async isExecuteApproved(commandLine, signal) {
|
|
1995
2073
|
if (this.permissionMode === "approve-all") return true;
|
|
1996
2074
|
if (this.permissionMode === "deny-all") return false;
|
|
1997
2075
|
if (this.usesDefaultConfirmExecute && this.nonInteractivePermissions === "fail" && !canPromptForPermission()) throw new PermissionPromptUnavailableError();
|
|
1998
|
-
return await this.confirmExecute(commandLine);
|
|
2076
|
+
return await this.confirmExecute(commandLine, signal);
|
|
1999
2077
|
}
|
|
2000
2078
|
isRunning(terminal) {
|
|
2001
2079
|
return terminal.exitCode === void 0 && terminal.signal === void 0;
|
|
@@ -2059,25 +2137,26 @@ var TerminalManager = class {
|
|
|
2059
2137
|
while (hasLivePid(terminal.descendantPids)) await waitMs(25);
|
|
2060
2138
|
}
|
|
2061
2139
|
};
|
|
2062
|
-
async function spawnTerminalProcess(params, defaultCwd) {
|
|
2140
|
+
async function spawnTerminalProcess(params, defaultCwd, authority) {
|
|
2063
2141
|
const directCommand = buildTerminalSpawnCommand(params.command, params.args);
|
|
2064
2142
|
try {
|
|
2065
2143
|
return {
|
|
2066
|
-
proc: await spawnAndWait(directCommand, params, defaultCwd),
|
|
2144
|
+
proc: await spawnAndWait(directCommand, params, defaultCwd, authority),
|
|
2067
2145
|
spawnCommand: directCommand
|
|
2068
2146
|
};
|
|
2069
2147
|
} catch (error) {
|
|
2070
2148
|
const fallbackCommand = params.args === void 0 && isNotFoundSpawnError(error) ? buildTerminalFallbackSpawnCommand(params.command, params.cwd ?? defaultCwd) : void 0;
|
|
2071
2149
|
if (!fallbackCommand) throw error;
|
|
2072
2150
|
return {
|
|
2073
|
-
proc: await spawnAndWait(fallbackCommand, params, defaultCwd),
|
|
2151
|
+
proc: await spawnAndWait(fallbackCommand, params, defaultCwd, authority),
|
|
2074
2152
|
spawnCommand: fallbackCommand
|
|
2075
2153
|
};
|
|
2076
2154
|
}
|
|
2077
2155
|
}
|
|
2078
|
-
async function spawnAndWait(spawnCommand, params, defaultCwd) {
|
|
2156
|
+
async function spawnAndWait(spawnCommand, params, defaultCwd, authority) {
|
|
2079
2157
|
const spawnOptions = buildTerminalSpawnOptions(spawnCommand.command, params.cwd ?? defaultCwd, params.env);
|
|
2080
2158
|
if (spawnCommand.killProcessGroup) spawnOptions.detached = true;
|
|
2159
|
+
assertControlAuthority(authority);
|
|
2081
2160
|
const proc = spawn(spawnCommand.command, spawnCommand.args, spawnOptions);
|
|
2082
2161
|
await waitForSpawn(proc);
|
|
2083
2162
|
return proc;
|
|
@@ -2368,8 +2447,6 @@ var AcpClient = class {
|
|
|
2368
2447
|
lastKnownPid;
|
|
2369
2448
|
promptPermissionFailures = /* @__PURE__ */ new Map();
|
|
2370
2449
|
pendingConnectionRequests = /* @__PURE__ */ new Set();
|
|
2371
|
-
modelConfigIds = /* @__PURE__ */ new Map();
|
|
2372
|
-
legacyModelSessionIds = /* @__PURE__ */ new Set();
|
|
2373
2450
|
constructor(options) {
|
|
2374
2451
|
this.options = {
|
|
2375
2452
|
...options,
|
|
@@ -2473,11 +2550,15 @@ var AcpClient = class {
|
|
|
2473
2550
|
hasUnresolvedPrompt() {
|
|
2474
2551
|
return this.activePrompt !== void 0;
|
|
2475
2552
|
}
|
|
2476
|
-
|
|
2477
|
-
const
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2553
|
+
abortSessionRequests(sessionId) {
|
|
2554
|
+
const owners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
2555
|
+
const fallback = this.takePermissionAbortController(sessionId);
|
|
2556
|
+
for (const owner of owners) {
|
|
2557
|
+
owner.elicitationHandler = void 0;
|
|
2558
|
+
owner.elicitationController.abort();
|
|
2559
|
+
owner.requestController.abort();
|
|
2560
|
+
}
|
|
2561
|
+
fallback?.abort();
|
|
2481
2562
|
}
|
|
2482
2563
|
async start() {
|
|
2483
2564
|
if (this.hasLiveConnection()) return;
|
|
@@ -2634,19 +2715,19 @@ var AcpClient = class {
|
|
|
2634
2715
|
createConnection(stream, launch, capabilities) {
|
|
2635
2716
|
const app = client({ name: "acpx" }).onNotification(methods.client.session.update, async ({ params }) => {
|
|
2636
2717
|
await this.handleSessionUpdate(params);
|
|
2637
|
-
}).onNotification(methods.client.elicitation.complete, async () => {}).onRequest(methods.client.session.requestPermission, async ({ params }) => {
|
|
2638
|
-
return await this.handlePermissionRequest(params);
|
|
2718
|
+
}).onNotification(methods.client.elicitation.complete, async () => {}).onRequest(methods.client.session.requestPermission, async ({ params, signal }) => {
|
|
2719
|
+
return await this.handlePermissionRequest(params, signal);
|
|
2639
2720
|
}).onRequest(methods.client.elicitation.create, async ({ params, requestId, signal }) => {
|
|
2640
2721
|
return await this.handleElicitationRequest(params, requestId, signal);
|
|
2641
2722
|
});
|
|
2642
|
-
if (capabilities.fs?.readTextFile) app.onRequest(methods.client.fs.readTextFile, async ({ params }) => {
|
|
2643
|
-
return await this.handleReadTextFile(params);
|
|
2723
|
+
if (capabilities.fs?.readTextFile) app.onRequest(methods.client.fs.readTextFile, async ({ params, signal }) => {
|
|
2724
|
+
return await this.handleReadTextFile(params, signal);
|
|
2644
2725
|
});
|
|
2645
|
-
if (capabilities.fs?.writeTextFile) app.onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
|
|
2646
|
-
return await this.handleWriteTextFile(params);
|
|
2726
|
+
if (capabilities.fs?.writeTextFile) app.onRequest(methods.client.fs.writeTextFile, async ({ params, signal }) => {
|
|
2727
|
+
return await this.handleWriteTextFile(params, signal);
|
|
2647
2728
|
});
|
|
2648
|
-
if (capabilities.terminal) app.onRequest(methods.client.terminal.create, async ({ params }) => {
|
|
2649
|
-
return await this.handleCreateTerminal(params);
|
|
2729
|
+
if (capabilities.terminal) app.onRequest(methods.client.terminal.create, async ({ params, signal }) => {
|
|
2730
|
+
return await this.handleCreateTerminal(params, signal);
|
|
2650
2731
|
}).onRequest(methods.client.terminal.output, async ({ params }) => {
|
|
2651
2732
|
return await this.terminalManager.terminalOutput(params);
|
|
2652
2733
|
}).onRequest(methods.client.terminal.waitForExit, async ({ params }) => {
|
|
@@ -2733,7 +2814,6 @@ var AcpClient = class {
|
|
|
2733
2814
|
configOptions,
|
|
2734
2815
|
response: result
|
|
2735
2816
|
});
|
|
2736
|
-
this.rememberSessionModels(result.sessionId, models);
|
|
2737
2817
|
await this.captureAgentDescendants(this.agent);
|
|
2738
2818
|
return {
|
|
2739
2819
|
sessionId: result.sessionId,
|
|
@@ -2765,7 +2845,6 @@ var AcpClient = class {
|
|
|
2765
2845
|
}
|
|
2766
2846
|
this.loadedSessionId = sessionId;
|
|
2767
2847
|
const result = toReconnectedSessionResult(response);
|
|
2768
|
-
this.updateRememberedSessionModels(sessionId, result);
|
|
2769
2848
|
await this.captureAgentDescendants(this.agent);
|
|
2770
2849
|
return result;
|
|
2771
2850
|
}
|
|
@@ -2779,7 +2858,6 @@ var AcpClient = class {
|
|
|
2779
2858
|
}));
|
|
2780
2859
|
this.loadedSessionId = sessionId;
|
|
2781
2860
|
const result = toReconnectedSessionResult(response);
|
|
2782
|
-
this.updateRememberedSessionModels(sessionId, result);
|
|
2783
2861
|
await this.captureAgentDescendants(this.agent);
|
|
2784
2862
|
return result;
|
|
2785
2863
|
}
|
|
@@ -2801,6 +2879,7 @@ var AcpClient = class {
|
|
|
2801
2879
|
const normalizedPrompt = this.normalizePromptForAgent(prompt);
|
|
2802
2880
|
const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
|
|
2803
2881
|
const previousActivePrompt = this.activePrompt;
|
|
2882
|
+
const replacedOwners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
2804
2883
|
const activePrompt = this.beginActivePrompt(sessionId, onRequestWritten, onElicitation, onPermissionRequest, authority);
|
|
2805
2884
|
try {
|
|
2806
2885
|
const promptPromise = this.runConnectionRequest(() => connection.agent.request(methods.agent.session.prompt, {
|
|
@@ -2809,6 +2888,7 @@ var AcpClient = class {
|
|
|
2809
2888
|
}), authority);
|
|
2810
2889
|
activePrompt.promise = promptPromise;
|
|
2811
2890
|
previousActivePrompt?.elicitationController?.abort();
|
|
2891
|
+
for (const owner of replacedOwners) owner.requestController.abort();
|
|
2812
2892
|
return this.returnPromptResponseOrPermissionFailure(sessionId, await promptPromise);
|
|
2813
2893
|
} catch (error) {
|
|
2814
2894
|
if (activePrompt.admissionFailure) throw activePrompt.admissionFailure.error;
|
|
@@ -2827,7 +2907,8 @@ var AcpClient = class {
|
|
|
2827
2907
|
authority,
|
|
2828
2908
|
elicitationHandler,
|
|
2829
2909
|
permissionHandler,
|
|
2830
|
-
elicitationController: new AbortController()
|
|
2910
|
+
elicitationController: new AbortController(),
|
|
2911
|
+
requestController: new AbortController()
|
|
2831
2912
|
};
|
|
2832
2913
|
this.activePrompt = active;
|
|
2833
2914
|
this.pendingPromptOwners.push(active);
|
|
@@ -2866,6 +2947,7 @@ var AcpClient = class {
|
|
|
2866
2947
|
this.promptPermissionFailures.delete(active.sessionId);
|
|
2867
2948
|
}
|
|
2868
2949
|
active.elicitationController.abort();
|
|
2950
|
+
active.requestController.abort();
|
|
2869
2951
|
permissionController?.abort();
|
|
2870
2952
|
}
|
|
2871
2953
|
normalizePromptForAgent(prompt) {
|
|
@@ -2889,33 +2971,41 @@ var AcpClient = class {
|
|
|
2889
2971
|
modeId
|
|
2890
2972
|
}), authority, (error) => maybeWrapSessionControlError("session/set_mode", error, `for mode "${modeId}"`));
|
|
2891
2973
|
}
|
|
2892
|
-
async setSessionConfigOption(sessionId, configId, value, authority) {
|
|
2974
|
+
async setSessionConfigOption(sessionId, configId, value, models, authority) {
|
|
2975
|
+
assertControlAuthority(authority);
|
|
2976
|
+
const resolvedValue = models?.configId === configId ? this.resolveSessionModelId(value, models) : value;
|
|
2893
2977
|
const connection = this.getConnection();
|
|
2894
2978
|
return await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.setConfigOption, {
|
|
2895
2979
|
sessionId,
|
|
2896
2980
|
configId,
|
|
2897
|
-
value
|
|
2981
|
+
value: resolvedValue
|
|
2898
2982
|
}), authority, (error) => maybeWrapSessionControlError("session/set_config_option", error, `for "${configId}"="${value}"`));
|
|
2899
2983
|
}
|
|
2900
|
-
async setSessionModel(sessionId, modelId,
|
|
2901
|
-
|
|
2902
|
-
if (!
|
|
2903
|
-
const resolvedModelId =
|
|
2984
|
+
async setSessionModel(sessionId, modelId, models, authority) {
|
|
2985
|
+
assertControlAuthority(authority);
|
|
2986
|
+
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");
|
|
2987
|
+
const resolvedModelId = this.resolveSessionModelId(modelId, models);
|
|
2988
|
+
return models.configId ? await this.setSessionModelThroughConfig(sessionId, resolvedModelId, models.configId, authority) : await this.setSessionModelThroughLegacyMethod(sessionId, resolvedModelId, authority);
|
|
2989
|
+
}
|
|
2990
|
+
resolveSessionModelId(modelId, models) {
|
|
2991
|
+
const params = {
|
|
2904
2992
|
requestedModel: modelId,
|
|
2905
|
-
models
|
|
2993
|
+
models,
|
|
2906
2994
|
agentCommand: this.options.agentCommand
|
|
2995
|
+
};
|
|
2996
|
+
assertRequestedModelSupported({
|
|
2997
|
+
...params,
|
|
2998
|
+
context: "apply"
|
|
2907
2999
|
});
|
|
2908
|
-
return
|
|
3000
|
+
return resolveRequestedModelId(params);
|
|
2909
3001
|
}
|
|
2910
3002
|
async setSessionModelThroughConfig(sessionId, modelId, configId, authority) {
|
|
2911
3003
|
const connection = this.getConnection();
|
|
2912
|
-
|
|
3004
|
+
return await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.setConfigOption, {
|
|
2913
3005
|
sessionId,
|
|
2914
3006
|
configId,
|
|
2915
3007
|
value: modelId
|
|
2916
3008
|
}), authority, (error) => this.throwSessionModelError("session/set_config_option", modelId, error));
|
|
2917
|
-
this.rememberSessionModels(sessionId, modelStateFromConfigOptions(response.configOptions));
|
|
2918
|
-
return response;
|
|
2919
3009
|
}
|
|
2920
3010
|
async setSessionModelThroughLegacyMethod(sessionId, modelId, authority) {
|
|
2921
3011
|
const connection = this.getConnection();
|
|
@@ -2931,39 +3021,10 @@ var AcpClient = class {
|
|
|
2931
3021
|
const summary = acp ? formatSessionControlAcpSummary(acp) : error instanceof Error ? error.message : String(error);
|
|
2932
3022
|
throw new Error(`Failed ${method} for model "${modelId}": ${summary}`, { cause: error });
|
|
2933
3023
|
}
|
|
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
3024
|
async cancel(sessionId) {
|
|
2965
3025
|
const connection = this.getConnection();
|
|
2966
|
-
const
|
|
3026
|
+
const owners = this.pendingPromptOwners.filter((owner) => owner.sessionId === sessionId);
|
|
3027
|
+
const active = owners.at(-1);
|
|
2967
3028
|
const cancellation = active?.cancelPromise ?? this.runConnectionRequest(() => connection.agent.notify(methods.agent.session.cancel, { sessionId })).catch((error) => {
|
|
2968
3029
|
if (active?.cancelPromise === cancellation) active.cancelPromise = void 0;
|
|
2969
3030
|
throw error;
|
|
@@ -2972,19 +3033,20 @@ var AcpClient = class {
|
|
|
2972
3033
|
if (active) {
|
|
2973
3034
|
active.cancelPromise = cancellation;
|
|
2974
3035
|
this.cancellingSessionIds.add(sessionId);
|
|
2975
|
-
active.elicitationController?.abort();
|
|
2976
3036
|
} else this.cancellingSessionIds.delete(sessionId);
|
|
3037
|
+
for (const owner of owners) {
|
|
3038
|
+
owner.elicitationController.abort();
|
|
3039
|
+
owner.requestController.abort();
|
|
3040
|
+
}
|
|
2977
3041
|
permissionController?.abort();
|
|
2978
3042
|
await cancellation;
|
|
2979
3043
|
}
|
|
2980
3044
|
async closeSession(sessionId) {
|
|
2981
3045
|
const connection = this.getConnection();
|
|
2982
3046
|
this.cancellingSessionIds.add(sessionId);
|
|
2983
|
-
|
|
3047
|
+
this.abortSessionRequests(sessionId);
|
|
2984
3048
|
await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.close, { sessionId }));
|
|
2985
3049
|
if (this.loadedSessionId === sessionId) this.loadedSessionId = void 0;
|
|
2986
|
-
this.modelConfigIds.delete(sessionId);
|
|
2987
|
-
this.legacyModelSessionIds.delete(sessionId);
|
|
2988
3050
|
}
|
|
2989
3051
|
async listSessions(params = {}) {
|
|
2990
3052
|
const connection = this.getConnection();
|
|
@@ -3021,7 +3083,12 @@ var AcpClient = class {
|
|
|
3021
3083
|
async close() {
|
|
3022
3084
|
this.closing = true;
|
|
3023
3085
|
this.closeEpoch += 1;
|
|
3086
|
+
const permissionControllers = [...this.permissionAbortControllers.values()];
|
|
3087
|
+
this.permissionAbortControllers.clear();
|
|
3088
|
+
const owners = [...this.pendingPromptOwners];
|
|
3024
3089
|
this.abortActiveElicitation();
|
|
3090
|
+
for (const owner of owners) owner.requestController.abort();
|
|
3091
|
+
for (const controller of permissionControllers) controller.abort();
|
|
3025
3092
|
await this.terminalManager.shutdown();
|
|
3026
3093
|
const agent = this.agent;
|
|
3027
3094
|
if (agent) await this.terminateAgentProcess(agent);
|
|
@@ -3039,8 +3106,6 @@ var AcpClient = class {
|
|
|
3039
3106
|
this.permissionAbortControllers.clear();
|
|
3040
3107
|
this.promptPermissionFailures.clear();
|
|
3041
3108
|
this.loadedSessionId = void 0;
|
|
3042
|
-
this.modelConfigIds.clear();
|
|
3043
|
-
this.legacyModelSessionIds.clear();
|
|
3044
3109
|
this.initResult = void 0;
|
|
3045
3110
|
this.connection = void 0;
|
|
3046
3111
|
this.agent = void 0;
|
|
@@ -3255,27 +3320,41 @@ var AcpClient = class {
|
|
|
3255
3320
|
await connection.agent.request(methods.agent.authenticate, { methodId: selected.methodId });
|
|
3256
3321
|
this.log(`authenticated with method ${selected.methodId} (${selected.source})`);
|
|
3257
3322
|
}
|
|
3258
|
-
async handlePermissionRequest(params) {
|
|
3259
|
-
|
|
3260
|
-
if (this.
|
|
3261
|
-
|
|
3262
|
-
if (
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
const decision = classifyPermissionDecision(params, response);
|
|
3266
|
-
this.recordPermissionDecision(decision);
|
|
3323
|
+
async handlePermissionRequest(params, requestSignal) {
|
|
3324
|
+
const owner = this.captureDelegatedRequestOwner(params.sessionId, requestSignal);
|
|
3325
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3326
|
+
if (!this.hasPermissionOwner(params.sessionId)) return this.finishPermissionRequest(params, owner, cancelledPermissionResponse());
|
|
3327
|
+
if (this.initResult?.agentInfo?.name === "antigravity-acp" && params.toolCall.toolCallId.startsWith("interaction_")) {
|
|
3328
|
+
const response = this.handleModePermissionError(params.sessionId, new PermissionPromptUnavailableError("Antigravity requested a user answer. acpx cannot answer Antigravity interaction questions; continue in an interactive client."));
|
|
3329
|
+
return this.finishPermissionRequest(params, owner, response);
|
|
3267
3330
|
}
|
|
3268
|
-
|
|
3331
|
+
const response = await this.tryHandlePermissionRequestWithHost(params, owner) ?? await this.resolvePermissionRequestFromMode(params, owner);
|
|
3332
|
+
return this.finishPermissionRequest(params, owner, response);
|
|
3333
|
+
}
|
|
3334
|
+
finishPermissionRequest(params, owner, response) {
|
|
3335
|
+
const result = this.isDelegatedRequestActive(owner) ? response : cancelledPermissionResponse();
|
|
3336
|
+
this.recordPermissionDecision(classifyPermissionDecision(params, result));
|
|
3337
|
+
return result;
|
|
3338
|
+
}
|
|
3339
|
+
hasPermissionOwner(sessionId) {
|
|
3340
|
+
return !this.pendingPromptOwners.some((owner) => owner.permissionHandler !== void 0) || this.pendingPromptOwners.some((owner) => owner.sessionId === sessionId);
|
|
3269
3341
|
}
|
|
3270
3342
|
async handleElicitationRequest(request, requestId, requestSignal) {
|
|
3271
3343
|
const resolved = this.resolveElicitationOwner(request);
|
|
3272
3344
|
if ("response" in resolved) return resolved.response;
|
|
3273
3345
|
const { active, handler } = resolved.owner;
|
|
3274
|
-
const signal = AbortSignal.any([
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
signal
|
|
3278
|
-
|
|
3346
|
+
const signal = AbortSignal.any([
|
|
3347
|
+
requestSignal,
|
|
3348
|
+
active.elicitationController.signal,
|
|
3349
|
+
active.authority?.signal
|
|
3350
|
+
].filter((candidate) => candidate !== void 0));
|
|
3351
|
+
const outcome = await raceWithAbort(signal, Promise.resolve().then(async () => {
|
|
3352
|
+
signal.throwIfAborted();
|
|
3353
|
+
return await handler(request, {
|
|
3354
|
+
requestId,
|
|
3355
|
+
signal
|
|
3356
|
+
});
|
|
3357
|
+
}).then((response) => ({
|
|
3279
3358
|
kind: "response",
|
|
3280
3359
|
response
|
|
3281
3360
|
}), (error) => ({
|
|
@@ -3310,62 +3389,65 @@ var AcpClient = class {
|
|
|
3310
3389
|
isElicitationSessionCancelling(sessionId) {
|
|
3311
3390
|
return this.closing || this.cancellingSessionIds.has(sessionId);
|
|
3312
3391
|
}
|
|
3313
|
-
|
|
3314
|
-
const active = this.
|
|
3315
|
-
const
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
};
|
|
3392
|
+
captureDelegatedRequestOwner(sessionId, requestSignal) {
|
|
3393
|
+
const active = this.pendingPromptOwners.findLast((owner) => owner.sessionId === sessionId);
|
|
3394
|
+
const signal = AbortSignal.any([
|
|
3395
|
+
active?.requestController.signal ?? this.cancellationSignalForSession(sessionId),
|
|
3396
|
+
active?.authority?.signal,
|
|
3397
|
+
requestSignal
|
|
3398
|
+
].filter((candidate) => candidate !== void 0));
|
|
3399
|
+
const epoch = this.closeEpoch;
|
|
3322
3400
|
return {
|
|
3323
|
-
|
|
3324
|
-
|
|
3401
|
+
signal,
|
|
3402
|
+
permissionHandler: active?.permissionHandler ?? this.options.onPermissionRequest,
|
|
3403
|
+
assertActive: () => {
|
|
3404
|
+
if (this.closing || this.closeEpoch !== epoch || this.cancellingSessionIds.has(sessionId)) throw RequestError.requestCancelled();
|
|
3405
|
+
}
|
|
3325
3406
|
};
|
|
3326
3407
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3408
|
+
isDelegatedRequestActive(owner) {
|
|
3409
|
+
try {
|
|
3410
|
+
assertControlAuthority(owner);
|
|
3411
|
+
return true;
|
|
3412
|
+
} catch {
|
|
3413
|
+
return false;
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
async tryHandlePermissionRequestWithHost(params, owner) {
|
|
3417
|
+
const handler = owner.permissionHandler;
|
|
3418
|
+
if (!handler) return;
|
|
3419
|
+
const { signal } = owner;
|
|
3420
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3332
3421
|
try {
|
|
3333
3422
|
const decision = await raceWithAbort(signal, handler({
|
|
3334
3423
|
sessionId: params.sessionId,
|
|
3335
3424
|
raw: params,
|
|
3336
3425
|
inferredKind: inferToolKind(params)
|
|
3337
3426
|
}, { signal }));
|
|
3338
|
-
return this.hostPermissionDecisionResponse(params,
|
|
3427
|
+
return this.hostPermissionDecisionResponse(params, owner, decision);
|
|
3339
3428
|
} catch (error) {
|
|
3340
|
-
return this.hostPermissionErrorResponse(
|
|
3429
|
+
return this.hostPermissionErrorResponse(owner, error);
|
|
3341
3430
|
}
|
|
3342
3431
|
}
|
|
3343
|
-
hostPermissionDecisionResponse(params,
|
|
3344
|
-
if (
|
|
3345
|
-
this.recordPermissionDecision("cancelled");
|
|
3346
|
-
return cancelledPermissionResponse();
|
|
3347
|
-
}
|
|
3432
|
+
hostPermissionDecisionResponse(params, owner, decision) {
|
|
3433
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3348
3434
|
if (!decision) return;
|
|
3349
3435
|
const response = decisionToResponse(preferCodexPermissionRefusal(params, this.initResult?.agentInfo?.name), decision);
|
|
3350
|
-
this.recordPermissionDecision(classifyPermissionDecision(params, response));
|
|
3351
3436
|
return decision.outcome === "cancel" ? response : this.explainPermissionRefusal(params, response);
|
|
3352
3437
|
}
|
|
3353
|
-
hostPermissionErrorResponse(
|
|
3354
|
-
if (
|
|
3355
|
-
this.recordPermissionDecision("cancelled");
|
|
3356
|
-
return cancelledPermissionResponse();
|
|
3357
|
-
}
|
|
3438
|
+
hostPermissionErrorResponse(owner, error) {
|
|
3439
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3358
3440
|
this.log(`onPermissionRequest threw, falling through to mode-based resolver: ${error instanceof Error ? error.message : String(error)}`);
|
|
3359
3441
|
}
|
|
3360
|
-
async resolvePermissionRequestFromMode(params) {
|
|
3442
|
+
async resolvePermissionRequestFromMode(params, owner) {
|
|
3361
3443
|
try {
|
|
3362
|
-
|
|
3444
|
+
assertControlAuthority(owner);
|
|
3445
|
+
const result = await resolvePermissionRequestWithDetails(preferCodexPermissionRefusal(params, this.initResult?.agentInfo?.name), this.options.permissionMode, this.options.nonInteractivePermissions ?? "deny", this.options.permissionPolicy, owner.signal);
|
|
3446
|
+
assertControlAuthority(owner);
|
|
3363
3447
|
this.emitPermissionEscalation(result.escalation);
|
|
3364
|
-
return
|
|
3365
|
-
response: this.explainPermissionRefusal(params, result.response),
|
|
3366
|
-
recorded: false
|
|
3367
|
-
};
|
|
3448
|
+
return this.explainPermissionRefusal(params, result.response);
|
|
3368
3449
|
} catch (error) {
|
|
3450
|
+
if (!this.isDelegatedRequestActive(owner)) return cancelledPermissionResponse();
|
|
3369
3451
|
return this.handleModePermissionError(params.sessionId, error);
|
|
3370
3452
|
}
|
|
3371
3453
|
}
|
|
@@ -3388,11 +3470,7 @@ var AcpClient = class {
|
|
|
3388
3470
|
handleModePermissionError(sessionId, error) {
|
|
3389
3471
|
if (!(error instanceof PermissionPromptUnavailableError)) throw error;
|
|
3390
3472
|
this.notePromptPermissionFailure(sessionId, error);
|
|
3391
|
-
|
|
3392
|
-
return {
|
|
3393
|
-
response: cancelledPermissionResponse(),
|
|
3394
|
-
recorded: true
|
|
3395
|
-
};
|
|
3473
|
+
return cancelledPermissionResponse();
|
|
3396
3474
|
}
|
|
3397
3475
|
attachAgentLifecycleObservers(child, startedProcess, exitNotificationBarrier) {
|
|
3398
3476
|
const onExit = (exitCode, signal) => {
|
|
@@ -3519,28 +3597,30 @@ var AcpClient = class {
|
|
|
3519
3597
|
pending.reject(error);
|
|
3520
3598
|
}
|
|
3521
3599
|
}
|
|
3522
|
-
async handleReadTextFile(params) {
|
|
3600
|
+
async handleReadTextFile(params, requestSignal) {
|
|
3523
3601
|
try {
|
|
3524
|
-
return await this.filesystem.readTextFile(params);
|
|
3602
|
+
return await this.runDelegatedOperation(params.sessionId, requestSignal, (owner) => this.filesystem.readTextFile(params, owner));
|
|
3525
3603
|
} catch (error) {
|
|
3526
|
-
this.recordPermissionError(params.sessionId, error);
|
|
3527
3604
|
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
3605
|
throw error;
|
|
3529
3606
|
}
|
|
3530
3607
|
}
|
|
3531
|
-
async handleWriteTextFile(params) {
|
|
3532
|
-
|
|
3533
|
-
return await this.filesystem.writeTextFile(params);
|
|
3534
|
-
} catch (error) {
|
|
3535
|
-
this.recordPermissionError(params.sessionId, error);
|
|
3536
|
-
throw error;
|
|
3537
|
-
}
|
|
3608
|
+
async handleWriteTextFile(params, requestSignal) {
|
|
3609
|
+
return await this.runDelegatedOperation(params.sessionId, requestSignal, (owner) => this.filesystem.writeTextFile(params, owner));
|
|
3538
3610
|
}
|
|
3539
|
-
async handleCreateTerminal(params) {
|
|
3611
|
+
async handleCreateTerminal(params, requestSignal) {
|
|
3612
|
+
return await this.runDelegatedOperation(params.sessionId, requestSignal, (owner) => this.terminalManager.createTerminal(params, owner));
|
|
3613
|
+
}
|
|
3614
|
+
async runDelegatedOperation(sessionId, requestSignal, run) {
|
|
3615
|
+
const owner = this.captureDelegatedRequestOwner(sessionId, requestSignal);
|
|
3540
3616
|
try {
|
|
3541
|
-
|
|
3617
|
+
assertControlAuthority(owner);
|
|
3618
|
+
const result = await run(owner);
|
|
3619
|
+
assertControlAuthority(owner);
|
|
3620
|
+
return result;
|
|
3542
3621
|
} catch (error) {
|
|
3543
|
-
this.
|
|
3622
|
+
if (!this.isDelegatedRequestActive(owner)) throw RequestError.requestCancelled();
|
|
3623
|
+
this.recordPermissionError(sessionId, error);
|
|
3544
3624
|
throw error;
|
|
3545
3625
|
}
|
|
3546
3626
|
}
|
|
@@ -4037,20 +4117,6 @@ function recordPromptSubmission(conversation, prompt, timestamp = isoNow(), mess
|
|
|
4037
4117
|
trimConversationForRuntime(conversation);
|
|
4038
4118
|
return promptMessageId;
|
|
4039
4119
|
}
|
|
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
4120
|
function recordSessionUpdate(conversation, state, notification, timestamp = isoNow(), userMessageId) {
|
|
4055
4121
|
const acpx = ensureAcpxState$1(state);
|
|
4056
4122
|
const update = notification.update;
|
|
@@ -4456,14 +4522,42 @@ async function readLock(filePath) {
|
|
|
4456
4522
|
throw error;
|
|
4457
4523
|
}
|
|
4458
4524
|
}
|
|
4459
|
-
function lockPid(
|
|
4525
|
+
function lockPid(value) {
|
|
4526
|
+
if (!value || typeof value !== "object" || !("pid" in value)) return;
|
|
4527
|
+
const pid = value.pid;
|
|
4528
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
4529
|
+
}
|
|
4530
|
+
function parseLock(payload) {
|
|
4460
4531
|
try {
|
|
4461
|
-
|
|
4462
|
-
return typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4532
|
+
return JSON.parse(payload);
|
|
4463
4533
|
} catch {
|
|
4464
4534
|
return;
|
|
4465
4535
|
}
|
|
4466
4536
|
}
|
|
4537
|
+
async function tryAcquireGuard(filePath) {
|
|
4538
|
+
try {
|
|
4539
|
+
return await acquireFileLock(filePath, {
|
|
4540
|
+
managerKey: "acpx.session-turn",
|
|
4541
|
+
lockPath: `${filePath}.guard`,
|
|
4542
|
+
staleMs: Infinity,
|
|
4543
|
+
timeoutMs: LOCK_RETRY_MS,
|
|
4544
|
+
retry: {
|
|
4545
|
+
retries: 8,
|
|
4546
|
+
minTimeout: 1,
|
|
4547
|
+
maxTimeout: 2,
|
|
4548
|
+
factor: 1,
|
|
4549
|
+
randomize: false
|
|
4550
|
+
},
|
|
4551
|
+
staleRecovery: "remove-if-unchanged",
|
|
4552
|
+
payload: () => ({ pid: process.pid }),
|
|
4553
|
+
shouldReclaim: ({ payload }) => isProcessDefinitelyDead(lockPid(payload)),
|
|
4554
|
+
shouldRemoveStaleLock: ({ payload }) => isProcessDefinitelyDead(lockPid(payload))
|
|
4555
|
+
});
|
|
4556
|
+
} catch (error) {
|
|
4557
|
+
if (error.code === "file_lock_timeout") return;
|
|
4558
|
+
throw error;
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4467
4561
|
async function removeObservedLock(filePath, observed) {
|
|
4468
4562
|
const current = await readLock(filePath);
|
|
4469
4563
|
if (!current) return true;
|
|
@@ -4475,64 +4569,105 @@ async function removeObservedLock(filePath, observed) {
|
|
|
4475
4569
|
}
|
|
4476
4570
|
async function recoverAbandonedLock(filePath) {
|
|
4477
4571
|
const observed = await readLock(filePath);
|
|
4478
|
-
if (!observed) return;
|
|
4479
|
-
const pid = lockPid(observed.payload);
|
|
4480
|
-
if (pid
|
|
4481
|
-
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return;
|
|
4482
|
-
if (await removeObservedLock(filePath, observed))
|
|
4572
|
+
if (!observed) return true;
|
|
4573
|
+
const pid = lockPid(parseLock(observed.payload));
|
|
4574
|
+
if (pid && !isProcessDefinitelyDead(pid)) return false;
|
|
4575
|
+
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return false;
|
|
4576
|
+
if (await removeObservedLock(filePath, observed)) {
|
|
4577
|
+
incrementPerfCounter("session.events.stale_lock_recovered");
|
|
4578
|
+
return true;
|
|
4579
|
+
}
|
|
4580
|
+
return false;
|
|
4483
4581
|
}
|
|
4484
4582
|
async function publishLock(filePath, payload, signal) {
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4583
|
+
await withTempFile({
|
|
4584
|
+
rootDir: path.dirname(filePath),
|
|
4585
|
+
prefix: "session-turn",
|
|
4586
|
+
fileName: "lock"
|
|
4587
|
+
}, async (temporaryPath) => {
|
|
4588
|
+
await fs$1.writeFile(temporaryPath, payload, {
|
|
4589
|
+
encoding: "utf8",
|
|
4590
|
+
flag: "wx",
|
|
4591
|
+
mode: 384
|
|
4592
|
+
});
|
|
4593
|
+
signal?.throwIfAborted();
|
|
4594
|
+
try {
|
|
4595
|
+
await fs$1.link(temporaryPath, filePath);
|
|
4596
|
+
} catch (error) {
|
|
4597
|
+
if (!isHardlinkFallbackError(error)) throw error;
|
|
4598
|
+
await fs$1.writeFile(filePath, payload, {
|
|
4492
4599
|
encoding: "utf8",
|
|
4493
4600
|
flag: "wx",
|
|
4494
4601
|
mode: 384
|
|
4495
4602
|
});
|
|
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
|
-
}
|
|
4603
|
+
}
|
|
4604
|
+
});
|
|
4512
4605
|
}
|
|
4513
4606
|
async function removePublishedLock(filePath, payload) {
|
|
4514
4607
|
const current = await readLock(filePath);
|
|
4515
4608
|
if (current?.payload === payload) await removeObservedLock(filePath, current);
|
|
4516
4609
|
}
|
|
4517
|
-
async function
|
|
4610
|
+
async function rejectAcquisition(filePath, payload, guard, error) {
|
|
4611
|
+
const failures = [error];
|
|
4612
|
+
await removePublishedLock(filePath, payload).catch((cleanupError) => {
|
|
4613
|
+
failures.push(cleanupError);
|
|
4614
|
+
});
|
|
4615
|
+
await guard.release().catch((releaseError) => {
|
|
4616
|
+
failures.push(releaseError);
|
|
4617
|
+
});
|
|
4618
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn acquisition and cleanup failed");
|
|
4619
|
+
throw error;
|
|
4620
|
+
}
|
|
4621
|
+
function turnReceipt(filePath, observed, guard) {
|
|
4622
|
+
let disposed = false;
|
|
4623
|
+
let disposal;
|
|
4624
|
+
return { [Symbol.asyncDispose]: async () => {
|
|
4625
|
+
if (disposed) return;
|
|
4626
|
+
const pending = disposal ??= (async () => {
|
|
4627
|
+
await removeObservedLock(filePath, observed);
|
|
4628
|
+
await guard.release();
|
|
4629
|
+
disposed = true;
|
|
4630
|
+
})();
|
|
4631
|
+
try {
|
|
4632
|
+
await pending;
|
|
4633
|
+
} finally {
|
|
4634
|
+
if (disposal === pending) disposal = void 0;
|
|
4635
|
+
}
|
|
4636
|
+
} };
|
|
4637
|
+
}
|
|
4638
|
+
async function tryPublishLock(filePath, payload, signal) {
|
|
4639
|
+
for (;;) try {
|
|
4640
|
+
await publishLock(filePath, payload, signal);
|
|
4641
|
+
return true;
|
|
4642
|
+
} catch (error) {
|
|
4643
|
+
if (error.code !== "EEXIST") throw error;
|
|
4644
|
+
if (!await recoverAbandonedLock(filePath)) return false;
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
async function tryAcquireTurn(filePath, payload, signal) {
|
|
4648
|
+
const guard = await tryAcquireGuard(filePath);
|
|
4649
|
+
if (!guard) return;
|
|
4518
4650
|
try {
|
|
4651
|
+
signal?.throwIfAborted();
|
|
4652
|
+
if (!await tryPublishLock(filePath, payload, signal)) {
|
|
4653
|
+
await guard.release();
|
|
4654
|
+
return;
|
|
4655
|
+
}
|
|
4519
4656
|
const observed = await readLock(filePath);
|
|
4520
4657
|
if (observed?.payload !== payload) throw new Error(`Session turn ownership changed before admission: ${filePath}`);
|
|
4521
4658
|
signal?.throwIfAborted();
|
|
4522
|
-
return
|
|
4523
|
-
await removeObservedLock(filePath, observed);
|
|
4524
|
-
} };
|
|
4659
|
+
return turnReceipt(filePath, observed, guard);
|
|
4525
4660
|
} catch (error) {
|
|
4526
|
-
await
|
|
4527
|
-
throw error;
|
|
4661
|
+
return await rejectAcquisition(filePath, payload, guard, error);
|
|
4528
4662
|
}
|
|
4529
4663
|
}
|
|
4530
4664
|
async function acquireSessionTurn(recordId, signal) {
|
|
4531
|
-
const
|
|
4532
|
-
await fs$1.mkdir(path.dirname(
|
|
4665
|
+
const requestedPath = sessionEventLockPath(recordId);
|
|
4666
|
+
await fs$1.mkdir(path.dirname(requestedPath), {
|
|
4533
4667
|
recursive: true,
|
|
4534
4668
|
mode: 448
|
|
4535
4669
|
});
|
|
4670
|
+
const filePath = path.join(await fs$1.realpath(path.dirname(requestedPath)), path.basename(requestedPath));
|
|
4536
4671
|
lastCreatedAt = Math.max(Date.now(), lastCreatedAt + 1);
|
|
4537
4672
|
const payload = `${JSON.stringify({
|
|
4538
4673
|
pid: process.pid,
|
|
@@ -4540,16 +4675,10 @@ async function acquireSessionTurn(recordId, signal) {
|
|
|
4540
4675
|
}, null, 2)}\n`;
|
|
4541
4676
|
for (;;) {
|
|
4542
4677
|
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
|
-
}
|
|
4678
|
+
const turn = await tryAcquireTurn(filePath, payload, signal);
|
|
4679
|
+
if (turn) return turn;
|
|
4680
|
+
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
4551
4681
|
}
|
|
4552
|
-
return await observePublishedLock(filePath, payload, signal);
|
|
4553
4682
|
}
|
|
4554
4683
|
//#endregion
|
|
4555
4684
|
//#region src/session/queue/ipc-server.ts
|
|
@@ -4730,6 +4859,7 @@ var SessionQueueOwner = class SessionQueueOwner {
|
|
|
4730
4859
|
return true;
|
|
4731
4860
|
}
|
|
4732
4861
|
handleControlRequest(options) {
|
|
4862
|
+
this.taskSockets.add(options.socket);
|
|
4733
4863
|
writeQueueMessage(options.socket, {
|
|
4734
4864
|
type: "accepted",
|
|
4735
4865
|
requestId: options.requestId,
|
|
@@ -5042,7 +5172,7 @@ async function replayDesiredConfigOptions(params) {
|
|
|
5042
5172
|
for (const [configId, value] of Object.entries(params.desiredConfigOptions)) {
|
|
5043
5173
|
if (configId === replacingKey || acceptedConfigOptions && !acceptsSavedConfigValue(acceptedConfigOptions.find((option) => option.id === configId), value)) continue;
|
|
5044
5174
|
try {
|
|
5045
|
-
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value, params.replay.authority), params.timeoutMs);
|
|
5175
|
+
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value, advertisedModelState(params.record.acpx), params.replay.authority), params.timeoutMs);
|
|
5046
5176
|
params.replay.acknowledged = true;
|
|
5047
5177
|
params.record.acpx = applyConfigOptionSelection(params.record.acpx, configId, value, response);
|
|
5048
5178
|
acceptedConfigOptions = response.configOptions;
|
|
@@ -5093,6 +5223,7 @@ async function connectAndLoadSession(options) {
|
|
|
5093
5223
|
timeoutMs: options.timeoutMs
|
|
5094
5224
|
});
|
|
5095
5225
|
const { resumed, loadError, sessionId, createdFreshSession, pendingAgentSessionId, sessionModels } = loadState;
|
|
5226
|
+
applyReconnectedModelState(record, sessionModels, loadState.configOptionsPresent, loadState.legacyModelMetadataPresent, createdFreshSession);
|
|
5096
5227
|
const preferenceReplay = await replaySessionPreferences({
|
|
5097
5228
|
client,
|
|
5098
5229
|
record,
|
|
@@ -5313,7 +5444,7 @@ function createActiveSessionController(params) {
|
|
|
5313
5444
|
return response;
|
|
5314
5445
|
},
|
|
5315
5446
|
setSessionConfigOption: async (configId, value) => {
|
|
5316
|
-
return await params.client.setSessionConfigOption(getActiveSessionId(), configId, value);
|
|
5447
|
+
return await params.client.setSessionConfigOption(getActiveSessionId(), configId, value, advertisedModelState(params.record.acpx));
|
|
5317
5448
|
}
|
|
5318
5449
|
};
|
|
5319
5450
|
}
|
|
@@ -5436,14 +5567,38 @@ function toSessionMutationResult(result) {
|
|
|
5436
5567
|
loadError: result.loadError
|
|
5437
5568
|
};
|
|
5438
5569
|
}
|
|
5570
|
+
async function withOwnedDirectSession(options) {
|
|
5571
|
+
const waiting = new AbortController();
|
|
5572
|
+
const timeoutMs = options.timeoutMs;
|
|
5573
|
+
const timer = timeoutMs != null && timeoutMs > 0 ? setTimeout(() => waiting.abort(new TimeoutError(timeoutMs)), timeoutMs) : void 0;
|
|
5574
|
+
let ownership;
|
|
5575
|
+
let recordId;
|
|
5576
|
+
try {
|
|
5577
|
+
recordId = (await options.loadRecord(options.sessionRecordId)).acpxRecordId;
|
|
5578
|
+
ownership = await acquireSessionTurn(recordId, waiting.signal);
|
|
5579
|
+
} catch (error) {
|
|
5580
|
+
waiting.signal.throwIfAborted();
|
|
5581
|
+
throw error;
|
|
5582
|
+
} finally {
|
|
5583
|
+
clearTimeout(timer);
|
|
5584
|
+
}
|
|
5585
|
+
try {
|
|
5586
|
+
return await withConnectedSession({
|
|
5587
|
+
...options,
|
|
5588
|
+
sessionRecordId: recordId
|
|
5589
|
+
});
|
|
5590
|
+
} finally {
|
|
5591
|
+
await ownership[Symbol.asyncDispose]();
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5439
5594
|
async function runSessionSetModeDirect(options) {
|
|
5440
|
-
return toSessionMutationResult(await
|
|
5595
|
+
return toSessionMutationResult(await withOwnedDirectSession(buildDirectConnectedSessionOptions(options, async ({ client, sessionId, record }) => {
|
|
5441
5596
|
await withTimeout(client.setSessionMode(sessionId, options.modeId), options.timeoutMs);
|
|
5442
5597
|
setDesiredModeId(record, options.modeId);
|
|
5443
5598
|
})));
|
|
5444
5599
|
}
|
|
5445
5600
|
async function runSessionSetModelDirect(options) {
|
|
5446
|
-
const result = await
|
|
5601
|
+
const result = await withOwnedDirectSession(buildDirectConnectedSessionOptions({
|
|
5447
5602
|
...options,
|
|
5448
5603
|
replacingConfigOption: { key: "model" }
|
|
5449
5604
|
}, async ({ client, sessionId, record }) => {
|
|
@@ -5458,12 +5613,13 @@ async function runSessionSetModelDirect(options) {
|
|
|
5458
5613
|
};
|
|
5459
5614
|
}
|
|
5460
5615
|
async function runSessionSetConfigOptionDirect(options) {
|
|
5461
|
-
const result = await
|
|
5616
|
+
const result = await withOwnedDirectSession(buildDirectConnectedSessionOptions({
|
|
5462
5617
|
...options,
|
|
5463
5618
|
replacingConfigOption: { key: options.configId }
|
|
5464
5619
|
}, async ({ client, sessionId, record }) => {
|
|
5465
|
-
const
|
|
5466
|
-
|
|
5620
|
+
const models = advertisedModelState(record.acpx);
|
|
5621
|
+
const response = await withTimeout(client.setSessionConfigOption(sessionId, options.configId, options.value, models), options.timeoutMs);
|
|
5622
|
+
record.acpx = applyConfigOptionSelection(record.acpx, options.configId, options.value, response, models?.configId);
|
|
5467
5623
|
return response;
|
|
5468
5624
|
}));
|
|
5469
5625
|
return {
|
|
@@ -5482,16 +5638,31 @@ async function cancelSessionPrompt(options) {
|
|
|
5482
5638
|
cancelled: cancelled === true
|
|
5483
5639
|
};
|
|
5484
5640
|
}
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5641
|
+
/**
|
|
5642
|
+
* Dispatches the mode change to a running queue owner. Returns `undefined`
|
|
5643
|
+
* when no owner holds the session, leaving the direct-connection decision to
|
|
5644
|
+
* the caller.
|
|
5645
|
+
*
|
|
5646
|
+
* An owner advertising `persistsControlState` saves the accepted state itself.
|
|
5647
|
+
* Only the older caller-save path still reloads and rewrites the record here,
|
|
5648
|
+
* which is not serialized against another process doing the same.
|
|
5649
|
+
*/
|
|
5650
|
+
async function setSessionModeOnOwner(options) {
|
|
5651
|
+
const submittedToOwner = await trySetModeOnRunningOwner(options.sessionId, options.modeId, options.timeoutMs, options.verbose, options.assertDispatch);
|
|
5652
|
+
if (!submittedToOwner?.value) return;
|
|
5653
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5654
|
+
if (!submittedToOwner.persistsControlState) {
|
|
5488
5655
|
setDesiredModeId(record, options.modeId);
|
|
5489
5656
|
await writeSessionRecord(record);
|
|
5490
|
-
return {
|
|
5491
|
-
record,
|
|
5492
|
-
resumed: false
|
|
5493
|
-
};
|
|
5494
5657
|
}
|
|
5658
|
+
return {
|
|
5659
|
+
record,
|
|
5660
|
+
resumed: false
|
|
5661
|
+
};
|
|
5662
|
+
}
|
|
5663
|
+
async function setSessionMode(options) {
|
|
5664
|
+
const onOwner = await setSessionModeOnOwner(options);
|
|
5665
|
+
if (onOwner) return onOwner;
|
|
5495
5666
|
return await runSessionSetModeDirect({
|
|
5496
5667
|
sessionRecordId: options.sessionId,
|
|
5497
5668
|
modeId: options.modeId,
|
|
@@ -5505,18 +5676,24 @@ async function setSessionMode(options) {
|
|
|
5505
5676
|
verbose: options.verbose
|
|
5506
5677
|
});
|
|
5507
5678
|
}
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5679
|
+
/** Owner-only counterpart of {@link setSessionMode} for the model control. */
|
|
5680
|
+
async function setSessionModelOnOwner(options) {
|
|
5681
|
+
const submittedToOwner = await trySetModelOnRunningOwner(options.sessionId, options.modelId, options.timeoutMs, options.verbose, options.assertDispatch);
|
|
5682
|
+
if (!submittedToOwner) return;
|
|
5683
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5684
|
+
if (!submittedToOwner.persistsControlState) {
|
|
5685
|
+
record.acpx = applyModelSelection(record.acpx, options.modelId, submittedToOwner.value.response);
|
|
5513
5686
|
await writeSessionRecord(record);
|
|
5514
|
-
return {
|
|
5515
|
-
record,
|
|
5516
|
-
response: submittedToOwner.response,
|
|
5517
|
-
resumed: false
|
|
5518
|
-
};
|
|
5519
5687
|
}
|
|
5688
|
+
return {
|
|
5689
|
+
record,
|
|
5690
|
+
response: submittedToOwner.value.response,
|
|
5691
|
+
resumed: false
|
|
5692
|
+
};
|
|
5693
|
+
}
|
|
5694
|
+
async function setSessionModel(options) {
|
|
5695
|
+
const onOwner = await setSessionModelOnOwner(options);
|
|
5696
|
+
if (onOwner) return onOwner;
|
|
5520
5697
|
return await runSessionSetModelDirect({
|
|
5521
5698
|
sessionRecordId: options.sessionId,
|
|
5522
5699
|
modelId: options.modelId,
|
|
@@ -5530,18 +5707,24 @@ async function setSessionModel(options) {
|
|
|
5530
5707
|
verbose: options.verbose
|
|
5531
5708
|
});
|
|
5532
5709
|
}
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5710
|
+
/** Owner-only counterpart of {@link setSessionMode} for the config option control. */
|
|
5711
|
+
async function setSessionConfigOptionOnOwner(options) {
|
|
5712
|
+
const ownerResponse = await trySetConfigOptionOnRunningOwner(options.sessionId, options.configId, options.value, options.timeoutMs, options.verbose, options.assertDispatch);
|
|
5713
|
+
if (!ownerResponse) return;
|
|
5714
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5715
|
+
if (!ownerResponse.persistsControlState) {
|
|
5716
|
+
record.acpx = applyConfigOptionSelection(record.acpx, options.configId, options.value, ownerResponse.value);
|
|
5538
5717
|
await writeSessionRecord(record);
|
|
5539
|
-
return {
|
|
5540
|
-
record,
|
|
5541
|
-
response: ownerResponse,
|
|
5542
|
-
resumed: false
|
|
5543
|
-
};
|
|
5544
5718
|
}
|
|
5719
|
+
return {
|
|
5720
|
+
record,
|
|
5721
|
+
response: ownerResponse.value,
|
|
5722
|
+
resumed: false
|
|
5723
|
+
};
|
|
5724
|
+
}
|
|
5725
|
+
async function setSessionConfigOption(options) {
|
|
5726
|
+
const onOwner = await setSessionConfigOptionOnOwner(options);
|
|
5727
|
+
if (onOwner) return onOwner;
|
|
5545
5728
|
return await runSessionSetConfigOptionDirect({
|
|
5546
5729
|
sessionRecordId: options.sessionId,
|
|
5547
5730
|
configId: options.configId,
|
|
@@ -5626,11 +5809,17 @@ async function closeSession(sessionId) {
|
|
|
5626
5809
|
await tryCloseSessionOnRunningOwner({ sessionId: record.acpxRecordId }).catch(() => {});
|
|
5627
5810
|
await terminateQueueOwnerForSession(record.acpxRecordId);
|
|
5628
5811
|
if (record.pid != null && isProcessAlive(record.pid) && await isLikelyMatchingProcess(record.pid, record.agentCommand)) await terminateProcess(record.pid);
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5812
|
+
const ownership = await acquireSessionTurn(record.acpxRecordId);
|
|
5813
|
+
try {
|
|
5814
|
+
const current = await resolveSessionRecord(record.acpxRecordId);
|
|
5815
|
+
current.pid = void 0;
|
|
5816
|
+
current.closed = true;
|
|
5817
|
+
current.closedAt = isoNow$1();
|
|
5818
|
+
await writeSessionRecord(current);
|
|
5819
|
+
return current;
|
|
5820
|
+
} finally {
|
|
5821
|
+
await ownership[Symbol.asyncDispose]();
|
|
5822
|
+
}
|
|
5634
5823
|
}
|
|
5635
5824
|
//#endregion
|
|
5636
5825
|
//#region src/session/execution/session-management.ts
|
|
@@ -5951,16 +6140,182 @@ function installPerfMetricsCapture(options = {}) {
|
|
|
5951
6140
|
}
|
|
5952
6141
|
}
|
|
5953
6142
|
//#endregion
|
|
6143
|
+
//#region src/session/queue/control-admission.ts
|
|
6144
|
+
var QueueControlDeadline = class {
|
|
6145
|
+
controller = new AbortController();
|
|
6146
|
+
admissionSignal;
|
|
6147
|
+
expiresAt;
|
|
6148
|
+
expired;
|
|
6149
|
+
timeoutError;
|
|
6150
|
+
rejectExpiry;
|
|
6151
|
+
timer;
|
|
6152
|
+
nativeSettled = false;
|
|
6153
|
+
admitted = false;
|
|
6154
|
+
constructor(timeoutMs, ownerSignal) {
|
|
6155
|
+
this.admissionSignal = ownerSignal ? AbortSignal.any([this.controller.signal, ownerSignal]) : this.controller.signal;
|
|
6156
|
+
this.expiresAt = timeoutMs != null && timeoutMs > 0 ? performance.now() + timeoutMs : void 0;
|
|
6157
|
+
this.timeoutError = this.expiresAt === void 0 ? void 0 : new TimeoutError(timeoutMs);
|
|
6158
|
+
this.expired = new Promise((_resolve, reject) => {
|
|
6159
|
+
this.rejectExpiry = reject;
|
|
6160
|
+
if (this.expiresAt !== void 0) this.timer = setTimeout(() => this.expire(), timeoutMs);
|
|
6161
|
+
});
|
|
6162
|
+
this.expired.catch(() => {});
|
|
6163
|
+
}
|
|
6164
|
+
get signal() {
|
|
6165
|
+
return this.admissionSignal;
|
|
6166
|
+
}
|
|
6167
|
+
get timeoutSignal() {
|
|
6168
|
+
return this.controller.signal;
|
|
6169
|
+
}
|
|
6170
|
+
get remainingMs() {
|
|
6171
|
+
this.assertActive();
|
|
6172
|
+
return this.expiresAt === void 0 || this.nativeSettled ? void 0 : Math.max(1, this.expiresAt - performance.now());
|
|
6173
|
+
}
|
|
6174
|
+
get nativeAdmitted() {
|
|
6175
|
+
return this.admitted;
|
|
6176
|
+
}
|
|
6177
|
+
get authority() {
|
|
6178
|
+
return {
|
|
6179
|
+
signal: this.signal,
|
|
6180
|
+
assertActive: () => {
|
|
6181
|
+
this.assertActive();
|
|
6182
|
+
this.admitted = true;
|
|
6183
|
+
}
|
|
6184
|
+
};
|
|
6185
|
+
}
|
|
6186
|
+
assertActive() {
|
|
6187
|
+
if (this.expiresAt !== void 0 && performance.now() >= this.expiresAt) this.expire();
|
|
6188
|
+
this.signal.throwIfAborted();
|
|
6189
|
+
}
|
|
6190
|
+
expire() {
|
|
6191
|
+
if (this.nativeSettled || this.timeoutSignal.aborted || !this.timeoutError) return;
|
|
6192
|
+
this.controller.abort(this.timeoutError);
|
|
6193
|
+
this.rejectExpiry(this.timeoutError);
|
|
6194
|
+
}
|
|
6195
|
+
responseSettled() {
|
|
6196
|
+
this.nativeSettled = true;
|
|
6197
|
+
clearTimeout(this.timer);
|
|
6198
|
+
this.timer = void 0;
|
|
6199
|
+
}
|
|
6200
|
+
isExpired(error) {
|
|
6201
|
+
return this.timeoutSignal.aborted && error === this.timeoutSignal.reason;
|
|
6202
|
+
}
|
|
6203
|
+
async wait(operation) {
|
|
6204
|
+
return await Promise.race([operation, this.expired]);
|
|
6205
|
+
}
|
|
6206
|
+
};
|
|
6207
|
+
function deferred() {
|
|
6208
|
+
let resolve;
|
|
6209
|
+
let reject;
|
|
6210
|
+
const promise = new Promise((accept, fail) => {
|
|
6211
|
+
resolve = accept;
|
|
6212
|
+
reject = fail;
|
|
6213
|
+
});
|
|
6214
|
+
promise.catch(() => {});
|
|
6215
|
+
return {
|
|
6216
|
+
promise,
|
|
6217
|
+
resolve,
|
|
6218
|
+
reject
|
|
6219
|
+
};
|
|
6220
|
+
}
|
|
6221
|
+
function drained(operation) {
|
|
6222
|
+
return operation.then(() => {}, () => {});
|
|
6223
|
+
}
|
|
6224
|
+
var QueueOwnerControlAdmission = class {
|
|
6225
|
+
options;
|
|
6226
|
+
idleTail = Promise.resolve();
|
|
6227
|
+
ticket;
|
|
6228
|
+
stopped = new AbortController();
|
|
6229
|
+
pending = 0;
|
|
6230
|
+
constructor(options) {
|
|
6231
|
+
this.options = options;
|
|
6232
|
+
}
|
|
6233
|
+
get hasPending() {
|
|
6234
|
+
return this.pending > 0;
|
|
6235
|
+
}
|
|
6236
|
+
beginPrompt() {
|
|
6237
|
+
const ticket = {
|
|
6238
|
+
ready: deferred(),
|
|
6239
|
+
released: deferred(),
|
|
6240
|
+
rawTail: Promise.resolve(),
|
|
6241
|
+
sealed: false
|
|
6242
|
+
};
|
|
6243
|
+
this.ticket = ticket;
|
|
6244
|
+
return {
|
|
6245
|
+
ticket,
|
|
6246
|
+
priorIdle: this.idleTail
|
|
6247
|
+
};
|
|
6248
|
+
}
|
|
6249
|
+
publish(ticket, controls) {
|
|
6250
|
+
if (!ticket.sealed) ticket.ready.resolve(controls);
|
|
6251
|
+
}
|
|
6252
|
+
seal(ticket) {
|
|
6253
|
+
ticket.sealed = true;
|
|
6254
|
+
ticket.ready.reject(new QueueConnectionError("Prompt ended before controls became ready", {
|
|
6255
|
+
detailCode: "QUEUE_CONTROL_REQUEST_FAILED",
|
|
6256
|
+
origin: "queue",
|
|
6257
|
+
retryable: true
|
|
6258
|
+
}));
|
|
6259
|
+
return ticket.rawTail;
|
|
6260
|
+
}
|
|
6261
|
+
release(ticket) {
|
|
6262
|
+
if (this.ticket === ticket) this.ticket = void 0;
|
|
6263
|
+
ticket.released.resolve();
|
|
6264
|
+
}
|
|
6265
|
+
stopAdmission() {
|
|
6266
|
+
this.stopped.abort(new QueueConnectionError("Queue owner is closing", {
|
|
6267
|
+
detailCode: "QUEUE_OWNER_SHUTTING_DOWN",
|
|
6268
|
+
origin: "queue",
|
|
6269
|
+
retryable: true
|
|
6270
|
+
}));
|
|
6271
|
+
}
|
|
6272
|
+
async drainAdmitted() {
|
|
6273
|
+
await Promise.all([this.idleTail, this.ticket?.rawTail]);
|
|
6274
|
+
}
|
|
6275
|
+
async run(invoke, timeoutMs, replacingConfigOption) {
|
|
6276
|
+
const deadline = new QueueControlDeadline(timeoutMs, this.stopped.signal);
|
|
6277
|
+
this.pending += 1;
|
|
6278
|
+
const operation = this.enqueue(invoke, {
|
|
6279
|
+
deadline,
|
|
6280
|
+
replacingConfigOption
|
|
6281
|
+
});
|
|
6282
|
+
operation.finally(() => {
|
|
6283
|
+
this.pending -= 1;
|
|
6284
|
+
deadline.responseSettled();
|
|
6285
|
+
}).catch(() => {});
|
|
6286
|
+
return await deadline.wait(operation);
|
|
6287
|
+
}
|
|
6288
|
+
async enqueue(invoke, options) {
|
|
6289
|
+
options.deadline.assertActive();
|
|
6290
|
+
const ticket = this.ticket;
|
|
6291
|
+
if (ticket?.sealed) return ticket.released.promise.then(() => this.enqueue(invoke, options));
|
|
6292
|
+
if (ticket) {
|
|
6293
|
+
const operation = ticket.rawTail.then(async () => {
|
|
6294
|
+
const controls = await ticket.ready.promise.catch((error) => {
|
|
6295
|
+
options.deadline.assertActive();
|
|
6296
|
+
throw error;
|
|
6297
|
+
});
|
|
6298
|
+
options.deadline.assertActive();
|
|
6299
|
+
return await invoke(controls, options.deadline);
|
|
6300
|
+
});
|
|
6301
|
+
ticket.rawTail = drained(operation);
|
|
6302
|
+
return operation;
|
|
6303
|
+
}
|
|
6304
|
+
const operation = this.idleTail.then(async () => {
|
|
6305
|
+
options.deadline.assertActive();
|
|
6306
|
+
return await this.options.runIdle(invoke, options);
|
|
6307
|
+
});
|
|
6308
|
+
this.idleTail = drained(operation);
|
|
6309
|
+
return operation;
|
|
6310
|
+
}
|
|
6311
|
+
};
|
|
6312
|
+
//#endregion
|
|
5954
6313
|
//#region src/session/queue/owner-turn-controller.ts
|
|
5955
6314
|
var QueueOwnerTurnController = class {
|
|
5956
|
-
options;
|
|
5957
6315
|
state = "idle";
|
|
5958
6316
|
pendingCancel = false;
|
|
5959
6317
|
activeController;
|
|
5960
6318
|
waitingTurn;
|
|
5961
|
-
constructor(options) {
|
|
5962
|
-
this.options = options;
|
|
5963
|
-
}
|
|
5964
6319
|
get lifecycleState() {
|
|
5965
6320
|
return this.state;
|
|
5966
6321
|
}
|
|
@@ -5992,20 +6347,9 @@ var QueueOwnerTurnController = class {
|
|
|
5992
6347
|
clearActiveController() {
|
|
5993
6348
|
this.activeController = void 0;
|
|
5994
6349
|
}
|
|
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
6350
|
async requestCancel() {
|
|
6003
6351
|
const activeController = this.activeController;
|
|
6004
|
-
if (activeController?.hasActivePrompt())
|
|
6005
|
-
const cancelled = await activeController.requestCancelActivePrompt();
|
|
6006
|
-
if (cancelled) this.pendingCancel = false;
|
|
6007
|
-
return cancelled;
|
|
6008
|
-
}
|
|
6352
|
+
if (activeController?.hasActivePrompt()) return await this.cancelActivePrompt(activeController);
|
|
6009
6353
|
if (this.state === "starting" || this.state === "active") {
|
|
6010
6354
|
this.pendingCancel = true;
|
|
6011
6355
|
this.waitingTurn?.abort();
|
|
@@ -6016,32 +6360,138 @@ var QueueOwnerTurnController = class {
|
|
|
6016
6360
|
async applyPendingCancel() {
|
|
6017
6361
|
const activeController = this.activeController;
|
|
6018
6362
|
if (!this.pendingCancel || !activeController || !activeController.hasActivePrompt()) return false;
|
|
6019
|
-
|
|
6020
|
-
|
|
6363
|
+
return await this.cancelActivePrompt(activeController);
|
|
6364
|
+
}
|
|
6365
|
+
async cancelActivePrompt(activeController) {
|
|
6366
|
+
const turn = this.waitingTurn;
|
|
6367
|
+
const cancellation = activeController.requestCancelActivePrompt();
|
|
6368
|
+
turn?.abort();
|
|
6369
|
+
const cancelled = await cancellation;
|
|
6370
|
+
if (cancelled && this.waitingTurn === turn) this.pendingCancel = false;
|
|
6021
6371
|
return cancelled;
|
|
6022
6372
|
}
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6373
|
+
};
|
|
6374
|
+
//#endregion
|
|
6375
|
+
//#region src/session/execution/owned-controls.ts
|
|
6376
|
+
async function acceptControl(options, invoke, apply, deadline = new QueueControlDeadline()) {
|
|
6377
|
+
deadline.assertActive();
|
|
6378
|
+
const raw = invoke(deadline.authority).then(async (response) => {
|
|
6379
|
+
deadline.responseSettled();
|
|
6380
|
+
apply(response);
|
|
6381
|
+
await options.checkpoint();
|
|
6382
|
+
return response;
|
|
6383
|
+
}, (error) => {
|
|
6384
|
+
deadline.responseSettled();
|
|
6385
|
+
throw error;
|
|
6386
|
+
});
|
|
6387
|
+
try {
|
|
6388
|
+
return await deadline.wait(raw);
|
|
6389
|
+
} catch (error) {
|
|
6390
|
+
if (deadline.isExpired(error) && deadline.nativeAdmitted) {
|
|
6391
|
+
const [retired] = await Promise.allSettled([options.retire(), raw]);
|
|
6392
|
+
if (retired.status === "rejected") throw retired.reason;
|
|
6029
6393
|
}
|
|
6030
|
-
|
|
6394
|
+
throw error;
|
|
6031
6395
|
}
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6396
|
+
}
|
|
6397
|
+
function createOwnedSessionControls(options) {
|
|
6398
|
+
return {
|
|
6399
|
+
setSessionMode: async (modeId, deadline) => {
|
|
6400
|
+
await acceptControl(options, (authority) => options.client.setSessionMode(options.sessionId(), modeId, authority), () => setDesiredModeId(options.record, modeId), deadline);
|
|
6401
|
+
},
|
|
6402
|
+
setSessionModel: async (modelId, deadline) => {
|
|
6403
|
+
const models = advertisedModelState(options.record.acpx);
|
|
6404
|
+
return await acceptControl(options, (authority) => options.client.setSessionModel(options.sessionId(), modelId, models, authority), (response) => {
|
|
6405
|
+
options.record.acpx = applyModelSelection(options.record.acpx, modelId, response);
|
|
6406
|
+
}, deadline);
|
|
6407
|
+
},
|
|
6408
|
+
setSessionConfigOption: async (configId, value, deadline) => {
|
|
6409
|
+
const models = advertisedModelState(options.record.acpx);
|
|
6410
|
+
return await acceptControl(options, (authority) => options.client.setSessionConfigOption(options.sessionId(), configId, value, models, authority), (response) => {
|
|
6411
|
+
options.record.acpx = applyConfigOptionSelection(options.record.acpx, configId, value, response, models?.configId);
|
|
6412
|
+
}, deadline);
|
|
6413
|
+
}
|
|
6414
|
+
};
|
|
6415
|
+
}
|
|
6416
|
+
async function runIdleControlWithRecord(options, record, invoke) {
|
|
6417
|
+
const { client, deadline } = options;
|
|
6418
|
+
deadline.assertActive();
|
|
6419
|
+
let retirement;
|
|
6420
|
+
const retire = () => {
|
|
6421
|
+
retirement ??= client.close().catch((error) => {
|
|
6422
|
+
options.onRetirementFailure();
|
|
6423
|
+
throw error;
|
|
6424
|
+
});
|
|
6425
|
+
return retirement;
|
|
6426
|
+
};
|
|
6427
|
+
const onAbort = () => {
|
|
6428
|
+
retire().catch(() => {});
|
|
6429
|
+
};
|
|
6430
|
+
const checkpoint = async () => {
|
|
6431
|
+
record.lastUsedAt = isoNow$1();
|
|
6432
|
+
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
6433
|
+
await writeSessionRecord(record);
|
|
6434
|
+
};
|
|
6435
|
+
let sessionId = record.acpSessionId;
|
|
6436
|
+
const controls = createOwnedSessionControls({
|
|
6437
|
+
client,
|
|
6438
|
+
record,
|
|
6439
|
+
sessionId: () => sessionId,
|
|
6440
|
+
checkpoint,
|
|
6441
|
+
retire
|
|
6442
|
+
});
|
|
6443
|
+
client.setEventHandlers({ onSessionUpdate: (notification) => {
|
|
6444
|
+
record.acpx = recordSessionUpdate(record, record.acpx, notification);
|
|
6445
|
+
} });
|
|
6446
|
+
deadline.timeoutSignal.addEventListener("abort", onAbort, { once: true });
|
|
6447
|
+
let connected = false;
|
|
6448
|
+
let completed = false;
|
|
6449
|
+
try {
|
|
6450
|
+
await connectAndLoadSession({
|
|
6451
|
+
client,
|
|
6452
|
+
record,
|
|
6453
|
+
resumePolicy: "same-session-only",
|
|
6454
|
+
replacingConfigOption: options.replacingConfigOption,
|
|
6455
|
+
timeoutMs: deadline.remainingMs,
|
|
6456
|
+
authority: deadline.authority,
|
|
6457
|
+
verbose: options.verbose,
|
|
6458
|
+
activeController: {
|
|
6459
|
+
...controls,
|
|
6460
|
+
hasActivePrompt: () => client.hasActivePrompt(),
|
|
6461
|
+
requestCancelActivePrompt: () => client.requestCancelActivePrompt()
|
|
6462
|
+
},
|
|
6463
|
+
onSessionIdResolved: (value) => {
|
|
6464
|
+
sessionId = value;
|
|
6465
|
+
}
|
|
6466
|
+
});
|
|
6467
|
+
connected = true;
|
|
6468
|
+
deadline.assertActive();
|
|
6469
|
+
const value = await invoke(controls, deadline);
|
|
6470
|
+
completed = true;
|
|
6471
|
+
return value;
|
|
6472
|
+
} finally {
|
|
6473
|
+
deadline.timeoutSignal.removeEventListener("abort", onAbort);
|
|
6474
|
+
try {
|
|
6475
|
+
if (!connected) await retire();
|
|
6476
|
+
await retirement;
|
|
6477
|
+
if (!completed || retirement) await checkpoint().catch(() => {});
|
|
6478
|
+
} finally {
|
|
6479
|
+
client.clearEventHandlers();
|
|
6480
|
+
}
|
|
6037
6481
|
}
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6482
|
+
}
|
|
6483
|
+
async function runIdleOwnerControl(options, invoke) {
|
|
6484
|
+
const { acpxRecordId } = await resolveSessionRecord(options.sessionId);
|
|
6485
|
+
const ownership = await acquireSessionTurn(acpxRecordId, options.deadline.signal).catch((error) => {
|
|
6486
|
+
options.deadline.assertActive();
|
|
6487
|
+
throw error;
|
|
6488
|
+
});
|
|
6489
|
+
try {
|
|
6490
|
+
return await runIdleControlWithRecord(options, await resolveSessionRecord(acpxRecordId), invoke);
|
|
6491
|
+
} finally {
|
|
6492
|
+
await ownership[Symbol.asyncDispose]();
|
|
6043
6493
|
}
|
|
6044
|
-
}
|
|
6494
|
+
}
|
|
6045
6495
|
//#endregion
|
|
6046
6496
|
//#region src/session/execution/queue-owner-process.ts
|
|
6047
6497
|
function isNonEmptyStringArray(value) {
|
|
@@ -6223,20 +6673,27 @@ function responseMetaField(meta) {
|
|
|
6223
6673
|
return meta === void 0 ? {} : { _meta: meta };
|
|
6224
6674
|
}
|
|
6225
6675
|
function recoveredSessionResult(response, conversation, promptMessageId) {
|
|
6226
|
-
recordPromptResponseUsage(conversation, response
|
|
6676
|
+
recordPromptResponseUsage(conversation, response.usage, promptMessageId);
|
|
6227
6677
|
return {
|
|
6228
|
-
stopReason:
|
|
6678
|
+
stopReason: response.stopReason,
|
|
6229
6679
|
source: "session",
|
|
6230
|
-
...responseMetaField(response
|
|
6680
|
+
...responseMetaField(response._meta)
|
|
6231
6681
|
};
|
|
6232
6682
|
}
|
|
6233
|
-
function abortTimedOutRequests(error,
|
|
6234
|
-
if (error instanceof TimeoutError)
|
|
6683
|
+
function abortTimedOutRequests(error, lifetime) {
|
|
6684
|
+
if (error instanceof TimeoutError) lifetime.abort(error);
|
|
6685
|
+
}
|
|
6686
|
+
function promptRequestAuthority(lifetime, authority) {
|
|
6687
|
+
return {
|
|
6688
|
+
signal: authority?.signal ? AbortSignal.any([authority.signal, lifetime.signal]) : lifetime.signal,
|
|
6689
|
+
assertActive: () => assertControlAuthority(authority)
|
|
6690
|
+
};
|
|
6235
6691
|
}
|
|
6236
6692
|
async function runPromptTurn(params) {
|
|
6237
6693
|
let settledResponse;
|
|
6694
|
+
const lifetime = new AbortController();
|
|
6238
6695
|
try {
|
|
6239
|
-
const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation, params.onPermissionRequest, params.authority);
|
|
6696
|
+
const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation, params.onPermissionRequest, promptRequestAuthority(lifetime, params.authority));
|
|
6240
6697
|
promptPromise.then((response) => {
|
|
6241
6698
|
settledResponse = response;
|
|
6242
6699
|
}, () => {});
|
|
@@ -6253,14 +6710,16 @@ async function runPromptTurn(params) {
|
|
|
6253
6710
|
...responseMetaField(response._meta)
|
|
6254
6711
|
};
|
|
6255
6712
|
} catch (error) {
|
|
6256
|
-
abortTimedOutRequests(error,
|
|
6713
|
+
abortTimedOutRequests(error, lifetime);
|
|
6257
6714
|
if (!(error instanceof TimeoutError) || !params.promptMessageId) throw error;
|
|
6258
6715
|
await params.client.waitForSessionUpdatesIdle?.({
|
|
6259
6716
|
idleMs: SESSION_REPLY_IDLE_MS,
|
|
6260
6717
|
timeoutMs: SESSION_REPLY_DRAIN_TIMEOUT_MS
|
|
6261
6718
|
}).catch(() => {});
|
|
6262
|
-
if (
|
|
6719
|
+
if (settledResponse) return recoveredSessionResult(settledResponse, params.conversation, params.promptMessageId);
|
|
6263
6720
|
throw error;
|
|
6721
|
+
} finally {
|
|
6722
|
+
lifetime.abort();
|
|
6264
6723
|
}
|
|
6265
6724
|
}
|
|
6266
6725
|
//#endregion
|
|
@@ -6510,8 +6969,9 @@ function emitPromptDisconnectNotice(snapshot, verbose) {
|
|
|
6510
6969
|
if (!lastExit?.unexpectedDuringPrompt || !verbose) return;
|
|
6511
6970
|
process.stderr.write("[acpx] agent disconnected during prompt (" + lastExit.reason + ", exit=" + lastExit.exitCode + ", signal=" + (lastExit.signal ?? "none") + ")\n");
|
|
6512
6971
|
}
|
|
6513
|
-
async function preparePromptRetry(error, attempt, maxRetries, hasSideEffects, suppressSdkConsoleErrors) {
|
|
6972
|
+
async function preparePromptRetry(error, attempt, maxRetries, hasSideEffects, suppressSdkConsoleErrors, signal) {
|
|
6514
6973
|
if (attempt < maxRetries && !hasSideEffects() && isRetryablePromptError(error)) {
|
|
6974
|
+
signal?.throwIfAborted();
|
|
6515
6975
|
const delayMs = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
6516
6976
|
emitPromptRetryNotice({
|
|
6517
6977
|
error,
|
|
@@ -6520,11 +6980,18 @@ async function preparePromptRetry(error, attempt, maxRetries, hasSideEffects, su
|
|
|
6520
6980
|
maxRetries,
|
|
6521
6981
|
suppressSdkConsoleErrors
|
|
6522
6982
|
});
|
|
6523
|
-
await
|
|
6983
|
+
await setTimeout$1(delayMs, void 0, { signal }).catch((error) => {
|
|
6984
|
+
signal?.throwIfAborted();
|
|
6985
|
+
throw error;
|
|
6986
|
+
});
|
|
6987
|
+
signal?.throwIfAborted();
|
|
6524
6988
|
return !hasSideEffects();
|
|
6525
6989
|
}
|
|
6526
6990
|
return false;
|
|
6527
6991
|
}
|
|
6992
|
+
function isTurnCancellation(error, signal) {
|
|
6993
|
+
return signal?.aborted === true && error === signal.reason;
|
|
6994
|
+
}
|
|
6528
6995
|
function buildQueuedTaskRunOptions(sessionRecordId, task, options, outputFormatter) {
|
|
6529
6996
|
return {
|
|
6530
6997
|
sessionRecordId,
|
|
@@ -6546,7 +7013,9 @@ function buildQueuedTaskRunOptions(sessionRecordId, task, options, outputFormatt
|
|
|
6546
7013
|
sessionOptions: mergeSessionOptions(task.sessionOptions, options.sessionOptions),
|
|
6547
7014
|
onClientAvailable: options.onClientAvailable,
|
|
6548
7015
|
onClientClosed: options.onClientClosed,
|
|
7016
|
+
onClientCloseFailure: options.onClientCloseFailure,
|
|
6549
7017
|
onPromptActive: options.onPromptActive,
|
|
7018
|
+
onPromptFinalizing: options.onPromptFinalizing,
|
|
6550
7019
|
onPromptRequestWritten: () => {
|
|
6551
7020
|
if (task.reportPromptStarted) task.send({
|
|
6552
7021
|
type: "prompt_started",
|
|
@@ -6852,24 +7321,23 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6852
7321
|
});
|
|
6853
7322
|
let activeSessionIdForControl = record.acpSessionId;
|
|
6854
7323
|
let notifiedClientAvailable = false;
|
|
7324
|
+
let controlRetirement;
|
|
6855
7325
|
const activeController = {
|
|
6856
7326
|
hasActivePrompt: () => client.hasActivePrompt(),
|
|
6857
7327
|
requestCancelActivePrompt: async () => await client.requestCancelActivePrompt(),
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
return response;
|
|
6872
|
-
}
|
|
7328
|
+
...createOwnedSessionControls({
|
|
7329
|
+
client,
|
|
7330
|
+
record,
|
|
7331
|
+
sessionId: () => activeSessionIdForControl,
|
|
7332
|
+
checkpoint: () => liveCheckpoint.checkpoint(),
|
|
7333
|
+
retire: () => {
|
|
7334
|
+
controlRetirement ??= client.close().catch((error) => {
|
|
7335
|
+
options.onClientCloseFailure?.();
|
|
7336
|
+
throw error;
|
|
7337
|
+
});
|
|
7338
|
+
return controlRetirement;
|
|
7339
|
+
}
|
|
7340
|
+
})
|
|
6873
7341
|
};
|
|
6874
7342
|
const flushConnectOutput = (loadError) => {
|
|
6875
7343
|
bufferingConnectOutput = false;
|
|
@@ -6933,15 +7401,17 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6933
7401
|
conversation,
|
|
6934
7402
|
promptMessageId,
|
|
6935
7403
|
onPromptRequestWritten: options.onPromptRequestWritten,
|
|
6936
|
-
onPromptStarted: buildPromptStartedHook(attempt)
|
|
7404
|
+
onPromptStarted: buildPromptStartedHook(attempt),
|
|
7405
|
+
authority: { signal: options.waitSignal }
|
|
6937
7406
|
});
|
|
6938
7407
|
});
|
|
6939
7408
|
emitPromptPerfMetric(promptStartedAt, options.verbose);
|
|
6940
7409
|
return response;
|
|
6941
7410
|
};
|
|
6942
7411
|
const handlePromptFailure = async (error, attempt) => {
|
|
7412
|
+
if (isTurnCancellation(error, options.waitSignal)) throw error;
|
|
6943
7413
|
const snapshot = client.getAgentLifecycleSnapshot();
|
|
6944
|
-
if (snapshot.lastExit?.unexpectedDuringPrompt !== true && await preparePromptRetry(error, attempt, options.promptRetries ?? 0, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors)) return;
|
|
7414
|
+
if (snapshot.lastExit?.unexpectedDuringPrompt !== true && await preparePromptRetry(error, attempt, options.promptRetries ?? 0, () => promptTurnHadSideEffects, options.suppressSdkConsoleErrors, options.waitSignal)) return;
|
|
6945
7415
|
await failRuntimePrompt(error, snapshot);
|
|
6946
7416
|
};
|
|
6947
7417
|
const failRuntimePrompt = async (error, snapshot) => {
|
|
@@ -6965,10 +7435,18 @@ async function runOwnedSessionPrompt(options) {
|
|
|
6965
7435
|
};
|
|
6966
7436
|
const runPromptWithRetries = async (sessionId) => {
|
|
6967
7437
|
promptTurnActive = true;
|
|
6968
|
-
|
|
6969
|
-
|
|
7438
|
+
try {
|
|
7439
|
+
for (let attempt = 0;; attempt++) {
|
|
7440
|
+
options.waitSignal?.throwIfAborted();
|
|
7441
|
+
try {
|
|
7442
|
+
return await runPromptAttempt(sessionId, attempt);
|
|
7443
|
+
} catch (error) {
|
|
7444
|
+
await handlePromptFailure(error, attempt);
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
6970
7447
|
} catch (error) {
|
|
6971
|
-
|
|
7448
|
+
if (isTurnCancellation(error, options.waitSignal)) return { stopReason: "cancelled" };
|
|
7449
|
+
throw error;
|
|
6972
7450
|
}
|
|
6973
7451
|
};
|
|
6974
7452
|
const savePromptSuccess = async (response) => {
|
|
@@ -7029,7 +7507,24 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7029
7507
|
}
|
|
7030
7508
|
};
|
|
7031
7509
|
const cleanupPrompt = async (attempt) => {
|
|
7510
|
+
const controlDrain = options.onPromptFinalizing?.();
|
|
7511
|
+
const unresolvedPrompt = client.hasUnresolvedPrompt();
|
|
7032
7512
|
const steps = [
|
|
7513
|
+
async () => {
|
|
7514
|
+
if (unresolvedPrompt) await withTimeout(client.cancelActivePrompt(INTERRUPT_CANCEL_WAIT_MS), INTERRUPT_CANCEL_WAIT_MS).catch(() => {});
|
|
7515
|
+
},
|
|
7516
|
+
async () => {
|
|
7517
|
+
const journalFailed = attempt.status === "rejected" && attempt.error instanceof AcpxOperationalError && attempt.error.detailCode === "SESSION_JOURNAL_WRITE_FAILED";
|
|
7518
|
+
if (unresolvedPrompt || closeClientOnExit || journalFailed) try {
|
|
7519
|
+
await client.close();
|
|
7520
|
+
} catch (error) {
|
|
7521
|
+
options.onClientCloseFailure?.();
|
|
7522
|
+
throw error;
|
|
7523
|
+
}
|
|
7524
|
+
},
|
|
7525
|
+
async () => {
|
|
7526
|
+
await controlDrain;
|
|
7527
|
+
},
|
|
7033
7528
|
() => {
|
|
7034
7529
|
const duration = stopTotalTimer();
|
|
7035
7530
|
if (options.verbose) process.stderr.write(`[acpx] ${formatPerfMetric("prompt.total", duration)}\n`);
|
|
@@ -7038,10 +7533,6 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7038
7533
|
if (notifiedClientAvailable) options.onClientClosed?.();
|
|
7039
7534
|
},
|
|
7040
7535
|
() => 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
7536
|
() => applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot()),
|
|
7046
7537
|
() => applyConversation(record, conversation)
|
|
7047
7538
|
];
|
|
@@ -7068,6 +7559,7 @@ async function runOnce(options) {
|
|
|
7068
7559
|
const shouldMarkAcpErrorsEmitted = rendersAcpErrors(options.errorEmissionPolicy);
|
|
7069
7560
|
let promptTurnActive = false;
|
|
7070
7561
|
let promptTurnHadSideEffects = false;
|
|
7562
|
+
let controlState = {};
|
|
7071
7563
|
const acpErrors = new AcpErrorTracker();
|
|
7072
7564
|
const client = new AcpClient({
|
|
7073
7565
|
agentCommand: options.agentCommand,
|
|
@@ -7088,6 +7580,7 @@ async function runOnce(options) {
|
|
|
7088
7580
|
acpErrors.observe(output, direction, message);
|
|
7089
7581
|
},
|
|
7090
7582
|
onSessionUpdate: (notification) => {
|
|
7583
|
+
if (notification.update.sessionUpdate === "config_option_update") controlState = applyConfigOptionsToState(controlState, notification.update.configOptions);
|
|
7091
7584
|
if (promptTurnActive) promptTurnHadSideEffects = true;
|
|
7092
7585
|
options.onSessionUpdate?.(notification);
|
|
7093
7586
|
},
|
|
@@ -7127,7 +7620,9 @@ async function runOnce(options) {
|
|
|
7127
7620
|
return await withTimeout(client.createSession(absolutePath(options.cwd)), options.timeoutMs);
|
|
7128
7621
|
});
|
|
7129
7622
|
const sessionId = createdSession.sessionId;
|
|
7130
|
-
|
|
7623
|
+
if (createdSession.models) applyAdvertisedModelState(controlState, createdSession.models);
|
|
7624
|
+
controlState = applyConfigOptionsToState(controlState, createdSession.configOptions ?? []);
|
|
7625
|
+
const modelApplication = await applyRequestedModelIfAdvertised({
|
|
7131
7626
|
client,
|
|
7132
7627
|
sessionId,
|
|
7133
7628
|
requestedModel: options.sessionOptions?.model,
|
|
@@ -7136,7 +7631,11 @@ async function runOnce(options) {
|
|
|
7136
7631
|
timeoutMs: options.timeoutMs,
|
|
7137
7632
|
onWarning: options.suppressSdkConsoleErrors ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
|
|
7138
7633
|
});
|
|
7139
|
-
|
|
7634
|
+
if (modelApplication.response) controlState = applyConfigOptionsToState(controlState, modelApplication.response.configOptions);
|
|
7635
|
+
for (const configOption of options.configOptions ?? []) {
|
|
7636
|
+
const response = await withTimeout(client.setSessionConfigOption(sessionId, configOption.configId, configOption.value, advertisedModelState(controlState)), options.timeoutMs);
|
|
7637
|
+
controlState = applyConfigOptionsToState(controlState, response.configOptions);
|
|
7638
|
+
}
|
|
7140
7639
|
output.setContext({ sessionId });
|
|
7141
7640
|
const response = await runExecPromptWithRetries(sessionId);
|
|
7142
7641
|
promptTurnActive = false;
|
|
@@ -7231,54 +7730,6 @@ function createQueueOwnerSharedClient(options, sessionRecord) {
|
|
|
7231
7730
|
sessionOptions: mergeSessionOptions(options.sessionOptions, sessionOptionsFromRecord(sessionRecord))
|
|
7232
7731
|
});
|
|
7233
7732
|
}
|
|
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
7733
|
function logDeferredCancelFailure(error, verbose) {
|
|
7283
7734
|
if (!verbose) return;
|
|
7284
7735
|
process.stderr.write(`[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}\n`);
|
|
@@ -7322,16 +7773,17 @@ function createQueueOwnerShutdownController(params) {
|
|
|
7322
7773
|
let shutdownPromise;
|
|
7323
7774
|
const drainActiveTurn = async () => {
|
|
7324
7775
|
const turn = activeTurn;
|
|
7325
|
-
|
|
7326
|
-
params.turnController.requestCancel().catch((error) => {
|
|
7776
|
+
const pending = Promise.allSettled([turn, params.controls.drainAdmitted()]);
|
|
7777
|
+
if (turn) params.turnController.requestCancel().catch((error) => {
|
|
7327
7778
|
logDeferredCancelFailure(error, params.verbose);
|
|
7328
7779
|
});
|
|
7329
|
-
if (!await settlesWithin(
|
|
7330
|
-
await
|
|
7780
|
+
if (!await settlesWithin(pending, QUEUE_OWNER_ACTIVE_TURN_CANCEL_GRACE_MS)) await params.sharedClient.close().catch(() => {});
|
|
7781
|
+
await pending;
|
|
7331
7782
|
};
|
|
7332
7783
|
const request = () => {
|
|
7333
7784
|
requested = true;
|
|
7334
7785
|
params.stopHeartbeat();
|
|
7786
|
+
params.controls.stopAdmission();
|
|
7335
7787
|
params.getOwner()?.beginShutdown();
|
|
7336
7788
|
activeTurnShutdown ??= drainActiveTurn();
|
|
7337
7789
|
};
|
|
@@ -7342,14 +7794,20 @@ function createQueueOwnerShutdownController(params) {
|
|
|
7342
7794
|
request,
|
|
7343
7795
|
setActiveTurn: (turn) => {
|
|
7344
7796
|
activeTurn = turn;
|
|
7797
|
+
if (requested) activeTurnShutdown = Promise.all([activeTurnShutdown, drainActiveTurn()]).then(() => {});
|
|
7345
7798
|
},
|
|
7346
7799
|
clearActiveTurn: (turn) => {
|
|
7347
7800
|
if (activeTurn === turn) activeTurn = void 0;
|
|
7348
7801
|
},
|
|
7802
|
+
drain: () => {
|
|
7803
|
+
request();
|
|
7804
|
+
return activeTurnShutdown;
|
|
7805
|
+
},
|
|
7349
7806
|
shutdown: () => {
|
|
7350
7807
|
request();
|
|
7351
7808
|
shutdownPromise ??= (async () => {
|
|
7352
7809
|
await activeTurnShutdown;
|
|
7810
|
+
await params.getBackendClose()?.catch(() => {});
|
|
7353
7811
|
await closeQueueOwnerRuntime({
|
|
7354
7812
|
owner: params.getOwner(),
|
|
7355
7813
|
turnController: params.turnController,
|
|
@@ -7368,20 +7826,50 @@ function applyRequestedShutdown(owner, shutdown) {
|
|
|
7368
7826
|
function startQueueOwnerHeartbeat(params) {
|
|
7369
7827
|
if (!params.enabled) return;
|
|
7370
7828
|
return setInterval(() => {
|
|
7371
|
-
|
|
7829
|
+
params.refresh(params.owner.queueDepth());
|
|
7372
7830
|
}, QUEUE_OWNER_HEARTBEAT_INTERVAL_MS);
|
|
7373
7831
|
}
|
|
7832
|
+
function createLeaseHeartbeat(lease, shutdown) {
|
|
7833
|
+
let guardFailure;
|
|
7834
|
+
return {
|
|
7835
|
+
refresh: async (queueDepth) => {
|
|
7836
|
+
if (shutdown.requested) return;
|
|
7837
|
+
await refreshQueueOwnerLease(lease, { queueDepth }).catch((error) => {
|
|
7838
|
+
if (error instanceof QueueLeaseGuardSettlementError) {
|
|
7839
|
+
guardFailure ??= error;
|
|
7840
|
+
shutdown.request();
|
|
7841
|
+
}
|
|
7842
|
+
});
|
|
7843
|
+
},
|
|
7844
|
+
throwIfFailed: () => {
|
|
7845
|
+
if (guardFailure) throw guardFailure;
|
|
7846
|
+
}
|
|
7847
|
+
};
|
|
7848
|
+
}
|
|
7374
7849
|
async function runSessionQueueOwner(options) {
|
|
7375
7850
|
const lease = await tryAcquireQueueOwnerLease(options.sessionId, {
|
|
7376
7851
|
path: options.mcpConfigPath,
|
|
7377
7852
|
fingerprint: options.mcpConfigFingerprint
|
|
7378
7853
|
});
|
|
7379
7854
|
if (!lease) return;
|
|
7855
|
+
let runtimeFailure;
|
|
7380
7856
|
try {
|
|
7381
7857
|
await runQueueOwnerRuntime(options, lease);
|
|
7382
|
-
}
|
|
7858
|
+
} catch (error) {
|
|
7859
|
+
runtimeFailure = { error };
|
|
7860
|
+
}
|
|
7861
|
+
try {
|
|
7383
7862
|
await releaseQueueOwnerLease(lease);
|
|
7863
|
+
} catch (error) {
|
|
7864
|
+
if (runtimeFailure) throw new AggregateError([runtimeFailure.error, error], "Queue owner shutdown failed", { cause: error });
|
|
7865
|
+
throw error;
|
|
7384
7866
|
}
|
|
7867
|
+
if (runtimeFailure) throw runtimeFailure.error;
|
|
7868
|
+
}
|
|
7869
|
+
async function keepOwnerForPendingControls(controls, shutdown) {
|
|
7870
|
+
if (shutdown.requested || !controls.hasPending) return false;
|
|
7871
|
+
await controls.drainAdmitted();
|
|
7872
|
+
return !shutdown.requested;
|
|
7385
7873
|
}
|
|
7386
7874
|
async function runQueueOwnerRuntime(options, lease) {
|
|
7387
7875
|
const sessionRecord = await resolveSessionRecord(options.sessionId);
|
|
@@ -7392,7 +7880,16 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7392
7880
|
const maxQueueDepth = Math.max(1, Math.round(options.maxQueueDepth ?? 16));
|
|
7393
7881
|
const taskPollTimeoutMs = ttlMs === 0 ? void 0 : ttlMs;
|
|
7394
7882
|
const initialTaskPollTimeoutMs = taskPollTimeoutMs == null ? void 0 : Math.max(taskPollTimeoutMs, 1e3);
|
|
7395
|
-
const turnController =
|
|
7883
|
+
const turnController = new QueueOwnerTurnController();
|
|
7884
|
+
let activeControls;
|
|
7885
|
+
let backendCloseRequest;
|
|
7886
|
+
const controls = new QueueOwnerControlAdmission({ runIdle: async (invoke, controlOptions) => await runIdleOwnerControl({
|
|
7887
|
+
...controlOptions,
|
|
7888
|
+
client: sharedClient,
|
|
7889
|
+
sessionId: options.sessionId,
|
|
7890
|
+
verbose: options.verbose,
|
|
7891
|
+
onRetirementFailure: () => shutdown.request()
|
|
7892
|
+
}, invoke) });
|
|
7396
7893
|
const applyPendingCancel = async () => {
|
|
7397
7894
|
return await turnController.applyPendingCancel();
|
|
7398
7895
|
};
|
|
@@ -7402,23 +7899,34 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7402
7899
|
});
|
|
7403
7900
|
};
|
|
7404
7901
|
const setActiveController = (controller) => {
|
|
7902
|
+
activeControls = controller;
|
|
7405
7903
|
turnController.setActiveController(controller);
|
|
7406
7904
|
scheduleApplyPendingCancel();
|
|
7407
7905
|
};
|
|
7408
7906
|
const clearActiveController = () => {
|
|
7907
|
+
activeControls = void 0;
|
|
7409
7908
|
turnController.clearActiveController();
|
|
7410
7909
|
};
|
|
7411
|
-
const closeActiveBackendSession =
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7910
|
+
const closeActiveBackendSession = (timeoutMs) => {
|
|
7911
|
+
backendCloseRequest ??= Promise.resolve().then(async () => {
|
|
7912
|
+
await shutdown.drain();
|
|
7913
|
+
const latestRecord = await resolveSessionRecord(options.sessionId);
|
|
7914
|
+
if (!sharedClient.supportsCloseSession()) return false;
|
|
7915
|
+
await withTimeout(sharedClient.closeSession(latestRecord.acpSessionId), timeoutMs);
|
|
7916
|
+
return true;
|
|
7917
|
+
});
|
|
7918
|
+
return backendCloseRequest;
|
|
7416
7919
|
};
|
|
7417
7920
|
const runPromptTurn = async (run) => {
|
|
7418
7921
|
const waitSignal = turnController.beginTurn();
|
|
7922
|
+
const { ticket, priorIdle } = controls.beginPrompt();
|
|
7923
|
+
if (shutdown.requested) turnController.requestCancel().catch(() => {});
|
|
7419
7924
|
try {
|
|
7420
|
-
|
|
7925
|
+
await priorIdle;
|
|
7926
|
+
return await run(waitSignal, ticket);
|
|
7421
7927
|
} finally {
|
|
7928
|
+
await controls.seal(ticket);
|
|
7929
|
+
controls.release(ticket);
|
|
7422
7930
|
turnController.endTurn();
|
|
7423
7931
|
}
|
|
7424
7932
|
};
|
|
@@ -7431,6 +7939,8 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7431
7939
|
}
|
|
7432
7940
|
},
|
|
7433
7941
|
turnController,
|
|
7942
|
+
controls,
|
|
7943
|
+
getBackendClose: () => backendCloseRequest,
|
|
7434
7944
|
sharedClient,
|
|
7435
7945
|
sessionId: options.sessionId,
|
|
7436
7946
|
verbose: options.verbose
|
|
@@ -7438,6 +7948,7 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7438
7948
|
const onSignal = () => {
|
|
7439
7949
|
shutdown.request();
|
|
7440
7950
|
};
|
|
7951
|
+
const heartbeat = createLeaseHeartbeat(lease, shutdown);
|
|
7441
7952
|
process.once("SIGINT", onSignal);
|
|
7442
7953
|
process.once("SIGTERM", onSignal);
|
|
7443
7954
|
process.once("SIGHUP", onSignal);
|
|
@@ -7450,17 +7961,17 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7450
7961
|
},
|
|
7451
7962
|
closeSession: async (timeoutMs) => await closeActiveBackendSession(timeoutMs),
|
|
7452
7963
|
setSessionMode: async (modeId, timeoutMs) => {
|
|
7453
|
-
await
|
|
7964
|
+
await controls.run((context, deadline) => context.setSessionMode(modeId, deadline), timeoutMs);
|
|
7454
7965
|
},
|
|
7455
|
-
setSessionModel: async (modelId, timeoutMs) => await
|
|
7966
|
+
setSessionModel: async (modelId, timeoutMs) => await controls.run((context, deadline) => context.setSessionModel(modelId, deadline), timeoutMs, { key: "model" }),
|
|
7456
7967
|
setSessionConfigOption: async (configId, value, timeoutMs) => {
|
|
7457
|
-
return await
|
|
7968
|
+
return await controls.run((context, deadline) => context.setSessionConfigOption(configId, value, deadline), timeoutMs, { key: configId });
|
|
7458
7969
|
}
|
|
7459
7970
|
}, {
|
|
7460
7971
|
maxQueueDepth,
|
|
7461
7972
|
onQueueDepthChanged: (queueDepth) => {
|
|
7462
7973
|
setPerfGauge("queue.owner.depth", queueDepth);
|
|
7463
|
-
|
|
7974
|
+
heartbeat.refresh(queueDepth);
|
|
7464
7975
|
}
|
|
7465
7976
|
});
|
|
7466
7977
|
applyRequestedShutdown(owner, shutdown);
|
|
@@ -7470,19 +7981,22 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7470
7981
|
maxQueueDepth,
|
|
7471
7982
|
verbose: options.verbose
|
|
7472
7983
|
});
|
|
7473
|
-
await
|
|
7984
|
+
await heartbeat.refresh(owner.queueDepth());
|
|
7474
7985
|
heartbeatTimer = startQueueOwnerHeartbeat({
|
|
7475
7986
|
enabled: !shutdown.requested,
|
|
7476
|
-
|
|
7987
|
+
refresh: heartbeat.refresh,
|
|
7477
7988
|
owner
|
|
7478
7989
|
});
|
|
7479
7990
|
let isFirstTask = true;
|
|
7480
7991
|
while (true) {
|
|
7481
7992
|
const pollTimeoutMs = isFirstTask ? initialTaskPollTimeoutMs : taskPollTimeoutMs;
|
|
7482
7993
|
const task = await owner.nextTask(pollTimeoutMs);
|
|
7483
|
-
if (!task)
|
|
7994
|
+
if (!task) {
|
|
7995
|
+
if (await keepOwnerForPendingControls(controls, shutdown)) continue;
|
|
7996
|
+
break;
|
|
7997
|
+
}
|
|
7484
7998
|
isFirstTask = false;
|
|
7485
|
-
const turnPromise = runPromptTurn(async (waitSignal) => {
|
|
7999
|
+
const turnPromise = runPromptTurn(async (waitSignal, ticket) => {
|
|
7486
8000
|
try {
|
|
7487
8001
|
await runQueuedTask(options.sessionId, task, {
|
|
7488
8002
|
sharedClient,
|
|
@@ -7496,7 +8010,10 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7496
8010
|
sessionOptions: options.sessionOptions,
|
|
7497
8011
|
onClientAvailable: setActiveController,
|
|
7498
8012
|
onClientClosed: clearActiveController,
|
|
8013
|
+
onClientCloseFailure: shutdown.request,
|
|
8014
|
+
onPromptFinalizing: () => controls.seal(ticket),
|
|
7499
8015
|
onPromptActive: async () => {
|
|
8016
|
+
if (activeControls) controls.publish(ticket, activeControls);
|
|
7500
8017
|
turnController.markPromptActive();
|
|
7501
8018
|
await applyPendingCancel();
|
|
7502
8019
|
},
|
|
@@ -7520,7 +8037,9 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7520
8037
|
process.off("SIGTERM", onSignal);
|
|
7521
8038
|
process.off("SIGHUP", onSignal);
|
|
7522
8039
|
await shutdown.shutdown();
|
|
8040
|
+
await lease.updates;
|
|
7523
8041
|
}
|
|
8042
|
+
heartbeat.throwIfFailed();
|
|
7524
8043
|
}
|
|
7525
8044
|
async function sendSession(options) {
|
|
7526
8045
|
options.signal?.throwIfAborted();
|
|
@@ -7560,6 +8079,6 @@ async function sendSession(options) {
|
|
|
7560
8079
|
}
|
|
7561
8080
|
const queueOwnerRuntimeTestInternals = { queueOwnerExitIsFatal };
|
|
7562
8081
|
//#endregion
|
|
7563
|
-
export {
|
|
8082
|
+
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
8083
|
|
|
7565
|
-
//# sourceMappingURL=queue-owner-runtime-
|
|
8084
|
+
//# sourceMappingURL=queue-owner-runtime-Cr0dWJuL.js.map
|