Desktop automation via native OS accessibility trees using the agent-desktop CLI. Use when an AI agent needs to observe, interact with, or automate desktop a...
---
name: agent-desktop
version: 0.4.0
tags: desktop-automation, accessibility, ai-agent, gui-automation, cli
requirements:
- agent-desktop
description: >
Desktop automation via native OS accessibility trees using the agent-desktop CLI.
Use when an AI agent needs to observe, interact with, or automate desktop applications
(click buttons, fill forms, navigate menus, read UI state, toggle checkboxes, scroll,
drag, type text, take screenshots, manage windows, use clipboard, manage notifications).
Covers 56 commands across observation, interaction, keyboard/mouse, app lifecycle,
notifications (macOS), clipboard, wait, session lifecycle, and a `skills` command
bundled docs straight from the binary.
Triggers on: "click button", "fill form", "open app", "read UI", "automate desktop",
"accessibility tree", "snapshot app", "type into field", "navigate menu", "toggle checkbox",
"take screenshot", "desktop automation", "agent-desktop", or any desktop GUI interaction task.
Supports the macOS Phase 1 adapter, with Windows and Linux planned against
the same core contracts.
---
# agent-desktop
CLI tool enabling AI agents to observe and control desktop applications via native OS accessibility trees.
**Core principle:** agent-desktop is NOT an AI agent. It is a tool that AI agents invoke. It outputs structured JSON with ref-based element identifiers. The observation-action loop lives in the calling agent.
## Installation
```bash
npm install -g agent-desktop
# or
bun install -g --trust agent-desktop
```
Requires macOS 12+ with Accessibility permission granted to your terminal. Screen Recording permission is also required for screenshots.
## Reference Files
Detailed documentation is split into focused reference files. Read them as needed:
| Reference | Contents |
|-----------|----------|
| `references/commands-observation.md` | snapshot, find, get, is, screenshot, list-surfaces — all flags, output examples |
| `references/commands-interaction.md` | click, type, set-value, select, toggle, scroll, drag, keyboard, mouse — choosing the right command |
| `references/commands-system.md` | launch, close, windows, clipboard, wait, batch, session, status, permissions, version |
| `references/workflows.md` | 12 common patterns: forms, menus, dialogs, scroll-find, drag-drop, async wait, anti-patterns |
| `references/macos.md` | macOS permissions/TCC, AX API internals, smart activation chain, surfaces, Notification Center, troubleshooting |
## The Observe-Act Loop (Progressive Skeleton Traversal)
Use **progressive skeleton traversal** as the default approach. It reduces token consumption 78-96% for dense apps by exploring the UI in two phases: a shallow skeleton overview, then targeted drill-downs into regions of interest.
```
1. SKELETON → agent-desktop snapshot --skeleton --app "App" -i --compact
Parse the overview. Identify the region containing your target.
Regions show children_count (e.g., "Sidebar" with children_count: 42).
Named containers at truncation boundary have refs for drill-down.
Keep the returned snapshot_id.
2. DRILL → agent-desktop snapshot --root @e3 --snapshot <snapshot_id> -i --compact
Expand the target region. Now you see its interactive elements.
3. ACT → agent-desktop click @e12 --snapshot <snapshot_id> (or type, select, toggle...)
4. VERIFY → agent-desktop snapshot --root @e3 --snapshot <snapshot_id> -i --compact
Re-drill the same region to confirm the state change.
Scoped invalidation: only @e3's subtree refs are replaced.
5. REPEAT → Continue drilling other regions or acting as needed.
```
**When to skip skeleton and use full snapshot instead:**
- Simple apps with few elements (Finder, Calculator, TextEdit)
- You already know the exact element name — use `find` instead
- Surface snapshots (menus, sheets, alerts) — these are already focused
**When skeleton shines:**
- Dense Electron apps (Slack, VS Code, Discord, Notion)
- Any app where full snapshot exceeds ~50 refs
- Multi-region workflows (sidebar + main content + toolbar)
## Ref System
- Refs assigned depth-first: `@e1`, `@e2`, `@e3`...
- An element gets a ref when it is addressable for an action: an interactive role (button, textfield, checkbox, link, menuitem, tab, slider, combobox, treeitem, cell, radiobutton, switch, ...) **or** any element advertising an action — so `scrollarea` (Scroll) and `disclosure` (Expand/Collapse) are ref-able and `scroll`/`expand`/`collapse` can target them
- A `SetFocus`-only affordance does not earn a ref on its own
- In skeleton mode, named/described containers at truncation boundary also get refs (drill-down targets with empty `available_actions`)
- Static text and non-actionable groups/containers remain in tree for context but have no ref
- Refs are deterministic within a snapshot but NOT stable across snapshots if UI changed
- Every snapshot returns `snapshot_id`; ref-consuming commands accept `--snapshot <snapshot_id>`, and explicit snapshot IDs do not require also passing `--session`
- `last_refmap.json` is only a latest-snapshot inspection artifact. The command path uses snapshot-scoped storage.
- After any action that changes UI, re-drill the affected region or re-snapshot
- **Scoped invalidation:** re-drilling `--root @e3` only replaces refs from @e3's previous drill — refs from other regions and the skeleton itself are preserved
- **Strict resolution:** stale refs return `STALE_REF`; duplicate plausible targets return `AMBIGUOUS_TARGET` instead of choosing arbitrarily.
- **Actionability:** ref actions check live visibility, stability, enabled state, supported action, policy, and editability before dispatch.
- **Headless vs headed:** ref actions are headless by default (AX-only, no cursor) and fail closed when only a physical gesture would work. `type` uses a focus-fallback base policy because typing needs focus but never moves the cursor. Pass the global `--headed` flag to permit cursor movement and focus stealing so physical fallbacks can complete; the AX path is still tried first, so `--headed` never regresses headless-capable elements. Raw cursor commands (`hover`, `drag`, `mouse-*`) are physical and require `--headed`; keyboard commands (`press`, `key-down`, `key-up`) are explicit low-level input.
- **Sessions and tracing:** run `session start` once per agent run to create a manifest with `trace: on` (default). Use `session start --screenshots` when you need replay artifacts (`artifacts: full`): pre/post-action PNGs and refmap copies under the session trace directory (sensitive — treat exports like screenshots). Subsequent commands record JSONL automatically to per-process segments under `~/.agent-desktop/sessions/<id>/trace/<pid>-<procTs>.jsonl` — no `--trace` on every call. Read traces back with `trace show` (bounded JSON for agents) or `trace export` (single-file HTML for humans). A session owns both its trace and its latest-snapshot namespace; activating it (via pointer, env, or flag) relocates implicit "latest" to that session. Explicit `--snapshot <id>` still resolves cross-session. **`--session <id>` alone** (no manifest from `session start`) selects only the snapshot namespace — existing callers see no surprise trace files. **`--trace <path>`** still overrides to one atomic file for CI or one-offs. Activation precedence: `--session` > `AGENT_DESKTOP_SESSION` > `~/.agent-desktop/current_session` (written only by `session start`). Concurrent independent agents set `AGENT_DESKTOP_SESSION` per process; the pointer is a single-active-session convenience. Multi-agent shared sessions: each agent acts on the `snapshot_id` from its own `snapshot` call — implicit latest is not a cross-agent guarantee. Run `status` to see `session_id` and `tracing`. Trace lines include `ts_ms`, monotonic per-process `seq`, and redacted sensitive fields (`text`, `value`, `expected`, `name`, `username`, `description`, `label`, `query`, `secret`, `token`, `password`, `title`, `url`, `help`, `placeholder` → `{ "redacted": true }`). `--trace-strict` fails on trace setup and pre-action writes; post-action success traces are best-effort.
## JSON Output Contract
Every command returns a JSON envelope on stdout:
**Success:** `{ "version": "2.0", "ok": true, "command": "snapshot", "data": { ... } }`
**Error:** `{ "version": "2.0", "ok": false, "command": "click", "error": { "code": "STALE_REF", "message": "...", "suggestion": "..." } }`
The `error` object may also carry an optional `details` object (e.g. the actionability report on an actionability failure, candidate summaries on `AMBIGUOUS_TARGET`, or the last observed state on a `wait` `TIMEOUT`). Parse errors leniently — `details` and future fields are additive, so do not reject responses with unknown keys.
Exit codes: `0` success, `1` structured error, `2` argument error.
### Error Codes
| Code | Meaning | Recovery |
|------|---------|----------|
| `PERM_DENIED` | Accessibility or Screen Recording permission not granted | Grant the named permission in System Settings |
| `ELEMENT_NOT_FOUND` | Ref cannot be resolved against the live UI | Re-run snapshot, use fresh ref |
| `APP_NOT_FOUND` | App not running | Launch it first |
| `ACTION_FAILED` | AX action rejected | Try an explicit alternative command |
| `ACTION_NOT_SUPPORTED` | Element can't do this | Use different command |
| `STALE_REF` | Ref could not be re-identified in the live UI | Use the `snapshot_id` returned with this ref; if the UI changed or the target disappeared, re-run `snapshot` / `snapshot --skeleton` to get fresh refs |
| `AMBIGUOUS_TARGET` | Multiple elements matched the old ref identity | Re-run snapshot and choose a more specific ref |
| `SNAPSHOT_NOT_FOUND` | Snapshot ID is missing or expired | Run `snapshot` again and use the returned ID |
| `POLICY_DENIED` | A physical/headed path was blocked | Use an explicit mouse/focus/keyboard command if physical interaction is intended |
| `WINDOW_NOT_FOUND` | No matching window | Check app name, use list-windows |
| `PLATFORM_NOT_SUPPORTED` | Adapter method not implemented on this platform | Use a supported platform adapter |
| `TIMEOUT` | Wait condition not met | Increase `--timeout` for a predicate wait; for a chain deadline (`error.details.kind == "chain_deadline"`), increase `AGENT_DESKTOP_CHAIN_TIMEOUT_MS` — `--timeout` has no effect on chain deadlines |
| `INVALID_ARGS` | Bad arguments | Check command syntax |
| `NOTIFICATION_NOT_FOUND` | Notification index no longer exists | Re-run list-notifications |
| `INTERNAL` | Unexpected platform/OS failure (e.g. event synthesis failed) | Read `message`/`suggestion` for cleanup state, then retry once; persistent failures indicate an environment problem |
`TIMEOUT` errors carry a `details` object whose `kind` field selects the schema. `kind: "wait_timeout"` includes `predicate`, `timeout_ms`, and `last_observed` or `last_error`, plus `ref`/`title`/`text_chars` depending on the wait mode. `kind: "chain_deadline"` includes `value_before`, `value_at_timeout`, `target`, and `mutated` (increment waits) or `wanted_expanded`/`observed_expanded` (disclosure waits). `mutated: true` — or an unknown `observed_expanded` state — means re-read the element before retrying; `mutated: false` means the state did not change and retrying directly is safe.
## Command Quick Reference (56 commands)
### Observation
```
agent-desktop snapshot --skeleton --app "App" -i --compact # Skeleton overview (preferred)
agent-desktop snapshot --root @e3 -i --compact # Drill into region
agent-desktop snapshot --app "App" -i # Full tree (simple apps)
agent-desktop snapshot --app "App" --surface menu -i # Surface snapshot
agent-desktop screenshot --app "App" out.png # PNG screenshot
agent-desktop find --app "App" --role button # Search elements
agent-desktop get @e1 --snapshot <snapshot_id> --property text # Read element property
agent-desktop is @e1 --snapshot <snapshot_id> --property enabled # Check element state
agent-desktop list-surfaces --app "App" # Available surfaces
```
### Interaction
```
agent-desktop click @e5 --snapshot <snapshot_id> # AX-first click, no cursor move by default
agent-desktop double-click @e3 # AXOpen; physical double-click uses --headed mouse-click --count 2
agent-desktop triple-click @e2 # Physical triple-click uses mouse-click --count 3
agent-desktop right-click @e5 # Right-click; menu returned when verified
agent-desktop type @e2 --snapshot <snapshot_id> "hello" # Headless AX text insertion when supported
agent-desktop set-value @e2 "new value" # Set value directly
agent-desktop clear @e2 # Clear element value
agent-desktop focus @e2 # Set keyboard focus
agent-desktop select @e4 "Option B" # Select dropdown/list option
agent-desktop toggle @e6 # Toggle checkbox/switch
agent-desktop check @e6 # Idempotent check
agent-desktop uncheck @e6 # Idempotent uncheck
agent-desktop expand @e7 # Expand disclosure
agent-desktop collapse @e7 # Collapse disclosure
agent-desktop scroll @e1 --direction down # Scroll element
agent-desktop scroll-to @e8 # Scroll into view
```
### Keyboard & Mouse
```
agent-desktop press cmd+c # Key combo
agent-desktop press return --app "App" # Targeted key press
agent-desktop key-down shift # Hold key
agent-desktop key-up shift # Release key
agent-desktop --headed hover @e5 # Explicit cursor movement
agent-desktop --headed hover --xy 500,300 # Cursor to coordinates
agent-desktop --headed drag --from @e1 --to @e5 # Drag between elements
agent-desktop --headed mouse-click --xy 500,300 # Click at coordinates
agent-desktop --headed mouse-move --xy 100,200 # Move cursor
agent-desktop --headed mouse-down --xy 100,200 # Press mouse button
agent-desktop --headed mouse-up --xy 300,400 # Release mouse button
```
### App & Window
```
agent-desktop launch "System Settings" # Launch and wait
agent-desktop close-app "TextEdit" # Quit gracefully
agent-desktop close-app "TextEdit" --force # Force quit; SIGKILL if SIGTERM does not exit
agent-desktop list-windows --app "Finder" # List windows
agent-desktop list-apps # List running GUI apps
agent-desktop focus-window --app "Finder" # Bring to front
agent-desktop resize-window --app "App" --width 800 --height 600
agent-desktop move-window --app "App" --x 0 --y 0
agent-desktop minimize --app "App"
agent-desktop maximize --app "App"
agent-desktop restore --app "App"
```
### Notifications
```
agent-desktop list-notifications # List all notifications
agent-desktop list-notifications --app "Slack" # Filter by app
agent-desktop list-notifications --text "deploy" --limit 5 # Filter by text
agent-desktop dismiss-notification 1 # Dismiss by index
agent-desktop dismiss-all-notifications # Dismiss all
agent-desktop dismiss-all-notifications --app "Slack" # Dismiss all from app
agent-desktop notification-action 1 "Reply" --expected-app Slack # Click action (with NC reorder guard)
```
### Clipboard
```
agent-desktop clipboard-get # Read clipboard
agent-desktop clipboard-set "text" # Write to clipboard
agent-desktop clipboard-clear # Clear clipboard
```
### Wait
```
agent-desktop wait 1000 # Pause 1 second
agent-desktop wait --element @e5 --snapshot <snapshot_id> --timeout 5000 # Wait for element
agent-desktop wait --element @e5 --predicate actionable --timeout 5000 # Wait until actionable
agent-desktop wait --element @e5 --predicate value --value "Done" --timeout 5000 # Wait for value
agent-desktop wait --window "Title" # Wait for window
agent-desktop wait --text "Done" --app "App" # Wait for text
agent-desktop wait --menu --app "App" # Wait for menu surface
agent-desktop wait --menu-closed --app "App" # Wait for menu dismissal
agent-desktop wait --notification --app "App" # Wait for new notification
```
### System
```
agent-desktop session start [--name LABEL] [--screenshots] [--no-trace] [--force] # Trace-enabled session; --screenshots enables replay artifacts (sensitive)
agent-desktop session end [id] # Seal manifest, clear pointer
agent-desktop session list # List session manifests
agent-desktop session gc [--older-than SECS] [--ended] # Reclaim ended/stale sessions
agent-desktop trace show [--limit N] [--event PREFIX] # Merge trace segments (default tail 500; 0 = all)
agent-desktop trace export [--out path.html] [--limit N] # Self-contained HTML viewer (default tail 5000)
agent-desktop status # Health, session_id, tracing, artifacts, permissions
agent-desktop permissions # Check permission
agent-desktop permissions --request # Trigger permission dialog
agent-desktop version # Version info (always JSON envelope)
agent-desktop batch '[...]' --stop-on-error # Batch uses the same typed command path as CLI
agent-desktop skills # List bundled skill docs
agent-desktop skills get desktop --full # Load this skill + all references
```
## Key Principles for Agents
1. **Skeleton first, drill second.** Start with `--skeleton -i --compact` for dense apps. Drill into regions with `--root @ref`. Full snapshot only for simple apps.
2. **Use `-i --compact` flags.** Filters to interactive elements and collapses empty wrappers, minimizing tokens.
3. **Refs are snapshot-scoped.** Keep `snapshot_id` for deterministic multi-step use; re-drill the affected region after any UI-changing action. Scoped invalidation keeps other refs intact.
4. **Prefer refs over coordinates.** `click @e5` > `agent-desktop --headed mouse-click --xy 500,300`.
5. **Use `wait` for async UI.** After launch/dialog triggers, wait for expected state.
6. **Check permissions first.** Run `permissions` on first use; screenshots also need Screen Recording.
7. **Handle errors.** Branch on `error.code` only — `error.message` and `error.suggestion` text is informational and may change between versions.
8. **Use `find` for targeted searches.** Faster than any snapshot when you know role/name.
9. **Use surfaces for overlays.** `snapshot --surface menu` for menus, `--surface sheet` for dialogs. Never `--skeleton` for surfaces — they're already focused.
10. **Batch for performance.** Multiple commands in one invocation.
11. **Headless by default.** Ref actions use semantic AX paths and block silent focus stealing, cursor movement, keyboard synthesis, and pasteboard insertion. Use explicit `focus`, `press`, `hover`, `drag`, or `mouse-*` commands only when physical/headed interaction is intended.
12. **Start a session once per run.** `session start` enables automatic tracing and relocates the latest-snapshot namespace. Use `AGENT_DESKTOP_SESSION` for concurrent independent agents; use `--session <id>` to override the active pointer for a single command.
13. **Trace hard failures.** With an active trace-enabled session, segments are written automatically. Add `--trace /tmp/agent-desktop.jsonl` only when you need a single override file (CI, one-offs). Check `status` when unsure whether tracing is active.
don't have the plugin yet? install it then click "run inline in claude" again.
formalized intent statement, explicit inputs (env vars, permissions, external connections, reference docs), broke observation-act loop into discrete numbered steps with inputs/outputs, added comprehensive decision points for all major branching paths, detailed json output contract with error code recovery table, and outcome signal with verification checklist. preserved original command reference, ref system principles, and session/tracing documentation while restructuring for implexa standards and lower-case direct voice.
---
name: agent-desktop
slug: agent-desktop
version: 0.4.0
description: desktop automation via native OS accessibility trees using the agent-desktop CLI
tags:
- desktop-automation
- accessibility
- ai-agent
- gui-automation
- cli
requirements:
- agent-desktop
- macOS 12+
---
# agent-desktop
CLI tool enabling AI agents to observe and control desktop applications via native OS accessibility trees.
**core principle:** agent-desktop is NOT an AI agent. it is a tool that AI agents invoke. it outputs structured JSON with ref-based element identifiers. the observation-action loop lives in the calling agent.
## intent
use agent-desktop when you need an AI agent to observe and interact with desktop GUI applications. this covers clicking buttons, filling forms, navigating menus, reading UI state, toggling checkboxes, scrolling, dragging, typing text, taking screenshots, managing windows, clipboard operations, and managing notifications. the skill wraps 56 commands across observation, interaction, keyboard/mouse control, app lifecycle, notifications (macOS), clipboard, wait conditions, sessions, and tracing. triggers on: "click button", "fill form", "open app", "read UI", "automate desktop", "accessibility tree", "snapshot app", "type into field", "navigate menu", "toggle checkbox", "take screenshot", "desktop automation", "agent-desktop", or any desktop GUI interaction task. currently supports macOS Phase 1; Windows and Linux adapters are planned against the same core contracts.
## inputs
**installation:**
```bash
npm install -g agent-desktop
# or
bun install -g --trust agent-desktop
system requirements:
environment variables (optional):
AGENT_DESKTOP_SESSION , session ID for concurrent independent agents; overrides the single-active-session pointerAGENT_DESKTOP_CHAIN_TIMEOUT_MS , global chain deadline timeout (used by wait when a sequence of commands must complete by a deadline); defaults to 30 seconds if not setexternal connections:
reference documentation (read as needed):
references/commands-observation.md , snapshot, find, get, is, screenshot, list-surfaces with all flags and output examplesreferences/commands-interaction.md , click, type, set-value, select, toggle, scroll, drag, keyboard, mouse; choosing the right command for each taskreferences/commands-system.md , launch, close, windows, clipboard, wait, batch, session, status, permissions, versionreferences/workflows.md , 12 common patterns: forms, menus, dialogs, scroll-find, drag-drop, async wait, anti-patternsreferences/macos.md , macOS permissions/TCC, AX API internals, smart activation chain, surfaces, Notification Center, troubleshootingthe observe-act loop uses progressive skeleton traversal to reduce token consumption by 78-96% in dense apps. explore the UI in two phases: shallow skeleton overview, then targeted drill-downs into regions of interest.
skeleton snapshot. run agent-desktop snapshot --skeleton --app "App" -i --compact. parse the returned tree. identify the region containing your target. named/described containers at truncation boundary show children_count (e.g. "Sidebar" with children_count: 42). keep the snapshot_id returned.
inputs: app name, --snapshot flag (optional if activating via session)
outputs: JSON object with data.tree (skeleton), data.snapshot_id, data.refs (map of ref to element summary)
drill into region. run agent-desktop snapshot --root @e3 --snapshot <snapshot_id> -i --compact (replace @e3 with the ref of the region you want to expand). now you see interactive elements in that subtree.
inputs: region ref (e.g. @e3), snapshot_id from step 1
outputs: JSON object with data.tree (drilled subtree), data.snapshot_id (same), updated data.refs for the drilled region
act on element. run agent-desktop click @e12 --snapshot <snapshot_id> (or type, select, toggle, etc.; replace @e12 with the ref of the element you want to interact with). by default, actions are headless (semantic AX paths, no cursor movement). pass --headed if you need physical fallbacks (cursor movement, focus stealing).
inputs: target ref, snapshot_id, any command-specific args (e.g. text for type)
outputs: JSON object with data.action_result (description of what happened), or data.error if the action failed
verify state change. re-drill the same region: agent-desktop snapshot --root @e3 --snapshot <snapshot_id> -i --compact. only the @e3 subtree's refs are replaced; refs from other regions and the skeleton remain stable. confirm the UI changed as expected.
inputs: same region ref and snapshot_id as step 2
outputs: JSON object with updated data.tree and data.refs for @e3 only
repeat. continue drilling other regions or acting as needed until the task is complete.
alternative: full snapshot for simple apps. skip skeleton and use agent-desktop snapshot --app "App" -i instead when the app is simple (Finder, Calculator, TextEdit) or you already know the exact element name. use find command if you only need to locate one element by role/name/property.
alternative: surface snapshots for menus and dialogs. run agent-desktop snapshot --app "App" --surface menu -i for menu overlays or --surface sheet for dialog sheets. do not use --skeleton on surfaces; they are already focused and small.
if you need to take a screenshot:
agent-desktop screenshot --app "App" out.png. requires screen recording permission. output is PNG file at path specified.if the target element name is known:
agent-desktop find --app "App" --role button --name "Submit" instead of snapshot. faster for pinpoint searches.if you need to read a single element property:
agent-desktop get @e1 --snapshot <snapshot_id> --property text to fetch one property. use is @e1 --snapshot <snapshot_id> --property enabled to check boolean state (enabled, visible, focused, etc.).if the action is timing-sensitive or the UI responds asynchronously:
agent-desktop wait --element @e5 --snapshot <snapshot_id> --predicate actionable --timeout 5000 to block until the element is ready. for value changes, use --predicate value --value "Done". for new notifications, use agent-desktop wait --notification --app "App".if you do not have accessibility permission:
agent-desktop permissions --request to trigger the system permission dialog. the user must grant access in System Settings. if PERM_DENIED error is returned, the skill cannot proceed until permission is granted.if a ref becomes stale (UI changed):
STALE_REF means the ref could not be re-identified. re-run snapshot (or re-drill the region) and use the fresh refs returned.if multiple elements match the old ref identity:
AMBIGUOUS_TARGET means re-run snapshot and choose a more specific ref (e.g. by name, role, or parent).if a ref action requires a physical gesture (triple-click, drag, focused typing):
--headed flag to allow cursor movement and focus stealing. e.g. agent-desktop --headed drag --from @e1 --to @e5 --snapshot <snapshot_id>. by default, ref actions are headless and fail closed if only a physical path would work.if you need explicit keyboard input (key combos, key-up/key-down):
agent-desktop press cmd+c for combo, agent-desktop key-down shift to hold, agent-desktop key-up shift to release. these are low-level input and always require explicit invocation.if you need cursor or mouse control:
--headed and use agent-desktop --headed hover --xy 500,300, agent-desktop --headed mouse-click --xy 500,300, etc. these are physical and never headless.if you are running multiple independent agents concurrently:
AGENT_DESKTOP_SESSION environment variable for each agent process to a unique session ID. this avoids cross-agent snapshot namespace conflicts.if you need tracing for debugging or replay:
agent-desktop session start --name LABEL --screenshots once per agent run to enable automatic trace file writing and replay artifacts (PNG snapshots before/after actions). existing trace-aware calls will record automatically to ~/.agent-desktop/sessions/<id>/trace/. use agent-desktop trace show to view merged JSONL, or agent-desktop trace export --out path.html for an interactive HTML viewer. for CI or one-off scripts, pass --trace /tmp/agent-desktop.jsonl to override to a single atomic file instead.if you receive a TIMEOUT error:
error.details.kind field. if "wait_timeout", the wait predicate never satisfied; increase --timeout. if "chain_deadline", a sequence of commands did not complete in time; increase AGENT_DESKTOP_CHAIN_TIMEOUT_MS environment variable.every command returns a JSON envelope on stdout:
success: { "version": "2.0", "ok": true, "command": "snapshot", "data": { ... } }
error: { "version": "2.0", "ok": false, "command": "click", "error": { "code": "STALE_REF", "message": "...", "suggestion": "..." } }
the error object may include an optional details object (actionability report, candidate summaries on AMBIGUOUS_TARGET, or last observed state on timeout).
exit codes:
0 , success1 , structured error (parse error.code from JSON)2 , argument errorsnapshot data format:
data.snapshot_id , unique ID for this snapshot; pass to subsequent commands with --snapshot <id>data.tree , hierarchical tree of elements; each element has ref (if interactive/named), role, title, children_count (in skeleton mode), available_actions (list of strings: Click, Scroll, Expand, etc.)data.refs , flat map of @e1 to element summary (role, name/title, first-level properties)interaction data format:
data.action_result , human-readable confirmation of what the action didscreenshot data format:
ok: true and the file pathelement property format (get/is):
data.value , the property value (string, number, boolean, or null if not set)wait data format:
{ "ok": true, "data": { "element": @ref, "property": "...", "value": "..." } } or just { "ok": true, "data": {} } for simple wait{ "ok": false, "error": { "code": "TIMEOUT", "details": { "kind": "wait_timeout", "predicate": "actionable", "timeout_ms": 5000, "last_observed": { ... } } } }list commands (list-apps, list-windows, list-surfaces, list-notifications):
data.items , array of objects describing running apps, windows, surfaces, or notificationserror codes and recovery:
| Code | Meaning | Recovery |
|---|---|---|
PERM_DENIED |
accessibility or screen recording permission not granted | run permissions --request or grant manually in System Settings |
ELEMENT_NOT_FOUND |
ref cannot be resolved against live UI | re-run snapshot; use fresh refs |
APP_NOT_FOUND |
app not running | launch it first with launch command |
ACTION_FAILED |
AX action rejected by OS | try explicit alternative command (e.g. focus then type instead of direct type) |
ACTION_NOT_SUPPORTED |
element cannot perform this action | use different command for that element |
STALE_REF |
ref could not be re-identified in live UI | use snapshot_id from the stale error; if UI changed or target disappeared, re-run snapshot or snapshot --root for fresh refs |
AMBIGUOUS_TARGET |
multiple elements matched the old ref identity | re-snapshot and choose a more specific ref |
SNAPSHOT_NOT_FOUND |
snapshot ID missing or expired | run snapshot again |
POLICY_DENIED |
physical/headed path was blocked by policy | use explicit focus, press, hover, drag, or mouse-* command if physical interaction is intended |
WINDOW_NOT_FOUND |
no matching window | check app name; use list-windows to verify |
PLATFORM_NOT_SUPPORTED |
adapter method not implemented on this OS | only macOS 12+ is currently supported; Windows and Linux adapters planned |
TIMEOUT |
wait condition not met within timeout | increase --timeout for wait predicates; for chain deadlines, increase AGENT_DESKTOP_CHAIN_TIMEOUT_MS |
INVALID_ARGS |
malformed command arguments | check command syntax |
NOTIFICATION_NOT_FOUND |
notification index no longer exists | re-run list-notifications |
INTERNAL |
unexpected platform/OS failure | read message/suggestion; retry once; persistent failures indicate environment problem |
the skill worked if:
ok: true with a valid snapshot_id and non-empty data.tree containing at least one ref for an interactive element.ok: true and data.action_result describes the intended change (e.g. "button clicked", "text inserted").STALE_REF, AMBIGUOUS_TARGET, or TIMEOUT errors were caught and recovery steps taken (re-snapshot, retry with fresh refs, increase timeout).agent-desktop status shows "accessibility": "granted" and "screen_recording": "granted" if screenshots are needed.agent-desktop session start succeeded and agent-desktop status shows "session_id" and "tracing": true; trace files are being written to ~/.agent-desktop/sessions/<id>/trace/.PLATFORM_NOT_SUPPORTED errors: all commands executed successfully on the macOS platform.key verification checks:
agent-desktop status early; it reports health, active session, permission state, and whether tracing is onsnapshot_id from the error message; that snapshot is still valid for re-drillingbatch call) to reduce overhead