Legato
Legato is an open-source computer-use harness for any OpenAI-compatible model (Qwen, DeepSeek, Claude, or your
own vLLM or SGLang server, directly or through OpenRouter). It drives a desktop sandbox (E2B, or any sandbox you can
screenshot and send input to) with a Claude-style computer tool. It waits until the screen is ready instead of
sleeping a fixed time, and it bounds every model call. Apache-2.0.
pip install "legatolabs[e2b]" # the package is legatolabs on PyPI; you import it as `legato`
Results
Same model, same desktops, same tasks, all arms run at the same time. The tasks are 24 Excel-to-PowerPoint office workflows on real Ubuntu desktops (LibreOffice). Times are agent time: first screenshot to last model turn. Ranges are 95% task-bootstrap intervals. Full method, raw data and losses: docs/benchmarks.md.
| Model | Legato compared with | Time | Success |
|---|---|---|---|
| Claude Sonnet 5.5 | Anthropic's computer-use reference loop (re-implemented) | 1.48x faster (0.678 [0.604, 0.757]) | 48/48 vs 48/48 |
| Qwen3.8-27B | OSWorld's Qwen agent scaffold (mm_agents/qwen) |
about the same (0.953 [0.798, 1.134]) | 71/96 vs 53/96 (74% vs 55%, two runs) |
- The Claude result is one run. The saving is almost all fixed waiting: 14 s vs 45 s per task. Model time is the same.
- The Qwen success gain held on two independent task sets: +21 and +17 points.
- The Qwen speed result. In the first run, OSWorld's agent let four rollouts generate up to 32,768 tokens in one reply, so Legato looked 2.4x faster. On fresh tasks, without those runaways, the two are tied. We report the tie.
- On OSWorld tasks with OSWorld's own harness (120 tasks, Qwen3.5-9B), Legato's ready-wait and fast reset cut task time to 0.52 [0.43, 0.65] of the 5 s fixed-wait setting, at equal success. That is not an official OSWorld-Verified submission.
Quickstart
An open model through OpenRouter, on an E2B desktop:
import os
import legato
from e2b_desktop import Sandbox
agent = legato.Harness(model="qwen/qwen3.8-27b", base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"])
desk = Sandbox.create() # needs E2B_API_KEY
try:
result = agent.run("Open the terminal and print the date", legato.attach(desk), max_steps=30)
print(result.report()) # where the time went: model, actions, waiting for the screen
finally:
desk.kill()
The same harness works with Claude: legato.Harness(model="anthropic/claude-sonnet-5.5", ...). It switches to pixel
coordinates and Anthropic's recommended screenshot size. It also works with your own server:
base_url="http://localhost:8000/v1". From the command line:
legato run "Open the terminal and print the date" --model qwen/qwen3.8-27b --base-url https://openrouter.ai/api/v1
Any sandbox, without E2B: pass a screenshot function and an action executor.
env = legato.attach(screenshot=lambda: png_bytes(), execute=lambda action: send_input(action))
action is a legato.Action (type click / double_click / right_click / type / key / scroll / drag / wait, with
pixel x, y, text, keys). More in docs/sandboxes.md and docs/models.md.
What it does differently
| Legato | Typical reference loop | |
|---|---|---|
| After each action | waits until the screen stops changing; on E2B the check runs inside the sandbox in one call | fixed sleep (Anthropic's demo: 2 s; OSWorld runners: 0–5 s) |
| Actions per turn | up to 3, one screenshot after them | one per call, or all returned with a screenshot after each |
| Screenshot history | old screenshots dropped 4 at a time past 8, so the provider's prompt cache keeps hitting | all kept, or one dropped per step |
| Model calls | timeout, retry, 4,096-token budget; a reply cut off before acting gets a note on the next turn | often unbounded |
| Timing | result.report() splits each run into model, action, waiting and screenshot time |
none |
legato.Harness.reference(...) is a re-implementation of Anthropic's reference loop on the same plumbing, so you can
measure the difference on your own tasks.
Reproduce the benchmarks
pip install "legatolabs[office,video]"
legato bench office generate --out bench --repeat 2 # 24 tasks: .xlsx data, .pptx decks, grader specs
legato rl collect --tasks bench/tasks.json --harness legato --policy-url https://openrouter.ai/api/v1 \
--model qwen/qwen3.8-27b --api-key-env OPENROUTER_API_KEY --eval-only --osworld ~/OSWorld \
--version legato --out runs/legato --desktops 6 --group 2 --max-steps 25
legato rl collect ... --harness qwen --no-ready-signal --fixed-wait 0 --version qwen-osworld --out runs/qwen-osworld
legato bench h2h --tasks bench/tasks.json --arm qwen-osworld=runs/qwen-osworld --arm legato=runs/legato \
--ref qwen-osworld --out report
The desktops are OSWorld's Docker VMs, so you need a Linux host with KVM and an OSWorld checkout. See docs/benchmarks.md.
Comparisons and migration
- Legato vs Anthropic's computer-use demo
- Legato vs OSWorld's Qwen agent
- Legato vs Agent S3
- Legato vs UI-TARS-desktop
- Migrating from cua-agent (cua-agent is no longer maintained)
- Migrating from Bytebot (Bytebot is archived)
MCP server
legato mcp gives Claude Code, Cursor or any MCP client a cloud desktop. Each action returns once the screen has
settled, with the new screenshot. See docs/mcp.md.
pip install "legatolabs[e2b,mcp]"
claude mcp add legato -e E2B_API_KEY=your_key -- legato mcp
RL rollouts
legato rl collects graded desktop rollouts for computer-use RL. It records token logprobs and token ids, runs
GRPO-style groups with oversampling and straggler cancel, caps in-flight requests, and profiles each batch. You bring
the model server and the trainer. See docs/rl.md.
FAQ
- Why
pip install legatolabsbutimport legato? The PyPI namelegatowas taken, so the package is published aslegatolabs. The module, the command line tool and the docs all uselegato. - Is Legato an alternative to cua-agent or Bytebot? Yes, for the agent loop. Both are no longer maintained; see the migration guides above.
- Which models work? Any model behind an OpenAI-compatible chat API that accepts images. Native tool calls are used when the server supports them; otherwise JSON in the reply text is parsed. Measured so far: Claude Sonnet 5.5 and Qwen3.8-27B. See docs/models.md.
- Does it need a GPU? No. Point it at a hosted API or your own server.
- Is there a hosted version? Optional: legato.computer runs desktops with pause, resume and fork. Nothing in this package requires it.
More in docs/faq.md. License: Apache-2.0.
Metadata
Release files for legatolabs 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| legatolabs-0.1.0.tar.gz | 74.8 kB | With provenance | Details for legatolabs-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| legatolabs-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for legatolabs-0.1.0-py3-none-any.whl |
Total release size: 146.7 kB
Release files / legatolabs-0.1.0.tar.gz
| Download URL | legatolabs-0.1.0.tar.gz |
|---|---|
| Size | 74.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4ae48076338b205d073570aefb21c6f657b20939e1d2b8fa41ae7c0e9241c933
|
|
BLAKE2b-256 checksum How to use checksums |
347ca3f7dd5b340ccfa6745cdb978bb6692d840d5b34ab952bc53493288d53bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.
Transparency logRelease files / legatolabs-0.1.0-py3-none-any.whl
| Download URL | legatolabs-0.1.0-py3-none-any.whl |
|---|---|
| Size | 71.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b55c6c0de968c2ec6f8fb6faf81107accce3d3266db4569c27e3fded6cad4231
|
|
BLAKE2b-256 checksum How to use checksums |
3374e96d97b2ee2e71662474fc9271b5379433e855a0554c8ee8e36ef7c061a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.
Transparency logSecurity for legatolabs 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for legatolabs-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.