acpx 0.1.16 → 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 PermissionPromptUnavailableError, D as isAcpResourceNotFoundError, E as isAcpQueryClosedBeforeResponseError, F as AuthPolicyError, H as SessionModeReplayError, I as ClaudeAcpSessionCreateTimeoutError, L as CopilotAcpUnsupportedError, N as SESSION_RECORD_SCHEMA, O as normalizeOutputError, P as AgentSpawnError, R as GeminiAcpStartupTimeoutError, S as startPerfTimer, T as formatErrorMessage, U as SessionNotFoundError, V as QueueConnectionError, W as SessionResolutionError, _ 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, l as isProcessAlive, m as tryAcquireQueueOwnerLease, o as trySetModeOnRunningOwner, p as terminateQueueOwnerForSession, s as trySubmitToRunningOwner, t as QUEUE_CONNECT_RETRY_MS, u as refreshQueueOwnerLease, v as incrementPerfCounter, w as extractAcpError, x as setPerfGauge, y as measurePerf, z as PermissionDeniedError } from "./queue-ipc-CEetz4_7.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) {
@@ -1189,17 +1274,14 @@ var AcpClient = class {
1189
1274
  this.loadedSessionId = sessionId;
1190
1275
  return { agentSessionId: extractRuntimeSessionId(response?._meta) };
1191
1276
  }
1192
- async prompt(sessionId, text) {
1277
+ async prompt(sessionId, prompt) {
1193
1278
  const connection = this.getConnection();
1194
1279
  const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
1195
1280
  let promptPromise;
1196
1281
  try {
1197
1282
  promptPromise = connection.prompt({
1198
1283
  sessionId,
1199
- prompt: [{
1200
- type: "text",
1201
- text
1202
- }]
1284
+ prompt: typeof prompt === "string" ? textPrompt(prompt) : prompt
1203
1285
  });
1204
1286
  } catch (error) {
1205
1287
  restoreConsoleError?.();
@@ -1226,17 +1308,27 @@ var AcpClient = class {
1226
1308
  }
1227
1309
  }
1228
1310
  async setSessionMode(sessionId, modeId) {
1229
- await this.getConnection().setSessionMode({
1230
- sessionId,
1231
- modeId
1232
- });
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
+ }
1233
1320
  }
1234
1321
  async setSessionConfigOption(sessionId, configId, value) {
1235
- return await this.getConnection().setSessionConfigOption({
1236
- sessionId,
1237
- configId,
1238
- value
1239
- });
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
+ }
1240
1332
  }
1241
1333
  async cancel(sessionId) {
1242
1334
  const connection = this.getConnection();
@@ -1362,17 +1454,13 @@ var AcpClient = class {
1362
1454
  } catch (error) {
1363
1455
  if (error instanceof PermissionPromptUnavailableError) {
1364
1456
  this.notePromptPermissionFailure(params.sessionId, error);
1365
- this.permissionStats.requested += 1;
1366
- this.permissionStats.cancelled += 1;
1457
+ this.recordPermissionDecision("cancelled");
1367
1458
  return { outcome: { outcome: "cancelled" } };
1368
1459
  }
1369
1460
  throw error;
1370
1461
  }
1371
1462
  const decision = classifyPermissionDecision(params, response);
1372
- this.permissionStats.requested += 1;
1373
- if (decision === "approved") this.permissionStats.approved += 1;
1374
- else if (decision === "denied") this.permissionStats.denied += 1;
1375
- else this.permissionStats.cancelled += 1;
1463
+ this.recordPermissionDecision(decision);
1376
1464
  return response;
1377
1465
  }
1378
1466
  attachAgentLifecycleObservers(child) {
@@ -1405,13 +1493,18 @@ var AcpClient = class {
1405
1493
  return error;
1406
1494
  }
1407
1495
  async handleReadTextFile(params) {
1408
- 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
+ }
1409
1502
  }
