acpx 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,8 @@ import os from "node:os";
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { execFile, spawn } from "node:child_process";
8
8
  import { Readable, Writable } from "node:stream";
9
- import { ClientSideConnection, PROTOCOL_VERSION, RequestError } from "@agentclientprotocol/sdk";
9
+ import { PROTOCOL_VERSION, client, methods } from "@agentclientprotocol/sdk";
10
10
  import readline from "node:readline/promises";
11
- import { promisify } from "node:util";
12
11
  //#region src/errors.ts
13
12
  var AcpxOperationalError = class extends Error {
14
13
  outputCode;
@@ -983,7 +982,7 @@ function parseJsonRpcErrorMessage(message) {
983
982
  }
984
983
  //#endregion
985
984
  //#region src/session/event-log.ts
986
- const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 64 * 1024 * 1024;
985
+ const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 67108864;
987
986
  function sessionBaseDir$1() {
988
987
  return path.join(os.homedir(), ".acpx", "sessions");
989
988
  }
@@ -1721,7 +1720,8 @@ function assertPersistedKeyPolicy(value) {
1721
1720
  //#endregion
1722
1721
  //#region src/session/persistence/atomic-write.ts
1723
1722
  function createAtomicWriteTempPath(filePath, createUniqueId = randomUUID) {
1724
- return `${filePath}.${process.pid}.${Date.now()}.${createUniqueId()}.tmp`;
1723
+ const tempName = `.acpx-write.${process.pid}.${Date.now()}.${createUniqueId()}.tmp`;
1724
+ return path.join(path.dirname(filePath), tempName);
1725
1725
  }
1726
1726
  //#endregion
1727
1727
  //#region src/session/persistence/index.ts
@@ -2068,7 +2068,7 @@ function toWritePreview(content) {
2068
2068
  const visibleLines = lines.slice(0, WRITE_PREVIEW_MAX_LINES);
2069
2069
  let preview = visibleLines.join("\n");
2070
2070
  if (lines.length > visibleLines.length) preview += `\n... (${lines.length - visibleLines.length} more lines)`;
2071
- if (preview.length > WRITE_PREVIEW_MAX_CHARS) preview = `${preview.slice(0, WRITE_PREVIEW_MAX_CHARS - 3)}...`;
2071
+ if (preview.length > WRITE_PREVIEW_MAX_CHARS) preview = `${preview.slice(0, 1197)}...`;
2072
2072
  return preview;
2073
2073
  }
2074
2074
  async function defaultConfirmWrite(filePath, preview) {
@@ -2633,9 +2633,41 @@ function getAcpxVersion() {
2633
2633
  cachedVersion = resolveAcpxVersion();
2634
2634
  return cachedVersion;
2635
2635
  }
2636
- //#endregion
2637
- //#region src/acp/client-process.ts
2638
- const execFileAsync = promisify(execFile);
2636
+ async function runTimedExecFile(command, args, options = {}) {
2637
+ const timeoutMs = Math.max(1, Math.round(options.timeoutMs ?? 8e3));
2638
+ return await new Promise((resolve, reject) => {
2639
+ let settled = false;
2640
+ let timer;
2641
+ const child = execFile(command, [...args], {
2642
+ encoding: "utf8",
2643
+ maxBuffer: options.maxBufferBytes ?? 33554432,
2644
+ killSignal: "SIGKILL",
2645
+ windowsHide: options.windowsHide
2646
+ }, (error, stdout) => {
2647
+ if (settled) return;
2648
+ settled = true;
2649
+ if (timer) clearTimeout(timer);
2650
+ if (error) {
2651
+ reject(error);
2652
+ return;
2653
+ }
2654
+ resolve(stdout);
2655
+ });
2656
+ timer = setTimeout(() => {
2657
+ if (settled) return;
2658
+ settled = true;
2659
+ const killed = child.kill("SIGKILL");
2660
+ child.stdout?.destroy();
2661
+ child.stderr?.destroy();
2662
+ child.unref();
2663
+ reject(Object.assign(/* @__PURE__ */ new Error(`${command} timed out after ${timeoutMs}ms`), {
2664
+ code: "ETIMEDOUT",
2665
+ killed,
2666
+ signal: "SIGKILL"
2667
+ }));
2668
+ }, timeoutMs);
2669
+ });
2670
+ }
2639
2671
  function normalizeAgentCommandInput(value) {
2640
2672
  if (typeof value === "string") return { agentCommand: value };
2641
2673
  const parts = toCommandParts([...value]);
@@ -2832,8 +2864,7 @@ function isWindowsExecutableCommand(command) {
2832
2864
  return WINDOWS_EXECUTABLE_EXTENSION_RE.test(normalized);
2833
2865
  }
2834
2866
  async function runWslpath(cwd) {
2835
- const { stdout } = await execFileAsync("wslpath", ["-w", cwd], { encoding: "utf8" });
2836
- return stdout;
2867
+ return await runTimedExecFile("wslpath", ["-w", cwd]);
2837
2868
  }
2838
2869
  function basenameToken(value) {
2839
2870
  return path.basename(value).toLowerCase().replace(/\.(cmd|exe|bat)$/u, "");
@@ -3307,7 +3338,7 @@ function maybeWrapSessionControlError(method, error, context) {
3307
3338
  }
3308
3339
  //#endregion
3309
3340
  //#region src/acp/terminal-manager.ts
3310
- const DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES = 64 * 1024;
3341
+ const DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES = 65536;
3311
3342
  const DEFAULT_KILL_GRACE_MS = 1500;
3312
3343
  function nowIso() {
3313
3344
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -3376,6 +3407,7 @@ var TerminalManager = class {
3376
3407
  usesDefaultConfirmExecute;
3377
3408
  confirmExecute;
3378
3409
  killGraceMs;
3410
+ processHelperTimeoutMs;
3379
3411
  terminals = /* @__PURE__ */ new Map();
3380
3412
  constructor(options) {
3381
3413
  this.cwd = options.cwd;
@@ -3385,6 +3417,7 @@ var TerminalManager = class {
3385
3417
  this.usesDefaultConfirmExecute = options.confirmExecute == null;
3386
3418
  this.confirmExecute = options.confirmExecute ?? defaultConfirmExecute;
3387
3419
  this.killGraceMs = Math.max(0, Math.round(options.killGraceMs ?? DEFAULT_KILL_GRACE_MS));
3420
+ this.processHelperTimeoutMs = Math.max(1, Math.round(options.processHelperTimeoutMs ?? 8e3));
3388
3421
  }
3389
3422
  updatePermissionPolicy(permissionMode, nonInteractivePermissions) {
3390
3423
  this.permissionMode = permissionMode;
@@ -3411,6 +3444,7 @@ var TerminalManager = class {
3411
3444
  process: proc,
3412
3445
  killProcessGroup: spawnCommand.killProcessGroup,
3413
3446
  descendantPids: /* @__PURE__ */ new Set(),
3447
+ processHelperTimeoutMs: this.processHelperTimeoutMs,
3414
3448
  output: Buffer.alloc(0),
3415
3449
  truncated: false,
3416
3450
  outputByteLimit,
@@ -3636,7 +3670,7 @@ var TerminalManager = class {
3636
3670
  }
3637
3671
  async captureDescendantPids(terminal, pid) {
3638
3672
  if (!this.isRunning(terminal)) await terminal.processGroupSnapshotPromise?.catch(() => {});
3639
- for (const descendantPid of await listDescendantPids(pid)) terminal.descendantPids.add(descendantPid);
3673
+ for (const descendantPid of await listDescendantPids(pid, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(descendantPid);
3640
3674
  }
3641
3675
  async waitForCleanupAfterSignal(terminal) {
3642
3676
  return await Promise.race([this.waitForTerminalAndTrackedDescendants(terminal).then(() => true), waitMs(this.killGraceMs).then(() => false)]);
@@ -3689,10 +3723,10 @@ function commandPathExists(command, cwd) {
3689
3723
  const resolvedPath = path.isAbsolute(command) ? command : path.resolve(cwd, command);
3690
3724
  return fs.existsSync(resolvedPath);
3691
3725
  }
3692
- async function listDescendantPids(rootPid) {
3726
+ async function listDescendantPids(rootPid, timeoutMs) {
3693
3727
  let output;
3694
3728
  try {
3695
- output = await runProcessListCommand();
3729
+ output = await runProcessListCommand(timeoutMs);
3696
3730
  } catch {
3697
3731
  return [];
3698
3732
  }
@@ -3725,47 +3759,23 @@ function parseProcessListLine(line) {
3725
3759
  parentPid
3726
3760
  };
3727
3761
  }
3728
- async function runProcessListCommand() {
3729
- if (process.platform === "win32") return await runWindowsProcessListCommand();
3730
- return await new Promise((resolve, reject) => {
3731
- const child = spawn("ps", ["-eo", "pid=,ppid="], { stdio: [
3732
- "ignore",
3733
- "pipe",
3734
- "pipe"
3735
- ] });
3736
- let stdout = "";
3737
- let stderr = "";
3738
- child.stdout.setEncoding("utf8");
3739
- child.stderr.setEncoding("utf8");
3740
- child.stdout.on("data", (chunk) => {
3741
- stdout += chunk;
3742
- });
3743
- child.stderr.on("data", (chunk) => {
3744
- stderr += chunk;
3745
- });
3746
- child.once("error", reject);
3747
- child.once("close", (code, signal) => {
3748
- if (code === 0) {
3749
- resolve(stdout);
3750
- return;
3751
- }
3752
- reject(/* @__PURE__ */ new Error(`ps exited with code ${code ?? "null"} signal ${signal ?? "null"}: ${stderr}`));
3753
- });
3754
- });
3762
+ async function runProcessListCommand(timeoutMs) {
3763
+ if (process.platform === "win32") return await runWindowsProcessListCommand(timeoutMs);
3764
+ return await runTimedExecFile("ps", ["-eo", "pid=,ppid="], { timeoutMs });
3755
3765
  }
3756
3766
  async function rememberProcessGroupPids(terminal) {
3757
3767
  const processGroupId = terminal.process.pid;
3758
3768
  if (!terminal.killProcessGroup || !processGroupId) return;
3759
3769
  if (process.platform === "win32") {
3760
- for (const pid of await listDescendantPids(processGroupId)) terminal.descendantPids.add(pid);
3770
+ for (const pid of await listDescendantPids(processGroupId, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(pid);
3761
3771
  return;
3762
3772
  }
3763
- for (const pid of await listProcessGroupPids(processGroupId)) if (pid !== processGroupId) terminal.descendantPids.add(pid);
3773
+ for (const pid of await listProcessGroupPids(processGroupId, terminal.processHelperTimeoutMs)) if (pid !== processGroupId) terminal.descendantPids.add(pid);
3764
3774
  }
3765
- async function listProcessGroupPids(processGroupId) {
3775
+ async function listProcessGroupPids(processGroupId, timeoutMs) {
3766
3776
  let output;
3767
3777
  try {
3768
- output = await runProcessGroupListCommand();
3778
+ output = await runProcessGroupListCommand(timeoutMs);
3769
3779
  } catch {
3770
3780
  return [];
3771
3781
  }
@@ -3779,66 +3789,18 @@ async function listProcessGroupPids(processGroupId) {
3779
3789
  }
3780
3790
  return pids;
3781
3791
  }
3782
- async function runProcessGroupListCommand() {
3783
- return await new Promise((resolve, reject) => {
3784
- const child = spawn("ps", ["-eo", "pid=,pgid="], { stdio: [
3785
- "ignore",
3786
- "pipe",
3787
- "pipe"
3788
- ] });
3789
- let stdout = "";
3790
- let stderr = "";
3791
- child.stdout.setEncoding("utf8");
3792
- child.stderr.setEncoding("utf8");
3793
- child.stdout.on("data", (chunk) => {
3794
- stdout += chunk;
3795
- });
3796
- child.stderr.on("data", (chunk) => {
3797
- stderr += chunk;
3798
- });
3799
- child.once("error", reject);
3800
- child.once("close", (code, signal) => {
3801
- if (code === 0) {
3802
- resolve(stdout);
3803
- return;
3804
- }
3805
- reject(/* @__PURE__ */ new Error(`ps exited with code ${code ?? "null"} signal ${signal ?? "null"}: ${stderr}`));
3806
- });
3807
- });
3808
- }
3809
- async function runWindowsProcessListCommand() {
3810
- return await new Promise((resolve, reject) => {
3811
- const child = spawn("powershell.exe", [
3812
- "-NoProfile",
3813
- "-NonInteractive",
3814
- "-Command",
3815
- ["Get-CimInstance Win32_Process |", "ForEach-Object { \"$($_.ProcessId) $($_.ParentProcessId)\" }"].join(" ")
3816
- ], {
3817
- stdio: [
3818
- "ignore",
3819
- "pipe",
3820
- "pipe"
3821
- ],
3822
- windowsHide: true
3823
- });
3824
- let stdout = "";
3825
- let stderr = "";
3826
- child.stdout.setEncoding("utf8");
3827
- child.stderr.setEncoding("utf8");
3828
- child.stdout.on("data", (chunk) => {
3829
- stdout += chunk;
3830
- });
3831
- child.stderr.on("data", (chunk) => {
3832
- stderr += chunk;
3833
- });
3834
- child.once("error", reject);
3835
- child.once("close", (code, signal) => {
3836
- if (code === 0) {
3837
- resolve(stdout);
3838
- return;
3839
- }
3840
- reject(/* @__PURE__ */ new Error(`powershell process list exited with code ${code ?? "null"} signal ${signal ?? "null"}: ${stderr}`));
3841
- });
3792
+ async function runProcessGroupListCommand(timeoutMs) {
3793
+ return await runTimedExecFile("ps", ["-eo", "pid=,pgid="], { timeoutMs });
3794
+ }
3795
+ async function runWindowsProcessListCommand(timeoutMs) {
3796
+ return await runTimedExecFile("powershell.exe", [
3797
+ "-NoProfile",
3798
+ "-NonInteractive",
3799
+ "-Command",
3800
+ ["Get-CimInstance Win32_Process |", "ForEach-Object { \"$($_.ProcessId) $($_.ParentProcessId)\" }"].join(" ")
3801
+ ], {
3802
+ timeoutMs,
3803
+ windowsHide: true
3842
3804
  });
3843
3805
  }
3844
3806
  async function killWindowsProcessTree(pid, signal) {
@@ -3898,6 +3860,14 @@ const STARTUP_STDERR_MAX_CHARS = 8192;
3898
3860
  const DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META = Object.freeze({ "cognition.ai/requestDiagnostics": true });
3899
3861
  const DEVIN_COMPATIBILITY_CLIENT_NAME = "windsurf";
3900
3862
  const DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION = "1.110.1";
3863
+ const ELICITATION_CANCEL_MESSAGES = {
3864
+ inactive: "elicitation owner is no longer active",
3865
+ unavailable: "elicitation handler is unavailable",
3866
+ unsupported: "elicitation mode is not supported",
3867
+ mismatchedSession: "elicitation session is not active",
3868
+ cancelled: "elicitation request was cancelled",
3869
+ requestScoped: "request-scoped elicitation has no owner"
3870
+ };
3901
3871
  function resolveClientInfo(devinAcp) {
3902
3872
  if (!devinAcp) return {
3903
3873
  name: "acpx",
@@ -3908,13 +3878,88 @@ function resolveClientInfo(devinAcp) {
3908
3878
  version: process.env.ACPX_DEVIN_WINDSURF_VERSION ?? DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION
3909
3879
  };
3910
3880
  }
3881
+ function normalizeElicitationModes(modes) {
3882
+ return [...new Set((modes ?? []).filter((mode) => mode === "form" || mode === "url"))];
3883
+ }
3884
+ function cancelledElicitationResponse(message) {
3885
+ return {
3886
+ action: "cancel",
3887
+ _meta: { message }
3888
+ };
3889
+ }
3890
+ function isKnownElicitationResponse(response) {
3891
+ if (!response || typeof response !== "object") return false;
3892
+ const action = response.action;
3893
+ return action === "accept" || action === "decline" || action === "cancel";
3894
+ }
3895
+ function elicitationSessionId(request) {
3896
+ const value = request.sessionId;
3897
+ return typeof value === "string" ? value : void 0;
3898
+ }
3899
+ function elicitationRequestScopeId(request) {
3900
+ const value = request.requestId;
3901
+ return value === null || typeof value === "string" || typeof value === "number" ? value : void 0;
3902
+ }
3903
+ function waitForAbort(signal) {
3904
+ if (signal.aborted) return Promise.resolve({ kind: "aborted" });
3905
+ return new Promise((resolve) => {
3906
+ signal.addEventListener("abort", () => resolve({ kind: "aborted" }), { once: true });
3907
+ });
3908
+ }
3909
+ function isExtensionNotification(message) {
3910
+ if (!("method" in message) || "id" in message || typeof message.method !== "string") return false;
3911
+ return message.method !== methods.client.session.update && message.method !== methods.client.elicitation.complete && message.method !== methods.protocol.cancelRequest;
3912
+ }
3913
+ function elicitationRequestId(message) {
3914
+ if (!("method" in message) || message.method !== methods.client.elicitation.create || !("id" in message)) return;
3915
+ return message.id;
3916
+ }
3917
+ function responseId(message) {
3918
+ if (!("id" in message) || "method" in message) return;
3919
+ return message.id;
3920
+ }
3921
+ function promptRequestOwner(message) {
3922
+ if (!("method" in message) || message.method !== methods.agent.session.prompt || !("id" in message)) return;
3923
+ const sessionId = message.params?.sessionId;
3924
+ return typeof sessionId === "string" ? {
3925
+ requestId: message.id,
3926
+ sessionId
3927
+ } : void 0;
3928
+ }
3929
+ function createAgentConnectionFacade(connection) {
3930
+ const agent = connection.agent;
3931
+ return {
3932
+ signal: connection.signal,
3933
+ close: (error) => connection.close(error),
3934
+ initialize: async (params) => await agent.request(methods.agent.initialize, params),
3935
+ authenticate: async (params) => {
3936
+ await agent.request(methods.agent.authenticate, params);
3937
+ },
3938
+ newSession: async (params) => await agent.request(methods.agent.session.new, params),
3939
+ loadSession: async (params) => await agent.request(methods.agent.session.load, params),
3940
+ resumeSession: async (params) => await agent.request(methods.agent.session.resume, params),
3941
+ prompt: async (params) => await agent.request(methods.agent.session.prompt, params),
3942
+ setSessionMode: async (params) => await agent.request(methods.agent.session.setMode, params),
3943
+ setSessionConfigOption: async (params) => await agent.request(methods.agent.session.setConfigOption, params),
3944
+ extMethod: async (method, params) => await agent.request(method, params),
3945
+ cancel: async (params) => await agent.notify(methods.agent.session.cancel, params),
3946
+ closeSession: async (params) => {
3947
+ await agent.request(methods.agent.session.close, params);
3948
+ },
3949
+ listSessions: async (params) => await agent.request(methods.agent.session.list, params)
3950
+ };
3951
+ }
3911
3952
  function resolveClientCapabilities(params) {
3912
3953
  const baseCapabilities = {
3913
3954
  fs: {
3914
3955
  readTextFile: params.fs,
3915
3956
  writeTextFile: params.fs
3916
3957
  },
3917
- terminal: params.terminal
3958
+ terminal: params.terminal,
3959
+ ...params.elicitationModes.length > 0 ? { elicitation: {
3960
+ ...params.elicitationModes.includes("form") ? { form: {} } : {},
3961
+ ...params.elicitationModes.includes("url") ? { url: {} } : {}
3962
+ } } : {}
3918
3963
  };
3919
3964
  if (!params.devinAcp) return baseCapabilities;
3920
3965
  return {
@@ -3922,9 +3967,6 @@ function resolveClientCapabilities(params) {
3922
3967
  _meta: DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META
3923
3968
  };
3924
3969
  }
3925
- function isDevinRequestDiagnosticsMethod(method) {
3926
- return method === "_cognition.ai/request_diagnostics";
3927
- }
3928
3970
  function hasResponseField(response, field) {
3929
3971
  return !!response && typeof response === "object" && field in response;
3930
3972
  }
@@ -3945,6 +3987,15 @@ function toReconnectedSessionResult(response) {
3945
3987
  legacyModelMetadataPresent: hasResponseField(response, "models")
3946
3988
  };
3947
3989
  }
3990
+ function snapshotPermissionPolicy(policy) {
3991
+ if (!policy) return;
3992
+ return {
3993
+ ...policy.autoApprove ? { autoApprove: [...policy.autoApprove] } : {},
3994
+ ...policy.autoDeny ? { autoDeny: [...policy.autoDeny] } : {},
3995
+ ...policy.escalate ? { escalate: [...policy.escalate] } : {},
3996
+ ...policy.defaultAction ? { defaultAction: policy.defaultAction } : {}
3997
+ };
3998
+ }
3948
3999
  function childProcessIsRunning(agent) {
3949
4000
  if (!agent) return false;
3950
4001
  return agent.exitCode == null && agent.signalCode == null && !agent.killed;
@@ -4037,6 +4088,7 @@ var AcpClient = class {
4037
4088
  suppressSessionUpdates = false;
4038
4089
  suppressReplaySessionUpdateMessages = false;
4039
4090
  activePrompt;
4091
+ pendingPromptOwners = [];
4040
4092
  cancellingSessionIds = /* @__PURE__ */ new Set();
4041
4093
  permissionAbortControllers = /* @__PURE__ */ new Map();
4042
4094
  closing = false;
@@ -4051,7 +4103,9 @@ var AcpClient = class {
4051
4103
  this.options = {
4052
4104
  ...options,
4053
4105
  cwd: asAbsoluteCwd(options.cwd),
4054
- authPolicy: options.authPolicy ?? "skip"
4106
+ authPolicy: options.authPolicy ?? "skip",
4107
+ permissionPolicy: snapshotPermissionPolicy(options.permissionPolicy),
4108
+ elicitationModes: normalizeElicitationModes(options.elicitationModes)
4055
4109
  };
4056
4110
  this.eventHandlers = {
4057
4111
  onAcpMessage: this.options.onAcpMessage,
@@ -4118,7 +4172,7 @@ var AcpClient = class {
4118
4172
  const shouldRefreshPermissionPolicy = options.permissionMode !== void 0 || options.nonInteractivePermissions !== void 0;
4119
4173
  if (options.permissionMode) this.options.permissionMode = options.permissionMode;
4120
4174
  if (options.nonInteractivePermissions !== void 0) this.options.nonInteractivePermissions = options.nonInteractivePermissions;
4121
- if (Object.prototype.hasOwnProperty.call(options, "permissionPolicy")) this.options.permissionPolicy = options.permissionPolicy;
4175
+ if (Object.prototype.hasOwnProperty.call(options, "permissionPolicy")) this.options.permissionPolicy = snapshotPermissionPolicy(options.permissionPolicy);
4122
4176
  this.updateClientCapabilityPreferences(options);
4123
4177
  this.refreshRuntimePermissionPolicy(shouldRefreshPermissionPolicy);
4124
4178
  if (options.suppressSdkConsoleErrors !== void 0) this.options.suppressSdkConsoleErrors = options.suppressSdkConsoleErrors;
@@ -4141,6 +4195,15 @@ var AcpClient = class {
4141
4195
  if (sessionId == null) return true;
4142
4196
  return this.activePrompt.sessionId === sessionId;
4143
4197
  }
4198
+ hasUnresolvedPrompt() {
4199
+ return this.activePrompt !== void 0;
4200
+ }
4201
+ endPromptElicitation(sessionId) {
4202
+ const active = this.activePrompt;
4203
+ if (!active || active.sessionId !== sessionId) return;
4204
+ active.elicitationHandler = void 0;
4205
+ active.elicitationController.abort();
4206
+ }
4144
4207
  async start() {
4145
4208
  if (this.connection && this.agent && isChildProcessRunning(this.agent)) return;
4146
4209
  if (this.connection || this.agent) await this.close();
@@ -4228,42 +4291,31 @@ var AcpClient = class {
4228
4291
  return requireAgentStdio(spawnedChild);
4229
4292
  }
4230
4293
  createConnection(stream, launch) {
4231
- return new ClientSideConnection(() => ({
4232
- sessionUpdate: async (params) => {
4233
- await this.handleSessionUpdate(params);
4234
- },
4235
- requestPermission: async (params) => {
4236
- return this.handlePermissionRequest(params);
4237
- },
4238
- extMethod: async (method) => {
4239
- if (launch.devinAcp && isDevinRequestDiagnosticsMethod(method)) return {};
4240
- const error = RequestError.methodNotFound(method);
4241
- if (!this.options.suppressSdkConsoleErrors) console.error(error.message);
4242
- throw error;
4243
- },
4244
- readTextFile: async (params) => {
4245
- return this.handleReadTextFile(params);
4246
- },
4247
- writeTextFile: async (params) => {
4248
- return this.handleWriteTextFile(params);
4249
- },
4250
- createTerminal: async (params) => {
4251
- return this.handleCreateTerminal(params);
4252
- },
4253
- terminalOutput: async (params) => {
4254
- return this.handleTerminalOutput(params);
4255
- },
4256
- waitForTerminalExit: async (params) => {
4257
- return this.handleWaitForTerminalExit(params);
4258
- },
4259
- killTerminal: async (params) => {
4260
- return this.handleKillTerminal(params);
4261
- },
4262
- releaseTerminal: async (params) => {
4263
- return this.handleReleaseTerminal(params);
4264
- },
4265
- extNotification: async () => {}
4266
- }), stream);
4294
+ const app = client({ name: "acpx" }).onNotification(methods.client.session.update, async ({ params }) => {
4295
+ await this.handleSessionUpdate(params);
4296
+ }).onNotification(methods.client.elicitation.complete, async () => {}).onRequest(methods.client.session.requestPermission, async ({ params }) => {
4297
+ return await this.handlePermissionRequest(params);
4298
+ }).onRequest(methods.client.elicitation.create, async ({ params, requestId, signal }) => {
4299
+ return await this.handleElicitationRequest(params, requestId, signal);
4300
+ }).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
4301
+ return await this.handleReadTextFile(params);
4302
+ }).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
4303
+ return await this.handleWriteTextFile(params);
4304
+ }).onRequest(methods.client.terminal.create, async ({ params }) => {
4305
+ return await this.handleCreateTerminal(params);
4306
+ }).onRequest(methods.client.terminal.output, async ({ params }) => {
4307
+ return await this.handleTerminalOutput(params);
4308
+ }).onRequest(methods.client.terminal.waitForExit, async ({ params }) => {
4309
+ return await this.handleWaitForTerminalExit(params);
4310
+ }).onRequest(methods.client.terminal.kill, async ({ params }) => {
4311
+ return await this.handleKillTerminal(params);
4312
+ }).onRequest(methods.client.terminal.release, async ({ params }) => {
4313
+ return await this.handleReleaseTerminal(params);
4314
+ });
4315
+ if (launch.devinAcp) app.onRequest("_cognition.ai/request_diagnostics", (params) => {
4316
+ return params && typeof params === "object" && !Array.isArray(params) ? params : {};
4317
+ }, async () => ({}));
4318
+ return createAgentConnectionFacade(app.connect(stream));
4267
4319
  }
4268
4320
  async initializeAgentConnection(params) {
4269
4321
  try {
@@ -4274,6 +4326,7 @@ var AcpClient = class {
4274
4326
  this.initResult = initResult;
4275
4327
  this.log(`initialized protocol version ${initResult.protocolVersion}`);
4276
4328
  } catch (error) {
4329
+ params.connection.close?.(error);
4277
4330
  await this.handleInitializeFailure(params, error);
4278
4331
  }
4279
4332
  }
@@ -4283,7 +4336,8 @@ var AcpClient = class {
4283
4336
  clientCapabilities: resolveClientCapabilities({
4284
4337
  devinAcp: launch.devinAcp,
4285
4338
  fs: this.options.fs !== false,
4286
- terminal: this.options.terminal !== false
4339
+ terminal: this.options.terminal !== false,
4340
+ elicitationModes: this.options.elicitationModes ?? []
4287
4341
  }),
4288
4342
  clientInfo: resolveClientInfo(launch.devinAcp)
4289
4343
  });
@@ -4306,9 +4360,24 @@ var AcpClient = class {
4306
4360
  createTappedStream(base) {
4307
4361
  const onAcpMessage = () => this.eventHandlers.onAcpMessage;
4308
4362
  const onAcpOutputMessage = () => this.eventHandlers.onAcpOutputMessage;
4363
+ const elicitationRequestIds = /* @__PURE__ */ new Set();
4364
+ const bindPromptOwner = (owner) => {
4365
+ this.bindPromptOwner(owner);
4366
+ };
4309
4367
  const shouldSuppressInboundReplaySessionUpdate = (message) => {
4310
4368
  return this.suppressReplaySessionUpdateMessages && isSessionUpdateNotification(message);
4311
4369
  };
4370
+ const observeInbound = (message) => {
4371
+ const requestId = elicitationRequestId(message);
4372
+ if (requestId !== void 0) {
4373
+ elicitationRequestIds.add(requestId);
4374
+ return;
4375
+ }
4376
+ if (!shouldSuppressInboundReplaySessionUpdate(message)) {
4377
+ onAcpOutputMessage()?.("inbound", message);
4378
+ onAcpMessage()?.("inbound", message);
4379
+ }
4380
+ };
4312
4381
  return {
4313
4382
  readable: new ReadableStream({ async start(controller) {
4314
4383
  const reader = base.readable.getReader();
@@ -4317,10 +4386,8 @@ var AcpClient = class {
4317
4386
  const { value, done } = await reader.read();
4318
4387
  if (done) break;
4319
4388
  if (!value) continue;
4320
- if (!shouldSuppressInboundReplaySessionUpdate(value)) {
4321
- onAcpOutputMessage()?.("inbound", value);
4322
- onAcpMessage()?.("inbound", value);
4323
- }
4389
+ observeInbound(value);
4390
+ if (isExtensionNotification(value)) continue;
4324
4391
  controller.enqueue(value);
4325
4392
  }
4326
4393
  } finally {
@@ -4329,8 +4396,13 @@ var AcpClient = class {
4329
4396
  }
4330
4397
  } }),
4331
4398
  writable: new WritableStream({ async write(message) {
4332
- onAcpOutputMessage()?.("outbound", message);
4333
- onAcpMessage()?.("outbound", message);
4399
+ const promptOwner = promptRequestOwner(message);
4400
+ if (promptOwner) bindPromptOwner(promptOwner);
4401
+ const id = responseId(message);
4402
+ if (!(id !== void 0 && elicitationRequestIds.delete(id))) {
4403
+ onAcpOutputMessage()?.("outbound", message);
4404
+ onAcpMessage()?.("outbound", message);
4405
+ }
4334
4406
  const writer = base.writable.getWriter();
4335
4407
  try {
4336
4408
  await writer.write(message);
@@ -4426,24 +4498,23 @@ var AcpClient = class {
4426
4498
  this.suppressSessionUpdates = previous.suppressSessionUpdates;
4427
4499
  this.suppressReplaySessionUpdateMessages = previous.suppressReplaySessionUpdateMessages;
4428
4500
  }
4429
- async prompt(sessionId, prompt) {
4501
+ async prompt(sessionId, prompt, onRequestStarted, onElicitation) {
4430
4502
  const connection = this.getConnection();
4431
4503
  const normalizedPrompt = this.normalizePromptForAgent(prompt);
4432
4504
  const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
4505
+ const activePrompt = this.beginActivePrompt(sessionId, onElicitation);
4433
4506
  let promptPromise;
4434
4507
  try {
4435
4508
  promptPromise = this.runConnectionRequest(() => connection.prompt({
4436
4509
  sessionId,
4437
4510
  prompt: normalizedPrompt
4438
- }));
4511
+ }), onRequestStarted, () => !connection.signal?.aborted);
4439
4512
  } catch (error) {
4513
+ this.clearActivePrompt(activePrompt);
4440
4514
  restoreConsoleError?.();
4441
4515
  throw error;
4442
4516
  }
4443
- this.activePrompt = {
4444
- sessionId,
4445
- promise: promptPromise
4446
- };
4517
+ activePrompt.promise = promptPromise;
4447
4518
  try {
4448
4519
  return this.returnPromptResponseOrPermissionFailure(sessionId, await promptPromise);
4449
4520
  } catch (error) {
@@ -4451,12 +4522,37 @@ var AcpClient = class {
4451
4522
  throw error;
4452
4523
  } finally {
4453
4524
  restoreConsoleError?.();
4454
- if (this.activePrompt?.promise === promptPromise) this.activePrompt = void 0;
4525
+ this.clearActivePrompt(activePrompt);
4455
4526
  this.cancellingSessionIds.delete(sessionId);
4456
4527
  this.abortAndDropPermissionSignal(sessionId);
4457
4528
  this.promptPermissionFailures.delete(sessionId);
4458
4529
  }
4459
4530
  }
4531
+ beginActivePrompt(sessionId, elicitationHandler) {
4532
+ const previous = this.activePrompt;
4533
+ this.cancellingSessionIds.delete(sessionId);
4534
+ const active = {
4535
+ sessionId,
4536
+ elicitationHandler,
4537
+ elicitationController: new AbortController()
4538
+ };
4539
+ this.activePrompt = active;
4540
+ this.pendingPromptOwners.push(active);
4541
+ previous?.elicitationController?.abort();
4542
+ return active;
4543
+ }
4544
+ bindPromptOwner(owner) {
4545
+ const active = this.pendingPromptOwners.find((candidate) => {
4546
+ return candidate.requestId === void 0 && candidate.sessionId === owner.sessionId;
4547
+ });
4548
+ if (active) active.requestId = owner.requestId;
4549
+ }
4550
+ clearActivePrompt(active) {
4551
+ if (this.activePrompt === active) this.activePrompt = void 0;
4552
+ const pendingIndex = this.pendingPromptOwners.indexOf(active);
4553
+ if (pendingIndex >= 0) this.pendingPromptOwners.splice(pendingIndex, 1);
4554
+ active.elicitationController.abort();
4555
+ }
4460
4556
  normalizePromptForAgent(prompt) {
4461
4557
  const normalizedPrompt = typeof prompt === "string" ? textPrompt(prompt) : prompt;
4462
4558
  const unsupportedPromptContent = getUnsupportedPromptContentMessage(normalizedPrompt, this.initResult?.agentCapabilities);
@@ -4569,12 +4665,18 @@ var AcpClient = class {
4569
4665
  }
4570
4666
  async cancel(sessionId) {
4571
4667
  const connection = this.getConnection();
4572
- this.cancellingSessionIds.add(sessionId);
4668
+ const active = this.activePrompt;
4669
+ if (active?.sessionId === sessionId) {
4670
+ this.cancellingSessionIds.add(sessionId);
4671
+ active.elicitationController?.abort();
4672
+ } else this.cancellingSessionIds.delete(sessionId);
4573
4673
  this.abortAndDropPermissionSignal(sessionId);
4574
4674
  await this.runConnectionRequest(() => connection.cancel({ sessionId }));
4575
4675
  }
4576
4676
  async closeSession(sessionId) {
4577
4677
  const connection = this.getConnection();
4678
+ this.cancellingSessionIds.add(sessionId);
4679
+ if (this.activePrompt?.sessionId === sessionId) this.activePrompt.elicitationController?.abort();
4578
4680
  await this.runConnectionRequest(() => connection.closeSession({ sessionId }));
4579
4681
  if (this.loadedSessionId === sessionId) this.loadedSessionId = void 0;
4580
4682
  this.modelConfigIds.delete(sessionId);
@@ -4600,21 +4702,25 @@ var AcpClient = class {
4600
4702
  this.log(`failed to send session/cancel: ${message}`);
4601
4703
  }
4602
4704
  if (waitMs <= 0) return;
4705
+ const activePromise = active.promise;
4706
+ if (!activePromise) return;
4603
4707
  let timer;
4604
4708
  const timeoutPromise = new Promise((resolve) => {
4605
4709
  timer = setTimeout(resolve, waitMs);
4606
4710
  });
4607
4711
  try {
4608
- return await Promise.race([active.promise.then((response) => response, () => void 0), timeoutPromise]);
4712
+ return await Promise.race([activePromise.then((response) => response, () => void 0), timeoutPromise]);
4609
4713
  } finally {
4610
4714
  if (timer) clearTimeout(timer);
4611
4715
  }
4612
4716
  }
4613
4717
  async close() {
4614
4718
  this.closing = true;
4719
+ this.abortActiveElicitation();
4615
4720
  await this.terminalManager.shutdown();
4616
4721
  const agent = this.agent;
4617
4722
  if (agent) await this.terminateAgentProcess(agent);
4723
+ this.closeConnection();
4618
4724
  if (this.pendingConnectionRequests.size > 0) this.rejectPendingConnectionRequests(this.lastAgentExit ? new AgentDisconnectedError(this.lastAgentExit.reason, this.lastAgentExit.exitCode, this.lastAgentExit.signal, { outputAlreadyEmitted: Boolean(this.activePrompt) }) : new AgentDisconnectedError("connection_close", null, null, { outputAlreadyEmitted: Boolean(this.activePrompt) }));
4619
4725
  this.sessionUpdateChain = Promise.resolve();
4620
4726
  this.observedSessionUpdates = 0;
@@ -4622,6 +4728,7 @@ var AcpClient = class {
4622
4728
  this.suppressSessionUpdates = false;
4623
4729
  this.suppressReplaySessionUpdateMessages = false;
4624
4730
  this.activePrompt = void 0;
4731
+ this.pendingPromptOwners.length = 0;
4625
4732
  this.cancellingSessionIds.clear();
4626
4733
  for (const controller of this.permissionAbortControllers.values()) controller.abort();
4627
4734
  this.permissionAbortControllers.clear();
@@ -4633,6 +4740,12 @@ var AcpClient = class {
4633
4740
  this.connection = void 0;
4634
4741
  this.agent = void 0;
4635
4742
  }
4743
+ abortActiveElicitation() {
4744
+ this.activePrompt?.elicitationController?.abort();
4745
+ }
4746
+ closeConnection() {
4747
+ this.connection?.close?.();
4748
+ }
4636
4749
  async terminateAgentProcess(child) {
4637
4750
  const stdinCloseGraceMs = resolveAgentCloseAfterStdinEndMs(this.options.agentCommand);
4638
4751
  this.endAgentStdin(child);
@@ -4791,12 +4904,12 @@ var AcpClient = class {
4791
4904
  const { command, args } = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
4792
4905
  return command.replace(/\\/g, "/").split("/").pop()?.replace(/\.(cmd|exe|ps1)$/iu, "").toLowerCase() === "grok" && args[0] === "agent" && args[1] === "stdio";
4793
4906
  }
4794
- async authenticateIfRequired(connection, methods) {
4795
- if (methods.length === 0) return;
4796
- const selected = this.selectAuthMethod(methods);
4907
+ async authenticateIfRequired(connection, authMethods) {
4908
+ if (authMethods.length === 0) return;
4909
+ const selected = this.selectAuthMethod(authMethods);
4797
4910
  if (!selected) {
4798
- if (this.options.authPolicy === "fail") throw new AuthPolicyError(`agent advertised auth methods [${methods.map((m) => m.id).join(", ")}] but no matching credentials found`);
4799
- this.log(`agent advertised auth methods [${methods.map((m) => m.id).join(", ")}] but no matching credentials found — skipping (agent may handle auth internally)`);
4911
+ if (this.options.authPolicy === "fail") throw new AuthPolicyError(`agent advertised auth methods [${authMethods.map((m) => m.id).join(", ")}] but no matching credentials found`);
4912
+ this.log(`agent advertised auth methods [${authMethods.map((m) => m.id).join(", ")}] but no matching credentials found — skipping (agent may handle auth internally)`);
4800
4913
  return;
4801
4914
  }
4802
4915
  await connection.authenticate({ methodId: selected.methodId });
@@ -4813,6 +4926,50 @@ var AcpClient = class {
4813
4926
  }
4814
4927
  return response;
4815
4928
  }
4929
+ async handleElicitationRequest(request, requestId, requestSignal) {
4930
+ const resolved = this.resolveElicitationOwner(request);
4931
+ if ("response" in resolved) return resolved.response;
4932
+ const { active, handler } = resolved.owner;
4933
+ const signal = AbortSignal.any([requestSignal, active.elicitationController.signal]);
4934
+ const handlerAttempt = Promise.resolve().then(async () => await handler(request, {
4935
+ requestId,
4936
+ signal
4937
+ })).then((response) => ({
4938
+ kind: "response",
4939
+ response
4940
+ }), (error) => ({
4941
+ kind: "error",
4942
+ error
4943
+ }));
4944
+ const outcome = await Promise.race([handlerAttempt, waitForAbort(signal)]);
4945
+ if (this.activePrompt !== active) return cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.inactive);
4946
+ if (outcome.kind === "aborted" || signal.aborted) return cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.cancelled);
4947
+ if (outcome.kind === "error" || !isKnownElicitationResponse(outcome.response)) return cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.unavailable);
4948
+ return outcome.response;
4949
+ }
4950
+ resolveElicitationOwner(request) {
4951
+ if (!this.options.elicitationModes?.includes(request.mode)) return { response: cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.unsupported) };
4952
+ const active = this.activePrompt;
4953
+ if (!active) return { response: cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.inactive) };
4954
+ const scope = this.resolveElicitationScope(request, active);
4955
+ if ("response" in scope) return scope;
4956
+ if (this.isElicitationSessionCancelling(scope.sessionId)) return { response: cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.cancelled) };
4957
+ if (!active.elicitationHandler) return { response: cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.unavailable) };
4958
+ return { owner: {
4959
+ active,
4960
+ handler: active.elicitationHandler
4961
+ } };
4962
+ }
4963
+ resolveElicitationScope(request, active) {
4964
+ const sessionId = elicitationSessionId(request);
4965
+ if (sessionId) return sessionId === active.sessionId ? { sessionId } : { response: cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.mismatchedSession) };
4966
+ const requestScopeId = elicitationRequestScopeId(request);
4967
+ if (requestScopeId === void 0 || requestScopeId !== active.requestId) return { response: cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.requestScoped) };
4968
+ return { sessionId: active.sessionId };
4969
+ }
4970
+ isElicitationSessionCancelling(sessionId) {
4971
+ return this.closing || this.cancellingSessionIds.has(sessionId);
4972
+ }
4816
4973
  async tryHandlePermissionRequestWithHost(params) {
4817
4974
  if (!this.options.onPermissionRequest) return;
4818
4975
  const signal = this.cancellationSignalForSession(params.sessionId);
@@ -4898,7 +5055,7 @@ var AcpClient = class {
4898
5055
  if (error) this.promptPermissionFailures.delete(sessionId);
4899
5056
  return error;
4900
5057
  }
4901
- async runConnectionRequest(run) {
5058
+ async runConnectionRequest(run, onRequestStarted, canStartRequest = () => true) {
4902
5059
  return await new Promise((resolve, reject) => {
4903
5060
  const pending = {
4904
5061
  settled: false,
@@ -4911,7 +5068,20 @@ var AcpClient = class {
4911
5068
  cb();
4912
5069
  };
4913
5070
  this.pendingConnectionRequests.add(pending);
4914
- Promise.resolve().then(run).then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
5071
+ Promise.resolve().then(async () => {
5072
+ if (pending.settled) return { started: false };
5073
+ const requestCanStart = canStartRequest();
5074
+ const request = run();
5075
+ if (requestCanStart) try {
5076
+ Promise.resolve(onRequestStarted?.()).catch(() => {});
5077
+ } catch {}
5078
+ return {
5079
+ started: true,
5080
+ value: await request
5081
+ };
5082
+ }).then((outcome) => {
5083
+ if (outcome.started) finish(() => resolve(outcome.value));
5084
+ }, (error) => finish(() => reject(error)));
4915
5085
  });
4916
5086
  }
4917
5087
  rejectPendingConnectionRequests(error) {
@@ -5712,18 +5882,6 @@ function trimRuntimeToolResult(result) {
5712
5882
  if (typeof result.output === "string") result.output = trimRuntimeText(result.output, MAX_RUNTIME_TOOL_IO_CHARS);
5713
5883
  }
5714
5884
  //#endregion
5715
- //#region src/session/config-options.ts
5716
- function applyConfigOptionsToState(state, configOptions) {
5717
- const acpxState = cloneSessionAcpxState(state) ?? {};
5718
- applyConfigOptionsModelState(acpxState, configOptions);
5719
- return acpxState;
5720
- }
5721
- function applyConfigOptionsToRecord(record, result) {
5722
- const configOptions = result?.configOptions;
5723
- if (!configOptions) return;
5724
- record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
5725
- }
5726
- //#endregion
5727
5885
  //#region src/session/mode-preference.ts
5728
5886
  function ensureAcpxState(state) {
5729
5887
  return state ?? {};
@@ -5756,17 +5914,6 @@ function setDesiredModeId(record, modeId) {
5756
5914
  else delete acpx.desired_mode_id;
5757
5915
  record.acpx = acpx;
5758
5916
  }
5759
- function setDesiredConfigOption(record, configId, value) {
5760
- const normalizedConfigId = normalizeModeId(configId);
5761
- if (!normalizedConfigId || normalizedConfigId === "mode" || normalizedConfigId === "model") return;
5762
- const acpx = ensureAcpxState(record.acpx);
5763
- const desired = { ...acpx.desired_config_options };
5764
- if (typeof value === "string") desired[normalizedConfigId] = value;
5765
- else delete desired[normalizedConfigId];
5766
- if (Object.keys(desired).length > 0) acpx.desired_config_options = desired;
5767
- else delete acpx.desired_config_options;
5768
- record.acpx = acpx;
5769
- }
5770
5917
  function clearDesiredConfigOption(state, configId) {
5771
5918
  const normalizedConfigId = normalizeModeId(configId);
5772
5919
  if (!normalizedConfigId || !state.desired_config_options) return;
@@ -5778,20 +5925,6 @@ function clearDesiredConfigOption(state, configId) {
5778
5925
  function getDesiredModelId(state) {
5779
5926
  return normalizeModelId(state?.session_options?.model);
5780
5927
  }
5781
- function hasStoredSessionOptions(options) {
5782
- return typeof options.model === "string" || Array.isArray(options.allowed_tools) || typeof options.max_turns === "number" || options.system_prompt !== void 0 || options.env !== void 0;
5783
- }
5784
- function setDesiredModelId(record, modelId, modelConfigId) {
5785
- const acpx = ensureAcpxState(record.acpx);
5786
- const normalized = normalizeModelId(modelId);
5787
- const sessionOptions = { ...acpx.session_options };
5788
- if (normalized) sessionOptions.model = normalized;
5789
- else delete sessionOptions.model;
5790
- if (hasStoredSessionOptions(sessionOptions)) acpx.session_options = sessionOptions;
5791
- else delete acpx.session_options;
5792
- clearDesiredConfigOption(acpx, modelConfigId ?? modelStateFromConfigOptions(acpx.config_options)?.configId);
5793
- record.acpx = acpx;
5794
- }
5795
5928
  function setCurrentModelId(record, modelId) {
5796
5929
  const acpx = ensureAcpxState(record.acpx);
5797
5930
  const normalized = normalizeModelId(modelId);
@@ -5828,6 +5961,50 @@ async function applyRequestedModelIfAdvertised(params) {
5828
5961
  };
5829
5962
  }
5830
5963
  //#endregion
5964
+ //#region src/session/config-options.ts
5965
+ function applyConfigOptionsToState(state, configOptions) {
5966
+ const acpxState = cloneSessionAcpxState(state) ?? {};
5967
+ applyConfigOptionsModelState(acpxState, configOptions);
5968
+ return acpxState;
5969
+ }
5970
+ function applyConfigOptionsToRecord(record, result) {
5971
+ const configOptions = result?.configOptions;
5972
+ if (!configOptions) return;
5973
+ record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
5974
+ }
5975
+ function applyAcceptedConfigOptions(state, response) {
5976
+ const next = cloneSessionAcpxState(state) ?? {};
5977
+ if (!response) return next;
5978
+ applyConfigOptionsModelState(next, response.configOptions);
5979
+ if (!next.desired_config_options) return next;
5980
+ const desired = {};
5981
+ for (const option of response.configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(next.desired_config_options, option.id)) desired[option.id] = option.currentValue;
5982
+ if (Object.keys(desired).length > 0) next.desired_config_options = desired;
5983
+ else delete next.desired_config_options;
5984
+ return next;
5985
+ }
5986
+ function applyModelSelection(state, modelId, response) {
5987
+ const modelConfigId = advertisedModelState(state)?.configId;
5988
+ const next = applyAcceptedConfigOptions(state, response);
5989
+ next.session_options = {
5990
+ ...next.session_options,
5991
+ model: modelId
5992
+ };
5993
+ next.current_model_id = currentModelIdFromSetModelResponse(response, modelId);
5994
+ clearDesiredConfigOption(next, modelConfigId ?? advertisedModelState(next)?.configId);
5995
+ return next;
5996
+ }
5997
+ function applyConfigOptionSelection(state, configId, value, response, modelConfigId = advertisedModelState(state)?.configId) {
5998
+ if (configId === modelConfigId || configId === modelStateFromConfigOptions(response.configOptions)?.configId) return applyModelSelection(state, value, response);
5999
+ const next = cloneSessionAcpxState(state) ?? {};
6000
+ if (configId === "mode") next.desired_mode_id = value;
6001
+ else next.desired_config_options = {
6002
+ ...next.desired_config_options,
6003
+ [configId]: value
6004
+ };
6005
+ return applyAcceptedConfigOptions(next, response);
6006
+ }
6007
+ //#endregion
5831
6008
  //#region src/runtime/engine/reconnect.ts
5832
6009
  function isProcessAlive(pid) {
5833
6010
  if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
@@ -5872,8 +6049,11 @@ async function replayDesiredMode(params) {
5872
6049
  });
5873
6050
  }
5874
6051
  }
6052
+ function canReplayModel(desiredModelId, models, createdFreshSession) {
6053
+ return Boolean(desiredModelId) && (createdFreshSession || models !== void 0);
6054
+ }
5875
6055
  async function replayDesiredModel(params) {
5876
- if (!params.desiredModelId) return { replayed: false };
6056
+ if (!canReplayModel(params.desiredModelId, params.models, params.createdFreshSession)) return { replayed: false };
5877
6057
  try {
5878
6058
  emitModelSupportWarning(assertRequestedModelSupported({
5879
6059
  requestedModel: params.desiredModelId,
@@ -5881,21 +6061,21 @@ async function replayDesiredModel(params) {
5881
6061
  agentCommand: params.record.agentCommand,
5882
6062
  context: "replay"
5883
6063
  }), params.suppressWarnings);
5884
- if (!params.models || params.models.currentModelId === params.desiredModelId) return { replayed: false };
6064
+ if (!params.models) return { replayed: false };
5885
6065
  const response = await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId, params.models), params.timeoutMs);
5886
- applyConfigOptionsToRecord(params.record, response);
6066
+ params.record.acpx = applyModelSelection(params.record.acpx, params.desiredModelId, response);
5887
6067
  const models = response ? modelStateFromConfigOptions(response.configOptions) : {
5888
6068
  ...params.models,
5889
6069
  currentModelId: params.desiredModelId
5890
6070
  };
5891
- if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
6071
+ if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
5892
6072
  return {
5893
6073
  replayed: true,
5894
6074
  models,
5895
- configOptionsPresent: response !== void 0
6075
+ configOptions: response?.configOptions
5896
6076
  };
5897
6077
  } catch (error) {
5898
- throw new SessionModelReplayError(`Failed to replay saved session model ${params.desiredModelId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
6078
+ throw new SessionModelReplayError(`Failed to replay saved session model ${params.desiredModelId} on ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
5899
6079
  cause: error instanceof Error ? error : void 0,
5900
6080
  retryable: true
5901
6081
  });
@@ -5906,19 +6086,25 @@ function emitModelSupportWarning(warning, suppressWarnings) {
5906
6086
  }
5907
6087
  async function replayDesiredConfigOptions(params) {
5908
6088
  let result = { replayed: false };
5909
- for (const [configId, value] of Object.entries(params.desiredConfigOptions)) try {
5910
- const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
5911
- applyConfigOptionsToRecord(params.record, response);
5912
- result = {
5913
- replayed: true,
5914
- models: modelStateFromConfigOptions(response.configOptions)
5915
- };
5916
- if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
5917
- } catch (error) {
5918
- throw new SessionConfigOptionReplayError(`Failed to replay saved session config option ${configId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
5919
- cause: error instanceof Error ? error : void 0,
5920
- retryable: true
5921
- });
6089
+ let acceptedConfigOptions = params.acceptedConfigOptions;
6090
+ const replacingKey = resolveReplacingConfigOptionId(params.replacingConfigOption, params.record);
6091
+ for (const [configId, value] of Object.entries(params.desiredConfigOptions)) {
6092
+ if (configId === replacingKey || acceptedConfigOptions && !acceptsSavedConfigValue(acceptedConfigOptions.find((option) => option.id === configId), value)) continue;
6093
+ try {
6094
+ const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
6095
+ params.record.acpx = applyConfigOptionSelection(params.record.acpx, configId, value, response);
6096
+ acceptedConfigOptions = response.configOptions;
6097
+ result = {
6098
+ replayed: true,
6099
+ models: modelStateFromConfigOptions(response.configOptions)
6100
+ };
6101
+ if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
6102
+ } catch (error) {
6103
+ throw new SessionConfigOptionReplayError(`Failed to replay saved session config option ${configId} on ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
6104
+ cause: error instanceof Error ? error : void 0,
6105
+ retryable: true
6106
+ });
6107
+ }
5922
6108
  }
5923
6109
  return result;
5924
6110
  }
@@ -5965,10 +6151,12 @@ async function connectAndLoadSession(options) {
5965
6151
  createdFreshSession = loadState.createdFreshSession;
5966
6152
  pendingAgentSessionId = loadState.pendingAgentSessionId;
5967
6153
  sessionModels = loadState.sessionModels;
5968
- const preferenceReplay = await replayFreshSessionPreferences({
6154
+ const preferenceReplay = await replaySessionPreferences({
5969
6155
  client,
5970
6156
  record,
5971
6157
  createdFreshSession,
6158
+ reusingLoadedSession,
6159
+ replacingConfigOption: options.replacingConfigOption,
5972
6160
  sessionId,
5973
6161
  pendingAgentSessionId,
5974
6162
  originalSessionId,
@@ -5999,7 +6187,7 @@ function preserveLegacyModels(models) {
5999
6187
  return models && !models.configId ? models : void 0;
6000
6188
  }
6001
6189
  function resolveConfigOptionsPresenceAfterReplay(replay, initiallyPresent) {
6002
- return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.replayed && replay.modelReplay.configOptionsPresent;
6190
+ return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.configOptions !== void 0;
6003
6191
  }
6004
6192
  function applyReconnectedModelState(record, sessionModels, configOptionsPresent, legacyModelMetadataPresent, createdFreshSession) {
6005
6193
  clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent);
@@ -6022,18 +6210,30 @@ function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbos
6022
6210
  }
6023
6211
  if (shouldReconnect) process.stderr.write(`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session reconnect\n`);
6024
6212
  }
6025
- async function replayFreshSessionPreferences(params) {
6026
- if (!params.createdFreshSession) return {
6213
+ function replacesModel(replacingConfigOption, models, originalAcpx) {
6214
+ const key = replacingConfigOption?.key;
6215
+ return key !== void 0 && (key === "model" || key === models?.configId || key === advertisedModelState(originalAcpx)?.configId);
6216
+ }
6217
+ function acceptsSavedConfigValue(option, value) {
6218
+ if (!option || option.type !== "select") return false;
6219
+ return option.currentValue === value || option.options.some((entry) => "options" in entry ? entry.options.some((choice) => choice.value === value) : entry.value === value);
6220
+ }
6221
+ function resolveReplacingConfigOptionId(replacement, record) {
6222
+ return replacement?.resolve?.(record) ?? replacement?.key;
6223
+ }
6224
+ async function replaySessionPreferences(params) {
6225
+ if (params.reusingLoadedSession) return {
6027
6226
  modelReplay: { replayed: false },
6028
6227
  configReplay: { replayed: false }
6029
6228
  };
6030
6229
  let modelReplay = { replayed: false };
6031
6230
  let configReplay = { replayed: false };
6231
+ const replacingModel = replacesModel(params.replacingConfigOption, params.sessionModels, params.originalAcpx);
6032
6232
  try {
6033
6233
  await replayDesiredMode({
6034
6234
  client: params.client,
6035
6235
  sessionId: params.sessionId,
6036
- desiredModeId: params.desiredModeId,
6236
+ desiredModeId: params.createdFreshSession ? params.desiredModeId : void 0,
6037
6237
  previousSessionId: params.originalSessionId,
6038
6238
  timeoutMs: params.timeoutMs,
6039
6239
  verbose: params.verbose
@@ -6041,10 +6241,11 @@ async function replayFreshSessionPreferences(params) {
6041
6241
  modelReplay = await replayDesiredModel({
6042
6242
  client: params.client,
6043
6243
  sessionId: params.sessionId,
6044
- desiredModelId: params.desiredModelId,
6244
+ desiredModelId: replacingModel ? void 0 : params.desiredModelId,
6045
6245
  previousSessionId: params.originalSessionId,
6046
6246
  record: params.record,
6047
6247
  models: params.sessionModels,
6248
+ createdFreshSession: params.createdFreshSession,
6048
6249
  timeoutMs: params.timeoutMs,
6049
6250
  verbose: params.verbose,
6050
6251
  suppressWarnings: params.suppressWarnings
@@ -6053,7 +6254,9 @@ async function replayFreshSessionPreferences(params) {
6053
6254
  client: params.client,
6054
6255
  record: params.record,
6055
6256
  sessionId: params.sessionId,
6056
- desiredConfigOptions: params.desiredConfigOptions,
6257
+ desiredConfigOptions: replacingModel ? {} : params.desiredConfigOptions,
6258
+ acceptedConfigOptions: modelReplay.configOptions,
6259
+ replacingConfigOption: replacingModel ? void 0 : params.replacingConfigOption,
6057
6260
  previousSessionId: params.originalSessionId,
6058
6261
  timeoutMs: params.timeoutMs,
6059
6262
  verbose: params.verbose
@@ -6178,35 +6381,24 @@ function createActiveSessionController(params) {
6178
6381
  }
6179
6382
  async function withConnectedSession(options) {
6180
6383
  const record = await options.loadRecord(options.sessionRecordId);
6181
- const client = options.createClient?.({
6182
- agentCommand: record.agentCommand,
6183
- agentArgv: record.agentArgv,
6184
- cwd: absolutePath(record.cwd),
6185
- mcpServers: options.mcpServers,
6186
- permissionMode: options.permissionMode ?? "approve-reads",
6187
- nonInteractivePermissions: options.nonInteractivePermissions,
6188
- onPermissionRequest: options.onPermissionRequest,
6189
- authCredentials: options.authCredentials,
6190
- authPolicy: options.authPolicy,
6191
- fs: options.fs,
6192
- terminal: options.terminal,
6193
- verbose: options.verbose,
6194
- sessionOptions: sessionOptionsFromRecord(record)
6195
- }) ?? new AcpClient({
6384
+ const clientOptions = {
6196
6385
  agentCommand: record.agentCommand,
6197
6386
  agentArgv: record.agentArgv,
6198
6387
  cwd: absolutePath(record.cwd),
6199
6388
  mcpServers: options.mcpServers,
6200
6389
  permissionMode: options.permissionMode ?? "approve-reads",
6201
6390
  nonInteractivePermissions: options.nonInteractivePermissions,
6391
+ permissionPolicy: options.permissionPolicy,
6202
6392
  onPermissionRequest: options.onPermissionRequest,
6203
6393
  authCredentials: options.authCredentials,
6204
6394
  authPolicy: options.authPolicy,
6205
6395
  fs: options.fs,
6206
6396
  terminal: options.terminal,
6397
+ elicitationModes: options.elicitationModes,
6207
6398
  verbose: options.verbose,
6208
6399
  sessionOptions: sessionOptionsFromRecord(record)
6209
- });
6400
+ };
6401
+ const client = options.createClient?.(clientOptions) ?? new AcpClient(clientOptions);
6210
6402
  let activeSessionIdForControl = record.acpSessionId;
6211
6403
  let notifiedClientAvailable = false;
6212
6404
  const activeController = createActiveSessionController({
@@ -6220,6 +6412,7 @@ async function withConnectedSession(options) {
6220
6412
  client,
6221
6413
  record,
6222
6414
  resumePolicy: options.resumePolicy,
6415
+ replacingConfigOption: options.replacingConfigOption,
6223
6416
  timeoutMs: options.timeoutMs,
6224
6417
  verbose: options.verbose,
6225
6418
  activeController,
@@ -6278,7 +6471,7 @@ const SESSION_REPLY_IDLE_MS = 1e3;
6278
6471
  const SESSION_REPLY_DRAIN_TIMEOUT_MS = 5e3;
6279
6472
  async function runPromptTurn(params) {
6280
6473
  try {
6281
- const promptPromise = params.client.prompt(params.sessionId, params.prompt);
6474
+ const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestStarted, params.onElicitation);
6282
6475
  await params.onPromptStarted?.();
6283
6476
  const response = await withTimeout(promptPromise, params.timeoutMs);
6284
6477
  await params.client.waitForSessionUpdatesIdle?.({
@@ -6357,6 +6550,6 @@ var LiveSessionCheckpoint = class {
6357
6550
  }
6358
6551
  };
6359
6552
  //#endregion
6360
- export { createAtomicWriteTempPath as $, SESSION_RECORD_SCHEMA as $t, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as A, withInterrupt as At, permissionModeSatisfies as B, isRetryablePromptError as Bt, mergeSessionOptions as C, isPromptInput as Ct, applyLifecycleSnapshotToRecord as D, textPrompt as Dt, applyConversation as E, promptToDisplayText as Et, modelStateFromConfigOptions as F, resolveAgentArgv as Ft, findSessionByDirectoryWalk as G, AUTH_POLICIES as Gt, absolutePath as H, extractAcpError as Ht, normalizeAgentCommandInput as I, resolveAgentCommand as It, listSessionsForAgent as J, OUTPUT_ERROR_CODES as Jt, isoNow$2 as K, EXIT_CODES as Kt, renderArgvIdentity as L, resolveCanonicalAgentName as Lt, RequestedModelUnsupportedError as M, DEFAULT_AGENT_NAME as Mt, assertRequestedModelSupported as N, listBuiltInAgents as Nt, reconcileAgentSessionId as O, InterruptedError as Ot, isRequestedModelUnsupportedError as P, normalizeAgentName$1 as Pt, writeSessionRecord as Q, PERMISSION_POLICY_ACTIONS as Qt, splitCommandLine as R, exitCodeForOutputErrorCode as Rt, advertisedModelState as S, PromptInputValidationError as St, sessionOptionsFromRecord as T, parsePromptSource as Tt, findGitRepositoryRoot as U, isAcpResourceNotFoundError as Ut, DEFAULT_HISTORY_LIMIT as V, normalizeOutputError as Vt, findSession as W, toAcpErrorPayload as Wt, pruneSessions as X, OUTPUT_FORMATS as Xt, normalizeName as Y, OUTPUT_ERROR_ORIGINS as Yt, resolveSessionRecord as Z, PERMISSION_MODES as Zt, createSessionConversation as _, sessionEventSegmentPath as _t, applyRequestedModelIfAdvertised as a, recordPerfDuration as at, recordSessionUpdate as b, parseJsonRpcErrorMessage as bt, setCurrentModelId as c, startPerfTimer as ct, setDesiredModelId as d, normalizeRuntimeSessionId as dt, AcpxOperationalError as en, assertPersistedKeyPolicy as et, syncAdvertisedModelState as f, DEFAULT_EVENT_SEGMENT_MAX_BYTES as ft, cloneSessionConversation as g, sessionEventLockPath as gt, cloneSessionAcpxState as h, sessionEventActivePath as ht, connectAndLoadSession as i, measurePerf as it, REQUESTED_MODEL_UNSUPPORTED_REASONS as j, withTimeout as jt, AcpClient as k, TimeoutError as kt, setDesiredConfigOption as l, parseSessionRecord as lt, applyConfigOptionsToState as m, sessionBaseDir$1 as mt, runPromptTurn as n, QueueConnectionError as nn, getPerfMetricsSnapshot as nt, currentModelIdFromSetModelResponse as o, resetPerfMetrics as ot, applyConfigOptionsToRecord as p, defaultSessionEventLog as pt, listSessions as q, NON_INTERACTIVE_PERMISSION_POLICIES as qt, withConnectedSession as r, QueueProtocolError as rn, incrementPerfCounter as rt, clearDesiredConfigOption as s, setPerfGauge as st, LiveSessionCheckpoint as t, AgentSpawnError as tn, formatPerfMetric as tt, setDesiredModeId as u, serializeSessionRecordForDisk as ut, recordClientOperation as v, extractSessionUpdateNotification as vt, persistSessionOptions as w, mergePromptSourceWithText as wt, trimConversationForRuntime as x, parsePromptStopReason as xt, recordPromptSubmission as y, isAcpJsonRpcMessage as yt, getAcpxVersion as z, formatErrorMessage as zt };
6553
+ export { formatPerfMetric as $, AgentSpawnError as $t, RequestedModelUnsupportedError as A, DEFAULT_AGENT_NAME as At, absolutePath as B, extractAcpError as Bt, sessionOptionsFromRecord as C, parsePromptSource as Ct, AcpClient as D, TimeoutError as Dt, reconcileAgentSessionId as E, InterruptedError as Et, runTimedExecFile as F, resolveCanonicalAgentName as Ft, listSessions as G, NON_INTERACTIVE_PERMISSION_POLICIES as Gt, findSession as H, toAcpErrorPayload as Ht, splitCommandLine as I, exitCodeForOutputErrorCode as It, pruneSessions as J, OUTPUT_FORMATS as Jt, listSessionsForAgent as K, OUTPUT_ERROR_CODES as Kt, getAcpxVersion as L, formatErrorMessage as Lt, modelStateFromConfigOptions as M, normalizeAgentName$1 as Mt, normalizeAgentCommandInput as N, resolveAgentArgv as Nt, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as O, withInterrupt as Ot, renderArgvIdentity as P, resolveAgentCommand as Pt, assertPersistedKeyPolicy as Q, AcpxOperationalError as Qt, permissionModeSatisfies as R, isRetryablePromptError as Rt, persistSessionOptions as S, mergePromptSourceWithText as St, applyLifecycleSnapshotToRecord as T, textPrompt as Tt, findSessionByDirectoryWalk as U, AUTH_POLICIES as Ut, findGitRepositoryRoot as V, isAcpResourceNotFoundError as Vt, isoNow$2 as W, EXIT_CODES as Wt, writeSessionRecord as X, PERMISSION_POLICY_ACTIONS as Xt, resolveSessionRecord as Y, PERMISSION_MODES as Yt, createAtomicWriteTempPath as Z, SESSION_RECORD_SCHEMA as Zt, recordPromptSubmission as _, isAcpJsonRpcMessage as _t, applyConfigOptionSelection as a, setPerfGauge as at, advertisedModelState as b, PromptInputValidationError as bt, applyRequestedModelIfAdvertised as c, serializeSessionRecordForDisk as ct, setDesiredModeId as d, defaultSessionEventLog as dt, QueueConnectionError as en, getPerfMetricsSnapshot as et, syncAdvertisedModelState as f, sessionBaseDir$1 as ft, recordClientOperation as g, extractSessionUpdateNotification as gt, createSessionConversation as h, sessionEventSegmentPath as ht, connectAndLoadSession as i, resetPerfMetrics as it, isRequestedModelUnsupportedError as j, listBuiltInAgents as jt, REQUESTED_MODEL_UNSUPPORTED_REASONS as k, withTimeout as kt, currentModelIdFromSetModelResponse as l, normalizeRuntimeSessionId as lt, cloneSessionConversation as m, sessionEventLockPath as mt, runPromptTurn as n, measurePerf as nt, applyConfigOptionsToRecord as o, startPerfTimer as ot, cloneSessionAcpxState as p, sessionEventActivePath as pt, normalizeName as q, OUTPUT_ERROR_ORIGINS as qt, withConnectedSession as r, recordPerfDuration as rt, applyModelSelection as s, parseSessionRecord as st, LiveSessionCheckpoint as t, QueueProtocolError as tn, incrementPerfCounter as tt, setCurrentModelId as u, DEFAULT_EVENT_SEGMENT_MAX_BYTES as ut, recordSessionUpdate as v, parseJsonRpcErrorMessage as vt, applyConversation as w, promptToDisplayText as wt, mergeSessionOptions as x, isPromptInput as xt, trimConversationForRuntime as y, parsePromptStopReason as yt, DEFAULT_HISTORY_LIMIT as z, normalizeOutputError as zt };
6361
6554
 
6362
- //# sourceMappingURL=live-checkpoint-CBecfnSH.js.map
6555
+ //# sourceMappingURL=live-checkpoint-Gw2oGjhe.js.map