agent-hitch 0.2.6 → 0.2.8

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 (562) hide show
  1. package/README.md +30 -3
  2. package/README.zh-CN.md +14 -2
  3. package/dist/bin/hitch.js +25 -3
  4. package/dist/bin/hitch.js.map +1 -1
  5. package/dist/scripts/canary-benchmark.js +54 -0
  6. package/dist/scripts/canary-benchmark.js.map +1 -0
  7. package/dist/scripts/canary-buildkit-secrets.js +166 -0
  8. package/dist/scripts/canary-buildkit-secrets.js.map +1 -0
  9. package/dist/scripts/canary-collision-domains.js +77 -0
  10. package/dist/scripts/canary-collision-domains.js.map +1 -0
  11. package/dist/scripts/canary-docker-image-observation.js +181 -0
  12. package/dist/scripts/canary-docker-image-observation.js.map +1 -0
  13. package/dist/scripts/canary-eval-scheduler-throughput.js +47 -0
  14. package/dist/scripts/canary-eval-scheduler-throughput.js.map +1 -0
  15. package/dist/scripts/canary-gpu-hardware.js +95 -0
  16. package/dist/scripts/canary-gpu-hardware.js.map +1 -0
  17. package/dist/scripts/canary-harbor-load.js +365 -0
  18. package/dist/scripts/canary-harbor-load.js.map +1 -0
  19. package/dist/scripts/canary-packaged-harness.js +75 -0
  20. package/dist/scripts/canary-packaged-harness.js.map +1 -0
  21. package/dist/scripts/canary-resource-load.js +188 -0
  22. package/dist/scripts/canary-resource-load.js.map +1 -0
  23. package/dist/scripts/check-architecture.js +10 -5
  24. package/dist/scripts/check-architecture.js.map +1 -1
  25. package/dist/scripts/docker-reaper-crash-child.js +21 -0
  26. package/dist/scripts/docker-reaper-crash-child.js.map +1 -0
  27. package/dist/src/adapters/catalog.js +3 -1
  28. package/dist/src/adapters/catalog.js.map +1 -1
  29. package/dist/src/adapters/discovery.js +1 -1
  30. package/dist/src/adapters/discovery.js.map +1 -1
  31. package/dist/src/adapters/providers/claude.js +6 -0
  32. package/dist/src/adapters/providers/claude.js.map +1 -1
  33. package/dist/src/adapters/providers/codex-auth.js +29 -0
  34. package/dist/src/adapters/providers/codex-auth.js.map +1 -0
  35. package/dist/src/adapters/providers/codex.js +29 -6
  36. package/dist/src/adapters/providers/codex.js.map +1 -1
  37. package/dist/src/adapters/providers/deepseek.js +6 -0
  38. package/dist/src/adapters/providers/deepseek.js.map +1 -1
  39. package/dist/src/adapters/providers/model-call.js +26 -0
  40. package/dist/src/adapters/providers/model-call.js.map +1 -0
  41. package/dist/src/adapters/providers/opencode.js +6 -0
  42. package/dist/src/adapters/providers/opencode.js.map +1 -1
  43. package/dist/src/adapters/providers/pi.js +6 -0
  44. package/dist/src/adapters/providers/pi.js.map +1 -1
  45. package/dist/src/artifacts/index.js +1 -0
  46. package/dist/src/artifacts/index.js.map +1 -1
  47. package/dist/src/artifacts/preparer.js +7 -2
  48. package/dist/src/artifacts/preparer.js.map +1 -1
  49. package/dist/src/backends/harbor/agent-budget.js +31 -0
  50. package/dist/src/backends/harbor/agent-budget.js.map +1 -0
  51. package/dist/src/backends/harbor/backend.js +59 -107
  52. package/dist/src/backends/harbor/backend.js.map +1 -1
  53. package/dist/src/backends/harbor/environment-config.js +92 -0
  54. package/dist/src/backends/harbor/environment-config.js.map +1 -0
  55. package/dist/src/backends/harbor/index.js +6 -1
  56. package/dist/src/backends/harbor/index.js.map +1 -1
  57. package/dist/src/backends/harbor/model-proxy-config.js +23 -0
  58. package/dist/src/backends/harbor/model-proxy-config.js.map +1 -0
  59. package/dist/src/backends/harbor/process.js +177 -0
  60. package/dist/src/backends/harbor/process.js.map +1 -0
  61. package/dist/src/backends/harbor/regrade.js +77 -0
  62. package/dist/src/backends/harbor/regrade.js.map +1 -0
  63. package/dist/src/backends/harbor/runtime-toolchain.js +30 -0
  64. package/dist/src/backends/harbor/runtime-toolchain.js.map +1 -0
  65. package/dist/src/backends/harbor/supervisor.js +74 -0
  66. package/dist/src/backends/harbor/supervisor.js.map +1 -0
  67. package/dist/src/backends/harbor/task-resources.js +206 -0
  68. package/dist/src/backends/harbor/task-resources.js.map +1 -0
  69. package/dist/src/backends/harbor/tools.js +6 -29
  70. package/dist/src/backends/harbor/tools.js.map +1 -1
  71. package/dist/src/backends/index.js +5 -1
  72. package/dist/src/backends/index.js.map +1 -1
  73. package/dist/src/benchmarks/index.js +3 -0
  74. package/dist/src/benchmarks/index.js.map +1 -0
  75. package/dist/src/benchmarks/loader.js +271 -0
  76. package/dist/src/benchmarks/loader.js.map +1 -0
  77. package/dist/src/benchmarks/metrics.js +16 -0
  78. package/dist/src/benchmarks/metrics.js.map +1 -0
  79. package/dist/src/benchmarks/toml.js +13 -0
  80. package/dist/src/benchmarks/toml.js.map +1 -0
  81. package/dist/src/benchmarks/validation.js +242 -0
  82. package/dist/src/benchmarks/validation.js.map +1 -0
  83. package/dist/src/cli/arguments.js +6 -4
  84. package/dist/src/cli/arguments.js.map +1 -1
  85. package/dist/src/cli/commands/benchmark.js +22 -0
  86. package/dist/src/cli/commands/benchmark.js.map +1 -0
  87. package/dist/src/cli/commands/capabilities.js +18 -0
  88. package/dist/src/cli/commands/capabilities.js.map +1 -0
  89. package/dist/src/cli/commands/daemon.js +135 -8
  90. package/dist/src/cli/commands/daemon.js.map +1 -1
  91. package/dist/src/cli/commands/eval.js +228 -6
  92. package/dist/src/cli/commands/eval.js.map +1 -1
  93. package/dist/src/cli/commands/images.js +40 -0
  94. package/dist/src/cli/commands/images.js.map +1 -0
  95. package/dist/src/cli/commands/run.js +42 -17
  96. package/dist/src/cli/commands/run.js.map +1 -1
  97. package/dist/src/cli/commands/runs.js +25 -3
  98. package/dist/src/cli/commands/runs.js.map +1 -1
  99. package/dist/src/cli/commands/trajectory.js +96 -5
  100. package/dist/src/cli/commands/trajectory.js.map +1 -1
  101. package/dist/src/cli/commands/verifier.js +36 -0
  102. package/dist/src/cli/commands/verifier.js.map +1 -0
  103. package/dist/src/cli/commands/worker.js +79 -0
  104. package/dist/src/cli/commands/worker.js.map +1 -0
  105. package/dist/src/cli/main.js +10 -0
  106. package/dist/src/cli/main.js.map +1 -1
  107. package/dist/src/cli/output.js +84 -4
  108. package/dist/src/cli/output.js.map +1 -1
  109. package/dist/src/control-plane/build-admission.js +69 -0
  110. package/dist/src/control-plane/build-admission.js.map +1 -0
  111. package/dist/src/control-plane/build-records.js +6 -0
  112. package/dist/src/control-plane/build-records.js.map +1 -0
  113. package/dist/src/control-plane/collisions.js +40 -0
  114. package/dist/src/control-plane/collisions.js.map +1 -0
  115. package/dist/src/control-plane/eval-control-state.js +22 -0
  116. package/dist/src/control-plane/eval-control-state.js.map +1 -0
  117. package/dist/src/control-plane/eval-control-work.js +44 -0
  118. package/dist/src/control-plane/eval-control-work.js.map +1 -0
  119. package/dist/src/control-plane/eval-image-resolution.js +52 -0
  120. package/dist/src/control-plane/eval-image-resolution.js.map +1 -0
  121. package/dist/src/control-plane/eval-image-services.js +30 -0
  122. package/dist/src/control-plane/eval-image-services.js.map +1 -0
  123. package/dist/src/control-plane/eval-records.js +252 -0
  124. package/dist/src/control-plane/eval-records.js.map +1 -0
  125. package/dist/src/control-plane/eval-recovery.js +181 -0
  126. package/dist/src/control-plane/eval-recovery.js.map +1 -0
  127. package/dist/src/control-plane/eval-scheduler.js +450 -0
  128. package/dist/src/control-plane/eval-scheduler.js.map +1 -0
  129. package/dist/src/control-plane/index.js +20 -0
  130. package/dist/src/control-plane/index.js.map +1 -0
  131. package/dist/src/control-plane/local-worker.js +49 -0
  132. package/dist/src/control-plane/local-worker.js.map +1 -0
  133. package/dist/src/control-plane/model-capture-planning.js +12 -0
  134. package/dist/src/control-plane/model-capture-planning.js.map +1 -0
  135. package/dist/src/control-plane/remote-result-transport.js +175 -0
  136. package/dist/src/control-plane/remote-result-transport.js.map +1 -0
  137. package/dist/src/control-plane/remote-work-coordinator.js +333 -0
  138. package/dist/src/control-plane/remote-work-coordinator.js.map +1 -0
  139. package/dist/src/control-plane/remote-work-inputs.js +266 -0
  140. package/dist/src/control-plane/remote-work-inputs.js.map +1 -0
  141. package/dist/src/control-plane/remote-work-item.js +62 -0
  142. package/dist/src/control-plane/remote-work-item.js.map +1 -0
  143. package/dist/src/control-plane/remote-work-recovery.js +220 -0
  144. package/dist/src/control-plane/remote-work-recovery.js.map +1 -0
  145. package/dist/src/control-plane/remote-worker-artifacts.js +135 -0
  146. package/dist/src/control-plane/remote-worker-artifacts.js.map +1 -0
  147. package/dist/src/control-plane/remote-worker-client.js +202 -0
  148. package/dist/src/control-plane/remote-worker-client.js.map +1 -0
  149. package/dist/src/control-plane/remote-worker-configuration.js +34 -0
  150. package/dist/src/control-plane/remote-worker-configuration.js.map +1 -0
  151. package/dist/src/control-plane/remote-worker-credentials.js +58 -0
  152. package/dist/src/control-plane/remote-worker-credentials.js.map +1 -0
  153. package/dist/src/control-plane/remote-worker-protocol.js +489 -0
  154. package/dist/src/control-plane/remote-worker-protocol.js.map +1 -0
  155. package/dist/src/control-plane/remote-worker-runner.js +214 -0
  156. package/dist/src/control-plane/remote-worker-runner.js.map +1 -0
  157. package/dist/src/control-plane/remote-workers.js +377 -0
  158. package/dist/src/control-plane/remote-workers.js.map +1 -0
  159. package/dist/src/control-plane/rerun-scheduler.js +448 -0
  160. package/dist/src/control-plane/rerun-scheduler.js.map +1 -0
  161. package/dist/src/control-plane/rerun-submission.js +62 -0
  162. package/dist/src/control-plane/rerun-submission.js.map +1 -0
  163. package/dist/src/control-plane/resources.js +182 -0
  164. package/dist/src/control-plane/resources.js.map +1 -0
  165. package/dist/src/control-plane/scheduler-eval-entry.js +32 -0
  166. package/dist/src/control-plane/scheduler-eval-entry.js.map +1 -0
  167. package/dist/src/control-plane/synthetic-result.js +21 -0
  168. package/dist/src/control-plane/synthetic-result.js.map +1 -0
  169. package/dist/src/control-plane/work-admission.js +23 -0
  170. package/dist/src/control-plane/work-admission.js.map +1 -0
  171. package/dist/src/control-plane/work-dispatcher.js +237 -0
  172. package/dist/src/control-plane/work-dispatcher.js.map +1 -0
  173. package/dist/src/controller-runtime/hash.js +19 -3
  174. package/dist/src/controller-runtime/hash.js.map +1 -1
  175. package/dist/src/controller-runtime/index.js +1 -1
  176. package/dist/src/controller-runtime/index.js.map +1 -1
  177. package/dist/src/controller-runtime/store.js +15 -6
  178. package/dist/src/controller-runtime/store.js.map +1 -1
  179. package/dist/src/daemon/auth.js +5 -0
  180. package/dist/src/daemon/auth.js.map +1 -1
  181. package/dist/src/daemon/client.js.map +1 -1
  182. package/dist/src/daemon/event-stream.js +46 -0
  183. package/dist/src/daemon/event-stream.js.map +1 -0
  184. package/dist/src/daemon/health.js +66 -0
  185. package/dist/src/daemon/health.js.map +1 -0
  186. package/dist/src/daemon/launcher.js +28 -1
  187. package/dist/src/daemon/launcher.js.map +1 -1
  188. package/dist/src/daemon/logging.js +23 -0
  189. package/dist/src/daemon/logging.js.map +1 -0
  190. package/dist/src/daemon/scheduler.js +26 -5
  191. package/dist/src/daemon/scheduler.js.map +1 -1
  192. package/dist/src/daemon/server.js +255 -98
  193. package/dist/src/daemon/server.js.map +1 -1
  194. package/dist/src/daemon/telemetry.js +163 -0
  195. package/dist/src/daemon/telemetry.js.map +1 -0
  196. package/dist/src/daemon/worker-routes.js +219 -0
  197. package/dist/src/daemon/worker-routes.js.map +1 -0
  198. package/dist/src/domain/benchmarks.js +2 -0
  199. package/dist/src/domain/benchmarks.js.map +1 -0
  200. package/dist/src/domain/bundles.js +2 -0
  201. package/dist/src/domain/bundles.js.map +1 -0
  202. package/dist/src/domain/control-plane.js +2 -0
  203. package/dist/src/domain/control-plane.js.map +1 -0
  204. package/dist/src/domain/execution-evidence.js +2 -0
  205. package/dist/src/domain/execution-evidence.js.map +1 -0
  206. package/dist/src/domain/execution-plan.js +2 -0
  207. package/dist/src/domain/execution-plan.js.map +1 -0
  208. package/dist/src/domain/images.js +2 -0
  209. package/dist/src/domain/images.js.map +1 -0
  210. package/dist/src/domain/index.js +13 -0
  211. package/dist/src/domain/index.js.map +1 -1
  212. package/dist/src/domain/interactions.js +2 -0
  213. package/dist/src/domain/interactions.js.map +1 -0
  214. package/dist/src/domain/providers.js +2 -0
  215. package/dist/src/domain/providers.js.map +1 -0
  216. package/dist/src/domain/resources.js +2 -0
  217. package/dist/src/domain/resources.js.map +1 -0
  218. package/dist/src/domain/runs.js.map +1 -1
  219. package/dist/src/domain/validation.js +11 -4
  220. package/dist/src/domain/validation.js.map +1 -1
  221. package/dist/src/domain/verifier-evidence-validation.js +214 -0
  222. package/dist/src/domain/verifier-evidence-validation.js.map +1 -0
  223. package/dist/src/domain/verifier-evidence.js +3 -0
  224. package/dist/src/domain/verifier-evidence.js.map +1 -0
  225. package/dist/src/domain/verifier-score-contract.js +232 -0
  226. package/dist/src/domain/verifier-score-contract.js.map +1 -0
  227. package/dist/src/domain/workers.js +2 -0
  228. package/dist/src/domain/workers.js.map +1 -0
  229. package/dist/src/evals/benchmark-adapter-manifest.js +219 -0
  230. package/dist/src/evals/benchmark-adapter-manifest.js.map +1 -0
  231. package/dist/src/evals/benchmark-candidate.js +36 -0
  232. package/dist/src/evals/benchmark-candidate.js.map +1 -0
  233. package/dist/src/evals/benchmark-run.js +166 -0
  234. package/dist/src/evals/benchmark-run.js.map +1 -0
  235. package/dist/src/evals/collect-only-rerun.js +183 -0
  236. package/dist/src/evals/collect-only-rerun.js.map +1 -0
  237. package/dist/src/evals/directory.js +30 -0
  238. package/dist/src/evals/directory.js.map +1 -0
  239. package/dist/src/evals/docker-engine-stats.js +89 -0
  240. package/dist/src/evals/docker-engine-stats.js.map +1 -0
  241. package/dist/src/evals/docker-ownership.js +51 -0
  242. package/dist/src/evals/docker-ownership.js.map +1 -0
  243. package/dist/src/evals/docker-reaper.js +144 -0
  244. package/dist/src/evals/docker-reaper.js.map +1 -0
  245. package/dist/src/evals/docker-resource-observer.js +207 -0
  246. package/dist/src/evals/docker-resource-observer.js.map +1 -0
  247. package/dist/src/evals/duration-estimator.js +126 -0
  248. package/dist/src/evals/duration-estimator.js.map +1 -0
  249. package/dist/src/evals/environment-image-planning.js +164 -0
  250. package/dist/src/evals/environment-image-planning.js.map +1 -0
  251. package/dist/src/evals/eval-artifact-planning.js +56 -0
  252. package/dist/src/evals/eval-artifact-planning.js.map +1 -0
  253. package/dist/src/evals/eval-finalization.js +10 -0
  254. package/dist/src/evals/eval-finalization.js.map +1 -0
  255. package/dist/src/evals/eval-lifecycle-events.js +29 -0
  256. package/dist/src/evals/eval-lifecycle-events.js.map +1 -0
  257. package/dist/src/evals/eval-logical-plan.js +51 -0
  258. package/dist/src/evals/eval-logical-plan.js.map +1 -0
  259. package/dist/src/evals/eval-result-builder.js +81 -0
  260. package/dist/src/evals/eval-result-builder.js.map +1 -0
  261. package/dist/src/evals/events.js +53 -5
  262. package/dist/src/evals/events.js.map +1 -1
  263. package/dist/src/evals/evolution-baseline.js +191 -0
  264. package/dist/src/evals/evolution-baseline.js.map +1 -0
  265. package/dist/src/evals/execution-evidence.js +116 -0
  266. package/dist/src/evals/execution-evidence.js.map +1 -0
  267. package/dist/src/evals/execution-leases.js +342 -0
  268. package/dist/src/evals/execution-leases.js.map +1 -0
  269. package/dist/src/evals/execution-plan-artifacts.js +72 -0
  270. package/dist/src/evals/execution-plan-artifacts.js.map +1 -0
  271. package/dist/src/evals/execution-plan-images.js +69 -0
  272. package/dist/src/evals/execution-plan-images.js.map +1 -0
  273. package/dist/src/evals/execution-plan-resources.js +174 -0
  274. package/dist/src/evals/execution-plan-resources.js.map +1 -0
  275. package/dist/src/evals/execution-plan.js +451 -0
  276. package/dist/src/evals/execution-plan.js.map +1 -0
  277. package/dist/src/evals/failure-classifier.js +61 -0
  278. package/dist/src/evals/failure-classifier.js.map +1 -0
  279. package/dist/src/evals/harbor-artifact-builder-image.js +99 -0
  280. package/dist/src/evals/harbor-artifact-builder-image.js.map +1 -0
  281. package/dist/src/evals/harbor-artifact-builder.js +358 -0
  282. package/dist/src/evals/harbor-artifact-builder.js.map +1 -0
  283. package/dist/src/evals/harbor-bridge-error.js +58 -3
  284. package/dist/src/evals/harbor-bridge-error.js.map +1 -1
  285. package/dist/src/evals/harbor-node-runtime.js +146 -0
  286. package/dist/src/evals/harbor-node-runtime.js.map +1 -0
  287. package/dist/src/evals/harbor-phase-timings.js +60 -0
  288. package/dist/src/evals/harbor-phase-timings.js.map +1 -0
  289. package/dist/src/evals/index.js +40 -2
  290. package/dist/src/evals/index.js.map +1 -1
  291. package/dist/src/evals/infrastructure-retry.js +208 -84
  292. package/dist/src/evals/infrastructure-retry.js.map +1 -1
  293. package/dist/src/evals/interaction-capture-import.js +49 -0
  294. package/dist/src/evals/interaction-capture-import.js.map +1 -0
  295. package/dist/src/evals/local-docker-provider.js +372 -0
  296. package/dist/src/evals/local-docker-provider.js.map +1 -0
  297. package/dist/src/evals/local-eval-planning.js +80 -0
  298. package/dist/src/evals/local-eval-planning.js.map +1 -0
  299. package/dist/src/evals/model-capture-plan.js +22 -0
  300. package/dist/src/evals/model-capture-plan.js.map +1 -0
  301. package/dist/src/evals/model-capture-runtime.js +114 -0
  302. package/dist/src/evals/model-capture-runtime.js.map +1 -0
  303. package/dist/src/evals/model-proxy-runtime-state.js +53 -0
  304. package/dist/src/evals/model-proxy-runtime-state.js.map +1 -0
  305. package/dist/src/evals/native-phase-evidence.js +440 -0
  306. package/dist/src/evals/native-phase-evidence.js.map +1 -0
  307. package/dist/src/evals/physical-retry-work.js +31 -0
  308. package/dist/src/evals/physical-retry-work.js.map +1 -0
  309. package/dist/src/evals/planned-execution-support.js +66 -0
  310. package/dist/src/evals/planned-execution-support.js.map +1 -0
  311. package/dist/src/evals/planned-execution.js +469 -0
  312. package/dist/src/evals/planned-execution.js.map +1 -0
  313. package/dist/src/evals/planned-progress-publisher.js +62 -0
  314. package/dist/src/evals/planned-progress-publisher.js.map +1 -0
  315. package/dist/src/evals/planned-remote-execution.js +40 -0
  316. package/dist/src/evals/planned-remote-execution.js.map +1 -0
  317. package/dist/src/evals/planned-retry-execution.js +174 -0
  318. package/dist/src/evals/planned-retry-execution.js.map +1 -0
  319. package/dist/src/evals/planned-retry-lifecycle.js +188 -0
  320. package/dist/src/evals/planned-retry-lifecycle.js.map +1 -0
  321. package/dist/src/evals/preparation-rerun.js +225 -0
  322. package/dist/src/evals/preparation-rerun.js.map +1 -0
  323. package/dist/src/evals/prepared-harness.js +76 -0
  324. package/dist/src/evals/prepared-harness.js.map +1 -0
  325. package/dist/src/evals/progress.js +52 -12
  326. package/dist/src/evals/progress.js.map +1 -1
  327. package/dist/src/evals/records.js +20 -3
  328. package/dist/src/evals/records.js.map +1 -1
  329. package/dist/src/evals/recovery.js +242 -0
  330. package/dist/src/evals/recovery.js.map +1 -0
  331. package/dist/src/evals/regrade-evidence.js +77 -0
  332. package/dist/src/evals/regrade-evidence.js.map +1 -0
  333. package/dist/src/evals/remote-infrastructure-retry.js +165 -0
  334. package/dist/src/evals/remote-infrastructure-retry.js.map +1 -0
  335. package/dist/src/evals/request.js +21 -3
  336. package/dist/src/evals/request.js.map +1 -1
  337. package/dist/src/evals/rerun-inputs.js +108 -0
  338. package/dist/src/evals/rerun-inputs.js.map +1 -0
  339. package/dist/src/evals/rerun-slots.js +4 -3
  340. package/dist/src/evals/rerun-slots.js.map +1 -1
  341. package/dist/src/evals/rerun-types.js +45 -0
  342. package/dist/src/evals/rerun-types.js.map +1 -0
  343. package/dist/src/evals/rerun.js +133 -88
  344. package/dist/src/evals/rerun.js.map +1 -1
  345. package/dist/src/evals/result-helpers.js +78 -0
  346. package/dist/src/evals/result-helpers.js.map +1 -0
  347. package/dist/src/evals/resume-state.js +36 -0
  348. package/dist/src/evals/resume-state.js.map +1 -0
  349. package/dist/src/evals/retry-backoff.js +20 -0
  350. package/dist/src/evals/retry-backoff.js.map +1 -0
  351. package/dist/src/evals/retry-state.js +195 -0
  352. package/dist/src/evals/retry-state.js.map +1 -0
  353. package/dist/src/evals/scheduler-metrics.js +95 -0
  354. package/dist/src/evals/scheduler-metrics.js.map +1 -0
  355. package/dist/src/evals/scheduler-trace-replay.js +107 -0
  356. package/dist/src/evals/scheduler-trace-replay.js.map +1 -0
  357. package/dist/src/evals/service-types.js +2 -0
  358. package/dist/src/evals/service-types.js.map +1 -0
  359. package/dist/src/evals/service.js +331 -347
  360. package/dist/src/evals/service.js.map +1 -1
  361. package/dist/src/evals/task-resources.js +209 -0
  362. package/dist/src/evals/task-resources.js.map +1 -0
  363. package/dist/src/evals/trial-environment-evidence.js +87 -0
  364. package/dist/src/evals/trial-environment-evidence.js.map +1 -0
  365. package/dist/src/evals/trial-execution-evidence.js +13 -0
  366. package/dist/src/evals/trial-execution-evidence.js.map +1 -0
  367. package/dist/src/evals/trial-import-identity.js +31 -0
  368. package/dist/src/evals/trial-import-identity.js.map +1 -0
  369. package/dist/src/evals/trial-import.js +183 -188
  370. package/dist/src/evals/trial-import.js.map +1 -1
  371. package/dist/src/evals/trial-publication-recovery.js +144 -0
  372. package/dist/src/evals/trial-publication-recovery.js.map +1 -0
  373. package/dist/src/evals/trial-publication.js +25 -0
  374. package/dist/src/evals/trial-publication.js.map +1 -0
  375. package/dist/src/evals/trial-reference-validation.js +49 -0
  376. package/dist/src/evals/trial-reference-validation.js.map +1 -0
  377. package/dist/src/evals/verifier-artifacts.js +317 -0
  378. package/dist/src/evals/verifier-artifacts.js.map +1 -0
  379. package/dist/src/evals/verifier-eligibility.js +25 -0
  380. package/dist/src/evals/verifier-eligibility.js.map +1 -0
  381. package/dist/src/evals/verifier-only-rerun.js +210 -0
  382. package/dist/src/evals/verifier-only-rerun.js.map +1 -0
  383. package/dist/src/evals/verifier-runtime.js +61 -0
  384. package/dist/src/evals/verifier-runtime.js.map +1 -0
  385. package/dist/src/evals/verifier-score-artifacts.js +119 -0
  386. package/dist/src/evals/verifier-score-artifacts.js.map +1 -0
  387. package/dist/src/evals/work-item-artifacts.js +23 -0
  388. package/dist/src/evals/work-item-artifacts.js.map +1 -0
  389. package/dist/src/foundation/config.js +17 -0
  390. package/dist/src/foundation/config.js.map +1 -1
  391. package/dist/src/foundation/contained-file.js +85 -0
  392. package/dist/src/foundation/contained-file.js.map +1 -0
  393. package/dist/src/foundation/credential-redaction.js +162 -0
  394. package/dist/src/foundation/credential-redaction.js.map +1 -0
  395. package/dist/src/foundation/environment-image-references.js +59 -0
  396. package/dist/src/foundation/environment-image-references.js.map +1 -0
  397. package/dist/src/foundation/executable.js +45 -5
  398. package/dist/src/foundation/executable.js.map +1 -1
  399. package/dist/src/foundation/fs.js +2 -1
  400. package/dist/src/foundation/fs.js.map +1 -1
  401. package/dist/src/foundation/index.js +6 -2
  402. package/dist/src/foundation/index.js.map +1 -1
  403. package/dist/src/foundation/process-identity.js +69 -0
  404. package/dist/src/foundation/process-identity.js.map +1 -0
  405. package/dist/src/images/context.js +47 -0
  406. package/dist/src/images/context.js.map +1 -0
  407. package/dist/src/images/docker-buildkit.js +137 -0
  408. package/dist/src/images/docker-buildkit.js.map +1 -0
  409. package/dist/src/images/dockerfile.js +67 -0
  410. package/dist/src/images/dockerfile.js.map +1 -0
  411. package/dist/src/images/gc.js +261 -0
  412. package/dist/src/images/gc.js.map +1 -0
  413. package/dist/src/images/index.js +10 -0
  414. package/dist/src/images/index.js.map +1 -0
  415. package/dist/src/images/manifest.js +109 -0
  416. package/dist/src/images/manifest.js.map +1 -0
  417. package/dist/src/images/ownership.js +5 -0
  418. package/dist/src/images/ownership.js.map +1 -0
  419. package/dist/src/images/records.js +32 -0
  420. package/dist/src/images/records.js.map +1 -0
  421. package/dist/src/images/registry.js +154 -0
  422. package/dist/src/images/registry.js.map +1 -0
  423. package/dist/src/images/service.js +224 -0
  424. package/dist/src/images/service.js.map +1 -0
  425. package/dist/src/model-access/capture.js +255 -0
  426. package/dist/src/model-access/capture.js.map +1 -0
  427. package/dist/src/model-access/index.js +5 -0
  428. package/dist/src/model-access/index.js.map +1 -0
  429. package/dist/src/model-access/policy.js +49 -0
  430. package/dist/src/model-access/policy.js.map +1 -0
  431. package/dist/src/model-access/proxy.js +336 -0
  432. package/dist/src/model-access/proxy.js.map +1 -0
  433. package/dist/src/model-access/records.js +167 -0
  434. package/dist/src/model-access/records.js.map +1 -0
  435. package/dist/src/runs/adapter-process.js +12 -0
  436. package/dist/src/runs/adapter-process.js.map +1 -0
  437. package/dist/src/runs/bundle.js +450 -0
  438. package/dist/src/runs/bundle.js.map +1 -0
  439. package/dist/src/runs/events.js +12 -1
  440. package/dist/src/runs/events.js.map +1 -1
  441. package/dist/src/runs/executor.js +46 -45
  442. package/dist/src/runs/executor.js.map +1 -1
  443. package/dist/src/runs/finalizer.js +17 -0
  444. package/dist/src/runs/finalizer.js.map +1 -1
  445. package/dist/src/runs/index.js +8 -1
  446. package/dist/src/runs/index.js.map +1 -1
  447. package/dist/src/runs/manifest.js +5 -5
  448. package/dist/src/runs/manifest.js.map +1 -1
  449. package/dist/src/runs/phase-bundle.js +86 -0
  450. package/dist/src/runs/phase-bundle.js.map +1 -0
  451. package/dist/src/runs/phase-cancellation.js +107 -0
  452. package/dist/src/runs/phase-cancellation.js.map +1 -0
  453. package/dist/src/runs/phase-group.js +113 -0
  454. package/dist/src/runs/phase-group.js.map +1 -0
  455. package/dist/src/runs/query.js +1 -1
  456. package/dist/src/runs/query.js.map +1 -1
  457. package/dist/src/runs/records.js +3 -0
  458. package/dist/src/runs/records.js.map +1 -1
  459. package/dist/src/runs/request.js +10 -3
  460. package/dist/src/runs/request.js.map +1 -1
  461. package/dist/src/runs/training-candidate.js +161 -0
  462. package/dist/src/runs/training-candidate.js.map +1 -0
  463. package/dist/src/runs/verifier-evidence-redaction.js +61 -0
  464. package/dist/src/runs/verifier-evidence-redaction.js.map +1 -0
  465. package/dist/src/runs/verifier-evidence.js +459 -0
  466. package/dist/src/runs/verifier-evidence.js.map +1 -0
  467. package/dist/src/runs/verifier-structured-evidence.js +83 -0
  468. package/dist/src/runs/verifier-structured-evidence.js.map +1 -0
  469. package/dist/src/trajectories/analysis.js +288 -0
  470. package/dist/src/trajectories/analysis.js.map +1 -0
  471. package/dist/src/trajectories/chunk-projection.js +148 -0
  472. package/dist/src/trajectories/chunk-projection.js.map +1 -0
  473. package/dist/src/trajectories/content-projection.js +389 -0
  474. package/dist/src/trajectories/content-projection.js.map +1 -0
  475. package/dist/src/trajectories/dsh-chunk-contract.js +176 -0
  476. package/dist/src/trajectories/dsh-chunk-contract.js.map +1 -0
  477. package/dist/src/trajectories/dsh-contract.js +284 -0
  478. package/dist/src/trajectories/dsh-contract.js.map +1 -0
  479. package/dist/src/trajectories/events-chunk-drill.js +95 -0
  480. package/dist/src/trajectories/events-chunk-drill.js.map +1 -0
  481. package/dist/src/trajectories/events-page.js +367 -0
  482. package/dist/src/trajectories/events-page.js.map +1 -0
  483. package/dist/src/trajectories/index.js +3 -0
  484. package/dist/src/trajectories/index.js.map +1 -1
  485. package/dist/src/trajectories/provider-capture.js +15 -24
  486. package/dist/src/trajectories/provider-capture.js.map +1 -1
  487. package/dist/src/trajectories/providers/deepseek.js +3 -3
  488. package/dist/src/trajectories/providers/deepseek.js.map +1 -1
  489. package/dist/src/trajectories/request-attempt.js +47 -0
  490. package/dist/src/trajectories/request-attempt.js.map +1 -0
  491. package/dist/src/trajectories/stream-reader.js +311 -0
  492. package/dist/src/trajectories/stream-reader.js.map +1 -0
  493. package/dist/src/trajectories/surface-fold.js +126 -0
  494. package/dist/src/trajectories/surface-fold.js.map +1 -0
  495. package/dist/src/workers/index.js +3 -0
  496. package/dist/src/workers/index.js.map +1 -0
  497. package/dist/src/workers/remote-harbor-work-spec.js +97 -0
  498. package/dist/src/workers/remote-harbor-work-spec.js.map +1 -0
  499. package/dist/src/workers/remote-harbor-worker.js +223 -0
  500. package/dist/src/workers/remote-harbor-worker.js.map +1 -0
  501. package/docs/schemas/benchmark-hook-request-v1.schema.json +65 -0
  502. package/docs/schemas/benchmark-hook-response-v1.schema.json +75 -0
  503. package/docs/schemas/benchmark-hook-v1.schema.json +33 -0
  504. package/docs/schemas/benchmark-lock-v1.schema.json +351 -0
  505. package/docs/schemas/benchmark-metric-v1.schema.json +37 -0
  506. package/docs/schemas/benchmark-package-v1.schema.json +167 -0
  507. package/docs/schemas/benchmark-phase-group.schema.json +41 -0
  508. package/docs/schemas/benchmark-profile-v1.schema.json +141 -0
  509. package/docs/schemas/benchmark-task-v1.schema.json +384 -0
  510. package/docs/schemas/benchmark-tool-result-v1.schema.json +30 -0
  511. package/docs/schemas/environment-build-index.schema.json +13 -0
  512. package/docs/schemas/environment-build-record.schema.json +25 -0
  513. package/docs/schemas/environment-image-manifest.schema.json +62 -0
  514. package/docs/schemas/error.schema.json +22 -0
  515. package/docs/schemas/eval-control.schema.json +36 -0
  516. package/docs/schemas/eval-execution-plan.schema.json +216 -0
  517. package/docs/schemas/eval-progress.schema.json +1 -15
  518. package/docs/schemas/eval-rerun-result.schema.json +58 -0
  519. package/docs/schemas/eval-rerun-state.schema.json +61 -0
  520. package/docs/schemas/eval-rerun-submission.schema.json +60 -0
  521. package/docs/schemas/eval-result.schema.json +32 -13
  522. package/docs/schemas/eval-submission-input.schema.json +14 -0
  523. package/docs/schemas/eval-submission.schema.json +68 -0
  524. package/docs/schemas/eval-trial-publication.schema.json +15 -0
  525. package/docs/schemas/eval-trial-reference.schema.json +129 -0
  526. package/docs/schemas/execution-evidence.schema.json +96 -0
  527. package/docs/schemas/execution-lease.schema.json +50 -0
  528. package/docs/schemas/execution-provider-status.schema.json +65 -0
  529. package/docs/schemas/execution-worker.schema.json +53 -0
  530. package/docs/schemas/harbor-process-exit.schema.json +14 -0
  531. package/docs/schemas/interaction-capture-ref.schema.json +45 -0
  532. package/docs/schemas/local-provider-execution.schema.json +33 -0
  533. package/docs/schemas/model-interaction.schema.json +41 -0
  534. package/docs/schemas/regrade-assessment-reference.schema.json +11 -0
  535. package/docs/schemas/remote-result-envelope.schema.json +32 -0
  536. package/docs/schemas/remote-tree-envelope.schema.json +25 -0
  537. package/docs/schemas/remote-work-offer.schema.json +58 -0
  538. package/docs/schemas/remote-work-receipt.schema.json +50 -0
  539. package/docs/schemas/remote-worker-artifact.schema.json +17 -0
  540. package/docs/schemas/remote-worker-event.schema.json +18 -0
  541. package/docs/schemas/remote-worker-heartbeat.schema.json +37 -0
  542. package/docs/schemas/remote-worker-registration.schema.json +52 -0
  543. package/docs/schemas/result-bundle-index.schema.json +133 -0
  544. package/docs/schemas/run-context.schema.json +15 -0
  545. package/docs/schemas/run-environment-images.schema.json +39 -0
  546. package/docs/schemas/run-manifest.schema.json +4 -0
  547. package/docs/schemas/run-request.schema.json +4 -0
  548. package/docs/schemas/training-data-candidate.schema.json +46 -0
  549. package/docs/schemas/trajectory-analysis.schema.json +258 -0
  550. package/docs/schemas/trajectory-events-page.schema.json +40 -0
  551. package/docs/schemas/verifier-assessment.schema.json +77 -0
  552. package/docs/schemas/verifier-evidence.schema.json +276 -0
  553. package/integrations/harbor/hitch_benchmark.py +252 -0
  554. package/integrations/harbor/hitch_candidate_recycle.py +262 -0
  555. package/integrations/harbor/hitch_harbor_agent.py +698 -568
  556. package/integrations/harbor/hitch_harbor_environment.py +469 -0
  557. package/integrations/harbor/hitch_harbor_task_resources.py +275 -0
  558. package/integrations/harbor/hitch_harbor_verifier.py +83 -2
  559. package/integrations/harbor/hitch_phase_supervisor.py +452 -0
  560. package/integrations/harbor/hitch_tool_client.mjs +91 -0
  561. package/integrations/model-call/cli.js +57 -0
  562. package/package.json +30 -6
