Building an ACP-Compatible Agent Live — Bennet Fenner, Zed
AI Engineer · 2026-07-08 · 18м 19с · 3 918 просмотров · YouTube ↗
Топики: ai-agent-orchestration
🎧 Аудио
📝 Summary
model=deepseek-v4-flash · prompt=summary-v7 · 5 121→1 862 tokens · 2026-07-20 13:35:43
🎯 Главная суть
Zed разработал открытый протокол Agent Client Protocol (ACP) — JSON RPC на основе, похожий на MCP или LSP. ACP позволяет любым AI-агентам (Codex, Gemini, CLI‑агентам) общаться с редакторами кода через единый интерфейс. Уже 40 клиентов (JetBrains, Obsidian, OpenClaw) и множество агентов (OpenCode, Cursor) поддерживают ACP. Беннет Феннер демонстрирует live‑coding: превращает минимальный TypeScript-агент, умеющий только читать и редактировать файлы, в ACP-совместимого, использующего протокол для потоковой передачи токенов, отображения tool‑call’ов и даже проксирования файловой системы.
Зачем нужен Agent Client Protocol
С ростом числа AI-агентов и терминальных интерфейсов (Codex, Gemini CLI и т.д.) встала проблема: каждый агент имеет собственный формат общения. ACP унифицирует это — агент и клиент (редактор) взаимодействуют через стандартные JSON RPC‑сообщения. Для авторов агентом достаточно реализовать простой интерфейс из нескольких функций (инициализация, создание сессии, обработка промпта, отмена). Клиенты, в свою очередь, могут поддерживать разные способности (файловую систему, терминалы). Протокол уже работает в Zed, а адаптеры позволяют подключать существующих агентов без переписывания.
Базовый агент без ACP — две функции
Исходный кодинг-агент на TypeScript крайне минимален. Он использует эндпоинт Anthropic (опционально) и два инструмента: read_file (принимает путь, возвращает содержимое через fs) и edit_file (принимает путь, old_text и new_text, заменяет совпадение). Цикл агента — стандартный: прикрепить историю разговора к API, получить ответ модели; если модель завершает ход — выводит текст, если вызывает tool — выполнить локально, вернуть результат и повторить вызов API. Это ядро, к которому добавляется ACP.
Внедрение ACP: инициализация и сессии
Для ACP нужно реализовать интерфейс агента из TypeScript SDK. Первый метод — initialize: вернуть поддерживаемую версию протокола (текущую последнюю) и опциональные capabilities (для минимального агента — ничего). Аутентификация не требуется (API‑ключ берётся из .env). Второй — создание сессии: при запуске нового треда (в Zed или другом клиенте) агент генерирует случайный ID, сохраняет текущую рабочую директорию (из клиента) в карте состояний, возвращает ID клиенту. Позже в prompt запрос включает session_id; агент находит нужную сессию, извлекает текстовые блоки из промпта (игнорируя изображения) и запускает тот же цикл вызова модели.
Потоковая передача токенов через session update
Чтобы пользователь видел текст по мере генерации, в ACP есть понятие session update — асинхронные уведомления клиенту, не привязанные к ответу на запрос. Внутри цикла agent loop (при использовании Anthropic SDK) можно подписаться на событие stream.on('text'). На каждый полученный чанк отправляется session update типа agent_message_chunk с этим чанком и ID сессии. Клиент (Zed) отображает текст в реальном времени — так же, как при обычном чате.
Отображение tool‑call’ов и их результатов
Для инструментов (read_file, edit_file) тоже нужно отправлять session updates. Когда модель решает вызвать tool, агент сначала шлёт update с типом tool_call, указав заголовок, статус in_progress и метаданные (например, путь к файлу). После завершения работы инструмента отправляется второй update — tool_call_update, который меняет статус на завершённый и прикрепляет результат (содержимое файла для read, дифф для edit). В Zed эти tool‑call’ы отображаются в интерфейсе — пользователь видит, какой файл читается, какая замена производится, а для edit ещё и визуальный дифф (в ACP есть встроенный тип контента diff, включающий старый и новый текст).
Проксирование файловой системы через ACP
Клиент (Zed) может рекламировать возможность file_system — если она есть, агент не использует локальную файловую систему напрямую, а отправляет запросы на чтение/запись по ACP. Это важно, потому что буфер редактора может содержать незаписанные изменения (unsaved chains). Агент вызывает read_file через протокол, возвращая содержимое, видимое в редакторе. В демо это реализовано с помощью метода read_file из ACP, который проксируется на клиент. При edit‑tool результат — дифф; клиент применяет его к файлу в буфере.
Саморасширение агента: добавление терминального инструмента
Когда базовый агент уже умеет читать и писать файлы через ACP, его можно «бустрепнуть» — попросить добавить себе новый tool. Беннет передаёт промпт: «добавь инструмент для запуска терминала, используя такие‑то ACP‑апи». Модель генерирует код, описывающий tool terminal, который через ACP может выполнять команды в терминале клиента. После компиляции и перезапуска агент может запустить sleep 5; ls — результат команды отображается в интерфейсе Zed (создаётся терминал, выводится вывод).
Подключение и транспортировка
ACP работает поверх стандартного stdio (стандартный ввод/вывод). Команда JetBrains уже работает над удалённым транспортом. Для запуска агента в Zed достаточно указать команду: node path/to/agent.js. Никакого специального кода не требуется — Zed сам находит ACP-совместимый процесс.
📜 Transcript
en · 2 539 слов · 37 сегментов · clean
Показать текст транскрипта
I'm Bennett. I work at Zed, and we built an AI code editor, all written in Rust. And last year was kind of, as you probably all know, the rise of the AI coding agent terminal user interfaces with every major model provider, like building Cloud Code, Codex, Gemini, CLI, and so on. And so at Zed, we asked ourselves, how can we let users bring their agent of choice to our tool and enjoy a nice interface that is unified across all of them? And so that's why we decided we need some type of protocol called agent client protocol, which is similar to MCP or LSP. It's a JSON RPC based protocol. And the idea is basically that agents and clients can talk to each other through a unified interface. And yeah, it's online, it's open source, you can contribute if you want. At this point, we have a wide variety of agents already supporting this, either by an adapter that kind of translates the agents. native language to the ACP one. And then we have, for example, OpenCode and Cursor having ACP mode built into their CLI agents. And we also have a bunch of clients at this point, up to 40, that implement this, including OpenClaw, for example. OpenClaw itself is a client and a server, actually, like a client and an agent. And JetBrains and Obsidian and other people are supporting this. Great. So I'm going to do a live coding session. Let's see how well that goes. So, right. Basically, we have some pre-existing code. So this is Zed. Here I have some TypeScript code. Also, bear with me. I'm a Rust developer. I have basically zero clue about TypeScript. So if you see anything that you don't do in TypeScript, tell me afterwards. But yeah, like here's a very minimal coding agent that just doesn't support ACP, but it's kind of the bare minimum you need to like build a coding agent. So all it has is really like two tools, one to read a file and one to edit an existing file. Yeah, this is like pretty basic. It provides, the model has to provide a path and it has to provide an old text that is like then just replaced with new text and that's kind of everything. And then we have, in this case, like I'm using entropic. There's a way to prompt the agent with, yeah, the user can prompt the agent, which is the function that we were going to call here. And then we enter the agent loop. And that is kind of like the way all agents basically work is the model APIs are stateless. So you just attach some conversation up to this point. You call the endpoint. In this case, it's like the entropic API. And then we get a message from the model, and either it can be an end turn. That means the model decided to just output some text and do nothing, or it can call a tool. And in this case, it would be a read or a write tool call. And then in the case of a tool call, we run, for example, the edit file tool call locally, collect the result, and then send it up to the model again. That's where there's this loop. And then we call the API again with the conversation up to that point, and that's kind of everything. And then we have like these, this like handle tool call function here, which, yeah, handles like read and write tool calls, which does what you expect. We get the path, we read it from the file system, and we return some result. Right, so now the question is, how do we make this thing ACP compatible? And hopefully we can do it in... 10 minutes, so let's see. So yeah, I have some boilerplate here. In this case, I'm using the TypeScript SDK, as I said. And the way this works, you implement the agent interface provided by this library. And then you have to, at minimum, implement these three, four functions. So the first one that we're looking at is kind of this initialize here. All we really have to do here is respond with the protocol version we support, which in this case is just the latest. There's also some capabilities like client and the agent can itself advertise capabilities of stuff it supports, but we're building a very minimal coding agent, so we don't support anything outside that's necessary, I guess. Then for us, authentication is irrelevant since it's just using my API key from an . And then there's this concept of sessions in ACP. So basically, every time you start a thread in Z or in a different editor or client, you call a new session. And in a session, you can prompt. And then from there on, you can get your output. So all we really do here is we generate a random ID. Then we instantiate. this coding agent with the current working directory, which we get from the client. We store it in our internal map and then just return the ID to the client so that the client knows the ID. That is then used inside prompt because what prompt does in this case, the prompt request, if we go to the definition here, all this is doing is It provides a prompt, which is an array of content blocks that can be text, images, whatever, the session ID for reference. And so all we have to do here is we look it up in our internal state. We get the relevant agent for that current session. And then I have a helper function here, which just, in this case, ignores everything that's not a text because we don't support images and stuff like this. And then we call this prompt function I showed. which then runs the tool calling loop. And so then just like as a nice to have feature, we also support cancellation. That's also pretty simple. So these are kind of like the four minimal things we have to implement. And I have this hooked up in Z just by there's no special code. All I have is kind of like I tell Z there's some ACP compatible agent and you can run it with node and then my path to that agent.js file. Let's do that in here as well. When I run, I'm going to build, and then I'm going to restart, launch a new ACP demo agent. You can see if I ask it for something, you can see a weight indicator, and now it's done. So nothing. which is what we expect, right? Because what we can see here, if I go over here, we have some kind of ACP debug view in Z to make it easier for us to develop. And you can see Z sends a session new request. We respond with a session ID. And as soon as I type in something, we get prompt. And now we've got a stop reason, right? So these red arrows come from the adapter. that we are building from the agent, but it's not outputting tokens. Behind the scenes, it's obviously Anthropic is giving us tokens, but how do we make them show up in Z? That's the next thing that we want to focus on. The first thing that we need to do, our coding agent itself needs to have a way to send something over the connection. What we're going to do here is the coding agent is going to take the ACP connection and then also the session ID. Then inside here, we have to provide this connection and the ID. Then if we go, okay, I'm just going to close this. If we go back here, now inside of prompt, this is the Anthropic SDK. In this case, you can just use this stream on Yeah, that's like an entropic SDK. You can react to a text event. So every time we basically get a chunk, we send this kind of what ACP calls a session update. As I said, a session is like a single thread or a conversation. And then you can send updates, kind of like notifications to the client. that are not like a usual request response. It can happen at any time, and the client reacts to it. And so here, we are associating the session update with a session ID. And then there's a type of session updates. There are multiple. We will see more in a second. But this agent message chunk is just like, OK, hey, here's some new output from the model. And so if we build again, we go back to Z. I'm going to have to restart the agent. I go here. And I say, hello. Hello, hello. Well, here's Opus talking. You can see we're streaming in these individual chunks that we got from Anthropix. So, so far, so good. Let's hope the demo gods stay with us. So that's one piece. The next thing, though, is I want to, it's a coding agent. It's supposed to edit code. So I can actually tell it, already because we have tool support, right? I can tell it to read, for example, this like helpers.ts file and it's gonna give me, if the Wi-Fi isn't too slow, it's giving me a description of what this file actually contains. Like if you look here, that's basically the description of the file. But again, you don't see it in the UI, right? So now we need to emit more session updates, similar to like what we did for text. So if we actually go towards the read file, kind of what we need to do here is like the way it works usually in ACP, like you emit an initial tool call update, which I'm going to paste in here. So yeah, again, we're emitting a session update. In this case, it's a type of tool call. And then there are multiple properties we can specify, for example, like the title, kind of some kind of metadata. that Z uses, for example, for using some icons and stuff like this. Then we indicate that the status is in progress and you can also associate two calls with actual locations. Now we're signaling over ACP that something is in progress, this tool call. Then we also want to, at the end, once the tool call has finished, like at the bottom here, we want to send another update. In this case, it's not tool call itself, but it's a tool call update. So you have to emit on the agent, you have to emit a tool call, like session update of type tool call, and then once the client knows about it, you can emit updates for that. And this is the way that we set the status, and we can also return the content. And then one additional thing that I'm going to do here is the... Instead of, like you can see here, we're calling, I guess it's a bit... hard to see, but like here we're calling fsreadfile, right? So we're just using the native file system APIs. But ACP actually proxies the file system too. Like the client can provide a file system capability, and if it does, we can proxy those file system two calls over ACP. And it kind of makes sense, for example, for an editor, right? You want to... If I have unsafe chains in my buffer, they're not actually on the file system, but the agent should still see them. So that's why we call this like a retext file over ACP. And so now if we go back here and hit npm run build, and then go back here, restart, and say read this file, oops, not Rust, then we should be seeing Yeah, so now we see two calls, right? Okay, something is going wrong because everything is duplicated. Yeah, but like here you can see the output of the actual file. And because I think I'm low on time, we're basically going to do the same for edit file. And I'm going to compile again. And if I again restart. and say, add a comment at the top of source agent.type script, then you're going to see hello world is good. Hopefully, Opus is going to decide to add hello world at the top of the file. And we actually get a diff here, because over ACP we send a diff of the file. Is it complicating the first? Yeah, I think there's something going wrong with the connection. Yeah, no time to debug right now. Sorry. Am I out of time, or do I still have? OK, maybe we can see. So now that, OK, just another minute. Now that the coding agent supports reading and writing files, we can actually. Oh, great. OK, then I don't need to rush. Now we can kind of bootstrap the agent itself, right? Like the coding agent supports reading and writing files. I can just ask it, I prepared a prompt here, to add a terminal tool to itself. Let's see how this works out with the duplication we're seeing. But basically, I'm telling it something about the ACP APIs. And there's some APIs in ACP, which also, that's another capability of the client, where the client can advertise that it supports creating terminal and managing terminals for the agent. The agent can also do it itself, of course. But it's a nice way for us to add some more interactivity. And so the agent here decided to add a new tool description. Now it, yeah, I'm Vytecoding basically this terminal tool. And let's see if it actually builds. It does. Well, that's good. And then, again, I'm going to restart the agent. I'm going to run our demo agent, and I'm going to ask it to run sleep5ls. Let's see. There you go. You can see a terminal running, sleeping five seconds. There is the output, and there's, yeah. And that's it, basically. Thank you. Yeah, so that's how you kind of build an ACP compatible coding agent in 15 minutes or so. Yeah, if you want to. Check it out. Just go to agentclineprotocol.com. In case anyone wants the demo code, but please don't use it in production. It's all agent generated. It's not uploaded yet, but it's an empty repository. I'm going to upload it in a second. Yeah. Thank you for listening. Happy to answer questions. That's it. Yeah. The diffs, yeah, we have like in ACP, there are multiple content types, and one content type is diff. And so the agent sends old text, new text, and then that does the diffing for you. Yeah. Yeah. Cool. Do I need to go or can I? One more question. Yes. Yeah, the connection works over standard.io. There are some folks, I think, from the JetPrint people are working on remote transport, which we're going to have soon, I think. So yeah. But right now it works over standard L.O.
⚙️ Pipeline jobs
| Stage | Status | Att. | Updated | Error |
|---|---|---|---|---|
| download | done | 1/3 | 2026-07-20 13:35:12 | |
| transcribe | done | 1/3 | 2026-07-20 13:35:22 | |
| summarize | done | 1/3 | 2026-07-20 13:35:43 | |
| embed | done | 1/3 | 2026-07-20 13:35:45 |
📄 Описание YouTube
Показать
In this session, we'll be building a coding agent that implements ACP — covering protocol design, session lifecycle management, and handling tool calls. The session ends with a live demo of the finished agent running inside Zed, showing what ACP looks like in practice from both sides of the protocol.