Sessions and memory

Обізнаність про стан сеансу

Коли кілька сеансів працюють над однією проблемою — керівник делегує завдання дочірнім сеансам, людина безпосередньо переходить до робочого сеансу, два агенти координують дії через sessions_send — кожен сеанс формує припущення про інші. Ці припущення застарівають щойно втручається інший учасник. Обізнаність про стан сеансу — це механізм, який виявляє втручання, одноразово сповіщає відповідний сеанс і дає йому простий спосіб синхронізуватися перед виконанням дій.

Разом працюють три складники:

  1. Стійкий журнал сигналів записує вибрані зміни стану кожного сеансу.
  2. Спостерігачі зберігають курсори для кожної цілі й отримують одне об’єднане сповіщення про застарілий стан.
  3. Узгодження отримує точну різницю через session_status із changesSince.

Журнал сигналів

OpenClaw додає типізовану подію до спільної бази даних стану (session_state_events), коли у відстежуваному сеансі відбувається суттєва зміна. Події містять метадані й однорядковий опис — але ніколи не вміст повідомлення.

Тип Коли записується Сповіщає спостерігачів
human_direct_message Людина надсилає репліку безпосередньо у відстежуваний сеанс Так
upstream_missing Зникає зовнішнє джерело приєднаного сеансу Так
goal_changed Стан цілі сеансу створено, оновлено або очищено Так
child_spawned Створено дочірній сеанс субагента або ACP Ні (ініціалізує курсор)
run_completed Дочірній запуск успішно завершується Ні (лише журнал)
run_failed Дочірній запуск завершується помилкою, перевищує час очікування або скасовується Ні (лише журнал)
compacted Історію сеансу ущільнено Ні (лише журнал)
adopted Сеанс із каталогу приєднано до OpenClaw Ні (лише журнал)

Кожна подія вказує свого учасника (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 для його спостерігачів і видаляють зв’язок із зовнішнім джерелом. Подальше продовження сеансу з каталогу створює новий зв’язок.

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

Коли надходить подія, що передбачає сповіщення, а курсор спостерігача відстає, спостерігач отримує одне системне сповіщення під час своєї наступної репліки:

Code
Сеанс "agent:main:subagent:child" змінено (іншим учасником). Узгодьте стан перед виконанням дій: session_status sessionKey "agent:main:subagent:child" changesSince 12.

Спостерігачі головного сеансу також негайно активуються через Heartbeat; вкладені спостерігачі-субагенти отримують сповіщення під час своєї наступної репліки.

Протокол навмисно запобігає надмірній кількості сповіщень:

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

Узгодження

Сповіщення точно вказує спостерігачу, що потрібно зробити. session_status із changesSince: <version> повертає типізовані події після цієї версії (до 200), не просуваючи жодного курсора:

json
{  "stateVersion": 19,  "stateChanges": {    "events": [      {        "sequence": 14,        "kind": "human_direct_message",        "actorType": "human",        "summary": "повідомлення людини через telegram"      },      { "sequence": 19, "kind": "goal_changed", "actorType": "human", "summary": "ціль оновлено" }    ],    "historyGap": false  }}

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 не відстежується.

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

Was this useful?
On this page

On this page