Commit Graph

9168 Commits

Author SHA1 Message Date
Saurav Panda
9a09c4d7dc fix(cdp): timeout-wrap CDPClient.send_raw to break silent WebSocket hangs
cdp_use.CDPClient.send_raw awaits a future that only resolves when the
browser sends a response with a matching message id. There is no timeout
on that await. Against the cloud browser service, the failure mode we
observed is: WebSocket stays alive at the TCP/keepalive layer (proxy
keeps pong-ing our pings), but the browser upstream is dead / unhealthy
and never sends any CDP response. send_raw's future never resolves, and
every higher-level timeout in browser-use (session.start's 15s connect
guard, agent.step_timeout, tools.act's action timeout) relies on
eventually getting a response — so they all wait forever too.

Evidence from a 170k-task collector run: 1,090 empty-history traces,
100% hit the 240s outer watchdog, median duration 582s, max 2214s, with
cloud HTTP layer clean throughout (all 200/201). One sample showed
/json/version returning 200 OK and then 5 minutes of total silence on
the WebSocket before forced stop — classic silent-hang.

Fix: add TimeoutWrappedCDPClient, a thin subclass of cdp_use.CDPClient
that wraps send_raw in asyncio.wait_for(timeout=cdp_request_timeout_s).
Any CDP method that doesn't respond within the cap raises plain
TimeoutError, which propagates through existing `except TimeoutError`
handlers in session.py / tools/service.py. Uses the same defensive env
parse pattern as BROWSER_USE_ACTION_TIMEOUT_S — rejects empty /
non-numeric / nan / inf / non-positive values with a warning fallback.

Default is 60s: generous for slow operations like Page.captureScreenshot
or Page.printToPDF on heavy pages, but well below the 180s step timeout
and any typical outer watchdog. Override via BROWSER_USE_CDP_TIMEOUT_S.

Wired into both CDPClient construction sites in session.py (initial
connect + reconnect path). All 17 existing real-browser tests
(test_action_blank_page, test_multi_act_guards) still pass.
2026-04-20 17:40:32 -07:00
Saurav Panda
a9a7201d8d Merge branch 'main' into fix/action-timeout-hang 2026-04-20 17:02:08 -07:00
Saurav Panda
a97ba48345 fix(agent): timeout _execute_initial_actions so empty-history hangs cannot happen
The main execution loop already wraps _execute_step with asyncio.wait_for
using settings.step_timeout (default 180s). But _execute_initial_actions,
which runs before the main loop, is unwrapped — if it hangs (e.g. the
first navigate stalls on a silent CDP WebSocket before the per-action
timeout can catch it), the agent blocks indefinitely without ever
entering the main loop. No step gets recorded, history stays empty, and
any outer watchdog eventually kills the run with zero diagnostic data.

Wrap _execute_initial_actions with the same step_timeout. On timeout,
record the failure in state.last_result / consecutive_failures and fall
through to the main execution loop so the agent can still attempt to
recover. InterruptedError (from an interrupting callback) is still
swallowed silently — same contract as before.

Paired with the per-action asyncio.wait_for added in tools/service.py,
this closes the last unprotected path in the pre-main-loop flow.
2026-04-20 16:39:22 -07:00
Saurav Panda
8ad6f896ae Add record start/stop CLI command for session video capture (#4710)
Closes #4533.

## Summary

Adds `browser-use record start <path>` / `record stop` / `record status`
to capture the current session as an MP4 via CDP screencasting — all the
underlying machinery (`Page.startScreencast`, `VideoRecorderService`)
already existed in the repo; this just exposes it on the CLI.

- `RecordingWatchdog` gains a public `start_recording(path, size?,
framerate?)` / `stop_recording() -> Path` / `is_recording` API. The
existing `BrowserConnectedEvent`/`BrowserStopEvent` handler is
refactored to use it, so profile-driven recording
(`record_video_dir=...`) is unchanged.
- New `record` subcommand wired through argparse (`skill_cli/main.py`),
the daemon dispatch allowlist, and `skill_cli/commands/browser.py`.
Works with `--session NAME` via the existing named-daemon
infrastructure. `record stop` prints the saved file path so it can be
captured programmatically (as requested in the issue).
- `CLIBrowserSession` intentionally skips watchdogs; the handler lazily
attaches `RecordingWatchdog` on first `record start` so non-recording
sessions pay no cost.
- Output is `.mp4` (libx264) — matches the existing encoder. Gated
behind the existing `browser-use[video]` optional extra; the CLI returns
a helpful error if deps are missing.

## Example

```bash
browser-use --session demo record start /tmp/demo.mp4
browser-use --session demo open https://example.com
browser-use --session demo click 3
browser-use --session demo record stop
# /tmp/demo.mp4
```

## Test plan

- [x] `uv run pytest -vxs tests/ci/test_action_record.py` — 6 new tests,
all pass (~23s). Covers: full start/stop cycle against a real headless
browser (produces a decodable MP4), double-start rejection,
stop-without-start returns None, profile-driven flow unchanged, argparse
parsing, dispatch registration.
- [x] `uv run pyright` on changed files — clean.
- [x] `uv run ruff check` / `ruff format` — clean.
- [x] Live end-to-end CLI smoke test: `record start` → `open` → `record
stop` produced a valid ~11 KB MP4.
- [ ] CI green.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds `record start/stop/status` to the `browser-use` CLI to capture the
current session as an `.mp4` via CDP screencasting, with simple
start/stop APIs on `RecordingWatchdog` and reliable shutdown that
finalizes recordings.

- **New Features**
- `browser-use record start <path>`, `stop`, and `status`; `start`
supports `--framerate`, `stop` prints the saved path, and `status`
returns path, framerate, and size.
- Works with `--session NAME`; lazily attaches `RecordingWatchdog` so
non-recording sessions have no overhead.
- Outputs `.mp4` (libx264) via the existing encoder; gated behind
`browser-use[video]` with a clear error if missing.

- **Bug Fixes**
- `on_BrowserConnectedEvent` degrades gracefully when recording cannot
start (e.g., missing `browser-use[video]` or undetectable viewport) so
sessions still launch with `record_video_dir` set.
- Daemon shutdown now awaits `stop_recording()` (no timeout) and
finalizes any in-progress recording, preventing truncated MP4s.

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

<!-- End of auto-generated description by cubic. -->
2026-04-20 15:49:35 -07:00
Saurav Panda
d2985dcab9 review: reject non-finite timeouts + restore module after reload tests
Two more issues from automated review on #4711:

1. (P2, Codex) float() accepts 'nan' and 'inf' — both parse successfully
   and bypass the fallback path. 'nan' makes asyncio.wait_for time out
   immediately for every action; 'inf' effectively disables the hang
   guard. Extracted the parse into _parse_env_action_timeout() which
   rejects non-finite and non-positive values (including 0 and negatives)
   with a warning + fallback.

2. (P2, Cubic) The previous reload test left browser_use.tools.service
   pinned at _DEFAULT_ACTION_TIMEOUT_S=45.0 (the last monkeypatch value),
   which would leak into any later test in the same worker. Added a
   _restore_service_module fixture that pops the env var and reloads
   cleanly on teardown.

Expanded test coverage to include 'nan', 'NaN', 'inf', '-inf', '0', '-5'
alongside the existing '' / 'abc' cases — all fall back to 180s.
2026-04-20 15:45:36 -07:00
Saurav Panda
44f7ead5cd Drop timeout on recording finalize in daemon shutdown
`asyncio.wait_for(stop_recording(), timeout=5.0)` could expire while the
ffmpeg encoder was still flushing, leading the daemon's subsequent
`os._exit(0)` to kill the executor thread mid-write and leave the exact
truncated MP4 this hook was meant to prevent. `stop_recording()` already
offloads the blocking close to an executor, so awaiting it directly is
safe — and if it genuinely hangs, a stuck daemon is a clearer failure
signal than silent video corruption.

Verified end-to-end: start recording → `open` → `close` (no explicit
`record stop`) now produces a decodable MP4 with the captured frames.
2026-04-20 15:38:34 -07:00
Saurav Panda
1488a39b7f address PR review: raise default cap + tolerate bad env values
Two issues flagged by automated review on #4711:

1. (P1, Codex) The 90s default was *below* the extract action's intentional
   120s page_extraction_llm.ainvoke timeout (tools/service.py:1096,1172).
   Slow-but-valid extractions against large pages would be truncated into
   timeout errors — a regression. Raised default to 180s, which sits above
   that 120s inner cap with grace.

2. (P2, Cubic + Codex) float(os.getenv('BROWSER_USE_ACTION_TIMEOUT_S', '90'))
   ran at import time. An empty or non-numeric value (common with env
   templating) raised ValueError and prevented browser_use.tools.service
   from importing at all — turning a config typo into a process-wide
   startup failure. Wrapped in try/except with a warning and fallback to
   the hardcoded 180s default.

Tests:
- test_default_action_timeout_accommodates_extract_action — pins the
  default >= 150s so future edits can't silently regress extract.
- test_malformed_env_timeout_does_not_break_import — reloads the module
  with empty / non-numeric env values and asserts it falls back cleanly,
  plus verifies a valid numeric env value still takes effect.
2026-04-20 15:36:15 -07:00
Saurav Panda
ce81ada89a fix(tools): enforce per-action timeout to prevent hung event handlers
Individual CDP calls like Page.navigate() have their own 20s timeouts, but
the surrounding event-bus plumbing (await event, event_result()) does not.
When a cloud browser's CDP WebSocket goes silent mid-session, agent handlers
hang indefinitely — agents never emit a step, any outer watchdog eventually
fires, and the run returns with zero history.

Observed in practice: a 170k-task collector run produced 1,090 empty-history
traces (21% of output). 100% hit the 240s outer watchdog; median 582s, max
2214s. Cloud HTTP layer was clean (all 200/201) — hang was entirely in CDP.

Wrap registry.execute_action in asyncio.wait_for with a configurable per-
action cap (default 90s, BROWSER_USE_ACTION_TIMEOUT_S env var or
tools.act(action_timeout=...)). On timeout, the action returns
ActionResult(error=...) so the agent can record the step and recover.

New tests/ci/test_action_timeout.py covers both hung and fast handlers.
Existing tools.act tests (test_multi_act_guards, test_action_blank_page)
still pass.
2026-04-20 15:22:30 -07:00
Saurav Panda
132756dabb Address PR review feedback for record start/stop
- `on_BrowserConnectedEvent` now catches `RuntimeError` from
  `start_recording()` so sessions with `record_video_dir` configured but
  missing `[video]` extras (or a viewport that can't be sized) keep
  starting — prior graceful-degradation behavior is restored.
- Lazy `RecordingWatchdog` in the CLI handler now calls
  `attach_to_session()`, so `AgentFocusChangedEvent` / `BrowserStopEvent`
  handlers are wired correctly if the session dispatches them.
- Daemon shutdown finalizes any in-progress recording before tearing the
  browser down, preventing truncated MP4s on `close`, idle timeout, or
  signal-driven exit.
- Added regression test that monkeypatches `start_recording` to raise and
  asserts `on_BrowserConnectedEvent` swallows it without breaking startup.
2026-04-20 15:11:49 -07:00
Saurav Panda
b1d933258c Add record start/stop CLI command for session video capture
Closes #4533.

- `RecordingWatchdog` gains public `start_recording(path, size?, framerate?)`,
  `stop_recording() -> Path`, and `is_recording`; the existing
  `BrowserConnectedEvent`/`BrowserStopEvent` path is refactored to use them,
  so profile-driven recording behavior is unchanged.
- `browser-use record start <path>` / `record stop` / `record status`
  subcommands wired through argparse, daemon dispatch, and the browser
  command handler. `record stop` prints the saved file path so it can be
  captured programmatically, matching the issue's requested UX. Works with
  `--session NAME` via the existing named-daemon infrastructure.
- The CLI's `CLIBrowserSession` intentionally skips watchdogs; the handler
  lazily instantiates `RecordingWatchdog` on first `record start` so CLI
  recording doesn't pay the watchdog-setup cost for non-recording sessions.
- Output format is `.mp4` (libx264) since that's what the existing
  `VideoRecorderService` encodes; optional dependency gate is unchanged
  (`pip install "browser-use[video]"`).
- New `tests/ci/test_action_record.py` exercises the full stack against a
  real headless browser + `pytest-httpserver`, verifying decodable MP4
  output, double-start rejection, stop-without-start no-op, that the
  existing `profile.record_video_dir` flow still works, and the argparse /
  dispatch wiring.
2026-04-20 14:50:52 -07:00
laithrw
702b7352c3 Fix: Catch TypeError during lmnr import to prevent CLI startup crash (#4046) (#4104)
## 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. -->
2026-04-15 18:38:48 -04:00
Laith Weinberger
80f798bc17 drop test 2026-04-15 18:34:03 -04:00
Laith Weinberger
a336bd8a50 Merge remote-tracking branch 'origin/main' into fix/handle-lmnr-type-error-on-import-4046 2026-04-15 18:33:31 -04:00
laithrw
d49d3bd1ef fix input clear fallbacks and clarify clear-then-type behavior (#4692)
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. -->
2026-04-15 17:35:56 -04:00
Laith Weinberger
4476f6e16e fix input clear fallbacks and clarify clear-then-type behavior 2026-04-15 17:31:04 -04:00
laithrw
91d2eff4ac fix: add utf-8 encoding to Local State file read in list_chrome_profiles (#4690)
## 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. -->
2026-04-15 16:09:37 -04:00
laithrw
9c314e626e Merge branch 'main' into fix/local-state-utf8-encoding 2026-04-15 16:06:56 -04:00
voidborne-d
4c2d136de9 fix: add utf-8 encoding to Local State file read in list_chrome_profiles
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
2026-04-15 17:07:51 +00:00
shawn pana
15373241cd improve connect failure UX: fix chrome://inspect link and add fallbac… (#4678)
…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. -->
2026-04-14 15:01:37 -07:00
Shawn Pana
d0fbf4c580 improve connect failure UX: fix chrome://inspect link and add fallback 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.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:23:50 -07:00
Saurav Panda
ef32ed708a ci: pin stale workflow action (#4656)
## 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. -->
2026-04-13 17:34:47 -07:00
grtninja
b1f755d509 Merge branch 'main' into codex/ci-pin-stale-bot-sha 2026-04-12 19:18:48 -04:00
laithrw
5970007d86 close alias for BrowserSession stop (#4665)
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. -->
2026-04-12 17:51:28 -04:00
laithrw
76604913ad Merge branch 'main' into fix/browser-session-close 2026-04-12 17:45:44 -04:00
Laith Weinberger
c1eb87a35f close alias for BrowserSession stop
thousands of users have attempted to use close, so why not add it
2026-04-12 17:43:04 -04:00
laithrw
36438e409e prefer Playwright chromium over sys Chrome by default (#4664)
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. -->
2026-04-12 11:47:27 -04:00
Laith Weinberger
03e2bc4da8 prefer Playwright chromium over sys Chrome by default 2026-04-12 11:41:50 -04:00
laithrw
700833d2c0 fix: handle BrokenPipeError gracefully when MCP client disconnects (#4663)
<!-- 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. -->
2026-04-12 11:37:56 -04:00
Laith Weinberger
df4e2f9f15 fix: handle BrokenPipeError gracefully when MCP client disconnects 2026-04-12 11:34:39 -04:00
grtninja
7775265340 Merge branch 'main' into codex/ci-pin-stale-bot-sha 2026-04-11 22:15:59 -04:00
laithrw
1324a88445 fix pagination classifier to prioritize semantic labels over glyph syms (#4661)
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. -->
2026-04-11 18:39:05 -04:00
Laith Weinberger
9ad4c63cdb fix pagination classifier to prioritize semantic labels over shared glyph symbols 2026-04-11 18:30:46 -04:00
laithrw
24be2f0488 fix sensitive_data redaction order to prevent substring leaks (#4660)
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. -->
2026-04-11 18:21:03 -04:00
Laith Weinberger
65f87b7fca fix sensitive_data redaction order to prevent substring leaks 2026-04-11 18:16:24 -04:00
laithrw
67c2eddf85 fix asyncio.get_event_loop for python 3.14 compat (#4659)
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. -->
2026-04-11 18:13:27 -04:00
Laith Weinberger
99a8674214 fix asyncio.get_event_loop for python 3.14 cli compatibilit 2026-04-11 18:10:12 -04:00
laithrw
184c49713e fix(#4631): clear dom cache after scroll to prevent stale extract data (#4658)
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. -->
2026-04-11 18:09:26 -04:00
laithrw
60e7767228 Merge branch 'main' into issue-4631-review 2026-04-11 18:07:12 -04:00
Laith Weinberger
534eaafe7a clear dom cache after scroll to prevent stale extract data 2026-04-11 18:03:06 -04:00
grtninja
81089417fa ci: pin stale workflow action 2026-04-11 03:13:48 -04:00
Alexander Yue
cd60e6bdd5 Add per-link utm_medium slugs to README cloud links (#4653)
## Summary
- Differentiates the 8 cloud-bound README links by changing
`utm_medium=readme` to `utm_medium=readme-{slug}` (e.g.
`readme-badge-cloud`, `readme-skip-setup`, `readme-faq-captcha`)
- Enables per-placement attribution in PostHog without adding extra UTM
fields
- `utm_source=github` is unchanged so existing filters still work

## Slugs

| Link | `utm_medium` |
|---|---|
| Download stats badge | `readme-badge-downloads` |
| Cloud badge (nav) | `readme-badge-cloud` |
| "Skip the setup" CTA | `readme-skip-setup` |
| Quickstart API key | `readme-quickstart-api-key` |
| Cloud docs | `readme-cloud-docs` |
| Fully-Hosted Cloud Agent | `readme-hosted-agent` |
| CAPTCHA FAQ | `readme-faq-captcha` |
| Production FAQ | `readme-faq-production` |
2026-04-10 15:53:11 -07:00
Alezander9
889ccb81bb Add per-link utm_medium slugs to README cloud links for placement attribution 2026-04-10 15:48:51 -07:00
Alexander Yue
eeb767ef18 Improve OSS-to-cloud conversion: UTM tracking, better error messages, and cloud nudges (#4646)
## 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 |
2026-04-08 22:19:38 -07:00
Alezander9
f8f7be2e90 Fix test assertions to match updated error messages 2026-04-08 22:12:16 -07:00
Alezander9
76569995fd Improve OSS-to-cloud conversion: UTM tracking, better error messages, and cloud nudges
- 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
2026-04-08 22:05:50 -07:00
Saurav Panda
fa17b89846 chore: update browser-use-sdk from 2.0.15 to 3.4.2 (#4644)
## 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. -->
2026-04-08 18:28:17 -07:00
Saurav Panda
1a94f96ce9 fix: update imports for browser-use-sdk 3.4.2 and handle UUID id fields 2026-04-08 17:51:22 -07:00
Saurav Panda
c690af2051 chore: update browser-use-sdk from 2.0.15 to 3.4.2 2026-04-08 17:41:57 -07:00
laithrw
92d3d152a2 fix: guard against missing stdin in MCP stdio server startup (#4642)
<!-- 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. -->
2026-04-08 17:29:24 -04:00
laithrw
83317179cb Merge branch 'main' into fix/mcp-stdin-guard 2026-04-08 17:25:16 -04:00