---
read_when:
    - Перенос данных среды выполнения OpenClaw, кэша, транскриптов, состояния задач или временных файлов в SQLite
    - Проектирование миграций doctor из устаревших файлов JSON или JSONL
    - Изменение поведения резервного копирования, восстановления, VFS или хранилища worker
    - Удаление блокировок сеансов, очистки, усечения или путей совместимости JSON
summary: План миграции для превращения SQLite в основной слой долговременного состояния и кеша при сохранении файловой поддержки конфигурации
title: Рефакторинг состояния с приоритетом базы данных
x-i18n:
    generated_at: "2026-06-28T23:41:56Z"
    model: gpt-5.5
    postprocess_version: locale-links-v1
    provider: openai
    source_hash: 54995a9f43f740e7cc3ac3e0a4b69d73ddba6b2c30731193ab7ce3aa1dfc9d94
    source_path: refactor/database-first.md
    workflow: 16
---

# Рефакторинг состояния с приоритетом базы данных

## Решение

Использовать двухуровневую схему SQLite:

- Глобальная база данных: `~/.openclaw/state/openclaw.sqlite`
- База данных агента: одна база данных SQLite на агента для принадлежащего агенту рабочего пространства,
  транскрипта, VFS, артефактов и крупного состояния среды выполнения на уровне агента
- Конфигурация остается файловой: `openclaw.json` остается вне
  базы данных. Профили авторизации среды выполнения переносятся в SQLite; внешние файлы учетных данных провайдера или CLI
  остаются управляемыми владельцем вне базы данных OpenClaw.

Глобальная база данных является базой данных плоскости управления. Она владеет обнаружением агентов,
общим состоянием Gateway, сопряжением, состоянием устройств/узлов, журналами задач и потоков, состоянием плагинов,
состоянием среды выполнения планировщика, метаданными резервного копирования и состоянием миграций.

База данных агента является базой данных плоскости данных. Она владеет метаданными сеанса агента,
потоком событий транскрипта, рабочим пространством VFS или временным пространством имен, артефактами инструментов,
артефактами запусков и доступными для поиска/индексирования локальными кэширующими данными агента.

Это дает единое долговечное глобальное представление без принудительного помещения крупных рабочих пространств агентов,
транскриптов и бинарных временных данных в общий путь записи Gateway.

## Жесткий контракт

У этой миграции есть одна каноническая форма среды выполнения:

- Строки сеансов сохраняют только метаданные сеанса. Они не должны сохранять
  `transcriptLocator`, пути к файлам транскриптов, соседние пути JSONL, пути блокировок,
  метаданные обрезки или указатели совместимости файловой эпохи.
- Идентичность транскрипта всегда является идентичностью SQLite: `{agentId, sessionId}` плюс
  необязательные метаданные темы там, где это нужно протоколу.
- `sqlite-transcript://...` не является идентичностью среды выполнения или протокола. Новый код не должен
  выводить, сохранять, передавать, разбирать или мигрировать локаторы транскриптов. Среда выполнения и
  тесты вообще не должны содержать псевдолокаторы; документация может упоминать эту строку
  только чтобы запретить ее.
- Устаревшие `sessions.json`, JSONL транскриптов, `.jsonl.lock`, обрезка, усечение
  и старая логика путей сеансов относятся только к пути миграции/импорта doctor.
- Устаревшие псевдонимы конфигурации сеансов относятся только к миграции doctor. Среда выполнения не
  интерпретирует `session.idleMinutes`, `session.resetByType.dm` или
  межагентские псевдонимы основного сеанса `agent:main:*` для другого настроенного агента.
- Идентичность маршрутизации сеанса является типизированным реляционным состоянием. Горячие пути среды выполнения и UI
  должны читать `sessions.session_scope`, `sessions.account_id`,
  `sessions.primary_conversation_id`, `conversations` и
  `session_conversations`; они не должны разбирать `session_key` или извлекать
  идентичность провайдера из `session_entries.entry_json`, кроме как в виде совместимой
  тени, пока удаляются старые места вызова.
- Маркеры личных сообщений на уровне канала, такие как `dm` и `direct`, являются словарем маршрутизации,
  а не локаторами транскриптов или дескрипторами совместимости файлового хранилища.
- Устаревшая конфигурация обработчиков hooks относится только к поверхностям предупреждений/миграции doctor.
  Среда выполнения не должна загружать `hooks.internal.handlers`; hooks выполняются только через обнаруженные
  каталоги hooks и метаданные `HOOK.md`.
- Запуск среды выполнения, горячие пути ответов, Compaction, сброс, восстановление, диагностика,
  TTS, hooks памяти, субагенты, маршрутизация команд плагинов, границы протоколов и
  hooks должны передавать `{agentId, sessionId}` через среду выполнения.
- Тесты должны заполнять и проверять строки транскриптов SQLite через
  `{agentId, sessionId}`. Тесты, которые доказывают только проброс пути JSONL,
  сохранение локатора, предоставленного вызывающим кодом, или совместимость с файлом транскрипта, должны
  быть удалены, если они не покрывают импорт doctor, материализацию несеансовых материалов поддержки/отладки
  или форму протокола.
- `runEmbeddedPiAgent(...)`, подготовленные запуски воркеров и внутренняя встроенная
  попытка не должны принимать локаторы транскриптов. Они открывают менеджер транскриптов SQLite
  по `{agentId, sessionId}` и передают этот менеджер во внутреннюю
  PI-совместимую сессию агента, чтобы устаревшие вызывающие места не могли заставить runner записывать
  транскрипты JSON/JSONL.
- Диагностика runner должна сохранять записи трассировки среды выполнения/кэша/payload в SQLite.
  Диагностика среды выполнения не должна раскрывать настройки переопределения файлов JSONL или общие
  помощники экспорта транскриптов JSONL; пользовательские экспорты могут материализовать явные
  артефакты из строк базы данных без передачи имен файлов обратно в среду выполнения.
- Сырое логирование потока использует `OPENCLAW_RAW_STREAM=1` плюс диагностические строки SQLite.
  Старый контракт pi-mono `PI_RAW_STREAM`, `PI_RAW_STREAM_PATH` и
  файлового логгера `raw-openai-completions.jsonl` не является частью среды выполнения
  или тестов OpenClaw.
- Индексация памяти QMD не должна экспортировать транскрипты SQLite в файлы markdown.
  QMD индексирует только настроенные файлы памяти; поиск по транскриптам сеансов остается
  основанным на SQLite.
- Подпуть SDK QMD предназначен только для QMD в новом коде. Помощники индексации транскриптов сеансов SQLite
  находятся в `memory-core-host-engine-session-transcripts`; любой
  реэкспорт QMD предназначен только для совместимости и не должен использоваться кодом среды выполнения.
- Встроенные индексы памяти находятся в базе данных агента-владельца. Конфигурация среды выполнения и
  разрешенные контракты среды выполнения не должны раскрывать `memorySearch.store.path`; doctor
  удаляет этот устаревший ключ конфигурации, а текущий код передает агентский
  `databasePath` внутренне.

Работа по реализации должна продолжать удалять код, пока эти утверждения не станут истинными
без исключений вне границ doctor/импорта/экспорта/отладки.

## Целевое состояние и прогресс

### Жесткая цель

- Одна глобальная база данных SQLite владеет состоянием плоскости управления:
  `state/openclaw.sqlite`.
- Одна база данных SQLite на агента владеет состоянием плоскости данных:
  `agents/<agentId>/agent/openclaw-agent.sqlite`.
- Конфигурация остается файловой. `openclaw.json` не является частью этого
  рефакторинга базы данных.
- Устаревшие файлы являются только входными данными миграции doctor.
- Среда выполнения никогда не записывает и не читает JSONL сеансов или транскриптов как активное состояние.

### Целевые состояния

- `not-started`: код среды выполнения файловой эпохи все еще записывает активное состояние.
- `migrating`: код doctor/импорта может переносить файловые данные в SQLite.
- `dual-read`: временный мост читает и SQLite, и устаревшие файлы. Это состояние
  запрещено для этого рефакторинга, если оно явно не задокументировано как
  предназначенное только для doctor.
- `sqlite-runtime`: среда выполнения читает и записывает только SQLite.
- `clean`: устаревшие API среды выполнения и тесты удалены, а защита предотвращает
  регрессии.
- `done`: документация, тесты, резервное копирование, миграция doctor и проверки изменений доказывают
  чистое состояние.

### Текущее состояние

- Сеансы: `clean` для среды выполнения. Строки сеансов находятся в базе данных на агента,
  API среды выполнения используют `{agentId, sessionId}` или `{agentId, sessionKey}`, а
  `sessions.json` является устаревшим входом только для doctor.
- Транскрипты: `clean` для среды выполнения. События транскриптов, идентичности, снимки
  и события среды выполнения траекторий находятся в базе данных на агента. Среда выполнения больше
  не принимает локаторы транскриптов или пути к транскриптам JSONL.
- Встроенный runner PI: `clean`. Встроенные запуски PI, подготовленные воркеры, Compaction
  и циклы повторных попыток используют область сеанса SQLite и отклоняют устаревшие дескрипторы транскриптов.
- Cron: `clean` для среды выполнения. Среда выполнения использует `cron_jobs` и `cron_run_logs`;
  тесты среды выполнения используют именование `storeKey` SQLite, а пути Cron файловой эпохи остаются
  только в тестах устаревшей миграции doctor.
- Реестр задач: `clean`. Строки среды выполнения задач и TaskFlow находятся в
  `state/openclaw.sqlite`; не выпущенные импортеры сторонних SQLite удалены.
- Состояние плагинов: `clean`. Строки состояния/blob плагинов находятся в общей глобальной
  базе данных; старые помощники стороннего SQLite для состояния плагинов защищены от использования.
- Память: `sqlite-runtime` для встроенной памяти и индексации транскриптов сеансов.
  Таблицы индексов памяти находятся в базе данных на агента, состояние памяти плагинов использует
  общие строки состояния плагинов, а устаревшие файлы памяти являются входами миграции doctor
  или содержимым пользовательского рабочего пространства.
- Резервное копирование: `sqlite-runtime`. Этапы резервного копирования уплотняют снимки SQLite, пропускают живые
  побочные файлы WAL/SHM, проверяют целостность SQLite и записывают запуски резервного копирования в
  глобальную базу данных.
- Миграция doctor: `migrating`, намеренно. Doctor импортирует устаревшие JSON,
  JSONL и выведенные из употребления сторонние хранилища в SQLite, записывает запуски/источники миграций
  и удаляет успешно обработанные источники.
- Скрипты E2E: `clean` для покрытия среды выполнения. Заполнение Docker MCP записывает строки SQLite.
  Docker-скрипт runtime-context создает устаревший JSONL только внутри
  seed для миграции doctor и явно называет путь к устаревшему индексу сеансов.

### Оставшаяся работа

- [x] Переименовать переменные хранилища в runtime-тестах Cron, убрав `storePath`, если только
      они не являются устаревшими входами doctor.
      Файлы: `src/cron/service.test-harness.ts`,
      `src/cron/service.runs-one-shot-main-job-disables-it.test.ts`,
      `src/cron/service/timer.regression.test.ts`,
      `src/cron/service/ops.test.ts`, `src/cron/service/store.test.ts`,
      `src/cron/service.heartbeat-ok-summary-suppressed.test.ts`,
      `src/cron/service.main-job-passes-heartbeat-target-last.test.ts`,
      `src/cron/store.test.ts`.
      Доказательство: `pnpm check:database-first-legacy-stores`; `rg -n 'storePath' src/cron --glob '!**/commands/doctor/**'`.
- [x] Удалить или переименовать устаревшие тестовые моки экспорта файловой эпохи.
      Файл: `src/auto-reply/reply/commands-export-test-mocks.ts`.
      Доказательство: `rg -n 'resolveSessionFilePath|sessionFile|storePath|transcriptLocator' src/auto-reply/reply`.
- [x] Сделать устаревший seed JSONL для Docker runtime-context очевидно предназначенным только для doctor.
      Файл: `scripts/e2e/session-runtime-context-docker-client.ts`.
      Доказательство: `rg -n 'sessions\\.json|sessionFile|\\.jsonl' scripts/e2e/session-runtime-context-docker-client.ts` показывает только
      `seedBrokenLegacySessionForDoctorMigration`.
- [x] Поддерживать сгенерированные типы Kysely синхронизированными после любого изменения схемы.
      Файлы: `src/state/openclaw-state-schema.sql`,
      `src/state/openclaw-agent-schema.sql`,
      `src/state/*generated*`.
      Доказательство: в этом проходе изменений схемы нет; `pnpm db:kysely:check`;
      `pnpm lint:kysely`.
- [x] Повторно запустить фокусные тесты для затронутых хранилищ, команд и скриптов.
      Доказательство: `pnpm test src/cron/service/store.test.ts src/cron/store.test.ts src/cron/service.heartbeat-ok-summary-suppressed.test.ts src/cron/service.main-job-passes-heartbeat-target-last.test.ts src/cron/service.every-jobs-fire.test.ts src/cron/service.persists-delivered-status.test.ts src/cron/service.runs-one-shot-main-job-disables-it.test.ts src/cron/service/ops.test.ts src/cron/service/timer.regression.test.ts src/auto-reply/reply/commands-export-trajectory.test.ts extensions/telegram/src/thread-bindings.test.ts extensions/slack/src/monitor/message-handler/prepare.test.ts src/acp/translator.session-lineage-meta.test.ts`; `git diff --check`.
- [x] Перед объявлением `done` запустить changed gate или удаленное широкое доказательство.
      Доказательство: `pnpm check:changed --timed -- <changed extension paths>` прошел на
      Hetzner Crabbox run `run_3f1cabf6b25c` после временной настройки Node 24/pnpm и
      явной маршрутизации путей для синхронизированного рабочего пространства без `.git`.

### Не допускать регрессий

- Никаких локаторов транскриптов.
- Никаких активных файлов сеансов.
- Никаких фиктивных тестовых фикстур JSONL, кроме тестов устаревшей миграции doctor.
- Никакого сырого доступа к SQLite там, где ожидается Kysely.
- Никаких новых устаревших миграций БД. Эта схема не выпускалась; сохраняйте версию схемы
  `1`, если нет серьезной причины.

## Предположения после чтения кода

Никакие последующие продуктовые решения не блокируют этот план. Реализация должна
продолжаться с этими предположениями:

- Используйте `node:sqlite` напрямую и требуйте среду выполнения Node 22+ для этого пути
  хранения.
- Оставьте ровно один обычный файл конфигурации. Не переносите конфигурацию, манифесты Plugin
  или рабочие деревья Git в SQLite в рамках этого рефакторинга.
- Файлы совместимости времени выполнения не требуются. Устаревшие файлы JSON и JSONL являются
  только входными данными миграции. Локальные для ветки SQLite-сайдкары никогда не выпускались и
  удаляются, а не импортируются.
- `openclaw doctor --fix` отвечает за этап миграции устаревших файлов в базу данных.
  Запуск времени выполнения и `openclaw migrate` не должны нести устаревшие пути
  обновления базы данных OpenClaw.
- Совместимость учетных данных следует тому же правилу: учетные данные времени выполнения хранятся в
  SQLite. Старые файлы `auth-profiles.json`, `auth.json` для отдельных агентов и общие
  `credentials/oauth.json` являются входными данными миграции doctor, а затем удаляются
  после импорта.
- Состояние сгенерированного каталога моделей поддерживается базой данных. Код времени выполнения не должен записывать
  `agents/<agentId>/agent/models.json`; существующие файлы `models.json` являются устаревшими
  входными данными doctor и удаляются после импорта в `agent_model_catalogs`.
- Время выполнения не должно мигрировать, нормализовать или связывать локаторы транскриптов. Активная
  идентичность транскрипта — это `{agentId, sessionId}` в SQLite. Пути к файлам являются
  только устаревшими входными данными doctor, а `sqlite-transcript://...` должен исчезнуть из
  поверхностей времени выполнения, протокола, хуков и Plugin, а не рассматриваться как
  граничный дескриптор.
- Чтение SQLite-транскриптов во времени выполнения не выполняет старые миграции формы записей JSONL и
  не переписывает целые транскрипты ради совместимости. Устаревшая нормализация записей остается в
  явных утилитах doctor/import. Doctor нормализует устаревшие JSONL-файлы транскриптов
  перед вставкой строк SQLite; текущие строки времени выполнения уже записаны
  в текущей схеме транскриптов. Экспорт траектории/сессии читает эти строки как есть и
  не должен выполнять устаревшие миграции во время экспорта.
- Устаревшие помощники разбора/миграции JSONL-транскриптов предназначены только для doctor. Код формата
  транскриптов времени выполнения строит только текущий контекст SQLite-транскрипта; doctor
  отвечает за обновление старых записей JSONL перед вставкой строк.
- Старый помощник потоковой передачи JSONL-транскриптов, принадлежавший времени выполнения, был удален. Код
  импорта doctor отвечает за явное чтение устаревших файлов; история сессий времени выполнения читает
  строки SQLite.
- Привязки сервера приложения Codex используют OpenClaw `sessionId` как канонический
  ключ в пространстве имен состояния Codex Plugin. `sessionKey` — это метаданные для
  маршрутизации/отображения, и он не должен заменять устойчивый идентификатор сессии или возвращать
  идентичность файла транскрипта.
- Движки контекста получают текущий контракт времени выполнения напрямую. Реестр
  не должен оборачивать движки повторными попытками-шими, которые удаляют `sessionKey`,
  `transcriptScope` или `prompt`; движки, которые не могут принять текущие
  параметры с приоритетом базы данных, должны явно завершаться ошибкой, а не связываться адаптером.
- Результат резервного копирования должен оставаться одним архивным файлом. Содержимое базы данных должно попадать
  в этот архив как компактные снимки SQLite, а не как сырые живые WAL-сайдкары.
- Поиск по транскриптам полезен, но не требуется для первого этапа с приоритетом базы данных.
  Спроектируйте схему так, чтобы FTS можно было добавить позже.
- Выполнение worker должно оставаться экспериментальным за настройками, пока граница базы данных
  стабилизируется.

## Выводы По Прочитанному Коду

Текущая ветка уже прошла стадию proof-of-concept. Общая
база данных существует, Node `node:sqlite` подключен через небольшой помощник времени выполнения, а
прежние хранилища теперь записывают в `state/openclaw.sqlite` или в принадлежащую
базу данных `openclaw-agent.sqlite`.

Оставшаяся работа — не выбор SQLite, а поддержание новой границы чистой
и удаление любых интерфейсов, оформленных под совместимость, которые все еще выглядят как старый
файловый мир:

- Session `storePath` больше не является идентичностью времени выполнения, формой тестовой фикстуры или
  полем полезной нагрузки статуса. Тесты времени выполнения и bridge больше не содержат
  имя контракта `storePath`; код doctor/миграции владеет этой устаревшей терминологией.
- Записи сессий больше не проходят через старую внутрипроцессную очередь `store-writer.ts`.
  Записи SQLite-патчей вместо этого используют обнаружение конфликтов и ограниченные повторные попытки.
- Обнаружение устаревших путей все еще имеет допустимые применения для миграции, но код времени выполнения должен
  перестать рассматривать `sessions.json` и JSONL-файлы транскриптов как возможные цели
  записи.
- Таблицы, принадлежащие агентам, находятся в SQLite-базах данных отдельных агентов. Глобальная БД хранит
  строки реестра/плоскости управления; идентичность транскрипта — это `{agentId, sessionId}` в
  строках транскриптов отдельного агента. Код времени выполнения не должен сохранять пути файлов
  транскриптов или мигрировать локаторы транскриптов.
- Doctor уже импортирует несколько устаревших файлов. Очистка заключается в том, чтобы сделать это
  единой явной реализацией миграции, которую вызывает doctor, с устойчивым
  отчетом о миграции.

Дополнительные продуктовые вопросы не блокируют реализацию.

## Текущая Форма Кода

Ветка уже имеет настоящую общую базу SQLite:

- Минимальная версия runtime теперь Node 22+: `package.json`, runtime-проверка CLI,
  значения по умолчанию установщика, локатор runtime для macOS, CI и публичная
  документация по установке теперь согласованы. Старая ветка совместимости с
  Node 22 удалена.
