Модель данных

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

Каждый инструмент WireCat — tg, max, memo, zm — хранит прочитанное в одном локальном файле SQLite, wirecat.db. Эта страница для разработчиков и авторов агентов, которые хотят делать запросы к этому файлу или понимать, что агент может в нём найти. Сначала — сущности, потом связи между ними, потом то, как поиск находит контекст, и только в конце — столбцы, по которым стоит искать. Как хранилище открывается, обновляется и блокируется, описано в разделе локальное хранилище.

Страница описывает схему по состоянию на 10 октября 2026 года, на фиксированном коммите cli-messaging — пакета, которому принадлежит файл. Все столбцы с их значением перечислены в справочнике схемы.

Ключевые сущности

Как основные сущности ссылаются друг на друга

Словами: у человека есть учётные записи; учётные записи отправляют сообщения и письма и участвуют во встречах. Организация объединяет аккаунты и проекты. Каждый аккаунт приносит свои чаты, письма, документы и встречи; в чатах лежат сообщения, а сообщения складываются в обсуждения. Встреча относится к событию. В проекте лежат задачи и решения. Воспоминание и метка могут относиться к любой сущности, поэтому на их пунктирных линиях написано «любая сущность»; линия к аккаунту обозначает их все. Цвета группируют карточки: люди, чаты и почта, аккаунт, через который они пришли, документы и встречи, работа и то, что крепится к чему угодно.

Люди и учётные записи

Человек (person) — это один живой человек. Учётная запись (identity) — то, как его знает один источник: пользователь Telegram, пользователь MAX, адрес почты, участник Zoom. У одного человека много учётных записей; каждая принадлежит не более чем одному человеку, и рядом хранится, каким способом и с какой уверенностью их связали. Прежние версии профиля сохраняются. Бот (bot) — ИИ-агент или скрипт, который работает в хранилище: он может владеть проектами, брать задачи и писать заметки. Бот самого мессенджера — это учётная запись, а не бот.

Организации и проекты

Организация — компания, команда, семья или сообщество. Проект — место для задач с коротким ключом (MEET), типом (работа, клиент, личное, открытый код, другое) и необязательной организацией. Рабочие аккаунты и проекты принадлежат организации; семья — тоже организация.

Аккаунты и чаты

Аккаунт — одно подключение владельца: аккаунт мессенджера, почтовый ящик, Zoom, папка с заметками. Чат принадлежит одному аккаунту. Чаты вкладываются друг в друга: тема форума лежит внутри своей группы, группа обсуждения — под своим каналом. Ветка — другое: она висит на корневом сообщении внутри одного чата, как комментарии под постом канала.

Сообщения и обсуждения

Сообщение принадлежит чату, его отправляет учётная запись. При правке прежний текст сохраняется, а сообщение, удалённое в источнике, сохраняет строку без текста. Обсуждение — группа сообщений одного чата, относящихся к одному разговору; его собирают заново по ответам, упоминаниям и продолжениям от того же отправителя (как они собираются).

Почта

Письмо принадлежит цепочке писем и одному аккаунту. Отправитель и каждый получатель (to, cc, bcc) сводятся к учётным записям, поэтому письма и сообщения одного человека сходятся на одном человеке. Папки и ярлыки — это почтовые ящики; одно письмо может лежать в нескольких.

Документы, заметки и воспоминания

Документ существует сам по себе: файл в папке заметок, страница, страница вики. Заметка — короткий текст, который человек или бот написал об одной вещи: человеке, чате, встрече, задаче; комментарии к задаче — тоже заметки. Воспоминание (memory) — вывод агента: сводка, дневной дайджест, факт, предпочтение. Вывод может быть ошибочным, поэтому воспоминание всегда несёт свои доказательства, уверенность и статус (предложено, подтверждено, устарело, заменено).

События и встречи

Событие — запись владельца о том, что произошло, без полей конкретного сервиса. Встреча — один её раз, как его записало приложение для встреч: участники, расшифровки, чат встречи и сводки. Встреча указывает на своё событие, поэтому записи об одном звонке из разных источников сходятся на одном событии. У повторяющихся событий и повторяющихся встреч есть своя серия.

Задачи и решения

Задача принадлежит проекту и называется ключом и номером (MEET-12). Её тип — ошибка, функция, рутина, вопрос, просьба, упоминание или обещание: заданный кем-то вопрос — задача типа «вопрос», данное кем-то обещание — задача типа «обещание». Люди и боты назначаются на задачу в роли (исполнитель, ревьюер, наблюдатель), и каждое изменение — событие задачи. Решение — выбор, который действует, пока его не заменит другой; его доказательства привязаны к нему.

Метки и темы

Метка — свободный ярлык, который может поставить кто угодно. Тема — метка из короткого списка, который создаёт только владелец; агенты могут её предложить. У вещи может быть много меток и тем, и одну тему можно отметить как главную. Имя бывает либо меткой, либо темой, но не тем и другим сразу.

Предложенные и записанные действия

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

Как они связаны

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

  • Связи (links) соединяют две вещи и называют вид связи: задачу-вопрос с сообщением, которое на него ответило (answered-by), решение или воспоминание с его доказательством (evidence), вещь с тем, из чего она сделана (created-from), задачу с людьми, сообщениями, письмами и встречами, которых она касается.
  • Отметки (taggings) ставят метку или тему на любую вещь.
  • Заметки привязывают текст человека к любой вещи.
  • Псевдонимы (aliases) дают любой вещи другие имена: прозвище, имя контакта в одном мессенджере, короткое имя проекта. Их читают сопоставление имён и поиск.

Область (scope) — personal или work. Её задают у аккаунтов, организаций и проектов, а то, что в них лежит, наследует её: чат берёт область своего аккаунта, если не задал свою, а воспоминание берёт область своих доказательств. Рабочий вопрос читает только рабочие данные и никогда не затянет личный чат.

Как поиск находит контекст

Чтобы найти контекст для агента, нужны три шага, в таком порядке.

  1. Сузить таблицами. Человек, проект, аккаунт, время и область — это столбцы с индексами, так что первый шаг — обычный SQL. Короткий путь — индекс участия: по строке на каждое сообщение, письмо, встречу, задачу или документ, где человек участвовал, с его ролью, временем и областью. «Всё, что Alice Example делала по работе в этом месяце» — один диапазон по индексу.
  2. Искать слова и смысл только среди кандидатов. У каждого вида текста свой индекс слов и основ: сообщения, письма, вложения, документы, заметки, воспоминания и встречи. Затем векторы ранжируют по смыслу, а каждый кусок текста несёт область, аккаунт, проект и время своего источника, поэтому перебор векторов сначала фильтрует, а потом сравнивает. Как устроены слова, основы и векторы, объясняет страница как работает поиск.
  3. Расширить по связям. От найденного идти по цепочке ответов, к обсуждению, к корню ветки, к цепочке писем или к встрече, из которой пришла строка расшифровки, и по связям, которые на него указывают.
SELECT subject_type, subject_id, role, occurred_at
FROM involvements
WHERE person_id = :person AND scope = 'work' AND occurred_at >= :since
ORDER BY occurred_at DESC
LIMIT 50

Векторы бывают у длинных текстов: обсуждений, документов, писем, текста вложений, заметок, воспоминаний, расшифровок и сводок встреч, событий и задач. У отдельного сообщения своего вектора нет: его находят по словам или по смыслу через его обсуждение. Текст режется на куски, а вектор каждого куска хранится один раз на модель и хеш текста, так что один и тот же текст не векторизуется дважды.

Индекс участия производный: его собирают заново из отправителей, получателей, участников, исполнителей и подтверждённых связей, и в нём нет ничего сверх них.

Таблицы

Все таблицы хранилища по областям, с типом и значением каждого столбца. Время — в миллисекундах эпохи. В справочнике схемы есть ещё все ограничения и индексы.

