---
title: My Real Chrome v2 (publishable — scripting + native messaging, no debugger)
---

# My Real Chrome v2 (publishable — scripting + native messaging, no debugger)

## What it is

**"My Real Chrome v2"** is the user-facing name; the code namespace is `real-chrome-bridge-v2`.

Status: **in development** — hidden by default, **desktop-only**. Reveal via **Settings → Features → "My Real Chrome v2"** or `AMC_SHOW_REAL_CHROME_BRIDGE_V2=1`. Ships OFF; nothing runs until you enable it, load the companion extension, and grant per-site access.

A **clean-room, Chrome-Web-Store-approvable** rebuild of [My Real Chrome](real-chrome-bridge.md) that drives your real, signed-in Chrome **without the `debugger` permission**. It uses `chrome.scripting` (inject a driver into the page), `chrome.tabs.captureVisibleTab` (screenshots), and **native messaging** (transport) — the same architecture as Anthropic's published "Claude" Chrome extension. It is fully **separate** from v1; both can be installed at once.

## Where to find it

Nothing appears until you turn it on. The feature is revealed by flipping **Settings → Features → "My Real Chrome v2"** (it ships hidden and off). From then on the surface lives in Chrome rather than in Omniscio: you load the companion `real-chrome-extension-v2/` folder as an unpacked extension, and per-site access is granted by clicking the extension icon and pressing **Allow this site** (or **Allow all sites**). Driving it is an agent's job, over the local CLI control server — there is no bridge screen inside the app.

## How it behaves

### Why it exists (vs. v1)

- **Publishable.** The Web Store rarely approves consumer extensions that use `debugger` to drive the browser;
  v1 therefore ships load-it-yourself only. v2's permission set (`scripting`, `tabs`, `webNavigation`,
  `storage`, `alarms`, `nativeMessaging` + optional per-site host access) is the approvable shape.
- **Not poisoned by other extensions.** v1's debugger session is poisoned (CDP -32000) the instant a page
  contains another extension's frame (LastPass, Superhuman). v2 uses no debugger, so it has no such wall.

### What it is NOT

- **Not your existing tabs — by default.** The extension drives ONLY tabs it opened (the fresh-tab guardrail).
  The default-off **"drive any tab"** opt-in is the one deliberate exception.
- **Not the tabs you open next to it.** The default-off popup setting follows only a popup that a PAGE in a
  driven tab opened, such as a "Sign in with Google" window. A tab you open yourself — Ctrl+T, the **+**
  button, an address typed with Alt+Enter, Duplicate — is never taken over, even while a driven tab is the
  one in front.
- **Not on the network.** Transport is Chrome's native messaging to an app-registered host, relayed to the app
  over a private local pipe — **no TCP port at all**.
- **Not reachable from your phone.** Desktop-only: gated CLI routes, and the status channel is on the mobile/web
  bridge blocklist.

### Setup

1. **Enable** — Settings → Features → "My Real Chrome v2" (or `AMC_SHOW_REAL_CHROME_BRIDGE_V2=1`). The app
   registers the native-messaging host with Chrome (an HKCU registry entry) automatically.
2. **Load the extension** — `real-chrome-extension-v2/` as an unpacked extension (chrome://extensions →
   Developer mode → Load unpacked). There is **no token to paste** — it connects automatically.
3. **Grant access** — click the extension icon and press **Allow this site** (or **Allow all sites**). Chrome
   requires this click, so an agent can never silently gain access to a new site. Revoke any time in Chrome.

### Known limits (scripting can't match a debugger)

These are documented, expected limits — not bugs — of the no-debugger architecture:

- **Trusted input:** synthetic events (`isTrusted:false`); a few sites that gate on genuine user events won't respond.
- **File uploads:** not supported (no debugger file-set); a file picker needs the user.
- **Screenshots:** visible area of the **active** tab only. A background tab whose window is NOT the one in
  front is briefly activated then restored (nobody sees it); a background tab in the window the user IS
  looking at is **refused** rather than shown, because the switch would take over their screen — see
  _Background-first_ below.
- **Read-page:** a **DOM-derived** accessibility approximation, not the browser's real accessibility tree.
- **Not captured/controlled:** native JS dialogs, console messages, network response bodies, device/network emulation.
  `evaluate` runs page JS but is subject to the page's CSP.

### Background-first — the bridge never takes the user's screen on its own

Driving Chrome is invisible unless you ASK to be seen. This is deliberate: the user was clear that the
bridge must "work in the background unless the agent specifically pulls the screen to the front."

