# Team Board — Research: Agent Integration Исследование возможностей подключения AI-агентов к Team Board. --- ## Текущие возможности OpenClaw ### 1. sessions_spawn — спавн изолированных субагентов ``` Task → Spawn → Isolated Session → Result → Announce back ``` **Параметры:** - `task` (required) — задача для агента - `label` — метка для логов - `agentId` — ID агента (если разрешён в allowlist) - `model` — переопределение модели - `runTimeoutSeconds` — таймаут - `cleanup` — `delete|keep` **Поведение:** - Создаёт новую сессию `agent::subagent:` - Субагенты имеют полный набор инструментов минус session tools - Не блокирует: возвращает `{ status: "accepted", runId, childSessionKey }` - После завершения — announce в исходный чат --- ### 2. sessions_send — отправка между сессиями ``` Agent A → sessions_send → Agent B → Response ``` **Параметры:** - `sessionKey` (required) - `message` (required) - `timeoutSeconds` — 0 = fire-and-forget **Поведение:** - Поддерживает ping-pong между агентами (до 5 итераций) - Контекст сохраняется - Провenance маркируется как `inter_session` --- ### 3. sessions_list / sessions_history - Список активных сессий - История сообщений сессии - Фильтры по типу, времени, каналу --- ### 4. Multi-Agent Routing Несколько агентов в одном gateway: ```yaml agents: list: - id: main workspace: ~/.openclaw/workspace - id: coder workspace: ~/projects/coder-workspace - id: reviewer workspace: ~/projects/reviewer-workspace ``` **Каждый агент имеет:** - Свой workspace (файлы, AGENTS.md, SOUL.md) - Свой state directory - Своё хранилище сессий - Свои auth profiles --- ### 5. ACP (Agent Client Protocol) Стандартный протокол для подключения внешних агентов. ``` External Agent ↔ ACP ↔ OpenClaw Gateway ``` **Реализация в OpenClaw:** - `src/acp/server.ts` — ACP сервер - `src/acp/client.ts` — ACP клиент - Использует `@agentclientprotocol/sdk` **Dangerous tools требуют approval:** - exec, spawn, shell - sessions_spawn, sessions_send - gateway - fs_write, fs_delete, fs_move --- ## Варианты интеграции для Team Board ### Вариант 1: OpenClaw Multi-Agent (рекомендуется) ``` Team Board API ↓ OpenClaw Gateway ↓ ┌─────────────────────────────────┐ │ Agent: main (Lead) │ │ Agent: coder (Developer) │ │ Agent: reviewer (QA) │ │ Agent: analyst (Research) │ └─────────────────────────────────┘ ``` **Плюсы:** - Готовая инфраструктура - Сохранение контекста через сессии - Единый API - Безопасность (sandbox, permissions) - Инструменты (exec, browser, files) **Реализация:** 1. Добавить агентов в `openclaw.json` 2. Team Board вызывает `sessions_spawn` / `sessions_send` через Gateway API 3. Результаты через webhook или polling **Конфиг:** ```json { "agents": { "list": [ { "id": "main", "workspace": "~/.openclaw/workspace" }, { "id": "coder", "workspace": "~/workspaces/coder", "model": "anthropic/claude-sonnet-4-20250514" } ], "defaults": { "subagents": { "allowAgents": ["main", "coder", "reviewer"] } } } } ``` --- ### Вариант 2: ACP Bridge для CLI агентов Для подключения Claude Code CLI без траты API токенов. ``` Claude Code CLI (Max subscription) ↓ ACP Client Wrapper ↓ OpenClaw Gateway ↓ Team Board ``` **Компоненты:** 1. **PTY Session Manager** ```python import pexpect class CliSession: def __init__(self, command="claude"): self.child = pexpect.spawn(command) def send(self, message: str) -> str: self.child.sendline(message) self.child.expect(r'\n>') # wait for prompt return self.child.before.decode() ``` 2. **ACP Translator** ```python class AcpCliBridge: def handle_request(self, request): response = self.cli_session.send(request.message) return AcpResponse(content=response) ``` **Сложности:** - CLI интерактивный, не headless - Нужно парсить вывод - Контекст внутри CLI сессии --- ### Вариант 3: Direct API + Context Store Прямые вызовы API с хранением контекста в БД. ``` Team Board ↓ Agent Service ↓ ┌──────────────────────────────────┐ │ Claude API │ Codex │ Gemini │ └──────────────────────────────────┘ ↓ Context Store (PostgreSQL) ``` **Схема контекста:** ```sql CREATE TABLE agent_contexts ( id UUID PRIMARY KEY, agent_id UUID REFERENCES agents(id), session_id UUID REFERENCES sessions(id), messages JSONB, -- история сообщений metadata JSONB, -- system prompt, tools, etc. created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ ); ``` **При каждом запросе:** 1. Загрузить контекст из БД 2. Добавить новое сообщение 3. Вызвать API с полным контекстом 4. Сохранить ответ в БД --- ## Web Terminal Interface ### Архитектура ``` Browser (xterm.js) ↓ WebSocket Team Board Frontend ↓ WebSocket Team Board Backend (Session Manager) ↓ PTY / API Agent (OpenClaw / CLI / API) ``` ### Компоненты **Frontend:** - xterm.js — терминальный эмулятор - WebSocket клиент - Session selector UI **Backend:** - WebSocket сервер - Session Manager - PTY spawner (для CLI агентов) - OpenClaw Gateway client (для OpenClaw агентов) ### Пример кода (Backend) ```python from fastapi import WebSocket import asyncio class AgentSessionManager: def __init__(self): self.sessions = {} async def create_session(self, agent_type: str, config: dict): if agent_type == "openclaw": return OpenClawSession(config) elif agent_type == "cli": return CliSession(config) elif agent_type == "api": return ApiSession(config) async def handle_websocket(self, ws: WebSocket, session_id: str): session = self.sessions[session_id] async for message in ws.iter_text(): response = await session.send(message) await ws.send_text(response) ``` --- ## Сравнение вариантов | Критерий | OpenClaw Multi-Agent | ACP CLI Bridge | Direct API | |----------|---------------------|----------------|------------| | Сложность | Низкая | Высокая | Средняя | | Контекст | ✓ Автоматический | ✓ В CLI | Ручной | | Инструменты | ✓ Полный набор | ✓ CLI tools | ✗ Нет | | API токены | Нужны | Не нужны (Max) | Нужны | | Готовность | Сейчас | Требует разработки | Требует разработки | --- ## Рекомендуемый план ### Фаза 1: OpenClaw Multi-Agent (быстрый старт) 1. Добавить агентов в конфиг OpenClaw 2. Team Board API вызывает Gateway 3. Использовать `sessions_spawn` для задач 4. Использовать `sessions_send` для коммуникации **Результат:** Рабочая система с несколькими агентами ### Фаза 2: Web Terminal UI 1. xterm.js на фронте 2. WebSocket сервер 3. Прямое взаимодействие с агентами **Результат:** Terminal-like опыт в браузере ### Фаза 3: ACP CLI Bridge (опционально) 1. PTY wrapper для Claude Code CLI 2. ACP транслятор 3. Интеграция с Gateway **Результат:** CLI агенты без API токенов --- ## Открытые вопросы 1. **Headless mode в Claude Code CLI?** - Нужно проверить `claude --help` - Возможно есть `--message` или `--non-interactive` 2. **Как шарить файлы между агентами?** - Общий workspace? - Git как source of truth? - Копирование через API? 3. **Rate limiting для API агентов?** - Очередь задач - Приоритеты - Backoff при ошибках 4. **Мониторинг агентов?** - Статус (idle/working/error) - Использование токенов - Время выполнения --- ## Agent Management System ### Agent Registry (БД) ```sql CREATE TABLE agents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, -- "Coder", "Analyst" slug TEXT UNIQUE NOT NULL, -- "coder", "analyst" -- Настройки model TEXT, -- "claude-opus-4-5", "claude-sonnet" system_prompt TEXT, -- роль/персона max_concurrent INT DEFAULT 1, -- макс параллельных задач -- Статус status TEXT DEFAULT 'idle', -- idle, working, error, disabled current_tasks INT DEFAULT 0, -- Мета created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); ``` ### Web UI: Agent Config ``` ┌─────────────────────────────────────────────┐ │ Agents [+ Add] │ ├─────────────────────────────────────────────┤ │ 🟢 Coder claude-opus idle [⚙] │ │ 🟢 Reviewer claude-sonnet idle [⚙] │ │ 🟡 Analyst claude-opus working[⚙] │ │ ⚫ Tester claude-sonnet disabled[⚙] │ └─────────────────────────────────────────────┘ ``` ### Task Assignment Flow ``` 1. User создаёт задачу в Web UI 2. Назначает агента (или Auto → Lead решает) 3. Team Board → POST /hooks/agent → OpenClaw 4. OpenClaw проверяет лимиты, спавнит субагента 5. Субагент работает, результат → callback 6. Team Board обновляет задачу в БД ``` --- ## OpenClaw Webhooks API ### Конфигурация ```json { "hooks": { "enabled": true, "token": "${OPENCLAW_HOOKS_TOKEN}", "path": "/hooks", "defaultSessionKey": "hook:team-board", "allowRequestSessionKey": true, "allowedSessionKeyPrefixes": ["hook:", "task:"], "allowedAgentIds": ["main", "coder", "reviewer"] } } ``` ### Эндпоинты #### POST /hooks/wake — Разбудить основную сессию ```bash curl -X POST http://localhost:18789/hooks/wake \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Новая задача в Team Board", "mode": "now"}' ``` **Параметры:** - `text` (required) — описание события - `mode` — `now` (сразу) или `next-heartbeat` (при след. проверке) #### POST /hooks/agent — Запустить изолированную сессию ```bash curl -X POST http://localhost:18789/hooks/agent \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "message": "Напиши функцию сортировки на Python", "name": "TeamBoard", "agentId": "main", "sessionKey": "task:abc123", "deliver": true, "channel": "telegram", "timeoutSeconds": 300 }' ``` **Параметры:** - `message` (required) — промпт для агента - `name` — название хука для логов - `agentId` — ID агента (main, coder, etc.) - `sessionKey` — ключ сессии для отслеживания - `deliver` — отправить ответ в канал - `channel` — канал для ответа (telegram, discord, etc.) - `model` — переопределение модели - `thinking` — уровень размышлений (low, medium, high) - `timeoutSeconds` — таймаут выполнения ### Аутентификация ``` Authorization: Bearer # или x-openclaw-token: ``` ⚠️ Query-string токены (`?token=...`) запрещены. --- ## Team Board → OpenClaw Integration ### Архитектура ``` ┌─────────────────────────────────────────────────────────────┐ │ Team Board │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Web UI │ │ Tasks │ │ Agents │ │ │ │ (Next.js) │ │ Service │ │ Service │ │ │ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ └────────┬────────┘ │ │ │ │ │ ┌────────▼────────┐ │ │ │ OpenClaw │ │ │ │ Client │ │ │ └────────┬────────┘ │ └───────────────────────────────────┼─────────────────────────┘ │ HTTP ▼ ┌───────────────────────────────────────────────────────────────┐ │ OpenClaw Gateway │ │ localhost:18789 │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ /hooks/wake │ │/hooks/agent │ │ Sessions │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Agent: main (Марков) │ │ │ │ - sessions_spawn → субагенты │ │ │ │ - sessions_send → коммуникация │ │ │ │ - sessions_history → история │ │ │ └─────────────────────────────────────────────────────────┘ │ └───────────────────────────────────────────────────────────────┘ ``` ### OpenClaw Client (Python) ```python import httpx from typing import Optional class OpenClawClient: def __init__(self, base_url: str, token: str): self.base_url = base_url self.headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } async def wake(self, text: str, mode: str = "now"): """Разбудить основную сессию""" async with httpx.AsyncClient() as client: response = await client.post( f"{self.base_url}/hooks/wake", headers=self.headers, json={"text": text, "mode": mode} ) return response.json() async def run_agent( self, message: str, session_key: str, agent_id: str = "main", deliver: bool = False, timeout: int = 300 ): """Запустить задачу в изолированной сессии""" async with httpx.AsyncClient() as client: response = await client.post( f"{self.base_url}/hooks/agent", headers=self.headers, json={ "message": message, "name": "TeamBoard", "agentId": agent_id, "sessionKey": session_key, "deliver": deliver, "timeoutSeconds": timeout } ) return response.json() ``` ### Task Execution Flow ```python async def execute_task(task: Task, agent: Agent): client = OpenClawClient( base_url="http://localhost:18789", token=settings.OPENCLAW_TOKEN ) # Формируем промпт prompt = f""" Задача: {task.title} Описание: {task.description} Контекст проекта: {task.project.brief} Выполни задачу и верни результат. """ # Запускаем агента result = await client.run_agent( message=prompt, session_key=f"task:{task.id}", agent_id=agent.slug, timeout=600 ) # Обновляем статус task.status = "in_progress" task.openclaw_session = result.get("sessionKey") await task.save() return result ``` ### Получение результатов **Вариант 1: Polling** ```python async def poll_task_result(task: Task): client = OpenClawClient(...) # Получаем историю сессии history = await client.get_session_history(task.openclaw_session) # Парсим последний ответ last_message = history["messages"][-1] if last_message["role"] == "assistant": task.result = last_message["content"] task.status = "completed" await task.save() ``` **Вариант 2: Callback Webhook** ```python # Team Board получает callback от OpenClaw @app.post("/api/callbacks/openclaw") async def openclaw_callback(data: dict): session_key = data.get("sessionKey") result = data.get("result") # Находим задачу по session_key task = await Task.find_by_session(session_key) task.result = result task.status = "completed" await task.save() ``` --- ## Текущий статус OpenClaw ```bash # Gateway endpoint http://localhost:18789 # Текущий токен cat ~/.openclaw/openclaw.json | jq '.gateway.auth.token' # Webhooks пока не настроены — нужно включить ``` ### Для активации webhooks: ```json // ~/.openclaw/openclaw.json { "hooks": { "enabled": true, "token": "team-board-secret-token", "path": "/hooks" } } ``` --- *Документ обновляется по мере исследования.*