ACP Agent Runner
Run any Agent Client Protocol compatible coding agent as a LangBot AgentRunner.
ACP Agent Runner
acp-agent-runner runs an Agent Client Protocol compatible agent process as a LangBot AgentRunner.
It is a thin runtime adapter:
- LangBot remains the control plane.
- The plugin starts an ACP server over stdio, such as
npx -y @zed-industries/codex-acp,npx -y @agentclientprotocol/claude-agent-acp,opencode acp, ornpx -y @google/gemini-cli --acp. - In daemon mode, a user-side
langbot-runner-daemonconnects outward to the plugin and starts the ACP process on the user's machine. - The plugin speaks ACP JSON-RPC:
initialize,session/new,session/loadorsession/resume, andsession/prompt. - ACP
session/updatetext chunks are streamed back to LangBot. - LangBot multimodal input is mapped to ACP prompt content blocks when the selected ACP runtime advertises the matching prompt capability.
- LangBot tools, knowledge bases, and history are exposed through the SDK-owned run-scoped MCP bridge.
Runner ID
plugin:langbot-team/ACPAgentRunner/default
Configuration
Common local examples:
provider = claude-code
location = local
workspace = /path/to/workspace
provider = codex
location = local
workspace = /path/to/workspace
For custom ACP commands, set provider=custom and acp-command, for example:
provider = custom
location = local
workspace = /path/to/workspace
acp-command = opencode acp
For a user workstation behind NAT, enable the plugin daemon hub and run the user-side daemon:
# Plugin config
daemon-enabled = true
daemon-host = 0.0.0.0
daemon-port = 8766
daemon-token = <shared-token>
cd acp-agent-runner
python daemon.py \
--url ws://<langbot-public-host>:8766 \
--daemon-id alice-laptop \
--token <shared-token>
Then configure the runner:
provider = codex
location = daemon
daemon-id = alice-laptop
workspace = /Users/alice/project
Custom mode is for ACP stdio commands that are not built in, or for users who accept weaker/no ACP session recovery for a specific runtime:
provider = custom
location = local
workspace = /path/to/workspace
acp-command = kimi acp
provider = custom
location = local
workspace = /path/to/workspace
acp-command = agent acp
provider = custom
location = local
workspace = /path/to/workspace
acp-command = goose acp
acp-command must start an ACP JSON-RPC stdio server, not an interactive-only
CLI. The runner still calls initialize, then session/resume or
session/load when a stored session id exists and the runtime advertises that
capability, otherwise session/new. Set reuse-session=false to force a fresh
ACP session each run:
provider = custom
location = local
workspace = /path/to/workspace
acp-command = kimi acp
reuse-session = false
Useful options:
provider: built-in ACP launch preset. Current presets are limited to ACP runtimes that advertiseloadSessionorsessionCapabilities.resumeand have no known public cross-process resume blocker. Usecustomfor anything else.location:local,remote-ssh, ordaemon.workspace: agent workspace directory. Inremote-sshanddaemonmodes this path is on the remote/user machine.ssh-target: SSH target forremote-ssh, for example[email protected].ssh-port: SSH port. Defaults to22.ssh-identity-file: optional private key path on the LangBot host.daemon-id: stable ID of a connected user-side runner daemon forlocation=daemon.daemon-connect-timeout: seconds to wait for the selected daemon to come online.acp-command: optional command override. Required only forprovider=custom.env-json: JSON object merged into the process environment.reuse-session: persists and reusesexternal.acp_session_idwhen the ACP agent supportssession/resumeorsession/load.langbot-assets-enabled: injects the LangBot run-scoped MCP bridge into ACPmcpServers.langbot-assets-mode:auto,ephemeral, orgateway.autocurrently preserves the existing per-run bridge behavior;gatewayregisters the run in the SDK-owned long-lived HTTP MCP gateway.langbot-assets-gateway-port: optional fixed port forlangbot-assets-mode=gateway. Use a fixed port when another platform such as Dify needs to register the gateway URL.langbot-assets-gateway-public-url: optional externally reachable MCP URL, for examplehttps://example.com/mcp.
Headless ACP permission requests are answered with allow_once by default.
LangBot does not yet expose an interactive approval UI for these requests.
Steering (follow-up input)
This runner declares capabilities.steering: true. When a run is still in
progress and the user sends another message, LangBot absorbs it into the active
run instead of starting a new one. The runner drains these follow-ups at each
turn boundary via steering_pull and runs them as additional ACP
session/prompt turns that resume the same session (session/resume or
session/load). The run emits a single terminal run.completed once no
follow-ups remain.
Notes:
- Follow-ups are injected between turns, not mid-token.
- Follow-up turns currently carry text only; attachments on follow-ups are not yet forwarded (the first turn still maps full multimodal input as described below).
- In
daemonmode follow-ups are still drained (no message loss), but session continuity across follow-up turns is best-effort. - Steering only applies when the run has a conversation scope; otherwise the runner transparently falls back to single-turn execution.
Multimodal Input
The runner accepts LangBot structured input and attachments. It maps them to ACP
session/prompt content blocks according to the selected runtime's
agentCapabilities.promptCapabilities:
- inline image base64/data URLs are sent as ACP
imageblocks only when the runtime advertises image prompt support; - inline file base64/data URLs are sent as embedded ACP
resourceblocks only when the runtime advertises embedded context support; - URL-backed images or files are sent as ACP
resource_linkblocks; - unsupported inline attachments are not silently dropped; the prompt receives a short attachment note explaining which content was not sent.
Provider support still depends on the ACP executable being launched. A runtime that does not advertise image prompt support can still handle normal text input and URL resource links, but it will not receive inline image bytes.
Built-in provider commands are intentionally unpinned and assume the selected
runtime can run on the target machine. Built-in presets must support ACP session
recovery through loadSession or sessionCapabilities.resume; otherwise this
runner would silently degrade into single-turn execution after each stdio
process exits.
| Provider | Default ACP command | Verified session recovery signal |
|---|---|---|
auggie | npx -y @augmentcode/auggie --acp | loadSession |
autohand | npx -y @autohandai/autohand-acp | loadSession, session/resume |
claude-code | npx -y @agentclientprotocol/claude-agent-acp | loadSession, session/resume |
codebuddy-code | npx -y @tencent-ai/codebuddy-code --acp | loadSession |
codex | npx -y @zed-industries/codex-acp | loadSession, session/resume |
deepagents | npx -y deepagents-acp | loadSession |
dimcode | npx -y dimcode acp | loadSession, session/resume |
dirac | npx -y dirac-cli --acp | loadSession |
factory-droid | npx -y droid exec --output-format acp-daemon | loadSession, session/resume |
gemini | npx -y @google/gemini-cli --acp | loadSession |
glm-agent | npx -y glm-acp-agent | loadSession, session/resume |
kilo | npx -y @kilocode/cli acp | loadSession, session/resume |
opencode | opencode acp | loadSession, session/resume |
pi-acp | npx -y pi-acp | loadSession |
qwen-code | npx -y @qwen-code/qwen-code --acp --experimental-skills | loadSession, session/resume |
The following ACP-capable or ACP-adjacent CLIs are not built-in presets because they do not currently meet the session recovery bar for this runner:
| Provider | Reason |
|---|---|
agoragentic | initialize reports loadSession: false and no session/resume. |
cline | Headless initialize did not complete during verification, so session recovery could not be confirmed. |
cursor | Public reports show loadSession: true is advertised, but session/load fails for ACP-created sessions. |
github-copilot | Registry npx launch did not start a usable ACP binary during verification. |
goose | Upstream docs say ACP providers do not support session resume or fork yet. |
kimi | Public issue reports ACP load/switch starts with blank history even though CLI history exists. |
langcli | No registry/npm-verifiable ACP stdio package was available during verification. |
nova | Headless initialize did not complete during verification, so session recovery could not be confirmed. |
qoder | Public report shows ACP-created sessions cannot be loaded across process restarts despite loadSession: true. |
sigit | Packaged binary could not be verified in the test environment and no public session recovery evidence was found. |
Other ACP-compatible runtimes from the public ACP registry can still be used via
provider=custom and acp-command. DeepSeek's official Deep Code CLI is not a
built-in preset yet because its current public docs describe the interactive
deepcode CLI and MCP support, but do not document an ACP stdio mode.
For a remote Claude ACP process over SSH:
provider = claude-code
location = remote-ssh
ssh-target = [email protected]
workspace = /home/yhh/langbot-e2e/acp-workspace
Remote mode does not connect to a long-running ACP TCP service. LangBot starts the ACP process itself over SSH:
LangBot host
-> ssh user@host "cd <workspace> && exec <acp-command>"
-> ACP JSON-RPC over ssh stdio
When LangBot assets are enabled, the runner also starts a temporary run-scoped MCP bridge on the LangBot host and adds an SSH reverse tunnel on the same SSH connection:
remote ACP process
-> http://127.0.0.1:<forwarded-port>/mcp
-> SSH -R tunnel
-> LangBot run-scoped MCP bridge
The remote host must allow non-interactive SSH login from the LangBot host and
must already have the selected agent runtime installed, authenticated, and
available on PATH for non-interactive shells.
Platform notes:
- Linux remote: default path. The wrapper uses
bash -lc, creates the workspace directory, changes into it, then execsacp-command. - macOS remote: same as Linux when the remote has
bashand the selected agent runtime available to non-interactive SSH sessions. If a Homebrew/npm path is missing, set it throughenv-jsonor use an absolute command path. - Windows remote: not identical to Linux/macOS. Configure the hidden
remote-shell=powershellkey manually; the wrapper uses PowerShell to create the workspace and runacp-command. Windows OpenSSH must support reverse forwarding for LangBot MCP assets to work. A first-class Windows UI option is not exposed yet.
If a network policy blocks SSH reverse forwarding, set
mcp-bridge-transport=http plus mcp-public-url to a URL reachable from the
remote host, or disable LangBot asset injection with langbot-assets-enabled=false.
Run-Scoped LangBot Assets
By default the plugin starts a temporary run-scoped MCP bridge for each run. The
bridge URL and token are injected into ACP mcpServers, and the bridge is
stopped when the run finishes.
In location=daemon, the ACP process does not call the plugin's MCP bridge
directly. The user-side daemon starts a localhost HTTP MCP proxy, injects that
local URL into ACP, then forwards MCP JSON-RPC requests over the already-open
WebSocket connection back to the plugin. The plugin handles those requests with
the current run's AgentRunAPIProxy, so LangBot assets remain scoped by
run_id and the Host authorization snapshot. The user's workstation does not
need a public IP or inbound port for this path.
Runner code should import the SDK proxy from
langbot_plugin.api.proxies.agent_run.
To test the long-lived HTTP MCP gateway path with ACP, set:
langbot-assets-enabled = true
langbot-assets-mode = gateway
langbot-assets-gateway-port = 8765
In gateway mode, the SDK starts one process-long HTTP MCP gateway and registers
each LangBot run with a short-lived run token. ACP receives the gateway MCP URL
and token through mcpServers headers, so the model can call stable LangBot
gateway tools without seeing the token. The gateway tool list is stable and
includes:
langbot_list_assetslangbot_get_current_eventlangbot_history_pagelangbot_retrieve_knowledgelangbot_get_tool_detaillangbot_call_tool
The stable tool list is intentionally compatible with platforms such as Dify
that cache MCP provider tools. For those platforms, the same gateway can accept
the short-lived run_token as a tool argument instead of an HTTP header.
Register the provider URL with the /mcp path, for example
http://<langbot-host>:8765/mcp or the value configured in
langbot-assets-gateway-public-url.
The current LangBot run_id is included in the prompt for diagnostics only. ACP agents should follow MCP tool schemas exactly and should not add run_id to tool calls unless a specific tool schema asks for it.
Complete configuration index
In addition to the common fields described above, the current runner exposes:
daemon-connect-timeoutfor waiting on a selected daemon.langbot-assets-gateway-host,langbot-assets-gateway-port,langbot-assets-gateway-public-url, andlangbot-assets-token-ttlfor Asset Gateway mode.ssh-connect-timeout,ssh-extra-options, andssh-identity-filefor SSH execution.startup-timeoutandinitialize-timeoutfor ACP process startup and handshake deadlines.create-session-if-missingfor recovery fallback behavior.streamingfor incremental output.append-run-scope-promptfor adding LangBot run-scope guidance.mcp-servers-jsonfor additional MCP server configuration.
Scope
This plugin intentionally does not implement an agent platform, task board, workspace manager, or provider-specific CLI behavior. Provider setup, login, model selection, and tool permission semantics still belong to the ACP agent executable being launched.
Use ACP Agent Runner in Feishu/Lark bots, DingTalk bots, WeCom bots, WeChat bots, QQ bots, Slack bots, Discord bots, Telegram bots, LINE Bots, and Matrix Bots.
Host your bot on LangBot Cloud and connect this extension to the collaboration platforms your team already uses.