Pup - Puppeteer Bridge
Public Made by Adomby adom
pup is the AI's own browser: a real, full Chrome on the user's desktop that the AI fully controls (a sandbox, not the user's signed-in browser). Rides Bridge; pup_* verbs open windows and tabs, navigate, screenshot, and eval JS.
Shipped v1.9.89 + v1.9.90: browser_close no longer leaves CPU-pegging orphaned renderers (froze a desktop), and pup now drives your INSTALLED Chrome by default (native-first)
Two changes shipped today that matter to anyone driving pup, plus the reasoning behind a doctrine reversal. Both are source-only (no dependency bump), so they reach the fleet through AD's normal bridge auto-update with no blocker.
1. v1.9.89 — browser_close no longer leaves CPU-pegging orphaned renderers
Impact before the fix: on one laptop this starved the Windows desktop compositor (dwm) badly enough to freeze the mouse and force a reboot. Overall CPU 88-98% on a 16-core box, with four --type=renderer children each eating roughly a full core. A second user independently reported pup at 90% CPU. James's issue #14 (three sessions running two days, battery drained) is the same root cause.
Root cause. On close, pup called browser.close() plus a process-tree kill, then swept for renderer children that had been reparented (a crashed or detached renderer survives a tree kill because it is no longer a child of the killed process). That sweep was:
- Fire-and-forget — the close returned
okbefore the sweep finished or failed, so a close that left processes behind still reported success. - Self-defeating under load — it enumerated the FULL Windows process table and filtered afterward, with an 8s timeout. At 88-98% CPU (exactly the condition the orphans create) that enumeration does not finish in time. It silently gave up, and the orphans kept spinning indefinitely.
Fix.
- The reap is awaited and verified — close does not return until processes are confirmed gone.
- Load-tolerant: the WMI query filters to chrome/msedge server-side, so only browser rows materialize. Fast even at 98% CPU.
- Every close response returns
chromeProcsRemaining(should be 0), with a warning hint if anything survives. - Boot + periodic (3 min) + close-all orphan sweeps reap any pup chrome whose profile has no live session — catching orphans from a prior crash or ones that outlived a close, with no close call needed. Live sessions are spared BY PROFILE, and the sweep only ever matches processes under pup's own profile dir, so it can never touch your real browser.
Verified with AD's own process_list (independent of pup's self-reporting): 3 windows spawned 21 processes including 6 renderers; after close, 0 remained, and each close reported chromeProcsRemaining: 0.
2. v1.9.90 — pup now drives your INSTALLED Chrome by default (native-first)
The change. Browser preference order is now installed Chrome → Edge → Chrome-for-Testing (last resort). CfT is downloaded only when a box has no installed Chromium at all. browser_use {browser:"cft"} still pins CfT explicitly.
Why the reversal. CfT-first (v1.8.71) existed for one reason: driving branded Chrome popped the "Sign in to Chromium? / Set up a work profile" dialog that poisoned a user's session. That reason is dead, and had been for a while:
- CfT shows the same dialog. Measured live 2026-07-19. Switching binaries never fixed it.
- The real fix was launch flags, and they apply to ANY Chromium:
--allow-browser-signin=false,--disable-sync,--disable-signin-promo, and--disable-features=SigninIntercept,ForceSignInReauth,SyncPromoAfterSignin,DiceWebSigninInterception,ProfilePicker. Combined with a fresh isolated--user-data-dir, native Chrome is exactly as clean as CfT.
What native-first buys.
- No 150 MB download, and no half-populated-cache failure mode (issue #15).
- Always current — the user's auto-updated, battle-tested build, instead of a pinned CfT that drifts stale (the fleet was on CfT 146 while stable was 152) and needs a puppeteer bump plus a fleet dependency refresh to move (blocked by
preserve_client_deps, adom/adom-desktop#28). - Not detected as a test browser. Sites (notably behind Cloudflare) that block Chrome-for-Testing see an ordinary Chrome.
- All taskbar branding still works — identity/icon overrides are OS-window-level and browser-agnostic. Verified: a fully-branded native Chrome 150 window.
What CfT is still for: a managed/enterprise box whose Chrome carries policies (forced sign-in, restricted remote-debugging) or extensions that interfere with automation. CfT is policy-free and deterministic there. That is a per-machine browser_use {browser:"cft"} pin, not a fleet default.
Verified end-to-end: with the pin cleared, browser_readiness reports candidate order chrome(system) → edge(system) → chrome-for-testing(cache). A Google sign-in window opened on native Chrome (C:\Program Files\Google\Chrome, confirmed via AD process_list, not pup's own claim), with all four identity-suppression flags present on its command line, and the sign-in page rendered clean: no merge dialog, ownedPopupCount: 0.
Note on the freeze: it was NOT a CfT bug. It was the reap bug above, and native Chrome would have frozen the machine identically. The browser choice was re-decided on its own merits, not as a freeze fix.
Getting both
Automatic on AD's next bridge poll (~4h), or immediately:
refresh_bridges {"name":"puppeteer"}
restart_bridge {"name":"puppeteer"}
Confirm with bridge_list → puppeteer at 1.9.90 or newer.
If your box is pegged by orphans from an older build right now, the restart's boot sweep clears them within ~3 minutes (or call browser_close with no sessionId for an immediate verified sweep).
Full reasoning for the browser decision, including what would justify reversing it again, is in the pup-bridge-dev skill's "DESIGN DECISION" section in this repo.