@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
@@ -0,0 +1,214 @@
1
+ import { lstat, readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join, resolve } from "node:path";
4
+ import { z } from "zod";
5
+ import { walkAuthoredFiles } from "../../core/file-tree.js";
6
+ import { contentHash, valueHash } from "../../core/hash.js";
7
+ import {
8
+ prepareClaudeExperimentConfig,
9
+ prepareCodexExperimentConfig,
10
+ } from "./experiment-policy.js";
11
+
12
+ const ClaudeSettingsFileSchema = z.looseObject({
13
+ enabledPlugins: z.record(z.string(), z.boolean()).optional(),
14
+ });
15
+ const ClaudeInstalledPluginRecordSchema = z.looseObject({
16
+ installPath: z.string(),
17
+ });
18
+ const ClaudeInstalledPluginsFileSchema = z.looseObject({
19
+ plugins: z.record(z.string(), z.array(ClaudeInstalledPluginRecordSchema)),
20
+ });
21
+
22
+ const optionalRegularFileHash = async (path: string): Promise<string | null> => {
23
+ try {
24
+ const info = await lstat(path);
25
+ if (info.isSymbolicLink() || !info.isFile()) {
26
+ throw new Error(`behavior-affecting settings must be a regular file: ${path}`);
27
+ }
28
+ return contentHash(await readFile(path));
29
+ } catch (error) {
30
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
31
+ throw error;
32
+ }
33
+ };
34
+
35
+ const driverConfigRecord = (driver: unknown): Record<string, unknown> => {
36
+ if (!driver || typeof driver !== "object") return {};
37
+ const config = (driver as { config?: unknown }).config;
38
+ return config && typeof config === "object" ? (config as Record<string, unknown>) : {};
39
+ };
40
+
41
+ export const claudeConfigRoot = (driver: unknown): string => {
42
+ const config = driverConfigRecord(driver);
43
+ const env =
44
+ config.env && typeof config.env === "object"
45
+ ? (config.env as Record<string, unknown>)
46
+ : undefined;
47
+ const configured = env?.CLAUDE_CONFIG_DIR;
48
+ const root =
49
+ typeof configured === "string"
50
+ ? configured
51
+ : (process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"));
52
+ if (!isAbsolute(root)) {
53
+ throw new Error("CLAUDE_CONFIG_DIR must be absolute for reproducible experiment identity");
54
+ }
55
+ return resolve(root);
56
+ };
57
+
58
+ const hashBehaviorDirectory = async (
59
+ root: string,
60
+ label: string,
61
+ ): Promise<Array<{ path: string; hash: string }>> => {
62
+ try {
63
+ const info = await lstat(root);
64
+ if (info.isSymbolicLink() || !info.isDirectory()) {
65
+ throw new Error(`behavior-affecting Claude source must be a real directory: ${root}`);
66
+ }
67
+ } catch (error) {
68
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
69
+ throw error;
70
+ }
71
+ const paths = await walkAuthoredFiles(root);
72
+ if (paths.length > 5_000) {
73
+ throw new Error(`behavior-affecting Claude source '${label}' exceeds 5000 files`);
74
+ }
75
+ let bytes = 0;
76
+ const hashes: Array<{ path: string; hash: string }> = [];
77
+ for (const path of paths) {
78
+ const content = await readFile(join(root, path));
79
+ bytes += content.length;
80
+ if (bytes > 256 * 1024 * 1024) {
81
+ throw new Error(`behavior-affecting Claude source '${label}' exceeds 256 MiB`);
82
+ }
83
+ hashes.push({ path: `${label}/${path}`, hash: contentHash(content) });
84
+ }
85
+ return hashes;
86
+ };
87
+
88
+ /**
89
+ * Hash every intentionally loaded user behavior source without persisting its
90
+ * contents. This covers user instructions/rules/output styles and the exact
91
+ * installed contents of plugins enabled by settings.json.
92
+ */
93
+ export const claudeUserBehaviorHash = async (configRoot: string): Promise<string | null> => {
94
+ const settingsPath = join(configRoot, "settings.json");
95
+ const settingsHash = await optionalRegularFileHash(settingsPath);
96
+ const sources: Array<{ path: string; hash: string }> = [];
97
+ if (settingsHash) sources.push({ path: "settings.json", hash: settingsHash });
98
+ const claudeMdHash = await optionalRegularFileHash(join(configRoot, "CLAUDE.md"));
99
+ if (claudeMdHash) sources.push({ path: "CLAUDE.md", hash: claudeMdHash });
100
+ for (const directory of ["rules", "output-styles", "agents", "commands", "skills"]) {
101
+ sources.push(...(await hashBehaviorDirectory(join(configRoot, directory), directory)));
102
+ }
103
+
104
+ let settings: unknown;
105
+ try {
106
+ const contents = await readFile(settingsPath, "utf8");
107
+ try {
108
+ settings = JSON.parse(contents);
109
+ } catch {
110
+ throw new Error("Claude settings.json contains invalid JSON");
111
+ }
112
+ } catch (error) {
113
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
114
+ settings = undefined;
115
+ } else {
116
+ throw error;
117
+ }
118
+ }
119
+ const parsedSettings = ClaudeSettingsFileSchema.safeParse(settings);
120
+ if (settings !== undefined && !parsedSettings.success) {
121
+ throw new Error("Claude settings.json has an invalid enabledPlugins map");
122
+ }
123
+ const enabledPlugins = parsedSettings.success ? parsedSettings.data.enabledPlugins : undefined;
124
+ const enabledIds = Object.entries(enabledPlugins ?? {})
125
+ .filter(([, value]) => value === true)
126
+ .map(([id]) => id)
127
+ .sort();
128
+ if (enabledIds.length > 0) {
129
+ const installedPath = join(configRoot, "plugins", "installed_plugins.json");
130
+ const installedHash = await optionalRegularFileHash(installedPath);
131
+ if (installedHash) {
132
+ sources.push({ path: "plugins/installed_plugins.json", hash: installedHash });
133
+ }
134
+ let installed: unknown;
135
+ try {
136
+ installed = JSON.parse(await readFile(installedPath, "utf8"));
137
+ } catch {
138
+ installed = undefined;
139
+ }
140
+ const parsedInstalled = ClaudeInstalledPluginsFileSchema.safeParse(installed);
141
+ if (!parsedInstalled.success) {
142
+ throw new Error("enabled Claude plugins have invalid installed metadata");
143
+ }
144
+ const plugins = parsedInstalled.data.plugins;
145
+ for (const id of enabledIds) {
146
+ const records = plugins[id];
147
+ if (!records || records.length === 0) {
148
+ throw new Error(`enabled Claude plugin '${id}' has no installed content to hash`);
149
+ }
150
+ for (const [index, record] of records.entries()) {
151
+ const installPath = record.installPath;
152
+ sources.push(
153
+ ...(await hashBehaviorDirectory(resolve(installPath), `plugins/enabled/${id}/${index}`)),
154
+ );
155
+ }
156
+ }
157
+ }
158
+ return sources.length === 0 ? null : valueHash(sources);
159
+ };
160
+
161
+ export const configuredClaudeSourceHashes = async (
162
+ driver: unknown,
163
+ baseRoot: string,
164
+ cache: Map<string, Promise<string>> = new Map(),
165
+ ): Promise<Array<{ kind: "plugin" | "read"; path: string; hash: string }>> => {
166
+ const name =
167
+ typeof driver === "string" ? driver : (driver as { name?: unknown } | undefined)?.name;
168
+ const driverCli = name === "claude-code" || name === "codex" ? { name } : undefined;
169
+ if (!driverCli) return [];
170
+ const config = driverConfigRecord(driver);
171
+ // This also rejects path-bearing extraArgs that would evade the source
172
+ // hashes below. Runtime applies the same guard before driver creation.
173
+ if (driverCli.name === "claude-code") prepareClaudeExperimentConfig(config);
174
+ else prepareCodexExperimentConfig(config);
175
+ const isolation =
176
+ config.isolation && typeof config.isolation === "object"
177
+ ? (config.isolation as Record<string, unknown>)
178
+ : undefined;
179
+ const configured = [
180
+ ...(driverCli.name === "claude-code" && Array.isArray(config.pluginDirs)
181
+ ? config.pluginDirs.map((path) => ({ kind: "plugin" as const, path }))
182
+ : []),
183
+ ...(Array.isArray(config.addDirs)
184
+ ? config.addDirs.map((path) => ({ kind: "read" as const, path }))
185
+ : []),
186
+ ...(driverCli.name === "claude-code" && isolation && Array.isArray(isolation.allowRead)
187
+ ? isolation.allowRead.map((path) => ({ kind: "read" as const, path }))
188
+ : []),
189
+ ].filter(
190
+ (entry): entry is { kind: "plugin" | "read"; path: string } =>
191
+ typeof entry.path === "string" && entry.path.length > 0,
192
+ );
193
+ return Promise.all(
194
+ configured
195
+ .sort((a, b) => `${a.kind}:${a.path}`.localeCompare(`${b.kind}:${b.path}`))
196
+ .map(async ({ kind, path }) => {
197
+ const absolute = resolve(baseRoot, path);
198
+ const key = `${kind}:${absolute}`;
199
+ let hash = cache.get(key);
200
+ if (!hash) {
201
+ hash = hashBehaviorDirectory(
202
+ absolute,
203
+ `driver/${kind}/${contentHash(absolute).slice(0, 12)}`,
204
+ ).then(valueHash);
205
+ cache.set(key, hash);
206
+ }
207
+ return {
208
+ kind,
209
+ path: absolute,
210
+ hash: await hash,
211
+ };
212
+ }),
213
+ );
214
+ };
@@ -0,0 +1,195 @@
1
+ import { isAbsolute } from "node:path";
2
+
3
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
4
+ Boolean(value) && typeof value === "object" && !Array.isArray(value);
5
+
6
+ const EXPERIMENT_BEHAVIOR_SOURCE_ARGS = new Set([
7
+ "--continue",
8
+ "-c",
9
+ "--fork-session",
10
+ "--resume",
11
+ "-r",
12
+ "--add-dir",
13
+ "--append-system-prompt-file",
14
+ "--mcp-config",
15
+ "--plugin-dir",
16
+ "--settings",
17
+ "--setting-sources",
18
+ "--system-prompt-file",
19
+ ]);
20
+
21
+ const CODEX_EXPERIMENT_SOURCE_ARGS = new Set([
22
+ "--add-dir",
23
+ "--cd",
24
+ "-C",
25
+ "--config",
26
+ "-c",
27
+ "--profile",
28
+ "-p",
29
+ ]);
30
+
31
+ const bindSubprocessExperimentConfig = (config: Record<string, unknown>): unknown => {
32
+ if (typeof config.command !== "string" || !isAbsolute(config.command)) {
33
+ throw new Error(
34
+ "experiment subprocess commands must be absolute direct wrappers so Lux can fingerprint the invoked behavior",
35
+ );
36
+ }
37
+ if (typeof config.cwd === "string") {
38
+ throw new Error(
39
+ "experiment runs do not allow an explicit subprocess cwd; use the candidate subject and staged fixtures",
40
+ );
41
+ }
42
+ if (Array.isArray(config.args) && config.args.length > 0) {
43
+ throw new Error(
44
+ "experiment subprocesses do not allow args; put stable behavior in the fingerprinted direct wrapper",
45
+ );
46
+ }
47
+ if (isRecord(config.env) && Object.keys(config.env).length > 0) {
48
+ throw new Error(
49
+ "experiment subprocesses do not allow custom env; put stable behavior in the fingerprinted direct wrapper",
50
+ );
51
+ }
52
+ return { ...config, inheritEnv: false };
53
+ };
54
+
55
+ type AgentDriverName = "claude-code" | "codex";
56
+
57
+ const validateAgentExperimentConfig = (
58
+ driverName: AgentDriverName,
59
+ config: Record<string, unknown>,
60
+ ): { driverEnv: Record<string, unknown>; extraArgs: string[] } => {
61
+ const driverEnv = isRecord(config.env) ? config.env : {};
62
+ const commandResolutionOverride = Object.keys(driverEnv).find((name) =>
63
+ ["PATH", "PATHEXT"].includes(name.toUpperCase()),
64
+ );
65
+ if (commandResolutionOverride) {
66
+ throw new Error(
67
+ `experiment runs do not allow driver env.${commandResolutionOverride}; command resolution must match the fingerprinted host executable`,
68
+ );
69
+ }
70
+ if (typeof config.cwd === "string") {
71
+ throw new Error(
72
+ `experiment runs do not allow an explicit ${driverName} cwd; use the candidate subject and staged fixtures`,
73
+ );
74
+ }
75
+
76
+ const extraArgs = Array.isArray(config.extraArgs)
77
+ ? config.extraArgs.filter((value): value is string => typeof value === "string")
78
+ : [];
79
+ const disallowedArgs =
80
+ driverName === "claude-code" ? EXPERIMENT_BEHAVIOR_SOURCE_ARGS : CODEX_EXPERIMENT_SOURCE_ARGS;
81
+ const behaviorSourceArg = extraArgs.find((arg) =>
82
+ [...disallowedArgs].some((name) => arg === name || arg.startsWith(`${name}=`)),
83
+ );
84
+ if (behaviorSourceArg) {
85
+ throw new Error(
86
+ `experiment runs do not allow behavior-bearing extraArgs '${behaviorSourceArg}'; use structured driver fields so Lux can fingerprint the source`,
87
+ );
88
+ }
89
+
90
+ const isolation = isRecord(config.isolation) ? config.isolation : {};
91
+ const structuredPaths = [
92
+ ...(driverName === "claude-code" && Array.isArray(config.pluginDirs) ? config.pluginDirs : []),
93
+ ...(Array.isArray(config.addDirs) ? config.addDirs : []),
94
+ ...(driverName === "claude-code" && Array.isArray(isolation.allowRead)
95
+ ? isolation.allowRead
96
+ : []),
97
+ ].filter((value): value is string => typeof value === "string");
98
+ const relativeSource = structuredPaths.find((path) => !isAbsolute(path));
99
+ if (relativeSource) {
100
+ throw new Error(
101
+ `experiment driver source paths must be absolute after subjectPath binding: ${relativeSource}`,
102
+ );
103
+ }
104
+ return { driverEnv, extraArgs };
105
+ };
106
+
107
+ const bindCodexExperimentConfig = (
108
+ config: Record<string, unknown>,
109
+ extraArgs: readonly string[],
110
+ ): unknown => {
111
+ if (typeof config.profile === "string") {
112
+ throw new Error(
113
+ "experiment runs do not allow a Codex profile; use inline driver config so Lux can fingerprint it",
114
+ );
115
+ }
116
+ if (config.ephemeral === false) {
117
+ throw new Error("experiment runs require ephemeral Codex sessions");
118
+ }
119
+ const inlineConfig = isRecord(config.config) ? config.config : {};
120
+ const externalConfigKey = ((): string | undefined => {
121
+ const visit = (value: Record<string, unknown>, parent = ""): string | undefined => {
122
+ for (const [key, nested] of Object.entries(value)) {
123
+ const path = parent ? `${parent}.${key}` : key;
124
+ if (/(?:mcp_servers|instructions_file|notify|project_doc)/i.test(key)) return path;
125
+ if (isRecord(nested)) {
126
+ const found = visit(nested, path);
127
+ if (found) return found;
128
+ }
129
+ }
130
+ return undefined;
131
+ };
132
+ return visit(inlineConfig);
133
+ })();
134
+ if (externalConfigKey) {
135
+ throw new Error(
136
+ `experiment runs do not allow path-bearing Codex config '${externalConfigKey}'`,
137
+ );
138
+ }
139
+ const appServer = config.interactionMode !== "exec";
140
+ return {
141
+ ...config,
142
+ ephemeral: true,
143
+ ignoreUserConfig: true,
144
+ ignoreRules: !appServer,
145
+ ...(appServer && !config.isolation ? { isolation: { allowRead: [] } } : {}),
146
+ extraArgs,
147
+ };
148
+ };
149
+
150
+ const bindClaudeExperimentConfig = (
151
+ config: Record<string, unknown>,
152
+ extraArgs: readonly string[],
153
+ driverEnv: Record<string, unknown>,
154
+ ): unknown => {
155
+ if (config.isolation) return config;
156
+ return {
157
+ ...config,
158
+ extraArgs: [
159
+ ...extraArgs,
160
+ "--setting-sources",
161
+ "user",
162
+ "--strict-mcp-config",
163
+ "--settings",
164
+ JSON.stringify({ disableAllHooks: true }),
165
+ ],
166
+ env: {
167
+ ...driverEnv,
168
+ CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
169
+ CLAUDE_CODE_DISABLE_CLAUDE_MDS: "1",
170
+ CLAUDE_CODE_SKIP_PROMPT_HISTORY: "1",
171
+ CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1",
172
+ CLAUDE_CODE_AUTO_CONNECT_IDE: "false",
173
+ CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL: "1",
174
+ DISABLE_AUTOUPDATER: "1",
175
+ },
176
+ };
177
+ };
178
+
179
+ export const prepareClaudeExperimentConfig = <Cfg extends Record<string, unknown>>(
180
+ config: Cfg,
181
+ ): Cfg => {
182
+ const { driverEnv, extraArgs } = validateAgentExperimentConfig("claude-code", config);
183
+ return bindClaudeExperimentConfig(config, extraArgs, driverEnv) as Cfg;
184
+ };
185
+
186
+ export const prepareCodexExperimentConfig = <Cfg extends Record<string, unknown>>(
187
+ config: Cfg,
188
+ ): Cfg => {
189
+ const { extraArgs } = validateAgentExperimentConfig("codex", config);
190
+ return bindCodexExperimentConfig(config, extraArgs) as Cfg;
191
+ };
192
+
193
+ export const prepareSubprocessExperimentConfig = <Cfg extends Record<string, unknown>>(
194
+ config: Cfg,
195
+ ): Cfg => bindSubprocessExperimentConfig(config) as Cfg;
@@ -8,7 +8,7 @@ import {
8
8
  type Artifact,
9
9
  DoneSchema,
10
10
  type DriverEventMap,
11
- type DriverPlugin,
11
+ defineDriver,
12
12
  ErrorEventSchema,
13
13
  type FormatAnswerFn,
14
14
  formatWireAnswer,
@@ -25,6 +25,7 @@ import {
25
25
  TypedEmitter,
26
26
  } from "../../core/index.js";
27
27
  import { walkArtifacts } from "../shared/artifacts.js";
28
+ import { prepareSubprocessExperimentConfig } from "../shared/experiment-policy.js";
28
29
 
29
30
  export const SubprocessDriverConfigSchema = z
30
31
  .object({
@@ -81,6 +82,8 @@ export type SubprocessDriverConfig = z.infer<typeof SubprocessDriverConfigSchema
81
82
  const KILL_GRACE_MS = 5_000;
82
83
  const DEFAULT_MAX_LINE_BYTES = 10_000_000;
83
84
  const MAX_STDERR_TAIL_CHARS = 8_192;
85
+ const MAX_STDERR_CHARS = 65_536;
86
+ const MAX_STDERR_EVENTS = 128;
84
87
  const DEFAULT_MAX_ARTIFACT_FILES = 500;
85
88
  const MAX_FIXTURE_VALUES_BYTES = 64 * 1024;
86
89
  const ARTIFACT_SNAPSHOT_MODULE = fileURLToPath(
@@ -174,15 +177,38 @@ class SubprocessDriverImpl implements AgentDriver {
174
177
  // boundary from being mangled.
175
178
  child.stderr.setEncoding("utf8");
176
179
  let stderrTail = "";
180
+ let stderrChars = 0;
181
+ let stderrEvents = 0;
182
+ let stderrTruncated = false;
183
+ const recordStderr = (line: string): void => {
184
+ if (stderrTruncated || this.terminalSent) return;
185
+ const text = line.slice(0, Math.min(MAX_STDERR_TAIL_CHARS, MAX_STDERR_CHARS - stderrChars));
186
+ stderrChars += text.length;
187
+ stderrEvents++;
188
+ stderrTruncated =
189
+ text.length < line.length ||
190
+ stderrChars >= MAX_STDERR_CHARS ||
191
+ stderrEvents >= MAX_STDERR_EVENTS;
192
+ this.emitInOrder("progress", {
193
+ id: `subprocess-stderr-${stderrEvents}`,
194
+ kind: "driver-stderr",
195
+ payload: { text, truncated: stderrTruncated },
196
+ ts: Date.now(),
197
+ });
198
+ };
177
199
  child.stderr.on("data", (chunk: string) => {
178
200
  const lines = (stderrTail + chunk).split("\n");
179
201
  // Cap the retained partial line so unterminated stderr can't grow
180
202
  // without bound.
181
- stderrTail = (lines.pop() ?? "").slice(0, MAX_STDERR_TAIL_CHARS);
182
- for (const line of lines) process.stderr.write(`[subprocess] ${line}\n`);
203
+ stderrTail = lines.pop() ?? "";
204
+ for (const line of lines) recordStderr(line);
205
+ if (stderrTail.length > MAX_STDERR_TAIL_CHARS) {
206
+ recordStderr(stderrTail);
207
+ stderrTail = "";
208
+ }
183
209
  });
184
210
  child.stderr.on("close", () => {
185
- if (stderrTail) process.stderr.write(`[subprocess] ${stderrTail}\n`);
211
+ if (stderrTail) recordStderr(stderrTail);
186
212
  stderrTail = "";
187
213
  });
188
214
 
@@ -489,8 +515,21 @@ class SubprocessDriverImpl implements AgentDriver {
489
515
  }
490
516
  }
491
517
 
492
- export const subprocessDriver: DriverPlugin<SubprocessDriverConfig> = {
518
+ export const subprocessDriver = defineDriver({
493
519
  name: "subprocess",
494
520
  configSchema: SubprocessDriverConfigSchema,
521
+ runtime: (config) => ({
522
+ commands: [config.command],
523
+ capabilities: {
524
+ interactive: true,
525
+ artifacts: true,
526
+ isolation: false,
527
+ },
528
+ }),
529
+ experiment: {
530
+ version: "1",
531
+ prepare: prepareSubprocessExperimentConfig,
532
+ identity: () => null,
533
+ },
495
534
  create: (config) => new SubprocessDriverImpl(config),
496
- };
535
+ });
package/src/index.ts CHANGED
@@ -16,11 +16,6 @@ export {
16
16
  PersonaAnswererConfigSchema,
17
17
  personaAnswerer,
18
18
  } from "./answerers/persona/index.js";
19
- export {
20
- type ScriptedAnswererConfig,
21
- ScriptedAnswererConfigSchema,
22
- scriptedAnswerer,
23
- } from "./answerers/scripted/index.js";
24
19
  export {
25
20
  type TerminalAnswererConfig,
26
21
  TerminalAnswererConfigSchema,
@@ -188,11 +183,14 @@ export {
188
183
  AnswererModelCallAccountingSchema,
189
184
  type AnswererPlugin,
190
185
  type AnswerHistoryEntry,
186
+ answererRef,
187
+ defineAnswerer,
191
188
  } from "./core/answerer.js";
192
189
  export {
193
190
  inspectRunArtifact,
194
191
  type ReadRunArtifactTextOptions,
195
192
  type RunArtifactInspection,
193
+ type RunArtifactIssueReason,
196
194
  readRunArtifactText,
197
195
  } from "./core/artifact-evidence.js";
198
196
  export {
@@ -231,6 +229,7 @@ export {
231
229
  type EvalCase,
232
230
  type EvalCaseAuthoringOptions,
233
231
  type EvalCaseDefinition,
232
+ type EvalCaseInput,
234
233
  EvalCaseSchema,
235
234
  type FixturesRef,
236
235
  FixturesRefSchema,
@@ -251,8 +250,14 @@ export {
251
250
  type AgentDriver,
252
251
  type Artifact,
253
252
  ArtifactSchema,
253
+ type DriverCapability,
254
+ DriverCapabilitySchema,
254
255
  type DriverEventMap,
256
+ type DriverExperiment,
255
257
  type DriverPlugin,
258
+ type DriverRuntime,
259
+ defineDriver,
260
+ driverRef,
256
261
  type ResolvedFixtures,
257
262
  ResolvedFixturesSchema,
258
263
  type StartContext,
@@ -271,6 +276,7 @@ export {
271
276
  ReportedDriverMetricsSchema,
272
277
  } from "./core/driver-metrics.js";
273
278
  export { type EventHandler, TypedEmitter } from "./core/emitter.js";
279
+ export { type EnvironmentDeclaration, EnvironmentDeclarationSchema } from "./core/environment.js";
274
280
  export {
275
281
  type AgentAttribution,
276
282
  AgentAttributionSchema,
@@ -476,6 +482,11 @@ export {
476
482
  CodexDriverConfigSchema,
477
483
  codexDriver,
478
484
  } from "./drivers/codex/index.js";
485
+ export {
486
+ type CursorDriverConfig,
487
+ CursorDriverConfigSchema,
488
+ cursorDriver,
489
+ } from "./drivers/cursor/index.js";
479
490
  export {
480
491
  type McpProbeDriverConfig,
481
492
  McpProbeDriverConfigSchema,
@@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { dirname, extname, resolve, sep } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { z } from "zod";
5
+ import { assertDisjointClusters } from "../core/evaluation-clusters.js";
5
6
  import {
6
7
  type ExperimentSelector,
7
8
  formatZodIssues,
@@ -73,6 +74,15 @@ export const selectGraderAnnotations = (
73
74
  export const assertDisjointAnnotationSplits = (
74
75
  splits: Array<{ name: string; annotations: readonly GraderAnnotation[] }>,
75
76
  ): void => {
77
+ assertDisjointClusters(
78
+ splits.map((split) => ({
79
+ name: split.name,
80
+ items: split.annotations.map((annotation) => ({
81
+ id: annotation.id,
82
+ cluster: annotation.meta?.cluster,
83
+ })),
84
+ })),
85
+ );
76
86
  const idOwners = new Map<string, string>();
77
87
  const runOwners = new Map<string, string>();
78
88
  for (const split of splits) {
@@ -82,11 +82,10 @@ const acquireAnnotationLock = async (path: string): Promise<() => Promise<void>>
82
82
  }
83
83
  };
84
84
 
85
- export const upsertGraderAnnotation = async (
86
- filePath: string,
85
+ const updateAnnotation = async (
86
+ path: string,
87
87
  annotation: GraderAnnotation,
88
88
  ): Promise<GraderAnnotationDataset> => {
89
- const path = resolve(filePath);
90
89
  await mkdir(dirname(path), { recursive: true });
91
90
  const release = await acquireAnnotationLock(path);
92
91
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
@@ -114,3 +113,25 @@ export const upsertGraderAnnotation = async (
114
113
  await release();
115
114
  }
116
115
  };
116
+
117
+ // Serialize local writers before acquiring the cross-process lock. Otherwise
118
+ // a burst of updates spends its lock deadline contending with its own process.
119
+ const pendingUpdates = new Map<string, Promise<void>>();
120
+
121
+ export const upsertGraderAnnotation = (
122
+ filePath: string,
123
+ annotation: GraderAnnotation,
124
+ ): Promise<GraderAnnotationDataset> => {
125
+ const path = resolve(filePath);
126
+ const previous = pendingUpdates.get(path) ?? Promise.resolve();
127
+ const update = previous.then(() => updateAnnotation(path, annotation));
128
+ const settled = update.then(
129
+ () => undefined,
130
+ () => undefined,
131
+ );
132
+ pendingUpdates.set(path, settled);
133
+ void settled.then(() => {
134
+ if (pendingUpdates.get(path) === settled) pendingUpdates.delete(path);
135
+ });
136
+ return update;
137
+ };
@@ -16,6 +16,7 @@ type CampaignLifecycleContext = {
16
16
  work: unknown;
17
17
  result: unknown;
18
18
  error: unknown;
19
+ failed: boolean;
19
20
  terminalStatus: "passed" | "failed" | "aborted" | undefined;
20
21
  };
21
22
  type CampaignLifecycleOutcome =
@@ -57,6 +58,7 @@ const campaignLifecycleMachine = setup({
57
58
  "output" in event ? context.effects.resultStatus(event.output) : "failed",
58
59
  }),
59
60
  captureError: assign({
61
+ failed: true,
60
62
  error: ({ event }) =>
61
63
  "error" in event ? event.error : new Error("campaign lifecycle failed"),
62
64
  terminalStatus: ({ context, event }) =>
@@ -65,6 +67,7 @@ const campaignLifecycleMachine = setup({
65
67
  ),
66
68
  }),
67
69
  captureReleaseError: assign({
70
+ failed: true,
68
71
  error: ({ event }) => ("error" in event ? event.error : new Error("campaign release failed")),
69
72
  terminalStatus: "failed",
70
73
  }),
@@ -76,6 +79,7 @@ const campaignLifecycleMachine = setup({
76
79
  work: undefined,
77
80
  result: undefined,
78
81
  error: undefined,
82
+ failed: false,
79
83
  terminalStatus: undefined,
80
84
  }),
81
85
  initial: "preparing",
@@ -145,7 +149,7 @@ const campaignLifecycleMachine = setup({
145
149
  failed: { type: "final" },
146
150
  },
147
151
  output: ({ context }) => {
148
- if (context.error !== undefined) {
152
+ if (context.failed) {
149
153
  return {
150
154
  status: context.terminalStatus === "aborted" ? "aborted" : "failed",
151
155
  error: context.error,