# Руководство по созданию новой поверхности

Этот документ описывает, как написать новую новую поверхность (например, Discord, Slack или Custom Web) по образцу текущей Matrix-поверхности в ветке `main`.

Он основан на актуальной реализации Matrix surface в репозитории и отражает текущую продакшн-логику, а не устаревший легаси.

---

## 1. Общая архитектура

### 1.1. Что такое поверхность

Поверхность — это тонкий адаптер между конкретной платформой (Платформа) и общим ядром бота.

В репозитории есть разделение:

- `core/` — общее ядро и бизнес-логика
- `adapter/<platform>/` — реализация конкретной поверхности
- `sdk/real.py` — работа с реальной платформой / агентом
- `config/` — статическая конфигурация агентов
- `docs/surface-protocol.md` — общий контракт поверхностей

### 1.2. Как это работает

Поверхность должна:

- принимать нативные события от Платформа
- преобразовывать их в единый внутренний контракт (`IncomingMessage`, `IncomingCommand`, `IncomingCallback`)
- передавать их в `core`
- получать ответы из `core` (`OutgoingMessage`, `OutgoingUI`, `OutgoingTyping`, `OutgoingNotification`)
- преобразовывать ответы обратно в нативные нативные сообщения

Поверхность не должна:

- управлять жизненным циклом агентских контейнеров
- хранить долгую историю бесед вне `core`/платформы
- аутентифицировать пользователей сама (если это не часть Платформа API)

---

## 2. Структура новой поверхности

### 2.1. Основные каталоги

Рекомендуемая структура для новой платформы:

```
adapter/<platform>/
  bot.py
  converter.py
  agent_registry.py
  files.py
  handlers/
  store.py
```

### 2.2. Принцип reuse

По примеру Matrix surface, New surface должен переиспользовать общий `core` и общий `sdk`.

Не дублируйте бизнес-логику, а реализуйте только адаптер:

- `adapter/<platform>/converter.py` — конвертация событий платформы ⇄ внутренние структуры
- `adapter/<platform>/bot.py` — основной runtime, старт Платформа client, loop, отправка/прием
- `adapter/<platform>/agent_registry.py` — загрузка `config/<platform>-agents.yaml`
- `adapter/<platform>/files.py` — хранение входящих/исходящих вложений

---

## 3. Контракт входящих/исходящих событий

### 3.1. Внутренний формат

Смотрите `core/protocol.py`. Основные типы:

- `IncomingMessage` — обычное текстовое сообщение + вложения
- `IncomingCommand` — управляющая команда
- `IncomingCallback` — подтверждение / интерактивные действия
- `OutgoingMessage` — ответ пользователю
- `OutgoingUI` — интерфейсные элементы (кнопки и т.п.)
- `OutgoingTyping` — индикатор печати
- `OutgoingNotification` — системное уведомление

### 3.2. Пример конверсии Matrix

В Matrix-реализации `adapter/matrix/converter.py`:

- текст `!yes` / `!no` превращается в `IncomingCallback` с `action: confirm/cancel`
- `!list`/`!remove` говорят не агенту, а surface-процессу
- вложения `m.file`, `m.image`, `m.audio`, `m.video` нормализуются в `Attachment`

Для Платформа реализуйте аналогичную логику для native команд вашего клиента.

---

## 4. Реестр агентов и маршрутизация

### 4.1. Что хранит реестр

В текущей Matrix реализации есть `config/matrix-agents.yaml` и `adapter/matrix/agent_registry.py`.

Структура:

```yaml
user_agents:
  "@user0:matrix.example.org": agent-0
  "@user1:matrix.example.org": agent-1

agents:
  - id: agent-0
    label: "Agent 0"
    base_url: "http://lambda.coredump.ru:7000/agent_0/"
    workspace_path: "/agents/0"
```

### 4.2. Логика выбора агента

- `user_agents` маппит конкретного пользователя на `agent_id`
- если user_id не найден, используется первый агент из списка
- `agents[].base_url` определяет URL агента
- `agents[].workspace_path` определяет путь внутри surface-контейнера для этого агента