@@ -0,0 +1,469 @@
1
+ """Harbor Docker environment that stamps lease ownership on Compose resources."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import re
8
+ import tempfile
9
+ from pathlib import Path
10
+ from typing import Any, Mapping
11
+
12
+ import yaml
13
+ from harbor.constants import MAIN_SERVICE_NAME
14
+ from harbor.environments.docker.docker import DockerEnvironment
15
+
16
+ _LABEL_ROOT = "io.hitch.root-id"
17
+ _LABEL_PROVIDER = "io.hitch.provider"
18
+ _LABEL_EVAL = "io.hitch.eval-id"
19
+ _LABEL_WORK = "io.hitch.work-id"
20
+ _LABEL_LEASE = "io.hitch.lease-id"
21
+ _LABEL_EPOCH = "io.hitch.lease-epoch"
22
+ _LABEL_TASK = "io.hitch.task-id"
23
+ _REQUIRED_LABELS = {
24
+ _LABEL_ROOT,
25
+ _LABEL_PROVIDER,
26
+ _LABEL_EVAL,
27
+ _LABEL_WORK,
28
+ _LABEL_LEASE,
29
+ _LABEL_EPOCH,
30
+ }
31
+ _ALLOWED_LABELS = _REQUIRED_LABELS | {_LABEL_TASK}
32
+ _COMPOSE_RESET_MARKER = "__HITCH_COMPOSE_RESET_NULL__"
33
+ _GPU_OVERRIDE_PREFIX = "__HITCH_GPU_OVERRIDE_"
34
+
35
+
36
+ class HitchHarborDockerEnvironment(DockerEnvironment):
37
+ """Use Harbor's Docker semantics with a final ownership-label overlay."""
38
+
39
+ _hitch_static_no_network = False
40
+
41
+ def __init__(
42
+ self,
43
+ *args: Any,
44
+ hitch_ownership_labels: Mapping[str, str] | None = None,
45
+ hitch_service_resource_limits: Mapping[str, Mapping[str, int]] | None = None,
46
+ hitch_resolved_images: Mapping[str, str] | None = None,
47
+ hitch_prebuilt_task_image: str | None = None,
48
+ hitch_model_proxy_host_gateway: bool = False,
49
+ hitch_main_gpu_count: int = 0,
50
+ **kwargs: Any,
51
+ ) -> None:
52
+ self._hitch_ownership_labels = _validate_labels(hitch_ownership_labels)
53
+ self._hitch_service_resource_limits = _validate_resource_limits(
54
+ hitch_service_resource_limits
55
+ )
56
+ self._hitch_resolved_images = _validate_resolved_images(hitch_resolved_images)
57
+ self._hitch_prebuilt_task_image = _validate_prebuilt_task_image(
58
+ hitch_prebuilt_task_image
59
+ )
60
+ if not isinstance(hitch_model_proxy_host_gateway, bool):
61
+ raise ValueError("Hitch model proxy host gateway flag is invalid")
62
+ self._hitch_model_proxy_host_gateway = hitch_model_proxy_host_gateway
63
+ if isinstance(hitch_main_gpu_count, bool) or not isinstance(hitch_main_gpu_count, int) or hitch_main_gpu_count < 0:
64
+ raise ValueError("Hitch main GPU count is invalid")
65
+ self._hitch_main_gpu_count = hitch_main_gpu_count
66
+ self._hitch_ownership_temp_dir: tempfile.TemporaryDirectory[str] | None = None
67
+ self._hitch_ownership_compose_path: Path | None = None
68
+ self._hitch_phase_compose_path: Path | None = None
69
+ self._hitch_candidate_recycler = None
70
+ positional = list(args)
71
+ task_env_config = kwargs.get("task_env_config")
72
+ task_env_position: int | None = None
73
+ if task_env_config is None and len(positional) >= 5:
74
+ task_env_position = 4
75
+ task_env_config = positional[task_env_position]
76
+ self._hitch_static_no_network = _static_no_network(positional, kwargs, task_env_config)
77
+ self._hitch_static_network_receipt = None
78
+ if task_env_config is not None:
79
+ requested = getattr(task_env_config, "docker_image", None)
80
+ resolved = self._hitch_prebuilt_task_image or self._hitch_resolved_images.get(requested)
81
+ if resolved is not None:
82
+ updated = task_env_config.model_copy(update={"docker_image": resolved})
83
+ if task_env_position is None:
84
+ kwargs["task_env_config"] = updated
85
+ else:
86
+ positional[task_env_position] = updated
87
+ super().__init__(*positional, **kwargs)
88
+ self._hitch_benchmark = None
89
+ self._hitch_benchmark_platform = None
90
+ directory = getattr(self, "environment_dir", None)
91
+ if directory and (directory.parent / ".hitch-benchmark.json").is_file():
92
+ self._hitch_benchmark_platform = "linux/amd64"
93
+ if directory and directory.name == "environment" and (directory.parent / ".hitch-benchmark.json").is_file():
94
+ from hitch_benchmark import BenchmarkSession, descriptor
95
+ self._hitch_benchmark = BenchmarkSession(self, descriptor(directory))
96
+ if (
97
+ self._hitch_ownership_labels
98
+ or self._hitch_service_resource_limits
99
+ or self._hitch_resolved_images
100
+ or self._hitch_prebuilt_task_image
101
+ or self._hitch_model_proxy_host_gateway
102
+ or self._hitch_main_gpu_count > 0
103
+ or self._hitch_benchmark_platform
104
+ or self._hitch_static_no_network
105
+ ):
106
+ self._hitch_ownership_compose_path = self._write_ownership_overlay()
107
+
108
+ @property
109
+ def _docker_compose_paths(self) -> list[Path]:
110
+ if self._hitch_static_no_network and (self._environment_docker_compose_path.exists() or self.extra_docker_compose_paths):
111
+ raise ValueError("static no-network requires a single Dockerfile/image environment without Compose additions")
112
+ paths = list(super()._docker_compose_paths)
113
+ if self._hitch_ownership_compose_path is not None:
114
+ paths.append(self._hitch_ownership_compose_path)
115
+ if self._hitch_phase_compose_path is not None:
116
+ paths.append(self._hitch_phase_compose_path)
117
+ return paths
118
+
119
+ def _requires_egress_control(self, *, startup_network_policy, phase_network_policies):
120
+ if self._hitch_static_no_network:
121
+ return False
122
+ return super()._requires_egress_control(startup_network_policy=startup_network_policy, phase_network_policies=phase_network_policies)
123
+
124
+ @property
125
+ def capabilities(self):
126
+ result = super().capabilities
127
+ if self._hitch_static_no_network:
128
+ return result.model_copy(update={"disable_internet": True, "dynamic_network_policy": False})
129
+ return result
130
+
131
+ async def _apply_network_policy(self, network_policy):
132
+ if self._hitch_static_no_network:
133
+ if network_policy != self.network_policy:
134
+ raise ValueError("static no-network cannot change runtime network policy")
135
+ return
136
+ await super()._apply_network_policy(network_policy)
137
+
138
+ async def _verify_static_network(self):
139
+ result = await self._run_docker_compose_command(["ps", "--all", "--quiet", MAIN_SERVICE_NAME])
140
+ identity = result.stdout.strip()
141
+ if not re.fullmatch(r"[a-f0-9]{64}", identity):
142
+ raise RuntimeError("static no-network requires exactly one owned main container")
143
+ template = '{"mode":{{json .HostConfig.NetworkMode}},"networks":{{json .NetworkSettings.Networks}},"ports":{{json .HostConfig.PortBindings}}}'
144
+ process = await asyncio.create_subprocess_exec("docker", "inspect", "--format", template, identity,
145
+ stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.DEVNULL)
146
+ try:
147
+ stdout, _ = await asyncio.wait_for(process.communicate(), 15)
148
+ finally:
149
+ if process.returncode is None:
150
+ process.kill(); await process.wait()
151
+ if process.returncode != 0 or len(stdout) > 65536:
152
+ raise RuntimeError("could not verify static no-network container")
153
+ inspected = json.loads(stdout)
154
+ if inspected.get("mode") != "none" or set(inspected.get("networks") or {}) != {"none"} or inspected.get("ports"):
155
+ raise RuntimeError("Docker did not enforce the static no-network declaration")
156
+ self._hitch_static_network_receipt = {"container_id": identity, "network_mode": "none", "host_ports": False}
157
+
158
+ async def recycle_candidate_phase(self, phase_index: int) -> dict[str, Any]:
159
+ """Replace only main after the supervisor has exported the retired run.
160
+
161
+ Does not start a model or prepare a tool binding. The native task VM and
162
+ service containers remain live. Failures require whole-trial cleanup.
163
+ """
164
+ if self._hitch_candidate_recycler is None:
165
+ from hitch_candidate_recycle import CandidateRecycler
166
+ self._hitch_candidate_recycler = CandidateRecycler(self)
167
+ return await self._hitch_candidate_recycler.recycle(phase_index)
168
+
169
+ async def start(self, force_build: bool):
170
+ try:
171
+ await super().start(force_build)
172
+ if self._hitch_static_no_network:
173
+ await self._verify_static_network()
174
+ if self._hitch_benchmark:
175
+ await self._hitch_benchmark.prepare()
176
+ except BaseException:
177
+ # Harbor may abort setup before entering its normal teardown path.
178
+ try:
179
+ await self.stop(delete=True)
180
+ except BaseException:
181
+ pass
182
+ raise
183
+
184
+ async def stop_service(self, service: str) -> None:
185
+ try:
186
+ await super().stop_service(service)
187
+ if service == MAIN_SERVICE_NAME and self._hitch_benchmark:
188
+ await self._hitch_benchmark.snapshot()
189
+ except BaseException as error:
190
+ if self._hitch_benchmark:
191
+ self._hitch_benchmark.failure = {"phase": "quiesce", "error": type(error).__name__}
192
+ self._hitch_benchmark.write_journal()
193
+ raise
194
+
195
+ async def stop(self, delete: bool):
196
+ try:
197
+ if self._hitch_benchmark and self._hitch_benchmark.started:
198
+ try:
199
+ await super().stop_service(MAIN_SERVICE_NAME)
200
+ finally:
201
+ await self._hitch_benchmark.cleanup()
202
+ finally:
203
+ await super().stop(delete)
204
+
205
+ def _write_ownership_overlay(self) -> Path:
206
+ services = {MAIN_SERVICE_NAME}
207
+ if self._enable_egress_control:
208
+ services.add(self._EGRESS_CONTROL_SERVICE_NAME)
209
+ networks = {"default"}
210
+ volumes: set[str] = set()
211
+ external_networks: set[str] = set()
212
+ external_volumes: set[str] = set()
213
+ build_services: set[str] = set()
214
+ declared_platforms: set[str] = set()
215
+ image_overlays: dict[str, str] = {}
216
+ sources = [self._environment_docker_compose_path, *self.extra_docker_compose_paths]
217
+ for source in sources:
218
+ if not source.exists():
219
+ continue
220
+ document = yaml.safe_load(source.read_text(encoding="utf-8"))
221
+ if document is None:
222
+ continue
223
+ if not isinstance(document, dict):
224
+ raise ValueError(f"Docker Compose ownership source must be a mapping: {source}")
225
+ services.update(_mapping_names(document.get("services"), "services", source))
226
+ for name, config in (document.get("services") or {}).items():
227
+ if isinstance(config, dict):
228
+ if 'platform' in config:
229
+ if config['platform'] is not None:
230
+ declared_platforms.add(name)
231
+ else:
232
+ declared_platforms.discard(name)
233
+ if "build" in config and config.get("build") is not None:
234
+ build_services.add(name)
235
+ reference = config.get("image")
236
+ resolved = self._hitch_resolved_images.get(reference)
237
+ if resolved is not None:
238
+ image_overlays[name] = resolved
239
+ elif reference is not None:
240
+ image_overlays.pop(name, None)
241
+ _collect_resources(document.get("networks"), networks, external_networks, "networks", source)
242
+ _collect_resources(document.get("volumes"), volumes, external_volumes, "volumes", source)
243
+
244
+ labels = dict(self._hitch_ownership_labels)
245
+ service_overlays: dict[str, dict[str, Any]] = {}
246
+ for name in sorted(services):
247
+ config: dict[str, Any] = {}
248
+ # The benchmark default must not replace explicit per-service
249
+ # architecture, e.g. an ARM QEMU sidecar beside an AMD64 candidate.
250
+ if getattr(self, "_hitch_benchmark_platform", None) and name not in declared_platforms:
251
+ config["platform"] = self._hitch_benchmark_platform
252
+ if labels:
253
+ config["labels"] = labels
254
+ if name == MAIN_SERVICE_NAME and self._hitch_static_no_network:
255
+ config["network_mode"] = "none"
256
+ resolved_image = image_overlays.get(name)
257
+ if name == MAIN_SERVICE_NAME and self._hitch_prebuilt_task_image:
258
+ resolved_image = self._hitch_prebuilt_task_image
259
+ if resolved_image is not None:
260
+ config["image"] = resolved_image
261
+ if (
262
+ name == MAIN_SERVICE_NAME
263
+ and self._hitch_prebuilt_task_image
264
+ and name in build_services
265
+ ):
266
+ # Compose's normal merge semantics retain an earlier build stanza
267
+ # when an overlay only supplies image (or even build: null). The
268
+ # !reset tag is the supported way to make a successful Hitch
269
+ # prebuild authoritative and prevent a second mutable-context build.
270
+ config["build"] = _COMPOSE_RESET_MARKER
271
+ if name == MAIN_SERVICE_NAME and self._hitch_model_proxy_host_gateway:
272
+ config["extra_hosts"] = ["host.docker.internal:host-gateway"]
273
+ if name == MAIN_SERVICE_NAME and self._hitch_main_gpu_count > 0:
274
+ config["deploy"] = {
275
+ "resources": {
276
+ "reservations": {
277
+ "devices": f"{_GPU_OVERRIDE_PREFIX}{self._hitch_main_gpu_count}__"
278
+ }
279
+ }
280
+ }
281
+ if name != MAIN_SERVICE_NAME and self._hitch_service_resource_limits:
282
+ limits = self._hitch_service_resource_limits.get(name)
283
+ if limits is None:
284
+ raise ValueError(
285
+ f"Docker Compose sidecar has no Hitch resource limit: {name}"
286
+ )
287
+ config.update({
288
+ "cpus": limits["cpu_millis"] / 1000,
289
+ "mem_limit": limits["memory_bytes"],
290
+ })
291
+ if limits.get("gpu_count", 0) > 0:
292
+ config["deploy"] = {
293
+ "resources": {
294
+ "reservations": {
295
+ "devices": f"{_GPU_OVERRIDE_PREFIX}{limits['gpu_count']}__"
296
+ }
297
+ }
298
+ }
299
+ service_overlays[name] = config
300
+ overlay = {
301
+ "services": service_overlays,
302
+ "networks": {
303
+ name: {"labels": labels}
304
+ for name in sorted(networks - external_networks)
305
+ },
306
+ "volumes": {
307
+ name: {"labels": labels}
308
+ for name in sorted(volumes - external_volumes)
309
+ },
310
+ }
311
+ self._hitch_ownership_temp_dir = tempfile.TemporaryDirectory(
312
+ prefix="hitch-harbor-ownership-"
313
+ )
314
+ target = Path(self._hitch_ownership_temp_dir.name) / "docker-compose-hitch-ownership.yaml"
315
+ contents = json.dumps(overlay, indent=2, sort_keys=True)
316
+ contents = contents.replace(
317
+ f'"build": "{_COMPOSE_RESET_MARKER}"',
318
+ '"build": !reset null',
319
+ )
320
+ contents = re.sub(
321
+ rf'"devices": "{_GPU_OVERRIDE_PREFIX}([1-9][0-9]*)__"',
322
+ lambda match: '"devices": !override [{"capabilities": ["gpu"], "count": ' + match.group(1) + '}]',
323
+ contents,
324
+ )
325
+ target.write_text(contents, encoding="utf-8")
326
+ return target
327
+
328
+
329
+ def _static_no_network(positional, kwargs, task_env_config) -> bool:
330
+ """Use Docker's isolated namespace only when no phase needs connectivity.
331
+
332
+ Compose tasks and dynamic policies retain Harbor's egress-control path.
333
+ A single image/Dockerfile has no sibling-service network to preserve.
334
+ """
335
+ policy = kwargs.get("network_policy", positional[6] if len(positional) > 6 else None)
336
+ phases = kwargs.get("phase_network_policies", positional[7] if len(positional) > 7 else ()) or ()
337
+ directory = kwargs.get("environment_dir", positional[0] if positional else None)
338
+ return bool(
339
+ getattr(getattr(task_env_config, "os", None), "value", None) == "linux"
340
+ and getattr(getattr(policy, "network_mode", None), "value", None) == "no-network"
341
+ and all(phase == policy for phase in phases)
342
+ and directory is not None
343
+ and not (Path(directory) / "docker-compose.yaml").exists()
344
+ and not kwargs.get("extra_docker_compose")
345
+ )
346
+
347
+
348
+ def _mapping_names(value: Any, label: str, source: Path) -> set[str]:
349
+ if value is None:
350
+ return set()
351
+ if not isinstance(value, dict) or any(not isinstance(name, str) or not name for name in value):
352
+ raise ValueError(f"Docker Compose {label} must be a named mapping: {source}")
353
+ return set(value)
354
+
355
+
356
+ def _collect_resources(
357
+ value: Any,
358
+ names: set[str],
359
+ external: set[str],
360
+ label: str,
361
+ source: Path,
362
+ ) -> None:
363
+ for name in _mapping_names(value, label, source):
364
+ names.add(name)
365
+ config = value[name]
366
+ if isinstance(config, dict) and bool(config.get("external")):
367
+ external.add(name)
368
+
369
+
370
+ def _validate_labels(value: Mapping[str, str] | None) -> dict[str, str]:
371
+ if value is None:
372
+ return {}
373
+ if not isinstance(value, Mapping) or set(value) - _ALLOWED_LABELS or not _REQUIRED_LABELS <= set(value):
374
+ raise ValueError("Hitch Docker ownership label fields are invalid")
375
+ labels = dict(value)
376
+ if (
377
+ not re.fullmatch(r"[a-f0-9]{24}", labels.get(_LABEL_ROOT, ""))
378
+ or labels.get(_LABEL_PROVIDER) != "local-docker"
379
+ or not re.fullmatch(r"eval_[a-f0-9]{32}", labels.get(_LABEL_EVAL, ""))
380
+ or not re.fullmatch(r"work_[a-f0-9]{32}", labels.get(_LABEL_WORK, ""))
381
+ or not re.fullmatch(r"lease_[a-f0-9]{32}", labels.get(_LABEL_LEASE, ""))
382
+ or not re.fullmatch(r"[1-9][0-9]*", labels.get(_LABEL_EPOCH, ""))
383
+ or any(not isinstance(entry, str) or not entry or len(entry) > 4096 or "\x00" in entry or "\n" in entry or "\r" in entry for entry in labels.values())
384
+ ):
385
+ raise ValueError("Hitch Docker ownership label values are invalid")
386
+ return dict(sorted(labels.items()))
387
+
388
+
389
+ def _validate_resource_limits(
390
+ value: Mapping[str, Mapping[str, int]] | None,
391
+ ) -> dict[str, dict[str, int]]:
392
+ if value is None:
393
+ return {}
394
+ if not isinstance(value, Mapping):
395
+ raise ValueError("Hitch Docker service resource limits are invalid")
396
+ result: dict[str, dict[str, int]] = {}
397
+ for name, resources in value.items():
398
+ if (
399
+ not isinstance(name, str)
400
+ or not name
401
+ or name == MAIN_SERVICE_NAME
402
+ or len(name) > 255
403
+ or any(character in name for character in ("\x00", "\n", "\r"))
404
+ or not isinstance(resources, Mapping)
405
+ or set(resources) - {"cpu_millis", "memory_bytes", "gpu_count"}
406
+ or not {"cpu_millis", "memory_bytes"} <= set(resources)
407
+ or isinstance(resources.get("cpu_millis"), bool)
408
+ or not isinstance(resources.get("cpu_millis"), int)
409
+ or resources["cpu_millis"] < 1
410
+ or isinstance(resources.get("memory_bytes"), bool)
411
+ or not isinstance(resources.get("memory_bytes"), int)
412
+ or resources["memory_bytes"] < 1
413
+ or ("gpu_count" in resources and (
414
+ isinstance(resources.get("gpu_count"), bool)
415
+ or not isinstance(resources.get("gpu_count"), int)
416
+ or resources["gpu_count"] < 1
417
+ ))
418
+ ):
419
+ raise ValueError("Hitch Docker service resource limits are invalid")
420
+ result[name] = {
421
+ "cpu_millis": resources["cpu_millis"],
422
+ "memory_bytes": resources["memory_bytes"],
423
+ **({"gpu_count": resources["gpu_count"]} if "gpu_count" in resources else {}),
424
+ }
425
+ return dict(sorted(result.items()))
426
+
427
+
428
+ def _validate_resolved_images(value: Mapping[str, str] | None) -> dict[str, str]:
429
+ if value is None:
430
+ return {}
431
+ if not isinstance(value, Mapping):
432
+ raise ValueError("Hitch resolved image mapping is invalid")
433
+ result: dict[str, str] = {}
434
+ for requested, resolved in value.items():
435
+ if (
436
+ not _valid_image_reference(requested)
437
+ or not _valid_image_reference(resolved)
438
+ or re.search(r"@sha256:[a-f0-9]{64}$", resolved) is None
439
+ or _image_repository(requested) != _image_repository(resolved)
440
+ ):
441
+ raise ValueError("Hitch resolved image mapping is invalid")
442
+ result[requested] = resolved
443
+ return dict(sorted(result.items()))
444
+
445
+
446
+ def _validate_prebuilt_task_image(value: str | None) -> str | None:
447
+ if value is None:
448
+ return None
449
+ if not isinstance(value, str) or re.fullmatch(r"sha256:[a-f0-9]{64}", value) is None:
450
+ raise ValueError("Hitch prebuilt task image is invalid")
451
+ return value
452
+
453
+
454
+ def _valid_image_reference(value: Any) -> bool:
455
+ return (
456
+ isinstance(value, str)
457
+ and bool(value)
458
+ and len(value) <= 1024
459
+ and not any(character.isspace() or character == "\x00" for character in value)
460
+ and "://" not in value
461
+ and "$" not in value
462
+ )
463
+
464
+
465
+ def _image_repository(reference: str) -> str:
466
+ without_digest = reference.split("@", 1)[0]
467
+ slash = without_digest.rfind("/")
468
+ colon = without_digest.rfind(":")
469
+ return without_digest[:colon] if colon > slash else without_digest