Соглашения об именах

  • Таблицы во множественном числе, в snake_case. Первичный ключ — id; внешний ключ — <singular>_id.
  • external_id — собственный id строки в источнике; id другой вещи в источнике — <thing>_external_id.
  • created_at и updated_at говорят, когда строку сохранили и изменили в этом файле. Время событий в мире носит свои имена: sent_at, joined_at, started_at, due_at.
  • deleted_at значит «исчезло в источнике». Синхронизация никогда не удаляет строку; это делает только команда владельца.
  • Указатель на строку любой таблицы — текст <name>_type, имя таблицы в единственном числе, и целое <name>_id. Кто сделал вещь или владеет ею — person или bot.
  • У того, по чему ищут, свой столбец; metadata — JSON для того, по чему не ищут.
  • У флагов нет приставки is_. Время — целые числа в миллисекундах эпохи.
  • У каждого внешнего ключа и у каждой пары _type/_id есть индекс.

Источники

Каждая интеграция — это аккаунт; каждый запуск синхронизации записывается в журнал.

accounts

Каждая интеграция, которую подключает владелец: аккаунт мессенджера, почтовый ящик, Zoom, папка заметок.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
providertext, not nullМессенджер, к которому относится аккаунт, строчным именем, например telegram или max; список открыт, поэтому новым адаптерам не нужны изменения.
external_idtext, not nullсобственный id источника для него
nametextОтображаемое имя аккаунта, как его сообщает мессенджер; NULL, если неизвестно.
created_atinteger, not nullкогда эта строка была сохранена здесь
settingstextJSON: настройки интеграции, например путь и формат папки
statustextactive, paused, failed
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
scopetext, not nullpersonal или work; что запрос контекста может читать для данной цели, чтобы рабочий вопрос никогда не затягивал личные чаты
organization_idinteger → organizations.idорганизация, к которой относится рабочий аккаунт

sync_cursors

Для каждого аккаунта — что синхронизация помнит между запусками: маркер изменений, когда список последний раз был полным.

СтолбецТипЗначение
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
keytext, not nullИмя того, что синхронизация помнит для этого аккаунта, например chat_list_complete, history_start:&lt;chatId>, fetched:&lt;chatId> или маркер изменений источника.
valuetext, not nullЗапомненное значение в виде текста (маркер изменений, id сообщения, список JSON); числа вызывающий код кодирует сам.
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
created_atinteger, not nullкогда эта строка была сохранена здесь

sync_ranges

Отрезки истории каждого чата, которые хранятся полностью, чтобы загрузка знала, чего не хватает.

СтолбецТипЗначение
chat_idinteger, not null → chats.idchat, к которому это относится
from_keyinteger, not nullНаименьший ключ порядка источника (id сообщения Telegram) отрезка чата, который хранится полностью; сообщения, которых внутри него нет, были удалены.
to_keyinteger, not nullНаибольший ключ порядка источника этого полного отрезка; сообщения вне всех отрезков просто ещё не загружались.
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

fetch_leases

Кто сейчас загружает отрезок чата, чтобы два процесса не загружали одни и те же страницы.

СтолбецТипЗначение
chat_idinteger, not null → chats.idchat, к которому это относится
anchortext, not nullКакой отрезок или какая работа чата загружается, в виде метки, например gaps, чтобы два процесса никогда не загружали одни и те же страницы.
holdertext, not nullСлучайный id (UUID) процесса, который сейчас держит аренду.
expires_atinteger, not nullКогда аренда истекает (миллисекунды эпохи); после этого отрезок может взять другой держатель.

bot_updates

Каждое обновление, которое аккаунт бота получил через Bot API, в том виде, в каком пришло, чтобы его можно было изучить и воспроизвести.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт бота
external_idtext, not nullid обновления
kindtext, not nullсообщение, нажатие кнопки, изменение участников, …
payloadtext, not nullJSON, точно как получен
received_atinteger, not nullкогда пришло обновление
handled_atintegernull, пока не обработано
errortextпочему обработка не удалась
replayed_atintegerкогда его последний раз воспроизводили
created_atinteger, not nullкогда эта строка была сохранена здесь

syncs

Один запуск синхронизации интеграции: что он сделал и чем закончился.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
kindtext, not nullfull, incremental, import
started_atinteger, not nullкогда запуск начался
finished_atintegerкогда он закончился; null, пока он идёт
statustext, not nullrunning, succeeded, failed
countstextJSON: created, updated, deleted, skipped
errortextчто остановило неудачный запуск, как сообщил импортёр
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

Люди, организации и боты

Люди, их учётные записи в каждом источнике, организации, к которым они относятся, и агенты, которые здесь работают.

persons

Один человек. У каждого человека есть учётные записи в источниках; один человек — владелец хранилища.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
nametextОтображаемое имя человека (берётся из имени его первой учётной записи); NULL, если неизвестно.
ownerinteger, not null1 для человека, который владеет хранилищем; новое хранилище создаёт эту строку, поэтому она есть всегда.
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

identities

Как один источник знает человека: пользователь мессенджера, адрес почты, участник встречи.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
providertext, not nullМессенджер, в котором живёт учётная запись (telegram, max или любая строка, например email); одна учётная запись на источник, а не на аккаунт.
external_idtext, not nullсобственный id источника для него
usernametextПубличный ник человека в этом мессенджере, без @; NULL, если ника нет.
nametextТекущее отображаемое имя человека, как его последний раз сообщил мессенджер.
botinteger1, если мессенджер говорит, что эта учётная запись — бот, 0, если говорит, что нет, NULL, если неизвестно.
phone_hmactextХеш с ключом (HMAC) номера телефона человека, чтобы находить одного и того же человека в разных мессенджерах; сам номер никогда не попадает в журнал, фикстуру или документ. Пока его не заполняет ни один инструмент.
metadatatextJSON: то, что присылает источник, для чего нет своего столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
descriptiontextЧто они написали о себе.

К какому человеку относится учётная запись, как это решено и насколько это точно.

СтолбецТипЗначение
identity_idinteger, PK → identities.idучётная запись, которую связывают; одна связь на учётную запись
person_idinteger, not null → persons.idperson, к которому это относится
methodtext, not nullКак учётная запись была отнесена к своему человеку: initial при первой встрече или метка вызывающего кода, например manual или same-email.
confidencereal, not nullНасколько точна связь, от 0 до 1; 1 для каждой связи, которую пишет хранилище.
created_atinteger, not nullкогда эта строка была сохранена здесь
authortext, not nullКто решил связь: ingest для автоматической первой связи, иначе owner или имя программы.
sourcetextкакая интеграция или какой импортёр это предложил
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

Каждый перенос учётной записи от одного человека к другому, чтобы неверную связь можно было отменить.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
identity_idinteger, not null → identities.ididentity, к которой это относится
from_person_idinteger → persons.idperson, к которому это относится
to_person_idinteger, not null → persons.idperson, к которому это относится
methodtext, not nullКак решён этот перенос учётной записи между людьми (initial, manual, same-email…); хранится, чтобы плохую связь можно было отменить.
created_atinteger, not nullкогда эта строка была сохранена здесь
authortext, not nullКто сделал этот перенос: ingest, owner или имя программы.

identity_revisions

Каждый профиль, с которым видели человека, — новая строка, когда он отличается от предыдущего; в identities хранится последний.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
identity_idinteger, not null → identities.ididentity, к которой это относится
nametextОтображаемое имя человека в этом снимке профиля (строка добавляется только при изменении профиля).
usernametextНик (без @) человека в этом снимке.
descriptiontextТекст, который человек написал о себе, в этом снимке.
markstextJSON: отметки мессенджера — бот, мошенник, фейк, удалён, есть фото — где он их дал.
created_atinteger, not nullкогда эта строка была сохранена здесь

account_identities

Какие учётные записи видит каждый аккаунт и когда он последний раз общался с каждой один на один.

СтолбецТипЗначение
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
identity_idinteger, not null → identities.ididentity, к которой это относится
created_atinteger, not nullкогда эта строка была сохранена здесь
last_messaged_atintegerСамое новое сообщение их личного чата, как его последний раз вычислил refreshRecency: порядок контактов.
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

aliases

