Built-in Sandbox Agents
claude-code
Runs the Anthropic Claude Code CLI and requires
ANTHROPIC_API_KEY.codex
Runs the OpenAI Codex CLI and requires
CODEX_API_KEY.bub
Runs the bub coding agent and follows bub’s own authentication rules.
Run a built-in Agent
Setsandbox on an Eval or Experiment to choose where NiceEval creates the isolated environment:
There is no matching CLI flag or project-wide default provider. If neither the Eval nor Experiment contributes a template-bearing layer, link planning fails before NiceEval creates a Sandbox.
NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE for Claude Code or NICEEVAL_BUB_E2B_TEMPLATE for bub. Each constant is a complete, version-pinned reference whose version follows the Agent in that template. See Sandbox Providers: Prebuilt Environments and Runtime Checkpoints to add system packages, binaries, or model caches.
Built-in Agents exported from niceeval/adapter are factories. Put authentication, proxies, MCP, or GitHub skills in the factory arguments. Keep the model in the Experiment’s model field and the provider in its sandbox field:
Agent environment variables
Workflow
test(t). The Agent can see only files you wrote into the Sandbox.
t.sandbox.fileChanged() and the other attribution assertions cover only files the Agent changed during t.send(). NiceEval records workspace state before and after each t.send(), so the changes between those points belong to the Agent. Starting files you uploaded and verification materials you write after t.send() are not included. fileChanged("src/app.ts") passes only when the Agent really changed that file.
Requirements for collecting file changes
NiceEval creates a private Git repository in the Sandbox. It records workspace state before and after eacht.send(); the difference is the Agent’s work. Collection requires all of the following:
- The Sandbox has Git and a POSIX shell. Built-in provider images include them. Verify them for a custom image. The project under test does not need to be a Git repository: NiceEval’s private repository lives outside the workdir, does not touch your
.git, and is not visible to the Agent. - Files are written in the workdir. Collection covers
sandbox.workdir. OmittargetDirorcwdwhen writing files. For an absolute path, readsandbox.workdir; do not hard-code/workspace. Files outside the workdir are neither visible to the Agent nor collected. - The workdir root has no nested Git repository. Put the checkout directly in the workdir root. A submodule or another clone there is an execution error that lists the path, because normal file changes inside it would disappear from the record. Exclude a repository that does not participate in scoring with
diff.ignore. - The change happens during
t.send(). Files written after the finalt.send()are your verification material, not Agent work.
.git, node_modules, __pycache__, Python virtual environments, common build output, and package-manager caches by default. Otherwise one npm install could produce tens of thousands of paths. Adjust that list with an Eval’s diff field:
/ matches a name at every depth; one with / starts at the workdir root; a trailing / denotes a directory. The project’s own .gitignore does not participate, so project-ignored files are still recorded. NiceEval freezes the list at the first snapshot; a later Agent edit to .gitignore cannot affect it.
One t.send() window can collect at most 10,000 paths and 64 MiB of content. Exceeding either limit is an execution error, not an empty change set that could let a file assertion pass.
Lifecycle callbacks (.setup() and .teardown()) belong to the Experiment’s sandbox specification. Use them for environment setup that changes by Experiment: install an Experiment-specific binary, warm a cache, or load and save state across Attempts. Files they write are environment state, not part of the Agent diff. See Sandbox Providers: Lifecycle for the API and rules.
Create a custom Sandbox Agent
createNpmCliInstaller prepares the npm package on the host, then sends it into the Sandbox. The Sandbox does not need Node.js or npm. The complete workflow is in Create a Custom Sandbox Agent.
setup, every send, and teardown each receive feedback methods for their own scope:
ctx.progress({ message, current?, total? })updates short-lived status. Use it for CLI installation, a Turn, or transcript reading. Do not call it for every token or JSONL frame.ctx.diagnostic({ code, level, message, data?, dedupeKey? })records protocol degradation, missing transcripts, and cleanup problems. It enters the terminal’s permanent event stream and is committed as a channel event with the Attempt.- Throw when execution cannot continue. The Runner writes the stage, code, message, cause, and stack to a diagnostic channel, then forms an
erroredVerdict in theniceeval.verdictchannel.
console.log or console.error, or write process.stdout or process.stderr, from an Adapter. That breaks up the human dashboard and CI log order.
When a Sandbox or Adapter error appears during a run, the terminal prints an Attempt identity:
show --run <runId> --page attempt-<attemptId> command to inspect the structured error, diagnostics, and completed lifecycle stages. That page reads the timing channel, including queueing, Sandbox startup, shell work in setup and teardown, Agent CLI installation and startup, each send, correlated OTel model or tool work, and cleanup. It identifies the layer where an error or timeout occurred and where earlier time went.
When the tree has more than 80 detail nodes, it retains failures, slow points, and head and tail samples, then reports how many nodes it omitted. To audit every node, choose the complete planned timing route from the same Sample’s page index. An execution route uses events as its structure and attaches timing only to uniquely correlated events when OTel is present. Sandbox creation can fail before telemetry starts, so error review does not depend on a trace.