agent-hitch 0.2.5 → 0.2.7

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 (395) hide show
  1. package/README.md +14 -1
  2. package/dist/scripts/canary-buildkit-secrets.js +166 -0
  3. package/dist/scripts/canary-buildkit-secrets.js.map +1 -0
  4. package/dist/scripts/canary-collision-domains.js +77 -0
  5. package/dist/scripts/canary-collision-domains.js.map +1 -0
  6. package/dist/scripts/canary-docker-image-observation.js +181 -0
  7. package/dist/scripts/canary-docker-image-observation.js.map +1 -0
  8. package/dist/scripts/canary-gpu-hardware.js +95 -0
  9. package/dist/scripts/canary-gpu-hardware.js.map +1 -0
  10. package/dist/scripts/canary-harbor-load.js +365 -0
  11. package/dist/scripts/canary-harbor-load.js.map +1 -0
  12. package/dist/scripts/canary-packaged-harness.js +75 -0
  13. package/dist/scripts/canary-packaged-harness.js.map +1 -0
  14. package/dist/scripts/canary-resource-load.js +188 -0
  15. package/dist/scripts/canary-resource-load.js.map +1 -0
  16. package/dist/scripts/check-architecture.js +8 -4
  17. package/dist/scripts/check-architecture.js.map +1 -1
  18. package/dist/scripts/docker-reaper-crash-child.js +21 -0
  19. package/dist/scripts/docker-reaper-crash-child.js.map +1 -0
  20. package/dist/src/adapters/catalog.js +1 -0
  21. package/dist/src/adapters/catalog.js.map +1 -1
  22. package/dist/src/adapters/discovery.js +1 -1
  23. package/dist/src/adapters/discovery.js.map +1 -1
  24. package/dist/src/adapters/providers/claude.js +6 -0
  25. package/dist/src/adapters/providers/claude.js.map +1 -1
  26. package/dist/src/adapters/providers/codex.js +6 -0
  27. package/dist/src/adapters/providers/codex.js.map +1 -1
  28. package/dist/src/adapters/providers/deepseek.js +6 -0
  29. package/dist/src/adapters/providers/deepseek.js.map +1 -1
  30. package/dist/src/adapters/providers/opencode.js +6 -0
  31. package/dist/src/adapters/providers/opencode.js.map +1 -1
  32. package/dist/src/adapters/providers/pi.js +6 -0
  33. package/dist/src/adapters/providers/pi.js.map +1 -1
  34. package/dist/src/artifacts/index.js +1 -0
  35. package/dist/src/artifacts/index.js.map +1 -1
  36. package/dist/src/backends/harbor/backend.js +55 -104
  37. package/dist/src/backends/harbor/backend.js.map +1 -1
  38. package/dist/src/backends/harbor/environment-config.js +92 -0
  39. package/dist/src/backends/harbor/environment-config.js.map +1 -0
  40. package/dist/src/backends/harbor/index.js +5 -1
  41. package/dist/src/backends/harbor/index.js.map +1 -1
  42. package/dist/src/backends/harbor/model-proxy-config.js +23 -0
  43. package/dist/src/backends/harbor/model-proxy-config.js.map +1 -0
  44. package/dist/src/backends/harbor/process.js +177 -0
  45. package/dist/src/backends/harbor/process.js.map +1 -0
  46. package/dist/src/backends/harbor/runtime-toolchain.js +30 -0
  47. package/dist/src/backends/harbor/runtime-toolchain.js.map +1 -0
  48. package/dist/src/backends/harbor/supervisor.js +74 -0
  49. package/dist/src/backends/harbor/supervisor.js.map +1 -0
  50. package/dist/src/backends/harbor/task-resources.js +206 -0
  51. package/dist/src/backends/harbor/task-resources.js.map +1 -0
  52. package/dist/src/backends/harbor/tools.js +6 -29
  53. package/dist/src/backends/harbor/tools.js.map +1 -1
  54. package/dist/src/backends/harbor/verifier-config.js +11 -0
  55. package/dist/src/backends/harbor/verifier-config.js.map +1 -0
  56. package/dist/src/backends/index.js +4 -1
  57. package/dist/src/backends/index.js.map +1 -1
  58. package/dist/src/cli/arguments.js +14 -0
  59. package/dist/src/cli/arguments.js.map +1 -1
  60. package/dist/src/cli/commands/daemon.js +135 -8
  61. package/dist/src/cli/commands/daemon.js.map +1 -1
  62. package/dist/src/cli/commands/eval.js +214 -4
  63. package/dist/src/cli/commands/eval.js.map +1 -1
  64. package/dist/src/cli/commands/images.js +40 -0
  65. package/dist/src/cli/commands/images.js.map +1 -0
  66. package/dist/src/cli/commands/run.js +6 -3
  67. package/dist/src/cli/commands/run.js.map +1 -1
  68. package/dist/src/cli/commands/runs.js +25 -3
  69. package/dist/src/cli/commands/runs.js.map +1 -1
  70. package/dist/src/cli/commands/worker.js +79 -0
  71. package/dist/src/cli/commands/worker.js.map +1 -0
  72. package/dist/src/cli/main.js +4 -0
  73. package/dist/src/cli/main.js.map +1 -1
  74. package/dist/src/cli/output.js +72 -3
  75. package/dist/src/cli/output.js.map +1 -1
  76. package/dist/src/control-plane/build-admission.js +69 -0
  77. package/dist/src/control-plane/build-admission.js.map +1 -0
  78. package/dist/src/control-plane/build-records.js +6 -0
  79. package/dist/src/control-plane/build-records.js.map +1 -0
  80. package/dist/src/control-plane/collisions.js +40 -0
  81. package/dist/src/control-plane/collisions.js.map +1 -0
  82. package/dist/src/control-plane/eval-control-state.js +22 -0
  83. package/dist/src/control-plane/eval-control-state.js.map +1 -0
  84. package/dist/src/control-plane/eval-control-work.js +39 -0
  85. package/dist/src/control-plane/eval-control-work.js.map +1 -0
  86. package/dist/src/control-plane/eval-image-resolution.js +52 -0
  87. package/dist/src/control-plane/eval-image-resolution.js.map +1 -0
  88. package/dist/src/control-plane/eval-image-services.js +30 -0
  89. package/dist/src/control-plane/eval-image-services.js.map +1 -0
  90. package/dist/src/control-plane/eval-records.js +252 -0
  91. package/dist/src/control-plane/eval-records.js.map +1 -0
  92. package/dist/src/control-plane/eval-recovery.js +181 -0
  93. package/dist/src/control-plane/eval-recovery.js.map +1 -0
  94. package/dist/src/control-plane/eval-scheduler.js +447 -0
  95. package/dist/src/control-plane/eval-scheduler.js.map +1 -0
  96. package/dist/src/control-plane/index.js +20 -0
  97. package/dist/src/control-plane/index.js.map +1 -0
  98. package/dist/src/control-plane/local-worker.js +49 -0
  99. package/dist/src/control-plane/local-worker.js.map +1 -0
  100. package/dist/src/control-plane/model-capture-planning.js +12 -0
  101. package/dist/src/control-plane/model-capture-planning.js.map +1 -0
  102. package/dist/src/control-plane/remote-result-transport.js +172 -0
  103. package/dist/src/control-plane/remote-result-transport.js.map +1 -0
  104. package/dist/src/control-plane/remote-work-coordinator.js +332 -0
  105. package/dist/src/control-plane/remote-work-coordinator.js.map +1 -0
  106. package/dist/src/control-plane/remote-work-inputs.js +266 -0
  107. package/dist/src/control-plane/remote-work-inputs.js.map +1 -0
  108. package/dist/src/control-plane/remote-work-recovery.js +181 -0
  109. package/dist/src/control-plane/remote-work-recovery.js.map +1 -0
  110. package/dist/src/control-plane/remote-worker-artifacts.js +135 -0
  111. package/dist/src/control-plane/remote-worker-artifacts.js.map +1 -0
  112. package/dist/src/control-plane/remote-worker-client.js +202 -0
  113. package/dist/src/control-plane/remote-worker-client.js.map +1 -0
  114. package/dist/src/control-plane/remote-worker-configuration.js +34 -0
  115. package/dist/src/control-plane/remote-worker-configuration.js.map +1 -0
  116. package/dist/src/control-plane/remote-worker-credentials.js +58 -0
  117. package/dist/src/control-plane/remote-worker-credentials.js.map +1 -0
  118. package/dist/src/control-plane/remote-worker-protocol.js +510 -0
  119. package/dist/src/control-plane/remote-worker-protocol.js.map +1 -0
  120. package/dist/src/control-plane/remote-worker-runner.js +214 -0
  121. package/dist/src/control-plane/remote-worker-runner.js.map +1 -0
  122. package/dist/src/control-plane/remote-workers.js +377 -0
  123. package/dist/src/control-plane/remote-workers.js.map +1 -0
  124. package/dist/src/control-plane/rerun-scheduler.js +411 -0
  125. package/dist/src/control-plane/rerun-scheduler.js.map +1 -0
  126. package/dist/src/control-plane/rerun-submission.js +56 -0
  127. package/dist/src/control-plane/rerun-submission.js.map +1 -0
  128. package/dist/src/control-plane/resources.js +182 -0
  129. package/dist/src/control-plane/resources.js.map +1 -0
  130. package/dist/src/control-plane/scheduler-eval-entry.js +32 -0
  131. package/dist/src/control-plane/scheduler-eval-entry.js.map +1 -0
  132. package/dist/src/control-plane/synthetic-result.js +21 -0
  133. package/dist/src/control-plane/synthetic-result.js.map +1 -0
  134. package/dist/src/control-plane/work-admission.js +21 -0
  135. package/dist/src/control-plane/work-admission.js.map +1 -0
  136. package/dist/src/control-plane/work-dispatcher.js +229 -0
  137. package/dist/src/control-plane/work-dispatcher.js.map +1 -0
  138. package/dist/src/controller-runtime/hash.js +12 -1
  139. package/dist/src/controller-runtime/hash.js.map +1 -1
  140. package/dist/src/controller-runtime/index.js +1 -1
  141. package/dist/src/controller-runtime/index.js.map +1 -1
  142. package/dist/src/controller-runtime/store.js +15 -6
  143. package/dist/src/controller-runtime/store.js.map +1 -1
  144. package/dist/src/daemon/client.js.map +1 -1
  145. package/dist/src/daemon/event-stream.js +46 -0
  146. package/dist/src/daemon/event-stream.js.map +1 -0
  147. package/dist/src/daemon/health.js +66 -0
  148. package/dist/src/daemon/health.js.map +1 -0
  149. package/dist/src/daemon/launcher.js +28 -1
  150. package/dist/src/daemon/launcher.js.map +1 -1
  151. package/dist/src/daemon/logging.js +23 -0
  152. package/dist/src/daemon/logging.js.map +1 -0
  153. package/dist/src/daemon/scheduler.js +26 -5
  154. package/dist/src/daemon/scheduler.js.map +1 -1
  155. package/dist/src/daemon/server.js +255 -98
  156. package/dist/src/daemon/server.js.map +1 -1
  157. package/dist/src/daemon/telemetry.js +163 -0
  158. package/dist/src/daemon/telemetry.js.map +1 -0
  159. package/dist/src/daemon/worker-routes.js +219 -0
  160. package/dist/src/daemon/worker-routes.js.map +1 -0
  161. package/dist/src/domain/bundles.js +2 -0
  162. package/dist/src/domain/bundles.js.map +1 -0
  163. package/dist/src/domain/control-plane.js +2 -0
  164. package/dist/src/domain/control-plane.js.map +1 -0
  165. package/dist/src/domain/execution-evidence.js +2 -0
  166. package/dist/src/domain/execution-evidence.js.map +1 -0
  167. package/dist/src/domain/execution-plan.js +2 -0
  168. package/dist/src/domain/execution-plan.js.map +1 -0
  169. package/dist/src/domain/images.js +2 -0
  170. package/dist/src/domain/images.js.map +1 -0
  171. package/dist/src/domain/index.js +9 -0
  172. package/dist/src/domain/index.js.map +1 -1
  173. package/dist/src/domain/interactions.js +2 -0
  174. package/dist/src/domain/interactions.js.map +1 -0
  175. package/dist/src/domain/providers.js +2 -0
  176. package/dist/src/domain/providers.js.map +1 -0
  177. package/dist/src/domain/resources.js +2 -0
  178. package/dist/src/domain/resources.js.map +1 -0
  179. package/dist/src/domain/workers.js +2 -0
  180. package/dist/src/domain/workers.js.map +1 -0
  181. package/dist/src/evals/collect-only-rerun.js +182 -0
  182. package/dist/src/evals/collect-only-rerun.js.map +1 -0
  183. package/dist/src/evals/directory.js +30 -0
  184. package/dist/src/evals/directory.js.map +1 -0
  185. package/dist/src/evals/docker-engine-stats.js +89 -0
  186. package/dist/src/evals/docker-engine-stats.js.map +1 -0
  187. package/dist/src/evals/docker-ownership.js +51 -0
  188. package/dist/src/evals/docker-ownership.js.map +1 -0
  189. package/dist/src/evals/docker-reaper.js +144 -0
  190. package/dist/src/evals/docker-reaper.js.map +1 -0
  191. package/dist/src/evals/docker-resource-observer.js +207 -0
  192. package/dist/src/evals/docker-resource-observer.js.map +1 -0
  193. package/dist/src/evals/environment-image-planning.js +164 -0
  194. package/dist/src/evals/environment-image-planning.js.map +1 -0
  195. package/dist/src/evals/eval-artifact-planning.js +56 -0
  196. package/dist/src/evals/eval-artifact-planning.js.map +1 -0
  197. package/dist/src/evals/eval-finalization.js +10 -0
  198. package/dist/src/evals/eval-finalization.js.map +1 -0
  199. package/dist/src/evals/eval-lifecycle-events.js +16 -0
  200. package/dist/src/evals/eval-lifecycle-events.js.map +1 -0
  201. package/dist/src/evals/events.js +53 -5
  202. package/dist/src/evals/events.js.map +1 -1
  203. package/dist/src/evals/execution-evidence.js +116 -0
  204. package/dist/src/evals/execution-evidence.js.map +1 -0
  205. package/dist/src/evals/execution-leases.js +342 -0
  206. package/dist/src/evals/execution-leases.js.map +1 -0
  207. package/dist/src/evals/execution-plan-artifacts.js +72 -0
  208. package/dist/src/evals/execution-plan-artifacts.js.map +1 -0
  209. package/dist/src/evals/execution-plan-images.js +69 -0
  210. package/dist/src/evals/execution-plan-images.js.map +1 -0
  211. package/dist/src/evals/execution-plan-resources.js +174 -0
  212. package/dist/src/evals/execution-plan-resources.js.map +1 -0
  213. package/dist/src/evals/execution-plan.js +415 -0
  214. package/dist/src/evals/execution-plan.js.map +1 -0
  215. package/dist/src/evals/harbor-artifact-builder-image.js +99 -0
  216. package/dist/src/evals/harbor-artifact-builder-image.js.map +1 -0
  217. package/dist/src/evals/harbor-artifact-builder.js +354 -0
  218. package/dist/src/evals/harbor-artifact-builder.js.map +1 -0
  219. package/dist/src/evals/harbor-bridge-error.js +39 -3
  220. package/dist/src/evals/harbor-bridge-error.js.map +1 -1
  221. package/dist/src/evals/harbor-phase-timings.js +60 -0
  222. package/dist/src/evals/harbor-phase-timings.js.map +1 -0
  223. package/dist/src/evals/index.js +26 -3
  224. package/dist/src/evals/index.js.map +1 -1
  225. package/dist/src/evals/infrastructure-retry.js +218 -0
  226. package/dist/src/evals/infrastructure-retry.js.map +1 -0
  227. package/dist/src/evals/interaction-capture-import.js +49 -0
  228. package/dist/src/evals/interaction-capture-import.js.map +1 -0
  229. package/dist/src/evals/local-docker-provider.js +372 -0
  230. package/dist/src/evals/local-docker-provider.js.map +1 -0
  231. package/dist/src/evals/local-eval-planning.js +80 -0
  232. package/dist/src/evals/local-eval-planning.js.map +1 -0
  233. package/dist/src/evals/model-capture-plan.js +22 -0
  234. package/dist/src/evals/model-capture-plan.js.map +1 -0
  235. package/dist/src/evals/model-capture-runtime.js +114 -0
  236. package/dist/src/evals/model-capture-runtime.js.map +1 -0
  237. package/dist/src/evals/model-proxy-runtime-state.js +53 -0
  238. package/dist/src/evals/model-proxy-runtime-state.js.map +1 -0
  239. package/dist/src/evals/planned-execution-support.js +54 -0
  240. package/dist/src/evals/planned-execution-support.js.map +1 -0
  241. package/dist/src/evals/planned-execution.js +461 -0
  242. package/dist/src/evals/planned-execution.js.map +1 -0
  243. package/dist/src/evals/planned-remote-execution.js +40 -0
  244. package/dist/src/evals/planned-remote-execution.js.map +1 -0
  245. package/dist/src/evals/planned-retry-lifecycle.js +186 -0
  246. package/dist/src/evals/planned-retry-lifecycle.js.map +1 -0
  247. package/dist/src/evals/prepared-harness.js +76 -0
  248. package/dist/src/evals/prepared-harness.js.map +1 -0
  249. package/dist/src/evals/progress.js +4 -4
  250. package/dist/src/evals/progress.js.map +1 -1
  251. package/dist/src/evals/records.js +20 -3
  252. package/dist/src/evals/records.js.map +1 -1
  253. package/dist/src/evals/recovery.js +192 -0
  254. package/dist/src/evals/recovery.js.map +1 -0
  255. package/dist/src/evals/remote-infrastructure-retry.js +99 -0
  256. package/dist/src/evals/remote-infrastructure-retry.js.map +1 -0
  257. package/dist/src/evals/request.js +14 -1
  258. package/dist/src/evals/request.js.map +1 -1
  259. package/dist/src/evals/rerun-inputs.js +89 -0
  260. package/dist/src/evals/rerun-inputs.js.map +1 -0
  261. package/dist/src/evals/rerun-slots.js +12 -3
  262. package/dist/src/evals/rerun-slots.js.map +1 -1
  263. package/dist/src/evals/rerun-types.js +49 -0
  264. package/dist/src/evals/rerun-types.js.map +1 -0
  265. package/dist/src/evals/rerun.js +113 -75
  266. package/dist/src/evals/rerun.js.map +1 -1
  267. package/dist/src/evals/result-helpers.js +65 -0
  268. package/dist/src/evals/result-helpers.js.map +1 -0
  269. package/dist/src/evals/resume-state.js +36 -0
  270. package/dist/src/evals/resume-state.js.map +1 -0
  271. package/dist/src/evals/service-types.js +2 -0
  272. package/dist/src/evals/service-types.js.map +1 -0
  273. package/dist/src/evals/service.js +327 -237
  274. package/dist/src/evals/service.js.map +1 -1
  275. package/dist/src/evals/task-resources.js +209 -0
  276. package/dist/src/evals/task-resources.js.map +1 -0
  277. package/dist/src/evals/trial-environment-evidence.js +87 -0
  278. package/dist/src/evals/trial-environment-evidence.js.map +1 -0
  279. package/dist/src/evals/trial-execution-evidence.js +13 -0
  280. package/dist/src/evals/trial-execution-evidence.js.map +1 -0
  281. package/dist/src/evals/trial-import-identity.js +31 -0
  282. package/dist/src/evals/trial-import-identity.js.map +1 -0
  283. package/dist/src/evals/trial-import.js +166 -168
  284. package/dist/src/evals/trial-import.js.map +1 -1
  285. package/dist/src/evals/trial-publication-recovery.js +104 -0
  286. package/dist/src/evals/trial-publication-recovery.js.map +1 -0
  287. package/dist/src/evals/trial-publication.js +25 -0
  288. package/dist/src/evals/trial-publication.js.map +1 -0
  289. package/dist/src/evals/verifier-diagnostics.js +235 -0
  290. package/dist/src/evals/verifier-diagnostics.js.map +1 -0
  291. package/dist/src/evals/work-item-artifacts.js +23 -0
  292. package/dist/src/evals/work-item-artifacts.js.map +1 -0
  293. package/dist/src/foundation/config.js +17 -0
  294. package/dist/src/foundation/config.js.map +1 -1
  295. package/dist/src/foundation/credential-redaction.js +137 -0
  296. package/dist/src/foundation/credential-redaction.js.map +1 -0
  297. package/dist/src/foundation/environment-image-references.js +59 -0
  298. package/dist/src/foundation/environment-image-references.js.map +1 -0
  299. package/dist/src/foundation/executable.js +45 -5
  300. package/dist/src/foundation/executable.js.map +1 -1
  301. package/dist/src/foundation/index.js +5 -2
  302. package/dist/src/foundation/index.js.map +1 -1
  303. package/dist/src/foundation/process-identity.js +69 -0
  304. package/dist/src/foundation/process-identity.js.map +1 -0
  305. package/dist/src/images/context.js +47 -0
  306. package/dist/src/images/context.js.map +1 -0
  307. package/dist/src/images/docker-buildkit.js +137 -0
  308. package/dist/src/images/docker-buildkit.js.map +1 -0
  309. package/dist/src/images/dockerfile.js +67 -0
  310. package/dist/src/images/dockerfile.js.map +1 -0
  311. package/dist/src/images/gc.js +261 -0
  312. package/dist/src/images/gc.js.map +1 -0
  313. package/dist/src/images/index.js +10 -0
  314. package/dist/src/images/index.js.map +1 -0
  315. package/dist/src/images/manifest.js +109 -0
  316. package/dist/src/images/manifest.js.map +1 -0
  317. package/dist/src/images/ownership.js +5 -0
  318. package/dist/src/images/ownership.js.map +1 -0
  319. package/dist/src/images/records.js +32 -0
  320. package/dist/src/images/records.js.map +1 -0
  321. package/dist/src/images/registry.js +154 -0
  322. package/dist/src/images/registry.js.map +1 -0
  323. package/dist/src/images/service.js +224 -0
  324. package/dist/src/images/service.js.map +1 -0
  325. package/dist/src/model-access/capture.js +255 -0
  326. package/dist/src/model-access/capture.js.map +1 -0
  327. package/dist/src/model-access/index.js +5 -0
  328. package/dist/src/model-access/index.js.map +1 -0
  329. package/dist/src/model-access/policy.js +49 -0
  330. package/dist/src/model-access/policy.js.map +1 -0
  331. package/dist/src/model-access/proxy.js +336 -0
  332. package/dist/src/model-access/proxy.js.map +1 -0
  333. package/dist/src/model-access/records.js +167 -0
  334. package/dist/src/model-access/records.js.map +1 -0
  335. package/dist/src/runs/bundle.js +450 -0
  336. package/dist/src/runs/bundle.js.map +1 -0
  337. package/dist/src/runs/events.js +12 -1
  338. package/dist/src/runs/events.js.map +1 -1
  339. package/dist/src/runs/executor.js +24 -17
  340. package/dist/src/runs/executor.js.map +1 -1
  341. package/dist/src/runs/index.js +3 -1
  342. package/dist/src/runs/index.js.map +1 -1
  343. package/dist/src/runs/manifest.js +5 -5
  344. package/dist/src/runs/manifest.js.map +1 -1
  345. package/dist/src/runs/request.js +6 -1
  346. package/dist/src/runs/request.js.map +1 -1
  347. package/dist/src/runs/training-candidate.js +161 -0
  348. package/dist/src/runs/training-candidate.js.map +1 -0
  349. package/dist/src/trajectories/provider-capture.js +15 -24
  350. package/dist/src/trajectories/provider-capture.js.map +1 -1
  351. package/dist/src/trajectories/providers/deepseek.js +3 -3
  352. package/dist/src/trajectories/providers/deepseek.js.map +1 -1
  353. package/dist/src/workers/index.js +3 -0
  354. package/dist/src/workers/index.js.map +1 -0
  355. package/dist/src/workers/remote-harbor-work-spec.js +97 -0
  356. package/dist/src/workers/remote-harbor-work-spec.js.map +1 -0
  357. package/dist/src/workers/remote-harbor-worker.js +220 -0
  358. package/dist/src/workers/remote-harbor-worker.js.map +1 -0
  359. package/docs/schemas/environment-build-index.schema.json +13 -0
  360. package/docs/schemas/environment-build-record.schema.json +25 -0
  361. package/docs/schemas/environment-image-manifest.schema.json +62 -0
  362. package/docs/schemas/eval-control.schema.json +36 -0
  363. package/docs/schemas/eval-execution-plan.schema.json +216 -0
  364. package/docs/schemas/eval-request.schema.json +2 -0
  365. package/docs/schemas/eval-rerun-result.schema.json +53 -0
  366. package/docs/schemas/eval-rerun-state.schema.json +61 -0
  367. package/docs/schemas/eval-rerun-submission.schema.json +58 -0
  368. package/docs/schemas/eval-result.schema.json +34 -1
  369. package/docs/schemas/eval-submission-input.schema.json +14 -0
  370. package/docs/schemas/eval-submission.schema.json +68 -0
  371. package/docs/schemas/eval-trial-publication.schema.json +39 -0
  372. package/docs/schemas/execution-evidence.schema.json +96 -0
  373. package/docs/schemas/execution-lease.schema.json +50 -0
  374. package/docs/schemas/execution-provider-status.schema.json +65 -0
  375. package/docs/schemas/execution-worker.schema.json +53 -0
  376. package/docs/schemas/harbor-process-exit.schema.json +14 -0
  377. package/docs/schemas/interaction-capture-ref.schema.json +45 -0
  378. package/docs/schemas/local-provider-execution.schema.json +33 -0
  379. package/docs/schemas/model-interaction.schema.json +41 -0
  380. package/docs/schemas/remote-result-envelope.schema.json +32 -0
  381. package/docs/schemas/remote-tree-envelope.schema.json +25 -0
  382. package/docs/schemas/remote-work-offer.schema.json +58 -0
  383. package/docs/schemas/remote-work-receipt.schema.json +50 -0
  384. package/docs/schemas/remote-worker-artifact.schema.json +17 -0
  385. package/docs/schemas/remote-worker-event.schema.json +18 -0
  386. package/docs/schemas/remote-worker-heartbeat.schema.json +37 -0
  387. package/docs/schemas/remote-worker-registration.schema.json +52 -0
  388. package/docs/schemas/result-bundle-index.schema.json +133 -0
  389. package/docs/schemas/run-environment-images.schema.json +39 -0
  390. package/docs/schemas/training-data-candidate.schema.json +46 -0
  391. package/integrations/harbor/hitch_harbor_agent.py +300 -547
  392. package/integrations/harbor/hitch_harbor_environment.py +331 -0
  393. package/integrations/harbor/hitch_harbor_task_resources.py +275 -0
  394. package/integrations/harbor/hitch_harbor_verifier.py +337 -0
  395. package/package.json +16 -2
