Skip to content

[chore] Add OpenCode on E2B sandbox#5052

Draft
junaway wants to merge 1 commit into
chore/add-harness-opencodefrom
chore/add-opencode-e2b
Draft

[chore] Add OpenCode on E2B sandbox#5052
junaway wants to merge 1 commit into
chore/add-harness-opencodefrom
chore/add-opencode-e2b

Conversation

@junaway

@junaway junaway commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

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, and workspace.ts for the OpenCode x E2B combination, carrying forward the same E2B plumbing pattern established for Pi and Codex.

Tests / notes

  • New coverage: 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.
  • Base is chore/add-harness-opencode, not big-agents. Developed in parallel with chore/add-sandbox-e2b; expect the E2B plumbing here to converge with that branch's canonical version during the merge pass.
  • OpenCode-on-E2B runs with tools will hit the loud-refuse gate from chore/add-remote-tools-gate until the relay-client shim lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 2, 2026 21:42
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 2, 2026
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 2, 2026 9:42pm

Request Review

@dosubot dosubot Bot added the Backend label Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 883b3493-a1c6-4af7-a8be-79cbf08f4b48

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/add-opencode-e2b

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jp-agenta jp-agenta marked this pull request as draft July 2, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +158 to +165
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`)
Comment on lines +60 to +68
## `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.
Comment on lines +19 to +20
- [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)
Comment on lines +153 to +207
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" });
});
Comment on lines +48 to +58
## `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()
```
Comment on lines +14 to +17
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 }

Comment on lines +28 to +38
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
Comment on lines +5 to +10
- [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`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants