* fix(tabs): preserve refs across --tab peek and cover outer-tab-closed path
Follow-up to #1249 so `--tab <id>` is actually useful for agents:
- Save and restore the outer tab's `ref_map`, `iframe_sessions`, and
`active_frame_id` across a scoped command instead of clearing them.
`snapshot` → `--tab N <cmd>` → `click @e1` now keeps the outer tab's
refs intact. Scoped commands still see a clean slate so outer refs
can't resolve against the scoped tab's DOM.
- Close the coverage gap the Vercel review bot flagged on #1249: the
previous `e2e_tab_scoped_command_handles_outer_tab_closed` test used
`tab_close`, which is in the scoped-dispatch exclusion list, so it
never exercised the restore-skip branch it claimed to test. Renamed
to `e2e_tab_close_with_tab_id_closes_active_tab` with an honest
docstring, and added `e2e_tab_scoped_command_outer_tab_closed_mid_dispatch`
that actually hits the branch via `window.opener.close()` on a
script-opened intermediate tab.
- Add `e2e_tab_scoped_command_isolates_refs_from_outer_tab` pinning
that outer refs don't bleed into the scoped tab's DOM resolution.
- Rewrite `e2e_tab_scoped_command_clears_state_on_switch` as
`e2e_tab_scoped_command_preserves_outer_tab_state`, verifying the
restored @e1 still clicks end-to-end.
- Update the 52 `--help` entries for `--tab <id>` to describe peek /
restore semantics instead of a vague "Target specific tab ID".
- Update README, docs site, config schema, and the agent-facing
skills reference with working examples (refs survive the peek) and
a "when to use \`--tab <id>\` vs \`tab <id>\`" guide so agents pick
the right flag for their workflow.
* fix(tabs): use t<N> prefix for tab ids, add --label for named tabs
Follow-on to the tab work in #1249 and the prior commit, redesigning the
tab handle surface before release since nothing ships these features yet.
## Why
Incrementing integer tab ids (`1`, `2`, `3`) look indistinguishable from
positional indices in command output, LLM-generated scripts, and docs. In
the common single-agent case where position and id coincide, readers have
no visual cue for which mental model they're using. Positional indices
silently shift when unrelated tabs open/close, so misreading a handle as
an index is a correctness hazard.
## Changes
**Tab ids are now `t1`, `t2`, `t3` (strings).** Bare integer `tabId`
values are rejected with a teaching message rather than silently accepted.
The `t` prefix matches the `@e1` element-ref convention and makes ids
unmistakably non-positional at a glance.
**Labels.** Tabs can be created with a user-assigned label (e.g. `docs`,
`app`) via `tab new --label <name> [url]`. Labels are interchangeable
with `t<N>` ids everywhere a tab ref is accepted. They're never
auto-generated, never rewritten on navigation, and must be unique within
a session.
**Dashboard fix.** `packages/dashboard/src/types.ts` declared
`TabInfo.index: number` but the daemon has been sending `tabId` (not
`index`) since #892, making `tab.index` `undefined` and breaking the
dashboard's close/switch buttons silently. Updated the TS types and
usages to consume `tabId` (string) and optional `label`, restoring the
dashboard's tab interactions.
## Surface
- `cli/src/native/browser.rs`: `TabRef::parse` / `format_tab_id` /
`is_valid_label` / `PageInfo.label` / `BrowserManager::resolve_tab_ref`
/ `BrowserManager::has_label`. `tab_new` gains an optional label
argument with duplicate rejection. All JSON responses use the string
form and include the label.
- `cli/src/native/actions.rs`: scoped-command pre-dispatch and
`handle_tab_{switch,close,new}` parse string refs and resolve to
stable ids.
- `cli/src/{flags,commands,main,output}.rs`: `--tab` / config `tab`
are `String`; `tab` subcommand accepts `t<N>` or a label and supports
`tab new --label <name> [url]`. All 52 `--help` entries updated.
- `agent-browser.schema.json`: `tab` property type is now `string` with
a pattern matching `t<N>` or label form.
- `packages/dashboard`: `TabInfo.tabId: string` / `label?: string | null`;
`closeTabAtom`/`switchTabAtom` take `tabRef: string`; component props
updated.
- Docs: README, docs site (`commands/` and `configuration/`), and the
agent-facing skills reference rewritten with the new examples.
## Tests
- Added `TabRef::parse` / `format_tab_id` / `is_valid_label` unit tests
pinning the bare-integer rejection, the teaching error, label rules,
and round-tripping.
- Added `test_tab_switch_by_id` / `_by_label` / `test_tab_new_with_label`
/ `_with_label_and_url` / `_with_url_then_label` in `commands.rs`;
rewrote `test_tab_unknown_subcommand_errors` since labels make
`tab select` a legitimate ref.
- Added `e2e_tab_new_with_label_can_be_switched_and_peeked`,
`e2e_tab_new_with_duplicate_label_errors`,
`e2e_tab_scoped_command_rejects_bare_integer`.
- Migrated every existing tab e2e test (and one unit test) from
integer `tabId` to the string form.
`cargo fmt`, `cargo clippy -- -D warnings`, all 30 non-ignored tab unit
tests, all 13 tab e2e tests, and `tsc --noEmit` on the dashboard all
pass.
* refactor(tabs): drop --tab scoped peek flag; keep t<N> ids and labels
After fleshing out `--tab <id|label>` in the previous commits (scoped
pre/post-dispatch save/restore, ref preservation, outer-tab-closed edge
case, full e2e coverage), the machinery-to-value ratio makes the feature
hard to justify. Nixing it now while nothing has shipped.
## Why
- Every new daemon feature touching per-tab state has to reason about
scoped-dispatch interleaving. `ScopedRestore`, pre/post-dispatch hooks,
and the exclusion list add ongoing maintenance tax.
- Three separate PRs (#892, #1249, and this one pre-nix) were needed to
reach "works correctly." That's a smell.
- `tab <id|label>` switch + labels already cover the legible multi-tab
workflow case.
- `--tab` vs `tab <id>` have opposite lifecycle semantics but look
identical, teaching every agent two things where one would do.
- "Non-disruptive peek" isn't actually race-free: the daemon does swap
active tab during execution, so a concurrent client between pre- and
post-dispatch sees the scoped tab as active.
- Ref-based interaction with scoped tabs never worked ergonomically —
refs are per-tab, so `--tab N click @e1` requires `@e1` to already be
on tab N, which means a prior switch, which negates the peek.
- Adding a feature back is easy; removing shipped API is hard.
If per-tab caching (`HashMap<tab_id, RefMap>`) lands later, `--tab` can
be reintroduced essentially for free. That's the right time.
## Removed
- `--tab <id|label>` global flag (`cli/src/flags.rs`, `cli/src/main.rs`,
all 52 `--help` entries in `cli/src/output.rs`).
- `tab` property in `agent-browser.schema.json` and the config-options
row in `docs/src/app/configuration/page.mdx`.
- `ScopedRestore` struct, pre/post-dispatch save/restore in
`execute_command` (`cli/src/native/actions.rs`).
- `impl Default for RefMap` in `cli/src/native/element.rs` (only added
for `mem::take` in the scoped machinery).
- `e2e_tab_global_targeting`, `_snapshot`, `_snapshot_non_contiguous`,
`e2e_tab_scoped_command_preserves_outer_tab_state`,
`_isolates_refs_from_outer_tab`, `_restores_active_tab`,
`_outer_tab_closed_mid_dispatch`. 590 lines.
- The "When to use `--tab` vs `tab <id|label>`" sections in README,
docs site, and skills reference.
## Kept
- Stable tab ids (`t1`, `t2`, `t3`) with bare-integer rejection.
- User-assigned labels (`tab new --label docs [url]`), with duplicate
rejection and interchangeable use everywhere a tab ref is accepted.
- `BrowserManager::{active_tab_id, has_tab_id, resolve_tab_ref, has_label}`
accessors (still used by the remaining tab handlers).
- `TabRef::parse`, `format_tab_id`, `is_valid_label` and their unit
tests.
- Dashboard TS fix (`TabInfo.tabId` + `label`).
- `e2e_tab_close_with_tab_id_closes_active_tab` (renamed docstring to
drop the gone exclusion-list reference).
- `e2e_tab_new_with_label_can_be_switched_and_closed` (rewrite of the
previous `_and_peeked` test — now exercises only switch and close).
- `e2e_tab_switch_rejects_bare_integer` (rewrite targeting the
`tab_switch` daemon handler rather than the removed scoped path).
net: -900 lines across 12 files. `cargo fmt`, `cargo clippy -D warnings`,
all 25 non-ignored tab unit tests, all 6 tab e2e tests, and
`tsc --noEmit` on the dashboard all pass.
* fix(test): tolerate stale screencast frames in viewport e2e test
Chrome's `Page.startScreencast` `maxWidth`/`maxHeight` are upper bounds,
and early frames can arrive before the viewport resize fully takes effect.
Instead of asserting exact JPEG dimensions on the first frame, skip frames
with stale dimensions and wait for one that matches.
* Prepare v0.25.5
* v0.24.1
* fix: e2e test failures on CI
- e2e_relaunch_on_options_change: use headless for all launches;
the third launch only changes extensions, which is sufficient to
trigger the relaunch hash mismatch without needing an X display
- e2e_auth_login flake: reduce SPA render delay from 1200ms to 800ms
to add headroom within the 5s preferred selector window on slower
CI runners
Add provider icons and session creation from the dashboard UI.
Sessions can now be created with cloud providers (Browserbase,
Browserless, Browser Use, Kernel) in addition to local engines.
CLI changes:
- Track provider via .provider files alongside .engine files
- Add WaitUntil::None variant to skip lifecycle event waits for providers
- Auto-set waitUntil=none when --provider is used with navigate
- Fix Browser Use: use direct WSS connection (wss://connect.browser-use.com)
- Add connect_cdp_direct for providers with page-level CDP proxies
- Fix resolve_cdp_url to convert https:// provider URLs to wss://
- Treat empty CDP session_id as None (omit from protocol messages)
- Fix Browserbase: send explicit JSON body + Content-Type header
- Increase CDP connect timeout to 25s for remote providers
- Clean up .provider files on session close
Dashboard changes:
- Show provider or engine icon per session in sidebar
- New session dialog with unified engine/provider selector grid
- Async session creation with loading state and error display
- Kill zombie daemons on provider connection failure
- Parse CLI JSON error output for user-friendly messages
- Default new session URL to https://agent-browser.dev
* dashboard
* fix: re-apply download behavior on recording context (#1019)
* fix: re-apply download behavior on recording context
record start creates a new browser context via Target.createBrowserContext.
Browser.setDownloadBehavior called at launch only applies to the default
context, so downloads in the recording context are silently dropped.
Fix:
1. Store download_path on BrowserManager (from LaunchOptions)
2. After creating the recording context, call Browser.setDownloadBehavior
with the new browserContextId
This ensures downloads work during recording.
Fixes#1018
* fix: add download_path to third BrowserManager constructor (auto_connect_cdp)
* fix: reap zombie Chrome process and fast-detect crash for auto-restart (#1023)
When Chrome crashes (e.g. SIGTRAP from CHECK() assertion), the daemon
now:
1. Reaps the zombie immediately via a SIGCHLD handler in the event loop
that calls waitpid(-1, WNOHANG)
2. Detects the crash instantly on the next command via a non-blocking
try_wait() check (has_process_exited), avoiding the 3-second CDP
timeout that is_connection_alive() would incur
3. Auto-relaunches Chrome transparently for the caller
Fixes#1017
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* fix: route keyboard type through text input (#1014)
* fix: handle --clear flag in console command (#1015)
The console and errors commands parsed --clear from CLI args but the
action handlers silently ignored the flag. The handlers did not accept
the cmd parameter so they had no way to read the clear field.
Changes:
- Add clear_console() method to EventTracker in network.rs
- Update handle_console to accept cmd, read the clear field, and clear
the buffer when --clear is passed (returns {cleared: true})
- Update call site in execute_command to pass cmd
Co-authored-by: xuyongliang <yongliang.xyl@alibaba-inc.com>
* chore: patch release - ### Bug Fixes
- **Re-apply download behavior on r... (#1025)
* Add runtime stream enable/disable/status commands (#951)
* Add runtime stream management commands
* Run rustfmt and satisfy clippy
* Fix stream disable cleanup semantics
* Format stream disable regression tests
* fix: retain radio/checkbox elements in compact snapshot tree (#1008)
compact_tree() checked for "[ref=" to identify lines worth keeping, but
radio and checkbox elements render as e.g. [checked=false, ref=e1] where
the "[" opens before "checked=", not "ref=". Dropping the leading bracket
so the check is just "ref=" fixes the match for all elements with refs.
Fixes#1006
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* chore: version packages (#1027)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fixes
* dashboard
* fixes
* remove observe
* fmt
* fixes
* fixes
* jotai
* fmt
* upload dashboard
---------
Co-authored-by: Stefan Smiljkovic <stefan@vanila.io>
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Co-authored-by: zhanba <c5e1856@gmail.com>
Co-authored-by: xuyongliang <478439790@qq.com>
Co-authored-by: xuyongliang <yongliang.xyl@alibaba-inc.com>
Co-authored-by: Thomas Kosiewski <thoma471@googlemail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>