@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,239 @@
|
|
|
1
|
+
"""Terminal-Bench opencode agent with a coordination MCP mounted into the task container."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shlex
|
|
6
|
+
import subprocess
|
|
7
|
+
import time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from terminal_bench.agents.base_agent import AgentResult
|
|
11
|
+
from terminal_bench.agents.failure_mode import FailureMode
|
|
12
|
+
from terminal_bench.terminal.tmux_session import TmuxSession
|
|
13
|
+
|
|
14
|
+
from tb_agents.opencode_router_agent import (
|
|
15
|
+
_CONTAINER_DIR,
|
|
16
|
+
OpenCodeRouterAgent,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
DOCKER_BRIDGE_GATEWAY = os.environ.get("DOCKER_BRIDGE_GATEWAY", "172.17.0.1")
|
|
20
|
+
_WORKER_CONFIG_PATH = f"{_CONTAINER_DIR}/opencode-worker.json"
|
|
21
|
+
# The sidecar entrypoint, resolved relative to the bench dir (this file is bench/tb_agents/...).
|
|
22
|
+
_BENCH_DIR = Path(__file__).resolve().parent.parent
|
|
23
|
+
_REPO_DIR = _BENCH_DIR.parent
|
|
24
|
+
_SIDECAR_TS = _BENCH_DIR / "src" / "tb-supervisor-sidecar.mts"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_tsx() -> list[str]:
|
|
28
|
+
"""Prefer the pinned local tsx binary (no npx download / no shell hook rewrite)."""
|
|
29
|
+
for cand in (
|
|
30
|
+
_BENCH_DIR / "node_modules" / ".bin" / "tsx",
|
|
31
|
+
_REPO_DIR / "node_modules" / ".bin" / "tsx",
|
|
32
|
+
):
|
|
33
|
+
if cand.exists():
|
|
34
|
+
return [str(cand)]
|
|
35
|
+
return ["npx", "tsx"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class OpenCodeSupervisorAgent(OpenCodeRouterAgent):
|
|
39
|
+
"""`OpenCodeRouterAgent` + a coordination MCP mounted into the in-container opencode.
|
|
40
|
+
|
|
41
|
+
Adds a host orchestration sidecar and the `mcp.coordination` config block; otherwise
|
|
42
|
+
identical to Arm A. Worker model defaults to the same model as the supervisor.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, model_name: str, *args, **kwargs):
|
|
46
|
+
super().__init__(model_name, *args, **kwargs)
|
|
47
|
+
self._mcp_port: int | None = None
|
|
48
|
+
self._sidecar: subprocess.Popen | None = None
|
|
49
|
+
self._sidecar_stdout = None
|
|
50
|
+
self._sidecar_port_file: Path | None = None
|
|
51
|
+
self._sidecar_log_file: Path | None = None
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def name() -> str:
|
|
55
|
+
return "agent-runtime-opencode-supervisor"
|
|
56
|
+
|
|
57
|
+
def _mcp_url(self) -> str:
|
|
58
|
+
return f"http://{DOCKER_BRIDGE_GATEWAY}:{self._mcp_port}/mcp"
|
|
59
|
+
|
|
60
|
+
def _build_router_config(self) -> str:
|
|
61
|
+
"""Base router config plus the coordination MCP block."""
|
|
62
|
+
config = json.loads(super()._build_router_config())
|
|
63
|
+
if self._mcp_port is not None:
|
|
64
|
+
config["mcp"] = {
|
|
65
|
+
"coordination": {
|
|
66
|
+
"type": "remote",
|
|
67
|
+
"url": self._mcp_url(),
|
|
68
|
+
"enabled": True,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return json.dumps(config)
|
|
72
|
+
|
|
73
|
+
def _write_config_to_container(self, session: TmuxSession) -> None:
|
|
74
|
+
super()._write_config_to_container(session)
|
|
75
|
+
worker_config = OpenCodeRouterAgent._build_router_config(self)
|
|
76
|
+
session.container.exec_run(
|
|
77
|
+
[
|
|
78
|
+
"sh",
|
|
79
|
+
"-c",
|
|
80
|
+
f"printf '%s' {shlex.quote(worker_config)} > {_WORKER_CONFIG_PATH}",
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def _resolve_container_id(self, session: TmuxSession) -> str | None:
|
|
85
|
+
return getattr(session.container, "id", None) or getattr(
|
|
86
|
+
session.container, "short_id", None
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def _start_sidecar(self, container_id: str, logging_dir: Path | None) -> None:
|
|
90
|
+
base = logging_dir if logging_dir is not None else Path.cwd()
|
|
91
|
+
base.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
self._sidecar_port_file = base / ".tb-sidecar-port"
|
|
93
|
+
self._sidecar_log_file = base / "tb-sidecar-events.jsonl"
|
|
94
|
+
self._sidecar_port_file.unlink(missing_ok=True)
|
|
95
|
+
|
|
96
|
+
env = dict(os.environ)
|
|
97
|
+
env.update(
|
|
98
|
+
{
|
|
99
|
+
"TB_TARGET_CONTAINER": container_id,
|
|
100
|
+
"OPENAI_API_KEY": self._router_api_key,
|
|
101
|
+
"OPENAI_BASE_URL": self._router_base_url,
|
|
102
|
+
"WORKER_MODEL": self._model_name,
|
|
103
|
+
"TB_SIDECAR_PORT_FILE": str(self._sidecar_port_file),
|
|
104
|
+
"TB_SIDECAR_LOG": str(self._sidecar_log_file),
|
|
105
|
+
"DOCKER_BRIDGE_GATEWAY": DOCKER_BRIDGE_GATEWAY,
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
self._sidecar_stdout = open(base / "tb-sidecar.stdout.log", "w")
|
|
109
|
+
self._logger.info("Starting orchestration sidecar for container %s", container_id)
|
|
110
|
+
self._sidecar = subprocess.Popen(
|
|
111
|
+
[*_resolve_tsx(), str(_SIDECAR_TS)],
|
|
112
|
+
cwd=str(_BENCH_DIR),
|
|
113
|
+
env=env,
|
|
114
|
+
stdout=self._sidecar_stdout,
|
|
115
|
+
stderr=subprocess.STDOUT,
|
|
116
|
+
)
|
|
117
|
+
deadline = time.time() + 60.0
|
|
118
|
+
while time.time() < deadline:
|
|
119
|
+
if self._sidecar.poll() is not None:
|
|
120
|
+
raise RuntimeError(
|
|
121
|
+
f"orchestration sidecar exited early with code {self._sidecar.returncode} "
|
|
122
|
+
f"(see {base / 'tb-sidecar.stdout.log'})"
|
|
123
|
+
)
|
|
124
|
+
if self._sidecar_port_file.exists():
|
|
125
|
+
txt = self._sidecar_port_file.read_text().strip()
|
|
126
|
+
if txt.isdigit():
|
|
127
|
+
self._mcp_port = int(txt)
|
|
128
|
+
self._logger.info(
|
|
129
|
+
"Sidecar ready: mcp port=%d url=%s", self._mcp_port, self._mcp_url()
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
time.sleep(0.5)
|
|
133
|
+
raise RuntimeError("orchestration sidecar did not publish a port within 60s")
|
|
134
|
+
|
|
135
|
+
def _stop_sidecar(self) -> None:
|
|
136
|
+
if self._sidecar is None:
|
|
137
|
+
return
|
|
138
|
+
try:
|
|
139
|
+
self._sidecar.terminate()
|
|
140
|
+
try:
|
|
141
|
+
self._sidecar.wait(timeout=30)
|
|
142
|
+
except subprocess.TimeoutExpired:
|
|
143
|
+
self._sidecar.kill()
|
|
144
|
+
except Exception as e: # noqa: BLE001 - teardown is never fatal
|
|
145
|
+
self._logger.warning("Could not stop sidecar cleanly: %s", e)
|
|
146
|
+
finally:
|
|
147
|
+
if self._sidecar_stdout is not None:
|
|
148
|
+
try:
|
|
149
|
+
self._sidecar_stdout.close()
|
|
150
|
+
except Exception: # noqa: BLE001
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
def _orchestration_summary(self) -> dict:
|
|
154
|
+
summary: dict = {
|
|
155
|
+
"settled": [],
|
|
156
|
+
"worker_outputs": [],
|
|
157
|
+
"history_len": 0,
|
|
158
|
+
"worker_ran": False,
|
|
159
|
+
"worker_tokens": {"input": 0, "output": 0},
|
|
160
|
+
}
|
|
161
|
+
if self._sidecar_log_file is None:
|
|
162
|
+
return summary
|
|
163
|
+
final = Path(str(self._sidecar_log_file) + ".final.json")
|
|
164
|
+
try:
|
|
165
|
+
if final.exists():
|
|
166
|
+
data = json.loads(final.read_text())
|
|
167
|
+
settled = data.get("settled", []) or []
|
|
168
|
+
summary["settled"] = settled
|
|
169
|
+
summary["worker_outputs"] = data.get("workerOutputs", []) or []
|
|
170
|
+
summary["history_len"] = len(data.get("history", []) or [])
|
|
171
|
+
summary["worker_tokens"] = data.get(
|
|
172
|
+
"workerTokens", {"input": 0, "output": 0}
|
|
173
|
+
)
|
|
174
|
+
summary["worker_ran"] = any(
|
|
175
|
+
isinstance(w, dict) and w.get("containerId")
|
|
176
|
+
for w in summary["worker_outputs"]
|
|
177
|
+
) or len(settled) > 0
|
|
178
|
+
except Exception as e: # noqa: BLE001
|
|
179
|
+
self._logger.warning("Could not read orchestration summary: %s", e)
|
|
180
|
+
return summary
|
|
181
|
+
|
|
182
|
+
def perform_task(
|
|
183
|
+
self,
|
|
184
|
+
instruction: str,
|
|
185
|
+
session: TmuxSession,
|
|
186
|
+
logging_dir: Path | None = None,
|
|
187
|
+
) -> AgentResult:
|
|
188
|
+
container_id = self._resolve_container_id(session)
|
|
189
|
+
if not container_id:
|
|
190
|
+
self._logger.error("Could not resolve container id for supervisor sidecar.")
|
|
191
|
+
return AgentResult(failure_mode=FailureMode.UNKNOWN_AGENT_ERROR)
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
self._start_sidecar(container_id, logging_dir)
|
|
195
|
+
except Exception as e: # noqa: BLE001
|
|
196
|
+
self._logger.error("Sidecar failed to start: %s", e)
|
|
197
|
+
self._mcp_port = None
|
|
198
|
+
return AgentResult(failure_mode=FailureMode.UNKNOWN_AGENT_ERROR)
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
supervised_instruction = (
|
|
202
|
+
"You are the supervisor. You have an MCP server named \"coordination\" with "
|
|
203
|
+
"tools including spawn_agent(profile, task), observe_agent(workerId), "
|
|
204
|
+
"await_event(), and stop(). A spawned worker runs a full coding agent INSIDE "
|
|
205
|
+
"THIS SAME container and its file changes persist here, so you may delegate "
|
|
206
|
+
"concrete sub-tasks (e.g. \"install perl and 7z\", \"crack the hash with john\") "
|
|
207
|
+
"to workers via spawn_agent, then observe_agent/await_event for their results. "
|
|
208
|
+
"Delegate independent or heavy sub-tasks to workers when useful; do the rest "
|
|
209
|
+
"yourself. Complete the task fully.\n\n"
|
|
210
|
+
f"TASK:\n{instruction}"
|
|
211
|
+
)
|
|
212
|
+
result = super().perform_task(supervised_instruction, session, logging_dir)
|
|
213
|
+
finally:
|
|
214
|
+
self._stop_sidecar()
|
|
215
|
+
|
|
216
|
+
summary = self._orchestration_summary()
|
|
217
|
+
if logging_dir is not None:
|
|
218
|
+
try:
|
|
219
|
+
(logging_dir / "orchestration-summary.json").write_text(
|
|
220
|
+
json.dumps(summary, indent=2)
|
|
221
|
+
)
|
|
222
|
+
except Exception: # noqa: BLE001
|
|
223
|
+
pass
|
|
224
|
+
self._logger.info(
|
|
225
|
+
"Arm B orchestration: workers_settled=%d history_len=%d worker_ran=%s",
|
|
226
|
+
len(summary.get("settled", [])),
|
|
227
|
+
summary.get("history_len", 0),
|
|
228
|
+
summary.get("worker_ran"),
|
|
229
|
+
)
|
|
230
|
+
wt = summary.get("worker_tokens") or {}
|
|
231
|
+
worker_in = int(wt.get("input") or 0)
|
|
232
|
+
worker_out = int(wt.get("output") or 0)
|
|
233
|
+
if result.failure_mode == FailureMode.AGENT_INSTALLATION_FAILED:
|
|
234
|
+
return result
|
|
235
|
+
return AgentResult(
|
|
236
|
+
total_input_tokens=result.total_input_tokens + worker_in,
|
|
237
|
+
total_output_tokens=result.total_output_tokens + worker_out,
|
|
238
|
+
failure_mode=result.failure_mode,
|
|
239
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Custom Terminal-Bench agent that executes a precomputed shell script in the
|
|
2
|
+
task container, then returns control to the harness so the task's own verifier
|
|
3
|
+
scores the resulting container state.
|
|
4
|
+
|
|
5
|
+
This is the deterministic-judge seam for agent-runtime/bench: our worker produces
|
|
6
|
+
an *artifact* (the commands it ran to attempt the task) and the judge replays that
|
|
7
|
+
artifact in a fresh task container via `tb run --agent-import-path
|
|
8
|
+
tb_agents.script_agent:ScriptAgent --agent-kwarg script_path=<file>`. The harness
|
|
9
|
+
then runs the per-task tests exactly as it does for the oracle agent, so the score
|
|
10
|
+
comes from Terminal-Bench's published verifier — never a self-authored judge.
|
|
11
|
+
|
|
12
|
+
Mechanism mirrors terminal_bench.agents.oracle_agent.OracleAgent: copy the script
|
|
13
|
+
into the container and run it with `block=True`, so the agent step completes only
|
|
14
|
+
after the script finishes (or the harness agent-timeout fires).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from terminal_bench.agents.base_agent import AgentResult, BaseAgent
|
|
20
|
+
from terminal_bench.agents.failure_mode import FailureMode
|
|
21
|
+
from terminal_bench.terminal.tmux_session import TmuxSession
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ScriptAgent(BaseAgent):
|
|
25
|
+
def __init__(self, script_path: str | None = None, **kwargs):
|
|
26
|
+
super().__init__(**kwargs)
|
|
27
|
+
if not script_path:
|
|
28
|
+
raise ValueError(
|
|
29
|
+
"ScriptAgent requires --agent-kwarg script_path=<path to the "
|
|
30
|
+
"artifact script>. Got none."
|
|
31
|
+
)
|
|
32
|
+
path = Path(script_path)
|
|
33
|
+
if not path.is_file():
|
|
34
|
+
raise FileNotFoundError(f"ScriptAgent script_path not found: {path}")
|
|
35
|
+
self._script_path = path
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def name() -> str:
|
|
39
|
+
return "agent-runtime-script"
|
|
40
|
+
|
|
41
|
+
def perform_task(
|
|
42
|
+
self,
|
|
43
|
+
instruction: str,
|
|
44
|
+
session: TmuxSession,
|
|
45
|
+
logging_dir: Path | None = None,
|
|
46
|
+
) -> AgentResult:
|
|
47
|
+
# An empty artifact = no-op attempt (the must-fail control). Skip execution
|
|
48
|
+
# so the verifier scores the untouched container — equivalent to the nop
|
|
49
|
+
# agent — instead of running an empty script.
|
|
50
|
+
if self._script_path.read_text().strip():
|
|
51
|
+
session.copy_to_container(
|
|
52
|
+
self._script_path,
|
|
53
|
+
container_dir="/agent-runtime",
|
|
54
|
+
container_filename="attempt.sh",
|
|
55
|
+
)
|
|
56
|
+
session.send_keys(
|
|
57
|
+
["bash /agent-runtime/attempt.sh", "Enter"],
|
|
58
|
+
max_timeout_sec=float("inf"),
|
|
59
|
+
block=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return AgentResult(
|
|
63
|
+
total_input_tokens=0,
|
|
64
|
+
total_output_tokens=0,
|
|
65
|
+
failure_mode=FailureMode.NONE,
|
|
66
|
+
)
|