orizu 0.5.19 → 0.5.21

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 (378) hide show
  1. package/README.md +23 -0
  2. package/dist/archive-artifacts-cli.d.ts +50 -0
  3. package/dist/archive-artifacts-cli.d.ts.map +1 -0
  4. package/dist/artifact-archive.d.ts +48 -0
  5. package/dist/artifact-archive.d.ts.map +1 -0
  6. package/dist/artifact-pull.d.ts +22 -0
  7. package/dist/artifact-pull.d.ts.map +1 -0
  8. package/dist/banner.d.ts +3 -0
  9. package/dist/banner.d.ts.map +1 -0
  10. package/dist/connectors-cli.d.ts +31 -0
  11. package/dist/connectors-cli.d.ts.map +1 -0
  12. package/dist/credentials.d.ts +46 -0
  13. package/dist/credentials.d.ts.map +1 -0
  14. package/dist/csv.d.ts +49 -0
  15. package/dist/csv.d.ts.map +1 -0
  16. package/dist/dataset-download.d.ts +6 -0
  17. package/dist/dataset-download.d.ts.map +1 -0
  18. package/dist/dataset-edit-rows.d.ts +10 -0
  19. package/dist/dataset-edit-rows.d.ts.map +1 -0
  20. package/dist/dataset-upload-snapshot.d.ts +13 -0
  21. package/dist/dataset-upload-snapshot.d.ts.map +1 -0
  22. package/dist/daytona-slice-rehearsal.d.ts +89 -0
  23. package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
  24. package/dist/daytona-slice.d.ts +106 -0
  25. package/dist/daytona-slice.d.ts.map +1 -0
  26. package/dist/deploy-key-merge-core.d.ts +121 -0
  27. package/dist/deploy-key-merge-core.d.ts.map +1 -0
  28. package/dist/diff-comments-cli.d.ts +12 -0
  29. package/dist/diff-comments-cli.d.ts.map +1 -0
  30. package/dist/diff-comments-cli.js +21 -2
  31. package/dist/egress-allowlist-cli.d.ts +23 -0
  32. package/dist/egress-allowlist-cli.d.ts.map +1 -0
  33. package/dist/egress-policy.d.ts +108 -0
  34. package/dist/egress-policy.d.ts.map +1 -0
  35. package/dist/error-response.d.ts +9 -0
  36. package/dist/error-response.d.ts.map +1 -0
  37. package/dist/file-parser.d.ts +5 -0
  38. package/dist/file-parser.d.ts.map +1 -0
  39. package/dist/gepa-engine-dispatch.d.ts +9 -0
  40. package/dist/gepa-engine-dispatch.d.ts.map +1 -0
  41. package/dist/gepa-engine-dispatch.js +307 -0
  42. package/dist/gepa-python-command.d.ts +6 -0
  43. package/dist/gepa-python-command.d.ts.map +1 -0
  44. package/dist/gepa-python-command.js +26 -0
  45. package/dist/gepa-python-paths.d.ts +6 -0
  46. package/dist/gepa-python-paths.d.ts.map +1 -0
  47. package/dist/gepa-python-paths.js +42 -0
  48. package/dist/git-credential.d.ts +69 -0
  49. package/dist/git-credential.d.ts.map +1 -0
  50. package/dist/git-recovery-environment.d.ts +31 -0
  51. package/dist/git-recovery-environment.d.ts.map +1 -0
  52. package/dist/github-setup.d.ts +122 -0
  53. package/dist/github-setup.d.ts.map +1 -0
  54. package/dist/global-flags.d.ts +12 -0
  55. package/dist/global-flags.d.ts.map +1 -0
  56. package/dist/help.d.ts +46 -0
  57. package/dist/help.d.ts.map +1 -0
  58. package/dist/help.js +93 -25
  59. package/dist/hosted-boot.d.ts +273 -0
  60. package/dist/hosted-boot.d.ts.map +1 -0
  61. package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
  62. package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
  63. package/dist/hosted-bootstrap.d.ts +188 -0
  64. package/dist/hosted-bootstrap.d.ts.map +1 -0
  65. package/dist/hosted-broker-server.d.ts +50 -0
  66. package/dist/hosted-broker-server.d.ts.map +1 -0
  67. package/dist/hosted-git-http-server.d.ts +36 -0
  68. package/dist/hosted-git-http-server.d.ts.map +1 -0
  69. package/dist/hosted-harness-claude.d.ts +195 -0
  70. package/dist/hosted-harness-claude.d.ts.map +1 -0
  71. package/dist/hosted-harness-opencode.d.ts +227 -0
  72. package/dist/hosted-harness-opencode.d.ts.map +1 -0
  73. package/dist/hosted-harness.d.ts +118 -0
  74. package/dist/hosted-harness.d.ts.map +1 -0
  75. package/dist/hosted-harvest.d.ts +68 -0
  76. package/dist/hosted-harvest.d.ts.map +1 -0
  77. package/dist/hosted-headless.d.ts +72 -0
  78. package/dist/hosted-headless.d.ts.map +1 -0
  79. package/dist/hosted-loop-lifecycle.d.ts +206 -0
  80. package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
  81. package/dist/hosted-loop.d.ts +170 -0
  82. package/dist/hosted-loop.d.ts.map +1 -0
  83. package/dist/hosted-run-event-sink.d.ts +222 -0
  84. package/dist/hosted-run-event-sink.d.ts.map +1 -0
  85. package/dist/hosted-runtime-assets.d.ts +186 -0
  86. package/dist/hosted-runtime-assets.d.ts.map +1 -0
  87. package/dist/hosted-session-cli.d.ts +258 -0
  88. package/dist/hosted-session-cli.d.ts.map +1 -0
  89. package/dist/hosted-skill-staging.d.ts +73 -0
  90. package/dist/hosted-skill-staging.d.ts.map +1 -0
  91. package/dist/http.d.ts +8 -0
  92. package/dist/http.d.ts.map +1 -0
  93. package/dist/index.d.ts +22 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +119 -129
  96. package/dist/instruction-set-gepa-launch.d.ts +3 -0
  97. package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
  98. package/dist/instruction-set-gepa-launch.js +15 -0
  99. package/dist/instruction-set-loader.d.ts +6 -0
  100. package/dist/instruction-set-loader.d.ts.map +1 -0
  101. package/dist/instruction-set-loader.js +59 -0
  102. package/dist/instruction-set-manifest.d.ts +14 -0
  103. package/dist/instruction-set-manifest.d.ts.map +1 -0
  104. package/dist/instruction-set-manifest.js +162 -0
  105. package/dist/instruction-sets-cli.d.ts +40 -0
  106. package/dist/instruction-sets-cli.d.ts.map +1 -0
  107. package/dist/instruction-sets-cli.js +361 -0
  108. package/dist/json-response.d.ts +4 -0
  109. package/dist/json-response.d.ts.map +1 -0
  110. package/dist/jsonl-stream.d.ts +8 -0
  111. package/dist/jsonl-stream.d.ts.map +1 -0
  112. package/dist/kill-switch-cli.d.ts +30 -0
  113. package/dist/kill-switch-cli.d.ts.map +1 -0
  114. package/dist/loopback-serve.d.ts +25 -0
  115. package/dist/loopback-serve.d.ts.map +1 -0
  116. package/dist/manifests-cli.d.ts +22 -0
  117. package/dist/manifests-cli.d.ts.map +1 -0
  118. package/dist/markdown-report-input.d.ts +7 -0
  119. package/dist/markdown-report-input.d.ts.map +1 -0
  120. package/dist/markdown-report-input.js +15 -1
  121. package/dist/merge-job.d.ts +155 -0
  122. package/dist/merge-job.d.ts.map +1 -0
  123. package/dist/model-configs-cli.d.ts +19 -0
  124. package/dist/model-configs-cli.d.ts.map +1 -0
  125. package/dist/model-configs-cli.js +104 -0
  126. package/dist/optimization-export-cli.d.ts +19 -0
  127. package/dist/optimization-export-cli.d.ts.map +1 -0
  128. package/dist/optimizations-list-cli.d.ts +20 -0
  129. package/dist/optimizations-list-cli.d.ts.map +1 -0
  130. package/dist/optimizations-promote-cli.d.ts +8 -0
  131. package/dist/optimizations-promote-cli.d.ts.map +1 -0
  132. package/dist/optimizations-promote-cli.js +34 -0
  133. package/dist/option-validation.d.ts +15 -0
  134. package/dist/option-validation.d.ts.map +1 -0
  135. package/dist/preview-runtime.d.ts +63 -0
  136. package/dist/preview-runtime.d.ts.map +1 -0
  137. package/dist/prompt-draft-push.d.ts +60 -0
  138. package/dist/prompt-draft-push.d.ts.map +1 -0
  139. package/dist/prompt-label-cli.d.ts +11 -0
  140. package/dist/prompt-label-cli.d.ts.map +1 -0
  141. package/dist/prompt-label-cli.js +29 -0
  142. package/dist/prompt-length-wire.d.ts +28 -0
  143. package/dist/prompt-length-wire.d.ts.map +1 -0
  144. package/dist/prompt-length-wire.js +2 -2
  145. package/dist/prompt-report-cli.d.ts +10 -0
  146. package/dist/prompt-report-cli.d.ts.map +1 -0
  147. package/dist/prompt-report-cli.js +2 -2
  148. package/dist/prompt-summary-table.d.ts +15 -0
  149. package/dist/prompt-summary-table.d.ts.map +1 -0
  150. package/dist/prompt-summary-table.js +6 -0
  151. package/dist/registration-tag-warning.d.ts +32 -0
  152. package/dist/registration-tag-warning.d.ts.map +1 -0
  153. package/dist/report-comments-cli.d.ts +16 -0
  154. package/dist/report-comments-cli.d.ts.map +1 -0
  155. package/dist/report-markdown-blankness.d.ts +2 -0
  156. package/dist/report-markdown-blankness.d.ts.map +1 -0
  157. package/dist/report-markdown-blankness.js +6 -0
  158. package/dist/report-replacement-warning.d.ts +17 -0
  159. package/dist/report-replacement-warning.d.ts.map +1 -0
  160. package/dist/report-replacement-warning.js +31 -0
  161. package/dist/repository-backup-core.d.ts +225 -0
  162. package/dist/repository-backup-core.d.ts.map +1 -0
  163. package/dist/repository-recovery-manifest.d.ts +83 -0
  164. package/dist/repository-recovery-manifest.d.ts.map +1 -0
  165. package/dist/run-event-digest.d.ts +74 -0
  166. package/dist/run-event-digest.d.ts.map +1 -0
  167. package/dist/runner-dir-verify.d.ts +66 -0
  168. package/dist/runner-dir-verify.d.ts.map +1 -0
  169. package/dist/runner-instruction-set-contract.d.ts +24 -0
  170. package/dist/runner-instruction-set-contract.d.ts.map +1 -0
  171. package/dist/runner-instruction-set-contract.js +50 -0
  172. package/dist/sandbox-hygiene.d.ts +45 -0
  173. package/dist/sandbox-hygiene.d.ts.map +1 -0
  174. package/dist/sandbox-provider.d.ts +193 -0
  175. package/dist/sandbox-provider.d.ts.map +1 -0
  176. package/dist/scorer-draft-push.d.ts +50 -0
  177. package/dist/scorer-draft-push.d.ts.map +1 -0
  178. package/dist/scorers-cli.d.ts +20 -0
  179. package/dist/scorers-cli.d.ts.map +1 -0
  180. package/dist/scorers-cli.js +22 -0
  181. package/dist/scorers-verify-parity-bridge.d.ts +16 -0
  182. package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
  183. package/dist/scorers-verify-parity-bridge.js +90 -0
  184. package/dist/scorers-verify-parity.d.ts +81 -0
  185. package/dist/scorers-verify-parity.d.ts.map +1 -0
  186. package/dist/scorers-verify-parity.js +502 -0
  187. package/dist/scores-accept-cli.d.ts +21 -0
  188. package/dist/scores-accept-cli.d.ts.map +1 -0
  189. package/dist/secret-redaction.d.ts +47 -0
  190. package/dist/secret-redaction.d.ts.map +1 -0
  191. package/dist/session-timeline.d.ts +54 -0
  192. package/dist/session-timeline.d.ts.map +1 -0
  193. package/dist/setup-prompt.d.ts +5 -0
  194. package/dist/setup-prompt.d.ts.map +1 -0
  195. package/dist/skill-installer.d.ts +68 -0
  196. package/dist/skill-installer.d.ts.map +1 -0
  197. package/dist/skilled-proposer-launch.d.ts +7 -0
  198. package/dist/skilled-proposer-launch.d.ts.map +1 -0
  199. package/dist/skilled-proposer-launch.js +60 -0
  200. package/dist/smoke.d.ts +102 -0
  201. package/dist/smoke.d.ts.map +1 -0
  202. package/dist/start-intent.d.ts +12 -0
  203. package/dist/start-intent.d.ts.map +1 -0
  204. package/dist/task-assignment-manifest.d.ts +15 -0
  205. package/dist/task-assignment-manifest.d.ts.map +1 -0
  206. package/dist/task-create-error.d.ts +18 -0
  207. package/dist/task-create-error.d.ts.map +1 -0
  208. package/dist/team-connectors-cli.d.ts +29 -0
  209. package/dist/team-connectors-cli.d.ts.map +1 -0
  210. package/dist/types.d.ts +39 -0
  211. package/dist/types.d.ts.map +1 -0
  212. package/dist/vercel-sandbox-provider.d.ts +156 -0
  213. package/dist/vercel-sandbox-provider.d.ts.map +1 -0
  214. package/dist/workbench-cli.d.ts +233 -0
  215. package/dist/workbench-cli.d.ts.map +1 -0
  216. package/dist/workspace-sync.d.ts +207 -0
  217. package/dist/workspace-sync.d.ts.map +1 -0
  218. package/dist/workspace.d.ts +92 -0
  219. package/dist/workspace.d.ts.map +1 -0
  220. package/dist/zip-draft-push.d.ts +55 -0
  221. package/dist/zip-draft-push.d.ts.map +1 -0
  222. package/package.json +15 -2
  223. package/requirements/README.md +138 -0
  224. package/requirements/skilled-proposer.in +66 -0
  225. package/requirements/skilled-proposer.lock +265 -0
  226. package/scripts/build-cli-dist.mjs +30 -0
  227. package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
  228. package/scripts/orizu-source-checkout.d.mts +1 -0
  229. package/scripts/orizu-source-checkout.mjs +10 -0
  230. package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
  231. package/scripts/vendor-gepa-python.mjs +187 -4
  232. package/src/diff-comments-cli.ts +41 -3
  233. package/src/gepa-engine-dispatch.ts +340 -0
  234. package/src/gepa-python-command.ts +31 -0
  235. package/src/gepa-python-paths.ts +56 -0
  236. package/src/help.ts +95 -25
  237. package/src/index.ts +131 -166
  238. package/src/instruction-set-gepa-launch.ts +28 -0
  239. package/src/instruction-set-loader.ts +37 -0
  240. package/src/instruction-set-manifest.ts +120 -0
  241. package/src/instruction-sets-cli.ts +306 -0
  242. package/src/markdown-report-input.ts +19 -1
  243. package/src/model-configs-cli.ts +119 -0
  244. package/src/optimizations-promote-cli.ts +41 -0
  245. package/src/prompt-label-cli.ts +55 -0
  246. package/src/prompt-length-wire.ts +1 -1
  247. package/src/prompt-report-cli.ts +2 -2
  248. package/src/prompt-summary-table.ts +12 -0
  249. package/src/report-markdown-blankness.ts +6 -0
  250. package/src/report-replacement-warning.ts +35 -0
  251. package/src/runner-instruction-set-contract.ts +64 -0
  252. package/src/scorers-cli.ts +40 -0
  253. package/src/scorers-verify-parity-bridge.ts +90 -0
  254. package/src/scorers-verify-parity.ts +564 -0
  255. package/src/skilled-proposer-launch.ts +89 -0
  256. package/src/skilled-proposer-venv-manager.mjs +757 -0
  257. package/tsconfig.json +2 -0
  258. package/vendor/gepa-python/LICENSE +7 -0
  259. package/vendor/gepa-python/manifest.json +107 -0
  260. package/vendor/gepa-python/src/gepa/__init__.py +22 -0
  261. package/vendor/gepa-python/src/gepa/adapters/__init__.py +0 -0
  262. package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/__init__.py +1 -0
  263. package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
  264. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/__init__.py +26 -0
  265. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/confidence_adapter.py +618 -0
  266. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/scoring.py +206 -0
  267. package/vendor/gepa-python/src/gepa/adapters/default_adapter/__init__.py +0 -0
  268. package/vendor/gepa-python/src/gepa/adapters/default_adapter/default_adapter.py +206 -0
  269. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/__init__.py +0 -0
  270. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/dspy_adapter.py +525 -0
  271. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/instruction_proposal.py +500 -0
  272. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/__init__.py +6 -0
  273. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
  274. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/full_program_adapter.py +269 -0
  275. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/__init__.py +56 -0
  276. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
  277. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
  278. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
  279. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
  280. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
  281. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
  282. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
  283. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
  284. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
  285. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
  286. package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/__init__.py +11 -0
  287. package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/langchain_adapter.py +261 -0
  288. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/__init__.py +37 -0
  289. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_adapter.py +693 -0
  290. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_client.py +364 -0
  291. package/vendor/gepa-python/src/gepa/adapters/optimize_anything_adapter/optimize_anything_adapter.py +879 -0
  292. package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/__init__.py +0 -0
  293. package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
  294. package/vendor/gepa-python/src/gepa/api.py +468 -0
  295. package/vendor/gepa-python/src/gepa/core/__init__.py +0 -0
  296. package/vendor/gepa-python/src/gepa/core/adapter.py +227 -0
  297. package/vendor/gepa-python/src/gepa/core/callbacks.py +551 -0
  298. package/vendor/gepa-python/src/gepa/core/data_loader.py +74 -0
  299. package/vendor/gepa-python/src/gepa/core/engine.py +1025 -0
  300. package/vendor/gepa-python/src/gepa/core/result.py +271 -0
  301. package/vendor/gepa-python/src/gepa/core/state.py +706 -0
  302. package/vendor/gepa-python/src/gepa/examples/__init__.py +0 -0
  303. package/vendor/gepa-python/src/gepa/examples/aime.py +24 -0
  304. package/vendor/gepa-python/src/gepa/examples/anymaths-bench/eval_default.py +111 -0
  305. package/vendor/gepa-python/src/gepa/examples/anymaths-bench/train_anymaths.py +177 -0
  306. package/vendor/gepa-python/src/gepa/examples/mcp_adapter/__init__.py +4 -0
  307. package/vendor/gepa-python/src/gepa/examples/mcp_adapter/mcp_optimization_example.py +456 -0
  308. package/vendor/gepa-python/src/gepa/examples/rag_adapter/__init__.py +9 -0
  309. package/vendor/gepa-python/src/gepa/examples/rag_adapter/rag_optimization.py +824 -0
  310. package/vendor/gepa-python/src/gepa/examples/terminal-bench/train_terminus.py +161 -0
  311. package/vendor/gepa-python/src/gepa/gepa_utils.py +117 -0
  312. package/vendor/gepa-python/src/gepa/gskill/__init__.py +1 -0
  313. package/vendor/gepa-python/src/gepa/gskill/gskill/__init__.py +0 -0
  314. package/vendor/gepa-python/src/gepa/gskill/gskill/cost_tracker.py +194 -0
  315. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/__init__.py +0 -0
  316. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code.py +1117 -0
  317. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code_skills.py +613 -0
  318. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/mini_swe_agent.py +440 -0
  319. package/vendor/gepa-python/src/gepa/gskill/gskill/experiment_logger.py +367 -0
  320. package/vendor/gepa-python/src/gepa/gskill/gskill/swe_fitness_fn.py +209 -0
  321. package/vendor/gepa-python/src/gepa/gskill/gskill/swe_harness.py +352 -0
  322. package/vendor/gepa-python/src/gepa/gskill/gskill/train_optimize_anything.py +830 -0
  323. package/vendor/gepa-python/src/gepa/image.py +104 -0
  324. package/vendor/gepa-python/src/gepa/lm.py +261 -0
  325. package/vendor/gepa-python/src/gepa/logging/__init__.py +0 -0
  326. package/vendor/gepa-python/src/gepa/logging/experiment_tracker.py +457 -0
  327. package/vendor/gepa-python/src/gepa/logging/logger.py +75 -0
  328. package/vendor/gepa-python/src/gepa/logging/utils.py +130 -0
  329. package/vendor/gepa-python/src/gepa/optimize_anything.py +1701 -0
  330. package/vendor/gepa-python/src/gepa/proposer/__init__.py +0 -0
  331. package/vendor/gepa-python/src/gepa/proposer/base.py +53 -0
  332. package/vendor/gepa-python/src/gepa/proposer/merge.py +403 -0
  333. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/__init__.py +0 -0
  334. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/base.py +64 -0
  335. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflection_lm.py +178 -0
  336. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflective_mutation.py +641 -0
  337. package/vendor/gepa-python/src/gepa/py.typed +0 -0
  338. package/vendor/gepa-python/src/gepa/strategies/__init__.py +0 -0
  339. package/vendor/gepa-python/src/gepa/strategies/acceptance.py +66 -0
  340. package/vendor/gepa-python/src/gepa/strategies/batch_sampler.py +103 -0
  341. package/vendor/gepa-python/src/gepa/strategies/candidate_selector.py +82 -0
  342. package/vendor/gepa-python/src/gepa/strategies/component_selector.py +36 -0
  343. package/vendor/gepa-python/src/gepa/strategies/eval_policy.py +64 -0
  344. package/vendor/gepa-python/src/gepa/strategies/instruction_proposal.py +153 -0
  345. package/vendor/gepa-python/src/gepa/strategies/proposal_sampling.py +128 -0
  346. package/vendor/gepa-python/src/gepa/strategies/proposal_selection.py +84 -0
  347. package/vendor/gepa-python/src/gepa/utils/__init__.py +63 -0
  348. package/vendor/gepa-python/src/gepa/utils/code_execution.py +626 -0
  349. package/vendor/gepa-python/src/gepa/utils/stdio_capture.py +145 -0
  350. package/vendor/gepa-python/src/gepa/utils/stop_condition.py +230 -0
  351. package/vendor/gepa-python/src/gepa/visualization.py +359 -0
  352. package/vendor/orizu-gepa/pyproject.toml +18 -0
  353. package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +9 -0
  354. package/vendor/orizu-gepa/src/orizu_gepa_connector/__main__.py +5 -0
  355. package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +138 -0
  356. package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +666 -0
  357. package/vendor/orizu-gepa/src/orizu_gepa_connector/cli.py +7 -0
  358. package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +269 -0
  359. package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
  360. package/vendor/orizu-gepa/src/orizu_gepa_connector/preflight.py +64 -0
  361. package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +186 -0
  362. package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +721 -0
  363. package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
  364. package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +55 -0
  365. package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +203 -0
  366. package/vendor/orizu-gepa-python/pyproject.toml +1 -1
  367. package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +1 -0
  368. package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
  369. package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +52 -0
  370. package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
  371. package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
  372. package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
  373. package/vendor/skills/orizu-cli/SKILL.md +3 -1
  374. package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
  375. package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
  376. package/vendor/skills/orizu-cli/references/optimization-reports.md +1 -1
  377. package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +26 -1
  378. package/vendor/skills/orizu-cli/references/prompt-control-plane.md +113 -10
