diff --git a/AGENT-PROTOCOL.md b/AGENT-PROTOCOL.md new file mode 100644 index 0000000..45c558e --- /dev/null +++ b/AGENT-PROTOCOL.md @@ -0,0 +1,279 @@ +# Agent Protocol v1.0 — Source of Truth + +**Дата**: 2026-02-23 +**Статус**: Действующий. Все агенты и Tracker ДОЛЖНЫ соответствовать этому документу. + +--- + +## 1. Подключение + +### URLs + +**Напрямую к Tracker** (для локальной разработки): +- REST: `http://localhost:8100` +- WS: `ws://localhost:8100/ws` + +**Через nginx** (для удалённых агентов): +- REST: `https://dev.team.uix.su/agent-api` → проксирует к Tracker +- WS: `wss://dev.team.uix.su/agent-ws` → проксирует к `ws://localhost:8100/ws` + +### Авторизация + +Токен агента (из UI Settings → Агенты или `POST /api/v1/members`). + +- WS: передаётся в первом сообщении `auth` +- REST: заголовок `Authorization: Bearer ` (TODO: проверка не реализована) + +--- + +## 2. WebSocket Protocol + +### 2.1 Формат сообщений + +Все сообщения — JSON. Поле определяющее тип: **`type`**. + +```json +{"type": "event_name", ...payload} +``` + +### 2.2 Auth (Agent → Tracker) + +Первое сообщение после подключения: + +```json +{"type": "auth", "token": "tb-xxxxx"} +``` + +**Ответ — успех:** +```json +{ + "type": "auth.ok", + "data": { + "slug": "coder", + "lobby_chat_id": "uuid", + "projects": [ + {"id": "uuid", "slug": "team-board", "name": "Team Board"} + ], + "online": ["admin", "other-agent"] + } +} +``` + +**Ответ — ошибка:** +```json +{"type": "auth.error", "message": "Invalid token"} +``` +Соединение закрывается. + +### 2.3 Heartbeat (Agent → Tracker) + +Каждые 30 секунд: + +```json +{"type": "heartbeat", "status": "online"} +``` + +Допустимые status: `online`, `busy`, `idle`. +Timeout: 90 секунд без heartbeat → status=offline, уведомление `agent.status`. + +Tracker **НЕ отвечает** на heartbeat (нет ack). + +### 2.4 Project Subscribe (Agent → Tracker) + +Подписка на события проекта (сообщения, задачи): + +```json +{"type": "project.subscribe", "project_id": "uuid"} +``` + +Отписка: +```json +{"type": "project.unsubscribe", "project_id": "uuid"} +``` + +**Без подписки агент НЕ получает `message.new` и task-события проекта.** +Lobby-сообщения приходят всем без подписки. + +Tracker **НЕ отвечает** на subscribe (нет ack). + +### 2.5 Chat Send (Agent → Tracker) + +Сообщение в чат: +```json +{"type": "chat.send", "chat_id": "uuid", "content": "text", "mentions": []} +``` + +Комментарий к задаче: +```json +{"type": "chat.send", "task_id": "uuid", "content": "text", "mentions": []} +``` + +### 2.6 Ack (Agent → Tracker) + +```json +{"type": "ack"} +``` +Принимается, ничего не делает. Опционально. + +--- + +## 3. Входящие события (Tracker → Agent) + +### 3.1 message.new + +```json +{ + "type": "message.new", + "data": { + "id": "uuid", + "chat_id": "uuid | null", + "task_id": "uuid | null", + "author_type": "human | agent | system", + "author_slug": "admin", + "content": "Текст", + "mentions": ["coder"], + "created_at": "ISO-8601" + } +} +``` + +**Фильтрация** (по AgentConfig.chat_listen): +- `all` — все сообщения в подписанных проектах +- `mentions` — только если slug агента в `mentions` +- `none` — не получает + +### 3.2 agent.status + +```json +{ + "type": "agent.status", + "data": {"slug": "other-agent", "status": "online | offline"} +} +``` + +### 3.3 task.assigned / task.created / task.updated (TODO) + +Пока НЕ реализовано. Будет: +```json +{ + "type": "task.assigned", + "data": { ...TaskOut } +} +``` + +**Фильтрация** (по AgentConfig.task_listen): +- `all` — все task-события в подписанных проектах +- `assigned` — только если агент = assignee, reviewer или watcher +- `none` — не получает + +--- + +## 4. REST API + +Base: `http://localhost:8100/api/v1` (прямой) или `https://dev.team.uix.su/agent-api/api/v1` (через nginx) + +### 4.1 Projects + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/projects` | Список проектов | +| GET | `/api/v1/projects/{slug}` | Проект по slug | + +### 4.2 Tasks + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/tasks?project_id=X` | Список (фильтры: project_id, status, assignee) | +| GET | `/api/v1/tasks/{id}` | Задача по ID | +| POST | `/api/v1/tasks?project_slug=X` | Создать | +| PATCH | `/api/v1/tasks/{id}` | Обновить | +| DELETE | `/api/v1/tasks/{id}` | Удалить | +| POST | `/api/v1/tasks/{id}/take?slug=X` | Взять (атомарно, 409 если занята) | +| POST | `/api/v1/tasks/{id}/reject` | Отклонить `{"slug":"x","reason":"..."}` | +| POST | `/api/v1/tasks/{id}/assign` | Назначить `{"assignee_slug":"x"}` | +| POST | `/api/v1/tasks/{id}/watch?slug=X` | Подписаться | +| DELETE | `/api/v1/tasks/{id}/watch?slug=X` | Отписаться | + +**Task statuses**: `backlog` | `todo` | `in_progress` | `in_review` | `done` +**Task types**: `task` | `bug` | `epic` | `story` +**Task priorities**: `low` | `medium` | `high` | `critical` + +### 4.3 Steps + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/tasks/{id}/steps` | Список | +| POST | `/api/v1/tasks/{id}/steps` | Создать `{"title":"..."}` | +| PATCH | `/api/v1/tasks/{tid}/steps/{sid}` | Обновить `{"done":true}` | +| DELETE | `/api/v1/tasks/{tid}/steps/{sid}` | Удалить | + +### 4.4 Messages + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/messages?chat_id=X&limit=50` | Сообщения чата | +| GET | `/api/v1/messages?task_id=X` | Комментарии задачи | +| POST | `/api/v1/messages` | Отправить | +| GET | `/api/v1/messages/{id}/replies` | Тред | + +**Send message:** +```json +{ + "chat_id": "uuid (или null)", + "task_id": "uuid (или null)", + "content": "text", + "author_type": "agent", + "author_slug": "coder", + "mentions": [] +} +``` + +⚠️ Поля `author_type` и `author_slug` обязательны (default: `human`/`admin`). Агент ДОЛЖЕН указывать свой slug и type=agent. + +### 4.5 Members + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/members` | Список | +| GET | `/api/v1/members/{slug}` | По slug | + +--- + +## 5. НЕсуществующие эндпоинты + +Следующих эндпоинтов **НЕТ** в Tracker: + +- ❌ `POST /api/v1/agents/register` — регистрация через UI или `POST /api/v1/members` +- ❌ `POST /api/v1/agents/heartbeat` — heartbeat только через WS +- ❌ `POST /api/v1/tasks/{id}/files` — файлы пока не реализованы +- ❌ `GET /api/v1/tasks/{id}/files` — файлы пока не реализованы + +--- + +## 6. Типичный flow агента + +``` +1. Connect WS: ws://localhost:8100/ws +2. Send: {"type": "auth", "token": "tb-xxx"} +3. Receive: auth.ok → save projects, lobby_chat_id +4. For each project: {"type": "project.subscribe", "project_id": "uuid"} +5. Start heartbeat loop: {"type": "heartbeat", "status": "online"} every 30s +6. Listen for events: + - message.new → parse, run LLM, reply via REST POST /api/v1/messages + - task.assigned → take task, work, update status, comment result +7. All mutations via REST (не через WS) +``` + +--- + +## 7. AgentConfig + +| Поле | Тип | Описание | +|------|-----|----------| +| `name` | string | Отображаемое имя | +| `slug` | string | Уникальный ID (a-z, 0-9, -) | +| `capabilities` | string[] | Что умеет (code, review, ...) | +| `chat_listen` | `all` \| `mentions` \| `none` | Фильтр чат-сообщений | +| `task_listen` | `all` \| `assigned` \| `none` | Фильтр task-событий | +| `prompt` | string? | Системный промпт | +| `model` | string? | LLM модель |