---
name: pup-browsers-and-chrome
user-invocable: true
description: "How pup (Puppeteer bridge) picks and launches a browser, and every cold-start / readiness case. Covers: the installed-browser-first architecture (launches a FRESH CDP-driven process of installed Chrome → else Edge → else cached Chrome for Testing, spawn-verified with fallthrough, caches the winner as default); browser_use to pin chrome/edge/cft/auto; INSTALLING real Google Chrome on an Edge-only box (browser_use install:true) including the UAC-approval-notify flow on locked-down machines; installing/prewarming Chrome for Testing; browser_readiness (READ-ONLY — the right 'is it ready / which browser' probe, NOT browser_status); the cold-start error table (node_not_found, bridge_restarting, chrome_for_testing_installing, chrome_install_no_disk, CfT 'skipped' is not a failure); AD-managed Node provisioning (no install needed on AD >=1.9.63); and the shell-approval gate. Read on any browser-pick question, a fresh/first-run PC, an install/prewarm, a not-ready error, or a shell-approval prompt. Trigger words: browser_readiness, browser_use, browser_prewarm, chrome for testing, install chrome, chrome_for_testing_installing, node_not_found, bridge_restarting, chrome_install_no_disk, cold start pup, which browser, edge only box, UAC approve chrome, request_shell_approval, shell command approval, adom runtimes, pup not ready."
---

**Parent skill:** adom-desktop-puppeteer-bridge  ·  See also: `pup` (start here), `pup-windows-sessions-tabs`, `pup-screenshots-recording`.

# pup — browsers, Chrome install & cold-start

## How pup picks + launches a browser (the architecture)

pup drives the browser **already on the machine** — it does NOT ship or require its own. On every open it
builds a candidate list and **spawn-verifies** it:

1. **Chrome → Edge → cached Chrome for Testing** (a pinned default, if set, goes first). Every Windows PC
   ships Edge, so there's almost always a candidate with **no download**.
2. It **launches a fresh process** of that browser binary with an **isolated `--user-data-dir` + a CDP
   `--remote-debugging-port`** — a clean empty profile driven over the DevTools Protocol, never the user's
   signed-in profile, and a **real rendered window** (not headless).
3. If the top pick is present but **won't start** (corrupt/partial install), pup **falls through** to the
   next — so an open never hard-fails on one broken browser.
4. The browser that actually launched is **cached as the default** (persisted across bridge restarts) so it
   isn't re-probed each time.
5. **Chrome for Testing is the last resort** — fetched (~150 MB) only if the box has *no* Chrome and *no*
   Edge and *no* cached CfT.

## "Is it ready / which browser?" — `browser_readiness` (READ-ONLY)

```bash
adom-desktop browser_readiness '{}'
# → {ready, browserKind:"chrome"|"edge"|"chrome-for-testing", browserSource, browserExecutablePath,
#    candidates:[…the full fallthrough list…], defaultBrowser, diskFreeMb, chromeStablePhase, …}
```

- **`browser_readiness`** is the right probe — it never triggers a download and tells you exactly which
  browser will be driven + the candidate list + the default.
- **`browser_status` is NOT a readiness check** — it only lists open sessions.
- AD-core **`bridge_readiness`** is the platform-level "is this bridge up?" probe.

## Pinning a browser — `browser_use`

pup auto-picks, but you can override (persists across restarts; launch still spawn-verifies + falls back):

```bash
adom-desktop browser_use '{"browser":"chrome"}'   # pin installed Chrome
adom-desktop browser_use '{"browser":"edge"}'     # pin Edge
adom-desktop browser_use '{"browser":"cft"}'      # install Chrome for Testing (~150 MB once) + pin it
adom-desktop browser_use '{"browser":"auto"}'     # back to auto-pick
```

Every `browser_open_window` response also carries `browser` (what was driven), `availableBrowsers`, and
`defaultBrowser` — surface it if the user asks which browser pup is using.

## Installing real Google Chrome on an Edge-only box

If the user specifically wants Chrome:

```bash
adom-desktop browser_use '{"browser":"chrome","install":true}'
```

- Downloads Google's official installer and installs Chrome, then pins it. **On a normal PC this is
  silent** (user-scoped, no prompt).
