SpyBara
Go Premium

agent-sdk/file-checkpointing.md 2026-06-09 06:34 UTC to 2026-06-10 23:57 UTC

796 added, 0 removed.

2026
Sat 27 01:01 Fri 26 23:00 Thu 25 23:58 Wed 24 22:02 Tue 23 22:00 Mon 22 23:59 Fri 19 22:58 Thu 18 22:00 Wed 17 17:02 Tue 16 21:57 Mon 15 23:02 Sat 13 21:59 Fri 12 22:00 Thu 11 23:01 Wed 10 23:57 Tue 9 06:34 Mon 8 06:52 Sat 6 06:24 Fri 5 06:45 Thu 4 06:52 Wed 3 06:53 Tue 2 06:51

์ฒดํฌํฌ์ธํŒ…์œผ๋กœ ํŒŒ์ผ ๋ณ€๊ฒฝ ์‚ฌํ•ญ ๋˜๋Œ๋ฆฌ๊ธฐ

์—์ด์ „ํŠธ ์„ธ์…˜ ์ค‘ ํŒŒ์ผ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ถ”์ ํ•˜๊ณ  ํŒŒ์ผ์„ ์ด์ „์˜ ๋ชจ๋“  ์ƒํƒœ๋กœ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค

ํŒŒ์ผ ์ฒดํฌํฌ์ธํŒ…์€ ์—์ด์ „ํŠธ ์„ธ์…˜ ์ค‘ Write, Edit, NotebookEdit ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ์ˆ˜ํ–‰๋œ ํŒŒ์ผ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ถ”์ ํ•˜์—ฌ ํŒŒ์ผ์„ ์ด์ „์˜ ๋ชจ๋“  ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ง์ ‘ ์‹œ๋„ํ•ด๋ณด๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”? ๋Œ€ํ™”ํ˜• ์˜ˆ์ œ๋กœ ์ด๋™ํ•˜์„ธ์š”.

์ฒดํฌํฌ์ธํŒ…์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ์›์น˜ ์•Š๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์‹คํ–‰ ์ทจ์†Œ - ํŒŒ์ผ์„ ์•Œ๋ ค์ง„ ์–‘ํ˜ธํ•œ ์ƒํƒœ๋กœ ๋ณต์›
  • ๋Œ€์•ˆ ํƒ์ƒ‰ - ์ฒดํฌํฌ์ธํŠธ๋กœ ๋ณต์›ํ•œ ํ›„ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹ ์‹œ๋„
  • ์˜ค๋ฅ˜ ๋ณต๊ตฌ - ์—์ด์ „ํŠธ๊ฐ€ ์ž˜๋ชป๋œ ์ˆ˜์ •์„ ์ˆ˜ํ–‰ํ–ˆ์„ ๋•Œ

์ฒดํฌํฌ์ธํŒ… ์ž‘๋™ ๋ฐฉ์‹

ํŒŒ์ผ ์ฒดํฌํฌ์ธํŒ…์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด SDK๋Š” Write, Edit ๋˜๋Š” NotebookEdit ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๊ธฐ ์ „์— ํŒŒ์ผ์˜ ๋ฐฑ์—…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต ์ŠคํŠธ๋ฆผ์˜ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€์—๋Š” ๋ณต์› ์ง€์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฒดํฌํฌ์ธํŠธ UUID๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์ฒดํฌํฌ์ธํŠธ๋Š” ์—์ด์ „ํŠธ๊ฐ€ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค์Œ์˜ ๊ธฐ๋ณธ ์ œ๊ณต ๋„๊ตฌ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค:

๋„๊ตฌ ์„ค๋ช…
Write ์ƒˆ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ํŒŒ์ผ์„ ์ƒˆ ์ฝ˜ํ…์ธ ๋กœ ๋ฎ์–ด์”๋‹ˆ๋‹ค
Edit ๊ธฐ์กด ํŒŒ์ผ์˜ ํŠน์ • ๋ถ€๋ถ„์— ๋Œ€ํ•œ ๋Œ€์ƒ ํŽธ์ง‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค
NotebookEdit Jupyter ๋…ธํŠธ๋ถ(.ipynb ํŒŒ์ผ)์˜ ์…€์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค

