ACP Agents
fast-rlm can drive a coding agent that speaks the Agent Client Protocol (ACP) — Claude Code, Codex, opencode, and others — as the model behind a run.
For Claude Code, Codex and opencode, prefer cli:
CLI agents drive those three through their own non-interactive modes. No install step, no Node/npx, no bridge package to fall behind — and token/cost budgets actually work. ACP remains the route for any other agent that speaks the protocol, and stays fully supported.
Installation (opt-in)
ACP support is not shipped with fast-rlm. It pulls two npm dependency trees (the ACP provider and the Vercel AI SDK) and needs Node/npx for each agent's bridge package — overhead that the majority of runs, which use a plain API model, should not pay. The engine imports it dynamically, so a non-ACP run never resolves either package.
Install it once:
fast-rlm acp install # before your first acp: run
fast-rlm acp status # installed versions + available upgrades
fast-rlm acp install -u # move to the latest versions
Selecting an acp: agent without installing fails immediately with an
actionable message rather than a module-resolution error from inside the engine.
Version pinning, and why upgrades are yours
acp install resolves the current packages and records them in
~/.fast_rlm/acp.json (override the location with FAST_RLM_HOME):
{
"marker_version": 1,
"provider_version": "0.3.5",
"ai_sdk_version": "6.0.264",
"provider_specifier": "npm:@mcpc-tech/acp-ai-provider@0.3.5",
"ai_sdk_specifier": "npm:ai@6.0.264",
"bridges": { "claude-code": "0.16.2", "codex": "0.16.0" },
"bridge_packages": {
"claude-code": "@zed-industries/claude-code-acp",
"codex": "@zed-industries/codex-acp"
}
}
Runs use exactly these versions — bridges are spawned as npx -y <pkg>@<version>
rather than bare <pkg>, so a run is reproducible instead of silently picking up
whatever npx last fetched.
This is the fix for fast-rlm lagging the ACP ecosystem. The versions are user
data, not repo constants, so fast-rlm acp install -u picks up a newer ACP
provider or bridge with no fast-rlm release involved. A repeat install
without -u re-caches the recorded versions — a repair, never a silent upgrade.
Two details worth knowing:
- The AI SDK version is derived from the ACP provider, never resolved
independently. The provider currently depends on
ai@^6whileai@7is the latest release, so resolving both to "latest" would install an incompatible pair.acp installreads the provider's declared range and takes the newest release inside it. - If a bridge is renamed upstream (as
@zed-industries/claude-code-acpwas, to@agentclientprotocol/claude-agent-acp), editbridge_packagesin the marker. The package name is marker data too, so following a rename needs no release either.
How it works
The ACP agent is treated as a drop-in model, exactly like an OpenAI-compatible or Vertex model:
- fast-rlm sends the agent its system prompt and the message history.
- The agent replies with a
```replcode block. - fast-rlm executes that block in its own Pyodide sandbox and feeds the output back on the next turn.
The agent runs read-only — it never executes the code or writes files itself. All execution happens inside fast-rlm's sandbox.
Selecting an agent
Use an acp: prefix on primary_agent / sub_agent (the same idea as the
vertex/ prefix):
primary_agent: "acp:claude-code"
sub_agent: "acp:codex?model=gpt-5.5-codex" # ?model= override is optional
from fast_rlm import run, RLMConfig
run("What is 2+2?", config=RLMConfig(primary_agent="acp:opencode"))
Built-in presets
acp: name |
Launches | Read-only mode |
|---|---|---|
acp:claude-code |
npx -y @zed-industries/claude-code-acp@<pinned> |
plan (hard block) |
acp:codex |
npx -y @zed-industries/codex-acp@<pinned> |
read-only (approval-gated) |
acp:opencode |
opencode acp |
plan (hard block) |
<pinned> is the version fast-rlm acp install recorded in your marker.
Prerequisites:
- ACP support must be installed:
fast-rlm acp install. - The Claude Code and Codex presets shell out via
npx, so Node / npx must be on your PATH. (acp:opencodespawns theopencodebinary directly and needs no bridge package.) - The agent must already be authenticated in its own CLI (e.g.
claude /login,codex login,opencode auth login).
Backdoor: any other ACP agent
Backdoor agents need fast-rlm acp install too — the ACP provider is shared by
every acp: agent. Only their bridge versions are unmanaged: they are launched
exactly as you write them.
Only the three presets above are built in. To use any other ACP agent, register it
by command under acp_agents and select it by name. A registered name overrides a
built-in preset of the same name.
run(
"Summarize the input.",
config=RLMConfig(
primary_agent="acp:hermes",
acp_agents={
"hermes": {"command": "hermes", "args": ["acp"]},
"cursor": {"command": "npx", "args": ["-y", "cursor-agent-acp"]},
"pi": {"command": "npx", "args": ["-y", "pi-acp"]},
},
),
)
In YAML:
primary_agent: "acp:myagent"
acp_agents:
myagent:
command: npx
args: ["-y", "@acme/foo-acp"]
readonly_mode: plan # optional — the agent's read-only mode id, if any
model: some-model # optional default model
env: # optional extra env for the agent process
FOO: bar
| Field | Required | Meaning |
|---|---|---|
command |
yes | Executable to spawn. |
args |
no | Arguments passed to the command. |
readonly_mode |
no | The agent's read-only session mode id. When set, fast-rlm switches the session into it. |
model |
no | Default model id (overridable per call via ?model=). |
auth_method |
no | ACP auth method id (e.g. chatgpt). Pinning it silences the provider's "authMethodId is not configured" warning; only consulted on the lazy-auth fallback path. |
env |
no | Extra environment variables for the agent process. |
bridge_pkg |
no | The npm package spawned as the bridge. Built-in presets set this so acp install can pin it to an exact version (and follow an upstream rename) via the marker. Backdoor agents are launched as written. |
config_files |
no | Map of relative paths → JSON content to write into the temp cwd before launch. Use this to inject per-agent permission configs for custom agents (e.g. {".claude/settings.json": {"permissions": {"deny": ["Bash(*)"]}}} for a custom Claude Code variant). |
Tool stripping (built-in presets only)
A key goal of fast-rlm's ACP mode is that all computation happens in the Pyodide REPL, not inside the agent's own tool harness. Without restrictions, capable agents like Claude Code will use their native bash/file-read tools to pre-compute answers internally, then return hardcoded results — bypassing the observable REPL loop entirely.
The three built-in presets prevent this by injecting agent-specific permission configs into the throwaway cwd before each launch:
| Agent | Mechanism | Effect |
|---|---|---|
acp:claude-code |
.claude/settings.json written to temp cwd |
Denies Bash(*), Read(*), Write(*), Edit(*), WebFetch(*), WebSearch(*) via Claude's permissions system |
acp:opencode |
opencode.json written to temp cwd |
Sets bash, read, edit, glob, grep to "deny" as project-level config |
acp:codex |
-c sandbox_permissions=[] flag in launch args |
Passes an empty permissions array to the codex binary at startup |
After stripping, the agent can only return text (specifically ```repl ``` blocks).
All data access, computation, and side-effects go through the REPL.
Backdoor agents do not get this automatically. Agents registered via
acp_agentsare launched as-is — no config files are injected, no permission flags are added. If you want to restrict a custom agent, add aconfig_filesentry to its spec (see theAcpAgentSpecfields below) or setreadonly_modeto the agent's own permission mode id.
Safety & limitations
- Isolated cwd. Every ACP agent runs in a throwaway temp directory, so any stray write is contained there rather than in your project.
- Read-only enforcement varies by agent:
- opencode / Claude Code
planmode is a hard block — edit tools are removed. - Codex
read-onlyis approval-gated, and the ACP bridge auto-approves permission prompts, so codex can still write. The isolated temp cwd is its real guardrail. - Agents with no session modes (e.g. cursor, hermes) have no
readonly_modeand are contained by the temp cwd alone.
- opencode / Claude Code
- Budgets — only
max_global_callsworks. ACP agents report no token usage, somax_money_spent,max_completion_tokens, andmax_prompt_tokensare inert for them (always zero, never trip). The one budget that applies ismax_global_calls— a run-wide cap on total LLM calls — which defaults to50for ACP runs. Override it on the config or via--max-global-callsif you need more or fewer.