- **On a locked-down box (a VM / managed device) it needs a Windows UAC.** pup escalates automatically and,
  on **AD ≥ 1.9.84**, fires a native "**click YES to finish installing Chrome**" notification **from the
  bridge itself** — on the machine with the prompt **and** pinged to whatever device the user is attending
  (cross-AD). If the prompt expires it re-notifies with a one-tap "Approve now". All autonomous — no AI turn
  needed. On older AD (no bridge-notify) it raises the UAC once and returns a hint for **you** to relay
  ("go to the box and click YES"), then you re-run the verb.
- **Poll `browser_readiness`**: `chromeStablePhase` goes `installing → awaiting_uac` (waiting on the user's
  click) `→ ready`; `browserKind` flips to `"chrome"`. pup keeps driving Edge meanwhile, so nothing is
  blocked. `elevate:"auto"|true|false` on the call tunes the prompt behavior.

## Installing / prewarming Chrome for Testing (last resort)

```bash
adom-desktop browser_prewarm '{}'        # fetch CfT WITHOUT opening a window, then pin it (poll readiness)
adom-desktop browser_use '{"browser":"cft"}'   # same, via the pin path
```

Only needed on a box with no Chrome and no Edge. **CfT reported as `skipped` is NOT a failure** — on AD
≥ 1.9.76 the prewarm is conditional, so a box with a system browser skips the ~150 MB download and
`bridge_readiness` shows the asset `{present:false, skipped:true}`. That's the intended
installed-browser-first behavior; don't retry or report an error.

## Cold-start error table — what a `browser_open_window` response can mean

| Response | Means | Do |
|---|---|---|
| `ok:true` / a real session | A browser was found + launched | Proceed — the common case |
| `errorCode:"node_not_found"` / "could not run npm" | The bridge's Node runtime isn't ready yet | **AD ≥ 1.9.63 provisions Node for you** — check `adom-desktop runtimes '{}'`; if `installing`, wait a few seconds and **retry**. No user action. **Known AD false-negative:** if `runtimes` shows node `ready` AND `browser_readiness` works but other verbs still say `node_not_found`, it's an AD verb-gate bug — do NOT loop `desktop_install_node`; tell the user to update AD |
| `errorCode:"bridge_restarting"` (`stillRunning:true`) | AD ≥ 1.9.77: verb landed in the brief gap while the bridge respawns after an install/update | Poll `browser_readiness` until it answers, then retry the same verb — not a failure |
| `errorCode:"chrome_for_testing_installing"` (`installing:true`) | RARE — no Chrome AND no Edge, so pup is fetching CfT in the background | Poll `browser_readiness` until `{ready:true}`, then re-issue. Faster: ask the user to install Chrome/Edge (Edge ships with Windows) |
| `errorCode:"chrome_install_no_disk"` | No browser + too little disk for CfT (`readiness.diskFreeMb`) | Tell the user how much is free vs the ~800 MB needed; ask them to free space or install Edge. Do NOT retry the download |
| `errorCode:"chrome_install_failed"` / `chrome_download_network` / `chrome_install_permission` | No browser AND the CfT fallback download failed | Read `readiness.lastError`; ask the user to install Chrome or Edge |

**Always surface a verb's `_hint` verbatim** when it reports not-ready or failure.

## Node provisioning — you do NOTHING (AD ≥ 1.9.63)

pup is a Node process, but **you no longer install Node.** AD provisions the runtime itself — a system Node
if on PATH, else a portable **no-UAC** copy it downloads to `~/.adom/adom-runtimes/` and prewarms, then
spawns the bridge by absolute path. Do NOT run `desktop_install_node` / npm installers / ask the user to
install Node. On a truly fresh box it may be `installing` for a few seconds after first launch — check
`adom-desktop runtimes '{}'` (`{state: absent|installing|ready|failed}`) and retry once `ready`. (Legacy
`desktop_install_node` — which pops a UAC — is only for AD older than 1.9.63.)

## The shell-approval gate (rarely relevant to pup)

Core AD verbs and pup's own `browser_*` / `desktop_record_*` verbs run with **NO desktop prompt**. Only
open-ended `shell_execute` / `run_script` pop AD's "Shell command needs approval" dialog — which you
almost never need (prefer typed verbs). If one returns `needs_approval` / `human_only` on **AD ≥ 1.9.86**:
run `adom-desktop --target <box> request_shell_approval` (re-surfaces the dialog), ask the user to click
Approve/Bypass, then **retry your original verb**. Never call `shell_auto_approve` (human-only, refuses).
This is AD's own gate, NOT a Windows UAC.
