- Rm extract (dead, always errored)
- Rm sessions (confusable with cloud session)
- Rm server (overlaps with close)
- Rename type to type-text to distinguish from input
- Clarify close vs session stop and cookies vs profile cookies in help text and docs
- Auto-detect browser mode from existing session metadata instead of assuming default; error on explicit mode conflicts
- Make --json work after subcommands (not just before) via argparse SUPPRESS
- Deduplicate utils between main.py and utils.py (lazy portalocker import)
- Extract inline command handlers from main() into standalone functions
- Fix get_available_modes() to match is_mode_available() LOCAL_MODES logic
- Fix setup validation icons for boolean results
- Delete unused protocol.py
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Add support for custom HTTP headers via CDP on every request. Adds
Browser.set_extra_headers() and a watchdog-based example that applies
headers to new tabs and on focus changes.
- **New Features**
- Browser.set_extra_headers(headers, target_id=None) to set per-target
headers; automatically enables the Network domain; headers fully replace
previously set extras.
- Example at examples/browser/custom_headers.py uses
CustomHeadersWatchdog to listen to TabCreatedEvent and
AgentFocusChangedEvent to auto-apply headers to new tabs and when
switching tabs; includes a manual apply to the current tab; verified
with https://httpbin.org/headers.
<sup>Written for commit 75431077fb.
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 stale actions after cookie overlay dismissal by making evaluate
end the action sequence and clarifying Shadow DOM click guidance.
- **Bug Fixes**
- Marked evaluate as terminates_sequence to skip queued actions after
DOM mutations.
- Updated guidance: evaluate is now a page-changing action (always
last). For Shadow DOM, click indexed elements with click(index); only
use evaluate for non-indexed elements.
- Added test asserting evaluate terminates the sequence.
<sup>Written for commit 0184d8d5ae.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Fixes `UnboundLocalError` for `action_name` in `multi_act()` by ensuring
it's always defined before potential exception handling.
The `_check_stop_or_pause()` method was called before `action_name` was
assigned. If `_check_stop_or_pause()` raised an `InterruptedError`, the
subsequent `except` block would attempt to reference `action_name`,
which was not yet set, leading to an `UnboundLocalError`. This PR moves
the `action_name` extraction before the `try` block to guarantee its
definition.
---
[Slack
Thread](https://browser-use.slack.com/archives/C091NM0S79T/p1771375804927399?thread_ts=1771375804.927399&cid=C091NM0S79T)
<p><a
href="https://cursor.com/background-agent?bcId=bc-4f1e4564-d1fb-5ff1-8760-d94d7d2b205c"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> <a
href="https://cursor.com/agents?id=bc-4f1e4564-d1fb-5ff1-8760-d94d7d2b205c"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a></p>
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes an UnboundLocalError in multi_act by defining action_name before
potential interruptions, making exception handling and logging reliable.
Also reuses action_data for the 'done' check.
- **Bug Fixes**
- Compute action_data and action_name before the try block so the except
handler can safely reference them if _check_stop_or_pause() interrupts.
- Use the same action_data to validate 'done' when multiple actions are
provided.
<sup>Written for commit e03a18935e.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Small control-flow refactor limited to action execution/logging;
behavior should be unchanged except for avoiding a crash in rare
interruption/error paths.
>
> **Overview**
> Fixes an `UnboundLocalError` in `Agent.multi_act()` by computing
`action_data`/`action_name` *before* entering the per-action `try`
block, so error handling and demo-mode logging can always reference the
action being executed.
>
> Also reuses the precomputed `action_data` for the multi-action
`done`-action guard, avoiding a second `model_dump()` and keeping the
check consistent with the logged action name.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e03a18935e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Move action_name extraction before the try block so it's always
defined when the except block references it. Previously, if
_check_stop_or_pause() raised InterruptedError before action_name
was assigned, the exception handler would fail with:
UnboundLocalError: cannot access local variable 'action_name'
where it is not associated with a value
Also reuse the action_data dict for the 'done' action check since
it's now available earlier in the loop.
Co-authored-by: Magnus Müller <MagMueller@users.noreply.github.com>
The done_output field has max_length=100000, but agent.history.final_result()
can return arbitrarily long strings (e.g., large data extractions). This caused
Pydantic validation errors when constructing UpdateAgentTaskEvent at the end of
agent.run(), crashing the entire run even though the task itself succeeded.
Truncate done_output to MAX_STRING_LENGTH before passing to the constructor.
Co-authored-by: Magnus Müller <MagMueller@users.noreply.github.com>
Stop and reset the EventBus on keep_alive close so asyncio exits cleanly
and sessions can be reused. Fixes#3791.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fix keep_alive sessions hanging after agent.run() by stopping the
BrowserSession EventBus on close and resetting internal refs so asyncio
exits cleanly and sessions can be reused. Fixes#3791.
- **Bug Fixes**
- Stop EventBus on close when keep_alive=True (clear=False, 1s timeout
via TIMEOUT_BrowserSessionEventBusStopOnAgentClose).
- Null out event_queue and _on_idle to prevent lingering tasks.
<sup>Written for commit e9c7061187.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
- Add max_clickable_elements_length parameter to Agent.__init__ with default 40000
- Add max_clickable_elements_length to AgentSettings
- Pass parameter through MessageManager to AgentMessagePrompt
- Bump version to 0.11.10a2
Co-authored-by: Magnus Müller <MagMueller@users.noreply.github.com>
Fixes three issues in download monitoring:
1. **Prevent false PDF downloads from fetch/XHR**: Skip programmatic
requests (fetch() and XHR) since real browsers don't download PDFs
loaded this way
2. **Fix case-sensitive header lookup**: Normalize HTTP headers to
lowercase for case-insensitive Content-Disposition detection
3. **Fix race condition with blob downloads**: Capture initial file list
before any processing to detect fast blob: downloads
Fixes#3515
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Improves download monitoring accuracy by preventing false PDF events,
capturing fast blob downloads, and avoiding stale cache skips. Fixes
#3515.
- **Bug Fixes**
- Skip Fetch/XHR responses so programmatic PDF loads aren’t treated as
downloads
- Normalize headers to lowercase and ignore stale session cache entries;
allow retries if background download fails
- Capture initial downloads snapshot and detect new files when filePath
is missing to handle fast blob downloads
<sup>Written for commit 6db6678a83.
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
Prepare preview release 0.11.10a1 by updating the package version in
pyproject.toml. This enables publishing an alpha build for early
testing.
<sup>Written for commit 90e6143ff8.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Changes install.sh to clone from github.com/browser-use/browser-use@main
by default instead of installing from PyPI. Custom branches and repos
can still be specified via BROWSER_USE_BRANCH and BROWSER_USE_REPO
environment variables.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Update install.sh to install browser-use from
github.com/browser-use/browser-use@main by default instead of PyPI. You
can still install a specific branch or fork by setting
BROWSER_USE_BRANCH and BROWSER_USE_REPO.
<sup>Written for commit 8af57fa19a.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Changes install.sh to clone from github.com/browser-use/browser-use@main
by default instead of installing from PyPI. Custom branches and repos
can still be specified via BROWSER_USE_BRANCH and BROWSER_USE_REPO
environment variables.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Streamlines installation and expands the CLI with mode-based setup,
health checks, unified cloud/local management, robust tunnels, and
SDK‑verified LLM selection. Also includes minor CI lint fixes in tests.
- **New Features**
- Installer: mode-based install (--remote-only/--local-only/--full),
writes install-config.json, optional API key, cross‑platform cloudflared
install (Windows via winget/direct), venv handling, CI-friendly
non‑interactive flow, gum TUI with Windows fixes, auto PATH config
(registry + PowerShell profile), unified install URL; script moved to
browser_use/skill_cli/install.sh (public URL unchanged).
- CLI: setup and doctor (prints detailed check results); unified profile
management (local/cloud via SDK); cloud task/session ops (including
session create/share), new filters/flags (--session, --metadata,
--secret, --allowed-domain, --skill-id, --structured-output, --judge,
--judge-ground-truth, --start-url); token‑efficient monitoring
(--compact/--verbose/--last/--reverse/--step) with durations; LLM
selection via SDK‑verified lists with updated defaults (o3,
claude-sonnet-4-0, gemini-flash-latest).
- Remote tasks: async by default; --wait prints task_id/session_id
immediately, shows a simple progress indicator, forwards all cloud‑only
flags, polls without blocking, and handles Ctrl+C without canceling the
task.
- Routing/safety: local vs remote agent routing with no client‑side
defaults; metadata detects browser‑mode mismatches with clear guidance;
local mode doesn’t require a Browser‑Use API key.
- Tunnels: fully decoupled from sessions, persist across CLI exit, stop
--all supported, invoked without starting a session; implementation
consolidated into a single tunnel.py module for reliability.
- Cross‑platform fixes: Windows server stability (127.0.0.1 binding, PID
checks, no console window, port reuse); file‑lock based session server
lifecycle via portalocker to prevent race conditions/orphaned processes
(with stale cleanup and longer socket timeout); macOS PATH updates; CI
adds install.sh tests across macOS/Linux/Windows; refactor to
cloud_task.py/cloud_session.py with a shared SDK client; ruff fixes;
expanded docs and skills; minor CI lint cleanups in tests.
- **Migration**
- Installation modes: requesting an unavailable mode errors with
reinstall guidance; messages now suggest --remote-only in addition to
--full and --local-only (use install.sh with
--full/--remote-only/--local-only).
- Remote tasks return immediately; use --wait to block. With --wait, IDs
print immediately and Ctrl+C won’t cancel the remote task.
- Tunnels are independent; browser-use close no longer stops them. Use
browser-use tunnel stop <port> or --all.
- In remote workflows, --browser remote is only needed on the first
command in a session.
- LLM model selection now uses SDK‑verified lists; the browser-use/*
pattern is no longer supported. Use bu-latest/bu-1-0/bu-2-0 or provider
model IDs.
<sup>Written for commit bff682fee6.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
- Remove unused `import os` from test_doctor_command.py
- Add assert for proc.stdout in test_server_locking.py to satisfy pyright
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Removed all unittest.mock.patch usage
- Tests now use real functions and verify structure/behavior
- Use monkeypatch for env vars (acceptable for test isolation)
- Tests adapt to system state (cloudflared may or may not exist)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously only suggested --full, but --remote-only is a lighter-weight
option when users only need cloud browser support.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The session server had a race condition where concurrent CLI calls could
spawn multiple server processes. The old server would be orphaned when
the new one overwrote the PID file and deleted its socket.
Changes:
- Add flock-based locking using portalocker (already a dependency)
- Server acquires exclusive lock before writing PID file
- CLI checks both is_server_running() AND is_session_locked()
- Add kill_orphaned_server() to clean up servers with PID but no lock
- Increase socket timeout from 0.1s to 0.5s for reliability
- cleanup_session_files() now removes .lock and .meta files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename tunnel_manager.py to tunnel.py
- Remove dead code: commands/tunnel.py (session-scoped implementation never reached)
- Remove TunnelInfo dataclass and tunnels field from sessions.py
- Remove tunnel routing from server.py (unreachable code path)
- Update imports in main.py, doctor.py, setup.py, and tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove the interactive confirmation prompt for Windows PATH
configuration. Now behaves consistently with macOS/Linux which
automatically add to shell rc files without prompting.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update all references from browser-use.com/install.sh to
browser-use.com/cli/install.sh across documentation and code.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reverts changes to browser_use/llm/browser_use/chat.py to keep
CLI changes isolated. Browser-use models are now hardcoded in
agent.py instead of importing from chat.py.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Forward all cloud-only flags in _handle_remote_run_with_wait
Previously only forwarded 6 params; now includes session_id, vision,
metadata, secrets, allowed_domains, skill_ids, structured_output,
judge, judge_ground_truth, plus session creation for profile/proxy
- Use asyncio.to_thread for blocking SDK call in poll_until_complete
Prevents client.tasks.get_task from blocking the event loop while
polling for task completion
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use asyncio.to_thread() for poll_until_complete to prevent time.sleep()
from blocking the async event loop when --wait is used.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>