@bjoernrochel/sevro 0.1.0-rc.8 → 1.1.0

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 (582) hide show
  1. package/CONTRIBUTING.md +52 -6
  2. package/README.md +12 -7
  3. package/dist/examples/basic/graded.json +13 -0
  4. package/dist/examples/basic/host.js +9 -0
  5. package/dist/examples/basic/host.js.map +1 -0
  6. package/dist/examples/basic/prompt-only.json +7 -0
  7. package/dist/examples/recipes/candidate.js +23 -0
  8. package/dist/examples/recipes/candidate.js.map +1 -0
  9. package/dist/examples/recipes/cases/coding-change.json +21 -0
  10. package/dist/examples/recipes/cases/host-tools.json +21 -0
  11. package/dist/examples/recipes/cases/mise.json +21 -0
  12. package/dist/examples/recipes/cases/node.json +21 -0
  13. package/dist/examples/recipes/cases/python-uv.json +21 -0
  14. package/dist/examples/recipes/cases/semantic.json +17 -0
  15. package/dist/examples/recipes/coding-change/check.js +16 -0
  16. package/dist/examples/recipes/coding-change/check.js.map +1 -0
  17. package/dist/examples/recipes/executable.js +20 -0
  18. package/dist/examples/recipes/executable.js.map +1 -0
  19. package/dist/examples/recipes/extension-artifacts.js +56 -0
  20. package/dist/examples/recipes/extension-artifacts.js.map +1 -0
  21. package/dist/examples/recipes/extension-methods.js +167 -0
  22. package/dist/examples/recipes/extension-methods.js.map +1 -0
  23. package/dist/examples/recipes/extension.js +28 -0
  24. package/dist/examples/recipes/extension.js.map +1 -0
  25. package/dist/examples/recipes/modules.js +9 -0
  26. package/dist/examples/recipes/modules.js.map +1 -0
  27. package/dist/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  28. package/dist/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  29. package/dist/examples/recipes/repository-skill/README.md +3 -0
  30. package/dist/examples/recipes/run-inputs.js +144 -0
  31. package/dist/examples/recipes/run-inputs.js.map +1 -0
  32. package/dist/examples/recipes/run-options.js +103 -0
  33. package/dist/examples/recipes/run-options.js.map +1 -0
  34. package/dist/examples/recipes/run.js +62 -0
  35. package/dist/examples/recipes/run.js.map +1 -0
  36. package/dist/examples/recipes/runtime/empty.json +1 -0
  37. package/dist/examples/recipes/runtime/goals.json +1 -0
  38. package/dist/examples/recipes/runtime/mise.json +6 -0
  39. package/dist/examples/recipes/runtime/path.json +1 -0
  40. package/dist/examples/recipes/runtime/plugins.json +4 -0
  41. package/dist/examples/recipes/runtime/python-uv.json +10 -0
  42. package/dist/examples/recipes/semantic-judge.js +27 -0
  43. package/dist/examples/recipes/semantic-judge.js.map +1 -0
  44. package/dist/examples/templates/coding/adapter.js +14 -0
  45. package/dist/examples/templates/coding/adapter.js.map +1 -0
  46. package/dist/examples/templates/coding/case.json +15 -0
  47. package/dist/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  48. package/dist/examples/templates/repository-skill/extension-data.js +61 -0
  49. package/dist/examples/templates/repository-skill/extension-data.js.map +1 -0
  50. package/dist/examples/templates/repository-skill/extension.js +29 -0
  51. package/dist/examples/templates/repository-skill/extension.js.map +1 -0
  52. package/dist/examples/templates/response/adapter.js +13 -0
  53. package/dist/examples/templates/response/adapter.js.map +1 -0
  54. package/dist/examples/templates/response/case.json +13 -0
  55. package/dist/src/advisory.js +86 -0
  56. package/dist/src/advisory.js.map +1 -0
  57. package/dist/src/candidate-artifacts.js +139 -0
  58. package/dist/src/candidate-artifacts.js.map +1 -0
  59. package/dist/src/candidate-codex-state.js +110 -0
  60. package/dist/src/candidate-codex-state.js.map +1 -0
  61. package/dist/src/case-validation.js +92 -0
  62. package/dist/src/case-validation.js.map +1 -0
  63. package/dist/src/cli/cancellation.js +21 -0
  64. package/dist/src/cli/cancellation.js.map +1 -0
  65. package/dist/src/cli/command.js +60 -0
  66. package/dist/src/cli/command.js.map +1 -0
  67. package/dist/src/cli/completion.js +14 -0
  68. package/dist/src/cli/completion.js.map +1 -0
  69. package/dist/src/cli/demo.js +45 -0
  70. package/dist/src/cli/demo.js.map +1 -0
  71. package/dist/src/cli/evaluation.js +40 -0
  72. package/dist/src/cli/evaluation.js.map +1 -0
  73. package/dist/src/cli/extension.js +54 -0
  74. package/dist/src/cli/extension.js.map +1 -0
  75. package/dist/src/cli/help.js +190 -0
  76. package/dist/src/cli/help.js.map +1 -0
  77. package/dist/src/cli/hosts.js +33 -0
  78. package/dist/src/cli/hosts.js.map +1 -0
  79. package/dist/src/cli/init-settings.js +29 -0
  80. package/dist/src/cli/init-settings.js.map +1 -0
  81. package/dist/src/cli/init.js +82 -0
  82. package/dist/src/cli/init.js.map +1 -0
  83. package/dist/src/cli/invocation.js +431 -0
  84. package/dist/src/cli/invocation.js.map +1 -0
  85. package/dist/src/cli/loading.js +110 -0
  86. package/dist/src/cli/loading.js.map +1 -0
  87. package/dist/src/cli/preparation.js +88 -0
  88. package/dist/src/cli/preparation.js.map +1 -0
  89. package/dist/src/cli/presentation.js +61 -0
  90. package/dist/src/cli/presentation.js.map +1 -0
  91. package/dist/src/cli/progress.js +5 -0
  92. package/dist/src/cli/progress.js.map +1 -0
  93. package/dist/src/cli/run-manifest.js +108 -0
  94. package/dist/src/cli/run-manifest.js.map +1 -0
  95. package/dist/src/cli/run-options.js +367 -0
  96. package/dist/src/cli/run-options.js.map +1 -0
  97. package/dist/src/cli/run-overrides.js +51 -0
  98. package/dist/src/cli/run-overrides.js.map +1 -0
  99. package/dist/src/cli/settings.js +127 -0
  100. package/dist/src/cli/settings.js.map +1 -0
  101. package/dist/src/cli/summary.js +28 -0
  102. package/dist/src/cli/summary.js.map +1 -0
  103. package/dist/src/cli.js +20 -0
  104. package/dist/src/cli.js.map +1 -0
  105. package/dist/src/contract-types.js +2 -0
  106. package/dist/src/contract-types.js.map +1 -0
  107. package/dist/src/engine.js +4 -0
  108. package/dist/src/engine.js.map +1 -0
  109. package/dist/src/evaluation/advisory.js +85 -0
  110. package/dist/src/evaluation/advisory.js.map +1 -0
  111. package/dist/src/evaluation/check-plan.js +292 -0
  112. package/dist/src/evaluation/check-plan.js.map +1 -0
  113. package/dist/src/evaluation/collection.js +61 -0
  114. package/dist/src/evaluation/collection.js.map +1 -0
  115. package/dist/src/evaluation/context.js +2 -0
  116. package/dist/src/evaluation/context.js.map +1 -0
  117. package/dist/src/evaluation/grading-host.js +28 -0
  118. package/dist/src/evaluation/grading-host.js.map +1 -0
  119. package/dist/src/evaluation/grading.js +154 -0
  120. package/dist/src/evaluation/grading.js.map +1 -0
  121. package/dist/src/evaluation/invocation-plan.js +312 -0
  122. package/dist/src/evaluation/invocation-plan.js.map +1 -0
  123. package/dist/src/evaluation/plan.js +376 -0
  124. package/dist/src/evaluation/plan.js.map +1 -0
  125. package/dist/src/evaluation/progress.js +14 -0
  126. package/dist/src/evaluation/progress.js.map +1 -0
  127. package/dist/src/evaluation/resources.js +159 -0
  128. package/dist/src/evaluation/resources.js.map +1 -0
  129. package/dist/src/evaluation/run.js +148 -0
  130. package/dist/src/evaluation/run.js.map +1 -0
  131. package/dist/src/evaluation/scheduler.js +27 -0
  132. package/dist/src/evaluation/scheduler.js.map +1 -0
  133. package/dist/src/evaluation/semantic.js +183 -0
  134. package/dist/src/evaluation/semantic.js.map +1 -0
  135. package/dist/src/evaluation/trial.js +593 -0
  136. package/dist/src/evaluation/trial.js.map +1 -0
  137. package/dist/src/evaluation/types.js +20 -0
  138. package/dist/src/evaluation/types.js.map +1 -0
  139. package/dist/src/evidence/host-results.js +67 -0
  140. package/dist/src/evidence/host-results.js.map +1 -0
  141. package/dist/src/evidence/retained-artifacts.js +17 -0
  142. package/dist/src/evidence/retained-artifacts.js.map +1 -0
  143. package/dist/src/evidence/trial-writer.js +131 -0
  144. package/dist/src/evidence/trial-writer.js.map +1 -0
  145. package/dist/src/evidence/usage.js +14 -0
  146. package/dist/src/evidence/usage.js.map +1 -0
  147. package/dist/src/executable.js +20 -0
  148. package/dist/src/executable.js.map +1 -0
  149. package/dist/src/extension-client.js +212 -0
  150. package/dist/src/extension-client.js.map +1 -0
  151. package/dist/src/extension-session.js +236 -0
  152. package/dist/src/extension-session.js.map +1 -0
  153. package/dist/src/fixtures/advisory.js +206 -0
  154. package/dist/src/fixtures/advisory.js.map +1 -0
  155. package/dist/src/fixtures/bin.js +41 -0
  156. package/dist/src/fixtures/bin.js.map +1 -0
  157. package/dist/src/fixtures/cleanup.js +43 -0
  158. package/dist/src/fixtures/cleanup.js.map +1 -0
  159. package/dist/src/fixtures/create.js +94 -0
  160. package/dist/src/fixtures/create.js.map +1 -0
  161. package/dist/src/fixtures/generated.js +181 -0
  162. package/dist/src/fixtures/generated.js.map +1 -0
  163. package/dist/src/fixtures/git-hooks.js +16 -0
  164. package/dist/src/fixtures/git-hooks.js.map +1 -0
  165. package/dist/src/fixtures/preparation.js +201 -0
  166. package/dist/src/fixtures/preparation.js.map +1 -0
  167. package/dist/src/fixtures/repository.js +255 -0
  168. package/dist/src/fixtures/repository.js.map +1 -0
  169. package/dist/src/fixtures/setup.js +175 -0
  170. package/dist/src/fixtures/setup.js.map +1 -0
  171. package/dist/src/fixtures/tools.js +57 -0
  172. package/dist/src/fixtures/tools.js.map +1 -0
  173. package/dist/src/fixtures/workspace-state.js +155 -0
  174. package/dist/src/fixtures/workspace-state.js.map +1 -0
  175. package/dist/src/generated/cli-result.cjs +2 -0
  176. package/dist/src/generated/extension.cjs +2 -0
  177. package/dist/src/generated/report.cjs +2 -0
  178. package/dist/src/generated/run-evidence.cjs +2 -0
  179. package/dist/src/generated/runtime.cjs +2 -0
  180. package/dist/src/git.js +72 -0
  181. package/dist/src/git.js.map +1 -0
  182. package/dist/src/graders/artifact.js +68 -0
  183. package/dist/src/graders/artifact.js.map +1 -0
  184. package/dist/src/graders/git-head.js +112 -0
  185. package/dist/src/graders/git-head.js.map +1 -0
  186. package/dist/src/graders/output.js +345 -0
  187. package/dist/src/graders/output.js.map +1 -0
  188. package/dist/src/graders/semantic.js +132 -0
  189. package/dist/src/graders/semantic.js.map +1 -0
  190. package/dist/src/graders/shell.js +321 -0
  191. package/dist/src/graders/shell.js.map +1 -0
  192. package/dist/src/host-diagnostic-redaction.js +52 -0
  193. package/dist/src/host-diagnostic-redaction.js.map +1 -0
  194. package/dist/src/host-diagnostics.js +248 -0
  195. package/dist/src/host-diagnostics.js.map +1 -0
  196. package/dist/src/hosts/claude-continuation.js +89 -0
  197. package/dist/src/hosts/claude-continuation.js.map +1 -0
  198. package/dist/src/hosts/claude-credential.js +121 -0
  199. package/dist/src/hosts/claude-credential.js.map +1 -0
  200. package/dist/src/hosts/claude-events.js +136 -0
  201. package/dist/src/hosts/claude-events.js.map +1 -0
  202. package/dist/src/hosts/claude-keychain.js +100 -0
  203. package/dist/src/hosts/claude-keychain.js.map +1 -0
  204. package/dist/src/hosts/claude-login-recovery.js +67 -0
  205. package/dist/src/hosts/claude-login-recovery.js.map +1 -0
  206. package/dist/src/hosts/claude-login-refresh.js +146 -0
  207. package/dist/src/hosts/claude-login-refresh.js.map +1 -0
  208. package/dist/src/hosts/claude-login-store.js +124 -0
  209. package/dist/src/hosts/claude-login-store.js.map +1 -0
  210. package/dist/src/hosts/claude-login.js +74 -0
  211. package/dist/src/hosts/claude-login.js.map +1 -0
  212. package/dist/src/hosts/claude-native-goal.js +138 -0
  213. package/dist/src/hosts/claude-native-goal.js.map +1 -0
  214. package/dist/src/hosts/claude-process.js +10 -0
  215. package/dist/src/hosts/claude-process.js.map +1 -0
  216. package/dist/src/hosts/claude-refresh-lock.js +76 -0
  217. package/dist/src/hosts/claude-refresh-lock.js.map +1 -0
  218. package/dist/src/hosts/claude-repository-invocation.js +229 -0
  219. package/dist/src/hosts/claude-repository-invocation.js.map +1 -0
  220. package/dist/src/hosts/claude-settings.js +156 -0
  221. package/dist/src/hosts/claude-settings.js.map +1 -0
  222. package/dist/src/hosts/claude-skill-activations.js +131 -0
  223. package/dist/src/hosts/claude-skill-activations.js.map +1 -0
  224. package/dist/src/hosts/claude-state.js +237 -0
  225. package/dist/src/hosts/claude-state.js.map +1 -0
  226. package/dist/src/hosts/claude-tool-calls.js +150 -0
  227. package/dist/src/hosts/claude-tool-calls.js.map +1 -0
  228. package/dist/src/hosts/claude.js +373 -0
  229. package/dist/src/hosts/claude.js.map +1 -0
  230. package/dist/src/hosts/codex-app-server.js +764 -0
  231. package/dist/src/hosts/codex-app-server.js.map +1 -0
  232. package/dist/src/hosts/codex-configuration.js +91 -0
  233. package/dist/src/hosts/codex-configuration.js.map +1 -0
  234. package/dist/src/hosts/codex-events.js +104 -0
  235. package/dist/src/hosts/codex-events.js.map +1 -0
  236. package/dist/src/hosts/codex-exec-controls.js +434 -0
  237. package/dist/src/hosts/codex-exec-controls.js.map +1 -0
  238. package/dist/src/hosts/codex-invocation.js +54 -0
  239. package/dist/src/hosts/codex-invocation.js.map +1 -0
  240. package/dist/src/hosts/codex-marketplace.js +185 -0
  241. package/dist/src/hosts/codex-marketplace.js.map +1 -0
  242. package/dist/src/hosts/codex-native-calls.js +516 -0
  243. package/dist/src/hosts/codex-native-calls.js.map +1 -0
  244. package/dist/src/hosts/codex-process.js +10 -0
  245. package/dist/src/hosts/codex-process.js.map +1 -0
  246. package/dist/src/hosts/codex-profile.js +206 -0
  247. package/dist/src/hosts/codex-profile.js.map +1 -0
  248. package/dist/src/hosts/codex-session-files.js +89 -0
  249. package/dist/src/hosts/codex-session-files.js.map +1 -0
  250. package/dist/src/hosts/codex-state.js +310 -0
  251. package/dist/src/hosts/codex-state.js.map +1 -0
  252. package/dist/src/hosts/codex.js +527 -0
  253. package/dist/src/hosts/codex.js.map +1 -0
  254. package/dist/src/hosts/isolation-common.js +43 -0
  255. package/dist/src/hosts/isolation-common.js.map +1 -0
  256. package/dist/src/hosts/isolation-roots.js +64 -0
  257. package/dist/src/hosts/isolation-roots.js.map +1 -0
  258. package/dist/src/hosts/isolation.js +12 -0
  259. package/dist/src/hosts/isolation.js.map +1 -0
  260. package/dist/src/hosts/linux-sandbox.js +127 -0
  261. package/dist/src/hosts/linux-sandbox.js.map +1 -0
  262. package/dist/src/hosts/mac-sandbox.js +137 -0
  263. package/dist/src/hosts/mac-sandbox.js.map +1 -0
  264. package/dist/src/hosts/native-controls.js +151 -0
  265. package/dist/src/hosts/native-controls.js.map +1 -0
  266. package/dist/src/hosts/native-request.js +33 -0
  267. package/dist/src/hosts/native-request.js.map +1 -0
  268. package/dist/src/hosts/process-lifecycle.js +167 -0
  269. package/dist/src/hosts/process-lifecycle.js.map +1 -0
  270. package/dist/src/hosts/protected-worktrees.js +81 -0
  271. package/dist/src/hosts/protected-worktrees.js.map +1 -0
  272. package/dist/src/hosts/skill-hook-catalog.js +100 -0
  273. package/dist/src/hosts/skill-hook-catalog.js.map +1 -0
  274. package/dist/src/hosts/skill-hook-handler.js +228 -0
  275. package/dist/src/hosts/skill-hook-handler.js.map +1 -0
  276. package/dist/src/hosts/skill-hooks.js +423 -0
  277. package/dist/src/hosts/skill-hooks.js.map +1 -0
  278. package/dist/src/hosts/workspace-fingerprint.js +72 -0
  279. package/dist/src/hosts/workspace-fingerprint.js.map +1 -0
  280. package/dist/src/identity.js +167 -0
  281. package/dist/src/identity.js.map +1 -0
  282. package/dist/src/instrumentation.js +83 -0
  283. package/dist/src/instrumentation.js.map +1 -0
  284. package/dist/src/native-transcript-state.js +85 -0
  285. package/dist/src/native-transcript-state.js.map +1 -0
  286. package/dist/src/native-transcript-view.js +65 -0
  287. package/dist/src/native-transcript-view.js.map +1 -0
  288. package/dist/src/native-transcripts.js +208 -0
  289. package/dist/src/native-transcripts.js.map +1 -0
  290. package/dist/src/paths.js +13 -0
  291. package/dist/src/paths.js.map +1 -0
  292. package/dist/src/process-lifecycle.js +101 -0
  293. package/dist/src/process-lifecycle.js.map +1 -0
  294. package/dist/src/provenance.js +263 -0
  295. package/dist/src/provenance.js.map +1 -0
  296. package/dist/src/prune.js +228 -0
  297. package/dist/src/prune.js.map +1 -0
  298. package/dist/src/report.js +276 -0
  299. package/dist/src/report.js.map +1 -0
  300. package/dist/src/resolved-case.js +29 -0
  301. package/dist/src/resolved-case.js.map +1 -0
  302. package/dist/src/results-lock.js +40 -0
  303. package/dist/src/results-lock.js.map +1 -0
  304. package/dist/src/results.js +127 -0
  305. package/dist/src/results.js.map +1 -0
  306. package/dist/src/run-owner.js +283 -0
  307. package/dist/src/run-owner.js.map +1 -0
  308. package/dist/src/runner-paths.js +11 -0
  309. package/dist/src/runner-paths.js.map +1 -0
  310. package/dist/src/runtime/config.js +323 -0
  311. package/dist/src/runtime/config.js.map +1 -0
  312. package/dist/src/runtime/discovery-apple.js +65 -0
  313. package/dist/src/runtime/discovery-apple.js.map +1 -0
  314. package/dist/src/runtime/discovery-providers.js +37 -0
  315. package/dist/src/runtime/discovery-providers.js.map +1 -0
  316. package/dist/src/runtime/discovery.js +40 -0
  317. package/dist/src/runtime/discovery.js.map +1 -0
  318. package/dist/src/runtime/hook-processes.js +81 -0
  319. package/dist/src/runtime/hook-processes.js.map +1 -0
  320. package/dist/src/runtime/hooks.js +304 -0
  321. package/dist/src/runtime/hooks.js.map +1 -0
  322. package/dist/src/runtime/paths.js +244 -0
  323. package/dist/src/runtime/paths.js.map +1 -0
  324. package/dist/src/runtime/query.js +76 -0
  325. package/dist/src/runtime/query.js.map +1 -0
  326. package/dist/src/runtime/seeds.js +98 -0
  327. package/dist/src/runtime/seeds.js.map +1 -0
  328. package/dist/src/runtime/shared-cache.js +81 -0
  329. package/dist/src/runtime/shared-cache.js.map +1 -0
  330. package/dist/src/runtime/state.js +106 -0
  331. package/dist/src/runtime/state.js.map +1 -0
  332. package/dist/src/runtime-process.js +148 -0
  333. package/dist/src/runtime-process.js.map +1 -0
  334. package/dist/src/schema-types.js +2 -0
  335. package/dist/src/schema-types.js.map +1 -0
  336. package/dist/src/schema.js +16 -0
  337. package/dist/src/schema.js.map +1 -0
  338. package/dist/src/shell.js +5 -0
  339. package/dist/src/shell.js.map +1 -0
  340. package/dist/src/sqlite-lock.js +18 -0
  341. package/dist/src/sqlite-lock.js.map +1 -0
  342. package/dist/src/storage.js +21 -0
  343. package/dist/src/storage.js.map +1 -0
  344. package/dist/src/transcript-retention.js +109 -0
  345. package/dist/src/transcript-retention.js.map +1 -0
  346. package/dist/src/value-guards.js +26 -0
  347. package/dist/src/value-guards.js.map +1 -0
  348. package/docs/README.md +19 -15
  349. package/docs/architecture.md +141 -17
  350. package/docs/assets/README.md +5 -4
  351. package/docs/builtin-graders-v1.md +9 -2
  352. package/docs/codex-host.md +155 -57
  353. package/docs/contracts.md +2 -1
  354. package/docs/creating-cases.md +10 -4
  355. package/docs/development-cli.md +197 -24
  356. package/docs/documentation-quality.md +16 -5
  357. package/docs/evaluation-workflow.md +110 -0
  358. package/docs/extension-protocol-v1.md +26 -3
  359. package/docs/getting-started.md +29 -7
  360. package/docs/guide-evaluation.md +34 -2
  361. package/docs/identity-v1.md +29 -3
  362. package/docs/installing.md +35 -17
  363. package/docs/licensing.md +2 -2
  364. package/docs/native-controls.md +76 -5
  365. package/docs/native-hosts.md +197 -8
  366. package/docs/reading-results.md +102 -0
  367. package/docs/recipes.md +490 -0
  368. package/docs/releases.md +48 -13
  369. package/docs/report-v1.md +29 -1
  370. package/docs/results-v1.md +122 -4
  371. package/docs/run-settings-v1.md +147 -0
  372. package/docs/runtime-v1.md +255 -22
  373. package/docs/specs/repository-guide.md +1 -1
  374. package/docs/troubleshooting.md +12 -12
  375. package/docs/typescript-quality.md +106 -26
  376. package/examples/recipes/candidate.ts +26 -0
  377. package/examples/recipes/cases/coding-change.json +21 -0
  378. package/examples/recipes/cases/host-tools.json +21 -0
  379. package/examples/recipes/cases/mise.json +21 -0
  380. package/examples/recipes/cases/node.json +21 -0
  381. package/examples/recipes/cases/python-uv.json +21 -0
  382. package/examples/recipes/cases/semantic.json +17 -0
  383. package/examples/recipes/coding-change/check.ts +23 -0
  384. package/examples/recipes/executable.ts +19 -0
  385. package/examples/recipes/extension-artifacts.ts +65 -0
  386. package/examples/recipes/extension-methods.ts +174 -0
  387. package/examples/recipes/extension.ts +38 -0
  388. package/examples/recipes/modules.ts +9 -0
  389. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  390. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  391. package/examples/recipes/repository-skill/README.md +3 -0
  392. package/examples/recipes/run-inputs.ts +183 -0
  393. package/examples/recipes/run-options.ts +132 -0
  394. package/examples/recipes/run.ts +77 -0
  395. package/examples/recipes/runtime/empty.json +1 -0
  396. package/examples/recipes/runtime/goals.json +1 -0
  397. package/examples/recipes/runtime/mise.json +6 -0
  398. package/examples/recipes/runtime/path.json +1 -0
  399. package/examples/recipes/runtime/plugins.json +4 -0
  400. package/examples/recipes/runtime/python-uv.json +10 -0
  401. package/examples/recipes/semantic-judge.ts +29 -0
  402. package/examples/templates/coding/adapter.ts +17 -0
  403. package/examples/templates/coding/case.json +15 -0
  404. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  405. package/examples/templates/repository-skill/extension-data.ts +64 -0
  406. package/examples/templates/repository-skill/extension.ts +32 -0
  407. package/examples/templates/response/adapter.ts +12 -0
  408. package/examples/templates/response/case.json +13 -0
  409. package/package.json +23 -9
  410. package/schemas/report-v1.schema.json +9 -0
  411. package/schemas/run-evidence-v1.schema.json +2 -1
  412. package/schemas/runtime-v1.schema.json +43 -7
  413. package/src/advisory.ts +2 -2
  414. package/src/candidate-artifacts.ts +42 -11
  415. package/src/candidate-codex-state.ts +185 -0
  416. package/src/case-validation.ts +73 -50
  417. package/src/cli/cancellation.ts +20 -0
  418. package/src/cli/command.ts +87 -0
  419. package/src/cli/completion.ts +15 -0
  420. package/src/cli/demo.ts +61 -0
  421. package/src/cli/evaluation.ts +55 -0
  422. package/src/cli/extension.ts +79 -0
  423. package/src/cli/help.ts +199 -0
  424. package/src/cli/hosts.ts +55 -0
  425. package/src/cli/init-settings.ts +31 -0
  426. package/src/cli/init.ts +112 -0
  427. package/src/{cli-invocation.ts → cli/invocation.ts} +215 -218
  428. package/src/cli/loading.ts +151 -0
  429. package/src/cli/preparation.ts +154 -0
  430. package/src/cli/presentation.ts +93 -0
  431. package/src/cli/progress.ts +6 -0
  432. package/src/cli/run-manifest.ts +128 -0
  433. package/src/cli/run-options.ts +403 -0
  434. package/src/cli/run-overrides.ts +69 -0
  435. package/src/cli/settings.ts +154 -0
  436. package/src/cli/summary.ts +43 -0
  437. package/src/cli.ts +14 -592
  438. package/src/contract-types.ts +94 -0
  439. package/src/engine.ts +9 -5
  440. package/src/evaluation/advisory.ts +150 -0
  441. package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
  442. package/src/evaluation/collection.ts +88 -0
  443. package/src/evaluation/context.ts +10 -0
  444. package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
  445. package/src/evaluation/grading.ts +235 -0
  446. package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
  447. package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
  448. package/src/evaluation/progress.ts +19 -0
  449. package/src/evaluation/resources.ts +241 -0
  450. package/src/evaluation/run.ts +206 -0
  451. package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +77 -87
  452. package/src/evaluation/trial.ts +800 -0
  453. package/src/{evaluation-types.ts → evaluation/types.ts} +79 -29
  454. package/src/evidence/host-results.ts +85 -0
  455. package/src/evidence/retained-artifacts.ts +18 -0
  456. package/src/evidence/trial-writer.ts +203 -0
  457. package/src/evidence/usage.ts +17 -0
  458. package/src/executable.ts +19 -0
  459. package/src/extension-client.ts +40 -108
  460. package/src/extension-session.ts +24 -48
  461. package/src/{advisory-fixture.ts → fixtures/advisory.ts} +12 -72
  462. package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
  463. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  464. package/src/fixtures/create.ts +183 -0
  465. package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
  466. package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
  467. package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
  468. package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
  469. package/src/{fixture-setup.ts → fixtures/setup.ts} +36 -53
  470. package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
  471. package/src/{workspace-state.ts → fixtures/workspace-state.ts} +5 -5
  472. package/src/generated/cli-result.d.cts +2 -1
  473. package/src/generated/extension.d.cts +2 -1
  474. package/src/generated/report.cjs +1 -1
  475. package/src/generated/report.d.cts +2 -1
  476. package/src/generated/run-evidence.cjs +1 -1
  477. package/src/generated/run-evidence.d.cts +2 -1
  478. package/src/generated/runtime.cjs +1 -1
  479. package/src/generated/runtime.d.cts +2 -1
  480. package/src/git.ts +94 -0
  481. package/src/graders/git-head.ts +31 -85
  482. package/src/graders/shell.ts +54 -71
  483. package/src/host-diagnostic-redaction.ts +52 -0
  484. package/src/host-diagnostics.ts +331 -0
  485. package/src/hosts/claude-continuation.ts +57 -40
  486. package/src/hosts/claude-credential.ts +160 -63
  487. package/src/hosts/claude-events.ts +34 -18
  488. package/src/hosts/claude-keychain.ts +79 -21
  489. package/src/hosts/claude-login-recovery.ts +85 -0
  490. package/src/hosts/claude-login-refresh.ts +179 -0
  491. package/src/hosts/claude-login-store.ts +162 -0
  492. package/src/hosts/claude-login.ts +107 -0
  493. package/src/hosts/claude-native-goal.ts +15 -12
  494. package/src/hosts/claude-process.ts +10 -50
  495. package/src/hosts/claude-refresh-lock.ts +82 -0
  496. package/src/hosts/claude-repository-invocation.ts +9 -34
  497. package/src/hosts/claude-settings.ts +46 -9
  498. package/src/hosts/claude-skill-activations.ts +169 -0
  499. package/src/hosts/claude-state.ts +28 -15
  500. package/src/hosts/claude-tool-calls.ts +13 -10
  501. package/src/hosts/claude.ts +121 -81
  502. package/src/hosts/codex-app-server.ts +78 -52
  503. package/src/hosts/codex-configuration.ts +5 -10
  504. package/src/hosts/codex-exec-controls.ts +516 -0
  505. package/src/hosts/codex-marketplace.ts +12 -10
  506. package/src/hosts/codex-native-calls.ts +15 -122
  507. package/src/hosts/codex-process.ts +10 -183
  508. package/src/hosts/codex-profile.ts +36 -21
  509. package/src/hosts/codex-session-files.ts +1 -9
  510. package/src/hosts/codex-state.ts +134 -18
  511. package/src/hosts/codex.ts +210 -183
  512. package/src/hosts/isolation-common.ts +8 -11
  513. package/src/hosts/isolation-roots.ts +5 -4
  514. package/src/hosts/linux-sandbox.ts +15 -2
  515. package/src/hosts/mac-sandbox.ts +87 -18
  516. package/src/hosts/native-controls.ts +72 -22
  517. package/src/hosts/native-request.ts +55 -0
  518. package/src/hosts/process-lifecycle.ts +215 -49
  519. package/src/hosts/protected-worktrees.ts +3 -2
  520. package/src/hosts/skill-hook-catalog.ts +131 -0
  521. package/src/hosts/skill-hook-handler.ts +374 -0
  522. package/src/hosts/skill-hooks.ts +585 -0
  523. package/src/instrumentation.ts +7 -8
  524. package/src/native-transcript-state.ts +22 -3
  525. package/src/native-transcript-view.ts +52 -13
  526. package/src/native-transcripts.ts +26 -11
  527. package/src/paths.ts +22 -0
  528. package/src/process-lifecycle.ts +150 -0
  529. package/src/provenance.ts +34 -13
  530. package/src/prune.ts +300 -0
  531. package/src/report.ts +63 -36
  532. package/src/resolved-case.ts +1 -1
  533. package/src/results-lock.ts +42 -0
  534. package/src/results.ts +14 -18
  535. package/src/run-owner.ts +15 -9
  536. package/src/runner-paths.ts +21 -0
  537. package/src/{runtime-config.ts → runtime/config.ts} +160 -46
  538. package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
  539. package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
  540. package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
  541. package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +3 -2
  542. package/src/{runtime-hooks.ts → runtime/hooks.ts} +40 -28
  543. package/src/{runtime-paths.ts → runtime/paths.ts} +5 -19
  544. package/src/{runtime-query.ts → runtime/query.ts} +5 -5
  545. package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
  546. package/src/runtime/shared-cache.ts +130 -0
  547. package/src/{runtime-state.ts → runtime/state.ts} +28 -11
  548. package/src/runtime-process.ts +194 -0
  549. package/src/schema-types.ts +73 -161
  550. package/src/shell.ts +4 -0
  551. package/src/sqlite-lock.ts +25 -0
  552. package/src/transcript-retention.ts +146 -0
  553. package/src/value-guards.ts +22 -0
  554. package/docs/assets/sevro-logo.png +0 -0
  555. package/docs/assets/sevro-symbol.svg +0 -1
  556. package/docs/assets/sevro-wordmark.svg +0 -1
  557. package/docs/cache-ownership-validation.md +0 -49
  558. package/docs/claude-continuation-validation.md +0 -47
  559. package/docs/concurrency-validation.md +0 -86
  560. package/docs/configuration-root-validation.md +0 -70
  561. package/docs/engine-slice.md +0 -114
  562. package/docs/evidence/runtime-startup-30/README.md +0 -185
  563. package/docs/evidence/runtime-startup-30/after.json +0 -319
  564. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
  565. package/docs/evidence/runtime-startup-30/before.json +0 -403
  566. package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
  567. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
  568. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
  569. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
  570. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
  571. package/docs/evidence.md +0 -17
  572. package/docs/fixture-cleanup-validation.md +0 -73
  573. package/docs/guide-verification.json +0 -239
  574. package/src/evaluation-context.ts +0 -259
  575. package/src/evaluation-fixture.ts +0 -302
  576. package/src/evaluation-run.ts +0 -200
  577. package/src/evaluation-trial.ts +0 -1016
  578. package/src/hosts/claude-nested-skills.ts +0 -272
  579. package/src/hosts/codex-command-output.ts +0 -510
  580. package/src/hosts/codex-executor-command.ts +0 -291
  581. package/src/hosts/codex-skill-reads.ts +0 -783
  582. /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
