<!-- Markdown mirror. Canonical: https://empryo.com/docs/tools/browser -->

# The browser

> One browser tool on every surface: an embedded Chromium with audits on desktop, a headless webview in the TUI and in scripts. Local addresses by default.

## Overview

The agent gets a real browser through ONE tool: `browser(action, ...params)` - a single flat schema keyed by `action`, not two dozen separate tools. It exists on every surface:

| Surface | Engine | What it can do |
|---------|--------|----------------|
| Desktop app | Embedded, tabbed Chromium (visible panel) | Everything: drive, screenshot, audits, throttling, network recording, dev-server runner |
| TUI + headless | Headless system webview | Drive, snapshot, screenshot, inspect, eval, tabs - no visible panel, no DevTools profiling |

The same prompt works on both: snapshot refs, click/type/key (chords included), scroll, resize presets, wait, highlight, `clear_storage`.

## Local-dev only

`open`/`navigate` accept localhost and private-network addresses only. Public internet URLs are blocked by default - the browser is for the app you are building, not for web browsing. Opt out per project with `terminalBrowserAllowExternal: true` (terminal) or the desktop's network-policy setting.

![A screenshot costs ~1 500 tokens per attempt and the agent still guesses a selector. A snapshot returns every element with a stable ref for ~180, and the click lands first time.](https://empryo.com/media/docs-browser-snapshot.svg)

*A screenshot costs ~1 500 tokens per attempt and the agent still guesses a selector. A snapshot returns every element with a stable ref for ~180, and the click lands first time.*

## Snapshot first

`browser(action:'snapshot')` returns every visible interactive element as a short text tree with a stable `ref` per node:

```
  button "Save changes" ref=e7
* textbox "Email" ref=e3 [value=""]
  [scrollable "Results" y=0/1840 ref=e12]
```

The agent then acts on the ref - `browser(action:'click', ref:'e7')` - instead of guessing CSS selectors from source. A `*` marks elements new since the last snapshot, which is how the agent confirms what a click actually did without paying for a screenshot.

## Desktop extras

The desktop's Chromium adds the DevTools tier:

- `audit` - Core Web Vitals, page weight, a11y and SEO checks, horizontal-overflow detection
- `throttle` - slow-3g/4g/offline network emulation plus CPU slowdown
- `network_start` / `network_stop` - record every request (status, size, timing, failures)
- `run_server` / `stop_server` / `list_servers` - start dev servers from `.empryo/launch.json` and open them in a tab
- A visible, tabbed panel you share with the agent - tab leases keep two agents from typing into one page (see [tabs](/docs/agents/cross-tab))

## Terminal browser

On the TUI and in `--headless` runs the same tool drives a browser with no window: WKWebView on macOS, WebView2 on Windows, and Chrome or Chromium on Linux. On Linux, install one or point `BUN_CHROME_PATH` at it. Without a Chrome, the tool is not offered. Screenshots come back to the model as images, which is how it sees a page with no panel.

Turn it off entirely with `"terminalBrowser": false` in config.

## Config

| Key | Default | Effect |
|-----|---------|--------|
| `terminalBrowser` | on where available | The browser tool on TUI + headless |
| `terminalBrowserAllowExternal` | `false` | Allow public-internet URLs in the terminal browser |

## Not the same as computer use

This tool drives a page. [Computer use](/docs/tools/computer-use) drives UI the app
does not own, your own Chrome, an app window, the whole display, through an
accessibility tree, leases and capability switches. Same repo, different reach.