@@ -0,0 +1,757 @@
1
+ import { spawn, spawnSync } from 'node:child_process'
2
+ import { createHash, randomUUID } from 'node:crypto'
3
+ import {
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ readdirSync,
8
+ realpathSync,
9
+ renameSync,
10
+ rmSync,
11
+ statSync,
12
+ writeFileSync,
13
+ } from 'node:fs'
14
+ import { delimiter, join } from 'node:path'
15
+ import { devNull } from 'node:os'
16
+
17
+ const PYTHON_IDENTITY_PROGRAM = String.raw`
18
+ import json
19
+ import os
20
+ import platform
21
+ import sys
22
+ import sysconfig
23
+
24
+ print(json.dumps({
25
+ "implementation": platform.python_implementation(),
26
+ "version": list(sys.version_info[:3]),
27
+ "executable": os.path.realpath(sys.executable),
28
+ "platform": sysconfig.get_platform(),
29
+ "cacheTag": sys.implementation.cache_tag,
30
+ "soabi": sysconfig.get_config_var("SOABI"),
31
+ "system": platform.system(),
32
+ "machine": platform.machine(),
33
+ "osVersion": platform.mac_ver()[0] if platform.system() == "Darwin" else platform.release(),
34
+ "libc": list(platform.libc_ver()),
35
+ }))
36
+ `
37
+
38
+ const SSL_CERT_FILE_PROGRAM = String.raw`
39
+ import os
40
+ import ssl
41
+
42
+ paths = ssl.get_default_verify_paths()
43
+ for candidate in (paths.cafile, paths.openssl_cafile):
44
+ if candidate and os.path.isfile(candidate):
45
+ print(os.path.realpath(candidate))
46
+ break
47
+ `
48
+
49
+ const IMPORT_SMOKE_PROGRAM = String.raw`
50
+ import dspy
51
+ import skilled_proposer
52
+ from skilled_proposer import SkilledProposer
53
+
54
+ SkilledProposer(skills=[])
55
+ `
56
+
57
+ const PUBLISH_LOCK_PROGRAM = String.raw`
58
+ import fcntl
59
+ import os
60
+ import signal
61
+ import sys
62
+ import time
63
+
64
+ descriptor = os.open(sys.argv[1], os.O_CREAT | os.O_RDWR, 0o600)
65
+ try:
66
+ try:
67
+ fcntl.flock(descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
68
+ except BlockingIOError:
69
+ print("waiting", flush=True)
70
+ fcntl.flock(descriptor, fcntl.LOCK_EX)
71
+ node_pid = int(sys.argv[2])
72
+ guardian_pid = os.fork()
73
+ if guardian_pid == 0:
74
+ for stream_descriptor in (0, 1, 2):
75
+ try:
76
+ os.close(stream_descriptor)
77
+ except OSError:
78
+ pass
79
+ signal.signal(signal.SIGTERM, lambda _signal, _frame: sys.exit(0))
80
+ deadline = time.monotonic() + (25 * 60)
81
+ while time.monotonic() < deadline:
82
+ try:
83
+ os.kill(node_pid, 0)
84
+ except ProcessLookupError:
85
+ break
86
+ time.sleep(0.1)
87
+ sys.exit(0)
88
+ print(f"guardian:{guardian_pid}", flush=True)
89
+ print("acquired", flush=True)
90
+ sys.stdin.readline()
91
+ try:
92
+ os.kill(guardian_pid, signal.SIGTERM)
93
+ os.waitpid(guardian_pid, 0)
94
+ except ProcessLookupError:
95
+ pass
96
+ finally:
97
+ os.close(descriptor)
98
+ `
99
+
100
+ const MANAGER_SCHEMA_VERSION = 1
101
+ const MANIFEST_NAME = '.orizu-skilled-proposer-venv.json'
102
+ const LOCK_WAIT_MILLISECONDS = 25
103
+ // This exceeds the longest repair path while holding the lock: existing-cache
104
+ // validation (2m) + venv (2m) + install (10m) + staged validation (2m), plus
105
+ // cleanup/publication/release margin.
106
+ const LOCK_TIMEOUT_MILLISECONDS = 20 * 60 * 1000
107
+ const PYTHON_PROBE_TIMEOUT_MILLISECONDS = 15_000
108
+ const VENV_CREATE_TIMEOUT_MILLISECONDS = 2 * 60 * 1000
109
+ const VALIDATION_TIMEOUT_MILLISECONDS = 60_000
110
+ const PIP_INSTALL_TIMEOUT_MILLISECONDS = 10 * 60 * 1000
111
+ const TEST_BARRIER_TIMEOUT_MILLISECONDS = 60_000
112
+ const ABANDONED_STAGE_MAX_AGE_MILLISECONDS = 24 * 60 * 60 * 1000
113
+ const SSL_CERT_FILE_UNRESOLVED_WARNING = {
114
+ code: 'ALI_1505_SSL_CERT_FILE_UNRESOLVED',
115
+ message: 'no system CA bundle was resolved; pip will use its bundled trust store, but provider traffic may require SSL_CERT_FILE',
116
+ }
117
+
118
+ export class SkilledProposerVenvError extends Error {
119
+ constructor(code, message, report = {}) {
120
+ super(message)
121
+ this.name = 'SkilledProposerVenvError'
122
+ this.code = code
123
+ this.report = report
124
+ }
125
+ }
126
+
127
+ export function parseManagerArguments(argv) {
128
+ const options = { json: false, noIndex: false }
129
+ const valueOptions = new Map([
130
+ ['--python', 'python'],
131
+ ['--cache-root', 'cacheRoot'],
132
+ ['--lock', 'lock'],
133
+ ['--wheelhouse', 'wheelhouse'],
134
+ ['--vendored-gepa-path', 'vendoredGepaPath'],
135
+ ])
136
+ for (let index = 0; index < argv.length; index += 1) {
137
+ const argument = argv[index]
138
+ if (argument === '--json') {
139
+ options.json = true
140
+ continue
141
+ }
142
+ if (argument === '--no-index') {
143
+ options.noIndex = true
144
+ continue
145
+ }
146
+ const key = valueOptions.get(argument)
147
+ if (!key) {
148
+ throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', `unknown argument: ${argument}`)
149
+ }
150
+ const value = argv[index + 1]
151
+ if (!value || value.startsWith('--')) {
152
+ throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', `${argument} requires a value`)
153
+ }
154
+ options[key] = value
155
+ index += 1
156
+ }
157
+ for (const key of ['python', 'cacheRoot', 'lock']) {
158
+ if (!options[key]) {
159
+ throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', `--${key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)} is required`)
160
+ }
161
+ }
162
+ if (options.noIndex && !options.wheelhouse) {
163
+ throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', '--no-index requires --wheelhouse')
164
+ }
165
+ return options
166
+ }
167
+
168
+ function parsePythonIdentity(stdout, configuredPython) {
169
+ const trimmed = stdout.trim()
170
+ try {
171
+ const value = JSON.parse(trimmed)
172
+ if (Array.isArray(value.version) && value.version.length >= 2) return value
173
+ } catch {
174
+ // The approved outer red gate uses a deliberately tiny fake executable
175
+ // that prints only a version. This preserves mutation-free preflight.
176
+ }
177
+ const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(trimmed)
178
+ if (match) {
179
+ const version = match.slice(1).map(Number)
180
+ const [major, minor] = version
181
+ if (major !== 3 || minor < 10 || minor >= 15) {
182
+ return {
183
+ implementation: 'CPython',
184
+ version,
185
+ executable: configuredPython,
186
+ platform: 'unsupported-version-probe',
187
+ }
188
+ }
189
+ }
190
+ throw new SkilledProposerVenvError(
191
+ 'ALI_1505_PYTHON_PREFLIGHT_FAILED',
192
+ `unable to characterize selected Python: ${configuredPython}`,
193
+ )
194
+ }
195
+
196
+ function versionAtLeast(actual, required) {
197
+ const parts = String(actual).split('.').map(part => Number.parseInt(part, 10) || 0)
198
+ for (let index = 0; index < required.length; index += 1) {
199
+ if ((parts[index] || 0) > required[index]) return true
200
+ if ((parts[index] || 0) < required[index]) return false
201
+ }
202
+ return true
203
+ }
204
+
205
+ function assertSupportedWheelTarget(identity) {
206
+ // `platform.libc_ver()` is not merely cache identity on Linux: it is also a
207
+ // conservative admission check for the locked glibc wheel matrix. A bad
208
+ // guess can refuse a compatible host (named, before mutation), but cannot
209
+ // admit an sdist or make pip install an incompatible wheel because the
210
+ // customer install remains hash-locked and binary-only.
211
+ const machine = String(identity.machine).toLowerCase()
212
+ const isSupportedMac = identity.system === 'Darwin'
213
+ && machine === 'arm64'
214
+ && versionAtLeast(identity.osVersion, [15])
215
+ const [libcName, libcVersion] = Array.isArray(identity.libc) ? identity.libc : []
216
+ const isSupportedLinux = identity.system === 'Linux'
217
+ && ['x86_64', 'amd64'].includes(machine)
218
+ && libcName === 'glibc'
219
+ && versionAtLeast(libcVersion, [2, 28])
220
+ if (!isSupportedMac && !isSupportedLinux) {
221
+ throw new SkilledProposerVenvError(
222
+ 'ALI_1505_NO_LOCKED_BINARY_WHEEL',
223
+ `the skilled-proposer lock has no admitted wheel target for ${identity.system} ${identity.machine} (${identity.platform}, ${libcName || 'unknown-libc'} ${libcVersion || ''})`,
224
+ )
225
+ }
226
+ }
227
+
228
+ function spawnPython(python, args, env, timeout = PYTHON_PROBE_TIMEOUT_MILLISECONDS) {
229
+ return spawnSync(python, args, { encoding: 'utf8', env, timeout })
230
+ }
231
+
232
+ function isolatedPythonProbeEnv() {
233
+ const env = { ...process.env, PYTHONNOUSERSITE: '1' }
234
+ for (const name of Object.keys(env)) {
235
+ if (name.startsWith('PIP_')) delete env[name]
236
+ }
237
+ for (const name of [
238
+ 'PYTHONHOME',
239
+ 'PYTHONPATH',
240
+ 'PYTHONUSERBASE',
241
+ 'REQUESTS_CA_BUNDLE',
242
+ 'CURL_CA_BUNDLE',
243
+ ]) delete env[name]
244
+ env.PIP_CONFIG_FILE = devNull
245
+ env.PIP_DISABLE_PIP_VERSION_CHECK = '1'
246
+ env.PIP_NO_INPUT = '1'
247
+ return env
248
+ }
249
+
250
+ export function characterizePython(python, probeEnv = isolatedPythonProbeEnv()) {
251
+ const result = spawnPython(python, ['-c', PYTHON_IDENTITY_PROGRAM], probeEnv)
252
+ if (result.error || result.status !== 0) {
253
+ const detail = result.error?.message || result.stderr || result.stdout || 'no subprocess output'
254
+ throw new SkilledProposerVenvError(
255
+ 'ALI_1505_PYTHON_PREFLIGHT_FAILED',
256
+ `selected Python preflight failed: ${detail.trim()}`,
257
+ )
258
+ }
259
+ const identity = parsePythonIdentity(result.stdout, python)
260
+ const [major, minor] = identity.version
261
+ if (String(identity.implementation).toLowerCase() !== 'cpython'
262
+ || major !== 3 || minor < 10 || minor >= 15) {
263
+ throw new SkilledProposerVenvError(
264
+ 'ALI_1505_UNSUPPORTED_CPYTHON',
265
+ `skilled-proposer requires CPython >=3.10,<3.15; received ${identity.implementation} ${identity.version.join('.')}`,
266
+ )
267
+ }
268
+ assertSupportedWheelTarget(identity)
269
+ return {
270
+ ...identity,
271
+ executable: realpathSync(identity.executable),
272
+ }
273
+ }
274
+
275
+ function readAndValidateHashLock(lockPath) {
276
+ let contents
277
+ try {
278
+ contents = readFileSync(lockPath, 'utf8')
279
+ } catch (error) {
280
+ if (error?.code === 'ENOENT') {
281
+ throw new SkilledProposerVenvError(
282
+ 'ALI_1505_LOCK_MISSING',
283
+ `skilled-proposer lock file does not exist: ${lockPath}`,
284
+ )
285
+ }
286
+ throw error
287
+ }
288
+
289
+ const requirements = []
290
+ let pending = ''
291
+ for (const line of contents.split(/\r?\n/)) {
292
+ const trimmed = line.trim()
293
+ if (!trimmed || trimmed.startsWith('#')) continue
294
+ pending += `${pending ? ' ' : ''}${trimmed.replace(/\\$/, '').trim()}`
295
+ if (!trimmed.endsWith('\\')) {
296
+ requirements.push(pending)
297
+ pending = ''
298
+ }
299
+ }
300
+ if (pending) requirements.push(pending)
301
+ if (requirements.length === 0) {
302
+ throw new SkilledProposerVenvError(
303
+ 'ALI_1505_LOCK_UNHASHED_REQUIREMENT',
304
+ `skilled-proposer lock file contains no requirements: ${lockPath}`,
305
+ )
306
+ }
307
+ for (const requirement of requirements) {
308
+ const isExactPin = /^[A-Za-z0-9_.-]+(?:\[[^\]]+\])?==[^\s;]+/.test(requirement)
309
+ const hashes = [...requirement.matchAll(/(?:^|\s)--hash=([^\s]+)/g)].map(match => match[1])
310
+ const hashTokens = [...requirement.matchAll(/(?:^|\s)(--hash(?:=[^\s]*)?)(?=\s|$)/g)]
311
+ .map(match => match[1])
312
+ const hasMalformedHash = hashes.some(hash => !/^sha256:[a-fA-F0-9]{64}$/.test(hash))
313
+ || hashTokens.length !== hashes.length
314
+ if (hasMalformedHash) {
315
+ throw new SkilledProposerVenvError(
316
+ 'ALI_1505_LOCK_FORMAT_INVALID',
317
+ `dependency contains a malformed sha256 hash: ${requirement}`,
318
+ )
319
+ }
320
+ if (!isExactPin || hashes.length === 0) {
321
+ throw new SkilledProposerVenvError(
322
+ 'ALI_1505_LOCK_UNHASHED_REQUIREMENT',
323
+ `every dependency must be an exact pin with a sha256 hash: ${requirement}`,
324
+ )
325
+ }
326
+ }
327
+ return contents
328
+ }
329
+
330
+ function resolveSslCertFile(pythonIdentity, probeEnv) {
331
+ if (process.env.SSL_CERT_FILE) return process.env.SSL_CERT_FILE
332
+ if (testHook('ORIZU_ALI1505_TEST_FORCE_CA_UNRESOLVED') === '1') return null
333
+ const result = spawnPython(pythonIdentity.executable, ['-c', SSL_CERT_FILE_PROGRAM], probeEnv)
334
+ const candidate = result.status === 0 ? result.stdout.trim() : ''
335
+ if (candidate && existsSync(candidate)) return candidate
336
+ for (const commonPath of [
337
+ '/etc/ssl/cert.pem',
338
+ '/etc/ssl/certs/ca-certificates.crt',
339
+ '/etc/pki/tls/certs/ca-bundle.crt',
340
+ ]) {
341
+ if (existsSync(commonPath)) return commonPath
342
+ }
343
+ return null
344
+ }
345
+
346
+ function buildPublishKey(pythonIdentity, lockContents) {
347
+ const lockDigest = createHash('sha256').update(lockContents).digest('hex')
348
+ const identity = {
349
+ managerSchemaVersion: MANAGER_SCHEMA_VERSION,
350
+ implementation: pythonIdentity.implementation,
351
+ version: pythonIdentity.version,
352
+ executable: pythonIdentity.executable,
353
+ platform: pythonIdentity.platform,
354
+ cacheTag: pythonIdentity.cacheTag,
355
+ soabi: pythonIdentity.soabi,
356
+ system: pythonIdentity.system,
357
+ machine: pythonIdentity.machine,
358
+ osVersion: pythonIdentity.osVersion,
359
+ libc: pythonIdentity.libc,
360
+ lockDigest,
361
+ }
362
+ return {
363
+ lockDigest,
364
+ publishKey: createHash('sha256').update(JSON.stringify(identity)).digest('hex'),
365
+ }
366
+ }
367
+
368
+ function venvPython(venv) {
369
+ return process.platform === 'win32' ? join(venv, 'Scripts', 'python.exe') : join(venv, 'bin', 'python')
370
+ }
371
+
372
+ function runChecked(
373
+ python,
374
+ args,
375
+ env,
376
+ code,
377
+ description,
378
+ report = {},
379
+ timeout = VALIDATION_TIMEOUT_MILLISECONDS,
380
+ ) {
381
+ const result = spawnPython(python, args, env, timeout)
382
+ if (result.error || result.status !== 0) {
383
+ const detail = result.error?.message || result.stderr || result.stdout || 'no subprocess output'
384
+ throw new SkilledProposerVenvError(code, `${description}: ${detail.trim()}`, report)
385
+ }
386
+ }
387
+
388
+ function readValidManifest(venv, publishKey, lockDigest) {
389
+ try {
390
+ const manifest = JSON.parse(readFileSync(join(venv, MANIFEST_NAME), 'utf8'))
391
+ if (manifest.managerSchemaVersion !== MANAGER_SCHEMA_VERSION) return null
392
+ if (manifest.publishKey !== publishKey || manifest.lockDigest !== lockDigest) return null
393
+ if (!existsSync(join(venv, 'pyvenv.cfg'))) return null
394
+ if (!existsSync(venvPython(venv))) return null
395
+ return manifest
396
+ } catch {
397
+ return null
398
+ }
399
+ }
400
+
401
+ function smokeEnvironment(childEnv, pythonPathEntries) {
402
+ return {
403
+ ...childEnv,
404
+ PYTHONPATH: pythonPathEntries.join(delimiter),
405
+ }
406
+ }
407
+
408
+ function validatePublishedVenv(venv, manifest, childEnv, pythonPathEntries) {
409
+ if (!manifest) return null
410
+ try {
411
+ const python = venvPython(venv)
412
+ runChecked(
413
+ python,
414
+ ['-m', 'pip', 'check'],
415
+ childEnv,
416
+ 'ALI_1505_PIP_CHECK_FAILED',
417
+ 'published venv failed pip check',
418
+ )
419
+ runChecked(
420
+ python,
421
+ ['-c', IMPORT_SMOKE_PROGRAM],
422
+ smokeEnvironment(childEnv, pythonPathEntries),
423
+ 'ALI_1505_IMPORT_SMOKE_FAILED',
424
+ 'published venv failed import smoke test',
425
+ )
426
+ return manifest
427
+ } catch {
428
+ return null
429
+ }
430
+ }
431
+
432
+ function wait(milliseconds) {
433
+ return new Promise(resolve => setTimeout(resolve, milliseconds))
434
+ }
435
+
436
+ function testHook(name) {
437
+ if (process.env.ORIZU_ALI1505_TEST_HOOKS !== '1') return undefined
438
+ return process.env[name]
439
+ }
440
+
441
+ async function waitAtTestBarrier(cacheRoot) {
442
+ const barrier = testHook('ORIZU_ALI1505_TEST_START_BARRIER')
443
+ if (!barrier) return
444
+ const readyDirectory = join(cacheRoot, 'test-start-barrier-ready')
445
+ mkdirSync(readyDirectory, { recursive: true })
446
+ writeFileSync(join(readyDirectory, `${process.pid}-${randomUUID()}`), '')
447
+ const startedAt = Date.now()
448
+ while (existsSync(barrier)) {
449
+ if (Date.now() - startedAt >= TEST_BARRIER_TIMEOUT_MILLISECONDS) {
450
+ throw new SkilledProposerVenvError(
451
+ 'ALI_1505_TEST_START_BARRIER_TIMEOUT',
452
+ `timed out waiting for ALI-1505 test barrier: ${barrier}`,
453
+ )
454
+ }
455
+ await wait(LOCK_WAIT_MILLISECONDS)
456
+ }
457
+ }
458
+
459
+ function acquirePublishLock(python, lockFile, env) {
460
+ return new Promise((resolve, reject) => {
461
+ const child = spawn(python, ['-u', '-c', PUBLISH_LOCK_PROGRAM, lockFile, String(process.pid)], {
462
+ env,
463
+ stdio: ['pipe', 'pipe', 'pipe'],
464
+ })
465
+ let stdout = ''
466
+ let stderr = ''
467
+ let waitedForPublishLock = false
468
+ let acquired = false
469
+ let guardianPid = null
470
+ const timeout = setTimeout(() => {
471
+ child.kill()
472
+ reject(new SkilledProposerVenvError(
473
+ 'ALI_1505_PUBLISH_LOCK_TIMEOUT',
474
+ `timed out waiting for publish lock: ${lockFile}`,
475
+ { waitedForPublishLock: true },
476
+ ))
477
+ }, LOCK_TIMEOUT_MILLISECONDS)
478
+
479
+ child.stdout.setEncoding('utf8')
480
+ child.stderr.setEncoding('utf8')
481
+ child.stderr.on('data', chunk => { stderr += chunk })
482
+ child.stdout.on('data', chunk => {
483
+ stdout += chunk
484
+ const lines = stdout.split(/\r?\n/)
485
+ stdout = lines.pop() || ''
486
+ for (const line of lines) {
487
+ if (line === 'waiting') waitedForPublishLock = true
488
+ if (line.startsWith('guardian:')) guardianPid = Number.parseInt(line.slice('guardian:'.length), 10)
489
+ if (line === 'acquired' && !acquired) {
490
+ acquired = true
491
+ clearTimeout(timeout)
492
+ if (!Number.isSafeInteger(guardianPid) || guardianPid <= 0) {
493
+ child.kill()
494
+ reject(new SkilledProposerVenvError(
495
+ 'ALI_1505_PUBLISH_LOCK_FAILED',
496
+ 'publish-lock holder did not report a guardian process',
497
+ { waitedForPublishLock },
498
+ ))
499
+ return
500
+ }
501
+ resolve({ child, guardianPid, waitedForPublishLock })
502
+ }
503
+ }
504
+ })
505
+ child.once('error', error => {
506
+ if (acquired) return
507
+ clearTimeout(timeout)
508
+ reject(new SkilledProposerVenvError(
509
+ 'ALI_1505_PUBLISH_LOCK_FAILED',
510
+ `failed to start publish-lock holder: ${error.message}`,
511
+ { waitedForPublishLock },
512
+ ))
513
+ })
514
+ child.once('exit', (code, signal) => {
515
+ if (acquired) return
516
+ clearTimeout(timeout)
517
+ reject(new SkilledProposerVenvError(
518
+ 'ALI_1505_PUBLISH_LOCK_FAILED',
519
+ `publish-lock holder exited before acquisition (${signal || code}): ${stderr.trim() || stdout.trim() || 'no subprocess output'}`,
520
+ { waitedForPublishLock },
521
+ ))
522
+ })
523
+ })
524
+ }
525
+
526
+ async function releasePublishLock(lock) {
527
+ if (!lock?.child) return
528
+ if (lock.child.exitCode !== null || lock.child.signalCode !== null) {
529
+ try {
530
+ process.kill(lock.guardianPid, 'SIGTERM')
531
+ } catch (error) {
532
+ if (error?.code !== 'ESRCH') throw error
533
+ }
534
+ return
535
+ }
536
+ const exited = new Promise(resolve => lock.child.once('exit', resolve))
537
+ lock.child.stdin.end('release\n')
538
+ const timeout = setTimeout(() => lock.child.kill(), PYTHON_PROBE_TIMEOUT_MILLISECONDS)
539
+ await exited
540
+ clearTimeout(timeout)
541
+ }
542
+
543
+ async function killPublishLockLeaderAtTestHook(lock) {
544
+ if (testHook('ORIZU_ALI1505_TEST_KILL_PUBLISH_LOCK_LEADER') !== 'after-acquire') return
545
+ const exited = new Promise(resolve => lock.child.once('exit', resolve))
546
+ lock.child.kill('SIGKILL')
547
+ await exited
548
+ }
549
+
550
+ function removeAbandonedStages(venvsRoot, publishKey) {
551
+ const stagePrefix = `.${publishKey}-`
552
+ for (const entry of readdirSync(venvsRoot)) {
553
+ const stagePath = join(venvsRoot, entry)
554
+ const isCurrentKeyStage = entry.startsWith(stagePrefix) && entry.endsWith('.tmp')
555
+ const isManagedStage = /^\.[a-f0-9]{64}-.+\.tmp$/.test(entry)
556
+ let isAgedOtherKeyStage = false
557
+ if (isManagedStage) {
558
+ try {
559
+ isAgedOtherKeyStage = Date.now() - statSync(stagePath).mtimeMs
560
+ >= ABANDONED_STAGE_MAX_AGE_MILLISECONDS
561
+ } catch (error) {
562
+ if (error?.code === 'ENOENT') continue
563
+ throw error
564
+ }
565
+ }
566
+ // The current publish lock makes every same-key stage abandoned. Across
567
+ // keys, the 24-hour floor is well above the 25-minute guardian lifetime,
568
+ // so an admitted live builder is never collected without its own lock.
569
+ if (isCurrentKeyStage || isAgedOtherKeyStage) {
570
+ rmSync(stagePath, { recursive: true, force: true })
571
+ }
572
+ }
573
+ }
574
+
575
+ function installArguments(options, stagePython) {
576
+ const args = [
577
+ stagePython,
578
+ '-m', 'pip', 'install',
579
+ '--require-hashes',
580
+ '--only-binary=:all:',
581
+ ]
582
+ if (options.noIndex) args.push('--no-index', '--find-links', options.wheelhouse)
583
+ args.push('--requirement', options.lock)
584
+ return args
585
+ }
586
+
587
+ function installFailureCode(result) {
588
+ const output = `${result.stdout || ''}\n${result.stderr || ''}`
589
+ if (/do not match the hashes|hash mismatch/i.test(output)) {
590
+ return 'ALI_1505_ARTIFACT_HASH_MISMATCH'
591
+ }
592
+ if (/hashes are required/i.test(output)) return 'ALI_1505_LOCK_UNHASHED_REQUIREMENT'
593
+ if (/no matching distribution|could not find a version|not a supported wheel/i.test(output)) {
594
+ return 'ALI_1505_NO_LOCKED_BINARY_WHEEL'
595
+ }
596
+ return 'ALI_1505_PIP_INSTALL_FAILED'
597
+ }
598
+
599
+ function isolatedChildEnv(sslCertFile) {
600
+ const env = { ...isolatedPythonProbeEnv() }
601
+ if (sslCertFile) env.SSL_CERT_FILE = sslCertFile
602
+ else delete env.SSL_CERT_FILE
603
+ return env
604
+ }
605
+
606
+ export async function ensureSkilledProposerVenv(options) {
607
+ // This must stay first: unsupported interpreters may not create cache state or
608
+ // invoke a mutating `python -m` command.
609
+ const probeEnv = isolatedPythonProbeEnv()
610
+ const pythonIdentity = characterizePython(options.python, probeEnv)
611
+ const lockContents = readAndValidateHashLock(options.lock)
612
+ const sslCertFile = resolveSslCertFile(pythonIdentity, probeEnv)
613
+ const childEnv = isolatedChildEnv(sslCertFile)
614
+ const { lockDigest, publishKey } = buildPublishKey(pythonIdentity, lockContents)
615
+ const finalVenv = join(options.cacheRoot, 'venvs', publishKey)
616
+ const lockFile = join(options.cacheRoot, 'publish-locks', `${publishKey}.lock`)
617
+ const pythonPathEntries = options.vendoredGepaPath ? [options.vendoredGepaPath] : []
618
+ const baseReport = {
619
+ venv: finalVenv,
620
+ publishKey,
621
+ publishedVenv: false,
622
+ waitedForPublishLock: false,
623
+ pythonPathEntries,
624
+ executedPipArgv: null,
625
+ sslCertFile,
626
+ warnings: sslCertFile ? [] : [SSL_CERT_FILE_UNRESOLVED_WARNING],
627
+ }
628
+
629
+ mkdirSync(join(options.cacheRoot, 'venvs'), { recursive: true })
630
+ mkdirSync(join(options.cacheRoot, 'publish-locks'), { recursive: true })
631
+ await waitAtTestBarrier(options.cacheRoot)
632
+
633
+ const existing = validatePublishedVenv(
634
+ finalVenv,
635
+ readValidManifest(finalVenv, publishKey, lockDigest),
636
+ childEnv,
637
+ pythonPathEntries,
638
+ )
639
+ if (existing) return baseReport
640
+
641
+ const lock = await acquirePublishLock(pythonIdentity.executable, lockFile, childEnv)
642
+ await killPublishLockLeaderAtTestHook(lock)
643
+ const lockedReport = { ...baseReport, waitedForPublishLock: lock.waitedForPublishLock }
644
+
645
+ const stage = join(options.cacheRoot, 'venvs', `.${publishKey}-${process.pid}-${randomUUID()}.tmp`)
646
+ try {
647
+ // Holding the per-key publish lock proves that no live builder for this key
648
+ // can own one of these sibling staging directories.
649
+ removeAbandonedStages(join(options.cacheRoot, 'venvs'), publishKey)
650
+ const afterLockExisting = validatePublishedVenv(
651
+ finalVenv,
652
+ readValidManifest(finalVenv, publishKey, lockDigest),
653
+ childEnv,
654
+ pythonPathEntries,
655
+ )
656
+ if (afterLockExisting) {
657
+ return lockedReport
658
+ }
659
+ if (existsSync(finalVenv)) rmSync(finalVenv, { recursive: true, force: true })
660
+
661
+ runChecked(
662
+ pythonIdentity.executable,
663
+ ['-m', 'venv', stage],
664
+ childEnv,
665
+ 'ALI_1505_VENV_CREATE_FAILED',
666
+ 'failed to create staged venv',
667
+ lockedReport,
668
+ VENV_CREATE_TIMEOUT_MILLISECONDS,
669
+ )
670
+ const stagePython = venvPython(stage)
671
+ const executedPipArgv = installArguments(options, stagePython)
672
+ const installingReport = { ...lockedReport, executedPipArgv }
673
+ const installResult = spawnPython(
674
+ stagePython,
675
+ executedPipArgv.slice(1),
676
+ childEnv,
677
+ PIP_INSTALL_TIMEOUT_MILLISECONDS,
678
+ )
679
+ if (installResult.error || installResult.status !== 0) {
680
+ const detail = installResult.error?.message || installResult.stderr || installResult.stdout || 'no subprocess output'
681
+ throw new SkilledProposerVenvError(
682
+ installFailureCode(installResult),
683
+ `failed to install locked dependencies: ${detail.trim()}`,
684
+ installingReport,
685
+ )
686
+ }
687
+
688
+ if (testHook('ORIZU_ALI1505_TEST_INTERRUPT_AFTER') === 'staged-install') {
689
+ throw new SkilledProposerVenvError(
690
+ 'ALI_1505_TEST_INTERRUPT_AFTER_STAGED_INSTALL',
691
+ 'interrupted after staged install by ALI-1505 test hook',
692
+ { ...installingReport, publishedVenv: null },
693
+ )
694
+ }
695
+
696
+ runChecked(
697
+ stagePython,
698
+ ['-m', 'pip', 'check'],
699
+ childEnv,
700
+ 'ALI_1505_PIP_CHECK_FAILED',
701
+ 'staged venv failed pip check',
702
+ installingReport,
703
+ )
704
+ runChecked(
705
+ stagePython,
706
+ ['-c', IMPORT_SMOKE_PROGRAM],
707
+ smokeEnvironment(childEnv, pythonPathEntries),
708
+ 'ALI_1505_IMPORT_SMOKE_FAILED',
709
+ 'staged venv failed import smoke test',
710
+ installingReport,
711
+ )
712
+ writeFileSync(join(stage, MANIFEST_NAME), `${JSON.stringify({
713
+ managerSchemaVersion: MANAGER_SCHEMA_VERSION,
714
+ publishKey,
715
+ lockDigest,
716
+ pythonIdentity,
717
+ executedPipArgv,
718
+ }, null, 2)}\n`)
719
+ try {
720
+ renameSync(stage, finalVenv)
721
+ } catch (error) {
722
+ throw new SkilledProposerVenvError(
723
+ 'ALI_1505_VENV_PUBLISH_FAILED',
724
+ `failed to publish the validated venv atomically: ${error instanceof Error ? error.message : String(error)}`,
725
+ installingReport,
726
+ )
727
+ }
728
+ return { ...installingReport, publishedVenv: true }
729
+ } finally {
730
+ rmSync(stage, { recursive: true, force: true })
731
+ await releasePublishLock(lock)
732
+ }
733
+ }
734
+
735
+ export async function runManagerCli(argv) {
736
+ let options = { json: argv.includes('--json') }
737
+ try {
738
+ options = parseManagerArguments(argv)
739
+ const report = await ensureSkilledProposerVenv(options)
740
+ process.stdout.write(`${JSON.stringify(report)}\n`)
741
+ return 0
742
+ } catch (error) {
743
+ const managedError = error instanceof SkilledProposerVenvError
744
+ ? error
745
+ : new SkilledProposerVenvError('ALI_1505_VENV_MANAGER_FAILED', String(error))
746
+ if (options.json) {
747
+ process.stdout.write(`${JSON.stringify({
748
+ error: managedError.code,
749
+ message: managedError.message,
750
+ ...managedError.report,
751
+ publishedVenv: null,
752
+ })}\n`)
753
+ }
754
+ process.stderr.write(`${managedError.code}: ${managedError.message}\n`)
755
+ return 1
756
+ }
757
+ }