defineEval is the main entry point for authoring evals. Each eval file calls it once, passing a description and test(t), and default-exports the result.
Do not provide
id or name. NiceEval derives the eval ID from the file path.defineEval options
id
description
niceeval list and the view; purely informational — it does not affect scheduling or assertion evaluation.
tags
--tag filter and view categorization; a filtering dimension independent from ID-prefix filtering.
environment
"python-3.9-astropy-4.2"); translated into that provider’s prebuilt artifact via the Sandbox spec’s environments table.
judge
Config.judge, taking effect only for this one eval (e.g. to switch to a more expensive judge model).
reporters
Config.reporters, taking effect only for this one eval.
timeoutMs
metadata
diff
workdir). By default, .git, node_modules, build artifacts, and package-manager caches are excluded;
ignore adds further exclusions on top of the default list; include has the highest priority and explicitly adds matching paths back.
The composition rule is fixed as “default ∪ ignore, then punched through by include”; the list is frozen at the ledger’s anchor point.
setup
USER, the Compose service user:, and so on); when installing system dependencies, pass { user: "root" }
to runCommand (e.g. runCommand("apt-get", ["install", …], { user: "root" })) — semantics are consistent across providers.
The second parameter is a narrow context bound to eval.setup (ctx.progress / ctx.diagnostic,
see docs/feature/eval/README.md). setup returns no value; to pass an artifact through to teardown,
key it off the sandbox instance (concurrent attempts share the same module, so a plain module-level variable would get overwritten by other attempts).
teardown
eval.teardown → agent.teardown →
sandbox.teardown), with the Sandbox still alive at this point. Runs if and only if eval.setup’s point in time was reached —
an error thrown by setup or test does not exempt it, and not declaring setup does not affect whether it fires. An error thrown,
or exceeding the 30s cleanup budget, only records a teardown-failed diagnostic and does not change the verdict. Use it for
temporary Fixtures outside the Sandbox (a temporary repo / bucket); things inside the Sandbox are reclaimed automatically when it’s destroyed and don’t need this.
test
TestContext, drives the conversation / Sandbox operations, and asserts in place.
Test context: t
t (TestContext) is the high-level context the eval author receives. The runner assembles it according to the agent’s actual capabilities — fields like t.sandbox are only meaningful on agents constructed with defineSandboxAgent; the full rule set is in the capabilities reference. All members:
send
TurnHandle. The events are also accumulated into the default session’s cumulative event stream, for the scoped assertions below to use.
sendFile
path is relative to the project root; after reading, it is base64-encoded and handed to the adapter via TurnInput.files.
requireInputRequest
respond
TurnHandle. String form matches requests in order;
when multiple requests are paused together and you need to name which one you’re answering, use the RespondAnswer object form (see its type comment).
respondAll
optionId.
reply
sessionId
events
newSession
SessionHandle — not void. This is the key entry point for
multi-session isolation: sending and asserting on the new session are isolated from the default session and from
every other newSession() session — commonly used for scenarios like “multiple users in parallel conversations” or
“one main line plus one side branch.”
signal
model
reasoningEffort
flags
experiment.flags; an experimental condition, not a command-line switch).
progress
eval.run; it only reports, it does not assert (see docs/feature/eval/library/context.md, “Reporting long steps back to the run”).
diagnostic
level is “error” this does not automatically change the verdict — the test’s conclusion is still decided by assertions.
log
progress({ message: msg }) (debug logging); does not appear in the final result.
skip
skipped Verdict in the niceeval.verdict channel; reason cannot be empty.
check
ValueAssertion against any value, returning an AssertionHandle that can be chained with .gate() / .atLeast().
Assertion evaluation is deferred until finalize at the end of the eval; the call itself is synchronous and never throws — a failure just
records a failed assertion, it does not abort the rest of the code. Use require for “abort the eval immediately if this isn’t met.”
require
ValueAssertion against any value, evaluated immediately (when awaited); if it isn’t met, throws and aborts the rest of the eval’s
steps (the assertion is still recorded in the report, and does not affect other already-recorded assertions). The difference from check:
check only records and never throws, with assertion evaluation left to the end; require evaluates on the spot and aborts on failure — suited to
“if this prerequisite isn’t met, nothing written after it matters.” On success it returns the original value with its type preserved.
group
test('title', ...)). Purely for organization/reporting —
it does not change assertion evaluation: every assertion inside the group is still evaluated independently. Can be nested (titles joined with ›).
succeeded
parked
messageIncludes
token (across every turn on that session, not just the last one).
calledTool
match can constrain arguments / count / status.
notCalledTool
match condition).
toolOrder
usedNoTools
maxToolCalls
max.
loadedSkill
noFailedActions
event
opts.count can constrain the occurrence count.
notEvent
calledSubagent
match can constrain count / status / remoteUrl.
eventOrder
eventsSatisfy
label is required and becomes the assertion’s title.
sandbox
SandboxHandle).
usage
maxTokens
max.
maxCost
usd.
judge
t.judge.autoevals.*).
Judge assertions
t.judge.autoevals currently has three methods: factuality (whether the reply is consistent with the expected facts), closedQA (whether the reply satisfies the given question or rubric), and summarizes (whether the reply is a valid summary of the given source text source). All three directly use scorers from the autoevals (braintrust) library; { on: t.reply } specifies the text being scored.
Turn return type
t.send(...) returns a TurnHandle: convenience fields derived from the event stream, plus a full set of turn-scoped assertions.
events
toolCalls
events.
status
message
events).
data
outputEquals / outputMatches to compare against.
usage
outputEquals
data deep-equals the given value.
outputMatches
data satisfies the given schema (e.g. a zod schema).
messageIncludes
token (limited to this turn’s event stream, not across turns).
succeeded
parked
calledTool
match can further constrain arguments / count / status.
notCalledTool
match condition).
toolOrder
usedNoTools
maxToolCalls
max.
loadedSkill
noFailedActions
event
opts.count can constrain the occurrence count.
notEvent
calledSubagent
match can constrain count / status / remoteUrl.
eventOrder
eventsSatisfy
label is required and becomes the assertion’s title (the predicate itself is opaque, so the burden of explanation falls on label).
maxTokens
max.
maxCost
usd.
judge
t.judge.autoevals.*).
Dataset export
file/0000, file/0001, and so on.
When you already have a stable business key, you can instead default-export a Record<string, EvalDef>. For example, the key 15193 in swelancer.eval.ts generates swelancer/15193. A key must be a non-empty single path segment: it cannot be . or .., and cannot contain /, \, or control characters; discovery order is fixed by lexicographic order over the keys.