Skip to content

feat(v1): add prime-agent harness over native ACP via the published installer - #2296

Draft
mikasenghaas wants to merge 2 commits into
mainfrom
feat/prime-agent-harness-installer
Draft

feat(v1): add prime-agent harness over native ACP via the published installer#2296
mikasenghaas wants to merge 2 commits into
mainfrom
feat/prime-agent-harness-installer

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Add a prime-agent v1 harness driving Prime Agent's native ACP mode (prime-agent --mode acp), registered as harness="prime-agent".
  • Install through the published installer (curl -fsSL .../install.sh | sh): PRIME_AGENT_VERSION selects the release — the config exposes a single version knob (default 0.7.0) — and the installer verifies the published artifact itself. NPM_CONFIG_PREFIX keys the shared install per version under /var/tmp/vf-prime-agent/<version>; ensure_node supplies node (and curl on bare VM images, e.g. prime).
  • Model traffic rides interception as an OpenAI-compatible provider. models.json carries the env-var name for the API key (Prime Agent resolves process.env[apiKey] || apiKey), so the interception secret never reaches disk.
  • One live ACP process per trace via session() keeps a single Prime Agent session — and with it one IPython kernel — alive across interaction turns (the agent refuses a second session/new). Per-trace HOME/TMPDIR/daemon socket live under a hashed state root; cleanup stops the trace daemon and removes only that trace's state.
  • SUPPORTS_MCP = False: native ACP mode ignores client-provided MCP servers, so tool-bearing tasksets fail pairing up front instead of running with tools silently absent. Skills pass via --skill; the system prompt is passed once per launch via --append-system-prompt so resumed segments are not re-instructed.
  • Fix in the shared ACP session layer: a failed session/prompt now returns the segment's exit status instead of raising, so _check_result forgives it when the rollout already stopped (limit or @stop). Previously a mid-segment max_turns stop surfaced as HarnessError for any ACP session harness whose agent errors its prompt on the refused model call.

Related: #2254 and #2285 approach the same integration by installing the release tarball directly; this PR installs through the published installer and also fixes the stopped-rollout path in the shared ACP session layer.

Verification

All runs on prime VM sandboxes (--env.agent.runtime.type prime --env.agent.runtime.vm true) with openai/gpt-5.6-luna:

  • prime-agent ↔ prime-agent multi-turn exchange via agent.interaction(): clean 2-turn conversations on both sides, user_closed, no errors.
  • eval gsm8k-v1 (single-turn): 2/2 reward 1.00 — run.
  • eval kuhn-poker-v1 with both seats on prime-agent: interleaved interactions, no forfeits, clean zero-sum payoffs — run.
  • eval terminal-bench-2-v1 (harbor, per-task images) with --env.agent.max-turns 20: before the ACP fix both tasks errored with HarnessError on hitting the turn cap (run); after, 2/2 clean max_turns stops with no errors (run).
  • uv run ruff check, uv run ruff format --check, uv run pytest tests/v1 -m "not e2e" (72 passed). Pushed with --no-verify: the pre-push hooks run uv run --locked and fail on the known-stale uv.lock on main; uv.lock is untouched here.

🤖 Generated with Claude Code

Note

Add PrimeAgentHarness to run Prime Agent in ACP mode via published installer

  • Adds PrimeAgentHarness and PrimeAgentHarnessConfig (defaulting to version 0.7.0), exported from verifiers.v1.harnesses.
  • At setup time, installs the pinned Prime Agent version under PRIME_AGENT_DIR using a locked installer script, then initializes the ACP layer.
  • Per-trace isolation is achieved by hashing the trace ID (SHA-256, 16 hex chars) to derive a unique state root; each trace gets its own directories, models.json, and an executable wrapper that launches prime-agent in ACP mode.
  • For runtimes with live process support, sessions reuse a per-trace daemon and kernel across turns; otherwise falls back to single-shot launch execution.
  • Cleanup stops the per-trace daemon via the binary and removes the state directory.
  • Behavioral Change: failed ACP prompt operations now return ProgramResult(exit_code=1) with stderr detail instead of raising a RuntimeError.
📊 Macroscope summarized dfd49a0. 4 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

mikasenghaas and others added 2 commits August 6, 2026 17:36
…nstaller

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# One live ACP process per trace keeps a single Prime Agent session — and
# with it one IPython kernel — alive across turns: the agent refuses a
# second `session/new`, so a relaunch per segment cannot preserve state.
if not runtime.supports_live_processes:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High prime_agent/harness.py:111

The supports_live_processes == False fallback calls super().session(...), whose subsequent turns relaunch launch()_prepare(), reusing the same per-trace daemon socket. Prime Agent refuses a second session/new, so any multi-turn interaction on such a runtime fails on its second turn. Consider either rejecting these runtimes outright in setup or routing the fallback through a per-turn socket so no stale daemon is reused.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @verifiers/v1/harnesses/prime_agent/harness.py around line 111:

The `supports_live_processes == False` fallback calls `super().session(...)`, whose subsequent turns relaunch `launch()` → `_prepare()`, reusing the same per-trace daemon socket. Prime Agent refuses a second `session/new`, so any multi-turn interaction on such a runtime fails on its second turn. Consider either rejecting these runtimes outright in `setup` or routing the fallback through a per-turn socket so no stale daemon is reused.

root = self._root(trace)
# Stop this trace's daemon before deleting its state: a live worker would
# keep writing into a removed directory.
stopped = await runtime.run(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High prime_agent/harness.py:250

cleanup() runs prime-agent stop via self._bin() without adding NODE_BIN_DIR to PATH, unlike the launch wrapper in _prepare(). On images where Node lives only under /var/tmp/vf-node/bin, the stop command cannot find node, so cleanup logs a warning and runs rm -rf on the state directory while the daemon is still alive — leaking the process and leaving it to write into a removed directory. Invoke stop through the same PATH (or the prepared wrapper) that launches use.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @verifiers/v1/harnesses/prime_agent/harness.py around line 250:

`cleanup()` runs `prime-agent stop` via `self._bin()` without adding `NODE_BIN_DIR` to `PATH`, unlike the launch wrapper in `_prepare()`. On images where Node lives only under `/var/tmp/vf-node/bin`, the stop command cannot find `node`, so cleanup logs a warning and runs `rm -rf` on the state directory while the daemon is still alive — leaking the process and leaving it to write into a removed directory. Invoke stop through the same `PATH` (or the prepared wrapper) that launches use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant