Files
goodbuddy/docs/review/review-2026-09-03.md
T
mesalogo 4490e5fd15 fix: stabilize Runtime workflows for 0.12.3
OpenCode status checks could cold-start throwaway servers, unrelated
conversations could block each other, and model, Continue, or remote paths
could replay or duplicate work under failure and backlog conditions. Settings
could also lose edits or overwrite unrelated model fields.

This update makes status checks passive, keeps per-conversation ordering while
allowing concurrency, preserves native long-running work with bounded display
capture and backpressure, narrows retries to known pre-dispatch failures, and
aligns remote Ask permissions without reducing Execute. It also keeps
Embedding updates narrow, improves settings and browser guidance, removes the
unused legacy Main gateway state, and records the validated Desktop 0.12.3 and
Agent 0.11.17 release metadata.

Release note: 改善 OpenCode 切换、多会话长任务、模型重试、远程 Ask/Execute 与设置保存可靠性,并完成 Desktop 0.12.3 / Agent 0.11.17 发布准备。
2026-09-04 08:05:29 +08:00

14 KiB
Raw Blame History

GoodBuddy 0.12.3 发布前审查

状态

发布候选验证完成。代码问题和发布前 UI 逻辑问题已完成主线程复核与聚焦 回归;真实模型、当前源码 Agent 的共享 Linux x64 Host E2E、全量测试、 类型检查、Lint 和获批双语发布说明校验均已通过。Desktop/Agent 已分别升至 0.12.3/0.11.17。本次按用户选择不构建 LoongArch 实验预览;剩余操作 仅为审查暂存内容并提交,未经后续明确授权不 push、tag 或发布。

审查范围

  • 原始审查范围:f18e4abb3c0e8c9e7c5c870cb50a67e9ed8880fe 至 4e60a4ca616846fbfc312932456e1a1e7e0a0388。
  • 发布比较基线:Desktop v0.12.2 (c52732441aba599e643211b3d2175c8ff7027e26)。
  • 后续补充:Linux arm64 上本机 OpenCode Runtime 切换性能。
  • 用户已有的 Desktop Pet 设计提交 374968d50b3f1ae30165a5e12adf7f6213b6c00c 保持不变,并纳入最终 发布范围核对。

审查分别覆盖 Desktop/UI、文档与构建、远程执行、模型 Runtime、 稳定性以及性能/KISS。Subagent 服务曾出现无 Token、Provider 暂不可用、 认证失败和本机 EPERM,这些基础设施失败未计为审查完成;六个方向最终 都获得了结果,所有候选又由主线程沿生产调用链复核和去重。

已验证并修复的问题

1. 默认直连模型遗漏连接级请求设置

createAgentRuntime() 的默认直连聊天路径没有把当前模型连接的 requestHeaders、requestBody 和 maximumOutputTokens 交给 ModelAgentRuntime。设置页显示已保存,但普通聊天不会采用这些值。

处理:默认直连路径与独立 profile 路径使用相同字段,并通过真实请求 mock 验证 Header、Body 和输出上限到达 Provider 请求。

2. 切换 Embedding 连接会清空无关模型字段

Main 在切换当前 Embedding 连接时重建 modelProfiles,遗漏 maximumOutputTokens、自定义 Header 和自定义 Body。一次向量连接切换 会静默清除已保存的 LLM 设置。

最终处理:不再从完整设置 snapshot 重建输入。切换操作进入 RuntimeSettingsStore 的串行队列后,只验证并更新 activeEmbeddingConnectionId;失败回滚也只在当前值仍等于该次选择时恢复 这一字段,因此不会丢失模型 profile、API Key keep 语义、Runtime 选择、 环境覆盖或并发保存的其他设置。

3. ACP 入站背压过早恢复

远端 ACP 队列消费一个事件后就恢复入站,即使剩余项目数或字节数仍达到 上限,积压时会反复暂停和恢复并继续放大内存压力。

处理:统一使用 pendingUpdateLimitReached(),只有项目数和字节数都降到 阈值以下时才恢复输入。

4. Continue 截断后可能重复或乱序显示答案

