> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qoder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ファイル Checkpoint と Rewind

ファイル checkpoint は、1 回のセッションでツールが変更したローカルファイルの状態を記録するために使用されます。有効化すると、ホストアプリケーションは `QoderSDKClient.rewind_files(user_message_id, ...)` を呼び出して、追跡対象のファイルを特定のユーザーメッセージの処理開始時の状態へロールバックできます。

この 2 つの機能は連携して使用する必要があります。`enable_file_checkpointing=True` を有効化していない場合、`rewind_files()` には利用可能なファイルスナップショットが存在しません。

<div id="ファイル-checkpoint-の有効化" />

<div id="ファイルcheckpointの有効化" />

## ファイル checkpoint の有効化

`enable_file_checkpointing` は `QoderAgentOptions` のフィールドです。後続でロールバックが必要な場合は、`QoderSDKClient` で同じアクティブなセッションを保ち、options で checkpoint を有効化します：

```python theme={null}
from qoder_agent_sdk import QoderAgentOptions, QoderSDKClient


options = QoderAgentOptions(
    cwd="/path/to/project",
    enable_file_checkpointing=True,
    extra_args={"replay-user-messages": None},
    allowed_tools=["Read", "Edit", "Write"],
    permission_mode="acceptEdits",
)

async with QoderSDKClient(options=options) as client:
    await client.query("Refactor src/foo.py into a cleaner implementation.")
    async for message in client.receive_response():
        ...
```

`extra_args={"replay-user-messages": None}` は checkpoint を有効化するスイッチではありません。これはレスポンスストリーム内で `UserMessage` をリプレイし、ロールバックのアンカーとなる `uuid` を含めるためのものです。アプリケーションでユーザーが「このターンの前に戻る」を選択できるようにする場合は、通常これも同時に設定すべきです。

<div id="checkpoint-id-の取得" />

<div id="checkpointidの取得" />

## checkpoint ID の取得

`rewind_files()` はユーザーメッセージ ID をアンカーとして使用します。Python SDK では、レスポンスストリームの `UserMessage.uuid` からこの ID を取得するのが一般的です：

```python theme={null}
from qoder_agent_sdk import (
    QoderAgentOptions,
    QoderSDKClient,
    ResultMessage,
    UserMessage,
)


options = QoderAgentOptions(
    cwd="/path/to/project",
    enable_file_checkpointing=True,
    extra_args={"replay-user-messages": None},
    allowed_tools=["Write"],
    permission_mode="acceptEdits",
)

checkpoint_id: str | None = None

async with QoderSDKClient(options=options) as client:
    await client.query("Rewrite notes.txt as a two-line summary.")

    async for message in client.receive_response():
        if (
            isinstance(message, UserMessage)
            and message.uuid
            and message.parent_tool_use_id is None
        ):
            checkpoint_id = message.uuid

        if isinstance(message, ResultMessage) and message.is_error:
            raise RuntimeError(message.result or "Query failed.")

    if checkpoint_id is None:
        raise RuntimeError("No user message UUID was returned.")
```

`checkpoint_id` はユーザーメッセージの `uuid` であり、`session_id` でも `ResultMessage` の ID でもありません。これは当該 checkpoint を生成したセッションのコンテキスト内でのみ有効です。他のセッションではこの ID を直接ロールバックに使用できません。

<div id="dry-run-プレビュー" />

<div id="dryrunプレビュー" />

## Dry run プレビュー

ロールバックを実行する前に、まず `dry_run=True` で影響範囲をプレビューすることを推奨します。Dry run はファイルを変更しないため、確認ダイアログや監査ログに適しています。

```python theme={null}
preview = await client.rewind_files(checkpoint_id, dry_run=True)

if not preview["canRewind"]:
    print(preview.get("error", "Unable to rewind files."))
else:
    print(
        {
            "files": len(preview.get("filesChanged", [])),
            "insertions": preview.get("insertions", 0),
            "deletions": preview.get("deletions", 0),
        }
    )

    for file_path in preview.get("filesChanged", []):
        print(f"will be reverted: {file_path}")
```

`RewindFilesResult` には以下のフィールドが含まれます：

| フィールド          | 型           | 説明                                               |
| -------------- | ----------- | ------------------------------------------------ |
| `canRewind`    | `bool`      | ロールバックを実行できるかどうか。dry run 失敗時は例外を投げず、このフィールドで示される |
| `error`        | `str`       | `canRewind=False` 時の診断メッセージ                      |
| `filesChanged` | `list[str]` | これから復元される、または既に復元されたファイルパスのリスト                   |
| `insertions`   | `int`       | ロールバック動作で取り消される追加行数の合計                           |
| `deletions`    | `int`       | ロールバック動作で取り消される削除行数の合計                           |