- `src/state/openclaw-state-db.ts` открывает `openclaw.sqlite`, задает WAL,
  `synchronous=NORMAL`, `busy_timeout=30000`, `foreign_keys=ON` и применяет
  сгенерированный модуль схемы, полученный из
  `src/state/openclaw-state-schema.sql`.
- Типы таблиц Kysely и runtime-модули схемы генерируются из одноразовых баз
  SQLite, созданных из закоммиченных файлов `.sql`; runtime-код больше не хранит
  скопированные вручную строки схемы для глобальных, поагентных или proxy
  capture-баз данных.
- Runtime-хранилища выводят типы выбранных и вставленных строк из этих
  сгенерированных интерфейсов Kysely `DB`, а не дублируют формы строк SQLite
  вручную. Raw SQL по-прежнему ограничен применением схемы, pragmas и DDL только
  для миграций.
- Схемы SQLite сведены к `user_version = 1`, потому что эта структура базы
  данных еще не выпускалась. Runtime-открыватели создают только текущую схему;
  импорт из файлов в базу данных остается в коде doctor, а локальные для ветки
  помощники обновления базы данных удалены.
- Реляционное владение enforced там, где граница владения канонична:
  строки source-миграций каскадно удаляются от `migration_runs`, состояние
  доставки задач каскадно удаляется от `task_runs`, а строки идентичности
  transcript каскадно удаляются от событий transcript.
- Текущие общие таблицы включают `agent_databases`,
  `auth_profile_stores`, `auth_profile_state`,
  `plugin_state_entries`, `plugin_blob_entries`, `media_blobs`,
  `skill_uploads`, `capture_sessions`, `capture_events`, `capture_blobs`,
  `sandbox_registry_entries`, `cron_run_logs`, `cron_jobs`, `commitments`,
  `delivery_queue_entries`, `model_capability_cache`,
  `workspace_setup_state`, `native_hook_relay_bridges`,
  `current_conversation_bindings`, `plugin_binding_approvals`,
  `tui_last_sessions`, `acp_sessions`, `acp_replay_sessions`,
  `acp_replay_events`, `task_runs`, `task_delivery_state`, `flow_runs`,
  `subagent_runs`, `migration_runs` и `backup_runs`.
- Произвольное состояние, принадлежащее plugin, не получает типизированных таблиц,
  принадлежащих host. Установленные plugins используют `plugin_state_entries` для
  версионированных JSON-пayloads и `plugin_blob_entries` для байтов, с владением
  namespace/key, очисткой TTL, backup и записями миграций plugin. Состояние
  оркестрации plugin, принадлежащее host, по-прежнему может иметь типизированные
  таблицы, когда host владеет контрактом запроса, например
  `plugin_binding_approvals`.
- Миграции plugin — это миграции данных поверх namespace, принадлежащих plugin, а
  не миграции схемы host. Plugin может мигрировать собственные версионированные
  записи state/blob через migration provider, а host записывает статус source/run
  в обычный журнал миграций. Новые установки plugin не требуют изменения
  `openclaw-state-schema.sql`, если сам host не берет на себя владение новым
  межplugin-контрактом.
- `src/state/openclaw-agent-db.ts` открывает
  `agents/<agentId>/agent/openclaw-agent.sqlite`, регистрирует базу данных в
  глобальной DB и владеет agent-local таблицами session, transcript, VFS,
  artifact, cache и memory-index. Общее runtime-обнаружение теперь читает
  типизированный сгенерированный реестр `agent_databases`, а не реализует этот
  запрос заново в каждой точке вызова.
- Глобальные и поагентные базы данных записывают строку `schema_meta` с ролью
  базы данных, версией схемы, timestamps и agent id для агентских баз данных.
  Структура все еще остается на `user_version = 1`, потому что эта схема SQLite
  еще не выпускалась.
- Идентичность поагентной session теперь имеет каноническую корневую таблицу
  `sessions` с ключом `session_id`, где `session_key`, `session_scope`,
  `account_id`, `primary_conversation_id`, timestamps, поля отображения,
  metadata модели, harness id и связи parent/spawn представлены как запрашиваемые
  столбцы. `session_routes` — это уникальный активный индекс маршрута от
  `session_key` к текущему `session_id`, так что route key может перейти на новую
  durable session, не заставляя hot reads выбирать между дублирующимися строками
  `sessions.session_key`. Старый compatibility-shaped payload
  `session_entries.entry_json` привязан к durable-корню `session_id` внешним
  ключом; он больше не является единственным представлением session на уровне
  схемы.
- Поагентная идентичность внешних conversations тоже реляционная:
  `conversations` хранит нормализованную идентичность provider/account/conversation,
  а `session_conversations` связывает одну session OpenClaw с одной или несколькими
  внешними conversations. Это покрывает shared-main DM sessions, где несколько
  peers могут намеренно сопоставляться с одной session без искажения в
  `session_key`. SQLite также enforced уникальность для естественной идентичности
  provider, чтобы один и тот же кортеж channel/account/kind/peer/thread не мог
  разветвляться по разным conversation ids.
  Shared-main direct peers связываются с ролью `participant`, так что одна
  session OpenClaw может представлять несколько внешних DM peers, не понижая
  старых peers до расплывчатых related rows. `sessions.primary_conversation_id`
  по-прежнему указывает на текущую типизированную цель доставки. Закрытые столбцы
  routing/status enforced через SQLite `CHECK` constraints, а не только через
  TypeScript unions.
  Runtime-проекция session очищает compatibility routing shadows из
  `session_entries.entry_json` перед применением типизированных столбцов
  session/conversation, чтобы устаревшие JSON-payloads не могли воскресить цели
  доставки.
  Маршрутизация subagent announce также требует типизированный delivery context
  SQLite; она больше не откатывается к compatibility-полям маршрута `SessionEntry`.
  Явное наследование доставки Gateway `chat.send` читает типизированный delivery
  context SQLite вместо compatibility-полей `origin`/`last*`.
  `tools.effective` также выводит контекст provider/account/thread из
  типизированных строк delivery/routing SQLite, а не из устаревших
  `last*` shadows session-entry.
  Prompt context для системных событий пересобирает поля channel/to/account/thread
  из типизированных delivery-полей вместо `origin` shadows.
  Общий helper `deliveryContextFromSession` и mapper session-to-conversation теперь
  полностью игнорируют `SessionEntry.origin`; hot route identity могут создавать
  только типизированные delivery-поля и реляционные строки conversation.
  Нормализация runtime session entry удаляет `origin` перед сохранением или
  проекцией `entry_json`, а входящие metadata записывают типизированные поля
  channel/chat плюс реляционные строки conversation вместо создания новых origin
  shadows.
- События transcript, snapshots transcript и runtime-события trajectory теперь
  ссылаются на канонический поагентный корень `sessions` и каскадно удаляются при
  удалении session. Строки идентичности/idempotency transcript продолжают
  каскадно удаляться от точной строки события transcript.
- Индексы memory-core теперь используют явные таблицы agent-database
  `memory_index_meta`, `memory_index_sources`, `memory_index_chunks` и
  `memory_embedding_cache`, а `memory_index_state` отслеживает изменения revision.
  Необязательные побочные FTS/vector-индексы называются `memory_index_chunks_fts` и
  `memory_index_chunks_vec` вместо общих таблиц `meta`, `files`, `chunks`,
  `chunks_fts` или `chunks_vec`. Канонические имена сохраняют текущую форму строк
  path/source и совместимость serialized embedding. Эти таблицы являются
  derived/search cache, а не каноническим хранилищем transcript; их можно удалить
  и перестроить из файлов memory workspace и настроенных sources.
  При открытии выпущенного memory index с generic-name его metadata, sources,
  chunks и embedding cache мигрируют в канонические таблицы; производные
  FTS/vector-таблицы перестраиваются под своими каноническими именами.
- Состояние восстановления subagent run теперь находится в типизированных общих
  строках `subagent_runs` с проиндексированными ключами child, requester и
  controller session. Старый файл `subagents/runs.json` является только входом
  для миграции doctor.
- Текущие conversation bindings теперь находятся в типизированных общих строках
  `current_conversation_bindings` с ключом по нормализованному conversation id,
  где целевые столбцы agent/session, conversation kind, status, expiry и metadata
  хранятся как реляционные столбцы вместо дублированной непрозрачной записи
  binding. Durable binding key включает нормализованный conversation kind, чтобы
  direct/group/channel refs не могли конфликтовать, а SQLite отклоняет
  недопустимые значения binding kind/status. Старый файл
  `bindings/current-conversations.json` является только входом для миграции
  doctor.
- Восстановление delivery queue теперь накладывает типизированные столбцы queue
  для channel, target, account, session, retry, error, platform-send и recovery
  state поверх replay JSON. `entry_json` сохраняет replay payloads, hooks и
  formatting payload, но типизированные столбцы являются authoritative для hot
  queue routing/state.
- Указатели восстановления последней session TUI теперь находятся в типизированных
  общих строках `tui_last_sessions` с ключом по хешированному scope подключения/
  session TUI. Старый JSON-файл TUI является только входом для миграции doctor.
- Default TTS prefs теперь находятся в общих SQLite-строках plugin-state с ключом
  под plugin `speech-core`. Старый файл `settings/tts.json` является только входом
  для миграции doctor; runtime больше не читает и не записывает JSON-файлы TTS
  prefs, а устаревший resolver пути находится в модуле миграции doctor.
- Metadata secret target теперь говорит о stores, а не делает вид, что каждая
  цель credential является config file. `openclaw.json` остается config store;
  цели auth-profile используют типизированные строки SQLite `auth_profile_stores`
  с provider-shaped credentials, сохраненными как JSON payloads.
- Secret audit больше не сканирует retired поагентные файлы `auth.json`. Doctor
  владеет предупреждением об этом устаревшем файле, его импортом и удалением.
- Устаревшие helpers путей auth profile теперь находятся в doctor legacy code.
  Core helpers путей auth profile раскрывают идентичность auth-store SQLite и
  display locations, а не runtime-пути `auth-profiles.json` или
  `auth-state.json`.
- Runtime-модули восстановления subagent run и cache возможностей моделей
  OpenRouter теперь держат SQLite snapshot readers/writers отдельно от
  doctor-only legacy JSON import helpers. Возможности OpenRouter используют
  типизированные generic строки `model_capability_cache` под
  `provider_id = "openrouter"` вместо одного непрозрачного cache blob или
  provider-specific host table. `taskName` subagent run хранится в типизированном
  столбце `subagent_runs.task_name`; копия `payload_json` — это replay/debug
  data, а не источник hot display или lookup fields.
- `src/agents/filesystem/virtual-agent-fs.sqlite.ts` реализует SQLite VFS поверх
  таблицы агентской базы данных `vfs_entries`. Чтения directory, recursive
  exports, deletes и renames используют индексированные prefix ranges
  `(namespace, path)` вместо сканирования всего namespace или зависимости от
  сопоставления путей через `LIKE`.
- `src/agents/runtime-worker.entry.ts` создает поrunовые SQLite VFS, tool
  artifact, run artifact и scoped cache stores для workers.
- Маркеры завершения bootstrap workspace теперь находятся в типизированных общих
  строках `workspace_setup_state` с ключом по resolved workspace path вместо
  `.openclaw/workspace-state.json`; runtime больше не читает и не переписывает
  устаревший маркер workspace, а helper APIs больше не передают фейковый путь
  `.openclaw/setup-state` только для вывода storage identity.
- Exec approvals теперь находятся в singleton-строке типизированной общей SQLite
  `exec_approvals_config`. Doctor импортирует legacy
  `~/.openclaw/exec-approvals.json`; runtime-записи больше не создают, не
  переписывают и не сообщают этот файл как свое активное расположение store.
  macOS companion читает и записывает ту же строку таблицы
  `state/openclaw.sqlite`; на диске он оставляет только Unix prompt socket,
  потому что это IPC, а не durable runtime state.
- Модули device identity, device auth и bootstrap runtime теперь держат SQLite
  snapshot readers/writers отдельно от doctor-only legacy JSON import helpers.
  Device identity использует типизированные строки `device_identities`, а tokens
  device auth используют типизированные строки `device_auth_tokens`. Записи
  device auth согласуют строки по device/role вместо усечения token table, и
  runtime больше не маршрутизирует обновления single-token через старый адаптер
  whole-store. Устаревший
  Полезные нагрузки JSON версии 1 существуют только как формы импорта/экспорта doctor.
- Кэш обмена токенов GitHub Copilot использует общую таблицу состояния Plugin в SQLite
  по ключу `github-copilot/token-cache/default`. Это состояние кэша, принадлежащее провайдеру,
  поэтому оно намеренно не добавляет таблицу схемы хоста.
- Compaction GitHub Copilot больше не записывает побочные рабочие файлы
  `openclaw-compaction-*.json`. Harness вызывает RPC Compaction истории SDK для
  отслеживаемой сессии SDK, а OpenClaw хранит долговечное состояние сессии/транскрипта в
  SQLite вместо файлов-маркеров совместимости.
- Общая среда выполнения Swift (`OpenClawKit`) использует те же строки
  `state/openclaw.sqlite` для идентификатора устройства и авторизации устройства. Вспомогательные
  компоненты приложения macOS импортируют общие помощники SQLite вместо владения вторым путем
  JSON или SQLite. Оставшийся устаревший `identity/device.json` блокирует создание идентификатора
  до тех пор, пока doctor не импортирует его в SQLite, что соответствует стартовому шлюзу
  TypeScript и Android.
- Идентификатор устройства Android использует тот же совместимый с TypeScript ключевой материал,
  сохраненный в типизированных строках `state/openclaw.sqlite#table/device_identities`. Он никогда
  не читает и не записывает `openclaw/identity/device.json`; оставшийся устаревший файл блокирует
  запуск до тех пор, пока doctor не импортирует его в SQLite.
- Кэшированные токены авторизации устройства Android также используют типизированные строки
  `state/openclaw.sqlite#table/device_auth_tokens` и разделяют ту же семантику токенов версии 1,
  что и TypeScript и Swift. Среда выполнения больше не читает ключи совместимости `SecurePrefs`
  `gateway.deviceToken*`; они принадлежат только логике миграции/doctor.
- История последних пакетов уведомлений Android использует типизированные строки
  `android_notification_recent_packages`. Среда выполнения больше не мигрирует и не читает
  старые CSV-ключи SharedPreferences.
- Создание идентификатора устройства завершается закрытым отказом, когда существует устаревший
  `identity/device.json`, когда строка идентификатора SQLite недействительна или когда хранилище
  идентификаторов SQLite нельзя открыть. Doctor сначала импортирует и удаляет этот файл, поэтому
  запуск среды выполнения не может незаметно сменить идентификатор сопряжения до миграции.
- Выбор идентификатора устройства — это ключ строки SQLite, а не локатор JSON-файла. Тесты
  и помощники Gateway передают явные ключи идентификатора; только миграция doctor и стартовый
  шлюз с закрытым отказом знают имя выведенного из использования файла `identity/device.json`.
- Совместимость сброса сессии теперь находится в миграции конфигурации doctor:
  `session.idleMinutes` переносится в `session.reset.idleMinutes`,
  `session.resetByType.dm` переносится в `session.resetByType.direct`, а политика сброса
  среды выполнения читает только канонические ключи сброса.
- Совместимость устаревшей конфигурации теперь находится в `src/commands/doctor/`. Обычная
  валидация `readConfigFileSnapshot()` не импортирует устаревшие детекторы doctor и не помечает
  устаревшие проблемы; `runDoctorConfigPreflight()` добавляет эти проблемы для исправления/отчета
  doctor. Поток конфигурации doctor импортирует
  `src/commands/doctor/legacy-config.ts`, а старое исправление ID профилей OAuth находится в
  `src/commands/doctor/legacy/oauth-profile-ids.ts`.
- Команды, не относящиеся к doctor, не запускают автоматически исправление устаревшей конфигурации.
  Например, `openclaw update --channel` теперь завершается ошибкой при недействительной устаревшей
  конфигурации и просит пользователя запустить doctor, а не молча импортирует код миграции doctor.
- Web push, APNs, Voice Wake, проверки обновлений и состояние конфигурации теперь используют
  типизированные общие таблицы SQLite для подписок, ключей VAPID, регистраций узлов, строк
  триггеров, строк маршрутизации, состояния уведомлений об обновлениях и записей состояния
  конфигурации вместо цельных непрозрачных JSON-блобов. Записи снимков Web push и APNs теперь
  согласуют подписки/регистрации по первичному ключу вместо очистки их таблиц; состояние
  конфигурации делает то же самое по пути конфигурации.
  Их модули среды выполнения держат читатели/писатели снимков SQLite отдельно от помощников
  импорта устаревшего JSON, предназначенных только для doctor.
- Конфигурация хоста Node теперь использует типизированную singleton-строку в общей базе данных
  SQLite; doctor импортирует старый файл `node.json` перед обычным использованием среды выполнения.
- Сопряжение устройства/узла, сопряжение канала, списки разрешений каналов и состояние bootstrap
  теперь используют типизированные строки SQLite вместо цельных непрозрачных JSON-блобов. Одобрения
  привязок Plugin и состояние заданий Cron следуют тому же разделению: модули среды выполнения
  предоставляют операции на базе SQLite и нейтральные помощники снимков, а записи снимков
  сопряжения/bootstrap и одобрений привязок Plugin согласуют строки по первичному ключу вместо
  усечения таблиц, пока doctor импортирует/удаляет старые JSON-файлы через модули
  `src/commands/doctor/legacy/*`.
- Записи установленных Plugin теперь находятся в SQLite-индексе установленных Plugin.
  Чтение/запись конфигурации среды выполнения больше не мигрирует и не сохраняет старые данные
  авторской конфигурации `plugins.installs`; doctor импортирует эту устаревшую форму конфигурации
  в SQLite перед обычным использованием среды выполнения.
- Снимки восстановления учетных данных QQBot теперь находятся в состоянии Plugin SQLite по ключу
  `qqbot/credential-backups`. Среда выполнения больше не записывает
  `qqbot/data/credential-backup*.json`; doctor импортирует и удаляет эти устаревшие резервные
  файлы вместе с другими входными данными состояния QQBot.
- Планирование перезагрузки Gateway сравнивает снимки SQLite-индекса установленных Plugin во
  внутреннем пространстве различий `installedPluginIndex.installRecords.*`. Решения о перезагрузке
  среды выполнения больше не оборачивают эти строки в поддельные объекты конфигурации
  `plugins.installs`.
- Обновление учетных данных именованной учетной записи Matrix больше не происходит во время
  чтения средой выполнения. Doctor владеет переименованием старого верхнеуровневого
  `credentials/matrix/credentials.json`, когда можно разрешить единственную/default учетную
  запись Matrix.
- Модули среды выполнения базового сопряжения и Cron больше не экспортируют построители путей
  к устаревшему JSON. Устаревшие модули, принадлежащие doctor, строят исходные пути `pending.json`,
  `paired.json`, `bootstrap.json` и `cron/jobs.json` только для тестов импорта и миграции.
  Нормализация устаревшей формы заданий Cron и импорт журнала запусков Cron находятся в
  `src/commands/doctor/legacy/cron*.ts`.
- `src/commands/doctor/legacy/runtime-state.ts` импортирует устаревшие файлы состояния JSON,
  включая конфигурацию хоста Node, в SQLite из doctor. Новые импортеры устаревших файлов остаются
  в `src/commands/doctor/legacy/`.
- `src/commands/doctor/state-migrations.ts` импортирует устаревшие `sessions.json` и
  транскрипты `*.jsonl` напрямую в SQLite и удаляет успешно обработанные источники. Он больше
  не промежуточно размещает корневые устаревшие транскрипты через
  `agents/<agentId>/sessions/*.jsonl` и не создает каноническую цель JSONL перед импортом.
- Проверки целостности состояния doctor больше не сканируют устаревшие каталоги сессий и
  не предлагают удаление осиротевших JSONL. Устаревшие файлы транскриптов являются только
  входными данными миграции, а шаг миграции владеет импортом и удалением источников.
- Импорт устаревшего реестра песочницы находится в
  `src/commands/doctor/legacy/sandbox-registry.ts`; активные чтения и записи реестра песочницы
  остаются только в SQLite.
- Исправление состояния/импорта устаревших транскриптов сессий находится в
  `src/commands/doctor/legacy/session-transcript-health.ts`; командные модули среды выполнения
  больше не несут код разбора транскриптов JSONL или исправления активной ветки.