Другие имена для чего угодно: прозвище человека, имя контакта в одном мессенджере, короткое имя проекта. Их читают поиск и сопоставление имён.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
aliasable_typetext, not nullчто называет псевдоним: identity, person, organization, chat, project, bot, …
aliasable_idinteger, not nullстрока в таблице aliasable_type
account_idinteger → accounts.idзадан, когда псевдоним действует только с точки зрения одного аккаунта, как имя контакта в одном мессенджере; null, когда он действует везде
nametext, not nullпсевдоним, как написан: прозвище, короткое имя, прежнее имя
name_foldedtext, not nullname, приведённое для сопоставления: строчные буквы, без диакритики
displayinteger, not null1 для псевдонима, который показывают вместо настоящего имени; не больше одного на вещь и аккаунт
sourcetext, not nullвладелец, агент, импорт
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

identities_fts

Полнотекстовый индекс по name, username.

organizations

Компания, команда, семья или сообщество, с которыми имеет дело владелец. Проекты, рабочие аккаунты и учётные записи людей относятся к одной из них.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
kindtext, not nullcompany, team, family, community
nametext, not nullназвание организации
scopetext, not nullpersonal или work; что запрос контекста может читать для данной цели, чтобы рабочий вопрос никогда не затягивал личные чаты
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

bots

ИИ-агент или скрипт, который здесь работает: он регистрируется сам, владеет проектами и задачами, получает работу, пишет заметки. Бот мессенджера (бот Telegram) — это учётная запись, а не это.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
nametext, not null, uniqueник, например zm-puller
kindtext, not nullagent, script, integration
descriptiontextдля чего бот, одним предложением, написано при регистрации
owner_person_idinteger → persons.idкто его запускает
modeltextмодель, на которой он работает, если это агент
token_digesttextхеш его токена доступа, для случая, когда боты проходят аутентификацию
last_seen_atintegerпоследний раз, когда бот что-то здесь делал
disabled_atintegerзадаётся, когда владелец выключает бота; бот сохраняет то, чем владеет
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

Чаты и сообщения

Что приносят мессенджеры.

chats

Место для общения в одном аккаунте: диалог, группа, канал или избранное. Чаты могут быть вложенными.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
external_idtext, not nullсобственный id источника для него
kindtext, not nullТип чата: dialog (один на один), group, channel, saved (заметки для себя) или unknown.
titletextНазвание чата, как его показывает мессенджер; NULL, если названия нет.
unread_countintegerНепрочитанные сообщения, о которых сообщил мессенджер; NULL значит, что мессенджер не сообщил, и это не ноль.
last_message_atintegerВремя самого нового сообщения чата (миллисекунды эпохи); NULL, если сообщений никогда не было.
participants_countintegerЧисло участников, которое сообщил мессенджер; NULL, если не дано.
metadatatextJSON: то, что присылает источник, для чего нет своего столбца и по чему нет поиска
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
usernametextПубличный ник чата (без @), если он есть, например у публичного канала.
membership_statetextNULL — неизвестно. Доступность для поиска из этого не следует: покинутый чат сохраняет свои сообщения.
searchableinteger, not null1, если сообщения чата попадают в поиски, которые его не называют, 0, если их видит только поиск, который называет этот чат.
message_countinteger, not nullПоддерживается триггерами, чтобы запрос мог выбрать, как фильтр доходит до индекса.
members_tracked_atintegerКогда владелец попросил serve загружать список участников каждый день; NULL, если не отслеживается.
descriptiontextописание чата, как его даёт мессенджер
details_fetched_atintegerкогда последний раз читались название, ник и описание
created_atinteger, not nullкогда эта строка была сохранена здесь
parent_chat_idinteger → chats.idчат, внутри которого находится этот: тема форума в своей группе, группа обсуждения канала, канал в рабочем пространстве
scopetextзаменяет область аккаунта для этого чата; null берёт область аккаунта

chat_members

Кто есть в чате, как аккаунт видел это последний раз: список заменяет состав чата целиком.

СтолбецТипЗначение
chat_idinteger, not null → chats.idchat, к которому это относится
identity_idinteger, not null → identities.ididentity, к которой это относится
created_atinteger, not nullкогда эта строка была сохранена здесь

member_stays

Одно пребывание человека в группе, по спискам участников, прочитанным целиком или частично. Возвращение после выхода — новая строка. gone_at задаётся только по списку, прочитанному целиком: обрезанный список ничего не говорит о том, кого нет.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
chat_idinteger, not null → chats.idchat, к которому это относится
identity_idinteger, not null → identities.ididentity, к которой это относится
first_seen_atinteger, not nullКогда чтение списка участников впервые показало человека в группе для этого пребывания (миллисекунды эпохи).
last_seen_atinteger, not nullКогда чтение списка участников последний раз показало человека в группе (миллисекунды эпохи).
joined_atintegerКогда, по словам мессенджера, человек вступил; NULL, где мессенджер этого не говорит.
invited_by_identity_idinteger → identities.ididentity, к которой это относится
roletextРоль человека в группе при последнем чтении: owner, admin или member; NULL, если не дана.
left_atintegerКогда в полном списке участников впервые не оказалось человека, что закрывает это пребывание (миллисекунды эпохи); NULL, пока он ещё в группе.
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

member_counts

Размер группы раз в день: собственный счётчик мессенджера и сколько участников перечислило одно чтение.

СтолбецТипЗначение
chat_idinteger, not null → chats.idchat, к которому это относится
datetext, not nullYYYY-MM-DD, UTC; в SQLite нет типа даты
reported_countintegerчисло, которое сообщает мессенджер
listed_countinteger, not nullсколько участников на самом деле вернуло одно чтение
complete_listinteger, not nullвернуло ли это чтение весь список
created_atinteger, not nullкогда эта строка была сохранена здесь

messages

Одно сообщение в чате, с его текстом, отправителем, ответом и веткой.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
chat_idinteger, not null → chats.idchat, к которому это относится
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
external_idtext, not nullсобственный id источника для него
thread_external_idtextId мессенджера для темы или ветки внутри чата, к которой относится сообщение, если в мессенджере есть ветки.
sender_identity_idinteger → identities.ididentity, к которой это относится
sender_chat_external_idtextId мессенджера для чата, который написал сообщение (пост канала или сообщение от имени группы); NULL для человека.
sender_nametextОтображаемое имя отправителя, как оно указано в этом сообщении.
sent_atinteger, not nullКогда сообщение было отправлено (миллисекунды эпохи).
edited_atintegerКогда мессенджер последний раз записал правку (миллисекунды эпохи); NULL для сообщения, которое никогда не менялось.
deleted_atintegerкогда оно исчезло в источнике; строка остаётся, синхронизация никогда не удаляет строки физически
texttext, not nullТекст сообщения точно в том виде, как получен (пустая строка, если текста нет или он удалён).
reply_to_external_idtextId мессенджера для сообщения, на которое отвечает это, даже если его копия не пришла.
reply_totextJSON-копия сообщения, на которое ответили, как её прислал мессенджер (id, отправитель, время, текст, вложения); NULL, если не прислана.
forwardtextJSON-копия исходного сообщения, которое пересылает это (та же форма, что у reply_to); NULL, если это не пересылка.
outgoinginteger1, если его отправил этот аккаунт, 0, если кто-то другой, NULL, если собственная учётная запись аккаунта неизвестна.
reactionstextJSON реакций: счётчики по каждой реакции, собственная реакция этого аккаунта и итог; NULL, если ни разу не запрашивали.
metadatatextJSON: то, что присылает источник, для чего нет своего столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
sourcetext, not nullКак сообщение попало в хранилище, например history, send, backfill, update, context; только для диагностики.
normalized_texttextтекст, приведённый для поиска: строчные буквы, без диакритики; заполняет индексатор
normalizer_versionintegerВерсия правил нормализации текста, которые создали поисковый текст сообщения (сейчас 1); NULL, пока не нормализован.
mentionstextJSON: люди, которых текст упоминает по id, где это говорит мессенджер; @handle читаются из текста.
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
thread_root_idinteger → messages.idсообщение, от которого идёт ветка: ветка Slack, комментарии под постом канала, ответы в теме; null вне ветки

