diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index cbe2df5f30..5c95bcbcf9 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -2275,6 +2275,12 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record< type: 'boolean', description: 'Whether to enable the agent.', }, + customConfig: { + type: 'object', + description: + 'Agent-specific custom configuration. Each agent defines its own schema.', + additionalProperties: true, + }, }, }, CustomTheme: { diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts index 7a008f12a6..9f466d443f 100644 --- a/packages/core/src/config/config.test.ts +++ b/packages/core/src/config/config.test.ts @@ -1323,6 +1323,76 @@ describe('Server Config (config.ts)', () => { expect(mockCoreEvents.emitFeedback).not.toHaveBeenCalled(); }); }); + + describe('BrowserAgentConfig', () => { + it('should return default browser agent config when not provided', () => { + const config = new Config(baseParams); + const browserConfig = config.getBrowserAgentConfig(); + + expect(browserConfig.enabled).toBe(false); + expect(browserConfig.model).toBeUndefined(); + expect(browserConfig.customConfig.sessionMode).toBe('isolated'); + expect(browserConfig.customConfig.headless).toBe(false); + expect(browserConfig.customConfig.chromeProfilePath).toBeUndefined(); + expect(browserConfig.customConfig.visualModel).toBeUndefined(); + }); + + it('should return custom browser agent config from agents.overrides', () => { + const params: ConfigParameters = { + ...baseParams, + agents: { + overrides: { + browser_agent: { + enabled: true, + modelConfig: { model: 'custom-model' }, + customConfig: { + sessionMode: 'existing', + headless: true, + chromeProfilePath: '/path/to/profile', + visualModel: 'custom-visual-model', + }, + }, + }, + }, + }; + const config = new Config(params); + const browserConfig = config.getBrowserAgentConfig(); + + expect(browserConfig.enabled).toBe(true); + expect(browserConfig.model).toBe('custom-model'); + expect(browserConfig.customConfig.sessionMode).toBe('existing'); + expect(browserConfig.customConfig.headless).toBe(true); + expect(browserConfig.customConfig.chromeProfilePath).toBe( + '/path/to/profile', + ); + expect(browserConfig.customConfig.visualModel).toBe( + 'custom-visual-model', + ); + }); + + it('should apply defaults for partial custom config', () => { + const params: ConfigParameters = { + ...baseParams, + agents: { + overrides: { + browser_agent: { + enabled: true, + customConfig: { + headless: true, + }, + }, + }, + }, + }; + const config = new Config(params); + const browserConfig = config.getBrowserAgentConfig(); + + expect(browserConfig.enabled).toBe(true); + expect(browserConfig.customConfig.headless).toBe(true); + // Defaults for unspecified fields + expect(browserConfig.customConfig.sessionMode).toBe('isolated'); + }); + }); }); describe('setApprovalMode with folder trust', () => { diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 42f8508697..aa7ce13f64 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -193,10 +193,21 @@ export interface AgentRunConfig { maxTurns?: number; } +/** + * Override configuration for a specific agent. + * Generic fields (modelConfig, runConfig, enabled) are standard across all agents. + * Agent-specific settings go in customConfig - each agent defines its own type. + */ export interface AgentOverride { modelConfig?: ModelConfig; runConfig?: AgentRunConfig; enabled?: boolean; + /** + * Agent-specific custom configuration. + * Each agent defines and documents its own customConfig structure. + * Example: browser_agent uses BrowserAgentCustomConfig for sessionMode, headless, etc. + */ + customConfig?: Record; } export interface AgentSettings { @@ -254,6 +265,21 @@ export interface CustomTheme { GradientColors?: string[]; } +/** + * Browser agent custom configuration. + * Used in agents.overrides.browser_agent.customConfig + */ +export interface BrowserAgentCustomConfig { + /** Session mode: 'isolated' (launch new browser) or 'existing' (attach to Chrome M144+). Default: 'isolated' */ + sessionMode?: 'isolated' | 'existing'; + /** Run browser in headless mode. Default: false */ + headless?: boolean; + /** Path to Chrome profile directory for session persistence. */ + chromeProfilePath?: string; + /** Model override for the visual agent. */ + visualModel?: 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 @@ -2488,6 +2514,39 @@ export class Config { return this.enableHooksUI; } + /** + * Get override settings for a specific agent. + * Reads from agents.overrides.. + */ + getAgentOverride(agentName: string): AgentOverride | undefined { + return this.getAgentsSettings()?.overrides?.[agentName]; + } + + /** + * Get browser agent configuration. + * Combines generic AgentOverride fields with browser-specific customConfig. + * This is the canonical way to access browser agent settings. + */ + getBrowserAgentConfig(): { + enabled: boolean; + model?: string; + customConfig: BrowserAgentCustomConfig; + } { + const override = this.getAgentOverride('browser_agent'); + const customConfig = (override?.customConfig ?? + {}) as BrowserAgentCustomConfig; + return { + enabled: override?.enabled ?? false, + model: override?.modelConfig?.model, + customConfig: { + sessionMode: customConfig.sessionMode ?? 'isolated', + headless: customConfig.headless ?? false, + chromeProfilePath: customConfig.chromeProfilePath, + visualModel: customConfig.visualModel, + }, + }; + } + async createToolRegistry(): Promise { const registry = new ToolRegistry(this, this.messageBus);