mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-22 12:01:39 -07:00
Expose Codebase Investigator settings to the user (#10844)
This commit is contained in:
@@ -2543,59 +2543,6 @@ describe('loadCliConfig useRipgrep', () => {
|
||||
expect(config.getUseModelRouter()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig enableSubagents', () => {
|
||||
it('should be false by default when enableSubagents is not set in settings', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {};
|
||||
const config = await loadCliConfig(
|
||||
settings,
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.getEnableSubagents()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be true when enableSubagents is set to true in settings', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { experimental: { enableSubagents: true } };
|
||||
const config = await loadCliConfig(
|
||||
settings,
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.getEnableSubagents()).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false when enableSubagents is explicitly set to false in settings', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { experimental: { enableSubagents: false } };
|
||||
const config = await loadCliConfig(
|
||||
settings,
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.getEnableSubagents()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('screenReader configuration', () => {
|
||||
|
||||
@@ -758,7 +758,8 @@ export async function loadCliConfig(
|
||||
useModelRouter,
|
||||
enableMessageBusIntegration:
|
||||
settings.tools?.enableMessageBusIntegration ?? false,
|
||||
enableSubagents: settings.experimental?.enableSubagents ?? false,
|
||||
codebaseInvestigatorSettings:
|
||||
settings.experimental?.codebaseInvestigatorSettings,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -330,24 +330,5 @@ describe('SettingsSchema', () => {
|
||||
getSettingsSchema().experimental.properties.useModelRouter.default,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should have enableSubagents setting in schema', () => {
|
||||
expect(
|
||||
getSettingsSchema().experimental.properties.enableSubagents,
|
||||
).toBeDefined();
|
||||
expect(
|
||||
getSettingsSchema().experimental.properties.enableSubagents.type,
|
||||
).toBe('boolean');
|
||||
expect(
|
||||
getSettingsSchema().experimental.properties.enableSubagents.category,
|
||||
).toBe('Experimental');
|
||||
expect(
|
||||
getSettingsSchema().experimental.properties.enableSubagents.default,
|
||||
).toBe(false);
|
||||
expect(
|
||||
getSettingsSchema().experimental.properties.enableSubagents
|
||||
.requiresRestart,
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
import {
|
||||
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
|
||||
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type { CustomTheme } from '../ui/themes/theme.js';
|
||||
import type { SessionRetentionSettings } from './settings.js';
|
||||
@@ -1065,14 +1066,65 @@ const SETTINGS_SCHEMA = {
|
||||
'Enable model routing to route requests to the best model based on complexity.',
|
||||
showInDialog: true,
|
||||
},
|
||||
enableSubagents: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Subagents',
|
||||
codebaseInvestigatorSettings: {
|
||||
type: 'object',
|
||||
label: 'Codebase Investigator Settings',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description: 'Enable experimental subagents.',
|
||||
default: {},
|
||||
description: 'Configuration for Codebase Investigator.',
|
||||
showInDialog: false,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Codebase Investigator',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description: 'Enable the Codebase Investigator agent.',
|
||||
showInDialog: true,
|
||||
},
|
||||
maxNumTurns: {
|
||||
type: 'number',
|
||||
label: 'Codebase Investigator Max Num Turns',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: 15,
|
||||
description:
|
||||
'Maximum number of turns for the Codebase Investigator agent.',
|
||||
showInDialog: true,
|
||||
},
|
||||
maxTimeMinutes: {
|
||||
type: 'number',
|
||||
label: 'Max Time (Minutes)',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: 5,
|
||||
description:
|
||||
'Maximum time for the Codebase Investigator agent (in minutes).',
|
||||
showInDialog: false,
|
||||
},
|
||||
thinkingBudget: {
|
||||
type: 'number',
|
||||
label: 'Thinking Budget',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: -1,
|
||||
description:
|
||||
'The thinking budget for the Codebase Investigator agent.',
|
||||
showInDialog: false,
|
||||
},
|
||||
model: {
|
||||
type: 'string',
|
||||
label: 'Model',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: DEFAULT_GEMINI_MODEL,
|
||||
description:
|
||||
'The model to use for the Codebase Investigator agent.',
|
||||
showInDialog: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -342,7 +342,7 @@ describe('SettingsDialog', () => {
|
||||
|
||||
await wait();
|
||||
|
||||
expect(lastFrame()).toContain('● Use Model Router');
|
||||
expect(lastFrame()).toContain('● Codebase Investigator Max Num Turns');
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
@@ -33,7 +33,33 @@ export class AgentRegistry {
|
||||
}
|
||||
|
||||
private loadBuiltInAgents(): void {
|
||||
this.registerAgent(CodebaseInvestigatorAgent);
|
||||
const investigatorSettings = this.config.getCodebaseInvestigatorSettings();
|
||||
|
||||
// Only register the agent if it's enabled in the settings.
|
||||
if (investigatorSettings?.enabled) {
|
||||
const agentDef = {
|
||||
...CodebaseInvestigatorAgent,
|
||||
modelConfig: {
|
||||
...CodebaseInvestigatorAgent.modelConfig,
|
||||
model:
|
||||
investigatorSettings.model ??
|
||||
CodebaseInvestigatorAgent.modelConfig.model,
|
||||
thinkingBudget:
|
||||
investigatorSettings.thinkingBudget ??
|
||||
CodebaseInvestigatorAgent.modelConfig.thinkingBudget,
|
||||
},
|
||||
runConfig: {
|
||||
...CodebaseInvestigatorAgent.runConfig,
|
||||
max_time_minutes:
|
||||
investigatorSettings.maxTimeMinutes ??
|
||||
CodebaseInvestigatorAgent.runConfig.max_time_minutes,
|
||||
max_turns:
|
||||
investigatorSettings.maxNumTurns ??
|
||||
CodebaseInvestigatorAgent.runConfig.max_turns,
|
||||
},
|
||||
};
|
||||
this.registerAgent(agentDef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,6 +133,7 @@ vi.mock('../agents/registry.js', () => {
|
||||
const AgentRegistryMock = vi.fn();
|
||||
AgentRegistryMock.prototype.initialize = vi.fn();
|
||||
AgentRegistryMock.prototype.getAllDefinitions = vi.fn(() => []);
|
||||
AgentRegistryMock.prototype.getDefinition = vi.fn();
|
||||
return { AgentRegistry: AgentRegistryMock };
|
||||
});
|
||||
|
||||
@@ -600,31 +601,6 @@ describe('Server Config (config.ts)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('EnableSubagents Configuration', () => {
|
||||
it('should default enableSubagents to false when not provided', () => {
|
||||
const config = new Config(baseParams);
|
||||
expect(config.getEnableSubagents()).toBe(false);
|
||||
});
|
||||
|
||||
it('should set enableSubagents to true when provided as true', () => {
|
||||
const paramsWithSubagents: ConfigParameters = {
|
||||
...baseParams,
|
||||
enableSubagents: true,
|
||||
};
|
||||
const config = new Config(paramsWithSubagents);
|
||||
expect(config.getEnableSubagents()).toBe(true);
|
||||
});
|
||||
|
||||
it('should set enableSubagents to false when explicitly provided as false', () => {
|
||||
const paramsWithSubagents: ConfigParameters = {
|
||||
...baseParams,
|
||||
enableSubagents: false,
|
||||
};
|
||||
const config = new Config(paramsWithSubagents);
|
||||
expect(config.getEnableSubagents()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ContinueOnFailedApiCall Configuration', () => {
|
||||
it('should default continueOnFailedApiCall to false when not provided', () => {
|
||||
const config = new Config(baseParams);
|
||||
@@ -679,25 +655,26 @@ describe('Server Config (config.ts)', () => {
|
||||
expect(wasReadFileToolRegistered).toBe(false);
|
||||
});
|
||||
|
||||
it('should register subagents as tools when enableSubagents is true', async () => {
|
||||
it('should register subagents as tools when codebaseInvestigatorSettings.enabled is true', async () => {
|
||||
const params: ConfigParameters = {
|
||||
...baseParams,
|
||||
enableSubagents: true,
|
||||
codebaseInvestigatorSettings: { enabled: true },
|
||||
};
|
||||
const config = new Config(params);
|
||||
|
||||
const mockAgentDefinitions = [
|
||||
{ name: 'agent1', description: 'Agent 1', instructions: 'Inst 1' },
|
||||
{ name: 'agent2', description: 'Agent 2', instructions: 'Inst 2' },
|
||||
];
|
||||
const mockAgentDefinition = {
|
||||
name: 'codebase-investigator',
|
||||
description: 'Agent 1',
|
||||
instructions: 'Inst 1',
|
||||
};
|
||||
|
||||
const AgentRegistryMock = (
|
||||
(await vi.importMock('../agents/registry.js')) as {
|
||||
AgentRegistry: Mock;
|
||||
}
|
||||
).AgentRegistry;
|
||||
AgentRegistryMock.prototype.getAllDefinitions.mockReturnValue(
|
||||
mockAgentDefinitions,
|
||||
AgentRegistryMock.prototype.getDefinition.mockReturnValue(
|
||||
mockAgentDefinition,
|
||||
);
|
||||
|
||||
const SubagentToolWrapperMock = (
|
||||
@@ -714,14 +691,9 @@ describe('Server Config (config.ts)', () => {
|
||||
}
|
||||
).ToolRegistry.prototype.registerTool;
|
||||
|
||||
expect(SubagentToolWrapperMock).toHaveBeenCalledTimes(2);
|
||||
expect(SubagentToolWrapperMock).toHaveBeenCalledTimes(1);
|
||||
expect(SubagentToolWrapperMock).toHaveBeenCalledWith(
|
||||
mockAgentDefinitions[0],
|
||||
config,
|
||||
undefined,
|
||||
);
|
||||
expect(SubagentToolWrapperMock).toHaveBeenCalledWith(
|
||||
mockAgentDefinitions[1],
|
||||
mockAgentDefinition,
|
||||
config,
|
||||
undefined,
|
||||
);
|
||||
@@ -730,13 +702,13 @@ describe('Server Config (config.ts)', () => {
|
||||
const registeredWrappers = calls.filter(
|
||||
(call) => call[0] instanceof SubagentToolWrapperMock,
|
||||
);
|
||||
expect(registeredWrappers).toHaveLength(2);
|
||||
expect(registeredWrappers).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should not register subagents as tools when enableSubagents is false', async () => {
|
||||
it('should not register subagents as tools when codebaseInvestigatorSettings.enabled is false', async () => {
|
||||
const params: ConfigParameters = {
|
||||
...baseParams,
|
||||
enableSubagents: false,
|
||||
codebaseInvestigatorSettings: { enabled: false },
|
||||
};
|
||||
const config = new Config(params);
|
||||
|
||||
|
||||
@@ -116,6 +116,14 @@ export interface OutputSettings {
|
||||
format?: OutputFormat;
|
||||
}
|
||||
|
||||
export interface CodebaseInvestigatorSettings {
|
||||
enabled?: boolean;
|
||||
maxNumTurns?: number;
|
||||
maxTimeMinutes?: number;
|
||||
thinkingBudget?: number;
|
||||
model?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* All information required in CLI to handle an extension. Defined in Core so
|
||||
* that the collection of loaded, active, and inactive extensions can be passed
|
||||
@@ -268,7 +276,7 @@ export interface ConfigParameters {
|
||||
output?: OutputSettings;
|
||||
useModelRouter?: boolean;
|
||||
enableMessageBusIntegration?: boolean;
|
||||
enableSubagents?: boolean;
|
||||
codebaseInvestigatorSettings?: CodebaseInvestigatorSettings;
|
||||
continueOnFailedApiCall?: boolean;
|
||||
}
|
||||
|
||||
@@ -361,7 +369,7 @@ export class Config {
|
||||
private readonly outputSettings: OutputSettings;
|
||||
private readonly useModelRouter: boolean;
|
||||
private readonly enableMessageBusIntegration: boolean;
|
||||
private readonly enableSubagents: boolean;
|
||||
private readonly codebaseInvestigatorSettings?: CodebaseInvestigatorSettings;
|
||||
private readonly continueOnFailedApiCall: boolean;
|
||||
|
||||
constructor(params: ConfigParameters) {
|
||||
@@ -455,7 +463,7 @@ export class Config {
|
||||
this.useModelRouter = params.useModelRouter ?? false;
|
||||
this.enableMessageBusIntegration =
|
||||
params.enableMessageBusIntegration ?? false;
|
||||
this.enableSubagents = params.enableSubagents ?? false;
|
||||
this.codebaseInvestigatorSettings = params.codebaseInvestigatorSettings;
|
||||
this.continueOnFailedApiCall = params.continueOnFailedApiCall ?? true;
|
||||
this.extensionManagement = params.extensionManagement ?? true;
|
||||
this.storage = new Storage(this.targetDir);
|
||||
@@ -1039,8 +1047,8 @@ export class Config {
|
||||
return this.enableMessageBusIntegration;
|
||||
}
|
||||
|
||||
getEnableSubagents(): boolean {
|
||||
return this.enableSubagents;
|
||||
getCodebaseInvestigatorSettings(): CodebaseInvestigatorSettings | undefined {
|
||||
return this.codebaseInvestigatorSettings;
|
||||
}
|
||||
|
||||
async createToolRegistry(): Promise<ToolRegistry> {
|
||||
@@ -1135,9 +1143,11 @@ export class Config {
|
||||
}
|
||||
|
||||
// Register Subagents as Tools
|
||||
if (this.getEnableSubagents()) {
|
||||
const agentDefinitions = this.agentRegistry.getAllDefinitions();
|
||||
for (const definition of agentDefinitions) {
|
||||
if (this.getCodebaseInvestigatorSettings()?.enabled) {
|
||||
const definition = this.agentRegistry.getDefinition(
|
||||
'codebase_investigator',
|
||||
);
|
||||
if (definition) {
|
||||
// We must respect the main allowed/exclude lists for agents too.
|
||||
const excludeTools = this.getExcludeTools() || [];
|
||||
const allowedTools = this.getAllowedTools();
|
||||
|
||||
Reference in New Issue
Block a user