Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`、别的应用的数据目录同理。把这样一条伪造记录真写进生产用的那份记录文件、再走生产的启动计算,围栏的定义里确实出现了那个目录。现在:围栏用来算可写范围的项目清单改存在一个**只有应用主进程写得了、沙箱里的一切都写不到**的位置;界面里打开 / 关闭项目仍然照常更新它(只经主进程),而原来那份界面记录只留给界面自己用,**不再喂给围栏**。升级后第一次启动会把现有记录原样搬过去一次(那一刻的可写范围与今天完全相同,不引入新东西),日志里看得见搬了几条;之后即使有人往旧记录里塞东西,启动时会被记成「记录里有、清单里没有」并在整个会话里隔离 —— 哪怕界面把它原样写回,也进不了清单。新清单缺失或损坏时,围栏退到只有默认对话目录可写,并在日志里说明原因:拿不到清单不等于「什么都可写」。诚实的边界:①**这道围栏只管写不管读**,围栏内的代码本来就读得到你的文件,这里堵的是「写」;②首次搬迁**信任当时记录里已有的内容**——如果在升级之前就已经被塞了伪造条目,它会被一起搬过去(与今天的可写范围一致,没有更坏);③用户真正打开过的项目可写这条语义一字没改,只是换了清单从哪儿来;④被隔离的目录若恰好是你后来真想打开的,要先关掉那个(伪造出来的)标签页并重启,再打开一次;⑤沙箱里的代码仍然能往界面那份旧记录里塞条目,你会在界面上看到一个自己没开过的标签页 —— 它写不进去,但本版没有把这条通路本身堵上,也没有在界面上标出它是可疑的。
Expand Down
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion packages/ui-mac/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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。"
}
Loading