Покажи контекст


bash

cat /root/projects/team-board/CLAUDE.md 2>/dev/null || echo "CLAUDE.md not found"


.git/
agent-coder/
docs/
picogent/
runner/
sessions/
tracker/
web-client/
ws_test.py


CLAUDE.md not found


.git/
AGENT-CONNECTION-RESEARCH.md
AGENTS-INTEGRATION.md
ARCHITECTURE-REVIEW-2026-02-22.md
ARCHITECTURE.md
BRAINSTORM-AGENTS-2026-02-20.md
BRAINSTORM-CHATS-2026-02-21.md
BRAINSTORM-INDEX.md
BRAINSTORM-MCP-TOOLS-2026-02-22.md
BRAINSTORM-MICROSERVICES-2026-02-21.md
BRAINSTORM-MISC-2026-02-21.md
BRAINSTORM-PROJECTS-2026-02-20.md
BRAINSTORM-PROMPTS-2026-02-21.md
BRAINSTORM-PROTOCOL-2026-02-20.md
brainstorm-status.yaml
BRAINSTORM-TASKS-2026-02-20.md
BRAINSTORM-UI-2026-02-22.md
BRAINSTORM-WS-V2-2026-02-22.md
CONCEPTS.md
IDEAS.md
IMPLEMENTATION-PLAN.md
README.md
RESEARCH.md
TRACKER-PROTOCOL.md
WEBSOCKET-PROTOCOL.md
WS-PROTOCOL.md


.env.dev
.git/
.gitea/
.gitignore
docker-compose.yml
Dockerfile
README.md
requirements.txt
src/


.git/
.gitignore
node_modules/
package-lock.json
package.json
README.md
src/
tsconfig.json


.env.local
.git/
.gitea/
.gitignore
.next/
bff/
Dockerfile
next-env.d.ts
next.config.ts
node_modules/
package-lock.json
package.json
postcss.config.mjs
src/
tsconfig.json


__pycache__/
.git/
.gitignore
agent.py
brain.py
config.py
README.md
requirements.txt
venv/


.env
.git/
.gitignore
11agent.json
agent.example.json
agent.json
agents/
CLAUDE.md
client.html
dist/
Dockerfile
docs/
node_modules/
package-lock.json
package.json
README.md
skills/
src/
tsconfig.json


# CLAUDE.md — picogent

## Что это

Минимальный AI-агент с in-process agentic loop на базе Pi Agent Core. Два режима работы:

1. **WebSocket-режим (чат)** — сервер WebSocket, браузерный клиент (`client.html`) отправляет промпты, получает стриминг-ответы
2. **Agent-режим (Team Board Tracker)** — агент подключается к трекеру задач, получает задания (`task.assigned`), выполняет, отправляет результат

Режим определяется автоматически: если есть `agent.json` / директория с конфигом / env `TRACKER_URL` → agent, иначе → WebSocket.

## Команды

```bash
npm install          # Установка
npm run build        # tsc -> dist/
npm run dev          # Dev (tsx + .env)
npm start            # Production (node + .env)
npm test             # Тесты (vitest)
```

## Запуск

```bash
npm run dev                          # WebSocket-режим на :3100
npm run dev -- ./agents/coder/       # Agent-режим (директория)
npm run dev -- ./agent.json          # Agent-режим (файл)
AGENT_TRANSPORT=ws npm run dev -- ./agents/coder/  # Agent-режим через WebSocket
```

## Структура файлов

```
src/
  index.ts              # Точка входа, dual-mode wiring (WS-чат / Agent HTTP / Agent WS)
  config.ts             # Config + AgentConfig, loadConfig(), loadAgentConfig()
  agent.ts              # Ядро: Pi Agent Core session, runAgent() async generator
  sandbox.ts            # createSandboxedTools() — tools с проверкой allowed_paths
  router.ts             # TaskTracker interface + EventRouter: tracker events -> runAgent() -> tracker API
  logger.ts             # Pino logger
  session.ts            # (не используется, legacy)
  transport/
    websocket.ts        # WebSocket-сервер (чат-режим, принимает подключения от клиентов)
    http.ts             # HTTP-сервер (agent-режим, transport=http, POST /events)
    ws-client.ts        # WS-клиент к трекеру (agent-режим, transport=ws)
    types.ts            # Transport interface (IncomingMessage, OutgoingMessage, MessageHandler)
  tracker/
    client.ts           # HTTP-клиент к Tracker REST API (updateTask, addComment и т.д.)
    registration.ts     # Register + heartbeat с retry (используется только transport=http)
    types.ts            # TrackerTask, TrackerEvent, RegistrationPayload, HeartbeatPayload
```

## Архитектура Agent-режима — два транспорта

### Проблема

Изначально agent-режим работал только через HTTP: агент поднимает HTTP-сервер на `listen_port`, регистрируется в трекере с `callback_url`, трекер шлёт события POST-запросами. Это требует открытого порта на стороне агента и доступности агента по сети для трекера.

### Решение: WS-транспорт

Добавлен альтернативный WebSocket-транспорт (`transport: "ws"`). Агент сам подключается к трекеру — двунаправленный канал, не нужен ни открытый порт, ни callback_url. Удобно для NAT, firewall, облачных окружений.

### Как устроено

Выбор транспорта — поле `transport` в конфиге (`"http"` | `"ws"`, default `"http"` для обратной совместимости).

#### Общие компоненты (не зависят от транспорта)

| Компонент | Файл | Роль |
|-----------|------|------|
| `EventRouter` | `router.ts` | Получает `TrackerEvent`, вызывает `runAgent()`, отправляет результат через `TrackerClient` |
| `TrackerClient` | `tracker/client.ts` | HTTP-клиент к REST API трекера (PATCH task, POST comment). Используется в обоих транспортах |
| `TaskTracker` | `router.ts` (interface) | `addTask(id)` / `removeTask(id)` — нужен EventRouter для heartbeat-учёта |

#### transport=http

```
index.ts → startAgentHttp()
  ├── TrackerRegistration (implements TaskTracker) — register + heartbeat через REST
  ├── HttpTransport — HTTP-сервер POST /events
  └── EventRouter(config, client, registration)
```

Поток: Tracker → HTTP POST → HttpTransport → EventRouter → runAgent() → TrackerClient → Tracker

#### transport=ws

```
index.ts → startAgentWs()
  ├── WsClientTransport (implements TaskTracker) — connect + auth + heartbeat + events через WS
  └── EventRouter(config, client, wsTransport)
```

