Sessions and memory
セッション状態の認識
複数のセッションが同じ問題に取り組む場合(マネージャーが子に委任する、人間がワーカーセッションに直接介入する、2 つのエージェントが sessions_send を介して連携するなど)、各セッションは他のセッションについて前提を構築します。別のアクターが介入した瞬間に、それらの前提は古くなります。セッション状態認識は、その介入を検出し、影響を受けるセッションに一度だけ通知し、動作する前に低コストで状況を把握できるようにする仕組みです。
3 つの要素が連携します。
- 永続的なシグナルログが、セッションごとに選択された状態変更を記録します。
- ウォッチャーがターゲットごとのカーソルを保持し、集約された古い状態の通知を 1 件受け取ります。
- 調整が、
changesSinceを指定したsession_statusを介して正確な差分を取得します。
シグナルログ
監視対象のセッションに実質的な変更が発生すると、OpenClaw は共有状態データベース(session_state_events)に型付きイベントを追加します。イベントにはメタデータと 1 行の要約が含まれますが、メッセージの内容は決して含まれません。
| 種類 | 記録されるタイミング | ウォッチャーへの通知 |
|---|---|---|
human_direct_message |
人間が監視対象セッションに直接ターンを送信したとき | あり |
goal_changed |
セッションの目標状態が作成、更新、または消去されたとき | あり |
child_spawned |
サブエージェントまたは ACP 子セッションが作成されたとき | なし(カーソルを初期化) |
run_completed |
子の実行が正常に終了したとき | なし(ログのみ) |
run_failed |
子の実行が失敗、タイムアウト、またはキャンセルされたとき | なし(ログのみ) |
compacted |
セッションの履歴が圧縮されたとき | なし(ログのみ) |
各イベントには、そのアクター(human、agent、または system)が記録されます。キャンセルまたはタイムアウトした子の実行は失敗として記録され、正確な結果(cancelled、timeout、または error)がイベントペイロードに保持されます。
セッションの状態バージョンは、そのログ内で最も大きいシーケンス番号です。これは、プルーニング後も保持されるセッションごとの永続的なヘッドで追跡されます。セッションにログ記録済みの変更がある場合、sessions_list の行には stateVersion が含まれます。session_status は常にこれを報告します。
ログのみの種類は、通知ではなく調整履歴のために存在します。通常の子実行完了の配信は引き続きサブエージェントのアナウンスが担当し、シグナルログがそれを重複して通知することはありません。
ウォッチャー
ウォッチャーとは、ターゲットに対するカーソル(session_watch_cursors)を保持するセッションです。カーソルは次の 2 つの方法で作成されます。
- 暗黙的(生成エッジ)。 セッションがサブエージェントまたは ACP 子を生成すると、親のカーソルが子の生成時点のバージョンで自動的に初期化されます。親が手動でサブスクライブすることはありません。
- 明示的(
sessions_send watch: true)。 任意のコーディネーターが、生成したものではないターゲットを監視できます。sessions_sendにwatch: trueを渡すと、送信が正常にディスパッチされた後、送信元が実際にメッセージを受信したセッションのウォッチャーとして登録されます。登録はターゲットの現在の状態バージョンから開始され、それより前の履歴によって通知が生成されることはありません。このパラメーターが設定された場合、ツールの結果にはwatched: true|falseが報告されます。
ウォッチャーの ID は、エージェントで修飾されたセッションキーでなければなりません。session.scope="global" では、共有の global キーは複数のエージェント間で曖昧になるため、そのようなセッションには永続ログと changesSince は提供されますが、プロアクティブな通知は提供されません。
監視は自動的にクリーンアップされます。カーソル行はシグナルログの保持期間に合わせて期限切れになり、ウォッチャーセッションがリセットされると削除され、いずれかのセッションが削除された場合にも削除されます。v1 には監視解除の動詞はありません。
通知:多数ではなく 1 件
通知対象のイベントが記録され、ウォッチャーのカーソルが遅れている場合、ウォッチャーは次のターンでシステム通知を 1 件受け取ります。
セッション "agent:main:subagent:child" が変更されました(別のアクター)。動作する前に調整してください: session_status sessionKey "agent:main:subagent:child" changesSince 12.メインセッションのウォッチャーは Heartbeat のウェイクによって即座に起動されます。ネストされたサブエージェントのウォッチャーは、次のターンで通知を受け取ります。
このプロトコルは、意図的にスパムを防止する設計になっています。
- ウォッチャーとターゲットのペアごとに保留中の通知は 1 件。 保留中は通知テキストがバイト単位で安定しており、システムイベントキューがそれを重複排除するため、同じターゲットに 20 件の変更が立て続けに発生しても、ウォッチャーのプロンプトには 1 行だけ表示されます。
- 固定されたウォーターマーク。 通知がキューに追加されると、カーソルは通知済み位置を固定します。それ以降の実質的なイベントは実質的ウォーターマークのみを進め、再通知は行いません。
- ドレイン時に確認し、処理が交錯した場合にのみ再開。 ウォッチャーのターンで通知が消費されると、カーソルが進みます。キューへの追加からドレインまでの間にさらに実質的なイベントが到着した場合、残りに対して新しい通知が正確に 1 件だけ作成されます。
- 自己抑制。 ウォッチャー自身が発生させたイベントについて通知を受けることはありません。
- 再起動からの復旧。 保留中の通知はメモリ内キューに保持されます。Gateway の再起動後、起動時のスイープによって永続カーソルから通知が再生成されます。
調整
通知は、ウォッチャーが何をすべきかを正確に示します。changesSince: <version> を指定した session_status は、そのバージョンより後の型付きイベント(最大 200 件)を返しますが、カーソルは一切進めません。
{ "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 はシグナルログのヘッドであり、トランザクション型の変更データキャプチャーバージョンではありません。
現在の制限事項:
- 通知の配信では、1 つの Gateway プロセスが共有状態データベースを所有していることを前提とします。複数の Gateway は永続ログと
changesSinceを共有しますが、v1 ではプロセス間で通知をプッシュしません。 - 圧縮イベントは組み込みランタイムの圧縮所有者を対象とします。ネイティブハーネスのみの圧縮は完全には記録されません。
- キャンセル結果の詳細なペイロードは、現在 ACP 子の実行によって生成されます。ネイティブサブエージェントのキャンセルは汎用的な失敗として公開されます。