Это важно: именно на этом контракте строится разделение агентов по рабочим каталогам.

### 4.3. Рекомендуемая Версия для новой платформы

Создайте `config/<platform>-agents.yaml` с тем же смыслом.

- `user_agents` — маппинг external user_id → agent_id
- `agents` — список агентов
- `workspace_path` для каждого агента должен быть абсолютным путем внутри surface-контейнера, например `/agents/0`

---

## 5. Файловый контракт

### 5.1. Shared volume

Текущее Matrix-решение использует shared volume:

- surface монтирует общий том как `/agents`
- каждый агент видит свою поддиректорию как `/workspace`

Топология:

```
Bot (/agents)              Agent (/workspace = /agents/N/)
  /agents/0/report.pdf  ←──→  /workspace/report.pdf
```

### 5.2. Правила записи файлов

В `adapter/matrix/files.py` реализовано:

- входящий файл сохраняется прямо в `{workspace_root}/{filename}`
- возвращается путь `workspace_path` относительный внутри рабочего каталога агента
- при коллизии имен создаётся `file (1).ext`, `file (2).ext`
- `Attachment.workspace_path` передаётся агенту

Для исходящих файлов:

- surface читает файл из `workspace_root / workspace_path`
- загружает его в платформу

### 5.3. Пример поведения

- Пользователь отправляет файл → surface скачивает файл и кладёт его в agent workspace
- Агент получает `attachments=["report.pdf"]` и работает с относительным `workspace_path`
- Агент пишет результат в `/workspace/result.txt`
- surface читает `/agents/{N}/result.txt` и отправляет файл пользователю

---

## 6. Чат-менеджмент и контекст

### 6.1. `platform_chat_id`

Matrix-реализация использует `platform_chat_id` как стабильный идентификатор чата на стороне агента.

- `room_meta.platform_chat_id` определяется и сохраняется в `adapter/matrix/store.py`
- `reconcile_startup_state()` восстанавливает отсутствующие `platform_chat_id` при рестарте
- `RoutedPlatformClient` перенаправляет запросы агенту по `agent_id` + `platform_chat_id`

Для New surface тот же принцип:

- каждая внешняя беседа должна привязываться к одному внутреннему `chat_id`
- этот `chat_id` используется для вызовов агента
- если в Платформа есть несколько комнат/топиков, каждая должна иметь свой `surface_ref`

### 6.2. Команды управления чатами

Matrix поддерживает следующие команды, которые нужно сохранить в Платформа:

- `!new [название]` — создать новый чат
- `!chats` — список активных чатов
- `!rename <название>` — переименовать текущий чат
- `!archive` — архивировать чат
- `!clear` / `!reset` — сбросить контекст текущего чата
- `!yes` / `!no` — подтвердить или отменить действие агента
- `!list` — показать очередь вложений
- `!remove <n>` / `!remove all` — удалить вложение из очереди
- `!help` — справка

Эти команды реализованы в Matrix через `adapter/matrix/handlers/`.

### 6.3. Очередь вложений

Matrix surface поддерживает staged attachments:

- файл может быть отправлен без текста
- surface сохраняет файл в `staged_attachments` для конкретного room_id + user_id
- следующий текст отправляется агенту вместе со всеми файлами из очереди

В Платформа можно реализовать ту же модель:

- `!list` показывает текущую очередь
- `!remove` удаляет файл из очереди
- команда-индикатор или следующее текстовое сообщение отправляет queued attachments агенту

---

## 7. Runtime и окружение

### 7.1. Переменные среды

Для Matrix surface текущий runtime ожидает:

- `MATRIX_HOMESERVER` — URL Matrix-сервера
- `MATRIX_USER_ID` — `@bot:example.org`
- `MATRIX_PASSWORD` или `MATRIX_ACCESS_TOKEN`
- `MATRIX_PLATFORM_BACKEND` — должно быть `real` для продакшна
- `MATRIX_AGENT_REGISTRY_PATH` — путь к `config/matrix-agents.yaml`
- `AGENT_BASE_URL` — fallback URL агента
- `SURFACES_WORKSPACE_DIR` — путь к shared volume внутри контейнера (по умолчанию `/workspace` в коде, но в docs рекомендуют `/agents`)

