@tangle-network/agent-bench 0.1.0 → 0.3.6
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/CHANGELOG.md +23 -0
- package/HARNESS.md +302 -0
- package/README.md +39 -2
- package/dist/adapters.d.ts +15 -0
- package/dist/adapters.js +43 -0
- package/dist/adapters.js.map +1 -0
- package/dist/benchmarks/_harness.d.ts +125 -0
- package/dist/benchmarks/_harness.js +33 -0
- package/dist/benchmarks/_harness.js.map +1 -0
- package/dist/benchmarks/aec-bench.d.ts +27 -0
- package/dist/benchmarks/aec-bench.js +8 -0
- package/dist/benchmarks/aec-bench.js.map +1 -0
- package/dist/benchmarks/agentbench.d.ts +16 -0
- package/dist/benchmarks/agentbench.js +10 -0
- package/dist/benchmarks/agentbench.js.map +1 -0
- package/dist/benchmarks/appworld.d.ts +37 -0
- package/dist/benchmarks/appworld.js +14 -0
- package/dist/benchmarks/appworld.js.map +1 -0
- package/dist/benchmarks/bfcl.d.ts +18 -0
- package/dist/benchmarks/bfcl.js +10 -0
- package/dist/benchmarks/bfcl.js.map +1 -0
- package/dist/benchmarks/cad-design.d.ts +45 -0
- package/dist/benchmarks/cad-design.js +7 -0
- package/dist/benchmarks/cad-design.js.map +1 -0
- package/dist/benchmarks/cadbench.d.ts +19 -0
- package/dist/benchmarks/cadbench.js +8 -0
- package/dist/benchmarks/cadbench.js.map +1 -0
- package/dist/benchmarks/cadgenbench.d.ts +22 -0
- package/dist/benchmarks/cadgenbench.js +8 -0
- package/dist/benchmarks/cadgenbench.js.map +1 -0
- package/dist/benchmarks/commit0.d.ts +31 -0
- package/dist/benchmarks/commit0.js +10 -0
- package/dist/benchmarks/commit0.js.map +1 -0
- package/dist/benchmarks/crag.d.ts +14 -0
- package/dist/benchmarks/crag.js +9 -0
- package/dist/benchmarks/crag.js.map +1 -0
- package/dist/benchmarks/dabstep.d.ts +18 -0
- package/dist/benchmarks/dabstep.js +10 -0
- package/dist/benchmarks/dabstep.js.map +1 -0
- package/dist/benchmarks/enterpriseops-gym.d.ts +38 -0
- package/dist/benchmarks/enterpriseops-gym.js +10 -0
- package/dist/benchmarks/enterpriseops-gym.js.map +1 -0
- package/dist/benchmarks/finresearchbench.d.ts +15 -0
- package/dist/benchmarks/finresearchbench.js +8 -0
- package/dist/benchmarks/finresearchbench.js.map +1 -0
- package/dist/benchmarks/finsearchcomp.d.ts +49 -0
- package/dist/benchmarks/finsearchcomp.js +7 -0
- package/dist/benchmarks/finsearchcomp.js.map +1 -0
- package/dist/benchmarks/frames.d.ts +59 -0
- package/dist/benchmarks/frames.js +13 -0
- package/dist/benchmarks/frames.js.map +1 -0
- package/dist/benchmarks/hotpotqa.d.ts +48 -0
- package/dist/benchmarks/hotpotqa.js +15 -0
- package/dist/benchmarks/hotpotqa.js.map +1 -0
- package/dist/benchmarks/humaneval.d.ts +53 -0
- package/dist/benchmarks/humaneval.js +15 -0
- package/dist/benchmarks/humaneval.js.map +1 -0
- package/dist/benchmarks/mind2web.d.ts +41 -0
- package/dist/benchmarks/mind2web.js +9 -0
- package/dist/benchmarks/mind2web.js.map +1 -0
- package/dist/benchmarks/nomiracl.d.ts +15 -0
- package/dist/benchmarks/nomiracl.js +9 -0
- package/dist/benchmarks/nomiracl.js.map +1 -0
- package/dist/benchmarks/open-rag-bench.d.ts +14 -0
- package/dist/benchmarks/open-rag-bench.js +9 -0
- package/dist/benchmarks/open-rag-bench.js.map +1 -0
- package/dist/benchmarks/programbench.d.ts +38 -0
- package/dist/benchmarks/programbench.js +10 -0
- package/dist/benchmarks/programbench.js.map +1 -0
- package/dist/benchmarks/rag-shared.d.ts +42 -0
- package/dist/benchmarks/rag-shared.js +39 -0
- package/dist/benchmarks/rag-shared.js.map +1 -0
- package/dist/benchmarks/ragbench.d.ts +16 -0
- package/dist/benchmarks/ragbench.js +9 -0
- package/dist/benchmarks/ragbench.js.map +1 -0
- package/dist/benchmarks/simpleqa.d.ts +64 -0
- package/dist/benchmarks/simpleqa.js +11 -0
- package/dist/benchmarks/simpleqa.js.map +1 -0
- package/dist/benchmarks/swe-bench.d.ts +56 -0
- package/dist/benchmarks/swe-bench.js +14 -0
- package/dist/benchmarks/swe-bench.js.map +1 -0
- package/dist/benchmarks/t2-ragbench.d.ts +14 -0
- package/dist/benchmarks/t2-ragbench.js +9 -0
- package/dist/benchmarks/t2-ragbench.js.map +1 -0
- package/dist/benchmarks/tau-bench-shared.d.ts +26 -0
- package/dist/benchmarks/tau-bench-shared.js +10 -0
- package/dist/benchmarks/tau-bench-shared.js.map +1 -0
- package/dist/benchmarks/tau2-bench.d.ts +7 -0
- package/dist/benchmarks/tau2-bench.js +11 -0
- package/dist/benchmarks/tau2-bench.js.map +1 -0
- package/dist/benchmarks/tau3-banking.d.ts +15 -0
- package/dist/benchmarks/tau3-banking.js +9 -0
- package/dist/benchmarks/tau3-banking.js.map +1 -0
- package/dist/benchmarks/terminal-bench.d.ts +24 -0
- package/dist/benchmarks/terminal-bench.js +8 -0
- package/dist/benchmarks/terminal-bench.js.map +1 -0
- package/dist/benchmarks/toollm.d.ts +16 -0
- package/dist/benchmarks/toollm.js +10 -0
- package/dist/benchmarks/toollm.js.map +1 -0
- package/dist/benchmarks/trata-hedge.d.ts +32 -0
- package/dist/benchmarks/trata-hedge.js +7 -0
- package/dist/benchmarks/trata-hedge.js.map +1 -0
- package/dist/benchmarks/types.d.ts +107 -0
- package/dist/benchmarks/types.js +1 -0
- package/dist/benchmarks/types.js.map +1 -0
- package/dist/benchmarks/webarena-verified.d.ts +16 -0
- package/dist/benchmarks/webarena-verified.js +10 -0
- package/dist/benchmarks/webarena-verified.js.map +1 -0
- package/dist/chunk-2PVVP7GN.js +197 -0
- package/dist/chunk-2PVVP7GN.js.map +1 -0
- package/dist/chunk-2XU6OGEN.js +170 -0
- package/dist/chunk-2XU6OGEN.js.map +1 -0
- package/dist/chunk-53UPUNBZ.js +325 -0
- package/dist/chunk-53UPUNBZ.js.map +1 -0
- package/dist/chunk-5SBJCB6W.js +144 -0
- package/dist/chunk-5SBJCB6W.js.map +1 -0
- package/dist/chunk-7WSD27QQ.js +118 -0
- package/dist/chunk-7WSD27QQ.js.map +1 -0
- package/dist/chunk-C7T7WEK2.js +103 -0
- package/dist/chunk-C7T7WEK2.js.map +1 -0
- package/dist/chunk-CKUVRZ2T.js +251 -0
- package/dist/chunk-CKUVRZ2T.js.map +1 -0
- package/dist/chunk-HBSWHQNJ.js +30 -0
- package/dist/chunk-HBSWHQNJ.js.map +1 -0
- package/dist/chunk-HHXFIHXC.js +116 -0
- package/dist/chunk-HHXFIHXC.js.map +1 -0
- package/dist/chunk-IFAV6KEM.js +276 -0
- package/dist/chunk-IFAV6KEM.js.map +1 -0
- package/dist/chunk-INNOYXCP.js +387 -0
- package/dist/chunk-INNOYXCP.js.map +1 -0
- package/dist/chunk-J3KDJNX2.js +182 -0
- package/dist/chunk-J3KDJNX2.js.map +1 -0
- package/dist/chunk-JRWWGMK7.js +148 -0
- package/dist/chunk-JRWWGMK7.js.map +1 -0
- package/dist/chunk-JTHWEDEW.js +32 -0
- package/dist/chunk-JTHWEDEW.js.map +1 -0
- package/dist/chunk-KDIKRJGB.js +120 -0
- package/dist/chunk-KDIKRJGB.js.map +1 -0
- package/dist/chunk-LRRD7NAG.js +301 -0
- package/dist/chunk-LRRD7NAG.js.map +1 -0
- package/dist/chunk-ODT47UAY.js +221 -0
- package/dist/chunk-ODT47UAY.js.map +1 -0
- package/dist/chunk-PA2ZKHJC.js +230 -0
- package/dist/chunk-PA2ZKHJC.js.map +1 -0
- package/dist/chunk-PPYSEKFM.js +182 -0
- package/dist/chunk-PPYSEKFM.js.map +1 -0
- package/dist/chunk-PUIRNYI7.js +189 -0
- package/dist/chunk-PUIRNYI7.js.map +1 -0
- package/dist/chunk-R36V2VP7.js +169 -0
- package/dist/chunk-R36V2VP7.js.map +1 -0
- package/dist/chunk-R67DFVLO.js +142 -0
- package/dist/chunk-R67DFVLO.js.map +1 -0
- package/dist/chunk-SEVJPLZC.js +260 -0
- package/dist/chunk-SEVJPLZC.js.map +1 -0
- package/dist/chunk-SYDW647C.js +318 -0
- package/dist/chunk-SYDW647C.js.map +1 -0
- package/dist/chunk-TBKU5XQI.js +228 -0
- package/dist/chunk-TBKU5XQI.js.map +1 -0
- package/dist/chunk-TSWPNOYM.js +147 -0
- package/dist/chunk-TSWPNOYM.js.map +1 -0
- package/dist/chunk-UAIOHCUK.js +27 -0
- package/dist/chunk-UAIOHCUK.js.map +1 -0
- package/dist/chunk-UPAMRDX4.js +233 -0
- package/dist/chunk-UPAMRDX4.js.map +1 -0
- package/dist/chunk-VQRS7VUC.js +342 -0
- package/dist/chunk-VQRS7VUC.js.map +1 -0
- package/dist/chunk-X3BTXCJ4.js +262 -0
- package/dist/chunk-X3BTXCJ4.js.map +1 -0
- package/dist/chunk-X5YKXC6V.js +211 -0
- package/dist/chunk-X5YKXC6V.js.map +1 -0
- package/dist/chunk-Y6O2OCUO.js +130 -0
- package/dist/chunk-Y6O2OCUO.js.map +1 -0
- package/dist/chunk-YCGY7UIZ.js +208 -0
- package/dist/chunk-YCGY7UIZ.js.map +1 -0
- package/dist/chunk-Z7ML6L77.js +162 -0
- package/dist/chunk-Z7ML6L77.js.map +1 -0
- package/dist/chunk-ZEWMTR5M.js +136 -0
- package/dist/chunk-ZEWMTR5M.js.map +1 -0
- package/dist/index.d.ts +355 -0
- package/dist/index.js +1908 -0
- package/dist/index.js.map +1 -0
- package/fixtures/aec-bench.json +18 -0
- package/fixtures/agentbench-dbbench.json +22 -0
- package/fixtures/bfcl.json +45 -0
- package/fixtures/commit0.json +72 -0
- package/fixtures/crag.json +10 -0
- package/fixtures/dabstep.json +22 -0
- package/fixtures/enterpriseops-gym.json +103 -0
- package/fixtures/finresearchbench.json +21 -0
- package/fixtures/finsearchcomp.json +66 -0
- package/fixtures/frames.json +26 -0
- package/fixtures/hotpotqa.json +182 -0
- package/fixtures/nomiracl.json +26 -0
- package/fixtures/open-rag-bench.json +16 -0
- package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
- package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
- package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
- package/fixtures/pier-agent/no-model-task/task.toml +35 -0
- package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
- package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
- package/fixtures/programbench.json +17 -0
- package/fixtures/ragbench.json +21 -0
- package/fixtures/simpleqa.json +121 -0
- package/fixtures/t2-ragbench.json +13 -0
- package/fixtures/tau2-bench.json +16 -0
- package/fixtures/tau3-banking.json +16 -0
- package/fixtures/toollm.json +28 -0
- package/fixtures/webarena-verified.json +20 -0
- package/package.json +60 -20
- package/pier_agents/__init__.py +18 -0
- package/pier_agents/candidate_contract.py +755 -0
- package/pier_agents/process_boundary.py +321 -0
- package/pier_agents/tangle_candidate.py +907 -0
- package/pier_agents/workspace_boundary.py +368 -0
- package/scripts/appworld_driver.py +359 -0
- package/scripts/cadbench_prepare.py +22 -0
- package/scripts/cadgenbench_hard_parts.py +48 -0
- package/scripts/clbench_codebase_judge.py +73 -0
- package/scripts/commit0_judge.py +170 -0
- package/scripts/dabstep_judge.py +42 -0
- package/scripts/enterpriseops_gym_judge.py +281 -0
- package/scripts/programbench_judge.py +120 -0
- package/scripts/render-gate-chart.mjs +176 -0
- package/scripts/run-package-tests.mjs +56 -0
- package/scripts/terminate-pier-trial.mts +66 -0
- package/scripts/trata-hedge/README.md +56 -0
- package/scripts/trata-hedge/run.sh +60 -0
- package/scripts/trata-hedge/solve.py +83 -0
- package/scripts/verify-packed-consumer.mjs +235 -0
- package/scripts/verify-pier-agent.mts +715 -0
- package/scripts/verify-pier-pair.mts +74 -0
- package/scripts/verify-pier-recovery.mts +139 -0
- package/src/adapters.ts +26 -0
- package/src/benchmarks/_harness.test.mts +178 -0
- package/src/benchmarks/_harness.ts +239 -16
- package/src/benchmarks/agentbench.ts +163 -0
- package/src/benchmarks/appworld.test.mts +15 -9
- package/src/benchmarks/bfcl.ts +346 -0
- package/src/benchmarks/crag.ts +137 -0
- package/src/benchmarks/dabstep.test.mts +70 -0
- package/src/benchmarks/dabstep.ts +212 -0
- package/src/benchmarks/external-adapters.test.mts +150 -0
- package/src/benchmarks/finresearchbench.ts +269 -0
- package/src/benchmarks/humaneval.ts +20 -8
- package/src/benchmarks/nomiracl.ts +180 -0
- package/src/benchmarks/open-rag-bench.ts +153 -0
- package/src/benchmarks/rag-benchmarks.test.mts +138 -0
- package/src/benchmarks/rag-shared.ts +327 -0
- package/src/benchmarks/ragbench.ts +171 -0
- package/src/benchmarks/swe-bench.test.mts +61 -0
- package/src/benchmarks/swe-bench.ts +201 -19
- package/src/benchmarks/t2-ragbench.ts +166 -0
- package/src/benchmarks/tau-bench-shared.ts +214 -0
- package/src/benchmarks/tau2-bench.ts +30 -0
- package/src/benchmarks/tau3-banking.ts +29 -0
- package/src/benchmarks/terminal-bench.test.mts +33 -0
- package/src/benchmarks/terminal-bench.ts +23 -8
- package/src/benchmarks/toollm.ts +254 -0
- package/src/benchmarks/types.ts +42 -0
- package/src/benchmarks/webarena-verified.ts +200 -0
- package/src/commit0-prereqs.sh +0 -0
- package/src/coordination-mcp-container-reach.mts +181 -0
- package/src/decoder-live.mts +1 -1
- package/src/examples/README.md +103 -39
- package/src/examples/benchmark-matrix.mts +101 -0
- package/src/examples/lean-proof-gate.README.md +77 -0
- package/src/examples/lean-proof-gate.mts +162 -0
- package/src/examples/lean-verify.ts +95 -0
- package/src/examples/lean.Dockerfile +12 -0
- package/src/examples/math-demo.mts +9 -7
- package/src/examples/strategy-demo.mts +10 -12
- package/src/gate.ts +3 -2
- package/src/hev-eval.mts +69 -0
- package/src/hev-improve.mts +169 -0
- package/src/hev-structural.mts +688 -0
- package/src/index.ts +73 -0
- package/src/mbpp-structural.mts +662 -0
- package/src/pier-agent.test-fixtures.mts +19 -0
- package/src/pier-agent.test.mts +363 -0
- package/src/pier-agent.ts +657 -0
- package/src/pier-result-grader.mjs +30 -0
- package/src/pier-result-grader.test.mts +62 -0
- package/src/pier-result-grader.ts +108 -0
- package/src/pier-task-outcome.test.mts +117 -0
- package/src/pier-task-outcome.ts +240 -0
- package/src/pier-trial-controller.test.mts +412 -0
- package/src/pier-trial-controller.ts +858 -0
- package/src/pier-trial-supervisor.mjs +352 -0
- package/src/resolve-client.ts +25 -2
- package/src/run-benchmarks-cli.mts +72 -0
- package/src/run-benchmarks-report.ts +66 -0
- package/src/run-benchmarks.test.mts +231 -0
- package/src/run-benchmarks.ts +589 -0
- package/src/smoke-structural-rollout.mts +393 -0
- package/src/swe-bench-env.test.ts +207 -0
- package/src/swe-bench-env.ts +554 -0
- package/src/swe-jail.ts +293 -0
- package/src/swe-self-improve.mts +84 -0
- package/src/swe-structural-judge-policy.test.ts +117 -0
- package/src/swe-structural-judge-policy.ts +133 -0
- package/src/swe-structural-policy.test.ts +124 -0
- package/src/swe-structural-policy.ts +132 -0
- package/src/swe-structural-provenance.test.ts +93 -0
- package/src/swe-structural-provenance.ts +138 -0
- package/src/swe-structural.mts +1260 -0
- package/src/swe-temp.ts +14 -0
- package/src/tb-container-executor.mts +234 -0
- package/src/tb-container-executor.test.mts +99 -0
- package/src/tb-supervisor-sidecar.mts +222 -0
- package/src/trata-gepa.mts +1 -1
- package/steerers/eops-itsm-population.json +1 -0
- package/tb_agents/opencode_refine_agent.py +117 -0
- package/tb_agents/opencode_router_agent.py +406 -0
- package/tb_agents/opencode_supervisor_agent.py +239 -0
- package/tb_agents/script_agent.py +66 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""Verify and materialize runtime-signed workspaces inside a Pier container."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import secrets
|
|
6
|
+
import shlex
|
|
7
|
+
import tempfile
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path, PurePosixPath
|
|
10
|
+
from typing import Any, Protocol
|
|
11
|
+
|
|
12
|
+
from pier.environments.base import BaseEnvironment
|
|
13
|
+
|
|
14
|
+
from .candidate_contract import (
|
|
15
|
+
PreparedCandidateContract,
|
|
16
|
+
WorkspaceFile,
|
|
17
|
+
sha256_bytes,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _workspace_check_script(
|
|
22
|
+
root: str,
|
|
23
|
+
files: tuple[WorkspaceFile, ...],
|
|
24
|
+
*,
|
|
25
|
+
allow_task_metadata: bool,
|
|
26
|
+
) -> bytes:
|
|
27
|
+
quoted_root = shlex.quote(root)
|
|
28
|
+
prune = (
|
|
29
|
+
f"\\( -path {shlex.quote(root + '/.git')} -o "
|
|
30
|
+
f"-path {shlex.quote(root + '/.sidecar')} \\) -prune -o "
|
|
31
|
+
if allow_task_metadata
|
|
32
|
+
else ""
|
|
33
|
+
)
|
|
34
|
+
expected_args = " ".join(shlex.quote(file.path) for file in files)
|
|
35
|
+
expected = (
|
|
36
|
+
f"$(printf '%s\\n' {expected_args} | LC_ALL=C sort)" if files else "''"
|
|
37
|
+
)
|
|
38
|
+
checks = [
|
|
39
|
+
"#!/bin/sh",
|
|
40
|
+
"set -eu",
|
|
41
|
+
f"test -d {quoted_root}",
|
|
42
|
+
f"test ! -L {quoted_root}",
|
|
43
|
+
f'test "$(realpath -e -- {quoted_root})" = {quoted_root}',
|
|
44
|
+
f'test -z "$(find {quoted_root} {prune}-type l -print -quit)"',
|
|
45
|
+
f'test -z "$(find {quoted_root} {prune}! -type d ! -type f -print -quit)"',
|
|
46
|
+
f"observed=$(find {quoted_root} {prune}-type f -printf '%P\\n' | LC_ALL=C sort)",
|
|
47
|
+
f"expected={expected}",
|
|
48
|
+
'test "$observed" = "$expected"',
|
|
49
|
+
]
|
|
50
|
+
for file in files:
|
|
51
|
+
path = shlex.quote(f"{root}/{file.path}")
|
|
52
|
+
checks.extend(
|
|
53
|
+
[
|
|
54
|
+
f"test -f {path}",
|
|
55
|
+
f"test ! -L {path}",
|
|
56
|
+
f'test "$(stat -c %h -- {path})" = 1',
|
|
57
|
+
f'test "$(stat -c %a -- {path})" = {file.mode:o}',
|
|
58
|
+
f'test "$(wc -c < {path})" = {file.byte_length}',
|
|
59
|
+
f"test \"$(sha256sum -- {path} | cut -d' ' -f1)\" = "
|
|
60
|
+
f"{file.sha256.removeprefix('sha256:')}",
|
|
61
|
+
]
|
|
62
|
+
)
|
|
63
|
+
return ("\n".join(checks) + "\n").encode("utf-8")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class WorkspaceBoundaryHost(Protocol):
|
|
67
|
+
async def _exec(
|
|
68
|
+
self, environment: BaseEnvironment, command: str, **kwargs: Any
|
|
69
|
+
) -> Any: ...
|
|
70
|
+
|
|
71
|
+
async def _ensure_real_directory(
|
|
72
|
+
self,
|
|
73
|
+
environment: BaseEnvironment,
|
|
74
|
+
path: str,
|
|
75
|
+
*,
|
|
76
|
+
anchor: str,
|
|
77
|
+
mode: int = 0o755,
|
|
78
|
+
) -> None: ...
|
|
79
|
+
|
|
80
|
+
async def _assert_real_directory(
|
|
81
|
+
self,
|
|
82
|
+
environment: BaseEnvironment,
|
|
83
|
+
path: str,
|
|
84
|
+
*,
|
|
85
|
+
label: str,
|
|
86
|
+
user: str | int | None = "root",
|
|
87
|
+
) -> None: ...
|
|
88
|
+
|
|
89
|
+
async def _write_verified_file(
|
|
90
|
+
self,
|
|
91
|
+
environment: BaseEnvironment,
|
|
92
|
+
source: Path,
|
|
93
|
+
target: str,
|
|
94
|
+
*,
|
|
95
|
+
anchor: str,
|
|
96
|
+
mode: int,
|
|
97
|
+
digest: str,
|
|
98
|
+
byte_length: int,
|
|
99
|
+
) -> None: ...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass(frozen=True)
|
|
103
|
+
class WorkspaceBoundaryConfig:
|
|
104
|
+
contract: PreparedCandidateContract
|
|
105
|
+
task_snapshot: Path
|
|
106
|
+
control_root: str
|
|
107
|
+
protected_roots: tuple[str, ...]
|
|
108
|
+
error_type: type[Exception]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def candidate_git_env(task_root: str) -> dict[str, str]:
|
|
112
|
+
return {
|
|
113
|
+
"GIT_CONFIG_COUNT": "1",
|
|
114
|
+
"GIT_CONFIG_KEY_0": "safe.directory",
|
|
115
|
+
"GIT_CONFIG_VALUE_0": task_root,
|
|
116
|
+
"GIT_CONFIG_NOSYSTEM": "1",
|
|
117
|
+
"GIT_CONFIG_GLOBAL": "/dev/null",
|
|
118
|
+
"GIT_CONFIG_SYSTEM": "/dev/null",
|
|
119
|
+
"GIT_NO_REPLACE_OBJECTS": "1",
|
|
120
|
+
"GIT_TERMINAL_PROMPT": "0",
|
|
121
|
+
"LC_ALL": "C",
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class CandidateWorkspaceBoundary:
|
|
126
|
+
def __init__(self, host: WorkspaceBoundaryHost, config: WorkspaceBoundaryConfig):
|
|
127
|
+
self._host = host
|
|
128
|
+
self._config = config
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def _contract(self) -> PreparedCandidateContract:
|
|
132
|
+
return self._config.contract
|
|
133
|
+
|
|
134
|
+
def _error(self, message: str) -> Exception:
|
|
135
|
+
return self._config.error_type(message)
|
|
136
|
+
|
|
137
|
+
async def verify_repository(self, environment: BaseEnvironment) -> None:
|
|
138
|
+
root = shlex.quote(self._contract.task_root)
|
|
139
|
+
git = f"git -c core.hooksPath=/dev/null -C {root}"
|
|
140
|
+
env = candidate_git_env(self._contract.task_root)
|
|
141
|
+
head = await self._host._exec(environment, f"{git} rev-parse HEAD", env=env)
|
|
142
|
+
tree = await self._host._exec(
|
|
143
|
+
environment, f"{git} rev-parse 'HEAD^{{tree}}'", env=env
|
|
144
|
+
)
|
|
145
|
+
replacements = await self._host._exec(
|
|
146
|
+
environment,
|
|
147
|
+
f"{git} for-each-ref --format='%(refname)' refs/replace",
|
|
148
|
+
env=env,
|
|
149
|
+
)
|
|
150
|
+
if (head.stdout or "").strip() != self._contract.repository_base_commit:
|
|
151
|
+
raise self._error("task checkout HEAD does not match the signed base commit")
|
|
152
|
+
if (tree.stdout or "").strip() != self._contract.repository_base_tree:
|
|
153
|
+
raise self._error("task checkout tree does not match the signed base tree")
|
|
154
|
+
if (replacements.stdout or "").strip():
|
|
155
|
+
raise self._error("task checkout contains forbidden Git replace refs")
|
|
156
|
+
|
|
157
|
+
async def materialize_task_workspace(self, environment: BaseEnvironment) -> None:
|
|
158
|
+
root = self._contract.task_root
|
|
159
|
+
quoted = shlex.quote(root)
|
|
160
|
+
exists = await self._host._exec(
|
|
161
|
+
environment,
|
|
162
|
+
f"test -e {quoted} || test -L {quoted}",
|
|
163
|
+
user="root",
|
|
164
|
+
check=False,
|
|
165
|
+
)
|
|
166
|
+
if exists.return_code == 0:
|
|
167
|
+
return
|
|
168
|
+
if exists.return_code != 1:
|
|
169
|
+
raise self._error("could not inspect the signed task root")
|
|
170
|
+
|
|
171
|
+
parent = str(PurePosixPath(root).parent)
|
|
172
|
+
await self._host._ensure_real_directory(environment, parent, anchor="/")
|
|
173
|
+
await self._host._exec(
|
|
174
|
+
environment,
|
|
175
|
+
"\n".join(
|
|
176
|
+
[
|
|
177
|
+
"set -eu",
|
|
178
|
+
f"test ! -e {quoted}",
|
|
179
|
+
f"test ! -L {quoted}",
|
|
180
|
+
f"mkdir -- {quoted}",
|
|
181
|
+
f'test "$(realpath -e -- {quoted})" = {quoted}',
|
|
182
|
+
]
|
|
183
|
+
),
|
|
184
|
+
user="root",
|
|
185
|
+
)
|
|
186
|
+
await environment.upload_dir(self._config.task_snapshot, root)
|
|
187
|
+
git = f"git -c core.hooksPath=/dev/null -C {quoted}"
|
|
188
|
+
await self._host._exec(
|
|
189
|
+
environment,
|
|
190
|
+
"\n".join(
|
|
191
|
+
[
|
|
192
|
+
"set -eu",
|
|
193
|
+
f"{git} init -b main",
|
|
194
|
+
f"{git} config user.email fixture@tangle.tools",
|
|
195
|
+
f'{git} config user.name "Tangle Fixture"',
|
|
196
|
+
f"{git} add -A",
|
|
197
|
+
f"GIT_AUTHOR_DATE=2000-01-01T00:00:00Z "
|
|
198
|
+
f"GIT_COMMITTER_DATE=2000-01-01T00:00:00Z "
|
|
199
|
+
f"{git} commit -m baseline",
|
|
200
|
+
f"{git} tag benchmark-base",
|
|
201
|
+
]
|
|
202
|
+
),
|
|
203
|
+
env=candidate_git_env(root),
|
|
204
|
+
user="root",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
async def verify_pier_execution_identity(
|
|
208
|
+
self, environment: BaseEnvironment
|
|
209
|
+
) -> None:
|
|
210
|
+
expected = self._contract.container
|
|
211
|
+
configured_image = getattr(environment.task_env_config, "docker_image", None)
|
|
212
|
+
pinned_image = f"{expected['image']}@{expected['indexDigest']}"
|
|
213
|
+
if configured_image != pinned_image:
|
|
214
|
+
raise self._error(
|
|
215
|
+
"Pier task image does not match the signed immutable image: "
|
|
216
|
+
f"expected={pinned_image} observed={configured_image}"
|
|
217
|
+
)
|
|
218
|
+
configured_os = getattr(environment.task_env_config, "os", None)
|
|
219
|
+
configured_os = getattr(configured_os, "value", configured_os)
|
|
220
|
+
if configured_os is not None and str(configured_os).lower() != "linux":
|
|
221
|
+
raise self._error(
|
|
222
|
+
f"Pier task OS does not match signed Linux platform: {configured_os}"
|
|
223
|
+
)
|
|
224
|
+
platform = expected["platform"]
|
|
225
|
+
observed = await self._host._exec(
|
|
226
|
+
environment,
|
|
227
|
+
'printf \'%s %s\' "$(uname -s)" "$(uname -m)"',
|
|
228
|
+
user="root",
|
|
229
|
+
)
|
|
230
|
+
kernel, _, machine = (observed.stdout or "").strip().partition(" ")
|
|
231
|
+
architectures = {"x86_64": "amd64", "aarch64": "arm64"}
|
|
232
|
+
if kernel != "Linux" or architectures.get(machine) != platform["architecture"]:
|
|
233
|
+
raise self._error(
|
|
234
|
+
"running Pier platform does not match the signed container platform: "
|
|
235
|
+
f"observed={kernel}/{machine} expected=linux/{platform['architecture']}"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
async def verify_resolved_root_isolation(
|
|
239
|
+
self, environment: BaseEnvironment
|
|
240
|
+
) -> None:
|
|
241
|
+
roots = [self._contract.task_root]
|
|
242
|
+
if self._contract.candidate_root is not None:
|
|
243
|
+
roots.append(self._contract.candidate_root)
|
|
244
|
+
for root in roots:
|
|
245
|
+
await self._host._assert_real_directory(
|
|
246
|
+
environment, root, label="execution workspace"
|
|
247
|
+
)
|
|
248
|
+
if self._contract.candidate_root is not None:
|
|
249
|
+
task = shlex.quote(self._contract.task_root)
|
|
250
|
+
candidate = shlex.quote(self._contract.candidate_root)
|
|
251
|
+
await self._host._exec(
|
|
252
|
+
environment,
|
|
253
|
+
"\n".join(
|
|
254
|
+
[
|
|
255
|
+
"set -eu",
|
|
256
|
+
f"task=$(realpath -e -- {task})",
|
|
257
|
+
f"candidate=$(realpath -e -- {candidate})",
|
|
258
|
+
'test "$task" != "$candidate"',
|
|
259
|
+
'case "$task" in "$candidate"/*) exit 1;; esac',
|
|
260
|
+
'case "$candidate" in "$task"/*) exit 1;; esac',
|
|
261
|
+
]
|
|
262
|
+
),
|
|
263
|
+
user="root",
|
|
264
|
+
)
|
|
265
|
+
for protected in self._config.protected_roots:
|
|
266
|
+
quoted_protected = shlex.quote(protected)
|
|
267
|
+
for root in roots:
|
|
268
|
+
quoted_root = shlex.quote(root)
|
|
269
|
+
await self._host._exec(
|
|
270
|
+
environment,
|
|
271
|
+
"\n".join(
|
|
272
|
+
[
|
|
273
|
+
"set -eu",
|
|
274
|
+
f"if test -e {quoted_protected}; then",
|
|
275
|
+
f" protected=$(realpath -e -- {quoted_protected})",
|
|
276
|
+
f" root=$(realpath -e -- {quoted_root})",
|
|
277
|
+
' test "$root" != "$protected"',
|
|
278
|
+
' case "$root" in "$protected"/*) exit 1;; esac',
|
|
279
|
+
' case "$protected" in "$root"/*) exit 1;; esac',
|
|
280
|
+
"fi",
|
|
281
|
+
]
|
|
282
|
+
),
|
|
283
|
+
user="root",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
async def verify_container_workspace(
|
|
287
|
+
self,
|
|
288
|
+
environment: BaseEnvironment,
|
|
289
|
+
root: str,
|
|
290
|
+
files: tuple[WorkspaceFile, ...],
|
|
291
|
+
*,
|
|
292
|
+
allow_task_metadata: bool,
|
|
293
|
+
) -> None:
|
|
294
|
+
script = _workspace_check_script(
|
|
295
|
+
root,
|
|
296
|
+
files,
|
|
297
|
+
allow_task_metadata=allow_task_metadata,
|
|
298
|
+
)
|
|
299
|
+
control_root = self._config.control_root
|
|
300
|
+
target = f"{control_root}/workspace-check-{secrets.token_hex(24)}.sh"
|
|
301
|
+
with tempfile.NamedTemporaryFile(delete=False) as handle:
|
|
302
|
+
handle.write(script)
|
|
303
|
+
local_path = Path(handle.name)
|
|
304
|
+
control_ready = False
|
|
305
|
+
|
|
306
|
+
async def cleanup() -> None:
|
|
307
|
+
local_path.unlink(missing_ok=True)
|
|
308
|
+
if not control_ready:
|
|
309
|
+
return
|
|
310
|
+
await self._host._assert_real_directory(
|
|
311
|
+
environment,
|
|
312
|
+
control_root,
|
|
313
|
+
label="candidate control directory",
|
|
314
|
+
)
|
|
315
|
+
await self._host._exec(
|
|
316
|
+
environment,
|
|
317
|
+
f"rm -f -- {shlex.quote(target)}",
|
|
318
|
+
user="root",
|
|
319
|
+
)
|
|
320
|
+
await self._host._exec(
|
|
321
|
+
environment,
|
|
322
|
+
"\n".join(
|
|
323
|
+
[
|
|
324
|
+
"set -eu",
|
|
325
|
+
f"test ! -e {shlex.quote(target)}",
|
|
326
|
+
f"test ! -L {shlex.quote(target)}",
|
|
327
|
+
f'test -z "$(find {shlex.quote(control_root)} -maxdepth 1 '
|
|
328
|
+
"-name '.tangle-*' -print -quit)\"",
|
|
329
|
+
]
|
|
330
|
+
),
|
|
331
|
+
user="root",
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
await self._host._ensure_real_directory(
|
|
336
|
+
environment,
|
|
337
|
+
control_root,
|
|
338
|
+
anchor="/",
|
|
339
|
+
mode=0o700,
|
|
340
|
+
)
|
|
341
|
+
control_ready = True
|
|
342
|
+
await self._host._exec(
|
|
343
|
+
environment,
|
|
344
|
+
f"chmod 0700 -- {shlex.quote(control_root)}",
|
|
345
|
+
user="root",
|
|
346
|
+
)
|
|
347
|
+
await self._host._write_verified_file(
|
|
348
|
+
environment,
|
|
349
|
+
local_path,
|
|
350
|
+
target,
|
|
351
|
+
anchor=control_root,
|
|
352
|
+
mode=0o600,
|
|
353
|
+
digest=sha256_bytes(script),
|
|
354
|
+
byte_length=len(script),
|
|
355
|
+
)
|
|
356
|
+
await self._host._exec(
|
|
357
|
+
environment,
|
|
358
|
+
f"/bin/sh {shlex.quote(target)}",
|
|
359
|
+
user="root",
|
|
360
|
+
)
|
|
361
|
+
except BaseException as primary_error:
|
|
362
|
+
try:
|
|
363
|
+
await cleanup()
|
|
364
|
+
except BaseException as cleanup_error:
|
|
365
|
+
primary_error.add_note(f"workspace check cleanup failed: {cleanup_error}")
|
|
366
|
+
raise
|
|
367
|
+
else:
|
|
368
|
+
await cleanup()
|