Keep Cua Driver running and updated
Keep the Cua Driver daemon running, update it, choose a release channel, personalize the agent cursor, and control telemetry.
Keep the Cua Driver daemon running, update it, choose a release channel, personalize the agent cursor, and control telemetry.
MCP clients on macOS and all one-shot cua-driver call commands need a
running daemon. Apps that use CuaDriver.create() do not.
Save a LaunchAgent at ~/Library/LaunchAgents/com.trycua.cua-driver.plist
(from a repo checkout, bash libs/cua-driver/scripts/install-local.sh --autostart
writes it for you):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.trycua.cua-driver</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/CuaDriver.app/Contents/MacOS/cua-driver</string>
<string>serve</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>launchctl load ~/Library/LaunchAgents/com.trycua.cua-driver.plistThe LaunchAgent daemon is attributed to com.trycua.driver, so its grants
persist across reboots.
cua-driver status
cua-driver stopstop ends the daemon; the autostart entry brings it back at the next logon.
Mode flags are read once at start, so put them in the autostart entry.
Add each flag as its own <string> after serve, with an absolute path
(launchd does not expand ~), then launchctl unload and load the plist:
<string>serve</string>
<string>--permission-mode</string>
<string>bounded</string>
<string>--capability-manifest</string>
<string>/Users/you/cua-session.yaml</string>
<string>--approve-capability-manifest</string>A bad configuration fails startup instead of falling back to standard. A
user policy file works the same way: set CUA_DRIVER_POLICY_FILE in the
plist's EnvironmentVariables, the unit, or the User environment.
cua-driver check-update
cua-driver update --applycheck-update only checks (--json for scripts, --no-cache to skip the
1-hour cache); update --apply runs the canonical installer. Then restart the
daemon with your usual start command (open -n -g -a CuaDriver --args serve,
cua-driver autostart kick, or cua-driver serve), keeping any mode flags. The
same check is the check_for_update MCP tool.
The check reads the version the installer itself would install, so it agrees
with curl -fsSL https://cua.ai/driver/install.sh | bash, and it does not spend
GitHub's REST API quota for stable releases. When GitHub's API does answer with
a rate limit (nightly lookups and fallbacks), the message says so and shows when
the limit resets; set GITHUB_TOKEN or GH_TOKEN to use your own quota.
if cua-driver check-update --json | jq -e '.update_available' > /dev/null; then
cua-driver update --apply
fimcp, serve, and doctor print a banner when a newer release exists. Turn
the check off for one run with CUA_DRIVER_RS_UPDATE_CHECK=false, or always:
cua-driver config set update_check_enabled falseIf pacman owns the executable, update with sudo pacman -Syu; Driver's own
update and channel commands refuse with pacman guidance (builds with
#3636).
0.35 changes three things that scripts and wrappers may depend on. The typed
Rust, Python and TypeScript SDKs keep the full get_window_state response, so
they are only affected by item 3.
get_window_state is lean by default
(#4743). A read returns only the
compact tree_markdown, walks at most 250 nodes, and leaves out _note and
background_input. If you read structuredContent.elements, pass
tree_format: "elements" (or "both" if you also read tree_markdown).
Pass verbose: true for _note and background_input, and max_elements
for a larger walk. full_output: true returns the 0.34 response unchanged.
A row [N] of a read whose snapshot_id is s0000002a has the
element_token s0000002a:N.tree_markdown and in elements. Run a menu
item with invoke_menu and its path, or find it with query;
full_output: true still walks every menu. The walk diagnostics
nodes_visited, nodes_pending, walk_elapsed_ms and
screenshot_refreshed_snapshot need verbose: true.type_text sends \n, \r and \t as Return and Tab
(#4821) when it types keys
(Chromium and Electron fields, delivery_mode: "foreground", pixel
targets). Text with a line break now submits a form or an address bar, and
a tab moves focus. Remove them from text that should not submit, or write
the value with set_value where the field supports it.Scripts that read raw list_apps JSON on macOS should also treat
bundle_id, launch_path, kind and last_used as optional: since
#4916 they are omitted when unknown
instead of null, and the always-empty windows: [] is gone. The typed SDKs
already treat them as optional. For an app's windows, call
list_windows({app: "Name"}).
tools/list also grows by one: run_script (sandboxed JavaScript that drives
the driver's own tools in one call) is on by default. Client allowlists and
tool-count checks need the new name. Turn it off with
CUA_DRIVER_DISABLE_RUN_SCRIPT=1 or "disable_run_script": true in
~/.cua-driver/config.json; the old opt-in CUA_DRIVER_EXPERIMENTAL_SCRIPT=1
is no longer needed and is ignored.
Releases that include #3873 address
accessibility elements only by element_token. Update scripts, agents, and
wrappers that act on elements before you update:
element_token instead of element_index or snapshot_id. Read it
from structuredContent.elements[].element_token in the latest
get_window_state result (from 0.35, pass tree_format: "elements" to
get that array) and send it with the window's pid. The token
names its window, so window_id is optional; a different window_id is
refused with conflicting_element_target. get_window_state still returns
element_index and snapshot_id for display and correlation.invalid_arguments
(<tool>: unknown argument <name>) instead of ignoring them. This includes
element_index and snapshot_id on action tools, and from_zoom on the
Windows type_text, press_key, and hotkey tools, which Windows read
without advertising. Pass window coordinates or an element_token there.zoom against a window target return screenshot_context_missing until
the same session has read that window with a screenshot; Driver no longer
assumes native pixels. from_zoom coordinates return zoom_context_missing
once a newer read replaces the zoomed snapshot. Coordinates for a desktop
target are unaffected.get_window_state call replaces the
previous snapshot of that window and lists the ids it replaced in
invalidated_snapshot_ids. An older token, or a token from another Driver
runtime, is refused with stale_element_token, which lists the process's
current snapshots. Earlier releases reported a token from another runtime
as generation_mismatch.Each one-shot cua-driver call without a session label runs in its own
disposable session, and that session's snapshots are retired when the call
returns. Pass the same session label to the read and to the actions that use
its tokens or pixels.
Stable is the default. Nightlies are immutable builds of main.
cua-driver channel status
cua-driver channel set nightly
cua-driver update --applychannel set only saves the preference; update --apply installs. Fresh
installs can choose the channel directly:
curl -fsSL https://cua.ai/driver/install.sh | bash -s -- --channel nightlyOn Windows, the installer adds its directory to your User Path; to skip that:
& ([scriptblock]::Create((irm https://cua.ai/driver/install.ps1))) -NoPathUpdateTo pin one exact nightly for a single install, set CUA_DRIVER_RS_VERSION to
its full nightly-cua-driver-rs-v… tag from the GitHub release before running
the installer. The pin does not change the saved channel and never falls back
to another release.
The agent's cursor is an overlay, not the real pointer: a session-colored
pointer (cua.default, the only built-in theme). Its color tells agents apart.
It is a visual aid, not an authorization signal.
By default the cursor carries no badge. A small chip appears for about a second when the agent brings something to the foreground or presses a key combo such as ⌘C. The session name appears when you hover over the cursor, or while two or more agent cursors are on screen. Nothing is shown while the cursor moves.
The cursor.badges setting changes this for every cursor at once:
minimal (the default), verbose (the session name on reveal, plus delivery
and target chips on every action), or off. For example,
cua-driver config set cursor.badges verbose restores the earlier badge:
cua-driver start_session '{"session":"research"}'
cua-driver set_agent_cursor_theme \
'{"session":"research","theme_id":"cua.default","reduced_motion":"auto"}'
cua-driver get_agent_cursor_state '{"session":"research"}'set_agent_cursor_enabled hides or shows it; set_agent_cursor_motion tunes
movement: style picks the trajectory (signature_arc by default, or
spring_settle, magnetic, comet_swoop, adaptive, classic), timing
picks native, fitts, or fixed, and effects turns the trail, glow,
magnet, ripple, and squish on or off. The shell shortcut is
cua-driver cursor motion --session research --style comet_swoop. Labels longer than 28 characters are shortened.
A custom theme is a dotLottie archive with a cua/theme.json manifest
("schema": "cua.cursor-theme/2"), 128×128 30 fps animations for all twelve
actions (idle, observe, click, drag, scroll, text, key,
navigate, app, transfer, record, system), a still frame each, and an
author and license. Compile and install it locally; agents can select an
installed ID but never install one:
cua-driver cursor-theme validate theme.lottie
cua-driver cursor-theme build theme.lottie --output theme.cua-theme
cua-driver cursor-theme preview theme.cua-theme --output preview
cua-driver cursor-theme install theme.cua-theme
cua-driver cursor-theme list
cua-driver cursor-theme uninstall com.example.cursor.studioSix motion styles are built in. signature_arc is the default.
| Style | Feel |
|---|---|
signature_arc | One confident arc with a small follow-through. |
spring_settle | An arc that lands with one soft bounce. |
magnetic | Slows near the target, then is pulled in. |
comet_swoop | A wide arc with a short trail. |
adaptive | Careful for small targets, a swoop for long moves. |
classic | The original glide. |
Pick one for every new session by saving it as the default:
cua-driver config set cursor.motion.style magnetic
cua-driver config set cursor.motion.timing fitts
cua-driver config set cursor.motion.effects.trail true
cua-driver config get cursor.motion.styleThe default is saved in ~/.cua-driver/config.json and applies to sessions
started afterwards. Use null as the value to clear a key, or
cua-driver config set cursor.motion null to clear them all. A bad value is
rejected with the list of allowed styles.
Or choose per run, before the cursor first appears, in start_session:
cua-driver start_session '{"session":"research","cursor_motion":{"style":"spring_settle","timing":"fitts"}}'cursor_motion takes the same fields as set_agent_cursor_motion, and
start_session echoes it back. When several apply, the most specific wins:
set_agent_cursor_motion on the sessioncursor_motion in start_sessioncursor.motion.*)signature_arcReduced motion always wins: when the theme or host asks for it, every move is a short straight glide with no effects, whatever you chose.
Cua Driver has no settings window, so there is no cursor picker in the app. Use
cua-driver config set or start_session.
Cua Driver sends content-free, pseudonymous usage events to PostHog's EU endpoint, on by default. The installer shows a notice before the first event.
cua-driver telemetry status --json
cua-driver telemetry disable
cua-driver telemetry inspect cua_driver_mcp_tool_completed --json
cua-driver telemetry reset-idCUA_DRIVER_RS_TELEMETRY_ENABLED=false overrides the saved setting for one
process. The switches every Cua program shares also turn it off:
DO_NOT_TRACK=1, CUA_TELEMETRY=0 and cua telemetry off (see
Telemetry and privacy). inspect builds an event locally without sending it; reset-id
creates a new installation ID (uninstall --purge removes local state).
Events carry the product version, OS family and major version, architecture,
transport, built-in tool names (any other tool is other), and bucketed
durations and outcomes. GeoIP lookup is disabled on every event. Never collected: prompts, tool arguments or
results, typed text, screenshots, accessibility trees, window titles, app
names, file paths, URLs, or error messages. The update check is separate and
not controlled by this setting. See the
privacy policy.