refactor: Introduce dedicated browser agent configuration with session mode, headless, profile path, and visual model settings.

This commit is contained in:
Gaurav Ghosh
2026-02-23 11:52:00 -08:00
parent 52d9271e63
commit 64853dbfde
13 changed files with 179 additions and 77 deletions
+9
View File
@@ -87,6 +87,15 @@ they appear in the UI.
| Disable Loop Detection | `model.disableLoopDetection` | Disable automatic detection and prevention of infinite loops. | `false` |
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
### Agents
| UI Label | Setting | Description | Default |
| -------------------- | ---------------------------- | ---------------------------------------------------------- | -------------- |
| Browser Session Mode | `agents.browser.sessionMode` | Session mode: 'persistent', 'isolated', or 'existing'. | `"persistent"` |
| Browser Headless | `agents.browser.headless` | Run browser in headless mode. | `false` |
| Browser Profile Path | `agents.browser.profilePath` | Path to browser profile directory for session persistence. | `undefined` |
| Browser Visual Model | `agents.browser.visualModel` | Model override for the visual agent. | `undefined` |
### Context
| UI Label | Setting | Description | Default |
+21
View File
@@ -641,6 +641,27 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `{}`
- **Requires restart:** Yes
- **`agents.browser.sessionMode`** (enum):
- **Description:** Session mode: 'persistent', 'isolated', or 'existing'.
- **Default:** `"persistent"`
- **Values:** `"persistent"`, `"isolated"`, `"existing"`
- **Requires restart:** Yes
- **`agents.browser.headless`** (boolean):
- **Description:** Run browser in headless mode.
- **Default:** `false`
- **Requires restart:** Yes
- **`agents.browser.profilePath`** (string):
- **Description:** Path to browser profile directory for session persistence.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`agents.browser.visualModel`** (string):
- **Description:** Model override for the visual agent.
- **Default:** `undefined`
- **Requires restart:** Yes
#### `context`
- **`context.fileName`** (string | string[]):
-2
View File
@@ -196,7 +196,6 @@
{
"label": "resources_tab",
"items": [
{
{
"label": "Resources",
"items": [
@@ -216,7 +215,6 @@
{ "label": "Uninstall", "slug": "docs/resources/uninstall" }
]
}
]
},
{
+54 -6
View File
@@ -964,6 +964,60 @@ const SETTINGS_SCHEMA = {
ref: 'AgentOverride',
},
},
browser: {
type: 'object',
label: 'Browser Agent',
category: 'Advanced',
requiresRestart: true,
default: {},
description: 'Settings specific to the browser agent.',
showInDialog: false,
properties: {
sessionMode: {
type: 'enum',
label: 'Browser Session Mode',
category: 'Advanced',
requiresRestart: true,
default: 'persistent',
description:
"Session mode: 'persistent', 'isolated', or 'existing'.",
showInDialog: true,
options: [
{ value: 'persistent', label: 'Persistent' },
{ value: 'isolated', label: 'Isolated' },
{ value: 'existing', label: 'Existing' },
],
},
headless: {
type: 'boolean',
label: 'Browser Headless',
category: 'Advanced',
requiresRestart: true,
default: false,
description: 'Run browser in headless mode.',
showInDialog: true,
},
profilePath: {
type: 'string',
label: 'Browser Profile Path',
category: 'Advanced',
requiresRestart: true,
default: undefined as string | undefined,
description:
'Path to browser profile directory for session persistence.',
showInDialog: true,
},
visualModel: {
type: 'string',
label: 'Browser Visual Model',
category: 'Advanced',
requiresRestart: true,
default: undefined as string | undefined,
description: 'Model override for the visual agent.',
showInDialog: true,
},
},
},
},
},
@@ -2275,12 +2329,6 @@ 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: {
@@ -170,7 +170,8 @@ export function SettingsDialog({
updated = setPendingSettingValue(key, value, updated);
} else if (
(def?.type === 'number' && typeof value === 'number') ||
(def?.type === 'string' && typeof value === 'string')
(def?.type === 'string' && typeof value === 'string') ||
(def?.type === 'enum' && typeof value === 'string')
) {
updated = setPendingSettingValueAny(key, value, updated);
}
@@ -73,11 +73,11 @@ describe('browserAgentFactory', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
headless: false,
},
},
},
browser: {
headless: false,
},
},
});
@@ -160,12 +160,12 @@ describe('browserAgentFactory', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
headless: false,
visualModel: 'gemini-2.5-flash-preview',
},
},
},
browser: {
headless: false,
visualModel: 'gemini-2.5-flash-preview',
},
},
});
@@ -185,12 +185,12 @@ describe('browserAgentFactory', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
headless: false,
visualModel: 'gemini-2.5-flash-preview',
},
},
},
browser: {
headless: false,
visualModel: 'gemini-2.5-flash-preview',
},
},
});
@@ -32,11 +32,12 @@ describe('BrowserAgentInvocation', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
headless: false,
},
},
},
browser: {
headless: false,
sessionMode: 'isolated',
},
},
});
@@ -56,11 +56,11 @@ describe('BrowserManager', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
headless: false,
},
},
},
browser: {
headless: false,
},
},
});
@@ -165,11 +165,11 @@ describe('BrowserManager', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
headless: true,
},
},
},
browser: {
headless: true,
},
},
});
@@ -182,17 +182,17 @@ describe('BrowserManager', () => {
});
});
it('should pass chromeProfilePath as --userDataDir when configured', async () => {
it('should pass profilePath as --userDataDir when configured', async () => {
const profileConfig = makeFakeConfig({
agents: {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
chromeProfilePath: '/path/to/profile',
},
},
},
browser: {
profilePath: '/path/to/profile',
},
},
});
@@ -211,11 +211,11 @@ describe('BrowserManager', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
sessionMode: 'isolated',
},
},
},
browser: {
sessionMode: 'isolated',
},
},
});
@@ -234,11 +234,11 @@ describe('BrowserManager', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
sessionMode: 'existing',
},
},
},
browser: {
sessionMode: 'existing',
},
},
});
@@ -268,11 +268,11 @@ describe('BrowserManager', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
sessionMode: 'existing',
},
},
},
browser: {
sessionMode: 'existing',
},
},
});
@@ -259,11 +259,8 @@ export class BrowserManager {
if (browserConfig.customConfig.headless) {
mcpArgs.push('--headless');
}
if (browserConfig.customConfig.chromeProfilePath) {
mcpArgs.push(
'--userDataDir',
browserConfig.customConfig.chromeProfilePath,
);
if (browserConfig.customConfig.profilePath) {
mcpArgs.push('--userDataDir', browserConfig.customConfig.profilePath);
}
debugLogger.log(
@@ -343,7 +340,7 @@ export class BrowserManager {
`To fix this:\n` +
` 1. Close all Chrome windows using this profile, OR\n` +
` 2. Set sessionMode to "isolated" in settings.json to use a temporary profile, OR\n` +
` 3. Set chromeProfilePath in settings.json to use a different profile directory`,
` 3. Set profilePath in settings.json to use a different profile directory`,
);
}
// existing mode — shouldn't normally hit this, but handle gracefully
+11 -13
View File
@@ -1333,7 +1333,7 @@ describe('Server Config (config.ts)', () => {
expect(browserConfig.model).toBeUndefined();
expect(browserConfig.customConfig.sessionMode).toBe('persistent');
expect(browserConfig.customConfig.headless).toBe(false);
expect(browserConfig.customConfig.chromeProfilePath).toBeUndefined();
expect(browserConfig.customConfig.profilePath).toBeUndefined();
expect(browserConfig.customConfig.visualModel).toBeUndefined();
});
@@ -1345,14 +1345,14 @@ describe('Server Config (config.ts)', () => {
browser_agent: {
enabled: true,
modelConfig: { model: 'custom-model' },
customConfig: {
sessionMode: 'existing',
headless: true,
chromeProfilePath: '/path/to/profile',
visualModel: 'custom-visual-model',
},
},
},
browser: {
sessionMode: 'existing',
headless: true,
profilePath: '/path/to/profile',
visualModel: 'custom-visual-model',
},
},
};
const config = new Config(params);
@@ -1362,9 +1362,7 @@ describe('Server Config (config.ts)', () => {
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.profilePath).toBe('/path/to/profile');
expect(browserConfig.customConfig.visualModel).toBe(
'custom-visual-model',
);
@@ -1377,11 +1375,11 @@ describe('Server Config (config.ts)', () => {
overrides: {
browser_agent: {
enabled: true,
customConfig: {
headless: true,
},
},
},
browser: {
headless: true,
},
},
};
const config = new Config(params);
+4 -10
View File
@@ -202,16 +202,11 @@ 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<string, unknown>;
}
export interface AgentSettings {
overrides?: Record<string, AgentOverride>;
browser?: BrowserAgentCustomConfig;
}
export interface CustomTheme {
@@ -281,7 +276,7 @@ export interface BrowserAgentCustomConfig {
/** Run browser in headless mode. Default: false */
headless?: boolean;
/** Path to Chrome profile directory for session persistence. */
chromeProfilePath?: string;
profilePath?: string;
/** Model override for the visual agent. */
visualModel?: string;
}
@@ -2539,15 +2534,14 @@ export class Config {
customConfig: BrowserAgentCustomConfig;
} {
const override = this.getAgentOverride('browser_agent');
const customConfig = (override?.customConfig ??
{}) as BrowserAgentCustomConfig;
const customConfig = this.getAgentsSettings()?.browser ?? {};
return {
enabled: override?.enabled ?? false,
model: override?.modelConfig?.model,
customConfig: {
sessionMode: customConfig.sessionMode ?? 'persistent',
headless: customConfig.headless ?? false,
chromeProfilePath: customConfig.chromeProfilePath,
profilePath: customConfig.profilePath,
visualModel: customConfig.visualModel,
},
};
+4 -1
View File
@@ -149,6 +149,9 @@ describe('GeminiCliAgent Integration', () => {
throw new Error('Dynamic instruction failure');
},
model: 'gemini-2.0-flash',
fakeResponses: RECORD_MODE
? undefined
: getGoldenPath('agent-dynamic-instructions'),
});
const session = agent.session();
@@ -159,5 +162,5 @@ describe('GeminiCliAgent Integration', () => {
// Just consume the stream
}
}).rejects.toThrow('Dynamic instruction failure');
});
}, 30000);
});
+37 -5
View File
@@ -1086,6 +1086,43 @@
"additionalProperties": {
"$ref": "#/$defs/AgentOverride"
}
},
"browser": {
"title": "Browser Agent",
"description": "Settings specific to the browser agent.",
"markdownDescription": "Settings specific to the browser agent.\n\n- Category: `Advanced`\n- Requires restart: `yes`\n- Default: `{}`",
"default": {},
"type": "object",
"properties": {
"sessionMode": {
"title": "Browser Session Mode",
"description": "Session mode: 'persistent', 'isolated', or 'existing'.",
"markdownDescription": "Session mode: 'persistent', 'isolated', or 'existing'.\n\n- Category: `Advanced`\n- Requires restart: `yes`\n- Default: `persistent`",
"default": "persistent",
"type": "string",
"enum": ["persistent", "isolated", "existing"]
},
"headless": {
"title": "Browser Headless",
"description": "Run browser in headless mode.",
"markdownDescription": "Run browser in headless mode.\n\n- Category: `Advanced`\n- Requires restart: `yes`\n- Default: `false`",
"default": false,
"type": "boolean"
},
"profilePath": {
"title": "Browser Profile Path",
"description": "Path to browser profile directory for session persistence.",
"markdownDescription": "Path to browser profile directory for session persistence.\n\n- Category: `Advanced`\n- Requires restart: `yes`",
"type": "string"
},
"visualModel": {
"title": "Browser Visual Model",
"description": "Model override for the visual agent.",
"markdownDescription": "Model override for the visual agent.\n\n- Category: `Advanced`\n- Requires restart: `yes`",
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
@@ -2092,11 +2129,6 @@
"enabled": {
"type": "boolean",
"description": "Whether to enable the agent."
},
"customConfig": {
"type": "object",
"description": "Agent-specific custom configuration. Each agent defines its own schema.",
"additionalProperties": true
}
}
},