oh-my-pi 的 rewind 工具:以会话树分支剪除探索上下文并保留调查报告
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
rewind 是 oh-my-pi 编码代理(coding-agent)中与 checkpoint 配套的会话管理工具。它的核心使命是:结束一个活跃的 checkpoint,通过会话树分支机制剪除此前的探索性上下文,同时把一份简洁的调查报告保留进后续对话。读完本文,你将掌握 rewind 的注册与可见性规则、输入输出契约、11 步完整执行流程、四种运行模式、副作用边界与全部错误场景,并能从 checkpoint.ts、agent-session.ts 等源码层面理解其实现原理。
工具定位:为什么编码代理需要 rewind
在长时间编码任务中,代理往往会在一个方向上进行大量探索:阅读源码、运行命令、尝试方案,然后发现此路不通。如果这些中间过程全部保留在上下文里,不仅占用 token 预算,还会干扰模型对后续任务的判断。oh-my-pi 给出的答案是 checkpoint/rewind 这一对工具:
checkpoint在探索开始前记录当前会话树位置与目标;rewind在探索结束后,把上下文“回卷”到 checkpoint 处,同时保留一份非空的调查报告,作为此后对话的延续依据。
用文档原话概括,rewind 的职责是 "End an active checkpoint by pruning exploratory context and retaining a concise report"——结束活跃 checkpoint,通过剪除探索性上下文并保留一份简洁报告。它不恢复文件系统、不做 git 回滚,它只操纵会话树与会话上下文。
源码入口与协作组件
rewind 不是独立实现,而是横跨多个模块的协作功能。核心入口与协作关系如下:
| 角色 | 文件 | 职责 |
|---|---|---|
| 工具入口 | packages/coding-agent/src/tools/checkpoint.ts | RewindTool类定义、参数 Schema、execute()校验与返回 |
| 模型侧提示词 | packages/coding-agent/src/prompts/tools/rewind.md | 面向模型的一句话描述:结束活跃 checkpoint,回卷上下文并用报告替换中间探索 |
| 执行编排 | packages/coding-agent/src/session/agent-session.ts | AgentSession校验 pending rewind 状态、执行实际回卷、注入保留报告 |
| 会话树分支 | packages/coding-agent/src/session/session-manager.ts | branchWithSummary()分支持久化会话树并追加 summary/report 条目 |
| 上下文重建 | packages/coding-agent/src/session/session-context.ts | buildSessionContext()把持久化的branch_summary条目转换为模型可见的branchSummary消息 |
| 工具注册 | packages/coding-agent/src/tools/index.ts | 注册rewind工具并共享checkpoint.enabled门控 |
checkpoint与rewind在源码中被明确定义为一对互操作的类:checkpoint.ts 中CheckpointTool与RewindTool共享CheckpointState、CompletedRewindState等接口定义。
注册与可见性规则
工具元数据
RewindTool的元数据定义在 checkpoint.ts:
approval = "read":只读审批级别,不需要写权限审批;strict = true:严格模式,参数 Schema 约束严格;loadMode = "discoverable":可发现加载,模型可在工具列表中检索到它;- 执行是单发的(single-shot):rewind 的副作用不通过流式进度更新暴露,而是延后到回合结束统一生效;
intent固定返回"rewinding",供意图路由使用。
checkpoint.enabled 门控
rewind 的可用性由checkpoint.enabled设置控制,默认值为false。注册逻辑位于 tools/index.ts:
if (name === "checkpoint" || name === "rewind") return ( session.settings.get("checkpoint.enabled") && ((session.taskDepth ?? 0) === 0 || requestedTools !== undefined) );即:顶层会话(taskDepth === 0)在开启checkpoint.enabled后可见该工具;子代理默认不可发现,但可以通过显式的tools:/requested-tools 列表获得。
安全配对:checkpoint 与 rewind 自动互带
由于 checkpoint 和 rewind 是一对安全工具,只注册其中一个会导致代理“只能存档不能回卷”(或反之)而陷入困境。因此 tools/index.ts 实现自动互带:只要checkpoint.enabled开启,frontmatter 的tools:列表中显式请求了其中一个,另一个会被自动追加。该配对逻辑即使对受限会话(restricted session)同样生效。
xd:// 协议形态
在普通的tools.xdev会话中,可发现的内置工具可能以xd://rewind的形式呈现;而显式请求的工具则保持在顶层。这一约定与 oh-my-pi 其他内置工具的暴露方式一致。
输入输出契约
输入参数
rewind 只有一个必填参数,Schema 定义在 checkpoint.ts:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
report | string | 是 | 调查发现(investigation findings)。execute()会先trim(),空结果被拒绝 |
输出
工具返回单个文本结果并附带结构化details:
- 文本正文:
Rewind requested.Report captured for context replacement.
details:report: string—— 裁剪后的报告文本rewound: true
关键点:工具返回值并不是最终的 rewind。AgentSession会等待turn_end,然后异步应用 rewind 副作用。也就是说,模型调用rewind得到的只是“请求已受理”的确认,真正的分支与上下文替换发生在该助手回合结束之后。
完整执行流程(11 步)
结合文档与 agent-session.ts 源码,rewind 从调用到生效的完整链路如下:
注册门控:
RewindTool.createIf()本身总是构造工具实例,但注册(tools/index.ts)强制检查checkpoint.enabled以及顶层/显式子代理可见性规则。前置状态检查:
execute()在没有活跃 checkpoint 时区分两种状态(见 checkpoint.ts):- 存在已完成的 rewind 保留报告:抛
ToolError("Checkpoint already completed; continue from the retained rewind report instead of calling rewind again."); - 不存在任何已完成的 rewind:抛
ToolError("No active checkpoint. Create a checkpoint before calling rewind.")。
- 存在已完成的 rewind 保留报告:抛
报告裁剪校验:对
params.report执行trim(),为空则抛ToolError("Report cannot be empty.")。返回受理结果:返回
toolResult(),携带details.report与details.rewound = true。提取报告:成功的 rewind 工具结果返回后,
AgentSession通过#extractRewindReport()从details.report或第一个文本内容块中提取报告,存入#pendingRewindReport。回合结束触发:
turn_end时#extractRewindReport()在消息序列中查找 pending 或成功的 rewind 结果,调用#applyRewind()。分支并记录 summary:
#applyRewind()首先调用sessionManager.branchWithSummary(checkpointEntryId, report, { startedAt }),在 checkpoint 分支点记录一条branch_summary。若该条目已无法解析,记录警告并改为从根节点分支(见 agent-session.ts)。注入隐藏 rewind-report 消息:追加一条持久化的隐藏
rewind-report自定义消息。其内容由 packages/coding-agent/src/prompts/system/rewind-report.md 渲染而来——告知下一回合 checkpoint 已完成、不要再调用rewind,并附带报告正文;details 中包含{ report, startedAt, rewoundAt }。重建上下文并替换消息:设置
#lastCompletedRewind,从新活动分支重建 display/LLM 会话上下文,同时替换本回合的活动消息数组与agent.state.messages。探索分支与成功的 rewind 工具结果因此不会出现在下一次 provider 调用中。重置关联状态:重置 advisor 会话状态(保留成本统计),从新分支同步 todo 状态,关闭因历史重写而失效的 provider 会话。
清理与恢复:清空
#checkpointState与#pendingRewindReport。此后若会话恢复(resume)或进行树导航,持久化的保留报告会重新水合#lastCompletedRewind。
在源码层面,第 5~9 步分别对应 agent-session.ts(#extractRewindReport)与 agent-session.ts(#applyRewind)。值得注意的是#enforceRewindBeforeYield()(agent-session.ts):当处于活跃 checkpoint 且存在 pending rewind 报告时,代理被强制要求在让出(yield)前调用rewind,避免未完成 checkpoint 就结束回合。
四种运行模式 / 变体
| 模式 | 触发条件 | 行为 |
|---|---|---|
| 正常回卷(Normal rewind) | checkpoint 条目存在 | 会话历史从该确切条目分支 |
| 回退回卷(Fallback rewind) | checkpoint 条目 ID 在当前会话树中缺失 | 从根节点分支并记录警告(Rewind branch checkpoint missing, falling back to root) |
| 延后回合末应用(Deferred turn-end apply) | 正常流程 | 工具结果只请求回卷,分支与上下文替换在助手回合结束后进行 |
| 恢复的 checkpoint(Resumed checkpoint) | 活跃持久化分支上存在未完成的成功 checkpoint 工具结果 | 进程恢复后重新水合 checkpoint 状态,允许继续调用 rewind |
其中“恢复的 checkpoint”依赖#rehydrateCheckpointRewindState()(agent-session.ts):它会扫描当前分支,区分两种历史情形——已完成的 rewind(恢复#lastCompletedRewind,重复调用会收到“already completed”错误)与 checkpoint 后中断(恢复#checkpointState,下次rewind可正常完成)。
副作用分析
会话状态(transcript、memory、jobs、checkpoints、registries)
- 从 checkpoint 分支加保留的 summary/report 重建活跃对话历史;不恢复文件或进程状态;
- 追加隐藏自定义消息
rewind-report,携带恢复指引与报告正文; - 记录
#lastCompletedRewind,清空活跃 checkpoint 与 pending 报告,重置 advisors,从分支重同步 todo 状态,关闭因历史重写失效的 provider 会话; - 把持久化会话叶子(leaf)重新定位到 checkpoint 分支点,并追加新的会话条目。
文件系统
- 新的
branch_summary与custom_message条目通过SessionManager的常规追加持久化写入会话.jsonl文件; - 会话文件命名格式为
<ISO时间戳-冒号与点被替换>_<uuidv7>.jsonl,位于会话目录;未显式覆盖时默认目录为~/.omp/agent/sessions/<encoded-cwd>/。
用户可见提示 / 交互 UI
- 工具结果在回合末应用之前即可见;
- 持久化的
branch_summary在上下文重建时变为模型可见的branchSummary消息;compaction 渲染时以用户角色的<summary>块呈现; - 隐藏的
rewind-report自定义消息成为下一次 provider 调用的开发者角色保留指引。
后台工作 / 取消
rewind 的应用延后到turn_end,没有独立的 job 对象或取消句柄。
限制与上限
- 门控:可用性受
checkpoint.enabled限制,默认false; - 子代理:需要显式的 requested-tools 条目;请求任一 checkpoint 工具会自动附带其姊妹工具;
- 单 checkpoint 约束:一个会话最多一个活跃 checkpoint,没有命名、选择或区分多个 checkpoint 的路径;
- 报告非空:
trim()后报告文本必须非空; - 恢复范围:rewind 只恢复活跃对话/会话树上下文,不存在文件、产物(artifact)、blob、进程或 git 恢复路径;
- 持久化上限:持久化的报告/summary 内容受全局会话持久化上限
MAX_PERSIST_CHARS = 500_000约束。
错误处理清单
| 错误消息 | 抛出条件 |
|---|---|
Checkpoint already completed; continue from the retained rewind report instead of calling rewind again. | 活动分支已包含保留的完成记录时调用 rewind |
No active checkpoint. Create a checkpoint before calling rewind. | 既无活跃 checkpoint 也无已完成的 rewind |
Report cannot be empty. | 裁剪后的报告为空 |
(警告而非失败)Rewind branch checkpoint missing, falling back to root | apply 阶段 checkpoint 条目 ID 缺失,回退到根节点分支,不使已完成的工具调用失败 |
所有错误均通过ToolError抛出,实现在 checkpoint.ts 中可逐一对应。
边界与不恢复的内容
rewind 是非破坏性的:branchWithSummary()追加新的branch_summary条目并移动叶子,被放弃的条目仍留在.jsonl日志中,只是离开活动分支。明确不恢复的内容包括:
- 文件系统或 git 状态;
- packages/coding-agent/src/session/artifacts.ts 管理下的 artifacts;
- packages/coding-agent/src/session/blob-store.ts 管理下的 blob-store 负载;
- packages/coding-agent/src/session/history-storage.ts 中的 prompt 历史行;
- packages/coding-agent/src/session/agent-storage.ts 中的 auth 或其他 agent 存储。
同时,不存在并发编辑对账机制:rewind 既不合并也不回滚代码或会话之外的并发外部状态。
源码级原理:从状态机看 checkpoint/rewind 配对
在 checkpoint.ts 中,两种核心状态被明确定义:
CheckpointState:记录checkpointMessageCount(checkpoint 时内存消息数,含追加后的工具结果)、checkpointEntryId(用于会话树分支的条目 ID)、startedAt时间戳;CompletedRewindState:记录report、startedAt(被回卷的 checkpoint 时间戳)、rewoundAt(回卷完成时间戳)。
checkpoint 的选择是隐式的:rewind总是瞄准单一的#checkpointState——它来自最后一次未完成的成功checkpoint调用,或从持久化状态重新水合。没有 checkpoint 列表、标签或 ID 参数。
branchWithSummary()的实现位于 session-manager.ts:若branchFromId非空且不在索引中则抛错;将叶子设置为分支点,然后记录一条type: "branch_summary"的条目,携带fromId(或"root")、summary与details。
而持久化条目进入模型视野的桥梁在 session-context.ts:当重建上下文遇到branch_summary且 summary 非空时,createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)将其转换为branchSummary消息。这就是“被剪除的探索路径以摘要形式延续”的实现机制。
实战要点小结
- 使用 rewind 前,必须先确保
checkpoint.enabled = true(默认关闭),并在会话中先调用checkpoint记录目标; report必须非空,建议用一句话概括探索结论与后续建议,因为它将成为新分支下模型可见的<summary>与开发者角色的保留指引;- 不要在同一 checkpoint 上重复调用
rewind——第二次会得到 “already completed” 错误,应直接基于保留报告继续; - 若需要再次探索,需重新调用
checkpoint(正如 rewind-report.md 所提示的:"Need explore again → newcheckpoint"); - rewind 只回卷会话上下文,不恢复文件与 git 状态,涉及代码回退时应配合版本控制工具使用。
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考