## File: README.md

# agent-browser

Browser automation CLI for AI agents. Fast native Rust CLI.

[](https://skills.sh/vercel-labs/agent-browser)

## Installation

### Global Installation (recommended)

Installs the native Rust binary:

```bash
npm install agent-browser
agent-browser install  # Download Chrome from Chrome for Testing (first time only)
```

### Project Installation (local dependency)

For projects that want to pin the version in `package.json`:

```bash
npm install agent-browser
agent-browser install
```

Then use via `package.json` scripts or by invoking `agent-browser` directly.

### Homebrew (macOS)

```bash
brew install agent-browser
agent-browser install  # Download Chrome from Chrome for Testing (first time only)
```

### Cargo (Rust)

```bash
cargo install agent-browser
agent-browser install  # Download Chrome from Chrome for Testing (first time only)
```

### From Source

Requires Node.js 24+, pnpm 11+, and Rust.

```bash
git clone https://github.com/vercel-labs/agent-browser
cd agent-browser
pnpm install
pnpm build
pnpm build:native   # Requires Rust (https://rustup.rs)
pnpm link --global  # Makes agent-browser available globally
agent-browser install
```

### Linux Dependencies

On Linux, install system dependencies:

```bash
agent-browser install --with-deps
```

This exits nonzero if the package manager cannot install every required browser library.

### Updating

Upgrade to the latest version:

```bash
agent-browser upgrade
```

Detects your installation method (npm, Homebrew, or Cargo) and runs the appropriate update command automatically.

### Requirements

- **Chrome** - Run `agent-browser install` to download Chrome from [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (Google's official automation channel). Existing Chrome, Brave, Playwright, and Puppeteer installations are detected automatically. No Playwright or Node.js required for the daemon.
- **Node.js 24+ and pnpm 11+** - Only needed when building from source.
- **Rust** - Only needed when building from source (see From Source above).

## Quick Start

```bash
agent-browser open example.com
agent-browser snapshot                    # Get accessibility tree with refs
agent-browser click @e2                   # Click by ref from snapshot
agent-browser fill @e3 "test@example.com" # Fill by ref
agent-browser get text @e1                # Get text by ref
agent-browser screenshot page.png
agent-browser close
```

Clicks fail early when another element covers the target's click point, for example a consent banner or modal. Dismiss or interact with the reported covering element, then take a fresh snapshot before retrying the original ref.

Headless Chromium screenshots hide native scrollbars for consistent image output. Pass `--hide-scrollbars false` when launching to keep native scrollbars visible.

### Traditional Selectors (also supported)

```bash
agent-browser click "#submit"
agent-browser fill "#email" "test@example.com"
agent-browser find role button click --name "Submit"
```

## Commands

### Core Commands

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Get Info

```bash
agent-browser get text <sel>          # Get text content
agent-browser get html <sel>          # Get innerHTML
agent-browser get value <sel>         # Get input value
agent-browser get attr <sel> <attr>   # Get attribute
agent-browser get title               # Get page title
agent-browser get url                 # Get current URL
agent-browser get cdp-url             # Get CDP WebSocket URL (for DevTools, debugging)
agent-browser get count <sel>         # Count matching elements
agent-browser get box <sel>           # Get bounding box
agent-browser get styles <sel>        # Get computed styles
```

### Read Agent-Friendly Text

```bash
agent-browser read
agent-browser read https://example.com/article
agent-browser read https://example.com/article --filter overview
agent-browser read https://example.com/article --outline
agent-browser read https://docs.example.com --llms index --filter auth
agent-browser read https://docs.example.com --llms full --filter auth
agent-browser read example.com/article --require-md
agent-browser read https://example.com/article --json
```

`read` fetches a URL without launching Chrome. Omit the URL to read the rendered DOM of the active tab in the current browser session, including browser auth state and client-side updates. Explicit URL reads send `Accept: text/markdown` by default, try the same URL with `.md` appended when the first response is not markdown, walk ancestor paths toward `/` to find the nearest `llms.txt` for a matching docs link, print markdown or plain text when available, and fall back to readable text extracted from HTML. `--llms` and `--require-md` with no URL use the active tab URL because they depend on HTTP resources. `read` does not read `llms-full.txt` unless you ask for it.

Options: `--raw` prints the response body without HTML extraction, `--require-md` fails unless the server returns `Content-Type: text/markdown`, `--outline` prints a compact heading outline for one page, `--llms index` prints a compact nearest-ancestor `llms.txt` link list, `--llms full` reads the nearest-ancestor `llms-full.txt`, `--filter <text>` narrows page sections, llms links/sections, or outline headings, and `--timeout <ms>` changes the request timeout. Global safeguards such as `--allowed-domains`, `--content-boundaries`, and `--max-output` also apply to read fetches and output.

### Check State

```bash
agent-browser is visible <sel>        # Check if visible
agent-browser is enabled <sel>        # Check if enabled
agent-browser is checked <sel>        # Check if checked
```

### Find Elements (Semantic Locators)

```bash
agent-browser find role <role> <action> [value]       # By ARIA role
agent-browser find text <text> <action> [value]       # By text content
agent-browser find label <label> <action> [value]     # By label
agent-browser find placeholder  <action> [value]  # By placeholder
agent-browser find alt <text> <action> [value]        # By alt text
agent-browser find title <text> <action> [value]      # By title attr
agent-browser find testid  <action> [value]       # By data-testid
agent-browser find first <sel> <action> [value]       # First match
agent-browser find last <sel> <action> [value]        # Last match
agent-browser find nth <n> <sel> <action> [value]     # Nth match
```

**Actions:** `click`, `fill`, `check`, `hover`, `text`

**Options:** `--name <name>` (filter role by accessible name), `--exact` (exact, case-sensitive match; for `role` it applies to the accessible name, whose default is a case-insensitive substring)

**Examples:**

```bash
agent-browser find role button click --name "Submit"
agent-browser find role heading text --name "Skills"     # implicit roles work: <h2>=heading, =list, top-level <header>=banner
agent-browser find text "Sign In" click
agent-browser find label "Email" fill "test@test.com"
agent-browser find first ".item" click
agent-browser find nth 2 "a" text
```

### Wait

```bash
agent-browser wait <selector>         # Wait for element to be visible
agent-browser wait <ms>               # Wait for time (milliseconds)
agent-browser wait --text "Welcome"   # Wait for text to appear (substring match)
agent-browser wait --url "**/dash"    # Wait for URL pattern
agent-browser wait --load networkidle # Wait for load state
agent-browser wait --fn "window.ready === true"  # Wait for JS condition

# Wait for text/element to disappear
agent-browser wait --fn "!document.body.innerText.includes('Loading...')"
agent-browser wait "#spinner" --state hidden
```

**Load states:** `load`, `domcontentloaded`, `networkidle`

### Batch Execution

Execute multiple commands in a single invocation. Commands can be passed as quoted arguments or piped as JSON via stdin. This avoids per-command process startup overhead when running multi-step workflows.

```bash
# Argument mode: each quoted argument is a full command
agent-browser batch "open https://example.com" "snapshot -i" "screenshot"

# With --bail to stop on first error
agent-browser batch --bail "open https://example.com" "click @e1" "screenshot"

# Stdin mode: pipe commands as JSON
echo '[
  ["open", "https://example.com"],
  ["snapshot", "-i"],
  ["click", "@e1"],
  ["screenshot", "result.png"]
]' | agent-browser batch --json
```

### Clipboard

```bash
agent-browser clipboard read                      # Read text from clipboard
agent-browser clipboard write "Hello, World!"     # Write text to clipboard
agent-browser clipboard copy                      # Copy current selection (Ctrl+C)
agent-browser clipboard paste                     # Paste from clipboard (Ctrl+V)
```

### Mouse Control

```bash
agent-browser mouse move <x> <y>      # Move mouse
agent-browser mouse down [button]     # Press button (left/right/middle)
agent-browser mouse up [button]       # Release button
agent-browser mouse wheel <dy> [dx]   # Scroll wheel
```

### Browser Settings

```bash
agent-browser set viewport <w> <h> [scale]  # Set viewport size (scale for retina, e.g. 2)
agent-browser set device <name>       # Emulate device ("iPhone 14")
agent-browser set geo <lat> <lng>     # Set geolocation
agent-browser set offline [on|off]    # Toggle offline mode
agent-browser set headers <json>      # Extra HTTP headers
agent-browser set credentials   # HTTP basic auth
agent-browser set media [dark|light]  # Emulate color scheme
```

### Cookies & Storage

```bash
agent-browser cookies                 # Get all cookies
agent-browser cookies set <name> <val> # Set cookie
agent-browser cookies set --curl <file> # Import cookies from a Copy-as-cURL dump,
                                        # JSON array, or bare Cookie header (auto-detected)
agent-browser cookies clear           # Clear cookies

agent-browser storage local           # Get all localStorage
agent-browser storage local <key>     # Get specific key
agent-browser storage local set <k> <v>  # Set value
agent-browser storage local clear     # Clear all

agent-browser storage session         # Same for sessionStorage
```

### Network

```bash
agent-browser network route               # Intercept requests
agent-browser network route  --abort      # Block requests
agent-browser network route  --body <json>  # Mock response
agent-browser network route '*' --abort --resource-type script  # Block scripts only
agent-browser network unroute [url]            # Remove routes
agent-browser network requests                 # View tracked requests
agent-browser network requests --filter api    # Filter requests
agent-browser network requests --type xhr,fetch  # Filter by resource type
agent-browser network requests --method POST   # Filter by HTTP method
agent-browser network requests --status 2xx    # Filter by status (200, 2xx, 400-499)
agent-browser network request <requestId>      # View full request/response detail
agent-browser network har start                # Start HAR recording (embeds text response bodies)
agent-browser network har start --content all  # Embed all response bodies (binary as base64)
agent-browser network har start --content none # Metadata only, no bodies
agent-browser network har stop [output.har]    # Stop and save HAR (temp path if omitted)
```

### Tabs & Windows

```bash
agent-browser tab                              # List tabs (shows `tabId` and optional label)
agent-browser tab new [url]                    # New tab (optionally with URL)
agent-browser tab new --label docs [url]       # New tab with a user-assigned label
agent-browser tab <t<N>|label>                 # Switch to a tab by id or label
agent-browser tab close [t<N>|label]           # Close a tab (defaults to active)
agent-browser window new                       # New window
```

Tab ids are stable strings of the form `t1`, `t2`, `t3`. They're never reused within a session, so scripts and agents can keep referring to the same tab even after other tabs are opened or closed. Positional integers like `tab 2` are **not** accepted; the `t` prefix disambiguates handles from indices and mirrors the `@e1` convention used for element refs.

You can also assign a memorable label (`docs`, `app`, `admin`) and use it interchangeably with the id. Labels are never auto-generated and never rewritten on navigation — they're yours to name and keep:

```bash
agent-browser tab new --label docs https://docs.example.com
agent-browser tab docs               # switch to the docs tab
agent-browser snapshot               # populate refs for docs
agent-browser click @e3              # click uses docs's refs
agent-browser tab close docs         # close by label
```

`tab list --json` also reports each tab's CDP `targetId`, and target ids are accepted anywhere a tab ref is accepted (`tab <targetId>`, `tab close <targetId>`). Unlike `t<N>` ids, which are per-daemon counters, target ids stay stable across daemon restarts, so they're the right handle for scripts coordinating multiple sessions on one browser.

Switching to a tab discarded by Chrome's Memory Saver reactivates it, since a discarded tab has no renderer to drive. Reactivation reloads the discarded page and resets its unsaved state, and the switch result reports `"revived": true`. A tab whose page is paused by a JavaScript dialog is alive rather than discarded, so the switch leaves it untouched and reports `"dialogBlocked": true`; resolve the dialog with `dialog accept` or `dialog dismiss` before interacting. Closing the active tab onto a discarded successor revives it the same way and reports `"activeTabRevived": true`.

### Frames

```bash
agent-browser frame <sel>             # Switch to iframe
agent-browser frame main              # Back to main frame
```

### Dialogs

```bash
agent-browser dialog accept [text]    # Accept (with optional prompt text)
agent-browser dialog dismiss          # Dismiss
agent-browser dialog status           # Check if a dialog is currently open
```

By default, `alert` and `beforeunload` dialogs are automatically accepted so they never block the agent. `confirm` and `prompt` dialogs still require explicit handling. Use `--no-auto-dialog` (or `AGENT_BROWSER_NO_AUTO_DIALOG=1`) to disable automatic handling.

When a JavaScript dialog is pending, all command responses include a `warning` field with the dialog type and message.

### Diff

```bash
agent-browser diff snapshot                              # Compare current vs last snapshot
agent-browser diff snapshot --baseline before.txt        # Compare current vs saved snapshot file
agent-browser diff snapshot --selector "#main" --compact # Scoped snapshot diff
agent-browser diff screenshot --baseline before.png      # Visual pixel diff against baseline
agent-browser diff screenshot --baseline b.png -o d.png  # Save diff image to custom path
agent-browser diff screenshot --baseline b.png -t 0.2    # Adjust color threshold (0-1)
agent-browser diff url https://v1.com https://v2.com     # Compare two URLs (snapshot diff)
agent-browser diff url https://v1.com https://v2.com --screenshot  # Also visual diff
agent-browser diff url https://v1.com https://v2.com --wait-until networkidle  # Custom wait strategy
agent-browser diff url https://v1.com https://v2.com --selector "#main"  # Scope to element
```

### Debug

```bash
agent-browser trace start             # Start recording trace
agent-browser trace stop [path]       # Stop and save trace
agent-browser profiler start          # Start Chrome DevTools profiling
agent-browser profiler stop [path]    # Stop and save profile (.json)
agent-browser console                 # View console messages (log, error, warn, info)
agent-browser console --json          # JSON output with raw CDP args for programmatic access
agent-browser console --clear         # Clear console
agent-browser errors                  # View page errors (uncaught JavaScript exceptions)
agent-browser errors --clear          # Clear errors
agent-browser highlight <sel>         # Highlight element
agent-browser inspect                 # Open Chrome DevTools for the active page
agent-browser state save        # Save auth state
agent-browser state load        # Load auth state
agent-browser state list              # List saved state files
agent-browser state show <file>       # Show state summary
agent-browser state rename <old> <new> # Rename state file
agent-browser state clear [name]      # Clear states for session
agent-browser state clear --all       # Clear all saved states
agent-browser state clean --older-than <days>  # Delete old states
```

### Navigation

```bash
agent-browser back                    # Go back
agent-browser forward                 # Go forward
agent-browser reload                  # Reload page
agent-browser pushstate          # SPA client-side nav; auto-detects window.next.router.push,
                                      # falls back to history.pushState + popstate
```

### Pre-navigation setup

Some flows (SSR debug, auth cookies for protected origins, init scripts) need state set up *before* the first navigation. Use `open` with no URL to launch the browser, then stage cookies / routes / init scripts, then navigate. `batch` sends it all in one CLI call:

```bash
agent-browser batch \
  '["open"]' \
  '["network","route","*","--abort","--resource-type","script"]' \
  '["cookies","set","--curl","cookies.curl","--domain","localhost"]' \
  '["navigate","http://localhost:3000/target"]'
```

Without `batch` the same sequence is three commands that all reuse the same daemon (fast, but not one turn).

### React / Web Vitals

Agent-browser ships with first-class React introspection and universal Web Vitals metrics. The React commands need the React DevTools hook installed at launch; Web Vitals and pushstate are framework-agnostic.

```bash
agent-browser open --enable react-devtools    # Launch with React hook installed
agent-browser react tree                           # Full component tree
agent-browser react inspect <fiberId>              # props, hooks, state, source
agent-browser react renders start                  # Begin fiber render recording
agent-browser react renders stop [--json]          # Stop and print profile (--json for raw data)
agent-browser react suspense [--only-dynamic] [--json]  # Suspense boundaries + classifier
                                                         # --only-dynamic hides the "static" list
agent-browser vitals [url] [--json]                # LCP/CLS/TTFB/FCP/INP + hydration summary
```

Each `react ...` subcommand requires `--enable react-devtools` to have been passed at launch (the React DevTools `installHook.js` is embedded in the binary). Without it the commands error with `React DevTools hook not installed
- relaunch with --enable react-devtools`.

Works on any React app — Next.js, Remix, Vite+React, CRA, TanStack Start, React Native Web, etc. `vitals` and `pushstate` are framework-agnostic. `vitals` prints a summary by default; pass `--json` for the full structured payload.

### Accessibility audits

Run an [axe-core](https://github.com/dequelabs/axe-core) accessibility audit against the current page or a URL. The axe-core engine is embedded in the binary, so it works offline and under strict CSP. It runs private partial audits across the page's frame tree and merges serialized results without page messaging, so page-provided `window.axe` values remain intact and iframe violations retain their frame selector paths. Accessibility audits require a CDP browser and are not available with Safari or iOS WebDriver sessions.

```bash
agent-browser a11y                                 # Audit the current page
agent-browser a11y https://example.com             # Navigate, then audit
agent-browser a11y --tags wcag2a,wcag2aa           # Only rules with these axe tags
agent-browser a11y --selector "#main"              # Scope the audit to a subtree
agent-browser a11y example.com --json              # Full structured results
```

The default output lists each violation with its impact, rule id, fix guidance URL, and the CSS selectors of failing nodes:

```
url: https://example.com/
axe-core: 4.12.1  violations: 2  incomplete: 0  passes: 24

[critical] image-alt: Images must have alternative text (3 nodes)
  https://dequeuniversity.com/rules/axe/4.12/image-alt
  - img.hero
  - #logo > img
  - footer img
[serious] color-contrast: Elements must meet minimum color contrast ratio thresholds (1 node)
  https://dequeuniversity.com/rules/axe/4.12/color-contrast
  - .nav a.muted
```

`--json` returns the same data structured for automation (`counts`, `violations`, `incomplete`, each violation's `nodes` with `target`, `html`, and `failureSummary`). Each `target` preserves axe's selector path arrays, including nested arrays for shadow DOM boundaries. Rules that axe could not evaluate automatically are reported under `incomplete` for manual review.

### Init scripts

```bash
agent-browser open --init-script            # Register page init script before first navigation
                                                  # (repeatable; also AGENT_BROWSER_INIT_SCRIPTS env)
agent-browser addinitscript <js>                  # Register at runtime (returns identifier)
agent-browser removeinitscript        # Remove a previously registered init script
```

### Setup

```bash
agent-browser install                 # Download Chrome from Chrome for Testing (Google's official automation channel)
agent-browser install --with-deps     # Also install system deps (Linux)
agent-browser upgrade                 # Upgrade agent-browser to the latest version
agent-browser doctor                  # Diagnose the install and auto-clean stale daemon files
agent-browser doctor --fix            # Also run destructive repairs (reinstall Chrome, purge old state, ...)
agent-browser doctor --offline --quick  # Skip network probes and the live launch test
agent-browser mcp                     # Start an MCP stdio server
```

`doctor` checks your environment, Chrome install, daemon state, config files, encryption key, providers, network reachability, and runs a live headless browser launch test. Stale socket/pid sidecar files are auto-cleaned. Output is also available as `--json` for agents.

### Skills

```bash
agent-browser skills                  # List available skills
agent-browser skills list             # Same as above
agent-browser skills get <name>       # Output a skill's full content
agent-browser skills get <name> --full  # Include references and templates
agent-browser skills get protected-vercel-deployments  # Access protected Vercel deployments
agent-browser skills get --all        # Output every skill
agent-browser skills path [name]      # Print skill directory path
```

Serves bundled skill content that always matches the installed CLI version. AI agents use this to get current instructions rather than relying on cached copies. Set `AGENT_BROWSER_SKILLS_DIR` to override the skills directory path.

### MCP Server

```bash
agent-browser mcp
agent-browser mcp --tools all
agent-browser mcp --tools core,network,react
```

Starts a Model Context Protocol server over stdio. MCP clients launch this command as a subprocess and exchange newline-delimited JSON-RPC on stdin and stdout. The server defaults to MCP protocol 2025-11-25 and accepts older supported client protocol versions during initialization.

The default tools profile is `core`, which keeps MCP context small for everyday browser automation. Use `--tools all` for the full typed CLI parity surface, or combine profiles with commas, such as `--tools core,network,react`.

Profiles:

- `core` — Default. Navigation, snapshots, interaction, waits, reads, screenshots, JavaScript eval, close, tab basics, and profile discovery
- `network` — Network routes, request inspection, HAR, headers, credentials, offline
- `state` — Cookies, storage, auth, saved state, sessions, profiles, skills
- `debug` — Console/errors, tracing, profiling, recording, a11y audit, clipboard, plugins, doctor, dashboard, install, upgrade, chat, diff, batch, confirm/deny
- `tabs` — Back/forward/reload, tabs, windows, frames, dialogs
- `react` — React tree/inspect/renders/suspense, vitals, pushstate
- `mobile` — Viewport/device/geolocation/media, touch, swipe, mouse, keyboard
- `all` — Every MCP tool, including the full typed CLI parity surface

Common tools include:

- `agent_browser_tools_profiles`
- `agent_browser_open`
- `agent_browser_snapshot`
- `agent_browser_click`
- `agent_browser_fill`
- `agent_browser_type`
- `agent_browser_press`
- `agent_browser_wait_for_selector`
- `agent_browser_screenshot`
- `agent_browser_get_url`
- `agent_browser_eval`
- `agent_browser_close`

Each tool has typed fields such as `url`, `selector`, `text`, `key`, `session`, and `allowedDomains`, so MCP clients show meaningful approval prompts instead of raw command arrays. The common `allowedDomains` array maps to `--allowed-domains` and activates the same WebRTC containment and launch-mode restrictions. Each tool also accepts `extraArgs` for advanced CLI flags and exact CLI parity. Tool discovery is paginated and includes read-only/open-world annotations so modern MCP clients can load the large typed surface incrementally.

Example MCP client config:

```json
{
  "mcpServers": {
    "agent-browser": {
      "command": "agent-browser",
      "args": ["mcp"]
    }
  }
}
```

Full parity MCP client config:

```json
{
  "mcpServers": {
    "agent-browser": {
      "command": "agent-browser",
      "args": ["mcp", "--tools", "all"]
    }
  }
}
```

Tool invocations use the same config files and environment variables as the CLI. Use `session` in the tool arguments, or set `AGENT_BROWSER_SESSION`, to isolate browser state.

## Authentication

agent-browser provides multiple ways to persist login sessions so you don't re-authenticate every run.

### Quick summary

| Approach | Best for | Flag / Env |
|----------|----------|------------|
| **Chrome profile reuse** | Reuse your existing Chrome login state (cookies, sessions) with zero setup | `--profile <name>` / `AGENT_BROWSER_PROFILE` |
| **Persistent profile** | Full browser state (cookies, IndexedDB, service workers, cache) across restarts | `--profile ` / `AGENT_BROWSER_PROFILE` |
| **Session persistence** | Auto-save/restore cookies + localStorage from a stable session key | `--session  --restore` / `AGENT_BROWSER_RESTORE` |
| **Import from your browser** | Grab auth from a Chrome session you already logged into | `--auto-connect` + `state save` |
| **State file** | Load a previously saved state JSON on launch | `--state ` / `AGENT_BROWSER_STATE` |
| **Auth vault** | Store credentials locally (encrypted), login by name | `auth save` / `auth login` |

### Import auth from your browser

If you are already logged in to a site in Chrome, you can grab that auth state and reuse it:

```bash
# 1. Launch Chrome with remote debugging enabled
#    macOS:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222
#    Or use --auto-connect to discover an already-running Chrome

# 2. Connect and save the authenticated state
agent-browser --auto-connect state save ./my-auth.json

# 3. Use the saved auth in future sessions
agent-browser --state ./my-auth.json open https://app.example.com/dashboard

# 4. Or use --restore for automatic persistence
SESSION="$(agent-browser session id --scope worktree --prefix myapp)"
agent-browser --session "$SESSION" --restore --state ./my-auth.json open https://app.example.com/dashboard
# From now on, --session "$SESSION" --restore auto-saves/restores this state
```

> **Security notes:**
> - `--remote-debugging-port` exposes full browser control on localhost. Any local process can connect. Only use on trusted machines and close Chrome when done.
> - State files contain session tokens in plaintext. Add them to `.gitignore` and delete when no longer needed. For encryption at rest, set `AGENT_BROWSER_ENCRYPTION_KEY` (see [State Encryption](#state-encryption)).

For full details on login flows, OAuth, 2FA, cookie-based auth, and the auth vault, see the [Authentication](docs/src/app/sessions/page.mdx) docs.

## Sessions

Run multiple isolated browser instances:

```bash
# Different sessions
agent-browser --session agent1 open site-a.com
agent-browser --session agent2 open site-b.com

# Or via environment variable
AGENT_BROWSER_SESSION=agent1 agent-browser click "#btn"

# List active sessions
agent-browser session list
# Output:
# Active sessions:
# -> default
#    agent1

# Show current session
agent-browser session

# Generate a stable worktree-scoped session id
agent-browser session id --scope worktree --prefix next-dev-loop

# Inspect daemon, launch, and restore status
agent-browser session info --json
```

Each session has its own:

- Browser instance
- Cookies and storage
- Navigation history
- Authentication state

### Tab pinning

When several sessions share one Chrome over `--cdp`, each session remembers which tab it is bound to (by CDP target id, persisted across daemon restarts). A restarted daemon reattaches to the session's own tab instead of adopting whatever tab happens to be active, which is usually another session's.

By default, if the bound tab is closed the session falls back to a neighboring tab (legacy behavior). Pass `--pin-tab` (or set `AGENT_BROWSER_PIN_TAB=1`) to make the binding strict:

```bash
# Two agents sharing one Chrome, each pinned to its own tab
agent-browser --session agent1 --cdp 9222 --pin-tab open site-a.com
agent-browser --session agent2 --cdp 9222 --pin-tab open site-b.com
```

With `--pin-tab`:

- Attaching with no binding opens a fresh tab instead of adopting an existing one
- If the bound tab is closed, commands fail with a `tab_gone` error (exit code 1) instead of silently acting on another tab. JSON responses carry `"code": "tab_gone"` and recovery metadata in `data.targetId` plus optional `data.lastUrl`
- `tab list`, `tab new`, and `tab <ref>` still work in that state, so an agent can recover by binding a new tab
- Tabs opened by other sessions or the user never steal the pinned session's active tab

The flag is sticky per session: pass it once and later commands and daemon restarts keep the strict semantics. Pass `--no-pin-tab` to explicitly turn the pin off again.

`data.lastUrl` is emitted only for sanitized HTTP(S) URLs and `about:blank`. HTTP(S) credentials, query strings, and fragments are removed, and opaque URLs such as `data:` are omitted. Batch output exposes the same object as `result`.

When re-running a shared-tab script such as the repro from #1530, add `--pin-tab` to the first command for every session. Without it, `open` intentionally preserves the legacy behavior and navigates the shared active tab, so the original script still collides. The same rule applies when sessions attach with `--auto-connect` instead of `--cdp`.

## Chrome Profile Reuse

The fastest way to use your existing login state: pass a Chrome profile name to `--profile`:

```bash
# List available Chrome profiles
agent-browser profiles

# Reuse your default Chrome profile's login state
agent-browser --profile Default open https://gmail.com

# Use a named profile (by display name or directory name)
agent-browser --profile "Work" open https://app.example.com

# Or via environment variable
AGENT_BROWSER_PROFILE=Default agent-browser open https://gmail.com
```

This copies your Chrome profile to a temp directory (read-only snapshot, no changes to your original profile), so the browser launches with your existing cookies and sessions.

> **Note:** On Windows, close Chrome before using `--profile <name>` if Chrome is running, as some profile files may be locked.

## Persistent Profiles

For a persistent custom profile directory that stores state across browser restarts, pass a path to `--profile`:

```bash
# Use a persistent profile directory
agent-browser --profile ~/.myapp-profile open myapp.com

# Login once, then reuse the authenticated session
agent-browser --profile ~/.myapp-profile open myapp.com/dashboard

# Or via environment variable
AGENT_BROWSER_PROFILE=~/.myapp-profile agent-browser open myapp.com
```

The profile directory stores:

- Cookies and localStorage
- IndexedDB data
- Service workers
- Browser cache
- Login sessions

**Tip**: Use different profile paths for different projects to keep their browser state isolated.

## Session Persistence

Use `--restore` with a stable `--session` to automatically save and restore cookies and localStorage across browser restarts:

```bash
# Generate a stable id for this worktree and auto-save/load state
SESSION="$(agent-browser session id --scope worktree --prefix twitter)"
agent-browser --session "$SESSION" --restore open twitter.com

# Login once, then state persists automatically
# State files stored in ~/.agent-browser/sessions/

# Optional: validate restored state before auto-saving again
agent-browser --session "$SESSION" --restore --restore-check-text Dashboard open twitter.com
```

State is saved when the browser closes (explicit `close`, idle timeout, or daemon shutdown) and also periodically while the browser is open, so a browser window you close by hand still leaves a recent save behind. Periodic autosave waits for commands to settle, then saves at most once per `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` (default 30000; set to `0` to save only on close). Idle sessions keep saving on the same interval, so changes the page makes on its own (token refreshes, background requests) are captured too. It respects the `--restore-save` policy.

### State Encryption

Encrypt saved session data at rest with AES-256-GCM:

```bash
# Generate key: openssl rand -hex 32
export AGENT_BROWSER_ENCRYPTION_KEY=<64-char-hex-key>

# State files are now encrypted automatically
agent-browser --session secure --restore open example.com
```

| Variable                          | Description                                        |
| --------------------------------- | -------------------------------------------------- |
| `AGENT_BROWSER_RESTORE`           | Auto-save/load state persistence name              |
| `AGENT_BROWSER_RESTORE_SAVE`      | Restore save policy: `auto`, `always`, or `never`  |
| `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` | Min ms between periodic autosaves (default: 30000, 0 disables) |
| `AGENT_BROWSER_NAMESPACE`         | Namespace for daemon sockets and restore state     |
| `AGENT_BROWSER_SESSION_NAME`      | Legacy auto-save/load state persistence name       |
| `AGENT_BROWSER_ENCRYPTION_KEY`    | 64-char hex key for AES-256-GCM encryption         |
| `AGENT_BROWSER_STATE_EXPIRE_DAYS` | Auto-delete states older than N days (default: 30) |

## Security

agent-browser includes security features for safe AI agent deployments. All features are opt-in, and existing workflows are unaffected until you explicitly enable a feature:

- **Authentication Vault**: Store credentials locally (always encrypted), reference by name. The LLM never sees passwords. `auth login` navigates with `load` and then waits for login form selectors to appear (SPA-friendly, timeout follows the default action timeout). A key is auto-generated at `~/.agent-browser/.encryption-key` if `AGENT_BROWSER_ENCRYPTION_KEY` is not set: `echo "pass" | agent-browser auth save github --url https://github.com/login --username user --password-stdin` then `agent-browser auth login github`
- **Plugin System**: Extend agent-browser with external executable plugins. Plugins run out-of-process over the `agent-browser.plugin.v1` stdio JSON protocol and declare capabilities such as `credential.read`, `browser.provider`, `launch.mutate`, or `command.run`.
- **Content Boundary Markers**: Wrap page output in delimiters so LLMs can distinguish tool output from untrusted content: `--content-boundaries`
- **Domain Allowlist**: Restrict navigation to trusted domains (wildcards like `*.example.com` also match the bare domain): `--allowed-domains "example.com,*.example.com"`. Sub-resource requests (scripts, images, fetch), WebSocket/EventSource connections, and `sendBeacon` calls to non-allowed domains are blocked. WebRTC peer connections are disabled in supported Chromium sessions while the allowlist is active to prevent STUN, TURN, and DNS traffic from bypassing HTTP interception. Dedicated and shared workers are guarded with a bootstrap wrapper; if a page CSP forbids that wrapper, the worker fails closed rather than running without the allowlist guard. Pre-existing CDP sessions, auto-connect, Chrome profiles, direct-page provider plugins, agent-browser restore or state-file replay, raw Chrome args that select profiles, restore sessions, or open startup pages, iOS, and Safari reject this option because agent-browser cannot install equivalent containment before page scripts run. Include any CDN domains your target pages depend on (e.g., `*.cdn.example.com`).
- **Action Policy**: Gate destructive actions with a static policy file: `--action-policy ./policy.json`
- **Action Confirmation**: Require explicit approval for sensitive action categories: `--confirm-actions eval,download`
- **Output Length Limits**: Prevent context flooding: `--max-output 50000`

| Variable                            | Description                              |
| ----------------------------------- | ---------------------------------------- |
| `AGENT_BROWSER_CONTENT_BOUNDARIES`  | Wrap page output in boundary markers     |
| `AGENT_BROWSER_MAX_OUTPUT`          | Max characters for page output           |
| `AGENT_BROWSER_ALLOWED_DOMAINS`     | Comma-separated allowed domain patterns; requires a fresh controllable browser context without profile/session startup args, restore/state replay, or direct-page provider plugins |
| `AGENT_BROWSER_ACTION_POLICY`       | Path to action policy JSON file          |
| `AGENT_BROWSER_CONFIRM_ACTIONS`     | Action categories requiring confirmation |
| `AGENT_BROWSER_CONFIRM_INTERACTIVE` | Enable interactive confirmation prompts  |
| `AGENT_BROWSER_PLUGINS`             | JSON plugin registry override            |

See [Security documentation](https://agent-browser.dev/security) for details.

### Plugin System

Plugins let third-party tools integrate without becoming built-in agent-browser dependencies. Add a plugin from npm or GitHub:

```bash
agent-browser plugin add agent-browser-plugin-captcha
agent-browser plugin add @company/agent-browser-plugin-vault --name vault
agent-browser plugin add org/agent-browser-plugin-cloud-browser
```

References are resolved by shape: `name` uses npm, `@scope/name` uses npm, and `owner/repo` uses GitHub. `plugin add` writes `./agent-browser.json` by default; use `--global` for `~/.agent-browser/config.json`.

Plugin packages should support `plugin.manifest` so `plugin add` can discover their name and capabilities automatically. If a plugin does not support manifests, pass `--capability <name>` during add.

Plugins can also be configured manually in `agent-browser.json`:

```json
{
  "plugins": [
    {
      "name": "vault",
      "command": "agent-browser-plugin-vault",
      "capabilities": ["credential.read"]
    },
    {
      "name": "cloud-browser",
      "command": "agent-browser-plugin-cloud-browser",
      "capabilities": ["browser.provider"]
    },
    {
      "name": "stealth",
      "command": "agent-browser-plugin-stealth",
      "capabilities": ["launch.mutate"]
    },
    {
      "name": "captcha",
      "command": "agent-browser-plugin-captcha",
      "capabilities": ["command.run", "captcha.solve"]
    }
  ]
}
```

Inspect configured plugins:

```bash
agent-browser plugin list
agent-browser plugin show vault
```

Use a credential provider plugin for one login:

```bash
agent-browser auth login my-app --credential-provider vault --item "My App"
agent-browser auth login my-app --credential-provider vault --item "My App" --url https://app.example.com/login --username-selector "#email" --password-selector "#password" --submit-selector "button[type=submit]"
```

Use a browser provider plugin:

```bash
agent-browser --provider cloud-browser open https://example.com
```

Use a launch mutator plugin for stealth or local launch customization. The plugin can append Chrome args, extensions, and init scripts before the browser starts:

```bash
agent-browser open https://example.com
```

Use a generic plugin command for domain-specific tools such as CAPTCHA solvers:

```bash
agent-browser plugin run captcha captcha.solve --payload '{"siteKey":"...","url":"https://example.com"}'
```

The protocol request always includes `protocol`, `type`, `capability`, and `request`. A credential plugin receives `credential.resolve`, a browser provider receives `browser.launch`, a launch mutator receives `launch.mutate`, and generic commands receive the supplied request type. `plugin run` is for `command.run` and custom capabilities; core capabilities and protocol request types use their dedicated command paths. agent-browser keeps browser automation, redaction-sensitive output, and policy enforcement in core.

Gate plugin access by capability action:

```bash
agent-browser --confirm-actions plugin:vault:credential.read auth login my-app --credential-provider vault --item "My App"
agent-browser --confirm-actions plugin:cloud-browser:browser.provider --provider cloud-browser open https://example.com
agent-browser --confirm-actions plugin:stealth:launch.mutate open https://example.com
```

Do not put vault tokens or passwords in plugin command args. Use the vault vendor's own login/session mechanism or environment outside agent-browser config.

## Snapshot Options

The `snapshot` command supports filtering to reduce output size:

```bash
agent-browser snapshot                    # Full accessibility tree
agent-browser snapshot -i                 # Interactive elements only (buttons, inputs, links)
agent-browser snapshot -i --urls          # Interactive elements with link URLs
agent-browser snapshot -c                 # Compact (remove empty structural elements)
agent-browser snapshot -d 3               # Limit depth to 3 levels
agent-browser snapshot -s "#main"         # Scope to CSS selector
agent-browser snapshot -i -c -d 5         # Combine options
```

| Option                 | Description                                                             |
| ---------------------- | ----------------------------------------------------------------------- |
| `-i, --interactive`    | Only show interactive elements (buttons, links, inputs)                 |
| `-u, --urls`           | Include href URLs for link elements                                     |
| `-c, --compact`        | Remove empty structural elements                                        |
| `-d, --depth <n>`      | Limit tree depth                                                        |
| `-s, --selector <sel>` | Scope to CSS selector                                                   |

## Annotated Screenshots

The `--annotate` flag overlays numbered labels on interactive elements in the screenshot. Each label `[N]` corresponds to ref `@eN`, so the same refs work for both visual and text-based workflows.

Annotated screenshots are supported on the CDP-backed browser path (Chrome/Lightpanda). The Safari/WebDriver backend does not yet support `--annotate`.

```bash
agent-browser screenshot --annotate
# -> Screenshot saved to /tmp/screenshot-2026-02-17T12-00-00-abc123.png
#    [1] @e1 button "Submit"
#    [2] @e2 link "Home"
#    [3] @e3 textbox "Email"
```

After an annotated screenshot, refs are cached so you can immediately interact with elements:

```bash
agent-browser screenshot --annotate ./page.png
agent-browser click @e2     # Click the "Home" link labeled [2]
```

This is useful for multimodal AI models that can reason about visual layout, unlabeled icon buttons, canvas elements, or visual state that the text accessibility tree cannot capture.

## Options

| Option | Description |
|--------|-------------|
| `--session <name>` | Use isolated session (or `AGENT_BROWSER_SESSION` env) |
| `--restore [name]` | Auto-save/restore session state. Bare `--restore` uses `--session` as the key |
| `--restore-save ` | Restore save policy: `auto`, `always`, or `never` |
| `--restore-check-url <glob>` | Validate restored state against a URL pattern |
| `--restore-check-text <text>` | Validate restored state against page text |
| `--restore-check-fn <js>` | Validate restored state against a truthy JavaScript expression |
| `--namespace <name>` | Isolate daemon sockets and restore-state directories |
| `--session-name <name>` | Legacy alias for restore persistence key |
| `--profile <name\|path>` | Chrome profile name or persistent directory path (or `AGENT_BROWSER_PROFILE` env) |
| `--state ` | Load storage state from JSON file (or `AGENT_BROWSER_STATE` env) |
| `--headers <json>` | Set HTTP headers scoped to the URL's origin |
| `--executable-path ` | Custom browser executable (or `AGENT_BROWSER_EXECUTABLE_PATH` env) |
| `--extension ` | Load browser extension (repeatable; or `AGENT_BROWSER_EXTENSIONS` env) |
| `--init-script ` | Register a page init script before the first navigation (repeatable; or `AGENT_BROWSER_INIT_SCRIPTS` env) |
| `--enable <feature>` | Built-in init scripts: `react-devtools` (repeatable or comma-list; or `AGENT_BROWSER_ENABLE` env) |
| `--args <args>` | Browser launch args, comma or newline separated (or `AGENT_BROWSER_ARGS` env) |
| `--user-agent ` | Custom User-Agent string (or `AGENT_BROWSER_USER_AGENT` env) |
| `--proxy ` | Proxy server URL with optional auth (or `AGENT_BROWSER_PROXY` env) |
| `--proxy-bypass <hosts>` | Hosts to bypass proxy (or `AGENT_BROWSER_PROXY_BYPASS` env) |
| `--ignore-https-errors` | Ignore HTTPS certificate errors (useful for self-signed certs) |
| `--ca-cert ` | Trust a CA certificate or PEM bundle for locally launched Chromium on Linux; later commands in the same running session retain it when omitted (or `AGENT_BROWSER_CA_CERT` env) |
| `--no-ca-cert` | Clear CA trust retained by the running browser session (or `AGENT_BROWSER_CLEAR_CA_CERT`) |
| `--allow-file-access` | Allow file:// URLs to access local files (Chromium only) |
| `--hide-scrollbars ` | Hide native scrollbars in headless Chromium screenshots, enabled by default (or `AGENT_BROWSER_HIDE_SCROLLBARS` env) |
| `-p, --provider <name>` | Browser provider, including configured `browser.provider` plugins (or `AGENT_BROWSER_PROVIDER` env) |
| `--device <name>` | iOS device name, e.g. "iPhone 15 Pro" (or `AGENT_BROWSER_IOS_DEVICE` env) |
| `--json` | JSON output (for agents) |
| `--annotate` | Annotated screenshot with numbered element labels (or `AGENT_BROWSER_ANNOTATE` env) |
| `--screenshot-dir ` | Default screenshot output directory (or `AGENT_BROWSER_SCREENSHOT_DIR` env) |
| `--screenshot-quality <n>` | JPEG quality 0-100 (or `AGENT_BROWSER_SCREENSHOT_QUALITY` env) |
| `--screenshot-format <fmt>` | Screenshot format: `png`, `jpeg` (or `AGENT_BROWSER_SCREENSHOT_FORMAT` env) |
| `--headed` | Show browser window (not headless) (or `AGENT_BROWSER_HEADED` env) |
| `--webgpu` | Enable WebGPU; SwiftShader software Vulkan on Linux, no GPU required (or `AGENT_BROWSER_WEBGPU` env) |
| `--cdp ` | Connect via Chrome DevTools Protocol (port or WebSocket URL) |
| `--auto-connect` | Auto-discover and connect to running Chrome (or `AGENT_BROWSER_AUTO_CONNECT` env) |
| `--pin-tab` | Pin the session to its bound tab; fail with `tab_gone` instead of falling back to another tab (or `AGENT_BROWSER_PIN_TAB` env) |
| `--no-pin-tab` | Disable a sticky pin previously enabled with `--pin-tab` |
| `--color-scheme <scheme>` | Color scheme: `dark`, `light`, `no-preference` (or `AGENT_BROWSER_COLOR_SCHEME` env) |
| `--download-path ` | Default download directory (or `AGENT_BROWSER_DOWNLOAD_PATH` env) |
| `--content-boundaries` | Wrap page output in boundary markers for LLM safety (or `AGENT_BROWSER_CONTENT_BOUNDARIES` env) |
| `--max-output <chars>` | Truncate page output to N characters (or `AGENT_BROWSER_MAX_OUTPUT` env) |
| `--allowed-domains <list>` | Comma-separated allowed domain patterns; also disables WebRTC peer connections in supported Chromium sessions and rejects CDP, auto-connect, Chrome profiles, restore/state replay, direct-page provider plugins, unsafe startup `--args`, iOS, and Safari (or `AGENT_BROWSER_ALLOWED_DOMAINS` env) |
| `--action-policy ` | Path to action policy JSON file (or `AGENT_BROWSER_ACTION_POLICY` env) |
| `--confirm-actions <list>` | Action categories requiring confirmation (or `AGENT_BROWSER_CONFIRM_ACTIONS` env) |
| `--confirm-interactive` | Interactive confirmation prompts; auto-denies if stdin is not a TTY (or `AGENT_BROWSER_CONFIRM_INTERACTIVE` env) |
| `--engine <name>` | Browser engine: `chrome` (default), `lightpanda` (or `AGENT_BROWSER_ENGINE` env) |
| `--idle-timeout <time>` | Shut down the daemon after inactivity (`10s`, `3m`, `1h`, or raw ms). Defaults to `1h`; use `0` to disable (or `AGENT_BROWSER_IDLE_TIMEOUT_MS` env) |
| `--no-auto-dialog` | Disable automatic dismissal of `alert`/`beforeunload` dialogs (or `AGENT_BROWSER_NO_AUTO_DIALOG` env) |
| `--model <name>` | AI model for chat command (or `AI_GATEWAY_MODEL` env) |
| `-v`, `--verbose` | Show tool commands and their raw output (chat) |
| `-q`, `--quiet` | Show only AI text responses, hide tool calls (chat) |
| `--config ` | Use a custom config file (or `AGENT_BROWSER_CONFIG` env) |
| `--debug` | Debug output |

## Observability Dashboard

Monitor agent-browser sessions in real time with a local web dashboard showing a live viewport and command activity feed.

```bash
# Start the dashboard server (runs in background on port 4848)
agent-browser dashboard start
agent-browser dashboard start --port 8080   # Custom port

# All sessions are automatically visible in the dashboard
agent-browser open example.com

# Stop the dashboard
agent-browser dashboard stop
```

The dashboard runs as a standalone background process on port 4848, independent of browser sessions. It stays available even when no sessions are running, and it works from `http://localhost:4848` or a proxied/forwarded URL that reaches the dashboard server, such as `https://dashboard.agent-browser.localhost` or a Coder workspace URL. The browser stays on the dashboard origin; session-specific tabs, status, and stream traffic are proxied internally, so session ports do not need to be exposed.

The dashboard displays:
- **Live viewport**: real-time JPEG frames from the browser
- **Activity feed**: chronological command/result stream with timing and expandable details
- **Console output**: browser console messages (log, warn, error)
- **Session creation**: create new sessions from the UI with local engines (Chrome, Lightpanda) or cloud providers (AgentCore, Browserbase, Browserless, Browser Use, Kernel)
- **AI Chat**: chat with an AI assistant directly in the dashboard (requires Vercel AI Gateway configuration)

### AI Chat

The dashboard includes an optional AI chat panel powered by the Vercel AI Gateway. The same functionality is available directly from the CLI via the `chat` command. Set these environment variables to enable AI chat:

```bash
export AI_GATEWAY_API_KEY=gw_your_key_here
export AI_GATEWAY_MODEL=anthropic/claude-sonnet-4.6           # optional, this is the default
export AI_GATEWAY_URL=https://ai-gateway.vercel.sh           # optional, this is the default
```

**CLI usage:**

```bash
agent-browser chat "open google.com and search for cats"     # Single-shot
agent-browser chat                                           # Interactive REPL
agent-browser -q chat "summarize this page"                  # Quiet mode (text only)
agent-browser -v chat "fill in the login form"               # Verbose (show command output)
agent-browser --model openai/gpt-4o chat "take a screenshot" # Override model
```

The `chat` command translates natural language instructions into agent-browser commands, executes them, and streams the AI response. In interactive mode, type `quit` to exit. Use `--json` for structured output suitable for agent consumption.

**Dashboard usage:**

The Chat tab is always visible in the dashboard. When `AI_GATEWAY_API_KEY` is set, the Rust server proxies requests to the gateway and streams responses back using the Vercel AI SDK's UI Message Stream protocol. Without the key, sending a message shows an error inline.

## Configuration

Create an `agent-browser.json` file to set persistent defaults instead of repeating flags on every command.

**Locations (lowest to highest priority):**

1. `~/.agent-browser/config.json`: user-level defaults
2. `./agent-browser.json`: project-level overrides (in working directory)
3. `AGENT_BROWSER_*` environment variables override config file values
4. CLI flags override everything

**Example `agent-browser.json`:**

```json
{
  "headed": true,
  "proxy": "http://localhost:8080",
  "profile": "./browser-data",
  "userAgent": "my-agent/1.0",
  "hideScrollbars": false,
  "ignoreHttpsErrors": true,
  "plugins": [
    {
      "name": "vault",
      "command": "agent-browser-plugin-vault",
      "capabilities": ["credential.read"]
    }
  ]
}
```

**Example proxy CA configuration:**

```json
{
  "proxy": "http://localhost:8080",
  "caCert": "/etc/ssl/certs/proxy-ca.crt"
}
```

`caCert` remains effective for later commands in the same running session. Use `"clearCaCert": true`, `--no-ca-cert`, or `AGENT_BROWSER_CLEAR_CA_CERT=1` to remove it. Setting, changing, or clearing the CA relaunches Chromium without restarting the daemon. Repeating the same certificate content, including from a different path, reuses the current browser. On Linux, `agent-browser install --with-deps` installs the required `certutil`; otherwise install `libnss3-tools` on Debian/Ubuntu or `nss-tools` on RPM Linux.

Use `--config ` or `AGENT_BROWSER_CONFIG` to load a specific config file instead of the defaults:

```bash
agent-browser --config ./ci-config.json open example.com
AGENT_BROWSER_CONFIG=./ci-config.json agent-browser open example.com
```

All options from the table above can be set in the config file using camelCase keys (e.g., `--executable-path` becomes `"executablePath"`, `--proxy-bypass` becomes `"proxyBypass"`). Plugins are configured with the `"plugins"` array shown above. Unknown keys are ignored for forward compatibility.

A [JSON Schema](agent-browser.schema.json) is available for IDE autocomplete and validation. Add a `$schema` key to your config file to enable it:

```json
{
  "$schema": "https://agent-browser.dev/schema.json",
  "headed": true
}
```

Boolean flags accept an optional `true`/`false` value to override config settings. For example, `--headed false` disables `"headed": true` from config. A bare `--headed` is equivalent to `--headed true`.

Auto-discovered config files that are missing are silently ignored. If `--config ` points to a missing or invalid file, agent-browser exits with an error. Extensions from user and project configs are merged (concatenated), not replaced.

> **Tip:** If your project-level `agent-browser.json` contains environment-specific values (paths, proxies), consider adding it to `.gitignore`.

## Default Timeout

The default timeout for standard operations (clicks, waits, fills, etc.) is 25 seconds. This is intentionally below the CLI's 30-second IPC read timeout so that the daemon returns a proper error instead of the CLI timing out with EAGAIN.

Override the default timeout via environment variable:

```bash
# Set a longer timeout for slow pages (in milliseconds)
export AGENT_BROWSER_DEFAULT_TIMEOUT=45000
```

> **Note:** Setting this above 30000 (30s) may cause EAGAIN errors on slow operations because the CLI's read timeout will expire before the daemon responds. The CLI retries transient errors automatically, but response times will increase.

| Variable                        | Description                              |
| ------------------------------- | ---------------------------------------- |
| `AGENT_BROWSER_DEFAULT_TIMEOUT` | Default operation timeout in ms (default: 25000) |

## Selectors

### Refs (Recommended for AI)

Refs provide deterministic element selection from snapshots:

```bash
# 1. Get snapshot with refs
agent-browser snapshot
# Output:
# - heading "Example Domain" [ref=e1] [level=1]
# - button "Submit" [ref=e2]
# - textbox "Email" [ref=e3]
# - link "Learn more" [ref=e4]

# 2. Use refs to interact
agent-browser click @e2                   # Click the button
agent-browser fill @e3 "test@example.com" # Fill the textbox
agent-browser get text @e1                # Get heading text
agent-browser hover @e4                   # Hover the link
```

When a ref click is blocked by an overlay, the error includes the covering element, such as `covered by `. Click the banner or dialog control first, then run `snapshot` again before reusing refs.

**Why use refs?**

- **Deterministic**: Ref points to exact element from snapshot
- **Fast**: No DOM re-query needed
- **AI-friendly**: Snapshot + ref workflow is optimal for LLMs

### CSS Selectors

```bash
agent-browser click "#id"
agent-browser click ".class"
agent-browser click "div > button"
```

### Text & XPath

```bash
agent-browser click "text=Submit"
agent-browser click "xpath=//button"
```

### Semantic Locators

```bash
agent-browser find role button click --name "Submit"
agent-browser find label "Email" fill "test@test.com"
```

## Agent Mode

Use `--json` for machine-readable output:

```bash
agent-browser snapshot --json
# Returns: {"success":true,"data":{"snapshot":"...","refs":{"e1":{"role":"heading","name":"Title"},...}}}

agent-browser get text @e1 --json
agent-browser is visible @e2 --json
```

### Optimal AI Workflow

```bash
# 1. Navigate and get snapshot
agent-browser open example.com
agent-browser snapshot -i --json   # AI parses tree and refs

# 2. AI identifies target refs from snapshot
# 3. Execute actions using refs
agent-browser click @e2
agent-browser fill @e3 "input text"

# 4. Get new snapshot if page changed
agent-browser snapshot -i --json
```

### Command Chaining

Commands can be chained with `&&` in a single shell invocation. The browser persists via a background daemon, so chaining is safe and more efficient:

```bash
# Open, wait for load, and snapshot in one call
agent-browser open example.com && agent-browser wait --load networkidle && agent-browser snapshot -i

# Chain multiple interactions
agent-browser fill @e1 "user@example.com" && agent-browser fill @e2 "pass" && agent-browser click @e3

# Navigate and screenshot
agent-browser open example.com && agent-browser wait --load networkidle && agent-browser screenshot page.png
```

Use `&&` when you don't need intermediate output. Run commands separately when you need to parse output first (e.g., snapshot to discover refs before interacting).

## Headed Mode

Show the browser window for debugging:

```bash
agent-browser open example.com --headed
```

This opens a visible browser window instead of running headless.

On Linux hosts with no display (servers, containers), `--headed` still works: when `DISPLAY` is unset and Xvfb is installed, agent-browser starts a private virtual display for the browser and cleans it up on close (opt out with `AGENT_BROWSER_NO_XVFB=1`). Needed for [WebGPU screenshots](#webgpu), and useful for extensions that misbehave headless.

> **Note:** Browser extensions work in both headed and headless mode (Chrome's `--headless=new`).

## WebGPU

Headless Chrome does not expose WebGPU by default, so pages using it (three.js `WebGPURenderer`, Babylon.js, etc.) silently render black. The `--webgpu` flag enables a launch preset that makes WebGPU work, including in GPU-less containers and CI:

```bash
agent-browser --webgpu open https://my-webgpu-app.example.com
agent-browser screenshot app.png
```

On macOS and Windows this uses the hardware Metal/D3D backend. On Linux it routes WebGPU through SwiftShader's software Vulkan (no GPU needed), which requires the system Vulkan loader and Mesa ICD:

```bash
apt-get install -y libvulkan1 mesa-vulkan-drivers
```

One upstream caveat: headless Chrome cannot capture WebGPU canvas presentation in screenshots on Windows and Linux (rendering and in-page readbacks work; the capture is black). Screenshots of WebGPU pages work headless on macOS; on Windows run `--headed` in a logged-in desktop session; on Linux just add `--headed` — when no `DISPLAY` is set and Xvfb is installed, agent-browser starts a private virtual display automatically (opt out with `AGENT_BROWSER_NO_XVFB=1`).

Verify the full pipeline (adapter, render pass, and screenshot capture) with:

```bash
agent-browser doctor --webgpu
```

Notes for WebGPU pages:

- WebGPU only exists in secure contexts (`https://`, `http://localhost`, or `file://`).
- three.js `WebGPURenderer` initializes asynchronously and silently falls back to WebGL2 when no adapter is available — wait for the app to render its first frame before taking a screenshot.
- To prefer a real GPU on Linux instead of SwiftShader, override both the Vulkan driver and the adapter with `--args "--use-vulkan=native,--use-webgpu-adapter=default"` (user args win over the preset; `--use-webgpu-adapter` alone still enumerates only SwiftShader).

See the [WebGPU docs page](https://agent-browser.dev/webgpu) for the full platform matrix and container recipe.

## Authenticated Sessions

Use `--headers` to set HTTP headers for a specific origin, enabling authentication without login flows:

```bash
# Headers are scoped to api.example.com only
agent-browser open api.example.com --headers '{"Authorization": "Bearer <token>"}'

# Requests to api.example.com include the auth header
agent-browser snapshot -i --json
agent-browser click @e2

# Navigate to another domain - headers are NOT sent (safe!)
agent-browser open other-site.com
```

This is useful for:

- **Skipping login flows** - Authenticate via headers instead of UI
- **Switching users** - Start new sessions with different auth tokens
- **API testing** - Access protected endpoints directly
- **Security** - Headers are scoped to the origin, not leaked to other domains

To set headers for multiple origins, use `--headers` with each `open` command:

```bash
agent-browser open api.example.com --headers '{"Authorization": "Bearer token1"}'
agent-browser open api.acme.com --headers '{"Authorization": "Bearer token2"}'
```

For global headers (all domains), use `set headers`:

```bash
agent-browser set headers '{"X-Custom-Header": "value"}'
```

## Custom Browser Executable

Use a custom browser executable instead of the bundled Chromium. This is useful for:

- **Serverless deployment**: Use lightweight Chromium builds like `@sparticuz/chromium` (~50MB vs ~684MB)
- **System browsers**: Use an existing Chrome/Chromium installation
- **Custom builds**: Use modified browser builds

### CLI Usage

```bash
# Via flag
agent-browser --executable-path /path/to/chromium open example.com

# Via environment variable
AGENT_BROWSER_EXECUTABLE_PATH=/path/to/chromium agent-browser open example.com
```

### Serverless (Vercel)

Run agent-browser + Chrome in an ephemeral Vercel Sandbox microVM. No external server needed:

```typescript
import { runAgentBrowserCommand, withAgentBrowserSandbox } from "@agent-browser/sandbox/vercel";

const result = await withAgentBrowserSandbox(async (sandbox) => {
  await runAgentBrowserCommand(sandbox, ["open", "https://example.com"]);
  return runAgentBrowserCommand(sandbox, ["screenshot"]);
});
```

Install `@agent-browser/sandbox` and `@vercel/sandbox` in the consuming app. See the [sandbox helper example](examples/sandbox/) for minimal Vercel Sandbox usage, or the [environments example](examples/environments/) for a full UI demo with a deploy-to-Vercel button.

Fresh Vercel and eve sandboxes install Chromium system dependencies by default. Pass `installSystemDependencies: false` only when your sandbox image already includes those libraries.

### eve extension

Give an [eve](https://eve.dev) agent the full browser tool set by mounting the [`@agent-browser/eve`](packages/@agent-browser/eve/) extension:

```typescript
// agent/extensions/browser.ts
import browser from "@agent-browser/eve";

export default browser({});
```

This composes ~20 namespaced tools into the agent — `browser__navigate`, `browser__snapshot`, `browser__click`, `browser__fill`, `browser__find`, `browser__screenshot`, and more — all running agent-browser inside the agent's sandbox. agent-browser installs automatically on first use; pre-install it in `agent/sandbox.ts` with the `@agent-browser/eve/sandbox` helpers to bake the cost into the sandbox template instead. Configuration (domain allowlists, output limits, session naming) and per-tool overrides are covered in the [package README](packages/@agent-browser/eve/README.md), and the [eve example](examples/eve/) is a complete app with the extension mounted.

### Serverless (AWS Lambda)

```typescript
import chromium from '@sparticuz/chromium';
import { execSync } from 'child_process';

export async function handler() {
  const executablePath = await chromium.executablePath();
  const result = execSync(
    `AGENT_BROWSER_EXECUTABLE_PATH=${executablePath} agent-browser open https://example.com && agent-browser snapshot -i --json`,
    { encoding: 'utf-8' }
  );
  return JSON.parse(result);
}
```

## Local Files

Open and interact with local files (PDFs, HTML, etc.) using `file://` URLs:

```bash
# Enable file access (required for JavaScript to access local files)
agent-browser --allow-file-access open file:///path/to/document.pdf
agent-browser --allow-file-access open file:///path/to/page.html

# Take screenshot of a local PDF
agent-browser --allow-file-access open file:///Users/me/report.pdf
agent-browser screenshot report.png
```

The `--allow-file-access` flag adds Chromium flags (`--allow-file-access-from-files`, `--allow-file-access`) that allow `file://` URLs to:

- Load and render local files
- Access other local files via JavaScript (XHR, fetch)
- Load local resources (images, scripts, stylesheets)

**Note:** This flag only works with Chromium. For security, it's disabled by default.

## CDP Mode

Connect to an existing browser via Chrome DevTools Protocol:

```bash
# Start Chrome with: google-chrome --remote-debugging-port=9222

# Connect once, then run commands without --cdp
agent-browser connect 9222
agent-browser snapshot
agent-browser tab
agent-browser close

# Or pass --cdp on each command
agent-browser --cdp 9222 snapshot

# Connect to remote browser via WebSocket URL
agent-browser --cdp "wss://your-browser-service.com/cdp?token=..." snapshot
```

The `--cdp` flag accepts either:

- A port number (e.g., `9222`) for local connections via `http://localhost:{port}`
- A full WebSocket URL (e.g., `wss://...` or `ws://...`) for remote browser services

This enables control of:

- Electron apps
- Chrome/Chromium instances with remote debugging
- WebView2 applications
- Any browser exposing a CDP endpoint

### Auto-Connect

Use `--auto-connect` to automatically discover and connect to a running Chrome instance without specifying a port:

```bash
# Auto-discover running Chrome with remote debugging
agent-browser --auto-connect open example.com
agent-browser --auto-connect snapshot

# Or via environment variable
AGENT_BROWSER_AUTO_CONNECT=1 agent-browser snapshot
```

Auto-connect discovers Chrome by:

1. Reading Chrome's `DevToolsActivePort` file from the default user data directory
2. Falling back to probing common debugging ports (9222, 9229)
3. If HTTP-based discovery (`/json/version`, `/json/list`) fails, falling back to a direct WebSocket connection

This is useful when:

- Chrome 144+ has remote debugging enabled via `chrome://inspect/#remote-debugging` (which uses a dynamic port)
- You want a zero-configuration connection to your existing browser
- You don't want to track which port Chrome is using

## Streaming (Browser Preview)

Stream the browser viewport via WebSocket for live preview or "pair browsing" where a human can watch and interact alongside an AI agent.

### Streaming

Every session automatically starts a WebSocket stream server on an OS-assigned port. Use `stream status` to see the bound port and connection state:

```bash
agent-browser stream status
```

To bind to a specific port, set `AGENT_BROWSER_STREAM_PORT`:

```bash
AGENT_BROWSER_STREAM_PORT=9223 agent-browser open example.com
```

Frame encoding is daemon-wide:

| Variable | Default | Description |
|---|---|---|
| `AGENT_BROWSER_STREAM_QUALITY` | `80` | JPEG quality, 0 to 100 |
| `AGENT_BROWSER_STREAM_MAX_WIDTH` | the viewport | Caps frame width in pixels |
| `AGENT_BROWSER_STREAM_MAX_HEIGHT` | the viewport | Caps frame height in pixels |

Width and height cap the encoded frame and leave the page size alone, so a portrait or HiDPI viewport keeps its resolution unless you cap it. The live stream requests jpeg. An explicit `screencast_start` reconfigures the same screencast, so a client can see the format change mid-stream. On a busy page at 1280x720, quality 80 costs about 54 KB per frame, quality 20 about 25 KB, and quality 20 at 640x360 about 9 KB.

```bash
# Cheaper frames for a constrained link
AGENT_BROWSER_STREAM_QUALITY=20 \
AGENT_BROWSER_STREAM_MAX_WIDTH=640 \
AGENT_BROWSER_STREAM_MAX_HEIGHT=360 \
agent-browser open example.com
```

You can also manage streaming at runtime with `stream enable`, `stream disable`, and `stream status`:

```bash
agent-browser stream enable --port 9223   # Re-enable on a specific port
agent-browser stream disable              # Stop streaming for the session
```

The WebSocket server streams the browser viewport and accepts input events.

### WebSocket Protocol

Connect to `ws://localhost:9223` to receive frames and send input:

**Receive frames:**

```json
{
  "type": "frame",
  "seq": 41,
  "data": "",
  "metadata": {
    "deviceWidth": 1280,
    "deviceHeight": 720,
    "pageScaleFactor": 1,
    "offsetTop": 0,
    "scrollOffsetX": 0,
    "scrollOffsetY": 0,
    "timestamp": 1785038682238
  }
}
```

`seq` is a monotonic frame id, echoed back in an `ack` message under ack pacing. `metadata.timestamp` is the capture time in epoch milliseconds, so a client can tell how old a frame is by the time it draws it.

**Send mouse events:**

```json
{
  "type": "input_mouse",
  "eventType": "mousePressed",
  "x": 100,
  "y": 200,
  "button": "left",
  "clickCount": 1
}
```

**Send keyboard events:**

```json
{
  "type": "input_keyboard",
  "eventType": "keyDown",
  "key": "Enter",
  "code": "Enter"
}
```

**Send touch events:**

```json
{
  "type": "input_touch",
  "eventType": "touchStart",
  "touchPoints": [{ "x": 100, "y": 200 }]
}
```

**Cap the frame rate (per client):**

```json
{
  "type": "config",
  "maxFps": 10
}
```

Frames are delivered latest-first: the server picks the newest frame at send time, so frames produced while an earlier one is still being written are skipped rather than queued. `maxFps` (1 to 120, `0` = uncapped) limits delivery for that client only. A client that sends `{"type":"config","pacing":"ack"}` receives one frame at a time and acknowledges it with `{"type":"ack","seq":N}`, so nothing stale reaches the socket even if that client stalls; in the default push pacing, frames already handed to the transport are still delivered in order. Both settings can also be declared on the URL (`ws://127.0.0.1:/?pacing=ack&maxFps=10`), which is the only way to cover the connection's opening frame. Input events are read on a dedicated task per connection, so clicks and keystrokes dispatch immediately even while frames are mid-write to a slow client. They are sent to the browser without waiting for its reply, so a click stays responsive behind a burst of mouse moves, and ordering is preserved.

## Architecture

agent-browser uses a client-daemon architecture:

1. **Rust CLI** - Parses commands, communicates with daemon
2. **Rust Daemon** - Pure Rust daemon using direct CDP, no Node.js required

The daemon starts automatically on first command and persists between commands for fast subsequent operations. After **1 hour** with no commands or dashboard input it saves configured restore state, closes the browser, and exits, so an integration that dies without calling `close` cannot leak the daemon and its browser indefinitely; the next command starts a fresh daemon and configured state restore works as usual. A session without `--restore` or another restore key does not save browser state, so its transient state and open tabs are discarded at shutdown. Set `--idle-timeout` to a duration such as `30s`, `5m`, or `1h`, or set `AGENT_BROWSER_IDLE_TIMEOUT_MS` to a value in milliseconds. Use `0` to disable idle shutdown entirely. The default never closes a headed browser, including Safari and iOS WebDriver sessions, or a user-attached browser because those may be in direct human use. Provider-owned cloud browsers remain eligible for cleanup. An explicitly set timeout applies to every browser.

**Browser Engine:** Uses Chrome (from Chrome for Testing) by default. The `--engine` flag selects between `chrome` and `lightpanda`. Supported browsers: Chromium/Chrome (via CDP) and Safari (via WebDriver for iOS).

## Platforms

| Platform    | Binary      |
| ----------- | ----------- |
| macOS ARM64 | Native Rust |
| macOS x64   | Native Rust |
| Linux ARM64 | Native Rust |
| Linux x64   | Native Rust |
| Windows x64 | Native Rust |

## Usage with AI Agents

### Just ask the agent

The simplest approach is to tell your agent to use it:

```
Use agent-browser to test the login flow. Run agent-browser --help to see available commands.
```

The `--help` output is comprehensive and most agents can figure it out from there.

### AI Coding Assistants (recommended)

Add the skill to your AI coding assistant for richer context:

```bash
npx skills add vercel-labs/agent-browser
```

This works with Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, Goose, OpenCode, and Windsurf. The skill is fetched from the repository, so it stays up to date automatically. Do not copy `SKILL.md` from `node_modules` as it will become stale.

### Claude Code

Install as a Claude Code skill:

```bash
npx skills add vercel-labs/agent-browser
```

This adds a thin discovery stub at `.claude/skills/agent-browser/SKILL.md`. The stub is intentionally minimal — it points Claude Code at `agent-browser skills get core` to load the actual workflow content at runtime. This way the instructions always match the installed CLI version instead of going stale between releases.

### AGENTS.md / CLAUDE.md

For more consistent results, add to your project or global instructions file:

```markdown
## Browser Automation

Use `agent-browser` for web automation. Run `agent-browser --help` for all commands.

Core workflow:

1. `agent-browser open ` - Navigate to page
2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2)
3. `agent-browser click @e1` / `fill @e2 "text"` - Interact using refs
4. Re-snapshot after page changes
```

## Integrations

### iOS Simulator

Control real Mobile Safari in the iOS Simulator for authentic mobile web testing. Requires macOS with Xcode.

**Setup:**

```bash
# Install Appium and XCUITest driver
npm install appium
appium driver install xcuitest
```

**Usage:**

```bash
# List available iOS simulators
agent-browser device list

# Launch Safari on a specific device
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com

# Same commands as desktop
agent-browser -p ios snapshot -i
agent-browser -p ios tap @e1
agent-browser -p ios fill @e2 "text"
agent-browser -p ios screenshot mobile.png

# Mobile-specific commands
agent-browser -p ios swipe up
agent-browser -p ios swipe down 500

# Close session
agent-browser -p ios close
```

Or use environment variables:

```bash
export AGENT_BROWSER_PROVIDER=ios
export AGENT_BROWSER_IOS_DEVICE="iPhone 16 Pro"
agent-browser open https://example.com
```

| Variable                   | Description                                     |
| -------------------------- | ----------------------------------------------- |
| `AGENT_BROWSER_PROVIDER`   | Set to `ios` to enable iOS mode                 |
| `AGENT_BROWSER_IOS_DEVICE` | Device name (e.g., "iPhone 16 Pro", "iPad Pro") |
| `AGENT_BROWSER_IOS_UDID`   | Device UDID (alternative to device name)        |

**Supported devices:** All iOS Simulators available in Xcode (iPhones, iPads), plus real iOS devices.

**Note:** The iOS provider boots the simulator, starts Appium, and controls Safari. First launch takes ~30-60 seconds; subsequent commands are fast.

#### Real Device Support

Appium also supports real iOS devices connected via USB. This requires additional one-time setup:

**1. Get your device UDID:**

```bash
xcrun xctrace list devices
# or
system_profiler SPUSBDataType | grep -A 5 "iPhone\|iPad"
```

**2. Sign WebDriverAgent (one-time):**

```bash
# Open the WebDriverAgent Xcode project
cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent
open WebDriverAgent.xcodeproj
```

In Xcode:

- Select the `WebDriverAgentRunner` target
- Go to Signing & Capabilities
- Select your Team (requires Apple Developer account, free tier works)
- Let Xcode manage signing automatically

**3. Use with agent-browser:**

```bash
# Connect device via USB, then:
agent-browser -p ios --device "<DEVICE_UDID>" open https://example.com

# Or use the device name if unique
agent-browser -p ios --device "John's iPhone" open https://example.com
```

**Real device notes:**

- First run installs WebDriverAgent to the device (may require Trust prompt)
- Device must be unlocked and connected via USB
- Slightly slower initial connection than simulator
- Tests against real Safari performance and behavior

### Browserless

[Browserless](https://browserless.io) provides cloud browser infrastructure with a Sessions API. Use it when running agent-browser in environments where a local browser isn't available.

To enable Browserless, use the `-p` flag:

```bash
export BROWSERLESS_API_KEY="your-api-token"
agent-browser -p browserless open https://example.com
```

Or use environment variables for CI/scripts:

```bash
export AGENT_BROWSER_PROVIDER=browserless
export BROWSERLESS_API_KEY="your-api-token"
agent-browser open https://example.com
```

Optional configuration via environment variables:

| Variable                   | Description                                      | Default                                 |
| -------------------------- | ------------------------------------------------ | --------------------------------------- |
| `BROWSERLESS_API_URL`      | Base API URL (for custom regions or self-hosted) | `https://production-sfo.browserless.io` |
| `BROWSERLESS_BROWSER_TYPE` | Type of browser to use (chromium or chrome)      | chromium                                |
| `BROWSERLESS_TTL`          | Session TTL in milliseconds                      | `300000`                                |
| `BROWSERLESS_STEALTH`      | Enable stealth mode (`true`/`false`)             | `true`                                  |

When enabled, agent-browser connects to a Browserless cloud session instead of launching a local browser. All commands work identically.

Get your API token from the [Browserless Dashboard](https://browserless.io).

### Browserbase

[Browserbase](https://browserbase.com) provides remote browser infrastructure to make deployment of agentic browsing agents easy. Use it when running the agent-browser CLI in an environment where a local browser isn't feasible.

To enable Browserbase, use the `-p` flag:

```bash
export BROWSERBASE_API_KEY="your-api-key"
agent-browser -p browserbase open https://example.com
```

Or use environment variables for CI/scripts:

```bash
export AGENT_BROWSER_PROVIDER=browserbase
export BROWSERBASE_API_KEY="your-api-key"
agent-browser open https://example.com
```

When enabled, agent-browser connects to a Browserbase session instead of launching a local browser. All commands work identically.

Get your API key from the [Browserbase Dashboard](https://browserbase.com/overview).

### Browser Use

[Browser Use](https://browser-use.com) provides cloud browser infrastructure for AI agents. Use it when running agent-browser in environments where a local browser isn't available (serverless, CI/CD, etc.).

To enable Browser Use, use the `-p` flag:

```bash
export BROWSER_USE_API_KEY="your-api-key"
agent-browser -p browseruse open https://example.com
```

Or use environment variables for CI/scripts:

```bash
export AGENT_BROWSER_PROVIDER=browseruse
export BROWSER_USE_API_KEY="your-api-key"
agent-browser open https://example.com
```

When enabled, agent-browser connects to a Browser Use cloud session instead of launching a local browser. All commands work identically.

Get your API key from the [Browser Use Cloud Dashboard](https://cloud.browser-use.com/settings?tab=api-keys). Free credits are available to get started, with pay-as-you-go pricing after.

### Kernel

[Kernel](https://www.kernel.sh) provides cloud browser infrastructure for AI agents with features like stealth mode and persistent profiles.

To enable Kernel, use the `-p` flag:

```bash
export KERNEL_API_KEY="your-api-key"
agent-browser -p kernel open https://example.com
```

Or use environment variables for CI/scripts:

```bash
export AGENT_BROWSER_PROVIDER=kernel
export KERNEL_API_KEY="your-api-key"
agent-browser open https://example.com
```

Optional configuration via environment variables:

| Variable                 | Description                                                                      | Default |
| ------------------------ | -------------------------------------------------------------------------------- | ------- |
| `KERNEL_HEADLESS`        | Run browser in headless mode (`true`/`false`)                                    | `true`  |
| `KERNEL_STEALTH`         | Enable stealth mode to avoid bot detection (`true`/`false`)                      | `false` |
| `KERNEL_TIMEOUT_SECONDS` | Session timeout in seconds                                                       | `300`   |
| `KERNEL_PROFILE_NAME`    | Browser profile name for persistent cookies/logins (created if it doesn't exist) | (none)  |

When enabled, agent-browser connects to a Kernel cloud session instead of launching a local browser. All commands work identically.

**Profile Persistence:** When `KERNEL_PROFILE_NAME` is set, the profile will be created if it doesn't already exist. Cookies, logins, and session data are automatically saved back to the profile when the browser session ends, making them available for future sessions.

Get your API key from the [Kernel Dashboard](https://dashboard.onkernel.com).

### AgentCore

[AWS Bedrock AgentCore](https://aws.amazon.com/bedrock/agentcore/) provides cloud browser sessions with SigV4 authentication.

To enable AgentCore, use the `-p` flag:

```bash
agent-browser -p agentcore open https://example.com
```

Or use environment variables for CI/scripts:

```bash
export AGENT_BROWSER_PROVIDER=agentcore
agent-browser open https://example.com
```

Credentials are automatically resolved from environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) or the AWS CLI (`aws configure export-credentials`), which supports SSO, profiles, and IAM roles.

Optional configuration via environment variables:

| Variable                   | Description                                                          | Default          |
| -------------------------- | -------------------------------------------------------------------- | ---------------- |
| `AGENTCORE_REGION`         | AWS region for the AgentCore endpoint                                | `us-east-1`      |
| `AGENTCORE_BROWSER_ID`     | Browser identifier                                                   | `aws.browser.v1` |
| `AGENTCORE_PROFILE_ID`     | Browser profile for persistent state (cookies, localStorage)         | (none)           |
| `AGENTCORE_SESSION_TIMEOUT`| Session timeout in seconds                                           | `3600`           |
| `AWS_PROFILE`              | AWS CLI profile for credential resolution                            | `default`        |

**Browser profiles:** When `AGENTCORE_PROFILE_ID` is set, browser state (cookies, localStorage) is persisted across sessions automatically.

When enabled, agent-browser connects to an AgentCore cloud browser session instead of launching a local browser. All commands work identically.

## License

Apache-2.0

---

## File: docs/src/app/commands/page.mdx

# Commands

## Core

```
/* Detailed source-code truncated for AI context efficiency. */
```

Clicks fail before dispatch when another element covers the target's click point. The error names the covering element, for example `covered by `. Dismiss or interact with that element, take a fresh snapshot, then retry the original action.

Headless Chromium screenshots hide native scrollbars for consistent image output. Pass `--hide-scrollbars false` when launching to keep native scrollbars visible.

## Get info

```bash
agent-browser get text <sel>          # Get text content
agent-browser get html <sel>          # Get innerHTML
agent-browser get value <sel>         # Get input value
agent-browser get attr <sel> <attr>   # Get attribute
agent-browser get title               # Get page title
agent-browser get url                 # Get current URL
agent-browser get cdp-url             # Get CDP WebSocket URL
agent-browser get count <sel>         # Count matching elements
agent-browser get box <sel>           # Get bounding box
agent-browser get styles <sel>        # Get computed styles
```

## Read agent-friendly text

```bash
agent-browser read
agent-browser read https://example.com/article
agent-browser read https://example.com/article --filter overview
agent-browser read https://example.com/article --outline
agent-browser read https://docs.example.com --llms index --filter auth
agent-browser read https://docs.example.com --llms full --filter auth
agent-browser read example.com/article --require-md
agent-browser read https://example.com/article --json
```

`read` fetches a URL without launching Chrome. Omit the URL to read the rendered DOM of the active tab in the current browser session, including browser auth state and client-side updates. Explicit URL reads send `Accept: text/markdown` by default, try the same URL with `.md` appended when the first response is not markdown, walk ancestor paths toward `/` to find the nearest `llms.txt` for a matching docs link, print markdown or plain text when available, and fall back to readable text extracted from HTML. `--llms` and `--require-md` with no URL use the active tab URL because they depend on HTTP resources. `read` does not read `llms-full.txt` unless you ask for it.

Options: `--raw` prints the response body without HTML extraction, `--require-md` fails unless the server returns `Content-Type: text/markdown`, `--outline` prints a compact heading outline for one page, `--llms index` prints a compact nearest-ancestor `llms.txt` link list, `--llms full` reads the nearest-ancestor `llms-full.txt`, `--filter <text>` narrows page sections, llms links/sections, or outline headings, and `--timeout <ms>` changes the request timeout. Global safeguards such as `--allowed-domains`, `--content-boundaries`, and `--max-output` also apply to read fetches and output.

## Check state

```bash
agent-browser is visible <sel>        # Check if visible
agent-browser is enabled <sel>        # Check if enabled
agent-browser is checked <sel>        # Check if checked
```

## Find elements

Semantic locators with actions (`click`, `fill`, `check`, `hover`, `text`):

```bash
agent-browser find role <role> <action> [value]
agent-browser find text <text> <action> [value]
agent-browser find label <label> <action> [value]
agent-browser find placeholder  <action> [value]
agent-browser find alt <text> <action> [value]
agent-browser find title <text> <action> [value]
agent-browser find testid  <action> [value]
agent-browser find first <sel> <action> [value]
agent-browser find last <sel> <action> [value]
agent-browser find nth <n> <sel> <action> [value]
```

Options:

- `--name <name>`: filter role by accessible name
- `--exact`: exact, case-sensitive match. For `role` it applies to the accessible name, whose default is a case-insensitive substring.

Examples:

```bash
agent-browser find role button click --name "Submit"
agent-browser find role heading text --name "Skills"     # implicit roles work: <h2>=heading, =list, top-level <header>=banner
agent-browser find label "Email" fill "test@test.com"
agent-browser find alt "Logo" click
agent-browser find first ".item" click
agent-browser find last ".item" text
agent-browser find nth 2 ".card" hover
```

## Wait

```bash
agent-browser wait <selector>         # Wait for element
agent-browser wait <ms>               # Wait for time
agent-browser wait --text "Welcome"   # Wait for text (substring match)
agent-browser wait --url "**/dash"    # Wait for URL pattern
agent-browser wait --load networkidle # Wait for load state
agent-browser wait --fn "condition"   # Wait for JS condition
agent-browser wait --download [path]  # Wait for download
agent-browser wait --fn "!document.body.innerText.includes('Loading...')"  # Wait for text to disappear
agent-browser wait "#spinner" --state hidden           # Wait for element to disappear
```

## Downloads

```bash
agent-browser download <sel>    # Click element to trigger download
agent-browser wait --download [path]  # Wait for any download to complete
```

Use `--download-path <dir>` (or `AGENT_BROWSER_DOWNLOAD_PATH` env) to set a default download directory. Without it, downloads go to a temporary directory that is deleted when the browser closes.

See [Files & Clipboard](/files) for upload, download, local file, screenshot, PDF, and clipboard workflows.

## Mouse

```bash
agent-browser mouse move <x> <y>      # Move mouse
agent-browser mouse down [button]     # Press button
agent-browser mouse up [button]       # Release button
agent-browser mouse wheel <dy> [dx]   # Scroll wheel
```

## Clipboard

```bash
agent-browser clipboard read                      # Read text from clipboard
agent-browser clipboard write "Hello, World!"     # Write text to clipboard
agent-browser clipboard copy                      # Copy current selection (Ctrl+C)
agent-browser clipboard paste                     # Paste from clipboard (Ctrl+V)
```

See [Files & Clipboard](/files) for clipboard, upload, download, local file, screenshot, and PDF workflows.

## Settings

```bash
agent-browser set viewport <w> <h> [scale]  # Set viewport size (scale for retina, e.g. 2)
agent-browser set device <name>       # Emulate device ("iPhone 14")
agent-browser set geo <lat> <lng>     # Set geolocation
agent-browser set offline [on|off]    # Toggle offline mode
agent-browser set headers <json>      # Extra HTTP headers
agent-browser set credentials   # HTTP basic auth
agent-browser set media [dark|light]  # Emulate color scheme (persists for session)
```

Use `--color-scheme` for persistent dark/light mode across all commands:

```bash
agent-browser --color-scheme dark open https://example.com
```

## Cookies & storage

```bash
agent-browser cookies                 # Get all cookies
agent-browser cookies set <name> <val> # Set cookie
agent-browser cookies clear           # Clear cookies

agent-browser storage local           # Get all localStorage
agent-browser storage local <key>     # Get specific key
agent-browser storage local set <k> <v>  # Set value
agent-browser storage local clear     # Clear all

agent-browser storage session         # Same for sessionStorage
```

## Network

```bash
agent-browser network route               # Intercept requests
agent-browser network route  --abort      # Block requests
agent-browser network route  --body <json>  # Mock response
agent-browser network route '*' --abort --resource-type script  # Block scripts only
agent-browser network unroute [url]            # Remove routes
agent-browser network requests                 # View tracked requests
agent-browser network requests --clear         # Clear request log
agent-browser network requests --filter   # Filter by URL pattern
agent-browser network requests --type xhr,fetch  # Filter by resource type
agent-browser network requests --method POST   # Filter by HTTP method
agent-browser network requests --status 2xx    # Filter by status (200, 2xx, 400-499)
agent-browser network request <requestId>      # View full request/response detail
agent-browser network har start                # Start HAR recording (embeds text response bodies)
agent-browser network har start --content all  # Embed all response bodies (binary as base64)
agent-browser network har start --content none # Metadata only, no bodies
agent-browser network har stop [output.har]    # Stop and save HAR (temp path if omitted)
```

See [Network](/network) for pre-navigation routing, request filters, HAR export, and SSR/no-JavaScript debugging.

## Tabs & frames

```bash
agent-browser tab                              # List tabs (each row shows tabId and label)
agent-browser tab new [url]                    # New tab
agent-browser tab new --label docs [url]       # New tab with a user-assigned label
agent-browser tab <t<N>|label>                 # Switch to a tab by id or label
agent-browser tab close [t<N>|label]           # Close a tab (defaults to active)
agent-browser window new                       # Open new browser window
agent-browser frame <sel>                      # Switch to iframe by CSS selector
agent-browser frame @e3                        # Switch to iframe by element ref
agent-browser frame main                       # Back to main frame
```

### Stable tab ids and labels

Tab ids are stable strings of the form `t1`, `t2`, `t3`. They're never reused within a session, so `t2` keeps pointing at the same tab even as other tabs are opened or closed. The `t` prefix mirrors the `@e1` element-ref convention and is not interchangeable with positional integers — `agent-browser tab 2` errors with a teaching message; use `t2`.

You can also assign a memorable label (`docs`, `app`, `admin`) at tab-creation time and use it anywhere an id is accepted:

```bash
agent-browser tab new --label docs https://docs.example.com
agent-browser tab docs          # switch to the docs tab
agent-browser snapshot          # populate refs for docs
agent-browser click @e3         # click uses docs's refs
agent-browser tab close docs    # close by label
```

Labels are never auto-generated and never rewritten on navigation — an agent that names a tab `docs` keeps that name until the tab is closed. Labels are unique within a session; creating a second tab with an existing label errors.

`tab list --json` also reports each tab's CDP `targetId`, and target ids are accepted anywhere a tab ref is accepted (`tab <targetId>`, `tab close <targetId>`). Unlike `t<N>` ids, which are per-daemon counters, target ids stay stable across daemon restarts. Each session is bound to its active tab by target id and returns to it after a daemon restart; with `--pin-tab` the binding is strict and a closed bound tab produces a `tab_gone` error instead of a silent fallback. JSON errors include `code: "tab_gone"`, `data.targetId`, and optional sanitized `data.lastUrl`. Batch output carries the recovery object under `result`. See [CDP Mode](/cdp-mode) for the multi-session workflow.

Refs (`@e1`, etc.) are scoped to the tab that was active when the snapshot ran, so switch tabs first, then snapshot and interact:

```bash
agent-browser tab docs          # switch first
agent-browser snapshot          # refs for docs
agent-browser click @e3         # uses docs's refs
```

### Discarded tab revival

Browsers discard background tabs to save memory, leaving a tab with no renderer to drive. Switching to a discarded tab reactivates it, which reloads the page and resets its unsaved state; the switch result adds `"revived": true` so the reload is visible rather than silent. A tab whose page is paused by a JavaScript dialog is alive rather than discarded: the switch leaves it untouched and adds `"dialogBlocked": true`. Resolve the dialog with `dialog accept` or `dialog dismiss` and the tab keeps its state. Closing the active tab onto a discarded successor revives it the same way and reports `"activeTabRevived": true`.

### Iframe support

Iframes are detected automatically during snapshots. `Iframe` nodes are resolved and their content is inlined beneath the iframe element in the snapshot output. Refs assigned to elements inside iframes carry frame context, so `click`, `fill`, and other interactions work without manually switching frames.

```bash
agent-browser snapshot -i
# @e3 [Iframe] "payment-frame"
#   @e4 [input] "Card number"
#   @e5 [button] "Pay"

# Interact directly using refs — no frame switch needed
agent-browser fill @e4 "4111111111111111"
agent-browser click @e5

# Or switch frame context for scoped snapshots
agent-browser frame @e3
agent-browser snapshot -i             # Only elements inside that iframe
agent-browser frame main              # Return to main frame
```

The `frame` command accepts element refs (`@e3`), CSS selectors (`"#my-iframe"`), or frame name/URL.

## Dialogs

```bash
agent-browser dialog accept [text]    # Accept dialog (with optional prompt text)
agent-browser dialog dismiss          # Dismiss dialog
agent-browser dialog status           # Check if a dialog is currently open
```

By default, `alert` and `beforeunload` dialogs are automatically accepted so they never block the agent. `confirm` and `prompt` dialogs still require explicit handling. Use `--no-auto-dialog` (or `AGENT_BROWSER_NO_AUTO_DIALOG=1`) to disable automatic handling.

When a JavaScript dialog (`alert`, `confirm`, `prompt`) is pending, all command responses include a `warning` field with the dialog type and message.

## Streaming

```bash
agent-browser stream enable           # Start runtime WebSocket streaming on an auto-selected port
agent-browser stream enable --port 9223  # Bind a specific localhost port
agent-browser stream status           # Show enabled state, port, browser connection, screencasting
agent-browser stream disable          # Stop runtime streaming and remove the .stream metadata file
```

Streaming is enabled automatically for all sessions. Use these commands to check status, re-enable on a specific port, or disable streaming.

Streaming is separate from file-based recordings. Use [Video Recording](/recording) when you need a saved WebM artifact.

## Debug

```bash
agent-browser trace start             # Start trace
agent-browser trace stop [path]       # Stop and save trace
agent-browser profiler start          # Start Chrome DevTools profiling
agent-browser profiler stop [path]    # Stop and save profile (.json)
agent-browser record start      # Start video recording (WebM)
agent-browser record stop             # Stop and save video
agent-browser record restart    # Stop current and start new recording
agent-browser console                 # View console messages
agent-browser console --json          # JSON output with raw CDP args
agent-browser console --clear         # Clear console log
agent-browser errors                  # View page errors
agent-browser errors --clear          # Clear error log
agent-browser highlight <sel>         # Highlight element
agent-browser inspect                 # Open Chrome DevTools for the active page
```

See [Debugging](/debugging) for console, error, dialog, trace, highlight, and DevTools workflows. See [Video Recording](/recording) for `record` workflows and [Profiler](/profiler) for performance profiles.

## Auth vault

```bash
agent-browser auth save <name> [opts]    # Save auth profile
agent-browser auth login <name>          # Login using saved credentials
agent-browser auth login <name> --credential-provider  [--item <ref>] [--url ]
                                          # Resolve credentials from plugin
agent-browser auth login <name> --username-selector <s> --password-selector <s> [--submit-selector <s>]
                                          # Override selectors for one login
agent-browser auth list                  # List saved profiles (names and URLs only)
agent-browser auth show <name>           # Show profile metadata (no passwords)
agent-browser auth delete <name>         # Delete a saved profile
agent-browser plugin add <ref>           # Add a plugin from npm or GitHub
agent-browser plugin list                # List configured plugins
agent-browser plugin show <name>         # Show one configured plugin
agent-browser plugin run <name> <type> --payload <json>
                                          # Run an arbitrary plugin request
```

Save options:

- `--url `: login page URL (required)
- `--username `: username (required)
- `--password `: password (required unless `--password-stdin`)
- `--password-stdin`: read password from stdin (recommended to avoid shell history exposure)
- `--username-selector <sel>`: custom CSS selector for username field
- `--password-selector <sel>`: custom CSS selector for password field
- `--submit-selector <sel>`: custom CSS selector for submit button

`auth login` navigates with `load` and then waits for the username/password/submit selectors to appear before interacting. This improves reliability on SPA login pages where fields render after initial page load.

Plugin login options:

- `--credential-provider `: resolve credentials from a configured plugin
- `--item <ref>`: provider-specific vault item reference
- `--url `: login URL override
- `--username-selector <sel>`: selector override for this login
- `--password-selector <sel>`: selector override for this login
- `--submit-selector <sel>`: selector override for this login

Credential provider plugins run out-of-process over the `agent-browser.plugin.v1` stdio JSON protocol. They return credentials to the daemon for a single login and agent-browser does not save those credentials locally.

Other plugin capabilities use the same protocol:

- `browser.provider`: use the plugin with `--provider <name>` to return a CDP WebSocket URL
- `launch.mutate`: append local launch args, extensions, or init scripts before Chrome starts
- `command.run`: run arbitrary namespaced requests through `agent-browser plugin run`

`plugin run` is for `command.run` and custom capabilities. Core capabilities and protocol request types use their dedicated command paths.

```bash
echo "pass" | agent-browser auth save github --url https://github.com/login --username user --password-stdin
agent-browser auth login github
agent-browser plugin add agent-browser-plugin-vault --name vault
agent-browser auth login my-app --credential-provider vault --item "My App"
agent-browser --provider cloud-browser open https://example.com
agent-browser plugin run captcha captcha.solve --payload '{"siteKey":"...","url":"https://example.com"}'
agent-browser auth list
```

## Confirmation

When `--confirm-actions` is set, certain action categories return a `confirmation_required` response instead of executing immediately. Use `confirm` or `deny` to approve or reject the action.

```bash
agent-browser confirm <confirmation-id>  # Approve a pending action
agent-browser deny <confirmation-id>     # Deny a pending action
```

Pending confirmations auto-deny after 60 seconds.

```bash
agent-browser --confirm-actions eval,download eval "document.title"
# Returns confirmation_required with ID
agent-browser confirm c_8f3a1234
```

## State management

```bash
agent-browser state save        # Save auth state to file
agent-browser state load        # Load auth state from file
agent-browser state list              # List saved state files
agent-browser state show <file>       # Show state summary
agent-browser state rename <old> <new> # Rename state file
agent-browser state clear [name]      # Clear states for session name
agent-browser state clear --all       # Clear all saved states
agent-browser state clean --older-than <days>  # Delete old states
```

## Sessions

```bash
agent-browser session                 # Show current session name
agent-browser session list            # List active sessions
```

## Chrome profiles

```bash
agent-browser profiles               # List available Chrome profiles
agent-browser profiles --json        # List profiles as JSON
agent-browser --profile Default open https://gmail.com  # Reuse a profile's login state
```

## Dashboard

```bash
agent-browser dashboard [start]       # Start the dashboard server (default port: 4848)
agent-browser dashboard start --port <n>  # Start on a specific port
agent-browser dashboard stop          # Stop the dashboard server
```

Open the dashboard through `http://localhost:4848` or a proxied/forwarded dashboard URL such as `https://dashboard.agent-browser.localhost`. The browser stays on the dashboard origin; per-session tabs, status, and stream traffic are proxied internally, so session ports do not need to be exposed.

## Doctor

Diagnose your install, auto-clean stale daemon files, and optionally repair common problems.

```bash
agent-browser doctor                     # Full diagnosis (env, Chrome, daemons, config, providers, network, launch test)
agent-browser doctor --offline --quick   # Local-only, fastest
agent-browser doctor --fix               # Also run destructive repairs (reinstall Chrome, purge old state, ...)
agent-browser doctor --webgpu            # Also run a live WebGPU render probe (see the WebGPU page)
agent-browser doctor --json              # Structured JSON output for agents
```

Exit code is `0` if all checks pass (warnings are fine), `1` if any fail. See the [Installation page](/installation#doctor) for the full check catalog.

## Chat

Use natural language to control the browser via AI. The `chat` command translates instructions into agent-browser commands, executes them, and streams the AI response. Requires `AI_GATEWAY_API_KEY` to be set.

```bash
agent-browser chat "open google.com and search for cats"     # Single-shot instruction
agent-browser chat                                           # Interactive REPL (type quit to exit)
echo "summarize this page" | agent-browser chat              # Piped input
agent-browser -q chat "summarize this page"                  # Quiet: text only, no tool calls shown
agent-browser -v chat "fill in the login form"               # Verbose: show commands and their output
agent-browser --model openai/gpt-4o chat "take a screenshot" # Override the default AI model
agent-browser --json chat "open example.com"                 # Structured JSON output
```

Chat-specific options:

```bash
--model <name>           # AI model (or AI_GATEWAY_MODEL env, default: anthropic/claude-sonnet-4.6)
-v, --verbose            # Show tool commands and their raw output
-q, --quiet              # Show only the AI text response (hide tool calls)
```

## Navigation

```bash
agent-browser back                    # Go back
agent-browser forward                 # Go forward
agent-browser reload                  # Reload page
agent-browser pushstate          # SPA client-side nav; auto-detects window.next.router.push,
                                      # falls back to history.pushState + popstate
```

## Pre-navigation setup

Some flows need routes, cookies, or init scripts configured *before* the first navigation (SSR debug, auth on protected origins, etc.). `open` without a URL launches the browser but stays on `about:blank`, leaving room to stage state. `batch` makes it one CLI invocation:

```bash
agent-browser batch \
  '["open"]' \
  '["network","route","*","--abort","--resource-type","script"]' \
  '["cookies","set","--curl","cookies.curl","--domain","localhost"]' \
  '["navigate","http://localhost:3000/target"]'
```

## React / Web Vitals

React commands require `--enable react-devtools` at launch (installs the React DevTools hook before any page JS runs). `vitals` and `pushstate` work on any site.

```bash
agent-browser open --enable react-devtools    # Launch with React hook installed
agent-browser react tree                           # Full component tree
agent-browser react inspect <fiberId>              # Inspect one component
agent-browser react renders start                  # Begin fiber render recording
agent-browser react renders stop [--json]          # Stop + print profile
agent-browser react suspense [--only-dynamic] [--json]  # Suspense boundaries + classifier
                                                         # --only-dynamic hides the "static" list
agent-browser vitals [url] [--json]                # LCP/CLS/TTFB/FCP/INP + hydration
```

Works on any React app (Next.js, Remix, Vite+React, CRA, TanStack Start, React Native Web, etc.). `vitals` and `pushstate` are framework-agnostic.

See [React & Web Vitals](/react) for full command details and examples.

## Accessibility audits

Run an embedded axe-core audit against the current page or navigate to a URL first. The vendored engine works without a CDN request and runs under strict page CSP. It runs private partial audits across the page's frame tree and merges serialized results without page messaging, so page-provided `window.axe` values remain intact and iframe violations retain their frame selector paths. Accessibility audits require a CDP browser and are not available with Safari or iOS WebDriver sessions.

```bash
agent-browser a11y                                 # Audit the current page
agent-browser a11y https://example.com             # Navigate, then audit
agent-browser a11y --tags wcag2a,wcag2aa           # Filter by axe rule tags
agent-browser a11y --selector "#main"              # Scope to a subtree
agent-browser a11y https://example.com --json      # Structured results
```

The default output lists violations and incomplete checks with impact, rule ID, fix guidance, and failing-node paths. JSON output preserves axe's `target` selector path arrays, including nested arrays for shadow DOM boundaries and multiple entries for frame boundaries.

## Init scripts

```bash
agent-browser open --init-script            # Register before first navigation (repeatable)
agent-browser addinitscript <js>                  # Register at runtime (returns identifier)
agent-browser removeinitscript        # Remove a previously registered init script
```

See [Init Scripts & Extensions](/init-scripts) for launch-time scripts, runtime scripts, built-in React DevTools, and extensions.

## Global options

```
/* Detailed source-code truncated for AI context efficiency. */
```

See [Configuration](/configuration) for config files and environment variables. See [Proxy](/proxy) for proxy server, bypass, and credential settings.

## Batch execution

Execute multiple commands in a single invocation. Commands can be passed as quoted arguments or piped as JSON via stdin.

```bash
# Argument mode: each quoted argument is a full command
agent-browser batch "open https://example.com" "snapshot -i" "screenshot"

# With --bail to stop on first error
agent-browser batch --bail "open https://example.com" "click @e1" "screenshot"

# Stdin mode: pipe commands as JSON
echo '[
  ["open", "https://example.com"],
  ["snapshot", "-i"],
  ["click", "@e1"],
  ["screenshot", "result.png"]
]' | agent-browser batch --json
```



| Option | Description |
| --- | --- |
| `--bail` | Stop on first error (default: continue all commands) |
| `--json` | Output results as a JSON array |



## MCP server

```bash
agent-browser mcp
agent-browser mcp --tools all
agent-browser mcp --tools core,network,react
```

Starts a Model Context Protocol server over stdio. MCP clients launch this command as a subprocess and exchange newline-delimited JSON-RPC on stdin and stdout. The server defaults to MCP protocol 2025-11-25 and accepts older supported client protocol versions during initialization.

The default tools profile is `core`, which keeps MCP context small for everyday browser automation. Use `--tools all` for the full typed CLI parity surface, or combine profiles with commas, such as `--tools core,network,react`.

Profiles:

- `core` - Default. Navigation, snapshots, interaction, waits, reads, screenshots, JavaScript eval, close, tab basics, and profile discovery
- `network` - Network routes, request inspection, HAR, headers, credentials, offline
- `state` - Cookies, storage, auth, saved state, sessions, profiles, skills
- `debug` - Console/errors, tracing, profiling, recording, accessibility audits, clipboard, plugins, doctor, dashboard, install, upgrade, chat, diff, batch, confirm/deny
- `tabs` - Back/forward/reload, tabs, windows, frames, dialogs
- `react` - React tree/inspect/renders/suspense, vitals, pushstate
- `mobile` - Viewport/device/geolocation/media, touch, swipe, mouse, keyboard
- `all` - Every MCP tool, including the full typed CLI parity surface

Example MCP client config:

```json
{
  "mcpServers": {
    "agent-browser": {
      "command": "agent-browser",
      "args": ["mcp"]
    }
  }
}
```

With `--tools all`, the server exposes typed tools across the CLI command surface, including navigation, snapshots, input, waits, screenshots/PDF, get/is/find, mouse, network, storage, cookies, tabs, windows, frames, dialogs, tracing, recording, accessibility audits, clipboard, auth, state, iOS, diff, React, vitals, sessions, skills, plugins, doctor, dashboard, install, upgrade, chat, eval, and close.

Common tools include:

- `agent_browser_tools_profiles`
- `agent_browser_open`
- `agent_browser_snapshot`
- `agent_browser_click`
- `agent_browser_fill`
- `agent_browser_type`
- `agent_browser_press`
- `agent_browser_wait_for_selector`
- `agent_browser_screenshot`
- `agent_browser_get_url`
- `agent_browser_eval`
- `agent_browser_close`

Each tool has typed fields such as `url`, `selector`, `text`, `key`, `session`, `allowedDomains`, and `idleTimeout`, so MCP clients show meaningful approval prompts instead of raw command arrays. The common `allowedDomains` array maps to `--allowed-domains` and activates the same WebRTC containment and launch-mode restrictions. The common `idleTimeout` string maps to `--idle-timeout`, accepts values such as `30s`, `5m`, `1h`, or raw milliseconds, and accepts `0` to disable idle shutdown. Each tool also accepts `extraArgs` for advanced CLI flags and exact CLI parity. Tool discovery is paginated and includes read-only/open-world annotations so modern MCP clients can load the large typed surface incrementally.

Tool invocations use the same config files and environment variables as the CLI. Use `session` in the tool arguments, or set `AGENT_BROWSER_SESSION`, to isolate browser state.

## Command chaining

Chain commands with `&&` in a single shell invocation. The browser persists via a background daemon, so chaining works naturally and is more efficient than separate calls:

```bash
agent-browser open example.com && agent-browser wait --load networkidle && agent-browser snapshot -i
agent-browser fill @e1 "user@example.com" && agent-browser fill @e2 "pass" && agent-browser click @e3
agent-browser open example.com && agent-browser wait --load networkidle && agent-browser screenshot page.png
```

Use `&&` when you don't need to read intermediate output. Run commands separately when you need to parse output first (e.g., snapshot to discover refs, then interact with those refs).

## Local files

Open local files (PDFs, HTML) using `file://` URLs:

```bash
agent-browser --allow-file-access open file:///path/to/document.pdf
agent-browser --allow-file-access open file:///path/to/page.html
agent-browser screenshot output.png
```

The `--allow-file-access` flag enables JavaScript to access other local files. Chromium only.

See [Files & Clipboard](/files) for local files, uploads, downloads, screenshots, PDFs, and clipboard workflows.

---

## File: docs/src/app/changelog/page.mdx

# Changelog

## v0.34.0

August 10, 2026

### New Features

- Added **persistent session-to-tab binding for shared Chrome sessions**. Named sessions connected through `--cdp` or `--auto-connect` now remember their CDP target across commands and daemon restarts, and CDP target ids can be used directly as tab references. Start each session with `--pin-tab` to make the binding strict, so a tab closed externally returns a stable `tab_gone` error instead of silently adopting a neighboring tab. JSON output includes `data.targetId` and an optional sanitized `data.lastUrl`; batch exposes the same recovery data under `result` (#1589)

### Improvements

- Added a **Remote Agent Browser provider guide** covering Vercel authentication, disposable and stable sessions, custom VCR images, snapshots, and live-view URLs (#1648)

### Bug Fixes

- Fixed **parallel sessions sharing one Chrome hijacking each other's tabs**. Event-discovered targets no longer steal a pinned session's active tab, re-attach restores the persisted target instead of selecting index 0, and preliminary plus existing-daemon `--cdp` and `--auto-connect` paths preserve explicit pin enable and disable. Agent skill guidance now requires a named `--session` before the first command (#1589)
- Fixed **`agent-browser doctor` hanging on Chrome version detection**. Windows reads the version from the executable resource table without spawning Chrome; other platforms bound the version subprocess with a deadline, kill, and reap so inherited output handles cannot block forever or leave an orphan browser (#1641)

---

## v0.33.2

July 31, 2026

### New Features

- Added **input priority and per-client delivery settings** to the stream server: each WebSocket connection now splits into a reader task and a writer loop, so clicks and keystrokes dispatch to the browser without queueing behind a frame write. Adds a per-client `{"type":"config","maxFps":N}` cap (1 to 120, `0` uncapped) and opt-in ack pacing via `{"type":"config","pacing":"ack"}` with `{"type":"ack","seq":N}`, which keeps one frame in flight so a stalled client never drains a backlog. Both settings can also be declared on the connection URL (`?pacing=ack&maxFps=10`), the only way to cover the opening frame. Every frame now carries a monotonic `seq` (#1594)
- Added **configurable stream encoding**: frames were hardcoded to jpeg quality 80 at the session viewport, so frame rate was the only bandwidth lever. `AGENT_BROWSER_STREAM_QUALITY`, `AGENT_BROWSER_STREAM_MAX_WIDTH` and `AGENT_BROWSER_STREAM_MAX_HEIGHT` are read once per daemon and cut bytes without dropping frames: on a busy page at 1280x720, quality 20 takes a frame from 54 KB to 25 KB, and quality 20 at 640x360 takes it to 9 KB, frame rate unchanged in both. Width and height default to the session viewport, so a larger viewport is never downscaled unless both are set (#1626)

### Behavior Changes

- **Frame delivery is now latest-wins.** The stream server holds only the newest frame and reads it at send time, so frames produced while an earlier one is still being written are skipped. A client that assumed it received every frame from the WebSocket now receives fewer. Nothing in this repo consumes the stream that way, since `record` captures through CDP and the dashboard is a viewer, so this affects external consumers only (#1594)
- **`metadata.timestamp` now carries a real value.** It was always 0, because CDP sends the capture time as a float in seconds and the code read it as an integer. It is now epoch milliseconds, so a client can measure how stale the frame it is drawing is. A client that treated 0 as unknown is unaffected (#1594)

### Bug Fixes

- Fixed **keyboard input over the stream**, which worked in none of the three shapes the docs publish: an absent `key`, `code`, or `text` reached CDP as an explicit null, which rejects the whole command, so every key release and every non-printable key was silently dropped, the dashboard's own payloads included (#1594)
- Fixed **click latency behind mouse movement**: input dispatch awaited Chrome's reply before reading the next message, so a click sat one round trip behind every queued mouse move. After a 300-event sweep a click landed 2471ms late; it now lands in 6ms (#1627)
- Fixed **`stream disable`** returning while a slow client's input reader was still running (#1594)

---

## v0.33.1

July 28, 2026

### Behavior Changes

- The daemon now ships a **default idle timeout of 1 hour**: after an hour with no commands or dashboard input it saves configured restore state, closes the browser, and exits, so integrations that die without calling `close` no longer leak the daemon and its Chrome tree indefinitely. Sessions without `--restore` or another restore key discard transient browser state and open tabs when they shut down. Set `AGENT_BROWSER_IDLE_TIMEOUT_MS=0` to restore the old always-persist behavior, or any other value to tune it. Dashboard mouse, keyboard, and touch input reset the timer. The default never closes headed browsers, including Safari and iOS WebDriver sessions, or user-attached browsers that may be in direct human use. Provider-owned cloud browsers remain eligible for cleanup, and an explicitly configured timeout applies to all browsers, as before (#1605)

### Bug Fixes

- Fixed **tab recovery and selection** to avoid daemon hangs on Memory Saver-discarded tabs by selecting a live renderer on CDP connect, reviving tabs on switch or after close, treating dialog-blocked tabs as live, preserving refs on rejected operations, and surfacing recovery state across CLI, JSON, and MCP output (#1543)
- Fixed **a11y selector errors** to report invalid CSS selectors cleanly instead of exposing raw browser evaluation stack traces in text and JSON output (#1604)

---

## v0.33.0

July 23, 2026

### New Features

- Added **axe-core accessibility audits** with `agent-browser a11y [url]`, WCAG tag filtering, selector scoping, iframe-aware text and JSON results, an embedded offline and CSP-safe audit engine, and a matching MCP tool (#1596)

---

## v0.32.4

July 22, 2026

### Bug Fixes

- Fixed **find role** to match implicit ARIA roles and browser-computed accessible names through the accessibility tree, so semantic elements like `<h2>` (heading) and `` (list) resolve, with case-insensitive substring name matching that mirrors Playwright's `getByRole` (#1552)
- Fixed **element-not-found errors** to preserve the locator detail (selector, role, name, or index) instead of flattening every miss into one generic message, and aligned the advertised `find` actions with the set the dispatcher actually accepts (#1553)

---

## v0.32.3

July 19, 2026

### New Features

- Added **HAR response body capture** with text bodies embedded by default and configurable `all`, `text`, and `none` content modes across the CLI and MCP surfaces (#1578)
- Added a **derive-client skill** for recording browser traffic and generating reusable API clients from HAR request and response data (#1578)

---

## v0.32.2

July 17, 2026

### Improvements

- Updated **eve extension packaging** for eve 0.25.1, adopting the new source and dist extension manifest format, updating the eve example to stable AI SDK releases, and keeping extension tests aligned with eve's scoped config registry (#1570)

---

## v0.32.1

July 16, 2026

### Improvements

- Widened **eve compatibility** for `@agent-browser/eve` to accept eve 0.23 and future major releases without peer-resolution warnings (#1563)

### Documentation

- Standardized **eve branding** to use lowercase styling across the docs, examples, package readmes, and release notes (#1557)

---

## v0.32.0

July 15, 2026

### New Features

- **eve extension** - Added `@agent-browser/eve`, an eve extension that mounts the agent-browser tool set with namespaced browser tools, sandbox bootstrap helpers, docs, examples, CI, and release packaging (#1547)

### Security

- Hardened **domain allowlists** by blocking WebRTC bypasses, applying network containment across launch modes, workers, popups, restored state, and reused daemon sessions, rejecting unsafe startup arguments, and adding Chrome regression coverage (#1546)

### Bug Fixes

- Fixed **completed-page waits** so load and DOMContentLoaded waits resolve immediately when the current document is already ready, with structured eve wait timeout handling and focused coverage (#1554)

---

## v0.31.2

July 13, 2026

### New Features

- **WebGPU launch preset** - Added `--webgpu` across the CLI, config, environment, and MCP surfaces, with hardware backends on macOS and Windows, software Vulkan on Linux, automatic Xvfb for displayless headed sessions, and a `doctor --webgpu` render and screenshot probe (#1529)

### Improvements

- Added periodic **restore-state autosaves** while the browser remains open, preserving recent state after a browser window is closed by hand and capturing background page changes while honoring the restore save policy. The interval is configurable with `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` (#1509)

---

## v0.31.1

June 26, 2026

### Bug Fixes

- Fixed the **React renderer** so it picks the react-dom renderer instead of hardcoding renderer id 1, which prevented reading an empty tree on Next.js 16.3 Turbopack (#1491)

---

## v0.31.0

June 25, 2026

### New Features

- **Restore workflow** - Added `--restore`, `--restore-save`, restore validation flags, worktree-scoped `session id`, `session info`, and `--namespace` so agent runs can use stable, isolated, automatically restored browser state without managing state files by hand (#1486)

### Improvements

- Hardened **session lifecycle handling** with explicit daemon and browser compatibility checks, lifecycle status output, MCP support for restore options, and safer auto-save behavior that avoids overwriting good state after a failed restore or failed validation (#1486)

### Bug Fixes

- Fixed **restore lifecycle edge cases** around switching restore keys with a live browser, daemon configuration startup races, launch mode validation, and clearing restore failures after an explicit state load (#1486)

---

## v0.30.1

June 24, 2026

### Bug Fixes

- Fixed **URL waits** so `wait --url` and `waitforurl` honor glob patterns such as `**/dashboard` against the full active URL (#1483)

---

## v0.30.0

June 24, 2026

### New Features

- **Read command** - Added `agent-browser read [url]` and the matching MCP tool for agent-readable text extraction. URL reads prefer Markdown, try `.md` and nearby `llms.txt` docs, support outlines, filters, raw and JSON output, headers, and domain/output safeguards; omitting the URL reads the rendered active tab DOM with current browser state (#1480)

---

## v0.29.1

June 20, 2026

### Improvements

- Defaulted **sandbox system dependency installs** so the eve and Vercel sandbox helpers install Chromium's required libraries unless explicitly disabled, making first-run sandbox setup simpler (#1469)

---

## v0.29.0

June 20, 2026

### New Features

- **Sandbox package** - Added `@agent-browser/sandbox` with shared, eve, and Vercel Sandbox helpers, example projects, and docs for running agent-browser in hosted sandbox environments (#1465)

### Improvements

- Updated **sandbox release flow** so the new package stays version-synced with the CLI release and publishes from the correct workspace path (#1465)
- Reflowed **documentation prose** across the README, docs site, examples, and skills so Markdown and MDX wrap naturally in editors and renderers (#1466)

---

## v0.28.0

June 16, 2026

### New Features

- **MCP server** - Added `agent-browser mcp`, a stdio Model Context Protocol server with typed tools, paginated discovery, protocol negotiation, and startup tool profiles. The default `core` profile keeps context small, while `--tools all` exposes full CLI parity and composed profiles such as `core,network,react` are supported (#1454)
- **Plugin system** - Added out-of-process plugin support over the `agent-browser.plugin.v1` stdio protocol, with `plugin add/list/show/run`, manifest discovery, npm and GitHub refs, credential providers, browser provider plugins, launch mutators, custom command capabilities, config and env registry support, and capability-scoped policy gates (#1452)

### Infrastructure

- Added **context footprint eval coverage** for CLI skills, MCP core, and MCP full-profile surfaces, plus MCP parity tests to keep tool behavior aligned with the CLI (#1454)

---

## v0.27.3

June 12, 2026

### Bug Fixes

- Fixed **Windows ARM64 installs** by falling back to the Windows x64 binary during postinstall, avoiding failed downloads for a native ARM64 artifact that is not published (#1269)

---

## v0.27.2

June 10, 2026

### Bug Fixes

- Fixed **click reliability** by scrolling off-viewport elements into view before resolving coordinates, handling JavaScript dialogs promptly, recovering mouse state after dialog-opening clicks, and detecting click interception by overlays before dispatching input (#1432, #1434)
- Fixed **frame-scoped selectors and waits** so CSS-selector actions and `wait` respect the selected iframe, including cross-process iframes, with translated click coordinates (#1432)
- Fixed **wait timeout handling** so the documented 25s default is used, `--timeout` is honored across wait variants, and long waits receive an appropriate client read budget (#1432)
- Fixed **agent-facing form commands** so `find label` matches `aria-label` and `aria-labelledby`, `select` errors when no option matches, and `type` parses `--clear` and `--delay` instead of typing them as text (#1432)

### Improvements

- Cut **warm CLI command latency** from about 150ms to about 1ms by removing the unconditional daemon settle sleep and retrying once when a stale daemon socket is discovered (#1432)
- Extended **daemon respawn and retry** handling to batch commands so batches recover from a daemon exit after the initial liveness check (#1432)

### Infrastructure

- Pinned **GNU Linux release artifacts** to glibc 2.28 with zigbuild, added a release guard for newer GLIBC symbols, and aligned Docker release helpers with the pinned target (#1417)

---

## v0.27.1

June 1, 2026

### Improvements

- Improved **`vitals` command** output formatting for better readability (#1404)

### Documentation

- Surfaced agent-browser feature coverage in documentation (#1403)

---

## v0.27.0

May 7, 2026

### New Features

- **React introspection** - First-class React DevTools integration with new `react tree`, `react inspect <fiberId>`, `react renders start|stop`, and `react suspense` commands for full component-tree visibility, per-fiber props/hooks/state inspection, render profiling with mount/re-render counts and change details, and Suspense boundary classification with root-cause grouping and recommendations. React DevTools hook is vendored (MIT) and embedded in the binary with zero runtime dependencies (#1257)
- **Web Vitals** - New `vitals [url]` command that reports Core Web Vitals (LCP, CLS, TTFB, FCP, INP) plus React hydration phases for any page (#1257)
- **SPA navigation** - New `pushstate ` command for client-side SPA navigations without a full page load (#1257)
- **Init scripts and feature flags** - New `--init-script ` flag (repeatable; env `AGENT_BROWSER_INIT_SCRIPTS`) to register scripts before first navigation, and `--enable <feature>` flag (repeatable; env `AGENT_BROWSER_ENABLE`) for built-in init scripts such as `react-devtools` (#1257)
- **Network route resource type filter** - `network route` now accepts `--resource-type <csv>` to filter intercepted requests by CDP resource type (#1257)
- **cURL cookie import** - `cookies set --curl <file>` auto-detects JSON, cURL, and Cookie-header formats for bulk cookie import (#1257)
- **Dashboard proxy support** - The observability dashboard now works from proxied origins via a same-origin proxy, enabling deployment behind reverse proxies and path-based routing (#1111)

### Bug Fixes

- Fixed **`doctor` command** generating duplicate check ids when called multiple times in the same process (#1330)

### Infrastructure

- Switched npm publishing to **trusted publishing** via GitHub Actions OIDC, removing the need for manually managed npm tokens (#1273)

---

## v0.26.0

April 16, 2026

### New Features

- **`doctor` command** - Added `agent-browser doctor` for one-shot diagnosis of an install. Checks environment, Chrome, running daemons, config files, security, providers, and network connectivity; auto-cleans stale daemon sidecar files on every run; and performs a live headless launch test. Supports `--offline` to skip network probes, `--quick` to skip the launch test, `--fix` for opt-in repairs (install missing Chrome, close version-mismatched daemons, prune expired state files), and `--json` for structured output (#1254)
- **Stable tab ids and labels** - Tabs now have stable string ids like `t1`, `t2`, `t3` that don't shift when other tabs close or popups appear. Tabs can be created with a memorable label via `tab new --label <name> []`, and labels are interchangeable with `t<N>` ids everywhere a tab ref is accepted (`tab `, `tab close `). Bare-integer input is rejected with a teaching error so agents can't mistake stable handles for positional indices (#892, #1249, #1250)
- **`core` skill** - Renamed the built-in `agent-browser` skill to `core` and replaced its ~40-line discovery stub with a ~420-line usage guide covering the core snapshot-ref-act loop, reading, interacting, waiting, common workflows, troubleshooting, and global flags. `agent-browser skills get core` now returns content agents can use directly; `--full` adds references and templates. Added a `hidden:` frontmatter flag so the original `agent-browser` stub stays reachable for `npx skills add` discovery without polluting `skills list` (#1253)
- **JSON Schema for config files** - Added `agent-browser.schema.json` describing every config option with types and descriptions, enabling IDE autocomplete and validation when referenced via `$schema` in `agent-browser.json` or `~/.agent-browser/config.json`. The schema is served from the docs site at `https://agent-browser.dev/schema.json` (#1242, #1248)

### Bug Fixes

- Fixed **`--state` / `AGENT_BROWSER_STATE`** not actually loading saved browser state (cookies and localStorage) at launch. The flag had been fully plumbed through parsing, env propagation, and validation since the native Rust rewrite, but the load step was never wired up. Storage state now loads after launch across all four paths: explicit launch, auto-connect, provider, and local Chrome (#1241)

### Documentation

- `--help` output now shows the **skills** section first so agents discover `skills get core` (the canonical usage guide) before the core command list (#1251)

---

## v0.25.5

April 16, 2026

### Bug Fixes

- Fixed **`--auto-connect` CDP discovery** preferring HTTP endpoint discovery over the DevToolsActivePort websocket path, which could fail on some setups. The CLI now reads the websocket path from DevToolsActivePort first and only falls back to HTTP discovery (#1218)
- Fixed **recording context viewport** not inheriting the active viewport dimensions, causing recordings to use default resolution instead of the configured viewport (#1208)
- Fixed **`get box` and `get styles`** printing no data in text mode (#1231, #1233)
- Fixed **active page changing** when closing or removing earlier tabs. The previously focused page is now preserved correctly (#1220)

---

## v0.25.4

April 12, 2026

### New Features

- **`skills` command** - Added `agent-browser skills` command for discovering and installing agent skills, with built-in evaluation support for testing skills against live browser sessions (#1225, #1227)

### Bug Fixes

- Fixed **custom viewport dimensions** not being used in streaming frame metadata and image resolution (#1033)
- Fixed **`--ignore-https-errors`** not being re-applied to recording contexts, causing TLS errors during screen recordings (#1178)
- Fixed **duplicate option numbering** in the auth skill documentation (#1161)

### Documentation

- The docs site header now **dynamically fetches** the GitHub star count (#1202)

---

## v0.25.3

April 7, 2026

### Bug Fixes

- Fixed **hidden radio/checkbox inputs missing from snapshot refs** when a `<label>` wraps a `display:none` `` or ``. Chrome excludes these inputs from the accessibility tree entirely, making it impossible for AI agents to identify radio buttons and checkboxes via refs. Hidden inputs inside elements are now detected during cursor-interactive scanning and their parent nodes are promoted to the correct role with proper name and checked state (#1085)

### Documentation

- Added **clickable heading anchors** to the docs site, making it easy to link directly to any section (#1175)

---

## v0.25.2

April 6, 2026

### Bug Fixes

- Fixed **Chrome being killed after ~10s idle on Linux** caused by `PR_SET_PDEATHSIG` tracking the blocking thread that spawned Chrome rather than the daemon process. When Tokio reaped the idle thread, the kernel sent SIGKILL to Chrome even though the daemon was still alive (#1157, #1173)

---

## v0.25.1

April 6, 2026

### Improvements

- **Embedded dashboard** - The observability dashboard is now bundled directly into the CLI binary using `rust-embed`, eliminating the need for `dashboard install`. The dashboard is available immediately after installing agent-browser (#1169)

---

## v0.25.0

April 6, 2026

### New Features

- **AI chat command** - Added `chat` command for AI-powered browser automation. Supports single-shot mode (`chat "open google.com"`) and an interactive REPL. The AI agent can execute any agent-browser command via tool calls. Requires `AI_GATEWAY_API_KEY`. Configure the model with `--model` or `AI_GATEWAY_MODEL` (#1160, #1163)
- **Dashboard AI chat** - The observability dashboard now includes a built-in AI chat interface for conversational browser control alongside live session views (#1160, #1163)
- **`snapshot --urls`** - New `-u`/`--urls` flag to include href URLs for link elements in snapshot output, giving agents direct access to link targets without additional queries (#1160)
- **Batch argument mode** - The `batch` command now accepts commands as inline arguments in addition to reading from stdin, simplifying single-invocation multi-command workflows (#1160)

### Bug Fixes

- Fixed **`getByRole`** matching wrong elements (e.g. `<link>` stylesheet elements instead of `<a>` anchors) by rewriting the implementation to use the CDP accessibility tree with ref-based element resolution instead of CSS selectors (#1145)
- Fixed **`upload` command** not supporting accessibility tree refs (`@eN`) for file upload element selection (#1156)
- Fixed **`AGENT_BROWSER_DEFAULT_TIMEOUT`** not being applied to `wait` commands. The environment variable now propagates to all wait variants (`wait`, `wait --url`, `wait --text`, `wait --load`, `wait --fn`, `wait --download`) (#1153)
- Fixed **dashboard download** error handling with improved retry logic for more reliable dashboard installation (#1154)

---

## v0.24.1

April 4, 2026

### New Features

- **Chrome profile login state reuse** - `--profile <name>` now resolves Chrome profile names (e.g. `Default`, `Profile 1`) and copies the profile to a temp directory to reuse login state, cookies, and extensions without modifying the original. Added `profiles` command to list available Chrome profiles with `--json` support (#1131)

### Bug Fixes

- Fixed **`--ignore-https-errors`** not passing `--ignore-certificate-errors` as a Chrome launch flag, causing TLS errors like `ERR_SSL_PROTOCOL_ERROR` to be rejected at the network layer before CDP could intervene (#1132)
- Fixed **orphaned Chrome processes** on daemon exit by spawning Chrome in its own process group and killing the entire group on shutdown. On Linux, `PR_SET_PDEATHSIG` ensures Chrome is killed even if the daemon is OOM-killed (#1137)
- Fixed **CDP attach hang on Chrome 144+** when connecting to real browser sessions. Targets paused waiting for the debugger after attach are now resumed with `Runtime.runIfWaitingForDebugger` (#1133)
- Fixed **stale daemon after upgrade** silently reusing the old daemon process with broken CDP behavior. The daemon now writes a `.version` sidecar file and auto-restarts on version mismatch (#1134)
- Fixed **stale daemon/socket recovery** where `close --all` failed to clean up zombie daemons and stale files. Unreachable daemons are now force-killed and orphaned socket/pid files are removed (#1136)
- Fixed **idle timeout** not being respected because the sleep future was recreated on every select loop iteration, preventing the deadline from being reached (#1110)
- Fixed **browser not relaunching** when launch options change (e.g. adding extensions to `config.json`) between consecutive launch commands (#996)
- Fixed **`auto_launch()`** not honouring `AGENT_BROWSER_PROVIDER` for cloud providers, causing non-launch commands to fall back to local Chrome instead of connecting via the provider API (#1126)
- Fixed **HAR capture missing API requests** under heavy traffic by increasing the CDP broadcast buffer from 256 to 4096 events, reducing the drain interval from 500ms to 100ms, and enabling network tracking in cross-origin iframes (#1135)

---

## v0.24.0

April 2, 2026

### New Features

- **AWS Bedrock AgentCore provider** - Added AWS Bedrock AgentCore as a cloud browser provider. Connect with `--provider agentcore` or `AGENT_BROWSER_PROVIDER=agentcore`. Uses lightweight manual SigV4 signing for authentication with support for the full AWS credential provider chain (environment variables, AWS CLI, SSO, IAM roles). Configure with `AGENTCORE_REGION`, `AGENTCORE_PROFILE_ID`, and `AGENTCORE_BROWSER_ID` environment variables. Returns session ID and Live View URL in the launch response (#397)

### Documentation

- Added AgentCore provider page to docs site, README options table, SKILL.md, and dashboard provider icons (#1120)

---

## v0.23.4

March 31, 2026

### Bug Fixes

- Fixed **daemon hang on Linux** caused by a `waitpid(-1)` race condition in the SIGCHLD handler that stole exit statuses from Rust's `Child` handles, leaving the daemon in a broken state. Replaced the global signal handler with targeted crash detection via the existing drain interval (#1098)

---

## v0.23.3

March 30, 2026

### Bug Fixes

- Fixed **drag and drop** not working because `mouseMoved` events during the drag omitted the `buttons` bitmask, causing the browser to see `event.buttons === 0` and never fire `dragstart`/`dragover`/`drop` (#1087)

---

## v0.23.2

March 30, 2026

### New Features

- **Dashboard session creation** - Sessions can now be created directly from the dashboard UI. A new session dialog provides a unified selector grid for local engines (Chrome, Lightpanda) and cloud providers (Browserbase, Browserless, Browser Use, Kernel) with async creation, loading state, and error display (#1092)
- **Dashboard provider icons** - The session sidebar now shows the provider or engine icon for each session, making it easy to identify which backend a session is using (#1092)

### Bug Fixes

- Fixed **Browser Use** provider using an intermediate API call instead of connecting directly via WSS, which caused connection failures (#1092)
- Fixed **Browserbase** provider not sending an explicit JSON body and `Content-Type` header, causing session creation to fail (#1092)
- Fixed **provider navigation** hanging because `wait_for_lifecycle` waited for page load events that remote providers may not emit. Navigation with `--provider` now automatically sets `waitUntil=none` (#1092)
- Fixed **remote CDP connections** timing out by increasing the CDP connect timeout from 10s to 25s for cloud providers (#1092)
- Fixed **zombie daemon processes** not being cleaned up when a provider connection fails during session creation from the dashboard (#1092)

---

## v0.23.1

March 30, 2026

### New Features

- **Auto-dismissal for alert and beforeunload dialogs** - JavaScript `alert()` and `beforeunload` dialogs are now automatically accepted to prevent the agent from blocking indefinitely. `confirm` and `prompt` dialogs still require explicit `dialog accept/dismiss` commands. Disable with `--no-auto-dialog` flag or `AGENT_BROWSER_NO_AUTO_DIALOG` environment variable (#1075)
- **Puppeteer browser cache fallback** - Chrome discovery now searches `~/.cache/puppeteer/chrome/` (or `PUPPETEER_CACHE_DIR`) for Chrome binaries, so users with an existing Puppeteer installation can use agent-browser without a separate install step (#1088)
- **Console output improvements** - `console.log` of objects now shows the actual object preview (e.g. `{userId: "abc", count: 42}`) instead of `"Object"`. JSON output includes a raw `args` array for programmatic access (#1040)

### Bug Fixes

- Fixed **same-document navigation** (e.g. SPA hash routing) hanging forever because `wait_for_lifecycle` waited for a `Page.loadEventFired` that never fires on same-document navigations (#1059)
- Fixed **save_state** only capturing cookies and localStorage for the current origin, silently dropping cross-domain data (e.g. SSO/CAS auth cookies). Now uses `Network.getAllCookies` and collects localStorage from all visited origins (#1064)
- Fixed **externally opened tabs** not appearing in `tab list` when using `--cdp` mode. Tabs opened by the user or another CDP client are now detected and tracked (#1042)
- Fixed **dashboard server** not picking up installed files without a restart. `dashboard install` now takes effect immediately on a running server (#1066)
- Fixed **Windows Chrome extraction** failing because zip path normalization used forward slashes while the extraction code expected backslashes (#1088)

---

## v0.23.0

March 27, 2026

### New Features

- **Observability dashboard** - Added a local web UI (`dashboard`) that shows live browser viewports, command activity feeds, console output, network requests, storage, and extensions for all sessions. Manage it with `dashboard start`, `dashboard stop`, and `dashboard install`. The dashboard runs as a standalone background process and all sessions stream to it automatically (#1034)

```bash
agent-browser dashboard install
agent-browser dashboard start
agent-browser open example.com    # session appears in dashboard
agent-browser dashboard stop
```

- **Runtime stream management** - Added `stream enable`, `stream disable`, and `stream status` commands to control WebSocket streaming at runtime. Streaming is now always enabled by default; `AGENT_BROWSER_STREAM_PORT` overrides the port instead of toggling the feature (#951)
- **Close all sessions** - Added `close --all` flag to close every active browser session at once

### Bug Fixes

- Fixed **Lightpanda engine** compatibility (#1050)
- Fixed **Windows daemon TCP bind** failing when Hyper-V reserves the port by falling back to an OS-assigned port and writing it to a `.port` file (#1041)
- Fixed **Windows dashboard relay** using Unix socket instead of TCP (#1038)
- Fixed **radio/checkbox elements** being dropped from compact snapshot tree because the `ref=` check required a leading `[` that those elements lack (#1008)

---

## v0.22.3

March 25, 2026

### Bug Fixes

- **Re-apply download behavior on recording context** - Fixed an issue where downloads were silently dropped in recording contexts because `Browser.setDownloadBehavior` set at launch only applied to the default context. The download behavior is now re-applied when a new recording context is created (#1019)
- **Reap zombie Chrome process and fast-detect crash for auto-restart** - Added a non-blocking process-exit check before attempting CDP connection checks. This prevents a 3-second CDP timeout when Chrome has already crashed or exited, enabling faster detection and auto-restart of the browser (#1023)
- **Route keyboard `type` through text input** - Fixed keyboard `type` subaction to correctly route through the text input handler, and added support for an `insertText` subaction using `Input.insertText` (#1014)
- **Handle `--clear` flag in `console` command** - Fixed the `console` command to accept and process a `clear` parameter, allowing console event history to be cleared (#1015)

---

## v0.22.2

March 24, 2026

### New Features

- **Dialog status command** - Added `dialog status` command to check whether a JavaScript dialog is currently open (#999)
- **Dialog warning field** - Command responses now include a `warning` field when a JavaScript dialog is pending, indicating the dialog type and message (#999)

### Improvements

- **Standard proxy environment variables** - The proxy setting now automatically falls back to standard environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, and their lowercase variants), with `NO_PROXY`/`no_proxy` respected for bypass rules (#1000)
- **Font packages for `--with-deps`** - Installing with `--with-deps` now includes CJK and emoji font packages on Linux (Debian, RPM, and yum-based distros) to prevent missing glyphs when rendering international content (#1002)

### Bug Fixes

- Fixed `state show` always failing with "Missing 'path' parameter" due to a mismatched JSON field name (#994)
- Fixed `console` command returning only `Done` due to a JSON field name mismatch in the response (#986)
- Fixed browser-domain CDP events being dropped during downloads due to a `sessionId` mismatch (#998)
- Fixed proxy authentication by handling credentials via the CDP `Fetch.authRequired` event rather than passing them inline (#1000)

---

## v0.22.1

March 24, 2026

### Bug Fixes

- Fixed **modifier key chords** (e.g. `Control+a`, `Shift+Enter`, `Control+Shift+a`) not being handled correctly when using `press`. Modifier keys are now parsed and forwarded as CDP modifier bitmasks rather than treated as part of the key name (#980)
- Fixed **query parameters being dropped** from `--cdp` HTTP URLs (e.g. `http://host:9222?mode=Hello`). Query strings are now preserved and forwarded to the remote CDP endpoint (#982)

---

## v0.22.0

March 23, 2026

### New Features

- **Cross-origin iframe support** - Added support for snapshots and interactions within cross-origin iframes via `Target.setAutoAttach` (#949)
- **Network request detail and filtering** - Added `network request <requestId>` command to view full request/response detail, and new filtering options for `network requests` including `--type` (e.g. `xhr,fetch`), `--method` (e.g. `POST`), and `--status` (e.g. `2xx`, `400-499`) (#935)

```bash
agent-browser network requests --type xhr,fetch --method POST
agent-browser network request <requestId>
```

### Improvements

- **Snapshot usability** - Reduced AI cognitive load by filtering semantic noise from snapshot output; cursor-interactive elements are now included by default, making the `-C` flag unnecessary (#968)
- **Upgrade command** - Improved robustness of installation method detection in the upgrade command (#960)
- **Target tracking** - Enhanced target tracking and page information handling for more reliable browser session management (#969)

### Bug Fixes

- Fixed **viewport dimensions** being reported incorrectly in streaming status messages and screencast (#952)
- Fixed **`find` command** flags such as `--exact` and `--name` leaking into fill values when used with fill actions (#955)
- Fixed **state commands** incorrectly starting the daemon when no `session_name` is provided (#677, #964)
- Fixed **auto-connect** triggering when the daemon is already running, preventing duplicate connections (#971)
- Fixed **Enter key press** not working by adding a text field to `keyDown` events (#972)
- Fixed **download command** to properly handle absolute paths and correctly click target elements (#970)

### Breaking Changes

- The `-C` / `--cursor` flag for `snapshot` is deprecated; cursor-interactive elements are now included by default and the flag has no additional effect (#968)

---

## v0.21.4

March 20, 2026

### Bug Fixes

- **Auth login readiness** - `agent-browser auth login` now navigates with `load`, waits for usable login form selectors, and uses staged username detection (targeted email/username selectors first, then broad text-input fallback). This reduces SPA timing failures, avoids false matches on unrelated text fields, and prevents `networkidle` hangs on pages with continuous background requests.

---

## v0.21.3

March 20, 2026

### Bug Fixes

- **WebSocket keepalive for remote browsers** - Added WebSocket Ping frames and TCP `SO_KEEPALIVE` to prevent CDP connections from being silently dropped by intermediate proxies during idle periods (#936)
- **XPath selector support** - Fixed element resolution to correctly handle the `xpath=` selector prefix (#908)

### Performance

- **Fast-path for identical snapshots** - Short-circuits the Myers diff algorithm when comparing a snapshot to itself, avoiding unnecessary computation in retry and loop workloads (#922)

---

## v0.21.2

March 18, 2026

### Bug Fixes

- **Deduplicate text content in snapshots** - Fixed an issue where duplicate text content appeared in page snapshots (#909)
- **Native mouse drag state** - Fixed incorrect raw native mouse drag state not being properly tracked across `down`, `move`, and `up` events (#872)
- **Chrome headless launch failures** - Fixed browser launch failures caused by the `--enable-unsafe-swiftshader` flag in Chrome headless mode (#915)
- **Origin-scoped `--headers` persistence** - Restored correct persistence of origin-scoped headers set via `--headers` across navigation commands (#894)
- **Relative URLs in WebSocket domain filter** - Fixed handling of relative URLs in the WebSocket domain filter script (#624)

---

## v0.21.1

March 18, 2026

### New Features

- **HAR 1.2 network capture** - Added commands to capture and export network traffic in HAR 1.2 format, including accurate request/response timing, headers, body sizes, and resource types sourced from Chrome DevTools Protocol events (#864)
- **Built-in `upgrade` command** - Added `agent-browser upgrade` to self-update the CLI; automatically detects your installation method (npm, Homebrew, or Cargo) and runs the appropriate update command (#898)

```bash
agent-browser upgrade
```

---

## v0.21.0

March 17, 2026

### New Commands

- **`batch`** -- Execute multiple commands in a single invocation. Pipe a JSON array of string arrays to stdin and receive results sequentially. Supports `--bail` to stop on first error and `--json` for structured output.

```bash
echo '[["open","example.com"],["snapshot"]]' | agent-browser batch --json
```

- **`network har start/stop`** -- Capture and export network traffic in HAR 1.2 format.

```bash
agent-browser network har start
# ... interact with the page ...
agent-browser network har stop ./trace.har
```

### New Features

- **iframe support** -- CLI interactions and snapshots now traverse into iframe content, enabling automation of cross-frame pages.
- **`--idle-timeout` flag** -- Automatically shut down the daemon after a period of inactivity. Accepts human-friendly formats such as `10s`, `3m`, `1h`, or raw milliseconds. Also available as `AGENT_BROWSER_IDLE_TIMEOUT_MS`.
- **Cursor-interactive elements in snapshots** -- Cursor-interactive elements are now embedded directly into the snapshot tree for richer context.
- **Brave Browser auto-connect** -- Auto-discovery of Brave Browser for CDP connections on macOS, Linux, and Windows.
- **linux-musl (Alpine) builds** -- Pre-built binaries for linux-musl targeting both x64 and arm64, enabling native support for Alpine Linux and other musl-based distributions.
- **WebSocket fallback for CDP discovery** -- When HTTP-based CDP endpoint discovery fails, the CLI now falls back to a WebSocket connection automatically.

### Improvements

- **`--full`/`-f` refactored to command-level flag** -- Moved from a global flag to a per-command flag for clearer scoping.
- **Enhanced Chrome launch** -- Added `--user-data-dir` support and configurable launch timeout for more reliable browser startup. Chrome now retries launching up to 3 times on transient startup failures.
- **Consecutive `--auto-connect` commands** -- Multiple consecutive auto-connect commands no longer require a full browser relaunch; external connections are correctly identified and reused.
- **Batched CDP calls** -- `snapshot -C` and `screenshot --annotate` now batch CDP calls instead of issuing sequential round-trips per element, preventing timeouts on high-latency WSS connections.

### Bug Fixes

- Fixed remote CDP (WSS) snapshot and screenshot hangs by removing WebSocket message/frame size limits
- Fixed Material Design `check`/`uncheck` falling back to JS `.click()` for overlay-based controls
- Fixed punctuation characters being dropped in the `type` command
- Fixed WebSocket streaming by keeping the StreamServer instance alive
- Filtered internal Chrome targets (`chrome://`, `devtools://`) from auto-connect discovery
- Fixed `snapshot --selector` scoping to the matched element's subtree
- Fixed network idle detection returning prematurely for cached pages
- Fixed daemon panic on broken stderr pipe during Chrome launch
- Fixed broadcast channel lag being treated as stream closure
- Fixed daemon liveness detection for PID namespace isolation (e.g. `unshare`)
- Fixed Ubuntu dependency install accidentally removing system packages

---

## v0.20.0

March 13, 2026

### Full Native Rust

agent-browser is now 100% native Rust. The Node.js/Playwright daemon has been completely removed -- no Node.js runtime or Playwright dependency is required to run the daemon. The Rust native daemon is now the only implementation.



| Metric | Node.js | Rust |  |
| --- | --- | --- | --- |
| Cold start | 1002ms | 617ms | 1.6x faster |
| Daemon memory | 143 MB | 8 MB | 18x less |
| Install size | 710 MB | 7 MB | 99x smaller |



```bash
npm install agent-browser      # 7 MB install
agent-browser install              # download Chrome
agent-browser open example.com
agent-browser snapshot
```

### Improvements

- **Benchmarks** -- Added benchmark suite for comparing native vs Node.js daemon performance across cold start, warm start, memory, and install size.
- **Chromium installer hardened** -- Fixed zip path traversal vulnerability in Chrome for Testing installer.

### Bug Fixes

- Fixed `--headed false` flag not being respected in CLI
- Fixed "not found" error pattern in `to_ai_friendly_error` incorrectly catching non-element errors
- Fixed storage local key lookup parsing and text output
- Fixed Lightpanda engine launch with release binaries
- Hardened Lightpanda startup timeouts

---

## v0.19.0

March 13, 2026

### New Features

- **Browserless.io provider** -- Added browserless.io as a browser provider, supported in both Node.js and native daemon paths. Connect to remote Browserless instances using the `--provider browserless` flag or `AGENT_BROWSER_PROVIDER=browserless` environment variable.

```bash
export BROWSERLESS_API_KEY="your-api-key"
agent-browser --provider browserless open example.com
agent-browser --provider browserless screenshot ./page.png
```

- **`clipboard` command** -- Read from and write to the browser clipboard. Supports `read`, `write`, `copy` (simulates Ctrl+C), and `paste` (simulates Ctrl+V) operations.

```bash
agent-browser clipboard read
agent-browser clipboard write "Hello, World!"
agent-browser clipboard copy
agent-browser clipboard paste
```

- **Screenshot output configuration** -- New global flags for persistent screenshot settings: `--screenshot-dir`, `--screenshot-quality`, and `--screenshot-format`. Also available as environment variables `AGENT_BROWSER_SCREENSHOT_DIR`, `AGENT_BROWSER_SCREENSHOT_QUALITY`, and `AGENT_BROWSER_SCREENSHOT_FORMAT`.

```bash
agent-browser screenshot --screenshot-dir ./shots
agent-browser screenshot --screenshot-format jpeg --screenshot-quality 80
```

### Bug Fixes

- Fixed `wait --text` not working in native daemon path
- Fixed `BrowserManager.navigate()` and package entry point
- Fixed extensions not being loaded from `config.json`
- Fixed scroll on page load
- Fixed HTML retrieval by using `browser.getLocator()` for selector operations

---

## v0.18.0

March 12, 2026

### New Features

- **`inspect` command** -- Opens Chrome DevTools for the active page by launching a local proxy server that forwards the DevTools frontend to the browser's CDP WebSocket. Agent commands continue to work while DevTools is open.

```bash
agent-browser open example.com
agent-browser inspect          # opens DevTools in your browser
agent-browser click "Submit"   # commands still work while DevTools is open
```

- **`get cdp-url` subcommand** -- Retrieve the Chrome DevTools Protocol WebSocket URL for the active page, useful for connecting external debugging tools.

```bash
agent-browser get cdp-url
```

- **Screenshot annotate** -- The `--annotate` flag overlays numbered labels on interactive elements in screenshots.

### Improvements

- **KERNEL_API_KEY now optional** -- External credential injection no longer requires `KERNEL_API_KEY` to be set, making it easier to use Kernel with pre-configured environments.
- **Browserbase simplified** -- Removed the `BROWSERBASE_PROJECT_ID` requirement, reducing setup friction for Browserbase users.

### Bug Fixes

- Fixed Browserbase API using incorrect endpoint to release sessions
- Fixed CDP connect paths using hardcoded 10s timeout instead of the configurable default timeout
- Fixed lone Unicode surrogates causing errors by sanitizing with `toWellFormed()`
- Fixed CDP connection failure on IPv6-first systems
- Fixed recordings not inheriting the current viewport settings

---

## v0.17.1

March 9, 2026

### Improvements

- **Viewport scale factor** -- Added support for device scale factor (retina display) in the viewport command via an optional `scale` parameter.
- **Webview target support** -- Added webview target type support for better Electron application compatibility. The pages list now includes target type information.

---

## v0.17.0

March 8, 2026

### New Features

- **Lightpanda browser engine support** -- Added `--engine <name>` flag to select the browser engine (`chrome` by default, or `lightpanda`), implying `--native` mode. Configurable via `AGENT_BROWSER_ENGINE` environment variable.

```bash
agent-browser --engine lightpanda open example.com
```

- **Dialog dismiss command** -- Added support for `dismiss` subcommand in dialog command parsing.

### Improvements

- **Daemon startup error reporting** -- Daemon startup errors are now surfaced directly instead of showing an opaque timeout message.
- **CDP port discovery** -- Replaced hand-rolled HTTP client with `reqwest` for more reliable CDP port discovery.
- **Chrome extensions** -- Extensions now load correctly by forcing headed mode when extensions are present.
- **Google Translate bar suppression** -- Suppressed the Google Translate bar in native headless mode to avoid interference.
- **Auth cookie persistence** -- Auth cookies are now persisted on browser close in native mode.

### Bug Fixes

- Fixed native auth login failing due to incompatible encryption format.

### Performance

- Added benchmarks to the CLI codebase.

---

## v0.16.0

March 3, 2026

### New Features

- **Native Rust daemon (experimental).** A pure Rust daemon that communicates with Chrome directly via the Chrome DevTools Protocol (CDP), eliminating Node.js and Playwright dependencies entirely. Enable with `--native`, `AGENT_BROWSER_NATIVE=1`, or `"native": true` in your config file. Supports 150+ commands with full parity to the default Node.js daemon.

```bash
# Via flag
agent-browser --native open example.com

# Via environment variable
export AGENT_BROWSER_NATIVE=1
agent-browser open example.com
```

Or add to `agent-browser.json`:

```json
{"native": true}
```

### Architecture



|  | Default (Node.js) | Native (`--native`) |
| --- | --- | --- |
| **Runtime** | Node.js + Playwright | Pure Rust binary |
| **Protocol** | Playwright protocol | Direct CDP / WebDriver |
| **Install size** | Larger (Node.js + npm deps) | Smaller (single binary) |
| **Browser support** | Chromium, Firefox, WebKit | Chromium, Safari (via WebDriver) |
| **Stability** | Stable | Experimental |



### What's Supported

All core commands work in native mode: navigation, interaction (click, fill, type, press, hover, scroll, drag), observation (snapshot, screenshot, eval), state management (cookies, storage, state save/load), tabs, emulation (viewport, device, timezone, locale, geolocation), streaming, diffing, recording, and profiling.

The native daemon also includes a WebDriver backend for Safari and iOS support.

### Known Limitations

- Firefox and WebKit are not yet supported (Chromium and Safari only)
- Playwright trace format is not available (uses Chrome's built-in tracing)
- HAR export is not available
- Network route interception uses CDP Fetch domain instead of Playwright's route API
- The native and Node.js daemons share the same session socket. Use `agent-browser close` before switching between modes.

See the [Native Mode](/native-mode) page for full details.

---

## v0.15.0

February 25, 2026

### New Features

- **Authentication vault** -- Store credentials locally (always AES-256-GCM encrypted) and reference them by name. The LLM never sees passwords. Commands: `auth save`, `auth login`, `auth list`, `auth show`, `auth delete`. Passwords can be piped via stdin (`--password-stdin`) to avoid shell history exposure.
- **Content boundary markers** -- `--content-boundaries` wraps page-sourced output in structural delimiters with a per-process CSPRNG nonce, so LLMs can distinguish trusted tool output from untrusted page content. In `--json` mode, a `_boundary` object is injected with `nonce` and `origin` fields.
- **Domain allowlist** -- `--allowed-domains` restricts navigation, sub-resource requests, WebSocket connections, and EventSource streams to trusted domains. Supports exact match and wildcard prefix patterns (e.g., `*.example.com`).
- **Action policy** -- `--action-policy` gates actions using a static JSON policy file with `allow`/`deny` lists across 13 action categories. Auth vault operations bypass policy enforcement.
- **Action confirmation** -- `--confirm-actions` requires explicit approval for sensitive action categories. New `confirm` and `deny` commands for orchestrator use. `--confirm-interactive` enables human-in-the-loop terminal prompts (auto-denies if stdin is not a TTY). Pending confirmations auto-deny after 60 seconds.
- **Output length limits** -- `--max-output` truncates large page outputs to prevent LLM context flooding.
- **`--download-path` option** -- Set a default download directory via flag, `AGENT_BROWSER_DOWNLOAD_PATH` env var, or `downloadPath` config key. Without it, downloads go to a temporary directory deleted when the browser closes.
- **`--selector` flag for scroll** -- Scroll within a specific container element instead of the page: `agent-browser scroll down 500 --selector "div.scroll-container"`

```bash
# Auth vault
echo "pass" | agent-browser auth save github --url https://github.com/login --username user --password-stdin
agent-browser auth login github

# Security flags
agent-browser --content-boundaries --allowed-domains "example.com,*.example.com" --max-output 50000 open https://example.com

# Download path
agent-browser --download-path ./downloads open https://example.com

# Scroll within container
agent-browser scroll down 500 --selector "div.content"
```

### Environment Variables

Six new environment variables for security configuration: `AGENT_BROWSER_CONTENT_BOUNDARIES`, `AGENT_BROWSER_MAX_OUTPUT`, `AGENT_BROWSER_ALLOWED_DOMAINS`, `AGENT_BROWSER_ACTION_POLICY`, `AGENT_BROWSER_CONFIRM_ACTIONS`, `AGENT_BROWSER_CONFIRM_INTERACTIVE`.

---

## v0.14.0

February 23, 2026

### New Features

- **`keyboard` command** -- Type with real keystrokes, insert text, and press shortcuts at the currently focused element without needing a selector (`keyboard type`, `keyboard inserttext`).
- **`--color-scheme` flag** -- Persistent dark/light mode preference across browser sessions via flag or `AGENT_BROWSER_COLOR_SCHEME` env var.

```bash
agent-browser keyboard type "Hello world"
agent-browser keyboard inserttext "pasted text"
agent-browser --color-scheme dark open https://example.com
```

### Bug Fixes

- Fixed IPC EAGAIN errors (os error 35/11) with backpressure-aware socket writes, command serialization, and lowered default Playwright timeout to 25s (configurable via `AGENT_BROWSER_DEFAULT_TIMEOUT`).
- Fixed remote debugging (CDP) reconnection.
- Fixed state load failing when no browser is running.
- Fixed `--annotate` flag warning appearing when not explicitly passed via CLI.

---

## v0.13.0

February 20, 2026

### New Features

- **Diff commands** -- Compare snapshots, screenshots, and URLs between page states. Run visual pixel diffs against baseline images, compare accessibility tree snapshots with customizable depth and selectors, and diff two URLs side-by-side with optional screenshot comparison.

```bash
agent-browser diff snapshot
agent-browser diff screenshot --baseline before.png
agent-browser diff url https://staging.example.com https://prod.example.com
```

---

## v0.12.0

February 18, 2026

### New Features

- **Annotated screenshots** -- `--annotate` flag overlays numbered labels on interactive elements and prints a legend mapping each label to its element ref. Enables multimodal AI models to reason about visual layout while using the same `@eN` refs for subsequent interactions. Also settable via `AGENT_BROWSER_ANNOTATE` env var.

```bash
agent-browser screenshot --annotate
```

---

## v0.11.1

February 18, 2026

### Documentation

- Added documentation for command chaining with `&&` across README, CLI help output, docs, and skill files.

---

## v0.11.0

February 17, 2026

### New Features

- **Configuration file support** -- Automatic loading from user (`~/.agent-browser/config.json`) and project (`./agent-browser.json`) directories with priority-based merging.
- **Profiler commands** -- Chrome DevTools profiling with `profiler start` and `profiler stop`.
- **Browser extension loading** -- `--extension` flag to load browser extensions.
- **Storage state management** -- `state save` and `state load` commands for auth state persistence.
- **iOS device emulation** -- `--device` flag for device emulation.
- **Enhanced click** -- `--new-tab` option for click commands.
- **Enhanced find** -- Additional actions and filtering options.
- **CDP WebSocket URLs** -- `--cdp` now accepts WebSocket URLs in addition to ports.

---

## v0.10.0

February 13, 2026

### New Features

- **Session persistence** - Automatic save/restore of cookies and localStorage across browser restarts using `--session-name` flag
- **Encrypted state** - Optional AES-256-GCM encryption for saved session state data
- **State management commands** - New commands for listing, showing, renaming, clearing, and cleaning up session state files
- **New tab on click** - Added `--new-tab` option for click commands to open links in new tabs

```bash
# Persist session state
agent-browser --session-name myapp open https://example.com

# Manage saved states
agent-browser state list
agent-browser state show myapp
agent-browser state clear myapp
```

---

## v0.9.4

February 13, 2026

### Bug Fixes

- Fixed all Clippy lint warnings in the Rust CLI

---

## v0.9.3

February 12, 2026

### Improvements

- Added support for custom executable path in CLI browser launch options
- Documentation site UI improvements including a new chat component with sheet-based interface

---

## v0.9.2

February 10, 2026

### Improvements

- Migrated documentation site to MDX for improved content authoring
- Added AI-powered docs chat feature
- Updated README with Homebrew installation instructions for macOS users

---

## v0.9.1

February 5, 2026

### New Features

- **`--allow-file-access` flag** - Enable opening and interacting with local `file://` URLs (PDFs, HTML files) by passing Chromium flags that allow JavaScript access to local files
- **`-C`/`--cursor` flag for snapshots** - Include cursor-interactive elements like divs with onclick handlers or `cursor:pointer` styles

```bash
agent-browser --allow-file-access open file:///path/to/document.pdf
agent-browser snapshot -C
```

---

## v0.9.0

February 3, 2026

### New Features

- **iOS Simulator support** - Mobile Safari testing via Appium with real device and simulator support

```bash
# List available iOS simulators
agent-browser device list

# Launch on iOS device
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com

# Touch interactions
agent-browser tap @e1
agent-browser swipe up
```

---

## v0.8.10

February 2, 2026

### Improvements

- Added `--stdin` flag for eval command to read JavaScript from stdin, enabling heredoc usage for multiline scripts
- Fixed binary permission issues on macOS/Linux when postinstall scripts don't run

---

## v0.8.9

February 2, 2026

### Improvements

- Added `--stdin` flag for eval command to read JavaScript from stdin

---

## v0.8.8

February 2, 2026

### Improvements

- Added base64 encoding support for the eval command with `-b`/`--base64` flag to avoid shell escaping issues
- Updated documentation with AI agent setup instructions

---

## v0.8.7

February 2, 2026

### Bug Fixes

- Fixed browser launch options not being passed correctly when using persistent profiles
- Added pre-flight checks for socket path length limits and directory write permissions
- Improved error handling to properly exit with failure status when browser launch fails

---

## v0.8.6

January 31, 2026

### Bug Fixes

- Improved daemon connection reliability with automatic retry logic for transient errors
- CLI now cleans up stale socket and PID files before starting a new daemon

---

## v0.8.5

January 29, 2026

### Bug Fixes

- Fixed version synchronization to automatically update Cargo.lock alongside Cargo.toml during releases
- Made the CLI binary executable in the npm package

---

## v0.8.4

January 27, 2026

### Bug Fixes

- Fixed "Daemon not found" error when running through AI agents by resolving symlinks in the executable path

---

## v0.8.3

January 27, 2026

### Improvements

- Replaced shell-based CLI wrappers with a cross-platform Node.js wrapper to enable npx support on Windows
- Added postinstall logic to patch npm bin entry on global installs for zero-overhead native binary invocation
- Added CI tests to verify global installation across all platforms

---

## v0.8.2

January 26, 2026

### Bug Fixes

- Fixed the Windows CMD wrapper to use the native binary directly instead of routing through Node.js
- Added retry logic to CI install command for transient browser installation failures

---

## v0.8.1

January 26, 2026

### Improvements

- Improved release workflow to validate binary file sizes and ensure binaries are executable after npm install
- Updated documentation site with a new mobile navigation system

---

## v0.8.0

January 26, 2026

### New Features

- **Kernel cloud browser provider** - Connect to Kernel (kernel.sh) for remote browser infrastructure with stealth mode and persistent profiles

```bash
# Via -p flag
agent-browser -p kernel open https://example.com

# Via environment variable
export AGENT_BROWSER_PROVIDER=kernel
export KERNEL_API_KEY=your-api-key
agent-browser open https://example.com

# With persistent profile
export KERNEL_PROFILE_NAME=my-profile
agent-browser open https://example.com
```

- **Ignore HTTPS certificate errors** - New flag for working with self-signed certificates and development environments

```bash
agent-browser --ignore-https-errors open https://localhost:3000
```

- **Enhanced cookie management** - Extended `cookies set` command with additional flags for setting cookies before page load

```bash
agent-browser cookies set session_id "abc123" --url https://app.example.com --httpOnly --secure
agent-browser cookies set token "xyz" --domain .example.com --path /api --expires 1735689600
```

### Bug Fixes

- Fixed tab list command not recognizing new pages opened via clicks or `target="_blank"` links
- Fixed `check` command hanging indefinitely
- Fixed `set device` not applying deviceScaleFactor - HiDPI screenshots now work correctly
- Fixed state load and profile persistence not working in v0.7.6
- Screenshots now save to temp directory when no path is provided

### Security

- Daemon and stream server now reject cross-origin connections

---

## v0.7.1

January 23, 2026

### Bug Fixes

- **Fix native binary distribution** - Native binaries for all platforms (Linux x64/arm64, macOS x64/arm64, Windows x64) are now included in the npm package. Previously, the release workflow published to npm before building binaries, causing "No binary found" errors on installation.

---

## v0.7.0

January 23, 2026

### New Features

- **Cloud browser providers** - Connect to Browserbase or Browser Use for remote browser infrastructure

```bash
# Via -p flag (recommended)
agent-browser -p browserbase open https://example.com
agent-browser -p browseruse open https://example.com

# Via environment variable
export AGENT_BROWSER_PROVIDER=browserbase
agent-browser open https://example.com
```

- **Persistent browser profiles** - Store cookies, localStorage, and login sessions across browser restarts

```bash
agent-browser --profile ~/.myapp-profile open myapp.com
# Login persists across restarts
```

- **Remote CDP WebSocket URLs** - Connect to remote browser services via WebSocket

```bash
agent-browser --cdp "wss://browser-service.com/cdp?token=..." snapshot
```

- **`download` command** - Trigger downloads and wait for completion

```bash
agent-browser download @e1 ./file.pdf
agent-browser wait --download ./output.zip --timeout 30000
```

- **Browser launch configuration** - Fine-grained control over browser startup

```bash
agent-browser --args "--disable-gpu,--no-sandbox" open example.com
agent-browser --user-agent "Custom UA" open example.com
agent-browser --proxy-bypass "localhost,*.internal" open example.com
```

- **Enhanced skills** - Hierarchical structure with references and templates for Claude Code

### Bug Fixes

- Screenshot command now supports refs and has improved error messages
- WebSocket URLs work in `connect` command
- Fixed socket file location (uses `~/.agent-browser` instead of TMPDIR)
- Windows binary path fix (.exe extension)
- State load and path-based actions now show correct output messages

### Documentation

- Added Claude Code marketplace plugin installation instructions
- Updated skill documentation with references and templates
- Improved error documentation

---

## v0.6.0

January 18, 2026

### New Features

- **Video recording** - Record browser sessions to WebM using Playwright's native recording

```bash
agent-browser record start ./demo.webm
agent-browser click @e1
agent-browser record stop
```

- **`connect` command** - Connect to a browser via CDP and persist the connection for subsequent commands

```bash
agent-browser connect 9222
agent-browser snapshot  # No --cdp needed after connect
```

- **`--proxy` flag** - Configure browser proxy with optional authentication

```bash
agent-browser --proxy http://user:pass@proxy.com:8080 open example.com
```

- **`get styles` command** - Extract computed styles from elements

```bash
agent-browser get styles "button"
```

- **Claude marketplace plugin** - Added `.claude-plugin/marketplace.json` for Claude Code integration
- **Enhanced network output** - `network requests` now shows method, URL, and resource type
- **`--version` flag** - Display CLI version

### Bug Fixes

- Fix Windows daemon startup and port calculation
- Support `libasound2t64` on newer Ubuntu versions (24.04+)
- Prevent CDP timeout on empty URL tabs
- Output screenshot as base64 when no path provided
- Resolve refs in `get value` command
- Support URL parameter in `tab new` command
- Allow `about:`, `data:`, and `file:` URL schemes
- Detect stale unix socket by attempting connection
- Respect `AGENT_BROWSER_HEADED` environment variable
- Handle SIGPIPE to prevent panic when piping to `head`/`tail`
- Fix null path validation in screenshot command

### Protocol Alignment

These changes align the CLI with the daemon protocol for consistency:

- `select` command now uses `values` field (supports multiple selections)
- `frame main` uses `mainframe` action
- `mouse wheel` uses `wheel` action
- `set media` uses `emulatemedia` action
- Console output uses `messages` field

### Documentation

- Expanded SKILL.md with comprehensive command reference
- Updated README with new commands and options
- Updated CDP mode documentation with `connect` workflow

---

## File: docs/src/app/cdp-mode/page.mdx

# CDP Mode

Connect to an existing browser via Chrome DevTools Protocol:

```bash
# Start Chrome with: google-chrome --remote-debugging-port=9222

# Connect once, then run commands without --cdp
agent-browser connect 9222
agent-browser snapshot
agent-browser tab
agent-browser close

# Or pass --cdp on each command
agent-browser --cdp 9222 snapshot
```

## Remote WebSocket URLs

Connect to remote browser services via WebSocket URL:

```bash
# Connect to remote browser service
agent-browser --cdp "wss://browser-service.com/cdp?token=..." snapshot

# Works with any CDP-compatible service
agent-browser --cdp "ws://localhost:9222/devtools/browser/abc123" open example.com
```

The `--cdp` flag accepts either:

- A port number (e.g., `9222`) for local connections via `http://localhost:{port}`
- A full WebSocket URL (e.g., `wss://...` or `ws://...`) for remote browser services

## Auto-Connect

Use `--auto-connect` to automatically discover and connect to a running Chrome instance without specifying a port:

```bash
# Auto-discover running Chrome with remote debugging
agent-browser --auto-connect open example.com
agent-browser --auto-connect snapshot

# Or via environment variable
AGENT_BROWSER_AUTO_CONNECT=1 agent-browser snapshot
```

Auto-connect discovers Chrome by:

1. Reading Chrome's `DevToolsActivePort` file from the default user data directory
2. Falling back to probing common debugging ports (9222, 9229)
3. If HTTP-based discovery (`/json/version`, `/json/list`) fails, falling back to a direct WebSocket connection

This is useful when:

- Chrome 144+ has remote debugging enabled via `chrome://inspect/#remote-debugging` (which uses a dynamic port)
- You want a zero-configuration connection to your existing browser
- You don't want to track which port Chrome is using

## Tab pinning

When several sessions share one browser over CDP, each session remembers which tab it is bound to (by CDP target id, persisted across daemon restarts). A restarted daemon reattaches to the session's own tab instead of adopting whatever tab is currently active, which in a shared browser is usually another session's.

Pass `--pin-tab` (or set `AGENT_BROWSER_PIN_TAB=1`) to make the binding strict:

```bash
# Two agents sharing one Chrome, each pinned to its own tab
agent-browser --session agent1 --cdp 9222 --pin-tab open https://site-a.com
agent-browser --session agent2 --cdp 9222 --pin-tab open https://site-b.com
```

With `--pin-tab`:

- Attaching with no binding opens a fresh tab instead of adopting an existing one
- If the bound tab is closed, commands fail with a `tab_gone` error instead of silently acting on another tab. JSON responses carry `"code": "tab_gone"`, `data.targetId`, and optional `data.lastUrl`
- `tab list`, `tab new`, and `tab <ref>` still work in that state, so an agent can recover by binding a new tab
- Tabs opened by other sessions or the user never steal the pinned session's active tab

The flag is sticky per session: pass it once and later commands and daemon restarts keep the strict semantics. Pass `--no-pin-tab` to explicitly turn the pin off again. See [Sessions](/sessions) for the multi-session workflow and [Commands](/commands) for tab refs by CDP target id.

The structured `lastUrl` is emitted only for sanitized HTTP(S) URLs and `about:blank`. Credentials, query strings, and fragments are removed from HTTP(S) URLs, while opaque URLs such as `data:` are omitted. Batch JSON exposes the recovery object under `result`.

When re-running a shared-tab script such as the repro from #1530, add `--pin-tab` to the first command for every session. Without it, `open` intentionally preserves the legacy behavior and navigates the shared active tab, so the original script still collides. The same rule applies when sessions attach with `--auto-connect` instead of `--cdp`.

## Color scheme

Use `--color-scheme` to set a persistent preference when connecting via CDP:

```bash
agent-browser --cdp 9222 --color-scheme dark open https://example.com
agent-browser --cdp 9222 snapshot  # stays in dark mode
```

Or set it globally via config or environment variable:

```bash
AGENT_BROWSER_COLOR_SCHEME=dark agent-browser --cdp 9222 open https://example.com
```

## Use cases

This enables control of:

- Electron apps
- Chrome/Chromium with remote debugging
- WebView2 applications
- Remote browser services (via WebSocket URL)
- Any browser exposing a CDP endpoint

## Global options



| Option | Description |
| --- | --- |
| `--session &lt;name&gt;` | Use isolated session |
| `--profile &lt;path&gt;` | Persistent browser profile directory |
| `-p &lt;provider&gt;` | Browser provider (`browserbase`, `browseruse`, `kernel`, `browserless`, `agentcore`, or a configured `browser.provider` plugin) |
| `--headers &lt;json&gt;` | HTTP headers scoped to origin |
| `--executable-path` | Custom browser executable |
| `--args &lt;args&gt;` | Browser launch args (comma-separated) |
| `--user-agent &lt;ua&gt;` | Custom User-Agent string |
| `--proxy &lt;url&gt;` | Proxy server URL |
| `--proxy-bypass &lt;hosts&gt;` | Hosts to bypass proxy |
| `--json` | JSON output for scripts |
| `--name, -n` | Locator name filter |
| `--exact` | Exact, case-sensitive match (accessible name for role) |
| `--headed` | Show browser window |
| `{"--cdp "}` | CDP connection (port or WebSocket URL) |
| `--auto-connect` | Auto-discover and connect to running Chrome |
| `--pin-tab` | Pin the session to its bound tab (strict tab binding) |
| `--no-pin-tab` | Disable a sticky pin previously enabled with `--pin-tab` |
| `--color-scheme &lt;scheme&gt;` | Persistent color scheme (`dark`, `light`, `no-preference`) |
| `--debug` | Debug output |



## Cloud providers

Use the `-p` flag to connect to a cloud browser provider or configured `browser.provider` plugin instead of launching a local browser:

```bash
agent-browser -p browserbase open https://example.com
```

See the [Providers](/providers/browser-use) section for setup and configuration of each built-in provider: [Browser Use](/providers/browser-use), [Browserbase](/providers/browserbase), [Browserless](/providers/browserless), [Kernel](/providers/kernel), and [AgentCore](/providers/agentcore).

---

## File: docs/src/app/installation/page.mdx

# Installation

## Global installation (recommended)

Installs the native Rust binary for maximum performance:

```bash
npm install agent-browser
agent-browser install  # Download Chrome from Chrome for Testing (first time)
```

This is the fastest option -- commands run through the native Rust CLI directly with sub-millisecond parsing overhead.

## Quick start (no install)

```bash
npx agent-browser install   # Download Chrome (first time only)
npx agent-browser open example.com
```

## Project installation (local dependency)

For projects that want to pin the version in `package.json`:

```bash
npm install agent-browser
npx agent-browser install  # Download Chrome (first time)
```

Then use via `npx` or `package.json` scripts.

## Homebrew (macOS)

```bash
brew install agent-browser
agent-browser install  # Download Chrome (first time)
```

## Cargo (Rust)

```bash
cargo install agent-browser
agent-browser install  # Download Chrome (first time)
```

Compiles from source (~2-3 min). Requires Node.js 24+, pnpm 11+, and the Rust toolchain ([rustup.rs](https://rustup.rs)).

## From source

```bash
git clone https://github.com/vercel-labs/agent-browser
cd agent-browser
pnpm install
pnpm build
pnpm build:native
./bin/agent-browser install
pnpm link --global
```

## Linux dependencies

On Linux, install system dependencies:

```bash
agent-browser install --with-deps
```

This exits nonzero if the package manager cannot install every required browser library.

## Updating

Upgrade to the latest version:

```bash
agent-browser upgrade
```

Detects your installation method (npm, Homebrew, or Cargo) and runs the appropriate update command automatically. Displays the version change on success, or informs you if you are already on the latest version.

## Doctor

`doctor` diagnoses your install and auto-cleans stale daemon files. Run it whenever something stops working unexpectedly, or after upgrades:

```bash
agent-browser doctor                     # Full diagnosis
agent-browser doctor --offline --quick   # Local-only, fastest (~<1s)
agent-browser doctor --fix               # Also run destructive repairs
agent-browser doctor --webgpu            # Also run a live WebGPU render probe
agent-browser doctor --json              # Structured output
```

It checks:



| Category | What it checks |
| --- | --- |
| Environment | CLI version, platform, home directory, state and socket dirs, free disk space |
| Chrome | Chrome install path and version, cache dir, Puppeteer fallback, user-data dir and profile count, optional `lightpanda` engine |
| Daemons | Running daemons per session, stale `.sock` / `.pid` / `.version` / `.stream` files (auto-cleaned), version mismatch with the CLI, dashboard process liveness |
| Config | `~/.agent-browser/config.json`, `./agent-browser.json`, and any file at `AGENT_BROWSER_CONFIG` parse as valid JSON |
| Security | Encryption key env var or `~/.agent-browser/.encryption-key` (with 0600 permissions on unix), state file count and age vs `AGENT_BROWSER_STATE_EXPIRE_DAYS`, action policy file |
| Providers | Env vars for Browserless, Browserbase, Browser Use, Kernel, AgentCore (AWS creds), Appium (for `--provider ios`), and `AI_GATEWAY_API_KEY` for chat |
| Network | Reachability of the Chrome for Testing CDN, AI Gateway (if configured), and any currently selected provider endpoint (skipped under `--offline`) |
| Launch test | Spawns a scratch session, launches headless Chrome, navigates to `about:blank`, then closes. Measures wall time (skipped under `--quick`) |
| WebGPU probe | Opt-in via `--webgpu`: launches a scratch session with the [WebGPU preset](/webgpu), renders through a real WebGPU pass, and pixel-checks both an offscreen readback and a decoded screenshot. Add `--headed` to validate the headed/Xvfb capture path |



Stale sidecar files are always cleaned. Destructive actions are opt-in via `--fix`:



| Check | What `--fix` does |
| --- | --- |
| Chrome missing | Runs `agent-browser install` |
| Version-mismatched daemons | Sends `close` to each and cleans files |
| Old state files | Deletes state files older than `AGENT_BROWSER_STATE_EXPIRE_DAYS` (default 30) |
| Missing encryption key | Generates a new key at `~/.agent-browser/.encryption-key` (0600, unix); never overwrites an existing key |



Exit code is `0` if all checks pass (warnings are fine), `1` if any fail.

## Custom browser

Use a custom browser executable instead of bundled Chromium:

- **Serverless** - Use `@sparticuz/chromium` (~50MB vs ~684MB)
- **System browser** - Use existing Chrome installation
- **Custom builds** - Use modified browser builds

```bash
# Via flag
agent-browser --executable-path /path/to/chromium open example.com

# Via environment variable
AGENT_BROWSER_EXECUTABLE_PATH=/path/to/chromium agent-browser open example.com
```

### Serverless example

Use `@sparticuz/chromium` or similar to obtain a Chromium executable path, then pass it via `--executable-path` or `AGENT_BROWSER_EXECUTABLE_PATH`.

## AI agent setup

agent-browser works with any AI agent out of the box. For richer context:

### AI coding assistants (recommended)

Install the skill for your AI coding assistant:

```bash
npx skills add vercel-labs/agent-browser
```

This works with Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, Goose, OpenCode, and Windsurf. The skill is fetched from the repository and stays up to date automatically.

> **Do not** copy `SKILL.md` from `node_modules` -- it will become stale as new features are added. Always use `npx skills add` or reference the repository version.

### AGENTS.md / CLAUDE.md

Add to your instructions file:

```markdown
## Browser Automation

Use `agent-browser` for web automation. Run `agent-browser --help` for all commands.

Core workflow:
1. `agent-browser open ` - Navigate to page
2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2)
3. `agent-browser click @e1` / `fill @e2 "text"` - Interact using refs
4. Re-snapshot after page changes
```

---

## File: docs/src/app/ios/page.mdx

# iOS Simulator

Control real Mobile Safari in the iOS Simulator for authentic mobile web testing. Uses Appium with XCUITest for native automation.

## Requirements

- macOS with Xcode installed
- iOS Simulator runtimes (download via Xcode)
- Appium with XCUITest driver

## Setup

```bash
# Install Appium globally
npm install appium

# Install the XCUITest driver for iOS
appium driver install xcuitest
```

## List available devices

See all iOS simulators available on your system:

```bash
agent-browser device list

# Output:
# Available iOS Simulators:
#
#   ○ iPhone 16 Pro (iOS 18.0)
#     F21EEC0D-7618-419F-811B-33AF27A8B2FD
#   ○ iPhone 16 Pro Max (iOS 18.0)
#     50402807-C9B8-4D37-9F13-2E00E782C744
#   ○ iPad Pro 13-inch (M4) (iOS 18.0)
#     3A6C6436-B909-4593-866D-91D1062BB070
#   ...
```

## Basic usage

Use the `-p ios` flag to enable iOS mode. The workflow is identical to desktop:

```bash
# Launch Safari on iPhone 16 Pro
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com

# Get snapshot with refs (same as desktop)
agent-browser -p ios snapshot -i

# Interact using refs
agent-browser -p ios tap @e1
agent-browser -p ios fill @e2 "text"

# Take screenshot
agent-browser -p ios screenshot mobile.png

# Close session (shuts down simulator)
agent-browser -p ios close
```

## Mobile-specific commands

```bash
# Swipe gestures
agent-browser -p ios swipe up
agent-browser -p ios swipe down
agent-browser -p ios swipe left
agent-browser -p ios swipe right

# Swipe with distance (pixels)
agent-browser -p ios swipe up 500

# Tap (alias for click, semantically clearer for touch)
agent-browser -p ios tap @e1
```

## Environment variables

Configure iOS mode via environment variables:

```bash
export AGENT_BROWSER_PROVIDER=ios
export AGENT_BROWSER_IOS_DEVICE="iPhone 16 Pro"

# Now all commands use iOS
agent-browser open https://example.com
agent-browser snapshot -i
agent-browser tap @e1
```



| Variable | Description |
| --- | --- |
| `AGENT_BROWSER_PROVIDER` | Set to `ios` to enable iOS mode |
| `AGENT_BROWSER_IOS_DEVICE` | Device name (e.g., "iPhone 16 Pro") |
| `AGENT_BROWSER_IOS_UDID` | Device UDID (alternative to device name) |



## Supported devices

All iOS Simulators available in Xcode are supported, including:

- All iPhone models (iPhone 15, 16, 17, SE, etc.)
- All iPad models (iPad Pro, iPad Air, iPad mini, etc.)
- Multiple iOS versions (17.x, 18.x, etc.)

**Real devices** are also supported via USB connection (see below).

## Real device support

Appium can control Safari on real iOS devices connected via USB. This requires additional one-time setup.

### 1. Get your device UDID

```bash
# List connected devices
xcrun xctrace list devices

# Or via system profiler
system_profiler SPUSBDataType | grep -A 5 "iPhone\|iPad"
```

### 2. Sign WebDriverAgent (one-time)

WebDriverAgent needs to be signed with your Apple Developer certificate to run on real devices.

```bash
# Open the WebDriverAgent Xcode project
cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent
open WebDriverAgent.xcodeproj
```

In Xcode:

1. Select the `WebDriverAgentRunner` target
2. Go to Signing & Capabilities
3. Select your Team (requires Apple Developer account, free tier works)
4. Let Xcode manage signing automatically

### 3. Use with agent-browser

```bash
# Connect device via USB, then use the UDID
agent-browser -p ios --device "<DEVICE_UDID>" open https://example.com

# Or use the device name if unique
agent-browser -p ios --device "John's iPhone" open https://example.com
```

### Real device notes

- First run installs WebDriverAgent to the device (may require Trust prompt on device)
- Device must be unlocked and connected via USB
- Slightly slower initial connection than simulator
- Tests against real Safari performance and behavior
- On first install, go to Settings → General → VPN & Device Management to trust the developer certificate

## Performance notes

- **First launch:** Takes 30-60 seconds to boot the simulator and start Appium
- **Subsequent commands:** Fast (simulator stays running)
- **Close command:** Shuts down simulator and Appium server

## Differences from desktop



| Feature | Desktop | iOS |
| --- | --- | --- |
| Browser | Chrome, Lightpanda | Safari only |
| Tabs | Supported | Single tab only |
| PDF export | Supported | Not supported |
| Screencast | Supported | Not supported |
| Swipe gestures | Not native | Native support |



## Troubleshooting

### Appium not found

```bash
# Make sure Appium is installed globally
npm install appium
appium driver install xcuitest

# Verify installation
appium --version
```

### No simulators available

Open Xcode and download iOS Simulator runtimes from **Settings → Platforms**.

### Simulator won't boot

Try booting the simulator manually from Xcode or the Simulator app to ensure it works, then retry with agent-browser.

---

## File: docs/src/app/quick-start/page.mdx

# Quick Start

## Core workflow

Every browser automation follows this pattern:

```bash
# 1. Navigate
agent-browser open example.com

# 2. Snapshot to get element refs
agent-browser snapshot -i
# Output:
# @e1 [heading] "Example Domain"
# @e2 [link] "More information..."

# 3. Interact using refs
agent-browser click @e2

# 4. Re-snapshot after page changes
agent-browser snapshot -i
```

## Common commands

```bash
agent-browser open example.com
agent-browser snapshot -i                # Get interactive elements with refs
agent-browser click @e2                  # Click by ref
agent-browser fill @e3 "test@example.com" # Fill input by ref
agent-browser get text @e1               # Get text content
agent-browser screenshot                 # Save to temp directory
agent-browser screenshot page.png        # Save to specific path
agent-browser close
```

## Traditional selectors

CSS selectors and semantic locators also supported:

```bash
agent-browser click "#submit"
agent-browser fill "#email" "test@example.com"
agent-browser find role button click --name "Submit"
```

## Headed mode

Show browser window for debugging:

```bash
agent-browser open example.com --headed
```

## Wait for content

```bash
agent-browser wait @e1                   # Wait for element
agent-browser wait --load networkidle    # Wait for network idle
agent-browser wait --url "**/dashboard"  # Wait for URL pattern
agent-browser wait 2000                  # Wait milliseconds
```

## Command chaining

Chain commands with `&&` in a single shell call. The browser persists via a background daemon, so chaining is safe and efficient:

```bash
# Open, wait, and snapshot in one call
agent-browser open example.com && agent-browser wait --load networkidle && agent-browser snapshot -i

# Chain multiple interactions
agent-browser fill @e1 "user@example.com" && agent-browser fill @e2 "pass" && agent-browser click @e3

# Navigate and capture
agent-browser open example.com && agent-browser wait --load networkidle && agent-browser screenshot page.png
```

Use `&&` when you don't need intermediate output. Run commands separately when you need to parse output first (e.g., snapshot to discover refs before interacting).

## JSON output

For programmatic parsing in scripts:

```bash
agent-browser snapshot --json
agent-browser get text @e1 --json
```

Note: The default text output is more compact and preferred for AI agents.

---

## File: docs/src/app/selectors/page.mdx

# Selectors

## Refs (recommended)

Refs provide deterministic element selection from snapshots. Best for AI agents.

```bash
# 1. Get snapshot with refs
agent-browser snapshot
# Output:
# - heading "Example Domain" [ref=e1] [level=1]
# - button "Submit" [ref=e2]
# - textbox "Email" [ref=e3]
# - link "Learn more" [ref=e4]

# 2. Use refs to interact
agent-browser click @e2                   # Click the button
agent-browser fill @e3 "test@example.com" # Fill the textbox
agent-browser get text @e1                # Get heading text
agent-browser hover @e4                   # Hover the link
```

### Why refs?

- **Deterministic** - Ref points to exact element from snapshot
- **Fast** - No DOM re-query needed
- **AI-friendly** - LLMs can reliably parse and use refs

## CSS selectors

```bash
agent-browser click "#id"
agent-browser click ".class"
agent-browser click "div > button"
agent-browser click "[data-testid='submit']"
```

## Text & XPath

```bash
agent-browser click "text=Submit"
agent-browser click "xpath=//button[@type='submit']"
```

## Semantic locators

Find elements by role, label, or other semantic properties:

```bash
agent-browser find role button click --name "Submit"
agent-browser find role heading text --name "Skills"     # implicit roles work: <h2>=heading, =list, top-level <header>=banner
agent-browser find label "Email" fill "test@test.com"
agent-browser find placeholder "Search..." fill "query"
agent-browser find testid "submit-btn" click
```

---

## File: docs/src/app/sessions/page.mdx

# Sessions

Run multiple isolated browser instances:

```bash
# Different sessions
agent-browser --session agent1 open site-a.com
agent-browser --session agent2 open site-b.com

# Or via environment variable
AGENT_BROWSER_SESSION=agent1 agent-browser click "#btn"

# List active sessions
agent-browser session list
# Output:
# Active sessions:
# -> default
#    agent1

# Show current session
agent-browser session

# Generate a stable worktree-scoped session id
agent-browser session id --scope worktree --prefix next-dev-loop

# Inspect daemon, launch, and restore status
agent-browser session info --json
```

## Session isolation

Each session has its own:

- Browser instance
- Cookies and storage
- Navigation history
- Authentication state

## Tab pinning in a shared browser

Full isolation applies when each session launches its own browser. When sessions instead share one Chrome over `--cdp`, only the tab selection separates them. Each session remembers which tab it is bound to (by CDP target id, persisted across daemon restarts), so a restarted daemon reattaches to the session's own tab instead of adopting the most recently active one.

Add `--pin-tab` (or set `AGENT_BROWSER_PIN_TAB=1`) to make the binding strict:

```bash
agent-browser --session agent1 --cdp 9222 --pin-tab open https://site-a.com
agent-browser --session agent2 --cdp 9222 --pin-tab open https://site-b.com
```

With `--pin-tab`, a session whose bound tab was closed gets a `tab_gone` error instead of silently acting on another session's tab; run `tab new ` or pick a tab from `tab list` to recover. JSON output includes `"code": "tab_gone"`, `data.targetId`, and optional sanitized `data.lastUrl`. Batch output uses `result` for the same recovery object. The flag is sticky per session; pass `--no-pin-tab` to turn it off again. See [CDP Mode](/cdp-mode) for details.

## Chrome profile reuse

The simplest way to reuse your existing login state: pass a Chrome profile name to `--profile`. agent-browser copies the profile to a temp directory (read-only snapshot) and launches Chrome with your existing cookies and sessions.

```bash
# List available Chrome profiles
agent-browser profiles

# Reuse your default Chrome profile's login state
agent-browser --profile Default open https://gmail.com

# Use a named profile (by display name or directory name)
agent-browser --profile "Work" open https://app.example.com

# Or via environment variable
AGENT_BROWSER_PROFILE=Default agent-browser open https://gmail.com
```



| Detail | Description |
| --- | --- |
| Supported browsers | Chrome, Chrome Canary, Chromium, Brave |
| What's copied | Cookies, local storage, extensions state (cache dirs excluded for speed) |
| Original profile | Never modified (read-only snapshot) |
| Cleanup | Temp copy deleted when browser closes |
| Windows note | Close Chrome before using `--profile &lt;name&gt;` if Chrome is running |



## Persistent profiles

For a custom profile directory that persists state across browser restarts, pass a path to `--profile`:

```bash
# Use a persistent profile directory
agent-browser --profile ~/.myapp-profile open myapp.com

# Login once, then reuse the authenticated session
agent-browser --profile ~/.myapp-profile open myapp.com/dashboard

# Or via environment variable
AGENT_BROWSER_PROFILE=~/.myapp-profile agent-browser open myapp.com
```

The profile directory stores:

- Cookies and localStorage
- IndexedDB data
- Service workers
- Browser cache
- Login sessions

## Import auth from your browser

If you are already logged in to a site in Chrome, you can grab that auth state and reuse it in agent-browser. This is the fastest way to bypass login flows, OAuth, SSO, or 2FA.

**Step 1:** Start Chrome with remote debugging:

```bash
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222

# Linux
google-chrome --remote-debugging-port=9222
```

Log in to your target site(s) in this Chrome window.

`--remote-debugging-port` exposes full browser control on localhost. Any local process can connect. Only use on trusted machines and close Chrome when done.

**Step 2:** Connect and save the authenticated state:

```bash
agent-browser --auto-connect state save ./my-auth.json
```

**Step 3:** Use the saved auth in future sessions:

```bash
# Load auth at launch
agent-browser --state ./my-auth.json open https://app.example.com/dashboard

# Or load into an already-launched session
agent-browser open about:blank
agent-browser state load ./my-auth.json
agent-browser open https://app.example.com/dashboard
```

Combine with `--session  --restore` so the imported auth auto-persists across restarts:

```bash
SESSION="$(agent-browser session id --scope worktree --prefix myapp)"
agent-browser --session "$SESSION" --restore --state ./my-auth.json open https://app.example.com/dashboard
# From now on, state auto-saves/restores for this session
```

State files contain session tokens in plaintext. Add them to `.gitignore` and delete when no longer needed. For encryption at rest, see [State encryption](#state-encryption) below.

## Session persistence

Use `--restore` with a stable `--session` to automatically save and restore cookies and localStorage across browser restarts:

```bash
# Auto-save/load state for this worktree
SESSION="$(agent-browser session id --scope worktree --prefix twitter)"
agent-browser --session "$SESSION" --restore open twitter.com

# Login once, then state persists automatically
agent-browser --session "$SESSION" --restore click "#login"

# Optional validation prevents a bad restore from overwriting the previous good state
agent-browser --session "$SESSION" --restore --restore-check-text Dashboard open twitter.com
```

State files are stored in `~/.agent-browser/sessions/` and automatically loaded before navigation. With the default `--restore-save auto` policy, failed restore or failed validation skips auto-save.

When `--restore` or another restore key is configured, state is saved when the browser closes (explicit `close`, idle timeout, or daemon shutdown) and also periodically while the browser is open, so a browser window you close by hand still leaves a recent save behind. A session ID by itself only isolates the daemon and does not enable persistence; without a restore key, shutdown discards transient browser state and open tabs. Periodic autosave waits for commands to settle, then saves at most once per `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` (default 30000; set to `0` to save only on close). Idle sessions with configured persistence keep saving on the same interval, so changes the page makes on its own (token refreshes, background requests) are captured too. The daemon exits after one hour without commands or dashboard input by default; `--idle-timeout <time>` or `AGENT_BROWSER_IDLE_TIMEOUT_MS` tunes this, and `0` disables it. Headed, Safari/iOS WebDriver, and user-attached browsers are exempt from the default timeout; provider-owned cloud browsers are not. State saving respects the `--restore-save` policy.

### Restore key rules

Session and restore names must contain only alphanumeric characters, hyphens, and underscores. Use `agent-browser session id` to generate a valid key:

```bash
# Valid generated key
agent-browser session id --scope worktree --prefix my-project

# Invalid (will be rejected)
agent-browser --session "../bad" --restore open example.com    # path traversal
agent-browser --session "my session" --restore open example.com # spaces
agent-browser --session "foo/bar" --restore open example.com    # slashes
```

## State encryption

Encrypt saved state files (cookies, localStorage) using AES-256-GCM:

```bash
# Generate a 256-bit key (64 hex characters)
openssl rand -hex 32

# Set the encryption key
export AGENT_BROWSER_ENCRYPTION_KEY=<your-64-char-hex-key>

# State files are now encrypted automatically
agent-browser --session secure-session --restore open example.com

# List states shows encryption status
agent-browser state list
```

## State auto-expiration

Automatically delete old state files to prevent accumulation:

```bash
# Set expiration (default: 30 days)
export AGENT_BROWSER_STATE_EXPIRE_DAYS=7

# Manually clean old states
agent-browser state clean --older-than 7
```

## State management commands

```bash
# List all saved states
agent-browser state list

# Show state summary (cookies, origins, domains)
agent-browser state show my-session-default.json

# Rename a state file
agent-browser state rename old-name new-name

# Clear states for a specific session name
agent-browser state clear my-session

# Clear all saved states
agent-browser state clear --all

# Manual save/load (for custom paths)
agent-browser state save ./backup.json
agent-browser state load ./backup.json
```

## Authenticated sessions

Use `--headers` to set HTTP headers for a specific origin:

```bash
# Headers scoped to api.example.com only
agent-browser open api.example.com --headers '{"Authorization": "Bearer <token>"}'

# Requests to api.example.com include the auth header
agent-browser snapshot -i --json
agent-browser click @e2

# Navigate to another domain - headers NOT sent
agent-browser open other-site.com
```

Useful for:

- **Skipping login flows** - Authenticate via headers
- **Switching users** - Different auth tokens per session
- **API testing** - Access protected endpoints
- **Security** - Headers scoped to origin, not leaked

## Multiple origins

```bash
agent-browser open api.example.com --headers '{"Authorization": "Bearer token1"}'
agent-browser open api.acme.com --headers '{"Authorization": "Bearer token2"}'
```

## Global headers

For headers on all domains:

```bash
agent-browser set headers '{"X-Custom-Header": "value"}'
```

## Environment variables



| Variable | Description |
| --- | --- |
| `AGENT_BROWSER_SESSION` | Browser session ID (default: "default") |
| `AGENT_BROWSER_NAMESPACE` | Namespace for daemon sockets and restore-state directories |
| `AGENT_BROWSER_PIN_TAB` | Pin the session to its bound tab (strict tab binding) |
| `AGENT_BROWSER_RESTORE` | Auto-save/load state persistence key |
| `AGENT_BROWSER_RESTORE_SAVE` | Restore save policy: `auto`, `always`, or `never` |
| `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` | Minimum ms between periodic session autosaves (default: 30000, `0` disables) |
| `AGENT_BROWSER_RESTORE_CHECK_URL` | URL pattern restored state must match |
| `AGENT_BROWSER_RESTORE_CHECK_TEXT` | Page text restored state must contain |
| `AGENT_BROWSER_RESTORE_CHECK_FN` | JavaScript expression restored state must satisfy |
| `AGENT_BROWSER_SESSION_NAME` | Legacy auto-save/load state persistence name |
| `AGENT_BROWSER_ENCRYPTION_KEY` | 64-char hex key for AES-256-GCM encryption |
| `AGENT_BROWSER_STATE_EXPIRE_DAYS` | Auto-delete states older than N days (default: 30) |

---

## File: docs/src/app/snapshots/page.mdx

# Snapshots

The `snapshot` command returns a compact accessibility tree with refs for element interaction.

## Options

Filter output to reduce size:

```bash
agent-browser snapshot                    # Full accessibility tree
agent-browser snapshot -i                 # Interactive elements only (recommended)
agent-browser snapshot -c                 # Compact (remove empty elements)
agent-browser snapshot -d 3               # Limit depth to 3 levels
agent-browser snapshot -s "#main"         # Scope to CSS selector
agent-browser snapshot -i -c -d 5         # Combine options
```



| Option | Description |
| --- | --- |
| `-i, --interactive` | Only interactive elements (buttons, links, inputs) |
| `-u, --urls` | Include href URLs for link elements |
| `-c, --compact` | Remove empty structural elements |
| `-d, --depth` | Limit tree depth |
| `-s, --selector` | Scope to CSS selector |



## Output format

The default text output is compact and AI-friendly:

```bash
agent-browser snapshot -i
# Output:
# @e1 [heading] "Example Domain" [level=1]
# @e2 [button] "Submit"
# @e3 [input type="email"] placeholder="Email"
# @e4 [link] "Learn more"
```

## Using refs

Refs from the snapshot map directly to commands:

```bash
agent-browser click @e2      # Click the Submit button
agent-browser fill @e3 "a@b.com"  # Fill the email input
agent-browser get text @e1        # Get heading text
```

## Ref lifecycle

Refs are invalidated when the page changes. Always re-snapshot after navigation or DOM updates:

```bash
agent-browser click @e4      # Navigates to new page
agent-browser snapshot -i    # Get fresh refs
agent-browser click @e1      # Use new refs
```

## Annotated screenshots

For visual context alongside text snapshots, use `screenshot --annotate` to overlay numbered labels on interactive elements. Each label `[N]` maps to ref `@eN`:

In native mode, annotated screenshots currently work on the CDP-backed browser path (Chromium/Lightpanda). The Safari/WebDriver backend does not yet support `--annotate`.

```bash
agent-browser screenshot --annotate ./page.png
# -> Screenshot saved to ./page.png
#    [1] @e1 button "Submit"
#    [2] @e2 link "Home"
#    [3] @e3 textbox "Email"
agent-browser click @e2
```

Headless Chromium screenshots hide native scrollbars for consistent image output. Pass `--hide-scrollbars false` when launching to keep native scrollbars visible.

Annotated screenshots also cache refs, so you can interact with elements immediately. This is useful when the text snapshot is insufficient for unlabeled icons, canvas content, or visual layout verification.

## Iframes

Snapshots automatically detect and inline iframe content. Each `Iframe` node in the main frame is resolved and its child accessibility tree is included directly beneath it. Refs assigned to elements inside iframes carry frame context, so interactions work without switching frames first.

```bash
agent-browser snapshot -i
# @e1 [heading] "Checkout"
# @e2 [Iframe] "payment-frame"
#   @e3 [input] "Card number"
#   @e4 [button] "Pay"

agent-browser fill @e3 "4111111111111111"
agent-browser click @e4
```

Only one level of iframe nesting is expanded. Cross-origin iframes that block accessibility tree access and empty iframes are silently omitted.

To scope a snapshot to a single iframe, switch into it first:

```bash
agent-browser frame @e2
agent-browser snapshot -i       # Only elements inside that iframe
agent-browser frame main        # Return to main frame
```

## Best practices

1. Use `-i` to reduce output to actionable elements
2. Re-snapshot after page changes to get updated refs
3. Scope with `-s` for specific page sections
4. Use `-d` to limit depth on complex pages
5. Use `screenshot --annotate` when visual context is needed alongside refs

## JSON output

For programmatic parsing in scripts:

```bash
agent-browser snapshot --json
# {"success":true,"data":{"snapshot":"...","refs":{"e1":{"role":"heading","name":"Title"},...}}}
```

Note: JSON uses more tokens than text output. The default text format is preferred for AI agents.