Agent Client Protocol (editor integration)
Editors and orchestrators want to drive an agent themselves rather than have you type at a terminal. The Agent Client Protocol is the common language for that: the host launches the agent as a child process and talks to it over that process's stdin and stdout.
lev agent-client is Leviath speaking it. The host sends protocol messages, and the command turns
those into runs on the shared-world daemon, streaming output back as it happens.
Warning
"ACP" means two unrelated things, so Leviath never writes it unqualified. This is the Agent Client Protocol, JSON-RPC over stdio. It is not the Agent Communication Protocol, a REST and SSE API from the BeeAI project.
What it is
The Agent Client Protocol lets an editor host launch an agent as a subprocess and drive it over the process's stdin/stdout. Messages are framed as one compact JSON object per line (newline-delimited JSON-RPC 2.0), with a 64 KiB ceiling per frame. The handshake and turn cycle are:
initialize: capability exchange; the protocol version is1.session/new: open a session (carries the working directory).session/prompt: send a prompt turn; spawns (or, on later prompts, messages) an agent in the daemon.session/update: notifications streaming the agent's live output back to the host.session/cancel: cancel the in-flight turn.
The hosts named in the Leviath source are Zed and Gas City.
How a host connects
The host launches lev agent-client and speaks JSON-RPC over the process's own stdin/stdout. Every
prompt is forwarded to the daemon over its control socket; the daemon's live event stream and each
run's per-stage output are translated into session/update notifications until the run finishes or parks.
flowchart LR HOST["Editor host<br/>(Zed / Gas City)"] HOST -->|"stdin: JSON-RPC requests"| CLI["lev agent-client"] CLI -->|"stdout: session/update"| HOST CLI -->|"control socket"| DAEMON["Shared-world daemon"] DAEMON -->|"WorldEvent stream"| CLI
Note
stdout is reserved for the JSON-RPC channel. All logs and diagnostics go to stderr so they can't corrupt the protocol stream.
Starting it
lev agent-client --agent my-agentWith no --agent, each session's working directory is searched for an agent.leviath blueprint.
Flags (run lev agent-client --help for the authoritative list):
| Flag | Purpose |
|---|---|
--agent <name-or-path> |
Blueprint to serve: an installed agent name, or a path to one. Omitted, the session's working directory is searched. |
--yolo |
Approve every tool call without prompting. Recommended when the host does not implement session/request_permission (e.g. Gas City). |
--allow <tool> |
Allow a tool outright. Repeatable. |
--max-depth <n> |
Override the blueprint's max sub-agent tree depth. |
--no-seed-commands |
Refuse the blueprint's seed = { command = "..." } regions, which run at spawn before any approval prompt. |
--output-format <label> |
Ask for the final output in this shape. Any label works. |
--output-instructions <text> |
Extra guidance about that shape. |
The agent's answer
ACP has no result field. A turn returns a stop reason, and everything the user sees arrives as
agent_message_chunk updates carrying the stage's streamed output.
An agent that submits a final output gets one more chunk at the end of the turn, holding the answer and its format label. It is set apart from the streamed output, so a host can show it as the agent's conclusion rather than more log text.
A run that submits nothing adds nothing. Ask for a shape with --output-format, since the protocol
carries no field for it.
Permission handling
Hosts that implement the client-side methods advertise capabilities at initialize, and the agent
surfaces tool approvals as session/request_permission requests the host answers. Hosts that send
no capabilities (Gas City sends none) cannot answer such a request, so instead of deadlocking, the
question is surfaced as output and the turn stays in flight. Answer it from Leviath's own
surfaces, lev respond or lev dash, and the run continues; unanswered, it is denied after
[limits] interaction_timeout_secs. Use --yolo (or scoped --allow flags) to run unattended
against such a host.
Connecting a host
Point the host's agent command at lev agent-client, then open a session and prompt it. Output
streams back as the run progresses.
lev agent-client --agent coder --yoloGas City has a page of its own, with the city.toml stanza and the timeouts worth
adjusting.
Note
Editor integration is a thin front end over the daemon, exactly like lev run and lev serve. It
owns no agent world of its own. See the daemon for what actually hosts the run, and
the CLI reference for the rest of the lev commands.