Skip to content

fix(task_steps): reach the container of a container-mode deploy_sandbox sandbox - #68

Merged
psinghal20 merged 2 commits into
mainfrom
pratyush/container-sandbox-file-routing
Oct 6, 2026
Merged

psinghal20 merged 2 commits into
mainfrom
pratyush/container-sandbox-file-routing

Conversation

@psinghal20

@psinghal20 psinghal20 commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Background

A container-mode deploy_sandbox sandbox comes in two shapes:

  • The sandbox is the container (modal). A plain exec runs inside it.
  • The container runs beside a VM host (local, modal_vm, e2b, and any provider using the base create_container). A plain exec runs on the host; only docker exec <container> reaches the container's files.

VM-backed providers reattach every sandbox as a VM (local too, since #37), so a step that looks at the reattached sandbox's mode cannot tell a container-mode deploy apart. The mode deploy_sandbox recorded, DeployedSandbox.sandbox_mode, can.

Why agents and envs already worked

load_artifact with agent_name or env_id already reaches containers:

  • Agents (A2AAgent.load_file_artifact_universe): chooses by sandbox class. A VmSandbox gets docker exec -u 0 <container_name> mkdir + write_file_from_s3 (staged on the host, docker cp'd in); a container sandbox gets exec mkdir + a direct write.
  • Envs, service data (MCPServerEnv._copy_artifact_into_container): writes into a container-mode sandbox directly, and on a VM stages on the host and docker cps into the server's container.
  • Named sandboxes (sandbox_name): none of this. load_artifact always staged onto the VM host, and collect_artifacts / verify_sandbox always read it.

Edge cases

1. Loading into a container-mode deploy_sandbox

  • Setup: deploy_sandbox(sandbox_mode="container") → load_artifact(sandbox_name).
  • Before: staged onto the VM host. On a container provider that failed (exec_script / load_s3_file exist only on a VM sandbox); on a VM-backed provider the files landed on the host, where the deployed image never sees them.
  • Now: files go into the container the way an agent's container is loaded: the staging loop moved from A2AAgent.load_file_artifact_universe into a shared stage_files_into_container, which both use. URLs go through write_file_from_url, environment payloads into the sandbox's container. VM mode is unchanged.

2. Collecting from / verifying a container-mode deploy_sandbox

  • Setup: the sandbox from case 1 → collect_artifacts(sandbox_name) / verify_sandbox(sandbox_name).
  • Before: both read the VM host: consistent with the old load, never the container.
  • Now: on a VM-backed provider both reach into the container (docker exec <container_name>), decided by the recorded mode; a container provider is still exec'd directly.

3. Reading root-owned files from a non-root image

  • Setup: an image with a non-root USER, holding files the steps wrote as root (docker exec -u 0, docker cp).
  • Before: collect_artifacts ran docker exec <container> as the image's user and could not read a root-only file (base64 read failed), so the step dropped it.
  • Now: collect_artifacts reads inside a container as root (docker exec -u 0), like the steps that write there. This applies to agent containers on a VM too; collect only reads (stat, base64, find), so it can only see more. verify_sandbox probes keep the image's user, so a verifier's bash_cmd is unchanged.

Unchanged: VM-mode sandboxes, agents (including a reattached local agent, #37), and container providers.

Tests

  • tst/unit/task_step/test_container_sandbox_routing.py (new): which container collect_artifacts / verify_sandbox reach for an agent and for a named sandbox: VM, a VM-backed container (live container, and reattached as vm with a recorded container), and a container provider.
  • tst/unit/task_step/test_load_artifact_container_sandbox.py (new): container-mode staging on a container provider (mkdir + write_file_from_s3, nothing on a host) and on a VM-backed one (docker exec + write_file_from_s3), URLs via write_file_from_url, recorded metadata.
  • tst/unit/task_step/test_collect_artifacts_vm_host.py, test_collect_artifacts.py: _exec_args reads in a container with -u 0.
  • tst/integration/task_step/test_container_sandbox_files_local.py (new; Docker + a throwaway registry:2): load_artifact → verify_sandbox → collect_artifacts on a path only the container has, for a container-mode deploy_sandbox with a non-root image user (fails at load_artifact without this change) and for a local echo agent. Cleanup removes only the containers of sandboxes in the test's own sandbox root.
  • tst/unit + protocol suite in a python:3.12 slim image: 6084 passed; the 3 failures (local_sandbox_test process-group tests, run_docker_container_test::test_a_context_zip_from_a_non_s3_store_unpacks_in_a_real_local_sandbox) fail identically on main in that image (no unzip / process tools).
  • check_plugin_api.py: no break to the plugin surface.

Not covered: real modal_vm / e2b container-mode sandboxes; they take the same VmSandbox path as local.

Follow-up: run_code on deploy_sandbox sandboxes builds on this.

🤖 Generated with Claude Code

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the earlier file-routing and root-access findings are addressed.

Summary

Named container-mode sandboxes now receive, verify, and return files from their container, including when a VM-backed provider reattaches them as a VM. Container reads also run as root so they can see root-owned files.

  • Named container sandboxes receive artifact files in their own container.
  • Checks and collects named container sandbox files inside the container.
  • Container reads can see files written as root.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["Named sandbox file step"] --> B{"Recorded sandbox mode"}
  B -- VM --> C["VM host"]
  B -- Container --> D{"VM-backed provider?"}
  D -- Yes --> E["docker exec into container"]
  D -- No --> F["Use container sandbox directly"]
Loading

Reviews (3) · Last reviewed commit: "fix(verify_sandbox): probe container fil..."

…ox sandbox

A container-mode deploy_sandbox sandbox is either the container itself (a container provider
such as modal) or a container running beside a VM host (a VM-backed provider such as local,
modal_vm or e2b), where only `docker exec <container>` reaches its files. The sandbox_name
steps treated it as a VM:

- load_artifact(sandbox_name) staged onto the VM host: on a container provider the host-only
  calls do not exist, and on a VM-backed one the files land where the container never sees them.
- collect_artifacts / verify_sandbox(sandbox_name) read the VM host.

A VM-backed provider reattaches every sandbox as a VM, so the steps decide from the mode
deploy_sandbox recorded (DeployedSandbox.sandbox_mode):

- load_artifact stages files into a container-mode sandbox the way an agent's container is
  loaded, through stage_files_into_container (moved out of A2AAgent.load_file_artifact_universe,
  which now uses it); URLs go through write_file_from_url and environment payloads into the
  sandbox's container.
- collect_artifacts and verify_sandbox reach into the container of a container-mode sandbox on a
  VM-backed provider.
- collect_artifacts reads inside a container as root, like the steps that write there: files
  they leave are root-owned, which a non-root image user cannot always read.

VM-mode sandboxes and agents are unchanged.
Comment thread src/agent_env/task_step/task_steps/load_artifact.py
Comment thread src/agent_env/task_step/task_steps/verifiers/verify_sandbox.py
Comment thread tst/integration/task_step/test_container_sandbox_files_local.py
@psinghal20
psinghal20 force-pushed the pratyush/container-sandbox-file-routing branch from 50161e8 to e106517 Compare October 6, 2026 17:20
…r test's probe dir root-only

Addresses review: a non-root image user couldn't read files load_artifact wrote as root,
so probe_file_* criteria failed. bash_cmd_succeeds still runs as the image user.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@psinghal20
psinghal20 added this pull request to stack #72 October 6, 2026 17:51
@psinghal20
psinghal20 merged commit dc4c466 into main Oct 6, 2026
13 checks passed
@psinghal20
psinghal20 deleted the pratyush/container-sandbox-file-routing branch October 6, 2026 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants