## Summary
Fixes#4046
The skill CLI crashes on startup when `lmnr` is installed but internally
broken (e.g., Python 3.13 with certain package states). The import
raises `TypeError` instead of `ImportError`, which escapes the existing
handler and kills the entire application.
## Root Cause
`browser_use/observability.py` line 52 only catches `ImportError`, but a
broken `lmnr` installation can raise `TypeError` during its internal
initialization.
## Fix
Broadened `except ImportError` to `except (ImportError, TypeError)` so
the no-op fallback decorator is used in both failure modes. Chose
specific exceptions over `except Exception` to avoid masking unrelated
errors.
## Tests Added
New file: `tests/ci/test_observability.py` with 4 tests:
- `test_fallback_when_lmnr_not_installed` ImportError fallback
- `test_fallback_when_lmnr_raises_type_error` TypeError fallback
(regression for #4046)
- `test_observe_noop_decorator_works_on_sync_function` sync decorator
verification
- `test_observe_noop_decorator_works_on_async_function` async decorator
verification
All pass. Happy to adjust based on feedback!
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes a startup crash in the skill CLI when `lmnr` is installed but
broken by catching `TypeError` during import and falling back to the
no-op observe decorator. Keeps the CLI running even if observability is
unavailable.
- **Bug Fixes**
- Catch `(ImportError, TypeError)` in `browser_use/observability.py` and
disable observability when `lmnr` fails to import.
<sup>Written for commit 80f798bc17.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Resolves#4683
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes input clearing so we don’t stop on JS clear failures, and
clarifies “clear-then-type” as the default. Adds a clear-only option via
text="" and a way to append via clear=False. Resolves#4683.
- **Bug Fixes**
- Removed premature returns in JS clear to enable fallback strategies.
- Aligned docs and help to the default behavior: clear-then-type;
`text=""` clears only; `clear=False` appends (`browser_type` tool,
`InputTextAction` schema, CLI `input`, SKILL.md).
<sup>Written for commit 4476f6e16e.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
## Problem
`list_chrome_profiles()` in `browser_use/skill_cli/utils.py` opens
Chrome's `Local State` JSON file without specifying an encoding:
```python
with open(local_state_path) as f:
```
On Windows with a non-UTF-8 default locale (e.g. Chinese GBK/CP936),
Python's `open()` uses the system code page. Chrome's `Local State` is
always UTF-8, so profile names containing non-ASCII characters (e.g.
Chinese `用户1`) are decoded as mojibake (`鐢ㄦ埛1`).
## Fix
Add `encoding='utf-8'` to the `open()` call, consistent with how
`browser_use/browser/profile.py` already handles file reads (e.g. lines
949, 1062, 1108).
## Reproduction
On a Windows machine with Chinese system locale:
```python
profiles = Browser.list_chrome_profiles()
for p in profiles:
print(p["name"]) # Before fix: 鐢ㄦ埛1 (mojibake)
# After fix: 用户1 (correct)
```
Fixes#4673
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Read Chrome’s `Local State` in `list_chrome_profiles()` using UTF-8 to
prevent garbled profile names on Windows with non-UTF-8 locales.
Non-ASCII names (e.g., Chinese) now display correctly.
<sup>Written for commit 9c314e626e.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
On Windows with a non-UTF-8 default locale (e.g. Chinese GBK/CP936),
open() without an explicit encoding uses the system code page. Chrome's
Local State file is always UTF-8, so profile names containing non-ASCII
characters (e.g. Chinese '用户1') are decoded as mojibake.
Fixes#4673
…k guidance
When `browser-use connect` fails to discover a running Chrome, the error
now points to the correct `chrome://inspect/#remote-debugging` URL. The
SKILL.md also guides agents to prompt users with two options: enable
remote debugging or use managed Chromium with a Chrome profile.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes the connect failure UX by pointing to the correct Chrome remote
debugging page and adding clear fallback steps. The error now links to
`chrome://inspect/#remote-debugging`, and SKILL.md guides users to
either enable remote debugging or use managed Chromium with their Chrome
profile.
<sup>Written for commit d0fbf4c580.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
When `browser-use connect` fails to discover a running Chrome, the error
now points to the correct `chrome://inspect/#remote-debugging` URL. The
SKILL.md also guides agents to prompt users with two options: enable
remote debugging or use managed Chromium with a Chrome profile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary
- pin the privileged stale workflow action to the immutable commit
behind `actions/stale@v9`
- preserve current behavior by keeping the same major tag target and
only removing tag-retarget drift
- leave existing workflow permissions and stale policy settings
unchanged
## Why
This scheduled workflow runs with `issues: write` and `pull-requests:
write`, so pinning the marketplace action to a commit reduces
supply-chain drift without changing workflow behavior.
## Validation
- `git diff --check`
- `python -c "from pathlib import Path; import yaml;
yaml.safe_load(Path('.github/workflows/stale-bot.yml').read_text(encoding='utf-8'));
print('yaml-parse-ok')"`
- local PC Control review-coder queued against the staged diff; it
remained running during push, so I treated it as degraded evidence and
completed a bounded manual preflight on the one-line workflow change
## Notes
- This intentionally stays scoped to one workflow line.
- I did not change permissions, timing, or stale policy behavior.
- `uv` / `pre-commit` and `actionlint` were not available on this
Windows shell, so I did not claim those checks ran locally.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Pin `actions/stale` in the scheduled workflow to the immutable v9 commit
to eliminate tag drift and reduce supply-chain risk. Behavior,
permissions, schedule, and stale policy remain unchanged.
<sup>Written for commit b1f755d509.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
thousands of users have attempted to use close, so why not add it
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds `close()` to `BrowserSession` as an alias for `stop()`, so
`session.close()` cleanly stops the session and matches common APIs.
<sup>Written for commit 76604913ad.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Resolves#4610
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Prefer the `playwright`-bundled Chromium over system Chrome by default
to make local launches consistent across machines and CI. Also switches
installation to `uvx playwright install chromium`.
- **Refactors**
- Reordered search priority: channel-specific (non-default) ->
Playwright Chromium -> system Chrome -> other native browsers ->
Playwright headless-shell.
- Unified pattern ordering to always prioritize the target browser
group, then fall back to others.
- Switched install command and error messages from `chrome` to
`chromium`.
<sup>Written for commit 03e2bc4da8.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Gracefully handle MCP stdio disconnections by catching BrokenPipeError,
preventing crashes and shutting down the server cleanly.
- **Bug Fixes**
- Wrap `server.run` in a `try/except BrokenPipeError`.
- Log a warning and exit cleanly when the MCP client disconnects.
<sup>Written for commit df4e2f9f15.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Resolves#4620
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fix pagination button detection to prefer semantic labels ("first",
"last") over shared glyphs ("«", "»") across sites. Removed those glyphs
from first/last patterns and reordered checks so first/last win before
next/prev, treating "«" and "»" only as prev/next fallbacks.
<sup>Written for commit 9ad4c63cdb.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Resolves#4609
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes#4609 by preventing substring leaks during sensitive data
redaction. Redaction now replaces longer secrets first and uses shared
utils for consistent behavior.
- **Bug Fixes**
- Redact longest matches first to avoid partial/substring leaks.
- Support both legacy flat and domain-scoped `sensitive_data` formats.
- Apply consistent redaction across message manager and views.
- **Refactors**
- Added `collect_sensitive_data_values` and `redact_sensitive_string` in
`browser_use/utils.py`.
- Replaced inline redaction logic in
`browser_use/agent/message_manager/service.py` and
`browser_use/agent/views.py`.
<sup>Written for commit 65f87b7fca.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Fixes#4626
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Replace deprecated asyncio.get_event_loop/run_until_complete with
asyncio.run() in the CLI to restore Python 3.14 compatibility. Fixes
#4626 and prevents runtime errors in the `doctor` and `tunnel` commands.
<sup>Written for commit 99a8674214.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Resolves#4631
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Clears the DOM watchdog cache after scrolls to prevent stale extraction
data and ensure DOM reads reflect the current page state. Addresses
#4631 where post-scroll data could be outdated.
- **Bug Fixes**
- Clear `_dom_watchdog` cache after both element-target and CDP gesture
scrolls.
- Wait ~200ms before clearing after element-target scroll so the DOM can
settle.
<sup>Written for commit 60e7767228.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
## Summary
- Add UTM tracking params to all cloud-bound links across README, CLI,
and error messages — enables measuring which OSS surfaces drive cloud
sign-ups
- Rewrite README "Open Source vs Cloud" section: position cloud browsers
as the recommended pairing for OSS users, remove separate "Use Both"
section
- Rewrite error messages for `use_cloud=True` and `ChatBrowserUse()` to
clearly state what is wrong and what to do next (not vague
"authentication failed" messages)
- Add missing URLs to dead-end errors: invalid API key now links to key
page, insufficient credits now links to billing page
- Add cloud browser nudge on captcha detection (`logger.warning`)
- Add cloud browser nudge on local browser launch failure (Chromium not
installed, etc.)
- Fix pre-existing pyright error with `readline.add_history` on Windows
## Changes by file
**README.md** — UTM params on 8 cloud links, "Use Both" section folded
into "Use Open Source" with cloud browsers as recommended pairing
**browser_use/agent/service.py** — Captcha nudges rewritten as
`logger.warning` with cloud URL + UTM
**browser_use/browser/cloud/cloud.py** — Error messages rewritten:
"BROWSER_USE_API_KEY is not set" / "is invalid" + UTM on URLs
**browser_use/browser/session.py** — Auth error re-raise simplified (no
rewrap), new nudge on local browser launch failure
**browser_use/llm/browser_use/chat.py** — Error messages rewritten,
added URL to 401 (invalid key) and billing URL to 402 (insufficient
credits)
**browser_use/cli.py** — UTM params added to all cloud URLs, fix
`readline.add_history` pyright error on Windows by using `getattr` at
import time
**browser_use/init_cmd.py** — UTM param added to API key URL
**browser_use/skill_cli/commands/cloud.py** — UTM param added to API key
URL
## UTM scheme
| utm_source | utm_medium | Where |
|---|---|---|
| `github` | `readme` | README links |
| `oss` | `cli` | CLI init/TUI messages |
| `oss` | `use_cloud` | `Browser(use_cloud=True)` errors |
| `oss` | `chat_browser_use` | `ChatBrowserUse()` errors |
| `oss` | `captcha_nudge` | Runtime captcha detection |
| `oss` | `browser_launch_failure` | Local browser launch failure |
- Add UTM params to all cloud-bound links across README, CLI, and error messages
- Rewrite README Open Source vs Cloud section: position cloud browsers as
recommended pairing for OSS users, remove separate Use Both section
- Rewrite error messages for use_cloud=True and ChatBrowserUse() to clearly
state what is wrong and what to do next
- Add missing URLs: invalid API key now links to key page, insufficient
credits now links to billing page
- Add cloud browser nudge on captcha detection (logger.warning)
- Add cloud browser nudge on local browser launch failure
## Summary
- Updates `browser-use-sdk` dependency from `2.0.15` to `3.4.2`
- All existing imports verified working with the new version
## Test plan
- [x] `uv sync` succeeds
- [x] All SDK imports (`AsyncBrowserUse`, type imports) verified working
- [ ] CI tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Upgrade `browser-use-sdk` from 2.0.15 to 3.4.2 and align our code with
v3 API changes. No behavior changes.
- **Dependencies**
- Updated `browser-use-sdk` to `3.4.2` in `pyproject.toml`.
- **Refactors**
- Moved to new top‑level SDK imports (`AsyncBrowserUse`,
`ExecuteSkillResponse`, `SkillListResponse`, `ParameterSchema`,
`SkillResponse`).
- Handle UUID skill IDs from the SDK by casting to and comparing as
`str`.
- On error, return `ExecuteSkillResponse` with `result=None`,
`stderr=None`, and `latencyMs=None` for schema compatibility.
<sup>Written for commit 1a94f96ce9.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Guard `mcp.server.stdio` startup against missing stdin. When stdin is
absent, raise a clear RuntimeError so the process fails fast instead of
producing ambiguous startup errors.
<sup>Written for commit 83317179cb.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes runtime crashes when patching LLM ainvoke on Pydantic-backed
models by using `object.__setattr__` to assign `tracked_ainvoke`. This
bypasses Pydantic attribute validation and keeps the tracking wrapper
working.
<sup>Written for commit 3242d9dbc2.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
## Fixes#4463
### Problems
**1. `AgentHistoryList.load_from_dict` — KeyError on missing keys**
The current implementation uses direct dict subscript access that
crashes when loading history saved with an older browser-use version or
when a step entry is missing expected keys:
```python
for h in data['history']: # KeyError if 'history' key absent
if h['model_output']: # KeyError if 'model_output' absent
if 'interacted_element' not in h['state']: # KeyError if 'state' absent
```
**2. `AgentHistoryList.final_result()` — IndexError on empty result
list**
```python
if self.history and self.history[-1].result[-1].extracted_content:
# ^^^^^^^^^^^^^^^^ IndexError when result == []
```
Every sibling method (`is_done`, `is_successful`, `judgement`,
`is_judged`, `is_validated`) already guards with
`len(self.history[-1].result) > 0`. `final_result` was the only one
missing this check.
### Fix
- **`load_from_dict`**: use `data.get('history', [])` and `h.get(key)`
instead of direct subscript access, matching standard defensive Python
dict usage.
- **`final_result`**: add `len(self.history[-1].result) > 0` guard
before accessing `result[-1]`, consistent with all other methods in the
class.
### Changes
Only `browser_use/agent/views.py` is modified (14 additions, 8
deletions).
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Prevent crashes when loading agent history with missing fields and when
reading the final result on empty lists. This makes `AgentHistoryList`
safer and backward-compatible with legacy or partial histories.
- **Bug Fixes**
- `load_from_dict`: use `data.get('history', [])` and `h.get()` for safe
access; initialize missing `state` and default
`state.interacted_element` to `None`; validate `model_output` only when
it’s a dict.
- `final_result`: check `len(self.history[-1].result) > 0` before
indexing; return `None` when empty or no extracted content, consistent
with other methods.
<sup>Written for commit 6129ceb0b0.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
In response to #4214
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Revamped the LLM model docs with a quick-reference table, use‑case
recommendations, and clearer provider setup. Adds native coverage for
more providers, updates examples, and improves env/installation
guidance.
- **Refactors**
- Added Quick Reference (provider → class → env) and use‑case picks
based on benchmarks.
- Renamed to “Browser Use Cloud,” updated pricing/models, and listed
`browser-use/bu-30b-a3b-preview`.
- Expanded provider docs: DeepSeek, Mistral, Cerebras, OpenRouter,
LiteLLM; improved OpenAI/Anthropic/Gemini/Azure/Bedrock/Vercel/OCI
guidance with links.
- Standardized examples to `Agent` + `Chat*` classes and noted key env
vars.
- Moved OpenRouter out of OpenAI-compatible section; trimmed/reorganized
OpenAI-compatible examples.
- Minor fixes: `GOOGLE_API_KEY` deprecation note, Anthropic coordinate
clicking note, “LangChain” capitalization.
<sup>Written for commit c1151715d3.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
## Summary
- **Root cause**: The daemon accepted any connection on its socket
without authentication. On Windows the port is deterministic
(`adler32(session)`-derived), so any local process could connect and
dispatch the `python` action, executing arbitrary code via
`eval()`/`exec()` as the daemon owner. On Unix the socket may be
world-writable under a permissive umask.
- **Fix**: On `Daemon.run()`, generate a `secrets.token_hex(32)` token,
write it atomically to `~/.browser-use/{session}.token` with `chmod
0o600`. Validate every incoming request with `hmac.compare_digest`
before dispatching. Delete the token file on shutdown.
- **Client**: `send_command()` in `main.py` now reads the token file and
attaches it to every request. Falls back to `''` for old daemons
(no-op).
## Test plan
- [ ] Start daemon, run `browser-use python "1+1"` — should work
normally
- [ ] Send a raw request without token to socket — should get
`{"success": false, "error": "Unauthorized"}`
- [ ] Unauthenticated `shutdown` action should be ignored (daemon stays
up)
- [ ] After daemon stops, `~/.browser-use/default.token` should be
deleted
- [ ] All CI tests pass (`uv run pytest -vxs tests/ci`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Secure the daemon socket with a per-session auth token to block
unauthorized local connections and arbitrary code execution via the
`python` action. The CLI now reads the token and includes it with each
command.
- **Bug Fixes**
- Generate a session token in `Daemon.run()` (`secrets.token_hex(32)`);
write it atomically to `~/.browser-use/{session}.token` via a `*.tmp`
file created with `0o600`, then replace; raise if write fails; delete on
shutdown.
- Validate every request using `hmac.compare_digest`; unauthorized calls
return `{"success": false, "error": "Unauthorized"}` and `shutdown` is
honored only for authorized, successful requests.
- Client `send_command()` reads the token and sends it as `token`; falls
back to `''` for older daemons.
<sup>Written for commit ca2185ba61.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
- Use os.open() with mode 0o600 instead of write-then-chmod to eliminate
the permission race window where the temp file is briefly world-readable.
- Raise instead of warn when token file write fails: a daemon that cannot
persist its auth token is permanently unauthorized for all clients, so
failing fast is correct (identified by cubic).