← Commit history

describe catalog + install hint: mac-accurate

Kyle Bergstedt ·8024ba57ab ·3mo ago ·parent 934a861
2 files changed +33−4
CHANGELOG.md+22
@@ -2,6 +2,28 @@  ## UNRELEASED (rides the next version) — AD-base re-discovery + self-guarded powershell primitives +### The RUNTIME describe catalog and hints still said "taskbar" after the docs were fixed++KICAD's finding, reproduced in pup: after fixing the shipped SKILL.md entries I had not touched+`browser_describe` or the runtime `_hint`s — **the surface the SDK's first principle says the AI+actually reads.** AD relays these verbatim, so they outrank the skill file they contradicted.++- `browser_alert_window`'s describe entry read *"Flash the taskbar icon"*. Now states both forms+  (Windows: taskbar flash; macOS: unhide at z-position + an AD toast whose click focuses), matching+  the implementation that has been correct since the port.+- The Chrome-install hint was entirely Windows-shaped — UAC, `awaiting_uac`, "click YES on the UAC".+  On macOS the install is `hdiutil` + `ditto` into `/Applications` with **no elevation step and no+  `awaiting_uac` phase at all**, so a Mac AI would poll for a phase that never arrives and tell the+  user to approve a prompt that never appears. Now branches on `process.platform`.+- `browser_wiki_set_view` (the jump-list callback, which cannot fire on macOS) and+  `browser_configure` are scoped, the latter pointing at the `platform` / `taskbarSupported` fields+  so a caller branches on data rather than parsing prose.++The generalisable bit is KICAD's: *"the other place"* is not a memorisable list — for pup it was+frontmatter and then the describe catalog, for abe a README feature promise, for kicad runtime verb+metadata. What is constant is the question: after fixing a claim, ask **what else asserts this**,+and enumerate rather than recall.+ ### BEHAVIOURAL claims: shipped skills described a taskbar the Mac doesn't have  The worst of the doc classes, and the one no tool the fleet built can detect — these sentences
src/server.js+11−4
@@ -5684,7 +5684,7 @@ const server = http.createServer(async (req, res) => {             V('browser_open_tab', 'Open a new tab in an EXISTING session/window (returns {tabId, tabCount, activeTabId} — no need to diff list_tabs). This is the right verb for multi-URL work: ONE window with many tabs (one taskbar button), not many windows. The window stays in the background; the tab auto-flashes the taskbar (debounced) so the user notices. The USER can click the window\'s taskbar button at any time to view it — pup never pulls it back down.', { sessionId: 'required str', url: 'optional str', updateNote: 'optional str — short "what I did" note surfaced in list_tabs as lastAgentUpdate', silent: 'optional bool — suppress the auto-flash for this call' }, 30, { sessionId: 'work', url: 'https://example.com' }),             V('browser_close_tab', 'Close a tab.', { sessionId: 'required str', tabId: 'optional str (default active)' }, 15, { sessionId: 'work' }),             V('browser_close_window', 'Close a window/session (its tabs go too).', { sessionId: 'required str' }, 20, { sessionId: 'work' }),-            V('browser_wiki_set_view', 'Flip a WIKI pup window between the logged-in and public view IN PLACE (relaunches the same window under the other cookie jar). This is the callback for the per-window taskbar right-click jump-list task — it is a USER-initiated action (the window is brought on-screen). AI threads should NOT call this; pass wikiView:"authed"|"public" on browser_open_window instead.', { sessionId: 'required str', view: 'required "authed"|"public"' }, 45, { sessionId: 'wiki-bmv080', view: 'authed' }),+            V('browser_wiki_set_view', 'Flip a WIKI pup window between the logged-in and public view IN PLACE (relaunches the same window under the other cookie jar). This is the callback for the per-window taskbar right-click jump-list task (WINDOWS ONLY — macOS has no taskbar or jump list, so this callback never fires there; flip views with browser_open_window {wikiView} instead) — it is a USER-initiated action (the window is brought on-screen). AI threads should NOT call this; pass wikiView:"authed"|"public" on browser_open_window instead.', { sessionId: 'required str', view: 'required "authed"|"public"' }, 45, { sessionId: 'wiki-bmv080', view: 'authed' }),             V('browser_navigate', 'Navigate the active tab to a URL.', { sessionId: 'required str', url: 'required str' }, 45, { sessionId: 'work', url: 'https://example.com' }),             V('browser_back', 'History back.', { sessionId: 'required str' }, 20, { sessionId: 'work' }),             V('browser_forward', 'History forward.', { sessionId: 'required str' }, 20, { sessionId: 'work' }),@@ -5711,7 +5711,7 @@ const server = http.createServer(async (req, res) => {             V('browser_use', 'Pin (or clear) the browser pup drives by default: chrome | edge | cft | auto (or an explicit executablePath). "cft" fetches Chrome for Testing then pins it. With {browser:"chrome", install:true} it DOWNLOADS & INSTALLS real Google Chrome on an Edge-only box, then pins it — SILENT on a normal PC; on a locked-down box (VM/managed device) it needs a Windows approval, so the bridge pops a native "click YES on the UAC" notification and, if it expires, re-notifies with a one-tap "Approve now" (all autonomous — no AI turn). Poll browser_readiness.chromeStablePhase: installing → awaiting_uac → ready. Persists across restarts; launch still spawn-verifies + falls back.', { browser: '"chrome" | "edge" | "cft" | "auto"', install: 'optional bool — with browser:"chrome", install real Chrome if missing', elevate: 'optional "auto"(default)|true|false — auto: quiet install, prompt only if it needs admin; false: never prompt; true: go straight to the prompt', executablePath: 'optional — pin an exact browser binary' }, 240, {}),             V('browser_lower_os_window', 'Send a pup window to the BACKGROUND (behind the user\'s windows) without stealing focus — still fully drivable. The default for open; use this to re-hide a window you foregrounded.', { sessionId: 'optional str' }, 15, {}),             V('browser_raise_os_window', 'Bring a pup window to the FOREGROUND (user\'s screen). GATED (v1.8.70): REFUSED with foreground_reason_required unless you pass foregroundReason — a quote/paraphrase of what the user said that justifies it (min 10 chars; logged, recorded in lastAgentUpdate, auditable). Allowed ONLY when the user EXPLICITLY must SEE it (a demo, recording, "show me"). It STEALS focus — to just signal "I updated this window", use browser_alert_window (or the auto-flash), NOT raise. After the show-me: browser_lower_os_window to return it to the background. Alias: browser_focus_window.', { sessionId: 'optional str', foregroundReason: 'REQUIRED — why the user asked to see this window' }, 15, { foregroundReason: 'user said "show me the wiki page"' }),-            V('browser_alert_window', 'Flash the taskbar icon for a pup window — a non-intrusive "it\'s ready/DONE" nudge that does NOT steal focus. NOTE (v1.8.23): mutating verbs already AUTO-FLASH (debounced, one per ~5s burst per window) so the user always knows the agent touched a window — use this for an explicit end-of-task "it\'s ready" nudge on top of that. STRONGLY prefer this (or the auto-flash) over browser_raise_os_window, which steals focus and can leak a mid-typed keystroke into the page.', { sessionId: 'optional str', stop: 'optional bool — CLEAR the highlight instead of flashing' }, 15, {}),+            V('browser_alert_window', 'Nudge the user about a pup window WITHOUT stealing focus — on Windows it flashes the taskbar button; on macOS (no taskbar, and no API to bounce another app\'s Dock tile) it unhides the window at its z-position and posts an AD toast whose click focuses it. Same contract on both, so call it identically; just never tell a Mac user to look for a flashing icon. NOTE (v1.8.23): mutating verbs already AUTO-FLASH (debounced, one per ~5s burst per window) so the user always knows the agent touched a window — use this for an explicit end-of-task "it\'s ready" nudge on top of that. STRONGLY prefer this (or the auto-flash) over browser_raise_os_window, which steals focus and can leak a mid-typed keystroke into the page.', { sessionId: 'optional str', stop: 'optional bool — CLEAR the highlight instead of flashing' }, 15, {}),             V('browser_render_html', 'Render a local HTML string in the tab via page.setContent — UTF-8-correct (a data: URL mojibakes accented chars without an explicit charset). Great for previewing generated markup without a temp file. Auto-flashes the window.', { sessionId: 'required str', html: 'required HTML string', waitUntil: 'optional (domcontentloaded default)', updateNote: 'optional note for lastAgentUpdate', silent: 'optional bool — suppress the auto-flash' }, 30, { sessionId: 'work', html: '<h1>Café</h1>' }),             // Session/tab/window management + probes (were routed but undocumented).             V('browser_status', 'List all open pup sessions + their tabs (NOT a readiness check — use browser_readiness for that).', {}, 10, {}),@@ -5727,7 +5727,7 @@ const server = http.createServer(async (req, res) => {             V('desktop_record_status', 'Status of the desktop screen recorder + any active clip.', {}, 10, {}),             V('desktop_record_list', 'List completed desktop recordings on disk.', {}, 10, {}),             V('desktop_recorder_close', 'Close the desktop screen-recorder UI.', {}, 15, {}),-            V('browser_configure', 'Get/set user-facing pup preferences (persisted across bridge updates). taskbarGrouping: "split" (default — one taskbar button PER WINDOW, each badged with its own tab count; best for telling AI threads apart) | "grouped" (all pup windows stack under ONE "Adom Pup" button, badge shows window count; tidy taskbar). Applies to live windows immediately. Ask the USER which they prefer before changing it — this is their taskbar.', { taskbarGrouping: 'optional "split"|"grouped"' }, 15, { taskbarGrouping: 'grouped' }),+            V('browser_configure', 'Get/set user-facing pup preferences (persisted across bridge updates). taskbarGrouping: "split" (default — one taskbar button PER WINDOW, each badged with its own tab count; best for telling AI threads apart) | "grouped" (all pup windows stack under ONE "Adom Pup" button, badge shows window count; tidy taskbar). Applies to live windows immediately. Ask the USER which they prefer before changing it — this is their taskbar. WINDOWS ONLY: macOS has no taskbar, so the setting is stored but INERT there; the reply carries platform + taskbarSupported so branch on those, not on this text.', { taskbarGrouping: 'optional "split"|"grouped"' }, 15, { taskbarGrouping: 'grouped' }),             V('browser_describe', 'This list — the bridge\'s verbs + schemas, for AD\'s Verbs tab.', {}, 10, {}),           ] });           return;@@ -7932,7 +7932,14 @@ const server = http.createServer(async (req, res) => {           } else if (result.cleared) {             hint = `Default cleared → AUTO. pup will drive the first browser that launches (${availTxt}) and cache it. Next browser_open_window applies it.`;           } else if (result.installingChrome) {-            hint = `Downloading & installing REAL Google Chrome (~90 MB, user-scoped) and pinning it as the default. On a normal PC this is SILENT (no prompt). On a locked-down box (VM / managed device) it needs a Windows approval — the bridge pops a native "click YES on the UAC" notification on the user's desktop, and if it expires it re-notifies with a one-tap "Approve now" (no AI turn needed). Poll browser_readiness: chromeStablePhase installing → awaiting_uac (waiting on the user's click; chromeStableAwaitingApproval:true) → ready. Until then pup drives ${availTxt}, so nothing is blocked. Pass {browser:"chrome", install:true, elevate:false} to skip the prompt path, or elevate:true to go straight to it.`;+            // v1.9.86: this hint was entirely Windows-shaped (UAC, awaiting_uac, "click YES"). On+            // macOS the install is a dmg -> ditto -> /Applications (falling back to ~/Applications)+            // with NO elevation step and NO awaiting_uac phase, so a Mac AI reading the old text+            // would poll for a phase that never arrives and tell the user to approve a prompt that+            // never appears. Per the SDK's first principle the AI reads THIS, not the skill file.+            hint = process.platform === 'darwin'+              ? `Downloading & installing REAL Google Chrome (~90 MB) from Google's .dmg and pinning it as the default. On macOS there is NO elevation step and NO approval prompt: pup mounts the dmg with hdiutil and copies Google Chrome.app to /Applications, falling back to ~/Applications if that isn't writable. Poll browser_readiness: chromeStablePhase installing → ready (there is no awaiting_uac phase on macOS — do not wait for one, and do not tell the user to approve anything). If it fails at both destinations the install genuinely failed; read the error. Until then pup drives ${availTxt}, so nothing is blocked.`+              : `Downloading & installing REAL Google Chrome (~90 MB, user-scoped) and pinning it as the default. On a normal PC this is SILENT (no prompt). On a locked-down box (VM / managed device) it needs a Windows approval — the bridge pops a native "click YES on the UAC" notification on the user's desktop, and if it expires it re-notifies with a one-tap "Approve now" (no AI turn needed). Poll browser_readiness: chromeStablePhase installing → awaiting_uac (waiting on the user's click; chromeStableAwaitingApproval:true) → ready. Until then pup drives ${availTxt}, so nothing is blocked. Pass {browser:"chrome", install:true, elevate:false} to skip the prompt path, or elevate:true to go straight to it.`;           } else if (result.installing) {             hint = `Pinned Chrome for Testing as the default and started the ~150 MB download in the background. Poll browser_readiness until ready:true — until then pup falls back to installed Chrome/Edge (${availTxt}). Once cached, every open uses CfT.`;           } else {