master
Colby Knox v2.0.741 (insiders): Login import keeps working where nb cannot run it (pup-bridge#145): pup asks nb first, and only wh… 87889f6 5d ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185
{
 "progressContract": {
  "activeKey": "active",
  "blockedByPhases": [
   "consent",
   "dialog"
  ],
  "blockedOnUser": {
   "errorCode": "blocked_on_user",
   "inVerbWaitSec": 45,
   "promptLifetimeSec": 120,
   "rule": "ab BRIDGE_SDK 2.1.30: a verb parked on a HUMAN never bare-timeouts; it answers this shape",
   "stillRunning": "true = the wait continues in the background: re-call the SAME verb with the SAME args to re-attach (no second prompt) or watch pup_readiness.active[]; false = the human must act first, then re-issue the verb"
  },
  "slowVerbs": [
   "pup_open_window",
   "pup_prewarm",
   "pup_use"
  ],
  "statusVerb": "pup_readiness",
  "timings": {
   "open_window_warm": {
    "confidence": "measured",
    "estimatedSec": 11,
    "samples": 20
   }
  }
 },
 "verbs": [
  {
   "example": {},
   "hint": "This list \u2014 the bridge's verbs + schemas + hints, for AD's Verbs tab. Read it once to learn the surface.",
   "input": {},
   "longRunning": false,
   "name": "pup_describe",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_status"
   ],
   "statusVerb": null,
   "summary": "This list \u2014 the bridge's verbs + schemas, for AD's Verbs tab.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "Cold-start readiness probe: {ready, chromeForTestingInstalled, installing, installProgressPct}. On a fresh PC, poll this until ready:true before pup_open_window \u2014 the bridge auto-installs Chrome for Testing on its own.",
   "input": {},
   "longRunning": false,
   "name": "pup_readiness",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "ready:false with installing:true just means Chrome for Testing is still downloading \u2014 wait + poll, do NOT treat it as an error",
    "installPhase:\"failed\" \u2192 surface lastError to the user (usually network/proxy blocking the download, or low disk)"
   ],
   "related": [
    "pup_prewarm",
    "pup_open_window"
   ],
   "statusVerb": null,
   "summary": "Readiness + which browser pup will drive (installed Chrome/Edge, or cached Chrome for Testing) + the full candidate list and current default. READ-ONLY; never downloads.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "Installs Chrome for Testing (the browser pup drives) WITHOUT opening a window \u2014 the warm-before-first-use path. Returns fast; poll pup_readiness for completion.",
   "input": {
    "setDefault": "optional bool (default true) \u2014 pin CfT as the default",
    "wait": "optional bool \u2014 block until installed"
   },
   "longRunning": false,
   "name": "pup_prewarm",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "the ~150 MB download needs network access to storage.googleapis.com",
    "does not open a window \u2014 call pup_open_window once readiness reports ready:true"
   ],
   "related": [
    "pup_readiness",
    "pup_open_window"
   ],
   "statusVerb": null,
   "summary": "Install Chrome for Testing WITHOUT opening a window, and pin it as the default. Use when you specifically want CfT rather than the machine's installed Chrome/Edge.",
   "timeoutSeconds": 240
  },
  {
   "example": {},
   "hint": "List all open pup sessions + their tabs (NOT a readiness check \u2014 use pup_readiness for that).",
   "input": {},
   "longRunning": false,
   "name": "pup_status",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "List all open pup sessions + their tabs (NOT a readiness check \u2014 use pup_readiness for that).",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "List every open session/window with its tabs.",
   "input": {},
   "longRunning": false,
   "name": "pup_list_windows",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "List every open session/window with its tabs.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "START HERE for window management: everything YOUR thread owns \u2014 windows, tabs, urls, titles, state \u2014 with ready-to-run next steps. Zero args. You NEVER need an hwnd, desktop_find_window, or desktop_screenshot_window for a pup window: pup keys every window to its owning --ai-thread name and resolves ids for you \u2014 omit sessionId in any pup verb to target your own window, or pass \"ai-thread\" to address a thread by name (the CLI's --ai-thread flag, same spelling). pup_screenshot {} screenshots it; pup_reload {} refreshes it.",
   "input": {
    "ai-thread": "optional str \u2014 inspect another thread's windows (read-only listing); default = the calling thread. The CLI flag's exact spelling; aiThread also accepted"
   },
   "longRunning": false,
   "name": "pup_my_windows",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "START HERE for window management: everything YOUR thread owns \u2014 windows, tabs, urls, titles, state \u2014 with ready-to-run next steps. Zero args. You NEVER need an hwnd, desktop_find_window, or desktop_screenshot_window for a pup window: pup keys every window to its owning --ai-thread name and resolves ids for you \u2014 omit sessionId in any pup verb to target your own window, or pass \"ai-thread\" to address a thread by name (the CLI's --ai-thread flag, same spelling). pup_screenshot {} screenshots it; pup_reload {} refreshes it.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "START HERE for window management: everything YOUR thread owns \u2014 windows, tabs, urls, titles, state \u2014 with ready-to-run next steps. Zero args. You NEVER need an hwnd, desktop_find_window, or desktop_screenshot_window for a pup window: pup keys every window to its owning --ai-thread name and resolves ids for you \u2014 omit sessionId in any pup verb to target your own window, or pass \"ai-thread\" to address a thread by name (the CLI's --ai-thread flag, same spelling). pup_screenshot {} screenshots it; pup_reload {} refreshes it.",
   "input": {
    "ai-thread": "optional str \u2014 inspect another thread's windows (read-only listing); default = the calling thread. The CLI flag's exact spelling; aiThread also accepted"
   },
   "longRunning": false,
   "name": "pup_list_sessions",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Alias of pup_my_windows: list the sessions owned by the calling ai-thread. Passive, never launches a browser.",
   "timeoutSeconds": 10
  },
  {
   "example": {
    "height": 540,
    "owner": "mytask",
    "sessionId": "mytask-web",
    "url": "https://example.com",
    "width": 960
   },
   "hint": "Opens a generic, fully-controllable pup window (a fresh isolated Chrome/Edge process \u2014 never your real signed-in browser) that you then drive by sessionId. To drive the user's REAL Chrome/Edge, use the Adom browser extension's nbrowser_* verbs instead.",
   "input": {
    "appIconB64": "optional base64 PNG \u2014 override the app icon worn in the taskbar overlay. Usually UNNECESSARY: pup auto-fetches the page's own <link rel=\"icon\"> favicon (the adom-ui-design standard artifact every app must serve) and wears it automatically. An app-looking URL with NO favicon gets a reprimand hint.",
    "appName": "optional str \u2014 the APP this window is driving (e.g. \"shotlog\"); shown in the hover tooltip",
    "autoFlash": "optional bool (default true) \u2014 mutating verbs auto-flash this window's taskbar (debounced, never steals focus) so the user notices the agent touched it; pass false to make this window silent by default",
    "foreground": "optional bool \u2014 request showing the window on the user's screen. GATED (v1.8.70): requires foregroundReason too, else the open succeeds but stays BACKGROUND with foregroundDenied set. Only when the USER explicitly asked to watch.",
    "foregroundReason": "required WITH foreground:true \u2014 quote/paraphrase what the user said that justifies putting a window on their screen (min 10 chars; logged + recorded in lastAgentUpdate, auditable)",
    "height": "optional int px",
    "isolated": "optional bool \u2014 private throwaway profile: fresh-login tests, acting as a different user. REASON-GATED (v2.0.101): requires isolationReason or the open is refused \u2014 every window defaults to the shared adom-you profile",
    "isolationReason": "required WITH isolated:true \u2014 why this window must NOT ride the shared adom-you profile (min 10 chars; logged, auditable). webSecurity:false and wikiView:public carry their own built-in reason",
    "owner": "optional str \u2014 your thread/task label; lets pup protect this window from other threads",
    "profile": "optional str \u2014 a named durable jar; DEFAULT is the shared \"adom-you\" profile (signed-in state shared across windows). isolated:true overrides to a throwaway",
    "sessionId": "optional str \u2014 OMIT IT (recommended): your thread gets its OWN window keyed on your --ai-thread name; the first open creates it, later opens become TABS in it, and no other thread can touch it. Pass an explicit id only when you need a SEPARATE window (task-prefix it, e.g. \"mytask-web\")",
    "silent": "optional bool \u2014 suppress the auto-flash for just this call",
    "takeover": "optional bool \u2014 claim a window owned by another thread (only when the user explicitly asks)",
    "updateNote": "optional str \u2014 a short \"what I did\" note, surfaced back in list_tabs/list_windows as lastAgentUpdate so the user knows what changed",
    "url": "optional str",
    "webSecurity": "optional false \u2014 dev escape hatch for cross-origin eval; FORCES isolation so insecure flags never touch the credentialed profile",
    "width": "optional int px \u2014 sizes for screenshot framing, stays background",
    "wikiView": "optional \"authed\"|\"public\" \u2014 for wiki.adom.inc URLs. THREE distinct cases: (1) OMIT = the shared adom-you profile, LOGGED IN as the user if they have signed into the wiki (the usual case); this is NOT anonymous. (2) \"public\" = the SERVER renders the page exactly as a signed-out visitor sees it via the ?view=public param (adom/wiki#126: request user nulled, byte-identical to logged-out, private pages 404) \u2014 the window STAYS on the shared profile, session untouched, no isolated jar; use this to verify a publish did not leak private source. (3) \"authed\" = the explicit logged-in view (30-day SSO; first use may need a one-time login \u2014 response says wikiLoginNeeded). To compare \"what I see\" vs \"what the world sees\", open one \"authed\" (or omit) AND one \"public\" tab \u2014 do NOT rely on omit for the public view.",
    "x": "optional int px",
    "y": "optional int px"
   },
   "longRunning": false,
   "name": "pup_open_window",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "sessionId is OPTIONAL: omit it and your thread gets its OWN window (keyed on your --ai-thread name; later opens become tabs in it). Pass one only for a deliberate separate window \u2014 it is then your handle for every later call",
    "profile defaults to the durable shared 'adom-you' profile (logins/localStorage persist + are shared across all pup windows); isolated:true for a private throwaway \u2014 omit it for a plain \"open in pup\"",
    "sessionIds are SHARED across all AI threads on this desktop \u2014 a generic id (\"wiki\", \"test\") may collide with another thread's window and navigating it away steals their work. Use a task-prefixed sessionId + pass owner:\"<your-task>\" so pup can protect it",
    "reusing an existing sessionId with a new url NAVIGATES that window \u2014 check pup_status (shows owner per window) first if unsure"
   ],
   "related": [
    "pup_navigate",
    "pup_list_windows",
    "pup_close_window"
   ],
   "statusVerb": null,
   "summary": "Open a generic pup window \u2014 a fresh isolated Chrome/Edge process (never your real signed-in browser), fully drivable via CDP. ALWAYS opens in the BACKGROUND (does not steal focus; never disturbs the user's main work; still fully screenshottable/drivable) UNLESS you pass foreground:true \u2014 do that ONLY when the user explicitly wants to watch. Pass width+height (+ optional x,y, screen px) to place it at a specific size/position for screenshot framing \u2014 this NO LONGER foregrounds it (sizing stays in the background too; default unsized is maximized). profile defaults to the durable shared 'adom-you' profile (logins/localStorage persist + are shared across all pup windows); isolated:true for a private throwaway. OWNERSHIP: sessionIds are shared across ALL AI threads \u2014 use a task-prefixed sessionId + owner:\"<your-task>\" so pup can refuse other threads stealing your window (reusing an existing id with a new url navigates it away; declared cross-owner reuse errors with session_owned_by_another_thread unless takeover:true). (For the user's REAL Chrome/Edge, use the Adom extension's nbrowser_* verbs.)",
   "timeoutSeconds": 60
  },
  {
   "example": {
    "sessionId": "work",
    "url": "https://example.com"
   },
   "hint": "Drives the active tab to url and waits for load. Verify with a screenshot before asserting success.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "url": "required str"
   },
   "longRunning": false,
   "name": "pup_navigate",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "navigation can race SPA routing \u2014 confirm with pup_screenshot",
    "some sites need pup_wait for a selector after navigate"
   ],
   "related": [
    "pup_screenshot",
    "pup_wait",
    "pup_reload"
   ],
   "statusVerb": null,
   "summary": "Navigate the active tab to a URL.",
   "timeoutSeconds": 45
  },
  {
   "example": {},
   "hint": "",
   "input": {},
   "longRunning": false,
   "name": "pup_eval",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "the expression must be serializable JSON in its return value",
    "runs in the page context, not Node"
   ],
   "related": [
    "pup_evaluate"
   ],
   "statusVerb": null,
   "summary": "Alias of pup_evaluate: evaluate JS in the page and return the result. Add frame:\"<name|url-substring|index>\" to run inside an iframe.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "expression": "document.title",
    "sessionId": "work"
   },
   "hint": "Runs JS in the page and returns the result \u2014 use it to read DOM state or verify selectors before acting.",
   "input": {
    "expression": "required str \u2014 aliases accepted: expr, script, js, code",
    "frame": "optional str \u2014 run inside this iframe instead of the top document",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_evaluate",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "the expression must be serializable JSON in its return value",
    "runs in the page context, not Node"
   ],
   "related": [
    "pup_click",
    "pup_fetch_url"
   ],
   "statusVerb": null,
   "summary": "Evaluate JS in the page and return the result \u2014 THE verb for reading DOM state / asserting on a page (same handler as pup_eval). Add frame:\"<name|url-substring|index>\" to run inside an iframe.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "PNG of the page. Your primary way to SEE state \u2014 call it after navigate/click to confirm.",
   "input": {
    "fullPage": "optional bool",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_screenshot",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "default is the viewport; pass fullPage:true for the whole scroll height"
   ],
   "related": [
    "pup_screenshot_full_res",
    "pup_navigate"
   ],
   "statusVerb": null,
   "summary": "Screenshot the page (PNG; fullPage optional). NOTE: fullPage only covers DOCUMENT-level scroll \u2014 on SPAs that scroll inside a nested container it returns just the viewport + a nestedScroll hint; use pup_screenshot_element or pup_scroll for those.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Closes the whole session and all its tabs. Destructive.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_close_window",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "this ends the session \u2014 its sessionId is no longer valid afterward"
   ],
   "related": [
    "pup_list_windows"
   ],
   "statusVerb": null,
   "summary": "Close a window/session (its tabs go too).",
   "timeoutSeconds": 20
  },
  {
   "example": {},
   "hint": "Close ALL pup sessions + windows at once (destructive). NOTE: `{}` here does NOT mean \"the active session\" the way it does on other session-scoped verbs \u2014 with no args this closes EVERY window, across every AI thread on the machine, with no confirmation. To close ONE window use pup_close_window {sessionId}. Pass {sessionId} here only if you genuinely mean that one session.",
   "input": {},
   "longRunning": false,
   "name": "pup_close",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Close ALL pup sessions + windows at once (destructive). NOTE: `{}` here does NOT mean \"the active session\" the way it does on other session-scoped verbs \u2014 with no args this closes EVERY window, across every AI thread on the machine, with no confirmation. To close ONE window use pup_close_window {sessionId}. Pass {sessionId} here only if you genuinely mean that one session.",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Closes the whole session and all its tabs. Destructive.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_close_all_windows",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "this ends the session \u2014 its sessionId is no longer valid afterward"
   ],
   "related": [
    "pup_list_windows"
   ],
   "statusVerb": null,
   "summary": "Close every pup window owned by the calling ai-thread (pup_close_window with all:true). Other threads' windows are never touched.",
   "timeoutSeconds": 20
  },
  {
   "example": {},
   "hint": "Everything about ONE pup window as JSON \u2014 owner AI thread, container, profile, tabs, age, last agent action, and the window's hwnd (for desktop_screenshot_window when you need the browser chrome in frame). For an AI caller this is a PURE DATA query: nothing is drawn on the user's screen. Visual surfaces are user-initiated only \u2014 a jump-list click shows the native taskbar caption, a toolbar-popup click shows the in-page floaty card. showCaption:true opts an AI caller into the caption ONLY when the user asked to have the window identified on screen. Read-only, never owner-gated.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "showCaption": "optional bool \u2014 draw the ~12s taskbar caption too; ONLY when the user asked to see which window this is"
   },
   "longRunning": false,
   "name": "pup_window_info",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Everything about ONE pup window as JSON \u2014 owner AI thread, container, profile, tabs, age, last agent action, and the window's hwnd (for desktop_screenshot_window when you need the browser chrome in frame). For an AI caller this is a PURE DATA query: nothing is drawn on the user's screen. Visual surfaces are user-initiated only \u2014 a jump-list click shows the native taskbar caption, a toolbar-popup click shows the in-page floaty card. showCaption:true opts an AI caller into the caption ONLY when the user asked to have the window identified on screen. Read-only, never owner-gated.",
   "timeoutSeconds": 15
  },
  {
   "example": {
    "sessionId": "work",
    "url": "https://example.com"
   },
   "hint": "Adds a tab to an existing session; returns the new tabId.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "silent": "optional bool \u2014 suppress the auto-flash for this call",
    "updateNote": "optional str \u2014 short \"what I did\" note surfaced in list_tabs as lastAgentUpdate",
    "url": "optional str"
   },
   "longRunning": false,
   "name": "pup_open_tab",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "the session must already exist (pup_open_window first)"
   ],
   "related": [
    "pup_switch_tab",
    "pup_list_tabs",
    "pup_close_tab"
   ],
   "statusVerb": null,
   "summary": "Open a new tab in an EXISTING session/window (returns {tabId, tabCount, activeTabId} \u2014 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 \u2014 pup never pulls it back down.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "List the tabs in a session.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_list_tabs",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "List the tabs in a session.",
   "timeoutSeconds": 10
  },
  {
   "example": {
    "sessionId": "work",
    "tabId": "tab-2"
   },
   "hint": "Activate a tab WITHIN its session/window (idempotent \u2014 safe to call when the tab is already active). Does NOT raise a background window to the user's screen: the tab becomes the window's visible tab wherever the window is, and the taskbar auto-flash tells the user something changed. Also self-heals the window (re-park if a bug ever stranded it off-screen) and refreshes the Adom taskbar badge \u2014 good citizenship for long-lived shared windows.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "silent": "optional bool",
    "tabId": "required str",
    "updateNote": "optional str"
   },
   "longRunning": false,
   "name": "pup_switch_tab",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Activate a tab WITHIN its session/window (idempotent \u2014 safe to call when the tab is already active). Does NOT raise a background window to the user's screen: the tab becomes the window's visible tab wherever the window is, and the taskbar auto-flash tells the user something changed. Also self-heals the window (re-park if a bug ever stranded it off-screen) and refreshes the Adom taskbar badge \u2014 good citizenship for long-lived shared windows.",
   "timeoutSeconds": 10
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Closes one tab; the session stays open.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "tabId": "optional str (default active)"
   },
   "longRunning": false,
   "name": "pup_close_tab",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "omit tabId to close the active tab \u2014 closing the last tab may close the window"
   ],
   "related": [
    "pup_close_window",
    "pup_list_tabs"
   ],
   "statusVerb": null,
   "summary": "Close a tab.",
   "timeoutSeconds": 15
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Make a session the active one (for verbs that default to active).",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_switch_window",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Make a session the active one (for verbs that default to active).",
   "timeoutSeconds": 10
  },
  {
   "example": {
    "selector": "button#go",
    "sessionId": "work"
   },
   "hint": "Clicks by CSS selector (preferred) or by x,y coords. Selector is more robust across layouts.",
   "input": {
    "button": "optional \"left\"|\"right\"|\"middle\"",
    "clickCount": "optional int 1-3",
    "domClick": "optional bool \u2014 dispatch a synthetic element.click() instead of a trusted CDP click; for React controls that ignore trusted clicks",
    "selector": "optional str",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "x": "optional int",
    "y": "optional int"
   },
   "longRunning": false,
   "name": "pup_click",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "selector must match exactly one visible element \u2014 verify with pup_evaluate first",
    "coords are viewport-relative; a scrolled element may be off-screen"
   ],
   "related": [
    "pup_type",
    "pup_input_dispatch",
    "pup_screenshot"
   ],
   "statusVerb": null,
   "summary": "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 \u2014 retry with domClick:true to fire a synthetic element.click().",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "selector": "select[name=timezone]",
    "label": "Central Time"
   },
   "hint": "Sets the option through the native setter and fires input+change so framework-bound selects update; works on both lanes (DevTools evaluate, so page CSP and Trusted Types cannot refuse it). For custom dropdowns (Material, React Select) use pup_click on the control, then pup_click the option by text. option_not_found lists the first 30 options.",
   "input": {
    "selector": "required str",
    "value": "optional str (option value)",
    "label": "optional str (visible text, case-insensitive, substring ok)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_select",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_fill",
    "pup_click",
    "pup_type"
   ],
   "statusVerb": null,
   "summary": "Choose a <select> option by value or visible label, in the background (fires input+change).",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "selector": "#menu",
    "sessionId": "work"
   },
   "hint": "Move the pointer over a target WITHOUT clicking (reveals tooltips / hover menus). Glides the visible Adom cursor there too.",
   "input": {
    "intent": "optional str \u2014 narrated on the cursor label",
    "selector": "optional str",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "steps": "optional int (default 8)",
    "x": "optional int",
    "y": "optional int"
   },
   "longRunning": false,
   "name": "pup_hover",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Move the pointer over a target WITHOUT clicking (reveals tooltips / hover menus). Glides the visible Adom cursor there too.",
   "timeoutSeconds": 15
  },
  {
   "example": {
    "clear": true,
    "selector": "#user",
    "sessionId": "work",
    "text": "adominc1"
   },
   "hint": "Types into the focused or selector-targeted field.",
   "input": {
    "clear": "optional bool \u2014 empty the field first (replace, not append)",
    "selector": "optional str",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "text": "required str"
   },
   "longRunning": false,
   "name": "pup_type",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "focus the field first (pup_click the input) or pass selector",
    "does not clear existing text \u2014 select-all + delete if you need a clean field"
   ],
   "related": [
    "pup_click",
    "pup_press_key"
   ],
   "statusVerb": null,
   "summary": "Type text into a field via real CDP keystrokes (isTrusted). Reads the value back and returns valueLanded \u2014 real keystrokes only register in a FOREGROUNDED window, so if valueLanded:false the field stayed empty (the silent no-op): use pup_fill (background-safe) for a non-React input, or pup_login {realKeystrokes:true} for a login form. Pass clear:true to REPLACE instead of appending.",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "key": "a",
    "modifiers": [
     "Control"
    ],
    "sessionId": "work"
   },
   "hint": "Presses a key or chord (Enter, Ctrl+A, Tab).",
   "input": {
    "key": "required str",
    "modifiers": "optional array \u2014 any of [\"Control\",\"Shift\",\"Alt\",\"Meta\"] (aliases ctrl/cmd/command/option accepted); held down around the key press",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_press_key",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "chord syntax is \"Ctrl+A\" \u2014 verify the page has focus on the intended element"
   ],
   "related": [
    "pup_type",
    "pup_input_dispatch"
   ],
   "statusVerb": null,
   "summary": "Press a key, optionally with modifier chords held (Ctrl+A, Ctrl+V, Shift+Tab).",
   "timeoutSeconds": 15
  },
  {
   "example": {
    "sessionId": "work",
    "toText": "Download"
   },
   "hint": "Scroll the ACTUAL scrolling element (nearest ancestor whose scrollHeight>clientHeight, NOT just window \u2014 window.scrollTo no-ops on nested-scroll SPAs). Scroll by delta {dy,dx} OR to an element {toSelector|toText} (pierces shadow DOM). Returns {scrollElement, scrollTop, scrollHeight, clientHeight} so you can confirm it landed.",
   "input": {
    "behavior": "optional auto|smooth",
    "block": "optional start|center|end (for scroll-to; default center)",
    "dx": "optional int px",
    "dy": "optional int px (down+/up-)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "toSelector": "optional CSS selector to scroll to",
    "toText": "optional visible text to scroll to"
   },
   "longRunning": false,
   "name": "pup_scroll",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Scroll the ACTUAL scrolling element (nearest ancestor whose scrollHeight>clientHeight, NOT just window \u2014 window.scrollTo no-ops on nested-scroll SPAs). Scroll by delta {dy,dx} OR to an element {toSelector|toText} (pierces shadow DOM). Returns {scrollElement, scrollTop, scrollHeight, clientHeight} so you can confirm it landed.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "selector": "#email",
    "sessionId": "work",
    "text": "[email protected]"
   },
   "hint": "Set a field's value in the BACKGROUND (no foreground, no keystrokes): writes .value via the native setter and fires input+change so React/Vue/Angular controlled inputs pick it up. The fix for pup_type's silent no-op on a backgrounded window. Reads the value back and returns valueLanded. For React-MASKED password fields that reject programmatic writes, use pup_login {realKeystrokes:true} instead.",
   "input": {
    "selector": "required str",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "text": "required str"
   },
   "longRunning": false,
   "name": "pup_fill",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Set a field's value in the BACKGROUND (no foreground, no keystrokes): writes .value via the native setter and fires input+change so React/Vue/Angular controlled inputs pick it up. The fix for pup_type's silent no-op on a backgrounded window. Reads the value back and returns valueLanded. For React-MASKED password fields that reject programmatic writes, use pup_login {realKeystrokes:true} instead.",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "ms": 500,
    "sessionId": "work"
   },
   "hint": "Wait N ms (or for a selector) before the next step.",
   "input": {
    "ms": "optional int",
    "selector": "optional str",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_wait",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Wait N ms (or for a selector) before the next step.",
   "timeoutSeconds": 60
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Reloads the active tab.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_reload",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "A page with unsaved changes raises a native \"Reload site?\" dialog that BLOCKS the window: the reload appears to time out and so does everything else. Answer it with pup_dialog."
   ],
   "related": [
    "pup_navigate",
    "pup_wait",
    "pup_dialog"
   ],
   "statusVerb": null,
   "summary": "Reload the active tab.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "History back on the active tab.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_back",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_forward",
    "pup_navigate"
   ],
   "statusVerb": null,
   "summary": "History back.",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "History forward on the active tab.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_forward",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_back",
    "pup_navigate"
   ],
   "statusVerb": null,
   "summary": "History forward.",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "reset": true,
    "sessionId": "work"
   },
   "hint": "Pin the page viewport (CDP device-metrics) to an exact size for deterministic screenshots \u2014 OR clear that pin. \u26a0 A pin PERSISTS: it survives navigation and hard reload, and while set the window stops tracking OS resizing (a human resizing it sees no reflow, which looks exactly like an app layout bug). ALWAYS clear it when done. Reported as emulatedViewport in pup_status / pup_list_windows.",
   "input": {
    "deviceScaleFactor": "optional number (default 1)",
    "height": "int \u2014 required unless reset",
    "reset": "optional bool \u2014 CLEAR the override and track the OS window again (width/height not needed)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "width": "int \u2014 required unless reset"
   },
   "longRunning": false,
   "name": "pup_set_viewport",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Pin the page viewport (CDP device-metrics) to an exact size for deterministic screenshots \u2014 OR clear that pin. \u26a0 A pin PERSISTS: it survives navigation and hard reload, and while set the window stops tracking OS resizing (a human resizing it sees no reflow, which looks exactly like an app layout bug). ALWAYS clear it when done. Reported as emulatedViewport in pup_status / pup_list_windows.",
   "timeoutSeconds": 15
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Console errors + failed network requests for a session/tab (clears by default).",
   "input": {
    "clear": "optional bool (default true)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "tabId": "optional str"
   },
   "longRunning": false,
   "name": "pup_errors",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Console errors + failed network requests for a session/tab (clears by default).",
   "timeoutSeconds": 10
  },
  {
   "name": "pup_dialog",
   "summary": "Answer a JavaScript dialog (alert, confirm, prompt, beforeunload) on the page: accept or dismiss, with promptText for a prompt.",
   "input": {
    "action": "accept | dismiss (default accept)",
    "promptText": "optional str: the answer typed into a prompt()",
    "window": "optional: the pup window (default the calling thread's)"
   },
   "example": {
    "action": "accept"
   },
   "hint": "A dialog blocks every other verb on that tab until it is answered. pup_errors and the verb that tripped it name the open dialog.",
   "related": [
    "pup_errors",
    "pup_click"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "example": {
    "sessionId": "work",
    "type": "mousePressed",
    "x": 100,
    "y": 200
   },
   "hint": "Low-level CDP mouse/keyboard input at coords \u2014 beats some overlays/iframes that swallow synthetic clicks.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "type": "required str",
    "x": "optional int",
    "y": "optional int"
   },
   "longRunning": false,
   "name": "pup_input_dispatch",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "coords are viewport pixels",
    "last-resort when pup_click can't reach the element \u2014 still cannot pass Cloudflare interactive challenges"
   ],
   "related": [
    "pup_click",
    "pup_press_key"
   ],
   "statusVerb": null,
   "summary": "Low-level input (mouse/keyboard) at coords \u2014 beats some overlays.",
   "timeoutSeconds": 20
  },
  {
   "name": "pup_devtools",
   "hint": "Send ONE DevTools protocol command (DOM, Runtime, Input, Page, Network, Emulation...) to a pup window, on any lane. On the default extension lane it goes through pup's own extension, so every pup window has CDP-level access with no debug port. Never give up or switch machines because the CDP lane is off: use this.",
   "input": {
    "method": "str, e.g. \"DOM.getDocument\"",
    "params": "optional object",
    "tabId": "optional str",
    "timeoutSec": "optional int 1-40 (default 20)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "output": {
    "success": "bool",
    "result": "the command's result object",
    "lane": "\"ext\"|\"cdp\""
   },
   "example": {
    "method": "Runtime.evaluate",
    "params": {
     "expression": "document.title",
     "returnByValue": true
    }
   },
   "longRunning": false
  },
  {
   "example": {
    "html": "<h1>Caf\u00e9</h1>",
    "sessionId": "work"
   },
   "hint": "Render a local HTML string in the tab via page.setContent \u2014 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.",
   "input": {
    "html": "required HTML string",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "silent": "optional bool \u2014 suppress the auto-flash",
    "updateNote": "optional note for lastAgentUpdate",
    "waitUntil": "optional (domcontentloaded default)"
   },
   "longRunning": false,
   "name": "pup_render_html",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Render a local HTML string in the tab via page.setContent \u2014 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.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "sessionId": "work",
    "url": "https://api.example.com"
   },
   "hint": "Fetches a URL using the tab's live cookies/session \u2014 for authed APIs the bare CLI can't reach. Returns bodyBase64.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "url": "required str"
   },
   "longRunning": false,
   "name": "pup_fetch_url",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "for session-scoped URLs pass the tabId whose cookies you need",
    "returns base64 \u2014 decode it caller-side"
   ],
   "related": [
    "pup_navigate",
    "pup_evaluate"
   ],
   "statusVerb": null,
   "summary": "Fetch a URL's content via the browser (cookies/session intact).",
   "timeoutSeconds": 45
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Maximize (or restore/fullscreen/minimize) a pup window via CDP \u2014 no AD desktop verb required. Does NOT foreground it.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "state": "optional \"maximized\"|\"normal\"|\"minimized\"|\"fullscreen\" (default maximized)"
   },
   "longRunning": false,
   "name": "pup_maximize",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Maximize (or restore/fullscreen/minimize) a pup window via CDP \u2014 no AD desktop verb required. Does NOT foreground it.",
   "timeoutSeconds": 15
  },
  {
   "example": {
    "foregroundReason": "user said \"show me the wiki page\""
   },
   "hint": "Bring a pup window to the FOREGROUND (user's screen). GATED (v1.8.70): REFUSED with foreground_reason_required unless you pass foregroundReason \u2014 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 \u2014 to just signal \"I updated this window\", use pup_alert_window (or the auto-flash), NOT raise. After the show-me: pup_lower_os_window to return it to the background. Alias: pup_focus_window. IMPORTANT: if you are foregrounding in order to CLICK a native dialog or a cross-origin FedCM/Google account chooser, do NOT raise-then-click yourself \u2014 use pup_assisted_click, which waits for the user to actually be on the window (alt-tab aware) and narrates via captions, so you never act while they are not ready.",
   "input": {
    "foregroundReason": "REQUIRED \u2014 why the user asked to see this window",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_raise_os_window",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Bring a pup window to the FOREGROUND (user's screen). GATED (v1.8.70): REFUSED with foreground_reason_required unless you pass foregroundReason \u2014 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 \u2014 to just signal \"I updated this window\", use pup_alert_window (or the auto-flash), NOT raise. After the show-me: pup_lower_os_window to return it to the background. Alias: pup_focus_window. IMPORTANT: if you are foregrounding in order to CLICK a native dialog or a cross-origin FedCM/Google account chooser, do NOT raise-then-click yourself \u2014 use pup_assisted_click, which waits for the user to actually be on the window (alt-tab aware) and narrates via captions, so you never act while they are not ready.",
   "timeoutSeconds": 15
  },
  {
   "example": {},
   "hint": "",
   "input": {},
   "longRunning": false,
   "name": "pup_focus_window",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_raise_os_window"
   ],
   "statusVerb": null,
   "summary": "Alias of pup_raise_os_window (foregroundReason required). It is also what a pup taskbar button's relaunch command calls when the user clicks it; a human caller gets no warning toast.",
   "timeoutSeconds": 30
  },
  {
   "example": {},
   "hint": "Send a pup window to the BACKGROUND (behind the user's windows) without stealing focus \u2014 still fully drivable. The default for open; use this to re-hide a window you foregrounded.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_lower_os_window",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Send a pup window to the BACKGROUND (behind the user's windows) without stealing focus \u2014 still fully drivable. The default for open; use this to re-hide a window you foregrounded.",
   "timeoutSeconds": 15
  },
  {
   "example": {},
   "hint": "Nudge the user about a pup window WITHOUT stealing focus \u2014 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 \u2014 use this for an explicit end-of-task \"it's ready\" nudge on top of that. STRONGLY prefer this (or the auto-flash) over pup_raise_os_window, which steals focus and can leak a mid-typed keystroke into the page.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "stop": "optional bool \u2014 CLEAR the highlight instead of flashing"
   },
   "longRunning": false,
   "name": "pup_alert_window",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Nudge the user about a pup window WITHOUT stealing focus \u2014 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 \u2014 use this for an explicit end-of-task \"it's ready\" nudge on top of that. STRONGLY prefer this (or the auto-flash) over pup_raise_os_window, which steals focus and can leak a mid-typed keystroke into the page.",
   "timeoutSeconds": 15
  },
  {
   "example": {},
   "hint": "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.",
   "input": {
    "mode": "\"indeterminate\" | \"value\" | \"off\"",
    "note": "optional short stage label for the hover tooltip",
    "sessionId": "optional \u2014 defaults to your thread's own window",
    "value": "optional 0-100 (mode:\"value\")"
   },
   "longRunning": false,
   "name": "pup_progress",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "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.",
   "timeoutSeconds": 8
  },
  {
   "example": {
    "taskbarGrouping": "grouped"
   },
   "hint": "Get/set user-facing pup preferences (persisted across bridge updates). taskbarGrouping: \"split\" (default \u2014 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 \u2014 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.",
   "input": {
    "autoLogin": "optional \"on\"|\"off\" \u2014 auto-fill a vault-matched login form on page load (fill only, never submits), on by default",
    "overlaySize": "optional \"50\"|\"100\" \u2014 how big the favicon badge is drawn on the taskbar button. \"50\" (the DEFAULT) draws it at half size so it stays clear of the thread tile's bottom line of text; \"100\" is the older, larger badge",
    "reportIssues": "optional \"on\"|\"off\" \u2014 pup's bug-report invitation (_reportIssues), on by default",
    "taskbarActivity": "optional \"progress\"|\"flash\"|\"off\" \u2014 how pup signals it is working on a window, on that window's taskbar button: a determinate progress bar sized to the operation's running-average time (default), the old orange flash, or nothing. Replaces the former openFlash/autoFlash/agentActivity toggles.",
    "taskbarGrouping": "optional \"split\"|\"grouped\""
   },
   "longRunning": false,
   "name": "pup_configure",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Get/set user-facing pup preferences (persisted across bridge updates). taskbarGrouping: \"split\" (default \u2014 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 \u2014 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.",
   "timeoutSeconds": 15
  },
  {
   "example": {},
   "hint": "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 \u2014 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 \u2014 no AI turn). Poll pup_readiness.chromeStablePhase: installing \u2192 awaiting_uac \u2192 ready. Persists across restarts; launch still spawn-verifies + falls back.",
   "input": {
    "browser": "\"chrome\" | \"edge\" | \"cft\" | \"auto\"",
    "elevate": "optional \"auto\"(default)|true|false \u2014 auto: quiet install, prompt only if it needs admin; false: never prompt; true: go straight to the prompt",
    "executablePath": "optional \u2014 pin an exact browser binary",
    "install": "optional bool \u2014 with browser:\"chrome\", install real Chrome if missing"
   },
   "longRunning": false,
   "name": "pup_use",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "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 \u2014 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 \u2014 no AI turn). Poll pup_readiness.chromeStablePhase: installing \u2192 awaiting_uac \u2192 ready. Persists across restarts; launch still spawn-verifies + falls back.",
   "timeoutSeconds": 240
  },
  {
   "name": "pup_clear_cookies",
   "summary": "Delete cookies in the window's profile for one site (domain or origin), optionally only the named cookies.",
   "input": {
    "domain": "str, e.g. example.com (or origin / origins)",
    "origin": "optional str",
    "origins": "optional [str]",
    "names": "optional [str]: only these cookie names"
   },
   "example": {
    "domain": "example.com"
   },
   "hint": "Clears the SHARED profile's cookies for that site: every pup window on the profile is signed out of it. Use it to test a logged-out flow, not as a reset.",
   "related": [
    "pup_clear_site_data",
    "pup_credentials"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "name": "pup_clear_site_data",
   "summary": "Clear storage for a site (cookies, localStorage, IndexedDB, cache) in the window profile.",
   "input": {
    "origin": "str: https://example.com (or origins)",
    "origins": "optional [str]",
    "all": "optional bool: every origin in the profile",
    "everyone": "optional bool: required with all:true because it signs every pup window out of everything"
   },
   "example": {
    "origin": "https://example.com"
   },
   "hint": "all:true wipes the whole shared profile and is refused without everyone:true.",
   "related": [
    "pup_clear_cookies"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "name": "pup_quit_idle_browsers",
   "summary": "Quit every pup browser that has no windows (so the next open loads freshly staged extensions).",
   "input": {},
   "example": {},
   "hint": "A browser with live windows is never touched.",
   "related": [
    "pup_restart_browser"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "example": {},
   "hint": "Recover orphaned sessions: reconnect dropped CDP sockets + rebuild sessions from window titles. Pass adoptOrphans:true to also adopt untagged tabs.",
   "input": {
    "adoptOrphans": "optional bool"
   },
   "longRunning": false,
   "name": "pup_rescan",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Recover orphaned sessions: reconnect dropped CDP sockets + rebuild sessions from window titles. Pass adoptOrphans:true to also adopt untagged tabs.",
   "timeoutSeconds": 30
  },
  {
   "name": "pup_restart_browser",
   "summary": "Restart pup's browser for a profile; every window on it comes back at its page, in the background.",
   "input": {
    "profile": "optional str (default adom-you)"
   },
   "example": {},
   "hint": "Use it when a lane is wedged. Windows are relaunched from their saved state; in-page drafts are lost.",
   "related": [
    "pup_quit_idle_browsers",
    "pup_readiness"
   ],
   "timeoutSeconds": 90,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "example": {
    "avatarB64": "<base64 png>",
    "name": "John Lauer"
   },
   "hint": "Set the Adom identity shown in the floating owner chip on every pup window (avatar + name). Chrome's own profile chip cannot take a custom local image (Google sign-in only), so the user's Adom avatar lives here instead. Pass a pre-circle-cropped PNG as base64. Applies to live windows immediately + persists.",
   "input": {
    "avatarB64": "optional str \u2014 base64 PNG (ideally ~48px, circle-cropped); the leading data: prefix is stripped if present",
    "clear": "optional bool \u2014 remove the stored avatar + name",
    "enabled": "optional bool \u2014 turn the whole owner chip on/off",
    "name": "optional str \u2014 display name shown in the chip tooltip"
   },
   "longRunning": false,
   "name": "pup_set_identity",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Set the Adom identity shown in the floating owner chip on every pup window (avatar + name). Chrome's own profile chip cannot take a custom local image (Google sign-in only), so the user's Adom avatar lives here instead. Pass a pre-circle-cropped PNG as base64. Applies to live windows immediately + persists.",
   "timeoutSeconds": 15
  },
  {
   "name": "pup_audit_icons",
   "summary": "Read back each pup window's taskbar identity from Windows and compare it with what pup stamped.",
   "input": {},
   "example": {},
   "hint": "Detector only: nothing is re-stamped.",
   "related": [
    "pup_set_identity",
    "pup_sweep_identities"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "example": {},
   "hint": "Open the LIVE pup management dashboard in its OWN window (its own profile, so its own taskbar icon). Shows every window pup is driving as taskbar button -> window(s) -> tabs with each tab favicon, owning AI thread, foreground/background + the reason given to foreground it, highFps lease, uptime, and WHY each overlay icon was chosen. Live over SSE. Reuses the one window on repeat calls; backgrounded by default like any pup window.",
   "input": {},
   "longRunning": false,
   "name": "pup_dashboard",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Open the LIVE pup management dashboard in its OWN window (its own profile, so its own taskbar icon). Shows every window pup is driving as taskbar button -> window(s) -> tabs with each tab favicon, owning AI thread, foreground/background + the reason given to foreground it, highFps lease, uptime, and WHY each overlay icon was chosen. Live over SSE. Reuses the one window on repeat calls; backgrounded by default like any pup window.",
   "timeoutSeconds": 30
  },
  {
   "example": {},
   "hint": "List saved logins (host + username only; passwords are in the OS keychain and NEVER returned). pup captures these automatically as you sign into sites and auto-fills them in the background on return visits.",
   "input": {},
   "longRunning": false,
   "name": "pup_credentials",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "List saved logins (host + username only; passwords are in the OS keychain and NEVER returned). pup captures these automatically as you sign into sites and auto-fills them in the background on return visits.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "",
   "input": {},
   "longRunning": false,
   "name": "credential_list",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_credentials"
   ],
   "statusVerb": null,
   "summary": "Alias of pup_credentials: list saved logins (host and username only, never passwords).",
   "timeoutSeconds": 30
  },
  {
   "name": "credential_set",
   "summary": "Store a login in pup's vault for a host.",
   "input": {
    "host": "str",
    "username": "str",
    "password": "str (never echoed or logged)"
   },
   "example": {
    "host": "example.com",
    "username": "[email protected]",
    "password": "<secret>"
   },
   "hint": "Matched across the site's whole registrable domain on return visits.",
   "related": [
    "pup_credentials",
    "credential_delete",
    "pup_login"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "name": "credential_delete",
   "summary": "Remove a saved login from pup's vault.",
   "input": {
    "host": "str",
    "username": "optional str: only this account"
   },
   "example": {
    "host": "example.com"
   },
   "hint": "",
   "related": [
    "pup_credentials",
    "credential_set"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "example": {
    "sessionId": "work",
    "submit": true,
    "submitReason": "confirmed the tab is on the real vendor domain I intended to sign into"
   },
   "hint": "Fill (and optionally submit) an HTML login form in the CURRENT tab, in the BACKGROUND \u2014 no Chrome autofill dropdown, no foregrounding, React/Vue-safe. Creds from the keychain vault (matched on the tab URL; captured automatically as you log in, or stored via credential_set / save:true) or passed explicitly. Passwords are NEVER echoed back. Vault-matched pages AUTO-FILL on load (_autoLogin on the response). SUBMIT IS REASON-GATED: submit:true requires submitReason AND makes you confirm the live domain (the refusal regurgitates the exact host so a lookalike/phishing site is caught before the secret is sent). pup_configure {autoLogin:\"off\"} disables auto-fill; {captureLogins:\"off\"} stops auto-capture.",
   "input": {
    "host": "optional str \u2014 override the vault host key when saving",
    "password": "optional str \u2014 with username, fills these instead of the vault",
    "realKeystrokes": "optional bool \u2014 type with REAL key events instead of a background .value-set. Needed for React-controlled MASKED fields (Gusto/Keycloak SSO) that ignore .value and would submit an empty password. It briefly FOREGROUNDS the window (real keystrokes only register on-screen). Use when a normal fill returned filledPassword:false / reactMaskedFieldSuspected, or when the login-recipe for the site says so.",
    "save": "optional bool \u2014 with explicit creds, store them (OS keychain) so next time is automatic",
    "sessionId": "optional str \u2014 omit to target the window your own thread owns",
    "submit": "optional bool \u2014 submit after filling; REQUIRES submitReason",
    "submitReason": "required WITH submit:true \u2014 why you are confident the CURRENT domain is the correct, intended site to send these credentials to (min 10 chars; the refusal shows you the live host to confirm; logged, auditable)",
    "username": "optional str"
   },
   "longRunning": false,
   "name": "pup_login",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Fill (and optionally submit) an HTML login form in the CURRENT tab, in the BACKGROUND \u2014 no Chrome autofill dropdown, no foregrounding, React/Vue-safe. Creds from the keychain vault (matched on the tab URL; captured automatically as you log in, or stored via credential_set / save:true) or passed explicitly. Passwords are NEVER echoed back. Vault-matched pages AUTO-FILL on load (_autoLogin on the response). SUBMIT IS REASON-GATED: submit:true requires submitReason AND makes you confirm the live domain (the refusal regurgitates the exact host so a lookalike/phishing site is caught before the secret is sent). pup_configure {autoLogin:\"off\"} disables auto-fill; {captureLogins:\"off\"} stops auto-capture.",
   "timeoutSeconds": 20
  },
  {
   "example": {},
   "hint": "Each account comes back with kind (work or personal, from its domain), the browser profile labels the user gave it (\"John Personal\"), whether pup is already signed in, whether pup holds its password, and likelyStale for an account found only in pup's vault. pup never chooses between accounts: pick from the task, the user's work and personal skills, or ask the user.",
   "input": {
    "refresh": "optional bool (redo the whole search now)"
   },
   "longRunning": false,
   "name": "pup_google_accounts",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "An account only in pup's vault and signed into no browser here is usually years old; Google will demand a second step for it."
   ],
   "related": [
    "pup_google_signin",
    "pup_credentials",
    "pup_import_browser_logins"
   ],
   "statusVerb": null,
   "summary": "List the user's Google accounts: every Chrome/Edge profile, nb's browser profiles, pup's vault, and which ones pup's own profile is already signed into. Cached a day.",
   "timeoutSeconds": 10
  },
  {
   "example": {
    "account": "[email protected]",
    "submitReason": "the task is the work calendar, so the work account"
   },
   "hint": "Already signed into pup: no password is sent, the window just opens the account. Otherwise pup fills the email and the password saved for exactly that account and submits. If Google asks for a second step the answer is google_verification_required with the options or the code field, and the exact call to finish it; a text code can be read by a thread with the user's standing permission to read their texts. google_password_missing names where the account is signed in and how to import its password (pup_import_browser_logins {hostFilter:\"google.com\"}). The account is confirmed by name at the end: Gmail silently opens another signed-in account when the requested one is not signed in.",
   "input": {
    "account": "optional str: an email, or \"work\" / \"personal\" when exactly one account of that kind exists. Omit and pup lists the accounts and asks (google_account_choice_required).",
    "submitReason": "str, 10+ chars: required before pup sends a saved password (why this account, for what task)",
    "continue": "optional str: where to land after sign-in (default: that account's Gmail)",
    "window": "optional str: the pup window to use (a new tab in it); default the calling thread's window",
    "code": "optional str: finish a verification step with the code Google sent (with window)",
    "challenge": "optional str: pick a verification option by its words, e.g. \"text message\" (with window)",
    "refresh": "optional bool: redo the account search first"
   },
   "longRunning": false,
   "name": "pup_google_signin",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "Do not restart a sign-in that stopped on a verification step: that sends the password again and a new code. Finish it with {window, code} or {window, challenge}.",
    "Never ask the user to type their password to you: import it or let them sign in once in the pup window."
   ],
   "related": [
    "pup_google_accounts",
    "pup_login",
    "pup_import_browser_logins"
   ],
   "statusVerb": null,
   "summary": "Sign the user into Google as a named account, hands-free: account search, email step, saved password, and the verification step handed back with exact next calls.",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "host": "github.com",
    "reason": "signing into GitHub in my own browser to open a PR for the user"
   },
   "hint": "Reveal ONE saved login (username + password) to a LOCAL caller on the desktop, gated by a sticky consent toast the user must click. One host per call, never bulk. A relayed (cloud container) call is refused with relay_forbidden: from a cloud thread use pup_login instead, which signs the site in inside a pup window without the secret ever leaving the vault. The secret is returned ONCE and must never be logged or echoed.",
   "input": {
    "host": "required str \u2014 the site, e.g. \"github.com\"",
    "reason": "required str (min 10 chars) \u2014 shown to the user on the consent toast; say what you will do with it"
   },
   "longRunning": false,
   "name": "pup_credential_reveal",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Reveal ONE saved login (username + password) to a LOCAL caller on the desktop, gated by a sticky consent toast the user must click. One host per call, never bulk. A relayed (cloud container) call is refused with relay_forbidden: from a cloud thread use pup_login instead, which signs the site in inside a pup window without the secret ever leaving the vault. The secret is returned ONCE and must never be logged or echoed.",
   "timeoutSeconds": 130
  },
  {
   "example": {},
   "hint": "Store a payment card in the OS-keychain card vault under a nickname. Ask the USER for the details \u2014 never harvest them from a page. Validates Luhn + expiry + cvv. No verb ever returns the number or CVV afterward.",
   "input": {
    "cvv": "required 3-4 digits",
    "expiry": "required MM/YY",
    "name": "optional cardholder name",
    "nickname": "required str, e.g. \"adom-amex\"",
    "number": "required str"
   },
   "longRunning": false,
   "name": "pup_card_set",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Store a payment card in the OS-keychain card vault under a nickname. Ask the USER for the details \u2014 never harvest them from a page. Validates Luhn + expiry + cvv. No verb ever returns the number or CVV afterward.",
   "timeoutSeconds": 15
  },
  {
   "example": {},
   "hint": "List saved payment cards: nickname, brand, last4, expiry, cardholder only. The number and CVV never leave the keychain.",
   "input": {},
   "longRunning": false,
   "name": "pup_cards",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "List saved payment cards: nickname, brand, last4, expiry, cardholder only. The number and CVV never leave the keychain.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "Remove a saved card from the keychain + index by nickname.",
   "input": {
    "nickname": "required str"
   },
   "longRunning": false,
   "name": "pup_card_delete",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Remove a saved card from the keychain + index by nickname.",
   "timeoutSeconds": 10
  },
  {
   "example": {
    "amount": "$350",
    "merchant": "venturedallas.com",
    "nickname": "adom-amex"
   },
   "hint": "Fill a saved card into the checkout page of a pup window WITHOUT the digits ever reaching you: pup types them into the card fields (walking payment-processor iframes like Stripe) and returns booleans. EVERY fill is gated by a desktop approval toast naming merchant, amount and last4 \u2014 the user authorizes the specific charge in the moment. Pass merchant+amount so the toast is honest. Allowed from cloud threads (the human click is the gate). After ok:true, screenshot to verify, then submit.",
   "input": {
    "amount": "recommended str, e.g. \"$350\"",
    "merchant": "recommended str \u2014 shown on the approval toast",
    "nickname": "required str",
    "window": "optional (defaults to your thread's window)"
   },
   "longRunning": false,
   "name": "pup_fill_card",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Fill a saved card into the checkout page of a pup window WITHOUT the digits ever reaching you: pup types them into the card fields (walking payment-processor iframes like Stripe) and returns booleans. EVERY fill is gated by a desktop approval toast naming merchant, amount and last4 \u2014 the user authorizes the specific charge in the moment. Pass merchant+amount so the toast is honest. Allowed from cloud threads (the human click is the gate). After ok:true, screenshot to verify, then submit.",
   "timeoutSeconds": 130
  },
  {
   "example": {
    "sessionId": "wiki-bmv080",
    "view": "authed"
   },
   "hint": "Flip a WIKI pup window between the logged-in and public view IN PLACE: every wiki tab reloads with the server-side ?view=public param appended (public) or stripped (authed) \u2014 adom/wiki#126 renders the request exactly as a signed-out visitor while the session stays untouched, so there is no relaunch, no cookie wipe, no profile switch (the legacy jar relaunch survives only as a fallback for servers that ignore the param). This is the callback for the per-window taskbar jump-list task (WINDOWS ONLY) \u2014 a USER-initiated action. AI threads should NOT call this; pass wikiView:\"authed\"|\"public\" on pup_open_window instead.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "view": "required \"authed\"|\"public\"|\"toggle\" \u2014 toggle flips whatever the window currently shows (the Alt+Shift+V hotkey form)"
   },
   "longRunning": false,
   "name": "pup_wiki_set_view",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Flip a WIKI pup window between the logged-in and public view IN PLACE: every wiki tab reloads with the server-side ?view=public param appended (public) or stripped (authed) \u2014 adom/wiki#126 renders the request exactly as a signed-out visitor while the session stays untouched, so there is no relaunch, no cookie wipe, no profile switch (the legacy jar relaunch survives only as a fallback for servers that ignore the param). This is the callback for the per-window taskbar jump-list task (WINDOWS ONLY) \u2014 a USER-initiated action. AI threads should NOT call this; pass wikiView:\"authed\"|\"public\" on pup_open_window instead.",
   "timeoutSeconds": 45
  },
  {
   "example": {},
   "hint": "Start the HUMAN annotation overlay on a window's active tab (pen/arrow/box/text toolbar injected in-page; also on every window's taskbar jump list as \"Annotate this page\"). Two capture modes the user can also flip in the toolbar: \"auto\" (default) captures the instant the mouse lifts \u2014 the one-red-circle case; \"multi\" accumulates shapes (color swatches: red/blue/yellow one click, more in a flyout) and captures ONCE on Done. Done captures a PNG AND leaves the drawing in the DOM, so the owning thread's next pup_screenshot shows exactly what the user marked \u2014 check session.lastAnnotation via pup_list_windows. action:\"cancel\" removes the overlay.",
   "input": {
    "action": "optional \"cancel\"",
    "mode": "optional \"auto\"|\"multi\"",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_annotate",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Start the HUMAN annotation overlay on a window's active tab (pen/arrow/box/text toolbar injected in-page; also on every window's taskbar jump list as \"Annotate this page\"). Two capture modes the user can also flip in the toolbar: \"auto\" (default) captures the instant the mouse lifts \u2014 the one-red-circle case; \"multi\" accumulates shapes (color swatches: red/blue/yellow one click, more in a flyout) and captures ONCE on Done. Done captures a PNG AND leaves the drawing in the DOM, so the owning thread's next pup_screenshot shows exactly what the user marked \u2014 check session.lastAnnotation via pup_list_windows. action:\"cancel\" removes the overlay.",
   "timeoutSeconds": 15
  },
  {
   "example": {},
   "hint": "TEST SEAM: simulate a user click on the floaty pencil (fires the same host click handler a mouse does; the sweep then starts annotate within ~4s). Built for the recurring floaty ralph \u2014 assert the annotate overlay appears after calling this.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "tabId": "optional tab"
   },
   "longRunning": false,
   "name": "pup_floaty_click",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "TEST SEAM: simulate a user click on the floaty pencil (fires the same host click handler a mouse does; the sweep then starts annotate within ~4s). Built for the recurring floaty ralph \u2014 assert the annotate overlay appears after calling this.",
   "timeoutSeconds": 10
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Starts video recording of the page for this session.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_record_start",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "one recording per session \u2014 stop the current one before starting another"
   ],
   "related": [
    "pup_record_stop",
    "pup_record_status"
   ],
   "statusVerb": null,
   "summary": "Start recording the page (video).",
   "timeoutSeconds": 20
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Stops recording and returns the saved file path.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_record_stop",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "no-op if nothing is recording for this session"
   ],
   "related": [
    "pup_record_start",
    "pup_record_list"
   ],
   "statusVerb": null,
   "summary": "Stop recording; returns the saved file.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "Whether a recording is active for the session.",
   "input": {
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_record_status",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_record_start",
    "pup_record_stop"
   ],
   "statusVerb": null,
   "summary": "Recording status for a session.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "Lists saved page recordings.",
   "input": {},
   "longRunning": false,
   "name": "pup_record_list",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [
    "pup_record_start"
   ],
   "statusVerb": null,
   "summary": "List recordings.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "Open pup's Chrome screen-share recorder HUD (WebM; consent = the visible HUD). Canonical name (pup-bridge#94).",
   "input": {
    "reason": "str (required)"
   },
   "longRunning": false,
   "name": "pup_recorder_open",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Open pup's Chrome screen-share recorder HUD (WebM; consent = the visible HUD). Canonical name (pup-bridge#94).",
   "timeoutSeconds": 20
  },
  {
   "example": {},
   "hint": "Close the Chrome screen-recorder HUD (auto-stops a clip).",
   "input": {},
   "longRunning": false,
   "name": "pup_recorder_close",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Close the Chrome screen-recorder HUD (auto-stops a clip).",
   "timeoutSeconds": 15
  },
  {
   "example": {},
   "hint": "Start a whole-screen recording through Chrome screen-share (WebM VP9, system audio + mic). Canonical name; for a background no-browser MP4 of one window use ab-core desktop_record_window_start.",
   "input": {
    "audio": "bool",
    "confirmDesktopNotTabRecording": "true",
    "fps": "int",
    "maxDurationMs": "int",
    "reason": "str (required)"
   },
   "longRunning": false,
   "name": "pup_screen_record_start",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Start a whole-screen recording through Chrome screen-share (WebM VP9, system audio + mic). Canonical name; for a background no-browser MP4 of one window use ab-core desktop_record_window_start.",
   "timeoutSeconds": 20
  },
  {
   "example": {},
   "hint": "Stop the Chrome screen recording; returns the .webm path.",
   "input": {},
   "longRunning": false,
   "name": "pup_screen_record_stop",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Stop the Chrome screen recording; returns the .webm path.",
   "timeoutSeconds": 30
  },
  {
   "example": {},
   "hint": "Status of the Chrome screen recorder + any active clip.",
   "input": {},
   "longRunning": false,
   "name": "pup_screen_record_status",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Status of the Chrome screen recorder + any active clip.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "List completed Chrome screen recordings on disk.",
   "input": {},
   "longRunning": false,
   "name": "pup_screen_record_list",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "List completed Chrome screen recordings on disk.",
   "timeoutSeconds": 10
  },
  {
   "name": "pup_highfps_extend",
   "summary": "Renew the highFps lease on a window that must keep rendering while in the background.",
   "input": {
    "minutes": "int, max 120",
    "window": "optional: the pup window (default the calling thread's)"
   },
   "example": {
    "minutes": 20
   },
   "hint": "The lease auto-expires so a background window can never drain the battery; renew only while you still need it.",
   "related": [
    "pup_open_window"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "example": {
    "selector": ".download-card",
    "sessionId": "work"
   },
   "hint": "Screenshot ONE element, resolved by CSS selector OR visible text (pierces shadow DOM), scrolled into view first. The clean way to grab a single card/widget on a component-heavy page. Text matches the TIGHTEST text node \u2014 to capture the enclosing CARD, add closest (e.g. {text:\"Download\u2026\", closest:\".download-card\"}); with closest, an ambiguous phrase still lands on the real widget (skips prose that has no such ancestor). Returns base64 + filePath like pup_screenshot.",
   "input": {
    "closest": "optional selector \u2014 climb to the nearest ancestor matching it (the card container)",
    "deep": "optional bool \u2014 pierce shadow DOM (default true)",
    "padding": "optional int px around the element",
    "selector": "CSS selector (or use text)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "text": "visible text to match (or use selector)"
   },
   "longRunning": false,
   "name": "pup_screenshot_element",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Screenshot ONE element, resolved by CSS selector OR visible text (pierces shadow DOM), scrolled into view first. The clean way to grab a single card/widget on a component-heavy page. Text matches the TIGHTEST text node \u2014 to capture the enclosing CARD, add closest (e.g. {text:\"Download\u2026\", closest:\".download-card\"}); with closest, an ambiguous phrase still lands on the real widget (skips prose that has no such ancestor). Returns base64 + filePath like pup_screenshot.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "sessionId": "work"
   },
   "hint": "FULL-resolution page screenshot (NOT downscaled \u2014 for saving to disk, NOT for Read: it can exceed Claude's image limit). Use pup_screenshot for AI-readable shots.",
   "input": {
    "fullPage": "optional bool",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_screenshot_full_res",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "FULL-resolution page screenshot (NOT downscaled \u2014 for saving to disk, NOT for Read: it can exceed Claude's image limit). Use pup_screenshot for AI-readable shots.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "selector": "button.download",
    "sessionId": "work"
   },
   "hint": "Download a file THROUGH THE BROWSER, exactly as the user clicking it would \u2014 real session, real cookies, real anti-bot cookies \u2014 and Chrome saves it to the Downloads folder. This is the right verb for vendor files (datasheets, KiCad/Altium libraries, STEP 3D models) that a server-side fetch/curl can never get past the site's bot wall. Pass selector to click the actual download button (authentic), or url to force-download via an in-page a[download]. Confirms completion via Chrome's download events and returns the ACTUAL saved path + byte count + file magic. For a gated file, log in on the window first (pup_login).",
   "input": {
    "saveAs": "optional str \u2014 filename override",
    "saveDir": "optional str \u2014 default the user's Downloads folder",
    "selector": "optional str \u2014 the real download control to click",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "url": "optional str \u2014 download this URL via an in-page a[download] click"
   },
   "longRunning": false,
   "name": "pup_download",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Download a file THROUGH THE BROWSER, exactly as the user clicking it would \u2014 real session, real cookies, real anti-bot cookies \u2014 and Chrome saves it to the Downloads folder. This is the right verb for vendor files (datasheets, KiCad/Altium libraries, STEP 3D models) that a server-side fetch/curl can never get past the site's bot wall. Pass selector to click the actual download button (authentic), or url to force-download via an in-page a[download]. Confirms completion via Chrome's download events and returns the ACTUAL saved path + byte count + file magic. For a gated file, log in on the window first (pup_login).",
   "timeoutSeconds": 100
  },
  {
   "example": {
    "selector": "input[type=file]",
    "filePath": "C:/Users/me/Downloads/gerbers.zip"
   },
   "hint": "Chrome reads the file itself, so a 5 GB file costs the same as a small one: never base64 a file through pup_eval again. The path is on the PC, not in your container: adom-bridge send_files first if the file is in the container. The verb reads the input back and fires no extra events, so a page that uploads on change sends the file once. attached:true means the page has the file, not that the site has it: screenshot for the page's own progress. Key, password and browser-login files, AppData, hidden folders in the home folder, system folders and network paths are refused (errorCode path_refused).",
   "input": {
    "filePath": "required str unless filePaths: absolute path on the PC the browser runs on",
    "filePaths": "optional array of paths, for an input that takes several files",
    "selector": "optional str (the input, or its Browse button or label; hidden inputs, open shadow roots and same-origin frames are searched)",
    "text": "optional str (visible text of the Browse control, instead of selector)",
    "armForNextChooser": "optional bool (the page creates its input when Browse is clicked: arm, then pup_click the Browse control)",
    "chooserStatus": "optional bool (did the armed chooser get answered?)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_upload_file",
   "output": {
    "attached": "bool",
    "verified": "bool (read back from the page)",
    "files": "[{name, sizeBytes}]",
    "clearedByPage": "bool (the page took the file and cleared the input)",
    "acceptWarning": "optional str"
   },
   "pitfalls": [
    "Do not click a Browse button without arming first: the OS dialog opens in front of the user and freezes the window.",
    "A missing path, two files on a single-file input and a disabled input are refused here because Chrome would accept them silently."
   ],
   "related": [
    "pup_click",
    "pup_screenshot",
    "pup_wait",
    "pup_download"
   ],
   "statusVerb": null,
   "summary": "Upload a file from this PC to a page: put it on the page's <input type=file> with no OS file picker, in the background, at any size.",
   "timeoutSeconds": 20
  },
  {
   "example": {},
   "hint": "",
   "input": {},
   "longRunning": false,
   "name": "pup_launch",
   "output": {
    "success": "bool"
   },
   "pitfalls": [
    "sessionId is OPTIONAL: omit it and your thread gets its OWN window (keyed on your --ai-thread name; later opens become tabs in it). Pass one only for a deliberate separate window \u2014 it is then your handle for every later call",
    "profile defaults to the durable shared 'adom-you' profile (logins/localStorage persist + are shared across all pup windows); isolated:true for a private throwaway \u2014 omit it for a plain \"open in pup\"",
    "sessionIds are SHARED across all AI threads on this desktop \u2014 a generic id (\"wiki\", \"test\") may collide with another thread's window and navigating it away steals their work. Use a task-prefixed sessionId + pass owner:\"<your-task>\" so pup can protect it",
    "reusing an existing sessionId with a new url NAVIGATES that window \u2014 check pup_status (shows owner per window) first if unsure"
   ],
   "related": [
    "pup_open_window"
   ],
   "statusVerb": null,
   "summary": "Alias of pup_open_window (url defaults to about:blank).",
   "timeoutSeconds": 30
  },
  {
   "example": {},
   "hint": "Report pup dependency health (wiki issue #15): depsPresent, selfInstalledThisBoot, lastError. Use this to answer \"is pup broken?\" without reading spawn logs.",
   "input": {},
   "longRunning": false,
   "name": "pup_deps",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Report pup dependency health (wiki issue #15): depsPresent, selfInstalledThisBoot, lastError. Use this to answer \"is pup broken?\" without reading spawn logs.",
   "timeoutSeconds": 10
  },
  {
   "example": {},
   "hint": "Status/trigger for the toolbar extension, which AUTO-INSTALLS invisibly at every profile launch (transient pipe-launch + CDP Extensions.loadUnpacked; persists in the profile). Running profiles pick it up on their next launch; cold profiles install immediately.",
   "input": {
    "profile": "optional, default adom-you"
   },
   "longRunning": false,
   "name": "pup_install_toolbar",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Status/trigger for the toolbar extension, which AUTO-INSTALLS invisibly at every profile launch (transient pipe-launch + CDP Extensions.loadUnpacked; persists in the profile). Running profiles pick it up on their next launch; cold profiles install immediately.",
   "timeoutSeconds": 30
  },
  {
   "example": {
    "dryRun": true
   },
   "hint": "Delete LEAKED isolated profile directories \u2014 the iso-* throwaway jars minted by isolated:true opens (and by wikiView:public before 2.0.132, when the public view moved to the server-side ?view=public param) whose window is long gone (they used to pile up, 100s per machine). Only removes an iso-* dir that no live session or session file owns, that no Chrome is holding, and that is >15 min old; durable jars (adom-you, named profiles) are NEVER touched. Runs automatically every 15 min too. dryRun:true previews the list without deleting.",
   "input": {
    "dryRun": "optional bool: list, delete nothing",
    "staleDays": "optional int, default 14",
    "isoOnly": "optional bool: only throwaway iso-* profiles",
    "all": "optional bool: ignore the idle age (still never a protected, open or needed profile)"
   },
   "longRunning": false,
   "name": "pup_reap_profiles",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Delete pup profiles nobody uses: by default every profile idle for staleDays (14) that is not protected (adom-you, adom-wiki-authed, pup-dashboard, _recorder), not open in a browser, and not needed to restore a saved window; with isoOnly:true only leaked throwaway iso-* profiles. dryRun:true lists without deleting. pup's own boot pass runs the isoOnly form.",
   "timeoutSeconds": 20
  },
  {
   "name": "pup_cache_prune",
   "summary": "Delete superseded browser builds and leftover partial downloads from pup's browser download cache, keeping the build pup drives and anything in use.",
   "input": {
    "dryRun": "optional bool: report what would go, delete nothing"
   },
   "example": {
    "dryRun": true
   },
   "hint": "pup runs this a minute after every boot (2.0.677). Reports removed (path, MB), kept (with why) and freedMB.",
   "related": [
    "pup_readiness",
    "pup_reap_profiles"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "name": "pup_sweep_identities",
   "summary": "Unregister every pup taskbar identity (registry key and any Start Menu anchor) that no live window holds, and delete stale tile/icon generations.",
   "input": {},
   "example": {},
   "hint": "pup runs this a minute after every boot and releases a window identity on close (2.0.666+); on demand it is for a box that ran older builds. Returns unregistered, shortcutsRemoved, iconDirsRemoved.",
   "related": [
    "pup_audit_icons"
   ],
   "timeoutSeconds": 30,
   "output": {},
   "pitfalls": [],
   "longRunning": false,
   "statusVerb": "pup_readiness"
  },
  {
   "example": {
    "confirm": true
   },
   "hint": "There is no dry run: list what you would lose first with pup_status (profiles, windows) and pup_credentials.",
   "input": {
    "confirm": "required bool true: actually remove"
   },
   "longRunning": false,
   "name": "pup_uninstall",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "DESTRUCTIVE: remove pup's on-disk footprint from this machine: every pup profile and saved window, the staged extensions, the taskbar icons and tiles, and every taskbar identity pup registered. Pass confirm:true. The saved-login vault is kept (credential_delete removes logins one by one), and Adom Bridge's bridge_uninstall removes the pup program itself.",
   "timeoutSeconds": 60
  },
  {
   "example": {},
   "hint": "MOVE this pup window's page into the user's Hydrogen workspace: the active tab's URL is handed to Hydrogen (ah) on this machine via its Browser Picker (webview by default, honoring the user's per-domain choice), then the pup window closes \u2014 its owner initiated the move. keepWindow:true keeps the pup window open too. Needs Hydrogen running locally (hydrogen_unreachable otherwise; the window is left untouched on failure). Cloud Hydrogen (hc) is not yet supported.",
   "input": {
    "keepWindow": "optional bool \u2014 hand the URL over but keep the pup window open",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)"
   },
   "longRunning": false,
   "name": "pup_move_to_workspace",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "MOVE this pup window's page into the user's Hydrogen workspace: the active tab's URL is handed to Hydrogen (ah) on this machine via its Browser Picker (webview by default, honoring the user's per-domain choice), then the pup window closes \u2014 its owner initiated the move. keepWindow:true keeps the pup window open too. Needs Hydrogen running locally (hydrogen_unreachable otherwise; the window is left untouched on failure). Cloud Hydrogen (hc) is not yet supported.",
   "timeoutSeconds": 25
  },
  {
   "example": {
    "label": "your Google account",
    "reason": "clicking your Google account to finish signing into Autodesk",
    "sessionId": "adsk",
    "x": 900,
    "y": 379
   },
   "hint": "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 \u2014 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 \u2014 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.",
   "input": {
    "label": "required str \u2014 WHAT you are clicking (e.g. \"your Google account\")",
    "maxWaitMs": "optional int \u2014 how long to wait for the user (default 120000)",
    "reason": "required str \u2014 WHY you must click (shown to the user as a caption)",
    "sessionId": "optional str \u2014 OMIT IT: zero-id addressing resolves your --ai-thread name to your own window. Pass a window label only when one thread runs several windows (`window` is the new name for this param; sessionId is the legacy alias)",
    "x": "required int \u2014 WINDOW-image pixel of the target (from desktop_screenshot_window)",
    "y": "required int"
   },
   "longRunning": false,
   "name": "pup_assisted_click",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "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 \u2014 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 \u2014 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.",
   "timeoutSeconds": 130
  },
  {
   "name": "pup_import_scan",
   "summary": "List the Chromium browser profiles on this desktop that have saved passwords (Chrome/Edge/Brave). Windows-only. Read-only discovery; the decrypt is a separate, consented step.",
   "input": {},
   "output": {
    "success": "bool"
   },
   "timeoutSeconds": 60,
   "statusVerb": null,
   "longRunning": false,
   "example": {},
   "hint": "Run this first to see what pup_import_browser_logins can pull.",
   "related": [
    "pup_import_browser_logins",
    "pup_credentials"
   ],
   "pitfalls": []
  },
  {
   "example": {
    "hostFilter": [
     "snapeda",
     "ultralibrarian",
     "digikey"
    ]
   },
   "hint": "Answers consent_pending while the user approves; call again with the SAME arguments to collect the result (no second prompt). The answer says via:\"nb\" or via:\"ab-helper\", and which profile was read. no_data means that profile has no saved passwords: pass profile:\"<key from nbrowser_profiles>\" for another.",
   "input": {
    "hostFilter": "optional str or str[]: only import logins whose host contains one of these",
    "sessionFor": "optional str: one site (e.g. \"x.com\") whose signed-in session to bring over instead of passwords",
    "profile": "optional str: the browser profile nb reads, as nbrowser_profiles lists it (e.g. \"chrome:[email protected]\"); default the active one",
    "browsers": "optional str[] (helper fallback only): subset of [\"chrome\",\"edge\",\"brave\"]",
    "sourceProfile": "optional str (helper fallback, sessionFor): which \"<browser> / <profile>\" to take when several have a session",
    "reason": "optional str: shown to the user with the prompt"
   },
   "longRunning": false,
   "name": "pup_import_browser_logins",
   "output": {
    "success": "bool"
   },
   "pitfalls": [],
   "related": [],
   "statusVerb": null,
   "summary": "Seed pup's vault from the user's OWN saved browser passwords (or, with sessionFor, one site's signed-in session), so pup can sign in as them on sites they already use. Through nb, the browser exports its own store and the user answers one Windows Hello; where nb cannot run (not installed, too old, no extension connected) pup falls back to Adom Bridge's decrypt helper (a consent and a Windows admin prompt; Edge yes, Chrome 156+ no). Returns COUNTS ONLY; values are never logged or returned.",
   "timeoutSeconds": 190
  },
  {
   "name": "pup_vault_sync",
   "summary": "Merge this desktop's adom-you logins with another desktop's, BOTH directions, so both boxes hold the union (newest-wins on conflict). One consent toast on this box; passwords move pup-to-pup and never touch an AI.",
   "input": {
    "peer": "required str (the other desktop's name from `targets`)",
    "reason": "required str, 8+ chars, shown to the user"
   },
   "output": {
    "success": "bool"
   },
   "timeoutSeconds": 60,
   "statusVerb": null,
   "longRunning": false,
   "example": {},
   "hint": "Use when two of the user's desktops have each learned different logins and you want both complete. Not a one-way push: it is a real union.",
   "related": [
    "pup_credentials",
    "pup_import_browser_logins"
   ],
   "pitfalls": []
  },
  {
   "name": "pup_vault_export",
   "summary": "Internal to pup_vault_sync: hand this box's stored logins (with passwords) to a same-account peer desktop's pup. Refused for cloud/relay callers.",
   "input": {},
   "output": {
    "success": "bool"
   },
   "timeoutSeconds": 30,
   "statusVerb": null,
   "longRunning": false,
   "example": {},
   "hint": "You do not call this directly; pup_vault_sync calls it on the peer.",
   "related": [
    "pup_vault_sync"
   ],
   "pitfalls": []
  },
  {
   "name": "pup_vault_import",
   "summary": "Internal to pup_vault_sync: merge a peer desktop's logins into this box's vault (newest-wins). Refused for cloud/relay callers.",
   "input": {
    "entries": "array of {host, username, password, updatedAt}"
   },
   "output": {
    "success": "bool"
   },
   "timeoutSeconds": 30,
   "statusVerb": null,
   "longRunning": false,
   "example": {},
   "hint": "You do not call this directly; pup_vault_sync calls it on the peer.",
   "related": [
    "pup_vault_sync"
   ],
   "pitfalls": []
  }
 ],
 "notes": [
  {
   "name": "pup_NATIVE_INPUT_WARNING",
   "summary": "\u26d4 READ BEFORE USING AD's desktop_click / desktop_type / desktop_press_key ON A PUP WINDOW. Native OS input does NOT target a window \u2014 it goes to WHATEVER WINDOW HAS OS FOCUS at that instant. This user runs many AI threads and alt-tabs away from a foregrounded window almost immediately. If you fire native input without warning them, your keystrokes land in THEIR document (this happened: a username was typed into the user's own window). REQUIRED: notify_user toast + desktop_caption FIRST, tell them explicitly \"don't type or click until I say done\", wait until the window is genuinely foreground, re-verify immediately before EACH event, and caption when done. pup_assisted_click already does all of this \u2014 use it. And ALWAYS prefer CDP input (pup_click/pup_type/pup_hover): it needs no foreground and disturbs nobody. A native verb returning ok:true means the event was dispatched to the OS, NOT that it reached your window."
  },
  {
   "name": "pup_eval_frame_note",
   "summary": "pup_eval accepts frame:\"<name|url-substring|index>\" to run inside an IFRAME (login forms usually live in one). Cross-origin frames work. Omit for the top document; a bad value returns frame_not_found WITH the list of frames in the tab."
  }
 ]
}