* feat: add screenshot output config, clipboard CLI commands, and fix wait --text native path
## Summary
- Add `--screenshot-dir`, `--screenshot-quality`, and `--screenshot-format` CLI flags (with corresponding `AGENT_BROWSER_SCREENSHOT_DIR`, `AGENT_BROWSER_SCREENSHOT_QUALITY`, `AGENT_BROWSER_SCREENSHOT_FORMAT` env vars) so users can configure where and how screenshots are saved without specifying a full path every time
- Add `clipboard read`, `clipboard write <text>`, `clipboard copy`, and `clipboard paste` CLI commands, exposing the existing protocol-level clipboard handlers that were previously only accessible via JSON-RPC
- Fix `wait --text` in native mode: the CLI was emitting `selector: "text=..."` (a Playwright-style locator) which native's `querySelector` can't handle. Now emits a `text` field that correctly hits the native `wait_for_text` polling path
- Add native clipboard `copy` and `paste` support via CDP `Input.dispatchKeyEvent`, and a `write` operation to the Node.js handler
* fix: resolve CI failures in Rust formatting and TypeScript typecheck
Use string-based page.evaluate for clipboard writeText to avoid
referencing `navigator` in Node.js compilation context. Run cargo fmt
to fix formatting in commands.rs and screenshot.rs.
* fix: clipboard write captures full multi-word text
Use rest[1..].join(" ") instead of rest.get(1) so unquoted multi-word
input like `clipboard write hello world` sends the full string rather
than silently dropping everything after the first word.
* improvements
* fixes
* improvements
* improvements
Fix issue where Chrome extensions specified in the `extensions` field of `config.json` were not being loaded when launching the browser.
## Problem
Extensions configured via the `extensions` field in `config.json` were not being passed to the Chrome browser launch command, causing them to be ignored.
## Changes
- Added `!flags.extensions.is_empty()` to the launch trigger condition to ensure browser launch is triggered when extensions are configured
- Added extensions to the launch command JSON payload so they are properly passed to the browser
Fixes#726
* feat: add browserless provider integration to native browser implementation
This PR adds support for the Browserless provider to the native browser implementation, expanding the available remote browser providers from 3 to 4.
## Changes Made
- **Added `connect_browserless()` function**: Implements session creation with Browserless API using environment variables for configuration
- **Updated provider routing**: Added "browserless" case to the main provider switch statement
- **Added session cleanup**: Implemented proper session termination using the stop URL returned by Browserless
- **Updated documentation**: Modified comments and error messages to include Browserless in the supported provider list
- **Environment variable support**: Added support for configurable Browserless settings including API key, URL, browser type, TTL, and stealth mode
## Implementation Details
- Uses standard Browserless session API with POST to create sessions and DELETE to terminate
- Supports both chromium and chrome browser types with validation
- Includes proper error handling for API failures and missing configuration
- Stores the stop URL as session_id for cleanup purposes
- Follows the existing provider pattern for consistency
Fixes#744
* fix: URL-encode API key in browserless session request
Use reqwest's .query() method instead of string-formatting the token
directly into the URL, matching the Node.js implementation's use of
encodeURIComponent. Prevents malformed URLs if the API key contains
special characters.
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* Fix HTML retrieval by using browser.getLocator() for selector operations
This PR fixes an issue where HTML content retrieval was not working properly when using selectors.
**Problem:**
The `get html` command and other selector-based operations were failing because they were using `page.locator()` directly instead of the browser manager's locator method.
**Changes:**
- Updated `handleContent()` to use `browser.getLocator()` instead of `page.locator()` for HTML retrieval with selectors
- Applied the same fix to other affected functions: `handleCount()`, `handleBoundingBox()`, `handleInnerText()`, `handleInnerHtml()`, and `handleSetValue()`
- Ensures consistent locator handling across all selector-based operations
**Implementation Details:**
The fix replaces direct `page.locator(command.selector)` calls with `browser.getLocator(command.selector)` to ensure proper element selection and interaction through the browser manager's abstraction layer.
Fixes#735
* Fix remaining page.locator() calls to use browser.getLocator()
Apply the same fix to all remaining functions that were using
page.locator(command.selector) directly instead of going through
browser.getLocator(): handleWheel, handleHighlight, handleClear,
handleSelectAll, handleDispatch, handleNth, handleMultiSelect,
and handleDiffScreenshot.
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Resolves CI slowdown issues caused by limited availability of Windows containers with 8 cores by switching to the standard Windows runner image.
## Changes Made
- Updated `rust-cross` job to use `windows-latest` instead of `windows-latest-8-cores`
- Updated `windows-integration` job to use `windows-latest` instead of `windows-latest-8-cores`
- Updated `global-install` job matrix to use `windows-latest` instead of `windows-latest-8-cores`
This change trades some performance for better availability and faster CI queue times, as the standard Windows runners have much better availability than the 8-core variant.
Fixes#741
Updates the skills documentation to include the missing `vercel-sandbox` skill that was missing from both the available skills list and installation commands.
## Changes
- Added `vercel-sandbox` skill to the Available Skills list with description
- Added installation command for `vercel-sandbox` skill
- Added dedicated section for `vercel-sandbox` with key features and usage details
- Removed duplicate paragraph in the electron section
The `vercel-sandbox` skill enables running agent-browser + headless Chrome inside ephemeral Vercel Sandbox microVMs with features like snapshot startup, persistent workflows, and automatic OIDC authentication.
Fixes#712
* next.js guide
* better
* shadcn
* fixes
* fix: correct screenshot test assertion to check path instead of base64
The daemon returns { path: savePath } for screenshot commands, not base64.
* fix: cross-platform Chrome detection and gitignore hardening
- Replace hardcoded macOS Chrome path with findLocalChrome() that
searches common paths on macOS, Linux, and WSL, with a clear error
message when no Chrome is found.
- Add .env and .env*.local to .gitignore to prevent accidental
secret commits.
* fix: correct Vercel deploy button repo URL to vercel-labs/agent-browser
* clean up
* demo
* next page
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* Fix clippy warnings across CLI codebase
Fixes#653
* Fix remaining items_after_test_module clippy warnings
Move functions defined after `mod tests` blocks to before the test
modules in recording.rs and webdriver/client.rs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Fix Chrome extensions not loading by forcing headed mode when extensions present
Fixes#640
* Restore wait_or_kill() and add tests for headless+extensions logic
Restore the ChromeProcess::wait_or_kill() method that was accidentally
removed. It is still referenced by BrowserProcess in browser.rs and is
needed for graceful shutdown / cookie persistence (PR #650).
Add unit tests verifying --headless=new is omitted when extensions are
present.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix window-size leak in headed+extensions mode and remove unused channel option
- Skip --window-size=1280,720 when extensions force headed mode (native)
- Remove unexplained channel: 'chromium' from extensions launch path (TS)
- Add window-size assertion to existing extension test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When the daemon process crashes during startup (e.g., missing
Playwright), stderr was discarded via Stdio::null(), so users only
saw "Daemon failed to start (port: ...)" with no diagnostic info.
Now captures daemon stderr via Stdio::piped() and detects early process
exit with try_wait() during the startup polling loop. If the daemon
crashes, the actual error from stderr is shown to the user.
Also forwards --debug flag to the daemon process as AGENT_BROWSER_DEBUG
so debug logging works end-to-end.
Closes#56
Fixes#600
Three improvements to `--native` Chrome launching:
- `find_chrome()` now falls back to Playwright's browser cache (`~/.cache/ms-playwright/`) when no system Chrome is found
- Auto-detect containers/VMs (root, Docker, Podman, cgroups) and inject `--no-sandbox`
- Chrome stderr is now captured and included in launch error messages, with a hint when sandbox errors are detected
The native PR introduced tokio-tungstenite and reqwest with native-tls,
which depends on openssl-sys (C library). This breaks the release
workflow's cargo-zigbuild cross-compilation on Linux because zig's C
compiler can't find the system OpenSSL headers.
Switch to rustls (pure Rust TLS) which has zero C dependencies and
cross-compiles trivially. Also shrinks the dependency tree.
* fix(windows): resolve daemon startup failures and Git Bash compatibility
Three root causes behind 27 open Windows issues:
1. Path::canonicalize() returns \\?\ prefixed paths on Windows that
Node.js cannot parse, preventing daemon startup. Strip the prefix
before passing to Node. (fixes#522, #390, #56, #25, #37, #89)
2. Git Bash/MSYS2 translates Unix-style paths and resolves node to
a shell wrapper script. Use node.exe explicitly and set
MSYS_NO_PATHCONV/MSYS2_ARG_CONV_EXCL to prevent argument mangling.
(fixes#148, #108, #171)
3. postinstall fixWindowsShims() hardcoded x64 arch and did not verify
the native binary exists before rewriting shims. Now detects arch
dynamically and validates the binary path. (fixes#262)
Also:
- Error messages now show TCP port on Windows instead of Unix socket path
- Windows CI expanded to test full daemon lifecycle (open, snapshot, close)
* fix(windows): strip \\?\ prefix in auth-cli path (fixes#579)
Same canonicalize() issue as the daemon spawn path, but in
run_auth_cli() which passes the script path to Node.js.
* add security hardening features
- Add authentication vault (`auth save/login/list/show/delete`) so credentials are stored locally and never exposed to the LLM (fixes Snyk W007)
- Add `--content-boundaries` flag to wrap page-sourced output in structural markers, helping LLMs distinguish tool output from untrusted page content (fixes Snyk W011)
- Add `--allowed-domains` flag to restrict browser navigation to trusted domains
- Add `--action-policy` for static allow/deny gating of action categories, with opt-in `--confirm-actions`/`--confirm-interactive` for orchestrator or human-in-the-loop confirmation
- Add `--max-output` flag to truncate large page outputs, preventing context flooding
- New docs page at /security, updated README, SKILL.md, CLI help text, and templates
* fixes
* fixes
* fixes
* fixes
* fixes
* fixes
* fixes
* docs
* prepare v0.15
* add security hardening features
- Add authentication vault (`auth save/login/list/show/delete`) so credentials are stored locally and never exposed to the LLM (fixes Snyk W007)
- Add `--content-boundaries` flag to wrap page-sourced output in structural markers, helping LLMs distinguish tool output from untrusted page content (fixes Snyk W011)
- Add `--allowed-domains` flag to restrict browser navigation to trusted domains
- Add `--action-policy` for static allow/deny gating of action categories, with opt-in `--confirm-actions`/`--confirm-interactive` for orchestrator or human-in-the-loop confirmation
- Add `--max-output` flag to truncate large page outputs, preventing context flooding
- New docs page at /security, updated README, SKILL.md, CLI help text, and templates
* fixes
* fixes
* fixes
* fixes
* fixes
* fixes
* fixes
* docs
* add --selector flag to scroll command
The `scroll` command uses `window.scrollBy()`, which has no effect on apps
that use custom scrollable containers (e.g. a nested div with overflow-y: auto).
The backend `handleScroll` already supports a `selector` parameter, but the CLI
never exposed it. This adds `-s` / `--selector` to the `scroll` command so users
can target a specific scrollable element:
agent-browser scroll down 500 --selector "div.scroll-container"
Also fixes the backend to apply `direction`/`amount` when a selector is present
(previously those fields were only used in the no-selector branch).
Closes#501
* fixes
* feat: add --download-path option
Adds a `--download-path` flag (and `AGENT_BROWSER_DOWNLOAD_PATH` env / `downloadPath` config key) to set a default download directory for browser downloads.
Without this, Playwright stores downloads in a temp directory that is deleted when the browser closes. The new option passes through to Playwright's `downloadsPath` on `launch()` and `launchPersistentContext()`.
Fixes#507
* improvements
* fixes
* fixes
Fixes#519. Playwright defaults `colorScheme` to `light` on all new contexts, overriding the browser/OS dark mode setting. This is especially disruptive in CDP mode, where every reconnection resets the scheme. The `set media dark` command also didn't persist its choice to new tabs or pages.
- Add `--color-scheme <dark|light|no-preference>` flag, config key (`colorScheme`), and env var (`AGENT_BROWSER_COLOR_SCHEME`)
- Store the preference in `BrowserManager` and automatically apply it to all new contexts (via Playwright's context option) and all new pages (via `page.emulateMedia` in `setupPageTracking`)
- `set media dark/light` now also persists its choice for subsequent pages and tabs
`state load` always fails with "Cannot load state while browser is running" even when no browser is running, making the command completely unusable (#526).
The daemon's auto-launch logic starts a browser before `state_load` gets to handle the command. This adds `state_load` to the exclusion list alongside `launch` and `close`, so `handleStateLoad` can perform its own launch with the state file.
Fixes#491
When `--start-maximized` or `--window-size` is passed as a browser arg, Playwright's default viewport (1280x720) overrides the browser's own window sizing, making those flags have no effect on the page content.
This change auto-detects those args and sets `viewport: null` so Playwright defers to the browser's window size. Explicit viewport values still take priority.
Also allows `viewport: null` in the launch protocol for agents that want to disable viewport emulation directly.
## Summary
- Return a `ParseError` when `--headers` receives invalid JSON instead of silently dropping the headers and proceeding
- Reject `frame` commands that provide no `selector`, `name`, or `url` (previously returned `{ switched: true }` without doing anything)
- Add missing mutual exclusion check for `--cdp` + `--extension` (extensions require a local browser, not a CDP connection)
## Summary
- Fix `allowFileAccess` being silently stripped from launch commands by adding it to the Zod schema in `protocol.ts` (the `--allow-file-access` CLI flag was not reaching the browser)
- Fix `trace stop` requiring a path argument despite help text documenting it as optional -- now works with or without a path
- Fix `addscript`/`addstyle` silently succeeding when neither `content` nor `url` is provided -- now returns a validation error
- Replace hardcoded ANSI escape code with `color::error_indicator()` in `main.rs` to respect `NO_COLOR`
- Fix double-parse pattern and add descriptive expect messages in `commands.rs`
- Fix incomplete string escaping in `snapshot.ts` `buildSelector` (use `JSON.stringify` instead of manual quote escaping)
- Simplify redundant ternary in `snapshot.ts` cursor-interactive role assignment
- Sync docs changelog with CHANGELOG.md (v0.8.1 through v0.10.0)
* docs: fix 6 documentation issues (#303, #245, #186, #134, #61, #73)
Addresses six open documentation issues in a single pass:
- **#303** -- Add `npx agent-browser` usage across README, SKILL.md, docs site, and `--help` output for zero-install experience. Global install is recommended as the fastest path (native Rust CLI vs Node.js indirection with npx).
- **#245** -- Document Claude Code skill installation with `npx skills add vercel-labs/agent-browser`
- **#186** -- Split installation instructions into Global (recommended), Quick Start (npx), and Project (local dependency) sections with clear guidance on when to use each
- **#134** -- Add "Why agent-browser over playwright-mcp?" comparison table to README covering output format, element selection, protocol, sessions, performance, mobile, cloud, and streaming
- **#61** -- Add "Timeouts and Slow Pages" section to SKILL.md documenting the 60s default timeout, all `wait` variants, and guidance for slow websites
- **#73** -- Replace stale `cp node_modules/...` advice with `npx skills add`, add warning against copying SKILL.md manually, add "Session Management and Cleanup" section to SKILL.md
* remove section
* fix doc
- Moves `skills/skill-creator/` to `.agents/skills/skill-creator/` so that only the project-specific `agent-browser` skill remains in `skills/`
- Non-agent-browser skills like `skill-creator` are generic tooling and don't belong alongside the product skill, which was confusing to users
Update `setupContextTracking` in `BrowserManager` to auto-switch `activePageIndex` to newly opened tabs and invalidate the CDP session accordingly. This mirrors what `newTab()` and `newWindow()` already do for explicitly created tabs, and aligns CLI behavior with how real browsers focus newly opened tabs.
Fixes#384
Summary
- The `--exact` flag on `find role`, `find label`, and `find placeholder` was accepted by the CLI but silently dropped by the server. The Zod validation schema, TypeScript types, and action handlers all lacked the `exact` field, so it was stripped before reaching Playwright's `getByRole`, `getByLabel`, and `getByPlaceholder` calls.
- Added `exact` to the schema, types, and handler for all three locators so the flag is forwarded to Playwright as intended.
- Added tests confirming `exact: true` survives protocol parsing for `getbyrole`, `getbylabel`, and `getbyplaceholder`.
Fixes#402
* feat: add --allow-file-access flag for file:// URL support
Adds the ability to open and interact with local files using file:// URLs.
This enables use cases like viewing local PDFs, testing local HTML files,
and allowing JavaScript to access other local files via XHR.
The flag adds Chromium's --allow-file-access-from-files and --allow-file-access
launch arguments. Only supported in Chromium browsers.
Fixes#345
* fix: add cli_allow_file_access tracking to prevent spurious warning
When --allow-file-access is set via AGENT_BROWSER_ALLOW_FILE_ACCESS env var
(not CLI), don't warn about the flag being ignored when daemon is already running.
* fix: only warn about ignored flags when explicitly passed via CLI
The warning about launch-time options being ignored (when daemon is
already running) was incorrectly shown when options were set via
environment variables like AGENT_BROWSER_EXECUTABLE_PATH, even when
no CLI flag was passed.
Now the warning only appears when flags are explicitly passed on the
command line, not when values come solely from environment variables.
Fixes#372
* feat: add cursor-interactive element detection in snapshots
Add -C/--cursor flag to snapshot command that detects clickable elements
that don't have proper ARIA roles but are interactive based on:
- cursor: pointer CSS style
- onclick attribute/handler
- tabindex attribute
This helps with modern web apps that use custom divs/spans as buttons.
Fixes#366
* fix: add cursor option to getSnapshot type signature
The warning about launch-time options being ignored (when daemon is
already running) was incorrectly shown when options were set via
environment variables like AGENT_BROWSER_EXECUTABLE_PATH, even when
no CLI flag was passed.
Now the warning only appears when flags are explicitly passed on the
command line, not when values come solely from environment variables.
Fixes#372
* Add base64 input for eval command
Adds -b/--base64 flag to decode script from base64, avoiding shell escaping issues for AI agents.
* Document base64 eval in SKILL.md