์ฒดํฌํฌ์ธํŠธ ์‹œ์Šคํ…œ์€ ๋‹ค์Œ์„ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค:

  • ์„ธ์…˜ ์ค‘์— ์ƒ์„ฑ๋œ ํŒŒ์ผ
  • ์„ธ์…˜ ์ค‘์— ์ˆ˜์ •๋œ ํŒŒ์ผ
  • ์ˆ˜์ •๋œ ํŒŒ์ผ์˜ ์›๋ณธ ์ฝ˜ํ…์ธ 

์ฒดํฌํฌ์ธํŠธ๋กœ ๋˜๋Œ๋ฆฌ๋ฉด ์ƒ์„ฑ๋œ ํŒŒ์ผ์€ ์‚ญ์ œ๋˜๊ณ  ์ˆ˜์ •๋œ ํŒŒ์ผ์€ ํ•ด๋‹น ์‹œ์ ์˜ ์ฝ˜ํ…์ธ ๋กœ ๋ณต์›๋ฉ๋‹ˆ๋‹ค.

์ฒดํฌํฌ์ธํŒ… ๊ตฌํ˜„

ํŒŒ์ผ ์ฒดํฌํฌ์ธํŒ…์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์˜ต์…˜์—์„œ ํ™œ์„ฑํ™”ํ•˜๊ณ , ์‘๋‹ต ์ŠคํŠธ๋ฆผ์—์„œ ์ฒดํฌํฌ์ธํŠธ UUID๋ฅผ ์บก์ฒ˜ํ•œ ๋‹ค์Œ, ๋ณต์›์ด ํ•„์š”ํ•  ๋•Œ rewindFiles()(TypeScript) ๋˜๋Š” rewind_files()(Python)๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ๋Š” ์ „์ฒด ํ๋ฆ„์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค: ์ฒดํฌํฌ์ธํŒ…์„ ํ™œ์„ฑํ™”ํ•˜๊ณ , ์‘๋‹ต ์ŠคํŠธ๋ฆผ์—์„œ ์ฒดํฌํฌ์ธํŠธ UUID์™€ ์„ธ์…˜ 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())
1

์ฒดํฌํฌ์ธํŒ… ํ™œ์„ฑํ™”

์ฒดํฌํฌ์ธํŒ…์„ ํ™œ์„ฑํ™”ํ•˜๊ณ  ์ฒดํฌํฌ์ธํŠธ UUID๋ฅผ ์ˆ˜์‹ ํ•˜๋„๋ก SDK ์˜ต์…˜์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค:

์˜ต์…˜ Python TypeScript ์„ค๋ช…
์ฒดํฌํฌ์ธํŒ… ํ™œ์„ฑํ™” enable_file_checkpointing=True enableFileCheckpointing: true ๋˜๋Œ๋ฆฌ๊ธฐ ์œ„ํ•ด ํŒŒ์ผ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค
์ฒดํฌํฌ์ธํŠธ 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

์ฒดํฌํฌ์ธํŠธ UUID ๋ฐ ์„ธ์…˜ ID ์บก์ฒ˜

replay-user-messages ์˜ต์…˜์ด ์„ค์ •๋˜๋ฉด(์œ„์— ํ‘œ์‹œ๋จ), ์‘๋‹ต ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€์—๋Š” ์ฒดํฌํฌ์ธํŠธ๋กœ ์‚ฌ์šฉ๋˜๋Š” UUID๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ ์ฒซ ๋ฒˆ์งธ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ UUID(message.uuid)๋ฅผ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ๋˜๋Œ๋ฆฌ๋ฉด ๋ชจ๋“  ํŒŒ์ผ์ด ์›๋ณธ ์ƒํƒœ๋กœ ๋ณต์›๋ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ฒดํฌํฌ์ธํŠธ๋ฅผ ์ €์žฅํ•˜๊ณ  ์ค‘๊ฐ„ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๋ ค๋ฉด ์—ฌ๋Ÿฌ ๋ณต์› ์ง€์ ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

์„ธ์…˜ ID(message.session_id)๋ฅผ ์บก์ฒ˜ํ•˜๋Š” ๊ฒƒ์€ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์ด ์™„๋ฃŒ๋œ ํ›„ ๋‚˜์ค‘์— ๋˜๋Œ๋ฆฌ๋ ค๋Š” ๊ฒฝ์šฐ์—๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ฒดํฌํฌ์ธํŠธ ์ „์— ์œ„ํ—˜ํ•œ ์ž‘์—… ์˜ˆ์ œ์ฒ˜๋Ÿผ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ rewindFiles()๋ฅผ ์ฆ‰์‹œ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ ์„ธ์…˜ ID ์บก์ฒ˜๋ฅผ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

