Meta-Harness
A minimal outer loop that delegates selection AND mutation to a skill-steered proposer over an append-only candidate history, returning a scalar or task-configured Pareto frontier.
"""Coding-agent adapters for the Meta-Harness ``coding_agent`` proposer.
The reference proposer is a *coding agent* — an LLM wrapped in a tool-use loop with filesystem
access — that BROWSES the on-disk archive D (grep/cat) and writes new candidate files. The
reference uses Claude Code, but any headless coding-agent CLI that can (a) read files under a
working dir and (b) write files there works the same way, because the whole contract is prompt-
driven: "read D, write ``candidates/<name>.py`` + ``pending_eval.json``". This module abstracts the
launch behind a small adapter so the agent is a config choice (``proposer.coding_agent``).
Each adapter knows only three agent-specific things: the binary, how to pass the SKILL.md steering
(a real ``--append-system-prompt`` flag, or — for agents without one — prepended to the prompt),
and the headless/full-access flags. The archive D, the task prompt, and the ``pending_eval.json``
contract are identical across agents.
Adapters:
* ``claude_code`` — Claude Code CLI (``claude -p``). Subscription-billed, native ``--append-
system-prompt``. Fully wired; the default. Reuses its native-trace config layout while scoping
subscription auth to each agent session (API keys stripped so a run can never bill the API).
* ``codex`` — OpenAI Codex CLI (``codex exec``). Built from the real ``codex exec`` flags
(``-C`` cwd, ``--sandbox``/``--dangerously-bypass-approvals-and-sandbox``, ``-m`` model, ``-c
model_reasoning_effort=…``); no system-prompt flag, so the skill is prepended to the prompt.
EXPERIMENTAL — uses Codex's own auth (``codex login`` / ``OPENAI_API_KEY``); not run-tested here.
* ``cursor_agent`` — Cursor Agent CLI (``cursor-agent -p``). EXPERIMENTAL and best-effort: the
binary is not always installed, so selecting it without ``cursor-agent`` on PATH fails fast with
an install hint. Skill prepended to the prompt.
To add another agent: subclass :class:`CodingAgentAdapter`, implement :meth:`spec`, and register it
in :data:`ADAPTERS`.
"""
from __future__ import annotations
import base64
import json
import logging
import os
import subprocess
from dataclasses import dataclass, field
from pathlib import Path
from ..claude_code.scaffold import _kill_proc_group, _prepare_claude_config_dir
log = logging.getLogger(__name__)
_CLAUDE_MODEL_PREFIXES = ("claude-", "sonnet", "opus", "haiku")
_DISALLOWED_CLAUDE_TOOLS = "AskUserQuestion,EnterPlanMode,ExitPlanMode"
# codex `model_reasoning_effort` accepts these; map max/xhigh down to the top codex level
_CODEX_EFFORT = {"minimal": "minimal", "low": "low", "medium": "medium", "high": "high",
"xhigh": "high", "max": "high"}
_CLAUDE_AUTH_FILE_ENV = "GALAPAGOS_CLAUDE_AUTH_FILE"
@dataclass
class LaunchSpec:
"""Everything :func:`run_agent` needs to launch one headless coding-agent session at ``cwd=D``."""
argv: list[str]
env: dict = field(default_factory=dict)
log_name: str = "agent.log"
class CodingAgentAdapter:
"""One headless coding-agent CLI. Subclasses supply :attr:`name`/:attr:`binary`/:meth:`spec`."""
name: str = "base"
binary: str = ""
experimental: bool = False
install_hint: str = ""
def resolve_binary(self) -> str:
import shutil
path = shutil.which(self.binary)
if path is None:
raise RuntimeError(
f"meta_harness proposer.coding_agent={self.name!r} needs the {self.binary!r} CLI on "
f"PATH{(' — ' + self.install_hint) if self.install_hint else ''}. "
"Install it, pick a different proposer.coding_agent, or set proposer.mode: llm.")
return path
def spec(self, d_dir: Path, system_text: str, user_prompt: str, *, model: str | None,
effort: str | None, max_turns: int | None) -> LaunchSpec:
raise NotImplementedError
def launch(self, d_dir: Path, system_text: str, user_prompt: str, *, model: str | None,
effort: str | None, max_turns: int | None, timeout: int,
log_name: str | None = None) -> int:
spec = self.spec(d_dir, system_text, user_prompt, model=model, effort=effort,
max_turns=max_turns)
if log_name:
spec.log_name = log_name
return run_agent(spec, d_dir, timeout)
def run_agent(spec: LaunchSpec, d_dir: Path, timeout: int) -> int:
"""Run one agent session (stdout+stderr → ``d_dir/<log_name>``), killing the whole process group
on timeout. Returns the exit code (124 on timeout)."""
log_path = d_dir / spec.log_name
log_path.parent.mkdir(parents=True, exist_ok=True)
with open(log_path, "w") as log_fh:
proc = subprocess.Popen(spec.argv, stdin=subprocess.DEVNULL, stdout=log_fh,
stderr=subprocess.STDOUT, cwd=str(d_dir), env=spec.env,
start_new_session=True)
try:
return proc.wait(timeout=timeout)
except subprocess.TimeoutExpired:
_kill_proc_group(proc)
proc.wait()
log.warning("coding-agent session timed out after %ss", timeout)
return 124
# ---- claude_code (the default; fully wired) ------------------------------------------------------
class ClaudeCodeAdapter(CodingAgentAdapter):
name = "claude_code"
binary = "claude"
install_hint = "npm install -g @anthropic-ai/claude-code (subscription: `claude setup-token`)"
def __init__(self):
# One adapter instance belongs to one proposer/run (get_adapter returns a fresh instance).
# Container auth is consumed from a short-lived bootstrap file once, then retained only in
# controller memory so later rounds need no persistent secret file or PID-1 environment value.
self._auth_loaded = False
self._oauth_token: str | None = None
self._credential_bytes: bytes | None = None
def _load_subscription_auth(self, env: dict) -> None:
"""Consume subscription auth once without leaving the bootstrap secret on disk.
The outer Docker orchestrator stages a mode-0600 JSON file *after* container start. Its path is
non-secret; its contents are removed immediately after this method reads them. Host mode falls
back to the normal OAuth env var or ``~/.claude/.credentials.json``.
"""
if self._auth_loaded:
return
bootstrap_raw = env.pop(_CLAUDE_AUTH_FILE_ENV, None)
if bootstrap_raw:
bootstrap = Path(bootstrap_raw)
try:
payload = json.loads(bootstrap.read_text(encoding="utf-8"))
kind = payload.get("kind")
if kind == "oauth-token":
self._oauth_token = str(payload.get("value") or "").strip()
if not self._oauth_token:
raise ValueError("empty OAuth token")
elif kind == "login-credentials":
self._credential_bytes = base64.b64decode(str(payload.get("value") or ""),
validate=True)
if not self._credential_bytes:
raise ValueError("empty credentials payload")
else:
raise ValueError(f"unknown auth kind {kind!r}")
except Exception as exc:
raise RuntimeError(f"invalid Meta-Harness Claude auth bootstrap: {exc}") from exc
finally:
try:
bootstrap.unlink()
except OSError:
pass
else:
token = (env.pop("CLAUDE_CODE_OAUTH_TOKEN", None) or "").strip()
if token:
self._oauth_token = token
else:
source_raw = env.pop("GALAPAGOS_CLAUDE_CREDENTIALS_FILE", None)
source = Path(source_raw) if source_raw else Path.home() / ".claude" / ".credentials.json"
if source.is_file():
self._credential_bytes = source.read_bytes()
else:
raise RuntimeError(
"meta_harness proposer.coding_agent='claude_code' needs subscription auth: "
"set CLAUDE_CODE_OAUTH_TOKEN (`claude setup-token`) or sign in with "
"`claude` + /login")
self._auth_loaded = True
def _setup_session_auth(self, env: dict, claude_config_dir: Path) -> None:
for var in ("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"):
env.pop(var, None)
self._load_subscription_auth(env)
if self._oauth_token:
env["CLAUDE_CODE_OAUTH_TOKEN"] = self._oauth_token
return
target = claude_config_dir / ".credentials.json"
target.write_bytes(self._credential_bytes or b"")
target.chmod(0o600)
def spec(self, d_dir, system_text, user_prompt, *, model, effort, max_turns) -> LaunchSpec:
if model and not any(model.startswith(p) for p in _CLAUDE_MODEL_PREFIXES):
raise ValueError(f"claude_code drives Claude models, got {model!r}; use a "
"claude-*/sonnet/opus/haiku name (or null for the CLI default)")
cli = self.resolve_binary()
# Harbor-style native traces plus session-scoped subscription auth: the trace directory
# persists, while the credential file exists only for the duration of this launch.
claude_config_dir = _prepare_claude_config_dir(d_dir)
env = dict(os.environ)
env["CLAUDE_CONFIG_DIR"] = str(claude_config_dir)
self._setup_session_auth(env, claude_config_dir)
argv = [cli, "-p", user_prompt,
"--dangerously-skip-permissions", "--disallowedTools", _DISALLOWED_CLAUDE_TOOLS,
"--output-format", "stream-json", "--verbose"]
if system_text: # no-skill mode passes "" — omit the flag entirely
argv[3:3] = ["--append-system-prompt", system_text]
if max_turns is not None:
argv += ["--max-turns", str(int(max_turns))]
if model:
argv += ["--model", model]
if effort:
argv += ["--effort", effort] # claude accepts low|medium|high|xhigh|max
return LaunchSpec(argv=argv, env=env, log_name="claude.log")
def launch(self, d_dir: Path, system_text: str, user_prompt: str, *, model: str | None,
effort: str | None, max_turns: int | None, timeout: int,
log_name: str | None = None) -> int:
try:
return super().launch(
d_dir, system_text, user_prompt, model=model, effort=effort,
max_turns=max_turns, timeout=timeout, log_name=log_name)
finally:
# Native traces stay in D; subscription credentials never do. This also runs when binary
# resolution, process launch, timeout handling, or the agent itself fails.
credentials = d_dir / "claude_sessions" / ".credentials.json"
try:
credentials.unlink()
except OSError:
pass
# ---- codex (experimental; built from the real `codex exec` flags) --------------------------------
class CodexAdapter(CodingAgentAdapter):
name = "codex"
binary = "codex"
experimental = True
install_hint = "npm install -g @openai/codex (auth: `codex login` or OPENAI_API_KEY)"
def spec(self, d_dir, system_text, user_prompt, *, model, effort, max_turns) -> LaunchSpec:
cli = self.resolve_binary()
env = dict(os.environ) # Codex resolves its own auth (~/.codex login or OPENAI_API_KEY)
# Codex has no system-prompt flag → prepend the skill steering to the prompt.
prompt = f"{system_text}\n\n{user_prompt}" if system_text else user_prompt
argv = [cli, "exec", prompt, "-C", str(d_dir), "--skip-git-repo-check",
"--dangerously-bypass-approvals-and-sandbox"]
if model:
argv += ["-m", model]
if effort:
argv += ["-c", f'model_reasoning_effort="{_CODEX_EFFORT.get(effort, "high")}"']
return LaunchSpec(argv=argv, env=env, log_name="codex.log")
# ---- cursor_agent (experimental; gated on availability) ------------------------------------------
class CursorAgentAdapter(CodingAgentAdapter):
name = "cursor_agent"
binary = "cursor-agent"
experimental = True
install_hint = "install Cursor's agent CLI (`cursor-agent`); auth via `cursor-agent login`"
def spec(self, d_dir, system_text, user_prompt, *, model, effort, max_turns) -> LaunchSpec:
cli = self.resolve_binary()
env = dict(os.environ)
prompt = f"{system_text}\n\n{user_prompt}" if system_text else user_prompt
# Cursor Agent headless print mode with full-access (flags may change across versions —
# isolated here for easy adjustment).
argv = [cli, "-p", prompt, "--force"]
if model:
argv += ["--model", model]
return LaunchSpec(argv=argv, env=env, log_name="cursor.log")
ADAPTERS: dict[str, type[CodingAgentAdapter]] = {
adapter.name: adapter for adapter in (ClaudeCodeAdapter, CodexAdapter, CursorAgentAdapter)
}
def get_adapter(name: str) -> CodingAgentAdapter:
"""Resolve a coding-agent adapter by ``proposer.coding_agent`` name (raises on unknown)."""
try:
return ADAPTERS[name]()
except KeyError:
raise ValueError(
f"unknown proposer.coding_agent {name!r}; available: {', '.join(sorted(ADAPTERS))}")