Skip to main content
会話とセッション

ファイル Checkpoint と Rewind

ファイル checkpoint は、セッション内でツールが変更したローカルファイルの状態を記録します。enableFileCheckpointing(TypeScript)/ enable_file_checkpointing(Python)を有効にすると、rewindFiles(userMessageId, ...) / rewind_files(user_message_id, ...) で、あるユーザーメッセージの処理開始時点の状態にファイルを巻き戻せます。 この2つの機能はセットで使います。checkpoint を有効にしなければ、rewind に使えるファイルスナップショットはありません。

ファイル checkpoint の有効化

import { query } from '@qoder-ai/qoder-agent-sdk';

const q = query({
  prompt: 'Refactor src/foo.ts into a cleaner implementation',
  options: {
    cwd: '/path/to/project',
    enableFileCheckpointing: true,
    allowedTools: ['Read', 'Edit', 'Write'],
    permissionMode: 'acceptEdits',
  },
});
Python で後から巻き戻す必要がある場合は、QoderSDKClient で同じアクティブセッションを維持します。例の extra_args={"replay-user-messages": None} は checkpoint の有効化スイッチではありません。レスポンスストリームに UserMessage をリプレイさせ、巻き戻しアンカーとして使える uuid を付与します——ユーザーに「このターンの前に戻る」を選ばせる UI があるなら、通常は併せて設定すべきです。

巻き戻しアンカーの取得:user message ID

Rewind はユーザーメッセージ ID をアンカーとし、取得方法は SDK ごとに異なります:
  • TypeScript:正確な巻き戻しが必要な場合は、構造化入力で自分で uuid を生成することを推奨します。UI が「あのメッセージの前に戻る」を安定して逆引きできます。
  • Python:レスポンスストリーム内の UserMessage.uuid からこの ID を取得するのが一般的です(extra_args={"replay-user-messages": None} と併用)。
import { randomUUID } from 'node:crypto';
import { query } from '@qoder-ai/qoder-agent-sdk';

const userMessageId = randomUUID();

async function* input() {
  yield {
    type: 'user' as const,
    uuid: userMessageId,
    parent_tool_use_id: null,
    message: {
      role: 'user' as const,
      content: [
        {
          type: 'text' as const,
          text: 'Rewrite notes.txt as a two-line summary.',
        },
      ],
    },
  };

  // If your application needs to call rewind later within the same session,
  // keep yielding subsequent user inputs here instead of closing the stream.
}

const q = query({
  prompt: input(),
  options: {
    cwd: '/path/to/project',
    enableFileCheckpointing: true,
    allowedTools: ['Read', 'Write'],
    permissionMode: 'acceptEdits',
  },
});
巻き戻しアンカーはユーザーメッセージの uuid であり、session_id でも result メッセージの ID でもありません。その checkpoint を生成したセッションコンテキスト内でのみ有効で、他のセッションからこの ID で直接巻き戻すことはできません。

Dry run プレビュー

巻き戻しを実行する前に、dry run で影響範囲をプレビューすることを推奨します:巻き戻し可能か、影響を受けるファイル、全体の挿入/削除統計。Dry run はファイルを変更しないため、確認ダイアログや監査ログに適しています。 返される RewindFilesResult には以下のフィールドが含まれます:
フィールド説明
canRewindbooleanロールバックを実行できるかどうか。dry run の失敗時はエラーをスローせず、このフィールドで示します
errorstring?canRewind が false の場合の診断テキスト。そのままユーザーに表示できます
filesChangedstring[]?影響を受けるファイルの絶対パスのリスト。UI でロールバックされる各ファイルを一覧表示するのに使用できます
insertionsnumber?巻き戻しが「取り消す追加」行数の合計(集計値)
deletionsnumber?巻き戻しが「取り消す削除」行数の合計(集計値)
SDK は現在、RewindFilesResult で影響を受けるファイルのリストと行レベルの集計統計のみを返します。ファイルごとの diff は返しません。ファイルごとの差分を表示する必要がある場合は、dry run 後に filesChanged をもとにディスクの内容を読み取ってチェックポイントと比較するか、rewind 実行後に git/ワークスペースの差分ツールを使用してください。
const preview = await q.rewindFiles(userMessageId, { dryRun: true });

