await agent.execute("apply for a job at browserbase")
agent turns high level tasks into fully autonomous browser workflows. You can customize the agent by specifying the LLM provider and model, setting custom instructions for behavior, and configuring max steps.
You can use specialized computer use models from Google, OpenAI, Anthropic, or Microsoft as shown below, with mode set to "cua". To compare the performance of different computer use models, you can visit our evals page.
Deprecation Notice: The cua: true option is deprecated and will be removed in a future version. Use mode: "cua" instead.
const agent = stagehand.agent({ mode: "cua", model: "google/gemini-3-flash-preview", systemPrompt: "You are a helpful assistant...",});await agent.execute({ instruction: "Go to Hacker News and find the most controversial post from today, then read the top 3 comments and summarize the debate.", maxSteps: 20, highlightCursor: true})
const agent = stagehand.agent({ mode: "cua", model: "openai/computer-use-preview", systemPrompt: "You are a helpful assistant...",});await agent.execute({ instruction: "Go to Hacker News and find the most controversial post from today, then read the top 3 comments and summarize the debate.", maxSteps: 20, highlightCursor: true})
const agent = stagehand.agent({ mode: "cua", model: "anthropic/claude-sonnet-4-6", systemPrompt: "You are a helpful assistant...",});await agent.execute({ instruction: "Go to Hacker News and find the most controversial post from today, then read the top 3 comments and summarize the debate.", maxSteps: 20, highlightCursor: true})
Both DOM and CUA modes have their strengths and weaknesses. Hybrid mode combines them, giving the agent access to both coordinate-based and DOM-based tools to better account for where each may fall short.
Model Requirements: Hybrid mode requires models that can reliably perform coordinate-based actions from screenshots. The following models are recommended:
Anthropic: any Claude model (e.g. anthropic/claude-sonnet-4-6, anthropic/claude-haiku-4-5-20251001)
Other models may not reliably produce accurate coordinates for clicking and typing.
Hybrid mode requires experimental: true in your Stagehand constructor.
const stagehand = new Stagehand({ env: "BROWSERBASE", experimental: true, // Required for hybrid mode});await stagehand.init();const agent = stagehand.agent({ mode: "hybrid", model: "google/gemini-3-flash-preview",});const page = stagehand.context.pages()[0];await page.goto("https://example.com");await agent.execute({ instruction: "Click the sign up button and fill out the registration form", maxSteps: 20,});
const stagehand = new Stagehand({ env: "BROWSERBASE", experimental: true, // Required for hybrid mode});await stagehand.init();const agent = stagehand.agent({ mode: "hybrid", model: "anthropic/claude-haiku-4-5-20251001", systemPrompt: "You are a helpful assistant that interacts with web pages visually.",});await agent.execute({ instruction: "Navigate the page and interact with the form elements", maxSteps: 15, highlightCursor: true, // Enabled by default in hybrid mode});
Stagehand agents come with built-in tools for browser automation, but you can customize the toolset by adding your own custom tools or excluding built-in ones.
Custom tools enhance agents with additional capabilities for more granular control and better performance. Unlike MCP integrations, custom tools are defined inline and execute directly within your application.
Custom tools provide a cleaner, more performant alternative to MCP integrations when you need specific functionality.
Use the tool helper exported from @browserbasehq/stagehand to define custom tools:
import { tool } from "@browserbasehq/stagehand";import { z } from "zod";const agent = stagehand.agent({ model: "openai/gpt-5", tools: { getWeather: tool({ description: 'Get the current weather in a location', inputSchema: z.object({ location: z.string().describe('The location to get weather for'), }), execute: async ({ location }) => { // Your custom logic here const weather = await fetchWeatherAPI(location); return { location, temperature: weather.temp, conditions: weather.conditions, }; }, }), }, systemPrompt: 'You are a helpful assistant with access to weather data.',});await agent.execute("What's the weather in San Francisco and should I bring an umbrella?");
import { tool } from "@browserbasehq/stagehand";import { z } from "zod";const agent = stagehand.agent({ mode: "cua", model: "anthropic/claude-sonnet-4-6", tools: { searchDatabase: tool({ description: 'Search for records in the database', inputSchema: z.object({ query: z.string().describe('The search query'), limit: z.number().optional().describe('Max results to return'), }), execute: async ({ query, limit = 10 }) => { const results = await db.search(query, limit); return { results }; }, }), calculatePrice: tool({ description: 'Calculate the total price with tax', inputSchema: z.object({ amount: z.number().describe('The base amount'), taxRate: z.number().describe('Tax rate as decimal (e.g., 0.08 for 8%)'), }), execute: async ({ amount, taxRate }) => { const total = amount * (1 + taxRate); return { total: total.toFixed(2) }; }, }), },});await agent.execute("Find products under $50 and calculate the total with 8% tax");
import { tool } from "@browserbasehq/stagehand";import { z } from "zod";const agent = stagehand.agent({ model: "google/gemini-2.0-flash", tools: { sendEmail: tool({ description: 'Send an email via SendGrid', inputSchema: z.object({ to: z.string().email().describe('Recipient email address'), subject: z.string().describe('Email subject'), body: z.string().describe('Email body content'), }), execute: async ({ to, subject, body }) => { const response = await fetch('https://api.sendgrid.com/v3/mail/send', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.SENDGRID_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ personalizations: [{ to: [{ email: to }] }], from: { email: 'noreply@example.com' }, subject, content: [{ type: 'text/plain', value: body }], }), }); return { sent: response.ok, messageId: response.headers.get('X-Message-Id'), }; }, }), },});await agent.execute("Fill out the contact form and send me a confirmation email at user@example.com");
Use custom tools when you need specific functionality within your application. Use MCP integrations when connecting to external services or when you need standardized cross-application tools.
Prevent the agent from using specific built-in tools during execution. This is useful when you want to restrict the agent’s capabilities or avoid certain behaviors.
Non-CUA agents only. Requires experimental: true. Not available when cua: true.
// Prevent the agent from taking screenshots during executionconst result = await agent.execute({ instruction: "Fill out the contact form", excludeTools: ["screenshot"],});// Prevent the agent from extracting dataconst result = await agent.execute({ instruction: "Click through the signup flow", excludeTools: ["extract"],});// Disable web search capabilityconst result = await agent.execute({ instruction: "Find information on the current page", excludeTools: ["search"],});
Enable the search tool by setting useSearch: true in agent.execute(). This gives the agent the ability to perform web searches using the Browserbase Search API, which is useful when the agent needs to find URLs or gather information before navigating.
Requires a valid Browserbase API key. Set BROWSERBASE_API_KEY in your environment, or pass apiKey in the Stagehand constructor.
const result = await agent.execute({ instruction: "Find the latest pricing for Browserbase", useSearch: true, maxSteps: 20,});
Use variables to pass sensitive data (like passwords, API keys, or personal information) to the agent without exposing the actual values to the LLM. The agent sees only variable names and descriptions, while the actual values are substituted at runtime.
Non-CUA agents only. Variables are not available with Computer Use Agents.
Agents can be enhanced with external tools and services through MCP (Model Context Protocol) integrations. This allows your agent to access external APIs and data sources beyond just browser interactions.
const agent = stagehand.agent({ mode: "cua", model: "openai/computer-use-preview", integrations: [ `https://mcp.exa.ai/mcp?exaApiKey=${process.env.EXA_API_KEY}`, ], systemPrompt: `You have access to web search through Exa. Use it to find current information before browsing.`});await agent.execute("Search for the best headphones of 2025 and go through checkout for the top recommendation");
import { connectToMCPServer } from "@browserbasehq/stagehand";const supabaseClient = await connectToMCPServer( `https://server.smithery.ai/@supabase-community/supabase-mcp/mcp?api_key=${process.env.SMITHERY_API_KEY}`);const agent = stagehand.agent({ mode: "cua", model: "openai/computer-use-preview", integrations: [supabaseClient], systemPrompt: `You can interact with Supabase databases. Use these tools to store and retrieve data.`});await agent.execute("Search for restaurants and save the first result to the database");
MCP integrations enable agents to be more powerful by combining browser automation with external APIs, databases, and services. The agent can intelligently decide when to use browser actions versus external tools.
Enable streaming mode to receive incremental responses from the agent. This is useful for building real-time UIs that show the agent’s reasoning as it progresses.
Non-CUA agents only. Streaming, callbacks, abort signals, and message continuation are only available when using the standard agent (without mode: "cua"). These features are not supported with Computer Use Agents.
These are experimental features. Set experimental: true in your Stagehand constructor to enable them.
Streaming-only callbacks (onChunk, onFinish, onError, onAbort) will throw an error if used without stream: true. If you need these callbacks, enable streaming in your agent configuration.
Continue a conversation across multiple agent executions by passing the messages from a previous result. This is useful for multi-turn interactions or breaking complex tasks into steps while maintaining context.
Non-CUA agents only. Message continuation requires experimental: true and is not available with Computer Use Agents.
const stagehand = new Stagehand({ env: "LOCAL", experimental: true, // Required for message continuation});await stagehand.init();const agent = stagehand.agent({ model: "anthropic/claude-sonnet-4-5-20250929",});const page = stagehand.context.pages()[0];await page.goto("https://example.com/products");// First execution: search for productsconst firstResult = await agent.execute({ instruction: "Search for wireless headphones and note the top 3 results", maxSteps: 10,});console.log("First task:", firstResult.message);// Continue with the same context: ask follow-upconst secondResult = await agent.execute({ instruction: "Now filter by price under $100 and tell me which of those 3 are still available", maxSteps: 10, messages: firstResult.messages, // Pass previous conversation});console.log("Follow-up:", secondResult.message);// Continue further: take action based on conversation historyconst thirdResult = await agent.execute({ instruction: "Add the cheapest one to the cart", maxSteps: 10, messages: secondResult.messages, // Chain the conversation});console.log("Final action:", thirdResult.message);
Define a Zod schema to receive structured data when the agent completes its task. This is useful when you need specific information extracted from the agent’s execution, such as prices, dates, or other structured data.
Non-CUA agents only. Structured output requires experimental: true and is not available with Computer Use Agents.
Use .describe() on schema fields to help the agent understand what data to extract.
import { z } from "zod";const stagehand = new Stagehand({ env: "LOCAL", experimental: true, // Required for structured output});await stagehand.init();const agent = stagehand.agent({ model: "anthropic/claude-sonnet-4-5-20250929",});const page = stagehand.context.pages()[0];await page.goto("https://www.google.com/flights");const result = await agent.execute({ instruction: "Find the cheapest flight from NYC to LA for next week", maxSteps: 20, output: z.object({ price: z.string().describe("The price of the flight"), airline: z.string().describe("The airline name"), departureTime: z.string().describe("Departure time"), arrivalTime: z.string().describe("Arrival time"), }),});// Access the structured outputconsole.log(result.output);// { price: "$199", airline: "Delta", departureTime: "8:00 AM", arrivalTime: "11:30 AM" }
const result = await agent.execute({ instruction: "Extract all items from the shopping cart", output: z.object({ items: z.array(z.object({ name: z.string().describe("Product name"), quantity: z.number().describe("Quantity in cart"), unitPrice: z.string().describe("Price per item"), totalPrice: z.string().describe("Total price for this item"), })).describe("List of items in the cart"), subtotal: z.string().describe("Cart subtotal before tax"), tax: z.string().optional().describe("Tax amount if shown"), total: z.string().describe("Final total"), }),});console.log(`Found ${result.output?.items.length} items in cart`);console.log(`Total: ${result.output?.total}`);
const agent = stagehand.agent({ model: "anthropic/claude-sonnet-4-5-20250929", stream: true,});const streamResult = await agent.execute({ instruction: "Find the top 3 search results", output: z.object({ results: z.array(z.object({ title: z.string().describe("The title of the search result"), url: z.string().url().describe("The URL of the search result"), snippet: z.string().describe("A brief description or snippet"), })).max(3).describe("Top 3 search results"), }),});// Stream the text outputfor await (const delta of streamResult.textStream) { process.stdout.write(delta);}// Get the structured output from the final resultconst finalResult = await streamResult.result;console.log(finalResult.output?.results);
Stagehand uses a 1288x711 viewport by default. Other viewport sizes may reduce performance. If you need to modify the viewport, you can edit in the Browser Configuration.
Control the maximum number of steps the agent can take to complete the task using the maxSteps parameter.
// Set maxSteps to control how many actions the agent can takeawait agent.execute({ instruction: "Sign me up for a library card", maxSteps: 15 // Agent will stop after 15 steps if task isn't complete});
For complex tasks, increase the maxSteps limit and check task success.
// Complex multi-step task requiring more actionsconst result = await agent.execute({ instruction: "Find and apply for software engineering jobs, filtering by remote work and saving 3 applications", maxSteps: 30, // Higher limit for complex workflows});// Check if the task completed successfullyif (result.success === true) { console.log("Task completed successfully!");} else { console.log("Task failed or was incomplete");}
Problem: Agent stops before finishing the requested taskSolutions:
Check if the agent is hitting the maxSteps limit (default is 20)
Increase maxSteps for complex tasks: maxSteps: 30 or higher
Break very complex tasks into smaller sequential executions
// Increase maxSteps for complex tasksawait agent.execute({ instruction: "Complete the multi-page registration form with all required information", maxSteps: 40 // Increased limit for complex task});// Or break into smaller tasks with success checkingconst firstResult = await agent.execute({ instruction: "Fill out page 1 of the registration form", maxSteps: 15});// Only proceed if the first task was successfulif (firstResult.success === true) { await agent.execute({ instruction: "Navigate to page 2 and complete remaining fields", maxSteps: 15 });} else { console.log("First task failed, stopping execution");}
Agent is failing to click the proper elements
Problem: Agent clicks on wrong elements or fails to interact with the correct UI componentsSolutions:
Ensure proper viewport size: Stagehand uses 1288x711 by default (optimal for Computer Use models)
Avoid changing viewport dimensions as other sizes may reduce performance