app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
← Commit history
page.json+1−1@@ -4,7 +4,7 @@ "slug": "pup-bridge", "title": "Pup - Puppeteer Bridge", "brief": "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.",- "version": "2.0.388",+ "version": "2.0.390", "tags": [ "skills", "pup",
tools/pup-dashboard+8−31@@ -84,29 +84,12 @@ function censusThread(j) { // So /new always opened the tab (which is why John SAW a thread appear) and the second call always // bailed on the missing sid. The composite below opens the thread AND delivers the prompt in one // request; `name` and `tags` stage exactly as they do on /new and attach when the sid appears.-async function sendToNewThread(phrase) {- // UN-PARKED 2026-08-24 (John: "we are re-enabling the send to ai"). The ah build thread certified- // the crash family dead on the gate-fixed build (33ac2074, six raw-CDP typing helpers that ran- // outside the concurrency gate), ~20 min of the kill condition with zero process deaths.- if (!SAY.includes(phrase)) return { ok: false, why: 'unknown phrase' };- const guard = await shellSupportsSend();- if (!guard.ok) return { ok: false, why: guard.why };- // 45s, per the skill: "Opening drives the editor (extension command, dual detection, tab- // placement) and legitimately takes 10-30s on a busy workbench; every CDP touch also serializes- // through one gate ... A 5-10s client timeout manufactures phantom failures." The old code used- // hpost's 4s default, so even a correct call would have reported failure on a busy workbench.- const run = await hpost('/ai-threads/run-prompt', {- new: true, prompt: phrase, submit: true, name: 'Pup - say it', tags: ['dock:pup-bridge'],- }, 45000);- if (!run.status) return { ok: false, why: 'Could not reach the AI panel (' + (run.error || 'no response') + '); the phrase was copied instead.' };- if (!(run.status >= 200 && run.status < 300)) return { ok: false, why: 'The AI panel refused the prompt (HTTP ' + run.status + '); the phrase was copied instead.' };- const j = run.json || {};- if (j.ok === false) return { ok: false, why: 'The AI panel refused the prompt (' + (j.error || j.why || 'no reason given') + '); the phrase was copied instead.' };- // The response verifies delivery against the on-disk transcript, so report what it actually- // says rather than treating HTTP 200 as proof the prompt landed in the editor.- return { ok: true, sid: j.sid || null, deliveredTo: j.delivered_to || null };-}-+// 2026-09-03 (dock-bar broadcast, Hydrogen 1.0.187+ Send-to-AI contract): the container-side+// send path (sendToNewThread -> /ai-threads/run-prompt, verified via delivered_to) is REMOVED. On+// 1.0.187+ delivered_to is never filled, so this path would have called every successful send a+// failure. Send to AI lives in the bridge-served dashboard (newbie-panel.js 2.22.0, job/state+// contract, polled through the bridge's /ai-threads/run-prompt/status proxy). The routes below+// answer 410 so a stale caller gets a pointer instead of a phantom failure. // RELEASE-VERSION GATE, on the ONE endpoint that actually carries a release version. // Corrected twice, so the record matters: // build_sha - LIES on incremental builds (the build thread had to verify by code marker).@@ -549,14 +532,8 @@ async function serve() { return res.end(JSON.stringify({ lines })); } if ((u.endsWith('/api/say-send') || u.endsWith('/api/try')) && req.method === 'POST') {- let body = ''; req.on('data', d => { body += d; });- req.on('end', async () => {- let phrase = ''; try { phrase = String(JSON.parse(body).phrase || ''); } catch (e) {}- const out = await sendToNewThread(phrase);- res.writeHead(200, { 'content-type': 'application/json', ...CORS });- res.end(JSON.stringify(out));- });- return;+ res.writeHead(410, { 'content-type': 'application/json', ...CORS });+ return res.end(JSON.stringify({ ok: false, gone: true, why: 'Send to AI moved to the bridge dashboard (newbie-panel 2.22.0, Hydrogen 1.0.187+ job contract); this container-side route no longer sends.' })); } if (u.endsWith('/api/open-settings') && req.method === 'POST') { const tgt = await resolveTarget();