Поток: Agent → WS connect → Tracker ← events → WsClientTransport → EventRouter → runAgent() → TrackerClient (REST) → Tracker

**Ключевое отличие:** В WS-режиме нет `TrackerRegistration` и нет `HttpTransport`. Регистрация и heartbeat встроены в сам WebSocket-канал. А вот `TrackerClient` по-прежнему используется для REST-вызовов (updateTask, addComment) — эти операции идут через HTTP, не через WS.

### Интерфейс TaskTracker

```typescript
// router.ts
export interface TaskTracker {
  addTask(taskId: string): void;
  removeTask(taskId: string): void;
}
```

**Зачем:** EventRouter при обработке задачи вызывает `addTask()`/`removeTask()` чтобы heartbeat (неважно HTTP или WS) отправлял актуальный список задач и статус `busy`/`idle`. Раньше EventRouter зависел напрямую от `TrackerRegistration` — теперь зависит от интерфейса, что позволяет подставить WsClientTransport.

**Реализуют:**
- `TrackerRegistration` (для transport=http) — хранит Set, heartbeat через REST
- `WsClientTransport` (для transport=ws) — хранит Set, heartbeat через WS

### WS-клиент: детали реализации (`ws-client.ts`)

**URL:** Выводится из `tracker_url` автоматически — `http://` → `ws://`, `https://` → `wss://`, path `/ws?client_type=agent&client_id={slug}`. Совместим с существующим WS-эндпоинтом трекера (FastAPI, `ws/handler.py`).

**Протокол (JSON через WebSocket):**

```
Agent → Tracker:  { type: "auth", token: "...", agent: { name, slug, capabilities, max_concurrent_tasks } }
Tracker → Agent:  { type: "auth_ok" }
Tracker → Agent:  { type: "auth_error", message: "..." }
Tracker → Agent:  { type: "event", event: "task.assigned", data: {...}, ts: ..., id: "evt-xxx" }
Agent → Tracker:  { type: "ack", id: "evt-xxx" }
Agent → Tracker:  { type: "heartbeat", status: "idle"|"busy", current_tasks: ["task-1"] }
```

**Жизненный цикл:**
1. `start()` → `connect()` → WebSocket open → send `auth`
2. Получаем `auth_ok` → start heartbeat → resolve start() promise
3. Получаем `auth_error` → reject start() promise, close WS
4. При закрытии WS (если не `stop()`) → scheduleReconnect()
5. `stop()` → close WS, clear timers

**Reconnect:** Exponential backoff 1s → 2s → 4s → ... → 30s cap. Сбрасывается при успешной auth. При первом `start()` если auth_error — не реконнектится (promise reject). При последующих обрывах — реконнект автоматически.

**Dedup:** Set<string> с обрезкой при >10000 (оставляем последние 5000). Идентично HttpTransport.

**Heartbeat:** setInterval с `heartbeatIntervalSec` из конфига. Отправляет текущий статус (`idle`/`busy`) и список задач. Останавливается при закрытии WS, перезапускается при реконнекте.

### Что НЕ реализовано на стороне трекера

Серверная часть (tracker, Python/FastAPI) пока **не имеет** полной поддержки agent WS-протокола. Текущий `ws/handler.py`:
- Принимает WebSocket-подключения с `client_type=agent`
- Обрабатывает `agent.heartbeat` (отвечает `agent.heartbeat.ack`)
- **Не** обрабатывает: `auth`, `ack`, отправку `event` агенту

**Для полной работы transport=ws нужно доработать трекер:** добавить обработку `type: "auth"` (валидация токена, регистрация агента), отправку `type: "event"` при назначении задачи агенту, и обработку `type: "ack"`.

## Ключевые модули

### index.ts — Точка входа

- `main()` → `hasAgentConfig()` ? `startAgentMode()` : `startWebSocketMode()`
- `startAgentMode()` → `loadAgentConfig()` → `initAgent()` → `TrackerClient` → if ws: `startAgentWs()`, else: `startAgentHttp()`
- `startAgentHttp(config, client)` — TrackerRegistration + HttpTransport + EventRouter
- `startAgentWs(config, client)` — WsClientTransport + EventRouter (без HTTP-сервера)
- `startWebSocketMode()` — WebSocketTransport (чат-режим, не связан с трекером)

### agent.ts — Ядро

- `initAgent(provider, apiKey)` — вызвать при старте для injection API key (через `AuthStorage.setRuntimeApiKey()`)
- `runAgent(prompt, options)` — async generator, yield-ит AgentMessage (`type: 'text'|'tool_use'|'tool_result'|'error'|'done'|'session'`)
- MODEL_ALIASES: sonnet → claude-sonnet-4-6, opus → claude-opus-4-6, haiku → claude-haiku-4-5 и т.д.
- DEFAULT_MODELS: автоматически создаёт `~/.picogent/models.json`
- Skills: `loadSkills()` + `formatSkillsForPrompt()` из pi-coding-agent (progressive loading)
- Sandbox: `createSandboxedTools(workDir, allowedPaths)` из sandbox.ts

### config.ts — Конфигурация

- `Config` — для WebSocket-режима, из env vars
- `AgentConfig` — для Agent-режима, из agent.json + env
- `loadConfig()` / `loadAgentConfig()` — загрузчики
- `resolveConfig()` — CLI arg (dir/file) → env → default
- `hasAgentConfig()` — определяет режим (есть конфиг/dir/TRACKER_URL?)

Приоритет: env > JSON file > defaults.

### sandbox.ts

- `createSandboxedTools(cwd, allowedPaths?)` — создаёт tools с проверкой путей
- allowedPaths пустой/undefined = без ограничений
- Оборачивает Read/Write/Edit operations с `guard()`, Bash с `spawnHook`

### router.ts — Роутер событий

- `TaskTracker` interface — абстракция для учёта активных задач
- `EventRouter` — принимает `TrackerEvent`, диспетчеризует:
  - `task.assigned` → updateTask(in_progress) → runAgent() → addComment → updateTask(review)
  - `chat.message` → runAgent() → addComment (если есть task_id)
- `buildPromptFromTask(task)` — формирует промпт из данных задачи (key, title, description, priority, files)
- Контроль конкурентности: `activeTasks` counter + `maxConcurrentTasks` check

### tracker/client.ts — REST-клиент

