eve 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +14 -0
  3. package/dist/src/cli/banner.d.ts +6 -3
  4. package/dist/src/cli/banner.js +1 -1
  5. package/dist/src/cli/commands/init.js +1 -1
  6. package/dist/src/cli/dev/tui/agent-header.js +1 -1
  7. package/dist/src/cli/run.js +1 -1
  8. package/dist/src/evals/runner/run-evals.d.ts +1 -1
  9. package/dist/src/harness/input-requests.d.ts +20 -1
  10. package/dist/src/harness/input-requests.js +1 -1
  11. package/dist/src/harness/tool-loop.js +1 -1
  12. package/dist/src/internal/application/package.js +1 -1
  13. package/dist/src/protocol/message.d.ts +11 -2
  14. package/dist/src/protocol/message.js +2 -2
  15. package/dist/src/public/next/index.d.ts +1 -7
  16. package/dist/src/public/next/index.js +1 -1
  17. package/dist/src/public/next/vercel-output-config.js +1 -1
  18. package/dist/src/setup/scaffold/create/project.js +1 -1
  19. package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
  20. package/dist/src/setup/scaffold/create/web-template.js +27 -4
  21. package/dist/src/setup/scaffold/update/channels.js +1 -1
  22. package/docs/README.md +19 -5
  23. package/docs/agent-config.md +16 -19
  24. package/docs/channels/custom.mdx +24 -18
  25. package/docs/channels/discord.mdx +25 -13
  26. package/docs/channels/eve.mdx +24 -6
  27. package/docs/channels/github.mdx +41 -10
  28. package/docs/channels/linear.mdx +29 -36
  29. package/docs/channels/overview.mdx +16 -4
  30. package/docs/channels/slack.mdx +59 -32
  31. package/docs/channels/teams.mdx +19 -7
  32. package/docs/channels/telegram.mdx +23 -9
  33. package/docs/channels/twilio.mdx +28 -6
  34. package/docs/concepts/context-control.md +24 -41
  35. package/docs/concepts/default-harness.md +19 -9
  36. package/docs/concepts/execution-model-and-durability.md +14 -14
  37. package/docs/concepts/security-model.md +32 -8
  38. package/docs/concepts/sessions-runs-and-streaming.md +11 -9
  39. package/docs/connections.mdx +49 -29
  40. package/docs/evals/assertions.mdx +30 -29
  41. package/docs/evals/cases.mdx +10 -10
  42. package/docs/evals/judge.mdx +14 -14
  43. package/docs/evals/overview.mdx +15 -17
  44. package/docs/evals/reporters.mdx +19 -7
  45. package/docs/evals/running.mdx +2 -2
  46. package/docs/evals/targets.mdx +15 -4
  47. package/docs/getting-started.mdx +70 -31
  48. package/docs/guides/auth-and-route-protection.md +18 -12
  49. package/docs/guides/client/continuations.mdx +5 -13
  50. package/docs/guides/client/messages.mdx +2 -2
  51. package/docs/guides/client/output-schema.mdx +2 -2
  52. package/docs/guides/client/overview.mdx +4 -4
  53. package/docs/guides/client/streaming.mdx +6 -5
  54. package/docs/guides/deployment.md +22 -20
  55. package/docs/guides/dev-tui.md +85 -13
  56. package/docs/guides/dynamic-capabilities.md +24 -8
  57. package/docs/guides/dynamic-workflows.md +33 -12
  58. package/docs/guides/frontend/nextjs.mdx +28 -14
  59. package/docs/guides/frontend/nuxt.mdx +21 -4
  60. package/docs/guides/frontend/overview.mdx +11 -11
  61. package/docs/guides/frontend/sveltekit.mdx +20 -3
  62. package/docs/guides/frontend/use-eve-agent-svelte.mdx +106 -14
  63. package/docs/guides/frontend/use-eve-agent-vue.mdx +108 -15
  64. package/docs/guides/hooks.md +14 -60
  65. package/docs/guides/instrumentation.md +24 -18
  66. package/docs/guides/remote-agents.md +19 -15
  67. package/docs/guides/session-context.md +12 -12
  68. package/docs/guides/state.md +25 -9
  69. package/docs/instructions.mdx +18 -10
  70. package/docs/introduction.md +12 -12
  71. package/docs/legal.md +16 -0
  72. package/docs/meta.json +1 -0
  73. package/docs/reference/cli.md +101 -53
  74. package/docs/reference/meta.json +1 -1
  75. package/docs/reference/project-layout.md +39 -32
  76. package/docs/reference/typescript-api.md +1 -1
  77. package/docs/sandbox.mdx +25 -19
  78. package/docs/schedules.mdx +16 -14
  79. package/docs/skills.mdx +8 -6
  80. package/docs/subagents.mdx +39 -13
  81. package/docs/tools.mdx +13 -9
  82. package/docs/tutorial/connect-a-warehouse.mdx +16 -7
  83. package/docs/tutorial/first-agent.mdx +21 -11
  84. package/docs/tutorial/guard-the-spend.mdx +11 -9
  85. package/docs/tutorial/how-it-runs.mdx +10 -8
  86. package/docs/tutorial/query-sample-data.mdx +8 -8
  87. package/docs/tutorial/remember-definitions.mdx +5 -5
  88. package/docs/tutorial/run-analysis.mdx +13 -11
  89. package/docs/tutorial/ship-it.mdx +70 -14
  90. package/docs/tutorial/team-playbooks.mdx +33 -10
  91. package/package.json +1 -1
@@ -3,37 +3,38 @@ title: "Assertions"
3
3
  description: "Run-level methods, t.check value assertions, the matcher mini-language, and gate vs soft severity."
4
4
  ---
5
5
 
