Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/headless/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ node packages/headless/harbor/run-terminal-bench-smoke.mjs --compare --task '*sq

`harbor/run-harness-ab.mjs` compares Maka with one pinned CLI under the same model, reasoning effort, task instructions, task order, account/provider route, and benchmark verifier while each agent retains its native runtime policy. A run resolves three named axes before creating its run root: benchmark (`MAKA_HARNESS_AB_BENCHMARK`), runtime (`MAKA_HARNESS_AB_RUNTIME`), and competitor harness (`MAKA_HARNESS_AB_COMPETITOR`). Runtime profiles own provider, model, reasoning, route, authentication mode, billing, and pricing; harness profiles own only the pinned CLI and adapter configuration. Omitting the runtime preserves existing behavior: Kimi Code and OpenCode use Kimi K3 Max, while Codex uses `gpt-5.6-sol` with `xhigh` reasoning. The A/B task order is frozen independently of Oracle evidence. Maka keeps active and stale tool-result pruning enabled while semantic compact is explicitly disabled in both the manifest and runtime environment.

The supported compositions are deliberately sparse: Terminal-Bench 2.1 supports Kimi with Kimi Code or OpenCode, GLM-5.2 or DeepSeek V4 Flash with OpenCode, and Codex with Codex; DeepSWE 1.1 supports Kimi with Kimi Code and Codex with Codex. The default `terminal-bench-2.1` benchmark binds the frozen 89-task Terminal-Bench 2.1 source to Harbor. The runner consumes an existing task tree whose ids and canonical fingerprint match the pinned revision; it does not download or duplicate that source. `deep-swe-1.1` binds the frozen 30-task discriminative subset to Pier (`pier` on `PATH`; the resolved `pier --version` is frozen into the toolchain and resume identity and recorded in the manifest), reads tasks from `~/.maka/eval/task-sources/deep-swe-6db64a40/tasks` by default, and grades with each task's own verifier. `deep-swe-1.1-full` is the sibling profile over the same pinned task source and executor, comparing the whole 113-task leaderboard set — the tree fingerprint is asserted over every discovered task dir, so a partial or modified checkout fails fast. `MAKA_HARNESS_AB_LIMIT=5` stays the operational canary for every benchmark; the full DeepSWE profiles are `30` and `113` respectively.
The supported compositions are deliberately sparse: Terminal-Bench 2.1 supports Kimi with Kimi Code or OpenCode, GLM-5.2 or DeepSeek V4 Flash with OpenCode, and Codex with Codex; DeepSWE 1.1 supports Kimi with Kimi Code, Codex with Codex, and — on the full-113 profile — DeepSeek V4 Flash with OpenCode. The default `terminal-bench-2.1` benchmark binds the frozen 89-task Terminal-Bench 2.1 source to Harbor. The runner consumes an existing task tree whose ids and canonical fingerprint match the pinned revision; it does not download or duplicate that source. `deep-swe-1.1` binds the frozen 30-task discriminative subset to Pier (`pier` on `PATH`; the resolved `pier --version` is frozen into the toolchain and resume identity and recorded in the manifest), reads tasks from `~/.maka/eval/task-sources/deep-swe-6db64a40/tasks` by default, and grades with each task's own verifier. `deep-swe-1.1-full` is the sibling profile over the same pinned task source and executor, comparing the whole 113-task leaderboard set — the tree fingerprint is asserted over every discovered task dir, so a partial or modified checkout fails fast. `MAKA_HARNESS_AB_LIMIT=5` stays the operational canary for every benchmark; the full DeepSWE profiles are `30` and `113` respectively. Pier benchmarks route in-container agents through a host credential proxy advertised as `host.docker.internal` by default; native Linux Docker injects no such name, so on a bare-metal VM set `MAKA_HARNESS_AB_PROVIDER_PROXY_ADVERTISED_HOST` to a docker-bridge-reachable host address (e.g. `172.17.0.1`).

Validate the frozen task source and preview the A/B plan without reading a key or starting Harbor:

