Repository navigation
feat(run_code): give a script on an env's host the env's address on any provider, and stage each run in a dir of its own - #228
earakely-scale wants to merge 5 commits into
Conversation
…s that host reaches them
A run_code script on an env's host had no way to find the env: scripts assumed
the gateway at 127.0.0.1:18765, where VM providers publish it. The local
provider maps that port into 20000-30000, since deployments share one host, so
those scripts got connection refused.
Its input now carries env = {id, gateway_url, mcp_url}, each URL under one of
the sandbox's tunnels rewritten to loopback at the port the tunnel publishes
(host_port), since a host can't always call its own public URL. On VM providers
that is 127.0.0.1:18765, as before; on the local provider, the mapped port.
Scripts on a sandbox or in an agent get no env.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…so runs on one machine can't clear each other's run_code staged into /tmp/agent_env_run_code/<step id> on the host. Local sandboxes share this machine's /tmp, so two runs of a step with the same id, such as the same task run twice at once, staged into one dir: a later run cleared the earlier one's files (runner.py missing) or replaced its input and result, so one script read another run's env. Chaos found it with four runs of one bundle at once, on the old release too. A host target now stages in sandbox.scoped_name(_RUN_DIR)/<step id>, which on the local provider ends in the sandbox's id, so its teardown removes it; on other providers scoped_name keeps the name, so nothing changes there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Chaos and live verificationThe same battery ran on the old release (agentenv-framework 0.9.1332, from PyPI) and on this PR (7de72ab; 1bbbbc9 only rewraps a docstring).
Local provider:
Live:
CoopBench on Linux and the stock Mac:
Unchanged: #221's Clean:
smol_vm and sail_vmThese ran on an Ubuntu VM (m8i.2xlarge with nested virtualization, so smol gets
The reach task:
|
…input is built The env's endpoints rode on the target: an env field on both target classes and the protocol, None for every agent target. The rule is simpler than that, so _build_input now states it: a step with env_id gets input["env"], built from the env's record and the target's sandbox. The record's lookup moves to _deployed_env, which _host_target shares, and the protocol declares the sandbox both targets carry instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rd, as its sandbox reaches it, on any provider
input["env"] is now {"id", "url", "mcp_url"}: the env's address, where its card is, and the MCP endpoint the card
declares, for a gateway, a lone server or any other kind of env. Sandbox.url_on_host turns one of a sandbox's own
URLs into the address a process on its host reaches it at, following the host ports and addresses it publishes on,
and replaces run_code's private helper. An env in a container sandbox, which has no host to stage on, runs the
script in that container instead of failing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…120 columns Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
A
run_codescript on an env's host had no way to find the env. Scripts assumed127.0.0.1:18765, where the VM providers publish a gateway. That breaks in three cases:modal_gpu) has no VM host, so the step failed with'ModalSandbox' object has no attribute 'exec_script'.What changes:
The script's input names the env in protocol terms:
urlis the env's address. Its card is aturl + "/.well-known/agent-env.json", and every path the card declares joins ontourl.mcp_urlis the MCP endpoint the card declares.DeployedEnvrecord, so this holds for a gateway, a lone server or a plugin's env, with no check on the record's type. A record from before the card hasurlset toNone.The sandbox says how its host reaches its own URLs. New
Sandbox.url_on_host(url), besideurl_from_sandbox, which is untouched.tunnel_urls(longest match first, path kept) becomeshttp://<address>:<host_port(port)><path>. The address is loopback unless the sandbox publishes only on other addresses (host_ips). Any other URL is returned unchanged.127.0.0.1:18765, the address scripts already use. beta_scale's path-prefixed proxy tunnel, which a host can't hairpin through, maps there too. The local provider gives the mapped port.An env in a container sandbox runs the script in that container, as a container-mode
deploy_sandboxon Modal already does. The env is at loopback on its own port there. The image needspython3.Only
env_idsteps getenv. Agent anddeploy_sandboxscripts are unchanged, and scripts that don't read the key see no difference.Not done: having the local provider publish 18765 itself when it's free. With two local runs at once, one run's script would silently reach the other run's env.
Also fixed (found in chaos, and on the old release too):
/tmp/agent_env_run_code/<step>, for example the same task run twice at once. One run's reset deleted another'srunner.py, or replaced its input and result, so one script read another run'senv.sandbox.scoped_name(_RUN_DIR)/<step>. On the local provider that ends in the sandbox's id, so its teardown removes it. Elsewherescoped_namekeeps the name, so nothing changes on VM providers.Tests
url_on_host(test_url_rewrites.py):host_ipsempty, with loopback, non-loopback only, and IPv6;LocalSandboxport map.input["env"]from four kinds of record: a gateway env, a lone server, a plugin's env whose card puts MCP at/v2/mcp, and a card-less older record.execute:input.json;env;sudo,dockeror host-file writes, and gets loopback URLs.VmSandbox, since the target is chosen by class.env.tomlenv_provider_type) on the local provider. It checks thaturlis loopback at the mapped port, fetches the card aturl, and opens an MCP session atmcp_url. Both pass on macOS.Rollout
bump-version: this is on the hot path of everyrun_codestep on the worker. The chaos results are in one comment below. They cover Linux and macOS VMs plus live modal_vm, beta_scale and Modal-container runs, with gateway and server envs, old and new code.For a stored task's scripts to use this, they read
input["env"]["url"], keeping their current default as a fallback. CoopBench's stored scripts are left as they are, so a materialized copy still needs that one-line edit to run on the local provider.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no actionable new issue was found.Summary
Gives env-host scripts the env's address and MCP endpoint, rewritten for where the script runs.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A["run_code with env_id"] --> B["Load deployed env and sandbox"] B --> C{"VmSandbox?"} C -->|Yes| D["Run on host"] C -->|No| E["Run inside container"] B --> F["Rewrite env URLs through url_on_host"] F --> G["Stage input.json with args, results, and env"] D --> G E --> G G --> H["Run script and read result"]Reviews (4) · Last reviewed commit: "feat(run_code): give an env-host script ..." · Reviewed by Greptile