@@ -1,42 +1,56 @@
1
1
  import path from "node:path";
2
- import { mkdir } from "node:fs/promises";
3
- import { prepareHarness, preparedArtifactDirectory, resolveHarness } from "../artifacts/index.js";
4
- import { HitchError, SCHEMA_VERSION, atomicWriteJSON, ensureDir, invalidInput, statePaths } from "../foundation/index.js";
2
+ import { resolveHarness } from "../artifacts/index.js";
3
+ import { HitchError, SCHEMA_VERSION, atomicWriteJSON, beginEvalEnvironmentImagePlanning, credentialValuesFromEnv, ensureDir, invalidInput, safeDiagnosticMessage, statePaths, withEnvironmentImageReferenceLock, writeEvalEnvironmentImageReferences } from "../foundation/index.js";
5
4
  import { parseHarnessReference } from "../revisions/index.js";
6
5
  import { buildLocalGitTransport, lockedHarnessRef, runHarborBackend, verifyLocalGitTransport } from "../backends/index.js";
7
6
  import { ensureControllerRuntime, writeRuntimeReference } from "../controller-runtime/index.js";
8
7
  import { importEvalTrialRun, importEvalTrialRuns, TrialBundlePendingError, validateEvalTrialReferences } from "./trial-import.js";
9
8
  import { EvalEventSink } from "./events.js";
