@prismatic-io/lux 0.0.2-preview.14 → 0.0.2-preview.16

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 (388) hide show
  1. package/lib/answerers/claude-code/index.d.ts +14 -2
  2. package/lib/answerers/claude-code/index.d.ts.map +1 -1
  3. package/lib/answerers/claude-code/index.js +3 -3
  4. package/lib/answerers/claude-code/index.js.map +1 -1
  5. package/lib/answerers/persona/index.d.ts +28 -2
  6. package/lib/answerers/persona/index.d.ts.map +1 -1
  7. package/lib/answerers/persona/index.js +3 -3
  8. package/lib/answerers/persona/index.js.map +1 -1
  9. package/lib/answerers/terminal/index.d.ts +8 -2
  10. package/lib/answerers/terminal/index.d.ts.map +1 -1
  11. package/lib/answerers/terminal/index.js +3 -2
  12. package/lib/answerers/terminal/index.js.map +1 -1
  13. package/lib/assertions/core/resource-checks.d.ts.map +1 -1
  14. package/lib/assertions/core/resource-checks.js +3 -5
  15. package/lib/assertions/core/resource-checks.js.map +1 -1
  16. package/lib/assertions/rubric/index.d.ts.map +1 -1
  17. package/lib/assertions/rubric/index.js +7 -1
  18. package/lib/assertions/rubric/index.js.map +1 -1
  19. package/lib/assertions/rubric/internal.d.ts +16 -5
  20. package/lib/assertions/rubric/internal.d.ts.map +1 -1
  21. package/lib/assertions/rubric/internal.js +10 -6
  22. package/lib/assertions/rubric/internal.js.map +1 -1
  23. package/lib/authoring.d.ts +32 -2
  24. package/lib/authoring.d.ts.map +1 -1
  25. package/lib/authoring.js +33 -1
  26. package/lib/authoring.js.map +1 -1
  27. package/lib/cli/bin.js +2 -13
  28. package/lib/cli/bin.js.map +1 -1
  29. package/lib/cli/command-runtime.d.ts +3 -3
  30. package/lib/cli/command-runtime.d.ts.map +1 -1
  31. package/lib/cli/command-runtime.js +17 -6
  32. package/lib/cli/command-runtime.js.map +1 -1
  33. package/lib/cli/init-templates.d.ts +11 -0
  34. package/lib/cli/init-templates.d.ts.map +1 -0
  35. package/lib/cli/init-templates.js +207 -0
  36. package/lib/cli/init-templates.js.map +1 -0
  37. package/lib/cli/init.d.ts +7 -0
  38. package/lib/cli/init.d.ts.map +1 -1
  39. package/lib/cli/init.js +23 -173
  40. package/lib/cli/init.js.map +1 -1
  41. package/lib/cli/output-schemas.d.ts +1399 -0
  42. package/lib/cli/output-schemas.d.ts.map +1 -0
  43. package/lib/cli/output-schemas.js +91 -0
  44. package/lib/cli/output-schemas.js.map +1 -0
  45. package/lib/cli/program.d.ts +135 -4
  46. package/lib/cli/program.d.ts.map +1 -1
  47. package/lib/cli/program.js +725 -520
  48. package/lib/cli/program.js.map +1 -1
  49. package/lib/cli/render/reporter.d.ts.map +1 -1
  50. package/lib/cli/render/reporter.js +24 -4
  51. package/lib/cli/render/reporter.js.map +1 -1
  52. package/lib/cli/run-options.d.ts +0 -4
  53. package/lib/cli/run-options.d.ts.map +1 -1
  54. package/lib/cli/run-options.js +8 -19
  55. package/lib/cli/run-options.js.map +1 -1
  56. package/lib/cli/view.d.ts +5 -3
  57. package/lib/cli/view.d.ts.map +1 -1
  58. package/lib/cli/view.js +120 -78
  59. package/lib/cli/view.js.map +1 -1
  60. package/lib/core/annotation.d.ts +6 -2
  61. package/lib/core/annotation.d.ts.map +1 -1
  62. package/lib/core/annotation.js +2 -1
  63. package/lib/core/annotation.js.map +1 -1
  64. package/lib/core/answerer.d.ts +12 -0
  65. package/lib/core/answerer.d.ts.map +1 -1
  66. package/lib/core/answerer.js +2 -0
  67. package/lib/core/answerer.js.map +1 -1
  68. package/lib/core/assertion.d.ts +3 -0
  69. package/lib/core/assertion.d.ts.map +1 -1
  70. package/lib/core/assertion.js +3 -0
  71. package/lib/core/assertion.js.map +1 -1
  72. package/lib/core/case.d.ts +24 -2
  73. package/lib/core/case.d.ts.map +1 -1
  74. package/lib/core/case.js +5 -1
  75. package/lib/core/case.js.map +1 -1
  76. package/lib/core/driver.d.ts +40 -0
  77. package/lib/core/driver.d.ts.map +1 -1
  78. package/lib/core/driver.js +12 -0
  79. package/lib/core/driver.js.map +1 -1
  80. package/lib/core/environment.d.ts +18 -0
  81. package/lib/core/environment.d.ts.map +1 -0
  82. package/lib/core/environment.js +21 -0
  83. package/lib/core/environment.js.map +1 -0
  84. package/lib/core/evaluation-clusters.d.ts +17 -0
  85. package/lib/core/evaluation-clusters.d.ts.map +1 -0
  86. package/lib/core/evaluation-clusters.js +23 -0
  87. package/lib/core/evaluation-clusters.js.map +1 -0
  88. package/lib/core/experiment.d.ts +11 -0
  89. package/lib/core/experiment.d.ts.map +1 -1
  90. package/lib/core/experiment.js +31 -17
  91. package/lib/core/experiment.js.map +1 -1
  92. package/lib/core/file-tree.d.ts +7 -0
  93. package/lib/core/file-tree.d.ts.map +1 -0
  94. package/lib/core/file-tree.js +35 -0
  95. package/lib/core/file-tree.js.map +1 -0
  96. package/lib/core/index.d.ts +1 -0
  97. package/lib/core/index.d.ts.map +1 -1
  98. package/lib/core/index.js +1 -0
  99. package/lib/core/index.js.map +1 -1
  100. package/lib/core/lifecycle-fixtures.d.ts.map +1 -1
  101. package/lib/core/lifecycle-fixtures.js +4 -2
  102. package/lib/core/lifecycle-fixtures.js.map +1 -1
  103. package/lib/core/platform-process.d.ts +5 -1
  104. package/lib/core/platform-process.d.ts.map +1 -1
  105. package/lib/core/platform-process.js +48 -10
  106. package/lib/core/platform-process.js.map +1 -1
  107. package/lib/core/run.d.ts +93 -1
  108. package/lib/core/run.d.ts.map +1 -1
  109. package/lib/core/run.js +16 -0
  110. package/lib/core/run.js.map +1 -1
  111. package/lib/core/usage.d.ts +5 -0
  112. package/lib/core/usage.d.ts.map +1 -1
  113. package/lib/core/usage.js +3 -0
  114. package/lib/core/usage.js.map +1 -1
  115. package/lib/drivers/claude-code/index.d.ts +65 -2
  116. package/lib/drivers/claude-code/index.d.ts.map +1 -1
  117. package/lib/drivers/claude-code/index.js +30 -3
  118. package/lib/drivers/claude-code/index.js.map +1 -1
  119. package/lib/drivers/codex/app-events.d.ts.map +1 -1
  120. package/lib/drivers/codex/app-events.js +41 -30
  121. package/lib/drivers/codex/app-events.js.map +1 -1
  122. package/lib/drivers/codex/exec-events.d.ts +4 -0
  123. package/lib/drivers/codex/exec-events.d.ts.map +1 -0
  124. package/lib/drivers/codex/exec-events.js +230 -0
  125. package/lib/drivers/codex/exec-events.js.map +1 -0
  126. package/lib/drivers/codex/index.d.ts +76 -4
  127. package/lib/drivers/codex/index.d.ts.map +1 -1
  128. package/lib/drivers/codex/index.js +35 -232
  129. package/lib/drivers/codex/index.js.map +1 -1
  130. package/lib/drivers/cursor/acp-transport.d.ts +38 -0
  131. package/lib/drivers/cursor/acp-transport.d.ts.map +1 -0
  132. package/lib/drivers/cursor/acp-transport.js +152 -0
  133. package/lib/drivers/cursor/acp-transport.js.map +1 -0
  134. package/lib/drivers/cursor/config.d.ts +22 -0
  135. package/lib/drivers/cursor/config.d.ts.map +1 -0
  136. package/lib/drivers/cursor/config.js +26 -0
  137. package/lib/drivers/cursor/config.js.map +1 -0
  138. package/lib/drivers/cursor/events.d.ts +13 -0
  139. package/lib/drivers/cursor/events.d.ts.map +1 -0
  140. package/lib/drivers/cursor/events.js +73 -0
  141. package/lib/drivers/cursor/events.js.map +1 -0
  142. package/lib/drivers/cursor/index.d.ts +37 -0
  143. package/lib/drivers/cursor/index.d.ts.map +1 -0
  144. package/lib/drivers/cursor/index.js +276 -0
  145. package/lib/drivers/cursor/index.js.map +1 -0
  146. package/lib/drivers/cursor/interaction.d.ts +7 -0
  147. package/lib/drivers/cursor/interaction.d.ts.map +1 -0
  148. package/lib/drivers/cursor/interaction.js +66 -0
  149. package/lib/drivers/cursor/interaction.js.map +1 -0
  150. package/lib/drivers/mcp/index.d.ts +35 -2
  151. package/lib/drivers/mcp/index.d.ts.map +1 -1
  152. package/lib/drivers/mcp/index.js +16 -3
  153. package/lib/drivers/mcp/index.js.map +1 -1
  154. package/lib/drivers/shared/experiment-behavior.d.ts +13 -0
  155. package/lib/drivers/shared/experiment-behavior.d.ts.map +1 -0
  156. package/lib/drivers/shared/experiment-behavior.js +197 -0
  157. package/lib/drivers/shared/experiment-behavior.js.map +1 -0
  158. package/lib/drivers/shared/experiment-policy.d.ts +4 -0
  159. package/lib/drivers/shared/experiment-policy.d.ts.map +1 -0
  160. package/lib/drivers/shared/experiment-policy.js +143 -0
  161. package/lib/drivers/shared/experiment-policy.js.map +1 -0
  162. package/lib/drivers/subprocess/index.d.ts +37 -2
  163. package/lib/drivers/subprocess/index.d.ts.map +1 -1
  164. package/lib/drivers/subprocess/index.js +46 -6
  165. package/lib/drivers/subprocess/index.js.map +1 -1
  166. package/lib/index.d.ts +6 -5
  167. package/lib/index.d.ts.map +1 -1
  168. package/lib/index.js +4 -3
  169. package/lib/index.js.map +1 -1
  170. package/lib/orchestrator/annotation-loader.d.ts +6 -2
  171. package/lib/orchestrator/annotation-loader.d.ts.map +1 -1
  172. package/lib/orchestrator/annotation-loader.js +8 -0
  173. package/lib/orchestrator/annotation-loader.js.map +1 -1
  174. package/lib/orchestrator/annotation-store.d.ts.map +1 -1
  175. package/lib/orchestrator/annotation-store.js +16 -2
  176. package/lib/orchestrator/annotation-store.js.map +1 -1
  177. package/lib/orchestrator/campaign-lifecycle.d.ts +1 -0
  178. package/lib/orchestrator/campaign-lifecycle.d.ts.map +1 -1
  179. package/lib/orchestrator/campaign-lifecycle.js +4 -1
  180. package/lib/orchestrator/campaign-lifecycle.js.map +1 -1
  181. package/lib/orchestrator/compare.d.ts +4 -0
  182. package/lib/orchestrator/compare.d.ts.map +1 -1
  183. package/lib/orchestrator/compare.js +13 -1
  184. package/lib/orchestrator/compare.js.map +1 -1
  185. package/lib/orchestrator/comparison-identity.d.ts.map +1 -1
  186. package/lib/orchestrator/comparison-identity.js +1 -0
  187. package/lib/orchestrator/comparison-identity.js.map +1 -1
  188. package/lib/orchestrator/config.d.ts +42 -0
  189. package/lib/orchestrator/config.d.ts.map +1 -1
  190. package/lib/orchestrator/config.js +6 -2
  191. package/lib/orchestrator/config.js.map +1 -1
  192. package/lib/orchestrator/discover.d.ts +1 -0
  193. package/lib/orchestrator/discover.d.ts.map +1 -1
  194. package/lib/orchestrator/discover.js +7 -1
  195. package/lib/orchestrator/discover.js.map +1 -1
  196. package/lib/orchestrator/doctor.d.ts.map +1 -1
  197. package/lib/orchestrator/doctor.js +34 -26
  198. package/lib/orchestrator/doctor.js.map +1 -1
  199. package/lib/orchestrator/driver-capabilities.d.ts +3 -0
  200. package/lib/orchestrator/driver-capabilities.d.ts.map +1 -0
  201. package/lib/orchestrator/driver-capabilities.js +10 -0
  202. package/lib/orchestrator/driver-capabilities.js.map +1 -0
  203. package/lib/orchestrator/driver-identity.d.ts +5 -0
  204. package/lib/orchestrator/driver-identity.d.ts.map +1 -0
  205. package/lib/orchestrator/driver-identity.js +41 -0
  206. package/lib/orchestrator/driver-identity.js.map +1 -0
  207. package/lib/orchestrator/experiment-context.d.ts +10 -0
  208. package/lib/orchestrator/experiment-context.d.ts.map +1 -1
  209. package/lib/orchestrator/experiment-contracts.d.ts +10 -0
  210. package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
  211. package/lib/orchestrator/experiment-corpus.d.ts.map +1 -1
  212. package/lib/orchestrator/experiment-corpus.js +17 -19
  213. package/lib/orchestrator/experiment-corpus.js.map +1 -1
  214. package/lib/orchestrator/experiment-effects.d.ts +33 -0
  215. package/lib/orchestrator/experiment-effects.d.ts.map +1 -0
  216. package/lib/orchestrator/experiment-effects.js +44 -0
  217. package/lib/orchestrator/experiment-effects.js.map +1 -0
  218. package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -1
  219. package/lib/orchestrator/experiment-evaluation.js +36 -3
  220. package/lib/orchestrator/experiment-evaluation.js.map +1 -1
  221. package/lib/orchestrator/experiment-identity.d.ts.map +1 -1
  222. package/lib/orchestrator/experiment-identity.js +18 -29
  223. package/lib/orchestrator/experiment-identity.js.map +1 -1
  224. package/lib/orchestrator/experiment-loader.d.ts +7 -2
  225. package/lib/orchestrator/experiment-loader.d.ts.map +1 -1
  226. package/lib/orchestrator/experiment-report.d.ts +70 -0
  227. package/lib/orchestrator/experiment-report.d.ts.map +1 -1
  228. package/lib/orchestrator/experiment-report.js +9 -1
  229. package/lib/orchestrator/experiment-report.js.map +1 -1
  230. package/lib/orchestrator/experiment-runs.d.ts +22 -1
  231. package/lib/orchestrator/experiment-runs.d.ts.map +1 -1
  232. package/lib/orchestrator/experiment-runs.js +13 -5
  233. package/lib/orchestrator/experiment-runs.js.map +1 -1
  234. package/lib/orchestrator/experiment-runtime-identity.d.ts +0 -16
  235. package/lib/orchestrator/experiment-runtime-identity.d.ts.map +1 -1
  236. package/lib/orchestrator/experiment-runtime-identity.js +5 -220
  237. package/lib/orchestrator/experiment-runtime-identity.js.map +1 -1
  238. package/lib/orchestrator/experiment-selection.d.ts.map +1 -1
  239. package/lib/orchestrator/experiment-selection.js +52 -0
  240. package/lib/orchestrator/experiment-selection.js.map +1 -1
  241. package/lib/orchestrator/experiment.d.ts +0 -2
  242. package/lib/orchestrator/experiment.d.ts.map +1 -1
  243. package/lib/orchestrator/experiment.js +23 -15
  244. package/lib/orchestrator/experiment.js.map +1 -1
  245. package/lib/orchestrator/fs-read.d.ts +1 -6
  246. package/lib/orchestrator/fs-read.d.ts.map +1 -1
  247. package/lib/orchestrator/fs-read.js +2 -32
  248. package/lib/orchestrator/fs-read.js.map +1 -1
  249. package/lib/orchestrator/grader-experiment.d.ts +17 -1
  250. package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
  251. package/lib/orchestrator/grader-experiment.js +52 -7
  252. package/lib/orchestrator/grader-experiment.js.map +1 -1
  253. package/lib/orchestrator/grading.d.ts.map +1 -1
  254. package/lib/orchestrator/grading.js +13 -6
  255. package/lib/orchestrator/grading.js.map +1 -1
  256. package/lib/orchestrator/list-runs.d.ts +1 -0
  257. package/lib/orchestrator/list-runs.d.ts.map +1 -1
  258. package/lib/orchestrator/list-runs.js +27 -6
  259. package/lib/orchestrator/list-runs.js.map +1 -1
  260. package/lib/orchestrator/optimization-lifecycle.d.ts.map +1 -1
  261. package/lib/orchestrator/optimization-lifecycle.js +3 -1
  262. package/lib/orchestrator/optimization-lifecycle.js.map +1 -1
  263. package/lib/orchestrator/orchestrator.d.ts +6 -1
  264. package/lib/orchestrator/orchestrator.d.ts.map +1 -1
  265. package/lib/orchestrator/orchestrator.js +17 -143
  266. package/lib/orchestrator/orchestrator.js.map +1 -1
  267. package/lib/orchestrator/paired-effects.d.ts +34 -0
  268. package/lib/orchestrator/paired-effects.d.ts.map +1 -0
  269. package/lib/orchestrator/paired-effects.js +158 -0
  270. package/lib/orchestrator/paired-effects.js.map +1 -0
  271. package/lib/orchestrator/provenance.d.ts +5 -1
  272. package/lib/orchestrator/provenance.d.ts.map +1 -1
  273. package/lib/orchestrator/provenance.js +16 -3
  274. package/lib/orchestrator/provenance.js.map +1 -1
  275. package/lib/orchestrator/regrade.d.ts +1 -0
  276. package/lib/orchestrator/regrade.d.ts.map +1 -1
  277. package/lib/orchestrator/run-execution.d.ts +2 -0
  278. package/lib/orchestrator/run-execution.d.ts.map +1 -1
  279. package/lib/orchestrator/run-execution.js +18 -3
  280. package/lib/orchestrator/run-execution.js.map +1 -1
  281. package/lib/orchestrator/run-lifecycle.d.ts +1 -0
  282. package/lib/orchestrator/run-lifecycle.d.ts.map +1 -1
  283. package/lib/orchestrator/run-lifecycle.js +3 -2
  284. package/lib/orchestrator/run-lifecycle.js.map +1 -1
  285. package/lib/orchestrator/suite-runs.d.ts +3 -1
  286. package/lib/orchestrator/suite-runs.d.ts.map +1 -1
  287. package/lib/orchestrator/suite-runs.js +12 -2
  288. package/lib/orchestrator/suite-runs.js.map +1 -1
  289. package/lib/orchestrator/suite-summary.d.ts.map +1 -1
  290. package/lib/orchestrator/suite-summary.js +2 -1
  291. package/lib/orchestrator/suite-summary.js.map +1 -1
  292. package/lib/previewer/server.d.ts +6 -0
  293. package/lib/previewer/server.d.ts.map +1 -1
  294. package/lib/previewer/server.js +21 -5
  295. package/lib/previewer/server.js.map +1 -1
  296. package/package.json +2 -2
  297. package/skills/lux/references/cli.md +5 -2
  298. package/skills/lux/references/eval-authoring.md +0 -7
  299. package/skills/lux-answerer/SKILL.md +1 -1
  300. package/src/answerers/claude-code/index.ts +3 -3
  301. package/src/answerers/persona/index.ts +3 -3
  302. package/src/answerers/terminal/index.ts +4 -9
  303. package/src/assertions/core/resource-checks.ts +3 -5
  304. package/src/assertions/rubric/index.ts +7 -1
  305. package/src/assertions/rubric/internal.ts +13 -8
  306. package/src/authoring.ts +331 -2
  307. package/src/cli/bin.ts +2 -12
  308. package/src/cli/command-runtime.ts +20 -9
  309. package/src/cli/init-templates.ts +223 -0
  310. package/src/cli/init.ts +38 -184
  311. package/src/cli/output-schemas.ts +106 -0
  312. package/src/cli/program.ts +621 -548
  313. package/src/cli/render/reporter.ts +25 -4
  314. package/src/cli/run-options.ts +8 -25
  315. package/src/cli/view.ts +45 -84
  316. package/src/core/annotation.ts +2 -1
  317. package/src/core/answerer.ts +9 -0
  318. package/src/core/assertion.ts +3 -0
  319. package/src/core/case.ts +5 -1
  320. package/src/core/driver.ts +38 -0
  321. package/src/core/environment.ts +22 -0
  322. package/src/core/evaluation-clusters.ts +35 -0
  323. package/src/core/experiment.ts +60 -23
  324. package/src/core/file-tree.ts +35 -0
  325. package/src/core/index.ts +1 -0
  326. package/src/core/lifecycle-fixtures.ts +4 -2
  327. package/src/core/platform-process.ts +54 -6
  328. package/src/core/run.ts +20 -0
  329. package/src/core/usage.ts +8 -0
  330. package/src/drivers/claude-code/index.ts +34 -3
  331. package/src/drivers/codex/app-events.ts +43 -33
  332. package/src/drivers/codex/exec-events.ts +258 -0
  333. package/src/drivers/codex/index.ts +37 -262
  334. package/src/drivers/cursor/README.md +57 -0
  335. package/src/drivers/cursor/acp-transport.ts +182 -0
  336. package/src/drivers/cursor/config.ts +29 -0
  337. package/src/drivers/cursor/events.ts +83 -0
  338. package/src/drivers/cursor/index.ts +316 -0
  339. package/src/drivers/cursor/interaction.ts +90 -0
  340. package/src/drivers/mcp/index.ts +16 -3
  341. package/src/drivers/shared/experiment-behavior.ts +214 -0
  342. package/src/drivers/shared/experiment-policy.ts +195 -0
  343. package/src/drivers/subprocess/index.ts +45 -6
  344. package/src/index.ts +16 -5
  345. package/src/orchestrator/annotation-loader.ts +10 -0
  346. package/src/orchestrator/annotation-store.ts +24 -3
  347. package/src/orchestrator/campaign-lifecycle.ts +5 -1
  348. package/src/orchestrator/compare.ts +16 -1
  349. package/src/orchestrator/comparison-identity.ts +1 -0
  350. package/src/orchestrator/config.ts +6 -2
  351. package/src/orchestrator/discover.ts +8 -1
  352. package/src/orchestrator/doctor.ts +40 -22
  353. package/src/orchestrator/driver-capabilities.ts +16 -0
  354. package/src/orchestrator/driver-identity.ts +59 -0
  355. package/src/orchestrator/experiment-corpus.ts +23 -28
  356. package/src/orchestrator/experiment-effects.ts +61 -0
  357. package/src/orchestrator/experiment-evaluation.ts +45 -5
  358. package/src/orchestrator/experiment-identity.ts +27 -36
  359. package/src/orchestrator/experiment-report.ts +21 -1
  360. package/src/orchestrator/experiment-runs.ts +11 -4
  361. package/src/orchestrator/experiment-runtime-identity.ts +5 -243
  362. package/src/orchestrator/experiment-selection.ts +77 -0
  363. package/src/orchestrator/experiment.ts +26 -24
  364. package/src/orchestrator/fs-read.ts +2 -32
  365. package/src/orchestrator/grader-experiment.ts +72 -8
  366. package/src/orchestrator/grading.ts +15 -6
  367. package/src/orchestrator/list-runs.ts +29 -6
  368. package/src/orchestrator/optimization-lifecycle.ts +4 -1
  369. package/src/orchestrator/orchestrator.ts +26 -183
  370. package/src/orchestrator/paired-effects.ts +209 -0
  371. package/src/orchestrator/provenance.ts +23 -3
  372. package/src/orchestrator/run-execution.ts +18 -3
  373. package/src/orchestrator/run-lifecycle.ts +4 -2
  374. package/src/orchestrator/suite-runs.ts +10 -1
  375. package/src/orchestrator/suite-summary.ts +2 -1
  376. package/src/previewer/server.ts +27 -5
  377. package/viewer/app.js +27 -6
  378. package/viewer/detail.js +22 -5
  379. package/lib/answerers/scripted/index.d.ts +0 -18
  380. package/lib/answerers/scripted/index.d.ts.map +0 -1
  381. package/lib/answerers/scripted/index.js +0 -63
  382. package/lib/answerers/scripted/index.js.map +0 -1
  383. package/lib/cli/skills.d.ts +0 -38
  384. package/lib/cli/skills.d.ts.map +0 -1
  385. package/lib/cli/skills.js +0 -84
  386. package/lib/cli/skills.js.map +0 -1
  387. package/src/answerers/scripted/index.ts +0 -78
  388. package/src/cli/skills.ts +0 -129
