Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions docs/adr/0013-conversation-orchestration.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,27 @@ sequenceDiagram
Talk-->>Edge: текстовый вопрос
end
Edge-->>Channel: общий ответ канала
Channel->>Edge: actionId + payloadHash + решение
Edge->>Action: то же решение + identity
Action-->>Channel: принятое состояние действия
```

Границы компонентов:

- `Channel Gateway` проверяет identity, нормализует запрос и переводит общий ответ в протокол канала.
Он остаётся stateless и не вызывает `Agent Runtime` или `Action Service` напрямую после миграции.
Он остаётся stateless: сообщения передаёт Conversation Service, а команды виджета — Action Service.
Прямой вызов Agent Runtime остаётся только на временном совместимом маршруте.
- `Conversation Service` хранит сообщения и состояние диалога, обеспечивает идемпотентность по
`tenantId + subject + requestKey`, вызывает Agent и Action.
- `Agent Runtime` только возвращает предложение действия или вопрос для уточнения.
- `Action Service` остаётся источником истины для `actionId`, `payloadHash`, статуса и результата.
- `Widget SDK` проверяет и отображает общий контракт карточки. Он не вызывает Agent, MCP или базу.
- Кнопка карточки отправляет решение вместе с `actionId` и `payloadHash`; только `Action Service`
принимает или отклоняет действие.
- Кнопка карточки отправляет решение вместе с `actionId` и `payloadHash` в Channel Gateway. Gateway не
меняет команду, а передаёт её в Action Service; только Action Service принимает или отклоняет действие.

Переход выполняется совместимо: существующий `/api/v1/messages` остаётся временным техническим
маршрутом до появления Conversation Service. Новый диалоговый endpoint добавляется отдельно; старый
удаляется только в следующей major-версии контракта.
Переход выполнен совместимо: `/api/v1/conversations/messages` обслуживает новые каналы, а существующий
`/api/v1/messages` остаётся временным техническим маршрутом. Решение виджета проходит через
`/api/v1/actions/{actionId}/decisions`. Старый маршрут удаляется только в следующей major-версии.

## Причина

Expand All @@ -63,8 +67,8 @@ sequenceDiagram

## Последствия

- Появится отдельный репозиторий `conversation-service` со своей базой и миграциями.
- До его реализации Channel Gateway продолжает прямой вызов Agent как временный мост.
- Conversation Service работает в отдельном репозитории со своей базой и миграциями.
- Channel Gateway сохраняет прямой вызов Agent только для обратной совместимости.
- Контракт ответа канала должен различать текст, карточку подтверждения и результат.
- Карточка содержит только отображаемые данные и ссылку на сохранённое действие; секреты и правила
выполнения в неё не попадают.
Expand Down
35 changes: 35 additions & 0 deletions docs/adr/0014-telegram-account-link.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ADR-0014: Telegram связывается с identity через Device Flow

- Status: accepted
- Date: 2026-09-25

## Контекст

Telegram update содержит внешний user id, но не содержит JWT Portable Agent. Если Gateway будет доверять
этому id, адаптер сможет выдать себя за любого пользователя. Пароль пользователя также нельзя передавать
боту или сохранять в адаптере.

## Решение

Команда `/link` начинает OAuth 2.0 Device Authorization Grant в Keycloak. Пользователь открывает ссылку,
входит в Keycloak и подтверждает одноразовый код. После подтверждения адаптер получает короткий access
token и refresh token.

Access token используется для вызова Channel Gateway от имени пользователя. Refresh token шифруется
AES-256-GCM до записи в отдельную базу Telegram Adapter. Ключ приходит из secret manager и не хранится
в Git. Telegram user id остаётся только внешним ключом канала.

## Границы

- Telegram Adapter отвечает за webhook, account link и формат Telegram.
- Keycloak подтверждает identity и выпускает токены.
- Channel Gateway продолжает проверять JWT и не доверяет заголовкам с внешним user id.
- Conversation Service хранит диалог.
- Action Service принимает решение и выполняет действие.

## Последствия

- первый вход требует открыть страницу Keycloak;
- адаптер хранит зашифрованную связь и становится stateful;
- нужны polling, `/unlink`, отзыв refresh token и ротация ключа;
- такой же подход можно повторить для VK и других каналов без изменения Gateway.
8 changes: 5 additions & 3 deletions docs/architecture/REPOSITORY_MODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Portable Agent использует GitHub Organization и отдельную р
|---|---|---|
| `.github` | Общие шаблоны, правила и CI/CD | Создан, развивается |
| `platform` | Архитектура, ADR, карта и публичная документация | Создан, сайт опубликован |
| `contracts` | OpenAPI, AsyncAPI, JSON Schema и примеры | Bundle `2.1.0` выпущен и используется сервисами |
| `contracts` | OpenAPI, AsyncAPI, JSON Schema и примеры | Локальный bundle `2.5.0` используется сервисами |
| `action-service` | Java-сервис действий | jOOQ, MVC, outbox и Temporal worker работают в backend-срезе |
| `agent-runtime` | Python-сервис агента | MVC, JWT и предложение календарного действия работают в backend-срезе |
| `deploy` | Compose, Helm charts и тестовые окружения | Локальный backend-срез и GitHub acceptance проходят одной командой |
Expand All @@ -17,6 +17,9 @@ Portable Agent использует GitHub Organization и отдельную р
| `calendar-mcp` | MCP-интеграция календаря | Fake Calendar, OIDC и идемпотентность работают в общем сценарии |
| `mcp-gateway` | Безопасный вызов настроенных MCP-сервисов | Stateless-маршрутизатор работает между Action и Calendar MCP |
| `channel-gateway` | Единый вход независимых каналов | Текстовый API, JWT и вызов Agent Runtime работают в общем сценарии |
| `conversation-service` | Состояние диалога | PostgreSQL, защита повторов и цепочка Agent → Action работают в общем сценарии |
| `widget-sdk` | Независимая от канала модель виджета | Карточка подтверждения и decision command проверяются без UI-фреймворка |
| `telegram-adapter` | Тонкая граница Telegram | Локальная репа, защищённый webhook и начало Device Flow проверены тестами и Docker build |

Каркас означает, что настроены структура и инженерные проверки. Это не означает, что правила бизнеса уже
спроектированы или реализованы.
Expand All @@ -25,10 +28,9 @@ Portable Agent использует GitHub Organization и отдельную р

```text
portable-agent organization
├── conversation-service состояние диалога
├── approval-service подтверждение действий
├── policy-bundle правила OPA
└── widget-sdk переносимые виджеты
└── следующие channel adapters
```

Названия и границы запланированных репозиториев могут измениться до начала реализации.
Expand Down
30 changes: 17 additions & 13 deletions docs/delivery/CURRENT_STATUS.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
# Текущее состояние по репозиториям

Дата среза: 14 сентября 2026 года. Этот файл обновляется после мержа этапа, а не после локального
эксперимента.
Дата среза: 25 сентября 2026 года. Этот файл обновляется после проверенного локального этапа или мержа,
а не после незавершённого эксперимента.

| Репа | Что меняем или добавляем | Ожидаемый результат | Фактический результат | Следующий шаг |
|---|---|---|---|---|
| `.github` | Общие workflows и правила | Одинаковый CI/CD для всех сервисов | Java, Python, Node, docs, security и container workflows работают; чистый Trivy runner исправлен | Подключать правила к каждой новой репе |
| `contracts` | Версионируемые внешние и внутренние API | Один проверяемый источник сетевых DTO | Bundle `2.3.0` выпущен; добавлен Conversation API | Подключить контракт к Conversation Service |
| `action-service` | Подтверждение и надёжное выполнение | `AWAITING_APPROVAL → APPROVED → EXECUTING → SUCCEEDED/FAILED` | jOOQ, outbox, Temporal worker и вызов MCP Gateway работают; backend acceptance зелёный | Принимать команду через Channel Gateway |
| `contracts` | Версионируемые внешние и внутренние API | Один проверяемый источник сетевых DTO | Локальный bundle `2.5.0`: сообщения, confirmation card и решение через Gateway | Выпустить после восстановления GitHub |
| `action-service` | Подтверждение и надёжное выполнение | `AWAITING_APPROVAL → APPROVED → EXECUTING → SUCCEEDED/FAILED` | jOOQ, outbox, Temporal worker и вызов MCP Gateway работают; решение приходит через Channel Gateway | Оставить источником истины для решения и статуса |
| `calendar-mcp` | `create_event` | Идемпотентное создание fake-события | Fake Calendar, OIDC и защита от дублей работают в общем сценарии | Оставить эталонным fake-коннектором |
| `mcp-gateway` | OIDC, allowlist и MCP client | Безопасный stateless-маршрутизатор | Foundation смержен и проверен вызовом Calendar MCP | Добавлять коннекторы только по контракту |
| `deploy` | Приложения в локальном Compose | Одна команда поднимает вертикальный backend-срез | Channel, Agent, Action, Temporal, MCP Gateway и Calendar поднимаются; GitHub smoke зелёный | Ускорить сборку полного smoke |
| `test-lab` | Календарный acceptance-тест | Один JWT и один контракт на всём пути | Схема Agent `2.1.0`, подтверждение и проверка часового пояса работают | Добавить повтор запроса и проверку отсутствия дубля |
| `deploy` | Приложения в локальном Compose | Одна команда поднимает вертикальный backend-срез | Чистый срез поднят и проверен; Gateway связан с Conversation и Action | Подключить Telegram adapter после решения по identity |
| `test-lab` | Календарный acceptance-тест | Один JWT и один контракт на всём пути | 19/19: карточка, решение через Gateway, Temporal, offset и отсутствие дубля | Добавить первый адаптер канала |
| `agent-runtime` | Текст в предложение действия | Детерминированное предложение встречи без скрытого выполнения | API `2.1.0`, проверка JWT и календарное предложение работают в общем сценарии | Вызывать через Channel Gateway, AI-модель пока не выбирать |
| `channel-gateway` | Общий вход каналов | Web и Telegram используют один контракт | Репа создана; JWT, OpenAPI-типы и вызов Agent работают в общем acceptance | После Conversation Service заменить временный прямой вызов Agent |
| `conversation-service` | Состояние диалога и прикладная оркестрация | Повтор сообщения не создаёт второе действие | Хранение и durable processing смержены: ключ повтора, TTL, atomic lease, fencing token и reply cleanup проверены на PostgreSQL | Вызвать Agent Runtime по закреплённому контракту |
| `widget-sdk` | Карточка подтверждения | Один UI-контракт для разных каналов | Renderer-neutral core `0.1.0` выпущен; runtime schema, generated type и decision command проверены | Подключить к первому адаптеру после Conversation Service |
| `channel-gateway` | Общий вход каналов | Web и Telegram используют один контракт | Сообщение идёт через Conversation; команда Widget SDK — через Gateway в Action | Подключить первый адаптер |
| `conversation-service` | Состояние диалога и прикладная оркестрация | Повтор сообщения не создаёт второе действие | PostgreSQL, lease, Agent → Action, карточка и сохранение offset проверены общим E2E | Оставить владельцем диалога при подключении адаптера |
| `widget-sdk` | Карточка подтверждения | Один UI-контракт для разных каналов | Renderer-neutral core создаёт проверенную decision command | Подключить к Telegram adapter |
| `telegram-adapter` | Telegram как сменный канал | Telegram identity привязывается только после входа через Keycloak | Webhook и полный Device Flow: lease, AES-256-GCM, PostgreSQL, 25/25 unit, repository integration и Docker build | Обновлять access token и передавать сообщение в Gateway |

## Что уже проверено

Expand All @@ -32,7 +33,7 @@
```

