会话文件格式
会话以 JSONL(JSON Lines)文件存储。每行是一个带有 type 字段的 JSON 对象。会话条目通过 id/parentId 字段形成树形结构,支持原地分支而无需创建新文件。
文件位置
其中 <path> 是工作目录,/ 替换为 -。
删除会话
可以通过删除 ~/.pi/agent/sessions/ 下的 .jsonl 文件来移除会话。
Pi 也支持在 /resume 中交互式删除会话(选择会话后按 Ctrl+D,然后确认)。如果可用,Pi 会使用 trash CLI 避免永久删除。
会话版本
会话在头部有一个版本字段:
- 版本 1:线性条目序列(遗留格式,加载时自动迁移)
- 版本 2:通过
id/parentId链接的树形结构 - 版本 3:将
hookMessage角色重命名为custom(扩展统一)
现有会话在加载时会自动迁移到当前版本(v3)。
消息类型
会话条目包含 AgentMessage 对象。理解这些类型对于解析会话和编写扩展至关重要。
内容块
消息包含类型化内容块的数组:
基础消息类型(来自 pi-ai)
扩展消息类型(来自 pi-coding-agent)
AgentMessage 联合类型
条目基类
所有条目(SessionHeader 除外)都继承 SessionEntryBase:
条目类型
SessionHeader
文件的第一行。仅包含元数据,不属于树的一部分(无 id/parentId)。
对于有父会话的会话(通过 /fork 或 newSession({ parentSession }) 创建):
SessionMessageEntry
对话中的一条消息。message 字段包含一个 AgentMessage。
ModelChangeEntry
用户在会话中切换模型时发出。
ThinkingLevelChangeEntry
用户更改思考/推理级别时发出。
CompactionEntry
上下文被压缩时创建。存储较早消息的摘要。
可选字段:
details:实现特定的数据(例如,默认值为{ readFiles: string[], modifiedFiles: string[] },或扩展的自定义数据)fromHook:true表示由扩展生成,false/undefined表示 Pi 生成(遗留字段名)
BranchSummaryEntry
通过 /tree 切换分支时创建,由 LLM 生成到公共祖先的左侧分支摘要。捕获被放弃路径的上下文。
可选字段:
details:文件追踪数据(默认为{ readFiles: string[], modifiedFiles: string[] }),或扩展的自定义数据fromHook:true表示由扩展生成,false/undefined表示 Pi 生成(遗留字段名)
CustomEntry
扩展状态持久化。不参与 LLM 上下文。
使用 customType 在重新加载时识别你的扩展条目。
CustomMessageEntry
扩展注入的消息,参与 LLM 上下文。
字段说明:
content:字符串或(TextContent | ImageContent)[](与 UserMessage 相同)display:true= 在 TUI 中以特殊样式显示,false= 隐藏details:可选的扩展特定元数据(不发送给 LLM)
LabelEntry
用户在条目上定义的书签/标记。
将 label 设为 undefined 可清除标签。
SessionInfoEntry
会话元数据(例如用户定义的显示名称)。通过 /name 命令或扩展中的 pi.setSessionName() 设置。
设置后,会话名称会在会话选择器(/resume)中替代第一条消息显示。
树形结构
条目形成一棵树:
- 第一个条目的
parentId为null - 每个后续条目通过
parentId指向其父节点 - 分支从较早的条目创建新的子节点
- "叶子"是树中的当前位置
上下文构建
buildSessionContext() 从当前叶子遍历到根,生成提供给 LLM 的消息列表:
- 收集路径上的所有条目
- 提取当前模型和思考级别设置
- 如果路径上有
CompactionEntry:- 先输出摘要
- 然后是从
firstKeptEntryId到压缩点的消息 - 然后是压缩点之后的消息
- 将
BranchSummaryEntry和CustomMessageEntry转换为适当的消息格式
解析示例
SessionManager API
以编程方式处理会话的关键方法。
静态创建方法
SessionManager.create(cwd, sessionDir?)- 新建会话SessionManager.open(path, sessionDir?)- 打开现有会话文件SessionManager.continueRecent(cwd, sessionDir?)- 继续最近的会话或创建新会话SessionManager.inMemory(cwd?)- 无文件持久化SessionManager.forkFrom(sourcePath, targetCwd, sessionDir?)- 从另一个项目分支会话
静态列表方法
SessionManager.list(cwd, sessionDir?, onProgress?)- 列出目录的会话SessionManager.listAll(onProgress?)- 列出所有项目的全部会话
实例方法 - 会话管理
newSession(options?)- 开始新会话(选项:{ parentSession?: string })setSessionFile(path)- 切换到不同的会话文件createBranchedSession(leafId)- 将分支提取到新的会话文件
实例方法 - 追加(全部返回条目 ID)
appendMessage(message)- 添加消息appendThinkingLevelChange(level)- 记录思考级别变更appendModelChange(provider, modelId)- 记录模型变更appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)- 添加压缩appendCustomEntry(customType, data?)- 扩展状态(不在上下文中)appendSessionInfo(name)- 设置会话显示名称appendCustomMessageEntry(customType, content, display, details?)- 扩展消息(在上下文中)appendLabelChange(targetId, label)- 设置/清除标签
实例方法 - 树导航
getLeafId()- 当前位置getLeafEntry()- 获取当前叶子条目getEntry(id)- 按 ID 获取条目getBranch(fromId?)- 从条目遍历到根getTree()- 获取完整树结构getChildren(parentId)- 获取直接子节点getLabel(id)- 获取条目的标签branch(entryId)- 将叶子移至较早的条目resetLeaf()- 重置叶子为 null(在任何条目之前)branchWithSummary(entryId, summary, details?, fromHook?)- 带上下文摘要分支
实例方法 - 上下文与信息
buildSessionContext()- 获取用于 LLM 的消息、思考级别和模型getEntries()- 所有条目(不含头部)getHeader()- 会话头部元数据getSessionName()- 从最新的 session_info 条目获取显示名称getCwd()- 工作目录getSessionDir()- 会话存储目录getSessionId()- 会话 UUIDgetSessionFile()- 会话文件路径(内存模式为 undefined)isPersisted()- 会话是否已保存到磁盘