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
Concurrent agents on one bridge: relayed commands carry no caller identity, so a bridge cannot arbitrate them and lifecycle verbs hit everyone
TL;DR
When several AI agents drive the same bridge on the same desktop at once, AD relays every one of their commands indistinguishably. A bridge receives no caller identity, so it cannot arbitrate between concurrent agents; no log can attribute a verb to the agent that issued it; and any agent's bridge_kill / bridge_pause silently destroys every other agent's state.
This is the AD-core half of concurrent-agent contention on pup. The session-level half (pup's owner model) is filed separately on the bridge's page — see the cross-ref at the bottom, per the bridge-ownership skill.
Important scoping note up front: this is not a throughput or locking problem. I measured that, and the transport layer is healthy (numbers below). The defect is identity, visibility, and blast radius.
Environment
- AD 1.9.177, target
H2O(Windows), connected via relay - puppeteer bridge (
source=cache), Node v26.5.0 - Caller: cloud container via the relay, 2026-07-25
- Other bridges live on the same AD: fusion360 (running, persistent)
Finding 1 (AD core) — relayed commands carry no caller identity, but AD has one
AD already knows a caller label and uses it for its own verbs. From this machine's AD lifecycle log (bridge_log_read {name:"puppeteer"} → adLifecycle):
[AD 2026-07-24 20:06:55] reaped by the bridge_kill verb (killedPids=[28084]); requested by testing
[AD 2026-07-24 20:40:04] reaped by the bridge_kill verb (killedPids=[16136]); requested by testing
[AD 2026-07-22 04:19:24] reaped by the bridge_kill verb (killedPids=[]); requested by testing
requested by testing — AD attributes its own lifecycle verbs to a caller.
But the bridge's own log, for the verbs AD relayed into it, has nothing:
[verb] browser_eval session=rev
[verb] browser_screenshot session=rev
[verb] browser_status
[verb] browser_navigate session=rev url=https://traces-…adom.cloud/reveal
Every line is anonymous. The only caller-ish token that ever reaches the bridge is one the caller supplies about itself (pup's owner arg), which is optional and unverifiable.
Why this bites
(a) No bridge can arbitrate concurrent agents. A bridge's only means of telling agents apart is a self-asserted string. pup does implement an ownership model on top of it — and it degrades to nothing in practice. The single live session on this machine right now:
{ "sessionId": "rev", "owner": null, "ageMinutes": 43, "errorCount": 3,
"background": "backgrounded",
"url": "https://traces-fuyou1ziuokn.adom.cloud/reveal" }
owner: null after 43 minutes of active driving. Per pup's documented rules an anonymous grab of that window is allowed (merely "loudly reprimanded" in _hint), so any second agent can navigate or close a window another agent is mid-task in. The protection exists but nothing can make it non-optional as long as ownership is caller-asserted.
(b) Interference is undiagnosable after the fact. Below is the bridge log during this investigation. My read-only probes (browser_status) are interleaved with a different agent's screenshot loop, and nothing distinguishes them:
[verb] browser_screenshot session=rev
[activity] "rev" agent driving — taskbar progress ON
[verb] browser_status
^ last line repeated 3x
[activity] "rev" idle — taskbar progress OFF
[verb] browser_status
Handler error: Error: page.screenshot timed out after 20000ms
^ last line repeated 6x
Two agents, one session, one undifferentiated stream. When a user reports "pup got weird while two things were running", there is no way to reconstruct who did what.
(c) [activity] "<session>" agent driving is per-session, not per-agent — so with two agents on one session it flaps ON/OFF and the taskbar progress indicator misreports.
This is the same gap flagged as "worth considering separately" at the end of the relay-token hardening issue (#33): X-Adom-User is transport-trusted attribution the caller supplies, so it does not distinguish callers.
Finding 2 (AD core) — lifecycle verbs are global and blind to other callers
bridge_kill, bridge_pause, bridge_uninstall (and a bridge-level browser_close) take effect for every caller. One agent doing routine recovery wipes out every other agent's in-flight work, with:
- no warning that other callers are currently active,
- no way for the acting agent to even know others are active (a direct consequence of Finding 1),
- no record afterwards of whose sessions were destroyed.
This is not hypothetical: bridge_kill is exactly what the hints recommend for a wedged bridge, and #31 documents an AI walking that recovery path. In a single-agent world that is fine. With concurrent agents it is a foot-gun that AD currently cannot even warn about.
What I measured that is NOT broken (so nobody chases it)
Transport-level concurrency is healthy. Please don't spend time here:
| Test | Result |
|---|---|
Sequential baseline, 3× browser_status |
31 / 37 / 40 ms wall |
6 concurrent browser_status (6 simulated agents) |
38, 66, 80, 84, 84, 85 ms wall — all status: ok. Parallel, not stacked (6× serial would be ~210 ms). |
Slow verb vs. fast verb: 15 s browser_eval (readOnly) on rev, plus 2× browser_status fired 1 s later |
eval 15320 ms (ok, correct result); the two status calls returned in 35 ms and 41 ms — not queued behind it |
No global bridge lock, no serialization pathology, no head-of-line blocking. Concurrent agents can share a bridge fine mechanically. What they cannot do is tell each other apart.
Asks
- Stamp a relay-assigned, non-forgeable caller identity on every relayed command, and pass it to bridges as a documented header (e.g.
X-Adom-Caller-Id+ a displayX-Adom-Caller-Name). AD already derives a caller label for its own verbs — expose that same label to bridges. Add it to the bridge SDK contract so bridge authors can rely on it. This also closes the forgeable-X-Adom-Usernote in #33. - Include the caller id in the per-verb relay path so a bridge can log
[verb] browser_eval session=rev caller=<id>and interference becomes reconstructable. - Make concurrent callers visible.
targets/statusshould list currently-connected callers;bridge_detailshould show which callers have touched that bridge recently (with last-seen). Today an agent has no way to discover it is not alone. - Guard destructive lifecycle verbs when other callers are active.
bridge_kill/bridge_pause/bridge_uninstallshould report the other live callers in the response, and ideally require areason(the permission gate already has this pattern) rather than silently taking everyone down.
Happy to test a pre-release — we routinely have several agent threads against one desktop, which is what surfaced this.
Ownership split (per the bridge-ownership skill): pup's session-level arbitration — making owner non-optional, refusing cross-owner grabs, per-session in-flight state — is the bridge author's and is filed at adom/adom-desktop-puppeteer-bridge#22. This issue is deliberately limited to what only AD core can fix: caller identity, caller visibility, and lifecycle blast radius.