Computer & Tools Layers#
Layer 1: computer/#
Computer ABC (base.py)#
The capability surface of a computer. Three kinds of operations:
Observe (one-shot queries):
capture,cursor_position,displays,windows,focused_window,ui_tree,clipboard_read,processes,environment,read_file,list_dir,stat,notifications.Act (one-shot state changes):
pointer,key,text,open_app,close_app,focus_app,focus_window,move_window,close_window,perform_ui_action,clipboard_write,write_file,delete,move,mkdir,shell,exec,lock_screen.Subscribe (server-pushed streams):
subscribe_display,subscribe_input— return async iterators.
Plus a sync capabilities() returning a CapabilityManifest so callers can
check support before attempting an operation.
Convenience helpers on top of the abstract primitives: click, drag,
scroll, press, hotkey, type_text, clipboard_text / clipboard_set_text.
LocalComputer (local.py)#
Concrete implementation for the current machine. Wraps the existing screen
capture (mss), accessibility backends (AppleScript / AT-SPI2 / UI Automation),
input (pyautogui), and filesystem / process primitives. All blocking I/O
runs in asyncio.to_thread.
RemoteComputer (remote.py)#
Implements the same Computer ABC by forwarding every call to a
Peer. Each abstract method serialises params, awaits
peer.call(method, params), and validates the result back into a Pydantic
model. Subscriptions return async iterators backed by peer.stream(...).
capabilities() is synchronous (per the ABC) and served from the manifest
cached during the HELLO handshake — no round-trip.
ComputerDispatcher (dispatcher.py)#
Server-side router. Implements the protocol’s Dispatcher Protocol by
mapping each method name (display.capture, input.pointer, …) to the
matching Computer method, with Pydantic de/serialisation at the boundary.
types.py#
Pydantic value types exchanged across the boundary: Point, Rect,
Pixmap (with built-in pixel ↔ logical coordinate translation), Display,
Window, Process, UIElement, PointerEvent, KeyEvent,
ClipboardContents, FileEntry, CompletedCommand, Environment,
Notification, the Capability enum, and the CapabilityManifest.
Auxiliary modules#
capture.py— mss-based screen capture. Wider-than-1920 displays are downscaled; the resultingPixmapalways carries the true logical screen dimensions so coordinates translate cleanly.marks.py— Set-of-Marks rendering helpers (draw_som_marks,overlay_cursor). The element list is now produced byComputer.ui_tree()rather than by a separate accessibility query.ocr.py—ocr_image(png_bytes)runs the best available OCR backend (pytesseract → macOS Vision → WinRT OCR). Decoupled from capture so it works on aPixmapfrom any Computer, including a remote one.sandbox.py— per-session audit log used by tools that record actions.
Computer Use: Three-Tier Grounding Architecture#
All computer-use interaction follows a strict three-tier hierarchy. The tier priority is encoded directly in tool descriptions, so any model that reads the tool list follows it without a separate orchestration layer.
Tier 1: Semantic AX ──(element not found)──► Tier 2: Grounded SoM ──(AX tree empty)──► Tier 3: Raw Pixel
Every action at any tier is followed by an automatic per-action pixel-diff against the prior screenshot — a free feedback signal that tells the model whether its action had any visual effect.
Tier 1 — Semantic Accessibility Grounding#
The ui tool finds elements by title and role via the platform
accessibility API and acts on them directly. No pixel coordinates are
involved at any point.
The tool performs a depth-first search over Computer.ui_tree(), matching
case-insensitively and by substring. When a match is found:
macOS:
click elemvia System Events AppleScript, with a bounds clause to disambiguate multiple controls sharing a label.Linux:
doAction("click")via AT-SPI2.Windows:
click_input()via UI Automation / pywinauto.
If the backend does not support native actions, the tool falls back to a
pointer click at element.bounds.center — still Tier 1, because the
coordinate comes from the accessibility tree, not from the model.
Key property: resolution-independent. Element positions are in logical pixels from the OS and are invariant to DPI, window position, and app version.
Tier 2 — Grounded Set-of-Marks (SoM)#
When Tier 1 cannot find a named element but the AX tree is non-empty, the
model calls screenshot(marks=True). The pipeline:
Capture screen via
mss, downscale to ≤ 1920 px (LANCZOS), record scale factorssx = W_img / W_logical,sy = H_img / H_logical.Query the same AX backend as Tier 1 for all interactive elements with their logical bounding boxes.
Project each bounding box into image pixels:
x_img = floor(x_logical × sx),y_img = floor(y_logical × sy).Render numbered, colored bounding-box chips onto the image via Pillow.
Return the annotated PNG and a text index:
[3] AXButton "Save" at (120,340) 80×30 — click mark 3 to interact.
The model picks a mark by index. The system maps the index back to the element’s logical center and issues the click — no model coordinate estimation occurs.
Why “grounded” matters: marks come from the AX tree, so each mark is a platform-confirmed interactive control. False-positive rate is zero by construction, unlike vision-detector-based SoM (e.g. OmniParser).
Tier 3 — Raw Pixel Mouse Control#
Used only when the AX tree is empty (games, canvas apps, custom-rendered
surfaces). The model reads coordinates from an unannotated screenshot and
passes image_width / image_height with every click. The mouse tool
normalizes those back to logical pixels before issuing the pointer event
(see Retina normalization below).
Unified Element Schema#
All three platform backends produce a flat list of dicts with a single schema:
{ mark, role, label, x, y, w, h } ← all in logical screen pixels
The SoM renderer, element search, click dispatch, replay engine, and pixel-diff feedback are all platform-agnostic above this contract.
Platform |
API |
Element limit |
|---|---|---|
macOS |
AppleScript / System Events |
120 per window |
Linux |
AT-SPI2 ( |
150, depth ≤ 8 |
Windows |
UI Automation ( |
150 |
Retina / HiDPI Coordinate Normalization#
mss captures at physical resolution (e.g. 2880×1800 on a 2× Retina Mac).
The OS pointer API operates in logical pixels (e.g. 1440×900). Passing
physical-pixel image coordinates directly to the pointer API produces clicks
at 2× the intended position.
Pixmap carries four dimension fields: width, height (image after
downscaling), logical_width, logical_height (from OS display API).
Scale factors sx and sy are pre-computed and used in two places:
SoM rendering: logical AX coordinates scaled up to image pixels for chip placement.
Mouse tool: image coordinates from the model scaled back down:
x_logical = floor(x_img / sx),y_logical = floor(y_img / sy).
A 2% no-op band skips scaling when image and logical dimensions effectively match, avoiding rounding artefacts at unity scale.
Per-Action Pixel-Diff Feedback#
After every screenshot, capture.py pixel-diffs the new capture against the
previous one using a per-channel absolute-difference threshold of 10 intensity
units (to filter compression noise). The result is a structured report
appended to every screenshot tool output:
changed: bool
change_fraction: float # 0.0–1.0
changed_region: [x, y, w, h] # bounding box of changed pixels
summary: str # natural-language signal for the model
Example outputs:
12.3% of pixels changed in region [x=400, y=200, 600×300px].
No visible change detected — the action may not have had any effect.
This closes the perception loop per-action, in tens of milliseconds, with no VLM call. The model can immediately detect ineffective actions and re-plan (retry via a different tier, adjust parameters, or check focus).
Tier-Agnostic Action Replay#
Every action — regardless of which tier executed it — is recorded with two parameter dicts:
params— human-readable summary for the audit display.replay_params— complete, platform-neutral parameter set for re-execution.
A Tier 1 (semantic AX) action records {action, app, title, role, ...}.
A Tier 2/3 (pixel) action records {action, x, y, image_width, image_height, ...}.
The replay engine re-issues the action through the same tool interface without needing the original screenshot, the original AX tree, or the original tier-selection decision.
Layer 2: tools/#
base.py#
ToolResult — title, output, error, attachments, metadata
Attachment — filename, content (bytes), media_type
ToolContext — session_id, permission_handler, computer
Tool — ABC: name, description, Params (Pydantic), execute()
The crucial field on ToolContext is computer: Computer — every tool
calls ctx.computer.X(...) instead of poking pyautogui / AppleScript
directly. Swap in a RemoteComputer and the same tool runs against another
machine with zero code changes.
ToolContext.check_permission(tool, argument, description) calls the
injected handler before every action. Raise PermissionDeniedError to block.
Tool files#
Each file contains one Tool subclass. None of them open subprocesses or
poke OS APIs directly — they always go through ctx.computer.
File |
Class |
What it does |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Local session state; never remoted |
|
|
Local audit log; never remoted |
Next: Protocol Layer →