checkpoint_id = None
session_id = None

async 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

ํŒŒ์ผ ๋˜๋Œ๋ฆฌ๊ธฐ

์ŠคํŠธ๋ฆผ์ด ์™„๋ฃŒ๋œ ํ›„ ๋˜๋Œ๋ฆฌ๋ ค๋ฉด ๋นˆ ํ”„๋กฌํ”„ํŠธ๋กœ ์„ธ์…˜์„ ์žฌ๊ฐœํ•˜๊ณ  ์ฒดํฌํฌ์ธํŠธ UUID์™€ ํ•จ๊ป˜ rewind_files()(Python) ๋˜๋Š” rewindFiles()(TypeScript)๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ ์ค‘์—๋„ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒดํฌํฌ์ธํŠธ ์ „์— ์œ„ํ—˜ํ•œ ์ž‘์—…์—์„œ ํ•ด๋‹น ํŒจํ„ด์„ ์ฐธ์กฐํ•˜์„ธ์š”.

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์™€ ์ฒดํฌํฌ์ธํŠธ ID๋ฅผ ์บก์ฒ˜ํ•œ ๊ฒฝ์šฐ CLI์—์„œ๋„ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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

์ผ๋ฐ˜์ ์ธ ํŒจํ„ด

์ด๋Ÿฌํ•œ ํŒจํ„ด์€ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋”ฐ๋ผ ์ฒดํฌํฌ์ธํŠธ UUID๋ฅผ ์บก์ฒ˜ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์œ„ํ—˜ํ•œ ์ž‘์—… ์ „์— ์ฒดํฌํฌ์ธํŠธ

์ด ํŒจํ„ด์€ ๊ฐ€์žฅ ์ตœ๊ทผ์˜ ์ฒดํฌํฌ์ธํŠธ UUID๋งŒ ์œ ์ง€ํ•˜๋ฉฐ, ๊ฐ ์—์ด์ „ํŠธ ํ„ด ์ „์— ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌ ์ค‘์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋งˆ์ง€๋ง‰ ์•ˆ์ „ํ•œ ์ƒํƒœ๋กœ ์ฆ‰์‹œ ๋˜๋Œ๋ฆฌ๊ณ  ๋ฃจํ”„๋ฅผ ๋ฒ—์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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())

์—ฌ๋Ÿฌ ๋ณต์› ์ง€์ 

Claude๊ฐ€ ์—ฌ๋Ÿฌ ํ„ด์— ๊ฑธ์ณ ๋ณ€๊ฒฝ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ, ๋ชจ๋“  ๋ฐฉ์‹์œผ๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ๋ณด๋‹ค๋Š” ํŠน์ • ์ง€์ ์œผ๋กœ ๋˜๋Œ๋ฆฌ๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Claude๊ฐ€ ์ฒซ ๋ฒˆ์งธ ํ„ด์—์„œ ํŒŒ์ผ์„ ๋ฆฌํŒฉํ† ๋งํ•˜๊ณ  ๋‘ ๋ฒˆ์งธ ํ„ด์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒฝ์šฐ, ๋ฆฌํŒฉํ† ๋ง์€ ์œ ์ง€ํ•˜๋˜ ํ…Œ์ŠคํŠธ๋Š” ์‹คํ–‰ ์ทจ์†Œํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํŒจํ„ด์€ ๋ชจ๋“  ์ฒดํฌํฌ์ธํŠธ UUID๋ฅผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ๋ฐฐ์—ด์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์„ธ์…˜์ด ์™„๋ฃŒ๋œ ํ›„ ์ด์ „์˜ ๋ชจ๋“  ์ฒดํฌํฌ์ธํŠธ๋กœ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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())

์ง์ ‘ ์‹œ๋„ํ•ด๋ณด๊ธฐ

์ด ์™„์ „ํ•œ ์˜ˆ์ œ๋Š” ์ž‘์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , ์—์ด์ „ํŠธ๊ฐ€ ๋ฌธ์„œ ์ฃผ์„์„ ์ถ”๊ฐ€ํ•˜๋„๋ก ํ•˜๋ฉฐ, ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ‘œ์‹œํ•œ ๋‹ค์Œ, ๋˜๋Œ๋ฆฌ๊ณ  ์‹ถ์€์ง€ ๋ฌป์Šต๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ ์ „์— 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
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())

