@bjoernrochel/sevro 1.0.0 → 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 (403) hide show
  1. package/CONTRIBUTING.md +34 -4
  2. package/README.md +7 -6
  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/architecture.md +5 -2
  349. package/docs/creating-cases.md +2 -3
  350. package/docs/development-cli.md +12 -7
  351. package/docs/documentation-quality.md +4 -3
  352. package/docs/evaluation-workflow.md +8 -8
  353. package/docs/getting-started.md +7 -6
  354. package/docs/guide-evaluation.md +1 -1
  355. package/docs/identity-v1.md +4 -1
  356. package/docs/installing.md +31 -13
  357. package/docs/licensing.md +2 -2
  358. package/docs/native-hosts.md +1 -1
  359. package/docs/recipes.md +41 -40
  360. package/docs/releases.md +20 -4
  361. package/docs/run-settings-v1.md +3 -3
  362. package/docs/specs/repository-guide.md +1 -1
  363. package/docs/troubleshooting.md +1 -1
  364. package/docs/typescript-quality.md +18 -1
  365. package/examples/recipes/executable.ts +19 -0
  366. package/examples/recipes/extension-artifacts.ts +1 -1
  367. package/examples/recipes/extension.ts +2 -1
  368. package/examples/recipes/modules.ts +9 -0
  369. package/examples/recipes/run-inputs.ts +14 -19
  370. package/examples/recipes/run-options.ts +9 -8
  371. package/examples/recipes/run.ts +21 -7
  372. package/examples/templates/repository-skill/extension-data.ts +1 -1
  373. package/examples/templates/repository-skill/extension.ts +2 -1
  374. package/package.json +14 -6
  375. package/src/cli/demo.ts +3 -2
  376. package/src/cli/help.ts +4 -3
  377. package/src/cli/init-settings.ts +5 -4
  378. package/src/cli/init.ts +9 -8
  379. package/src/cli/invocation.ts +2 -1
  380. package/src/cli/preparation.ts +2 -2
  381. package/src/cli.ts +1 -1
  382. package/src/executable.ts +19 -0
  383. package/src/extension-client.ts +6 -12
  384. package/src/fixtures/setup.ts +3 -2
  385. package/src/git.ts +2 -1
  386. package/src/graders/git-head.ts +3 -2
  387. package/src/graders/shell.ts +4 -3
  388. package/src/hosts/claude-keychain.ts +3 -2
  389. package/src/hosts/claude-login-refresh.ts +3 -2
  390. package/src/hosts/claude-refresh-lock.ts +2 -1
  391. package/src/hosts/isolation-roots.ts +3 -2
  392. package/src/hosts/process-lifecycle.ts +4 -6
  393. package/src/hosts/protected-worktrees.ts +2 -1
  394. package/src/hosts/skill-hook-handler.ts +1 -1
  395. package/src/hosts/skill-hooks.ts +10 -2
  396. package/src/provenance.ts +7 -6
  397. package/src/results-lock.ts +4 -4
  398. package/src/run-owner.ts +15 -9
  399. package/src/runner-paths.ts +21 -0
  400. package/src/runtime/hook-processes.ts +2 -1
  401. package/src/runtime/hooks.ts +9 -7
  402. package/src/runtime-process.ts +194 -0
  403. package/src/sqlite-lock.ts +25 -0