message_revisions

Прежние тексты сообщения, одна строка на правку.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
message_idinteger, not null → messages.idmessage, к которому это относится
texttext, not nullТекст сообщения до того, как правка его заменила.
edited_atintegerВремя правки (миллисекунды эпохи), которое было у сообщения, пока у него был этот старый текст; NULL, если до этого его не правили.
created_atinteger, not nullкогда эта строка была сохранена здесь

Каждый кандидат на «более раннее сообщение, на которое отвечает это», и откуда он взялся.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
chat_idinteger, not null → chats.idЧат сообщения; хранится здесь, чтобы пересборка находила и удаляла старую сборку, не читая messages.
message_idinteger, not null → messages.idmessage, к которому это относится
parent_idinteger → messages.idNULL: источник говорит, что это сообщение начинает обсуждение.
sourcetext, not nullКто предложил этого родителя для сообщения: provider (собственный ответ мессенджера), rule или agent.
kindtext, not nullПочему сообщение связано с родителем: reply, mention, same_sender (правила/источник) или answer (выбор агента).
confidencereal, not nullНасколько источник уверен, что это верный родитель, от 0 до 1.
methodtext, not nullИмя правила или модель агента.
versiontextДля связей от источника и правил — версия правил связывания, которая её записала (в виде текста); для связей агента — версия навыка агента, если дана.
batchtextКакой пакет агента это записал.
buildintegerПересборка, которая записала связь от источника или правила; NULL для связи агента, которая переживает пересборки.
created_atinteger, not nullкогда эта строка была сохранена здесь
stale_atintegerОдин из концов связи изменился после её записи; не выбирается, пока не спросят снова.
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

message_counter_observations

Последнее значение счётчика просмотров, реакций или комментариев сообщения.

СтолбецТипЗначение
message_idinteger, not null → messages.idmessage, к которому это относится
countertext, not nullКакой счётчик наблюдали: views, reactions или comments.
valuereal, not nullЗначение счётчика при наблюдении (неотрицательное число).
created_atinteger, not nullкогда эта строка была сохранена здесь
sourcetext, not nullКак его наблюдали: remote_fetch (хранилище спросило) или remote_update (мессенджер прислал изменение).

message_transcripts

Что сказало голосовое сообщение, по ключу из чата и id сообщения, а не по строке сообщения: сообщение можно прослушать раньше, чем хранилище его получит. Производные данные — его можно прослушать снова.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
message_idinteger → messages.idзадаётся, когда сообщение сохранено
chat_idinteger, not null → chats.idchat, к которому это относится
message_external_idtext, not nullId мессенджера для расшифрованного голосового сообщения; ключ такой, потому что сообщение можно прослушать раньше, чем оно сохранено.
texttext, not nullЧто сказало голосовое сообщение, по расшифровке; никогда не пусто.
sourcetext, not nullМодель или мессенджер, которые его прослушали.
heard_atinteger, not nullКогда сделана расшифровка (миллисекунды эпохи).
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

chats_fts

Полнотекстовый индекс по title.

messages_fts

Полнотекстовый индекс по text.

message_words

Полнотекстовый индекс по normalized_text, scope.

message_stems

Полнотекстовый индекс по stems, scope.

message_stems_pending

Сообщения с устаревшими основами. Её заполняют триггеры, потому что SQL не умеет выделять основы; очищает JS.

СтолбецТипЗначение
idinteger, PKПервичный ключ.

member_observations

Одно чтение списка участников чата: когда, был ли это весь список, сколько он сообщил и сколько вернул. По ним хранение данных узнаёт, кто был в группе на контрольную точку.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
chat_idinteger, not null → chats.idchat, к которому это относится
observed_atinteger, not nullкогда прочитали список участников
started_atintegerкогда началось чтение, для списка, прочитанного по страницам
completeinteger, not null1, когда чтение вернуло весь список, поэтому участника, которого в нём нет, больше нет в группе
reported_countintegerчисло, которое сообщает мессенджер
listed_countinteger, not nullсколько участников вернуло это чтение
sourcetext, not nullчто его прочитало: синхронизация, команда, импорт
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

member_observation_members

Кого видело одно чтение списка участников.

СтолбецТипЗначение
member_observation_idinteger, not null → member_observations.idmember_observation, к которому это относится
identity_idinteger, not null → identities.ididentity, к которой это относится
member_stay_idinteger, not null → member_stays.idпребывание, в котором был участник, когда его видели

Почта

У почты свои таблицы: цепочки, темы, получатели, почтовые ящики.

email_threads

Обсуждение по почте.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
external_idtext, not nullid цепочки Gmail или вычислен из References
subjecttextтема первого письма цепочки, без Re: и Fwd:
last_email_atintegerкогда было отправлено самое новое письмо цепочки
emails_countinteger, not nullсколько писем цепочки хранится
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

emails

Одно письмо.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
email_thread_idinteger, not null → email_threads.idemail_thread, к которой это относится
external_idtext, not nullзаголовок Message-ID
subjecttextзаголовок Subject, декодированный
from_identity_idinteger → identities.ididentity, к которой это относится
from_addresstextадрес отправителя, строчными буквами
from_nametextотображаемое имя отправителя, как написано в From
sent_atintegerзаголовок Date
received_atintegerкогда его получил почтовый ящик владельца
in_reply_totextMessage-ID письма, на которое отвечает это
referencestextJSON: заголовок References
body_texttextтекстовое тело; берётся из HTML, если у письма нет текстовой части
body_htmltextтело HTML, как получено
snippettextпервые слова тела, для списков
outgoinginteger1, когда его отправил владелец
readinteger1, когда оно помечено прочитанным в почтовом ящике
flaggedinteger1, когда оно помечено флажком или звёздочкой
draftinteger1 для черновика, который ещё не отправлен
sizeintegerразмер исходного письма, в байтах
headerstextJSON: все заголовки, как получены
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

email_recipients

Один адрес в письме.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
email_idinteger, not null → emails.idemail, к которому это относится
identity_idinteger → identities.ididentity, к которой это относится
addresstext, not nullадрес, строчными буквами
nametextотображаемое имя, написанное рядом с ним
roletext, not nullto, cc, bcc, reply_to
positioninteger, not nullпорядок внутри родителя, с 0
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

mailboxes

Папка IMAP или ярлык Gmail. Собственные метки владельца — это отметки.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
external_idtext, not nullсобственный id источника для него
nametext, not nullимя папки или ярлыка, как его показывает источник
kindtextinbox, sent, archive, label, folder
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

email_mailboxes

В каких почтовых ящиках лежит письмо; Gmail кладёт одно письмо в несколько.

СтолбецТипЗначение
email_idinteger, not null → emails.idemail, к которому это относится
mailbox_idinteger, not null → mailboxes.idmailboxe, к которому это относится
created_atinteger, not nullкогда эта строка была сохранена здесь

email_words

Полнотекстовый индекс по normalized_text, scope.

email_stems

Полнотекстовый индекс по stems, scope.

email_index_pending

Строки писем, ждущие индексации: триггеры ставят в очередь, JS нормализует, выделяет основы и пишет индекс.

СтолбецТипЗначение
idinteger, not nullСтрока, ждущая индексации.
indexable_typetext, not nullк какой таблице относится ждущая строка

Вложения

Файлы сообщения, письма или встречи, с извлечённым из них текстом.

attachments

