Deep Dive · Computer use

Agent Desktop: Computer Use Without Screenshots

Vercel Labs shipped an Apache-2.0 CLI that lets an agent drive your desktop by reading the operating system's accessibility tree instead of looking at pixels. No vision model, no coordinates, no OCR — and one honest weakness that decides how far it goes.

Agent Desktop: Free Open Source Computer Use, 40x Faster — video thumbnail
Video: Prism Labs 7:16 2 November 2026
agent-desktop Accessibility trees Rust CLI Apache-2.0

🚫 The claim: no screenshot at all 0:00

Every mainstream computer-use stack works the same way: take a screenshot, send it to a vision model, ask the model to infer coordinates, click there, repeat. It is general — it can see anything a human can see — and it is slow, because a frontier model has to look at an image and reason about geometry on every single step.

Vercel Labs' agent-desktop does not take a screenshot at all. It asks the operating system for the live structure of a window — the same accessibility layer screen readers use — and gets back JSON with a reference for every element an agent can act on.

"Give the model text, because text is what models are actually good at."

That sentence is the whole architecture. If the interface can describe itself, there is no reason to make a model guess from a picture of it.

🏢 Who built it, and when 0:26

FactDetail
OriginVercel Labs, the experimental arm of the company behind Next.js — ~10,000 followers and 396 public repositories on GitHub
Announced31 October 2026 by Chris Tate, with one line: "agent browser for your whole computer"
LicenceApache-2.0, free
Relation to agent-browseragent-browser already applied this philosophy to websites; agent-desktop extends it to every app on the machine

The naming is the strategy. agent-browser (Apache-2.0, Rust, ~45k stars) became the token-efficient way for agents to drive web pages: structured text in, actions out. agent-desktop is that same contract, one layer down the stack.

🔄 Observe, act, verify 0:59

The loop in three moves, and the third one is the point:

StepWhat happens
Observesnapshot --app Finder -i returns JSON: every interactive element, labelled by role, each with a ref
Actclick @s8f3k2p9:e8 — the agent targets a ref, not a coordinate
VerifySnapshot again and check what changed. Nothing is inferred from a picture

If you have watched a vision agent click two pixels left of a button, the value is obvious: a ref either exists or it does not. There is no near-miss, no drifting coordinate space, and no need to re-derive where the button was.

The architectural choice worth naming: the loop lives in your agent, not in the tool. agent-desktop has no model inside. It is deterministic plumbing — closer to a compiler or a database client than to an agent — which is why it can be called by Claude Code, Codex, Cursor or a shell script without favouring a vendor.

⚡ The speed gap 1:39

The video's headline numbers, as it presents them:

OperationTimeWhy
Accessibility tree read50–200 ms (typical ~120 ms)Local OS call; no model in the path
Single call via the native binary~21 msProcess overhead only
Screenshot-vision computer use, per observation3–8 sModel must look at an image and reason about coordinates every step

Multiply by the number of observations a task needs — he uses 50 as an illustration — and the difference becomes seconds versus minutes. The second saving is financial: a snapshot is plain JSON of a few hundred tokens, not an image sent to a vision model on every glance.

Provenance matters here. These figures are attributed in the video to "third-party testing", and no performance numbers appear in the project's own documentation or README — so treat them as a plausible order of magnitude rather than a specification. The structural claim is safe without the numbers: a local JSON read has no model in the loop, and a screenshot loop does, on every step.

📋 What a snapshot actually looks like 2:45

The contract is small enough to read in full. A trimmed snapshot from the project's own quick start:

FieldMeaning
snapshot_idIdentifies this observation — carried inside every ref from it
ref_count / completeHow many actionable elements were found, and whether the tree was fully traversed
treeNested roles and names: a window, its children, their children
ref_idThe handle the agent acts on: @s8f3k2p9:e8
available_actionsWhat may be done to that element — Click, and so on

No bounding boxes to estimate, no OCR, no coordinate space to calibrate, no pixel matching. Every command prints one JSON envelope and exits, so an agent can parse it with the same tooling it uses for any other CLI.

This is why the approach is token-cheap as well as fast: a few hundred tokens of roles and names describe an interface that would cost an image — and a model's reasoning about that image — on every step. The saving compounds with the length of the task.

🔖 Refs, and the stale-ref design 4:40

The detail that separates this from a wrapper around accessibility APIs: the ref carries the id of the snapshot it came from. A stale ref from an old observation is rejected rather than silently clicking whatever now occupies that position.

SituationWhat the tool does
A bare ref like @e8 with no snapshotFails with INVALID_ARGS
The UI changed since that snapshotSTALE_REF — take a new snapshot
Two elements could matchAMBIGUOUS_TARGET — disambiguate, do not guess
Action succeededConfirmed before returning, so the agent's next step starts from truth