HTTP-клиент к API трекера. Используется **в обоих** транспортах (http и ws) для мутаций:
- `register(payload)` → POST `/api/v1/agents/register`
- `heartbeat(payload)` → POST `/api/v1/agents/heartbeat`
- `updateTask(id, fields)` → PATCH `/api/v1/tasks/{id}`
- `addComment(id, content)` → POST `/api/v1/tasks/{id}/comments`
- `uploadFile()`, `getTask()`, `listTaskFiles()` — дополнительные

Авторизация: Bearer token в заголовке.

### tracker/registration.ts — HTTP-регистрация

Используется **только** в transport=http:
- `register(callbackUrl)` — отправляет POST register, retry каждые 5с при ошибке
- `startHeartbeat()` / `stopHeartbeat()` — интервальные POST heartbeat
- Implements `TaskTracker` — addTask/removeTask для учёта в heartbeat

### tracker/types.ts — Типы

```typescript
TrackerEvent   { event: string, data: Record<string,unknown>, ts: number, id: string }
TrackerTask    { id, key, title, description, status, priority, project_id, parent_id?, files? }
TrackerFile    { id, name, url, size }
RegistrationPayload  { name, slug, capabilities, max_concurrent_tasks, callback_url }
HeartbeatPayload     { status: 'idle'|'busy', current_tasks: string[] }
```

### transport/http.ts — HTTP-транспорт

HTTP-сервер (agent-режим, transport=http):
- POST `/events` — принимает TrackerEvent, dedup по id, вызывает handler async
- GET `/health` — `{ status: 'ok' }`
- Dedup: Set<string>, обрезка при >10000 (последние 5000)

### transport/ws-client.ts — WS-транспорт

WebSocket-клиент к трекеру (agent-режим, transport=ws). Описание выше в разделе архитектуры.

### transport/websocket.ts — WS-сервер (чат)

WebSocket-сервер для чат-режима (не agent). Принимает `IncomingMessage`, стримит `OutgoingMessage`.

### transport/types.ts — Общие типы

```typescript
IncomingMessage  { id, content, sessionId?, workDir?, model?, systemPrompt? }
OutgoingMessage  { id, type: 'text'|'tool_use'|'tool_result'|'error'|'done', content, sessionId? }
MessageHandler   (msg: IncomingMessage) => Promise<void>
Transport        { start(), stop(), onMessage(handler), send(clientId, msg) }
```

## Зависимости (Pi SDK)

```
@mariozechner/pi-agent-core    — AgentTool, createAgentSession, SessionManager
@mariozechner/pi-coding-agent  — createReadTool, createWriteTool и т.д., loadSkills
@mariozechner/pi-ai            — streamSimple (LLM streaming)
```

Важно:
- `codingTools` — дефолтные tools привязанные к process.cwd(), **НЕ** используем напрямую
- `createCodingTools(cwd)` — tools привязанные к cwd, но без path restriction
- Вместо них используем свой `createSandboxedTools()` из sandbox.ts
- `ModelRegistry.find(provider, modelId)` требует ПОЛНЫЙ model ID, не алиас
- `AuthStorage.setRuntimeApiKey()` — runtime injection, вызывать до первого prompt

## Конфигурация агента

Файл `agent.json` (или через env). Полный пример с описаниями: `agent.example.json`.

Обязательные поля: `tracker_url`, `token`.
Env-переменные имеют приоритет над JSON.

| Поле | Env | Default | Описание |
|---|---|---|---|
| `name` | `AGENT_NAME` | `Agent` | Имя агента в трекере |
| `slug` | `AGENT_SLUG` | `agent` | Уникальный ID (латиница) |
| `prompt` | `AGENT_PROMPT` | — | Системный промпт (роль агента) |
| `tracker_url` | `TRACKER_URL` | — | URL трекера (**обязательно**) |
| `token` | `AGENT_TOKEN` | — | Bearer-токен (**обязательно**) |
| `transport` | `AGENT_TRANSPORT` | `http` | `http` или `ws` |
| `listen_port` | `AGENT_PORT` | `3200` | Порт HTTP (только transport=http) |
| `work_dir` | `PICOGENT_WORK_DIR` | agentHome/cwd | Рабочая директория |
| `model` | `PICOGENT_MODEL` | `sonnet` | Модель LLM |
| `provider` | `PICOGENT_PROVIDER` | `anthropic` | Провайдер LLM |
| `api_key` | `PICOGENT_API_KEY` | — | API ключ |
| `capabilities` | — | `["coding"]` | Возможности для трекера |
| `max_concurrent_tasks` | — | `2` | Макс. параллельных задач |
| `heartbeat_interval_sec` | — | `30` | Интервал heartbeat (сек.) |
| `allowed_paths` | — | `[]` (без ограничений) | Ограничение доступа к FS |

## Режим директории

```
agents/coder/
  agent.json       # конфиг
  skills/          # скилы (SKILL.md)
  sessions/        # сессии (auto)
```

`agentHome` = путь к директории. Skills из `agentHome/skills/`, сессии в `agentHome/sessions/`.

## Связь с трекером (Team Board Tracker)

Трекер — отдельный проект (`tracker/`), Python/FastAPI + SQLAlchemy. API:

- REST: `/api/v1/agents/`, `/api/v1/tasks/`, `/api/v1/projects/`, `/api/v1/labels/`, `/api/v1/chats/`
- WebSocket: `/ws?client_type=...&client_id=...`

Текущий WS-handler трекера (`tracker/src/tracker/ws/handler.py`) поддерживает:
- `agent.heartbeat` → `agent.heartbeat.ack`
- `chat.send` / `chat.subscribe` / `chat.unsubscribe` — чат-функционал

**Не поддерживает пока:** protocol `type: "auth"/"auth_ok"/"event"/"ack"` — нужно для transport=ws.

Агенты в трекере: CRUD `/api/v1/agents/`, модель Agent (id, name, slug, token, capabilities, status, max_concurrent, timeout_seconds).

## Известные ограничения

- Bash sandbox проверяет только cwd, не содержимое команд
- Grep/Find/Ls не ограничиваются allowed_paths (read-only)
- session.ts не используется (legacy от первой версии)
- **transport=ws** требует доработки серверной части трекера (auth, event dispatch, ack handling)
- TrackerClient (REST) endpoints — могут потребовать адаптации под конкретный трекер
- В WS-режиме REST-вызовы (updateTask, addComment) идут отдельным HTTP-каналом, не через WS

## Происхождение

Проект извлечён из двух соседних проектов:

- **OpenClaw** (`openclaw/`) — паттерн in-process agent loop через Pi Agent Core
- **NanoClaw** (`nanoclaw/`) — паттерн pino-логгера, структура "сообщение → агент → результат"

