acpx 0.2.0 → 0.3.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.
@@ -1,7 +1,7 @@
1
- import { t as __exportAll } from "./rolldown-runtime-CjeV3_4I.js";
2
- import { n as isSessionUpdateNotification, t as isAcpJsonRpcMessage } from "./acp-jsonrpc-CGT_1Mel.js";
3
- import { B as ClaudeAcpSessionCreateTimeoutError, E as normalizeOutputError, G as QueueConnectionError, H as GeminiAcpStartupTimeoutError, I as promptToDisplayText, J as SessionResolutionError, K as SessionModeReplayError, L as textPrompt, M as extractAcpError, N as isAcpResourceNotFoundError, R as AgentSpawnError, S as startPerfTimer, T as isAcpQueryClosedBeforeResponseError, U as PermissionDeniedError, V as CopilotAcpUnsupportedError, W as PermissionPromptUnavailableError, _ as getPerfMetricsSnapshot, a as trySetConfigOptionOnRunningOwner, b as resetPerfMetrics, c as SessionQueueOwner, d as releaseQueueOwnerLease, f as terminateProcess, g as formatPerfMetric, h as waitMs$1, i as tryCancelOnRunningOwner, j as SESSION_RECORD_SCHEMA, l as isProcessAlive, m as tryAcquireQueueOwnerLease, o as trySetModeOnRunningOwner, p as terminateQueueOwnerForSession, q as SessionNotFoundError, s as trySubmitToRunningOwner, t as QUEUE_CONNECT_RETRY_MS, u as refreshQueueOwnerLease, v as incrementPerfCounter, w as formatErrorMessage, x as setPerfGauge, y as measurePerf, z as AuthPolicyError } from "./queue-ipc-C8StWiZt.js";
4
- import { n as normalizeRuntimeSessionId, t as extractRuntimeSessionId } from "./runtime-session-id-B03l5p1Q.js";
1
+ import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
+ import { B as CopilotAcpUnsupportedError, E as normalizeOutputError, F as promptToDisplayText, G as SessionModeReplayError, H as PermissionDeniedError, I as textPrompt, J as extractAcpError, K as SessionNotFoundError, L as AgentSpawnError, R as AuthPolicyError, S as startPerfTimer, T as isAcpQueryClosedBeforeResponseError, U as PermissionPromptUnavailableError, V as GeminiAcpStartupTimeoutError, W as QueueConnectionError, Y as isAcpResourceNotFoundError, _ as getPerfMetricsSnapshot, a as trySetConfigOptionOnRunningOwner, b as resetPerfMetrics, c as SessionQueueOwner, d as releaseQueueOwnerLease, f as terminateProcess, g as formatPerfMetric, h as waitMs$1, i as tryCancelOnRunningOwner, j as SESSION_RECORD_SCHEMA, l as isProcessAlive, m as tryAcquireQueueOwnerLease, o as trySetModeOnRunningOwner, p as terminateQueueOwnerForSession, q as SessionResolutionError, s as trySubmitToRunningOwner, u as refreshQueueOwnerLease, v as incrementPerfCounter, w as formatErrorMessage, x as setPerfGauge, y as measurePerf, z as ClaudeAcpSessionCreateTimeoutError } from "./queue-ipc-EQLpBMKv.js";
3
+ import { n as isSessionUpdateNotification, t as isAcpJsonRpcMessage } from "./acp-jsonrpc-BNHXq7qK.js";
4
+ import { n as normalizeRuntimeSessionId, t as extractRuntimeSessionId } from "./runtime-session-id-C544sPPL.js";
5
5
  import fs, { realpathSync, statSync } from "node:fs";
6
6
  import fs$1 from "node:fs/promises";
7
7
  import path from "node:path";
@@ -11,7 +11,6 @@ import { ClientSideConnection, PROTOCOL_VERSION, ndJsonStream } from "@agentclie
11
11
  import readline from "node:readline/promises";
12
12
  import { randomUUID } from "node:crypto";
13
13
  import os from "node:os";
14
-
15
14
  //#region src/permission-prompt.ts
16
15
  async function promptForPermission(options) {
17
16
  if (!process.stdin.isTTY || !process.stderr.isTTY) return false;
@@ -28,7 +27,6 @@ async function promptForPermission(options) {
28
27
  rl.close();
29
28
  }
30
29
  }
31
-
32
30
  //#endregion
33
31
  //#region src/filesystem.ts
34
32
  const WRITE_PREVIEW_MAX_LINES = 16;
@@ -176,7 +174,6 @@ var FileSystemHandlers = class {
176
174
  this.onOperation?.(operation);
177
175
  }
178
176
  };
179
-
180
177
  //#endregion
181
178
  //#region src/permissions.ts
