Claude Media
Agent SDK File Checkpointingでファイル変更を巻き戻す

Agent SDK File Checkpointingでファイル変更を巻き戻す

Claude Agent SDKのenableFileCheckpointingでファイル変更を追跡し、rewindFiles()で任意の時点へ戻す実装手順と5つの制限をまとめます。

Agent SDKのFile Checkpointingとは

File Checkpointingは、Agent SDKのセッション中にWrite・Edit・NotebookEditツールで加えたファイル変更を追跡し、任意の時点へ巻き戻す機能です。enableFileCheckpointing(Python: enable_file_checkpointing)をtrueにすると、この3つのツールがファイルを書き換える直前にSDKがバックアップを作成します。応答ストリームに流れるユーザーメッセージにはチェックポイントのUUIDが付き、そのUUIDをrewindFiles()(Python: rewind_files())に渡すとファイルを巻き戻せます。

会話履歴とファイルは別々に管理されている点が要です。rewindFiles()が戻すのはディスク上のファイルだけで、会話の文脈やコンテキストはそのまま残ります。「実装を1つ試してダメだったので元のファイルに戻すが、会話はそのまま続けたい」という場面にちょうど合う設計です。

Claude Codeを対話CLIとして使っている場合は、/rewindコマンドとsettings.jsonfileCheckpointingEnabledが同じ仕組みの入り口になります。CLIでの操作や制限はClaude Code checkpointの制限fileCheckpointingEnabledの無効化にまとめてあります。本記事は、自分のアプリケーションからAgent SDKのAPIとしてチェックポイントを操作する実装に絞ります。

巻き戻すと具体的に何が起きるか

rewindFiles()を呼ぶと、Claude Codeはそのチェックポイント以降に作成したファイルを削除し、変更したファイルをチェックポイント時点の内容に復元します。処理対象になるのはあくまでファイルシステム上の状態で、会話履歴・コンテキストはこの操作を挟んでも一切変化しません。rewindFiles()を呼んだ直後にセッションを続けても、Claudeは巻き戻し前の会話をそのまま覚えています。

この「会話とファイルの分離」は設計上の要点です。たとえば実装方針をA案で1手番進めた後にB案を試したい場合、ファイルだけをA案着手前の状態へ戻しつつ、「A案は不採用でB案を試す」という文脈は会話に残したまま次の指示を出せます。ファイルと会話の両方を戻したい場合は、セッション自体をforkして新しい枝を作る方法と組み合わせます。

実装前に必要なもの

Agent SDKのインストールとセットアップが済んでいる前提で進めます。Python・TypeScriptどちらのSDKでも同じ3ステップで実装できます。

ステップ1: チェックポイントを有効にする

enableFileCheckpointingに加えて、extraArgs(Python: extra_args)にreplay-user-messagesを指定します。これを忘れると次のステップでUUIDが取得できません。

import { query } from "@anthropic-ai/claude-agent-sdk";
 
const response = query({
  prompt: "認証モジュールをリファクタリングして",
  options: {
    enableFileCheckpointing: true,
    permissionMode: "acceptEdits",
    extraArgs: { "replay-user-messages": null }
  }
});
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
 
options = ClaudeAgentOptions(
    enable_file_checkpointing=True,
    permission_mode="acceptEdits",
    extra_args={"replay-user-messages": None},
)
 
async with ClaudeSDKClient(options) as client:
    await client.query("認証モジュールをリファクタリングして")

permissionMode: "acceptEdits"はファイル編集を確認なしで通す設定です。チェックポイントを試す間はこれで自動承認しておくと、編集が実際にトリガーされて挙動を確認しやすくなります。

ステップ2: チェックポイントUUIDとセッションIDを取得する

replay-user-messagesを設定すると、応答ストリームの各ユーザーメッセージにuuidが付きます。多くの用途では、最初のユーザーメッセージのUUIDだけを覚えておけば、変更前の状態に戻す復元点として使えます。複数の復元点を使い分けたい場合は後述の「複数の復元点を保持する」を参照してください。

let checkpointId: string | undefined;
let sessionId: string | undefined;
 
for await (const message of response) {
  if (message.type === "user" && message.uuid && !checkpointId) {
    checkpointId = message.uuid;
  }
  if ("session_id" in message && !sessionId) {
    sessionId = message.session_id;
  }
}

