SpyBara
Go Premium

agent-sdk/file-checkpointing.md 2026-10-31 23:02 UTC to 2026-11-01 23:57 UTC

No changes for this document in this history range.

2026

Отмотка изменений файлов с помощью checkpointing

Отслеживайте изменения файлов во время сеансов агента и восстанавливайте файлы в любое предыдущее состояние

File checkpointing отслеживает изменения файлов, внесённые через инструменты Write, Edit и NotebookEdit во время сеанса агента, позволяя вам отмотать файлы в любое предыдущее состояние. Чтобы увидеть, как отмотка восстанавливает файл, перейдите к интерактивному примеру.

[!WARNING]

rewindFiles() не отменяет изменения, которые sed -i или другая команда Bash внесла в файл, если только этот файл не был изменён в основном диалоге таким инструментом, как Write или Edit. Правки субагентов не отслеживаются, тогда как правки скилла с context: fork, работающего на переднем плане, отслеживаются.

Как работает checkpointing

Когда вы включаете file checkpointing, SDK создаёт резервные копии файлов перед их изменением через инструменты Write, Edit или NotebookEdit. Пользовательские сообщения в потоке ответов включают UUID checkpoint, который вы можете использовать как точку восстановления.

[!NOTE]

File rewinding восстанавливает файлы на диске в предыдущее состояние. Это не отматывает саму беседу. История беседы и контекст остаются нетронутыми после вызова rewindFiles() (TypeScript) или rewind_files() (Python).

Когда вы отматываете к checkpoint, Claude Code удаляет файлы, которые он создал, и восстанавливает файлы, которые он изменил, до их содержимого в этот момент. Claude Code пропускает отслеживаемый путь, который является символической ссылкой, жёсткой ссылкой или другим нерегулярным файлом. Он также пропускает отслеживаемый файл, родительский каталог которого больше не разрешается в его местоположение во время checkpoint, или чей резервную копию он не может безопасно прочитать. RewindFilesResult подсчитывает каждый пропущенный путь в своём поле skippedLinks. Пропуск требует Claude Code v2.1.216 или позже; до v2.1.216 rewind писал и удалял через ссылки на отслеживаемых путях.

Реализация checkpointing

Чтобы использовать file checkpointing, включите его в ваших параметрах, захватите UUID checkpoint из потока ответов, затем вызовите rewindFiles() (TypeScript) или rewind_files() (Python) когда вам нужно восстановить.

Примеры в этом разделе используют приглашение "Refactor the authentication module". Запустите их в проекте, который содержит модуль аутентификации, или измените приглашение на имена файлов, которые существуют в вашем проекте, чтобы вы могли наблюдать изменения файлов и видеть, как отмотка восстанавливает их.

Этот пример включает checkpointing, захватывает UUID checkpoint и ID сеанса из потока ответов, затем возобновляет сеанс позже для отмотки файлов. Пронумерованные комментарии примера соответствуют шагам, которые следуют ниже.

import asyncio
from claude_agent_sdk import (
    ClaudeSDKClient,
    ClaudeAgentOptions,
    UserMessage,
    ResultMessage,
)


async def main():
    # Step 1: Enable checkpointing
    options = ClaudeAgentOptions(
        enable_file_checkpointing=True,
        permission_mode="acceptEdits",  # Auto-accept file edits without prompting
        extra_args={
            "replay-user-messages": None
        },  # Required to receive checkpoint UUIDs in the response stream
    )

    checkpoint_id = None
    session_id = None

    # Run the query and capture checkpoint UUID and session ID
    async with ClaudeSDKClient(options) as client:
        await client.query("Refactor the authentication module")

        # Step 2: Capture checkpoint UUID from the first user message
        async for message in client.receive_response():
            if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
                checkpoint_id = message.uuid
            if isinstance(message, ResultMessage) and not session_id:
                session_id = message.session_id

    # Step 3: Later, rewind by resuming the session with an empty prompt
    if checkpoint_id and session_id:
        async with ClaudeSDKClient(
            ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
        ) as client:
            await client.query("")  # Empty prompt to open the connection
            async for message in client.receive_response():
                await client.rewind_files(checkpoint_id)
                break
        print(f"Rewound to checkpoint: {checkpoint_id}")


asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";

async function main() {
  // Step 1: Enable checkpointing
  const opts = {
    enableFileCheckpointing: true,
    permissionMode: "acceptEdits" as const, // Auto-accept file edits without prompting
    extraArgs: { "replay-user-messages": null } // Required to receive checkpoint UUIDs in the response stream
  };

  const response = query({
    prompt: "Refactor the authentication module",
    options: opts
  });

  let checkpointId: string | undefined;
  let sessionId: string | undefined;

  // Step 2: Capture checkpoint UUID from the first user message
  try {
    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;
      }
    }
  } catch (error) {
    // A single-shot query() throws after yielding an error result. If the
    // failure was an error result, sessionId and checkpointId were already
    // captured by the loop above; connection or process failures yield no
    // result message.
    console.error(`Session ended with an error: ${error}`);
  }

  // Step 3: Later, rewind by resuming the session with an empty prompt
  if (checkpointId && sessionId) {
    const rewindQuery = query({
      prompt: "", // Empty prompt to open the connection
      options: { ...opts, resume: sessionId }
    });

    for await (const msg of rewindQuery) {
      await rewindQuery.rewindFiles(checkpointId);
      break;
    }
    console.log(`Rewound to checkpoint: ${checkpointId}`);
  }
}

main();

1. Включение checkpointing

Настройте параметры SDK для включения checkpointing и получения UUID checkpoint:

Параметр Python TypeScript Описание
Включить checkpointing enable_file_checkpointing=True enableFileCheckpointing: true Отслеживает изменения файлов для отмотки
Получить UUID checkpoint extra_args={"replay-user-messages": None} extraArgs: { 'replay-user-messages': null } Требуется для получения UUID пользовательских сообщений в потоке

Основной пример в разделе Реализация checkpointing также устанавливает режим разрешений на acceptEdits, который одобряет редактирование файлов агентом без приглашения. Для получения дополнительной информации см. Режимы разрешений.

2. Захват UUID checkpoint и ID сеанса

С установленным параметром replay-user-messages, каждое пользовательское сообщение в потоке ответов имеет UUID, который служит checkpoint.

Для большинства случаев использования захватите UUID первого пользовательского сообщения (message.uuid); отмотка к нему восстанавливает отслеживаемые файлы в их исходное состояние. Чтобы сохранить несколько checkpoint и отмотать к промежуточным состояниям, см. Несколько точек восстановления.

Захват ID сеанса (message.session_id) является необязательным; вам он нужен только если вы хотите отмотать позже, после завершения потока. Если вы вызываете rewindFiles() немедленно, пока всё ещё обрабатываете сообщения (как это делает пример в Checkpoint перед рискованными операциями), вы можете пропустить захват ID сеанса.

3. Отмотка файлов

Чтобы отмотать после завершения потока, возобновите сеанс с пустым приглашением и вызовите rewind_files() (Python) или rewindFiles() (TypeScript) с вашим UUID checkpoint. Включите checkpointing в параметрах возобновленного сеанса также, и вызовите отмотку изнутри цикла ответов. Вы также можете отмотать во время потока. См. Checkpoint перед рискованными операциями для этого паттерна.

Если вы захватили ID сеанса и UUID checkpoint, вы также можете отмотать из CLI. Эта команда требует исполняемого файла claude, который поставляется с установкой Claude Code. SDK включает checkpointing для вас, но когда вы запускаете claude -p напрямую, вы должны установить переменную окружения CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING:

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

Флаг --rewind-files не отображается в выводе claude --help, но CLI принимает его, как показано. Когда отмотка успешна, команда выводит Files rewound to state at message <checkpoint-uuid> и завершается без отправки приглашения.