Файл сообщения, письма или встречи, с извлечённым из него текстом.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
attachable_typetext, not nullmessage, email или meeting
attachable_idinteger, not nullстрока в таблице attachable_type
positioninteger, not nullпорядок внутри родителя, с 0
kindtext, not nullТип вложения строчными буквами, например photo, video, file, voice, sticker, share.
mimetextMIME-тип файла, если мессенджер его даёт.
nametextИмя файла, если мессенджер его даёт.
titletextЗаголовок веб-страницы, которой поделились (для вложений share).
urltextСсылка, которая открывает байты или страницу, которой поделились, если мессенджер её выдаёт.
sizeintegerРазмер файла в байтах, если мессенджер его даёт.
widthintegerШирина в пикселях для изображений и видео.
heightintegerВысота в пикселях для изображений и видео.
durationrealДлительность в секундах для аудио и видео.
provider_reftextНепрозрачный JSON, который нужен адаптеру мессенджера, чтобы позже загрузить байты; вне этого адаптера он ничего не значит.
local_pathtextКуда messages download сохранил файл на этом компьютере; NULL, если его ни разу не загружали.
texttextтекст, извлечённый из файла
normalized_texttextтекст, приведённый для поиска: строчные буквы, без диакритики; заполняет индексатор
extractiontextкак получен текст: text, ocr, agent, failed
extractortextЧто создало текст вложения: plain, docx:[email protected], pdf:[email protected], движок OCR или собственная метка агента.
extraction_errortextКороткий код причины, почему текст не получен (например no_text), никогда не строка файла; NULL при успехе.
content_sha256textШестнадцатеричный SHA-256 прочитанных байтов файла, чтобы не читать неизменённый файл снова.
extracted_atintegerКогда был записан текст вложения (или его ошибка) (миллисекунды эпохи).
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

attachment_words

Полнотекстовый индекс по normalized_text.

Документы, заметки и воспоминания

Документы — это самостоятельные файлы и страницы; заметки — то, что человек написал о чём-то; воспоминания — то, к какому выводу пришёл агент.

documents

Самостоятельный файл или страница: файл в папке заметок (md, pdf, docx, xlsx…), страница Notion, файл Drive, вики-страница memo.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idпапка, Notion, Drive
external_idtext, not nullпуть в папке, id страницы, id файла
kindtext, not nullfile, page, wiki
titletextиз front matter, первого заголовка или имени файла, в этом порядке
locationtextгде он лежит в своём источнике: путь папки, папка Drive, родительская страница Notion
file_nametextс расширением; null для страницы, которая не файл
extensiontextстрочными буквами, без точки: md, pdf, docx
urltextгде его открывает источник
storagetextlocal, drive, notion, web — где лежат байты
local_pathtextкопия на этой машине, если её хранят; для локальной папки — сам файл
mimetextтип данных, например application/pdf
sizeintegerразмер файла, в байтах
content_hashtextхеш байтов файла; неизменённый файл не читается снова
front_mattertextJSON, как написано в файле
bodytextMarkdown как есть; извлечённый текст для PDF и Office; OCR для сканов
normalized_texttextтекст, приведённый для поиска: строчные буквы, без диакритики; заполняет индексатор
extractiontextnone, text, ocr, agent, failed
extraction_errortextпочему текст не удалось извлечь
languagetextопределённый язык тела, кодом ISO 639-1
revisioninteger, not nullсчётчик правок, растёт при каждом изменении; правка называет версию, которую прочитала, поэтому два автора не могут перезаписать друг друга
export_pathtextкуда его экспортировал memo, для вики-страниц
external_created_atintegerкогда, по словам источника, он создан
external_updated_atintegerкогда, по словам источника, он последний раз изменился
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

document_revisions

Прежние тела документа.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
document_idinteger, not null → documents.iddocument, к которому это относится
bodytext, not nullтело, каким оно было до этой версии
revisioninteger, not nullсчётчик правок, растёт при каждом изменении; правка называет версию, которую прочитала, поэтому два автора не могут перезаписать друг друга
created_atinteger, not nullкогда эта строка была сохранена здесь

document_words

Полнотекстовый индекс по normalized_text, scope.

document_stems

Полнотекстовый индекс по stems, scope.

document_index_pending

Строки документов, ждущие индексации: триггеры ставят в очередь, JS нормализует, выделяет основы и пишет индекс.

СтолбецТипЗначение
idinteger, not nullСтрока, ждущая индексации.
indexable_typetext, not nullк какой таблице относится ждущая строка

notes

Короткий текст о чём-то: о человеке, чате, событии, документе, задаче. Комментарии к задаче — тоже заметки.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
notable_typetext, not nullо чём заметка: person, chat, event, document, task, …; заметка ни о чём конкретном — о человеке владельца
notable_idinteger, not nullстрока в таблице notable_type
titletextнеобязательный заголовок
bodytext, not nullзаметка, в Markdown
author_typetextperson или bot
author_idintegerстрока в таблице author_type
revisioninteger, not nullсчётчик правок, растёт при каждом изменении; правка называет версию, которую прочитала, поэтому два автора не могут перезаписать друг друга
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

note_revisions

Прежние тела заметки.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
note_idinteger, not null → notes.idnote, к которой это относится
bodytext, not nullтело, каким оно было до этой версии
revisioninteger, not nullсчётчик правок, растёт при каждом изменении; правка называет версию, которую прочитала, поэтому два автора не могут перезаписать друг друга
created_atinteger, not nullкогда эта строка была сохранена здесь

note_words

Полнотекстовый индекс по normalized_text, scope.

note_stems

Полнотекстовый индекс по stems, scope.

note_index_pending

Строки заметок, ждущие индексации: триггеры ставят в очередь, JS нормализует, выделяет основы и пишет индекс.

СтолбецТипЗначение
idinteger, not nullСтрока, ждущая индексации.
indexable_typetext, not nullк какой таблице относится ждущая строка

memories

К какому выводу пришёл агент: сводка, дневной дайджест, факт, предпочтение. Это производные данные, и они могут ошибаться, поэтому несут свои доказательства (links вида evidence), уверенность и статус; заметки — то, что написал человек.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
kindtext, not nullsummary, digest, fact, preference
bodytext, not nullтекст воспоминания
subject_typetextо чём оно: person, chat, meeting, project, …; null для общего факта
subject_idintegerстрока в таблице subject_type
author_typetext, not nullperson или bot
author_idinteger, not nullстрока в таблице author_type
modeltextмодель, которая его написала
confidencereal0–1, как оценил автор
statustext, not nullproposed, confirmed, stale, superseded
last_verified_atintegerкогда его доказательства последний раз проверялись
supersedes_idinteger → memories.idвоспоминание, которое заменяет это
scopetext, not nullpersonal или work, по его доказательствам
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

memory_words

Полнотекстовый индекс по normalized_text, scope.

memory_stems

Полнотекстовый индекс по stems, scope.

memory_index_pending

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

СтолбецТипЗначение
idinteger, not nullСтрока, ждущая индексации.
indexable_typetext, not nullк какой таблице относится ждущая строка

События и встречи

События владельца и запись о них в каждом приложении для встреч.

event_series

Повторяющееся событие, над повторением любого отдельного источника.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
titletextкак называется повторяющееся событие, например еженедельная синхронизация
recurrencetextRRULE, если известно
origintext, not nullauto или owner
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

events

Собственная запись владельца о том, что произошло, как человек — это запись владельца о ком-то. Не содержит полей источника: источники ссылаются на неё.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
event_series_idinteger → event_series.idevent_sery, к которой это относится
titletextназвание владельца; заполняется из первой связанной встречи или записи календаря
descriptiontextо чём событие
locationtextгде оно проходит: адрес, комната или онлайн
starts_atintegerкогда оно начинается
ends_atintegerкогда оно заканчивается
timezonetextзона IANA, в которой проходит событие, например Europe/Madrid
origintext, not nullauto или owner
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

meeting_series

Повторяющаяся или запланированная встреча источника.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
external_idtext, not nullid встречи Zoom
event_series_idinteger → event_series.idevent_sery, к которой это относится
titletextтема встречи у источника
descriptiontextповестка, как её хранит источник
kindtextinstant, scheduled, recurring
recurrencetextJSON, правило источника
host_identity_idinteger → identities.ididentity, к которой это относится
join_urltextссылка для входа; по ней же потом находится запись календаря
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

meetings

Одно проведение: оно было один раз, в одно время.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
meeting_series_idinteger → meeting_series.idmeeting_sery, к которой это относится
event_idinteger → events.idзадаётся связыванием событий
external_idtext, not nullUUID проведения Zoom
titletextтема этого проведения
descriptiontextповестка; её же несёт запись календаря
locationtextфизическая комната, если источник её называет
join_urltextссылка для входа на это проведение
started_atintegerкогда проведение началось
ended_atintegerкогда оно закончилось
duration_msintegerсколько оно длилось, в миллисекундах
timezonetextзона IANA, которую источник даёт для встречи
host_identity_idinteger → identities.ididentity, к которой это относится
participants_countintegerсколько человек участвовало, как сообщает источник
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

