Rewind file changes with checkpointing changedagent-sdk/file-checkpointing
Nearest release: v2.1.301, published 4 hours before upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Upstream edited this page at 15 Oct 2026 19:23 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:37 UTC.
Upstream edited
Recorded here
Lines+548added
Lines−564removed
From line
4
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits9to this page, all time
The whole hunk
from line 4, old and new numbered
/
The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.
from line 4
44
55File checkpointing tracks file modifications made through the [Write](/docs/en/tools-reference#write-tool-behavior), [Edit](/docs/en/tools-reference#edit-tool-behavior), and [NotebookEdit](/docs/en/tools-reference#notebookedit-tool-behavior) tools during an agent session, allowing you to rewind files to any previous state. To watch a rewind restore a file, jump to the [interactive example](#try-it-out).
66
7<Warning>
8 Only changes made through the Write, Edit, and NotebookEdit tools are tracked. Changes made through Bash commands (like `echo > file.txt` or `sed -i`) are not captured by the checkpoint system, and neither are edits a [subagent](/docs/en/agent-sdk/subagents) applies, except a [skill with `context: fork`](/docs/en/skills#run-skills-in-a-subagent) that runs in the foreground.
9</Warning>
7> [!WARNING]
8>
9> Only changes made through the Write, Edit, and NotebookEdit tools are tracked. Changes made through Bash commands (like `echo > file.txt` or `sed -i`) are not captured by the checkpoint system, and neither are edits a [subagent](/docs/en/agent-sdk/subagents) applies, except a [skill with `context: fork`](/docs/en/skills#run-skills-in-a-subagent) that runs in the foreground.
1010
1111## How checkpointing works
1212
1313When you enable file checkpointing, the SDK creates backups of files before modifying them through the Write, Edit, or NotebookEdit tools. User messages in the response stream include a checkpoint UUID that you can use as a restore point.
1414
15<Note>
16 File rewinding restores files on disk to a previous state. It does not rewind the conversation itself. The conversation history and context remain intact after calling `rewindFiles()` (TypeScript) or `rewind_files()` (Python).
17</Note>
15> [!NOTE]
16>
17> File rewinding restores files on disk to a previous state. It does not rewind the conversation itself. The conversation history and context remain intact after calling `rewindFiles()` (TypeScript) or `rewind_files()` (Python).
1818
1919When you rewind to a checkpoint, Claude Code deletes the files it created and restores the files it modified to their content at that point. Claude Code skips a tracked path that is a symlink, hard link, or other non-regular file. It also skips a tracked file whose parent directory no longer resolves to its checkpoint-time location, or whose backup it can't read safely. [`RewindFilesResult`](/docs/en/agent-sdk/typescript#rewindfilesresult) counts every skipped path in its `skippedLinks` field. Skipping requires Claude Code v2.1.216 or later; before v2.1.216, a rewind wrote and deleted through links at tracked paths.
2020
from line 26
2626
2727This example enables checkpointing, captures the checkpoint UUID and session ID from the response stream, then resumes the session later to rewind files. The example's numbered comments match the steps that follow.
2828
29<CodeGroup>
30 ```python Python theme={null}
31 import asyncio
32 from claude_agent_sdk import (
33 ClaudeSDKClient,
34 ClaudeAgentOptions,
35 UserMessage,
36 ResultMessage,
37 )
38
39
40 async def main():
41 # Step 1: Enable checkpointing
42 options = ClaudeAgentOptions(
43 enable_file_checkpointing=True,
44 permission_mode="acceptEdits", # Auto-accept file edits without prompting
45 extra_args={
46 "replay-user-messages": None
47 }, # Required to receive checkpoint UUIDs in the response stream
48 )
49
50 checkpoint_id = None
51 session_id = None
52
53 # Run the query and capture checkpoint UUID and session ID
54 async with ClaudeSDKClient(options) as client:
55 await client.query("Refactor the authentication module")
56
57 # Step 2: Capture checkpoint UUID from the first user message
58 async for message in client.receive_response():
59 if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
60 checkpoint_id = message.uuid
61 if isinstance(message, ResultMessage) and not session_id:
62 session_id = message.session_id
63
64 # Step 3: Later, rewind by resuming the session with an empty prompt
65 if checkpoint_id and session_id:
66 async with ClaudeSDKClient(
67 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
68 ) as client:
69 await client.query("") # Empty prompt to open the connection
70 async for message in client.receive_response():
71 await client.rewind_files(checkpoint_id)
72 break
73 print(f"Rewound to checkpoint: {checkpoint_id}")
74
75
76 asyncio.run(main())
77 ```
78
79 ```typescript TypeScript theme={null}
80 import { query } from "@anthropic-ai/claude-agent-sdk";
81
82 async function main() {
83 // Step 1: Enable checkpointing
84 const opts = {
85 enableFileCheckpointing: true,
86 permissionMode: "acceptEdits" as const, // Auto-accept file edits without prompting
87 extraArgs: { "replay-user-messages": null } // Required to receive checkpoint UUIDs in the response stream
88 };
89
90 const response = query({
91 prompt: "Refactor the authentication module",
92 options: opts
29```python Python theme={null}
30import asyncio
31from claude_agent_sdk import (
32 ClaudeSDKClient,
33 ClaudeAgentOptions,
34 UserMessage,
35 ResultMessage,
36)
37
38
39async def main():
40 # Step 1: Enable checkpointing
41 options = ClaudeAgentOptions(
42 enable_file_checkpointing=True,
43 permission_mode="acceptEdits", # Auto-accept file edits without prompting
44 extra_args={
45 "replay-user-messages": None
46 }, # Required to receive checkpoint UUIDs in the response stream
47 )
48
49 checkpoint_id = None
50 session_id = None
51
52 # Run the query and capture checkpoint UUID and session ID
53 async with ClaudeSDKClient(options) as client:
54 await client.query("Refactor the authentication module")
55
56 # Step 2: Capture checkpoint UUID from the first user message
57 async for message in client.receive_response():
58 if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
59 checkpoint_id = message.uuid
60 if isinstance(message, ResultMessage) and not session_id:
61 session_id = message.session_id
62
63 # Step 3: Later, rewind by resuming the session with an empty prompt
64 if checkpoint_id and session_id:
65 async with ClaudeSDKClient(
66 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
67 ) as client:
68 await client.query("") # Empty prompt to open the connection
69 async for message in client.receive_response():
70 await client.rewind_files(checkpoint_id)
71 break
72 print(f"Rewound to checkpoint: {checkpoint_id}")
73
74
75asyncio.run(main())
76```
77
78```typescript TypeScript theme={null}
79import { query } from "@anthropic-ai/claude-agent-sdk";
80
81async function main() {
82 // Step 1: Enable checkpointing
83 const opts = {
84 enableFileCheckpointing: true,
85 permissionMode: "acceptEdits" as const, // Auto-accept file edits without prompting
86 extraArgs: { "replay-user-messages": null } // Required to receive checkpoint UUIDs in the response stream
87 };
88
89 const response = query({
90 prompt: "Refactor the authentication module",
91 options: opts
92 });
93
94 let checkpointId: string | undefined;
95 let sessionId: string | undefined;
96
97 // Step 2: Capture checkpoint UUID from the first user message
98 try {
99 for await (const message of response) {
100 if (message.type === "user" && message.uuid && !checkpointId) {
101 checkpointId = message.uuid;
102 }
103 if ("session_id" in message && !sessionId) {
104 sessionId = message.session_id;
105 }
106 }
107 } catch (error) {
108 // A single-shot query() throws after yielding an error result. If the
109 // failure was an error result, sessionId and checkpointId were already
110 // captured by the loop above; connection or process failures yield no
111 // result message.
112 console.error(`Session ended with an error: ${error}`);
113 }
114
115 // Step 3: Later, rewind by resuming the session with an empty prompt
116 if (checkpointId && sessionId) {
117 const rewindQuery = query({
118 prompt: "", // Empty prompt to open the connection
119 options: { ...opts, resume: sessionId }
93120 });
94121
95 let checkpointId: string | undefined;
96 let sessionId: string | undefined;
97
98 // Step 2: Capture checkpoint UUID from the first user message
99 try {
100 for await (const message of response) {
101 if (message.type === "user" && message.uuid && !checkpointId) {
102 checkpointId = message.uuid;
103 }
104 if ("session_id" in message && !sessionId) {
105 sessionId = message.session_id;
106 }
107 }
108 } catch (error) {
109 // A single-shot query() throws after yielding an error result. If the
110 // failure was an error result, sessionId and checkpointId were already
111 // captured by the loop above; connection or process failures yield no
112 // result message.
113 console.error(`Session ended with an error: ${error}`);
114 }
115
116 // Step 3: Later, rewind by resuming the session with an empty prompt
117 if (checkpointId && sessionId) {
118 const rewindQuery = query({
119 prompt: "", // Empty prompt to open the connection
120 options: { ...opts, resume: sessionId }
121 });
122
123 for await (const msg of rewindQuery) {
124 await rewindQuery.rewindFiles(checkpointId);
125 break;
126 }
127 console.log(`Rewound to checkpoint: ${checkpointId}`);
128 }
129 }
130
131 main();
132 ```
133</CodeGroup>
134
135<Steps>
136 <Step title="Enable checkpointing">
137 Configure your SDK options to enable checkpointing and receive checkpoint UUIDs:
138
139 | Option | Python | TypeScript | Description |
140 | - | - | - | - |
141 | Enable checkpointing | `enable_file_checkpointing=True` | `enableFileCheckpointing: true` | Tracks file changes for rewinding |
142 | Receive checkpoint UUIDs | `extra_args={"replay-user-messages": None}` | `extraArgs: { 'replay-user-messages': null }` | Required to get user message UUIDs in the stream |
143
144 The lead example under [Implement checkpointing](#implement-checkpointing) also sets the permission mode to `acceptEdits`, which approves the agent's file edits without a prompt. For more information, see [Permission modes](/docs/en/agent-sdk/permissions#permission-modes).
145 </Step>
146
147 <Step title="Capture checkpoint UUID and session ID">
148 With the `replay-user-messages` option set, each user message in the response stream has a UUID that serves as a checkpoint.
149
150 For most use cases, capture the first user message UUID (`message.uuid`); rewinding to it restores the tracked files to their original state. To store multiple checkpoints and rewind to intermediate states, see [Multiple restore points](#multiple-restore-points).
151
152 Capturing the session ID (`message.session_id`) is optional; you only need it if you want to rewind later, after the stream completes. If you're calling `rewindFiles()` immediately while still processing messages (as the example in [Checkpoint before risky operations](#checkpoint-before-risky-operations) does), you can skip capturing the session ID.
153 </Step>
154
155 <Step title="Rewind files">
156 To rewind after the stream completes, resume the session with an empty prompt and call `rewind_files()` (Python) or `rewindFiles()` (TypeScript) with your checkpoint UUID. Enable checkpointing on the resumed session's options too, and call rewind from inside the response loop. You can also rewind during the stream. See [Checkpoint before risky operations](#checkpoint-before-risky-operations) for that pattern.
157
158 If you capture the session ID and checkpoint ID, you can also rewind from the CLI. This command requires the `claude` executable, which comes from [installing Claude Code](/docs/en/setup). The SDK enables checkpointing for you, but when you run `claude -p` directly you must set the `CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING` environment variable:
159
160 ```bash theme={null}
161 CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true claude -p --resume <session-id> --rewind-files <checkpoint-uuid>
162 ```
163
164 The `--rewind-files` flag doesn't appear in `claude --help` output, but the CLI accepts it as shown. When the rewind succeeds, the command prints `Files rewound to state at message <checkpoint-uuid>` and exits without sending a prompt.
165 </Step>
166</Steps>
122 for await (const msg of rewindQuery) {
123 await rewindQuery.rewindFiles(checkpointId);
124 break;
125 }
126 console.log(`Rewound to checkpoint: ${checkpointId}`);
127 }
128}
129
130main();
131```
132
133**1. Enable checkpointing**
134
135Configure your SDK options to enable checkpointing and receive checkpoint UUIDs:
136
137| Option | Python | TypeScript | Description |
138| - | - | - | - |
139| Enable checkpointing | `enable_file_checkpointing=True` | `enableFileCheckpointing: true` | Tracks file changes for rewinding |
140| Receive checkpoint UUIDs | `extra_args={"replay-user-messages": None}` | `extraArgs: { 'replay-user-messages': null }` | Required to get user message UUIDs in the stream |
141
142The lead example under [Implement checkpointing](#implement-checkpointing) also sets the permission mode to `acceptEdits`, which approves the agent's file edits without a prompt. For more information, see [Permission modes](/docs/en/agent-sdk/permissions#permission-modes).
143
144**2. Capture checkpoint UUID and session ID**
145
146With the `replay-user-messages` option set, each user message in the response stream has a UUID that serves as a checkpoint.
147
148For most use cases, capture the first user message UUID (`message.uuid`); rewinding to it restores the tracked files to their original state. To store multiple checkpoints and rewind to intermediate states, see [Multiple restore points](#multiple-restore-points).
149
150Capturing the session ID (`message.session_id`) is optional; you only need it if you want to rewind later, after the stream completes. If you're calling `rewindFiles()` immediately while still processing messages (as the example in [Checkpoint before risky operations](#checkpoint-before-risky-operations) does), you can skip capturing the session ID.
151
152**3. Rewind files**
153
154To rewind after the stream completes, resume the session with an empty prompt and call `rewind_files()` (Python) or `rewindFiles()` (TypeScript) with your checkpoint UUID. Enable checkpointing on the resumed session's options too, and call rewind from inside the response loop. You can also rewind during the stream. See [Checkpoint before risky operations](#checkpoint-before-risky-operations) for that pattern.
155
156If you capture the session ID and checkpoint ID, you can also rewind from the CLI. This command requires the `claude` executable, which comes from [installing Claude Code](/docs/en/setup). The SDK enables checkpointing for you, but when you run `claude -p` directly you must set the `CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING` environment variable:
157
158```bash theme={null}
159CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true claude -p --resume <session-id> --rewind-files <checkpoint-uuid>
160```
161
162The `--rewind-files` flag doesn't appear in `claude --help` output, but the CLI accepts it as shown. When the rewind succeeds, the command prints `Files rewound to state at message <checkpoint-uuid>` and exits without sending a prompt.
167163
168164## Common patterns
169165
from line 171
175171
176172Before running this example, replace `your_revert_condition` (Python) or `yourRevertCondition` (TypeScript) with your own check, such as error detection or a validation failure; the placeholder is not defined in the example.
177173
178<CodeGroup>
179 ```python Python theme={null}
180 import asyncio
181 from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, UserMessage
182
183
184 async def main():
185 options = ClaudeAgentOptions(
186 enable_file_checkpointing=True,
187 permission_mode="acceptEdits",
188 extra_args={"replay-user-messages": None},
189 )
190
191 safe_checkpoint = None
192
193 async with ClaudeSDKClient(options) as client:
194 await client.query("Refactor the authentication module")
195
196 async for message in client.receive_response():
197 # Update checkpoint before each agent turn starts
198 # This overwrites the previous checkpoint. Only keep the latest
199 if isinstance(message, UserMessage) and message.uuid:
200 safe_checkpoint = message.uuid
201
202 # Decide when to revert based on your own logic
203 # For example: error detection, validation failure, or user input
204 if your_revert_condition and safe_checkpoint:
205 await client.rewind_files(safe_checkpoint)
206 # Exit the loop after rewinding, files are restored
207 break
208
209
210 asyncio.run(main())
211 ```
212
213 ```typescript TypeScript theme={null}
214 import { query } from "@anthropic-ai/claude-agent-sdk";
215
216 async function main() {
217 const response = query({
218 prompt: "Refactor the authentication module",
219 options: {
220 enableFileCheckpointing: true,
221 permissionMode: "acceptEdits" as const,
222 extraArgs: { "replay-user-messages": null }
223 }
224 });
225
226 let safeCheckpoint: string | undefined;
227
228 for await (const message of response) {
229 // Update checkpoint before each agent turn starts
230 // This overwrites the previous checkpoint. Only keep the latest
231 if (message.type === "user" && message.uuid) {
232 safeCheckpoint = message.uuid;
233 }
234
235 // Decide when to revert based on your own logic
236 // For example: error detection, validation failure, or user input
237 if (yourRevertCondition && safeCheckpoint) {
238 await response.rewindFiles(safeCheckpoint);
239 // Exit the loop after rewinding, files are restored
240 break;
241 }
242 }
243 }
244
245 main();
246 ```
247</CodeGroup>
248
249### Multiple restore points
250
251If Claude makes changes across multiple turns, you might want to rewind to a specific point rather than all the way back. For example, if Claude refactors a file in turn one and adds tests in turn two, you might want to keep the refactor but undo the tests.
252
253This pattern stores all checkpoint UUIDs in an array with metadata. After the session completes, you can rewind to any previous checkpoint:
254
255<CodeGroup>
256 ```python Python theme={null}
257 import asyncio
258 from dataclasses import dataclass
259 from datetime import datetime
260 from claude_agent_sdk import (
261 ClaudeSDKClient,
262 ClaudeAgentOptions,
263 UserMessage,
264 ResultMessage,
265 )
266
267
268 # Store checkpoint metadata for better tracking
269 @dataclass
270 class Checkpoint:
271 id: str
272 description: str
273 timestamp: datetime
274
275
276 async def main():
277 options = ClaudeAgentOptions(
278 enable_file_checkpointing=True,
279 permission_mode="acceptEdits",
280 extra_args={"replay-user-messages": None},
281 )
282
283 checkpoints = []
284 session_id = None
285
286 async with ClaudeSDKClient(options) as client:
287 await client.query("Refactor the authentication module")
288
289 async for message in client.receive_response():
290 if isinstance(message, UserMessage) and message.uuid:
291 checkpoints.append(
292 Checkpoint(
293 id=message.uuid,
294 description=f"After turn {len(checkpoints) + 1}",
295 timestamp=datetime.now(),
296 )
297 )
298 if isinstance(message, ResultMessage) and not session_id:
299 session_id = message.session_id
300
301 # Later: rewind to any checkpoint by resuming the session
302 if checkpoints and session_id:
303 target = checkpoints[0] # Pick any checkpoint
304 async with ClaudeSDKClient(
305 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
306 ) as client:
307 await client.query("") # Empty prompt to open the connection
308 async for message in client.receive_response():
309 await client.rewind_files(target.id)
310 break
311 print(f"Rewound to: {target.description}")
312
313
314 asyncio.run(main())
315 ```
316
317 ```typescript TypeScript theme={null}
318 import { query } from "@anthropic-ai/claude-agent-sdk";
319
320 // Store checkpoint metadata for better tracking
321 interface Checkpoint {
322 id: string;
323 description: string;
324 timestamp: Date;
325 }
326
327 async function main() {
328 const opts = {
174```python Python theme={null}
175import asyncio
176from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, UserMessage
177
178
179async def main():
180 options = ClaudeAgentOptions(
181 enable_file_checkpointing=True,
182 permission_mode="acceptEdits",
183 extra_args={"replay-user-messages": None},
184 )
185
186 safe_checkpoint = None
187
188 async with ClaudeSDKClient(options) as client:
189 await client.query("Refactor the authentication module")
190
191 async for message in client.receive_response():
192 # Update checkpoint before each agent turn starts
193 # This overwrites the previous checkpoint. Only keep the latest
194 if isinstance(message, UserMessage) and message.uuid:
195 safe_checkpoint = message.uuid
196
197 # Decide when to revert based on your own logic
198 # For example: error detection, validation failure, or user input
199 if your_revert_condition and safe_checkpoint:
200 await client.rewind_files(safe_checkpoint)
201 # Exit the loop after rewinding, files are restored
202 break
203
204
205asyncio.run(main())
206```
207
208```typescript TypeScript theme={null}
209import { query } from "@anthropic-ai/claude-agent-sdk";
210
211async function main() {
212 const response = query({
213 prompt: "Refactor the authentication module",
214 options: {
329215 enableFileCheckpointing: true,
330216 permissionMode: "acceptEdits" as const,
331217 extraArgs: { "replay-user-messages": null }
332 };
333
334 const response = query({
335 prompt: "Refactor the authentication module",
336 options: opts
218 }
219 });
220
221 let safeCheckpoint: string | undefined;
222
223 for await (const message of response) {
224 // Update checkpoint before each agent turn starts
225 // This overwrites the previous checkpoint. Only keep the latest
226 if (message.type === "user" && message.uuid) {
227 safeCheckpoint = message.uuid;
228 }
229
230 // Decide when to revert based on your own logic
231 // For example: error detection, validation failure, or user input
232 if (yourRevertCondition && safeCheckpoint) {
233 await response.rewindFiles(safeCheckpoint);
234 // Exit the loop after rewinding, files are restored
235 break;
236 }
237 }
238}
239
240main();
241```
242
243### Multiple restore points
244
245If Claude makes changes across multiple turns, you might want to rewind to a specific point rather than all the way back. For example, if Claude refactors a file in turn one and adds tests in turn two, you might want to keep the refactor but undo the tests.
246
247This pattern stores all checkpoint UUIDs in an array with metadata. After the session completes, you can rewind to any previous checkpoint:
248
249```python Python theme={null}
250import asyncio
251from dataclasses import dataclass
252from datetime import datetime
253from claude_agent_sdk import (
254 ClaudeSDKClient,
255 ClaudeAgentOptions,
256 UserMessage,
257 ResultMessage,
258)
259
260
261# Store checkpoint metadata for better tracking
262@dataclass
263class Checkpoint:
264 id: str
265 description: str
266 timestamp: datetime
267
268
269async def main():
270 options = ClaudeAgentOptions(
271 enable_file_checkpointing=True,
272 permission_mode="acceptEdits",
273 extra_args={"replay-user-messages": None},
274 )
275
276 checkpoints = []
277 session_id = None
278
279 async with ClaudeSDKClient(options) as client:
280 await client.query("Refactor the authentication module")
281
282 async for message in client.receive_response():
283 if isinstance(message, UserMessage) and message.uuid:
284 checkpoints.append(
285 Checkpoint(
286 id=message.uuid,
287 description=f"After turn {len(checkpoints) + 1}",
288 timestamp=datetime.now(),
289 )
290 )
291 if isinstance(message, ResultMessage) and not session_id:
292 session_id = message.session_id
293
294 # Later: rewind to any checkpoint by resuming the session
295 if checkpoints and session_id:
296 target = checkpoints[0] # Pick any checkpoint
297 async with ClaudeSDKClient(
298 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
299 ) as client:
300 await client.query("") # Empty prompt to open the connection
301 async for message in client.receive_response():
302 await client.rewind_files(target.id)
303 break
304 print(f"Rewound to: {target.description}")
305
306
307asyncio.run(main())
308```
309
310```typescript TypeScript theme={null}
311import { query } from "@anthropic-ai/claude-agent-sdk";
312
313// Store checkpoint metadata for better tracking
314interface Checkpoint {
315 id: string;
316 description: string;
317 timestamp: Date;
318}
319
320async function main() {
321 const opts = {
322 enableFileCheckpointing: true,
323 permissionMode: "acceptEdits" as const,
324 extraArgs: { "replay-user-messages": null }
325 };
326
327 const response = query({
328 prompt: "Refactor the authentication module",
329 options: opts
330 });
331
332 const checkpoints: Checkpoint[] = [];
333 let sessionId: string | undefined;
334
335 try {
336 for await (const message of response) {
337 if (message.type === "user" && message.uuid) {
338 checkpoints.push({
339 id: message.uuid,
340 description: `After turn ${checkpoints.length + 1}`,
341 timestamp: new Date()
342 });
343 }
344 if ("session_id" in message && !sessionId) {
345 sessionId = message.session_id;
346 }
347 }
348 } catch (error) {
349 // A single-shot query() throws after yielding an error result. If the
350 // failure was an error result, sessionId and the checkpoints array were
351 // already populated by the loop above; connection or process failures
352 // yield no result message.
353 console.error(`Session ended with an error: ${error}`);
354 }
355
356 // Later: rewind to any checkpoint by resuming the session
357 if (checkpoints.length > 0 && sessionId) {
358 const target = checkpoints[0]; // Pick any checkpoint
359 const rewindQuery = query({
360 prompt: "", // Empty prompt to open the connection
361 options: { ...opts, resume: sessionId }
337362 });
338363
339 const checkpoints: Checkpoint[] = [];
340 let sessionId: string | undefined;
341
342 try {
343 for await (const message of response) {
344 if (message.type === "user" && message.uuid) {
345 checkpoints.push({
346 id: message.uuid,
347 description: `After turn ${checkpoints.length + 1}`,
348 timestamp: new Date()
349 });
350 }
351 if ("session_id" in message && !sessionId) {
352 sessionId = message.session_id;
353 }
364 for await (const msg of rewindQuery) {
365 await rewindQuery.rewindFiles(target.id);
366 break;
367 }
368 console.log(`Rewound to: ${target.description}`);
369 }
370}
371
372main();
373```
374
375## Try it out
376
377This complete example creates a small utility file, has the agent add documentation comments, shows you the changes, then asks if you want to rewind.
378
379Before you begin, make sure you have the [Claude Agent SDK installed](/docs/en/agent-sdk/quickstart).
380
381**1. Create a test file**
382
383Create a new file called `utils.py` (Python) or `utils.ts` (TypeScript) and paste the following code:
384
385```python utils.py theme={null}
386def add(a, b):
387 return a + b
388
389
390def subtract(a, b):
391 return a - b
392
393
394def multiply(a, b):
395 return a * b
396
397
398def divide(a, b):
399 if b == 0:
400 raise ValueError("Cannot divide by zero")
401 return a / b
402```
403
404```typescript utils.ts theme={null}
405export function add(a: number, b: number): number {
406 return a + b;
407}
408
409export function subtract(a: number, b: number): number {
410 return a - b;
411}
412
413export function multiply(a: number, b: number): number {
414 return a * b;
415}
416
417export function divide(a: number, b: number): number {
418 if (b === 0) {
419 throw new Error("Cannot divide by zero");
420 }
421 return a / b;
422}
423```
424
425**2. Run the interactive example**
426
427Create a new file called `try_checkpointing.py` (Python) or `try_checkpointing.ts` (TypeScript) in the same directory as your utility file, and paste the following code.
428
429This script asks Claude to add doc comments to your utility file, then gives you the option to rewind and restore the original.
430
431```python try_checkpointing.py theme={null}
432import asyncio
433from claude_agent_sdk import (
434 ClaudeSDKClient,
435 ClaudeAgentOptions,
436 UserMessage,
437 ResultMessage,
438)
439
440
441async def main():
442 # Configure the SDK with checkpointing enabled
443 # - enable_file_checkpointing: Track file changes for rewinding
444 # - permission_mode: Auto-accept file edits without prompting
445 # - extra_args: Required to receive user message UUIDs in the stream
446 options = ClaudeAgentOptions(
447 enable_file_checkpointing=True,
448 permission_mode="acceptEdits",
449 extra_args={"replay-user-messages": None},
450 )
451
452 checkpoint_id = None # Store the user message UUID for rewinding
453 session_id = None # Store the session ID for resuming
454
455 print("Running agent to add doc comments to utils.py...\n")
456
457 # Run the agent and capture checkpoint data from the response stream
458 async with ClaudeSDKClient(options) as client:
459 await client.query("Add doc comments to utils.py")
460
461 async for message in client.receive_response():
462 # Capture the first user message UUID - this is our restore point
463 if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
464 checkpoint_id = message.uuid
465 # Capture the session ID so we can resume later
466 if isinstance(message, ResultMessage):
467 session_id = message.session_id
468
469 print("Done! Open utils.py to see the added doc comments.\n")
470
471 # Ask the user if they want to rewind the changes
472 if checkpoint_id and session_id:
473 response = input("Rewind to remove the doc comments? (y/n): ")
474
475 if response.lower() == "y":
476 # Resume the session with an empty prompt, then rewind
477 async with ClaudeSDKClient(
478 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
479 ) as client:
480 await client.query("") # Empty prompt opens the connection
481 async for message in client.receive_response():
482 await client.rewind_files(checkpoint_id) # Restore files
483 break
484
485 print(
486 "\n✓ File restored! Open utils.py to verify the doc comments are gone."
487 )
488 else:
489 print("\nKept the modified file.")
490
491
492asyncio.run(main())
493```
494
495```typescript try_checkpointing.ts theme={null}
496import { query } from "@anthropic-ai/claude-agent-sdk";
497import * as readline from "readline";
498
499async function main() {
500 // Configure the SDK with checkpointing enabled
501 // - enableFileCheckpointing: Track file changes for rewinding
502 // - permissionMode: Auto-accept file edits without prompting
503 // - extraArgs: Required to receive user message UUIDs in the stream
504 const opts = {
505 enableFileCheckpointing: true,
506 permissionMode: "acceptEdits" as const,
507 extraArgs: { "replay-user-messages": null }
508 };
509
510 let sessionId: string | undefined; // Store the session ID for resuming
511 let checkpointId: string | undefined; // Store the user message UUID for rewinding
512
513 console.log("Running agent to add doc comments to utils.ts...\n");
514
515 // Run the agent and capture checkpoint data from the response stream
516 const response = query({
517 prompt: "Add doc comments to utils.ts",
518 options: opts
519 });
520
521 try {
522 for await (const message of response) {
523 // Capture the first user message UUID - this is our restore point
524 if (message.type === "user" && message.uuid && !checkpointId) {
525 checkpointId = message.uuid;
354526 }
355 } catch (error) {
356 // A single-shot query() throws after yielding an error result. If the
357 // failure was an error result, sessionId and the checkpoints array were
358 // already populated by the loop above; connection or process failures
359 // yield no result message.
360 console.error(`Session ended with an error: ${error}`);
361 }
362
363 // Later: rewind to any checkpoint by resuming the session
364 if (checkpoints.length > 0 && sessionId) {
365 const target = checkpoints[0]; // Pick any checkpoint
527 // Capture the session ID so we can resume later
528 if ("session_id" in message) {
529 sessionId = message.session_id;
530 }
531 }
532 } catch (error) {
533 // A single-shot query() throws after yielding an error result. If the
534 // failure was an error result, checkpointId and sessionId were already
535 // captured by the loop above; connection or process failures yield no
536 // result message.
537 console.error(`Session ended with an error: ${error}`);
538 }
539
540 console.log("Done! Open utils.ts to see the added doc comments.\n");
541
542 // Ask the user if they want to rewind the changes
543 if (checkpointId && sessionId) {
544 const rl = readline.createInterface({
545 input: process.stdin,
546 output: process.stdout
547 });
548
549 const answer = await new Promise<string>((resolve) => {
550 rl.question("Rewind to remove the doc comments? (y/n): ", resolve);
551 });
552 rl.close();
553
554 if (answer.toLowerCase() === "y") {
555 // Resume the session with an empty prompt, then rewind
366556 const rewindQuery = query({
367 prompt: "", // Empty prompt to open the connection
557 prompt: "", // Empty prompt opens the connection
368558 options: { ...opts, resume: sessionId }
369559 });
370560
371561 for await (const msg of rewindQuery) {
372 await rewindQuery.rewindFiles(target.id);
562 await rewindQuery.rewindFiles(checkpointId); // Restore files
373563 break;
374564 }
375 console.log(`Rewound to: ${target.description}`);
376 }
377 }
378
379 main();
380 ```
381</CodeGroup>
382
383## Try it out
384
385This complete example creates a small utility file, has the agent add documentation comments, shows you the changes, then asks if you want to rewind.
386
387Before you begin, make sure you have the [Claude Agent SDK installed](/docs/en/agent-sdk/quickstart).
388
389<Steps>
390 <Step title="Create a test file">
391 Create a new file called `utils.py` (Python) or `utils.ts` (TypeScript) and paste the following code:
392
393 <CodeGroup>
394 ```python utils.py theme={null}
395 def add(a, b):
396 return a + b
397
398
399 def subtract(a, b):
400 return a - b
401
402
403 def multiply(a, b):
404 return a * b
405
406
407 def divide(a, b):
408 if b == 0:
409 raise ValueError("Cannot divide by zero")
410 return a / b
411 ```
412
413 ```typescript utils.ts theme={null}
414 export function add(a: number, b: number): number {
415 return a + b;
416 }
417
418 export function subtract(a: number, b: number): number {
419 return a - b;
420 }
421
422 export function multiply(a: number, b: number): number {
423 return a * b;
424 }
425
426 export function divide(a: number, b: number): number {
427 if (b === 0) {
428 throw new Error("Cannot divide by zero");
429 }
430 return a / b;
431 }
432 ```
433 </CodeGroup>
434 </Step>
435
436 <Step title="Run the interactive example">
437 Create a new file called `try_checkpointing.py` (Python) or `try_checkpointing.ts` (TypeScript) in the same directory as your utility file, and paste the following code.
438
439 This script asks Claude to add doc comments to your utility file, then gives you the option to rewind and restore the original.
440
441 <CodeGroup>
442 ```python try_checkpointing.py theme={null}
443 import asyncio
444 from claude_agent_sdk import (
445 ClaudeSDKClient,
446 ClaudeAgentOptions,
447 UserMessage,
448 ResultMessage,
449 )
450
451
452 async def main():
453 # Configure the SDK with checkpointing enabled
454 # - enable_file_checkpointing: Track file changes for rewinding
455 # - permission_mode: Auto-accept file edits without prompting
456 # - extra_args: Required to receive user message UUIDs in the stream
457 options = ClaudeAgentOptions(
458 enable_file_checkpointing=True,
459 permission_mode="acceptEdits",
460 extra_args={"replay-user-messages": None},
461 )
462
463 checkpoint_id = None # Store the user message UUID for rewinding
464 session_id = None # Store the session ID for resuming
465
466 print("Running agent to add doc comments to utils.py...\n")
467
468 # Run the agent and capture checkpoint data from the response stream
469 async with ClaudeSDKClient(options) as client:
470 await client.query("Add doc comments to utils.py")
471
472 async for message in client.receive_response():
473 # Capture the first user message UUID - this is our restore point
474 if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
475 checkpoint_id = message.uuid
476 # Capture the session ID so we can resume later
477 if isinstance(message, ResultMessage):
478 session_id = message.session_id
479
480 print("Done! Open utils.py to see the added doc comments.\n")
481
482 # Ask the user if they want to rewind the changes
483 if checkpoint_id and session_id:
484 response = input("Rewind to remove the doc comments? (y/n): ")
485
486 if response.lower() == "y":
487 # Resume the session with an empty prompt, then rewind
488 async with ClaudeSDKClient(
489 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
490 ) as client:
491 await client.query("") # Empty prompt opens the connection
492 async for message in client.receive_response():
493 await client.rewind_files(checkpoint_id) # Restore files
494 break
495
496 print(
497 "\n✓ File restored! Open utils.py to verify the doc comments are gone."
498 )
499 else:
500 print("\nKept the modified file.")
501
502
503 asyncio.run(main())
504 ```
505
506 ```typescript try_checkpointing.ts theme={null}
507 import { query } from "@anthropic-ai/claude-agent-sdk";
508 import * as readline from "readline";
509
510 async function main() {
511 // Configure the SDK with checkpointing enabled
512 // - enableFileCheckpointing: Track file changes for rewinding
513 // - permissionMode: Auto-accept file edits without prompting
514 // - extraArgs: Required to receive user message UUIDs in the stream
515 const opts = {
516 enableFileCheckpointing: true,
517 permissionMode: "acceptEdits" as const,
518 extraArgs: { "replay-user-messages": null }
519 };
520
521 let sessionId: string | undefined; // Store the session ID for resuming
522 let checkpointId: string | undefined; // Store the user message UUID for rewinding
523
524 console.log("Running agent to add doc comments to utils.ts...\n");
525
526 // Run the agent and capture checkpoint data from the response stream
527 const response = query({
528 prompt: "Add doc comments to utils.ts",
529 options: opts
530 });
531
532 try {
533 for await (const message of response) {
534 // Capture the first user message UUID - this is our restore point
535 if (message.type === "user" && message.uuid && !checkpointId) {
536 checkpointId = message.uuid;
537 }
538 // Capture the session ID so we can resume later
539 if ("session_id" in message) {
540 sessionId = message.session_id;
541 }
542 }
543 } catch (error) {
544 // A single-shot query() throws after yielding an error result. If the
545 // failure was an error result, checkpointId and sessionId were already
546 // captured by the loop above; connection or process failures yield no
547 // result message.
548 console.error(`Session ended with an error: ${error}`);
549 }
550
551 console.log("Done! Open utils.ts to see the added doc comments.\n");
552
553 // Ask the user if they want to rewind the changes
554 if (checkpointId && sessionId) {
555 const rl = readline.createInterface({
556 input: process.stdin,
557 output: process.stdout
558 });
559
560 const answer = await new Promise<string>((resolve) => {
561 rl.question("Rewind to remove the doc comments? (y/n): ", resolve);
562 });
563 rl.close();
564
565 if (answer.toLowerCase() === "y") {
566 // Resume the session with an empty prompt, then rewind
567 const rewindQuery = query({
568 prompt: "", // Empty prompt opens the connection
569 options: { ...opts, resume: sessionId }
570 });
571
572 for await (const msg of rewindQuery) {
573 await rewindQuery.rewindFiles(checkpointId); // Restore files
574 break;
575 }
576
577 console.log("\n✓ File restored! Open utils.ts to verify the doc comments are gone.");
578 } else {
579 console.log("\nKept the modified file.");
580 }
581 }
582 }
583
584 main();
585 ```
586 </CodeGroup>
587 </Step>
588
589 <Step title="Run the example">
590 Run the script from the same directory as your utility file.
591
592 <Tip>
593 Open your utility file (`utils.py` or `utils.ts`) in your IDE or editor before running the script. You'll see the file update in real-time as the agent adds doc comments, then revert back to the original when you choose to rewind.
594 </Tip>
595
596 <Tabs>
597 <Tab title="Python">
598 ```bash theme={null}
599 python try_checkpointing.py
600 ```
601 </Tab>
602
603 <Tab title="TypeScript">
604 ```bash theme={null}
605 npx tsx try_checkpointing.ts
606 ```
607 </Tab>
608 </Tabs>
609
610 You'll see the agent add doc comments, then a prompt asking if you want to rewind. If you choose yes, the file is restored to its original state.
611 </Step>
612</Steps>
565
566 console.log("\n✓ File restored! Open utils.ts to verify the doc comments are gone.");
567 } else {
568 console.log("\nKept the modified file.");
569 }
570 }
571}
572
573main();
574```
575
576**3. Run the example**
577
578Run the script from the same directory as your utility file.
579
580> [!TIP]
581>
582> Open your utility file (`utils.py` or `utils.ts`) in your IDE or editor before running the script. You'll see the file update in real-time as the agent adds doc comments, then revert back to the original when you choose to rewind.
583
584**Python**
585
586```bash theme={null}
587python try_checkpointing.py
588```
589
590**TypeScript**
591
592```bash theme={null}
593npx tsx try_checkpointing.ts
594```
595
596You'll see the agent add doc comments, then a prompt asking if you want to rewind. If you choose yes, the file is restored to its original state.
613597
614598## Limitations
615599
616600
No line in this hunk matches that.