OpenCode 2.2.6:run 等后台任务,CI 会挂死
OpenCode v2.2.6 于 2026-11-06 发布:run 会等自己起的后台命令、子代理与图片任务跑完才退出,官方明说没有时间上限,CI 必须自带 timeout;MCP 支持连接池,本地 API 收紧。
2026-11-08 · OpenCode 官方 Release Notes(GitHub Releases API 逐 tag 一手核对,2026-11-08)
发布 2026-11-08核实 2026-11-08要点
v2.2.6(tag 时间戳2026-11-06T23:37:20Z,prerelease: false)已转正,接替 10-30 的v2.2.5;1.x 线仍停在v1.19.5。-
opencode run改为等待后台任务:它自己起的后台 shell 命令、子代理、图片任务全部跑完,才让模型对结果作答再退出。等待时打印waiting for N background job(s)(--format json下是background_wait事件)。 - 官方原文明确「没有时间上限」:永不退出的任务(dev server、文件 watcher)会让 run 一直挂着,CI 必须自己设 timeout。
- 🆕
opencode run --worktree(-w):在新的 git worktree 里起会话,--worktree=<name>可命名;不能与--continue/--session同用。 - 🆕
opencode run --archived:会话创建即归档,不进 inbox、首页与会话列表,--continue也会跳过。 - 🆕 MCP 连接池:
pool: true让同配置的 MCP server 在多个项目间共享一条连接,进程更少、启动更快。 - 本地 API 安全收紧:文件读取与媒体改走沙箱化下载;
/api/*拒绝跨站浏览器导航与非许可来源请求;webfetch 与 Code Mode 的fetch拒绝带凭据的 URL,权限匹配时忽略#fragment,且 Code Mode 的fetch会逐个校验重定向。 - 🆕 SIGTERM 语义明确:取消会话与其后台任务,退出码
143。 - 其余:
session_move离开当前项目前要求external_directory权限;按 provider / model 配置会话预热(interval: "auto"按缓存 TTL);新增websearch.use策略动作与OPENCODE_FLAGS=all;实验性 Code Mode 字节码 VM(默认关)。
这条「修复」会直接打断 CI
官方把它放在 Bug fixes 里,原文是:opencode run 现在会等待它启动的后台 shell 命令、子代理与图片任务,等它们返回结果让模型看完,然后才退出。等待期间打印 waiting for N background job(s)。
紧跟着一句更关键:"There is no time limit"。一个永不退出的任务——典型就是 npm run dev 或文件 watcher——会让 run 一直开着,直到你按 Ctrl+C 或发 SIGTERM。官方给的处置建议是「CI 任务自己设 timeout」。
这条改动的合理性很清楚:以前 run 可能在后代任务还没跑完时就退出,把「做了一半」当结果交出去,那是更糟的假成功。但代价也很清楚:任何在后台上起过常驻进程的脚本,升级后都会从「提前退出」变成「永不退出」。
| 场景 | 2.2.5 及之前 | 2.2.6 |
|---|---|---|
| 纯问答型 run | 直接退出 | 直接退出 |
run 里起过 npm run dev | 可能提前退出(假成功) | 一直等,永不退出 |
| CI 里 run 超时 | 看进程策略 | 必须显式 timeout,否则占满 runner |
| 中断方式 | 进程被杀 | SIGTERM → 取消会话与后台任务,退出码 143 |
给 CI 的最小修法:timeout 900 opencode run ... 之类的外层兜底,并把 143 当「被取消」而不是「失败」处理。
三处安全收紧值得单独读
① /api/* 不再接受跨站来源
文件读取与媒体改走沙箱化下载,并拒绝跨站浏览器导航与非许可来源对 /api/* 的请求。官方给的威胁模型很直白:「项目里的一个文件不能对 server 的 origin 执行脚本」。也就是说,opencode serve 起在本机之后,别的网页不能借浏览器把本地文件读走。
这条对把 opencode serve 暴露在内网或容器的团队尤其重要——此前「本地 HTTP 服务」默认被当作安全边界内。
② webfetch 拒绝带凭据的 URL,权限匹配忽略 #fragment
https://user:pass@host/... 这类带凭据的 URL 现在直接拒;权限规则匹配时把 #fragment 忽略掉。第二条是绕过修补:以前 https://allowed.com/page#@evil.com 这类构造可能让规则判断与实际请求的目标不一致。
③ Code Mode 的 fetch 逐个校验重定向
这是最容易被漏掉的一条:只校验第一个 URL 的权限系统,会被一次重定向绕过。现在每一个跳转都要过 webfetch 规则。
其余值得注意的变化
run --worktree:脚本化任务默认落在新 worktree 里,不污染当前工作区;与--continue/--session互斥(因为那两个都指向已有会话)。run --archived:CI 与脚本的运行不再刷满 inbox——这是「Agent 用量上来之后会话列表变成垃圾场」的正面解法。- MCP 连接池(
pool: true):同配置 server 跨项目共享一条连接。代价是隔离性:多个项目共用一个 MCP 进程时,它的工作目录、环境变量、凭据作用域到底算谁的,官方本条没展开。 - MCP 连接等待 2 秒:模型调用前会等仍在连接的 server 最多 2 秒,让「启动快但没连上」的 server 也能赶上第一次提问。以前这种情况是工具直接缺席。
- 实验性 Code Mode 字节码 VM:默认关,要显式
experimental.codemode_vm: true或OPENCODE_FLAGS=codemode_vm。 - npm 升级安装脚本恢复执行:此前被禁的
npm upgrade安装脚本重新放行——这会重新引入「升级时装包脚本执行」的风险面,容器化环境之外的团队要自己权衡。
AI 之家 观点
一、「等后台任务跑完」是正确方向的正确实现,但它把复杂度转嫁给调用方了。 假成功是 Agent 最坏的失败模式:看起来完成了,实际只做了一半。修掉它是必须的。但官方选择不设默认上限,等于要求每个脚本化调用自己兜底——这在一个 90% 的用户是「复制文档里的命令」的产品里,不是个安全默认。合理做法应该是「默认有上限 + 可关」,而不是「默认无上限 + 记得设」。
二、这次最该被记住的不是新功能,是三条安全修补背后的同一个模式。 拒绝带凭据的 URL、匹配时忽略 #fragment、逐个校验重定向——三条都在修「规则看的东西和实际执行的东西不是同一个」。这跟 11-07 那轮 Claude Code 的四处权限绕过是同一类问题:Agent 的权限系统普遍建立在「文本匹配」上,而文本可以构造。 凡是「allow 一条 URL / 一条命令」的设计,都该按这个思路自查一遍。
三、run --archived 这个小开关透露了真实用法。 官方专门为「脚本与 CI 运行」加了一个「别进我的收件箱」的选项,说明相当多的人在用 opencode run 跑自动化,而且会话列表已经被这些运行刷爆了。这类「为高频用法打补丁」的功能,比任何 roadmap 都更能说明产品实际被怎么用。
四、给使用者的四条动作:① CI 里的 opencode run 全部补外层 timeout;② 把退出码 143 当「取消」而非「失败」,否则监控会误报;③ 容器 / 内网跑 opencode serve 的,升级后复测一遍 /api/* 的访问路径(跨来源请求被拒是行为变更);④ 用 MCP 的评估 pool: true 是否适合——省进程的代价可能是隔离边界变模糊。
待核实
pool: true下 MCP 进程的工作目录、环境变量与凭据作用域——官方只写「同配置共享一条连接」。waiting for N background job(s)与background_wait事件的具体字段,以及能否通过配置限制等待。--archived会话是否有自动清理策略,以及归档后能否重新进入。- npm 安装脚本恢复执行的安全边界(是否只在本地升级生效)。
- 会话预热
interval: "auto"的实际触发规则——按缓存 TTL 推算,但 TTL 来源未说明。
相关阅读
来源
常见问题
为什么升级后 CI 里的 opencode run 不结束了?
这是 2.2.6 的行为变更:opencode run 现在会等它自己起的后台 shell 命令、子代理与图片任务全部结束,再让模型对结果作答后退出。官方明说没有时间上限——一个永不退出的任务(dev server、文件 watcher)会让 run 一直挂着,必须靠 Ctrl+C、SIGTERM 或 CI 侧 timeout 收尾。
怎么让脚本化运行不污染会话列表?
用 opencode run --archived,会话创建时就是归档状态,不会出现在 inbox、首页与 TUI 会话列表里,也会被 --continue 跳过。
SIGTERM 之后是什么行为?
官方写 SIGTERM 会取消会话及其后台任务并以退出码 143 结束。也就是说超时杀进程现在是「取消一次会话」的语义,而不是「进程硬死」。