这一章解释 Claude Code 为什么能“继续上一轮”:transcript 存在哪里,恢复时会还原哪些状态,subagent transcript 和 worktree 状态又是怎么挂回来的。
Claude Code 的会话恢复不是只把历史消息重新读一遍。当前快照里,恢复链路至少同时处理:
- 主 transcript JSONL
- subagent transcript 与 metadata
- 文件历史与 attribution 快照
- context collapse 边界
- TodoWrite 产生的待办状态
- session mode / agent / model override / worktree
也就是说,resume 更接近“重建一个持续中的工程会话”,而不是“把聊天记录贴回来”。
- transcript 与 session metadata:src/utils/sessionStorage.ts
- 会话恢复主逻辑:src/utils/sessionRestore.ts
- 对话消息恢复与清洗:src/utils/conversationRecovery.ts
flowchart TD
A["sessionId / transcript path"] --> B["读取 JSONL transcript"]
B --> C["deserializeMessages / 清洗中断与孤儿消息"]
C --> D["restoreSessionStateFromLog"]
D --> E["文件历史 / attribution / context collapse / todos"]
B --> F["restoreSessionMetadata"]
F --> G["session mode / agent / model override"]
G --> H["restoreWorktreeSession"]
E --> I["恢复后的 AppState / QueryEngine 初始消息"]
H --> I
src/utils/sessionStorage.ts 直接表明:
- 主会话 transcript 使用 JSONL 存储。
- 默认落在 Claude 配置目录下的
projects/中。 - transcript 路径与
sessionProjectDir绑定,避免 resume 后路径漂移。 - subagent transcript 单独写到
sessionId/subagents/...目录。 - transcript 中真正参与 parentUuid 链的只有 transcript message;旧版
progress会被桥接或跳过。
这一层说明 Anthropic 把“会话状态”视为结构化日志,而不是单个大 JSON blob。
sessionStorage.ts 里把 user、assistant、attachment、system 定义为 transcript message,同时明确把 progress 排除在 parentUuid 链之外。注释还说明旧版 progress entry 会导致 resume 后真实消息被 orphan。
这意味着恢复系统的第一原则不是“保留一切 UI 细节”,而是“保证消息链和 API 语义稳定”。高频进度条可以丢,真正参与推理的消息不能乱。
src/utils/sessionRestore.ts 的 restoreSessionStateFromLog() 直接恢复:
- 文件历史快照
- attribution 状态
- context collapse commit log 与 snapshot
- TodoWrite 产生的 todos
同一文件还会恢复:
- standalone agent 的名称与颜色
- resumed session 的 agent type
- resumed agent 对应的 model override
- coordinator / normal mode 切换后的 agent definition 刷新
所以 resume 不只是恢复消息,还恢复“这个 session 当时处在什么工作模式里”。
src/utils/conversationRecovery.ts 负责把 transcript 重新变成可继续推理的消息序列。按函数命名和注释可确认,它会处理:
- 旧 attachment 类型迁移
- unresolved tool use
- orphaned thinking-only assistant message
- 纯空白 assistant message
- interrupted turn / interrupted prompt
这一步的意义很大。它说明 transcript 不是天然可 replay 的,需要先做“恢复期数据修复”。
sessionRestore.ts 里直接调用 restoreWorktreeSession(),也会在需要时写回 saveWorktreeState()。这说明 worktree 不是外部附加功能,而是 session identity 的一部分。
换句话说,如果一个 agent 会话是在隔离 worktree 中跑的,恢复它时不把 worktree 一起接回来,会话语义就是错的。
sessionStorage.ts 通过 setAgentTranscriptSubdir()、getAgentTranscriptPath() 给 subagent transcript 单独分目录,而且允许 workflow run 级别的 grouping。
这表明 Anthropic 在日志层已经接受了“一个主会话包含多个执行单元”的现实。主线程 transcript 和子代理 transcript 不是临时拼接,而是正式分层存储。
如果你在研究 Claude Code 的“持续运行能力”,这一章是关键,因为它揭示了几个事实:
- 会话恢复是日志重建,不是内存续命。
- 可恢复对象包括消息、权限模式、agent、worktree 和部分 UI/任务状态。
- transcript 格式为了恢复正确性专门排除了某些 UI-only entry。
- Claude Code 已经把 subagent / workflow 看成会话的一等公民。
Claude Code 的 resume 能力来自一套分层持久化设计:主 transcript 保存推理链,恢复逻辑补回执行状态,conversation recovery 清洗脏消息,worktree 与 subagent transcript 保持工程语义连续。