Skip to content

[STG-2850] Add Browser Use to v4 migration guide - #2756

Open
bbclanker wants to merge 3 commits into
mainfrom
stg-2850/browser-use-v4-migration-guide
Open

[STG-2850] Add Browser Use to v4 migration guide#2756
bbclanker wants to merge 3 commits into
mainfrom
stg-2850/browser-use-v4-migration-guide

Conversation

@bbclanker

Copy link
Copy Markdown
Contributor

Summary

Adds a "Migrate Browser Use to v4" guide under packages/docs/v4/migrations/, modeled on the existing v3 and Playwright migration guides in that directory.

The guide frames the core migration story: Browser Use is an autonomous-agent framework (Agent(task=..., llm=...).run()), and Stagehand v4 has no agent object — so, like the v3 guide's "why agent() is gone" narrative, it steers users to code mode (an assistant writes a script once) and tool calling (a model drives the full Stagehand API as tools). It leads with Python (Browser Use is Python-first) and includes TypeScript tabs.

Covered:

  • Side-by-side hello world, code mode, and tool calling
  • Breaking-change diffs: initialization/teardown, the task string, models (ChatOpenAI/ChatBrowserUsemodel), structured output (output_model_schemaextract()), sensitive data (sensitive_datavariables), custom tools (@tools.action), and Browser(...) / @sandbox config
  • A quick-reference mapping table and troubleshooting section
  • Registered the page in docs.json under the v4 Migration guide group

Browser Use API details were grounded against the browser-use/browser-use repo's own reference docs; Stagehand v4 API against the existing v4 reference pages. All cross-links and deep anchors were verified to resolve, and docs.json validates as JSON.

Test plan

  • jq confirms docs.json is valid JSON
  • Verified all 13 cross-linked v4 pages exist and the models#model-gateway / act#secure-your-automations anchors resolve
  • Confirmed all Python/TypeScript API idioms used (browserbase.launch, local_browser.launch, new_page, wait_for_load_state, ModelConfig, data.extraction, etc.) match usage elsewhere in the v4 docs
  • Recommend running just check (Mint config + link validation) in CI

Requested by: sophie@browserbase.com
Linear: https://linear.app/browserbase/issue/STG-2850/add-browser-use-to-v4-migration-guide
Slack thread: https://browserbase.slack.com/archives/D0AH75TNSF9/p1786985811934049

@mintlify

mintlify Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
stagehand 🟢 Ready View Preview Aug 17, 2026, 5:02 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cubic analysis

4 issues found across 2 files

Confidence score: 3/5

  • packages/docs/v4/migrations/browser-use.mdx shows the Python Models example passing ModelConfig(...) to Stagehand.create(), although Python expects a model string and separate model_api_key; following the migration can prevent users from creating Stagehand. Update the example to the supported Python signature.
  • packages/docs/v4/migrations/browser-use.mdx combines Python and TypeScript call shapes in the connection mapping, showing Python readers an invalid local_browser.connect({ cdpUrl }) example; split the language-specific mappings and use cdp_url for Python.
  • packages/docs/v4/migrations/browser-use.mdx uses TypeScript option spelling in the Python quick-reference row, making the documented migration target invalid; change the row to Python keyword syntax with cdp_url.
  • packages/docs/v4/migrations/browser-use.mdx documents the non-existent hybrid call local_browser.launch({ headless: false }), which may leave Python users with an unusable launch example; keep the call consistently in Python syntax or switch it fully to TypeScript.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/docs/v4/migrations/browser-use.mdx">

<violation number="1" location="packages/docs/v4/migrations/browser-use.mdx:385">
P2: This migration sentence combines Python and TypeScript APIs in one call shape, so Python readers get an invalid `local_browser.connect({ cdpUrl })` example. Split the mapping by language and use `cdp_url` for Python.