Выполненные основные моменты консолидации/удаления:

- Состояние Plugin теперь использует общую базу данных `state/openclaw.sqlite`. Старый
  импортёр бокового файла `plugin-state/state.sqlite`, локального для ветки, удалён,
  потому что эта схема SQLite никогда не выпускалась. Вспомогательные средства проб и
  тестов сообщают общий `databasePath` вместо того, чтобы раскрывать путь SQLite,
  специфичный для состояния Plugin.
- Таблицы среды выполнения задач и Task Flow теперь находятся в общей базе данных
  `state/openclaw.sqlite` вместо `tasks/runs.sqlite` и
  `tasks/flows/registry.sqlite`; старые импортёры боковых файлов удалены по той же
  причине: эта схема не выпускалась.
- `src/config/sessions/store.ts` больше не нужен `storePath` для входящих
  метаданных, обновлений маршрутов или чтения времени обновления. Сохранение команд,
  очистка сессий CLI, глубина субагентов, переопределения аутентификации и
  идентичность сессии транскрипта используют API строк агента/сессии. Записи
  применяются как исправления строк SQLite с оптимистичной повторной попыткой при
  конфликте.
- Разрешение цели сессии теперь раскрывает цели баз данных для каждого агента, а не
  устаревшие пути `sessions.json`. Общий Gateway, метаданные ACP, исправление маршрутов
  doctor и `openclaw sessions` перечисляют `agent_databases` вместе с настроенными
  агентами.
- Маршрутизация сессий Gateway теперь использует `resolveGatewaySessionDatabaseTarget`;
  возвращаемая цель содержит `databasePath` и ключи строк-кандидатов SQLite вместо
  устаревшего пути к файлу хранилища сессий.
- Типы среды выполнения сессий каналов теперь раскрывают `{agentId, sessionKey}` для
  чтения времени обновления, входящих метаданных и обновлений последнего маршрута.
  Старый тип совместимости `saveSessionStore(storePath, store)` удалён.
- Среда выполнения Plugin, API расширений и поверхности барреля `config/sessions`
  теперь направляют код Plugin к вспомогательным средствам строк сессий на базе SQLite.
  Экспорты совместимости корневой библиотеки (`loadSessionStore`, `saveSessionStore`,
  `resolveStorePath`) остаются как устаревшие шины для существующих потребителей. Старый
  помощник `resolveLegacySessionStorePath` удалён; построение устаревших путей
  `sessions.json` теперь локально для миграции и тестовых фикстур.
- `src/config/sessions/session-entries.sqlite.ts` теперь хранит канонические записи
  сессий в базе данных каждого агента и поддерживает чтение/upsert/удаление исправлений
  на уровне строк. Runtime upsert/patch/delete больше не сканирует варианты регистра и
  не вычищает устаревшие ключи-псевдонимы; за канонизацию отвечает doctor. Отдельный
  помощник импорта JSON удалён, а миграция объединяет upsert более новых строк вместо
  замены всей таблицы сессий. Публичные помощники чтения/списка/загрузки проецируют
  горячие метаданные сессий из типизированных строк `sessions` и `conversations`;
  `entry_json` является теневым полем совместимости/отладки и может быть устаревшим или
  недействительным без потери типизированной идентичности сессии или контекста доставки.
- `src/config/sessions/delivery-info.ts` теперь разрешает контекст доставки из
  типизированных строк `sessions` + `conversations` + `session_conversations` для
  каждого агента. Он больше не восстанавливает идентичность доставки среды выполнения
  из `session_entries.entry_json`; отсутствующая типизированная строка беседы является
  проблемой миграции/исправления doctor, а не резервным вариантом среды выполнения.
- Решения сброса сохранённых сессий теперь предпочитают типизированные метаданные
  `sessions.session_scope`, `sessions.chat_type` и `sessions.channel`. Разбор
  `sessionKey` остаётся только для явных суффиксов веток/тем в целях команд; классификация
  сброса группы и прямого чата больше не берётся из формы ключа.
- Классификация отображения списка/статуса сессий теперь использует типизированные
  метаданные чата и вид сессии Gateway. Она больше не считает подстроки `:group:` или
  `:channel:` внутри `session_key` устойчивым источником истины о группе/прямом чате.
- Выбор политики тихого ответа теперь использует только явный тип беседы или метаданные
  поверхности. Он больше не угадывает политику прямого/группового чата по подстрокам
  `session_key`.
- Разрешение модели отображения сессии теперь получает идентификатор агента из цели базы
  данных сессий SQLite вместо извлечения его из `session_key`.
- Гидратация цели объявления agent-to-agent теперь использует только типизированный
  `deliveryContext` из `sessions.list`. Она больше не восстанавливает маршрутизацию
  канала/аккаунта/ветки из устаревшего `origin`, зеркальных полей `last*` или формы
  `session_key`.
- Отклонение цели ветки в `sessions_send` теперь читает типизированные метаданные
  маршрутизации SQLite. Оно больше не отклоняет и не принимает цели путём разбора
  суффиксов веток из ключа цели.
- Проверка политики инструментов с областью группы теперь читает типизированную
  маршрутизацию беседы SQLite для текущей или порождённой сессии. Она больше не доверяет
  идентичности группы/канала через декодирование `sessionKey`; переданные вызывающим
  кодом идентификаторы групп отбрасываются, когда за них не ручается типизированная
  строка сессии.
- Сопоставление переопределений модели канала теперь использует явные метаданные группы
  и родительской беседы. Оно больше не декодирует идентификаторы родительских бесед из
  `parentSessionKey`.
- Наследование сохранённых переопределений модели теперь требует явный ключ родительской
  сессии из типизированного контекста сессии. Оно больше не выводит родительские
  переопределения из суффиксов `:thread:` или `:topic:` в `sessionKey`.
- Старый wrapper информации о ветке сессии и parser веток загруженного Plugin удалены;
  код среды выполнения больше не импортирует `config/sessions/thread-info`.
- Помощник бесед канала больше не раскрывает мосты разбора полного ключа сессии. Ядро
  по-прежнему нормализует принадлежащие провайдерам сырые идентификаторы бесед через
  `resolveSessionConversation(...)`, но не восстанавливает факты маршрута из
  `sessionKey`.
- Доставка завершения, политика отправки и сопровождение задач больше не выводят тип
  чата из формы `session_key`. Старый parser типа чата по ключу удалён; этим путям
  требуются типизированные метаданные сессии, типизированный контекст доставки или явная
  лексика цели доставки.
- Список/статус сессий, диагностика, привязка аккаунта одобрения, фильтрация Heartbeat в
  TUI и сводки использования больше не добывают маршрутизацию провайдера/аккаунта/ветки/
  отображения из `SessionEntry.origin`. Единственные оставшиеся runtime-чтения `origin`
  относятся к несессионным понятиям или объектам доставки текущего хода.
- Нативный поиск беседы для запроса одобрения теперь читает типизированные строки
  маршрутизации сессий для каждого агента. Он больше не разбирает идентичность беседы
  канала/группы/ветки из `sessionKey`; отсутствующие типизированные метаданные являются
  проблемой миграции/исправления.
- Полезные нагрузки событий Gateway session changed/chat/session больше не повторяют
  `SessionEntry.origin` или теневые маршруты `last*`; клиенты получают типизированные
  `channel`, `chatType` и `deliveryContext`.
- Разрешение доставки Heartbeat теперь может получать типизированный SQLite
  `deliveryContext` напрямую, а среда выполнения Heartbeat передаёт строку доставки
  сессии для каждого агента вместо того, чтобы полагаться на тени совместимости
  `session_entries` для текущей маршрутизации.
- Разрешение цели доставки изолированного агента Cron также гидратирует свой текущий
  маршрут из типизированной строки доставки сессии для каждого агента, прежде чем
  откатиться к полезной нагрузке записи совместимости.
- Разрешение origin объявления субагента теперь протягивает типизированный контекст
  доставки сессии запрашивающего через `loadRequesterSessionEntry` и предпочитает эту
  строку теням совместимости `last*`/`deliveryContext`.
- Обновления входящих метаданных сессии теперь сначала объединяются с типизированной
  строкой доставки для каждого агента; старые поля доставки `SessionEntry` используются
  только как резерв, когда типизированной строки беседы не существует.
- Извлечение доставки при перезапуске/обновлении теперь отдаёт приоритет типизированному
  SQLite `threadId` доставки над фрагментами темы/ветки, разобранными из `sessionKey`;
  разбор используется только как резерв для устаревших ключей в форме ветки.
- Идентификаторы каналов контекста hook-агента теперь предпочитают типизированную
  идентичность беседы SQLite, затем явные метаданные сообщения. Они больше не разбирают
  фрагменты провайдера/группы/канала из `sessionKey`.
- Наследование внешнего маршрута Gateway `chat.send` теперь читает типизированные
  метаданные маршрутизации сессии SQLite вместо вывода области канала/прямого/группового
  чата из частей `sessionKey`. Сессии с областью канала наследуют только тогда, когда
  типизированный канал сессии и тип чата совпадают с сохранённым контекстом доставки;
  общие main-сессии сохраняют своё более строгое правило CLI/без клиентских метаданных.
- Пробуждение restart-sentinel и маршрутизация продолжений теперь читают типизированные
  строки доставки/маршрутизации SQLite перед постановкой в очередь пробуждений Heartbeat
  или маршрутизированных продолжений хода агента. Они больше не восстанавливают контекст
  доставки из JSON-тени записи сессии.
- Разрешение контекста Gateway `tools.effective` теперь читает типизированные строки
  доставки/маршрутизации SQLite для входных данных провайдера, аккаунта, цели, ветки и
  режима ответа. Оно больше не восстанавливает эти горячие поля маршрутизации из
  устаревших теней origin в `session_entries.entry_json`.
- Маршрутизация голосовой консультации в реальном времени теперь разрешает доставку
  родителя/вызова из типизированных строк сессий SQLite для каждого агента. Она больше
  не откатывается к теням совместимости `SessionEntry.deliveryContext` при выборе
  маршрута сообщения встроенного агента.
- Ретрансляция Heartbeat порождения ACP и маршрутизация родительского потока теперь
  читают родительскую доставку из типизированных строк сессий SQLite. Они больше не
  восстанавливают родительский контекст доставки из теней записей сессий совместимости.
- Сохранение маршрута доставки сессии теперь следует типизированным метаданным чата и
  сохранённым столбцам доставки. Оно больше не извлекает подсказки канала, маркеры
  direct/main или форму ветки из `sessionKey`; внутренние webchat-маршруты наследуют
  внешнюю цель только тогда, когда в SQLite уже есть типизированная/сохранённая
  идентичность доставки для сессии.
- Общее извлечение доставки сессии теперь читает только точную типизированную строку
  доставки сессии SQLite. Оно больше не разбирает суффиксы ветки/темы и не откатывается
  от ключа в форме ветки к базовому ключу сессии.
- Диспетчеризация ответов, восстановление restart sentinel и маршрутизация голосовой
  консультации в реальном времени теперь используют точные типизированные строки
  сессий/бесед SQLite для маршрутизации веток. Они больше не восстанавливают
  идентификаторы веток или контекст доставки базовой сессии путём разбора ключей сессий
  в форме ветки.
- Ограничение истории встроенного PI теперь использует типизированную проекцию
  маршрутизации сессии SQLite (`sessions` + primary `conversations`) для провайдера,
  типа чата и идентичности собеседника. Оно больше не разбирает провайдера, DM, группу
  или форму ветки из `sessionKey`.
- Вывод доставки инструментов Cron теперь использует только явную доставку или текущий
  типизированный контекст доставки. Он больше не декодирует цели канала, собеседника,
  аккаунта или ветки из `agentSessionKey`.
- Строки сессий среды выполнения больше не несут старый псевдоним маршрута
  `lastProvider`. Помощники и тесты используют типизированные поля `lastChannel` и
  `deliveryContext`; миграция doctor — единственное место, где следует переводить старые
  псевдонимы маршрутов или сохранённые тени `origin`.
- События транскриптов, строки VFS и строки артефактов инструментов теперь записываются
  в базу данных каждого агента. Невыпущенная глобальная таблица сопоставления файлов
  транскриптов удалена; вместо этого doctor записывает устаревшие исходные пути в
  долговечные строки миграции.
- Поиск транскриптов среды выполнения больше не сканирует смещения байтов JSONL и не
  проверяет устаревшие файлы транскриптов. Пути Gateway chat/media/history читают строки
  транскриптов из SQLite; session JSONL теперь является только устаревшим входом doctor,
  а не состоянием среды выполнения или форматом экспорта.
- Родительские и ветвящиеся связи транскриптов используют структурированные метаданные
  `parentTranscriptScope: {agentId, sessionId}` в заголовках транскриптов SQLite, а не
  строковые локаторы, похожие на пути `agent-db:...transcript_events...`.
- Контракт менеджера транскриптов больше не раскрывает неявные сохранённые конструкторы
  `create(cwd)` или `continueRecent(cwd)`. Сохранённые менеджеры транскриптов открываются
  с явной областью `{agentId, sessionId}`; только менеджеры в памяти остаются без области
  для тестов и чистых преобразований транскриптов.
- API хранилища транскриптов среды выполнения разрешают область SQLite, а не пути
  файловой системы. Старый помощник `resolve...ForPath` и неиспользуемые параметры записи
  `transcriptPath` удалены из вызывающего кода среды выполнения.
- Разрешение сессий среды выполнения теперь использует `{agentId, sessionId}` и не должно
  выводить строки `sqlite-transcript://<agent>/<session>` для внешних границ.
  Устаревшие абсолютные пути JSONL являются только входами миграции doctor.
- Записи прямого моста ретрансляции нативных hook теперь находятся в типизированных общих
  строках `native_hook_relay_bridges`, ключом которых является идентификатор ретрансляции.
  Среда выполнения больше не записывает JSON-реестр `/tmp` или непрозрачные общие записи
  для этих краткоживущих записей мостов.
- `runEmbeddedPiAgent(...)` больше не имеет параметра локатора транскрипта.
  Подготовленные дескрипторы воркеров также не включают локаторы транскриптов. Состояние сеанса среды выполнения
  и поставленные в очередь последующие запуски несут `{agentId, sessionId}` вместо
  производных дескрипторов транскрипта.
- Встроенное Compaction теперь берет область SQLite из `agentId` и `sessionId`.
  Хуки Compaction, вызовы context-engine, делегирование CLI и ответы протокола
  не должны получать производные дескрипторы `sqlite-transcript://...`. Код
  экспорта/отладки может материализовать явные пользовательские артефакты из строк,
  но не предоставляет универсальный путь экспорта JSONL сеанса и не передает имена
  файлов обратно в идентификатор среды выполнения.
- `/export-session` читает строки транскрипта из SQLite и записывает только
  запрошенное автономное HTML-представление. Встроенный просмотрщик больше не
  реконструирует и не скачивает JSONL сеанса из этих строк.
- Делегирование context-engine больше не разбирает локатор транскрипта, чтобы
  восстановить идентификатор агента. Подготовленный контекст среды выполнения
  передает разрешенный `agentId` во встроенный адаптер Compaction.
- Перезапись транскрипта и оперативное усечение результатов инструментов теперь
  читают и сохраняют состояние транскрипта по `{agentId, sessionId}` и не выводят
  временные локаторы для полезной нагрузки событий обновления транскрипта.
- Поверхность вспомогательных функций состояния транскрипта больше не имеет
  вариантов `readTranscriptState`, `replaceTranscriptStateEvents` или
  `persistTranscriptStateMutation` на основе локаторов. Вызывающий код среды
  выполнения должен использовать API `{agentId, sessionId}`. Импорт doctor читает
  устаревшие файлы по явному пути к файлу и записывает строки SQLite; он не
  мигрирует строки локаторов.
- Контракт session-manager среды выполнения больше не предоставляет `open(locator)`,
  `forkFrom(locator)` или `setTranscriptLocator(...)`. Постоянные менеджеры
  сеансов открываются только по `{agentId, sessionId}`; вспомогательные функции
  списка/ответвления находятся в API сеансов и контрольных точек, ориентированных
  на строки, а не в фасаде менеджера транскриптов.
- API чтения транскриптов Gateway в первую очередь работают с областью. Они
  принимают `{agentId, sessionId}` и не принимают позиционный локатор транскрипта,
  который мог бы случайно стать идентификатором среды выполнения. Разбор активного
  локатора транскрипта удален; устаревшие исходные пути читаются только кодом
  импорта doctor.
- События обновления транскрипта также в первую очередь работают с областью.
  `emitSessionTranscriptUpdate` больше не принимает голую строку локатора, а
  слушатели маршрутизируют по `{agentId, sessionId}` без разбора дескриптора.
- Широковещательная передача session-message в Gateway разрешает ключи сеансов
  из области агента/сеанса, а не из локатора транскрипта. Старый преобразователь
  и кэш ключей из локатора транскрипта в ключ сеанса удалены.
- Фильтры SSE session-history в Gateway фильтруют оперативные обновления по области
  агента/сеанса. Он больше не канонизирует кандидаты локаторов транскрипта,
  realpath или файловые идентификаторы транскриптов, чтобы решить, должен ли поток
  получить обновление.
- Хуки жизненного цикла сеанса больше не выводят и не предоставляют локаторы
  транскриптов в `session_end`. Потребители хуков получают `sessionId`,
  `sessionKey`, идентификаторы следующего сеанса и контекст агента; файлы
  транскриптов не являются частью контракта жизненного цикла.
- Хуки сброса также больше не выводят и не предоставляют локаторы транскриптов.
  Полезная нагрузка `before_reset` несет восстановленные сообщения SQLite и причину
  сброса, а идентификатор сеанса остается в контексте хука.
- Сброс harness агента больше не принимает локатор транскрипта. Диспетчеризация
  сброса ограничена `sessionId`/`sessionKey` и причиной.
- Типы сеансов расширений агента больше не предоставляют `transcriptLocator`;
  расширения должны использовать контекст сеанса и API среды выполнения, а не
  обращаться к файловому идентификатору транскрипта.
- Хуки Compaction Plugin больше не предоставляют локаторы транскриптов. Контекст
  хука уже несет идентификатор сеанса, а чтение транскриптов должно идти через
  API SQLite, учитывающие область, а не через файловые дескрипторы.
- Хуки `before_agent_finalize` больше не предоставляют `transcriptPath`, включая
  полезные нагрузки ретрансляции нативных хуков. Хуки финализации используют
  только контекст сеанса.
- Ответы сброса Gateway больше не синтезируют локатор транскрипта в возвращаемой
  записи. Сброс создает строки транскрипта SQLite, возвращает чистую запись сеанса
  и оставляет доступ к транскрипту читателям, учитывающим область.
- Результаты встроенного запуска и Compaction больше не показывают локаторы
  транскриптов для учета сеанса. Автоматическое Compaction обновляет только
  активный `sessionId`, счетчики Compaction и метаданные токенов.
- Результаты встроенных попыток больше не возвращают `transcriptLocatorUsed`, а
  результаты `compact()` context-engine больше не возвращают локаторы транскриптов.
  Циклы повторных попыток среды выполнения принимают только последующий `sessionId`.
- Результаты добавления транскриптов delivery-mirror больше не возвращают локаторы
  транскриптов. Вызывающий код получает добавленный `messageId`; сигналы обновления
  транскрипта используют область SQLite.
- Вспомогательные функции ответвления родительского сеанса возвращают только
  ответвленный `sessionId`. Подготовка подагента передает движкам область дочернего
  агента/сеанса.
- Параметры CLI runner и повторное заполнение истории больше не принимают локаторы
  транскриптов. Чтение истории CLI разрешает область транскрипта SQLite из `{agentId,
sessionId}` и контекста ключа сеанса.
- Тестовые фикстуры CLI и встроенного runner теперь заполняют и читают строки
  транскрипта SQLite по идентификатору сеанса, вместо того чтобы притворяться, что
  активные сеансы являются файлами `*.jsonl`, или передавать строку
  `sqlite-transcript://...` через параметры среды выполнения.
- События защиты результатов инструментов сеанса испускаются из известной области
  сеанса, даже когда у менеджера в памяти нет производного локатора. Его тесты
  больше не подделывают активные файлы транскриптов `/tmp/*.jsonl`.