@@ -0,0 +1,208 @@
1
+ import { lstat, readdir, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { gzipSync } from "node:zlib";
4
+ import { sha256 } from "./identity.js";
5
+ import { HostExecutionFailure } from "./evaluation/types.js";
6
+ import { isRecord } from "./value-guards.js";
7
+ import { rethrowDiagnosticPersistenceError, } from "./host-diagnostics.js";
8
+ export function candidateTranscriptRoots(root) {
9
+ return root ? [root] : [];
10
+ }
11
+ export function candidateTranscriptEnvironment(root) {
12
+ return root ? { SEVRO_CANDIDATE_TRANSCRIPTS: root } : {};
13
+ }
14
+ export async function retainEnabledNativeTranscripts(result, policy, root, host, role, rootSessionId) {
15
+ if (policy?.nativeTranscripts === true)
16
+ await retainNativeTranscripts(result, root, host, role, rootSessionId ?? observedRootSession(result));
17
+ }
18
+ function observedRootSession(result) {
19
+ const observation = result.observations?.find((item) => item.id === "sevro.host.native-goal");
20
+ const session = observation?.data.threadId;
21
+ return typeof session === "string" ? session : undefined;
22
+ }
23
+ export async function failedNativeTranscripts(cause, policy, root, host, role, diagnostics) {
24
+ rethrowDiagnosticPersistenceError(cause);
25
+ requireFailureCapture(cause, policy, diagnostics);
26
+ const result = cause instanceof HostExecutionFailure
27
+ ? cause.result
28
+ : {
29
+ finalMessage: null,
30
+ complete: false,
31
+ executionFailed: true,
32
+ actualCondition: "passive",
33
+ };
34
+ await diagnostics.retain(result, cause);
35
+ await retainEnabledNativeTranscripts(result, policy, root, host, role);
36
+ return result;
37
+ }
38
+ function requireFailureCapture(cause, policy, diagnostics) {
39
+ if (policy?.nativeTranscripts !== true && !diagnostics.enabled)
40
+ throw cause;
41
+ }
42
+ const MAX_FILES = 4096;
43
+ const MAX_BYTES = 5 * 1024 * 1024;
44
+ const MAX_ENCODED_FILES_BYTES = 7 * 1024 * 1024;
45
+ /** Capture only a fresh role's transcript tree; never follow native source links. */
46
+ export async function retainNativeTranscripts(result, root, host, role = "candidate", rootSessionId = null) {
47
+ const capture = {
48
+ files: [],
49
+ bytes: 0,
50
+ entries: 0,
51
+ encodedBytes: 0,
52
+ issues: [],
53
+ };
54
+ try {
55
+ await captureDirectory(root, "", capture);
56
+ }
57
+ catch {
58
+ capture.issues.push("source_unavailable");
59
+ }
60
+ observeCaptureRoot(capture, rootSessionId, host);
61
+ const completeness = captureCompleteness(capture, result);
62
+ const bundle = {
63
+ format: "sevro.native-transcripts.v2",
64
+ host,
65
+ role,
66
+ rootSessionId,
67
+ completeness,
68
+ issues: capture.issues,
69
+ files: capture.files,
70
+ };
71
+ const lists = nativeResultLists(result);
72
+ lists.artifacts.push({
73
+ id: "sevro.native-transcripts.bundle",
74
+ bytes: Buffer.from(JSON.stringify(bundle)),
75
+ });
76
+ lists.observations.push({
77
+ id: "sevro.host.native-transcripts",
78
+ completeness,
79
+ data: {
80
+ method: "native_files",
81
+ host,
82
+ role,
83
+ rootSessionId,
84
+ artifactId: "sevro.native-transcripts.bundle",
85
+ issues: capture.issues,
86
+ files: capture.files.map(({ path, sha256 }) => ({ path, sha256 })),
87
+ },
88
+ });
89
+ }
90
+ function nativeResultLists(result) {
91
+ result.artifacts ??= [];
92
+ result.observations ??= [];
93
+ return { artifacts: result.artifacts, observations: result.observations };
94
+ }
95
+ function observeCaptureRoot(capture, session, host) {
96
+ if (session === null) {
97
+ capture.issues.push("root_session_unknown");
98
+ return;
99
+ }
100
+ const suffix = host === "codex" ? `-${session}.jsonl` : `/${session}.jsonl`;
101
+ if (!capture.files.some((file) => file.path.endsWith(suffix)))
102
+ capture.issues.push("root_session_missing");
103
+ }
104
+ function captureCompleteness(capture, result) {
105
+ if (!capture.files.some((file) => file.path.endsWith(".jsonl")))
106
+ return "unavailable";
107
+ if (capture.issues.length || !result.complete || result.executionFailed)
108
+ return "partial";
109
+ return "complete";
110
+ }
111
+ async function captureDirectory(root, path, capture) {
112
+ const directory = join(root, path);
113
+ if (!(await lstat(directory)).isDirectory())
114
+ throw new Error("invalid source directory");
115
+ for (const name of (await readdir(directory)).sort()) {
116
+ capture.entries++;
117
+ if (capture.entries > MAX_FILES) {
118
+ capture.issues.push("entry_limit");
119
+ return;
120
+ }
121
+ await captureEntry(root, join(path, name), capture);
122
+ }
123
+ }
124
+ async function captureEntry(root, path, capture) {
125
+ try {
126
+ const info = await lstat(join(root, path));
127
+ if (info.isDirectory()) {
128
+ await captureDirectory(root, path, capture);
129
+ return;
130
+ }
131
+ if (!info.isFile()) {
132
+ capture.issues.push("unsafe_entry");
133
+ return;
134
+ }
135
+ await captureFile(root, path, info.size, capture);
136
+ }
137
+ catch {
138
+ capture.issues.push("entry_unavailable");
139
+ }
140
+ }
141
+ async function captureFile(root, path, size, capture) {
142
+ if (capture.bytes + size > MAX_BYTES) {
143
+ capture.issues.push("byte_limit");
144
+ return;
145
+ }
146
+ const bytes = await readFile(join(root, path));
147
+ if (bytes.length !== size) {
148
+ capture.issues.push("source_changed");
149
+ return;
150
+ }
151
+ const file = {
152
+ path,
153
+ sha256: sha256(bytes),
154
+ gzipBase64: gzipSync(bytes).toString("base64"),
155
+ };
156
+ const issue = nativeFileIssue(path, bytes);
157
+ if (issue && !capture.issues.includes(issue))
158
+ capture.issues.push(issue);
159
+ retainCaptureFile(file, bytes.length, capture);
160
+ }
161
+ function nativeFileIssue(path, bytes) {
162
+ if (path.endsWith(".json"))
163
+ return nativeSidecarIssue(bytes);
164
+ if (!path.endsWith(".jsonl"))
165
+ return null;
166
+ return nativeTranscriptIssue(bytes);
167
+ }
168
+ function nativeTranscriptIssue(bytes) {
169
+ try {
170
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
171
+ if (!text.trim())
172
+ return "empty_transcript";
173
+ const records = text.replace(/\r?\n$/, "").split("\n");
174
+ if (!records.every(validNativeRecord))
175
+ return "malformed_transcript";
176
+ return null;
177
+ }
178
+ catch {
179
+ return "malformed_transcript";
180
+ }
181
+ }
182
+ function nativeSidecarIssue(bytes) {
183
+ try {
184
+ JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
185
+ return null;
186
+ }
187
+ catch {
188
+ return "malformed_sidecar";
189
+ }
190
+ }
191
+ function validNativeRecord(line) {
192
+ const record = JSON.parse(line);
193
+ return isRecord(record) && nativeRecordType(record.type);
194
+ }
195
+ function nativeRecordType(type) {
196
+ return typeof type === "string" && type.trim().length > 0;
197
+ }
198
+ function retainCaptureFile(file, size, capture) {
199
+ const encoded = Buffer.byteLength(JSON.stringify(file));
200
+ if (capture.encodedBytes + encoded > MAX_ENCODED_FILES_BYTES) {
201
+ capture.issues.push("bundle_limit");
202
+ return;
203
+ }
204
+ capture.bytes += size;
205
+ capture.encodedBytes += encoded;
206
+ capture.files.push(file);
207
+ }
208
+ //# sourceMappingURL=native-transcripts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-transcripts.js","sourceRoot":"","sources":["native-transcripts.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { gzipSync } from \"node:zlib\";\nimport { sha256 } from \"./identity\";\nimport { HostExecutionFailure, type HostResult } from \"./evaluation/types\";\nimport type { RuntimeRole } from \"./runtime/state\";\nimport type { RuntimePolicy } from \"./runtime/config\";\nimport { isRecord } from \"./value-guards\";\nimport {\n rethrowDiagnosticPersistenceError,\n type HostDiagnostics,\n} from \"./host-diagnostics\";\n\nexport function candidateTranscriptRoots(root: string | undefined): string[] {\n return root ? [root] : [];\n}\n\nexport function candidateTranscriptEnvironment(\n root: string | undefined,\n): Record<string, string> {\n return root ? { SEVRO_CANDIDATE_TRANSCRIPTS: root } : {};\n}\n\nexport async function retainEnabledNativeTranscripts(\n result: HostResult,\n policy: RuntimePolicy | undefined,\n root: string,\n host: \"codex\" | \"claude\",\n role?: RuntimeRole,\n rootSessionId?: string,\n): Promise<void> {\n if (policy?.nativeTranscripts === true)\n await retainNativeTranscripts(\n result,\n root,\n host,\n role,\n rootSessionId ?? observedRootSession(result),\n );\n}\n\nfunction observedRootSession(result: HostResult): string | undefined {\n const observation = result.observations?.find(\n (item) => item.id === \"sevro.host.native-goal\",\n );\n const session = observation?.data.threadId;\n return typeof session === \"string\" ? session : undefined;\n}\n\nexport async function failedNativeTranscripts(\n cause: unknown,\n policy: RuntimePolicy | undefined,\n root: string,\n host: \"codex\" | \"claude\",\n role: RuntimeRole | undefined,\n diagnostics: HostDiagnostics,\n): Promise<HostResult> {\n rethrowDiagnosticPersistenceError(cause);\n requireFailureCapture(cause, policy, diagnostics);\n const result: HostResult =\n cause instanceof HostExecutionFailure\n ? cause.result\n : {\n finalMessage: null,\n complete: false,\n executionFailed: true,\n actualCondition: \"passive\",\n };\n await diagnostics.retain(result, cause);\n await retainEnabledNativeTranscripts(result, policy, root, host, role);\n return result;\n}\n\nfunction requireFailureCapture(\n cause: unknown,\n policy: RuntimePolicy | undefined,\n diagnostics: HostDiagnostics,\n): void {\n if (policy?.nativeTranscripts !== true && !diagnostics.enabled) throw cause;\n}\n\nconst MAX_FILES = 4096;\nconst MAX_BYTES = 5 * 1024 * 1024;\nconst MAX_ENCODED_FILES_BYTES = 7 * 1024 * 1024;\ntype NativeFile = { path: string; sha256: string; gzipBase64: string };\ntype Capture = {\n files: NativeFile[];\n bytes: number;\n entries: number;\n encodedBytes: number;\n issues: string[];\n};\n\n/** Capture only a fresh role's transcript tree; never follow native source links. */\nexport async function retainNativeTranscripts(\n result: HostResult,\n root: string,\n host: \"codex\" | \"claude\",\n role: RuntimeRole = \"candidate\",\n rootSessionId: string | null = null,\n): Promise<void> {\n const capture: Capture = {\n files: [],\n bytes: 0,\n entries: 0,\n encodedBytes: 0,\n issues: [],\n };\n try {\n await captureDirectory(root, \"\", capture);\n } catch {\n capture.issues.push(\"source_unavailable\");\n }\n observeCaptureRoot(capture, rootSessionId, host);\n const completeness = captureCompleteness(capture, result);\n const bundle = {\n format: \"sevro.native-transcripts.v2\",\n host,\n role,\n rootSessionId,\n completeness,\n issues: capture.issues,\n files: capture.files,\n };\n const lists = nativeResultLists(result);\n lists.artifacts.push({\n id: \"sevro.native-transcripts.bundle\",\n bytes: Buffer.from(JSON.stringify(bundle)),\n });\n lists.observations.push({\n id: \"sevro.host.native-transcripts\",\n completeness,\n data: {\n method: \"native_files\",\n host,\n role,\n rootSessionId,\n artifactId: \"sevro.native-transcripts.bundle\",\n issues: capture.issues,\n files: capture.files.map(({ path, sha256 }) => ({ path, sha256 })),\n },\n });\n}\n\nfunction nativeResultLists(result: HostResult) {\n result.artifacts ??= [];\n result.observations ??= [];\n return { artifacts: result.artifacts, observations: result.observations };\n}\n\nfunction observeCaptureRoot(\n capture: Capture,\n session: string | null,\n host: string,\n): void {\n if (session === null) {\n capture.issues.push(\"root_session_unknown\");\n return;\n }\n const suffix = host === \"codex\" ? `-${session}.jsonl` : `/${session}.jsonl`;\n if (!capture.files.some((file) => file.path.endsWith(suffix)))\n capture.issues.push(\"root_session_missing\");\n}\n\nfunction captureCompleteness(capture: Capture, result: HostResult) {\n if (!capture.files.some((file) => file.path.endsWith(\".jsonl\")))\n return \"unavailable\" as const;\n if (capture.issues.length || !result.complete || result.executionFailed)\n return \"partial\" as const;\n return \"complete\" as const;\n}\n\nasync function captureDirectory(\n root: string,\n path: string,\n capture: Capture,\n): Promise<void> {\n const directory = join(root, path);\n if (!(await lstat(directory)).isDirectory())\n throw new Error(\"invalid source directory\");\n for (const name of (await readdir(directory)).sort()) {\n capture.entries++;\n if (capture.entries > MAX_FILES) {\n capture.issues.push(\"entry_limit\");\n return;\n }\n await captureEntry(root, join(path, name), capture);\n }\n}\n\nasync function captureEntry(\n root: string,\n path: string,\n capture: Capture,\n): Promise<void> {\n try {\n const info = await lstat(join(root, path));\n if (info.isDirectory()) {\n await captureDirectory(root, path, capture);\n return;\n }\n if (!info.isFile()) {\n capture.issues.push(\"unsafe_entry\");\n return;\n }\n await captureFile(root, path, info.size, capture);\n } catch {\n capture.issues.push(\"entry_unavailable\");\n }\n}\n\nasync function captureFile(\n root: string,\n path: string,\n size: number,\n capture: Capture,\n): Promise<void> {\n if (capture.bytes + size > MAX_BYTES) {\n capture.issues.push(\"byte_limit\");\n return;\n }\n const bytes = await readFile(join(root, path));\n if (bytes.length !== size) {\n capture.issues.push(\"source_changed\");\n return;\n }\n const file = {\n path,\n sha256: sha256(bytes),\n gzipBase64: gzipSync(bytes).toString(\"base64\"),\n };\n const issue = nativeFileIssue(path, bytes);\n if (issue && !capture.issues.includes(issue)) capture.issues.push(issue);\n retainCaptureFile(file, bytes.length, capture);\n}\n\nfunction nativeFileIssue(path: string, bytes: Buffer): string | null {\n if (path.endsWith(\".json\")) return nativeSidecarIssue(bytes);\n if (!path.endsWith(\".jsonl\")) return null;\n return nativeTranscriptIssue(bytes);\n}\n\nfunction nativeTranscriptIssue(bytes: Buffer): string | null {\n try {\n const text = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n if (!text.trim()) return \"empty_transcript\";\n const records = text.replace(/\\r?\\n$/, \"\").split(\"\\n\");\n if (!records.every(validNativeRecord)) return \"malformed_transcript\";\n return null;\n } catch {\n return \"malformed_transcript\";\n }\n}\n\nfunction nativeSidecarIssue(bytes: Buffer): string | null {\n try {\n JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes));\n return null;\n } catch {\n return \"malformed_sidecar\";\n }\n}\n\nfunction validNativeRecord(line: string): boolean {\n const record: unknown = JSON.parse(line);\n return isRecord(record) && nativeRecordType(record.type);\n}\n\nfunction nativeRecordType(type: unknown): boolean {\n return typeof type === \"string\" && type.trim().length > 0;\n}\n\nfunction retainCaptureFile(\n file: NativeFile,\n size: number,\n capture: Capture,\n): void {\n const encoded = Buffer.byteLength(JSON.stringify(file));\n if (capture.encodedBytes + encoded > MAX_ENCODED_FILES_BYTES) {\n capture.issues.push(\"bundle_limit\");\n return;\n }\n capture.bytes += size;\n capture.encodedBytes += encoded;\n capture.files.push(file);\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { isAbsolute, relative, sep } from "node:path";
2
+ /** Lexical containment includes the root itself; callers resolve symlinks first. */
3
+ export function insideRoot(root, path) {
4
+ const child = relative(root, path);
5
+ return (child === "" ||
6
+ (child !== ".." && !child.startsWith(`..${sep}`) && !isAbsolute(child)));
7
+ }
8
+ /** Keep outermost roots in input order, retaining the first equivalent spelling. */
9
+ export function minimalRoots(roots) {
10
+ return roots.filter((root, index) => !roots.some((other, otherIndex) => insideRoot(other, root) &&
11
+ (!insideRoot(root, other) || otherIndex < index)));
12
+ }
13
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,kBAAkB;AAEtD,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,IAAY;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,OAAO,CACL,KAAK,KAAK,EAAE;QACZ,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CACxE,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,OAAO,KAAK,CAAC,MAAM,CACjB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACd,CAAC,KAAK,CAAC,IAAI,CACT,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CACpB,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC;QACvB,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,UAAU,GAAG,KAAK,CAAC,CACnD,CACJ,CAAC;AACJ,CAAC","sourcesContent":["import { isAbsolute, relative, sep } from \"node:path\";\n\n/** Lexical containment includes the root itself; callers resolve symlinks first. */\nexport function insideRoot(root: string, path: string): boolean {\n const child = relative(root, path);\n return (\n child === \"\" ||\n (child !== \"..\" && !child.startsWith(`..${sep}`) && !isAbsolute(child))\n );\n}\n\n/** Keep outermost roots in input order, retaining the first equivalent spelling. */\nexport function minimalRoots(roots: string[]): string[] {\n return roots.filter(\n (root, index) =>\n !roots.some(\n (other, otherIndex) =>\n insideRoot(other, root) &&\n (!insideRoot(root, other) || otherIndex < index),\n ),\n );\n}\n"]}
@@ -0,0 +1,101 @@
1
+ /** Keep the deadline active until the caller has finished failure cleanup. */
2
+ export class ProcessDeadline {
3
+ options;
4
+ timer;
5
+ cancel;
6
+ constructor(options) {
7
+ this.options = options;
8
+ }
9
+ wait(completed) {
10
+ const timeout = this.timeout();
11
+ const aborted = this.aborted();
12
+ const completion = typeof completed === "function" ? completed() : completed;
13
+ return Promise.race([completion, timeout, aborted]);
14
+ }
15
+ timeout() {
16
+ return new Promise((_resolve, reject) => {
17
+ this.timer = setTimeout(() => {
18
+ reject(this.options.interruption("timeout"));
19
+ }, this.options.timeoutMs);
20
+ });
21
+ }
22
+ aborted() {
23
+ return new Promise((_resolve, reject) => {
24
+ const signal = this.options.signal;
25
+ if (!signal)
26
+ return;
27
+ this.cancel = () => {
28
+ reject(this.options.interruption("cancelled"));
29
+ };
30
+ if (signal.aborted)
31
+ this.cancel();
32
+ else
33
+ signal.addEventListener("abort", this.cancel, { once: true });
34
+ });
35
+ }
36
+ dispose() {
37
+ if (this.timer)
38
+ clearTimeout(this.timer);
39
+ if (this.cancel)
40
+ this.options.signal?.removeEventListener("abort", this.cancel);
41
+ }
42
+ }
43
+ function killGroup(pid) {
44
+ try {
45
+ process.kill(-pid, "SIGKILL");
46
+ return true;
47
+ }
48
+ catch {
49
+ // The owned process group may already be gone.
50
+ return false;
51
+ }
52
+ }
53
+ function useProcessGroup(options) {
54
+ return options.alwaysGroup === true || process.platform !== "win32";
55
+ }
56
+ function killOwnedProcess(proc, options) {
57
+ if (options.fallbackError === "propagate") {
58
+ proc.kill("SIGKILL");
59
+ return;
60
+ }
61
+ try {
62
+ proc.kill("SIGKILL");
63
+ }
64
+ catch {
65
+ // The owned process may already have exited.
66
+ }
67
+ }
68
+ /** Only use for children spawned in a new, runner-owned process group. */
69
+ export function terminateOwnedProcessGroup(proc, options = {}) {
70
+ if (useProcessGroup(options) && killGroup(proc.pid))
71
+ return;
72
+ killOwnedProcess(proc, options);
73
+ }
74
+ function retainedChunk(bytes, size, options) {
75
+ if (options.overflow === "reject" && size + bytes.byteLength > options.limit)
76
+ throw options.exceeded();
77
+ return bytes.subarray(0, Math.max(0, options.limit - size));
78
+ }
79
+ /** Truncation retains a byte prefix while still draining the entire stream. */
80
+ export async function captureBoundedBytes(stream, options) {
81
+ const chunks = [];
82
+ let size = 0;
83
+ let truncated = false;
84
+ for await (const bytes of stream.values({
85
+ preventCancel: !options.cancelOnError,
86
+ })) {
87
+ options.observe?.(bytes);
88
+ const retained = retainedChunk(bytes, size, options);
89
+ if (retained.byteLength)
90
+ chunks.push(retained);
91
+ size += retained.byteLength;
92
+ truncated ||= retained.byteLength !== bytes.byteLength;
93
+ }
94
+ return { bytes: Buffer.concat(chunks), truncated };
95
+ }
96
+ /** Reject oversize output before strictly decoding the complete UTF-8 stream. */
97
+ export async function captureBoundedText(stream, options) {
98
+ const { bytes } = await captureBoundedBytes(stream, options);
99
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
100
+ }
101
+ //# sourceMappingURL=process-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-lifecycle.js","sourceRoot":"","sources":["process-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["export type ProcessInterruptionReason = \"timeout\" | \"cancelled\";\n\ninterface DeadlineOptions {\n timeoutMs: number;\n signal?: AbortSignal;\n interruption: (reason: ProcessInterruptionReason) => Error;\n}\n\n/** Keep the deadline active until the caller has finished failure cleanup. */\nexport class ProcessDeadline {\n private timer: ReturnType<typeof setTimeout> | undefined;\n private cancel: (() => void) | undefined;\n\n constructor(private readonly options: DeadlineOptions) {}\n\n wait<T>(completed: Promise<T> | (() => Promise<T>)): Promise<T> {\n const timeout = this.timeout();\n const aborted = this.aborted();\n const completion =\n typeof completed === \"function\" ? completed() : completed;\n return Promise.race([completion, timeout, aborted]);\n }\n\n private timeout(): Promise<never> {\n return new Promise((_resolve, reject) => {\n this.timer = setTimeout(() => {\n reject(this.options.interruption(\"timeout\"));\n }, this.options.timeoutMs);\n });\n }\n\n private aborted(): Promise<never> {\n return new Promise((_resolve, reject) => {\n const signal = this.options.signal;\n if (!signal) return;\n this.cancel = () => {\n reject(this.options.interruption(\"cancelled\"));\n };\n if (signal.aborted) this.cancel();\n else signal.addEventListener(\"abort\", this.cancel, { once: true });\n });\n }\n\n dispose(): void {\n if (this.timer) clearTimeout(this.timer);\n if (this.cancel)\n this.options.signal?.removeEventListener(\"abort\", this.cancel);\n }\n}\n\ninterface OwnedProcess {\n pid: number;\n kill: (signal: \"SIGKILL\") => unknown;\n}\n\ninterface TerminationOptions {\n alwaysGroup?: boolean;\n fallbackError?: \"ignore\" | \"propagate\";\n}\n\nfunction killGroup(pid: number): boolean {\n try {\n process.kill(-pid, \"SIGKILL\");\n return true;\n } catch {\n // The owned process group may already be gone.\n return false;\n }\n}\n\nfunction useProcessGroup(options: TerminationOptions): boolean {\n return options.alwaysGroup === true || process.platform !== \"win32\";\n}\n\nfunction killOwnedProcess(\n proc: OwnedProcess,\n options: TerminationOptions,\n): void {\n if (options.fallbackError === \"propagate\") {\n proc.kill(\"SIGKILL\");\n return;\n }\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n // The owned process may already have exited.\n }\n}\n\n/** Only use for children spawned in a new, runner-owned process group. */\nexport function terminateOwnedProcessGroup(\n proc: OwnedProcess,\n options: TerminationOptions = {},\n): void {\n if (useProcessGroup(options) && killGroup(proc.pid)) return;\n killOwnedProcess(proc, options);\n}\n\ntype RejectedCapture = {\n overflow: \"reject\";\n exceeded: () => Error;\n};\n\ninterface CaptureOptions {\n limit: number;\n observe?: (bytes: Uint8Array) => void;\n cancelOnError?: boolean;\n}\n\ntype ByteCaptureOptions = CaptureOptions &\n (RejectedCapture | { overflow: \"truncate\" });\n\nfunction retainedChunk(\n bytes: Uint8Array,\n size: number,\n options: ByteCaptureOptions,\n): Uint8Array {\n if (options.overflow === \"reject\" && size + bytes.byteLength > options.limit)\n throw options.exceeded();\n return bytes.subarray(0, Math.max(0, options.limit - size));\n}\n\n/** Truncation retains a byte prefix while still draining the entire stream. */\nexport async function captureBoundedBytes(\n stream: ReadableStream<Uint8Array>,\n options: ByteCaptureOptions,\n): Promise<{ bytes: Uint8Array; truncated: boolean }> {\n const chunks: Uint8Array[] = [];\n let size = 0;\n let truncated = false;\n for await (const bytes of stream.values({\n preventCancel: !options.cancelOnError,\n })) {\n options.observe?.(bytes);\n const retained = retainedChunk(bytes, size, options);\n if (retained.byteLength) chunks.push(retained);\n size += retained.byteLength;\n truncated ||= retained.byteLength !== bytes.byteLength;\n }\n return { bytes: Buffer.concat(chunks), truncated };\n}\n\n/** Reject oversize output before strictly decoding the complete UTF-8 stream. */\nexport async function captureBoundedText(\n stream: ReadableStream<Uint8Array>,\n options: CaptureOptions & RejectedCapture,\n): Promise<string> {\n const { bytes } = await captureBoundedBytes(stream, options);\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n}\n"]}
@@ -0,0 +1,263 @@
1
+ import { runnerRoot, sourceRuntime } from "./runner-paths.js";
2
+ import { spawnSync } from "./runtime-process.js";
3
+ import { lstat, readFile, readdir, readlink, realpath } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { dirname, join, relative, resolve, sep } from "node:path";
6
+ import { insideRoot } from "./paths.js";
7
+ import { pathToFileURL } from "node:url";
8
+ import packageJson from "../../package.json" with { type: "json" };
9
+ import { hashJson, sha256 } from "./identity.js";
10
+ import { insideRuntimeRoot } from "./runtime/paths.js";
11
+ const MAX_DIRTY_BYTES = 32 * 1024 * 1024;
12
+ const MAX_BUILD_BYTES = 64 * 1024 * 1024;
13
+ const MAX_BUILD_FILES = 4096;
14
+ function fileOrder(left, right) {
15
+ return left.path < right.path ? -1 : left.path > right.path ? 1 : 0;
16
+ }
17
+ class BuildSnapshot {
18
+ root;
19
+ files = [];
20
+ bytes = 0;
21
+ constructor(root) {
22
+ this.root = root;
23
+ }
24
+ async collect(path) {
25
+ const absolute = join(this.root, path), info = await lstat(absolute);
26
+ if (info.isSymbolicLink())
27
+ throw new Error("package build contains a symbolic link");
28
+ if (info.isDirectory()) {
29
+ for (const entry of (await readdir(absolute)).sort())
30
+ await this.collect(join(path, entry));
31
+ return;
32
+ }
33
+ await this.collectFile(path, absolute, info);
34
+ }
35
+ async collectFile(path, absolute, info) {
36
+ if (!info.isFile())
37
+ throw new Error("package build contains a non-file");
38
+ const bytes = await readFile(absolute);
39
+ this.bytes += bytes.byteLength;
40
+ if (this.files.length >= MAX_BUILD_FILES || this.bytes > MAX_BUILD_BYTES)
41
+ throw new Error("package build exceeds the size limit");
42
+ this.files.push({ path: path.split(sep).join("/"), sha256: sha256(bytes) });
43
+ }
44
+ }
45
+ /** Digest the packaged runtime and public contract files in path order. */
46
+ export async function packageBuildDigest(root = runnerRoot) {
47
+ const snapshot = new BuildSnapshot(await realpath(root));
48
+ for (const path of [
49
+ "package.json",
50
+ "README.md",
51
+ "docs",
52
+ "examples",
53
+ "schemas",
54
+ "src",
55
+ ...(!sourceRuntime ? ["dist"] : []),
56
+ ])
57
+ await snapshot.collect(path);
58
+ return hashJson({
59
+ format: "sevro.build.v1",
60
+ files: snapshot.files.sort(fileOrder),
61
+ });
62
+ }
63
+ class ProjectSnapshot {
64
+ root;
65
+ excluded;
66
+ files = [];
67
+ bytes = 0;
68
+ constructor(root, excluded) {
69
+ this.root = root;
70
+ this.excluded = excluded;
71
+ }
72
+ isExcluded(path) {
73
+ return path === ".git" || this.excluded.has(path);
74
+ }
75
+ async collect(path) {
76
+ if (this.isExcluded(path))
77
+ return;
78
+ const absolute = join(this.root, path), info = await lstat(absolute);
79
+ if (info.isSymbolicLink())
80
+ throw new Error("project snapshot contains a symbolic link");
81
+ if (info.isDirectory()) {
82
+ await this.collectDirectory(path, absolute);
83
+ return;
84
+ }
85
+ await this.collectFile(path, absolute, info);
86
+ }
87
+ async collectDirectory(path, absolute) {
88
+ for (const entry of (await readdir(absolute)).sort())
89
+ await this.collect(path === "" ? entry : join(path, entry));
90
+ }
91
+ async collectFile(path, absolute, info) {
92
+ if (!info.isFile())
93
+ throw new Error("project snapshot contains a non-file");
94
+ const bytes = await readFile(absolute);
95
+ this.bytes += bytes.byteLength;
96
+ if (this.files.length >= MAX_BUILD_FILES || this.bytes > MAX_BUILD_BYTES)
97
+ throw new Error("project snapshot exceeds the size limit");
98
+ this.files.push({
99
+ path: path.split(sep).join("/"),
100
+ sha256: sha256(bytes),
101
+ executable: Boolean(info.mode & 0o111),
102
+ });
103
+ }
104
+ }
105
+ export async function projectIdentityDigest(root, provenance, excludedRoots = []) {
106
+ if (provenance.revision)
107
+ return hashJson({
108
+ format: "sevro.project.v1",
109
+ revision: provenance.revision,
110
+ dirtyPatchDigest: provenance.dirtyPatchDigest,
111
+ });
112
+ const projectRoot = resolve(root), excluded = new Set(excludedRoots.map((path) => relative(projectRoot, resolve(path))));
113
+ const snapshot = new ProjectSnapshot(projectRoot, excluded);
114
+ await snapshot.collect("");
115
+ return hashJson({
116
+ format: "sevro.project.v1",
117
+ files: snapshot.files.sort(fileOrder),
118
+ });
119
+ }
120
+ function git(root, ...arguments_) {
121
+ const result = spawnSync(["git", "-C", root, ...arguments_], {
122
+ env: { PATH: process.env.PATH ?? "/usr/bin:/bin", LC_ALL: "C" },
123
+ stdout: "pipe",
124
+ stderr: "ignore",
125
+ });
126
+ if (result.exitCode !== 0)
127
+ throw new Error("Git identity is unavailable");
128
+ if (result.stdout.byteLength > MAX_DIRTY_BYTES)
129
+ throw new Error("Git identity exceeds the size limit");
130
+ return Buffer.from(result.stdout);
131
+ }
132
+ async function dirtyPatchDigest(gitRoot, excludedRoots) {
133
+ const trackedPatch = git(gitRoot, "diff", "--binary", "HEAD", "--");
134
+ const untrackedPaths = git(gitRoot, "ls-files", "--others", "--exclude-standard", "-z")
135
+ .toString("utf8")
136
+ .split("\0")
137
+ .filter(Boolean)
138
+ .filter((path) => !excludedRoots.some((root) => insideRuntimeRoot(root, resolve(gitRoot, path))))
139
+ .sort();
140
+ let dirtyBytes = trackedPatch.byteLength;
141
+ const untracked = [];
142
+ for (const path of untrackedPaths) {
143
+ const bytes = await untrackedIdentityBytes(gitRoot, path);
144
+ dirtyBytes += bytes.byteLength;
145
+ if (dirtyBytes > MAX_DIRTY_BYTES)
146
+ throw new Error("Git identity exceeds the size limit");
147
+ untracked.push({ path, sha256: sha256(bytes) });
148
+ }
149
+ return trackedPatch.byteLength || untracked.length
150
+ ? hashJson({ trackedPatch: sha256(trackedPatch), untracked })
151
+ : null;
152
+ }
153
+ function requireSafeUntrackedPath(root, absolute) {
154
+ if (!insideRoot(root, absolute))
155
+ throw new Error("Git checkout has an unsafe untracked path");
156
+ }
157
+ async function untrackedIdentityBytes(root, path) {
158
+ const absolute = resolve(root, path);
159
+ requireSafeUntrackedPath(root, absolute);
160
+ const info = await lstat(absolute);
161
+ if (info.isSymbolicLink())
162
+ return Buffer.from(await readlink(absolute), "utf8");
163
+ if (info.isFile())
164
+ return readFile(absolute);
165
+ throw new Error("Git checkout has an unsupported untracked entry");
166
+ }
167
+ /** Local development identity; packaged use never calls Git. */
168
+ export async function checkoutProvenance(root, buildDigest) {
169
+ const canonicalRoot = await realpath(root);
170
+ const gitRoot = await realpath(git(canonicalRoot, "rev-parse", "--show-toplevel").toString().trim());
171
+ if (gitRoot !== canonicalRoot)
172
+ throw new Error("local Sevro checkout root does not match its Git repository");
173
+ const revision = git(canonicalRoot, "rev-parse", "HEAD").toString().trim();
174
+ if (!/^[a-f0-9]{40,64}$/.test(revision))
175
+ throw new Error("local Sevro checkout revision is invalid");
176
+ return {
177
+ source: "checkout",
178
+ root: pathToFileURL(canonicalRoot).href,
179
+ revision,
180
+ dirtyPatchDigest: await dirtyPatchDigest(canonicalRoot, []),
181
+ buildDigest,
182
+ };
183
+ }
184
+ async function enclosingGitRoot(root) {
185
+ let path = root;
186
+ for (;;) {
187
+ if (await gitMetadataAt(path))
188
+ return path;
189
+ const parent = dirname(path);
190
+ if (parent === path)
191
+ return null;
192
+ path = parent;
193
+ }
194
+ }
195
+ async function gitMetadataAt(path) {
196
+ try {
197
+ const metadataPath = join(path, ".git");
198
+ const metadata = await lstat(metadataPath);
199
+ return !(await emptyTemporaryGitPlaceholder(path, metadataPath, metadata));
200
+ }
201
+ catch (error) {
202
+ if (error.code === "ENOENT")
203
+ return false;
204
+ throw error;
205
+ }
206
+ }
207
+ async function emptyTemporaryGitPlaceholder(directory, metadataPath, metadata) {
208
+ // Codex's Linux sandbox can leave an empty mount placeholder at /tmp/.git.
209
+ return (process.platform === "linux" &&
210
+ (directory === "/tmp" || directory === tmpdir()) &&
211
+ metadata.isDirectory() &&
212
+ (await readdir(metadataPath)).length === 0);
213
+ }
214
+ export async function projectProvenance(root, excludedRoots) {
215
+ const canonicalRoot = await realpath(root);
216
+ const gitEntryRoot = await enclosingGitRoot(canonicalRoot);
217
+ if (!gitEntryRoot)
218
+ return {
219
+ root: pathToFileURL(canonicalRoot).href,
220
+ revision: null,
221
+ dirtyPatchDigest: null,
222
+ };
223
+ const gitRoot = await realpath(git(canonicalRoot, "rev-parse", "--show-toplevel").toString().trim());
224
+ if (gitRoot !== gitEntryRoot)
225
+ throw new Error("project Git root does not match its repository entry");
226
+ let revision;
227
+ try {
228
+ revision = git(gitRoot, "rev-parse", "HEAD").toString().trim();
229
+ }
230
+ catch {
231
+ git(gitRoot, "status", "--porcelain");
232
+ return {
233
+ root: pathToFileURL(canonicalRoot).href,
234
+ revision: null,
235
+ dirtyPatchDigest: null,
236
+ };
237
+ }
238
+ if (!/^[a-f0-9]{40,64}$/.test(revision))
239
+ throw new Error("project Git revision is invalid");
240
+ return {
241
+ root: pathToFileURL(canonicalRoot).href,
242
+ revision,
243
+ dirtyPatchDigest: await projectDirtyDigest(gitRoot, excludedRoots),
244
+ };
245
+ }
246
+ async function projectDirtyDigest(root, excludedRoots) {
247
+ return dirtyPatchDigest(root, excludedRoots ?? []);
248
+ }
249
+ export async function runnerProvenance(buildDigest, checkoutRoot) {
250
+ if (checkoutRoot) {
251
+ const actualRoot = await realpath(runnerRoot);
252
+ if ((await realpath(checkoutRoot)) !== actualRoot)
253
+ throw new Error("local Sevro checkout root does not match the running package");
254
+ return checkoutProvenance(actualRoot, buildDigest);
255
+ }
256
+ return {
257
+ source: "package",
258
+ packageName: packageJson.name,
259
+ version: packageJson.version,
260
+ buildDigest,
261
+ };
262
+ }
263
+ //# sourceMappingURL=provenance.js.map