Перейти до основного вмісту
Коли кілька сеансів працюють над однією проблемою — керівник делегує завдання дочірнім сеансам, людина безпосередньо переходить до робочого сеансу, два агенти координують дії через sessions_send — кожен сеанс формує припущення про інші. Ці припущення застарівають щойно втручається інший учасник. Обізнаність про стан сеансу — це механізм, який виявляє втручання, одноразово сповіщає відповідний сеанс і дає йому простий спосіб синхронізуватися перед виконанням дій. Разом працюють три складники:
  1. Стійкий журнал сигналів записує вибрані зміни стану кожного сеансу.
  2. Спостерігачі зберігають курсори для кожної цілі й отримують одне об’єднане сповіщення про застарілий стан.
  3. Узгодження отримує точну різницю через 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 для його спостерігачів і видаляють зв’язок із зовнішнім джерелом. Подальше продовження сеансу з каталогу створює новий зв’язок.

Сповіщення: одне, а не багато

Коли надходить подія, що передбачає сповіщення, а курсор спостерігача відстає, спостерігач отримує одне системне сповіщення під час своєї наступної репліки:
Спостерігачі головного сеансу також негайно активуються через Heartbeat; вкладені спостерігачі-субагенти отримують сповіщення під час своєї наступної репліки. Протокол навмисно запобігає надмірній кількості сповіщень:
  • Одне сповіщення в очікуванні для кожної пари спостерігач/ціль. Текст сповіщення залишається побайтово незмінним, поки воно очікує, а черга системних подій усуває його дублікати, тому двадцять швидких змін однієї цілі все одно створюють лише один рядок у запиті спостерігача.
  • Зафіксована позначка. Курсор фіксує свою сповіщену позицію, коли сповіщення додається до черги. Подальші суттєві події переміщують лише позначку суттєвих змін і не створюють повторного сповіщення.
  • Підтвердження під час вилучення з черги, повторне відкриття лише для проміжних змін. Коли репліка спостерігача отримує сповіщення, курсор просувається. Якщо між додаванням до черги й вилученням із неї надійшли додаткові суттєві події, для решти відкривається рівно одне нове сповіщення.
  • Приглушення власних подій. Спостерігач ніколи не отримує сповіщень про події, які спричинив сам.
  • Відновлення після перезапуску. Сповіщення в очікуванні зберігаються в черзі в пам’яті; початкове сканування повторно створює їх зі стійких курсорів після перезапуску 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 не відстежується.

Пов’язані матеріали