@junctionpanel/server 0.1.74 → 0.1.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
6
6
  declare const GEMINI_MODES: readonly ["default", "auto_edit", "yolo", "plan"];
7
7
  type GeminiModeId = (typeof GEMINI_MODES)[number];
8
8
  type GeminiInteractionSubtype = "tool" | "question" | "plan";
9
+ type GeminiAcpLaunchFlag = "--acp" | "--experimental-acp";
9
10
  export declare const GEMINI_MODEL_CATALOG: readonly [{
10
11
  readonly id: "auto-gemini-2.5";
11
12
  readonly label: "Auto 2.5";
@@ -683,7 +684,7 @@ type GeminiRecordedTurnSummary = {
683
684
  usage: AgentUsage;
684
685
  modelId: string | null;
685
686
  };
686
- export declare function ensureGeminiAcpSupport(runtimeSettings?: ProviderRuntimeSettings): Promise<void>;
687
+ export declare function ensureGeminiAcpSupport(runtimeSettings?: ProviderRuntimeSettings): Promise<GeminiAcpLaunchFlag>;
687
688
  export declare function clearGeminiAcpSupportCacheForTests(): void;
688
689
  export declare function normalizeGeminiMode(modeId: string | undefined): GeminiModeId;
689
690
  export declare function getRequestedGeminiSessionState(config: {
@@ -1 +1 @@
1
- {"version":3,"file":"gemini-agent.d.ts","sourceRoot":"","sources":["../../../../../src/server/agent/providers/gemini-agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gemini-agent.d.ts","sourceRoot":"","sources":["../../../../../src/server/agent/providers/gemini-agent.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import * as acp from "@agentclientprotocol/sdk";
2
- import { execSync, spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { spawn } from "node:child_process";
3
4
  import { access, open, readdir, readFile, realpath, stat } from "node:fs/promises";
4
5
  import { homedir } from "node:os";
5
6
  import path from "node:path";
@@ -7,6 +8,7 @@ import { Readable as NodeReadable, Writable as NodeWritable } from "node:stream"
7
8
  import { setTimeout as delay } from "node:timers/promises";
8
9
  import { z } from "zod";
9
10
  import { applyProviderEnv, isProviderCommandAvailable, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
11
+ import { resolveCommandPathWithFallback } from "../../provider-command-resolution.js";
10
12
  import { ToolEditInputSchema, ToolEditOutputSchema, ToolReadInputSchema, ToolReadOutputSchema, ToolSearchInputSchema, ToolShellInputSchema, ToolShellOutputSchema, ToolWriteInputSchema, ToolWriteOutputSchema, toEditToolDetail, toReadToolDetail, toSearchToolDetail, toShellToolDetail, toWriteToolDetail, } from "./tool-call-detail-primitives.js";
11
13
  import { coerceToolCallId, nonEmptyString } from "./tool-call-mapper-utils.js";
12
14
  const GEMINI_PROVIDER = "gemini";
@@ -18,7 +20,8 @@ const GEMINI_RECORDED_SESSION_POLL_INTERVAL_MS = 100;
18
20
  const GEMINI_RECORDED_SESSION_DISCOVERY_TIMEOUT_MS = 200;
19
21
  const GEMINI_PLAN_TEXT_MAX_BYTES = 64 * 1024;
20
22
  const GEMINI_PLAN_TEXT_TRUNCATION_MARKER = "\n\n[Plan truncated because the file exceeded Junction's Gemini plan preview limit.]";
21
- const GEMINI_ACP_PROBE_TIMEOUT_MS = 3000;
23
+ const GEMINI_ACP_INITIALIZE_TIMEOUT_MS = 12000;
24
+ const GEMINI_ACP_PROBE_TIMEOUT_MS = 12000;
22
25
  const GEMINI_ACP_PROBE_CACHE_TTL_MS = 10 * 60000;
23
26
  const GEMINI_CAPABILITIES = {
24
27
  supportsStreaming: true,
@@ -205,31 +208,48 @@ class AsyncEventQueue {
205
208
  return this.items.shift() ?? null;
206
209
  }
207
210
  }
208
- function resolveGeminiBinary() {
209
- try {
210
- const geminiPath = execSync("which gemini", { encoding: "utf8" }).trim();
211
- if (geminiPath) {
212
- return geminiPath;
213
- }
214
- }
215
- catch {
216
- // fall through
217
- }
218
- throw new Error("Gemini CLI not found. Please install gemini globally so Junction can launch the provider.");
219
- }
220
211
  function formatGeminiLaunchError(error) {
221
212
  const message = stringifyUnknown(error);
222
213
  return new Error(`Failed to launch Gemini ACP process: ${message}`);
223
214
  }
215
+ function resolveGeminiBinary(runtimeSettings) {
216
+ const env = applyProviderEnv(process.env, runtimeSettings);
217
+ const geminiPath = resolveCommandPathWithFallback("gemini", {
218
+ env,
219
+ platform: process.platform,
220
+ loginShell: env.SHELL ?? process.env.SHELL ?? null,
221
+ });
222
+ if (geminiPath) {
223
+ return geminiPath;
224
+ }
225
+ throw new Error("Gemini CLI not found. Please install gemini globally so Junction can launch the provider.");
226
+ }
227
+ function resolveGeminiLaunchContext(runtimeSettings) {
228
+ const env = applyProviderEnv(process.env, runtimeSettings);
229
+ return {
230
+ env,
231
+ launchPrefix: resolveProviderCommandPrefix(runtimeSettings?.command, () => resolveGeminiBinary(runtimeSettings)),
232
+ };
233
+ }
224
234
  function getGeminiAcpProbeCacheKey(runtimeSettings) {
235
+ const mergedEnv = applyProviderEnv(process.env, runtimeSettings);
236
+ const fingerprintEnv = Object.fromEntries(Object.entries(mergedEnv)
237
+ // Shell bookkeeping churns frequently and should not invalidate the probe cache.
238
+ .filter(([key]) => !["OLDPWD", "PWD", "SHLVL", "_"].includes(key))
239
+ .sort(([left], [right]) => left.localeCompare(right)));
240
+ const envHash = createHash("sha256")
241
+ .update(JSON.stringify(fingerprintEnv))
242
+ .digest("hex");
225
243
  return JSON.stringify({
226
244
  command: runtimeSettings?.command ?? null,
245
+ path: mergedEnv.PATH ?? null,
246
+ envHash,
227
247
  });
228
248
  }
229
- async function runGeminiAcpSupportProbe(runtimeSettings) {
230
- const launchPrefix = resolveProviderCommandPrefix(runtimeSettings?.command, resolveGeminiBinary);
231
- const env = applyProviderEnv(process.env, runtimeSettings);
232
- const child = spawn(launchPrefix.command, [...launchPrefix.args, "--help"], {
249
+ async function runGeminiAcpSupportProbe(runtimeSettings, launchFlag) {
250
+ const { env, launchPrefix } = resolveGeminiLaunchContext(runtimeSettings);
251
+ const probeArgs = [...launchPrefix.args, launchFlag, "--help"];
252
+ const child = spawn(launchPrefix.command, probeArgs, {
233
253
  env,
234
254
  stdio: ["ignore", "pipe", "pipe"],
235
255
  });
@@ -250,7 +270,7 @@ async function runGeminiAcpSupportProbe(runtimeSettings) {
250
270
  child.kill();
251
271
  }
252
272
  finish(() => {
253
- reject(new Error("Gemini ACP support probe timed out while checking `gemini --help`."));
273
+ reject(new Error(`Gemini ACP support probe timed out while checking \`gemini ${launchFlag} --help\`.`));
254
274
  });
255
275
  }, GEMINI_ACP_PROBE_TIMEOUT_MS);
256
276
  child.once("error", (error) => {
@@ -264,54 +284,109 @@ async function runGeminiAcpSupportProbe(runtimeSettings) {
264
284
  child.stderr?.on("data", (chunk) => {
265
285
  stderr += chunk.toString();
266
286
  });
267
- child.once("close", () => {
287
+ child.once("close", (code, signal) => {
268
288
  finish(() => {
269
- const output = `${stdout}\n${stderr}`;
270
- if (output.includes("--acp")) {
289
+ const output = `${stdout}\n${stderr}`.trim();
290
+ if (code === 0 && output.includes(launchFlag)) {
271
291
  resolve();
272
292
  return;
273
293
  }
274
- reject(new Error("Installed Gemini CLI does not support --acp. Upgrade Gemini CLI to a version that includes ACP support."));
294
+ if (isGeminiUnsupportedLaunchFlagError(output, launchFlag)) {
295
+ reject(new Error(launchFlag === "--acp"
296
+ ? "Installed Gemini CLI does not support --acp. Upgrade Gemini CLI to a version that includes ACP support or still exposes --experimental-acp."
297
+ : "Installed Gemini CLI does not support ACP launch mode. Upgrade Gemini CLI to a version that includes --acp or --experimental-acp."));
298
+ return;
299
+ }
300
+ if (typeof code === "number" && code !== 0) {
301
+ reject(new Error(`Gemini ACP support probe failed while checking \`gemini ${launchFlag} --help\`: ${output || `process exited with code ${code}`}`));
302
+ return;
303
+ }
304
+ if (signal) {
305
+ reject(new Error(`Gemini ACP support probe failed while checking \`gemini ${launchFlag} --help\`: process exited with signal ${signal}`));
306
+ return;
307
+ }
308
+ reject(new Error(launchFlag === "--acp"
309
+ ? "Installed Gemini CLI does not support --acp. Upgrade Gemini CLI to a version that includes ACP support or still exposes --experimental-acp."
310
+ : "Installed Gemini CLI does not support ACP launch mode. Upgrade Gemini CLI to a version that includes --acp or --experimental-acp."));
275
311
  });
276
312
  });
277
313
  });
278
314
  }
279
- export async function ensureGeminiAcpSupport(runtimeSettings) {
315
+ function getCachedGeminiAcpLaunchFlag(runtimeSettings) {
280
316
  const cacheKey = getGeminiAcpProbeCacheKey(runtimeSettings);
281
317
  const cached = geminiAcpProbeCache.get(cacheKey);
282
- if (cached && Date.now() - cached.checkedAt < GEMINI_ACP_PROBE_CACHE_TTL_MS) {
283
- if (cached.error) {
284
- throw new Error(cached.error.message);
285
- }
286
- return;
318
+ if (!cached) {
319
+ return null;
320
+ }
321
+ if (Date.now() - cached.checkedAt >= GEMINI_ACP_PROBE_CACHE_TTL_MS) {
322
+ geminiAcpProbeCache.delete(cacheKey);
323
+ return null;
324
+ }
325
+ return cached.launchFlag;
326
+ }
327
+ function setCachedGeminiAcpLaunchFlag(runtimeSettings, launchFlag) {
328
+ geminiAcpProbeCache.set(getGeminiAcpProbeCacheKey(runtimeSettings), {
329
+ checkedAt: Date.now(),
330
+ launchFlag,
331
+ });
332
+ }
333
+ function getGeminiAcpLaunchCandidates(runtimeSettings) {
334
+ const cached = getCachedGeminiAcpLaunchFlag(runtimeSettings);
335
+ if (!cached) {
336
+ return ["--acp", "--experimental-acp"];
337
+ }
338
+ return cached === "--acp"
339
+ ? ["--acp", "--experimental-acp"]
340
+ : ["--experimental-acp", "--acp"];
341
+ }
342
+ function isGeminiUnsupportedLaunchFlagError(error, launchFlag) {
343
+ const normalizedMessage = stringifyUnknown(error).toLowerCase();
344
+ const normalizedFlagPattern = new RegExp(`(^|[^a-z0-9-])${launchFlag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").toLowerCase()}([^a-z0-9-]|$)`);
345
+ return (normalizedFlagPattern.test(normalizedMessage) &&
346
+ (normalizedMessage.includes("unknown") ||
347
+ normalizedMessage.includes("unsupported") ||
348
+ normalizedMessage.includes("invalid") ||
349
+ normalizedMessage.includes("unrecognized") ||
350
+ normalizedMessage.includes("did you mean") ||
351
+ normalizedMessage.includes("not support")));
352
+ }
353
+ export async function ensureGeminiAcpSupport(runtimeSettings) {
354
+ const cacheKey = getGeminiAcpProbeCacheKey(runtimeSettings);
355
+ const cachedLaunchFlag = getCachedGeminiAcpLaunchFlag(runtimeSettings);
356
+ if (cachedLaunchFlag) {
357
+ return cachedLaunchFlag;
287
358
  }
288
359
  const existingInflight = geminiAcpProbeInflight.get(cacheKey);
289
360
  if (existingInflight) {
290
- await existingInflight;
291
- return;
361
+ return await existingInflight;
292
362
  }
293
363
  const probePromise = (async () => {
364
+ let lastUnsupportedError = null;
294
365
  try {
295
- await runGeminiAcpSupportProbe(runtimeSettings);
296
- geminiAcpProbeCache.set(cacheKey, {
297
- checkedAt: Date.now(),
298
- error: null,
299
- });
300
- }
301
- catch (error) {
302
- const normalizedError = error instanceof Error ? error : new Error(stringifyUnknown(error));
303
- geminiAcpProbeCache.set(cacheKey, {
304
- checkedAt: Date.now(),
305
- error: normalizedError,
306
- });
307
- throw normalizedError;
366
+ for (const launchFlag of getGeminiAcpLaunchCandidates(runtimeSettings)) {
367
+ try {
368
+ await runGeminiAcpSupportProbe(runtimeSettings, launchFlag);
369
+ setCachedGeminiAcpLaunchFlag(runtimeSettings, launchFlag);
370
+ return launchFlag;
371
+ }
372
+ catch (error) {
373
+ const normalizedError = error instanceof Error ? error : new Error(stringifyUnknown(error));
374
+ if (isGeminiUnsupportedLaunchFlagError(normalizedError, launchFlag)) {
375
+ lastUnsupportedError = normalizedError;
376
+ continue;
377
+ }
378
+ throw normalizedError;
379
+ }
380
+ }
308
381
  }
309
382
  finally {
310
383
  geminiAcpProbeInflight.delete(cacheKey);
311
384
  }
385
+ throw (lastUnsupportedError ??
386
+ new Error("Installed Gemini CLI does not support ACP launch mode. Upgrade Gemini CLI to a version that includes --acp or --experimental-acp."));
312
387
  })();
313
388
  geminiAcpProbeInflight.set(cacheKey, probePromise);
314
- await probePromise;
389
+ return await probePromise;
315
390
  }
316
391
  export function clearGeminiAcpSupportCacheForTests() {
317
392
  geminiAcpProbeCache.clear();
@@ -1466,7 +1541,6 @@ class GeminiAgentSession {
1466
1541
  if (this.initialized) {
1467
1542
  return;
1468
1543
  }
1469
- await ensureGeminiAcpSupport(this.runtimeSettings);
1470
1544
  await this.openConnection();
1471
1545
  this.initialized = true;
1472
1546
  }
@@ -1738,6 +1812,25 @@ class GeminiAgentSession {
1738
1812
  this.connection = null;
1739
1813
  this.child = null;
1740
1814
  }
1815
+ async teardownPartialConnection(params) {
1816
+ this.finishHistoryCapture();
1817
+ if (params.child.stdin) {
1818
+ params.child.stdin.end();
1819
+ }
1820
+ if (!params.child.killed) {
1821
+ params.child.kill();
1822
+ }
1823
+ await params.closePromise.catch(() => { });
1824
+ if (this.connection === params.connection) {
1825
+ this.connection = null;
1826
+ }
1827
+ if (this.child === params.child) {
1828
+ this.child = null;
1829
+ }
1830
+ if (this.closePromise === params.closePromise) {
1831
+ this.closePromise = Promise.resolve();
1832
+ }
1833
+ }
1741
1834
  async setModel(modelId) {
1742
1835
  await this.initialize();
1743
1836
  const normalizedModelId = typeof modelId === "string" && modelId.trim().length > 0 ? modelId.trim() : null;
@@ -1762,16 +1855,26 @@ class GeminiAgentSession {
1762
1855
  this.config.model = normalizedModelId;
1763
1856
  }
1764
1857
  async setThinkingOption(_thinkingOptionId) { }
1765
- async openConnection() {
1766
- const launchPrefix = resolveProviderCommandPrefix(this.runtimeSettings?.command, resolveGeminiBinary);
1767
- const env = applyProviderEnv(process.env, this.runtimeSettings);
1768
- const launchArgs = [...launchPrefix.args, "--acp"];
1858
+ buildLaunchFailureMessage(params) {
1859
+ if (params.stderr) {
1860
+ return params.stderr;
1861
+ }
1862
+ if (typeof params.code === "number" && params.code !== 0) {
1863
+ return `Gemini ACP process exited with code ${params.code}`;
1864
+ }
1865
+ if (params.signal) {
1866
+ return `Gemini ACP process exited with signal ${params.signal}`;
1867
+ }
1868
+ return "Gemini ACP process closed before initialization";
1869
+ }
1870
+ async startConnectionWithLaunchFlag(launchFlag) {
1871
+ const { env, launchPrefix } = resolveGeminiLaunchContext(this.runtimeSettings);
1872
+ const launchArgs = [...launchPrefix.args, launchFlag];
1769
1873
  const child = spawn(launchPrefix.command, launchArgs, {
1770
1874
  cwd: this.config.cwd,
1771
1875
  env,
1772
1876
  stdio: ["pipe", "pipe", "pipe"],
1773
1877
  });
1774
- this.child = child;
1775
1878
  let initializing = true;
1776
1879
  let rejectLaunchFailure = null;
1777
1880
  const launchFailure = new Promise((_, reject) => {
@@ -1787,35 +1890,49 @@ class GeminiAgentSession {
1787
1890
  cwd: this.config.cwd,
1788
1891
  }, "Gemini ACP process failed to launch");
1789
1892
  if (initializing) {
1790
- this.connection = null;
1791
- this.child = null;
1792
- this.closePromise = Promise.resolve();
1793
1893
  rejectLaunchFailure?.(launchError);
1794
1894
  }
1795
1895
  });
1796
1896
  if (!child.stdin || !child.stdout || !child.stderr) {
1797
- await this.close().catch(() => { });
1897
+ if (!child.killed) {
1898
+ child.kill();
1899
+ }
1798
1900
  throw new Error("Gemini ACP process did not expose stdio pipes");
1799
1901
  }
1800
1902
  const stderrChunks = [];
1801
1903
  child.stderr.on("data", (chunk) => {
1802
- const text = chunk.toString("utf8");
1904
+ const text = chunk.toString();
1803
1905
  if (text.length > 0) {
1804
1906
  stderrChunks.push(text);
1805
1907
  }
1806
1908
  });
1807
- this.closePromise = new Promise((resolve) => {
1909
+ const stream = acp.ndJsonStream(NodeWritable.toWeb(child.stdin), NodeReadable.toWeb(child.stdout));
1910
+ const connection = new acp.ClientSideConnection(() => ({
1911
+ sessionUpdate: async (notification) => {
1912
+ await this.handleSessionUpdate(notification);
1913
+ },
1914
+ requestPermission: async (request) => this.handlePermissionRequest(request),
1915
+ }), stream);
1916
+ const closePromise = new Promise((resolve) => {
1808
1917
  child.once("close", (code, signal) => {
1809
1918
  const stderr = stderrChunks.join("").trim();
1810
- this.child = null;
1811
- this.connection = null;
1919
+ const message = this.buildLaunchFailureMessage({
1920
+ code,
1921
+ signal,
1922
+ stderr,
1923
+ });
1924
+ if (initializing) {
1925
+ rejectLaunchFailure?.(new Error(message));
1926
+ resolve();
1927
+ return;
1928
+ }
1929
+ if (this.child === child) {
1930
+ this.child = null;
1931
+ }
1932
+ if (this.connection === connection) {
1933
+ this.connection = null;
1934
+ }
1812
1935
  if (!this.closed) {
1813
- const message = stderr ||
1814
- (typeof code === "number" && code !== 0
1815
- ? `Gemini ACP process exited with code ${code}`
1816
- : signal
1817
- ? `Gemini ACP process exited with signal ${signal}`
1818
- : "Gemini ACP process closed");
1819
1936
  if (this.activeTurnQueue) {
1820
1937
  this.activeTurnQueue.push({
1821
1938
  type: "turn_failed",
@@ -1829,16 +1946,9 @@ class GeminiAgentSession {
1829
1946
  resolve();
1830
1947
  });
1831
1948
  });
1832
- const stream = acp.ndJsonStream(NodeWritable.toWeb(child.stdin), NodeReadable.toWeb(child.stdout));
1833
- this.connection = new acp.ClientSideConnection(() => ({
1834
- sessionUpdate: async (notification) => {
1835
- await this.handleSessionUpdate(notification);
1836
- },
1837
- requestPermission: async (request) => this.handlePermissionRequest(request),
1838
- }), stream);
1839
1949
  try {
1840
1950
  await Promise.race([
1841
- this.connection.initialize({
1951
+ connection.initialize({
1842
1952
  protocolVersion: acp.PROTOCOL_VERSION,
1843
1953
  clientCapabilities: {
1844
1954
  fs: {
@@ -1853,15 +1963,32 @@ class GeminiAgentSession {
1853
1963
  },
1854
1964
  }),
1855
1965
  launchFailure,
1966
+ delay(GEMINI_ACP_INITIALIZE_TIMEOUT_MS).then(() => {
1967
+ throw new Error(`Gemini ACP initialization timed out while launching with \`${launchFlag}\`.`);
1968
+ }),
1856
1969
  ]);
1857
1970
  initializing = false;
1971
+ this.child = child;
1972
+ this.connection = connection;
1973
+ this.closePromise = closePromise;
1974
+ }
1975
+ catch (error) {
1976
+ initializing = false;
1977
+ child.stdin.end();
1978
+ if (!child.killed) {
1979
+ child.kill();
1980
+ }
1981
+ await closePromise.catch(() => { });
1982
+ throw error;
1983
+ }
1984
+ try {
1858
1985
  if (this.sessionId) {
1859
1986
  const recordedSessionMatch = await findGeminiRecordedSessionForCwd(this.config.cwd, this.sessionId);
1860
1987
  const recordedSession = recordedSessionMatch?.session ?? null;
1861
1988
  this.hasObservedRecordedSession = recordedSession !== null;
1862
1989
  this.recordedSessionFilePath = recordedSessionMatch?.filePath ?? null;
1863
1990
  this.beginHistoryCapture(recordedSession ? countGeminiRecordedHistoryEvents(recordedSession) : null);
1864
- const response = await this.connection.loadSession({
1991
+ const response = await connection.loadSession({
1865
1992
  sessionId: this.sessionId,
1866
1993
  cwd: this.config.cwd,
1867
1994
  mcpServers: toGeminiAcpMcpServers(this.config.mcpServers),
@@ -1875,7 +2002,7 @@ class GeminiAgentSession {
1875
2002
  }
1876
2003
  }
1877
2004
  else {
1878
- const response = await this.connection.newSession({
2005
+ const response = await connection.newSession({
1879
2006
  cwd: this.config.cwd,
1880
2007
  mcpServers: toGeminiAcpMcpServers(this.config.mcpServers),
1881
2008
  });
@@ -1883,13 +2010,42 @@ class GeminiAgentSession {
1883
2010
  this.applySessionSetup(response);
1884
2011
  }
1885
2012
  await this.applyRequestedOverrides();
2013
+ setCachedGeminiAcpLaunchFlag(this.runtimeSettings, launchFlag);
1886
2014
  }
1887
2015
  catch (error) {
1888
- initializing = false;
1889
- await this.close().catch(() => { });
2016
+ await this.teardownPartialConnection({
2017
+ child,
2018
+ connection,
2019
+ closePromise,
2020
+ });
1890
2021
  throw new Error(formatGeminiAcpError(error));
1891
2022
  }
1892
2023
  }
2024
+ async openConnection() {
2025
+ let lastUnsupportedError = null;
2026
+ for (const launchFlag of getGeminiAcpLaunchCandidates(this.runtimeSettings)) {
2027
+ try {
2028
+ await this.startConnectionWithLaunchFlag(launchFlag);
2029
+ return;
2030
+ }
2031
+ catch (error) {
2032
+ const normalizedError = error instanceof Error ? error : new Error(stringifyUnknown(error));
2033
+ if (isGeminiUnsupportedLaunchFlagError(normalizedError, launchFlag)) {
2034
+ lastUnsupportedError = normalizedError;
2035
+ this.logger.info({
2036
+ launchFlag,
2037
+ message: normalizedError.message,
2038
+ normalizedMessage: normalizedError.message.toLowerCase(),
2039
+ classifier: "isGeminiUnsupportedLaunchFlagError",
2040
+ }, "Gemini ACP launch flag unsupported; trying next candidate");
2041
+ continue;
2042
+ }
2043
+ throw normalizedError;
2044
+ }
2045
+ }
2046
+ throw (lastUnsupportedError ??
2047
+ new Error("Installed Gemini CLI does not support ACP launch mode. Upgrade Gemini CLI to a version that includes --acp or --experimental-acp."));
2048
+ }
1893
2049
  applySessionSetup(response) {
1894
2050
  if (response.modes) {
1895
2051
  this.availableModes = response.modes.availableModes.map((mode) => toAgentMode(mode));