Download packages/cli/src/acp/acpSessionManager.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 9.54 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/acp/acpSessionManager.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/acp/acpSessionManager.ts
-
curl -L -o acpSessionManager.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/acp/acpSessionManager.ts
9.54 kB
| /** | |
| * @license | |
| * Copyright 2026 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { | |
| type Config, | |
| AuthType, | |
| MCPServerConfig, | |
| debugLogger, | |
| startupProfiler, | |
| convertSessionToClientHistory, | |
| createPolicyUpdater, | |
| } from '@google/gemini-cli-core'; | |
| import * as acp from '@agentclientprotocol/sdk'; | |
| import { randomUUID } from 'node:crypto'; | |
| import { loadSettings, type LoadedSettings } from '../config/settings.js'; | |
| import { SessionSelector } from '../utils/sessionUtils.js'; | |
| import { Session } from './acpSession.js'; | |
| import { AcpFileSystemService } from './acpFileSystemService.js'; | |
| import { getAcpErrorMessage } from './acpErrors.js'; | |
| import { buildAvailableModels, buildAvailableModes } from './acpUtils.js'; | |
| import { loadCliConfig, type CliArgs } from '../config/config.js'; | |
| import { startAutoMemoryIfEnabled } from '../utils/autoMemory.js'; | |
| export interface AuthDetails { | |
| apiKey?: string; | |
| baseUrl?: string; | |
| customHeaders?: Record<string, string>; | |
| } | |
| export class AcpSessionManager { | |
| private sessions: Map<string, Session> = new Map(); | |
| private clientCapabilities: acp.ClientCapabilities | undefined; | |
| constructor( | |
| private settings: LoadedSettings, | |
| private argv: CliArgs, | |
| private connection: acp.AgentSideConnection, | |
| ) {} | |
| setClientCapabilities(capabilities: acp.ClientCapabilities) { | |
| this.clientCapabilities = capabilities; | |
| } | |
| getSession(sessionId: string): Session | undefined { | |
| return this.sessions.get(sessionId); | |
| } | |
| dispose(): void { | |
| for (const session of this.sessions.values()) { | |
| session.dispose(); | |
| } | |
| this.sessions.clear(); | |
| } | |
| async newSession( | |
| { cwd, mcpServers }: acp.NewSessionRequest, | |
| authDetails: AuthDetails, | |
| ): Promise<acp.NewSessionResponse> { | |
| const sessionId = randomUUID(); | |
| const loadedSettings = loadSettings(cwd); | |
| const config = await this.newSessionConfig( | |
| sessionId, | |
| cwd, | |
| mcpServers, | |
| loadedSettings, | |
| ); | |
| const authType = | |
| loadedSettings.merged.security.auth.selectedType || | |
| (authDetails.baseUrl || process.env['GOOGLE_GEMINI_BASE_URL'] | |
| ? AuthType.GATEWAY | |
| : AuthType.USE_GEMINI); | |
| let isAuthenticated = false; | |
| let authErrorMessage = ''; | |
| try { | |
| await config.refreshAuth( | |
| authType, | |
| authDetails.apiKey, | |
| authDetails.baseUrl, | |
| authDetails.customHeaders, | |
| ); | |
| isAuthenticated = true; | |
| // Extra validation for Gemini API key | |
| const contentGeneratorConfig = config.getContentGeneratorConfig(); | |
| if ( | |
| authType === AuthType.USE_GEMINI && | |
| (!contentGeneratorConfig || !contentGeneratorConfig.apiKey) | |
| ) { | |
| isAuthenticated = false; | |
| authErrorMessage = 'Gemini API key is missing or not configured.'; | |
| } | |
| } catch (e) { | |
| isAuthenticated = false; | |
| authErrorMessage = getAcpErrorMessage(e); | |
| debugLogger.error( | |
| `Authentication failed: ${e instanceof Error ? e.stack : e}`, | |
| ); | |
| } | |
| if (!isAuthenticated) { | |
| throw new acp.RequestError( | |
| -32000, | |
| authErrorMessage || 'Authentication required.', | |
| ); | |
| } | |
| if (this.clientCapabilities?.fs) { | |
| const acpFileSystemService = new AcpFileSystemService( | |
| this.connection, | |
| sessionId, | |
| this.clientCapabilities.fs, | |
| config.getFileSystemService(), | |
| cwd, | |
| ); | |
| config.setFileSystemService(acpFileSystemService); | |
| } | |
| await config.initialize(); | |
| startupProfiler.flush(config); | |
| startAutoMemoryIfEnabled(config); | |
| const geminiClient = config.getGeminiClient(); | |
| const chat = await geminiClient.startChat(); | |
| const session = new Session( | |
| sessionId, | |
| chat, | |
| config, | |
| this.connection, | |
| this.settings, | |
| ); | |
| this.sessions.set(sessionId, session); | |
| setTimeout(() => { | |
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | |
| session.sendAvailableCommands(); | |
| }, 0); | |
| const { availableModels, currentModelId } = buildAvailableModels( | |
| config, | |
| loadedSettings, | |
| ); | |
| const response = { | |
| sessionId, | |
| modes: { | |
| availableModes: buildAvailableModes(config.isPlanEnabled()), | |
| currentModeId: config.getApprovalMode(), | |
| }, | |
| models: { | |
| availableModels, | |
| currentModelId, | |
| }, | |
| }; | |
| return response; | |
| } | |
| async loadSession( | |
| { sessionId, cwd, mcpServers }: acp.LoadSessionRequest, | |
| authDetails: AuthDetails, | |
| ): Promise<acp.LoadSessionResponse> { | |
| const config = await this.initializeSessionConfig( | |
| sessionId, | |
| cwd, | |
| mcpServers, | |
| authDetails, | |
| ); | |
| const sessionSelector = new SessionSelector(config.storage); | |
| const { sessionData, sessionPath } = | |
| await sessionSelector.resolveSession(sessionId); | |
| const clientHistory = convertSessionToClientHistory(sessionData.messages); | |
| const geminiClient = config.getGeminiClient(); | |
| await geminiClient.initialize(); | |
| await geminiClient.resumeChat(clientHistory, { | |
| conversation: sessionData, | |
| filePath: sessionPath, | |
| }); | |
| const session = new Session( | |
| sessionId, | |
| geminiClient.getChat(), | |
| config, | |
| this.connection, | |
| this.settings, | |
| ); | |
| const existingSession = this.sessions.get(sessionId); | |
| if (existingSession) { | |
| existingSession.dispose(); | |
| } | |
| this.sessions.set(sessionId, session); | |
| // Stream history back to client | |
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | |
| session.streamHistory(sessionData.messages); | |
| setTimeout(() => { | |
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | |
| session.sendAvailableCommands(); | |
| }, 0); | |
| const { availableModels, currentModelId } = buildAvailableModels( | |
| config, | |
| this.settings, | |
| ); | |
| const response = { | |
| modes: { | |
| availableModes: buildAvailableModes(config.isPlanEnabled()), | |
| currentModeId: config.getApprovalMode(), | |
| }, | |
| models: { | |
| availableModels, | |
| currentModelId, | |
| }, | |
| }; | |
| return response; | |
| } | |
| private async initializeSessionConfig( | |
| sessionId: string, | |
| cwd: string, | |
| mcpServers: acp.McpServer[], | |
| authDetails: AuthDetails, | |
| ): Promise<Config> { | |
| const selectedAuthType = | |
| this.settings.merged.security.auth.selectedType || | |
| (authDetails.baseUrl || process.env['GOOGLE_GEMINI_BASE_URL'] | |
| ? AuthType.GATEWAY | |
| : undefined); | |
| if (!selectedAuthType) { | |
| throw acp.RequestError.authRequired(); | |
| } | |
| // 1. Create config WITHOUT initializing it (no MCP servers started yet) | |
| const config = await this.newSessionConfig(sessionId, cwd, mcpServers); | |
| // 2. Authenticate BEFORE initializing configuration or starting MCP servers. | |
| // This satisfies the security requirement to verify the user before executing | |
| // potentially unsafe server definitions. | |
| try { | |
| await config.refreshAuth( | |
| selectedAuthType, | |
| authDetails.apiKey, | |
| authDetails.baseUrl, | |
| authDetails.customHeaders, | |
| ); | |
| } catch (e) { | |
| debugLogger.error(`Authentication failed: ${e}`); | |
| throw acp.RequestError.authRequired(); | |
| } | |
| // 3. Set the ACP FileSystemService (if supported) before config initialization | |
| if (this.clientCapabilities?.fs) { | |
| const acpFileSystemService = new AcpFileSystemService( | |
| this.connection, | |
| sessionId, | |
| this.clientCapabilities.fs, | |
| config.getFileSystemService(), | |
| cwd, | |
| ); | |
| config.setFileSystemService(acpFileSystemService); | |
| } | |
| // 4. Now that we are authenticated, it is safe to initialize the config | |
| // which starts the MCP servers and other heavy resources. | |
| await config.initialize(); | |
| startupProfiler.flush(config); | |
| startAutoMemoryIfEnabled(config); | |
| return config; | |
| } | |
| async newSessionConfig( | |
| sessionId: string, | |
| cwd: string, | |
| mcpServers: acp.McpServer[], | |
| loadedSettings?: LoadedSettings, | |
| ): Promise<Config> { | |
| const currentSettings = loadedSettings || this.settings; | |
| const mergedMcpServers = { ...currentSettings.merged.mcpServers }; | |
| for (const server of mcpServers) { | |
| if ( | |
| 'type' in server && | |
| (server.type === 'sse' || server.type === 'http') | |
| ) { | |
| // HTTP or SSE MCP server | |
| const headers = Object.fromEntries( | |
| server.headers.map(({ name, value }) => [name, value]), | |
| ); | |
| mergedMcpServers[server.name] = new MCPServerConfig( | |
| undefined, // command | |
| undefined, // args | |
| undefined, // env | |
| undefined, // cwd | |
| server.type === 'sse' ? server.url : undefined, // url (sse) | |
| server.type === 'http' ? server.url : undefined, // httpUrl | |
| headers, | |
| ); | |
| } else if ('command' in server) { | |
| // Stdio MCP server | |
| const env: Record<string, string> = {}; | |
| for (const { name: envName, value } of server.env) { | |
| env[envName] = value; | |
| } | |
| mergedMcpServers[server.name] = new MCPServerConfig( | |
| server.command, | |
| server.args, | |
| env, | |
| cwd, | |
| ); | |
| } | |
| } | |
| const settings = { | |
| ...currentSettings.merged, | |
| mcpServers: mergedMcpServers, | |
| }; | |
| const config = await loadCliConfig(settings, sessionId, this.argv, { cwd }); | |
| createPolicyUpdater( | |
| config.getPolicyEngine(), | |
| config.messageBus, | |
| config.storage, | |
| ); | |
| return config; | |
| } | |
| } | |