Для New surface используйте аналогичные переменные:

- `PLATFORM_PLATFORM_BACKEND=real`
- `PLATFORM_AGENT_REGISTRY_PATH=/app/config/<platform>-agents.yaml`
- `SURFACES_WORKSPACE_DIR=/agents`
- `AGENT_BASE_URL` — если хотите общий fallback

### 7.2. Environment contract

В коде `adapter/matrix/bot.py`:

- `_agent_base_url_from_env()` читает `AGENT_BASE_URL` или `AGENT_WS_URL`
- `_load_agent_registry_from_env()` читает `MATRIX_AGENT_REGISTRY_PATH`
- `_build_platform_from_env()` выбирает `RealPlatformClient` при `MATRIX_PLATFORM_BACKEND=real`

В New surface реализуйте ту же логику, заменив префиксы на `PLATFORM_`.

---

## 8. Локальное тестирование

Для тестирования новой поверхности вместе с одним локальным агентом используйте паттерн `docker-compose.fullstack.yml`.
В этом режиме:
- Запускается 1 контейнер вашей поверхности
- Запускается 1 контейнер `platform-agent`
- Поднимается локальный shared volume (`surfaces-agents`)
- Поверхность настроена маршрутизировать запросы на `http://platform-agent:8000` (через `AGENT_BASE_URL`)
- Пользователь общается с ботом, а бот напрямую общается с локальным агентом, разделяя с ним общую папку для файлов.

Это самый быстрый способ проверить интеграцию новой платформы без внешнего бэкенда.

---

## 9. Реализация шаг за шагом

1. Скопировать `adapter/matrix/` как шаблон для `adapter/<platform>/`.
2. Сделать `adapter/<platform>/converter.py`:
   - превратить native нативные сообщения в `IncomingMessage`
   - превратить команды в `IncomingCommand`
   - превратить yes/no-подтверждения в `IncomingCallback`
3. Сделать `adapter/<platform>/agent_registry.py` на основе `adapter/matrix/agent_registry.py`.
4. Сделать `adapter/<platform>/files.py` на основе `adapter/matrix/files.py`.
5. Сделать `adapter/<platform>/bot.py`:
   - инстанцировать runtime
   - читать env vars `PLATFORM_*`
   - загружать реестр агентов
   - обрабатывать входящие события
   - отправлять `Outgoing*` обратно в Платформа
6. Реализовать команды управления чатами и очередь вложений.
7. Прописать `config/<platform>-agents.yaml`.
8. Прописать `docker-compose.platform.yml` или аналог, чтобы surface монтировал `/agents`.
9. Написать тесты по аналогии с `tests/adapter/matrix/`.
10. Проверить, что все env vars читаются из окружения и не зависят от устаревших Matrix-переменных.

---

## 10. Важные замечания

- Текущий Matrix surface на ветке `main` — активная реализация, а не устаревший легаси.
- Документация и код согласованы: `agent_registry`, `files`, `routed_platform`, `reconciliation` работают вместе.
- Обязательно явно задавайте `SURFACES_WORKSPACE_DIR=/agents` в production, если `workspace_path` в реестре указывает на `/agents/*`.
- Для New surface сохраните ту же архитектуру: surface = thin adapter, агенты = внешние сервисы.
- Не пытайтесь в surface реализовывать логику запуска/стопа агент-контейнеров.

---

## 11. Полезные ссылки внутри репозитория

- `README.md`
- `docs/deploy-architecture.md`
- `docs/surface-protocol.md`
- `adapter/matrix/bot.py`
- `adapter/matrix/converter.py`
- `adapter/matrix/agent_registry.py`
- `adapter/matrix/files.py`
- `adapter/matrix/routed_platform.py`
- `adapter/matrix/reconciliation.py`
- `tests/adapter/matrix/`