6
- Assertions are how an eval grades what its `test(t)` function produced. Each one **records** a result onto `t` and returns a chainable handle the runner reads the recorded results to compute the verdict, so a single run reports every failing assertion rather than dying on the first. There are two deterministic surfaces: run-level methods on `t`, and `t.check` for grading a specific value. For model-graded assertions, see [Judge](./judge).
6
+ Assertions are how an eval grades what its `test(t)` function produced. Each one **records** a result onto `t` and returns a chainable handle. The runner reads the recorded results to compute the verdict, so a single run reports every failing assertion rather than dying on the first. There are two deterministic surfaces: run-level methods on `t`, and `t.check` for grading a specific value. For model-graded assertions, see [Judge](./judge).
7
7
 
8
8
  ## Run-level assertions
9
9
 
10
- Run-level assertions read the whole run, so they take no value. They are methods on `t` and gate by default.
11
-
12
- | Assertion | Asserts |
13
- | --------------------------------------------------- | --------------------------------------------------------------------------------- |
14
- | `t.completed()` | The run did not fail and did not park on unanswered HITL input |
15
- | `t.didNotFail()` | No terminal failure and no `turn.failed`/`step.failed` events (parked runs pass) |
16
- | `t.waiting()` | The run parked on HITL input (for approval-shaped evals) |
17
- | `t.messageIncludes(token)` | Joined assistant text contains `token` (string or RegExp) |
18
- | `t.outputEquals(value)` / `t.outputMatches(schema)` | Deep equality / Standard Schema (e.g. Zod) validation of the parsed output |
19
- | `t.calledTool(name, opts?)` | A matching tool call happened (`input`, `output`, `isError`, `times` constraints) |
20
- | `t.notCalledTool(name)` | No call to `name` |
21
- | `t.toolOrder([...names])` | Tool names appear in order (other calls may interleave) |
22
- | `t.usedNoTools()` | No tool calls at all |
23
- | `t.maxToolCalls(n)` | At most `n` tool calls |
24
- | `t.noFailedActions()` | No tool, subagent, or skill action reported a failure |
25
- | `t.calledSubagent(name, opts?)` | A subagent delegation happened (`remoteUrl`, `output` constraints) |
26
- | `t.event(predicate, label)` | Escape hatch: any predicate over the typed event stream |
27
-
28
- `t.completed()` subsumes `t.didNotFail()` reach for `completed` unless you specifically want to allow a parked run.
10
+ Run-level assertions read the whole run, so they take no value. They are methods on `t` and gate by default. Several key off whether a run **parked**: paused on an unanswered human-in-the-loop (HITL) input request, waiting for an approval or answer before it can continue.
11
+
12
+ | Assertion | Asserts |
13
+ | --------------------------------------------------- | --------------------------------------------------------------------------------------- |
14
+ | `t.completed()` | The run did not fail and did not park on unanswered HITL input |
15
+ | `t.didNotFail()` | No terminal failure and no `turn.failed`/`step.failed` events (parked runs pass) |
16
+ | `t.waiting()` | The run parked on HITL input (for approval-shaped evals) |
17
+ | `t.messageIncludes(token)` | Joined assistant text contains `token` (string or RegExp) |
18
+ | `t.outputEquals(value)` / `t.outputMatches(schema)` | Deep equality or Standard Schema (e.g. Zod) validation of the agent's structured output |
19
+ | `t.calledTool(name, opts?)` | A matching tool call happened (`input`, `output`, `isError`, `times` constraints) |
20
+ | `t.notCalledTool(name)` | No call to `name` |
21
+ | `t.toolOrder([...names])` | Tool names appear in order (other calls may interleave) |
22
+ | `t.usedNoTools()` | No tool calls at all |
23
+ | `t.maxToolCalls(n)` | At most `n` tool calls |
24
+ | `t.noFailedActions()` | No tool, subagent, or skill action reported a failure |
25
+ | `t.calledSubagent(name, opts?)` | A subagent delegation happened (`remoteUrl`, `output` constraints) |
26
+ | `t.event(predicate, label)` | Escape hatch: any predicate over the typed event stream |
27
+
28
+ `t.completed()` subsumes `t.didNotFail()`, so reach for `completed` unless you specifically want to allow a parked run. The structured output that `t.outputEquals` and `t.outputMatches` read is the agent's structured output (see the [output schema guide](../guides/client/output-schema)).
29
29
 
30
30
  ```ts
31
31
  await t.send("What is the weather in Brooklyn?");
32
32
  t.completed();
33
33
  t.calledTool("get_weather");
34
- t.usedNoTools(); // mutually exclusive with the line above — pick the one you mean
35
34
  ```
36
35
 
36
+ `t.calledTool` and `t.usedNoTools` are mutually exclusive; assert one or the other, never both in the same run.
37
+
37
38
  ## Value assertions with `t.check`
38
39
 
39
40
  `t.check(value, assertion)` grades an explicit value against a builder from `eve/evals/expect`. The value can be `t.reply`, a turn's `.message`, parsed JSON, or any local you computed:
@@ -54,11 +55,11 @@ t.check(t.reply, similarity("Sunny, 72F")); // fuzzy 0–1 Levenshtein (soft)
54
55
  | `matches(schema)` | validates against a Standard Schema | gate |
55
56
  | `similarity(expected)` | normalized Levenshtein similarity, 1 = identical | soft |
56
57
 
57
- Pick the cheapest builder that captures what "correct" means. When exact match is too strict but a judge model is overkill, `similarity` is the middle ground; for nuanced grading, reach for the [judge](./judge).
58
+ Pick the cheapest builder that captures what "correct" means. When exact match is too strict but a judge model is overkill, `similarity` is the middle ground. For nuanced grading, reach for the [judge](./judge).
58
59
 
59
60
  ## The matcher mini-language
60
61
 
