From 4209bc26fd2c1b80cc6687f1cd685d67bb9bd32b Mon Sep 17 00:00:00 2001 From: Markov Date: Sun, 15 Feb 2026 15:41:37 +0100 Subject: [PATCH] docs: add Agent Management and OpenClaw Webhooks integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agent Registry schema - OpenClaw Webhooks API documentation - Team Board → OpenClaw architecture - Python client example - Task execution flow --- RESEARCH.md | 309 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) diff --git a/RESEARCH.md b/RESEARCH.md index 9d59874..79e72c1 100644 --- a/RESEARCH.md +++ b/RESEARCH.md @@ -354,4 +354,313 @@ class AgentSessionManager: --- +## 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" + } +} +``` + +--- + *Документ обновляется по мере исследования.*