Continue Host 的事件流出现 gap 后仍继续转发后续 delta,最终 fallback 又可能输出完整答案,导致已显示内容重复或乱序。

处理:一旦确认事件流溢出就停止转发后续 delta;最终文本只有在它确实以 已显示文本为前缀时才追加未显示后缀。

5. 直连模型可能重放已到达 Provider 的请求

普通 ECONNRESET、Header/Body timeout 和 GoodBuddy 自身请求 timeout 并不能证明 POST 尚未到达 Provider。自动重试这些错误可能重复生成和计费, 且每次尝试会重新开始请求时限。

处理:自动重试仅保留明确的连接前失败,包括 DNS 临时失败、连接拒绝、 网络/主机不可达、connect timeout,以及明确发生在 TLS 建立前的 reset。 普通 reset 和请求阶段 timeout 不再自动重放。

6. 远端 Ask 不能只依赖 ACP 权限回调

OpenCode 1.18.9 的默认 build Agent 最终权限包含通配 allow。若 Runtime 未产生 requestPermission,仅在 ACP 回调中拒绝写工具不能形成完整的 Ask 只读边界。

处理:Ask 启动同时注入顶层和 build Agent 的 permission: "ask"; Execute 不注入该覆盖。受管模型桥 helper 增加固定 --work-mode 参数, 让直接 Runtime 和模型桥 Runtime 使用同一规则。构建、双语功能清单、 远程 Host 技术设计及源码注释同步改为这条双重边界,不再描述已撤回的 read-only sandbox。

实际使用隔离配置运行 opencode debug config 和 opencode debug agent build --pure 验证过 OpenCode 1.18.9 的配置优先级。 曾评估独立 HOME/XDG 与 --pure,但它们会隐藏用户正常环境或插件,已因 可能影响功能而撤回。

7. Main 中保留了一套没有生产调用者的旧模型网关

RemoteModelGateway、ModelCallOperationStore 及其共享 operation contracts 只被自身测试和旧 Main 网关引用。真实远端调度由 Agent 侧 AgentModelGateway 承担,保留两套实现会让修复落到错误路径。

处理:删除无生产构造者的 Main 旧实现及孤立测试;把仍在使用的 resolved profile digest 移入 managed-model-bridge.ts。摘要继续绑定请求定制, 但明确排除 API Key。

8. Linux arm64 切换 OpenCode 会阻塞在用后即毁的冷启动

Composer 切换先同步等待一次 agent.getStatus()。旧路径会读取已分配 Skills/MCP、创建临时配置和全新 XDG 目录、启动 opencode serve、等待 health、调用 session.list,随后立即杀死进程并递归清理。切换成功后 原生能力清单还会执行另一次独立冷启动,首次真实请求再启动可复用实例。 此前提高 30 秒上限、改用 health 和跳过 --version 都没有消除这次 阻塞冷启动。

处理:本机受管 OpenCode 的被动状态检查只解析设置、模型凭据和已选择 路径,不读取 Skills/MCP,也不启动 Server。设置中的显式连接测试、原生 能力清单和首次真实请求仍执行完整启动、health 与连接检查。状态探针仍 为一次性对象,不把空闲 OpenCode Server 留在执行缓存中。

Windows x64 上仓库现有真实 bundled OpenCode 回环探针显示每次冷启动约 1.8 秒;历史 Linux ARM 回归已证明同一阶段可能超过 10 秒。修复后的被动 状态单测为毫秒级且断言没有 spawn、client 或 health 调用。

9. OpenCode 被动状态可能验证错误的 fallback 路径

被动状态初版先让 validator 搜索可用二进制。若用户明确配置的路径已被 删除,validator 可能改为找到 bundled 或 PATH 中的另一个候选并报告就绪, 但真实启动仍会选择那条失效的配置路径。

处理:先按真实 launch 的优先级只选择目标路径,再对这个精确路径执行 不启动进程的 canonical-file 校验;校验阶段禁止 fallback 到其他候选。

10. 自定义 Header 可能保存 Fetch 无法编码的值

