Skip to content

Add native ACP harness continuation#2046

Merged
hallerite merged 3 commits into
feat/agent-chatfrom
agent/acp-user-simulation
Jul 22, 2026
Merged

Add native ACP harness continuation#2046
hallerite merged 3 commits into
feat/agent-chatfrom
agent/acp-user-simulation

Conversation

@xeophon

@xeophon xeophon commented Jul 16, 2026

Copy link
Copy Markdown
Member

Overview

Adds user-simulation continuation across v1 harnesses on top of the unified agent chat exchange in #2049, using native ACP only where the underlying agent supports it.

Details

  • Exposes vf.ACP as the public native ACP transport and surfaces the ACP-visible agent response through ChatSession.
  • Uses the upstream pi-acp adapter for persistent Pi sessions, with full-transcript recovery when the session marker is missing.
  • Keeps Kimi Code and RLM as explicit stateless transcript replays, while Compact remains direct so its process-local state is preserved.
  • Installs Pi and its adapters from npm with a compatible Node.js runtime on Debian and Alpine.
  • Keeps existing harness launch overrides compatible with the parent exchange API.

Note

Add native ACP harness continuation for Kimi, Pi, Pool, and RLM agents

  • Introduces a new ACP class and runner script that executes ACP agents, captures streaming assistant text as visible_reply, handles session resume, and auto-approves permissions.
  • Migrates Kimi, Pi, and Pool harnesses to run via the ACP adapter instead of direct --print mode, enabling session handling and structured reply capture.
  • Adds visible_reply: str | None to ProgramResult so ACP-based harnesses can surface assistant output to callers.
  • Harness.run now returns ProgramResult and only raises HarnessError on non-zero exit when no stop condition is set; RolloutRun.step now returns ProgramResult | None instead of bool.
  • All launch handlers now accept data: TaskData and resolve prompts via self.resolve_prompt(data) rather than reading directly from trace.task.data.
  • Behavioral Change: Pi and Pool harnesses no longer invoke agents in direct print mode; existing rollouts relying on step() returning bool will break.

Macroscope summarized 2280387.


Note

Medium Risk
Switches several agent harnesses from print-mode CLIs to ACP/session semantics and changes step()'s return type—multi-turn rollouts and callers of step() may behave differently; install paths now pull npm/Node and external agent binaries.

Overview
Introduces vf.ACP as the shared Agent Client Protocol runner (uv script + session resume, MCP wiring, streamed assistant text as visible_reply on ProgramResult). Kimi Code, Pi, and Pool no longer use one-shot print/exec modes—they launch native acp subprocesses with per-rollout session files so Agent.chat() can continue exchanges via the default Harness.resume relaunch (Pi via pi-acp, npm/Node 22.19+ install).

RLM opts into SUPPORTS_MESSAGE_PROMPT and JSON-serializes Messages prompts for stateless transcript replay; compact/claude launch paths take TaskData and resolve_prompt(data). Harness.run returns ProgramResult; RolloutRun.step returns ProgramResult | None (replacing bool), and ChatSession.turn prefers visible_reply over trace text when present.

Reviewed by Cursor Bugbot for commit 2280387. Bugbot is set up for automated code reviews on this repo. Configure here.

@xeophon xeophon changed the title Add ACP-backed user simulation to MCP harnesses Add ACP Jul 16, 2026
@xeophon xeophon changed the title Add ACP Add ACP-backed user simulation Jul 16, 2026
@xeophon
xeophon force-pushed the agent/acp-user-simulation branch from 7bbb979 to a20343f Compare July 17, 2026 13:44
@xeophon xeophon changed the title Add ACP-backed user simulation Add ACP-backed harness continuation Jul 17, 2026
@xeophon
xeophon changed the base branch from main to feat/agent-chat July 17, 2026 13:45
@hallerite
hallerite force-pushed the feat/agent-chat branch 9 times, most recently from 19308e0 to 4d8f9a3 Compare July 21, 2026 22:46
@xeophon
xeophon force-pushed the agent/acp-user-simulation branch from a20343f to 5d6c2be Compare July 22, 2026 09:04
@xeophon
xeophon marked this pull request as ready for review July 22, 2026 09:07
Comment thread environments/compact/compact/harness.py Outdated
@xeophon
xeophon force-pushed the agent/acp-user-simulation branch 2 times, most recently from 2562cfb to 3a4ab43 Compare July 22, 2026 09:14
if ! command -v curl >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
{ apt-get update -qq && apt-get install -y -qq curl ca-certificates nodejs npm >/dev/null; } \
|| apk add --no-cache curl ca-certificates nodejs npm >/dev/null
if [ -f /etc/alpine-release ]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium pi/harness.py:45

