diff --git a/packages/cli/src/acp/acpClient.test.ts b/packages/cli/src/acp/acpClient.test.ts index abad9d374d..ca525182b5 100644 --- a/packages/cli/src/acp/acpClient.test.ts +++ b/packages/cli/src/acp/acpClient.test.ts @@ -177,6 +177,9 @@ describe('GeminiAgent', () => { getHasAccessToPreviewModel: vi.fn().mockReturnValue(false), getCheckpointingEnabled: vi.fn().mockReturnValue(false), getDisableAlwaysAllow: vi.fn().mockReturnValue(false), + get config() { + return this; + }, } as unknown as Mocked>>; mockSettings = { merged: { @@ -656,6 +659,12 @@ describe('Session', () => { getGitService: vi.fn().mockResolvedValue({} as GitService), waitForMcpInit: vi.fn(), getDisableAlwaysAllow: vi.fn().mockReturnValue(false), + get config() { + return this; + }, + get toolRegistry() { + return mockToolRegistry; + }, } as unknown as Mocked; mockConnection = { sessionUpdate: vi.fn(), diff --git a/packages/cli/src/acp/acpClient.ts b/packages/cli/src/acp/acpClient.ts index 44c0373515..bd5a52f126 100644 --- a/packages/cli/src/acp/acpClient.ts +++ b/packages/cli/src/acp/acpClient.ts @@ -47,6 +47,7 @@ import { DEFAULT_GEMINI_MODEL_AUTO, PREVIEW_GEMINI_MODEL_AUTO, getDisplayString, + type AgentLoopContext, } from '@google/gemini-cli-core'; import * as acp from '@agentclientprotocol/sdk'; import { AcpFileSystemService } from './fileSystemService.js'; @@ -104,7 +105,7 @@ export class GeminiAgent { private customHeaders: Record | undefined; constructor( - private config: Config, + private context: AgentLoopContext, private settings: LoadedSettings, private argv: CliArgs, private connection: acp.AgentSideConnection, @@ -148,7 +149,7 @@ export class GeminiAgent { }, ]; - await this.config.initialize(); + await this.context.config.initialize(); const version = await getVersion(); return { protocolVersion: acp.PROTOCOL_VERSION, @@ -220,7 +221,7 @@ export class GeminiAgent { this.baseUrl = baseUrl; this.customHeaders = headers; - await this.config.refreshAuth( + await this.context.config.refreshAuth( method, apiKey ?? this.apiKey, baseUrl, @@ -537,7 +538,7 @@ export class Session { constructor( private readonly id: string, private readonly chat: GeminiChat, - private readonly config: Config, + private readonly context: AgentLoopContext, private readonly connection: acp.AgentSideConnection, private readonly settings: LoadedSettings, ) {} @@ -552,13 +553,15 @@ export class Session { } setMode(modeId: acp.SessionModeId): acp.SetSessionModeResponse { - const availableModes = buildAvailableModes(this.config.isPlanEnabled()); + const availableModes = buildAvailableModes( + this.context.config.isPlanEnabled(), + ); const mode = availableModes.find((m) => m.id === modeId); if (!mode) { throw new Error(`Invalid or unavailable mode: ${modeId}`); } // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - this.config.setApprovalMode(mode.id as ApprovalMode); + this.context.config.setApprovalMode(mode.id as ApprovalMode); return {}; } @@ -579,7 +582,7 @@ export class Session { } setModel(modelId: acp.ModelId): acp.SetSessionModelResponse { - this.config.setModel(modelId); + this.context.config.setModel(modelId); return {}; } @@ -634,7 +637,7 @@ export class Session { } } - const tool = this.config.getToolRegistry().getTool(toolCall.name); + const tool = this.context.toolRegistry.getTool(toolCall.name); await this.sendUpdate({ sessionUpdate: 'tool_call', @@ -658,7 +661,7 @@ export class Session { const pendingSend = new AbortController(); this.pendingPrompt = pendingSend; - await this.config.waitForMcpInit(); + await this.context.config.waitForMcpInit(); const promptId = Math.random().toString(16).slice(2); const chat = this.chat; @@ -712,8 +715,8 @@ export class Session { try { const model = resolveModel( - this.config.getModel(), - (await this.config.getGemini31Launched?.()) ?? false, + this.context.config.getModel(), + (await this.context.config.getGemini31Launched?.()) ?? false, ); const responseStream = await chat.sendMessageStream( { model }, @@ -804,9 +807,9 @@ export class Session { // eslint-disable-next-line @typescript-eslint/no-unused-vars parts: Part[], ): Promise { - const gitService = await this.config.getGitService(); + const gitService = await this.context.config.getGitService(); const commandContext = { - config: this.config, + agentContext: this.context, settings: this.settings, git: gitService, sendMessage: async (text: string) => { @@ -842,7 +845,7 @@ export class Session { const errorResponse = (error: Error) => { const durationMs = Date.now() - startTime; logToolCall( - this.config, + this.context.config, new ToolCallEvent( undefined, fc.name ?? '', @@ -872,7 +875,7 @@ export class Session { return errorResponse(new Error('Missing function name')); } - const toolRegistry = this.config.getToolRegistry(); + const toolRegistry = this.context.toolRegistry; const tool = toolRegistry.getTool(fc.name); if (!tool) { @@ -908,7 +911,10 @@ export class Session { const params: acp.RequestPermissionRequest = { sessionId: this.id, - options: toPermissionOptions(confirmationDetails, this.config), + options: toPermissionOptions( + confirmationDetails, + this.context.config, + ), toolCall: { toolCallId: callId, status: 'pending', @@ -974,7 +980,7 @@ export class Session { const durationMs = Date.now() - startTime; logToolCall( - this.config, + this.context.config, new ToolCallEvent( undefined, fc.name ?? '', @@ -988,7 +994,7 @@ export class Session { ), ); - this.chat.recordCompletedToolCalls(this.config.getActiveModel(), [ + this.chat.recordCompletedToolCalls(this.context.config.getActiveModel(), [ { status: CoreToolCallStatus.Success, request: { @@ -1006,8 +1012,8 @@ export class Session { fc.name, callId, toolResult.llmContent, - this.config.getActiveModel(), - this.config, + this.context.config.getActiveModel(), + this.context.config, ), resultDisplay: toolResult.returnDisplay, error: undefined, @@ -1020,8 +1026,8 @@ export class Session { fc.name, callId, toolResult.llmContent, - this.config.getActiveModel(), - this.config, + this.context.config.getActiveModel(), + this.context.config, ); } catch (e) { const error = e instanceof Error ? e : new Error(String(e)); @@ -1036,7 +1042,7 @@ export class Session { kind: toAcpToolKind(tool.kind), }); - this.chat.recordCompletedToolCalls(this.config.getActiveModel(), [ + this.chat.recordCompletedToolCalls(this.context.config.getActiveModel(), [ { status: CoreToolCallStatus.Error, request: { @@ -1122,18 +1128,18 @@ export class Session { const atPathToResolvedSpecMap = new Map(); // Get centralized file discovery service - const fileDiscovery = this.config.getFileService(); + const fileDiscovery = this.context.config.getFileService(); const fileFilteringOptions: FilterFilesOptions = - this.config.getFileFilteringOptions(); + this.context.config.getFileFilteringOptions(); const pathSpecsToRead: string[] = []; const contentLabelsForDisplay: string[] = []; const ignoredPaths: string[] = []; - const toolRegistry = this.config.getToolRegistry(); + const toolRegistry = this.context.toolRegistry; const readManyFilesTool = new ReadManyFilesTool( - this.config, - this.config.getMessageBus(), + this.context.config, + this.context.messageBus, ); const globTool = toolRegistry.getTool('glob'); @@ -1152,8 +1158,11 @@ export class Session { let currentPathSpec = pathName; let resolvedSuccessfully = false; try { - const absolutePath = path.resolve(this.config.getTargetDir(), pathName); - if (isWithinRoot(absolutePath, this.config.getTargetDir())) { + const absolutePath = path.resolve( + this.context.config.getTargetDir(), + pathName, + ); + if (isWithinRoot(absolutePath, this.context.config.getTargetDir())) { const stats = await fs.stat(absolutePath); if (stats.isDirectory()) { currentPathSpec = pathName.endsWith('/') @@ -1173,7 +1182,7 @@ export class Session { } } catch (error) { if (isNodeError(error) && error.code === 'ENOENT') { - if (this.config.getEnableRecursiveFileSearch() && globTool) { + if (this.context.config.getEnableRecursiveFileSearch() && globTool) { this.debug( `Path ${pathName} not found directly, attempting glob search.`, ); @@ -1181,7 +1190,7 @@ export class Session { const globResult = await globTool.buildAndExecute( { pattern: `**/*${pathName}*`, - path: this.config.getTargetDir(), + path: this.context.config.getTargetDir(), }, abortSignal, ); @@ -1195,7 +1204,7 @@ export class Session { if (lines.length > 1 && lines[1]) { const firstMatchAbsolute = lines[1].trim(); currentPathSpec = path.relative( - this.config.getTargetDir(), + this.context.config.getTargetDir(), firstMatchAbsolute, ); this.debug( @@ -1410,7 +1419,7 @@ export class Session { } debug(msg: string) { - if (this.config.getDebugMode()) { + if (this.context.config.getDebugMode()) { debugLogger.warn(msg); } } diff --git a/packages/cli/src/acp/acpResume.test.ts b/packages/cli/src/acp/acpResume.test.ts index 9668ef74f8..77021004ca 100644 --- a/packages/cli/src/acp/acpResume.test.ts +++ b/packages/cli/src/acp/acpResume.test.ts @@ -97,6 +97,9 @@ describe('GeminiAgent Session Resume', () => { getHasAccessToPreviewModel: vi.fn().mockReturnValue(false), getGemini31LaunchedSync: vi.fn().mockReturnValue(false), getCheckpointingEnabled: vi.fn().mockReturnValue(false), + get config() { + return this; + }, } as unknown as Mocked; mockSettings = { merged: { @@ -158,9 +161,10 @@ describe('GeminiAgent Session Resume', () => { ], }; - mockConfig.getToolRegistry = vi.fn().mockReturnValue({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (mockConfig as any).toolRegistry = { getTool: vi.fn().mockReturnValue({ kind: 'read' }), - }); + }; (SessionSelector as unknown as Mock).mockImplementation(() => ({ resolveSession: vi.fn().mockResolvedValue({ diff --git a/packages/cli/src/acp/commands/extensions.ts b/packages/cli/src/acp/commands/extensions.ts index c2bd0e7190..a6e08f9bbc 100644 --- a/packages/cli/src/acp/commands/extensions.ts +++ b/packages/cli/src/acp/commands/extensions.ts @@ -53,7 +53,7 @@ export class ListExtensionsCommand implements Command { context: CommandContext, _: string[], ): Promise { - const extensions = listExtensions(context.config); + const extensions = listExtensions(context.agentContext.config); const data = extensions.length ? extensions : 'No extensions installed.'; return { name: this.name, data }; @@ -134,7 +134,7 @@ export class EnableExtensionCommand implements Command { args: string[], ): Promise { const enableContext = getEnableDisableContext( - context.config, + context.agentContext.config, args, 'enable', ); @@ -156,7 +156,8 @@ export class EnableExtensionCommand implements Command { if (extension?.mcpServers) { const mcpEnablementManager = McpServerEnablementManager.getInstance(); - const mcpClientManager = context.config.getMcpClientManager(); + const mcpClientManager = + context.agentContext.config.getMcpClientManager(); const enabledServers = await mcpEnablementManager.autoEnableServers( Object.keys(extension.mcpServers), ); @@ -191,7 +192,7 @@ export class DisableExtensionCommand implements Command { args: string[], ): Promise { const enableContext = getEnableDisableContext( - context.config, + context.agentContext.config, args, 'disable', ); @@ -223,7 +224,7 @@ export class InstallExtensionCommand implements Command { context: CommandContext, args: string[], ): Promise { - const extensionLoader = context.config.getExtensionLoader(); + const extensionLoader = context.agentContext.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { return { name: this.name, @@ -268,7 +269,7 @@ export class LinkExtensionCommand implements Command { context: CommandContext, args: string[], ): Promise { - const extensionLoader = context.config.getExtensionLoader(); + const extensionLoader = context.agentContext.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { return { name: this.name, @@ -313,7 +314,7 @@ export class UninstallExtensionCommand implements Command { context: CommandContext, args: string[], ): Promise { - const extensionLoader = context.config.getExtensionLoader(); + const extensionLoader = context.agentContext.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { return { name: this.name, @@ -369,7 +370,7 @@ export class RestartExtensionCommand implements Command { context: CommandContext, args: string[], ): Promise { - const extensionLoader = context.config.getExtensionLoader(); + const extensionLoader = context.agentContext.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { return { name: this.name, data: 'Cannot restart extensions.' }; } @@ -424,7 +425,7 @@ export class UpdateExtensionCommand implements Command { context: CommandContext, args: string[], ): Promise { - const extensionLoader = context.config.getExtensionLoader(); + const extensionLoader = context.agentContext.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { return { name: this.name, data: 'Cannot update extensions.' }; } diff --git a/packages/cli/src/acp/commands/init.ts b/packages/cli/src/acp/commands/init.ts index 5c4197f84c..a9104aa84f 100644 --- a/packages/cli/src/acp/commands/init.ts +++ b/packages/cli/src/acp/commands/init.ts @@ -22,7 +22,7 @@ export class InitCommand implements Command { context: CommandContext, _args: string[] = [], ): Promise { - const targetDir = context.config.getTargetDir(); + const targetDir = context.agentContext.config.getTargetDir(); if (!targetDir) { throw new Error('Command requires a workspace.'); } diff --git a/packages/cli/src/acp/commands/memory.ts b/packages/cli/src/acp/commands/memory.ts index f88aaac4f2..ac919f2a9b 100644 --- a/packages/cli/src/acp/commands/memory.ts +++ b/packages/cli/src/acp/commands/memory.ts @@ -49,7 +49,7 @@ export class ShowMemoryCommand implements Command { context: CommandContext, _: string[], ): Promise { - const result = showMemory(context.config); + const result = showMemory(context.agentContext.config); return { name: this.name, data: result.content }; } } @@ -63,7 +63,7 @@ export class RefreshMemoryCommand implements Command { context: CommandContext, _: string[], ): Promise { - const result = await refreshMemory(context.config); + const result = await refreshMemory(context.agentContext.config); return { name: this.name, data: result.content }; } } @@ -76,7 +76,7 @@ export class ListMemoryCommand implements Command { context: CommandContext, _: string[], ): Promise { - const result = listMemoryFiles(context.config); + const result = listMemoryFiles(context.agentContext.config); return { name: this.name, data: result.content }; } } @@ -95,7 +95,7 @@ export class AddMemoryCommand implements Command { return { name: this.name, data: result.content }; } - const toolRegistry = context.config.getToolRegistry(); + const toolRegistry = context.agentContext.toolRegistry; const tool = toolRegistry.getTool(result.toolName); if (tool) { const abortController = new AbortController(); @@ -106,10 +106,10 @@ export class AddMemoryCommand implements Command { await tool.buildAndExecute(result.toolArgs, signal, undefined, { shellExecutionConfig: { sanitizationConfig: DEFAULT_SANITIZATION_CONFIG, - sandboxManager: context.config.sandboxManager, + sandboxManager: context.agentContext.sandboxManager, }, }); - await refreshMemory(context.config); + await refreshMemory(context.agentContext.config); return { name: this.name, data: `Added memory: "${textToAdd}"`, diff --git a/packages/cli/src/acp/commands/restore.ts b/packages/cli/src/acp/commands/restore.ts index ec9166ed84..6898cff2e1 100644 --- a/packages/cli/src/acp/commands/restore.ts +++ b/packages/cli/src/acp/commands/restore.ts @@ -29,7 +29,8 @@ export class RestoreCommand implements Command { context: CommandContext, args: string[], ): Promise { - const { config, git: gitService } = context; + const { agentContext: agentContext, git: gitService } = context; + const { config } = agentContext; const argsStr = args.join(' '); try { @@ -116,7 +117,7 @@ export class ListCheckpointsCommand implements Command { readonly description = 'Lists all available checkpoints.'; async execute(context: CommandContext): Promise { - const { config } = context; + const { config } = context.agentContext; try { if (!config.getCheckpointingEnabled()) { diff --git a/packages/cli/src/acp/commands/types.ts b/packages/cli/src/acp/commands/types.ts index 099f0c923f..6f5656bd89 100644 --- a/packages/cli/src/acp/commands/types.ts +++ b/packages/cli/src/acp/commands/types.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Config, GitService } from '@google/gemini-cli-core'; +import type { AgentLoopContext, GitService } from '@google/gemini-cli-core'; import type { LoadedSettings } from '../../config/settings.js'; export interface CommandContext { - config: Config; + agentContext: AgentLoopContext; settings: LoadedSettings; git?: GitService; sendMessage: (text: string) => Promise; diff --git a/packages/cli/src/nonInteractiveCliCommands.ts b/packages/cli/src/nonInteractiveCliCommands.ts index e09db71312..35cf5105ab 100644 --- a/packages/cli/src/nonInteractiveCliCommands.ts +++ b/packages/cli/src/nonInteractiveCliCommands.ts @@ -65,9 +65,9 @@ export const handleSlashCommand = async ( const logger = new Logger(config?.getSessionId() || '', config?.storage); - const context: CommandContext = { + const commandContext: CommandContext = { services: { - config, + agentContext: config, settings, git: undefined, logger, @@ -84,7 +84,7 @@ export const handleSlashCommand = async ( }, }; - const result = await commandToExecute.action(context, args); + const result = await commandToExecute.action(commandContext, args); if (result) { switch (result.type) { diff --git a/packages/cli/src/services/prompt-processors/atFileProcessor.test.ts b/packages/cli/src/services/prompt-processors/atFileProcessor.test.ts index 3f49248169..3b84baae67 100644 --- a/packages/cli/src/services/prompt-processors/atFileProcessor.test.ts +++ b/packages/cli/src/services/prompt-processors/atFileProcessor.test.ts @@ -31,11 +31,14 @@ describe('AtFileProcessor', () => { mockConfig = { // The processor only passes the config through, so we don't need a full mock. + get config() { + return this; + }, } as unknown as Config; context = createMockCommandContext({ services: { - config: mockConfig, + agentContext: mockConfig, }, }); @@ -60,7 +63,7 @@ describe('AtFileProcessor', () => { const prompt: PartUnion[] = [{ text: 'Analyze @{file.txt}' }]; const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); const result = await processor.process(prompt, contextWithoutConfig); diff --git a/packages/cli/src/services/prompt-processors/atFileProcessor.ts b/packages/cli/src/services/prompt-processors/atFileProcessor.ts index 48e527ed5f..8c1b168584 100644 --- a/packages/cli/src/services/prompt-processors/atFileProcessor.ts +++ b/packages/cli/src/services/prompt-processors/atFileProcessor.ts @@ -25,7 +25,7 @@ export class AtFileProcessor implements IPromptProcessor { input: PromptPipelineContent, context: CommandContext, ): Promise { - const config = context.services.config; + const config = context.services.agentContext?.config; if (!config) { return input; } diff --git a/packages/cli/src/services/prompt-processors/shellProcessor.test.ts b/packages/cli/src/services/prompt-processors/shellProcessor.test.ts index 84010ab625..8ab4581228 100644 --- a/packages/cli/src/services/prompt-processors/shellProcessor.test.ts +++ b/packages/cli/src/services/prompt-processors/shellProcessor.test.ts @@ -89,6 +89,9 @@ describe('ShellProcessor', () => { getPolicyEngine: vi.fn().mockReturnValue({ check: mockPolicyEngineCheck, }), + get config() { + return this as unknown as Config; + }, }; context = createMockCommandContext({ @@ -98,7 +101,7 @@ describe('ShellProcessor', () => { args: 'default args', }, services: { - config: mockConfig as Config, + agentContext: mockConfig as Config, }, session: { sessionShellAllowlist: new Set(), @@ -120,7 +123,7 @@ describe('ShellProcessor', () => { const prompt: PromptPipelineContent = createPromptPipelineContent('!{ls}'); const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); diff --git a/packages/cli/src/services/prompt-processors/shellProcessor.ts b/packages/cli/src/services/prompt-processors/shellProcessor.ts index 4c8369f664..0042dc4f49 100644 --- a/packages/cli/src/services/prompt-processors/shellProcessor.ts +++ b/packages/cli/src/services/prompt-processors/shellProcessor.ts @@ -74,7 +74,7 @@ export class ShellProcessor implements IPromptProcessor { ]; } - const config = context.services.config; + const config = context.services.agentContext?.config; if (!config) { throw new Error( `Security configuration not loaded. Cannot verify shell command permissions for '${this.commandName}'. Aborting.`, diff --git a/packages/cli/src/test-utils/mockCommandContext.test.ts b/packages/cli/src/test-utils/mockCommandContext.test.ts index 310bf74864..605718e027 100644 --- a/packages/cli/src/test-utils/mockCommandContext.test.ts +++ b/packages/cli/src/test-utils/mockCommandContext.test.ts @@ -46,15 +46,19 @@ describe('createMockCommandContext', () => { const overrides = { services: { - config: mockConfig, + agentContext: { config: mockConfig }, }, }; const context = createMockCommandContext(overrides); - expect(context.services.config).toBeDefined(); - expect(context.services.config?.getModel()).toBe('gemini-pro'); - expect(context.services.config?.getProjectRoot()).toBe('/test/project'); + expect(context.services.agentContext).toBeDefined(); + expect(context.services.agentContext?.config?.getModel()).toBe( + 'gemini-pro', + ); + expect(context.services.agentContext?.config?.getProjectRoot()).toBe( + '/test/project', + ); // Verify a default property on the same nested object is still there expect(context.services.logger).toBeDefined(); diff --git a/packages/cli/src/test-utils/mockCommandContext.ts b/packages/cli/src/test-utils/mockCommandContext.ts index b153aaf85e..15e6422e1a 100644 --- a/packages/cli/src/test-utils/mockCommandContext.ts +++ b/packages/cli/src/test-utils/mockCommandContext.ts @@ -36,7 +36,7 @@ export const createMockCommandContext = ( args: '', }, services: { - config: null, + agentContext: null, settings: { merged: defaultMergedSettings, diff --git a/packages/cli/src/ui/commands/aboutCommand.test.ts b/packages/cli/src/ui/commands/aboutCommand.test.ts index f1c010678e..0fa1f709ba 100644 --- a/packages/cli/src/ui/commands/aboutCommand.test.ts +++ b/packages/cli/src/ui/commands/aboutCommand.test.ts @@ -36,10 +36,12 @@ describe('aboutCommand', () => { beforeEach(() => { mockContext = createMockCommandContext({ services: { - config: { - getModel: vi.fn(), - getIdeMode: vi.fn().mockReturnValue(true), - getUserTierName: vi.fn().mockReturnValue(undefined), + agentContext: { + config: { + getModel: vi.fn(), + getIdeMode: vi.fn().mockReturnValue(true), + getUserTierName: vi.fn().mockReturnValue(undefined), + }, }, settings: { merged: { @@ -57,9 +59,10 @@ describe('aboutCommand', () => { } as unknown as CommandContext); vi.mocked(getVersion).mockResolvedValue('test-version'); - vi.spyOn(mockContext.services.config!, 'getModel').mockReturnValue( - 'test-model', - ); + vi.spyOn( + mockContext.services.agentContext!.config, + 'getModel', + ).mockReturnValue('test-model'); process.env['GOOGLE_CLOUD_PROJECT'] = 'test-gcp-project'; Object.defineProperty(process, 'platform', { value: 'test-os', @@ -160,9 +163,9 @@ describe('aboutCommand', () => { }); it('should display the tier when getUserTierName returns a value', async () => { - vi.mocked(mockContext.services.config!.getUserTierName).mockReturnValue( - 'Enterprise Tier', - ); + vi.mocked( + mockContext.services.agentContext!.config.getUserTierName, + ).mockReturnValue('Enterprise Tier'); if (!aboutCommand.action) { throw new Error('The about command must have an action.'); } diff --git a/packages/cli/src/ui/commands/aboutCommand.ts b/packages/cli/src/ui/commands/aboutCommand.ts index afd1ada9cd..8b436d69b8 100644 --- a/packages/cli/src/ui/commands/aboutCommand.ts +++ b/packages/cli/src/ui/commands/aboutCommand.ts @@ -34,7 +34,8 @@ export const aboutCommand: SlashCommand = { process.env['SEATBELT_PROFILE'] || 'unknown' })`; } - const modelVersion = context.services.config?.getModel() || 'Unknown'; + const modelVersion = + context.services.agentContext?.config.getModel() || 'Unknown'; const cliVersion = await getVersion(); const selectedAuthType = context.services.settings.merged.security.auth.selectedType || ''; @@ -48,7 +49,7 @@ export const aboutCommand: SlashCommand = { }); const userEmail = cachedAccount ?? undefined; - const tier = context.services.config?.getUserTierName(); + const tier = context.services.agentContext?.config.getUserTierName(); const aboutItem: Omit = { type: MessageType.ABOUT, @@ -68,7 +69,7 @@ export const aboutCommand: SlashCommand = { }; async function getIdeClientName(context: CommandContext) { - if (!context.services.config?.getIdeMode()) { + if (!context.services.agentContext?.config.getIdeMode()) { return ''; } const ideClient = await IdeClient.getInstance(); diff --git a/packages/cli/src/ui/commands/agentsCommand.test.ts b/packages/cli/src/ui/commands/agentsCommand.test.ts index 5e6cc36efa..1a5de99122 100644 --- a/packages/cli/src/ui/commands/agentsCommand.test.ts +++ b/packages/cli/src/ui/commands/agentsCommand.test.ts @@ -26,6 +26,7 @@ describe('agentsCommand', () => { let mockContext: ReturnType; let mockConfig: { getAgentRegistry: ReturnType; + config: Config; }; beforeEach(() => { @@ -37,11 +38,14 @@ describe('agentsCommand', () => { getAllAgentNames: vi.fn().mockReturnValue([]), reload: vi.fn(), }), + get config() { + return this as unknown as Config; + }, }; mockContext = createMockCommandContext({ services: { - config: mockConfig as unknown as Config, + agentContext: mockConfig as unknown as Config, settings: { workspace: { path: '/mock/path' }, merged: { agents: { overrides: {} } }, @@ -53,7 +57,7 @@ describe('agentsCommand', () => { it('should show an error if config is not available', async () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); @@ -226,7 +230,7 @@ describe('agentsCommand', () => { it('should show an error if config is not available for enable', async () => { const contextWithoutConfig = createMockCommandContext({ - services: { config: null }, + services: { agentContext: null }, }); const enableCommand = agentsCommand.subCommands?.find( (cmd) => cmd.name === 'enable', @@ -332,7 +336,7 @@ describe('agentsCommand', () => { it('should show an error if config is not available for disable', async () => { const contextWithoutConfig = createMockCommandContext({ - services: { config: null }, + services: { agentContext: null }, }); const disableCommand = agentsCommand.subCommands?.find( (cmd) => cmd.name === 'disable', @@ -433,7 +437,7 @@ describe('agentsCommand', () => { it('should show an error if config is not available', async () => { const contextWithoutConfig = createMockCommandContext({ - services: { config: null }, + services: { agentContext: null }, }); const configCommand = agentsCommand.subCommands?.find( (cmd) => cmd.name === 'config', diff --git a/packages/cli/src/ui/commands/agentsCommand.ts b/packages/cli/src/ui/commands/agentsCommand.ts index 3658c741ff..d1b582d673 100644 --- a/packages/cli/src/ui/commands/agentsCommand.ts +++ b/packages/cli/src/ui/commands/agentsCommand.ts @@ -21,7 +21,7 @@ const agentsListCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context: CommandContext) => { - const { config } = context.services; + const config = context.services.agentContext?.config; if (!config) { return { type: 'message', @@ -61,7 +61,8 @@ async function enableAction( context: CommandContext, args: string, ): Promise { - const { config, settings } = context.services; + const config = context.services.agentContext?.config; + const { settings } = context.services; if (!config) { return { type: 'message', @@ -137,7 +138,8 @@ async function disableAction( context: CommandContext, args: string, ): Promise { - const { config, settings } = context.services; + const config = context.services.agentContext?.config; + const { settings } = context.services; if (!config) { return { type: 'message', @@ -216,7 +218,7 @@ async function configAction( context: CommandContext, args: string, ): Promise { - const { config } = context.services; + const config = context.services.agentContext?.config; if (!config) { return { type: 'message', @@ -266,7 +268,8 @@ async function configAction( } function completeAgentsToEnable(context: CommandContext, partialArg: string) { - const { config, settings } = context.services; + const config = context.services.agentContext?.config; + const { settings } = context.services; if (!config) return []; const overrides = settings.merged.agents.overrides; @@ -278,7 +281,7 @@ function completeAgentsToEnable(context: CommandContext, partialArg: string) { } function completeAgentsToDisable(context: CommandContext, partialArg: string) { - const { config } = context.services; + const config = context.services.agentContext?.config; if (!config) return []; const agentRegistry = config.getAgentRegistry(); @@ -287,7 +290,7 @@ function completeAgentsToDisable(context: CommandContext, partialArg: string) { } function completeAllAgents(context: CommandContext, partialArg: string) { - const { config } = context.services; + const config = context.services.agentContext?.config; if (!config) return []; const agentRegistry = config.getAgentRegistry(); @@ -328,7 +331,7 @@ const agentsReloadCommand: SlashCommand = { description: 'Reload the agent registry', kind: CommandKind.BUILT_IN, action: async (context: CommandContext) => { - const { config } = context.services; + const config = context.services.agentContext?.config; const agentRegistry = config?.getAgentRegistry(); if (!agentRegistry) { return { diff --git a/packages/cli/src/ui/commands/authCommand.test.ts b/packages/cli/src/ui/commands/authCommand.test.ts index 88e3273c8d..ff4f2ba614 100644 --- a/packages/cli/src/ui/commands/authCommand.test.ts +++ b/packages/cli/src/ui/commands/authCommand.test.ts @@ -9,6 +9,7 @@ import { authCommand } from './authCommand.js'; import { type CommandContext } from './types.js'; import { createMockCommandContext } from '../../test-utils/mockCommandContext.js'; import { SettingScope } from '../../config/settings.js'; +import type { GeminiClient } from '@google/gemini-cli-core'; vi.mock('@google/gemini-cli-core', async () => { const actual = await vi.importActual('@google/gemini-cli-core'); @@ -24,8 +25,10 @@ describe('authCommand', () => { beforeEach(() => { mockContext = createMockCommandContext({ services: { - config: { - getGeminiClient: vi.fn(), + agentContext: { + geminiClient: { + stripThoughtsFromHistory: vi.fn(), + }, }, }, }); @@ -101,17 +104,19 @@ describe('authCommand', () => { const mockStripThoughts = vi.fn(); const mockClient = { stripThoughtsFromHistory: mockStripThoughts, - } as unknown as ReturnType< - NonNullable['getGeminiClient'] - >; - - if (mockContext.services.config) { - mockContext.services.config.getGeminiClient = vi.fn(() => mockClient); + } as unknown as GeminiClient; + if (mockContext.services.agentContext?.config) { + mockContext.services.agentContext.config.getGeminiClient = vi.fn( + () => mockClient, + ); } await logoutCommand!.action!(mockContext, ''); - expect(mockStripThoughts).toHaveBeenCalled(); + expect( + mockContext.services.agentContext?.geminiClient + .stripThoughtsFromHistory, + ).toHaveBeenCalled(); }); it('should return logout action to signal explicit state change', async () => { @@ -123,7 +128,7 @@ describe('authCommand', () => { it('should handle missing config gracefully', async () => { const logoutCommand = authCommand.subCommands?.[1]; - mockContext.services.config = null; + mockContext.services.agentContext = null; const result = await logoutCommand!.action!(mockContext, ''); diff --git a/packages/cli/src/ui/commands/authCommand.ts b/packages/cli/src/ui/commands/authCommand.ts index 80c432894c..084763058c 100644 --- a/packages/cli/src/ui/commands/authCommand.ts +++ b/packages/cli/src/ui/commands/authCommand.ts @@ -39,7 +39,7 @@ const authLogoutCommand: SlashCommand = { undefined, ); // Strip thoughts from history instead of clearing completely - context.services.config?.getGeminiClient()?.stripThoughtsFromHistory(); + context.services.agentContext?.geminiClient.stripThoughtsFromHistory(); // Return logout action to signal explicit state change return { type: 'logout', diff --git a/packages/cli/src/ui/commands/bugCommand.test.ts b/packages/cli/src/ui/commands/bugCommand.test.ts index 88db905e77..c2c1a9a1d6 100644 --- a/packages/cli/src/ui/commands/bugCommand.test.ts +++ b/packages/cli/src/ui/commands/bugCommand.test.ts @@ -83,16 +83,18 @@ describe('bugCommand', () => { it('should generate the default GitHub issue URL', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getModel: () => 'gemini-pro', - getBugCommand: () => undefined, - getIdeMode: () => true, - getGeminiClient: () => ({ + agentContext: { + config: { + getModel: () => 'gemini-pro', + getBugCommand: () => undefined, + getIdeMode: () => true, + getContentGeneratorConfig: () => ({ authType: 'oauth-personal' }), + }, + geminiClient: { getChat: () => ({ getHistory: () => [], }), - }), - getContentGeneratorConfig: () => ({ authType: 'oauth-personal' }), + }, }, }, }); @@ -126,18 +128,20 @@ describe('bugCommand', () => { ]; const mockContext = createMockCommandContext({ services: { - config: { - getModel: () => 'gemini-pro', - getBugCommand: () => undefined, - getIdeMode: () => true, - getGeminiClient: () => ({ + agentContext: { + config: { + getModel: () => 'gemini-pro', + getBugCommand: () => undefined, + getIdeMode: () => true, + getContentGeneratorConfig: () => ({ authType: 'vertex-ai' }), + storage: { + getProjectTempDir: () => '/tmp/gemini', + }, + }, + geminiClient: { getChat: () => ({ getHistory: () => history, }), - }), - getContentGeneratorConfig: () => ({ authType: 'vertex-ai' }), - storage: { - getProjectTempDir: () => '/tmp/gemini', }, }, }, @@ -172,16 +176,18 @@ describe('bugCommand', () => { 'https://internal.bug-tracker.com/new?desc={title}&details={info}'; const mockContext = createMockCommandContext({ services: { - config: { - getModel: () => 'gemini-pro', - getBugCommand: () => ({ urlTemplate: customTemplate }), - getIdeMode: () => true, - getGeminiClient: () => ({ + agentContext: { + config: { + getModel: () => 'gemini-pro', + getBugCommand: () => ({ urlTemplate: customTemplate }), + getIdeMode: () => true, + getContentGeneratorConfig: () => ({ authType: 'vertex-ai' }), + }, + geminiClient: { getChat: () => ({ getHistory: () => [], }), - }), - getContentGeneratorConfig: () => ({ authType: 'vertex-ai' }), + }, }, }, }); diff --git a/packages/cli/src/ui/commands/bugCommand.ts b/packages/cli/src/ui/commands/bugCommand.ts index 26ddb7e850..134bccc9f0 100644 --- a/packages/cli/src/ui/commands/bugCommand.ts +++ b/packages/cli/src/ui/commands/bugCommand.ts @@ -32,8 +32,8 @@ export const bugCommand: SlashCommand = { autoExecute: false, action: async (context: CommandContext, args?: string): Promise => { const bugDescription = (args || '').trim(); - const { config } = context.services; - + const agentContext = context.services.agentContext; + const config = agentContext?.config; const osVersion = `${process.platform} ${process.version}`; let sandboxEnv = 'no sandbox'; if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') { @@ -73,7 +73,7 @@ export const bugCommand: SlashCommand = { info += `* **IDE Client:** ${ideClient}\n`; } - const chat = config?.getGeminiClient()?.getChat(); + const chat = agentContext?.geminiClient?.getChat(); const history = chat?.getHistory() || []; let historyFileMessage = ''; let problemValue = bugDescription; @@ -134,7 +134,7 @@ export const bugCommand: SlashCommand = { }; async function getIdeClientName(context: CommandContext) { - if (!context.services.config?.getIdeMode()) { + if (!context.services.agentContext?.config.getIdeMode()) { return ''; } const ideClient = await IdeClient.getInstance(); diff --git a/packages/cli/src/ui/commands/chatCommand.test.ts b/packages/cli/src/ui/commands/chatCommand.test.ts index c0288fbef2..04d0753ee8 100644 --- a/packages/cli/src/ui/commands/chatCommand.test.ts +++ b/packages/cli/src/ui/commands/chatCommand.test.ts @@ -70,18 +70,19 @@ describe('chatCommand', () => { mockContext = createMockCommandContext({ services: { - config: { - getProjectRoot: () => '/project/root', - getGeminiClient: () => - ({ - getChat: mockGetChat, - }) as unknown as GeminiClient, - storage: { - getProjectTempDir: () => '/project/root/.gemini/tmp/mockhash', + agentContext: { + config: { + getProjectRoot: () => '/project/root', + getContentGeneratorConfig: () => ({ + authType: AuthType.LOGIN_WITH_GOOGLE, + }), + storage: { + getProjectTempDir: () => '/project/root/.gemini/tmp/mockhash', + }, }, - getContentGeneratorConfig: () => ({ - authType: AuthType.LOGIN_WITH_GOOGLE, - }), + geminiClient: { + getChat: mockGetChat, + } as unknown as GeminiClient, }, logger: { saveCheckpoint: mockSaveCheckpoint, @@ -698,7 +699,11 @@ Hi there!`; beforeEach(() => { mockGetLatestApiRequest = vi.fn(); - mockContext.services.config!.getLatestApiRequest = + if (!mockContext.services.agentContext!.config) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (mockContext.services.agentContext!.config as any) = {}; + } + mockContext.services.agentContext!.config.getLatestApiRequest = mockGetLatestApiRequest; vi.spyOn(process, 'cwd').mockReturnValue('/project/root'); vi.spyOn(Date, 'now').mockReturnValue(1234567890); diff --git a/packages/cli/src/ui/commands/chatCommand.ts b/packages/cli/src/ui/commands/chatCommand.ts index 8b38204aa2..87aacb056b 100644 --- a/packages/cli/src/ui/commands/chatCommand.ts +++ b/packages/cli/src/ui/commands/chatCommand.ts @@ -35,7 +35,7 @@ const getSavedChatTags = async ( context: CommandContext, mtSortDesc: boolean, ): Promise => { - const cfg = context.services.config; + const cfg = context.services.agentContext?.config; const geminiDir = cfg?.storage?.getProjectTempDir(); if (!geminiDir) { return []; @@ -103,7 +103,8 @@ const saveCommand: SlashCommand = { }; } - const { logger, config } = context.services; + const { logger } = context.services; + const config = context.services.agentContext?.config; await logger.initialize(); if (!context.overwriteConfirmed) { @@ -125,7 +126,7 @@ const saveCommand: SlashCommand = { } } - const chat = config?.getGeminiClient()?.getChat(); + const chat = context.services.agentContext?.geminiClient?.getChat(); if (!chat) { return { type: 'message', @@ -172,7 +173,8 @@ const resumeCheckpointCommand: SlashCommand = { }; } - const { logger, config } = context.services; + const { logger } = context.services; + const config = context.services.agentContext?.config; await logger.initialize(); const checkpoint = await logger.loadCheckpoint(tag); const conversation = checkpoint.history; @@ -298,7 +300,7 @@ const shareCommand: SlashCommand = { }; } - const chat = context.services.config?.getGeminiClient()?.getChat(); + const chat = context.services.agentContext?.geminiClient?.getChat(); if (!chat) { return { type: 'message', @@ -344,7 +346,7 @@ export const debugCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context): Promise => { - const req = context.services.config?.getLatestApiRequest(); + const req = context.services.agentContext?.config.getLatestApiRequest(); if (!req) { return { type: 'message', diff --git a/packages/cli/src/ui/commands/clearCommand.test.ts b/packages/cli/src/ui/commands/clearCommand.test.ts index 0072bebf27..77f6e4854d 100644 --- a/packages/cli/src/ui/commands/clearCommand.test.ts +++ b/packages/cli/src/ui/commands/clearCommand.test.ts @@ -36,24 +36,25 @@ describe('clearCommand', () => { mockContext = createMockCommandContext({ services: { - config: { - getGeminiClient: () => - ({ - resetChat: mockResetChat, - getChat: () => ({ - getChatRecordingService: mockGetChatRecordingService, - }), - }) as unknown as GeminiClient, - setSessionId: vi.fn(), - getEnableHooks: vi.fn().mockReturnValue(false), - getMessageBus: vi.fn().mockReturnValue(undefined), - getHookSystem: vi.fn().mockReturnValue({ - fireSessionEndEvent: vi.fn().mockResolvedValue(undefined), - fireSessionStartEvent: vi.fn().mockResolvedValue(undefined), - }), - injectionService: { - clear: mockHintClear, + agentContext: { + config: { + getEnableHooks: vi.fn().mockReturnValue(false), + setSessionId: vi.fn(), + getMessageBus: vi.fn().mockReturnValue(undefined), + getHookSystem: vi.fn().mockReturnValue({ + fireSessionEndEvent: vi.fn().mockResolvedValue(undefined), + fireSessionStartEvent: vi.fn().mockResolvedValue(undefined), + }), + injectionService: { + clear: mockHintClear, + }, }, + geminiClient: { + resetChat: mockResetChat, + getChat: () => ({ + getChatRecordingService: mockGetChatRecordingService, + }), + } as unknown as GeminiClient, }, }, }); @@ -98,7 +99,7 @@ describe('clearCommand', () => { const nullConfigContext = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); diff --git a/packages/cli/src/ui/commands/clearCommand.ts b/packages/cli/src/ui/commands/clearCommand.ts index 05eb96193f..061c4f9085 100644 --- a/packages/cli/src/ui/commands/clearCommand.ts +++ b/packages/cli/src/ui/commands/clearCommand.ts @@ -20,8 +20,8 @@ export const clearCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context, _args) => { - const geminiClient = context.services.config?.getGeminiClient(); - const config = context.services.config; + const geminiClient = context.services.agentContext?.geminiClient; + const config = context.services.agentContext?.config; // Fire SessionEnd hook before clearing const hookSystem = config?.getHookSystem(); diff --git a/packages/cli/src/ui/commands/compressCommand.test.ts b/packages/cli/src/ui/commands/compressCommand.test.ts index 5fd6f8dc6a..fd60b54354 100644 --- a/packages/cli/src/ui/commands/compressCommand.test.ts +++ b/packages/cli/src/ui/commands/compressCommand.test.ts @@ -22,11 +22,10 @@ describe('compressCommand', () => { mockTryCompressChat = vi.fn(); context = createMockCommandContext({ services: { - config: { - getGeminiClient: () => - ({ - tryCompressChat: mockTryCompressChat, - }) as unknown as GeminiClient, + agentContext: { + geminiClient: { + tryCompressChat: mockTryCompressChat, + } as unknown as GeminiClient, }, }, }); diff --git a/packages/cli/src/ui/commands/compressCommand.ts b/packages/cli/src/ui/commands/compressCommand.ts index a52e75ab32..6d53667010 100644 --- a/packages/cli/src/ui/commands/compressCommand.ts +++ b/packages/cli/src/ui/commands/compressCommand.ts @@ -39,9 +39,11 @@ export const compressCommand: SlashCommand = { try { ui.setPendingItem(pendingMessage); const promptId = `compress-${Date.now()}`; - const compressed = await context.services.config - ?.getGeminiClient() - ?.tryCompressChat(promptId, true); + const compressed = + await context.services.agentContext?.geminiClient?.tryCompressChat( + promptId, + true, + ); if (compressed) { ui.addItem( { diff --git a/packages/cli/src/ui/commands/copyCommand.test.ts b/packages/cli/src/ui/commands/copyCommand.test.ts index 611162fe20..6a1d36ca21 100644 --- a/packages/cli/src/ui/commands/copyCommand.test.ts +++ b/packages/cli/src/ui/commands/copyCommand.test.ts @@ -29,10 +29,10 @@ describe('copyCommand', () => { mockContext = createMockCommandContext({ services: { - config: { - getGeminiClient: () => ({ + agentContext: { + geminiClient: { getChat: mockGetChat, - }), + }, }, }, }); @@ -301,7 +301,7 @@ describe('copyCommand', () => { if (!copyCommand.action) throw new Error('Command has no action'); const nullConfigContext = createMockCommandContext({ - services: { config: null }, + services: { agentContext: null }, }); const result = await copyCommand.action(nullConfigContext, ''); diff --git a/packages/cli/src/ui/commands/copyCommand.ts b/packages/cli/src/ui/commands/copyCommand.ts index 0c01b252ec..746d6899a6 100644 --- a/packages/cli/src/ui/commands/copyCommand.ts +++ b/packages/cli/src/ui/commands/copyCommand.ts @@ -18,7 +18,7 @@ export const copyCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context, _args): Promise => { - const chat = context.services.config?.getGeminiClient()?.getChat(); + const chat = context.services.agentContext?.geminiClient?.getChat(); const history = chat?.getHistory(); // Get the last message from the AI (model role) diff --git a/packages/cli/src/ui/commands/directoryCommand.test.tsx b/packages/cli/src/ui/commands/directoryCommand.test.tsx index bdfa6ac3a0..837bc696b7 100644 --- a/packages/cli/src/ui/commands/directoryCommand.test.tsx +++ b/packages/cli/src/ui/commands/directoryCommand.test.tsx @@ -85,11 +85,14 @@ describe('directoryCommand', () => { getFileFilteringOptions: () => ({ ignore: [], include: [] }), setUserMemory: vi.fn(), setGeminiMdFileCount: vi.fn(), + get config() { + return this; + }, } as unknown as Config; mockContext = { services: { - config: mockConfig, + agentContext: mockConfig, settings: { merged: { memoryDiscoveryMaxDirs: 1000, diff --git a/packages/cli/src/ui/commands/directoryCommand.tsx b/packages/cli/src/ui/commands/directoryCommand.tsx index 70206410de..4106efa97b 100644 --- a/packages/cli/src/ui/commands/directoryCommand.tsx +++ b/packages/cli/src/ui/commands/directoryCommand.tsx @@ -60,7 +60,7 @@ async function finishAddingDirectories( } if (added.length > 0) { - const gemini = config.getGeminiClient(); + const gemini = config.geminiClient; if (gemini) { await gemini.addDirectoryContext(); @@ -110,9 +110,9 @@ export const directoryCommand: SlashCommand = { // Filter out existing directories let filteredSuggestions = suggestions; - if (context.services.config) { + if (context.services.agentContext?.config) { const workspaceContext = - context.services.config.getWorkspaceContext(); + context.services.agentContext.config.getWorkspaceContext(); const existingDirs = new Set( workspaceContext.getDirectories().map((dir) => path.resolve(dir)), ); @@ -144,11 +144,11 @@ export const directoryCommand: SlashCommand = { action: async (context: CommandContext, args: string) => { const { ui: { addItem }, - services: { config, settings }, + services: { agentContext, settings }, } = context; const [...rest] = args.split(' '); - if (!config) { + if (!agentContext) { addItem({ type: MessageType.ERROR, text: 'Configuration is not available.', @@ -156,7 +156,7 @@ export const directoryCommand: SlashCommand = { return; } - if (config.isRestrictiveSandbox()) { + if (agentContext.config.isRestrictiveSandbox()) { return { type: 'message' as const, messageType: 'error' as const, @@ -181,7 +181,7 @@ export const directoryCommand: SlashCommand = { const errors: string[] = []; const alreadyAdded: string[] = []; - const workspaceContext = config.getWorkspaceContext(); + const workspaceContext = agentContext.config.getWorkspaceContext(); const currentWorkspaceDirs = workspaceContext.getDirectories(); const pathsToProcess: string[] = []; @@ -252,7 +252,7 @@ export const directoryCommand: SlashCommand = { trustedDirs={added} errors={errors} finishAddingDirectories={finishAddingDirectories} - config={config} + config={agentContext.config} addItem={addItem} /> ), @@ -264,7 +264,12 @@ export const directoryCommand: SlashCommand = { errors.push(...result.errors); } - await finishAddingDirectories(config, addItem, added, errors); + await finishAddingDirectories( + agentContext.config, + addItem, + added, + errors, + ); return; }, }, @@ -275,16 +280,16 @@ export const directoryCommand: SlashCommand = { action: async (context: CommandContext) => { const { ui: { addItem }, - services: { config }, + services: { agentContext }, } = context; - if (!config) { + if (!agentContext) { addItem({ type: MessageType.ERROR, text: 'Configuration is not available.', }); return; } - const workspaceContext = config.getWorkspaceContext(); + const workspaceContext = agentContext.config.getWorkspaceContext(); const directories = workspaceContext.getDirectories(); const directoryList = directories.map((dir) => `- ${dir}`).join('\n'); addItem({ diff --git a/packages/cli/src/ui/commands/extensionsCommand.test.ts b/packages/cli/src/ui/commands/extensionsCommand.test.ts index d1c2ede5e8..dc49390c7e 100644 --- a/packages/cli/src/ui/commands/extensionsCommand.test.ts +++ b/packages/cli/src/ui/commands/extensionsCommand.test.ts @@ -161,14 +161,16 @@ describe('extensionsCommand', () => { mockContext = createMockCommandContext({ services: { - config: { - getExtensions: mockGetExtensions, - getExtensionLoader: vi.fn().mockReturnValue(mockExtensionLoader), - getWorkingDir: () => '/test/dir', - reloadSkills: mockReloadSkills, - getAgentRegistry: vi.fn().mockReturnValue({ - reload: mockReloadAgents, - }), + agentContext: { + config: { + getExtensions: mockGetExtensions, + getExtensionLoader: vi.fn().mockReturnValue(mockExtensionLoader), + getWorkingDir: () => '/test/dir', + reloadSkills: mockReloadSkills, + getAgentRegistry: vi.fn().mockReturnValue({ + reload: mockReloadAgents, + }), + }, }, }, ui: { @@ -917,7 +919,7 @@ describe('extensionsCommand', () => { expect(restartAction).not.toBeNull(); mockRestartExtension = vi.fn(); - mockContext.services.config!.getExtensionLoader = vi + mockContext.services.agentContext!.config.getExtensionLoader = vi .fn() .mockImplementation(() => ({ getExtensions: mockGetExtensions, @@ -927,7 +929,7 @@ describe('extensionsCommand', () => { }); it('should show a message if no extensions are installed', async () => { - mockContext.services.config!.getExtensionLoader = vi + mockContext.services.agentContext!.config.getExtensionLoader = vi .fn() .mockImplementation(() => ({ getExtensions: () => [], @@ -1017,7 +1019,7 @@ describe('extensionsCommand', () => { }); it('shows an error if no extension loader is available', async () => { - mockContext.services.config!.getExtensionLoader = vi.fn(); + mockContext.services.agentContext!.config.getExtensionLoader = vi.fn(); await restartAction!(mockContext, '--all'); diff --git a/packages/cli/src/ui/commands/extensionsCommand.ts b/packages/cli/src/ui/commands/extensionsCommand.ts index 8fe206bfc4..8e988917e5 100644 --- a/packages/cli/src/ui/commands/extensionsCommand.ts +++ b/packages/cli/src/ui/commands/extensionsCommand.ts @@ -54,8 +54,8 @@ function showMessageIfNoExtensions( } async function listAction(context: CommandContext) { - const extensions = context.services.config - ? listExtensions(context.services.config) + const extensions = context.services.agentContext?.config + ? listExtensions(context.services.agentContext.config) : []; if (showMessageIfNoExtensions(context, extensions)) { @@ -88,8 +88,8 @@ function updateAction(context: CommandContext, args: string): Promise { (resolve) => (resolveUpdateComplete = resolve), ); - const extensions = context.services.config - ? listExtensions(context.services.config) + const extensions = context.services.agentContext?.config + ? listExtensions(context.services.agentContext.config) : []; if (showMessageIfNoExtensions(context, extensions)) { @@ -128,7 +128,7 @@ function updateAction(context: CommandContext, args: string): Promise { }, }); if (names?.length) { - const extensions = listExtensions(context.services.config!); + const extensions = listExtensions(context.services.agentContext!.config); for (const name of names) { const extension = extensions.find( (extension) => extension.name === name, @@ -156,7 +156,8 @@ async function restartAction( context: CommandContext, args: string, ): Promise { - const extensionLoader = context.services.config?.getExtensionLoader(); + const extensionLoader = + context.services.agentContext?.config.getExtensionLoader(); if (!extensionLoader) { context.ui.addItem({ type: MessageType.ERROR, @@ -235,8 +236,8 @@ async function restartAction( if (failures.length < extensionsToRestart.length) { try { - await context.services.config?.reloadSkills(); - await context.services.config?.getAgentRegistry()?.reload(); + await context.services.agentContext?.config.reloadSkills(); + await context.services.agentContext?.config.getAgentRegistry()?.reload(); } catch (error) { context.ui.addItem({ type: MessageType.ERROR, @@ -274,7 +275,8 @@ async function exploreAction( const useRegistryUI = settings.experimental?.extensionRegistry; if (useRegistryUI) { - const extensionManager = context.services.config?.getExtensionLoader(); + const extensionManager = + context.services.agentContext?.config.getExtensionLoader(); if (extensionManager instanceof ExtensionManager) { return { type: 'custom_dialog' as const, @@ -331,7 +333,8 @@ function getEnableDisableContext( names: string[]; scope: SettingScope; } | null { - const extensionLoader = context.services.config?.getExtensionLoader(); + const extensionLoader = + context.services.agentContext?.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { debugLogger.error( `Cannot ${context.invocation?.name} extensions in this environment`, @@ -431,7 +434,8 @@ async function enableAction(context: CommandContext, args: string) { if (extension?.mcpServers) { const mcpEnablementManager = McpServerEnablementManager.getInstance(); - const mcpClientManager = context.services.config?.getMcpClientManager(); + const mcpClientManager = + context.services.agentContext?.config.getMcpClientManager(); const enabledServers = await mcpEnablementManager.autoEnableServers( Object.keys(extension.mcpServers ?? {}), ); @@ -463,7 +467,8 @@ async function installAction( args: string, requestConsentOverride?: (consent: string) => Promise, ) { - const extensionLoader = context.services.config?.getExtensionLoader(); + const extensionLoader = + context.services.agentContext?.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { debugLogger.error( `Cannot ${context.invocation?.name} extensions in this environment`, @@ -529,7 +534,8 @@ async function installAction( } async function linkAction(context: CommandContext, args: string) { - const extensionLoader = context.services.config?.getExtensionLoader(); + const extensionLoader = + context.services.agentContext?.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { debugLogger.error( `Cannot ${context.invocation?.name} extensions in this environment`, @@ -593,7 +599,8 @@ async function linkAction(context: CommandContext, args: string) { } async function uninstallAction(context: CommandContext, args: string) { - const extensionLoader = context.services.config?.getExtensionLoader(); + const extensionLoader = + context.services.agentContext?.config.getExtensionLoader(); if (!(extensionLoader instanceof ExtensionManager)) { debugLogger.error( `Cannot ${context.invocation?.name} extensions in this environment`, @@ -692,7 +699,8 @@ async function configAction(context: CommandContext, args: string) { } } - const extensionManager = context.services.config?.getExtensionLoader(); + const extensionManager = + context.services.agentContext?.config.getExtensionLoader(); if (!(extensionManager instanceof ExtensionManager)) { debugLogger.error( `Cannot ${context.invocation?.name} extensions in this environment`, @@ -729,7 +737,7 @@ export function completeExtensions( context: CommandContext, partialArg: string, ) { - let extensions = context.services.config?.getExtensions() ?? []; + let extensions = context.services.agentContext?.config.getExtensions() ?? []; if (context.invocation?.name === 'enable') { extensions = extensions.filter((ext) => !ext.isActive); diff --git a/packages/cli/src/ui/commands/hooksCommand.test.ts b/packages/cli/src/ui/commands/hooksCommand.test.ts index 930658e1ab..0059f86105 100644 --- a/packages/cli/src/ui/commands/hooksCommand.test.ts +++ b/packages/cli/src/ui/commands/hooksCommand.test.ts @@ -93,7 +93,7 @@ describe('hooksCommand', () => { // Create mock context with config and settings mockContext = createMockCommandContext({ services: { - config: mockConfig, + agentContext: { config: mockConfig }, settings: mockSettings, }, }); @@ -141,7 +141,7 @@ describe('hooksCommand', () => { it('should return error when config is not loaded', async () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); @@ -225,7 +225,7 @@ describe('hooksCommand', () => { it('should return error when config is not loaded', async () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); @@ -338,7 +338,7 @@ describe('hooksCommand', () => { it('should return error when config is not loaded', async () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); @@ -470,7 +470,7 @@ describe('hooksCommand', () => { it('should return empty array when config is not available', () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); @@ -567,7 +567,7 @@ describe('hooksCommand', () => { it('should return error when config is not loaded', async () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); @@ -691,7 +691,7 @@ describe('hooksCommand', () => { it('should return error when config is not loaded', async () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); diff --git a/packages/cli/src/ui/commands/hooksCommand.ts b/packages/cli/src/ui/commands/hooksCommand.ts index bc51f42037..4bdc9ead54 100644 --- a/packages/cli/src/ui/commands/hooksCommand.ts +++ b/packages/cli/src/ui/commands/hooksCommand.ts @@ -27,7 +27,8 @@ import { HooksDialog } from '../components/HooksDialog.js'; function panelAction( context: CommandContext, ): MessageActionReturn | OpenCustomDialogActionReturn { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -55,7 +56,8 @@ async function enableAction( context: CommandContext, args: string, ): Promise { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -108,7 +110,8 @@ async function disableAction( context: CommandContext, args: string, ): Promise { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -163,7 +166,8 @@ function completeEnabledHookNames( context: CommandContext, partialArg: string, ): string[] { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) return []; const hookSystem = config.getHookSystem(); @@ -183,7 +187,8 @@ function completeDisabledHookNames( context: CommandContext, partialArg: string, ): string[] { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) return []; const hookSystem = config.getHookSystem(); @@ -209,7 +214,8 @@ function getHookDisplayName(hook: HookRegistryEntry): string { async function enableAllAction( context: CommandContext, ): Promise { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -280,7 +286,8 @@ async function enableAllAction( async function disableAllAction( context: CommandContext, ): Promise { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', diff --git a/packages/cli/src/ui/commands/ideCommand.test.ts b/packages/cli/src/ui/commands/ideCommand.test.ts index 1ddb55dc89..2cb880feaa 100644 --- a/packages/cli/src/ui/commands/ideCommand.test.ts +++ b/packages/cli/src/ui/commands/ideCommand.test.ts @@ -60,10 +60,12 @@ describe('ideCommand', () => { settings: { setValue: vi.fn(), }, - config: { - getIdeMode: vi.fn(), - setIdeMode: vi.fn(), - getUsageStatisticsEnabled: vi.fn().mockReturnValue(false), + agentContext: { + config: { + getIdeMode: vi.fn(), + setIdeMode: vi.fn(), + getUsageStatisticsEnabled: vi.fn().mockReturnValue(false), + }, }, }, } as unknown as CommandContext; diff --git a/packages/cli/src/ui/commands/ideCommand.ts b/packages/cli/src/ui/commands/ideCommand.ts index 1f726f90e5..df26fdf471 100644 --- a/packages/cli/src/ui/commands/ideCommand.ts +++ b/packages/cli/src/ui/commands/ideCommand.ts @@ -217,9 +217,13 @@ export const ideCommand = async (): Promise => { ); // Poll for up to 5 seconds for the extension to activate. for (let i = 0; i < 10; i++) { - await setIdeModeAndSyncConnection(context.services.config!, true, { - logToConsole: false, - }); + await setIdeModeAndSyncConnection( + context.services.agentContext!.config, + true, + { + logToConsole: false, + }, + ); if ( ideClient.getConnectionStatus().status === IDEConnectionStatus.Connected @@ -262,7 +266,10 @@ export const ideCommand = async (): Promise => { 'ide.enabled', true, ); - await setIdeModeAndSyncConnection(context.services.config!, true); + await setIdeModeAndSyncConnection( + context.services.agentContext!.config, + true, + ); const { messageType, content } = getIdeStatusMessage(ideClient); context.ui.addItem( { @@ -285,7 +292,10 @@ export const ideCommand = async (): Promise => { 'ide.enabled', false, ); - await setIdeModeAndSyncConnection(context.services.config!, false); + await setIdeModeAndSyncConnection( + context.services.agentContext!.config, + false, + ); const { messageType, content } = getIdeStatusMessage(ideClient); context.ui.addItem( { diff --git a/packages/cli/src/ui/commands/initCommand.test.ts b/packages/cli/src/ui/commands/initCommand.test.ts index 62991c7610..0e4f24a1fe 100644 --- a/packages/cli/src/ui/commands/initCommand.test.ts +++ b/packages/cli/src/ui/commands/initCommand.test.ts @@ -31,8 +31,10 @@ describe('initCommand', () => { // Create a fresh mock context for each test mockContext = createMockCommandContext({ services: { - config: { - getTargetDir: () => targetDir, + agentContext: { + config: { + getTargetDir: () => targetDir, + }, }, }, }); @@ -94,7 +96,7 @@ describe('initCommand', () => { // Arrange: Create a context without config const noConfigContext = createMockCommandContext(); if (noConfigContext.services) { - noConfigContext.services.config = null; + noConfigContext.services.agentContext = null; } // Act: Run the command's action diff --git a/packages/cli/src/ui/commands/initCommand.ts b/packages/cli/src/ui/commands/initCommand.ts index ea0d1ea0c6..d4d8040622 100644 --- a/packages/cli/src/ui/commands/initCommand.ts +++ b/packages/cli/src/ui/commands/initCommand.ts @@ -23,14 +23,14 @@ export const initCommand: SlashCommand = { context: CommandContext, _args: string, ): Promise => { - if (!context.services.config) { + if (!context.services.agentContext?.config) { return { type: 'message', messageType: 'error', content: 'Configuration not available.', }; } - const targetDir = context.services.config.getTargetDir(); + const targetDir = context.services.agentContext.config.getTargetDir(); const geminiMdPath = path.join(targetDir, 'GEMINI.md'); const result = performInit(fs.existsSync(geminiMdPath)); diff --git a/packages/cli/src/ui/commands/mcpCommand.test.ts b/packages/cli/src/ui/commands/mcpCommand.test.ts index 3acace0774..9a3254fbae 100644 --- a/packages/cli/src/ui/commands/mcpCommand.test.ts +++ b/packages/cli/src/ui/commands/mcpCommand.test.ts @@ -119,7 +119,10 @@ describe('mcpCommand', () => { mockContext = createMockCommandContext({ services: { - config: mockConfig, + agentContext: { + config: mockConfig, + toolRegistry: mockConfig.getToolRegistry(), + }, }, }); }); @@ -132,7 +135,7 @@ describe('mcpCommand', () => { it('should show an error if config is not available', async () => { const contextWithoutConfig = createMockCommandContext({ services: { - config: null, + agentContext: null, }, }); @@ -146,7 +149,8 @@ describe('mcpCommand', () => { }); it('should show an error if tool registry is not available', async () => { - mockConfig.getToolRegistry = vi.fn().mockReturnValue(undefined); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (mockContext.services.agentContext as any).toolRegistry = undefined; const result = await mcpCommand.action!(mockContext, ''); @@ -196,9 +200,13 @@ describe('mcpCommand', () => { ...mockServer3Tools, ]; - mockConfig.getToolRegistry = vi.fn().mockReturnValue({ + const mockToolRegistry = { getAllTools: vi.fn().mockReturnValue(allTools), - }); + }; + mockConfig.getToolRegistry = vi.fn().mockReturnValue(mockToolRegistry); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (mockContext.services.agentContext as any).toolRegistry = + mockToolRegistry; const resourcesByServer: Record< string, diff --git a/packages/cli/src/ui/commands/mcpCommand.ts b/packages/cli/src/ui/commands/mcpCommand.ts index 9ccaaf4273..0fb6b5a1dd 100644 --- a/packages/cli/src/ui/commands/mcpCommand.ts +++ b/packages/cli/src/ui/commands/mcpCommand.ts @@ -42,8 +42,8 @@ const authCommand: SlashCommand = { args: string, ): Promise => { const serverName = args.trim(); - const { config } = context.services; - + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -138,7 +138,7 @@ const authCommand: SlashCommand = { await mcpClientManager.restartServer(serverName); } // Update the client with the new tools - const geminiClient = config.getGeminiClient(); + const geminiClient = context.services.agentContext?.geminiClient; if (geminiClient?.isInitialized()) { await geminiClient.setTools(); } @@ -162,7 +162,8 @@ const authCommand: SlashCommand = { } }, completion: async (context: CommandContext, partialArg: string) => { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) return []; const mcpServers = config.getMcpClientManager()?.getMcpServers() || {}; @@ -177,7 +178,8 @@ const listAction = async ( showDescriptions = false, showSchema = false, ): Promise => { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -188,7 +190,7 @@ const listAction = async ( config.setUserInteractedWithMcp(); - const toolRegistry = config.getToolRegistry(); + const toolRegistry = agentContext.toolRegistry; if (!toolRegistry) { return { type: 'message', @@ -334,7 +336,8 @@ const reloadCommand: SlashCommand = { action: async ( context: CommandContext, ): Promise => { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -360,7 +363,7 @@ const reloadCommand: SlashCommand = { await mcpClientManager.restart(); // Update the client with the new tools - const geminiClient = config.getGeminiClient(); + const geminiClient = agentContext.geminiClient; if (geminiClient?.isInitialized()) { await geminiClient.setTools(); } @@ -377,7 +380,8 @@ async function handleEnableDisable( args: string, enable: boolean, ): Promise { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { return { type: 'message', @@ -465,8 +469,8 @@ async function handleEnableDisable( ); await mcpClientManager.restart(); } - if (config.getGeminiClient()?.isInitialized()) - await config.getGeminiClient().setTools(); + if (agentContext.geminiClient?.isInitialized()) + await agentContext.geminiClient.setTools(); context.ui.reloadCommands(); return { type: 'message', messageType: 'info', content: msg }; @@ -477,7 +481,8 @@ async function getEnablementCompletion( partialArg: string, showEnabled: boolean, ): Promise { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) return []; const servers = Object.keys( config.getMcpClientManager()?.getMcpServers() || {}, diff --git a/packages/cli/src/ui/commands/memoryCommand.test.ts b/packages/cli/src/ui/commands/memoryCommand.test.ts index 4e70054fac..f02393bef2 100644 --- a/packages/cli/src/ui/commands/memoryCommand.test.ts +++ b/packages/cli/src/ui/commands/memoryCommand.test.ts @@ -102,10 +102,12 @@ describe('memoryCommand', () => { mockContext = createMockCommandContext({ services: { - config: { - getUserMemory: mockGetUserMemory, - getGeminiMdFileCount: mockGetGeminiMdFileCount, - getExtensionLoader: () => new SimpleExtensionLoader([]), + agentContext: { + config: { + getUserMemory: mockGetUserMemory, + getGeminiMdFileCount: mockGetGeminiMdFileCount, + getExtensionLoader: () => new SimpleExtensionLoader([]), + }, }, }, }); @@ -250,7 +252,7 @@ describe('memoryCommand', () => { mockContext = createMockCommandContext({ services: { - config: mockConfig, + agentContext: { config: mockConfig }, settings: { merged: { memoryDiscoveryMaxDirs: 1000, @@ -268,7 +270,7 @@ describe('memoryCommand', () => { if (!reloadCommand.action) throw new Error('Command has no action'); // Enable JIT in mock config - const config = mockContext.services.config; + const config = mockContext.services.agentContext?.config; if (!config) throw new Error('Config is undefined'); vi.mocked(config.isJitContextEnabled).mockReturnValue(true); @@ -370,7 +372,7 @@ describe('memoryCommand', () => { if (!reloadCommand.action) throw new Error('Command has no action'); const nullConfigContext = createMockCommandContext({ - services: { config: null }, + services: { agentContext: null }, }); await expect( @@ -413,8 +415,10 @@ describe('memoryCommand', () => { }); mockContext = createMockCommandContext({ services: { - config: { - getGeminiMdFilePaths: mockGetGeminiMdfilePaths, + agentContext: { + config: { + getGeminiMdFilePaths: mockGetGeminiMdfilePaths, + }, }, }, }); diff --git a/packages/cli/src/ui/commands/memoryCommand.ts b/packages/cli/src/ui/commands/memoryCommand.ts index 44c632c67a..145fbae9c3 100644 --- a/packages/cli/src/ui/commands/memoryCommand.ts +++ b/packages/cli/src/ui/commands/memoryCommand.ts @@ -29,7 +29,7 @@ export const memoryCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context) => { - const config = context.services.config; + const config = context.services.agentContext?.config; if (!config) return; const result = showMemory(config); @@ -81,7 +81,7 @@ export const memoryCommand: SlashCommand = { ); try { - const config = context.services.config; + const config = context.services.agentContext?.config; if (config) { const result = await refreshMemory(config); @@ -111,7 +111,7 @@ export const memoryCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context) => { - const config = context.services.config; + const config = context.services.agentContext?.config; if (!config) return; const result = listMemoryFiles(config); diff --git a/packages/cli/src/ui/commands/modelCommand.test.ts b/packages/cli/src/ui/commands/modelCommand.test.ts index 89938eb037..aa2359d8fa 100644 --- a/packages/cli/src/ui/commands/modelCommand.test.ts +++ b/packages/cli/src/ui/commands/modelCommand.test.ts @@ -37,8 +37,11 @@ describe('modelCommand', () => { } const mockRefreshUserQuota = vi.fn(); - mockContext.services.config = { + mockContext.services.agentContext = { refreshUserQuota: mockRefreshUserQuota, + get config() { + return this; + }, } as unknown as Config; await modelCommand.action(mockContext, ''); @@ -66,8 +69,11 @@ describe('modelCommand', () => { (c) => c.name === 'manage', ); const mockRefreshUserQuota = vi.fn(); - mockContext.services.config = { + mockContext.services.agentContext = { refreshUserQuota: mockRefreshUserQuota, + get config() { + return this; + }, } as unknown as Config; await manageCommand!.action!(mockContext, ''); @@ -84,7 +90,7 @@ describe('modelCommand', () => { expect(setCommand).toBeDefined(); const mockSetModel = vi.fn(); - mockContext.services.config = { + mockContext.services.agentContext = { setModel: mockSetModel, getHasAccessToPreviewModel: vi.fn().mockReturnValue(true), getUserId: vi.fn().mockReturnValue('test-user'), @@ -98,6 +104,9 @@ describe('modelCommand', () => { getPolicyEngine: vi.fn().mockReturnValue({ getApprovalMode: vi.fn().mockReturnValue('auto'), }), + get config() { + return this; + }, } as unknown as Config; await setCommand!.action!(mockContext, 'gemini-pro'); @@ -116,7 +125,7 @@ describe('modelCommand', () => { (c) => c.name === 'set', ); const mockSetModel = vi.fn(); - mockContext.services.config = { + mockContext.services.agentContext = { setModel: mockSetModel, getHasAccessToPreviewModel: vi.fn().mockReturnValue(true), getUserId: vi.fn().mockReturnValue('test-user'), @@ -130,6 +139,9 @@ describe('modelCommand', () => { getPolicyEngine: vi.fn().mockReturnValue({ getApprovalMode: vi.fn().mockReturnValue('auto'), }), + get config() { + return this; + }, } as unknown as Config; await setCommand!.action!(mockContext, 'gemini-pro --persist'); diff --git a/packages/cli/src/ui/commands/modelCommand.ts b/packages/cli/src/ui/commands/modelCommand.ts index ead7e521c5..facaba81ba 100644 --- a/packages/cli/src/ui/commands/modelCommand.ts +++ b/packages/cli/src/ui/commands/modelCommand.ts @@ -34,10 +34,10 @@ const setModelCommand: SlashCommand = { const modelName = parts[0]; const persist = parts.includes('--persist'); - if (context.services.config) { - context.services.config.setModel(modelName, !persist); + if (context.services.agentContext?.config) { + context.services.agentContext.config.setModel(modelName, !persist); const event = new ModelSlashCommandEvent(modelName); - logModelSlashCommand(context.services.config, event); + logModelSlashCommand(context.services.agentContext.config, event); context.ui.addItem({ type: MessageType.INFO, @@ -53,8 +53,8 @@ const manageModelCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context: CommandContext) => { - if (context.services.config) { - await context.services.config.refreshUserQuota(); + if (context.services.agentContext?.config) { + await context.services.agentContext.config.refreshUserQuota(); } return { type: 'dialog', diff --git a/packages/cli/src/ui/commands/oncallCommand.tsx b/packages/cli/src/ui/commands/oncallCommand.tsx index ba4cbe4835..23236ea49c 100644 --- a/packages/cli/src/ui/commands/oncallCommand.tsx +++ b/packages/cli/src/ui/commands/oncallCommand.tsx @@ -24,7 +24,8 @@ export const oncallCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context, args): Promise => { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { throw new Error('Config not available'); } @@ -56,7 +57,8 @@ export const oncallCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context, args): Promise => { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { throw new Error('Config not available'); } diff --git a/packages/cli/src/ui/commands/planCommand.test.ts b/packages/cli/src/ui/commands/planCommand.test.ts index fab1267b17..49c00ce8bd 100644 --- a/packages/cli/src/ui/commands/planCommand.test.ts +++ b/packages/cli/src/ui/commands/planCommand.test.ts @@ -52,14 +52,16 @@ describe('planCommand', () => { beforeEach(() => { mockContext = createMockCommandContext({ services: { - config: { - isPlanEnabled: vi.fn(), - setApprovalMode: vi.fn(), - getApprovedPlanPath: vi.fn(), - getApprovalMode: vi.fn(), - getFileSystemService: vi.fn(), - storage: { - getPlansDir: vi.fn().mockReturnValue('/mock/plans/dir'), + agentContext: { + config: { + isPlanEnabled: vi.fn(), + setApprovalMode: vi.fn(), + getApprovedPlanPath: vi.fn(), + getApprovalMode: vi.fn(), + getFileSystemService: vi.fn(), + storage: { + getPlansDir: vi.fn().mockReturnValue('/mock/plans/dir'), + }, }, }, }, @@ -83,17 +85,19 @@ describe('planCommand', () => { }); it('should switch to plan mode if enabled', async () => { - vi.mocked(mockContext.services.config!.isPlanEnabled).mockReturnValue(true); - vi.mocked(mockContext.services.config!.getApprovedPlanPath).mockReturnValue( - undefined, - ); + vi.mocked( + mockContext.services.agentContext!.config.isPlanEnabled, + ).mockReturnValue(true); + vi.mocked( + mockContext.services.agentContext!.config.getApprovedPlanPath, + ).mockReturnValue(undefined); if (!planCommand.action) throw new Error('Action missing'); await planCommand.action(mockContext, ''); - expect(mockContext.services.config!.setApprovalMode).toHaveBeenCalledWith( - ApprovalMode.PLAN, - ); + expect( + mockContext.services.agentContext!.config.setApprovalMode, + ).toHaveBeenCalledWith(ApprovalMode.PLAN); expect(coreEvents.emitFeedback).toHaveBeenCalledWith( 'info', 'Switched to Plan Mode.', @@ -102,10 +106,12 @@ describe('planCommand', () => { it('should display the approved plan from config', async () => { const mockPlanPath = '/mock/plans/dir/approved-plan.md'; - vi.mocked(mockContext.services.config!.isPlanEnabled).mockReturnValue(true); - vi.mocked(mockContext.services.config!.getApprovedPlanPath).mockReturnValue( - mockPlanPath, - ); + vi.mocked( + mockContext.services.agentContext!.config.isPlanEnabled, + ).mockReturnValue(true); + vi.mocked( + mockContext.services.agentContext!.config.getApprovedPlanPath, + ).mockReturnValue(mockPlanPath); vi.mocked(processSingleFileContent).mockResolvedValue({ llmContent: '# Approved Plan Content', returnDisplay: '# Approved Plan Content', @@ -128,7 +134,7 @@ describe('planCommand', () => { it('should copy the approved plan to clipboard', async () => { const mockPlanPath = '/mock/plans/dir/approved-plan.md'; vi.mocked( - mockContext.services.config!.getApprovedPlanPath, + mockContext.services.agentContext!.config.getApprovedPlanPath, ).mockReturnValue(mockPlanPath); vi.mocked(readFileWithEncoding).mockResolvedValue('# Plan Content'); @@ -149,7 +155,7 @@ describe('planCommand', () => { it('should warn if no approved plan is found', async () => { vi.mocked( - mockContext.services.config!.getApprovedPlanPath, + mockContext.services.agentContext!.config.getApprovedPlanPath, ).mockReturnValue(undefined); const copySubCommand = planCommand.subCommands?.find( diff --git a/packages/cli/src/ui/commands/planCommand.ts b/packages/cli/src/ui/commands/planCommand.ts index cfa3f9433e..c38d021d90 100644 --- a/packages/cli/src/ui/commands/planCommand.ts +++ b/packages/cli/src/ui/commands/planCommand.ts @@ -22,7 +22,7 @@ import * as path from 'node:path'; import { copyToClipboard } from '../utils/commandUtils.js'; async function copyAction(context: CommandContext) { - const config = context.services.config; + const config = context.services.agentContext?.config; if (!config) { debugLogger.debug('Plan copy command: config is not available in context'); return; @@ -53,7 +53,7 @@ export const planCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: false, action: async (context) => { - const config = context.services.config; + const config = context.services.agentContext?.config; if (!config) { debugLogger.debug('Plan command: config is not available in context'); return; diff --git a/packages/cli/src/ui/commands/policiesCommand.test.ts b/packages/cli/src/ui/commands/policiesCommand.test.ts index 554d5cd53d..c5baa89d5d 100644 --- a/packages/cli/src/ui/commands/policiesCommand.test.ts +++ b/packages/cli/src/ui/commands/policiesCommand.test.ts @@ -32,7 +32,7 @@ describe('policiesCommand', () => { describe('list subcommand', () => { it('should show error if config is missing', async () => { - mockContext.services.config = null; + mockContext.services.agentContext = null; const listCommand = policiesCommand.subCommands![0]; await listCommand.action!(mockContext, ''); @@ -50,8 +50,11 @@ describe('policiesCommand', () => { const mockPolicyEngine = { getRules: vi.fn().mockReturnValue([]), }; - mockContext.services.config = { + mockContext.services.agentContext = { getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine), + get config() { + return this; + }, } as unknown as Config; const listCommand = policiesCommand.subCommands![0]; @@ -85,8 +88,11 @@ describe('policiesCommand', () => { const mockPolicyEngine = { getRules: vi.fn().mockReturnValue(mockRules), }; - mockContext.services.config = { + mockContext.services.agentContext = { getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine), + get config() { + return this; + }, } as unknown as Config; const listCommand = policiesCommand.subCommands![0]; @@ -142,8 +148,11 @@ describe('policiesCommand', () => { const mockPolicyEngine = { getRules: vi.fn().mockReturnValue(mockRules), }; - mockContext.services.config = { + mockContext.services.agentContext = { getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine), + get config() { + return this; + }, } as unknown as Config; const listCommand = policiesCommand.subCommands![0]; diff --git a/packages/cli/src/ui/commands/policiesCommand.ts b/packages/cli/src/ui/commands/policiesCommand.ts index f4bd13de28..40ed56ae3b 100644 --- a/packages/cli/src/ui/commands/policiesCommand.ts +++ b/packages/cli/src/ui/commands/policiesCommand.ts @@ -51,7 +51,8 @@ const listPoliciesCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context) => { - const { config } = context.services; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) { context.ui.addItem( { diff --git a/packages/cli/src/ui/commands/restoreCommand.test.ts b/packages/cli/src/ui/commands/restoreCommand.test.ts index 2a5def5c42..a2f29ca5b9 100644 --- a/packages/cli/src/ui/commands/restoreCommand.test.ts +++ b/packages/cli/src/ui/commands/restoreCommand.test.ts @@ -47,14 +47,17 @@ describe('restoreCommand', () => { getProjectTempCheckpointsDir: vi.fn().mockReturnValue(checkpointsDir), getProjectTempDir: vi.fn().mockReturnValue(geminiTempDir), }, - getGeminiClient: vi.fn().mockReturnValue({ + geminiClient: { setHistory: mockSetHistory, - }), + }, + get config() { + return this; + }, } as unknown as Config; mockContext = createMockCommandContext({ services: { - config: mockConfig, + agentContext: mockConfig, git: mockGitService, }, }); diff --git a/packages/cli/src/ui/commands/restoreCommand.ts b/packages/cli/src/ui/commands/restoreCommand.ts index 3051588e7c..cf18836c20 100644 --- a/packages/cli/src/ui/commands/restoreCommand.ts +++ b/packages/cli/src/ui/commands/restoreCommand.ts @@ -37,10 +37,11 @@ async function restoreAction( args: string, ): Promise { const { services, ui } = context; - const { config, git: gitService } = services; + const { agentContext, git: gitService } = services; const { addItem, loadHistory } = ui; - const checkpointDir = config?.storage.getProjectTempCheckpointsDir(); + const checkpointDir = + agentContext?.config.storage.getProjectTempCheckpointsDir(); if (!checkpointDir) { return { @@ -116,7 +117,7 @@ async function restoreAction( } else if (action.type === 'load_history' && loadHistory) { loadHistory(action.history); if (action.clientHistory) { - config?.getGeminiClient()?.setHistory(action.clientHistory); + agentContext!.geminiClient?.setHistory(action.clientHistory); } } } @@ -140,8 +141,9 @@ async function completion( _partialArg: string, ): Promise { const { services } = context; - const { config } = services; - const checkpointDir = config?.storage.getProjectTempCheckpointsDir(); + const { agentContext } = services; + const checkpointDir = + agentContext?.config.storage.getProjectTempCheckpointsDir(); if (!checkpointDir) { return []; } diff --git a/packages/cli/src/ui/commands/rewindCommand.test.tsx b/packages/cli/src/ui/commands/rewindCommand.test.tsx index 529991b07f..d93d365a3e 100644 --- a/packages/cli/src/ui/commands/rewindCommand.test.tsx +++ b/packages/cli/src/ui/commands/rewindCommand.test.tsx @@ -97,15 +97,17 @@ describe('rewindCommand', () => { mockContext = createMockCommandContext({ services: { - config: { - getGeminiClient: () => ({ + agentContext: { + geminiClient: { getChatRecordingService: mockGetChatRecordingService, setHistory: mockSetHistory, sendMessageStream: mockSendMessageStream, - }), - getSessionId: () => 'test-session-id', - getContextManager: () => ({ refresh: mockResetContext }), - getProjectRoot: mockGetProjectRoot, + }, + config: { + getSessionId: () => 'test-session-id', + getContextManager: () => ({ refresh: mockResetContext }), + getProjectRoot: mockGetProjectRoot, + }, }, }, ui: { @@ -293,7 +295,12 @@ describe('rewindCommand', () => { it('should fail if client is not initialized', () => { const context = createMockCommandContext({ services: { - config: { getGeminiClient: () => undefined }, + agentContext: { + geminiClient: undefined, + get config() { + return this; + }, + }, }, }) as unknown as CommandContext; @@ -309,8 +316,11 @@ describe('rewindCommand', () => { it('should fail if recording service is unavailable', () => { const context = createMockCommandContext({ services: { - config: { - getGeminiClient: () => ({ getChatRecordingService: () => undefined }), + agentContext: { + geminiClient: { getChatRecordingService: () => undefined }, + get config() { + return this; + }, }, }, }) as unknown as CommandContext; diff --git a/packages/cli/src/ui/commands/rewindCommand.tsx b/packages/cli/src/ui/commands/rewindCommand.tsx index c4af3e845d..c4e0284d0f 100644 --- a/packages/cli/src/ui/commands/rewindCommand.tsx +++ b/packages/cli/src/ui/commands/rewindCommand.tsx @@ -61,7 +61,7 @@ async function rewindConversation( client.setHistory(clientHistory as Content[]); // Reset context manager as we are rewinding history - await context.services.config?.getContextManager()?.refresh(); + await context.services.agentContext?.config.getContextManager()?.refresh(); // Update UI History // We generate IDs based on index for the rewind history @@ -94,7 +94,8 @@ export const rewindCommand: SlashCommand = { description: 'Jump back to a specific message and restart the conversation', kind: CommandKind.BUILT_IN, action: (context) => { - const config = context.services.config; + const agentContext = context.services.agentContext; + const config = agentContext?.config; if (!config) return { type: 'message', @@ -102,7 +103,7 @@ export const rewindCommand: SlashCommand = { content: 'Config not found', }; - const client = config.getGeminiClient(); + const client = agentContext.geminiClient; if (!client) return { type: 'message', diff --git a/packages/cli/src/ui/commands/setupGithubCommand.ts b/packages/cli/src/ui/commands/setupGithubCommand.ts index c68dd5cb88..afc9b7210e 100644 --- a/packages/cli/src/ui/commands/setupGithubCommand.ts +++ b/packages/cli/src/ui/commands/setupGithubCommand.ts @@ -230,7 +230,7 @@ export const setupGithubCommand: SlashCommand = { } // Get the latest release tag from GitHub - const proxy = context?.services?.config?.getProxy(); + const proxy = context?.services?.agentContext?.config.getProxy(); const releaseTag = await getLatestGitHubRelease(proxy); const readmeUrl = `https://github.com/google-github-actions/run-gemini-cli/blob/${releaseTag}/README.md#quick-start`; diff --git a/packages/cli/src/ui/commands/skillsCommand.test.ts b/packages/cli/src/ui/commands/skillsCommand.test.ts index 89f690e143..120ba01ed7 100644 --- a/packages/cli/src/ui/commands/skillsCommand.test.ts +++ b/packages/cli/src/ui/commands/skillsCommand.test.ts @@ -68,7 +68,7 @@ describe('skillsCommand', () => { ]; context = createMockCommandContext({ services: { - config: { + agentContext: { getSkillManager: vi.fn().mockReturnValue({ getAllSkills: vi.fn().mockReturnValue(skills), getSkills: vi.fn().mockReturnValue(skills), @@ -80,6 +80,9 @@ describe('skillsCommand', () => { ), }), getContentGenerator: vi.fn(), + get config() { + return this; + }, } as unknown as Config, settings: { merged: createTestMergedSettings({ skills: { disabled: [] } }), @@ -162,7 +165,8 @@ describe('skillsCommand', () => { }); it('should filter built-in skills by default and show them with "all"', async () => { - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); const mockSkills = [ { name: 'regular', @@ -452,7 +456,8 @@ describe('skillsCommand', () => { }); it('should show error if skills are disabled by admin during disable', async () => { - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); vi.mocked(skillManager.isAdminEnabled).mockReturnValue(false); const disableCmd = skillsCommand.subCommands!.find( @@ -470,7 +475,8 @@ describe('skillsCommand', () => { }); it('should show error if skills are disabled by admin during enable', async () => { - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); vi.mocked(skillManager.isAdminEnabled).mockReturnValue(false); const enableCmd = skillsCommand.subCommands!.find( @@ -497,8 +503,7 @@ describe('skillsCommand', () => { const reloadSkillsMock = vi.fn().mockImplementation(async () => { await new Promise((resolve) => setTimeout(resolve, 200)); }); - // @ts-expect-error Mocking reloadSkills - context.services.config.reloadSkills = reloadSkillsMock; + context.services.agentContext!.config.reloadSkills = reloadSkillsMock; const actionPromise = reloadCmd.action!(context, ''); @@ -537,15 +542,15 @@ describe('skillsCommand', () => { (s) => s.name === 'reload', )!; const reloadSkillsMock = vi.fn().mockImplementation(async () => { - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); vi.mocked(skillManager.getSkills).mockReturnValue([ { name: 'skill1' }, { name: 'skill2' }, { name: 'skill3' }, ] as SkillDefinition[]); }); - // @ts-expect-error Mocking reloadSkills - context.services.config.reloadSkills = reloadSkillsMock; + context.services.agentContext!.config.reloadSkills = reloadSkillsMock; await reloadCmd.action!(context, ''); @@ -562,13 +567,13 @@ describe('skillsCommand', () => { (s) => s.name === 'reload', )!; const reloadSkillsMock = vi.fn().mockImplementation(async () => { - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); vi.mocked(skillManager.getSkills).mockReturnValue([ { name: 'skill1' }, ] as SkillDefinition[]); }); - // @ts-expect-error Mocking reloadSkills - context.services.config.reloadSkills = reloadSkillsMock; + context.services.agentContext!.config.reloadSkills = reloadSkillsMock; await reloadCmd.action!(context, ''); @@ -585,14 +590,14 @@ describe('skillsCommand', () => { (s) => s.name === 'reload', )!; const reloadSkillsMock = vi.fn().mockImplementation(async () => { - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); vi.mocked(skillManager.getSkills).mockReturnValue([ { name: 'skill2' }, // skill1 removed, skill3 added { name: 'skill3' }, ] as SkillDefinition[]); }); - // @ts-expect-error Mocking reloadSkills - context.services.config.reloadSkills = reloadSkillsMock; + context.services.agentContext!.config.reloadSkills = reloadSkillsMock; await reloadCmd.action!(context, ''); @@ -608,7 +613,7 @@ describe('skillsCommand', () => { const reloadCmd = skillsCommand.subCommands!.find( (s) => s.name === 'reload', )!; - context.services.config = null; + context.services.agentContext = null; await reloadCmd.action!(context, ''); @@ -628,8 +633,7 @@ describe('skillsCommand', () => { const reloadSkillsMock = vi.fn().mockImplementation(async () => { await new Promise((_, reject) => setTimeout(() => reject(error), 200)); }); - // @ts-expect-error Mocking reloadSkills - context.services.config.reloadSkills = reloadSkillsMock; + context.services.agentContext!.config.reloadSkills = reloadSkillsMock; const actionPromise = reloadCmd.action!(context, ''); await vi.advanceTimersByTimeAsync(100); @@ -651,7 +655,8 @@ describe('skillsCommand', () => { const disableCmd = skillsCommand.subCommands!.find( (s) => s.name === 'disable', )!; - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); const mockSkills = [ { name: 'skill1', @@ -681,7 +686,8 @@ describe('skillsCommand', () => { const enableCmd = skillsCommand.subCommands!.find( (s) => s.name === 'enable', )!; - const skillManager = context.services.config!.getSkillManager(); + const skillManager = + context.services.agentContext!.config.getSkillManager(); const mockSkills = [ { name: 'skill1', diff --git a/packages/cli/src/ui/commands/skillsCommand.ts b/packages/cli/src/ui/commands/skillsCommand.ts index 6f1672208d..a1f9c82445 100644 --- a/packages/cli/src/ui/commands/skillsCommand.ts +++ b/packages/cli/src/ui/commands/skillsCommand.ts @@ -46,7 +46,7 @@ async function listAction( } } - const skillManager = context.services.config?.getSkillManager(); + const skillManager = context.services.agentContext?.config.getSkillManager(); if (!skillManager) { context.ui.addItem({ type: MessageType.ERROR, @@ -127,8 +127,8 @@ async function linkAction( text: `Successfully linked skills from "${sourcePath}" (${scope}).`, }); - if (context.services.config) { - await context.services.config.reloadSkills(); + if (context.services.agentContext?.config) { + await context.services.agentContext.config.reloadSkills(); } } catch (error) { context.ui.addItem({ @@ -150,14 +150,14 @@ async function disableAction( }); return; } - const skillManager = context.services.config?.getSkillManager(); + const skillManager = context.services.agentContext?.config.getSkillManager(); if (skillManager?.isAdminEnabled() === false) { context.ui.addItem( { type: MessageType.ERROR, text: getAdminErrorMessage( 'Agent skills', - context.services.config ?? undefined, + context.services.agentContext?.config ?? undefined, ), }, Date.now(), @@ -211,14 +211,14 @@ async function enableAction( return; } - const skillManager = context.services.config?.getSkillManager(); + const skillManager = context.services.agentContext?.config.getSkillManager(); if (skillManager?.isAdminEnabled() === false) { context.ui.addItem( { type: MessageType.ERROR, text: getAdminErrorMessage( 'Agent skills', - context.services.config ?? undefined, + context.services.agentContext?.config ?? undefined, ), }, Date.now(), @@ -246,7 +246,7 @@ async function enableAction( async function reloadAction( context: CommandContext, ): Promise { - const config = context.services.config; + const config = context.services.agentContext?.config; if (!config) { context.ui.addItem({ type: MessageType.ERROR, @@ -333,7 +333,7 @@ function disableCompletion( context: CommandContext, partialArg: string, ): string[] { - const skillManager = context.services.config?.getSkillManager(); + const skillManager = context.services.agentContext?.config.getSkillManager(); if (!skillManager) { return []; } @@ -347,7 +347,7 @@ function enableCompletion( context: CommandContext, partialArg: string, ): string[] { - const skillManager = context.services.config?.getSkillManager(); + const skillManager = context.services.agentContext?.config.getSkillManager(); if (!skillManager) { return []; } diff --git a/packages/cli/src/ui/commands/statsCommand.test.ts b/packages/cli/src/ui/commands/statsCommand.test.ts index 57fff84b6b..86ecf68654 100644 --- a/packages/cli/src/ui/commands/statsCommand.test.ts +++ b/packages/cli/src/ui/commands/statsCommand.test.ts @@ -43,12 +43,15 @@ describe('statsCommand', () => { it('should display general session stats when run with no subcommand', async () => { if (!statsCommand.action) throw new Error('Command has no action'); - mockContext.services.config = { + mockContext.services.agentContext = { refreshUserQuota: vi.fn(), refreshAvailableCredits: vi.fn(), getUserTierName: vi.fn(), getUserPaidTier: vi.fn(), getModel: vi.fn(), + get config() { + return this; + }, } as unknown as Config; await statsCommand.action(mockContext, ''); @@ -80,7 +83,7 @@ describe('statsCommand', () => { .fn() .mockReturnValue('2025-01-01T12:00:00Z'); - mockContext.services.config = { + mockContext.services.agentContext = { refreshUserQuota: mockRefreshUserQuota, getUserTierName: mockGetUserTierName, getModel: mockGetModel, @@ -89,6 +92,9 @@ describe('statsCommand', () => { getQuotaResetTime: mockGetQuotaResetTime, getUserPaidTier: vi.fn(), refreshAvailableCredits: vi.fn(), + get config() { + return this; + }, } as unknown as Config; await statsCommand.action(mockContext, ''); diff --git a/packages/cli/src/ui/commands/statsCommand.ts b/packages/cli/src/ui/commands/statsCommand.ts index fe991e97ed..2ca4596337 100644 --- a/packages/cli/src/ui/commands/statsCommand.ts +++ b/packages/cli/src/ui/commands/statsCommand.ts @@ -29,8 +29,8 @@ function getUserIdentity(context: CommandContext) { const cachedAccount = userAccountManager.getCachedGoogleAccount(); const userEmail = cachedAccount ?? undefined; - const tier = context.services.config?.getUserTierName(); - const paidTier = context.services.config?.getUserPaidTier(); + const tier = context.services.agentContext?.config.getUserTierName(); + const paidTier = context.services.agentContext?.config.getUserPaidTier(); const creditBalance = getG1CreditBalance(paidTier) ?? undefined; return { selectedAuthType, userEmail, tier, creditBalance }; @@ -50,7 +50,7 @@ async function defaultSessionView(context: CommandContext) { const { selectedAuthType, userEmail, tier, creditBalance } = getUserIdentity(context); - const currentModel = context.services.config?.getModel(); + const currentModel = context.services.agentContext?.config.getModel(); const statsItem: HistoryItemStats = { type: MessageType.STATS, @@ -62,16 +62,19 @@ async function defaultSessionView(context: CommandContext) { creditBalance, }; - if (context.services.config) { + if (context.services.agentContext?.config) { const [quota] = await Promise.all([ - context.services.config.refreshUserQuota(), - context.services.config.refreshAvailableCredits(), + context.services.agentContext.config.refreshUserQuota(), + context.services.agentContext.config.refreshAvailableCredits(), ]); if (quota) { statsItem.quotas = quota; - statsItem.pooledRemaining = context.services.config.getQuotaRemaining(); - statsItem.pooledLimit = context.services.config.getQuotaLimit(); - statsItem.pooledResetTime = context.services.config.getQuotaResetTime(); + statsItem.pooledRemaining = + context.services.agentContext.config.getQuotaRemaining(); + statsItem.pooledLimit = + context.services.agentContext.config.getQuotaLimit(); + statsItem.pooledResetTime = + context.services.agentContext.config.getQuotaResetTime(); } } @@ -107,10 +110,13 @@ export const statsCommand: SlashCommand = { isSafeConcurrent: true, action: (context: CommandContext) => { const { selectedAuthType, userEmail, tier } = getUserIdentity(context); - const currentModel = context.services.config?.getModel(); - const pooledRemaining = context.services.config?.getQuotaRemaining(); - const pooledLimit = context.services.config?.getQuotaLimit(); - const pooledResetTime = context.services.config?.getQuotaResetTime(); + const currentModel = context.services.agentContext?.config.getModel(); + const pooledRemaining = + context.services.agentContext?.config.getQuotaRemaining(); + const pooledLimit = + context.services.agentContext?.config.getQuotaLimit(); + const pooledResetTime = + context.services.agentContext?.config.getQuotaResetTime(); context.ui.addItem({ type: MessageType.MODEL_STATS, selectedAuthType, diff --git a/packages/cli/src/ui/commands/toolsCommand.test.ts b/packages/cli/src/ui/commands/toolsCommand.test.ts index f5ff86f259..02d9ddb5bc 100644 --- a/packages/cli/src/ui/commands/toolsCommand.test.ts +++ b/packages/cli/src/ui/commands/toolsCommand.test.ts @@ -30,8 +30,8 @@ describe('toolsCommand', () => { it('should display an error if the tool registry is unavailable', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => undefined, + agentContext: { + toolRegistry: undefined, }, }, }); @@ -48,10 +48,10 @@ describe('toolsCommand', () => { it('should display "No tools available" when none are found', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => ({ + agentContext: { + toolRegistry: { getAllTools: () => [] as Array>, - }), + }, }, }, }); @@ -69,8 +69,8 @@ describe('toolsCommand', () => { it('should list tools without descriptions by default (no args)', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => ({ getAllTools: () => mockTools }), + agentContext: { + toolRegistry: { getAllTools: () => mockTools }, }, }, }); @@ -90,8 +90,8 @@ describe('toolsCommand', () => { it('should list tools without descriptions when "list" arg is passed', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => ({ getAllTools: () => mockTools }), + agentContext: { + toolRegistry: { getAllTools: () => mockTools }, }, }, }); @@ -111,8 +111,8 @@ describe('toolsCommand', () => { it('should list tools with descriptions when "desc" arg is passed', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => ({ getAllTools: () => mockTools }), + agentContext: { + toolRegistry: { getAllTools: () => mockTools }, }, }, }); @@ -144,8 +144,8 @@ describe('toolsCommand', () => { it('subcommand "list" should display tools without descriptions', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => ({ getAllTools: () => mockTools }), + agentContext: { + toolRegistry: { getAllTools: () => mockTools }, }, }, }); @@ -165,8 +165,8 @@ describe('toolsCommand', () => { it('subcommand "desc" should display tools with descriptions', async () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => ({ getAllTools: () => mockTools }), + agentContext: { + toolRegistry: { getAllTools: () => mockTools }, }, }, }); @@ -196,8 +196,8 @@ describe('toolsCommand', () => { const mockContext = createMockCommandContext({ services: { - config: { - getToolRegistry: () => ({ getAllTools: () => mockTools }), + agentContext: { + toolRegistry: { getAllTools: () => mockTools }, }, }, }); diff --git a/packages/cli/src/ui/commands/toolsCommand.ts b/packages/cli/src/ui/commands/toolsCommand.ts index 082da26fab..d3e5aef74b 100644 --- a/packages/cli/src/ui/commands/toolsCommand.ts +++ b/packages/cli/src/ui/commands/toolsCommand.ts @@ -15,7 +15,7 @@ async function listTools( context: CommandContext, showDescriptions: boolean, ): Promise { - const toolRegistry = context.services.config?.getToolRegistry(); + const toolRegistry = context.services.agentContext?.toolRegistry; if (!toolRegistry) { context.ui.addItem({ type: MessageType.ERROR, diff --git a/packages/cli/src/ui/commands/types.ts b/packages/cli/src/ui/commands/types.ts index 7bd640090f..4065e075bf 100644 --- a/packages/cli/src/ui/commands/types.ts +++ b/packages/cli/src/ui/commands/types.ts @@ -11,11 +11,11 @@ import type { ConfirmationRequest, } from '../types.js'; import type { - Config, GitService, Logger, CommandActionReturn, AgentDefinition, + AgentLoopContext, } from '@google/gemini-cli-core'; import type { LoadedSettings } from '../../config/settings.js'; import type { UseHistoryManagerReturn } from '../hooks/useHistoryManager.js'; @@ -39,7 +39,7 @@ export interface CommandContext { // Core services and configuration services: { // TODO(abhipatel12): Ensure that config is never null. - config: Config | null; + agentContext: AgentLoopContext | null; settings: LoadedSettings; git: GitService | undefined; logger: Logger; diff --git a/packages/cli/src/ui/commands/upgradeCommand.test.ts b/packages/cli/src/ui/commands/upgradeCommand.test.ts index 9c54eb0191..bb07c1bd44 100644 --- a/packages/cli/src/ui/commands/upgradeCommand.test.ts +++ b/packages/cli/src/ui/commands/upgradeCommand.test.ts @@ -33,11 +33,13 @@ describe('upgradeCommand', () => { vi.clearAllMocks(); mockContext = createMockCommandContext({ services: { - config: { - getContentGeneratorConfig: vi.fn().mockReturnValue({ - authType: AuthType.LOGIN_WITH_GOOGLE, - }), - getUserTierName: vi.fn().mockReturnValue(undefined), + agentContext: { + config: { + getContentGeneratorConfig: vi.fn().mockReturnValue({ + authType: AuthType.LOGIN_WITH_GOOGLE, + }), + getUserTierName: vi.fn().mockReturnValue(undefined), + }, }, }, } as unknown as CommandContext); @@ -62,7 +64,7 @@ describe('upgradeCommand', () => { it('should return an error message when NOT logged in with Google', async () => { vi.mocked( - mockContext.services.config!.getContentGeneratorConfig, + mockContext.services.agentContext!.config.getContentGeneratorConfig, ).mockReturnValue({ authType: AuthType.USE_GEMINI, }); @@ -118,9 +120,9 @@ describe('upgradeCommand', () => { }); it('should return info message for ultra tiers', async () => { - vi.mocked(mockContext.services.config!.getUserTierName).mockReturnValue( - 'Advanced Ultra', - ); + vi.mocked( + mockContext.services.agentContext!.config.getUserTierName, + ).mockReturnValue('Advanced Ultra'); if (!upgradeCommand.action) { throw new Error('The upgrade command must have an action.'); diff --git a/packages/cli/src/ui/commands/upgradeCommand.ts b/packages/cli/src/ui/commands/upgradeCommand.ts index 9bbea156ce..f7c09a42f0 100644 --- a/packages/cli/src/ui/commands/upgradeCommand.ts +++ b/packages/cli/src/ui/commands/upgradeCommand.ts @@ -23,8 +23,8 @@ export const upgradeCommand: SlashCommand = { description: 'Upgrade your Gemini Code Assist tier for higher limits', autoExecute: true, action: async (context) => { - const authType = - context.services.config?.getContentGeneratorConfig()?.authType; + const config = context.services.agentContext?.config; + const authType = config?.getContentGeneratorConfig()?.authType; if (authType !== AuthType.LOGIN_WITH_GOOGLE) { // This command should ideally be hidden if not logged in with Google, // but we add a safety check here just in case. @@ -36,7 +36,7 @@ export const upgradeCommand: SlashCommand = { }; } - const tierName = context.services.config?.getUserTierName(); + const tierName = config?.getUserTierName(); if (isUltraTier(tierName)) { return { type: 'message', diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx b/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx index 6de411ae64..04b521e6a6 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx @@ -423,7 +423,7 @@ describe('useSlashCommandProcessor', () => { expect(childAction).toHaveBeenCalledWith( expect.objectContaining({ services: expect.objectContaining({ - config: mockConfig, + agentContext: mockConfig, }), ui: expect.objectContaining({ addItem: mockAddItem, diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index d070840f2d..1839670df7 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -209,7 +209,7 @@ export const useSlashCommandProcessor = ( const commandContext = useMemo( (): CommandContext => ({ services: { - config, + agentContext: config, settings, git: gitService, logger,