Commit Graph

9822 Commits

Author SHA1 Message Date
Laith Weinberger 4fa5de9972 quickstart + sync skill 2026-07-14 17:20:30 -07:00
laithrw 703e8356cb remove telemetry (#5165)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Removed in-repo telemetry and delegated CLI event reporting to
`browser_harness` for unified, simpler telemetry. No user-facing
behavior changes; events report via Harness when available.

- **Refactors**
- Removed PostHog sender and `CLITelemetryEvent`; cleaned up exports and
dead code in `telemetry/*`.
- Added `_capture_via_harness` that calls
`browser_harness.telemetry.capture_cli_event` if present, and
`_set_harness_client_env` to set `BH_CLIENT`/`BH_CLIENT_VERSION`.
- Added `_delegated_to_harness` to prevent double-reporting; only
capture when not delegated.
- Simplified CLI telemetry context by dropping agent/model detection,
task parsing, and mode tracking; kept only command name.

- **Migration**
- If you imported `browser_use.telemetry.CLITelemetryEvent` or
`capture_detached`, switch to
`browser_harness.telemetry.capture_cli_event`.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5165?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-07-07 10:51:02 -07:00
laithrw d1af39626e Merge branch 'main' into feat/delegate-cli-telemetry-to-harness 2026-07-07 10:48:36 -07:00
Laith Weinberger 8e279db893 global var 2026-07-07 10:38:35 -07:00
Laith Weinberger 803fd85f97 remove telemetry 2026-07-07 10:27:38 -07:00
laithrw d2c1535a58 improve CLI hint (#5164)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Clarifies the CLI legacy migration hint so users can upgrade to CLI 3.0
without confusion. The message now instructs to run `browser-use skill
install` to install the CLI 3.0 skill (replacing any existing
browser-use skill) and to load the skill after installation.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5164?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-07-07 10:11:08 -07:00
Laith Weinberger 55a56c31c4 improve CLI hint 2026-07-07 10:07:15 -07:00
laithrw 09813100a3 updated readme (#5162)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Simplified the README by replacing the LLM Quickstart steps with a
single link to llms-full.txt for coding agents, reducing clutter and
speeding setup.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5162?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-07-08 00:07:05 +08:00
laithrw 4c030a8e72 Merge branch 'main' into worktree-remove-llm-quickstart 2026-07-08 00:05:08 +08:00
laithrw 3c1d1c96ac rename browser harness -> use except urls (#5161)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Renamed all non-URL mentions of "browser-harness" to "browser-use" in
generated skill docs to prevent broken links and simplify the logic.

- **Refactors**
- Replaced multiple targeted string replacements with a single regex
that skips URL path segments (uses `re.sub(r'(?<!/)browser-harness',
'browser-use', body)`).

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5161?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-07-08 00:04:55 +08:00
Laith Weinberger 00552f5b64 updated readme 2026-07-07 09:04:08 -07:00
laithrw 53b1316076 Merge branch 'main' into skill-rename-regex 2026-07-08 00:02:34 +08:00
laithrw 90f1013cf6 Fix/cli slowdown (#5150)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Fixes CLI slowdowns by sending telemetry in a detached helper with a 5s
network timeout, and adds better error context via the stderr tail. Also
improves agent detection and sends a truncated task for easier
debugging.

- **Bug Fixes**
- Telemetry is sent via a detached helper to avoid blocking the CLI;
respects `ANONYMIZED_TELEMETRY` and uses a 5s send timeout.
- Error events include the last 500 chars of stderr as context when
available.

- **New Features**
- Broader agent client detection via env markers (e.g., Cursor, Copilot,
Claude Code, Gemini CLI, OpenClaw, Hermes, CodeX, Amp).
- Sends a truncated task (up to 20k chars) instead of fully redacting;
device ID is unified, persisted, and falls back to a machine
fingerprint.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5150?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-07-07 23:57:18 +08:00
laithrw 2d01ea316f Merge branch 'main' into fix/cli-slowdown 2026-07-07 23:48:25 +08:00
Laith Weinberger 61e069d1ed Merge branch 'fix/cli-slowdown' of https://github.com/browser-use/browser-use into fix/cli-slowdown 2026-07-07 08:47:48 -07:00
Laith Weinberger c04c724c97 timeout 2026-07-07 08:46:53 -07:00
laithrw d32d0bf05e reduce CLI repetition (#5160)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Condensed the README by removing the duplicated CLI setup snippet. The
“Browser Use CLI 3.0 is here.” headline now links to the CLI section
(`#-cli`) to centralize instructions and reduce clutter.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5160?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-07-07 23:35:26 +08:00
Laith Weinberger 95050db6ee reduce CLI repetition 2026-07-07 08:30:39 -07:00
Magnus Müller 052787f9c7 Update README.md (#5154) 2026-07-06 19:59:22 -07:00
Magnus Müller e352891498 Update README.md 2026-07-06 19:57:48 -07:00
Magnus Müller cc553f7cc3 Remove Template Quickstart section from README (#5153)
Removed the Template Quickstart section from the README.
2026-07-06 18:53:03 -07:00
Magnus Müller 0005c57c01 Remove Template Quickstart section from README
Removed the Template Quickstart section from the README.
2026-07-06 18:51:43 -07:00
Saurav Panda 52d67e7efe Fix visibility check mutating shared snapshot bounds (#5151)
## Problem

`is_element_visible_according_to_all_parents` mutated
`node.snapshot_node.bounds` **in place** while walking the frame chain
(`dom/service.py:295-296, 333-334`):

1. Every checked node's bounds were permanently shifted by frame offsets
and scroll, corrupting coordinates shared with `absolute_position` math,
paint-order filtering, and iframe hidden-element counting. The check
wasn't even idempotent — calling it twice on the same node could return
different results.
2. A frame node appears in **its own** frame chain
(`_construct_enhanced_node` appends it before computing its visibility),
so an iframe's bounds got offset by themselves — coordinates doubled. An
iframe at y=1200 on a 900px viewport (threshold 1000) became y=2400 →
classified invisible → `should_process_iframe=False` → its entire
content subtree silently dropped from extraction. A likely contributor
to "elements missing from the DOM" reports and one of the reasons
cross-origin iframe support has been flaky.

## Fix

Work on a copy of the bounds; skip `frame is node` in the chain walk. No
behavioral change for correctly-classified elements.

## Verification

New unit tests (no browser needed) that fail on the previous code:
- bounds object unchanged after a visibility check
- the check is idempotent
- an iframe checked against a frame chain containing itself is not
double-transformed (y=1200 stays visible)

Plus existing DOM serializer browser tests passing; pyright and ruff
clean.

Part of a series of small fixes from an internal review (#5133, #5146,
#5147, #5148).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes a visibility bug that mutated shared bounds and double-offset
iframes. We now copy bounds and skip self in the frame chain, restoring
correct visibility and preventing dropped iframe content.

- **Bug Fixes**
- Copy `snapshot_node.bounds` before transforms to avoid in-place
mutations.
- Skip `frame is node` when walking the frame chain to prevent double
transforms.
- Add tests for non-mutation and iframe visibility; strengthen the
idempotency test to catch drift at threshold.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5151?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-07-07 09:34:26 +08:00
Saurav Panda 94ea8f60c8 Strengthen DOM visibility idempotency test 2026-07-06 18:27:04 -07:00
Laith Weinberger 425bcb8de3 rename browser harness -> use except urls 2026-07-06 17:52:00 -07:00
Saurav Panda 9863fe17fb Fix visibility check mutating shared snapshot bounds
is_element_visible_according_to_all_parents mutated snapshot_node.bounds in
place while walking the frame chain, permanently shifting every checked
node's coordinates by frame offsets and scroll — corrupting values shared
with absolute_position math, paint-order filtering, and any later visibility
check (the function was not even idempotent).

Worse, a frame node appears in its own frame chain (_construct_enhanced_node
appends it before computing visibility), so an iframe's bounds were offset
by themselves — coordinates doubled — wrongly classifying iframes past
half the viewport threshold as invisible and silently dropping their entire
content subtree from extraction.

Work on a copy of the bounds and skip self in the frame chain.
2026-07-06 17:40:46 -07:00
Saurav Panda 448cc10d62 Detect LLM output truncation instead of failing with misleading parse errors (#5148)
## Problem

When structured output hits the completion-token cap, the JSON arrives
cut mid-string. Nothing checked `finish_reason`/`stop_reason` anywhere
(`grep stop_reason browser_use/agent/service.py` → no hits), so users
saw either:

- an opaque `Unterminated string starting at...` / pydantic validation
error, or
- **worse**: a valid-but-chopped answer, if truncation landed between
JSON fields — silent partial answers.

With `ChatOpenAI`'s default `max_completion_tokens=4096`, this regularly
hits long `done(text=...)` and `extract` outputs. The real cause was
never surfaced.

## Fix

Each provider checks its truncation signal before parsing structured
output and raises a clear error naming the cause and the knob:

- **OpenAI** (`finish_reason == 'length'`): raise
`ModelProviderError('Model output was truncated at
max_completion_tokens=N; ... increase max_completion_tokens or request
shorter output')`
- **Anthropic** (`stop_reason == 'max_tokens'`): same, for the tool_use
input path; plus an `except ModelProviderError: raise` guard so the new
error isn't re-wrapped by the generic catch-all into a status-502 blob
- **Google** (`MAX_TOKENS` finish reason): shared
`_raise_if_output_truncated()` helper applied to both the structured
path and the fallback text path

Status code **400** is deliberate: it's not in any provider's retryable
set (Google retries 429/5xx), and retrying an identical request would
truncate identically.

## Verification

- Regression test drives the **real OpenAI SDK** against a local
httpserver returning `finish_reason='length'` with JSON cut mid-string —
asserts the error names the truncation and the parameter, not a parse
failure (fails on previous code with the misleading validation error). A
companion test confirms normal `finish_reason='stop'` responses still
parse.
- 9 LLM tests passing; pyright and ruff clean.

Anthropic/Google paths are verified by type-checker and code-trace;
their SDK transports don't point at a local httpserver as easily —
follow-up welcome if there's an established fake for them.

Part of a series of small fixes from an internal review (#5133, #5146,
#5147).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Detect and surface LLM output truncation instead of misleading JSON
parse errors, and allow switching to a fallback LLM when truncation
occurs. Also handles reasoning models that return `content=null` with a
length-based finish reason.

- **Bug Fixes**
- OpenAI: If `finish_reason == 'length'`, raise
`ModelOutputTruncatedError` (400) before the missing-content guard;
reference `max_completion_tokens` or say "the model's output token
limit" when unset.
- Anthropic: If `stop_reason == 'max_tokens'`, raise
`ModelOutputTruncatedError`; add `except ModelProviderError: raise` so
the clear error isn’t re-wrapped.
- Google: Detect `MAX_TOKENS` in both structured and fallback paths via
`_raise_if_output_truncated`; raise (400) referencing
`max_output_tokens` or the generic phrase when unset.
- Agent: Introduced `ModelOutputTruncatedError` (400). Provider retry
loops skip it; the agent treats it as fallback-switchable so a
higher-cap model can succeed.
- Tests: Cover truncation vs normal parse, unset-cap message,
`content=None` case, and that truncation triggers a fallback LLM switch.

- **Refactors**
  - Trimmed comments to essentials.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5148?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-07-07 08:39:12 +08:00
laithrw 285ba3b401 Merge branch 'main' into fix/cli-slowdown 2026-07-07 08:29:32 +08:00
Laith Weinberger b1bf5281ad better error tracking 2026-07-06 17:28:02 -07:00
Laith Weinberger 9ee4929912 don't slow down CLI 2026-07-06 17:15:17 -07:00
Saurav Panda 248f98f208 Merge branch 'main' into fix/llm-output-truncation 2026-07-07 07:51:02 +08:00
Saurav Panda 2afef14667 Trim comments to essentials 2026-07-06 16:47:27 -07:00
laithrw 95ec7c634c shim for legacy CLI cmds (#5149)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Adds a legacy command shim to the `browser-use` CLI that detects pre-3.0
subcommands/flags and prints a short migration guide with examples. It
exits with code 2 and tags the run as `legacy:<cmd>` instead of running
the harness.

- **New Features**
- Detects common pre-3.0 commands and flags and maps them to clear
hints.
- Prints a structured migration message to stderr with stdin piping
examples, core helpers, and doc links.
- Exits with status 2 to signal misuse; avoids starting the browser
harness.
- Updates command context/dispatch to label events as `legacy:<cmd>` for
easier tracking.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5149?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-07-07 07:46:39 +08:00
Saurav Panda e672ab6de7 Detect truncation before the missing-content guard
OpenAI reasoning models can spend the entire max_completion_tokens budget on
hidden reasoning, returning finish_reason='length' with content=null. The
truncation check ran after the missing-content guard, so that case raised
the generic 'Failed to parse structured output' (500) instead of
ModelOutputTruncatedError — no truncation signal, no fallback switch. Check
finish_reason first; it does not depend on content.
2026-07-06 16:45:33 -07:00
Laith Weinberger b65642f810 shim for legacy CLI cmds 2026-07-06 16:40:41 -07:00
Saurav Panda db70a813c1 Let truncation errors switch to the fallback LLM
The 400 status chosen for truncation errors (deliberately outside provider
retry lists — an identical retry truncates identically) also fell outside
Agent._try_switch_to_fallback_llm's allowlist, so a configured fallback_llm
could no longer rescue a truncated run. Before this PR the downstream parse
failure was wrapped as a 502 ModelProviderError, which did allow the switch.

Introduce ModelOutputTruncatedError(ModelProviderError, status 400): the
three providers raise it, provider retry loops still skip it, and the
agent's fallback check treats it as switchable explicitly — a fallback with
a different output cap can succeed where the primary truncated.
2026-07-06 16:31:18 -07:00
Saurav Panda db2b2c67f9 Describe truncation gracefully when the token cap is unset
max_completion_tokens (OpenAI) and max_output_tokens (Google) are optional;
when set to None the truncation message printed 'truncated at
max_output_tokens=None'. Fall back to "the model's output token limit" —
a MAX_TOKENS/length finish reason means some server-side cap fired even
with no client-side cap configured. Anthropic's max_tokens is non-optional
and unaffected.
2026-07-06 15:56:03 -07:00
Saurav Panda 5f690c207e Detect LLM output truncation instead of failing with misleading parse errors
Structured output cut off at the completion-token cap was never detected:
OpenAI's finish_reason='length', Anthropic's stop_reason='max_tokens', and
Gemini's MAX_TOKENS finish reason all produced JSON cut mid-string, which
surfaced as an opaque parse error ('Unterminated string starting at...') —
or worse, a valid-but-chopped prefix. The actual cause (output token cap)
was never mentioned, and with defaults like max_completion_tokens=4096 this
regularly hits long done()/extract outputs.

Each provider now checks the finish/stop reason before parsing structured
output and raises a clear ModelProviderError ('Model output was truncated
at max_*_tokens=N; increase it or request shorter output'). Status code 400
is used deliberately: it is not in any retry list, and retrying the same
request would truncate identically.

Also adds an 'except ModelProviderError: raise' guard in the Anthropic
handler so the new error is not re-wrapped by the generic catch-all.
2026-07-06 15:17:52 -07:00
Saurav Panda 6313da7846 Preserve BrowserError's structured memory through execute_action (#5147)
## Problem

`Registry.execute_action`'s catch-all `except Exception` also caught
`BrowserError`, flattening it into a generic `RuntimeError('Error
executing action ...')`. That destroyed the structured
`short_term_memory` / `long_term_memory` the error carries specifically
to steer the LLM's next action — e.g. the list of available dropdown
options when the agent clicks a `<select>`.

The `except BrowserError` branch in `Tools.act` (which formats those
memories into an `ActionResult` via `handle_browser_error`) was dead
code for any action that lets a `BrowserError` propagate through
`execute_action` — `upload_file`, `dropdown_options` via
`event_result(raise_if_any=True)`, extraction handler errors. The LLM
got an opaque error string instead of actionable context, burning extra
steps rediscovering what the error already knew.

## Fix

One `except BrowserError: raise` ahead of the generic handlers in
`execute_action`, so `handle_browser_error` in `Tools.act` is the single
formatting point again.

## Verification

- New regression test: a registered action raising
`BrowserError(short_term_memory=..., long_term_memory=...)` must arrive
in the `ActionResult` with `error` = long-term memory and
`extracted_content` = short-term memory. Fails on the previous code with
the flattened generic string.
- 17 tools/registry tests passing; pyright and ruff clean.

Part of a series of small fixes from an internal review (same batch as
#5133, #5146).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Preserves structured `BrowserError` memory through
`Registry.execute_action` while keeping plain errors recoverable, so
agents get context without breaking callers. `Tools.act` stays the
single formatting point for structured errors.

- **Bug Fixes**
- Re-raise `BrowserError` only when `long_term_memory` is present;
otherwise, flatten to `RuntimeError` to preserve previous recoverable
behavior.
- Map `long_term_memory` -> `ActionResult.error` and `short_term_memory`
-> `ActionResult.extracted_content` for structured errors.
- Add tests for both structured and plain `BrowserError` paths to lock
in behavior.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5147?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-07-07 06:17:04 +08:00
Saurav Panda 782535c34f Keep plain BrowserErrors recoverable: only structured ones bypass the generic handler
An unconditional re-raise sent BrowserErrors without long_term_memory (e.g.
upload_file's failure paths) into Tools.act's handle_browser_error, which
re-raises exactly those — escaping act() as an exception where callers
previously got a recoverable ActionResult(error=...).

Guard the bypass on long_term_memory being present (the exact condition
handle_browser_error formats without re-raising; short_term_memory alone
would still re-raise), and flatten plain BrowserErrors to RuntimeError as
before. Regression test covers the plain-BrowserError path through
tools.act.
2026-07-06 15:04:37 -07:00
Saurav Panda f30c3952c9 Preserve BrowserError's structured memory through execute_action
Registry.execute_action's catch-all handler flattened BrowserError into a
generic 'Error executing action ...' RuntimeError, destroying the structured
short_term_memory/long_term_memory the error carries to steer the LLM's next
action (e.g. the list of available dropdown options when clicking a select).
The 'except BrowserError' branch in Tools.act that formats those memories
into an ActionResult was dead code for any action that let a BrowserError
propagate (upload_file, dropdown_options via event_result, extraction
handlers).

Re-raise BrowserError before the generic handlers so handle_browser_error
becomes the single formatting point again.
2026-07-06 14:52:39 -07:00
Saurav Panda 4cd898212c Fix markdown extraction destroying URLs and dropping long link lines (#5146)
## Problem

Two content-destruction bugs in `extract_clean_markdown` caused
extraction results to silently lose content — a direct source of agents
reporting 'not found on page' for content that was there:

1. **All percent-encodings stripped**: `re.sub(r'%[0-9A-Fa-f]{2}', '',
content)` ran over the entire markdown output, corrupting every URL
containing `%20`/`%2F`/`%26`... — precisely when `extract_links=True`
was requested.
2. **Long link lines dropped**: the JSON-blob heuristic removed any line
over 100 chars starting with `{` **or `[`** — which matches long
markdown links `[text](very-long-url)`, clickable images
`[![alt](src)](href)`, and citation-style lines. Items simply vanished
from extracted lists.

## Fix

- Delete the `%XX` regex entirely (markdownify does not emit stray
percent-encodings; URLs must survive intact).
- Only drop long `{`/`[`-prefixed lines that actually parse as JSON
(`json.loads`), so SPA state blobs are still filtered while markdown
links survive. The three upstream regexes for common blob shapes are
unchanged.
- Extract the HTML→markdown conversion into a pure
`convert_html_to_markdown()` helper so this stage is unit-testable
without a browser.

Trade-off: JS object literals (unquoted keys) over 100 chars are now
kept since they aren't valid JSON — deliberately erring toward never
destroying real content.

## Verification

- New regression tests: long markdown link lines and clickable-image
lines survive; valid JSON array blobs are still dropped; percent-encoded
URLs survive the full HTML→markdown pipeline. The link-line test fails
on the previous code.
- 37 markdown extractor/chunking tests passing; pyright and ruff clean.

Part of a series of small fixes from an internal review (same batch as
#5133).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes content loss in markdown extraction by preserving percent-encoded
URLs and keeping long markdown link/image lines. Prevents broken links
and “not found on page” misses during link extraction.

- **Bug Fixes**
- Removed `%XX` stripping from the output so `%20`, `%2F`, `%26`, etc.
in URLs are preserved.
- Only drop long `{`/`[` lines if they parse as JSON; long `[text](url)`
and `[![alt](src)](href)` lines now remain.

- **Refactors**
- Extracted HTML→markdown into `convert_html_to_markdown()` for easier
testing and reuse.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5146?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-07-07 05:49:46 +08:00
Saurav Panda ab08dea62c Fix markdown extraction destroying URLs and dropping long link lines
Two content-destruction bugs in extract_clean_markdown:

- A cleanup regex stripped every %XX sequence from the converted markdown,
  corrupting all percent-encoded URLs (%20, %2F, ...) — precisely when
  extract_links=True was requested.
- The JSON-blob line filter dropped any line over 100 chars starting with
  '{' OR '[' — silently deleting long markdown links [text](long-url),
  clickable images, and citation-style lines.

Delete the %XX regex, and only drop long lines that actually parse as JSON
(json.loads) so SPA state blobs are still filtered while markdown links
survive. Also extract the HTML->markdown conversion into a pure
convert_html_to_markdown() helper so this stage is unit-testable.
2026-07-06 14:45:25 -07:00
Saurav Panda 68a11ef77d Fix navigation readiness detection: per-target lifecycle event storage (#5133)
## Problem

Every navigation could burn its full readiness timeout (3s same-domain /
8s cross-domain) and then proceed on a page in an unknown load state —
DOM snapshots and screenshots taken on half-loaded pages, plus 3-8s of
dead time per navigation.

**Root cause:** `_navigate_and_wait` polls a lifecycle-event deque that
was fed by a per-session closure registered via
`cdp_client.register.Page.lifecycleEvent(...)`. cdp-use's event registry
is **single-slot per CDP method** (`self._handlers[method] = callback`),
so every new target attach **replaced** the previous tab's handler.
Existing tabs' deques froze with only pre-navigation events, which the
loaderId filter then (correctly) rejected — guaranteed timeout.

This is deterministic with 2+ tabs, and nondeterministic even single-tab
because each target gets attached twice (manual `attachToTarget` +
browser-level `setAutoAttach`), creating two session objects with two
competing registrations while `_get_session_for_target` picks one
arbitrarily.

## Fix

- Lifecycle events are now stored **per target_id in SessionManager**,
fed by **one global** `Page.lifecycleEvent` handler registered once in
`start_monitoring()` and routed by `session_id` — same pattern as the
existing Target attach/detach handlers. Buffers are freed on target
removal.
- `_navigate_and_wait` reads the per-target buffer and returns a timeout
status string instead of swallowing readiness timeouts;
`on_NavigateToUrlEvent` surfaces it via
`NavigationCompleteEvent.loading_status`, so downstream consumers can
distinguish 'loaded' from 'gave up waiting'.
- loaderId-less lifecycle events that predate the current navigation are
skipped (defense against stale entries).
- Dropped unused `CDPSession._lifecycle_lock`.

## Verification

Deterministic regression test
(`tests/ci/browser/test_navigation_readiness.py`): navigate tab A after
opening tab B — **before: exactly 3.01s (burned timeout), after:
<0.5s**. Also covers: fast-page navigation completes without burning the
timeout, readiness timeout is reported via the return status (page with
a stalled `<img>` holding back `load`), and successful navigation
returns no timeout status.

- 4 new tests + 20 existing navigation/session/cross-origin tests
passing
- pyright and ruff clean

## Notes

- The loaderId filter itself was verified correct against live Chrome
(new-navigation events match `Page.navigate`'s returned loaderId within
ms) and is kept as-is.
- Known remaining issue (separate PR): HAR recording registers
`Page.lifecycleEvent` too and will clobber this global handler when
`record_har_path` is set — same single-slot registry problem, needs a
proper fan-out multiplexer.
- The double-attach (two CDP sessions per pre-existing target) is also a
separate follow-up; it's harmless to this wait loop now.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes navigation readiness by storing lifecycle events per target and
routing them through one global `Page.lifecycleEvent` handler. Also
short-circuits same-document navigations so fragment/History API changes
don’t burn the timeout.

- **Bug Fixes**
- Store lifecycle events per `target_id` in `SessionManager`, fed by a
single global `Page.lifecycleEvent` from `cdp-use`; clear buffers on
target removal.
- `_navigate_and_wait` reads the per-target buffer and returns a timeout
status string; surfaced via `NavigationCompleteEvent.loading_status`.
- Ignore stale signals: drop events with mismatched `loaderId`, and skip
loaderId-less events older than the navigation start.
- Treat same-document navigations (no `loaderId`) as complete
immediately.
- Regression tests: fast navigations finish without burning fallback
timeouts; navigating tab A remains fast after opening tab B; readiness
timeouts are reported; same-document navigation completes immediately.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5133?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-07-07 05:41:48 +08:00
Saurav Panda b4b6868232 Handle same-document navigations without burning the readiness timeout
Page.navigate omits loaderId for same-document navigations (#fragment,
History API), and Chrome emits no new load/DOMContentLoaded lifecycle events
for them — the navigation is already committed when Page.navigate returns.
The stale-event timestamp guard would otherwise reject all buffered events
and burn the full readiness timeout.

Short-circuit when loaderId is absent, and simplify the stale-event guard
(the no-navigation-id case can no longer reach it). Regression test drains
the previous load's trailing networkIdle first so a stale event can't
accidentally satisfy the wait.
2026-07-04 04:27:18 +08:00
Saurav Panda c5f0fa767c Fix navigation readiness detection: per-target lifecycle event storage
Navigation waits polled a per-session event deque whose feeding handler was
registered per-session on cdp-use's single-slot event registry. Any later
target attach replaced the handler, freezing existing tabs' deques with only
pre-navigation events, so every navigation on those tabs burned the full
readiness timeout (3s same-domain / 8s cross-domain) and then proceeded on a
page in unknown load state.

- Store lifecycle events per target_id in SessionManager, fed by ONE global
  Page.lifecycleEvent handler registered in start_monitoring() and routed by
  session_id; buffers are freed on target removal
- _navigate_and_wait reads the per-target buffer and now returns a timeout
  status string instead of swallowing readiness timeouts;
  on_NavigateToUrlEvent surfaces it via NavigationCompleteEvent.loading_status
- Skip loaderId-less lifecycle events that predate the current navigation
- Drop unused CDPSession._lifecycle_lock

Deterministic regression test: navigating tab A after opening tab B took
exactly the 3s fallback timeout before this fix, <0.5s after.
2026-07-03 22:48:20 +08:00
shawn pana 18484f23ac Update README.md (#5122)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Reorganized the README to move the “Browser Use CLI 3.0” announcement
and copy‑paste snippet below the hero image for better flow. Removed the
top promo block and updated the cloud link copy and UTM medium to
`readme-skip-setup`.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5122?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-07-02 08:43:55 +08:00
shawn pana 58030ca5c4 Update README.md 2026-07-02 08:41:52 +08:00
laithrw 0162e02820 Bump browser-use to 0.13.3 (#5117)
Bumps the package version to 0.13.3 for the CLI 3.0 release.

Checks run locally:
- `uv run ruff check --no-fix --select PLE`
- `uv build`

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Bumps `browser-use` to 0.13.3 for the CLI 3.0 release. No code changes;
version-only update to trigger the release pipeline.

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

<a
href="https://cubic.dev/pr/browser-use/browser-use/pull/5117?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. -->
0.13.3
2026-07-01 23:05:46 +08:00
Laith Weinberger 28d1308f0c bump browser-use to 0.13.3 2026-07-01 23:02:39 +08:00