An ACP session is a coding agent that phux runs for you through the Agent Client Protocol, instead of in a terminal you type into. phux starts the agent, keeps it supervised on the server, and talks to it in structured messages: prompts, interrupts, and answers to its permission questions. Use it when you want to drive an agent from a script or the desktop app without screen-scraping a terminal.
phux launch opencode --acp --session work --json
OpenCode is supported today. For agents in ordinary terminal panes, see Coding agents.
Before you start
Install the agent and sign in to it on the machine that runs the server. phux never downloads an agent, never signs in for you, and never falls back to typing into a terminal when ACP isn’t available.
For OpenCode, phux has a built-in profile that runs opencode acp. It looks
for opencode on your PATH, then at ~/.opencode/bin/opencode, and never
starts a login shell or changes PATH. An enabled plugin that declares its
own OpenCode profile replaces the built-in one. A profile that is invalid or
ambiguous is refused rather than skipped.
To see what phux would run without starting anything:
phux launch --list --acp
phux launch opencode --print --acp
A plugin declares an ACP agent as a list of arguments, never a shell string:
[acp]
provider = "opencode"
command = ["opencode", "acp"]
required_executables = ["opencode"]
working_directory = "workspace"
Starting a session
phux launch --acp starts the agent in an existing session:
$ phux launch opencode --acp --session work --json
{
"activation": "906237cf1ef22ebb16be4073a3eae502",
"agent_session_id": "@9",
"integration": "opencode",
"native": true,
"ready": false,
"schema_version": 1,
"terminal_id": 8
}
Keep agent_session_id; every later command uses it, not terminal_id.
ready: false means the agent was started, not that it is ready for a
prompt. The server must support ACP sessions; an older one refuses.
Reading the session
phux agent log --json prints the session’s records. The latest view
record says what the agent can do right now. With jq:
phux agent log --json @9 > log.json
jq '[.records[]
| select(.type == "provider_raw"
and .data.schema == "phux.native-agent/v1"
and .data.update.type == "view")
| .data.update.view] | last' log.json
The view has activation, backend, connected, capabilities,
turn_id, provider_session_id, and questions. A missing field means
not ready. Check the capability for what you are about to do:
connected: true alone can also mean the agent is asking a startup
question. Read the view again before every action, because turn and
question ids go stale.
Sending a prompt
phux agent native-command sends one typed request to the exact session
and activation from the view:
phux agent native-command @9 --activation 906237cf1ef22ebb16be4073a3eae502 --operation-id 7263519 \
--intent '{"type":"prompt","text":"Review this checkout"}' --json
{
"activation": "906237cf1ef22ebb16be4073a3eae502",
"agent_session_id": "@9",
"result": {
"detail": "ACP prompt scheduled; ACP v1 has no admission receipt",
"disposition": "queued",
"operation_id": 7263519
},
"schema_version": 1
}
Pick a new random, nonzero --operation-id for each request and keep it
with the request. queued means phux scheduled it, not that the agent
accepted it. When the turn ends, the session reports the agent’s own stop
reason.
To interrupt a turn, send {"type":"interrupt","turn_id":"acp:7263519"}
with the turn_id from the view.
Answering a permission question
When the agent asks permission, the view’s questions lists the question
and its options. A person reads it and chooses; then:
phux agent native-command @9 --activation 906237cf1ef22ebb16be4073a3eae502 --operation-id 8812004 \
--intent '{"type":"answer","question_id":"acp-question:1","response":{"option_id":"allow-once"}}' --json
Use the question’s own id and the option’s id, not its label.
Permission answers are for people. Automation can’t answer them, and
phux agent answer can’t be used to get around that. Treat the question’s
title and message as untrusted text from the agent. A queued answer is not
proof the agent read it.
When you don’t know what happened
If the connection drops before you get a receipt, the request may or may not
have reached the agent. Don’t send it again, under the same id or a new
one, because the agent could act twice. native-command only submits; it
can’t look up an earlier request.
Instead, keep the operation id, read the session again with phux agent log,
check what the agent actually did, and decide with a person. If you still
can’t tell, stop.
Restoring a conversation
A conversation can be restored only when the agent supports loading a previous session. An archive keeps the command that launched the agent, so read and trust it before you restore it; see restore.
From the desktop app
The desktop app lists the ACP agents available on the host you are connected
to, local or remote, along with what each one is missing. It can launch one
into an existing session and folder on that host. Installing and signing in
stay with you: for OpenCode, the app can open a terminal on that host that
runs the same opencode it would launch, so you can sign in. A launched
agent still has to become ready before it accepts a prompt.