On Alpine, the install script uses the distribution's unpinned nodejs package and then aborts if it is older than 22.19, instead of downloading the pinned VF_PI_NODE_VERSION like the non-Alpine branch does. On Alpine images whose repository ships an older Node, setup fails with "Pi requires Node.js 22.19 or newer" rather than installing the required version. Consider applying the same pinned-Node download logic to the Alpine branch instead of relying on the system package.

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

On Alpine, the install script uses the distribution's unpinned `nodejs` package and then aborts if it is older than 22.19, instead of downloading the pinned `VF_PI_NODE_VERSION` like the non-Alpine branch does. On Alpine images whose repository ships an older Node, `setup` fails with "Pi requires Node.js 22.19 or newer" rather than installing the required version. Consider applying the same pinned-Node download logic to the Alpine branch instead of relying on the system package.

Comment thread verifiers/v1/harnesses/pi/harness.py Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 22, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

Unable to check for correctness in 2280387. This PR introduces a new ACP protocol feature affecting multiple harnesses with significant new logic. Multiple unresolved review comments identify potential bugs including a P1 issue with message ID handling that could break ACP harness segments.

You can customize Macroscope's approvability policy. Learn more.

@hallerite
hallerite force-pushed the feat/agent-chat branch 2 times, most recently from 3c8abb2 to 388137c Compare July 22, 2026 10:44
@xeophon
xeophon force-pushed the agent/acp-user-simulation branch from 3a4ab43 to ea18c95 Compare July 22, 2026 12:22
@xeophon xeophon changed the title Add ACP-backed harness continuation Add native ACP harness continuation Jul 22, 2026
Comment thread verifiers/v1/harnesses/pi/harness.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea18c958c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

mcp_args: list[str] = []
restore_home = f'export HOME="${HOME_VAR}"; unset {HOME_VAR}; '

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore Pi's original HOME through the variable

When a Pi rollout has no MCP servers, this wrapper prologue is emitted before exec pi ..., but it sets HOME to the literal string VF_PI_ORIGINAL_HOME rather than the value saved by ACP_COMMAND before it rewrites HOME to the agent dir. That makes every non-MCP Pi segment run with a bogus relative home, so Pi and model-executed commands that rely on home-scoped config/cache/state no longer see the runtime's real home; use $VF_PI_ORIGINAL_HOME here instead.

Useful? React with 👍 / 👎.

name: {"disabled": True} for name in self.config.disabled_tools or []
},
}
await runtime.write(SETTINGS_PATH, json.dumps(settings).encode())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pool decline exit unhandled

Medium Severity

The Pool harness no longer maps exit code 4 to success after switching to pool acp via POOL_ACP.run. When the agent declines a task with that code, Harness.run now treats a non-zero exit as failure and raises HarnessError instead of returning a successful ProgramResult.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9d03dbbce1cc468f7cd7eb38b6223cad483b1c01. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d03dbbce1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread verifiers/v1/acp/_runner.py Outdated
update.content, TextContentBlock
):
return
if update.message_id is not None and update.message_id != self.message_id:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Tolerate ACP chunks without message IDs

ACP v1 text chunks from the pinned agent-client-protocol==0.11.0 schema do not expose message_id, so the first normal AgentMessageChunk delivered by Pool/Kimi/Pi causes this callback to raise AttributeError before visible_reply is accumulated. In that case connection.prompt() fails and every native ACP harness segment errors out as soon as the agent streams text; use getattr(update, "message_id", None) or otherwise treat a missing ID as a single message.

Useful? React with 👍 / 👎.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9f1192f. Configure here.

Comment thread environments/compact/compact/harness.py
@xeophon
xeophon force-pushed the agent/acp-user-simulation branch from 9f1192f to 2280387 Compare July 22, 2026 14:42

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2280387cdb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
prompt_path = f"{agent_dir}/prompt.txt"
await runtime.write(prompt_path, prompt.encode())
await runtime.write(f"{agent_dir}/models.json", json.dumps(models).encode())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Suppress Pi ACP startup chunks

When starting a new Pi ACP session, pi-acp emits its default startup-info block as an agent_message_chunk immediately after session/new; because this harness never writes Pi settings to set quietStartup, that out-of-band chunk can be captured by the generic ACP runner as the segment's visible_reply. In Agent.chat() this makes the first Pi reply include the startup banner (or even satisfy the “produced a reply” check) instead of just the model's answer, so Pi-driven multi-turn/user-sim flows receive polluted assistant text.

Useful? React with 👍 / 👎.

@hallerite
hallerite merged commit 4d58eda into main Jul 22, 2026
7 checks passed
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.

2 participants