跳转到主要内容

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_checkpointingQoderAgentOptions 的字段。需要后续回滚时,使用 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 包含这些字段:
字段类型说明
canRewindbool是否可以执行回滚。dry run 失败时不抛错,由该字段标记
errorstrcanRewind=False 时的诊断文案
filesChangedlist[str]将要恢复或已经恢复的文件路径列表
insertionsint回滚动作会撤销的新增行数汇总
deletionsint回滚动作会撤销的删除行数汇总
当前返回值只包含受影响文件列表和汇总的行级统计,不返回每个文件的具体 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`boolNone`启用文件 checkpoint,供 rewind_files() 使用
QoderAgentOptions.extra_args`dict[str, strNone]`{"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 通常可以直接作为用户可见诊断。