acpx 0.13.1 → 0.14.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.
@@ -38,7 +38,12 @@ var SessionResolutionError = class extends AcpxOperationalError {};
38
38
  var AgentSpawnError = class extends AcpxOperationalError {
39
39
  agentCommand;
40
40
  constructor(agentCommand, cause) {
41
- super(`Failed to spawn agent command: ${agentCommand}`, { cause: cause instanceof Error ? cause : void 0 });
41
+ const spawnEnoent = (cause instanceof Error ? cause.code : void 0) === "ENOENT";
42
+ const message = spawnEnoent ? `Failed to spawn agent command: ${agentCommand}. The agent process could not start because a required executable, interpreter, working directory, or other launch path was not found. Check the command, effective PATH, and working directory, or verify the custom agent's configured argv.` : `Failed to spawn agent command: ${agentCommand}`;
43
+ super(message, {
44
+ cause: cause instanceof Error ? cause : void 0,
45
+ ...spawnEnoent ? { detailCode: "AGENT_SPAWN_ENOENT" } : {}
46
+ });
42
47
  this.agentCommand = agentCommand;
43
48
  }
44
49
  };
@@ -711,19 +716,17 @@ async function withInterrupt(run, onInterrupt) {
711
716
  process.off("SIGHUP", onSighup);
712
717
  cb();
713
718
  };
