Codex app-server: как встроить агента в продукт и не открыть лишний доступ
Codex можно использовать как отдельное рабочее место: открыть задачу, дать контекст, получить результат. Но иногда команде хочется другого: встроить агента прямо в собственный продукт, редактор, внутреннюю панель или клиентский интерфейс.
Вот здесь появляется Codex app-server. Это не просто ещё один способ вызвать модель. Это интерфейс, через который rich client может разговаривать с Codex: создавать диалоги, запускать turns, получать события прогресса, показывать approvals и хранить историю.
Главная ошибка — начинать с протокола. У app-server есть JSON-RPC, transports, initialization, thread, turn, item. Но для владельца продукта первый вопрос другой: что именно пользователь сможет делать с агентом, где виден результат и какая граница доступа не должна быть пересечена.
Почему app-server не нужен для каждой автоматизации
Если нужно запустить Codex в CI, обработать job, сделать проверку в pipeline или автоматизировать повторяемое действие, app-server обычно не первый выбор. Официальная документация прямо говорит: для автоматизации jobs и CI нужно использовать Codex SDK.
App-server нужен тогда, когда Codex становится частью продукта. Не скриптом рядом. Не задачей в фоне. А пользовательским рабочим контуром: человек видит conversation, запускает agent work, читает progress, подтверждает действия и получает итог.
Поэтому вопрос не «можем ли мы подключиться». Вопрос: «строим ли мы пользовательский агентный интерфейс». Если нет, лучше не усложнять.
Что документация говорит о Codex app-server
В документации OpenAI app-server описан как интерфейс, который Codex использует для rich clients, например для VS Code extension. Он подходит для глубоких интеграций внутри собственного продукта: authentication, conversation history, approvals и streamed agent events.
Протокол построен вокруг JSON-RPC 2.0 сообщений. Поддерживаются разные transports: stdio, WebSocket, Unix socket и режим off. Для WebSocket есть health endpoints, но сам WebSocket transport отмечен как experimental и unsupported.
Документация отдельно предупреждает про удалённое exposing. Локальные listeners вроде ws://127.0.0.1:PORT подходят для localhost и SSH port-forwarding. Но non-loopback WebSocket listeners могут во время rollout разрешать unauthenticated connections by default, поэтому перед удалённым доступом нужно настраивать WebSocket auth.
Основные primitives простые: thread — conversation, turn — один запрос и последующая работа агента, item — единица input или output. Клиент сначала делает initialize, затем starts or resumes thread, starts turn, читает события и ждёт turn completed.
Это не готовый продукт. Это строительный слой для продукта.
Когда это именно продуктовая интеграция
App-server имеет смысл, если у вас есть пользовательский сценарий, где человек должен видеть работу агента внутри вашего интерфейса.
Например: внутренний редактор требований, где менеджер запускает Codex для проверки задачи; админка продукта, где Codex готовит change plan; рабочая область поддержки, где агент анализирует issue и предлагает действия; IDE-like среда, где пользователь смотрит agent events и approvals.
Общий признак такой интеграции: пользователю важен не только финальный текст, но и процесс. Он должен видеть, что агент начал turn, какие items появляются, где нужны approvals, когда работа завершилась и какой artifact получился.
Если процесс невидим пользователю, возможно, app-server избыточен.
Что дать Codex перед проектированием
Чтобы Codex помог спроектировать такую интеграцию, ему нельзя давать только фразу «встрой app-server». Нужно дать продуктовую рамку.
Минимальный input:
| Блок | Что описать |
|---|---|
| Сценарий | что пользователь делает в продукте |
| Пользователь | кто запускает агента и с какими правами |
| Artifact | что агент должен вернуть |
| Transport | локальный, stdio, Unix socket, WebSocket или пока неизвестно |
| Access boundary | что нельзя открывать агенту или клиенту |
| Lifecycle | где initialize, thread, turn, events, completion |
| Approvals | какие действия требуют человека |
| Failure behavior | что показать при отказе, overload или auth problem |
Так Codex проектирует не «подключение», а управляемый продуктовый контур.
Как выбрать transport и не потерять границу доступа
Transport — это не техническая мелочь. Это место, где начинается security boundary.
Для локального клиента проще думать про stdio или локальный listener. Для продукта с отдельным UI может возникнуть WebSocket. Но документация прямо называет WebSocket experimental and unsupported, а удалённое exposing требует auth.
Поэтому в плане интеграции нужно отдельно спросить:
Какой transport подходит для нашего сценария и почему?
Можно ли оставить его local-only?
Если нужен WebSocket, где включается auth?
Что запрещено передавать через командную строку?
Что клиент делает при отказе сервера или перегрузке?Хороший план не прячет эти вопросы в конец. Он начинает с них.
Как должен выглядеть артефакт интеграции
Результат работы Codex должен быть проверяемым. Не «мы подключим app-server», а конкретная схема.
В нормальном artifact должны быть:
- выбранный сценарий и причина, почему app-server вообще нужен;
- transport decision;
- auth boundary;
- initialization flow;
- thread and turn flow;
- список user-visible events;
- approvals;
- retry/failure behavior;
- что остаётся outside scope;
- следующий engineering step.
Если Codex предлагает сразу писать клиент, но не описывает auth, events и human approval points, это ещё не план. Это набросок.
Как проверить план без кода
Непрограммисту не нужно читать JSON-RPC schema. Нужно проверить управляемость.
Первый вопрос: понятно ли, кто пользователь и что он может запустить?
Второй вопрос: видно ли, где агент работает, где progress, где итог?
Третий вопрос: есть ли граница доступа? Если план говорит «откроем WebSocket», но не говорит про auth и local/remote boundary, план сырой.
Четвёртый вопрос: есть ли человеческие approvals? Если агент может совершать действия без явной точки подтверждения, нужно остановиться.
Пятый вопрос: есть ли fallback? Документация описывает, что при перегрузке server может вернуть retry-later ошибку. Пользовательский интерфейс должен понимать, что делать не только в счастливом сценарии.
Где остаётся человеческое решение
Codex может помочь спроектировать client flow. Он может предложить transport, описать lifecycle, перечислить события и риски. Но он не должен сам решать, что ваш продукт готов открыть агентный контур пользователям.
Человек решает, нужен ли app-server или достаточно SDK. Человек решает, какие пользователи видят агента. Человек решает, где нужны approvals. Человек решает, можно ли включать WebSocket и при каких auth constraints. Человек решает, какие события показывать в UI, а какие оставить внутренними.
App-server хорош именно там, где нужен продуктовый агентный интерфейс. Но продуктовый интерфейс начинается не с подключения. Он начинается с решения, где граница.
Шаблон запроса
Можно дать Codex такой запрос:
Помоги спроектировать интеграцию Codex app-server для нашего продукта.
Сценарий:
[что пользователь делает в продукте]
Пользователь и права:
[кто запускает агента, какие действия разрешены]
Нужный artifact:
[что агент должен вернуть: план, diff, diagnosis, summary, questions]
Transport:
[stdio / local WebSocket / Unix socket / пока неизвестно]
Access boundary:
- что нельзя открывать;
- какие данные нельзя отправлять;
- какие действия требуют approval;
- можно ли использовать только local listener.
Lifecycle:
Опиши initialize, thread/start или thread/resume, turn/start, streamed events, turn/completed.
Failure behavior:
Что показывать пользователю при auth error, overload/retry later, interruption или failed turn.
Результат:
1. скажи, нужен ли app-server или лучше SDK;
2. дай transport decision;
3. опиши auth boundary;
4. дай user-visible event map;
5. перечисли approvals;
6. дай engineering handoff без секретов и без production token values.Так app-server перестаёт быть пугающей технической страницей. Он становится вопросом архитектуры продукта: где пользователь просит агента о работе, где агент показывает ход, где человек подтверждает действие и где доступ заканчивается.