It runs without prebuilding too
Prebuilding is only for speed. The Adapter and the Sandbox each handle their own side: the Sandbox configuration decides which Provider to use and which prebuilt environment to start from; the Adapter checks whether the CLI it needs is present. When Claude Code or Codex is missing its CLI, it is installed at run time: the installation package is prepared outside the eval network, then sent into the Sandbox through the file interface.- What Codex sends into the Sandbox is a native installation package with its own runtime, so it installs even when the image has no Node.
- Claude Code requires npm in the image.
- The default Bub 0.4.0, besides the OTel plugin, also pins
any-llm-sdk==1.17.0andopenai==2.31.0. When any of the Bub version, these two dependencies, the OTel plugin, or the Python plugin list changes, it reinstalls;command -v bubalone cannot show that it is installed. A prebuilt environment made with an old installation marker reinstalls once. When you explicitly choose another Bub version, the other packages it depends on are your responsibility.
Continue building from the official baseline
All three built-in Providers can continue building from an official baseline, so you do not install the Agent from a blank environment. A prebuilt environment is built only once when dependencies change, with a new versioned name for each build. Do not put the build in a dynamic callback that runs for every Attempt.E2B: derive from official and public templates
E2B officially provides theclaude template for Claude Code and the codex template for Codex. NiceEval provides a thin E2B-specific wrapper that lets you keep building from these two official starting points with E2B’s native API. E2B has no official Bub template, so the Bub branch uses the same pinned dependencies and OTel plugin as bubAgent().
NiceEval also publishes three public templates that any E2B Team can reference. NiceEval maintains their full names and versions; use the constants directly:
2.1.207, Codex 0.144.1, and Bub 0.4.0. The template version follows the Agent inside it, in the form 0.144.1-r1: the Agent version plus NiceEval’s build revision. The three Agents release independently, unrelated to the NiceEval library’s version. So use the constants in your project, do not copy these strings, and you do not need to track versions yourself. When a derived template needs to record its base template, use the constant’s value too.
In this batch of public templates, the Claude Code template keeps E2B’s official /usr npm prefix, so ordinary users cannot write global modules directly. When you need to install more global Node tools such as pnpm or yarn at run time, install them explicitly into /usr/local, which both templates already put on PATH:
sudo npm install -g; it can conflict with root global packages already in the template. NiceEval will unify this default prefix in templates published later; until the release notes say a new template handles it, keep --prefix /usr/local.
Derive your own template from the official Codex template:
scripts/build-e2b-template.ts
e2bCodingAgentTemplate("claude-code" | "codex" | "bub") returns E2B’s native TemplateBuilder, so you can keep using E2B capabilities such as .aptInstall(), .runCmd(), and .copy(). The built alias pins the official starting point and project dependencies into one reproducible result. When dependencies change, rebuild and use a new versioned alias.
When the Bub Adapter is configured with pythonPlugins, pass the same set of packages to the factory when building the template. Only then does the run time recognize that the plugins are preinstalled:
Docker: use NiceEval-maintained images, or derive from the official node base image
To run NiceEval’s built-inclaude-code, codex, or bub Adapter directly, use the matching public image: niceeval/claude-code, niceeval/codex, or niceeval/bub.
Each image installs only its own Agent CLI and comes in both linux/amd64 and linux/arm64. Tags match the corresponding E2B public templates, and the version is the version of the Agent in the image. Stable CI should use a named constant or digest, not the changing latest:
library/* images. A new tag is published only when the Agent version or build method changes, independent of the NiceEval library’s release cadence.
When you only need one Agent, or need to add your project’s own dependencies, write a Dockerfile derived from Docker’s official node:24-slim:
Dockerfile
USER, so it runs as root by default; /usr/local/bin is already on root’s PATH, and commands installed with npm install -g work directly.
When you need a non-root user (for example, Claude Code refuses --dangerously-skip-permissions under root), add a line USER node to the Dockerfile; node:24-slim includes this user. You can also override it with dockerSandbox({ source: { type: "image", image }, user: "node" }).
When the Agent is installed elsewhere (for example ~/.local/bin), remember to add that directory to PATH. dockerSandbox must spell out the image, and stable CI references a tag that does not change.
Vercel: take a snapshot from the official runtime
Vercel has no template repository like E2B and does not use Dockerfiles. A Vercel snapshot is taken from a running microVM: use the Vercel SDK to start a Sandbox from the official runtime (node24), install the Agent CLI, call .snapshot() to get snap_..., then pass it to vercelSandbox({ snapshotId }):
scripts/build-vercel-snapshot.ts
snap_7sIjfs71xfmVly0WEUTGhTBoMGeL, but it cannot be used across accounts.
Run-time checkpoints
createCheckpoint() / restoreCheckpoint() solve a different problem: pack specified Linux paths in a Sandbox into a Buffer, then restore them into another Sandbox that has already been created:
restoreCheckpoint() in a dynamic .before(). After a successful restore, register createCheckpoint() with context.onCleanup(), so the save completes before the Sandbox is destroyed.
sandboxReuse: true keeps the same Sandbox only within one niceeval exp command. When several terminals run at the same time and all read and write the same checkpoint, declare sharedState: { key } at the top level of the Experiment. NiceEval acquires the lease for this key before Experiment setup and Sandbox creation, and releases it only after the checkpoint is saved, the Sandbox is destroyed, and Experiment teardown completes. It only guarantees that one side uses it at a time; it does not save the checkpoint, roll back on errors, or coordinate across machines.