コンテンツにスキップ

セッションの外部ストレージ保存(SessionStore)

facts /Claude Code /agent-sdk-session-storage

stable1 か月前 · 2026-08-23

概要

SDK は既定でセッションのトランスクリプトを、ローカルの ~/.claude/projects/ 配下に JSONL で書く。SessionStore アダプタを与えると、同じトランスクリプトを自前のバックエンド(S3・Redis・データベースなど)へミラーでき、あるホストで作ったセッションを、作業ディレクトリが一致する別のホストで再開できる。

用途として挙げられているもの。

  • 複数ホストの配備: サーバーレス関数・オートスケールするワーカー・CI ランナーはファイルシステムを共有しない
  • 永続性: ローカルのコンテナは揮発する。S3 やデータベースに置けば再起動・再デプロイをまたぐ
  • コンプライアンスと監査: 自分が統制するストレージに、自分の保持規則・暗号化・アクセス制御で置く

仕様

SessionStore の形

必須メソッドは append と load の 2 つ、任意が 4 つ。

メソッド必須呼ばれるとき
append○トランスクリプトのバッチがローカルへ書かれた後。エントリは JSON-safe なオブジェクトで、ローカル JSONL の 1 行に対応する
load○resume 指定時、または continue: true がストア上の最新セッションを解決したときに、サブプロセス起動前。listSessionSummaries からの一覧がフォールバックしたときもセッションごとに 1 回。未知のセッションなら null を返す
listSessions–listSessions({ sessionStore }) と、continue: true の query() / startup() から。未定義だと continue: true は例外になり、listSessionSummaries が無ければ listSessions({ sessionStore }) も例外になる
listSessionSummaries–listSessions({ sessionStore }) が全セッションのメタデータを 1 回で読むため。要約は append の中で維持する。 未定義なら listSessions + セッションごとの load にフォールバックする
delete–deleteSession({ sessionStore }) から。主キー(subpath 無し)の削除は、そのセッションの全サブキーへ波及させ、要約エントリも消す必要がある。未定義なら削除は何もしない(追記専用のバックエンド向け)
listSubkeys–resume 時に subagent のトランスクリプトを見つけるため。未定義だと主トランスクリプトしか復元されない

SessionKey

  • projectKey — 作業ディレクトリを、安定した・ファイルシステムで安全な形に符号化したもの
  • sessionId — セッションの UUID
  • subpath — subagent のトランスクリプトや sidecar ファイルに属するときだけ入る。不透明な鍵の接尾辞として扱う(オンディスクの配置に従い、例: subagents/agent-<id>)。未定義なら主トランスクリプトを指す

projectKey が作業ディレクトリを符号化しているため、ストアからの resume / continue は元の実行と一致する作業ディレクトリから行う。

  • TypeScript では、query の env オプションで CLAUDE_CODE_PROJECT_DIR_NAME を CLAUDE_CONFIG_DIR と並べて設定すると、その query のエントリと resume / continue の探索がその名前で鍵付けされる。listSessions や deleteSession のような単独のヘルパーは env を取らずプロセスの環境を読むため、ホストプロセス側にも同じ名前と CLAUDE_CONFIG_DIR を設定する。Agent SDK v0.3.234 以降が必要

SessionSummaryEntry

  • mtime は sidecar のストレージ書き込み時刻。listSessions が返す mtime と同じ時計を共有する必要がある
  • data は SDK が所有する不透明な状態。解釈せずそのまま保存する
  • エントリの組み立ては、append の中で各バッチに対し foldSessionSummary(Python は fold_session_summary)を呼んで行う
  • subpath を持つバッチは飛ばす(subagent のトランスクリプトを主セッションの要約に混ぜない)
  • fold は mtime を設定しない。 永続化時に、TypeScript は options.mtime 引数、Python は返ったエントリのフィールド上書きで打つ
  • 同じセッションへの並行 append は sidecar で競合しうるため、read-fold-write をトランザクション・CAS・セッション単位のロックのいずれかで直列化する(fold 自体は純粋)

アダプタを書く

  • append に渡るエントリは SessionStoreEntry({ type: string; ... })。不透明な JSON-safe 値として、順序どおりに保存し、load で同じ順序で返す
  • load が返すエントリは append したものと deep-equal であればよい。 バイト単位で同じ直列化は求められないため、キー順を並べ替える Postgres の jsonb のようなバックエンドでも問題ない
  • TypeScript SDK のリポジトリに S3・Redis・Postgres の参照実装がある(examples/session-stores/)。npm には公開されていないため、必要な src/ のファイルを自分のプロジェクトへ複写して、対応するバックエンドのクライアントを入れる
  • 適合性テスト(conformance suite)が両 SDK に同梱される。任意メソッドのテストは未実装なら自動で飛ばされる。TypeScript は shared/conformance.ts を自分のテストへ複写、Python はパッケージ同梱で run_session_store_conformance に引数なしのファクトリを渡す(契約ごとに新しいストアを作るため、毎回空のストレージから始まる必要がある)

