opencode v2 架构路线图:Effect 原生 Agent 循环、session_input 收件箱与 EventV2 事件核心
specs/v2/todo.md 是 opencode 团队推进 v2 发布的架构路线图文档,记录了从 Hono 迁移到 Effect HttpApi 之后,服务端在 Agent 执行循环、提示词准入、事件持久化三条主线上“已完成什么、还剩什么”。读完本篇,你能理解 v2 中 SessionExecution 进程级恢复语义、session_input 持久化收件箱的 steer/queue 投递模型、EventV2 聚合事件序列的实现位置,以及官方明确列为“后续切片”的崩溃恢复与集群化边界。
路线图的背景与总体结构
v2 的总体目标是“离开重建阶段(rebuild phase)”,走向正式发布。路线图分为三大块:
- Post-Hono cleanup(Kit):opencode server 已迁移到 Effect HttpApi 后端,剩余工作以清理为主——删除兼容 shim、收缩 Zod 表面、简化过去用于对比 Hono 与 HttpApi 行为的测试装置;
- New Data Mode(Dax):基本完成,团队正在处理子代理(subagent)、技能调用(skill invocations)与 shell 命令的建模;
- 各垂直切片:Agent 循环、事件系统、插件 API、配置、鉴权、模型库、Provider 插件化等,每个切片标注了负责人与剩余工作量。
其余章节(Agent 循环、Event、加固清理、热重载)则是路线图的技术主体,下面逐条结合仓库源码展开。
Agent 循环重做:第一个 Effect 原生本地运行器
路线图“Rework agent loop”一节确认:第一个 Effect 原生的本地 runner 切片已经实现,且不再通过旧版 SessionPrompt.loop(...) 桥接。它给出的实现要点与源码位置一一对应:
- 进程级
SessionExecution.resume(sessionID)。服务接口定义了四个原语:active(快照本进程拥有的活跃执行)、resume(空闲时启动、活跃时加入)、wake(登记新的合并式后续唤醒)、interrupt(中断本进程拥有的活动工作,空闲时为空操作)。服务注释明确其职责是“从 Session ID 路由到该 Session 所属 Location 的运行器”。 - Location 级缓存的
SessionRunner。路由层从 Session 读模型查出location,经LocationServiceMap取对应 Location 的服务层,再调用SessionRunner.run({ sessionID, force })。Runner 接口注释说明它“从已记录的 Session 历史执行一次本地续跑”;显式 run(force: true)即使没有合格工作也会做一次 provider 尝试。Runner 每次只解析一个受支持的 catalog 模型,并按“一次一个显式llm.stream(request)provider 回合”的方式推进。 - 持久化 V2 投影。投影器记录文本、推理、provider 失败、工具调用、工具结果与助手输出,续跑时重新加载投影历史;新的用户输入会重置所选 agent 配置好的 provider 回合配额。
- 作用域
ToolRegistry。工具注册表以@opencode/v2/ToolRegistry服务形式存在,通告工具定义并提供首个带权限检查的read内建工具。 - 同 Session 并发恢复合并、跨 Session 并发。这一语义由 SessionRunCoordinator 保证:接口注释即“对每个 key 串行化执行,同时允许不同 key 并发运行”。run 实现用
uninterruptibleMask检查活跃表:若该 Session 已有执行,直接await其doneDeferred 加入等待;wake则通过pendingWake标志合并重复唤醒,执行结束后若存在挂起唤醒会启动一个 successor fiber 继续 drain。
提示词准入:持久化的 session_input 收件箱
文档特别强调:提示词准入改为使用持久化的 session_input 收件箱,而非立即投影进 transcript。投递模型分两档:
steer输入在下一个安全的 provider 回合边界提升(promote),当前 drain 需要续跑;queue输入保持 FIFO,直到 Session 否则会进入空闲,再逐个提升。
源码印证了这一设计。SessionInput.admit 并不直接插表,而是先发布 SessionEvent.PromptAdmitted 持久化事件,事件携带的聚合序列号(event.durable.seq)回写成 admitted_seq,返回 Admitted 记录(含 delivery 字段);projectAdmitted 才把行写入 session_input 表,且若对应消息已存在于 transcript 表则以 LifecycleConflict 终止,保证“收件箱”与“已投影消息”两种状态互斥。promoted_seq 由 projectPrompted 在提升时一次性更新(isNull(promoted_seq) 条件防止重复提升)。Delivery 类型来自 schema 包。相关数据库迁移可按 event_sourced_session_input、session_input_inbox、simplify_session_input 三个命名在 database/migration 目录下追溯其演进。
下一步评审的切片
路线图列出的下一批切片(未完成,作为官方计划引用):
- 保留急切的本地工具结算(eager structured local-tool settlement):每个完整工具调用先持久化记录,立即启动其子执行,provider 回合结束后等待全部结算,最后只重载一次投影历史;
- 重新审视每回合工具调用上限、输出截断与背压:当前本地切片中急切本地执行是故意无界的,而 SQLite 发布保持串行;
- 移除公开内存态
@opencode-ai/llm工具循环,待其剩余的单回合 native-adapter 用法被窄化类型分发器替代; - 批量流式 delta 并补充覆盖索引;
- 通过 HTTP 与生成的 SDK 暴露可重放的 Session 事件游标,供远端消费者使用;
- 把 BackgroundJob 服务接入 V2 工具执行:支持后台 bash 任务与后台 agent 派发,带持久化状态观察、完成投递与显式取消/续跑语义;
- 持久化/集群化中断、重试与陈旧 owner 围栏仅在切片具体化后再加。
推迟的持久化续跑恢复(Deferred durable continuation recovery)
这一节是路线图中设计约束最密集的部分,核心告诫是:不要从“建议性唤醒(advisory wake)”推断模糊的 provider 工作可以安全重试。首个收件箱驱动的 runner 故意省略外层 provider-attempt 标记,直到它有具体消费者和完整恢复策略。文档要求把崩溃后的续跑恢复设计成一个显式切片,应建模以下事实:
- 已提升输入与投影历史的状态;
- 排队输入的提升与 steering 分配;
- provider-attempt 准备阶段与 provider-dispatch 模糊性的区分;
- 工具之后跨进程丢失的必需续跑;
- 对未知结果的显式
retry/abandon决策; - 仅在 provider 与工具幂等性保证安全时才做有界自动重试;
- 重试预算、退避、可见的恢复状态、启动发现,以及未来的集群所有权围栏。
并明确禁止:不要仅为“分组这些事实”而引入一个外层持久化执行身份——进程本地的 Session drain 没有持久化 transcript 边界。
事件系统:EventV2 自包含持久化核心
路线图的 Event 一节(负责人 Kit)确认:自包含的持久化 EventV2 核心服务已实现,拥有带同步版本的持久化、事务性排序、pub/sub、重放,以及不依赖旧 bus 系统的 replay-owner 声明。
EventV2 源码印证了其形态:事件按 aggregateID 组织,latestSequence 从 event_sequence 表读取当前序列(缺省 -1),readAggregate 支持带 after/limit 的聚合读取,并通过 durable 事件 manifest(@opencode-ai/schema/durable-event-manifest 的 Durable.get(type))做类型校验,未知 durable 事件类型会抛 InvalidDurableEventError。这与文档描述的“事务性排序 + 可重放”一致。
剩余切片只有两条:
- 在远端消费者需要时,通过 HTTP 与生成的 SDK 暴露内建的消费者侧 Session 游标 API(与 Agent 循环一节中的游标切片相互呼应);
- 保持 replay-owner 声明与未来的集群 Session 执行所有权、陈旧运行时围栏彼此独立。
相关测试见 event.test.ts 与 session-run-coordinator.test.ts。
其余垂直切片:插件、配置、鉴权、模型库与 Provider
- Plugin API 设计(James):需确定服务端插件的工作方式与有用钩子。文档中的初步想法包括:插件获得 immer draft,使坏突变可以被丢弃;插件获得全局
opencode实例(如opencode.session.prompt()、opencode.tool.register({...}))。 - Config 重做:对配置再来一轮清理,修正既犯的错误并尽量简化;旧配置应自动转换为新格式。
- Auth:已有一个可跟踪任意类型鉴权(不仅是 provider)的基础鉴权系统。
- Model Database:已有一个允许模型动态注册的基础模型服务。
- Provider:Provider 应以插件形式注册,基于各自的逻辑/配置自动加载,并把模型注册进模型库。
这五条构成“一切皆插件”方向的骨架:Provider、模型、鉴权、插件最终都收敛到插件化注册模型上。
Deferred hardening cleanup:不阻塞功能切片的加固清单
路线图单列了“推迟的加固清理”,原则是:保持可见,但不阻塞功能切片,除非 canary 期间出现具体故障。完整清单如下:
- 跨进程串行化数据库迁移声明——当前迁移仅受进程内信号量保护,两个进程同时启动针对同一个 SQLite 库仍可能竞态;
- 用 Effect
RcMap加每个活跃聚合一个共享PubSub.sliding<void>(1)简化进程本地持久化尾部唤醒生命周期,同时保持 SQLite 游标重放与 subscribe-before-history 语义不变; - 对大型持久化聚合的重放读取做分页,而不是把陈旧游标之后的每一行加载进一个数组;
- 决定已连接尾部是否需要面向跨进程 SQLite 写方的周期性轮询兜底——当前建议性唤醒是故意进程本地的;
- 在解析前对流式受限地收集 websearch 响应体;
- 为 ripgrep 增加执行超时与有界行分帧;
- 对未解析的 URL 与文件附件来源做“实体化或一致拒绝”的决定;
- 决定无状态 OpenAI Responses hosted-tool 的续跑行为:当
store !== false时,重建的托管输出可作为存储的item_reference重放;store: false则有意省略不可用的引用路径; - 决定是否保留已弃用的
@opencode-ai/llm编排导出; - 若兼容消费者需要,保留或别名已重命名的文件系统 SDK 生成类型名;
- 针对敌意外部进程重新审视系统调用级的突变围堵(
openat、O_NOFOLLOW、受支持平台上的描述符相对突变)。
愿景:Everything is hotreloadable
路线图的最后一条描绘了目标形态:不再需要因变更而拆除重建——每个服务都应发出细粒度事件,其他服务据此反应并自我重配置。前端同样能收到这些事件(例如 model.added),同时这也让启动不必阻塞等待。这与 EventV2 的 pub/sub 能力、以及 New Data Mode 中“模型可动态注册”的基础设施方向一致,可以推断 v2 的事件总线最终会成为跨服务热重载的统一机制。
如何继续深入
- 路线图原文:specs/v2/todo.md,同目录下还有 session.md、config.md、tools.md、provider-model.md 等 v2 设计文档;
- 执行与协调:execution.ts、execution/local.ts、run-coordinator.ts、runner/index.ts;
- 提示词准入:session/input.ts、schema/src/session-input.ts;
- 事件核心:event.ts、tool/registry.ts;
- 行为验证:session-runner.test.ts、session-runner-tool-registry.test.ts、session-run-coordinator.test.ts、event.test.ts。
需要注意的是,本文基于当前仓库状态:路线图中标注“已完成”的切片(本地 runner、session_input 收件箱、EventV2 核心)在 packages/core 源码中均有对应实现与测试;而“下一步切片”“推迟的恢复设计”“加固清单”属于官方计划中的未实现工作,引用时应以该文档的表述为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00