10205 Commits

Author SHA1 Message Date
Magnus Müller d2fcda58b8 fix(llm/google): keep the first user message when include_system_in_user is set (#5597)
Fixes #5596

`ChatGoogle(include_system_in_user=True)` is meant to prepend the system
text to the first user message. When that message has list content it
replaced it instead: the system text became the only part sent, and the
user's text and images were dropped.

That is the shape an agent run always produces. `use_vision` defaults to
`True`, `AgentMessagePrompt.get_user_message()` returns list content as
soon as there is a screenshot, and `MessageHistory.get_messages()` puts
the state message first among the user messages. So with this flag on,
every step sent Gemini the system prompt and nothing else, with no error
anywhere.

The cause was structural: the content-part conversion lived in the
`else` arm of the prepend branch, so taking the prepend branch skipped
it. The fix computes the system text first and then always runs the
conversion, folding the system text into the leading text only for `str`
content, which is what it already did.

Before, on `main`:

```
include_system_in_user=False user ["'<browser_state>the page and the task live'", 'image']
include_system_in_user=True  user ["'You are a browser agent.'"]
```

After:

```
include_system_in_user=False user ["'<browser_state>the page and the task live'", 'image']
include_system_in_user=True  user ["'You are a browser agent.'", "'<browser_state>the page and the task live'", 'image']
```

### Tests

Three tests in `tests/ci/models/test_llm_google.py`, real objects only:

- `test_include_system_in_user_keeps_list_content_parts`: text part and
image survive alongside the prepended system text.
- `test_include_system_in_user_string_content_is_merged_into_one_part`:
parity guard for the `str` path, which was already correct and must stay
a single merged part. This one passes on `main` too.
- `test_include_system_in_user_keeps_the_agent_state_message`: builds
the message through the real `AgentMessagePrompt` with `use_vision=True`
and asserts the task and screenshot reach the serialized contents.

Reverting the change in `browser_use/llm/google/serializer.py` and
rerunning:

```
FAILED tests/ci/models/test_llm_google.py::test_include_system_in_user_keeps_list_content_parts
E   AssertionError: user text was dropped
    assert '<browser_state>the page and the task live here</browser_state>' in 'You are a browser agent.'

FAILED tests/ci/models/test_llm_google.py::test_include_system_in_user_keeps_the_agent_state_message
E   AssertionError: the task never reached the model

2 failed, 1 passed
```

With the fix, `pytest tests/ci/models/test_llm_google.py` gives 8
passed, 1 skipped (the skip is the live `gemini-3-flash-preview` test,
no API key here).

### Checks

`pre-commit run --files browser_use/llm/google/serializer.py
tests/ci/models/test_llm_google.py` passes every hook, including ruff
check, ruff format and pyright at the versions pinned in
`.pre-commit-config.yaml`.

Full suite, `pytest tests/ci -q -o addopts="" --timeout=120`: 2 failed,
1108 passed, 34 skipped on this branch, against 2 failed, 1105 passed,
34 skipped on `main` (the +3 are the new tests). The two failures are
the same `tests/ci/test_beta_agent.py` isinstance checks in both runs;
they are order-dependent and pass on their own, and they are unrelated
to this change.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes #5596 by keeping the first user message's content when
`include_system_in_user=True` is set on `ChatGoogle`. Previously, when
the message had list content (text plus images, as the agent always
produces with vision on), the system text replaced it and the user's
text and screenshot were dropped; now the system text is prepended and
the user's parts are preserved.

- Reorders the serializer so content conversion runs for both string and
list content, merging system text only into the leading text part.
- Adds three tests covering list content, string content parity, and the
agent's actual message shape.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5597?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-08-30 02:53:15 -07:00
Magnus Müller 6a2b0f4f6c Merge branch 'main' into fix_gemini_system_in_user_drops_parts 2026-08-30 02:51:07 -07:00
Magnus Müller e6ada0d0b7 fix(browser): honor BROWSER_USE_HEADLESS in BrowserProfile (#5420) (#5475)
- Read BROWSER_USE_HEADLESS env var via default_factory in
BrowserProfile

- Preserve fallback to display detection when env var is unset

- Add unit tests covering env var parsing and overrides

Fixes #5420

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Honors `BROWSER_USE_HEADLESS` in `BrowserProfile` so the env var now
sets the default headless mode instead of being ignored. Unset values
still fall back to display detection, and explicit headless args still
override. Hosts with `BROWSER_USE_HEADLESS` set may see their default
browser mode change.

- `BrowserLaunchArgs.headless` now uses a `default_factory` that parses
the env var case-insensitively: `'0'`/`'false'`/`'no'`/`'off'`/`''` =>
False, any other non-empty value => True, unset => None.
- `BrowserSession` inherits the value via its `browser_profile`.
- Tests for `BROWSER_USE_HEADLESS` and `BROWSER_USE_DISABLE_EXTENSIONS`
are deduplicated with pytest parametrization, covering profile/session
propagation and explicit overrides.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5475?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-08-30 02:29:04 -07:00
Magnus Müller 34d7978da9 Merge branch 'main' into fix/issue-5420-honor-browser-use-headless 2026-08-30 02:26:44 -07:00
Magnus Müller df21c18249 docs: correct Cloud signup credit (#5602)
## Summary
- replace the stale five-free-task claim with the current $15 one-time
signup credit
- fix the surrounding grammar

## Test
- `pre-commit run --files CLOUD.md`
- `git diff --check`

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Corrects the Cloud signup credit in `CLOUD.md` from five free tasks to
the current $15 one-time signup credit (if eligible), and fixes the
surrounding grammar.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5602?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-08-30 02:02:26 -07:00
MagMueller b4e68f19b2 docs: correct signup credit in Cloud guide
Signed-off-by: MagMueller <mamagnus00@gmail.com>
2026-08-29 21:41:22 -07:00
MagMueller b4bad86d21 fix: allow pydantic 2.13 2026-08-29 20:55:35 -07:00
Magnus Müller b87e33402d docs: migrate remote browser skill to CLI 3.0 (#5583)
## Summary
- replace removed pre-3.0 subcommands in the remote-browser skill with
the current piped-Python workflow
- document named Browser Use Cloud daemons for isolated sandbox browser
work
- add a regression test so deleted CLI commands do not return to the
skill

## Why
`skills/remote-browser/SKILL.md` still starts with `browser-use open`
and `browser-use state`. The current CLI exits 2 for both commands and
tells users to use `new_tab()` and `page_info()` through stdin.

## Test
- `uv run --python 3.11 pytest -q
tests/ci/test_browser_use_skill_install_docs.py`
- `uv run --python 3.11 pre-commit run --files
skills/remote-browser/SKILL.md
tests/ci/test_browser_use_skill_install_docs.py`
- `uv run --python 3.11 python
/Users/magnus/.codex/skills/.system/skill-creator/scripts/quick_validate.py
skills/remote-browser`

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Migrates the remote-browser skill to the current Browser Use CLI 3.0
workflow, replacing removed pre-3.0 subcommands (`browser-use open`,
`browser-use state`, etc.) with the piped-Python interface via stdin
heredocs.

- Documents named Browser Use Cloud daemons for isolated browser
sessions.
- Adds a regression test blocking retired CLI commands from returning to
the skill.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5583?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-08-29 20:36:49 -07:00
Saurav Panda 05ea3bd2c9 Merge branch 'main' into add-orcarouter-provider 2026-08-29 15:37:01 -07:00
MagMueller edfccc4078 test(openrouter): assert request parameters are forwarded 2026-08-29 11:08:31 -07:00
MagMueller f356f2ed3e fix(openrouter): validate client and response inputs 2026-08-29 10:57:03 -07:00
Aniket Wagh a266399645 fix(llm/google): keep the first user message when include_system_in_user is set 2026-08-29 20:27:38 +05:30
MagMueller 7ed622f0de test: cover all retired browser commands 2026-08-28 22:48:56 -07:00
MagMueller 5525c08c54 docs: migrate remote browser skill to CLI 3.0 2026-08-28 22:48:56 -07:00
MagMueller 295a57d957 docs(cloud): add API v4 skill reference
Signed-off-by: MagMueller <mamagnus00@gmail.com>
2026-08-28 19:31:53 -07:00
Saurav Panda 2e32d26034 fix: keep balanced trailing brackets in URLs extracted from task text (#5576)
Fixes #5575

## What was wrong

`sanitize_url_candidate()` drops trailing prose punctuation so that "Go
to https://example.com/docs." does not navigate with the sentence's
period attached. It did that with a single character class:

```python
return re.sub(r'[.,;:!?()\[\]]+$', '', candidate)
```

which also strips closing brackets the URL opened itself.
`Agent._extract_start_url()` runs every URL it finds in the task through
this before auto-navigating, so:

```
Summarize https://en.wikipedia.org/wiki/Python_(programming_language)
```

navigated to
`https://en.wikipedia.org/wiki/Python_(programming_language` and landed
on the wrong page. Wikipedia disambiguation links are the common shape.

## The fix

Whether a trailing bracket belongs to the URL is a question of balance.
`.../Python_(programming_language)` closes a bracket the URL opened, so
it is part of the path. In "See the docs (https://example.com/guide)"
the prose opened it, so dropping it stays correct. The loop strips
trailing punctuation as before and only drops a closing bracket when the
candidate has more closers than openers.

## Before and after

```python
from browser_use.agent.service import Agent
Agent._extract_start_url(None, 'Summarize https://en.wikipedia.org/wiki/Python_(programming_language)')
```

| task text | before | after |
| --- | --- | --- |
| `Summarize
https://en.wikipedia.org/wiki/Python_(programming_language)` |
`...Python_(programming_language` | `...Python_(programming_language)` |
| `Check https://example.com/a[1] please` | `https://example.com/a[1` |
`https://example.com/a[1]` |
| `Go to https://example.com/docs. Then stop.` |
`https://example.com/docs` | `https://example.com/docs` |
| `See the docs (https://example.com/guide) for details.` |
`https://example.com/guide` | `https://example.com/guide` |

The last two rows are the behaviour that had to be preserved: a sentence
period is still dropped, and a bracket the prose opened is still
dropped.

Bracket totals are counted once and decremented while trimming, with an
end index instead of reslicing, so a candidate ending in a long run of
brackets stays linear like the regex it replaces. 200,000 trailing `)`
takes 0.011s.

## Testing

Added assertions to `test_beta_agent_exposes_task_helper_methods`, next
to the existing `_extract_start_url` cases, covering both directions:
the bracket the URL opened is kept, the one the prose opened is not.
They fail on `main` and pass here.

Full `tests/ci`, before and after, on the same machine:

```
clean tree:  2 failed, 1094 passed, 34 skipped
this branch: 2 failed, 1095 passed, 34 skipped
```

The two failures are pre-existing and unrelated, both in
`test_beta_agent.py`
(`test_beta_agent_constructor_type_hints_match_browser_use_core_params`
and `test_beta_agent_initializes_tools_and_action_models`, `Tools`
class-identity assertions). They fail identically on a clean tree. I
also diffed the skipped-test lists between the two runs and they are
identical, so nothing changed state.

```
uv run ruff check browser_use/utils.py tests/ci/test_beta_agent.py   ->  All checks passed!
uv run ruff format --check ...                                      ->  2 files already formatted
uv run pyright browser_use/utils.py                                 ->  0 errors, 0 warnings, 0 informations
```

No new warnings.
2026-08-28 17:19:10 -07:00
Saurav Panda 4df55bb2bc Merge branch 'main' into fix_url_trailing_bracket_truncation 2026-08-28 17:16:41 -07:00
Saurav Panda 7cdc4dcd5d Upgrade datamodel-code-generator to 0.75.1 (#5588)
Bumps `datamodel-code-generator` from 0.53.0 to 0.75.1 in the `eval`
extra dependencies.

This is a routine dependency update to pull in bug fixes, performance
improvements, and new features from the datamodel-code-generator
project. The eval extra is used for evaluation and code generation
tasks.

No source code changes required—this is a straightforward version bump
of a transitive dependency.

https://claude.ai/code/session_013ZqGkMkydPR5r4zPpEAgKk

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Bumps `datamodel-code-generator` in the `eval` extra from 0.53.0 to
0.75.1 to clear ten CVEs the old version was vulnerable to. No source
code changes required.

**Context**
- The widest affected advisory is CVE-2026-55415 (<= 0.63.0), so any
release above 0.63.0 clears all ten.
- Nothing in the repo imports the package or calls its CLI, but CI
installs it via `uv sync --dev --all-extras`.
- Verified the full dependency graph (399 packages) still resolves and
0.75.1 generates models correctly.
- The update stays compatible with the project's `requires-python
>=3.10` and `pydantic <3,>=2` pins.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5588?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-08-28 15:53:41 -07:00
Saurav Panda fee260adf2 Merge branch 'main' into claude/datamodel-code-generator-cves-x37u9r 2026-08-28 15:51:12 -07:00
Magnus Müller f6c66917ab docs(cloud): replace retired V1 API examples with V4 (#5578)
## Why

Every checked-in raw Cloud example targets retired `/api/v1` routes that
now return 404. The folder also shows the old bearer header and legacy
request fields.

## What

- replace the five broken V1 samples with one minimal V4 create → status
→ result flow
- use the current `X-Browser-Use-API-Key` header and terminal run
statuses
- load `.env` as the setup guide promises
- link optional fields to the live V4 OpenAPI spec instead of
duplicating claims that can drift

## Verification

- `uv run ruff check examples/cloud/01_basic_task.py`
- `uv run ruff format --check examples/cloud/01_basic_task.py`
- `uv run python -m py_compile examples/cloud/01_basic_task.py`
- mocked create → running → completed → summary contract test
- `uv run pre-commit run --files examples/cloud/README.md
examples/cloud/env.example examples/cloud/01_basic_task.py`
- live route probe: the three documented V1 routes return 404;
`/api/v4/runs` returns the expected missing-API-key 401 without
credentials

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Replaces the retired V1 Cloud API examples with a single V4 flow so the
checked-in samples stop hitting endpoints that return 404.

- Drops the fast-mode, structured output, proxy, and search API examples
in favor of one create → status → result flow.
- Switches to the `X-Browser-Use-API-Key` header and terminal run
statuses.
- Cancels a run that exceeds the configurable wait limit and rejects
non-positive or non-finite `BROWSER_USE_RUN_TIMEOUT` values.
- Loads `.env` via `dotenv` as the README setup promises.
- Points optional fields to the live V4 OpenAPI spec instead of
repeating them in the README.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5578?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-08-28 13:38:32 -07:00
Ronit Sawaiyan 230881a49b Merge branch 'main' into fix/5569-send-keys-plus 2026-08-29 02:07:43 +05:30
MagMueller 1359fb22cb docs(cloud): validate example timeout setting 2026-08-28 13:35:17 -07:00
MagMueller 2391a244b3 docs(cloud): cancel runs after example timeout 2026-08-28 13:35:17 -07:00
MagMueller 80802eefb9 docs(cloud): replace retired v1 API examples with v4 2026-08-28 13:35:17 -07:00
Magnus Müller 33f22b8b4f Fix WebSocket drops during reconnect window (#5373)
## Summary

- Record WebSocket message-handler drops that occur while an automatic
reconnect is in progress.
- Schedule another automatic reconnect after the in-flight attempt
finishes.
- Clear pending reconnect state during session reset.
- Add a focused regression test.

Fixes #5366

## Tests

- `uv run pre-commit run --all-files --show-diff-on-failure`
- `uv run pyright`
- `uv run pytest -vxs tests/ci/test_browser_session.py`
- `uv run pytest -vxs tests/ci` — 483 passed, 28 skipped before an
unrelated existing failure in
`tests/ci/test_beta_agent.py::test_beta_agent_constructor_type_hints_match_browser_use_core_params`.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes WebSocket drops that happen during an in-flight reconnect by
recording them and automatically retrying after the attempt finishes.
Improves session stability and clears pending reconnect state on reset.
Fixes #5366.

- **Bug Fixes**
- Record WS drops during reconnect via `_reconnect_pending`, then
auto-schedule a follow-up reconnect once the attempt ends.
  - Clear `_reconnect_pending` on session reset and wake any waiters.
- Add a focused regression test that verifies a follow-up reconnect is
scheduled after a drop during reconnect.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5373?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-08-28 13:29:03 -07:00
Magnus Müller 281cbc8c74 Merge branch 'main' into fix/issue-5366 2026-08-28 13:26:58 -07:00
Saurav Panda fce843f406 Merge remote-tracking branch 'origin/main' into claude/datamodel-code-generator-cves-x37u9r 2026-08-28 19:59:33 +00:00
Saurav Panda c1c1830a69 fix(registry): replace sensitive placeholders inside tuple params (#5570)
### Summary

- add tuple traversal to `Registry._replace_sensitive_data`
- preserve tuple containers while replacing nested sensitive-data
placeholders
- add regression coverage for tuple and nested tuple action params

### Tests

```powershell
.venv\Scripts\python.exe -m pytest tests\ci\security\test_sensitive_data.py::test_replace_sensitive_data_inside_tuple tests\ci\security\test_sensitive_data.py::test_replace_sensitive_data_inside_nested_tuple -q
.venv\Scripts\python.exe -m pytest tests\ci\security\test_sensitive_data.py -q
.venv\Scripts\python.exe -m ruff check browser_use\tools\registry\service.py tests\ci\security\test_sensitive_data.py
git diff --check

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes `Registry._replace_sensitive_data` so sensitive-data placeholders inside tuple action params are replaced instead of left as-is, while preserving tuple types.

**Bug Fixes**
- Adds tuple traversal to the recursive secret replacement, keeping tuple containers intact for top-level and nested tuples.
- Adds regression tests covering tuple and nested tuple action params.

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

<a href="https://cubic.dev/pr/browser-use/browser-use/pull/5570?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-08-28 12:24:18 -07:00
Saurav Panda 8a5a3000e0 Merge branch 'main' into fix/5568-tuple-sensitive-data 2026-08-28 12:22:28 -07:00
MagMueller 6de522cb9a docs: sync generated Browser Harness skill 2026-08-28 12:22:03 -07:00
MagMueller 38fa689a10 build: require Browser Harness 0.1.10 2026-08-28 12:22:03 -07:00
Saurav Panda ab7fd48f63 Merge branch 'main' into fix/5568-tuple-sensitive-data 2026-08-28 12:20:13 -07:00
Saurav Panda 58d88ae309 Bump datamodel-code-generator to 0.75.1 to clear 10 CVEs
The eval extra pinned datamodel-code-generator==0.53.0, which falls inside
the affected range of ten advisories (CVE-2026-54621, -54653, -54654,
-54655, -54656, -54690, -54691, -55389, -55391, -55415). The widest range
is CVE-2026-55415 (<= 0.63.0), so any release above 0.63.0 clears all ten;
0.75.1 is the current latest.

CI installs this via `uv sync --dev --all-extras`, so the vulnerable
package was landing in CI environments even though nothing in the repo
imports it or invokes the datamodel-codegen CLI.

Verified the full graph (399 packages, all extras + dev) still resolves,
and that 0.75.1 installs and generates models correctly. Requires-python
>=3.10 and pydantic <3,>=2 both stay compatible with the project's pins.
2026-08-28 18:11:15 +00:00
IENVYshanks 2b56dc8f59 test(browser): validate literal plus CDP key events 2026-08-28 23:08:25 +05:30
Magnus Müller 30b5aca5b0 Merge branch 'main' into fix/issue-5366 2026-08-28 02:25:57 -07:00
r266-tech 81a472a3e2 fix(dom): expose image context for directly clickable images 2026-08-28 09:07:07 +00:00
IENVYshanks 0a6e427c6f fix(browser): support literal plus in send_keys 2026-08-28 11:25:30 +05:30
Aniket Wagh 3c565af3e0 perf: keep bracket trimming linear in sanitize_url_candidate
The trimming loop counted brackets across the whole candidate on every
iteration, so a candidate ending in many unmatched brackets rescanned it
once per bracket. 50,000 trailing ')' took 0.9s, where the regex this
replaced was linear.

Count the four bracket characters once and decrement as characters are
trimmed, tracking the end index instead of reslicing. Same results, and
200,000 trailing ')' now takes 0.011s.
2026-08-28 09:50:05 +05:30
Aniket Wagh d33cc68f06 fix: keep balanced trailing brackets in URLs extracted from task text
sanitize_url_candidate() strips trailing prose punctuation so that
"Go to https://example.com/docs." does not navigate with the sentence's
period attached. It also stripped every trailing ) and ], including the
ones the URL opened itself, so a task like

    Summarize https://en.wikipedia.org/wiki/Python_(programming_language)

auto-navigated to .../Python_(programming_language and landed on the wrong
page. Wikipedia disambiguation links are the common case.

Whether the bracket belongs to the URL is decided by balance: a closing
bracket with a matching opener inside the candidate is part of the path,
while one the prose opened, as in "(see https://example.com/guide)", is not.
Strip trailing punctuation as before, and only drop a closing bracket when
the candidate has more of them than openers.

Fixes #5575
2026-08-28 09:50:05 +05:30
Magnus Müller 67e7194c06 fix(dom): expose image context for clickable elements (#5541)
Fixes #4312

Image-only clickable elements can be indistinguishable in the serialized
DOM when they have no text or accessible label. Include bounded
descendant image context on the interactive parent, using
alt/title/aria-label and a query-stripped image filename while ignoring
data URLs.

Validation:
- uv run pytest -q tests/ci/test_image_only_dom_representation.py
tests/ci/test_dom_paint_order_serialization.py
- uv run ruff check browser_use/dom/serializer/serializer.py
tests/ci/test_image_only_dom_representation.py
- uv run ruff format --check browser_use/dom/serializer/serializer.py
tests/ci/test_image_only_dom_representation.py
- uv run pre-commit run --files browser_use/dom/serializer/serializer.py
tests/ci/test_image_only_dom_representation.py

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes #4312 by exposing bounded descendant image context in the
serialized DOM for image-only interactive elements. Previously,
interactive parents without text or labels serialized without context;
now they carry image alt/title/aria-label and a query/fragment-stripped
filename, with traversal and allocation bounds.

- Add `image_alt`, `image_title`, `image_label`, and `image_src`
(query/fragment-stripped filename) to interactive parents; skip `data:`
and query-only sources; cap each value to 100 chars.
- Limit to three descendant images and at most 100 descendants; traverse
lazily without copying child lists to bound allocations.
- Keep paint-order serialization unchanged; add tests for filename
propagation, query/fragment stripping, data URL filtering, traversal
limits, and non-eager traversal.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5541?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-08-27 20:42:28 -07:00
Magnus Müller fa29b0e05d Merge branch 'main' into fix/image-only-clickable-context 2026-08-27 20:37:34 -07:00
郑耀翔 9bfb1d3258 fix(registry): replace sensitive placeholders in tuples 2026-08-28 09:30:24 +08:00
Magnus Müller 35fccbb418 fix(llm): ignore empty reasoning model patterns (#5545)
## Summary

- centralize reasoning-model pattern matching for the LLM adapters
- ignore empty or whitespace-only patterns instead of treating every
model as a reasoning model
- preserve the existing matching behavior for non-empty patterns across
OpenAI, Azure, and Vercel

This prevents an empty `reasoning_models` entry from silently dropping
sampling parameters such as `temperature` and `frequency_penalty`.

## Testing

- `py -m uv run pytest -q tests/ci/models/test_llm_openai.py` (7 passed,
1 skipped)
- `py -m uv run pytest -q tests/ci/models/test_azure_responses_api.py -k
ShouldUseResponsesAPI` (6 passed)
- `py -m uv run pyright browser_use/llm/base.py
browser_use/llm/openai/chat.py browser_use/llm/azure/chat.py
browser_use/llm/vercel/chat.py tests/ci/models/test_llm_openai.py`
- `py -m uv run ruff check browser_use/llm/base.py
browser_use/llm/openai/chat.py browser_use/llm/azure/chat.py
browser_use/llm/vercel/chat.py tests/ci/models/test_llm_openai.py`

Fixes #5543

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Ignore empty and whitespace-only reasoning-model patterns so regular
models are no longer treated as reasoning models, which previously
stripped sampling params like `temperature` and `frequency_penalty`.
Matching is centralized in `is_reasoning_model`, and the `openai`,
`azure`, and `vercel` adapters all use it so only real patterns apply.

**Bug Fixes**
- Tests cover empty-pattern cases and verify reasoning params are only
set on true matches.

Fixes #5543.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5545?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-08-27 17:40:39 -07:00
Magnus Müller 56b76ddbad Merge branch 'main' into fix/5543-ignore-empty-reasoning-models 2026-08-27 17:38:05 -07:00
Magnus Müller 6ed72e1fb3 fix(filesystem): render markdown emphasis in PDF export (#5540)
`write_file` tells the model that PDF content is markdown, but after
#5502 only `#` / `##` / `###` headings were styled. Bold, italic, inline
code, and bullets printed as literal `**syntax**`.

Keep the #5502 escape-first behavior (raw ReportLab markup cannot be
restored — that is the parse bug). After `html.escape`, convert a small
markdown subset to RML. Underscores stay literal so `snake_case` names
are not italicized. `*` is only emphasis when it actually wraps a span —
`2 * 3 * 4` and globs are left alone. Fenced blocks skip inline
conversion so shell backticks survive.

Heading offsets are shared with `DocxFile` via `_split_heading`.

Fixes #5538

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Render markdown emphasis in PDF exports from `write_file` so PDFs match
the documented markdown contract. Previously only `#`/`##`/`###`
headings rendered; now bold, italic, inline code, and bullets format
correctly while we keep the escape-first fix to avoid `ReportLab` parse
issues.

- Convert `**bold**`, `*italic*`, `` `inline` ``, and list bullets
(`-`/`*`) to RML; code fences bypass inline conversion; underscores
never italicize.
- Keep glob tokens and arithmetic stars literal (e.g., `*.txt`,
`**/foo/**`, `2 * 3 * 4`); inline code protects emphasis markers.
- Restore inline code via collision-safe placeholders so user text
cannot be mistaken for stash tokens.
- Share heading parsing with `DocxFile` to align `#`/`##`/`###` levels;
add tests for emphasis, bullets, fences, globs, and edge cases.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5540?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-08-27 16:40:11 -07:00
MagMueller f168606cb9 fix(filesystem): make PDF markdown restoration collision-safe 2026-08-27 16:27:35 -07:00
lei_lei beb7494caf fix(filesystem): keep glob tokens and code-span stars literal in PDFs 2026-08-27 16:27:35 -07:00
lei_lei 25b88477a7 fix(filesystem): render markdown emphasis in PDF export
Escape first to keep the ReportLab parse fix, then convert bold/italic/code/bullets to RML so write_file PDFs match the documented markdown contract.
2026-08-27 16:27:35 -07:00
Magnus Müller 162f20fb33 fix(browser): clear cookies via Storage domain, not Network (#5562)
BrowserSession.clear_cookies() sends Network.clearBrowserCookies on the
root CDP client, which always fails:

  RuntimeError: {'code': -32601,
'message': "'Network.clearBrowserCookies' wasn't found"}

Network is a per-target domain and is not dispatchable without a session
attachment; the root client has none. Storage is browser-level, and the
cookies() getter two lines above already uses Storage.getCookies()
there.

Measured against a live CDP endpoint:

  Network.clearBrowserCookies()                  REJECTED -32601
  Storage.clearCookies()                         OK
  Storage.clearCookies(session_id=...)           OK
  Network.clearBrowserCookies(session_id=...)    OK

The private _cdp_clear_cookies() already uses Storage.clearCookies with
a session_id, though its docstring still refers to
Network.clearBrowserCookies
- this looks like the public method was missed when that one was fixed.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes `BrowserSession.clear_cookies()` so it actually clears cookies.
Previously it sent `Network.clearBrowserCookies` on the root CDP client,
which always fails because Network is a per-target domain. Now it uses
`Storage.clearCookies`, a browser-level command that matches the private
`_cdp_clear_cookies()` method.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5562?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-08-27 13:39:40 -07:00
Magnus Müller 3caaeed6e4 Merge branch 'main' into fix/clear-cookies-storage-domain 2026-08-27 13:38:24 -07:00