Сознательно выброшено: каналы, контейнеры, плагины, auth profiles, MCP, subagents и прочая сложность. Picogent — это голый agent loop + транспорт.


# Team Board — Архитектура
Версия: 0.4
Дата: 2026-02-22

## Что это

Платформа для совместной работы людей и AI-агентов над проектами. Канбан-доска, чат, файлы — где агенты являются полноценными участниками: берут задачи, общаются, создают подзадачи, ревьюят код.

**Ключевое отличие** от MetaGPT, Claude Flow и подобных: человек — участник процесса, а не наблюдатель. Всё происходит на человеческом языке, в прозрачном чате.

## Компоненты

```
┌─────────────┐     ┌──────────────┐     ┌──────────────┐
│  Web Client │────▶│     BFF      │────▶│   Tracker    │
│  (Next.js)  │     │  (FastAPI)   │     │  (FastAPI)   │
└─────────────┘     └──────────────┘     └──────┬───────┘
                                                │
       ┌────────────────────────────────────────┤
       │              │              │          │
  ┌────▼────┐   ┌────▼────┐   ┌────▼────┐  ┌──▼───┐
  │Picogent │   │Picogent │   │Telegram │  │  DB  │
  │ Кодер   │   │Архитект │   │ Bridge  │  │  Pg  │
  └─────────┘   └─────────┘   └─────────┘  └──────┘
```

| Компонент | Стек | Порт | Описание |
|-----------|------|------|----------|
| **Tracker** | Python, FastAPI, SQLAlchemy, PostgreSQL | 8100 | Ядро. REST API + WebSocket. Не доступен извне. |
| **BFF** | Python, FastAPI | 8200 | Прокси для Web Client. JWT auth. |
| **Web Client** | Next.js 15, Tailwind CSS | 3100 | UI: канбан, чат, настройки. |
| **Picogent** | Node.js, TypeScript, Pi Agent Core | — | AI-агент. Подключается к Tracker по WS. |
| **Telegram Bridge** | TBD | — | Дублирует чат проекта в Telegram (с топиками). |

## Участники (Members)

### Ключевой принцип: все равны
Агент и человек — **одна модель (Member)**. Различие только в type и методе авторизации.

```
Member:
  id: UUID
  name: string
  slug: string (уникальный)
  type: human | agent
  role: owner | member | observer | bridge
  auth_method: password | oauth | token
  status: online | offline | busy
  avatar_url: string?
```

Для `type=agent` — дополнительные поля (или связанная таблица `AgentConfig`):
```
AgentConfig:
  member_id: UUID (FK → Member)
  capabilities: string[]       # coding, review, testing...
  chat_listen: all | mentions
  task_listen: all | mentions
  prompt: text                 # system prompt
  model: string                # LLM модель
```

### Авторизация
| Тип | Метод |
|-----|-------|
| human (Web UI) | login/password, OAuth |
| human (MCP Client) | login/password, OAuth |
| agent (Picogent) | token |
| bridge | token |

MCP Client (Claude Code, Cursor) — это **человек с инструментами**, не агент.

### Роли
| Роль | Права |
|------|-------|
| `owner` | Все |
| `member` | send_messages, create_tasks, update_tasks |
| `observer` | Только чтение |
| `bridge` | send_messages |

### Конфигурация агента
Один бинарник (picogent), роль = конфиг (`agent.json`):
```json
{
  "name": "Кодер",
  "slug": "coder",
  "prompt": "Ты опытный разработчик...",
  "model": "sonnet",
  "capabilities": ["coding", "review"],
  "chat_listen": "mentions",
  "task_listen": "mentions",
  "tracker_url": "http://localhost:8100",
  "token": "tb-agent-xxx"
}
```

### Listen Modes (раздельные)
- `chat_listen: all` — слышит все сообщения в чатах проекта
- `chat_listen: mentions` — только при @упоминании
- `task_listen: all` — получает все события задач проекта
- `task_listen: mentions` — только свои задачи (assignee/reviewer/watcher) и @mentions

Примеры:
- Архитектор: chat=all, task=all
- Кодер: chat=mentions, task=mentions
- Тестер: chat=mentions, task=all

### Одна сессия на агента
Всё в одном контексте: задачи, чат, ответы. Агент видит полную картину.

### Checkpoint Pattern (глухой агент)
LLM блокирующий — агент не слышит пока думает.
Решения: короткие задачи (5-10 мин) + проверка входящих между шагами.

### Heartbeat и таймауты
- Агент шлёт `heartbeat` каждые 30 сек
- Нет heartbeat 90 сек → `status=offline`, уведомление в чат
- Незавершённые задачи → возвращаются в `todo`
- systemd перезапускает picogent, агент делает `session.resume`

## Проекты

```
Project:
  id: UUID
  name: string
  slug: string
  description: text
  repo_urls: string[]    # multi-repo
  status: active | archived
```

**Вкладки UI:** канбан, чат, дашборд, настройки, файлы, activity feed.

**Документация проекта:** указывается в description / repo_urls. Агенты читают при онбординге.

## Задачи

### Модель
```
Task:
  id: UUID
  title: string
  description: text (markdown)
  type: task | bug | epic | story
  status: backlog | todo | in_progress | in_review | done
  priority: critical | high | medium | low
  labels: string[]
  parent_id: UUID?           # подзадача
  depends_on: UUID[]         # зависимости
  assignee_slug: string?
  reviewer_slug: string?
  watchers: string[]         # slugs наблюдателей
  time_spent: int            # минуты
  project_id: UUID
```

### Статусы
Любой → любой. Без жёсткого flow.

### Подзадачи vs Этапы (Steps)
- **Подзадачи** = полноценные задачи на канбан-доске (parent_id)
- **Этапы (steps)** = чеклист внутри задачи (прогресс агента, не на канбане)

### Watchers (наблюдатели)
Jira-паттерн. Любой может подписаться на задачу и получать уведомления.
- Assignee, reviewer, watchers — получают все события задачи
- Автор задачи = автоматически watcher
- MCP tools: `watch_task`, `unwatch_task`

### Назначение
1. Человек назначает вручную
2. Архитектор создаёт и назначает
3. Агент сам берёт из `todo` (`take_task` — атомарно)
4. Label matching: лейблы задачи ∩ capabilities агента

### Особенности
- Агент может **отклонить** задачу (`reject_task`) с причиной
- Автообнаружение блокеров (зависимость застряла → пинг в чат)
- Циклические зависимости запрещены

