@prismatic-io/lux 0.0.1 → 0.0.2-preview.10

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 (256) hide show
  1. package/lib/answerers/persona/index.d.ts +2 -1
  2. package/lib/answerers/persona/index.d.ts.map +1 -1
  3. package/lib/answerers/persona/index.js +19 -5
  4. package/lib/answerers/persona/index.js.map +1 -1
  5. package/lib/answerers/terminal/index.js +1 -1
  6. package/lib/answerers/terminal/index.js.map +1 -1
  7. package/lib/assertions/authoring.d.ts +14 -0
  8. package/lib/assertions/authoring.d.ts.map +1 -0
  9. package/lib/assertions/authoring.js +3 -0
  10. package/lib/assertions/authoring.js.map +1 -0
  11. package/lib/assertions/core/helpers.d.ts +2 -7
  12. package/lib/assertions/core/helpers.d.ts.map +1 -1
  13. package/lib/assertions/core/helpers.js +2 -6
  14. package/lib/assertions/core/helpers.js.map +1 -1
  15. package/lib/assertions/core/index.d.ts.map +1 -1
  16. package/lib/assertions/core/index.js +2 -1
  17. package/lib/assertions/core/index.js.map +1 -1
  18. package/lib/assertions/core/resource-checks.d.ts +19 -0
  19. package/lib/assertions/core/resource-checks.d.ts.map +1 -1
  20. package/lib/assertions/core/resource-checks.js +32 -0
  21. package/lib/assertions/core/resource-checks.js.map +1 -1
  22. package/lib/authoring.d.ts +1 -0
  23. package/lib/authoring.d.ts.map +1 -1
  24. package/lib/authoring.js +1 -0
  25. package/lib/authoring.js.map +1 -1
  26. package/lib/cli/program.d.ts.map +1 -1
  27. package/lib/cli/program.js +12 -67
  28. package/lib/cli/program.js.map +1 -1
  29. package/lib/cli/render/reporter.d.ts.map +1 -1
  30. package/lib/cli/render/reporter.js +16 -3
  31. package/lib/cli/render/reporter.js.map +1 -1
  32. package/lib/cli/run-options.d.ts +1 -0
  33. package/lib/cli/run-options.d.ts.map +1 -1
  34. package/lib/cli/run-options.js.map +1 -1
  35. package/lib/cli/view.d.ts +13 -0
  36. package/lib/cli/view.d.ts.map +1 -0
  37. package/lib/cli/view.js +126 -0
  38. package/lib/cli/view.js.map +1 -0
  39. package/lib/core/action-events.d.ts +29 -0
  40. package/lib/core/action-events.d.ts.map +1 -0
  41. package/lib/core/action-events.js +51 -0
  42. package/lib/core/action-events.js.map +1 -0
  43. package/lib/core/agent-activity.d.ts +49 -0
  44. package/lib/core/agent-activity.d.ts.map +1 -0
  45. package/lib/core/agent-activity.js +206 -0
  46. package/lib/core/agent-activity.js.map +1 -0
  47. package/lib/core/agent-events.d.ts +26 -0
  48. package/lib/core/agent-events.d.ts.map +1 -0
  49. package/lib/core/agent-events.js +13 -0
  50. package/lib/core/agent-events.js.map +1 -0
  51. package/lib/core/artifact-evidence.d.ts +8 -0
  52. package/lib/core/artifact-evidence.d.ts.map +1 -1
  53. package/lib/core/artifact-evidence.js +36 -4
  54. package/lib/core/artifact-evidence.js.map +1 -1
  55. package/lib/core/assertion.d.ts +19 -9
  56. package/lib/core/assertion.d.ts.map +1 -1
  57. package/lib/core/assertion.js +26 -0
  58. package/lib/core/assertion.js.map +1 -1
  59. package/lib/core/case-authoring-state.d.ts +12 -0
  60. package/lib/core/case-authoring-state.d.ts.map +1 -0
  61. package/lib/core/case-authoring-state.js +43 -0
  62. package/lib/core/case-authoring-state.js.map +1 -0
  63. package/lib/core/case.d.ts +29 -1
  64. package/lib/core/case.d.ts.map +1 -1
  65. package/lib/core/case.js +14 -6
  66. package/lib/core/case.js.map +1 -1
  67. package/lib/core/driver-metrics.d.ts +98 -0
  68. package/lib/core/driver-metrics.d.ts.map +1 -0
  69. package/lib/core/driver-metrics.js +36 -0
  70. package/lib/core/driver-metrics.js.map +1 -0
  71. package/lib/core/driver.d.ts +3 -0
  72. package/lib/core/driver.d.ts.map +1 -1
  73. package/lib/core/driver.js.map +1 -1
  74. package/lib/core/events.d.ts +36 -0
  75. package/lib/core/events.d.ts.map +1 -1
  76. package/lib/core/events.js +12 -2
  77. package/lib/core/events.js.map +1 -1
  78. package/lib/core/experiment.d.ts +19 -0
  79. package/lib/core/experiment.d.ts.map +1 -1
  80. package/lib/core/experiment.js +2 -0
  81. package/lib/core/experiment.js.map +1 -1
  82. package/lib/core/index.d.ts +4 -0
  83. package/lib/core/index.d.ts.map +1 -1
  84. package/lib/core/index.js +4 -0
  85. package/lib/core/index.js.map +1 -1
  86. package/lib/core/lifecycle-fixtures.d.ts +3 -1
  87. package/lib/core/lifecycle-fixtures.d.ts.map +1 -1
  88. package/lib/core/lifecycle-fixtures.js +4 -2
  89. package/lib/core/lifecycle-fixtures.js.map +1 -1
  90. package/lib/core/lifecycle-hooks.d.ts +9 -0
  91. package/lib/core/lifecycle-hooks.d.ts.map +1 -1
  92. package/lib/core/lifecycle-hooks.js +8 -2
  93. package/lib/core/lifecycle-hooks.js.map +1 -1
  94. package/lib/core/run.d.ts +150 -0
  95. package/lib/core/run.d.ts.map +1 -1
  96. package/lib/core/run.js +9 -0
  97. package/lib/core/run.js.map +1 -1
  98. package/lib/core/wire.d.ts +12 -0
  99. package/lib/core/wire.d.ts.map +1 -1
  100. package/lib/core/wire.js +5 -1
  101. package/lib/core/wire.js.map +1 -1
  102. package/lib/drivers/claude-code/parse-events.d.ts.map +1 -1
  103. package/lib/drivers/claude-code/parse-events.js +73 -8
  104. package/lib/drivers/claude-code/parse-events.js.map +1 -1
  105. package/lib/drivers/codex/app-events.d.ts +9 -2
  106. package/lib/drivers/codex/app-events.d.ts.map +1 -1
  107. package/lib/drivers/codex/app-events.js +93 -12
  108. package/lib/drivers/codex/app-events.js.map +1 -1
  109. package/lib/drivers/codex/index.d.ts.map +1 -1
  110. package/lib/drivers/codex/index.js +10 -14
  111. package/lib/drivers/codex/index.js.map +1 -1
  112. package/lib/drivers/subprocess/index.d.ts.map +1 -1
  113. package/lib/drivers/subprocess/index.js +6 -0
  114. package/lib/drivers/subprocess/index.js.map +1 -1
  115. package/lib/index.d.ts +10 -6
  116. package/lib/index.d.ts.map +1 -1
  117. package/lib/index.js +10 -6
  118. package/lib/index.js.map +1 -1
  119. package/lib/orchestrator/annotation-store.d.ts.map +1 -1
  120. package/lib/orchestrator/annotation-store.js +6 -2
  121. package/lib/orchestrator/annotation-store.js.map +1 -1
  122. package/lib/orchestrator/case-extensions.d.ts +6 -0
  123. package/lib/orchestrator/case-extensions.d.ts.map +1 -0
  124. package/lib/orchestrator/case-extensions.js +28 -0
  125. package/lib/orchestrator/case-extensions.js.map +1 -0
  126. package/lib/orchestrator/compare.d.ts +13 -1
  127. package/lib/orchestrator/compare.d.ts.map +1 -1
  128. package/lib/orchestrator/compare.js +12 -0
  129. package/lib/orchestrator/compare.js.map +1 -1
  130. package/lib/orchestrator/config.d.ts.map +1 -1
  131. package/lib/orchestrator/config.js +0 -2
  132. package/lib/orchestrator/config.js.map +1 -1
  133. package/lib/orchestrator/doctor.d.ts.map +1 -1
  134. package/lib/orchestrator/doctor.js +5 -3
  135. package/lib/orchestrator/doctor.js.map +1 -1
  136. package/lib/orchestrator/experiment-contracts.d.ts +19 -0
  137. package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
  138. package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -1
  139. package/lib/orchestrator/experiment-evaluation.js +17 -0
  140. package/lib/orchestrator/experiment-evaluation.js.map +1 -1
  141. package/lib/orchestrator/experiment-report.d.ts +19 -0
  142. package/lib/orchestrator/experiment-report.d.ts.map +1 -1
  143. package/lib/orchestrator/experiment-runs.d.ts +38 -0
  144. package/lib/orchestrator/experiment-runs.d.ts.map +1 -1
  145. package/lib/orchestrator/grader-experiment.d.ts +12 -0
  146. package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
  147. package/lib/orchestrator/grading.d.ts +2 -1
  148. package/lib/orchestrator/grading.d.ts.map +1 -1
  149. package/lib/orchestrator/grading.js +16 -4
  150. package/lib/orchestrator/grading.js.map +1 -1
  151. package/lib/orchestrator/index.d.ts +1 -1
  152. package/lib/orchestrator/index.d.ts.map +1 -1
  153. package/lib/orchestrator/index.js +1 -1
  154. package/lib/orchestrator/index.js.map +1 -1
  155. package/lib/orchestrator/list-runs.d.ts +7 -1
  156. package/lib/orchestrator/list-runs.d.ts.map +1 -1
  157. package/lib/orchestrator/list-runs.js +50 -6
  158. package/lib/orchestrator/list-runs.js.map +1 -1
  159. package/lib/orchestrator/loader.d.ts.map +1 -1
  160. package/lib/orchestrator/loader.js +2 -0
  161. package/lib/orchestrator/loader.js.map +1 -1
  162. package/lib/orchestrator/orchestrator.d.ts +3 -0
  163. package/lib/orchestrator/orchestrator.d.ts.map +1 -1
  164. package/lib/orchestrator/orchestrator.js +42 -14
  165. package/lib/orchestrator/orchestrator.js.map +1 -1
  166. package/lib/orchestrator/owned-lock.d.ts.map +1 -1
  167. package/lib/orchestrator/owned-lock.js +52 -38
  168. package/lib/orchestrator/owned-lock.js.map +1 -1
  169. package/lib/orchestrator/regrade.d.ts +6 -0
  170. package/lib/orchestrator/regrade.d.ts.map +1 -1
  171. package/lib/orchestrator/regrade.js +8 -4
  172. package/lib/orchestrator/regrade.js.map +1 -1
  173. package/lib/orchestrator/run-execution.d.ts +2 -1
  174. package/lib/orchestrator/run-execution.d.ts.map +1 -1
  175. package/lib/orchestrator/run-execution.js +20 -5
  176. package/lib/orchestrator/run-execution.js.map +1 -1
  177. package/lib/orchestrator/suite-compare.d.ts +34 -0
  178. package/lib/orchestrator/suite-compare.d.ts.map +1 -1
  179. package/lib/orchestrator/suite-summary.d.ts +68 -0
  180. package/lib/orchestrator/suite-summary.d.ts.map +1 -1
  181. package/lib/orchestrator/suite-summary.js +45 -7
  182. package/lib/orchestrator/suite-summary.js.map +1 -1
  183. package/lib/previewer/artifacts.d.ts +25 -0
  184. package/lib/previewer/artifacts.d.ts.map +1 -0
  185. package/lib/previewer/artifacts.js +180 -0
  186. package/lib/previewer/artifacts.js.map +1 -0
  187. package/lib/previewer/server.d.ts +105 -0
  188. package/lib/previewer/server.d.ts.map +1 -0
  189. package/lib/previewer/server.js +542 -0
  190. package/lib/previewer/server.js.map +1 -0
  191. package/package.json +2 -2
  192. package/skills/lux/SKILL.md +7 -3
  193. package/skills/lux/references/cli.md +25 -2
  194. package/skills/lux/references/eval-authoring.md +158 -4
  195. package/skills/lux-answerer/SKILL.md +1 -1
  196. package/src/answerers/persona/index.ts +20 -4
  197. package/src/answerers/terminal/index.ts +1 -1
  198. package/src/assertions/authoring.ts +81 -0
  199. package/src/assertions/core/helpers.ts +3 -6
  200. package/src/assertions/core/index.ts +2 -0
  201. package/src/assertions/core/resource-checks.ts +35 -0
  202. package/src/authoring.ts +1 -0
  203. package/src/cli/program.ts +14 -93
  204. package/src/cli/render/reporter.ts +18 -3
  205. package/src/cli/run-options.ts +1 -0
  206. package/src/cli/view.ts +168 -0
  207. package/src/core/action-events.ts +59 -0
  208. package/src/core/agent-activity.ts +236 -0
  209. package/src/core/agent-events.ts +17 -0
  210. package/src/core/artifact-evidence.ts +47 -4
  211. package/src/core/assertion.ts +41 -22
  212. package/src/core/case-authoring-state.ts +65 -0
  213. package/src/core/case.ts +57 -6
  214. package/src/core/driver-metrics.ts +51 -0
  215. package/src/core/driver.ts +3 -0
  216. package/src/core/events.ts +14 -2
  217. package/src/core/experiment.ts +2 -0
  218. package/src/core/index.ts +4 -0
  219. package/src/core/lifecycle-fixtures.ts +7 -2
  220. package/src/core/lifecycle-hooks.ts +16 -2
  221. package/src/core/run.ts +9 -0
  222. package/src/core/wire.ts +5 -1
  223. package/src/drivers/claude-code/parse-events.ts +98 -12
  224. package/src/drivers/codex/app-events.ts +116 -16
  225. package/src/drivers/codex/index.ts +16 -14
  226. package/src/drivers/subprocess/index.ts +6 -0
  227. package/src/index.ts +50 -6
  228. package/src/orchestrator/annotation-store.ts +5 -1
  229. package/src/orchestrator/case-extensions.ts +44 -0
  230. package/src/orchestrator/compare.ts +18 -0
  231. package/src/orchestrator/config.ts +0 -2
  232. package/src/orchestrator/doctor.ts +5 -3
  233. package/src/orchestrator/experiment-evaluation.ts +23 -0
  234. package/src/orchestrator/grading.ts +21 -2
  235. package/src/orchestrator/index.ts +1 -0
  236. package/src/orchestrator/list-runs.ts +66 -5
  237. package/src/orchestrator/loader.ts +2 -0
  238. package/src/orchestrator/orchestrator.ts +86 -40
  239. package/src/orchestrator/owned-lock.ts +54 -34
  240. package/src/orchestrator/regrade.ts +8 -7
  241. package/src/orchestrator/run-execution.ts +24 -3
  242. package/src/orchestrator/suite-summary.ts +101 -8
  243. package/src/previewer/artifacts.ts +221 -0
  244. package/src/previewer/server.ts +765 -0
  245. package/viewer/app.css +2075 -0
  246. package/viewer/app.js +606 -0
  247. package/viewer/artifact-view.js +48 -0
  248. package/viewer/charts.js +86 -0
  249. package/viewer/detail.js +1214 -0
  250. package/viewer/format.js +129 -0
  251. package/viewer/index.html +151 -0
  252. package/lib/answerers/scripted/index.d.ts +0 -8
  253. package/lib/answerers/scripted/index.d.ts.map +0 -1
  254. package/lib/answerers/scripted/index.js +0 -163
  255. package/lib/answerers/scripted/index.js.map +0 -1
  256. package/src/answerers/scripted/index.ts +0 -186
