From 57ce959cce8b01068f3d30968253abf9ddb070f9 Mon Sep 17 00:00:00 2001 From: Alexander Gekov Date: Tue, 23 Jun 2026 15:32:28 +0300 Subject: [PATCH 1/5] feat: replace Mode/Model info callouts with field tooltips Remove the placeholder 'Mode Info' and 'Model Info' notice callouts on the Agent resource and fold their guidance into the relevant field tooltips. Add docs links for picking a mode and for Stagehand model evals so users can choose modes and compare model performance inline. Co-authored-by: Cursor --- nodes/Browserbase/Browserbase.node.ts | 61 +++++---------------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/nodes/Browserbase/Browserbase.node.ts b/nodes/Browserbase/Browserbase.node.ts index a21926f..71381b9 100644 --- a/nodes/Browserbase/Browserbase.node.ts +++ b/nodes/Browserbase/Browserbase.node.ts @@ -167,20 +167,6 @@ function buildProperties(): INodeProperties[] { ], default: 'search', }, - { - displayName: 'Mode Info', - name: 'modeNotice', - type: 'notice', - default: '', - displayOptions: { - show: { - resource: ['agent'], - operation: ['execute'], - }, - }, - description: - 'CUA uses vision/coordinates (best for complex UIs). DOM uses selectors (faster, any LLM). Hybrid combines both.', - }, { displayName: 'Starting URL', name: 'url', @@ -240,37 +226,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', @@ -313,7 +269,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', @@ -343,7 +300,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', @@ -391,7 +349,8 @@ function buildProperties(): INodeProperties[] { }, ], 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', @@ -431,7 +390,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', @@ -459,7 +419,8 @@ function buildProperties(): INodeProperties[] { }, ], 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', From 901e0064824f12761472233c37dd275c95d52bb6 Mon Sep 17 00:00:00 2001 From: Alexander Gekov Date: Tue, 23 Jun 2026 18:56:39 +0300 Subject: [PATCH 2/5] feat: support per-provider BYOK model keys in credentials Replace the single Model API Key with provider-specific key fields (Anthropic, OpenAI, Google) so users can store their own key for the provider they use. The node resolves the key matching the selected model's provider, falling back to the legacy modelApiKey field (kept as deprecated) so existing credentials keep working across re-saves. Co-authored-by: Cursor --- README.md | 2 +- credentials/BrowserbaseApi.credentials.ts | 35 +++++++++++++++++++-- nodes/Browserbase/Browserbase.node.ts | 38 ++++++++++++++++------- 3 files changed, 61 insertions(+), 14 deletions(-) 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 71381b9..0ed4f99 100644 --- a/nodes/Browserbase/Browserbase.node.ts +++ b/nodes/Browserbase/Browserbase.node.ts @@ -63,7 +63,7 @@ function getSessionId(response: Record): string | undefined { function getHeaders( credentials: ICredentialDataDecryptedObject, options?: { - includeModelApiKey?: boolean; + modelApiKey?: string; }, ): BrowserbaseHeaders { const headers: BrowserbaseHeaders = { @@ -72,8 +72,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,6 +84,19 @@ 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 [ { @@ -1157,16 +1170,19 @@ export class Browserbase implements INodeType { const 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 (resource === 'agent' && 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 From 7daac36dfdf34ecf96068583bb6b10f20de07bf6 Mon Sep 17 00:00:00 2001 From: Alexander Gekov Date: Wed, 24 Jun 2026 16:02:14 +0300 Subject: [PATCH 3/5] feat: add v3 node layout with grouped Model Options Introduce typeVersion 3 with a streamlined UI gated by @version ranges: - Collapse Resource + Operation into a single "Action" dropdown - Rename agent action to "Run an Agent" (value unchanged) - Move the top-level "Model" (driver) field above URL/Instruction - Group Mode, Agent Model, Model Source and advanced settings into a single "Model Options" collection Use _cnd range gates (gte/lt 3) so the UI matches the >= 3 checks in execute(), keeping v2/2.1 workflows intact and future minor bumps safe. Co-authored-by: Cursor --- nodes/Browserbase/Browserbase.node.ts | 410 +++++++++++++++++++++++--- 1 file changed, 365 insertions(+), 45 deletions(-) diff --git a/nodes/Browserbase/Browserbase.node.ts b/nodes/Browserbase/Browserbase.node.ts index 0ed4f99..72524dd 100644 --- a/nodes/Browserbase/Browserbase.node.ts +++ b/nodes/Browserbase/Browserbase.node.ts @@ -99,11 +99,48 @@ function resolveModelApiKey( 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', @@ -127,15 +164,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', @@ -147,6 +185,7 @@ function buildProperties(): INodeProperties[] { noDataExpression: true, displayOptions: { show: { + '@version': [{ _cnd: { lt: 3 } }], resource: ['fetch'], }, }, @@ -167,6 +206,7 @@ function buildProperties(): INodeProperties[] { noDataExpression: true, displayOptions: { show: { + '@version': [{ _cnd: { lt: 3 } }], resource: ['search'], }, }, @@ -180,6 +220,50 @@ function buildProperties(): INodeProperties[] { ], default: 'search', }, + { + displayName: 'Model', + name: 'driverModel', + type: 'options', + displayOptions: { + show: { + '@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', + }, + { + name: 'Gemini 3 Pro (Google)', + value: 'google/gemini-3-pro', + }, + { + name: 'GPT-4o (OpenAI)', + value: 'openai/gpt-4o', + }, + { + name: 'GPT-4o Mini (OpenAI)', + value: 'openai/gpt-4o-mini', + }, + ], + default: 'google/gemini-3-flash', + description: + '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', name: 'url', @@ -190,7 +274,6 @@ function buildProperties(): INodeProperties[] { description: 'The starting page URL for the agent', displayOptions: { show: { - resource: ['agent'], operation: ['execute'], }, }, @@ -208,11 +291,209 @@ 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: [ + { + 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', + }, + ], + 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: [ + { + 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', + }, + ], + 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', @@ -220,7 +501,7 @@ function buildProperties(): INodeProperties[] { noDataExpression: true, displayOptions: { show: { - resource: ['agent'], + '@version': [{ _cnd: { lt: 3 } }], operation: ['execute'], }, }, @@ -247,7 +528,7 @@ function buildProperties(): INodeProperties[] { type: 'options', displayOptions: { show: { - resource: ['agent'], + '@version': [{ _cnd: { lt: 3 } }], operation: ['execute'], }, }, @@ -291,7 +572,7 @@ function buildProperties(): INodeProperties[] { type: 'options', displayOptions: { show: { - resource: ['agent'], + '@version': [{ _cnd: { lt: 3 } }], operation: ['execute'], }, }, @@ -322,7 +603,7 @@ function buildProperties(): INodeProperties[] { type: 'options', displayOptions: { show: { - resource: ['agent'], + '@version': [{ _cnd: { lt: 3 } }], operation: ['execute'], mode: ['cua'], }, @@ -371,7 +652,7 @@ function buildProperties(): INodeProperties[] { type: 'options', displayOptions: { show: { - resource: ['agent'], + '@version': [{ _cnd: { lt: 3 } }], operation: ['execute'], mode: ['dom'], }, @@ -412,7 +693,7 @@ function buildProperties(): INodeProperties[] { type: 'options', displayOptions: { show: { - resource: ['agent'], + '@version': [{ _cnd: { lt: 3 } }], operation: ['execute'], mode: ['hybrid'], }, @@ -443,7 +724,7 @@ function buildProperties(): INodeProperties[] { default: {}, displayOptions: { show: { - resource: ['agent'], + '@version': [{ _cnd: { lt: 3 } }], operation: ['execute'], }, }, @@ -486,9 +767,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: [ @@ -534,7 +813,6 @@ function buildProperties(): INodeProperties[] { default: {}, displayOptions: { show: { - resource: ['agent'], operation: ['execute'], }, }, @@ -599,7 +877,6 @@ function buildProperties(): INodeProperties[] { default: {}, displayOptions: { show: { - resource: ['agent'], operation: ['execute'], }, }, @@ -686,7 +963,6 @@ function buildProperties(): INodeProperties[] { description: 'The search query to run', displayOptions: { show: { - resource: ['search'], operation: ['search'], }, }, @@ -703,7 +979,6 @@ function buildProperties(): INodeProperties[] { description: 'How many search results to return (1-25)', displayOptions: { show: { - resource: ['search'], operation: ['search'], }, }, @@ -718,7 +993,6 @@ function buildProperties(): INodeProperties[] { description: 'The URL to fetch', displayOptions: { show: { - resource: ['fetch'], operation: ['fetch'], }, }, @@ -731,7 +1005,6 @@ function buildProperties(): INodeProperties[] { default: {}, displayOptions: { show: { - resource: ['fetch'], operation: ['fetch'], }, }, @@ -768,9 +1041,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', @@ -933,17 +1206,59 @@ 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; } if (modelSource === 'userProvidedKey') { @@ -952,16 +1267,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, @@ -1065,17 +1375,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') { @@ -1166,12 +1476,22 @@ 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'); let modelApiKey: string | undefined; - if (resource === 'agent' && modelSource === 'userProvidedKey') { + if (operation === 'execute' && modelSource === 'userProvidedKey') { const provider = (this.getNodeParameter('driverModel', i) as string).split('/')[0]; modelApiKey = resolveModelApiKey(credentials, provider); if (!modelApiKey) { @@ -1192,9 +1512,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)); From 21f26ac4982bc6ff4b0f34ea8c5370e86ee2680a Mon Sep 17 00:00:00 2001 From: Alexander Gekov Date: Fri, 26 Jun 2026 14:33:34 +0300 Subject: [PATCH 4/5] fix: use canonical Gemini 3 -preview IDs for driver model The driver Model dropdowns listed google/gemini-3-flash and google/gemini-3-pro, but Stagehand's canonical IDs are google/gemini-3-flash-preview and google/gemini-3-pro-preview. Since the Agent Model falls back to the driver Model when unset, the v3 default (gemini-3-flash + CUA) sent an ID absent from AVAILABLE_CUA_MODELS, so resolveModel() returned isCua: false and the agent silently skipped CUA mode. Reconcile both dropdowns and the v3 default to the canonical IDs. Co-authored-by: Cursor --- nodes/Browserbase/Browserbase.node.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nodes/Browserbase/Browserbase.node.ts b/nodes/Browserbase/Browserbase.node.ts index 72524dd..f6c1538 100644 --- a/nodes/Browserbase/Browserbase.node.ts +++ b/nodes/Browserbase/Browserbase.node.ts @@ -245,11 +245,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)', @@ -260,7 +260,7 @@ function buildProperties(): INodeProperties[] { value: 'openai/gpt-4o-mini', }, ], - default: 'google/gemini-3-flash', + default: 'google/gemini-3-flash-preview', description: '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.', }, @@ -547,11 +547,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)', From 2d110d89079a0787354e6a554e53b0f1730c5055 Mon Sep 17 00:00:00 2001 From: Alexander Gekov Date: Fri, 26 Jun 2026 16:56:17 +0300 Subject: [PATCH 5/5] fix: sync CUA model list and validate agent-model fallback Mirror Stagehand's AVAILABLE_CUA_MODELS as a single CUA_MODEL_OPTIONS source that drives both Agent Model (CUA) dropdowns and a CUA_MODELS guard, so an unset Agent Model falling back to a non-CUA driver model no longer makes Stagehand silently skip CUA mode. Also dedupe the Hybrid dropdowns into a shared HYBRID_MODEL_OPTIONS list (adds Opus 4.8 and OpenAI gpt-5.4-mini/gpt-5.5) and standardize the Haiku ID. Co-authored-by: Cursor --- nodes/Browserbase/Browserbase.node.ts | 164 +++++++++++--------------- 1 file changed, 68 insertions(+), 96 deletions(-) diff --git a/nodes/Browserbase/Browserbase.node.ts b/nodes/Browserbase/Browserbase.node.ts index f6c1538..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 = { @@ -317,40 +371,7 @@ function buildProperties(): INodeProperties[] { 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: '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.', @@ -403,20 +424,7 @@ function buildProperties(): INodeProperties[] { 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: '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.', @@ -608,40 +616,7 @@ function buildProperties(): INodeProperties[] { 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. See Stagehand model evals to compare model performance.', @@ -698,20 +673,7 @@ function buildProperties(): INodeProperties[] { 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). See Stagehand model evals to compare model performance.', @@ -1261,6 +1223,16 @@ export class Browserbase implements INodeType { 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') { const driverProvider = driverModel.split('/')[0]; const agentProvider = agentModel.split('/')[0];