第 9 章:持久化与工作区 —— SQLite 记录事实,Location 选择运行环境¶
1. 为什么 coding agent 必须持久化¶
一次 coding task 可能持续几十分钟,包含大量工具输出,用户还可能:
- 关闭 UI 后重新打开;
- 从同一个 session 继续 prompt;
- revert / fork / share;
- 在多个 worktree 间切换;
- 让多个客户端观察同一个执行。
因此历史、输入箱、事件、compaction、context snapshot、project directory 等必须脱离进程内存。
2. 数据层的角色¶
packages/core/src/database 通过 Effect、Drizzle 和 SQLite 提供数据库服务,migration 记录 schema 演进。源码中的迁移名字透露出领域变化轨迹:workspace、session location、event、context snapshot、session input inbox、projection index 等。
可以把数据分成四类:
| 类别 | 典型记录 | 作用 |
|---|---|---|
| 领域事实 | project、session、message、part | 用户可见历史和资源 |
| 执行输入 | session input / prompt admission | 等待被 promotion 的工作 |
| 运行投影 | session message projection、status、usage | API/UI 查询友好 |
| 上下文控制 | Context Epoch、snapshot、compaction | 重建 provider request |
3. Project、Directory、Workspace、Location¶
这些词很容易混淆:
- Project:OpenCode 识别和持久化的代码项目;
- Directory:当前请求 / Session 实际工作目录;
- Workspace:可以包含多个项目或目录的更高层作用域;
- Location:运行时服务选择依赖的坐标,至少包含 directory,可带 workspaceID;
- Worktree:Git 工作树,是项目目录的一种实际布局。
specs/project.md 和 AGENTS.md 共同说明了方向:一个实例服务多个项目和不同 worktree,Session、Runner、Provider、Tool Registry、Filesystem 都要按 Location 作用域解析。
4. Session 的消息不是一个 JSON 大 blob¶
OpenCode 的消息结构分成 info 和 parts:
Session Message
├─ info: role / id / parent / model / usage / timing
└─ parts[]
├─ text
├─ reasoning
├─ tool (pending/running/completed/error)
├─ file / snapshot / patch
├─ compaction / summary
└─ metadata / native continuation
Part 粒度让流式更新、工具状态和 UI 渲染更细;projector 可以增量更新一个 tool part,而不必重写完整 assistant message。
5. Snapshot、Patch、Revert¶
coding agent 的“对话历史”和“文件历史”相互关联但不相同。Snapshot 服务跟踪工作树在某些时间点的状态;patch 表示修改内容;revert 作用于 session message / file state 的组合语义。
这样模型可以说“回到某个消息之前”,而系统不必把每一次文件修改都当成普通文本塞进上下文。
6. 迁移文件为什么也是设计文档¶
数据库 migration 比 README 更诚实地记录需求的时间顺序。例如:
旧历史结构
→ workspace / session location
→ event sequence / projection index
→ input inbox
→ context snapshot / epoch
→ V2 session state reset / simplified input
阅读 migration 时应关注:
- 新表是事实还是 cache;
- 是否有 backfill / data migration;
- 旧字段何时被保留为兼容;
- index 是否服务于 history cursor / replay;
- 运行时是否已开始读取新字段。
7. Tool output file 是“外置上下文”¶
完整工具结果写文件,不是简单日志。它具有:
- 生命周期管理;
- session / tool call 关联;
- 截断时的可发现路径;
- 后续 read / UI 展示入口;
- 让数据库和 provider message 保持小的预算。
这也是为什么 Core 里出现 tool-output-store.ts,而不是把全部输出留在 SessionMessage。
本章小结¶
OpenCode 的持久化设计把“事实、执行输入、查询投影、上下文控制”分开。Location 把这些数据和运行时服务放回正确的项目作用域;SQLite 让 Session 在客户端断开或进程重启后仍可恢复和重放。