OpenCode Session & Runner
SessionV2
OpenCode 的 Session 是全事件溯源的 durable session。这是它和 Pi/Hermes 最大的架构差异。
text
packages/core/src/session.ts核心模型
typescript
interface SessionV2 {
id: string;
input: SessionInput; // 输入项(user messages, tool approvals)
events: EventV2[]; // 事件流(完整执行历史)
state: SessionState; // 派生状态(从 events 投影)
}EventV2 事件溯源
OpenCode 将每个操作建模为事件:
typescript
type EventV2 =
| UserMessageEvent // "用户发送了消息"
| AssistantDeltaEvent // "模型返回了 delta"
| ToolCallStartedEvent // "开始执行工具"
| ToolCallCompletedEvent // "工具执行完成"
| PermissionAskedEvent // "请求权限"
| SessionCompressedEvent // "会话被压缩"
| StopEvent // "停止信号"
| ErrorEvent // "错误"所有事件写入 SQLite,状态可以被随时重放。
SessionV2.prompt() 调用
typescript
class SessionV2 {
prompt(input: string, options: PromptOptions): Effect<PromptResult> {
// 1. 构建 session_input
// 2. 确保 session 被唤醒(wake)
// 3. 触发 SessionExecution.local().execute()
// 4. 返回最终响应
}
}SessionRunner
text
packages/core/src/session/runner/SessionRunner 是实际运行 agent 的引擎:
SessionRunner.run()
↓
1. 准备 context(历史消息 + system prompt + 工具)
2. LLM.request()
3. 处理响应
├─ text delta → 追加到 stream
└─ tool_calls → ToolRegistry.settle()
4. 事件持久化
5. 循环直到完成或达到限制与 Hermes 的 Session 对比
| 维度 | OpenCode | Hermes |
|---|---|---|
| 存储 | SQLite (event-sourced) | SQLite + FTS5 (message-centric) |
| 投影 | 状态从事件重放 | 状态直接存储 |
| 压缩 | 事件压缩 + 重放 | 消息压缩 + 摘要 |
| 搜索 | 事件查询 | FTS5 全文搜索 |
| 可恢复性 | 完全(事件流完整) | 部分(消息历史完整) |
为什么 Event Sourcing?
OpenCode 使用事件溯源的好处:
- 审计追踪:完整知道 agent 做了什么
- 调试:回放任何一个 session
- 断点续传:crash 后从最后事件恢复
- 多客户端:event stream 可以推送多个 TUI/web 客户端
代价是复杂度比 Hermes 的消息存储方式高不少。