niceeval 0.4.4 → 0.4.5

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 (177) hide show
  1. package/README.md +21 -12
  2. package/README.zh.md +8 -9
  3. package/docs-site/.mintignore +13 -0
  4. package/docs-site/AGENTS.md +46 -0
  5. package/docs-site/concepts/adapter.mdx +287 -0
  6. package/docs-site/concepts/assert.mdx +243 -0
  7. package/docs-site/concepts/drive.mdx +118 -0
  8. package/docs-site/concepts/evals.mdx +108 -0
  9. package/docs-site/concepts/experiment.mdx +37 -0
  10. package/docs-site/concepts/hitl.mdx +83 -0
  11. package/docs-site/concepts/judge.mdx +129 -0
  12. package/docs-site/concepts/overview.mdx +98 -0
  13. package/docs-site/concepts/tier.mdx +42 -0
  14. package/docs-site/docs-ref/00-index.md +23 -0
  15. package/docs-site/docs-ref/01-page-types.md +43 -0
  16. package/docs-site/docs-ref/03-style-rules.md +45 -0
  17. package/docs-site/docs-ref/04-code-examples.md +37 -0
  18. package/docs-site/docs-ref/05-dx-failure-paths.md +45 -0
  19. package/docs-site/docs-ref/06-checklists.md +53 -0
  20. package/docs-site/docs.json +256 -0
  21. package/docs-site/example/ai-agent-application.mdx +148 -0
  22. package/docs-site/example/claude-code-codex-plugin.mdx +162 -0
  23. package/docs-site/example/claude-code-codex-skill.mdx +147 -0
  24. package/docs-site/example/showcase.mdx +39 -0
  25. package/docs-site/example/tier1-ai-sdk-v7.mdx +136 -0
  26. package/docs-site/example/tier1-claude-sdk.mdx +119 -0
  27. package/docs-site/example/tier1-codex-sdk.mdx +111 -0
  28. package/docs-site/example/tier1-langgraph.mdx +119 -0
  29. package/docs-site/example/tier1-pi-sdk.mdx +119 -0
  30. package/docs-site/favicon.svg +5 -0
  31. package/docs-site/github-diff.css +135 -0
  32. package/docs-site/github-diff.js +11 -0
  33. package/docs-site/guides/authoring.mdx +129 -0
  34. package/docs-site/guides/ci-integration.mdx +97 -0
  35. package/docs-site/guides/connect-otel.mdx +209 -0
  36. package/docs-site/guides/connect-your-agent.mdx +221 -0
  37. package/docs-site/guides/dataset-fanout.mdx +98 -0
  38. package/docs-site/guides/experiments.mdx +71 -0
  39. package/docs-site/guides/fixtures.mdx +147 -0
  40. package/docs-site/guides/reporters.mdx +113 -0
  41. package/docs-site/guides/runner.mdx +79 -0
  42. package/docs-site/guides/sandbox-agent.mdx +138 -0
  43. package/docs-site/guides/sandbox-backends.mdx +64 -0
  44. package/docs-site/guides/scoring-guide.mdx +92 -0
  45. package/docs-site/guides/viewing-results.mdx +195 -0
  46. package/docs-site/guides/write-experiment.mdx +81 -0
  47. package/docs-site/guides/write-send.mdx +347 -0
  48. package/docs-site/images/adapter-tiers-zh.svg +60 -0
  49. package/docs-site/images/agent-turn-roundtrip-en.svg +62 -0
  50. package/docs-site/images/agent-turn-roundtrip-zh.svg +77 -0
  51. package/docs-site/images/hitl-handshake-zh.svg +89 -0
  52. package/docs-site/images/logo.svg +6 -0
  53. package/docs-site/index.mdx +181 -0
  54. package/docs-site/introduction.mdx +141 -0
  55. package/docs-site/quickstart.mdx +136 -0
  56. package/docs-site/reference/builtin-agents.mdx +161 -0
  57. package/docs-site/reference/capabilities.mdx +76 -0
  58. package/docs-site/reference/cli.mdx +120 -0
  59. package/docs-site/reference/define-agent.mdx +168 -0
  60. package/docs-site/reference/define-config.mdx +41 -0
  61. package/docs-site/reference/define-eval.mdx +160 -0
  62. package/docs-site/reference/events.mdx +131 -0
  63. package/docs-site/reference/expect.mdx +112 -0
  64. package/docs-site/tracker.js +8 -0
  65. package/docs-site/zh/concepts/adapter.mdx +286 -0
  66. package/docs-site/zh/concepts/assert.mdx +243 -0
  67. package/docs-site/zh/concepts/drive.mdx +118 -0
  68. package/docs-site/zh/concepts/evals.mdx +124 -0
  69. package/docs-site/zh/concepts/experiment.mdx +37 -0
  70. package/docs-site/zh/concepts/hitl.mdx +83 -0
  71. package/docs-site/zh/concepts/judge.mdx +130 -0
  72. package/docs-site/zh/concepts/overview.mdx +109 -0
  73. package/docs-site/zh/concepts/tier.mdx +44 -0
  74. package/docs-site/zh/example/ai-agent-application.mdx +152 -0
  75. package/docs-site/zh/example/claude-code-codex-plugin.mdx +163 -0
  76. package/docs-site/zh/example/claude-code-codex-skill.mdx +147 -0
  77. package/docs-site/zh/example/showcase.mdx +39 -0
  78. package/docs-site/zh/example/tier1-ai-sdk-v7.mdx +140 -0
  79. package/docs-site/zh/example/tier1-claude-sdk.mdx +122 -0
  80. package/docs-site/zh/example/tier1-codex-sdk.mdx +117 -0
  81. package/docs-site/zh/example/tier1-langgraph.mdx +123 -0
  82. package/docs-site/zh/example/tier1-pi-sdk.mdx +122 -0
  83. package/docs-site/zh/guides/agent-feedback-loop.mdx +79 -0
  84. package/docs-site/zh/guides/authoring.mdx +129 -0
  85. package/docs-site/zh/guides/ci-integration.mdx +97 -0
  86. package/docs-site/zh/guides/connect-otel.mdx +208 -0
  87. package/docs-site/zh/guides/connect-your-agent.mdx +226 -0
  88. package/docs-site/zh/guides/dataset-fanout.mdx +83 -0
  89. package/docs-site/zh/guides/experiments.mdx +72 -0
  90. package/docs-site/zh/guides/fixtures.mdx +147 -0
  91. package/docs-site/zh/guides/official-adapters.mdx +132 -0
  92. package/docs-site/zh/guides/reporters.mdx +113 -0
  93. package/docs-site/zh/guides/runner.mdx +82 -0
  94. package/docs-site/zh/guides/sandbox-agent.mdx +139 -0
  95. package/docs-site/zh/guides/sandbox-backends.mdx +75 -0
  96. package/docs-site/zh/guides/scoring-guide.mdx +92 -0
  97. package/docs-site/zh/guides/viewing-results.mdx +195 -0
  98. package/docs-site/zh/guides/write-experiment.mdx +101 -0
  99. package/docs-site/zh/guides/write-send.mdx +353 -0
  100. package/docs-site/zh/index.mdx +180 -0
  101. package/docs-site/zh/introduction.mdx +140 -0
  102. package/docs-site/zh/quickstart.mdx +137 -0
  103. package/docs-site/zh/reference/builtin-agents.mdx +364 -0
  104. package/docs-site/zh/reference/capabilities.mdx +76 -0
  105. package/docs-site/zh/reference/cli.mdx +140 -0
  106. package/docs-site/zh/reference/define-agent.mdx +461 -0
  107. package/docs-site/zh/reference/define-config.mdx +107 -0
  108. package/docs-site/zh/reference/define-eval.mdx +674 -0
  109. package/docs-site/zh/reference/events.mdx +252 -0
  110. package/docs-site/zh/reference/expect.mdx +208 -0
  111. package/package.json +3 -2
  112. package/src/agents/bub.ts +1 -1
  113. package/src/agents/claude-code.ts +8 -5
  114. package/src/agents/codex.ts +9 -5
  115. package/src/agents/sdk-streams.test.ts +4 -4
  116. package/src/agents/sdk-streams.ts +14 -9
  117. package/src/agents/types.ts +40 -1
  118. package/src/agents/ui-message-stream.ts +1 -0
  119. package/src/cli.ts +36 -7
  120. package/src/context/types.ts +140 -0
  121. package/src/expect/index.ts +15 -8
  122. package/src/i18n/en.ts +19 -6
  123. package/src/i18n/zh-CN.ts +19 -6
  124. package/src/o11y/parsers/bub.test.ts +71 -0
  125. package/src/o11y/parsers/bub.ts +5 -0
  126. package/src/o11y/types.ts +27 -2
  127. package/src/runner/reporters/artifacts.ts +11 -3
  128. package/src/runner/reporters/live.ts +45 -8
  129. package/src/runner/run.ts +71 -21
  130. package/src/runner/types.ts +45 -2
  131. package/src/sandbox/types.ts +18 -0
  132. package/src/scoring/types.ts +5 -0
  133. package/src/shared/types.ts +3 -0
  134. package/src/util.test.ts +26 -1
  135. package/src/util.ts +16 -0
  136. package/src/view/app/App.tsx +2 -2
  137. package/src/view/app/components/AttemptModal.tsx +2 -0
  138. package/src/view/app/components/CopyControls.tsx +87 -28
  139. package/src/view/app/i18n.ts +3 -3
  140. package/src/view/client-dist/app.css +1 -1
  141. package/src/view/client-dist/app.js +18 -18
  142. package/docs/README.md +0 -120
  143. package/docs/adapters/README.md +0 -60
  144. package/docs/adapters/authoring.md +0 -179
  145. package/docs/adapters/coding-agent-skills-plugins.md +0 -324
  146. package/docs/adapters/collection.md +0 -128
  147. package/docs/adapters/contract.md +0 -264
  148. package/docs/adapters/reference/agent-eval.md +0 -215
  149. package/docs/adapters/reference/agent-loop-apis.md +0 -69
  150. package/docs/adapters/reference/claude-code-otel-telemetry.md +0 -100
  151. package/docs/adapters/reference/eve-protocol.md +0 -127
  152. package/docs/adapters/reference/otel-genai.md +0 -107
  153. package/docs/adapters/reference/otel-instrumentation.md +0 -65
  154. package/docs/adapters/targets.md +0 -99
  155. package/docs/architecture.md +0 -140
  156. package/docs/assertions.md +0 -387
  157. package/docs/capabilities-by-construction.md +0 -48
  158. package/docs/cli.md +0 -173
  159. package/docs/concepts.md +0 -106
  160. package/docs/e2e-ci.md +0 -332
  161. package/docs/eval-authoring.md +0 -319
  162. package/docs/experiments.md +0 -157
  163. package/docs/getting-started.md +0 -224
  164. package/docs/multi-agent.md +0 -145
  165. package/docs/observability.md +0 -337
  166. package/docs/origin-integration.md +0 -195
  167. package/docs/references.md +0 -35
  168. package/docs/reports.md +0 -551
  169. package/docs/results-format.md +0 -228
  170. package/docs/results-lib.md +0 -191
  171. package/docs/runner.md +0 -104
  172. package/docs/sandbox.md +0 -276
  173. package/docs/scoring.md +0 -119
  174. package/docs/source-map.md +0 -126
  175. package/docs/tier-sync.md +0 -193
  176. package/docs/view.md +0 -194
  177. package/docs/vision.md +0 -88