@@ -42,34 +42,23 @@ export interface AssertionImpl<C extends Assertion = Assertion> {
42
42
  * `AssertionImpl`, plugins must describe their case configuration so Lux can
43
43
  * reject invalid plans before starting a subject run.
44
44
  */
45
- export type AssertionPlugin<C extends Assertion = Assertion> = AssertionImpl<C> & {
45
+ export type AssertionPlugin<
46
+ C extends Assertion = Assertion,
47
+ Input extends Assertion = C,
48
+ > = AssertionImpl<C> & {
46
49
  configSchema: z.ZodObject & z.ZodType<C>;
50
+ /** Type-only input accepted while authoring before schema defaults apply. */
51
+ readonly __input?: Input;
47
52
  /** Author-maintained identity for evaluator/schema behavior. */
48
53
  pluginVersion: string;
49
54
  };
50
55
 
51
56
  type SchemaAssertion<S extends z.ZodObject> = z.output<S> extends Assertion ? z.output<S> : never;
52
-
53
- type IsUnion<T, Whole = T> = T extends Whole ? ([Whole] extends [T] ? false : true) : never;
54
-
55
- type SingleStringLiteral<T> = T extends string
56
- ? string extends T
57
- ? never
58
- : true extends IsUnion<T>
59
- ? never
60
- : T
61
- : never;
57
+ type SchemaAssertionInput<S extends z.ZodObject> =
58
+ z.input<S> extends Assertion ? z.input<S> : never;
62
59
 
63
60
  type LiteralAssertionType<S extends z.ZodObject> =
64
- SchemaAssertion<S> extends { type: infer OutputType }
65
- ? z.input<S> extends { type: infer InputType }
66
- ? SingleStringLiteral<OutputType> extends infer Literal extends string
67
- ? [InputType, Literal] extends [Literal, InputType]
68
- ? Literal
69
- : never
70
- : never
71
- : never
72
- : never;
61
+ S["shape"]["type"] extends z.ZodLiteral<infer Literal extends string> ? Literal : never;
73
62
 
74
63
  export type AssertionPluginDefinition<S extends z.ZodObject> = {
75
64
  type: LiteralAssertionType<S>;
@@ -111,12 +100,34 @@ export const assertionPluginIssue = (definition: {
111
100
  * Define a schema-validated assertion plugin while inferring the config passed
112
101
  * to `evaluate` from the schema.
113
102
  */
103
+ export const baseAssertionFields = {
104
+ id: z.string().min(1).optional(),
105
+ weight: z.number().positive().optional(),
106
+ threshold: z.number().min(0).max(1).optional(),
107
+ name: z.string().optional(),
108
+ } as const;
109
+
114
110
  export const defineAssertion = <S extends z.ZodObject>(
115
111
  definition: AssertionPluginDefinition<S>,
116
- ): AssertionPlugin<SchemaAssertion<S>> => {
112
+ ): AssertionPlugin<SchemaAssertion<S>, SchemaAssertionInput<S>> => {
117
113
  const issue = assertionPluginIssue(definition);
118
114
  if (issue) throw new TypeError(issue);
119
- return definition as unknown as AssertionPlugin<SchemaAssertion<S>>;
115
+ return definition as unknown as AssertionPlugin<SchemaAssertion<S>, SchemaAssertionInput<S>>;
116
+ };
117
+
118
+ export const defineAssertionRegistry = <const Plugins extends readonly AssertionPlugin[]>(
119
+ ...plugins: Plugins
120
+ ): Plugins => {
121
+ const types = new Set<string>();
122
+ for (const plugin of plugins) {
123
+ const issue = assertionPluginIssue(plugin);
124
+ if (issue) throw new TypeError(issue);
125
+ if (types.has(plugin.type)) {
126
+ throw new TypeError(`assertion registry has duplicate type '${plugin.type}'`);
127
+ }
128
+ types.add(plugin.type);
129
+ }
130
+ return plugins;
120
131
  };
121
132
 
122
133
  export const GradingAssertionEntrySchema = z.object({
@@ -137,11 +148,19 @@ export const GradingAssertionEntrySchema = z.object({
137
148
  });
138
149
  export type GradingAssertionEntry = z.infer<typeof GradingAssertionEntrySchema>;
139
150
 
151
+ const SkippedGradingAssertionSchema = z.object({
152
+ id: z.string(),
153
+ type: z.string(),
154
+ name: z.string(),
155
+ reason: z.literal("judge-disabled"),
156
+ });
157
+
140
158
  export const GradingReportSchema = z.object({
141
159
  schemaVersion: z.literal(1),
142
160
  casePassed: z.boolean().nullable(),
143
161
  caseScore: z.number().nullable(),
144
162
  assertions: z.array(GradingAssertionEntrySchema),
163
+ skippedAssertions: z.array(SkippedGradingAssertionSchema).optional(),
145
164
  /**
146
165
  * Present when the subject run or grading did not complete. Assertions may
147
166
  * still contain partial diagnostics, but the report is not a clean verdict.
@@ -0,0 +1,65 @@
1
+ import type { AssertionPlugin } from "./assertion.js";
2
+ import type { EvalCase } from "./case.js";
3
+ import type { LifecycleHooksPlugin } from "./lifecycle-hooks.js";
4
+
5
+ export type EvalCaseExtensions = {
6
+ assertions: readonly AssertionPlugin[];
7
+ fixtures: readonly LifecycleHooksPlugin[];
8
+ };
9
+
10
+ const CASE_EXTENSIONS_KEY = Symbol.for("@prismatic-io/lux/eval-case-extensions");
11
+ const extensionState = globalThis as typeof globalThis & { [key: symbol]: unknown };
12
+ const sharedCaseExtensions = extensionState[CASE_EXTENSIONS_KEY];
13
+ // A project-local package and the CLI can load separate Lux module instances.
14
+ const caseExtensions =
15
+ sharedCaseExtensions instanceof WeakMap
16
+ ? (sharedCaseExtensions as WeakMap<object, EvalCaseExtensions>)
17
+ : new WeakMap<object, EvalCaseExtensions>();
18
+ extensionState[CASE_EXTENSIONS_KEY] = caseExtensions;
19
+
20
+ const uniqueBy = <T>(values: readonly T[], keyOf: (value: T) => string, kind: string): T[] => {
21
+ const unique = new Map<string, T>();
22
+ for (const value of values) {
23
+ const key = keyOf(value);
24
+ const existing = unique.get(key);
25
+ if (existing && existing !== value) {
26
+ throw new TypeError(`case has conflicting ${kind} definitions for '${key}'`);
27
+ }
28
+ unique.set(key, value);
29
+ }
30
+ return [...unique.values()];
31
+ };
32
+
33
+ export const collectEvalCaseExtensions = (
34
+ input: EvalCase,
35
+ assertions: readonly AssertionPlugin[] = [],
36
+ fixtures: readonly LifecycleHooksPlugin[] = [],
37
+ ): EvalCaseExtensions => {
38
+ const availableAssertions = uniqueBy(assertions, (plugin) => plugin.type, "assertion");
39
+ const byType = new Map(availableAssertions.map((plugin) => [plugin.type, plugin]));
40
+ return {
41
+ assertions: uniqueBy(
42
+ (input.assertions ?? []).flatMap((assertion) => {
43
+ const plugin = byType.get(assertion.type);
44
+ return plugin ? [plugin] : [];
45
+ }),
46
+ (plugin) => plugin.type,
47
+ "assertion",
48
+ ),
49
+ fixtures: uniqueBy(fixtures, (fixture) => fixture.name, "fixture"),
50
+ };
51
+ };
52
+
53
+ export const setEvalCaseExtensions = (evalCase: EvalCase, extensions: EvalCaseExtensions): void => {
54
+ if (extensions.assertions.length > 0 || extensions.fixtures.length > 0) {
55
+ caseExtensions.set(evalCase, extensions);
56
+ }
57
+ };
58
+
59
+ export const evalCaseExtensions = (evalCase: EvalCase): EvalCaseExtensions =>
60
+ caseExtensions.get(evalCase) ?? { assertions: [], fixtures: [] };
61
+
62
+ export const transferEvalCaseExtensions = (source: EvalCase, target: EvalCase): void => {
63
+ const extensions = caseExtensions.get(source);
64
+ if (extensions) caseExtensions.set(target, extensions);
65
+ };
package/src/core/case.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { z } from "zod";
2
+ import type { AssertionPlugin } from "./assertion.js";
3
+ import { collectEvalCaseExtensions, setEvalCaseExtensions } from "./case-authoring-state.js";
4
+ import type { LifecycleHooksPlugin } from "./lifecycle-hooks.js";
2
5
  import { formatZodIssues } from "./zod.js";
3
6
 
4
7
  export const FixturesRefSchema = z.union([
@@ -67,14 +70,62 @@ export const EvalCaseSchema = EvalCaseObjectSchema
67
70
  });
68
71
  export type EvalCase = z.infer<typeof EvalCaseSchema>;
69
72
 
70
- export const defineEvalCase = (input: EvalCase): EvalCase => {
71
- const parsed = EvalCaseSchema.safeParse(input);
72
- if (!parsed.success) {
73
- throw new Error(`Invalid eval case: ${formatZodIssues(parsed.error)}`);
74
- }
75
- return parsed.data;
73
+ type AssertionPlanFields = Pick<Assertion, "id" | "name" | "threshold" | "weight">;
74
+ type AssertionInputOf<Plugin> =
75
+ Plugin extends AssertionPlugin<Assertion, infer Input> ? Input & AssertionPlanFields : never;
76
+
77
+ export type EvalCaseInput<AllowedAssertion extends Assertion = Assertion> = Omit<
78
+ EvalCase,
79
+ "assertions"
80
+ > & {
81
+ assertions?: AllowedAssertion[];
82
+ };
83
+
84
+ export type EvalCaseAuthoringOptions<
85
+ Plugins extends readonly AssertionPlugin[] = readonly AssertionPlugin[],
86
+ > = {
87
+ assertions?: Plugins;
88
+ fixtures?: readonly LifecycleHooksPlugin[];
89
+ };
90
+
91
+ export type EvalCaseDefinition<AllowedAssertion extends Assertion = Assertion> = {
92
+ (input: EvalCaseInput<AllowedAssertion>): EvalCase;
93
+ /**
94
+ * Create a case definition with selectively activated lifecycle fixtures.
95
+ * Further calls to `extend` append fixtures in setup order.
96
+ */
97
+ extend<const Plugins extends readonly AssertionPlugin[] = readonly []>(
98
+ options: EvalCaseAuthoringOptions<Plugins>,
99
+ ): EvalCaseDefinition<AllowedAssertion | AssertionInputOf<Plugins[number]>>;
76
100
  };
77
101
 
102
+ export const createEvalCaseDefinition = <AllowedAssertion extends Assertion = Assertion>(
103
+ inheritedAssertions: readonly AssertionPlugin[] = [],
104
+ inheritedFixtures: readonly LifecycleHooksPlugin[] = [],
105
+ ): EvalCaseDefinition<AllowedAssertion> => {
106
+ const define = ((input: EvalCaseInput<AllowedAssertion>): EvalCase => {
107
+ const parsed = EvalCaseSchema.safeParse(input);
108
+ if (!parsed.success) {
109
+ throw new Error(`Invalid eval case: ${formatZodIssues(parsed.error)}`);
110
+ }
111
+ const extensions = collectEvalCaseExtensions(
112
+ parsed.data,
113
+ inheritedAssertions,
114
+ inheritedFixtures,
115
+ );
116
+ setEvalCaseExtensions(parsed.data, extensions);
117
+ return parsed.data;
118
+ }) as EvalCaseDefinition<AllowedAssertion>;
119
+ define.extend = (({ assertions = [], fixtures = [] }: EvalCaseAuthoringOptions) =>
120
+ createEvalCaseDefinition(
121
+ [...inheritedAssertions, ...assertions],
122
+ [...inheritedFixtures, ...fixtures],
123
+ )) as EvalCaseDefinition<AllowedAssertion>["extend"];
124
+ return define;
125
+ };
126
+
127
+ export const defineEvalCase = createEvalCaseDefinition();
128
+
78
129
  const PersistedFixturesRefSchema = z.union([
79
130
  z.string(),
80
131
  z.looseObject({ kind: z.literal("dir"), path: z.string() }),
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+
3
+ export const DriverMetricUnitSchema = z.enum([
4
+ "number",
5
+ "count",
6
+ "milliseconds",
7
+ "bytes",
8
+ "tokens",
9
+ "usd",
10
+ "ratio",
11
+ "percent",
12
+ "score",
13
+ ]);
14
+ export type DriverMetricUnit = z.infer<typeof DriverMetricUnitSchema>;
15
+
16
+ export const DriverMetricDescriptorSchema = z
17
+ .object({
18
+ unit: DriverMetricUnitSchema.default("number"),
19
+ label: z.string().min(1).max(120).optional(),
20
+ better: z.enum(["lower", "higher", "neutral"]).default("neutral"),
21
+ })
22
+ .strict();
23
+ export type DriverMetricDescriptor = z.infer<typeof DriverMetricDescriptorSchema>;
24
+
25
+ export const DriverMetricSchema = DriverMetricDescriptorSchema.extend({
26
+ value: z.number().finite(),
27
+ }).strict();
28
+ export type DriverMetric = z.infer<typeof DriverMetricSchema>;
29
+
30
+ export const DriverMetricsSchema = z.record(z.string().min(1).max(120), DriverMetricSchema);
31
+ export type DriverMetrics = z.infer<typeof DriverMetricsSchema>;
32
+
33
+ const ReportedDriverMetricSchema = z.union([z.number().finite(), DriverMetricSchema]);
34
+ export const ReportedDriverMetricsSchema = z.record(
35
+ z.string().min(1).max(120),
36
+ ReportedDriverMetricSchema,
37
+ );
38
+ export type ReportedDriverMetrics = z.input<typeof ReportedDriverMetricsSchema>;
39
+
40
+ export const normalizeDriverMetrics = (value: unknown): DriverMetrics => {
41
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
42
+ return Object.fromEntries(
43
+ Object.entries(value).flatMap(([name, metric]) => {
44
+ if (name.length === 0 || name.length > 120) return [];
45
+ const parsed = DriverMetricSchema.safeParse(
46
+ typeof metric === "number" ? { value: metric } : metric,
47
+ ).data;
48
+ return parsed ? [[name, parsed]] : [];
49
+ }),
50
+ );
51
+ };
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import type { EventHandler } from "./emitter.js";
3
3
  import type { Answer, Done, ErrorEvent, Interrupt, Progress, ReadyState } from "./events.js";
4
+ import type { FixtureValueBinding } from "./lifecycle-hooks.js";
4
5
 
5
6
  export const ArtifactSchema = z.object({
6
7
  path: z.string(),
@@ -28,6 +29,8 @@ export type StartContext = {
28
29
  caseId?: string;
29
30
  prompt: string;
30
31
  fixtures?: ResolvedFixtures | undefined;
32
+ /** Authored fixture values selected for this case. */
33
+ fixtureValues?: readonly FixtureValueBinding[] | undefined;
31
34
  /** Root containing persisted runs. Drivers may use it as an isolation boundary. */
32
35
  runsRoot?: string | undefined;
33
36
  runDir: string;
@@ -57,11 +57,22 @@ export const AnswerSchema = z.discriminatedUnion("kind", [
57
57
  ]);
58
58
  export type Answer = z.infer<typeof AnswerSchema>;
59
59
 
60
+ export const AgentAttributionSchema = z.object({
61
+ id: z.string().min(1),
62
+ role: z.enum(["main", "subagent"]),
63
+ parentId: z.exactOptional(z.string().min(1)),
64
+ spawnToolUseId: z.exactOptional(z.string().min(1)),
65
+ name: z.exactOptional(z.string().min(1)),
66
+ model: z.exactOptional(z.string().min(1)),
67
+ });
68
+ export type AgentAttribution = z.infer<typeof AgentAttributionSchema>;
69
+
60
70
  export const ProgressSchema = z.object({
61
71
  id: z.string(),
62
72
  kind: z.string(),
63
73
  payload: z.unknown(),
64
74
  ts: z.number(),
75
+ agent: z.exactOptional(AgentAttributionSchema),
65
76
  });
66
77
  export type Progress = z.infer<typeof ProgressSchema>;
67
78
 
@@ -69,8 +80,8 @@ export const DoneSchema = z.object({
69
80
  exitReason: z.literal("done"),
70
81
  /**
71
82
  * Driver-defined summary. The orchestrator recognizes `usage`, `cost`,
72
- * `modelCalls`, and `unpricedModelCalls` when present while preserving room
73
- * for domain-specific measurements.
83
+ * `modelCalls`, `unpricedModelCalls`, and scalar `metrics` when present while
84
+ * preserving room for other domain-specific evidence.
74
85
  */
75
86
  summary: z.unknown().optional(),
76
87
  });
@@ -82,6 +93,7 @@ export type ErrorEventReason = z.infer<typeof ErrorEventReasonSchema>;
82
93
  export const ErrorEventSchema = z.object({
83
94
  exitReason: ErrorEventReasonSchema,
84
95
  reason: z.string(),
96
+ summary: z.unknown().optional(),
85
97
  });
86
98
  export type ErrorEvent = z.infer<typeof ErrorEventSchema>;
87
99
 
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { DriverMetricDescriptorSchema } from "./driver-metrics.js";
2
3
  import { ConfidenceConfigSchema } from "./statistics.js";
3
4
  import { TokenUsageSchema } from "./usage.js";
4
5
  import { formatZodIssues } from "./zod.js";
@@ -415,6 +416,7 @@ export const CandidateEvaluationSchema = z.object({
415
416
  passRate: z.number().min(0).max(1),
416
417
  casePassRates: z.record(z.string(), z.number().min(0).max(1)),
417
418
  metrics: z.record(z.string(), z.number()),
419
+ metricDefinitions: z.exactOptional(z.record(z.string(), DriverMetricDescriptorSchema)),
418
420
  metricCalls: z.exactOptional(z.number().int().nonnegative()),
419
421
  judgeCalls: z.exactOptional(z.number().int().nonnegative()),
420
422
  unpricedModelCalls: z.exactOptional(z.number().int().nonnegative()),
package/src/core/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export * from "./action-events.js";
2
+ export * from "./agent-activity.js";
3
+ export * from "./agent-events.js";
1
4
  export * from "./annotation.js";
2
5
  export * from "./answerer.js";
3
6
  export * from "./artifact-evidence.js";
@@ -6,6 +9,7 @@ export * from "./assertion.js";
6
9
  export * from "./case.js";
7
10
  export * from "./conversation-turn.js";
8
11
  export * from "./driver.js";
12
+ export * from "./driver-metrics.js";
9
13
  export * from "./emitter.js";
10
14
  export * from "./events.js";
11
15
  export * from "./exec.js";
@@ -2,6 +2,7 @@ import { valueHash } from "./hash.js";
2
2
  import {
3
3
  type CaseLifecycleHookContext,
4
4
  defineLifecycleHooks,
5
+ type FixtureValue,
5
6
  type LifecycleHookCleanup,
6
7
  type LifecycleHooksPlugin,
7
8
  type SuiteLifecycleHookContext,
@@ -22,6 +23,8 @@ export type LifecycleFixturesDefinition = {
22
23
  * to execution identity.
23
24
  */
24
25
  identity?: unknown;
26
+ /** Public, serializable data made available to the selected case's driver. */
27
+ value?: FixtureValue;
25
28
  suite?: LifecycleFixture<SuiteLifecycleHookContext>;
26
29
  case?: LifecycleFixture<CaseLifecycleHookContext>;
27
30
  };
@@ -109,8 +112,8 @@ const activateFixture = async <Context extends SuiteLifecycleHookContext>(
109
112
  * Setup belongs before `use`; teardown belongs after it.
110
113
  */
111
114
  export const defineFixtures = (definition: LifecycleFixturesDefinition): LifecycleHooksPlugin => {
112
- if (!definition.suite && !definition.case) {
113
- throw new TypeError("fixtures plugin must define suite or case");
115
+ if (!definition.suite && !definition.case && definition.value === undefined) {
116
+ throw new TypeError("fixtures plugin must define value, suite, or case");
114
117
  }
115
118
  if (definition.suite !== undefined && typeof definition.suite !== "function") {
116
119
  throw new TypeError("fixtures plugin suite must be a function");
@@ -123,8 +126,10 @@ export const defineFixtures = (definition: LifecycleFixturesDefinition): Lifecyc
123
126
  return defineLifecycleHooks({
124
127
  name: definition.name,
125
128
  pluginVersion: definition.pluginVersion,
129
+ ...(definition.value === undefined ? {} : { value: definition.value }),
126
130
  ...(definition.identity === undefined ? {} : { identity: definition.identity }),
127
131
  implementationHash: valueHash({
132
+ value: definition.value,
128
133
  identity: definition.identity,
129
134
  suite: definition.suite,
130
135
  case: definition.case,
@@ -1,7 +1,15 @@
1
+ import { z } from "zod";
1
2
  import { valueHash } from "./hash.js";
2
3
 
3
4
  export type LifecycleHookCleanup = () => void | Promise<void>;
4
5
  export type LifecycleHookSetupResult = LifecycleHookCleanup | undefined;
6
+ export const FixtureValueSchema = z.json();
7
+ export type FixtureValue = z.infer<typeof FixtureValueSchema>;
8
+
9
+ export type FixtureValueBinding = {
10
+ name: string;
11
+ value: FixtureValue;
12
+ };
5
13
 
6
14
  export type SuiteLifecycleHookItem = {
7
15
  id: string;
@@ -27,6 +35,8 @@ export type LifecycleHooksPlugin = {
27
35
  name: string;
28
36
  /** Author-maintained identity for setup and teardown behavior. */
29
37
  pluginVersion: string;
38
+ /** Public, serializable data made available to the selected case's driver. */
39
+ value?: FixtureValue;
30
40
  /**
31
41
  * Public, non-secret values captured by hook closures and relevant to
32
42
  * execution identity. Included in the implementation hash.
@@ -75,8 +85,11 @@ export const lifecycleHooksPluginIssue = (
75
85
  if (definition.setupCase !== undefined && typeof definition.setupCase !== "function") {
76
86
  return "lifecycle hooks plugin setupCase must be a function";
77
87
  }
78
- if (!definition.setupSuite && !definition.setupCase) {
79
- return "lifecycle hooks plugin must define setupSuite or setupCase";
88
+ if (definition.value !== undefined && !FixtureValueSchema.safeParse(definition.value).success) {
89
+ return "lifecycle hooks plugin value must be JSON-serializable";
90
+ }
91
+ if (!definition.setupSuite && !definition.setupCase && definition.value === undefined) {
92
+ return "lifecycle hooks plugin must define a value, setupSuite, or setupCase";
80
93
  }
81
94
  return undefined;
82
95
  };
@@ -93,6 +106,7 @@ export const defineLifecycleHooks = (definition: LifecycleHooksPlugin): Lifecycl
93
106
  implementationHash:
94
107
  definition.implementationHash ??
95
108
  valueHash({
109
+ value: definition.value,
96
110
  identity: definition.identity,
97
111
  setupSuite: definition.setupSuite,
98
112
  setupCase: definition.setupCase,
package/src/core/run.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { z } from "zod";
2
+ import { ActionTimingSchema } from "./action-events.js";
3
+ import { AgentActivitySchema } from "./agent-activity.js";
2
4
  import { EvalCaseSchema } from "./case.js";
3
5
  import { ArtifactSchema } from "./driver.js";
6
+ import { DriverMetricsSchema } from "./driver-metrics.js";
4
7
  import { ErrorEventReasonSchema, ReadyStateSchema, RunEventSchema } from "./events.js";
5
8
  import { ToolOutcomeSummarySchema } from "./tool-events.js";
6
9
  import { RunUsageSchema } from "./usage.js";
@@ -112,6 +115,8 @@ export const RunMetadataSchema = z.object({
112
115
  }),
113
116
  ),
114
117
  cost: z.exactOptional(z.object({ usd: z.exactOptional(z.number()) })),
118
+ /** Driver-defined scalar measurements with display and optimization semantics. */
119
+ driverMetrics: z.exactOptional(DriverMetricsSchema),
115
120
  interruptCounts: z.object({ ask: z.number(), approve: z.number(), custom: z.number() }),
116
121
  /** Legacy raw count of progress events whose kind is `tool-call`. */
117
122
  toolCallCount: z.exactOptional(z.number()),
@@ -124,6 +129,10 @@ export const RunMetadataSchema = z.object({
124
129
  ),
125
130
  /** Wall-clock per phase, derived from the lifecycle event stream. */
126
131
  phaseDurationMs: z.exactOptional(PhaseDurationsSchema),
132
+ /** Driver-reported semantic action latency, measured from `start-driver`. */
133
+ actionTiming: z.exactOptional(ActionTimingSchema),
134
+ /** Per-agent lifecycle and activity derived from the attributed event stream. */
135
+ agentActivity: z.exactOptional(z.array(AgentActivitySchema)),
127
136
  /**
128
137
  * Harness/infrastructure failure that invalidates this run as candidate
129
138
  * evidence. A driver-reported subject failure intentionally leaves this
package/src/core/wire.ts CHANGED
@@ -47,6 +47,7 @@ const ErrorEventSchema = z.object({
47
47
  type: z.literal("error"),
48
48
  exitReason: ErrorEventReasonSchema.default("error"),
49
49
  reason: z.string(),
50
+ summary: z.unknown().optional(),
50
51
  });
51
52
 
52
53
  export const WireEventSchema = z.discriminatedUnion("type", [
@@ -85,7 +86,10 @@ const wireEventToParsed = (event: WireEvent): ParsedDriverEvent => {
85
86
  if (event.type === "done") {
86
87
  return { tag: "done", done: { exitReason: "done", summary: event.summary } };
87
88
  }
88
- return { tag: "error", error: { exitReason: event.exitReason, reason: event.reason } };
89
+ return {
90
+ tag: "error",
91
+ error: { exitReason: event.exitReason, reason: event.reason, summary: event.summary },
92
+ };
89
93
  };
90
94
 
91
95
  /**