chant acp
chant acp speaks the Agent Client Protocol over stdio. An editor or a fountain sandbox spawns the process, sends JSON-RPC 2.0 on stdin, and gets session/update notifications back.
It imports no fountain type, route or URL. It ships in this lexicon because a fountain Agent with runtime: "acp" and runtime_command: "chant acp" is its first host, and any ACP client drives it unchanged.
chant acp # the bare form a client spawnschant acp serve # the same thing, spelled outchant acp --durable-requestschant acp --steward box-steward # what the Steward's Agent runsA prompt is a chant command line
Section titled “A prompt is a chant command line”Every session/prompt is one chant command line:
chant run prod-apply --env prodchant lifecycle diff --livechant buildEach turn is a command someone could have typed, so a steward’s conversation reads as the environment’s shell history.
No shell parses it: the text is split with quote awareness and nothing else. No $VAR, no &&, no pipes, no globbing, no redirection. The verb is then resolved against chant’s own command registry, so the set of things this accepts is exactly the set chant --help prints, and a prompt that does not parse to a chant verb is refused before anything runs. rm -rf / never reaches a shell, because there is no shell.
A parsed command runs in this process, against the checkout the session’s cwd points at.
What a client sees
Section titled “What a client sees”| Prompt | Updates |
|---|---|
chant run <op> | one tool_call per declared step, pending, emitted before the run starts, then a tool_call_update as each step settles. Stdout and stderr stream as agent_message_chunks, and the run’s ledger record is the final chunk. |
| any other verb | runs with --json; output streams as agent_message_chunks the same way. |
An Op’s declaration is the plan, so the whole plan arrives up front and fills in, and a client can draw every step before the first one runs. Tool calls carry kind: "execute", the title <phase> / <fn>, and the step’s args as rawInput.
A record that matches no declared step, which is what an onFailure phase produces, opens its own tool call and closes it at once. Failure phases are not part of the plan because they only run when the plan fails.
Protocol surface
Section titled “Protocol surface”| Method | Behavior |
|---|---|
initialize | negotiates down to the highest version both ends speak. Reports loadSession: false, sessionCapabilities.resume, text-only prompt capabilities, and no auth methods: the agent runs as whoever spawned it. |
session/new | a session is a working directory and nothing else. mcpServers is ignored, because chant’s tools are its own verbs. |
session/resume | takes up a session by the id the client holds, in the cwd it sends. A process that never opened that id opens it again: the session is only its working directory. Fountain spawns a new chant acp for every turn and resumes from a conversation’s second turn on. |
session/prompt | one command line, one turn. Turns are serialized: a turn borrows the process writers to stream output and changes into the session’s cwd, both process-wide, so one connection runs one command at a time. |
session/cancel | a notification. It aborts the in-flight step through the executor’s signal, which then runs the Op’s onFailure phases before the run settles. The pending session/prompt answers with stopReason: "cancelled". |
session/load is not implemented. There is no transcript to replay that the run ledger does not already hold, so a client resumes with session/resume. A gate a previous process asked about under --durable-requests does not survive a resume in a new process; answering it says to prompt the command line again.
Stop reasons are end_turn, cancelled, refusal (the command line did not parse to a chant verb) and waiting (a gate, under --durable-requests).
By default a run that reaches an unapproved gate ends its turn. The reply carries the pending fact and the line that resolves it:
chant approve prod-apply deploy-window --approver <you>The gate is a fact on chant’s ledger. The turn ends and frees the sandbox, and the next run re-evaluates the fact.
--durable-requests
Section titled “--durable-requests”With the flag, the same gate becomes a session/request_permission carrying allow_once and reject_once, and the turn ends waiting. The client answers on a later prompt whose _meta.chant.permission names the request id and the option chosen; allowing records the gate resolution the way chant approve does and re-runs the command line, which now walks through the gate.
It is off by default because a request that outlives a turn needs fountain#1635. fountain carries it from v0.21.0, and chant’s fountain runtime does not answer through it yet (chant#2391). Until then the flag is useful against an editor or any other ACP client that can hold a request across turns.
--steward <name>
Section titled “--steward <name>”The Steward’s Agent runs chant acp --steward <name>, so every turn in the session is that steward’s (chant#2749). A decision point an Op asks during a turn names the steward, makes its model call only through a capability the steward names, and stays open in the workspace for a person. A run that stops on one prints the question and the points answer line, and the turn ends. chant workspace points answer from the session is refused with answer-in-steward-turn, as a gate’s resolution from the session is refused.
The runtime: "acp" and runtime_command pair the steward sets is in fountain’s spec from v0.21.0. An older instance rejects the pair at apply. See the Agent section of Resources.
Redaction
Section titled “Redaction”The server does not redact. A step’s output reaches the conversation verbatim and fountain redacts secrets on the way in. chant never reads or prints the environment it was spawned with, so the conversation holds only what the command printed.