Install
openclaw skills install @berriosb/opencode-acp-control-3Use when an AI agent needs to start, drive, or monitor an OpenCode CLI session programmatically over the Agent Client Protocol (ACP). Triggers include requests to "spawn OpenCode", "control OpenCode from another agent", "automate OpenCode over JSON-RPC", "resume an OpenCode session", or any task that requires the agent to act as an ACP client rather than an interactive user. Provides the JSON-RPC 2.0 framing, session lifecycle, polling strategy, permission-request handling, and update detection needed to wrap OpenCode from inside another AI agent.
openclaw skills install @berriosb/opencode-acp-control-3Drive an OpenCode CLI session over the Agent Client Protocol (ACP).
Use it when the calling agent must:
Do not use it for direct file editing, one-off shell commands, or any task that does not require the Agent Client Protocol.
| Action | Generic tool call |
|---|---|
| Start OpenCode | terminal(command: "opencode acp --cwd /path/to/project", background: true) |
| Send JSON-RPC frame | process.write(processId, "<frame>\n") |
| Read available output | process.poll(processId) (repeat every ~2s) |
| Stop OpenCode | process.kill(processId) |
| List past sessions | terminal(command: "opencode session list", workdir: "<project>") |
| Get current version | terminal(command: "opencode --version") |
| Prompt the user | ask_user(question, options) |
The calling agent must map these generic names to its own platform (Hermes,
Clawdbot, etc.). See README.md for the mapping table.
\n). Not LSP Content-Length.id; the calling agent increments
monotonically starting at 0. Notifications have no id and never produce a
response.sessionId returned by session/new is the only
reference; treat it as a string.fs.readTextFile, fs.writeTextFile, and terminal
in the initialize handshake.terminal(
command: "opencode acp --cwd /path/to/project",
background: true,
workdir: "/path/to/project"
)
Save the returned processId. All subsequent frames go through it.
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{
"protocolVersion":1,
"clientCapabilities":{
"fs":{"readTextFile":true,"writeTextFile":true},
"terminal":true
},
"clientInfo":{
"name":"opencode-acp-control",
"title":"OpenCode ACP Control",
"version":"0.3.0"
}
}}
Expect result.protocolVersion: 1.
{"jsonrpc":"2.0","id":1,"method":"session/new","params":{
"cwd":"/path/to/project",
"mcpServers":[]
}}
Save result.sessionId (e.g. "sess_abc123").
{"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{
"sessionId":"sess_abc123",
"prompt":[{"type":"text","text":"List the TypeScript files in this repo."}]
}}
Poll stdout every ~2s until a response arrives whose id matches your request
and whose result.stopReason is set. While polling you will also receive
notifications:
{"jsonrpc":"2.0","method":"session/update","params":{...}}
Collect them in order — they make up the agent's streamed output.
{"jsonrpc":"2.0","method":"session/cancel","params":{"sessionId":"sess_abc123"}}
No response is sent for a cancel — it is a notification.
OpenCode asks for confirmation before running shell commands or editing files by sending a server-to-client request:
{"jsonrpc":"2.0","id":12,"method":"requestPermission","params":{
"sessionId":"sess_abc123",
"toolCall":{"toolCallId":"call_1","status":"pending",
"title":"bash","rawInput":{"command":"npm install"},"kind":"bash"}
}}
Prompt the user, then respond with the matching id:
{"jsonrpc":"2.0","id":12,"result":{"reply":"once"}} // allow once
{"jsonrpc":"2.0","id":12,"result":{"reply":"always"}} // allow for the session
{"jsonrpc":"2.0","id":12,"result":{"reply":"reject"}} // deny
For each OpenCode instance the calling agent holds:
| Field | Source |
|---|---|
processId | Returned by the terminal(background:true) call |
sessionId | Returned by session/new (OpenCode-internal) |
nextId | Integer counter for the next request, starting at 0 |
stopReason | Last terminal reason observed (end_turn, cancelled, max_tokens) |
process.poll calls.terminal("opencode session list", workdir: "<project>") returns a table of
{id, updated, messages}.
ask_user to pick one.
terminal("opencode acp --cwd <project>", background:true) to restart.
initialize (id=0).
session/load with the chosen id, plus cwd and mcpServers:
{"jsonrpc":"2.0","id":1,"method":"session/load","params":{
"sessionId":"sess_abc123","cwd":"/path/to/project","mcpServers":[]
}}
OpenCode streams the full conversation history back through session/update
notifications.
| Symptom | Likely cause | Action |
|---|---|---|
| Empty polls for >5 min | Long agent thinking, model stall, or network drop | Cancel + restart |
parse error on stdout | Garbled binary output or partial frame | Skip the line, continue |
| Process exits unexpectedly | OpenCode crashed | Inspect stderr, restart |
initialize rejects protocolVersion | OpenCode < v1.1.0 or client drift | Upgrade OpenCode, align clientInfo.version |
requestPermission keeps arriving | Session is in a tool-call loop | Cancel, narrow the prompt |
session/load 404s | Stale or deleted session id | Fall back to session/new |
OpenCode auto-updates on restart. To check and trigger an update:
terminal("opencode --version") → current version.
web_fetch("https://github.com/sst/opencode/releases/latest") → latest tag
in the redirect URL.
Compare versions. If newer:
process(action:"list") to find every running opencode acp process.process.kill(processId) for each.terminal("opencode acp", background:true) to restart and trigger the
auto-download.Verify with opencode --version again. If still old, fall back to a manual
install (review the installer script before piping curl | bash):
curl -fsSL https://opencode.ai/install | bash
cwd must be absolute; normalize before sending.sort_keys=True if possible) to make log
diffs stable.cwd must be inside the sandbox;
the ACP server inherits the calling agent's filesystem access.initialize after a process start must complete before any
other request — JSON-RPC servers reject out-of-order calls.examples/acp_demo.py — runnable end-to-end demo with --dry-run and
--no-prompt modes.README.md — quick start and tool-platform mapping.CHANGELOG.md — release notes.