Repository navigation
Add ACP v1 agent mode (cecli --acp) - #714
Merged
Merged
Conversation
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.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
cecli --acpruns cecli as an Agent Client Protocol v1agent 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
initialize,session/new,session/promptandsession/cancel.as tool cards with their output, the todo list as the client's plan, and the
Yieldsummaryas a message.
session/request_permission(yes / yes to all / skip all / no).available_commands_update).@-mentioned files are added to the chat; files outside the project are added read-only.
what
--yoloturns on.models (startup model, aliases, tag overrides,
.cecli.model.settings.yml), followed by thechat models of every provider whose API key is set, using the onboarding wizard's provider
and key lookup. A new optional
acp-modelssetting narrows that list with the same matchingas
/models, e.g.acp-models: [claude-sonnet, gpt-5*]./ask,/model,/reasoning-effort, ...), andtyping a command updates the picker. Legacy
session/set_modeworks too, for clientswithout 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:Delegate, workspace and workflow agents) is announced withsubagent_update, and its output, tool calls and permission requests go to its own session;running,requires_actionwhile it waits forpermission, then
idlewithend_turn,cancelledorerror;Broadcastmessages are reported assession_message;session/cancelon a child stops only that sub-agent, and a blockingDelegatecarries onwith the interrupted summary. Cancelling the parent's turn also stops the sub-agents it
waits for;
PAGER=cat. Otherwisegit logand friends wait for a keypressinside the PTY and the turn hangs until the command times out.
How it works
session/newpicks the directory, and ceclistarts there. A new thread on the same project gets a cleared chat.
AcpInputOutput.get_inputhands it thenext prompt, and the turn ends when the loop asks for input again.
cecli/helpers/server/acp/agent/:agent.py(protocol handlers, turns,permissions, sub-agent sessions),
transport.py(a smallTransportinterface plusStdioTransport, so a WebSocket transport could drive the same agent),io.py(theInputOutputsubclass),updates.py(cecli output assession/update),config.py(pickers) and
mcp.py(client MCP servers).--acponly counts on the command line, because the project has to be picked before anyconfig is read. In a config file or
CECLI_ACPit is ignored with a warning.Changes to existing code
About 200 lines outside the new package:
args.py,main.py: the--acpand--acp-modelsflags and startup wiring.io.py: no-optool_call_eventandsubagent_eventhooks. The coders calltool_call_eventat three points;AgentServiceandBroadcastcallsubagent_event.type="usage-report", so ACP can leave the token/cost line outof the chat (it still goes to the history file).
onboarding/providers.py: an optionalmodesfilter onget_models_for_provider, so thepicker leaves out image-generation models. Onboarding itself is unchanged.
io_proxy.py:tool_call_eventis tagged with the coder's uuid, like other output.agents/service.py: a newcancel_sub_agent(), plus fixes found while cancelling a singlesub-agent, which also apply in the terminal:
Delegateno longer turns a cancelled sub-agent into a cancelled parent; itreturns the interrupted summary, and still cancels the sub-agent when the parent is
cancelled;
cancellation (it turns into a ^C and
generate()returns normally);Yield(tools/_yield.py) no longer raises when a sub-agent it waits for was cancelled.Testing
tests/helpers/server/acp/test_agent.py(35 tests), sub-agent events and cancellation intests/subagents/test_service.py, plus one each intests/basic/test_main.pyandtests/helpers/test_onboarding.py.pyteston Python 3.14 (macOS): 2453 passed, 21 skipped. pre-commit passes on all files.server passed in
session/new, and sub-agents (blocking and parallel backgrounddelegations, cancelling a child, cancelling the parent) with a local model.
subagentsyet, so in Zed sub-agents still show in the main thread.main.Docs:
cecli/website/docs/config/acp.mdNot in this PR
session/load, image prompts, and cost and context usage viausage_updatePAGERfix for the terminal agent mode, which has the samegit loghang_inject_sub_agent_resultruns before the status is set in those branches. That predatesthis PR, so it's left for a separate fix.