Session 是 Claude Code 最容易被低估的部分。它不只是“消息数组”,而是贯穿当前会话、子 agent、持久化、恢复和日志边界的核心聚合。
src/utils/sessionStorage.tstranscript 持久化与恢复主实现src/bootstrap/state.tssession id、cwd、project dir 等运行时全局状态src/QueryEngine.ts会话内消息与 usage 的主控制器src/utils/sessionRestore.ts恢复逻辑
flowchart TB
Runtime[当前会话运行时]
Engine[QueryEngine mutableMessages]
Storage[sessionStorage.ts]
Transcript[session jsonl]
Agent[subagent transcripts]
Restore[session restore]
Runtime --> Engine
Engine --> Storage
Storage --> Transcript
Storage --> Agent
Transcript --> Restore
Agent --> Restore
Claude Code 把 transcript 视为会话真相源,但不是所有 UI 消息都会进 transcript。
src/utils/sessionStorage.ts 明确区分:
- transcript message
- progress message
其中:
- user / assistant / attachment / system 会参与 transcript
- progress 属于 UI 临时态,不应参与 parentUuid chain
这是一条非常关键的约束。
因为 progress 是高频、瞬时、不可复算的 UI 状态。
如果把它混进会话链,会导致:
- parentUuid 链断裂
- resume 时链路混乱
- transcript 体积爆炸
Claude Code 在代码里专门为此写了兼容旧数据的桥接逻辑。
sequenceDiagram
participant E as QueryEngine
participant S as sessionStorage
participant F as session file
E->>S: recordTranscript(...)
S->>S: 过滤 transcript message
S->>F: append jsonl
Note over S,F: 仅持久化真正参与因果链的消息
sessionStorage.ts 还专门支持 subagent transcript:
- 主 session transcript
subagents/目录下的 agent transcript
这很重要,因为 Claude Code 并不是把所有 agent 输出混成一份大日志,而是保留层级结构。
Session 文件路径不是简单 cwd + sessionId,而是通过 project dir 和 session project dir 统一管理。
这么做是为了避免:
- symlink 引起的路径漂移
- resume 后 session 写到另一个目录
- 主 transcript 与 subagent transcript 脱节
QueryEngine 负责会话运行时状态:
mutableMessagesreadFileStatetotalUsage
sessionStorage.ts 负责持久化和恢复:
- 写 transcript
- 读取 transcript
- 管理 agent transcript path
- 处理兼容和边界条件
重点看:
isTranscriptMessage()isChainParticipant()getTranscriptPath()getAgentTranscriptPath()
看运行时会话态和持久化系统如何衔接。
- transcript 被严格定义为“可参与真实会话链的消息”
- progress message 与 transcript message 明确隔离
- subagent transcript 有独立目录结构
- resume、兼容、路径一致性都被当作一等工程问题处理
建议顺序:
src/utils/sessionStorage.tssrc/bootstrap/state.tssrc/QueryEngine.tssrc/utils/sessionRestore.ts