name: pup user-invocable: true description: "Drive a browser on the user's desktop from the cloud — the Puppeteer 'pup' bridge (browser_* verbs via the adom-desktop CLI). pup launches a FRESH, isolated process of the browser ALREADY on the machine (installed Chrome, else Microsoft Edge — every Windows PC has Edge), driven over CDP, in the BACKGROUND so it never disturbs the user; NOT the user's real signed-in profile (that's the Adom extension's nbrowser_* verbs), NOT headless (a real rendered window, so screenshots/recording are true-to-life). Chrome for Testing only as a last resort. Open/close windows + tabs, navigate, screenshot, eval JS, record. This is the START-HERE skill: the mental model + when-to-use + the skill map; deeper topics route to the sub-skills. For 'is it ready' use browser_readiness (not browser_status). Trigger words: pup, puppeteer, browser window, open in pup, open my app in pup, browser screenshot, browser eval, browser reload, visual debug, headful chrome, browser_open_window, browser_screenshot, browser_readiness, browser_prewarm, chrome for testing, open in browser."

Pup — browser control on the user's desktop (start here)

Drive a browser on the user's desktop from a cloud container via the adom-desktop CLI. Every verb is a Bash command (NOT an MCP tool):

adom-desktop browser_open_window '{"sessionId":"mytask-web","owner":"mytask","url":"https://example.com"}'
adom-desktop browser_screenshot  '{"sessionId":"mytask-web"}'

The mental model — what pup actually is (read this once)

