Commit Graph
29 Commits
Author SHA1 Message Date
Kelvin Amoaba 030c88deec feat(computer): return tree diffs after actions and add get-app-state --query (#36023)
* feat(computer): send tree diffs after actions and add get-app-state --query

Action results now carry only the accessibility-tree lines that changed since
the caller's last view of the same window (~ changed, + added, - removed as
index ranges, or an explicit no-change line), plus the focused element. The
runtime sidecar keeps the last tree per session/worktree and app, so all three
providers get it. The CLI opts in with treeDiff; older runtimes strip the
param and keep returning the full tree, and --full-tree opts out.

The macOS helper now keeps an element's index across snapshots of a window:
AX identity first, then the next unclaimed index with the same signature for
views that rebuild their AX objects, and never reuses a removed index.

get-app-state --query prints matching elements with their ancestors and
--max-elements caps the output, both in the CLI over the rendered tree.

Custom action names with raw newlines no longer split an element over lines.

* fix(computer): preserve parent changes in snapshot diffs

* fix(computer): refuse stale index reuse for lookalike controls

* fix(computer): print full trees in action replies

* fix(computer): keep controls located in snapshot replies
2026-10-29 23:05:41 +00:00
Kelvin Amoaba c97e6f16bc fix(computer): report macOS actions that changed nothing, read back wrong or hung (#36020)
* fix(computer): fail set-value when its read-back shows a different value

A value_mismatch came back as ok with exit 0, so callers that check only
the exit status took an ignored or misplaced write as success. The runtime
now turns it into a verification_failed error at the sidecar boundary,
which covers every provider and every caller of the RPC.

Part of #33511

* fix(computer): pair macOS AX windows with their CGWindowID

AppKit and WebKit windows don't answer AXWindowNumber, so matching a
capture to its accessibility window fell back to frames. Two windows with
the same frame then paired the screenshot of one with the tree and title
of whichever AX listed first, under the first one's id. Read the id with
_AXUIElementGetWindow, as yabai and Hammerspoon do, and keep AXWindowNumber
as the fallback.

Fixes #27252

* fix(computer): fail fast when a macOS app stops answering accessibility

Every request runs behind one helper lock, and a hung app cost a timeout
per AX read, so one get-app-state on it held every agent for up to the 60 s
RPC timeout. Pin the AX messaging timeout at 1.5 s, refuse an app that
doesn't answer a root probe, and stop a tree walk at the first read that
waits out the timeout, each with an action_timeout error.

A press or write that the app received but didn't answer now fails the
same way, saying it may have taken effect. Before, the click fell through
to AXConfirm and then a synthetic click, so a button whose handler blocked
ran twice, and type-text typed the text again as keys.

* fix(computer): report macOS accessibility actions that changed nothing

AXPress and other accessibility actions came back as plain unasserted
success even when the app ignored them, as Chromium does for a role=button
whose handler sits on a child. After such an action the helper now compares
the target's tree, frame and on-screen windows with the snapshot it took
just before. If they still match after re-reading for about a second, the
result says unverified/suspected_noop; if they change late, the result
carries the settled state instead.

The new reason is additive; older CLIs print it like any other reason.

Refs #31758

* fix(computer): propagate late macOS observation failures

* fix(computer): select existing text before replacement

* fix(computer): share native macOS window identity
2026-10-29 22:55:16 +00:00
Kelvin Amoaba c31fdaa6a5 feat(computer): drive macOS apps in the background by default (#36005)
* feat(computer): drive macOS apps in the background by default

Clicks no longer activate and raise the target first. Pointer and key
input go to the target window's pid through a new BackgroundInputTransport:
mouse and scroll events carry the window id in field 51 and a window-local
location, keys run inside a focus-without-raise key-window scope, and
chords carry no Unicode string so menu key equivalents fire. Scroll and
drag now land (#27306); typed text no longer doubles in Electron (#17942).

The HID tap and focus fence stay for --restore-window only. Results
report action.delivery, the handshake reports supports.input.background,
and a missing private symbol or a popup click fails with
background_unavailable instead of falling back to the foreground.

* test(computer): read typed key strings back instead of diffing live-state events

* fix(computer): keep Unicode characters in one key event

* fix(computer): preserve active macOS key windows

Refuse key-window changes in the active app and surface failed
focus records, including cleanup after partially delivered input.

* fix(computer): guard macOS click routes before delivery

* fix(computer): let paste events read the clipboard

* fix(computer): classify clicks in the requested macOS window

* fix(computer): fence macOS scroll and drag recipients
2026-10-29 22:13:19 +00:00
221eb1ce8b fix(computer-use): trust the dev app's bundle id in the helper's peer check (#26500)
pnpm dev launches the app as com.stablyai.orca.dev, but the macOS helper's
isTrustedOrcaApplication only trusted com.stablyai.orca, com.github.Electron
and ids starting with com.stablyai.orca.dev. (with the trailing dot). Every
computer-use request from a dev build failed with "computer-use agent peer is
not authorized".

Move the bundle id rule into TrustedOrcaApplication in
OrcaComputerUseMacOSCore so it is tested, and trust com.stablyai.orca.dev
exactly as well. Look-alikes such as com.stablyai.orca.devtools stay
untrusted.

Fixes #26498

Co-authored-by: petersindex <287557875+petersindex@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:38 -07:00
F8 Team 810c262b1e fix(computer-use): release macOS screenshot memory after each request (#27374)
The macOS helper serves each sidecar connection on a dedicated thread
that lives as long as the connection, and the sidecar keeps one
connection open while the helper runs. Encoding a screenshot with
NSBitmapImageRep leaves autoreleased objects that keep the captured
image alive, and that thread never drained its pool, so every
screenshot kept a window-sized buffer in WindowServer until the helper
restarted.

Drain an autorelease pool around each request.
2026-10-10 21:11:59 -07:00
Jinwoo Hong 8b9307301c fix(computer-macos): allow final click to dismiss target (#15169) 2026-08-17 15:23:37 -07:00
Brennan Benson 1aa51f6914 fix(computer): preserve accessibility value types (#15031)
* fix(computer): preserve AX value types

* fix(computer): preserve exact integer values

* fix(computer): preserve exact integer exponent forms
2026-08-17 01:20:31 -07:00
Brennan Benson 66dfdc456f feat(computer-use): support macOS middle click and stop the silent left-click fallback (#14721)
* feat(computer-use): support macOS middle click and gate the AX click path

`--mouse-button middle` already validated end-to-end through the CLI, the
zod schema, and the provider validator, and both the Windows and Linux
providers honored it. Only the macOS provider rejected it outright with
"middle-click is not yet supported", so the flag was a dead end on the one
platform that has no fallback.

Two changes:

- Add `.middle` to the macOS button mapping. macOS has no dedicated middle
  event family, so it rides `otherMouseDown`/`otherMouseUp` with the button
  number carried by `mouseButton: .center`; that constructor argument is
  honored for exactly the `otherMouse*` types, so no extra field write is
  needed.
- Validate the requested button before the accessibility fast path, and skip
  that path for buttons it cannot express. Previously the raw string was read
  unvalidated, and `performClickAction` only special-cased `right`, so
  `click --mouse-button middle --element-index N` (no modifiers, count 1) fell
  through to `AXPress` — a left click — and reported success with
  `path: "accessibility"`. Any unrecognized button string did the same. This
  matches guards the Windows and Linux providers already had.

The button enum moves into `OrcaComputerUseMacOSCore` so it is unit-testable;
`main.swift` keeps only the CoreGraphics mapping.

Also documents `--mouse-button` in the computer-use skill guide, which never
mentioned the flag, so agents on Windows and Linux had no way to discover it.

* test(computer-use): cover macOS middle click in the real-desktop e2e suite

* test(computer-use): prove macOS middle-click delivery
2026-08-15 00:41:45 -07:00
Jinwoo Hong 6e63bbbb52 fix(computer-use): prevent repeated screen recording prompts (#13427) 2026-08-09 20:20:36 -07:00
Jinwoo Hong 9b70ce0424 fix(computer-use): settle macOS permission checks (#13025) 2026-08-07 15:59:00 -07:00
Jinwoo HongandJinwoo-H c9485fdded fix(computer): fence macOS HID coordinate clicks (#12981)
Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com>
2026-08-06 21:18:52 -07:00
Brennan Benson c2da0e47f9 fix(computer): deliver macOS coordinate clicks via the HID event tap (STA-3433) (#12839)
Mouse events posted with CGEventPostToPid reach the target app with no
window association, so AppKit never routes the press to a view: hover
states fire but the control is never activated, and the mouseUp is
dropped outright when posted back-to-back. Post click events to the HID
event tap instead (as keyboard synthesis already does), pace them, and
stamp mouseEventClickState so multi-clicks register.

Synthetic clicks now also report verification unverified/synthetic_input
from the helper itself, matching the other synthetic actions.
2026-08-05 22:14:18 -07:00
Neil 14de3fa14d fix(computer): reap mac helper after client loss (#11493)
* perf(computer): add mac helper owner-loss benchmark

Measure the release helper's resident memory before and after its owner-session deadline. Record exact revisions, per-trial RSS, retained state, and clean-exit latency so lifecycle reclamation is reproducible.

* fix(computer): reap mac helper after client loss

Bind the detached macOS helper lifetime to authenticated socket ownership. Reap the helper after its final authenticated client disconnects, and add a startup deadline for sessions that never authenticate.

* test(computer): harden owner benchmark cleanup

* test(computer): make owner benchmark cleanup failure-safe

* test(computer): close remaining owner cleanup races
2026-07-30 19:24:34 -07:00
Neil 8ad9448905 revert: restore pre-worker process boundaries (#11481) 2026-07-29 20:01:31 -07:00
Neil 1f2f809a11 fix(computer): bind macOS helper to supervised peer pid (#11475) 2026-07-29 19:49:36 -07:00
Neil d0f341ad69 fix(computer-use): make modifier clicks interruption-safe (#11451)
* fix(computer-use): make modifier clicks interruption-safe

* fix(computer-use): pace modified Windows multiclicks

* fix(computer-use): address modifier safety review
2026-07-29 18:29:10 -07:00
Neil 4f536ed601 fix(computer): close helper session review gaps (#11428)
* fix(computer): close helper session review gaps

* perf(computer): stop released session registration retries
2026-07-29 16:38:11 -07:00
Neil 0349cb6bdb fix(computer): reap mac helper after client loss (#11425) 2026-07-29 16:17:30 -07:00
Jinwoo HongandOrca f3e379d6dc fix: cache macos computer-use snapshot ax reads (#6130)
Co-authored-by: Orca <help@stably.ai>
2026-06-22 20:57:13 -07:00
Jinwoo HongandOrca 76cb846d68 Harden computer use runtime and CLI (#4705)
Co-authored-by: Orca <help@stably.ai>
2026-06-07 17:49:01 -07:00
09872bfe7b Fix macOS keyboard actions targeting the wrong app (#3960)
* fix: guard macos synthetic input focus

* Tighten macOS keyboard focus safety

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-06-03 19:00:34 -04:00
Trevin Chow be594b8652 Fix macOS helper path and token cleanup (#3961)
## Summary
- stop the macOS helper from unlinking caller-supplied socket and token paths
- keep parent-owned token cleanup tied to the helper startup that created it
- fail closed for non-socket socket-path collisions and cover helper cleanup races with regression tests

## Verification
- swift test --package-path native/computer-use-macos
- pnpm exec vitest run --config config/vitest.config.ts src/main/computer/macos-native-provider-client.test.ts
- pnpm run typecheck:node
- pnpm lint
- pnpm run build:computer-macos
- Electron/helper startup smoke validation
2026-06-03 16:59:02 -04:00
Neil 2f5e1980d4 perf: strip cached macOS screenshots (#4094) 2026-05-31 04:06:32 -07:00
Neil 0adb030bbb Keep macOS Computer Use runtime permission checks quiet (#2764) 2026-05-30 20:17:52 -07:00
Trevin ChowandJinjing 3cb09cc844 fix(computer-use): stop malformed numeric args from crashing the agent (#2894)
* fix(computer-use): stop malformed numeric args from crashing the agent

requiredNumber only checked isFinite, and several request handlers cast the
resulting Double straight to Int/UInt32 (elementIndex, clickCount, pages,
from/toElementIndex, windowId, windowIndex). Int(Double) traps when the value
is outside the integer's representable range, so a single malformed request
such as `{"elementIndex": 1e300}` crashed the entire agent process, killing
all in-flight automation.

Add a bounded conversion helper in the Core library and route every untrusted
Double->integer cast through it. boundedInteger truncates toward zero like
Int(Double) but returns nil (via init(exactly:)) instead of trapping when the
value is non-finite or out of range; the request handlers then surface a clean
invalid_argument error (or resolve to nil for the optional window lookups).

The helper lives in the Core library because the test target cannot import the
executable target where the handlers live. A negative-control run confirms the
out-of-range test traps with the previous Int(Double) cast and passes with the
fix.

* review: close remaining numeric crash paths

- parse stale-element validation indexes through bounded conversion
- reject malformed window selectors instead of dropping the target window
- bound synthetic scroll wheel deltas and cover Int32 conversion

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-05-27 00:53:09 -07:00
Jinwoo HongandOrca 3a40c62169 Improve Computer Use permission setup (#2758)
Co-authored-by: Orca <help@stably.ai>
2026-05-25 14:54:47 -07:00
Jinwoo HongandOrca 0314643244 Fix macOS Computer Use permission helper window behavior (#1722)
Co-authored-by: Orca <help@stably.ai>
2026-05-13 01:40:04 -07:00
Jinwoo HongandOrca ff7de93928 Fix macOS computer-use helper permission checks (#1705)
Co-authored-by: Orca <help@stably.ai>
2026-05-11 17:31:11 -07:00
Jinwoo HongandOrca 0f54103dda Add native computer-use automation (#1683)
Co-authored-by: Orca <help@stably.ai>
2026-05-11 14:20:08 -07:00