Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1a9fbb4
feat(cua-driver): add semantic browser snapshots
Jul 17, 2026
2ccb87c
fix(cua-driver): harden existing browser attachment
Jul 17, 2026
f378009
test(cua-driver): harden inactive browser tabs
Jul 17, 2026
3348fc5
fix(cua-driver): stabilize background browser tabs
Jul 17, 2026
a7b7413
fix(cua-driver): deliver background browser keystrokes
Jul 17, 2026
00f33c4
fix(cua-driver): deliver trusted background browser clicks
Jul 17, 2026
a19e909
fix(cua-driver): wait for background tab input readiness
Jul 17, 2026
e6643f2
fix(cua-driver): harden macOS browser profile setup
Jul 17, 2026
fa13890
feat(cua-driver): add browser telemetry contract
Jul 17, 2026
34ae4c1
Merge remote-tracking branch 'origin/main' into codex/browser-complet…
Jul 18, 2026
6611622
fix(cua-driver): report unknown browser tab selection
Jul 18, 2026
21ca689
feat(cua-driver): add browser dialogs and uploads
Jul 18, 2026
b4007a8
feat(cua-driver): complete browser mutations
Jul 18, 2026
fe4d526
test(cua-driver): extend standalone browser matrix
Jul 18, 2026
b1ef600
docs(cua-driver): document browser mutation coverage
Jul 18, 2026
6ea7806
fix(cua-driver): preserve cross-platform browser posture
Jul 18, 2026
446c56e
test(cua-driver): serialize telemetry environment assertions
Jul 18, 2026
af2488a
Merge remote-tracking branch 'origin/main' into codex/browser-complet…
Jul 18, 2026
00f5905
docs(cua-driver): preserve source contribution credit
Jul 18, 2026
31b4f85
fix(cua-driver): model Linux dialog delivery
Jul 18, 2026
6fe95ec
Merge remote-tracking branch 'origin/main' into codex/browser-complet…
Jul 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ Agents still begin with `list_apps` and `list_windows`. They select a concrete
mints opaque target and tab capabilities. Raw DevTools target identifiers are
not part of the public contract.

Selected-tab state is deliberately tri-state. `active: true` or `false` is
reported only when the native window title uniquely identifies one DevTools
tab. Duplicate titles, empty titles, and other ambiguity produce `active:
null` for every candidate instead of treating DevTools list order as native
selection evidence. A caller can still target an explicit returned tab id.

This preserves one targeting model across native and web content. It also
prevents a browser helper from choosing the first tab, first window, or first
process match when several candidates exist.
Expand Down Expand Up @@ -54,13 +60,24 @@ default click uses `Input.dispatchMouseEvent`, but Chromium's standalone window
is known to activate on that route on macOS and Linux. Cua Driver returns
`browser_input_trust_unavailable` there before dispatch. Standalone Chrome and
Edge on Windows, and the bounded embedded Electron route, have passing trusted
background evidence.
background evidence. The same trust distinction applies to hover,
right-click, double-click, scroll, and drag through `browser_pointer`.

A caller must explicitly request `input_route: "dom_event"` to invoke an
element's DOM click behavior. That route is synthetic even when it preserves
full-background posture. The driver does not silently change trust models to
make a call appear successful.

Page-owned JavaScript dialogs are modeled as short-lived capabilities rather
than native-window guesses. Inspection returns the kind and an opaque dialog
generation; accept or dismiss succeeds only while that exact dialog remains
current. The page's initial creation of a Chromium native modal may activate
the browser; after occlusion is re-established, inspection and resolution do
not require another activation. File assignment bypasses native pickers through an exact live file
input ref. Downloads additionally cross an open-world filesystem boundary, so
they require host approval, a canonical destination directory, exact event
correlation, and path-free output.

## Capabilities have a lifetime

Target ids, tab ids, and page refs belong to a named driver session. Page refs
Expand Down
9 changes: 9 additions & 0 deletions docs/content/docs/concepts/how-cua-driver-is-validated.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ keys, scrolling, dragging, child windows, controls, and editor behavior. This
makes a failure reproducible without depending on the changing state of an
installed third-party application.

The optional standalone-browser lane launches installed Chrome, Edge, or
Chromium with a fresh repo-owned profile and one exact source-built driver. Its
rows cover binding and ambiguous active-tab evidence, semantic snapshots,
frames, inactive tabs, JavaScript dialogs, file assignment, extended pointer
actions, and approval-scoped downloads. The target stays fully occluded behind
the foreground sentinel while the fixture journal proves page state. This lane
is the release evidence for typed browser behavior; CDP acknowledgements alone
do not count as delivery.

## One catalog describes each behavior cell

