Documentation Index
Fetch the complete documentation index at: https://docs.qoder.com/llms.txt
Use this file to discover all available pages before exploring further.
文件 checkpoint 用来记录一次会话里被工具修改过的本地文件状态。启用后,宿主应用可以调用 QoderSDKClient.rewind_files(user_message_id, ...),把被追踪的文件恢复到某条用户消息开始处理时的状态。
这两个能力需要配合使用:没有启用 enable_file_checkpointing=True 时,rewind_files() 没有可用的文件快照。
启用文件 checkpoint
enable_file_checkpointing 是 QoderAgentOptions 的字段。需要后续回滚时,使用 QoderSDKClient 保持同一个活跃会话,并在 options 里打开 checkpoint:
from qoder_agent_sdk import QoderAgentOptions, QoderSDKClient
options = QoderAgentOptions(
cwd="/path/to/project",
enable_file_checkpointing=True,
extra_args={"replay-user-messages": None},
allowed_tools=["Read", "Edit", "Write"],
permission_mode="acceptEdits",
)
async with QoderSDKClient(options=options) as client:
await client.query("Refactor src/foo.py into a cleaner implementation.")
async for message in client.receive_response():
...
extra_args={"replay-user-messages": None} 不是启用 checkpoint 的开关。它的作用是让响应流里回放 UserMessage,并带上可作为回滚锚点的 uuid。如果你的应用需要让用户点选「回到这一轮之前」,通常应该同时设置它。
获取 checkpoint ID
rewind_files() 以用户消息 ID 为锚点。Python SDK 中常见做法是从响应流里的 UserMessage.uuid 捕获这个 ID:
from qoder_agent_sdk import (
QoderAgentOptions,
QoderSDKClient,
ResultMessage,
UserMessage,
)
options = QoderAgentOptions(
cwd="/path/to/project",
enable_file_checkpointing=True,
extra_args={"replay-user-messages": None},
allowed_tools=["Write"],
permission_mode="acceptEdits",
)
checkpoint_id: str | None = None
async with QoderSDKClient(options=options) as client:
await client.query("Rewrite notes.txt as a two-line summary.")
async for message in client.receive_response():
if (
isinstance(message, UserMessage)
and message.uuid
and message.parent_tool_use_id is None
):
checkpoint_id = message.uuid
if isinstance(message, ResultMessage) and message.is_error:
raise RuntimeError(message.result or "Query failed.")
if checkpoint_id is None:
raise RuntimeError("No user message UUID was returned.")
checkpoint_id 是用户消息的 uuid,不是 session_id,也不是 ResultMessage 的 ID。它只在产生该 checkpoint 的会话上下文中有效;其他会话不能拿这个 ID 直接回滚。
Dry run 预览
执行回滚前,建议先用 dry_run=True 预览影响范围。Dry run 不会修改文件,适合做确认弹窗或审计日志。
preview = await client.rewind_files(checkpoint_id, dry_run=True)
if not preview["canRewind"]:
print(preview.get("error", "Unable to rewind files."))
else:
print(
{
"files": len(preview.get("filesChanged", [])),
"insertions": preview.get("insertions", 0),
"deletions": preview.get("deletions", 0),
}
)
for file_path in preview.get("filesChanged", []):
print(f"will be reverted: {file_path}")
RewindFilesResult 包含这些字段:
| 字段 | 类型 | 说明 |
|---|
canRewind | bool | 是否可以执行回滚。dry run 失败时不抛错,由该字段标记 |
error | str | canRewind=False 时的诊断文案 |
filesChanged | list[str] | 将要恢复或已经恢复的文件路径列表 |
insertions | int | 回滚动作会撤销的新增行数汇总 |
deletions | int | 回滚动作会撤销的删除行数汇总 |
当前返回值只包含受影响文件列表和汇总的行级统计,不返回每个文件的具体 diff。需要展示逐文件差异时,可以先 dry run 获取 filesChanged,再结合自己的工作区 diff 逻辑展示。
执行回滚
确认影响范围后,不传 dry_run 即可执行回滚:
result = await client.rewind_files(checkpoint_id)
if result["canRewind"]:
print(result.get("filesChanged", []))
回滚只恢复被 checkpoint 追踪到的本地文件状态,不会回滚对话历史。也就是说,模型仍然保留之前会话里的上下文;UI 需要根据 filesChanged 自行刷新编辑器、文件树或 diff 视图。
失败语义
| 调用形式 | 不可回滚时的表现 |
|---|
await client.rewind_files(id, dry_run=True) | 返回 {"canRewind": False, "error": ...},适合直接展示诊断 |
await client.rewind_files(id) | 抛出异常,调用方应捕获并展示失败原因 |
try:
await client.rewind_files(checkpoint_id)
except Exception as exc:
print(str(exc))
常见失败原因包括:没有启用 enable_file_checkpointing、传入的 ID 不是有效用户消息 UUID、该 ID 不属于当前会话、目标消息没有可回滚的文件快照。
Settings 关系
QoderAgentOptions.settings 可以和 enable_file_checkpointing 同时使用:
options = QoderAgentOptions(
cwd="/path/to/project",
settings={"theme": "dark"},
enable_file_checkpointing=True,
)
当 enable_file_checkpointing=True 时,SDK 会在传给 CLI 的 settings 中合并 general.fileCheckpointing.enabled = True。如果已有其他 settings 字段,它们会被保留;如果已有 fileCheckpointing 配置,enabled 会以 SDK 选项为准。
- 只回滚本地文件 checkpoint;MCP 工具、远程服务或数据库等外部副作用不会被撤销。
- 通过
Bash 直接写文件的变更不作为可回滚文件快照处理。
- 文件内容可以恢复;目录创建这类目录级副作用不一定会被撤销。
- checkpoint ID 与会话绑定。恢复同一会话后可以继续使用对应 ID;不同会话之间不可混用。
字段速查
| 入口 | 类型 | 说明 | |
|---|
QoderAgentOptions.enable_file_checkpointing | `bool | None` | 启用文件 checkpoint,供 rewind_files() 使用 |
QoderAgentOptions.extra_args | `dict[str, str | None]` | 传 {"replay-user-messages": None} 可在流里拿到 UserMessage.uuid |
QoderSDKClient.rewind_files(user_message_id, dry_run=False) | async method | 预览或执行文件回滚 | |
最佳实践
- 保存 user message UUID:把
UserMessage.uuid 和你的 UI 消息记录绑定起来,避免靠文本内容反查。
- 先 dry run 再执行:先展示影响文件和统计,再让用户确认回滚。
- 回滚后刷新 UI:根据
filesChanged 重新加载相关文件状态。
- 失败时展示
error:dry run 返回的 error 通常可以直接作为用户可见诊断。