์ด ์˜ˆ์ œ๋Š” ์™„์ „ํ•œ ์ฒดํฌํฌ์ธํŒ… ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:

  1. ์ฒดํฌํฌ์ธํŒ… ํ™œ์„ฑํ™”: enable_file_checkpointing=True ๋ฐ permission_mode="acceptEdits"๋กœ SDK๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ ํŒŒ์ผ ํŽธ์ง‘์„ ์ž๋™์œผ๋กœ ์Šน์ธํ•ฉ๋‹ˆ๋‹ค
  2. ์ฒดํฌํฌ์ธํŠธ ๋ฐ์ดํ„ฐ ์บก์ฒ˜: ์—์ด์ „ํŠธ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ์ฒซ ๋ฒˆ์งธ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ UUID(๋ณต์› ์ง€์ ) ๋ฐ ์„ธ์…˜ ID๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค
  3. ๋˜๋Œ๋ฆฌ๊ธฐ ํ”„๋กฌํ”„ํŠธ: ์—์ด์ „ํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํŒŒ์ผ์„ ํ™•์ธํ•˜์—ฌ ๋ฌธ์„œ ์ฃผ์„์„ ๋ณด๊ณ  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์‹คํ–‰ ์ทจ์†Œํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค
  4. ์žฌ๊ฐœ ๋ฐ ๋˜๋Œ๋ฆฌ๊ธฐ: ์˜ˆ์ธ ๊ฒฝ์šฐ ๋นˆ ํ”„๋กฌํ”„ํŠธ๋กœ ์„ธ์…˜์„ ์žฌ๊ฐœํ•˜๊ณ  rewind_files()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์›๋ณธ ํŒŒ์ผ์„ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค
3

์˜ˆ์ œ ์‹คํ–‰

์œ ํ‹ธ๋ฆฌํ‹ฐ ํŒŒ์ผ๊ณผ ๊ฐ™์€ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

python try_checkpointing.py

์—์ด์ „ํŠธ๊ฐ€ ๋ฌธ์„œ ์ฃผ์„์„ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ ๋˜๋Œ๋ฆฌ๊ณ  ์‹ถ์€์ง€ ๋ฌป๋Š” ํ”„๋กฌํ”„ํŠธ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ์„ ํƒํ•˜๋ฉด ํŒŒ์ผ์ด ์›๋ณธ ์ƒํƒœ๋กœ ๋ณต์›๋ฉ๋‹ˆ๋‹ค.

์ œํ•œ ์‚ฌํ•ญ

ํŒŒ์ผ ์ฒดํฌํฌ์ธํŒ…์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ œํ•œ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

์ œํ•œ ์‚ฌํ•ญ ์„ค๋ช…
Write/Edit/NotebookEdit ๋„๊ตฌ๋งŒ Bash ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ˆ˜ํ–‰๋œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์ถ”์ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
๋™์ผํ•œ ์„ธ์…˜ ์ฒดํฌํฌ์ธํŠธ๋Š” ์ด๋ฅผ ์ƒ์„ฑํ•œ ์„ธ์…˜์— ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค
ํŒŒ์ผ ์ฝ˜ํ…์ธ ๋งŒ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ, ์ด๋™ ๋˜๋Š” ์‚ญ์ œ๋Š” ๋˜๋Œ๋ฆฌ๊ธฐ๋กœ ์‹คํ–‰ ์ทจ์†Œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
๋กœ์ปฌ ํŒŒ์ผ ์›๊ฒฉ ๋˜๋Š” ๋„คํŠธ์›Œํฌ ํŒŒ์ผ์€ ์ถ”์ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

๋ฌธ์ œ ํ•ด๊ฒฐ

์ฒดํฌํฌ์ธํŒ… ์˜ต์…˜์ด ์ธ์‹๋˜์ง€ ์•Š์Œ

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๋ฅผ ์ˆ˜์‹ ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์›์ธ: replay-user-messages ์˜ต์…˜์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ์ฑ…: ์˜ต์…˜์— extra_args={"replay-user-messages": None}(Python) ๋˜๋Š” extraArgs: { 'replay-user-messages': null }(TypeScript)์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

"No file checkpoint found for message" ์˜ค๋ฅ˜