meeting_participants

Один человек на одной встрече, каким его видела эта встреча; имя и почта остаются с этой встречей.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
meeting_idinteger, not null → meetings.idmeeting, к которой это относится
identity_idinteger, not null → identities.idключ — id пользователя у источника, иначе почта, иначе name@meeting
display_nametextимя, показанное на этой встрече; позднее переименование его не меняет
emailtextадрес, который даёт источник, если даёт
roletexthost, co-host, attendee, panelist, guest
joined_atintegerпервый вход
left_atintegerпоследний выход
duration_msintegerвремя на встрече, суммарно по всем входам
sessionstextJSON: каждый вход и выход
external_idtextсобственный id источника для него
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

meeting_transcripts

Одна расшифровка встречи: от Zoom, от бота или исправленная версия, которая заменяет старую.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
meeting_idinteger, not null → meetings.idmeeting, к которой это относится
sourcetext, not nullapi, file, fireflies
formattextvtt
languagetextязык расшифровки, кодом ISO 639-1
content_hashtextодин и тот же файл, импортированный дважды, пропускается
external_created_atintegerкогда, по словам источника, он создан
superseded_atintegerзадаётся, когда более новая расшифровка той же встречи заменяет эту; эта сохраняется
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

meeting_transcript_rows

Одна строка расшифровки (реплика WebVTT): кто говорил, когда и что сказал.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
meeting_transcript_idinteger, not null → meeting_transcripts.idmeeting_transcript, к которой это относится
positioninteger, not nullпорядок внутри родителя, с 0
start_msinteger, not nullкогда начинается строка, в миллисекундах от начала записи
end_msinteger, not nullкогда оно заканчивается
speaker_participant_idinteger → meeting_participants.idучастник, чьё имя даёт реплика, найденный внутри встречи; null, если не подходит ровно один участник
speaker_nametextкак написано в реплике
texttext, not nullчто было сказано, без префикса говорящего
normalized_texttextтекст, приведённый для поиска: строчные буквы, без диакритики; заполняет индексатор
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь

meeting_chat_messages

Чат внутри встречи.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
meeting_idinteger, not null → meetings.idmeeting, к которой это относится
external_idtextсобственный id источника для него
sent_atinteger, not nullкогда оно отправлено
sender_participant_idinteger → meeting_participants.idmeeting_participant, к которому это относится
sender_nametextимя отправителя, как его показывает чат
recipienttextвсем или имя для личного сообщения
texttext, not nullсообщение
normalized_texttextтекст, приведённый для поиска: строчные буквы, без диакритики; заполняет индексатор
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

meeting_summaries

Сводка встречи — от ИИ источника, от бота, от владельца или от запуска LLM здесь.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
meeting_idinteger, not null → meetings.idmeeting, к которой это относится
sourcetext, not nullzoom-ai, fireflies, owner, llm
titletextсобственный заголовок сводки
overviewtextкороткий абзац сводки
sectionstextJSON: [{label, text}]
next_stepstextJSON: [text]
contenttextполный текст источника
doc_urltextссылка на сводку у источника, если она есть
external_created_atintegerкогда, по словам источника, он создан
external_updated_atintegerкогда, по словам источника, он последний раз изменился
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

meeting_words

Полнотекстовый индекс по normalized_text, scope.

meeting_stems

Полнотекстовый индекс по stems, scope.

meeting_index_pending

Строки строк расшифровки, чата встречи и сводок, ждущие индексации: триггеры ставят в очередь, JS нормализует, выделяет основы и пишет индекс.

СтолбецТипЗначение
idinteger, not nullСтрока, ждущая индексации.
indexable_typetext, not nullк какой таблице относится ждущая строка

Задачи

Система задач и тикетов для людей и агентов: проекты с ключами, задачи с типами и приоритетами, исполнители — люди или боты, и решения, принятые по ходу. Обещание — это задача типа promise; вопрос — задача типа question, ответ на которую связан с ней.

reminders

Когда напомнить кому-то о задаче и было ли напоминание доставлено.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
task_idinteger, not null → tasks.idtask, к которой это относится
account_idinteger, not null → accounts.idаккаунт интеграции, через который это пришло
due_atinteger, not nullКогда должно сработать напоминание (миллисекунды эпохи).
timezonetext, not nullЧасовой пояс IANA, в котором запланировано напоминание, например Europe/Madrid.
statetext, not nullСостояние доставки: pending, leased (занято доставщиком), delivered или cancelled.
revisioninteger, not nullсчётчик правок, растёт при каждом изменении; правка называет версию, которую прочитала, поэтому два автора не могут перезаписать друг друга
lease_untilintegerДо какого времени текущий доставщик держит напоминание (миллисекунды эпохи); после этого его может занять другой. NULL, если не занято.
receipttextId, выданный доставщику при захвате, который он должен вернуть, чтобы подтвердить доставку; NULL, если не занято.
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

projects

Место для задач, с коротким ключом, с которого начинаются их id (MEET-12).

СтолбецТипЗначение
idinteger, PKПервичный ключ.
keytext, not null, uniqueзаглавными буквами, например MEET
nametext, not nullназвание проекта
descriptiontextдля чего проект
typetext, not nullwork, client, personal, oss, other; метки группируют проекты сверх этого
organization_idinteger → organizations.idorganization, к которой это относится
account_idinteger → accounts.idаккаунт, для которого проект-входящие собирает задачи; null для собственного проекта владельца
scopetext, not nullpersonal или work; что запрос контекста может читать для данной цели, чтобы рабочий вопрос никогда не затягивал личные чаты
owner_typetextperson или bot
owner_idintegerстрока в таблице owner_type
tasks_countinteger, not nullпоследний выданный номер; следующая задача берёт +1 в той же транзакции
statustextactive, archived
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

tasks

Задача или тикет, одинаково для людей и агентов. Всё, чего она касается, — люди, сообщения, письма, документы, встречи — это строка links; её метки — это отметки; её комментарии — это заметки.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
project_idinteger, not null → projects.idproject, к которому это относится
numberinteger, not nullвнутри проекта
keytext, not null, unique&lt;project key>-&lt;number>, id, которым пользуются люди и агенты
titletext, not nullодна строка о том, что нужно сделать
descriptiontextподробности, в Markdown
typetext, not nullbug, feature, chore, question, request, mention, promise — последние четыре пакет задач находит в сообщениях
statustext, not nullopen, in_progress, blocked, done, dismissed
priorityinteger0 срочно … 4 низкий
parent_idinteger → tasks.idродитель подзадачи
due_atintegerкогда она должна быть сделана
started_atintegerкогда кто-то её начал (статус in_progress)
closed_atintegerкогда она стала done или dismissed
closed_by_typetextperson или bot
closed_by_idintegerстрока в таблице closed_by_type
close_reasontextпочему её закрыли, в нескольких словах, например no-reply-needed
author_typetext, not nullperson или bot
author_idinteger, not nullстрока в таблице author_type
sourcetext, not nullвладелец, агент, правило или импорт
package_idtext, uniqueсобственный id задачи в пакете задач; null для задачи, созданной здесь
source_locatortextиз чего возникла задача: указатель на сообщение, никогда не его текст
source_kindtextвид того, что называет source_locator
source_grouptextгруппа, в которую её относит пакет задач
resolutiontextчем она закончилась, словами; ответ на вопрос. Откуда пришёл ответ — это строка links вида answered-by
verdicttextuseful или not_useful, как владелец оценил задачу, поднятую агентом; null, пока не оценена
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

task_assignments

Кто работает над задачей: человек или бот, в какой-то роли.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
task_idinteger, not null → tasks.idtask, к которой это относится
assignee_typetext, not nullperson или bot
assignee_idinteger, not nullстрока в таблице assignee_type
roletext, not nullassignee, reviewer, watcher
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

