10284 Commits

Author SHA1 Message Date
Magnus Müller 843819cb81 docs: point bu-30b-a3b-preview at self-hosting instead of Cloud (#5774)
**Option B of two. Do not merge both.** Sibling: #5773, which deletes
the mentions instead.

## What is broken

`browser-use/bu-30b-a3b-preview` is still a live route in the Cloud
gateway
(`backend/llm_use/gateway/pricing.py` `MODAL_MODELS`, `service.py`
`_call_modal`), but
nothing is serving it. The Modal app behind that route,
`browser-use-llm-prod` in
`browser-use/deploy-llm` (`deploy.py:18`, 2x H200 `min_containers`), was
last deployed on
2025-12-16 and has not been deployed since.

Production, service `browser-use-production-llm-use`, last 90 days: 5
Modal client
initializations and 5 `Modal LLM call failed: Error code: 503`, paired
within 14 seconds.
Every observed call failed. The gateway turns that upstream 503 into a
generic HTTP 500 for
the caller, which is why it reads as "the model does not exist".

## Why not just delete it

The weights are public and people are using them.
https://huggingface.co/browser-use/bu-30b-a3b-preview is a public repo:
31B, 2.34k downloads
in the last month, 265 likes. The model exists. Only our hosting of it
does not.

## What this PR does

Says what the model actually is - open weights you run yourself.

- `examples/models/bu_oss.py` now starts from `vllm serve` and connects
with `ChatOpenAI`
against `http://localhost:8000/v1`. No `BROWSER_USE_API_KEY`, no
dependency on the dead
Cloud route. `BU_OSS_BASE_URL` / `BU_OSS_API_KEY` override the endpoint.
- `browser_use/llm/browser_use/chat.py` - the docstring now says Cloud
does not serve it.
- `skills/open-source/references/models.md` - drops the priceless OSS
pricing row and adds a
short self-hosting section with the vLLM command from the model card and
the weights URL.

Not touched: the gateway route, and
`tests/ci/models/test_llm_browseruse.py:85`.

## Checks

`ruff check`, `ruff format --check` and `git diff --check` clean.
The rewritten example was imported and constructed in this worktree:
`ChatOpenAI(model='browser-use/bu-30b-a3b-preview',
base_url='http://localhost:8000/v1', ...)`
resolves to provider `openai` with that base URL. No agent was run, and
no GPU was started,
so the end-to-end self-hosted run is not claimed here.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Updates docs and the example for `browser-use/bu-30b-a3b-preview` so the
model is presented as open weights users host themselves instead of a
Browser Use Cloud model.

- `examples/models/bu_oss.py` now uses a vLLM server and connects via
`ChatOpenAI`, with optional `BU_OSS_BASE_URL`/`BU_OSS_API_KEY`
overrides.
- The `ChatBrowserUse` docstring states the model is self-hosted only
and points to the example.
- The skills model table drops the Cloud pricing row and adds the vLLM
command and weights URL.

<sup>Written for commit 09dbfcbca9.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5774?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-13 12:45:27 -07:00
Magnus Müller 09dbfcbca9 Merge branch 'main' into agency/agi-729-oss-model-self-host 2026-09-13 12:43:03 -07:00
Magnus Müller 6e1977daa0 docs: point AI agents and crawlers to llms.txt from the README (#5775)
## Why
A neutral audit of ChatGPT, Perplexity, Google AI Mode, Gemini and
Claude (50 answers) shows every engine describes Browser Use as "an
open-source Python library" and misses the cloud browser, the hosted
agent API and Browser Harness. The README is the most-crawled page we
have, so it should say what the product is and where the
machine-readable map lives.

## Change
One blockquote above "Which Browser Use do I need?" pointing AI agents
and crawlers to https://browser-use.com/llms.txt and
https://docs.browser-use.com/llms.txt, and naming the three surfaces in
one sentence.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_016rqDxQUeLj81KTgS46m72e


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds a blockquote to the README pointing AI agents and crawlers to the
product's `llms.txt` files so answer engines stop describing Browser Use
as a Python-only library.

The blockquote links to `browser-use.com/llms.txt` and
`docs.browser-use.com/llms.txt` and names all three product surfaces:
the open-source agent, the cloud browser, and the hosted agent API.

<sup>Written for commit d5b77ab66c.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5775?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-13 09:27:33 -07:00
MagMueller d5b77ab66c docs: point AI agents and crawlers to llms.txt from the README
Answer engines describe Browser Use as a Python-only library because
that is what they find first. One line near the top of the README
names the product map at browser-use.com/llms.txt, the docs index, and
the three surfaces (open-source agent, $0.02/hour cloud browser, hosted
agent API).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016rqDxQUeLj81KTgS46m72e
2026-09-12 00:39:18 -07:00
MagMueller 6f8b0626b6 docs: point bu-30b-a3b-preview at self-hosting instead of Cloud
Browser Use Cloud does not serve `browser-use/bu-30b-a3b-preview`. The gateway
still has the route (cloud `backend/llm_use/gateway/pricing.py` MODAL_MODELS,
`service.py` _call_modal), but the Modal app behind it,
`browser-use-llm-prod` in browser-use/deploy-llm, was last deployed on
2025-12-16 and every production call since returns upstream HTTP 503, which the
gateway reports to the caller as a generic 500.

The weights are public and in use: https://huggingface.co/browser-use/bu-30b-a3b-preview
is a public repo with 2.34k downloads in the last month. So instead of dropping
the model, say what it actually is - open weights you host yourself.

- `examples/models/bu_oss.py` now starts from a vLLM server and talks to it
  through `ChatOpenAI`, so it needs no BROWSER_USE_API_KEY and does not depend
  on the dead Cloud route.
- The `ChatBrowserUse` docstring says Cloud does not serve it.
- The skills model table gains a self-hosting section with the vLLM command
  from the model card, and loses the priceless OSS row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oComYHNbeV4e21v22Bbdn
2026-09-12 00:22:16 -07:00
Magnus Müller 50f205533f docs: name agents in CLI setup introduction (#5762)
Name Claude Code, Codex, Hermes, and OpenClaw in the CLI quickstart
introduction so readers know where to paste the setup prompt.

Validation: pre-commit passed for README.md; git diff --check passed.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Names Claude Code, Codex, Hermes, and OpenClaw in the CLI quickstart so
readers know which agents can receive the browser setup prompt.

<sup>Written for commit b752b973d3.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5762?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-09 13:10:15 -07:00
MagMueller b752b973d3 docs: name agents in CLI setup introduction 2026-09-09 13:07:03 -07:00
Magnus Müller 7d17103fae docs: simplify benchmark heading and link JS harness (#5761)
Use “Browser Use Benchmark v2” as the README section heading and remove
the repeated title from the plot image so the chart follows the heading
directly.

Add Browser Harness JS to Related Repositories after confirming that it
is public and MIT-licensed. The browser-use-js repository redirects to
Browser Use Pi, which is already listed.

The plot uses a lossless JPEG crop removing only its top 80 rows.
Decoded-pixel comparison confirms that every retained pixel matches the
original.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Replaces the generic “Benchmark” README heading with “Browser Use
Benchmark v2” and removes the repeated title from the plot so the chart
follows the heading directly. Adds the public, MIT-licensed Browser
Harness JS repository to Related Repositories; the existing Browser Use
Pi entry remains the destination for the redirected `browser-use-js`
repository.

- The plot is a lossless crop with only its top 80 title rows removed,
so all retained pixels remain unchanged.
- The documentation README links to the source image and records the
crop rationale.

<sup>Written for commit 2957f8a1c8.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5761?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-09 13:06:26 -07:00
MagMueller 2957f8a1c8 docs: move benchmark title out of plot and link JS harness 2026-09-09 13:03:53 -07:00
Magnus Müller f5f58c6ef4 docs: restore branded header and refine README paths (#5759)
Restore the original branded README header, including the AI browser
agent artwork, downloads, GitHub stars, Discord, and other navigation
badges, while retaining the orange bridge painting beneath it.

Shorten the CLI introduction to one sentence and remove OpenCode from
the README and diagram. Update the diagram captions to “Easiest way to
scale up,” “Automate your own browser tasks,” and “Run fully local.”
Identify Browser Harness as our CLI and add Browser Use Pi, Video Use,
and macOS Harness to Related Repositories with one-line descriptions
verified against their public READMEs.

Validation: pre-commit and git diff checks passed. README anchors
resolve; both SVGs parse and contain the requested captions. The Python
examples are unchanged. Both diagram themes and the GitHub header were
visually inspected; header assets load on desktop and mobile, with no
page overflow at 390 pixels.
2026-09-09 12:54:42 -07:00
MagMueller 3ef1629408 docs: restore branded README header and clarify paths 2026-09-09 12:52:33 -07:00
Magnus Müller eb2099a545 docs: refresh README branding and related repositories (#5758)
The README header used several rows of badges before explaining how to
start. Replace them with one purpose line, a short description of the
three entry points, and Quickstart/Demos/Docs/Cloud navigation. Add the
existing orange key-bridge painting from the Browser Use website beneath
the introduction.

Add a Related Repositories section near the bottom for Browser Harness,
Cloud SDK, and Benchmark. Move community links and the download badge
into the footer, preserve the driving-test GIF and product diagram, and
document the artwork source.

Validation: pre-commit and git diff checks passed. The artwork URL
returns the original website JPEG successfully (169 kB). All README
heading anchors and the three repository links resolve. GitHub renders
the artwork at 720 pixels on desktop and 324 pixels on mobile without
page overflow; the related-repositories table fits mobile width.
2026-09-09 12:43:51 -07:00
MagMueller cc4c9e8d1d docs: simplify README header and add related repositories 2026-09-09 12:41:28 -07:00
Magnus Müller e48b974d3b docs: refresh README quickstart and FAQ (#5757)
The README left the first Python example without a run command or
visible result, and its FAQ blurred the free library, paid models,
managed browsers, and fully hosted agent.

This update keeps OpenAI Luna as the default, adds commented BU2 and
cloud-browser alternatives with API-key instructions, and completes the
save/run/result flow. It refreshes the FAQ with current model and
authentication guidance, a self-contained custom-tool example, and
separate browser/agent hosting choices. Hermes and OpenClaw now appear
consistently in the CLI copy and both diagram themes.

Validation: pre-commit passed for all changed files; all three Python
snippets and the commented alternatives parse; model/browser
configuration and custom-tool registration/execution were checked
against the current library without model calls or browser provisioning;
all 23 Markdown links resolve. Both diagram themes were visually
inspected.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Refreshes the README quickstart and FAQ so the Python example is
runnable end-to-end and the free library is clearly separated from paid
models, managed browsers, and the hosted agent.

The quickstart now includes the run command and output, plus commented
BU2 and cloud-browser alternatives with API-key instructions. The FAQ
updates model recommendations, replaces the custom-tool snippet with a
runnable example, and clarifies browser vs. agent hosting choices.
Hermes and OpenClaw now appear consistently in CLI copy and both diagram
themes.

<sup>Written for commit bfc01ebfad.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5757?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-09 12:36:14 -07:00
MagMueller bfc01ebfad docs: refresh README quickstart and FAQ 2026-09-09 12:33:50 -07:00
Magnus Müller c25ae8f919 docs: number the three paths and simplify README copy (#5756)
Make the three Browser Use paths consistent between the diagram and
quickstarts: fully hosted cloud, CLI, and Python library. Combine the
open source Browser Use agent and its Python library into one diagram
block connected directly to local and cloud browsers.

Rename the demo heading to “Navigate the web like a human does.” and
mention choosing a date and time. Keep the inline animation, remove the
separate Johannes link, expand the hosted-service description, and
remove the no-card text and Odysseys claim.

Validation: pre-commit, whitespace checks, Python snippet parsing,
numbered section order and anchors, SVG parsing, and single-block
structure in both diagram themes passed. Inspected both diagram themes
and the actual GitHub README on desktop and at 390px mobile width. All
three quickstart anchors resolve, the inline GIF remains present, and
there is no page overflow.
2026-09-09 12:17:24 -07:00
MagMueller 674b0dfce2 docs: clarify numbered paths and simplify README demo copy 2026-09-09 12:15:38 -07:00
Magnus Müller fcc4f01964 docs: embed driving demo and clarify the three Browser Use paths (#5754)
The README required opening X to watch the driving-test demo and did not
show how the Python library fits alongside the CLI and hosted service.
Embed the complete recording as a looping GIF and lead the product guide
with an editable diagram of all three paths.

- Fully hosted cloud runs OpenCode, Browser Use CLI, and a cloud
browser.
- The CLI connects existing agents, including Pi, to local or cloud
browsers.
- The Python library runs the Browser Use agent directly and can use
local or cloud browsers.

Remove the Playwright branch, the vertical diagram expansion, the three
artwork cards, and the duplicate open-source/cloud comparison. Follow
the graphic with the CLI quickstart, existing direct-OpenAI Luna
example, and hosted API docs. Simplify the $15 credit text and remove
the pricing text-file link.

The SVGs are adapted from the SDK's product diagram. The GIF preserves
Johannes's public 20.4-second recording at 960×540 and 10 fps; it is
hosted externally so it does not add media to repository clones.

Validation: pre-commit, whitespace checks, Python snippet parsing, local
image paths, SVG parsing, and visual inspection of both diagram themes
passed. Verified actual GitHub rendering on desktop and at 390px mobile
width, all three section anchors, and automatic GIF playback without
clicking. The public attachment downloads without authentication and
matches the local GIF byte for byte.

Demo preview:

![Browser Use V4 booking a driving
test](https://github.com/user-attachments/assets/135885e8-1141-4e10-b719-bf690ae7d260)
2026-09-09 12:06:49 -07:00
MagMueller 0acc9ce89b docs: embed driving demo and simplify the three Browser Use paths 2026-09-09 12:05:07 -07:00
Magnus Müller ac0e918bbf docs: feature driving-test demo and clarify Browser Use product choices (#5753)
The README's older form and extraction demos did not reflect the current
product or website. Feature Johannes's public driving-test booking demo
and explain the local, managed-browser, and hosted-agent options using
the website's existing artwork.

Reuse the current light/dark product diagrams from the SDK docs, with a
separate expandable vertical view for mobile. The Python quickstart
calls OpenAI directly with `ChatOpenAI(model='gpt-5.6-luna',
reasoning_effort='xhigh')`, an `OPENAI_API_KEY`, and explicit `.env`
loading. Preserve the benchmark plot and its qualification.

Sources: [driving-test
demo](https://x.com/mathisdittrich/status/2078619618265141560), [public
showcase](https://browser-use.com/showcase), and [product
guide](https://docs.browser-use.com/cloud/which-product). Asset
provenance is recorded in `static/readme/README.md`.

Validation:
- Pre-commit and `git diff --check` passed.
- All three Python snippets parse; local image references resolve and
all four SVGs parse.
- Verified actual GitHub rendering at desktop and 390px mobile widths,
including the vertical diagram disclosure. The product table fits
without horizontal scrolling.
- New website/docs links return HTTP 200. No live model invocation was
performed.
2026-09-09 11:50:06 -07:00
MagMueller cb66bf1313 docs: focus on driving-test demo and use Luna directly through OpenAI 2026-09-09 11:48:29 -07:00
MagMueller 554700d9eb docs: keep product paths readable on mobile 2026-09-09 11:40:42 -07:00
MagMueller b5963c9dc3 docs: use a real demo frame and GitHub-compatible diagram themes 2026-09-09 11:37:42 -07:00
MagMueller 5e7178f7eb docs: refresh README with branded product paths and public demos 2026-09-09 11:35:57 -07:00
Magnus Müller b1f1162c4c docs: replace README plot with hard benchmark v2 (#5752)
Replace the README's BU Bench V1 plot with the supplied Browser Use
Benchmark v2 artwork. Reduce the GPT-6 ASTRA label and 77.3% score text,
retaining the score value.

The caption links to `browser-use/benchmark`, explains that the
benchmark targets the hardest browser tasks, and notes that smaller
models can achieve very high success rates on easier tasks. Preserve the
benchmark repository's qualification that the plotted results cover a
60-task subset. Remove the outdated reference to the plot as an
open-source versus hosted-agent comparison.

Validation: pre-commit checks and `git diff --check` passed; the edited
chart's labels and plotted values were visually checked against the
supplied artwork.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Replaces the README's BU Bench V1 plot with the new Browser Use
Benchmark v2 artwork. Updates the caption and surrounding text to
describe the new benchmark, including its focus on the hardest browser
tasks and the 60-task subset used for the plotted results. Also removes
the outdated open-source vs hosted-agent comparison and the "see plot
above" reference in the cloud agent section.

<sup>Written for commit f40fa559fb.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5752?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-09 11:18:15 -07:00
MagMueller f40fa559fb docs: show hard benchmark v2 in README 2026-09-09 11:14:45 -07:00
Gregor Žunič 2b1f9d3779 Make the README and Cloud skill easier for agents to use (#5714)
Add concise starter choices, eligible credits, and bounded V4 guidance.
2026-09-07 12:55:33 -07:00
Gregor Žunič 293b338f26 Shorten Cloud starter guidance 2026-09-07 12:41:37 -07:00
Gregor Žunič 47ed960772 docs: check Python run status enum correctly 2026-09-07 11:20:58 -07:00
Gregor Žunič 2db9f6ef02 docs: guide agents from free credits to a bounded Cloud run 2026-09-07 11:17:48 -07:00
Magnus Müller 8a9b5f8618 fix(openrouter): pass custom fields through SDK extra_body (#5703)
Passing OpenRouter-specific fields such as `extra_body={"provider":
{"order": ["test-provider"]}}` currently fails before any HTTP request
with `AsyncCompletions.create() got an unexpected keyword argument
'provider'`.

Both text and structured-output paths unpack `extra_body` into SDK
keyword arguments. Pass it through the SDK's `extra_body` parameter
instead, so custom fields become part of the JSON request body.

Regression tests exercise the real OpenAI SDK with an HTTPX mock
transport and verify custom fields reach the outgoing JSON for both
output modes. They also cover omitted and empty extra bodies and
structured response parsing.

Validation:
- Before the fix: 2 regression failures, 8 passing tests in the
OpenRouter model suite.
- After the fix: 15 tests passed across the OpenRouter model and
token-cost suites.
- All applicable pre-commit hooks passed, including Ruff and Pyright.
- No real API key or live provider request was used.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes passing OpenRouter-specific fields through the SDK's `extra_body`
parameter so custom fields like provider routing reach the HTTP request
body instead of failing before any request is sent.

- Text and structured-output paths now forward `extra_body` instead of
unpacking it into SDK keyword arguments.
- Adds regression tests with a mock HTTP transport covering both output
modes and omitted or empty `extra_body` values.

<sup>Written for commit d05053ed60.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5703?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-07 11:12:49 -07:00
shashank-100 d05053ed60 fix(openrouter): pass custom fields through SDK extra_body 2026-09-06 13:13:27 +05:30
Magnus Müller e25ab65e69 fix: honor MCP disable security environment setting (#5695)
## Fix

Read the documented `BROWSER_USE_DISABLE_SECURITY` setting when
resolving local MCP browser configuration.

The default remains secure. An unset variable leaves the stored profile
unchanged; explicit `true` or `false` overrides it without rewriting the
config file. Existing explicit browser-session parameters still take
priority.

Only the config declaration/mapping and its regression tests change.
This does not add a tool-controlled security switch or alter the normal
BrowserProfile default.

## Verification

- Before the mapping fix: four new regression cases failed; fourteen
passed.
- After: all eighteen focused config tests pass, including unset,
persisted true/false and explicit environment overrides.
- The related profile arguments, extension-security and lazy-config
checks also pass: twenty-seven local cases in total.
- All applicable pre-commit hooks pass.
- Four fresh owned headless Chrome sessions exercised the actual MCP
browser initialization and two synthetic loopback origins. Unset and
false kept cross-origin fetch blocked with no `--disable-web-security`
flag. True enabled the flag and allowed the synthetic response. An
explicit false session override restored the block even with the
environment set to true.
- CI's hosted task evaluation reports 2/2, but both tasks log that they
skipped because `BROWSER_USE_API_KEY` is absent. Those are not counted
as agent or provider validation.

The local proof used no provider calls, shared browser profile or
production request. No release or deployment was performed. The explicit
true setting intentionally disables browser web-security checks, as
already documented.
2026-09-05 10:28:28 -07:00
Magnus Müller fc40175e26 test: update the actual default MCP profile in precedence fixture 2026-09-05 10:25:46 -07:00
Magnus Müller 72e53e3f3b fix: honor MCP disable security environment setting 2026-09-05 10:20:11 -07:00
Magnus Müller 4970e3ad2b fix: use canonical LLM model property in cloud events (#5279)
## Summary

- use the canonical LLM model property throughout the related adapter
paths
- align the internal proxy with the canonical interface
- add focused regression coverage for a minimal adapter

## Tests

- `uv run pytest -q
tests/ci/test_beta_agent.py::test_beta_agent_runs_through_sdk_and_reuses_session_for_followup
tests/ci/test_agent_cloud_events.py`
- `uv run ruff check browser_use/beta/service.py
browser_use/agent/cloud_events.py tests/ci/test_agent_cloud_events.py
tests/ci/test_beta_agent.py`
- `uv run ruff format --check browser_use/beta/service.py
browser_use/agent/cloud_events.py tests/ci/test_agent_cloud_events.py
tests/ci/test_beta_agent.py`
- `uv run pre-commit run --all-files`
2026-09-05 09:11:06 -07:00
MagMueller d5fb0f5620 Merge remote-tracking branch 'origin/main' into HEAD 2026-09-05 07:51:52 -07:00
Magnus Müller e3251d87e0 fix(cli): support module invocation on Windows (#5552)
## Description

Closes #5539

The `uv`-generated `browser-use.exe` console-script launcher can be
blocked by Windows Smart App Control before Python starts. The package
already exposes `browser_use.cli:main`, but it had no package module
entry point, so users could not use the interpreter-based fallback.

## Type of Change

- [x] Bug fix (non-breaking change that fixes a known issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Add `browser_use/__main__.py` delegating to the existing CLI `main()`.
- Add a subprocess regression test proving `python -m browser_use doctor
--help` matches the existing CLI module entry point.

## Testing

- [x] Focused CLI tests pass (`uv run pytest -q
tests/ci/test_browser_use_cli.py`)
- [x] Ruff check passes (`uv run ruff check browser_use/__main__.py
tests/ci/test_browser_use_cli.py`)
- [x] Ruff format check passes (`uv run ruff format --check
browser_use/__main__.py tests/ci/test_browser_use_cli.py`)
- [x] Pre-commit passes on changed files (`uv run pre-commit run --files
browser_use/__main__.py tests/ci/test_browser_use_cli.py`)
- [x] New test added for the module entry point
- [ ] Full test suite
- [ ] Pyright on the full repository

### Test Output

```text
uv run pytest -q tests/ci/test_browser_use_cli.py
4 passed in 1.72s

uv run ruff check browser_use/__main__.py tests/ci/test_browser_use_cli.py
All checks passed!

uv run ruff format --check browser_use/__main__.py tests/ci/test_browser_use_cli.py
2 files already formatted

uv run pre-commit run --files browser_use/__main__.py tests/ci/test_browser_use_cli.py
all applicable hooks passed
```

## Real Behavior Proof

- Environment: Windows 10 host, CPython 3.12.14 managed by `uv`, source
checkout from `main`.
- Exact command / steps: `uv run python -m browser_use doctor --help`
and `uv run python -m browser_use.cli doctor --help`.
- Observed result: both commands exited successfully and printed `usage:
browser-use doctor [--fix-snap]`.
- Not tested: Windows 11 Smart App Control enforcement itself, because
this environment does not expose that policy state. The module path
avoids the generated console-script executable; signed distribution
remains an upstream packaging concern.

## Runtime Rollout Safety

- Rollout-managed feature(s): none.
- Minimum rollout channel: next package release.
- Stable/default behavior changed: no; this adds an alternate invocation
path.
- Kill switch / disable path: not applicable.
- Unsafe override required: none.
- Qualification impact: focused CLI tests and pre-commit checks.
- Rollback path: remove `browser_use/__main__.py` and its focused test.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have added tests that prove the fix is effective
- [x] New and existing focused tests pass locally
- [x] I did **not** edit `CHANGELOG.md`

## Screenshots (if applicable)

Not applicable.

## Additional Notes

This provides the interpreter-based workaround requested in #5539
without disabling Smart App Control or changing the existing
console-script behavior.
2026-09-05 07:50:12 -07:00
MagMueller 0b2275a1a2 Merge remote-tracking branch 'origin/main' into HEAD 2026-09-05 00:00:04 -07:00
MagMueller e369e7c1ab Merge remote-tracking branch 'origin/main' into HEAD 2026-09-05 00:00:04 -07:00
MagMueller b1028339e9 Merge remote-tracking branch 'origin/main' into HEAD 2026-09-04 23:59:51 -07:00
Magnus Müller d5453ae80e fix(llm): resolve DeepSeek and Cerebras API keys explicitly (#5672)
## Summary
- resolve `DEEPSEEK_API_KEY` and `CEREBRAS_API_KEY` explicitly instead
of letting `AsyncOpenAI` fall back to `OPENAI_API_KEY`
- raise a 401 `ModelProviderError` when neither an explicit `api_key`
nor the provider's env var is set
- extends the fix from #5599 to the two remaining adapters with the same
defect

## Details

`ChatDeepSeek` and `ChatCerebras` pass `api_key=self.api_key` into
`AsyncOpenAI` while pointing `base_url` at `api.deepseek.com` and
`api.cerebras.ai`. When `api_key` is `None`, the SDK resolves
`OPENAI_API_KEY` and authenticates those third-party endpoints with it.

This is the same defect described in item 2 of #5598 and fixed for
`ChatOpenRouter` and `ChatVercel` in #5599. `ChatOrcaRouter` already
guards against it, and states the hazard in an inline comment:

> `AsyncOpenAI` falls back to `OPENAI_API_KEY` when `api_key` is unset,
which would send an unrelated provider's key to the OrcaRouter endpoint.

`skills/open-source/references/models.md` already documents
`DEEPSEEK_API_KEY` (line 15) and `CEREBRAS_API_KEY` (line 18) as these
providers' env vars, and `config.py` exposes a `DEEPSEEK_API_KEY`
property — neither adapter read them. No docs change is needed; this
makes the code match what was already documented.

## Scope

I audited the nine adapters that authenticate with an API key at
construction time, using a script that sets `OPENAI_API_KEY` to a
canary, unsets every provider-specific variable, and inspects the
resolved key and `base_url` on the constructed client.

Five adapters point an OpenAI-SDK client at a non-OpenAI `base_url`.
Three already guard (`ChatOpenRouter`, `ChatVercel`, `ChatOrcaRouter`);
these two did not.

Verified unaffected and deliberately unchanged: `ChatGroq` and
`ChatAnthropic` use their own vendor SDKs and never resolved the canary.
`ChatOpenAI` resolves `OPENAI_API_KEY` while pointed at
`api.openai.com`, which is correct.

Before / after, with all other rows byte-identical:

```
 LEAK  the canary to a foreign endpoint : ['ChatDeepSeek', 'ChatCerebras']
 LEAK  the canary to a foreign endpoint : none
```
## Tests
- `uv run pytest -q tests/ci/models/test_llm_deepseek.py
tests/ci/models/test_llm_cerebras.py`
- `uv run pre-commit run --files browser_use/llm/deepseek/chat.py
browser_use/llm/cerebras/chat.py tests/ci/models/test_llm_deepseek.py
tests/ci/models/test_llm_cerebras.py`

The new tests fail on `main` and pass with this change:


tests/ci/models/test_llm_deepseek.py::test_provider_key_does_not_fall_back_to_openai_key
FAILED
    AssertionError: assert 'wrong-provider-key' == 'deepseek-key'

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes DeepSeek and Cerebras clients sending `OPENAI_API_KEY` to their
endpoints when no explicit key is provided. Each adapter now uses
`DEEPSEEK_API_KEY` or `CEREBRAS_API_KEY` when no `api_key` is passed,
and raises a 401 `ModelProviderError` if neither is set.

- Explicit `api_key` takes precedence over the provider env var.
- Setups relying on `OPENAI_API_KEY` for these providers must switch to
the provider-specific env var.
- Adds regression tests for fallback prevention and explicit key
precedence.

<sup>Written for commit 24f60f71bb.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5672?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-04 23:49:49 -07:00
Growth Radar QA 24f60f71bb Merge remote-tracking branch 'origin/main' into HEAD 2026-09-04 23:43:34 -07:00
Magnus Müller 1706dd4e37 fix(dom): match hidden code styles case-insensitively (#5640)
## Summary

- normalize casing and whitespace before applying the existing `display:
none` filter for `<code>` elements
- prevent hidden code payloads from entering clean Markdown when inline
CSS uses valid case or whitespace variants
- add real-browser regression coverage for lowercase, mixed-case,
tab-separated, and `!important` declarations

Fixes #5637

## Testing

- `uv run pytest tests/ci/test_html_serializer_style.py -q` — 4 passed
- `uv run pytest tests/ci/test_markdown_extractor.py
tests/ci/test_markdown_chunking.py -q` — 38 passed
- `uv run pre-commit run --files
browser_use/dom/serializer/html_serializer.py
tests/ci/test_html_serializer_style.py` — all hooks passed

Each regression case verifies that Chrome computes the element as
`display: none` and that `extract_clean_markdown` omits the hidden
payload while preserving visible content.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes hidden code payloads from leaking into clean Markdown when inline
`style` uses valid case or whitespace variants like `Display: None` or
`DISPLAY:\tNONE`. Fixes #5637.

- Normalizes style casing and whitespace before applying the existing
`display: none` filter.
- Adds regression tests for lower-case, mixed-case, tab-separated, and
`!important` declarations.

<sup>Written for commit c9b3e5507a.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5640?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-04 23:36:45 -07:00
Magnus Müller c9b3e5507a Merge branch 'main' into fix/hidden-code-style-case 2026-09-04 23:34:04 -07:00
Magnus Müller 2b696cbdc5 fix(aws): support query strings in Bedrock image URLs (#5676)
## Why

`AWSBedrockMessageSerializer._is_url_image()` checked the raw URL
suffix, so otherwise supported image URLs were rejected when they
contained a query string or fragment. The same check was case-sensitive
for the HTTP scheme.

That prevents common signed/CDN image URLs from reaching the existing
Bedrock download path.

## What changed

- Parse URLs with `urlsplit()` and classify supported extensions from
the path, independent of query strings and fragments.
- Accept HTTP(S) schemes case-insensitively while rejecting missing or
malformed authorities without leaking parser exceptions.
- Prefer a recognized response `Content-Type`, then use the parsed path
extension when the server returns a generic or missing content type.
- Reject BMP URLs at the classifier because Bedrock Converse supports
JPEG, PNG, GIF, and WebP—not BMP.
- Add real local HTTP-server coverage for signed query preservation,
uppercase schemes, content-type precedence, generic/missing MIME
fallback, and malformed URLs.

## Verification

- `uv run pytest tests/ci/models/test_aws_bedrock_serializer.py
tests/ci/models/test_chat_anthropic_bedrock_client_config.py
tests/ci/models/test_chat_anthropic_bedrock_empty_content.py` — 22
passed
- `uv run pre-commit run --all-files` — all hooks passed, including Ruff
and Pyright
- `./bin/test.sh` — 1,157 passed, 34 skipped
- `git diff --check origin/main..HEAD` — clean

The regression was also reproduced against `origin/main`: a queried PNG
was rejected before the change. The HTTP tests use a real local server
rather than mocked download calls.

Fixes #5658

Developed with AI assistance; I reviewed the final diff and validation
results.
2026-09-04 23:32:30 -07:00
Magnus Müller d283202160 Merge branch 'main' into fix/bedrock-image-url-query 2026-09-04 23:30:34 -07:00
Magnus Müller 9b8dccce92 fix(llm): remap retired Mistral pixtral-large alias (#5673)
## Summary
- `pixtral_large` and `mistral_pixtral-large` resolved to
`pixtral-large-latest`, which Mistral no longer accepts
- remap both to `mistral-medium-latest`, the replacement named on
Mistral's model card
- add factory tests covering all five Mistral aliases

## Details

`get_llm_by_name` maps Pixtral Large in two places — `mistral_aliases`
for the unprefixed form and `mistral_map` for the provider-prefixed
form. Both pointed at `pixtral-large-latest`. Mistral lists Pixtral
Large (`pixtral-large-2411`) in its deprecated and retired models table,
and the identifier is no longer accepted.

Verified against `api.mistral.ai` on 2026-09-04, paid tier, identical
request shape for all five:

    mistral-medium-latest   HTTP 200
    mistral-small-latest    HTTP 200
    codestral-latest        HTTP 200
    mistral-large-latest    HTTP 200
    pixtral-large-latest    HTTP 400
{"type":"invalid_model","message":"Invalid model: pixtral-large-latest"}

The four passing controls rule out a credentials or tier issue: the same
key succeeds on every other alias in the map. Only
`pixtral-large-latest` is rejected, and Mistral reports it as an invalid
identifier rather than a gated one.

Pixtral Large's model card names Mistral Medium 3.5 as its replacement,
so both entries now resolve to `mistral-medium-latest`. Mistral Medium
3.5 is multimodal, so image support is preserved. Removing the aliases
outright was the alternative,
but that would surface the same failure as an opaque API error rather
than resolving to a working model — happy to change it if you'd rather
they be dropped.

The other four aliases were checked and left unchanged.

## Tests
- `uv run pytest -q tests/ci/models/test_llm_model_factory.py`
- `uv run pre-commit run --files browser_use/llm/models.py
tests/ci/models/test_llm_model_factory.py`

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Remaps both Mistral Pixtral Large aliases (`pixtral_large` and
`mistral_pixtral-large`) from the retired `pixtral-large-latest` to
`mistral-medium-latest`, which Mistral's model card lists as the
replacement, so requests no longer fail with an invalid model error.

- Mistral rejects `pixtral-large-latest` with HTTP 400; the other four
Mistral aliases still return HTTP 200.
- Mistral Medium 3.5 is multimodal, so image support is preserved.
- Adds tests covering all five Mistral aliases.

<sup>Written for commit 10729fbd68.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5673?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-04 23:23:09 -07:00
Magnus Müller 10729fbd68 Merge branch 'main' into fix/mistral-retired-pixtral-alias 2026-09-04 23:20:39 -07:00
Magnus Müller cfe10a2358 fix(llm): stop dropping system messages in the Gemini request (#5664)
Fixes #5628
Fixes #5624

### Problem

`GoogleMessageSerializer.serialize_messages()` lost system instructions
in two independent ways, so this fixes both together — they live in the
same function and share the same state.

**1. Only the last system message survived (#5628).** With the default
`include_system_in_user=False`, each system message assigned to the same
variable:

```python
else:
    system_message = message.content   # overwrites the previous one
```

**2. The system text vanished entirely when an assistant turn came first
(#5624).** With `include_system_in_user=True`, the prepend was gated on
`not formatted_messages`:

```python
if include_system_in_user and system_parts and role == 'user' and not formatted_messages:
```

For a `system -> assistant -> user` ordering, `formatted_messages` is
already non-empty by the time the user message arrives, so the text was
never prepended — and because `system_message` stays `None` on that
branch, it was not returned as a system instruction either. It was
simply dropped.

### Fix

- Always collect into `system_parts`, regardless of the flag, so nothing
is overwritten.
- Drop the `not formatted_messages` guard. The documented behaviour
targets the first *user* message; what precedes it is irrelevant.
`system_parts` is cleared after use, so it still fires exactly once.
- Join whatever remains in `system_parts` into the returned instruction.
With `include_system_in_user=False` that is every system message, in
order. With it set, this only triggers when there was no user message to
merge into — previously that case discarded the text silently.

A single system message still produces the identical instruction string,
so existing callers see no change.

### Tests

New `tests/ci/models/test_google_serializer.py` covering the unchanged
single-message case, both messages surviving in order, the `system ->
assistant -> user` prepend, and the no-user-message fallback.

### Evidence

On `main` (fix reverted, new tests present):

```
test_single_system_message_becomes_the_system_instruction                    PASSED
test_all_system_messages_reach_the_system_instruction                        FAILED
test_system_text_is_prepended_even_when_an_assistant_message_comes_first     FAILED
test_system_text_falls_back_to_the_instruction_when_there_is_no_user_message FAILED
3 failed, 1 passed in 6.63s
```

With this branch: `4 passed`.

`tests/ci/models` and `tests/ci/security` pass (212 tests), along with
`ruff check`, `ruff format`, and `pyright`.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes #5628 and #5624: `GoogleMessageSerializer.serialize_messages()`
dropped system messages in two ways, collapsing multiple system messages
to the last one and losing system text entirely when an assistant
message preceded the first user message. The serializer now preserves
every system message, prepends them to the first user message even after
an assistant turn, and returns leftover text as the system instruction
when no user message exists or when the first user turn has already been
serialized. Single system messages still produce the identical
instruction string, and regression tests cover all five cases.

<sup>Written for commit 007d63516c.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5664?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
2026-09-04 18:54:55 -07:00