app
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.
Never-red overlays, clear-flashes that truly clears, fg/bg buttons, full-coverage ralph (four John asks in one build). (1) RED LEDS GALORE root cause: the overlay enforcer added in 1.9.236 sat inside the health interval whose FIRST LINE is 'if (minimalTouch()) return' - minimal-touch is the normal mode, so the enforcer never ran once; after every bridge restart all windows sat bare until some verb happened to repaint. Enforcement now has its OWN ungated 10s interval (overlays are explicitly allowed in minimal-touch, v1.9.191), and rescanProfile repaints every reattached session ~1.5s after a restart - so model==glass within seconds and a window is never knowingly bare longer than ~10s. (2) CLEAR-FLASHES: the 1.9.242 hwnd tracking was per-boot, so flashes started before a bridge restart (every ship) were invisible to the sweep - exactly what John hit twice. clear-all now ENUMERATES every visible top-level window of every pup-managed Chrome process (same PS enum as the badge resolver, filtered to pup's own PIDs so the user's personal Chrome is untouched) and stops each one - no bookkeeping to lose; response reports the swept count. (3) Every window row gets [bring to fg]/[send to bg] and every tab row gets [fg]/[bg] - implemented as loopback self-calls into the REAL raise/lower verbs so every safety behaviour applies unchanged (on-screen caption with the reason, standard reveal geometry, badge re-assert after reveal); a dashboard click is the user acting, which satisfies the foreground gate by definition. (4) The ralph suite now drives EVERY dashboard button and asserts EFFECTS: fg/bg assert the foreground flag + active tab actually changed; flash/clear assert ON GLASS via taskbar screenshots (attention-pixel count must rise on flash-all and fall back after clear) plus the enumerated sweep count; close-all asserts 0 windows remain; collapse/jump/filter widgets asserted in-DOM.
John Lauer
·
ed1f7c2
·
10d ago
Publish 1.9.242
John Lauer
·
b82d3bf
·
10d ago
Clear-flashes actually clears now + favicon cache-bust (John: 'i just hit clear flashes and it didn't clear all your flashes. have you truly done full ralph testing of yourself?'). Honest answer was no - the ralph test read ok:true from the flash actions but never verified the taskbar effect. ROOT CAUSE, same disease as the badge saga: flashViaAD addressed windows by TITLE tag, so a stop silently no-oped on any window whose title lacked the tag (PDF viewer, retitled pages) - and FlashWindowEx keeps a button orange until a FLASHW_STOP reaches that exact HWND, so those stayed lit forever. Fixes: (1) flashViaAD resolves the session's HWND (same resolver as the badge paint) and addresses flash/stop by hwnd, title only as fallback; (2) pup REMEMBERS every hwnd it ever flashed this boot (_flashedHwnds) and clear-flashes sweeps a stop to ALL of them, catching windows whose session closed or retitled after flashing; (3) the ralph test now asserts the EFFECT: flashedPending (exposed in the snapshot) must drop to 0 after unflash-all, not just ok:true. Also: the dashboard favicon link is now versioned (?v=BRIDGE_VERSION) - the new adom-ui-design favicon shipped in 1.9.239 but Chrome's favicon cache kept showing the old two-dots tile because the URL never changed; every ship now busts it.
John Lauer
·
ca8f72d
·
10d ago
Publish 1.9.241
John Lauer
·
8ba2b0e
·
11d ago
Fix the real cause of every favicon falling to generic: analyzeIconBytes referenced 'sharpMod', but that name is NOT a module global - sharp is required LOCALLY inside each function that needs it (applyAppOverlay defines its own 'let sharpMod' at use site; the module-level binding is named 'sharp'). So analyzeIconBytes threw a ReferenceError on the first sharp call, its try/catch marked the icon 'unreadable', resolveFaviconRich returned null, and EVERY window fell back to the generic glyph (github, wiki, the dashboard's own icon - all showed generic despite having real favicons). Fix: require('sharp') locally inside analyzeIconBytes like the sibling functions do. This is the exact class of bug the restructure plan calls out - the same capability (rasterize) re-implemented in N places with N local requires instead of one shared module. Combined with the 1.9.240 rasterize-before-stats fix, viewBox-only SVGs and normal PNG/ICO favicons now resolve for real.
John Lauer
·
d1caf7a
·
11d ago
Publish 1.9.240
John Lauer
·
10ca55e
·
11d ago
Fix: SVG favicons with only a viewBox (no width/height) were wrongly judged unreadable. The new analyzeIconBytes called sharp(bytes).stats() on the raw SVG, but such an SVG has no intrinsic size so sharp throws 'unsupported image' - the old paint path always .resize()d first, which masked it. Effect: the dashboard's OWN icon.svg (and any viewBox-only SVG favicon) resolved to 'none', its badge+favicon 404'd every SSE tick (52 console errors), and the overlay LED read red/MISMATCH. Fix: rasterize at a fixed 48px with high density FIRST, then measure transparency/luminance on the guaranteed-decodable PNG.
John Lauer
·
a1cb047
·
11d ago
Publish 1.9.239
John Lauer
·
6e52aa5
·
11d ago
Fix: dashboard script broke in the browser (SNAP undefined, ralph caught it). favTags used .join('\\n'), but inside the DASHBOARD_HTML template literal a backslash-n evaluates to a REAL newline, so the served page got .join('<newline>') — an unterminated string that killed the whole inline script (page rendered its static shell only). node --check passed because it sees the raw source, not the evaluated template. Fix: use String.fromCharCode(10) for the tooltip newline. Also added tests/check_dashboard_script.js which EVALUATES the template and parses the served <script> exactly as the browser receives it, closing the gap that let this ship — the prior check parsed raw source and could not see template-escape surprises.
John Lauer
·
7a4cf85
·
11d ago
Publish 1.9.238
John Lauer
·
ce0539c
·
11d ago
Favicon acquisition rebuilt into a real multi-state resolver + per-tab state in the dashboard (John: 'favicons these days can be svg or png, dark mode or light mode, transparent... you seem to not be doing much logic there... create all of the states of how to get a favicon... and place that info with each tab's info row... then determine which favicon to retrieve to have it show the best/nicest in the windows taskbar, light vs dark vs transparent'). NEW resolveFaviconRich: (1) enumerates EVERY declared icon with its type/sizes/media/rel (link rel=icon/shortcut/apple-touch/mask-icon + synthetic /favicon.ico); (2) SCORES for a DARK taskbar - a media='(prefers-color-scheme: dark)' variant is the art a site designed to sit on dark so it wins (+100); a light-mode variant is usually dark ink that vanishes on the taskbar so it loses (-40); then SVG > sized-PNG > apple-touch > ICO for crispness; mask-icon deprioritised; (3) fetches best-first with the page's cookies (authed icons), falling back to bridge fetch then disk; (4) rasterizes via sharp and MEASURES transparency (stats.isOpaque) + luminance, and if the top pick is transparent AND dark-dominant (would vanish on the dark taskbar) it keeps scanning for a lighter sibling before settling. Full acquisition STATE recorded per resolution: format (SVG/PNG/ICO/JPEG), color-scheme mode (dark/light/default), transparent+darkDominant+luminance, kind (page-favicon / sibling-tab / favicon.ico / app-provided / generic-web / generic-pdf / none), how it was fetched, and candidate count. The dashboard now shows this as compact chips on EACH TAB's row (kind, format, x-mode, transparent/opaque) with a full tooltip, so it's visible per tab exactly how every favicon was obtained - including whether a generic web or generic PDF icon had to be synthesized because none truly existed. The active tab's live overlay state feeds its row; other tabs' state comes from the rich resolver's per-URL cache populated by the dashboard favicon fetch.
John Lauer
·
3742e88
·
11d ago
Publish 1.9.237
John Lauer
·
5c6e4b7
·
11d ago
Overlay correctness + LED match indicator + real pup favicon (John's audit). FOUR fixes: (1) Ripped out the dead AUMID code I wrongly added in 1.9.235/236 - we stopped using AUMID; minimal-touch stamps none (verified zero Adom.Pup* registry keys, every session curAppId=null), so grouping is now purely one taskbar button per Chrome PROCESS with that browser exe's real icon as the base, no phantom AUMID branch. (2) THE favicon bug John kept hitting: applyAppOverlay set _appOverlay=true even when it fell to the GENERIC icon (favicon fetch timed out, e.g. during a post-restart enforcement paint), and never retried - so a window that could show github's real favicon stayed stuck on the generic Adom mark forever. Now the badge tracks generic-vs-real (_overlayIsGeneric), and the 15s health sweep UPGRADES a stuck-generic badge to the real page favicon the moment it becomes fetchable (90s cadence), in addition to healing bare windows. (3) LED indicator (John: 'put an led indicator indicating the check for overlay icons match came back green vs red'): every window, every taskbar button, and a header rollup now show a green/amber/red LED - green = real page-favicon overlay painted and the exact bytes are on disk (dashboard badge == what Windows was handed), amber = painted but legit generic fallback (page truly has no favicon), red = bare/unpainted (taskbar would be wrong). The strip badge now mirrors the FOREGROUND window's overlay (what Windows actually fronts) else newest-painted, and says which. (4) Real pup dashboard favicon designed against adom-ui-design (exact brand tokens accent #00b8b1 / on-accent #05221f / bg #0d1117 / border #30363d): two overlapping browser-window cards with an SSE heartbeat pulse = live windows monitoring, distinct from the plain Adom leaf mark, crisp at 16px.
John Lauer
·
c8b9834
·
11d ago
Publish 1.9.236
John Lauer
·
4b4e6c8
·
11d ago
OVERLAYS ARE NOW ENFORCED, not fire-and-forget (John: 'the whole point here is to get you to finally create overlay icons correctly cuz you still aren't doing it right. the fact that you still seem to not ensure there are overlay icons is infuriating'). Root causes of bare taskbar buttons: an overlay paint that failed transiently (window not yet resolvable by hwnd, favicon not loaded, AD busy) was NEVER retried, and a bridge restart wiped the painted state, so windows that once wore a badge sat bare afterwards. The 15s health sweep now re-applies the overlay to ANY session not currently wearing one, with a 60s per-session cooldown, and logs each enforcement. Also: the dashboard strip's window-count badge moved to BOTTOM-LEFT because it was sitting exactly on the top-right overlay corner, covering the very thing the mimic exists to show; and _curAppId (a window's stamped AUMID) is now persisted in the session file and restored on recovery, because it drives real-taskbar grouping (a stamped window keeps its OWN button until close) and losing it across restarts made the dashboard mimic diverge from the real taskbar.
John Lauer
·
0869863
·
11d ago
Publish 1.9.235
John Lauer
·
f58a56e
·
11d ago
Taskbar mimic made FAITHFUL (John's on-glass audit: 'i want to see the full icon you're showing in the taskbar, i.e. the chrome icon and then exactly where the overlay is placed. and right now your icons in the dashboard are inconsistent with what's in the taskbar'). The audit against a real taskbar screenshot found three lies: the strip blew the overlay badge up as the whole button instead of showing the base icon with the badge in its corner; the grouping collapsed AUMID-stamped windows (which keep their OWN real buttons with the teal category tile until closed, even under minimal-touch) into the process bucket, so the dashboard showed fewer buttons than the taskbar; and the badge shown was wins[0]'s dump rather than what the button actually wears. Fixes: (1) each strip button now renders the REAL base icon - extracted from the browser exe that owns the profile process via PowerShell ExtractAssociatedIcon and cached, so Edge shows Edge - with the painted overlay at TOP-RIGHT at ~55%, the placement measured from his actual taskbar, and NO overlay when none is painted, because a bare Chrome button must render bare; (2) grouping honours a still-stamped _curAppId as its own button with its category tile (served from the shipped pup-cat icos via the existing ico extractor), only unstamped windows share the browser-process button; (3) the badge shown is the NEWEST actually-painted overlay in the group, mirroring last-painter-wins on the real button. Tree group headers use the same base+overlay stack.
John Lauer
·
b628674
·
11d ago
Publish 1.9.234
John Lauer
·
f967f2c
·
11d ago
Dashboard rebuilt as the three-level TREE John specified: a mimic of the Windows taskbar at the top (one button per real taskbar icon in real order, each wearing its real overlay badge, click to jump), then a collapsible tree of the window(s) under each taskbar icon, then the tabs under each window as leaf nodes. Icons correct at every level: taskbar button and window show the composited overlay badge, each tab shows its own favicon fetched through the page. Per-node actions (flash/clear/close on windows, close on tabs, group flash/clear on taskbar nodes), the overlay-decision trace per window, foreground reason, highFps, viewport pin, crash and owner tags, and the thread/text filters carry over. Replaces the flat card list. The whole DASHBOARD_HTML block was rewritten cleanly rather than patched, since prior partial edits plus a linter pass had tangled it; both server.js and the extracted dashboard script were parse-checked before shipping.
John Lauer
·
d2af6f0
·
11d ago
Add dashboard ralph test: drives every dashboard action end to end (page script executes, SSE emits, icon serves, taskbar rows never exceed chrome processes, close-tab/close-window/flash/flash-all all take real effect) and asserts each result via browser_list_windows. This is the test that was missing across roughly 30 broken dashboard ships; 8/8 passing on 1.9.233.
John Lauer
·
ccf283d
·
11d ago
Publish 1.9.233
John Lauer
·
c6850aa
·
11d ago
Dashboard: strip em-dashes from the UI (John caught them, standing rule), fix the flash-all button, and fix taskbar-icon grouping to match the real taskbar. EM-DASHES removed from 7 user-visible strings (title, header, ago-fallback, url/tab fallbacks, overlay-reason line). FLASH: flashViaAD early-returned under minimalTouch and silently swallowed the dashboard buttons too; a human clicking flash is user intent, not automatic machinery, so dashboard actions now pass force:true to bypass the gate while automatic flashing stays suppressed. GROUPING: under minimalTouch pup stamps no per-window AUMID so Windows groups unbranded windows by their Chrome PROCESS (one button per profile); the dashboard was keying by hwnd/session and inventing a row per window, so it showed 3 rows when the taskbar had 2 icons. Now keyed by Chrome process, matching the real taskbar. The earlier dollar-sign identifier collision and backtick-in-comment syntax errors that kept the page from running are confirmed resolved: the served script now parses clean.
John Lauer
·
45cd4b4
·
11d ago
Publish 1.9.232
John Lauer
·
0444c42
·
11d ago
Dashboard finally renders: fix the inline onerror that killed the script. Every img used onerror=this.style.visibility=SINGLEQUOTE hidden SINGLEQUOTE inside a single-quoted JS string, so the inner quotes closed the string early and produced Uncaught SyntaxError missing paren after argument list at the served line 30 col 130 (found by appending the served script to the live DOM and reading window.onerror, after browser_errors showed zero because the script never RAN to throw a catchable error). This is why the page rendered its static shell through seven ships while the SSE backend was healthy all along. Replaced all three inline handlers with a data-fallback=hide attribute plus ONE delegated error listener, same pattern as the click delegation, so no inline handler can ever put quotes-in-quotes again. Note for future: node --check and even new Function on the extracted template CANNOT catch this because template-literal unescaping differs from what the HTTP server emits byte-for-byte; the only reliable check is loading the served page in a real browser.
John Lauer
·
97ecaff
·
11d ago
Publish 1.9.231
John Lauer
·
70ce588
·
11d ago
Dashboard gets its own icon and its own window (John). ICON: the dashboard is an Adom app, so per adom-ui-design it serves its OWN icon at /dashboard/icon.svg rather than inheriting Chrome's, a clean 32x32 Adom-teal pup mark that reads at 16px in a taskbar overlay. Served as a real asset rather than a data URI so pup's favicon pipeline fetches it exactly like any other app icon, which means the dashboard window's taskbar badge is produced by the very code path the dashboard exists to display. OWN WINDOW: new browser_dashboard verb always opens the dashboard in its own window on its own durable profile, so it carries its own taskbar identity and can be found and closed independently; a fixed sessionId means repeat calls refresh the single window instead of spawning duplicates. Backgrounded by default like any pup window, because a monitor must not steal the screen.
John Lauer
·
46ecf3d
·
11d ago
Publish 1.9.230
John Lauer
·
d9ff501
·
11d ago
Dashboard finally renders: pup was breaking its own page. The dashboard declared a global named dollar-sign for its query helper, but pup INJECTS window.dollar-sign (its deep shadow-DOM helper) into every page it drives, and a const colliding with an existing global is a parse-time SyntaxError that kills the entire script. The page therefore rendered its static shell and nothing else, while the SSE stream was healthy the whole time (verified by curling the endpoint from the desktop and getting live window data immediately). Renamed the helper to avoid the collision. Also added a pre-ship check that extracts the dashboard script out of the template literal and parses it, because node --check validates only the outer file and cannot see inside a template string, which is why several ships passed their syntax check while serving a dead page.
John Lauer
·
b3726de
·
11d ago
Publish 1.9.229
John Lauer
·
ad18257
·
11d ago
Dashboard SSE: report snapshot failures instead of swallowing them. The stream caught any error from dashboardSnapshot(), silently stopped, and left the page stuck on connecting forever with no way to see why. That is precisely the failure-suppression pattern the restructure plan documents (525 catch blocks, many hiding real errors), reproduced by me in brand-new code written the same day. The error is now logged bridge-side and streamed to the client, which renders it on the page, and headers are flushed so the stream opens immediately.
John Lauer
·
e7f2211
·
11d ago
Publish 1.9.228
John Lauer
·
807a28e
·
11d ago
Document the foreground-caption rule in the pup skill and the maintainer skill (John). Foregrounding is a rude operation: it takes over the screen of a user who runs many AI threads and alt-tabs straight back to their own work, which is why pup forces a foregroundReason before granting a raise. That reason must be SHOWN, not merely recorded, because an audit trail nobody sees is not accountability. Rule as written: every granted foreground flashes an on-screen caption naming the session and quoting the caller reason, for 2 seconds maximum, and it is deliberately NOT gated behind minimalTouch because it is a user-protection signal rather than cosmetic machinery. Any NEW path that foregrounds a window must call captionForegroundReason.
John Lauer
·
3aaa587
·
11d ago
Publish 1.9.227
John Lauer
·
96c0ece
·
11d ago
Fix the dashboard's dead script. The action buttons added in 1.9.226 inlined onclick handlers INSIDE the page's template literal, and the nested quote-escaping produced a JavaScript syntax error — so the script never executed, SNAP was never defined, and the page rendered its static shell with zero data while the backend was working perfectly. Replaced every inline handler with data attributes plus ONE delegated click listener, which removes the nested-escaping layer entirely so this class of bug cannot recur. Also added a pre-ship check that extracts DASHBOARD_HTML, pulls out its <script>, and parses it with new Function() — node --check only validates the outer file and cannot see inside a template literal, which is exactly why four consecutive ships passed their syntax check while shipping a broken page.
John Lauer
·
34913f3
·
11d ago
Publish 1.9.226
John Lauer
·
cfdaff2
·
11d ago
Dashboard: actions + overlay reasoning (John). ACTIONS — close all windows, close an individual window, close an individual tab, flash a taskbar button orange or clear it (per button, per window, and flash-all/clear-all). All routed through a POST /dashboard/action endpoint that reuses the same internals the verbs use, so the dashboard can never drift from verb behaviour; closing works by closing the session's tabs, which is exactly what browser_close_window does, rather than inventing a second close path. OVERLAY REASONING — every window now records and displays HOW its taskbar icon was decided: which tab was picked and that it was picked by asking Chrome (visibilityState) rather than pup's own bookkeeping, the favicon source URL, whether it came from the cookie-bearing page fetch, the bridge fetch, an inline data URI or a generic fallback, how it was composed, and critically whether it was painted by resolved HWND or by fragile title matching — the last one being the exact distinction behind a full day of unpaintable PDF-window badges.
John Lauer
·
bb300ec
·
11d ago
Publish 1.9.225
John Lauer
·
92eca04
·
11d ago
Dashboard hierarchy (John): render TASKBAR BUTTON -> window(s) -> tabs, with a favicon on every tab. Which windows share a taskbar button now depends on the real rule — taskbarGrouping 'split' gives each window its own AUMID and therefore its own button, 'grouped' stacks them under one Adom Pup button, and under minimalTouch pup stamps no identity so they are plain Chrome buttons — and the dashboard models that explicitly. This is also diagnostic: two sessions sharing one hwnd (the duplicate-session bug that split one PDF window's five tabs across five sessions) now show as two rows under a SINGLE button instead of looking like two unrelated windows. Each tab shows its own favicon via a new /dashboard/favicon endpoint, fetched through the page so cookie-protected app icons resolve, cached by tab URL so repeated SSE renders never re-fetch, and capped so the cache cannot grow unbounded.
John Lauer
·
14bb89d
·
11d ago
Publish 1.9.224
John Lauer
·
cb6cbd0
·
11d ago
Live pup management dashboard at /dashboard (John's ask), served by the bridge's own HTTP server with SSE. Shows every window pup is driving right now: its ACTUAL composed overlay badge served inline from the debug dump (not a re-render, so what you see is what Windows was handed), full tab list with the active tab and any crashed ones marked, the owning AI thread plus whether that ownership is declared or authenticated from caller identity, foreground vs background WITH the justification the caller had to give to foreground it, highFps lease and minutes remaining, uptime, resolved hwnd (or 'unresolved' in red, which is the signature of the title-vs-handle class of bug), pinned emulated viewport, page error counts and lost-browser state. Filter by AI thread or free-text across session/url. Updates every 2s over SSE. Built from the SAME in-memory registries the verbs use with NO CDP calls, so opening the dashboard can never wake a throttled page or add load to the shared socket — it reports what pup BELIEVES, which is exactly what you want when debugging pup, because a divergence between the dashboard and the screen IS the bug. Single self-contained document, no CDN or build step, so it works on a machine with no network.
John Lauer
·
19d457e
·
11d ago
Restructure step 0: characterization suite. Locks the CURRENT observable behaviour of pup's verbs (21 cases: read-only verbs, the open-drive-close lifecycle, and four refusal paths that must keep refusing with the same errorCode) so that splitting the 3,601-line handler into verbs/ modules surfaces any behavioural drift as a diff instead of as a user-reported bug days later. Compares response SHAPE and invariants, not volatile values — timings, ids and paths are normalised, as are the advisory fields pup emits intermittently by design (_reportIssues, _cleanupReport, occasional hints), because a suite with false positives gets ignored and is worse than no suite. Verified in both directions: clean run reports drift 0, and an injected baseline change IS caught.
John Lauer
·
393200d
·
11d ago
Add RESTRUCTURE-PLAN.md: measured architectural assessment + staged migration plan. Data: 12,485-line server.js with a 3,601-line HTTP handler holding all 58 verbs inline, 525 catch blocks (failure suppression, not robustness), 98 uncoordinated timers, 19 ad-hoc createCDPSession sites with NO pipeline, and duplicate subsystems (6 park + 6 badge + 6 recovery + 4 identity functions) which is the measured cause of the fixed-it-in-one-spot-missed-another failures. Concludes AGAINST a Rust rewrite: every failure this week was structural (stale cache key, two competing code paths, window resolved by title not handle, un-detached CDP session) and would reproduce in any language, while the file holds hard-won correct knowledge (reparent survival, FedCM click sequence, park geometry, occlusion behaviour) a rewrite would risk. Proposes extracting core/ (cdp pipeline, sessions, identity, placement, taskbar, recovery, respond) + verbs/ modules behind the unchanged verb contract, in 7 independently-shippable steps, with each mapped to the specific open wiki issues it resolves.
John Lauer
·
3189cf3
·
11d ago
Publish 1.9.223
John Lauer
·
75e0501
·
11d ago
Issue #22 follow-through: add ownerSource to browser_status and browser_list_windows ('declared' when the caller passed owner, 'caller-identity' when pup derived it from AD's authenticated X-Adom-Caller-* headers, null when unowned). The 1.9.222 edit that was meant to include this silently did not apply. With ownership now defaulting to the authenticated caller, an agent needs to be able to tell a self-asserted owner from an authenticated one before deciding whether to respect it.
John Lauer
·
3276a45
·
11d ago
Issue #22 (AdityaAngajala) — session ownership is no longer opt-in. His Finding 1: owner was an optional self-asserted string, so the one real session on his machine, driven actively for 43 minutes by another agent, reported owner:null and had no protection at all; the guard that protects a busy window was opt-in and the real session had opted out by omission. The issue named the blocker as 'relayed commands carry no caller identity, so owner can only ever be a self-asserted string' — that blocker is gone: AD 1.9.180+ sends X-Adom-Caller-* and pup already binds _ownerThread from it. Now the advisory owner field is backed by that AUTHENTICATED identity whenever the caller did not declare one, both at session creation and on any later verb, so a session is owned from birth instead of when someone remembers to ask. Adds ownerSource ('declared' vs 'caller-identity') to browser_status and browser_list_windows so an agent can tell a self-asserted owner from an authenticated one. The cold-start double-claim race in his Finding 2 is NOT addressed here and the issue stays open for it.
John Lauer
·
8fef9eb
·
11d ago
Publish 1.9.221
John Lauer
·
922ebb7
·
11d ago
Captions are hard-capped at 2 seconds (John's standing rule, caught live). pupCaption defaulted to 8s and callers were passing 5s, 6s and even 15s — text parked over the user's screen long after it had been read. The cap is enforced at the single choke point (PUP_CAPTION_MAX_MS = 2000), so no caller can exceed it regardless of what it requests. Recorded as a standing rule in the pup skill and the maintainer skill: a caption is a glance, not a dialog; if something genuinely needs longer attention it belongs in a toast (notify_user, which the user dismisses) or in the verb response, not parked on their screen.
John Lauer
·
62db5f4
·
11d ago
Publish 1.9.220
John Lauer
·
9a581c5
·
11d ago
Issue #5 (John, 2026-07-06) — browser_raise_os_window now resolves the window by the TRACKED HANDLE instead of matching the title suffix. His original point: right after a navigation the new page sets its own title BEFORE the bridge re-appends '(session: <id>)', so the lookup fails; and it turns out Chrome's PDF viewer never carries the suffix at all, which is what made PDF windows unraisable and their taskbar buttons unpaintable — a full day was spent rediscovering that this week. sessionHwnd() now prefers the handle pup stores at park, falls back to geometry-matching against the profile's Chrome PID (v1.9.216), and only then to a title lookup, so raise targets the same window regardless of what its title currently says.
John Lauer
·
ed77f46
·
11d ago
Publish 1.9.219
John Lauer
·
0b71616
·
11d ago