セッション管理詳細解説
このドキュメントでは、OpenClawがセッションをエンドツーエンドで管理する方法を説明します:
- セッションルーティング (着信メッセージが
sessionKeyにマッピングされる仕組み) - セッションストア (
sessions.json) とその追跡内容 - トランスクリプト永続化 (
*.jsonl) とその構造 - トランスクリプトの整理 (実行前のプロバイダー固有の修正)
- コンテキスト制限 (コンテキストウィンドウ vs 追跡トークン)
- コンパクション (手動 + 自動コンパクション) と事前コンパクション作業をフックする場所
- サイレントハウスキーピング (ユーザーに表示される出力を生成すべきでないメモリ書き込みなど)
より高レベルの概要を最初に知りたい場合は、以下から始めてください:
信頼できる情報源: ゲートウェイ
OpenClawは、セッション状態を所有する単一のゲートウェイプロセスを中心に設計されています。
- UI (macOSアプリ、WebコントロールUI、TUI) は、セッションリストとトークン数を取得するためにゲートウェイにクエリを実行する必要があります。
- リモートモードでは、セッションファイルはリモートホスト上にあります。「ローカルのMacファイルを確認する」ことは、ゲートウェイが使用しているものを反映しません。
2つの永続化レイヤー
OpenClawはセッションを2つのレイヤーで永続化します:
- セッションストア (
sessions.json)- キー/値マップ:
sessionKey -> SessionEntry - 小さく、可変的、編集(またはエントリ削除)が安全
- セッションメタデータを追跡 (現在のセッションID、最終アクティビティ、トグル、トークンカウンターなど)
- キー/値マップ:
- トランスクリプト (
<sessionId>.jsonl)- ツリー構造を持つ追記専用のトランスクリプト (エントリは
id+parentIdを持つ) - 実際の会話 + ツール呼び出し + コンパクション要約を保存
- 将来のターンのためにモデルコンテキストを再構築するために使用
- ツリー構造を持つ追記専用のトランスクリプト (エントリは
ディスク上の場所
エージェントごと、ゲートウェイホスト上:
- ストア:
~/.openclaw/agents/<agentId>/sessions/sessions.json - トランスクリプト:
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl- Telegramトピックセッション:
.../<sessionId>-topic-<threadId>.jsonl
- Telegramトピックセッション:
OpenClawはこれらを src/config/sessions.ts 経由で解決します。
ストアのメンテナンスとディスク制御
セッション永続化には、sessions.json とトランスクリプトアーティファクトのための自動メンテナンス制御 (session.maintenance) があります:
mode:warn(デフォルト) またはenforcepruneAfter: 古いエントリのカットオフ期間 (デフォルト30d)maxEntries:sessions.json内のエントリ数の上限 (デフォルト500)rotateBytes:sessions.jsonが大きすぎる場合にローテート (デフォルト10mb)resetArchiveRetention:*.reset.<timestamp>トランスクリプトアーカイブの保持期間 (デフォルト:pruneAfterと同じ;falseでクリーンアップ無効化)maxDiskBytes: オプションのセッションディレクトリ予算highWaterBytes: クリーンアップ後のオプションの目標値 (デフォルトmaxDiskBytesの80%)
ディスク予算クリーンアップの実施順序 (mode: "enforce"):
- まず最も古いアーカイブ済みまたは孤立したトランスクリプトアーティファクトを削除。
- それでも目標値を超える場合、最も古いセッションエントリとそのトランスクリプトファイルを削除。
- 使用量が
highWaterBytes以下になるまで続行。
mode: "warn" では、OpenClawは潜在的な削除を報告しますが、ストア/ファイルを変更しません。必要に応じてメンテナンスを実行:
openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce
Cronセッションと実行ログ
分離されたcron実行もセッションエントリ/トランスクリプトを作成し、専用の保持制御があります:
cron.sessionRetention(デフォルト24h) は、古い分離cron実行セッションをセッションストアから削除します (falseで無効化)。cron.runLog.maxBytes+cron.runLog.keepLinesは~/.openclaw/cron/runs/<jobId>.jsonlファイルを削除します (デフォルト:2_000_000バイトと2000行)。
セッションキー (sessionKey)
sessionKey は、どの会話バケットにいるかを識別します (ルーティング + 分離)。一般的なパターン:
- メイン/直接チャット (エージェントごと):
agent:<agentId>:<mainKey>(デフォルトmain) - グループ:
agent:<agentId>:<channel>:group:<id> - ルーム/チャンネル (Discord/Slack):
agent:<agentId>:<channel>:channel:<id>または...:room:<id> - Cron:
cron:<job.id> - Webhook:
hook:<uuid>(上書きされない限り)
正規のルールは /concepts/session に文書化されています。
セッションID (sessionId)
各 sessionKey は、現在の sessionId (会話を継続するトランスクリプトファイル) を指します。経験則:
- リセット (
/new,/reset) は、そのsessionKeyに対して新しいsessionIdを作成します。 - 日次リセット (デフォルトはゲートウェイホストの現地時間で午前4:00) は、リセット境界後の次のメッセージで新しい
sessionIdを作成します。 - アイドル期限切れ (
session.reset.idleMinutesまたはレガシーsession.idleMinutes) は、アイドルウィンドウ後にメッセージが到着したときに新しいsessionIdを作成します。日次とアイドルの両方が設定されている場合、どちらか早く期限切れになる方が優先されます。 - スレッド親フォークガード (
session.parentForkMaxTokens, デフォルト100000) は、親セッションが既に大きすぎる場合、親トランスクリプトのフォークをスキップします。新しいスレッドは新規に開始します。0に設定すると無効化されます。
実装詳細: 決定は src/auto-reply/reply/session.ts の initSessionState() で行われます。
セッションストアスキーマ (sessions.json)
ストアの値の型は src/config/sessions.ts の SessionEntry です。主要なフィールド (網羅的ではありません):
sessionId: 現在のトランスクリプトID (sessionFileが設定されていない限り、ファイル名はこれから派生)updatedAt: 最終アクティビティタイムスタンプsessionFile: オプションの明示的なトランスクリプトパス上書きchatType:direct | group | room(UIと送信ポリシーに役立つ)provider,subject,room,space,displayName: グループ/チャンネルラベリングのためのメタデータ- トグル:
thinkingLevel,verboseLevel,reasoningLevel,elevatedLevelsendPolicy(セッションごとの上書き)
- モデル選択:
providerOverride,modelOverride,authProfileOverride
- トークンカウンター (ベストエフォート / プロバイダー依存):
inputTokens,outputTokens,totalTokens,contextTokens
compactionCount: このセッションキーに対して自動コンパクションが完了した回数memoryFlushAt: 最後の事前コンパクションメモリフラッシュのタイムスタンプmemoryFlushCompactionCount: 最後のフラッシュが実行されたときのコンパクション回数
ストアは編集しても安全ですが、ゲートウェイが権限を持ちます:セッション実行時にエントリを書き換えたり再ハイドレートしたりする可能性があります。
トランスクリプト構造 (*.jsonl)
トランスクリプトは @mariozechner/pi-coding-agent の SessionManager によって管理されます。ファイルはJSONL形式です:
- 最初の行: セッションヘッダー (
type: "session",id,cwd,timestamp, オプションparentSessionを含む) - その後:
id+parentIdを持つセッションエントリ (ツリー)
注目すべきエントリタイプ:
message: ユーザー/アシスタント/toolResultメッセージcustom_message: モデルコンテキストに入る拡張機能注入メッセージ (UIから非表示にできる)custom: モデルコンテキストに入らない拡張機能状態compaction:firstKeptEntryIdとtokensBeforeを持つ永続化されたコンパクション要約branch_summary: ツリーブランチをナビゲートする際の永続化された要約
OpenClawは意図的にトランスクリプトを「修正」しません。ゲートウェイは SessionManager を使用してトランスクリプトを読み書きします。
コンテキストウィンドウ vs 追跡トークン
2つの異なる概念が重要です:
- モデルコンテキストウィンドウ: モデルごとのハードキャップ (モデルから見えるトークン)
- セッションストアカウンター:
sessions.jsonに書き込まれるローリング統計 (/status とダッシュボードで使用)
制限を調整する場合:
- コンテキストウィンドウはモデルカタログから来ます (設定で上書き可能)。
- ストア内の
contextTokensは実行時の推定/報告値です。厳密な保証として扱わないでください。
詳細は /token-use を参照してください。
コンパクション: その概要
コンパクションは、古い会話をトランスクリプト内の永続化された compaction エントリに要約し、最近のメッセージはそのまま保持します。コンパクション後、将来のターンでは以下が見えます:
- コンパクション要約
firstKeptEntryId以降のメッセージ
コンパクションは永続的です (セッションプルーニングとは異なります)。 /concepts/session-pruning を参照してください。
自動コンパクションが発生するタイミング (Piランタイム)
組み込みPiエージェントでは、自動コンパクションは2つのケースでトリガーされます:
- オーバーフロー回復: モデルがコンテキストオーバーフローエラーを返す → コンパクト → 再試行。
- しきい値メンテナンス: 成功したターンの後、以下の場合:
contextTokens > contextWindow - reserveTokens ここで:
contextWindowはモデルのコンテキストウィンドウreserveTokensはプロンプト + 次のモデル出力のために確保されたヘッドルーム
これらはPiランタイムのセマンティクスです (OpenClawはイベントを消費しますが、いつコンパクトするかはPiが決定します)。
コンパクション設定 (reserveTokens, keepRecentTokens)
Piのコンパクション設定はPi設定内にあります:
{
compaction: {
enabled: true,
reserveTokens: 16384,
keepRecentTokens: 20000,
},
}
OpenClawは組み込み実行のための安全な下限も強制します:
compaction.reserveTokens < reserveTokensFloorの場合、OpenClawはそれを引き上げます。- デフォルトの下限は
20000トークンです。 agents.defaults.compaction.reserveTokensFloor: 0を設定すると下限を無効化します。- 既に高い場合は、OpenClawはそのままにします。
理由:コンパクションが避けられなくなる前に、マルチターンの「ハウスキーピング」(メモリ書き込みなど) のための十分なヘッドルームを残すため。実装:src/agents/pi-settings.ts の ensurePiCompactionReserveTokens() (src/agents/pi-embedded-runner.ts から呼び出されます)。
ユーザーに見える部分
コンパクションとセッション状態は以下で観察できます:
/status(任意のチャットセッション内)openclaw status(CLI)openclaw sessions/sessions --json- 詳細モード:
🧹 Auto-compaction complete+ コンパクション回数
サイレントハウスキーピング (NO_REPLY)
OpenClawは、ユーザーが中間出力を見るべきではないバックグラウンドタスクのための「サイレント」ターンをサポートします。慣例:
- アシスタントはその出力を
NO_REPLYで開始し、「ユーザーに返信を配信しない」ことを示します。 - OpenClawは配信レイヤーでこれを除去/抑制します。
2026.1.10 時点で、OpenClawは部分チャンクが NO_REPLY で始まる場合のドラフト/タイピングストリーミングも抑制するため、サイレント操作中に部分出力が漏れることはありません。
事前コンパクション「メモリフラッシュ」 (実装済み)
目標:自動コンパクションが発生する前に、耐久性のある状態をディスクに書き込む (例:エージェントワークスペース内の memory/YYYY-MM-DD.md) サイレントなエージェントターンを実行し、コンパクションが重要なコンテキストを消去できないようにする。OpenClawは事前しきい値フラッシュアプローチを使用します:
- セッションコンテキスト使用量を監視。
- 「ソフトしきい値」 (Piのコンパクションしきい値より下) を超えたら、エージェントへのサイレントな「今すぐメモリを書き込む」指示を実行。
NO_REPLYを使用してユーザーには何も見せない。
設定 (agents.defaults.compaction.memoryFlush):
enabled(デフォルト:true)softThresholdTokens(デフォルト:4000)prompt(フラッシュターンのユーザーメッセージ)systemPrompt(フラッシュターンに追加される追加システムプロンプト)
注意点:
- デフォルトのプロンプト/システムプロンプトには、配信を抑制するための
NO_REPLYヒントが含まれています。 - フラッシュはコンパクションサイクルごとに1回実行されます (
sessions.jsonで追跡)。 - フラッシュは組み込みPiセッションに対してのみ実行されます (CLIバックエンドはスキップ)。
- セッションワークスペースが読み取り専用 (
workspaceAccess: "ro"または"none") の場合、フラッシュはスキップされます。 - ワークスペースファイルレイアウトと書き込みパターンについては Memory を参照してください。
Piは拡張APIに session_before_compact フックも公開していますが、OpenClawのフラッシュロジックは現在ゲートウェイ側にあります。
トラブルシューティングチェックリスト
- セッションキーが間違っている? /concepts/session から始めて、
/statusのsessionKeyを確認してください。 - ストアとトランスクリプトが一致しない?
openclaw statusからゲートウェイホストとストアパスを確認してください。 - コンパクションが頻繁に発生する? 以下を確認:
- モデルコンテキストウィンドウ (小さすぎる)
- コンパクション設定 (
reserveTokensがモデルウィンドウに対して高すぎると、早期コンパクションが発生する可能性あり) - ツール結果の肥大化:セッションプルーニングを有効化/調整
- サイレントターンが漏れている? 返信が
NO_REPLY(正確なトークン) で始まっていること、およびストリーミング抑制修正を含むビルドを使用していることを確認してください。