defineAdapter and can bind domain checks in assertions; several implementations share an interface and assertions through defineAdapterContract.
For the complete definition and how to run it, see Evaluate a custom application.
defineAgent and defineSandboxAgent below are dedicated to conversational integration, using send to return the standard event stream.
defineAgent
Use this for direct agent integration:
defineAgent always has Agent.kind set to "direct" (an internal discriminator you do not declare). t has no capability bits that must be declared to unlock them; filesystem assertions such as t.sandbox exist only on Agents built with defineSandboxAgent (kind: "sandbox"). Everything else is determined by the events send actually returns and how ctx.session is used; see the Capabilities reference. For the complete field set, see “Agent and Adapter Context fields” below.
Direct agent example
input (TurnInput)
Files attached to this turn (images and so on). An adapter that does not support multimodal input can ignore them.
Present only on response turns (
t.respond / t.respondAll): structured answers per request, matched by requestId.defineSandboxAgent
Use this for coding agent CLIs. The resulting Agent.kind is always "sandbox"; filesystem assertions such as t.sandbox and t.sandbox.fileChanged() are unlocked only on this kind of agent. The complete field set is also in “Agent and Adapter Context fields” below, under SandboxAgentDef. The complete methods of ctx.sandbox (the Sandbox interface) are in “Sandbox interface” further below.
Register it
Agent and Adapter Context fields
The construction parameters ofdefineAgent (DirectAgentDef) and defineSandboxAgent (SandboxAgentDef),
plus the ctx (AgentContext) that both receive in send(input, ctx):
A custom Adapter’s create(ctx) uses AdapterCreateContext, and its cleanup callback uses AdapterCleanupContext.
AdapterAssertionsFactoryContext
app
check
AdapterCleanupContext
timeoutMs
deadlineAt
signal
AdapterCreateContext
models
recordUsage
sealUsage
attach
recordTrace
evalId
experimentId
attempt
signal
model
reasoningEffort
flags
progress
diagnostic
log
onCleanup
DirectAgentDef
name
Agent.name. It is not a registry lookup key; it is used only for display, result attribution, and the dedupe fingerprint.
evidenceCoverage
completeEvidenceCoverage for complete collection.
before
tracing
spanMapper
send
classifySendFailure
Agent.classifySendFailure.
after
before point (a before throw does not exempt it),
run once in finally.
SandboxAgentDef
name
Agent.name. It is not a registry lookup key; it is used only for display, result attribution, and the dedupe fingerprint.
evidenceCoverage
completeEvidenceCoverage for complete collection.
sandbox
install
installers
agent.install. The factory normalizes an omission into an empty array.
before
agent.install. The runner calls it once after the Sandbox
is ready (after layer prepare/install/baseline) and before the first send; it returns no value.
tracing
spanMapper
send
classifySendFailure
Agent.classifySendFailure.
after
before point (a before throw does not exempt it),
run once in finally.
AgentContext
signal
fetch) to exit
gracefully early, but it is not the only hard boundary: even if the adapter ignores it entirely, the runner uses
Effect.timeoutTo as a backstop to force after (stopping the Sandbox container).
model
model field. Sandbox agents usually write it into configuration in before; direct agents usually use it in send to select the model.
reasoningEffort
flags
flags field passed through verbatim; its content and structure are entirely defined by the Experiment author
(such as { webResearch: true }, { systemPrompt: "..." }). The adapter reads its fields by its own convention;
the framework itself neither interprets nor validates the content. The name deliberately avoids the CLI-parsed
flag (run-level —timeout/—budget and so on); the two are unrelated concepts.
experimentId
runWho / AgentRun.experimentId); undefined when not running
through an Experiment (such as outside the CLI, constructing AgentRun directly). Typical uses:
a SandboxLayer command isolating cross-attempt infrastructure content per Experiment, or an adapter switching authentication / routing per Experiment.
It is a different dimension from flags (the concrete
values of Experiment conditions): this is only a stable identifier of “which Experiment is running” and carries no condition content.
evalId
evalGroup
attempt
session
telemetry
tracing block / the config’s telemetry exists):
this run’s OTLP traces receiver information (endpoint + env for env-based export).
How to hand it to the CLI is declared by the agent’s tracing block: env-based spreads ctx.telemetry.env
into send; file-based writes configuration in tracing.configure. A remote HTTP integration’s send
only needs to spread headers into the request headers (a new traceparent per turn); the endpoint is fixed by startup configuration
(defineConfig({ telemetry: { port } })) and is not passed from here.
progress
agent/ci do not print each one, and it does not enter the final result; do not call it for every token/delta.
The runner attributes it by the lifecycle phase the callback is currently in (agent.before / agent.run / agent.after);
the caller cannot impersonate another phase (see docs/feature/experiments/library/README.md).
diagnostic
dedupeKey.
Even with level “error” it does not change Turn.status / verdict; throw when you cannot continue.
log
ctx.session (AgentSession) stores the state for one session line: every send on the same session line gets the same ctx.session, and a new session line (the Eval’s first turn / after t.newSession()) gets a fresh one. Accessors:
id?: string/capture(id): void— for session continuation when the server keeps history:idis the session id this line has recorded (undefinedon a new line), andcapturerecords the returned id (it takes effect only if none has been recorded yet).createSessionKey<T>(name)— creates a typed key at Adapter module scope. Keys are isolated by symbol identity.get(key)/set(key, value)— store and read client-side history or Adapter-private state.take(key)— reads and deletes the HITL pause state, consumed once.
Sandbox interface
ctx.sandbox (Sandbox) of a Sandbox agent is the handle to the current isolated environment. Sandbox extends SandboxOperations and SandboxTransferOperations; all members are listed below by declaring interface. CommandOptions holds the options for runCommand / runShell:
SandboxOperations
workdir
cwd/targetDir also lands here.
runCommand
args are passed as separate argv entries and are not interpreted by a shell (no &&, pipes, or glob expansion).
Prefer it when you only want to run one executable, arguments come from external input, or you are worried about injection.
runShell
&&, pipes, $(), redirection, and so on.
Use it when you need to chain several commands or branch on conditions.
runCommandOrThrow
runCommand, but throws SandboxCommandExitError on a non-zero exit. The error message includes
a bounded, cleaned, and redacted stderr tail; it falls back to stdout when stderr is empty. The full output is kept in the exception’s
result field. A successful result’s exitCode is fixed to 0 in its type.
runShellOrThrow
runShell, but throws SandboxCommandExitError on a non-zero exit. The error summary,
full output, and successful result semantics are the same as runCommandOrThrow.
readText
writeText
readBytes
writeBytes
pathExists
SandboxTransferOperations
upload
uploadFile
uploadDirectory
downloadFile
downloadDirectory
Sandbox
stop
sandboxId
nativeInstanceId
otlpHost
string: the host receiver is reachable from inside the Sandbox through this hostname.null: the provider does not promise a connection back to the host; the runner tries to start an attempt-scope receiver inside the Sandbox. This does not guarantee tracing succeeds; when the image lacks the runtime the receiver needs, only a supplemental diagnostic is recorded.defineConfig({ telemetry: { host } })can override it explicitly when the author already provides a controlled tunnel.
appendLog
docker logs /
the Logs tab of the Docker UI shows the agent’s turn-by-turn activity live. Implemented by the docker provider; others can omit it.
CommandOptions
sensitiveValues
env
PATH is a Sandbox
managed variable; each provider keeps the PATH it computed and does not guarantee it can be overridden here; to extend PATH, use
the Sandbox factory’s pathPrepend (see docs/feature/sandbox/library/README.md “PATH: managed variable and
pathPrepend”).
cwd
Sandbox.workdir. Relative paths resolve against the workdir, and absolute paths are used as is.
stream
docker logs / the Logs tab of the Docker UI
shows it live). Turn it on for agent commands (codex exec / bub run / claude) to see the agent’s raw output in the container
log. Each provider implements it (docker: tee into the file PID1 tails;
unsupported providers ignore it); how the log surfaces is the provider’s business, and the adapter only declares intent.
onStdout
CommandResult. When the provider does not support real streaming, it calls it at least once with the full
stdout after the command ends; it must not drop it silently.
onStderr
onStdout; the full stderr is still kept in CommandResult.
user
USER,
the Compose service user:, the E2B template default user; see
docs/feature/sandbox/library/README.md “Execution identity”).
The semantics are consistent across providers, and each provider maps them to its own native mechanism (docker: exec --user; E2B:
{ user }; Vercel: accepts only "root", mapped to { sudo: true }, other values error).
Providers that run as root throughout treat it as a no-op; providers that cannot switch identity at all may not support it (and throw), but the semantics of omission and
explicit values stay consistent and do not vary by provider.
timeoutMs
signal
ctx and t
ctx is the run context the adapter side sees. t is the test context the Eval author sees. Both use the same run data, but have different responsibilities.