This kills the classic computer-use failure mode — the screen changed and the agent did not notice — by making the mistake impossible rather than detectable. It is the same principle as a database's optimistic concurrency check, applied to a user interface.

🛡 Safety by refusal 3:45

The safety posture is unusually conservative for a developer preview, and the video is right to call it refreshing:

BehaviourWhat it means
Headless by defaultNo pointer movement, no focus stealing. Physical input requires --headed
Raw input primitives refuse to runkey-down, key-up, mouse-down, mouse-up are four of the sixty commands and will not execute until a daemon can release a held key or button
Protected OS processesCannot be closed
Elevated apps on WindowsReject standard callers with a permission error instead of letting an agent escalate
"When in doubt, refuse. Make the agent's world predictable rather than powerful."

That is the correct priority order for a tool that has hands on your whole machine. A predictable agent can be retried and composed; a powerful one that occasionally does something unexpected cannot be trusted with a task loop.

📦 Inside the repository 5:22

PropertyDetail
LanguageRust, with a platform adapter per OS
Accessibility layersmacOS AX API (13+), Windows UI Automation (10 1809+), Linux AT-SPI (Ubuntu 24.04+, X11 or Wayland)
Commands60 command names, 56 operational — observe, act on a ref, keyboard/mouse, apps and windows, notifications, clipboard, wait, sessions, system
Installnpm install -g agent-desktop, Homebrew on macOS/Linux, or native archives from GitHub Releases
EmbeddingA C library (libagent_desktop_ffi) for in-process calls, with a Python ctypes example in the README
Interfaces beyond the CLIAn MCP server mode, and a skill installable into agent harnesses
Telemetry of its ownOptional traces and a cursor overlay for debugging

The consequence is neutrality. Anything that can run a shell command can drive a desktop: Claude Code, Codex, Cursor, or your own script — which is genuinely rare in a category where most computer-use stacks assume you have married one vendor's agent.

⚖️ The honest debate: what trees cannot see 5:58

Here is the weakness the video promises at the start, and it is real and structural: accessibility trees are only as good as the labels developers wrote.

CaseWhat happensMitigation
Canvas-rendered apps, games, most design toolsInvisible to the approach — no tree to readScreenshot vision still exists as a fallback
Electron apps with thin or missing labelsHalf a UI: elements exist but cannot be identifiedPull the web content out to agent-browser over a DevTools port
Apps that publish no accessibility tree at allNot automatable by this tool, full stopUse a different approach for that app

The Electron bridge is the clever part, and it is documented rather than improvised: for a Chromium or Electron app you launch, agent-desktop can open a DevTools port that agent-browser connects to, so the native chrome stays on the desktop tool and the web contents go to the browser tool. The two projects are designed to meet in the middle.

The architectural split, stated cleanly: screenshot loops handle anything visible, including canvas; tree reads handle anything labelled, instantly and cheaply. The right answer is not one or the other — it is a router that knows which surface it is looking at. Which is why "accessibility-first, pixels as fallback" is the correct default rather than a compromise.
There is also a privacy dimension the README raises and reviews usually skip: snapshots, clipboard reads, screenshots and debug artifacts can all contain private desktop content, and the project says to keep them out of public logs and uploads. An agent that reads your whole accessibility tree is reading everything on screen — including whatever happens to be in the window behind it, if it is asked to list surfaces.

🗓 Eight months to 1.0, then a sprint 6:30

DateEvent
19 February 2026Repository created — quiet start
Spring–summerIteration on the ref model and the per-platform adapters
28 Octoberv1.0.0 released; v1.0.1 patched the same day
30 Octoberv1.0.2
31 OctoberPublic announcement — after the stable release, not before it
3 Novemberv1.1.0, and ~13,200 stars / ~910 forks

The video's read is the right one: shipping the software before the hype is a credibility signal, and in a category this failure-prone the ordering is the strongest evidence you get from outside. The star count moved fast too — the video quotes 8,778 stars and 609 forks within days of the announcement; the repository was at roughly 13,200 stars and 910 forks when checked for this article, so the trajectory continued.

✅ Claims checked