task_events

История задачи: создана, изменён статус, назначена, перенесён срок.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
task_idinteger, not null → tasks.idtask, к которой это относится
actor_typetextperson или bot
actor_idintegerстрока в таблице actor_type
kindtext, not nullчто произошло: created, status, assigned, unassigned, due, priority, moved
changestextJSON: {field: [from, to]}
created_atinteger, not nullкогда эта строка была сохранена здесь

decisions

Сделанный выбор, который действует, пока его не заменят: почему мы что-то делаем. Его доказательства — сообщение, строка расшифровки или документ — это links вида evidence; решение, предложенное агентом, ссылается обратно на то воспоминание через created-from.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
project_idinteger → projects.idproject, к которому это относится
statementtext, not nullрешение одним предложением
statustext, not nullproposed, accepted, superseded, reversed
decided_atintegerкогда оно принято, по доказательствам
supersedes_idinteger → decisions.idрешение, которое заменяет это
confirmed_by_typetextкто его принял; null, пока оно предложено
confirmed_by_idintegerкто его принял; null, пока оно предложено
sourcetext, not nullвладелец, агент, импорт
metadatatextJSON: то, что присылает источник, для чего нет столбца и по чему нет поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь
deleted_atintegerисчезло в источнике; синхронизация никогда не удаляет строки физически

Агенты и действия

Что предлагают агенты и что они сделали: каждое внешнее действие ждёт одобрения, каждый вызов инструмента записывается в журнал.

proposed_actions

То, что агент хочет сделать вне хранилища, — ответить, отправить, удалить, забанить, заглушить, закрепить, пригласить, создать задачу, — и что ждёт одобрения человека. Без одобрения ничего внешнего не происходит.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
kindtext, not nullreply, send, delete, ban, mute, pin, invite, create_task, …
account_idinteger → accounts.idаккаунт, который выполнит действие
target_typetextна что оно действует: message, chat, identity, task, …
target_idintegerстрока в таблице target_type
payloadtextJSON: что сделать, например текст ответа
reasontextпочему агент это предлагает
statustext, not nullproposed, approved, rejected, executed, failed
proposed_by_typetext, not nullperson или bot
proposed_by_idinteger, not nullстрока в таблице proposed_by_type
decided_by_typetextperson или bot
decided_by_idintegerстрока в таблице decided_by_type
decided_atintegerкогда его одобрили или отклонили
executed_atintegerкогда его выполнили
resulttextJSON: что вернул источник
errortextпочему оно не удалось, если не удалось
verdicttextuseful или not_useful, как владелец оценил предложение
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

agent_actions

Каждый инструмент, который агент вызвал через CLI или MCP: кто, какой инструмент, на каком уровне доступа, над чем. Журнал аудита того, что сделали агенты.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
actor_typetext, not nullperson или bot
actor_idinteger, not nullстрока в таблице actor_type
tooltext, not nullкоманда или инструмент MCP
tiertext, not nullread, draft, write-private, write-public, destructive, admin
target_typetextвид того, на что указывает target_id: имя таблицы в единственном числе
target_idintegerстрока в таблице target_type
statustext, not nullok, refused, failed
errortextпочему вызов не удался или был отклонён
started_atinteger, not nullкогда начался вызов
finished_atintegerкогда он закончился; null, пока он идёт
created_atinteger, not nullкогда эта строка была сохранена здесь

Метки, связи и сохранённые поиски

Метки и темы, связи между любыми двумя вещами и сохранённые поиски.

tags

Имя метки, хранится один раз. Какие вещи её несут — это taggings.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
nametext, not null, uniqueуникально среди меток и тем, поэтому одно имя никогда не бывает и тем и другим
kindtext, not nulltag: свободная метка; topic: о чём вещь, из короткого списка, который создаёт только владелец (агенты предлагают)
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

auto_tag_claims

Метки, которые правила ставят на чат, сопоставляя его название, ник и описание.

СтолбецТипЗначение
chat_idinteger, not null → chats.idchat, к которому это относится
tag_idinteger, not null → tags.idtag, к которой это относится
algorithmtext, not nullИмя и версия правил, которые назначили автоматическую метку, сейчас keywords-v1.
scorereal, not nullДоля полей метаданных чата (название, ник, описание), которые совпали с ключевыми словами метки: 1/3, 2/3 или 1; это не вероятность.
fieldstext, not nullJSON-список совпавших полей метаданных, из title, username, description.
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

Каждая связь между двумя вещами, которую не хранит ни один столбец. Виды: links-to (документ или заметка ссылается на что-то, как написано в нём), about (заметка или документ о человеке, проекте, …), member-of (чат или человек относится к проекту или организации), labelled (аккаунт папки → метка, путь подпапки в anchor, чтобы каждый документ в ней нёс метку), answered-by (задача-вопрос → сообщение, которое на неё ответило), evidence (решение или воспоминание → его источник), created-from (вещь → то, из чего она сделана). Виды, которые хранилище пока не пишет: duplicate-of (тот же вопрос, заданный снова), related-to, assigned-to.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
from_typetext, not nullвид того, на что указывает from_id: имя таблицы в единственном числе
from_idinteger, not nullстрока в таблице from_type
to_typetextвид того, на что указывает to_id: имя таблицы в единственном числе
to_idintegernull, пока связь ещё никого не называет
kindtext, not nullсвязь; виды перечислены в описании таблицы
anchortextЗаголовок или фрагмент блока внутри цели, на который указывает связь, например #Heading или #^block; NULL для всей цели.
sourcetext, not nullОткуда взялась связь: file (написана в файле заметки), owner (добавлена владельцем) или suggested (предложена, ещё не подтверждена).
target_texttextЦель, как написана (например имя в заметке); хранится, пока связь никого не называет; до 500 символов.
target_foldedtexttarget_text, приведённый по регистру и диакритике, без начального @; его сравнивают с новыми именами людей и псевдонимами, чтобы найти цель связи.
roletextРоль, которую называет отношение, например должность в связи member-of; свободный текст до 200 символов.
evidencetextСвободный текст о том, почему отношение верно (до 2000 символов).
metadatatextJSON: то, что присылает источник, для чего нет своего столбца и по чему нет поиска
confirmedinteger, not null1, если владелец подтверждает связь, 0, если это только слабое, неподтверждённое предложение.
created_atinteger, not nullкогда эта строка была сохранена здесь
authortextowner, agent, rule
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

searches

Каждый запуск search messages и stats messages show, с параметрами, как их дал вызывающий, — никогда не сообщение и не результат. Строка с именем — это сохранённый поиск; одинаковый безымянный запуск считается в своей строке.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
nametext, uniqueУникальное имя сохранённого поиска; NULL для безымянного запуска, который хранится только как история.
commandtext, not nullsearch или stats.
paramstext, not nullJSON, ключи отсортированы, поэтому один и тот же запуск даёт один и тот же текст.
languagetext, not nulllucene-v1 или legacy.
versioninteger, not nullВерсия языка поисковых запросов (сейчас 1).
fields_versioninteger, not nullВерсия списка имён полей для поиска, по которому написан запрос (сейчас 2).
created_atinteger, not nullкогда эта строка была сохранена здесь
last_run_atintegerКогда поиск последний раз запускали (миллисекунды эпохи); NULL для сохранённого поиска, который ни разу не запускали.
runsinteger, not nullСколько раз запускали этот поиск.
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

taggings

Одна метка на одной вещи.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
tag_idinteger, not null → tags.idtag, к которой это относится
taggable_typetext, not nullchat, identity, message, email, document, note, person, organization, project, task, event, …
taggable_idinteger, not nullстрока в таблице taggable_type
maininteger, not null1 на главной теме вещи; не больше одной на вещь
sourcetext, not nullоткуда она: владелец, агент, auto, импорт
author_typetextperson или bot
author_idintegerстрока в таблице author_type
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

Куски, векторы и состояние поиска

Производные данные: их можно пересобрать из таблиц выше. Каждый длинный текст режется на chunks; у текста каждого куска одна строка в embeddings.

conversations

