@junctionpanel/server 0.1.75 → 0.1.77
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.
- package/dist/server/client/daemon-client-terminal-stream-manager.d.ts +1 -0
- package/dist/server/client/daemon-client-terminal-stream-manager.d.ts.map +1 -1
- package/dist/server/client/daemon-client-terminal-stream-manager.js +20 -5
- package/dist/server/client/daemon-client-terminal-stream-manager.js.map +1 -1
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +44 -33
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/providers/gemini-agent.d.ts +2 -1
- package/dist/server/server/agent/providers/gemini-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/gemini-agent.js +233 -77
- package/dist/server/server/agent/providers/gemini-agent.js.map +1 -1
- package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
- package/dist/server/server/agent/timeline-projection.js +68 -52
- package/dist/server/server/agent/timeline-projection.js.map +1 -1
- package/dist/server/server/session.d.ts +0 -2
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +157 -33
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/utils/checkout-git.d.ts +8 -5
- package/dist/server/utils/checkout-git.d.ts.map +1 -1
- package/dist/server/utils/checkout-git.js +91 -37
- package/dist/server/utils/checkout-git.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as acp from "@agentclientprotocol/sdk";
|
|
2
|
-
import {
|
|
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
|
|
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 =
|
|
231
|
-
const
|
|
232
|
-
const child = spawn(launchPrefix.command,
|
|
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(
|
|
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(
|
|
289
|
+
const output = `${stdout}\n${stderr}`.trim();
|
|
290
|
+
if (code === 0 && output.includes(launchFlag)) {
|
|
271
291
|
resolve();
|
|
272
292
|
return;
|
|
273
293
|
}
|
|
274
|
-
|
|
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
|
-
|
|
315
|
+
function getCachedGeminiAcpLaunchFlag(runtimeSettings) {
|
|
280
316
|
const cacheKey = getGeminiAcpProbeCacheKey(runtimeSettings);
|
|
281
317
|
const cached = geminiAcpProbeCache.get(cacheKey);
|
|
282
|
-
if (cached
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
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
|
-
|
|
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(
|
|
1904
|
+
const text = chunk.toString();
|
|
1803
1905
|
if (text.length > 0) {
|
|
1804
1906
|
stderrChunks.push(text);
|
|
1805
1907
|
}
|
|
1806
1908
|
});
|
|
1807
|
-
|
|
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
|
-
|
|
1811
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1889
|
-
|
|
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));
|