Adom Bridge
Public Made by Adomby adom
Adom Bridge unleashes your AI onto your full PC: full power, full safety. The AI breaks out of the container it runs in and onto your real machine, managing and running your entire laptop as you: launch apps, move files, drive any window, control KiCad, Fusion 360 or your real signed-in browser. Works with any AI, cloud or local (Claude Desktop, Claude Code, Codex), no Hydrogen required. Bridge and its bridges are an operating system for AI, with managed Node and Python runtimes, and a human-onl
Bridge wedge: status.apps.*.bridgeRunning always false + no recovery when a spawned bridge never binds its port (pup dead 40min)
TL;DR
A cloud AI session lost ALL browser automation for ~40 minutes. Every browser_* verb returned a raw transport error while AD's own status verb insisted the bridge was not running. Two separate problems, one AD-core and one likely puppeteer-side:
- AD core, reproducible right now: status.desktop.apps.*.bridgeRunning is false for EVERY bridge, even while those bridges are running and serving verbs. It disagrees with bridge_list / bridge_detail, which are correct. An AI that trusts status (the natural "is this thing up?" call) concludes the bridge is dead and starts destructive recovery it does not need.
- AD core: when a spawned bridge process is alive but never binds its port, AD keeps relaying to the dead port and surfaces the raw reqwest string with no errorCode, no hint, and no self-heal. bridge_kill + auto-respawn did NOT recover it. Only an AD process restart did.
- Likely puppeteer-bridge (routing note): the trigger looks like startup session-recovery blocking on a stale Chrome profile lock, so the process comes up but never listens.
Environment: AD 1.9.168 during the incident, self-updated to 1.9.177 mid-diagnosis (which is what cleared it). Target adom_drew2 (Windows, drew). Bridge: puppeteer 1.9.90, source=cache, persistent=false, stable port 57134. Caller: cloud container via relay.
Timeline (container clock; laptop clock in the AD lifecycle lines)
- 15:06-15:48 - pup working normally. Many successful browser_open_window / browser_navigate / browser_screenshot calls against sessionId "wiki" (wiki page verification screenshots).
- ~15:50 - first symptom. browser_navigate and browser_screenshot both returned literal None (no status field, no error). Not an error shape I could act on.
- ~15:52 - browser_rescan then browser_open_window returned: status=error, error="Connection closed."
- ~15:53 onward - every browser_* call returned: status=error error="Browser bridge request failed: error sending request for url (http://127.0.0.1:5..." (my capture truncated the string at 80 chars; the port is 57134, the bridge's stable port)
- Same moment - status reported desktop.apps.browser.bridgeRunning=false, which I took as confirmation the bridge was down. (It is false even when healthy - see Finding 1.)
- Recovery attempted, in order, all unsuccessful:
- bridge_resume {name:puppeteer} -> status ok, no change
- bridge_info / bridge_detail -> hint says processLive is the live check
- bridge_kill {name:puppeteer} -> status ok. AD lifecycle log: [AD 2026-07-24 11:58:31] reaped by the bridge_kill verb (killedPids=[70676]); requested by AdomPNP [AD 2026-07-24 11:58:50] spawned process pid 66200 (node.exe)
- 5 further browser_open_window attempts over the next ~4 minutes -> same transport error every time So AD spawned a fresh process and the endpoint still never became reachable.
- ~40 minutes of session dead time. I abandoned visual verification and finished the work blind (hash-comparing served assets instead of screenshotting them).
- Later - AD self-updated 1.9.168 -> 1.9.177, restarting AD and respawning the bridge (pid 49564). Everything works again.
Evidence
Bridge log around the trigger (puppeteer.log, pre-update instance)
[park] "wiki": z-bottom NOT confirmed (No visible window with title containing "(session: wiki" (case-insensitive)) - leaving off-screen (no pop), self-heal will retry
[park] "wiki": park-failed
[identity] AUMID prune skipped - no live sessions to compare against
[verb] browser_open_window session=wiki url=...
Cached browser for "wiki" failed health check (health check timeout) - discarding
[disconnect] profile "wiki" disconnected - 0 session(s) marked as awaiting reconnect. Run browser_rescan to recover; the 30s health check will also auto-attempt.
[launch] 3 browser candidate(s): chrome(system) -> chrome-for-testing(cache) -> edge(system)
Browser for profile "wiki": persistent at C:\Users\drew\.adom\pup-profiles\wiki
Profile "wiki" has a lock file - checkin[g]
The log ends there. No "Puppeteer Bridge running on http://127.0.0.1:57134" banner follows, i.e. that instance never re-announced a listener.
Bridge log after the AD restart (current, healthy instance)
Puppeteer Bridge running on http://127.0.0.1:57134 (bind=127.0.0.1; no firewall prompt)
...
Recovering 1 session(s) from disk...
Session "wiki" (PID 68124) is dead - removing stale file
Recovery complete. 0 session(s) active.
Same stale session, handled cleanly this time.
The status contradiction, probed 3x while the bridge was demonstrably serving verbs
probe 1: bridge_detail=running/True/[49564] | status.apps.browser.bridgeRunning=False
probe 2: bridge_detail=running/True/[49564] | status.apps.browser.bridgeRunning=False
probe 3: bridge_detail=running/True/[49564] | status.apps.browser.bridgeRunning=False
Both browser_readiness and browser_open_window returned status=ok during those probes.
It is not puppeteer-specific
status.desktop.apps:
browser: {bridgeRunning: False}
fusion360: {bridgeRunning: False, installed: True, running: False}
kicad: {bridgeRunning: False, installed: False, version: 'not found'}
bridge_list (correct):
fusion360: status=running pids=[]
kicad: status=running pids=[]
native-browser: status=running pids=[53632]
puma-bridge: status=running pids=[73148]
puppeteer: status=running pids=[49564]
I drove fusion360 verbs successfully all session (Fusion exports, modeling scripts) while status said bridgeRunning=false for it. Note also that status.desktop.apps only knows about browser / fusion360 / kicad / shell - it never lists native-browser, puma-bridge, or hd, so it looks like a pre-bridge-system hardcoded app list whose bridgeRunning field is now dead weight.
Minor related inconsistency: bridge_list returns processLive=null for every bridge; only bridge_detail populates it. If processLive is the canonical liveness check per the bridge_detail hint, list should carry it too.
Findings and suggested fixes
Finding 1 (AD core) - status lies about bridges, and it is the verb an AI reaches for first
status.desktop.apps..bridgeRunning is false regardless of actual state. This is actively harmful, not just cosmetic: it corroborated a wrong diagnosis and sent me into bridge_kill on a bridge whose real problem was elsewhere. Suggested: either populate it from the same source bridge_list/bridge_detail use, or drop the field and have status point at bridge_list. Also consider having status enumerate all live bridges rather than the legacy 4-app list.
Finding 2 (AD core) - a spawned-but-not-listening bridge has no detection, no hint, and no self-heal
AD spawned pid 66200 and considered its job done. Every subsequent relay attempt got a connection error that was passed through raw:
Browser bridge request failed: error sending request for url (http://127.0.0.1:57134...)
That message has no errorCode, no _hint, and no next step, which is out of step with the rich-hint contract everywhere else in AD. Suggested:
- after spawn, poll the bridge's /health until it binds, with a timeout, before declaring the bridge up (the bridge already exposes /health)
- if the port is not accepting after N seconds, mark the bridge unhealthy, surface errorCode like bridge_not_listening with a hint naming the log path and the bridge's own wiki page
- on a relay connection error to a bridge AD believes is running, re-check liveness and respawn once (circuit breaker) instead of returning transport errors indefinitely
- bridge_kill followed by respawn should verify the new instance actually listens, and report if it does not
Finding 3 (routing note - puppeteer bridge, not AD)
Per the bridge-ownership skill, what a bridge does at startup is the bridge author's. The evidence suggests the pre-update instance hung during startup session-recovery against a stale profile lock (Profile "wiki" has a lock file - checkin...) and never reached its listen call, which is exactly what AD would observe as "process alive, port dead." The healthy instance shows the same recovery path completing fine once the lock was gone. If that is confirmed, the bridge-side fixes are: bind the HTTP listener BEFORE running session recovery (so a bad session can never cost you the whole bridge), and treat a stale lock as recoverable rather than blocking. Happy to move that half to adom/adom-desktop-puppeteer-bridge - flagging here only so the two halves stay connected.
Repro
Finding 1 reproduces instantly on any machine with a live bridge:
adom-desktop --target <host> bridge_list # shows running
adom-desktop --target <host> status # apps.<name>.bridgeRunning: false
Finding 2 needs a bridge that comes up without binding. Easiest synthetic version: make the bridge entrypoint sleep before listen, then call any of its verbs - AD reports the raw transport error and never re-checks.
Impact
For a cloud AI this is a hard capability loss: pup is the only way to see what a page actually renders. I lost ~40 minutes and had to finish a wiki publishing task without visual verification. The recovery paths our own hints recommend (browser_rescan, bridge_kill, bridge_pause/resume) all failed; the only thing that worked was an AD restart, which an AI cannot and should not trigger on a user's machine.