8. HTTP API
🔵 Сервер отдаёт HTTP-управляющую плоскость плюс чат-эндпоинт, стримящий Server-Sent Events (SSE). Список эндпоинтов печатается при старте в логе.
8.1 Эндпоинты
Страницы и статика (публичные)
| Endpoint | Описание |
|---|---|
GET / |
markdown-сайт (лендинг) |
GET /site/ · GET /site/:page |
тот же сайт в подпути |
GET /sessions · /budgets · /cron · /workflows · /dashboard |
админ-UI |
GET /agents/:name |
страница одного агента |
GET /widget · /widget.js · /dist/* |
виджет и его ассеты |
GET /demo · /demo/overlay |
демо виджета / оверлей поверх живых сайтов |
GET /distrib/* |
архивы дистрибутива (linux/windows zip + sha256, android apk) |
GET /proxy?url=… · /proxy/content?url=… |
HTTP-прокси для фронтенда |
GET /login · POST /login · POST /logout |
web-логин админ-UI |
Публичный API
| Endpoint | Описание |
|---|---|
GET /health |
healthcheck |
GET /v1/models |
доступные модели |
GET /v1/templates |
шаблоны агентов (welcome, tools) |
POST /v1/chat/completions |
чат с агентом (SSE, собственный формат) |
POST /v1/continuations |
следующая страница лениво-пагинированной таблицы |
POST /v1/sessions/:id |
закрыть сессию (публичный — виджет шлёт sendBeacon) |
POST /v1/inbound/:template |
разовый ход агента от внешней системы (JSON, опционально bearer) |
POST /v1/user-ticket |
выдать HMAC-подписанный токен идентичности |
POST /v1/feedback |
like/dislike по ответу |
POST /v1/workflows/:name/webhook |
webhook-триггер воркфлоу (защищён токеном воркфлоу) |
/ext/** (кастомные) |
http_route / static_route — пользовательские REST-эндпоинты и статика, добавленные глобально (config/routes/) или агентами (route_files:); auth public/bearer/token_env/user на маршрут (см. §21) |
Админ-API 🔒
| Endpoint | Описание |
|---|---|
GET /v1/agents · POST /v1/agents |
список агентов · создать агента |
GET /v1/agents/:name |
снапшот одного агента |
DELETE /v1/agents/:pid |
убить агента и потомков |
POST /v1/agents/:name/bench/run |
запустить бенчмарк шаблона |
GET /v1/agents/:name/bench/runs · /bench/runs/:id |
история и деталь бенчмарка |
GET /v1/sessions |
список сессий |
GET /v1/sessions/:id · DELETE /v1/sessions/:id |
деталь сессии · закрыть |
GET /v1/budgets |
бюджетные группы и процессы |
GET /v1/cron · DELETE /v1/cron/:name |
задания cron и история · удалить |
GET /v1/workflows · POST /v1/workflows/:name/run |
воркфлоу · запустить |
GET /v1/workflows/runs/:id · /runs/:id/events |
запуск воркфлоу · его события |
GET /v1/dashboard |
системный дашборд (нагрузка, токены, серии) |
GET /v1/feedback/stats |
статистика оценок |
GET /v1/users |
соответствия канал → пользователь |
POST /v1/users/link · POST /v1/users/:id/unlink |
слить/разъединить идентичности |
GET /v1/memory/:scope |
инспекция долгой памяти скоупа |
🔒 = требует Authorization: Bearer <токен>, когда настроена админ-auth
(см. §8.3).
Пагинация
Списочные эндпоинты (/v1/sessions, /v1/agents, /v1/templates,
/v1/budgets) принимают ?limit=N&offset=M. limit — размер страницы (обрезается
к 1–1000, по умолчанию 100), offset — сколько строк пропустить (по умолчанию 0).
Ответы включают total (а /v1/sessions — ещё history_total). /v1/sessions
добавляет history_truncated: true, когда прошлых сессий больше текущей страницы.
8.2 Chat completions
POST /v1/chat/completions стримит Server-Sent Events (SSE). Путь и форма
запроса похожи на OpenAI, но формат ответа свой — он не совместим с
OpenAI-SDK (нет choices[].delta, нет data: [DONE]).
curl http://127.0.0.1:3000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "ngu-agent",
"messages": [{"role": "user", "content": "Как поступить в НГУ?"}]
}'
Поля запроса:
| Поле | Обязат. | Смысл |
|---|---|---|
model |
да | имя шаблона (ngu-agent, drom-agent, …) или встроенный assistant |
messages |
да | список {role, content}; в контекст попадают роли user и system |
session |
нет | токен сессии — одна сессия = один изолированный агент. Опусти — legacy-режим (общий агент шаблона) |
stream |
нет | принимается для совместимости, игнорируется — всегда стрим |
temperature |
нет | принимается, игнорируется |
max_tokens |
нет | принимается, игнорируется |
Заголовок X-User-Id задаёт user_id для per-user-бюджетов и скоупа памяти.
Когда настроен auth.web_user_secret_env, сервер требует HMAC-подписанный
X-User-Token (из POST /v1/user-ticket); см. §8.8.
Ответ (события SSE)
Тело — поток именованных SSE-событий; done — единственное, которое есть всегда.
event |
Полезная нагрузка data |
|---|---|
token |
{"content": "<токен>", "final": bool} |
tool_call |
{"name": "<инструмент>", "body": "<рендер вызова>"} |
tool_progress |
{"name": "<инструмент>", "body": "<прогресс>"} |
tool_result |
{"name": "<инструмент>", "body": "<рендер результата>"} |
result_blocks |
`{"name": "<инструмент>", "blocks": [ {type: "text" |
budget |
{"tokens": n, "prompt": n, "completion": n, "cached": n, "total": n, "cost": "$x.xxxx"} |
done |
{} — стрим завершён |
result_blocks несёт визуальный слой инструмента (графики, таблицы, текст,
файлы); блок table может включать continuation: {id} — POST /v1/continuations
с этим id вернёт блоки следующей страницы, а блок file несёт name, mime и
data (base64) для скачивания. См. общие поля.
Ошибки (неизвестная модель, ошибка сессии, исчерпание бюджета, rate-limit)
сообщаются как token-события внутри 200, а не HTTP-кодом ошибки.
8.3 Auth
Эндпоинты 🔒 принимают либо Authorization: Bearer <токен> (для API-клиентов),
либо cookie agentos_session (для браузера после web-логина).
- Bearer-токены — из
auth.admin_tokens(картаимя → токенвconfig/server.yaml) илиauth.admin_token_env(списокимя=токен). Имя токена пишется в access-лог для аудита. - Web-логин — из
auth.users(логин → пароль).POST /loginпроверяет креды и ставитHttpOnlycookie (12 ч); логин пишется в access-лог.POST /logoutочищает cookie. Браузерные UI (/sessions,/budgets) редиректят на/loginпри 401.
Если auth настроена, но ни один credential не разрешается — админ-API отдаёт 401 на каждый запрос (fail-closed). Без токенов и пользователей админ-API открыт (dev-режим).
# API-клиент (bearer)
curl -H 'Authorization: Bearer <token>' http://127.0.0.1:3000/v1/sessions
# Web-логин (браузерный поток)
curl -c cookies.txt -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"<password>"}' \
http://127.0.0.1:3000/login
curl -b cookies.txt http://127.0.0.1:3000/v1/sessions
8.4 Встраивание виджета
<script src="http://127.0.0.1:3000/widget.js"></script>
Виджет отдаётся с /widget, общается с /v1/chat/completions и закрывает
сессии через POST /v1/sessions/:id (sendBeacon). Полный разбор виджета
(конфигурация, выбор модели, демо) — в §19.
8.5 Прокси
/proxy и /proxy/content скачивают URL для фронтенда. Ограничения:
- только
http/https, порты 80/443; - хосты только из
proxy.allow_hosts(когда список непуст); - приватные, loopback, link-local и reserved-диапазоны (включая
169.254.169.254) блокируются всегда, на каждом редиректе.
8.6 Healthcheck
curl http://127.0.0.1:3000/health
8.7 HTTPS / TLS
Задай AGENT_OS_TLS_CERT и AGENT_OS_TLS_KEY (пути к PEM) — сервер поднимет
HTTPS. Иначе — обычный HTTP на AGENT_OS_BIND (по умолчанию 127.0.0.1:3000).
8.8 Входящий webhook и идентичность
Входящий webhook
POST /v1/inbound/:template — внешняя система (CRM, ERP, мониторинг, бэкенд
формы, …) запускает один ход агента и синхронно читает ответ — без SSE, без
массива messages.
curl -X POST http://127.0.0.1:3000/v1/inbound/ngu-agent \
-H 'Content-Type: application/json' \
-d '{"message":"какие общежития у НГУ?","session_id":"crm-123"}'
Запрос:
| Поле | Обязат. | Смысл |
|---|---|---|
message |
да | сообщение пользователя |
session_id |
нет | стабильный id для переиспользования контекста; опущен = новая сессия |
user_id |
нет | идентичность конечного пользователя (Origin::Webhook); опущен = аноним |
Ответ: {"answer": "...", "status": "success", "session_id": "...", "cost_usd": 0.0, "prompt_tokens": n, "completion_tokens": n, "data": null}.
Auth — bearer-токен при заданном inbound.token_env (пусто = открыто, dev).
Токены идентичности конечных пользователей
Когда auth.web_user_secret_env указывает env с HMAC-секретом, виджет получает
подписанный токен и шлёт его как X-User-Token вместо самоназванного
X-User-Id:
curl -X POST http://127.0.0.1:3000/v1/user-ticket \
-H 'Content-Type: application/json' -d '{"user_id":"cw-browser-id"}'
# → {"user_id":"cw-browser-id","token":"cw-browser-id.<hmac>"}
Без секрета сервер доверяет X-User-Id напрямую (dev-режим).
Канонические идентичности пользователей
GET /v1/users перечисляет соответствия (origin, external_id) → canonical_id;
оператор может слить два канальных id (например web-id и Telegram-id), чтобы
они делили память, бюджет и состояние:
curl -X POST http://127.0.0.1:3000/v1/users/link \
-H 'Authorization: Bearer <token>' \
-d '{"survivor":"cw-browser-id","merged":"tg:123"}'
Слияние мигрирует scoped state store, долгую память и in-memory бюджетные ведра;
дальнейшие сессии обоих каналов резолвятся в survivor.
8.9 Обратная связь (feedback)
Виджет шлёт оценки ответов (like / dislike / cleared) в POST /v1/feedback
(публичный), а статистика — в GET /v1/feedback/stats (🔒).
Оценка замыкает цикл качества: хук memory по лайку/дизлайку поднимает/опускает
importance фактов, вспомненных в этом ходе (§6.6). Тело запроса включает
value, user_text, session.