Your users want your agent in their editor. Editors such as Zed start an agent as a process and talk the Agent Client Protocol (ACP) over stdin and stdout. serveAcp makes your harness an ACP v2 agent.
Each ACP session is one harness session. The ACP session id is the thread id.
user is the user of the editor, for example serveAcp({ host, harness: assistant, user: { id: 'ada' } }). Every session opens as that user, so /connect saves keys for them. On a host with access, user is required.
The prompts of a session run one after another, in the order that the editor sent them. A prompt with a large file keeps its place while the file uploads. The harness busy setting does not apply.
The agent holds a session only while a prompt runs or waits. An idle session closes after the host lifecycle.closeIdleSessionsAfterMs, and the next prompt opens it again.
version is the version that initialize reports. Default: '0.0.0'.
cwd is the working directory that the session list shows for each session. Default: process.cwd().
Use a persistent store instead of memoryPersistence() if a session must stay after the process stops.
Cancel stops the running turn, also while the editor asks for an answer. A turn that fails ends with the error stop reason and the message.
Image blocks, audio blocks, and embedded files of a prompt go to the media store of the session. A file that the harness refuses fails the prompt with an invalid params error. See Send and show media.
The agent sends each wait to the editor by its kind:
Kind
What the editor shows
approval
A permission request with the id of the real tool call. Allow always shows only for an approval of permissions(), because its answer schema takes always.
question, human
A form elicitation of the answer schema.
sign-in
A URL elicitation with the sign-in page.
client-tool
Nothing. The editor cannot run it, so the agent cancels it.
The editor must declare form and URL elicitation in initialize. If it does not, the agent cancels that wait, and the model continues without the answer.
A question that a tool asks while it runs (step.ask, an MCP server) is a form elicitation too. An answer that does not fit the schema asks again.
If the editor cancels the prompt while it asks, the turn stops. Its waits stay open until the next prompt.
ACP v2 is still a draft, so this API is experimental.
A client tool call, and a wait that the editor did not declare it can show, are cancelled. The model then continues without the answer.
Each session in the list shows the same working directory: the cwd of the agent. If the editor asks for the sessions of another directory, the list is empty.
An old session opens without its earlier messages. The editor shows only the new turns. The model still gets the full thread.
A resource link in a prompt is only named. The harness does not read it. The model can open it with its own tools.
serveAcp uses stdin and stdout when you pass no stream. To use another transport, pass an ACP Stream, or call createAcpAgent({ host, harness }) and connect the app yourself:
One agent app serves one connection. It reads the editor capabilities from that connection, and a second connection to it fails with an error. To serve more editors, for example over HTTP, make one app for each connection. With the AcpServer of @agentclientprotocol/sdk/experimental/server, pass createAgent: () => createAcpAgent({ host, harness: assistant }).