Repository navigation
feat(computer): setup and check steps reach inside a computer, and computer app envs - #380
Conversation
…ach inside a computer load_artifact writes files into a computer's home through its driver's data/add; run_code and run_container_unit_tests_verifier run inside it as the session user through computer-exec/v1; env_outcome_verifier hands scripts the env's base URL as env_url. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…app computer ComputerAppEnvironment in agentenv_protocol.computer declares an app to a computer; a computer_app env reads it at put through _declare, stores its folder, and deployed alone composes it onto its computer and driver under /opt/agentenv/apps/<name>/, with env_params app_settings handed to it at boot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The unit-test verifier on a computer stops when it can't clear the reward file, so one an agent left behind a read-only folder isn't graded. - An optional secret setting (SecretStr | None) is a secret too, at save and at boot. - Loads read through grants only on a computer whose network allows everything; an allowlist loads inline. - run_code keeps the runner's 10 s inside the computer's command limit, so a script at the limit is stopped by its runner. - app_settings are checked against the full schema of each app's Settings, required fields included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l names its cause - A secret app setting is checked only as a reference (or null, where it's optional), never against the constraints on its value, which its value then never reaches; a required one left out is named. - A data/add, data/get or run whose connection to the driver breaks fails naming the env, the call and the cause, which httpx often leaves empty (found by the chaos run on EC2: a bare "ReadError: "). - test_artifact_staging imports LocalSandbox from local_docker, where #369 moved it; main's unit tier has failed on it since #374. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4df9a68 to
a46f63c
Compare
…does, whoever runs the tests CodeBuild runs the unit tier as root, which a read-only folder doesn't stop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
EC2 rerun at a46f63c (amd64, c6i.2xlarge): |
….toml alone @computer_app declares what an app is (supports, permissions); its env.toml names it and the computer and driver it deploys alone on, which a bundle builds with it. A name only the class gave was never built, since a bundle reads references from env.toml (decided in review of Greptile's finding). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…puter/ computer_driver.py and computer_app.py move into the computer package as driver.py and app.py, beside env.py and the install, gate, composer and inside modules all three share. The env types keep their names; the plugin-API check reports no break. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eaving Env as it was load_artifact refuses an environment artifact aimed at an env whose type has no loader for it, at save and before it loads, instead of Env growing two methods that only refuse (decided in review). The CLI's hasattr checks are main's again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… deploy, and old workers read user - A computer-exec call whose connection breaks fails naming why and is never sent again: a driver that restarted would run it a second time beside the first, which outlives the driver that started it. run_code's staging ends whatever still runs from the step's folder first, so a retry runs once. - The computer provider refuses an app that names no computer with its own refusal, not AttributeError. - run_container_unit_tests_verifier writes user only when it's set, so a release before env_id targets still reads a container step's user as root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pkill -f reads its pattern as an extended regular expression, so a dot in a step id matched any character and starting read.v1 could end read-v1's runner. The pattern now escapes what such an expression gives a meaning to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
On Modal (
Two first-run failures were the harness's own and are fixed there: one check counted Not in this PR, tracked in FD-4621: 🤖 Generated with Claude Code |
…keep what they share in utils env/envs/computer/ now holds the three env types, env.py, app_env.py and driver_env.py; the modules they share (install, gate, composer, boot, params and inside) move to env/envs/computer/utils/. Env type names don't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or quotes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agentenv_protocol.computer defines AppDeclaration and DriverDeclaration. ComputerAppEnvironment.declaration() and _declare build them, and the computer_app and computer_driver envs store and read them back as models, not plain dicts; the gate, the app's installs and its secret settings read their fields. settings stays the JSON schema app_settings is checked against, and supports the platform predicates drivers name. The stored JSON is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M0.9 and M1.1 of the CUA env epic (FD-4345): the framework's setup and check steps reach inside a computer, FD-4366, and the
computer_appenv type, deployable alone as a one-app computer, FD-4359. It follows the reviewed design, CUA Env M0.9 + M1.1: Code in the Computer, and Computer Apps, with its 15 choices confirmed. It builds on #369 (M0.7), now merged, and is rebased ontomain(#374).What's in it
M0.9: steps that reach inside the computer. Every setup and check step on a computer env now goes through its driver, as the session user, inside the desktop session; none runs on the sandbox's host or as root.
load_artifactof a file or file universe (choice 1a): a computer env overridesload_file_artifact_universeto send the files asdata/addparts (env/envs/computer/utils/inside.py).destination_pathis relative to the home,Downloadsby default; an absolute path is refused, and the record keeps home-relative paths (choice 3).load_artifactrefuses one aimed at an env whose type has no loader for it (load_environment_artifact,load_environment_universe_artifact), at save and before it loads, so a computer env gets a clear refusal instead of anAttributeErrorat run.Envitself is unchanged (decided in review, rather than base defaults that refuse). How an environment universe maps onto a computer's apps is designed with M1.4 (FD-4369).run_codewithenv_idon a computer (choices 1b, 5, 6, 7): a fourth target, chosen when the env's card servescomputer-exec/v1run, so a plugin driver works too. A card servingrunbut notdata/addordata/getis refused, naming what's missing.~/.agentenv/run_code/<step id>/withdata/add, run withrun, the result read back withdata/getafter the same 50 MiB size check as every target, and the folder removed, since the agent can read the home.input["env"]holds the driver on loopback,http://127.0.0.1:<port>and its/mcp, which code inside the computer reaches with no network. The deploy record'smetadata.computernow keeps the driver'sportfor this.timeout_secondsthat leaves the runner less than 10 s of the card'smax_timeout_sis refused, so the runner always stops the script itself; the client waits for the command's timeout plus a margin.request_idand is sent once: a call whose connection to the driver breaks fails naming the cause, since a driver that restarted would run it a second time beside the first, which outlives the driver that started it. Staging first ends whatever still runs from the step's folder, so a retried step runs once.run_container_unit_tests_verifierwithenv_id(choice 8): exclusive withsandbox_name/container_name, it reusesrun_code's driver target. Commands run as the session user (usermust be left unset),env_varsgo into the run'senv, andresult_paths/reward_pathare home-relative (an absolute one is refused) and read withdata/get. The step writesuseronly when it's set, so a worker on an earlier release still reads a container step's user as root.env_outcome_verifier(choice 9): a script whoseverifytakes anenv_urlkeyword gets the env's address, which the protocol client takes as it is. The shipped examples stop stripping/mcp.M1.1: computer apps.
ComputerAppEnvironmentinagentenv_protocol.computer(choices 11, 13, 14): anAgentEnvEnvironmentwhose class declares what it is to a computer.@computer_app(supports, permissions)declares what it is; its env.toml names it (environment_name) and the computer and driver it deploys alone on.linux = Linux(files, python, services)is how a Linux computer installs it (feat(computer): the computer env type and the composer (container forms) #358's install format);launch = Launch(at, command, window, ready=Ready(...)); a skill as a file (skill = "skill.md") or an@agent_skillmethod, not both;@install_step(scope=...)and@ready_check.Settings(aSecretStrfield is a secret), read asself.settings.permissionsis stored, not checked.computerextra) on first use, so an app's code imports with only the plain protocol package.env/envs/computer/asdriver_env.pyandapp_env.py, besideenv.py, and the modules all three share (install, gate, composer, boot, params andinside) move intoenv/envs/computer/utils/(decided in review). The env type names don't change, and the plugin-API check reports no break._declaredispatches by base class:python -m agentenv_protocol.computer._declarereads a driver or an app, and says which; acomputer_driverenv naming an app's class, or the reverse, is refused.agentenv_protocol.computerdefinesAppDeclarationandDriverDeclaration, whichComputerAppEnvironment.declaration()and_declarebuild, and which thecomputer_appandcomputer_driverenvs store and read back as models rather than plain dicts.settingsstays the JSON schemaapp_settingsis checked against, andsupportsthe platform predicates drivers already name. The stored JSON is unchanged, so a driver stored by a released version reads as before.computer_appenv type (env/envs/computer/app_env.py): its env.toml takesmodule,environment_name, and thecomputeranddriverto deploy it alone on, which a bundle builds with it.putreads the class, checks its install, and stores its folder as a file universe.ComputerAppEnvis aComputerEnv, so with a computer and a driver it is checked by gate 1 (its platforms and arches against the base's and the driver's, an install for the base's OS), composed and deployed like one, as a one-app computer./opt/agentenv/apps/<name>/, built like the driver's layer: its files, a venv of its requirements, and its services as supervisord programs run as the session user, with itsENVIRONMENT_NAME.appsis still refused, until M1.3.app_settings(choice 12): a newenv_paramskey,app_settings: {<app>: {<name>: value}}, checked at preflight against the full JSON schema of each app'sSettings(jsonschema, now declared; it already came with mcp), required fields included, a secret (aSecretStr, optional or not) only as a baresecret:/env:reference. At boot each reference is resolved and the values reach the app's services as one JSON variable,AGENTENV_APP_SETTINGS, keyed by app.computer-exec.Where this differs from the design
frames.pyimportsresults.py, which importsmcp, so makingavandPILlazy wouldn't have been enough. Insteadagentenv_protocol.computerloads the driver, card and recording names on first use, and an app's import loads onlyapp.py. Same outcome;recording.pyandframes.pyare untouched.user(choice 8 allowed the session user's name too): leaving it unset is the one way to run as the session user, so the step needn't look the name up.@computer_appnames no computer or driver (choice 11 had the decorator name defaults; decided in review of Greptile's finding): a bundle reads references from env.toml alone, so a computer or driver only the class named was never built. env.toml is the one place for them, as for the app's name.runis never sent again (choice 6 resent a dropped call with itsrequest_id): the driver starts each command in its own process group, so a driver killed outright leaves the command running, and the restarted one doesn't know the request and runs it again beside the first. A dropped call fails instead, and the step's retry stages afresh.Review
Greptile's review of ec434c1 raised six threads. Five are fixed in 4df9a68: the verifier on a computer stops when it can't clear the reward file; an optional
SecretStris a secret at save and at boot; an allowlist network loads inline rather than through grants it may not reach;run_codekeeps the runner's 10 s inside the computer's command limit; andapp_settingsare validated against the full schema. The sixth, that a computer and driver named only by@computer_appcan't be resolved by a bundle (which reads references from env.toml alone), is decided in 1e5def9:@computer_appno longer names them (difference 4).Greptile's review of 4df9a68 raised three more, on secrets in that schema check, fixed in a46f63c: a secret is checked only as a reference (or null where it's optional), never against the constraints on its value, which therefore never reaches a message, and a required one left out is named. a46f63c also makes a
data/add,data/getorrunwhose connection to the driver breaks fail naming the env, the call and the cause (the EC2 chaos run below found a bareReadError:), and fixestest_artifact_staging.py's import ofLocalSandbox, which #374 brought in from before #369 moved it, somain's unit tier fails on it.Review of 1e5def9 raised seven more concerns. Three are fixed in 7a92051: a
runwhose connection breaks is never sent again (difference 5); an app deployed with no computer is refused, not anAttributeError; and the verifier writesuseronly when it's set. Decided to keep as they are: checks on a computer run as the user the agent ran as, with the model key in the verifier's environment; an inline load has no limit but its 64 MiB batches, for now; andComputerAppEnvstays aComputerEnv, which FD-4367 (M1.3) notes. A computer on Modal with a real object store is tested below.Greptile's review of 7a92051 raised one:
pkill -fread the step id as a regular expression, so startingread.v1could endread-v1's runner. Fixed in 334fc5e: the pattern escapes what an extended regular expression gives a meaning to. In Ubuntu 24.04,pgrep -fwith the escaped pattern finds onlyread.v1's runner, and with the old one both.Greptile's review of c68ef97 raised one more, an inline
1500limit on quoted stderr, now_ERROR_TAIL_CHARS(c5d3131); the other thread it listed was the secrets finding fixed in a46f63c. Also decided in review:ComputerAppEnvkeeps its storedmodule, which the composer will read when the computer runs the class's own methods (M1.4, M1.5), and the declarations are typed (above).Testing
test_deep_nesting[json]fails on local Python 3.14, onmaintoo, and a gateway timing test passed rerun alone); the fixture app's integration test passed again there. New tests cover the computer's loader (inline and grant parts, batching, a closed network, refused destinations),run_codeon a computer (staging, loopbackenv, the timeout cap, a dropped call resent with itsrequest_idwhile a new run gets its own, a failed script leaving no folder), the verifier'senv_idtarget,env_url, the environment-artifact refusals, the app class and_declare, the app's bundle folder, its composition, its fit and itsapp_settings.tst/integration/computer, laptop arm64, real Docker and the local registry), at 6ace0d5, and again at c68ef97 after the modules moved and at a4cb89a with the typed declarations: 21 passed and 1 skipped (the agent task, which needs a model endpoint). New: thedesktopexample'sfilestask loads a CSV intoDocuments, reads it withrun_codeinside the computer while asking the driver for its card on loopback, and checks it with the verifier'senv_idtarget; a closed-network computer loads a file inline and runs code that reaches its driver; a fixture app, deployed alone throughagent-env run, comes up with its card named after it and its service running from a plain-protocol venv on the settings the deploy gave it, a secret resolved at boot.data/addper file, through read grants on an open network (0.9 s) and inline on a closed one (1.7 s);data/add: the step fails withRemoteProtocolError: Server disconnected without sending a response; supervisord has the driver back in 3 s, a retry loads every file whole, and no.partfile is left in the home;run_coderun (SIGKILL; rerun at 7a92051): the step fails withRemoteProtocolError: Server disconnected without sending a response, naming the env and the call, and isn't sent again; the killed run's runner and script were still going, and the retry ends them, runs once and leaves no staging folder;run_codepast its timeout: reported as timed out after 5 s, with no process of it left running and its folder removed.tst/integration/computer21 passed and 1 skipped, and chaos 9/10: the driver killed duringdata/addfailed the step withReadError:, naming no cause. a46f63c fixes that; at a46f63c, 21 passed and 1 skipped, and chaos 10/10.agent-env run desktop --task agent, laptop only, the model key held in-process), at 6ace0d5: passed in 53.4 s.load_artifactput the figures in~/Downloadsthrough a read grant; Claude Code, given the computer's skill, madeDocuments/Reportsand moved the file into it through the file manager (the prompt took 40.3 s), and the check confirmed it through the driver withenv_url. Two sandboxes torn down, nothing left.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no actionable issue was found in the changes since the previous review.Summary
This PR lets setup and check steps run inside computers through their drivers. It also adds computer apps that can deploy alone.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR App[App class] --> Declare[Typed declaration] Declare --> Store[Stored app env] Store --> Compose[Compose base, driver and app] Compose --> Computer[Deployed computer] Steps[Setup and check steps] --> Driver[Computer driver] Driver --> Computer Driver --> Results[Files and check results]Reviews (12) · Last reviewed commit: "refactor(computer): type what a driver o..." · Reviewed by Greptile