セッションIDはストリーム完了後に巻き戻したいときだけ必要です。ストリームを処理している最中にrewindFiles()を呼ぶ「リスクの高い操作の前にチェックポイントを打つ」パターン(後述)では、セッションIDの取得自体を省略できます。

ステップ3: セッションを再開してファイルを巻き戻す

ストリームが完了したあとに巻き戻すには、空プロンプトでセッションを再開し、開いた接続の中でrewindFiles()を呼びます。

const rewindQuery = query({
  prompt: "",
  options: { ...opts, resume: sessionId }
});
 
for await (const msg of rewindQuery) {
  await rewindQuery.rewindFiles(checkpointId);
  break;
}
async with ClaudeSDKClient(
    ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
) as client:
    await client.query("")
    async for message in client.receive_response():
        await client.rewind_files(checkpoint_id)
        break

ストリームを処理し終えたrewindFiles()を呼ぶと、CLIプロセスへの接続がすでに閉じているため"ProcessTransport is not ready for writing"というエラーになります。巻き戻しは必ず、セッションを再開して開いた新しい接続の中で行います。

セッションIDとチェックポイントIDさえ分かれば、CLIから直接巻き戻すことも可能です。この場合はclaude実行ファイル(Agent SDKパッケージには含まれず、別途Claude Codeのインストールが必要)を使い、SDKが内部で立てる環境変数CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTINGを自分で指定します。

CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true \
  claude -p --resume <session-id> --rewind-files <checkpoint-uuid>

--rewind-filesclaude --helpの出力には表示されませんが、CLIは受け付けます。巻き戻しが成功するとFiles rewound to state at message <checkpoint-uuid>と表示され、プロンプトを送らずに終了します。

よく使う2つのパターン

リスクの高い操作の前にチェックポイントを打つ

直近のチェックポイントIDだけを保持し続け、エージェントの手番が進むたびに上書きします。何か問題が起きたら、直前の安全な状態へすぐ戻してループを抜けられます。

let safeCheckpoint: string | undefined;
 
for await (const message of response) {
  if (message.type === "user" && message.uuid) {
    safeCheckpoint = message.uuid;
  }
  if (yourRevertCondition && safeCheckpoint) {
    await response.rewindFiles(safeCheckpoint);
    break;
  }
}

yourRevertConditionは、エラー検知やバリデーション失敗など自分のアプリケーション側のロジックに置き換えます。

複数の復元点を保持する

エージェントが複数の手番にまたがって変更を加える場合、最初まで一気に戻すのではなく、特定の手番まで戻したいことがあります。全チェックポイントを配列にメタデータ付きで貯めておけば、セッション完了後にどの時点へでも戻せます。

interface Checkpoint {
  id: string;
  description: string;
}
 
const checkpoints: Checkpoint[] = [];
 
for await (const message of response) {
  if (message.type === "user" && message.uuid) {
    checkpoints.push({ id: message.uuid, description: `手番${checkpoints.length + 1}後` });
  }
}

たとえば1手番目でリファクタリング、2手番目でテスト追加が行われた場合、リファクタリングは残してテストだけ取り消す、といった使い分けができます。

5つの制限事項

制限内容
Write / Edit / NotebookEditのみ内容Bashコマンド経由の変更(sed -iecho >等)は追跡されない
サブエージェントの編集内容サブエージェントが加えた変更は追跡・復元されない。例外はフォアグラウンドで動くcontext: forkのskill。追跡外の変更はgitで戻す
同一セッション限定内容チェックポイントは作成元のセッションに紐づく
ファイル内容のみ内容ディレクトリの作成・移動・削除は巻き戻しの対象外
ローカルファイルのみ内容リモートやネットワーク越しのファイルは追跡されない

巻き戻し時、Claude Codeはシンボリックリンク・ハードリンク・その他の非通常ファイルになっているパスをスキップします。バックアップ時点からディレクトリ構造が変わっていて解決できないパス、バックアップを安全に読み取れないパスも同様です。スキップされたパスの総数はRewindFilesResultskippedLinksフィールドに入ります。このスキップ処理はv2.1.216以降の挙動で、それより前のバージョンではリンク先まで書いて消していました。

Git・CLIのcheckpointとの使い分け

自分のアプリケーションにファイルの巻き戻しを組み込むとき、SDKのFile Checkpointingとgitのどちらを使うべきか迷う場面があります。

