mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-13 12:57:12 -07:00
refactor: Introduce dedicated browser agent configuration with session mode, headless, profile path, and visual model settings.
This commit is contained in:
@@ -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 |
|
||||
|
||||
@@ -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[]):
|
||||
|
||||
@@ -196,7 +196,6 @@
|
||||
{
|
||||
"label": "resources_tab",
|
||||
"items": [
|
||||
{
|
||||
{
|
||||
"label": "Resources",
|
||||
"items": [
|
||||
@@ -216,7 +215,6 @@
|
||||
{ "label": "Uninstall", "slug": "docs/resources/uninstall" }
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user