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.
- package/README.md +30 -3
- package/README.zh-CN.md +14 -2
- package/dist/bin/hitch.js +25 -3
- package/dist/bin/hitch.js.map +1 -1
- package/dist/scripts/canary-benchmark.js +54 -0
- package/dist/scripts/canary-benchmark.js.map +1 -0
- package/dist/scripts/canary-buildkit-secrets.js +166 -0
- package/dist/scripts/canary-buildkit-secrets.js.map +1 -0
- package/dist/scripts/canary-collision-domains.js +77 -0
- package/dist/scripts/canary-collision-domains.js.map +1 -0
- package/dist/scripts/canary-docker-image-observation.js +181 -0
- package/dist/scripts/canary-docker-image-observation.js.map +1 -0
- package/dist/scripts/canary-eval-scheduler-throughput.js +47 -0
- package/dist/scripts/canary-eval-scheduler-throughput.js.map +1 -0
- package/dist/scripts/canary-gpu-hardware.js +95 -0
- package/dist/scripts/canary-gpu-hardware.js.map +1 -0
- package/dist/scripts/canary-harbor-load.js +365 -0
- package/dist/scripts/canary-harbor-load.js.map +1 -0
- package/dist/scripts/canary-packaged-harness.js +75 -0
- package/dist/scripts/canary-packaged-harness.js.map +1 -0
- package/dist/scripts/canary-resource-load.js +188 -0
- package/dist/scripts/canary-resource-load.js.map +1 -0
- package/dist/scripts/check-architecture.js +10 -5
- package/dist/scripts/check-architecture.js.map +1 -1
- package/dist/scripts/docker-reaper-crash-child.js +21 -0
- package/dist/scripts/docker-reaper-crash-child.js.map +1 -0
- package/dist/src/adapters/catalog.js +3 -1
- package/dist/src/adapters/catalog.js.map +1 -1
- package/dist/src/adapters/discovery.js +1 -1
- package/dist/src/adapters/discovery.js.map +1 -1
- package/dist/src/adapters/providers/claude.js +6 -0
- package/dist/src/adapters/providers/claude.js.map +1 -1
- package/dist/src/adapters/providers/codex-auth.js +29 -0
- package/dist/src/adapters/providers/codex-auth.js.map +1 -0
- package/dist/src/adapters/providers/codex.js +29 -6
- package/dist/src/adapters/providers/codex.js.map +1 -1
- package/dist/src/adapters/providers/deepseek.js +6 -0
- package/dist/src/adapters/providers/deepseek.js.map +1 -1
- package/dist/src/adapters/providers/model-call.js +26 -0
- package/dist/src/adapters/providers/model-call.js.map +1 -0
- package/dist/src/adapters/providers/opencode.js +6 -0
- package/dist/src/adapters/providers/opencode.js.map +1 -1
- package/dist/src/adapters/providers/pi.js +6 -0
- package/dist/src/adapters/providers/pi.js.map +1 -1
- package/dist/src/artifacts/index.js +1 -0
- package/dist/src/artifacts/index.js.map +1 -1
- package/dist/src/artifacts/preparer.js +7 -2
- package/dist/src/artifacts/preparer.js.map +1 -1
- package/dist/src/backends/harbor/agent-budget.js +31 -0
- package/dist/src/backends/harbor/agent-budget.js.map +1 -0
- package/dist/src/backends/harbor/backend.js +59 -107
- package/dist/src/backends/harbor/backend.js.map +1 -1
- package/dist/src/backends/harbor/environment-config.js +92 -0
- package/dist/src/backends/harbor/environment-config.js.map +1 -0
- package/dist/src/backends/harbor/index.js +6 -1
- package/dist/src/backends/harbor/index.js.map +1 -1
- package/dist/src/backends/harbor/model-proxy-config.js +23 -0
- package/dist/src/backends/harbor/model-proxy-config.js.map +1 -0
- package/dist/src/backends/harbor/process.js +177 -0
- package/dist/src/backends/harbor/process.js.map +1 -0
- package/dist/src/backends/harbor/regrade.js +77 -0
- package/dist/src/backends/harbor/regrade.js.map +1 -0
- package/dist/src/backends/harbor/runtime-toolchain.js +30 -0
- package/dist/src/backends/harbor/runtime-toolchain.js.map +1 -0
- package/dist/src/backends/harbor/supervisor.js +74 -0
- package/dist/src/backends/harbor/supervisor.js.map +1 -0
- package/dist/src/backends/harbor/task-resources.js +206 -0
- package/dist/src/backends/harbor/task-resources.js.map +1 -0
- package/dist/src/backends/harbor/tools.js +6 -29
- package/dist/src/backends/harbor/tools.js.map +1 -1
- package/dist/src/backends/index.js +5 -1
- package/dist/src/backends/index.js.map +1 -1
- package/dist/src/benchmarks/index.js +3 -0
- package/dist/src/benchmarks/index.js.map +1 -0
- package/dist/src/benchmarks/loader.js +271 -0
- package/dist/src/benchmarks/loader.js.map +1 -0
- package/dist/src/benchmarks/metrics.js +16 -0
- package/dist/src/benchmarks/metrics.js.map +1 -0
- package/dist/src/benchmarks/toml.js +13 -0
- package/dist/src/benchmarks/toml.js.map +1 -0
- package/dist/src/benchmarks/validation.js +242 -0
- package/dist/src/benchmarks/validation.js.map +1 -0
- package/dist/src/cli/arguments.js +6 -4
- package/dist/src/cli/arguments.js.map +1 -1
- package/dist/src/cli/commands/benchmark.js +22 -0
- package/dist/src/cli/commands/benchmark.js.map +1 -0
- package/dist/src/cli/commands/capabilities.js +18 -0
- package/dist/src/cli/commands/capabilities.js.map +1 -0
- package/dist/src/cli/commands/daemon.js +135 -8
- package/dist/src/cli/commands/daemon.js.map +1 -1
- package/dist/src/cli/commands/eval.js +228 -6
- package/dist/src/cli/commands/eval.js.map +1 -1
- package/dist/src/cli/commands/images.js +40 -0
- package/dist/src/cli/commands/images.js.map +1 -0
- package/dist/src/cli/commands/run.js +42 -17
- package/dist/src/cli/commands/run.js.map +1 -1
- package/dist/src/cli/commands/runs.js +25 -3
- package/dist/src/cli/commands/runs.js.map +1 -1
- package/dist/src/cli/commands/trajectory.js +96 -5
- package/dist/src/cli/commands/trajectory.js.map +1 -1
- package/dist/src/cli/commands/verifier.js +36 -0
- package/dist/src/cli/commands/verifier.js.map +1 -0
- package/dist/src/cli/commands/worker.js +79 -0
- package/dist/src/cli/commands/worker.js.map +1 -0
- package/dist/src/cli/main.js +10 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/output.js +84 -4
- package/dist/src/cli/output.js.map +1 -1
- package/dist/src/control-plane/build-admission.js +69 -0
- package/dist/src/control-plane/build-admission.js.map +1 -0
- package/dist/src/control-plane/build-records.js +6 -0
- package/dist/src/control-plane/build-records.js.map +1 -0
- package/dist/src/control-plane/collisions.js +40 -0
- package/dist/src/control-plane/collisions.js.map +1 -0
- package/dist/src/control-plane/eval-control-state.js +22 -0
- package/dist/src/control-plane/eval-control-state.js.map +1 -0
- package/dist/src/control-plane/eval-control-work.js +44 -0
- package/dist/src/control-plane/eval-control-work.js.map +1 -0
- package/dist/src/control-plane/eval-image-resolution.js +52 -0
- package/dist/src/control-plane/eval-image-resolution.js.map +1 -0
- package/dist/src/control-plane/eval-image-services.js +30 -0
- package/dist/src/control-plane/eval-image-services.js.map +1 -0
- package/dist/src/control-plane/eval-records.js +252 -0
- package/dist/src/control-plane/eval-records.js.map +1 -0
- package/dist/src/control-plane/eval-recovery.js +181 -0
- package/dist/src/control-plane/eval-recovery.js.map +1 -0
- package/dist/src/control-plane/eval-scheduler.js +450 -0
- package/dist/src/control-plane/eval-scheduler.js.map +1 -0
- package/dist/src/control-plane/index.js +20 -0
- package/dist/src/control-plane/index.js.map +1 -0
- package/dist/src/control-plane/local-worker.js +49 -0
- package/dist/src/control-plane/local-worker.js.map +1 -0
- package/dist/src/control-plane/model-capture-planning.js +12 -0
- package/dist/src/control-plane/model-capture-planning.js.map +1 -0
- package/dist/src/control-plane/remote-result-transport.js +175 -0
- package/dist/src/control-plane/remote-result-transport.js.map +1 -0
- package/dist/src/control-plane/remote-work-coordinator.js +333 -0
- package/dist/src/control-plane/remote-work-coordinator.js.map +1 -0
- package/dist/src/control-plane/remote-work-inputs.js +266 -0
- package/dist/src/control-plane/remote-work-inputs.js.map +1 -0
- package/dist/src/control-plane/remote-work-item.js +62 -0
- package/dist/src/control-plane/remote-work-item.js.map +1 -0
- package/dist/src/control-plane/remote-work-recovery.js +220 -0
- package/dist/src/control-plane/remote-work-recovery.js.map +1 -0
- package/dist/src/control-plane/remote-worker-artifacts.js +135 -0
- package/dist/src/control-plane/remote-worker-artifacts.js.map +1 -0
- package/dist/src/control-plane/remote-worker-client.js +202 -0
- package/dist/src/control-plane/remote-worker-client.js.map +1 -0
- package/dist/src/control-plane/remote-worker-configuration.js +34 -0
- package/dist/src/control-plane/remote-worker-configuration.js.map +1 -0
- package/dist/src/control-plane/remote-worker-credentials.js +58 -0
- package/dist/src/control-plane/remote-worker-credentials.js.map +1 -0
- package/dist/src/control-plane/remote-worker-protocol.js +489 -0
- package/dist/src/control-plane/remote-worker-protocol.js.map +1 -0
- package/dist/src/control-plane/remote-worker-runner.js +214 -0
- package/dist/src/control-plane/remote-worker-runner.js.map +1 -0
- package/dist/src/control-plane/remote-workers.js +377 -0
- package/dist/src/control-plane/remote-workers.js.map +1 -0
- package/dist/src/control-plane/rerun-scheduler.js +448 -0
- package/dist/src/control-plane/rerun-scheduler.js.map +1 -0
- package/dist/src/control-plane/rerun-submission.js +62 -0
- package/dist/src/control-plane/rerun-submission.js.map +1 -0
- package/dist/src/control-plane/resources.js +182 -0
- package/dist/src/control-plane/resources.js.map +1 -0
- package/dist/src/control-plane/scheduler-eval-entry.js +32 -0
- package/dist/src/control-plane/scheduler-eval-entry.js.map +1 -0
- package/dist/src/control-plane/synthetic-result.js +21 -0
- package/dist/src/control-plane/synthetic-result.js.map +1 -0
- package/dist/src/control-plane/work-admission.js +23 -0
- package/dist/src/control-plane/work-admission.js.map +1 -0
- package/dist/src/control-plane/work-dispatcher.js +237 -0
- package/dist/src/control-plane/work-dispatcher.js.map +1 -0
- package/dist/src/controller-runtime/hash.js +19 -3
- package/dist/src/controller-runtime/hash.js.map +1 -1
- package/dist/src/controller-runtime/index.js +1 -1
- package/dist/src/controller-runtime/index.js.map +1 -1
- package/dist/src/controller-runtime/store.js +15 -6
- package/dist/src/controller-runtime/store.js.map +1 -1
- package/dist/src/daemon/auth.js +5 -0
- package/dist/src/daemon/auth.js.map +1 -1
- package/dist/src/daemon/client.js.map +1 -1
- package/dist/src/daemon/event-stream.js +46 -0
- package/dist/src/daemon/event-stream.js.map +1 -0
- package/dist/src/daemon/health.js +66 -0
- package/dist/src/daemon/health.js.map +1 -0
- package/dist/src/daemon/launcher.js +28 -1
- package/dist/src/daemon/launcher.js.map +1 -1
- package/dist/src/daemon/logging.js +23 -0
- package/dist/src/daemon/logging.js.map +1 -0
- package/dist/src/daemon/scheduler.js +26 -5
- package/dist/src/daemon/scheduler.js.map +1 -1
- package/dist/src/daemon/server.js +255 -98
- package/dist/src/daemon/server.js.map +1 -1
- package/dist/src/daemon/telemetry.js +163 -0
- package/dist/src/daemon/telemetry.js.map +1 -0
- package/dist/src/daemon/worker-routes.js +219 -0
- package/dist/src/daemon/worker-routes.js.map +1 -0
- package/dist/src/domain/benchmarks.js +2 -0
- package/dist/src/domain/benchmarks.js.map +1 -0
- package/dist/src/domain/bundles.js +2 -0
- package/dist/src/domain/bundles.js.map +1 -0
- package/dist/src/domain/control-plane.js +2 -0
- package/dist/src/domain/control-plane.js.map +1 -0
- package/dist/src/domain/execution-evidence.js +2 -0
- package/dist/src/domain/execution-evidence.js.map +1 -0
- package/dist/src/domain/execution-plan.js +2 -0
- package/dist/src/domain/execution-plan.js.map +1 -0
- package/dist/src/domain/images.js +2 -0
- package/dist/src/domain/images.js.map +1 -0
- package/dist/src/domain/index.js +13 -0
- package/dist/src/domain/index.js.map +1 -1
- package/dist/src/domain/interactions.js +2 -0
- package/dist/src/domain/interactions.js.map +1 -0
- package/dist/src/domain/providers.js +2 -0
- package/dist/src/domain/providers.js.map +1 -0
- package/dist/src/domain/resources.js +2 -0
- package/dist/src/domain/resources.js.map +1 -0
- package/dist/src/domain/runs.js.map +1 -1
- package/dist/src/domain/validation.js +11 -4
- package/dist/src/domain/validation.js.map +1 -1
- package/dist/src/domain/verifier-evidence-validation.js +214 -0
- package/dist/src/domain/verifier-evidence-validation.js.map +1 -0
- package/dist/src/domain/verifier-evidence.js +3 -0
- package/dist/src/domain/verifier-evidence.js.map +1 -0
- package/dist/src/domain/verifier-score-contract.js +232 -0
- package/dist/src/domain/verifier-score-contract.js.map +1 -0
- package/dist/src/domain/workers.js +2 -0
- package/dist/src/domain/workers.js.map +1 -0
- package/dist/src/evals/benchmark-adapter-manifest.js +219 -0
- package/dist/src/evals/benchmark-adapter-manifest.js.map +1 -0
- package/dist/src/evals/benchmark-candidate.js +36 -0
- package/dist/src/evals/benchmark-candidate.js.map +1 -0
- package/dist/src/evals/benchmark-run.js +166 -0
- package/dist/src/evals/benchmark-run.js.map +1 -0
- package/dist/src/evals/collect-only-rerun.js +183 -0
- package/dist/src/evals/collect-only-rerun.js.map +1 -0
- package/dist/src/evals/directory.js +30 -0
- package/dist/src/evals/directory.js.map +1 -0
- package/dist/src/evals/docker-engine-stats.js +89 -0
- package/dist/src/evals/docker-engine-stats.js.map +1 -0
- package/dist/src/evals/docker-ownership.js +51 -0
- package/dist/src/evals/docker-ownership.js.map +1 -0
- package/dist/src/evals/docker-reaper.js +144 -0
- package/dist/src/evals/docker-reaper.js.map +1 -0
- package/dist/src/evals/docker-resource-observer.js +207 -0
- package/dist/src/evals/docker-resource-observer.js.map +1 -0
- package/dist/src/evals/duration-estimator.js +126 -0
- package/dist/src/evals/duration-estimator.js.map +1 -0
- package/dist/src/evals/environment-image-planning.js +164 -0
- package/dist/src/evals/environment-image-planning.js.map +1 -0
- package/dist/src/evals/eval-artifact-planning.js +56 -0
- package/dist/src/evals/eval-artifact-planning.js.map +1 -0
- package/dist/src/evals/eval-finalization.js +10 -0
- package/dist/src/evals/eval-finalization.js.map +1 -0
- package/dist/src/evals/eval-lifecycle-events.js +29 -0
- package/dist/src/evals/eval-lifecycle-events.js.map +1 -0
- package/dist/src/evals/eval-logical-plan.js +51 -0
- package/dist/src/evals/eval-logical-plan.js.map +1 -0
- package/dist/src/evals/eval-result-builder.js +81 -0
- package/dist/src/evals/eval-result-builder.js.map +1 -0
- package/dist/src/evals/events.js +53 -5
- package/dist/src/evals/events.js.map +1 -1
- package/dist/src/evals/evolution-baseline.js +191 -0
- package/dist/src/evals/evolution-baseline.js.map +1 -0
- package/dist/src/evals/execution-evidence.js +116 -0
- package/dist/src/evals/execution-evidence.js.map +1 -0
- package/dist/src/evals/execution-leases.js +342 -0
- package/dist/src/evals/execution-leases.js.map +1 -0
- package/dist/src/evals/execution-plan-artifacts.js +72 -0
- package/dist/src/evals/execution-plan-artifacts.js.map +1 -0
- package/dist/src/evals/execution-plan-images.js +69 -0
- package/dist/src/evals/execution-plan-images.js.map +1 -0
- package/dist/src/evals/execution-plan-resources.js +174 -0
- package/dist/src/evals/execution-plan-resources.js.map +1 -0
- package/dist/src/evals/execution-plan.js +451 -0
- package/dist/src/evals/execution-plan.js.map +1 -0
- package/dist/src/evals/failure-classifier.js +61 -0
- package/dist/src/evals/failure-classifier.js.map +1 -0
- package/dist/src/evals/harbor-artifact-builder-image.js +99 -0
- package/dist/src/evals/harbor-artifact-builder-image.js.map +1 -0
- package/dist/src/evals/harbor-artifact-builder.js +358 -0
- package/dist/src/evals/harbor-artifact-builder.js.map +1 -0
- package/dist/src/evals/harbor-bridge-error.js +58 -3
- package/dist/src/evals/harbor-bridge-error.js.map +1 -1
- package/dist/src/evals/harbor-node-runtime.js +146 -0
- package/dist/src/evals/harbor-node-runtime.js.map +1 -0
- package/dist/src/evals/harbor-phase-timings.js +60 -0
- package/dist/src/evals/harbor-phase-timings.js.map +1 -0
- package/dist/src/evals/index.js +40 -2
- package/dist/src/evals/index.js.map +1 -1
- package/dist/src/evals/infrastructure-retry.js +208 -84
- package/dist/src/evals/infrastructure-retry.js.map +1 -1
- package/dist/src/evals/interaction-capture-import.js +49 -0
- package/dist/src/evals/interaction-capture-import.js.map +1 -0
- package/dist/src/evals/local-docker-provider.js +372 -0
- package/dist/src/evals/local-docker-provider.js.map +1 -0
- package/dist/src/evals/local-eval-planning.js +80 -0
- package/dist/src/evals/local-eval-planning.js.map +1 -0
- package/dist/src/evals/model-capture-plan.js +22 -0
- package/dist/src/evals/model-capture-plan.js.map +1 -0
- package/dist/src/evals/model-capture-runtime.js +114 -0
- package/dist/src/evals/model-capture-runtime.js.map +1 -0
- package/dist/src/evals/model-proxy-runtime-state.js +53 -0
- package/dist/src/evals/model-proxy-runtime-state.js.map +1 -0
- package/dist/src/evals/native-phase-evidence.js +440 -0
- package/dist/src/evals/native-phase-evidence.js.map +1 -0
- package/dist/src/evals/physical-retry-work.js +31 -0
- package/dist/src/evals/physical-retry-work.js.map +1 -0
- package/dist/src/evals/planned-execution-support.js +66 -0
- package/dist/src/evals/planned-execution-support.js.map +1 -0
- package/dist/src/evals/planned-execution.js +469 -0
- package/dist/src/evals/planned-execution.js.map +1 -0
- package/dist/src/evals/planned-progress-publisher.js +62 -0
- package/dist/src/evals/planned-progress-publisher.js.map +1 -0
- package/dist/src/evals/planned-remote-execution.js +40 -0
- package/dist/src/evals/planned-remote-execution.js.map +1 -0
- package/dist/src/evals/planned-retry-execution.js +174 -0
- package/dist/src/evals/planned-retry-execution.js.map +1 -0
- package/dist/src/evals/planned-retry-lifecycle.js +188 -0
- package/dist/src/evals/planned-retry-lifecycle.js.map +1 -0
- package/dist/src/evals/preparation-rerun.js +225 -0
- package/dist/src/evals/preparation-rerun.js.map +1 -0
- package/dist/src/evals/prepared-harness.js +76 -0
- package/dist/src/evals/prepared-harness.js.map +1 -0
- package/dist/src/evals/progress.js +52 -12
- package/dist/src/evals/progress.js.map +1 -1
- package/dist/src/evals/records.js +20 -3
- package/dist/src/evals/records.js.map +1 -1
- package/dist/src/evals/recovery.js +242 -0
- package/dist/src/evals/recovery.js.map +1 -0
- package/dist/src/evals/regrade-evidence.js +77 -0
- package/dist/src/evals/regrade-evidence.js.map +1 -0
- package/dist/src/evals/remote-infrastructure-retry.js +165 -0
- package/dist/src/evals/remote-infrastructure-retry.js.map +1 -0
- package/dist/src/evals/request.js +21 -3
- package/dist/src/evals/request.js.map +1 -1
- package/dist/src/evals/rerun-inputs.js +108 -0
- package/dist/src/evals/rerun-inputs.js.map +1 -0
- package/dist/src/evals/rerun-slots.js +4 -3
- package/dist/src/evals/rerun-slots.js.map +1 -1
- package/dist/src/evals/rerun-types.js +45 -0
- package/dist/src/evals/rerun-types.js.map +1 -0
- package/dist/src/evals/rerun.js +133 -88
- package/dist/src/evals/rerun.js.map +1 -1
- package/dist/src/evals/result-helpers.js +78 -0
- package/dist/src/evals/result-helpers.js.map +1 -0
- package/dist/src/evals/resume-state.js +36 -0
- package/dist/src/evals/resume-state.js.map +1 -0
- package/dist/src/evals/retry-backoff.js +20 -0
- package/dist/src/evals/retry-backoff.js.map +1 -0
- package/dist/src/evals/retry-state.js +195 -0
- package/dist/src/evals/retry-state.js.map +1 -0
- package/dist/src/evals/scheduler-metrics.js +95 -0
- package/dist/src/evals/scheduler-metrics.js.map +1 -0
- package/dist/src/evals/scheduler-trace-replay.js +107 -0
- package/dist/src/evals/scheduler-trace-replay.js.map +1 -0
- package/dist/src/evals/service-types.js +2 -0
- package/dist/src/evals/service-types.js.map +1 -0
- package/dist/src/evals/service.js +331 -347
- package/dist/src/evals/service.js.map +1 -1
- package/dist/src/evals/task-resources.js +209 -0
- package/dist/src/evals/task-resources.js.map +1 -0
- package/dist/src/evals/trial-environment-evidence.js +87 -0
- package/dist/src/evals/trial-environment-evidence.js.map +1 -0
- package/dist/src/evals/trial-execution-evidence.js +13 -0
- package/dist/src/evals/trial-execution-evidence.js.map +1 -0
- package/dist/src/evals/trial-import-identity.js +31 -0
- package/dist/src/evals/trial-import-identity.js.map +1 -0
- package/dist/src/evals/trial-import.js +183 -188
- package/dist/src/evals/trial-import.js.map +1 -1
- package/dist/src/evals/trial-publication-recovery.js +144 -0
- package/dist/src/evals/trial-publication-recovery.js.map +1 -0
- package/dist/src/evals/trial-publication.js +25 -0
- package/dist/src/evals/trial-publication.js.map +1 -0
- package/dist/src/evals/trial-reference-validation.js +49 -0
- package/dist/src/evals/trial-reference-validation.js.map +1 -0
- package/dist/src/evals/verifier-artifacts.js +317 -0
- package/dist/src/evals/verifier-artifacts.js.map +1 -0
- package/dist/src/evals/verifier-eligibility.js +25 -0
- package/dist/src/evals/verifier-eligibility.js.map +1 -0
- package/dist/src/evals/verifier-only-rerun.js +210 -0
- package/dist/src/evals/verifier-only-rerun.js.map +1 -0
- package/dist/src/evals/verifier-runtime.js +61 -0
- package/dist/src/evals/verifier-runtime.js.map +1 -0
- package/dist/src/evals/verifier-score-artifacts.js +119 -0
- package/dist/src/evals/verifier-score-artifacts.js.map +1 -0
- package/dist/src/evals/work-item-artifacts.js +23 -0
- package/dist/src/evals/work-item-artifacts.js.map +1 -0
- package/dist/src/foundation/config.js +17 -0
- package/dist/src/foundation/config.js.map +1 -1
- package/dist/src/foundation/contained-file.js +85 -0
- package/dist/src/foundation/contained-file.js.map +1 -0
- package/dist/src/foundation/credential-redaction.js +162 -0
- package/dist/src/foundation/credential-redaction.js.map +1 -0
- package/dist/src/foundation/environment-image-references.js +59 -0
- package/dist/src/foundation/environment-image-references.js.map +1 -0
- package/dist/src/foundation/executable.js +45 -5
- package/dist/src/foundation/executable.js.map +1 -1
- package/dist/src/foundation/fs.js +2 -1
- package/dist/src/foundation/fs.js.map +1 -1
- package/dist/src/foundation/index.js +6 -2
- package/dist/src/foundation/index.js.map +1 -1
- package/dist/src/foundation/process-identity.js +69 -0
- package/dist/src/foundation/process-identity.js.map +1 -0
- package/dist/src/images/context.js +47 -0
- package/dist/src/images/context.js.map +1 -0
- package/dist/src/images/docker-buildkit.js +137 -0
- package/dist/src/images/docker-buildkit.js.map +1 -0
- package/dist/src/images/dockerfile.js +67 -0
- package/dist/src/images/dockerfile.js.map +1 -0
- package/dist/src/images/gc.js +261 -0
- package/dist/src/images/gc.js.map +1 -0
- package/dist/src/images/index.js +10 -0
- package/dist/src/images/index.js.map +1 -0
- package/dist/src/images/manifest.js +109 -0
- package/dist/src/images/manifest.js.map +1 -0
- package/dist/src/images/ownership.js +5 -0
- package/dist/src/images/ownership.js.map +1 -0
- package/dist/src/images/records.js +32 -0
- package/dist/src/images/records.js.map +1 -0
- package/dist/src/images/registry.js +154 -0
- package/dist/src/images/registry.js.map +1 -0
- package/dist/src/images/service.js +224 -0
- package/dist/src/images/service.js.map +1 -0
- package/dist/src/model-access/capture.js +255 -0
- package/dist/src/model-access/capture.js.map +1 -0
- package/dist/src/model-access/index.js +5 -0
- package/dist/src/model-access/index.js.map +1 -0
- package/dist/src/model-access/policy.js +49 -0
- package/dist/src/model-access/policy.js.map +1 -0
- package/dist/src/model-access/proxy.js +336 -0
- package/dist/src/model-access/proxy.js.map +1 -0
- package/dist/src/model-access/records.js +167 -0
- package/dist/src/model-access/records.js.map +1 -0
- package/dist/src/runs/adapter-process.js +12 -0
- package/dist/src/runs/adapter-process.js.map +1 -0
- package/dist/src/runs/bundle.js +450 -0
- package/dist/src/runs/bundle.js.map +1 -0
- package/dist/src/runs/events.js +12 -1
- package/dist/src/runs/events.js.map +1 -1
- package/dist/src/runs/executor.js +46 -45
- package/dist/src/runs/executor.js.map +1 -1
- package/dist/src/runs/finalizer.js +17 -0
- package/dist/src/runs/finalizer.js.map +1 -1
- package/dist/src/runs/index.js +8 -1
- package/dist/src/runs/index.js.map +1 -1
- package/dist/src/runs/manifest.js +5 -5
- package/dist/src/runs/manifest.js.map +1 -1
- package/dist/src/runs/phase-bundle.js +86 -0
- package/dist/src/runs/phase-bundle.js.map +1 -0
- package/dist/src/runs/phase-cancellation.js +107 -0
- package/dist/src/runs/phase-cancellation.js.map +1 -0
- package/dist/src/runs/phase-group.js +113 -0
- package/dist/src/runs/phase-group.js.map +1 -0
- package/dist/src/runs/query.js +1 -1
- package/dist/src/runs/query.js.map +1 -1
- package/dist/src/runs/records.js +3 -0
- package/dist/src/runs/records.js.map +1 -1
- package/dist/src/runs/request.js +10 -3
- package/dist/src/runs/request.js.map +1 -1
- package/dist/src/runs/training-candidate.js +161 -0
- package/dist/src/runs/training-candidate.js.map +1 -0
- package/dist/src/runs/verifier-evidence-redaction.js +61 -0
- package/dist/src/runs/verifier-evidence-redaction.js.map +1 -0
- package/dist/src/runs/verifier-evidence.js +459 -0
- package/dist/src/runs/verifier-evidence.js.map +1 -0
- package/dist/src/runs/verifier-structured-evidence.js +83 -0
- package/dist/src/runs/verifier-structured-evidence.js.map +1 -0
- package/dist/src/trajectories/analysis.js +288 -0
- package/dist/src/trajectories/analysis.js.map +1 -0
- package/dist/src/trajectories/chunk-projection.js +148 -0
- package/dist/src/trajectories/chunk-projection.js.map +1 -0
- package/dist/src/trajectories/content-projection.js +389 -0
- package/dist/src/trajectories/content-projection.js.map +1 -0
- package/dist/src/trajectories/dsh-chunk-contract.js +176 -0
- package/dist/src/trajectories/dsh-chunk-contract.js.map +1 -0
- package/dist/src/trajectories/dsh-contract.js +284 -0
- package/dist/src/trajectories/dsh-contract.js.map +1 -0
- package/dist/src/trajectories/events-chunk-drill.js +95 -0
- package/dist/src/trajectories/events-chunk-drill.js.map +1 -0
- package/dist/src/trajectories/events-page.js +367 -0
- package/dist/src/trajectories/events-page.js.map +1 -0
- package/dist/src/trajectories/index.js +3 -0
- package/dist/src/trajectories/index.js.map +1 -1
- package/dist/src/trajectories/provider-capture.js +15 -24
- package/dist/src/trajectories/provider-capture.js.map +1 -1
- package/dist/src/trajectories/providers/deepseek.js +3 -3
- package/dist/src/trajectories/providers/deepseek.js.map +1 -1
- package/dist/src/trajectories/request-attempt.js +47 -0
- package/dist/src/trajectories/request-attempt.js.map +1 -0
- package/dist/src/trajectories/stream-reader.js +311 -0
- package/dist/src/trajectories/stream-reader.js.map +1 -0
- package/dist/src/trajectories/surface-fold.js +126 -0
- package/dist/src/trajectories/surface-fold.js.map +1 -0
- package/dist/src/workers/index.js +3 -0
- package/dist/src/workers/index.js.map +1 -0
- package/dist/src/workers/remote-harbor-work-spec.js +97 -0
- package/dist/src/workers/remote-harbor-work-spec.js.map +1 -0
- package/dist/src/workers/remote-harbor-worker.js +223 -0
- package/dist/src/workers/remote-harbor-worker.js.map +1 -0
- package/docs/schemas/benchmark-hook-request-v1.schema.json +65 -0
- package/docs/schemas/benchmark-hook-response-v1.schema.json +75 -0
- package/docs/schemas/benchmark-hook-v1.schema.json +33 -0
- package/docs/schemas/benchmark-lock-v1.schema.json +351 -0
- package/docs/schemas/benchmark-metric-v1.schema.json +37 -0
- package/docs/schemas/benchmark-package-v1.schema.json +167 -0
- package/docs/schemas/benchmark-phase-group.schema.json +41 -0
- package/docs/schemas/benchmark-profile-v1.schema.json +141 -0
- package/docs/schemas/benchmark-task-v1.schema.json +384 -0
- package/docs/schemas/benchmark-tool-result-v1.schema.json +30 -0
- package/docs/schemas/environment-build-index.schema.json +13 -0
- package/docs/schemas/environment-build-record.schema.json +25 -0
- package/docs/schemas/environment-image-manifest.schema.json +62 -0
- package/docs/schemas/error.schema.json +22 -0
- package/docs/schemas/eval-control.schema.json +36 -0
- package/docs/schemas/eval-execution-plan.schema.json +216 -0
- package/docs/schemas/eval-progress.schema.json +1 -15
- package/docs/schemas/eval-rerun-result.schema.json +58 -0
- package/docs/schemas/eval-rerun-state.schema.json +61 -0
- package/docs/schemas/eval-rerun-submission.schema.json +60 -0
- package/docs/schemas/eval-result.schema.json +32 -13
- package/docs/schemas/eval-submission-input.schema.json +14 -0
- package/docs/schemas/eval-submission.schema.json +68 -0
- package/docs/schemas/eval-trial-publication.schema.json +15 -0
- package/docs/schemas/eval-trial-reference.schema.json +129 -0
- package/docs/schemas/execution-evidence.schema.json +96 -0
- package/docs/schemas/execution-lease.schema.json +50 -0
- package/docs/schemas/execution-provider-status.schema.json +65 -0
- package/docs/schemas/execution-worker.schema.json +53 -0
- package/docs/schemas/harbor-process-exit.schema.json +14 -0
- package/docs/schemas/interaction-capture-ref.schema.json +45 -0
- package/docs/schemas/local-provider-execution.schema.json +33 -0
- package/docs/schemas/model-interaction.schema.json +41 -0
- package/docs/schemas/regrade-assessment-reference.schema.json +11 -0
- package/docs/schemas/remote-result-envelope.schema.json +32 -0
- package/docs/schemas/remote-tree-envelope.schema.json +25 -0
- package/docs/schemas/remote-work-offer.schema.json +58 -0
- package/docs/schemas/remote-work-receipt.schema.json +50 -0
- package/docs/schemas/remote-worker-artifact.schema.json +17 -0
- package/docs/schemas/remote-worker-event.schema.json +18 -0
- package/docs/schemas/remote-worker-heartbeat.schema.json +37 -0
- package/docs/schemas/remote-worker-registration.schema.json +52 -0
- package/docs/schemas/result-bundle-index.schema.json +133 -0
- package/docs/schemas/run-context.schema.json +15 -0
- package/docs/schemas/run-environment-images.schema.json +39 -0
- package/docs/schemas/run-manifest.schema.json +4 -0
- package/docs/schemas/run-request.schema.json +4 -0
- package/docs/schemas/training-data-candidate.schema.json +46 -0
- package/docs/schemas/trajectory-analysis.schema.json +258 -0
- package/docs/schemas/trajectory-events-page.schema.json +40 -0
- package/docs/schemas/verifier-assessment.schema.json +77 -0
- package/docs/schemas/verifier-evidence.schema.json +276 -0
- package/integrations/harbor/hitch_benchmark.py +252 -0
- package/integrations/harbor/hitch_candidate_recycle.py +262 -0
- package/integrations/harbor/hitch_harbor_agent.py +698 -568
- package/integrations/harbor/hitch_harbor_environment.py +469 -0
- package/integrations/harbor/hitch_harbor_task_resources.py +275 -0
- package/integrations/harbor/hitch_harbor_verifier.py +83 -2
- package/integrations/harbor/hitch_phase_supervisor.py +452 -0
- package/integrations/harbor/hitch_tool_client.mjs +91 -0
- package/integrations/model-call/cli.js +57 -0
- 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
|