Skip to content

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@^6 while ai@7 is the latest release, so resolving both to "latest" would install an incompatible pair. acp install reads the provider's declared range and takes the newest release inside it.
  • If a bridge is renamed upstream (as @zed-industries/claude-code-acp was, to @agentclientprotocol/claude-agent-acp), edit bridge_packages in 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:

  1. fast-rlm sends the agent its system prompt and the message history.
  2. The agent replies with a ```repl code block.
  3. 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:opencode spawns the opencode binary 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_agents are launched as-is — no config files are injected, no permission flags are added. If you want to restrict a custom agent, add a config_files entry to its spec (see the AcpAgentSpec fields below) or set readonly_mode to 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 plan mode is a hard block — edit tools are removed.
    • Codex read-only is 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_mode and are contained by the temp cwd alone.
  • Budgets — only max_global_calls works. ACP agents report no token usage, so max_money_spent, max_completion_tokens, and max_prompt_tokens are inert for them (always zero, never trip). The one budget that applies is max_global_calls — a run-wide cap on total LLM calls — which defaults to 50 for ACP runs. Override it on the config or via --max-global-calls if you need more or fewer.