第 1 章:总览 —— OpenCode 不是一个 Loop,而是一台 Agent 产品运行时¶
1. 三种看 OpenCode 的视角¶
打开仓库时很容易被 packages/app、packages/opencode、packages/core 和一大组 workspace package 分散注意力。先固定三个视角:
视角一:产品¶
OpenCode 是一个开源 coding agent。用户可以从 CLI 启动 TUI,可以直接 run 一次任务,也可以运行 Server,让 Web、Desktop、远程 SDK 或 ACP 连接进来。README 中的 build、plan 和 general agent 只是产品层入口,真正的执行能力在服务层。
视角二:运行时¶
运行时由 Effect Layer 组装。packages/opencode/src/effect/app-runtime.ts 的 AppLayer 把 Database、Config、Provider、Session、LLM、LSP、MCP、ToolRegistry、Project、Workspace 等服务组合起来,再由 ManagedRuntime 提供 runPromise 等执行出口。
视角三:可恢复系统¶
Session 不是一组内存消息,而是由数据库、事件、消息投影、输入箱和 Context Epoch 组成的长期对象。SessionRunner 只负责当前进程拥有的 drain;它不拥有 Session 的永久身份,也不把“正在运行”写成一条不可恢复的黑盒状态。
2. 顶层数据流¶
flowchart TB
subgraph Hosts[宿主]
CLI[packages/opencode CLI]
TUI[packages/tui]
APP[packages/app]
DESKTOP[packages/desktop]
ACP[ACP adapter]
end
subgraph Boundary[稳定边界]
API[Effect HttpApi]
CLIENT[client / sdk-next]
EVENTS[Event V2 / Sync / SSE]
end
subgraph Runtime[OpenCode runtime]
SERVER[packages/opencode server]
CORE[packages/core services]
LLM[packages/llm]
end
subgraph State[状态]
DB[(SQLite)]
FS[(project filesystem)]
PROVIDER[Provider APIs]
end
CLI --> SERVER
TUI --> CLIENT
APP --> CLIENT
DESKTOP --> CLIENT
ACP --> SERVER
CLIENT --> API
API --> SERVER
SERVER --> CORE
CORE --> LLM
CORE --> DB
CORE --> FS
LLM --> PROVIDER
SERVER --> EVENTS
EVENTS --> TUI
EVENTS --> APP
3. 最值得先记住的五个边界¶
| 边界 | 责任 | 不应该做什么 |
|---|---|---|
| Schema | 定义 ID、消息、Part、Event、Prompt 的可编码形状 | 不放 UI 行为和 provider 具体调用 |
| Core | 领域服务、数据库、Session V2、工具和位置作用域 | 不依赖具体 UI 宿主 |
| Server | 启动 runtime、鉴权、路由、HTTP/WebSocket | 不让客户端导入后端私有模块 |
| LLM | canonical request / event 与 provider wire format | 不决定 Session 如何落盘 |
| Client / SDK | 把 HttpApi 变成调用者可消费的 API | 不复制 server orchestration |
这种分界让同一个 Session 能被 TUI 和 Web 同时观察;也让 provider 适配器可以替换,而不要求 TUI 知道 Anthropic 或 OpenAI 的请求格式。
4. OpenCode 的中心不是 prompt(),而是“事实 + 投影”¶
旧式 Agent 通常是:
OpenCode 的现实更接近:
durable input
→ promotion event
→ projected session history
→ canonical LLM request
→ streamed domain events
→ durable message / part projection
→ tool settlement
→ next provider turn
这里有两个重要推论:
- 模型请求是投影,不是事实本身。 需要重新请求时,系统可以从 durable history 和当前 Context Epoch 重建。
- 执行状态是局部的。 某个进程正在跑,并不意味着 Session 的全部语义只存在于这个进程的内存里。
5. 代码规模带来的阅读策略¶
OpenCode 的 packages/opencode/src 同时包含 CLI、V1 服务、V2 路由、插件、MCP、LSP、分享、安装和迁移代码。如果按目录顺序读,会把“产品功能”误认为“核心执行链”。建议先走这些入口:
packages/opencode/src/index.ts:CLI 命令地图。packages/opencode/src/effect/app-runtime.ts:运行时依赖地图。packages/opencode/src/server/server.ts:HTTP listener 和 runtime 生命周期。packages/opencode/src/server/routes/instance/httpapi/api.ts:API 组合边界。packages/core/src/session/runner/llm.ts:Session V2 的真实 provider-turn。packages/core/src/system-context/*:上下文的稳定基线和更新机制。packages/opencode/src/tool/registry.ts/packages/core/src/tool/*:两代工具系统的连接处。
不要从 UI 开始
UI 文件很多,但它们主要告诉你“服务器暴露了什么”。要理解“为什么状态会这样变化”,先读 Schema、Session、Event 和 API,再回到 TUI / App。
6. 本章小结¶
- OpenCode 是一个带 Server、数据库、SDK 和多端宿主的产品运行时。
- Session V2 把 prompt admission、provider turn、tool settlement 和事件回放拆开。
- Effect Layer 是依赖装配图,不只是异步语法。
- API 和 Event 是客户端与后端之间的主边界。