- Вспомогательные функции BTW и checkpoint Compaction теперь читают и ответвляют
  строки транскриптов по области SQLite. Метаданные контрольной точки теперь
  хранят только идентификаторы сеансов и leaf/entry; производные локаторы больше
  не записываются в полезные нагрузки контрольных точек.
- Поиск transcript-key в Gateway использует область транскрипта SQLite на границах
  протокола и больше не вызывает realpath или stat для имен файлов транскриптов.
- Автоматическая ротация транскрипта Compaction записывает последующие строки
  транскрипта напрямую через хранилище транскриптов SQLite. Строки сеансов хранят
  только идентификатор последующего сеанса, а не долговечный путь JSONL или
  сохраненный локатор.
- Встроенное Compaction context-engine использует вспомогательные функции ротации
  транскриптов с именами SQLite. Тесты ротации больше не создают пути последующих
  JSONL и не моделируют активные сеансы как файлы.
- Управляемое хранение исходящих изображений формирует ключ кэша transcript-message
  из статистики транскрипта SQLite вместо вызовов stat файловой системы.
- Блокировки сеансов среды выполнения и отдельная устаревшая ветка doctor для
  `.jsonl.lock` удалены.
- Runtime barrel Microsoft Teams и публичный SDK Plugin больше не реэкспортируют
  старую вспомогательную функцию блокировки файлов; пути долговечного состояния
  Plugin опираются на SQLite.
- Обрезка сеансов по возрасту/количеству и явная очистка сеансов удалены.
  Doctor владеет устаревшим импортом; устаревшие сеансы сбрасываются или удаляются
  явно.
- Проверки целостности doctor больше не считают устаревший файл JSONL допустимым
  активным транскриптом для строки сеанса SQLite. Состояние активного транскрипта
  проверяется только по SQLite; устаревшие файлы JSONL сообщаются как входные
  данные для миграции или очистки сирот.
- Doctor больше не считает `agents/<agent>/sessions/` обязательным состоянием
  среды выполнения. Он сканирует этот каталог только если он уже существует, как
  вход для устаревшего импорта или очистки сирот.
- Gateway `sessions.resolve`, пути исправления/сброса/compact сеанса, создание
  подагента, fast abort, метаданные ACP, изолированные Heartbeat сеансы и исправление
  TUI больше не мигрируют и не обрезают устаревшие ключи сеансов как побочный
  эффект обычной работы среды выполнения.
- Разрешение сеанса команды CLI теперь возвращает владеющий `agentId` вместо
  `storePath` и больше не копирует устаревшие строки основного сеанса при обычном
  разрешении `--to` или `--session-id`. Канонизация устаревших основных строк
  относится только к doctor.
- Разрешение глубины подагента в среде выполнения больше не читает `sessions.json`
  или хранилища сеансов JSON5. Оно читает `session_entries` SQLite по идентификатору
  агента, а устаревшие метаданные глубины/сеанса могут попасть только через путь
  импорта doctor.
- Переопределения сеансов профилей аутентификации сохраняются через прямые upsert
  строк `{agentId, sessionKey}` вместо ленивой загрузки файловой среды выполнения
  session-store.
- Подробная фильтрация auto-reply и вспомогательные функции обновления сеанса
  теперь читают/upsert строки сеансов SQLite по идентификатору сеанса и больше не
  требуют устаревший путь хранилища перед изменением постоянного состояния строк.
- Вспомогательные функции метаданных сеанса command-run теперь используют имена и
  пути модулей, ориентированные на entry; старая поверхность вспомогательных функций
  команды `session-store` удалена.
- Заполнение bootstrap header и усиление границы ручного Compaction теперь напрямую
  изменяют строки транскрипта SQLite. Вызывающий код среды выполнения передает
  идентификатор сеанса, а не записываемые пути `.jsonl`.
- Тихое воспроизведение ротации сеанса копирует недавние ходы пользователя/ассистента
  по `{agentId, sessionId}` из строк транскрипта SQLite. Оно больше не принимает
  исходные или целевые локаторы транскриптов.
- Новые строки сеансов среды выполнения больше не хранят локаторы транскриптов.
  Вызывающий код напрямую использует `{agentId, sessionId}`; команды экспорта/отладки
  могут выбирать имена выходных файлов при материализации строк.
- Запуск нового постоянного сеанса транскрипта теперь всегда открывает строки SQLite
  по области. Менеджер сеансов больше не переиспользует прежний путь или локатор
  транскрипта файловой эпохи как идентификатор нового сеанса.
- Постоянные сеансы транскриптов используют явный API
  `openTranscriptSessionManagerForSession({agentId, sessionId})`. Старые статические
  фасады `SessionManager.create/openForSession/list/forkFromSession` удалены, чтобы
  тесты и код среды выполнения не могли случайно воссоздать обнаружение сеансов
  файловой эпохи.
- Среда выполнения Plugin больше не предоставляет `api.runtime.agent.session.resolveTranscriptLocatorPath`;
  код Plugin использует вспомогательные функции строк SQLite и значения области.
- Публичная поверхность SDK `session-store-runtime` теперь экспортирует только
  вспомогательные функции строк сеансов и строк транскриптов. Целевые вспомогательные
  функции схемы/пути/транзакций SQLite находятся в `sqlite-runtime`; сырые
  вспомогательные функции open/close/reset остаются локальными только для внутренних
  тестов.
- Устаревшие классификаторы имен файлов траекторий/контрольных точек `.jsonl`
  теперь находятся в устаревшем модуле файлов сеансов doctor. Основная валидация
  сеансов больше не импортирует вспомогательные функции файловых артефактов, чтобы
  определять обычные идентификаторы сеансов SQLite.
- Блокирующие запуски подагентов Active Memory используют строки транскрипта SQLite
  вместо создания временных или постоянных файлов `session.jsonl` в состоянии Plugin.
  Старая опция `transcriptDir` удалена.
- Одноразовая генерация slug и запуски планировщика Crestodian используют строки
  транскрипта SQLite вместо создания временных файлов `session.jsonl`.
- Запуски вспомогательной функции `llm-task` и извлечение скрытых обязательств также
  используют строки транскрипта SQLite, поэтому эти вспомогательные сеансы только
  для модели больше не создают временные файлы транскриптов JSON/JSONL.
- `TranscriptSessionManager` теперь является только открытой областью транскрипта
  SQLite. Код среды выполнения открывает его через
  `openTranscriptSessionManagerForSession({agentId, sessionId})`; потоки создания,
  ветвления, продолжения, списка и ответвления находятся в принадлежащих им
  вспомогательных функциях строк SQLite, а не в статических фасадах менеджера.
  Код doctor/import/debug обрабатывает явные устаревшие исходные файлы вне менеджера
  сеансов среды выполнения.
- Устаревшие фасадные методы `SessionManager.newSession()` и
  `SessionManager.createBranchedSession()` удалены. Новые сеансы и потомки
  транскриптов создаются принадлежащим им рабочим процессом SQLite, а не путем
  изменения уже открытого менеджера в другой постоянный сеанс.
- Решения об ответвлении родительского транскрипта и создание ответвлений больше не
  принимают `storePath` или `sessionsDir`; они используют область транскрипта SQLite
  `{agentId, sessionId}` вместо сохраненных метаданных путей файловой системы.
- Memory-host больше не экспортирует no-op вспомогательные функции классификации
  транскриптов каталога сеанса; фильтрация транскриптов теперь выводится из
  метаданных строк SQLite при построении entry.
- Тесты экспорта сеансов Memory-host и QMD используют области транскриптов SQLite.
  Старые пути `agents/<agentId>/sessions/*.jsonl` остаются покрытыми только там,
  где тест намеренно доказывает совместимость doctor/import/export.
- Сырое инспектирование сеансов QA-lab теперь использует `sessions.list` через Gateway
  вместо чтения `agents/qa/sessions/sessions.json`; обратная связь MSteams
  добавляется напрямую в транскрипты SQLite без создания фиктивного пути JSONL.
- Общие входящие ходы каналов теперь несут `{agentId, sessionKey}`, а не
  устаревший `storePath`. Пути записи LINE, WhatsApp, Slack, Discord, Telegram,
  Matrix, Signal, iMessage, BlueBubbles, Feishu, Google Chat, IRC, Nextcloud Talk, Zalo,
  Zalo Personal, QA Channel, Microsoft Teams, Mattermost, Synology Chat, Tlon,
  Twitch и QQBot теперь читают метаданные updated-at и записывают
  строки входящих сеансов через идентичность SQLite.
- Сохранение локатора транскрипта удалено из строк активных сеансов.
  `resolveSessionTranscriptTarget` возвращает `agentId`, `sessionId` и необязательные
  метаданные темы; doctor — единственный код, который импортирует устаревшие имена
  файлов транскриптов.
- Заголовки транскриптов runtime начинаются с версии SQLite `1`. Обновления старых
  форм JSONL V1/V2/V3 находятся только в импорте doctor и нормализуют импортированные
  заголовки до текущей версии транскрипта SQLite перед сохранением строк.
- Защита database-first теперь запрещает `SessionManager.listAll` и
  `SessionManager.forkFromSession`; рабочие процессы перечисления сеансов и
  fork/restore должны оставаться на строковых/областных API SQLite.
- Защита также запрещает имена устаревших помощников разбора JSONL транскриптов и
  восстановления active-branch вне кода doctor/import, поэтому runtime не может
  получить второй устаревший путь миграции транскриптов.
- Встроенные запуски PI отклоняют входящие дескрипторы транскриптов. Они используют
  идентичность SQLite `{agentId, sessionId}` перед запуском worker и снова перед тем,
  как попытка затронет состояние транскрипта. Устаревший ввод `/tmp/*.jsonl` не может
  выбрать цель записи runtime.
- Записи трассировки кэша, payload Anthropic, raw stream и временной шкалы диагностики
  теперь пишутся в типизированные строки SQLite `diagnostic_events`. Пакеты стабильности
  Gateway теперь пишутся в типизированные строки SQLite `diagnostic_stability_bundles`.
  Старые пути переопределения JSONL `diagnostics.cacheTrace.filePath`,
  `OPENCLAW_CACHE_TRACE_FILE`, `OPENCLAW_ANTHROPIC_PAYLOAD_LOG_FILE` и
  `OPENCLAW_DIAGNOSTICS_TIMELINE_PATH` удалены, и обычный захват стабильности больше
  не пишет файлы `logs/stability/*.json`.
- Сохранение Cron теперь согласует строки SQLite `cron_jobs` вместо
  удаления и повторной вставки всей таблицы заданий при каждом сохранении. Обратные
  записи цели Plugin обновляют совпадающие строки cron напрямую и удерживают состояние
  cron runtime в той же транзакции базы данных состояния.
- Вызывающий код Cron runtime теперь использует стабильный ключ хранилища cron SQLite.
  Устаревшие пути `cron.store` являются только входами импорта doctor; производственный
  Gateway, обслуживание задач, статус, run-log и пути обратной записи цели Telegram
  используют `resolveCronStoreKey` и больше не нормализуют ключ как путь. Статус Cron
  теперь сообщает `storeKey`, а не старое поле `storePath` в форме файла.
- Загрузка и планирование Cron runtime больше не нормализуют устаревшие сохраненные
  формы заданий, такие как `jobId`, `schedule.cron`, числовой `atMs`, строковые
  булевы значения или отсутствующий `sessionTarget`. Устаревший импорт doctor владеет
  этими исправлениями до вставки строк в SQLite.
- ACP spawn больше не разрешает и не сохраняет пути файлов JSONL транскриптов.
  Настройка spawn и thread-bind сохраняет строку сеанса SQLite напрямую и сохраняет
  идентификатор сеанса как удерживаемую идентичность транскрипта.
- API метаданных сеансов ACP теперь читают/перечисляют/upsert строки SQLite по `agentId`
  и больше не раскрывают `storePath` как часть контракта записи сеанса ACP.
- Учет использования сеансов и агрегирование использования Gateway теперь разрешают
  транскрипты только по `{agentId, sessionId}`. Кэш стоимости/использования и сводки
  обнаруженных сеансов больше не синтезируют и не возвращают строки локаторов
  транскриптов.
- Добавление чата Gateway, сохранение abort-partial, `/sessions.send` и записи
  медиа webchat в транскрипт добавляются напрямую через область транскрипта SQLite.
  Помощник внедрения транскрипта Gateway больше не принимает параметр
  `transcriptLocator`.
- Обнаружение транскриптов SQLite теперь перечисляет только области и статистику
  транскриптов: `{agentId, sessionId, updatedAt, eventCount}`. Мертвый
  помощник совместимости `listSqliteSessionTranscriptLocators` и поле `locator`
  для каждой строки удалены.
- Runtime восстановления транскриптов теперь раскрывает только
  `repairTranscriptSessionStateIfNeeded({agentId, sessionId})`. Старый
  помощник восстановления на основе локатора удален; код doctor/debug читает явные
  пути исходных файлов и никогда не мигрирует строки локаторов.
- Runtime журнала воспроизведения ACP теперь хранит строки воспроизведения для каждого
  сеанса в общей базе данных состояния SQLite вместо `acp/event-ledger.json`; doctor
  импортирует и удаляет устаревший файл.
- Помощники чтения транскриптов Gateway теперь находятся в
  `src/gateway/session-transcript-readers.ts` вместо старого имени модуля
  `session-utils.fs`. Проверка истории повторных попыток fallback названа по содержимому
  транскрипта SQLite, а не по старой поверхности файлового помощника.
- Помощники Gateway injected-chat и compaction теперь передают область транскрипта
  SQLite через внутренние API помощников вместо именования значений путями транскриптов
  или исходными файлами.
- Обнаружение продолжения bootstrap теперь проверяет строки транскриптов SQLite через
  `hasCompletedBootstrapTranscriptTurn`; оно больше не раскрывает имя помощника
  в форме файла.
- Тесты embedded-runner теперь используют идентичность транскрипта SQLite, и открытие
  нового менеджера транскриптов всегда требует явного `sessionId`.
- Помощники индексирования памяти теперь используют терминологию транскриптов SQLite
  на всем пути: host экспортирует `listSessionTranscriptScopesForAgent` и
  `sessionTranscriptKeyForScope`, целевые очереди синхронизации — `sessionTranscripts`,
  публичные результаты поиска по сеансам раскрывают непрозрачные пути
  `transcript:<agent>:<session>`, а внутренний ключ источника БД — `session:<session>`
  при `source_kind='sessions'` вместо фиктивного пути файла.
- Универсальный помощник persistent-dedupe в Plugin SDK больше не раскрывает параметры
  в форме файла. Вызывающий код предоставляет ключи областей SQLite, а долговечные
  строки dedupe живут в общем состоянии Plugin.
- Токены SSO Microsoft Teams перенесены из заблокированных JSON-файлов в состояние
  Plugin SQLite. Doctor импортирует `msteams-sso-tokens.json`, перестраивает канонические
  ключи токенов SSO из payload и удаляет исходный файл. Делегированные токены OAuth
  остаются на существующей границе приватных файлов учетных данных.
- Состояние кэша синхронизации Matrix перенесено из `bot-storage.json` в состояние
  Plugin SQLite. Doctor импортирует устаревшие raw или обернутые payload синхронизации
  и удаляет исходный файл. Активные клиенты Matrix и QA Matrix передают корневой каталог
  хранилища синхронизации SQLite, а не фиктивный путь `sync-store.json` или
  `bot-storage.json`.
- Состояние устаревшей миграции crypto Matrix перенесено из
  `legacy-crypto-migration.json` в состояние Plugin SQLite. Doctor импортирует
  старый файл состояния; снимки IndexedDB Matrix SDK перенесены из
  `crypto-idb-snapshot.json` в blob Plugin SQLite. Ключи восстановления Matrix и
  учетные данные являются строками состояния Plugin SQLite; их старые JSON-файлы —
  только входы миграции doctor.
- Журналы активности Memory Wiki теперь используют состояние Plugin SQLite вместо
  `.openclaw-wiki/log.jsonl`. Поставщик миграции Memory Wiki импортирует старые
  журналы JSONL; markdown wiki и содержимое пользовательского vault остаются
  файловыми как содержимое workspace.
- Memory Wiki больше не создает `.openclaw-wiki/state.json` или неиспользуемый
  каталог `.openclaw-wiki/locks`. Поставщик миграции удаляет эти выведенные из
  использования файлы метаданных Plugin, если они все еще есть в старом vault.
- Записи аудита Crestodian теперь используют состояние Plugin SQLite core вместо
  `audit/crestodian.jsonl`. Doctor импортирует устаревший журнал аудита JSONL и
  удаляет его после успешного импорта.
- Записи аудита записи/наблюдения конфигурации теперь используют состояние Plugin
  SQLite core вместо `logs/config-audit.jsonl`. Doctor импортирует устаревший журнал
  аудита JSONL и удаляет его после успешного импорта.
- macOS companion больше не пишет локальные для приложения sidecar-файлы
  `logs/config-audit.jsonl` или `logs/config-health.json` при редактировании
  `openclaw.json`. Файл конфигурации остается файловым, снимки восстановления
  остаются рядом с файлом конфигурации, а долговечное состояние аудита/здоровья
  конфигурации принадлежит хранилищу SQLite Gateway.
- Ожидающие подтверждения rescue Crestodian теперь используют состояние Plugin SQLite
  core вместо `crestodian/rescue-pending/*.json`. Doctor импортирует устаревшие файлы
  ожидающих подтверждений и удаляет их после успешного импорта.
- Временное состояние arm Phone Control теперь использует состояние Plugin SQLite
  вместо `plugins/phone-control/armed.json`. Doctor импортирует устаревший файл
  состояния armed в пространство имен `phone-control/arm-state` и удаляет файл.
- Doctor больше не исправляет транскрипты JSONL на месте и не создает резервные файлы
  JSONL. Он импортирует активную ветку в SQLite и удаляет устаревший источник.
- Поиск транскриптов hook session-memory использует чтения SQLite только по области
  `{agentId, sessionId}`. Его помощник больше не принимает и не выводит локаторы
  транскриптов, чтения устаревших файлов или параметры перезаписи файлов.
- Привязки разговоров app-server Codex теперь ключуют состояние Plugin SQLite по
  ключу сеанса OpenClaw или явной области `{agentId, sessionId}`. Они не должны
  сохранять fallback-привязки путей транскриптов.
- Чтения mirrored-history app-server Codex используют только область транскрипта
  SQLite; они не должны восстанавливать идентичность из путей файлов транскриптов.
- Пути сброса role-ordering и compaction больше не удаляют старые файлы транскриптов;
  reset только ротирует строку сеанса SQLite и идентичность транскрипта.
- Ответы reset и checkpoint Gateway возвращают чистые строки сеансов плюс идентификаторы
  сеансов. Они больше не синтезируют локаторы транскриптов SQLite для клиентов.
- Dreaming memory-core больше не удаляет строки сеансов, проверяя отсутствие файлов
  JSONL. Очистка subagent проходит через API runtime сеансов вместо проверок
  существования в файловой системе. Его тесты приема транскриптов заполняют строки
  SQLite напрямую вместо создания fixtures `agents/<id>/sessions` или placeholder
  локаторов.
- Индексирование транскриптов памяти может раскрывать `transcript:<agentId>:<sessionId>`
  как виртуальный путь результата поиска для помощников цитирования/чтения.
  Долговечный источник индекса является реляционным (`source_kind='sessions'`,
  `source_key='session:<sessionId>'`, `session_id=<sessionId>`), поэтому это значение
  не является локатором транскрипта runtime, не является путем файловой системы и
  никогда не должно передаваться обратно в API runtime сеансов.
- Статус памяти doctor Gateway читает краткосрочный recall и счетчики phase-signal
  из строк состояния Plugin SQLite вместо `memory/.dreams/*.json`; вывод CLI и
  doctor теперь помечает это хранилище как хранилище SQLite, а не путь.
- Runtime memory-core, статус CLI, методы doctor Gateway и фасады Plugin SDK больше
  не аудируют и не архивируют устаревшие файлы `.dreams/session-corpus`. Эти файлы
  являются только входами миграции; doctor импортирует их в SQLite и удаляет источник
  после проверки. Активные строки доказательств приема сеансов теперь используют
  виртуальный путь SQLite `memory/session-ingestion/<day>.txt`; runtime никогда не
  пишет и не выводит состояние из `.dreams/session-corpus`.
