> ## 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.

# File Checkpoint and Rewind

File checkpoint records the state of local files modified by tools during a session. Once enabled, the host application can call `QoderSDKClient.rewind_files(user_message_id, ...)` to roll back tracked files to the state they were in when a particular user message started processing.

These two capabilities must be used together: without `enable_file_checkpointing=True`, `rewind_files()` has no file snapshots to use.

<div id="enabling-file-checkpoint" />

<div id="enablingfilecheckpoint" />

## Enabling File Checkpoint

`enable_file_checkpointing` is a field on `QoderAgentOptions`. To enable subsequent rewinds, use `QoderSDKClient` to keep the same active session and turn on checkpointing in options:

```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}` is not the switch that enables checkpointing. Its purpose is to replay `UserMessage` events in the response stream, carrying the `uuid` that can serve as a rewind anchor. If your application needs the user to click "go back to before this turn," you typically set both.

<div id="obtaining-the-checkpoint-id" />

<div id="obtainingthecheckpointid" />

## Obtaining the Checkpoint ID

`rewind_files()` uses the user message ID as its anchor. The common pattern in the Python SDK is to capture this ID from `UserMessage.uuid` in the response stream:

```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` is the user message's `uuid` — not the `session_id`, and not the `ResultMessage` ID. It is only valid in the session context that produced the checkpoint; other sessions cannot use this ID to rewind directly.

<div id="dry-run-preview" />

<div id="dryrunpreview" />

## Dry Run Preview

Before executing a rewind, it is recommended to preview the impact with `dry_run=True`. A dry run does not modify files, making it suitable for confirmation dialogs or audit logs.

```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` contains these fields:

| Field          | Type        | Description                                                                                           |
| -------------- | ----------- | ----------------------------------------------------------------------------------------------------- |
| `canRewind`    | `bool`      | Whether the rewind can be performed. Dry run does not throw on failure; this field signals the result |
| `error`        | `str`       | Diagnostic message when `canRewind=False`                                                             |
| `filesChanged` | `list[str]` | List of file paths that will be reverted, or have already been reverted                               |
| `insertions`   | `int`       | Total inserted lines that the rewind will undo (aggregate)                                            |
| `deletions`    | `int`       | Total deleted lines that the rewind will undo (aggregate)                                             |

The current return value only contains the list of affected files and aggregate line-level statistics — it does not return per-file diffs. When you need to display per-file differences, do a dry run first to obtain `filesChanged`, then combine it with your own workspace diff logic for display.

<div id="executing-rewind" />

<div id="executingrewind" />

## Executing Rewind

Once you have confirmed the impact scope, omit `dry_run` to execute the rewind:

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

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

Rewind only restores the local file state tracked by the checkpoint; it does not roll back conversation history. That is, the model still retains context from earlier turns; the UI must refresh the editor, file tree, or diff view itself based on `filesChanged`.

<div id="failure-semantics" />

<div id="failuresemantics" />

## Failure Semantics

| Call form                                     | Behavior when rewind is not possible                                                 |
| --------------------------------------------- | ------------------------------------------------------------------------------------ |
| `await client.rewind_files(id, dry_run=True)` | Returns `{"canRewind": False, "error": ...}`, suitable for direct diagnostic display |
| `await client.rewind_files(id)`               | Raises an exception; the caller should catch it and display the failure reason       |

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

Common failure causes include: `enable_file_checkpointing` is not enabled, the supplied ID is not a valid user message UUID, the ID does not belong to the current session, or the target message has no rewindable file snapshots.

<div id="settings-relationship" />

<div id="settingsrelationship" />

## Settings Relationship

`QoderAgentOptions.settings` can be used together with `enable_file_checkpointing`:

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

When `enable_file_checkpointing=True`, the SDK merges `general.fileCheckpointing.enabled = True` into the settings passed to the CLI. Other settings fields are preserved; if a `fileCheckpointing` configuration already exists, `enabled` is taken from the SDK option.

<div id="boundaries" />

## Boundaries

* Only local file checkpoints are rewound; external side effects from MCP tools, remote services, or databases are not undone.
* File changes made by writing files directly through `Bash` are not treated as rewindable file snapshots.
* File contents can be restored; directory-level side effects such as directory creation may not be undone.
* The checkpoint ID is bound to the session. After resuming the same session, the corresponding ID can still be used; it cannot be mixed across different sessions.

<div id="field-reference" />

<div id="fieldreference" />

## Field Reference

| Entry point                                                   | Type             | Description                    |                                                                                   |
| ------------------------------------------------------------- | ---------------- | ------------------------------ | --------------------------------------------------------------------------------- |
| `QoderAgentOptions.enable_file_checkpointing`                 | \`bool           | None\`                         | Enables file checkpoint for use with `rewind_files()`                             |
| `QoderAgentOptions.extra_args`                                | \`dict\[str, str | None]\`                        | Pass `{"replay-user-messages": None}` to receive `UserMessage.uuid` in the stream |
| `QoderSDKClient.rewind_files(user_message_id, dry_run=False)` | async method     | Preview or execute file rewind |                                                                                   |

<div id="best-practices" />

<div id="bestpractices" />

## Best Practices

* **Save the user message UUID**: Bind `UserMessage.uuid` to your UI's message records, instead of looking them up by text.
* **Dry run before executing**: Show the affected files and statistics first, then have the user confirm the rewind.
* **Refresh UI after rewind**: Reload relevant file state based on `filesChanged`.
* **Show `error` on failure**: The `error` returned by dry run is typically suitable as user-visible diagnostics.
