app
Adom Bridge
Public Made by Adomby adom
Adom Bridge unleashes your AI onto your full PC: full power, full safety. The AI breaks out of the container it runs in and onto your real machine, managing and running your entire laptop as you: launch apps, move files, drive any window, control KiCad, Fusion 360 or your real signed-in browser. Works with any AI, cloud or local (Claude Desktop, Claude Code, Codex), no Hydrogen required. Bridge and its bridges are an operating system for AI, with managed Node and Python runtimes, and a human-onl
← Commit history
Publish 2.1.137
9 files changed
+271−241
SKILL.md+1−1@@ -1885,7 +1885,7 @@ Architectural notes for Claude: ### Shell — `shell_execute` (escape hatch only) -- `shell_execute` -- Run a shell command on the desktop. The CLI handles approval polling internally — it returns the final `{success, output, error, exitCode}` once the user clicks Allow on the desktop dialog. **You do not need to poll, retry, or call `get_deferred_result` yourself.** The CLI emits `HINT:` lines to stderr while waiting (every 15s) so an AI Monitor sees progress. +- `shell_execute` -- Run a shell command on the desktop. The CLI handles approval polling internally — it returns the final `{success, output, error, exitCode}` once the user clicks Allow on the desktop dialog. 2.1.132+: ab holds the prompt for `approvalWaitSec` (default 60, max 600); a lapse answers `errorCode:"approval_timeout"` (nobody answered, NOT a deny: re-send with a longer wait or `request_shell_approval {wait:true}`), an explicit Deny answers `errorCode:"approval_denied"`. **You do not need to poll, retry, or call `get_deferred_result` yourself.** The CLI emits `HINT:` lines to stderr while waiting (every 15s) so an AI Monitor sees progress. - `shell_kill_all` -- Kill all running shell commands and deny pending approvals. **v1.7.16 fix.** Earlier versions (v1.7.15 specifically) had a regression where the CLI's approval-wait loop re-sent the command every 1s instead of polling for the deferred result; that produced an infinite "Another shell command is already waiting for approval" loop because each retry created a fresh approval the user could never out-click. v1.7.16 fixes it. If you see that error string with a `>= 1.7.16` CLI, the bug is back — file an issue. If you see it with `< 1.7.16`, upgrade the CLI: `adom-wiki asset get apps/adom-bridge docker_binary -o /usr/local/bin/adom-bridge`.
dist/linux/adom-bridge⋯ 1 unchanged line ⋯
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-bridge", - "version": "2.1.136", + "version": "2.1.137", "type": "app", "brief": "Adom Bridge unleashes your AI onto your full PC: full power, full safety. The AI breaks out of the container it runs in and onto your real machine, managing and running your entire laptop as you: launch apps, move files, drive any window, control KiCad, Fusion 360 or your real signed-in browser. Works with any AI, cloud or local (Claude Desktop, Claude Code, Codex), no Hydrogen required. Bridge and its bridges are an operating system for AI, with managed Node and Python runtimes, and a human-only permission system keeps you in charge of every side-effecting command.", "description": "Install this package in a cloud or Hydrogen Desktop container, not on the PC. It delivers two things: the Linux adom-bridge-cli (linked onto PATH) and the Claude skill set that teaches an AI every desktop verb (shell, files, screenshots, windows, notifications, KiCad, Fusion 360, browser automation). The CLI drives the Adom Bridge app running on your Windows PC over the relay; the Windows app itself is the separate installer in the Download box, not part of this package. Run adom-wiki pkg update to keep the CLI and skills current.",
page.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-bridge",- "version": "2.1.136",+ "version": "2.1.137", "type": "app", "description": "Install this package in a cloud or Hydrogen Desktop container, not on the PC. It delivers two things: the Linux adom-bridge-cli (linked onto PATH) and the Claude skill set that teaches an AI every desktop verb (shell, files, screenshots, windows, notifications, KiCad, Fusion 360, browser automation). The CLI drives the Adom Bridge app running on your Windows PC over the relay; the Windows app itself is the separate installer in the Download box, not part of this package. Run adom-wiki pkg update to keep the CLI and skills current.", "keywords": [
skills/adom-bridge-direct-api/SKILL.md+12@@ -313,6 +313,18 @@ silently no-ops** (ab comes back on the OLD version). So: Hydrogen initiated the apply, so Hydrogen already knows the restart is intentional — treat ab's disappearance in this window as expected, not a crash. +### Permission decision history (2.1.135+, wiki #191) + +`POST /permission/dismiss` bodies ab sends to Hydrogen now carry `decidedAt` (RFC3339 UTC, the origin's stamp) and `requestedAt` next to `id`, `decision`, `decidedOn`. A window that was away catches up with the verb `permission_history {sinceSeq, since, limit}` (`POST /command {"command":"permission_history"}` on this API): decided prompts merged with their request (type, verb, summary, reason, requestedAt) plus decision, decidedOn, decidedBy, decidedAt, cursor-style like `notify_events`; `pending` lists the prompts still live on the box. Durable in `~/.adom/permission-history.jsonl` (size-capped). + +### `GET /open/<bridge>` (2.1.133+, wiki #201) + +A STABLE address for a bridge's own web page (a dashboard). The bridge's runtime port is ab-allocated and can move (a Windows exclusion range after a boot, a respawn after repeated failures), so a browser tab or bookmark pointing at `http://127.0.0.1:<bridge port>/` dies after a sleep with "refused to connect". ab's own port does not move, so `http://127.0.0.1:24680/open/kicad` resolves the live port at click time, starts a stopped dynamic bridge if needed, and answers `302 Location: http://127.0.0.1:<live port>/`. Optional `?path=/sub/page` (must be an absolute path; scheme/host are refused). Unknown bridge → 404 `{errorCode:"unknown_bridge"}`; a bridge that cannot be started → 503 HTML page that retries every 5 s. Every `bridge_list` row carries it as `stableUrl`. Put THIS on cards, in bookmarks and in "open the dashboard" links, never the bridge port. + +```bash +curl -si http://127.0.0.1:24680/open/kicad | head -3 # HTTP/1.1 307 ... Location: http://127.0.0.1:51624/ +``` + ### `POST /update/check` Force a manifest check now. If a newer version is found, ab stages it **in the background**;
skills/adom-bridge-permissions/SKILL.md+16−3@@ -90,9 +90,19 @@ The refusal itself already surfaced a human-facing prompt on the machine: the in This is ungated — it only SURFACES, never grants. It re-shows the in-window approval dialog, fires the toast, and brings ab to the front, so "there is nothing to click" is never true. -2. **Ask the human** (in your conversation) to click **Approve 1 hr**, **Approve 24 hr**, or **Bypass Permissions** in the Adom Bridge window or on the toast. +2. **Tell the human** (in your conversation) to click **Approve 1 hr**, **Approve 24 hr**, or **Auto mode** in the Adom Bridge window, on the toast, or in Hydrogen when ab is embedded there. Say the exact switch: a "bypass permissions" mode in the AI app (Claude Code / Codex) is a different switch and does not cover this. -3. **RETRY the original verb unchanged** (with its `reason`). One grant covers the whole exec/write/registry/kill/input class for the granted window. +3. **WAIT FOR THE CLICK WITH THE VERB, never by idling.** A turn-based agent that stops after step 2 waits blindly until the human types "approved". Instead: + + ```bash + adom-bridge request_shell_approval '{"wait":true,"timeoutSec":600}' + # -> {granted:true, source:"dialog"|"toast"|"settings"|"card-or-hydrogen"|"decided-on:<box>", expiresInSec, permanent} + # -> {granted:false, pending:true, timedOut:true} when nobody clicked within the budget (ask again, or stop) + ``` + + It blocks up to `timeoutSec` (the CLI stretches its own budget to match) and answers the moment a grant lands on ANY surface (v2.1.100, wiki #185). Every grant/revoke also appears in `notify_events` as `shell_granted` / `shell_revoked`. + +4. **RETRY the original verb unchanged** (with its `reason`). One grant covers the whole exec/write/registry/kill/input class for the granted window. For anything long (a `send_files` of a big file, a `run_script` that takes minutes) send the retry with `responseMode:"job"` and follow `job_events {jobId}` / `job_status {jobId}` (v2.1.50, wiki #154) instead of holding one call open across the relay timeout. ## Grants are HUMAN-ONLY — you cannot self-grant @@ -151,8 +161,11 @@ If `willPrompt` is true and no human is at the machine, get the grant FIRST (`re | Situation | What you do | |---|---| | `errorCode: reason_required` | Resend the SAME call plus `reason:"<one plain sentence>"` | -| `errorCode: needs_approval` | `request_shell_approval`, ask the human to click Approve, retry | +| `errorCode: needs_approval` | tell the human which switch, `request_shell_approval {wait:true,timeoutSec}` until `granted`, retry (long work: `responseMode:"job"`) | | `errorCode: human_only` | You tried to self-grant. Ask the human instead | +| `errorCode: approval_denied` (shell_execute) | A human clicked Deny. Do not auto-retry; ask them | +| `errorCode: approval_timeout` (shell_execute, 2.1.132+) | Nobody answered within `approvalWaitSec` (default 60 s, max 600). NOT a deny: re-send with a longer `approvalWaitSec` for a long install, or `request_shell_approval {wait:true,timeoutSec}` then retry. `timeoutSeconds` bounds the RUN, never this wait | | `errorCode: denied_by_user` | The human said no. Do not auto-retry; ask them | | Planning an unattended run | Check `status.permissions.shell.willPrompt` first | +| "Was that prompt allowed or denied, and when?" | `permission_history {sinceSeq}` (2.1.135+): decided prompts with decision, decidedOn, decidedBy, decidedAt, requestedAt; `pending` lists the ones still live | | Need to drop a grant | `shell_auto_approve {"duration_secs":0}` (allowed for any caller) |
skills/adom-bridge-recording/SKILL.md+5−1@@ -10,10 +10,14 @@ description: Record the user's screen with Adom Bridge's NATIVE recorder (v2.1.3 - Start: `desktop_record_start {"monitor":0, "fps":30, "audio":true, "maxDurationMs":600000, "reason":"..."}` -> `{recordingId, path, encoder, hardwareEncoder, audio, audioNote?}`. `encoder` is the H.264 MFT Media Foundation chose; `hardwareEncoder:true` = NVENC / Quick Sync / AMF. - Stop: `desktop_record_stop {"recordingId"}` (id optional when one take is running) -> the finalized file, bytes, frames, audioFrames. Pull it with `pull_file`. - Stop is BOUNDED (v2.1.104, wiki #189): it waits up to 15 s for the encoder to close the MP4. If the file is still being finalised it answers `{state:"finalising", stillRunning:true, frames, bytes}` and KEEPS the take registered: poll `desktop_record_status {recordingId}` until `state` is `finalised` (or `failed` with `finaliseError`), then call `desktop_record_stop {recordingId}` once more to collect it. A take is never dropped. The file is fragmented MP4 and grows on disk while recording, so `bytes` in status is live; do not pull a file before `finalised`. -- `desktop_record_encoders` lists the codecs this box can hardware-encode: `{codecs:{h264:{available,hardware,encoders:[{name,hardware}]}, h265:{...}}, default:"h264"}`. h264 is the default (most compatible). **HEVC works** too: `codec:"h265"` records an HEVC MP4 (~40% smaller at similar quality; needs a modern player). HEVC uses the GPU path (a D3D11 VideoProcessor makes NV12 for the hardware H.265 encoder), so `gpu:false` is ignored for h265. `bitrate` (bits/s) and `fps` are also start args. +- `desktop_record_encoders` lists the codecs this box can hardware-encode: `{codecs:{h264:{available,hardware,encoders:[{name,hardware}]}, h265:{...}}, default:"h264"}`. h264 is the default (most compatible). **HEVC works** too: `codec:"h265"` records an HEVC MP4 (~40% smaller at similar quality; needs a modern player). HEVC uses the GPU path (a D3D11 VideoProcessor makes NV12 for the hardware H.265 encoder), so `gpu:false` is ignored for h265. `fps` is a start arg too. +- **Choosing a mode (read this before you pass any encoder option):** say nothing and you get the tuned default: peak-constrained VBR, exact length, a still screen costs almost nothing, fast motion stays sharp. Pass `bitrate` only for a hard cap you actually need (a slow disk, a size limit). Pass `quality` only on content you know is not video or noise (no cap in that mode). Pass `codec:"h265"` when the player is modern and you want half the file. The worked examples with measured numbers and the five example clips: https://wiki.adom.inc/adom/adom-bridge/files/docs/recording.md +- Rate control (v2.1.128, John: fast motion went splotchy under the old bits-per-second cap): the DEFAULT is peak-constrained VBR with a mean of 3x the old resolution-derived target (about 33 Mbit/s at 1440p30, 72 at 4K) and a peak of 2x the mean. A still screen costs almost nothing (measured 0.34 MB for a 14 s still page) and fast motion gets up to the peak instead of smearing (a noise-field stress page: 30 Mbit/s and readable text, where the old 11 Mbit/s CBR was mush). `bitrate` (bits/s) switches to CBR at that rate when you need a hard cap; `maxBitrate` sets the VBR peak; `quality` 1..100 selects constant-quality mode, which the NVENC MFT does NOT cap (185 Mbit/s on the noise page at 75), so only ask for it on content you know. The start reply and status carry `rateControl` (vbr | cbr | quality). - Status carries the encoder's own counters: `encodedFrames`, `encodeBacklog` (received minus encoded), `sinkOutstanding`, `encodeFps`, plus `droppedFrames` (pool empty, or ticks skipped because the encoder was more than 2 s behind). A rising `encodeBacklog` or a low `encodeFps` at a big resolution means the encoder cannot keep up: pass a lower `bitrate` / `fps`, or record a window instead of a 4K monitor. +- The timeline is WALL-CLOCK EXACT (v2.1.125, wiki #195). Windows Graphics Capture delivers a frame only when the screen content changes, so a still screen yields few frames and a tiny file, and a moving screen yields the full frame rate; the encoded duration equals the take's wall clock in both cases (a frame count far below fps x seconds is normal, not lost time). ab stamps every frame on the take's wall clock and, after the MP4 is closed, rewrites the container's per-frame durations from those stamps (the Media Foundation fragmented-MP4 sink gives the last frame of every 2 s fragment a duration of 1/fps whatever the real gap was, which is what used to shorten still passages). The stop/status payload reports it as `timeline: {repaired, fragments, samples, endBeforeMs, endAfterMs}` (`repaired:false` + `reason:"no fragments"` for the h265 container, which is not fragmented and already carries exact durations). A held frame is re-written at most every 2 s so any player can seek into a long still passage. - Status: `desktop_record_status {"recordingId"?}`; no id lists every registered take (`autoStopped:true` = it hit `maxDurationMs`, call stop to collect it). - List: `desktop_record_list` = finished MP4s on disk, newest first. +- Disk: `desktop_disk_free {path?}` (v2.1.127, wiki #196) -> `{drive, freeBytes, availableBytes, totalBytes, freePercent, recordingsDir}` for the drive holding the recordings folder (or any path). Read-only and ungated, so an automated run can refuse a take before it fills the disk. - Window mode: give `hwnd` or `titleContains` instead of `monitor` (or call `desktop_record_window_start`): background-capturable, no audio. - `audio:false` skips the loopback track; `audioNote` explains an automatically disabled track (no output device, unsupported mix format). - `desktop_recorder_open` / `desktop_recorder_close` are retired (errorCode `verb_retired`).
skills/adom-bridge-verbs/SKILL.md+234−233@@ -1,233 +1,234 @@-name: adom-bridge-verbs-description: The full reference for Adom Bridge's own (ab-core) verbs - every desktop_* / top-level verb the direct API and CLI expose, grouped by category, with a one-line description of what each does and what it takes/returns. Read this to discover what ab can do to a Windows PC (windows, screenshots, input, UI automation, files, shell/WSL, registry/fonts, tunnels, notifications, lifecycle, bridge management, credentials). BRIDGE verbs (kicad_*, fusion_*, pup_*, ...) are NOT here - each bridge ships its own verb skill; this is ab-core only. There is also a live, browsable version at http://127.0.0.1:24680/verbs on any machine running ab.--# Adom Bridge core verbs--Every verb below runs via the direct HTTP API - `POST http://127.0.0.1:24680/command {"command":"<verb>","args":{...}}` (loopback; `app` optional/inferred) - or the CLI: `adom-bridge <verb> '<json>'`. On bad/missing args the verb's own `_hint` returns the arg schema. A live, filterable, tabbed version (with a tab per installed bridge) is at **http://127.0.0.1:24680/verbs**.--**Scope:** these are ab-CORE verbs only. BRIDGE verbs (`kicad_*`, `fusion_*`, `pup_*`/`browser_*`, `nbrowser_*`, `blender_*`, ...) are owned + documented by each bridge - install a bridge and read its own skill (or `bridge_list` -> `sync_skills`). The live `/verbs` page shows bridge verbs too (descriptions pulled from each bridge's `describe`).---## Windows--- **`clear_window_attention`** — Clear a window's taskbar attention highlight (stop the flash + clear the tint) with no net focus change.-- **`close_window`** — Gracefully close a window (WM_CLOSE, so save prompts fire); resolves hwnd|titleContains; optional forceAfterMs kills the OWNING PROCESS only if the window outlives it. Returns measured closed/forced, never asserted.-- **`display`** — Report this desktop's monitor geometry - bounds, work area, per-monitor DPI/scale, primary, and the virtual-screen box; powers display-aware RDP sizing.-- **`enum_windows`** — Enumerate EVERY top-level window (filter by pids/className/includeOffscreen) via Win32 EnumWindows; resolves a window even before it's placed on screen.-- **`find_window`** — Resolve a window's hwnd by titleContains/className in z-order (best=topmost); returns {best, windows[]}.-- **`flash_window`** — Flash a window's taskbar button (FlashWindowEx) as an orange attention cue - until focused or a fixed count; stop:true clears it.-- **`get_foreground`** — Return the OS foreground window {hwnd, pid, title} - check before any z-order/focus change so you never steal a window the user is actively using.-- **`list_monitors`** — Enumerate all monitors with physical + logical (DIP) bounds, work area, DPI, scale, and primary flag; bounds share set_window_bounds' coordinate space.-- **`list_windows`** — List all visible top-level windows with their hwnd, title, position/rect, z-order index (z=0 is topmost), plus owner/pid/image/minimized/maximized/enabled/hung extras and a `degraded` flag when enumeration was partial (never trust a silent empty list).-- **`raise_window`** — Full raise dance for parked/no-activate/minimized windows: clear WS_EX_NOACTIVATE, restore if minimized, foreground-lock bypass; raised is measured, not asserted.-- **`screenshot_window`** — Capture one window by hwnd/titleContains via PrintWindow (works backgrounded/occluded/minimized, no focus steal); returns owned child dialogs as separate images + a coordMap.-- **`set_window_bounds`** — Move + resize a window to exact physical-pixel x/y/w/h (DPI-aware), un-maximizing first; returns the resulting rect.-- **`set_window_identity`** — Make any window present as a named app: set its AppUserModelID + icon + display name (+ relaunch command) so the taskbar, Alt-Tab, hover and pins read your app.-- **`set_window_jumplist`** — Set native jump-list tasks (max 16) on a taskbar button's right-click menu for a registered AppUserModelID; the caller supplies each task, [] clears the list.-- **`set_window_state`** — Set a window's state (maximize|minimize|restore|show|hide|bottom) without necessarily foregrounding; state:bottom atomically sends it behind others in the background.-- **`send_to_bottom`** (v2.1.33, wiki #144) — Put a window at the BOTTOM of the z-order without activating it; atomic check-and-skip when the user is interacting with it (`wasForeground`), `force:true` to demote anyway. Same code path as `set_window_state {state:"bottom"}`.-- **`get_window_identity`** (v2.1.33, wiki #147) — Read back each window's stamped `appId` / relaunch trio from its property store for a batch of `hwnds`; `appId:null` = identity drift, re-stamp with `set_window_identity`. Read-only, ungated.-- **`mesh_status` / `mesh_peers`** (v2.1.40, Adom Mesh P0) — this ab's node identity (ed25519 nodeId), its direct QUIC endpoints and the same-account peers from the Carbon KV directory + LAN mDNS. `--target <name>` tries the mesh first (direct, end-to-end encrypted, no relay) and falls back to the hc relay; a mesh answer carries `route.via = "mesh"`. Relay mode is off in P0 (cross-network reach still needs the hc relay until the Adom negotiation relay lands).-- **`clipboard_set` / `clipboard_get`** (v2.1.33, wiki #145) — MULTI-FORMAT: `clipboard_set {text?, html?, image?: path|base64 PNG}` writes every given format in ONE transaction (CF_UNICODETEXT + CF_HTML + PNG/CF_DIB) so a single paste carries all of them; `clipboard_get {formats:["text","html","image"]}` reads them back (image = PNG imageBase64 + imagePath + size) plus `available`. Gated like the other input verbs. Bridges: stop shipping clipboard helpers.-- **`taskbar`** — Paint glanceable status on any window's taskbar button: progress bar, overlay badge/icon, flash, and thumbnail tooltip/clip (ITaskbarList3). v2.1.22: a progress bar carries a fail-safe TTL (default 10 min, refreshed by every tick; `progress.ttlMs` overrides, 0 = never) so a dead painter cannot leave a frozen bar, and `{action:"list"}` enumerates everything ab currently paints so a restarted painter can sweep its own leftovers.-- **`ui_window`** — Window ops via UIA WindowPattern/TransformPattern: minimize|maximize|restore|close, or move{x,y}/resize{w,h}.-- **`wait_for_window`** — Block (bounded) until a window matching titleContains/className/pid APPEARS - or with state:"absent" until it GOES AWAY (dialog dismissed, app exited). The honest alternative to sleep-and-rescreenshot loops.-- **`window_hide`** — Hide ab's own main window; the app keeps running in the background (window_show brings it back).-- **`window_show`** — Show + focus ab's main window (CLI: desktop_window_show); per-call panel intent for the Hydrogen companion roll-up. Fired by ah's tray 'Open Adom Bridge'.--## Capture & recording--- **Whole-desktop recording is NATIVE as of v2.1.35 (wiki #143):** `record_start {monitor?, fps?, audio?, maxDurationMs?}` captures one monitor with Windows Graphics Capture plus the system audio mix (WASAPI loopback) into an H.264/AAC MP4, no browser, no picker; a persistent caption shows the user the take is running; `record_stop` finalizes, `record_status` (no id = all) and `record_list` (finished files) round it out; `record_window_*` is the single-window spelling. The response names the encoder Media Foundation chose (`encoder`, `hardwareEncoder` = NVENC / Quick Sync / AMF) and, as of v2.1.51 (wiki #149), the pipeline: `gpuZeroCopy:true` / `framePath:"gpu"` means frames stayed on the GPU end to end (WGC surface, a pooled DXGI sample, the hardware encoder through a shared D3D11 device manager: no CPU map, no memcpy, no upload); `framePath:"cpu"` is the automatic fallback when the box or driver refuses the D3D-aware chain (`gpu:false` forces it); `bitrate` (bits/s) overrides the resolution-derived target. `droppedFrames` on stop/status counts ticks the encoder was too far behind to take a pooled sample. `recorder_open/close` are retired. Chrome screen-share recording (OS picker consent, WebM) is pup's `pup_screen_record_*`, kept on purpose as a different tool.-- **`record_list`** — List completed desktop recording .webm files on disk with their sidecar metadata. (pup-served; see above)-- **`record_start`** — Start a whole-desktop recording (every on-screen pixel) via Chrome getDisplayMedia to .webm; opens the HUD if needed. Reason required.-- **`record_status`** — Report the desktop recorder HUD state and any active clip.-- **`record_stop`** — Stop the active whole-desktop clip and finalize the .webm; the HUD stays open in Ready state for the next clip.-- **`record_window_start`** — Start a native single-window recording (Windows Graphics Capture + Media Foundation) to MP4 - no picker/banner, background-capturable; target by hwnd or titleContains.-- **`record_window_status`** — Report the status of a WGC single-window recording.-- **`record_window_stop`** — Finalize a WGC single-window recording: stop capture, write the MP4 moov atom, return the on-disk path.-- **`recorder_close`** — Close the desktop recorder HUD, ending the session (auto-stops any active clip first).-- **`recorder_open`** — Open the whole-desktop recorder HUD (always-on-top window) without starting a clip; reason required.-- **`screenshot_screen`** — Capture the entire desktop (all monitors) as lossless PNG - only shows what's visually on top; prefer screenshot_window for a specific backgrounded app.--## Input & UI automation--- **`ad_scroll`** — Scroll ab's OWN window panels (bridges|activity|connections) to top/bottom or by a delta, returning post-scroll geometry so you can page and screenshot below the fold.-- **`caption`** — Show a large always-on-top, click-through text overlay on the user's desktop for narration / screen-share labels / status banners (Win32 layered window).-- **`click`** — Real OS left-click at {x,y} (screen px, image px with `space:image`+`shotId`, or a fraction of a window with {hwnd|titleContains, fx, fy} - 0..1 of the client area, DPI-proof). GLOBAL OS input via SendInput - a real pointer move at the screen level, indistinguishable from the human's own mouse, so it drives ANY window including elevated/admin apps and custom-drawn UIs (CAD, games, installers) that UIA and CDP cannot reach. The glowing Adom cursor is glided there first BY DEFAULT so the user sees WHO is driving; pass label to narrate it, or cursor:false to suppress. Requires a `reason` from a relay caller (rendered on screen).-- **`cursor`** — **VISUAL ONLY, does NOT move the mouse** (use `hover`/`move`/`click` for that). Shows/glides/hides the glowing teal Adom cursor overlay - the Adom mark + a narration label - so the human sees the Adom ecosystem is driving. Auto-hides.-- **`demo_panel`** — Floaty glass BACK / PLAY-PAUSE / NEXT / MUTE transport the USER controls during a guided demo (plus a corner X = a `stop` event). `show` creates/updates it (reason required; same-id shows persist omitted fields), `poll` drains the user's presses between beats (honor paused / next / back / mute / stop / idle_hide; `muted` rides every reply - silence your narration audio while true), `hide` ends the show; `press` (reason required) injects a synthetic button press through the real push path (tagged synthetic:true) for ralph-testing your wiring. A LOCAL caller may pass `eventCallbackUrl` (loopback http) for an instant POST per press; the 10-min idle auto-hide announces itself as `idle_hide` on both wires. A press never executes anything - it only queues an event for you. Full recipe (panel + captions + cursor pacing loop): the **adom-bridge-demo-kit** skill.-- **`find_control`** — Read-only UIA discovery: find a control by name/automationId/role under a window and report whether it's background-actionable plus its screen rect.-- **`find_controls`** — READ-ONLY: return ALL UIA controls matching a selector as an array with nth indices.-- **`mouse_move`** / **`mouse_click`** / **`mouse_double_click`** / **`mouse_right_click`** — aliases of `hover` / `click` / `double_click` / `right_click`, named so the verbs that move the mouse are findable by searching "mouse". Same behaviour, same gating.-- **`hover`** — **MOVE THE MOUSE** to {x,y} without clicking (reveals tooltips/hover states). Alias: `move`. GLOBAL OS input via SendInput - a real pointer move at the screen level, indistinguishable from the human's own mouse, so it drives ANY window including elevated/admin apps and custom-drawn UIs (CAD, games, installers) that UIA and CDP cannot reach. The glowing Adom cursor is glided there first BY DEFAULT so the user sees WHO is driving; pass label to narrate it, or cursor:false to suppress. Requires a `reason` from a relay caller (rendered on screen).-- **`navigate`** — Focus-neutrally open a URL in a browser window's active tab via UIA (SetValue the omnibox, Enter, restore prior foreground); returns an honest navigated signal.-- **`press_key`** — Send keyboard chords/shortcuts via SendInput (real keys reach chrome:// + native dialogs). Alias: send_keys.-- **`show_ui`** — Stage an in-window ab UI element and wait ~350ms for render so it can be captured via screenshot_window.-- **`type`** — Type a literal Unicode string into the focused control via SendInput (any keyboard layout); optional window/hwnd to focus first. Works on chrome:// + native dialogs.-- **`ui_capabilities`** — READ-ONLY: the foreground-hint table for every UIA verb - consult before acting to know whether a call steals the screen.-- **`ui_click`** — Background UIA InvokePattern click of a control by accessible name - no foreground/focus steal/cursor move; falls back to a foregrounding SendInput click if no pattern.-- **`ui_events`** — Drain buffered UIA events past sinceSeq (poll cursor, like bridge_log_read) and list active watches. READ-ONLY.-- **`ui_expand`** — Expand/collapse a tree node or combobox via UIA ExpandCollapsePattern (background, no focus steal).-- **`ui_focus`** — SetFocus to a UIA control; this often raises the window - the measured foreground block reports whether it did.-- **`ui_from_point`** — READ-ONLY: identify the UIA control under a pixel (screen px, or image px via space:image+shotId).-- **`ui_get`** — READ-ONLY: read a control's current value/state - Value, ToggleState, RangeValue, selected, ExpandState - whichever apply.-- **`ui_grid_get`** — READ-ONLY: read UIA GridPattern dimensions (rowCount/columnCount) and a {row,col} cell's info.-- **`ui_scroll`** — Scroll a region via UIA ScrollPattern {dir}, or bring an item into view via ScrollItemPattern (pass no dir).-- **`ui_select`** — Select a list item / tab / radio via UIA SelectionItemPattern (background).-- **`ui_selftest`** — Write the UIA capability table to ~/.adom/ui-capabilities.json; with {confirm,window} it empirically measures foreground by toggling a real control and restoring it.-- **`ui_set`** — Background UIA ValuePattern SetValue on a control by name (no foreground); optional submit commits with Enter, then restores the prior foreground window.-- **`ui_set_range`** — Set a slider/spinner value via UIA RangeValuePattern (background).-- **`ui_text`** — READ-ONLY: read the text of a document/edit/text control via UIA TextPattern.-- **`ui_toggle`** — Toggle a checkbox/switch via UIA TogglePattern in the background, or set it to {state:true|false}.-- **`ui_tree`** — READ-ONLY: dump the UI Automation accessibility subtree under a window (or a subtree scope) - see the whole controllable surface, no name guessing.-- **`ui_unwatch`** — Stop one UIA watch or all of them; the watcher thread tears its handler down within ~150ms.-- **`ui_watch`** — Subscribe to a UIA event stream and get a watchId; events flow to the activity-log and a ring buffer drained with ui_events. READ-ONLY.--## Files--- **`delete_file`** — Delete a file or directory in ab's context with typed args and no shell quoting; idempotent. Absolute path required.-- **`list_dir`** — List a directory as structured entries (name/isDir/size), dirs first then alphabetical; absolute path, no shell quoting.-- **`list_files`** — List one directory's files matching a shell-glob, optionally only those modified after a timestamp; non-recursive, newest-first.-- **`open_folder`** — Open a file or folder in Windows Explorer.-- **`open_url`** — Open a URL in the user's real default OS browser (their logged-in Edge/Chrome/Firefox), not Puppeteer - for pages a human must interact with.-- **`pull_file`** — Pull files from the desktop back to the container, streamed as 1 MiB binary WS frames (no practical size limit).-- **`read_file`** — Read a file in ab's own context (expands %VARS%/~, handles wcifs paths), replacing type/Get-Content quoting.-- **`send_files`** — Send files from the container to the desktop - lands in the configured receive dir (Downloads) or an absolute dest; handles multi-MB to hundreds-of-MB files.-- **`watch_files`** — Block until a file matching a glob appears in a directory (mtime > since) or timeoutMs elapses; polls the filesystem, no shell.-- **`write_file`** — Write a file in ab's context (parent dirs created), replacing Set-Content/echo quoting; retrieve with pull_file or run with run_script.--## Shell / WSL / process--- **`find_exe`** — Resolve an executable's absolute path without launching - searches absolute path, webdeploy version globs, Start Menu .lnk targets, then PATH.-- **`launch`** — Launch an executable by absolute path or bare name, verifying it exists first (avoids the Windows 'cannot find' dialog). Args/cwd/detached; returns pid, resolvedPath.-- **`launch_process`** — Launch a detached fire-and-forget process with an args ARRAY (no shell quoting); env/cwd/window overrides, plus breakawayFromJob/detached flags so the child survives its parent's job-object teardown (installers, updaters). Returns pid immediately; stop with process_kill.-- **`launch_rdp_client`** — Launch mstsc against an open RDP tunnel's local port with display-aware sizing; first verifies 127.0.0.1:<localPort> is listening, refusing a dead port.-- **`process_kill`** — Kill a process by pid (surgical, preferred) or image name (kills all with that name), force + child tree. Returns exitCode, stdout, stderr.-- **`process_list`** — List processes with reliable command lines via CIM (pid/name/parent/handles/memory); optional name or pid filter. Replaces tasklist/Get-Process.-- **`relaunch_self`** — Dev self-restart: spawns the visible ad-relauncher (HOST context) to redeploy from the build tree, relaunch, and verify. Poll relauncher_status.-- **`relauncher_status`** — Query the ad-relauncher's state JSON: overall status, current/per-step progress, stopping/launching version info, HOST/SANDBOX context, and log tail.-- **`request_shell_approval`** — Surface a human-clickable shell-approval prompt (in-window dialog + toast + foreground ab). The AI's lever to trigger the human grant - it never grants itself.-- **`run_script`** — Execute a base64 (or plain-body) script via cmd/powershell/bash/node/python with zero quote mangling. Returns stdout, stderr, exitCode, timedOut. Tier-2 gated.-- **`set_shell_auto_approve`** — Set or revoke the shell auto-approve grant. A GRANT from a relay caller is refused (human_only) and surfaces the human prompt; a REVOKE (duration_secs:0) is allowed for any caller.-- **`shell_execute`** — Run a shell command on the desktop (Windows cmd /c, else sh -c); Tier-2 gated escape hatch. Args command/cwd/timeoutSeconds -> stdout, stderr, exitCode.-- **`wsl_exec`** — Run a base64 bash script inside a WSL distro via canonical wsl -d <distro> -u <user>, auto UTF-16 decode. Args distro/user/scriptB64. Tier-2 gated.-- **`wsl_exec_async`** — Async held-session WSL exec for multi-minute work (golden-image bakes): spawns detached, streams to a log, returns a jobId immediately. Poll wsl_job_status. Tier-2 gated.-- **`wsl_export`** — Export a named WSL distro to a .tar (or .vhdx with vhd:true); long op returns a jobId - poll wsl_job_status. Tier-2 gated.-- **`wsl_import`** — Import a tarball as a NEW WSL2 distro (wsl --import); long op returns a jobId - poll wsl_job_status. Tier-2 gated.-- **`wsl_job_status`** — Poll an async WSL job (exec/import/export/unregister): running, done, exitCode, elapsedMs, outputTail. Read-only.-- **`wsl_list`** — List installed WSL distros (name, state, version, default) via a fast registry read (no LxssManager wake). Read-only.-- **`wsl_recover`** — Recover a wedged WSL by reaping orphaned/hung wsl.exe clients. Dry-run by default; {confirm:true} reaps; spares distros with child processes.-- **`wsl_unregister`** — Unregister (DELETE) one named WSL distro; long op returns a jobId. Irreversible; refuses the live Adom-Workspace. Tier-2 gated.--## Registry & fonts--- **`font_ensure_brand`** — Idempotently ensure the Adom brand fonts (Satoshi, Familjen Grotesk, JetBrains Mono) are installed per-user, fetching any missing. Tier-2 gated for relay callers.-- **`font_install`** — Install a .ttf/.otf font per-user with NO admin (from a local path or URL): copies to the user Fonts dir, writes HKCU, broadcasts WM_FONTCHANGE. Refuses .woff2.-- **`font_list`** — List installed Windows font families (per-user AND machine) from the HKCU/HKLM Fonts registry; read-only, ungated, optional name-substring filter.-- **`font_uninstall`** — Remove a PER-USER installed font (never touches machine fonts): drops the HKCU value, RemoveFontResourceW, deletes the file, broadcasts WM_FONTCHANGE.-- **`registry_delete`** — Delete a registry value (name set) or an entire key + subtree (name omitted). Tier-2 gated.-- **`registry_get`** — Read a Windows registry value: hive + plain (unescaped) key + optional value name. Returns exists, type, value.-- **`registry_set`** — Set a Windows registry value, creating the key path (REG_SZ/REG_DWORD). Tier-2 gated; HKLM needs elevation.--## Network / tunnels / relay--- **`connect_all`** — Connect every enabled relay server that isn't already connected (mirrors the tray 'Connect All'); returns spawned vs already-connected counts.-- **`disconnect_all`** — Close all live relay WS connections; config entries stay and can be reconnected with connect_all.-- **`kick_all`** — Force-disconnect all relay WebSocket clients (they auto-reconnect in ~5s). Use when connections are stale or duplicated.-- **`net_stats`** — TCP/socket stats: totals, listening/established/timeWait, ephemeral-port range + headroom, and per-pid socket counts (the signal ab's spawn-headroom gate uses).-- **`rdp`** — Alias for 'tunnel rdp': open a verified tunnel then launch mstsc on the same target, sized to the laptop's real screen. Args to/target/size/monitor.-- **`server_add`** — Register/upsert a relay server connection by name (url, optional authToken, autoConnect); persists to config.json. Equivalent to the GUI's Quick Add.-- **`server_list`** — List persisted relay servers with live connection status (connected, status, clientCount).-- **`server_remove`** — Disconnect (if connected) and delete a relay server entry by name. Idempotent.-- **`server_sync`** — Re-read the signed-in user's cloud profile server list (cloud-wins merge) and connect now - propagates a new or token-rotated relay to a live desktop.-- **`tunnel`** — Grouped TCP-over-relay tunneling (open|close|list|check|rdp): binds a 127.0.0.1 listener on a desktop while the relay dials the upstream; both legs verified. Args to/target/local.-- **`tunnel_check`** — Alias for 'tunnel check': on-demand per-leg health for one tunnel - ab probes its listener, relay probes upstream, names the failed leg.-- **`tunnel_close`** — Alias for 'tunnel close': tear down a tunnel or one conn; select by tunnelId, local port, or all:true (reaps leaks).-- **`tunnel_list`** — Alias for 'tunnel list': live tunnels with relay + desktop reconciliation, flagging leaked rows.-- **`tunnel_open`** — Alias for 'tunnel open': bind a verified local TCP listener to an upstream host:port over the relay. Returns tunnelId, localPort.--## Notifications--- **`notify_dismiss`** — Programmatically clear a sticky toast by its correlation id - removes it from the notification center and clears any un-consumed response; best-effort.-- **`notify_events`** — PUSH-drain every toast-button click since sinceSeq in ONE call (durable, cross-process cursor); poll this instead of notify_response per-id.-- **`notify_response`** — Poll which toast button the user clicked, by correlation id: {pending:true} until they click, then {pending:false, action}. Consumed on read.-- **`notify_user`** — Send a native branded Windows toast (title/body/level/buttons/progress); optionally focus a window on click, or wait:true to block and return the user's choice.--## Lifecycle / update / settings--- **`apply_update`** — Apply the already-staged update and relaunch ab (programmatic 'Install now'); respects busy and only runs ab's own SHA-verified signed release.-- **`bridge_check_updates`** — Read-only: for each installed bridge report current vs latest wiki version and updateAvailable. Downloads nothing; run before refresh_bridges.-- **`embedded_enter`** — Switch ab into ah-embedded mode at runtime (runtime-adopt): on ah's death ab reverts to standalone rather than exiting. Idempotent.-- **`embedded_release`** — Gracefully leave embedded mode - always reverts ab to standalone (tray restored, marker cleared); never exits the process (use shutdown to exit).-- **`embedded_status`** — Report whether ah is managing this ab: embedded flag, owner, source, enteredVia, pending relay url/name, start-hidden, and marker path.-- **`logout`** — Clear ab's cached Adom Cloud session (~/.adom/session.json) so the footer stops showing a stale email; equivalent to the GUI Sign-out.-- **`request_update_approval`** — Surface the footer 'Update available - Install now / Later' banner + foreground ab on an attended box. Does not apply the update.-- **`restart`** — Production-safe in-place restart of ab (no redeploy): reuses ad-relauncher with source=dest=the deployed exe. Poll relauncher_status until done_ok.-- **`restart_bridge`** — Kill + respawn a bridge in one call (reaps its tracked PIDs plus any orphan on its runtime port), respawning from the current cache on the same stable port.-- **`revoke_approvals`** — Clear all shell auto-approve grants and deny any pending approval requests.-- **`settings_get`** — Read ab's durable user settings (launch_on_boot, update_mode, prewarm_*, file_receive_directory, etc.) plus whether the launch-on-boot OS artifact exists. Read-only.-- **`settings_set`** — Change whitelisted ab settings (launch_on_boot applies the Startup artifact immediately); the sanctioned way for ah to forward setting changes. Tier-2 gated for relay callers.-- **`shutdown`** — Graceful ab exit - stop bridges (kicad/fusion/puppeteer) then exit ~500ms after replying. Used by ah's tray Quit.-- **`update_check`** — Force the target to check the wiki manifest now and, if newer, download + SHA-verify (stage) it in the background. Returns the update_status shape.-- **`update_demo`** — Test/demo verb: safely show any update-tier PRESENTATION (normal banner / high toast / emergency countdown dry-run) for docs/screenshots; nothing installs.-- **`update_demo_emergency`** — Test/demo verb: run the REAL emergency forced-update chain (manifest resolve, signed download+SHA, Authenticode gate, countdown) then DRY-RUN the apply - never restarts.-- **`update_set_mode`** — Set the target's auto-update policy: auto (apply on the 4h/launch poll), prompt (banner, default), or off. Persists.-- **`update_status`** — Read the app self-update state: currentVersion, latestVersion, updateAvailable/Ready, state, updateMode, and priority/forced. Read-only.--## Bridge management--- **`bridge_detail`** — Alias for bridge_info: rich detail for one bridge (log tail, resolved ports, license, full verb list, processLive).-- **`bridge_info`** — Rich detail for one bridge (alias bridge_detail): everything bridge_list returns plus logTail, resolved ports, license, full verb list, manifestUrl, processLive.-- **`bridge_install`** — Install a bridge from a wiki manifest URL: download + SHA-verify + unzip the runtime into the cache and rescan; persists the URL for auto-updates. Returns skillPkg/installSkill.-- **`bridge_kill`** — Kill all of one bridge's tracked processes (image-verified reap); it respawns on the next routed verb (or via the supervisor) on the same stable port.-- **`bridge_dev_mode`** — Freeze/unfreeze ONE bridge against ALL automatic updates (`{name, on}` writes a .dev-pin marker) so a developer's local changes in its cache are never clobbered by the sweep/on-launch sync/auto-update/refresh. `bridge_list` shows `devPinned:true`; the GUI card shows an amber "dev pin" badge with one-click Unpin. NEVER remove someone else's dev pin to "get the latest" without asking - the pin means a human is mid-development.-- **`bridge_list`** — List every installed bridge (name, version, source, ports, verbPrefixes, paused, livePids, devPinned) plus update-diagnosis fields (manifestSource, tier, lastSyncDetail with the probed tier + outcome, versionDrift) and each bridge's skillPkg/installSkill for skill discovery.-- **`bridge_log_read`** — Read/stream a bridge's log: initial tailLines, or new bytes since sinceOffset (tail -f). Returns log (bridge stdout) + adLifecycle (ab's spawn/reap audit) separately.-- **`bridge_pause`** — Pause a bridge: calls to its verb prefixes fail with bridge_paused until bridge_resume. Routing flag only - the server process is not killed.-- **`bridge_readiness`** — Read-only readiness probe across every bridge - never spawns or installs. Reports host-app installed, runtime ready, and state (ready/warming/needs-runtime/no-app).-- **`bridge_resume`** — Resume a paused bridge - flip the routing flag back on. Inverse of bridge_pause.-- **`bridge_start_all`** — Re-enable bridge spawning after bridge_stop_all/--safe-mode: flip the kill-switch ON; bridges lazy-spawn on the next routed verb.-- **`bridge_stop_all`** — Global kill-switch: disable ALL bridge spawning AND stop the running bundled bridges (kicad/fusion/browser); the one-call cure for a runaway bridge.-- **`bridge_uninstall`** — Remove a bridge from the cache (bundled ones revert to the bundled fallback, third-party fully removed); does NOT kill the running server process.-- **`install_kicad`** — Unattended KiCad install via winget (~700MB, 2-5 min); a fixed-command verb (no shell-approval). Call when kicad_list_versions returns kicad_not_installed.-- **`install_node`** — Unattended Node.js LTS install via winget; call when a browser_* verb returns node_not_found, then retry - the bridge reads node.exe from the registry, no restart.-- **`refresh_bridges`** — Force an immediate wiki sync of the bridge cache (all, or {name}); applies on-disk updates and auto-restarts a running bridge to pick up new code.-- **`refresh_heroes`** — Force-refresh ONLY the bridge ART cache (hero images + wiki meta); does not sync bridge code (use refresh_bridges).-- **`runtimes`** — Read-only status of the ab-managed portable Node/Python runtimes bridges share (state, version, source system|cache, exe). No-UAC provisioning.-- **`sync_skills`** — Reconcile this container's ~/.claude/skills with the connected desktop's bridges: pkg-install each bridge's skillPkg. Idempotent; also auto-runs daily.--## Credentials--- **`decrypt_browser_credentials`** — Decrypt the user's own saved Chrome/Edge/Brave passwords+cookies (incl. App-Bound Encryption) via an elevated helper; LOCAL callers only (a relay caller is refused `relay_forbidden`). **Requires `reason`** — the user reads it on screen while deciding. Gated by an on-screen caption naming the asking thread + what it wants, an in-ab consent dialog, and the Windows UAC prompt.--## Wiki / misc--- **`activity_filter`** — Set the human's GUI activity-log filter by bridge name and/or free-text substring; view-only, does not change what verbs the AI can call.-- **`set_project_watch`** — Enable/disable watching a KiCad project directory: on file change ab base64-syncs the project files to connected relay servers.-- **`trigger_project_watch`** — Force an immediate re-sync of the watched KiCad project's files to connected relay servers (no waiting for the poll).-- **`wiki_exec`** — Run the bundled adom-wiki CLI on the user's PC (repo/pkg/release/discussion ops) with the signed-in session token injected. Args argv/cwd/timeoutSec. Tier-2 gated.--## Other--- **`bring_to_front`** — Bring a window (by hwnd or titleContains) to the foreground, optionally maximizing or restoring it.-- **`commands`** — Return the full CLI help-map: every verb grouped by namespace with description/args/returns (aliases: list_commands, help).-- **`double_click`** — Real OS double-click at {x,y} via SendInput; optional focus-first window/hwnd.-- **`janitor`** — CLI-local screenshot-cache janitor: {sub:status} lists managed dirs/sizes/policy, {sub:run} forces a sweep now. Keeps the pull cache bounded.-- **`list_browsers`** — Catalog every installed native browser plus its profiles and signed-in account emails; the static inventory for picking browser+profile before open_url.-- **`ping`** — Round-trip connectivity test (5s timeout); optional message echoed back. Run first to verify the desktop connection.-- **`register_app_identity`** — Register an AppUserModelID with Windows (HKCU key + optional Start Menu shortcut) so it resolves to a name + icon; call once per install. shortcut:false = registry-only (nothing in the Start Menu); its known cost is that the taskbar jump list flashes on the first right-click and stays on the second (wiki #155, measured). `anchor:true` opts in to a small visible anchor .lnk inside one Start Menu subfolder (`anchorFolder`, default "Adom Bridge") that fixes it, at the price of a Start Menu entry (default off since 2.1.49: a clean Start Menu wins).-- **`right_click`** — Real OS right-click at {x,y} via SendInput to open context menus; optional focus-first window/hwnd.-- **`status`** — Query connected clients, capabilities, and app install/run status (kicad/fusion/browser), plus WSL wedge and bridge-update health.-- **`targets`** — List the Adom Bridges connected to this relay (name, hostname, version, os) so you can address one with --target <name> or --target all.-- **`timeouts`** — The per-verb HTTP timeout registry: defaults plus the long verbs mapped to their budget and the status-verb to poll on a timeout.-- **`responseMode`** on ANY verb (2.1.50, wiki #154) — `"job"`: the call returns `{jobId, state, cursor}` at once and the verb runs under that job; a gated verb PARKS on `waiting_for_approval` and resumes once on a real human decision (any surface, any mesh box) instead of failing with needs_approval. `"stream"`: the same job followed by the CLI, one JSON line per lifecycle event, result last, bounded by `streamTimeoutSeconds` (detaching never cancels).-- **`job_status`** / **`job_events`** / **`job_cancel`** (2.1.50, wiki #154) — snapshot, cursor-based event replay with long-poll (`waitMs`), and withdrawal of a parked job.-- **`app_instances`** / **`app_instance`** (2.1.50, wiki #126) — the instance-registry primitive for multi-instance apps: every running instance of an exe with a STABLE key (image:pid:start time), its windows (hwnd/title/hung), command line and a per-instance scratchDir; `app_instance` resolves exactly one by key | pid | hwnd | titleContains or refuses with `ambiguous_instance` + candidates.-- **`bridge_audit`** (2.1.50, wiki #124) — the command audit: every verb ab ran (who: thread/container/delegate, why: reason, origin relay|local|bridge, durationMs, success/errorCode), filterable by bridge/thread/verb/since/failuresOnly/installGrantOnly; in-memory ring + `~/.adom/audit/commands.jsonl`. Also returns the bridge's own persistent events.-- **`bridge_event_log`** (2.1.50, wiki #124) — a bridge appends its own operational event (dialog reap, watchdog firing, instance change) to `~/.adom/audit/bridges/<bridge>.jsonl`, which outlives its respawn; read back with `bridge_audit {bridge}`.-- **`relay_boot_hook`** (2.1.50, wiki #45; CLI-local) — `status | install | remove` a cron `@reboot` line that restarts `adom-bridge serve --supervise` after a container reboot.-- **`desktop_taskbar` thumbbar** (2.1.50, wiki #38) — `thumbbar:[{id?, icon, tooltip?, enabled?, hidden?}]` (max 7): an icon-only status strip under the hover preview; slots are added once per window and only their icons/tooltips/flags change afterwards.-- **`unregister_app_identity`** — Remove an app-identity registration: delete its HKCU AppUserModelId key and any Start Menu shortcut carrying that AUMID (root and the anchor folder; pass `anchorFolder` if you named one; an emptied folder is removed). Idempotent.-- **`user_idle_ms`** — Milliseconds since the last system-wide keyboard/mouse input (GetLastInputInfo) - yield intrusive work while the user is active.+--- +name: adom-bridge-verbs +description: The full reference for Adom Bridge's own (ab-core) verbs - every desktop_* / top-level verb the direct API and CLI expose, grouped by category, with a one-line description of what each does and what it takes/returns. Read this to discover what ab can do to a Windows PC (windows, screenshots, input, UI automation, files, shell/WSL, registry/fonts, tunnels, notifications, lifecycle, bridge management, credentials). BRIDGE verbs (kicad_*, fusion_*, pup_*, ...) are NOT here - each bridge ships its own verb skill; this is ab-core only. There is also a live, browsable version at http://127.0.0.1:24680/verbs on any machine running ab. +--- + +# Adom Bridge core verbs + +Every verb below runs via the direct HTTP API - `POST http://127.0.0.1:24680/command {"command":"<verb>","args":{...}}` (loopback; `app` optional/inferred) - or the CLI: `adom-bridge <verb> '<json>'`. On bad/missing args the verb's own `_hint` returns the arg schema. A live, filterable, tabbed version (with a tab per installed bridge) is at **http://127.0.0.1:24680/verbs**. + +**Scope:** these are ab-CORE verbs only. BRIDGE verbs (`kicad_*`, `fusion_*`, `pup_*`/`browser_*`, `nbrowser_*`, `blender_*`, ...) are owned + documented by each bridge - install a bridge and read its own skill (or `bridge_list` -> `sync_skills`). The live `/verbs` page shows bridge verbs too (descriptions pulled from each bridge's `describe`). + + +## Windows + +- **`clear_window_attention`** — Clear a window's taskbar attention highlight (stop the flash + clear the tint) with no net focus change. +- **`close_window`** — Gracefully close a window (WM_CLOSE, so save prompts fire); resolves hwnd|titleContains; optional forceAfterMs kills the OWNING PROCESS only if the window outlives it. Returns measured closed/forced, never asserted. +- **`display`** — Report this desktop's monitor geometry - bounds, work area, per-monitor DPI/scale, primary, and the virtual-screen box; powers display-aware RDP sizing. +- **`enum_windows`** — Enumerate EVERY top-level window (filter by pids/className/includeOffscreen) via Win32 EnumWindows; resolves a window even before it's placed on screen. +- **`find_window`** — Resolve a window's hwnd by titleContains/className in z-order (best=topmost); returns {best, windows[]}. +- **`flash_window`** — Flash a window's taskbar button (FlashWindowEx) as an orange attention cue - until focused or a fixed count; stop:true clears it. +- **`get_foreground`** — Return the OS foreground window {hwnd, pid, title} - check before any z-order/focus change so you never steal a window the user is actively using. +- **`list_monitors`** — Enumerate all monitors with physical + logical (DIP) bounds, work area, DPI, scale, and primary flag; bounds share set_window_bounds' coordinate space. +- **`list_windows`** — List all visible top-level windows with their hwnd, title, position/rect, z-order index (z=0 is topmost), plus owner/pid/image/minimized/maximized/enabled/hung extras and a `degraded` flag when enumeration was partial (never trust a silent empty list). +- **`raise_window`** — Full raise dance for parked/no-activate/minimized windows: clear WS_EX_NOACTIVATE, restore if minimized, foreground-lock bypass; raised is measured, not asserted. +- **`screenshot_window`** — Capture one window by hwnd/titleContains via PrintWindow (works backgrounded/occluded/minimized, no focus steal); returns owned child dialogs as separate images + a coordMap. +- **`set_window_bounds`** — Move + resize a window to exact physical-pixel x/y/w/h (DPI-aware), un-maximizing first; returns the resulting rect. +- **`set_window_identity`** — Make any window present as a named app: set its AppUserModelID + icon + display name (+ relaunch command) so the taskbar, Alt-Tab, hover and pins read your app. +- **`set_window_jumplist`** — Set native jump-list tasks (max 16) on a taskbar button's right-click menu for a registered AppUserModelID; the caller supplies each task, [] clears the list. +- **`set_window_state`** — Set a window's state (maximize|minimize|restore|show|hide|bottom) without necessarily foregrounding; state:bottom atomically sends it behind others in the background. +- **`send_to_bottom`** (v2.1.33, wiki #144) — Put a window at the BOTTOM of the z-order without activating it; atomic check-and-skip when the user is interacting with it (`wasForeground`), `force:true` to demote anyway. Same code path as `set_window_state {state:"bottom"}`. +- **`get_window_identity`** (v2.1.33, wiki #147) — Read back each window's stamped `appId` / relaunch trio from its property store for a batch of `hwnds`; `appId:null` = identity drift, re-stamp with `set_window_identity`. Read-only, ungated. +- **`mesh_status` / `mesh_peers`** (v2.1.40, Adom Mesh P0) — this ab's node identity (ed25519 nodeId), its direct QUIC endpoints and the same-account peers from the Carbon KV directory + LAN mDNS. `--target <name>` tries the mesh first (direct, end-to-end encrypted, no relay) and falls back to the hc relay; a mesh answer carries `route.via = "mesh"`. Relay mode is off in P0 (cross-network reach still needs the hc relay until the Adom negotiation relay lands). +- **`clipboard_set` / `clipboard_get`** (v2.1.33, wiki #145) — MULTI-FORMAT: `clipboard_set {text?, html?, image?: path|base64 PNG}` writes every given format in ONE transaction (CF_UNICODETEXT + CF_HTML + PNG/CF_DIB) so a single paste carries all of them; `clipboard_get {formats:["text","html","image"]}` reads them back (image = PNG imageBase64 + imagePath + size) plus `available`. Gated like the other input verbs. Bridges: stop shipping clipboard helpers. +- **`taskbar`** — Paint glanceable status on any window's taskbar button: progress bar, overlay badge/icon, flash, and thumbnail tooltip/clip (ITaskbarList3). v2.1.22: a progress bar carries a fail-safe TTL (default 10 min, refreshed by every tick; `progress.ttlMs` overrides, 0 = never) so a dead painter cannot leave a frozen bar, and `{action:"list"}` enumerates everything ab currently paints so a restarted painter can sweep its own leftovers. +- **`ui_window`** — Window ops via UIA WindowPattern/TransformPattern: minimize|maximize|restore|close, or move{x,y}/resize{w,h}. +- **`wait_for_window`** — Block (bounded) until a window matching titleContains/className/pid APPEARS - or with state:"absent" until it GOES AWAY (dialog dismissed, app exited). The honest alternative to sleep-and-rescreenshot loops. +- **`window_hide`** — Hide ab's own main window; the app keeps running in the background (window_show brings it back). +- **`window_show`** — Show + focus ab's main window (CLI: desktop_window_show); per-call panel intent for the Hydrogen companion roll-up. Fired by ah's tray 'Open Adom Bridge'. + +## Capture & recording + +- **Whole-desktop recording is NATIVE as of v2.1.35 (wiki #143):** `record_start {monitor?, fps?, audio?, maxDurationMs?}` captures one monitor with Windows Graphics Capture plus the system audio mix (WASAPI loopback) into an H.264/AAC MP4, no browser, no picker; a persistent caption shows the user the take is running; `record_stop` finalizes, `record_status` (no id = all) and `record_list` (finished files) round it out; `record_window_*` is the single-window spelling. The response names the encoder Media Foundation chose (`encoder`, `hardwareEncoder` = NVENC / Quick Sync / AMF) and, as of v2.1.51 (wiki #149), the pipeline: `gpuZeroCopy:true` / `framePath:"gpu"` means frames stayed on the GPU end to end (WGC surface, a pooled DXGI sample, the hardware encoder through a shared D3D11 device manager: no CPU map, no memcpy, no upload); `framePath:"cpu"` is the automatic fallback when the box or driver refuses the D3D-aware chain (`gpu:false` forces it); `bitrate` (bits/s) overrides the resolution-derived target. `droppedFrames` on stop/status counts ticks the encoder was too far behind to take a pooled sample. `recorder_open/close` are retired. Chrome screen-share recording (OS picker consent, WebM) is pup's `pup_screen_record_*`, kept on purpose as a different tool. +- **`record_list`** — List completed desktop recording .webm files on disk with their sidecar metadata. (pup-served; see above) +- **`record_start`** — Start a whole-desktop recording (every on-screen pixel) via Chrome getDisplayMedia to .webm; opens the HUD if needed. Reason required. +- **`record_status`** — Report the desktop recorder HUD state and any active clip. +- **`record_stop`** — Stop the active whole-desktop clip and finalize the .webm; the HUD stays open in Ready state for the next clip. +- **`record_window_start`** — Start a native single-window recording (Windows Graphics Capture + Media Foundation) to MP4 - no picker/banner, background-capturable; target by hwnd or titleContains. +- **`record_window_status`** — Report the status of a WGC single-window recording. +- **`record_window_stop`** — Finalize a WGC single-window recording: stop capture, write the MP4 moov atom, return the on-disk path. +- **`recorder_close`** — Close the desktop recorder HUD, ending the session (auto-stops any active clip first). +- **`recorder_open`** — Open the whole-desktop recorder HUD (always-on-top window) without starting a clip; reason required. +- **`screenshot_screen`** — Capture the entire desktop (all monitors) as lossless PNG - only shows what's visually on top; prefer screenshot_window for a specific backgrounded app. + +## Input & UI automation + +- **`ad_scroll`** — Scroll ab's OWN window panels (bridges|activity|connections) to top/bottom or by a delta, returning post-scroll geometry so you can page and screenshot below the fold. +- **`caption`** — Show a large always-on-top, click-through text overlay on the user's desktop for narration / screen-share labels / status banners (Win32 layered window). +- **`click`** — Real OS left-click at {x,y} (screen px, image px with `space:image`+`shotId`, or a fraction of a window with {hwnd|titleContains, fx, fy} - 0..1 of the client area, DPI-proof). GLOBAL OS input via SendInput - a real pointer move at the screen level, indistinguishable from the human's own mouse, so it drives ANY window including elevated/admin apps and custom-drawn UIs (CAD, games, installers) that UIA and CDP cannot reach. The glowing Adom cursor is glided there first BY DEFAULT so the user sees WHO is driving; pass label to narrate it, or cursor:false to suppress. Requires a `reason` from a relay caller (rendered on screen). +- **`cursor`** — **VISUAL ONLY, does NOT move the mouse** (use `hover`/`move`/`click` for that). Shows/glides/hides the glowing teal Adom cursor overlay - the Adom mark + a narration label - so the human sees the Adom ecosystem is driving. Auto-hides. +- **`demo_panel`** — Floaty glass BACK / PLAY-PAUSE / NEXT / MUTE transport the USER controls during a guided demo (plus a corner X = a `stop` event). `show` creates/updates it (reason required; same-id shows persist omitted fields), `poll` drains the user's presses between beats (honor paused / next / back / mute / stop / idle_hide; `muted` rides every reply - silence your narration audio while true), `hide` ends the show; `press` (reason required) injects a synthetic button press through the real push path (tagged synthetic:true) for ralph-testing your wiring. A LOCAL caller may pass `eventCallbackUrl` (loopback http) for an instant POST per press; the 10-min idle auto-hide announces itself as `idle_hide` on both wires. A press never executes anything - it only queues an event for you. Full recipe (panel + captions + cursor pacing loop): the **adom-bridge-demo-kit** skill. +- **`find_control`** — Read-only UIA discovery: find a control by name/automationId/role under a window and report whether it's background-actionable plus its screen rect. +- **`find_controls`** — READ-ONLY: return ALL UIA controls matching a selector as an array with nth indices. +- **`mouse_move`** / **`mouse_click`** / **`mouse_double_click`** / **`mouse_right_click`** — aliases of `hover` / `click` / `double_click` / `right_click`, named so the verbs that move the mouse are findable by searching "mouse". Same behaviour, same gating. +- **`hover`** — **MOVE THE MOUSE** to {x,y} without clicking (reveals tooltips/hover states). Alias: `move`. GLOBAL OS input via SendInput - a real pointer move at the screen level, indistinguishable from the human's own mouse, so it drives ANY window including elevated/admin apps and custom-drawn UIs (CAD, games, installers) that UIA and CDP cannot reach. The glowing Adom cursor is glided there first BY DEFAULT so the user sees WHO is driving; pass label to narrate it, or cursor:false to suppress. Requires a `reason` from a relay caller (rendered on screen). +- **`navigate`** — Focus-neutrally open a URL in a browser window's active tab via UIA (SetValue the omnibox, Enter, restore prior foreground); returns an honest navigated signal. +- **`press_key`** — Send keyboard chords/shortcuts via SendInput (real keys reach chrome:// + native dialogs). Alias: send_keys. +- **`show_ui`** — Stage an in-window ab UI element and wait ~350ms for render so it can be captured via screenshot_window. +- **`type`** — Type a literal Unicode string into the focused control via SendInput (any keyboard layout); optional window/hwnd to focus first. Works on chrome:// + native dialogs. +- **`ui_capabilities`** — READ-ONLY: the foreground-hint table for every UIA verb - consult before acting to know whether a call steals the screen. +- **`ui_click`** — Background UIA InvokePattern click of a control by accessible name - no foreground/focus steal/cursor move; falls back to a foregrounding SendInput click if no pattern. +- **`ui_events`** — Drain buffered UIA events past sinceSeq (poll cursor, like bridge_log_read) and list active watches. READ-ONLY. +- **`ui_expand`** — Expand/collapse a tree node or combobox via UIA ExpandCollapsePattern (background, no focus steal). +- **`ui_focus`** — SetFocus to a UIA control; this often raises the window - the measured foreground block reports whether it did. +- **`ui_from_point`** — READ-ONLY: identify the UIA control under a pixel (screen px, or image px via space:image+shotId). +- **`ui_get`** — READ-ONLY: read a control's current value/state - Value, ToggleState, RangeValue, selected, ExpandState - whichever apply. +- **`ui_grid_get`** — READ-ONLY: read UIA GridPattern dimensions (rowCount/columnCount) and a {row,col} cell's info. +- **`ui_scroll`** — Scroll a region via UIA ScrollPattern {dir}, or bring an item into view via ScrollItemPattern (pass no dir). +- **`ui_select`** — Select a list item / tab / radio via UIA SelectionItemPattern (background). +- **`ui_selftest`** — Write the UIA capability table to ~/.adom/ui-capabilities.json; with {confirm,window} it empirically measures foreground by toggling a real control and restoring it. +- **`ui_set`** — Background UIA ValuePattern SetValue on a control by name (no foreground); optional submit commits with Enter, then restores the prior foreground window. +- **`ui_set_range`** — Set a slider/spinner value via UIA RangeValuePattern (background). +- **`ui_text`** — READ-ONLY: read the text of a document/edit/text control via UIA TextPattern. +- **`ui_toggle`** — Toggle a checkbox/switch via UIA TogglePattern in the background, or set it to {state:true|false}. +- **`ui_tree`** — READ-ONLY: dump the UI Automation accessibility subtree under a window (or a subtree scope) - see the whole controllable surface, no name guessing. +- **`ui_unwatch`** — Stop one UIA watch or all of them; the watcher thread tears its handler down within ~150ms. +- **`ui_watch`** — Subscribe to a UIA event stream and get a watchId; events flow to the activity-log and a ring buffer drained with ui_events. READ-ONLY. + +## Files + +- **`delete_file`** — Delete a file or directory in ab's context with typed args and no shell quoting; idempotent. Absolute path required. +- **`list_dir`** — List a directory as structured entries (name/isDir/size), dirs first then alphabetical; absolute path, no shell quoting. +- **`list_files`** — List one directory's files matching a shell-glob, optionally only those modified after a timestamp; non-recursive, newest-first. +- **`open_folder`** — Open a file or folder in Windows Explorer. +- **`open_url`** — Open a URL in the user's real default OS browser (their logged-in Edge/Chrome/Firefox), not Puppeteer - for pages a human must interact with. +- **`pull_file`** — Pull files from the desktop back to the container, streamed as 1 MiB binary WS frames (no practical size limit). +- **`read_file`** — Read a file in ab's own context (expands %VARS%/~, handles wcifs paths), replacing type/Get-Content quoting. +- **`send_files`** — Send files from the container to the desktop - lands in the configured receive dir (Downloads) or an absolute dest; handles multi-MB to hundreds-of-MB files. +- **`watch_files`** — Block until a file matching a glob appears in a directory (mtime > since) or timeoutMs elapses; polls the filesystem, no shell. +- **`write_file`** — Write a file in ab's context (parent dirs created), replacing Set-Content/echo quoting; retrieve with pull_file or run with run_script. + +## Shell / WSL / process + +- **`find_exe`** — Resolve an executable's absolute path without launching - searches absolute path, webdeploy version globs, Start Menu .lnk targets, then PATH. +- **`launch`** — Launch an executable by absolute path or bare name, verifying it exists first (avoids the Windows 'cannot find' dialog). Args/cwd/detached; returns pid, resolvedPath. +- **`launch_process`** — Launch a detached fire-and-forget process with an args ARRAY (no shell quoting); env/cwd/window overrides, plus breakawayFromJob/detached flags so the child survives its parent's job-object teardown (installers, updaters). Returns pid immediately; stop with process_kill. +- **`launch_rdp_client`** — Launch mstsc against an open RDP tunnel's local port with display-aware sizing; first verifies 127.0.0.1:<localPort> is listening, refusing a dead port. +- **`process_kill`** — Kill a process by pid (surgical, preferred) or image name (kills all with that name), force + child tree. Returns exitCode, stdout, stderr. +- **`process_list`** — List processes with reliable command lines via CIM (pid/name/parent/handles/memory); optional name or pid filter. Replaces tasklist/Get-Process. +- **`relaunch_self`** — Dev self-restart: spawns the visible ad-relauncher (HOST context) to redeploy from the build tree, relaunch, and verify. Poll relauncher_status. +- **`relauncher_status`** — Query the ad-relauncher's state JSON: overall status, current/per-step progress, stopping/launching version info, HOST/SANDBOX context, and log tail. +- **`request_shell_approval`** — Surface a human-clickable shell-approval prompt (in-window dialog + toast + foreground ab). The AI's lever to trigger the human grant - it never grants itself. +- **`run_script`** — Execute a base64 (or plain-body) script via cmd/powershell/bash/node/python with zero quote mangling. Returns stdout, stderr, exitCode, timedOut. Tier-2 gated. +- **`set_shell_auto_approve`** — Set or revoke the shell auto-approve grant. A GRANT from a relay caller is refused (human_only) and surfaces the human prompt; a REVOKE (duration_secs:0) is allowed for any caller. +- **`shell_execute`** — Run a shell command on the desktop (Windows cmd /c, else sh -c); Tier-2 gated escape hatch. Args command/cwd/timeoutSeconds -> stdout, stderr, exitCode. +- **`wsl_exec`** — Run a base64 bash script inside a WSL distro via canonical wsl -d <distro> -u <user>, auto UTF-16 decode. Args distro/user/scriptB64. Tier-2 gated. +- **`wsl_exec_async`** — Async held-session WSL exec for multi-minute work (golden-image bakes): spawns detached, streams to a log, returns a jobId immediately. Poll wsl_job_status. Tier-2 gated. +- **`wsl_export`** — Export a named WSL distro to a .tar (or .vhdx with vhd:true); long op returns a jobId - poll wsl_job_status. Tier-2 gated. +- **`wsl_import`** — Import a tarball as a NEW WSL2 distro (wsl --import); long op returns a jobId - poll wsl_job_status. Tier-2 gated. +- **`wsl_job_status`** — Poll an async WSL job (exec/import/export/unregister): running, done, exitCode, elapsedMs, outputTail. Read-only. +- **`wsl_list`** — List installed WSL distros (name, state, version, default) via a fast registry read (no LxssManager wake). Read-only. +- **`wsl_recover`** — Recover a wedged WSL by reaping orphaned/hung wsl.exe clients. Dry-run by default; {confirm:true} reaps; spares distros with child processes. +- **`wsl_unregister`** — Unregister (DELETE) one named WSL distro; long op returns a jobId. Irreversible; refuses the live Adom-Workspace. Tier-2 gated. + +## Registry & fonts + +- **`font_ensure_brand`** — Idempotently ensure the Adom brand fonts (Satoshi, Familjen Grotesk, JetBrains Mono) are installed per-user, fetching any missing. Tier-2 gated for relay callers. +- **`font_install`** — Install a .ttf/.otf font per-user with NO admin (from a local path or URL): copies to the user Fonts dir, writes HKCU, broadcasts WM_FONTCHANGE. Refuses .woff2. +- **`font_list`** — List installed Windows font families (per-user AND machine) from the HKCU/HKLM Fonts registry; read-only, ungated, optional name-substring filter. +- **`font_uninstall`** — Remove a PER-USER installed font (never touches machine fonts): drops the HKCU value, RemoveFontResourceW, deletes the file, broadcasts WM_FONTCHANGE. +- **`registry_delete`** — Delete a registry value (name set) or an entire key + subtree (name omitted). Tier-2 gated. +- **`registry_get`** — Read a Windows registry value: hive + plain (unescaped) key + optional value name. Returns exists, type, value. +- **`registry_set`** — Set a Windows registry value, creating the key path (REG_SZ/REG_DWORD). Tier-2 gated; HKLM needs elevation. + +## Network / tunnels / relay + +- **`connect_all`** — Connect every enabled relay server that isn't already connected (mirrors the tray 'Connect All'); returns spawned vs already-connected counts. +- **`disconnect_all`** — Close all live relay WS connections; config entries stay and can be reconnected with connect_all. +- **`kick_all`** — Force-disconnect all relay WebSocket clients (they auto-reconnect in ~5s). Use when connections are stale or duplicated. +- **`net_stats`** — TCP/socket stats: totals, listening/established/timeWait, ephemeral-port range + headroom, and per-pid socket counts (the signal ab's spawn-headroom gate uses). +- **`rdp`** — Alias for 'tunnel rdp': open a verified tunnel then launch mstsc on the same target, sized to the laptop's real screen. Args to/target/size/monitor. +- **`server_add`** — Register/upsert a relay server connection by name (url, optional authToken, autoConnect); persists to config.json. Equivalent to the GUI's Quick Add. +- **`server_list`** — List persisted relay servers with live connection status (connected, status, clientCount). +- **`server_remove`** — Disconnect (if connected) and delete a relay server entry by name. Idempotent. +- **`server_sync`** — Re-read the signed-in user's cloud profile server list (cloud-wins merge) and connect now - propagates a new or token-rotated relay to a live desktop. +- **`tunnel`** — Grouped TCP-over-relay tunneling (open|close|list|check|rdp): binds a 127.0.0.1 listener on a desktop while the relay dials the upstream; both legs verified. Args to/target/local. +- **`tunnel_check`** — Alias for 'tunnel check': on-demand per-leg health for one tunnel - ab probes its listener, relay probes upstream, names the failed leg. +- **`tunnel_close`** — Alias for 'tunnel close': tear down a tunnel or one conn; select by tunnelId, local port, or all:true (reaps leaks). +- **`tunnel_list`** — Alias for 'tunnel list': live tunnels with relay + desktop reconciliation, flagging leaked rows. +- **`tunnel_open`** — Alias for 'tunnel open': bind a verified local TCP listener to an upstream host:port over the relay. Returns tunnelId, localPort. + +## Notifications + +- **`notify_dismiss`** — Programmatically clear a sticky toast by its correlation id - removes it from the notification center and clears any un-consumed response; best-effort. +- **`notify_events`** — PUSH-drain every toast-button click since sinceSeq in ONE call (durable, cross-process cursor); poll this instead of notify_response per-id. +- **`permission_history`** — 2.1.135: decided permission prompts on this machine merged with their request (verb, summary, reason, requestedAt) plus decision, decidedOn, decidedBy, decidedAt; cursor like notify_events; `pending` = prompts still live. Use it when a mirrored prompt was "closed on another surface". +- **`notify_response`** — Poll which toast button the user clicked, by correlation id: {pending:true} until they click, then {pending:false, action}. Consumed on read. +- **`notify_user`** — Send a native branded Windows toast (title/body/level/buttons/progress); optionally focus a window on click, or wait:true to block and return the user's choice. + +## Lifecycle / update / settings + +- **`apply_update`** — Apply the already-staged update and relaunch ab (programmatic 'Install now'); respects busy and only runs ab's own SHA-verified signed release. +- **`bridge_check_updates`** — Read-only: for each installed bridge report current vs latest wiki version and updateAvailable. Downloads nothing; run before refresh_bridges. +- **`embedded_enter`** — Switch ab into ah-embedded mode at runtime (runtime-adopt): on ah's death ab reverts to standalone rather than exiting. Idempotent. +- **`embedded_release`** — Gracefully leave embedded mode - always reverts ab to standalone (tray restored, marker cleared); never exits the process (use shutdown to exit). +- **`embedded_status`** — Report whether ah is managing this ab: embedded flag, owner, source, enteredVia, pending relay url/name, start-hidden, and marker path. +- **`logout`** — Clear ab's cached Adom Cloud session (~/.adom/session.json) so the footer stops showing a stale email; equivalent to the GUI Sign-out. +- **`request_update_approval`** — Surface the footer 'Update available - Install now / Later' banner + foreground ab on an attended box. Does not apply the update. +- **`restart`** — Production-safe in-place restart of ab (no redeploy): reuses ad-relauncher with source=dest=the deployed exe. Poll relauncher_status until done_ok. +- **`restart_bridge`** — Kill + respawn a bridge in one call (reaps its tracked PIDs plus any orphan on its runtime port), respawning from the current cache on the same stable port. +- **`revoke_approvals`** — Clear all shell auto-approve grants and deny any pending approval requests. +- **`settings_get`** — Read ab's durable user settings (launch_on_boot, update_mode, prewarm_*, file_receive_directory, etc.) plus whether the launch-on-boot OS artifact exists. Read-only. +- **`settings_set`** — Change whitelisted ab settings (launch_on_boot applies the Startup artifact immediately); the sanctioned way for ah to forward setting changes. Tier-2 gated for relay callers. +- **`shutdown`** — Graceful ab exit - stop bridges (kicad/fusion/puppeteer) then exit ~500ms after replying. Used by ah's tray Quit. +- **`update_check`** — Force the target to check the wiki manifest now and, if newer, download + SHA-verify (stage) it in the background. Returns the update_status shape. +- **`update_demo`** — Test/demo verb: safely show any update-tier PRESENTATION (normal banner / high toast / emergency countdown dry-run) for docs/screenshots; nothing installs. +- **`update_demo_emergency`** — Test/demo verb: run the REAL emergency forced-update chain (manifest resolve, signed download+SHA, Authenticode gate, countdown) then DRY-RUN the apply - never restarts. +- **`update_set_mode`** — Set the target's auto-update policy: auto (apply on the 4h/launch poll), prompt (banner, default), or off. Persists. +- **`update_status`** — Read the app self-update state: currentVersion, latestVersion, updateAvailable/Ready, state, updateMode, and priority/forced. Read-only. + +## Bridge management + +- **`bridge_detail`** — Alias for bridge_info: rich detail for one bridge (log tail, resolved ports, license, full verb list, processLive). +- **`bridge_info`** — Rich detail for one bridge (alias bridge_detail): everything bridge_list returns plus logTail, resolved ports, license, full verb list, manifestUrl, processLive. +- **`bridge_install`** — Install a bridge from a wiki manifest URL: download + SHA-verify + unzip the runtime into the cache and rescan; persists the URL for auto-updates. Returns skillPkg/installSkill. +- **`bridge_kill`** — Kill all of one bridge's tracked processes (image-verified reap); it respawns on the next routed verb (or via the supervisor) on the same stable port. +- **`bridge_dev_mode`** — Freeze/unfreeze ONE bridge against ALL automatic updates (`{name, on}` writes a .dev-pin marker) so a developer's local changes in its cache are never clobbered by the sweep/on-launch sync/auto-update/refresh. `bridge_list` shows `devPinned:true`; the GUI card shows an amber "dev pin" badge with one-click Unpin. NEVER remove someone else's dev pin to "get the latest" without asking - the pin means a human is mid-development. +- **`bridge_list`** — List every installed bridge (name, version, source, ports, verbPrefixes, paused, livePids, devPinned) plus update-diagnosis fields (manifestSource, tier, lastSyncDetail with the probed tier + outcome, versionDrift) and each bridge's skillPkg/installSkill for skill discovery. +- **`bridge_log_read`** — Read/stream a bridge's log: initial tailLines, or new bytes since sinceOffset (tail -f). Returns log (bridge stdout) + adLifecycle (ab's spawn/reap audit) separately. +- **`bridge_pause`** — Pause a bridge: calls to its verb prefixes fail with bridge_paused until bridge_resume. Routing flag only - the server process is not killed. +- **`bridge_readiness`** — Read-only readiness probe across every bridge - never spawns or installs. Reports host-app installed, runtime ready, and state (ready/warming/needs-runtime/no-app). +- **`bridge_resume`** — Resume a paused bridge - flip the routing flag back on. Inverse of bridge_pause. +- **`bridge_start_all`** — Re-enable bridge spawning after bridge_stop_all/--safe-mode: flip the kill-switch ON; bridges lazy-spawn on the next routed verb. +- **`bridge_stop_all`** — Global kill-switch: disable ALL bridge spawning AND stop the running bundled bridges (kicad/fusion/browser); the one-call cure for a runaway bridge. +- **`bridge_uninstall`** — Remove a bridge from the cache (bundled ones revert to the bundled fallback, third-party fully removed); does NOT kill the running server process. +- **`install_kicad`** — Unattended KiCad install via winget (~700MB, 2-5 min); a fixed-command verb (no shell-approval). Call when kicad_list_versions returns kicad_not_installed. +- **`install_node`** — Unattended Node.js LTS install via winget; call when a browser_* verb returns node_not_found, then retry - the bridge reads node.exe from the registry, no restart. +- **`refresh_bridges`** — Force an immediate wiki sync of the bridge cache (all, or {name}); applies on-disk updates and auto-restarts a running bridge to pick up new code. +- **`refresh_heroes`** — Force-refresh ONLY the bridge ART cache (hero images + wiki meta); does not sync bridge code (use refresh_bridges). +- **`runtimes`** — Read-only status of the ab-managed portable Node/Python runtimes bridges share (state, version, source system|cache, exe). No-UAC provisioning. +- **`sync_skills`** — Reconcile this container's ~/.claude/skills with the connected desktop's bridges: pkg-install each bridge's skillPkg. Idempotent; also auto-runs daily. + +## Credentials + +- **`decrypt_browser_credentials`** — Decrypt the user's own saved Chrome/Edge/Brave passwords+cookies (incl. App-Bound Encryption) via an elevated helper; LOCAL callers only (a relay caller is refused `relay_forbidden`). **Requires `reason`** — the user reads it on screen while deciding. Gated by an on-screen caption naming the asking thread + what it wants, an in-ab consent dialog, and the Windows UAC prompt. + +## Wiki / misc + +- **`activity_filter`** — Set the human's GUI activity-log filter by bridge name and/or free-text substring; view-only, does not change what verbs the AI can call. +- **`set_project_watch`** — Enable/disable watching a KiCad project directory: on file change ab base64-syncs the project files to connected relay servers. +- **`trigger_project_watch`** — Force an immediate re-sync of the watched KiCad project's files to connected relay servers (no waiting for the poll). +- **`wiki_exec`** — Run the bundled adom-wiki CLI on the user's PC (repo/pkg/release/discussion ops) with the signed-in session token injected. Args argv/cwd/timeoutSec. Tier-2 gated. + +## Other + +- **`bring_to_front`** — Bring a window (by hwnd or titleContains) to the foreground, optionally maximizing or restoring it. +- **`commands`** — Return the full CLI help-map: every verb grouped by namespace with description/args/returns (aliases: list_commands, help). +- **`double_click`** — Real OS double-click at {x,y} via SendInput; optional focus-first window/hwnd. +- **`janitor`** — CLI-local screenshot-cache janitor: {sub:status} lists managed dirs/sizes/policy, {sub:run} forces a sweep now. Keeps the pull cache bounded. +- **`list_browsers`** — Catalog every installed native browser plus its profiles and signed-in account emails; the static inventory for picking browser+profile before open_url. +- **`ping`** — Round-trip connectivity test (5s timeout); optional message echoed back. Run first to verify the desktop connection. +- **`register_app_identity`** — Register an AppUserModelID with Windows (HKCU key + optional Start Menu shortcut) so it resolves to a name + icon; call once per install. shortcut:false = registry-only (nothing in the Start Menu); its known cost is that the taskbar jump list flashes on the first right-click and stays on the second (wiki #155, measured). `anchor:true` opts in to a small visible anchor .lnk inside one Start Menu subfolder (`anchorFolder`, default "Adom Bridge") that fixes it, at the price of a Start Menu entry (default off since 2.1.49: a clean Start Menu wins). +- **`right_click`** — Real OS right-click at {x,y} via SendInput to open context menus; optional focus-first window/hwnd. +- **`status`** — Query connected clients, capabilities, and app install/run status (kicad/fusion/browser), plus WSL wedge and bridge-update health. +- **`targets`** — List the Adom Bridges connected to this relay (name, hostname, version, os) so you can address one with --target <name> or --target all. +- **`timeouts`** — The per-verb HTTP timeout registry: defaults plus the long verbs mapped to their budget and the status-verb to poll on a timeout. +- **`responseMode`** on ANY verb (2.1.50, wiki #154) — `"job"`: the call returns `{jobId, state, cursor}` at once and the verb runs under that job; a gated verb PARKS on `waiting_for_approval` and resumes once on a real human decision (any surface, any mesh box) instead of failing with needs_approval. `"stream"`: the same job followed by the CLI, one JSON line per lifecycle event, result last, bounded by `streamTimeoutSeconds` (detaching never cancels). +- **`job_status`** / **`job_events`** / **`job_cancel`** (2.1.50, wiki #154) — snapshot, cursor-based event replay with long-poll (`waitMs`), and withdrawal of a parked job. +- **`app_instances`** / **`app_instance`** (2.1.50, wiki #126) — the instance-registry primitive for multi-instance apps: every running instance of an exe with a STABLE key (image:pid:start time), its windows (hwnd/title/hung), command line and a per-instance scratchDir; `app_instance` resolves exactly one by key | pid | hwnd | titleContains or refuses with `ambiguous_instance` + candidates. +- **`bridge_audit`** (2.1.50, wiki #124) — the command audit: every verb ab ran (who: thread/container/delegate, why: reason, origin relay|local|bridge, durationMs, success/errorCode), filterable by bridge/thread/verb/since/failuresOnly/installGrantOnly; in-memory ring + `~/.adom/audit/commands.jsonl`. Also returns the bridge's own persistent events. +- **`bridge_event_log`** (2.1.50, wiki #124) — a bridge appends its own operational event (dialog reap, watchdog firing, instance change) to `~/.adom/audit/bridges/<bridge>.jsonl`, which outlives its respawn; read back with `bridge_audit {bridge}`. +- **`relay_boot_hook`** (2.1.50, wiki #45; CLI-local) — `status | install | remove` a cron `@reboot` line that restarts `adom-bridge serve --supervise` after a container reboot. +- **`desktop_taskbar` thumbbar** (2.1.50, wiki #38) — `thumbbar:[{id?, icon, tooltip?, enabled?, hidden?}]` (max 7): an icon-only status strip under the hover preview; slots are added once per window and only their icons/tooltips/flags change afterwards. +- **`unregister_app_identity`** — Remove an app-identity registration: delete its HKCU AppUserModelId key and any Start Menu shortcut carrying that AUMID (root and the anchor folder; pass `anchorFolder` if you named one; an emptied folder is removed). Idempotent. +- **`user_idle_ms`** — Milliseconds since the last system-wide keyboard/mouse input (GetLastInputInfo) - yield intrusive work while the user is active.
skills/adom-bridge/SKILL.md+1−1@@ -1885,7 +1885,7 @@ Architectural notes for Claude: ### Shell — `shell_execute` (escape hatch only) -- `shell_execute` -- Run a shell command on the desktop. The CLI handles approval polling internally — it returns the final `{success, output, error, exitCode}` once the user clicks Allow on the desktop dialog. **You do not need to poll, retry, or call `get_deferred_result` yourself.** The CLI emits `HINT:` lines to stderr while waiting (every 15s) so an AI Monitor sees progress. +- `shell_execute` -- Run a shell command on the desktop. The CLI handles approval polling internally — it returns the final `{success, output, error, exitCode}` once the user clicks Allow on the desktop dialog. 2.1.132+: ab holds the prompt for `approvalWaitSec` (default 60, max 600); a lapse answers `errorCode:"approval_timeout"` (nobody answered, NOT a deny: re-send with a longer wait or `request_shell_approval {wait:true}`), an explicit Deny answers `errorCode:"approval_denied"`. **You do not need to poll, retry, or call `get_deferred_result` yourself.** The CLI emits `HINT:` lines to stderr while waiting (every 15s) so an AI Monitor sees progress. - `shell_kill_all` -- Kill all running shell commands and deny pending approvals. **v1.7.16 fix.** Earlier versions (v1.7.15 specifically) had a regression where the CLI's approval-wait loop re-sent the command every 1s instead of polling for the deferred result; that produced an infinite "Another shell command is already waiting for approval" loop because each retry created a fresh approval the user could never out-click. v1.7.16 fixes it. If you see that error string with a `>= 1.7.16` CLI, the bug is back — file an issue. If you see it with `< 1.7.16`, upgrade the CLI: `adom-wiki asset get apps/adom-bridge docker_binary -o /usr/local/bin/adom-bridge`.