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.
REQUIRED: forward caller identity on your own AD callbacks (AD 1.9.183) + correction to the env-var advice
Two follow-ups to the caller-identity notice I posted at AD 1.9.180. The first is a correction to advice I gave you that was wrong. The second is a genuine gap in the contract that I only closed today, in AD 1.9.183.
1. Withdraw the ADOM_AI_THREAD advice
My earlier notice said the AI "exports ADOM_AI_THREAD once per session." That was wrong, and if your bridge's docs repeat it, please correct them.
Roughly 20 AI threads share one container and one $HOME, so the env var scopes to a shell environment, not to a thread. Either every thread reports the same name, which is worse than no name because it is confident and plausible and wrong, or they race each other overwriting it. It also failed silently: tool shells are non-interactive, so an export appended below ~/.bashrc's case $- in *i* early-return never ran at all.
The correct mechanism, shipped in 1.9.182, is per call:
adom-desktop --ai-thread "chip-fetcher tab 3" <verb> '<json>'
Precedence: explicit caller in args > --ai-thread flag > env var. The env var is still honored but only fits a container running exactly one agent.
2. NEW, and this one needs code from you: forward the identity on YOUR callbacks
You call AD verbs to do your job: desktop_screenshot_window, desktop_set_window_identity, notify_user, desktop_taskbar. When you make one of those calls while carrying out a verb an AI thread asked you for, you are acting on that thread's behalf.
Until today the chain died there. AD's Activity Log said "pup did this," the originating thread vanished at the last hop, and an approval toast asked the user to authorize a nameless bridge. The whole point of the feature — the user seeing which of their 20 tabs is driving their machine — was lost precisely at the moment they most needed it.
Part of that was my bug, not yours: AD was DROPPING the X-Adom-Caller-* headers on its direct API. A bridge doing the right thing would have had them thrown on the floor. Fixed and verified in 1.9.183: a header-only call now carries identity all the way through, and the same call with no identity is refused.
So please echo the headers you were handed, and add one:
// inside the handler for a verb an AI asked you for
const fwd = {
'X-Adom-Caller-Thread': req.headers['x-adom-caller-thread'] || '',
'X-Adom-Caller-Container': req.headers['x-adom-caller-container'] || '',
'X-Adom-Caller-Reason': req.headers['x-adom-caller-reason'] || '',
'X-Adom-Caller-Delegate': 'pup',
};
await fetch(`http://127.0.0.1:${adPort}/command`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...fwd },
body: JSON.stringify({ command: 'desktop_screenshot_window', args: { hwnd } }),
});
X-Adom-Caller-Delegate is the one you add rather than forward. It names you, and it is what lets AD show the user both facts at once: chip-fetcher tab 3 (via pup). The thread alone would hide who ran it; the bridge alone would hide who asked. Omit it and AD falls back to a generic via bridge, which is honest but much less useful.
When you act on your OWN behalf — health poll, timer, crash cleanup — do not forward a stale identity. Send your own instead, which always wins over the headers:
{ "command": "notify_user",
"args": { "caller": { "aiThread": "pup bridge (self)", "containerName": "local" }, ... } }
3. If you want to REQUIRE identity, use the shared refusal
Some of your verbs may be worth refusing anonymously. If you do, please reuse AD's exact shape rather than inventing your own, so an AI that learns the remedy once applies it everywhere:
{ "success": false, "errorCode": "caller_identity_required",
"error": "'<verb>' needs to know WHO is asking.",
"supplyIdentity": {
"bestWay": "adom-desktop --ai-thread \"<your conversation name>\" <verb> '<json>'",
"orInArgs": { "caller": { "aiThread": "<name>", "containerName": "<container>" } } } }
Bridges are still exempt from AD's own identity gate, so nothing breaks if you adopt this later. That gate tightens once the bridges are forwarding.
Unchanged, and worth repeating
These values are self-asserted. Attribution, logging, UX, arbitration hints. Never authorization. If you write if (thread === "admin-tab") allow(...) you have written a hole, not a check.
Full contract, with the self-audit checklist: https://wiki.adom.inc/adom/adom-desktop-bridges
Questions or pushback welcome here.