diff --git a/CHANGELOG.md b/CHANGELOG.md index f00fb8e2c976..be0a8bccef24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,17 +6,13 @@ > 写入时机:用户可见实现 PR 写入 `[Unreleased]`;实际发布时移动到日期和 > 版本标题。具体流程见 [Alpha documentation SOP](https://github.com/jinjunnn/alpha-work/blob/main/governance/documentation-sop.md)。 -## [Unreleased] +## [0.1.16] - 2026-09-22 ### Changed - **自己配的远程 MCP 连接器又能连上了**(#1381)——此前:在「接入其它服务」里填一个远程 MCP 服务器的地址(类型选「远程 URL」),保存成功、界面上也有这一条,但它**永远连不上**:0.1.13 装进应用的本机出网围栏按名单放行,而这条地址写在一个 AI 有权改写的配置文件里,围栏不敢照它放行,于是连接一律被我们自己拒掉(真机日志:`mcp.deepwiki.com:443 … verdict:"deny"`),看起来像对方的服务坏了。现在:远程连接器的地址与自定义模型节点一样,存进应用状态目录里一个**只有应用主进程写得了**的文件(`mcp-servers/<环境>.json`,与 `custom-providers/` 并排);每次引擎启动前,放行名单与引擎拿到的连接器清单都从这个文件算出来,配置文件里写的远程连接器(不管是谁写的)从此**既不被放行,也不会被引擎当成已启用的连接器**(引擎拿到的是「已停用」)。添加时的地址检查与出网围栏用的是同一个判断:指向本机的地址(localhost / 127.0.0.1)或非 https 地址在保存那一刻就被拒并用人话说明原因,不再出现「加得进、连不上」。在连接器上点「停用 / 启用」只改账本,不再往配置文件里写。诚实的边界:①**本地命令型 MCP 连接器不受影响**,它们仍存在配置文件里,照旧生效(它们不需要出网授权);②**升级前用旧方式加过的远程连接器会被忽略**(启动日志里写明忽略了哪一条、为什么),要用的话在「接入其它服务」里重新添加一次即可 —— 本机没有这样的连接器;③远程连接器的自定义请求头照旧随记录一起保存,但这一版的添加界面本来就不提供填请求头的入口;④真机上「连接器真的连上了」那一条 `verdict:"allow"` 的取证归发版前的 RC 检查单,本版的判据是真引擎级别的(引擎拿到的配置 + 放行名单)。 - **自己填地址接入其它服务(OpenAI、Gemini 等)又能用了,而且加上就能发消息**(#1392)——此前:模型选择器里「自己填一个服务地址加节点」那半在上一版被关掉了(#1397),因为这么加出来的节点记录存在一个 AI 有权改写的配置文件里,出网围栏不敢照它放行,加了也发不出消息。现在:入口回来了(底部「+ 添加供应商 / 接入其它服务」,列表最后一行「接入其它服务」),填地址、选 OpenAI / Anthropic 兼容格式、填 Key、写上模型 ID,保存后这个节点出现在模型清单里,它的地址进了本机出网围栏的放行名单,消息真的发得出去。它的记录从此存在应用自己的状态目录里一个**只有应用主进程写得了**的文件中 —— AI 和它启动的一切改不到;而配置文件里写的服务节点(不管是谁写的)从此**既不出现在模型清单,也不会被放行**。诚实的边界:①**指向本机的地址仍不支持**(localhost / 127.0.0.1 一类,本机模型这条线不做),而且只支持 https 地址:填了会在保存那一刻被拒并用人话说明原因,不再出现「加得进、发不出」;②密钥不进那份记录,仍存在系统钥匙串里(与之前一样);③**升级前用旧方式加过的自定义节点会被忽略**(启动日志里写明忽略了哪些、为什么),要用的话在模型选择器里重新添加一次即可 —— 本机没有这样的节点;④远程 MCP 连接器的地址仍是另一件事,不在这条线上。 - **一轮还在跑的时候,时间线里一直看得出来**(#1399)——此前一轮对话在跑的时候,时间线里常常**什么都不动**:只有刚发出、模型还没开口那一刻有三个跳动的点,模型一吐出第一个字它就没了;模型在「想」还没吐字、或者工具卡还没建起来的那几十秒,页面是静止的;轮到你去点批准的时候更彻底,时间线里一个字都不提,只有输入框的提示文案变了。现在活跃回合的**最后一行**始终是一条「回合脚行」:一个跟顶栏同款的脉冲点、一句「正在生成」、一个从你发出这条消息算起的计时(分:秒)。模型在想、在写、在跑工具、自动重试,这一行都不变 —— 细节由上面的思考块和工具卡说,它只负责说「这一轮还在跑」。只有一种情况它换一副面孔:轮到你了 —— 等你批准时它变成琥珀底的小卡「等待你的决定 · 生成已暂停,到审批窗口里选择」,模型反问你时是「等你回答 · 生成已暂停,到输入框上方的提问卡回答」;脉冲停、计时收起,没有按钮(动作仍在审批弹窗和提问卡里,审批也没有搬进时间线)。回合一结束,这一行同帧让位给结局:完成 → 回合脚注,停止 → 中断行,出错 → 错误卡,零正文 → 空回合行,不留残影。屏幕阅读器听得到状态变化:文字部分是礼貌播报的状态区,整轮至多三次(出现 / 转成等你 / 回到运行),计时每秒跳动不播报;系统开了「减弱动效」时脉冲点静止、计时照走。此前那个「正在思考…」胶囊并入了这一行,不再单独出现。诚实的边界:①**审批与提问同时挂起时按审批说**——审批弹窗是强模态、盖在提问卡之上,你此刻能动手的只有它;②**顶栏那个状态胶囊没动**:回合在等你时它仍显示「正在生成…」,与脚行的「等待你的决定」不一致,是否让胶囊也分两面归后续;③计时不是进度,不承诺还要多久;它从消息发出算起,所以中途重开会话看到的是真实已过时长,不是从零起;④「回合结束时仍在运行态的工具卡同帧收口」这一条靠引擎在回合结束前把工具状态收口,本版没有在时间线侧另加一道收口,也没有在真机上实测这一格;⑤本版没有截图取证(打包验证归发版门),样式按已批设计稿的类名与令牌落地,由样式棘轮测试把关。 -- **模型选择器里「自己填一个服务地址加节点」那半关掉了;给自带供应商填 Key 那半一个操作都没变**(#1397)——底部那个「+ 添加自定义节点 / 供应商」后面其实是两件事。一件是**给应用自带的那几家(DeepSeek、智谱 GLM、通义千问、Kimi)填你自己的 Key** —— 这件照旧,位置、步骤、已经填过的 Key 全都没动。另一件是**自己填一个服务地址、加一个节点** —— 这件**加完一条消息也发不出去**:我们不敢照那个地址放行出网,因为它存在一个 AI 有权限改写的文件里,照它放行等于让 AI 给自己开一条出网通道(0.1.15 的 #1379 里已经写明这条边界)。留着一个加了就用不了的入口,比没有这个入口更糟,所以这一版把它关掉,并**在它原来的位置**用一句话说清:现在不支持自己填服务地址、为什么(还没有一个 AI 改不到的地方存它)、以后会支持。入口与标题的说法也跟着收窄 —— 它现在只剩「给自带的供应商填 Key」这一件事。诚实的边界:①**什么都没有被删**:你以前加过的自定义节点、存在钥匙串里的密钥,原样留着,这一版只是不再提供「新增」;②**以后会支持自己填地址**,前提是先给那个地址找一个 AI 改不到的存放处(另票),届时这个入口会回来;③如果你的列表里有一个老的自定义节点显示「密钥需重填:重新添加同名服务即可覆盖」,那句提示现在**做不到**了 —— 重填的办法正是被关掉的这条路;本机没有这样的节点,所以这一版没有动那句提示;④远程 MCP 连接器的地址是配置文件里的事,不在这条线上。 - -### Security - - **沙箱不再能被它自己罩着的代码撑开到应用自己的数据目录**(#1390)——我们对用户说的是:AI 和它启动的一切只能写你打开的那个项目。实际上围栏在每次启动时按「你最近打开过哪些项目」决定哪些目录可写,而这份「最近打开」的记录**正好存在围栏允许 AI 写的地方**。于是被打开的仓库里一个插件、或 AI 自己跑的一条命令,往那份记录里塞一条「最近打开的项目是应用数据目录」,下次启动应用,应用自己的状态目录(模型配置、密钥引用、已安装连接器的账本)就在可写范围里了 —— 用户从没打开过它。把一条伪造记录喂给生产里那个真函数,它确实把那个目录选进了可写集。现在:来自这份记录的候选目录,凡是与应用自己的状态目录**有关**的 —— 就是它、在它里面、或者把它包在里面(比如整个 `~/Library/Application Support`)—— 一律不进可写集,并在启动日志里写明为什么被排除;比较在把路径归一化(解掉软链、还原真实大小写)之后做,所以写成大写小写变形或用软链指过去也一样被拒。诚实的边界:①**这一步只护住应用自己的状态目录**,主目录下别的地方 —— `~/Library/LaunchAgents`(写进去等于开机自启)、`~/.ssh`、别的应用的数据目录 —— 仍然能被同一手法撑开,真正的修法是把「最近打开」的记录搬到只有应用主进程写得了的位置,那是第二步,见下一条(#1394);②**这道围栏只管写不管读**:围栏内的代码本来就读得到你的文件,这条洞加上去的是「写」的能力,堵的也只是「写」;③用户真正打开过的项目可写这条语义一个字没改,这里只收窄、不放宽。 - **沙箱的可写范围不再由沙箱里写得到的文件决定**(#1394)——上一条堵住了应用自己的状态目录,但根子还在:围栏在每次启动时按「你最近打开过哪些项目」决定哪些目录可写,而这份「最近打开」的记录**存在围栏允许 AI 写的地方**。于是被打开仓库里的一个插件、或 AI 自己跑的一条命令,往那份记录里塞一条「最近打开的项目是 `~/Library/LaunchAgents`」,下次启动那个目录就可写 —— 往里放一个文件就是开机自启;`~/.ssh`、别的应用的数据目录同理。把这样一条伪造记录真写进生产用的那份记录文件、再走生产的启动计算,围栏的定义里确实出现了那个目录。现在:围栏用来算可写范围的项目清单改存在一个**只有应用主进程写得了、沙箱里的一切都写不到**的位置;界面里打开 / 关闭项目仍然照常更新它(只经主进程),而原来那份界面记录只留给界面自己用,**不再喂给围栏**。升级后第一次启动会把现有记录原样搬过去一次(那一刻的可写范围与今天完全相同,不引入新东西),日志里看得见搬了几条;之后即使有人往旧记录里塞东西,启动时会被记成「记录里有、清单里没有」并在整个会话里隔离 —— 哪怕界面把它原样写回,也进不了清单。新清单缺失或损坏时,围栏退到只有默认对话目录可写,并在日志里说明原因:拿不到清单不等于「什么都可写」。诚实的边界:①**这道围栏只管写不管读**,围栏内的代码本来就读得到你的文件,这里堵的是「写」;②首次搬迁**信任当时记录里已有的内容**——如果在升级之前就已经被塞了伪造条目,它会被一起搬过去(与今天的可写范围一致,没有更坏);③用户真正打开过的项目可写这条语义一字没改,只是换了清单从哪儿来;④被隔离的目录若恰好是你后来真想打开的,要先关掉那个(伪造出来的)标签页并重启,再打开一次;⑤沙箱里的代码仍然能往界面那份旧记录里塞条目,你会在界面上看到一个自己没开过的标签页 —— 它写不进去,但本版没有把这条通路本身堵上,也没有在界面上标出它是可疑的。 diff --git a/bun.lock b/bun.lock index 6659d776248b..880b68b00237 100644 --- a/bun.lock +++ b/bun.lock @@ -1038,7 +1038,7 @@ }, "packages/ui-mac": { "name": "ui-mac", - "version": "0.1.15", + "version": "0.1.16", "dependencies": { "@alpha-code/contracts-consumer": "workspace:*", "@file-viewer/pptx": "3.0.0", diff --git a/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-640/ledger-page.json b/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-640/ledger-page.json index 8d6b2a913200..e10c99fc69fa 100644 --- a/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-640/ledger-page.json +++ b/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-640/ledger-page.json @@ -2,7 +2,7 @@ "kind": "schema", "contract": "LedgerPageV1", "consumer": "alpha-code", - "consumer_version": "0.1.15", + "consumer_version": "0.1.16", "expect": "valid", "value": { "schema_version": 1, diff --git a/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-681/model-catalog-v2.json b/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-681/model-catalog-v2.json index 4040f07e30fe..dfec0a2e03ce 100644 --- a/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-681/model-catalog-v2.json +++ b/packages/alpha-contracts-consumer/fixtures/consumers/alpha-code-681/model-catalog-v2.json @@ -2,7 +2,7 @@ "kind": "schema", "contract": "ModelCatalogV2", "consumer": "alpha-code", - "consumer_version": "0.1.15", + "consumer_version": "0.1.16", "expect": "valid", "value": { "schema_version": 2, diff --git a/packages/ui-mac/package.json b/packages/ui-mac/package.json index 8bedf5a0927a..9a7c9e6014af 100644 --- a/packages/ui-mac/package.json +++ b/packages/ui-mac/package.json @@ -1,7 +1,7 @@ { "name": "ui-mac", "private": true, - "version": "0.1.15", + "version": "0.1.16", "type": "module", "description": "Code Puppy self-owned Mac Electron shell. A re-themed clone of opencode's packages/desktop (the proven thin shell around the shared @opencode-ai/app renderer). Custom theme + incremental screen customization live on top; opencode source stays untouched.", "license": "MIT", diff --git a/packages/ui-mac/src/renderer/extensions/alpha-catalog.snapshot.json b/packages/ui-mac/src/renderer/extensions/alpha-catalog.snapshot.json index e674e10414ab..3e5501d21605 100644 --- a/packages/ui-mac/src/renderer/extensions/alpha-catalog.snapshot.json +++ b/packages/ui-mac/src/renderer/extensions/alpha-catalog.snapshot.json @@ -4,6 +4,6 @@ "entries": 28, "sha256": "400e27a11d06e21ac1719e48fc1a8919955148b2f4117cd1ecabec20d7cb2562", "source": "https://codepuppy.cn/catalog/v1/catalog.json", - "fetchedAt": "2026-09-21T07:10:43.188Z", + "fetchedAt": "2026-09-23T01:22:14.422Z", "_note": "由 scripts/sync-catalog-snapshot.mjs 生成;alpha-catalog.json 禁手编(REQ-046),上架/撤架唯一动作在 alpha-web catalog-src。" }