## Сообщения (Unified Message)

**Одна модель** для чата и комментариев задач:
```
Message:
  id: UUID
  content: text (markdown)
  author_type: human | agent | system
  author_slug: string
  chat_id: UUID?             # если в чате (lobby / project)
  task_id: UUID?             # если комментарий к задаче
  parent_id: UUID?           # thread (ответ на сообщение)
  mentions: string[]
  attachments: Attachment[]
  voice_url: string?
  created_at: timestamp
```

### Типы чатов
| Тип | Описание |
|-----|----------|
| **Lobby** | Глобальный чат |
| **Project Chat** | Per-project |
| **Task Comments** | Per-task (через task_id в Message) |

### Фичи
- **Threads** — только в чатах (parent_id). В задачах — просто лента.
- **Голосовые** — Thoth транскрибирует → текст + аудио
- **Файлы** — attachments в сообщениях

### Значки авторов в UI
👤 человек, 🤖 агент, ⚙ система

## Файлы

**Отдельного файлового сервиса нет.** Файлы = attachments в Message (к сообщениям, комментариям задач).

```
Attachment:
  id: UUID
  message_id: UUID
  filename: string
  mime_type: string
  size: int
  storage_path: string       # путь на диске
```

**Конфигурация:** путь к директории хранилища в конфиге Tracker.

Документация проекта — ссылка в `repo_urls[]` или описании проекта.

## Протокол

### Принцип
- **WebSocket** = real-time: события (push) + отправка сообщений в чат
- **REST (MCP tools)** = все мутации: задачи, steps, файлы, статусы

### WS: Клиент → Сервер
| Событие | Описание |
|---------|----------|
| `auth` | Токен + инфо агента |
| `heartbeat` | Статус (idle/busy) |
| `ack` | Подтверждение события |
| `chat.send` | Сообщение в чат |
| `project.subscribe` | Подписка на проект (чат + task events) |
| `project.unsubscribe` | Отписка |

### WS: Сервер → Клиент
| Событие | Фильтрация |
|---------|-----------|
| `auth.ok` / `auth.error` | — |
| `message.new` | По chat_listen + task_listen + ownership |
| `task.created` | task_listen:all |
| `task.updated` | task_listen:all ИЛИ assignee/reviewer/watcher |
| `task.assigned` | Только assignee |
| `agent.status` | Всем |

### Фильтрация событий на сервере
Агент получает только релевантное:
- **task events** → assignee + reviewer + watchers + task_listen:all
- **chat events** → chat_listen:all ИЛИ @mention
- Агенты с `task_listen:mentions` НЕ получают чужие task.created/updated

### Router (в Picogent)
Тупой text relay: WS event → текстовое сообщение → единственная сессия агента.
Агент сам решает что делать — вызывает MCP tools.

## MCP Tools (REST API)

### Задачи
| Tool | Описание |
|------|----------|
| `list_tasks` | Список (фильтры: project, status, assignee, labels) |
| `get_task` | Получить задачу по ID/key |
| `create_task` | Создать |
| `update_task` | Обновить поля |
| `take_task` | Взять себе (атомарно) |
| `reject_task` | Отклонить с причиной |
| `assign_task` | Назначить другому |
| `delete_task` | Удалить |

### Steps
| Tool | Описание |
|------|----------|
| `add_step` | Добавить этап |
| `complete_step` | Завершить этап |
| `update_step` | Обновить текст |

### Сообщения
| Tool | Описание |
|------|----------|
| `send_message` | В чат (chat_id) или к задаче (task_id) |
| `reply_message` | Ответ в thread (parent_id) |
| `list_messages` | Список (по chat_id / task_id) |

### Файлы
| Tool | Описание |
|------|----------|
| `upload_file` | Загрузить |
| `list_files` | Список по задаче/проекту |
| `download_file` | Скачать |

### Проекты и участники
| Tool | Описание |
|------|----------|
| `list_projects` | Список проектов |
| `get_project` | Информация о проекте |
| `list_members` | Кто в проекте |
| `update_status` | Обновить свой статус |
| `watch_task` | Подписаться на задачу |
| `unwatch_task` | Отписаться |

## Git Workflow

- Агент клонирует repo, работает в ветке `{type}/{task-key}-{slug}`
- По завершении → Merge Request в Gitea
- Ревью → approve/reject → автомерж
- Конфликты решает MR owner. Макс 3 итерации, потом эскалация
- Git tools НЕ в MCP — агент работает через git CLI

## Промпты агентов

Многослойный промпт:
1. System Prompt (agent.json) — базовая роль, неизменная
2. Agent Prompt (prompt.md) — агент сам улучшает
3. Project Context (docs) — документация
4. Skills — progressive loading
5. Tools (MCP) — автоматически
6. Guidelines — общие правила
7. Session History
8. Current Message

Agent Home:
```
agents/coder/
  agent.json       # конфиг
  prompt.md        # само-улучшаемый промпт
  memory/          # per-project память
  workspace/       # рабочие файлы
  sessions/        # сессии (JSONL)
```

## Web UI (текущее состояние)

### Реализовано
- KanbanBoard (колонки, drag & drop)
- TaskModal (title, description, status, priority, assignee, delete)
- ChatPanel (lobby)
- Sidebar, CreateProjectModal, AuthGuard, Login

### Следующие шаги
1. Комментарии в TaskModal (Message с task_id)
2. Steps в TaskModal (live-прогресс агента)
3. Agent management (генерация токенов)
4. Dashboard проекта

## Telegram Bridge (будущее)
- Один бот, WS к tracker как bridge
- Топики в Telegram = проекты
- Формат: `👤 Eugene: текст` / `🤖 Кодер: текст`

## Деплой
- Tracker: Docker Compose (+ PostgreSQL)
- BFF + Web Client: systemd на хосте
- Picogent: systemd (один процесс на агента)
- CI/CD: Gitea Actions

## На будущее
- **Web MCP Server** — любой LLM-клиент работает с трекером через MCP
- **Redis Streams** — event bus для масштабирования (если понадобится)
- **Multi-instance агентов** — scaling
- **OAuth / Authentik** — мульти-пользователь
- **Session Compaction** — сжатие контекста при превышении лимита

## Открытые вопросы
- Лейблы: глобальные или per-project?
- Реакции в чатах: как обрабатывать?
- Multi-instance: slug = роль, instance_id?
- Rollback файлов (attachments не версионированы)

