本版的主线是远程工作区第一次真正可用:此前这条线没有发布过远端能装的内核,所以「在另一台机器上开工作区」在任何配置下都装不上东西,而失败会报成一个与真因无关的错误码。同期把项目自身状态移出跨项目共享的缓存前缀(首轮缓存命中率实测 4.2% → 54.7%),并把 Controller 按生命周期拆成协作者。自 2.11.0 起 56 个提交。
升级路径:无需人工步骤。三平台照旧自更新——Linux 走 .deb,Windows 运行下一个安装器,macOS 替换自身 bundle。
新增
远程工作区:这条线现在会发布远端要装的内核
- 发布 CLI 归档。此前 studio 线只发桌面包,而远端安装的三条路里有两条要按版本名去取一个 release。它们取的地址不存在(tag 是
studio-v2.11.0,取的却是/download/v2.11.0/),于是双双在发网络请求之前就被拒,只剩 npm 一条——而 npm 上那个包属于 1.x 线,装下来低于开工作区所需的内核版本。现在每次发布同时产出六个平台的reasonix-<os>-<arch>归档与SHA256SUMS,releaseasset.Line表达 tag 命名空间(它是 tag 前缀,不是update.Line那种安装布局——一个类型同时回答两件事,就会让 tag 前缀去决定.deb装到哪)。 - 内核声明自己的版本。打包脚本此前不注入版本,于是发布出去的内核自称
dev,而按版本取 release 的两条路都拒绝一个非发布版本。顺带修好了一个从未生效的门:ParseVersion读不了studio-v2.11.0(它在第一个连字符处断开),版本因此为空,而「没说版本」按设计是放行的——所以内核版本下限对整条 studio 线从来没有真正生效过。注入的是v2.12.0这种 semver 而非完整 tag,门才成立。 - 取过一次的发布不再取第二次。为一台够不到 release 的机器安装时,归档由本机取下、经 SFTP 推过去;每多一台同平台的机器就重付一次同样的下载。现在验证过的字节留在
<cache>/cli/<tag>/<goos>-<goarch>,第二台起不再下载,而安装包体积不变——把 CLI 塞进 Studio 是相反的取舍:给每次安装加约 30MB,只为省一次下载,且只对同平台的远端有效。缓存旁存摘要,那是本机磁盘的完整性检查而不是边界(能写二进制的也能写摘要),不匹配即当作未命中。 - 内核可以托管 Studio 页面。新界面此前只有一条到达路径——经桌面外壳,隔着一道绑死
127.0.0.1且不可放宽的 loopback 门。其它任何打开内核的方式拿到的都是内核内嵌的旧页面。现在页面挂载是 hub 的能力(--page,落在/_studio/),两个宿主共用一份。它位于认证门之内:外壳把页面包在门外是安全的(loopback 门先答),但一个绑到网络地址的 serve 没有那道门,同样的写法等于把页面交给任何能连到端口的人。 - 首次连接不再是一项配置任务,以及一条临时
-R转发在重连后仍留在原端口上。
其它
- 上下文窗口在「注意到它缺失的地方」直接声明:中继服务以自己的名义转发别人的模型,没有任何探测或目录能说出它的上限,此前只能把人指到连接页。
- 折叠阈值成为设置项,并说明是哪一条边界触发的。
- 工具调用会报告它花在命令之外的时间;宿主记录一个会话计费多少、冷启动花了多少。
- 请求会说明它携带的正文是否与上次发出的相同。
修复
远端安装:探测说能用、启动必崩
一个内核能否被驱动,此前只靠在 serve --help 里找一个 port-file 来判断。但启动传的不止这一个——broker 那一对晚了一整条发布线才出现。于是一个只有前半组标志的旧内核通过了探测,带着 --provider-broker 被启动,在一个它从未定义过的标志上退出。到达使用者的是 20 秒后的「serve 从未报告端口」:一个超时、一台机器、一句「去看日志」——而那份日志已被失败清理删掉。真因(那台机器上的内核来自另一条线)没有出现在任何一处。
现在探测问的就是启动会传的那一组,并有测试断言两者相等。同一形状还有两处:失败清理与停止各自抄了一份「一次 serve 的记录由哪些文件组成」,broker 令牌加进来时两边都漏了,于是每次失败的启动都把本机的凭据留在远端;remote test 与 remote connect 接的是不同的参数,前者因此把一条 connect 会走的路报成关闭、把一条注定被拒的路报成开着。
失败归因也补上了缺的那个身份:ErrNoReleaseForBuild 说「这个构建没有可安装的发布」,排在各条路自己的错误码之前——被指去装 curl 的人装完了仍然没有路。npm 那条路装完后版本不够时,现在报的是版本不够,而不是「装到 PATH 之外」。没有摘掉 npm 这条路:摘它需要硬编码「npm 上是 1.x」这个会过期的判断。
另外,一个由程序启动、凭据来自 broker 的内核不再在启动时查询余额——它按设计没有自己的凭据,那次查询必然失败,于是每份远端日志的开头都常驻一条与故障无关的错误,而那份日志只在排查故障时才会被打开。
编码与路径
- 命令输出按它被写出时的代码页读取。
- 大于预读窗口的 CJK 文件不再被判成 GB18030。
- 以盘符根开头的 glob 走那个盘,而不是当前目录;在盘符根打开的项目不再显示为
\。
其它
- MCP:会话的 MCP 生命期不再等同于构建它的那次请求;连接一个待定服务器是一个决定并被记录;仓库声明的服务器会等待回答;读取 Claude Code 实际写入的 scope。
- capability:目录是索引而不是每一条描述;批处理分发器在模型读得到的地方被命名;工具自己的名字解析到它自己。
- agent:从未运行的调用不再判定一次修改失败;在参数中途被截断的调用即使终端看起来完整也会被丢弃;子代理的工具 id 只做一次命名空间;依赖屏障不再丢掉委派。
- 被拒绝的完成会说明是哪一条主张没有成立、缺了什么。
- 判断哪些文件是测试不再靠读工作区。
- 界面要过而目录里没有的八个 i18n 键。
- 最新的指令可以取代它之前的副本。
- 长文件名不再覆盖它自己的 +N -N;前缀缓存说明文字不再成为那一行的溃缩区。
内部
- Controller 按生命周期拆成协作者:会话的生命周期与持久化、审批编排及其守卫、MCP 服务器编排、把输入变成一个回合与运行一个回合、取消与善后,各自成文件;一次回合的投影从八处前置改为一处。
- 项目自身的规则、记忆索引、技能目录与版本控制状态移出每个项目共享的缓存前缀,改由回合承担。
- 安装脚本现在会被真正执行而不只是被写出来:下载、校验摘要、解包、定位、读回版本,全程真跑一次 shell。
- macOS 按架构分别构建而不再产出单一 universal bundle;产物按其中的 bundle 命名;不再打包 bundle 里没有任何东西会读取的文件。
macOS 已用 Developer ID 签名并公证,打开时不需要清除隔离属性。Windows 的窗口、内核与安装器都经 SignPath 做了 Authenticode 签名。Linux 从 .deb 安装。
三个平台都能自更新:Linux 走自己的包,Windows 运行下一个安装器,macOS 替换自身 bundle。