mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-24 12:04:56 -07:00
feat(core): subagent local execution and tool isolation (#22718)
This commit is contained in:
@@ -13,10 +13,43 @@ import {
|
||||
afterEach,
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
|
||||
const {
|
||||
mockSendMessageStream,
|
||||
mockScheduleAgentTools,
|
||||
mockSetSystemInstruction,
|
||||
mockCompress,
|
||||
mockMaybeDiscoverMcpServer,
|
||||
mockStopMcp,
|
||||
} = vi.hoisted(() => ({
|
||||
mockSendMessageStream: vi.fn().mockResolvedValue({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
yield {
|
||||
type: 'chunk',
|
||||
value: { candidates: [] },
|
||||
};
|
||||
},
|
||||
}),
|
||||
mockScheduleAgentTools: vi.fn(),
|
||||
mockSetSystemInstruction: vi.fn(),
|
||||
mockCompress: vi.fn(),
|
||||
mockMaybeDiscoverMcpServer: vi.fn().mockResolvedValue(undefined),
|
||||
mockStopMcp: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
vi.mock('../tools/mcp-client-manager.js', () => ({
|
||||
McpClientManager: class {
|
||||
maybeDiscoverMcpServer = mockMaybeDiscoverMcpServer;
|
||||
stop = mockStopMcp;
|
||||
},
|
||||
}));
|
||||
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { LocalAgentExecutor, type ActivityCallback } from './local-executor.js';
|
||||
import { makeFakeConfig } from '../test-utils/config.js';
|
||||
import { ToolRegistry } from '../tools/tool-registry.js';
|
||||
import { PromptRegistry } from '../prompts/prompt-registry.js';
|
||||
import { ResourceRegistry } from '../resources/resource-registry.js';
|
||||
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
|
||||
import { LSTool } from '../tools/ls.js';
|
||||
import { LS_TOOL_NAME, READ_FILE_TOOL_NAME } from '../tools/tool-names.js';
|
||||
@@ -70,18 +103,6 @@ import type {
|
||||
import { getModelConfigAlias, type AgentRegistry } from './registry.js';
|
||||
import type { ModelRouterService } from '../routing/modelRouterService.js';
|
||||
|
||||
const {
|
||||
mockSendMessageStream,
|
||||
mockScheduleAgentTools,
|
||||
mockSetSystemInstruction,
|
||||
mockCompress,
|
||||
} = vi.hoisted(() => ({
|
||||
mockSendMessageStream: vi.fn(),
|
||||
mockScheduleAgentTools: vi.fn(),
|
||||
mockSetSystemInstruction: vi.fn(),
|
||||
mockCompress: vi.fn(),
|
||||
}));
|
||||
|
||||
let mockChatHistory: Content[] = [];
|
||||
const mockSetHistory = vi.fn((newHistory: Content[]) => {
|
||||
mockChatHistory = newHistory;
|
||||
@@ -2722,6 +2743,67 @@ describe('LocalAgentExecutor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Isolation', () => {
|
||||
it('should initialize McpClientManager when mcpServers are defined', async () => {
|
||||
const { MCPServerConfig } = await import('../config/config.js');
|
||||
const mcpServers = {
|
||||
'test-server': new MCPServerConfig('node', ['server.js']),
|
||||
};
|
||||
|
||||
const definition = {
|
||||
...createTestDefinition(),
|
||||
mcpServers,
|
||||
};
|
||||
|
||||
vi.spyOn(mockConfig, 'getMcpClientManager').mockReturnValue({
|
||||
maybeDiscoverMcpServer: mockMaybeDiscoverMcpServer,
|
||||
} as unknown as ReturnType<typeof mockConfig.getMcpClientManager>);
|
||||
|
||||
await LocalAgentExecutor.create(definition, mockConfig);
|
||||
|
||||
const mcpManager = mockConfig.getMcpClientManager();
|
||||
expect(mcpManager?.maybeDiscoverMcpServer).toHaveBeenCalledWith(
|
||||
'test-server',
|
||||
mcpServers['test-server'],
|
||||
expect.objectContaining({
|
||||
toolRegistry: expect.any(ToolRegistry),
|
||||
promptRegistry: expect.any(PromptRegistry),
|
||||
resourceRegistry: expect.any(ResourceRegistry),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should inherit main registry tools', async () => {
|
||||
const parentMcpTool = new DiscoveredMCPTool(
|
||||
{} as unknown as CallableTool,
|
||||
'main-server',
|
||||
'tool1',
|
||||
'desc1',
|
||||
{},
|
||||
mockConfig.getMessageBus(),
|
||||
);
|
||||
|
||||
parentToolRegistry.registerTool(parentMcpTool);
|
||||
|
||||
const definition = createTestDefinition();
|
||||
definition.toolConfig = undefined; // trigger inheritance
|
||||
|
||||
vi.spyOn(mockConfig, 'getMcpClientManager').mockReturnValue({
|
||||
maybeDiscoverMcpServer: vi.fn(),
|
||||
} as unknown as ReturnType<typeof mockConfig.getMcpClientManager>);
|
||||
const executor = await LocalAgentExecutor.create(
|
||||
definition,
|
||||
mockConfig,
|
||||
onActivity,
|
||||
);
|
||||
const agentTools = (
|
||||
executor as unknown as { toolRegistry: ToolRegistry }
|
||||
).toolRegistry.getAllToolNames();
|
||||
|
||||
expect(agentTools).toContain(parentMcpTool.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DeclarativeTool instance tools (browser agent pattern)', () => {
|
||||
/**
|
||||
* The browser agent passes DeclarativeTool instances (not string names) in
|
||||
@@ -2827,13 +2909,11 @@ describe('LocalAgentExecutor', () => {
|
||||
const navTool = new MockTool({ name: 'navigate_page' });
|
||||
|
||||
const definition = createInstanceToolDefinition([clickTool, navTool]);
|
||||
|
||||
const executor = await LocalAgentExecutor.create(
|
||||
definition,
|
||||
mockConfig,
|
||||
onActivity,
|
||||
);
|
||||
|
||||
const registry = executor['toolRegistry'];
|
||||
expect(registry.getTool('click')).toBeDefined();
|
||||
expect(registry.getTool('navigate_page')).toBeDefined();
|
||||
|
||||
Reference in New Issue
Block a user