61
- `t.calledTool` and `t.calledSubagent` take a matcher object `{ input, output, isError, times }` for tools, `{ remoteUrl, output }` for subagents. Each field accepts a literal (objects partial-deep-match), a RegExp, or a function. A matcher function receives the value and returns either a boolean (acts as a predicate) or an expected value to compare against (handy for runner-assigned values like environment-provided URLs):
62
+ `t.calledTool` and `t.calledSubagent` take a matcher object: `{ input, output, isError, times }` for tools, `{ remoteUrl, output }` for subagents. Each field accepts a literal (objects partial-deep-match), a RegExp, or a function. A matcher function receives the value and returns either a boolean (acts as a predicate) or an expected value to compare against (handy for runner-assigned values like environment-provided URLs):
62
63
 
63
64
  ```ts
64
65
  t.calledTool("bash", { input: { command: /^pwd/ }, isError: false, times: 1 });
@@ -73,9 +74,9 @@ t.calledSubagent("weather", {
73
74
 
74
75
  ## Run state and derived facts
75
76
 
76
- A turn that leaves the session open for a next message is the normal end state of a successful turn. Parking on unanswered HITL input is tracked separately that is what `t.completed()` and `t.waiting()` key off.
77
+ Beyond the raw `t.events` stream, the runner derives typed facts the assertions read: tool calls (name, input, output, error state), subagent calls, and HITL input requests. A turn that leaves the session open for a next message is the normal end state of a successful turn; parking on unanswered HITL input is tracked separately, and that is what `t.completed()` and `t.waiting()` key off.
77
78
 
78
- Beyond the raw `t.events` stream, the runner derives typed facts the assertions read: tool calls (name, input, output, error state), subagent calls, and HITL input requests. The built-in assertions cover almost everything; when you need to read the stream directly, `t.event(predicate, label)` is the escape hatch:
79
+ The built-in assertions cover almost everything. When you need to read the stream directly, `t.event(predicate, label)` is the escape hatch:
79
80
 
80
81
  ```ts
