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 перестаёт быть пугающей технической страницей. Он становится вопросом архитектуры продукта: где пользователь просит агента о работе, где агент показывает ход, где человек подтверждает действие и где доступ заканчивается.