@@ -1,6 +1,12 @@
1
+ import { spawn, type Subprocess } from "./runtime-process";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import validExchange from "./generated/extension.cjs";
3
4
  import { isRecord, isStringArray } from "./value-guards";
5
+ import {
6
+ captureBoundedBytes,
7
+ ProcessDeadline,
8
+ terminateOwnedProcessGroup,
9
+ } from "./process-lifecycle";
4
10
 
5
11
  const MAX_MESSAGE_BYTES = 8 * 1024 * 1024;
6
12
  const MAX_DIAGNOSTIC_BYTES = 64 * 1024;
@@ -35,61 +41,6 @@ export interface ExchangeOptions {
35
41
  signal?: AbortSignal;
36
42
  }
37
43
 
38
- function boundedChunk(
39
- value: Uint8Array,
40
- size: number,
41
- limit: number,
42
- truncate: boolean,
43
- ): Uint8Array {
44
- if (!truncate && size + value.byteLength > limit)
45
- throw new ExtensionProtocolError("extension message exceeds 8 MiB");
46
- return value.subarray(0, Math.max(0, limit - size));
47
- }
48
-
49
- async function readBounded(
50
- stream: ReadableStream<Uint8Array>,
51
- limit: number,
52
- truncate: boolean,
53
- ): Promise<Uint8Array> {
54
- const reader = stream.getReader();
55
- const chunks: Uint8Array[] = [];
56
- let size = 0;
57
- for (;;) {
58
- const { value, done } = await reader.read();
59
- if (done) break;
60
- const kept = boundedChunk(value, size, limit, truncate);
61
- if (kept.byteLength) chunks.push(kept);
62
- size += kept.byteLength;
63
- }
64
- return concatenateChunks(chunks, size);
65
- }
66
-
67
- function concatenateChunks(chunks: Uint8Array[], size: number): Uint8Array {
68
- const result = new Uint8Array(size);
69
- let offset = 0;
70
- for (const chunk of chunks) {
71
- result.set(chunk, offset);
72
- offset += chunk.byteLength;
73
- }
74
- return result;
75
- }
76
-
77
- function terminate(proc: Bun.Subprocess): void {
78
- if (process.platform !== "win32") {
79
- try {
80
- process.kill(-proc.pid, "SIGKILL");
81
- return;
82
- } catch {
83
- // The group may have exited while the runner was handling the error.
84
- }
85
- }
86
- try {
87
- proc.kill("SIGKILL");
88
- } catch {
89
- // The process already exited.
90
- }
91
- }
92
-
93
44
  interface SuccessfulExtensionResponse extends ExtensionResponse {
94
45
  result: Record<string, unknown>;
95
46
  error?: never;
@@ -121,9 +72,9 @@ function extensionPayload(request: ExtensionRequest): Uint8Array {
121
72
  function spawnExtension(
122
73
  command: string[],
123
74
  options: ExchangeOptions,
124
- ): Bun.Subprocess {
75
+ ): Subprocess {
125
76
  try {
126
- return Bun.spawn(command, {
77
+ return spawn(command, {
127
78
  cwd: options.cwd,
128
79
  stdin: "pipe",
129
80
  stdout: "pipe",
@@ -137,22 +88,15 @@ function spawnExtension(
137
88
  }
138
89
  }
139
90
 
140
- function extensionWriter(proc: Bun.Subprocess) {
91
+ function extensionWriter(proc: Subprocess) {
141
92
  const stdin = proc.stdin;
142
93
  if (!stdin || typeof stdin === "number")
143
94
  throw new ExtensionProtocolError("extension pipes are unavailable");
144
95
  return stdin;
145
96
  }
146
97
 
147
- function extensionReaders(proc: Bun.Subprocess) {
98
+ function extensionReaders(proc: Subprocess) {
148
99
  const { stdout, stderr } = proc;
149
- if (
150
- !stdout ||
151
- typeof stdout === "number" ||
152
- !stderr ||
153
- typeof stderr === "number"
154
- )
155
- throw new ExtensionProtocolError("extension pipes are unavailable");
156
100
  return { stdout, stderr };
157
101
  }
158
102
 
@@ -217,13 +161,23 @@ function assertSuccessfulResponse(
217
161
  }
218
162
 
219
163
  class ExtensionExchange {
220
- private timer: ReturnType<typeof setTimeout> | undefined;
221
- private cancel: (() => void) | undefined;
164
+ private readonly deadline: ProcessDeadline;
222
165
  constructor(
223
- private readonly proc: Bun.Subprocess,
224
- private readonly options: ExchangeOptions,
225
- private readonly timeoutMs: number,
226
- ) {}
166
+ private readonly proc: Subprocess,
167
+ options: ExchangeOptions,
168
+ timeoutMs: number,
169
+ ) {
170
+ this.deadline = new ProcessDeadline({
171
+ timeoutMs,
172
+ signal: options.signal,
173
+ interruption: (reason) =>
174
+ new ExtensionProtocolError(
175
+ reason === "timeout"
176
+ ? "extension timed out"
177
+ : "extension request cancelled",
178
+ ),
179
+ });
180
+ }
227
181
 
228
182
  private async output(
229
183
  request: ExtensionRequest,
@@ -232,9 +186,17 @@ class ExtensionExchange {
232
186
  const stdin = extensionWriter(this.proc);
233
187
  const { stdout, stderr } = extensionReaders(this.proc);
234
188
  const write = writeExtensionRequest(stdin, payload);
235
- const [bytes, , exitCode] = await Promise.all([
236
- readBounded(stdout, MAX_MESSAGE_BYTES, false),
237
- readBounded(stderr, MAX_DIAGNOSTIC_BYTES, true),
189
+ const [{ bytes }, , exitCode] = await Promise.all([
190
+ captureBoundedBytes(stdout, {
191
+ limit: MAX_MESSAGE_BYTES,
192
+ overflow: "reject",
193
+ exceeded: () =>
194
+ new ExtensionProtocolError("extension message exceeds 8 MiB"),
195
+ }),
196
+ captureBoundedBytes(stderr, {
197
+ limit: MAX_DIAGNOSTIC_BYTES,
198
+ overflow: "truncate",
199
+ }),
238
200
  this.proc.exited,
239
201
  write,
240
202
  ]);
@@ -248,48 +210,18 @@ class ExtensionExchange {
248
210
  return response;
249
211
  }
250
212
 
251
- private timeout(): Promise<never> {
252
- return new Promise((_resolve, reject) => {
253
- this.timer = setTimeout(() => {
254
- reject(new ExtensionProtocolError("extension timed out"));
255
- }, this.timeoutMs);
256
- });
257
- }
258
-
259
- private aborted(): Promise<never> {
260
- return new Promise((_resolve, reject) => {
261
- const signal = this.options.signal;
262
- if (!signal) return;
263
- this.cancel = () => {
264
- reject(new ExtensionProtocolError("extension request cancelled"));
265
- };
266
- if (signal.aborted) this.cancel();
267
- else signal.addEventListener("abort", this.cancel, { once: true });
268
- });
269
- }
270
-
271
- private dispose(): void {
272
- if (this.timer) clearTimeout(this.timer);
273
- if (this.cancel)
274
- this.options.signal?.removeEventListener("abort", this.cancel);
275
- }
276
-
277
213
  async run(
278
214
  request: ExtensionRequest,
279
215
  payload: Uint8Array,
280
216
  ): Promise<SuccessfulExtensionResponse> {
281
217
  try {
282
- return await Promise.race([
283
- this.output(request, payload),
284
- this.timeout(),
285
- this.aborted(),
286
- ]);
218
+ return await this.deadline.wait(this.output(request, payload));
287
219
  } catch (cause) {
288
- terminate(this.proc);
220
+ terminateOwnedProcessGroup(this.proc);
289
221
  if (cause instanceof ExtensionProtocolError) throw cause;
290
222
  throw new ExtensionProtocolError("extension transport failed", { cause });
291
223
  } finally {
292
- this.dispose();
224
+ this.deadline.dispose();
293
225
  }
294
226
  }
295
227
  }
@@ -4,9 +4,20 @@ import { createHash, randomUUID } from "node:crypto";
4
4
  import { createReadStream } from "node:fs";
5
5
  import { isAbsolute } from "node:path";
6
6
  import { canonicalJson, hashJson } from "./identity";
7
- import type { GeneratedFixture } from "./generated-fixture";
8
- import type { RepositoryFixture } from "./repository-fixture";
9
- import type { FixtureSetupDeclaration } from "./fixture-setup";
7
+ import type { GeneratedFixture } from "./fixtures/generated";
8
+ import type { RepositoryFixture } from "./fixtures/repository";
9
+ import type { FixtureSetupDeclaration } from "./fixtures/setup";
10
+ import type {
11
+ ArtifactReference,
12
+ Assessment,
13
+ CheckDeclaration,
14
+ DomainOutcome,
15
+ ExtensionCheckOutcome,
16
+ MetricEvidence,
17
+ ObservationEvidence,
18
+ TaskVerdict,
19
+ } from "./contract-types";
20
+ import type { InstrumentationRequest } from "./instrumentation";
10
21
  import {
11
22
  ExtensionProtocolError,
12
23
  exchangeExtension,
@@ -32,11 +43,7 @@ export interface ExtensionCase {
32
43
  | { kind: "inline"; files: Record<string, string> }
33
44
  | RepositoryFixture
34
45
  | GeneratedFixture;
35
- checks: {
36
- id: string;
37
- grader: string;
38
- configuration: Record<string, unknown>;
39
- }[];
46
+ checks: CheckDeclaration[];
40
47
  requiredEvidence: string[];
41
48
  extensionData: Record<string, unknown>;
42
49
  }
@@ -51,10 +58,7 @@ export interface PreparationResult {
51
58
  gitExclude?: boolean;
52
59
  executable?: boolean;
53
60
  }[];
54
- requestedInstrumentation: {
55
- id: string;
56
- configuration: Record<string, unknown>;
57
- }[];
61
+ requestedInstrumentation: InstrumentationRequest[];
58
62
  fixtureSetup?: FixtureSetupDeclaration;
59
63
  codexMarketplace?: {
60
64
  artifactRoot: string;
@@ -77,48 +81,20 @@ export interface PreparationResult {
77
81
 
78
82
  export interface EvaluationRequest {
79
83
  caseId: string;
80
- execution: {
81
- status: "completed" | "failed" | "cancelled" | "not_run";
84
+ execution: Assessment["execution"] & {
82
85
  errorCode?: string;
83
86
  };
84
- observations: {
85
- id: string;
86
- source: string;
87
- completeness: "complete" | "partial" | "unavailable";
88
- data: Record<string, unknown>;
89
- }[];
90
- builtinChecks: {
91
- id: string;
92
- status: "passed" | "failed" | "unavailable";
93
- detail?: string;
94
- evidenceRefs: string[];
95
- }[];
96
- artifacts: {
97
- id: string;
98
- path: string;
99
- sha256: string;
100
- gitExclude?: boolean;
101
- executable?: boolean;
102
- }[];
87
+ observations: ObservationEvidence[];
88
+ builtinChecks: ExtensionCheckOutcome[];
89
+ artifacts: ArtifactReference[];
103
90
  extensionData: Record<string, unknown>;
104
91
  }
105
92
 
106
93
  export interface EvaluationResult {
107
- checks: {
108
- id: string;
109
- status: "passed" | "failed" | "unavailable";
110
- detail?: string;
111
- evidenceRefs: string[];
112
- }[];
113
- metrics: { id: string; value: number | null; unit: string }[];
114
- domainOutcomes?: {
115
- id: string;
116
- status: "passed" | "failed" | "unavailable";
117
- detail?: string;
118
- evidenceRefs: string[];
119
- data?: Record<string, unknown>;
120
- }[];
121
- taskVerdictRecommendation?: "passed" | "failed" | "not_assessed";
94
+ checks: ExtensionCheckOutcome[];
95
+ metrics: MetricEvidence[];
96
+ domainOutcomes?: DomainOutcome[];
97
+ taskVerdictRecommendation?: TaskVerdict;
122
98
  }
123
99
 
124
100
  export interface ExtensionSessionOptions extends ExchangeOptions {
@@ -1,5 +1,5 @@
1
1
  import type { Dirent, Stats } from "node:fs";
2
- import { isMissingFile } from "./fixture-tools";
2
+ import { isMissingFile } from "./tools";
3
3
  import {
4
4
  lstat,
5
5
  mkdir,
@@ -11,7 +11,9 @@ import {
11
11
  copyFile,
12
12
  } from "node:fs/promises";
13
13
  import { allocateWorkspace, removeWorkspace } from "./workspace-state";
14
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
14
+ import { dirname, isAbsolute, join, resolve } from "node:path";
15
+ import { insideRoot as contained } from "../paths";
16
+ import { runGit } from "../git";
15
17
  import { fixtureParts } from "./preparation";
16
18
 
17
19
  const MAX_GIT_OUTPUT = 16 * 1024 * 1024;
@@ -19,87 +21,25 @@ const MAX_UNTRACKED_BYTES = 32 * 1024 * 1024;
19
21
  const MAX_UNTRACKED_FILES = 1024;
20
22
  const HIDDEN_ROOTS = [".agents", ".claude", ".codex", ".git"];
21
23
 
22
- function contained(root: string, path: string): boolean {
23
- const child = relative(root, path);
24
- return (
25
- child === "" ||
26
- (child !== ".." && !child.startsWith(`..${sep}`) && !isAbsolute(child))
27
- );
28
- }
29
-
30
24
  function excluded(path: string, roots: string[]): boolean {
31
25
  return roots.some((root) => path === root || path.startsWith(`${root}/`));
32
26
  }
33
27
 
34
- async function bounded(stream: ReadableStream<Uint8Array>): Promise<Buffer> {
35
- const chunks: Uint8Array[] = [];
36
- let size = 0;
37
- for await (const chunk of stream) {
38
- size += chunk.byteLength;
39
- if (size > MAX_GIT_OUTPUT)
40
- throw new Error("advisory fixture Git output is too large");
41
- chunks.push(chunk);
42
- }
43
- return Buffer.concat(chunks);
44
- }
45
-
46
- function nullGitConfig(): string {
47
- return process.platform === "win32" ? "NUL" : "/dev/null";
48
- }
49
- function gitEnvironment(): Record<string, string> {
50
- return {
51
- PATH: process.env.PATH ?? "/usr/bin:/bin",
52
- ...(process.env.SystemRoot ? { SystemRoot: process.env.SystemRoot } : {}),
53
- GIT_CONFIG_NOSYSTEM: "1",
54
- GIT_CONFIG_GLOBAL: nullGitConfig(),
55
- GIT_TERMINAL_PROMPT: "0",
56
- GIT_AUTHOR_NAME: "Sevro Advisory",
57
- GIT_AUTHOR_EMAIL: "advisory@sevro.invalid",
58
- GIT_COMMITTER_NAME: "Sevro Advisory",
59
- GIT_COMMITTER_EMAIL: "advisory@sevro.invalid",
60
- GIT_AUTHOR_DATE: "2000-01-01T00:00:00+00:00",
61
- GIT_COMMITTER_DATE: "2000-01-01T00:00:00+00:00",
62
- };
63
- }
64
-
65
28
  async function git(
66
29
  cwd: string,
67
30
  args: string[],
68
31
  input?: Uint8Array,
69
32
  ): Promise<Buffer> {
70
- const proc = Bun.spawn(
71
- ["git", "-c", `core.hooksPath=${nullGitConfig()}`, ...args],
72
- {
73
- cwd,
74
- stdin: input ? "pipe" : "ignore",
75
- stdout: "pipe",
76
- stderr: "pipe",
77
- env: gitEnvironment(),
78
- },
79
- );
80
33
  try {
81
- const write = (async () => {
82
- if (!input) return;
83
- if (!proc.stdin || typeof proc.stdin === "number")
84
- throw new Error("advisory fixture Git input unavailable");
85
- await proc.stdin.write(input);
86
- await proc.stdin.end();
87
- })();
88
- const [out, , code] = await Promise.all([
89
- bounded(proc.stdout),
90
- bounded(proc.stderr),
91
- proc.exited,
92
- write,
93
- ]);
94
- if (code !== 0) throw new Error("advisory fixture Git operation failed");
95
- return out;
34
+ return await runGit({
35
+ cwd,
36
+ args,
37
+ input,
38
+ identity: "advisory",
39
+ label: "advisory fixture",
40
+ maxOutputBytes: MAX_GIT_OUTPUT,
41
+ });
96
42
  } catch (cause) {
97
- try {
98
- proc.kill("SIGKILL");
99
- } catch {
100
- // The process may already have exited.
101
- }
102
- await proc.exited;
103
43
  throw new Error("advisory fixture Git operation failed", { cause });
104
44
  }
105
45
  }
@@ -2,7 +2,7 @@ import {
2
2
  prepareFixtureTools,
3
3
  installFixtureTools,
4
4
  isMissingFile,
5
- } from "./fixture-tools";
5
+ } from "./tools";
6
6
  import { lstat, mkdir, realpath } from "node:fs/promises";
7
7
  import { join } from "node:path";
8
8
 
@@ -15,6 +15,18 @@ async function restoreDirectoryAccess(path: string): Promise<void> {
15
15
  await restoreDirectoryAccess(join(path, child.name));
16
16
  }
17
17
 
18
+ /** Remove owned disposable state without following cache or fixture symlinks. */
19
+ export async function removeDisposableDirectory(path: string): Promise<void> {
20
+ try {
21
+ await rm(path, { recursive: true, force: true });
22
+ } catch (error) {
23
+ if ((error as NodeJS.ErrnoException).code === "ENOTDIR") return;
24
+ if (!permissionFailure(error)) throw error;
25
+ await restoreDirectoryAccess(path);
26
+ await rm(path, { recursive: true, force: true });
27
+ }
28
+ }
29
+
18
30
  /** Clear a completed fixture while preserving its reserved root for active peers. */
19
31
  export async function clearFixtureContents(workspace: string): Promise<void> {
20
32
  const root = await lstat(workspace);
@@ -26,13 +38,7 @@ export async function clearFixtureContents(workspace: string): Promise<void> {
26
38
  await Promise.all(
27
39
  names.map(async (name) => {
28
40
  const path = join(workspace, name);
29
- try {
30
- await rm(path, { recursive: true, force: true });
31
- } catch (error) {
32
- if (!permissionFailure(error)) throw error;
33
- await restoreDirectoryAccess(path);
34
- await rm(path, { recursive: true, force: true });
35
- }
41
+ await removeDisposableDirectory(path);
36
42
  }),
37
43
  );
38
44
  }
@@ -0,0 +1,183 @@
1
+ import { appendFile, mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { allocateWorkspace, removeWorkspace } from "./workspace-state";
4
+ import { fixtureBinDirectory, installFixtureBin } from "./bin";
5
+ import { installGitHooks } from "./git-hooks";
6
+ import {
7
+ materializeGeneratedFixture,
8
+ type GeneratedFixture,
9
+ } from "./generated";
10
+ import { runFixtureSetup, type FixtureSetup } from "./setup";
11
+ import {
12
+ fixtureParts,
13
+ safePreparationTarget,
14
+ type InlineArtifact,
15
+ type PreparationSources,
16
+ } from "./preparation";
17
+ import {
18
+ applyRepositoryOverlay,
19
+ cloneRepositorySource,
20
+ type RepositoryFixture,
21
+ type RepositorySource,
22
+ } from "./repository";
23
+ import {
24
+ EvaluationConfigurationError,
25
+ type ResolvedCase,
26
+ } from "../evaluation/types";
27
+
28
+ export type FixtureSource<Repository = RepositorySource> =
29
+ | { kind: "inline"; files: ResolvedCase["fixture"]["files"] }
30
+ | { kind: "generated"; fixture: GeneratedFixture }
31
+ | { kind: "repository"; fixture: RepositoryFixture; repository: Repository };
32
+
33
+ interface FixtureOptions {
34
+ artifacts?: InlineArtifact[];
35
+ sources?: PreparationSources;
36
+ setup?: FixtureSetup | null;
37
+ projectRoot: string;
38
+ signal?: AbortSignal;
39
+ reservedWorkspace?: string;
40
+ candidateRuntimeRoot?: string;
41
+ }
42
+
43
+ export async function createFixture(
44
+ source: FixtureSource,
45
+ options: FixtureOptions,
46
+ ): Promise<string> {
47
+ const paths = inlineFixturePaths(source);
48
+ const artifacts = options.artifacts ?? [];
49
+ const workspace = options.reservedWorkspace ?? (await allocateWorkspace());
50
+ try {
51
+ await initializeFixtureHistory(source, options.sources, workspace);
52
+ await installDeclaredFixtureTools(source, workspace);
53
+ await writeInlineFixtureFiles(paths, workspace);
54
+ await setupFixture(
55
+ options.setup,
56
+ workspace,
57
+ options.projectRoot,
58
+ options.signal,
59
+ options.candidateRuntimeRoot,
60
+ );
61
+ await mountPreparationArtifacts(artifacts, workspace);
62
+ await excludePreparationArtifacts(artifacts, workspace);
63
+ await fixtureBinDirectory(workspace);
64
+ return workspace;
65
+ } catch (error) {
66
+ if (!options.reservedWorkspace) await removeWorkspace(workspace);
67
+ throw error;
68
+ }
69
+ }
70
+
71
+ function inlineFixturePaths(source: FixtureSource) {
72
+ return Object.entries(
73
+ source.kind === "inline" ? (source.files ?? {}) : {},
74
+ ).map(([path, content]) => ({ path, parts: fixtureParts(path), content }));
75
+ }
76
+
77
+ async function cloneFixtureRepository(
78
+ source: Extract<FixtureSource, { kind: "repository" }>,
79
+ sources: PreparationSources | undefined,
80
+ workspace: string,
81
+ ): Promise<void> {
82
+ if (!sources)
83
+ throw new EvaluationConfigurationError(
84
+ "repository source root is required and must be absolute",
85
+ );
86
+ await cloneRepositorySource(
87
+ source.fixture.sourceRef,
88
+ sources,
89
+ source.repository,
90
+ workspace,
91
+ );
92
+ }
93
+
94
+ async function initializeFixtureHistory(
95
+ source: FixtureSource,
96
+ sources: PreparationSources | undefined,
97
+ workspace: string,
98
+ ): Promise<void> {
99
+ switch (source.kind) {
100
+ case "repository":
101
+ await cloneFixtureRepository(source, sources, workspace);
102
+ await applyRepositoryOverlay(source.fixture, workspace);
103
+ break;
104
+ case "generated":
105
+ await materializeGeneratedFixture(source.fixture, workspace);
106
+ break;
107
+ case "inline":
108
+ break;
109
+ }
110
+ }
111
+
112
+ async function installDeclaredFixtureTools(
113
+ source: FixtureSource,
114
+ workspace: string,
115
+ ): Promise<void> {
116
+ const fixture = source.kind === "inline" ? undefined : source.fixture;
117
+ await installFixtureBin(fixture?.bin, workspace);
118
+ await installGitHooks(fixture?.hooks, workspace);
119
+ }
120
+
121
+ async function writeInlineFixtureFiles(
122
+ files: ReturnType<typeof inlineFixturePaths>,
123
+ workspace: string,
124
+ ): Promise<void> {
125
+ for (const file of files) {
126
+ const target = join(workspace, ...file.parts);
127
+ await mkdir(dirname(target), { recursive: true, mode: 0o700 });
128
+ await writeFile(target, file.content, { flag: "wx", mode: 0o600 });
129
+ }
130
+ }
131
+
132
+ async function setupFixture(
133
+ setup: FixtureSetup | null | undefined,
134
+ workspace: string,
135
+ projectRoot: string,
136
+ signal?: AbortSignal,
137
+ candidateRuntimeRoot?: string,
138
+ ): Promise<void> {
139
+ if (!setup) return;
140
+ await runFixtureSetup(setup, {
141
+ workspace,
142
+ projectRoot,
143
+ fixtureBinDir: (await fixtureBinDirectory(workspace)) ?? undefined,
144
+ signal,
145
+ candidateRuntimeRoot,
146
+ });
147
+ }
148
+
149
+ async function mountPreparationArtifacts(
150
+ artifacts: InlineArtifact[],
151
+ workspace: string,
152
+ ): Promise<void> {
153
+ for (const artifact of artifacts) {
154
+ const target = await safePreparationTarget(
155
+ workspace,
156
+ artifact.relativePath,
157
+ );
158
+ await writeFile(target, artifact.bytes, {
159
+ flag: "wx",
160
+ mode: artifact.executable ? 0o700 : 0o600,
161
+ });
162
+ }
163
+ }
164
+
165
+ async function excludePreparationArtifacts(
166
+ artifacts: InlineArtifact[],
167
+ workspace: string,
168
+ ): Promise<void> {
169
+ const gitExcluded = artifacts.filter((artifact) => artifact.gitExclude);
170
+ if (!gitExcluded.length) return;
171
+ const special = new Set(["\\", "*", "?", "[", "]", "#", "!", " "]);
172
+ const patterns = gitExcluded.map((artifact) =>
173
+ Array.from(artifact.relativePath)
174
+ .map((character) =>
175
+ special.has(character) ? `\\${character}` : character,
176
+ )
177
+ .join(""),
178
+ );
179
+ await appendFile(
180
+ join(workspace, ".git", "info", "exclude"),
181
+ `\n${patterns.map((path) => `/${path}`).join("\n")}\n`,
182
+ );
183
+ }