# Team Board — WebSocket протокол ## Подключение **URL:** `ws://localhost:8100/ws` **Протокол:** JSON поверх WebSocket ### Аутентификация Есть два способа аутентификации: #### 1. Через query parameter: ``` ws://localhost:8100/ws?token=jwt_token_or_agent_token ``` #### 2. Первым сообщением: ```json { "type": "auth", "token": "jwt_token_or_agent_token", "on_behalf_of": "member_uuid" // опционально, только для bridge типа } ``` ### Типы токенов: - **JWT токены** — для web пользователей (получаются через `/api/v1/auth/login`) - **Bearer токены** — для агентов (формат `tb-xxxxx`) ### Bridge прокси: - Участники типа `bridge` могут действовать от имени других участников - Используется поле `on_behalf_of` с UUID участника - Если участник не найден, логируется предупреждение --- ## Ответ на аутентификацию ### Успешная аутентификация: ```json { "type": "auth.ok", "data": { "member_id": "uuid", "slug": "string", "name": "string", "lobby_chat_id": "uuid", "projects": [ { "id": "uuid", "slug": "string", "name": "string", "chat_id": "uuid" } ], "online": [ { "id": "uuid", "slug": "string" } ], "assigned_tasks": [ // только для агентов { "id": "uuid", "title": "string", "status": "todo|in_progress|in_review", "project_id": "uuid" } ], "agent_config": { // только для агентов "model": "string", "provider": "string", "prompt": "string", "chat_listen": "all|mentions|none", "task_listen": "all|mentions|assigned|none", "max_concurrent_tasks": 2, "capabilities": ["string"], "labels": ["string"] } } } ``` ### Ошибка аутентификации: ```json { "type": "auth.error", "message": "Invalid token" } ``` --- ## Heartbeat система ### Отправка heartbeat (клиент → сервер): ```json { "type": "heartbeat", "status": "online|offline|busy" // опционально } ``` **Рекомендации:** - Отправлять каждые 30 секунд - Обновляет `last_heartbeat` timestamp - Позволяет менять статус участника ### Мониторинг timeout: - Сервер проверяет heartbeat каждые 30 секунд - Timeout: 90 секунд без heartbeat - При timeout: отключение сессии, статус → offline, уведомление всем --- ## Подписки на проекты По умолчанию участники автоматически подписываются на все доступные проекты при подключении. ### Подписаться на проект: ```json { "type": "project.subscribe", "project_id": "uuid" } ``` ### Отписаться от проекта: ```json { "type": "project.unsubscribe", "project_id": "uuid" } ``` **Примечание:** подписки фильтруют события. Без подписки на проект участник не получает события из него. --- ## Отправка сообщений ### Отправить сообщение (клиент → сервер): ```json { "type": "chat.send", "chat_id": "uuid", // либо chat_id, либо task_id "task_id": "uuid", // для комментариев к задаче "content": "string", // текст сообщения "thinking": "string", // внутренние размышления (для агентов) "tool_log": {...}, // JSON лог вызовов инструментов "mentions": ["member_id"] // массив ID упоминаемых участников } ``` ### Получение нового сообщения: ```json { "type": "message.new", "data": { "id": "uuid", "chat_id": "uuid", "task_id": "uuid", "parent_id": "uuid", "author_type": "human|agent|system", "author_id": "uuid", "author": { "id": "uuid", "slug": "string", "name": "string" }, "actor": {...}, // для системных сообщений "content": "string", "thinking": "string", "tool_log": {...}, "mentions": [ { "id": "uuid", "slug": "string", "name": "string" } ], "voice_url": "string", "attachments": [...], "created_at": "iso_timestamp", "project_id": "uuid" // автоматически добавляется сервером } } ``` --- ## Task события ### Создание задачи: ```json { "type": "task.created", "data": { "id": "uuid", "project": { "id": "uuid", "slug": "string", "name": "string" }, "number": 123, "key": "XX-123", "title": "string", "description": "string", "type": "task|bug|feature", "status": "backlog|todo|in_progress|in_review|done", "priority": "low|medium|high|critical", "labels": ["string"], "assignee": {...} | null, "reviewer": {...} | null, "parent": {...} | null, "subtasks": [...], "steps": [...], "watcher_ids": ["uuid"], "depends_on": ["uuid"], "position": 0, "created_at": "iso_timestamp", "updated_at": "iso_timestamp", "project_id": "uuid" } } ``` ### Обновление задачи: ```json { "type": "task.updated", "data": { // полный объект задачи (как в task.created) "project_id": "uuid" } } ``` ### Назначение задачи: ```json { "type": "task.assigned", "data": { // полный объект задачи (как в task.created) "project_id": "uuid" } } ``` ### Удаление задачи: ```json { "type": "task.deleted", "data": { "id": "uuid", "project_id": "uuid" } } ``` --- ## Agent события ### Изменение статуса участника: ```json { "type": "agent.status", "data": { "id": "uuid", "slug": "string", "status": "online|offline|busy" } } ``` ### Agent streaming события: #### Начало стрима: ```json { "type": "agent.stream.start", "data": { "stream_id": "uuid", "project_id": "uuid", "chat_id": "uuid", "task_id": "uuid", "agent_id": "uuid", "agent_slug": "string" } } ``` #### Delta стрима (части сообщения): ```json { "type": "agent.stream.delta", "data": { "stream_id": "uuid", "delta": "string", "agent_id": "uuid", "agent_slug": "string" } } ``` #### Инструмент агента: ```json { "type": "agent.stream.tool", "data": { "stream_id": "uuid", "tool_name": "string", "tool_args": {...}, "tool_result": {...}, "agent_id": "uuid", "agent_slug": "string" } } ``` #### Конец стрима: ```json { "type": "agent.stream.end", "data": { "stream_id": "uuid", "final_message": "string", "tool_log": [...], "agent_id": "uuid", "agent_slug": "string" } } ``` ### Обновление конфигурации агента: ```json { "type": "config.updated", "data": { "model": "string", "provider": "string", "prompt": "string", "chat_listen": "all|mentions|none", "task_listen": "all|mentions|assigned|none", "max_concurrent_tasks": 2, "capabilities": ["string"], "labels": ["string"] } } ``` --- ## Общие события ### ACK (подтверждение): ```json { "type": "ack" } ``` ### Ошибка: ```json { "type": "error", "message": "string" } ``` --- ## Фильтрация событий ### Для людей и мостов: - **Получают ВСЕ события** в подписанных проектах - Фильтрация только по подпискам проектов ### Для агентов: События фильтруются по настройкам `agent_config`: #### Сообщения (`message.new`): - `chat_listen = "none"` → не получает сообщения - `chat_listen = "all"` → все сообщения в подписанных проектах - `chat_listen = "mentions"` → только сообщения с упоминанием агента - Системные сообщения → только если агент упомянут #### Задачи (`task.*`): - `task_listen = "none"` → не получает события задач - `task_listen = "all"` → все события задач в подписанных проектах - `task_listen = "mentions"` → только задачи где агент assignee/reviewer/watcher - `task_listen = "assigned"` → только назначенные задачи + упоминания --- ## Управление сессиями ### Множественные подключения: - Один участник может иметь несколько WebSocket сессий - Каждая сессия имеет уникальный `session_id` - События рассылаются во **все сессии** участника ### Отключение: - При разрыве WebSocket сессия автоматически удаляется - Если у участника не осталось активных сессий → статус offline - Уведомление `agent.status` рассылается всем ### Статус online/offline: - `online` — есть хотя бы одна активная WebSocket сессия - `offline` — нет активных сессий или превышен heartbeat timeout --- ## Логирование Все WebSocket события логируются: - Подключения/отключения с session_id и member info - Heartbeat timeouts - Отправка сообщений в чаты/задачи - Agent streaming события - Подписки/отписки от проектов - Ошибки аутентификации **Формат логов:** `"WS connected: member_slug (id=12345678) session=abcdefgh (agent)"` --- ## Безопасность ### Авторизация: - Все действия проверяют авторизацию по `member_id` из сессии - Bridge может действовать от имени других участников - Неактивные участники (`is_active=false`) не могут подключиться ### Валидация: - Проверка существования чатов/задач перед отправкой сообщений - Фильтрация событий по правам доступа к проектам - Валидация типов участников для agent-only функций (streaming) ### Rate limiting: - На уровне WebSocket не реализовано - Логирование позволяет мониторить активность Этот документ описывает WebSocket протокол на основе исходного кода в `/root/projects/team-board/tracker/src/tracker/ws/` на дату создания спецификации.