Группа сообщений одного чата, которые относятся к одному обсуждению, из одной сборки.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
chat_idinteger, not null → chats.idchat, к которому это относится
first_message_idinteger, not null → messages.idmessage, к которому это относится
buildinteger, not nullПишется пакетами под новым номером, затем сразу становится текущей: читатели никогда не видят половину сборки.
first_atinteger, not nullВремя отправки первого сообщения обсуждения (миллисекунды эпохи).
last_atinteger, not nullВремя отправки последнего сообщения обсуждения (миллисекунды эпохи).
message_countinteger, not nullСколько сообщений в обсуждении.
built_atinteger, not nullКогда выполнялась сборка, которая создала это обсуждение (миллисекунды эпохи).
algorithm_versioninteger, not nullВерсия правил связывания, которые сгруппировали сообщения (сейчас 5).
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

conversation_messages

Какие сообщения входят в каждое обсуждение.

СтолбецТипЗначение
conversation_idinteger, not null → conversations.idconversation, к которому это относится
message_idinteger, not null → messages.idmessage, к которому это относится

conversation_state

Какие чаты включил пользователь и насколько свежи их обсуждения.

СтолбецТипЗначение
chat_idinteger, PK → chats.idчат; одна строка на чат с включённой группировкой
enabled_atinteger, not nullКогда владелец включил группировку обсуждений для этого чата (миллисекунды эпохи).
built_atintegerКогда были собраны текущие обсуждения этого чата (миллисекунды эпохи); NULL до первой сборки.
algorithm_versionintegerВерсия правил связывания текущей сборки; NULL до первой сборки.
current_buildintegerСборка, которую видят читатели; более высокая сейчас пишется или не удалась.

embeddings

Один вектор на модель и текст: его использует любой кусок, чей content_hash совпадает.

СтолбецТипЗначение
modeltext, not null&lt;provider>:&lt;model>:&lt;dims> — векторы разных моделей никогда не смешиваются.
content_hashtext, not nullШестнадцатеричный SHA-256 текста куска, который получила модель векторов; сам текст никогда не хранится.
dimsinteger, not nullЧисло измерений вектора (его длина в float32).
vectorblob, not nullFloat32, little-endian, единичной длины.
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

search_index_state

Насколько построен производный поисковый индекс, одна строка на индекс. watermark — наибольший pk сообщения на момент создания индекса: строки выше него индексируют триггеры, строки до него — пакеты, дошедшие до filled_through.

СтолбецТипЗначение
nametext, PKкакой индекс описывает строка, например message_words
watermarkinteger, not nullНаибольший pk сообщения на момент создания индекса; строки выше него индексируют триггеры, строки до него — пакетное заполнение.
filled_throughinteger, not nullНаибольший pk сообщения, который пакетное заполнение уже проиндексировало; индекс полон, когда он доходит до отметки.
terms_throughinteger, not nullНаибольший pk сообщения, чьи слова есть в словаре исправления опечаток; 0 значит, что словарь не построен.
normalizer_versioninteger, not nullВерсия правил нормализации текста, с которыми построен индекс.
built_atintegerКогда индекс закончил заполняться (миллисекунды эпохи); NULL, пока он ещё заполняется.
analyzertextВыбор стеммеров и версия Snowball, которые построили строку основ (analyzerIdentity); NULL, пока её не займёт заполнение.

search_terms

Каждое слово в поисковом индексе сообщений, для исправления опечаток.

СтолбецТипЗначение
termtext, PKслово, как проиндексировано
lengthinteger, not nullЧисло символов в слове; по нему ищут кандидатов на опечатку похожей длины.

search_term_trigrams

Куски из трёх букв каждого поискового слова, чтобы находить слова, похожие на слово с опечаткой.

СтолбецТипЗначение
trigramtext, not nullКусок из трёх символов известного слова; по нему ищут слова, похожие на слово с опечаткой.
lengthinteger, not nullДлина в символах слова, из которого взята триграмма.
termtext, not nullИзвестное поисковое слово, к которому относится триграмма (у чисел триграмм нет).

involvements

Кто в чём участвовал и когда: одна строка на человека или учётную запись на сообщение, письмо, встречу, задачу или документ. Берётся из отправителей, получателей, участников, исполнителей и связей и пересобирается по желанию; превращает «всё об Alex» в один диапазон индекса.

СтолбецТипЗначение
idinteger, PKПервичный ключ.
person_idinteger → persons.idnull, пока учётная запись ни с кем не связана
identity_idinteger → identities.ididentity, к которой это относится
subject_typetext, not nullmessage, email, meeting, task, document, note, …
subject_idinteger, not nullстрока в таблице subject_type
roletext, not nullsender, recipient, participant, assignee, author, mentioned, linked
occurred_atinteger, not nullкогда произошёл субъект
scopetext, not nullpersonal или work, по аккаунту, чату или проекту субъекта
account_idinteger → accounts.idаккаунт интеграции, через который это пришло
project_idinteger → projects.idproject, к которому это относится
created_atinteger, not nullкогда эта строка была сохранена здесь

involvement_pending

Вещи, чьи строки involvements устарели: сообщение, чат, встреча, письмо, задача или что угодно, откуда начинается связь. Триггеры ставят в очередь; обработка очереди пересчитывает строки этой вещи.

СтолбецТипЗначение
idinteger, not nullСтрока, ждущая индексации.
indexable_typetext, not nullк какой таблице относится ждущая строка

chunks

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

СтолбецТипЗначение
idinteger, PKПервичный ключ.
chunkable_typetext, not nulldocument, email, attachment, note, memory, conversation, meeting_transcript, meeting_summary, event, task
chunkable_idinteger, not nullстрока в таблице chunkable_type
positioninteger, not nullпорядок внутри родителя, с 0
start_offsetinteger, not nullгде кусок начинается в тексте родителя, в символах
end_offsetinteger, not nullгде он заканчивается, не включая
content_hashtext, not nullхеш текста куска; его вектор — строка embeddings с этим хешем, поэтому одинаковый текст получает вектор один раз
scopetextкопируется из аккаунта, чата или проекта родителя, чтобы векторный поиск фильтровал до сравнения; триггеры следят за изменением чата или аккаунта
account_idinteger → accounts.idкопируется из родителя, фильтр для векторного поиска
project_idinteger → projects.idкопируется из родителя, если он к нему относится
occurred_atintegerкогда произошёл родитель (отправлен, проведён, написан), фильтр для векторного поиска
created_atinteger, not nullкогда эта строка была сохранена здесь
updated_atinteger, not nullкогда эта строка последний раз изменилась здесь

chunk_messages

Из каких сообщений вырезан кусок обсуждения. Одна строка на строку conversation в chunks; удаление любого из сообщений удаляет кусок.

СтолбецТипЗначение
chunk_idinteger, PK → chunks.idкусок типа conversation
first_message_idinteger, not null → messages.idmessage, к которому это относится
last_message_idinteger, not null → messages.idmessage, к которому это относится
text_startintegerгде кусок начинается внутри текста первого сообщения, если он начинается посреди сообщения
text_endintegerгде он заканчивается внутри текста последнего сообщения

Хранилище

Служебные данные самого файла.

schema_migrations

Какие миграции применены к этому файлу.

СтолбецТипЗначение
versioninteger, PKномер миграции
min_compatibleinteger, not nullСамая старая версия схемы, чей код ещё может безопасно писать в файл этой версии; сборка старше неё отказывается работать с файлом.
applied_atinteger, not nullКогда эта миграция была применена к файлу (миллисекунды эпохи).

store_settings

Настройки самого файла хранилища, общие для всех профилей, tg и MAX, — в отличие от файла конфигурации профиля.

СтолбецТипЗначение
keytext, PKимя настройки
valuetext, not nullЗначение настройки в виде текста, общее для всех профилей и мессенджеров, которые используют этот файл хранилища.
atinteger, not nullКогда настройка последний раз записывалась (миллисекунды эпохи).

Чтобы увидеть эти таблицы в работе, прочитайте, как работает поиск; чтобы увидеть, как хранилище встроено в инструменты, — архитектуру.