Files
wehub-resource-sync a789495a98
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:34 +08:00

150 lines
6.7 KiB
Markdown

# Proposal: native `computer` tool for macOS computer use
## Summary
Add a single native tool, **`computer`**, that lets the agent observe and control
the macOS GUI — screenshots, the accessibility (AX) tree, mouse/keyboard input,
window/app management, and clipboard — through one `action`-dispatched interface.
This mirrors the existing **`browser`** tool (`crates/jcode-app-core/src/tool/browser.rs`):
one registered tool, an `action: String` that selects a sub-operation, with optional
typed params. It gives jcode a closed control loop (*see screen → decide → act*)
without depending on a browser or external automation tooling.
## Motivation
- The agent can already drive a browser; it cannot drive native macOS apps, system
UI, or anything outside the browser sandbox.
- "Computer use" agents need exactly three primitives: **read the screen**, **read
UI structure**, and **synthesize input**. macOS exposes all three through the
Accessibility / Quartz Event Services / ScreenCaptureKit stack.
- A single, well-scoped tool keeps the tool surface small and the permission story
in one place.
## Architecture
```
crates/jcode-macos-control/ (new) cfg(target_os = "macos") platform crate
└─ AX (accessibility-sys), CGEvent (core-graphics),
CoreFoundation (core-foundation), screenshots (ScreenCaptureKit / CGDisplay),
app/window control (objc2 + objc2-app-kit), clipboard (objc2 NSPasteboard)
crates/jcode-app-core/src/tool/computer.rs (new) ComputerTool
└─ thin dispatch layer: parse input -> call jcode-macos-control -> ToolOutput
└─ registered in crates/jcode-app-core/src/tool/mod.rs base_tools()
```
- All native APIs are reached through existing Rust bindings (`objc2`,
`accessibility-sys`, `core-graphics`, `core-foundation`) — **no Swift/ObjC build
step**.
- On non-macOS targets the tool still registers but every action returns a clean
`unsupported on this platform` error, so the tool list stays stable across OSes.
- `screenshot` returns its image via `ToolOutput::with_image` (base64), matching how
`browser` returns screenshots today.
## Permissions (the important part)
macOS splits this across **four** TCC permissions. Programmatic *request* support
differs per permission:
| Permission | Used for | Programmatic request |
|---|---|---|
| **Accessibility** | drive other apps' UI, inject `CGEvent` input | ⚠️ prompt + deep-link only; user must toggle |
| **Screen Recording** | screenshots / `get_ui_tree` of some apps | ✅ `CGRequestScreenCaptureAccess()` |
| **Input Monitoring** | reading the global input stream | ✅ `IOHIDRequestAccess(...)` |
| **Automation** (Apple Events) | scripting cooperating apps | ✅ prompts on first send, per target app |
**Accessibility is the one that cannot be auto-granted** (Apple's anti-malware
boundary), and it is required for input injection. Best achievable flow, exposed via
the `request_permissions` action:
1. `AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt: true])` — shows the
system dialog *and auto-adds jcode to the Accessibility list* (toggled off).
2. Deep-link to the exact pane:
`open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"`.
3. Poll `AXIsProcessTrusted()` until granted, then report ready.
So the experience becomes **one prompt + one toggle**, not "go hunt in Settings" —
but never zero-touch for Accessibility.
`check_permissions` reports the current state of all four so the agent can tell the
user precisely what is missing before attempting control.
## Actions
`action` (required) selects the operation. Params below are optional and validated
per action.
**Permissions**
- `check_permissions` → status of accessibility / screen-recording / input-monitoring
- `request_permissions` → prompt + deep-link flow above
**Observe**
- `screenshot``{ display?, window_id?, region? }` → image
- `get_ui_tree``{ pid? | frontmost, max_depth? }` → serialized AX tree (role, title, value, position, size, actions)
- `find_element``{ role?, title?, value?, pid? }` → matching elements + their identifiers
- `element_at``{ x, y }` → element under the point
**Mouse**
- `move``{ x, y }`
- `click` / `double_click` / `right_click``{ x?, y? }` (current position if omitted)
- `drag``{ from: [x,y], to: [x,y] }`
- `scroll``{ x?, y?, dx, dy }`
**Keyboard**
- `type``{ text }`
- `key``{ keys: "cmd+shift+4" }` (chord)
- `key_down` / `key_up``{ key }`
**Semantic AX (preferred over raw input when available)**
- `press``{ element }` (AXPress)
- `set_value``{ element, value }`
- `get_value``{ element }`
- `perform_action``{ element, ax_action }`
- `select_menu``{ app, path: ["File", "Export…"] }`
**Window / app**
- `list_apps`, `activate_app` `{ app }`
- `list_windows` `{ pid? }`, `focus_window` `{ window }`
- `move_window` `{ window, x, y }`, `resize_window` `{ window, w, h }`
- `minimize_window` / `close_window` `{ window }`
**Clipboard**
- `get_clipboard`, `set_clipboard` `{ text }`
> Element identifiers: `find_element` / `get_ui_tree` return stable-enough handles
> (e.g. `pid` + AX path or a session-scoped element id) that semantic actions accept,
> so the agent can act structurally instead of by pixel coordinates when possible.
## Safety
Computer use is high-blast-radius, so:
- **Permission-gated** like other powerful tools; refuses early with a clear message
if Accessibility/Screen Recording is missing.
- **`dry_run` param** on mutating actions — resolves and reports the target without
acting.
- **Screenshot-assisted confirmation** for destructive coordinate clicks (return the
region/element being targeted).
- **No global input *capture*** in v1 (we synthesize input but do not log the user's
keystrokes), keeping us out of Input Monitoring unless a future feature needs it.
- Prefer **semantic AX actions** over blind coordinate input wherever the element is
resolvable — more robust and more auditable.
## Implementation plan
1. `jcode-macos-control` crate: permissions, screenshot, AX read, AX action,
CGEvent input, window/app control, clipboard. Unit-test the pure parts
(input parsing, chord parsing, tree serialization).
2. `ComputerTool` in `tool/computer.rs`: input struct + `action` dispatch +
schema + description; register `"computer"` in `tool/mod.rs` `base_tools()`.
3. Default-off / gated rollout + docs in `docs/`.
4. Follow-up: Windows/Linux backends behind the same tool surface.
## Open questions
- Element handle format — `pid`+AX-path vs an opaque session-scoped id cache?
- Should `request_permissions` block-and-poll, or return immediately with status and
let the agent re-check?
- Default enablement: opt-in flag vs always-registered-but-gated?