agent-hitch 0.2.7 → 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 +16 -2
- 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-eval-scheduler-throughput.js +47 -0
- package/dist/scripts/canary-eval-scheduler-throughput.js.map +1 -0
- package/dist/scripts/check-architecture.js +3 -2
- package/dist/scripts/check-architecture.js.map +1 -1
- package/dist/src/adapters/catalog.js +2 -1
- package/dist/src/adapters/catalog.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 +23 -6
- package/dist/src/adapters/providers/codex.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/artifacts/index.js +1 -1
- 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 +6 -4
- package/dist/src/backends/harbor/backend.js.map +1 -1
- package/dist/src/backends/harbor/index.js +1 -0
- package/dist/src/backends/harbor/index.js.map +1 -1
- package/dist/src/backends/harbor/regrade.js +77 -0
- package/dist/src/backends/harbor/regrade.js.map +1 -0
- package/dist/src/backends/index.js +1 -0
- 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/eval.js +15 -3
- package/dist/src/cli/commands/eval.js.map +1 -1
- package/dist/src/cli/commands/run.js +37 -15
- package/dist/src/cli/commands/run.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/main.js +6 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/output.js +14 -2
- package/dist/src/cli/output.js.map +1 -1
- package/dist/src/control-plane/eval-control-work.js +5 -0
- package/dist/src/control-plane/eval-control-work.js.map +1 -1
- package/dist/src/control-plane/eval-scheduler.js +4 -1
- package/dist/src/control-plane/eval-scheduler.js.map +1 -1
- package/dist/src/control-plane/index.js +1 -1
- package/dist/src/control-plane/index.js.map +1 -1
- package/dist/src/control-plane/remote-result-transport.js +3 -0
- package/dist/src/control-plane/remote-result-transport.js.map +1 -1
- package/dist/src/control-plane/remote-work-coordinator.js +1 -0
- package/dist/src/control-plane/remote-work-coordinator.js.map +1 -1
- 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 +44 -5
- package/dist/src/control-plane/remote-work-recovery.js.map +1 -1
- package/dist/src/control-plane/remote-worker-protocol.js +2 -23
- package/dist/src/control-plane/remote-worker-protocol.js.map +1 -1
- package/dist/src/control-plane/rerun-scheduler.js +51 -14
- package/dist/src/control-plane/rerun-scheduler.js.map +1 -1
- package/dist/src/control-plane/rerun-submission.js +10 -4
- package/dist/src/control-plane/rerun-submission.js.map +1 -1
- package/dist/src/control-plane/work-admission.js +2 -0
- package/dist/src/control-plane/work-admission.js.map +1 -1
- package/dist/src/control-plane/work-dispatcher.js +9 -1
- package/dist/src/control-plane/work-dispatcher.js.map +1 -1
- package/dist/src/controller-runtime/hash.js +7 -2
- package/dist/src/controller-runtime/hash.js.map +1 -1
- package/dist/src/daemon/auth.js +5 -0
- package/dist/src/daemon/auth.js.map +1 -1
- package/dist/src/domain/benchmarks.js +2 -0
- package/dist/src/domain/benchmarks.js.map +1 -0
- package/dist/src/domain/index.js +4 -0
- package/dist/src/domain/index.js.map +1 -1
- 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/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 +2 -1
- package/dist/src/evals/collect-only-rerun.js.map +1 -1
- package/dist/src/evals/directory.js +1 -1
- package/dist/src/evals/directory.js.map +1 -1
- package/dist/src/evals/duration-estimator.js +126 -0
- package/dist/src/evals/duration-estimator.js.map +1 -0
- package/dist/src/evals/eval-lifecycle-events.js +13 -0
- package/dist/src/evals/eval-lifecycle-events.js.map +1 -1
- 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/evolution-baseline.js +191 -0
- package/dist/src/evals/evolution-baseline.js.map +1 -0
- package/dist/src/evals/execution-plan.js +39 -3
- package/dist/src/evals/execution-plan.js.map +1 -1
- 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.js +6 -2
- package/dist/src/evals/harbor-artifact-builder.js.map +1 -1
- package/dist/src/evals/harbor-bridge-error.js +41 -1
- 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/index.js +15 -0
- package/dist/src/evals/index.js.map +1 -1
- package/dist/src/evals/infrastructure-retry.js +109 -22
- package/dist/src/evals/infrastructure-retry.js.map +1 -1
- 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 +22 -10
- package/dist/src/evals/planned-execution-support.js.map +1 -1
- package/dist/src/evals/planned-execution.js +120 -112
- package/dist/src/evals/planned-execution.js.map +1 -1
- 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-retry-execution.js +174 -0
- package/dist/src/evals/planned-retry-execution.js.map +1 -0
- package/dist/src/evals/planned-retry-lifecycle.js +4 -2
- package/dist/src/evals/planned-retry-lifecycle.js.map +1 -1
- package/dist/src/evals/preparation-rerun.js +225 -0
- package/dist/src/evals/preparation-rerun.js.map +1 -0
- package/dist/src/evals/progress.js +48 -8
- package/dist/src/evals/progress.js.map +1 -1
- package/dist/src/evals/recovery.js +54 -4
- package/dist/src/evals/recovery.js.map +1 -1
- 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 +84 -18
- package/dist/src/evals/remote-infrastructure-retry.js.map +1 -1
- package/dist/src/evals/request.js +21 -3
- package/dist/src/evals/request.js.map +1 -1
- package/dist/src/evals/rerun-inputs.js +19 -0
- package/dist/src/evals/rerun-inputs.js.map +1 -1
- package/dist/src/evals/rerun-types.js +2 -6
- package/dist/src/evals/rerun-types.js.map +1 -1
- package/dist/src/evals/rerun.js +27 -20
- package/dist/src/evals/rerun.js.map +1 -1
- package/dist/src/evals/result-helpers.js +13 -0
- package/dist/src/evals/result-helpers.js.map +1 -1
- 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.js +87 -138
- package/dist/src/evals/service.js.map +1 -1
- package/dist/src/evals/trial-import.js +47 -50
- package/dist/src/evals/trial-import.js.map +1 -1
- package/dist/src/evals/trial-publication-recovery.js +45 -5
- package/dist/src/evals/trial-publication-recovery.js.map +1 -1
- 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/foundation/contained-file.js +85 -0
- package/dist/src/foundation/contained-file.js.map +1 -0
- package/dist/src/foundation/credential-redaction.js +26 -1
- package/dist/src/foundation/credential-redaction.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 +2 -1
- package/dist/src/foundation/index.js.map +1 -1
- package/dist/src/runs/adapter-process.js +12 -0
- package/dist/src/runs/adapter-process.js.map +1 -0
- package/dist/src/runs/executor.js +24 -30
- 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 +5 -0
- package/dist/src/runs/index.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 +4 -2
- package/dist/src/runs/request.js.map +1 -1
- 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/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/remote-harbor-worker.js +3 -0
- package/dist/src/workers/remote-harbor-worker.js.map +1 -1
- 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/error.schema.json +22 -0
- package/docs/schemas/eval-progress.schema.json +1 -15
- package/docs/schemas/eval-rerun-result.schema.json +6 -1
- package/docs/schemas/eval-rerun-submission.schema.json +3 -1
- package/docs/schemas/eval-result.schema.json +1 -15
- package/docs/schemas/eval-trial-publication.schema.json +1 -25
- package/docs/schemas/eval-trial-reference.schema.json +129 -0
- package/docs/schemas/regrade-assessment-reference.schema.json +11 -0
- package/docs/schemas/run-context.schema.json +15 -0
- package/docs/schemas/run-manifest.schema.json +4 -0
- package/docs/schemas/run-request.schema.json +4 -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 +541 -43
- package/integrations/harbor/hitch_harbor_environment.py +138 -0
- package/integrations/harbor/hitch_harbor_verifier.py +60 -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 +17 -6
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import asyncio
|
|
5
6
|
import json
|
|
6
7
|
import re
|
|
7
8
|
import tempfile
|
|
@@ -35,6 +36,8 @@ _GPU_OVERRIDE_PREFIX = "__HITCH_GPU_OVERRIDE_"
|
|
|
35
36
|
class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
36
37
|
"""Use Harbor's Docker semantics with a final ownership-label overlay."""
|
|
37
38
|
|
|
39
|
+
_hitch_static_no_network = False
|
|
40
|
+
|
|
38
41
|
def __init__(
|
|
39
42
|
self,
|
|
40
43
|
*args: Any,
|
|
@@ -62,12 +65,16 @@ class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
|
62
65
|
self._hitch_main_gpu_count = hitch_main_gpu_count
|
|
63
66
|
self._hitch_ownership_temp_dir: tempfile.TemporaryDirectory[str] | None = None
|
|
64
67
|
self._hitch_ownership_compose_path: Path | None = None
|
|
68
|
+
self._hitch_phase_compose_path: Path | None = None
|
|
69
|
+
self._hitch_candidate_recycler = None
|
|
65
70
|
positional = list(args)
|
|
66
71
|
task_env_config = kwargs.get("task_env_config")
|
|
67
72
|
task_env_position: int | None = None
|
|
68
73
|
if task_env_config is None and len(positional) >= 5:
|
|
69
74
|
task_env_position = 4
|
|
70
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
|
|
71
78
|
if task_env_config is not None:
|
|
72
79
|
requested = getattr(task_env_config, "docker_image", None)
|
|
73
80
|
resolved = self._hitch_prebuilt_task_image or self._hitch_resolved_images.get(requested)
|
|
@@ -78,6 +85,14 @@ class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
|
78
85
|
else:
|
|
79
86
|
positional[task_env_position] = updated
|
|
80
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))
|
|
81
96
|
if (
|
|
82
97
|
self._hitch_ownership_labels
|
|
83
98
|
or self._hitch_service_resource_limits
|
|
@@ -85,16 +100,108 @@ class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
|
85
100
|
or self._hitch_prebuilt_task_image
|
|
86
101
|
or self._hitch_model_proxy_host_gateway
|
|
87
102
|
or self._hitch_main_gpu_count > 0
|
|
103
|
+
or self._hitch_benchmark_platform
|
|
104
|
+
or self._hitch_static_no_network
|
|
88
105
|
):
|
|
89
106
|
self._hitch_ownership_compose_path = self._write_ownership_overlay()
|
|
90
107
|
|
|
91
108
|
@property
|
|
92
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")
|
|
93
112
|
paths = list(super()._docker_compose_paths)
|
|
94
113
|
if self._hitch_ownership_compose_path is not None:
|
|
95
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)
|
|
96
117
|
return paths
|
|
97
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
|
+
|
|
98
205
|
def _write_ownership_overlay(self) -> Path:
|
|
99
206
|
services = {MAIN_SERVICE_NAME}
|
|
100
207
|
if self._enable_egress_control:
|
|
@@ -104,6 +211,7 @@ class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
|
104
211
|
external_networks: set[str] = set()
|
|
105
212
|
external_volumes: set[str] = set()
|
|
106
213
|
build_services: set[str] = set()
|
|
214
|
+
declared_platforms: set[str] = set()
|
|
107
215
|
image_overlays: dict[str, str] = {}
|
|
108
216
|
sources = [self._environment_docker_compose_path, *self.extra_docker_compose_paths]
|
|
109
217
|
for source in sources:
|
|
@@ -117,6 +225,11 @@ class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
|
117
225
|
services.update(_mapping_names(document.get("services"), "services", source))
|
|
118
226
|
for name, config in (document.get("services") or {}).items():
|
|
119
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)
|
|
120
233
|
if "build" in config and config.get("build") is not None:
|
|
121
234
|
build_services.add(name)
|
|
122
235
|
reference = config.get("image")
|
|
@@ -132,8 +245,14 @@ class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
|
132
245
|
service_overlays: dict[str, dict[str, Any]] = {}
|
|
133
246
|
for name in sorted(services):
|
|
134
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
|
|
135
252
|
if labels:
|
|
136
253
|
config["labels"] = labels
|
|
254
|
+
if name == MAIN_SERVICE_NAME and self._hitch_static_no_network:
|
|
255
|
+
config["network_mode"] = "none"
|
|
137
256
|
resolved_image = image_overlays.get(name)
|
|
138
257
|
if name == MAIN_SERVICE_NAME and self._hitch_prebuilt_task_image:
|
|
139
258
|
resolved_image = self._hitch_prebuilt_task_image
|
|
@@ -207,6 +326,25 @@ class HitchHarborDockerEnvironment(DockerEnvironment):
|
|
|
207
326
|
return target
|
|
208
327
|
|
|
209
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
|
+
|
|
210
348
|
def _mapping_names(value: Any, label: str, source: Path) -> set[str]:
|
|
211
349
|
if value is None:
|
|
212
350
|
return set()
|
|
@@ -23,9 +23,15 @@ from harbor.verifier.verifier import Verifier
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
MAX_LOG_BYTES = 256 * 1024
|
|
26
|
+
HITCH_AGENT_OUTCOME_NAME = "hitch-agent-outcome.json"
|
|
26
27
|
LOG_NAMES = ("test-stdout.txt", "test-stderr.txt", "stdout.txt", "stderr.txt")
|
|
27
|
-
OUTPUT_NAMES = (*LOG_NAMES, "reward.txt", "reward.json", "ctrf.json")
|
|
28
|
-
CONTROL_NAMES = (
|
|
28
|
+
OUTPUT_NAMES = (*LOG_NAMES, "reward.txt", "reward.json", "process.json", "feedback.json", "ctrf.json")
|
|
29
|
+
CONTROL_NAMES = (
|
|
30
|
+
"infrastructure-error.json",
|
|
31
|
+
"infrastructure-retry-history.json",
|
|
32
|
+
"candidate-ineligible.json",
|
|
33
|
+
"eligibility-gate.json",
|
|
34
|
+
)
|
|
29
35
|
|
|
30
36
|
INFRASTRUCTURE_PATTERNS: tuple[tuple[str, tuple[re.Pattern[str], ...]], ...] = (
|
|
31
37
|
(
|
|
@@ -126,8 +132,28 @@ class HitchRetryingVerifier(Verifier):
|
|
|
126
132
|
self._write_phase_timing(started_ns)
|
|
127
133
|
|
|
128
134
|
async def _verify_with_retries(self) -> VerifierResult:
|
|
135
|
+
directory = getattr(getattr(getattr(self, "task", None), "paths", None), "environment_dir", None)
|
|
136
|
+
if directory and (directory.parent / ".hitch-benchmark.json").is_file():
|
|
137
|
+
from hitch_benchmark import restore_final_response, validate_collected_submission
|
|
138
|
+
validate_collected_submission(self)
|
|
139
|
+
await restore_final_response(self)
|
|
129
140
|
# Do not trust a control file left by the candidate or a prior phase.
|
|
130
141
|
await self._remove_files(CONTROL_NAMES)
|
|
142
|
+
eligibility = self._read_trusted_agent_outcome()
|
|
143
|
+
if eligibility is None:
|
|
144
|
+
self._write_json("eligibility-gate.json", {
|
|
145
|
+
"schema_version": "1", "status": "unavailable", "verifier_executed": True,
|
|
146
|
+
})
|
|
147
|
+
elif eligibility["gradeability"] == "ungradeable":
|
|
148
|
+
self._write_json("candidate-ineligible.json", {
|
|
149
|
+
"schema_version": "1",
|
|
150
|
+
"code": "candidate_evidence_unavailable",
|
|
151
|
+
"run_id": eligibility["run_id"],
|
|
152
|
+
"candidate_bundle": eligibility["candidate_bundle"],
|
|
153
|
+
"reason_code": eligibility.get("reason_code", "candidate_evidence_unavailable"),
|
|
154
|
+
"verifier_executed": False,
|
|
155
|
+
})
|
|
156
|
+
return VerifierResult(rewards={"reward": 0})
|
|
131
157
|
attempts: list[dict[str, Any]] = []
|
|
132
158
|
for attempt in range(1, self.infrastructure_retries + 2):
|
|
133
159
|
result: VerifierResult | None = None
|
|
@@ -152,6 +178,10 @@ class HitchRetryingVerifier(Verifier):
|
|
|
152
178
|
if attempts:
|
|
153
179
|
self._write_history("recovered", attempts)
|
|
154
180
|
assert result is not None
|
|
181
|
+
directory = getattr(getattr(getattr(self, "task", None), "paths", None), "environment_dir", None)
|
|
182
|
+
if directory and (directory.parent / ".hitch-benchmark.json").is_file():
|
|
183
|
+
from hitch_benchmark import normalize_rewards
|
|
184
|
+
return normalize_rewards(self, result)
|
|
155
185
|
return result
|
|
156
186
|
|
|
157
187
|
attempts.append(
|
|
@@ -188,6 +218,34 @@ class HitchRetryingVerifier(Verifier):
|
|
|
188
218
|
|
|
189
219
|
raise AssertionError("unreachable verifier retry state")
|
|
190
220
|
|
|
221
|
+
def _read_trusted_agent_outcome(self) -> dict[str, Any] | None:
|
|
222
|
+
source = self.trial_paths.verifier_dir.parent / "agent" / HITCH_AGENT_OUTCOME_NAME
|
|
223
|
+
try:
|
|
224
|
+
if not source.is_file() or source.stat().st_size <= 0 or source.stat().st_size > 16 * 1024:
|
|
225
|
+
return None
|
|
226
|
+
value = json.loads(source.read_text(encoding="utf-8"))
|
|
227
|
+
except (OSError, UnicodeError, json.JSONDecodeError):
|
|
228
|
+
return None
|
|
229
|
+
if not isinstance(value, dict) or set(value) - {
|
|
230
|
+
"schema_version", "run_id", "status", "candidate_bundle", "submission_snapshot", "gradeability", "reason_code",
|
|
231
|
+
}:
|
|
232
|
+
return None
|
|
233
|
+
if value.get("schema_version") != "1" or re.fullmatch(r"run_[a-f0-9]{32}", str(value.get("run_id", ""))) is None:
|
|
234
|
+
return None
|
|
235
|
+
if value.get("status") not in {"succeeded", "failed", "timed_out", "cancelled"}:
|
|
236
|
+
return None
|
|
237
|
+
if value.get("candidate_bundle") not in {"complete", "missing", "invalid"}:
|
|
238
|
+
return None
|
|
239
|
+
if value.get("submission_snapshot") not in {"complete", "missing", "not-required"}:
|
|
240
|
+
return None
|
|
241
|
+
if value.get("gradeability") not in {"gradeable", "ungradeable"}:
|
|
242
|
+
return None
|
|
243
|
+
if value["gradeability"] == "gradeable" and value["candidate_bundle"] != "complete":
|
|
244
|
+
return None
|
|
245
|
+
if "reason_code" in value and (not isinstance(value["reason_code"], str) or not value["reason_code"]):
|
|
246
|
+
return None
|
|
247
|
+
return value
|
|
248
|
+
|
|
191
249
|
def _write_phase_timing(self, started_ns: int) -> None:
|
|
192
250
|
self._write_json(
|
|
193
251
|
"hitch-phase-timings.json",
|