Проверка запускает реальные контейнеры отдельных репозиториев по закреплённым commit SHA. Последний
зелёный запуск вошёл в `deploy` через merge `722d33f`.
локальный зелёный запуск: `deploy` `3e67663`, 19 из 19 проверок прошли 23 сентября 2026 года.

## Текущий порядок

Expand All @@ -48,9 +49,12 @@
-> [готово] каркас Conversation Service
-> [готово] privacy-first решение и идемпотентное хранение
-> [готово] durable processing и защита нескольких worker
-> [следом] вызов Agent Runtime
-> переключение Channel Gateway
-> Telegram adapter
-> [готово] вызов Agent Runtime и создание Action
-> [готово] переключение Channel Gateway
-> [готово] решение виджета через Channel Gateway
-> [решено] Telegram identity привязывается через OAuth Device Flow
-> [готово] Telegram adapter: webhook, `/link`, polling и зашифрованная связь
-> [следом] refresh access token и сообщение через Gateway
```

## Правило результата
Expand Down
14 changes: 9 additions & 5 deletions docs/product/CALENDAR_EVENT_MVP.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,24 @@ sequenceDiagram
autonumber
actor User as Пользователь
participant Channel as Канал
participant Talk as Conversation Service
participant Agent as Agent Runtime
participant Action as Action Service
participant Widget as Виджет
participant Flow as Temporal
participant Calendar as Fake Calendar

User->>Channel: Создай встречу завтра в 12:00 на 30 минут
Channel->>Agent: Нормализованный текст и часовой пояс
Agent-->>Channel: Уточнение, если данных не хватает
Agent->>Action: calendar.create_event + payload + requestKey
Action-->>Widget: AWAITING_APPROVAL + payloadHash
Channel->>Talk: Нормализованный текст и часовой пояс
Talk->>Agent: Разобрать сообщение
Agent-->>Talk: Уточнение, если данных не хватает
Talk->>Action: calendar.create_event + payload + requestKey
Action-->>Talk: AWAITING_APPROVAL + payloadHash
Talk-->>Widget: Карточка сохранённого действия
Widget-->>User: Показать точные данные
User->>Widget: Подтвердить payloadHash
Widget->>Action: CONFIRM
Widget->>Channel: CONFIRM + payloadHash
Channel->>Action: Та же команда решения
Action->>Flow: Выполнить сохранённое действие
Flow->>Calendar: Создать событие с requestKey
Calendar-->>Flow: eventId
Expand Down
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ nav:
- Платформа разработки: adr/0010-developer-platform.md
- Режимы container CI: adr/0011-container-ci-modes.md
- Contract-first: adr/0012-contract-first.md
- Conversation Service: adr/0013-conversation-orchestration.md
- Привязка Telegram: adr/0014-telegram-account-link.md
Loading
Loading