@mutagent/evaluator 0.1.0-alpha.6 → 0.2.0-alpha.1

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 (28) hide show
  1. package/.claude/skills/mutagent-evaluator/SKILL.md +23 -25
  2. package/.claude/skills/mutagent-evaluator/assets/agents/dataset-builder.md +6 -5
  3. package/.claude/skills/mutagent-evaluator/assets/agents/evaluator.md +77 -14
  4. package/.claude/skills/mutagent-evaluator/assets/templates/discover-report.template.html +397 -0
  5. package/.claude/skills/mutagent-evaluator/assets/templates/eval-report.template.html +594 -0
  6. package/.claude/skills/mutagent-evaluator/assets/templates/review-report.template.html +560 -0
  7. package/.claude/skills/mutagent-evaluator/golden/judge-trajectory.prose.md +69 -0
  8. package/.claude/skills/mutagent-evaluator/references/data-registry.md +43 -0
  9. package/.claude/skills/mutagent-evaluator/references/eval-layers.md +52 -0
  10. package/.claude/skills/mutagent-evaluator/references/eval-stage.md +1 -1
  11. package/.claude/skills/mutagent-evaluator/scripts/aggregate-discover.ts +254 -9
  12. package/.claude/skills/mutagent-evaluator/scripts/audience.ts +84 -0
  13. package/.claude/skills/mutagent-evaluator/scripts/code-eval-library.ts +201 -0
  14. package/.claude/skills/mutagent-evaluator/scripts/contracts/agentspec-evals.ts +101 -39
  15. package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-matrix.ts +287 -3
  16. package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-types.ts +30 -4
  17. package/.claude/skills/mutagent-evaluator/scripts/materialize-dataset.ts +130 -68
  18. package/.claude/skills/mutagent-evaluator/scripts/matrix-judge.ts +219 -1
  19. package/.claude/skills/mutagent-evaluator/scripts/merge-criteria.ts +849 -0
  20. package/.claude/skills/mutagent-evaluator/scripts/render-discover-report-v3.ts +1155 -0
  21. package/.claude/skills/mutagent-evaluator/scripts/render-eval-report-v3.ts +610 -0
  22. package/.claude/skills/mutagent-evaluator/scripts/render-eval-report.ts +2 -1
  23. package/.claude/skills/mutagent-evaluator/scripts/render-review-report-v3.ts +691 -0
  24. package/.claude/skills/mutagent-evaluator/scripts/route-failures.ts +10 -7
  25. package/.claude/skills/mutagent-evaluator/scripts/run-evaluate.ts +46 -1
  26. package/.claude/skills/mutagent-evaluator/scripts/run-review.ts +266 -0
  27. package/.claude/skills/mutagent-evaluator/scripts/verify-render.ts +728 -0
  28. package/package.json +2 -2
