- Updated Nginx config to serve static SPA from web-client-new/dist/ - All API requests now proxy directly to Tracker (port 8100) - WebSocket connections go directly to Tracker with JWT auth - Stopped and disabled team-board-bff and team-board-web services - Fixed file permissions for nginx to serve static files - Migration complete: Next.js+BFF → Vite+React+Direct-Tracker
32 lines
72 KiB
JSON
32 lines
72 KiB
JSON
{"type":"session","version":3,"id":"581e7ec4-4b41-4026-99e3-be1567eef425","timestamp":"2026-02-23T18:12:20.971Z","cwd":"/root/projects/team-board/picogent"}
|
||
{"type":"model_change","id":"bc539ead","parentId":null,"timestamp":"2026-02-23T18:12:20.972Z","provider":"anthropic","modelId":"claude-sonnet-4-6"}
|
||
{"type":"thinking_level_change","id":"46c38812","parentId":"bc539ead","timestamp":"2026-02-23T18:12:20.972Z","thinkingLevel":"off"}
|
||
{"type":"message","id":"3b320d59","parentId":"46c38812","timestamp":"2026-02-23T18:12:20.973Z","message":{"role":"user","content":[{"type":"text","text":"Покажи контекст"}],"timestamp":1771870340972}}
|
||
{"type":"message","id":"1562d321","parentId":"3b320d59","timestamp":"2026-02-23T18:12:22.737Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_01KspxS6LvuyhniAVZMFvb6e","name":"ls","arguments":{}},{"type":"toolCall","id":"toolu_01HFRrjknVRktxEXm6tzji8t","name":"bash","arguments":{"command":"cat /root/projects/team-board/CLAUDE.md 2>/dev/null || echo \"CLAUDE.md not found\""}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":3,"output":99,"cacheRead":0,"cacheWrite":2357,"totalTokens":2459,"cost":{"input":0.000009,"output":0.001485,"cacheRead":0,"cacheWrite":0.00883875,"total":0.01033275}},"stopReason":"toolUse","timestamp":1771870340972}}
|
||
{"type":"message","id":"bdde4b99","parentId":"1562d321","timestamp":"2026-02-23T18:12:22.741Z","message":{"role":"toolResult","toolCallId":"toolu_01KspxS6LvuyhniAVZMFvb6e","toolName":"ls","content":[{"type":"text","text":".git/\nagent-coder/\ndocs/\npicogent/\nrunner/\nsessions/\ntracker/\nweb-client/\nws_test.py"}],"isError":false,"timestamp":1771870342737}}
|
||
{"type":"message","id":"015d441c","parentId":"bdde4b99","timestamp":"2026-02-23T18:12:22.744Z","message":{"role":"toolResult","toolCallId":"toolu_01HFRrjknVRktxEXm6tzji8t","toolName":"bash","content":[{"type":"text","text":"CLAUDE.md not found\n"}],"isError":false,"timestamp":1771870342744}}
|
||
{"type":"message","id":"13391f4a","parentId":"015d441c","timestamp":"2026-02-23T18:12:25.813Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_01P7HzPEWhj8QZ1E9A2UAhwd","name":"ls","arguments":{"path":"/root/projects/team-board/docs"}},{"type":"toolCall","id":"toolu_01WsqXDC96ckihsaCECh8U9f","name":"ls","arguments":{"path":"/root/projects/team-board/tracker"}},{"type":"toolCall","id":"toolu_01TazjzSvakKYB3wAx7Bcb9i","name":"ls","arguments":{"path":"/root/projects/team-board/runner"}},{"type":"toolCall","id":"toolu_01M1j4FJMNLqgRmDpmH8AhJn","name":"ls","arguments":{"path":"/root/projects/team-board/web-client"}},{"type":"toolCall","id":"toolu_01YVi2fW1MXpZePL4nFZwLBR","name":"ls","arguments":{"path":"/root/projects/team-board/agent-coder"}},{"type":"toolCall","id":"toolu_01Mq7ZPzcFimZ6DzKBv1M7NC","name":"ls","arguments":{"path":"/root/projects/team-board/picogent"}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":282,"cacheRead":2357,"cacheWrite":204,"totalTokens":2844,"cost":{"input":0.000003,"output":0.00423,"cacheRead":0.0007071,"cacheWrite":0.0007650000000000001,"total":0.005705100000000001}},"stopReason":"toolUse","timestamp":1771870342744}}
|
||
{"type":"message","id":"1b614338","parentId":"13391f4a","timestamp":"2026-02-23T18:12:25.814Z","message":{"role":"toolResult","toolCallId":"toolu_01P7HzPEWhj8QZ1E9A2UAhwd","toolName":"ls","content":[{"type":"text","text":".git/\nAGENT-CONNECTION-RESEARCH.md\nAGENTS-INTEGRATION.md\nARCHITECTURE-REVIEW-2026-02-22.md\nARCHITECTURE.md\nBRAINSTORM-AGENTS-2026-02-20.md\nBRAINSTORM-CHATS-2026-02-21.md\nBRAINSTORM-INDEX.md\nBRAINSTORM-MCP-TOOLS-2026-02-22.md\nBRAINSTORM-MICROSERVICES-2026-02-21.md\nBRAINSTORM-MISC-2026-02-21.md\nBRAINSTORM-PROJECTS-2026-02-20.md\nBRAINSTORM-PROMPTS-2026-02-21.md\nBRAINSTORM-PROTOCOL-2026-02-20.md\nbrainstorm-status.yaml\nBRAINSTORM-TASKS-2026-02-20.md\nBRAINSTORM-UI-2026-02-22.md\nBRAINSTORM-WS-V2-2026-02-22.md\nCONCEPTS.md\nIDEAS.md\nIMPLEMENTATION-PLAN.md\nREADME.md\nRESEARCH.md\nTRACKER-PROTOCOL.md\nWEBSOCKET-PROTOCOL.md\nWS-PROTOCOL.md"}],"isError":false,"timestamp":1771870345814}}
|
||
{"type":"message","id":"f9b0da02","parentId":"1b614338","timestamp":"2026-02-23T18:12:25.814Z","message":{"role":"toolResult","toolCallId":"toolu_01WsqXDC96ckihsaCECh8U9f","toolName":"ls","content":[{"type":"text","text":".env.dev\n.git/\n.gitea/\n.gitignore\ndocker-compose.yml\nDockerfile\nREADME.md\nrequirements.txt\nsrc/"}],"isError":false,"timestamp":1771870345814}}
|
||
{"type":"message","id":"9e7b9520","parentId":"f9b0da02","timestamp":"2026-02-23T18:12:25.814Z","message":{"role":"toolResult","toolCallId":"toolu_01TazjzSvakKYB3wAx7Bcb9i","toolName":"ls","content":[{"type":"text","text":".git/\n.gitignore\nnode_modules/\npackage-lock.json\npackage.json\nREADME.md\nsrc/\ntsconfig.json"}],"isError":false,"timestamp":1771870345814}}
|
||
{"type":"message","id":"c2c57b5f","parentId":"9e7b9520","timestamp":"2026-02-23T18:12:25.815Z","message":{"role":"toolResult","toolCallId":"toolu_01M1j4FJMNLqgRmDpmH8AhJn","toolName":"ls","content":[{"type":"text","text":".env.local\n.git/\n.gitea/\n.gitignore\n.next/\nbff/\nDockerfile\nnext-env.d.ts\nnext.config.ts\nnode_modules/\npackage-lock.json\npackage.json\npostcss.config.mjs\nsrc/\ntsconfig.json"}],"isError":false,"timestamp":1771870345815}}
|
||
{"type":"message","id":"919ac89e","parentId":"c2c57b5f","timestamp":"2026-02-23T18:12:25.815Z","message":{"role":"toolResult","toolCallId":"toolu_01YVi2fW1MXpZePL4nFZwLBR","toolName":"ls","content":[{"type":"text","text":"__pycache__/\n.git/\n.gitignore\nagent.py\nbrain.py\nconfig.py\nREADME.md\nrequirements.txt\nvenv/"}],"isError":false,"timestamp":1771870345815}}
|
||
{"type":"message","id":"1714b067","parentId":"919ac89e","timestamp":"2026-02-23T18:12:25.815Z","message":{"role":"toolResult","toolCallId":"toolu_01Mq7ZPzcFimZ6DzKBv1M7NC","toolName":"ls","content":[{"type":"text","text":".env\n.git/\n.gitignore\n11agent.json\nagent.example.json\nagent.json\nagents/\nCLAUDE.md\nclient.html\ndist/\nDockerfile\ndocs/\nnode_modules/\npackage-lock.json\npackage.json\nREADME.md\nskills/\nsrc/\ntsconfig.json"}],"isError":false,"timestamp":1771870345815}}
|
||
{"type":"message","id":"813989a9","parentId":"1714b067","timestamp":"2026-02-23T18:12:27.634Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_01CPpFEfrqXBhQnWuTQME91e","name":"read","arguments":{"path":"/root/projects/team-board/picogent/CLAUDE.md"}},{"type":"toolCall","id":"toolu_01GcLrG66mHwVcwXZc9td4q6","name":"read","arguments":{"path":"/root/projects/team-board/docs/ARCHITECTURE.md"}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":115,"cacheRead":2561,"cacheWrite":996,"totalTokens":3673,"cost":{"input":0.000003,"output":0.001725,"cacheRead":0.0007683,"cacheWrite":0.003735,"total":0.0062313}},"stopReason":"toolUse","timestamp":1771870345815}}
|
||
{"type":"message","id":"6cf0ed00","parentId":"813989a9","timestamp":"2026-02-23T18:12:27.636Z","message":{"role":"toolResult","toolCallId":"toolu_01CPpFEfrqXBhQnWuTQME91e","toolName":"read","content":[{"type":"text","text":"# CLAUDE.md — picogent\n\n## Что это\n\nМинимальный AI-агент с in-process agentic loop на базе Pi Agent Core. Два режима работы:\n\n1. **WebSocket-режим (чат)** — сервер WebSocket, браузерный клиент (`client.html`) отправляет промпты, получает стриминг-ответы\n2. **Agent-режим (Team Board Tracker)** — агент подключается к трекеру задач, получает задания (`task.assigned`), выполняет, отправляет результат\n\nРежим определяется автоматически: если есть `agent.json` / директория с конфигом / env `TRACKER_URL` → agent, иначе → WebSocket.\n\n## Команды\n\n```bash\nnpm install # Установка\nnpm run build # tsc -> dist/\nnpm run dev # Dev (tsx + .env)\nnpm start # Production (node + .env)\nnpm test # Тесты (vitest)\n```\n\n## Запуск\n\n```bash\nnpm run dev # WebSocket-режим на :3100\nnpm run dev -- ./agents/coder/ # Agent-режим (директория)\nnpm run dev -- ./agent.json # Agent-режим (файл)\nAGENT_TRANSPORT=ws npm run dev -- ./agents/coder/ # Agent-режим через WebSocket\n```\n\n## Структура файлов\n\n```\nsrc/\n index.ts # Точка входа, dual-mode wiring (WS-чат / Agent HTTP / Agent WS)\n config.ts # Config + AgentConfig, loadConfig(), loadAgentConfig()\n agent.ts # Ядро: Pi Agent Core session, runAgent() async generator\n sandbox.ts # createSandboxedTools() — tools с проверкой allowed_paths\n router.ts # TaskTracker interface + EventRouter: tracker events -> runAgent() -> tracker API\n logger.ts # Pino logger\n session.ts # (не используется, legacy)\n transport/\n websocket.ts # WebSocket-сервер (чат-режим, принимает подключения от клиентов)\n http.ts # HTTP-сервер (agent-режим, transport=http, POST /events)\n ws-client.ts # WS-клиент к трекеру (agent-режим, transport=ws)\n types.ts # Transport interface (IncomingMessage, OutgoingMessage, MessageHandler)\n tracker/\n client.ts # HTTP-клиент к Tracker REST API (updateTask, addComment и т.д.)\n registration.ts # Register + heartbeat с retry (используется только transport=http)\n types.ts # TrackerTask, TrackerEvent, RegistrationPayload, HeartbeatPayload\n```\n\n## Архитектура Agent-режима — два транспорта\n\n### Проблема\n\nИзначально agent-режим работал только через HTTP: агент поднимает HTTP-сервер на `listen_port`, регистрируется в трекере с `callback_url`, трекер шлёт события POST-запросами. Это требует открытого порта на стороне агента и доступности агента по сети для трекера.\n\n### Решение: WS-транспорт\n\nДобавлен альтернативный WebSocket-транспорт (`transport: \"ws\"`). Агент сам подключается к трекеру — двунаправленный канал, не нужен ни открытый порт, ни callback_url. Удобно для NAT, firewall, облачных окружений.\n\n### Как устроено\n\nВыбор транспорта — поле `transport` в конфиге (`\"http\"` | `\"ws\"`, default `\"http\"` для обратной совместимости).\n\n#### Общие компоненты (не зависят от транспорта)\n\n| Компонент | Файл | Роль |\n|-----------|------|------|\n| `EventRouter` | `router.ts` | Получает `TrackerEvent`, вызывает `runAgent()`, отправляет результат через `TrackerClient` |\n| `TrackerClient` | `tracker/client.ts` | HTTP-клиент к REST API трекера (PATCH task, POST comment). Используется в обоих транспортах |\n| `TaskTracker` | `router.ts` (interface) | `addTask(id)` / `removeTask(id)` — нужен EventRouter для heartbeat-учёта |\n\n#### transport=http\n\n```\nindex.ts → startAgentHttp()\n ├── TrackerRegistration (implements TaskTracker) — register + heartbeat через REST\n ├── HttpTransport — HTTP-сервер POST /events\n └── EventRouter(config, client, registration)\n```\n\nПоток: Tracker → HTTP POST → HttpTransport → EventRouter → runAgent() → TrackerClient → Tracker\n\n#### transport=ws\n\n```\nindex.ts → startAgentWs()\n ├── WsClientTransport (implements TaskTracker) — connect + auth + heartbeat + events через WS\n └── EventRouter(config, client, wsTransport)\n```\n\nПоток: Agent → WS connect → Tracker ← events → WsClientTransport → EventRouter → runAgent() → TrackerClient (REST) → Tracker\n\n**Ключевое отличие:** В WS-режиме нет `TrackerRegistration` и нет `HttpTransport`. Регистрация и heartbeat встроены в сам WebSocket-канал. А вот `TrackerClient` по-прежнему используется для REST-вызовов (updateTask, addComment) — эти операции идут через HTTP, не через WS.\n\n### Интерфейс TaskTracker\n\n```typescript\n// router.ts\nexport interface TaskTracker {\n addTask(taskId: string): void;\n removeTask(taskId: string): void;\n}\n```\n\n**Зачем:** EventRouter при обработке задачи вызывает `addTask()`/`removeTask()` чтобы heartbeat (неважно HTTP или WS) отправлял актуальный список задач и статус `busy`/`idle`. Раньше EventRouter зависел напрямую от `TrackerRegistration` — теперь зависит от интерфейса, что позволяет подставить WsClientTransport.\n\n**Реализуют:**\n- `TrackerRegistration` (для transport=http) — хранит Set, heartbeat через REST\n- `WsClientTransport` (для transport=ws) — хранит Set, heartbeat через WS\n\n### WS-клиент: детали реализации (`ws-client.ts`)\n\n**URL:** Выводится из `tracker_url` автоматически — `http://` → `ws://`, `https://` → `wss://`, path `/ws?client_type=agent&client_id={slug}`. Совместим с существующим WS-эндпоинтом трекера (FastAPI, `ws/handler.py`).\n\n**Протокол (JSON через WebSocket):**\n\n```\nAgent → Tracker: { type: \"auth\", token: \"...\", agent: { name, slug, capabilities, max_concurrent_tasks } }\nTracker → Agent: { type: \"auth_ok\" }\nTracker → Agent: { type: \"auth_error\", message: \"...\" }\nTracker → Agent: { type: \"event\", event: \"task.assigned\", data: {...}, ts: ..., id: \"evt-xxx\" }\nAgent → Tracker: { type: \"ack\", id: \"evt-xxx\" }\nAgent → Tracker: { type: \"heartbeat\", status: \"idle\"|\"busy\", current_tasks: [\"task-1\"] }\n```\n\n**Жизненный цикл:**\n1. `start()` → `connect()` → WebSocket open → send `auth`\n2. Получаем `auth_ok` → start heartbeat → resolve start() promise\n3. Получаем `auth_error` → reject start() promise, close WS\n4. При закрытии WS (если не `stop()`) → scheduleReconnect()\n5. `stop()` → close WS, clear timers\n\n**Reconnect:** Exponential backoff 1s → 2s → 4s → ... → 30s cap. Сбрасывается при успешной auth. При первом `start()` если auth_error — не реконнектится (promise reject). При последующих обрывах — реконнект автоматически.\n\n**Dedup:** Set<string> с обрезкой при >10000 (оставляем последние 5000). Идентично HttpTransport.\n\n**Heartbeat:** setInterval с `heartbeatIntervalSec` из конфига. Отправляет текущий статус (`idle`/`busy`) и список задач. Останавливается при закрытии WS, перезапускается при реконнекте.\n\n### Что НЕ реализовано на стороне трекера\n\nСерверная часть (tracker, Python/FastAPI) пока **не имеет** полной поддержки agent WS-протокола. Текущий `ws/handler.py`:\n- Принимает WebSocket-подключения с `client_type=agent`\n- Обрабатывает `agent.heartbeat` (отвечает `agent.heartbeat.ack`)\n- **Не** обрабатывает: `auth`, `ack`, отправку `event` агенту\n\n**Для полной работы transport=ws нужно доработать трекер:** добавить обработку `type: \"auth\"` (валидация токена, регистрация агента), отправку `type: \"event\"` при назначении задачи агенту, и обработку `type: \"ack\"`.\n\n## Ключевые модули\n\n### index.ts — Точка входа\n\n- `main()` → `hasAgentConfig()` ? `startAgentMode()` : `startWebSocketMode()`\n- `startAgentMode()` → `loadAgentConfig()` → `initAgent()` → `TrackerClient` → if ws: `startAgentWs()`, else: `startAgentHttp()`\n- `startAgentHttp(config, client)` — TrackerRegistration + HttpTransport + EventRouter\n- `startAgentWs(config, client)` — WsClientTransport + EventRouter (без HTTP-сервера)\n- `startWebSocketMode()` — WebSocketTransport (чат-режим, не связан с трекером)\n\n### agent.ts — Ядро\n\n- `initAgent(provider, apiKey)` — вызвать при старте для injection API key (через `AuthStorage.setRuntimeApiKey()`)\n- `runAgent(prompt, options)` — async generator, yield-ит AgentMessage (`type: 'text'|'tool_use'|'tool_result'|'error'|'done'|'session'`)\n- MODEL_ALIASES: sonnet → claude-sonnet-4-6, opus → claude-opus-4-6, haiku → claude-haiku-4-5 и т.д.\n- DEFAULT_MODELS: автоматически создаёт `~/.picogent/models.json`\n- Skills: `loadSkills()` + `formatSkillsForPrompt()` из pi-coding-agent (progressive loading)\n- Sandbox: `createSandboxedTools(workDir, allowedPaths)` из sandbox.ts\n\n### config.ts — Конфигурация\n\n- `Config` — для WebSocket-режима, из env vars\n- `AgentConfig` — для Agent-режима, из agent.json + env\n- `loadConfig()` / `loadAgentConfig()` — загрузчики\n- `resolveConfig()` — CLI arg (dir/file) → env → default\n- `hasAgentConfig()` — определяет режим (есть конфиг/dir/TRACKER_URL?)\n\nПриоритет: env > JSON file > defaults.\n\n### sandbox.ts\n\n- `createSandboxedTools(cwd, allowedPaths?)` — создаёт tools с проверкой путей\n- allowedPaths пустой/undefined = без ограничений\n- Оборачивает Read/Write/Edit operations с `guard()`, Bash с `spawnHook`\n\n### router.ts — Роутер событий\n\n- `TaskTracker` interface — абстракция для учёта активных задач\n- `EventRouter` — принимает `TrackerEvent`, диспетчеризует:\n - `task.assigned` → updateTask(in_progress) → runAgent() → addComment → updateTask(review)\n - `chat.message` → runAgent() → addComment (если есть task_id)\n- `buildPromptFromTask(task)` — формирует промпт из данных задачи (key, title, description, priority, files)\n- Контроль конкурентности: `activeTasks` counter + `maxConcurrentTasks` check\n\n### tracker/client.ts — REST-клиент\n\nHTTP-клиент к API трекера. Используется **в обоих** транспортах (http и ws) для мутаций:\n- `register(payload)` → POST `/api/v1/agents/register`\n- `heartbeat(payload)` → POST `/api/v1/agents/heartbeat`\n- `updateTask(id, fields)` → PATCH `/api/v1/tasks/{id}`\n- `addComment(id, content)` → POST `/api/v1/tasks/{id}/comments`\n- `uploadFile()`, `getTask()`, `listTaskFiles()` — дополнительные\n\nАвторизация: Bearer token в заголовке.\n\n### tracker/registration.ts — HTTP-регистрация\n\nИспользуется **только** в transport=http:\n- `register(callbackUrl)` — отправляет POST register, retry каждые 5с при ошибке\n- `startHeartbeat()` / `stopHeartbeat()` — интервальные POST heartbeat\n- Implements `TaskTracker` — addTask/removeTask для учёта в heartbeat\n\n### tracker/types.ts — Типы\n\n```typescript\nTrackerEvent { event: string, data: Record<string,unknown>, ts: number, id: string }\nTrackerTask { id, key, title, description, status, priority, project_id, parent_id?, files? }\nTrackerFile { id, name, url, size }\nRegistrationPayload { name, slug, capabilities, max_concurrent_tasks, callback_url }\nHeartbeatPayload { status: 'idle'|'busy', current_tasks: string[] }\n```\n\n### transport/http.ts — HTTP-транспорт\n\nHTTP-сервер (agent-режим, transport=http):\n- POST `/events` — принимает TrackerEvent, dedup по id, вызывает handler async\n- GET `/health` — `{ status: 'ok' }`\n- Dedup: Set<string>, обрезка при >10000 (последние 5000)\n\n### transport/ws-client.ts — WS-транспорт\n\nWebSocket-клиент к трекеру (agent-режим, transport=ws). Описание выше в разделе архитектуры.\n\n### transport/websocket.ts — WS-сервер (чат)\n\nWebSocket-сервер для чат-режима (не agent). Принимает `IncomingMessage`, стримит `OutgoingMessage`.\n\n### transport/types.ts — Общие типы\n\n```typescript\nIncomingMessage { id, content, sessionId?, workDir?, model?, systemPrompt? }\nOutgoingMessage { id, type: 'text'|'tool_use'|'tool_result'|'error'|'done', content, sessionId? }\nMessageHandler (msg: IncomingMessage) => Promise<void>\nTransport { start(), stop(), onMessage(handler), send(clientId, msg) }\n```\n\n## Зависимости (Pi SDK)\n\n```\n@mariozechner/pi-agent-core — AgentTool, createAgentSession, SessionManager\n@mariozechner/pi-coding-agent — createReadTool, createWriteTool и т.д., loadSkills\n@mariozechner/pi-ai — streamSimple (LLM streaming)\n```\n\nВажно:\n- `codingTools` — дефолтные tools привязанные к process.cwd(), **НЕ** используем напрямую\n- `createCodingTools(cwd)` — tools привязанные к cwd, но без path restriction\n- Вместо них используем свой `createSandboxedTools()` из sandbox.ts\n- `ModelRegistry.find(provider, modelId)` требует ПОЛНЫЙ model ID, не алиас\n- `AuthStorage.setRuntimeApiKey()` — runtime injection, вызывать до первого prompt\n\n## Конфигурация агента\n\nФайл `agent.json` (или через env). Полный пример с описаниями: `agent.example.json`.\n\nОбязательные поля: `tracker_url`, `token`.\nEnv-переменные имеют приоритет над JSON.\n\n| Поле | Env | Default | Описание |\n|---|---|---|---|\n| `name` | `AGENT_NAME` | `Agent` | Имя агента в трекере |\n| `slug` | `AGENT_SLUG` | `agent` | Уникальный ID (латиница) |\n| `prompt` | `AGENT_PROMPT` | — | Системный промпт (роль агента) |\n| `tracker_url` | `TRACKER_URL` | — | URL трекера (**обязательно**) |\n| `token` | `AGENT_TOKEN` | — | Bearer-токен (**обязательно**) |\n| `transport` | `AGENT_TRANSPORT` | `http` | `http` или `ws` |\n| `listen_port` | `AGENT_PORT` | `3200` | Порт HTTP (только transport=http) |\n| `work_dir` | `PICOGENT_WORK_DIR` | agentHome/cwd | Рабочая директория |\n| `model` | `PICOGENT_MODEL` | `sonnet` | Модель LLM |\n| `provider` | `PICOGENT_PROVIDER` | `anthropic` | Провайдер LLM |\n| `api_key` | `PICOGENT_API_KEY` | — | API ключ |\n| `capabilities` | — | `[\"coding\"]` | Возможности для трекера |\n| `max_concurrent_tasks` | — | `2` | Макс. параллельных задач |\n| `heartbeat_interval_sec` | — | `30` | Интервал heartbeat (сек.) |\n| `allowed_paths` | — | `[]` (без ограничений) | Ограничение доступа к FS |\n\n## Режим директории\n\n```\nagents/coder/\n agent.json # конфиг\n skills/ # скилы (SKILL.md)\n sessions/ # сессии (auto)\n```\n\n`agentHome` = путь к директории. Skills из `agentHome/skills/`, сессии в `agentHome/sessions/`.\n\n## Связь с трекером (Team Board Tracker)\n\nТрекер — отдельный проект (`tracker/`), Python/FastAPI + SQLAlchemy. API:\n\n- REST: `/api/v1/agents/`, `/api/v1/tasks/`, `/api/v1/projects/`, `/api/v1/labels/`, `/api/v1/chats/`\n- WebSocket: `/ws?client_type=...&client_id=...`\n\nТекущий WS-handler трекера (`tracker/src/tracker/ws/handler.py`) поддерживает:\n- `agent.heartbeat` → `agent.heartbeat.ack`\n- `chat.send` / `chat.subscribe` / `chat.unsubscribe` — чат-функционал\n\n**Не поддерживает пока:** protocol `type: \"auth\"/\"auth_ok\"/\"event\"/\"ack\"` — нужно для transport=ws.\n\nАгенты в трекере: CRUD `/api/v1/agents/`, модель Agent (id, name, slug, token, capabilities, status, max_concurrent, timeout_seconds).\n\n## Известные ограничения\n\n- Bash sandbox проверяет только cwd, не содержимое команд\n- Grep/Find/Ls не ограничиваются allowed_paths (read-only)\n- session.ts не используется (legacy от первой версии)\n- **transport=ws** требует доработки серверной части трекера (auth, event dispatch, ack handling)\n- TrackerClient (REST) endpoints — могут потребовать адаптации под конкретный трекер\n- В WS-режиме REST-вызовы (updateTask, addComment) идут отдельным HTTP-каналом, не через WS\n\n## Происхождение\n\nПроект извлечён из двух соседних проектов:\n\n- **OpenClaw** (`openclaw/`) — паттерн in-process agent loop через Pi Agent Core\n- **NanoClaw** (`nanoclaw/`) — паттерн pino-логгера, структура \"сообщение → агент → результат\"\n\nСознательно выброшено: каналы, контейнеры, плагины, auth profiles, MCP, subagents и прочая сложность. Picogent — это голый agent loop + транспорт.\n"}],"isError":false,"timestamp":1771870347636}}
|
||
{"type":"message","id":"bce6aa8c","parentId":"6cf0ed00","timestamp":"2026-02-23T18:12:27.639Z","message":{"role":"toolResult","toolCallId":"toolu_01GcLrG66mHwVcwXZc9td4q6","toolName":"read","content":[{"type":"text","text":"# Team Board — Архитектура\nВерсия: 0.4\nДата: 2026-02-22\n\n## Что это\n\nПлатформа для совместной работы людей и AI-агентов над проектами. Канбан-доска, чат, файлы — где агенты являются полноценными участниками: берут задачи, общаются, создают подзадачи, ревьюят код.\n\n**Ключевое отличие** от MetaGPT, Claude Flow и подобных: человек — участник процесса, а не наблюдатель. Всё происходит на человеческом языке, в прозрачном чате.\n\n## Компоненты\n\n```\n┌─────────────┐ ┌──────────────┐ ┌──────────────┐\n│ Web Client │────▶│ BFF │────▶│ Tracker │\n│ (Next.js) │ │ (FastAPI) │ │ (FastAPI) │\n└─────────────┘ └──────────────┘ └──────┬───────┘\n │\n ┌────────────────────────────────────────┤\n │ │ │ │\n ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌──▼───┐\n │Picogent │ │Picogent │ │Telegram │ │ DB │\n │ Кодер │ │Архитект │ │ Bridge │ │ Pg │\n └─────────┘ └─────────┘ └─────────┘ └──────┘\n```\n\n| Компонент | Стек | Порт | Описание |\n|-----------|------|------|----------|\n| **Tracker** | Python, FastAPI, SQLAlchemy, PostgreSQL | 8100 | Ядро. REST API + WebSocket. Не доступен извне. |\n| **BFF** | Python, FastAPI | 8200 | Прокси для Web Client. JWT auth. |\n| **Web Client** | Next.js 15, Tailwind CSS | 3100 | UI: канбан, чат, настройки. |\n| **Picogent** | Node.js, TypeScript, Pi Agent Core | — | AI-агент. Подключается к Tracker по WS. |\n| **Telegram Bridge** | TBD | — | Дублирует чат проекта в Telegram (с топиками). |\n\n## Участники (Members)\n\n### Ключевой принцип: все равны\nАгент и человек — **одна модель (Member)**. Различие только в type и методе авторизации.\n\n```\nMember:\n id: UUID\n name: string\n slug: string (уникальный)\n type: human | agent\n role: owner | member | observer | bridge\n auth_method: password | oauth | token\n status: online | offline | busy\n avatar_url: string?\n```\n\nДля `type=agent` — дополнительные поля (или связанная таблица `AgentConfig`):\n```\nAgentConfig:\n member_id: UUID (FK → Member)\n capabilities: string[] # coding, review, testing...\n chat_listen: all | mentions\n task_listen: all | mentions\n prompt: text # system prompt\n model: string # LLM модель\n```\n\n### Авторизация\n| Тип | Метод |\n|-----|-------|\n| human (Web UI) | login/password, OAuth |\n| human (MCP Client) | login/password, OAuth |\n| agent (Picogent) | token |\n| bridge | token |\n\nMCP Client (Claude Code, Cursor) — это **человек с инструментами**, не агент.\n\n### Роли\n| Роль | Права |\n|------|-------|\n| `owner` | Все |\n| `member` | send_messages, create_tasks, update_tasks |\n| `observer` | Только чтение |\n| `bridge` | send_messages |\n\n### Конфигурация агента\nОдин бинарник (picogent), роль = конфиг (`agent.json`):\n```json\n{\n \"name\": \"Кодер\",\n \"slug\": \"coder\",\n \"prompt\": \"Ты опытный разработчик...\",\n \"model\": \"sonnet\",\n \"capabilities\": [\"coding\", \"review\"],\n \"chat_listen\": \"mentions\",\n \"task_listen\": \"mentions\",\n \"tracker_url\": \"http://localhost:8100\",\n \"token\": \"tb-agent-xxx\"\n}\n```\n\n### Listen Modes (раздельные)\n- `chat_listen: all` — слышит все сообщения в чатах проекта\n- `chat_listen: mentions` — только при @упоминании\n- `task_listen: all` — получает все события задач проекта\n- `task_listen: mentions` — только свои задачи (assignee/reviewer/watcher) и @mentions\n\nПримеры:\n- Архитектор: chat=all, task=all\n- Кодер: chat=mentions, task=mentions\n- Тестер: chat=mentions, task=all\n\n### Одна сессия на агента\nВсё в одном контексте: задачи, чат, ответы. Агент видит полную картину.\n\n### Checkpoint Pattern (глухой агент)\nLLM блокирующий — агент не слышит пока думает.\nРешения: короткие задачи (5-10 мин) + проверка входящих между шагами.\n\n### Heartbeat и таймауты\n- Агент шлёт `heartbeat` каждые 30 сек\n- Нет heartbeat 90 сек → `status=offline`, уведомление в чат\n- Незавершённые задачи → возвращаются в `todo`\n- systemd перезапускает picogent, агент делает `session.resume`\n\n## Проекты\n\n```\nProject:\n id: UUID\n name: string\n slug: string\n description: text\n repo_urls: string[] # multi-repo\n status: active | archived\n```\n\n**Вкладки UI:** канбан, чат, дашборд, настройки, файлы, activity feed.\n\n**Документация проекта:** указывается в description / repo_urls. Агенты читают при онбординге.\n\n## Задачи\n\n### Модель\n```\nTask:\n id: UUID\n title: string\n description: text (markdown)\n type: task | bug | epic | story\n status: backlog | todo | in_progress | in_review | done\n priority: critical | high | medium | low\n labels: string[]\n parent_id: UUID? # подзадача\n depends_on: UUID[] # зависимости\n assignee_slug: string?\n reviewer_slug: string?\n watchers: string[] # slugs наблюдателей\n time_spent: int # минуты\n project_id: UUID\n```\n\n### Статусы\nЛюбой → любой. Без жёсткого flow.\n\n### Подзадачи vs Этапы (Steps)\n- **Подзадачи** = полноценные задачи на канбан-доске (parent_id)\n- **Этапы (steps)** = чеклист внутри задачи (прогресс агента, не на канбане)\n\n### Watchers (наблюдатели)\nJira-паттерн. Любой может подписаться на задачу и получать уведомления.\n- Assignee, reviewer, watchers — получают все события задачи\n- Автор задачи = автоматически watcher\n- MCP tools: `watch_task`, `unwatch_task`\n\n### Назначение\n1. Человек назначает вручную\n2. Архитектор создаёт и назначает\n3. Агент сам берёт из `todo` (`take_task` — атомарно)\n4. Label matching: лейблы задачи ∩ capabilities агента\n\n### Особенности\n- Агент может **отклонить** задачу (`reject_task`) с причиной\n- Автообнаружение блокеров (зависимость застряла → пинг в чат)\n- Циклические зависимости запрещены\n\n## Сообщения (Unified Message)\n\n**Одна модель** для чата и комментариев задач:\n```\nMessage:\n id: UUID\n content: text (markdown)\n author_type: human | agent | system\n author_slug: string\n chat_id: UUID? # если в чате (lobby / project)\n task_id: UUID? # если комментарий к задаче\n parent_id: UUID? # thread (ответ на сообщение)\n mentions: string[]\n attachments: Attachment[]\n voice_url: string?\n created_at: timestamp\n```\n\n### Типы чатов\n| Тип | Описание |\n|-----|----------|\n| **Lobby** | Глобальный чат |\n| **Project Chat** | Per-project |\n| **Task Comments** | Per-task (через task_id в Message) |\n\n### Фичи\n- **Threads** — только в чатах (parent_id). В задачах — просто лента.\n- **Голосовые** — Thoth транскрибирует → текст + аудио\n- **Файлы** — attachments в сообщениях\n\n### Значки авторов в UI\n👤 человек, 🤖 агент, ⚙️ система\n\n## Файлы\n\n**Отдельного файлового сервиса нет.** Файлы = attachments в Message (к сообщениям, комментариям задач).\n\n```\nAttachment:\n id: UUID\n message_id: UUID\n filename: string\n mime_type: string\n size: int\n storage_path: string # путь на диске\n```\n\n**Конфигурация:** путь к директории хранилища в конфиге Tracker.\n\nДокументация проекта — ссылка в `repo_urls[]` или описании проекта.\n\n## Протокол\n\n### Принцип\n- **WebSocket** = real-time: события (push) + отправка сообщений в чат\n- **REST (MCP tools)** = все мутации: задачи, steps, файлы, статусы\n\n### WS: Клиент → Сервер\n| Событие | Описание |\n|---------|----------|\n| `auth` | Токен + инфо агента |\n| `heartbeat` | Статус (idle/busy) |\n| `ack` | Подтверждение события |\n| `chat.send` | Сообщение в чат |\n| `project.subscribe` | Подписка на проект (чат + task events) |\n| `project.unsubscribe` | Отписка |\n\n### WS: Сервер → Клиент\n| Событие | Фильтрация |\n|---------|-----------|\n| `auth.ok` / `auth.error` | — |\n| `message.new` | По chat_listen + task_listen + ownership |\n| `task.created` | task_listen:all |\n| `task.updated` | task_listen:all ИЛИ assignee/reviewer/watcher |\n| `task.assigned` | Только assignee |\n| `agent.status` | Всем |\n\n### Фильтрация событий на сервере\nАгент получает только релевантное:\n- **task events** → assignee + reviewer + watchers + task_listen:all\n- **chat events** → chat_listen:all ИЛИ @mention\n- Агенты с `task_listen:mentions` НЕ получают чужие task.created/updated\n\n### Router (в Picogent)\nТупой text relay: WS event → текстовое сообщение → единственная сессия агента.\nАгент сам решает что делать — вызывает MCP tools.\n\n## MCP Tools (REST API)\n\n### Задачи\n| Tool | Описание |\n|------|----------|\n| `list_tasks` | Список (фильтры: project, status, assignee, labels) |\n| `get_task` | Получить задачу по ID/key |\n| `create_task` | Создать |\n| `update_task` | Обновить поля |\n| `take_task` | Взять себе (атомарно) |\n| `reject_task` | Отклонить с причиной |\n| `assign_task` | Назначить другому |\n| `delete_task` | Удалить |\n\n### Steps\n| Tool | Описание |\n|------|----------|\n| `add_step` | Добавить этап |\n| `complete_step` | Завершить этап |\n| `update_step` | Обновить текст |\n\n### Сообщения\n| Tool | Описание |\n|------|----------|\n| `send_message` | В чат (chat_id) или к задаче (task_id) |\n| `reply_message` | Ответ в thread (parent_id) |\n| `list_messages` | Список (по chat_id / task_id) |\n\n### Файлы\n| Tool | Описание |\n|------|----------|\n| `upload_file` | Загрузить |\n| `list_files` | Список по задаче/проекту |\n| `download_file` | Скачать |\n\n### Проекты и участники\n| Tool | Описание |\n|------|----------|\n| `list_projects` | Список проектов |\n| `get_project` | Информация о проекте |\n| `list_members` | Кто в проекте |\n| `update_status` | Обновить свой статус |\n| `watch_task` | Подписаться на задачу |\n| `unwatch_task` | Отписаться |\n\n## Git Workflow\n\n- Агент клонирует repo, работает в ветке `{type}/{task-key}-{slug}`\n- По завершении → Merge Request в Gitea\n- Ревью → approve/reject → автомерж\n- Конфликты решает MR owner. Макс 3 итерации, потом эскалация\n- Git tools НЕ в MCP — агент работает через git CLI\n\n## Промпты агентов\n\nМногослойный промпт:\n1. System Prompt (agent.json) — базовая роль, неизменная\n2. Agent Prompt (prompt.md) — агент сам улучшает\n3. Project Context (docs) — документация\n4. Skills — progressive loading\n5. Tools (MCP) — автоматически\n6. Guidelines — общие правила\n7. Session History\n8. Current Message\n\nAgent Home:\n```\nagents/coder/\n agent.json # конфиг\n prompt.md # само-улучшаемый промпт\n memory/ # per-project память\n workspace/ # рабочие файлы\n sessions/ # сессии (JSONL)\n```\n\n## Web UI (текущее состояние)\n\n### Реализовано\n- KanbanBoard (колонки, drag & drop)\n- TaskModal (title, description, status, priority, assignee, delete)\n- ChatPanel (lobby)\n- Sidebar, CreateProjectModal, AuthGuard, Login\n\n### Следующие шаги\n1. Комментарии в TaskModal (Message с task_id)\n2. Steps в TaskModal (live-прогресс агента)\n3. Agent management (генерация токенов)\n4. Dashboard проекта\n\n## Telegram Bridge (будущее)\n- Один бот, WS к tracker как bridge\n- Топики в Telegram = проекты\n- Формат: `👤 Eugene: текст` / `🤖 Кодер: текст`\n\n## Деплой\n- Tracker: Docker Compose (+ PostgreSQL)\n- BFF + Web Client: systemd на хосте\n- Picogent: systemd (один процесс на агента)\n- CI/CD: Gitea Actions\n\n## На будущее\n- **Web MCP Server** — любой LLM-клиент работает с трекером через MCP\n- **Redis Streams** — event bus для масштабирования (если понадобится)\n- **Multi-instance агентов** — scaling\n- **OAuth / Authentik** — мульти-пользователь\n- **Session Compaction** — сжатие контекста при превышении лимита\n\n## Открытые вопросы\n- Лейблы: глобальные или per-project?\n- Реакции в чатах: как обрабатывать?\n- Multi-instance: slug = роль, instance_id?\n- Rollback файлов (attachments не версионированы)\n\n## Устаревшие документы\n- `WS-PROTOCOL.md` — заменён BRAINSTORM-WS-V2-2026-02-22.md\n- `AGENTS-INTEGRATION.md` — заменён ARCHITECTURE.md\n"}],"isError":false,"timestamp":1771870347638}}
|
||
{"type":"message","id":"61327d71","parentId":"bce6aa8c","timestamp":"2026-02-23T18:12:29.974Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_01KXtnFYimHPFZgfGENqEXRT","name":"read","arguments":{"path":"/root/projects/team-board/docs/IMPLEMENTATION-PLAN.md"}},{"type":"toolCall","id":"toolu_01J28MCfqLuJExiXf36jZSU3","name":"read","arguments":{"path":"/root/projects/team-board/docs/brainstorm-status.yaml"}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":119,"cacheRead":3557,"cacheWrite":10154,"totalTokens":13831,"cost":{"input":0.000003,"output":0.0017850000000000001,"cacheRead":0.0010670999999999999,"cacheWrite":0.0380775,"total":0.0409326}},"stopReason":"toolUse","timestamp":1771870347639}}
|
||
{"type":"message","id":"f9b81d95","parentId":"61327d71","timestamp":"2026-02-23T18:12:29.976Z","message":{"role":"toolResult","toolCallId":"toolu_01KXtnFYimHPFZgfGENqEXRT","toolName":"read","content":[{"type":"text","text":"# Team Board — План реализации\nДата: 2026-02-22 | Обновлено: 2026-02-23\nМетод: BMAD Planning\n\n## Текущее состояние\n\n### Tracker v0.2.0 (Python/FastAPI) ✅\n- Модели: Member, AgentConfig, Project, Task, Step, Chat, Message, Attachment\n- API: auth, members, projects, tasks, steps, messages, ws_router\n- WS: auth, heartbeat, chat.subscribe, chat.send, message broadcast\n- БД: PostgreSQL, без миграций (recreate from scratch)\n\n### Web Client v0.2.0 (Next.js) ✅\n- Страницы: login, projects list, project board\n- Компоненты: KanbanBoard (drag-drop + mobile tabs), TaskModal (steps, comments, assignee), ChatPanel (REST), CreateTaskModal, CreateProjectModal, AuthGuard\n- BFF: Python/FastAPI (порт 8200), JWT auth, proxy к Tracker\n- WS proxy через BFF с token-based auth\n- Два окружения: dev.team.uix.su (активное) / team.uix.su (placeholder)\n\n### Что было исправлено в v0.2.0 (бывшие несоответствия)\n- ✅ Member + AgentConfig вместо отдельной Agent модели\n- ✅ Unified Message (chat_id + task_id + parent_id)\n- ✅ Steps модель\n- ✅ Watchers на Task (string[])\n- ✅ Attachment модель\n- ✅ repo_urls = array\n- ✅ Правильные статусы (backlog/todo/in_progress/in_review/done)\n- ✅ Task key на бэкенде (TE-1)\n- ⬜ WS handler не фильтрует по listen_mode (Phase 3)\n- ⬜ Нет project.subscribe (Phase 3)\n- ⬜ Heartbeat — базовый (Phase 3)\n\n---\n\n## Эпики\n\n### Epic 1: Новые модели БД ✅ DONE\n**Приоритет: MUST** (всё остальное зависит от этого)\n\nОчистить БД, создать модели с нуля по ARCHITECTURE.md v0.4.\n**Статус: Полностью выполнен в Tracker v0.2.0**\n\n**Stories:**\n\n**E1-S1: Member + AgentConfig**\nЗаменить User + Agent на единую модель Member.\n```\nMember: id, name, slug, type(human|agent), role(owner|member|observer|bridge),\n auth_method, password_hash?, token?, status, avatar_url\nAgentConfig: member_id(FK), capabilities[], chat_listen, task_listen,\n prompt, model\n```\nAcceptance: модели созданы, init_db создаёт владельца (admin)\n\n**E1-S2: Unified Message + Attachment**\nЗаменить Chat + ChatMessage на Message + Attachment.\n```\nMessage: id, content, author_type, author_slug,\n chat_id?(lobby/project), task_id?, parent_id?(thread),\n mentions[], voice_url?, created_at\nAttachment: id, message_id(FK), filename, mime_type, size, storage_path\n```\nChat таблица остаётся (lobby, project chats), но Message может быть без chat_id (task comment).\nAcceptance: Message работает и для чата, и для комментариев\n\n**E1-S3: Task обновление**\n```\n- status: backlog|todo|in_progress|in_review|done\n- type: task|bug|epic|story\n- labels: string[] (ARRAY, вместо отдельной таблицы TaskLabel)\n- watchers: string[] (ARRAY of slugs)\n- depends_on: UUID[] (ARRAY, вместо TaskDependency таблицы)\n- assignee_slug: string (вместо assigned_agent_id FK)\n- reviewer_slug: string?\n- Убрать: requires_pr, pr_url (пока не нужны)\n```\nAcceptance: Task соответствует ARCHITECTURE.md\n\n**E1-S4: Step модель**\n```\nStep: id, task_id(FK), title, done(bool), position(int)\n```\nAcceptance: Steps CRUD работает\n\n**E1-S5: Project обновление**\n```\n- repo_urls: string[] (ARRAY, вместо git_repo)\n- Убрать: key_prefix, task_counter (номера задач генерить иначе)\n```\nAcceptance: Project соответствует ARCHITECTURE.md\n\n**E1-S6: Очистка и init**\n- Удалить Alembic миграции\n- DROP ALL + CREATE ALL при первом запуске\n- Seed: создать admin (owner), lobby chat\nAcceptance: чистый старт, `docker compose down -v && up` работает\n\n---\n\n### Epic 2: REST API (MCP-ready) ✅ DONE\n**Приоритет: MUST**\n\nОбновить API эндпоинты для всех MCP tools.\n**Статус: Выполнен в Tracker v0.2.0. Все CRUD эндпоинты работают. Некоторые продвинутые (take/reject/assign, files upload) — ещё не реализованы.**\n\n**Stories:**\n\n**E2-S1: Auth API**\n- `POST /api/v1/auth/login` — логин → JWT\n- `POST /api/v1/auth/register` — регистрация (disabled by default)\n- Token validation middleware\nAcceptance: JWT auth работает\n\n**E2-S2: Tasks API**\n- `GET /api/v1/tasks` — list (фильтры: project_id, status, assignee, labels)\n- `GET /api/v1/tasks/{id}` — get\n- `POST /api/v1/tasks` — create\n- `PATCH /api/v1/tasks/{id}` — update\n- `DELETE /api/v1/tasks/{id}` — delete\n- `POST /api/v1/tasks/{id}/take` — take (атомарно)\n- `POST /api/v1/tasks/{id}/reject` — reject с причиной\n- `POST /api/v1/tasks/{id}/assign` — assign\n- `POST /api/v1/tasks/{id}/watch` — watch\n- `DELETE /api/v1/tasks/{id}/watch` — unwatch\nAcceptance: все эндпоинты работают, take атомарный\n\n**E2-S3: Steps API**\n- `GET /api/v1/tasks/{id}/steps`\n- `POST /api/v1/tasks/{id}/steps`\n- `PATCH /api/v1/tasks/{task_id}/steps/{step_id}`\n- `DELETE /api/v1/tasks/{task_id}/steps/{step_id}`\nAcceptance: CRUD steps\n\n**E2-S4: Messages API (unified)**\n- `GET /api/v1/messages` — list (фильтры: chat_id, task_id, parent_id, limit/offset)\n- `POST /api/v1/messages` — send (chat_id или task_id)\n- `POST /api/v1/messages/{id}/reply` — thread reply\nAcceptance: работает для чатов и комментариев задач\n\n**E2-S5: Files API**\n- `POST /api/v1/messages/{id}/attachments` — upload\n- `GET /api/v1/attachments/{id}` — download\n- `GET /api/v1/attachments` — list (фильтры: task_id, chat_id)\nAcceptance: upload/download работает\n\n**E2-S6: Projects API**\n- Обновить существующий API под новые поля (repo_urls)\nAcceptance: CRUD проектов работает\n\n**E2-S7: Members API**\n- `GET /api/v1/members` — list (фильтр: project_id)\n- `GET /api/v1/members/{slug}` — get\n- `POST /api/v1/members` — create (agent с токеном)\n- `PATCH /api/v1/members/{slug}` — update\n- `PATCH /api/v1/members/me/status` — update own status\nAcceptance: CRUD members, генерация токенов для агентов\n\n---\n\n### Epic 3: WebSocket Protocol v2 ⬜ PARTIAL\n**Приоритет: MUST**\n**Статус: Базовый WS (auth, chat.subscribe, chat.send, broadcast) работает. Listen_mode фильтрация, project.subscribe, heartbeat timeout — ещё нет.**\n\n**Stories:**\n\n**E3-S1: Auth + Heartbeat**\n- `auth` → token validation → `auth.ok` с контекстом (проекты, online members)\n- `heartbeat` → обновление status, last_seen_at\n- Timeout 90s → status=offline, уведомление\nAcceptance: агент подключается и держит сессию\n\n**E3-S2: Project Subscribe**\n- `project.subscribe` / `project.unsubscribe`\n- Подписка = получение chat + task events для этого проекта\nAcceptance: подписка/отписка работает\n\n**E3-S3: Event Filtering**\n- `message.new` — фильтрация по chat_listen + ownership\n- `task.created/updated/assigned` — фильтрация по task_listen + assignee/reviewer/watchers\nAcceptance: агент с mentions получает только своё, агент с all — всё\n\n**E3-S4: chat.send через WS**\n- Отправка сообщения через WS (помимо REST)\n- Broadcast по подписчикам с фильтрацией\nAcceptance: чат работает в реальном времени\n\n---\n\n### Epic 4: Web Client обновление ⬜ PARTIAL\n**Приоритет: SHOULD**\n\n**Stories:**\n\n**E4-S1: TaskModal — комментарии**\n- Лента сообщений (Message с task_id) внизу модалки\n- Input для нового комментария\n- Значки авторов: 👤/🤖/⚙️\nAcceptance: комментарии к задаче работают\n\n**E4-S2: TaskModal — Steps**\n- Чеклист между описанием и комментариями\n- Добавление/завершение/удаление steps\n- Live-обновление через WS\nAcceptance: steps отображаются и обновляются\n\n**E4-S3: TaskModal — watchers**\n- Кнопка \"👁 Наблюдать\" / \"Отписаться\"\n- Список watchers в сайдбаре модалки\nAcceptance: подписка/отписка работает\n\n**E4-S4: Agent Management**\n- Страница /agents — список агентов со статусами\n- Создание агента (name, slug, capabilities) → генерация токена\n- Отображение токена (один раз при создании)\nAcceptance: можно создать агента и получить токен\n\n**E4-S5: Обновление моделей в клиенте**\n- api.ts: обновить типы (Member вместо Agent, Message вместо ChatMessage)\n- ws.ts: обновить события (message.new, project.subscribe, heartbeat)\n- ChatPanel: использовать Unified Message\nAcceptance: клиент работает с новым API\n\n---\n\n### Epic 5: Picogent интеграция\n**Приоритет: COULD** (после Epic 1-3)\n\n**Stories:**\n\n**E5-S1: MCP Tools Provider**\n- Обёртка над Tracker REST API\n- Все 21+ tools из BRAINSTORM-MCP-TOOLS\nAcceptance: агент может вызывать tools\n\n**E5-S2: Router v2**\n- WS event → текст → единственная сессия\n- Фильтрация по listen_mode на стороне picogent (дополнительно к серверной)\nAcceptance: агент получает события как текст\n\n**E5-S3: Single Session**\n- Одна сессия на агента (не per-task)\n- session.resume при reconnect\nAcceptance: контекст сохраняется между задачами\n\n---\n\n## Порядок выполнения\n\n```\nPhase 1: Backend Foundation\n E1 (модели) → E2 (API) → E3 (WS v2)\n\nPhase 2: Frontend\n E4-S5 (типы) → E4-S1 (комменты) → E4-S2 (steps) → E4-S4 (agents)\n\nPhase 3: Agent\n E5-S1 (MCP) → E5-S2 (router) → E5-S3 (session)\n```\n\n## Оценка объёма\n\n| Epic | Stories | Сложность | ~Часы |\n|------|---------|-----------|-------|\n| E1: Модели | 6 | Средняя | 4-6 |\n| E2: REST API | 7 | Средняя | 8-12 |\n| E3: WS v2 | 4 | Высокая | 6-8 |\n| E4: Web Client | 5 | Средняя | 8-10 |\n| E5: Picogent | 3 | Высокая | 10-14 |\n| **Итого** | **25** | | **36-50** |\n"}],"isError":false,"timestamp":1771870349975}}
|
||
{"type":"message","id":"e250403f","parentId":"f9b81d95","timestamp":"2026-02-23T18:12:29.978Z","message":{"role":"toolResult","toolCallId":"toolu_01J28MCfqLuJExiXf36jZSU3","toolName":"read","content":[{"type":"text","text":"# Team Board — Статусы брейнштормов\n# Обновлено: 2026-02-22\n\ntopics:\n\n # === ГОТОВО ===\n\n agents_architecture:\n name: \"Агентная архитектура\"\n status: done\n doc: BRAINSTORM-AGENTS-2026-02-20.md\n summary: \"Один binary (picogent), роль=конфиг, git workflow, MCP tools, checkpoint pattern\"\n\n tasks:\n name: \"Задачи (Tasks)\"\n status: done\n doc: BRAINSTORM-TASKS-2026-02-20.md\n summary: \"Поля, статусы, подзадачи vs этапы, reject, auto-assign, блокеры\"\n\n projects:\n name: \"Проекты (Projects)\"\n status: done\n doc: BRAINSTORM-PROJECTS-2026-02-20.md\n summary: \"Вкладки, multi-repo, онбординг, listen modes, label matching\"\n\n protocol:\n name: \"Протокол Picogent↔Tracker\"\n status: done\n doc: BRAINSTORM-PROTOCOL-2026-02-20.md\n summary: \"WS=real-time, REST=мутации через MCP, router=text relay, one session per agent\"\n\n chats:\n name: \"Чаты\"\n status: done\n doc: BRAINSTORM-CHATS-2026-02-21.md\n summary: \"Lobby/project/task, threads, голосовые, реакции TBD\"\n\n misc:\n name: \"Разное (notifications, rollback, memory, deploy)\"\n status: done\n doc: BRAINSTORM-MISC-2026-02-21.md\n summary: \"Telegram bridge, OpenClaw bridge, agent memory, deploy\"\n\n microservices:\n name: \"Микросервисы\"\n status: done\n doc: BRAINSTORM-MICROSERVICES-2026-02-21.md\n summary: \"Event Bus (Redis Streams — идея), Saga, idempotency, Circuit Breaker\"\n\n prompts:\n name: \"Промпты агентов\"\n status: done\n doc: BRAINSTORM-PROMPTS-2026-02-21.md\n summary: \"Системные промпты ролей, guidelines\"\n\n # === ЗАПЛАНИРОВАНО ===\n\n file_storage:\n name: \"Файловое хранилище\"\n status: done\n doc: null\n summary: \"Файлы = вложения к задачам, сообщениям, комментариям, документации. Отдельного сервиса нет.\"\n\n members_model:\n name: \"Модель участников (Members)\"\n status: done\n doc: null\n summary: \"Упрощённо: один пользователь + агенты с токенами. Веб-клиент генерирует токены агентов.\"\n\n mcp_tools:\n name: \"MCP Tools для Tracker\"\n status: done\n doc: BRAINSTORM-MCP-TOOLS-2026-02-22.md\n summary: \"21 tool: tasks, steps, messages, files, projects, members. Git не в MCP.\"\n\n web_ui:\n name: \"Web UI детали\"\n status: done\n doc: BRAINSTORM-UI-2026-02-22.md\n summary: \"Unified Message (чат+комменты), steps в TaskModal, threads только в чате\"\n\n telegram_bridge:\n name: \"Telegram Bridge\"\n status: deferred\n doc: null\n notes: |\n - Один бот, WS к tracker\n - Топики в Telegram = проекты\n - Форматирование сообщений\n - Telegram→Tracker и обратно\n\n security:\n name: \"Безопасность\"\n status: deferred\n doc: null\n notes: |\n - Allowed paths, rate limiting\n - Audit log\n - Token rotation\n\n ci_cd_deploy:\n name: \"CI/CD и деплой\"\n status: deferred\n doc: null\n notes: |\n - systemd для picogent\n - Docker Compose для tracker stack\n - Автоперезапуск, логирование\n - Scaling\n\n ws_protocol_v2:\n name: \"WebSocket протокол v2 (реализация)\"\n status: done\n doc: BRAINSTORM-WS-V2-2026-02-22.md\n summary: \"Раздельные listen modes (chat/task), watchers, project.subscribe, unified message.new\"\n"}],"isError":false,"timestamp":1771870349977}}
|
||
{"type":"message","id":"a3eab027","parentId":"e250403f","timestamp":"2026-02-23T18:12:33.009Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_01VZEKdG2fQKZovPFY2g7shF","name":"read","arguments":{"path":"/root/projects/team-board/docs/BRAINSTORM-WS-V2-2026-02-22.md"}},{"type":"toolCall","id":"toolu_011uJmTudrTAkSWtFtrCGqYX","name":"bash","arguments":{"command":"ls /root/projects/team-board/tracker/src/"}},{"type":"toolCall","id":"toolu_01SZPstT6EzL96ypcLJ8VbMM","name":"ls","arguments":{"path":"/root/projects/team-board/web-client/src"}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":175,"cacheRead":13711,"cacheWrite":4850,"totalTokens":18737,"cost":{"input":0.000003,"output":0.002625,"cacheRead":0.004113299999999999,"cacheWrite":0.0181875,"total":0.024928799999999997}},"stopReason":"toolUse","timestamp":1771870349978}}
|
||
{"type":"message","id":"857ae7b2","parentId":"a3eab027","timestamp":"2026-02-23T18:12:33.018Z","message":{"role":"toolResult","toolCallId":"toolu_01VZEKdG2fQKZovPFY2g7shF","toolName":"read","content":[{"type":"text","text":"# Брейншторм: WebSocket Protocol v2\nДата: 2026-02-22\n\n## Принципы\n- **WS = real-time** (события push + чат)\n- **REST = все мутации** (через MCP tools)\n- **Фильтрация на сервере** — агент получает только релевантное\n\n## Listen Modes (раздельные)\n- `chat_listen`: `all` | `mentions` — сообщения в чатах\n- `task_listen`: `all` | `mentions` — события по задачам\n\nПримеры:\n- Архитектор: chat=all, task=all\n- Кодер: chat=mentions, task=mentions\n- PM: chat=all, task=all\n- Тестер: chat=mentions, task=all\n\n## Watchers (наблюдатели)\nJira-паттерн. Любой может подписаться на задачу.\n\n```\nTask:\n assignee_slug\n reviewer_slug\n watchers[] # slugs наблюдателей\n```\n\nКто получает task events:\n1. Assignee — всегда\n2. Reviewer — всегда\n3. Watchers — всегда\n4. task_listen:all — всегда\n5. task_listen:mentions — только при @mention\n\nMCP tools: `watch_task`, `unwatch_task`\nUI: кнопка \"👁 Наблюдать\" в TaskModal\nАвтор задачи = автоматически watcher.\n\n## Клиент → Сервер\n| Событие | Описание |\n|---------|----------|\n| `auth` | Токен + полная инфо агента |\n| `heartbeat` | Статус (idle/busy) |\n| `ack` | Подтверждение события |\n| `chat.send` | Сообщение в чат (real-time) |\n| `project.subscribe` | Подписка на проект (= чат + task events) |\n| `project.unsubscribe` | Отписка |\n\nМутации задач (create/update/take/reject) — только через REST.\n\n## Сервер → Клиент\n| Событие | Фильтрация |\n|---------|-----------|\n| `auth.ok` / `auth.error` | — |\n| `message.new` | По chat_listen + ownership |\n| `task.created` | task_listen:all |\n| `task.updated` | task_listen:all ИЛИ assignee/reviewer/watcher |\n| `task.assigned` | Только assignee |\n| `agent.status` | Всем |\n\n## Project Subscribe = Chat Subscribe\nОдна сущность. Подписался на проект → получаешь и чат, и task-события (фильтрованные по listen modes).\n\n## Unified Message\n`message.new` — единое событие для чата и комментариев задач.\nПоля `task_id` или `chat_id` определяют контекст.\n"}],"isError":false,"timestamp":1771870353012}}
|
||
{"type":"message","id":"d0855291","parentId":"857ae7b2","timestamp":"2026-02-23T18:12:33.021Z","message":{"role":"toolResult","toolCallId":"toolu_011uJmTudrTAkSWtFtrCGqYX","toolName":"bash","content":[{"type":"text","text":"tracker\n"}],"isError":false,"timestamp":1771870353020}}
|
||
{"type":"message","id":"1c99e054","parentId":"d0855291","timestamp":"2026-02-23T18:12:33.021Z","message":{"role":"toolResult","toolCallId":"toolu_01SZPstT6EzL96ypcLJ8VbMM","toolName":"ls","content":[{"type":"text","text":"app/\ncomponents/\nlib/"}],"isError":false,"timestamp":1771870353021}}
|
||
{"type":"message","id":"a9ad1be7","parentId":"1c99e054","timestamp":"2026-02-23T18:12:35.086Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_01TeFkCzoMMucYKbeddke7RF","name":"bash","arguments":{"command":"find /root/projects/team-board/tracker/src/tracker -type f -name \"*.py\" | sort"}},{"type":"toolCall","id":"toolu_01HNqjRxBEXTW5u7ueY7ccmT","name":"ls","arguments":{"path":"/root/projects/team-board/web-client/src/app"}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":126,"cacheRead":18561,"cacheWrite":1035,"totalTokens":19723,"cost":{"input":0.000003,"output":0.00189,"cacheRead":0.0055683,"cacheWrite":0.00388125,"total":0.01134255}},"stopReason":"toolUse","timestamp":1771870353021}}
|
||
{"type":"message","id":"68ca056d","parentId":"a9ad1be7","timestamp":"2026-02-23T18:12:35.090Z","message":{"role":"toolResult","toolCallId":"toolu_01TeFkCzoMMucYKbeddke7RF","toolName":"bash","content":[{"type":"text","text":"/root/projects/team-board/tracker/src/tracker/api/auth.py\n/root/projects/team-board/tracker/src/tracker/api/__init__.py\n/root/projects/team-board/tracker/src/tracker/api/members.py\n/root/projects/team-board/tracker/src/tracker/api/messages.py\n/root/projects/team-board/tracker/src/tracker/api/projects.py\n/root/projects/team-board/tracker/src/tracker/api/steps.py\n/root/projects/team-board/tracker/src/tracker/api/tasks.py\n/root/projects/team-board/tracker/src/tracker/app.py\n/root/projects/team-board/tracker/src/tracker/config.py\n/root/projects/team-board/tracker/src/tracker/database.py\n/root/projects/team-board/tracker/src/tracker/init_db.py\n/root/projects/team-board/tracker/src/tracker/__init__.py\n/root/projects/team-board/tracker/src/tracker/models/base.py\n/root/projects/team-board/tracker/src/tracker/models/chat.py\n/root/projects/team-board/tracker/src/tracker/models/__init__.py\n/root/projects/team-board/tracker/src/tracker/models/member.py\n/root/projects/team-board/tracker/src/tracker/models/project.py\n/root/projects/team-board/tracker/src/tracker/models/task.py\n/root/projects/team-board/tracker/src/tracker/services/__init__.py\n/root/projects/team-board/tracker/src/tracker/ws/handler.py\n/root/projects/team-board/tracker/src/tracker/ws/__init__.py\n/root/projects/team-board/tracker/src/tracker/ws/manager.py\n"}],"isError":false,"timestamp":1771870355090}}
|
||
{"type":"message","id":"77ad411a","parentId":"68ca056d","timestamp":"2026-02-23T18:12:35.091Z","message":{"role":"toolResult","toolCallId":"toolu_01HNqjRxBEXTW5u7ueY7ccmT","toolName":"ls","content":[{"type":"text","text":"(protected)/\napi/\nglobals.css\nlayout.tsx\nlogin/"}],"isError":false,"timestamp":1771870355091}}
|
||
{"type":"message","id":"60b278bb","parentId":"77ad411a","timestamp":"2026-02-23T18:12:37.223Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_019KisBUmyj3ZdPLnj2PrPRZ","name":"bash","arguments":{"command":"find /root/projects/team-board/web-client/src -type f | sort"}},{"type":"toolCall","id":"toolu_018F9tA1ebVoiF5MoMr4Evoe","name":"bash","arguments":{"command":"find /root/projects/team-board/web-client/bff -type f | sort"}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":126,"cacheRead":19596,"cacheWrite":655,"totalTokens":20378,"cost":{"input":0.000003,"output":0.00189,"cacheRead":0.0058788,"cacheWrite":0.00245625,"total":0.010228049999999999}},"stopReason":"toolUse","timestamp":1771870355091}}
|
||
{"type":"message","id":"8b92cc8d","parentId":"60b278bb","timestamp":"2026-02-23T18:12:37.232Z","message":{"role":"toolResult","toolCallId":"toolu_019KisBUmyj3ZdPLnj2PrPRZ","toolName":"bash","content":[{"type":"text","text":"/root/projects/team-board/web-client/src/app/globals.css\n/root/projects/team-board/web-client/src/app/layout.tsx\n/root/projects/team-board/web-client/src/app/login/page.tsx\n/root/projects/team-board/web-client/src/app/(protected)/layout.tsx\n/root/projects/team-board/web-client/src/app/(protected)/page.tsx\n/root/projects/team-board/web-client/src/app/(protected)/projects/new/page.tsx\n/root/projects/team-board/web-client/src/app/(protected)/projects/[slug]/page.tsx\n/root/projects/team-board/web-client/src/app/(protected)/settings/agents/page.tsx\n/root/projects/team-board/web-client/src/app/(protected)/settings/layout.tsx\n/root/projects/team-board/web-client/src/app/(protected)/settings/page.tsx\n/root/projects/team-board/web-client/src/components/AgentModal.tsx\n/root/projects/team-board/web-client/src/components/AuthGuard.tsx\n/root/projects/team-board/web-client/src/components/ChatPanel.tsx\n/root/projects/team-board/web-client/src/components/CreateAgentModal.tsx\n/root/projects/team-board/web-client/src/components/CreateProjectModal.tsx\n/root/projects/team-board/web-client/src/components/CreateTaskModal.tsx\n/root/projects/team-board/web-client/src/components/KanbanBoard.tsx\n/root/projects/team-board/web-client/src/components/ProjectFiles.tsx\n/root/projects/team-board/web-client/src/components/ProjectSettings.tsx\n/root/projects/team-board/web-client/src/components/Sidebar.tsx\n/root/projects/team-board/web-client/src/components/TaskModal.tsx\n/root/projects/team-board/web-client/src/lib/api.ts\n/root/projects/team-board/web-client/src/lib/auth-client.ts\n/root/projects/team-board/web-client/src/lib/ws.ts\n"}],"isError":false,"timestamp":1771870357228}}
|
||
{"type":"message","id":"d5f88b93","parentId":"8b92cc8d","timestamp":"2026-02-23T18:12:37.237Z","message":{"role":"toolResult","toolCallId":"toolu_018F9tA1ebVoiF5MoMr4Evoe","toolName":"bash","content":[{"type":"text","text":"/root/projects/team-board/web-client/bff/app.py\n/root/projects/team-board/web-client/bff/auth.py\n/root/projects/team-board/web-client/bff/config.py\n/root/projects/team-board/web-client/bff/__pycache__/app.cpython-312.pyc\n/root/projects/team-board/web-client/bff/__pycache__/auth.cpython-312.pyc\n/root/projects/team-board/web-client/bff/__pycache__/config.cpython-312.pyc\n/root/projects/team-board/web-client/bff/__pycache__/tracker_client.cpython-312.pyc\n/root/projects/team-board/web-client/bff/__pycache__/ws_proxy.cpython-312.pyc\n/root/projects/team-board/web-client/bff/requirements.txt\n/root/projects/team-board/web-client/bff/tracker_client.py\n/root/projects/team-board/web-client/bff/ws_proxy.py\n"}],"isError":false,"timestamp":1771870357237}}
|
||
{"type":"message","id":"e504301d","parentId":"d5f88b93","timestamp":"2026-02-23T18:12:56.311Z","message":{"role":"assistant","content":[{"type":"text","text":"Вот полный контекст проекта **Team Board**:\n\n---\n\n## Структура проекта\n\n```\nteam-board/\n tracker/ # Python/FastAPI + PostgreSQL — ядро, порт 8100\n web-client/ # Next.js + BFF (FastAPI) — UI, порт 3100/8200\n picogent/ # Node.js/TS AI-агент на Pi Agent Core\n agent-coder/ # Python-агент (legacy/альтернатива)\n docs/ # Архитектура, брейнштормы, планы\n```\n\n---\n\n## Текущее состояние (v0.2.0)\n\n### ✅ Tracker\n- Модели: `Member`, `AgentConfig`, `Project`, `Task`, `Step`, `Chat`, `Message`, `Attachment`\n- API: auth, members, projects, tasks, steps, messages\n- WS: auth, heartbeat, chat.subscribe/send, broadcast\n\n### ✅ Web Client\n- Страницы: login, projects list, project board\n- Компоненты: KanbanBoard (drag-drop), TaskModal (steps, comments, assignee), ChatPanel, CreateTaskModal, CreateProjectModal, AgentModal, ProjectSettings\n- BFF: FastAPI прокси с JWT auth\n\n### ✅ Picogent\n- Dual-mode: WS-чат / Agent (transport=http|ws)\n- EventRouter: task.assigned → runAgent() → addComment + updateTask\n- Два транспорта: HTTP (callback) и WS (клиент)\n- Sandbox, Skills, Pi Agent Core\n\n---\n\n## Что ещё не сделано (из плана)\n\n| Epic | Статус |\n|------|--------|\n| **E3: WS v2** — listen_mode фильтрация, project.subscribe, heartbeat timeout | ⬜ PARTIAL |\n| **E4: Web Client** — комментарии в TaskModal, watchers | ⬜ PARTIAL |\n| **E5: Picogent** — MCP tools provider, single session | ⬜ TODO |\n| **transport=ws** в пикоджент | ⬜ Сервер (трекер) не готов |\n\n---\n\n## Ключевые технологические решения\n\n- **WS = real-time** (события push + чат), **REST = все мутации**\n- **Member** — единая модель для человека и агента (type: human|agent)\n- **Unified Message** — и чат и комментарии задач одна модель (chat_id | task_id)\n- **Picogent** — один бинарник, роль = конфиг (`agent.json`)\n- **Агент** авторизуется токеном, человек — JWT (логин/пароль)\n\nЧто хочешь делать дальше?"}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":724,"cacheRead":20251,"cacheWrite":1026,"totalTokens":22002,"cost":{"input":0.000003,"output":0.01086,"cacheRead":0.0060753,"cacheWrite":0.0038475000000000002,"total":0.0207858}},"stopReason":"stop","timestamp":1771870357237}}
|