Lets editors that speak the Agent Client Protocol, such as Zed, launch cecli
as an agent over stdin/stdout.
- Prompts drive the normal linear run loop; slash commands are announced
as available commands and @-mentioned files are added to the chat
- Model output and reasoning stream as message and thought chunks; agent
tool calls become tool cards, the todo list becomes the plan, and the
Yield summary is shown as a message
- Confirmations become permission requests that show their question;
cancel interrupts the running request
- Mode (including Agent YOLO), model and reasoning effort are offered as
session config options backed by the matching cecli commands; the model
list adds the chat models of every provider with an API key set, and
--acp-models narrows it
- MCP servers from the editor (stdio, HTTP, SSE) are connected alongside
cecli's own
- Sub-agents are reported as ACP subagent sessions (unstable RFD) when the
client advertises the subagents capability: each gets its own session,
work state, messages between sessions and its own cancel
- Pagers are disabled for commands, and the per-request token/cost line is
kept out of the editor
The agent lives in cecli/helpers/server/acp/agent/. Core changes are
InputOutput.tool_call_event and subagent_event hooks (no-ops outside ACP),
a usage-report output type, AgentService.cancel_sub_agent, the --acp and
--acp-models flags and startup wiring in main.py. Cancelling a sub-agent no
longer fails a blocking Delegate or a Yield waiting on it, and is reported
as interrupted even when streaming absorbed the cancel.
cecli --acp runs cecli as an Agent Client Protocol v1
agent over stdio, so editors that speak ACP (Zed, or any other ACP v1 client) can launch it as
a subprocess and drive it with JSON-RPC on stdin/stdout. Discussed with @dwash96 on Discord
first; I've been using it day to day in Zed.
It only advertises what it implements, so a client never sees a capability cecli doesn't
handle. The WebSocket bridge (bridge.py, protocol.py, ws_server.py) is untouched.
What it does
as tool cards with their output, the todo list as the client's plan, and the Yield summary
as a message.
@-mentioned files are added to the chat; files outside the project are added read-only.
what --yolo turns on.
models (startup model, aliases, tag overrides, .cecli.model.settings.yml), followed by the
chat models of every provider whose API key is set, using the onboarding wizard's provider
and key lookup. A new optional acp-models setting narrows that list with the same matching
as /models, e.g. acp-models: [claude-sonnet, gpt-5*].
typing a command updates the picker. Legacy session/set_mode works too, for clients
without config options.
before the session starts.
Subagent Sessions RFD. It is unstable in
ACP v1, so this only happens for clients that advertise clientCapabilities.subagents:
subagent_update, and its output, tool calls and permission requests go to its own session;
permission, then idle with end_turn, cancelled or error;
session_message;
with the interrupted summary. Cancelling the parent's turn also stops the sub-agents it
waits for;
inside the PTY and the turn hangs until the command times out.
How it works
starts there. A new thread on the same project gets a cleared chat.
next prompt, and the turn ends when the loop asks for input again.
permissions, sub-agent sessions), transport.py (a small Transport interface plus
StdioTransport, so a WebSocket transport could drive the same agent), io.py (the
InputOutput subclass), updates.py (cecli output as session/update), config.py
(pickers) and mcp.py (client MCP servers).
config is read. In a config file or CECLI_ACP it is ignored with a warning.
Changes to existing code
About 200 lines outside the new package:
tool_call_event at three points; AgentService and Broadcast call subagent_event.
of the chat (it still goes to the history file).
picker leaves out image-generation models. Onboarding itself is unchanged.
sub-agent, which also apply in the terminal:
returns the interrupted summary, and still cancels the sub-agent when the parent is
cancelled;
cancellation (it turns into a ^C and generate() returns normally);
Testing
tests/subagents/test_service.py, plus one each in tests/basic/test_main.py and
tests/helpers/test_onboarding.py.
server passed in session/new, and sub-agents (blocking and parallel background
delegations, cancelling a child, cancelling the parent) with a local model.
Docs: cecli/website/docs/config/acp.md
Not in this PR
_inject_sub_agent_result runs before the status is set in those branches. That predates
this PR, so it's left for a separate fix.