81
82
  t.event(
@@ -87,11 +88,11 @@ t.event(
87
88
 
88
89
  ## Severity
89
90
 
90
- Every assertion returns a chainable handle. Severity rides on the assertion there is no separate thresholds map to keep in sync.
91
+ Every assertion returns a chainable handle. Severity rides on the assertion, so there is no separate thresholds map to keep in sync.
91
92
 
92
- - `.gate(threshold?)` hard. A miss marks the eval `failed` and `eve eval` exits non-zero.
93
- - `.soft(threshold?)` tracked data. A below-threshold miss marks the eval `scored`, fatal only under `--strict`. With no threshold, it is tracked-only and never fails.
94
- - `.atLeast(threshold)` soft with a bar (equivalent to `.soft(threshold)`).
93
+ - `.gate(threshold?)` is hard. A miss marks the eval `failed` and `eve eval` exits non-zero.
94
+ - `.soft(threshold?)` is tracked data. A below-threshold miss marks the eval `scored`, fatal only under `--strict`. With no threshold, it is tracked-only and never fails.
95
+ - `.atLeast(threshold)` is soft with a bar (equivalent to `.soft(threshold)`).
95
96
 
96
97
  The defaults are chosen so you rarely set severity. Run-level methods and `includes`/`equals`/`matches` are gates; `similarity` and every `t.judge.*` assertion are soft. Annotate only when you deviate:
97
98
 
@@ -3,11 +3,11 @@ title: "Cases"
3
3
  description: "Author single-turn and multi-turn evals with test(t), and fan one file out over a dataset."
4
4
  ---
5
5
 
6
- Each eval file is one graded case. The runner executes its `test(t)` function against the target, captures every event, and computes a verdict from the [assertions](./assertions) you recorded. Every eval single-turn, multi-turn, HITL, or dataset-driven — is the same shape: one `async test(t)` function that drives the agent and asserts inline.
6
+ Each eval file is one graded case by default, and a single file can fan out over a dataset by default-exporting an array (covered below). The runner executes each `test(t)` function against the target, captures every event, and computes a verdict from the [assertions](./assertions) you recorded. Every eval shares one shape, whether single-turn, multi-turn, human-in-the-loop (HITL), or dataset-driven: one `async test(t)` function that drives the agent and asserts inline.
7
7
 
8
8
  ## Single-turn evals
9
9
 
10
- The common case sends one turn and asserts on the reply. `t.send(input)` resolves once the turn settles; `t.reply` is the last assistant message:
10
+ The common case sends one turn and asserts on the reply. `t.send(input)` resolves once the turn settles, and `t.reply` is the last assistant message:
11
11
 
12
12
  ```ts title="evals/weather/brooklyn-forecast.eval.ts"
13
13
  import { defineEval } from "eve/evals";
@@ -22,7 +22,7 @@ export default defineEval({
22
22
  });
23
23
  ```
24
24
 
25
- Some evals only care about behavior, not text assert on the run and skip the content check entirely:
25
+ Some evals only care about behavior, not text. Assert on the run and skip the content check entirely:
26
26
 
27
27
  ```ts title="evals/weather/no-tools-for-greetings.eval.ts"
28
28
  import { defineEval } from "eve/evals";
@@ -43,7 +43,7 @@ Identity is the file path, so directories are the grouping mechanism. `evals/wea
43
43
  ```text
44
44
  evals/
45
45
  ├── weather/
46
- │ ├── shared.ts # helpers not an eval
46
+ │ ├── shared.ts # helpers, not an eval
47
47
  │ ├── brooklyn-forecast.eval.ts
48
48
  │ └── no-tools-for-greetings.eval.ts
49
49
  └── smoke.eval.ts
@@ -51,7 +51,7 @@ evals/
51
51
 
52
52
  ## Multi-turn evals
53
53
 
54
- Drive several turns in sequence branching, HITL approvals, structured output, attachments, multiple sessions. Because assertions live in the function, an intermediate value is just a local variable: judge a draft before the next turn overwrites it, then keep going.
54
+ Drive several turns in sequence for branching, HITL approvals, structured output, attachments, or multiple sessions. Because assertions live in the function, an intermediate value is a local variable. Judge a draft before the next turn overwrites it, then keep going.
55
55
 
56
56
  ```ts title="evals/draft-then-send.eval.ts"
57
57
  import { defineEval } from "eve/evals";
@@ -69,7 +69,7 @@ export default defineEval({
69
69
  });
70
70
  ```
71
71
 
72
- Bespoke preconditions that no built-in assertion expresses are plain `throw`s a thrown error marks the eval `failed` with the message in the result:
72
+ For a precondition no built-in assertion expresses, `throw`. A thrown error marks the eval `failed` with the message in the result:
73
73
 
74
74
  ```ts title="evals/session-continuity.eval.ts"
75
75
  import { defineEval } from "eve/evals";
@@ -96,22 +96,22 @@ export default defineEval({
96
96
 
97
97
  `t` drives the primary session; `t.newSession()` returns an independent `EveEvalSession` against the same target, whose events feed the same run-level assertions.
98
98
 
99
- - `t.send(input)` sends a turn and waits for it to settle. It accepts the same input as `ClientSession.send()` a string or a structured message and resolves to a turn carrying `.message` and `.expectOk()`.
99
+ - `t.send(input)` sends a turn and waits for it to settle. It accepts the same input as `ClientSession.send()` (a string or a structured message) and resolves to a turn carrying `.message` and `.expectOk()`.
100
100
  - `t.sendFile(text, path, mediaType?)` attaches a local file as a data URL.
101
101
  - `t.expectInputRequests(filter?)` asserts the previous turn parked on HITL input and returns the pending requests.
102
102
  - `t.respond(...responses)` answers specific pending input requests and sends them as the next turn.
103
103
  - `t.respondAll(optionId)` answers every pending input request with the same option and sends the responses as the next turn.
104
104
  - `t.reply` is the last assistant message (or `null`); `t.sessionId` is the current session id; `t.events` is the full typed event stream captured so far.
105
105
 
106
- Each `send` (and `respond`/`respondAll`) resolves to a turn whose `expectOk()` throws only when the turn ended failed a session left open for a next message is the normal end state of a successful turn.
106
+ Each `send` (and `respond`/`respondAll`) resolves to a turn whose `expectOk()` throws only when the turn ended failed. A session left open for a next message is the normal end state of a successful turn.
107
107
 
108
108
  Events from every session are captured in the result and artifacts. `t.log(message)` records debug lines into the eval artifact; `--verbose` also streams them to stdout as evals run. `t.signal` is an `AbortSignal` that fires on timeout.
109
109
 
110
- For driving sessions created outside the eval by a channel webhook or a schedule see [Targets](./targets).
110
+ For driving sessions created outside the eval, by a channel webhook or a schedule, see [Targets](./targets).
111
111
 
112
112
  ## Datasets: exporting an array
113
113
 
114
- To fan one file out over a dataset, default-export an array of `defineEval(...)` values. Eval modules are ESM, so top-level `await` can load anything. Ids derive from the file name plus a zero-padded index (`sql/0000`, `sql/0001`, …, in array order). The loaders (`loadJson`, `loadYaml` from `eve/evals/loaders`) parse fixture files relative to the app root:
114
+ To fan one file out over a dataset, default-export an array of `defineEval(...)` values. Eval modules are ESM, so top-level `await` can load anything. Ids derive from the file name plus a zero-padded index in array order (`sql/0000`, `sql/0001`, and so on). The loaders (`loadJson`, `loadYaml` from `eve/evals/loaders`) parse fixture files relative to the app root:
115
115
 
116
116
  ```ts title="evals/sql.eval.ts"
117
117
  import { defineEval } from "eve/evals";
@@ -3,7 +3,7 @@ title: "Judge"
3
3
  description: "Grade evals with an LLM judge via t.judge.autoevals, set thresholds on the assertion, and configure the judge model."
4
4
  ---
5
5
 
6
- When no deterministic [assertion](./assertions) captures what "good" means factual correctness, summary quality, free-form criteria grade the run with an LLM judge. The `t.judge.*` assertions are the only model-backed ones, and they use a judge model that is resolved separately from the agent under test: Eve only uses it for scoring, never to swap out the agent.
6
+ When no deterministic [assertion](./assertions) captures what "good" means (factual correctness, summary quality, free-form criteria), grade the run with an LLM judge. The `t.judge.*` assertions are the only model-backed ones, and they use a judge model that is resolved separately from the agent under test. Eve only uses it for scoring, never to swap out the agent.
7
7
 
8
8
  ```ts
9
9
  import { defineEval } from "eve/evals";
@@ -19,7 +19,7 @@ export default defineEval({
19
19
 
20
20
  ## The graders
21
21
 
22
- The judges live under `t.judge.autoevals` the namespace names the [Braintrust autoevals](https://github.com/braintrustdata/autoevals) grader family, so the factuality and closedQA semantics are autoevals', not Eve-invented. Each grades `t.reply` by default and is soft by default (tracked, no gate):
22
+ The judges live under `t.judge.autoevals`. The namespace names the [Braintrust autoevals](https://github.com/braintrustdata/autoevals) grader family, so the factuality and closedQA semantics are autoevals', not Eve-invented. Each grader scores `t.reply` by default and is soft by default (tracked, no gate):
23
23
 
24
24
  | Grader | Grades |
25
25
  | ---------------------------------------- | -------------------------------------------------------------------------------------- |
@@ -30,8 +30,8 @@ The judges live under `t.judge.autoevals` — the namespace names the [Braintrus
30
30
 
31
31
  The reference or criteria is the positional argument. An options object follows:
32
32
 
33
- - `on` the value to grade, defaulting to `t.reply`. Pass an intermediate draft or parsed value to grade it instead.
34
- - `model` / `modelOptions` a per-call judge override (see below).
33
+ - `on` is the value to grade, defaulting to `t.reply`. Pass an intermediate draft or parsed value to grade it instead.
34
+ - `model` and `modelOptions` are a per-call judge override (see below).
35
35
 
36
36
  ```ts
37
37
  const draft = await t.send("Draft the welcome email.");
@@ -40,11 +40,11 @@ t.judge.autoevals.closedQA("professional tone", { on: draft.message }).atLeast(0
40
40
 
41
41
  ## Soft scoring and thresholds
42
42
 
43
- Judge assertions are soft, so the threshold rides on the assertion handle there is no separate thresholds map:
43
+ Judge assertions are soft, so the threshold rides on the assertion handle. There is no separate thresholds map:
44
44
 
45
- - **No threshold** tracked-only. The score lands in reports and artifacts and never fails the eval. Use it to watch a metric without gating on it.
46
- - `.atLeast(threshold)` a soft bar. A below-threshold score marks the eval `scored`, fatal only under `eve eval --strict`.
47
- - `.gate(threshold)` promote a judge to a hard gate that fails the eval outright.
45
+ - **No threshold** is tracked-only. The score lands in reports and artifacts and never fails the eval. Use it to watch a metric without gating on it.
46
+ - `.atLeast(threshold)` is a soft bar. A below-threshold score marks the eval `scored`, fatal only under `eve eval --strict`.
47
+ - `.gate(threshold)` promotes a judge to a hard gate that fails the eval outright.
48
48
 
49
49
  ```ts
50
50
  t.judge.autoevals.closedQA("cites a source"); // tracked, never fails
@@ -58,9 +58,9 @@ A judge runs once per assertion and burns tokens, so reach for one only when not
58
58
 
59
59
  The judge model is resolved once when the runner builds `t`. It is **never** the model under test. Three levels resolve innermost-wins:
60
60
 
61
- 1. **Per-call** `t.judge.autoevals.closedQA("…", { model, modelOptions })`.
62
- 2. **Per-eval** `defineEval({ judge: { model, modelOptions }, test })`.
63
- 3. **Project default** `defineEvalConfig({ judge: { model, modelOptions } })` in `evals.config.ts`.
61
+ 1. **Per-call**: `t.judge.autoevals.closedQA("…", { model, modelOptions })`.
62
+ 2. **Per-eval**: `defineEval({ judge: { model, modelOptions }, test })`.
63
+ 3. **Project default**: `defineEvalConfig({ judge: { model, modelOptions } })` in `evals.config.ts`.
64
64
 
65
65
  ```ts title="evals/evals.config.ts"
66
66
  import { defineEvalConfig } from "eve/evals";
@@ -78,14 +78,14 @@ export default defineEval({
78
78
  async test(t) {
79
79
  await t.send("Explain quantum tunneling to a 10-year-old.");
80
80
  t.judge.autoevals.factuality(reference).atLeast(0.7);
81
- t.judge.autoevals.closedQA("is concise", { model: "anthropic/claude-haiku-4-5" }); // cheaper, per-call
81
+ t.judge.autoevals.closedQA("is concise", { model: "anthropic/claude-haiku-4.5" }); // cheaper, per-call
82
82
  },
83
83
  });
84
84
  ```
85
85
 
86
- `judge` in `evals.config.ts` is optional a tree of fully deterministic evals can omit it. But calling `t.judge.*` with no judge model resolved is a fail-fast error at eval definition time.
86
+ `judge` in `evals.config.ts` is optional, and a tree of fully deterministic evals can omit it. Calling `t.judge.*` with no judge model resolved records a failed gate: the runner scores the assertion after the `test` function runs, the missing model throws, and the eval fails with that message.
87
87
 
88
- A **string model id** (e.g. `"anthropic/claude-opus-4.8"`) routes through the Vercel AI Gateway and needs `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN` in the environment; an **AI SDK `LanguageModel` instance** is used directly. With a model configured but no credentials, a judge-backed eval **skips visibly** like other real-model legs, so mock-model fixture runs stay green. For provider-specific judge settings, use `modelOptions.providerOptions`.
88
+ A **string model id** (e.g. `"anthropic/claude-opus-4.8"`) routes through the Vercel AI Gateway and needs `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN` in the environment. An **AI SDK `LanguageModel` instance** is used directly. With a model configured but no credentials, a judge-backed eval **skips visibly** rather than failing, so the run reports the skip instead of a spurious error. For provider-specific judge settings, use `modelOptions.providerOptions`.
89
89
 
90
90
  ## What to read next
91
91
 
@@ -3,13 +3,13 @@ title: "Overview"
3
3
  description: "Define repeatable scored checks for an Eve agent with defineEval and run them with eve eval."
4
4
  ---
5
5
 
6
- An eval is a scored check that runs your agent against real sessions and grades the result. Use it to catch regressions when you change a prompt or a tool: drive the agent through one or more turns, then assert on what it did the run completed, the right tool ran, the reply contains the right text and optionally ship the results to Braintrust.
6
+ An eval is a scored check that runs your agent against real sessions and grades the result, catching regressions when you change a prompt or a tool. Drive the agent through one or more turns, assert on what it did (the run completed, the right tool ran, the reply contains the right text), and optionally ship the results to Braintrust.
7
7
 
8
- Evals exercise the same HTTP surface your users hit. The runner boots (or targets) a real agent server, drives sessions through the [TypeScript client](../guides/client/overview) protocol, and grades what comes back so a passing eval means the agent actually booted, accepted a request, and produced the result you asserted.
8
+ Evals exercise the same HTTP surface your users hit. The runner boots (or targets) a real agent server, drives sessions through the [TypeScript client](../guides/client/overview) protocol, and grades what comes back, so a passing eval means the agent booted, accepted a request, and produced the result you asserted.
9
9
 
10
10
  ## `defineEval`
11
11
 
12
- Eve discovers evals under the app-root `evals/` directory, in `.eval.ts` files. Each file is exactly one eval one graded case. The file path is the eval's identity, so you don't author an `id` or `name`; directories group related evals (`evals/weather/brooklyn-forecast.eval.ts` id `weather/brooklyn-forecast`).
12
+ Eve discovers evals under the app-root `evals/` directory, in `.eval.ts` files. Each file is one eval by default. A file can also default-export an array to fan out over a dataset (see [Cases](./cases)). The file path is the eval's identity, so you don't author an `id` or `name`. Directories group related evals (`evals/weather/brooklyn-forecast.eval.ts` becomes id `weather/brooklyn-forecast`).
13
13
 
14
14
  ```text
15
15
  my-agent/
@@ -40,7 +40,7 @@ export default defineEval({
40
40
  });
41
41
  ```
42
42
 
43
- `test` is the only required field. The rest are optional: `description`, `judge`, `tags`, `metadata`, `timeoutMs`, `reporters`. The init template adds `evals/**/*.ts` to `tsconfig.json`, so your eval code type-checks alongside the app.
43
+ `test` is the only required field. The rest are optional: `description`, `judge`, `tags`, `metadata`, `timeoutMs`, and `reporters`. The init template adds `evals/**/*.ts` to `tsconfig.json`, so your eval code type-checks alongside the app.
44
44
 
45
45
  ## `evals.config.ts`
46
46
 
@@ -56,11 +56,11 @@ export default defineEvalConfig({
56
56
  });
57
57
  ```
58
58
 
59
- Everything is optional. `judge` sets the default model for [LLM-as-judge](./judge) assertions (`t.judge.*`) — only evals that use them need it, so a tree of fully deterministic evals can omit it entirely. `reporters`, `maxConcurrency`, and `timeoutMs` round out the defaults. Config `reporters` observe every eval in the run set one `Braintrust()` here instead of adding it to each eval. CLI flags (`--max-concurrency`, `--timeout`) and per-eval values take precedence over the config defaults.
59
+ Everything is optional. `judge` sets the default model for [LLM-as-judge](./judge) assertions (`t.judge.*`); a tree of fully deterministic evals can omit it. `reporters`, `maxConcurrency`, and `timeoutMs` round out the defaults. Config `reporters` observe every eval in the run, so set one `Braintrust()` here instead of adding it to each eval. CLI flags (`--max-concurrency`, `--timeout`) and per-eval values take precedence over the config defaults.
60
60
 
61
61
  ## The `t` context
62
62
 
63
- `t` is both the driver and the assertion surface. There are no separate `input`, `run`, `checks`, or `scores` fields you write ordinary control flow, sending turns and asserting inline.
63
+ `t` is both the driver and the assertion surface. There are no separate `input`, `run`, `checks`, or `scores` fields. You write ordinary control flow, sending turns and asserting inline.
64
64
 
65
65
  - **Drive** the agent: `t.send(...)`, `t.respond(...)`, `t.respondAll(...)`, `t.sendFile(...)`, `t.expectInputRequests(...)`, `t.newSession()`. Read what came back with `t.reply` (the last assistant message), `t.sessionId`, and `t.events`. See [Cases](./cases).
66
66
  - **Assert** with three surfaces, covered next.
@@ -69,16 +69,16 @@ Everything is optional. `judge` sets the default model for [LLM-as-judge](./judg
69
69
 
70
70
  Each surface matches a genuinely different kind of judgment:
71
71
 
72
- - **Run-level methods** read the whole run `t.completed()`, `t.calledTool("get_weather")`, `t.usedNoTools()`, `t.toolOrder([...])`. They take no value because they observe the run itself. See [Assertions](./assertions).
73
- - **`t.check(value, assertion)`** grades an explicit value with a deterministic builder from `eve/evals/expect` `t.check(t.reply, includes("sunny"))`. Grade `t.reply`, an intermediate draft, parsed JSON anything. See [Assertions](./assertions).
74
- - **`t.judge.autoevals.*`** is the LLM-as-judge surface `t.judge.autoevals.closedQA("cites a source")`. It grades `t.reply` by default and uses the configured judge model, never the agent under test. See [Judge](./judge).
72
+ - **Run-level methods** read the whole run, like `t.completed()`, `t.calledTool("get_weather")`, `t.usedNoTools()`, and `t.toolOrder([...])`. They take no value because they observe the run itself. See [Assertions](./assertions).
73
+ - **`t.check(value, assertion)`** grades an explicit value with a deterministic builder from `eve/evals/expect`, such as `t.check(t.reply, includes("sunny"))`. Grade `t.reply`, an intermediate draft, parsed JSON, or anything else. See [Assertions](./assertions).
74
+ - **`t.judge.autoevals.*`** is the LLM-as-judge surface, like `t.judge.autoevals.closedQA("cites a source")`. It grades `t.reply` by default and uses the configured judge model, never the agent under test. See [Judge](./judge).
75
75
 
76
76
  ## Gate vs soft
77
77
 
78
- Every assertion returns a chainable handle, so severity rides on the assertion itself there is no separate thresholds map:
78
+ Every assertion returns a chainable handle, so severity rides on the assertion itself. There is no separate thresholds map.
79
79
 
80
80
  - **Gates** are hard. A failed gate marks the eval `failed` and `eve eval` exits non-zero. Run-level methods, `includes`, `equals`, and `matches` are gates by default.
81
- - **Soft** assertions are tracked data. They land in reports and artifacts, and a below-threshold soft assertion marks the eval `scored` visible but not fatal, unless you pass `--strict`. `similarity` and every `t.judge.*` assertion are soft by default. A soft assertion with no threshold is tracked-only and never fails.
81
+ - **Soft** assertions are tracked data. They land in reports and artifacts, and a below-threshold soft assertion marks the eval `scored` (visible but not fatal, unless you pass `--strict`). `similarity` and every `t.judge.*` assertion are soft by default. A soft assertion with no threshold is tracked-only and never fails.
82
82
 
83
83
  Override per assertion: `.gate(threshold?)` promotes to a hard gate, `.soft(threshold?)` demotes to tracked, and `.atLeast(threshold)` is a soft assertion with a bar.
84
84
 
@@ -89,7 +89,7 @@ t.judge.autoevals.closedQA("cites a source"); // soft, tracked (no threshold)
89
89
  t.judge.autoevals.factuality(reference).atLeast(0.7); // soft, gated under --strict at 0.7
90
90
  ```
91
91
 
92
- ## Run it
92
+ ## Run evals with eve eval
93
93
 
94
94
  ```bash
95
95
  eve eval # run all discovered evals against a local dev server
@@ -101,7 +101,9 @@ Exit code `0` means every eval passed its gates. See [Running evals](./running)
101
101
 
102
102
  ## A good baseline
103
103
 
104
- Most apps do fine with a few small smoke evals. Assert behavior with `t.completed()` plus one or two content checks, keep dataset fixtures in `evals/data/`, and only reach for a judge or Braintrust once you actually need fuzzy grading or shared result review. In CI, run `eve eval --strict` so soft threshold misses fail the build too.
104
+ Most apps do fine with a few small smoke evals. Assert behavior with `t.completed()` plus one or two content checks, keep dataset fixtures in `evals/data/`, and reach for a judge or Braintrust only when you need fuzzy grading or shared result review. In CI, run `eve eval --strict` so soft threshold misses fail the build too.
105
+
106
+ ## What to read next
105
107
 
106
108
  The rest of this section covers each piece:
107
109
 
@@ -111,8 +113,4 @@ The rest of this section covers each piece:
111
113
  - [Targets](./targets): local vs remote targets for the same eval files
112
114
  - [Reporters](./reporters): Braintrust experiments and JUnit XML
113
115
  - [Running evals](./running): the `eve eval` CLI, exit codes, and artifacts
114
-
115
- ## What to read next
116
-
117
- - [Cases](./cases): author your first evals
118
116
  - [Tools](../tools): the surface most evals assert on
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  title: "Reporters"
3
- description: "Ship eval results to Braintrust experiments or JUnit XML Eve runs and scores everything itself."
3
+ description: "Ship eval results to Braintrust experiments or JUnit XML. Eve runs and scores everything itself."
4
4
  ---
5
5
 
6
- Eve runs and grades everything itself; reporters just ship the results out. The CLI prints a console summary by default one line per eval, failed assertions with their messages and reporters from `eve/evals/reporters` add destinations on top.
6
+ Eve runs and grades everything itself; reporters ship the results out. The CLI prints a console summary by default (one line per eval, with failed assertions and their messages), and reporters from `eve/evals/reporters` add destinations on top.
7
7
 
8
- Reporters attach in two places. Declare them in `evals.config.ts` to observe **every** eval in the run — the usual choice for a shared destination like one Braintrust experiment, so you don't repeat the reporter in each file. Or list them on an individual eval's `reporters` to scope a destination to that eval (or to a group of evals that share one instance).
8
+ You are responsible for ensuring any observability or eval provider is approved for the data exported to it.
9
+
10
+ Reporters attach in two places. Declare them in `evals.config.ts` to observe **every** eval in the run, the usual choice for a shared destination like one Braintrust experiment, so you don't repeat the reporter in each file. Or list them on an individual eval's `reporters` to scope a destination to that eval (or to a group of evals that share one instance).
9
11
 
10
12
  ## Braintrust
11
13
 
@@ -38,13 +40,13 @@ export default defineEval({
38
40
 
39
41
  The reporter config takes an optional `projectName` and `experimentName`, plus a base experiment (by name or id) to diff against. Gate assertions log as binary scores under a `gate:` prefix so experiments diff gate regressions the same way they diff soft-score regressions. Eval `metadata` rides along to reporters.
40
42
 
41
- A reporter instance observes the evals that reference it: share one instance across several evals the config, a `shared.ts` export, or every entry of a dataset array and their results land in a single experiment. Listing the same config reporter on an eval too does not double-report it.
43
+ A reporter instance observes the evals that reference it. Share one instance across several evals (the config, a `shared.ts` export, or every entry of a dataset array) and their results land in a single experiment. Listing the same config reporter on an eval too does not double-report it.
42
44
 
43
- Braintrust needs its SDK installed in the app and credentials in the environment. Pass `--skip-report` to run the eval without shipping results (this also suppresses config reporters) useful locally when iterating.
45
+ Braintrust needs its SDK installed in the app and credentials in the environment: install the `braintrust` package (`npm install braintrust`) and set `BRAINTRUST_API_KEY`. Pass `--skip-report` to run the eval without shipping results, which also suppresses config reporters and is useful locally when iterating.
44
46
 
45
47
  ## JUnit
46
48
 
47
- `JUnit({ filePath })` writes JUnit XML for CI annotations. The `--junit <path>` CLI flag does the same thing without touching the eval file, which is usually the better fit CI owns the output path, not the eval:
49
+ `JUnit({ filePath })` writes JUnit XML for CI annotations. The `--junit <path>` CLI flag does the same thing without touching the eval file, usually the better fit because CI owns the output path, not the eval:
48
50
 
49
51
  ```bash
50
52
  eve eval --strict --junit .eve/junit.xml
@@ -54,7 +56,17 @@ Each eval becomes one `<testcase>` named by its path-derived id; failed gates an
54
56
 
55
57
  ## Custom reporters
56
58
 
57
- A reporter implements the `EvalReporter` interface from `eve/evals/reporters` and receives the same structured results the built-ins do. Reach for one only when a destination isn't covered the per-run artifacts under `.eve/evals/` already capture everything for ad-hoc inspection.
59
+ A reporter implements the `EvalReporter` interface from `eve/evals/reporters` and receives the same structured results the built-ins do. The runner calls three lifecycle methods, each of which may return a promise for async work like a remote upload:
60
+
61
+ ```ts
62
+ interface EvalReporter {
63
+ onRunStart(evaluations: readonly EveEval[], target: EveEvalTarget): void | Promise<void>;
64
+ onEvalComplete(result: EveEvalResult): void | Promise<void>;
65
+ onRunComplete(summary: EveEvalRunSummary): void | Promise<void>;
66
+ }
67
+ ```
68
+
69
+ `onRunStart` fires once before any eval runs, `onEvalComplete` fires after each observed eval with its checks, scores, and verdict, and `onRunComplete` fires once with the aggregated summary. Reach for a custom reporter only when a destination isn't covered. The per-run artifacts under `.eve/evals/` already capture everything for ad-hoc inspection.
58
70
 
59
71
  ## What to read next
60
72
 
@@ -15,7 +15,7 @@ eve eval --timeout 60000 # per-eval timeout in milliseconds
15
15
  eve eval --max-concurrency 4 # cap concurrent eval executions (default 8)
16
16
  eve eval --junit .eve/junit.xml # write JUnit XML
17
17
  eve eval --list # print discovered evals without running
18
- eve eval --verbose # stream per-eval ctx.log lines to stdout
18
+ eve eval --verbose # stream per-eval t.log lines to stdout
19
19
  eve eval --json # machine-readable output
20
20
  eve eval --skip-report # skip config and eval-defined reporters (e.g. Braintrust)
21
21
  ```
@@ -27,7 +27,7 @@ Positional ids match exactly or by directory prefix: `eve eval weather` runs `ev
27
27
  | Code | Means |
28
28
  | ---- | ------------------------------------------------------------------------------- |
29
29
  | `0` | Every eval passed its gates (and soft thresholds, under `--strict`) |
30
- | `1` | Any eval failed a failed gate, an execution error, or a strict threshold miss |
30
+ | `1` | Any eval failed (a failed gate, an execution error, or a strict threshold miss) |
31
31
  | `2` | Configuration error |
32
32
 
33
33
  ## Artifacts
@@ -3,7 +3,7 @@ title: "Targets"
3
3
  description: "Point evals at a local dev server or a deployment with the same eval files."
4
4
  ---
5
5
 
6
- An eval target is always an HTTP URL. `eve eval` starts a local dev server, while `eve eval --url <url>` runs against an existing server or deployment the same eval files work for both, which is what makes evals usable as end-to-end tests in CI.
6
+ An eval target is always an HTTP URL. `eve eval` starts a local dev server, while `eve eval --url <url>` runs against an existing server or deployment. The same eval files work for both, which is what makes evals usable as end-to-end tests in CI.
7
7
 
8
8
  The runner polls `/eve/v1/health`, verifies `/eve/v1/info`, and exposes the live target as `t.target` inside the `test` function.
9
9
 
@@ -22,11 +22,22 @@ export default defineEval({
22
22
  });
23
23
  ```
24
24
 
25
- - `t.target.fetch(path, init)` performs an authenticated fetch against the target useful for channel and webhook ingress.
25
+ - `t.target.fetch(path, init)` performs an authenticated fetch against the target, useful for channel and webhook ingress. See [Authentication](#authentication) for how the runner authenticates.
26
26
  - `t.target.dispatchSchedule(id)` triggers a [schedule](../schedules) through the dev-only schedule route and returns the session ids it created. It works only against a target with dev routes enabled (the local `eve eval` dev server, or a deployment running in development mode), and throws otherwise.
27
- - `t.target.attachSession(sessionId, { startIndex? })` consumes one turn from a session created outside the eval by a channel or a schedule so its events feed the run-level assertions.
27
+ - `t.target.attachSession(sessionId, { startIndex? })` consumes one turn from a session created outside the eval, by a channel or a schedule, so its events feed the run-level assertions. `startIndex` skips events before that position, so a session already partway through its stream resumes from where you left off rather than replaying from the start.
28
28
 
29
- Sessions attached this way are full `EveEvalSession`s: you can keep driving them with `send` and read their event streams. The run-level assertions on `t` (`t.completed()`, `t.calledTool(...)`) read the whole run, attached sessions included.
29
+ Sessions attached this way are full `EveEvalSession`s: you can keep driving them with `send` and read their event streams. The run-level assertions on `t` (`t.completed()`, `t.calledTool(...)`) read the whole run, including attached sessions.
30
+
31
+ ## Authentication
32
+
33
+ Local targets send no auth: `eve eval` owns the dev server it boots. A remote `--url` target connects with the same credentials as every other development client, resolved in this order:
34
+
35
+ - A Vercel OIDC trusted-IDP token, sent as a per-request header. It bypasses Deployment Protection without a per-project secret, so a CI job with a pulled OIDC token reaches a protected preview deployment without extra setup.
36
+ - An `x-vercel-protection-bypass` header, added when `VERCEL_AUTOMATION_BYPASS_SECRET` is set.
37
+ - A bearer token resolved from the same OIDC cascade.
38
+ - `EVE_EVAL_AUTH_TOKEN`, which overrides the bearer with a static token for targets whose auth is not OIDC-based.
39
+
40
+ `t.target.fetch(path, init)` carries these same credentials, so channel and webhook ingress you exercise through it authenticates the same way the session protocol does.
30
41
 
31
42
  ## What to read next
32
43