- Публичные артефакты memory-core раскрывают события host SQLite как виртуальный
  JSON-артефакт `memory/events/memory-host-events.json`; они больше не используют
  повторно устаревший исходный путь `.dreams/events.jsonl`.
- Реестры sandbox container/browser теперь используют общую таблицу SQLite
  `sandbox_registry_entries` с типизированными столбцами сеанса, образа, timestamp,
  backend/config и browser port. Doctor импортирует устаревшие монолитные и
  сегментированные JSON-файлы реестров и удаляет успешно импортированные источники.
  Чтения runtime используют типизированные столбцы строк как источник истины;
  `entry_json` — только копия для replay/debug.
- Commitments теперь используют типизированную общую таблицу `commitments` вместо
  JSON-blob всего хранилища. Сохранения снимков выполняют upsert по идентификатору
  commitment и удаляют только отсутствующие строки вместо очистки и повторной вставки
  таблицы. Runtime загружает commitments из типизированных столбцов scope,
  delivery-window, status, attempt и text; `record_json` — только копия для
  replay/debug. Doctor импортирует устаревший `commitments.json` и удаляет его после
  успешного импорта.
- Определения заданий Cron, состояние расписания и история запусков больше не имеют
  JSON-писателей или читателей runtime. Runtime использует строки `cron_jobs` с
  типизированным расписанием,
  столбцы payload, delivery, failure-alert, session, status и runtime-state, а также типизированные
  метаданные `cron_run_logs` для статуса, сводки диагностики, статуса/ошибки доставки,
  сессии/запуска, модели и общих значений токенов. `job_json` — только копия для воспроизведения/отладки; `state_json` хранит вложенную
  диагностику runtime, для которой еще нет полей горячих запросов, а runtime
  восстанавливает горячие поля состояния из типизированных столбцов. Doctor импортирует
  устаревшие файлы `jobs.json`, `jobs-state.json` и `runs/*.jsonl` и удаляет
  импортированные источники. Обратные записи целей Plugin обновляют соответствующие строки `cron_jobs`
  вместо загрузки и замены всего хранилища cron.
- Запуск Gateway игнорирует устаревшие маркеры `notify: true` в runtime
  projection. Doctor преобразует их в явную доставку SQLite, когда
  `cron.webhook` действителен, удаляет инертные маркеры, когда он не задан, и сохраняет
  их с предупреждением, когда настроенный webhook недействителен.
- Очереди исходящей доставки и доставки сессий теперь хранят статус очереди, вид записи,
  ключ сессии, канал, цель, идентификатор учетной записи, счетчик повторов, последнюю попытку/ошибку,
  состояние восстановления и маркеры отправки платформы как типизированные столбцы в общей
  таблице `delivery_queue_entries`. Восстановление runtime читает эти горячие поля из
  типизированных столбцов, а мутации повторов/восстановления обновляют эти столбцы напрямую,
  без перезаписи JSON воспроизведения. Полная JSON payload остается только как
  blob воспроизведения/отладки для тел сообщений и других холодных данных воспроизведения.
- Управляемые записи исходящих изображений теперь используют типизированные общие
  строки `managed_outgoing_image_records`, а байты media по-прежнему хранятся в
  `media_blobs`. JSON-запись остается только как копия для воспроизведения/отладки.
- Настройки Discord для выбора модели, хэши развертывания команд и привязки веток
  теперь используют общее состояние Plugin в SQLite. Их планы импорта устаревшего JSON находятся в поверхности
  настройки/doctor-миграции Plugin Discord, а не в коде миграции ядра.
- Детекторы устаревшего импорта Plugin используют doctor-именованные модули, такие как
  `doctor-legacy-state.ts` или `doctor-state-imports.ts`; обычные runtime-модули каналов
  не должны импортировать детекторы устаревшего JSON.
- Курсоры catchup BlueBubbles и входящие маркеры dedupe теперь используют общее состояние
  Plugin в SQLite. Их планы импорта устаревшего JSON находятся в поверхности
  настройки/doctor-миграции Plugin BlueBubbles, а не в коде миграции ядра.
- Смещения обновлений Telegram, строки кеша стикеров, строки кеша отправленных сообщений,
  строки кеша имен тем и привязки веток теперь используют общее состояние Plugin
  в SQLite. Их планы импорта устаревшего JSON находятся в поверхности
  настройки/doctor-миграции Plugin Telegram, а не в коде миграции ядра.
- Курсоры catchup iMessage, сопоставления коротких идентификаторов ответов и строки dedupe sent-echo
  теперь используют общее состояние Plugin в SQLite. Старые файлы `imessage/catchup/*.json`,
  `imessage/reply-cache.jsonl` и `imessage/sent-echoes.jsonl` являются
  только входными данными doctor.
- Строки dedupe сообщений Feishu теперь используют общее состояние Plugin в SQLite вместо
  файлов `feishu/dedup/*.json`. Его план импорта устаревшего JSON находится в поверхности
  настройки/doctor-миграции Plugin Feishu, а не в коде миграции ядра.
- Беседы, опросы, ожидающие буферы загрузок и извлеченные из обратной связи данные
  Microsoft Teams теперь используют общее состояние Plugin в SQLite и таблицы blob. Путь ожидающей загрузки
  использует `plugin_blob_entries`, поэтому media-буферы хранятся как SQLite BLOB
  вместо base64 JSON. Имена runtime helper теперь используют именование SQLite/состояния,
  а не файлового хранилища `*-fs`, и старый shim `storePath` удален
  из этих хранилищ. Его план импорта устаревшего JSON находится в поверхности
  настройки/doctor-миграции Plugin Microsoft Teams.
- Размещенные исходящие media Zalo теперь используют общие SQLite `plugin_blob_entries`
  вместо временных JSON/bin sidecar-файлов `openclaw-zalo-outbound-media`.
- HTML и метаданные просмотрщика diff теперь используют общие SQLite `plugin_blob_entries`
  вместо временных файлов `meta.json`/`viewer.html`. Отрендеренные выходные PNG/PDF остаются
  временными материализациями, потому что доставке канала все еще нужен путь к файлу.
- Управляемые документы Canvas теперь используют общие SQLite `plugin_blob_entries`
  вместо каталога по умолчанию `state/canvas/documents`. Хост Canvas отдает эти
  blob напрямую; локальные файлы создаются только для явного операторского контента `host.root`
  или временной материализации, когда нижестоящему считывателю media
  требуется путь.
- Решения аудита File Transfer теперь используют общие SQLite `plugin_state_entries`
  вместо неограниченного runtime-журнала `audit/file-transfer.jsonl`. Doctor
  импортирует устаревший файл аудита JSONL в состояние Plugin и удаляет источник
  после чистого импорта.
- Аренды процессов ACPX и идентификатор экземпляра gateway теперь используют общее состояние Plugin
  в SQLite. Doctor импортирует устаревший файл `gateway-instance-id` в состояние Plugin
  и удаляет источник.
- Сгенерированные wrapper-скрипты ACPX и изолированный дом Codex являются временной
  материализацией под временным корнем OpenClaw, а не долговечным состоянием OpenClaw. Долговечные
  runtime-записи ACPX — это строки аренды SQLite и gateway-instance;
  старая config-поверхность ACPX `stateDir` удалена, потому что runtime-состояние
  туда больше не записывается.
- Медиа-вложения Gateway теперь используют общую SQLite-таблицу `media_blobs` как
  каноническое хранилище байтов. Локальные пути, возвращаемые каналам и поверхностям совместимости
  sandbox, являются временными материализациями строки базы данных, а не
  долговечным media-хранилищем. Runtime-списки разрешенных media больше не включают устаревшие
  корни `$OPENCLAW_STATE_DIR/media` или `media` каталога config; эти каталоги являются
  только источниками импорта doctor.
- Автодополнение shell больше не записывает кеш-файлы `$OPENCLAW_STATE_DIR/completions/*`.
  Пути smoke install, doctor, update и release используют сгенерированный
  вывод completion или подключение профиля вместо долговечных кеш-файлов completion.
- Подготовка загрузок Skills в Gateway теперь использует общие строки `skill_uploads`. Метаданные загрузки,
  ключи идемпотентности и байты архива живут в SQLite; установщик
  получает только временно материализованный путь к архиву, пока идет установка.
- Встроенные вложения subagent больше не материализуются в workspace
  `.openclaw/attachments/*`. Путь spawn подготавливает seed-записи SQLite VFS,
  встроенные запуски добавляют эти записи в scratch-пространство runtime конкретного агента,
  а инструменты с дисковой поддержкой накладывают этот SQLite scratch для путей вложений. Старые
  registry-столбцы attachment-dir для subagent-run и cleanup hooks удалены.
- Гидратация изображений CLI больше не поддерживает стабильные кеш-файлы `openclaw-cli-images`.
  Внешние backend CLI по-прежнему получают пути к файлам, но эти пути являются
  временными материализациями для каждого запуска с cleanup.
- Диагностика cache-trace, диагностика payload Anthropic, диагностика raw model stream,
  события timeline диагностики и stability bundles Gateway теперь
  записывают строки SQLite вместо файлов `logs/*.jsonl` или
  `logs/stability/*.json`.
  Флаги переопределения runtime-путей и env vars удалены; команды export/debug
  могут явно материализовать файлы из строк базы данных.
- macOS companion больше не имеет rolling writer `diagnostics.jsonl`. Логи приложения
  идут в unified logging, а долговечная диагностика Gateway остается на базе SQLite.
- Список записей macOS port-guardian теперь использует типизированные общие строки SQLite
  `macos_port_guardian_records` вместо JSON-файла Application Support
  или непрозрачного singleton blob.
- Singleton-блокировки Gateway теперь используют типизированные общие строки SQLite `state_leases` в
  области `gateway_locks` вместо lock-файлов во временном каталоге. Документация по устранению неполадок Fly и OAuth
  теперь указывает на lease/auth refresh lock SQLite вместо устаревшей очистки file-lock.
- Состояние restart sentinel Gateway теперь использует типизированные общие строки SQLite
  `gateway_restart_sentinel` вместо `restart-sentinel.json`; runtime
  читает вид sentinel, статус, маршрутизацию, сообщение, продолжение и статистику из
  типизированных столбцов. `payload_json` — только копия для воспроизведения/отладки. Runtime-код очищает
  строку SQLite напрямую и больше не несет plumbing для очистки файлов.
- Состояние restart intent Gateway и supervisor handoff теперь используют типизированные общие
  строки SQLite `gateway_restart_intent` и `gateway_restart_handoff` вместо
  sidecar-файлов `gateway-restart-intent.json` и
  `gateway-supervisor-restart-handoff.json`.
- Координация singleton Gateway теперь использует типизированные строки `state_leases` в
  `gateway_locks` вместо записи файлов `gateway.<hash>.lock`. Строка lease
  владеет владельцем блокировки, сроком действия, heartbeat и debug payload; SQLite владеет
  атомарной границей acquire/release. Удаленная опция каталога file-lock
  исчезла; тесты используют идентификатор строки SQLite напрямую.
- Старый неиспользуемый helper отчета об использовании cron, который сканировал файлы `cron/runs/*.jsonl`,
  был удален. Отчеты истории запусков Cron должны читать типизированные
  строки SQLite `cron_run_logs`.
- Восстановление main-session restart теперь обнаруживает кандидатов-агентов через
  SQLite registry `agent_databases` вместо сканирования каталогов `agents/*/sessions`.
- Восстановление повреждения сессии Gemini теперь удаляет только строку сессии SQLite;
  ему больше не нужен устаревший gate `storePath`, и он не пытается unlink производного
  пути transcript JSONL.
- Обработка переопределения путей теперь считает буквальные значения среды `undefined`/`null`
  незаданными, предотвращая случайные базы данных `undefined/state/*.sqlite`
  в корне repo во время тестов или handoff из shell.
- Отпечатки работоспособности config теперь используют типизированные общие строки SQLite `config_health_entries`
  вместо `logs/config-health.json`, оставляя обычный config-файл единственным
  неучетным конфигурационным документом. macOS companion хранит только
  process-local состояние работоспособности и не воссоздает старый JSON sidecar.
- Runtime auth profile больше не импортирует и не записывает JSON-файлы учетных данных. Каноническое
  хранилище учетных данных — SQLite; `auth-profiles.json`, per-agent
  `auth.json` и общие `credentials/oauth.json` являются входными данными doctor migration,
  которые удаляются после импорта.
- Тесты сохранения/состояния auth profile теперь напрямую проверяют типизированные таблицы auth SQLite
  и используют устаревшие имена файлов auth-profile только для входных данных doctor migration.
- `openclaw secrets apply` очищает только config-файл, env-файл и SQLite
  хранилище auth-profile. Он больше не содержит совместимой логики, которая редактирует
  удаленный per-agent `auth.json`; doctor отвечает за импорт и удаление этого файла.
- Планы и применения миграции секретов Hermes импортируют профили API-ключей напрямую
  в SQLite-хранилище auth-profile. Она больше не записывает и не проверяет
  `auth-profiles.json` как промежуточную цель.
- Пользовательская документация по auth теперь описывает
  `state/openclaw.sqlite#table/auth_profile_stores/<agentDir>` вместо
  указания пользователям проверять или копировать `auth-profiles.json`; устаревшие имена OAuth/auth JSON
  остаются задокументированными только как входные данные doctor-import.
- Helper core state-path больше не раскрывают удаленный файл `credentials/oauth.json`.
  Устаревшее имя файла локально для пути импорта auth doctor.
- Документация install, security, onboarding, model-auth и SecretRef теперь описывает
  строки auth-profile SQLite и резервное копирование/миграцию всего состояния вместо
  per-agent JSON-файлов auth-profile.
- Обнаружение моделей PI теперь передает канонические учетные данные во встроенное
  хранилище auth `pi-coding-agent`. Оно больше не создает, не очищает и не записывает
  per-agent `auth.json` во время обнаружения.
- Настройки триггера Voice Wake и маршрутизации теперь используют типизированные общие таблицы SQLite
  вместо `settings/voicewake.json`, `settings/voicewake-routing.json` или
  непрозрачных generic-строк; doctor импортирует устаревшие JSON-файлы и удаляет их после
  успешной миграции.
- Состояние проверки обновлений теперь использует типизированную общую строку `update_check_state` вместо
  `update-check.json` или непрозрачного generic blob; doctor импортирует
  устаревший JSON-файл и удаляет его после успешной миграции.
- Состояние работоспособности config теперь использует типизированные общие строки `config_health_entries` вместо
  `logs/config-health.json` или непрозрачного generic blob; doctor
  импортирует устаревший JSON-файл и удаляет его после успешной миграции.
- Одобрения привязки бесед Plugin теперь используют типизированные
  строки `plugin_binding_approvals` вместо непрозрачного общего состояния SQLite или
  `plugin-binding-approvals.json`; устаревший файл является входными данными миграции doctor.
- Универсальные привязки текущего разговора теперь сохраняют типизированные
  строки `current_conversation_bindings` вместо перезаписи
  `bindings/current-conversations.json`; doctor импортирует устаревший JSON-файл и
  удаляет его после успешной миграции.
- Реестры синхронизации импортированных источников Memory Wiki теперь сохраняют по одной строке состояния Plugin в SQLite
  для каждого ключа хранилища/источника вместо перезаписи `.openclaw-wiki/source-sync.json`;
  поставщик миграции импортирует и удаляет устаревший JSON-реестр.
- Записи запусков импорта ChatGPT в Memory Wiki теперь сохраняют по одной строке состояния Plugin в SQLite
  для каждого хранилища/id запуска вместо записи `.openclaw-wiki/import-runs/*.json`.
  Снимки отката остаются явными файлами хранилища, пока архивирование снимков
  запусков импорта не будет перенесено в blob-хранилище.
- Скомпилированные дайджесты Memory Wiki теперь сохраняют строки blob Plugin в SQLite вместо
  записи `.openclaw-wiki/cache/agent-digest.json` и
  `.openclaw-wiki/cache/claims.jsonl`. Поставщик миграции импортирует старые файлы кэша
  и удаляет каталог кэша, когда он становится пустым.
- Отслеживание установки Skills в ClawHub теперь сохраняет по одной строке состояния Plugin в SQLite для каждого
  рабочего пространства/Skills вместо записи или чтения sidecar-файлов `.clawhub/lock.json` и
  `.clawhub/origin.json` во время выполнения. Код среды выполнения использует объекты состояния
  отслеживаемой установки, а не lockfile/origin-абстракции в форме файлов. Doctor
  импортирует устаревшие sidecar-файлы из настроенных рабочих пространств агентов и удаляет их
  после чистого импорта.
- Индекс установленных Plugin теперь читает и записывает типизированную общую singleton-строку SQLite
  `installed_plugin_index` вместо `plugins/installs.json`; устаревший
  JSON-файл является только входными данными миграции doctor и удаляется после импорта.
- Устаревший помощник пути `plugins/installs.json` теперь находится в устаревшем коде doctor.
  Модули индекса Plugin среды выполнения предоставляют только варианты сохранения на базе SQLite,
  а не путь к JSON-файлу.
- Sentinel перезапуска Gateway, намерение перезапуска и состояние передачи supervisor теперь используют
  типизированные общие строки SQLite (`gateway_restart_sentinel`,
  `gateway_restart_intent` и `gateway_restart_handoff`) вместо универсальных
  непрозрачных blob. Код перезапуска среды выполнения не имеет контракта sentinel/intent/handoff
  в форме файлов.
- Кэш синхронизации Matrix, метаданные хранилища, привязки тредов, маркеры дедупликации входящих сообщений,
  состояние cooldown проверки запуска, криптографические снимки SDK IndexedDB,
  учетные данные и ключи восстановления теперь используют общие таблицы состояния/blob Plugin в SQLite.
  Структуры путей среды выполнения больше не предоставляют путь метаданных `storage-meta.json`;
  это имя файла является только входными данными устаревшей миграции. Их план импорта устаревшего JSON
  находится в поверхности настройки/миграции doctor Plugin Matrix.
- Запуск Matrix больше не сканирует, не сообщает и не завершает устаревшее файловое
  состояние Matrix. Обнаружение файлов Matrix, создание устаревших криптографических снимков, состояние
  миграции восстановления room-key, импорт и удаление источника полностью принадлежат doctor.
- Barrels миграции Matrix среды выполнения были удалены. Помощники обнаружения и изменения
  устаревшего состояния/криптографии импортируются Matrix doctor напрямую, а не являются
  частью API среды выполнения.
- Маркеры повторного использования снимков миграции Matrix теперь находятся в состоянии Plugin SQLite
  вместо `matrix/migration-snapshot.json`; doctor все еще может повторно использовать тот же
  проверенный архив до миграции без записи sidecar-файла состояния.
- Курсоры шины Nostr и состояние публикации профиля теперь используют общее состояние Plugin SQLite.
  Их план импорта устаревшего JSON находится в поверхности настройки/миграции doctor Plugin Nostr.
- Переключатели сессий Active Memory теперь используют общее состояние Plugin SQLite вместо
  `session-toggles.json`; повторное включение памяти удаляет строку вместо
  перезаписи JSON-объекта.
- Предложения Skill Workshop и счетчики проверок теперь используют общее состояние Plugin SQLite
  вместо хранилищ `skill-workshop/<workspace>.json` для каждого рабочего пространства. Каждое
  предложение хранится как отдельная строка в `skill-workshop/proposals`, а счетчик
  проверок — как отдельная строка в `skill-workshop/reviews`.
- Запуски subagent-проверяющих Skill Workshop теперь используют резолвер transcript сессии
  среды выполнения вместо создания sidecar-путей сессий `skill-workshop/<sessionId>.json`.
- Аренды процессов ACPX теперь используют общее состояние Plugin SQLite в
  `acpx/process-leases` вместо реестра всего файла `process-leases.json`.
  Каждая аренда хранится в собственной строке, сохраняя очистку устаревших процессов при запуске
  без пути перезаписи JSON во время выполнения.
- Скрипты-обертки ACPX и изолированный home Codex генерируются во
  временном корне OpenClaw. Они создаются заново по мере необходимости и не являются входными данными
  резервного копирования или миграции.
