# Phase 4: Matrix MVP — Agent Context + Context Management — Context

**Gathered:** 2026-04-16
**Status:** Ready for planning
**Source:** Conversation context (2026-04-16 design session)

<domain>
## Phase Boundary

Привести Matrix-бот к рабочему состоянию для MVP-деплоя в контейнер:
- Убрать наш кастомный `AgentSessionClient` и thread_id патч из `platform-agent`, перейти на актуальный origin/main платформы с `AgentApi` из `lambda_agent_api`
- Добавить 4 команды управления контекстом агента: `!save`, `!load`, `!reset`, `!context`
- Упаковать Matrix-бот в Docker-контейнер

НЕ входит в фазу:
- Изменения в platform-agent (это задача команды платформы)
- Telegram адаптер
- E2EE
- Skills system (ждём платформу)

</domain>

<decisions>
## Implementation Decisions

### Архитектура платформы (locked)

- **Один контейнер = один чат**: `AgentService` с `thread_id = "default"` — намеренная архитектура. Изоляция на уровне контейнеров, не thread_id. Не менять.
- **Убрать thread_id патч**: наш коммит `1dca2c1` в `external/platform-agent` удаляем. Переходим на `origin/main` platform-agent.
- **Удалить `build_thread_key`**: функция больше не нужна. Убрать из `sdk/agent_session.py` и `sdk/real.py`.
- **Заменить `AgentSessionClient` на `AgentApi`**: использовать `AgentApi` из `external/platform-agent_api/lambda_agent_api/agent_api.py`. Он уже правильно обрабатывает все event-типы (неизвестные → `logger.warning`, без краша).

### !save (locked)

- Синтаксис: `!save` (автоимя по дате/времени) или `!save [имя]`
- Механизм: Matrix-бот посылает агенту текстовое сообщение "Summarize our conversation and save to /workspace/contexts/[name].md. Reply only with: Saved: [name]"
- Имена сохранений хранятся в `PrototypeStateStore` (список для `!load`)
- Агент сам пишет файл через свои инструменты (`write_file`)

### !load (locked)

- `!load` без аргументов → бот показывает нумерованный список сохранений
- Пользователь вводит **число** (1, 2, 3...) для выбора
- Выход из состояния: `0` или `!cancel`
- После выбора: бот посылает агенту "Load context from /workspace/contexts/[name].md and use it as background for our conversation. Reply: Loaded: [name]"
- Состояние ожидания выбора хранится в Matrix store (аналогично pending_confirm)

### !reset (locked)

- Показывает confirmation-диалог:
  ```
  Сбросить контекст агента? Выбери:
    !yes — сбросить
    !save [имя] — сохранить и сбросить
    !no — отмена
  ```
- `!yes` → вызвать `POST {AGENT_BASE_URL}/reset` (resets AgentService singleton)
- `!save имя` → сначала выполняется логика !save, затем POST /reset
- `!no` → отмена
- Fallback если `/reset` endpoint недоступен (404): вернуть пользователю "Reset endpoint недоступен. Обратитесь к администратору."
- `AGENT_BASE_URL` — новая env переменная (HTTP base URL агента, отдельно от `AGENT_WS_URL`)

### !context (locked)

- Показывает: имя текущей сессии (если загружали через `!load`), токены из последнего ответа (`tokens_used` из `MsgEventEnd`), список сохранений (имена + даты)
- Не делает никаких вызовов к агенту

### Dockerfile + docker-compose (locked)

- `Dockerfile` для Matrix-бота (`adapter/matrix/bot.py`)
- `docker-compose.yml` с сервисом `matrix-bot`
- Env переменные через `.env` файл
- Platform-agent запускается отдельно (не входит в compose этой фазы)

### Claude's Discretion

- Структура хранения saved sessions в PrototypeStateStore (dict name→timestamp)
- Формат автоимени для !save без аргументов
- HTTP клиент для POST /reset (aiohttp или httpx)
- Точный формат промптов к агенту для save/load

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Platform клиент (заменяем)
- `sdk/agent_session.py` — текущий AgentSessionClient, УДАЛЯЕМ/ЗАМЕНЯЕМ
- `sdk/real.py` — RealPlatformClient, обновляем под AgentApi
- `external/platform-agent_api/lambda_agent_api/agent_api.py` — новый клиент AgentApi
- `external/platform-agent_api/lambda_agent_api/server.py` — типы сообщений (MsgStatus, MsgEventTextChunk, MsgEventEnd, etc.)
- `external/platform-agent_api/lambda_agent_api/client.py` — MsgUserMessage

### Matrix адаптер (расширяем)
- `adapter/matrix/bot.py` — точка входа, MatrixBot, build_runtime
- `adapter/matrix/handlers/` — существующие обработчики команд
- `adapter/matrix/store.py` — get_room_meta, set_pending_confirm (паттерн для !load state)
- `sdk/prototype_state.py` — PrototypeStateStore, расширяем для saved sessions

### Состояние платформы
- `.planning/threads/matrix-dev-prototype-agent-platform-state.md` — исследование от 2026-04-14

### Существующая архитектура команд
- `core/protocol.py` — IncomingCommand, OutgoingMessage, OutgoingUI
- `core/handlers/` — паттерны регистрации обработчиков

</canonical_refs>

<specifics>
## Specific Ideas

- `AgentApi` требует явного `connect()` при старте и `close()` при завершении — lifecycle нужно встроить в `MatrixBot`
- `AgentApi.send_message()` — AsyncIterator, возвращает `MsgEventTextChunk` чанки и `MsgEventEnd`
- Для `!load` состояние "ожидаем число" хранить по ключу `load_pending:{matrix_user_id}:{room_id}` в store (аналог pending_confirm)
- `AGENT_BASE_URL` — HTTP URL, например `http://127.0.0.1:8000`; `AGENT_WS_URL` = `ws://127.0.0.1:8000/agent_ws/`
- platform-agent origin/main: `POST /reset` эндпоинта нет — это нужно запросить у команды платформы. До тех пор `!reset` возвращает "Reset endpoint недоступен"

</specifics>

<deferred>
## Deferred Ideas

- Замена `PrototypeStateStore` на реальный control-plane из platform-master (Phase 3)
- Skills интеграция через SkillsMiddleware (ждём платформу)
- E2EE для Matrix
- `!reset` через docker restart (заменяется на /reset endpoint когда платформа добавит)
- Суммаризация контекста (агент сам решает как писать в файл)

</deferred>

---

*Phase: 04-matrix-mvp-shared-agent-context-and-context-management-comma*
*Context gathered: 2026-04-16 via conversation design session*