์ด ์˜ค๋ฅ˜๋Š” ์ง€์ •๋œ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ UUID์— ๋Œ€ํ•œ ์ฒดํฌํฌ์ธํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ ์›์ธ:

  • ์›๋ณธ ์„ธ์…˜์—์„œ ํŒŒ์ผ ์ฒดํฌํฌ์ธํŒ…์ด ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค(enable_file_checkpointing ๋˜๋Š” enableFileCheckpointing์ด true๋กœ ์„ค์ •๋˜์ง€ ์•Š์Œ)
  • ์žฌ๊ฐœ ๋ฐ ๋˜๋Œ๋ฆฌ๊ธฐ๋ฅผ ์‹œ๋„ํ•˜๊ธฐ ์ „์— ์„ธ์…˜์ด ์ œ๋Œ€๋กœ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค

ํ•ด๊ฒฐ์ฑ…: ์›๋ณธ ์„ธ์…˜์—์„œ enable_file_checkpointing=True(Python) ๋˜๋Š” enableFileCheckpointing: true(TypeScript)๊ฐ€ ์„ค์ •๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ ๋‹ค์Œ, ์˜ˆ์ œ์— ํ‘œ์‹œ๋œ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: ์ฒซ ๋ฒˆ์งธ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ UUID๋ฅผ ์บก์ฒ˜ํ•˜๊ณ , ์„ธ์…˜์„ ์™„์ „ํžˆ ์™„๋ฃŒํ•œ ๋‹ค์Œ, ๋นˆ ํ”„๋กฌํ”„ํŠธ๋กœ ์žฌ๊ฐœํ•˜๊ณ  rewindFiles()๋ฅผ ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

"ProcessTransport is not ready for writing" ์˜ค๋ฅ˜

์ด ์˜ค๋ฅ˜๋Š” ์‘๋‹ต์„ ๋ฐ˜๋ณตํ•˜๋Š” ๊ฒƒ์„ ์™„๋ฃŒํ•œ ํ›„ rewindFiles() ๋˜๋Š” rewind_files()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ฃจํ”„๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด CLI ํ”„๋กœ์„ธ์Šค์— ๋Œ€ํ•œ ์—ฐ๊ฒฐ์ด ๋‹ซํž™๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ์ฑ…: ๋นˆ ํ”„๋กฌํ”„ํŠธ๋กœ ์„ธ์…˜์„ ์žฌ๊ฐœํ•œ ๋‹ค์Œ ์ƒˆ ์ฟผ๋ฆฌ์—์„œ ๋˜๋Œ๋ฆฌ๊ธฐ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค:

# Resume session with empty prompt, then rewind
async 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

๋‹ค์Œ ๋‹จ๊ณ„

  • ์„ธ์…˜: ์„ธ์…˜์„ ์žฌ๊ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค. ์ด๋Š” ์ŠคํŠธ๋ฆผ์ด ์™„๋ฃŒ๋œ ํ›„ ๋˜๋Œ๋ฆฌ๊ธฐ์— ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์„ธ์…˜ ID, ๋Œ€ํ™” ์žฌ๊ฐœ ๋ฐ ์„ธ์…˜ ํฌํ‚น์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.
  • ๊ถŒํ•œ: Claude๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์™€ ํŒŒ์ผ ์ˆ˜์ •์ด ์Šน์ธ๋˜๋Š” ๋ฐฉ์‹์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํŽธ์ง‘์ด ๋ฐœ์ƒํ•˜๋Š” ์‹œ๊ธฐ๋ฅผ ๋” ๋งŽ์ด ์ œ์–ดํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • TypeScript SDK ์ฐธ์กฐ: query() ๋ฐ rewindFiles() ๋ฉ”์„œ๋“œ์˜ ๋ชจ๋“  ์˜ต์…˜์„ ํฌํ•จํ•œ ์™„์ „ํ•œ API ์ฐธ์กฐ์ž…๋‹ˆ๋‹ค.
  • Python SDK ์ฐธ์กฐ: ClaudeAgentOptions ๋ฐ rewind_files() ๋ฉ”์„œ๋“œ์˜ ๋ชจ๋“  ์˜ต์…˜์„ ํฌํ•จํ•œ ์™„์ „ํ•œ API ์ฐธ์กฐ์ž…๋‹ˆ๋‹ค.