## Устаревшие документы
- `WS-PROTOCOL.md` — заменён BRAINSTORM-WS-V2-2026-02-22.md
- `AGENTS-INTEGRATION.md` — заменён ARCHITECTURE.md


# Team Board — План реализации
Дата: 2026-02-22 | Обновлено: 2026-02-23
Метод: BMAD Planning

## Текущее состояние

### Tracker v0.2.0 (Python/FastAPI) ✅
- Модели: Member, AgentConfig, Project, Task, Step, Chat, Message, Attachment
- API: auth, members, projects, tasks, steps, messages, ws_router
- WS: auth, heartbeat, chat.subscribe, chat.send, message broadcast
- БД: PostgreSQL, без миграций (recreate from scratch)

### Web Client v0.2.0 (Next.js) ✅
- Страницы: login, projects list, project board
- Компоненты: KanbanBoard (drag-drop + mobile tabs), TaskModal (steps, comments, assignee), ChatPanel (REST), CreateTaskModal, CreateProjectModal, AuthGuard
- BFF: Python/FastAPI (порт 8200), JWT auth, proxy к Tracker
- WS proxy через BFF с token-based auth
- Два окружения: dev.team.uix.su (активное) / team.uix.su (placeholder)

### Что было исправлено в v0.2.0 (бывшие несоответствия)
- ✅ Member + AgentConfig вместо отдельной Agent модели
- ✅ Unified Message (chat_id + task_id + parent_id)
- ✅ Steps модель
- ✅ Watchers на Task (string[])
- ✅ Attachment модель
- ✅ repo_urls = array
- ✅ Правильные статусы (backlog/todo/in_progress/in_review/done)
- ✅ Task key на бэкенде (TE-1)
- ⬜ WS handler не фильтрует по listen_mode (Phase 3)
- ⬜ Нет project.subscribe (Phase 3)
- ⬜ Heartbeat — базовый (Phase 3)

---

## Эпики

### Epic 1: Новые модели БД ✅ DONE
**Приоритет: MUST** (всё остальное зависит от этого)

Очистить БД, создать модели с нуля по ARCHITECTURE.md v0.4.
**Статус: Полностью выполнен в Tracker v0.2.0**

**Stories:**

**E1-S1: Member + AgentConfig**
Заменить User + Agent на единую модель Member.
```
Member: id, name, slug, type(human|agent), role(owner|member|observer|bridge),
  auth_method, password_hash?, token?, status, avatar_url
AgentConfig: member_id(FK), capabilities[], chat_listen, task_listen,
  prompt, model
```
Acceptance: модели созданы, init_db создаёт владельца (admin)

**E1-S2: Unified Message + Attachment**
Заменить Chat + ChatMessage на Message + Attachment.
```
Message: id, content, author_type, author_slug,
  chat_id?(lobby/project), task_id?, parent_id?(thread),
  mentions[], voice_url?, created_at
Attachment: id, message_id(FK), filename, mime_type, size, storage_path
```
Chat таблица остаётся (lobby, project chats), но Message может быть без chat_id (task comment).
Acceptance: Message работает и для чата, и для комментариев

**E1-S3: Task обновление**
```
- status: backlog|todo|in_progress|in_review|done
- type: task|bug|epic|story
- labels: string[] (ARRAY, вместо отдельной таблицы TaskLabel)
- watchers: string[] (ARRAY of slugs)
- depends_on: UUID[] (ARRAY, вместо TaskDependency таблицы)
- assignee_slug: string (вместо assigned_agent_id FK)
- reviewer_slug: string?
- Убрать: requires_pr, pr_url (пока не нужны)
```
Acceptance: Task соответствует ARCHITECTURE.md

**E1-S4: Step модель**
```
Step: id, task_id(FK), title, done(bool), position(int)
```
Acceptance: Steps CRUD работает

**E1-S5: Project обновление**
```
- repo_urls: string[] (ARRAY, вместо git_repo)
- Убрать: key_prefix, task_counter (номера задач генерить иначе)
```
Acceptance: Project соответствует ARCHITECTURE.md

**E1-S6: Очистка и init**
- Удалить Alembic миграции
- DROP ALL + CREATE ALL при первом запуске
- Seed: создать admin (owner), lobby chat
Acceptance: чистый старт, `docker compose down -v && up` работает

---

### Epic 2: REST API (MCP-ready) ✅ DONE
**Приоритет: MUST**

Обновить API эндпоинты для всех MCP tools.
**Статус: Выполнен в Tracker v0.2.0. Все CRUD эндпоинты работают. Некоторые продвинутые (take/reject/assign, files upload) — ещё не реализованы.**

**Stories:**

**E2-S1: Auth API**
- `POST /api/v1/auth/login` — логин → JWT
- `POST /api/v1/auth/register` — регистрация (disabled by default)
- Token validation middleware
Acceptance: JWT auth работает

**E2-S2: Tasks API**
- `GET /api/v1/tasks` — list (фильтры: project_id, status, assignee, labels)
- `GET /api/v1/tasks/{id}` — get
- `POST /api/v1/tasks` — create
- `PATCH /api/v1/tasks/{id}` — update
- `DELETE /api/v1/tasks/{id}` — delete
- `POST /api/v1/tasks/{id}/take` — take (атомарно)
- `POST /api/v1/tasks/{id}/reject` — reject с причиной
- `POST /api/v1/tasks/{id}/assign` — assign
- `POST /api/v1/tasks/{id}/watch` — watch
- `DELETE /api/v1/tasks/{id}/watch` — unwatch
Acceptance: все эндпоинты работают, take атомарный

**E2-S3: Steps API**
- `GET /api/v1/tasks/{id}/steps`
- `POST /api/v1/tasks/{id}/steps`
- `PATCH /api/v1/tasks/{task_id}/steps/{step_id}`
- `DELETE /api/v1/tasks/{task_id}/steps/{step_id}`
Acceptance: CRUD steps

**E2-S4: Messages API (unified)**
- `GET /api/v1/messages` — list (фильтры: chat_id, task_id, parent_id, limit/offset)
- `POST /api/v1/messages` — send (chat_id или task_id)
- `POST /api/v1/messages/{id}/reply` — thread reply
Acceptance: работает для чатов и комментариев задач

**E2-S5: Files API**
- `POST /api/v1/messages/{id}/attachments` — upload
- `GET /api/v1/attachments/{id}` — download
- `GET /api/v1/attachments` — list (фильтры: task_id, chat_id)
Acceptance: upload/download работает