原 schema 会接受中文等 UTF-16 code unit 大于 255 的 Header 值,但 Runtime 构造 Fetch Headers 时会因不符合 ByteString 要求抛错。用户会 看到设置保存成功,实际请求却在发送前失败。

处理:保存和 Runtime 共用的 schema 现在同时拒绝控制字符及 code unit 大于 255 的值,并增加中文 Header 值回归用例。

11. 设置保存进行中仍可编辑并丢失新输入

保存按钮会进入 pending 状态,但 textarea 等设置控件仍可修改。旧保存 响应返回后会用已保存 snapshot 覆盖这段新输入并清除 dirty 状态。

处理:设置主体在保存或连接测试期间统一设为 inert 和 aria-busy,避免产生无法归属到当前请求的新草稿,同时保持现有异步反馈。

12. Embedding-only 更新可能写回环境值或覆盖并发保存

Embedding-only helper 曾从应用环境变量覆盖后的 effective profile 生成 写回输入。在环境临时指定 Anthropic/API Key 时,切换 Embedding 连接可能 把这些有效值持久化进原本的 OpenAI/no-auth 配置。该 helper 还会在设置 串行队列外读取完整 snapshot;若另一项设置保存先入队,旧 snapshot 随后 可能把新保存内容整体覆盖,失败回滚也有同样风险。

处理:删除整份设置重建 helper,改为在 RuntimeSettingsStore 队列内只 验证并更新 activeEmbeddingConnectionId。失败回滚也只恢复这一字段; 若期间用户已选择其他连接则不覆盖。测试覆盖环境覆盖移除、并发完整保存 和窄回滚后其他设置仍保留。

13. 内置浏览器卡片没有说明仅 Execute 可用

“能力与工具”中的浏览器卡片只显示通用的“按模式读写”,但后端只在 Execute 模式提供 browser tools,容易让 Ask 用户误以为当前模式可用。

处理:浏览器卡片增加双语“仅 Execute / Execute only”摘要,其他 MCP 卡片和后端权限逻辑不变。

14. 长任务活动展示没有实际容量边界

候选已取消“超过 100 个工具便终止 Runtime”的功能限制,但同时移除了 消息中工具、子 Agent 和交错 block 数组的所有保留上限。长任务会让 Renderer 每次事件反复复制并线性搜索不断增长的数组,恢复与 SQLite metadata 也会持续变大,和文档所述“只截断本地展示副本”不一致。

处理:Runtime 继续不受工具调用数限制;每条回复的本地副本最多保留 500 条工具/子 Agent 活动和 2,000 个交错展示块。超出后显示持久的双语 提示,已保留活动仍可更新状态,原生任务、模型轮次和工具执行均不取消。 该上限高于 v0.12.2 已发布的 100 条 schema 上限,不破坏已发布数据。

15. 持续 journal 背压会高频轮询 SQLite

远端 Runtime 输出在 journal 暂时满时每 25 毫秒重新执行一次同步 SQLite append。Desktop 断连或消费者较慢数分钟时,每个受阻 binding 都会每秒 触发 40 次无效事务和事件循环唤醒。

处理:第一次仍立即尝试,失败后从 25 毫秒开始指数退避,最高 1 秒; 容量恢复、取消、deadline 和 Runtime 输出暂停语义不变。

候选问题处置

  • Desktop/UI 首轮审查未发现 P0-P3;发布前第二轮按 Runtime 切换、 模型/Embedding 设置和 MCP/工具展示三组检查,共确认并修复上述 5 个 可复现问题。
  • 文档与网站校验未发现发布阻断问题。
  • 不为 Ask 使用独立 HOME/XDG,也不加 --pure,以免禁用正常插件或用户 环境。
  • 不让普通状态或原生清单探针永久进入执行缓存,避免恢复“仅查看状态便 留下多个空闲 OpenCode 服务”的已修复问题。
  • 未发现 Main 旧模型网关的生产、文档或导出调用者;Agent 侧真实网关及 共享 wire contracts 保留。
  • 删除 canonicalModelRequestHeaders();其唯一调用结果会立即进入已有 canonicalJson(),后者已按稳定 code-unit 顺序递归排序对象键。直接传入 Headers 对象转换结果可保持摘要不变并移除 locale 排序重复。
  • OpenCode/ACP 在单次活动 Prompt 内保留增量工具身份,以正确归并后续状态 并在终态识别未完成/失败工具;这些 map 随 Prompt 结束释放。本轮只限制 Renderer/Main 的持久展示副本,不为 Runtime 增加会中止功能的工具配额。
  • 未对没有具体失败路径的全局配额、持久恢复或额外安全层建议增加框架。

