Skip to content

Commit e12c176

Browse files
authored
Merge pull request #153 from secondorderai/codex/local-dev-unrelated-changes
Codex/local dev unrelated changes
2 parents a3e8450 + d7f3438 commit e12c176

11 files changed

Lines changed: 683 additions & 49 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Ouroboros Terminal-Bench 2.0 Pilot Harness
2+
3+
This directory contains a local pilot harness for running the Ouroboros CLI on
4+
Terminal-Bench 2.0 through Harbor. It is meant to validate that Ouroboros can be
5+
installed, built, and invoked inside Harbor task containers.
6+
7+
This is not a leaderboard submission package. It does not add audited
8+
leaderboard metadata or a full ATIF trajectory converter.
9+
10+
## Files
11+
12+
- `ouroboros_tbench_agent.py`: Harbor `BaseInstalledAgent` adapter for
13+
Ouroboros.
14+
- `run-pilot.sh`: local convenience script for a one-concurrency pilot run.
15+
16+
The directory name contains a hyphen, so it is not imported as a Python package.
17+
`run-pilot.sh` adds this directory to `PYTHONPATH` and imports the adapter as:
18+
19+
```bash
20+
ouroboros_tbench_agent:OuroborosInstalledAgent
21+
```
22+
23+
## Prerequisites
24+
25+
- Docker Desktop installed and running.
26+
- `uv` installed.
27+
- Network access from task containers for installing Bun and calling the model
28+
provider.
29+
- `OPENAI_API_KEY` exported in the shell.
30+
31+
Optional environment variables:
32+
33+
- `OUROBOROS_TBENCH_MODEL`, default `openai/gpt-5.5`
34+
- `OUROBOROS_TBENCH_REASONING`, default `medium`
35+
- `OUROBOROS_TBENCH_MAX_STEPS`, default `50`
36+
- `OUROBOROS_TBENCH_N_CONCURRENT`, default `1`
37+
- `OUROBOROS_TBENCH_TIMEOUT_SEC`, default `3600`
38+
- `OUROBOROS_TBENCH_JOBS_DIR`, default `/private/tmp/ouroboros-tbench/jobs`
39+
40+
## Developer Execution Plan
41+
42+
1. Start Docker Desktop and verify the daemon is reachable:
43+
44+
```bash
45+
docker info
46+
```
47+
48+
2. Export credentials:
49+
50+
```bash
51+
export OPENAI_API_KEY=...
52+
```
53+
54+
3. Verify Harbor is available through `uv`:
55+
56+
```bash
57+
uv tool run harbor --help
58+
```
59+
60+
4. Run the Harbor oracle sanity check:
61+
62+
```bash
63+
uv tool run harbor run --dataset terminal-bench@2.0 --agent oracle --n-concurrent 1
64+
```
65+
66+
5. Run the Ouroboros pilot:
67+
68+
```bash
69+
benchmarks/terminal-bench/run-pilot.sh
70+
```
71+
72+
6. Inspect results:
73+
74+
```bash
75+
ls -la /private/tmp/ouroboros-tbench/jobs
76+
```
77+
78+
Open the latest Harbor job directory and inspect the trial `agent/`,
79+
`verifier/`, `result.json`, and `trial.log` files. Ouroboros logs are written
80+
as `agent/ouroboros.txt`, `agent/ouroboros-stdout.txt`, and
81+
`agent/ouroboros-stderr.txt`.
82+
83+
## Verification Without Running The Benchmark
84+
85+
Check shell syntax:
86+
87+
```bash
88+
bash -n benchmarks/terminal-bench/run-pilot.sh
89+
```
90+
91+
Validate the adapter import:
92+
93+
```bash
94+
PYTHONPATH=benchmarks/terminal-bench \
95+
uv tool run --with harbor python -c "from ouroboros_tbench_agent import OuroborosInstalledAgent; print(OuroborosInstalledAgent.name())"
96+
```
97+
98+
Run the repo verification suite:
99+
100+
```bash
101+
bun run verify
102+
```
103+
104+
## Troubleshooting
105+
106+
### Docker daemon is down
107+
108+
If `docker info` fails, start Docker Desktop and wait until it reports that the
109+
engine is running.
110+
111+
### `OPENAI_API_KEY` is missing
112+
113+
`run-pilot.sh` exits early when `OPENAI_API_KEY` is empty because the default
114+
model is `openai/gpt-5.5`.
115+
116+
### Harbor is missing
117+
118+
Use `uv run harbor --help`. If `uv` cannot resolve Harbor, install it with:
119+
120+
```bash
121+
uv tool install harbor
122+
```
123+
124+
### Container setup fails while installing Bun
125+
126+
Confirm the task container has outbound network access and can reach
127+
`https://bun.sh`. Some Terminal-Bench tasks may intentionally restrict internet
128+
access; those tasks are not suitable for this pilot adapter without pre-baking
129+
Ouroboros and Bun into the agent image.
130+
131+
### Ouroboros build fails in the task container
132+
133+
Inspect `agent/ouroboros-stderr.txt` and `trial.log` in the latest Harbor job
134+
directory. The adapter uploads a filtered copy of the current repo and runs
135+
`bun install` followed by `bun run --filter @ouroboros/cli build`.
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import os
2+
import shlex
3+
import shutil
4+
from pathlib import Path
5+
6+
from harbor.agents.installed.base import BaseInstalledAgent, with_prompt_template
7+
from harbor.environments.base import BaseEnvironment
8+
from harbor.models.agent.context import AgentContext
9+
10+
11+
REPO_ROOT = Path(__file__).resolve().parents[2]
12+
CONTAINER_REPO_DIR = "/installed-agent/ouroboros"
13+
AGENT_LOG_NAME = "ouroboros.txt"
14+
AGENT_STDOUT_NAME = "ouroboros-stdout.txt"
15+
AGENT_STDERR_NAME = "ouroboros-stderr.txt"
16+
17+
18+
def _env_or_default(name: str, default: str) -> str:
19+
value = os.environ.get(name)
20+
return value if value and value.strip() else default
21+
22+
23+
class OuroborosInstalledAgent(BaseInstalledAgent):
24+
"""Harbor installed-agent adapter for running Ouroboros CLI on TB 2.0."""
25+
26+
SUPPORTS_ATIF = False
27+
28+
@staticmethod
29+
def name() -> str:
30+
return "ouroboros"
31+
32+
def get_version_command(self) -> str | None:
33+
return (
34+
f"cd {shlex.quote(CONTAINER_REPO_DIR)} && "
35+
"./packages/cli/dist/ouroboros --version"
36+
)
37+
38+
async def install(self, environment: BaseEnvironment) -> None:
39+
await self.exec_as_root(
40+
environment,
41+
command=(
42+
"if command -v apk >/dev/null 2>&1; then "
43+
"apk add --no-cache bash curl unzip ca-certificates tar; "
44+
"elif command -v apt-get >/dev/null 2>&1; then "
45+
"apt-get update && apt-get install -y "
46+
"bash curl unzip ca-certificates tar; "
47+
"elif command -v yum >/dev/null 2>&1; then "
48+
"yum install -y bash curl unzip ca-certificates tar; "
49+
"else "
50+
"echo 'No supported package manager found; assuming prerequisites exist' >&2; "
51+
"fi"
52+
),
53+
env={"DEBIAN_FRONTEND": "noninteractive"},
54+
timeout_sec=300,
55+
)
56+
57+
await self.exec_as_agent(
58+
environment,
59+
command=(
60+
"if ! command -v bun >/dev/null 2>&1; then "
61+
"curl -fsSL https://bun.sh/install | bash; "
62+
"fi"
63+
),
64+
timeout_sec=300,
65+
)
66+
67+
upload_dir = self._prepare_repo_upload()
68+
await environment.upload_dir(upload_dir, CONTAINER_REPO_DIR)
69+
70+
await self.exec_as_agent(
71+
environment,
72+
command=(
73+
'export BUN_INSTALL="$HOME/.bun"; '
74+
'export PATH="$BUN_INSTALL/bin:$PATH"; '
75+
"bun install && bun run --filter @ouroboros/cli build"
76+
),
77+
cwd=CONTAINER_REPO_DIR,
78+
timeout_sec=900,
79+
)
80+
81+
@with_prompt_template
82+
async def run(
83+
self, instruction: str, environment: BaseEnvironment, context: AgentContext
84+
) -> None:
85+
model = _env_or_default("OUROBOROS_TBENCH_MODEL", "openai/gpt-5.5")
86+
reasoning = _env_or_default("OUROBOROS_TBENCH_REASONING", "medium")
87+
max_steps = _env_or_default("OUROBOROS_TBENCH_MAX_STEPS", "50")
88+
89+
env = {
90+
"OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY", ""),
91+
"OUROBOROS_TBENCH_MODEL": model,
92+
"OUROBOROS_TBENCH_REASONING": reasoning,
93+
"OUROBOROS_TBENCH_MAX_STEPS": max_steps,
94+
}
95+
env = {key: value for key, value in env.items() if value}
96+
97+
command = (
98+
"mkdir -p /logs/agent && "
99+
'export BUN_INSTALL="$HOME/.bun"; '
100+
'export PATH="$BUN_INSTALL/bin:$PATH"; '
101+
f"{shlex.quote(CONTAINER_REPO_DIR)}/packages/cli/dist/ouroboros "
102+
f"--model {shlex.quote(model)} "
103+
f"--reasoning-effort {shlex.quote(reasoning)} "
104+
"--no-stream --no-rsi "
105+
f"--max-steps {shlex.quote(max_steps)} "
106+
f"-m {shlex.quote(instruction)} "
107+
f"> /logs/agent/{AGENT_STDOUT_NAME} "
108+
f"2> /logs/agent/{AGENT_STDERR_NAME}; "
109+
"status=$?; "
110+
f"cat /logs/agent/{AGENT_STDOUT_NAME} "
111+
f"/logs/agent/{AGENT_STDERR_NAME} > /logs/agent/{AGENT_LOG_NAME}; "
112+
"exit $status"
113+
)
114+
115+
await self.exec_as_agent(
116+
environment,
117+
command=command,
118+
env=env,
119+
timeout_sec=int(_env_or_default("OUROBOROS_TBENCH_TIMEOUT_SEC", "3600")),
120+
)
121+
122+
def populate_context_post_run(self, context: AgentContext) -> None:
123+
log_path = self.logs_dir / AGENT_LOG_NAME
124+
stdout_path = self.logs_dir / AGENT_STDOUT_NAME
125+
stderr_path = self.logs_dir / AGENT_STDERR_NAME
126+
127+
context.metadata = {
128+
"agent": self.name(),
129+
"model": _env_or_default("OUROBOROS_TBENCH_MODEL", "openai/gpt-5.5"),
130+
"reasoning_effort": _env_or_default("OUROBOROS_TBENCH_REASONING", "medium"),
131+
"max_steps": _env_or_default("OUROBOROS_TBENCH_MAX_STEPS", "50"),
132+
"log_path": str(log_path),
133+
"stdout_path": str(stdout_path),
134+
"stderr_path": str(stderr_path),
135+
"log_excerpt": self._read_excerpt(log_path),
136+
"stdout_excerpt": self._read_excerpt(stdout_path),
137+
"stderr_excerpt": self._read_excerpt(stderr_path),
138+
}
139+
140+
def _prepare_repo_upload(self) -> Path:
141+
target = self.logs_dir / "repo-upload"
142+
if target.exists():
143+
shutil.rmtree(target)
144+
145+
ignore = shutil.ignore_patterns(
146+
".git",
147+
".DS_Store",
148+
"node_modules",
149+
"dist",
150+
"out",
151+
"coverage",
152+
".cache",
153+
".turbo",
154+
"tmp",
155+
"logs",
156+
"*.log",
157+
".ouroboros-transcripts.db",
158+
)
159+
shutil.copytree(REPO_ROOT, target, ignore=ignore)
160+
return target
161+
162+
def _read_excerpt(self, path: Path, limit: int = 4000) -> str | None:
163+
if not path.exists():
164+
return None
165+
166+
text = path.read_text(errors="replace")
167+
if len(text) <= limit:
168+
return text
169+
return text[:limit] + "\n...[truncated]"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
6+
JOBS_DIR="${OUROBOROS_TBENCH_JOBS_DIR:-/private/tmp/ouroboros-tbench/jobs}"
7+
N_CONCURRENT="${OUROBOROS_TBENCH_N_CONCURRENT:-1}"
8+
9+
export OUROBOROS_TBENCH_MODEL="${OUROBOROS_TBENCH_MODEL:-openai/gpt-5.5}"
10+
export OUROBOROS_TBENCH_REASONING="${OUROBOROS_TBENCH_REASONING:-medium}"
11+
export OUROBOROS_TBENCH_MAX_STEPS="${OUROBOROS_TBENCH_MAX_STEPS:-50}"
12+
13+
if ! command -v uv >/dev/null 2>&1; then
14+
echo "error: uv is required. Install it from https://docs.astral.sh/uv/." >&2
15+
exit 1
16+
fi
17+
18+
if ! command -v docker >/dev/null 2>&1; then
19+
echo "error: docker is required. Install Docker Desktop and start it." >&2
20+
exit 1
21+
fi
22+
23+
if ! docker info >/dev/null 2>&1; then
24+
echo "error: Docker daemon is not reachable. Start Docker Desktop, then retry." >&2
25+
exit 1
26+
fi
27+
28+
if [[ -z "${OPENAI_API_KEY:-}" ]]; then
29+
echo "error: OPENAI_API_KEY is required for the default openai/gpt-5.5 run." >&2
30+
exit 1
31+
fi
32+
33+
if command -v harbor >/dev/null 2>&1; then
34+
HARBOR_CMD=(harbor)
35+
else
36+
HARBOR_CMD=(uv tool run harbor)
37+
fi
38+
39+
mkdir -p "$JOBS_DIR"
40+
41+
echo "Running Ouroboros Terminal-Bench 2.0 pilot"
42+
echo "repo: $REPO_ROOT"
43+
echo "jobs: $JOBS_DIR"
44+
echo "model: $OUROBOROS_TBENCH_MODEL"
45+
echo "reasoning: $OUROBOROS_TBENCH_REASONING"
46+
echo "max steps: $OUROBOROS_TBENCH_MAX_STEPS"
47+
echo "concurrency: $N_CONCURRENT"
48+
49+
cd "$REPO_ROOT"
50+
51+
PYTHONPATH="$SCRIPT_DIR${PYTHONPATH:+:$PYTHONPATH}" \
52+
"${HARBOR_CMD[@]}" run \
53+
--dataset terminal-bench@2.0 \
54+
--agent-import-path ouroboros_tbench_agent:OuroborosInstalledAgent \
55+
--n-concurrent "$N_CONCURRENT" \
56+
--jobs-dir "$JOBS_DIR" \
57+
"$@"

0 commit comments

Comments
 (0)