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.
master
John Lauer
docs: first-run contract in the Rust plan (arav-rog findings), changelog 2.0.397-2.0.399, ship.sh publishes pkg beforeā¦
47808a0
2mo ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
// Gate: the consent registry (pup-bridge#92, ab SDK blocked_on_user rule). Pure, no ab, no OS.
// Proves: in-verb cap returns blocked_on_user (pending) while the toast keeps collecting;
// a re-call with the same key re-attaches (no second toast); the decision is handed over exactly
// once; a toast timeout retracts the toast (notify_dismiss) and records consent_timeout.
const assert = require('assert');
const { createRegistry, blockedOnUser } = require('../src/consent.js');
const calls = [];
let resolveToast = null;
const abCommand = (verb, args) => {
calls.push({ verb, args });
if (verb === 'notify_user') return new Promise((r) => { resolveToast = r; });
if (verb === 'notify_dismiss') return Promise.resolve({ dismissed: true });
return Promise.resolve({});
};
const reg = createRegistry({ abCommand, abPayload: (r) => r, inVerbMs: 40, totalSec: 120, pickupMs: 10000 });
(async () => {
const toast = { idPrefix: 'credreveal', title: 'Reveal your example.com login?', buttons: [{ label: 'Reveal' }, { label: 'Deny' }] };
// 1. first call: no decision within the in-verb cap -> pending
const a = await reg.ask({ key: 'pup_credential_reveal|example.com|thread-a', verb: 'pup_credential_reveal', what: 'Waiting for the user', toast });
assert.strictEqual(a.pending, true, 'first call is pending after the in-verb cap');
assert.ok(a.consentId.startsWith('credreveal-'), 'consent id carries the prefix');
assert.strictEqual(calls.filter((c) => c.verb === 'notify_user').length, 1, 'exactly one toast shown');
assert.strictEqual(calls[0].args.wait, true); assert.strictEqual(calls[0].args.timeoutSec, 120);
const rows1 = reg.rows();
assert.strictEqual(rows1.length, 1); assert.strictEqual(rows1[0].phase, 'consent'); assert.strictEqual(rows1[0].blockedOnUser, true);
// 2. re-call with the same key: re-attaches, still one toast
const b = await reg.ask({ key: 'pup_credential_reveal|example.com|thread-a', verb: 'pup_credential_reveal', what: 'Waiting for the user', toast });
assert.strictEqual(b.pending, true); assert.strictEqual(b.consentId, a.consentId, 'same consent id on re-attach'); assert.strictEqual(b.attached, 1);
assert.strictEqual(calls.filter((c) => c.verb === 'notify_user').length, 1, 'no second toast on re-attach');
// 3. the user clicks while nobody is attached: the row flips to consent_decided; the next
// re-call collects the decision exactly once and the record is gone afterwards
resolveToast({ action: 'Reveal' });
await new Promise((r) => setTimeout(r, 5));
const rows2 = reg.rows();
assert.strictEqual(rows2[0].phase, 'consent_decided'); assert.strictEqual(rows2[0].blockedOnUser, false);
const c = await reg.ask({ key: 'pup_credential_reveal|example.com|thread-a', verb: 'pup_credential_reveal', what: 'Waiting for the user', toast });
assert.strictEqual(c.pending, false); assert.strictEqual(c.decision, 'Reveal'); assert.strictEqual(c.consentId, a.consentId);
assert.strictEqual(reg.map.size, 0, 'picked-up record removed');
assert.strictEqual(calls.filter((c2) => c2.verb === 'notify_dismiss').length, 0, 'a clicked toast is not retracted');
// 4. a different key is a different toast
const d = await reg.ask({ key: 'pup_credential_reveal|other.com|thread-a', verb: 'pup_credential_reveal', what: 'Waiting for the user', toast });
assert.strictEqual(d.pending, true); assert.notStrictEqual(d.consentId, a.consentId);
assert.strictEqual(calls.filter((c2) => c2.verb === 'notify_user').length, 2);
// 5. toast timeout (ab resolves with no action): retract via notify_dismiss, decision null
resolveToast({});
await new Promise((r) => setTimeout(r, 5));
assert.strictEqual(calls.filter((c2) => c2.verb === 'notify_dismiss').length, 1, 'timed-out toast retracted');
const e = await reg.ask({ key: 'pup_credential_reveal|other.com|thread-a', verb: 'pup_credential_reveal', what: 'Waiting for the user', toast });
assert.strictEqual(e.pending, false); assert.strictEqual(e.decision, null, 'timeout hands over null (consent_timeout)');
// 6. the response shape
const shape = blockedOnUser({ stillRunning: true, blockedBy: 'click Reveal', _hint: 'wait' });
assert.deepStrictEqual(Object.keys(shape).sort(), ['_hint', 'blockedBy', 'errorCode', 'status', 'stillRunning', 'success'].sort());
assert.strictEqual(shape.errorCode, 'blocked_on_user'); assert.strictEqual(shape.success, false); assert.strictEqual(shape.stillRunning, true);
console.log('check_consent: OK');
})().catch((err) => { console.error('check_consent FAILED:', err && err.stack || err); process.exit(1); });