The Rust catalog records the dimensions that affect delivery:
Expand Down
65 changes: 62 additions & 3 deletions docs/content/docs/how-to-guides/driver/drive-a-web-page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,13 @@ get_browser_state({
})
```

Keep the returned opaque `target_id` and the `tab_id` whose `active` field is
`true`. Mutation is available only when `binding_quality` is `exact`. A
heuristic or ambiguous match is refused.
Keep the returned opaque `target_id`. Use the `tab_id` whose `active` field is
`true` only when selection is uniquely proven. `active: false` is proven
unselected; `active: null` means the native window cannot distinguish tabs,
such as two tabs with the same title. In that case, select an explicit tab by
its returned metadata and never infer selection from list order. Mutation is
available only when `binding_quality` is `exact`. A heuristic or ambiguous
window match is refused.

## Snapshot the tab

Expand Down Expand Up @@ -249,6 +253,61 @@ browser_type({
The default `insert_text` mode is efficient for ordinary text. Use
`mode: "keystrokes"` when the page depends on per-character keyboard events.

## Use extended pointer actions

`browser_pointer` supports `hover`, `right_click`, `double_click`, `scroll`,
and `drag`. Use the trusted route when genuine browser input semantics are
required and the platform reports that it can preserve posture. Use the
explicit synthetic route with current refs when DOM event semantics are
acceptable:

```jsonc
browser_pointer({
"target_id": "<target_id>",
"tab_id": "<tab_id>",
"ref": "p3:4",
"action": "scroll",
"input_route": "dom_event",
"delta_y": 240,
"session": "research-1"
})
```

For drag, pass `destination_ref` from the same exact frame. The driver refuses
mixed-frame or stale destinations instead of translating them approximately.

## Handle a page-owned dialog

Prime and inspect the exact tab with `browser_dialog({action:"inspect"})`.
When `present` is true, pass the returned `dialog_id` to `accept` or `dismiss`.
Only an accepted prompt may include `prompt_text`. This tool does not handle
browser permission prompts, extension UI, native sheets, or file pickers.
Creating a Chromium native modal can activate its browser window; once you
restore the intended occlusion, inspecting and resolving that exact dialog do
not activate it again on Windows and macOS. Resolution defaults to
`delivery_mode:"background"`. Linux Chromium cannot resolve the native modal
while preserving background posture, so the driver returns
`browser_input_trust_unavailable` before dispatch. Retry with
`delivery_mode:"foreground"` only when foreground activation is acceptable.

## Assign files without a picker

Take a semantic snapshot and choose a file-input ref whose `actions` contains
`upload`. Call `browser_set_input_files` with absolute paths to direct regular
files. Symlinks, directories, missing paths, and more than 32 files are
refused. Verify the page's uploaded-file state afterward; the tool response
contains only a count and never echoes the local paths.

## Download into an approved directory

Choose a current ref that activates the download and call `browser_download`
through an MCP host that presents its destructive-tool approval. Pass an
existing canonical absolute `destination_root`. The tool temporarily scopes
Chromium's browser-wide download behavior, correlates the exact frame and
opaque download id, restores the default on every outcome, and returns only
the final byte count and opaque id. It does not return a filename, URL, or
local path. A direct raw call without host approval is refused.

## Navigate

```jsonc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ Entries in `refs` declare their supported action kinds:
}
```

`browser_click` and `browser_type` reject a semantic ref when the requested
action is absent from `actions`, returning `browser_action_unavailable` before
delivery. Legacy `dom_refs_v1` refs retain their existing behavior.
The closed action vocabulary is `click`, `type`, and `upload`.
`browser_click`, `browser_type`, `browser_set_input_files`, and
`browser_download` reject a semantic ref when the required action is absent,
returning `browser_action_unavailable` before delivery. A file input declares
`upload`, not `type`. Legacy `dom_refs_v1` refs retain their existing behavior.

## Content refs

Expand Down
80 changes: 77 additions & 3 deletions docs/content/docs/reference/cua-driver/mcp-tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description: Reference for every MCP tool Cua Driver exposes

import { Callout } from 'fumadocs-ui/components/callout';

`cua-driver` exposes 43 MCP tools through a single stdio server (`cua-driver mcp`). Every tool is also callable from the shell as `cua-driver <name> '<JSON-args>'`.
`cua-driver` exposes 47 MCP tools through a single stdio server (`cua-driver mcp`). Every tool is also callable from the shell as `cua-driver <name> '<JSON-args>'`.

Tool names are `snake_case`. Responses are MCP `CallTool.Result` envelopes: a text content block prefixed with a `✅` summary (or the error reason on failure), plus optional image or structured-content blocks on tools that produce them. See the [CLI reference](/reference/cua-driver/cli-reference) for CLI-specific options like `--socket` and `--screenshot-out-file`.

Expand Down Expand Up @@ -425,7 +425,7 @@ Mapping: by='page' → larger step; by='line' → smaller step; amount = number
- `y` (number, optional): Window-local screenshot Y. See `x`.

```json
{"direction":"down"}
{"direction":"up"}
```

### `move_cursor`
Expand Down Expand Up @@ -493,7 +493,7 @@ Actions:
- `window_id` (integer, optional): Target window ID from list_windows.

```json
{"action":"get_text"}
{"action":"execute_javascript"}
```

## Recording tools
Expand Down Expand Up @@ -827,3 +827,77 @@ Type text into an exactly-bound tab via the Input domain. mode="insert_text" (de
```json
{"ref":"example","tab_id":"example","target_id":"example","text":"hello"}
```

### `browser_dialog`

Inspect or resolve a page-owned JavaScript alert, confirm, prompt, or beforeunload dialog on one exactly-bound tab. This never handles browser permission UI, extension UI, native dialogs, or file pickers. Inspect returns an opaque dialog_id; accept/dismiss require that exact current id. Resolution defaults to background delivery; Linux callers must explicitly request foreground delivery because Chromium's native modal cannot be resolved there without changing foreground posture.

**Arguments:**

- `action` (string, required)
- `delivery_mode` (string, optional): Requested foreground posture for accept/dismiss. Linux Chromium requires foreground; inspect is read-only.
- `dialog_id` (string, optional): Opaque current dialog generation returned by action=inspect.
- `prompt_text` (string, optional): Sensitive response text, valid only when accepting a prompt dialog.
- `session` (string, optional): Stable caller-declared session id. Browser targets, tabs, and refs are scoped to this session.
- `tab_id` (string, required): Opaque tab id from get_browser_state (session-scoped).
- `target_id` (string, required): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).

```json
{"action":"inspect","tab_id":"example","target_id":"example"}
```

### `browser_set_input_files`

Assign one or more explicit absolute local files to an exact live &lt;input type=file&gt; ref through CDP. This bypasses native file pickers, rejects symlinks and non-regular files, and never returns local paths.

**Arguments:**

- `files` (array of string, required)
- `ref` (string, required): Page element ref in the p&lt;snapshot&gt;:&lt;index&gt; namespace from get_browser_state. Refs are invalidated by navigation and by newer snapshots of the same tab.
- `session` (string, optional): Stable caller-declared session id. Browser targets, tabs, and refs are scoped to this session.
- `tab_id` (string, required): Opaque tab id from get_browser_state (session-scoped).
- `target_id` (string, required): Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id).

```json
{"files":["example"],"ref":"example","tab_id":"example","target_id":"example"}
```

### `browser_download`

Trigger one download through an exact live browser ref and save it inside an explicitly approved directory. Requires MCP-host destructive-tool approval, refuses ambiguous or stale capabilities, and never returns the source URL, filename, or destination path.

**Arguments:**

- `destination_root` (string, required): Absolute, existing, canonical directory approved to receive the download.
- `ref` (string, required): Live page ref whose activation initiates the download.
- `session` (string, required): Explicit caller session owning the browser capabilities.
- `tab_id` (string, required): Opaque exact tab id from get_browser_state.
- `target_id` (string, required): Opaque exact browser target id from get_browser_state.

```json
{"destination_root":"example","ref":"example","session":"example","tab_id":"example","target_id":"example"}
```

### `browser_pointer`

Perform hover, right-click, double-click, scroll, or drag in an exactly-bound browser tab. The trusted route uses CDP Input events and refuses if standalone background posture cannot be preserved. The explicit dom_event route requires a page ref and synthesizes full-background DOM events. Never activates or brings a tab to the foreground.

**Arguments:**

- `action` (string, required)
- `delta_x` (number, optional): Horizontal scroll delta in CSS pixels.
- `delta_y` (number, optional): Vertical scroll delta in CSS pixels.
- `destination_ref` (string, optional): Drag destination page ref in the exact same frame.
- `input_route` (string, optional)
- `ref` (string, optional): Origin page ref. Alternative to x/y.
- `session` (string, required): Explicit caller session owning the browser capabilities.
- `tab_id` (string, required): Opaque tab id minted by get_browser_state.
- `target_id` (string, required): Opaque target id minted by get_browser_state.
- `to_x` (number, optional): Drag destination viewport x in CSS pixels.
- `to_y` (number, optional): Drag destination viewport y in CSS pixels.
- `x` (number, optional): Origin viewport x in CSS pixels.
- `y` (number, optional): Origin viewport y in CSS pixels.

```json
{"action":"hover","session":"example","tab_id":"example","target_id":"example"}
```
6 changes: 3 additions & 3 deletions docs/content/docs/reference/cua-driver/platform-support.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ harnesses; an unlisted trust class or host shape is not implied.

| Surface | Proven page routes | Explicit limit |
| --- | --- | --- |
| Standalone Chrome and Edge on Windows | Snapshot, navigation, ref-bound typing, trusted background click, explicit DOM click, frames, multi-tab, and exact ambiguity refusal | Elevated-integrity and unsupported native-host relationships still refuse |
| Standalone Chrome on macOS | Snapshot, navigation, ref-bound typing, explicit DOM click, frames, multi-tab, and exact ambiguity refusal | Trusted CDP pointer input returns `browser_input_trust_unavailable` before dispatch |
| Standalone Chrome on Linux X11 | Snapshot, navigation, ref-bound typing, explicit DOM click, frames, multi-tab, and exact ambiguity refusal | Trusted CDP pointer input returns `browser_input_trust_unavailable` before dispatch |
| Standalone Chrome and Edge on Windows | Snapshot, navigation, ref-bound typing, trusted background click, explicit DOM click and pointer actions, JavaScript dialogs, file assignment, approved downloads, frames, multi-tab, and exact ambiguity refusal | Elevated-integrity and unsupported native-host relationships still refuse |
| Standalone Chrome on macOS | Snapshot, navigation, ref-bound typing, explicit DOM click and pointer actions, JavaScript dialogs, file assignment, approved downloads, frames, multi-tab, and exact ambiguity refusal | Trusted CDP pointer input returns `browser_input_trust_unavailable` before dispatch |
| Standalone Chrome and Edge on Linux X11 | Snapshot, navigation, ref-bound typing, explicit DOM click and pointer actions, foreground JavaScript-dialog resolution, file assignment, approved downloads, frames, multi-tab, and exact ambiguity refusal | Trusted CDP pointer input and background JavaScript-dialog resolution return `browser_input_trust_unavailable` before dispatch |
| Electron on Windows, macOS, X11, and validated Sway | Typed mutation while one proven native window maps to one CDP page | A second page or native window invalidates the bounded exact route |
| Tauri, WKWebView, WebKitGTK, and common split-process WebView2 | Browser identity plus a side-effect-free structured refusal | No exact native-host-to-engine relationship is currently proven |
| Safari and Firefox | Native discovery and native AX/PX fallbacks | No typed browser mutation engine is currently advertised |
Expand Down
6 changes: 5 additions & 1 deletion docs/content/docs/reference/cua-driver/telemetry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ The start event is emitted for a successful `start_session` declaration or the f

The end event is emitted for `end_session` and idle eviction. It contains aggregate counters and booleans. When the platform has a cursor entry for that session, it also includes bounded cursor categories: enabled state, built-in/default/custom icon class, automatic/custom color source, label presence, motion customization, and an active-cursor count bucket. `cursor_outcome_observed=false` distinguishes sessions with no readable cursor entry from a disabled or default cursor. Cua Driver uses the caller session string only as a process-local map key. The string, a hash of it, and a replacement join token are absent from telemetry payloads.

Computer-action success covers fixed pointer and keyboard capabilities, app launch and kill, window activation, state-changing `page` operations, and successful `browser_navigate`, `browser_click`, and `browser_type` calls. A structured browser refusal does not count as a completed computer action. `get_browser_state` is a read, and `browser_prepare` is tracked as browser use rather than a computer action because preparation may either reuse an endpoint or produce an approved visible side effect.
Computer-action success covers fixed pointer and keyboard capabilities, app launch and kill, window activation, state-changing `page` operations, and successful browser navigation, click, type, pointer, file-assignment, download, or dialog-resolution calls. A structured browser refusal does not count as a completed computer action. `get_browser_state` and `browser_dialog` inspection are reads for this classifier, and `browser_prepare` is tracked as browser use rather than a computer action because preparation may either reuse an endpoint or produce an approved visible side effect.

The end event includes `used_browser` and a bounded `browser_refusal_count_bucket`. It does not include browser targets, pages, profiles, or per-site information.

Expand All @@ -141,6 +141,10 @@ Typed browser tools use these reviewed operation values:
| `browser_prepare` | `browser_prepare_isolated`, `browser_prepare_existing_profile`, or `other` |
| `browser_click` | `browser_click_trusted`, `browser_click_dom_event`, or `other` |
| `browser_type` | `browser_type_insert_text`, `browser_type_keystrokes`, or `other` |
| `browser_dialog` | `browser_dialog_inspect`, `browser_dialog_accept`, `browser_dialog_dismiss`, or `other` |
| `browser_set_input_files` | `browser_set_input_files` |
| `browser_download` | `browser_download` |
| `browser_pointer` | Action-and-route values such as `browser_pointer_scroll_trusted` or `browser_pointer_drag_dom_event`; invalid combinations become `other` |
| `browser_navigate` | `not_applicable`; the tool name already identifies the operation |

### Structured browser refusals
Expand Down
Loading
Loading