ClaimVerdictEvidence
Vercel Labs shipped Agent Desktop, free and open sourceCorrectvercel-labs/agent-desktop, Apache-2.0, Rust, created 19 Feb 2026
It reads the OS accessibility tree instead of screenshotsCorrectREADME: "reads the accessibility tree of a desktop app and returns it as JSON, with a ref for every element"
macOS, Windows and Linux supportCorrectAX API (macOS 13+), UI Automation (Windows 10 1809+), AT-SPI (Ubuntu 24.04+)
Refs carry the snapshot id; stale refs are rejectedCorrectBare @e8 fails INVALID_ARGS; changed UI returns STALE_REF or AMBIGUOUS_TARGET
Headless by default; pointer and focus untouched unless --headedCorrectREADME: "Actions are headless by default: no pointer movement, no focus change"
Raw key-down/mouse-down primitives refuse to runCorrectFour of 60 commands refuse until a daemon can release a held key or button
60+ commands across all three platformsCorrect, precisely60 command names, 56 operational, grouped into observe / act / keyboard+mouse / apps+windows / notifications / clipboard / wait / sessions / system
Ships as CLI, installable skill, and a C library with Python bindingsCorrectnpm/Homebrew CLI, skills command, libagent_desktop_ffi with a ctypes example; also an MCP server mode
Electron/Chromium handoff to agent-browser over a DevTools portCorrectDocumented: launch "App" --cdp opens a DevTools endpoint for agent-browser
Canvas apps and unlabelled UIs are blind spotsCorrect — stated by the projectDocs: "It reads accessibility trees and does not match pixels, so it cannot see a UI that publishes no accessibility tree"
Announced 31 October by Chris Tate; v1.0.0 on 28 OctoberConsistentReleases: v1.0.0 and v1.0.1 on 28 Oct, v1.0.2 on 30 Oct, v1.1.0 on 3 Nov
8,778 stars / 609 forks within days of 1.0Was accurate; now higher~13,200 stars / ~910 forks on 4 November 2026
Tree read 50–200 ms (typical ~120 ms); ~21 ms per native call; screenshot vision 3–8 sUnverifiedAttributed in the video to third-party testing; no performance figures in the project's own docs or README
Vercel Labs ~10,000 followers, 395 reposCorrect10,082 followers, 396 public repositories
Independent reproduction on Linux, no pixel matching in the loopSecond-handReported as Flavio Copes' end-to-end test in the video; not independently re-run here

Basis note. Built from the full 7:16 transcript, then verified against the vercel-labs/agent-desktop README, the project documentation at agent-browser.dev/desktop, and the GitHub releases API on 4 November 2026. The structural claims hold; the performance numbers do not appear in the project's own material and are marked as reported. Star counts move quickly and are dated.

🔑 Key takeaways

  • Read the interface, do not photograph it. The accessibility tree is a structured description the OS already maintains — and the model is far better at text than at estimating coordinates.
  • No model inside the tool. agent-desktop is deterministic plumbing the agent calls; the loop stays in your harness, which is why it works with Claude Code, Codex, Cursor or a shell script.
  • Refs beat coordinates. A ref either exists or it does not — no near-misses, no drifting coordinate space, no pixel matching.
  • Put the snapshot id inside the ref. A stale handle is rejected instead of clicking whatever moved into that position: optimistic concurrency, applied to a GUI.
  • Verify by observing again. Observe, act, observe — the third step is what turns actions into a loop that can be trusted.
  • Predictable beats powerful. Headless by default, no focus stealing, raw input primitives disabled, protected processes uncloseable, elevated apps refusing standard callers.
  • Speed and cost move together: a local JSON read has no model and no image, so it saves both seconds and tokens on every observation.
  • The blind spot is structural: accessibility trees are as good as the labels developers wrote. Canvas is invisible; thin Electron UIs are half-visible.
  • The right answer is a router, not a winner: trees for labelled surfaces, pixels as fallback, and a DevTools handoff to agent-browser for Chromium and Electron web content.
  • Neutrality is the moat. Shipping as CLI, skill, C library and MCP server means no vendor lock-in — rare in computer use, where most stacks assume one agent.
  • Release order is a credibility signal. Stable versions first, announcement second, patch same night: eight quiet months, then a sprint.
  • Treat snapshots as sensitive. An accessibility tree read of a window can contain private content; keep snapshots, clipboard reads and traces out of logs and uploads.

🔗 Resources

Basis note. Everything structural in this article — the command groups, the ref format and its error codes, the headless default, the platform accessibility APIs, the C library, the MCP mode, the Electron handoff — was checked against the repository and its documentation, not taken from the transcript. The performance figures are the one part that could not be verified: they are marked as reported in the claims table, and the reasoning that survives without them is stated separately.

⏱ Timestamp index

0:00Screenshot loops at 3–8 seconds per look
0:26Vercel Labs, and the October 31 announcement
0:59Observe, act, observe
1:39The speed gap
2:16The CLI in action
2:45What a snapshot looks like
3:15Ref format and stale-ref rejection
3:45Safety by refusal
4:23Traction: stars, forks, 60 commands
4:57No pixel match in the loop
5:22Inside the repo: Rust, adapters, FFI
5:58The honest debate: labels and canvas
6:30Eight months to 1.0
7:03Give your agent hands
☰ View all