跳转至

第 12 章:阅读方法 —— 如何继续深入 OpenCode 源码

1. 推荐的源码阅读路线

路线 A:理解一次 prompt

httpapi/groups/session.ts
  → handlers/session.ts
  → core/session/input.ts
  → core/session/execution.ts
  → core/session/runner/llm.ts
  → core/session/runner/to-llm-message.ts
  → llm/route/client.ts
  → core/session/runner/publish-llm-event.ts
  → schema/session-event.ts

路线 B:理解一次工具调用

tool/registry.ts
  → tool/tool.ts
  → permission/index.ts
  → concrete tool (read / edit / shell / task)
  → truncate / tool-output-store
  → tool part event
  → next SessionRunner turn

路线 C:理解一次上下文变化

system-context/builtins.ts
  → system-context/registry.ts
  → session/context-epoch.ts
  → session/history.ts
  → mid-conversation system message
  → LLMRequest.system + messages

路线 D:理解 UI 为什么更新

LLMEvent
  → EventV2 publisher / bridge
  → public event manifest
  → SSE / Sync
  → client / sdk
  → app / tui session store
  → timeline / tool renderer

2. 读一个新模块时的五个问题

  1. 输入是 durable fact、runtime state 还是 user configuration?
  2. 输出是领域状态、LLM projection、HTTP contract 还是 UI projection?
  3. 作用域是 global、workspace、Location、project 还是 session?
  4. 失败时应该 throw、变成 event、变成 tool result,还是被降级?
  5. 是否必须可 replay,还是纯实时通知就够?

3. 关键设计决策清单

决策 解决的问题 代价
Effect Layer 依赖可替换、资源有生命周期 初读需要理解 Layer / Service
Location scope 多项目、多 worktree、单 server 每个服务不能偷读全局目录
Durable input admission 与 execution 解耦 需要 inbox、promotion 和 cursor
Event V2 实时与重放统一 需要 manifest、projector、bridge
Context Epoch 稳定 baseline + 动态更新 需要 snapshot、epoch、update message
Canonical LLM IR 吸收 provider 差异 需要 adapter 和 metadata 保真
Tool Registry 权限、schema、输出治理集中 工具不能随手直接调用系统 API
SDK boundary 多端共享契约 API 缺能力时必须先扩展后端

4. 常见误读

误读 1:session/prompt.ts 就是全部 Agent Loop

它是重要的 legacy orchestration,但 V2 的规范化执行链在 packages/core/src/session/runner。如果只读前者,会漏掉 durable input、Context Epoch 和 Location ownership。

误读 2:SSE 事件就是数据库事实

事件可能先到达订阅者,再被 projector 落盘。要判断最终状态,要追踪 event durable semantics 和 projected table。

误读 3:Provider plugin 等于模型适配器

Provider 还涉及 catalog、credential、auth、transform、native metadata、usage 和错误。只加一个 createOpenAI() 并不能保证 Session continuation 正确。

误读 4:TUI 复杂,所以 backend 逻辑应该搬进 TUI

相反,复杂 TUI 更需要清晰 SDK wire contract 和 tolerant renderer。specs/tui-package.md 明确禁止 TUI 依赖 backend implementation。

5. 术语速查

术语 一句话解释
Session History 经过 Epoch / compaction 选择后,发给 provider 的时间顺序消息投影
Session Drain 当前进程从 durable work 执行到 settle 的本地连续运行段
Safe Provider-Turn Boundary durable promotion、tool settlement 完成后,允许接纳上下文变化的位置
Context Source 有 stable key、codec、loader 和 renderer 的 typed context 单元
Context Snapshot 用于比较 source 上次有效值的模型隐藏 JSON 状态
Model Tool Output 写入 Session、受大小约束的工具输出投影
Managed Tool Output File 保存完整工具输出的临时文件
Native Continuation Metadata provider 要求下一轮继续使用的 opaque protocol metadata
Embedded OpenCode 复用同一 router,通过内存 HttpClient 运行的同进程 host

6. 最后一个架构判断

如果把 OpenCode 压缩成一句话:

OpenCode 是一个 Location-scoped、Effect-native、事件驱动的 coding-agent server;它把用户输入、模型请求、工具副作用和 UI 状态都放到可投影、可重建的边界上。

这比“支持很多 provider 的 CLI”更接近源码真实复杂度,也解释了为什么项目需要 Core、Server、SDK、Session V2 和多个客户端。

源码锚点总表