@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,167 @@
1
+ import { createHash } from "node:crypto";
2
+ const ALGORITHM = "sevro.identity.v1";
3
+ const DIGEST = /^[a-f0-9]{64}$/;
4
+ const DIMENSION_KEYS = [
5
+ "runnerBuildDigest",
6
+ "projectDigest",
7
+ "configurationDigest",
8
+ "extensionDigest",
9
+ "extensionProtocol",
10
+ "caseDigest",
11
+ "fixtureDigest",
12
+ "checksDigest",
13
+ "requiredEvidenceDigest",
14
+ "evaluatorDigest",
15
+ "graderDigest",
16
+ "instrumentationDigest",
17
+ "routeDigest",
18
+ "condition",
19
+ "trialCount",
20
+ "passThreshold",
21
+ ];
22
+ function surrogateKind(code) {
23
+ if (code >= 0xd800 && code <= 0xdbff)
24
+ return "high";
25
+ return code >= 0xdc00 && code <= 0xdfff ? "low" : null;
26
+ }
27
+ function assertUnicode(value) {
28
+ for (let index = 0; index < value.length; index++) {
29
+ const kind = surrogateKind(value.charCodeAt(index));
30
+ if (kind === "high") {
31
+ if (surrogateKind(value.charCodeAt(++index)) !== "low")
32
+ throw new Error("JSON string contains an unpaired surrogate");
33
+ }
34
+ else if (kind === "low") {
35
+ throw new Error("JSON string contains an unpaired surrogate");
36
+ }
37
+ }
38
+ }
39
+ class CanonicalJsonWriter {
40
+ visiting = new WeakSet();
41
+ write(item) {
42
+ if (item === null)
43
+ return "null";
44
+ switch (typeof item) {
45
+ case "string":
46
+ assertUnicode(item);
47
+ return JSON.stringify(item);
48
+ case "number":
49
+ return this.number(item);
50
+ case "boolean":
51
+ return item ? "true" : "false";
52
+ case "object":
53
+ return this.object(item);
54
+ default:
55
+ throw new Error("value is not JSON data");
56
+ }
57
+ }
58
+ number(item) {
59
+ if (!Number.isFinite(item))
60
+ throw new Error("JSON number must be finite");
61
+ return JSON.stringify(item);
62
+ }
63
+ object(item) {
64
+ if (this.visiting.has(item))
65
+ throw new Error("JSON value contains a cycle");
66
+ this.visiting.add(item);
67
+ try {
68
+ if (Array.isArray(item))
69
+ return `[${item.map((value) => this.write(value)).join(",")}]`;
70
+ this.assertPlainObject(item);
71
+ const entries = Object.keys(item)
72
+ .sort()
73
+ .map((key) => this.property(item, key));
74
+ return `{${entries.join(",")}}`;
75
+ }
76
+ finally {
77
+ this.visiting.delete(item);
78
+ }
79
+ }
80
+ assertPlainObject(item) {
81
+ const prototype = Object.getPrototypeOf(item);
82
+ if (prototype !== Object.prototype && prototype !== null)
83
+ throw new Error("value is not a plain JSON object");
84
+ if (Object.getOwnPropertySymbols(item).length)
85
+ throw new Error("JSON object has symbol keys");
86
+ }
87
+ property(item, key) {
88
+ assertUnicode(key);
89
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
90
+ if (!descriptor || !Object.hasOwn(descriptor, "value"))
91
+ throw new Error("JSON object has an accessor property");
92
+ const value = descriptor.value;
93
+ return `${JSON.stringify(key)}:${this.write(value)}`;
94
+ }
95
+ }
96
+ /** RFC 8785 JSON canonicalization for ordinary JSON values. */
97
+ export function canonicalJson(value) {
98
+ return new CanonicalJsonWriter().write(value);
99
+ }
100
+ /** Hash exact bytes, or a string's UTF-8 bytes, without JSON canonicalization. */
101
+ export function sha256(content) {
102
+ return createHash("sha256").update(content).digest("hex");
103
+ }
104
+ export function hashJson(value) {
105
+ return sha256(canonicalJson(value));
106
+ }
107
+ function validatedDimensions(value) {
108
+ if (!value || typeof value !== "object" || Array.isArray(value))
109
+ throw new Error("identity dimensions must be a JSON object");
110
+ const record = value;
111
+ validateDimensionKeys(record);
112
+ validateDigestDimensions(record);
113
+ validateExtensionDimension(record);
114
+ validateTrialDimensions(record);
115
+ return record;
116
+ }
117
+ function validateDimensionKeys(record) {
118
+ for (const key of DIMENSION_KEYS)
119
+ if (!Object.hasOwn(record, key))
120
+ throw new Error(`missing identity dimension: ${key}`);
121
+ const unexpected = Object.keys(record).filter((key) => !DIMENSION_KEYS.includes(key));
122
+ const firstUnexpected = unexpected[0];
123
+ if (firstUnexpected !== undefined)
124
+ throw new Error(`unexpected identity dimension: ${firstUnexpected}`);
125
+ }
126
+ function validateDigestDimensions(record) {
127
+ for (const key of DIMENSION_KEYS.filter((name) => name.endsWith("Digest"))) {
128
+ if (key === "extensionDigest" && record[key] === null)
129
+ continue;
130
+ if (!validDigest(record[key]))
131
+ throw new Error(`invalid identity dimension: ${key}`);
132
+ }
133
+ }
134
+ function validateExtensionDimension(record) {
135
+ if ((record.extensionDigest === null) !== (record.extensionProtocol === null) ||
136
+ (record.extensionProtocol !== null &&
137
+ record.extensionProtocol !== "sevro.extension.v1"))
138
+ throw new Error("extension identity requires its matching protocol");
139
+ }
140
+ function validateTrialDimensions(record) {
141
+ if (record.condition !== "passive" && record.condition !== "enforced")
142
+ throw new Error("invalid identity condition");
143
+ if (!Number.isSafeInteger(record.trialCount) ||
144
+ record.trialCount < 1)
145
+ throw new Error("invalid identity trial count");
146
+ validatePassThreshold(record.passThreshold);
147
+ }
148
+ function validatePassThreshold(value) {
149
+ if (typeof value !== "number" ||
150
+ !Number.isFinite(value) ||
151
+ value <= 0 ||
152
+ value > 1)
153
+ throw new Error("invalid identity pass threshold");
154
+ }
155
+ /** Digest the exact comparison dimensions with a versioned domain prefix. */
156
+ export function createEvaluationIdentity(dimensions) {
157
+ const checked = validatedDimensions(dimensions);
158
+ const digest = createHash("sha256")
159
+ .update(`${ALGORITHM}\n`, "utf8")
160
+ .update(canonicalJson(checked), "utf8")
161
+ .digest("hex");
162
+ return { algorithm: ALGORITHM, digest, dimensions: checked };
163
+ }
164
+ function validDigest(value) {
165
+ return typeof value === "string" && DIGEST.test(value);
166
+ }
167
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["identity.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\n\nconst ALGORITHM = \"sevro.identity.v1\";\nconst DIGEST = /^[a-f0-9]{64}$/;\nconst DIMENSION_KEYS = [\n \"runnerBuildDigest\",\n \"projectDigest\",\n \"configurationDigest\",\n \"extensionDigest\",\n \"extensionProtocol\",\n \"caseDigest\",\n \"fixtureDigest\",\n \"checksDigest\",\n \"requiredEvidenceDigest\",\n \"evaluatorDigest\",\n \"graderDigest\",\n \"instrumentationDigest\",\n \"routeDigest\",\n \"condition\",\n \"trialCount\",\n \"passThreshold\",\n] as const;\n\nexport interface IdentityDimensions {\n runnerBuildDigest: string;\n projectDigest: string;\n configurationDigest: string;\n extensionDigest: string | null;\n extensionProtocol: \"sevro.extension.v1\" | null;\n caseDigest: string;\n fixtureDigest: string;\n checksDigest: string;\n requiredEvidenceDigest: string;\n evaluatorDigest: string;\n graderDigest: string;\n instrumentationDigest: string;\n routeDigest: string;\n condition: \"passive\" | \"enforced\";\n trialCount: number;\n passThreshold: number;\n}\n\nfunction surrogateKind(code: number): \"high\" | \"low\" | null {\n if (code >= 0xd800 && code <= 0xdbff) return \"high\";\n return code >= 0xdc00 && code <= 0xdfff ? \"low\" : null;\n}\n\nfunction assertUnicode(value: string): void {\n for (let index = 0; index < value.length; index++) {\n const kind = surrogateKind(value.charCodeAt(index));\n if (kind === \"high\") {\n if (surrogateKind(value.charCodeAt(++index)) !== \"low\")\n throw new Error(\"JSON string contains an unpaired surrogate\");\n } else if (kind === \"low\") {\n throw new Error(\"JSON string contains an unpaired surrogate\");\n }\n }\n}\n\nclass CanonicalJsonWriter {\n private readonly visiting = new WeakSet();\n\n write(item: unknown): string {\n if (item === null) return \"null\";\n switch (typeof item) {\n case \"string\":\n assertUnicode(item);\n return JSON.stringify(item);\n case \"number\":\n return this.number(item);\n case \"boolean\":\n return item ? \"true\" : \"false\";\n case \"object\":\n return this.object(item);\n default:\n throw new Error(\"value is not JSON data\");\n }\n }\n\n private number(item: number): string {\n if (!Number.isFinite(item)) throw new Error(\"JSON number must be finite\");\n return JSON.stringify(item);\n }\n\n private object(item: object): string {\n if (this.visiting.has(item)) throw new Error(\"JSON value contains a cycle\");\n this.visiting.add(item);\n try {\n if (Array.isArray(item))\n return `[${item.map((value: unknown) => this.write(value)).join(\",\")}]`;\n this.assertPlainObject(item);\n const entries = Object.keys(item)\n .sort()\n .map((key) => this.property(item, key));\n return `{${entries.join(\",\")}}`;\n } finally {\n this.visiting.delete(item);\n }\n }\n\n private assertPlainObject(item: object): void {\n const prototype: unknown = Object.getPrototypeOf(item);\n if (prototype !== Object.prototype && prototype !== null)\n throw new Error(\"value is not a plain JSON object\");\n if (Object.getOwnPropertySymbols(item).length)\n throw new Error(\"JSON object has symbol keys\");\n }\n\n private property(item: object, key: string): string {\n assertUnicode(key);\n const descriptor = Object.getOwnPropertyDescriptor(item, key);\n if (!descriptor || !Object.hasOwn(descriptor, \"value\"))\n throw new Error(\"JSON object has an accessor property\");\n const value: unknown = descriptor.value;\n return `${JSON.stringify(key)}:${this.write(value)}`;\n }\n}\n\n/** RFC 8785 JSON canonicalization for ordinary JSON values. */\nexport function canonicalJson(value: unknown): string {\n return new CanonicalJsonWriter().write(value);\n}\n\n/** Hash exact bytes, or a string's UTF-8 bytes, without JSON canonicalization. */\nexport function sha256(content: string | Uint8Array): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nexport function hashJson(value: unknown): string {\n return sha256(canonicalJson(value));\n}\n\nfunction validatedDimensions(value: unknown): IdentityDimensions {\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n throw new Error(\"identity dimensions must be a JSON object\");\n const record = value as Record<string, unknown>;\n validateDimensionKeys(record);\n validateDigestDimensions(record);\n validateExtensionDimension(record);\n validateTrialDimensions(record);\n return record as unknown as IdentityDimensions;\n}\n\nfunction validateDimensionKeys(record: Record<string, unknown>): void {\n for (const key of DIMENSION_KEYS)\n if (!Object.hasOwn(record, key))\n throw new Error(`missing identity dimension: ${key}`);\n const unexpected = Object.keys(record).filter(\n (key) => !DIMENSION_KEYS.includes(key as (typeof DIMENSION_KEYS)[number]),\n );\n const firstUnexpected = unexpected[0];\n if (firstUnexpected !== undefined)\n throw new Error(`unexpected identity dimension: ${firstUnexpected}`);\n}\n\nfunction validateDigestDimensions(record: Record<string, unknown>): void {\n for (const key of DIMENSION_KEYS.filter((name) => name.endsWith(\"Digest\"))) {\n if (key === \"extensionDigest\" && record[key] === null) continue;\n if (!validDigest(record[key]))\n throw new Error(`invalid identity dimension: ${key}`);\n }\n}\n\nfunction validateExtensionDimension(record: Record<string, unknown>): void {\n if (\n (record.extensionDigest === null) !== (record.extensionProtocol === null) ||\n (record.extensionProtocol !== null &&\n record.extensionProtocol !== \"sevro.extension.v1\")\n )\n throw new Error(\"extension identity requires its matching protocol\");\n}\n\nfunction validateTrialDimensions(record: Record<string, unknown>): void {\n if (record.condition !== \"passive\" && record.condition !== \"enforced\")\n throw new Error(\"invalid identity condition\");\n if (\n !Number.isSafeInteger(record.trialCount) ||\n (record.trialCount as number) < 1\n )\n throw new Error(\"invalid identity trial count\");\n validatePassThreshold(record.passThreshold);\n}\n\nfunction validatePassThreshold(value: unknown): void {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0 ||\n value > 1\n )\n throw new Error(\"invalid identity pass threshold\");\n}\n\n/** Digest the exact comparison dimensions with a versioned domain prefix. */\nexport function createEvaluationIdentity(dimensions: unknown): {\n algorithm: typeof ALGORITHM;\n digest: string;\n dimensions: IdentityDimensions;\n} {\n const checked = validatedDimensions(dimensions);\n const digest = createHash(\"sha256\")\n .update(`${ALGORITHM}\\n`, \"utf8\")\n .update(canonicalJson(checked), \"utf8\")\n .digest(\"hex\");\n return { algorithm: ALGORITHM, digest, dimensions: checked };\n}\n\nfunction validDigest(value: unknown): value is string {\n return typeof value === \"string\" && DIGEST.test(value);\n}\n"]}
@@ -0,0 +1,83 @@
1
+ import { isRecord, isUnknownArray, validQualifiedId as validInstrumentationId, } from "./value-guards.js";
2
+ import { canonicalJson } from "./identity.js";
3
+ export class InstrumentationEvidenceError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "InstrumentationEvidenceError";
7
+ }
8
+ }
9
+ function validRequest(value) {
10
+ return (isRecord(value) &&
11
+ validInstrumentationId(value.id) &&
12
+ isRecord(value.configuration));
13
+ }
14
+ function validateSupportedInstrumentation(supported) {
15
+ const ids = new Set(supported.map((item) => item.id));
16
+ if (supported.some((item) => !validInstrumentationId(item.id) ||
17
+ typeof item.executionChanging !== "boolean") ||
18
+ ids.size !== supported.length)
19
+ throw new Error("host instrumentation capabilities are invalid");
20
+ }
21
+ function validateRequestedInstrumentation(requested) {
22
+ const ids = new Set(requested.map((item) => item.id));
23
+ if (requested.some((item) => !validRequest(item)) ||
24
+ ids.size !== requested.length)
25
+ throw new Error("requested instrumentation is invalid or duplicated");
26
+ }
27
+ function requestedCapability(item, supported, negotiated) {
28
+ const capability = supported.find((entry) => entry.id === item.id);
29
+ if (!capability || !negotiated.includes(item.id))
30
+ throw new Error("extension preparation requested unsupported instrumentation");
31
+ return capability;
32
+ }
33
+ function validateInstrumentationCondition(capability, condition) {
34
+ if (condition === "passive" && capability.executionChanging)
35
+ throw new Error("passive condition cannot apply execution-changing instrumentation");
36
+ }
37
+ /** Validate an extension's request before the candidate host starts. */
38
+ export function prepareInstrumentation(requested, supported, negotiated, condition) {
39
+ validateSupportedInstrumentation(supported);
40
+ validateRequestedInstrumentation(requested);
41
+ for (const item of requested)
42
+ validateInstrumentationCondition(requestedCapability(item, supported, negotiated), condition);
43
+ return snapshotInstrumentation(requested);
44
+ }
45
+ /** Own the canonical JSON parameters independently of adapter state. */
46
+ export function snapshotInstrumentation(requested) {
47
+ return JSON.parse(canonicalJson(requested));
48
+ }
49
+ /** A host must report exactly the instrumentation it applied. */
50
+ export function verifyAppliedInstrumentation(requested, applied, requestedCondition, actualCondition) {
51
+ const actual = applied ?? [];
52
+ validateAppliedRequests(actual, requested);
53
+ validateActualCondition(actualCondition);
54
+ if (mismatchedCondition(actualCondition, requestedCondition) ||
55
+ (requested.length > 0 && actualCondition !== requestedCondition))
56
+ throw new InstrumentationEvidenceError("host did not confirm the requested condition");
57
+ return snapshotInstrumentation(actual);
58
+ }
59
+ function requestArray(value) {
60
+ return isUnknownArray(value) && value.every(validRequest);
61
+ }
62
+ function validateAppliedRequests(actual, requested) {
63
+ if (!requestArray(actual) ||
64
+ new Set(actual.map((item) => item.id)).size !== actual.length ||
65
+ canonicalJson(actual) !== canonicalJson(requested))
66
+ throw new InstrumentationEvidenceError("host applied instrumentation does not match the request");
67
+ }
68
+ function validActualCondition(value) {
69
+ return (value === undefined ||
70
+ value === "unknown" ||
71
+ value === "passive" ||
72
+ value === "enforced");
73
+ }
74
+ function validateActualCondition(value) {
75
+ if (!validActualCondition(value))
76
+ throw new InstrumentationEvidenceError("host condition evidence is invalid");
77
+ }
78
+ function mismatchedCondition(actual, requested) {
79
+ if (actual === undefined || actual === "unknown")
80
+ return false;
81
+ return actual !== requested;
82
+ }
83
+ //# sourceMappingURL=instrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n isRecord,\n isUnknownArray,\n validQualifiedId as validInstrumentationId,\n} from \"./value-guards\";\nimport { canonicalJson } from \"./identity\";\n\nexport interface InstrumentationRequest {\n id: string;\n configuration: Record<string, unknown>;\n}\n\nexport interface InstrumentationCapability {\n id: string;\n executionChanging: boolean;\n}\n\nexport class InstrumentationEvidenceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InstrumentationEvidenceError\";\n }\n}\n\nfunction validRequest(value: unknown): value is InstrumentationRequest {\n return (\n isRecord(value) &&\n validInstrumentationId(value.id) &&\n isRecord(value.configuration)\n );\n}\n\nfunction validateSupportedInstrumentation(\n supported: InstrumentationCapability[],\n): void {\n const ids = new Set(supported.map((item) => item.id));\n if (\n supported.some(\n (item) =>\n !validInstrumentationId(item.id) ||\n typeof item.executionChanging !== \"boolean\",\n ) ||\n ids.size !== supported.length\n )\n throw new Error(\"host instrumentation capabilities are invalid\");\n}\n\nfunction validateRequestedInstrumentation(\n requested: InstrumentationRequest[],\n): void {\n const ids = new Set(requested.map((item) => item.id));\n if (\n requested.some((item) => !validRequest(item)) ||\n ids.size !== requested.length\n )\n throw new Error(\"requested instrumentation is invalid or duplicated\");\n}\n\nfunction requestedCapability(\n item: InstrumentationRequest,\n supported: InstrumentationCapability[],\n negotiated: string[],\n): InstrumentationCapability {\n const capability = supported.find((entry) => entry.id === item.id);\n if (!capability || !negotiated.includes(item.id))\n throw new Error(\n \"extension preparation requested unsupported instrumentation\",\n );\n return capability;\n}\n\nfunction validateInstrumentationCondition(\n capability: InstrumentationCapability,\n condition: \"passive\" | \"enforced\",\n): void {\n if (condition === \"passive\" && capability.executionChanging)\n throw new Error(\n \"passive condition cannot apply execution-changing instrumentation\",\n );\n}\n\n/** Validate an extension's request before the candidate host starts. */\nexport function prepareInstrumentation(\n requested: InstrumentationRequest[],\n supported: InstrumentationCapability[],\n negotiated: string[],\n condition: \"passive\" | \"enforced\",\n): InstrumentationRequest[] {\n validateSupportedInstrumentation(supported);\n validateRequestedInstrumentation(requested);\n for (const item of requested)\n validateInstrumentationCondition(\n requestedCapability(item, supported, negotiated),\n condition,\n );\n return snapshotInstrumentation(requested);\n}\n\n/** Own the canonical JSON parameters independently of adapter state. */\nexport function snapshotInstrumentation(\n requested: InstrumentationRequest[],\n): InstrumentationRequest[] {\n return JSON.parse(canonicalJson(requested)) as InstrumentationRequest[];\n}\n\n/** A host must report exactly the instrumentation it applied. */\nexport function verifyAppliedInstrumentation(\n requested: InstrumentationRequest[],\n applied: InstrumentationRequest[] | undefined,\n requestedCondition: \"passive\" | \"enforced\",\n actualCondition: \"passive\" | \"enforced\" | \"unknown\" | undefined,\n): InstrumentationRequest[] {\n const actual = applied ?? [];\n validateAppliedRequests(actual, requested);\n validateActualCondition(actualCondition);\n if (\n mismatchedCondition(actualCondition, requestedCondition) ||\n (requested.length > 0 && actualCondition !== requestedCondition)\n )\n throw new InstrumentationEvidenceError(\n \"host did not confirm the requested condition\",\n );\n return snapshotInstrumentation(actual);\n}\n\nfunction requestArray(value: unknown): value is InstrumentationRequest[] {\n return isUnknownArray(value) && value.every(validRequest);\n}\n\nfunction validateAppliedRequests(\n actual: unknown,\n requested: InstrumentationRequest[],\n): asserts actual is InstrumentationRequest[] {\n if (\n !requestArray(actual) ||\n new Set(actual.map((item) => item.id)).size !== actual.length ||\n canonicalJson(actual) !== canonicalJson(requested)\n )\n throw new InstrumentationEvidenceError(\n \"host applied instrumentation does not match the request\",\n );\n}\n\nfunction validActualCondition(value: unknown): boolean {\n return (\n value === undefined ||\n value === \"unknown\" ||\n value === \"passive\" ||\n value === \"enforced\"\n );\n}\n\nfunction validateActualCondition(value: unknown): void {\n if (!validActualCondition(value))\n throw new InstrumentationEvidenceError(\n \"host condition evidence is invalid\",\n );\n}\n\nfunction mismatchedCondition(\n actual: string | undefined,\n requested: string,\n): boolean {\n if (actual === undefined || actual === \"unknown\") return false;\n return actual !== requested;\n}\n"]}
@@ -0,0 +1,85 @@
1
+ import { lstat, mkdir, mkdtemp, realpath } from "node:fs/promises";
2
+ import { realpathSync } from "node:fs";
3
+ import { tmpdir, userInfo } from "node:os";
4
+ import { basename, dirname, join, sep } from "node:path";
5
+ /** A permanent deny boundary also covers roles allocated after command launch. */
6
+ export async function nativeStateParent() {
7
+ const root = nativeStatePath();
8
+ await mkdir(root, { recursive: true, mode: 0o700 });
9
+ const info = await lstat(root);
10
+ requirePrivateNamespace(info);
11
+ return realpath(root);
12
+ }
13
+ /** Canonical base also binds declaration symlinks before any private tree is read. */
14
+ export function nativeStatePath() {
15
+ return join(realpathSync(nativeStateBase()), "sevro-native-private");
16
+ }
17
+ export function nativeStateProtectedRoots() {
18
+ return [nativeStatePath(), join(nativeStateBase(), "sevro-native-private")];
19
+ }
20
+ function nativeStateBase() {
21
+ return process.platform === "linux" ? "/var/tmp" : tmpdir();
22
+ }
23
+ function requirePrivateNamespace(info) {
24
+ if (!info.isDirectory() ||
25
+ info.uid !== userInfo().uid ||
26
+ (info.mode & 0o077) !== 0)
27
+ throw new Error("native state namespace must be an owned private directory");
28
+ }
29
+ export async function allocateNativeState(prefix, runtimeWorkspace) {
30
+ await requireExternalNativeNamespace(runtimeWorkspace);
31
+ return realpath(await mkdtemp(join(await nativeStateParent(), prefix)));
32
+ }
33
+ /** Workspace writes must never reach existing or future native peer state. */
34
+ async function requireExternalNativeNamespace(workspace) {
35
+ if (workspace === undefined)
36
+ return;
37
+ const fixtureRoot = join(await realpath(workspace), sep);
38
+ const namespaceRoot = join(nativeStatePath(), sep);
39
+ if (fixtureRoot.startsWith(namespaceRoot) ||
40
+ namespaceRoot.startsWith(fixtureRoot))
41
+ throw new Error("native runtime namespace overlaps the fixture workspace");
42
+ }
43
+ export async function requireNativeTranscriptView(root) {
44
+ if (root === undefined)
45
+ return;
46
+ const parent = await nativeStateParent();
47
+ if (dirname(root) !== parent ||
48
+ !basename(root).startsWith("view-") ||
49
+ (await realpath(root)) !== root)
50
+ throw new Error("native transcript view must be runner-owned and canonical");
51
+ requirePrivateNamespace(await lstat(root));
52
+ }
53
+ export async function requireNativeTranscriptViews(roots) {
54
+ for (const root of roots ?? [])
55
+ await requireNativeTranscriptView(root);
56
+ }
57
+ /** Curated hook copies are a separate runner-owned exception, never policy roots. */
58
+ export async function requireNativeHookRoots(roots, privateRoot) {
59
+ for (const root of roots ?? [])
60
+ await requireNativeHookRoot(root, privateRoot);
61
+ }
62
+ async function requireNativeHookRoot(root, privateRoot) {
63
+ const parent = await nativeStateParent();
64
+ const state = dirname(dirname(root));
65
+ if (!nativeHookLayout(root, state, parent) || (await realpath(root)) !== root)
66
+ throw new Error("native hook read root must be a canonical curated mount");
67
+ await requireNativeHookOwner(state, privateRoot);
68
+ const info = await lstat(root);
69
+ if (!info.isDirectory() || info.uid !== userInfo().uid)
70
+ throw new Error("native hook read root must be owned");
71
+ }
72
+ async function requireNativeHookOwner(state, privateRoot) {
73
+ if (dirname(privateRoot) !== state ||
74
+ (await realpath(privateRoot)) !== privateRoot)
75
+ throw new Error("native hook read root must belong to the current request");
76
+ requirePrivateNamespace(await lstat(state));
77
+ requirePrivateNamespace(await lstat(privateRoot));
78
+ }
79
+ function nativeHookLayout(root, state, parent) {
80
+ return (dirname(state) === parent &&
81
+ /^(?:claude|codex)-/.test(basename(state)) &&
82
+ basename(dirname(root)) === "runtime-plugins" &&
83
+ /^plugin-[0-9]+$/.test(basename(root)));
84
+ }
85
+ //# sourceMappingURL=native-transcript-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-transcript-state.js","sourceRoot":"","sources":["native-transcript-state.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdir, mkdtemp, realpath } from \"node:fs/promises\";\nimport type { Stats } from \"node:fs\";\nimport { realpathSync } from \"node:fs\";\nimport { tmpdir, userInfo } from \"node:os\";\nimport { basename, dirname, join, sep } from \"node:path\";\n\n/** A permanent deny boundary also covers roles allocated after command launch. */\nexport async function nativeStateParent(): Promise<string> {\n const root = nativeStatePath();\n await mkdir(root, { recursive: true, mode: 0o700 });\n const info = await lstat(root);\n requirePrivateNamespace(info);\n return realpath(root);\n}\n\n/** Canonical base also binds declaration symlinks before any private tree is read. */\nexport function nativeStatePath(): string {\n return join(realpathSync(nativeStateBase()), \"sevro-native-private\");\n}\n\nexport function nativeStateProtectedRoots(): string[] {\n return [nativeStatePath(), join(nativeStateBase(), \"sevro-native-private\")];\n}\n\nfunction nativeStateBase(): string {\n return process.platform === \"linux\" ? \"/var/tmp\" : tmpdir();\n}\n\nfunction requirePrivateNamespace(info: Stats): void {\n if (\n !info.isDirectory() ||\n info.uid !== userInfo().uid ||\n (info.mode & 0o077) !== 0\n )\n throw new Error(\n \"native state namespace must be an owned private directory\",\n );\n}\n\nexport async function allocateNativeState(\n prefix: string,\n runtimeWorkspace?: string,\n): Promise<string> {\n await requireExternalNativeNamespace(runtimeWorkspace);\n return realpath(await mkdtemp(join(await nativeStateParent(), prefix)));\n}\n\n/** Workspace writes must never reach existing or future native peer state. */\nasync function requireExternalNativeNamespace(\n workspace?: string,\n): Promise<void> {\n if (workspace === undefined) return;\n const fixtureRoot = join(await realpath(workspace), sep);\n const namespaceRoot = join(nativeStatePath(), sep);\n if (\n fixtureRoot.startsWith(namespaceRoot) ||\n namespaceRoot.startsWith(fixtureRoot)\n )\n throw new Error(\"native runtime namespace overlaps the fixture workspace\");\n}\n\nexport async function requireNativeTranscriptView(\n root: string | undefined,\n): Promise<void> {\n if (root === undefined) return;\n const parent = await nativeStateParent();\n if (\n dirname(root) !== parent ||\n !basename(root).startsWith(\"view-\") ||\n (await realpath(root)) !== root\n )\n throw new Error(\n \"native transcript view must be runner-owned and canonical\",\n );\n requirePrivateNamespace(await lstat(root));\n}\n\nexport async function requireNativeTranscriptViews(\n roots: string[] | undefined,\n): Promise<void> {\n for (const root of roots ?? []) await requireNativeTranscriptView(root);\n}\n\n/** Curated hook copies are a separate runner-owned exception, never policy roots. */\nexport async function requireNativeHookRoots(\n roots: string[] | undefined,\n privateRoot: string,\n): Promise<void> {\n for (const root of roots ?? [])\n await requireNativeHookRoot(root, privateRoot);\n}\n\nasync function requireNativeHookRoot(\n root: string,\n privateRoot: string,\n): Promise<void> {\n const parent = await nativeStateParent();\n const state = dirname(dirname(root));\n if (!nativeHookLayout(root, state, parent) || (await realpath(root)) !== root)\n throw new Error(\"native hook read root must be a canonical curated mount\");\n await requireNativeHookOwner(state, privateRoot);\n const info = await lstat(root);\n if (!info.isDirectory() || info.uid !== userInfo().uid)\n throw new Error(\"native hook read root must be owned\");\n}\n\nasync function requireNativeHookOwner(\n state: string,\n privateRoot: string,\n): Promise<void> {\n if (\n dirname(privateRoot) !== state ||\n (await realpath(privateRoot)) !== privateRoot\n )\n throw new Error(\"native hook read root must belong to the current request\");\n requirePrivateNamespace(await lstat(state));\n requirePrivateNamespace(await lstat(privateRoot));\n}\n\nfunction nativeHookLayout(\n root: string,\n state: string,\n parent: string,\n): boolean {\n return (\n dirname(state) === parent &&\n /^(?:claude|codex)-/.test(basename(state)) &&\n basename(dirname(root)) === \"runtime-plugins\" &&\n /^plugin-[0-9]+$/.test(basename(root))\n );\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { gunzipSync } from "node:zlib";
4
+ import { allocateNativeState } from "./native-transcript-state.js";
5
+ import { fixtureParts } from "./fixtures/preparation.js";
6
+ import { isRecord, isUnknownArray } from "./value-guards.js";
7
+ import { sha256 } from "./identity.js";
8
+ const MAX_NATIVE_BYTES = 5 * 1024 * 1024;
9
+ export async function materializeNativeTranscriptView(bytes) {
10
+ const value = JSON.parse(Buffer.from(bytes).toString("utf8"));
11
+ const bundle = nativeBundle(value);
12
+ const root = await allocateNativeState("view-");
13
+ await writeFile(join(root, "index.json"), bytes, { flag: "wx", mode: 0o600 });
14
+ const filesRoot = join(root, bundle.host);
15
+ let remainingBytes = MAX_NATIVE_BYTES;
16
+ for (const item of bundle.files)
17
+ remainingBytes -= await materializeNativeFile(filesRoot, item, bundle.format, remainingBytes);
18
+ return root;
19
+ }
20
+ function nativeBundle(value) {
21
+ if (!isRecord(value) || !nativeFormat(value.format))
22
+ throw new Error("invalid native transcript bundle");
23
+ if (!nativeHost(value.host))
24
+ throw new Error("invalid native transcript host");
25
+ if (!isUnknownArray(value.files))
26
+ throw new Error("invalid native transcript files");
27
+ return { host: value.host, files: value.files, format: value.format };
28
+ }
29
+ function nativeFormat(value) {
30
+ return (value === "sevro.native-transcripts.v1" ||
31
+ value === "sevro.native-transcripts.v2");
32
+ }
33
+ function nativeHost(value) {
34
+ return value === "codex" || value === "claude";
35
+ }
36
+ async function materializeNativeFile(root, value, format, remainingBytes) {
37
+ if (!isRecord(value) || typeof value.path !== "string")
38
+ throw new Error("invalid native transcript file");
39
+ const path = join(root, ...fixtureParts(value.path));
40
+ const bytes = nativeFileBytes(value, format, remainingBytes);
41
+ if (bytes.length > remainingBytes)
42
+ throw new Error("native transcript byte limit exceeded");
43
+ if (sha256(bytes) !== value.sha256)
44
+ throw new Error("native transcript hash mismatch");
45
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
46
+ await writeFile(path, bytes, { flag: "wx", mode: 0o600 });
47
+ return bytes.length;
48
+ }
49
+ function nativeFileBytes(value, format, remainingBytes) {
50
+ const encoded = format === "sevro.native-transcripts.v1"
51
+ ? value.bytesBase64
52
+ : value.gzipBase64;
53
+ if (typeof encoded !== "string")
54
+ throw new Error("invalid native transcript file");
55
+ const bytes = Buffer.from(encoded, "base64");
56
+ if (format === "sevro.native-transcripts.v1")
57
+ return bytes;
58
+ try {
59
+ return gunzipSync(bytes, { maxOutputLength: Math.max(1, remainingBytes) });
60
+ }
61
+ catch (cause) {
62
+ throw new Error("invalid native transcript gzip", { cause });
63
+ }
64
+ }
65
+ //# sourceMappingURL=native-transcript-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-transcript-view.js","sourceRoot":"","sources":["native-transcript-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { gunzipSync } from \"node:zlib\";\nimport { allocateNativeState } from \"./native-transcript-state\";\nimport { fixtureParts } from \"./fixtures/preparation\";\nimport { isRecord, isUnknownArray } from \"./value-guards\";\nimport { sha256 } from \"./identity\";\n\nconst MAX_NATIVE_BYTES = 5 * 1024 * 1024;\ntype NativeFormat =\n \"sevro.native-transcripts.v1\" | \"sevro.native-transcripts.v2\";\n\nexport async function materializeNativeTranscriptView(\n bytes: Uint8Array,\n): Promise<string> {\n const value: unknown = JSON.parse(Buffer.from(bytes).toString(\"utf8\"));\n const bundle = nativeBundle(value);\n const root = await allocateNativeState(\"view-\");\n await writeFile(join(root, \"index.json\"), bytes, { flag: \"wx\", mode: 0o600 });\n const filesRoot = join(root, bundle.host);\n let remainingBytes = MAX_NATIVE_BYTES;\n for (const item of bundle.files)\n remainingBytes -= await materializeNativeFile(\n filesRoot,\n item,\n bundle.format,\n remainingBytes,\n );\n return root;\n}\n\nfunction nativeBundle(value: unknown) {\n if (!isRecord(value) || !nativeFormat(value.format))\n throw new Error(\"invalid native transcript bundle\");\n if (!nativeHost(value.host))\n throw new Error(\"invalid native transcript host\");\n if (!isUnknownArray(value.files))\n throw new Error(\"invalid native transcript files\");\n return { host: value.host, files: value.files, format: value.format };\n}\n\nfunction nativeFormat(value: unknown): value is NativeFormat {\n return (\n value === \"sevro.native-transcripts.v1\" ||\n value === \"sevro.native-transcripts.v2\"\n );\n}\n\nfunction nativeHost(value: unknown): value is \"codex\" | \"claude\" {\n return value === \"codex\" || value === \"claude\";\n}\n\nasync function materializeNativeFile(\n root: string,\n value: unknown,\n format: NativeFormat,\n remainingBytes: number,\n): Promise<number> {\n if (!isRecord(value) || typeof value.path !== \"string\")\n throw new Error(\"invalid native transcript file\");\n const path = join(root, ...fixtureParts(value.path));\n const bytes = nativeFileBytes(value, format, remainingBytes);\n if (bytes.length > remainingBytes)\n throw new Error(\"native transcript byte limit exceeded\");\n if (sha256(bytes) !== value.sha256)\n throw new Error(\"native transcript hash mismatch\");\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await writeFile(path, bytes, { flag: \"wx\", mode: 0o600 });\n return bytes.length;\n}\n\nfunction nativeFileBytes(\n value: Record<string, unknown>,\n format: NativeFormat,\n remainingBytes: number,\n): Buffer {\n const encoded =\n format === \"sevro.native-transcripts.v1\"\n ? value.bytesBase64\n : value.gzipBase64;\n if (typeof encoded !== \"string\")\n throw new Error(\"invalid native transcript file\");\n const bytes = Buffer.from(encoded, \"base64\");\n if (format === \"sevro.native-transcripts.v1\") return bytes;\n try {\n return gunzipSync(bytes, { maxOutputLength: Math.max(1, remainingBytes) });\n } catch (cause) {\n throw new Error(\"invalid native transcript gzip\", { cause });\n }\n}\n"]}