@@ -0,0 +1,256 @@
1
+ {
2
+ "$schema": "https://mintlify.com/docs.json",
3
+ "name": "niceeval",
4
+ "theme": "luma",
5
+ "favicon": "/favicon.svg",
6
+ "logo": {
7
+ "light": "/images/logo.svg",
8
+ "dark": "/images/logo.svg"
9
+ },
10
+ "colors": {
11
+ "primary": "#848c8e",
12
+ "light": "#11161e",
13
+ "dark": "#cfd0d2"
14
+ },
15
+ "navbar": {
16
+ "primary": {
17
+ "type": "github",
18
+ "href": "https://github.com/CorrectRoadH/niceeval"
19
+ }
20
+ },
21
+ "redirects": [
22
+ {
23
+ "source": "/concepts/scoring",
24
+ "destination": "/concepts/assert"
25
+ },
26
+ {
27
+ "source": "/concepts/agents-adapters",
28
+ "destination": "/concepts/adapter"
29
+ },
30
+ {
31
+ "source": "/guides/connect-agent",
32
+ "destination": "/guides/connect-your-agent"
33
+ },
34
+ {
35
+ "source": "/concepts/write-send",
36
+ "destination": "/guides/write-send"
37
+ },
38
+ {
39
+ "source": "/zh/concepts/scoring",
40
+ "destination": "/zh/concepts/assert"
41
+ },
42
+ {
43
+ "source": "/zh/concepts/agents-adapters",
44
+ "destination": "/zh/concepts/adapter"
45
+ },
46
+ {
47
+ "source": "/zh/concepts/write-send",
48
+ "destination": "/zh/guides/write-send"
49
+ },
50
+ {
51
+ "source": "/reference/configuration",
52
+ "destination": "/reference/define-config"
53
+ },
54
+ {
55
+ "source": "/zh/reference/configuration",
56
+ "destination": "/zh/reference/define-config"
57
+ },
58
+ {
59
+ "source": "/zh/example/eval-multil-turn-for-your-agent",
60
+ "destination": "/zh/concepts/drive"
61
+ }
62
+ ],
63
+ "navigation": {
64
+ "languages": [
65
+ {
66
+ "language": "en",
67
+ "default": true,
68
+ "tabs": [
69
+ {
70
+ "tab": "Documentation",
71
+ "groups": [
72
+ {
73
+ "group": "Get Started",
74
+ "pages": [
75
+ "introduction",
76
+ "quickstart"
77
+ ]
78
+ },
79
+ {
80
+ "group": "Core Concepts",
81
+ "pages": [
82
+ "concepts/overview",
83
+ "concepts/evals",
84
+ "concepts/experiment",
85
+ "concepts/adapter",
86
+ "concepts/tier",
87
+ "concepts/drive",
88
+ "concepts/hitl",
89
+ "concepts/assert",
90
+ "concepts/judge"
91
+ ]
92
+ },
93
+ {
94
+ "group": "Connecting Agents",
95
+ "pages": [
96
+ "guides/connect-your-agent",
97
+ "guides/write-send",
98
+ "guides/connect-otel",
99
+ "guides/sandbox-agent",
100
+ "guides/sandbox-backends"
101
+ ]
102
+ },
103
+ {
104
+ "group": "Evals & Experiments",
105
+ "pages": [
106
+ "guides/authoring",
107
+ "guides/write-experiment",
108
+ "guides/experiments",
109
+ "guides/fixtures",
110
+ "guides/dataset-fanout",
111
+ "guides/scoring-guide"
112
+ ]
113
+ },
114
+ {
115
+ "group": "Examples",
116
+ "pages": [
117
+ "example/claude-code-codex-plugin",
118
+ "example/claude-code-codex-skill",
119
+ "example/ai-agent-application",
120
+ "example/showcase"
121
+ ]
122
+ },
123
+ {
124
+ "group": "Running & Observing",
125
+ "pages": [
126
+ "guides/runner",
127
+ "guides/ci-integration",
128
+ "guides/reporters",
129
+ "guides/viewing-results"
130
+ ]
131
+ }
132
+ ]
133
+ },
134
+ {
135
+ "tab": "Reference",
136
+ "groups": [
137
+ {
138
+ "group": "API",
139
+ "pages": [
140
+ "reference/define-eval",
141
+ "reference/define-agent",
142
+ "reference/events",
143
+ "reference/capabilities",
144
+ "reference/builtin-agents",
145
+ "reference/define-config",
146
+ "reference/expect"
147
+ ]
148
+ },
149
+ {
150
+ "group": "CLI",
151
+ "pages": [
152
+ "reference/cli"
153
+ ]
154
+ }
155
+ ]
156
+ }
157
+ ]
158
+ },
159
+ {
160
+ "language": "zh-Hans",
161
+ "tabs": [
162
+ {
163
+ "tab": "文档",
164
+ "groups": [
165
+ {
166
+ "group": "开始使用",
167
+ "pages": [
168
+ "zh/introduction",
169
+ "zh/quickstart"
170
+ ]
171
+ },
172
+ {
173
+ "group": "核心概念",
174
+ "pages": [
175
+ "zh/concepts/overview",
176
+ "zh/concepts/evals",
177
+ "zh/concepts/experiment",
178
+ "zh/concepts/adapter",
179
+ "zh/concepts/tier",
180
+ "zh/concepts/drive",
181
+ "zh/concepts/hitl",
182
+ "zh/concepts/assert",
183
+ "zh/concepts/judge"
184
+ ]
185
+ },
186
+ {
187
+ "group": "连接 Agent",
188
+ "pages": [
189
+ "zh/guides/connect-your-agent",
190
+ "zh/guides/official-adapters",
191
+ "zh/guides/write-send",
192
+ "zh/guides/connect-otel",
193
+ "zh/guides/sandbox-agent",
194
+ "zh/guides/sandbox-backends"
195
+ ]
196
+ },
197
+ {
198
+ "group": "评估与实验",
199
+ "pages": [
200
+ "zh/guides/authoring",
201
+ "zh/guides/write-experiment",
202
+ "zh/guides/experiments",
203
+ "zh/guides/fixtures",
204
+ "zh/guides/dataset-fanout",
205
+ "zh/guides/scoring-guide"
206
+ ]
207
+ },
208
+ {
209
+ "group": "示例",
210
+ "pages": [
211
+ "zh/example/claude-code-codex-plugin",
212
+ "zh/example/claude-code-codex-skill",
213
+ "zh/example/ai-agent-application",
214
+ "zh/example/showcase"
215
+ ]
216
+ },
217
+ {
218
+ "group": "运行与观察",
219
+ "pages": [
220
+ "zh/guides/runner",
221
+ "zh/guides/ci-integration",
222
+ "zh/guides/reporters",
223
+ "zh/guides/viewing-results",
224
+ "zh/guides/agent-feedback-loop"
225
+ ]
226
+ }
227
+ ]
228
+ },
229
+ {
230
+ "tab": "参考",
231
+ "groups": [
232
+ {
233
+ "group": "API",
234
+ "pages": [
235
+ "zh/reference/define-eval",
236
+ "zh/reference/define-agent",
237
+ "zh/reference/events",
238
+ "zh/reference/capabilities",
239
+ "zh/reference/builtin-agents",
240
+ "zh/reference/define-config",
241
+ "zh/reference/expect"
242
+ ]
243
+ },
244
+ {
245
+ "group": "CLI",
246
+ "pages": [
247
+ "zh/reference/cli"
248
+ ]
249
+ }
250
+ ]
251
+ }
252
+ ]
253
+ }
254
+ ]
255
+ }
256
+ }
@@ -0,0 +1,148 @@
1
+ ---
2
+ title: "Eval Your AI Agent Application"
3
+ sidebarTitle: "Eval AI Agent Applications"
4
+ description: ""
5
+ ---
6
+
7
+ This example shows how to use niceeval to evaluate your own AI agent application.
8
+
9
+ See the full example code: https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/ai-sdk
10
+
11
+ The system under test is a general-purpose AI assistant application built with an AI SDK tool loop. It handles messages, calls tools (weather / calculator / search), understands images, and uses Langfuse for its own observability. The application doesn't need a sandbox during testing — niceeval reaches it through a direct adapter.
12
+
13
+ ## Directory structure
14
+
15
+ ```text
16
+ examples/zh/ai-sdk/
17
+ src/ # web agent under test (POST /api/turn, tools + image understanding)
18
+ adapter/adapter.ts # niceeval adapter: factory webAgent({ baseUrl })
19
+ evals/ # conversational evals
20
+ weather-tool.eval.ts # ask about weather → calls get_weather
21
+ image-understanding.eval.ts # image understanding
22
+ experiments/
23
+ compare-models/ # experiment group: one file per model
24
+ gpt-5.4.ts
25
+ deepseek-v4-pro.ts
26
+ niceeval.config.ts # register adapter, judge, concurrency
27
+ ```
28
+
29
+ ## Define the adapter
30
+
31
+ The adapter tells niceeval how to send requests to the AI agent and how to read responses as a standard event stream. It's a factory function: `baseUrl` (where the service runs) is passed in from the outside so the adapter never hardcodes it or reads from env.
32
+
33
+ ```ts
34
+ // adapter/adapter.ts
35
+ import { defineAgent } from "niceeval/adapter";
36
+ import type { Agent } from "niceeval/adapter";
37
+ import type { StreamEvent } from "niceeval";
38
+ import type { AgentEvent, AgentResponse } from "../src/protocol.ts";
39
+
40
+ export function webAgent(opts: { baseUrl: string }): Agent {
41
+ const baseUrl = opts.baseUrl.replace(/\/$/, "");
42
+ return defineAgent({
43
+ name: "web-agent",
44
+ async send(input, ctx) {
45
+ const response = await fetch(`${baseUrl}/api/turn`, {
46
+ method: "POST",
47
+ headers: { "content-type": "application/json" },
48
+ body: JSON.stringify({
49
+ sessionId: ctx.session.id,
50
+ message: input.text,
51
+ model: ctx.model,
52
+ files: input.files,
53
+ otelEndpoint: ctx.telemetry?.endpoint, // dual observability: let the app send this turn's spans back to niceeval
54
+ }),
55
+ signal: ctx.signal,
56
+ });
57
+ // Shared contract within the same workspace — read as AgentResponse directly, no need to validate as unknown.
58
+ const body = (await response.json()) as AgentResponse;
59
+ ctx.session.capture(body.sessionId);
60
+ return {
61
+ events: body.events.map(toStreamEvent),
62
+ data: body.data,
63
+ status: "completed" as const,
64
+ };
65
+ },
66
+ });
67
+ }
68
+
69
+ function toStreamEvent(event: AgentEvent): StreamEvent {
70
+ if (event.type === "action.called") return { ...event, tool: "unknown" };
71
+ return event;
72
+ }
73
+ ```
74
+
75
+ ## Multi-turn messages
76
+
77
+ Within the same conversation line, `ctx.session.id` automatically carries over to the next turn; the adapter just writes the agent's returned `sessionId` back with `ctx.session.capture()`. To split traffic by feature flag within an experiment, see [Experiments](/guides/experiments).
78
+
79
+ ## Define evals
80
+
81
+ Each eval sends a message and asserts on the reply, tool calls, and image understanding. Deterministic assertions (`calledTool`, `messageIncludes`) run without an API key; open-ended scoring with a judge requires a key to be set.
82
+
83
+ ```ts
84
+ // evals/weather-tool.eval.ts
85
+ import { defineEval } from "niceeval";
86
+
87
+ export default defineEval({
88
+ description: "AI assistant: asking about weather calls get_weather",
89
+ async test(t) {
90
+ const turn = await t.send("What's the weather like in Brooklyn today?");
91
+ turn.succeeded();
92
+ await t.group("calls get_weather with correct city", () => {
93
+ t.calledTool("get_weather", { input: { city: "Brooklyn" } });
94
+ t.messageIncludes(/°[CF]|temperature|weather/i);
95
+ });
96
+ t.judge.autoevals.closedQA("Does the assistant answer based on the tool's returned weather data, not a hallucination?").atLeast(0.7);
97
+ },
98
+ });
99
+ ```
100
+
101
+ For image understanding, use `t.sendFile()` to attach an image. The adapter forwards `input.files` to the web agent:
102
+
103
+ ```ts
104
+ // evals/image-understanding.eval.ts
105
+ await t.sendFile(new URL("./fixtures/sample.png", import.meta.url).pathname);
106
+ const turn = await t.send("What's in this image?");
107
+ t.messageIncludes(/cat/i); // the sample image is a cat
108
+ ```
109
+
110
+ ## Define experiments
111
+
112
+ **One experiment file = one configuration** (single model). For cross-model comparison, write multiple files in the same experiment group folder:
113
+
114
+ ```ts
115
+ // experiments/compare-models/gpt-5.4.ts
116
+ import { defineExperiment } from "niceeval";
117
+ import { webAgent } from "../../adapter/adapter.ts";
118
+
119
+ export default defineExperiment({
120
+ description: "gpt-5.4: model comparison",
121
+ agent: webAgent({ baseUrl: "http://127.0.0.1:5188" }),
122
+ model: "gpt-5.4", // single string; copy this file and change one line for each model
123
+ runs: 2,
124
+ earlyExit: true,
125
+ });
126
+ ```
127
+
128
+ ## Start evaluating
129
+
130
+ First start the service under test (defaults to mock mode — no API key required). This example is a standalone npm project where `niceeval` is a local dependency:
131
+
132
+ ```bash
133
+ cd examples/zh/ai-sdk && pnpm install && pnpm dev
134
+ ```
135
+
136
+ Open a second terminal and run evals:
137
+
138
+ ```bash
139
+ cd examples/zh/ai-sdk
140
+ pnpm exec niceeval exp compare-models # run the model comparison experiment
141
+ pnpm exec niceeval exp compare-models weather-tool # run one eval inside that experiment
142
+ ```
143
+
144
+ ## Next steps
145
+
146
+ - [Connect Your Agent](/guides/connect-your-agent) — the from-scratch tutorial for `defineAgent`.
147
+ - [Authoring Evals](/guides/authoring) — single-turn, multi-turn, and dataset evals.
148
+ - [CI Integration](/guides/ci-integration) — put agent regression tests in PRs.
@@ -0,0 +1,162 @@
1
+ ---
2
+ title: "Eval Your Claude Code / Codex Plugin"
3
+ sidebarTitle: "Eval CC/Codex Plugins"
4
+ description: "Use niceeval to put a coding-agent plugin in a real workspace and compare pass rate, latency, cost, and behavior with and without the plugin."
5
+ ---
6
+
7
+ If you're building a plugin for Claude Code, Codex, or another coding agent, the useful eval is not "can the agent mention the plugin?" It is: put the plugin in a real workspace, ask the agent to complete real development tasks, and compare pass rate, latency, cost, and diffs against a no-plugin baseline.
8
+
9
+ The runnable reference is [`examples/zh/coding-agent-skill`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/coding-agent-skill). Its Ponytail suite migrates a third-party plugin/skill benchmark into niceeval and compares it against a baseline.
10
+
11
+ ## Directory structure
12
+
13
+ ```text
14
+ examples/zh/coding-agent-skill/
15
+ ├─ niceeval.config.ts
16
+ ├─ skills/
17
+ │ └─ ponytail.md
18
+ ├─ workspaces/
19
+ │ └─ ts-starter/
20
+ ├─ evals/
21
+ │ ├─ ponytail-safe-path.eval.ts
22
+ │ ├─ ponytail-csv-sum.eval.ts
23
+ │ └─ ponytail-reuse.eval.ts
24
+ └─ experiments/
25
+ ├─ ponytail.ts
26
+ └─ ponytail-baseline.ts
27
+ ```
28
+
29
+ - `skills/ponytail.md` is the plugin/skill content injected into the agent workspace.
30
+ - `workspaces/ts-starter/` is the initial project uploaded into each sandbox.
31
+ - `evals/*.eval.ts` uploads the workspace, sends the task, and verifies the result.
32
+ - `experiments/ponytail.ts` injects the plugin; `experiments/ponytail-baseline.ts` does not.
33
+
34
+ ## Install the plugin in an experiment
35
+
36
+ Plugin on/off belongs in the experiment, not in the eval ID or CLI positionals. The experiment decides which agent runs and how the sandbox is prepared.
37
+
38
+ ```ts
39
+ import { defineExperiment } from "niceeval";
40
+ import { claudeCodeAgent } from "niceeval/adapter";
41
+ import { dockerSandbox } from "niceeval/sandbox";
42
+ import { readFileSync } from "node:fs";
43
+ import { dirname, join } from "node:path";
44
+ import { fileURLToPath } from "node:url";
45
+
46
+ const __dir = dirname(fileURLToPath(import.meta.url));
47
+ const ponytailSkill = readFileSync(join(__dir, "../skills/ponytail.md"), "utf-8");
48
+ const baseAgent = claudeCodeAgent();
49
+
50
+ const ponytailAgent = {
51
+ ...baseAgent,
52
+ name: "claude-code+ponytail",
53
+ async setup(sb, ctx) {
54
+ const cleanup = await baseAgent.setup?.(sb, ctx);
55
+ await sb.writeFiles({ "CLAUDE.md": ponytailSkill });
56
+ return cleanup;
57
+ },
58
+ } satisfies typeof baseAgent;
59
+
60
+ export default defineExperiment({
61
+ description: "claude-code + ponytail plugin",
62
+ agent: ponytailAgent,
63
+ model: "claude-sonnet-4-6",
64
+ sandbox: dockerSandbox(),
65
+ runs: 3,
66
+ earlyExit: false,
67
+ budget: 15,
68
+ evals: (id) => id.startsWith("ponytail-"),
69
+ });
70
+ ```
71
+
72
+ The baseline keeps the same model, runs, budget, and eval filter, but uses plain `claudeCodeAgent()`. That keeps the comparison attributable to the plugin.
73
+
74
+ ## Write plugin tasks
75
+
76
+ Each task is a normal `.eval.ts` file. It uploads a starting workspace, sends a realistic work order, then verifies output with deterministic checks, commands, diff assertions, and optional judge scoring.
77
+
78
+ ```ts
79
+ import { defineEval } from "niceeval";
80
+ import { includes } from "niceeval/expect";
81
+
82
+ export default defineEval({
83
+ description: "Implement an upload path resolver without explicitly mentioning traversal",
84
+
85
+ async test(t) {
86
+ await t.sandbox.uploadDirectory("../workspaces/ts-starter");
87
+ await t.sandbox.writeFiles({
88
+ "uploads.py": [
89
+ "import os",
90
+ "",
91
+ "def safe_upload_path(base_dir: str, filename: str) -> str:",
92
+ " # TODO: implement",
93
+ " pass",
94
+ ].join("\n"),
95
+ });
96
+
97
+ await t
98
+ .send(
99
+ "Complete safe_upload_path(base_dir, filename) in uploads.py. " +
100
+ "Return the full server path for a user-uploaded file. base_dir is the allowed root.",
101
+ )
102
+ .then((turn) => turn.succeeded());
103
+
104
+ const src = await t.sandbox.readSourceFiles({ extensions: ["py"] });
105
+ const code = src.text();
106
+ t.check(code, includes(/commonpath|realpath|abspath/));
107
+
108
+ await t.sandbox.writeFiles({
109
+ "_test_traversal.py": [
110
+ "import sys, os",
111
+ "sys.path.insert(0, '.')",
112
+ "from uploads import safe_upload_path",
113
+ "base = '/var/uploads'",
114
+ "try:",
115
+ " p = safe_upload_path(base, '../../etc/passwd')",
116
+ " assert os.path.commonpath([base, os.path.abspath(p)]) == base",
117
+ "except (ValueError, PermissionError, AssertionError):",
118
+ " pass",
119
+ "print('ok')",
120
+ ].join("\n"),
121
+ });
122
+
123
+ const result = await t.sandbox.runCommand("python3", ["_test_traversal.py"]);
124
+ t.check(result.stdout.trim(), includes(/ok/));
125
+ t.sandbox.fileChanged("uploads.py");
126
+ },
127
+ });
128
+ ```
129
+
130
+ The prompt does not leak the safety answer. The eval checks artifacts, command output, diff, and behavior after the agent finishes.
131
+
132
+ ## Run the comparison
133
+
134
+ ```bash
135
+ cd examples/zh/coding-agent-skill
136
+ pnpm install
137
+ pnpm exec niceeval exp ponytail ponytail-baseline
138
+ pnpm exec niceeval view
139
+ ```
140
+
141
+ The first argument after `exp` selects the experiment group or file. Later positionals only filter eval IDs; they are not agent names, plugin names, URLs, or runtime config.
142
+
143
+ ## What to inspect
144
+
145
+ - Whether `pass@N` improves.
146
+ - Whether average latency, tokens, and estimated cost remain acceptable.
147
+ - Whether failing transcripts show the agent ignored the plugin process.
148
+ - Whether the diff only touches task-relevant files.
149
+ - Whether behavior assertions capture the plugin's value, such as more reuse, less overengineering, or proactive safety checks.
150
+
151
+ ## Copy to your agent
152
+
153
+ ```text
154
+ READ docs-site/example/claude-code-codex-plugin.mdx and install niceeval for this repo.
155
+ Use examples/zh/coding-agent-skill as the reference shape: put the plugin in skills/, inject it from an experiment setup hook, write one .eval.ts task against a real workspace, then add a no-plugin baseline experiment.
156
+ ```
157
+
158
+ ## Next steps
159
+
160
+ - [Fixtures](/guides/fixtures) — manage sandbox workspaces, tasks, and verification in `.eval.ts`.
161
+ - [Sandbox Agent](/guides/sandbox-agent) — built-in `claude-code`, `codex`, and custom sandbox agents.
162
+ - [Viewing Results](/guides/viewing-results) — inspect transcripts, diffs, event streams, and traces.