ミラーの構造(dual-write)

  • Claude Code のサブプロセスは常にローカルディスクへ先に書き、SDK が同じバッチをストアの append() へ転送する。 ストアはローカルの置き換えではなくミラー
  • 新規セッション(またはストアに何も無いときの resume): ローカルのトランスクリプトが実行後も残り、ストアには複製が入る
  • ストアから resume した実行: 実行終了時にローカルの複製が削除されるため、ストアが唯一の永続コピーになる
  • 新規セッションでローカルに残したくない場合は、options.env の CLAUDE_CONFIG_DIR を一時ディレクトリにする。TypeScript では env がサブプロセスの環境を置き換えるため process.env を展開する
  • 設定ディレクトリ内のファイル(OAuth 認証情報や user settings.json の apiKeyHelper)でサインインしている場合、それらを一時ディレクトリへ複写するか、env に ANTHROPIC_API_KEY を設定する。しないと Not logged in で失敗する

ストアからの resume

resume(または TypeScript の continue: true / Python の continue_conversation=True)とストアを併せて渡すと、SDK はサブプロセス起動前にストアへトランスクリプトを要求する。

  • resume: 渡した ID のセッションを要求する
  • continue: ストア上の最新セッションを要求する

ストアが返した場合、SDK は一時的な設定ディレクトリへ書き出し、CLAUDE_CONFIG_DIR をそこへ向けてサブプロセスを実行し、終了時にそのディレクトリを削除する。

一時ディレクトリへ複写されるものは言語で異なる。

  • TypeScript: 認証情報・.claude.json・user settings.json。settings.json からは一時的な設定ディレクトリで不都合になるキー(enabledPlugins、extraKnownMarketplaces、その別名 additionalMarketplaces、env ブロック内の CLAUDE_CONFIG_DIR)を取り除く。Agent SDK v0.3.232 より前は別名を取り除いていなかった。 v0.3.222 より前は認証情報と .claude.json だけを複写していた
  • Python: 認証情報と .claude.json のみ。user settings.json の apiKeyHelper で認証するアプリは、ストアからの resume で Not logged in になる(managed / project settings の apiKeyHelper は CLAUDE_CONFIG_DIR の影響を受けない場所から読まれるため動く)

ストアに何も無い場合は実際の設定ディレクトリで実行され、結果は渡したオプションによる。

  • resume: 両 SDK とも ID をサブプロセスへ渡し、ストア無しの resume と同じくローカルのトランスクリプトを再開する
  • continue: true(TypeScript): 新しいセッションを始める
  • continue_conversation=True(Python): ローカルの最新セッションから続ける

制約・注意点

  • ミラーへの書き込みは best-effort。 append() が reject した場合、短い待機を挟んで合計 3 回まで再試行する。タイムアウトしたものは再試行しない(元の呼び出しが後から着く可能性があるため)。それでも失敗したらエラーを記録し、{ type: "system", subtype: "mirror_error" } をイテレータへ流し、そのバッチを捨てて処理を続ける
  • 再試行で同じエントリが再送されうるため、append() 側で entry.uuid による重複排除を行う
  • ストアの障害はエージェントを止めない(サブプロセスはローカルへ先に書くため)。ただしストアから resume した実行では、捨てられたバッチは実行終了後どこにも残らない
  • ストアと併用できないオプションが 2 つある。 併用すると起動時に例外になる
    • TypeScript の persistSession: false(ミラーの元になるローカル書き込みを止めるため。Python に同等のオプションは無い)
    • file checkpointing(enableFileCheckpointing / enable_file_checkpointing。バックアップを直接ローカルへ書き、ストアへミラーされない)
  • getSessionMessages は compaction 後の連鎖を返す。 自動 compaction 後は以前のターンが要約に置き換わるため、ストアに 503 件の生エントリがあるセッションでも 18 メッセージしか返らないことがある。生の履歴が要るなら store.load(key) を直接呼ぶ
  • forkSession はバイト単位の複製ではない。 元のエントリを読み、すべての sessionId を書き換え、メッセージ UUID を対応づけ直してから新しい鍵で append する。アダプタ側の複製や CopyObject では古いセッション ID を参照したままになる
  • SDK は自分からストアを削除しない。 保持(TTL、S3 のライフサイクル、定期削除)はアダプタの責任。ローカルのトランスクリプトは cleanupPeriodDays により独立して掃除される。ストアから resume した実行はローカルに残さないため、ストアの保持設定が唯一の保持になる

sessionStore を受け付ける関数(TypeScript)

query() / startup() / listSessions() / getSessionInfo() / getSessionMessages() / renameSession() / tagSession() / deleteSession() / forkSession() / listSubagents() / getSubagentMessages()

Python では ClaudeAgentOptions の session_store で query() をストアに対して動かす。他の操作はストアを引数に取る個別の関数(list_sessions_from_store()、get_session_info_from_store()、get_session_messages_from_store()、list_subagents_from_store()、get_subagent_messages_from_store()、rename_session_via_store()、tag_session_via_store()、delete_session_via_store()、fork_session_via_store())を使う。startup() に Python の同等物は無い。 Python SDK リファレンスにある単独の関数(list_sessions() など)はローカルのセッションファイルを読む。

関連

  • facts/claude-code/agent-sdk.md
  • facts/claude-code/agent-sdk-typescript.md
  • facts/claude-code/agent-sdk-python.md
  • facts/claude-code/agent-sdk-subagents.md