@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,151 @@
1
+ import { isRecord, isUnknownArray } from "../value-guards.js";
2
+ import { summarizeClaudeEvents, parseClaudeEvents, } from "./claude-events.js";
3
+ const MAX_CALLS = 128;
4
+ function label(value, max) {
5
+ return (typeof value === "string" &&
6
+ value.length > 0 &&
7
+ value.length <= max &&
8
+ /^[A-Za-z0-9_.:-]+$/.test(value));
9
+ }
10
+ export function codexNativeControls(observation) {
11
+ const complete = observation.completeness === "complete" && inspectedExecCalls(observation);
12
+ return {
13
+ id: "sevro.host.native-controls",
14
+ completeness: controlCompleteness(observation.completeness, complete),
15
+ data: {
16
+ method: "native_control_calls",
17
+ calls: observation.data.toolCalls.map(({ ordinal, namespace, name }) => ({
18
+ ordinal,
19
+ namespace,
20
+ name,
21
+ })),
22
+ acceptedAgentCount: complete
23
+ ? observation.data.acceptedSpawns.length
24
+ : null,
25
+ directAcceptedAgentCount: observation.data.acceptedSpawns.length,
26
+ coverage: {
27
+ nativeCalls: observation.completeness,
28
+ execInspection: inspectedExecCalls(observation)
29
+ ? "complete"
30
+ : "partial",
31
+ acceptedAgents: "direct_native_calls",
32
+ },
33
+ ...nativeCounts(observation),
34
+ ...execEvidence(observation.data.execCalls),
35
+ },
36
+ };
37
+ }
38
+ function nativeCounts(observation) {
39
+ const complete = observation.completeness === "complete";
40
+ return {
41
+ submittedExecCalls: complete ? observation.data.submittedExecCalls : null,
42
+ truncated: complete ? false : null,
43
+ };
44
+ }
45
+ function controlCompleteness(source, complete) {
46
+ return source === "complete" && !complete ? "partial" : source;
47
+ }
48
+ function execEvidence(receipts) {
49
+ return receipts ? { execCalls: receipts } : {};
50
+ }
51
+ function inspectedExecCalls(observation) {
52
+ const receipts = observation.data.execCalls ?? [];
53
+ const submissions = observation.data.toolCalls.filter((call) => call.name === "exec");
54
+ if (submissions.length !== observation.data.submittedExecCalls ||
55
+ receipts.length !== submissions.length)
56
+ return false;
57
+ return receipts.every((receipt, index) => receipt.completeness === "complete" &&
58
+ receipt.ordinal === submissions[index]?.ordinal);
59
+ }
60
+ const MALFORMED = Symbol("malformed native control event");
61
+ function assistantBlocks(event) {
62
+ if (event === MALFORMED)
63
+ return MALFORMED;
64
+ if (!assistantEvent(event))
65
+ return null;
66
+ const content = isRecord(event.message) ? event.message.content : null;
67
+ return isUnknownArray(content) ? content : MALFORMED;
68
+ }
69
+ function nativeBlock(value) {
70
+ const types = ["text", "thinking", "redacted_thinking", "tool_use"];
71
+ return isRecord(value) && types.includes(String(value.type)) ? value : null;
72
+ }
73
+ function toolIdentity(block, ids) {
74
+ if (!label(block.id, 128) || !label(block.name, 256) || ids.has(block.id))
75
+ return null;
76
+ return { id: block.id, name: block.name };
77
+ }
78
+ class ClaudeNativeControls {
79
+ calls = [];
80
+ ids = new Set();
81
+ malformed = false;
82
+ truncated = false;
83
+ parse(stream) {
84
+ for (const line of stream.lines) {
85
+ if (!this.observeEvent(line.parsed ? line.value : MALFORMED))
86
+ break;
87
+ }
88
+ }
89
+ observeEvent(event) {
90
+ const blocks = assistantBlocks(event);
91
+ if (blocks === null)
92
+ return true;
93
+ if (blocks === MALFORMED) {
94
+ this.malformed = true;
95
+ return true;
96
+ }
97
+ for (const block of blocks)
98
+ if (!this.observeBlock(block))
99
+ return false;
100
+ return true;
101
+ }
102
+ observeBlock(value) {
103
+ const block = nativeBlock(value);
104
+ if (!block) {
105
+ this.malformed = true;
106
+ return true;
107
+ }
108
+ if (block.type !== "tool_use")
109
+ return true;
110
+ const identity = toolIdentity(block, this.ids);
111
+ if (!identity) {
112
+ this.malformed = true;
113
+ return true;
114
+ }
115
+ this.ids.add(identity.id);
116
+ if (this.calls.length >= MAX_CALLS) {
117
+ this.truncated = true;
118
+ return false;
119
+ }
120
+ this.calls.push({
121
+ ordinal: this.calls.length,
122
+ namespace: "claude",
123
+ name: identity.name,
124
+ });
125
+ return true;
126
+ }
127
+ }
128
+ /** Observe all native labels without retaining arguments or interpreting policy. */
129
+ export function claudeNativeControls(stream, exitCode) {
130
+ const events = parseClaudeEvents(stream);
131
+ const summary = summarizeClaudeEvents(events, exitCode);
132
+ const controls = new ClaudeNativeControls();
133
+ controls.parse(events);
134
+ return {
135
+ id: "sevro.host.native-controls",
136
+ completeness: summary.complete && !controls.malformed && !controls.truncated
137
+ ? "complete"
138
+ : "partial",
139
+ data: {
140
+ method: "native_control_calls",
141
+ calls: controls.calls,
142
+ acceptedAgentCount: null,
143
+ submittedExecCalls: null,
144
+ truncated: controls.truncated,
145
+ },
146
+ };
147
+ }
148
+ function assistantEvent(value) {
149
+ return isRecord(value) && value.type === "assistant";
150
+ }
151
+ //# sourceMappingURL=native-controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-controls.js","sourceRoot":"","sources":["native-controls.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRecord, isUnknownArray } from \"../value-guards\";\nimport {\n summarizeClaudeEvents,\n parseClaudeEvents,\n type ClaudeEvents,\n} from \"./claude-events\";\nimport type { codexNativeCallObservation } from \"./codex-native-calls\";\nimport type { ExecCallReceipt } from \"./codex-exec-controls\";\n\nconst MAX_CALLS = 128;\ntype Call = { ordinal: number; namespace: string; name: string };\ntype Entry = Record<string, unknown>;\n\nexport interface NativeControlObservation {\n id: \"sevro.host.native-controls\";\n completeness: \"complete\" | \"partial\" | \"unavailable\";\n data: {\n method: \"native_control_calls\";\n calls: Call[];\n acceptedAgentCount: number | null;\n directAcceptedAgentCount?: number;\n coverage?: {\n nativeCalls: NativeControlObservation[\"completeness\"];\n execInspection: \"complete\" | \"partial\";\n acceptedAgents: \"direct_native_calls\";\n };\n submittedExecCalls: number | null;\n execCalls?: ExecCallReceipt[];\n truncated: boolean | null;\n };\n}\n\nfunction label(value: unknown, max: number): value is string {\n return (\n typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= max &&\n /^[A-Za-z0-9_.:-]+$/.test(value)\n );\n}\n\nexport function codexNativeControls(\n observation: Awaited<ReturnType<typeof codexNativeCallObservation>>,\n): NativeControlObservation {\n const complete =\n observation.completeness === \"complete\" && inspectedExecCalls(observation);\n return {\n id: \"sevro.host.native-controls\",\n completeness: controlCompleteness(observation.completeness, complete),\n data: {\n method: \"native_control_calls\",\n calls: observation.data.toolCalls.map(({ ordinal, namespace, name }) => ({\n ordinal,\n namespace,\n name,\n })),\n acceptedAgentCount: complete\n ? observation.data.acceptedSpawns.length\n : null,\n directAcceptedAgentCount: observation.data.acceptedSpawns.length,\n coverage: {\n nativeCalls: observation.completeness,\n execInspection: inspectedExecCalls(observation)\n ? \"complete\"\n : \"partial\",\n acceptedAgents: \"direct_native_calls\",\n },\n ...nativeCounts(observation),\n ...execEvidence(observation.data.execCalls),\n },\n };\n}\nfunction nativeCounts(\n observation: Awaited<ReturnType<typeof codexNativeCallObservation>>,\n) {\n const complete = observation.completeness === \"complete\";\n return {\n submittedExecCalls: complete ? observation.data.submittedExecCalls : null,\n truncated: complete ? false : null,\n };\n}\nfunction controlCompleteness(\n source: NativeControlObservation[\"completeness\"],\n complete: boolean,\n): NativeControlObservation[\"completeness\"] {\n return source === \"complete\" && !complete ? \"partial\" : source;\n}\nfunction execEvidence(receipts: ExecCallReceipt[] | undefined) {\n return receipts ? { execCalls: receipts } : {};\n}\n\nfunction inspectedExecCalls(\n observation: Awaited<ReturnType<typeof codexNativeCallObservation>>,\n): boolean {\n const receipts = observation.data.execCalls ?? [];\n const submissions = observation.data.toolCalls.filter(\n (call) => call.name === \"exec\",\n );\n if (\n submissions.length !== observation.data.submittedExecCalls ||\n receipts.length !== submissions.length\n )\n return false;\n return receipts.every(\n (receipt, index) =>\n receipt.completeness === \"complete\" &&\n receipt.ordinal === submissions[index]?.ordinal,\n );\n}\n\nconst MALFORMED = Symbol(\"malformed native control event\");\n\nfunction assistantBlocks(event: unknown): unknown[] | null | typeof MALFORMED {\n if (event === MALFORMED) return MALFORMED;\n if (!assistantEvent(event)) return null;\n const content = isRecord(event.message) ? event.message.content : null;\n return isUnknownArray(content) ? content : MALFORMED;\n}\n\nfunction nativeBlock(value: unknown): Entry | null {\n const types = [\"text\", \"thinking\", \"redacted_thinking\", \"tool_use\"];\n return isRecord(value) && types.includes(String(value.type)) ? value : null;\n}\n\nfunction toolIdentity(\n block: Entry,\n ids: Set<string>,\n): { id: string; name: string } | null {\n if (!label(block.id, 128) || !label(block.name, 256) || ids.has(block.id))\n return null;\n return { id: block.id, name: block.name };\n}\n\nclass ClaudeNativeControls {\n readonly calls: Call[] = [];\n readonly ids = new Set<string>();\n malformed = false;\n truncated = false;\n\n parse(stream: ClaudeEvents): void {\n for (const line of stream.lines) {\n if (!this.observeEvent(line.parsed ? line.value : MALFORMED)) break;\n }\n }\n\n private observeEvent(event: unknown): boolean {\n const blocks = assistantBlocks(event);\n if (blocks === null) return true;\n if (blocks === MALFORMED) {\n this.malformed = true;\n return true;\n }\n for (const block of blocks) if (!this.observeBlock(block)) return false;\n return true;\n }\n\n private observeBlock(value: unknown): boolean {\n const block = nativeBlock(value);\n if (!block) {\n this.malformed = true;\n return true;\n }\n if (block.type !== \"tool_use\") return true;\n const identity = toolIdentity(block, this.ids);\n if (!identity) {\n this.malformed = true;\n return true;\n }\n this.ids.add(identity.id);\n if (this.calls.length >= MAX_CALLS) {\n this.truncated = true;\n return false;\n }\n this.calls.push({\n ordinal: this.calls.length,\n namespace: \"claude\",\n name: identity.name,\n });\n return true;\n }\n}\n\n/** Observe all native labels without retaining arguments or interpreting policy. */\nexport function claudeNativeControls(\n stream: string | ClaudeEvents,\n exitCode: number,\n): NativeControlObservation {\n const events = parseClaudeEvents(stream);\n const summary = summarizeClaudeEvents(events, exitCode);\n const controls = new ClaudeNativeControls();\n controls.parse(events);\n return {\n id: \"sevro.host.native-controls\",\n completeness:\n summary.complete && !controls.malformed && !controls.truncated\n ? \"complete\"\n : \"partial\",\n data: {\n method: \"native_control_calls\",\n calls: controls.calls,\n acceptedAgentCount: null,\n submittedExecCalls: null,\n truncated: controls.truncated,\n },\n };\n}\n\nfunction assistantEvent(value: unknown): value is Entry {\n return isRecord(value) && value.type === \"assistant\";\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { join } from "node:path";
2
+ import { requireCandidateRuntime, requireCandidateArtifactRoots, } from "../candidate-artifacts.js";
3
+ import { requireNativeTranscriptView } from "../native-transcript-state.js";
4
+ import { isOptionalNonblankString } from "../value-guards.js";
5
+ export const MAX_HOST_TIMEOUT_MS = 30 * 60_000;
6
+ export function validHostTimeout(timeoutMs) {
7
+ return (Number.isSafeInteger(timeoutMs) &&
8
+ timeoutMs >= 1 &&
9
+ timeoutMs <= MAX_HOST_TIMEOUT_MS);
10
+ }
11
+ /** Common admission checks run before native state or processes are allocated. */
12
+ export async function validateNativeHostRequest(request, host) {
13
+ requireFollowUp(request.followUpPrompt, host);
14
+ await requireNativeTranscriptView(request.candidateTranscriptRoot);
15
+ await requireCandidateRuntime(request.candidateRuntimeRoot, request.runtimeRole);
16
+ await requireCandidateArtifactRoots(request.candidateArtifactRoots);
17
+ requireFixtureBin(request);
18
+ requirePassiveRequest(request, host);
19
+ }
20
+ function requireFollowUp(prompt, host) {
21
+ if (!isOptionalNonblankString(prompt))
22
+ throw new Error(`${host} follow-up prompt must be nonempty`);
23
+ }
24
+ function requireFixtureBin(request) {
25
+ if (request.fixtureBinDir !== undefined &&
26
+ request.fixtureBinDir !== join(request.workspace, ".git", "fixture-bin"))
27
+ throw new Error("fixture binary path is outside the workspace");
28
+ }
29
+ function requirePassiveRequest(request, host) {
30
+ if (request.instrumentation?.length || request.condition !== "passive")
31
+ throw new Error(`${host} enforcement instrumentation is unavailable`);
32
+ }
33
+ //# sourceMappingURL=native-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-request.js","sourceRoot":"","sources":["native-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,kBAAkB;AAEjC,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAC9B,kCAA+B;AAChC,OAAO,EAAE,2BAA2B,EAAE,sCAAmC;AACzE,OAAO,EAAE,wBAAwB,EAAE,2BAAwB;AAE3D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,MAAM,CAAC;AAE/C,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,OAAO,CACL,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC;QAC/B,SAAS,IAAI,CAAC;QACd,SAAS,IAAI,mBAAmB,CACjC,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAA0C,EAC1C,IAAwB;IAExB,eAAe,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,2BAA2B,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACnE,MAAM,uBAAuB,CAC3B,OAAO,CAAC,oBAAoB,EAC5B,OAAO,CAAC,WAAW,CACpB,CAAC;IACF,MAAM,6BAA6B,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACpE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC3B,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,eAAe,CAAC,MAA0B,EAAE,IAAY;IAC/D,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oCAAoC,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,iBAAiB,CAAC,OAA0C;IACnE,IACE,OAAO,CAAC,aAAa,KAAK,SAAS;QACnC,OAAO,CAAC,aAAa,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC;QAExE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,qBAAqB,CAC5B,OAA0C,EAC1C,IAAY;IAEZ,IAAI,OAAO,CAAC,eAAe,EAAE,MAAM,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;QACpE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6CAA6C,CAAC,CAAC;AAC1E,CAAC","sourcesContent":["import { join } from \"node:path\";\nimport type { HostAdapter } from \"../engine\";\nimport {\n requireCandidateRuntime,\n requireCandidateArtifactRoots,\n} from \"../candidate-artifacts\";\nimport { requireNativeTranscriptView } from \"../native-transcript-state\";\nimport { isOptionalNonblankString } from \"../value-guards\";\n\nexport const MAX_HOST_TIMEOUT_MS = 30 * 60_000;\n\nexport function validHostTimeout(timeoutMs: number): boolean {\n return (\n Number.isSafeInteger(timeoutMs) &&\n timeoutMs >= 1 &&\n timeoutMs <= MAX_HOST_TIMEOUT_MS\n );\n}\n\n/** Common admission checks run before native state or processes are allocated. */\nexport async function validateNativeHostRequest(\n request: Parameters<HostAdapter[\"run\"]>[0],\n host: \"Codex\" | \"Claude\",\n): Promise<void> {\n requireFollowUp(request.followUpPrompt, host);\n await requireNativeTranscriptView(request.candidateTranscriptRoot);\n await requireCandidateRuntime(\n request.candidateRuntimeRoot,\n request.runtimeRole,\n );\n await requireCandidateArtifactRoots(request.candidateArtifactRoots);\n requireFixtureBin(request);\n requirePassiveRequest(request, host);\n}\n\nfunction requireFollowUp(prompt: string | undefined, host: string): void {\n if (!isOptionalNonblankString(prompt))\n throw new Error(`${host} follow-up prompt must be nonempty`);\n}\n\nfunction requireFixtureBin(request: Parameters<HostAdapter[\"run\"]>[0]): void {\n if (\n request.fixtureBinDir !== undefined &&\n request.fixtureBinDir !== join(request.workspace, \".git\", \"fixture-bin\")\n )\n throw new Error(\"fixture binary path is outside the workspace\");\n}\n\nfunction requirePassiveRequest(\n request: Parameters<HostAdapter[\"run\"]>[0],\n host: string,\n): void {\n if (request.instrumentation?.length || request.condition !== \"passive\")\n throw new Error(`${host} enforcement instrumentation is unavailable`);\n}\n"]}
@@ -0,0 +1,167 @@
1
+ import { spawn } from "../runtime-process.js";
2
+ import { validHostTimeout } from "./native-request.js";
3
+ import { ProcessDeadline } from "../process-lifecycle.js";
4
+ import { captureBoundedBytes, captureBoundedText, terminateOwnedProcessGroup, } from "../process-lifecycle.js";
5
+ import { HostExecutionFailure } from "../evaluation/types.js";
6
+ import { HostProcessDiagnostics, } from "../host-diagnostics.js";
7
+ export const MAX_STDERR_BYTES = 64 * 1024;
8
+ export const MAX_EVENT_BYTES = 8 * 1024 * 1024;
9
+ class HostProcessInterruption extends Error {
10
+ reason;
11
+ constructor(host, reason) {
12
+ super(`${host} run ${reason === "timeout" ? "timed out" : "cancelled"}`);
13
+ this.reason = reason;
14
+ }
15
+ }
16
+ /** Race completion against the host deadline and cancellation, then release listeners. */
17
+ async function awaitHostProcess(proc, completed, options) {
18
+ const deadline = new ProcessDeadline({
19
+ ...options,
20
+ interruption: (reason) => new HostProcessInterruption(options.host, reason),
21
+ });
22
+ try {
23
+ return await deadline.wait(completed);
24
+ }
25
+ catch (error) {
26
+ terminateOwnedProcessGroup(proc);
27
+ await proc.exited;
28
+ throw error;
29
+ }
30
+ finally {
31
+ deadline.dispose();
32
+ }
33
+ }
34
+ export function hostFailureMessage(result, host) {
35
+ const failure = result.processFailure;
36
+ if (!failure)
37
+ return undefined;
38
+ const labels = {
39
+ "isolation-preflight": `${host} isolation preflight`,
40
+ "executable-preflight": `${host} executable preflight`,
41
+ execution: `${host} run`,
42
+ };
43
+ if (!Object.hasOwn(labels, failure.phase))
44
+ return undefined;
45
+ return `${labels[failure.phase]} ${failureSuffix(failure)}`;
46
+ }
47
+ function failureSuffix(failure) {
48
+ if (failure.reason === "timeout" && validHostTimeout(failure.timeoutMs))
49
+ return `timed out after ${failure.timeoutMs / 1000} s`;
50
+ return failure.reason === "cancelled" ? "cancelled" : "failed";
51
+ }
52
+ export class HostProcessFailure extends HostExecutionFailure {
53
+ constructor(host, phase, process, reason = "exit", timeoutMs = 60_000) {
54
+ const failure = { phase, reason, timeoutMs };
55
+ const result = {
56
+ finalMessage: null,
57
+ complete: false,
58
+ executionFailed: true,
59
+ actualCondition: "passive",
60
+ processFailure: failure,
61
+ artifacts: [
62
+ {
63
+ id: `${host.toLowerCase()}.process`,
64
+ bytes: Buffer.from(JSON.stringify({
65
+ ...failure,
66
+ code: process.code,
67
+ signal: process.signal,
68
+ durationMs: process.durationMs,
69
+ stderrTruncated: process.stderrTruncated,
70
+ })),
71
+ },
72
+ { id: `${host.toLowerCase()}.stderr`, bytes: process.stderr },
73
+ ],
74
+ };
75
+ super(hostFailureMessage(result, host), result);
76
+ }
77
+ }
78
+ async function writeHostInput(proc, input) {
79
+ if (input === undefined)
80
+ return;
81
+ if (!proc.stdin || typeof proc.stdin === "number")
82
+ throw new Error("Codex input pipe unavailable");
83
+ await proc.stdin.write(input);
84
+ await proc.stdin.end();
85
+ }
86
+ function spawnHostProcess(options, capture) {
87
+ try {
88
+ return spawn(options.argv, {
89
+ cwd: options.cwd,
90
+ env: options.env,
91
+ detached: process.platform !== "win32",
92
+ stdin: options.input === undefined ? "ignore" : "pipe",
93
+ stdout: "pipe",
94
+ stderr: "pipe",
95
+ });
96
+ }
97
+ catch (cause) {
98
+ capture.recordUnstarted();
99
+ throw cause;
100
+ }
101
+ }
102
+ function processPhase(options) {
103
+ return options.phase ?? "execution";
104
+ }
105
+ function requireHostProcess(options) {
106
+ if (options.signal?.aborted)
107
+ throw new Error(`${options.host} run cancelled`);
108
+ }
109
+ function hostCompletion(proc, options, capture) {
110
+ const started = performance.now();
111
+ const stdout = captureBoundedText(proc.stdout, {
112
+ limit: MAX_EVENT_BYTES,
113
+ overflow: "reject",
114
+ exceeded: () => new Error(`${options.host} event stream exceeds the size limit`),
115
+ observe: (bytes) => {
116
+ capture.stdout.add(bytes);
117
+ },
118
+ });
119
+ const stderr = captureBoundedBytes(proc.stderr, {
120
+ limit: MAX_STDERR_BYTES,
121
+ overflow: "truncate",
122
+ cancelOnError: true,
123
+ observe: (bytes) => {
124
+ capture.stderr.add(bytes);
125
+ },
126
+ });
127
+ const input = writeHostInput(proc, options.input);
128
+ const completed = Promise.all([stdout, proc.exited, stderr, input]).then(([out, code, err]) => ({
129
+ out,
130
+ code,
131
+ stderr: err.bytes,
132
+ stderrTruncated: err.truncated,
133
+ signal: proc.signalCode ?? null,
134
+ durationMs: performance.now() - started,
135
+ }));
136
+ return { completed, drains: [stdout, stderr, input], started };
137
+ }
138
+ export async function runHostProcess(options) {
139
+ requireHostProcess(options);
140
+ const capture = new HostProcessDiagnostics(options.diagnostics);
141
+ const proc = spawnHostProcess(options, capture);
142
+ const { completed, drains, started } = hostCompletion(proc, options, capture);
143
+ try {
144
+ const result = await awaitHostProcess(proc, completed, {
145
+ ...options,
146
+ });
147
+ await capture.record(proc);
148
+ return result;
149
+ }
150
+ catch (cause) {
151
+ await Promise.allSettled(drains);
152
+ if (!(cause instanceof HostProcessInterruption)) {
153
+ await capture.record(proc, "error");
154
+ throw cause;
155
+ }
156
+ await capture.record(proc, cause.reason);
157
+ throw new HostProcessFailure(options.host, processPhase(options), {
158
+ code: await proc.exited,
159
+ out: capture.stdout.bytes().toString("utf8"),
160
+ stderr: capture.stderr.bytes(),
161
+ stderrTruncated: capture.stderr.truncated,
162
+ signal: proc.signalCode ?? null,
163
+ durationMs: performance.now() - started,
164
+ }, cause.reason, options.timeoutMs);
165
+ }
166
+ }
167
+ //# sourceMappingURL=process-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-lifecycle.js","sourceRoot":"","sources":["process-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, type Subprocess } from \"../runtime-process\";\nimport { validHostTimeout } from \"./native-request\";\nimport { ProcessDeadline } from \"../process-lifecycle\";\nimport {\n captureBoundedBytes,\n captureBoundedText,\n terminateOwnedProcessGroup,\n} from \"../process-lifecycle\";\nimport { HostExecutionFailure, type HostResult } from \"../evaluation/types\";\nimport {\n HostProcessDiagnostics,\n type HostDiagnostics,\n} from \"../host-diagnostics\";\n\nexport const MAX_STDERR_BYTES = 64 * 1024;\nexport const MAX_EVENT_BYTES = 8 * 1024 * 1024;\nclass HostProcessInterruption extends Error {\n constructor(\n host: string,\n readonly reason: \"timeout\" | \"cancelled\",\n ) {\n super(`${host} run ${reason === \"timeout\" ? \"timed out\" : \"cancelled\"}`);\n }\n}\n\n/** Race completion against the host deadline and cancellation, then release listeners. */\nasync function awaitHostProcess<T>(\n proc: Subprocess,\n completed: Promise<T>,\n options: HostProcessOptions,\n): Promise<T> {\n const deadline = new ProcessDeadline({\n ...options,\n interruption: (reason) => new HostProcessInterruption(options.host, reason),\n });\n try {\n return await deadline.wait(completed);\n } catch (error) {\n terminateOwnedProcessGroup(proc);\n await proc.exited;\n throw error;\n } finally {\n deadline.dispose();\n }\n}\n\nexport type Phase = NonNullable<HostResult[\"processFailure\"]>[\"phase\"];\nexport type HostProcessResult = {\n code: number;\n out: string;\n stderr: Uint8Array;\n stderrTruncated: boolean;\n signal: string | null;\n durationMs: number;\n};\n\nexport function hostFailureMessage(\n result: HostResult,\n host: string,\n): string | undefined {\n const failure = result.processFailure;\n if (!failure) return undefined;\n const labels = {\n \"isolation-preflight\": `${host} isolation preflight`,\n \"executable-preflight\": `${host} executable preflight`,\n execution: `${host} run`,\n };\n if (!Object.hasOwn(labels, failure.phase)) return undefined;\n return `${labels[failure.phase]} ${failureSuffix(failure)}`;\n}\n\nfunction failureSuffix(\n failure: NonNullable<HostResult[\"processFailure\"]>,\n): string {\n if (failure.reason === \"timeout\" && validHostTimeout(failure.timeoutMs))\n return `timed out after ${failure.timeoutMs / 1000} s`;\n return failure.reason === \"cancelled\" ? \"cancelled\" : \"failed\";\n}\n\nexport class HostProcessFailure extends HostExecutionFailure {\n constructor(\n host: \"Codex\" | \"Claude\",\n phase: Phase,\n process: HostProcessResult,\n reason: NonNullable<HostResult[\"processFailure\"]>[\"reason\"] = \"exit\",\n timeoutMs = 60_000,\n ) {\n const failure = { phase, reason, timeoutMs };\n const result: HostResult = {\n finalMessage: null,\n complete: false,\n executionFailed: true,\n actualCondition: \"passive\",\n processFailure: failure,\n artifacts: [\n {\n id: `${host.toLowerCase()}.process`,\n bytes: Buffer.from(\n JSON.stringify({\n ...failure,\n code: process.code,\n signal: process.signal,\n durationMs: process.durationMs,\n stderrTruncated: process.stderrTruncated,\n }),\n ),\n },\n { id: `${host.toLowerCase()}.stderr`, bytes: process.stderr },\n ],\n };\n super(hostFailureMessage(result, host), result);\n }\n}\n\nexport interface HostProcessOptions {\n host: \"Codex\" | \"Claude\";\n diagnostics: HostDiagnostics;\n argv: string[];\n cwd: string;\n env: Record<string, string>;\n input?: string;\n timeoutMs: number;\n signal?: AbortSignal;\n phase?: Phase;\n}\n\nasync function writeHostInput(proc: Subprocess, input?: string): Promise<void> {\n if (input === undefined) return;\n if (!proc.stdin || typeof proc.stdin === \"number\")\n throw new Error(\"Codex input pipe unavailable\");\n await proc.stdin.write(input);\n await proc.stdin.end();\n}\n\nfunction spawnHostProcess(\n options: HostProcessOptions,\n capture: HostProcessDiagnostics,\n) {\n try {\n return spawn(options.argv, {\n cwd: options.cwd,\n env: options.env,\n detached: process.platform !== \"win32\",\n stdin: options.input === undefined ? \"ignore\" : \"pipe\",\n stdout: \"pipe\",\n stderr: \"pipe\",\n });\n } catch (cause) {\n capture.recordUnstarted();\n throw cause;\n }\n}\n\nfunction processPhase(options: HostProcessOptions): Phase {\n return options.phase ?? \"execution\";\n}\n\nfunction requireHostProcess(options: HostProcessOptions): void {\n if (options.signal?.aborted) throw new Error(`${options.host} run cancelled`);\n}\n\nfunction hostCompletion(\n proc: ReturnType<typeof spawnHostProcess>,\n options: HostProcessOptions,\n capture: HostProcessDiagnostics,\n) {\n const started = performance.now();\n const stdout = captureBoundedText(proc.stdout, {\n limit: MAX_EVENT_BYTES,\n overflow: \"reject\",\n exceeded: () =>\n new Error(`${options.host} event stream exceeds the size limit`),\n observe: (bytes) => {\n capture.stdout.add(bytes);\n },\n });\n const stderr = captureBoundedBytes(proc.stderr, {\n limit: MAX_STDERR_BYTES,\n overflow: \"truncate\",\n cancelOnError: true,\n observe: (bytes) => {\n capture.stderr.add(bytes);\n },\n });\n const input = writeHostInput(proc, options.input);\n const completed = Promise.all([stdout, proc.exited, stderr, input]).then(\n ([out, code, err]) => ({\n out,\n code,\n stderr: err.bytes,\n stderrTruncated: err.truncated,\n signal: proc.signalCode ?? null,\n durationMs: performance.now() - started,\n }),\n );\n return { completed, drains: [stdout, stderr, input], started };\n}\n\nexport async function runHostProcess(\n options: HostProcessOptions,\n): Promise<HostProcessResult> {\n requireHostProcess(options);\n const capture = new HostProcessDiagnostics(options.diagnostics);\n const proc = spawnHostProcess(options, capture);\n const { completed, drains, started } = hostCompletion(proc, options, capture);\n try {\n const result = await awaitHostProcess(proc, completed, {\n ...options,\n });\n await capture.record(proc);\n return result;\n } catch (cause) {\n await Promise.allSettled(drains);\n if (!(cause instanceof HostProcessInterruption)) {\n await capture.record(proc, \"error\");\n throw cause;\n }\n await capture.record(proc, cause.reason);\n throw new HostProcessFailure(\n options.host,\n processPhase(options),\n {\n code: await proc.exited,\n out: capture.stdout.bytes().toString(\"utf8\"),\n stderr: capture.stderr.bytes(),\n stderrTruncated: capture.stderr.truncated,\n signal: proc.signalCode ?? null,\n durationMs: performance.now() - started,\n },\n cause.reason,\n options.timeoutMs,\n );\n }\n}\n"]}
@@ -0,0 +1,81 @@
1
+ import { spawnSync } from "../runtime-process.js";
2
+ import { isMissingFile } from "../fixtures/tools.js";
3
+ import { lstat, readdir, realpath, stat } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { dirname, isAbsolute, join } from "node:path";
6
+ async function containingRepository(root) {
7
+ let directory = (await stat(root)).isDirectory() ? root : dirname(root);
8
+ for (;;) {
9
+ if (await hasRepositoryMetadata(directory))
10
+ return directory;
11
+ const parent = dirname(directory);
12
+ if (parent === directory)
13
+ return null;
14
+ directory = parent;
15
+ }
16
+ }
17
+ async function hasRepositoryMetadata(directory) {
18
+ try {
19
+ const path = join(directory, ".git");
20
+ const metadata = await lstat(path);
21
+ return !(await emptyTemporaryGitPlaceholder(directory, path, metadata));
22
+ }
23
+ catch (cause) {
24
+ if (isMissingFile(cause))
25
+ return false;
26
+ throw new Error("protected repository metadata is unreadable", { cause });
27
+ }
28
+ }
29
+ async function emptyTemporaryGitPlaceholder(directory, path, metadata) {
30
+ // Codex's Linux sandbox can leave an empty mount placeholder at /tmp/.git.
31
+ return (process.platform === "linux" &&
32
+ (directory === "/tmp" || directory === tmpdir()) &&
33
+ metadata.isDirectory() &&
34
+ (await readdir(path)).length === 0);
35
+ }
36
+ /** Protect the primary checkout and linked worktrees without requiring Git. */
37
+ export async function protectedWorktrees(root) {
38
+ const repository = await containingRepository(root);
39
+ if (!repository)
40
+ return [];
41
+ const paths = listWorktreePaths(repository);
42
+ const existing = [];
43
+ for (const path of paths) {
44
+ try {
45
+ existing.push(await realpath(path));
46
+ }
47
+ catch (error) {
48
+ if (!isMissingFile(error))
49
+ throw new Error("protected repository worktree is unreadable", {
50
+ cause: error,
51
+ });
52
+ }
53
+ }
54
+ return existing;
55
+ }
56
+ function listWorktreePaths(repository) {
57
+ const result = spawnSync(["git", "-C", repository, "worktree", "list", "--porcelain", "-z"], {
58
+ env: {
59
+ PATH: process.env.PATH ?? "/usr/bin:/bin",
60
+ LC_ALL: "C",
61
+ GIT_CONFIG_NOSYSTEM: "1",
62
+ GIT_CONFIG_GLOBAL: "/dev/null",
63
+ },
64
+ stdout: "pipe",
65
+ stderr: "ignore",
66
+ });
67
+ if (result.exitCode !== 0 || result.stdout.byteLength > 1024 * 1024)
68
+ throw new Error("protected repository worktrees are unavailable");
69
+ const paths = result.stdout
70
+ .toString()
71
+ .split("\0")
72
+ .filter((field) => field.startsWith("worktree "))
73
+ .map((field) => field.slice(9));
74
+ validateWorktreePaths(paths);
75
+ return paths;
76
+ }
77
+ function validateWorktreePaths(paths) {
78
+ if (!paths.length || paths.some((path) => !isAbsolute(path)))
79
+ throw new Error("protected repository worktree paths are invalid");
80
+ }
81
+ //# sourceMappingURL=protected-worktrees.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protected-worktrees.js","sourceRoot":"","sources":["protected-worktrees.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"../runtime-process\";\nimport { isMissingFile } from \"../fixtures/tools\";\nimport { lstat, readdir, realpath, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, isAbsolute, join } from \"node:path\";\n\nasync function containingRepository(root: string): Promise<string | null> {\n let directory = (await stat(root)).isDirectory() ? root : dirname(root);\n for (;;) {\n if (await hasRepositoryMetadata(directory)) return directory;\n const parent = dirname(directory);\n if (parent === directory) return null;\n directory = parent;\n }\n}\n\nasync function hasRepositoryMetadata(directory: string): Promise<boolean> {\n try {\n const path = join(directory, \".git\");\n const metadata = await lstat(path);\n return !(await emptyTemporaryGitPlaceholder(directory, path, metadata));\n } catch (cause) {\n if (isMissingFile(cause)) return false;\n throw new Error(\"protected repository metadata is unreadable\", { cause });\n }\n}\n\nasync function emptyTemporaryGitPlaceholder(\n directory: string,\n path: string,\n metadata: Awaited<ReturnType<typeof lstat>>,\n): Promise<boolean> {\n // Codex's Linux sandbox can leave an empty mount placeholder at /tmp/.git.\n return (\n process.platform === \"linux\" &&\n (directory === \"/tmp\" || directory === tmpdir()) &&\n metadata.isDirectory() &&\n (await readdir(path)).length === 0\n );\n}\n\n/** Protect the primary checkout and linked worktrees without requiring Git. */\nexport async function protectedWorktrees(root: string): Promise<string[]> {\n const repository = await containingRepository(root);\n if (!repository) return [];\n const paths = listWorktreePaths(repository);\n const existing: string[] = [];\n for (const path of paths) {\n try {\n existing.push(await realpath(path));\n } catch (error) {\n if (!isMissingFile(error))\n throw new Error(\"protected repository worktree is unreadable\", {\n cause: error,\n });\n }\n }\n return existing;\n}\n\nfunction listWorktreePaths(repository: string): string[] {\n const result = spawnSync(\n [\"git\", \"-C\", repository, \"worktree\", \"list\", \"--porcelain\", \"-z\"],\n {\n env: {\n PATH: process.env.PATH ?? \"/usr/bin:/bin\",\n LC_ALL: \"C\",\n GIT_CONFIG_NOSYSTEM: \"1\",\n GIT_CONFIG_GLOBAL: \"/dev/null\",\n },\n stdout: \"pipe\",\n stderr: \"ignore\",\n },\n );\n if (result.exitCode !== 0 || result.stdout.byteLength > 1024 * 1024)\n throw new Error(\"protected repository worktrees are unavailable\");\n const paths = result.stdout\n .toString()\n .split(\"\\0\")\n .filter((field) => field.startsWith(\"worktree \"))\n .map((field) => field.slice(9));\n validateWorktreePaths(paths);\n return paths;\n}\n\nfunction validateWorktreePaths(paths: string[]): void {\n if (!paths.length || paths.some((path) => !isAbsolute(path)))\n throw new Error(\"protected repository worktree paths are invalid\");\n}\n"]}
@@ -0,0 +1,100 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstat, readFile, readdir, realpath } from "node:fs/promises";
3
+ import { join, relative, resolve } from "node:path";
4
+ import { isRecord } from "../value-guards.js";
5
+ const MAX_SKILLS = 128;
6
+ const MAX_SKILL_BYTES = 1024 * 1024;
7
+ const MAX_CATALOG_BYTES = 8 * 1024 * 1024;
8
+ export async function codexSkillCatalogRoots(workspace, pluginRoots, nativeSkillRoots) {
9
+ const roots = [
10
+ { directory: join(workspace, ".agents/skills"), boundary: workspace },
11
+ ...nativeSkillRoots,
12
+ ];
13
+ for (const root of pluginRoots) {
14
+ const manifest = JSON.parse(await readFile(join(root, ".claude-plugin/plugin.json"), "utf8"));
15
+ if (!isRecord(manifest) || typeof manifest.name !== "string")
16
+ throw new Error("invalid skill catalog plugin manifest");
17
+ roots.push({
18
+ directory: join(root, "skills"),
19
+ boundary: root,
20
+ namespace: manifest.name,
21
+ });
22
+ }
23
+ return roots;
24
+ }
25
+ /** Snapshot only known skill mounts; shell syntax does not enter discovery. */
26
+ export async function skillHookCatalog(roots) {
27
+ const catalog = { skills: [], complete: true, bytes: 0 };
28
+ for (const root of roots)
29
+ await readRoot(root, catalog);
30
+ return { skills: catalog.skills, catalogComplete: catalog.complete };
31
+ }
32
+ async function readRoot(root, catalog) {
33
+ try {
34
+ await requireCatalogRoot(root);
35
+ const entries = await readdir(root.directory, { withFileTypes: true });
36
+ const directories = catalogDirectories(entries, catalog);
37
+ for (const entry of directories.sort((a, b) => a.name.localeCompare(b.name))) {
38
+ await readSkill(root, entry.name, catalog);
39
+ }
40
+ }
41
+ catch (cause) {
42
+ if (!missing(cause))
43
+ catalog.complete = false;
44
+ }
45
+ }
46
+ function catalogDirectories(entries, catalog) {
47
+ const directories = [];
48
+ for (const entry of entries) {
49
+ if (entry.isSymbolicLink())
50
+ catalog.complete = false;
51
+ if (entry.isDirectory() && /^[A-Za-z0-9._-]+$/.test(entry.name))
52
+ directories.push(entry);
53
+ }
54
+ return directories;
55
+ }
56
+ async function requireCatalogRoot(root) {
57
+ const expected = resolve(await realpath(root.boundary), relative(root.boundary, root.directory));
58
+ if ((await realpath(root.directory)) !== expected)
59
+ throw new Error("redirected skill catalog root");
60
+ }
61
+ function missing(cause) {
62
+ return cause instanceof Error && "code" in cause && cause.code === "ENOENT";
63
+ }
64
+ async function readSkill(root, name, catalog) {
65
+ try {
66
+ const path = join(root.directory, name, "SKILL.md");
67
+ const file = await lstat(path);
68
+ if (!readableSkill(file)) {
69
+ catalog.complete = false;
70
+ return;
71
+ }
72
+ const bytes = await readFile(path);
73
+ if (bytes.length > MAX_SKILL_BYTES)
74
+ throw new Error("skill changed during catalog read");
75
+ addSkill(bytes, [root.namespace, name].filter(Boolean).join(":"), catalog);
76
+ }
77
+ catch (cause) {
78
+ if (!missing(cause))
79
+ catalog.complete = false;
80
+ }
81
+ }
82
+ function readableSkill(file) {
83
+ return file.isFile() && file.size <= MAX_SKILL_BYTES;
84
+ }
85
+ function addSkill(bytes, invocation, catalog) {
86
+ if (!bytes.length)
87
+ return;
88
+ catalog.bytes += bytes.length;
89
+ if (catalog.skills.length >= MAX_SKILLS ||
90
+ catalog.bytes > MAX_CATALOG_BYTES) {
91
+ catalog.complete = false;
92
+ return;
93
+ }
94
+ catalog.skills.push({
95
+ invocation,
96
+ sha256: createHash("sha256").update(bytes).digest("hex"),
97
+ body: bytes.toString("utf8"),
98
+ });
99
+ }
100
+ //# sourceMappingURL=skill-hook-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-hook-catalog.js","sourceRoot":"","sources":["skill-hook-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,oBAAoB;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,yBAAyB;AACtE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB;AAGpD,OAAO,EAAE,QAAQ,EAAE,2BAAwB;AAE3C,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAY1C,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,SAAiB,EACjB,WAAqB,EACrB,gBAAoC;IAEpC,MAAM,KAAK,GAAuB;QAChC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE;QACrE,GAAG,gBAAgB;KACpB,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAY,IAAI,CAAC,KAAK,CAClC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,4BAA4B,CAAC,EAAE,MAAM,CAAC,CACjE,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ;YAC1D,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC;YACT,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;YAC/B,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,QAAQ,CAAC,IAAI;SACzB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAyB;IAC9D,MAAM,OAAO,GAAY,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAClE,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;AACvE,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,IAAsB,EAAE,OAAgB;IAC9D,IAAI,CAAC;QACH,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzD,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC5C,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAC7B,EAAE,CAAC;YACF,MAAM,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC;IAChD,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAiB,EAAE,OAAgB;IAC7D,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,cAAc,EAAE;YAAE,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC;QACrD,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAC7D,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,IAAsB;IACtD,MAAM,QAAQ,GAAG,OAAO,CACtB,MAAM,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAC7B,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CACxC,CAAC;IACF,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,QAAQ;QAC/C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC9E,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,IAAsB,EACtB,IAAY,EACZ,OAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC;YACzB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,MAAM,GAAG,eAAe;YAChC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACvD,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC;IAChD,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAuC;IAC5D,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,IAAI,eAAe,CAAC;AACvD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,UAAkB,EAAE,OAAgB;IACnE,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO;IAC1B,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;IAC9B,IACE,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,UAAU;QACnC,OAAO,CAAC,KAAK,GAAG,iBAAiB,EACjC,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC;QACzB,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;QAClB,UAAU;QACV,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACxD,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;KAC7B,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { lstat, readFile, readdir, realpath } from \"node:fs/promises\";\nimport { join, relative, resolve } from \"node:path\";\nimport type { Dirent } from \"node:fs\";\nimport type { HookSkill } from \"./skill-hook-handler\";\nimport { isRecord } from \"../value-guards\";\n\nconst MAX_SKILLS = 128;\nconst MAX_SKILL_BYTES = 1024 * 1024;\nconst MAX_CATALOG_BYTES = 8 * 1024 * 1024;\ninterface Catalog {\n skills: HookSkill[];\n complete: boolean;\n bytes: number;\n}\nexport interface SkillCatalogRoot {\n directory: string;\n boundary: string;\n namespace?: string;\n}\n\nexport async function codexSkillCatalogRoots(\n workspace: string,\n pluginRoots: string[],\n nativeSkillRoots: SkillCatalogRoot[],\n): Promise<SkillCatalogRoot[]> {\n const roots: SkillCatalogRoot[] = [\n { directory: join(workspace, \".agents/skills\"), boundary: workspace },\n ...nativeSkillRoots,\n ];\n for (const root of pluginRoots) {\n const manifest: unknown = JSON.parse(\n await readFile(join(root, \".claude-plugin/plugin.json\"), \"utf8\"),\n );\n if (!isRecord(manifest) || typeof manifest.name !== \"string\")\n throw new Error(\"invalid skill catalog plugin manifest\");\n roots.push({\n directory: join(root, \"skills\"),\n boundary: root,\n namespace: manifest.name,\n });\n }\n return roots;\n}\n\n/** Snapshot only known skill mounts; shell syntax does not enter discovery. */\nexport async function skillHookCatalog(roots: SkillCatalogRoot[]) {\n const catalog: Catalog = { skills: [], complete: true, bytes: 0 };\n for (const root of roots) await readRoot(root, catalog);\n return { skills: catalog.skills, catalogComplete: catalog.complete };\n}\n\nasync function readRoot(root: SkillCatalogRoot, catalog: Catalog) {\n try {\n await requireCatalogRoot(root);\n const entries = await readdir(root.directory, { withFileTypes: true });\n const directories = catalogDirectories(entries, catalog);\n for (const entry of directories.sort((a, b) =>\n a.name.localeCompare(b.name),\n )) {\n await readSkill(root, entry.name, catalog);\n }\n } catch (cause) {\n if (!missing(cause)) catalog.complete = false;\n }\n}\n\nfunction catalogDirectories(entries: Dirent[], catalog: Catalog): Dirent[] {\n const directories: Dirent[] = [];\n for (const entry of entries) {\n if (entry.isSymbolicLink()) catalog.complete = false;\n if (entry.isDirectory() && /^[A-Za-z0-9._-]+$/.test(entry.name))\n directories.push(entry);\n }\n return directories;\n}\n\nasync function requireCatalogRoot(root: SkillCatalogRoot) {\n const expected = resolve(\n await realpath(root.boundary),\n relative(root.boundary, root.directory),\n );\n if ((await realpath(root.directory)) !== expected)\n throw new Error(\"redirected skill catalog root\");\n}\n\nfunction missing(cause: unknown): boolean {\n return cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\";\n}\n\nasync function readSkill(\n root: SkillCatalogRoot,\n name: string,\n catalog: Catalog,\n) {\n try {\n const path = join(root.directory, name, \"SKILL.md\");\n const file = await lstat(path);\n if (!readableSkill(file)) {\n catalog.complete = false;\n return;\n }\n const bytes = await readFile(path);\n if (bytes.length > MAX_SKILL_BYTES)\n throw new Error(\"skill changed during catalog read\");\n addSkill(bytes, [root.namespace, name].filter(Boolean).join(\":\"), catalog);\n } catch (cause) {\n if (!missing(cause)) catalog.complete = false;\n }\n}\n\nfunction readableSkill(file: Awaited<ReturnType<typeof lstat>>): boolean {\n return file.isFile() && file.size <= MAX_SKILL_BYTES;\n}\n\nfunction addSkill(bytes: Buffer, invocation: string, catalog: Catalog) {\n if (!bytes.length) return;\n catalog.bytes += bytes.length;\n if (\n catalog.skills.length >= MAX_SKILLS ||\n catalog.bytes > MAX_CATALOG_BYTES\n ) {\n catalog.complete = false;\n return;\n }\n catalog.skills.push({\n invocation,\n sha256: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n body: bytes.toString(\"utf8\"),\n });\n}\n"]}