1410
1503
  async handleWriteTextFile(params) {
1411
1504
  try {
1412
1505
  return await this.filesystem.writeTextFile(params);
1413
1506
  } catch (error) {
1414
- if (error instanceof PermissionPromptUnavailableError) this.notePromptPermissionFailure(params.sessionId, error);
1507
+ this.recordPermissionError(params.sessionId, error);
1415
1508
  throw error;
1416
1509
  }
1417
1510
  }
@@ -1419,7 +1512,7 @@ var AcpClient = class {
1419
1512
  try {
1420
1513
  return await this.terminalManager.createTerminal(params);
1421
1514
  } catch (error) {
1422
- if (error instanceof PermissionPromptUnavailableError) this.notePromptPermissionFailure(params.sessionId, error);
1515
+ this.recordPermissionError(params.sessionId, error);
1423
1516
  throw error;
1424
1517
  }
1425
1518
  }
@@ -1435,6 +1528,26 @@ var AcpClient = class {
1435
1528
  async handleReleaseTerminal(params) {
1436
1529
  return await this.terminalManager.releaseTerminal(params);
1437
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
+ }
1438
1551
  async handleSessionUpdate(notification) {
1439
1552
  const sequence = ++this.observedSessionUpdates;
1440
1553
  this.sessionUpdateChain = this.sessionUpdateChain.then(async () => {
@@ -1472,7 +1585,6 @@ var AcpClient = class {
1472
1585
  throw new Error(`Timed out waiting for session replay drain after ${normalizedTimeoutMs}ms`);
1473
1586
  }
1474
1587
  };
1475
-
1476
1588
  //#endregion
1477
1589
  //#region src/perf-metrics-capture.ts
1478
1590
  const PERF_METRICS_FILE_ENV = "ACPX_PERF_METRICS_FILE";
@@ -1544,7 +1656,6 @@ function installPerfMetricsCapture(options = {}) {
1544
1656
  process.once(signal, handler);
1545
1657
  }
1546
1658
  }
1547
-
1548
1659
  //#endregion
1549
1660
  //#region src/session-conversation-model.ts
1550
1661
  const MAX_RUNTIME_MESSAGES = 200;
@@ -1802,11 +1913,14 @@ function cloneSessionAcpxState(state) {
1802
1913
  };
1803
1914
  }
1804
1915
  function recordPromptSubmission(conversation, prompt, timestamp = isoNow$1()) {
1805
- const text = prompt.trim();
1806
- if (!text) return;
1916
+ const userContent = (typeof prompt === "string" ? textPrompt(prompt) : prompt).map((content) => contentToUserContent(content)).filter((content) => content !== void 0);
1917
+ if (userContent.length === 0) return;
1807
1918
  conversation.messages.push({ User: {
1808
1919
  id: nextUserMessageId(),
1809
- content: [{ Text: trimRuntimeText(text, MAX_RUNTIME_AGENT_TEXT_CHARS) }]
1920
+ content: userContent.map((content) => {
1921
+ if ("Text" in content) return { Text: trimRuntimeText(content.Text, MAX_RUNTIME_AGENT_TEXT_CHARS) };
1922
+ return content;
1923
+ })
1810
1924
  } });
1811
1925
  updateConversationTimestamp(conversation, timestamp);
1812
1926
  trimConversationForRuntime(conversation);
@@ -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;
@@ -3114,8 +3260,8 @@ async function runQueuedTask(sessionRecordId, task, options) {
3114
3260
  try {
3115
3261
  const result = await runSessionPrompt({
3116
3262
  sessionRecordId,
3117
- message: task.message,
3118
3263
  mcpServers: options.mcpServers,
3264
+ prompt: task.prompt ?? textPrompt(task.message),
3119
3265
  permissionMode: task.permissionMode,
3120
3266
  nonInteractivePermissions: task.nonInteractivePermissions ?? options.nonInteractivePermissions,
3121
3267
  authCredentials: options.authCredentials,
@@ -3164,12 +3310,14 @@ async function runSessionPrompt(options) {
3164
3310
  });
3165
3311
  const conversation = cloneSessionConversation(record);
3166
3312
  let acpxState = cloneSessionAcpxState(record.acpx);
3167
- recordPromptSubmission(conversation, options.message, isoNow());
3313
+ recordPromptSubmission(conversation, options.prompt, isoNow());
3168
3314
  output.setContext({ sessionId: record.acpxRecordId });
3169
3315
  const eventWriter = await measurePerf("session.events.open", async () => {
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,30 +3386,43 @@ 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);
3257
3422
  let response;
3258
3423
  try {
3259
3424
  const promptStartedAt = Date.now();
3260
- const promptPromise = client.prompt(activeSessionId, options.message);
3425
+ const promptPromise = client.prompt(activeSessionId, options.prompt);
3261
3426
  if (options.onPromptActive) try {
3262
3427
  await options.onPromptActive();
3263
3428
  } catch (error) {
@@ -3346,7 +3511,7 @@ async function runOnce(options) {
3346
3511
  })).sessionId;
3347
3512
  output.setContext({ sessionId });
3348
3513
  const response = await measurePerf("runtime.exec.prompt", async () => {
3349
- return await withTimeout(client.prompt(sessionId, options.message), options.timeoutMs);
3514
+ return await withTimeout(client.prompt(sessionId, options.prompt), options.timeoutMs);
3350
3515
  });
3351
3516
  output.flush();
3352
3517
  return toPromptResult(response.stopReason, sessionId, client);
@@ -3460,7 +3625,8 @@ async function ensureSession(options) {
3460
3625
  async function submitToRunningOwner(options, waitForCompletion) {
3461
3626
  return await trySubmitToRunningOwner({
3462
3627
  sessionId: options.sessionId,
3463
- message: options.message,
3628
+ message: promptToDisplayText(options.prompt),
3629
+ prompt: options.prompt,
3464
3630
  permissionMode: options.permissionMode,
3465
3631
  nonInteractivePermissions: options.nonInteractivePermissions,
3466
3632
  outputFormatter: options.outputFormatter,
@@ -3618,7 +3784,7 @@ async function sendSession(options) {
3618
3784
  for (let attempt = 0; attempt < QUEUE_OWNER_STARTUP_MAX_ATTEMPTS; attempt += 1) {
3619
3785
  const queued = await submitToRunningOwner(options, waitForCompletion);
3620
3786
  if (queued) return queued;
3621
- await waitMs$1(QUEUE_CONNECT_RETRY_MS);
3787
+ await waitMs$1(50);
3622
3788
  }
3623
3789
  throw new Error(`Session queue owner failed to start for session ${options.sessionId}`);
3624
3790
  }
@@ -3706,11 +3872,10 @@ async function closeSession(sessionId) {
3706
3872
  await writeSessionRecord(record);
3707
3873
  return record;
3708
3874
  }
3709
-
3710
3875
  //#endregion
3711
3876
  //#region src/session.ts
3712
3877
  var session_exports = /* @__PURE__ */ __exportAll({
3713
- DEFAULT_HISTORY_LIMIT: () => DEFAULT_HISTORY_LIMIT,
3878
+ DEFAULT_HISTORY_LIMIT: () => 20,
3714
3879
  DEFAULT_QUEUE_OWNER_TTL_MS: () => DEFAULT_QUEUE_OWNER_TTL_MS,
3715
3880
  InterruptedError: () => InterruptedError,
3716
3881
  TimeoutError: () => TimeoutError,
@@ -3731,7 +3896,7 @@ var session_exports = /* @__PURE__ */ __exportAll({
3731
3896
  setSessionConfigOption: () => setSessionConfigOption,
3732
3897
  setSessionMode: () => setSessionMode
3733
3898
  });
3734
-
3735
3899
  //#endregion
3736
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 };
3737
- //# sourceMappingURL=session-BARXiu6-.js.map
3901
+
3902
+ //# sourceMappingURL=session-C2Q8ktsN.js.map