Skip to main content

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

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

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

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.
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:
FieldTypeDescription
canRewindboolWhether the rewind can be performed. Dry run does not throw on failure; this field signals the result
errorstrDiagnostic message when canRewind=False
filesChangedlist[str]List of file paths that will be reverted, or have already been reverted
insertionsintTotal inserted lines that the rewind will undo (aggregate)
deletionsintTotal 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.

Executing Rewind

Once you have confirmed the impact scope, omit dry_run to execute the rewind:
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.

Failure Semantics

Call formBehavior 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
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.

Settings Relationship

QoderAgentOptions.settings can be used together with enable_file_checkpointing:
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.

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.

Field Reference

Entry pointTypeDescription
QoderAgentOptions.enable_file_checkpointing`boolNone`Enables file checkpoint for use with rewind_files()
QoderAgentOptions.extra_args`dict[str, strNone]`Pass {"replay-user-messages": None} to receive UserMessage.uuid in the stream
QoderSDKClient.rewind_files(user_message_id, dry_run=False)async methodPreview or execute file rewind

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.