import asynciofrom 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())
1
启用checkpointing
配置您的SDK选项以启用checkpointing并接收checkpoint UUID:
选项
Python
TypeScript
描述
启用checkpointing
enable_file_checkpointing=True
enableFileCheckpointing: true
跟踪文件更改以便回滚
接收checkpoint UUID
extra_args={"replay-user-messages": None}
extraArgs: { 'replay-user-messages': null }
需要在流中获取用户消息UUID
options = ClaudeAgentOptions( enable_file_checkpointing=True, permission_mode="acceptEdits", extra_args={"replay-user-messages": None},)async with ClaudeSDKClient(options) as client: await client.query("Refactor the authentication module")
2
捕获checkpoint UUID和会话ID
设置replay-user-messages选项后(如上所示),响应流中的每个用户消息都有一个UUID,用作checkpoint。对于大多数用例,捕获第一个用户消息UUID(message.uuid);回滚到它会将所有文件恢复到原始状态。要存储多个checkpoint并回滚到中间状态,请参阅多个恢复点。捕获会话ID(message.session_id)是可选的;只有在您想在流完成后回滚时才需要它。如果您在处理消息时立即调用rewindFiles()(如Checkpoint before risky operations中的示例所做的那样),您可以跳过捕获会话ID。
checkpoint_id = Nonesession_id = Noneasync for message in client.receive_response(): # Update checkpoint on each user message (keeps the latest) if isinstance(message, UserMessage) and message.uuid: checkpoint_id = message.uuid # Capture session ID from the result message if isinstance(message, ResultMessage): session_id = message.session_id
3
回滚文件
要在流完成后回滚,使用空提示恢复会话,并使用您的checkpoint UUID调用rewind_files()(Python)或rewindFiles()(TypeScript)。您也可以在流期间回滚;有关该模式,请参阅Checkpoint before risky operations。
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
如果您捕获了会话ID和checkpoint ID,您也可以从CLI回滚:
claude -p --resume <session-id> --rewind-files <checkpoint-uuid>
import asynciofrom claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, UserMessageasync 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 breakasyncio.run(main())
import asynciofrom dataclasses import dataclassfrom datetime import datetimefrom claude_agent_sdk import ( ClaudeSDKClient, ClaudeAgentOptions, UserMessage, ResultMessage,)# Store checkpoint metadata for better tracking@dataclassclass Checkpoint: id: str description: str timestamp: datetimeasync 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())
def add(a, b): return a + bdef subtract(a, b): return a - bdef multiply(a, b): return a * bdef divide(a, b): if b == 0: raise ValueError("Cannot divide by zero") return a / b
import asynciofrom 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())
# Resume session with empty prompt, then rewindasync with ClaudeSDKClient( ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)) as client: await client.query("") async for message in client.receive_response(): await client.rewind_files(checkpoint_id) break