После первой части цикла, где были введены сущности AgentTurn, AgentPlanItem и AgentEvent, автор переходит к следующему слою архитектуры. Трёх таблиц оказывается недостаточно: агент всё ещё не умеет хранить выданные пользователем разрешения, восстанавливать диалог после разрыва соединения и безопасно запускать длинные операции в фоне.
Центральная идея статьи — каждая из этих проблем решается отдельной durable-сущностью, а не хранением данных в памяти процесса. Память процесса исчезает при рестарте; база данных — нет.
| Сущность | Что хранит | Зачем нужна |
|---|---|---|
| ApprovalGrant | Выданное пользователем разрешение | Не спрашивать повторно одно и то же действие в рамках допустимого scope |
| SessionContext | Активный turn, профиль агента, краткую историю, pending approval | Восстановить диалог и текущую сцену сессии |
| ProjectContext | Активный проект, файлы, настройки, текущую операцию | Не дать двум тяжёлым операциям одновременно менять один проект |
| BackgroundJob | Длинную операцию вне HTTP-запроса | Парсинг, retry, progress, cancellation |
| WorkerHeartbeat | Присутствие и занятость исполнителя | Отличить долгую работу от упавшего worker-а |
ApprovalGrant — это запись о том, что пользователь разрешил конкретное действие. Сущность привязана к session_id, project_id, tool_name, режиму доступа mode и полю scope в формате JSON, которое описывает машинно проверяемые границы: например, только чтение, только в рамках одного проекта, только до определённого времени. Поле expires_at делает разрешение временным — агент не накапливает бессрочные права. Если approval хранится только в памяти, после рестарта агент либо снова спросит пользователя, либо продолжит без понятного основания — оба варианта плохи в продакшене.
SessionContext хранит компактную техническую карточку сессии: активный turn, pending approval, event cursor для восстановления UI после reconnect.
SessionContext решает другую задачу: восстановить «сцену» после любого разрыва — закрытой вкладки, перезапуска backend, упавшего worker-а. Это не полный лог сообщений и не вся память агента, а компактная техническая карточка: какой turn_id сейчас активен, есть ли незавершённое подтверждение в поле pending_approval, с какого события нужно продолжить чтение UI (event_cursor), какой профиль агента выбран. Поле summary хранит сжатую историю диалога, чтобы не тащить весь transcript в каждый prompt к LLM. Автор особо оговаривает: SessionContext не должен превращаться в «помойку» — base64 файлов, трейсбеки и ответы модели туда не кладут.
ProjectContext защищает рабочую область от состояния гонки. Пользователь может открыть один проект в нескольких вкладках и запустить несколько операций подряд. Без durable-состояния проекта два фоновых job-а могут одновременно начать изменять одни и те же файлы. ProjectContext хранит, какая операция сейчас активна, и не даёт запустить вторую тяжёлую операцию поверх первой.
BackgroundJob и WorkerHeartbeat закрывают проблему длинных операций — парсинга, пакетной обработки файлов, retry-циклов. HTTP-запрос не может держать соединение минутами; фоновый job может. WorkerHeartbeat при этом позволяет системе отличить worker, который долго работает, от worker-а, который просто умер и перестал обновлять heartbeat. Без этого механизма lease на задачу может зависнуть навсегда.
Пятая сущность — политика durable payload — описывает правила того, что вообще допустимо хранить в event log. Base64-содержимое файлов, секреты и длинные строки туда не попадают: для них есть файловое хранилище и blob storage. Это не архитектурная деталь, а требование к операционной стоимости: event log, разбухший от бинарных данных, становится дорогим и медленным.
В совокупности пять сущностей дополняют три из первой части и формируют минимальный набор для агента, который переживает рестарты, не теряет разрешения и не ломает проект параллельными операциями. Подход не привязан к конкретному LLM или фреймворку — это слой персистентности поверх любой модели.