10
9
  import { createEvalProgress, mergeEvalProgressTrial, writeEvalProgress } from "./progress.js";
10
+ import { infrastructureFailureTrials, runInfrastructureRetries } from "./infrastructure-retry.js";
11
11
  import { newEvalId, resolveLocalDatasetTaskIds, validateEvalId, validateEvalRequest } from "./request.js";
12
12
  import { invalidTrialSlots } from "./rerun-slots.js";
13
- export async function runEval({ evalId = newEvalId(), request, root, env = process.env, harborExecutable, signal, onEvent, trialBundleGraceMs }) {
13
+ import { prepareEvalDirectory } from "./directory.js";
14
+ import { buildEvalExecutionPlan, DEFAULT_EVAL_TRIAL_RESOURCES } from "./execution-plan.js";
15
+ import { planLocalEvalInputs } from "./local-eval-planning.js";
16
+ import { resolvedImageMapping } from "./environment-image-planning.js";
17
+ import { assertBackendTrialSet, attemptDirectoryName, localSourceBackendFailure, summarizeTrialRefs, transportSummary } from "./result-helpers.js";
18
+ import { executePlannedHarborTasks } from "./planned-execution.js";
19
+ import { assertEvalResumeState, executionPlanWorkState, loadEvalResumeState } from "./resume-state.js";
20
+ import { modelCaptureDegradationEvent, resolveEvalModelCapturePlan } from "./model-capture-plan.js";
21
+ import { finalizeEvalResult } from "./eval-finalization.js";
22
+ import { prepareHostHarborArtifactForTest } from "./prepared-harness.js";
23
+ import { prepareHarborArtifact } from "./harbor-artifact-builder.js";
24
+ import { prepareEvalArtifactAssignments, preparedArtifactPlanFields } from "./eval-artifact-planning.js";
25
+ import { startEvalModelCaptureRuntime } from "./model-capture-runtime.js";
26
+ import { recoverPromotedEvalTrialPublications } from "./trial-publication-recovery.js";
27
+ import { emitEvalPlanLifecycle } from "./eval-lifecycle-events.js";
28
+ export async function runEval({ evalId = newEvalId(), request, root, env = process.env, harborExecutable, signal, onEvent, trialBundleGraceMs, precreated = false, normalizedRequest, maxConcurrentOverride, executionResources, executionResourceSource = "operator-default", executionStrategy = "legacy-attempt-shards", executionWorker, modelCapturePlan, workItemAdmission, remoteWorkExecutor, resumeExisting = false, onControlPhase, onWorkItemState, dockerResourceReaper, environmentBuildMode = "backend", environmentImageResolver, environmentImageBuilder, environmentImageManifestLoader, harborArtifactBuilder }) {
14
29
  if (!root)
15
30
  throw invalidInput("a Hitch state root is required for eval");
16
31
  evalId = validateEvalId(evalId);
17
- const normalized = await validateEvalRequest(request);
18
- const evalsDirectory = await ensureDir(statePaths(root).evals);
19
- const evalDirectory = path.join(evalsDirectory, evalId);
20
- try {
21
- await mkdir(evalDirectory, { mode: 0o700 });
22
- }
23
- catch (error) {
24
- if (error.code === "EEXIST") {
25
- throw new HitchError(`eval ID already exists: ${evalId}`, { code: "eval_id_conflict", exitCode: 2 });
26
- }
27
- throw error;
32
+ const persistedRequest = normalizedRequest || await validateEvalRequest(request);
33
+ if (maxConcurrentOverride !== undefined && (!Number.isSafeInteger(maxConcurrentOverride) || maxConcurrentOverride < 1 || maxConcurrentOverride > persistedRequest.max_concurrent)) {
34
+ throw invalidInput("control-plane max concurrency override is invalid");
28
35
  }
29
- const startedAt = new Date();
36
+ const normalized = maxConcurrentOverride === undefined ? persistedRequest : { ...persistedRequest, max_concurrent: maxConcurrentOverride };
37
+ const evalsDirectory = await ensureDir(statePaths(root).evals);
38
+ const evalDirectory = await prepareEvalDirectory({ evalsDirectory, evalId, request: persistedRequest, precreated });
39
+ let startedAt = new Date();
30
40
  const sink = new EvalEventSink(evalDirectory, evalId, onEvent);
31
41
  await sink.open();
32
- await atomicWriteJSON(path.join(evalDirectory, "request.json"), normalized);
33
42
  let result;
34
43
  let trialRefs = [];
35
44
  let progress = null;
45
+ let captureRuntime;
36
46
  try {
47
+ const planningStartedAt = Date.now();
37
48
  sink.emit({ type: "eval.started", backend: normalized.backend, dataset: normalized.dataset });
49
+ sink.emit({ type: "eval.planning.started" });
50
+ await onControlPhase?.("planning");
38
51
  const requestedReference = parseHarnessReference(normalized.harness_ref);
39
52
  const resolvedRevision = await resolveHarness(normalized.harness_ref, { root, env });
53
+ await onControlPhase?.("preparing");
40
54
  let localTransport;
41
55
  if (resolvedRevision.source.type === "git" && resolvedRevision.source.registered !== true) {
42
56
  const selector = requestedReference.selector;
@@ -61,50 +75,6 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
61
75
  status: "verified",
62
76
  });
63
77
  }
64
- const selector = requestedReference.selector;
65
- const verifiedLocalGitSource = localTransport && selector.type === "commit" && selector.source?.explicit
66
- ? {
67
- directory: selector.source.local_path,
68
- commit: localTransport.manifest.commit,
69
- tree: localTransport.manifest.tree,
70
- resolutionIdentity: localTransport.manifest.resolution_identity,
71
- payloadSha256: localTransport.manifest.payload_sha256,
72
- }
73
- : undefined;
74
- const artifact = await prepareHarness(resolvedRevision, {
75
- root,
76
- env,
77
- ...(signal ? { signal } : {}),
78
- ...(verifiedLocalGitSource ? { verifiedLocalGitSource } : {}),
79
- });
80
- if (!artifact.artifact_integrity || !artifact.entrypoint_integrity) {
81
- throw new HitchError("host-prepared harness artifact has no complete integrity metadata", {
82
- code: "artifact_integrity_mismatch",
83
- exitCode: 5,
84
- });
85
- }
86
- const preparedArtifact = {
87
- directory: preparedArtifactDirectory(root, artifact.artifact_id),
88
- artifact_id: artifact.artifact_id,
89
- artifact_integrity: artifact.artifact_integrity,
90
- entrypoint_integrity: artifact.entrypoint_integrity,
91
- harness_id: artifact.harness_id,
92
- revision_identity: artifact.revision_identity,
93
- adapter_version: artifact.adapter_version,
94
- recipe_version: artifact.recipe_version,
95
- platform: artifact.platform,
96
- node_version: artifact.toolchain.node || process.version,
97
- source_type: artifact.source_type,
98
- };
99
- sink.emit({
100
- type: "eval.harness-artifact.prepared",
101
- harness: artifact.harness_id,
102
- artifact_id: artifact.artifact_id,
103
- platform: artifact.platform,
104
- cache_hit: artifact.cache_hit,
105
- });
106
- // Phase 2: the shared, read-only, SHA-256-addressed controller runtime
107
- // cache replaces the per-eval Hitch runtime copy (spec §4).
108
78
  const controllerRuntime = await ensureControllerRuntime({ root });
109
79
  const runtimeRefFile = await writeRuntimeReference(evalDirectory, controllerRuntime);
110
80
  sink.emit({
@@ -114,10 +84,27 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
114
84
  reference: runtimeRefFile,
115
85
  });
116
86
  const localTaskIds = await resolveLocalDatasetTaskIds(normalized.dataset);
87
+ const resume = resumeExisting ? await loadEvalResumeState(evalDirectory) : null;
88
+ await withEnvironmentImageReferenceLock(root, () => beginEvalEnvironmentImagePlanning(evalDirectory, evalId));
89
+ const localPlanning = await planLocalEvalInputs({ root, dataset: normalized.dataset, taskIds: localTaskIds, defaultResources: executionResources ?? DEFAULT_EVAL_TRIAL_RESOURCES, defaultSource: executionResourceSource, benchmarkId: normalized.benchmark_id, benchmarkRevision: normalized.benchmark_revision, buildMode: environmentBuildMode, harborTaskResourceInspector: path.join(controllerRuntime.directory, "payload", "integrations", "harbor", "hitch_harbor_task_resources.py"), ...(environmentImageResolver ? { resolver: environmentImageResolver } : {}), ...(environmentImageBuilder ? { builder: environmentImageBuilder } : {}), ...(resume ? { resumePlan: resume.executionPlan } : {}), ...(harborExecutable ? { harborExecutable } : {}), env, ...(signal ? { signal } : {}) });
90
+ await withEnvironmentImageReferenceLock(root, () => writeEvalEnvironmentImageReferences(evalDirectory, evalId, localPlanning.environmentImages));
91
+ const selectedArtifactBuilder = harborArtifactBuilder ?? (env.NODE_ENV === "test" && env.HITCH_TEST_HOST_ARTIFACT_BUILDER === "1" ? prepareHostHarborArtifactForTest : prepareHarborArtifact);
92
+ const prepared = await prepareEvalArtifactAssignments({ builder: selectedArtifactBuilder, root, resolvedRevision, requestedReference, controllerRuntime, ...(localTransport ? { localTransport } : {}), taskRuntimeContracts: localPlanning.taskRuntimeContracts, sink, env, ...(signal ? { signal } : {}) });
93
+ const preparedAssignments = prepared.assignments;
94
+ const preparedArtifact = prepared.primary;
95
+ const preparedArtifacts = prepared.artifactsById;
96
+ const multipleRuntimeContracts = preparedAssignments.length > 1;
97
+ const taskResources = localPlanning.taskResources;
117
98
  const plannedTasks = localTaskIds?.length ?? null;
118
99
  const plannedTrials = plannedTasks === null ? null : plannedTasks * normalized.attempts;
119
100
  if (plannedTrials !== null && !Number.isSafeInteger(plannedTrials))
120
101
  throw invalidInput("planned trial count exceeds the safe integer range");
102
+ if (resume)
103
+ startedAt = new Date(resume.progress.started_at);
104
+ const capture = resolveEvalModelCapturePlan({ requested: modelCapturePlan, resumed: resume?.executionPlan.model_capture, resuming: Boolean(resume) });
105
+ const activeCaptureRuntime = await startEvalModelCaptureRuntime({ plan: capture.plan, evalId, evalDirectory, env });
106
+ captureRuntime = activeCaptureRuntime;
107
+ capture.plan = activeCaptureRuntime.plan;
121
108
  const plan = {
122
109
  schema_version: SCHEMA_VERSION,
123
110
  eval_id: evalId,
@@ -134,20 +121,62 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
134
121
  benchmark_id: normalized.benchmark_id,
135
122
  benchmark_revision: normalized.benchmark_revision,
136
123
  attempts: normalized.attempts,
137
- attempt_execution: "harbor-attempt-shards-v1",
124
+ attempt_execution: (executionStrategy === "local-task-slots-v1" || multipleRuntimeContracts) && localTaskIds !== null
125
+ ? "harbor-task-slots-v1"
126
+ : "harbor-attempt-shards-v1",
138
127
  max_concurrent: normalized.max_concurrent,
128
+ ...(capture.persist ? { model_capture: capture.plan } : {}),
129
+ infrastructure_retries: normalized.infrastructure_retries,
130
+ infrastructure_retry_backoff_ms: normalized.infrastructure_retry_backoff_ms,
139
131
  ...(localTaskIds === null ? {} : { tasks: localTaskIds }),
140
132
  controller_runtime: {
141
133
  runtime_id: controllerRuntime.runtime_id,
142
134
  manifest_digest: controllerRuntime.manifest_digest,
143
135
  },
144
- prepared_artifact: preparedArtifactSummary(preparedArtifact),
136
+ ...preparedArtifactPlanFields(preparedAssignments),
145
137
  ...(localTransport ? { local_source_transport: transportSummary(localTransport) } : {}),
146
- created_at: new Date().toISOString(),
138
+ created_at: typeof resume?.plan.created_at === "string" ? resume.plan.created_at : new Date().toISOString(),
147
139
  };
148
- await atomicWriteJSON(path.join(evalDirectory, "resolution.json"), resolvedRevision);
149
- await atomicWriteJSON(path.join(evalDirectory, "plan.json"), plan);
150
- progress = createEvalProgress({
140
+ const expectedExecutionPlan = buildEvalExecutionPlan({
141
+ evalId,
142
+ request: normalized,
143
+ candidate: {
144
+ revisionIdentity: resolvedRevision.identity,
145
+ artifactId: preparedArtifact.artifact_id,
146
+ artifactAssignments: preparedAssignments.map((entry) => ({
147
+ taskIds: entry.taskIds,
148
+ artifactId: entry.artifact.artifact_id,
149
+ runtimeContract: {
150
+ docker_platform: entry.runtimeContract.dockerPlatform,
151
+ artifact_platform: entry.runtimeContract.artifactPlatform,
152
+ node_version: entry.runtimeContract.nodeVersion,
153
+ },
154
+ })),
155
+ },
156
+ tasks: localTaskIds,
157
+ maxParallelism: normalized.max_concurrent,
158
+ ...(executionResources ? { trialResources: executionResources } : {}),
159
+ ...(taskResources ? { taskResources } : {}),
160
+ ...(localPlanning.environmentImages.length > 0 ? { environmentImages: localPlanning.environmentImages } : {}),
161
+ ...(localPlanning.environmentImageFallbacks.length > 0 ? { environmentImageFallbacks: localPlanning.environmentImageFallbacks } : {}),
162
+ ...(executionWorker ? { provider: executionWorker.provider } : {}),
163
+ modelCapture: capture.persist ? capture.plan : null,
164
+ ...((executionStrategy === "local-task-slots-v1" || multipleRuntimeContracts) && localTaskIds !== null ? { workItemMode: "task-slots" } : {}),
165
+ createdAt: plan.created_at,
166
+ });
167
+ if (resume)
168
+ assertEvalResumeState({ state: resume, expectedPlan: plan, expectedExecutionPlan, expectedResolutionIdentity: resolvedRevision.identity, plannedTasks, plannedTrials });
169
+ else
170
+ await Promise.all([
171
+ atomicWriteJSON(path.join(evalDirectory, "resolution.json"), resolvedRevision),
172
+ atomicWriteJSON(path.join(evalDirectory, "plan.json"), plan),
173
+ atomicWriteJSON(path.join(evalDirectory, "execution-plan.json"), expectedExecutionPlan),
174
+ ]);
175
+ const executionPlan = resume?.executionPlan ?? expectedExecutionPlan;
176
+ const captureDegradation = modelCaptureDegradationEvent(executionPlan.model_capture);
177
+ if (captureDegradation)
178
+ sink.emit(captureDegradation);
179
+ progress = resume?.progress ?? createEvalProgress({
151
180
  evalId,
152
181
  benchmarkId: normalized.benchmark_id,
153
182
  benchmarkRevision: normalized.benchmark_revision,
@@ -155,7 +184,10 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
155
184
  plannedTrials,
156
185
  startedAt: startedAt.toISOString(),
157
186
  });
158
- await writeEvalProgress(evalDirectory, progress);
187
+ if (!resume)
188
+ await writeEvalProgress(evalDirectory, progress);
189
+ else
190
+ progress = (await recoverPromotedEvalTrialPublications({ root, evalDirectory, plan: executionPlan, progress, sink })).progress;
159
191
  sink.emit({
160
192
  type: "eval.planned",
161
193
  harness: resolvedRevision.harness_id,
@@ -163,7 +195,11 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
163
195
  attempts: normalized.attempts,
164
196
  planned_tasks: plannedTasks,
165
197
  planned_trials: plannedTrials,
198
+ work_items: executionPlan.work_items.length,
199
+ membership: executionPlan.membership,
166
200
  });
201
+ emitEvalPlanLifecycle(sink, executionPlan, planningStartedAt);
202
+ await onControlPhase?.("running", executionPlanWorkState(executionPlan, progress));
167
203
  if (localTransport) {
168
204
  await verifyLocalGitTransport(localTransport, {
169
205
  expected: {
@@ -176,126 +212,213 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
176
212
  });
177
213
  }
178
214
  const backendRuns = [];
179
- for (let logicalAttempt = 1; logicalAttempt <= normalized.attempts; logicalAttempt += 1) {
180
- if (signal?.aborted)
181
- break;
182
- const backendDirectory = normalized.attempts === 1
183
- ? path.join(evalDirectory, "harbor")
184
- : path.join(evalDirectory, "harbor", attemptDirectoryName(logicalAttempt));
185
- const harborJobDirectory = path.join(backendDirectory, "job");
186
- const shardRefs = [];
187
- const publish = async (ref) => {
188
- if (ref.attempt !== logicalAttempt) {
189
- throw new HitchError(`Harbor returned attempt ${ref.attempt}, expected ${logicalAttempt}`, {
190
- code: "eval_trial_attempt_mismatch",
191
- exitCode: 12,
192
- });
193
- }
194
- if (!shardRefs.some((current) => current.trial_id === ref.trial_id))
195
- shardRefs.push(ref);
196
- if (progress === null)
197
- throw new Error("eval progress was not initialized");
198
- const previousGeneration = progress.generation;
199
- progress = mergeEvalProgressTrial(progress, ref);
200
- if (progress.generation === previousGeneration)
201
- return;
202
- await validateEvalTrialReferences(root, evalId, [ref], {
203
- benchmarkId: normalized.benchmark_id,
204
- benchmarkRevision: normalized.benchmark_revision,
205
- });
206
- await writeEvalProgress(evalDirectory, progress);
207
- sink.emit({
208
- type: "eval.trial.published",
209
- trial_id: ref.trial_id,
210
- task_id: ref.task_id,
211
- attempt: ref.attempt,
212
- run_id: ref.run_id,
213
- observation_status: ref.observation_status,
214
- settled_trials: progress.trials.length,
215
- generation: progress.generation,
216
- });
217
- };
218
- const backendRun = await runHarborBackend({
215
+ const infrastructureRetryRuns = [];
216
+ const plannedTaskExecution = (executionStrategy === "local-task-slots-v1" || multipleRuntimeContracts) && localTaskIds !== null;
217
+ if (plannedTaskExecution) {
218
+ const execution = await executePlannedHarborTasks({
219
219
  evalId,
220
220
  evalDirectory,
221
- backendDirectory,
222
- logicalAttempt,
223
- request: { ...normalized, attempts: 1 },
221
+ plan: executionPlan,
222
+ progress,
223
+ request: normalized,
224
224
  root,
225
225
  resolvedRevision,
226
- runtimeDirectory: controllerRuntime.directory,
227
- runtimeId: controllerRuntime.runtime_id,
226
+ controllerRuntime,
228
227
  preparedArtifact,
228
+ preparedArtifacts,
229
229
  ...(localTransport ? { localTransport } : {}),
230
230
  env,
231
231
  ...(harborExecutable !== undefined ? { harborExecutable } : {}),
232
232
  ...(signal ? { signal } : {}),
233
233
  ...(trialBundleGraceMs === undefined ? {} : { trialBundleGraceMs }),
234
- emit: (event) => sink.emit(event),
235
- onTrialSettled: async (trial, context) => {
236
- try {
237
- const ref = await importEvalTrialRun({
238
- root,
239
- evalId,
240
- evalDirectory,
241
- harborJobDirectory,
242
- expectedAttempt: logicalAttempt,
243
- request: normalized,
244
- resolvedRevision,
245
- benchmarkId: normalized.benchmark_id,
246
- benchmarkRevision: normalized.benchmark_revision,
247
- runtimeId: controllerRuntime.runtime_id,
248
- requireCompleteMarker: true,
249
- allowMissingBundleDiagnostic: context.bundleWaitExpired,
250
- }, trial, shardRefs.length, shardRefs);
251
- await publish(ref);
252
- return true;
253
- }
254
- catch (error) {
255
- if (error instanceof TrialBundlePendingError)
256
- return false;
257
- throw error;
258
- }
234
+ sink,
235
+ worker: executionWorker || {
236
+ workerId: `worker_process_${process.pid}`,
237
+ provider: "local-docker",
238
+ collisionDomainId: `local-process:${process.pid}`,
259
239
  },
240
+ ...(workItemAdmission ? { admission: workItemAdmission } : {}),
241
+ ...(onWorkItemState ? { onWorkItemState } : {}),
242
+ ...(dockerResourceReaper ? { dockerResourceReaper } : {}),
243
+ ...(environmentImageManifestLoader ? { environmentImageManifestLoader } : {}),
244
+ ...(activeCaptureRuntime.exporter ? { interactionCaptureExporter: activeCaptureRuntime.exporter } : {}),
245
+ ...(remoteWorkExecutor ? { remoteWorkExecutor } : {}),
260
246
  });
261
- backendRuns.push({ attempt: logicalAttempt, run: backendRun });
262
- const terminalRefs = await importEvalTrialRuns({
263
- root,
264
- evalId,
265
- evalDirectory,
266
- harborJobDirectory,
267
- expectedAttempt: logicalAttempt,
268
- request: normalized,
269
- resolvedRevision,
270
- benchmarkId: normalized.benchmark_id,
271
- benchmarkRevision: normalized.benchmark_revision,
272
- runtimeId: controllerRuntime.runtime_id,
273
- rawResult: backendRun.rawResult,
274
- }, shardRefs);
275
- for (const ref of terminalRefs)
276
- await publish(ref);
277
- assertBackendTrialSet(backendRun.rawResult, shardRefs);
278
- await validateEvalTrialReferences(root, evalId, shardRefs, {
279
- benchmarkId: normalized.benchmark_id,
280
- benchmarkRevision: normalized.benchmark_revision,
281
- });
282
- if (backendRun.backend.process_exit_code !== 0 || backendRun.rawResult === null)
283
- break;
284
- if (localTransport && localSourceBackendFailure(backendRun.rawResult))
285
- break;
247
+ progress = execution.progress;
248
+ backendRuns.push(...execution.backendRuns.map((entry) => ({
249
+ attempt: entry.attempt,
250
+ run: entry.run,
251
+ workId: entry.workId,
252
+ tasks: entry.tasks,
253
+ leaseId: entry.leaseId,
254
+ })));
255
+ infrastructureRetryRuns.push(...execution.infrastructureRetryRuns);
286
256
  }
257
+ else
258
+ for (let logicalAttempt = 1; logicalAttempt <= normalized.attempts; logicalAttempt += 1) {
259
+ if (signal?.aborted)
260
+ break;
261
+ const backendDirectory = normalized.attempts === 1
262
+ ? path.join(evalDirectory, "harbor")
263
+ : path.join(evalDirectory, "harbor", attemptDirectoryName(logicalAttempt));
264
+ const harborJobDirectory = path.join(backendDirectory, "job");
265
+ const shardRefs = [];
266
+ const publish = async (ref) => {
267
+ if (ref.attempt !== logicalAttempt) {
268
+ throw new HitchError(`Harbor returned attempt ${ref.attempt}, expected ${logicalAttempt}`, {
269
+ code: "eval_trial_attempt_mismatch",
270
+ exitCode: 12,
271
+ });
272
+ }
273
+ if (!shardRefs.some((current) => current.trial_id === ref.trial_id))
274
+ shardRefs.push(ref);
275
+ if (progress === null)
276
+ throw new Error("eval progress was not initialized");
277
+ const previousGeneration = progress.generation;
278
+ progress = mergeEvalProgressTrial(progress, ref);
279
+ if (progress.generation === previousGeneration)
280
+ return;
281
+ await validateEvalTrialReferences(root, evalId, [ref], {
282
+ benchmarkId: normalized.benchmark_id,
283
+ benchmarkRevision: normalized.benchmark_revision,
284
+ });
285
+ await writeEvalProgress(evalDirectory, progress);
286
+ sink.emit({
287
+ type: "eval.trial.published",
288
+ trial_id: ref.trial_id,
289
+ task_id: ref.task_id,
290
+ attempt: ref.attempt,
291
+ run_id: ref.run_id,
292
+ observation_status: ref.observation_status,
293
+ settled_trials: progress.trials.length,
294
+ generation: progress.generation,
295
+ });
296
+ };
297
+ const backendRun = await runHarborBackend({
298
+ evalId,
299
+ evalDirectory,
300
+ backendDirectory,
301
+ logicalAttempt,
302
+ request: { ...normalized, attempts: 1 },
303
+ root,
304
+ resolvedRevision,
305
+ runtimeDirectory: controllerRuntime.directory,
306
+ runtimeId: controllerRuntime.runtime_id,
307
+ preparedArtifact,
308
+ ...(activeCaptureRuntime.exporter ? { modelProxy: activeCaptureRuntime.exporter.route } : {}),
309
+ resolvedImages: resolvedImageMapping(executionPlan.work_items.find((item) => item.logical_attempt === logicalAttempt)?.image_refs ?? []),
310
+ ...(executionResources ? { executionResources } : {}),
311
+ env,
312
+ ...(harborExecutable !== undefined ? { harborExecutable } : {}),
313
+ ...(signal ? { signal } : {}),
314
+ ...(trialBundleGraceMs === undefined ? {} : { trialBundleGraceMs }),
315
+ emit: (event) => sink.emit(event),
316
+ onTrialSettled: async (trial, context) => {
317
+ try {
318
+ const ref = await importEvalTrialRun({
319
+ root,
320
+ evalId,
321
+ evalDirectory,
322
+ harborJobDirectory,
323
+ expectedAttempt: logicalAttempt,
324
+ request: normalized,
325
+ resolvedRevision,
326
+ benchmarkId: normalized.benchmark_id,
327
+ benchmarkRevision: normalized.benchmark_revision,
328
+ runtimeId: controllerRuntime.runtime_id,
329
+ env,
330
+ modelCapturePlan: activeCaptureRuntime.plan,
331
+ ...(activeCaptureRuntime.exporter ? { interactionCaptureExporter: activeCaptureRuntime.exporter } : {}),
332
+ requireCompleteMarker: true,
333
+ allowMissingBundleDiagnostic: context.bundleWaitExpired,
334
+ }, trial, shardRefs.length, shardRefs);
335
+ await publish(ref);
336
+ return true;
337
+ }
338
+ catch (error) {
339
+ if (error instanceof TrialBundlePendingError)
340
+ return false;
341
+ throw error;
342
+ }
343
+ },
344
+ });
345
+ backendRuns.push({ attempt: logicalAttempt, run: backendRun });
346
+ const terminalRefs = await importEvalTrialRuns({
347
+ root,
348
+ evalId,
349
+ evalDirectory,
350
+ harborJobDirectory,
351
+ expectedAttempt: logicalAttempt,
352
+ request: normalized,
353
+ resolvedRevision,
354
+ benchmarkId: normalized.benchmark_id,
355
+ benchmarkRevision: normalized.benchmark_revision,
356
+ runtimeId: controllerRuntime.runtime_id,
357
+ env,
358
+ modelCapturePlan: activeCaptureRuntime.plan,
359
+ ...(activeCaptureRuntime.exporter ? { interactionCaptureExporter: activeCaptureRuntime.exporter } : {}),
360
+ rawResult: backendRun.rawResult,
361
+ }, shardRefs);
362
+ for (const ref of terminalRefs)
363
+ await publish(ref);
364
+ assertBackendTrialSet(backendRun.rawResult, shardRefs);
365
+ await validateEvalTrialReferences(root, evalId, shardRefs, {
366
+ benchmarkId: normalized.benchmark_id,
367
+ benchmarkRevision: normalized.benchmark_revision,
368
+ });
369
+ if (backendRun.backend.process_exit_code !== 0 || backendRun.rawResult === null)
370
+ break;
371
+ if (localTransport && localSourceBackendFailure(backendRun.rawResult))
372
+ break;
373
+ if (progress === null)
374
+ throw new Error("eval progress was not initialized");
375
+ const retries = await runInfrastructureRetries({
376
+ evalId,
377
+ evalDirectory,
378
+ logicalAttempt,
379
+ initialRefs: shardRefs,
380
+ progress,
381
+ request: normalized,
382
+ root,
383
+ resolvedRevision,
384
+ controllerRuntime,
385
+ preparedArtifact,
386
+ resolvedImages: resolvedImageMapping(executionPlan.work_items.find((item) => item.logical_attempt === logicalAttempt)?.image_refs ?? []),
387
+ ...(executionResources ? { executionResources } : {}),
388
+ modelCapturePlan: activeCaptureRuntime.plan,
389
+ ...(activeCaptureRuntime.exporter ? { interactionCaptureExporter: activeCaptureRuntime.exporter } : {}),
390
+ ...(localTransport ? { localTransport } : {}),
391
+ env,
392
+ ...(harborExecutable !== undefined ? { harborExecutable } : {}),
393
+ ...(signal ? { signal } : {}),
394
+ ...(trialBundleGraceMs === undefined ? {} : { trialBundleGraceMs }),
395
+ sink,
396
+ ...(localTransport ? { stopAfterResult: localSourceBackendFailure } : {}),
397
+ });
398
+ progress = retries.progress;
399
+ infrastructureRetryRuns.push(...retries.runs);
400
+ }
287
401
  if (progress === null)
288
402
  throw new Error("eval progress was not initialized");
403
+ await onControlPhase?.("finalizing");
289
404
  trialRefs = progress.trials;
290
405
  const cancelled = signal?.aborted === true;
291
406
  const localSourceFailure = localTransport
292
407
  ? backendRuns.some(({ run }) => localSourceBackendFailure(run.rawResult))
408
+ || infrastructureRetryRuns.some(({ run }) => localSourceBackendFailure(run.rawResult))
293
409
  : false;
294
- const backendsSucceeded = backendRuns.length === normalized.attempts
295
- && backendRuns.every(({ run }) => run.backend.process_exit_code === 0 && run.rawResult !== null);
410
+ const expectedBackendRuns = plannedTaskExecution ? executionPlan.work_items.length : normalized.attempts;
411
+ const backendsSucceeded = backendRuns.every(({ run }) => run.backend.process_exit_code === 0 && run.rawResult !== null)
412
+ && (plannedTaskExecution ? progress.trials.length === plannedTrials : backendRuns.length === expectedBackendRuns);
296
413
  const invalidTrials = localTaskIds === null
297
414
  ? trialRefs.filter((trial) => trial.observation_status !== "valid").map((trial) => ({ task_id: trial.task_id, attempt: trial.attempt }))
298
415
  : invalidTrialSlots(localTaskIds, normalized.attempts, progress);
416
+ const infrastructureFailures = infrastructureFailureTrials(trialRefs);
417
+ const infrastructureFailureSlots = infrastructureFailures.map((trial) => ({ task_id: trial.task_id, attempt: trial.attempt }));
418
+ const verifierRetriesExhausted = normalized.infrastructure_retries > 0 && infrastructureFailures.some((trial) => trial.invalid_reason === "verifier_infrastructure_failure");
419
+ const infrastructureErrorCode = verifierRetriesExhausted || (normalized.infrastructure_retries > 0 && infrastructureRetryRuns.length > 0)
420
+ ? "eval_infrastructure_retries_exhausted"
421
+ : "eval_has_infrastructure_failures";
299
422
  const succeeded = !cancelled && !localSourceFailure && backendsSucceeded && invalidTrials.length === 0;
300
423
  const singleBackend = backendRuns.length === 1 ? backendRuns[0].run : undefined;
301
424
  result = {
@@ -304,13 +427,39 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
304
427
  status: cancelled ? "cancelled" : succeeded ? "succeeded" : "failed",
305
428
  exit_code: cancelled ? 9 : succeeded ? 0 : 13,
306
429
  ...(singleBackend ? { backend: singleBackend.backend, backend_summary: singleBackend.summary } : {}),
307
- ...(normalized.attempts > 1 ? {
430
+ ...(plannedTaskExecution ? {
431
+ backend_work_items: backendRuns.map(({ attempt, workId, tasks, leaseId, run }) => ({
432
+ work_id: workId,
433
+ lease_id: leaseId,
434
+ attempt,
435
+ tasks,
436
+ backend: run.backend,
437
+ backend_summary: run.summary,
438
+ })),
439
+ } : normalized.attempts > 1 ? {
308
440
  backend_runs: backendRuns.map(({ attempt, run }) => ({
309
441
  attempt,
310
442
  backend: run.backend,
311
443
  backend_summary: run.summary,
312
444
  })),
313
445
  } : {}),
446
+ infrastructure_retry_policy: {
447
+ max_retries: normalized.infrastructure_retries,
448
+ backoff_ms: normalized.infrastructure_retry_backoff_ms,
449
+ verifier_execution: "same_trial_verifier_only",
450
+ candidate_rerun_on_verifier_failure: false,
451
+ },
452
+ ...(infrastructureRetryRuns.length > 0 ? {
453
+ infrastructure_retry_runs: infrastructureRetryRuns.map(({ attempt, retry, tasks, triggers, refs, run, leaseId, workId }) => ({
454
+ execution_kind: "physical-infrastructure-retry", ...(leaseId ? { lease_id: leaseId } : {}), ...(workId ? { work_id: workId } : {}), attempt,
455
+ retry,
456
+ tasks,
457
+ trigger_trials: triggers,
458
+ trials: refs,
459
+ backend: run.backend,
460
+ backend_summary: run.summary,
461
+ })),
462
+ } : {}),
314
463
  candidate: plan.candidate,
315
464
  dataset: normalized.dataset,
316
465
  benchmark_id: normalized.benchmark_id,
@@ -318,7 +467,7 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
318
467
  generation: progress.generation,
319
468
  trials: trialRefs,
320
469
  summary: summarizeTrialRefs(trialRefs),
321
- prepared_artifact: preparedArtifactSummary(preparedArtifact),
470
+ ...preparedArtifactPlanFields(preparedAssignments),
322
471
  ...(localTransport ? { local_source_transport: transportSummary(localTransport) } : {}),
323
472
  ...(succeeded ? {} : {
324
473
  error: {
@@ -328,14 +477,18 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
328
477
  ? "local_source_materialize_failed"
329
478
  : !backendsSucceeded
330
479
  ? "harbor_failed"
331
- : "eval_has_invalid_tasks",
480
+ : infrastructureFailures.length > 0
481
+ ? infrastructureErrorCode
482
+ : "eval_has_invalid_tasks",
332
483
  message: cancelled
333
484
  ? "eval was cancelled"
334
485
  : localSourceFailure
335
486
  ? "Harbor rejected the transported local Git source before candidate execution"
336
487
  : !backendsSucceeded
337
- ? `Harbor attempt shards completed ${backendRuns.length}/${normalized.attempts}`
338
- : `eval has invalid or missing trials: ${invalidTrials.map(trial => `${trial.task_id}#${trial.attempt}`).join(", ")}`,
488
+ ? `Harbor work items completed ${backendRuns.length}/${expectedBackendRuns}`
489
+ : infrastructureFailures.length > 0
490
+ ? `${infrastructureErrorCode === "eval_infrastructure_retries_exhausted" ? "infrastructure retries exhausted" : "verifier infrastructure failure"}: ${infrastructureFailureSlots.map(trial => `${trial.task_id}#${trial.attempt}`).join(", ")}`
491
+ : `eval has invalid or missing trials: ${invalidTrials.map(trial => `${trial.task_id}#${trial.attempt}`).join(", ")}`,
339
492
  },
340
493
  }),
341
494
  started_at: startedAt.toISOString(),
@@ -352,7 +505,7 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
352
505
  exit_code: signal?.aborted ? 9 : typed ? error.exitCode : 12,
353
506
  error: {
354
507
  code: signal?.aborted ? "cancelled" : typed ? error.code : "internal_error",
355
- message: error?.message || String(error),
508
+ message: safeDiagnosticMessage(error, credentialValuesFromEnv(normalized.pass_env, env)),
356
509
  },
357
510
  benchmark_id: normalized.benchmark_id,
358
511
  benchmark_revision: normalized.benchmark_revision,
@@ -362,71 +515,8 @@ export async function runEval({ evalId = newEvalId(), request, root, env = proce
362
515
  completed_at: new Date().toISOString(),
363
516
  };
364
517
  }
365
- await atomicWriteJSON(path.join(evalDirectory, "result.json"), result);
366
- sink.emit({ type: result.status === "succeeded" ? "eval.completed" : "eval.failed", status: result.status, exit_code: result.exit_code, error: result.error });
367
- await sink.close();
368
- return result;
369
- }
370
- function assertBackendTrialSet(rawResult, refs) {
371
- const trials = Array.isArray(rawResult?.trial_results) ? rawResult.trial_results : [];
372
- const backendIds = new Set(trials.map((trial) => typeof trial.trial_name === "string" ? trial.trial_name : null).filter((value) => value !== null));
373
- const refIds = new Set(refs.map((ref) => ref.trial_id));
374
- if (backendIds.size !== trials.length || backendIds.size !== refIds.size || [...backendIds].some((id) => !refIds.has(id))) {
375
- throw new Error("Harbor terminal trial set does not match published eval progress");
376
- }
377
- }
378
- function attemptDirectoryName(attempt) {
379
- return `attempt-${String(attempt).padStart(4, "0")}`;
380
- }
381
- export function summarizeTrialRefs(trials) {
382
- const valid = trials.filter((trial) => trial.observation_status === "valid" && typeof trial.reward === "number");
383
- const rewards = valid.map((trial) => trial.reward);
384
- const aggregate = rewards.length
385
- ? {
386
- count: rewards.length,
387
- mean: rewards.reduce((sum, reward) => sum + reward, 0) / rewards.length,
388
- min: Math.min(...rewards),
389
- max: Math.max(...rewards),
390
- }
391
- : null;
392
- return {
393
- n_trials: trials.length,
394
- n_completed: valid.length,
395
- n_invalid: trials.length - valid.length,
396
- primary_reward: aggregate?.mean ?? null,
397
- rewards: aggregate ? { reward: aggregate } : {},
398
- };
399
- }
400
- function localSourceBackendFailure(rawResult) {
401
- if (!rawResult)
402
- return false;
403
- // The bridge uses this fixed, non-secret marker when setup cannot verify or
404
- // materialize the uploaded source. Do not surface provider exception text in
405
- // the durable Hitch error, because it may contain backend diagnostics.
406
- return JSON.stringify(rawResult).includes("hitch-local-source-materialize:");
407
- }
408
- function transportSummary(transport) {
409
- const manifest = transport.manifest;
410
- return {
411
- kind: manifest.kind,
412
- resolution_identity: manifest.resolution_identity,
413
- commit: manifest.commit,
414
- tree: manifest.tree,
415
- payload_sha256: manifest.payload_sha256,
416
- payload_bytes: manifest.payload_bytes,
417
- object_count: manifest.object_count,
418
- file_count: manifest.file_count,
419
- };
420
- }
421
- function preparedArtifactSummary(artifact) {
422
- return {
423
- artifact_id: artifact.artifact_id,
424
- artifact_integrity: artifact.artifact_integrity,
425
- entrypoint_integrity: artifact.entrypoint_integrity,
426
- harness_id: artifact.harness_id,
427
- revision_identity: artifact.revision_identity,
428
- platform: artifact.platform,
429
- source_type: artifact.source_type,
430
- };
518
+ if (captureRuntime)
519
+ await captureRuntime.close().catch((error) => sink.emit({ type: "interaction.capture.close-failed", code: error.code || "model_capture_close_failed" }));
520
+ return finalizeEvalResult(evalDirectory, sink, result);
431
521
  }
432
522
  //# sourceMappingURL=service.js.map