if (!preview.canRewind) {
  // Show the diagnostic message in the UI.
  console.error(preview.error);
  return;
}

// Overall stats across all affected files.
console.log({
  files: preview.filesChanged?.length ?? 0,
  insertions: preview.insertions ?? 0,
  deletions: preview.deletions ?? 0,
});

// Per-file listing — useful for a confirmation dialog.
for (const file of preview.filesChanged ?? []) {
  console.log(`will be reverted: ${file}`);
}

Rewind の実行

影響範囲を確認したら、dry run パラメーターを省略して巻き戻しを実行します:
const result = await q.rewindFiles(userMessageId);
console.log(result.filesChanged);
ロールバックは checkpoint で追跡されたローカルファイルの状態のみを復元し、会話履歴をロールバックすることはありません。つまり、モデルは以前のセッションのコンテキストを引き続き保持しています。UI は filesChanged に基づいてエディタ、ファイルツリー、または diff ビューを自身でリフレッシュする必要があります。

失敗のセマンティクス

呼び出し形式Rewind 不可時の動作
Dry run モード(rewindFiles(id, { dryRun: true }) / rewind_files(id, dry_run=True){ canRewind: false, error } を返し、UI での診断表示に便利
実行モード(rewindFiles(id) / rewind_files(id)TypeScript は Promise が reject、Python は例外を送出。呼び出し側で捕捉して失敗理由を表示してください
try {
  await q.rewindFiles(userMessageId);
} catch (error) {
  console.error(error instanceof Error ? error.message : String(error));
}
よくある失敗原因:ファイル checkpoint が未有効、ID が有効なユーザーメッセージ UUID でない、ID が現在のセッションに属さない、対象メッセージに巻き戻し可能なファイルスナップショットがない。

Settings との関係

options の settings フィールドはファイル checkpoint スイッチと併用できます。Settings オブジェクト、または settings ファイルの絶対パス文字列を渡せます:
  • settings オブジェクトを渡すと、SDK は general.fileCheckpointing.enabled = true を自動的にマージするため、手書き不要です。既存の settings フィールドは保持され、既存の fileCheckpointing 設定がある場合、enabled は SDK オプションが優先されます。
  • settings ファイルパス文字列を渡す場合、SDK はファイル内容を書き換えないため、自分で設定してください:
{
  "general": {
    "fileCheckpointing": {
      "enabled": true
    }
  }
}
checkpoint スイッチだけを設定して settings を渡さない構成でも、rewind だけ使う用途には十分です。
options: {
  cwd: '/path/to/project',
  settings: { theme: 'dark' },
  enableFileCheckpointing: true,
}

境界

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

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

エントリ(TypeScript / Python)説明
enableFileCheckpointing / enable_file_checkpointingrewind 用のファイル checkpoint を有効化
settings / settingsCLI に渡す settings。オブジェクトの場合 SDK が general.fileCheckpointing.enabled をマージ
extra_args(Python のみ){"replay-user-messages": None} を渡すとストリームで UserMessage.uuid を取得可能
q.rewindFiles(userMessageId, { dryRun }) / client.rewind_files(user_message_id, dry_run=False)ファイル巻き戻しのプレビューまたは実行

戻り値リファレンス

type RewindFilesResult = {
  canRewind: boolean;
  error?: string;
  filesChanged?: string[];
  insertions?: number;
  deletions?: number;
};

ベストプラクティス

  • user message ID を保存する:巻き戻し機能が必要なアプリは、メッセージ送信時に uuid を保存してください(Python では UserMessage.uuid を UI のメッセージ記録に紐づける)。UI テキストからの逆引きに頼らないこと。
  • Rewind 前に dry run を行う:まず影響範囲を表示し、その後ユーザーに Rewind の実行を確認させてください。
  • Rewind 後に UI をリフレッシュ:Rewind はファイルのみを変更し、会話履歴は変更しません。UI は filesChanged に基づいて関連ビューを再読み込みする必要があります。
  • 失敗時は error をユーザーに見せるcanRewind が false の場合の error テキストは、通常そのままエンドユーザー向けの診断として表示できます。
ファイル Checkpoint と Rewind - Qoder