182
179
  function selected(optionId) {
@@ -251,7 +248,6 @@ function classifyPermissionDecision(params, response) {
251
248
  if (selectedOption.kind === "allow_once" || selectedOption.kind === "allow_always") return "approved";
252
249
  return "denied";
253
250
  }
254
-
255
251
  //#endregion
256
252
  //#region src/session-runtime-helpers.ts
257
253
  var TimeoutError = class extends Error {
@@ -305,7 +301,6 @@ async function withInterrupt(run, onInterrupt) {
305
301
  run().then((result) => finish(() => resolve(result)), (error) => finish(() => reject(error)));
306
302
  });
307
303
  }
308
-
309
304
  //#endregion
310
305
  //#region src/terminal.ts
311
306
  const DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES = 64 * 1024;
@@ -602,7 +597,6 @@ var TerminalManager = class {
602
597
  await Promise.race([terminal.exitPromise.then(() => void 0), waitMs(this.killGraceMs)]);
603
598
  }
604
599
  };
605
-
606
600
  //#endregion
607
601
  //#region src/client.ts
608
602
  const REPLAY_IDLE_MS = 80;
@@ -614,7 +608,13 @@ const AGENT_CLOSE_KILL_GRACE_MS = 1e3;
614
608
  const GEMINI_ACP_STARTUP_TIMEOUT_MS = 15e3;
615
609
  const CLAUDE_ACP_SESSION_CREATE_TIMEOUT_MS = 6e4;
616
610
  const GEMINI_VERSION_TIMEOUT_MS = 2e3;
611
+ const GEMINI_ACP_FLAG_VERSION = [
612
+ 0,
613
+ 33,
614
+ 0
615
+ ];
617
616
  const COPILOT_HELP_TIMEOUT_MS = 2e3;