Expand Down
2 changes: 2 additions & 0 deletions packages/headless/harbor/harness_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
_SYMBOL_MODULES = {
"BaseInstalledAgent": "agents.installed.base",
"Codex": "agents.installed.codex",
"OpenCode": "agents.installed.opencode",
"CliFlag": "agents.installed.base",
"NonZeroAgentExitCodeError": "agents.installed.base",
"with_prompt_template": "agents.installed.base",
"BaseEnvironment": "environments.base",
"AgentContext": "models.agent.context",
Expand Down
59 changes: 55 additions & 4 deletions packages/headless/harbor/opencode_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@
from pathlib import Path
from typing import Any

from harbor.agents.installed.base import NonZeroAgentExitCodeError, with_prompt_template
from harbor.agents.installed.opencode import OpenCode
from harbor.environments.base import BaseEnvironment
from harbor.models.agent.context import AgentContext
from harness_compat import (
AgentContext,
BaseEnvironment,
NetworkAllowlist as _NetworkAllowlist,
NonZeroAgentExitCodeError,
OpenCode,
with_prompt_template,
)
from provider_proxy import provider_proxy_endpoint, warn_if_pier_unreachable_proxy_port

from trial_pricing import estimate_cost, pricing_from_env

Expand All @@ -34,6 +39,29 @@ def name() -> str:
def get_version_command(self) -> str | None:
return f"{shlex.quote(str(_TOOLCHAIN_OPENCODE))} --version"

def install_spec(self) -> None:
# The pinned OpenCode toolchain is bind-mounted read-only and only
# verified (sha256 checksums + manifest fingerprint) in install().
# Pier's inherited spec would instead install OpenCode from the
# network, which offline tasks cannot reach and which would break the
# fixed-build comparison. None keeps the runtime verify path
# unchanged (Pier runs install() when no spec is preinstalled).
return None

def network_allowlist(self) -> _NetworkAllowlist | None:
# Called only under Pier; plain Harbor never calls it and
# harness_compat exports NetworkAllowlist = None there.
if _NetworkAllowlist is None:
return None
# The container runs the pinned OpenCode CLI against
# OPENCODE_CONFIG (opencode-benchmark.json), whose provider options
# point at MAKA_PROVIDER_PROXY_URL (see _run_with_stop_sentinel);
# that proxy host is the only egress the container needs. No fallback
# domain: a misconfigured trial fails here, at environment creation.
hostname, port = provider_proxy_endpoint(self._get_env, "OpenCode")
warn_if_pier_unreachable_proxy_port(port, "OpenCode")
return _NetworkAllowlist(domains=[hostname])

async def install(self, environment: BaseEnvironment) -> None:
expected_fingerprint = self._get_env("MAKA_OPENCODE_TOOLCHAIN_FINGERPRINT")
if not expected_fingerprint:
Expand Down Expand Up @@ -69,6 +97,10 @@ async def run(
self._started_at_ms = int(time.time() * 1000)
try:
await self._run_with_stop_sentinel(instruction, environment)
# Hydrate before reading error events: under Pier the explicit
# --mounts-json replaces the default /logs bind-mount, so the CLI
# stream only exists inside the container until downloaded.
await self._download_agent_logs(environment)
if messages := self._error_messages():
raise NonZeroAgentExitCodeError(
"OpenCode emitted error event(s): " + "; ".join(messages[:3])
Expand All @@ -78,6 +110,25 @@ async def run(
raise
finally:
self._finished_at_ms = int(time.time() * 1000)
await self._download_agent_logs(environment)

async def _download_agent_logs(self, environment: BaseEnvironment) -> None:
# _error_messages() and populate_context_post_run read opencode.txt
# from the host log dir. Under plain Harbor the agent log dir is
# bind-mounted, so the file is already host-side and is skipped; under
# Pier a --mounts-json run replaces the default log mounts while
# capabilities.mounted stays true (pier docker.py), so pier's own log
# download never runs — without this hydration a real provider error
# event is invisible to failure classification and the parsed
# trajectory/steps/token metadata come out empty. Best-effort, same
# contract as the Kimi and Codex arms.
local = self.logs_dir / "opencode.txt"
if local.exists():
return
try:
await environment.download_file("/logs/agent/opencode.txt", local)
except Exception as exc: # noqa: BLE001 - best-effort log hydration.
self.logger.debug("Could not download OpenCode stream %s: %s", local, exc)

def populate_context_post_run(self, context: AgentContext) -> None:
super().populate_context_post_run(context)
Expand Down
16 changes: 15 additions & 1 deletion packages/headless/harbor/run-harness-ab.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,21 @@ const SUPPORTED_HARNESS_COMPOSITIONS = new Set([
'deep-swe-1.1|openai-codex-gpt-5.6-sol-xhigh|codex',
'deep-swe-1.1-full|kimi-coding-plan-k3-max|kimi-code',
'deep-swe-1.1-full|openai-codex-gpt-5.6-sol-xhigh|codex',
'deep-swe-1.1-full|deepseek-v4-flash-max|opencode',
]);
const RESOLVED_HARNESS_COMPOSITIONS = new WeakSet();

/** Resolve host-proxy hub options from the harness environment. Pier's
* in-container agents dial the host credential proxy at an advertised host;
* the default (host.docker.internal) only exists on Docker Desktop, so a
* native-Linux VM must name a docker-bridge-reachable address explicitly. */
export function resolveHarnessProviderProxyHubOptions(
raw = process.env.MAKA_HARNESS_AB_PROVIDER_PROXY_ADVERTISED_HOST,
) {
const advertisedHost = raw?.trim();
return advertisedHost ? { providerProxyAdvertisedHost: advertisedHost } : {};
}

export function resolveHarnessCompetitorProfile(raw = 'kimi-code') {
const profile = HARNESS_COMPETITOR_PROFILES[raw];
if (!profile) {
Expand Down Expand Up @@ -936,7 +948,9 @@ async function runLocked({
const systemPromptPath = join(runRoot, 'prompts', 'default-system-prompt.txt');
const evaluatedTaskIds = new Set(evaluationTasks.map((task) => task.id));
const providerProxyHub =
benchmarkProfile.executor === 'pier' ? await createPierProviderProxyHub() : undefined;
benchmarkProfile.executor === 'pier'
? await createPierProviderProxyHub(resolveHarnessProviderProxyHubOptions())
: undefined;

const report = await runExperiment({
runRoot,
Expand Down
105 changes: 97 additions & 8 deletions packages/headless/harbor/tests/test_harness_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@
import io
import os
import sys
import asyncio
import tempfile
from pathlib import Path

_HARBOR_DIR = Path(__file__).resolve().parent.parent
if str(_HARBOR_DIR) not in sys.path:
sys.path.insert(0, str(_HARBOR_DIR))

_ADAPTER_MODULES = ("harness_compat", "maka_agent", "kimi_code_agent", "codex_agent")
_ADAPTER_MODULES = ("harness_compat", "maka_agent", "kimi_code_agent", "codex_agent", "opencode_agent")

# Sentinel returned by tests that cannot run under this interpreter; the
# __main__ runner counts these as skips, not passes. pytest treats the return
Expand Down Expand Up @@ -109,6 +111,7 @@ def _fresh_adapters():
importlib.import_module("maka_agent"),
importlib.import_module("kimi_code_agent"),
importlib.import_module("codex_agent"),
importlib.import_module("opencode_agent"),
)


Expand Down Expand Up @@ -158,8 +161,13 @@ def _restore_modules(saved_pier) -> None:
def test_pier_tree_selected_and_agent_info_is_pier_type():
if not _pier_gate():
return SKIPPED
maka_mod, kimi_mod, codex_mod = _fresh_adapters()
for cls in (maka_mod.MakaAgent, kimi_mod.MakaKimiCodeAgent, codex_mod.MakaCodexAgent):
maka_mod, kimi_mod, codex_mod, opencode_mod = _fresh_adapters()
for cls in (
maka_mod.MakaAgent,
kimi_mod.MakaKimiCodeAgent,
codex_mod.MakaCodexAgent,
opencode_mod.MakaOpenCodeAgent,
):
assert issubclass(cls, PierBaseInstalledAgent), cls.__mro__
agent = cls(logs_dir=Path("/tmp"), model_name="k3")
# The DeepSWE pilot failed pydantic validation because agent_info was a
Expand All @@ -177,7 +185,7 @@ def test_pier_tree_selected_and_agent_info_is_pier_type():
def test_maka_agent_network_policy_under_pier():
if not _pier_gate():
return SKIPPED
maka_mod, _, _ = _fresh_adapters()
maka_mod, _, _, _ = _fresh_adapters()

# Host-side LLM mode: model calls happen on the host, container is offline.
host_side = maka_mod.MakaAgent(
Expand Down Expand Up @@ -239,7 +247,7 @@ def test_maka_agent_network_policy_under_pier():
def test_kimi_agent_network_shape_under_pier():
if not _pier_gate():
return SKIPPED
_, kimi_mod, _ = _fresh_adapters()
_, kimi_mod, _, _ = _fresh_adapters()

# Portless HTTPS endpoint (default 443): allowlisted without any warning.
configured = kimi_mod.MakaKimiCodeAgent(
Expand Down Expand Up @@ -291,7 +299,7 @@ def test_kimi_agent_network_shape_under_pier():
def test_codex_agent_network_shape_under_pier():
if not _pier_gate():
return SKIPPED
_, _, codex_mod = _fresh_adapters()
_, _, codex_mod, _ = _fresh_adapters()

# The container runs the pinned Codex CLI against the maka-http provider
# config pointed at the host proxy; that proxy host is the single egress
Expand Down Expand Up @@ -330,13 +338,94 @@ def test_codex_agent_network_shape_under_pier():
)


@_isolated_maka_env
def test_opencode_agent_network_shape_under_pier():
if not _pier_gate():
return SKIPPED
_, _, _, opencode_mod = _fresh_adapters()

# The pinned toolchain is bind-mounted read-only and only verified in
# install(); Pier's inherited spec would reinstall OpenCode from the
# network, which offline tasks cannot reach and which would break the
# fixed-build comparison.
agent = opencode_mod.MakaOpenCodeAgent(
logs_dir=Path("/tmp"),
extra_env={"MAKA_PROVIDER_PROXY_URL": "http://host.docker.internal:443"},
)
assert agent.install_spec() is None

# The container dials only the host provider proxy (see
# _run_with_stop_sentinel); that proxy host is the single egress grant.
stderr = io.StringIO()
with contextlib.redirect_stderr(stderr):
allow = agent.network_allowlist()
assert isinstance(allow, NetworkAllowlist)
assert allow.domains == ["host.docker.internal"]
assert stderr.getvalue() == ""

# Squid-unreachable port: warn on stderr, still allowlist the host (same
# shared contract as the Kimi and Codex adapters).
proxied = opencode_mod.MakaOpenCodeAgent(
logs_dir=Path("/tmp"),
extra_env={"MAKA_PROVIDER_PROXY_URL": "https://proxy.internal:8443/v1"},
)
stderr = io.StringIO()
with contextlib.redirect_stderr(stderr):
proxied_allow = proxied.network_allowlist()
assert proxied_allow.domains == ["proxy.internal"]
assert "ports 80 and 443" in stderr.getvalue(), stderr.getvalue()

# No fallback domain: a missing proxy URL fails at allowlist time, same as
# the run path, instead of granting spurious egress.
unset = opencode_mod.MakaOpenCodeAgent(logs_dir=Path("/tmp"))
_raises(
unset.network_allowlist,
ValueError,
"OpenCode requires the host provider proxy",
)

@_isolated_maka_env
def test_opencode_agent_hydrates_container_log_to_host_logs_dir():
# Runs under both interpreters: pier replaces the default /logs bind-mount
# when the runner passes explicit --mounts-json (pier docker.py), so the
# host-side adapter must download the CLI stream before _error_messages()
# and populate_context_post_run() parse it; under plain Harbor the file is
# already host-side and the download is skipped.
_, _, _, opencode_mod = _fresh_adapters()

with tempfile.TemporaryDirectory() as tmp:
agent = opencode_mod.MakaOpenCodeAgent(logs_dir=Path(tmp))

written = {}

class _PierEnv:
async def download_file(self, remote, local):
written[remote] = Path(local)
Path(local).write_text('{"type":"step_finish"}\n')

asyncio.run(agent._download_agent_logs(_PierEnv()))
assert written == {"/logs/agent/opencode.txt": Path(tmp) / "opencode.txt"}

# Idempotent: an existing host-side log (plain-Harbor bind mount, or a
# repeated call from run()'s finally) is never overwritten.
calls = []

class _HarborEnv:
async def download_file(self, remote, local):
calls.append(remote)

asyncio.run(agent._download_agent_logs(_HarborEnv()))
assert calls == []
assert (Path(tmp) / "opencode.txt").read_text() == '{"type":"step_finish"}\n'


@_isolated_maka_env
def test_kimi_runtime_env_forwards_ipv6_proxy_url():
# Runs under BOTH trees (no pier gate): _runtime_env keeps main's plain-
# Harbor contract and forwards the proxy URL opaquely, IPv6 included; the
# IPv6 rejection is a Pier NetworkAllowlist constraint and lives only in
# the network_allowlist() path.
_, kimi_mod, _ = _fresh_adapters()
_, kimi_mod, _, _ = _fresh_adapters()
agent = kimi_mod.MakaKimiCodeAgent(
logs_dir=Path("/tmp"),
extra_env={
Expand All @@ -360,7 +449,7 @@ def test_codex_agent_hydrates_agent_logs_best_effort():
import asyncio
import tempfile

_, _, codex_mod = _fresh_adapters()
_, _, codex_mod, _ = _fresh_adapters()

class _RecordingEnvironment:
def __init__(self) -> None:
Expand Down
3 changes: 3 additions & 0 deletions packages/headless/src/__tests__/harbor-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3691,6 +3691,9 @@ class OpenCode:
self.model_name = kwargs.get("model_name") or "openai/mimo-v2.5-pro"
self._instruction = None
self._prompt_template_path = Path(prompt_template_path) if prompt_template_path else None
# Real harbor/pier bases set self.logger in __init__; the adapter's
# best-effort log hydration logs through it.
self.logger = types.SimpleNamespace(debug=lambda *args, **kwargs: None)

@staticmethod
def name():
Expand Down
33 changes: 33 additions & 0 deletions packages/headless/src/__tests__/harness-ab-cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,39 @@ test('harness A/B CLI rejects modified task contents before reading credentials'
}
});

test('harness A/B forwards an explicit host-proxy advertised host for native-Linux Pier runs', async () => {
const { resolveHarnessProviderProxyHubOptions } = await import(
new URL('../../harbor/run-harness-ab.mjs', import.meta.url).href
);
// Native Linux Docker injects no host.docker.internal; the VM operator
// names the docker-bridge-reachable host address explicitly.
assert.deepEqual(resolveHarnessProviderProxyHubOptions('172.17.0.1'), {
providerProxyAdvertisedHost: '172.17.0.1',
});
// Unset or blank keeps the hub default (host.docker.internal).
assert.deepEqual(resolveHarnessProviderProxyHubOptions(undefined), {});
assert.deepEqual(resolveHarnessProviderProxyHubOptions(' '), {});
});

test('harness A/B supports the DeepSeek-metered OpenCode composition on full DeepSWE', async () => {
const { resolveHarnessAbRunId, resolveHarnessComposition } = await import(
new URL('../../harbor/run-harness-ab.mjs', import.meta.url).href
);
const composition = resolveHarnessComposition({
benchmark: 'deep-swe-1.1-full',
runtime: 'deepseek-v4-flash-max',
competitor: 'opencode',
});
assert.equal(composition.benchmarkProfile.executor, 'pier');
assert.equal(composition.runtimeProfile.model, 'deepseek-v4-flash');
assert.equal(composition.runtimeProfile.billingMode, 'metered');
assert.equal(composition.competitorProfile.id, 'opencode');
assert.equal(
resolveHarnessAbRunId(composition),
'deepseek-v4-flash-maka-vs-opencode-deepswe-full-v1',
);
});

test('harness A/B resolves the full DeepSWE benchmark as a sibling profile', async () => {
const {
resolveHarnessAbRunId,
Expand Down
Loading
Loading