[chore] Add OpenCode on E2B sandbox#5052
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds E2B as a sandbox provider for the runner and wires the opencode harness to run inside E2B with parity around tool relay/MCP delivery, workspace materialization, and usage reporting.
Changes:
- Implement E2B sandbox provider creation (template/env wiring + leak backstop) and run-plan normalization (
isE2B,isRemoteSandbox, E2B network-policy refusal, E2B cwd/relay paths). - Extend remote-sandbox behavior (Daytona + E2B) for MCP layering, workspace preparation via sandbox FS APIs, and Pi usage readback via sandbox FS APIs.
- Add/extend unit tests and add design/docs + E2B template Dockerfile/README.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| services/runner/src/engines/sandbox_agent/provider.ts | Adds E2B provider wiring (e2b import, timeout backstop, create options, provider selection). |
| services/runner/src/engines/sandbox_agent/run-plan.ts | Adds E2B plan fields (isE2B, isRemoteSandbox), E2B cwd/relay derivation, and E2B network-policy refusal. |
| services/runner/src/engines/sandbox_agent/workspace.ts | Treats any remote sandbox as FS-API-backed for mkdir/write and skill uploads. |
| services/runner/src/engines/sandbox_agent/usage.ts | Generalizes Daytona usage FS readback to “remote sandbox” usage FS readback. |
| services/runner/src/engines/sandbox_agent/mcp.ts | Gates internal loopback MCP advertisement on any remote sandbox (not just Daytona). |
| services/runner/src/engines/sandbox_agent/pi-assets.ts | Treats any remote sandbox as non-local for Pi local asset prep. |
| services/runner/src/engines/sandbox_agent.ts | Threads E2B cwd factory into plan build; updates multiple “remote vs local” gates to use isRemoteSandbox; updates MCP/tool relay/usage wiring accordingly. |
| services/runner/tests/unit/session-mcp-layering.test.ts | Updates MCP layering tests to pass the new isRemote input and adds E2B-specific assertions. |
| services/runner/tests/unit/sandbox-agent-workspace.test.ts | Updates plan shape (isRemoteSandbox) and adds E2B workspace FS API coverage for opencode + skills. |
| services/runner/tests/unit/sandbox-agent-usage.test.ts | Adds E2B usage writeback readback coverage via sandbox FS API when remote. |
| services/runner/tests/unit/sandbox-agent-run-plan.test.ts | Adds opencode-on-E2B normalization and policy/secret propagation assertions. |
| services/runner/tests/unit/sandbox-agent-provider.test.ts | Adds E2B timeout/create-option tests alongside existing Daytona provider tests. |
| services/runner/tests/unit/sandbox-agent-e2b-run-plan.test.ts | New E2B-focused run-plan suite (includes opencode-on-E2B cases). |
| services/runner/tests/unit/sandbox-agent-e2b-provider.test.ts | New E2B-focused provider/create/backstop suite. |
| services/runner/package.json | Adds @e2b/code-interpreter dependency needed for the sandbox-agent E2B subpath provider. |
| services/runner/pnpm-lock.yaml | Locks @e2b/code-interpreter and its transitive deps; updates sandbox-agent optional peer snapshot. |
| services/runner/sandbox-images/e2b/e2b.Dockerfile | New E2B template Dockerfile (Node 22 + sandbox-agent + Pi tooling). |
| services/runner/sandbox-images/e2b/README.md | New documentation for building/configuring the E2B template and runner vars. |
| api/oss/src/utils/env.py | Adds E2BConfig and registers it on EnvironSettings for API env configuration. |
| docs/design/agent-workflows/projects/add-opencode-e2b/specs.md | New design/spec doc for opencode-on-E2B wiring. |
| docs/design/agent-workflows/projects/add-opencode-e2b/tasks.md | New task checklist doc for the work. |
| docs/design/agent-workflows/projects/add-opencode-e2b/research.md | New research/investigation notes for E2B + opencode wiring. |
Files not reviewed (1)
- services/runner/pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const template = process.env.E2B_TEMPLATE ?? "agenta-sandbox-agent"; | ||
| const create = buildE2BCreate(piExtEnv, secrets); | ||
| return e2b({ | ||
| template, | ||
| create: { envs: (create as any).envs } as any, | ||
| timeoutMs: (create as any).timeoutMs, | ||
| autoPause: (create as any).autoPause, | ||
| }); |
| `harness=opencode, sandbox=e2b` combination works. E2B-specific wiring is duplicated from | ||
| the sibling `chore/add-sandbox-e2b` branch; it will be deduped after both land. | ||
|
|
||
| ## Provider wiring (`services/agent/src/engines/sandbox_agent/provider.ts`) |
| return e2b({ template, create: { envs }, timeoutMs, autoPause }) | ||
| ``` | ||
|
|
||
| ## Run-plan wiring (`services/agent/src/engines/sandbox_agent/run-plan.ts`) |
| ## `services/agent/package.json` | ||
|
|
||
| Add `"@e2b/code-interpreter": "^1.0.0"` to `dependencies`. | ||
|
|
||
| ## `services/agent/sandbox-images/e2b/` | ||
|
|
||
| Reuse the `e2b.Dockerfile` from the Pi-only sibling template. The opencode binary is | ||
| auto-installed at runtime by the daemon (`install-agent opencode`), so no additional | ||
| bake step is needed. The README notes both Pi and opencode support. |
| - [x] `services/agent/sandbox-images/e2b/e2b.Dockerfile`: Node 22, sandbox-agent, pi-acp | ||
| - [x] `services/agent/sandbox-images/e2b/README.md`: note Pi + opencode (auto-installed) |
| it("opencode on E2B: acpAgent=opencode with isE2B/isRemoteSandbox true", () => { | ||
| const result = buildRunPlan( | ||
| { | ||
| harness: "opencode", | ||
| sandbox: "e2b", | ||
| messages: [{ role: "user", content: "hello" }], | ||
| secrets: { ANTHROPIC_API_KEY: "sk-ant" }, | ||
| credentialMode: "env", | ||
| } as AgentRunRequest, | ||
| { createE2BCwd: () => "/root/work/agenta-abc123" }, | ||
| ); | ||
|
|
||
| assert.equal(result.ok, true); | ||
| if (!result.ok) return; | ||
| assert.equal(result.plan.harness, "opencode"); | ||
| assert.equal(result.plan.acpAgent, "opencode"); | ||
| assert.equal(result.plan.isPi, false); | ||
| assert.equal(result.plan.isDaytona, false); | ||
| assert.equal(result.plan.isE2B, true); | ||
| assert.equal(result.plan.isRemoteSandbox, true); | ||
| assert.equal(result.plan.cwd, "/root/work/agenta-abc123"); | ||
| }); | ||
|
|
||
| it("opencode on E2B: restricted network is refused (no egress control)", () => { | ||
| const result = buildRunPlan( | ||
| { | ||
| harness: "opencode", | ||
| sandbox: "e2b", | ||
| messages: [{ role: "user", content: "hello" }], | ||
| sandboxPermission: { network: { mode: "off" }, enforcement: "strict" }, | ||
| } as AgentRunRequest, | ||
| { createE2BCwd: () => "/root/work/agenta-abc123" }, | ||
| ); | ||
|
|
||
| assert.equal(result.ok, false); | ||
| if (result.ok) return; | ||
| assert.equal(result.error, E2B_NETWORK_UNSUPPORTED_MESSAGE); | ||
| }); | ||
|
|
||
| it("opencode on E2B: ANTHROPIC_API_KEY secrets reach the plan", () => { | ||
| const result = buildRunPlan( | ||
| { | ||
| harness: "opencode", | ||
| sandbox: "e2b", | ||
| messages: [{ role: "user", content: "hi" }], | ||
| secrets: { ANTHROPIC_API_KEY: "sk-ant-e2b" }, | ||
| credentialMode: "env", | ||
| } as AgentRunRequest, | ||
| { createE2BCwd: () => "/root/work/agenta-abc123" }, | ||
| ); | ||
|
|
||
| assert.equal(result.ok, true); | ||
| if (!result.ok) return; | ||
| assert.deepEqual(result.plan.secrets, { ANTHROPIC_API_KEY: "sk-ant-e2b" }); | ||
| }); |
| ## `api/oss/src/utils/env.py` — E2bConfig | ||
|
|
||
| ```python | ||
| class E2bConfig(BaseModel): | ||
| api_key: str | None = os.getenv("E2B_API_KEY") | ||
| template: str | None = os.getenv("E2B_TEMPLATE") | ||
| model_config = ConfigDict(extra="ignore") | ||
|
|
||
| # EnvironSettings: | ||
| e2b: E2bConfig = E2bConfig() | ||
| ``` |
| DEFAULT_E2B_TIMEOUT_MS = 30 * 60 * 1000 // 30 min backstop | ||
| e2bTimeoutMs(raw?) // parse E2B_TIMEOUT_MS; clamp >= 1 ms | ||
| buildE2bCreate(piExtEnv, secrets) -> { envs, timeoutMs, autoPause: true } | ||
|
|
| E2B_NETWORK_UNSUPPORTED_MESSAGE // new named constant (parallel to LOCAL_) | ||
| RunPlan.isE2b: boolean // new field | ||
| BuildRunPlanDeps.createE2bCwd? // injectable for tests | ||
| defaultE2bCwd() // /root/work/agenta-<hex> | ||
|
|
||
| buildRunPlan(): | ||
| isE2b = sandboxId === "e2b" | ||
| if networkRestricted && isE2b -> error E2B_NETWORK_UNSUPPORTED_MESSAGE | ||
| if networkRestricted && !isDaytona && !isE2b -> error LOCAL_NETWORK_UNSUPPORTED_MESSAGE | ||
| cwd = isDaytona ? createDaytonaCwd() : isE2b ? createE2bCwd() : createLocalCwd() | ||
| plan.isE2b = isE2b |
| - [x] `provider.ts`: `import { e2b } from "sandbox-agent/e2b"` | ||
| - [x] `provider.ts`: `DEFAULT_E2B_TIMEOUT_MS`, `e2bTimeoutMs()`, `buildE2bCreate()` | ||
| - [x] `provider.ts`: `buildSandboxProvider` E2B branch | ||
| - [x] `run-plan.ts`: `E2B_NETWORK_UNSUPPORTED_MESSAGE` | ||
| - [x] `run-plan.ts`: `RunPlan.isE2b`, `BuildRunPlanDeps.createE2bCwd`, `defaultE2bCwd()` | ||
| - [x] `run-plan.ts`: E2B network gate + cwd selection + `plan.isE2b` |
Context
With OpenCode working locally and E2B established as a sandbox provider, OpenCode needed its own E2B wiring for provider options, usage reporting, and MCP relay parity with the other harnesses on E2B.
Changes
Extends
provider.ts,run-plan.ts,usage.ts, andworkspace.tsfor the OpenCode x E2B combination, carrying forward the same E2B plumbing pattern established for Pi and Codex.Tests / notes
sandbox-agent-e2b-provider.test.ts,sandbox-agent-e2b-run-plan.test.ts,sandbox-agent-provider.test.ts,sandbox-agent-run-plan.test.ts,sandbox-agent-usage.test.ts,sandbox-agent-workspace.test.ts,session-mcp-layering.test.ts.chore/add-harness-opencode, notbig-agents. Developed in parallel withchore/add-sandbox-e2b; expect the E2B plumbing here to converge with that branch's canonical version during the merge pass.chore/add-remote-tools-gateuntil the relay-client shim lands.