CLI Reference
AgentEval ships a CLI for managing the .agenteval/ workspace from the terminal and CI/CD pipelines — and, via the
gatekeeper verb group, a language-neutral runtime-policy service any process (Python, Node, bash, a CI step) can
call for a versioned gate verdict + exit code.
Installation
# Recommended — install once, use anywhere
dotnet tool install --global AgentEval.Cli --prerelease
# Update later
dotnet tool update --global AgentEval.Cli --prerelease
# Or run from a cloned repo (contributor / development path)
dotnet run --project src/AgentEval.Cli -- <command>
After global install, the agenteval command is available system-wide. Requires .NET 8
SDK or later for the core surface; agenteval mc serve additionally requires .NET 10
because Mission Control depends on Hot Chocolate 16 + MapStaticAssets (net10-only). On
.NET 8/9 installations, mc serve exits with a graceful "requires .NET 10" message rather
than failing obscurely.
Examples below use the global agenteval form. To run from a cloned repo, substitute
dotnet run --project src/AgentEval.Cli -- (note the trailing --).
Environment variables
The CLI honours the following process-level environment variables.
AI_INFERENCE_PROVIDER — which provider the CLI talks to
Every bench and calibrate command reaches its model through this selector. Set it to one of
azure, bitdeer, openai, foundry or openai-compatible, then set that provider's variables:
AI_INFERENCE_PROVIDER |
Provider | Variables it needs |
|---|---|---|
azure |
Azure OpenAI | AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_API_KEY + AZURE_OPENAI_DEPLOYMENT |
bitdeer |
Bitdeer AI Model Studio | BITDEER_API_KEY (BITDEER_ENDPOINT, BITDEER_MODEL have defaults) |
openai |
OpenAI | OPENAI_API_KEY (OPENAI_BASE_URL, OPENAI_MODEL have defaults) |
foundry |
Azure AI Foundry | FOUNDRY_ENDPOINT + FOUNDRY_API_KEY + FOUNDRY_MODEL |
openai-compatible |
any OpenAI-compatible host | OPENAI_COMPATIBLE_ENDPOINT + OPENAI_COMPATIBLE_MODEL (OPENAI_COMPATIBLE_API_KEY optional; a keyless local host gets no-key-needed) |
Leaving the selector unset is supported and backward compatible. The resolver then auto-detects, in
this order: Azure OpenAI, Bitdeer, OpenAI, Foundry, OpenAI-compatible — the first one whose variables are
all present wins. A machine that has only ever set AZURE_OPENAI_* therefore behaves exactly as it did
before this variable existed.
Endpoint policy: an endpoint must be https, or http to loopback. Diagnostics name the variable
and the reason, never its value, because a configured URL can carry a token in its user-info, query or
fragment and the diagnostic reaches stderr.
Reasoning is off by default. Every chat client the CLI, the samples and the demos build from these
variables sends a request that sets no reasoning level of its own with reasoning_effort: none. When the
provider rejects that as an invalid request (HTTP 400 or 422) it sends it again at low, and when the
provider rejects both (a model with no reasoning setting, such as gpt-4o) with no reasoning field, which
is the provider's default. The level that worked is remembered per provider, endpoint and model for the rest
of the process, so a rejected level costs one failed call per model. Code that sets ChatOptions.Reasoning
keeps its own level. In your own code, chatClient.WithMinimalReasoning() (AgentEval.Core) does the same.
The AEF dataset runner (agenteval run --plan) resolves the same none → low → provider-default
sequence during readiness, with every attempt charged to its role's call and cost bounds. Cases then use the
accepted setting for that role without renegotiating it. Each transport call makes one attempt; preparation
reserves up to three accounted readiness attempts per role and suite.
Why: on Bitdeer, GLM-5.3-Flash at its default reasoning spent two thirds of its output tokens thinking and
took 2.2 times as long on an article-length answer (2026-10-21); Bitdeer rejects none and accepts low,
which turns GLM's reasoning off. Results measured before 2026-10-21 ran at each provider's default.
Provider selection was added in v0.41.0-beta. Before that the CLI assumed Azure OpenAI.
AZURE_OPENAI_JUDGE_ENDPOINT, AZURE_OPENAI_JUDGE_API_KEY, AZURE_OPENAI_JUDGE_DEPLOYMENT
An optional judge override, independent of the selector above. When all three are set they win outright, so a capable grader can face a cheap subject in a single run. The same endpoint policy applies.
AGENTEVAL_ALLOW_STUB_JUDGE (retired)
Ignored since 0.43. Through 0.42 it let the bench commands, and calibrate, run with a placeholder judge that
scored 75/100 with every criterion met, so the results and calibration figures measured no judge. There is no
stand-in judge now: with no provider configured, every command that needs a judge exits 3. To try a command
without a model, use --sut mock (see agenteval bench): it runs a built-in stand-in agent
and a placeholder judge, says MOCK, exits 11 and writes nothing to .agenteval/.
Resolution order (exit codes per Exit codes):
- Test override (programmatic; not user-visible).
- All three
AZURE_OPENAI_JUDGE_*set → that judge, whatever the selector says. - A provider resolves with credentials → the real judge for that provider.
- Any provider variable or the selector is set, but a provider could not be built → exit 3
(
RuntimeError), saying the provider is misconfigured. - Nothing configured → exit 3, listing each provider and the variables it would need.
AgentEval__Root
Workspace-root override for processes that aren't launched from inside the workspace. Read by agenteval mc serve (the Mission Control host) and any program using AgentEvalServiceCollectionExtensions.AddAgentEvalAll(). Double-underscore is ASP.NET Core's hierarchical-key separator (AgentEval:Root in appsettings.json → AgentEval__Root as an env var).
ASPNETCORE_URLS
Honoured only when launching Mission Control directly (dotnet run --project src/AgentEval.MissionControl). agenteval mc serve forcibly binds to http://127.0.0.1:<port> and overrides this variable — there is no built-in auth in Phase 1, so the CLI hard-pins to loopback. To bind a broader interface (e.g. LAN), run the portal binary directly with your own ASPNETCORE_URLS and accept the trust trade-off.
Troubleshooting: --log-file <path>
A global option — available on every command, not just the ones shown below. Writes a human-readable, plain-text log of every LLM round-trip (request + response, including tool calls, usage, and finish reason) to <path>, separate from the command's normal stdout/stderr. Also captures the full exception (type + message + stack trace) for any request that fails, not just the short one-line summary the CLI prints to stderr by default.
agenteval eval --dataset my-data.jsonl --azure --deployment-name gpt-4o-mini --log-file trace.log
agenteval gatekeeper calibrate --gate judge:crescendo-trajectory-turn-shift --azure --deployment-name gpt-4o-mini --log-file calibrate-debug.log
Covers every LLM call the CLI makes for the invoked command — the agent/SUT under test, judge, attacker (RedTeam Crescendo/PAIR/TAP), and Copilot Studio's live connector all get logged when active.
⚠️ Contains raw, unredacted content. The log file includes the full text of every prompt and response — which can carry secrets, PII, or anything else present in your data or the model's output. --log-file is opt-in specifically for troubleshooting: turning it on means you want to see exactly what was sent and received. Never commit or share the resulting file. The file is overwritten on each invocation, so a fresh run always starts clean.
An unwritable --log-file path (missing parent directory, no permissions) never fails the command — it prints one warning to stderr and the invoked command runs exactly as it would without --log-file at all. Verbose logging is a debugging aid; it must never be why an otherwise-successful run fails.
A command line that does not parse exits 2 before --log-file or --capture-fixture is opened, so neither file is created or overwritten — see Exit codes.
Fixture capture
--capture-fixture <path> is a global option, available on every command. It writes one JSON line per LLM
round-trip to <path>, for agenteval log-file to turn into a test fixture or to
replay against another model. It is separate from --log-file, and the two can be passed together:
--log-file is a log for reading, while each --capture-fixture line is a self-contained record of one
round-trip, including every message the request sent.
agenteval eval --dataset my-data.jsonl --azure --deployment-name gpt-4o-mini --capture-fixture capture.jsonl
Each line has these fields:
| Field | Content |
|---|---|
Index, Label |
The round-trip's position among those of the same client, and the client's role (agent, judge, …). |
Kind |
response; error (the call threw); or abandoned (a streamed response the caller stopped reading before it ended). |
TimestampUtc, ElapsedMs |
When the line was written, and how long the round-trip took. |
Request |
The instructions, the options (temperature, max output tokens, model id, tool definitions) and every message sent, as role and text. |
Response |
Text, tool calls, finish reason and token usage. Only on response lines. |
Error |
The full exception text, stack trace included. Only on error lines. |
Treat the file like a --log-file. Strings that match known credential formats are masked before each line
is written; everything else — prompts, responses, personal data — is written as it was sent or received. Never
commit or share the file. It is overwritten on each invocation, and an unwritable path prints one warning and the
command runs without capture.
Commands
agenteval init
Initialize a starter evaluation dataset in the current directory.
Synopsis
agenteval init [--format yaml|json] [-o <path>] [--force]
What it does
Writes a sample dataset file for the legacy eval command surface. The default output is
agenteval.yaml; pass --format json for a JSON starter, -o to choose a different file,
and --force to overwrite an existing target.
Options
| Option | Description |
|---|---|
--format <yaml|json> |
Output format. Default: yaml. |
-o, --output <path> |
Output file path. Default: agenteval.{format}. |
--force |
Overwrite an existing file. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Dataset written successfully. |
2 |
Invalid format or the target file already exists. |
agenteval init-workspace
Initialize the canonical .agenteval/ workspace for the current solution.
Synopsis
agenteval init-workspace [--name <display-name>]
What it does
Walks up from the current directory until it finds a .sln, .slnx, or .git marker and treats
that directory as the workspace root. Creates .agenteval/ if it does not exist, then writes:
solution.json— solution-level identity: a random UUID, the display name, andschemaVersion: "1.0".README.md— overview of the workspace layout..gitignore— excludes per-run artifacts and red-team outputs from source control.
If solution.json already exists, the command reports that the workspace is already initialized and
exits cleanly.
Options
| Option | Description |
|---|---|
--name <display-name> |
Display name to record in solution.json. Defaults to the directory name of the solution root. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Initialized successfully (or already initialized). |
1 |
Could not locate a solution root. |
agenteval eval
Evaluate an AI agent against a dataset.
Synopsis
agenteval eval --dataset <path> --endpoint <url> [--model <name>] [--azure --deployment-name <name>] [options]
What it does
Loads a YAML, JSON, JSONL, CSV, or TSV dataset, evaluates the agent, and exports results as JSON,
JUnit/XML, Markdown, TRX or CSV, with AEF as the run record. It supports stochastic reruns (--runs),
LLM-as-judge, named metrics (--metrics), and AEF evidence under --output-dir.
Key options
| Option | Description |
|---|---|
--dataset <path> |
Required. Input dataset file: JSON, JSONL, CSV/TSV or YAML. Field names match in any spelling (expected_output, expectedOutput, ExpectedOutput). |
--endpoint <url> / --azure / --deployment-name <name> |
Choose OpenAI-compatible or Azure OpenAI mode. |
--model <name> |
Required for non-Azure endpoints. |
--api-key <key> |
API key or environment variable fallback. |
--sut copilot-studio |
Evaluate a live Microsoft Copilot Studio agent instead of --endpoint/--azure — bring your own dataset (prompts + judge criteria); requires --copilotstudio-config/--i-understand-live-side-effects. See Copilot Studio. |
--system-prompt / --system-prompt-file |
Set the agent system prompt inline or from file. A --system-prompt-file that does not exist is ignored. |
--temperature <value> |
Sampling temperature sent with every agent call. Omitted: nothing is sent and the provider's default applies. Given: the value is sent as given, 0 included. 0 narrows sampling; it does not guarantee identical outputs. |
--max-tokens <n> |
Maximum output tokens per agent call. Omitted: nothing is sent. |
--metrics <list> |
Comma-separated named metrics to score in addition to the normal pass/fail check (e.g. llm_relevance,code_tool_success). Each is scored against the same captured response; the agent is not called again. Omitted: no named metric is scored. An unknown name fails before any network call. Accepted names: llm_relevance, llm_faithfulness, llm_context_precision, llm_context_recall, llm_answer_correctness, llm_groundedness, llm_coherence, llm_fluency, llm_bias, llm_misinformation, llm_toxicity, llm_task_completion, code_tool_success, code_tool_efficiency, code_toxicity, code_skill_disclosure_efficiency, embed_answer_similarity, embed_response_context, embed_query_context. agenteval list --type metrics prints these and also names --metrics does not accept, each marked [library only: not available via --metrics]: code_tool_selection, code_tool_arguments, code_recall_at_k, code_mrr and ConversationCompleteness. They need input --metrics has no source for (expected tools, a K; ConversationCompleteness scores a multi-turn conversation, not a single response), so construct them in code. LLM-based (llm_*) names need --judge, or on the --endpoint/--azure path fall back to the agent's own model; code-based (code_*) names need neither. Embedding names (embed_*) use the environment-selected provider and its embedding model, independently of explicit subject/judge flags; see embedding configuration and usage evidence. A requested metric that is not measured on some case (its judge call failed, its reply could not be read, the case lacked an input it needs) makes a run in which nothing failed exit 11, never 0. Ignored, with a warning, when --runs is greater than 1. |
--runs <N> |
Runs per test case. Default 1. Must be at least 1; greater than 1 is stochastic mode (below), which needs at least 3 runs. |
--success-threshold <0..1> |
Stochastic mode only: the share of a test case's runs that must pass for the test case to pass. Default 0.8. Not used, and not checked, when --runs is 1. |
--judge / --judge-model |
Separate LLM-as-judge endpoint/model. |
--format <fmt> |
Export format. Default json. In stochastic mode, one entry per test case (below). |
-o, --output <path> |
Output file for single-file formats. Default: stdout. |
--output-dir <path> |
Sealed AEF evidence destination. Cannot conflict with --no-aef or a different --aef directory. |
--save-golden <path> |
Save this run as a golden trace: each test case's verdict, output, and tool calls with their arguments, as JSON to commit beside the dataset (below). |
--golden <path> |
Compare this run with a golden trace and exit on regressions only (below). |
--fail-on-tool-change |
With --golden, also exit 1 when a test case's tool calls changed. |
--quiet |
Suppress the header, progress, summary, and the --metrics and --sut warnings. Errors are still printed. |
--aef <dir> / --no-aef |
Where the sealed AEF run goes: by default a workspace's .agenteval/aef; --aef writes it anywhere and fails the command (exit 3) if it cannot; --no-aef writes none. See Runs from eval, redteam and bench. |
--subject <kind:name> / --subject-version <v> |
The subject the AEF run names, such as agent:support-bot (default model:<model>), and its exact version. |
Stochastic mode (--runs greater than 1)
Each test case is run N times. A test case passes when the share of its runs that passed is at least
--success-threshold, and the command exits 1 when any test case fails. A run that errored or got no verdict
from the judge is not measured: when such runs could still decide whether the threshold is met (undecided), or no
run was measured, the test case has no conclusive verdict, and the command exits 11 if no test case failed. The stochastic runner needs at
least 3 runs, so --runs 2 is a usage error (exit 2), as are 0 and negative values; in this mode a
--success-threshold outside 0–1 is a usage error too. These checks run before anything is loaded or called.
A table and a pass/fail line per test case, and a closing summary, are printed to stderr. The export (--format,
-o) has one entry per test case, not one per run: it passes or fails on the pass rate, and its
score is the mean over the runs. Every format carries the runs as four metric columns: stochastic_runs,
stochastic_runs_passed, stochastic_pass_rate (0–100) and stochastic_score_sd. A failed test case's message names
its pass rate and the threshold (3 of 5 runs passed (60.0%), below the 80% threshold.). JUnit system-out and TRX
stdout also list each run with its score or error and the confidence interval for the mean. The report's name ends in
(stochastic, N runs per test), and Mode, RunsPerTest and SuccessThreshold are recorded in the JSON export's
metadata. AEF (--aef or --output-dir) retains trials and rollups separately. Through 0.43 this mode wrote no export at all. --metrics is ignored in this mode, with a warning.
Golden traces (--save-golden, --golden)
A golden trace is a saved run: for each test case, whether it passed, its score, the agent's output, and the tool calls it made in order, with their arguments (object keys sorted, so key order is not a change). Save one from a run you trust and commit it:
agenteval eval --dataset cases.yaml --endpoint <url> --model <name> --save-golden golden/cases.trace.json
Later runs compare against it:
agenteval eval --dataset cases.yaml --endpoint <url> --model <name> --golden golden/cases.trace.json
Each test case is reported on stderr as regressed (passed in the golden trace, fails now), improved, tools changed (a tool added, dropped or reordered, or called with other arguments), output changed, unchanged, added, removed or NOT MEASURED (the golden run or this one produced no verdict for it: the judge produced none, or the run errored; its placeholder fail is neither a regression nor an improvement). Test cases are matched by name. Output is compared after trimming and normalising line endings. Model output varies from run to run, so an output change is reported and does not fail the run. When either run recorded no tool data, tool calls are not compared.
With --golden the exit code follows the comparison, not the raw verdicts: 1 when a test case regressed (or, with
--fail-on-tool-change, called different tools), 11 when none did but a test case is not measured, 0 otherwise. A
test that already failed in the golden trace does not fail the build. Regressions and test cases not measured are
printed even with --quiet. Give both options to compare and then update the
file. A golden trace records one run per test case, so neither option combines with --runs above 1. A
--golden file that is missing or unreadable is a usage error, raised before any agent call. In code, the same is
GoldenTrace.FromResults(results) and GoldenTraceComparer.Compare(golden, current) in AgentEval.Snapshots.
With --sut
The target configures its own model, so --temperature, --max-tokens, --system-prompt and
--system-prompt-file are not applied. A warning names the ones given (not printed with --quiet).
Exit codes
| Code | Meaning |
|---|---|
0 |
Every test case passed. With --golden: no test case regressed (and, with --fail-on-tool-change, none changed its tool calls). |
1 |
At least one test case failed — in stochastic mode, its pass rate was below --success-threshold even counting every run that was not measured as a pass. With --golden: a test case regressed, or changed its tool calls under --fail-on-tool-change. |
2 |
Usage error: a missing --dataset, an unknown option, a value that does not parse (such as --runs abc), --runs below 1, a --runs or --success-threshold value stochastic mode does not accept, a --golden file that is missing or not a golden trace, --fail-on-tool-change without --golden, or a golden-trace option with --runs above 1. |
3 |
Runtime or configuration error — including a missing --endpoint/--azure/--model, a dataset that does not exist or is empty, an unknown --metrics name, and an unknown --format, which is reported only after the evaluation has run. |
11 |
No conclusive verdict, and no test case failed. In stochastic mode: a test case's verdict is undecided (runs that errored or got no judge verdict could still decide it) or not measured (no run was). With --metrics: a requested metric was not measured on some case (a judge call that failed, a reply that could not be read, an input the case lacked), so such a run never exits 0. With --golden: no test case regressed, but one has no verdict in the golden trace or in this run (NOT MEASURED), so the comparison cannot say whether it regressed. |
agenteval migrate
Migrate legacy AgentEval output paths to the canonical .agenteval/ layout. Dry-run by default; pass --apply to commit changes.
Synopsis
agenteval migrate [--apply] [--root <path>]
What it does
Walks the workspace looking for three legacy patterns and reports (or moves, with --apply) each to its canonical location:
- Uppercase
.AgentEval/(Windows-collapsed casing) → lowercase.agenteval/(preserves audit-chain integrity by moving in-place on the same volume). TestResults/traces/*.jsonlegacy trace dumps →subjects/<kind>/<name>/runs/<runId>/traces/agent-trace.jsonper discovered subject (file is renamed to the canonical name).- Flat
.agenteval/benchmarks/outside the per-subject hierarchy →subjects/<kind>/<name>/benchmarks/....
The dry-run output lists each move as MOVE <src> → <dest> so you can preview before committing. --apply performs the moves; --root <path> lets you target a specific workspace explicitly instead of the auto-detected one.
Options
| Option | Description |
|---|---|
--apply |
Commit the moves. Without it, the command only prints what it would do. |
--root <path> |
Workspace root path. Default: auto-detected. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Migration plan printed (dry-run) or applied (--apply). |
1 |
Could not locate a workspace root, or an I/O error occurred during a move. |
agenteval doctor
Validate the .agenteval/ workspace structure and content hashes.
Synopsis
agenteval doctor
What it does
Performs five checks in sequence:
solution.json— Verifies thatschemaVersion,id(non-empty GUID), andnameare all present and well-formed.- Subject-name consistency — For each subject folder under
subjects/agents/andsubjects/workflows/, verifies that the sanitizednamefield insidesubject.jsonmatches the folder name on disk. - Per-run content hashes — For each run with a
manifest.json, recomputes the SHA-256 hash over the run's summary, sorted scenario results, and optional trace, and compares it against the storedcontentHash. - Compliance evidence audit chain — For each
evidence.jsonundercompliance/, verifies thatsourceRun.manifestHashmatches thecontentHashrecorded in the source run'smanifest.json. - Stray output paths — Detects accidentally-created folders that shadow the canonical layout (
.AgentEval/with mixed case on case-sensitive filesystems, strayTestResults/traces/, or a flat.agenteval/benchmarks/outside the per-subject hierarchy) and reports them as errors so they can be removed or merged.
After all checks, prints a summary line:
Errors: N | Warnings: N | OK: N
Example output (clean workspace)
✔ solution.json OK
✔ Run 3f8a1b2c (subject: TravelAgent)
✔ compliance/GDPR/TravelAgent/2026-04-10_14-32-00/evidence.json
Errors: 0 | Warnings: 0 | OK: 3
Example output (issues found)
✔ solution.json OK
✖ Hash mismatch in run 3f8a1b2c (subject: TravelAgent).
Errors: 1 | Warnings: 0 | OK: 1
Exit codes
| Code | Meaning |
|---|---|
0 |
No errors found. |
1 |
Could not locate a solution root or .agenteval/ is missing. |
2 |
One or more validation errors found. |
Warnings (e.g. a subject folder with a missing subject.json) do not affect the exit code.
agenteval compare
Scenario scores and deltas are nullable. If either side did not measure a scenario, the table shows
n/m for that score and n/a for its delta. JSON includes explicit null baseline, candidate and
delta fields where absent. The library's RunComparison.MeanScoreDelta is null when no matched pair
was measured; a genuinely zero delta stays zero. Comparability checks and regression exit rules still apply.
Compare two AEF or legacy store runs, and print score deltas only when their recorded comparison facts agree.
Synopsis
agenteval compare --baseline <run-dir> --candidate <run-dir> [--strict] [--json] [--fail-on-regression]
What it does
Accepts native AEF run directories, legacy store run directories, copied legacy run folders and directories
containing legacy scenario JSON files. A legacy run is converted through the store-v1 converter
into temporary AEF evidence; its original files are unchanged. Pointing at its scenarios/ folder also works.
Both inputs are verified before comparison, and invalid AEF evidence is refused. The report includes integrity,
run status and whether evidence was imported. An import seal protects the converted evidence from import onward;
it does not establish that the original run was sealed when it executed. Signature trust is not checked here.
The report lists importer-supplied header fields. Without a legacy manifest, original execution identity,
timing and completion are unknown: the temporary import uses a new ID, an unknown subject and explicit
placeholder timestamps, with an aborted status explaining that missing evidence. A malformed manifest is
refused. Copied folders use a bounded snapshot of ordinary files; links and special files are refused.
Comparison uses final observations, excluding individual trial rows. Native composite evaluations select their
evaluation leaves; performance benchmarks select the whole benchmark score and retain component readings as
supporting evidence. A readable AEF run still needs recorded, unambiguous comparison facts to produce deltas.
The historical eval --output-dir report layout (results.jsonl, summary.json, run.json) is not a legacy
scenario-store folder.
The comparison is refused — the reasons are printed and no delta is — when:
- the two runs do not hold the same set of scenario ids, or share none;
- either run recorded no comparability facts for a shared scenario (runs written before those facts existed); or
- for a shared scenario, one of these axes differs between the runs, or only one run recorded it:
evalKey,evalVersion,effectiveBar(the pass bar applied, compared exactly),evaluationPolicyDigest(scoring configuration and measurement protocol),judge(model, deterministic or unknown instrument),judge.modelId,judge.rubricDigest, andstimulus(a hash of what the scenario was asked); - the selected scores have different recorded metric identities, kinds, directions, scales or units.
An axis that neither run recorded is not counted as a match: it is printed as a blind spot, before the
deltas. --strict refuses on it instead. When neither run used a judge, judge matches but judge.modelId is
such a blind spot, so --strict refuses two runs that were both graded without a judge.
Performance policy evidence includes normalization budgets, sampling settings, aggregation and the frozen pricing
schedule. It excludes subject identity and observed outcomes: different agents can be compared under the same
policy. Older runs without a policy digest expose that absence; --strict refuses it. A digest recorded on only
one side blocks even without strict mode. Different-policy trials cannot be rolled into one comparison instrument.
Stimulus digests in AEF require content capture to be enabled; missing or heterogeneous stimuli remain unknown.
When the runs are comparable, the report lists each shared scenario's baseline score, candidate score and delta,
then the mean delta and how many scenarios recovered (failed, then passed) or regressed (passed, then failed).
A delta too small to show at four decimal places is printed in scientific notation rather than as 0.0000. Two
findings are printed without blocking the comparison: scenarios where a run recorded no usable chance floor, so
the delta cannot be read against chance, and scenarios graded by a judge running the subject's own model.
Options
| Option | Description |
|---|---|
--baseline <path> |
Required. The baseline run directory. |
--candidate <path> |
Required. The candidate run directory. |
--strict |
Also refuse when an axis was recorded by neither run. |
--json |
Print the comparison as JSON on stdout instead of the report. deltas is left out when the comparison is refused; when it is not, recovered, regressed and regressedScenarios (the ids) are included. |
--fail-on-regression |
Exit 1 when the runs are comparable and a scenario the baseline passed fails in the candidate, and name those scenarios. For a CI step that must fail on a regression; without it, a comparable result exits 0 whatever it shows. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Comparable; the deltas are printed. |
1 |
With --fail-on-regression: comparable, and at least one scenario regressed (passed in the baseline, did not pass in the candidate: failed, errored or not run). A scenario not measured in the baseline cannot regress, so it does not set this exit code even when the candidate fails it. |
2 |
A path is missing or not a directory, holds no scenario files, or holds a file that is not a readable scenario; or a run repeats a scenario id. |
13 |
Incomparable; the reasons are printed and no delta is. |
agenteval bench
Run benchmark families against a subject (agent or workflow). The benchmark registry now includes
GDPR, EU AI Act, Agentic, OWASP, MITRE, NIST, Performance, LongMemEval, TypedMemEval, Memory,
Trace Fidelity, and AutoAudit. Results flow into .agenteval/ so Mission Control and
agenteval doctor can read them (except autoaudit, which prints its report and writes it with --out).
Synopsis
agenteval bench --list
agenteval bench <family> [family-specific options]
agenteval bench gdpr calibrate [--root <path>] [--out <path>] [--decisions]
agenteval bench eu-ai-act calibrate [--root <path>] [--out <path>] [--decisions]
agenteval bench agentic calibrate [--root <path>] [--out <path>] [--records <path>] [--limit <n>]
Families
GDPR and EU AI Act accept --runs N (at least 1, default 1). Repeated runs use majority voting,
retain each trial in AEF, and persist the voted result for reporting. Ties choose the more severe label;
a pass supported by only some requested trials becomes a warning. --sut mock still runs only its demonstration.
| Family | Purpose |
|---|---|
gdpr |
GDPR compliance benchmark. |
eu-ai-act |
EU AI Act compliance benchmark. |
agentic |
Agentic tool-use benchmark family. |
owasp |
OWASP LLM Top 10 red-team benchmark. |
mitre |
MITRE ATLAS red-team benchmark. |
nist |
NIST AI RMF-style red-team benchmark. |
perf |
Latency / throughput / cost benchmark. |
longmemeval |
Long-context memory benchmark. |
typedmemeval |
TypedMemEval v5 (AgentEval) — one vertical per run: prospective, episodic, arithmetic, working-memory or forgetting. |
memory |
Memory retention / cross-session benchmark. |
trace-fidelity |
Chat-boundary vs agent-boundary trace reconciliation. |
workflow-trace-fidelity |
Per-executor workflow ledger (tokens + finish reason) vs chat-boundary truth. |
autoaudit |
Glass Box auto-audit of the configured models (or --models a,b): one support task each, ranked on honesty, safety and cost. Without a provider it refuses; --sut mock runs the scripted showcase, labelled MOCK. |
Notes
agenteval bench --listprints the registry-backed family catalog.- AEF runs:
gdpr,eu-ai-act,agentic,owasp,mitre,nist,perf,memory,longmemeval,typedmemeval,autoaudit,trace-fidelityandworkflow-trace-fidelitywrite each run as a sealed AEF run into the workspace's.agenteval/aef(or--aef <dir>;--no-aefwrites none;--subject-versionrecords the subject's exact version). Autoaudit applies the supplied version to every auditedmodel:<name>; use separate invocations when versions differ. A real target is recorded aslive, a graded--responseor trace asreplayed. See Runs from eval, redteam and bench. Saved runs without an exact subject version cannot satisfy a checkpoint lane; native commands print a note after writing them. Eval and red-team suppress this informational note with--quiet. - Exit codes:
bench <family>andbench <regulation> calibratereturn 9 (FAIL), 10 (WARN —bench <family>only), or 11 (indeterminate) for a benchmark gate outcome, and 3 if the judge fails to configure — see Exit codes. A missing required option (--subject, and for some families--input,--vertical,--agent-trace/--chat-traceor--workflow-trace), an--evidence-detailvalue other thanreferencesorcontent, andagenteval benchwith no family and no--listreturn 2. - Compliance and agentic families support calibration helpers where available. Each calibration report names the judge's provider and model in its header.
bench gdpr calibrate --decisionsandbench eu-ai-act calibrate --decisionsgrade the golden datasets with the decision model (TypeSafe Jev) instead of the generative judge. The transport is read fromTYPESAFE_API_KEYorOPENROUTER_API_KEY(JEV_TRANSPORTforces one,JEV_MODELpins a build); with neither configured the command exits 3. The report header names the provider (TypeSafe or OpenRouter) and the model that was requested, marked as requested, because the provider may serve a different build under that name. It never contains the key or the endpoint.typedmemevaltakes--vertical <prospective|episodic|arithmetic|workingmemory|forgetting>and--subject, both required — the verticals measure different mechanisms, so there is no default. Corpora are embedded (no download, no dataset path); a real model is required, from whichever providerAI_INFERENCE_PROVIDERselects (theAZURE_OPENAI_*trio when it is unset), and there is no stub fallback. It prints the typed outcome vector with every denominator and gates on nothing: the family publishes no pass threshold, so a run exits 0 when it measured anything and 11 (GateIndeterminate) when it measured nothing, and its run summary is recorded asWARN(indeterminate) rather than PASS/FAIL. Cite results asTypedMemEval-<Vertical> v5 (AgentEval)— never summed or averaged with LongMemEval numbers.--vertical prospectiveadditionally requires the agent under test to implementITimestampedHistoryInjectableAgent; the run refuses before its first provider call otherwise.- Family-specific options and presets are documented under Benchmarks and the family pages in the TOC.
- For the Trace Fidelity and AutoAudit families, see the historical design docs under
docs/glassbox-history/(linked in the TOC under Resources). - Every
benchfamily that grades an agent needs a real target, or it refuses (exit 2).owasp/mitre/nist/perftake--from-env(the configured provider;--azure-from-env, its old name, still works),--sut copilot-studio(same flags aseval/redteam) or a generic--endpoint <url> --model <name> [--api-key <key>]OpenAI-compatible endpoint.gdpr/eu-ai-acttake--from-envor--sut copilot-studio(each scenario's prompt is sent to the live agent), or the agent's real answer with--response/--response-fileplus the--inputit answered.agenticgrades a supplied--response/--response-filewith its--input, plus--reference/--reference-file(the expected answer) and--context/--context-file(the retrieved context) for the checks that grade against them (rag-quality); without them those checks report not measured.--sut mockruns a built-in stand-in instead of an agent: the run says MOCK, exits 11 whatever it scores, and nothing is written to.agenteval/. Through 0.42 these commands quietly fell back to a stand-in and stored the result as a measurement. owasp/mitre/nistgrade judge first, asredteam --judgedoes. The judge model comes from the environment (theAZURE_OPENAI_JUDGE_*override if set, otherwise the providerAI_INFERENCE_PROVIDERselects); there is no option to pick it. With no provider configured the command exits 3;--sut mockneeds none and grades with the oracles alone. Before the scan the command makes one short call to the judge and exits 3 if it does not answer. The semantic attacks are graded by Composite Judges (several judge calls per probe); the other attacks by their per-attack oracle, which asks the judge only when it is inconclusive, and the judge may then only raise the probe to "attack succeeded". If a judge call fails during the scan, or the scan runs out of time, the run is INCOMPLETE: stored asWARNand exit 11, never a pass — unless what it did measure already fails it, which stays a fail (FAIL, exit 9). See the OWASP and MITRE pages for probe counts.
agenteval list
List the legacy command-surface catalogues used by eval / redteam.
Synopsis
agenteval list [--type metrics|attacks|exporters|datasets]
What it does
Prints the available metrics, attack types, export formats, and dataset formats. With no filter it prints all four catalogues.
The metrics catalogue marks every name that eval --metrics cannot resolve with
[library only: not available via --metrics]. Whether a name carries the marker is read from the same table
--metrics resolves against, so the listing cannot offer a name the command then refuses. Marked names need
input --metrics has no source for (expected tools, a K); construct them in code.
Options
| Option | Description |
|---|---|
--type <metrics|attacks|exporters|datasets> |
Print a single catalogue instead of all four. |
Exit codes: 0 when the catalogue is printed; 2 for an unknown --type.
agenteval redteam
Run low-level red-team scans against an agent. This is the fully parameterised scanner surface; the
bench owasp and bench mitre families wrap curated presets around it.
Synopsis
agenteval redteam [--azure] [--endpoint <url>] [--model <name>] [--deployment-name <name>] [--attacks <list>] [--format <fmt>] ...
Key options
| Option | Description |
|---|---|
--azure / --endpoint / --deployment-name |
Azure OpenAI mode. |
--endpoint / --model |
OpenAI-compatible mode (OpenAI, Ollama, Groq, vLLM, LM Studio, etc.). |
--sut |
Built-in target instead of an endpoint: gatekeeper-demo (the Gatekeeper demo: the configured model behind the gate, or a labelled scripted model when no provider is configured) or copilot-studio (a live Microsoft Copilot Studio agent). |
--scripted |
With --sut gatekeeper-demo: run the scripted model even when a provider is configured. Deterministic and free; use it for stable CI baselines. A baseline taken on one model is refused against a run on another (exit 3). See Attack the gate. With --attacks memory-poisoning: run a scripted worst-case model instead of --endpoint/--model; it saves what it is told and acts on recalled poison (it shows the gates, not a model). |
--attacks |
Comma-separated attack list; --pack imports external benchmark packs. memory-poisoning runs on its own: the memory-security corpus against the model you name, behind AgentEval's memory protection (see Memory poisoning); it takes --memory-trials <n>, --format markdown\|json, --fail-on vuln\|never and --timeout-per-probe (each model call), refuses the probe-scan options (--sut, --intensity, --system-prompt, --pack, …) rather than ignoring them, and exits 0 pass, 1 a security check failed, 11 one could not be measured and none failed. |
--transform |
Also run every single-turn probe encoded: codecs (base64, rot13, hex, …) or a group (reversible, lossy, all). The plaintext probes still run; each codec multiplies the probe count. See Transform pipeline. |
--judge / --attacker |
Separate judge/attacker models for LLM-as-judge and attacker-LLM flows. |
--format / -o |
Export format and output destination. |
--baseline, --save-baseline, --fail-on |
Regression gating for CI. |
--calibration |
Relative scoring against a reference cohort. |
--explain |
Attach an LLM rationale to each finding (requires --judge). |
--benign-controls |
Also run benign look-alike requests and report over-refusal beside the attack success rate, graded by the over-refusal judge (needs --judge; does not change the verdict). |
--aef <dir> / --no-aef |
Where the sealed AEF run goes: by default a workspace's .agenteval/aef; --aef writes it anywhere and fails the command (exit 3) if it cannot; --no-aef writes none. See Runs from eval, redteam and bench. |
--subject <kind:name> / --subject-version <v> |
The subject the AEF run names, such as agent:support-bot (default model:<model>), and its exact version. |
For the full flag matrix and examples, see Red Team Security. Exit codes: see Exit codes.
agenteval gatekeeper
Run Gatekeeper runtime-enforcement gates from the terminal or any language — the same policy you red-team with,
exposed as a language-neutral policy service. A process pipes a JSON payload to gatekeeper inspect and gets a
versioned verdict JSON + an exit code; the deterministic gates need no credentials and are byte-stable, so they
drop straight into CI.
Synopsis
agenteval gatekeeper list-gates [--json] [--phase inspect|serve|all]
agenteval gatekeeper inspect --gate <id> [--input <file.jsonl>] [--policy block|warn] [gate flags] [model flags]
agenteval gatekeeper calibrate --gate judge:<axis> <model flags> [--certify]
Subcommands
| Subcommand | Purpose |
|---|---|
list-gates |
List every gate, its state class, whether it needs a model, and its span policy. |
inspect |
Evaluate one JSON payload (stdin) or one per line (--input JSONL) against a gate/panel; emits a versioned verdict + exit code. |
calibrate |
Score a judge:<axis> against its gold set + keyword-oracle baseline; --certify writes the certificate the honesty guard reads. |
serve |
Reserved for stateful accumulator gates — not implemented. |
judge:* gates read/write a per-model calibration certificate under .agenteval/gatekeeper/certs/ (override with
--cert-dir); the deterministic and tool gates are credential-free and CI-safe. For the full flag matrix, the verdict
JSON contract, and the honesty guard, see Gatekeeper from any language.
agenteval skills scan
Static, offline compliance scan of a directory of MAF Agent Skills — no model call, no credentials. Reaches the
same SkillComplianceValidator/MafSkillScanner library code the Agent Skills evaluation
suite ships, from the CLI.
Synopsis
agenteval skills scan <path> [--format console|markdown|json] [-o|--output <file>] [--fail-on-noncompliant]
[--write-baseline] [--baseline-root <dir>] [--repo] [--check-baseline]
[--save-manifest-baseline <file>] [--manifest-baseline <file>] [--baseline-note <text>]
What it does
Walks <path> for SKILL.md-rooted skill folders (the same convention MAF's own AgentFileSkillsSource
discovers by), checks each against the GA SKILL.md authoring rules (name/description/compatibility) plus
governance flags (script-execution review, untrusted resource sources, experimental allowed-tools), and
renders a report. v1 is compliance-only — the composite Skill Health & Security Index is library-only for now
(see Agent Skills). Three additional governance signals ride along in the same report:
cross-location content drift (--repo/scan-workspace only, always on), trust-on-first-use reputation
matching (--check-baseline, opt-in), and manifest hash-pin drift against an explicit trust-time pin
(--manifest-baseline, opt-in) — see Agent Skills for how each works.
When a skills-lock.json (the project lock file skill installers such as ChilliCream's skills CLI write) is in
<path> or a parent of it up to the repository root, each finding names where its skill came from
(→ from chillicream/agent-skills@a1b2c3d, in every format), and --write-baseline stores the source and ref on
the snapshot. Nothing is fetched: it is a pointer for a person to check upstream, and it never changes a finding
or the exit code.
Options
| Option | Description |
|---|---|
<path> |
Required, positional. Directory containing one or more skill folders (a REPO ROOT when --repo is set). |
--format <fmt> |
console (default), markdown, or json. |
-o, --output <path> |
Write the rendered report to a file instead of stdout. |
--fail-on-noncompliant |
Exit 1 when the scan finds a High-severity finding. Default off (informational-only). Cross-location drift (Medium) and previously-vetted matches (Low) never trigger this; a manifest-baseline drift finding (High) DOES. |
--write-baseline |
Capture a timestamped baseline snapshot (structural fingerprint + full file-content hash per skill) into the baseline ledger. See agenteval skills baseline below. |
--baseline-root <dir> |
Baseline ledger root directory. Default .agenteval/skills-baselines. Used by both --write-baseline and --check-baseline. |
--repo |
Treat <path> as a repo root: scan every known skill-directory convention found under it (.claude/skills, .agents/skills, .windsurf/skills, ...) and aggregate the results into ONE combined report, instead of treating <path> itself as one skill directory. Per-convention breakdown (found/not-present, skill/finding counts) prints to stderr. When two or more conventions define the same skill name with DIFFERENT content, a CrossLocationContentDrift finding is added automatically. |
--check-baseline |
Trust-on-first-use: compare each scanned skill's content hash against the baseline ledger's history. A match against any prior snapshot for the same name adds an informational MatchesPreviouslyVettedCopy finding. Meaningless on a first-ever scan (no history yet) — pair with --write-baseline on earlier runs. |
--save-manifest-baseline <file> |
Capture a trust-time hash-pin of every scanned skill's manifest content (name, description, resource/script inventory, allowed-tools, compatibility) to this JSON file. A SINGLE pinned file, distinct from --write-baseline's multi-snapshot ledger — mirrors the RedTeam baseline/diff CI pattern: commit this file, then re-check future scans against it. |
--manifest-baseline <file> |
Check every scanned skill against a --save-manifest-baseline file. A skill whose manifest content changed since the pin was captured is reported as a High-severity ManifestChangedSinceBaseline finding — a possible rug-pull. |
--baseline-note <text> |
Optional human note saved alongside --save-manifest-baseline (e.g. who approved it, why). |
Exit codes
| Code | Meaning |
|---|---|
0 |
Scan completed (compliant, or --fail-on-noncompliant not set). |
1 |
--fail-on-noncompliant was set and a High-severity finding was found. |
3 |
Runtime error (e.g. <path> does not exist). |
agenteval skills scan-workspace
Agent Skills Wave 3a — filesystem-only, credential-free scan across a folder of already-cloned repos.
Every immediate (non-hidden) subdirectory of <path> is treated as one repo and scanned with the same
pipeline scan --repo uses, then combined into one report. No GitHub/GitLab API, no token — the operator's own
clone step is what already decides which repos are visible; that access-control question is deliberately kept
outside this verb's trust boundary (contrast with the still-gated, API-driven scan-org, Wave 3b).
Synopsis
agenteval skills scan-workspace <path> [--format console|markdown|json] [-o|--output <file>] [--fail-on-noncompliant]
[--write-baseline] [--baseline-root <dir>] [--check-baseline]
[--save-manifest-baseline <file>] [--manifest-baseline <file>] [--baseline-note <text>]
What it does
Fans out scan --repo's existing per-repo pipeline over every immediate subdirectory of <path>, combining
findings/coverage into one report (per-repo skill/finding counts print to stderr). Every entry's location is
tagged {repoFolder}/{conventionPath}, so cross-location content drift — the same detector scan --repo
already uses, unchanged — now also fires across repos, not just across conventions within one repo: the
same skill name with different content in two different cloned repos is exactly the drift/poisoning signal
this is for.
gh repo clone myorg/service-a ~/audit/service-a
gh repo clone myorg/service-b ~/audit/service-b
agenteval skills scan-workspace ~/audit --write-baseline --format json -o report.json
Options
| Option | Description |
|---|---|
<path> |
Required, positional. A folder whose immediate subdirectories are repo roots. |
--format <fmt> |
console (default), markdown, or json. |
-o, --output <path> |
Write the rendered report to a file instead of stdout. |
--fail-on-noncompliant |
Exit 1 when the scan finds a High-severity finding. Cross-location drift (Medium) and previously-vetted matches (Low) never trigger this — only High-severity findings do. |
--write-baseline |
Capture a timestamped baseline snapshot across all scanned repos into the baseline ledger. |
--baseline-root <dir> |
Baseline ledger root directory. Default .agenteval/skills-baselines-workspace — deliberately DIFFERENT from scan's .agenteval/skills-baselines default, so a plain scan --write-baseline can't accidentally get diffed against a much larger workspace-scale snapshot. Pass the same root to both verbs explicitly if you want one shared ledger. |
--check-baseline |
Trust-on-first-use across the whole workspace — see scan's --check-baseline above. |
--save-manifest-baseline <file> / --manifest-baseline <file> / --baseline-note <text> |
Same single-pin manifest hash-drift gate as scan — see scan's own rows above. A skill name duplicated across repos is deduplicated the same way --repo already tolerates it. |
Known limitation: skills baseline diff/history track only one location per skill name even when a
single snapshot legitimately has several (a pre-existing Wave 2 shortcut) — at workspace scale, where the
same name across many repos is the expected case, this means the persisted ledger's diff/history can miss
drift in every repo except whichever sorts first. The live scan-time CrossLocationContentDrift finding does
NOT have this limitation. See Agent Skills for the full explanation.
Exit codes: same as scan above.
agenteval skills baseline
list, diff and history inspect the multi-snapshot skill baseline ledger that
agenteval skills scan --write-baseline writes to. Each snapshot is a full point-in-time capture (structural
fingerprint + file-content hash per skill, plus that skill's compliance findings at the time) — never
overwritten, so the ledger accumulates history across scans. approve works on a different file: the
single-file manifest baseline that Gatekeeper's skill check reads.
Synopsis
agenteval skills baseline list [--baseline-root <dir>]
agenteval skills baseline diff [--baseline-root <dir>] [--since <id>] [--skill <name>] [--hash structural|content]
agenteval skills baseline history <skill-name> [--baseline-root <dir>]
agenteval skills baseline approve <skill-name> --skill-path <dir> --baseline <file> [--note <text>]
list — every captured snapshot (Id, capture time, scanned root, skill count), oldest listed first.
diff — compares two snapshots (default: the two most recent; pass --since <id> to diff a specific
snapshot against the latest) using the same ManifestDriftDetector primitive PromptTemplateDriftGate/
McpToolDescriptionPoisoningGate use, over either the structural fingerprint or the full content hash
(--hash, default content — the stronger signal). A Changed skill is flagged CHANGED + NEW HIGH FINDING
when the change also introduced a new High-severity compliance finding (vs. changed, no new High finding
for a cosmetic-only edit) — this is the "don't cry wolf on every cosmetic edit" guard.
history <skill-name> — walks the ledger chronologically and reports every point where that skill's
content hash changed, with any High-severity findings present at each change point.
approve <skill-name> — re-pins one skill in a manifest baseline file: the SkillManifestBaseline JSON that
skills scan --save-manifest-baseline writes and GatekeeperOptions.SkillBaselinePath points at. Use it after
reviewing a change to that skill, for example when Gatekeeper's skill check has raised a SkillDriftException.
It scans --skill-path offline (no model call), finds the skill whose manifest name equals <skill-name>
exactly (case-sensitive), and replaces that skill's structural fingerprint — and its content hash, when the
skill has a folder on disk. Every other skill's pinned entries are left as they were. The file's capture time
is set to now; --note replaces the stored note, and without it the existing note is kept. A missing file or
parent directory is created.
Options
| Option | Description |
|---|---|
--baseline-root <dir> (list, diff, history) |
Baseline ledger root directory. Default .agenteval/skills-baselines (must match what scan --write-baseline used). |
--since <id> (diff only) |
Diff this snapshot's Id against the most recent snapshot, instead of the two most recent. |
--skill <name> (diff only) |
Only show the diff for this skill. |
--hash structural\|content (diff only) |
Which hash to diff. Default content. |
--skill-path <dir> (approve only) |
Required. The skill's directory, or a parent directory holding many skills. |
--baseline <file> (approve only) |
Required. The manifest baseline file to update. |
--note <text> (approve only) |
A note stored on the baseline — who approved it, and why. |
Exit codes: 0 on success (including "nothing to diff yet" — informational, not an error); 3 on a runtime error (e.g. --since <id> not found in the ledger, or for approve, no skill named <skill-name> under --skill-path).
agenteval compliance render
Re-render a PDF report from existing compliance evidence — no LLM cost (the evidence is already on disk).
Synopsis
agenteval compliance render --regulation <reg> --subject <name> [--ts <timestamp>] [--root <path>]
| Option | Description |
|---|---|
--regulation <reg> |
Required. Regulation identifier: gdpr or eu-ai-act. |
--subject <name> |
Required. Subject name to render evidence for. |
--ts <timestamp> |
Recorded report timestamp (yyyy-MM-dd_HH-mm-ss). Defaults to most recent; an explicit timestamp can select an older captured report. |
--root <path> |
Workspace root. Default: auto-detected. |
A missing --regulation or --subject exits 2.
The reader verifies native AEF runs under .agenteval/aef/runs and reads captured rich compliance artifacts from those verified bytes. Legacy timestamp directories pass through the shared StoreV1 converter. Imported reports retain their exact bytes and are labeled imported: not sealed when it ran; an ingest seal establishes custody from conversion onward. A standalone report does not establish its original execution's timing, completion or provenance.
Missing captured content, malformed reports, conflicting identities and invalid native evidence exit 1. A legacy sibling cannot recover content omitted by the native capture policy. Selecting the latest report does not silently substitute an older captured report when a newer native report is unavailable. The bounded native scan verifies candidates before subject filtering, so an invalid or incompletely scanned native source must be resolved before rendering.
PDFs are written to .agenteval/compliance/{regulation}/{subject}/{timestamp}/report.pdf, outside the AEF source folder. Rendering leaves the source evidence and its seal unchanged. The printed integrity outcome and signature-trust status are separate; this command does not apply a signer trust policy.
agenteval render
Re-render Markdown and PDF from an original agentic report in verified AEF — no LLM cost.
Synopsis
agenteval render --benchmark <kind> --subject <name> [--ts <timestamp>] [--root <path>]
| Option | Description |
|---|---|
--benchmark <kind> |
Required. Benchmark type (currently: agentic). |
--subject <name> |
Required. Subject name to render results for. |
--ts <timestamp> |
Timestamp directory. Defaults to most recent. |
--root <path> |
Workspace root. Default: auto-detected. |
A missing --benchmark or --subject exits 2.
Native runs need --aef-content on when the benchmark runs to retain the original report for later rendering.
Capture off keeps the report identity but omits its document. Rendering returns 1 when captured content is
unavailable or evidence is invalid or ambiguous; it does not fall back to a dual-written legacy report.
Existing legacy report folders are converted on read, with ingest custody labelled separately from original
execution provenance. Reports still go to .agenteval/benchmarks/agentic/<subject>/<timestamp>/.
agenteval log-file
Work with a file written by --capture-fixture.
Synopsis
agenteval log-file to-fixture <captured.jsonl> --out <fixture.json>
agenteval log-file replay <captured.jsonl> --out <report.md> (--from-env | --endpoint <url> --model <name> [--api-key <key>]) [--strict-text]
agenteval log-file gate-replay <captured.jsonl> --baseline <gates.json> --candidate <gates.json> [--json]
to-fixture writes a JSON array of scripted turns that ScriptedChatClient.FromFixture loads, so a test
can play back real model responses with no model behind it. A fixture scripts what the model answered, not what
it was asked, so only responses are kept: each response line becomes a turn (text, tool calls, finish reason,
token counts); each error line becomes a turn that throws the first line of the captured exception; and
abandoned lines are skipped, with a note on stderr saying how many.
replay reads the capture itself — not a to-fixture output, which no longer holds the requests — and
resends the request of each response line to another target, one at a time and independently: the same
messages, instructions, temperature, max output tokens, model id and tool definitions. Tools are declared to
the target but never run. Each round-trip is compared on structure, not wording:
| Verdict | When |
|---|---|
| Fail | The target threw; the tool calls differ (tool names and argument names, with the number of calls); the finish reason differs; or, with --strict-text, the text differs. |
| Flag | Tool calls and finish reason match, but the response shape differs — a length bucket (under 100, up to 500, or over 500 characters) plus whether the text has a list or a code block. |
| Pass | Tool calls, finish reason and response shape all match. |
Token usage and latency are shown for information and never change a verdict. error and abandoned lines
are not replayed; the report counts them as skipped. The Markdown report is written to --out and also printed
to stdout.
gate-replay answers "what would this gate change have done to real traffic?" without a model or a network
call. It takes every tool call in the capture's response lines and runs two Gatekeeper tool-gate configurations
over each one with the real gates (GateReplayer): --baseline, the gates in force, and --candidate, the
proposed ones. It prints both verdicts for every call, marks the calls whose verdict differs, and counts the calls
the candidate newly blocks and newly lets through (--json gives the same as JSON). A configuration is a JSON
array of gates, named by the ids agenteval gatekeeper list-gates prints; [] is no gate:
[
{ "gate": "tool:forbidden-tool", "forbidden": ["send_email", "delete_db"] },
{ "gate": "tool:argument-pattern", "pattern": "rm\\s+-rf" },
{ "gate": "tool:domain-allowlist", "allowedDomains": ["docs.example.com"] }
]
Only these three gates, which read a call's own arguments, are replayed. A capture keeps the text of earlier
messages but not tool results, so a gate that reads the conversation (tool:referential-integrity,
tool:taint-tracking) would not decide here what it decided live; it is refused with that reason rather than
replayed on half a history. Each gate is replayed with its default settings.
--capture-fixture masks credential shapes (keys, tokens) in what it writes, so a call whose arguments were masked
does not carry what the model sent. When either configuration has an argument gate (tool:argument-pattern,
tool:domain-allowlist), such a call is marked "not measured" ("measured": false in --json) and left out of the
counts: its verdict would be about the mask.
Options
| Option | Description |
|---|---|
<captured> |
Required, positional. A file written by --capture-fixture. |
--out <path> |
Required. to-fixture: the fixture JSON array. replay: the Markdown report. A missing parent directory is created. |
--from-env (replay) |
Replay against the provider AI_INFERENCE_PROVIDER selects (see Environment variables). --azure-from-env is its old name and still works. Checked before --endpoint. |
--endpoint <url> / --model <name> / --api-key <key> (replay) |
Replay against an OpenAI-compatible endpoint. --model is required with --endpoint. Without --api-key, OPENAI_API_KEY is used, and without that a placeholder key for keyless local servers. |
--strict-text (replay) |
Also fail a round-trip whose text is not identical. Off by default: model output is not reproducible, even against the same model with the same settings. |
--baseline <gates.json> / --candidate <gates.json> (gate-replay) |
Required. The two gate configurations to compare. |
--json (gate-replay) |
Print the comparison as JSON on stdout instead of the report. |
Exit codes
| Code | Meaning |
|---|---|
0 |
to-fixture: the fixture was written. replay: no round-trip failed (flags do not fail the run). gate-replay: the replay ran, whatever it found. |
1 |
replay: at least one round-trip failed. |
2 |
replay: no target was given, --endpoint was given without --model, or --from-env found no configured provider. gate-replay: a configuration file is missing, is not a JSON array of gates, or names a gate it cannot replay; or the capture holds no tool call. |
3 |
The capture file does not exist, or another error occurred (for example, a line that is not a capture record). |
agenteval mc serve
Start the Mission Control web portal — GraphQL, REST, and SPA on one port — from any working directory. Requires .NET 10. See Mission Control Getting Started.
Synopsis
agenteval mc serve [--port <N>] [--workspace <path>]
| Option | Env var | Default | Description |
|---|---|---|---|
--port <N> |
(none — see note) | 5000 |
Bind a different HTTP port. mc serve forcibly binds to http://127.0.0.1:<port> and ignores any pre-set ASPNETCORE_URLS (see Environment variables). |
--workspace <path> |
AgentEval__Root |
current directory | Workspace root. Mission Control reads {workspace}/.agenteval/. |
The CLI spawns AgentEval.MissionControl(.exe|.dll) co-located in the same publish directory. The subprocess inherits its working directory from the CLI's bin folder so the SPA's static-asset pipeline resolves correctly; the workspace is plumbed through the AgentEval__Root env var.
Exit codes
| Code | Meaning |
|---|---|
0 |
Stopped cleanly (Ctrl+C). |
1 |
Port unavailable, MC assembly missing, or subprocess failed to start. |
2 |
Running on net8/net9 — Mission Control requires .NET 10. |
agenteval mc doctor
Verify Mission Control's runtime artefacts are co-located with the CLI and the SPA bundle is intact. Useful diagnostic before mc serve fails with a less-informative error. Sibling to agenteval doctor (which validates workspace data, not portal binaries). Requires .NET 10.
Synopsis
agenteval mc doctor
What it checks
AgentEval.MissionControl.dll(and.exeon Windows) is present alongside the CLI.wwwroot/exists withindex.htmland a populatedassets/folder (JS + CSS bundles).- The Web SDK's static-asset manifest (
*.staticwebassets.endpoints.jsonor*.runtime.json) is present. - On non-Windows,
dotnetis on PATH (the CLI spawns the MC.dllviadotnet).
Prints Errors: N | Warnings: N | OK: N and exits 2 on any error.
agenteval assert-ai
Work with Microsoft's ASSERT (assert-ai 0.3.0 formats). The guide is
ASSERT Interoperability.
Synopsis
agenteval assert-ai serve (--from-env [--model <m>] | --endpoint <url> --model <m> [--api-key <key>]) [--system-prompt <text>] [--port 8765] [--path /assert] [--host localhost]
agenteval assert-ai import <run-dir> [--format text|json|markdown] [-o <file>] [--taxonomy <file>] [--test-set <file>] [--calibration <file>] [--max-harm-rate <0-1>] [--max-over-refusal-rate <0-1>] [--max-unmeasured <0-1>]
agenteval assert-ai export --golden <file.jsonl>... --taxonomy <taxonomy.json> --judge-model <model> --out <dir> [--evaluator <key>...] [--suite agenteval] [--run judge-1] [--assert-root <path>]
agenteval assert-ai calibrate <run-dir> --cases <agenteval-cases.json> [--format text|json] [-o <calibration.json>]
serve answers ASSERT's endpoint requests ({message, history} → {response, events}) with the model your
provider variables select, or an OpenAI-compatible endpoint, until Ctrl+C. Point ASSERT's
pipeline.inference.target.endpoint at the URL it prints; keep the host name localhost (ASSERT refuses a literal
127.0.0.1 unless ASSERT_ALLOW_PRIVATE_ENDPOINTS=1). --host + answers every host name, for ASSERT in a container
calling host.docker.internal; on Windows that needs a URL reservation (netsh http add urlacl url=http://+:8765/ user=Everyone, as administrator). It serves a bare model; to serve an agent with tools, use AssertAiTarget from
code.
import reads an ASSERT run directory (the one holding scores.jsonl) and prints ASSERT's harm and over-refusal
rates per kind of test case, computed as ASSERT computes them, every case's verdict, and the cases with no score row.
A failed judge is an error and a case with no row is named, never counted as a pass. Exit 1 when a rate is above
--max-harm-rate or --max-over-refusal-rate; 11 when a gate is set and it cannot pass: more cases unmeasured than
--max-unmeasured allows (default none), the run's manifest says it did not complete, or no rate could be measured;
2 for a directory or file that cannot be read.
export writes labelled golden cases (the agentic calibration JSONL format) as an ASSERT judge-only run, with the
config to run it and agenteval-cases.json mapping ASSERT's positional ids back to the case ids. --taxonomy is
required: ASSERT's judge cannot run without one.
calibrate compares ASSERT's verdicts on exported cases with their labels: accuracy with a 95% Wilson interval,
Cohen's κ, dangerous errors (a labelled failure not flagged, including when the judge found no category relevant) and
false alarms; a case the judge did not decide is counted as not measured. It refuses a run whose transcripts are not
the ones exported with the case map. -o writes the result for import --calibration, which attaches it only to runs
of the same judge on the same taxonomy. Exit 11 when the judge decided no case.
agenteval aef
Work with AEF 1.0 runs, the AgentEval Evidence Format (a release candidate). The guide is AEF Evidence Format.
Synopsis
agenteval aef verify <run-dir> [--policy <trust-policy.json>] [--anchors <run-hashes.json>] [--json]
agenteval aef seal <run-dir> [--key <pkcs8.pem>] [--sealed-by producer|ingest] [--json]
agenteval aef view <run-dir> [--at <time>] [--policy <trust-policy.json>] [--json]
agenteval aef checkpoint <manifest.json> --runs <dir> [--policy <trust-policy.json>] [--at <time>] [--envelope <file>] [--json]
agenteval aef export <store-dir> <out-dir> [--run <run-id>] [--target-mode live|replayed|scripted|mocked] [--content-capture on|off] [--no-seal] [--key <pkcs8.pem>] [--json]
agenteval aef import store-v1 <store-dir> <out-dir> [--run <run-id>] [--target-mode live|replayed|scripted|mocked] [--content-capture on|off] [--no-seal] [--key <pkcs8.pem>] [--json]
agenteval aef import assert-ai <assert-run-dir> <out-dir> [--taxonomy <file>] [--test-set <file>] [--calibration <file>] [--max-harm-rate <0-1>] [--max-over-refusal-rate <0-1>] [--content-capture on|off] [--no-seal] [--key <pkcs8.pem>] [--json]
agenteval aef import otel <logs-file> <out-dir> --run-id <id> --from <tool> --subject <kind:name> [--subject-kind agent] --target-mode live|replayed|scripted|mocked [--content-capture on|off] [--no-seal] [--key <pkcs8.pem>] [--json]
agenteval aef export-otel <run-dir> <out-file> [--policy <trust-policy.json>] [--json]
agenteval aef import inspect <log-file> <out-dir> --target-mode live|replayed|scripted|mocked [--content-capture on|off] [--no-seal] [--key <pkcs8.pem>] [--json]
agenteval aef export-inspect <run-dir> <out-file> [--ignore-overlays] [--policy <trust-policy.json>] [--json]
import store-v1 converts an existing legacy run folder, or a .agenteval workspace selected with --run,
through the same converter as the retained export spelling. Its report says imported: not sealed when it ran;
an ingest seal protects the conversion from import onward, not the original execution's history. Source files
are preserved. See legacy import for the source-folder contract.
An explicit workspace run folder is imported exactly as selected. A workspace plus --run must identify one
folder; duplicate IDs across subjects are refused as ambiguous. Matching compliance reports must agree on
subject, source run ID and the recorded source hash. With --content-capture on, rich report bytes are retained
and cited by the corresponding scenarios; off retains only generic compliance evidence. Agreement with a
recorded hash does not establish the original execution's custody.
verify reports a run as intact, unsealed or invalid, with every problem. With --policy, it also says
which trusted identities signed it, and lets authorized redactions withhold blobs; with --anchors, whether one of
the run hashes you trust is the run's. Exit 1 when the run is invalid.
seal writes seal.json for a closed run, and attestation.dsse.json when --key gives a signing key (ECDSA
P-256). --sealed-by producer (the default) is for the run's own producer, which may seal only a run with no
problem; ingest is for a host taking custody of a run. Exit 1 when the run cannot be sealed.
view shows the run as its overlays leave it at --at (default: now): overridden results, reviews, waivers in
force, withheld blobs. The sealed files never change.
checkpoint checks a checkpoint manifest alone, against the runs it names (found under --runs by their
run.json), each lane's recomputed result and its decision, and its signature (<name>.dsse.json beside the
manifest, or --envelope). Exit 1 on any problem.
export converts a run of AgentEval's .agenteval/ store (store v1) into an AEF run marked as imported. Give a
run folder, or the workspace with --run. Store v1 does not record how the target was driven, so --target-mode
defaults to mocked: an exported run is never passed off as live evidence unless you say it was. The run is sealed
as ingest unless --no-seal.
import assert-ai converts an ASSERT run into an AEF run: a root line and harm and
over-refusal lines per case, ASSERT's rates in summary.json, with optional gate limits. With --content-capture off, no prompt, response, transcript or judge reasoning is kept.
import otel converts OpenTelemetry gen_ai.evaluation.result events (OTLP/JSON logs, one LogsData per line)
into an AEF run, as the OpenTelemetry interop page maps them: a line
per event, the run's id, subject and target mode from you, contentCapture on unless --content-capture off
(then logs carrying content are refused), sealed as ingest unless --no-seal, and verified. Events the page refuses
(two services, an event with no case or no name, a second event of one case with one name, …) exit 2, naming the
rule.
export-otel writes an AEF run as OpenTelemetry events: one LogsData line per result line, one
gen_ai.evaluation.result event per score, labelled with the result's sealed state (overlays are not applied) and
parented to its traceLink. The run must verify (intact or unsealed; --policy lets an authorized redaction
withhold a reasoning blob); the output file must not exist. A run it cannot export (one that does not verify, a time
OpenTelemetry cannot hold, a reasoning blob that is not UTF-8 or is over 4 MiB) exits 2, and nothing is written.
import inspect converts an Inspect eval log in .json form (for an .eval log,
run inspect log dump first) into an AEF run, as the Inspect interop page
maps it: a line per sample and score (trial = epoch − 1 when the log has more than one epoch), a rollup line per
reduction, the summary recomputed from the lines in one lane main, and the run's usage from stats. The subject is
model:<eval.model>; the target mode is yours, and contentCapture is on unless --content-capture off (then no
case content, explanation or answer is kept). A closed log is sealed as ingest unless --no-seal; a started log
gives a running run, which is not sealed. The log is read as I-JSON within a nesting depth of 64, Inspect's bare NaN
allowed only where an unscored value can be. A log the page refuses (one that is not I-JSON, such as a member named
twice, Infinity or a number that overflows; a time without an offset; a numeric id that is no integer of at most
2^53 − 1; a boolean value or a string other than C, I, P and N; a reducer without an AEF value; a missing
reduction; a mean the lines do not give; Score.history, an invalidation or log_updates; …) exits 2, naming the
rule, and nothing is written.
export-inspect writes an AEF run as one Inspect EvalLog in .json form: a sample per case and trial, a score
per result line under its path (the AEF facts Inspect has no field for under metadata.aef), a reduction per rollup
line, an EvalScore per summary entry; eval.model is the subject's ref with its name decoded. A run with
contentCapture: off gives no explanation, not even a line's reason. The run must verify (intact or unsealed;
--policy lets an authorized redaction withhold a blob, which is then left out), and its sealed lines are exported: a
run with overlay events is refused unless --ignore-overlays leaves them out. A run the page refuses (no suite, two
judges, two summary entries at one path, a count metric, output or transcript evidence a line cites, a reasoning
or content blob that is not UTF-8, …) exits 2, naming the rule, and nothing is written.
Every verb takes --json and then prints one JSON value. Exit 2 is a usage or input error.
Exit codes
The CLI's exit-code contract, so CI can branch on the outcome. Source of truth: src/AgentEval.Cli/ExitCodes.cs.
| Code | Meaning |
|---|---|
0 |
Success — passed / allowed / no gate blocked. |
1 |
Test failure — one or more evaluations failed (eval, redteam). |
2 |
Usage error (bad flags, malformed input). Reserved strictly for bad-argument paths — see BUG-22 below. Every command returns it for a parse error (see below). bench, compliance render and render also return it for an argument they reject themselves before the run starts: a missing --subject; an unknown preset or domain pack (every bench family) or vertical (bench typedmemeval); an invalid --budget-tier; an invalid --sut configuration or --endpoint without --model; a --response-file that cannot be read; no target at all (every bench family that grades an agent, and redteam and eval), --sut mock together with a real target, or a --response without the --input it answered. A missing .agenteval/ workspace is not an argument error: those commands exit 1 and tell you to run agenteval init-workspace. |
3 |
Runtime error (connection/model/IO failure). Also returned when a judge fails to build (JudgeFactory — missing or partial Azure OpenAI credentials, or a thrown exception constructing the client): that's a runtime/config problem, not a bad CLI argument. redteam --sut gatekeeper-demo --baseline also returns it when the baseline was taken on a different model (scripted vs real, or another real model). |
4 |
Regression vs a supplied --baseline — redteam --fail-on regression gate (a NEW finding vs pre-existing). |
5 |
gatekeeper inspect — a gate Blocked on real evidence. |
6 |
gatekeeper inspect — fail-closed: the CLI could not evaluate (e.g. a history gate with no messages). Not a policy block. |
7 |
gatekeeper inspect — not certified: the honesty guard refused an un-calibrated judge (run calibrate --certify, or pass --allow-uncalibrated). |
8 |
redteam --sut copilot-studio — a live scan hit its --max-credits cap (BudgetExceeded). Enforced as an ESTIMATE (turns counted, not metered spend — the SDK exposes no real credit-cost field); see Copilot Studio. |
9 |
bench <family> / bench <reg> calibrate — the composite/calibration gate is a hard FAIL (for calibration: a pillar or category FAIL, INFRA-FAIL or INCOMPLETE). |
10 |
bench <family> — the composite gate is a WARN (soft finding, below ideal but not a hard failure). Calibration commands never return this — their thresholds are pass/fail binary. |
11 |
No conclusive verdict, and nothing failed. bench <family> — the composite gate could not produce a conclusive verdict (e.g. skipped). Every --sut mock run exits 11 whatever it scores: it measured no agent. eval --runs N — a test case's verdict is undecided or not measured. eval --metrics … — a requested metric was not measured on some case. bench <reg> calibrate / bench agentic calibrate — a pillar or category is NOT MEASURED (nothing scored). A failure still exits 1 (eval) or 9 (calibrate). |
12 |
Reserved by ADR-031. No command returns it. |
13 |
compare — incomparable: the two runs could not be shown to have measured the same thing, so no delta was emitted (Incomparable). Distinct from 11, which means a run produced nothing scoreable: here both runs produced verdicts, and comparing them would be meaningless. See agenteval compare. redteam --baseline — the baseline or the current scan has no probe that reached a verdict, so the comparison is refused, neither a regression (4) nor a pass; see Red Team Security. |
Parse errors exit 2. An unknown command or option, a value that does not parse (--runs abc), a missing
required option, or no command at all exits 2. System.CommandLine prints the errors to stderr and the command's
help to stdout, as it always has; only the exit code differs from System.CommandLine's default, which is 1 and
would read as a test failure. --help exits 0, including on an otherwise incomplete command line
(agenteval eval --help without --dataset), and so does agenteval --version.
redteam uses 1 for failure, 3 for runtime error, and 4 for a --fail-on regression gate. Code 8 is
returned by a live --sut copilot-studio scan that hits --max-credits (BudgetExceeded) — an estimate, not a
metered value. gatekeeper's 5/6/7 are deliberately distinct exit codes — see
Gatekeeper from any language.
BUG-22 (resolved 2026-07-19): code 2 used to be overloaded — bench/calibrate returned it for gate
FAIL/WARN as well as bad arguments, and JudgeFactory config failures also returned it, so CI could not tell
"invoked wrong" from "agent failed the gate" from "judge misconfigured". This is now split across 2 (bad
arguments only), 3 (judge/runtime config problems), and 9/10/11 (gate outcomes) as documented above.
This is a breaking change for any external CI pipeline that branched on exit code 2 from bench/
calibrate commands — update those pipelines to check the new codes. See src/AgentEval.Cli/ExitCodes.cs
and CHANGELOG.md.
See Also
- Getting Started — C# library quickstart.
- The
.agenteval/Workspace — canonical layout, schema versions, audit chain. - Gatekeeper from any language — the
gatekeeperverb group, verdict JSON, and honesty guard. - Gatekeeper (Runtime Enforcement) — the runtime enforcement middleware overview.
- Mission Control Getting Started — the read-only web portal.