- **Opening a tab** puts it `active:false` in the user's own last-focused window. It never mints a focused
  window, and never switches them off what they are reading.
- **Reading and acting** (`read-page`, `get-page-text`, `click`, `type`, `evaluate`, `navigate` on your own
  tab) change nothing on screen and are always safe.
- **`screenshot` is the one read that could move the screen** — `captureVisibleTab` photographs a window's
  VISIBLE tab, so shooting a background tab means bringing it forward first. It only does that when the
  switch is invisible (the tab is already on screen, or its window is behind something). Otherwise it
  **refuses** with a message naming your two options: `get-page-text` to read it without touching the
  screen, or `foreground` to show it on purpose.
- **`foreground` is the deliberate screen-grab**, and the only op that raises the Chrome window. Reach for
  it when a HUMAN needs to see the tab — a login hand-off, a "look at this" moment. Never call it as
  preparation for a mouse op: clicks and typing work on background tabs and need no foregrounding.

For a login flow, use the same **drive → hand off → resume** pattern as v1: drive to the sign-in, foreground the
tab for the user to finish, then resume on the now-authenticated tab. `foreground` activates the tab **and raises
the Chrome window itself** (TOPMOST → NOTOPMOST, restoring it first if minimized) — the extension's
`chrome.windows.update{focused}` alone cannot, because Windows silently vetoes Chrome's `SetForegroundWindow`
when Chrome is not already the foreground process. So the hand-off works even when Chrome is behind another
window or minimized; never tell the user to un-cover or un-minimize it by hand.

## For agents

### How an agent uses it (CLI routes)

While enabled AND connected, an agent drives it through the local CLI control server. Every route is
feature-gated (404 when off), full-trust-token-authenticated, and fast-fails (409) when no extension is
connected. The `/real-chrome-v2/*` surface (the core drive vocabulary):

**Perceive:** `GET /real-chrome-v2/status` · `GET /real-chrome-v2/tabs` · `POST /real-chrome-v2/read-page`
(DOM-derived a11y snapshot with `[ref=eN]` markers) · `get-page-text` · `screenshot`
**Navigate:** `navigate` · `reload` · `navigate-back` · `foreground` · `wait-for`
**Interact:** `click` · `type` · `hover` · `press-key` (presses the focused element; pass a `ref` to focus it first) · `select-option` · `fill-form` · `evaluate`
**Control:** `open-tab` · `close-tab` · `switch-tab` · `attach-tab` / `detach-tab` (drive-any-tab opt-in)

**When a command is slow or the link drops.** A command whose page is stuck fails on its own after
its deadline, with a "timed out" error; the link and every other command stay up, and it is not
retried for you. After a timeout the app asks the extension for its tab list — which touches no
page — and resets the link only if that goes unanswered too. After a real drop, a call waits up to
five seconds for the extension to re-dial before answering "no browser connected", and
`GET /real-chrome-v2/status` records when and why the link last dropped (`lastDisconnectedAt`,
`lastDisconnectReason`).

`read-page` returns stable refs you pass to `click`/`type`; refs go stale on navigation (re-read the page).
It also surfaces `contenteditable` rich-text editors as `textbox` refs so `type` reaches them (basic
editors; a heavy framework editor may still manage its own model).

A field's VALUE is used as its label only when nothing safer names it, and NEVER for a credential
field — a `type=password` box, or one whose `autocomplete` declares a password / one-time-code / card
number, reads as `[redacted]`. Chrome autofills a saved password into that value, so without this the
snapshot would hand the caller the user's plaintext password. You still learn that a value is PRESENT
(so "did my `type` land?" stays answerable); to have a human fill a secret you must never see, use
`POST /input-request`.

### Under the hood

Engine `src/main/services/real-chrome-bridge-v2/`, extension `real-chrome-extension-v2/`, host
`resources/real-chrome-v2-host/`. Invariants + the guarding-test map:
[real-chrome-bridge-v2-contract.md](../../.claude/memory/contracts/real-chrome-bridge-v2-contract.md).

## Related

Both bridges can be installed at once, and [real-chrome-bridge.md](real-chrome-bridge.md) is v1 — the debugger-based original this rebuild exists to replace, and the place to read about the drive → hand off → resume login pattern the two share. For the other ways Omniscio drives a browser, see [embedded-browser.md](embedded-browser.md) and [browser-logins.md](browser-logins.md). The invariants this bridge must hold, and the map of tests that guard them, are locked in [real-chrome-bridge-v2-contract.md](../../.claude/memory/contracts/real-chrome-bridge-v2-contract.md).