Общие паттерны

Эти паттерны показывают различные способы захвата и использования UUID checkpoint в зависимости от вашего случая использования.

Checkpoint перед рискованными операциями

Этот паттерн сохраняет только самый последний UUID checkpoint, обновляя его перед каждым ходом агента. Если что-то пойдёт не так во время обработки, вы можете немедленно отмотать к последнему безопасному состоянию и выйти из цикла.

Перед запуском этого примера замените your_revert_condition (Python) или yourRevertCondition (TypeScript) на вашу собственную проверку, такую как обнаружение ошибок или сбой валидации; заполнитель не определён в примере.

import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, UserMessage


async def main():
    options = ClaudeAgentOptions(
        enable_file_checkpointing=True,
        permission_mode="acceptEdits",
        extra_args={"replay-user-messages": None},
    )

    safe_checkpoint = None

    async with ClaudeSDKClient(options) as client:
        await client.query("Refactor the authentication module")

        async for message in client.receive_response():
            # Update checkpoint before each agent turn starts
            # This overwrites the previous checkpoint. Only keep the latest
            if isinstance(message, UserMessage) and message.uuid:
                safe_checkpoint = message.uuid

            # Decide when to revert based on your own logic
            # For example: error detection, validation failure, or user input
            if your_revert_condition and safe_checkpoint:
                await client.rewind_files(safe_checkpoint)
                # Exit the loop after rewinding, files are restored
                break


asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";

async function main() {
  const response = query({
    prompt: "Refactor the authentication module",
    options: {
      enableFileCheckpointing: true,
      permissionMode: "acceptEdits" as const,
      extraArgs: { "replay-user-messages": null }
    }
  });

  let safeCheckpoint: string | undefined;

  for await (const message of response) {
    // Update checkpoint before each agent turn starts
    // This overwrites the previous checkpoint. Only keep the latest
    if (message.type === "user" && message.uuid) {
      safeCheckpoint = message.uuid;
    }

    // Decide when to revert based on your own logic
    // For example: error detection, validation failure, or user input
    if (yourRevertCondition && safeCheckpoint) {
      await response.rewindFiles(safeCheckpoint);
      // Exit the loop after rewinding, files are restored
      break;
    }
  }
}

main();

Несколько точек восстановления

Если Claude вносит изменения в несколько ходов, вы можете захотеть отмотать к определённой точке, а не полностью назад. Например, если Claude рефакторит файл в ход один и добавляет тесты в ход два, вы можете захотеть сохранить рефакторинг, но отменить тесты.

Этот паттерн сохраняет все UUID checkpoint в массиве с метаданными. После завершения сеанса вы можете отмотать к любому предыдущему checkpoint:

import asyncio
from dataclasses import dataclass
from datetime import datetime
from claude_agent_sdk import (
    ClaudeSDKClient,
    ClaudeAgentOptions,
    UserMessage,
    ResultMessage,
)


# Store checkpoint metadata for better tracking
@dataclass
class Checkpoint:
    id: str
    description: str
    timestamp: datetime


async def main():
    options = ClaudeAgentOptions(
        enable_file_checkpointing=True,
        permission_mode="acceptEdits",
        extra_args={"replay-user-messages": None},
    )

    checkpoints = []
    session_id = None

    async with ClaudeSDKClient(options) as client:
        await client.query("Refactor the authentication module")

        async for message in client.receive_response():
            if isinstance(message, UserMessage) and message.uuid:
                checkpoints.append(
                    Checkpoint(
                        id=message.uuid,
                        description=f"After turn {len(checkpoints) + 1}",
                        timestamp=datetime.now(),
                    )
                )
            if isinstance(message, ResultMessage) and not session_id:
                session_id = message.session_id

    # Later: rewind to any checkpoint by resuming the session
    if checkpoints and session_id:
        target = checkpoints[0]  # Pick any checkpoint
        async with ClaudeSDKClient(
            ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
        ) as client:
            await client.query("")  # Empty prompt to open the connection
            async for message in client.receive_response():
                await client.rewind_files(target.id)
                break
        print(f"Rewound to: {target.description}")


asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";

// Store checkpoint metadata for better tracking
interface Checkpoint {
  id: string;
  description: string;
  timestamp: Date;
}

async function main() {
  const opts = {
    enableFileCheckpointing: true,
    permissionMode: "acceptEdits" as const,
    extraArgs: { "replay-user-messages": null }
  };

  const response = query({
    prompt: "Refactor the authentication module",
    options: opts
  });

  const checkpoints: Checkpoint[] = [];
  let sessionId: string | undefined;

  try {
    for await (const message of response) {
      if (message.type === "user" && message.uuid) {
        checkpoints.push({
          id: message.uuid,
          description: `After turn ${checkpoints.length + 1}`,
          timestamp: new Date()
        });
      }
      if ("session_id" in message && !sessionId) {
        sessionId = message.session_id;
      }
    }
  } catch (error) {
    // A single-shot query() throws after yielding an error result. If the
    // failure was an error result, sessionId and the checkpoints array were
    // already populated by the loop above; connection or process failures
    // yield no result message.
    console.error(`Session ended with an error: ${error}`);
  }

  // Later: rewind to any checkpoint by resuming the session
  if (checkpoints.length > 0 && sessionId) {
    const target = checkpoints[0]; // Pick any checkpoint
    const rewindQuery = query({
      prompt: "", // Empty prompt to open the connection
      options: { ...opts, resume: sessionId }
    });

    for await (const msg of rewindQuery) {
      await rewindQuery.rewindFiles(target.id);
      break;
    }
    console.log(`Rewound to: ${target.description}`);
  }
}

main();

Попробуйте

Этот полный пример создаёт небольшой служебный файл, просит агента добавить комментарии к документации, показывает вам изменения, затем спрашивает, хотите ли вы отмотать.

Прежде чем начать, убедитесь, что у вас установлен Claude Agent SDK.

1. Создание тестового файла

Создайте новый файл с именем utils.py (Python) или utils.ts (TypeScript) и вставьте следующий код:

def add(a, b):
    return a + b


def subtract(a, b):
    return a - b


def multiply(a, b):
    return a * b


def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b
export function add(a: number, b: number): number {
  return a + b;
}

export function subtract(a: number, b: number): number {
  return a - b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}

export function divide(a: number, b: number): number {
  if (b === 0) {
    throw new Error("Cannot divide by zero");
  }
  return a / b;
}

2. Запуск интерактивного примера

Создайте новый файл с именем try_checkpointing.py (Python) или try_checkpointing.ts (TypeScript) в том же каталоге, что и ваш служебный файл, и вставьте следующий код.

Этот скрипт просит Claude добавить комментарии к документации в ваш служебный файл, затем даёт вам возможность отмотать и восстановить оригинал.

import asyncio
from claude_agent_sdk import (
    ClaudeSDKClient,
    ClaudeAgentOptions,
    UserMessage,
    ResultMessage,
)