@@ -0,0 +1,201 @@
1
+ /**
2
+ * code-eval-library — the L0 CODE-CHECKS layer of the v3 layered walk (W2 · D4=A).
3
+ *
4
+ * The growing library of code-detectable failure patterns, run FIRST as the cheap
5
+ * early filter before any trace is read by the LLM judge. Each entry is a STATIC
6
+ * check over the MatrixPacket's trajectory — NO LLM, no content semantics. A
7
+ * pattern FIRING flags a CANDIDATE for the deeper layers; it is NOT a verdict
8
+ * (code-detectable ≠ incorrect — the meta-judge fence). The judge records each
9
+ * hit as `codeEvalHits[{pattern, anchor, detail}]` (E-NEW-5) — evidence, never
10
+ * a layer verdict on its own.
11
+ *
12
+ * TS re-implementation of the Labs seeds (spec-first absorption, D4=A). The
13
+ * Python originals stay in Labs as measurement provenance (catch 1.0 / FP 0.0
14
+ * held-out on the aliased benchmark — a maturity fence, not a claim about any
15
+ * new subject). Origins:
16
+ * CE-P1 fault-passthrough ← M-tool-fault-passthrough (fail at k → DIFFERENT tool at k+1)
17
+ * CE-P2 error-output ← M-tool-output-check (completeness arm: any error output)
18
+ * CE-P3 malformed-structure ← M-tool-output-check (structural arm; schema-less here: truncation only)
19
+ * CE-P4 unguarded-send ← M-expected-outcome-map (required-review-missing invariant)
20
+ * CE-P5 out-of-order-gate ← M-behavioral-tree-divergence (send precedes the present review)
21
+ *
22
+ * GENERIC fence: the send/review effect classifier is name-heuristic SEED logic
23
+ * and is INJECTABLE — a subject profile may carry better effect classes; nothing
24
+ * subject-specific is hard-coded. Deterministic — no clock, no random, no network.
25
+ *
26
+ * CLI (how the judge runs L0 per the mode doc):
27
+ * bun scripts/code-eval-library.ts <packet.json> → {"hits":[{pattern,anchor,detail}]} on stdout
28
+ */
29
+ import { readFileSync } from "node:fs";
30
+
31
+ /** One L0 hit — mirrors the CodeEvalHit contract (eval-matrix.ts). */
32
+ export interface CodeEvalHit {
33
+ pattern: string;
34
+ anchor: string;
35
+ detail: string;
36
+ }
37
+
38
+ /** The minimal step shape the library reads (the packet's trajectory[N]). */
39
+ export interface CodeEvalStep {
40
+ name: string;
41
+ input?: unknown;
42
+ output?: unknown;
43
+ }
44
+
45
+ /** send/review effect classes for the gate patterns (CE-P4/P5). */
46
+ export type EffectClass = "send" | "review" | "other";
47
+
48
+ /** Heuristic SEED classifier — injectable; a subject profile may override. */
49
+ export function defaultEffectClassifier(step: CodeEvalStep): EffectClass {
50
+ const n = step.name.toLowerCase();
51
+ if (/send|dispatch|publish|reply|post/.test(n)) return "send";
52
+ if (/review|approv|guard|verify|check|confirm/.test(n)) return "review";
53
+ return "other";
54
+ }
55
+
56
+ /** Did this tool output fail? success:false OR an error-shaped payload. */
57
+ function outputFailed(step: CodeEvalStep): boolean {
58
+ const out = step.output;
59
+ if (out === null || out === undefined || typeof out !== "object") return false;
60
+ const rec = out as Record<string, unknown>;
61
+ if (rec["success"] === false) return true;
62
+ if (typeof rec["error"] === "string" && rec["error"].length > 0) return true;
63
+ if (rec["status"] === "error" || rec["status"] === "failed") return true;
64
+ return false;
65
+ }
66
+
67
+ /** Was this output truncated? (the packet marks it; absent = false). */
68
+ function outputTruncated(step: CodeEvalStep): boolean {
69
+ const out = step.output;
70
+ if (out === null || out === undefined || typeof out !== "object") return false;
71
+ return (out as Record<string, unknown>)["truncated"] === true;
72
+ }
73
+
74
+ /**
75
+ * CE-P1 fault-passthrough: a tool fails at step k and step k+1 is a DIFFERENT
76
+ * tool (not a same-tool retry, not run-end). Strict-local ⇒ an UPPER BOUND —
77
+ * handling that arrives >1 step later is under-credited (the origin's fence).
78
+ */
79
+ function ceP1(steps: CodeEvalStep[]): CodeEvalHit | null {
80
+ for (let k = 0; k < steps.length; k++) {
81
+ const s = steps[k]!;
82
+ if (!outputFailed(s)) continue;
83
+ if (k + 1 >= steps.length) continue; // run ended at the fault → handled-by-termination
84
+ const nxt = steps[k + 1]!;
85
+ if (nxt.name === s.name) continue; // same-tool retry → handled
86
+ return {
87
+ pattern: "CE-P1 fault-passthrough",
88
+ anchor: `trajectory.${k}`,
89
+ detail: `${s.name} failed; the chronologically NEXT step is a DIFFERENT tool (${nxt.name}) — proceeded on a broken result (strict-local upper bound).`,
90
+ };
91
+ }
92
+ return null;
93
+ }
94
+
95
+ /** CE-P2 error-output: ANY error tool output (completeness arm; a later-handled
96
+ * error still fires — separating handled is CE-P1's job, the declared cost). */
97
+ function ceP2(steps: CodeEvalStep[]): CodeEvalHit | null {
98
+ const k = steps.findIndex(outputFailed);
99
+ if (k < 0) return null;
100
+ return {
101
+ pattern: "CE-P2 error-output",
102
+ anchor: `trajectory.${k}`,
103
+ detail: `${steps[k]!.name} returned an error-shaped output (success:false / error payload).`,
104
+ };
105
+ }
106
+
107
+ /** CE-P3 malformed-structure (schema-less form): a TRUNCATED payload is missing
108
+ * its trailing fields — structural. Full schema conformance needs a derived
109
+ * clean-corpus schema (not available generically) — declared scope limit. */
110
+ function ceP3(steps: CodeEvalStep[]): CodeEvalHit | null {
111
+ for (let k = 0; k < steps.length; k++) {
112
+ if (outputFailed(steps[k]!)) continue; // error outputs are CE-P2's domain (disjoint scoping)
113
+ if (outputTruncated(steps[k]!)) {
114
+ return {
115
+ pattern: "CE-P3 malformed-structure",
116
+ anchor: `trajectory.${k}`,
117
+ detail: `${steps[k]!.name} output marked truncated — trailing fields missing (structural).`,
118
+ };
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+
124
+ /** CE-P4 unguarded-send: a send effect with NO review effect anywhere. */
125
+ function ceP4(seq: { cls: EffectClass; k: number; name: string }[]): CodeEvalHit | null {
126
+ const send = seq.find((e) => e.cls === "send");
127
+ if (send === undefined) return null;
128
+ if (seq.some((e) => e.cls === "review")) return null; // review present → CE-P5's domain
129
+ return {
130
+ pattern: "CE-P4 unguarded-send",
131
+ anchor: `trajectory.${send.k}`,
132
+ detail: `${send.name} (send effect) with NO review-class step anywhere in the trajectory.`,
133
+ };
134
+ }
135
+
136
+ /** CE-P5 out-of-order-gate: review EXISTS but a send precedes the first review. */
137
+ function ceP5(seq: { cls: EffectClass; k: number; name: string }[]): CodeEvalHit | null {
138
+ const firstReview = seq.findIndex((e) => e.cls === "review");
139
+ if (firstReview < 0) return null; // no review at all → CE-P4's domain, not a reorder
140
+ const early = seq.find((e, i) => e.cls === "send" && i < firstReview);
141
+ if (early === undefined) return null;
142
+ return {
143
+ pattern: "CE-P5 out-of-order-gate",
144
+ anchor: `trajectory.${early.k}`,
145
+ detail: `${early.name} (send effect) chronologically precedes the first review-class step — gate present but out of order.`,
146
+ };
147
+ }
148
+
149
+ /** The step order of the input array. CE-P1/CE-P5 are ORDER-BEARING; a
150
+ * MatrixPacket's `trajectory` preserves the trace's observations order, which
151
+ * is REVERSE-chronological in this intake (the judge brief says the same:
152
+ * "the observations array is reverse-chronological"). Default matches the
153
+ * packet so `bun … <packet.json>` is correct out of the box. */
154
+ export type StepOrder = "chronological" | "reverse-chronological";
155
+
156
+ /**
157
+ * Run the whole library over a packet's trajectory steps. Returns every fired
158
+ * hit (empty = no pattern fired — which is NOT a clean-bill verdict, only "no
159
+ * code-detectable candidate"). Order-bearing checks (CE-P1/CE-P5) run over the
160
+ * CHRONOLOGICAL sequence; anchors always cite the ORIGINAL input index (so a
161
+ * `trajectory.N` anchor re-resolves against the packet as handed). PURE +
162
+ * deterministic.
163
+ */
164
+ export function runCodeEvalLibrary(
165
+ steps: CodeEvalStep[],
166
+ classify: (s: CodeEvalStep) => EffectClass = defaultEffectClassifier,
167
+ order: StepOrder = "reverse-chronological",
168
+ ): CodeEvalHit[] {
169
+ // normalize to chronological, remembering each step's ORIGINAL packet index.
170
+ const indexed = steps.map((s, k) => ({ s, k }));
171
+ const chrono = order === "reverse-chronological" ? [...indexed].reverse() : indexed;
172
+ const chronoSteps = chrono.map((e) => e.s);
173
+ const seq = chrono.map((e, i) => ({ cls: classify(e.s), k: e.k, name: e.s.name, i }));
174
+ // re-anchor a hit produced against chrono positions back to the packet index.
175
+ const reanchor = (h: CodeEvalHit | null): CodeEvalHit | null => {
176
+ if (h === null) return h;
177
+ const m = /^trajectory\.(\d+)$/.exec(h.anchor);
178
+ if (m === null) return h;
179
+ const orig = chrono[Number.parseInt(m[1]!, 10)];
180
+ return orig === undefined ? h : { ...h, anchor: `trajectory.${orig.k}` };
181
+ };
182
+ return [
183
+ reanchor(ceP1(chronoSteps)),
184
+ reanchor(ceP2(chronoSteps)),
185
+ reanchor(ceP3(chronoSteps)),
186
+ ceP4(seq), // seq entries already carry the original index k
187
+ ceP5(seq),
188
+ ].filter((h): h is CodeEvalHit => h !== null);
189
+ }
190
+
191
+ // ── CLI: bun scripts/code-eval-library.ts <packet.json> ──────────────────────
192
+ if (import.meta.main) {
193
+ const packetPath = process.argv[2];
194
+ if (packetPath === undefined) {
195
+ console.error("usage: bun scripts/code-eval-library.ts <packet.json>");
196
+ process.exit(2);
197
+ }
198
+ const packet = JSON.parse(readFileSync(packetPath, "utf8")) as { trajectory?: CodeEvalStep[] };
199
+ const hits = runCodeEvalLibrary(packet.trajectory ?? []);
200
+ console.info(JSON.stringify({ hits }, null, 2));
201
+ }
@@ -1,84 +1,146 @@
1
1
  /**
2
- * scripts/contracts/agentspec-evals.ts — the MINIMAL agentspec.evals slice the
2
+ * scripts/contracts/agentspec-evals.ts — the MINIMAL `spec.evaluation` slice the
3
3
  * EVAL stage consumes (standalone — NEVER imports the agentspec skill's schema).
4
4
  * ---------------------------------------------------------------------------
5
- * The ADL `*build` stage emits an agentspec whose `definition.evals` block carries
6
- * the SEED material the evaluator materializes into a real dataset (F8) + criteria:
5
+ * AgentSpec 0.3.0 (D18/D19) replaces the flat 0.2 `definition.evals` shape
6
+ * (`dataset_categories[] × edge_cases[]`) with `spec.evaluation`, a universal
7
+ * closure of THREE arrays (each may be empty, F05):
7
8
  *
8
- * - dataset_categories[] — `{ id, description, edge_cases[] }` — the golden
9
- * eval-suite slices + the explicit edge_cases each must exercise (the dataset
10
- * DEFINITION the spec hands forward; "seed, don't duplicate").
11
- * - scenarios[] — `{ id, description, expected_behavior, category?,
12
- * edge_case? }` — representative situations + the correct behavior (extra seed
13
- * material, optionally tagged into a category).
14
- * - success_criteria[] — `{ id, criterion, type, goal }` — binary-actionable
15
- * pass/fail criteria (type llm-judge | code-check).
9
+ * - criteria[] — `{ id, description, type, goal }` — binary-actionable pass/fail
10
+ * criteria (type llm-judge | code-check). (0.2 `success_criteria[]`, with
11
+ * `criterion` `description`.)
12
+ * - scenarios[] — `{ id, description, expectedBehavior, edgeCase? }` — representative
13
+ * situations + the correct behavior. Scenario↔dataset linkage now lives on the
14
+ * dataset ITEM (`scenarioRef`), not on the scenario.
15
+ * - datasets[] — `{ id, description, mapsTo?, categories[], caseDimensions?,
16
+ * items[]|itemsRef? }` REAL, already-materialized dataset rows (D18):
17
+ * · mapsTo — direct job/scenario/criterion references this dataset exercises.
18
+ * · categories[] — DATASET-LOCAL taxonomy (`{ id, description, generationGuidance? }`),
19
+ * no longer a top-level global list (D18).
20
+ * · caseDimensions — a MAP `name → { description?, values[] }`; the independent
21
+ * variation axes each item's `case` assigns over.
22
+ * · items[] — each `{ id, category?, scenarioRef?, case?, input?, expected? }`
23
+ * carrying a KIND-SPECIFIC `input`/`expected` payload (opaque data — we read the
24
+ * shape structurally, never interpret the kind).
16
25
  *
17
26
  * We DECLARE only the fields we read, with `additionalProperties: true` on the
18
- * objects so a richer agentspec still parses (forward-compatible). The cross-skill
27
+ * objects so a richer 0.3.x agentspec still parses (forward-compatible). The cross-skill
19
28
  * import ban (coding-rules "Sealed-Sibling" + standalone discipline) is why this is
20
29
  * a local re-declaration, not an import. PURE — no clock / random / network.
21
30
  */
22
31
  import { type Static, Type } from "@sinclair/typebox";
23
32
  import { Value } from "@sinclair/typebox/value";
24
33
 
25
- /** A dataset CATEGORY slice + the edge-cases it must exercise. */
26
- export const AgentspecDatasetCategorySchema = Type.Object(
34
+ /** A binary-actionable SUCCESS CRITERION (0.3 `criteria[]`). */
35
+ export const SpecEvalCriterionSchema = Type.Object(
27
36
  {
28
37
  id: Type.String({ minLength: 1 }),
29
38
  description: Type.String({ minLength: 1 }),
30
- edge_cases: Type.Array(Type.String()),
39
+ type: Type.Union([Type.Literal("llm-judge"), Type.Literal("code-check")]),
40
+ goal: Type.String({ minLength: 1 }),
31
41
  },
32
42
  { additionalProperties: true },
33
43
  );
34
- export type AgentspecDatasetCategory = Static<typeof AgentspecDatasetCategorySchema>;
44
+ export type SpecEvalCriterion = Static<typeof SpecEvalCriterionSchema>;
35
45
 
36
46
  /** A representative SCENARIO (the situation + correct behavior). */
37
- export const AgentspecScenarioSchema = Type.Object(
47
+ export const SpecEvalScenarioSchema = Type.Object(
38
48
  {
39
49
  id: Type.String({ minLength: 1 }),
40
50
  description: Type.String({ minLength: 1 }),
41
- expected_behavior: Type.String({ minLength: 1 }),
51
+ expectedBehavior: Type.String({ minLength: 1 }),
52
+ edgeCase: Type.Optional(Type.Boolean()),
53
+ },
54
+ { additionalProperties: true },
55
+ );
56
+ export type SpecEvalScenario = Static<typeof SpecEvalScenarioSchema>;
57
+
58
+ /** A DATASET-LOCAL category slice (D18) — the taxonomy is owned by its dataset. */
59
+ export const SpecEvalDatasetCategorySchema = Type.Object(
60
+ {
61
+ id: Type.String({ minLength: 1 }),
62
+ description: Type.String({ minLength: 1 }),
63
+ generationGuidance: Type.Optional(Type.String()),
64
+ },
65
+ { additionalProperties: true },
66
+ );
67
+ export type SpecEvalDatasetCategory = Static<typeof SpecEvalDatasetCategorySchema>;
68
+
69
+ /** One independent variation AXIS in a dataset's `caseDimensions` map (the map VALUE). */
70
+ export const SpecEvalCaseDimensionSchema = Type.Object(
71
+ {
72
+ description: Type.Optional(Type.String()),
73
+ values: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
74
+ },
75
+ { additionalProperties: true },
76
+ );
77
+ export type SpecEvalCaseDimension = Static<typeof SpecEvalCaseDimensionSchema>;
78
+
79
+ /**
80
+ * A single, already-materialized dataset ITEM. `case` assigns a value per
81
+ * `caseDimensions` axis; `input`/`expected` are opaque kind-specific payloads we
82
+ * carry structurally but never interpret.
83
+ */
84
+ export const SpecEvalDatasetItemSchema = Type.Object(
85
+ {
86
+ id: Type.String({ minLength: 1 }),
42
87
  category: Type.Optional(Type.String()),
43
- edge_case: Type.Optional(Type.Boolean()),
88
+ scenarioRef: Type.Optional(Type.String()),
89
+ case: Type.Optional(Type.Record(Type.String({ minLength: 1 }), Type.String())),
90
+ input: Type.Optional(Type.Unknown()),
91
+ expected: Type.Optional(Type.Unknown()),
44
92
  },
45
93
  { additionalProperties: true },
46
94
  );
47
- export type AgentspecScenario = Static<typeof AgentspecScenarioSchema>;
95
+ export type SpecEvalDatasetItem = Static<typeof SpecEvalDatasetItemSchema>;
48
96
 
49
- /** A binary-actionable SUCCESS CRITERION. */
50
- export const AgentspecSuccessCriterionSchema = Type.Object(
97
+ /** Direct job/scenario/criterion references a dataset exercises (all optional). */
98
+ export const SpecEvalDatasetMapsToSchema = Type.Object(
99
+ {
100
+ jobs: Type.Optional(Type.Array(Type.String())),
101
+ scenarios: Type.Optional(Type.Array(Type.String())),
102
+ criteria: Type.Optional(Type.Array(Type.String())),
103
+ },
104
+ { additionalProperties: true },
105
+ );
106
+ export type SpecEvalDatasetMapsTo = Static<typeof SpecEvalDatasetMapsToSchema>;
107
+
108
+ /** A dataset: local categories + variation axes + the real materialized `items[]`. */
109
+ export const SpecEvalDatasetSchema = Type.Object(
51
110
  {
52
111
  id: Type.String({ minLength: 1 }),
53
- criterion: Type.String({ minLength: 1 }),
54
- type: Type.Union([Type.Literal("llm-judge"), Type.Literal("code-check")]),
55
- goal: Type.String({ minLength: 1 }),
112
+ description: Type.String({ minLength: 1 }),
113
+ mapsTo: Type.Optional(SpecEvalDatasetMapsToSchema),
114
+ categories: Type.Optional(Type.Array(SpecEvalDatasetCategorySchema)),
115
+ caseDimensions: Type.Optional(Type.Record(Type.String({ minLength: 1 }), SpecEvalCaseDimensionSchema)),
116
+ items: Type.Optional(Type.Array(SpecEvalDatasetItemSchema)),
117
+ itemsRef: Type.Optional(Type.String()),
56
118
  },
57
119
  { additionalProperties: true },
58
120
  );
59
- export type AgentspecSuccessCriterion = Static<typeof AgentspecSuccessCriterionSchema>;
121
+ export type SpecEvalDataset = Static<typeof SpecEvalDatasetSchema>;
60
122
 
61
- /** The `definition.evals` slice. Each array may be empty structurally. */
62
- export const AgentspecEvalsSchema = Type.Object(
123
+ /** The `spec.evaluation` closure. Each array is required but may be empty (F05). */
124
+ export const SpecEvaluationSchema = Type.Object(
63
125
  {
64
- success_criteria: Type.Array(AgentspecSuccessCriterionSchema),
65
- scenarios: Type.Array(AgentspecScenarioSchema),
66
- dataset_categories: Type.Array(AgentspecDatasetCategorySchema),
126
+ criteria: Type.Array(SpecEvalCriterionSchema),
127
+ scenarios: Type.Array(SpecEvalScenarioSchema),
128
+ datasets: Type.Array(SpecEvalDatasetSchema),
67
129
  },
68
130
  { additionalProperties: true },
69
131
  );
70
- export type AgentspecEvals = Static<typeof AgentspecEvalsSchema>;
132
+ export type SpecEvaluation = Static<typeof SpecEvaluationSchema>;
71
133
 
72
134
  /**
73
- * Parse + narrow the agentspec.evals slice (guarded). THROWS on schema violation —
74
- * a malformed seed must never silently reach materialization. PURE.
135
+ * Parse + narrow the `spec.evaluation` slice (guarded). THROWS on schema violation —
136
+ * a malformed contract must never silently reach materialization. PURE.
75
137
  */
76
- export function parseAgentspecEvals(value: unknown): AgentspecEvals {
77
- if (!Value.Check(AgentspecEvalsSchema, value)) {
78
- const first = [...Value.Errors(AgentspecEvalsSchema, value)][0];
138
+ export function parseSpecEvaluation(value: unknown): SpecEvaluation {
139
+ if (!Value.Check(SpecEvaluationSchema, value)) {
140
+ const first = [...Value.Errors(SpecEvaluationSchema, value)][0];
79
141
  throw new Error(
80
- `parseAgentspecEvals: schema violation at '${first?.path ?? "(root)"}': ` +
81
- `${first?.message ?? "invalid agentspec evals slice"}`,
142
+ `parseSpecEvaluation: schema violation at '${first?.path ?? "(root)"}': ` +
143
+ `${first?.message ?? "invalid spec.evaluation slice"}`,
82
144
  );
83
145
  }
84
146
  return value;