**E2-S6: Projects API**
- Обновить существующий API под новые поля (repo_urls)
Acceptance: CRUD проектов работает

**E2-S7: Members API**
- `GET /api/v1/members` — list (фильтр: project_id)
- `GET /api/v1/members/{slug}` — get
- `POST /api/v1/members` — create (agent с токеном)
- `PATCH /api/v1/members/{slug}` — update
- `PATCH /api/v1/members/me/status` — update own status
Acceptance: CRUD members, генерация токенов для агентов

---

### Epic 3: WebSocket Protocol v2 ⬜ PARTIAL
**Приоритет: MUST**
**Статус: Базовый WS (auth, chat.subscribe, chat.send, broadcast) работает. Listen_mode фильтрация, project.subscribe, heartbeat timeout — ещё нет.**

**Stories:**

**E3-S1: Auth + Heartbeat**
- `auth` → token validation → `auth.ok` с контекстом (проекты, online members)
- `heartbeat` → обновление status, last_seen_at
- Timeout 90s → status=offline, уведомление
Acceptance: агент подключается и держит сессию

**E3-S2: Project Subscribe**
- `project.subscribe` / `project.unsubscribe`
- Подписка = получение chat + task events для этого проекта
Acceptance: подписка/отписка работает

**E3-S3: Event Filtering**
- `message.new` — фильтрация по chat_listen + ownership
- `task.created/updated/assigned` — фильтрация по task_listen + assignee/reviewer/watchers
Acceptance: агент с mentions получает только своё, агент с all — всё

**E3-S4: chat.send через WS**
- Отправка сообщения через WS (помимо REST)
- Broadcast по подписчикам с фильтрацией
Acceptance: чат работает в реальном времени

---

### Epic 4: Web Client обновление ⬜ PARTIAL
**Приоритет: SHOULD**

**Stories:**

**E4-S1: TaskModal — комментарии**
- Лента сообщений (Message с task_id) внизу модалки
- Input для нового комментария
- Значки авторов: 👤/🤖/⚙
Acceptance: комментарии к задаче работают

**E4-S2: TaskModal — Steps**
- Чеклист между описанием и комментариями
- Добавление/завершение/удаление steps
- Live-обновление через WS
Acceptance: steps отображаются и обновляются

**E4-S3: TaskModal — watchers**
- Кнопка "👁 Наблюдать" / "Отписаться"
- Список watchers в сайдбаре модалки
Acceptance: подписка/отписка работает

**E4-S4: Agent Management**
- Страница /agents — список агентов со статусами
- Создание агента (name, slug, capabilities) → генерация токена
- Отображение токена (один раз при создании)
Acceptance: можно создать агента и получить токен

**E4-S5: Обновление моделей в клиенте**
- api.ts: обновить типы (Member вместо Agent, Message вместо ChatMessage)
- ws.ts: обновить события (message.new, project.subscribe, heartbeat)
- ChatPanel: использовать Unified Message
Acceptance: клиент работает с новым API

---

### Epic 5: Picogent интеграция
**Приоритет: COULD** (после Epic 1-3)

**Stories:**

**E5-S1: MCP Tools Provider**
- Обёртка над Tracker REST API
- Все 21+ tools из BRAINSTORM-MCP-TOOLS
Acceptance: агент может вызывать tools

**E5-S2: Router v2**
- WS event → текст → единственная сессия
- Фильтрация по listen_mode на стороне picogent (дополнительно к серверной)
Acceptance: агент получает события как текст

**E5-S3: Single Session**
- Одна сессия на агента (не per-task)
- session.resume при reconnect
Acceptance: контекст сохраняется между задачами

---

## Порядок выполнения

```
Phase 1: Backend Foundation
  E1 (модели) → E2 (API) → E3 (WS v2)

Phase 2: Frontend
  E4-S5 (типы) → E4-S1 (комменты) → E4-S2 (steps) → E4-S4 (agents)

Phase 3: Agent
  E5-S1 (MCP) → E5-S2 (router) → E5-S3 (session)
```

## Оценка объёма

| Epic | Stories | Сложность | ~Часы |
|------|---------|-----------|-------|
| E1: Модели | 6 | Средняя | 4-6 |
| E2: REST API | 7 | Средняя | 8-12 |
| E3: WS v2 | 4 | Высокая | 6-8 |
| E4: Web Client | 5 | Средняя | 8-10 |
| E5: Picogent | 3 | Высокая | 10-14 |
| **Итого** | **25** | | **36-50** |


# Team Board — Статусы брейнштормов
# Обновлено: 2026-02-22

topics:

  # === ГОТОВО ===

  agents_architecture:
    name: "Агентная архитектура"
    status: done
    doc: BRAINSTORM-AGENTS-2026-02-20.md
    summary: "Один binary (picogent), роль=конфиг, git workflow, MCP tools, checkpoint pattern"

  tasks:
    name: "Задачи (Tasks)"
    status: done
    doc: BRAINSTORM-TASKS-2026-02-20.md
    summary: "Поля, статусы, подзадачи vs этапы, reject, auto-assign, блокеры"

  projects:
    name: "Проекты (Projects)"
    status: done
    doc: BRAINSTORM-PROJECTS-2026-02-20.md
    summary: "Вкладки, multi-repo, онбординг, listen modes, label matching"

  protocol:
    name: "Протокол Picogent↔Tracker"
    status: done
    doc: BRAINSTORM-PROTOCOL-2026-02-20.md
    summary: "WS=real-time, REST=мутации через MCP, router=text relay, one session per agent"

  chats:
    name: "Чаты"
    status: done
    doc: BRAINSTORM-CHATS-2026-02-21.md
    summary: "Lobby/project/task, threads, голосовые, реакции TBD"

  misc:
    name: "Разное (notifications, rollback, memory, deploy)"
    status: done
    doc: BRAINSTORM-MISC-2026-02-21.md
    summary: "Telegram bridge, OpenClaw bridge, agent memory, deploy"

  microservices:
    name: "Микросервисы"
    status: done
    doc: BRAINSTORM-MICROSERVICES-2026-02-21.md
    summary: "Event Bus (Redis Streams — идея), Saga, idempotency, Circuit Breaker"

  prompts:
    name: "Промпты агентов"
    status: done
    doc: BRAINSTORM-PROMPTS-2026-02-21.md
    summary: "Системные промпты ролей, guidelines"

  # === ЗАПЛАНИРОВАНО ===

  file_storage:
    name: "Файловое хранилище"
    status: done
    doc: null
    summary: "Файлы = вложения к задачам, сообщениям, комментариям, документации. Отдельного сервиса нет."

  members_model:
    name: "Модель участников (Members)"
    status: done
    doc: null
    summary: "Упрощённо: один пользователь + агенты с токенами. Веб-клиент генерирует токены агентов."

  mcp_tools:
    name: "MCP Tools для Tracker"
    status: done
    doc: BRAINSTORM-MCP-TOOLS-2026-02-22.md
    summary: "21 tool: tasks, steps, messages, files, projects, members. Git не в MCP."

  web_ui:
    name: "Web UI детали"
    status: done
    doc: BRAINSTORM-UI-2026-02-22.md
    summary: "Unified Message (чат+комменты), steps в TaskModal, threads только в чате"

  telegram_bridge:
    name: "Telegram Bridge"
    status: deferred
    doc: null
    notes: |
      - Один бот, WS к tracker
      - Топики в Telegram = проекты
      - Форматирование сообщений
      - Telegram→Tracker и обратно

  security:
    name: "Безопасность"
    status: deferred
    doc: null
    notes: |
      - Allowed paths, rate limiting
      - Audit log
      - Token rotation

  ci_cd_deploy:
    name: "CI/CD и деплой"
    status: deferred
    doc: null
    notes: |
      - systemd для picogent
      - Docker Compose для tracker stack
      - Автоперезапуск, логирование
      - Scaling

  ws_protocol_v2:
    name: "WebSocket протокол v2 (реализация)"
    status: done
    doc: BRAINSTORM-WS-V2-2026-02-22.md
    summary: "Раздельные listen modes (chat/task), watchers, project.subscribe, unified message.new"