async def main():
    # Configure the SDK with checkpointing enabled
    # - enable_file_checkpointing: Track file changes for rewinding
    # - permission_mode: Auto-accept file edits without prompting
    # - extra_args: Required to receive user message UUIDs in the stream
    options = ClaudeAgentOptions(
        enable_file_checkpointing=True,
        permission_mode="acceptEdits",
        extra_args={"replay-user-messages": None},
    )

    checkpoint_id = None  # Store the user message UUID for rewinding
    session_id = None  # Store the session ID for resuming

    print("Running agent to add doc comments to utils.py...\n")

    # Run the agent and capture checkpoint data from the response stream
    async with ClaudeSDKClient(options) as client:
        await client.query("Add doc comments to utils.py")

        async for message in client.receive_response():
            # Capture the first user message UUID - this is our restore point
            if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
                checkpoint_id = message.uuid
            # Capture the session ID so we can resume later
            if isinstance(message, ResultMessage):
                session_id = message.session_id

    print("Done! Open utils.py to see the added doc comments.\n")

    # Ask the user if they want to rewind the changes
    if checkpoint_id and session_id:
        response = input("Rewind to remove the doc comments? (y/n): ")

        if response.lower() == "y":
            # Resume the session with an empty prompt, then rewind
            async with ClaudeSDKClient(
                ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
            ) as client:
                await client.query("")  # Empty prompt opens the connection
                async for message in client.receive_response():
                    await client.rewind_files(checkpoint_id)  # Restore files
                    break

            print(
                "\n✓ File restored! Open utils.py to verify the doc comments are gone."
            )
        else:
            print("\nKept the modified file.")


asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";
import * as readline from "readline";

async function main() {
  // Configure the SDK with checkpointing enabled
  // - enableFileCheckpointing: Track file changes for rewinding
  // - permissionMode: Auto-accept file edits without prompting
  // - extraArgs: Required to receive user message UUIDs in the stream
  const opts = {
    enableFileCheckpointing: true,
    permissionMode: "acceptEdits" as const,
    extraArgs: { "replay-user-messages": null }
  };

  let sessionId: string | undefined; // Store the session ID for resuming
  let checkpointId: string | undefined; // Store the user message UUID for rewinding

  console.log("Running agent to add doc comments to utils.ts...\n");

  // Run the agent and capture checkpoint data from the response stream
  const response = query({
    prompt: "Add doc comments to utils.ts",
    options: opts
  });

  try {
    for await (const message of response) {
      // Capture the first user message UUID - this is our restore point
      if (message.type === "user" && message.uuid && !checkpointId) {
        checkpointId = message.uuid;
      }
      // Capture the session ID so we can resume later
      if ("session_id" in message) {
        sessionId = message.session_id;
      }
    }
  } catch (error) {
    // A single-shot query() throws after yielding an error result. If the
    // failure was an error result, checkpointId and sessionId were already
    // captured by the loop above; connection or process failures yield no
    // result message.
    console.error(`Session ended with an error: ${error}`);
  }

  console.log("Done! Open utils.ts to see the added doc comments.\n");

  // Ask the user if they want to rewind the changes
  if (checkpointId && sessionId) {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });

    const answer = await new Promise<string>((resolve) => {
      rl.question("Rewind to remove the doc comments? (y/n): ", resolve);
    });
    rl.close();

    if (answer.toLowerCase() === "y") {
      // Resume the session with an empty prompt, then rewind
      const rewindQuery = query({
        prompt: "", // Empty prompt opens the connection
        options: { ...opts, resume: sessionId }
      });

      for await (const msg of rewindQuery) {
        await rewindQuery.rewindFiles(checkpointId); // Restore files
        break;
      }

      console.log("\n✓ File restored! Open utils.ts to verify the doc comments are gone.");
    } else {
      console.log("\nKept the modified file.");
    }
  }
}

main();

3. Запуск примера

Запустите скрипт из того же каталога, что и ваш служебный файл.

[!TIP]

Откройте ваш служебный файл (utils.py или utils.ts) в вашей IDE или редакторе перед запуском скрипта. Вы увидите, как файл обновляется в реальном времени, когда агент добавляет комментарии к документации, затем вернётся к оригиналу, когда вы выберете отмотку.

[tab] Python

python try_checkpointing.py

[tab] TypeScript

npx tsx try_checkpointing.ts

Вы увидите, как агент добавляет комментарии к документации, затем появится приглашение, спрашивающее, хотите ли вы отмотать. Если вы выберете да, файл будет восстановлен в его исходное состояние.

Ограничения

File checkpointing имеет следующие ограничения:

