* feat(desktop): keep an agentenv computer up and drive it through REA's MCP tools - A Python helper keeps one agentenv computer up per build with run_bundle(keep=True) and reports its address, MCP URL, control token and sandbox id; it tears the computer down when stdin closes. - AgentEnvComputer owns the helper, waits for readiness, holds one MCP session to the computer's computer_* tools, records through the computer-recording routes, and removes the sandbox's containers by label when agentenv cannot confirm teardown. The token never leaves it. - start_build_computer starts a computer from a request (archive package, apt repository or .deb, opened on demand with REA in /opt/rea) or from a bundle folder; act_on_build_computer forwards screenshot, click, drag, scroll, type, key and wait and returns the driver's images unchanged; close_build_computer tears it down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(desktop): remove a build computer's container by name when agentenv cannot confirm teardown agentenv's local provider runs an ordinary computer as agent-<sandbox id> without the agentenv.sandbox label, so the label-only fallback found nothing and reported a running computer as removed. The fallback now lists containers by exact name and by label. The real test holds its recording open long enough for a first frame, checks the container by name, and runs rea inside the computer; the guide and tool descriptions say the app opens from the Applications menu and that a screenshot after opening a menu should come from a wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(desktop): own failed build-computer starts, keep ids past oversized screenshots, bound recordings - A start is owned from the helper's spawn. One that fails and whose teardown is unconfirmed stays held under a computer_id close can retry; closeAll refuses new starts, cancels and joins starts in progress, then closes every held computer. - When the first screenshot is too large, the error carries REA's result with the computer_id, and start_build_computer takes screenshot region, format and quality. - Recordings are refused before reading when the manifest's total exceeds the allowance, then fetched one segment at a time, each read bounded and checked against its size and sha256. - The helper keeps the tail of its output (a new tail retention mode in ProviderProcessSupervisor), so a build's own error is not crowded out. - A helper that reported no computer exits on its own before SIGTERM, so no phantom owner is kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(desktop): bound a build computer's action log Every action kept its full arguments for the computer's whole life, so a long session grew REA's memory without limit. The log now keeps the latest 256 actions within 256 KiB, cuts each string to 1 KiB and each list to 64 items (naming what it cut), and keeps the total and the last applied index of each kind of action, so rules over the whole session hold after entries drop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
8.8 KiB
Desktop build computer
A build computer is a local agentenv computer that an agent drives while it builds a desktop app environment. Claude Code cannot attach a new MCP server in the middle of a session, so REA keeps the computer up and forwards the agent's computer-use actions to it through three MCP tools:
| Tool | What it does |
|---|---|
start_build_computer |
Starts a computer from a request or a bundle folder; returns its id and a first screenshot |
act_on_build_computer |
Sends one action to the computer's driver and returns the driver's answer, images included |
close_build_computer |
Tears the computer down, by agentenv's teardown or by removing its sandbox's containers |
The tools are MCP-only, like the debugger sessions: the computer lives as long as the REA MCP server that started it, or until it is closed.
Requirements
- Docker, and an agentenv-framework install whose
agent-envprogram is named by the absoluteREA_AGENT_ENV_COMMAND. Bundles target agentenv 0.9.1431. REA runs its helper,bridge/agentenv/computer.py, with that runner's Python: the interpreter on the program's#!line, or thepython3beside it in its virtual environment. REA_DOCKER_COMMAND, the absolute path of the Docker CLI, lets REA remove a computer whose agentenv teardown was not confirmed. Without it, REA names the command to run instead.- For a request,
REA_BUILD_COMPUTER_REA_ARCHIVE: the absolute path of a tar archive of a runnable REA, with its production dependencies, whose one top folder holdsscripts/rea.mjs. The computer runs REA from it.
Set these where the MCP server starts; a shell export does not reach a server that is already running.
Start a computer
From a request, REA renders a bundle in a private temporary folder: a computer
app on the ubuntu-24.04 base with the reference driver that installs the
app, opens it on demand with gtk-launch <desktop entry> and keeps no seed and
no data hooks. The app is not open when the computer starts: agentenv lists it
in the desktop's Applications menu under Other by the environment's name, such
as mousepad, and that entry launches it through the app's server.
{
"source": {
"kind": "request",
"desktop_entry": "org.xfce.mousepad",
"window_class": "Mousepad",
"install": { "kind": "archive", "package": "mousepad" }
}
}
install takes one of:
kind |
Installs |
|---|---|
archive |
package from the Ubuntu 24.04 archive, on amd64 and arm64 |
apt_repository |
package from the repository at uri with suite and components, trusting the key file key_file |
package_file |
The .deb at url, checked against sha256, for one architecture, after its apt dependencies are in |
Build steps run with the network off, so a .deb's dependencies must be
listed. The bundle adds REA in a build-only layer: Node.js
24.22.1, downloaded per architecture and checked against its release checksum,
and REA from REA_BUILD_COMPUTER_REA_ARCHIVE, both unpacked into /opt/rea by
a root build step, with node and rea on PATH. An exported bundle carries
none of it.
From a bundle folder, such as one export-desktop-application wrote, REA
starts that bundle's tasks/smoke.json as it is:
{ "source": { "kind": "bundle", "path": "/home/ada/mousepad-bundle" } }
REA runs the deploy with run_bundle(keep=True, emulate=False) on the local
sandbox provider, so only the host's own architecture is built. The first start
of a bundle builds its image and can take minutes; later starts reuse it. REA
then waits until the computer's readiness checks pass, opens one MCP session
to its driver and takes a screenshot, with the region, format and quality
given in screenshot. The result holds computer_id, agentenv's sandbox_id,
the environment name or bundle folder, and the screenshot's frame as structured
content beside its image.
A failed start returns the last 16 KiB of the helper's stderr, where a failing
build prints its error, and its exit status; a cancelled one stops the helper,
which tears down whatever agentenv brought up. When that teardown is not
confirmed, the error's computer_id names what REA keeps for
close_build_computer to retry. Closing the server cancels starts still in
progress and waits for them before closing every computer.
Act
act_on_build_computer takes computer_id, an action and the driver's own
arguments for computer_<action>, which REA passes through:
action |
Arguments |
|---|---|
screenshot |
region, format (png or jpeg), quality |
click |
x, y, button, count (2 double-clicks), modifiers |
drag |
path of [x, y] points, button, duration_ms, modifiers |
scroll |
direction, x, y, amount, modifiers |
type |
text, where \n presses Enter and \t Tab |
key |
keys, one chord pressed in order, and repeat |
wait |
ms |
Pointer actions also take frame_id, space and norm_scale, and every
action but screenshot takes screenshot: true for an image afterwards. That
image is taken at once, so after an action that opens a menu or a window, ask
for it with a wait of a few hundred milliseconds instead.
Points are pixels of the latest frame, so take a screenshot first; with
space: "norm" they are on a 0..norm_scale grid over it. Keys are W3C names
or one character; REA translates model spellings such as ctrl+s, Return,
esc and cmd and leaves other names for the driver to judge. An argument the
action does not take is refused before anything is sent.
The result carries the driver's content unchanged, its text and images, after
REA's own JSON: action_index and the driver's structured result. A refusal,
such as a stale frame, is an error carrying the computer's message. A response
larger than the MCP budget is refused with how to ask for a smaller image: a
region, or jpeg with a lower quality. That error keeps REA's own result,
computer_id included, as details.result, since the action or start has
happened.
Every action is logged on the computer in order with the arguments sent, its
start and end and whether it was applied, refused or failed. The log keeps the
latest 256 actions within 256 KiB of JSON, dropping the oldest first. Each
string argument, such as type's text, is kept to its first 1 KiB and each
list, such as drag's path, to its first 64 items; an entry names the
arguments it cut in truncated_arguments, and the driver gets them whole. The
log counts every action, so it says how many it dropped, and keeps the index of
the last applied action of each kind for rules over the whole session. The log
and the computer's recording hooks (computer-recording/v1 start and end under
REA's own request id, and the segments fetched through the driver's data/get
one at a time, each read within its manifest size and checked against it) are
kept for later build stages; they are not tools. A recording larger than a
quarter of the V8 heap is refused before any segment is read.
Close
close_build_computer ends REA's MCP session and closes the helper's stdin,
on which the helper runs agentenv's teardown. When agentenv confirms nothing is
left, the result says teardown: "agentenv". Otherwise REA removes the
containers of that exact sandbox: agentenv runs a computer as
agent-<sandbox id> without a label and labels its other containers
agentenv.sandbox=<sandbox id>, so REA lists both with docker ps -aq --filter, removes them with docker rm -f, and says
teardown: "container_label". When neither confirms it, the error names what
is left and the computer stays listed, so closing it again retries. Closing the
MCP server closes every computer the same way, and a helper whose REA server
exits sees its stdin close and tears down.
The control token that guards the computer's framework-only routes stays in REA: no result, error or log carries it. Code running in the computer as its user can still read it, which is acceptable for a local build.
See the specification for scope and verification.