docs: WebSocket protocol specification
- Message format (event/data/req_id) - Subscription channels (project, task, agents) - Events: tasks, comments, files, chat, agents - Human flow (browser → BFF → tracker) - Agent flow (direct to tracker) - Broadcast rules - Session examples
This commit is contained in:
parent
74f00799a1
commit
5061c60b9a
218
WS-PROTOCOL.md
Normal file
218
WS-PROTOCOL.md
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "task.updated",
|
||||||
|
"data": { ... },
|
||||||
|
"ts": 1771181000,
|
||||||
|
"id": "msg-uuid" // для дедупликации
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Запросы (клиент → трекер):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "task.update",
|
||||||
|
"data": { "task_id": "...", "status": "in_progress" },
|
||||||
|
"req_id": "client-uuid" // для сопоставления ответа
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ответы (трекер → клиент):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "task.update.ok",
|
||||||
|
"data": { ... },
|
||||||
|
"req_id": "client-uuid" // тот же req_id
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ошибки:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "error",
|
||||||
|
"data": { "message": "Task not found", "code": 404 },
|
||||||
|
"req_id": "client-uuid"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Подписки (subscriptions)
|
||||||
|
|
||||||
|
При подключении клиент подписывается на события:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "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" }
|
||||||
|
```
|
||||||
Loading…
Reference in New Issue
Block a user