現在の戻り値には影響を受けたファイルリストと行レベルの集計統計のみが含まれており、ファイルごとの具体的な diff は返しません。ファイルごとの差分を表示する必要がある場合は、まず dry run で `filesChanged` を取得し、独自のワークスペース diff ロジックと組み合わせて表示してください。

<div id="ロールバックの実行" />

## ロールバックの実行

影響範囲を確認したら、`dry_run` を渡さずにロールバックを実行できます：

```python theme={null}
result = await client.rewind_files(checkpoint_id)

if result["canRewind"]:
    print(result.get("filesChanged", []))
```

ロールバックは checkpoint で追跡されたローカルファイルの状態のみを復元し、会話履歴をロールバックすることはありません。つまり、モデルは以前のセッションのコンテキストを引き続き保持しています。UI は `filesChanged` に基づいてエディタ、ファイルツリー、または diff ビューを自身でリフレッシュする必要があります。

<div id="失敗のセマンティクス" />

## 失敗のセマンティクス

| 呼び出し形式                                        | ロールバック不可時の動作                                            |
| --------------------------------------------- | ------------------------------------------------------- |
| `await client.rewind_files(id, dry_run=True)` | `{"canRewind": False, "error": ...}` を返し、診断を直接表示するのに適する |
| `await client.rewind_files(id)`               | 例外を投げるため、呼び出し元はキャッチして失敗理由を表示すべき                         |

```python theme={null}
try:
    await client.rewind_files(checkpoint_id)
except Exception as exc:
    print(str(exc))
```

よくある失敗原因：`enable_file_checkpointing` が有効化されていない、渡された ID が有効なユーザーメッセージ UUID ではない、その ID が現在のセッションに属していない、対象メッセージにロールバック可能なファイルスナップショットが存在しない。

<div id="settings-との関係" />

<div id="settingsとの関係" />

## Settings との関係

`QoderAgentOptions.settings` は `enable_file_checkpointing` と同時に使用できます：

```python theme={null}
options = QoderAgentOptions(
    cwd="/path/to/project",
    settings={"theme": "dark"},
    enable_file_checkpointing=True,
)
```

`enable_file_checkpointing=True` の場合、SDK は CLI に渡される settings に `general.fileCheckpointing.enabled = True` をマージします。既に他の settings フィールドが存在する場合は保持されます。既に `fileCheckpointing` 設定が存在する場合は、`enabled` は SDK オプションが優先されます。

<div id="境界" />

## 境界

* ローカルファイル checkpoint のみをロールバックします。MCP ツール、リモートサービス、データベースなどの外部副作用は取り消されません。
* `Bash` を介して直接ファイルに書き込まれた変更は、ロールバック可能なファイルスナップショットとしては扱われません。
* ファイル内容は復元できますが、ディレクトリ作成のようなディレクトリレベルの副作用は必ずしも取り消されません。
* checkpoint ID はセッションに紐付きます。同じセッションを復元すれば対応する ID を引き続き使用できますが、異なるセッション間では混用できません。

<div id="フィールドクイックリファレンス" />

## フィールドクイックリファレンス

| エントリ                                                          | 型                | 説明                    |                                                                      |
| ------------------------------------------------------------- | ---------------- | --------------------- | -------------------------------------------------------------------- |
| `QoderAgentOptions.enable_file_checkpointing`                 | \`bool           | None\`                | ファイル checkpoint を有効化し、`rewind_files()` で使用                           |
| `QoderAgentOptions.extra_args`                                | \`dict\[str, str | None]\`               | `{"replay-user-messages": None}` を渡すとストリームで `UserMessage.uuid` を取得可能 |
| `QoderSDKClient.rewind_files(user_message_id, dry_run=False)` | async method     | ファイルロールバックのプレビューまたは実行 |                                                                      |

<div id="ベストプラクティス" />

## ベストプラクティス

* **ユーザーメッセージ UUID を保存する**：`UserMessage.uuid` を UI のメッセージ記録に紐付け、テキスト内容で逆引きするのを避けてください。
* **先に dry run、その後実行**：影響を受けるファイルと統計を先に表示し、ユーザーに確認させてからロールバックしてください。
* **ロールバック後に UI をリフレッシュ**：`filesChanged` に基づいて関連ファイル状態を再ロードしてください。
* **失敗時に `error` を表示**：dry run が返す `error` は通常、ユーザー向け診断としてそのまま使用できます。