617
+ const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = new Set([-32601, -32602]);
618
618
  function shouldSuppressSdkConsoleError(args) {
619
619
  if (args.length === 0) return false;
620
620
  return typeof args[0] === "string" && args[0] === "Error handling request";
@@ -724,7 +724,7 @@ function basenameToken(value) {
724
724
  return path.basename(value).toLowerCase().replace(/\.(cmd|exe|bat)$/u, "");
725
725
  }
726
726
  function isGeminiAcpCommand(command, args) {
727
- return basenameToken(command) === "gemini" && args.includes("--experimental-acp");
727
+ return basenameToken(command) === "gemini" && (args.includes("--acp") || args.includes("--experimental-acp"));
728
728
  }
729
729
  function isClaudeAcpCommand(command, args) {
730
730
  if (basenameToken(command) === "claude-agent-acp") return true;
@@ -733,6 +733,38 @@ function isClaudeAcpCommand(command, args) {
733
733
  function isCopilotAcpCommand(command, args) {
734
734
  return basenameToken(command) === "copilot" && args.includes("--acp");
735
735
  }
736
+ function readWindowsEnvValue(env, key) {
737
+ const matchedKey = Object.keys(env).find((entry) => entry.toUpperCase() === key);
738
+ return matchedKey ? env[matchedKey] : void 0;
739
+ }
740
+ function resolveWindowsCommand(command, env = process.env) {
741
+ const extensions = (readWindowsEnvValue(env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").map((value) => value.trim().toLowerCase()).filter((value) => value.length > 0);
742
+ const candidates = path.extname(command).length > 0 ? [command] : extensions.map((extension) => `${command}${extension}`);
743
+ if (command.includes("/") || command.includes("\\") || path.isAbsolute(command)) return candidates.find((candidate) => fs.existsSync(candidate));
744
+ const pathValue = readWindowsEnvValue(env, "PATH");
745
+ if (!pathValue) return;
746
+ for (const directory of pathValue.split(";")) {
747
+ const trimmedDirectory = directory.trim();
748
+ if (trimmedDirectory.length === 0) continue;
749
+ for (const candidate of candidates) {
750
+ const resolved = path.join(trimmedDirectory, candidate);
751
+ if (fs.existsSync(resolved)) return resolved;
752
+ }
753
+ }
754
+ }
755
+ function shouldUseWindowsBatchShell(command, platform = process.platform, env = process.env) {
756
+ if (platform !== "win32") return false;
757
+ const resolvedCommand = resolveWindowsCommand(command, env) ?? command;
758
+ const ext = path.extname(resolvedCommand).toLowerCase();
759
+ return ext === ".cmd" || ext === ".bat";
760
+ }
761
+ function buildSpawnCommandOptions(command, options, platform = process.platform, env = process.env) {
762
+ if (!shouldUseWindowsBatchShell(command, platform, env)) return options;
763
+ return {
764
+ ...options,
765
+ shell: true
766
+ };
767
+ }
736
768
  function resolveGeminiAcpStartupTimeoutMs() {
737
769
  const raw = process.env.ACPX_GEMINI_ACP_STARTUP_TIMEOUT_MS;
738
770
  if (typeof raw === "string" && raw.trim().length > 0) {
@@ -749,16 +781,38 @@ function resolveClaudeAcpSessionCreateTimeoutMs() {
749
781
  }
750
782
  return CLAUDE_ACP_SESSION_CREATE_TIMEOUT_MS;
751
783
  }
784
+ function parseGeminiVersion(value) {
785
+ if (typeof value !== "string") return;
786
+ const normalized = value.trim();
787
+ const match = normalized.match(/(\d+)\.(\d+)\.(\d+)/);
788
+ if (!match) return;
789
+ return {
790
+ raw: normalized,
791
+ parts: [
792
+ Number(match[1]),
793
+ Number(match[2]),
794
+ Number(match[3])
795
+ ]
796
+ };
797
+ }
798
+ function compareVersionParts(left, right) {
799
+ for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
800
+ const leftPart = left[index] ?? 0;
801
+ const rightPart = right[index] ?? 0;
802
+ if (leftPart !== rightPart) return leftPart - rightPart;
803
+ }
804
+ return 0;
805
+ }
752
806
  async function detectGeminiVersion(command) {
753
807
  return await new Promise((resolve) => {
754
- const child = spawn(command, ["--version"], {
808
+ const child = spawn(command, ["--version"], buildSpawnCommandOptions(command, {
755
809
  stdio: [
756
810
  "ignore",
757
811
  "pipe",
758
812
  "pipe"
759
813
  ],
760
814
  windowsHide: true
761
- });
815
+ }));
762
816
  let stdout = "";
763
817
  let stderr = "";
764
818
  let settled = false;
@@ -787,20 +841,26 @@ async function detectGeminiVersion(command) {
787
841
  finish(void 0);
788
842
  });
789
843
  child.once("close", () => {
790
- finish(`${stdout}\n${stderr}`.split(/\r?\n/).map((line) => line.trim()).find((line) => /^\d+\.\d+\.\d+/.test(line)));
844
+ finish(parseGeminiVersion(`${stdout}\n${stderr}`.split(/\r?\n/).map((line) => line.trim()).find((line) => /\d+\.\d+\.\d+/.test(line))));
791
845
  });
792
846
  });
793
847
  }
848
+ async function resolveGeminiCommandArgs(command, args) {
849
+ if (basenameToken(command) !== "gemini" || !args.includes("--acp")) return [...args];
850
+ const version = await detectGeminiVersion(command);
851
+ if (version && compareVersionParts(version.parts, GEMINI_ACP_FLAG_VERSION) < 0) return args.map((arg) => arg === "--acp" ? "--experimental-acp" : arg);
852
+ return [...args];
853
+ }
794
854
  async function readCommandOutput(command, args, timeoutMs) {
795
855
  return await new Promise((resolve) => {
796
- const child = spawn(command, [...args], {
856
+ const child = spawn(command, [...args], buildSpawnCommandOptions(command, {
797
857
  stdio: [
798
858
  "ignore",
799
859
  "pipe",
800
860
  "pipe"
801
861
  ],
802
862
  windowsHide: true
803
- });
863
+ }));
804
864
  let stdout = "";
805
865
  let stderr = "";
806
866
  let settled = false;
@@ -836,7 +896,7 @@ async function readCommandOutput(command, args, timeoutMs) {
836
896
  async function buildGeminiAcpStartupTimeoutMessage(command) {
837
897
  const parts = ["Gemini CLI ACP startup timed out before initialize completed.", "This usually means the local Gemini CLI is waiting on interactive OAuth or has incompatible ACP subprocess behavior."];
838
898
  const version = await detectGeminiVersion(command);
839
- if (version) parts.push(`Detected Gemini CLI version: ${version}.`);
899
+ if (version) parts.push(`Detected Gemini CLI version: ${version.raw}.`);
840
900
  if (!process.env.GEMINI_API_KEY && !process.env.GOOGLE_API_KEY) parts.push("No GEMINI_API_KEY or GOOGLE_API_KEY was set for non-interactive auth.");
841
901
  parts.push("Try upgrading Gemini CLI and using API-key-based auth for non-interactive ACP runs.");
842
902
  return parts.join(" ");
@@ -886,6 +946,30 @@ function buildClaudeCodeOptionsMeta(options) {
886
946
  if (Object.keys(claudeCodeOptions).length === 0) return;
887
947
  return { claudeCode: { options: claudeCodeOptions } };
888
948
  }
949
+ function asRecord$3(value) {
950
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
951
+ return value;
952
+ }
953
+ function isLikelySessionControlUnsupportedError(acp) {
954
+ if (SESSION_CONTROL_UNSUPPORTED_ACP_CODES.has(acp.code)) return true;
955
+ if (acp.code !== -32603) return false;
956
+ const details = asRecord$3(acp.data)?.details;
957
+ return typeof details === "string" && details.toLowerCase().includes("invalid params");
958
+ }
959
+ function formatSessionControlAcpSummary(acp) {
960
+ const details = asRecord$3(acp.data)?.details;
961
+ if (typeof details === "string" && details.trim().length > 0) return `${details.trim()} (ACP ${acp.code}, adapter reported "${acp.message}")`;
962
+ return `${acp.message} (ACP ${acp.code})`;
963
+ }
964
+ function maybeWrapSessionControlError(method, error, context) {
965
+ const acp = extractAcpError(error);
966
+ if (!acp || !isLikelySessionControlUnsupportedError(acp)) return error;
967
+ const acpSummary = formatSessionControlAcpSummary(acp);
968
+ const message = `Agent rejected ${method}${context ? ` ${context}` : ""}: ${acpSummary}. The adapter may not implement ${method}, or the requested value is not supported.`;
969
+ const wrapped = new Error(message, { cause: error instanceof Error ? error : void 0 });
970
+ wrapped.acp = acp;
971
+ return wrapped;
972
+ }
889
973
  function buildAgentEnvironment(authCredentials) {
890
974
  const env = { ...process.env };
891
975
  if (!authCredentials) return env;
@@ -1018,11 +1102,12 @@ var AcpClient = class {
1018
1102
  async start() {
1019
1103
  if (this.connection && this.agent && isChildProcessRunning(this.agent)) return;
1020
1104
  if (this.connection || this.agent) await this.close();
1021
- const { command, args } = splitCommandLine(this.options.agentCommand);
1105
+ const { command, args: initialArgs } = splitCommandLine(this.options.agentCommand);
1106
+ const args = await resolveGeminiCommandArgs(command, initialArgs);
1022
1107
  this.log(`spawning agent: ${command} ${args.join(" ")}`);
1023
1108
  const geminiAcp = isGeminiAcpCommand(command, args);
1024
1109
  if (isCopilotAcpCommand(command, args)) await ensureCopilotAcpSupport(command);
1025
- const spawnedChild = spawn(command, args, buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials));
1110
+ const spawnedChild = spawn(command, args, buildSpawnCommandOptions(command, buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials)));
1026
1111
  try {
1027
1112
  await waitForSpawn(spawnedChild);
1028
1113
  } catch (error) {
@@ -1223,17 +1308,27 @@ var AcpClient = class {
1223
1308
  }
1224
1309
  }
1225
1310
  async setSessionMode(sessionId, modeId) {
1226
- await this.getConnection().setSessionMode({
1227
- sessionId,
1228
- modeId
1229
- });
1311
+ const connection = this.getConnection();
1312
+ try {
1313
+ await connection.setSessionMode({
1314
+ sessionId,
1315
+ modeId
1316
+ });
1317
+ } catch (error) {
1318
+ throw maybeWrapSessionControlError("session/set_mode", error, `for mode "${modeId}"`);
1319
+ }
1230
1320
  }
1231
1321
  async setSessionConfigOption(sessionId, configId, value) {
1232
- return await this.getConnection().setSessionConfigOption({
1233
- sessionId,
1234
- configId,
1235
- value
1236
- });
1322
+ const connection = this.getConnection();
1323
+ try {
1324
+ return await connection.setSessionConfigOption({
1325
+ sessionId,
1326
+ configId,
1327
+ value
1328
+ });
1329
+ } catch (error) {
1330
+ throw maybeWrapSessionControlError("session/set_config_option", error, `for "${configId}"="${value}"`);
1331
+ }
1237
1332
  }
1238
1333
  async cancel(sessionId) {
1239
1334
  const connection = this.getConnection();
@@ -1359,17 +1454,13 @@ var AcpClient = class {
1359
1454
  } catch (error) {
1360
1455
  if (error instanceof PermissionPromptUnavailableError) {
1361
1456
  this.notePromptPermissionFailure(params.sessionId, error);
1362
- this.permissionStats.requested += 1;
1363
- this.permissionStats.cancelled += 1;
1457
+ this.recordPermissionDecision("cancelled");
1364
1458
  return { outcome: { outcome: "cancelled" } };
1365
1459
  }
1366
1460
  throw error;
1367
1461
  }
1368
1462
  const decision = classifyPermissionDecision(params, response);
1369
- this.permissionStats.requested += 1;
1370
- if (decision === "approved") this.permissionStats.approved += 1;
1371
- else if (decision === "denied") this.permissionStats.denied += 1;
1372
- else this.permissionStats.cancelled += 1;
1463
+ this.recordPermissionDecision(decision);
1373
1464
  return response;
1374
1465
  }
1375
1466
  attachAgentLifecycleObservers(child) {
@@ -1402,13 +1493,18 @@ var AcpClient = class {
1402
1493
  return error;
1403
1494
  }
1404
1495
  async handleReadTextFile(params) {
1405
- return await this.filesystem.readTextFile(params);
1496
+ try {
1497
+ return await this.filesystem.readTextFile(params);
1498
+ } catch (error) {
1499
+ this.recordPermissionError(params.sessionId, error);
1500
+ throw error;
1501
+ }
1406
1502
  }
1407
1503
  async handleWriteTextFile(params) {
1408
1504
  try {
1409
1505
  return await this.filesystem.writeTextFile(params);
1410
1506
  } catch (error) {
1411
- if (error instanceof PermissionPromptUnavailableError) this.notePromptPermissionFailure(params.sessionId, error);
1507
+ this.recordPermissionError(params.sessionId, error);
1412
1508
  throw error;
1413
1509
  }
1414
1510
  }
@@ -1416,7 +1512,7 @@ var AcpClient = class {
1416
1512
  try {
1417
1513
  return await this.terminalManager.createTerminal(params);
1418
1514
  } catch (error) {
1419
- if (error instanceof PermissionPromptUnavailableError) this.notePromptPermissionFailure(params.sessionId, error);
1515
+ this.recordPermissionError(params.sessionId, error);
1420
1516
  throw error;
1421
1517
  }
1422
1518
  }
@@ -1432,6 +1528,26 @@ var AcpClient = class {
1432
1528
  async handleReleaseTerminal(params) {
1433
1529
  return await this.terminalManager.releaseTerminal(params);
1434
1530
  }
1531
+ recordPermissionDecision(decision) {
1532
+ this.permissionStats.requested += 1;
1533
+ if (decision === "approved") {
1534
+ this.permissionStats.approved += 1;
1535
+ return;
1536
+ }
1537
+ if (decision === "denied") {
1538
+ this.permissionStats.denied += 1;
1539
+ return;
1540
+ }
1541
+ this.permissionStats.cancelled += 1;
1542
+ }
1543
+ recordPermissionError(sessionId, error) {
1544
+ if (error instanceof PermissionPromptUnavailableError) {
1545
+ this.notePromptPermissionFailure(sessionId, error);
1546
+ this.recordPermissionDecision("cancelled");
1547
+ return;
1548
+ }
1549
+ if (error instanceof PermissionDeniedError) this.recordPermissionDecision("denied");
1550
+ }
1435
1551
  async handleSessionUpdate(notification) {
1436
1552
  const sequence = ++this.observedSessionUpdates;
1437
1553
  this.sessionUpdateChain = this.sessionUpdateChain.then(async () => {
@@ -1469,7 +1585,6 @@ var AcpClient = class {
1469
1585
  throw new Error(`Timed out waiting for session replay drain after ${normalizedTimeoutMs}ms`);
1470
1586
  }
1471
1587
  };
1472
-
1473
1588
  //#endregion
1474
1589
  //#region src/perf-metrics-capture.ts
1475
1590
  const PERF_METRICS_FILE_ENV = "ACPX_PERF_METRICS_FILE";
@@ -1541,7 +1656,6 @@ function installPerfMetricsCapture(options = {}) {
1541
1656
  process.once(signal, handler);
1542
1657
  }
1543
1658
  }
1544
-
1545
1659
  //#endregion
1546
1660
  //#region src/session-conversation-model.ts
1547
1661
  const MAX_RUNTIME_MESSAGES = 200;
@@ -1892,11 +2006,9 @@ function trimConversationForRuntime(conversation) {
1892
2006
  const requestUsageEntries = Object.entries(conversation.request_token_usage);
1893
2007
  if (requestUsageEntries.length > MAX_RUNTIME_REQUEST_TOKEN_USAGE) conversation.request_token_usage = Object.fromEntries(requestUsageEntries.slice(-MAX_RUNTIME_REQUEST_TOKEN_USAGE));
1894
2008
  }
1895
-
1896
2009
  //#endregion
1897
2010
  //#region src/session-event-log.ts
1898
2011
  const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 64 * 1024 * 1024;
1899
- const DEFAULT_EVENT_MAX_SEGMENTS = 5;
1900
2012
  function sessionBaseDir$1() {
1901
2013
  return path.join(os.homedir(), ".acpx", "sessions");
1902
2014
  }
@@ -1915,14 +2027,13 @@ function sessionEventLockPath(sessionId) {
1915
2027
  function defaultSessionEventLog(sessionId) {
1916
2028
  return {
1917
2029
  active_path: sessionEventActivePath(sessionId),
1918
- segment_count: DEFAULT_EVENT_MAX_SEGMENTS,
2030
+ segment_count: 5,
1919
2031
  max_segment_bytes: DEFAULT_EVENT_SEGMENT_MAX_BYTES,
1920
- max_segments: DEFAULT_EVENT_MAX_SEGMENTS,
2032
+ max_segments: 5,
1921
2033
  last_write_at: void 0,
1922
2034
  last_write_error: null
1923
2035
  };
1924
2036
  }
1925
-
1926
2037
  //#endregion
1927
2038
  //#region src/session-persistence/serialize.ts
1928
2039
  function serializeSessionRecordForDisk(record) {
@@ -1962,7 +2073,6 @@ function serializeSessionRecordForDisk(record) {
1962
2073
  acpx: canonical.acpx
1963
2074
  };
1964
2075
  }
1965
-
1966
2076
  //#endregion
1967
2077
  //#region src/persisted-key-policy.ts
1968
2078
  const SNAKE_CASE_KEY = /^[a-z][a-z0-9_]*$/;
@@ -2029,7 +2139,6 @@ function assertPersistedKeyPolicy(value) {
2029
2139
  if (violations.length === 0) return;
2030
2140
  throw new Error(`Persisted key policy violation (expected snake_case keys): ${violations.join(", ")}`);
2031
2141
  }
2032
-
2033
2142
  //#endregion
2034
2143
  //#region src/session-persistence/parse.ts
2035
2144
  function asRecord$1(value) {
@@ -2206,7 +2315,7 @@ function normalizeOptionalSignal(value) {
2206
2315
  function parseSessionRecord(raw) {
2207
2316
  const record = asRecord$1(raw);
2208
2317
  if (!record) return null;
2209
- if (record.schema !== SESSION_RECORD_SCHEMA) return null;
2318
+ if (record.schema !== "acpx.session.v1") return null;
2210
2319
  const name = normalizeOptionalName(record.name);
2211
2320
  const pid = normalizeOptionalPid(record.pid);
2212
2321
  const closed = normalizeOptionalBoolean(record.closed, false);
@@ -2255,7 +2364,6 @@ function parseSessionRecord(raw) {
2255
2364
  acpx: parseAcpxState(record.acpx)
2256
2365
  };
2257
2366
  }
2258
-
2259
2367
  //#endregion
2260
2368
  //#region src/session-persistence/index.ts
2261
2369
  const SESSION_INDEX_SCHEMA = "acpx.session-index.v1";
@@ -2347,7 +2455,6 @@ async function loadOrRebuildSessionIndex(sessionDir) {
2347
2455
  if (existing && existing.files.length === files.length && existing.files.every((file, index) => file === files[index])) return existing;
2348
2456
  return await rebuildSessionIndex(sessionDir);
2349
2457
  }
2350
-
2351
2458
  //#endregion
2352
2459
  //#region src/session-persistence/repository.ts
2353
2460
  const DEFAULT_HISTORY_LIMIT = 20;
@@ -2498,7 +2605,6 @@ async function findSessionByDirectoryWalk(options) {
2498
2605
  if (!isWithinBoundary(walkBoundary, current)) return;
2499
2606
  }
2500
2607
  }
2501
-
2502
2608
  //#endregion
2503
2609
  //#region src/session-events.ts
2504
2610
  const LOCK_RETRY_MS = 15;
@@ -2614,8 +2720,8 @@ var SessionEventWriter = class SessionEventWriter {
2614
2720
  }
2615
2721
  static async open(record, options = {}) {
2616
2722
  const lock = await acquireEventsLock(record.acpxRecordId);
2617
- const maxSegmentBytes = options.maxSegmentBytes ?? record.eventLog.max_segment_bytes ?? DEFAULT_EVENT_SEGMENT_MAX_BYTES;
2618
- const maxSegments = options.maxSegments ?? record.eventLog.max_segments ?? DEFAULT_EVENT_MAX_SEGMENTS;
2723
+ const maxSegmentBytes = options.maxSegmentBytes ?? record.eventLog.max_segment_bytes ?? 67108864;
2724
+ const maxSegments = options.maxSegments ?? record.eventLog.max_segments ?? 5;
2619
2725
  const activePath = sessionEventActivePath(record.acpxRecordId);
2620
2726
  const activeSizeBytes = await statSize(activePath);
2621
2727
  const segmentCount = Number.isInteger(record.eventLog.segment_count) && record.eventLog.segment_count > 0 ? record.eventLog.segment_count : await countExistingSegments(record.acpxRecordId, maxSegments) || 1;
@@ -2685,7 +2791,6 @@ var SessionEventWriter = class SessionEventWriter {
2685
2791
  }
2686
2792
  }
2687
2793
  };
2688
-
2689
2794
  //#endregion
2690
2795
  //#region src/queue-owner-turn-controller.ts
2691
2796
  var QueueOwnerTurnController = class {
@@ -2767,7 +2872,6 @@ var QueueOwnerTurnController = class {
2767
2872
  return await this.options.setSessionConfigOptionFallback(configId, value, timeoutMs);
2768
2873
  }
2769
2874
  };
2770
-
2771
2875
  //#endregion
2772
2876
  //#region src/session-mode-preference.ts
2773
2877
  function ensureAcpxState(state) {
@@ -2788,7 +2892,6 @@ function setDesiredModeId(record, modeId) {
2788
2892
  else delete acpx.desired_mode_id;
2789
2893
  record.acpx = acpx;
2790
2894
  }
2791
-
2792
2895
  //#endregion
2793
2896
  //#region src/session-runtime/lifecycle.ts
2794
2897
  function applyLifecycleSnapshotToRecord(record, snapshot) {
@@ -2821,7 +2924,6 @@ function applyConversation(record, conversation) {
2821
2924
  record.cumulative_token_usage = conversation.cumulative_token_usage;
2822
2925
  record.request_token_usage = conversation.request_token_usage;
2823
2926
  }
2824
-
2825
2927
  //#endregion
2826
2928
  //#region src/session-runtime/connect-load.ts
2827
2929
  function shouldFallbackToNewSession(error, record) {
@@ -2901,7 +3003,6 @@ async function connectAndLoadSession(options) {
2901
3003
  loadError
2902
3004
  };
2903
3005
  }
2904
-
2905
3006
  //#endregion
2906
3007
  //#region src/session-runtime/prompt-runner.ts
2907
3008
  async function withConnectedSession(options) {
@@ -3018,7 +3119,6 @@ async function runSessionSetConfigOptionDirect(options) {
3018
3119
  loadError: result.loadError
3019
3120
  };
3020
3121
  }
3021
-
3022
3122
  //#endregion
3023
3123
  //#region src/session-runtime/queue-owner-process.ts
3024
3124
  function resolveQueueOwnerSpawnArgs(argv = process.argv) {
@@ -3055,7 +3155,6 @@ function spawnQueueOwnerProcess(options) {
3055
3155
  const payload = JSON.stringify(options);
3056
3156
  spawn(process.execPath, resolveQueueOwnerSpawnArgs(), buildQueueOwnerSpawnOptions(payload)).unref();
3057
3157
  }
3058
-
3059
3158
  //#endregion
3060
3159
  //#region src/session-runtime.ts
3061
3160
  const DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
@@ -3104,6 +3203,53 @@ const DISCARD_OUTPUT_FORMATTER = {
3104
3203
  onError() {},
3105
3204
  flush() {}
3106
3205
  };
3206
+ function jsonRpcIdKey(value) {
3207
+ if (typeof value === "string") return `s:${value}`;
3208
+ if (typeof value === "number" && Number.isFinite(value)) return `n:${value}`;
3209
+ }
3210
+ function extractJsonRpcRequestInfo(message) {
3211
+ const candidate = message;
3212
+ if (typeof candidate.method !== "string") return;
3213
+ const idKey = jsonRpcIdKey(candidate.id);
3214
+ if (!idKey) return;
3215
+ return {
3216
+ idKey,
3217
+ method: candidate.method
3218
+ };
3219
+ }
3220
+ function extractJsonRpcResponseInfo(message) {
3221
+ const candidate = message;
3222
+ const idKey = jsonRpcIdKey(candidate.id);
3223
+ if (!idKey) return;
3224
+ const hasError = Object.hasOwn(candidate, "error");
3225
+ if (!hasError && !Object.hasOwn(candidate, "result")) return;
3226
+ return {
3227
+ idKey,
3228
+ hasError
3229
+ };
3230
+ }
3231
+ function filterRecoverableLoadFallbackOutput(messages) {
3232
+ const requestMethodById = /* @__PURE__ */ new Map();
3233
+ const failedLoadRequestIds = /* @__PURE__ */ new Set();
3234
+ for (const message of messages) {
3235
+ const request = extractJsonRpcRequestInfo(message);
3236
+ if (request) {
3237
+ requestMethodById.set(request.idKey, request.method);
3238
+ continue;
3239
+ }
3240
+ const response = extractJsonRpcResponseInfo(message);
3241
+ if (!response || !response.hasError) continue;
3242
+ if (requestMethodById.get(response.idKey) === "session/load") failedLoadRequestIds.add(response.idKey);
3243
+ }
3244
+ if (failedLoadRequestIds.size === 0) return messages;
3245
+ return messages.filter((message) => {
3246
+ const request = extractJsonRpcRequestInfo(message);
3247
+ if (request && request.method === "session/load" && failedLoadRequestIds.has(request.idKey)) return false;
3248
+ const response = extractJsonRpcResponseInfo(message);
3249
+ if (response && failedLoadRequestIds.has(response.idKey)) return false;
3250
+ return true;
3251
+ });
3252
+ }
3107
3253
  function normalizeQueueOwnerTtlMs(ttlMs) {
3108
3254
  if (ttlMs == null) return DEFAULT_QUEUE_OWNER_TTL_MS;
3109
3255
  if (!Number.isFinite(ttlMs) || ttlMs < 0) return DEFAULT_QUEUE_OWNER_TTL_MS;
@@ -3170,6 +3316,8 @@ async function runSessionPrompt(options) {
3170
3316
  return await SessionEventWriter.open(record);
3171
3317
  });
3172
3318
  const pendingMessages = [];
3319
+ const pendingConnectOutputMessages = [];
3320
+ let bufferingConnectOutput = true;
3173
3321
  let sawAcpMessage = false;
3174
3322
  let eventWriterClosed = false;
3175
3323
  const closeEventWriter = async (checkpoint) => {
@@ -3208,6 +3356,10 @@ async function runSessionPrompt(options) {
3208
3356
  pendingMessages.push(message);
3209
3357
  },
3210
3358
  onAcpOutputMessage: (_direction, message) => {
3359
+ if (bufferingConnectOutput) {
3360
+ pendingConnectOutputMessages.push(message);
3361
+ return;
3362
+ }
3211
3363
  output.onAcpMessage(message);
3212
3364
  },
3213
3365
  onSessionUpdate: (notification) => {
@@ -3234,23 +3386,36 @@ async function runSessionPrompt(options) {
3234
3386
  try {
3235
3387
  return await withInterrupt(async () => {
3236
3388
  const connectStartedAt = Date.now();
3237
- const { sessionId: activeSessionId, resumed, loadError } = await measurePerf("runtime.connect_and_load", async () => await connectAndLoadSession({
3238
- client,
3239
- record,
3240
- timeoutMs: options.timeoutMs,
3241
- verbose: options.verbose,
3242
- activeController,
3243
- onClientAvailable: (controller) => {
3244
- options.onClientAvailable?.(controller);
3245
- notifiedClientAvailable = true;
3246
- },
3247
- onConnectedRecord: (connectedRecord) => {
3248
- connectedRecord.lastPromptAt = isoNow();
3249
- },
3250
- onSessionIdResolved: (sessionId) => {
3251
- activeSessionIdForControl = sessionId;
3389
+ const { sessionId: activeSessionId, resumed, loadError } = await measurePerf("runtime.connect_and_load", async () => {
3390
+ try {
3391
+ return await connectAndLoadSession({
3392
+ client,
3393
+ record,
3394
+ timeoutMs: options.timeoutMs,
3395
+ verbose: options.verbose,
3396
+ activeController,
3397
+ onClientAvailable: (controller) => {
3398
+ options.onClientAvailable?.(controller);
3399
+ notifiedClientAvailable = true;
3400
+ },
3401
+ onConnectedRecord: (connectedRecord) => {
3402
+ connectedRecord.lastPromptAt = isoNow();
3403
+ },
3404
+ onSessionIdResolved: (sessionId) => {
3405
+ activeSessionIdForControl = sessionId;
3406
+ }
3407
+ });
3408
+ } catch (error) {
3409
+ bufferingConnectOutput = false;
3410
+ for (const message of pendingConnectOutputMessages) output.onAcpMessage(message);
3411
+ pendingConnectOutputMessages.length = 0;
3412
+ throw error;
3252
3413
  }
3253
- }));
3414
+ });
3415
+ bufferingConnectOutput = false;
3416
+ const connectOutputMessages = loadError == null ? pendingConnectOutputMessages : filterRecoverableLoadFallbackOutput(pendingConnectOutputMessages);
3417
+ for (const message of connectOutputMessages) output.onAcpMessage(message);
3418
+ pendingConnectOutputMessages.length = 0;
3254
3419
  if (options.verbose) process.stderr.write(`[acpx] ${formatPerfMetric("prompt.connect_and_load", Date.now() - connectStartedAt)}\n`);
3255
3420
  output.setContext({ sessionId: record.acpxRecordId });
3256
3421
  await flushPendingMessages(false);
@@ -3619,7 +3784,7 @@ async function sendSession(options) {
3619
3784
  for (let attempt = 0; attempt < QUEUE_OWNER_STARTUP_MAX_ATTEMPTS; attempt += 1) {
3620
3785
  const queued = await submitToRunningOwner(options, waitForCompletion);
3621
3786
  if (queued) return queued;
3622
- await waitMs$1(QUEUE_CONNECT_RETRY_MS);
3787
+ await waitMs$1(50);
3623
3788
  }
3624
3789
  throw new Error(`Session queue owner failed to start for session ${options.sessionId}`);
3625
3790
  }
@@ -3707,11 +3872,10 @@ async function closeSession(sessionId) {
3707
3872
  await writeSessionRecord(record);
3708
3873
  return record;
3709
3874
  }
3710
-
3711
3875
  //#endregion
3712
3876
  //#region src/session.ts
3713
3877
  var session_exports = /* @__PURE__ */ __exportAll({
3714
- DEFAULT_HISTORY_LIMIT: () => DEFAULT_HISTORY_LIMIT,
3878
+ DEFAULT_HISTORY_LIMIT: () => 20,
3715
3879
  DEFAULT_QUEUE_OWNER_TTL_MS: () => DEFAULT_QUEUE_OWNER_TTL_MS,
3716
3880
  InterruptedError: () => InterruptedError,
3717
3881
  TimeoutError: () => TimeoutError,
@@ -3732,7 +3896,7 @@ var session_exports = /* @__PURE__ */ __exportAll({
3732
3896
  setSessionConfigOption: () => setSessionConfigOption,
3733
3897
  setSessionMode: () => setSessionMode
3734
3898
  });
3735
-
3736
3899
  //#endregion
3737
3900
  export { findGitRepositoryRoot as a, flushPerfMetricsCapture as c, DEFAULT_HISTORY_LIMIT as i, installPerfMetricsCapture as l, DEFAULT_QUEUE_OWNER_TTL_MS as n, findSession as o, runSessionQueueOwner as r, findSessionByDirectoryWalk as s, session_exports as t, InterruptedError as u };
3738
- //# sourceMappingURL=session-C6nyqSfk.js.map
3901
+
3902
+ //# sourceMappingURL=session-C2Q8ktsN.js.map