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 is 1.
  • 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

bash
lev agent-client --agent my-agent

With 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.

bash
lev agent-client --agent coder --yolo

Gas 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.