sessions_send — кожен сеанс формує припущення про інші. Ці припущення застарівають щойно втручається інший учасник. Обізнаність про стан сеансу — це механізм, який виявляє втручання, одноразово сповіщає відповідний сеанс і дає йому простий спосіб синхронізуватися перед виконанням дій.
Разом працюють три складники:
- Стійкий журнал сигналів записує вибрані зміни стану кожного сеансу.
- Спостерігачі зберігають курсори для кожної цілі й отримують одне об’єднане сповіщення про застарілий стан.
- Узгодження отримує точну різницю через
session_statusізchangesSince.
Журнал сигналів
OpenClaw додає типізовану подію до спільної бази даних стану (session_state_events), коли у відстежуваному сеансі відбувається суттєва зміна. Події містять метадані й однорядковий опис — але ніколи не вміст повідомлення.
Кожна подія вказує свого учасника (
human, agent або system). Скасовані дочірні запуски й запуски з перевищеним часом очікування записуються як помилки, а точний результат (cancelled, timeout або error) зберігається в корисному навантаженні події.
Версія стану сеансу — це просто найбільший порядковий номер у його журналі, який відстежується у стійкому заголовку кожного сеансу й зберігається після очищення. Рядки sessions_list містять stateVersion, якщо в журналі сеансу є зміни; session_status завжди повідомляє його.
Типи, призначені лише для журналу, існують для історії узгодження, а не для сповіщень: звичайне доставлення відомостей про завершення дочірнього запуску залишається відповідальністю оголошень субагентів, і журнал сигналів ніколи його не дублює.
Спостерігачі
Спостерігач — це сеанс, який зберігає курсор (session_watch_cursors) для цілі. Курсори створюються двома способами:
- Неявно (зв’язки породження). Коли сеанс породжує субагента або дочірній сеанс ACP, курсор батьківського сеансу автоматично ініціалізується версією породження дочірнього сеансу. Батьківські сеанси ніколи не підписуються вручну.
- Явно (
sessions_send watch: true). Будь-який координатор може відстежувати ціль, яку він не породив: передайтеwatch: trueуsessions_send, і після успішного надсилання відправника буде зареєстровано як спостерігача за сеансом, який фактично отримав повідомлення. Реєстрація починається з поточної версії стану цілі — попередня історія ніколи не створює сповіщень. Результат інструмента міститьwatched: true|false, якщо параметр було задано.
session.scope="global" спільний ключ global неоднозначний для різних агентів, тому такі сеанси отримують стійкий журнал і changesSince, але не проактивні сповіщення.
Відстеження очищується автоматично: термін дії рядків курсорів завершується разом із терміном зберігання журналу сигналів, вони видаляються після скидання сеансу-спостерігача та разом із будь-яким із двох сеансів. У v1 немає команди припинення відстеження.
Приєднані з каталогу відстежувані сеанси перевіряються з фіксованою періодичністю на безпосередню активність людини у зовнішньому джерелі. Виявлена активність потрапляє до того самого журналу сигналів і проходить той самий процес сповіщення спостерігачів, що й інші безпосередні репліки людини.
Якщо зовнішнє джерело приєднаного сеансу видалено ззовні, три послідовні невдалі перевірки (приблизно три такти моніторингу) створюють один сигнал upstream_missing для його спостерігачів і видаляють зв’язок із зовнішнім джерелом. Подальше продовження сеансу з каталогу створює новий зв’язок.
Сповіщення: одне, а не багато
Коли надходить подія, що передбачає сповіщення, а курсор спостерігача відстає, спостерігач отримує одне системне сповіщення під час своєї наступної репліки:- Одне сповіщення в очікуванні для кожної пари спостерігач/ціль. Текст сповіщення залишається побайтово незмінним, поки воно очікує, а черга системних подій усуває його дублікати, тому двадцять швидких змін однієї цілі все одно створюють лише один рядок у запиті спостерігача.
- Зафіксована позначка. Курсор фіксує свою сповіщену позицію, коли сповіщення додається до черги. Подальші суттєві події переміщують лише позначку суттєвих змін і не створюють повторного сповіщення.
- Підтвердження під час вилучення з черги, повторне відкриття лише для проміжних змін. Коли репліка спостерігача отримує сповіщення, курсор просувається. Якщо між додаванням до черги й вилученням із неї надійшли додаткові суттєві події, для решти відкривається рівно одне нове сповіщення.
- Приглушення власних подій. Спостерігач ніколи не отримує сповіщень про події, які спричинив сам.
- Відновлення після перезапуску. Сповіщення в очікуванні зберігаються в черзі в пам’яті; початкове сканування повторно створює їх зі стійких курсорів після перезапуску Gateway.
Узгодження
Сповіщення точно вказує спостерігачу, що потрібно зробити.session_status із changesSince: <version> повертає типізовані події після цієї версії (до 200), не просуваючи жодного курсора:
historyGap: true означає, що запитана версія передує збереженій історії — оновіть увесь стан сеансу (sessions_history, session_status) замість того, щоб вважати відповідь точною різницею. Сигнал про прогалину точний: він надходить із позначки очищення для кожного сеансу, а не виводиться з арифметики порядкових номерів.
Зберігання та обмеження
Історія зберігається у спільній базі даних стану й обмежена 30 днями та 50 000 рядками; заголовки кожного сеансу залишаються монотонними після очищення. Записування виконується за принципом докладання всіх зусиль — невдале додавання реєструється в журналі й ніколи не спричиняє помилку вихідної репліки — томуstateVersion є заголовком журналу сигналів, а не транзакційною версією відстеження змін даних.
Поточні обмеження:
- Доставлення сповіщень передбачає, що спільною базою даних стану керує один процес Gateway. Кілька Gateway спільно використовують стійкий журнал і
changesSince, але v1 не передає сповіщення між процесами. - Події Compaction охоплюють власників ущільнення вбудованого середовища виконання; ущільнення, яке виконується лише нативним каркасом, реєструється не повністю.
- Докладні дані про результат скасування наразі створюють дочірні запуски ACP; скасування нативних субагентів відображаються як загальні помилки.
- Виявлення власного відлуння із зовнішнього джерела порівнює нормалізований текст користувача. Зовнішній запит, що збігається з одним із 10 останніх повідомлень користувача на стороні OpenClaw у цьому сеансі, вважається власним відлунням.
- Один локальний рядок Claude JSONL, більший за ліміт сканування 1 MiB за один цикл, блокує курсор цього сеансу у v1; некласифіковані байти ніколи не пропускаються.
- Перевірки Claude на спареному вузлі класифікують останні 50 елементів стенограми за один цикл. Більші сплески можуть опинитися поза межами вікна сканування v1.
- Читання історії Claude на спареному вузлі не надає однозначного результату про відсутність гілки, тому віддалені видалення Claude не класифікуються як
upstream_missingу v1. - Сеанси каталогу, які не було приєднано, залишаються поза рівнем обізнаності у v1.
- Сеанси, приєднані до появи цієї функції, не мають зв’язку із зовнішнім джерелом; один раз продовжте їх із каталогу, щоб розпочати моніторинг зовнішнього джерела.
- Зв’язки із зовнішніми джерелами передбачають, що кожен ключ приєднаного сеансу відповідає одному агенту-власнику (під час приєднання використовується агент сховища за замовчуванням). Приєднання однієї зовнішньої гілки кількома агентами у v1 не відстежується.
Пов’язані матеріали
- Інструменти сеансів —
sessions_send,session_status,sessions_list - Субагенти — зв’язки породження й оголошення про завершення
- Heartbeat — як сповіщення в черзі активують головні сеанси
- Керування сеансами — ключі, області дії та життєвий цикл сеансів