diff --git a/README.md b/README.md
index eb660bf..c8287ef 100644
--- a/README.md
+++ b/README.md
@@ -103,7 +103,7 @@ The node uses one Browserbase credential:
| --- | --- |
| `Browserbase API Key` | Required for all resources |
| `Browserbase Project ID (Deprecated)` | Optional legacy header |
-| `Model API Key` | Optional. Only needed for Agent when using your own model provider key |
+| `Anthropic / OpenAI / Google API Key` | Optional. Bring your own model API key per provider for the Agent. Leave all empty to use the Browserbase Model Gateway. |
## Example Usage
diff --git a/credentials/BrowserbaseApi.credentials.ts b/credentials/BrowserbaseApi.credentials.ts
index e11025e..65ed981 100644
--- a/credentials/BrowserbaseApi.credentials.ts
+++ b/credentials/BrowserbaseApi.credentials.ts
@@ -44,13 +44,44 @@ export class BrowserbaseApi implements ICredentialType {
description: 'Optional. Your Browserbase project ID (no longer required for new setups)',
},
{
- displayName: 'Model API Key',
+ displayName: 'Anthropic API Key',
+ name: 'anthropicApiKey',
+ type: 'string',
+ typeOptions: { password: true },
+ default: '',
+ required: false,
+ description:
+ 'Optional. Bring your own Anthropic key for the Agent. Leave all model keys empty to use the Browserbase Model Gateway.',
+ },
+ {
+ displayName: 'OpenAI API Key',
+ name: 'openAiApiKey',
+ type: 'string',
+ typeOptions: { password: true },
+ default: '',
+ required: false,
+ description:
+ 'Optional. Bring your own OpenAI key for the Agent. Leave all model keys empty to use the Browserbase Model Gateway.',
+ },
+ {
+ displayName: 'Google API Key',
+ name: 'googleApiKey',
+ type: 'string',
+ typeOptions: { password: true },
+ default: '',
+ required: false,
+ description:
+ 'Optional. Bring your own Google (Gemini) key for the Agent. Leave all model keys empty to use the Browserbase Model Gateway.',
+ },
+ {
+ displayName: 'Model API Key (Deprecated)',
name: 'modelApiKey',
type: 'string',
typeOptions: { password: true },
default: '',
required: false,
- description: 'Optional. Provide your own model API key, or leave blank to use the Browserbase Model Gateway.',
+ description:
+ 'Deprecated. Use the provider-specific keys above instead. Kept for credentials created before per-provider keys existed; used as a fallback when no matching provider key is set.',
},
];
diff --git a/nodes/Browserbase/Browserbase.node.ts b/nodes/Browserbase/Browserbase.node.ts
index a21926f..0446e1a 100644
--- a/nodes/Browserbase/Browserbase.node.ts
+++ b/nodes/Browserbase/Browserbase.node.ts
@@ -7,6 +7,7 @@ import {
type INodeCredentialTestResult,
type INodeExecutionData,
type INodeProperties,
+ type INodePropertyOptions,
type INodeType,
type INodeTypeDescription,
type IHttpRequestMethods,
@@ -16,6 +17,59 @@ import {
const STAGEHAND_BASE_URL = 'https://api.stagehand.browserbase.com';
const API_BASE_URL = 'https://api.browserbase.com';
+// Single source of truth for CUA "Agent Model" options — mirrors Stagehand's
+// AVAILABLE_CUA_MODELS
+const CUA_MODEL_OPTIONS: INodePropertyOptions[] = [
+ { name: 'Claude Fable 5 (Anthropic)', value: 'anthropic/claude-fable-5' },
+ {
+ name: 'Claude Haiku 4.5 (2025-10-01, Anthropic)',
+ value: 'anthropic/claude-haiku-4-5-20251001',
+ },
+ { name: 'Claude Haiku 4.5 (Anthropic)', value: 'anthropic/claude-haiku-4-5' },
+ {
+ name: 'Claude Opus 4.5 (2025-11-01, Anthropic)',
+ value: 'anthropic/claude-opus-4-5-20251101',
+ },
+ { name: 'Claude Opus 4.6 (Anthropic)', value: 'anthropic/claude-opus-4-6' },
+ { name: 'Claude Opus 4.8 (Anthropic)', value: 'anthropic/claude-opus-4-8' },
+ {
+ name: 'Claude Sonnet 4 (2025-05-14, Anthropic)',
+ value: 'anthropic/claude-sonnet-4-20250514',
+ },
+ {
+ name: 'Claude Sonnet 4.5 (2025-09-29, Anthropic)',
+ value: 'anthropic/claude-sonnet-4-5-20250929',
+ },
+ { name: 'Claude Sonnet 4.6 (Anthropic)', value: 'anthropic/claude-sonnet-4-6' },
+ {
+ name: 'Computer Use Preview (2025-03-11, OpenAI)',
+ value: 'openai/computer-use-preview-2025-03-11',
+ },
+ { name: 'Computer Use Preview (OpenAI)', value: 'openai/computer-use-preview' },
+ { name: 'Fara 7B (Microsoft)', value: 'microsoft/fara-7b' },
+ { name: 'Gemini 2.5 CUA (Google)', value: 'google/gemini-2.5-computer-use-preview-10-2025' },
+ { name: 'Gemini 3 Flash (Google)', value: 'google/gemini-3-flash-preview' },
+ { name: 'Gemini 3 Pro (Google)', value: 'google/gemini-3-pro-preview' },
+ { name: 'Gemini 3.5 Flash (Google)', value: 'google/gemini-3.5-flash' },
+ { name: 'GPT-5.4 (OpenAI)', value: 'openai/gpt-5.4' },
+ { name: 'GPT-5.4 Mini (OpenAI)', value: 'openai/gpt-5.4-mini' },
+ { name: 'GPT-5.5 (OpenAI)', value: 'openai/gpt-5.5' },
+];
+
+const CUA_MODELS = new Set(CUA_MODEL_OPTIONS.map((option) => option.value));
+
+// Curated "Agent Model" options for Hybrid mode (must support coordinate actions).
+// Shared by the v3 and legacy modelHybrid dropdowns.
+const HYBRID_MODEL_OPTIONS: INodePropertyOptions[] = [
+ { name: 'Claude Haiku 4.5 (Anthropic)', value: 'anthropic/claude-haiku-4-5' },
+ { name: 'Claude Opus 4.8 (Anthropic)', value: 'anthropic/claude-opus-4-8' },
+ { name: 'Claude Sonnet 4.6 (Anthropic)', value: 'anthropic/claude-sonnet-4-6' },
+ { name: 'Gemini 3 Flash (Google)', value: 'google/gemini-3-flash-preview' },
+ { name: 'Gemini 3 Pro (Google)', value: 'google/gemini-3-pro-preview' },
+ { name: 'GPT-5.4 Mini (OpenAI)', value: 'openai/gpt-5.4-mini' },
+ { name: 'GPT-5.5 (OpenAI)', value: 'openai/gpt-5.5' },
+];
+
type BrowserbaseHeaders = Record;
type BrowserOptions = {
@@ -63,7 +117,7 @@ function getSessionId(response: Record): string | undefined {
function getHeaders(
credentials: ICredentialDataDecryptedObject,
options?: {
- includeModelApiKey?: boolean;
+ modelApiKey?: string;
},
): BrowserbaseHeaders {
const headers: BrowserbaseHeaders = {
@@ -72,8 +126,8 @@ function getHeaders(
'x-bb-api-key': credentials.browserbaseApiKey as string,
};
- if (options?.includeModelApiKey) {
- headers['x-model-api-key'] = credentials.modelApiKey as string;
+ if (options?.modelApiKey) {
+ headers['x-model-api-key'] = options.modelApiKey;
}
const projectId = (credentials.browserbaseProjectId as string)?.trim();
@@ -84,13 +138,63 @@ function getHeaders(
return headers;
}
+function resolveModelApiKey(
+ credentials: ICredentialDataDecryptedObject,
+ provider: string,
+): string {
+ const perProvider: Record = {
+ anthropic: credentials.anthropicApiKey,
+ openai: credentials.openAiApiKey,
+ google: credentials.googleApiKey,
+ };
+ // Fall back to the legacy single `modelApiKey` field for credentials saved before per-provider keys existed.
+ return ((perProvider[provider] as string) || (credentials.modelApiKey as string) || '').trim();
+}
+
function buildProperties(): INodeProperties[] {
return [
+ {
+ displayName: 'Action',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { gte: 3 } }],
+ },
+ },
+ options: [
+ {
+ name: 'Run an Agent',
+ value: 'execute',
+ description: 'Run an AI agent to perform browser automation tasks',
+ action: 'Run an agent',
+ },
+ {
+ name: 'Fetch a Webpage',
+ value: 'fetch',
+ description: 'Fetch a page without starting a browser session',
+ action: 'Fetch a webpage',
+ },
+ {
+ name: 'Search the Web',
+ value: 'search',
+ description: 'Search the web and return structured results',
+ action: 'Search the web',
+ },
+ ],
+ default: 'execute',
+ },
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { lt: 3 } }],
+ },
+ },
options: [
{
name: 'Agent',
@@ -114,15 +218,16 @@ function buildProperties(): INodeProperties[] {
noDataExpression: true,
displayOptions: {
show: {
+ '@version': [{ _cnd: { lt: 3 } }],
resource: ['agent'],
},
},
options: [
{
- name: 'Execute',
+ name: 'Run an Agent',
value: 'execute',
- description: 'Execute an AI agent to perform browser automation tasks',
- action: 'Execute an agent',
+ description: 'Run an AI agent to perform browser automation tasks',
+ action: 'Run an agent',
},
],
default: 'execute',
@@ -134,6 +239,7 @@ function buildProperties(): INodeProperties[] {
noDataExpression: true,
displayOptions: {
show: {
+ '@version': [{ _cnd: { lt: 3 } }],
resource: ['fetch'],
},
},
@@ -154,6 +260,7 @@ function buildProperties(): INodeProperties[] {
noDataExpression: true,
displayOptions: {
show: {
+ '@version': [{ _cnd: { lt: 3 } }],
resource: ['search'],
},
},
@@ -168,18 +275,48 @@ function buildProperties(): INodeProperties[] {
default: 'search',
},
{
- displayName: 'Mode Info',
- name: 'modeNotice',
- type: 'notice',
- default: '',
+ displayName: 'Model',
+ name: 'driverModel',
+ type: 'options',
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { gte: 3 } }],
operation: ['execute'],
},
},
+ options: [
+ {
+ name: 'Claude Haiku 4.5 (Anthropic)',
+ value: 'anthropic/claude-haiku-4-5',
+ },
+ {
+ name: 'Claude Opus 4.6 (Anthropic)',
+ value: 'anthropic/claude-opus-4-6',
+ },
+ {
+ name: 'Claude Sonnet 4.6 (Anthropic)',
+ value: 'anthropic/claude-sonnet-4-6',
+ },
+ {
+ name: 'Gemini 3 Flash (Google)',
+ value: 'google/gemini-3-flash-preview',
+ },
+ {
+ name: 'Gemini 3 Pro (Google)',
+ value: 'google/gemini-3-pro-preview',
+ },
+ {
+ name: 'GPT-4o (OpenAI)',
+ value: 'openai/gpt-4o',
+ },
+ {
+ name: 'GPT-4o Mini (OpenAI)',
+ value: 'openai/gpt-4o-mini',
+ },
+ ],
+ default: 'google/gemini-3-flash-preview',
description:
- 'CUA uses vision/coordinates (best for complex UIs). DOM uses selectors (faster, any LLM). Hybrid combines both.',
+ 'The model that drives the browser and runs the agent. Handles both navigation and reasoning by default. To use a different reasoning model, set "Agent Model" in Model Options. See Stagehand model evals to compare models.',
},
{
displayName: 'Starting URL',
@@ -191,7 +328,6 @@ function buildProperties(): INodeProperties[] {
description: 'The starting page URL for the agent',
displayOptions: {
show: {
- resource: ['agent'],
operation: ['execute'],
},
},
@@ -209,11 +345,163 @@ function buildProperties(): INodeProperties[] {
description: 'The task for the agent to complete',
displayOptions: {
show: {
- resource: ['agent'],
operation: ['execute'],
},
},
},
+ {
+ displayName: 'Model Options',
+ name: 'modelOptions',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ displayOptions: {
+ show: {
+ '@version': [{ _cnd: { gte: 3 } }],
+ operation: ['execute'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Agent Model',
+ name: 'modelCua',
+ type: 'options',
+ displayOptions: {
+ show: {
+ mode: ['cua'],
+ },
+ },
+ options: CUA_MODEL_OPTIONS,
+ default: 'google/gemini-3-flash-preview',
+ description:
+ 'Optional. Overrides the reasoning model for CUA mode. Defaults to the Model above. See Stagehand model evals to compare models.',
+ },
+ {
+ displayName: 'Agent Model',
+ name: 'modelDom',
+ type: 'options',
+ displayOptions: {
+ show: {
+ mode: ['dom'],
+ },
+ },
+ options: [
+ {
+ name: 'Claude Sonnet 4.6 (Anthropic)',
+ value: 'anthropic/claude-sonnet-4-6',
+ },
+ {
+ name: 'Gemini 3 Flash (Google)',
+ value: 'google/gemini-3-flash-preview',
+ },
+ {
+ name: 'Gemini 3 Pro (Google)',
+ value: 'google/gemini-3-pro-preview',
+ },
+ {
+ name: 'GPT-4.1 (OpenAI)',
+ value: 'openai/gpt-4.1',
+ },
+ {
+ name: 'GPT-4o (OpenAI)',
+ value: 'openai/gpt-4o',
+ },
+ {
+ name: 'GPT-4o Mini (OpenAI) - Budget',
+ value: 'openai/gpt-4o-mini',
+ },
+ ],
+ default: 'google/gemini-3-flash-preview',
+ description:
+ 'Optional. Overrides the reasoning model for DOM mode. Defaults to the Model above. See Stagehand model evals to compare models.',
+ },
+ {
+ displayName: 'Agent Model',
+ name: 'modelHybrid',
+ type: 'options',
+ displayOptions: {
+ show: {
+ mode: ['hybrid'],
+ },
+ },
+ options: HYBRID_MODEL_OPTIONS,
+ default: 'google/gemini-3-flash-preview',
+ description:
+ 'Optional. Overrides the reasoning model for Hybrid mode (must support coordinate actions). Defaults to the Model above. See Stagehand model evals to compare models.',
+ },
+ {
+ displayName: 'Highlight Cursor',
+ name: 'highlightCursor',
+ type: 'boolean',
+ default: true,
+ description: 'Whether to highlight the cursor during execution (CUA/Hybrid only)',
+ },
+ {
+ displayName: 'Max Steps',
+ name: 'maxSteps',
+ type: 'number',
+ default: 20,
+ description: 'Maximum number of steps the agent can take',
+ },
+ {
+ displayName: 'Mode',
+ name: 'mode',
+ type: 'options',
+ options: [
+ {
+ name: 'CUA (Computer Use Agent)',
+ value: 'cua',
+ description: 'Uses vision and coordinates. Works with CUA-specific models.',
+ },
+ {
+ name: 'DOM',
+ value: 'dom',
+ description: 'Uses DOM selectors. Works with any LLM. Faster.',
+ },
+ {
+ name: 'Hybrid (Experimental)',
+ value: 'hybrid',
+ description: 'Combines vision and DOM. Requires specific models.',
+ },
+ ],
+ default: 'cua',
+ description:
+ 'How the agent interacts with pages. CUA uses vision/coordinates (best for complex UIs). DOM uses selectors (faster, works with any LLM). Hybrid combines both. How to pick a mode.',
+ },
+ {
+ displayName: 'Model Source',
+ name: 'modelSource',
+ type: 'options',
+ options: [
+ {
+ name: 'Model Gateway (Browserbase)',
+ value: 'gateway',
+ description: 'Use Browserbase-managed model routing. Mix any providers freely.',
+ },
+ {
+ name: 'User-Provided API Key',
+ value: 'userProvidedKey',
+ description:
+ 'Use your own model API key from credentials. Same provider required for both models.',
+ },
+ ],
+ default: 'gateway',
+ description:
+ 'How model calls are routed. Model Gateway lets you freely mix providers. User-provided API key uses your own key from credentials and requires the Model and Agent Model to be from the same provider.',
+ },
+ {
+ displayName: 'System Prompt',
+ name: 'systemPrompt',
+ type: 'string',
+ typeOptions: {
+ rows: 4,
+ },
+ default: '',
+ placeholder: 'e.g. You are a helpful assistant that extracts data from websites',
+ description: 'Custom system prompt for the agent',
+ },
+ ],
+ },
{
displayName: 'Model Source',
name: 'modelSource',
@@ -221,7 +509,7 @@ function buildProperties(): INodeProperties[] {
noDataExpression: true,
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { lt: 3 } }],
operation: ['execute'],
},
},
@@ -240,37 +528,7 @@ function buildProperties(): INodeProperties[] {
],
default: 'gateway',
description:
- 'Choose how model calls are routed. Model Gateway lets you mix providers; User-provided API key requires both models from the same provider.',
- },
- {
- displayName: 'Model Info',
- name: 'modelNoticeGateway',
- type: 'notice',
- default: '',
- displayOptions: {
- show: {
- resource: ['agent'],
- operation: ['execute'],
- modelSource: ['gateway'],
- },
- },
- description:
- 'Using the Browserbase Model Gateway. You can freely mix models from different providers for Driver and Agent.',
- },
- {
- displayName: 'Model Info',
- name: 'modelNoticeBYOK',
- type: 'notice',
- default: '',
- displayOptions: {
- show: {
- resource: ['agent'],
- operation: ['execute'],
- modelSource: ['userProvidedKey'],
- },
- },
- description:
- 'Using your own API key from credentials. Both Driver and Agent models MUST be from the same provider.',
+ 'How model calls are routed. Model Gateway lets you freely mix providers for Driver and Agent. User-provided API key uses your own key from credentials and requires both models from the same provider.',
},
{
displayName: 'Driver Model',
@@ -278,7 +536,7 @@ function buildProperties(): INodeProperties[] {
type: 'options',
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { lt: 3 } }],
operation: ['execute'],
},
},
@@ -297,11 +555,11 @@ function buildProperties(): INodeProperties[] {
},
{
name: 'Gemini 3 Flash (Google)',
- value: 'google/gemini-3-flash',
+ value: 'google/gemini-3-flash-preview',
},
{
name: 'Gemini 3 Pro (Google)',
- value: 'google/gemini-3-pro',
+ value: 'google/gemini-3-pro-preview',
},
{
name: 'GPT-4o (OpenAI)',
@@ -313,7 +571,8 @@ function buildProperties(): INodeProperties[] {
},
],
default: 'anthropic/claude-sonnet-4-6',
- description: 'Model for browser session (DOM-based, used for navigation)',
+ description:
+ 'Model for browser session (DOM-based, used for navigation). See Stagehand model evals to compare model performance.',
},
{
displayName: 'Mode',
@@ -321,7 +580,7 @@ function buildProperties(): INodeProperties[] {
type: 'options',
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { lt: 3 } }],
operation: ['execute'],
},
},
@@ -343,7 +602,8 @@ function buildProperties(): INodeProperties[] {
},
],
default: 'cua',
- description: 'Agent mode determines how the agent interacts with pages',
+ description:
+ 'How the agent interacts with pages. CUA uses vision/coordinates (best for complex UIs). DOM uses selectors (faster, works with any LLM). Hybrid combines both. How to pick a mode.',
},
{
displayName: 'Agent Model',
@@ -351,47 +611,15 @@ function buildProperties(): INodeProperties[] {
type: 'options',
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { lt: 3 } }],
operation: ['execute'],
mode: ['cua'],
},
},
- options: [
- {
- name: 'Claude Haiku 4.5 (Anthropic)',
- value: 'anthropic/claude-haiku-4-5',
- },
- {
- name: 'Claude Opus 4.6 (Anthropic)',
- value: 'anthropic/claude-opus-4-6',
- },
- {
- name: 'Claude Sonnet 4.6 (Anthropic)',
- value: 'anthropic/claude-sonnet-4-6',
- },
- {
- name: 'Computer Use Preview (2025-03-11, OpenAI)',
- value: 'openai/computer-use-preview-2025-03-11',
- },
- {
- name: 'Computer Use Preview (OpenAI)',
- value: 'openai/computer-use-preview',
- },
- {
- name: 'Gemini 2.5 CUA (Google)',
- value: 'google/gemini-2.5-computer-use-preview-10-2025',
- },
- {
- name: 'Gemini 3 Flash (Google)',
- value: 'google/gemini-3-flash-preview',
- },
- {
- name: 'Gemini 3 Pro (Google)',
- value: 'google/gemini-3-pro-preview',
- },
- ],
+ options: CUA_MODEL_OPTIONS,
default: 'anthropic/claude-sonnet-4-6',
- description: 'CUA model for vision-based browser control',
+ description:
+ 'CUA model for vision-based browser control. See Stagehand model evals to compare model performance.',
},
{
displayName: 'Agent Model',
@@ -399,7 +627,7 @@ function buildProperties(): INodeProperties[] {
type: 'options',
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { lt: 3 } }],
operation: ['execute'],
mode: ['dom'],
},
@@ -431,7 +659,8 @@ function buildProperties(): INodeProperties[] {
},
],
default: 'anthropic/claude-sonnet-4-6',
- description: 'LLM for DOM-based browser control',
+ description:
+ 'LLM for DOM-based browser control. See Stagehand model evals to compare model performance.',
},
{
displayName: 'Agent Model',
@@ -439,27 +668,15 @@ function buildProperties(): INodeProperties[] {
type: 'options',
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { lt: 3 } }],
operation: ['execute'],
mode: ['hybrid'],
},
},
- options: [
- {
- name: 'Gemini 3 Flash (Google)',
- value: 'google/gemini-3-flash-preview',
- },
- {
- name: 'Claude Sonnet 4.6 (Anthropic)',
- value: 'anthropic/claude-sonnet-4-6',
- },
- {
- name: 'Claude Haiku 4.5 (Anthropic)',
- value: 'anthropic/claude-haiku-4-5-20251001',
- },
- ],
+ options: HYBRID_MODEL_OPTIONS,
default: 'anthropic/claude-sonnet-4-6',
- description: 'Model for hybrid mode (must support coordinate actions)',
+ description:
+ 'Model for hybrid mode (must support coordinate actions). See Stagehand model evals to compare model performance.',
},
{
displayName: 'Options',
@@ -469,7 +686,7 @@ function buildProperties(): INodeProperties[] {
default: {},
displayOptions: {
show: {
- resource: ['agent'],
+ '@version': [{ _cnd: { lt: 3 } }],
operation: ['execute'],
},
},
@@ -512,9 +729,7 @@ function buildProperties(): INodeProperties[] {
'Pass sensitive data to the agent. The LLM sees %variableName% placeholders and descriptions, but never the actual values.',
displayOptions: {
show: {
- resource: ['agent'],
operation: ['execute'],
- mode: ['dom', 'hybrid'],
},
},
options: [
@@ -560,7 +775,6 @@ function buildProperties(): INodeProperties[] {
default: {},
displayOptions: {
show: {
- resource: ['agent'],
operation: ['execute'],
},
},
@@ -625,7 +839,6 @@ function buildProperties(): INodeProperties[] {
default: {},
displayOptions: {
show: {
- resource: ['agent'],
operation: ['execute'],
},
},
@@ -712,7 +925,6 @@ function buildProperties(): INodeProperties[] {
description: 'The search query to run',
displayOptions: {
show: {
- resource: ['search'],
operation: ['search'],
},
},
@@ -729,7 +941,6 @@ function buildProperties(): INodeProperties[] {
description: 'How many search results to return (1-25)',
displayOptions: {
show: {
- resource: ['search'],
operation: ['search'],
},
},
@@ -744,7 +955,6 @@ function buildProperties(): INodeProperties[] {
description: 'The URL to fetch',
displayOptions: {
show: {
- resource: ['fetch'],
operation: ['fetch'],
},
},
@@ -757,7 +967,6 @@ function buildProperties(): INodeProperties[] {
default: {},
displayOptions: {
show: {
- resource: ['fetch'],
operation: ['fetch'],
},
},
@@ -794,9 +1003,9 @@ export class Browserbase implements INodeType {
name: 'browserbase',
icon: 'file:../../icons/browserbase.svg',
group: ['transform'],
- version: [2, 2.1],
+ version: [2, 2.1, 3],
subtitle:
- '={{$parameter["resource"] === "agent" ? $parameter["operation"] + ": " + $parameter["mode"] : $parameter["operation"]}}',
+ '={{$parameter["operation"] === "execute" ? "Run an agent" : ($parameter["operation"] === "fetch" ? "Fetch a webpage" : "Search the web")}}',
description: 'Browser automation, web search, and page fetches with Browserbase.',
defaults: {
name: 'Browserbase',
@@ -959,17 +1168,69 @@ export class Browserbase implements INodeType {
url = normalizeUrl(url);
const instruction = executeFunctions.getNodeParameter('instruction', itemIndex) as string;
- const modelSource = executeFunctions.getNodeParameter('modelSource', itemIndex) as string;
const driverModel = executeFunctions.getNodeParameter('driverModel', itemIndex) as string;
- const mode = executeFunctions.getNodeParameter('mode', itemIndex) as string;
+ let mode: string;
+ let modelSource: string;
let agentModel: string;
- if (mode === 'cua') {
- agentModel = executeFunctions.getNodeParameter('modelCua', itemIndex) as string;
- } else if (mode === 'dom') {
- agentModel = executeFunctions.getNodeParameter('modelDom', itemIndex) as string;
+ let maxSteps: number;
+ let systemPrompt: string | undefined;
+ let highlightCursor: boolean;
+
+ if (executeFunctions.getNode().typeVersion >= 3) {
+ // v3+: model settings live in the "Model Options" collection.
+ const modelOptions = executeFunctions.getNodeParameter('modelOptions', itemIndex, {}) as {
+ mode?: string;
+ modelSource?: string;
+ modelCua?: string;
+ modelDom?: string;
+ modelHybrid?: string;
+ maxSteps?: number;
+ systemPrompt?: string;
+ highlightCursor?: boolean;
+ };
+ mode = modelOptions.mode ?? 'cua';
+ modelSource = modelOptions.modelSource ?? 'gateway';
+ // Agent Model is an optional override; when unset it falls back to the top-level Model.
+ if (mode === 'cua') {
+ agentModel = modelOptions.modelCua || driverModel;
+ } else if (mode === 'dom') {
+ agentModel = modelOptions.modelDom || driverModel;
+ } else {
+ agentModel = modelOptions.modelHybrid || driverModel;
+ }
+ maxSteps = modelOptions.maxSteps ?? 20;
+ systemPrompt = modelOptions.systemPrompt;
+ highlightCursor = modelOptions.highlightCursor ?? true;
} else {
- agentModel = executeFunctions.getNodeParameter('modelHybrid', itemIndex) as string;
+ // v2/2.1: legacy top-level model fields and "Options" collection.
+ modelSource = executeFunctions.getNodeParameter('modelSource', itemIndex, 'gateway') as string;
+ mode = executeFunctions.getNodeParameter('mode', itemIndex, 'cua') as string;
+ if (mode === 'cua') {
+ agentModel = executeFunctions.getNodeParameter('modelCua', itemIndex) as string;
+ } else if (mode === 'dom') {
+ agentModel = executeFunctions.getNodeParameter('modelDom', itemIndex) as string;
+ } else {
+ agentModel = executeFunctions.getNodeParameter('modelHybrid', itemIndex) as string;
+ }
+ const options = executeFunctions.getNodeParameter('options', itemIndex, {}) as {
+ maxSteps?: number;
+ systemPrompt?: string;
+ highlightCursor?: boolean;
+ };
+ maxSteps = options.maxSteps ?? 20;
+ systemPrompt = options.systemPrompt;
+ highlightCursor = options.highlightCursor ?? true;
+ }
+
+ // CUA mode only works with computer-use-capable models. When the Agent
+ // Model override is unset it falls back to the driver Model, which may not
+ // be a CUA model — sending it would make Stagehand silently skip CUA mode.
+ if (mode === 'cua' && !CUA_MODELS.has(agentModel)) {
+ throw new NodeOperationError(
+ executeFunctions.getNode(),
+ `CUA mode requires a computer-use-capable Agent Model, but "${agentModel}" is not one. Set "Agent Model" in Model Options to a CUA model (e.g. google/gemini-3-flash-preview), or switch Mode to DOM.`,
+ );
}
if (modelSource === 'userProvidedKey') {
@@ -978,16 +1239,11 @@ export class Browserbase implements INodeType {
if (driverProvider !== agentProvider) {
throw new NodeOperationError(
executeFunctions.getNode(),
- `When using your own model API key, both Driver and Agent models must be from the same provider. Driver is "${driverProvider}", Agent is "${agentProvider}".`,
+ `When using your own model API key, the Model and Agent Model must be from the same provider. Model is "${driverProvider}", Agent Model is "${agentProvider}".`,
);
}
}
- const options = executeFunctions.getNodeParameter('options', itemIndex, {}) as {
- maxSteps?: number;
- systemPrompt?: string;
- highlightCursor?: boolean;
- };
const browserOptions = executeFunctions.getNodeParameter(
'browserOptions',
itemIndex,
@@ -1091,17 +1347,17 @@ export class Browserbase implements INodeType {
model: agentModel,
};
- if (options.systemPrompt) {
- agentConfigBody.systemPrompt = options.systemPrompt;
+ if (systemPrompt) {
+ agentConfigBody.systemPrompt = systemPrompt;
}
const executeOptions: Record = {
instruction,
- maxSteps: options.maxSteps ?? 20,
+ maxSteps,
};
- if ((mode === 'cua' || mode === 'hybrid') && options.highlightCursor !== false) {
- executeOptions.highlightCursor = options.highlightCursor ?? true;
+ if ((mode === 'cua' || mode === 'hybrid') && highlightCursor !== false) {
+ executeOptions.highlightCursor = highlightCursor;
}
if (mode === 'dom' || mode === 'hybrid') {
@@ -1192,20 +1448,33 @@ export class Browserbase implements INodeType {
for (let i = 0; i < items.length; i++) {
try {
- const resource = this.getNodeParameter('resource', i) as string;
- const modelSource = this.getNodeParameter('modelSource', i, 'gateway') as string;
+ const operation = this.getNodeParameter('operation', i) as string;
+ let modelSource = 'gateway';
+ if (operation === 'execute') {
+ if (this.getNode().typeVersion >= 3) {
+ const itemModelOptions = this.getNodeParameter('modelOptions', i, {}) as {
+ modelSource?: string;
+ };
+ modelSource = itemModelOptions.modelSource ?? 'gateway';
+ } else {
+ modelSource = this.getNodeParameter('modelSource', i, 'gateway') as string;
+ }
+ }
const credentials = await this.getCredentials('browserbaseApi');
- if (resource === 'agent' && modelSource === 'userProvidedKey' && !credentials.modelApiKey) {
- throw new NodeOperationError(
- this.getNode(),
- 'Model Source is set to "User-provided API key" but no Model API Key is configured in the Browserbase credentials.',
- );
+ let modelApiKey: string | undefined;
+ if (operation === 'execute' && modelSource === 'userProvidedKey') {
+ const provider = (this.getNodeParameter('driverModel', i) as string).split('/')[0];
+ modelApiKey = resolveModelApiKey(credentials, provider);
+ if (!modelApiKey) {
+ throw new NodeOperationError(
+ this.getNode(),
+ `Model Source is set to "User-provided API key" but no API key for "${provider}" is configured in the Browserbase credentials.`,
+ );
+ }
}
- const headers = getHeaders(credentials, {
- includeModelApiKey: resource === 'agent' && modelSource === 'userProvidedKey',
- });
+ const headers = getHeaders(credentials, { modelApiKey });
const useCredentialBaseUrls = this.getNode().typeVersion >= 2.1;
const apiBaseUrl = useCredentialBaseUrls
@@ -1215,9 +1484,9 @@ export class Browserbase implements INodeType {
? normalizeBaseUrl((credentials.stagehandBaseUrl as string) || STAGEHAND_BASE_URL)
: STAGEHAND_BASE_URL;
- if (resource === 'search') {
+ if (operation === 'search') {
returnData.push(await node.executeSearch(this, i, headers, apiBaseUrl));
- } else if (resource === 'fetch') {
+ } else if (operation === 'fetch') {
returnData.push(await node.executeFetch(this, i, headers, apiBaseUrl));
} else {
returnData.push(await node.executeAgent(this, i, headers, stagehandBaseUrl));