feat(core): Remove legacy settings. (#17244)

This commit is contained in:
joshualitt
2026-01-22 12:59:47 -08:00
committed by GitHub
parent 2ac7900d95
commit 62dd9b5b3c
15 changed files with 321 additions and 390 deletions
@@ -13,24 +13,24 @@ import {
READ_FILE_TOOL_NAME,
} from '../tools/tool-names.js';
import { DEFAULT_GEMINI_MODEL } from '../config/models.js';
import { makeFakeConfig } from '../test-utils/config.js';
describe('CodebaseInvestigatorAgent', () => {
const config = makeFakeConfig();
const agent = CodebaseInvestigatorAgent(config);
it('should have the correct agent definition', () => {
expect(CodebaseInvestigatorAgent.name).toBe('codebase_investigator');
expect(CodebaseInvestigatorAgent.displayName).toBe(
'Codebase Investigator Agent',
);
expect(CodebaseInvestigatorAgent.description).toBeDefined();
expect(agent.name).toBe('codebase_investigator');
expect(agent.displayName).toBe('Codebase Investigator Agent');
expect(agent.description).toBeDefined();
const inputSchema =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
CodebaseInvestigatorAgent.inputConfig.inputSchema as any;
agent.inputConfig.inputSchema as any;
expect(inputSchema.properties['objective']).toBeDefined();
expect(inputSchema.required).toContain('objective');
expect(CodebaseInvestigatorAgent.outputConfig?.outputName).toBe('report');
expect(CodebaseInvestigatorAgent.modelConfig?.model).toBe(
DEFAULT_GEMINI_MODEL,
);
expect(CodebaseInvestigatorAgent.toolConfig?.tools).toEqual([
expect(agent.outputConfig?.outputName).toBe('report');
expect(agent.modelConfig?.model).toBe(DEFAULT_GEMINI_MODEL);
expect(agent.toolConfig?.tools).toEqual([
LS_TOOL_NAME,
READ_FILE_TOOL_NAME,
GLOB_TOOL_NAME,
@@ -44,7 +44,7 @@ describe('CodebaseInvestigatorAgent', () => {
ExplorationTrace: ['trace'],
RelevantLocations: [],
};
const processed = CodebaseInvestigatorAgent.processOutput?.(report);
const processed = agent.processOutput?.(report);
expect(processed).toBe(JSON.stringify(report, null, 2));
});
});
@@ -11,8 +11,15 @@ import {
LS_TOOL_NAME,
READ_FILE_TOOL_NAME,
} from '../tools/tool-names.js';
import { DEFAULT_GEMINI_MODEL } from '../config/models.js';
import {
DEFAULT_THINKING_MODE,
DEFAULT_GEMINI_MODEL,
PREVIEW_GEMINI_FLASH_MODEL,
isPreviewModel,
} from '../config/models.js';
import { z } from 'zod';
import type { Config } from '../config/config.js';
import { ThinkingLevel } from '@google/genai';
// Define a type that matches the outputConfig schema for type safety.
const CodebaseInvestigationReportSchema = z.object({
@@ -41,65 +48,82 @@ const CodebaseInvestigationReportSchema = z.object({
* A Proof-of-Concept subagent specialized in analyzing codebase structure,
* dependencies, and technologies.
*/
export const CodebaseInvestigatorAgent: LocalAgentDefinition<
typeof CodebaseInvestigationReportSchema
> = {
name: 'codebase_investigator',
kind: 'local',
displayName: 'Codebase Investigator Agent',
description: `The specialized tool for codebase analysis, architectural mapping, and understanding system-wide dependencies.
export const CodebaseInvestigatorAgent = (
config: Config,
): LocalAgentDefinition<typeof CodebaseInvestigationReportSchema> => {
// Use Preview Flash model if the main model is any of the preview models.
// If the main model is not a preview model, use the default pro model.
const model = isPreviewModel(config.getModel())
? PREVIEW_GEMINI_FLASH_MODEL
: DEFAULT_GEMINI_MODEL;
return {
name: 'codebase_investigator',
kind: 'local',
displayName: 'Codebase Investigator Agent',
description: `The specialized tool for codebase analysis, architectural mapping, and understanding system-wide dependencies.
Invoke this tool for tasks like vague requests, bug root-cause analysis, system refactoring, comprehensive feature implementation or to answer questions about the codebase that require investigation.
It returns a structured report with key file paths, symbols, and actionable architectural insights.`,
inputConfig: {
inputSchema: {
type: 'object',
properties: {
objective: {
type: 'string',
description: `A comprehensive and detailed description of the user's ultimate goal.
inputConfig: {
inputSchema: {
type: 'object',
properties: {
objective: {
type: 'string',
description: `A comprehensive and detailed description of the user's ultimate goal.
You must include original user's objective as well as questions and any extra context and questions you may have.`,
},
},
},
required: ['objective'],
},
},
outputConfig: {
outputName: 'report',
description: 'The final investigation report as a JSON object.',
schema: CodebaseInvestigationReportSchema,
},
// The 'output' parameter is now strongly typed as CodebaseInvestigationReportSchema
processOutput: (output) => JSON.stringify(output, null, 2),
modelConfig: {
model: DEFAULT_GEMINI_MODEL,
generateContentConfig: {
temperature: 0.1,
topP: 0.95,
thinkingConfig: {
includeThoughts: true,
thinkingBudget: -1,
required: ['objective'],
},
},
},
outputConfig: {
outputName: 'report',
description: 'The final investigation report as a JSON object.',
schema: CodebaseInvestigationReportSchema,
},
runConfig: {
maxTimeMinutes: 5,
maxTurns: 15,
},
// The 'output' parameter is now strongly typed as CodebaseInvestigationReportSchema
processOutput: (output) => JSON.stringify(output, null, 2),
toolConfig: {
// Grant access only to read-only tools.
tools: [LS_TOOL_NAME, READ_FILE_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME],
},
modelConfig: {
model,
generateContentConfig: {
temperature: 0.1,
topP: 0.95,
thinkingConfig: isPreviewModel(model)
? {
includeThoughts: true,
thinkingLevel: ThinkingLevel.HIGH,
}
: {
includeThoughts: true,
thinkingBudget: DEFAULT_THINKING_MODE,
},
},
},
promptConfig: {
query: `Your task is to do a deep investigation of the codebase to find all relevant files, code locations, architectural mental map and insights to solve for the following user objective:
runConfig: {
maxTimeMinutes: 3,
maxTurns: 10,
},
toolConfig: {
// Grant access only to read-only tools.
tools: [
LS_TOOL_NAME,
READ_FILE_TOOL_NAME,
GLOB_TOOL_NAME,
GREP_TOOL_NAME,
],
},
promptConfig: {
query: `Your task is to do a deep investigation of the codebase to find all relevant files, code locations, architectural mental map and insights to solve for the following user objective:
<objective>
\${objective}
</objective>`,
systemPrompt: `You are **Codebase Investigator**, a hyper-specialized AI agent and an expert in reverse-engineering complex software projects. You are a sub-agent within a larger development system.
systemPrompt: `You are **Codebase Investigator**, a hyper-specialized AI agent and an expert in reverse-engineering complex software projects. You are a sub-agent within a larger development system.
Your **SOLE PURPOSE** is to build a complete mental model of the code relevant to a given investigation. You must identify all relevant files, understand their roles, and foresee the direct architectural consequences of potential changes.
You are a sub-agent in a larger system. Your only responsibility is to provide deep, actionable context.
- **DO:** Find the key modules, classes, and functions that are part of the problem and its solution.
@@ -158,5 +182,6 @@ When you are finished, you **MUST** call the \`complete_task\` tool. The \`repor
}
\`\`\`
`,
},
},
};
};
+48 -19
View File
@@ -14,7 +14,8 @@ import { coreEvents, CoreEvent } from '../utils/events.js';
import { A2AClientManager } from './a2a-client-manager.js';
import {
DEFAULT_GEMINI_FLASH_LITE_MODEL,
GEMINI_MODEL_ALIAS_AUTO,
DEFAULT_GEMINI_MODEL,
DEFAULT_THINKING_MODE,
PREVIEW_GEMINI_FLASH_MODEL,
PREVIEW_GEMINI_MODEL,
PREVIEW_GEMINI_MODEL_AUTO,
@@ -23,6 +24,7 @@ import * as tomlLoader from './agentLoader.js';
import { SimpleExtensionLoader } from '../utils/extensionLoader.js';
import type { ConfigParameters } from '../config/config.js';
import type { ToolRegistry } from '../tools/tool-registry.js';
import { ThinkingLevel } from '@google/genai';
vi.mock('./agentLoader.js', () => ({
loadAgentsFromDirectory: vi
@@ -127,14 +129,27 @@ describe('AgentRegistry', () => {
);
});
it('should use preview flash model for codebase investigator if main model is preview pro', async () => {
const previewConfig = makeMockedConfig({
model: PREVIEW_GEMINI_MODEL,
codebaseInvestigatorSettings: {
enabled: true,
model: GEMINI_MODEL_ALIAS_AUTO,
},
it('should use default model for codebase investigator for non-preview models', async () => {
const previewConfig = makeMockedConfig({ model: DEFAULT_GEMINI_MODEL });
const previewRegistry = new TestableAgentRegistry(previewConfig);
await previewRegistry.initialize();
const investigatorDef = previewRegistry.getDefinition(
'codebase_investigator',
) as LocalAgentDefinition;
expect(investigatorDef).toBeDefined();
expect(investigatorDef?.modelConfig.model).toBe(DEFAULT_GEMINI_MODEL);
expect(
investigatorDef?.modelConfig.generateContentConfig?.thinkingConfig,
).toStrictEqual({
includeThoughts: true,
thinkingBudget: DEFAULT_THINKING_MODE,
});
});
it('should use preview flash model for codebase investigator if main model is preview pro', async () => {
const previewConfig = makeMockedConfig({ model: PREVIEW_GEMINI_MODEL });
const previewRegistry = new TestableAgentRegistry(previewConfig);
await previewRegistry.initialize();
@@ -146,15 +161,17 @@ describe('AgentRegistry', () => {
expect(investigatorDef?.modelConfig.model).toBe(
PREVIEW_GEMINI_FLASH_MODEL,
);
expect(
investigatorDef?.modelConfig.generateContentConfig?.thinkingConfig,
).toStrictEqual({
includeThoughts: true,
thinkingLevel: ThinkingLevel.HIGH,
});
});
it('should use preview flash model for codebase investigator if main model is preview auto', async () => {
const previewConfig = makeMockedConfig({
model: PREVIEW_GEMINI_MODEL_AUTO,
codebaseInvestigatorSettings: {
enabled: true,
model: GEMINI_MODEL_ALIAS_AUTO,
},
});
const previewRegistry = new TestableAgentRegistry(previewConfig);
@@ -172,9 +189,13 @@ describe('AgentRegistry', () => {
it('should use the model from the investigator settings', async () => {
const previewConfig = makeMockedConfig({
model: PREVIEW_GEMINI_MODEL,
codebaseInvestigatorSettings: {
enabled: true,
model: DEFAULT_GEMINI_FLASH_LITE_MODEL,
agents: {
overrides: {
codebase_investigator: {
enabled: true,
modelConfig: { model: DEFAULT_GEMINI_FLASH_LITE_MODEL },
},
},
},
});
const previewRegistry = new TestableAgentRegistry(previewConfig);
@@ -232,8 +253,12 @@ describe('AgentRegistry', () => {
it('should NOT load TOML agents when enableAgents is false', async () => {
const disabledConfig = makeMockedConfig({
enableAgents: false,
codebaseInvestigatorSettings: { enabled: false },
cliHelpAgentSettings: { enabled: false },
agents: {
overrides: {
codebase_investigator: { enabled: false },
cli_help: { enabled: false },
},
},
});
const disabledRegistry = new TestableAgentRegistry(disabledConfig);
@@ -254,9 +279,13 @@ describe('AgentRegistry', () => {
expect(registry.getDefinition('cli_help')).toBeDefined();
});
it('should register CLI help agent if disabled', async () => {
it('should NOT register CLI help agent if disabled', async () => {
const config = makeMockedConfig({
cliHelpAgentSettings: { enabled: false },
agents: {
overrides: {
cli_help: { enabled: false },
},
},
});
const registry = new TestableAgentRegistry(config);
+3 -69
View File
@@ -16,13 +16,7 @@ import { A2AClientManager } from './a2a-client-manager.js';
import { ADCHandler } from './remote-invocation.js';
import { type z } from 'zod';
import { debugLogger } from '../utils/debugLogger.js';
import {
DEFAULT_GEMINI_MODEL,
GEMINI_MODEL_ALIAS_AUTO,
PREVIEW_GEMINI_FLASH_MODEL,
isPreviewModel,
isAutoModel,
} from '../config/models.js';
import { isAutoModel } from '../config/models.js';
import {
type ModelConfig,
ModelConfigService,
@@ -149,68 +143,8 @@ export class AgentRegistry {
}
private loadBuiltInAgents(): void {
const investigatorSettings = this.config.getCodebaseInvestigatorSettings();
const cliHelpSettings = this.config.getCliHelpAgentSettings();
const agentsSettings = this.config.getAgentsSettings();
const agentsOverrides = agentsSettings.overrides ?? {};
// Only register the agent if it's enabled in the settings and not explicitly disabled via overrides.
if (
investigatorSettings?.enabled &&
agentsOverrides[CodebaseInvestigatorAgent.name]?.enabled !== false
) {
let model;
const settingsModel = investigatorSettings.model;
// Check if the user explicitly set a model in the settings.
if (settingsModel && settingsModel !== GEMINI_MODEL_ALIAS_AUTO) {
model = settingsModel;
} else {
// Use Preview Flash model if the main model is any of the preview models
// If the main model is not preview model, use default pro model.
model = isPreviewModel(this.config.getModel())
? PREVIEW_GEMINI_FLASH_MODEL
: DEFAULT_GEMINI_MODEL;
}
const agentDef = {
...CodebaseInvestigatorAgent,
modelConfig: {
...CodebaseInvestigatorAgent.modelConfig,
model,
generateContentConfig: {
...CodebaseInvestigatorAgent.modelConfig.generateContentConfig,
thinkingConfig: {
...CodebaseInvestigatorAgent.modelConfig.generateContentConfig
?.thinkingConfig,
thinkingBudget:
investigatorSettings.thinkingBudget ??
CodebaseInvestigatorAgent.modelConfig.generateContentConfig
?.thinkingConfig?.thinkingBudget,
},
},
},
runConfig: {
...CodebaseInvestigatorAgent.runConfig,
maxTimeMinutes:
investigatorSettings.maxTimeMinutes ??
CodebaseInvestigatorAgent.runConfig.maxTimeMinutes,
maxTurns:
investigatorSettings.maxNumTurns ??
CodebaseInvestigatorAgent.runConfig.maxTurns,
},
};
this.registerLocalAgent(agentDef);
}
// Register the CLI help agent if it's explicitly enabled and not explicitly disabled via overrides.
if (
cliHelpSettings.enabled &&
agentsOverrides[CliHelpAgent.name]?.enabled !== false
) {
this.registerLocalAgent(CliHelpAgent(this.config));
}
// Register the generalist agent.
this.registerLocalAgent(CodebaseInvestigatorAgent(this.config));
this.registerLocalAgent(CliHelpAgent(this.config));
this.registerLocalAgent(GeneralistAgent(this.config));
}
+13 -10
View File
@@ -941,10 +941,14 @@ describe('Server Config (config.ts)', () => {
expect(wasReadFileToolRegistered).toBe(false);
});
it('should register subagents as tools when codebaseInvestigatorSettings.enabled is true', async () => {
it('should register subagents as tools when agents.overrides.codebase_investigator.enabled is true', async () => {
const params: ConfigParameters = {
...baseParams,
codebaseInvestigatorSettings: { enabled: true },
agents: {
overrides: {
codebase_investigator: { enabled: true },
},
},
};
const config = new Config(params);
@@ -991,11 +995,15 @@ describe('Server Config (config.ts)', () => {
expect(registeredWrappers).toHaveLength(1);
});
it('should not register subagents as tools when codebaseInvestigatorSettings.enabled is false', async () => {
it('should not register subagents as tools when agents are disabled', async () => {
const params: ConfigParameters = {
...baseParams,
codebaseInvestigatorSettings: { enabled: false },
cliHelpAgentSettings: { enabled: false },
agents: {
overrides: {
codebase_investigator: { enabled: false },
cli_help: { enabled: false },
},
},
};
const config = new Config(params);
@@ -1010,11 +1018,6 @@ describe('Server Config (config.ts)', () => {
expect(DelegateToAgentToolMock).not.toHaveBeenCalled();
});
it('should not set default codebase investigator model in config (defaults in registry)', () => {
const config = new Config(baseParams);
expect(config.getCodebaseInvestigatorSettings()?.model).toBeUndefined();
});
describe('with minified tool class names', () => {
beforeEach(() => {
Object.defineProperty(
+3 -39
View File
@@ -49,7 +49,6 @@ import {
DEFAULT_GEMINI_EMBEDDING_MODEL,
DEFAULT_GEMINI_FLASH_MODEL,
DEFAULT_GEMINI_MODEL_AUTO,
DEFAULT_THINKING_MODE,
isPreviewModel,
PREVIEW_GEMINI_MODEL,
PREVIEW_GEMINI_MODEL_AUTO,
@@ -144,14 +143,6 @@ export interface OutputSettings {
format?: OutputFormat;
}
export interface CodebaseInvestigatorSettings {
enabled?: boolean;
maxNumTurns?: number;
maxTimeMinutes?: number;
thinkingBudget?: number;
model?: string;
}
export interface ExtensionSetting {
name: string;
description: string;
@@ -168,10 +159,6 @@ export interface ResolvedExtensionSetting {
source?: string;
}
export interface CliHelpAgentSettings {
enabled?: boolean;
}
export interface AgentRunConfig {
maxTimeMinutes?: number;
maxTurns?: number;
@@ -368,8 +355,6 @@ export interface ConfigParameters {
policyEngineConfig?: PolicyEngineConfig;
output?: OutputSettings;
disableModelRouterForAuth?: AuthType[];
codebaseInvestigatorSettings?: CodebaseInvestigatorSettings;
cliHelpAgentSettings?: CliHelpAgentSettings;
continueOnFailedApiCall?: boolean;
retryFetchErrors?: boolean;
enableShellOutputEfficiency?: boolean;
@@ -513,8 +498,6 @@ export class Config {
private readonly messageBus: MessageBus;
private readonly policyEngine: PolicyEngine;
private readonly outputSettings: OutputSettings;
private readonly codebaseInvestigatorSettings: CodebaseInvestigatorSettings;
private readonly cliHelpAgentSettings: CliHelpAgentSettings;
private readonly continueOnFailedApiCall: boolean;
private readonly retryFetchErrors: boolean;
private readonly enableShellOutputEfficiency: boolean;
@@ -688,18 +671,6 @@ export class Config {
this.enableHooks = params.enableHooks ?? true;
this.disabledHooks = params.disabledHooks ?? [];
this.codebaseInvestigatorSettings = {
enabled: params.codebaseInvestigatorSettings?.enabled ?? true,
maxNumTurns: params.codebaseInvestigatorSettings?.maxNumTurns ?? 10,
maxTimeMinutes: params.codebaseInvestigatorSettings?.maxTimeMinutes ?? 3,
thinkingBudget:
params.codebaseInvestigatorSettings?.thinkingBudget ??
DEFAULT_THINKING_MODE,
model: params.codebaseInvestigatorSettings?.model,
};
this.cliHelpAgentSettings = {
enabled: params.cliHelpAgentSettings?.enabled ?? true,
};
this.continueOnFailedApiCall = params.continueOnFailedApiCall ?? true;
this.enableShellOutputEfficiency =
params.enableShellOutputEfficiency ?? true;
@@ -1895,14 +1866,6 @@ export class Config {
return this.enableHooksUI;
}
getCodebaseInvestigatorSettings(): CodebaseInvestigatorSettings {
return this.codebaseInvestigatorSettings;
}
getCliHelpAgentSettings(): CliHelpAgentSettings {
return this.cliHelpAgentSettings;
}
async createToolRegistry(): Promise<ToolRegistry> {
const registry = new ToolRegistry(this, this.messageBus);
@@ -1980,10 +1943,11 @@ export class Config {
* Registers the DelegateToAgentTool if agents or related features are enabled.
*/
private registerDelegateToAgentTool(registry: ToolRegistry): void {
const agentsOverrides = this.getAgentsSettings().overrides ?? {};
if (
this.isAgentsEnabled() ||
this.getCodebaseInvestigatorSettings().enabled ||
this.getCliHelpAgentSettings().enabled
agentsOverrides['codebase_investigator']?.enabled !== false ||
agentsOverrides['cli_help']?.enabled !== false
) {
// Check if the delegate tool itself is allowed (if allowedTools is set)
const allowedTools = this.getAllowedTools();