(Based on your team's feedback about SDK-native field spellings.) .</violation>

<violation number="2" location="packages/docs/v4/migrations/browser-use.mdx:402">
P1: The Python example in the Models section passes a `ModelConfig(...)` object to `Stagehand.create()`, but the Python SDK's `create()` takes a `model: str` plus a separate `model_api_key` argument; `ModelConfig` is the Go/TypeScript form. As written this example won't run (a `ModelConfig` is neither a model-name string nor a callable LLM). Use `model="anthropic/claude-sonnet-4-6"` with `model_api_key=os.environ["ANTHROPIC_API_KEY"]` to match the actual Python API documented in configuration/models.mdx.</violation>

<violation number="3" location="packages/docs/v4/migrations/browser-use.mdx:510">
P2: This row shows a non-existent hybrid API: `local_browser.launch({ headless: false })`. Keep Python names with Python argument syntax, or switch entirely to TypeScript naming.

(Based on your team's feedback about SDK-native field spellings.) .</violation>

<violation number="4" location="packages/docs/v4/migrations/browser-use.mdx:543">
P2: This quick-reference mapping uses TypeScript option spelling inside the Python SDK call, so the documented migration target is invalid for Python. Use `cdp_url` keyword syntax in this row.</violation>
</file>

Linked issue analysis

Linked issue: STG-2850: Add Browser Use to v4 migration guide

Status Acceptance criteria Notes
Create a Browser Use → Stagehand v4 migration guide The new browser-use.mdx guide provides migration guidance, examples, breaking-change mappings, troubleshooting, and references for both Python and TypeScript.
Make the migration guide discoverable in the v4 documentation The new guide is added to the v4 Migration guide group in the documentation navigation.
Architecture diagram
sequenceDiagram
    participant Dev as Developer/Migration Reader
    participant Docs as v4 Docs (Mintlify)
    participant DocsJSON as docs.json Config
    participant BrowserUse as Browser Use Agent
    participant Stagehand as Stagehand v4 SDK
    participant Browser as Browser Context
    participant LLM as LLM (e.g., GPT-4)
    
    Note over Dev,Docs: NEW: v4 Migration Guide for Browser Use users
    
    Dev->>Docs: Navigates to v4/migrations/browser-use
    Docs->>DocsJSON: CHANGED: Resolves page in nav config
    DocsJSON-->>Docs: Returns page reference
    Docs-->>Dev: Renders migration guide

    rect rgb(240, 240, 240)
    Note over Dev,LLM: Scenario A: Original Browser Use Pattern
    Dev->>BrowserUse: Agent(task="...", llm=...)
    BrowserUse->>Browser: Auto-manages browser session
    BrowserUse->>LLM: Per-step inference calls
    LLM-->>BrowserUse: Action decisions
    BrowserUse-->>Dev: Task result
    end

    rect rgb(240, 240, 240)
    Note over Dev,LLM: Scenario B: Stagehand v4 Pattern (Code Mode)
    Dev->>Stagehand: browserbase.launch(api_key)
    Stagehand->>Browser: Creates browser instance
    Dev->>Stagehand: Stagehand.create(browser)
    Stagehand->>Browser: Attaches runtime
    Dev->>Browser: page.locator() / page.goto()
    Browser-->>Dev: Deterministic results
    opt Model needed
        Dev->>Stagehand: stagehand.act("...")
        Stagehand->>LLM: Single inference call
        LLM-->>Stagehand: Action
        Stagehand-->>Dev: Result
    end
    end

    rect rgb(240, 240, 240)
    Note over Dev,LLM: Scenario C: Tool Calling Pattern
    Dev->>Stagehand: Exposes API as tools
    Stagehand->>Browser: page.snapshot() returns xpathMap
    Browser-->>Stagehand: Accessibility tree + selectors
    Stagehand->>LLM: Model reads page structure
    LLM-->>Stagehand: Selects tool + selector
    Stagehand->>Browser: Executes locator action
    Browser-->>Stagehand: Result
    Stagehand-->>Dev: Final response
    end

    Note over Dev,Docs: Includes: code mode, tool calling, migration diffs,<br/>env vars, custom tools, troubleshooting
Loading

Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.

Re-trigger cubic

Comment on lines +402 to +406
+ model=ModelConfig(
+ model_name="anthropic/claude-sonnet-4-6",
+ api_key=os.environ["ANTHROPIC_API_KEY"],
+ ),
+ )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The Python example in the Models section passes a ModelConfig(...) object to Stagehand.create(), but the Python SDK's create() takes a model: str plus a separate model_api_key argument; ModelConfig is the Go/TypeScript form. As written this example won't run (a ModelConfig is neither a model-name string nor a callable LLM). Use model="anthropic/claude-sonnet-4-6" with model_api_key=os.environ["ANTHROPIC_API_KEY"] to match the actual Python API documented in configuration/models.mdx.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/docs/v4/migrations/browser-use.mdx, line 402:

<comment>The Python example in the Models section passes a `ModelConfig(...)` object to `Stagehand.create()`, but the Python SDK's `create()` takes a `model: str` plus a separate `model_api_key` argument; `ModelConfig` is the Go/TypeScript form. As written this example won't run (a `ModelConfig` is neither a model-name string nor a callable LLM). Use `model="anthropic/claude-sonnet-4-6"` with `model_api_key=os.environ["ANTHROPIC_API_KEY"]` to match the actual Python API documented in configuration/models.mdx.</comment>

<file context>
@@ -0,0 +1,578 @@
+- agent = Agent(task="...", llm=ChatAnthropic(model="claude-sonnet-4-0"))
++ stagehand = await Stagehand.create(
++     browser=browser,
++     model=ModelConfig(
++         model_name="anthropic/claude-sonnet-4-6",
++         api_key=os.environ["ANTHROPIC_API_KEY"],
</file context>
Suggested change
+ model=ModelConfig(
+ model_name="anthropic/claude-sonnet-4-6",
+ api_key=os.environ["ANTHROPIC_API_KEY"],
+ ),
+ )
stagehand = await Stagehand.create(
browser=browser,
model="anthropic/claude-sonnet-4-6",
model_api_key=os.environ["ANTHROPIC_API_KEY"],
)

Comment thread packages/docs/v4/migrations/browser-use.mdx
| `use_vision=True` | `screenshot: true` on an `extract()` call |
| `generate_gif=True` | Browserbase [session recording](/v4/configuration/observability) |
| `Browser(...)` / `BrowserSession(...)` | `local_browser.launch()` or `browserbase.launch()` |
| `Browser(cdp_url=...)` | `local_browser.connect({ cdpUrl })` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This quick-reference mapping uses TypeScript option spelling inside the Python SDK call, so the documented migration target is invalid for Python. Use cdp_url keyword syntax in this row.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/docs/v4/migrations/browser-use.mdx, line 543:

<comment>This quick-reference mapping uses TypeScript option spelling inside the Python SDK call, so the documented migration target is invalid for Python. Use `cdp_url` keyword syntax in this row.</comment>

<file context>
@@ -0,0 +1,578 @@
+| `use_vision=True` | `screenshot: true` on an `extract()` call |
+| `generate_gif=True` | Browserbase [session recording](/v4/configuration/observability) |
+| `Browser(...)` / `BrowserSession(...)` | `local_browser.launch()` or `browserbase.launch()` |
+| `Browser(cdp_url=...)` | `local_browser.connect({ cdpUrl })` |
+| `Browser(allowed_domains=...)` | `browser.context.setDomainPolicy({ allowedDomains })` |
+| `Browser(storage_state=...)` | Cookie API, or a Browserbase context |
</file context>
Suggested change
| `Browser(cdp_url=...)` | `local_browser.connect({ cdpUrl })` |
| `Browser(cdp_url=...)` | `local_browser.connect(cdp_url="...")` |


| Browser Use | Stagehand v4 |
| --- | --- |
| `Browser(headless=False)` | `local_browser.launch({ headless: false })` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This row shows a non-existent hybrid API: local_browser.launch({ headless: false }). Keep Python names with Python argument syntax, or switch entirely to TypeScript naming.

(Based on your team's feedback about SDK-native field spellings.) .

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/docs/v4/migrations/browser-use.mdx, line 510:

<comment>This row shows a non-existent hybrid API: `local_browser.launch({ headless: false })`. Keep Python names with Python argument syntax, or switch entirely to TypeScript naming.

(Based on your team's feedback about SDK-native field spellings.) .</comment>

<file context>
@@ -0,0 +1,578 @@
+
+| Browser Use | Stagehand v4 |
+| --- | --- |
+| `Browser(headless=False)` | `local_browser.launch({ headless: false })` |
+| `Browser(cdp_url="http://localhost:9222")` | `local_browser.connect({ cdpUrl: "http://localhost:9222" })` |
+| `Browser(proxy=ProxySettings(...))` | `proxy` on `local_browser.launch()`, or Browserbase proxies |
</file context>
Suggested change
| `Browser(headless=False)` | `local_browser.launch({ headless: false })` |
| `Browser(headless=False)` | `local_browser.launch(headless=False)` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make all of the table into python syntax because that's what the migration guide mentions at the top (and what BU is)

</Tab>
</Tabs>

Use `local_browser.launch()` for a browser on your machine, `browserbase.launch({ apiKey })` for a hosted one, and `local_browser.connect({ cdpUrl })` or `browserbase.connect({ apiKey, sessionId })` to attach to one that's already running. `browserbase.launch()` is what enables [server-side caching](/v4/best-practices/caching) and the [Model Gateway](/v4/configuration/models#model-gateway). Stagehand closes only the browsers it launched, so `stagehand.close()` leaves the browser running and you call `browser.close()` yourself. See [browser configuration](/v4/configuration/browser).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This migration sentence combines Python and TypeScript APIs in one call shape, so Python readers get an invalid local_browser.connect({ cdpUrl }) example. Split the mapping by language and use cdp_url for Python.

(Based on your team's feedback about SDK-native field spellings.) .

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/docs/v4/migrations/browser-use.mdx, line 385:

<comment>This migration sentence combines Python and TypeScript APIs in one call shape, so Python readers get an invalid `local_browser.connect({ cdpUrl })` example. Split the mapping by language and use `cdp_url` for Python.

(Based on your team's feedback about SDK-native field spellings.) .</comment>

<file context>
@@ -0,0 +1,578 @@
+</Tab>
+</Tabs>
+
+Use `local_browser.launch()` for a browser on your machine, `browserbase.launch({ apiKey })` for a hosted one, and `local_browser.connect({ cdpUrl })` or `browserbase.connect({ apiKey, sessionId })` to attach to one that's already running. `browserbase.launch()` is what enables [server-side caching](/v4/best-practices/caching) and the [Model Gateway](/v4/configuration/models#model-gateway). Stagehand closes only the browsers it launched, so `stagehand.close()` leaves the browser running and you call `browser.close()` yourself. See [browser configuration](/v4/configuration/browser).
+
+### The task string becomes explicit steps
</file context>

Comment thread packages/docs/v4/migrations/browser-use.mdx Outdated
Comment thread packages/docs/v4/migrations/browser-use.mdx Outdated
- **[Code mode](#code-mode)** puts the model in front of the run, not inside it. A coding assistant writes a Stagehand script once; you run that script every time after. Browserbase recommends starting here.
- **[Tool calling](#tool-calling)** keeps a model in the loop at runtime, the way Browser Use does, but drives the browser through the full Stagehand API as its tools instead of one broad task string.

Either way, `act()`, `extract()`, and `observe()` stay in your toolbox for the steps that genuinely need a model. You just stop handing a model the entire task.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Either way, `act()`, `extract()`, and `observe()` stay in your toolbox for the steps that genuinely need a model. You just stop handing a model the entire task.
`act()`, `extract()`, and `observe()` stay available for the steps where a natural-language instruction beats a selector.


- A browser factory (`browserbase.launch()` or `local_browser.launch()`) replaces the implicit browser inside `Agent`, and `Stagehand.create()` attaches the runtime to it.
- The task string is gone. You write the steps.
- Where Browser Use would have spent a model call reading the page, a `page.locator()` does it for free. Spend `act()` and `extract()` only where the page needs judgement.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Where Browser Use would have spent a model call reading the page, a `page.locator()` does it for free. Spend `act()` and `extract()` only where the page needs judgement.
- Where Browser Use would have spent a model call reading the page, a `page.locator()` reads it directly. Spend `act()` and `extract()` only where the page needs judgement.

- Where Browser Use would have spent a model call reading the page, a `page.locator()` does it for free. Spend `act()` and `extract()` only where the page needs judgement.

<Warning>
Stagehand reads no environment variables of its own. Browser Use auto-loads `.env` and picks up `OPENAI_API_KEY`, `BROWSER_USE_API_KEY`, and friends. In v4 you pass every key explicitly: the Browserbase API key to the factory, and any model key in the `model` option. `load_dotenv()` still works to get values into `os.environ`; nothing reads them for you.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Stagehand reads no environment variables of its own. Browser Use auto-loads `.env` and picks up `OPENAI_API_KEY`, `BROWSER_USE_API_KEY`, and friends. In v4 you pass every key explicitly: the Browserbase API key to the factory, and any model key in the `model` option. `load_dotenv()` still works to get values into `os.environ`; nothing reads them for you.
Stagehand reads no environment variables of its own. Browser Use auto-loads `.env` and picks up `OPENAI_API_KEY` and `BROWSER_USE_API_KEY`. In v4 you pass every key explicitly: the Browserbase API key to the factory, and any model key in the `model` option. `load_dotenv()` still populates `os.environ`, but you pass in the values yourself.


**`ImportError: cannot import name 'Agent'`.** There is no `Agent` in v4. Replace `Agent(task=...).run()` with [code mode](#code-mode) or [tool calling](#tool-calling).

**Nothing reads my API key.** Stagehand reads no environment variables. Pass the Browserbase key to `browserbase.launch()` and any model key in the `model` option. `load_dotenv()` only populates `os.environ`; you still pass the values in.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Nothing reads my API key.** Stagehand reads no environment variables. Pass the Browserbase key to `browserbase.launch()` and any model key in the `model` option. `load_dotenv()` only populates `os.environ`; you still pass the values in.
**`KeyError: 'BROWSERBASE_API_KEY'`.** Stagehand reads no environment variables. Pass the Browserbase key to `browserbase.launch()` and any model key in the `model` option. `load_dotenv()` only populates `os.environ`; you still pass the values in.


**Nothing reads my API key.** Stagehand reads no environment variables. Pass the Browserbase key to `browserbase.launch()` and any model key in the `model` option. `load_dotenv()` only populates `os.environ`; you still pass the values in.

**My script has no `history` object to read results from.** There's no run history. The value you'd have read from `history.final_result()` or `history.structured_output` is the return of your last `extract()` call, on `.data`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**My script has no `history` object to read results from.** There's no run history. The value you'd have read from `history.final_result()` or `history.structured_output` is the return of your last `extract()` call, on `.data`.
**`AttributeError` on `history` or `final_result()`.** There's no run history. The value you'd have read from `history.final_result()` or `history.structured_output` is the return of your last `extract()` call, on `.data`.


**My script has no `history` object to read results from.** There's no run history. The value you'd have read from `history.final_result()` or `history.structured_output` is the return of your last `extract()` call, on `.data`.

**My custom `@tools.action` function has nowhere to register.** Call it directly in code mode, or add it to your tool set for [tool calling](#tool-calling). Only the decorator goes away; the function body is unchanged.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**My custom `@tools.action` function has nowhere to register.** Call it directly in code mode, or add it to your tool set for [tool calling](#tool-calling). Only the decorator goes away; the function body is unchanged.
**A `@tools.action` function has nowhere to register.** Call it directly in code mode, or add it to your tool set for [tool calling](#tool-calling). Only the decorator goes away; the function body is unchanged.


**My custom `@tools.action` function has nowhere to register.** Call it directly in code mode, or add it to your tool set for [tool calling](#tool-calling). Only the decorator goes away; the function body is unchanged.

**A retried step repeats a side effect.** You're retrying `act()`, the same hazard Browser Use's `max_failures` loop had. Retry `observe()` instead and pass the resulting action to `act()` once.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**A retried step repeats a side effect.** You're retrying `act()`, the same hazard Browser Use's `max_failures` loop had. Retry `observe()` instead and pass the resulting action to `act()` once.
**A retried step repeats a side effect.** You're retrying `act()`. Retry `observe()` instead and pass the resulting action to `act()` once.


**A retried step repeats a side effect.** You're retrying `act()`, the same hazard Browser Use's `max_failures` loop had. Retry `observe()` instead and pass the resulting action to `act()` once.

**My generated script uses `Agent` or old Stagehand APIs.** The assistant is drawing on Browser Use and older Stagehand patterns in its training data. Install the rule files from [AI rules](/v4/first-steps/ai-rules).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**My generated script uses `Agent` or old Stagehand APIs.** The assistant is drawing on Browser Use and older Stagehand patterns in its training data. Install the rule files from [AI rules](/v4/first-steps/ai-rules).
**Your generated script uses `Agent` or old Stagehand APIs.** The assistant is drawing on Browser Use and older Stagehand patterns in its training data. Install the rule files from [AI rules](/v4/first-steps/ai-rules).

Co-authored-by: Alyssa Keimach <7604716+akeimach@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 13c5327

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Co-authored-by: Alyssa Keimach <7604716+akeimach@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants