Desktop control with accessibility-first observation and actions, pixel fallback, screenshots, zoom, screen recording, and switching between registered computers. Qualified on macOS; Windows, Linux and HarmonyOS backends are experimental.
The plugin controls computers, not "the screen". computer_list shows the
registry; one computer is always active, and every tool acts on the active
computer unless given computer.
computer: "<id>" on any tool to act on (and stickily switch to) that
computer. computer_switch changes the active computer without acting.local is the machine the plugin runs on. ssh computers run the bundled
remote agent (pushed automatically at registration). hdc computers are
HarmonyOS devices driven over hdc.When the local helper is installed, it owns the input route even when the
host also includes a native binary. A disconnected helper is an error, never
permission to bypass it with direct input. control_paused and
control_stopped mean the person paused or stopped Computer Use. Stop acting
and wait for them; do not change environment variables, restart the helper,
create another session or use another tool to defeat their choice. After Stop,
the old session remains invalid even when the person allows new sessions.
The helper's own setup, permission and safety controls belong to the person.
Do not operate them or approve the host's pending authorization yourself.
Observe once, act once, then verify.
request_access once. It names missing
permissions and missing tools per platform, and never pops dialogs. Its
via field says who holds the permissions: "app" means the Codewhale
Computer Use desktop app is doing the work (grants belong to it);
"direct" means the hosting app or terminal is. Follow the actual
appHint: bundled Codewhale builds already carry their native helper.list_apps shows running apps only. If the user names an app that is
absent, call open_application once with the original user-provided name,
copied character-for-character — including case, spaces, punctuation, and
suffixes such as app or .exe. Do not translate, localize, normalize,
shorten, or retry with guesses.get_app_state defaults to a text-first summary (macOS AX / Windows
UIA / Linux AT-SPI / HarmonyOS uitest) with controls, values, actions,
layout, element indices and a state_id. Start here without a screenshot,
whether or not the model supports vision. Pass query, role, limit
and offset instead of dumping the whole tree — truncated dumps hide the
title and search field. detail:"compact" is smaller (same indices,
shorter labels). detail:"full" adds nested menus and tree paths.
find_elements searches a cached state_id or observes now. Missing
labels or values mean unknown content, not something to guess. get_value
reads one field live.focus then type
or key for composers (or pass the element target straight to
type/key — it focuses first, in the same call), set_value for
ordinary fields, perform_action (AXPress/Invoke/click…) for advertised
actions, element click. Newlines in type are Return/Enter;
press_enter:true sends after the text. Never expect \\n to send a
chat message. run_actions batches up to 8 steps
(click → type → key return → get_value).
macOS provides background element actions; Linux AT-SPI support depends
on the control. Windows currently refuses scoped semantic mutations.
Windows and Linux are development backends: do not assume their raw
input is background-safe or that native Pause/Stop controls are available.get_app_state({app_ref, include_ocr:true}). Pass ocr_region:[x,y,w,h]
in screen points to recognize one rect instead of the whole window. This
captures locally, without a vision model. Check ocr.status; recognized
blocks include confidence, pixel bounds and ready-to-use coordinate
targets. OCR text is not a control role or an advertised action. Verify
uncertain text and observe again after changes. Other platforms return an
explicit unavailable status while keeping their accessibility state usable.
A text-only model must not infer unlabeled icons, charts or other graphical
meaning from OCR or a screenshot file path.
With vision, when accessibility cannot express the target: screenshot
(optionally zoom for small targets) and act with a coordinate target.
Default coordinates are pixels in the latest returned raster. Pass
space:"screen" to send absolute screen points from the AX tree and skip
conversion. After a new screenshot, old raster pixels are stale.
If the host reports an omitted or oversized image, capture a smaller app
window/region or zoom, then use that returned raster. Do not guess from a
file path or reuse coordinates from an image the model never received.
5b. When the UI needs time — a page loading, a dialog appearing or
dismissing, a spinner finishing — call wait_for instead of looping
get_app_state + wait by hand: it polls the accessibility tree until
a query/role match appears (state:"present") or disappears
(state:"absent"), then returns the matched elements bound to a fresh
state_id you can target immediately. A timed_out:true receipt means
the condition never held — observe and reconsider rather than repeating
the same wait.action_sent: true means it may already have happened — never replay.
On macOS type also reports verified: false (with
verification_required: "screenshot") means the focused control's value
did not reflect the text, so confirm with a screenshot before relying on
the input.{"type":"element","state_id":"s-1","index":4} — prefer this.
Elements are revalidated against the live tree before every action: if the
element moved, the click lands on its fresh center and the receipt carries
target_reacquired: true; if it no longer resolves (or changed role) the
call fails element_stale — call get_app_state again for a fresh
state_id. A state_id only works on the computer that issued it
(state_wrong_computer).{"type":"coordinate","x":496,"y":331} — pixels from the latest
raster only; submit x/y unchanged, never transform them yourself.
{"type":"coordinate","x":100,"y":200,"space":"screen"} is an absolute
screen point (what AX position uses). zoom returns a bindable raster of
its own: after zooming, raster coordinates are pixels in the zoomed image.
Points outside the bound raster fail target_outside_raster instead of
landing somewhere unintended.state_ids.open_application with activate:false to bind input to the
intended process, even when the app is already running; pass pid when two
processes share a bundle id. Then the two halves behave differently:
type, key, focus,
set_value, get_value, select_text and perform_action reach the
bound process without moving the pointer or changing the foreground.
Prefer them. Text entry uses writable accessibility selection when
available; verify the resulting value. get_app_state, list_windows
and screenshot default to the selected app.left_click first tries the bound application's accessibility action,
including focusing a field that is not AXPressable. right_click uses
advertised context-menu actions. scroll uses the target's accessibility
scrollbar; prefer a scroll-area element and read the receipt's unit and
value change. If accessibility cannot act, strategy:"app" posts a
pointer event only when the point is inside the bound app's window, then
restores the cursor — not a global desktop click. Raw double/triple/middle
click, drag, hover, and strategy:"event" fail with
shared_pointer_required before moving the cursor. Missing semantic
scrolling or context-menu support is a refusal, never permission to
activate. Use strategy:"app", another advertised accessibility action,
or a separate computer.open_application(activate:true). Do not select it merely to work around a
background refusal. Receipts identify input_scope: "shared-desktop";
pointer gestures use the physical cursor, even if it is restored afterward.
Keys and raw pointer gestures stop when another app takes focus. Never
keep reactivating after the user takes control; return to activate:false
when the shared-desktop step ends.get_app_state. Use the advertised action (often
AXPress to open a menu, then AXPick on its item), then observe again.window_blocked_by_modal_sheet): deal with the sheet first.
Use app-scoped screenshots (app_ref) to avoid capturing unrelated windows.
Watching the preview does not authorize shared-desktop control. Enable it
only when the user asks to watch; disable it when finished. The preview is a
local app view, not an isolated desktop. Process-directed actions still
change the target app: do not work in an app the user is actively editing.
Close only disposable documents created by your task; never quit a user app.uitest synthesizes touches; there is no hover or cursor.cmd (cmd+c), Linux/Windows use ctrl (ctrl+c).key is the key-press tool: return, enter, backspace, tab,
escape, chords and repeats. hold_key holds for a duration.type sends unicode. Newlines and press_enter become Return; they do
not insert a literal line break or U+FFFC.set_value on ordinary fields; prefer focus then type/key
on chat composers.recording_start → work → recording_stop returns the finalized file path.
macOS uses ScreenCaptureKit inside the signed helper — no system recorder UI
and no desktop dimming overlay (a receipt warning about Screen Recording
permission means the user must grant it once). Linux and Windows recording is
unavailable pending session-owned cleanup; use screenshots. HarmonyOS uses
snapshot-series (no native CLI recorder —
the receipt says so). recording_status / recording_list report bytes and
paths. Screenshots land in the same directory.
stop_computer_control is the kill switch; after it, actions fail closed
for the session. Do not continue after it or after a denied permission.[x,y,w,h]
region; call screenshot; report path, size, computer/display. Black or
empty capture means Screen Recording permission is missing (macOS) for the
app (via: "app") or the host terminal (via: "direct"): say which and
stop.recording_start (parse computer id, fps, display, duration
or "record for 30s" → durationSec on macOS), then report id, path, mode.
To stop, find the running id via recording_list and call recording_stop.computer_list; if asked to add: ssh user@host
(agent is pushed automatically) or hdc [target] for a HarmonyOS device;
otherwise show the registry and remind that any tool accepts computer.computer_list, then request_access per computer; call out
anything that will fail closed with the exact install hint from the receipt.21282f1
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.