@@ -1,4 +1,5 @@
1
- import { basename, extname, isAbsolute, join, resolve } from "node:path";
1
+ import { basename, extname, join, resolve } from "node:path";
2
+ import { type EnvironmentDeclaration, EnvironmentDeclarationSchema } from "../core/environment.js";
2
3
  import {
3
4
  type Answerer,
4
5
  type AnswererModelCallAccounting,
@@ -24,6 +25,7 @@ import {
24
25
  registryForEvalCase,
25
26
  valuesForFixtures,
26
27
  } from "./case-extensions.js";
28
+ import { validateDriverCapabilities } from "./driver-capabilities.js";
27
29
  import { evaluatorImplementationHash } from "./evaluator-identity.js";
28
30
  import {
29
31
  assertionEvaluatorIdentities,
@@ -102,6 +104,7 @@ const recordLifecycleCleanupFailure = async (result: RunResult, error: unknown):
102
104
  };
103
105
 
104
106
  export type OrchestratorOptions = {
107
+ environment?: EnvironmentDeclaration | undefined;
105
108
  registry: Registry;
106
109
  /** Answerer used when a case names none. Defaults to "persona". */
107
110
  defaultAnswerer?: AnswererRef;
@@ -114,6 +117,8 @@ export type OrchestratorOptions = {
114
117
  };
115
118
 
116
119
  export type RunRequest = {
120
+ /** Effective parent suite worker limit; direct runs default to one. */
121
+ suiteConcurrency?: number | undefined;
117
122
  /** Path to a TS case file. Mutually exclusive with `case`. */
118
123
  casePath?: string;
119
124
  /** Pre-loaded EvalCase. Mutually exclusive with `casePath`. */
@@ -265,191 +270,22 @@ const driverConfigOf = (ref: DriverRef): unknown =>
265
270
  const answererConfigOf = (ref: AnswererRef): unknown =>
266
271
  typeof ref === "object" ? ref.config : undefined;
267
272
 
268
- const EXPERIMENT_BEHAVIOR_SOURCE_ARGS = new Set([
269
- "--continue",
270
- "-c",
271
- "--fork-session",
272
- "--resume",
273
- "-r",
274
- "--add-dir",
275
- "--append-system-prompt-file",
276
- "--mcp-config",
277
- "--plugin-dir",
278
- "--settings",
279
- "--setting-sources",
280
- "--system-prompt-file",
281
- ]);
282
-
283
- const CODEX_EXPERIMENT_SOURCE_ARGS = new Set([
284
- "--add-dir",
285
- "--cd",
286
- "-C",
287
- "--config",
288
- "-c",
289
- "--profile",
290
- "-p",
291
- ]);
292
-
293
- const bindSubprocessExperimentConfig = (config: Record<string, unknown>): unknown => {
294
- if (typeof config.command !== "string" || !isAbsolute(config.command)) {
295
- throw new Error(
296
- "experiment subprocess commands must be absolute direct wrappers so Lux can fingerprint the invoked behavior",
297
- );
298
- }
299
- if (typeof config.cwd === "string") {
300
- throw new Error(
301
- "experiment runs do not allow an explicit subprocess cwd; use the candidate subject and staged fixtures",
302
- );
303
- }
304
- if (Array.isArray(config.args) && config.args.length > 0) {
305
- throw new Error(
306
- "experiment subprocesses do not allow args; put stable behavior in the fingerprinted direct wrapper",
307
- );
308
- }
309
- if (isRecord(config.env) && Object.keys(config.env).length > 0) {
310
- throw new Error(
311
- "experiment subprocesses do not allow custom env; put stable behavior in the fingerprinted direct wrapper",
312
- );
313
- }
314
- return { ...config, inheritEnv: false };
315
- };
316
-
317
- type AgentDriverName = "claude-code" | "codex";
318
-
319
- const validateAgentExperimentConfig = (
320
- driverName: AgentDriverName,
321
- config: Record<string, unknown>,
322
- ): { driverEnv: Record<string, unknown>; extraArgs: string[] } => {
323
- const driverEnv = isRecord(config.env) ? config.env : {};
324
- const commandResolutionOverride = Object.keys(driverEnv).find((name) =>
325
- ["PATH", "PATHEXT"].includes(name.toUpperCase()),
326
- );
327
- if (commandResolutionOverride) {
328
- throw new Error(
329
- `experiment runs do not allow driver env.${commandResolutionOverride}; command resolution must match the fingerprinted host executable`,
330
- );
331
- }
332
- if (typeof config.cwd === "string") {
333
- throw new Error(
334
- `experiment runs do not allow an explicit ${driverName} cwd; use the candidate subject and staged fixtures`,
335
- );
336
- }
337
-
338
- const extraArgs = Array.isArray(config.extraArgs)
339
- ? config.extraArgs.filter((value): value is string => typeof value === "string")
340
- : [];
341
- const disallowedArgs =
342
- driverName === "claude-code" ? EXPERIMENT_BEHAVIOR_SOURCE_ARGS : CODEX_EXPERIMENT_SOURCE_ARGS;
343
- const behaviorSourceArg = extraArgs.find((arg) =>
344
- [...disallowedArgs].some((name) => arg === name || arg.startsWith(`${name}=`)),
345
- );
346
- if (behaviorSourceArg) {
347
- throw new Error(
348
- `experiment runs do not allow behavior-bearing extraArgs '${behaviorSourceArg}'; use structured driver fields so Lux can fingerprint the source`,
349
- );
350
- }
351
-
352
- const isolation = isRecord(config.isolation) ? config.isolation : {};
353
- const structuredPaths = [
354
- ...(driverName === "claude-code" && Array.isArray(config.pluginDirs) ? config.pluginDirs : []),
355
- ...(Array.isArray(config.addDirs) ? config.addDirs : []),
356
- ...(driverName === "claude-code" && Array.isArray(isolation.allowRead)
357
- ? isolation.allowRead
358
- : []),
359
- ].filter((value): value is string => typeof value === "string");
360
- const relativeSource = structuredPaths.find((path) => !isAbsolute(path));
361
- if (relativeSource) {
362
- throw new Error(
363
- `experiment driver source paths must be absolute after subjectPath binding: ${relativeSource}`,
364
- );
365
- }
366
- return { driverEnv, extraArgs };
367
- };
368
-
369
- const bindCodexExperimentConfig = (
370
- config: Record<string, unknown>,
371
- extraArgs: readonly string[],
372
- ): unknown => {
373
- if (typeof config.profile === "string") {
374
- throw new Error(
375
- "experiment runs do not allow a Codex profile; use inline driver config so Lux can fingerprint it",
376
- );
377
- }
378
- if (config.ephemeral === false) {
379
- throw new Error("experiment runs require ephemeral Codex sessions");
380
- }
381
- const inlineConfig = isRecord(config.config) ? config.config : {};
382
- const externalConfigKey = ((): string | undefined => {
383
- const visit = (value: Record<string, unknown>, parent = ""): string | undefined => {
384
- for (const [key, nested] of Object.entries(value)) {
385
- const path = parent ? `${parent}.${key}` : key;
386
- if (/(?:mcp_servers|instructions_file|notify|project_doc)/i.test(key)) return path;
387
- if (isRecord(nested)) {
388
- const found = visit(nested, path);
389
- if (found) return found;
390
- }
391
- }
392
- return undefined;
393
- };
394
- return visit(inlineConfig);
395
- })();
396
- if (externalConfigKey) {
397
- throw new Error(
398
- `experiment runs do not allow path-bearing Codex config '${externalConfigKey}'`,
399
- );
400
- }
401
- const appServer = config.interactionMode !== "exec";
402
- return {
403
- ...config,
404
- ephemeral: true,
405
- ignoreUserConfig: true,
406
- ignoreRules: true,
407
- ...(appServer && !config.isolation ? { isolation: { allowRead: [] } } : {}),
408
- extraArgs,
409
- };
410
- };
411
-
412
- const bindClaudeExperimentConfig = (
413
- config: Record<string, unknown>,
414
- extraArgs: readonly string[],
415
- driverEnv: Record<string, unknown>,
416
- ): unknown => {
417
- if (config.isolation) return config;
418
- return {
419
- ...config,
420
- extraArgs: [
421
- ...extraArgs,
422
- "--setting-sources",
423
- "user",
424
- "--strict-mcp-config",
425
- "--settings",
426
- JSON.stringify({ disableAllHooks: true }),
427
- ],
428
- env: {
429
- ...driverEnv,
430
- CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
431
- CLAUDE_CODE_DISABLE_CLAUDE_MDS: "1",
432
- CLAUDE_CODE_SKIP_PROMPT_HISTORY: "1",
433
- CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1",
434
- CLAUDE_CODE_AUTO_CONNECT_IDE: "false",
435
- CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL: "1",
436
- DISABLE_AUTOUPDATER: "1",
437
- },
438
- };
439
- };
440
-
441
273
  export const boundExperimentDriverConfig = (
442
- driverName: string,
274
+ plugin: import("../core/driver.js").DriverPlugin,
443
275
  config: unknown,
444
276
  experimentId: string | undefined,
445
277
  ): unknown => {
446
- if (!experimentId || !isRecord(config)) return config;
447
- if (driverName === "subprocess") return bindSubprocessExperimentConfig(config);
448
- if (driverName !== "claude-code" && driverName !== "codex") return config;
449
- const { driverEnv, extraArgs } = validateAgentExperimentConfig(driverName, config);
450
- return driverName === "codex"
451
- ? bindCodexExperimentConfig(config, extraArgs)
452
- : bindClaudeExperimentConfig(config, extraArgs, driverEnv);
278
+ if (!experimentId) return config;
279
+ if (!plugin.experiment) {
280
+ throw new Error(
281
+ `driver '${plugin.name}' supports ordinary eval runs but does not declare controlled experiment support`,
282
+ );
283
+ }
284
+ return validatePluginConfig(
285
+ plugin.configSchema,
286
+ plugin.experiment.prepare(config),
287
+ `driver '${plugin.name}' experiment configuration`,
288
+ );
453
289
  };
454
290
 
455
291
  const closeQuietly = async (close: () => Promise<void>, label: string): Promise<void> => {
@@ -463,6 +299,7 @@ const closeQuietly = async (close: () => Promise<void>, label: string): Promise<
463
299
  };
464
300
 
465
301
  export class Orchestrator {
302
+ private readonly environment: EnvironmentDeclaration | undefined;
466
303
  private evaluatorHash: Promise<string> | undefined;
467
304
  readonly registry: Registry;
468
305
  private readonly defaultAnswerer: AnswererRef;
@@ -471,6 +308,9 @@ export class Orchestrator {
471
308
  private readonly lifecycleFixtures: readonly LifecycleHooksPlugin[];
472
309
 
473
310
  constructor(options: OrchestratorOptions) {
311
+ this.environment = options.environment
312
+ ? EnvironmentDeclarationSchema.parse(options.environment)
313
+ : undefined;
474
314
  this.registry = options.registry;
475
315
  this.defaultAnswerer = options.defaultAnswerer ?? "persona";
476
316
  this.defaultDriver = options.defaultDriver;
@@ -534,7 +374,7 @@ export class Orchestrator {
534
374
  // Validate both configs before instantiating either plugin so a
535
375
  // schema failure can't leak a half-initialized driver/answerer.
536
376
  const driverCfg = boundExperimentDriverConfig(
537
- driverName,
377
+ driverPlugin,
538
378
  validatePluginConfig(
539
379
  driverPlugin.configSchema,
540
380
  driverConfigOf(driverRef),
@@ -548,6 +388,7 @@ export class Orchestrator {
548
388
  `answerer '${answererName}'`,
549
389
  );
550
390
 
391
+ validateDriverCapabilities(driverPlugin, driverCfg, evalCase.requires);
551
392
  const driver = await driverPlugin.create(driverCfg);
552
393
  let answerer: Answerer;
553
394
  try {
@@ -599,6 +440,7 @@ export class Orchestrator {
599
440
  skipJudge: request.skipJudge ?? false,
600
441
  }),
601
442
  fixtureValues: valuesForFixtures(lifecycleFixtures),
443
+ environment: this.environment,
602
444
  driver,
603
445
  answerer,
604
446
  },
@@ -797,6 +639,7 @@ export class Orchestrator {
797
639
  observer: handle ?? observer,
798
640
  abortSignal: opts.abortSignal,
799
641
  suiteId: suite.manifest.suiteId,
642
+ suiteConcurrency: concurrency,
800
643
  subjectRoot: opts.subjectRoot,
801
644
  subjectHash: opts.subjectHash,
802
645
  profileId: opts.profileId,
@@ -0,0 +1,209 @@
1
+ import { z } from "zod";
2
+ import { clusterKey } from "../core/evaluation-clusters.js";
3
+ import {
4
+ bootstrapMeanConfidenceInterval,
5
+ type CandidateEvaluation,
6
+ type ConfidenceConfig,
7
+ ConfidenceIntervalSchema,
8
+ } from "../core/index.js";
9
+
10
+ type CaseSamples = NonNullable<CandidateEvaluation["caseSamples"]>;
11
+ type Evaluation = CandidateEvaluation & { repetitions: number };
12
+
13
+ export const clusterCaseSamples = (
14
+ groups: readonly (CaseSamples | undefined)[],
15
+ ): CaseSamples | undefined => {
16
+ if (groups.some((group) => group === undefined)) return undefined;
17
+ const result = new Map<string, CaseSamples[string]>();
18
+ for (const group of groups) {
19
+ for (const [id, sample] of Object.entries(group ?? {})) {
20
+ const current = result.get(id);
21
+ if (!current) {
22
+ result.set(id, structuredClone(sample));
23
+ continue;
24
+ }
25
+ if (current.identityHash !== sample.identityHash) current.identityHash = null;
26
+ if (current.clusterId !== sample.clusterId) current.identityHash = null;
27
+ const metrics = new Map(Object.entries(current.metrics));
28
+ for (const [metric, value] of Object.entries(sample.metrics)) {
29
+ const before = metrics.get(metric);
30
+ metrics.set(
31
+ metric,
32
+ before
33
+ ? {
34
+ mean: (before.mean * before.n + value.mean * value.n) / (before.n + value.n),
35
+ n: before.n + value.n,
36
+ }
37
+ : { ...value },
38
+ );
39
+ }
40
+ current.metrics = Object.fromEntries(metrics);
41
+ }
42
+ }
43
+ return Object.fromEntries(result);
44
+ };
45
+
46
+ const caseGroups = (samples: CaseSamples): Map<string, string[]> => {
47
+ const groups = new Map<string, string[]>();
48
+ for (const id of Object.keys(samples).sort()) {
49
+ const key = clusterKey(id, samples[id]?.clusterId);
50
+ groups.set(key, [...(groups.get(key) ?? []), id]);
51
+ }
52
+ return groups;
53
+ };
54
+
55
+ export const groupedCaseMetricSamples = (
56
+ samples: CaseSamples | undefined,
57
+ repetitions: number,
58
+ ): Record<string, number[]> | undefined => {
59
+ if (!samples || !Object.values(samples).some((sample) => sample.clusterId !== undefined))
60
+ return undefined;
61
+ const groups = caseGroups(samples);
62
+ const metrics = new Set(Object.values(samples).flatMap((sample) => Object.keys(sample.metrics)));
63
+ return Object.fromEntries(
64
+ [...metrics].flatMap((metric) => {
65
+ if (
66
+ Object.values(samples).some(
67
+ (sample) =>
68
+ !Object.hasOwn(sample.metrics, metric) || sample.metrics[metric]?.n !== repetitions,
69
+ )
70
+ )
71
+ return [];
72
+ return [
73
+ [
74
+ metric,
75
+ [...groups.values()].map(
76
+ (ids) =>
77
+ ids.reduce((sum, id) => sum + (samples[id]?.metrics[metric]?.mean ?? 0), 0) /
78
+ ids.length,
79
+ ),
80
+ ],
81
+ ];
82
+ }),
83
+ );
84
+ };
85
+
86
+ export const PairedEffectSchema = z.object({
87
+ caseIds: z.array(z.string()),
88
+ n: z.number().int().nonnegative(),
89
+ caseCount: z.number().int().nonnegative().optional(),
90
+ clusters: z.record(z.string(), z.array(z.string())).optional(),
91
+ meanDelta: z.number().finite().nullable(),
92
+ interval: ConfidenceIntervalSchema.nullable(),
93
+ note: z.string().nullable(),
94
+ });
95
+ export type PairedEffect = z.infer<typeof PairedEffectSchema>;
96
+
97
+ export const confidenceGateIssue = (
98
+ interval: z.infer<typeof ConfidenceIntervalSchema>,
99
+ ): string | undefined =>
100
+ interval.method === "percentile-bootstrap" && interval.lower === interval.upper
101
+ ? "zero-width percentile-bootstrap interval cannot establish promotion confidence; use Hoeffding for bounded means or collect more varied independent evidence"
102
+ : undefined;
103
+
104
+ const pairedDeltas = (
105
+ before: Evaluation | undefined,
106
+ after: Evaluation | undefined,
107
+ metric: string,
108
+ ): { caseIds: string[]; values: number[]; clusters: Record<string, string[]> } => {
109
+ if (!before?.caseSamples || !after?.caseSamples)
110
+ throw new Error("keyed case evidence unavailable");
111
+ if (
112
+ before.profileId !== after.profileId ||
113
+ before.split !== after.split ||
114
+ before.repetitions !== after.repetitions
115
+ ) {
116
+ throw new Error("paired evidence requires matching profile, split, and repetitions");
117
+ }
118
+ const caseIds = Object.keys(before.caseSamples).sort();
119
+ const afterIds = Object.keys(after.caseSamples).sort();
120
+ const beforeCases = Object.keys(before.casePassRates).sort();
121
+ const afterCases = Object.keys(after.casePassRates).sort();
122
+ if (
123
+ caseIds.length === 0 ||
124
+ [afterIds, beforeCases, afterCases].some(
125
+ (ids) => JSON.stringify(ids) !== JSON.stringify(caseIds),
126
+ )
127
+ ) {
128
+ throw new Error("paired evidence requires identical canonical case sets");
129
+ }
130
+ const values = caseIds.map((id) => {
131
+ const a = before.caseSamples?.[id];
132
+ const b = after.caseSamples?.[id];
133
+ if (!a?.identityHash || a.identityHash !== b?.identityHash)
134
+ throw new Error(`paired case '${id}' has incompatible evaluation identity`);
135
+ if (a.clusterId !== b.clusterId)
136
+ throw new Error(`paired case '${id}' has incompatible cluster membership`);
137
+ const av = Object.hasOwn(a.metrics, metric) ? a.metrics[metric] : undefined;
138
+ const bv = Object.hasOwn(b.metrics, metric) ? b.metrics[metric] : undefined;
139
+ if (!av || !bv || av.n !== before.repetitions || bv.n !== after.repetitions) {
140
+ throw new Error(
141
+ `paired metric '${metric}' lacks complete repetition coverage for case '${id}'`,
142
+ );
143
+ }
144
+ return bv.mean - av.mean;
145
+ });
146
+ const byCase = new Map(caseIds.map((id, index) => [id, values[index] ?? 0]));
147
+ const groups = caseGroups(before.caseSamples);
148
+ return {
149
+ caseIds,
150
+ clusters: Object.fromEntries(groups),
151
+ values: [...groups.values()].map(
152
+ (ids) => ids.reduce((sum, id) => sum + (byCase.get(id) ?? 0), 0) / ids.length,
153
+ ),
154
+ };
155
+ };
156
+
157
+ export const pairedEffect = (
158
+ before: Evaluation | undefined,
159
+ after: Evaluation | undefined,
160
+ metric: string,
161
+ confidence?: ConfidenceConfig,
162
+ ): PairedEffect => {
163
+ let paired: ReturnType<typeof pairedDeltas>;
164
+ try {
165
+ paired = pairedDeltas(before, after, metric);
166
+ } catch (error) {
167
+ return {
168
+ caseIds: [],
169
+ n: 0,
170
+ caseCount: 0,
171
+ clusters: {},
172
+ meanDelta: null,
173
+ interval: null,
174
+ note: error instanceof Error ? error.message : String(error),
175
+ };
176
+ }
177
+ const result: PairedEffect = {
178
+ caseIds: paired.caseIds,
179
+ n: paired.values.length,
180
+ caseCount: paired.caseIds.length,
181
+ clusters: paired.clusters,
182
+ meanDelta: paired.values.reduce((sum, value) => sum + value, 0) / paired.values.length,
183
+ interval: null,
184
+ note: null,
185
+ };
186
+ if (!confidence) return result;
187
+ try {
188
+ if (confidence.method === "wilson")
189
+ throw new Error("Wilson cannot estimate paired differences");
190
+ if (confidence.method === "hoeffding") {
191
+ const interval = bootstrapMeanConfidenceInterval(
192
+ paired.values.map((value) => (value + 1) / 2),
193
+ confidence,
194
+ );
195
+ result.interval = {
196
+ ...interval,
197
+ estimate: 2 * interval.estimate - 1,
198
+ lower: 2 * interval.lower - 1,
199
+ upper: 2 * interval.upper - 1,
200
+ };
201
+ } else {
202
+ result.interval = bootstrapMeanConfidenceInterval(paired.values, confidence);
203
+ }
204
+ result.note = confidenceGateIssue(result.interval) ?? null;
205
+ } catch (error) {
206
+ result.note = error instanceof Error ? error.message : String(error);
207
+ }
208
+ return result;
209
+ };
@@ -1,6 +1,8 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { lstat, readFile, readlink } from "node:fs/promises";
3
+ import { availableParallelism, cpus, release, totalmem } from "node:os";
3
4
  import { dirname, join, parse, relative, resolve, sep } from "node:path";
5
+ import type { EnvironmentDeclaration } from "../core/environment.js";
4
6
  import type { RunProvenance } from "../core/index.js";
5
7
  import { contentHash, exec, valueHash } from "../core/index.js";
6
8
 
@@ -113,13 +115,30 @@ export const runtimeIdentity = (): RunProvenance["runtime"] => ({
113
115
  arch: process.arch,
114
116
  node: process.version,
115
117
  ...(process.versions.bun ? { bun: process.versions.bun } : {}),
118
+ host: {
119
+ osRelease: release(),
120
+ cpuModels: [...new Set(cpus().map((cpu) => cpu.model))].sort(),
121
+ logicalCpuCount: cpus().length,
122
+ availableParallelism: availableParallelism(),
123
+ totalMemoryBytes: totalmem(),
124
+ },
116
125
  });
117
126
 
118
- export const runProvenance = async (casePath: string | null): Promise<RunProvenance> => {
127
+ export const runProvenance = async (
128
+ casePath: string | null,
129
+ options: {
130
+ environment?: EnvironmentDeclaration | undefined;
131
+ concurrency?: number | undefined;
132
+ } = {},
133
+ ): Promise<RunProvenance> => {
119
134
  const runtime = runtimeIdentity();
120
- if (!casePath) return { runtime };
135
+ const environment = {
136
+ ...(options.environment ? { declared: options.environment } : {}),
137
+ concurrency: options.concurrency ?? 1,
138
+ };
139
+ if (!casePath) return { runtime, environment };
121
140
  const root = gitRootAbove(casePath);
122
- if (!root) return { runtime, source: { casePath: resolve(casePath) } };
141
+ if (!root) return { runtime, environment, source: { casePath: resolve(casePath) } };
123
142
  let git = repositories.get(root);
124
143
  if (!git) {
125
144
  git = inspectRepository(root);
@@ -128,6 +147,7 @@ export const runProvenance = async (casePath: string | null): Promise<RunProvena
128
147
  const state = await git;
129
148
  return {
130
149
  runtime,
150
+ environment,
131
151
  source: {
132
152
  casePath: portablePath(relative(root, resolve(casePath))),
133
153
  ...(state ? { git: state } : {}),
@@ -1,6 +1,7 @@
1
1
  import { cp } from "node:fs/promises";
2
2
  import { dirname, isAbsolute, resolve } from "node:path";
3
3
  import { z } from "zod";
4
+ import type { EnvironmentDeclaration } from "../core/environment.js";
4
5
  import {
5
6
  type AgentDriver,
6
7
  type Answerer,
@@ -60,6 +61,7 @@ export const INLINE_CASE_PATH = "<inline>";
60
61
  type RunDirCarrier = { luxRunDir?: string };
61
62
  type InterruptCounts = { ask: number; approve: number; custom: number };
62
63
  export type RunWithPluginsArgs = {
64
+ environment?: EnvironmentDeclaration | undefined;
63
65
  request: RunRequest;
64
66
  casePath: string;
65
67
  authoredCase: EvalCase;
@@ -173,8 +175,17 @@ const SummaryModelAccountingSchema = z.object({
173
175
  unpricedModelCalls: z.number().int().nonnegative().optional(),
174
176
  });
175
177
 
176
- const usageFromSummary = (summary: unknown): TokenUsage | null =>
177
- SummaryUsageSchema.safeParse(summary).data?.usage ?? null;
178
+ const usageFromSummary = (summary: unknown): TokenUsage | null => {
179
+ if (!summary || typeof summary !== "object") return null;
180
+ const raw = (summary as { usage?: unknown }).usage;
181
+ if (!raw || typeof raw !== "object") return null;
182
+ const counts = raw as Record<string, unknown>;
183
+ const hasCount = ["input", "output", "cacheRead", "cacheCreation"].some((key) => {
184
+ const value = counts[key];
185
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
186
+ });
187
+ return hasCount ? (SummaryUsageSchema.safeParse(summary).data?.usage ?? null) : null;
188
+ };
178
189
 
179
190
  const costFromSummary = (summary: unknown): number | undefined => {
180
191
  if (!summary || typeof summary !== "object") return undefined;
@@ -320,6 +331,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
320
331
  const startedAt = args.request.startedAt ?? new Date();
321
332
  const provenance = await runProvenance(
322
333
  args.casePath === INLINE_CASE_PATH ? null : args.casePath,
334
+ { environment: args.environment, concurrency: args.request.suiteConcurrency },
323
335
  );
324
336
  const observer = guardRunObserver(args.request.observer);
325
337
  const runDir = await openRunDir({
@@ -528,6 +540,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
528
540
  exitReason: "error",
529
541
  driver: { name: this.args.driverName, ...ready },
530
542
  answerer: answererMetadata(this.args.answererName, this.args.answererConfig),
543
+ agentUsageAvailability: "unavailable",
531
544
  usage: {
532
545
  agent: emptyUsage(),
533
546
  answerer: readAnswererUsage(this.args.answerer),
@@ -580,8 +593,9 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
580
593
  const answererAccounting = readAnswererModelAccounting(this.args.answerer);
581
594
  const agentCalls = this.ready ? (reportedModelAccounting?.modelCalls ?? 1) : 0;
582
595
  const operationalFailure = loopFailure(outcome, this.collection.error);
596
+ const agentUsage = usageFromSummary(summary);
583
597
  const usage: RunUsage = {
584
- agent: usageFromSummary(summary) ?? emptyUsage(),
598
+ agent: agentUsage ?? emptyUsage(),
585
599
  answerer: readAnswererUsage(this.args.answerer),
586
600
  grading: emptyUsage(),
587
601
  };
@@ -597,6 +611,7 @@ class RunExecution implements RunLifecycleEffects<RunResult> {
597
611
  driver: { name: this.args.driverName, ...ready },
598
612
  answerer: answererMetadata(this.args.answererName, this.args.answererConfig),
599
613
  usage,
614
+ agentUsageAvailability: agentUsage === null ? "unavailable" : "reported",
600
615
  modelAccounting: {
601
616
  agentCalls,
602
617
  answererCalls: answererAccounting.calls,
@@ -23,6 +23,7 @@ type RunLifecycleContext = {
23
23
  phase: RunFailurePhase;
24
24
  result: unknown;
25
25
  error: unknown;
26
+ failed: boolean;
26
27
  };
27
28
  type RunLifecycleInput = { effects: ErasedEffects };
28
29
  type RunLifecycleOutcome =
@@ -56,7 +57,7 @@ const runLifecycleMachine = setup({
56
57
  actions: {
57
58
  captureError: assign(({ context, event }) => {
58
59
  const error = "error" in event ? event.error : new Error("run lifecycle failed");
59
- return { error, phase: isRunTraceLimit(error) ? "trace" : context.phase };
60
+ return { error, failed: true, phase: isRunTraceLimit(error) ? "trace" : context.phase };
60
61
  }),
61
62
  captureResult: assign({
62
63
  result: ({ event }) => ("output" in event ? event.output : undefined),
@@ -73,6 +74,7 @@ const runLifecycleMachine = setup({
73
74
  phase: "setup",
74
75
  result: undefined,
75
76
  error: undefined,
77
+ failed: false,
76
78
  }),
77
79
  initial: "preparing",
78
80
  states: {
@@ -153,7 +155,7 @@ const runLifecycleMachine = setup({
153
155
  failed: { type: "final" },
154
156
  },
155
157
  output: ({ context }) => {
156
- if (context.error !== undefined) {
158
+ if (context.failed) {
157
159
  return { status: "failed", error: context.error };
158
160
  }
159
161
  return { status: "succeeded", result: context.result };
@@ -126,6 +126,7 @@ export const loadSuiteRun = async (path: string): Promise<SuiteRun | null> =>
126
126
 
127
127
  export const listSuiteRuns = async (
128
128
  runsRoot: string,
129
+ options: { onReadError?: (path: string, error: unknown) => void } = {},
129
130
  ): Promise<Array<SuiteRun & { suiteDir: string }>> => {
130
131
  const root = join(runsRoot, SUITES_DIR);
131
132
  if (!existsSync(root)) return [];
@@ -133,7 +134,15 @@ export const listSuiteRuns = async (
133
134
  .filter((entry) => entry.isDirectory())
134
135
  .map((entry) => join(root, entry.name));
135
136
  const manifests = await Promise.all(
136
- dirs.map(async (suiteDir) => ({ suiteDir, suite: await loadSuiteRun(suiteDir) })),
137
+ dirs.map(async (suiteDir) => {
138
+ try {
139
+ return { suiteDir, suite: await loadSuiteRun(suiteDir) };
140
+ } catch (error) {
141
+ if (!options.onReadError) throw error;
142
+ options.onReadError(suiteDir, error);
143
+ return { suiteDir, suite: null };
144
+ }
145
+ }),
137
146
  );
138
147
  return manifests
139
148
  .flatMap(({ suiteDir, suite }) => (suite ? [{ ...suite, suiteDir }] : []))
@@ -8,6 +8,7 @@ import {
8
8
  isUsageEmpty,
9
9
  type RunMetadata,
10
10
  } from "../core/index.js";
11
+ import { hasAgentUsage } from "../core/usage.js";
11
12
  import { runComparisonIdentity } from "./comparison-identity.js";
12
13
  import { safeReadJson } from "./fs-read.js";
13
14
  import type { SuiteCaseResult } from "./orchestrator.js";
@@ -102,7 +103,7 @@ const collectMetadata = (samples: MetricSamples, metadata: RunMetadata): void =>
102
103
  add(samples, "durationMs", Math.max(0, ended - started));
103
104
  }
104
105
 
105
- if (!isUsageEmpty(metadata.usage.agent)) {
106
+ if (hasAgentUsage(metadata)) {
106
107
  add(samples, "agent.input", metadata.usage.agent.input);
107
108
  add(samples, "agent.output", metadata.usage.agent.output);
108
109
  add(samples, "agent.cacheRead", metadata.usage.agent.cacheRead);