Skip to main content
You wrote a Skill for Claude Code and want to know whether the Agent really fixes bugs more accurately with it installed. This kind of eval cannot just look at a reply: the Agent reads code, edits files, and runs commands, and the result shows up in the repository. Sandbox Agents are built for this. NiceEval first creates an isolated Sandbox, puts your project in it, then starts the Coding Agent’s CLI inside and hands it the task. The Agent can freely edit files and run commands without touching your machine. Afterwards, NiceEval collects the conversation record, file changes, and test results for the eval to score.

Built-in Sandbox Agents

These three Coding Agents work out of the box; you only need the matching API key:

claude-code

Runs the Anthropic Claude Code CLI; requires ANTHROPIC_API_KEY.

codex

Runs the OpenAI Codex CLI; requires CODEX_API_KEY.

bub

Runs the bub coding agent; requires BUB_API_KEY and BUB_API_BASE.

Run a built-in Agent

First get a built-in Agent running in local Docker. Set sandbox on the Experiment (or the eval) to tell NiceEval where to create the Sandbox:
The second command runs the same eval 10 times. After the run, view results in the terminal with pnpm exec niceeval show, or in the browser with pnpm exec niceeval view.
The Sandbox can only be written in the Experiment or the eval. There is no matching CLI flag and no project-level default Provider. When neither place specifies a Sandbox image or template, NiceEval reports an error before creating any Sandbox.
When running Coding Agents in the cloud, you can use the E2B public templates NiceEval publishes directly, saving the time of installing the CLI for every Attempt:
Claude Code uses NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE, and Bub uses NICEEVAL_BUB_E2B_TEMPLATE. The constants’ values are complete template references with pinned versions, and the version updates with the Agent version in the template, so you do not need to track it yourself. To add system packages, binaries, or model caches to a template, see Prebuilt Sandbox Environments · Continue building from the official baseline. The built-in Agents exported from niceeval/adapter are factory functions. To configure auth, proxies, MCP, or GitHub Skills, write them in the factory parameters. The model still goes in the Experiment’s model field, and the Sandbox Provider still goes in the sandbox field:

Agent environment variables

Before running, confirm these variables are set. Without them, the Agent cannot call the model in the Sandbox. These are each Agent’s official variable names. When CI stores only one set of shared credentials, pass them explicitly in the Experiment with apiKey, baseUrl. This does not change the variable names the Adapter reads by default.

What one Attempt does, in order

When debugging, first know where each step happens. An Attempt runs in this order:
Starting files and verification commands are both written in test(t). While the Agent runs, it can only see the files you have already written into the Sandbox. File assertions such as t.sandbox.fileChanged() only look at files the Agent changed during t.send(). NiceEval records the workspace state before and after each t.send(), and the changes in between are attributed to the Agent. Starting files you uploaded and verification files written after t.send() do not count, so fileChanged("src/app.ts") passes only when the Agent really changed that file.

Make file changes recordable

When a file assertion fails unexpectedly, or niceeval show @<locator> --diff shows no changes, go through the points below first. NiceEval creates a separate private Git repository in the Sandbox and records the workspace state before and after each t.send(). This requires:
  • git and a POSIX shell in the Sandbox. The default images of the built-in Providers have them; confirm it yourself for custom images. Whether the project under test is itself a Git repository does not matter: the private repository lives outside the working directory, does not touch your .git, and the Agent cannot see it.
  • Files written in the working directory. Only files under sandbox.workdir are recorded. Omit targetDir / cwd when writing files; when you need an absolute path, read sandbox.workdir instead of hard-coding /workspace. Files written outside the working directory are invisible to the Agent and cannot be recorded.
  • No second Git repository in the working directory. Put the project under test directly at the root of the working directory. When the working directory contains a submodule or another cloned repository, the Attempt reports an execution error listing the paths, because file changes in that repository would be silently missed. For a repository that really does not take part in scoring, exclude it entirely with diff.ignore.
  • Changes happen during t.send(). Files written after the last t.send() count as your verification files, not as the Agent’s changes.
.git, node_modules, __pycache__, Python virtual environments, common build outputs, and package manager caches are not recorded by default; otherwise a single npm install could produce tens of thousands of files. To adjust this, write the eval’s diff field:
Rules are written like gitignore, counted from the working directory root: entries without / match same-named items at any depth, entries with / match from the working directory root, and a trailing / means a directory. The project’s own .gitignore takes no part, so files the project ignores are still recorded. The exclusion list is fixed at the first recording, and later changes the Agent makes to .gitignore do not affect it. Before and after each t.send(), at most 100,000 paths and 256 MiB of deduplicated text are scanned. At most 10,000 changes are kept; beyond that, only a fixed leading portion is kept and marked partial (reason collection-cap-reached). Only exceeding the scan limit reports an execution error; it never pretends “no files changed” and lets a file assertion pass wrongly. When the workspace is very large, narrow the scope with diff.include or diff.ignore. Experiments, eval groups, evals, and Sandbox Agents can all add command-only .before() preparation steps. When the Agent writes its own .env, put that step in the Agent’s sandbox field with a larger changeFrequency, so fixed tools and Fixtures come first. For how to write them and the caching rules, see Sandbox · Prepare the Sandbox in order.

Custom Sandbox Agents

To evaluate a Coding Agent beyond the built-in ones, write your own with defineSandboxAgent. The Agent below runs the my-agent CLI in the Sandbox, reads back the conversation record it writes, and translates it into events:
createNpmCliInstaller downloads the npm package on the host, transfers it into the Sandbox, then installs it with npm. So the Sandbox needs Node.js and npm; only a platformPackage that bundles its own runtime does not. For the full steps, see Install the CLI for a Custom Sandbox Agent. setup, each send, and teardown each have their own feedback methods; use them to tell you where the run is and what went wrong:
  • ctx.progress({ message, current?, total? }) shows what is happening now, suited to steps such as installing the CLI, running a turn, or reading the conversation record. Do not call it for every token or every JSONL line.
  • ctx.diagnostic({ code, level, message, data?, dedupeKey? }) leaves a problem to review afterwards, such as incomplete output format, a missing conversation record, or a cleanup failure. It is shown in the terminal and saved with this Attempt’s results.
  • When it cannot continue, throw directly. NiceEval saves the failing phase, error code, error message, cause, and stack, and judges this Attempt errored.
Do not call console.log/error or write to process.stdout/stderr directly in the Adapter. They scramble the terminal’s run panel and also put CI logs out of order. When a Sandbox or Adapter error occurs during the run, the terminal gives this Attempt’s locator:
The first shows the full error, diagnostics, and completed phases. The second shows each phase’s duration, so you can see which step errored or timed out. When a script or CI needs the same information, use pnpm --silent exec niceeval query discover to find the Attempt trace request, then run it with query run. When there are many steps, once the phase tree exceeds 80 detail nodes, only failed, slow, and the first and last few items are kept, with a note on how many were omitted. To check every item, read the full trace with query. A Sandbox creation failure can happen before trace collection starts, so the error does not depend on the trace and show @<locator> can always show it.

Pass the model and flags to the CLI

The model and flags written in the Experiment appear on ctx.model and ctx.flags. The Adapter converts them into CLI arguments or a request body, and only then does the Agent really switch models or configuration.

Use a custom Agent in an Experiment

Put your Agent into an Experiment the same way as a built-in Agent:
attempts: 3 runs each eval 3 times. A Coding Agent’s approach varies a lot from run to run, so one run’s result does not say much.
Each Agent’s distinct behavior is written in its own Adapter. Do not branch on Agent names anywhere else.