Agentao can connect to and manage project-local ACP (Agent Client Protocol) servers. These are external agent processes that communicate over stdio using JSON-RPC 2.0 with NDJSON framing.
Create .agentao/acp.json in your project root:
{
"servers": {
"planner": {
"command": "node",
"args": ["./agents/planner/index.js"],
"env": { "LOG_LEVEL": "info" },
"cwd": ".",
"description": "Planning agent",
"autoStart": true
},
"reviewer": {
"command": "python",
"args": ["-m", "review_agent"],
"env": {},
"cwd": "./agents/reviewer",
"description": "Code review agent",
"autoStart": false,
"requestTimeoutMs": 120000
}
}
}
/acp commands/acp # Overview of all servers
/acp list # Same as /acp
/acp start <name> # Start a server
/acp stop <name> # Stop a server
/acp restart <name> # Restart a server
/acp send <name> <message> # Send a prompt (auto-connects; handles permission/input inline)
/acp cancel <name> # Cancel active turn
/acp status <name> # Detailed status
/acp logs <name> [lines] # View stderr output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
command |
string | yes | — | Executable to launch |
args |
string[] | yes | — | Command arguments |
env |
object | yes | — | Extra environment variables |
cwd |
string | yes | — | Working directory (relative to project root) |
autoStart |
boolean | no | true |
Reserved for bulk-start flows; current CLI does not auto-start servers just because /acp was opened |
startupTimeoutMs |
integer | no | 10000 |
Parsed config field; currently not enforced by the CLI runtime |
requestTimeoutMs |
integer | no | 60000 |
Per-request timeout in ms |
capabilities |
object | no | {} |
Server capability hints |
description |
string | no | "" |
Human-readable description |
Values in env support $VAR / ${VAR} expansion from the process environment, so secrets such as API keys can live in the shell / .env rather than in acp.json.
<home>/.agentao/acp.json — ACP servers are project-scoped./acp send explicitly./acp command, not at startup./acp send and at safe idle points in the main CLI loop.configured → starting → initializing → ready ↔ busy → stopping → stopped
↕
waiting_for_user
subprocess.Popen called.initialize + session/new) in progress.session/prompt.When an ACP server needs user input (permission confirmation or free-form text), it becomes a pending interaction. These appear in the inbox and are handled inline by the CLI.
Permission requests: choose 1 / 2 / 3 / 4
Input requests: type a reply inline at the prompt
Pending interactions are visible in /acp status <name> and /status.
User messages that explicitly name a configured ACP server are routed directly to that server instead of going through the normal main-agent turn. Recognised deterministic forms:
@server-name <task>server-name: <task>让 server-name <task> / 请 server-name <task>On a match the CLI prints ACP Delegation → <server> and reuses the same runner as /acp send (inline handling of permission / input requests).
Notes:
An earlier design proposed an experimental pushTaskCompleteToAgent flag that would bridge private task_complete notifications into the main Agentao conversation. It was dropped before landing: task_complete is not part of the ACP standard enum of sessionUpdate kinds, so shipping it would require every compatible server to speak a private extension. The flag, its queue, and the synthetic-message injection path are no longer present in the codebase. See the corresponding design doc for history.
Design doc:
Server stderr is captured in a bounded ring buffer (200 lines). View with:
/acp logs <name> # Last 50 lines
/acp logs <name> 100 # Last 100 lines
/status shows an ACP summary when servers are configured:
ACP servers: 1/2 running
ACP inbox: 3 queued
ACP interactions: 1 pending
/acp status <name> for the error message./acp logs <name> for stderr output.command exists and is executable.cwd is a valid directory.initialize with a valid ACP response.session/new with a sessionId./acp logs <name> for protocol errors./acp to see the current inbox count._agentao.cn/ask_userAgentao supports a private ACP extension method _agentao.cn/ask_user for requesting free-form text input from the user. This is advertised in the initialize response’s extensions array.
{
"jsonrpc": "2.0",
"id": "srv_123",
"method": "_agentao.cn/ask_user",
"params": {
"sessionId": "sess_xxx",
"question": "Please provide branch name"
}
}
{
"outcome": "answered",
"text": "feature/acp-client"
}
Or on cancellation:
{
"outcome": "cancelled"
}
If the user is unavailable, the sentinel "(user unavailable)" is returned as a conservative fallback — the turn is not crashed.