← Commit history

New pup_progress verb plus the pup-progress sub-skill: calling AI threads can now paint the Windows taskbar progress bar during long multi-stage work like site crawls and library harvesting, with indeterminate and percentage modes and a stage note on the hover tooltip; the open-window response and the verb catalog teach when it is warranted and when a quick page open should skip it, and the automatic activity strip yields while explicit progress is held.

John Lauer ·8cd7f9492b ·2mo ago ·parent e2a19d2
4 files changed +47−8
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "2.0.192",-  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.192/adom-bridge-puppeteer-v2.0.192.zip",-  "sha256": "f1219bc12407085f12358b386986e2e2505d9979c842fb7798e55c2614c3fb5b",-  "size": 923752,+  "version": "2.0.193",+  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.193/adom-bridge-puppeteer-v2.0.193.zip",+  "sha256": "77afb3717c6537b3e8a318ccd8fb543ccf6982b1b93514b74c9d36f194ac4587",+  "size": 925360,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-2.0.192+2.0.193
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "2.0.192",+  "version": "2.0.193",   "description": "Drives a browser ALREADY on the machine \u2014 installed Chrome, else Microsoft Edge (every Windows PC ships with Edge) \u2014 launched with a FRESH isolated profile, so the common case has NO ~150 MB Chrome-for-Testing download and it never touches the user's real tabs/logins. Binds its local server to LOOPBACK (127.0.0.1) only, so no Windows Firewall 'allow Node through the firewall' prompt ever pops. Chrome for Testing is fetched only as a LAST RESORT if the machine has no Chromium browser at all. SPAWN-VERIFIED fallback: pup tries each detected browser (Chrome \u2192 Edge \u2192 CfT) and actually launches it, falling through to the next if one won't start (and handling browsers like Edge that relaunch onto another PID), so a corrupt/partial install never hard-fails an open. The browser that launches is cached as the default (persisted across restarts) so it isn't re-probed each time. On spawn the bridge SELF-WARMS: detects browsers + disk, and only if the box has no Chrome/Edge and no cached CfT does it background-fetch CfT so the first open is instant. pup_use pins a specific browser (chrome | edge | cft | auto); {browser:'chrome',install:true} downloads & installs REAL Google Chrome (user-scoped, no admin) on an Edge-only box. pup_readiness (READ-ONLY) reports which browser will be used + the full candidate list + the default + free disk. Every failure returns a specific errorCode + _hint (chrome_install_no_disk, chrome_download_network, chrome_install_permission, chrome_stable_not_detected, \u2026). Launches with FULL browser permissions by default for full debug (strictPermissions:true opts out). Headful automation: open/close windows+tabs, navigate, screenshot, eval JS, record. pup is a GENERIC, non-profile browser only \u2014 every session is a fresh isolated Chrome/Edge process, never the user's real signed-in browser; profile defaults to sessionId. To drive the user's REAL Chrome/Edge (their profile, cookies, SSO), use the Adom browser EXTENSION's nbrowser_* verbs \u2014 a separate bridge, not pup's job. macOS (v1.9.74+): full parity with mac-native mechanics \u2014 background-by-default is the app-level hide (windows stay drivable/screenshottable while hidden), raise/lower map to unhide+activate/hide, pup_alert_window posts a native toast whose click focuses the window, pup_use {browser:'chrome',install:true} installs Chrome from the official dmg, and taskbar identity/badging remains Windows-only.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",
src/server.js+41−2
@@ -5105,6 +5105,9 @@ function markSessionBusy(sessionId) {   if (!sessions.has(sessionId)) return;   let st = _busyState.get(sessionId);   if (!st) { st = { busy: false, timer: null }; _busyState.set(sessionId, st); }+  // v2.0.193: while a caller holds EXPLICIT progress (pup_progress), the automatic busy strip stays+  // hands-off — otherwise its on/off toggling would fight the caller's crawl-stage bar.+  { const _es = sessions.get(sessionId); if (_es && _es._explicitProgress) return; }   if (!st.busy) {     st.busy = true;     console.log(`[activity] "${sessionId}" agent driving — taskbar progress ON`);@@ -8892,7 +8895,7 @@ async function enumAllPupChromeHwnds() { // one session. With sessionId omitted these resolve to the CALLER THREAD's own window (the same // ai-<thread-slug> keying pup_open_window uses) — never to activeSessionId, which is just whichever // window was touched last, usually another thread's.-const SESSION_TARGETING_VERBS = new Set(['pup_navigate', 'pup_reload', 'pup_back', 'pup_forward', 'pup_open_tab', 'pup_switch_tab', 'pup_close_tab', 'pup_close_window', 'pup_eval', 'pup_evaluate', 'pup_click', 'pup_type', 'pup_press_key', 'pup_input_dispatch', 'pup_screenshot', 'pup_screenshot_full_res', 'pup_errors', 'pup_list_tabs', 'pup_wait', 'pup_alert_window', 'pup_focus_window', 'pup_raise_os_window', 'pup_lower_os_window', 'pup_window_info', 'pup_record_start', 'pup_record_stop', 'pup_wiki_set_view', 'pup_switch_window', 'pup_annotate', 'pup_floaty_click', 'pup_move_to_workspace']);+const SESSION_TARGETING_VERBS = new Set(['pup_progress', 'pup_navigate', 'pup_reload', 'pup_back', 'pup_forward', 'pup_open_tab', 'pup_switch_tab', 'pup_close_tab', 'pup_close_window', 'pup_eval', 'pup_evaluate', 'pup_click', 'pup_type', 'pup_press_key', 'pup_input_dispatch', 'pup_screenshot', 'pup_screenshot_full_res', 'pup_errors', 'pup_list_tabs', 'pup_wait', 'pup_alert_window', 'pup_focus_window', 'pup_raise_os_window', 'pup_lower_os_window', 'pup_window_info', 'pup_record_start', 'pup_record_stop', 'pup_wiki_set_view', 'pup_switch_window', 'pup_annotate', 'pup_floaty_click', 'pup_move_to_workspace']); function threadDefaultSessionId(callerThread) {   return 'ai-' + String(callerThread).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40); }@@ -12718,6 +12721,7 @@ const server = http.createServer(async (req, res) => {             V('pup_navigate', 'Navigate the active tab to a URL.', { sessionId: 'required str', url: 'required str' }, 45, { sessionId: 'work', url: 'https://example.com' }),             V('pup_back', 'History back.', { sessionId: 'required str' }, 20, { sessionId: 'work' }),             V('pup_forward', 'History forward.', { sessionId: 'required str' }, 20, { sessionId: 'work' }),+            V('pup_progress', 'Paint the Windows taskbar PROGRESS BAR on your window during a LONG multi-stage task (crawling a site, login->search->download loops, batch harvesting datasheets/STEP files) so the user sees work happening at a glance. mode:"indeterminate" (pulsing) to start, mode:"value" with value:0-100 as stages complete, mode:"off" when done or aborted (always clear!). Optional note:"12/40 datasheets" shows on the button hover tooltip. YOUR judgment: a single quick page open does NOT warrant a progress bar. See the pup-progress skill.', { mode: '"indeterminate" | "value" | "off"', value: 'optional 0-100 (mode:"value")', note: 'optional short stage label for the hover tooltip', sessionId: 'optional — defaults to your thread\'s own window' }, 8, {}),             V('pup_reload', 'Reload the active tab.', { sessionId: 'required str' }, 30, { sessionId: 'work' }),             V('pup_click', 'Click an element (by selector or coords). Selector clicks LOCATE the element once then click by its center coordinates (fast on huge/animated DOMs, where page.click used to hit the 15s wall). Response includes method. If a React toggle/like/heart does NOT change state after a normal click, that control ignores trusted clicks — retry with domClick:true to fire a synthetic element.click().', { sessionId: 'required str', selector: 'optional str', x: 'optional int', y: 'optional int', domClick: 'optional bool — dispatch a synthetic element.click() instead of a trusted CDP click; for React controls that ignore trusted clicks', clickCount: 'optional int 1-3', button: 'optional "left"|"right"|"middle"' }, 20, { sessionId: 'work', selector: 'button#go' }),             V('pup_assisted_click', 'RESPECTFUL real-cursor click for a surface pup CDP cannot drive (a NATIVE OS dialog, or a cross-origin FedCM "Sign in with Google" account chooser that rejects every in-page click). Do NOT hand-roll raise+desktop_click for these — use THIS. It: captions the reason + toasts BEFORE touching the screen; foregrounds; then WAITS, detecting if the user alt-tabbed away and holding (captioning "Adom is waiting — click back when ready") until the user is actually on the window; then runs the TIGHT real-cursor hover->click that FedCM accepts (interleaving any screenshot/eval between raise and click makes the click silently no-op); captions each step. Times out as user_not_ready clicking NOTHING if they never return. First take a desktop_screenshot_window of the session to read the target\'s WINDOW-pixel x,y.', { sessionId: 'required str', x: 'required int — WINDOW-image pixel of the target (from desktop_screenshot_window)', y: 'required int', reason: 'required str — WHY you must click (shown to the user as a caption)', label: 'required str — WHAT you are clicking (e.g. "your Google account")', maxWaitMs: 'optional int — how long to wait for the user (default 120000)' }, 130, { sessionId: 'adsk', x: 900, y: 379, reason: 'clicking your Google account to finish signing into Autodesk', label: 'your Google account' }),@@ -13284,7 +13288,7 @@ const server = http.createServer(async (req, res) => {           const fgDenyHint = fgDenied ? ' ⛔ FOREGROUND DENIED: you passed foreground:true WITHOUT a foregroundReason, so the window opened in the BACKGROUND (the open itself succeeded). Foregrounding steals the user\'s screen+keyboard and is allowed ONLY when the USER explicitly asked to SEE this window. If they did: re-call with foreground:true + foregroundReason:"<quote/paraphrase what the user said>" (min 10 chars — it is recorded and auditable). If they did not: leave it background; pup_alert_window is the correct "it\'s ready" signal.' : '';           const bgHint = foreground             ? 'Opened in the FOREGROUND as EXPLICITLY requested (foreground/raise/show:true). Return it to the background with pup_lower_os_window once the user is done watching.'-            : `Opened in the BACKGROUND (behind the user's windows, did NOT steal focus)${sized ? ' — and placed at the requested size/position WITHOUT foregrounding, so framing it for a screenshot never disturbs the user' : ''}. This is the HARD default so pup never disrupts the user's main work. It is FULLY drivable here: pup_screenshot / pup_eval / pup_navigate all work while it stays hidden. ONLY bring it to the user's screen if they EXPLICITLY want to WATCH (a demo, a recording, a "show me") — pass foreground:true on open (or call pup_raise_os_window). Sizing alone does NOT foreground anymore.`;+            : `Opened in the BACKGROUND (behind the user's windows, did NOT steal focus)${sized ? ' — and placed at the requested size/position WITHOUT foregrounding, so framing it for a screenshot never disturbs the user' : ''}. This is the HARD default so pup never disrupts the user's main work. It is FULLY drivable here: pup_screenshot / pup_eval / pup_navigate all work while it stays hidden. ONLY bring it to the user's screen if they EXPLICITLY want to WATCH (a demo, a recording, a "show me") — pass foreground:true on open (or call pup_raise_os_window). Sizing alone does NOT foreground anymore. LONG MULTI-STAGE TASK in this window (crawling a site, login->search->download loops, batch harvesting)? Paint the taskbar progress bar so the user sees the work: pup_progress {mode:\"indeterminate\", note:\"crawling ti.com\"} to start, {mode:\"value\", value:40, note:\"12/40 datasheets\"} as stages advance, {mode:\"off\"} when done. A single quick page open does NOT need it — your call. Full guidance: the pup-progress skill.`;           // v1.8.16: ownership/reuse verdict — reprimand grabs, bless refreshes.           let ownHint = '';           if (reuse && reuse.navigatedAway) {@@ -16386,6 +16390,41 @@ const server = http.createServer(async (req, res) => {           sendJSON(res, { success: true, output: JSON.stringify({ errors: errs, count: errs.length, sessionId: resolvedId, tabId }) });           return;         }+        case 'pup_progress': {+          // v2.0.193 (John: "if a thread is crawling a site through login -> component pages ->+          // datasheets -> step files, it should paint the taskbar progress bar; a simple page open+          // shouldn't — the AI decides, but pup has to TEACH it"). Caller-facing progress control.+          //   pup_progress {mode:"indeterminate", note:"crawling ti.com"}          — pulsing bar+          //   pup_progress {mode:"value", value:35, note:"12/40 datasheets"}      — determinate+          //   pup_progress {mode:"off"}                                            — clear when done+          const _pm = String(args.mode || (typeof args.value === 'number' ? 'value' : 'indeterminate'));+          if (!['indeterminate', 'value', 'off'].includes(_pm)) { sendJSON(res, { success: false, error: 'mode must be indeterminate|value|off' }); return; }+          const _pv = Math.max(0, Math.min(100, Number(args.value) || 0));+          const _note = (typeof args.note === 'string' && args.note.trim()) ? args.note.trim().slice(0, 120) : null;+          let session, resolvedId;+          try { ({ session, resolvedId } = await recoverOrRelaunchSession(args)); }+          catch (e) { sendJSON(res, { success: false, errorCode: 'no_session', error: 'no window to set progress on', _hint: 'Open your window first (pup_open_window), then pup_progress.' }); return; }+          if (!session) { sendJSON(res, { success: false, errorCode: 'no_session', error: 'no window to set progress on', _hint: 'Open your window first (pup_open_window), then pup_progress.' }); return; }+          session._explicitProgress = _pm !== 'off';+          if (_pm !== 'off') { if (!session._progressSince) { session._progressSince = Date.now(); session._progressCount = (session._progressCount || 0) + 1; } }+          else if (session._progressSince) { session._progressMs = (session._progressMs || 0) + (Date.now() - session._progressSince); session._progressSince = null; }+          if (process.platform === 'win32' && !minimalTouch()) {+            try {+              await chrome.abCommand('desktop_taskbar', {+                ...(await windowTarget(session, resolvedId)),+                progress: _pm === 'off' ? { state: 'none' } : _pm === 'value' ? { state: 'normal', value: _pv } : { state: 'indeterminate' },+              }, { timeoutMs: 4000 });+              try { session._lastPaintSig = null; session._appOverlay = false; session._overlayEnforceAt = 0; } catch (e) {}+              if (_note) { try { await chrome.abCommand('desktop_taskbar', { ...(await windowTarget(session, resolvedId)), thumbnailTooltip: `${session.owner || resolvedId} — ${_note}` }, { timeoutMs: 4000 }); } catch (e) {}+              }+            } catch (e) {}+          }+          sendJSON(res, { success: true, output: JSON.stringify({ ok: true, sessionId: resolvedId, mode: _pm, ...(_pm === 'value' ? { value: _pv } : {}), ...(_note ? { note: _note } : {}),+            _hint: _pm === 'off'+              ? 'Progress bar cleared. Good hygiene: always clear when the multi-stage task finishes or aborts.'+              : `Taskbar progress ${_pm === 'value' ? `at ${_pv}%` : 'pulsing (indeterminate)'} on "${resolvedId}"${_note ? ` — hover tooltip says "${_note}"` : ''}. Update value/note as stages advance; pup_progress {mode:"off"} when done. The automatic activity strip stays out of your way while this is set.` }) });+          return;+        }         case 'pup_reload': {           let session, resolvedId;           // v1.6.1+: auto-recover (reconnect or relaunch) before surfacing