Skip to main content
E2B runs code in isolated cloud sandboxes that can pause and resume with their files and memory intact. A coding agent’s tools can run in a sandbox that holds a checkout of the repository, while the model loop stays in the Rebuno agent. Routing the tools through Rebuno checks each call against policy and records it as a tool_call step in the same execution as the agent’s model calls. The sandbox keeps the working files between dispatches and pauses while a call waits for approval. The design follows Coding agents on Rebuno.

Find the sandbox

Each task belongs to a session. The agent looks up the session’s sandbox by metadata, and creates one when there is none:
The handler calls it through step, which records the sandbox id:
On a re-dispatch the step replays the recorded id and connect resumes that sandbox, with the files the first attempt changed. If the sandbox no longer exists, connect fails rather than the agent continuing in an empty one. on_timeout: "pause" pauses a sandbox whose worker died instead of deleting it when its timeout expires.

Wrap the tools

The tools run inside the sandbox:
The model uses shell for git as well, committing to the session’s branch and pushing it. All three keep the default safe_to_retry. A command that runs again after a worker dies affects only the sandbox, and write_file replaces the whole file, so writing it twice leaves the same result. See idempotency.

Keep credentials out of the sandbox

Cloning and pushing need a GitHub token. Instead of placing the token in the sandbox, the agent adds a network rule that sets the Authorization header on requests to github.com at E2B’s egress proxy:
git works as usual inside the sandbox, and nothing in it can read the token. The agent sets the rule on every connect, so a sandbox resumed later gets the token the worker currently holds. Opening the pull request is a separate tool that calls GitHub’s API from the agent, outside the sandbox:
The rule covers only github.com, so code in the sandbox can push a branch but cannot reach the API to open a pull request, and the policy on open_pr holds. Pushes are not checked by policy. Protect the default branch with a GitHub ruleset that requires a pull request, so a push can only land on other branches.

Pause while waiting

When a call waits for approval, the handler unwinds with Blocked. The agent pauses the sandbox on the way out, and again when the run finishes:
The check reads execution().suspension because a framework can catch Blocked inside its own loop. While the approval is pending, no worker holds the run and the sandbox only keeps its storage. Once the approval is decided, the next dispatch replays up to the held call and connect resumes the sandbox. A dispatch that ends because another worker took over the run leaves the sandbox running for that worker.

Sessions

A session carries over between executions: the sandbox, the branch rebuno/<session>, and the conversation. The handler takes an optional session, and derives one from the execution id when it’s missing:
The derived value is the same on every dispatch of the execution. It uses the end of the id, since execution ids are UUIDv7 and their leading characters encode the creation time. The conversation is loaded at the start and saved at the end, both as steps:
A worker can die after the save and before the execution completes. On the next dispatch, a plain load would return a conversation that already contains the run, the model requests would no longer match the recorded ones, and nothing would replay. As a step, the load replays the conversation the execution started from. The handler returns the session with its answer. A follow-up execution with the same session resumes the sandbox, continues the conversation, and pushes to the same branch, which updates the open pull request.

Write the policy

Everything that runs in the sandbox is allowed, since the sandbox is what isolates it. Opening the pull request is the call that reaches other people, so it waits for approval. The local steps that find the sandbox and load the conversation are allowed without a rule.

Run it

examples/integrations/e2b has the full agent, the policy, and a dev kernel config. It stores conversations as files in sessions/ beside the agent. Set E2B_API_KEY, REPO (the repository as owner/name), GITHUB_TOKEN, LLM_MODEL, LLM_BASE_URL, and LLM_API_KEY. The token needs read and write access to the repository’s contents and pull requests. Then start the kernel and the agent from that directory:
Then create an execution:
The open_pr call appears in rebuno exec watch. See Approvals to approve it. The execution’s output includes its session. Pass it to continue the same work:
To see a re-dispatch, stop the agent after a few tool calls and start it again. The kernel dispatches the execution once its lease expires, two minutes by default. lease_timeout_seconds sets a shorter lease for the agent.