- Added window discovery, targeting, and frame validation for macOS, Windows, and Linux platforms. - Updated computer tool and protocol definitions to support listing and targeting specific application windows. - Refactored native computer tool exposure to function-only representation across models. - Updated documentation and test suites to reflect window-scoped computer control capabilities.
12 KiB
computer
Capture and control one real host window through native OS APIs. Pass a numeric window id for isolated, focus-preserving operation or
desktopfor the selected-display composite and its original global input behavior. This is not thebrowsertool and exposes no DOM or ARIA surface.
User setup, safety guidance, platform permissions, and verified limitations: Window-scoped computer use.
Source
- Entry:
packages/coding-agent/src/tools/computer.ts - Model-facing prompt:
packages/coding-agent/src/prompts/tools/computer.md - Safety prompt:
packages/coding-agent/src/prompts/system/computer-safety.md - Tool registration/gate:
packages/coding-agent/src/tools/index.ts - Approval wrapper:
packages/coding-agent/src/extensibility/extensions/wrapper.ts - Renderer:
packages/coding-agent/src/tools/computer-renderer.ts - Supervisor/protocol:
packages/coding-agent/src/tools/computer/{supervisor,protocol,worker,worker-entry}.ts - Native implementation:
crates/pi-natives/src/desktop.rs - Native loader:
packages/natives/native/loader-state.js
Availability and declaration
computer.enabledgates registration and defaults tofalse./computertoggles it for the current session without persisting settings.- Enabled tool load mode:
essential. - Concurrency:
exclusive. - The tool is always a JSON-schema function, including for models with provider-native Computer Use capability. Provider-native computer declarations cannot represent its optional host-window selector.
/computer statustherefore reportsfunctionexposure for every active model.
Unlike browser, computer operates native host windows. It can act in IDEs, terminals, native applications, browser windows, and system dialogs, but has no structured application or DOM inspection.
Settings
| Setting | Type | Default | Contract |
|---|---|---|---|
computer.enabled |
boolean | false |
Register tool. |
computer.backend |
auto | native |
auto |
Both prohibit non-native fallback. |
computer.display |
string | all |
all or numeric native monitor ID. |
computer.maxWidth |
number | 1920 |
Maximum composite PNG width. Image transports that cannot preserve original detail, including GitHub Copilot Responses and xAI OAuth, cap the effective width at 1280; Claude-family models use the same cap as a compatibility fallback. |
computer.maxHeight |
number | 1200 |
Maximum composite PNG height. Those coordinate-safe transports cap the effective height at 896; other models retain the configured limit. |
The controller snapshots these settings into one DesktopSessionOptions. Crossing the coordinate-safe sizing boundary during a model switch recreates the controller, resnapshots the options, and invalidates the prior coordinate frame; the next pointer action requires a fresh screenshot.
Inputs
Public schema:
{
window?: "desktop" | `${number}`,
actions?: Array<{
type: "click" | "double_click" | "drag" | "keypress" | "move" | "screenshot" | "scroll" | "type" | "wait",
x?: int32 >= 0, y?: int32 >= 0, // preceding screenshot pixels
button?: "left" | "right" | "wheel" | "back" | "forward",
path?: Array<{ x, y }>,
keys?: string[],
scroll_x?: int32, scroll_y?: int32,
text?: string
}>
}
Omitting both window and actions lists targets without capturing a display. desktop selects the configured display composite. A decimal id selects one entry from the latest list and normalizes to 1..=4294967295. Actions require a window; omitted or empty actions captures the selected target without input.
Action shapes
| Type | Shape |
|---|---|
click |
{ type, button: "left" | "right" | "wheel" | "back" | "forward", x, y, keys? } |
double_click |
{ type, x, y, keys?: string[] | null } |
drag |
{ type, path: Array<{x,y}>, keys? }; minimum two points |
keypress |
{ type, keys: string[] }; non-empty array and entries |
move |
{ type, x, y, keys? } |
screenshot |
{ type } |
scroll |
{ type, x, y, scroll_x, scroll_y, keys? } |
type |
{ type, text: string } |
wait |
{ type }; fixed two-second sleep |
Validation rejects missing, unexpected, and action-inapplicable fields before input at both JS and native boundaries. Coordinates, drag points, and scroll deltas must fit signed 32-bit integers; coordinates must also be non-negative. Mouse keys accept unique modifiers only. Keypress strings are case-insensitive, accept aliases and +-separated chords, and fall back to one Unicode character. wheel is the middle-button spelling; middle is invalid.
Nonzero scroll delta d becomes sign(d) × max(1, floor((abs(d)+50)/100)) native steps.
Approval
computerApproval(args) returns:
readwhen every action isscreenshotorwait, including an omitted/empty action batch;execfor any input action or malformed action payload.
Approval prompts include the selected window, render up to 12 ordered action summaries, truncate each line to 240 characters, and cap combined details at 2,000 characters.
The system safety prompt independently treats all UI as untrusted and requires point-of-risk confirmation for consequential actions.
Outputs
A successful list-only call returns:
content[0]: text listingdesktopplus current numeric window ids, app/title, geometry, and focus;details.windows: currentDesktopWindow[];- backend, display-server, permission, and capability metadata; no image or dimensions.
A successful targeted call returns that text as content[0], a fresh base64 PNG as content[1], target dimensions and id, display metadata, capabilities, and executed action names.
The renderer shows the selected target, up to three windows and three displays when collapsed, and the bounded remainder counts. Window/app/title strings and all other native metadata are sanitized before TUI rendering.
The function result uses each provider's ordinary text/image tool-result path. OMP does not upload captures to provider Files or emit native computer_call_output metadata.
Flow
- Tool registration checks
computer.enabled. ComputerToolconstructs a lazyComputerSupervisorand exposes the window-aware function schema.- The model omits
windowandactionsto enumerate targets without a capture. - The supervisor serializes the request, lazily starts one Bun worker, and calls native
DesktopSession.listWindows(). - The tool returns the target list as text with structured metadata and no image.
- The model chooses
desktopor a numeric id; the approval wrapper classifies its action batch. ComputerTool.execute()normalizes the target, validates actions, and passes both to the supervisor.- Coordinate input is rejected until the worker has returned a screenshot of that exact target.
- Native code validates and executes actions in order, defers
screenshotmarkers, and captures one fresh target PNG. - The worker transfers the PNG and preserves target/frame state for the next call.
- The tool returns the refreshed window list followed by the image and structured details.
Capture and coordinate mapping
Window enumeration is topmost-first, deduplicated, capped at 48, and filters minimized, tiny, and completely unlabeled entries. Each DesktopWindow reports id, title, app, global logical x/y/width/height, and focused.
For desktop, native capture enumerates selected monitors, sorts by logical y/x/id, coalesces mirrored rectangles, rejects invalid/overlapping layouts, and builds the bounded composite. Each DesktopDisplay maps global logical geometry to pixelX/pixelY/pixelWidth/pixelHeight in the PNG. Input maps screenshot pixels back through that display layout and retains the original global pointer/focus behavior.
For a numeric id, capture returns only that window. The frame contains one synthetic display mapping the PNG origin to the window's global rectangle. Before coordinate input, native code finds the id again: movement rebases against its current position; closure or size change clears the frame with DESKTOP_LAYOUT_CHANGED.
Window actions bypass global desktop input:
- macOS posts process-targeted NSEvent/CGEvent input;
- Win32 posts mouse/key/character messages to the HWND;
- X11 sends events directly to the selected client.
These paths do not activate the application or move the real pointer. Delivery can still be rejected by an application or protected OS surface.
Every coordinate action in a batch maps through the same frame returned by the prior successful call. A screenshot marker creates no intermediate result. Switching targets requires a capture-only call before coordinates.
Platform variants
| Target | Desktop | Numeric window |
|---|---|---|
darwin-x64, darwin-arm64 |
Bounded screencapture, Quartz/global native input |
screencapture -l, process-targeted events |
linux-x64, linux-arm64 (glibc/musl) |
X11 root GetImage, XTest |
X11 window GetImage, direct client events |
win32-x64 |
xcap displays, virtual-desktop SendInput |
xcap window, direct Win32 messages |
| Other targets | Native loader rejection | Native loader rejection |
macOS performs non-prompting Screen Recording preflight; Accessibility is required for input. Linux speaks X11 directly and requires DISPLAY, RandR, and XTEST. Pure Wayland windows are unavailable. Rootless XWayland has no capturable desktop root; numeric targets can include only XWayland clients. Windows enables DPI awareness before capture.
Worker and session lifecycle
ComputerSupervisor has a 10-second start timeout and 1.5-second close timeout, serializes calls after success or rejection, terminates the worker on abort, and supports owner-scoped bulk close.
ComputerWorkerCore serializes inbound messages and tracks the last target whose screenshot reached the caller. Every execute message carries both window and actions.
Native DesktopSession runs a named omp-desktop-session thread behind a FIFO channel. Every batch has a 60-second deadline checked before each action and final capture. Close waits up to two seconds, is idempotent, and does not let the destructor block indefinitely.
Side effects
- Captures the requested real host window into model/provider context.
desktopcaptures every selected visible display. - Delivers real keyboard and pointer events to the selected application. Numeric targets preserve foreground focus and the real pointer;
desktopuses global input. - Keeps a native worker and desktop session alive across calls.
- May expose secrets or notifications visible in the selected target or desktop composite.
- Does not launch a browser, upload to provider Files, persist screenshots, or spawn arbitrary helpers beyond its Bun/native workers and the bounded macOS capture service.
Errors
Stable native codes:
DESKTOP_INVALID_OPTIONSDESKTOP_INVALID_ACTIONDESKTOP_BACKEND_UNAVAILABLEDESKTOP_PERMISSION_DENIEDDESKTOP_CAPTURE_FAILEDDESKTOP_INPUT_FAILEDDESKTOP_LAYOUT_CHANGEDDESKTOP_COORDINATE_OUT_OF_BOUNDSDESKTOP_DEADLINE_EXCEEDEDDESKTOP_SESSION_CLOSEDDESKTOP_WORKER_FAILED
Tool and worker errors also include:
Computer call requires a window targetComputer actions require a window targetComputer window must be "desktop" or a numeric id from the preceding resultComputer call requires an array of actionsComputer call contains an invalid actionCoordinate computer actions require a screenshot of window ...Computer session is closedTimed out starting native computer worker
Platform remedies are listed in Window-scoped computer use: Troubleshooting.
Limits and proof boundary
- No non-native backend, browser fallback, DOM, or accessibility-tree control.
- Numeric window ids are ephemeral and listings are capped at 48.
- Background event delivery is application-dependent.
- No pure Wayland capture; rootless XWayland cannot provide
desktop, and native Wayland windows cannot be numeric targets. - Linux desktop-coordinate input rejects negative global origins and positions above 32767. Window-local input avoids that global pointer path.
- Windows and X11 window modules were compile-checked, not exercised on live hosts.
- A live macOS addon smoke returned 36 windows through capture-free
listWindows(), then captured id49at500×442, preserved frontmost pid800, and did not warp the real pointer during a targeted move.