714
- const rejectInterrupted = () => {
715
- onInterrupt().finally(() => {
716
- finish(() => reject(new InterruptedError()));
717
- });
719
+ const rejectInterrupted = (signal) => {
720
+ onInterrupt(signal).then(() => finish(() => reject(new InterruptedError())), (error) => finish(() => reject(error)));
718
721
  };
719
722
  const onSigint = () => {
720
- rejectInterrupted();
723
+ rejectInterrupted("SIGINT");
721
724
  };
722
725
  const onSigterm = () => {
723
- rejectInterrupted();
726
+ rejectInterrupted("SIGTERM");
724
727
  };
725
728
  const onSighup = () => {
726
- rejectInterrupted();
729
+ rejectInterrupted("SIGHUP");
727
730
  };
728
731
  process.once("SIGINT", onSigint);
729
732
  process.once("SIGTERM", onSigterm);
@@ -982,7 +985,7 @@ function parseJsonRpcErrorMessage(message) {
982
985
  }
983
986
  //#endregion
984
987
  //#region src/session/event-log.ts
985
- const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 64 * 1024 * 1024;
988
+ const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 67108864;
986
989
  function sessionBaseDir$1() {
987
990
  return path.join(os.homedir(), ".acpx", "sessions");
988
991
  }
@@ -1661,7 +1664,11 @@ const ZED_TAG_KEYS = /* @__PURE__ */ new Set([
1661
1664
  "RedactedThinking",
1662
1665
  "ToolUse"
1663
1666
  ]);
1664
- const MAP_OBJECT_PATHS = /* @__PURE__ */ new Set(["request_token_usage", "messages.Agent.tool_results"]);
1667
+ const MAP_OBJECT_PATHS = /* @__PURE__ */ new Set([
1668
+ "request_token_usage",
1669
+ "messages.Agent.tool_results",
1670
+ "acpx.session_options.env"
1671
+ ]);
1665
1672
  const OPAQUE_VALUE_PATHS = /* @__PURE__ */ new Set([
1666
1673
  "agent_capabilities",
1667
1674
  "messages.Agent.content.ToolUse.input",
@@ -2068,7 +2075,7 @@ function toWritePreview(content) {
2068
2075
  const visibleLines = lines.slice(0, WRITE_PREVIEW_MAX_LINES);
2069
2076
  let preview = visibleLines.join("\n");
2070
2077
  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)}...`;
2078
+ if (preview.length > WRITE_PREVIEW_MAX_CHARS) preview = `${preview.slice(0, 1197)}...`;
2072
2079
  return preview;
2073
2080
  }
2074
2081
  async function defaultConfirmWrite(filePath, preview) {
@@ -2633,6 +2640,9 @@ function getAcpxVersion() {
2633
2640
  cachedVersion = resolveAcpxVersion();
2634
2641
  return cachedVersion;
2635
2642
  }
2643
+ //#endregion
2644
+ //#region src/acp/client-process.ts
2645
+ const PROCESS_HELPER_TIMEOUT_MS = 8e3;
2636
2646
  async function runTimedExecFile(command, args, options = {}) {
2637
2647
  const timeoutMs = Math.max(1, Math.round(options.timeoutMs ?? 8e3));
2638
2648
  return await new Promise((resolve, reject) => {
@@ -3338,7 +3348,7 @@ function maybeWrapSessionControlError(method, error, context) {
3338
3348
  }
3339
3349
  //#endregion
3340
3350
  //#region src/acp/terminal-manager.ts
3341
- const DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES = 64 * 1024;
3351
+ const DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES = 65536;
3342
3352
  const DEFAULT_KILL_GRACE_MS = 1500;
3343
3353
  function nowIso() {
3344
3354
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -3399,6 +3409,7 @@ function waitMs(ms) {
3399
3409
  setTimeout(resolve, Math.max(0, ms));
3400
3410
  });
3401
3411
  }
3412
+ function onStreamError() {}
3402
3413
  var TerminalManager = class {
3403
3414
  cwd;
3404
3415
  permissionMode;
@@ -3464,6 +3475,8 @@ var TerminalManager = class {
3464
3475
  };
3465
3476
  proc.stdout.on("data", appendOutput);
3466
3477
  proc.stderr.on("data", appendOutput);
3478
+ proc.stdout.on("error", onStreamError);
3479
+ proc.stderr.on("error", onStreamError);
3467
3480
  proc.once("exit", (exitCode, signal) => {
3468
3481
  terminal.exitCode = exitCode;
3469
3482
  terminal.signal = signal;
@@ -3638,7 +3651,7 @@ var TerminalManager = class {
3638
3651
  } catch {
3639
3652
  return;
3640
3653
  }
3641
- await this.waitForCleanupAfterSignal(terminal);
3654
+ await this.waitForFinalCleanup(terminal);
3642
3655
  }
3643
3656
  async signalProcess(terminal, signal) {
3644
3657
  const pid = terminal.process.pid;
@@ -3655,10 +3668,10 @@ var TerminalManager = class {
3655
3668
  async signalWindowsProcessGroup(terminal, pid, signal) {
3656
3669
  await this.captureDescendantPids(terminal, pid);
3657
3670
  if (this.isRunning(terminal)) {
3658
- await killWindowsProcessTree(pid, signal);
3671
+ await killWindowsProcessTree(pid, signal, terminal.processHelperTimeoutMs);
3659
3672
  return;
3660
3673
  }
3661
- for (const descendantPid of terminal.descendantPids) await killWindowsProcessTree(descendantPid, signal);
3674
+ for (const descendantPid of terminal.descendantPids) await killWindowsProcessTree(descendantPid, signal, terminal.processHelperTimeoutMs);
3662
3675
  }
3663
3676
  async signalPosixProcessGroup(terminal, pid, signal) {
3664
3677
  await this.captureDescendantPids(terminal, pid);
@@ -3672,6 +3685,9 @@ var TerminalManager = class {
3672
3685
  if (!this.isRunning(terminal)) await terminal.processGroupSnapshotPromise?.catch(() => {});
3673
3686
  for (const descendantPid of await listDescendantPids(pid, terminal.processHelperTimeoutMs)) terminal.descendantPids.add(descendantPid);
3674
3687
  }
3688
+ async waitForFinalCleanup(terminal) {
3689
+ if (!await this.waitForCleanupAfterSignal(terminal) && process.platform === "win32") throw new Error("Terminal process cleanup did not finish after SIGKILL");
3690
+ }
3675
3691
  async waitForCleanupAfterSignal(terminal) {
3676
3692
  return await Promise.race([this.waitForTerminalAndTrackedDescendants(terminal).then(() => true), waitMs(this.killGraceMs).then(() => false)]);
3677
3693
  }
@@ -3803,25 +3819,19 @@ async function runWindowsProcessListCommand(timeoutMs) {
3803
3819
  windowsHide: true
3804
3820
  });
3805
3821
  }
3806
- async function killWindowsProcessTree(pid, signal) {
3822
+ async function killWindowsProcessTree(pid, signal, timeoutMs = PROCESS_HELPER_TIMEOUT_MS) {
3807
3823
  const args = [
3808
3824
  "/pid",
3809
3825
  String(pid),
3810
3826
  "/t"
3811
3827
  ];
3812
3828
  if (signal === "SIGKILL") args.push("/f");
3813
- await new Promise((resolve) => {
3814
- const child = spawn("taskkill", args, {
3815
- stdio: [
3816
- "ignore",
3817
- "ignore",
3818
- "ignore"
3819
- ],
3829
+ try {
3830
+ await runTimedExecFile("taskkill", args, {
3831
+ timeoutMs,
3820
3832
  windowsHide: true
3821
3833
  });
3822
- child.once("error", () => resolve());
3823
- child.once("close", () => resolve());
3824
- });
3834
+ } catch {}
3825
3835
  }
3826
3836
  function sendSignal(pid, signal) {
3827
3837
  try {
@@ -4361,9 +4371,8 @@ var AcpClient = class {
4361
4371
  const onAcpMessage = () => this.eventHandlers.onAcpMessage;
4362
4372
  const onAcpOutputMessage = () => this.eventHandlers.onAcpOutputMessage;
4363
4373
  const elicitationRequestIds = /* @__PURE__ */ new Set();
4364
- const bindPromptOwner = (owner) => {
4365
- this.bindPromptOwner(owner);
4366
- };
4374
+ const bindPromptOwner = (owner) => this.bindPromptOwner(owner);
4375
+ const onPromptRequestWritten = (active, owner) => this.onPromptRequestWritten(active, owner);
4367
4376
  const shouldSuppressInboundReplaySessionUpdate = (message) => {
4368
4377
  return this.suppressReplaySessionUpdateMessages && isSessionUpdateNotification(message);
4369
4378
  };
@@ -4397,7 +4406,7 @@ var AcpClient = class {
4397
4406
  } }),
4398
4407
  writable: new WritableStream({ async write(message) {
4399
4408
  const promptOwner = promptRequestOwner(message);
4400
- if (promptOwner) bindPromptOwner(promptOwner);
4409
+ const activePrompt = promptOwner ? bindPromptOwner(promptOwner) : void 0;
4401
4410
  const id = responseId(message);
4402
4411
  if (!(id !== void 0 && elicitationRequestIds.delete(id))) {
4403
4412
  onAcpOutputMessage()?.("outbound", message);
@@ -4409,6 +4418,7 @@ var AcpClient = class {
4409
4418
  } finally {
4410
4419
  writer.releaseLock();
4411
4420
  }
4421
+ if (activePrompt && promptOwner) onPromptRequestWritten(activePrompt, promptOwner);
4412
4422
  } })
4413
4423
  };
4414
4424
  }
@@ -4498,17 +4508,17 @@ var AcpClient = class {
4498
4508
  this.suppressSessionUpdates = previous.suppressSessionUpdates;
4499
4509
  this.suppressReplaySessionUpdateMessages = previous.suppressReplaySessionUpdateMessages;
4500
4510
  }
4501
- async prompt(sessionId, prompt, onRequestStarted, onElicitation) {
4511
+ async prompt(sessionId, prompt, onRequestWritten, onElicitation) {
4502
4512
  const connection = this.getConnection();
4503
4513
  const normalizedPrompt = this.normalizePromptForAgent(prompt);
4504
4514
  const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
4505
- const activePrompt = this.beginActivePrompt(sessionId, onElicitation);
4515
+ const activePrompt = this.beginActivePrompt(sessionId, onRequestWritten, onElicitation);
4506
4516
  let promptPromise;
4507
4517
  try {
4508
4518
  promptPromise = this.runConnectionRequest(() => connection.prompt({
4509
4519
  sessionId,
4510
4520
  prompt: normalizedPrompt
4511
- }), onRequestStarted, () => !connection.signal?.aborted);
4521
+ }));
4512
4522
  } catch (error) {
4513
4523
  this.clearActivePrompt(activePrompt);
4514
4524
  restoreConsoleError?.();
@@ -4528,11 +4538,12 @@ var AcpClient = class {
4528
4538
  this.promptPermissionFailures.delete(sessionId);
4529
4539
  }
4530
4540
  }
4531
- beginActivePrompt(sessionId, elicitationHandler) {
4541
+ beginActivePrompt(sessionId, onRequestWritten, elicitationHandler) {
4532
4542
  const previous = this.activePrompt;
4533
4543
  this.cancellingSessionIds.delete(sessionId);
4534
4544
  const active = {
4535
4545
  sessionId,
4546
+ onRequestWritten,
4536
4547
  elicitationHandler,
4537
4548
  elicitationController: new AbortController()
4538
4549
  };
@@ -4546,6 +4557,13 @@ var AcpClient = class {
4546
4557
  return candidate.requestId === void 0 && candidate.sessionId === owner.sessionId;
4547
4558
  });
4548
4559
  if (active) active.requestId = owner.requestId;
4560
+ return active;
4561
+ }
4562
+ onPromptRequestWritten(active, owner) {
4563
+ if (active.requestId !== owner.requestId || active.sessionId !== owner.sessionId) return;
4564
+ try {
4565
+ Promise.resolve(active.onRequestWritten?.()).catch(() => {});
4566
+ } catch {}
4549
4567
  }
4550
4568
  clearActivePrompt(active) {
4551
4569
  if (this.activePrompt === active) this.activePrompt = void 0;
@@ -5055,7 +5073,7 @@ var AcpClient = class {
5055
5073
  if (error) this.promptPermissionFailures.delete(sessionId);
5056
5074
  return error;
5057
5075
  }
5058
- async runConnectionRequest(run, onRequestStarted, canStartRequest = () => true) {
5076
+ async runConnectionRequest(run) {
5059
5077
  return await new Promise((resolve, reject) => {
5060
5078
  const pending = {
5061
5079
  settled: false,
@@ -5070,14 +5088,9 @@ var AcpClient = class {
5070
5088
  this.pendingConnectionRequests.add(pending);
5071
5089
  Promise.resolve().then(async () => {
5072
5090
  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
5091
  return {
5079
5092
  started: true,
5080
- value: await request
5093
+ value: await run()
5081
5094
  };
5082
5095
  }).then((outcome) => {
5083
5096
  if (outcome.started) finish(() => resolve(outcome.value));
@@ -5882,18 +5895,6 @@ function trimRuntimeToolResult(result) {
5882
5895
  if (typeof result.output === "string") result.output = trimRuntimeText(result.output, MAX_RUNTIME_TOOL_IO_CHARS);
5883
5896
  }
5884
5897
  //#endregion
5885
- //#region src/session/config-options.ts
5886
- function applyConfigOptionsToState(state, configOptions) {
5887
- const acpxState = cloneSessionAcpxState(state) ?? {};
5888
- applyConfigOptionsModelState(acpxState, configOptions);
5889
- return acpxState;
5890
- }
5891
- function applyConfigOptionsToRecord(record, result) {
5892
- const configOptions = result?.configOptions;
5893
- if (!configOptions) return;
5894
- record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
5895
- }
5896
- //#endregion
5897
5898
  //#region src/session/mode-preference.ts
5898
5899
  function ensureAcpxState(state) {
5899
5900
  return state ?? {};
@@ -5926,17 +5927,6 @@ function setDesiredModeId(record, modeId) {
5926
5927
  else delete acpx.desired_mode_id;
5927
5928
  record.acpx = acpx;
5928
5929
  }
5929
- function setDesiredConfigOption(record, configId, value) {
5930
- const normalizedConfigId = normalizeModeId(configId);
5931
- if (!normalizedConfigId || normalizedConfigId === "mode" || normalizedConfigId === "model") return;
5932
- const acpx = ensureAcpxState(record.acpx);
5933
- const desired = { ...acpx.desired_config_options };
5934
- if (typeof value === "string") desired[normalizedConfigId] = value;
5935
- else delete desired[normalizedConfigId];
5936
- if (Object.keys(desired).length > 0) acpx.desired_config_options = desired;
5937
- else delete acpx.desired_config_options;
5938
- record.acpx = acpx;
5939
- }
5940
5930
  function clearDesiredConfigOption(state, configId) {
5941
5931
  const normalizedConfigId = normalizeModeId(configId);
5942
5932
  if (!normalizedConfigId || !state.desired_config_options) return;
@@ -5948,20 +5938,6 @@ function clearDesiredConfigOption(state, configId) {
5948
5938
  function getDesiredModelId(state) {
5949
5939
  return normalizeModelId(state?.session_options?.model);
5950
5940
  }
5951
- function hasStoredSessionOptions(options) {
5952
- return typeof options.model === "string" || Array.isArray(options.allowed_tools) || typeof options.max_turns === "number" || options.system_prompt !== void 0 || options.env !== void 0;
5953
- }
5954
- function setDesiredModelId(record, modelId, modelConfigId) {
5955
- const acpx = ensureAcpxState(record.acpx);
5956
- const normalized = normalizeModelId(modelId);
5957
- const sessionOptions = { ...acpx.session_options };
5958
- if (normalized) sessionOptions.model = normalized;
5959
- else delete sessionOptions.model;
5960
- if (hasStoredSessionOptions(sessionOptions)) acpx.session_options = sessionOptions;
5961
- else delete acpx.session_options;
5962
- clearDesiredConfigOption(acpx, modelConfigId ?? modelStateFromConfigOptions(acpx.config_options)?.configId);
5963
- record.acpx = acpx;
5964
- }
5965
5941
  function setCurrentModelId(record, modelId) {
5966
5942
  const acpx = ensureAcpxState(record.acpx);
5967
5943
  const normalized = normalizeModelId(modelId);
@@ -5998,6 +5974,50 @@ async function applyRequestedModelIfAdvertised(params) {
5998
5974
  };
5999
5975
  }
6000
5976
  //#endregion
5977
+ //#region src/session/config-options.ts
5978
+ function applyConfigOptionsToState(state, configOptions) {
5979
+ const acpxState = cloneSessionAcpxState(state) ?? {};
5980
+ applyConfigOptionsModelState(acpxState, configOptions);
5981
+ return acpxState;
5982
+ }
5983
+ function applyConfigOptionsToRecord(record, result) {
5984
+ const configOptions = result?.configOptions;
5985
+ if (!configOptions) return;
5986
+ record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
5987
+ }
5988
+ function applyAcceptedConfigOptions(state, response) {
5989
+ const next = cloneSessionAcpxState(state) ?? {};
5990
+ if (!response) return next;
5991
+ applyConfigOptionsModelState(next, response.configOptions);
5992
+ if (!next.desired_config_options) return next;
5993
+ const desired = {};
5994
+ for (const option of response.configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(next.desired_config_options, option.id)) desired[option.id] = option.currentValue;
5995
+ if (Object.keys(desired).length > 0) next.desired_config_options = desired;
5996
+ else delete next.desired_config_options;
5997
+ return next;
5998
+ }
5999
+ function applyModelSelection(state, modelId, response) {
6000
+ const modelConfigId = advertisedModelState(state)?.configId;
6001
+ const next = applyAcceptedConfigOptions(state, response);
6002
+ next.session_options = {
6003
+ ...next.session_options,
6004
+ model: modelId
6005
+ };
6006
+ next.current_model_id = currentModelIdFromSetModelResponse(response, modelId);
6007
+ clearDesiredConfigOption(next, modelConfigId ?? advertisedModelState(next)?.configId);
6008
+ return next;
6009
+ }
6010
+ function applyConfigOptionSelection(state, configId, value, response, modelConfigId = advertisedModelState(state)?.configId) {
6011
+ if (configId === modelConfigId || configId === modelStateFromConfigOptions(response.configOptions)?.configId) return applyModelSelection(state, value, response);
6012
+ const next = cloneSessionAcpxState(state) ?? {};
6013
+ if (configId === "mode") next.desired_mode_id = value;
6014
+ else next.desired_config_options = {
6015
+ ...next.desired_config_options,
6016
+ [configId]: value
6017
+ };
6018
+ return applyAcceptedConfigOptions(next, response);
6019
+ }
6020
+ //#endregion
6001
6021
  //#region src/runtime/engine/reconnect.ts
6002
6022
  function isProcessAlive(pid) {
6003
6023
  if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
@@ -6042,8 +6062,11 @@ async function replayDesiredMode(params) {
6042
6062
  });
6043
6063
  }
6044
6064
  }
6065
+ function canReplayModel(desiredModelId, models, createdFreshSession) {
6066
+ return Boolean(desiredModelId) && (createdFreshSession || models !== void 0);
6067
+ }
6045
6068
  async function replayDesiredModel(params) {
6046
- if (!params.desiredModelId) return { replayed: false };
6069
+ if (!canReplayModel(params.desiredModelId, params.models, params.createdFreshSession)) return { replayed: false };
6047
6070
  try {
6048
6071
  emitModelSupportWarning(assertRequestedModelSupported({
6049
6072
  requestedModel: params.desiredModelId,
@@ -6051,21 +6074,21 @@ async function replayDesiredModel(params) {
6051
6074
  agentCommand: params.record.agentCommand,
6052
6075
  context: "replay"
6053
6076
  }), params.suppressWarnings);
6054
- if (!params.models || params.models.currentModelId === params.desiredModelId) return { replayed: false };
6077
+ if (!params.models) return { replayed: false };
6055
6078
  const response = await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId, params.models), params.timeoutMs);
6056
- applyConfigOptionsToRecord(params.record, response);
6079
+ params.record.acpx = applyModelSelection(params.record.acpx, params.desiredModelId, response);
6057
6080
  const models = response ? modelStateFromConfigOptions(response.configOptions) : {
6058
6081
  ...params.models,
6059
6082
  currentModelId: params.desiredModelId
6060
6083
  };
6061
- if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
6084
+ if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
6062
6085
  return {
6063
6086
  replayed: true,
6064
6087
  models,
6065
- configOptionsPresent: response !== void 0
6088
+ configOptions: response?.configOptions
6066
6089
  };
6067
6090
  } catch (error) {
6068
- throw new SessionModelReplayError(`Failed to replay saved session model ${params.desiredModelId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
6091
+ throw new SessionModelReplayError(`Failed to replay saved session model ${params.desiredModelId} on ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
6069
6092
  cause: error instanceof Error ? error : void 0,
6070
6093
  retryable: true
6071
6094
  });
@@ -6076,19 +6099,25 @@ function emitModelSupportWarning(warning, suppressWarnings) {
6076
6099
  }
6077
6100
  async function replayDesiredConfigOptions(params) {
6078
6101
  let result = { replayed: false };
6079
- for (const [configId, value] of Object.entries(params.desiredConfigOptions)) try {
6080
- const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
6081
- applyConfigOptionsToRecord(params.record, response);
6082
- result = {
6083
- replayed: true,
6084
- models: modelStateFromConfigOptions(response.configOptions)
6085
- };
6086
- if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
6087
- } catch (error) {
6088
- throw new SessionConfigOptionReplayError(`Failed to replay saved session config option ${configId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
6089
- cause: error instanceof Error ? error : void 0,
6090
- retryable: true
6091
- });
6102
+ let acceptedConfigOptions = params.acceptedConfigOptions;
6103
+ const replacingKey = resolveReplacingConfigOptionId(params.replacingConfigOption, params.record);
6104
+ for (const [configId, value] of Object.entries(params.desiredConfigOptions)) {
6105
+ if (configId === replacingKey || acceptedConfigOptions && !acceptsSavedConfigValue(acceptedConfigOptions.find((option) => option.id === configId), value)) continue;
6106
+ try {
6107
+ const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
6108
+ params.record.acpx = applyConfigOptionSelection(params.record.acpx, configId, value, response);
6109
+ acceptedConfigOptions = response.configOptions;
6110
+ result = {
6111
+ replayed: true,
6112
+ models: modelStateFromConfigOptions(response.configOptions)
6113
+ };
6114
+ if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
6115
+ } catch (error) {
6116
+ throw new SessionConfigOptionReplayError(`Failed to replay saved session config option ${configId} on ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
6117
+ cause: error instanceof Error ? error : void 0,
6118
+ retryable: true
6119
+ });
6120
+ }
6092
6121
  }
6093
6122
  return result;
6094
6123
  }
@@ -6135,10 +6164,12 @@ async function connectAndLoadSession(options) {
6135
6164
  createdFreshSession = loadState.createdFreshSession;
6136
6165
  pendingAgentSessionId = loadState.pendingAgentSessionId;
6137
6166
  sessionModels = loadState.sessionModels;
6138
- const preferenceReplay = await replayFreshSessionPreferences({
6167
+ const preferenceReplay = await replaySessionPreferences({
6139
6168
  client,
6140
6169
  record,
6141
6170
  createdFreshSession,
6171
+ reusingLoadedSession,
6172
+ replacingConfigOption: options.replacingConfigOption,
6142
6173
  sessionId,
6143
6174
  pendingAgentSessionId,
6144
6175
  originalSessionId,
@@ -6169,7 +6200,7 @@ function preserveLegacyModels(models) {
6169
6200
  return models && !models.configId ? models : void 0;
6170
6201
  }
6171
6202
  function resolveConfigOptionsPresenceAfterReplay(replay, initiallyPresent) {
6172
- return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.replayed && replay.modelReplay.configOptionsPresent;
6203
+ return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.configOptions !== void 0;
6173
6204
  }
6174
6205
  function applyReconnectedModelState(record, sessionModels, configOptionsPresent, legacyModelMetadataPresent, createdFreshSession) {
6175
6206
  clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent);
@@ -6192,18 +6223,30 @@ function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbos
6192
6223
  }
6193
6224
  if (shouldReconnect) process.stderr.write(`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session reconnect\n`);
6194
6225
  }
6195
- async function replayFreshSessionPreferences(params) {
6196
- if (!params.createdFreshSession) return {
6226
+ function replacesModel(replacingConfigOption, models, originalAcpx) {
6227
+ const key = replacingConfigOption?.key;
6228
+ return key !== void 0 && (key === "model" || key === models?.configId || key === advertisedModelState(originalAcpx)?.configId);
6229
+ }
6230
+ function acceptsSavedConfigValue(option, value) {
6231
+ if (!option || option.type !== "select") return false;
6232
+ return option.currentValue === value || option.options.some((entry) => "options" in entry ? entry.options.some((choice) => choice.value === value) : entry.value === value);
6233
+ }
6234
+ function resolveReplacingConfigOptionId(replacement, record) {
6235
+ return replacement?.resolve?.(record) ?? replacement?.key;
6236
+ }
6237
+ async function replaySessionPreferences(params) {
6238
+ if (params.reusingLoadedSession) return {
6197
6239
  modelReplay: { replayed: false },
6198
6240
  configReplay: { replayed: false }
6199
6241
  };
6200
6242
  let modelReplay = { replayed: false };
6201
6243
  let configReplay = { replayed: false };
6244
+ const replacingModel = replacesModel(params.replacingConfigOption, params.sessionModels, params.originalAcpx);
6202
6245
  try {
6203
6246
  await replayDesiredMode({
6204
6247
  client: params.client,
6205
6248
  sessionId: params.sessionId,
6206
- desiredModeId: params.desiredModeId,
6249
+ desiredModeId: params.createdFreshSession ? params.desiredModeId : void 0,
6207
6250
  previousSessionId: params.originalSessionId,
6208
6251
  timeoutMs: params.timeoutMs,
6209
6252
  verbose: params.verbose
@@ -6211,10 +6254,11 @@ async function replayFreshSessionPreferences(params) {
6211
6254
  modelReplay = await replayDesiredModel({
6212
6255
  client: params.client,
6213
6256
  sessionId: params.sessionId,
6214
- desiredModelId: params.desiredModelId,
6257
+ desiredModelId: replacingModel ? void 0 : params.desiredModelId,
6215
6258
  previousSessionId: params.originalSessionId,
6216
6259
  record: params.record,
6217
6260
  models: params.sessionModels,
6261
+ createdFreshSession: params.createdFreshSession,
6218
6262
  timeoutMs: params.timeoutMs,
6219
6263
  verbose: params.verbose,
6220
6264
  suppressWarnings: params.suppressWarnings
@@ -6223,7 +6267,9 @@ async function replayFreshSessionPreferences(params) {
6223
6267
  client: params.client,
6224
6268
  record: params.record,
6225
6269
  sessionId: params.sessionId,
6226
- desiredConfigOptions: params.desiredConfigOptions,
6270
+ desiredConfigOptions: replacingModel ? {} : params.desiredConfigOptions,
6271
+ acceptedConfigOptions: modelReplay.configOptions,
6272
+ replacingConfigOption: replacingModel ? void 0 : params.replacingConfigOption,
6227
6273
  previousSessionId: params.originalSessionId,
6228
6274
  timeoutMs: params.timeoutMs,
6229
6275
  verbose: params.verbose
@@ -6379,6 +6425,7 @@ async function withConnectedSession(options) {
6379
6425
  client,
6380
6426
  record,
6381
6427
  resumePolicy: options.resumePolicy,
6428
+ replacingConfigOption: options.replacingConfigOption,
6382
6429
  timeoutMs: options.timeoutMs,
6383
6430
  verbose: options.verbose,
6384
6431
  activeController,
@@ -6437,7 +6484,7 @@ const SESSION_REPLY_IDLE_MS = 1e3;
6437
6484
  const SESSION_REPLY_DRAIN_TIMEOUT_MS = 5e3;
6438
6485
  async function runPromptTurn(params) {
6439
6486
  try {
6440
- const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestStarted, params.onElicitation);
6487
+ const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation);
6441
6488
  await params.onPromptStarted?.();
6442
6489
  const response = await withTimeout(promptPromise, params.timeoutMs);
6443
6490
  await params.client.waitForSessionUpdatesIdle?.({
@@ -6516,6 +6563,6 @@ var LiveSessionCheckpoint = class {
6516
6563
  }
6517
6564
  };
6518
6565
  //#endregion
6519
- export { writeSessionRecord as $, PERMISSION_POLICY_ACTIONS as $t, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as A, TimeoutError as At, getAcpxVersion as B, formatErrorMessage as Bt, mergeSessionOptions as C, PromptInputValidationError as Ct, applyLifecycleSnapshotToRecord as D, promptToDisplayText as Dt, applyConversation as E, parsePromptSource as Et, modelStateFromConfigOptions as F, normalizeAgentName$1 as Ft, findSession as G, toAcpErrorPayload as Gt, DEFAULT_HISTORY_LIMIT as H, normalizeOutputError as Ht, normalizeAgentCommandInput as I, resolveAgentArgv as It, listSessions as J, NON_INTERACTIVE_PERMISSION_POLICIES as Jt, findSessionByDirectoryWalk as K, AUTH_POLICIES as Kt, renderArgvIdentity as L, resolveAgentCommand as Lt, RequestedModelUnsupportedError as M, withTimeout as Mt, assertRequestedModelSupported as N, DEFAULT_AGENT_NAME as Nt, reconcileAgentSessionId as O, textPrompt as Ot, isRequestedModelUnsupportedError as P, listBuiltInAgents as Pt, resolveSessionRecord as Q, PERMISSION_MODES as Qt, runTimedExecFile as R, resolveCanonicalAgentName as Rt, advertisedModelState as S, parsePromptStopReason as St, sessionOptionsFromRecord as T, mergePromptSourceWithText as Tt, absolutePath as U, extractAcpError as Ut, permissionModeSatisfies as V, isRetryablePromptError as Vt, findGitRepositoryRoot as W, isAcpResourceNotFoundError as Wt, normalizeName as X, OUTPUT_ERROR_ORIGINS as Xt, listSessionsForAgent as Y, OUTPUT_ERROR_CODES as Yt, pruneSessions as Z, OUTPUT_FORMATS as Zt, createSessionConversation as _, sessionEventLockPath as _t, applyRequestedModelIfAdvertised as a, measurePerf as at, recordSessionUpdate as b, isAcpJsonRpcMessage as bt, setCurrentModelId as c, setPerfGauge as ct, setDesiredModelId as d, serializeSessionRecordForDisk as dt, SESSION_RECORD_SCHEMA as en, createAtomicWriteTempPath as et, syncAdvertisedModelState as f, normalizeRuntimeSessionId as ft, cloneSessionConversation as g, sessionEventActivePath as gt, cloneSessionAcpxState as h, sessionBaseDir$1 as ht, connectAndLoadSession as i, QueueProtocolError as in, incrementPerfCounter as it, REQUESTED_MODEL_UNSUPPORTED_REASONS as j, withInterrupt as jt, AcpClient as k, InterruptedError as kt, setDesiredConfigOption as l, startPerfTimer as lt, applyConfigOptionsToState as m, defaultSessionEventLog as mt, runPromptTurn as n, AgentSpawnError as nn, formatPerfMetric as nt, currentModelIdFromSetModelResponse as o, recordPerfDuration as ot, applyConfigOptionsToRecord as p, DEFAULT_EVENT_SEGMENT_MAX_BYTES as pt, isoNow$2 as q, EXIT_CODES as qt, withConnectedSession as r, QueueConnectionError as rn, getPerfMetricsSnapshot as rt, clearDesiredConfigOption as s, resetPerfMetrics as st, LiveSessionCheckpoint as t, AcpxOperationalError as tn, assertPersistedKeyPolicy as tt, setDesiredModeId as u, parseSessionRecord as ut, recordClientOperation as v, sessionEventSegmentPath as vt, persistSessionOptions as w, isPromptInput as wt, trimConversationForRuntime as x, parseJsonRpcErrorMessage as xt, recordPromptSubmission as y, extractSessionUpdateNotification as yt, splitCommandLine as z, exitCodeForOutputErrorCode as zt };
6566
+ 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 };
6520
6567
 
6521
- //# sourceMappingURL=live-checkpoint-BSIrfgVo.js.map
6568
+ //# sourceMappingURL=live-checkpoint-BW9JivEG.js.map