Ограничение Описание
Изменения командами Bash Откат не отменяет изменения, внесённые командой Bash, за исключением файлов, которые основной диалог уже редактировал с помощью инструмента, такого как Write или Edit
Правки субагента Правки, которые применяет субагент, не отслеживаются, за исключением скилла с context: fork, работающего на переднем плане. Откат отменяет их только в тех файлах, которые основной диалог уже редактировал; для отката остальных используйте git
Один сеанс Checkpoints привязаны к сеансу, который их создал
Только содержимое файла Создание, перемещение или удаление каталогов не отменяется отмоткой
Локальные файлы Удалённые или сетевые файлы не отслеживаются

Troubleshooting

Параметры checkpointing не распознаны

Если enableFileCheckpointing или rewindFiles() недоступны, вы можете использовать старую версию SDK.

Решение: Обновитесь до последней версии SDK:

  • Python: pip install --upgrade claude-agent-sdk
  • TypeScript: npm install @anthropic-ai/claude-agent-sdk@latest

Пользовательские сообщения не имеют UUID

Если message.uuid имеет значение undefined или отсутствует, вы не получаете UUID checkpoint.

Причина: Параметр replay-user-messages не установлен.

Решение: Добавьте extra_args={"replay-user-messages": None} (Python) или extraArgs: { 'replay-user-messages': null } (TypeScript) в ваши параметры.

Ошибка "No file checkpoint found for this message"

Эта ошибка возникает, когда данные checkpoint не существуют для указанного UUID пользовательского сообщения.

Частые причины:

  • File checkpointing не был включён в исходном сеансе (enable_file_checkpointing или enableFileCheckpointing не был установлен на true)
  • Сеанс не был должным образом завершён перед попыткой возобновления и отмотки

Решение: Убедитесь, что enable_file_checkpointing=True (Python) или enableFileCheckpointing: true (TypeScript) был установлен в исходном сеансе, затем используйте паттерн, показанный в примерах: захватите UUID первого пользовательского сообщения, полностью завершите сеанс, затем возобновите с пустым приглашением и вызовите rewindFiles() один раз.

Ошибка "File rewinding is not enabled"

Эта ошибка возникает, когда вы пытаетесь выполнить неинтерактивную отмотку без включённого checkpointing: запуск простой команды claude -p с --rewind-files, или запуск сеанса SDK, включая возобновленный, чьи параметры не включают checkpointing. SDK устанавливает переменную окружения CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING внутренне только когда enable_file_checkpointing (Python) или enableFileCheckpointing (TypeScript) включены в сеансе, выполняющем отмотку; простой CLI никогда её не устанавливает.

Решение: Для простого CLI установите переменную окружения при запуске команды:

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

Для SDK установите enable_file_checkpointing=True (Python) или enableFileCheckpointing: true (TypeScript) в возобновленном сеансе, как это делается в примерах на этой странице.

Ошибка "ProcessTransport is not ready for writing"

Эта ошибка возникает, когда вы вызываете rewindFiles() или rewind_files() после завершения итерации по ответу. Соединение с процессом CLI закрывается при завершении цикла.

Решение: Возобновите сеанс с пустым приглашением, затем отмотайте в новом запросе. Основной пример в разделе Implement checkpointing показывает вызов возобновления и отмотки на обоих языках, в комментарии, отмеченном как Step 3.

Следующие шаги

  • Sessions: узнайте, как возобновлять сеансы, что требуется для отмотки после завершения потока. Охватывает ID сеансов, возобновление бесед и разветвление сеансов.
  • Permissions: настройте, какие инструменты может использовать Claude и как одобряются изменения файлов. Полезно, если вы хотите больше контроля над тем, когда происходят правки.
  • TypeScript SDK reference: полный справочник API, включая все параметры для query() и метода rewindFiles().
  • Python SDK reference: полный справочник API, включая все параметры для ClaudeAgentOptions и метода rewind_files().