@cursor/july 0.1.15 → 0.1.17
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.
- package/README.md +9 -4
- package/dist/bin/agent-serve.js +60 -2
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +13 -3
- package/dist/channels/slack/auth.d.ts +7 -1
- package/dist/channels/slack/auth.d.ts.map +1 -1
- package/dist/channels/slack/auth.js +14 -0
- package/dist/channels/slack/blocks.d.ts +61 -0
- package/dist/channels/slack/blocks.d.ts.map +1 -0
- package/dist/channels/slack/blocks.js +90 -0
- package/dist/channels/slack/defaults.d.ts +2 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +29 -3
- package/dist/channels/slack/dispatch.d.ts +3 -17
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/interactive.d.ts +39 -3
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +152 -7
- package/dist/channels/slack/manifest.d.ts +3 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/setup.d.ts +4 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +16 -2
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +24 -3
- package/dist/channels/slack/types.d.ts +77 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/connections.d.ts +4 -1
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +4 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.DpdGZTLe.js → app.BbEXr2KO.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DCBsrfwP.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.IB-0QqIT.js → VPLocalSearchBox.DBEi4HDp.js} +1 -1
- package/dist/docs/assets/chunks/{theme.-fYBoNtR.js → theme.DGfptcHw.js} +2 -2
- package/dist/docs/assets/{example-agents_index.md.BPXjUtRB.js → example-agents_index.md.D2PEVSXl.js} +2 -2
- package/dist/docs/assets/{example-agents_index.md.BPXjUtRB.lean.js → example-agents_index.md.D2PEVSXl.lean.js} +1 -1
- package/dist/docs/assets/example-agents_security-reviewer.md.BZSNycMf.js +19 -0
- package/dist/docs/assets/{example-agents_security-reviewer.md.CE9v-aKm.lean.js → example-agents_security-reviewer.md.BZSNycMf.lean.js} +1 -1
- package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.js → guides_mcp-oauth.md.Dd8EgSem.js} +3 -2
- package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.lean.js → guides_mcp-oauth.md.Dd8EgSem.lean.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.FWale6Ip.js → guides_slack.md.D_xFKlnF.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.CsSr3rDz.js → reference_connections.md.B9Q3TOve.js} +12 -5
- package/dist/docs/assets/{reference_connections.md.CsSr3rDz.lean.js → reference_connections.md.B9Q3TOve.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +5 -5
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +14 -14
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +6 -5
- package/dist/docs/guides/slack.html +5 -5
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +14 -7
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/evals/assertions.d.ts +190 -0
- package/dist/evals/assertions.d.ts.map +1 -0
- package/dist/evals/assertions.js +461 -0
- package/dist/evals/expect.d.ts +95 -0
- package/dist/evals/expect.d.ts.map +1 -0
- package/dist/evals/expect.js +194 -0
- package/dist/evals/judge.d.ts +133 -0
- package/dist/evals/judge.d.ts.map +1 -0
- package/dist/evals/judge.js +228 -0
- package/dist/evals/loaders.d.ts +51 -0
- package/dist/evals/loaders.d.ts.map +1 -0
- package/dist/evals/loaders.js +124 -0
- package/dist/evals/matchers.d.ts +95 -0
- package/dist/evals/matchers.d.ts.map +1 -0
- package/dist/evals/matchers.js +145 -0
- package/dist/evals/reporters.d.ts +70 -0
- package/dist/evals/reporters.d.ts.map +1 -0
- package/dist/evals/reporters.js +221 -0
- package/dist/evals/results.d.ts +103 -0
- package/dist/evals/results.d.ts.map +1 -0
- package/dist/evals/results.js +28 -0
- package/dist/evals/run-facts.d.ts +77 -0
- package/dist/evals/run-facts.d.ts.map +1 -0
- package/dist/evals/run-facts.js +187 -0
- package/dist/evals.d.ts +136 -31
- package/dist/evals.d.ts.map +1 -1
- package/dist/evals.js +76 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/cli-ax.d.ts +27 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +90 -12
- package/dist/internal/cli-mcp.d.ts +8 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -1
- package/dist/internal/cli-mcp.js +98 -0
- package/dist/internal/cli-slack.d.ts +29 -1
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +777 -12
- package/dist/internal/deploy-client.d.ts +7 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +9 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +29 -9
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +2 -0
- package/dist/internal/eval-judge-model.d.ts +59 -0
- package/dist/internal/eval-judge-model.d.ts.map +1 -0
- package/dist/internal/eval-judge-model.js +131 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +12 -20
- package/dist/internal/eval-runner.d.ts +31 -25
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +332 -128
- package/dist/internal/evals-client.d.ts.map +1 -1
- package/dist/internal/evals-client.js +7 -3
- package/dist/internal/local-env.d.ts +8 -0
- package/dist/internal/local-env.d.ts.map +1 -0
- package/dist/internal/local-env.js +47 -0
- package/dist/internal/playground/static.d.ts +3 -0
- package/dist/internal/playground/static.d.ts.map +1 -1
- package/dist/internal/playground/static.js +3 -0
- package/dist/internal/run-client.d.ts +15 -0
- package/dist/internal/run-client.d.ts.map +1 -1
- package/dist/internal/run-client.js +3 -2
- package/dist/internal/sdk-runner.d.ts +36 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +65 -25
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +10 -1
- package/dist/internal/session-engine.d.ts +18 -5
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +72 -19
- package/dist/internal/slack-provision-client.d.ts +130 -0
- package/dist/internal/slack-provision-client.d.ts.map +1 -0
- package/dist/internal/slack-provision-client.js +284 -0
- package/dist/playground/assets/cursor-icons-16-f_W_ogc-.woff2 +0 -0
- package/dist/playground/assets/index-CidizGZv.css +1 -0
- package/dist/playground/assets/index-DNqirsrK.js +85 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +70 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +26 -0
- package/docs/example-agents/index.md +1 -1
- package/docs/example-agents/security-reviewer.md +23 -21
- package/docs/guides/mcp-oauth.md +10 -5
- package/docs/guides/slack.md +3 -2
- package/docs/reference/connections.md +28 -18
- package/package.json +20 -2
- package/skills/create-agent/SKILL.md +15 -7
- package/skills/debug/SKILL.md +3 -1
- package/skills/mcp-auth/SKILL.md +1 -1
- package/skills/setup-slack/SKILL.md +173 -11
- package/src/bin/agent-serve.ts +63 -2
- package/src/channels/slack/api.ts +16 -3
- package/src/channels/slack/auth.ts +16 -1
- package/src/channels/slack/blocks.ts +139 -0
- package/src/channels/slack/defaults.ts +32 -4
- package/src/channels/slack/dispatch.ts +3 -18
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/interactive.ts +183 -19
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/setup.ts +16 -2
- package/src/channels/slack/slack-channel.ts +37 -6
- package/src/channels/slack/types.ts +89 -0
- package/src/connections.ts +4 -1
- package/src/evals/assertions.ts +704 -0
- package/src/evals/expect.ts +272 -0
- package/src/evals/judge.ts +346 -0
- package/src/evals/loaders.ts +113 -0
- package/src/evals/matchers.ts +246 -0
- package/src/evals/reporters.ts +274 -0
- package/src/evals/results.ts +132 -0
- package/src/evals/run-facts.ts +274 -0
- package/src/evals.ts +249 -42
- package/src/index.ts +2 -0
- package/src/internal/cli-ax.ts +134 -15
- package/src/internal/cli-mcp.ts +118 -5
- package/src/internal/cli-slack.ts +993 -15
- package/src/internal/deploy-client.ts +10 -0
- package/src/internal/discovery.ts +31 -5
- package/src/internal/distribution.ts +2 -0
- package/src/internal/eval-judge-model.ts +148 -0
- package/src/internal/eval-run-store.ts +10 -14
- package/src/internal/eval-runner.ts +466 -215
- package/src/internal/evals-client.ts +14 -2
- package/src/internal/local-env.ts +53 -0
- package/src/internal/playground/static.ts +3 -0
- package/src/internal/run-client.ts +17 -1
- package/src/internal/sdk-runner.ts +103 -22
- package/src/internal/server.ts +10 -0
- package/src/internal/session-engine.ts +89 -21
- package/src/internal/slack-provision-client.ts +438 -0
- package/src/types.ts +92 -4
- package/dist/channels/slack/post-update-delivery.d.ts +0 -85
- package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.D2d8CfFS.js +0 -1
- package/dist/docs/assets/example-agents_security-reviewer.md.CE9v-aKm.js +0 -19
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/persistence-coordinator.d.ts +0 -127
- package/dist/internal/persistence-coordinator.d.ts.map +0 -1
- package/dist/persistence.d.ts +0 -184
- package/dist/persistence.d.ts.map +0 -1
- package/dist/playground/assets/cursor-icons-16-CQ50JpfO.woff2 +0 -0
- package/dist/playground/assets/index-BVMOoiek.js +0 -86
- package/dist/playground/assets/index-DYmP4b--.css +0 -1
- /package/dist/docs/assets/{guides_slack.md.FWale6Ip.lean.js → guides_slack.md.D_xFKlnF.lean.js} +0 -0
package/src/evals.ts
CHANGED
|
@@ -44,48 +44,116 @@
|
|
|
44
44
|
* ],
|
|
45
45
|
* });
|
|
46
46
|
* ```
|
|
47
|
+
*
|
|
48
|
+
* ## Severity: gates and scores
|
|
49
|
+
*
|
|
50
|
+
* Every assertion returns a chainable handle, so severity rides on the
|
|
51
|
+
* assertion instead of a separate thresholds map. Gates are hard (a miss fails
|
|
52
|
+
* the eval and `eval` exits non-zero); soft assertions are tracked scores that
|
|
53
|
+
* mark the eval `scored` and only fail the build under `--strict`:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* t.succeeded(); // gate (default)
|
|
57
|
+
* t.calledTool("get_weather").soft(); // tracked, no gate
|
|
58
|
+
* t.check(t.reply, similarity("Sunny, 72F")).atLeast(0.8);
|
|
59
|
+
* t.judge.closedQA("cites a source").atLeast(0.6); // LLM-graded
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* Matchers narrow tool assertions (`{ input, output, status, count }` accept a
|
|
63
|
+
* literal, a RegExp, or a predicate), and each turn returned by `t.send(...)`
|
|
64
|
+
* carries the same vocabulary scoped to that turn.
|
|
47
65
|
*/
|
|
48
66
|
|
|
67
|
+
import type {
|
|
68
|
+
EvalAssertionResult,
|
|
69
|
+
EvalAssertions,
|
|
70
|
+
EvalVerdict,
|
|
71
|
+
} from "./evals/assertions.js";
|
|
72
|
+
import type { Expectation } from "./evals/expect.js";
|
|
73
|
+
import type { EvalJudge, EvalJudgeConfig } from "./evals/judge.js";
|
|
74
|
+
import type { EvalReporter } from "./evals/results.js";
|
|
49
75
|
import { normalizePositiveInt } from "./internal/bounded-int.js";
|
|
50
76
|
import { brandDefinition } from "./internal/brand.js";
|
|
51
77
|
import type { AgentCloudOptions, SessionEvent } from "./types.js";
|
|
52
78
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
// Assertion severity, the scoped assertion vocabulary, value assertion
|
|
80
|
+
// builders, the judge surface, and result/reporter types are all part of the
|
|
81
|
+
// authoring surface: one import (`@cursor/july/evals`) covers a whole eval file.
|
|
82
|
+
export type {
|
|
83
|
+
EvalAssertionHandle,
|
|
84
|
+
EvalAssertionResult,
|
|
85
|
+
EvalAssertionSeverity,
|
|
86
|
+
EvalAssertions,
|
|
87
|
+
EvalVerdict,
|
|
88
|
+
} from "./evals/assertions.js";
|
|
89
|
+
export { computeVerdict, verdictFailsRun } from "./evals/assertions.js";
|
|
90
|
+
export type { EvalSchemaLike, Expectation } from "./evals/expect.js";
|
|
91
|
+
export {
|
|
92
|
+
equals,
|
|
93
|
+
includes,
|
|
94
|
+
matches,
|
|
95
|
+
normalizedSimilarity,
|
|
96
|
+
satisfies,
|
|
97
|
+
similarity,
|
|
98
|
+
} from "./evals/expect.js";
|
|
99
|
+
export type {
|
|
100
|
+
EvalJudge,
|
|
101
|
+
EvalJudgeCallOptions,
|
|
102
|
+
EvalJudgeConfig,
|
|
103
|
+
EvalJudgeGraderName,
|
|
104
|
+
} from "./evals/judge.js";
|
|
105
|
+
// A custom judge built on `t.judge.model` embeds the same attacker-controlled
|
|
106
|
+
// agent output the built-in graders do, so it needs the same two protections.
|
|
107
|
+
export {
|
|
108
|
+
EVAL_JUDGE_INJECTION_GUARD,
|
|
109
|
+
fenceUntrusted,
|
|
110
|
+
} from "./evals/judge.js";
|
|
111
|
+
export type {
|
|
112
|
+
EvalCountMatcher,
|
|
113
|
+
EvalEventMatcher,
|
|
114
|
+
EvalEventOrderMatcher,
|
|
115
|
+
EvalMatcher,
|
|
116
|
+
EvalSubagentMatcher,
|
|
117
|
+
EvalToolCallMatcher,
|
|
118
|
+
EvalToolCallStatus,
|
|
119
|
+
} from "./evals/matchers.js";
|
|
120
|
+
export type {
|
|
121
|
+
EvalReporter,
|
|
122
|
+
EvalReporterEvalInfo,
|
|
123
|
+
EvalReporterTarget,
|
|
124
|
+
EvalRunResult,
|
|
125
|
+
EvalRunSummary,
|
|
126
|
+
} from "./evals/results.js";
|
|
127
|
+
export type {
|
|
128
|
+
EvalInputRequestFact,
|
|
129
|
+
EvalRunFacts,
|
|
130
|
+
EvalSubagentFact,
|
|
131
|
+
EvalToolCallFact,
|
|
132
|
+
} from "./evals/run-facts.js";
|
|
133
|
+
export { deriveRunFacts } from "./evals/run-facts.js";
|
|
82
134
|
|
|
83
|
-
|
|
135
|
+
/**
|
|
136
|
+
* One settled turn. Carries the same assertion vocabulary as `t`, narrowed to
|
|
137
|
+
* this turn's events, so an intermediate turn can be graded before the next
|
|
138
|
+
* `send` overwrites `t.reply`:
|
|
139
|
+
*
|
|
140
|
+
* ```ts
|
|
141
|
+
* const draft = await t.send("Draft the summary.");
|
|
142
|
+
* draft.calledTool("read_file");
|
|
143
|
+
* t.check(draft.message, includes("Summary"));
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export interface EvalTurnResult extends EvalAssertions {
|
|
84
147
|
message: string;
|
|
85
148
|
sessionId: string;
|
|
86
149
|
events: SessionEvent[];
|
|
150
|
+
/** Tool names requested during this turn, in order. */
|
|
87
151
|
toolCalls: string[];
|
|
88
152
|
ok: boolean;
|
|
153
|
+
/** 1-based turn index within the case. */
|
|
154
|
+
index: number;
|
|
155
|
+
/** Throws when this turn failed. Only needed when later steps depend on it. */
|
|
156
|
+
expectOk(): EvalTurnResult;
|
|
89
157
|
}
|
|
90
158
|
|
|
91
159
|
export interface EvalSendOptions {
|
|
@@ -101,7 +169,15 @@ export interface EvalSendOptions {
|
|
|
101
169
|
cloud?: AgentCloudOptions;
|
|
102
170
|
}
|
|
103
171
|
|
|
104
|
-
|
|
172
|
+
/**
|
|
173
|
+
* The eval driver and assertion surface.
|
|
174
|
+
*
|
|
175
|
+
* `t` both drives the agent and grades it. Assertions on `t` read the whole run
|
|
176
|
+
* once `test` finishes; assertions on the turn returned by `t.send(...)` read
|
|
177
|
+
* only that turn. Recorded assertions never throw, so one run reports every
|
|
178
|
+
* failure rather than dying on the first.
|
|
179
|
+
*/
|
|
180
|
+
export interface EvalTestContext extends EvalAssertions {
|
|
105
181
|
/** Send a turn and wait until the session parks (or fails). */
|
|
106
182
|
send(message: string, options?: EvalSendOptions): Promise<EvalTurnResult>;
|
|
107
183
|
/** Last assistant text, or null before the first send. */
|
|
@@ -109,6 +185,8 @@ export interface EvalTestContext {
|
|
|
109
185
|
readonly sessionId: string | undefined;
|
|
110
186
|
/** All events captured so far across turns. */
|
|
111
187
|
readonly events: SessionEvent[];
|
|
188
|
+
/** Turns settled so far, oldest first. */
|
|
189
|
+
readonly turns: readonly EvalTurnResult[];
|
|
112
190
|
/**
|
|
113
191
|
* 1-based index of this run when {@link EvalConfig.iterations} /
|
|
114
192
|
* {@link EvalDatapoint.iterations} expands a datapoint. Always `1` when
|
|
@@ -117,16 +195,25 @@ export interface EvalTestContext {
|
|
|
117
195
|
readonly iteration: number;
|
|
118
196
|
/** Total iterations for this datapoint (default `1`). */
|
|
119
197
|
readonly iterations: number;
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
|
|
198
|
+
/** Aborts when the case hits its timeout. */
|
|
199
|
+
readonly signal: AbortSignal;
|
|
200
|
+
/**
|
|
201
|
+
* LLM-as-judge assertions. Soft by default (tracked, never fatal); add a bar
|
|
202
|
+
* with `.atLeast(0.7)` or promote with `.gate(0.8)`. Requires a judge model
|
|
203
|
+
* from `defineEvalConfig({ judge })`, `defineEval({ judge })`, or the call.
|
|
204
|
+
*/
|
|
205
|
+
readonly judge: EvalJudge;
|
|
206
|
+
/**
|
|
207
|
+
* Record a gate that must pass before the script can safely continue.
|
|
208
|
+
* Returns `value` when it passes and stops the test body when it does not,
|
|
209
|
+
* without adding a duplicate execution error.
|
|
210
|
+
*/
|
|
211
|
+
require<T>(value: T, assertion: Expectation): Promise<T>;
|
|
212
|
+
/**
|
|
213
|
+
* End the case as skipped. Skipped cases are reported separately and never
|
|
214
|
+
* change the exit code. Call it before sending messages.
|
|
215
|
+
*/
|
|
216
|
+
skip(reason: string): never;
|
|
130
217
|
/** Debug line (surfaced under `agent-serve eval --verbose`). */
|
|
131
218
|
log(message: string): void;
|
|
132
219
|
/**
|
|
@@ -158,6 +245,12 @@ export interface EvalDatapoint {
|
|
|
158
245
|
* concurrent case.
|
|
159
246
|
*/
|
|
160
247
|
iterations?: number;
|
|
248
|
+
/** Free-form data carried onto the result and every reporter. */
|
|
249
|
+
metadata?: Record<string, unknown>;
|
|
250
|
+
/** Judge model for this datapoint (overrides `evals.config.ts`). */
|
|
251
|
+
judge?: EvalJudgeConfig;
|
|
252
|
+
/** Destinations for just this datapoint, in addition to config reporters. */
|
|
253
|
+
reporters?: EvalReporter[];
|
|
161
254
|
test: (t: EvalTestContext) => Promise<void>;
|
|
162
255
|
}
|
|
163
256
|
|
|
@@ -174,6 +267,12 @@ export interface EvalConfig {
|
|
|
174
267
|
* Case-level {@link EvalDatapoint.iterations} overrides this.
|
|
175
268
|
*/
|
|
176
269
|
iterations?: number;
|
|
270
|
+
/** Free-form data carried onto every result in this file and to reporters. */
|
|
271
|
+
metadata?: Record<string, unknown>;
|
|
272
|
+
/** Judge model for this file (overrides `evals.config.ts`). */
|
|
273
|
+
judge?: EvalJudgeConfig;
|
|
274
|
+
/** Destinations for this file, in addition to config reporters. */
|
|
275
|
+
reporters?: EvalReporter[];
|
|
177
276
|
/** Single-datapoint shorthand. Case id = file path under `evals/`. */
|
|
178
277
|
test?: (t: EvalTestContext) => Promise<void>;
|
|
179
278
|
/** Multiple datapoints in this file. Each id = `<fileId>/<case.id>`. */
|
|
@@ -189,6 +288,9 @@ export interface EvalCaseDefinition {
|
|
|
189
288
|
iterations?: number;
|
|
190
289
|
/** 1-based iteration index for this expanded runnable case. */
|
|
191
290
|
iteration?: number;
|
|
291
|
+
metadata?: Record<string, unknown>;
|
|
292
|
+
judge?: EvalJudgeConfig;
|
|
293
|
+
reporters?: EvalReporter[];
|
|
192
294
|
test: (t: EvalTestContext) => Promise<void>;
|
|
193
295
|
readonly __agentServe: "eval";
|
|
194
296
|
}
|
|
@@ -275,12 +377,19 @@ export function expandEvalDefinition(
|
|
|
275
377
|
const iterations = normalizeEvalIterations(
|
|
276
378
|
datapoint.iterations ?? definition.iterations ?? 1
|
|
277
379
|
);
|
|
380
|
+
const metadata =
|
|
381
|
+
datapoint.metadata === undefined && definition.metadata === undefined
|
|
382
|
+
? undefined
|
|
383
|
+
: { ...definition.metadata, ...datapoint.metadata };
|
|
278
384
|
return expandIterations({
|
|
279
385
|
baseId: `${fileId}/${datapoint.id}`,
|
|
280
386
|
description,
|
|
281
387
|
tags: datapoint.tags ?? definition.tags,
|
|
282
388
|
timeoutMs,
|
|
283
389
|
iterations,
|
|
390
|
+
metadata,
|
|
391
|
+
judge: datapoint.judge ?? definition.judge,
|
|
392
|
+
reporters: mergeReporters(definition.reporters, datapoint.reporters),
|
|
284
393
|
test: datapoint.test,
|
|
285
394
|
});
|
|
286
395
|
});
|
|
@@ -295,16 +404,36 @@ export function expandEvalDefinition(
|
|
|
295
404
|
tags: definition.tags,
|
|
296
405
|
timeoutMs: definition.timeoutMs,
|
|
297
406
|
iterations,
|
|
407
|
+
metadata: definition.metadata,
|
|
408
|
+
judge: definition.judge,
|
|
409
|
+
reporters: definition.reporters,
|
|
298
410
|
test: definition.test,
|
|
299
411
|
});
|
|
300
412
|
}
|
|
301
413
|
|
|
414
|
+
/** Union file-level and case-level reporters, keeping each instance once. */
|
|
415
|
+
function mergeReporters(
|
|
416
|
+
fileLevel: EvalReporter[] | undefined,
|
|
417
|
+
caseLevel: EvalReporter[] | undefined
|
|
418
|
+
): EvalReporter[] | undefined {
|
|
419
|
+
if (fileLevel === undefined) {
|
|
420
|
+
return caseLevel;
|
|
421
|
+
}
|
|
422
|
+
if (caseLevel === undefined) {
|
|
423
|
+
return fileLevel;
|
|
424
|
+
}
|
|
425
|
+
return [...new Set([...fileLevel, ...caseLevel])];
|
|
426
|
+
}
|
|
427
|
+
|
|
302
428
|
function expandIterations(args: {
|
|
303
429
|
baseId: string;
|
|
304
430
|
description?: string;
|
|
305
431
|
tags?: string[];
|
|
306
432
|
timeoutMs?: number;
|
|
307
433
|
iterations: number;
|
|
434
|
+
metadata?: Record<string, unknown>;
|
|
435
|
+
judge?: EvalJudgeConfig;
|
|
436
|
+
reporters?: EvalReporter[];
|
|
308
437
|
test: (t: EvalTestContext) => Promise<void>;
|
|
309
438
|
}): Array<{ id: string; definition: EvalCaseDefinition }> {
|
|
310
439
|
const out: Array<{ id: string; definition: EvalCaseDefinition }> = [];
|
|
@@ -321,6 +450,9 @@ function expandIterations(args: {
|
|
|
321
450
|
...(description !== undefined ? { description } : {}),
|
|
322
451
|
...(args.tags !== undefined ? { tags: args.tags } : {}),
|
|
323
452
|
...(args.timeoutMs !== undefined ? { timeoutMs: args.timeoutMs } : {}),
|
|
453
|
+
metadata: args.metadata,
|
|
454
|
+
judge: args.judge,
|
|
455
|
+
reporters: args.reporters,
|
|
324
456
|
iterations: args.iterations,
|
|
325
457
|
iteration,
|
|
326
458
|
test: args.test,
|
|
@@ -340,6 +472,50 @@ export function matchesEvalIdFilter(caseId: string, filter: string): boolean {
|
|
|
340
472
|
return caseId === filter || caseId.startsWith(`${filter}/`);
|
|
341
473
|
}
|
|
342
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Tag for suite-aggregator datapoints that wait on sibling cases from the
|
|
477
|
+
* same eval file (e.g. `deepsec-recall/overall`). Selecting a summary case
|
|
478
|
+
* alone expands the batch to the whole file via
|
|
479
|
+
* {@link expandSummarySuiteSelection}.
|
|
480
|
+
*/
|
|
481
|
+
export const EVAL_SUMMARY_TAG = "summary";
|
|
482
|
+
|
|
483
|
+
/** True when `tags` marks a suite-aggregator case. */
|
|
484
|
+
export function isEvalSummaryCase(
|
|
485
|
+
tags: readonly string[] | undefined
|
|
486
|
+
): boolean {
|
|
487
|
+
return tags?.includes(EVAL_SUMMARY_TAG) ?? false;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* If `selected` includes any summary-tagged suite aggregator, pull in every
|
|
492
|
+
* other datapoint from the same `fileId` so the aggregator is not run alone.
|
|
493
|
+
* Preserves discovery order from `all`.
|
|
494
|
+
*/
|
|
495
|
+
export function expandSummarySuiteSelection<
|
|
496
|
+
T extends { id: string; fileId: string },
|
|
497
|
+
>(
|
|
498
|
+
all: readonly T[],
|
|
499
|
+
selected: readonly T[],
|
|
500
|
+
isSummary: (item: T) => boolean
|
|
501
|
+
): T[] {
|
|
502
|
+
const fileIdsNeedingSuite = new Set(
|
|
503
|
+
selected.filter((item) => isSummary(item)).map((item) => item.fileId)
|
|
504
|
+
);
|
|
505
|
+
if (fileIdsNeedingSuite.size === 0) {
|
|
506
|
+
return [...selected];
|
|
507
|
+
}
|
|
508
|
+
const selectedIds = new Set(selected.map((item) => item.id));
|
|
509
|
+
const extras = all.filter(
|
|
510
|
+
(item) => fileIdsNeedingSuite.has(item.fileId) && !selectedIds.has(item.id)
|
|
511
|
+
);
|
|
512
|
+
if (extras.length === 0) {
|
|
513
|
+
return [...selected];
|
|
514
|
+
}
|
|
515
|
+
const want = new Set([...selectedIds, ...extras.map((item) => item.id)]);
|
|
516
|
+
return all.filter((item) => want.has(item.id));
|
|
517
|
+
}
|
|
518
|
+
|
|
343
519
|
/**
|
|
344
520
|
* Hard ceiling for concurrent eval cases.
|
|
345
521
|
*
|
|
@@ -365,6 +541,10 @@ export interface EvalCaseSnapshot {
|
|
|
365
541
|
tags?: string[];
|
|
366
542
|
status: EvalCaseRunStatus;
|
|
367
543
|
ok?: boolean;
|
|
544
|
+
/** Full grade, including `scored` (soft threshold miss) and `skipped`. */
|
|
545
|
+
verdict?: EvalVerdict;
|
|
546
|
+
/** Reason passed to `t.skip(...)`. */
|
|
547
|
+
skipReason?: string;
|
|
368
548
|
assertions?: EvalAssertionResult[];
|
|
369
549
|
sessionId?: string;
|
|
370
550
|
durationMs?: number;
|
|
@@ -372,6 +552,8 @@ export interface EvalCaseSnapshot {
|
|
|
372
552
|
logs?: string[];
|
|
373
553
|
/** Structured scores from {@link EvalTestContext.metric}. */
|
|
374
554
|
metrics?: Record<string, string | number>;
|
|
555
|
+
/** Author-supplied metadata from `defineEval({ metadata })`. */
|
|
556
|
+
metadata?: Record<string, unknown>;
|
|
375
557
|
inputs?: string[];
|
|
376
558
|
finalText?: string;
|
|
377
559
|
tools?: string[];
|
|
@@ -390,7 +572,19 @@ export interface EvalRunSnapshot {
|
|
|
390
572
|
startedAt: string;
|
|
391
573
|
finishedAt?: string;
|
|
392
574
|
error?: string;
|
|
393
|
-
summary: {
|
|
575
|
+
summary: {
|
|
576
|
+
passed: number;
|
|
577
|
+
failed: number;
|
|
578
|
+
/**
|
|
579
|
+
* Cases that only missed a soft threshold (fatal only under `--strict`).
|
|
580
|
+
* Optional: a batch persisted by an older serve process has no such count.
|
|
581
|
+
*/
|
|
582
|
+
scored?: number;
|
|
583
|
+
/** Cases ended by `t.skip(...)`; they never change the exit code. */
|
|
584
|
+
skipped?: number;
|
|
585
|
+
total: number;
|
|
586
|
+
done: number;
|
|
587
|
+
};
|
|
394
588
|
cases: EvalCaseSnapshot[];
|
|
395
589
|
config: {
|
|
396
590
|
maxConcurrency?: number;
|
|
@@ -453,6 +647,17 @@ export interface EvalConfigFile {
|
|
|
453
647
|
* request limits.
|
|
454
648
|
*/
|
|
455
649
|
maxConcurrency: number;
|
|
650
|
+
/**
|
|
651
|
+
* Default judge model for `t.judge.*` across this tree. Optional: a tree of
|
|
652
|
+
* fully deterministic evals never needs one.
|
|
653
|
+
*/
|
|
654
|
+
judge?: EvalJudgeConfig;
|
|
655
|
+
/**
|
|
656
|
+
* Destinations that observe **every** case in the run — the right place for a
|
|
657
|
+
* shared one, so you do not repeat it in each eval file. `--skip-report`
|
|
658
|
+
* suppresses them for a local iteration loop.
|
|
659
|
+
*/
|
|
660
|
+
reporters?: EvalReporter[];
|
|
456
661
|
/**
|
|
457
662
|
* Optional. Max batches kept in the **playground / `/v1/dev/evals*`**
|
|
458
663
|
* history window (in-memory, and pruned from the `defineStorage` evals
|
|
@@ -479,6 +684,8 @@ export function defineEvalConfig(config: EvalConfigFile): EvalConfigFile {
|
|
|
479
684
|
maxPlaygroundRuns: normalizeEvalMaxPlaygroundRuns(
|
|
480
685
|
config.maxPlaygroundRuns ?? EVAL_DEFAULT_MAX_PLAYGROUND_RUNS
|
|
481
686
|
),
|
|
687
|
+
judge: config.judge,
|
|
688
|
+
reporters: config.reporters,
|
|
482
689
|
};
|
|
483
690
|
}
|
|
484
691
|
|
package/src/index.ts
CHANGED
|
@@ -124,12 +124,14 @@ export {
|
|
|
124
124
|
export { defineTool } from "./tools.js";
|
|
125
125
|
export type * from "./types.js";
|
|
126
126
|
export {
|
|
127
|
+
cursorAccountServersHasMixedWildcard,
|
|
127
128
|
DEFAULT_MODEL,
|
|
128
129
|
DIRECT_TOOL_CALL_CHANNEL_ID,
|
|
129
130
|
isCursorAccountTransport,
|
|
130
131
|
isPeerConnectionTransport,
|
|
131
132
|
isSymbolicConnectionTransport,
|
|
132
133
|
MAX_RUNNING_TURNS_LIMIT,
|
|
134
|
+
normalizeCursorAccountServers,
|
|
133
135
|
} from "./types.js";
|
|
134
136
|
|
|
135
137
|
/**
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import { join, resolve } from "node:path";
|
|
13
|
-
import
|
|
13
|
+
import { Artifacts, JUnit } from "../evals/reporters.js";
|
|
14
|
+
import type {
|
|
15
|
+
EvalAssertionResult,
|
|
16
|
+
EvalReporter,
|
|
17
|
+
EvalRunSnapshot,
|
|
18
|
+
EvalVerdict,
|
|
19
|
+
} from "../evals.js";
|
|
14
20
|
import { serve } from "../index.js";
|
|
15
21
|
import type { AgentServeHandle } from "../types.js";
|
|
16
22
|
import { lookupSessionContinuation, runChat } from "./chat-client.js";
|
|
@@ -96,6 +102,19 @@ export interface AxCliOptions {
|
|
|
96
102
|
tags?: string[];
|
|
97
103
|
/** Eval ids / path prefixes. */
|
|
98
104
|
evalIds?: string[];
|
|
105
|
+
/** `eval`: a soft threshold miss (`scored`) also fails the exit code. */
|
|
106
|
+
strict?: boolean;
|
|
107
|
+
/** `eval`: write JUnit XML to this path. */
|
|
108
|
+
junit?: string;
|
|
109
|
+
/**
|
|
110
|
+
* `eval`: run-artifact directory. Defaults to a timestamped directory under
|
|
111
|
+
* `<dir>/.agent-serve/evals/`; `false` disables artifacts entirely.
|
|
112
|
+
*/
|
|
113
|
+
artifacts?: string | false;
|
|
114
|
+
/** `eval`: skip reporters declared in `evals.config.ts` and eval files. */
|
|
115
|
+
skipReport?: boolean;
|
|
116
|
+
/** `eval`: override `evals.config.ts` `maxConcurrency`. */
|
|
117
|
+
maxConcurrency?: number;
|
|
99
118
|
/** Scaffold target directory for `init`. */
|
|
100
119
|
initTarget?: string;
|
|
101
120
|
/** Tool name for `call`. */
|
|
@@ -839,6 +858,7 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
|
|
|
839
858
|
// batch lands in playground history and defineStorage evals (when set).
|
|
840
859
|
if (mode === "local") {
|
|
841
860
|
const progress = stream ? createStreamProgress() : undefined;
|
|
861
|
+
const artifactsDir = resolveEvalArtifactsDir(projectRoot, options);
|
|
842
862
|
try {
|
|
843
863
|
const results = await runDiscoveredEvals({
|
|
844
864
|
projectRoot,
|
|
@@ -846,6 +866,11 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
|
|
|
846
866
|
filterIds,
|
|
847
867
|
tags,
|
|
848
868
|
timeoutMs: options.timeoutMs,
|
|
869
|
+
maxConcurrency: options.maxConcurrency,
|
|
870
|
+
strict: options.strict === true,
|
|
871
|
+
skipReport: options.skipReport === true,
|
|
872
|
+
reporters: buildCliEvalReporters(options, artifactsDir),
|
|
873
|
+
target: { baseUrl: target.agentUrl, mode: "local" },
|
|
849
874
|
bearerToken: options.bearerToken,
|
|
850
875
|
headers: target.headers,
|
|
851
876
|
verbose: Boolean(options.verbose) || stream,
|
|
@@ -856,7 +881,7 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
|
|
|
856
881
|
process.stderr.write("No matching evals found.\n");
|
|
857
882
|
return 2;
|
|
858
883
|
}
|
|
859
|
-
return printEvalResults(results, options);
|
|
884
|
+
return printEvalResults(results, options, undefined, artifactsDir);
|
|
860
885
|
} finally {
|
|
861
886
|
progress?.close();
|
|
862
887
|
}
|
|
@@ -1359,6 +1384,53 @@ function createRemoteEvalProgress(
|
|
|
1359
1384
|
};
|
|
1360
1385
|
}
|
|
1361
1386
|
|
|
1387
|
+
/**
|
|
1388
|
+
* Where a local run writes its artifacts.
|
|
1389
|
+
*
|
|
1390
|
+
* Defaults to a timestamped directory under the project's gitignored
|
|
1391
|
+
* `.agent-serve/evals/`, so the full story of a failure is always on disk
|
|
1392
|
+
* without the eval author opting in. `--no-artifacts` turns it off.
|
|
1393
|
+
*/
|
|
1394
|
+
export function resolveEvalArtifactsDir(
|
|
1395
|
+
projectRoot: string,
|
|
1396
|
+
options: Pick<AxCliOptions, "artifacts">
|
|
1397
|
+
): string | undefined {
|
|
1398
|
+
if (options.artifacts === false) {
|
|
1399
|
+
return undefined;
|
|
1400
|
+
}
|
|
1401
|
+
if (typeof options.artifacts === "string" && options.artifacts !== "") {
|
|
1402
|
+
return resolve(options.artifacts);
|
|
1403
|
+
}
|
|
1404
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
1405
|
+
return join(projectRoot, ".agent-serve", "evals", stamp);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
/** JUnit / artifacts reporters requested via CLI flags. */
|
|
1409
|
+
function buildCliEvalReporters(
|
|
1410
|
+
options: AxCliOptions,
|
|
1411
|
+
artifactsDir: string | undefined
|
|
1412
|
+
): EvalReporter[] {
|
|
1413
|
+
const reporters: EvalReporter[] = [];
|
|
1414
|
+
if (artifactsDir !== undefined) {
|
|
1415
|
+
reporters.push(Artifacts({ dir: artifactsDir }));
|
|
1416
|
+
}
|
|
1417
|
+
if (options.junit !== undefined && options.junit !== "") {
|
|
1418
|
+
reporters.push(JUnit({ filePath: options.junit }));
|
|
1419
|
+
}
|
|
1420
|
+
return reporters;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
function evalVerdictOf(result: EvalRunResult): EvalVerdict {
|
|
1424
|
+
return result.verdict ?? (result.ok ? "passed" : "failed");
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
const EVAL_VERDICT_LABEL: Record<EvalVerdict, string> = {
|
|
1428
|
+
passed: "PASS",
|
|
1429
|
+
failed: "FAIL",
|
|
1430
|
+
scored: "SCORED",
|
|
1431
|
+
skipped: "SKIP",
|
|
1432
|
+
};
|
|
1433
|
+
|
|
1362
1434
|
function printEvalResults(
|
|
1363
1435
|
results: EvalRunResult[],
|
|
1364
1436
|
options: AxCliOptions,
|
|
@@ -1366,12 +1438,17 @@ function printEvalResults(
|
|
|
1366
1438
|
runId?: string;
|
|
1367
1439
|
status?: EvalRunSnapshot["status"];
|
|
1368
1440
|
error?: string;
|
|
1369
|
-
}
|
|
1441
|
+
},
|
|
1442
|
+
artifactsDir?: string
|
|
1370
1443
|
): number {
|
|
1371
|
-
const
|
|
1372
|
-
const
|
|
1444
|
+
const strict = options.strict === true;
|
|
1445
|
+
const counts = { passed: 0, failed: 0, scored: 0, skipped: 0 };
|
|
1446
|
+
for (const result of results) {
|
|
1447
|
+
counts[evalVerdictOf(result)]++;
|
|
1448
|
+
}
|
|
1373
1449
|
const batchFailed = batch?.status === "failed";
|
|
1374
|
-
const ok =
|
|
1450
|
+
const ok =
|
|
1451
|
+
counts.failed === 0 && !(strict && counts.scored > 0) && !batchFailed;
|
|
1375
1452
|
|
|
1376
1453
|
if (batch?.error !== undefined && batch.error !== "" && !options.json) {
|
|
1377
1454
|
// Always surface harness-level failures (not only --verbose).
|
|
@@ -1383,9 +1460,13 @@ function printEvalResults(
|
|
|
1383
1460
|
JSON.stringify(
|
|
1384
1461
|
{
|
|
1385
1462
|
ok,
|
|
1386
|
-
passed,
|
|
1387
|
-
failed,
|
|
1463
|
+
passed: counts.passed,
|
|
1464
|
+
failed: counts.failed,
|
|
1465
|
+
scored: counts.scored,
|
|
1466
|
+
skipped: counts.skipped,
|
|
1467
|
+
strict,
|
|
1388
1468
|
results,
|
|
1469
|
+
...(artifactsDir === undefined ? {} : { artifactsDir }),
|
|
1389
1470
|
...(batch?.runId === undefined ? {} : { runId: batch.runId }),
|
|
1390
1471
|
...(batch?.status === undefined ? {} : { status: batch.status }),
|
|
1391
1472
|
...(batch?.error === undefined || batch.error === ""
|
|
@@ -1398,19 +1479,19 @@ function printEvalResults(
|
|
|
1398
1479
|
);
|
|
1399
1480
|
} else {
|
|
1400
1481
|
for (const r of results) {
|
|
1482
|
+
const verdict = evalVerdictOf(r);
|
|
1401
1483
|
process.stdout.write(
|
|
1402
|
-
`${
|
|
1484
|
+
`${EVAL_VERDICT_LABEL[verdict]} ${r.id} (${(r.durationMs / 1000).toFixed(1)}s)`
|
|
1403
1485
|
);
|
|
1404
1486
|
if (r.description !== undefined) {
|
|
1405
1487
|
process.stdout.write(` — ${r.description}`);
|
|
1406
1488
|
}
|
|
1407
1489
|
process.stdout.write("\n");
|
|
1490
|
+
if (r.skipReason !== undefined) {
|
|
1491
|
+
process.stdout.write(` skipped: ${r.skipReason}\n`);
|
|
1492
|
+
}
|
|
1408
1493
|
for (const a of r.assertions) {
|
|
1409
|
-
process.stdout.write(
|
|
1410
|
-
` ${a.passed ? "✓" : "✗"} ${a.name}${
|
|
1411
|
-
a.detail !== undefined ? ` (${a.detail})` : ""
|
|
1412
|
-
}\n`
|
|
1413
|
-
);
|
|
1494
|
+
process.stdout.write(` ${formatEvalAssertionLine(a)}\n`);
|
|
1414
1495
|
}
|
|
1415
1496
|
for (const [name, value] of Object.entries(r.metrics ?? {})) {
|
|
1416
1497
|
process.stdout.write(` metric ${name}=${String(value)}\n`);
|
|
@@ -1427,13 +1508,51 @@ function printEvalResults(
|
|
|
1427
1508
|
);
|
|
1428
1509
|
}
|
|
1429
1510
|
}
|
|
1511
|
+
const extra: string[] = [];
|
|
1512
|
+
if (counts.scored > 0) {
|
|
1513
|
+
extra.push(
|
|
1514
|
+
`${counts.scored} scored${strict ? " (fatal: --strict)" : ""}`
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
if (counts.skipped > 0) {
|
|
1518
|
+
extra.push(`${counts.skipped} skipped`);
|
|
1519
|
+
}
|
|
1430
1520
|
process.stdout.write(
|
|
1431
|
-
`\n${passed} passed, ${failed} failed
|
|
1521
|
+
`\n${counts.passed} passed, ${counts.failed} failed${
|
|
1522
|
+
extra.length === 0 ? "" : `, ${extra.join(", ")}`
|
|
1523
|
+
}, ${results.length} total\n`
|
|
1432
1524
|
);
|
|
1525
|
+
if (artifactsDir !== undefined) {
|
|
1526
|
+
process.stdout.write(`artifacts: ${artifactsDir}\n`);
|
|
1527
|
+
}
|
|
1433
1528
|
}
|
|
1434
1529
|
return ok ? 0 : 1;
|
|
1435
1530
|
}
|
|
1436
1531
|
|
|
1532
|
+
/**
|
|
1533
|
+
* One assertion line. Soft assertions read `~` rather than `✗` when they miss,
|
|
1534
|
+
* so a tracked score is visibly different from a failed gate.
|
|
1535
|
+
*/
|
|
1536
|
+
export function formatEvalAssertionLine(
|
|
1537
|
+
assertion: EvalAssertionResult
|
|
1538
|
+
): string {
|
|
1539
|
+
const soft = (assertion.severity ?? "gate") === "soft";
|
|
1540
|
+
const mark = assertion.passed ? "✓" : soft ? "~" : "✗";
|
|
1541
|
+
const parts: string[] = [];
|
|
1542
|
+
if (assertion.score !== undefined) {
|
|
1543
|
+
parts.push(
|
|
1544
|
+
`score=${assertion.score.toFixed(3)}${
|
|
1545
|
+
assertion.threshold === undefined ? "" : ` >= ${assertion.threshold}`
|
|
1546
|
+
}`
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
if (assertion.detail !== undefined) {
|
|
1550
|
+
parts.push(assertion.detail);
|
|
1551
|
+
}
|
|
1552
|
+
const suffix = parts.length === 0 ? "" : ` (${parts.join(", ")})`;
|
|
1553
|
+
return `${mark} ${assertion.name}${soft ? " [soft]" : ""}${suffix}`;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1437
1556
|
/**
|
|
1438
1557
|
* `logs` — follow deploy progress (`--prod`) then runtime agent logs.
|
|
1439
1558
|
* Local / `--url` tails `/v1/logs` only.
|