- Сохранение реестра запусков subagent использует типизированные общие строки `subagent_runs`. Старый
  путь `subagents/runs.json` теперь является только входными данными миграции doctor, а
  имена помощников среды выполнения больше не описывают слой состояния как дисковый.
  Тесты среды выполнения больше не создают недопустимые или пустые фикстуры `runs.json` для проверки
  поведения реестра; они напрямую заполняют/читают строки SQLite.
- Резервное копирование подготавливает каталог состояния перед архивированием, копирует не относящиеся к базе данных файлы,
  создает снимки баз данных `*.sqlite` с помощью `VACUUM INTO`, пропускает живые sidecar-файлы WAL/SHM,
  записывает метаданные снимков в манифест архива и записывает
  завершенные запуски резервного копирования в SQLite вместе с манифестом архива. `openclaw backup
create` по умолчанию проверяет записанный архив; `--no-verify` — это
  явный быстрый путь.
- `openclaw backup restore` проверяет архив перед извлечением, повторно использует
  нормализованный манифест проверяющего и восстанавливает проверенные ресурсы манифеста в их
  записанные исходные пути. Для записи требуется `--yes`, а для плана восстановления поддерживается `--dry-run`.
- Старый фильтр нестабильных путей резервного копирования удален. Резервному копированию больше не нужен
  live-tar список пропуска для устаревших JSON/JSONL-файлов сессий или cron, потому что снимки SQLite
  подготавливаются до создания архива.
- Обычная подготовка настройки и onboarding рабочего пространства больше не создает
  каталоги `agents/<agentId>/sessions/`. Они создают только config/workspace;
  строки сессий SQLite и строки transcript создаются по требованию в
  базе данных каждого агента.
- Исправление прав безопасности теперь нацелено на глобальную и агентские базы данных SQLite
  плюс sidecar-файлы WAL/SHM вместо `sessions.json` и JSONL-файлов transcript.
- Имена среды выполнения реестра sandbox теперь описывают виды реестра SQLite напрямую
  вместо переноса устаревшей терминологии JSON-реестра через активное хранилище.
- `openclaw reset --scope config+creds+sessions` удаляет агентские
  базы данных `openclaw-agent.sqlite` плюс sidecar-файлы WAL/SHM, а не только устаревшие
  каталоги `sessions/`.
- Агрегирующие помощники сессий Gateway теперь используют имена, ориентированные на записи:
  `loadCombinedSessionEntriesForGateway` возвращает `{ databasePath, entries }`.
  Старое именование combined-store удалено из вызывающих мест среды выполнения.
- Заполнение Docker MCP-канала теперь записывает основную строку сессии и события transcript
  в агентскую базу данных SQLite вместо создания
  `sessions.json` и JSONL transcript.
- Встроенный хук памяти сессий теперь получает контекст предыдущей сессии из
  SQLite по `{agentId, sessionId}`. Он больше не сканирует, не хранит и не синтезирует
  пути transcript или каталоги `workspace/sessions`.
- Встроенный хук command-logger теперь записывает строки аудита команд в общую
  таблицу SQLite `command_log_entries` вместо добавления в
  `logs/commands.log`.
- Разрешающие списки pairing каналов теперь предоставляют только помощники чтения/записи на базе SQLite
  во время выполнения и в SDK Plugin. Старый резолвер путей `*-allowFrom.json` и
  файловый reader находятся только в устаревшем коде импорта doctor.
- `migration_runs` записывает выполнения миграций устаревшего состояния со статусом,
  временными метками и JSON-отчетами.
- `migration_sources` записывает каждый импортированный источник устаревшего файла с hash, size,
  record count, target table, run id, status и состоянием удаления источника.
- `backup_runs` записывает пути архивов резервного копирования, status и JSON-манифесты.
- Глобальная схема не сохраняет неиспользуемую таблицу реестра `agents`. Обнаружение
  баз данных агентов является каноническим реестром `agent_databases`, пока у среды выполнения
  не появится реальный владелец agent-record.
- Сгенерированный config каталога моделей хранится в типизированных глобальных строках SQLite
  `agent_model_catalogs`, ключом которых является каталог агента. Вызывающие места среды выполнения используют
  `ensureOpenClawModelCatalog`; в коде среды выполнения нет API совместимости `models.json`.
  Реализация записывает SQLite, а встроенный реестр PI гидратируется из сохраненной полезной нагрузки
  без создания файла `models.json`.
- Markdown-экспорт transcript сессий QMD и config `memory.qmd.sessions` были
  удалены. Нет коллекции QMD transcript, нет runtime-пути `qmd/sessions*`
  и нет файлового моста памяти сессий.
- Runtime memory-core импортирует помощники индексирования SQLite transcript из
  `openclaw/plugin-sdk/memory-core-host-engine-session-transcripts`, а не из
  подпути QMD SDK. Подпуть QMD сохраняет compatibility re-export только для
  внешних вызывающих мест, пока крупная очистка SDK не сможет удалить его.
- Собственный `index.sqlite` QMD теперь является временной runtime-материализацией, основанной на
  основной таблице SQLite `plugin_blob_entries`. Runtime больше не создает постоянный
  sidecar `~/.openclaw/agents/<agentId>/qmd`.
- Опциональный Plugin `memory-lancedb` больше не создает
  `~/.openclaw/memory/lancedb` как неявное управляемое OpenClaw хранилище. Это
  внешний backend LanceDB, который остается отключенным, пока оператор не настроит
  явный `dbPath`.
- `check:database-first-legacy-stores` отклоняет новый исходный код runtime, который сочетает
  имена устаревших хранилищ с write-style API файловой системы. Он также отклоняет исходный код runtime,
  который повторно вводит удаленные маркеры моста transcript
  `transcriptLocator` или `sqlite-transcript://...`. Код миграции, doctor, импорта
  и явного экспорта не-сессионных данных остается разрешенным. Более широкие имена устаревших контрактов,
  такие как `sessionFile`, `storePath` и старые фасады файловой эпохи `SessionManager`,
  все еще имеют текущих владельцев и требуют отдельной работы над migration guard,
  прежде чем смогут стать обязательной предварительной проверкой. Guard теперь также покрывает
  runtime-хранилища `cache/*.json`, универсальные sidecar-файлы
  `thread-bindings.json`, JSON состояния/run-log cron, JSON здоровья config,
  sidecar-файлы restart и lock, настройки Voice Wake, одобрения привязок Plugin,
  JSON индекса установленных Plugin, JSONL-аудит File Transfer, журналы активности Memory Wiki,
  старый текстовый журнал встроенного `command-logger` и raw-stream JSONL
  диагностические переключатели pi-mono. Он также запрещает старые имена модулей doctor legacy на корневом уровне,
  чтобы код совместимости оставался в `src/commands/doctor/`. Android debug handlers
  также используют logcat/in-memory вывод вместо подготовки файлов кэша `camera_debug.log` или
  `debug_logs.txt`.

## Целевая форма схемы

Держите схемы явными. Runtime-состояние, принадлежащее хосту, использует типизированные таблицы. Непрозрачное состояние, принадлежащее Plugin,
использует `plugin_state_entries` / `plugin_blob_entries`; универсальной хостовой таблицы
`kv` нет.

Глобальная база данных:

```text
state_leases(scope, lease_key, owner, expires_at, heartbeat_at, payload_json, created_at, updated_at)
exec_approvals_config(config_key, raw_json, socket_path, has_socket_token, default_security, default_ask, default_ask_fallback, auto_allow_skills, agent_count, allowlist_count, updated_at_ms)
schema_meta(meta_key, role, schema_version, agent_id, app_version, created_at, updated_at)
agent_databases(agent_id, path, schema_version, last_seen_at, size_bytes)
task_runs(...)
task_delivery_state(...)
flow_runs(...)
subagent_runs(run_id, child_session_key, requester_session_key, controller_session_key, created_at, ended_at, cleanup_handled, payload_json)
current_conversation_bindings(binding_key, binding_id, target_agent_id, target_session_id, target_session_key, channel, account_id, conversation_kind, parent_conversation_id, conversation_id, target_kind, status, bound_at, expires_at, metadata_json, updated_at)
plugin_binding_approvals(plugin_root, channel, account_id, plugin_id, plugin_name, approved_at)
tui_last_sessions(scope_key, session_key, updated_at)
plugin_state_entries(plugin_id, namespace, entry_key, value_json, created_at, expires_at)
plugin_blob_entries(plugin_id, namespace, entry_key, metadata_json, blob, created_at, expires_at)
media_blobs(subdir, id, content_type, size_bytes, blob, created_at, updated_at)
skill_uploads(upload_id, kind, slug, force, size_bytes, sha256, actual_sha256, received_bytes, archive_blob, created_at, expires_at, committed, committed_at, idempotency_key_hash)
web_push_subscriptions(endpoint_hash, subscription_id, endpoint, p256dh, auth, created_at_ms, updated_at_ms)
web_push_vapid_keys(key_id, public_key, private_key, subject, updated_at_ms)
apns_registrations(node_id, transport, token, relay_handle, send_grant, installation_id, topic, environment, distribution, token_debug_suffix, updated_at_ms)
node_host_config(config_key, version, node_id, token, display_name, gateway_host, gateway_port, gateway_tls, gateway_tls_fingerprint, updated_at_ms)
device_identities(identity_key, device_id, public_key_pem, private_key_pem, created_at_ms, updated_at_ms)
device_auth_tokens(device_id, role, token, scopes_json, updated_at_ms)
macos_port_guardian_records(pid, port, command, mode, timestamp)
workspace_setup_state(workspace_key, workspace_path, version, bootstrap_seeded_at, setup_completed_at, updated_at)
native_hook_relay_bridges(relay_id, pid, hostname, port, token, expires_at_ms, updated_at_ms)
model_capability_cache(provider_id, model_id, name, input_text, input_image, reasoning, supports_tools, context_window, max_tokens, cost_input, cost_output, cost_cache_read, cost_cache_write, updated_at_ms)
agent_model_catalogs(catalog_key, agent_dir, raw_json, updated_at)
managed_outgoing_image_records(attachment_id, session_key, message_id, created_at, updated_at, retention_class, alt, original_media_id, original_media_subdir, original_content_type, original_width, original_height, original_size_bytes, original_filename, record_json)
gateway_restart_sentinel(sentinel_key, version, kind, status, ts, session_key, thread_id, delivery_channel, delivery_to, delivery_account_id, message, continuation_json, doctor_hint, stats_json, payload_json, updated_at_ms)
channel_pairing_requests(channel_key, account_id, request_id, code, created_at, last_seen_at, meta_json)
channel_pairing_allow_entries(channel_key, account_id, entry, sort_order, updated_at)
voicewake_triggers(config_key, position, trigger, updated_at_ms)
voicewake_routing_config(config_key, version, default_target_mode, default_target_agent_id, default_target_session_key, updated_at_ms)
voicewake_routing_routes(config_key, position, trigger, target_mode, target_agent_id, target_session_key, updated_at_ms)
update_check_state(state_key, last_checked_at, last_notified_version, last_notified_tag, last_available_version, last_available_tag, auto_install_id, auto_first_seen_version, auto_first_seen_tag, auto_first_seen_at, auto_last_attempt_version, auto_last_attempt_at, auto_last_success_version, auto_last_success_at, updated_at_ms)
config_health_entries(config_path, last_known_good_json, last_promoted_good_json, last_observed_suspicious_signature, updated_at_ms)
sandbox_registry_entries(registry_kind, container_name, session_key, backend_id, runtime_label, image, created_at_ms, last_used_at_ms, config_label_kind, config_hash, cdp_port, no_vnc_port, entry_json, updated_at)
cron_run_logs(store_key, job_id, seq, ts, status, error, summary, diagnostics_summary, delivery_status, delivery_error, delivered, session_id, session_key, run_id, run_at_ms, duration_ms, next_run_at_ms, model, provider, total_tokens, entry_json, created_at)
cron_jobs(store_key, job_id, name, description, enabled, delete_after_run, created_at_ms, agent_id, session_key, schedule_kind, schedule_expr, schedule_tz, every_ms, anchor_ms, at, stagger_ms, session_target, wake_mode, payload_kind, payload_message, payload_model, payload_fallbacks_json, payload_thinking, payload_timeout_seconds, payload_allow_unsafe_external_content, payload_external_content_source_json, payload_light_context, payload_tools_allow_json, delivery_mode, delivery_channel, delivery_to, delivery_thread_id, delivery_account_id, delivery_best_effort, failure_delivery_mode, failure_delivery_channel, failure_delivery_to, failure_delivery_account_id, failure_alert_disabled, failure_alert_after, failure_alert_channel, failure_alert_to, failure_alert_cooldown_ms, failure_alert_include_skipped, failure_alert_mode, failure_alert_account_id, next_run_at_ms, running_at_ms, last_run_at_ms, last_run_status, last_error, last_duration_ms, consecutive_errors, consecutive_skipped, schedule_error_count, last_delivery_status, last_delivery_error, last_delivered, last_failure_alert_at_ms, job_json, state_json, runtime_updated_at_ms, schedule_identity, sort_order, updated_at)
delivery_queue_entries(queue_name, id, status, entry_kind, session_key, channel, target, account_id, retry_count, last_attempt_at, last_error, recovery_state, platform_send_started_at, entry_json, enqueued_at, updated_at, failed_at)
commitments(id, agent_id, session_key, channel, account_id, recipient_id, thread_id, sender_id, kind, sensitivity, source, status, reason, suggested_text, dedupe_key, confidence, due_earliest_ms, due_latest_ms, due_timezone, source_message_id, source_run_id, created_at_ms, updated_at_ms, attempts, last_attempt_at_ms, sent_at_ms, dismissed_at_ms, snoozed_until_ms, expired_at_ms, record_json)
migration_runs(id, started_at, finished_at, status, report_json)
migration_sources(source_key, migration_kind, source_path, target_table, source_sha256, source_size_bytes, source_record_count, last_run_id, status, imported_at, removed_source, report_json)
backup_runs(id, created_at, archive_path, status, manifest_json)
```

База данных агента:

```text
schema_meta(meta_key, role, schema_version, agent_id, app_version, created_at, updated_at)
sessions(session_id, session_key, session_scope, created_at, updated_at, started_at, ended_at, status, chat_type, channel, account_id, primary_conversation_id, model_provider, model, agent_harness_id, parent_session_key, spawned_by, display_name)
conversations(conversation_id, channel, account_id, kind, peer_id, parent_conversation_id, thread_id, native_channel_id, native_direct_user_id, label, metadata_json, created_at, updated_at)
session_conversations(session_id, conversation_id, role, first_seen_at, last_seen_at)
session_routes(session_key, session_id, updated_at)
session_entries(session_id, session_key, entry_json, updated_at)
transcript_events(session_id, seq, event_json, created_at)
transcript_event_identities(session_id, event_id, seq, event_type, has_parent, parent_id, message_idempotency_key, created_at)
transcript_snapshots(session_id, snapshot_id, reason, event_count, created_at, metadata_json)
vfs_entries(namespace, path, kind, content_blob, metadata_json, updated_at)
tool_artifacts(run_id, artifact_id, kind, metadata_json, blob, created_at)
run_artifacts(run_id, path, kind, metadata_json, blob, created_at)
trajectory_runtime_events(session_id, run_id, seq, event_json, created_at)
memory_index_meta(key, value)
memory_index_sources(path, source, hash, mtime, size)
memory_index_chunks(id, path, source, start_line, end_line, hash, model, text, embedding, updated_at)
memory_embedding_cache(provider, model, provider_key, hash, embedding, dims, updated_at)
memory_index_state(id, revision)
cache_entries(scope, key, value_json, blob, expires_at, updated_at)
```

В будущем поиск может добавить таблицы FTS без изменения канонических таблиц событий:

```text
transcript_events_fts(session_id, seq, text)
vfs_entries_fts(namespace, path, text)
```

Крупные значения должны использовать столбцы `blob`, а не кодирование строк JSON. Оставляйте
`value_json` для небольших структурированных данных, которые должны оставаться доступными для проверки обычными
инструментами SQLite.

`agent_databases` — канонический реестр для этой ветки. Не добавляйте таблицу
`agents`, пока не появится реальный владелец записей агентов; конфигурация агентов остается в
`openclaw.json`.

## Форма миграции Doctor

Doctor должен вызывать один явный шаг миграции, который можно отразить в отчете и безопасно
запускать повторно:

```bash
openclaw doctor --fix
```

`openclaw doctor --fix` вызывает реализацию миграции состояния после
обычной предварительной проверки конфигурации и создает проверенную резервную копию перед импортом. Запуск runtime
и `openclaw migrate` не должны импортировать устаревшие файлы состояния OpenClaw.

Свойства миграции:

- Один проход миграции обнаруживает все устаревшие файловые источники и создает план
  до изменения чего-либо.
- Doctor создает проверенный архив резервной копии перед миграцией до импорта
  устаревших файлов.
- Импорты идемпотентны и ключуются по пути источника, mtime, размеру, хэшу и целевой
  таблице.
- Успешно обработанные исходные файлы удаляются или архивируются после того, как целевая база данных
  зафиксировала изменения.
- Неудачные импорты оставляют источник нетронутым и записывают предупреждение в
  `migration_runs`.
- Код runtime читает только SQLite после появления миграции.
- Путь понижения версии или экспорта обратно в runtime-файлы не требуется.

## Инвентаризация миграции

Перенесите это в глобальную базу данных:

- Записи времени выполнения реестра задач теперь используют общую базу данных; непоставлявшийся
  импортер вспомогательной базы `tasks/runs.sqlite` удален. Сохранения снимков выполняют upsert по id задачи
  и удаляют только отсутствующие строки задач/доставки.
- Записи времени выполнения Task Flow теперь используют общую базу данных; непоставлявшийся
  импортер вспомогательной базы `tasks/flows/registry.sqlite` удален. Сохранения снимков
  выполняют upsert по id потока и удаляют только отсутствующие строки потоков.
- Записи времени выполнения состояния Plugin теперь используют общую базу данных; непоставлявшийся
  импортер вспомогательной базы `plugin-state/state.sqlite` удален.
- Встроенный поиск по памяти больше не использует по умолчанию `memory/<agentId>.sqlite`; его
  индексные таблицы находятся в базе данных соответствующего агента, а явное
  подключение вспомогательного хранилища `memorySearch.store.path` было перенесено в миграцию конфигурации doctor.
- Переиндексация встроенной памяти сбрасывает только принадлежащие памяти таблицы в базе данных агента.
  Она не должна заменять весь файл SQLite, потому что та же база данных содержит
  сеансы, транскрипты, строки VFS, артефакты и кэши времени выполнения.
- Реестры sandbox-контейнеров/браузеров из монолитного и сегментированного JSON. Записи времени выполнения
  теперь используют общую базу данных; импорт устаревшего JSON сохраняется.
- Определения заданий Cron, состояние расписания и история запусков теперь используют общий SQLite;
  doctor импортирует/удаляет устаревшие файлы `jobs.json`, `jobs-state.json` и
  `cron/runs/*.jsonl`
- Идентификация/auth устройства, push, проверка обновлений, commitments, кэш моделей OpenRouter,
  индекс установленных plugin и привязки app-server
- Записи сопряжения и bootstrap устройства/узла теперь используют типизированные таблицы SQLite
- Подписчики уведомлений device-pair и маркеры доставленных запросов теперь используют
  общую таблицу SQLite plugin-state вместо `device-pair-notify.json`.
- Записи voice-call о звонках теперь используют общую таблицу SQLite plugin-state в пространстве имен
  `voice-call` / `calls` вместо `calls.jsonl`; CLI plugin
  отслеживает и суммирует историю звонков на базе SQLite.
- Сеансы Gateway QQBot, записи известных пользователей и кэш цитат ref-index теперь используют
  состояние SQLite plugin в пространствах имен `qqbot` (`sessions`, `known-users`,
  `ref-index`) вместо `session-*.json`, `known-users.json` и
  `ref-index.jsonl`; миграция doctor/setup QQBot импортирует и удаляет
  устаревшие файлы.
- Предпочтения выбора модели Discord, хэши развертывания команд и привязки тем
  теперь используют состояние SQLite plugin в пространствах имен `discord`
  (`model-picker-preferences`, `command-deploy-hashes`, `thread-bindings`)
  вместо `model-picker-preferences.json`, `command-deploy-cache.json` и
  `thread-bindings.json`; миграция doctor/setup Discord импортирует и
  удаляет устаревшие файлы.