# Брейншторм: WebSocket Protocol v2
Дата: 2026-02-22

## Принципы
- **WS = real-time** (события push + чат)
- **REST = все мутации** (через MCP tools)
- **Фильтрация на сервере** — агент получает только релевантное

## Listen Modes (раздельные)
- `chat_listen`: `all` | `mentions` — сообщения в чатах
- `task_listen`: `all` | `mentions` — события по задачам

Примеры:
- Архитектор: chat=all, task=all
- Кодер: chat=mentions, task=mentions
- PM: chat=all, task=all
- Тестер: chat=mentions, task=all

## Watchers (наблюдатели)
Jira-паттерн. Любой может подписаться на задачу.

```
Task:
  assignee_slug
  reviewer_slug
  watchers[]       # slugs наблюдателей
```

Кто получает task events:
1. Assignee — всегда
2. Reviewer — всегда
3. Watchers — всегда
4. task_listen:all — всегда
5. task_listen:mentions — только при @mention

MCP tools: `watch_task`, `unwatch_task`
UI: кнопка "👁 Наблюдать" в TaskModal
Автор задачи = автоматически watcher.

## Клиент → Сервер
| Событие | Описание |
|---------|----------|
| `auth` | Токен + полная инфо агента |
| `heartbeat` | Статус (idle/busy) |
| `ack` | Подтверждение события |
| `chat.send` | Сообщение в чат (real-time) |
| `project.subscribe` | Подписка на проект (= чат + task events) |
| `project.unsubscribe` | Отписка |

Мутации задач (create/update/take/reject) — только через REST.

## Сервер → Клиент
| Событие | Фильтрация |
|---------|-----------|
| `auth.ok` / `auth.error` | — |
| `message.new` | По chat_listen + ownership |
| `task.created` | task_listen:all |
| `task.updated` | task_listen:all ИЛИ assignee/reviewer/watcher |
| `task.assigned` | Только assignee |
| `agent.status` | Всем |

## Project Subscribe = Chat Subscribe
Одна сущность. Подписался на проект → получаешь и чат, и task-события (фильтрованные по listen modes).

## Unified Message
`message.new` — единое событие для чата и комментариев задач.
Поля `task_id` или `chat_id` определяют контекст.


tracker


app/
components/
lib/


find /root/projects/team-board/tracker/src/tracker -type f -name "*.py" | sort


(protected)/
api/
globals.css
layout.tsx
login/


find /root/projects/team-board/web-client/src -type f | sort

find /root/projects/team-board/web-client/bff -type f | sort


Вот полный контекст проекта **Team Board**:

---

## Структура проекта

```
team-board/
  tracker/          # Python/FastAPI + PostgreSQL — ядро, порт 8100
  web-client/       # Next.js + BFF (FastAPI) — UI, порт 3100/8200
  picogent/         # Node.js/TS AI-агент на Pi Agent Core
  agent-coder/      # Python-агент (legacy/альтернатива)
  docs/             # Архитектура, брейнштормы, планы
```

---

## Текущее состояние (v0.2.0)

### ✅ Tracker
- Модели: `Member`, `AgentConfig`, `Project`, `Task`, `Step`, `Chat`, `Message`, `Attachment`
- API: auth, members, projects, tasks, steps, messages
- WS: auth, heartbeat, chat.subscribe/send, broadcast

### ✅ Web Client
- Страницы: login, projects list, project board
- Компоненты: KanbanBoard (drag-drop), TaskModal (steps, comments, assignee), ChatPanel, CreateTaskModal, CreateProjectModal, AgentModal, ProjectSettings
- BFF: FastAPI прокси с JWT auth

### ✅ Picogent
- Dual-mode: WS-чат / Agent (transport=http|ws)
- EventRouter: task.assigned → runAgent() → addComment + updateTask
- Два транспорта: HTTP (callback) и WS (клиент)
- Sandbox, Skills, Pi Agent Core

---

## Что ещё не сделано (из плана)

| Epic | Статус |
|------|--------|
| **E3: WS v2** — listen_mode фильтрация, project.subscribe, heartbeat timeout | ⬜ PARTIAL |
| **E4: Web Client** — комментарии в TaskModal, watchers | ⬜ PARTIAL |
| **E5: Picogent** — MCP tools provider, single session | ⬜ TODO |
| **transport=ws** в пикоджент | ⬜ Сервер (трекер) не готов |

---

## Ключевые технологические решения

- **WS = real-time** (события push + чат), **REST = все мутации**
- **Member** — единая модель для человека и агента (type: human|agent)
- **Unified Message** — и чат и комментарии задач одна модель (chat_id | task_id)
- **Picogent** — один бинарник, роль = конфиг (`agent.json`)
- **Агент** авторизуется токеном, человек — JWT (логин/пароль)

Что хочешь делать дальше?