Session Tree、Context 投影、Compaction 与 JSONL¶
pi Coding Agent 的 Session 并不是一条从头排到尾的消息数组,而是由 Session Entries 组成的可分支树,当前 Leaf 会从树中选出正在使用的路径。只有 Message、Custom Message、Branch Summary 和 Compaction 等 Entry 才会沿这条路径投影成模型 Context,而 JSONL Repo 保存的则是完整 Entry 序列。两者不是同一份数据。
Session Entry Tree
├─ 当前活动路径 → UI / 导航
└─ Context Entries → 模型 Messages
│
└─ Compaction Summary 替代旧前缀
完整 Entries → JSONL Session Repo
第 1 站:活动路径与模型 Context 分开构造¶
const path = buildSessionPath(entries, leafId, byId)
const messages = buildContextEntries(entries, leafId, byId)
.flatMap(sessionEntryToContextMessages)
return { messages, thinkingLevel, model }
- 调用者:打开、Resume、Fork、Navigate 或 Compact 后的 Session Manager。
- 输入:全部 Entries、当前 Leaf ID 与索引。
- 状态变化:不改原树;计算活动路径和模型可见 Entry 子集。
- 返回:Messages、Thinking Level 与 Model。
- 下一站:Agent Core 用这些消息继续 Prompt。
Label、普通 Custom Entry 和部分 UI 状态即使写进了 Session 文件,也不一定会进入模型,因此调试「为何模型忘了」时,应该检查 buildContextEntries() 的结果,而不能只凭文件里有没有那一行作判断。
Compaction 选择最新 Summary 与后续尾部¶
每次压缩都会写入一个 Summary Entry,而同一条路径经过多次压缩以后,Context 会从最新的有效 Summary 开始,再接上它后面的消息。旧 Entry 并未消失,审计和创建分支时仍然可以回到 Session Tree 中找到它们。
源码:查看 Entry 到 Context Message 的转换
- 调用者:
buildContextEntries()。 - 输入:沿当前 Leaf 的 Session Entries。
- 状态变化:用最新 Compaction/Branch Summary 建立有界模型视图。
- 返回:按角色排序的 Agent Messages。
- 下一站:模型请求;完整 JSONL 不被摘要覆盖。
Summary 是模型生成的有损结果,所以内容里应该保留精确路径、未完成事项以及关键 Tool Result 的引用,并在压缩完成后用确定性问题核对关键信息是否还在。不要只看 Token 是否下降。
为什么保留树,而不是把旧消息直接删除¶
Compaction 要解决的是当前模型窗口装不下历史的问题,而不是替审计存储腾出空间。完整的 Entry Tree 还要承担多种职责——回看记录、Fork、比较分支和解释摘要来源。一旦直接把旧前缀从存储中删除,不但无法再追溯「模型为什么得出这个摘要」,也会失去从较早节点创建新分支的能力。
第 2 站:JSONL Repo 处理尾部损坏和追加队列¶
源码:查看 JSONL Repo 的创建、打开与 Fork、查看尾部修复,以及查看追加队列。
- 调用者:Session Manager 创建、打开、追加和 Fork。
- 输入:CWD、Session ID、Entries 与目标路径。
- 状态变化:同一进程内预留
{cwd,id},串行追加 JSONL;打开时验证/修复尾部。 - 返回:Open Session、Writer 或类型化损坏错误。
- 下一站:Session Tree 重建并选择 Leaf。
尾行能够修复,并不意味着文件里的任何损坏都可以忽略,因为中间行一旦损坏,Entry 之间的父子关系也可能随之断裂。此时应拒绝打开,并保留原文件副本。
Resume 不能恢复外部世界¶
Session 可以恢复消息、模型选择、Thinking Level 和分支信息,却无法让已经退出的进程重新运行,也不能还原远端请求、外部数据库或工作区后来发生的修改。因此 Resume 完成以后,还要重新检查 CWD、Git Diff、工具可用性和凭据。
同一进程里的 Destination Reservation 也不能充当跨进程 Lease,如果多个进程会共享同一份 Session 存储,就还需要文件锁、Fencing 或其他后端事务来协调写入。
回到运费任务¶
如果任务在编辑文件以后中断,Session Tree 里可能同时留有原始 Tool Call、工具开始事件、完成结果以及后来生成的摘要。恢复时不能因为摘要写着「已经修改」就认定工作完成,而要把活动分支上的工具完成记录与当前文件差异对照起来。若新分支是从编辑前的节点 Fork 出来的,它就不该继承编辑后的 Tool Result,即使那条结果仍保存在同一个 JSONL 文件中。
练习:解释「文件里有,模型却不知道」¶
你在 Session JSONL 中找到一条 Label 和一条旧分支的 Message,但下一轮模型都没有看到。应该先检查什么?