- Курсоры догоняющей синхронизации BlueBubbles и входящие маркеры дедупликации теперь используют состояние SQLite plugin
  в пространствах имен `bluebubbles` (`catchup-cursors`, `inbound-dedupe`)
  вместо `bluebubbles/catchup/*.json` и
  `bluebubbles/inbound-dedupe/*.json`; миграция doctor/setup BlueBubbles
  импортирует и удаляет устаревшие файлы.
- Смещения обновлений Telegram, записи кэша стикеров, записи кэша цепочек ответов,
  записи кэша отправленных сообщений, записи кэша имен тем и привязки тем
  теперь используют состояние SQLite plugin в пространствах имен `telegram`
  (`update-offsets`, `sticker-cache`, `message-cache`, `sent-messages`,
  `topic-names`, `thread-bindings`) вместо `update-offset-*.json`,
  `sticker-cache.json`, `*.telegram-messages.json`,
  `*.telegram-sent-messages.json`, `*.telegram-topic-names.json` и
  `thread-bindings-*.json`; миграция doctor/setup Telegram импортирует и
  удаляет устаревшие файлы.
- Курсоры догоняющей синхронизации iMessage, сопоставления коротких reply id и строки дедупликации sent-echo
  теперь используют состояние SQLite plugin в пространствах имен `imessage` (`catchup-cursors`,
  `reply-cache`, `sent-echoes`) вместо `imessage/catchup/*.json`,
  `imessage/reply-cache.jsonl` и `imessage/sent-echoes.jsonl`; миграция doctor/setup iMessage
  импортирует и удаляет устаревшие файлы.
- Беседы Microsoft Teams, опросы, токены SSO и накопленные данные feedback learnings теперь
  используют пространства имен состояния SQLite plugin (`conversations`, `polls`, `sso-tokens`,
  `feedback-learnings`) вместо `msteams-conversations.json`,
  `msteams-polls.json`, `msteams-sso-tokens.json` и `*.learnings.json`; миграция
  doctor/setup Microsoft Teams импортирует и архивирует устаревшие файлы.
  Ожидающие загрузки являются краткоживущим кэшем SQLite, и старые JSON-файлы кэша
  не мигрируются.
- Кэш синхронизации Matrix, метаданные хранилища, привязки тем, входящие маркеры дедупликации,
  состояние cooldown проверки при запуске, учетные данные, ключи восстановления и
  криптографические снимки IndexedDB SDK теперь используют пространства имен состояния/blob SQLite plugin под
  `matrix` (`sync-store`, `storage-meta`, `thread-bindings`, `inbound-dedupe`,
  `startup-verification`, `credentials`, `recovery-key`, `idb-snapshots`)
  вместо `bot-storage.json`, `storage-meta.json`, `thread-bindings.json`,
  `inbound-dedupe.json`, `startup-verification.json`, `credentials.json`,
  `recovery-key.json` и `crypto-idb-snapshot.json`; миграция doctor/setup Matrix
  импортирует и удаляет эти устаревшие файлы из корней Matrix-хранилища, ограниченных аккаунтом.
- Курсоры шины Nostr и состояние публикации профиля теперь используют состояние SQLite plugin в
  пространствах имен `nostr` (`bus-state`, `profile-state`) вместо
  `bus-state-*.json` и `profile-state-*.json`; миграция doctor/setup Nostr
  импортирует и удаляет устаревшие файлы.
- Переключатели сеансов Active Memory теперь используют состояние SQLite plugin в
  `active-memory/session-toggles` вместо `session-toggles.json`.
- Очереди предложений Skill Workshop и счетчики ревью теперь используют состояние SQLite plugin
  в `skill-workshop/proposals` и `skill-workshop/reviews` вместо
  файлов `skill-workshop/<workspace>.json` для каждого workspace.
- Очереди исходящей доставки и доставки сеансов теперь совместно используют глобальную таблицу SQLite
  `delivery_queue_entries` с отдельными именами очередей
  (`outbound-delivery`, `session-delivery`) вместо устойчивых файлов
  `delivery-queue/*.json`, `delivery-queue/failed/*.json` и
  `session-delivery-queue/*.json`. Шаг doctor для устаревшего состояния импортирует
  ожидающие и неуспешные строки, удаляет устаревшие маркеры доставленных сообщений и удаляет старые
  JSON-файлы после импорта. Поля горячей маршрутизации и повторных попыток являются типизированными столбцами; JSON payload сохраняется только для replay/debug.
- Аренды процессов ACPX теперь используют состояние SQLite plugin в `acpx/process-leases`
  вместо `process-leases.json`.
- Метаданные запусков резервного копирования и миграции

Перенести это в базы данных агентов:

- Корни сеансов агента и payload записей сеанса в совместимой форме. Выполнено для
  записей времени выполнения: горячие метаданные сеанса доступны для запросов в `sessions`, а
  полный payload `SessionEntry` в устаревшей форме остается в `session_entries`.
- События транскриптов агента. Выполнено для записей времени выполнения.
- Контрольные точки Compaction и снимки транскриптов. Выполнено для записей времени выполнения:
  копии транскриптов контрольных точек являются строками транскриптов SQLite, а метаданные контрольных точек
  записываются в `transcript_snapshots`. Помощники контрольных точек Gateway
  теперь называют эти значения снимками транскриптов, а не исходными файлами.
- Scratch/workspace пространства имен VFS агента. Выполнено для записей VFS времени выполнения.
- Payload вложений subagent. Выполнено для записей времени выполнения: это seed-записи SQLite VFS
  и никогда не устойчивые файлы workspace.
- Артефакты инструментов. Выполнено для записей времени выполнения.
- Артефакты запусков. Выполнено для записей времени выполнения worker через таблицу
  `run_artifacts` для каждого агента.
- Локальные кэши времени выполнения агента. Выполнено для scoped-записей кэша времени выполнения worker через
  таблицу `cache_entries` для каждого агента. Gateway-wide кэши моделей остаются в
  глобальной базе данных, если только не станут специфичными для агента.
- Родительские журналы потоков ACP. Выполнено для записей времени выполнения.
- Сеансы replay ledger ACP. Выполнено для записей времени выполнения через
  `acp_replay_sessions` и `acp_replay_events`; устаревший `acp/event-ledger.json`
  остается только как входные данные doctor.
- Метаданные сеансов ACP. Выполнено для записей времени выполнения через `acp_sessions`; устаревшие
  блоки `entry.acp` в `sessions.json` являются только входными данными миграции doctor.
- Вспомогательные trajectory-файлы, когда они не являются явными export-файлами. Выполнено для записей
  времени выполнения: захват trajectory записывает строки `trajectory_runtime_events` в базе данных агента
  и зеркалирует артефакты, scoped для запуска, в SQLite. Устаревшие вспомогательные файлы являются только
  входными данными импорта doctor; export может материализовать свежие JSONL-выводы support-bundle,
  но не читает и не мигрирует старые вспомогательные trajectory/transcript-файлы во время выполнения.
  Захват trajectory во время выполнения предоставляет scope SQLite; помощники путей JSONL
  изолированы для export/debug поддержки и не реэкспортируются из runtime-модуля.
  Метаданные trajectory embedded-runner записывают идентичность `{agentId, sessionId, sessionKey}`
  вместо сохранения локатора транскрипта.

Пока оставить эти данные файловыми:

- `openclaw.json`
- файлы учетных данных provider или CLI
- манифесты plugin/package
- пользовательские workspace и Git-репозитории, когда выбран disk mode
- журналы, предназначенные для просмотра оператором tailing, если только конкретная поверхность журналирования не перенесена

## План миграции

### Фаза 0: Зафиксировать границу

Сделайте границу устойчивого состояния явной перед переносом дополнительных строк:

- Добавить таблицу `migration_runs` в глобальную базу данных.
  Выполнено для отчетов выполнения миграции устаревшего состояния.
- Добавить единую службу миграции состояния из файлов в базу данных, принадлежащую doctor.
  Выполнено: `openclaw doctor --fix` использует реализацию миграции устаревшего состояния.
- Сделать `plan` read-only, а `apply` должен создавать резервную копию, импортировать, проверять и
  затем удалять или помещать старые файлы в карантин.
  Выполнено: doctor создает проверенную резервную копию перед миграцией, передает путь резервной копии
  в `migration_runs` и повторно использует пути импортера/удаления.
- Добавить статические запреты, чтобы новый runtime-код не мог записывать устаревшие файлы состояния, при этом
  код миграции и тесты по-прежнему могли seed/read их.
  Выполнено для сейчас мигрированных устаревших хранилищ; guard также сканирует вложенные
  тесты на запрещенные runtime-контракты локатора транскриптов.

### Фаза 1: Завершить глобальную плоскость управления

Хранить общее координационное состояние в `state/openclaw.sqlite`:

- Агенты и реестр баз данных агентов
- Реестры Task и Task Flow
- Состояние Plugin
- Реестр sandbox-контейнеров/браузеров
- История запусков Cron/планировщика
- Сопряжение, устройство, push, update-check, TUI, кэши OpenRouter/моделей и другое
  небольшое runtime-состояние, scoped для Gateway
- Метаданные резервного копирования и миграции
- Байты медиа-вложений Gateway. Выполнено для записей времени выполнения; прямые файловые пути
  являются временными материализациями для совместимости с отправителями каналов и staging sandbox.
  Runtime allowlist принимает пути материализации SQLite, а не устаревшие корни state/config медиа.
  Doctor импортирует устаревшие медиафайлы в
  `media_blobs` и удаляет исходные файлы после успешной записи строк.
- Сеансы, события и blob payload захвата debug proxy. Выполнено: захваты находятся
  в общей БД состояния и открываются через общий bootstrap БД состояния, схему,
  WAL и настройки busy-timeout. Байты payload сжимаются gzip в
  `capture_blobs.data`; нет runtime-переопределения вспомогательной БД debug proxy,
  каталога blob или generated schema/codegen target только для proxy-capture.
  Миграция doctor/startup импортирует поставлявшиеся строки `debug-proxy/capture.sqlite`
  и связанные blob payload, включая активные устаревшие переопределения окружения DB/blob,
  затем архивирует эти источники, оставляя сертификаты CA нетронутыми.

Эта фаза также удаляет дублирующиеся openers вспомогательных файлов, permission helpers, настройку WAL,
очистку файловой системы и compatibility writers из этих подсистем.

### Фаза 2: Ввести базы данных для каждого агента

Создать одну базу данных на агента и зарегистрировать ее из глобальной БД:

```text
~/.openclaw/state/openclaw.sqlite
~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite
```

Глобальная строка `agent_databases` хранит путь, версию схемы, timestamp last-seen
и базовые метаданные размера/целостности. Runtime-код запрашивает БД агента у реестра,
а не выводит файловые пути напрямую.

БД агента содержит:

- `sessions` как канонический корень сеанса, с `session_entries` как таблицей полезной нагрузки в форме совместимости, привязанной к этому корню, и `session_routes` как уникальным поиском активного `session_key`
- `conversations` и `session_conversations` как нормализованная идентичность маршрутизации провайдера, привязанная к сеансам
- `transcript_events`
- снимки транскриптов и контрольные точки Compaction. Выполнено для записей среды выполнения.
- `vfs_entries`
- `tool_artifacts` и артефакты запусков
- локальные для агента строки среды выполнения/кеша. Выполнено для кешей в области воркера.
- события родительского потока ACP
- события траектории среды выполнения, когда они не являются явными артефактами экспорта

### Этап 3: Заменить API хранилища сеансов

Выполнено для среды выполнения. Поверхность хранилища сеансов в файловой форме не является активным контрактом среды выполнения:

- Среда выполнения больше не вызывает `loadSessionStore(storePath)` и не рассматривает `storePath` как идентичность сеанса.
- Операции среды выполнения со строками: `getSessionEntry`, `upsertSessionEntry`, `patchSessionEntry`, `deleteSessionEntry` и `listSessionEntries`.
- Помощники перезаписи всего хранилища, файловые писатели, тесты очередей, обрезка псевдонимов и параметры удаления устаревших ключей удалены из среды выполнения.
- Устаревшие экспорты совместимости корневого пакета все еще адаптируют канонические пути `sessions.json` к SQLite API строк.
- Разбор `sessions.json` остается только в коде миграции/импорта doctor и тестах doctor.
- Резервное чтение жизненного цикла среды выполнения читает заголовки транскриптов SQLite, а не первые строки JSONL.

Продолжайте удалять все, что повторно вводит параметры файловых блокировок, лексику обрезки/усечения как обслуживания файлов, идентичность через путь хранилища или тесты, единственная проверка которых — сохранение JSON.

### Этап 4: Перенести транскрипты, потоки ACP, траектории и VFS

Сделать каждый поток данных агента нативным для базы данных:

- Записи добавления транскрипта проходят через одну транзакцию SQLite, которая обеспечивает наличие заголовка сеанса, проверяет идемпотентность сообщения, выбирает родительский хвост, вставляет в `transcript_events` и записывает доступные для запросов метаданные идентичности в `transcript_event_identities`. Выполнено для прямых добавлений сообщений транскрипта и обычных сохраняемых добавлений `TranscriptSessionManager`; явные операции ветвления сохраняют свой явный выбор родителя и по-прежнему пишут строки SQLite без вывода какого-либо файлового локатора.
- Журналы родительских потоков ACP становятся строками, а не файлами `.acp-stream.jsonl`. Выполнено.
- Настройка порождения ACP больше не сохраняет пути JSONL транскриптов. Выполнено.
- Захват траектории среды выполнения пишет строки событий/артефакты напрямую. Явная команда поддержки/экспорта все еще может создавать JSONL-артефакты support-bundle как формат экспорта, но экспорт сеанса не воссоздает JSONL сеанса. Выполнено.
- Дисковые рабочие пространства остаются на диске, когда настроены в дисковом режиме.
- Черновая VFS и экспериментальный режим рабочего пространства только VFS используют базу данных агента.

Миграция импортирует старые файлы JSONL один раз, записывает счетчики/хеши в `migration_runs` и удаляет импортированные файлы после проверок целостности.

### Этап 5: Резервное копирование, восстановление, Vacuum и проверка

Резервные копии остаются одним архивным файлом:

- Выполнить checkpoint для каждой глобальной базы данных и базы данных агента.
- Сделать снимок каждой БД с использованием семантики резервного копирования SQLite или `VACUUM INTO`.
- Заархивировать компактные снимки БД, конфигурацию, внешние учетные данные и запрошенные экспорты рабочих пространств.
- Не включать сырые живые файлы `*.sqlite-wal` и `*.sqlite-shm`.
- Проверить, открыв каждый снимок БД и выполнив `PRAGMA integrity_check`.
  `openclaw backup create` выполняет эту проверку архива по умолчанию;
  `--no-verify` пропускает только постзаписной проход архива, а не проверку целостности создания снимка.
- Восстановление копирует снимки обратно в целевые пути. Эта ветка сбрасывает не выпущенную SQLite-разметку до `user_version = 1`; будущие выпущенные изменения схемы смогут добавить явные миграции, когда они понадобятся.

### Этап 6: Среда выполнения воркеров

Сохранять режим воркеров экспериментальным, пока приземляется разделение базы данных:

- Воркеры получают идентификатор агента, идентификатор запуска, режим файловой системы и идентичность реестра БД.
- Каждый воркер открывает собственное соединение SQLite.
- Родитель сохраняет полномочия доставки каналов, подтверждений, конфигурации и отмены.
- Начать с одного воркера на активный запуск; добавлять пул только после стабилизации жизненного цикла и владения соединениями БД.

### Этап 7: Удалить старый мир

Выполнено для управления сеансами среды выполнения. Старый мир разрешен только как явный ввод doctor или вывод поддержки/экспорта:

- Нет записей среды выполнения `sessions.json`, JSONL транскриптов, JSON-реестра песочницы, sidecar SQLite задач или sidecar SQLite состояния плагинов.
- Нет обрезки JSON/файлов сеансов, усечения файлов транскриптов, файловых блокировок сеансов или тестов сеансов в форме блокировок.
- Нет экспортов совместимости среды выполнения, цель которых — поддерживать старые файлы сеансов актуальными.
- Явные экспорты поддержки остаются запрошенными пользователем форматами архивирования/материализации и не должны передавать имена файлов обратно в идентичность среды выполнения.

## Резервное копирование и восстановление

Резервные копии должны быть одним архивным файлом, но захват базы данных должен быть нативным для SQLite:

1. Остановить длительную активность записи или войти в короткий барьер резервного копирования.
2. Для каждой глобальной базы данных и базы данных агента выполнить checkpoint.
3. Сделать снимок каждой базы данных с использованием семантики резервного копирования SQLite или `VACUUM INTO` во временный каталог резервной копии.
4. Заархивировать компактированные снимки баз данных, файл конфигурации, каталог учетных данных, выбранные рабочие пространства и манифест.
5. Проверить архив, открыв каждый включенный снимок SQLite и выполнив `PRAGMA integrity_check`.
   `openclaw backup create` делает это по умолчанию; `--no-verify` предназначен только для намеренного пропуска постзаписного прохода архива.

Не полагайтесь на сырые живые копии `*.sqlite`, `*.sqlite-wal` и `*.sqlite-shm` как на основной формат резервного копирования. Манифест архива должен записывать роль базы данных, идентификатор агента, версию схемы, исходный путь, путь снимка, размер в байтах и статус целостности.

Восстановление должно пересоздавать глобальную базу данных и файлы баз данных агентов из снимков архива. Поскольку SQLite-разметка еще не выпускалась, этот рефакторинг сохраняет только схему версии 1 плюс импорт из файлов в базу данных через doctor. Команда восстановления сначала проверяет архив, затем заменяет каждый ресурс манифеста из проверенной извлеченной полезной нагрузки.

## План рефакторинга среды выполнения

1. Добавить API реестра баз данных.
   - Разрешать пути глобальной БД и БД для каждого агента.
   - Сохранять не выпущенные схемы на `user_version = 1`; не добавлять код запуска миграций схемы, пока выпущенная схема не потребует этого.
   - Добавить помощники закрытия/checkpoint/целостности, используемые тестами, резервным копированием и doctor.

2. Свернуть sidecar-хранилища SQLite.
   - Перенести таблицы состояния плагинов в глобальную базу данных. Выполнено для записей среды выполнения; не выпущенный устаревший импортёр sidecar удален.
   - Перенести таблицы реестра задач в глобальную базу данных. Выполнено для записей среды выполнения; не выпущенный устаревший импортёр sidecar удален.
   - Перенести таблицы Task Flow в глобальную базу данных. Выполнено для записей среды выполнения; не выпущенный устаревший импортёр sidecar удален.
   - Перенести встроенные таблицы поиска памяти в базу данных каждого агента. Выполнено; явный пользовательский `memorySearch.store.path` теперь удаляется миграцией конфигурации doctor.
     Полная переиндексация выполняется на месте только против таблиц памяти; старый путь замены всего файла и помощник замены sidecar-индекса удалены.
   - Удалить дублирующиеся открыватели баз данных, настройку WAL, помощники разрешений и пути закрытия из этих подсистем.

3. Перенести принадлежащие агенту таблицы в базы данных для каждого агента.
   - Создавать БД агента по требованию через глобальный реестр баз данных. Выполнено.
   - Перенести записи сеансов среды выполнения, события транскриптов, строки VFS и артефакты инструментов в БД агентов. Выполнено.
   - Не мигрировать локальные для ветки записи сеансов общей БД, события транскриптов, строки VFS или артефакты инструментов; такая разметка никогда не выпускалась. Сохранять только устаревший импорт из файлов в базу данных в doctor.

