docs/specs/WEBSOCKET.md

12 KiB
Raw Blame History

Team Board — WebSocket протокол

Подключение

URL: ws://localhost:8100/ws
Протокол: JSON поверх WebSocket

Аутентификация

Есть два способа аутентификации:

1. Через query parameter:

ws://localhost:8100/ws?token=jwt_token_or_agent_token

2. Первым сообщением:

{
  "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 участника
  • Если участник не найден, логируется предупреждение

Ответ на аутентификацию

Успешная аутентификация:

{
  "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"]
    }
  }
}

Ошибка аутентификации:

{
  "type": "auth.error",
  "message": "Invalid token"
}

Heartbeat система

Отправка heartbeat (клиент → сервер):

{
  "type": "heartbeat",
  "status": "online|offline|busy"  // опционально
}

Рекомендации:

  • Отправлять каждые 30 секунд
  • Обновляет last_heartbeat timestamp
  • Позволяет менять статус участника

Мониторинг timeout:

  • Сервер проверяет heartbeat каждые 30 секунд
  • Timeout: 90 секунд без heartbeat
  • При timeout: отключение сессии, статус → offline, уведомление всем

Подписки на проекты

По умолчанию участники автоматически подписываются на все доступные проекты при подключении.

Подписаться на проект:

{
  "type": "project.subscribe",
  "project_id": "uuid"
}

Отписаться от проекта:

{
  "type": "project.unsubscribe", 
  "project_id": "uuid"
}

Примечание: подписки фильтруют события. Без подписки на проект участник не получает события из него.


Отправка сообщений

Отправить сообщение (клиент → сервер):

{
  "type": "chat.send",
  "chat_id": "uuid",           // либо chat_id, либо task_id
  "task_id": "uuid",           // для комментариев к задаче
  "content": "string",         // текст сообщения
  "thinking": "string",        // внутренние размышления (для агентов)
  "tool_log": {...},           // JSON лог вызовов инструментов
  "mentions": ["member_id"]    // массив ID упоминаемых участников
}

Получение нового сообщения:

{
  "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 события

Создание задачи:

{
  "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"
  }
}

Обновление задачи:

{
  "type": "task.updated",
  "data": {
    // полный объект задачи (как в task.created)
    "project_id": "uuid"
  }
}

Назначение задачи:

{
  "type": "task.assigned",
  "data": {
    // полный объект задачи (как в task.created)
    "project_id": "uuid"
  }
}

Удаление задачи:

{
  "type": "task.deleted", 
  "data": {
    "id": "uuid",
    "project_id": "uuid"
  }
}

Agent события

Изменение статуса участника:

{
  "type": "agent.status",
  "data": {
    "id": "uuid",
    "slug": "string",
    "status": "online|offline|busy"
  }
}

Agent streaming события:

Начало стрима:

{
  "type": "agent.stream.start",
  "data": {
    "stream_id": "uuid",
    "project_id": "uuid",
    "chat_id": "uuid",
    "task_id": "uuid",
    "agent_id": "uuid",
    "agent_slug": "string"
  }
}

Delta стрима (части сообщения):

{
  "type": "agent.stream.delta",
  "data": {
    "stream_id": "uuid", 
    "delta": "string",
    "agent_id": "uuid",
    "agent_slug": "string"
  }
}

Инструмент агента:

{
  "type": "agent.stream.tool",
  "data": {
    "stream_id": "uuid",
    "tool_name": "string",
    "tool_args": {...},
    "tool_result": {...},
    "agent_id": "uuid", 
    "agent_slug": "string"
  }
}

Конец стрима:

{
  "type": "agent.stream.end",
  "data": {
    "stream_id": "uuid",
    "final_message": "string",
    "tool_log": [...],
    "agent_id": "uuid",
    "agent_slug": "string"
  }
}

Обновление конфигурации агента:

{
  "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 (подтверждение):

{
  "type": "ack"
}

Ошибка:

{
  "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/ на дату создания спецификации.