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.
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 |
plan (hard block) |
acp:codex |
npx -y @zed-industries/codex-acp |
read-only (approval-gated) |
acp:opencode |
opencode acp |
plan (hard block) |
Prerequisites:
- The Claude Code and Codex presets shell out via
npx, so Node / npx must be on your PATH. - The agent must already be authenticated in its own CLI (e.g.
claude /login,codex login,opencode auth login).
Backdoor: any other ACP agent
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. |
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.