用途向いている手段理由
エージェントの1手番前まで、会話は続けたまま戻す向いている手段Agent SDKのrewindFiles()理由会話コンテキストを保ったままファイルだけ戻せる
恒久的な変更履歴として残す・チームで共有する向いている手段git理由チェックポイントはセッションに紐づき、セッション外では参照できない
Bashコマンドで加えた変更を取り消す向いている手段git理由File Checkpointingの追跡対象外(下記の制限を参照)
対話CLIの利用者自身が/rewindで操作する向いている手段Claude Codeの/rewindコマンド理由Agent SDKのAPI呼び出しが不要で、CLI単体で完結する

「セッション内の試行錯誤を素早く戻す」がFile Checkpointingの守備範囲で、恒久的な履歴管理やBash経由の変更はこれまでどおりgitが担当します。両者は競合する手段ではなく、役割が異なります。

よくあるつまずき

  • UUIDがundefinedのまま: extraArgs(またはextra_args)に{"replay-user-messages": null}を指定し忘れているケースがほとんどです。この設定がないとユーザーメッセージにUUIDが付きません
  • "No file checkpoint found for this message"エラー: 元のセッションでenableFileCheckpointingtrueにしないまま実行していたか、セッションが完了する前に再開・巻き戻しを試みた場合に起きます。最初のユーザーメッセージUUIDを取得し、セッションを完了させてから、空プロンプトで再開して1回だけrewindFiles()を呼ぶ手順を守ります
  • "File rewinding is not enabled"エラー: 非対話の巻き戻しでチェックポイントが有効になっていない場合に出ます。素のclaude -p--rewind-filesを付けただけ、あるいは巻き戻しを実行するセッション(再開したセッションを含む)のオプションでチェックポイントを有効にしていない場合です。SDKはenableFileCheckpointingが有効なセッションでのみ内部的に環境変数をセットするため、CLI単体では自分で環境変数を渡す必要があります
  • 古いSDKバージョン: enableFileCheckpointingrewindFiles()が見当たらない場合はSDKが古い可能性があります。Pythonはpip install --upgrade claude-agent-sdk、TypeScriptはnpm install @anthropic-ai/claude-agent-sdk@latestで更新します

まとめ

Agent SDKのFile Checkpointingは、enableFileCheckpointingextraArgsreplay-user-messagesを設定し、ユーザーメッセージのUUIDを復元点として保持し、必要なときに空プロンプトでセッションを再開してrewindFiles()を呼ぶ、という3ステップで実装できます。追跡対象はWrite・Edit・NotebookEditの3ツールに限られ、Bash経由の変更やサブエージェントの編集(フォアグラウンドのforkスキルを除く)は対象外です。会話履歴とファイル状態は独立して管理されるため、ファイルだけを戻して会話は続ける、という使い方が前提の設計になっています。

よくある質問

Bashツールで削除・リネームしたファイルも巻き戻せますか

戻せません。File Checkpointingが追跡するのはWrite・Edit・NotebookEditツール経由の変更だけです。Bashコマンドで消したりリネームしたりしたファイルは対象外で、取り消すにはgitを使う必要があります。

サブエージェントが編集したファイルは復元されますか

原則されません。例外は、context: forkを設定したskillがフォアグラウンドで動いた場合だけです。それ以外のサブエージェントの編集(バックグラウンドで動くforkスキルや通常のSub-agentなど)は追跡対象外で、gitで戻す必要があります。

この機能はいつから使えますか

enableFileCheckpointingrewindFiles()自体は現行のAgent SDKで利用できます。スキップされたリンクをskippedLinksで件数報告する挙動はClaude Code v2.1.216以降が対象で、それより前のバージョンではリンク先のファイルまで書き換えて削除していました。

CLIの/rewindコマンドとの違いは何ですか

/rewindはClaude Codeの対話セッションでユーザーが直接操作するコマンドで、settings.jsonfileCheckpointingEnabledで有効・無効を切り替えます。Agent SDKのenableFileCheckpointingは、自分のアプリケーションのコードから同じ仕組みをプログラム的に呼び出す入り口です。裏側の追跡・復元ロジックは共通ですが、操作する主体が対話ユーザーかアプリケーションのコードかが異なります。

この記事を共有:XはてブLinkedIn