Написать своего агента под Devin Desktop кажется большой задачей - как встроить полноценный продукт в чужой редактор, с его меню, панелями и правилами отрисовки. ACP снимает большую часть этой тяжести, но взамен требует понять контракт: что именно Desktop ждёт от процесса, чтобы вообще считать его агентом. Пока контракт не в голове, работа идёт наугад, и любая пауза выглядит поломкой.
Наивно представлять интеграцию как REST-API с эндпоинтами или как плагин, который зовут функциями и который отвечает возвращаемым значением. Отсюда ожидание тяжёлой обвязки: регистрация возможностей, схемы запросов, коллбэки на каждое событие. Этот образ пугает раньше, чем начата работа, и толкает искать сложность там, где её нет.
На деле модель проще и жёстче. Desktop запускает агента как локальный subprocess и общается с ним по JSON-RPC через stdio - тот же транспорт, которым ACP стандартизирует связь редактора и агента: агент это процесс, читающий stdin и пишущий stdout, а не сервер на порту. Минимальный контракт - четыре метода. initialize согласует версию протокола и заявляет возможности. session/new создаёт сессию под рабочую директорию. session/prompt принимает сообщение и ведёт turn. session/cancel прерывает начатую работу. Всё остальное надстраивается над этими четырьмя.
Самое важное происходит внутри turn, и это не один ответ, а поток. Пока агент работает, он стримит обратно notifications: session/update с кусками текста ассистента по мере генерации, tool_call и tool_call_update для отображения того, что делают инструменты, session/request_permission на чувствительные действия. Заканчивается turn значением stopReason - end_turn при нормальном завершении, cancelled при отмене, max_tokens при исчерпании лимита. Клиент не опрашивает агента в цикле, а слушает его поток и реагирует на каждое событие.
Почему так, а не запрос-ответ. Агент - это не короткая транзакция, а длящийся процесс с промежуточными результатами, правами и возможностью отмены на полпути. Поток notifications и есть способ показать человеку, что происходит, пока turn ещё не закончен: какие инструменты вызваны, что требует подтверждения прямо сейчас, каким планом агент идёт к цели. Свести это к одному финальному ответу значило бы спрятать всю середину работы - именно ту, где человек и принимает решения.
Разработка своего агента - это цикл правок, и Desktop его учитывает. Запись в локальный registry описывает запуск, как разобрано в предыдущей главе, а после пересборки бинарника не нужно перезапускать весь редактор со всеми открытыми сессиями: команда Reload ACP Connections в палитре переподнимает только ACP-соединения. Мелочь на вид, но за сессию отладки она экономит десятки полных перезапусков и держит остальной контекст нетронутым.
У встраивания есть документированные ограничения, и знать их дешевле, чем упереться в них вслепую. Desktop не выводит ACP session modes напрямую в интерфейс - режимы реализуют не как отдельный UI, а как session config с категорией "mode". Создание терминала тоже не advertised: агент не получает от Desktop терминал, а выполняет команды сам как subprocess и отдаёт их вывод через tool call updates. Это не баги и не временные пробелы, а форма контракта: то, чего в нём нет, нельзя дослать сбоку, и попытка обойти границу лишь тратит время.
Свой ACP-агент оправдан, когда нужна логика или экосистема, которой нет в Devin Local, и вы готовы держать на себе процесс, его права и его обновления. Для разовой правки это заведомый overkill; для устойчивого, повторяющегося сценария - способ вписать чужого исполнителя в те же поверхности Desktop, не ломая их общую модель прав и отображения. Граница проста: разовое - штатными средствами, устойчивое - через контракт.
Проверять готовность стоит по контракту, а не по ощущению "вроде запустилось". Отвечает ли агент на initialize согласованной версией протокола. Создаёт ли session/new сессию именно под нужную директорию. Идут ли во время session/prompt осмысленные session/update и tool_call, а не тишина до самого конца turn. Приходит ли request_permission там, где действие действительно чувствительно. Возвращается ли внятный stopReason, а не обрыв. Пять точек контракта проверяются по очереди, и если какая-то молчит, дефект локализован именно в ней, а не размазан по всему агенту.
Типичные провалы растут из непонимания транспорта. "Агент завис" - turn не вернул stopReason, и клиент ждёт его вечно, хотя работа давно кончилась. "Ничего не видно" - работа шла, но notifications не стримились, и человек лишён середины, по которой судит о ходе. "Режимы пропали" - их ждали в UI вместо session config с категорией mode. "Терминал не открылся" - его и не будет, команды идут subprocess-ом и видны как tool call updates. Признак один: агента писали как сервер, обязанный вернуть ответ, а не как процесс, обязанный вести поток. Держите в голове поток notifications и завершающий stopReason - и контракт перестаёт быть загадкой.