@bjoernrochel/sevro 1.0.0 → 1.1.1

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 (405) 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 +158 -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/claude-settings.ts +3 -1
  392. package/src/hosts/claude.ts +1 -1
  393. package/src/hosts/isolation-roots.ts +3 -2
  394. package/src/hosts/process-lifecycle.ts +4 -6
  395. package/src/hosts/protected-worktrees.ts +2 -1
  396. package/src/hosts/skill-hook-handler.ts +1 -1
  397. package/src/hosts/skill-hooks.ts +10 -2
  398. package/src/provenance.ts +7 -6
  399. package/src/results-lock.ts +4 -4
  400. package/src/run-owner.ts +15 -9
  401. package/src/runner-paths.ts +21 -0
  402. package/src/runtime/hook-processes.ts +2 -1
  403. package/src/runtime/hooks.ts +9 -7
  404. package/src/runtime-process.ts +194 -0
  405. package/src/sqlite-lock.ts +25 -0
@@ -0,0 +1,248 @@
1
+ import { diagnosticSecrets, redactDiagnosticText, } from "./host-diagnostic-redaction.js";
2
+ const MAX_TEXT_BYTES = 64 * 1024;
3
+ export class HostDiagnosticPersistenceError extends Error {
4
+ constructor(persistenceCause, hostCause) {
5
+ super("host diagnostic persistence failed", {
6
+ cause: new AggregateError([hostCause, persistenceCause], "host and persistence failures"),
7
+ });
8
+ }
9
+ }
10
+ export function rethrowDiagnosticPersistenceError(cause) {
11
+ if (cause instanceof HostDiagnosticPersistenceError)
12
+ throw cause;
13
+ }
14
+ /** Drain observers capture only a prefix; completion records actual native termination. */
15
+ export class HostProcessDiagnostics {
16
+ diagnostics;
17
+ stdout = new DiagnosticStream();
18
+ stderr = new DiagnosticStream();
19
+ started = performance.now();
20
+ constructor(diagnostics) {
21
+ this.diagnostics = diagnostics;
22
+ }
23
+ recordUnstarted() {
24
+ if (!this.diagnostics.enabled)
25
+ return;
26
+ this.diagnostics.process({
27
+ code: null,
28
+ signal: null,
29
+ durationMs: performance.now() - this.started,
30
+ out: "",
31
+ stderr: new Uint8Array(),
32
+ stderrTruncated: false,
33
+ reason: "spawn",
34
+ });
35
+ }
36
+ async record(process, reason = "exit") {
37
+ if (!this.diagnostics.enabled)
38
+ return;
39
+ this.diagnostics.process({
40
+ code: await process.exited,
41
+ signal: process.signalCode ?? null,
42
+ durationMs: performance.now() - this.started,
43
+ out: this.stdout.bytes().toString("utf8"),
44
+ stdoutTruncated: this.stdout.truncated,
45
+ stderr: this.stderr.bytes(),
46
+ stderrTruncated: this.stderr.truncated,
47
+ reason,
48
+ });
49
+ }
50
+ }
51
+ function boundedText(bytes, secrets) {
52
+ const redacted = Buffer.from(redactDiagnosticText(Buffer.from(bytes.subarray(0, MAX_TEXT_BYTES)).toString("utf8"), secrets));
53
+ return {
54
+ text: new TextDecoder().decode(redacted.subarray(0, MAX_TEXT_BYTES), {
55
+ stream: true,
56
+ }),
57
+ truncated: bytes.byteLength > MAX_TEXT_BYTES || redacted.byteLength > MAX_TEXT_BYTES,
58
+ };
59
+ }
60
+ /** Keep a byte-bounded prefix while the owning transport continues draining. */
61
+ export class DiagnosticStream {
62
+ chunks = [];
63
+ size = 0;
64
+ truncated = false;
65
+ add(bytes) {
66
+ const retained = bytes.subarray(0, Math.max(0, MAX_TEXT_BYTES - this.size));
67
+ if (retained.byteLength)
68
+ this.chunks.push(Buffer.from(retained));
69
+ this.size += retained.byteLength;
70
+ this.truncated ||= retained.byteLength !== bytes.byteLength;
71
+ }
72
+ bytes() {
73
+ return Buffer.concat(this.chunks);
74
+ }
75
+ }
76
+ function errorCode(cause) {
77
+ if (!("code" in cause))
78
+ return {};
79
+ const code = cause.code;
80
+ return typeof code === "string" || typeof code === "number" ? { code } : {};
81
+ }
82
+ function errorEvidence(cause, secrets) {
83
+ return errorChain(cause, secrets, new Set());
84
+ }
85
+ function errorChain(cause, secrets, seen) {
86
+ if (!(cause instanceof Error))
87
+ return {
88
+ name: "NonError",
89
+ message: redactDiagnosticText(String(cause).slice(0, 8192), secrets),
90
+ };
91
+ if (seen.has(cause) || seen.size === 8)
92
+ return { truncated: true };
93
+ seen.add(cause);
94
+ return {
95
+ ...errorDetails(cause, secrets),
96
+ ...(cause.cause === undefined
97
+ ? {}
98
+ : { cause: errorChain(cause.cause, secrets, seen) }),
99
+ };
100
+ }
101
+ function errorDetails(cause, secrets) {
102
+ return {
103
+ name: redactDiagnosticText(cause.name.slice(0, 200), secrets),
104
+ message: redactDiagnosticText(cause.message.slice(0, 8192), secrets),
105
+ stack: redactDiagnosticText(cause.stack?.slice(0, 8192) ?? "", secrets),
106
+ ...errorCode(cause),
107
+ };
108
+ }
109
+ /** A cleanup exception must not replace an already captured host/persistence failure. */
110
+ export async function cleanupNativeState(diagnostics, cleanup) {
111
+ beginNativeCleanup(diagnostics);
112
+ try {
113
+ await cleanup();
114
+ }
115
+ catch (cause) {
116
+ if (!diagnostics.failureCaptured)
117
+ throw cause;
118
+ console.warn("native state cleanup failed; original host failure preserved");
119
+ }
120
+ }
121
+ function beginNativeCleanup(diagnostics) {
122
+ if (!diagnostics.failureCaptured)
123
+ diagnostics.stage = "cleanup";
124
+ }
125
+ export function diagnosticConfiguration(enabled) {
126
+ return enabled ? { "sevro.host.retain-diagnostics": true } : {};
127
+ }
128
+ export function createHostDiagnostics(host, enabled, request) {
129
+ return enabled
130
+ ? new RetainedHostDiagnostics(host, request.runtimeRole, request.retainHostDiagnostics)
131
+ : new DisabledHostDiagnostics();
132
+ }
133
+ export async function diagnoseHostRequest(diagnostics, execute) {
134
+ try {
135
+ return await execute();
136
+ }
137
+ catch (cause) {
138
+ if (cause instanceof HostDiagnosticPersistenceError)
139
+ throw cause;
140
+ if (!diagnostics.enabled)
141
+ throw cause;
142
+ const result = {
143
+ finalMessage: null,
144
+ complete: false,
145
+ executionFailed: true,
146
+ actualCondition: "passive",
147
+ };
148
+ await diagnostics.retain(result, cause);
149
+ return result;
150
+ }
151
+ }
152
+ class DisabledHostDiagnostics {
153
+ enabled = false;
154
+ stage = "preparation";
155
+ failure;
156
+ failureCaptured = false;
157
+ readCredentials() {
158
+ return Promise.resolve();
159
+ }
160
+ registerCredentials() { }
161
+ process() { }
162
+ retain() {
163
+ return Promise.resolve();
164
+ }
165
+ retainFailure() {
166
+ return Promise.resolve();
167
+ }
168
+ }
169
+ /** Private failure evidence, independent of native sessions and transcript access. */
170
+ class RetainedHostDiagnostics {
171
+ host;
172
+ role;
173
+ persist;
174
+ enabled = true;
175
+ stage = "preparation";
176
+ failure;
177
+ failureCaptured = false;
178
+ processes = [];
179
+ processesDropped = 0;
180
+ secrets = [];
181
+ constructor(host, role = "candidate", persist) {
182
+ this.host = host;
183
+ this.role = role;
184
+ this.persist = persist;
185
+ }
186
+ async readCredentials(path) {
187
+ if (path)
188
+ this.secrets.push(...(await diagnosticSecrets(path)));
189
+ }
190
+ registerCredentials(environment, names) {
191
+ for (const name of names) {
192
+ const value = environment[name];
193
+ if (value)
194
+ this.secrets.push(value);
195
+ }
196
+ }
197
+ process(result) {
198
+ const stdout = boundedText(Buffer.from(result.out), this.secrets);
199
+ const stderr = boundedText(result.stderr, this.secrets);
200
+ this.addProcess({
201
+ stage: this.stage,
202
+ code: result.code,
203
+ signal: result.signal,
204
+ durationMs: result.durationMs,
205
+ reason: result.reason,
206
+ stdout: stdout.text,
207
+ stdoutTruncated: stdout.truncated || result.stdoutTruncated === true,
208
+ stderr: stderr.text,
209
+ stderrTruncated: stderr.truncated || result.stderrTruncated,
210
+ });
211
+ }
212
+ addProcess(receipt) {
213
+ this.processes.push(receipt);
214
+ while (this.processes.length > 32 ||
215
+ Buffer.byteLength(JSON.stringify(this.processes)) > 2 * 1024 * 1024) {
216
+ this.processes.shift();
217
+ this.processesDropped++;
218
+ }
219
+ }
220
+ async retainFailure(result, message) {
221
+ if (result.executionFailed)
222
+ await this.retain(result, this.failure ?? new Error(message));
223
+ }
224
+ async retain(result, cause) {
225
+ this.failureCaptured = true;
226
+ const bytes = Buffer.from(JSON.stringify({
227
+ format: "sevro.host-diagnostics.v1",
228
+ host: this.host,
229
+ role: this.role,
230
+ stage: this.stage,
231
+ error: errorEvidence(cause, this.secrets),
232
+ processes: this.processes,
233
+ processesDropped: this.processesDropped,
234
+ }));
235
+ if (this.persist) {
236
+ try {
237
+ await this.persist(bytes);
238
+ }
239
+ catch (persistenceCause) {
240
+ throw new HostDiagnosticPersistenceError(persistenceCause, cause);
241
+ }
242
+ return;
243
+ }
244
+ result.artifacts ??= [];
245
+ result.artifacts.push({ id: "sevro.host.diagnostics", bytes });
246
+ }
247
+ }
248
+ //# sourceMappingURL=host-diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-diagnostics.js","sourceRoot":"","sources":["host-diagnostics.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HostResult } from \"./evaluation/types\";\nimport type { RuntimeRole } from \"./runtime/state\";\nimport {\n diagnosticSecrets,\n redactDiagnosticText,\n} from \"./host-diagnostic-redaction\";\n\nconst MAX_TEXT_BYTES = 64 * 1024;\ninterface ProcessDiagnostic {\n code: number | null;\n out: string;\n stderr: Uint8Array;\n stderrTruncated: boolean;\n signal: string | null;\n durationMs: number;\n stdoutTruncated?: boolean;\n reason: \"exit\" | \"timeout\" | \"cancelled\" | \"error\" | \"spawn\";\n}\n\nexport type ProcessFailureReason = ProcessDiagnostic[\"reason\"];\n\nexport class HostDiagnosticPersistenceError extends Error {\n constructor(persistenceCause: unknown, hostCause: unknown) {\n super(\"host diagnostic persistence failed\", {\n cause: new AggregateError(\n [hostCause, persistenceCause],\n \"host and persistence failures\",\n ),\n });\n }\n}\n\nexport function rethrowDiagnosticPersistenceError(cause: unknown): void {\n if (cause instanceof HostDiagnosticPersistenceError) throw cause;\n}\n\n/** Drain observers capture only a prefix; completion records actual native termination. */\nexport class HostProcessDiagnostics {\n readonly stdout = new DiagnosticStream();\n readonly stderr = new DiagnosticStream();\n private started = performance.now();\n constructor(private diagnostics: HostDiagnostics) {}\n recordUnstarted(): void {\n if (!this.diagnostics.enabled) return;\n this.diagnostics.process({\n code: null,\n signal: null,\n durationMs: performance.now() - this.started,\n out: \"\",\n stderr: new Uint8Array(),\n stderrTruncated: false,\n reason: \"spawn\",\n });\n }\n async record(\n process: { exited: Promise<number | null>; signalCode?: string | null },\n reason: ProcessFailureReason = \"exit\",\n ): Promise<void> {\n if (!this.diagnostics.enabled) return;\n this.diagnostics.process({\n code: await process.exited,\n signal: process.signalCode ?? null,\n durationMs: performance.now() - this.started,\n out: this.stdout.bytes().toString(\"utf8\"),\n stdoutTruncated: this.stdout.truncated,\n stderr: this.stderr.bytes(),\n stderrTruncated: this.stderr.truncated,\n reason,\n });\n }\n}\n\nfunction boundedText(bytes: Uint8Array, secrets: string[]) {\n const redacted = Buffer.from(\n redactDiagnosticText(\n Buffer.from(bytes.subarray(0, MAX_TEXT_BYTES)).toString(\"utf8\"),\n secrets,\n ),\n );\n return {\n text: new TextDecoder().decode(redacted.subarray(0, MAX_TEXT_BYTES), {\n stream: true,\n }),\n truncated:\n bytes.byteLength > MAX_TEXT_BYTES || redacted.byteLength > MAX_TEXT_BYTES,\n };\n}\n\n/** Keep a byte-bounded prefix while the owning transport continues draining. */\nexport class DiagnosticStream {\n private chunks: Uint8Array[] = [];\n private size = 0;\n truncated = false;\n add(bytes: Uint8Array): void {\n const retained = bytes.subarray(0, Math.max(0, MAX_TEXT_BYTES - this.size));\n if (retained.byteLength) this.chunks.push(Buffer.from(retained));\n this.size += retained.byteLength;\n this.truncated ||= retained.byteLength !== bytes.byteLength;\n }\n bytes(): Buffer {\n return Buffer.concat(this.chunks);\n }\n}\n\nfunction errorCode(cause: Error): { code?: string | number } {\n if (!(\"code\" in cause)) return {};\n const code: unknown = cause.code;\n return typeof code === \"string\" || typeof code === \"number\" ? { code } : {};\n}\n\nfunction errorEvidence(\n cause: unknown,\n secrets: string[],\n): Record<string, unknown> {\n return errorChain(cause, secrets, new Set());\n}\n\nfunction errorChain(\n cause: unknown,\n secrets: string[],\n seen: Set<Error>,\n): Record<string, unknown> {\n if (!(cause instanceof Error))\n return {\n name: \"NonError\",\n message: redactDiagnosticText(String(cause).slice(0, 8192), secrets),\n };\n if (seen.has(cause) || seen.size === 8) return { truncated: true };\n seen.add(cause);\n return {\n ...errorDetails(cause, secrets),\n ...(cause.cause === undefined\n ? {}\n : { cause: errorChain(cause.cause, secrets, seen) }),\n };\n}\n\nfunction errorDetails(cause: Error, secrets: string[]) {\n return {\n name: redactDiagnosticText(cause.name.slice(0, 200), secrets),\n message: redactDiagnosticText(cause.message.slice(0, 8192), secrets),\n stack: redactDiagnosticText(cause.stack?.slice(0, 8192) ?? \"\", secrets),\n ...errorCode(cause),\n };\n}\n\n/** A cleanup exception must not replace an already captured host/persistence failure. */\nexport async function cleanupNativeState(\n diagnostics: HostDiagnostics,\n cleanup: () => Promise<void>,\n): Promise<void> {\n beginNativeCleanup(diagnostics);\n try {\n await cleanup();\n } catch (cause) {\n if (!diagnostics.failureCaptured) throw cause;\n console.warn(\n \"native state cleanup failed; original host failure preserved\",\n );\n }\n}\n\nfunction beginNativeCleanup(diagnostics: HostDiagnostics): void {\n if (!diagnostics.failureCaptured) diagnostics.stage = \"cleanup\";\n}\n\nexport function diagnosticConfiguration(enabled: boolean | undefined) {\n return enabled ? { \"sevro.host.retain-diagnostics\": true } : {};\n}\n\nexport function createHostDiagnostics(\n host: \"codex\" | \"claude\",\n enabled: boolean | undefined,\n request: {\n runtimeRole?: RuntimeRole;\n retainHostDiagnostics?: (bytes: Uint8Array) => Promise<void>;\n },\n): HostDiagnostics {\n return enabled\n ? new RetainedHostDiagnostics(\n host,\n request.runtimeRole,\n request.retainHostDiagnostics,\n )\n : new DisabledHostDiagnostics();\n}\n\nexport async function diagnoseHostRequest(\n diagnostics: HostDiagnostics,\n execute: () => Promise<HostResult>,\n): Promise<HostResult> {\n try {\n return await execute();\n } catch (cause) {\n if (cause instanceof HostDiagnosticPersistenceError) throw cause;\n if (!diagnostics.enabled) throw cause;\n const result: HostResult = {\n finalMessage: null,\n complete: false,\n executionFailed: true,\n actualCondition: \"passive\",\n };\n await diagnostics.retain(result, cause);\n return result;\n }\n}\n\nexport interface HostDiagnostics {\n readonly enabled: boolean;\n stage: string;\n failure: unknown;\n readonly failureCaptured: boolean;\n readCredentials(path: string | undefined): Promise<void>;\n registerCredentials(\n environment: Record<string, string>,\n names: readonly string[],\n ): void;\n process(result: ProcessDiagnostic): void;\n retain(result: HostResult, cause: unknown): Promise<void>;\n retainFailure(result: HostResult, message: string): Promise<void>;\n}\n\nclass DisabledHostDiagnostics implements HostDiagnostics {\n readonly enabled = false;\n stage = \"preparation\";\n failure: unknown;\n readonly failureCaptured = false;\n\n readCredentials(): Promise<void> {\n return Promise.resolve();\n }\n\n registerCredentials(): void {}\n\n process(): void {}\n\n retain(): Promise<void> {\n return Promise.resolve();\n }\n\n retainFailure(): Promise<void> {\n return Promise.resolve();\n }\n}\n\n/** Private failure evidence, independent of native sessions and transcript access. */\nclass RetainedHostDiagnostics implements HostDiagnostics {\n readonly enabled = true;\n stage = \"preparation\";\n failure: unknown;\n failureCaptured = false;\n private processes: Record<string, unknown>[] = [];\n private processesDropped = 0;\n private secrets: string[] = [];\n constructor(\n private host: \"codex\" | \"claude\",\n private role: RuntimeRole = \"candidate\",\n private persist?: (bytes: Uint8Array) => Promise<void>,\n ) {}\n\n async readCredentials(path: string | undefined): Promise<void> {\n if (path) this.secrets.push(...(await diagnosticSecrets(path)));\n }\n\n registerCredentials(\n environment: Record<string, string>,\n names: readonly string[],\n ): void {\n for (const name of names) {\n const value = environment[name];\n if (value) this.secrets.push(value);\n }\n }\n\n process(result: ProcessDiagnostic): void {\n const stdout = boundedText(Buffer.from(result.out), this.secrets);\n const stderr = boundedText(result.stderr, this.secrets);\n this.addProcess({\n stage: this.stage,\n code: result.code,\n signal: result.signal,\n durationMs: result.durationMs,\n reason: result.reason,\n stdout: stdout.text,\n stdoutTruncated: stdout.truncated || result.stdoutTruncated === true,\n stderr: stderr.text,\n stderrTruncated: stderr.truncated || result.stderrTruncated,\n });\n }\n\n private addProcess(receipt: Record<string, unknown>): void {\n this.processes.push(receipt);\n while (\n this.processes.length > 32 ||\n Buffer.byteLength(JSON.stringify(this.processes)) > 2 * 1024 * 1024\n ) {\n this.processes.shift();\n this.processesDropped++;\n }\n }\n\n async retainFailure(result: HostResult, message: string): Promise<void> {\n if (result.executionFailed)\n await this.retain(result, this.failure ?? new Error(message));\n }\n\n async retain(result: HostResult, cause: unknown): Promise<void> {\n this.failureCaptured = true;\n const bytes = Buffer.from(\n JSON.stringify({\n format: \"sevro.host-diagnostics.v1\",\n host: this.host,\n role: this.role,\n stage: this.stage,\n error: errorEvidence(cause, this.secrets),\n processes: this.processes,\n processesDropped: this.processesDropped,\n }),\n );\n if (this.persist) {\n try {\n await this.persist(bytes);\n } catch (persistenceCause) {\n throw new HostDiagnosticPersistenceError(persistenceCause, cause);\n }\n return;\n }\n result.artifacts ??= [];\n result.artifacts.push({ id: \"sevro.host.diagnostics\", bytes });\n }\n}\n"]}
@@ -0,0 +1,89 @@
1
+ import { isRecord } from "../value-guards.js";
2
+ import { randomUUID } from "node:crypto";
3
+ import { summarizeClaudeEvents, parseClaudeEvents, } from "./claude-events.js";
4
+ import { workspaceFingerprint } from "./workspace-fingerprint.js";
5
+ import { MAX_EVENT_BYTES, MAX_STDERR_BYTES } from "./process-lifecycle.js";
6
+ function boundResult(stream, session) {
7
+ if (stream.lines.some((line) => !line.parsed))
8
+ return false;
9
+ const results = stream.lines
10
+ .filter((line) => line.parsed)
11
+ .map((line) => line.value)
12
+ .filter(isRecord)
13
+ .filter((entry) => entry.type === "result");
14
+ const [result] = results;
15
+ return (results.length === 1 &&
16
+ result !== undefined &&
17
+ result.session_id === session);
18
+ }
19
+ function parsedTurn(result) {
20
+ return { ...result, events: parseClaudeEvents(result.out) };
21
+ }
22
+ /** Resume a declared follow-up in the same isolated native Claude session. */
23
+ export async function runClaudeTurns(options) {
24
+ if (options.followUpPrompt === undefined)
25
+ return parsedTurn(await options.run(options.prompt));
26
+ const sessionId = randomUUID();
27
+ const initialFingerprint = await workspaceFingerprint(options.workspace);
28
+ const initial = parsedTurn(await options.run(options.prompt, {
29
+ option: "--session-id",
30
+ id: sessionId,
31
+ }));
32
+ const summary = summarizeClaudeEvents(initial.events, initial.code);
33
+ const initialBound = boundResult(initial.events, sessionId);
34
+ if (!summary.complete || !initialBound)
35
+ return {
36
+ ...initial,
37
+ code: summary.complete ? 1 : initial.code,
38
+ sessionResultsBound: initialBound,
39
+ };
40
+ const beforeFollowUp = await workspaceFingerprint(options.workspace);
41
+ const followUp = parsedTurn(await options.run(options.followUpPrompt, {
42
+ option: "--resume",
43
+ id: sessionId,
44
+ }));
45
+ return combinedTurns(initial, followUp, sessionId, initialFingerprint, beforeFollowUp);
46
+ }
47
+ function combinedOutput(initial, followUp) {
48
+ const out = `${initial.out.trimEnd()}\n${followUp.out.trimStart()}`;
49
+ const stderr = [initial.err.trimEnd(), followUp.err.trimStart()]
50
+ .filter(Boolean)
51
+ .join("\n");
52
+ const err = new TextDecoder("utf-8", { ignoreBOM: true }).decode(Buffer.from(stderr, "utf8").subarray(0, MAX_STDERR_BYTES), { stream: true });
53
+ if (Buffer.byteLength(out, "utf8") > MAX_EVENT_BYTES)
54
+ throw new Error("Claude combined process output exceeds its limit");
55
+ return { out, err };
56
+ }
57
+ function continuationObservation(bound, sessionId, initialFingerprint, beforeFollowUp) {
58
+ const measured = initialFingerprint !== null && beforeFollowUp !== null;
59
+ return {
60
+ id: "sevro.claude.continuation",
61
+ completeness: bound && measured ? "complete" : "partial",
62
+ data: {
63
+ method: "same_session_resume",
64
+ sessionId,
65
+ preFollowUpWorktreeUnchanged: measured
66
+ ? initialFingerprint === beforeFollowUp
67
+ : null,
68
+ },
69
+ };
70
+ }
71
+ function combinedTurns(initial, followUp, sessionId, initialFingerprint, beforeFollowUp) {
72
+ const followUpSummary = summarizeClaudeEvents(followUp.events, followUp.code);
73
+ const sessionResultsBound = boundResult(followUp.events, sessionId);
74
+ const bound = followUpSummary.complete && sessionResultsBound;
75
+ const { out, err } = combinedOutput(initial, followUp);
76
+ return {
77
+ code: bound ? 0 : followUp.code || 1,
78
+ out,
79
+ err,
80
+ initialOut: initial.out,
81
+ followUpOut: followUp.out,
82
+ events: { lines: [...initial.events.lines, ...followUp.events.lines] },
83
+ initialEvents: initial.events,
84
+ followUpEvents: followUp.events,
85
+ sessionResultsBound,
86
+ continuation: continuationObservation(bound, sessionId, initialFingerprint, beforeFollowUp),
87
+ };
88
+ }
89
+ //# sourceMappingURL=claude-continuation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-continuation.js","sourceRoot":"","sources":["claude-continuation.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRecord } from \"../value-guards\";\nimport { randomUUID } from \"node:crypto\";\nimport {\n summarizeClaudeEvents,\n parseClaudeEvents,\n type ClaudeEvents,\n} from \"./claude-events\";\nimport { workspaceFingerprint } from \"./workspace-fingerprint\";\nimport { MAX_EVENT_BYTES, MAX_STDERR_BYTES } from \"./process-lifecycle\";\n\nexport type ClaudeProcessResult = { code: number; out: string; err: string };\nexport type ClaudeSession = { option: \"--session-id\" | \"--resume\"; id: string };\ntype Observation = {\n id: string;\n completeness: \"complete\" | \"partial\";\n data: Record<string, unknown>;\n};\ntype ClaudeTurns = ClaudeProcessResult & {\n events: ClaudeEvents;\n initialEvents?: ClaudeEvents;\n followUpEvents?: ClaudeEvents;\n initialOut?: string;\n followUpOut?: string;\n continuation?: Observation;\n sessionResultsBound?: boolean;\n};\n\nfunction boundResult(stream: ClaudeEvents, session: string): boolean {\n if (stream.lines.some((line) => !line.parsed)) return false;\n const results = stream.lines\n .filter((line) => line.parsed)\n .map((line) => line.value)\n .filter(isRecord)\n .filter((entry) => entry.type === \"result\");\n const [result] = results;\n return (\n results.length === 1 &&\n result !== undefined &&\n result.session_id === session\n );\n}\n\nfunction parsedTurn(result: ClaudeProcessResult) {\n return { ...result, events: parseClaudeEvents(result.out) };\n}\n\n/** Resume a declared follow-up in the same isolated native Claude session. */\nexport async function runClaudeTurns(options: {\n prompt: string;\n followUpPrompt?: string;\n workspace: string;\n run: (\n prompt: string,\n session?: ClaudeSession,\n ) => Promise<ClaudeProcessResult>;\n}): Promise<ClaudeTurns> {\n if (options.followUpPrompt === undefined)\n return parsedTurn(await options.run(options.prompt));\n const sessionId = randomUUID();\n const initialFingerprint = await workspaceFingerprint(options.workspace);\n const initial = parsedTurn(\n await options.run(options.prompt, {\n option: \"--session-id\",\n id: sessionId,\n }),\n );\n const summary = summarizeClaudeEvents(initial.events, initial.code);\n const initialBound = boundResult(initial.events, sessionId);\n if (!summary.complete || !initialBound)\n return {\n ...initial,\n code: summary.complete ? 1 : initial.code,\n sessionResultsBound: initialBound,\n };\n const beforeFollowUp = await workspaceFingerprint(options.workspace);\n const followUp = parsedTurn(\n await options.run(options.followUpPrompt, {\n option: \"--resume\",\n id: sessionId,\n }),\n );\n return combinedTurns(\n initial,\n followUp,\n sessionId,\n initialFingerprint,\n beforeFollowUp,\n );\n}\n\nfunction combinedOutput(\n initial: ReturnType<typeof parsedTurn>,\n followUp: ReturnType<typeof parsedTurn>,\n) {\n const out = `${initial.out.trimEnd()}\\n${followUp.out.trimStart()}`;\n const stderr = [initial.err.trimEnd(), followUp.err.trimStart()]\n .filter(Boolean)\n .join(\"\\n\");\n const err = new TextDecoder(\"utf-8\", { ignoreBOM: true }).decode(\n Buffer.from(stderr, \"utf8\").subarray(0, MAX_STDERR_BYTES),\n { stream: true },\n );\n if (Buffer.byteLength(out, \"utf8\") > MAX_EVENT_BYTES)\n throw new Error(\"Claude combined process output exceeds its limit\");\n return { out, err };\n}\n\nfunction continuationObservation(\n bound: boolean,\n sessionId: string,\n initialFingerprint: string | null,\n beforeFollowUp: string | null,\n): Observation {\n const measured = initialFingerprint !== null && beforeFollowUp !== null;\n return {\n id: \"sevro.claude.continuation\",\n completeness: bound && measured ? \"complete\" : \"partial\",\n data: {\n method: \"same_session_resume\",\n sessionId,\n preFollowUpWorktreeUnchanged: measured\n ? initialFingerprint === beforeFollowUp\n : null,\n },\n };\n}\n\nfunction combinedTurns(\n initial: ReturnType<typeof parsedTurn>,\n followUp: ReturnType<typeof parsedTurn>,\n sessionId: string,\n initialFingerprint: string | null,\n beforeFollowUp: string | null,\n): ClaudeTurns {\n const followUpSummary = summarizeClaudeEvents(followUp.events, followUp.code);\n const sessionResultsBound = boundResult(followUp.events, sessionId);\n const bound = followUpSummary.complete && sessionResultsBound;\n const { out, err } = combinedOutput(initial, followUp);\n return {\n code: bound ? 0 : followUp.code || 1,\n out,\n err,\n initialOut: initial.out,\n followUpOut: followUp.out,\n events: { lines: [...initial.events.lines, ...followUp.events.lines] },\n initialEvents: initial.events,\n followUpEvents: followUp.events,\n sessionResultsBound,\n continuation: continuationObservation(\n bound,\n sessionId,\n initialFingerprint,\n beforeFollowUp,\n ),\n };\n}\n"]}
@@ -0,0 +1,121 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { nativeStateParent } from "../native-transcript-state.js";
4
+ import { coversLifetime, parseClaudeLogin, rotatedLoginBytes, sameTokens, tokenDigest, } from "./claude-login.js";
5
+ import { claudeLoginRecovery, } from "./claude-login-recovery.js";
6
+ import { refreshClaudeLogin } from "./claude-login-refresh.js";
7
+ import { claudeLoginStore, MAX_CREDENTIAL_BYTES, requireBoundedCredential, } from "./claude-login-store.js";
8
+ import { withClaudeRefreshLock } from "./claude-refresh-lock.js";
9
+ export const CLAUDE_AUTH_VARIABLES = [
10
+ "ANTHROPIC_API_KEY",
11
+ "CLAUDE_CODE_OAUTH_TOKEN",
12
+ ];
13
+ /** Margin beyond the execution budget, matching Claude's own refresh window. */
14
+ export const CLAUDE_LOGIN_MARGIN_MS = 5 * 60_000;
15
+ /** Keep the CLI's authentication separate from the candidate tool environment. */
16
+ export async function stageClaudeAuthentication(configRoot, sourceFile, requirement) {
17
+ await mkdir(configRoot, { recursive: true, mode: 0o700 });
18
+ const environment = authenticationEnvironment(sourceFile);
19
+ if (!Object.keys(environment).length)
20
+ environment.CLAUDE_CODE_OAUTH_TOKEN = await claudeAccessToken(sourceFile, requirement);
21
+ return {
22
+ // Trials never receive a saved login; the path only anchors private state.
23
+ credentialFile: join(configRoot, ".credentials.json"),
24
+ environment,
25
+ };
26
+ }
27
+ function boundedEnvironmentCredential(value) {
28
+ if (Buffer.byteLength(value, "utf8") > MAX_CREDENTIAL_BYTES)
29
+ throw new Error("Claude environment credential exceeds the size limit");
30
+ return value;
31
+ }
32
+ function authenticationEnvironment(sourceFile) {
33
+ const environment = {};
34
+ if (sourceFile === undefined) {
35
+ for (const name of CLAUDE_AUTH_VARIABLES) {
36
+ const value = process.env[name];
37
+ if (value) {
38
+ environment[name] = boundedEnvironmentCredential(value);
39
+ }
40
+ }
41
+ }
42
+ return environment;
43
+ }
44
+ async function platformKeychain() {
45
+ if (process.platform !== "darwin")
46
+ return undefined;
47
+ const { claudeKeychain } = await import("./claude-keychain.js");
48
+ return claudeKeychain;
49
+ }
50
+ /**
51
+ * Provide an access token from the selected saved login. The parent alone
52
+ * refreshes it, so a rotating refresh token never enters disposable state.
53
+ */
54
+ export async function claudeAccessToken(sourceFile, requirement) {
55
+ const store = await claudeLoginStore(sourceFile, await platformKeychain());
56
+ const recovery = await claudeLoginRecovery(await nativeStateParent(), store.origin);
57
+ const login = await storedLogin(store);
58
+ if (coversLifetime(login, requirement.lifetimeMs) &&
59
+ !(await recovery.pending()))
60
+ return login.accessToken;
61
+ return withClaudeRefreshLock(store.lockRoot, () => lockedAccessToken(store, recovery, requirement));
62
+ }
63
+ async function storedLogin(store) {
64
+ return parseClaudeLogin(requireBoundedCredential(await store.read()));
65
+ }
66
+ /** Re-read under the lock: another process may already have refreshed. */
67
+ async function lockedAccessToken(store, recovery, requirement) {
68
+ const current = await recoveredLogin(store, recovery);
69
+ if (coversLifetime(current, requirement.lifetimeMs))
70
+ return current.accessToken;
71
+ const rotated = await refreshClaudeLogin(requirement.binary, refreshableLogin(current), recovery.workRoot);
72
+ await recovery.retain(current, rotated);
73
+ await persistRotation(store, recovery, current, rotated);
74
+ await recovery.discard();
75
+ return usableAccessToken(rotated, requirement.lifetimeMs);
76
+ }
77
+ function refreshableLogin(login) {
78
+ const { refreshToken } = login;
79
+ if (!refreshToken || !login.scopes.length)
80
+ throw new Error("Claude saved login has expired and cannot be refreshed; run claude and /login");
81
+ return { ...login, refreshToken };
82
+ }
83
+ function usableAccessToken(login, lifetimeMs) {
84
+ if (!coversLifetime(login, lifetimeMs))
85
+ throw new Error(`Claude login lifetime cannot cover the ${String(Math.ceil(lifetimeMs / 60_000))}-minute execution budget`);
86
+ return login.accessToken;
87
+ }
88
+ /** Complete a rotation left pending when its store could not be updated. */
89
+ async function recoveredLogin(store, recovery) {
90
+ const current = await storedLogin(store);
91
+ const pending = await recovery.pending();
92
+ if (!pending)
93
+ return current;
94
+ if (pending.replaces !== tokenDigest(current.refreshToken)) {
95
+ // The store moved on through another login; never overwrite it.
96
+ await recovery.discard();
97
+ return current;
98
+ }
99
+ const rotated = parseClaudeLogin(rotatedLoginBytes(current, { ...current, oauth: pending.oauth }));
100
+ await writeVerified(store, recovery, current, rotated);
101
+ await recovery.discard();
102
+ return rotated;
103
+ }
104
+ /** Compare-and-swap against the refresh token the rotation consumed. */
105
+ async function persistRotation(store, recovery, replaced, rotated) {
106
+ const latest = await storedLogin(store);
107
+ if (latest.refreshToken !== replaced.refreshToken)
108
+ return;
109
+ await writeVerified(store, recovery, latest, rotated);
110
+ }
111
+ async function writeVerified(store, recovery, latest, rotated) {
112
+ try {
113
+ await store.write(rotatedLoginBytes(latest, rotated));
114
+ if (!sameTokens(await storedLogin(store), rotated))
115
+ throw new Error("the stored login does not match the rotation");
116
+ }
117
+ catch (cause) {
118
+ throw new Error(`Claude refreshed the saved login but could not update ${store.origin}; Sevro retained the refreshed login at ${recovery.path} and restores it on the next run once the store is writable`, { cause });
119
+ }
120
+ }
121
+ //# sourceMappingURL=claude-credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-credential.js","sourceRoot":"","sources":["claude-credential.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { nativeStateParent } from \"../native-transcript-state\";\nimport {\n coversLifetime,\n parseClaudeLogin,\n rotatedLoginBytes,\n sameTokens,\n tokenDigest,\n type ClaudeLogin,\n} from \"./claude-login\";\nimport {\n claudeLoginRecovery,\n type ClaudeLoginRecovery,\n} from \"./claude-login-recovery\";\nimport { refreshClaudeLogin } from \"./claude-login-refresh\";\nimport {\n claudeLoginStore,\n MAX_CREDENTIAL_BYTES,\n requireBoundedCredential,\n type ClaudeKeychain,\n type ClaudeLoginStore,\n} from \"./claude-login-store\";\nimport { withClaudeRefreshLock } from \"./claude-refresh-lock\";\n\nexport const CLAUDE_AUTH_VARIABLES = [\n \"ANTHROPIC_API_KEY\",\n \"CLAUDE_CODE_OAUTH_TOKEN\",\n] as const;\n\n/** Margin beyond the execution budget, matching Claude's own refresh window. */\nexport const CLAUDE_LOGIN_MARGIN_MS = 5 * 60_000;\n\n/** What a saved login must provide before a trial may use it. */\nexport interface ClaudeLoginRequirement {\n /** Claude binary used for parent-owned refresh. */\n binary: string;\n /** Remaining access-token lifetime the complete execution needs. */\n lifetimeMs: number;\n}\n\n/** Keep the CLI's authentication separate from the candidate tool environment. */\nexport async function stageClaudeAuthentication(\n configRoot: string,\n sourceFile: string | undefined,\n requirement: ClaudeLoginRequirement,\n) {\n await mkdir(configRoot, { recursive: true, mode: 0o700 });\n const environment = authenticationEnvironment(sourceFile);\n if (!Object.keys(environment).length)\n environment.CLAUDE_CODE_OAUTH_TOKEN = await claudeAccessToken(\n sourceFile,\n requirement,\n );\n return {\n // Trials never receive a saved login; the path only anchors private state.\n credentialFile: join(configRoot, \".credentials.json\"),\n environment,\n };\n}\n\nfunction boundedEnvironmentCredential(value: string): string {\n if (Buffer.byteLength(value, \"utf8\") > MAX_CREDENTIAL_BYTES)\n throw new Error(\"Claude environment credential exceeds the size limit\");\n return value;\n}\n\nfunction authenticationEnvironment(sourceFile: string | undefined) {\n const environment: Record<string, string> = {};\n if (sourceFile === undefined) {\n for (const name of CLAUDE_AUTH_VARIABLES) {\n const value = process.env[name];\n if (value) {\n environment[name] = boundedEnvironmentCredential(value);\n }\n }\n }\n return environment;\n}\n\nasync function platformKeychain(): Promise<ClaudeKeychain | undefined> {\n if (process.platform !== \"darwin\") return undefined;\n const { claudeKeychain } = await import(\"./claude-keychain\");\n return claudeKeychain;\n}\n\n/**\n * Provide an access token from the selected saved login. The parent alone\n * refreshes it, so a rotating refresh token never enters disposable state.\n */\nexport async function claudeAccessToken(\n sourceFile: string | undefined,\n requirement: ClaudeLoginRequirement,\n): Promise<string> {\n const store = await claudeLoginStore(sourceFile, await platformKeychain());\n const recovery = await claudeLoginRecovery(\n await nativeStateParent(),\n store.origin,\n );\n const login = await storedLogin(store);\n if (\n coversLifetime(login, requirement.lifetimeMs) &&\n !(await recovery.pending())\n )\n return login.accessToken;\n return withClaudeRefreshLock(store.lockRoot, () =>\n lockedAccessToken(store, recovery, requirement),\n );\n}\n\nasync function storedLogin(store: ClaudeLoginStore): Promise<ClaudeLogin> {\n return parseClaudeLogin(requireBoundedCredential(await store.read()));\n}\n\n/** Re-read under the lock: another process may already have refreshed. */\nasync function lockedAccessToken(\n store: ClaudeLoginStore,\n recovery: ClaudeLoginRecovery,\n requirement: ClaudeLoginRequirement,\n): Promise<string> {\n const current = await recoveredLogin(store, recovery);\n if (coversLifetime(current, requirement.lifetimeMs))\n return current.accessToken;\n const rotated = await refreshClaudeLogin(\n requirement.binary,\n refreshableLogin(current),\n recovery.workRoot,\n );\n await recovery.retain(current, rotated);\n await persistRotation(store, recovery, current, rotated);\n await recovery.discard();\n return usableAccessToken(rotated, requirement.lifetimeMs);\n}\n\nfunction refreshableLogin(\n login: ClaudeLogin,\n): ClaudeLogin & { refreshToken: string } {\n const { refreshToken } = login;\n if (!refreshToken || !login.scopes.length)\n throw new Error(\n \"Claude saved login has expired and cannot be refreshed; run claude and /login\",\n );\n return { ...login, refreshToken };\n}\n\nfunction usableAccessToken(login: ClaudeLogin, lifetimeMs: number): string {\n if (!coversLifetime(login, lifetimeMs))\n throw new Error(\n `Claude login lifetime cannot cover the ${String(Math.ceil(lifetimeMs / 60_000))}-minute execution budget`,\n );\n return login.accessToken;\n}\n\n/** Complete a rotation left pending when its store could not be updated. */\nasync function recoveredLogin(\n store: ClaudeLoginStore,\n recovery: ClaudeLoginRecovery,\n): Promise<ClaudeLogin> {\n const current = await storedLogin(store);\n const pending = await recovery.pending();\n if (!pending) return current;\n if (pending.replaces !== tokenDigest(current.refreshToken)) {\n // The store moved on through another login; never overwrite it.\n await recovery.discard();\n return current;\n }\n const rotated = parseClaudeLogin(\n rotatedLoginBytes(current, { ...current, oauth: pending.oauth }),\n );\n await writeVerified(store, recovery, current, rotated);\n await recovery.discard();\n return rotated;\n}\n\n/** Compare-and-swap against the refresh token the rotation consumed. */\nasync function persistRotation(\n store: ClaudeLoginStore,\n recovery: ClaudeLoginRecovery,\n replaced: ClaudeLogin,\n rotated: ClaudeLogin,\n): Promise<void> {\n const latest = await storedLogin(store);\n if (latest.refreshToken !== replaced.refreshToken) return;\n await writeVerified(store, recovery, latest, rotated);\n}\n\nasync function writeVerified(\n store: ClaudeLoginStore,\n recovery: ClaudeLoginRecovery,\n latest: ClaudeLogin,\n rotated: ClaudeLogin,\n): Promise<void> {\n try {\n await store.write(rotatedLoginBytes(latest, rotated));\n if (!sameTokens(await storedLogin(store), rotated))\n throw new Error(\"the stored login does not match the rotation\");\n } catch (cause) {\n throw new Error(\n `Claude refreshed the saved login but could not update ${store.origin}; Sevro retained the refreshed login at ${recovery.path} and restores it on the next run once the store is writable`,\n { cause },\n );\n }\n}\n"]}