pup is a small Node bridge that Adom Desktop spawns on the user's machine. When you open a window it does NOT attach to the user's running browser and does NOT run headless. It:

  1. Launches a brand-new process of the browser ALREADY installed on the machine — installed Chrome first, else Microsoft Edge (every Windows PC ships Edge). So there's usually no download. It uses their browser binary, never their profile.
  2. …with its own isolated --user-data-dir + a CDP debugging port (--remote-debugging-port). That's a fresh, empty profile — no cookies, no logins, no extensions, its own cookie jar keyed by your profile name — and pup drives it over the Chrome DevTools Protocol. It is never the user's signed-in Chrome/Edge. (Need their real profile/cookies/SSO? That's the Adom extension nbrowser_* — see below.)
  3. Renders a REAL window, but keeps it in the BACKGROUND. It is not headless — a real GPU-composited window, so browser_screenshot / recording are true-to-life and canvas/WebGL/media render correctly. But pup immediately z-orders it to the bottom (Win32 SetWindowPos with SWP_NOACTIVATE), so it renders without stealing focus and without covering the user's active window. You drive and screenshot it fully while it stays hidden. foreground:true is the ONLY thing that shows it.
  4. Spawn-verifies + falls through. The pick order is Chrome → Edge → cached Chrome for Testing; pup actually launches each and, if one won't start (corrupt install), falls to the next — so an open never hard-fails on a broken browser. The one that launches is cached as the default. Only a box with no Chromium at all fetches Chrome for Testing (~150 MB, last resort).
  5. Is driven by sessionId, and one session = one window that holds many tabs.

Keep this model in mind and every verb makes sense: it's a disposable, isolated, backgrounded copy of the user's own browser, driven by CDP.

When pup — vs the Adom extension (nbrowser_*)

The task Tool
Screenshot / scrape / test / demo a page; drive an app you're building; anything that does NOT need the user's logins pup (this)
Log into a site, use saved passwords, fill a form on a signed-in page, download behind a login, sail past captchas Adom browser extension (nbrowser_*, the adom-browser-extension skill) — drives their REAL signed-in Chrome/Edge

pup is generic only — a fresh empty profile every time. It cannot use the user's cookies/SSO. That's by design; the extension is its counterpart for the real profile.

Skill map — this is a pack

Skill Read it for
pup (this file) The mental model, when-to-use, quick start, the essentials
pup-windows-sessions-tabs sessionIds + naming, window ownership (don't steal another AI thread's window), background vs foreground rules, many tabs in one window, list/switch/close, verifying what you opened
pup-screenshots-recording Screenshots (full-page, full-res), reading the shot, recording a window vs the whole desktop, framing a window for a clean shot without foregrounding
pup-browsers-and-chrome The browser detection/CDP-launch details, browser_use to pin a browser, installing Chrome / Chrome-for-Testing (incl. the UAC-notify flow), readiness, the cold-start error table, Node provisioning, the shell-approval gate
pup-adom-wiki Driving + verifying wiki.adom.inc pages in pup (show/screenshot/verify a published page; the login situation — pup views the wiki logged out for now)

(Maintaining the bridge itself? The pup-bridge-dev + pup-bridge-publish skills are source-only in the repo — not shipped here.)

Quick start — the 90% path

# Open in the BACKGROUND (automatic — never disturbs the user), then drive it invisibly.
adom-desktop browser_open_window '{"sessionId":"mytask-web","owner":"mytask","url":"http://localhost:3000"}'
adom-desktop browser_eval        '{"sessionId":"mytask-web","expr":"document.title"}'   # verify it really loaded
adom-desktop browser_screenshot  '{"sessionId":"mytask-web"}'                            # true-to-life PNG
  • Task-prefix your sessionId and pass owner — sessions are shared across every AI thread on the desktop; this stops another thread stealing your window (and you stealing theirs). See pup-windows-sessions-tabs.
  • ok:true means navigation started, not that content rendered — sanity-check with browser_eval on document.title (404s / login walls change it) or a screenshot.
  • On a fresh PC it's usually a non-event — pup drives the installed Chrome/Edge with no download. The rare "no browser at all" / Node-provisioning / disk cases are all in pup-browsers-and-chrome.

Taskbar preference — the USER can ask for "combined mode"

pup windows each get their own taskbar button by default ("split": one button per window, badged with its tab count). If the user says anything like "combine my pup windows", "group the pup icons", "one taskbar button for pup", "my taskbar is cluttered with pup windows" — that's the GROUPED preference: browser_configure {"taskbarGrouping":"grouped"} stacks every pup window under one "Adom Pup" button whose badge shows windows + total tabs. "Split them back" / "separate icons" → {"taskbarGrouping":"split"}. The setting persists across updates. Change it ONLY when the user asks — it's their taskbar. Details: pup-windows-sessions-tabs.

Window-type taskbar icons (split mode) — what the user sees per window

In split mode every pup window's taskbar icon states its CONTENT KIND. All five share the teal Adom tile with a solid browser-window drawing; the glyph inside the window is the category:

Glyph inside the window Category
open book Adom wiki, PUBLIC view (logged out)
book + person Adom wiki, SIGNED IN (the user's logged-in view)
the Adom mark an Adom APP (localhost / cloud-slug proxy URL)
globe the web (any other site)
two panes MIXED (tabs span more than one category)

The category derives from ALL tabs and updates live (about a second). The corner overlay stays the ACTIVE TAB'S FAVICON (or window/tab counters in grouped mode) — never repurpose it. Adom wiki tab titles also lead with a text glyph: solid dot = signed in, hollow dot = public. You never manage any of this; pup does it automatically. It is worth KNOWING because the user can see login state and window kind at a glance — do not open a second window to "check" what a taskbar icon already answers, and when the user says "the window with the book/globe icon" you know which one they mean.

A WIKI window's taskbar right-click jump list also carries a view toggle ("Adom wiki: switch to logged-in view" / "...to public view") — a USER affordance that relaunches that window under the other cookie jar with on-screen captions. Its callback verb browser_wiki_set_view is not for AI threads; pass wikiView:"authed"|"public" on browser_open_window instead. Details: pup-adom-wiki.

Key rules (the essentials — details in the sub-skills)

  • Always pass sessionId on session-scoped verbs — '{}' targets the active session, which may not be the one you mean. There is no top-level pup subcommand; it's adom-desktop browser_<cmd> <json>.
  • Background is guaranteed; foreground:true is the only way to show a window — not sizing, not navigating, not screenshotting.
  • Own your window (owner:"<task>", task-prefixed sessionId); never navigate/close a window you don't own.
  • One window, many tabs — use browser_open_tab, not a second browser_open_window, for multi-URL work.
  • browser_readiness (not browser_status) answers "is it ready / which browser?".
  • No semicolons in browser_eval — use the comma operator or an IIFE.
  • Complex page fighting you? (a querySelectorAll comes back empty, or a screenshot overshoots the target) — it's usually shadow DOM or a nested scroll container. Verify by DOM, scroll the element not the window, count before you eyeball. See pup-screenshots-recording → "Driving & verifying tricky pages."
  • Never broadly kill Chromebrowser_close_window for a specific session.
  • Surface a verb's _hint verbatim whenever it reports not-ready or failure.
  • If pup's verbs TIME OUT (or the AD LED is dim / browser.bridgeRunning:false) the bridge PROCESS is down — usually right after a restart_bridge or a crash. To wake it, fire one browser_open_window (a status probe like browser_readiness does NOT respawn a down bridge — only a window/tab verb does), then poll browser_list_windows with backoff until it answers. Do NOT spam opens while AD says bridge_starting — one is enough; it's already starting. (bridge_list status:"running" is not liveness; the LED + browser.bridgeRunning + an actual verb reply are.)
  • Full machine-readable catalog: adom-desktop browser_describe '{}'.