当前验证证据

  • npm run typecheck:通过。
  • npm run lint:通过。
  • npm test:323 个测试文件通过,8 个文件按环境跳过;3,471 项通过, 55 项跳过。
  • 发布修复聚焦矩阵:13 个测试文件,427 项通过,6 项按环境跳过。
  • OpenCode/SelectedRuntime 聚焦矩阵:79 项通过。
  • UI 逻辑矩阵:7 个测试文件,319 项通过;其中相关修复矩阵 4 个文件 224 项通过,OpenCode 单文件 62 项通过。
  • 最终审查修复矩阵:8 个测试文件、389 项通过;最后一次受影响子集 4 个文件、286 项通过。受影响文件 ESLint 和 git diff --check 通过。
  • 真实 bundled OpenCode 回环 Provider 探针:2 次本机模型路由均通过, 每次约 1.8 秒冷启动。
  • 2026-09-03 共享 Linux x64 Host 使用当前源码 profile 和已登记真实 OpenCode 1.18.9 运行 debug agent build --pure:Ask 的最后一条 */* wildcard 为 ask,Execute 为 allow;Ask 保留正常 HOME/XDG, Execute 不注入配置覆盖,直接 Runtime 与 model bridge 规则一致。共 0 次模型调用,唯一 /tmp/goodbuddy-e2e-release-profile-* 目录已清理, 共享安装未修改。
  • 直连 Runtime 自定义 Header+Body 真实最小请求:1 次调用通过;DeepSeek Harness 自定义 Header 真实最小请求:1 次调用通过。两者均由本机 loopback 先观察最终请求再转发,未启用工具或附件,也未输出凭据。
  • 使用当前源码构建的测试签名 Agent 0.11.17 Linux x64 bundle,在共享 Host 的唯一 /tmp/goodbuddy-e2e-release-* HOME 中完成真实 attach、 workspace validation 和 OpenCode 1.18.9 ACP Runtime 验证。两个并发 Ask 分别通过原生读取工具返回各自证据文件,Execute 通过原生 Bash 创建并读回 execute-output.txt;3 个 Runtime Prompt 共发生 6 次真实 Provider 请求,每次均观察到连接级自定义 Header+Body,6 个请求 Body digest 全部不同,未发生重放。
  • 同一 Host E2E 扫描隔离 Agent state 的 7 个文件、170,534 字节,未发现 API Key、自定义 Header 值或自定义 Body 值。测试 Agent、Provider 代理 均已精确停止;独立清理探针确认剩余测试目录和进程均为 0,共享安装未修改。 该 bundle 只使用临时测试签名,不是 production 构建或发布产物。
  • Desktop 0.12.3、Agent 0.11.17 的版本一致性脚本通过, git diff --check 通过。
  • 用户已批准 0.12.3 精确中英文发布说明并选择不构建 LoongArch 实验预览; npm run release:notes:verify 通过。发布说明源码测试不再把某一历史候选 恰好超过 500 字符的内容强加给每个后续版本,而是用独立 schema 样例持续 验证长场景条目不受任意字符上限限制。
  • 当前没有可用 Linux arm64 真机,因此本轮未取得 arm64 实机切换耗时; 该限制不冒充 x64 结果。修复已由不 spawn 的被动状态单测、Windows x64 回环时序和共享 Linux x64 当前源码 Runtime 路径共同覆盖。

提交与发布边界

  1. 提交前审查暂存内容,排除本机 .diff*.txt、临时 bundle/probe、凭据、 日志和其他用户私有产物。
  2. 提交后报告精确 commit。只有取得用户后续明确授权,才可 fetch/push 两个 main、等待 main CI 并创建 agent-v0.11.17 或 v0.12.3 标签。