4. Заменить API хранилища сеансов.
   - Удалить `storePath` как идентичность среды выполнения. Выполнено для среды выполнения и защищено `check:database-first-legacy-stores`: метаданные сеансов, обновления маршрутов, сохранение команд, очистка сеансов CLI, предварительные просмотры рассуждений Feishu, сохранение состояния транскрипта, глубина subagent, переопределения сеансов профиля auth, логика parent-fork и инспекция QA-lab теперь разрешают базу данных из канонических ключей агента/сеанса.
     Ответы списков сеансов Gateway/TUI/UI/macOS теперь раскрывают `databasePath` вместо устаревшего `path`; отладочные поверхности macOS показывают базу данных для каждого агента как состояние только для чтения вместо записи конфигурации `session.store`.
     `/status`, экспорт траекторий из чата и прокси зависимостей CLI больше не распространяют устаревшие пути хранилища; резервное чтение использования транскрипта читает SQLite по идентичности агента/сеанса. Тесты среды выполнения и bridge больше не раскрывают `storePath`; вводы doctor/миграции владеют этим устаревшим именем поля.
     Загрузка объединенных сеансов Gateway больше не имеет специальной ветки среды выполнения для нешаблонных значений `session.store`; она агрегирует строки SQLite для каждого агента.
     Устаревшая ветка doctor для блокировки сеансов и ее помощник очистки `.jsonl.lock` были удалены; теперь SQLite является границей конкурентного доступа сеансов.
     Горячие места вызовов среды выполнения используют имена помощников, ориентированные на строки, такие как `resolveSessionRowEntry`; старый совместимый псевдоним `resolveSessionStoreEntry` удален из среды выполнения и экспортов Plugin SDK.

- Использовать операции строк `{ agentId, sessionKey }`.
  Выполнено: `getSessionEntry`, `upsertSessionEntry`, `deleteSessionEntry`, `patchSessionEntry` и `listSessionEntries` — SQLite-first API, которые не требуют пути хранилища сеансов. Сводка статуса, статус локального агента, здоровье и команда списка `openclaw sessions` теперь читают строки для каждого агента напрямую и показывают пути к SQLite-базам данных каждого агента вместо путей `sessions.json`.
- Заменить удаление/вставку всего хранилища на `upsertSessionEntry`,
  `deleteSessionEntry`, `listSessionEntries` и SQL-запросы очистки.
  Выполнено для среды выполнения: горячие пути теперь используют API строк и патчи строк с повтором при конфликте; оставшиеся помощники импорта/замены всего хранилища ограничены кодом импорта миграции и тестами SQLite-бэкенда.
  - Удалить `store-writer.ts` и тесты очереди писателя. Выполнено.
  - Удалить обрезку устаревших ключей среды выполнения и параметры удаления псевдонимов из upsert/patch строк сеансов. Выполнено.

5. Удалить поведение JSON-реестра среды выполнения.
   - Сделать чтение и запись реестра песочницы только SQLite. Выполнено.
   - Импортировать монолитный и шардированный JSON только из этапа миграции. Выполнено.
   - Удалить блокировки шардированного реестра и записи JSON. Выполнено.

- Сохранять одну типизированную таблицу реестра вместо хранения строк реестра как общего непрозрачного JSON, если форма остается операционным состоянием горячего пути. Выполнено.

6. Удалить мутацию сеансов в форме файловых блокировок.
   - Выполнено для создания блокировок среды выполнения и API блокировок среды выполнения.
   - Отдельная устаревшая ветка очистки doctor для `.jsonl.lock` удалена.
   - `session.writeLock` — это устаревшая конфигурация, мигрируемая doctor, а не типизированная настройка среды выполнения.
   - Целостность состояния больше не имеет отдельного пути обрезки осиротевших файлов транскриптов; миграция doctor импортирует/удаляет устаревшие источники JSONL в одном месте.
   - Координация singleton Gateway использует типизированные строки SQLite `state_leases` под `gateway_locks` и больше не раскрывает шов каталога файловых блокировок.
   - Универсальное сохранение дедупликации Plugin SDK больше не использует файловые блокировки или JSON-файлы; оно пишет общие SQLite-строки состояния плагинов. Выполнено.
   - Координация QMD embed использует SQLite-аренду состояния вместо `qmd/embed.lock`. Выполнено.

7. Сделать воркеры осведомленными о базе данных.
   - Воркеры открывают собственные соединения SQLite.
   - Родитель владеет доставкой, обратными вызовами каналов и конфигурацией.
   - Воркер получает идентификатор агента, идентификатор запуска, режим файловой системы и идентичность реестра БД, а не живые handles.
   - `vfs-only` остается экспериментальным и использует базу данных агента как корень своего хранилища.
   - Сначала сохранять один воркер на активный запуск. Пул может подождать, пока срок жизни соединений БД и поведение отмены не станут скучными.

8. Интеграция резервного копирования.
   - Научить резервное копирование создавать снимки глобальных и агентских баз данных через SQLite backup или
     `VACUUM INTO`. Выполнено для обнаруженных файлов `*.sqlite` в state asset.
   - Добавить проверку резервных копий на целостность SQLite и версию схемы. Выполнено для
     создания резервной копии и стандартных проверок целостности архива.
   - Записывать метаданные запусков резервного копирования в SQLite. Выполнено через общую таблицу `backup_runs`
     с путем к архиву, статусом и JSON манифеста.
   - Добавить восстановление из проверенных архивных снимков. Выполнено: `openclaw backup
restore` проверяет перед извлечением, использует нормализованный
     манифест верификатора, поддерживает `--dry-run` и требует `--yes` перед заменой
     записанных исходных путей.
   - Включать экспорт VFS/рабочей области только по запросу; не экспортировать внутренности сессий
     как JSON или JSONL.

9. Удалить устаревшие тесты и код. Выполнено для известных поверхностей runtime-сессий.

- Удалить тесты, которые проверяют создание runtime-файлов `sessions.json` или transcript
  JSONL. Выполнено для основного хранилища сессий, чата, событий Gateway transcript,
  предпросмотра, жизненного цикла, обновлений command session-entry, сброса/трассировки автоответа и
  фикстур memory-core dreaming, маршрутизации цели approval, восстановления session transcript,
  восстановления разрешений безопасности, экспорта траекторий и экспорта сессий.
  Тесты transcript для active-memory теперь проверяют области SQLite и отсутствие создания временных или
  постоянных JSONL-файлов.
  Старый регрессионный тест heartbeat для обрезки transcript был удален, потому что
  runtime больше не обрезает JSONL transcripts.
  Тесты инструмента agent session-list больше не моделируют устаревшие пути `sessions.json`
  как форму ответа Gateway; тесты app/UI/macOS используют `databasePath`.
  Тесты использования transcript в `/status` теперь напрямую заполняют строки SQLite transcript
  вместо записи JSONL-файлов.
  Тесты жизненного цикла сессий Gateway теперь напрямую используют helpers для заполнения SQLite transcript;
  старая форма фикстуры однострочного session-file удалена из покрытия reset
  и delete.
  `sessions.delete` больше не возвращает поле файловой эпохи `archived: []`; удаление
  сообщает только результат мутации строки. Старый параметр `deleteTranscript`
  тоже удален: удаление сессии удаляет канонический корень `sessions` и позволяет
  SQLite каскадно удалить принадлежащие сессии строки transcript, snapshot и trajectory, так что
  ни один вызывающий код не может оставить transcript-сироты или забыть ветку очистки.
  Тесты захвата траекторий context-engine теперь читают строки `trajectory_runtime_events`
  из изолированной агентской базы данных вместо чтения
  `session.trajectory.jsonl`.
  Скрипты начального заполнения Docker MCP channel теперь напрямую заполняют строки SQLite. Прямые
  записи `sessions.json` ограничены фикстурами doctor.
  Tool Search Gateway E2E читает доказательства tool-call из строк SQLite transcript
  вместо сканирования файлов `agents/<agentId>/sessions/*.jsonl`.
  Host-события memory-core и scratch-строки session-corpus теперь живут в общем
  SQLite plugin-state; `events.jsonl` и `session-corpus/*.txt` являются только входами
  legacy doctor migration. Активные строки используют виртуальные пути `memory/session-ingestion/`,
  а не `.dreams/session-corpus`. Старый модуль восстановления memory-core dreaming
  и его CLI/Gateway тесты были удалены, потому что runtime больше не владеет
  восстановлением файлового архива для этого корпуса. Тесты memory-core
  bridge/public-artifact больше не показывают `.dreams/events.jsonl`; они
  используют имя виртуального JSON-артефакта на базе SQLite.
  Публичная документация SDK/Codex testing теперь говорит о состоянии сессий SQLite вместо файлов сессий,
  а пример channel-turn больше не раскрывает аргумент `storePath`.
  Состояние синхронизации Matrix теперь напрямую использует хранилище SQLite plugin-state. Активные
  контракты client/runtime передают корень хранилища аккаунта, а не путь `bot-storage.json`,
  а doctor импортирует legacy `bot-storage.json` в SQLite перед удалением
  источника. QA-сценарии Matrix restart/destructive теперь напрямую изменяют строку синхронизации SQLite
  вместо создания или удаления фиктивных файлов `bot-storage.json`, а
  субстрат E2EE передает корень sync-store вместо фиктивного
  пути `sync-store.json`.
  Выбор storage-root в Matrix больше не оценивает корни по legacy JSON-файлам sync/thread;
  он использует долговечные метаданные корня плюс реальное криптографическое состояние.
  Набор тестов SQLite session backend в runtime больше не фабрикует
  `sessions.json`; legacy исходные фикстуры теперь живут в тестах doctor,
  которые их импортируют.
  Тесты сессий Gateway больше не раскрывают helper `createSessionStoreDir` или
  неиспользуемую настройку временного пути session-store; каталоги фикстур явные, а прямая
  настройка строк использует именование SQLite session-row.
  Покрытие парсера JSON5 session-store только для doctor перенесено из infra-тестов
  в тесты migration doctor, поэтому runtime-наборы тестов больше не владеют legacy
  парсингом session-file.
  Runtime-тесты Microsoft Teams SSO/pending-upload больше не несут JSON sidecar
  фикстуры или парсеры; legacy-парсинг SSO token живет только в модуле
  migration Plugin. Тесты Telegram больше не заполняют фиктивные пути хранилищ `/tmp/*.json`;
  они напрямую сбрасывают message cache на базе SQLite. Общий helper
  OpenClaw test-state больше не раскрывает legacy writer `auth-profiles.json`;
  тесты doctor auth migration локально владеют этой фикстурой.
  Runtime-тесты для TUI указателей last-session, exec approvals, переключателей active-memory,
  проверки Matrix dedupe/startup, синхронизации источника Memory Wiki,
  привязок current-conversation, onboarding auth и импортов Hermes secret больше
  не создают старые sidecar-файлы и не проверяют отсутствие старых имен файлов. Они
  доказывают поведение через строки SQLite и публичные API хранилища; тесты doctor/migration
  являются единственным местом, где уместны legacy имена исходных файлов.
  Runtime-тесты для сопряжения device/node, channel allowFrom, restart intents,
  restart handoff, записей очереди доставки сессий, работоспособности config, кешей iMessage,
  cron jobs, заголовков PI transcript, реестров subagent и управляемых
  вложений изображений также больше не создают выведенные из употребления файлы JSON/JSONL только чтобы доказать,
  что они игнорируются или отсутствуют.
  Восстановление PI overflow больше не имеет fallback SessionManager для перезаписи/обрезки:
  обрезка tool-result и перезаписи transcript context-engine изменяют
  строки SQLite transcript, затем обновляют активное состояние prompt из базы данных.
  Сохраняемые добавления сообщений SessionManager делегируют атомарному helper добавления
  SQLite transcript выбор родителя и идемпотентность. Обычные добавления
  metadata/custom entry также выбирают текущего родителя внутри SQLite, поэтому
  устаревшие экземпляры manager не воскрешают гонки parent-chain до SQLite.
  Синтетическая очистка хвоста PI для mid-turn prechecks и `sessions_yield` теперь
  напрямую обрезает состояние SQLite transcript; старый мост удаления хвоста SessionManager
  и его тесты удалены.
  Захват checkpoint Compaction также делает снимки только из SQLite; вызывающий код
  больше не передает живой SessionManager как альтернативный источник transcript.
- Оставить тесты, которые заполняют legacy-файлы, только для migration.
- Доказательства на основе JSON-файлов заменены доказательствами строк SQL для активных runtime
  поверхностей.

- Добавить статические запреты на runtime-записи в legacy JSON-пути сессий/кеша.
  Выполнено для repo guard.

10. Сделать отчет migration аудируемым.
    - Записывать запуски migration в SQLite со временем started/finished, исходными
      путями, хешами источников, счетчиками, предупреждениями и путем резервной копии.
      Выполнено: выполнения legacy-state migration теперь сохраняют отчет `migration_runs`
      с инвентаризацией исходных путей/таблиц, SHA-256 исходных файлов, размерами,
      счетчиками записей, предупреждениями и путем резервной копии.
      Выполнено: выполнения legacy-state migration также сохраняют строки `migration_sources`
      для аудита на уровне источников и будущих решений skip/backfill.
    - Сделать apply идемпотентным. Повторный запуск после частичного импорта должен либо
      пропускать уже импортированный источник, либо объединять по стабильному ключу.
      Выполнено: индексы сессий, transcripts, очереди доставки, plugin state, task
      ledgers и принадлежащие агентам глобальные строки SQLite импортируются через стабильные ключи или
      семантику upsert/replace, поэтому повторные запуски объединяют данные без дублирования долговечных
      строк.
    - Неудачные импорты должны оставлять исходный файл на месте.
      Выполнено: неудачные импорты transcript теперь оставляют исходный JSONL-файл по
      обнаруженному пути, а `migration_sources` записывает источник как
      `warning` с `removed_source=0` для следующего запуска doctor.

## Правила производительности

- Одно соединение на поток/процесс допустимо; не используйте общие handles между
  workers.
- Используйте WAL, `foreign_keys=ON`, 30-секундный busy timeout и короткие
  write transactions `BEGIN IMMEDIATE`.
- Оставляйте helpers write transaction синхронными, пока/если async transaction
  API не добавит явную семантику mutex/backpressure.
- Держите parent delivery writes небольшими и транзакционными.
- Избегайте перезаписи всего хранилища; используйте upsert/delete на уровне строк.
- Добавьте индексы для list-by-agent, list-by-session, updated-at, run id и
  expiration paths перед переносом hot code.
- Храните большие артефакты, медиа и векторы как BLOBs или chunked BLOB rows, а не
  base64 или numeric-array JSON.
- Держите opaque plugin-state entries небольшими и scoped.
- Добавьте SQL cleanup для TTL/expiration вместо filesystem pruning.
  Выполнено для runtime-хранилищ, принадлежащих базе данных: media, plugin state, plugin blobs,
  persistent dedupe и agent cache истекают через строки SQLite. Оставшаяся
  очистка файловой системы ограничена временными materializations или явными
  командами удаления.

## Статические запреты

Добавить проверку репозитория, которая запрещает новые runtime-записи в legacy state paths:

- `sessions.json`
- `*.trajectory.jsonl`, кроме материализованных выводов support-bundle
- `.acp-stream.jsonl`
- `acp/event-ledger.json`
- файлы runtime-кэша `cache/*.json`
- `agents/<agentId>/agent/auth.json`
- `agents/<agentId>/agent/models.json`
- `credentials/oauth.json`
- `github-copilot.token.json`
- `openrouter-models.json`
- `auth-profiles.json`
- `auth-state.json`
- `exec-approvals.json`
- `workspace-state.json`
- Matrix `credentials*.json` и `recovery-key.json`
- `cron/runs/*.jsonl`
- `cron/jobs.json`
- `jobs-state.json`
- `device-pair-notify.json`
- `devices/pending.json`
- `devices/paired.json`
- `devices/bootstrap.json`
- `nodes/pending.json`
- `nodes/paired.json`
- `identity/device.json`
- `identity/device-auth.json`
- `push/web-push-subscriptions.json`
- `push/vapid-keys.json`
- `push/apns-registrations.json`
- `process-leases.json`
- `gateway-instance-id`
- `session-toggles.json`
- Memory-core `.dreams/events.jsonl`
- Memory-core `.dreams/session-corpus/`
- Memory-core `.dreams/daily-ingestion.json`
- Memory-core `.dreams/session-ingestion.json`
- Memory-core `.dreams/short-term-recall.json`
- Memory-core `.dreams/phase-signals.json`
- Memory-core `.dreams/short-term-promotion.lock`
- Skill Workshop `skill-workshop/<workspace>.json`
- Skill Workshop `skill-workshop/skill-workshop-review-*.json`
- Nostr `bus-state-*.json`
- Nostr `profile-state-*.json`
- `calls.jsonl`
- `known-users.json`
- `ref-index.jsonl`
- QQBot `session-*.json`
- BlueBubbles `bluebubbles/catchup/*.json`
- BlueBubbles `bluebubbles/inbound-dedupe/*.json`
- Telegram `update-offset-*.json`
- Telegram `sticker-cache.json`
- Telegram `*.telegram-messages.json`
- Telegram `*.telegram-sent-messages.json`
- Telegram `*.telegram-topic-names.json`
- Telegram `thread-bindings-*.json`
- iMessage `catchup/*.json`
- iMessage `reply-cache.jsonl`
- iMessage `sent-echoes.jsonl`
- Microsoft Teams `msteams-conversations.json`
- Microsoft Teams `msteams-polls.json`
- Microsoft Teams `msteams-sso-tokens.json`
- Microsoft Teams `*.learnings.json`
- Matrix `bot-storage.json`
- Matrix `sync-store.json`
- Matrix `thread-bindings.json`
- Matrix `inbound-dedupe.json`
- Matrix `startup-verification.json`
- Matrix `storage-meta.json`
- Matrix `crypto-idb-snapshot.json`
- Discord `model-picker-preferences.json`
- Discord `command-deploy-cache.json`
- JSON-файлы сегментов реестра песочницы
- JSON-файлы моста `/tmp` ретранслятора native hook
- `plugin-state/state.sqlite`
- произвольные runtime-сайдкары `openclaw-state.sqlite`
- `tasks/runs.sqlite`
- `tasks/flows/registry.sqlite`
- `bindings/current-conversations.json`
- `restart-sentinel.json`
- `gateway-restart-intent.json`
- `gateway-supervisor-restart-handoff.json`
- `gateway.<hash>.lock`
- `qmd/embed.lock`
- `commands.log`
- `config-health.json`
- `port-guard.json`
- `settings/voicewake.json`
- `settings/voicewake-routing.json`
- `plugin-binding-approvals.json`
- `plugins/installs.json`
- `audit/file-transfer.jsonl`
- `audit/crestodian.jsonl`
- `crestodian/rescue-pending/*.json`
- `plugins/phone-control/armed.json`
- Memory Wiki `.openclaw-wiki/log.jsonl`
- Memory Wiki `.openclaw-wiki/state.json`
- Memory Wiki `.openclaw-wiki/locks/`
- Memory Wiki `.openclaw-wiki/source-sync.json`
- Memory Wiki `.openclaw-wiki/import-runs/*.json`
- Memory Wiki `.openclaw-wiki/cache/agent-digest.json`
- Memory Wiki `.openclaw-wiki/cache/claims.jsonl`
- ClawHub `.clawhub/lock.json`
- ClawHub `.clawhub/origin.json`
- оформление профиля браузера `.openclaw-profile-decorated`
- открыватели файловых сессий `SessionManager.open(...)`
- фасады перечисления транскриптов `SessionManager.listAll(...)` и `TranscriptSessionManager.listAll(...)`
- фасады форка транскриптов `SessionManager.forkFromSession(...)` и
  `TranscriptSessionManager.forkFromSession(...)`
- фасады замены изменяемых сессий `SessionManager.newSession(...)` и `TranscriptSessionManager.newSession(...)`
- фасады ветвления сессий `SessionManager.createBranchedSession(...)` и
  `TranscriptSessionManager.createBranchedSession(...)`

Запрет должен разрешать тестам создавать legacy-фикстуры и разрешать коду миграции читать, импортировать и удалять legacy-источники файлов. Непоставленные SQLite-сайдкары остаются запрещенными и не получают разрешений на импорт через doctor.

## Критерии готовности

- Записи runtime-данных и кэша идут в глобальную или агентскую базу данных SQLite.
- Runtime больше не записывает индексы сессий, JSONL транскриптов, JSON реестра песочницы, task-сайдкар SQLite или plugin-state-сайдкар SQLite. Импортеры непоставленных task- и plugin-state-сайдкаров SQLite удалены.
- Импорт legacy-файлов выполняется только через doctor.
- Backup создает один архив с компактными снимками SQLite и подтверждением целостности.
- Рабочие процессы агентов могут запускаться с диском, VFS scratch или экспериментальным хранилищем только на VFS.
- Конфигурация и явные файлы учетных данных остаются единственными ожидаемыми постоянными управляющими файлами вне базы данных.
- Проверки репозитория предотвращают повторное появление legacy runtime-хранилищ файлов.
