docs/archive/WS-PROTOCOL.md

8.1 KiB
Raw Permalink Blame History

WebSocket протокол Team Board

Общая схема

┌──────────┐         ┌─────┐         ┌─────────┐
│ Next.js  │ ←http→  │ BFF │ ←ws→    │ Tracker  │
│ (браузер)│         │     │         │          │
└──────────┘         └─────┘         └─────────┘
                        ↑ ws (JWT)      ↑ ws (agent token)
                     Браузер          Agent Instance

Два типа клиентов:

Клиент Подключение Аутентификация
Человек (браузер) wss://team.uix.su/ws?token=<JWT> → BFF → Tracker JWT токен
Агент ws://tracker:8100/ws?token=<agent_token> напрямую Agent session token

BFF проксирует WebSocket для людей. Агенты подключаются напрямую к трекеру (внутренняя сеть).

Формат сообщений

Все сообщения — JSON:

{
  "event": "task.updated",
  "data": { ... },
  "ts": 1771181000,
  "id": "msg-uuid"          // для дедупликации
}

Запросы (клиент → трекер):

{
  "event": "task.update",
  "data": { "task_id": "...", "status": "in_progress" },
  "req_id": "client-uuid"   // для сопоставления ответа
}

Ответы (трекер → клиент):

{
  "event": "task.update.ok",
  "data": { ... },
  "req_id": "client-uuid"   // тот же req_id
}

Ошибки:

{
  "event": "error",
  "data": { "message": "Task not found", "code": 404 },
  "req_id": "client-uuid"
}

Подписки (subscriptions)

При подключении клиент подписывается на события:

{ "event": "subscribe", "data": { "channels": ["project:team-board", "task:TEA-1"] } }

Каналы:

  • project:<slug> — все события проекта (задачи, чат)
  • task:<key> — события конкретной задачи (комментарии, файлы, статус)
  • agents — статусы агентов (для UI настроек)
  • * — всё (только для агентов с subscription_mode=all)

События

Задачи

Исходящие (трекер → клиенты)

Event Когда Data
task.created Создана задача { task: TaskOut }
task.updated Изменилась задача { task: TaskOut, changes: ["status","priority"] }
task.deleted Удалена задача { task_id, key }
task.moved Перемещена (drag) { task_id, key, from_status, to_status }
task.assigned Назначена агенту { task: TaskOut, agent_id }
task.unassigned Снята с агента { task_id, key }

Входящие (клиент → трекер)

Event Data Кто может
task.create { project_id, title, description?, status?, parent_id? } человек, агент
task.update { task_id, ...fields } человек, агент (assigned)
task.delete { task_id } человек
task.take { task_id } агент
task.release { task_id } агент

Комментарии

Event Direction Data
comment.created ← трекер { task_id, comment: { id, sender, content, ts } }
comment.create → трекер { task_id, content }
comment.list → трекер { task_id }
comment.list.ok ← трекер { task_id, comments: [...] }

Файлы

Файлы передаются через HTTP (не WebSocket), но уведомления через WS:

Event Direction Data
file.uploaded ← трекер { task_id, file: { id, filename, mime_type } }
file.deleted ← трекер { task_id, file_id }

HTTP эндпоинты:

  • POST /api/v1/tasks/{id}/files — загрузить (multipart)
  • GET /api/v1/tasks/{id}/files — список
  • GET /api/v1/tasks/{id}/files/{fid}/download — скачать
  • DELETE /api/v1/tasks/{id}/files/{fid} — удалить

Чат

Event Direction Data
chat.message ← трекер { chat_id, message: { id, sender_type, sender_name, content, ts } }
chat.send → трекер { chat_id, content }
chat.history → трекер { chat_id, before?: msg_id, limit?: 50 }
chat.history.ok ← трекер { chat_id, messages: [...] }

Агенты

Event Direction Data
agent.online ← трекер { agent_id, name, capabilities }
agent.offline ← трекер { agent_id }
agent.heartbeat → трекер { status: "idle"|"busy", current_tasks: [...] }
agent.heartbeat.ack ← трекер {}

Жизненный цикл подключения

Человек (браузер)

1. Browser → BFF: wss://team.uix.su/ws?token=<JWT>
2. BFF validates JWT
3. BFF → Tracker: ws://tracker:8100/ws?client_type=human&client_id=<username>
4. BFF proxies messages bidirectionally
5. Browser sends: { event: "subscribe", data: { channels: ["project:team-board"] } }
6. Browser receives real-time updates

Агент

1. Agent → Tracker: ws://tracker:8100/ws?token=<agent_session_token>
2. Tracker validates token, marks agent online
3. Tracker broadcasts: { event: "agent.online", data: { ... } }
4. Agent auto-subscribes to assigned projects
5. Agent sends heartbeat every 30s
6. On disconnect → Tracker marks agent offline after 90s timeout

Broadcast правила

Событие Кому рассылается
task.created Все подписчики project:<slug>
task.updated Подписчики project:<slug> + task:<key>
comment.created Подписчики task:<key>
chat.message Подписчики project:<slug> (для чата проекта)
agent.online/offline Подписчики agents

Пример сессии (браузер)

→ { "event": "subscribe", "data": { "channels": ["project:team-board"] } }
← { "event": "subscribe.ok", "data": { "channels": ["project:team-board"] } }

// Кто-то создал задачу
← { "event": "task.created", "data": { "task": { "key": "TEA-6", "title": "New task", ... } } }

// Двигаем задачу
→ { "event": "task.update", "data": { "task_id": "...", "status": "in_progress" }, "req_id": "abc" }
← { "event": "task.update.ok", "data": { "task": { ... } }, "req_id": "abc" }
// + всем подписчикам:
← { "event": "task.updated", "data": { "task": { ... }, "changes": ["status"] } }

Пример сессии (агент)

→ { "event": "agent.heartbeat", "data": { "status": "idle", "current_tasks": [] } }
← { "event": "agent.heartbeat.ack" }

// Трекер назначает задачу
← { "event": "task.assigned", "data": { "task": { "key": "TEA-3", ... } } }

// Агент берёт в работу
→ { "event": "task.update", "data": { "task_id": "...", "status": "in_progress" }, "req_id": "r1" }
← { "event": "task.update.ok", "data": { ... }, "req_id": "r1" }

// Агент добавляет комментарий
→ { "event": "comment.create", "data": { "task_id": "...", "content": "Начал работу..." }, "req_id": "r2" }
← { "event": "comment.create.ok", "data": { ... }, "req_id": "r2" }

// Агент загружает файл (HTTP)
// POST /api/v1/tasks/{id}/files → file uploaded
// WS notification:
← { "event": "file.uploaded", "data": { "task_id": "...", "file": { ... } } }

// Агент завершает
→ { "event": "task.update", "data": { "task_id": "...", "status": "review" }, "req_id": "r3" }