pi-web-ui 0.81.0 → 0.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +146 -2
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/server/agent-service.js +437 -24
- package/dist/server/attachments.js +36 -0
- package/dist/server/dsh/dsh-agent-service.js +10 -3
- package/dist/server/index.js +5 -0
- package/dist/server/mcp-bridge.js +87 -16
- package/dist/server/subagent-templates.js +30 -2
- package/dist/server/subagents.js +25 -15
- package/dist/server/terminals.js +2 -2
- package/dist/server/tool-manager.js +7 -1
- package/dist/server/webui-context.js +28 -4
- package/package.json +1 -1
- package/themes/cyberpunk.css +1 -0
- package/themes/dazzle.css +1 -0
- package/themes/md-preview.css +1 -0
- package/themes/mist.css +1 -0
- package/themes/paper.css +1 -0
- package/themes/sakura.css +1 -0
- package/themes/white.css +1 -0
- package/web/dist/assets/{TerminalPanel-GQZf9XvH.js → TerminalPanel-B7W10RgC.js} +1 -1
- package/web/dist/assets/index-B1_0cm7B.js +348 -0
- package/web/dist/assets/index-D6KtyrNW.css +10 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-72bjWuR-.css +0 -10
- package/web/dist/assets/index-CiRSpf3e.js +0 -347
package/CHANGELOG.md
CHANGED
|
@@ -10,7 +10,149 @@
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## [0.84.0] — 2026-09-13
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Git 视图的左栏可以拖宽了**(issue #139)—— 改动文件 / 提交历史那一栏原来固定 300px,路径或分支名一长就被截断且无处可展。现在拖它右边的分隔条即可调宽(**双击复位**),宽度**跨会话记住**(`localStorage`);拖动时按容器宽度收敛,保证 diff 区不会被挤没(额外还有一条 `max-width` 兜底)。
|
|
18
|
+
- 与左右主面板同一套手感(拖动分隔条、双击复位、松开才写存档);纯函数 `web/src/scm-sidebar.ts` + 单测 `tests/unit/scm-sidebar.test.ts`,真浏览器回归 `tests/scm-test.mjs`(拖 +120px → 刷新后保持 → 双击回 300px)。
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **正在聊的那条对话不再从左栏「运行的对话」里消失**(issue #140)—— 原来新对话只有「被换到后台且仍在流式输出」时才入列,于是你在当前对话里发消息时,左栏里根本没有它;只开一条对话时连「运行的对话」这个标题都不渲染,观感像是对话丢了。现在**当前对话一旦有内容**(有消息、或已被首条提示词命名)就立刻出现在列表里(还在流式输出时也会亮起绿点),行上标「当前」、点击是安全的空动作。
|
|
23
|
+
- **空白新对话仍然不入列**(连点「新建对话」不会堆出一排空条目),历史对话列表也不受影响。
|
|
24
|
+
- 这是**纯展示口径**:`listed` 的语义,以及「被挤出后台时是保留还是释放 runtime」「每项目 8 个上限」这些规则完全没变(换走时该释放的仍然释放);列表里当前对话那一行的 ✕ 按同一口径判定,真能移出(不再静默无反应)。
|
|
25
|
+
- 回归:`tests/unit/…` 无(口径在服务端),零 token 协议测试 `tests/running-list-test.mjs`(空白不入列 / 流式中入列 / 跑完留住 / 当前行 ✕ 可移出 / 后台运行语义不变),真浏览器 `tests/panel-layout-test.mjs`(区标题、「当前」副标签、流式绿点、只有一行)。DSH 引擎同口径一并改了(共用同一套左栏与 wire 协议)。
|
|
26
|
+
|
|
27
|
+
- **切项目时左栏不再闪一下项目名**(#140 那个改动的回归)—— 切项目会自动切到该项目的对话;如果切过去时那个项目只有一条对话(就是这条当前对话),列表里只有一组,它顶上原来会闪出一行项目名再消失(实测约 8ms 一帧)。原因是两个信号不同时到达:`conversations` 推送先到(activeId 已是新项目的对话),带新 `cwd` 的快照后到,只按 `cwd` 分组的那一帧就把当前项目当成了「别的项目」。
|
|
28
|
+
- 现在**只要当前对话在列表里,就认它所在的分组为当前项目**(`currentCwd` 只在它不在列表里时——空白新对话——作为回落),并直接置顶,顺带免掉随后的位置跳动;其他项目的后台运行照旧显示项目名。
|
|
29
|
+
- 分组逻辑抽成纯函数 `web/src/conv-groups.ts` + 单测 `tests/unit/conv-groups.test.ts`,另有真浏览器逐帧回归 `tests/conv-group-flash-test.mjs`(MutationObserver 记录每一帧 DOM,两个方向都断言不再渲染项目名;改回只看 `cwd` 即变红)。
|
|
30
|
+
|
|
31
|
+
<!-- auto-i18n:start -->
|
|
32
|
+
### i18n
|
|
33
|
+
|
|
34
|
+
- 服务端新增 key(1):`terminals.cwd.outside.workspace`
|
|
35
|
+
<!-- auto-i18n:end -->
|
|
36
|
+
|
|
37
|
+
## [0.83.0] — 2026-09-13
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **子代理模板可以固定思考强度了**(issue #130)——模板原来只能固定模型、提示词与白名单:`explore` 想跑快点、`review` / `oracle` 想往深处想,只能跟主对话共用一个档位;更隐蔽的是子代理原本一律吃 SDK 默认档(medium),主对话调到 `xhigh` 也传不过去。现在模板编辑器里多了一个**思考强度**下拉(off / minimal / low / medium / high / xhigh / max,与顶栏那个下拉共用同一份档位与文案):
|
|
42
|
+
- **留空 = 跟随主对话当前强度**(与「模型留空 = 跟随主对话当前模型」同语义);指定了就用该档位 —— 于是「角色 + 模型 + 强度」能配成一套固定组合。
|
|
43
|
+
- **模型不支持的档位自动收敛**(SDK 行为:非推理模型只能 `off`,`xhigh` / `max` 需要模型声明 `thinkingLevelMap`),收敛不报错、不影响派发。唯一行为变化:子代理默认强度从「SDK 默认档」变成「跟随主对话」。
|
|
44
|
+
- AI 侧的 `subagent_templates` 清单里每个模板都报出模型与思考强度(没配的写「跟随主对话…」),派单时不用猜。
|
|
45
|
+
- 脏数据宽容:老 `subagent-templates.json` 没这个字段 → 空(跟随主对话);值写错(如 `ultra`)当未配置处理,不报错也不猜。
|
|
46
|
+
- 回归:单测(字段归一 / 只认七档 / 内置模板不预设强度 / 工具输出报出强度)、协议冒烟 `subagent-template-test.mjs`(wire 透传 + 非法值归一 + 落盘)、新增零 token 端到端 `subagent-thinking-test.mjs`(mock provider + reasoning 模型:模板 `high` → high、留空 → 跟随主对话的 `low`、模板 `max` → 收敛成 high;改动前这三条全是 medium)。
|
|
47
|
+
|
|
48
|
+
- **「浏览器操作」面板支持把已授权页面一键引用到对话** —— 之前要让模型操作某个页面,得在话里手打网址。现在:只授权了**一个**页面时,顶栏按钮直接变成那个页面的标题(点主体就把引用放进输入框,右侧 ▾ 仍是状态面板);多个页面时,面板里每项都有「引用到对话」,可连续引用多个。
|
|
49
|
+
- 引用进输入框的是 `🌐 页面标题` 的附件 chip(与「引用文件」同一套:可删、可多条、可和文件附件混搭),**不自动发送** —— 你补一句「把前十条读出来」再发;编辑重问时照旧恢复。
|
|
50
|
+
- 发送时服务端把附件渲染成给模型的一句话(`<browser-page url title>`):**这个页面已授权、用 `browser_page`、`target=` 该 origin** —— 模型不必从自然语言里猜网址,也不会跑去抓网页。
|
|
51
|
+
- 回归:`browser-control.test.ts`(单页紧凑态判定 / 引用附件形状)、`attachments.test.ts`(不读文件 + target 提示 + 属性转义)、`question-attachments.test.ts`(重问恢复),以及 `browser-cite-test.mjs` E2E(单页按钮、点击引用、去重、chip 删除、多页面板引用)。
|
|
52
|
+
|
|
53
|
+
- **`browser_page` 支持截图(`op:"shot"`):模型终于“看得见”页面** —— 之前它只能靠 `read` 读 DOM 文本,「这页看起来对不对」这类问题答不了。截图支持整屏或指定元素(按 rect 裁剪,元素几乎不在视口里时不截,而不是给一张碎图),长边默认 1280 / 上限 1568(JPEG)。
|
|
54
|
+
- **主模型能识图就直接给图**(当轮可见);**纯文本模型自动走视觉桥转写**(与用户粘贴图片同一套逻辑与提示词,设置里开着就生效,无需额外配置)。注意:`deliverAs:"nextTurn"` 的附件通道要等下一次用户发言才注入,所以截图**不能**走那条路——必须在工具结果里给图或转写文本。
|
|
55
|
+
- **两个必须知道的代价**(选项页与 README 都写明):① 浏览器的 `captureVisibleTab` 只认 `<all_urls>` 或「点图标那一刻的 activeTab」,普通 host 授权不够 —— 所以打开「允许截图」会弹一次「读取您在所有网站上的数据」,不给就保持关闭(模型仍能用 `read`);② 截图只能截当前活动标签页,扩展会先把目标页切到前台、**截完立刻切回**(失败也切回)。
|
|
56
|
+
- 开关:「允许截图」默认开(但要先授一次权限),关掉后 `shot` 直接被拒并说明去哪开。
|
|
57
|
+
- 回归:`page-picker-bridge.test.ts` 的 shot 用例(切页顺序、失败也切回、按 rect 裁剪、没权限时明确拒绝、开关关闭)、`browser-page-tool.test.ts` 的结果组装(给 image block 且 data 是纯 base64 / 走视觉桥带 `<vision-bridge>` / 桥不可用时说明原因 / 老替身不炸)、`page-picker-ai-ops.test.ts` 的 `metrics`,以及真扩展 E2E(截图开关与权限门控的文案)。真扩展 E2E 还抓到一个真 bug:`captureVisibleTab` 的 `quality` 必须是 **0-100 的整数**,传 0.72 会报 `expected integer`。
|
|
58
|
+
|
|
59
|
+
- **「AI 操作浏览器页面」在 pi-web-ui 侧终于有入口了**(顶栏「浏览器操作」按钮 + 状态面板)。上一版把能力做完了(`browser_page` 工具 + 扩展授权表),但之前**用户那边一片空白**:不知道有这个能力、不知道去哪开通、不知道能说什么。现在:
|
|
60
|
+
- 顶栏按钮显示状态(授权了几个页面),需要你动手时(未授权 / 总开关关)变成醒目色;
|
|
61
|
+
- 面板里报「扩展在不在 / 已授权的页面(标题 + 地址 + 开没开)/ 两个总开关」、给出授权三步、两句可照拄的例子;
|
|
62
|
+
- **「打开扩展设置页」按钮**:网页不能自己导航到 `chrome-extension://`(浏览器会拦),所以这一步由扩展代劳(新动作 `openOptions`);状态查询走新动作 `status`。
|
|
63
|
+
- **AI 在页面上动手时,那个页面会闪一条提示**:「AI 正在操作本页 · click #submit」(右下角、挂 shadow DOM、只报信不拦截、2.2s 淡出、不堆叠、卸桥时一并清掉)—— 否则用户会以为页面自己在动。
|
|
64
|
+
- 回归:`tests/unit/browser-control.test.ts`(状态查询与开设置页的四种失败/成功路径)、`page-picker-ai-ops.test.ts` 的提示条用例、`tests/page-picker-edge-ext-test.mjs` 里真扩展下的 `status` / `openOptions`(真的开出一个设置页)。
|
|
65
|
+
|
|
66
|
+
- **模型多了一个 `browser_page` 工具:直接操作你在浏览器里授权的页面**(需要 pi-web-ui 0.83.0+ 与 page-picker 扩展 0.4.0+)。以前人只能把页面“描述”给 AI(拾取元素→粘上下文),现在模型可以在对话里直接读那个页面、点它的按钮、填表单、滚动、跳转,必要时在它里跑一段脚本。
|
|
67
|
+
- **链路**:模型调工具 → 服务端把请求推给浏览器里那个 pi-web-ui 页面(新协议消息 `page_request`)→ 页面经宿主桥 `window.__piWebUiHost.pageCall()` 转给扩展 → 扩展在授权页面上执行 → 结果原路回到模型(`page_response`)。所以**那个 pi-web-ui 标签页得开着**(与拾取投递同一个取舍)。
|
|
68
|
+
- **只需授权被操作的那个页面**:另一端固定是 pi-web-ui 页面,不用像页面桥那样配两端。授权在扩展选项页点一次(顺带申请 host 权限),可随时收回。
|
|
69
|
+
- **八个动作**:`pages` / `read`(文本/HTML/title/url/元素查询)/ `click` / `type`(走原生 setter + 补发 input/change,React 受控组件也认;`submit` 发回车)/ `scroll` / `goto`(先回结果再跳)/ `wait`(等元素或文案)/ `eval`。
|
|
70
|
+
- **三层开关**:pi-web-ui 设置→工具里的 `browser_page`、扩展选项页的「允许 AI 操作页面」总开关、以及单独一项 **`eval`(默认关)** —— eval 等于把页面交给模型写的脚本,要用得手动打开。
|
|
71
|
+
- **安全边界**:授权列表是唯一凭据(未授权页面一个字节都不注入);只有“浏览器里那个已绑服务地址的 pi-web-ui 页面”能发起(用 `sender.tab.url` 判定);动作名过白名单(其它一律拒)。
|
|
72
|
+
- **失败都说人话**:没授权去选项页授权 / 页面没打开 / 多个页面要指定 target / 选择器没匹上 / wait 超时返回 `found:null` / 页面 CSP 禁了 eval 时提示换动作 / 浏览器里没开 pi-web-ui 页面时提前退出。
|
|
73
|
+
- 已知边界(写进 README 与选项页):eval 受目标页面 CSP 约束;桥只在顶层帧,跨源 iframe 拿不到;`chrome://`/扩展页/商店页/`file://` 不能授权;页面里的第三方脚本也能看到注入的 `window.__piBridge`(与页面桥同一个边界)。
|
|
74
|
+
- 回归:`tests/unit/browser-page-tool.test.ts`(工具 schema、args 组装、超时归一、`pageCall` 超时/迟到响应/无前端/中止)、`tests/unit/page-picker-ai-ops.test.ts`(八个动作的真 jsdom 行为)、`page-picker-bridge.test.ts` 的 AI 路由(宿主/授权页/配对页三角色、总开关与 eval 开关、白名单、`pages` 由 worker 直答)、`tests/unit/plugin-host-page-call.test.ts`(宿主桥 pageCall 的三条纪律)、`tests/page-picker-bridge-test.mjs` 与 `tests/page-picker-edge-ext-test.mjs`(**真浏览器**:宿主读授权页标题、真点击、eval 开关、宿主页面不走配对表)。
|
|
75
|
+
|
|
76
|
+
- **page-picker 扩展:页面桥 —— 两个配对过的页面可以互相读写**(跨源、跨标签页、跨窗口)。拾取是单向的(网页 → pi-web-ui 输入框),这块把另一个方向也打开:对端页面 `window.__piBridge.on("orders", () => …)` 注册能力,这边 `await window.__piBridge.call({ op: "orders" })` 拿到数据,或者调 `highlight` 去操作对端的 DOM。浏览器里只有扩展能做到这件事(跨源 `postMessage` 要 `window.open` 的句柄且对方配合,`BroadcastChannel` / `localStorage` 只限同源)。
|
|
77
|
+
- **默认关闭,只认配对**:选项页「页面桥」里填两个 origin → 点「授权并添加配对」(权限申请必须在扩展自己的页面上点,网页上的按钮给不了浏览器要的手势)。没配对的页面一个字节都不注入。
|
|
78
|
+
- **地址不用手打**:在要配对的两个页面里各点一次扩展图标,它们就进了候选下拉(点过图标那一刻有 `activeTab`,url 与标题都可读;不为此多要 host/`tabs` 权限);开发页的拾取浮条上还有「与另一页配对…」按钮,点一下把本页预填好并直接打开设置页的配对面板。
|
|
79
|
+
- **准入只看 `sender.tab.url`**:消息体里自称是对端也不作数 —— 否则 A 页面可以冒充 B,把 B 的数据全拿走。停用的配对同样拒绝,且与「没配对」分开报原因(一个是去启用、一个是去添加)。
|
|
80
|
+
- **失败都说人话**:对端没打开、对端没注册那个 op(报错里列出它注册了什么)、结果过大(参数 ≤ 256KB / 结果 ≤ 512KB)、返回值不可克隆(循环引用)、对端 handler 抛错、超时(默认 5s,页面侧自己兜底,Promise 不会悬着)。对端刚导航完(桥还没装上)会自动补装一次再重试。
|
|
81
|
+
- **注入时机**:SW 启动、配对表变更、页面导航完成时协调;删配对/停用会把页面上的桥卸下。
|
|
82
|
+
- 已知边界(写在选项页与 README 里,不藏):桥在页面**主世界**,所以被配对页面里的任何脚本(含第三方广告/统计)都摸得到 `window.__piBridge` —— 只对你信任的页面开桥;`chrome://`、扩展页、商店页、`file://` 不能配对;桥装在顶层帧。
|
|
83
|
+
- 回归:`tests/unit/page-picker-bridge.test.ts`(准入/路由/体积/页面侧函数的自包含性/配对候选)、`page-picker-options.test.ts` 的配对管理单测(加/删/停用真的落盘并通知 worker、候选下拉与 `?pair=` 深链预填)、`tests/page-picker-bridge-test.mjs` 浏览器 E2E(**两个真实 origin + 真实 `dist/bridge.js` + 真实 background 逻辑**:A 读 B 的数据、B 改 A 的 DOM、没配对的页面调不动任何人、删配对后桥真的被卸下),以及 `tests/page-picker-edge-ext-test.mjs` 的**真扩展**场景(真 `executeScript` 注入 MAIN world + 真 `storage.local` 喂候选下拉)。
|
|
84
|
+
|
|
85
|
+
- **page-picker 扩展:六个预设与「发送什么」的逐项勾选,在拾取页面上就能改** —— 原来只有扩展选项页能改(为了改一个勾选得去开 `chrome://extensions`),而「这次只要源码位置」「这次只排查样式」这种判断,恰恰是站在页面上看着元素时才有的。现在点完元素后,**底部确认条里就有一排预设 chip**(精简 / 标准 / 完整 / 改对地方 / 样式 / 文案),右边「调整项 ▾」可展开 8 项逐项勾选(与选项页等价,默认收起);拾取阶段的信息条上常显当前预设,`Alt+1~6` 是同一个入口(键盘也能把整套流程走完)。
|
|
86
|
+
- 改完**立刻按新档位重新采集已经选好的元素**:快照是点击那一刻取的,不重采就会出现「浮条上写着精简、发出去的还是完整档」;元素已被页面换掉(SPA 重渲染)时保留原快照,不影响投递。
|
|
87
|
+
- 选择会**写回扩展设置**(选项页同步可见、下次拾取沿用;只写 `detail` + `sections` 两个键,不碰服务地址与口令)。后台没响应时在**摘要行尾**说明「没同步到扩展设置(这次的选择只在本页生效)」—— 不用 toast,因为确认条正开着,盖住它反而看不清。
|
|
88
|
+
- **不允许勾到一项不剩**(空列表在契约里会回落标准组合,那会让人以为「我全取消了它还发」):取消最后一项直接拒绝并提示。
|
|
89
|
+
- 回归:预设短名 / `applySectionToggle` / 热键解析单测,浮条控件单测(jsdom 真点击:点 chip 回调、逐项勾选、拒绝清空、折叠面板、告警文案),service worker 写回单测(脏数据回落 / 只写两个键 / 写失败报错),E2E 页面上切预设后落盘 + 已选元素重采 + 发出去的 Markdown 真的变瘦。
|
|
90
|
+
|
|
91
|
+
- **图片工具插件适配手机端**:三栏硬布局(队列 236px + 舞台 + 参数栏 320px,一条 media query 都没有)在手机上必定挤爆 —— 现在视口 ≤ 640px 时改成上下堆叠:队列变成顶部横向缩略图带(只留缩略图与删除,名字/尺寸在手机宽度里全是省略号),参数栏变成底部抽屉(默认半开;顶上那条手柄一点就收起,只剩 tab 行 + 动作行,舞台立刻高一倍以上;抽屉收着时点任意 tab 会自动展开),舞台独占剩余高度,各区块自己滚,不会把宿主的 `.plugin-view` 顶出外层滚动条。
|
|
92
|
+
- 窄屏下按钮不再折行(中文按钮被压窄会变成竖排的「适应」),状态条改横向滚、不再换行把舞台越顶越小;弹窗 / 工作区列表贴边,行高按能点中做。
|
|
93
|
+
- 触屏(`pointer: coarse`)裁剪把手 11px → 20px、滑杆与勾选框同步加高;裁剪拖动本来就吃 `touch-action: none`,不会和页面滚动打架。
|
|
94
|
+
- 宽屏三栏布局与尺寸未动(回归里仍断言桌面三栏宽度与总高度)。
|
|
95
|
+
- 回归:`image-toolkit-view-test.mjs` 新增手机端一段(真 Chrome 390×780 + `isMobile`/`hasTouch`:堆叠方向、横向缩略图带、自下而上顺序、无横向溢出、抽屉收起与点 tab 展开、收起后舞台变高、触屏把手尺寸),并给测试页补上与宿主一致的 `<meta name="viewport">`(缺了它 `isMobile` 模拟下布局宽度是 980,断点根本不命中)。
|
|
96
|
+
|
|
97
|
+
### Fixed
|
|
98
|
+
|
|
99
|
+
- **右栏扩展区不再是一块「浅色主题下的深灰块」**:那块 widget 容器写死了 `background: rgba(0, 0, 0, 0.15)` —— 深色主题下正好,浅色 / 暖纸 / 雾蓝 / 樱粉主题下就是一块压在浅底上的深灰。现改成主题 token `--sunken-bg`(深色 = 15% 黑;浅色系按各自色调给 4%~5% 低透),`make-light-theme.mjs` 的 LIGHT_DERIVED / PAPER / MIST / SAKURA 各补一条、7 个内置主题由生成器同步重出。
|
|
100
|
+
- 语义写进 token 注释:**布局里禁止写死 `rgba(0,0,0,…)`**,凹陷内容面(比所在底板低一层的区域)一律引用 `--sunken-bg` —— 与上一版那批幽灵 token 同一条纪律。
|
|
101
|
+
|
|
102
|
+
- **goalbar / 问卷面板的背景不再比聊天区差一档**(PR #131 的观感跟进)——上一版把 goalbar 的幽灵 token 修好之后,它(和问卷面板)成了一块带底色的卡片;而它们所在的那一段(消息区与输入区之间)**只有卡片自己有底色**,卡片四周与列外区域的间隙露的是裸页面背景 —— 浅色与壁纸主题下就是**一条比聊天区更暗、一直横到面板两侧的带子**(深色主题差得少一点,同样能看出来)。
|
|
103
|
+
- 玻璃底上移到容器:`.main` 整块聊天面板统一涂 `--msgs-bg`(消息区 + goalbar / 问卷面板那一段 + 输入区),`.messages-wrap` 与 `.inputbar` 不再各涂一层 —— 叠加两层反而比 goalbar 区亮一档,会换一条新的横向色阶。
|
|
104
|
+
- goalbar 与问卷面板**不再自带底色**(即 0.82.0 的观感):背景 = 聊天背景,活动态靠琥珀边框、问卷靠强调色边框 + 投影区分;折叠态的小 pill 仍是控件底(`--chip-bg`),选项预览块(`.question-preview`)与问卷底部 sticky 条保持原样。
|
|
105
|
+
|
|
106
|
+
- **修掉一批「幽灵 CSS 变量」:goalbar、问卷面板、插件按钮在浅色主题下不再是深色块**(PR #131)—— 引用一个**全史从未定义**的自定义属性,按 CSS 规范是 guaranteed-invalid:**整条声明在计算值阶段失效**。`--bg-elev1`(正确名是 `--bg-elev`)从引入它的那笔提交起就是笔误,后果是:goalbar 全家**没有填充**(看着像故意画个框)、输入框丢掉整条 `box-shadow`(连聚焦光环一起没)、插件里带 fallback 的写法则静默用硬编码 `#16161d` → 白色 / 雾蓝 / 暖纸 / 樱粉主题下是深色块。同批还修了 `--glow-inset`(全史未定义)与 `--text-2`(全史未定义,`color` 回落 inherit → 比预期亮)。
|
|
107
|
+
- 按语义改回已定义变量(与 13be9ab 那批改名同方向):菜单内说明块 / 按钮 / 徽章 → `--bg-elev`;消息头悬停 → `--bg-elev2`(用 elev 的话白色主题下等于卡片色,悬停看不见);`--glow-inset` → `--glow-05`(`--glow-*` 家族最小的高光,各主题已有对应浅色值);`--text-2` → `--text-dim`;webmail / demo-mailbox 的 `--bg-elev1` / `--bg-elev0` → `--bg-elev` / `--bg`(保留原 fallback)。
|
|
108
|
+
- **goalbar 与问卷面板改走壁纸体系**:`.goalbar` → `--card-bg`、`.goalbar-hint` → `--chip-bg`、`.goalbar-active` 的 8% 琥珀叠色同步换底;`.dialog-inline`(扩展弹窗 / 提问对话框)与 `.question-preview` → `--card-bg` —— 它们本来就是列内卡片(与消息列同宽),原来写死 `--bg-elev2` 在壁纸 / 半透明主题下与四周玻璃面板有明显色阶差。问卷的 sticky 底栏保持实色(它的职责是遮住从下面滚过的正文,半透会让正文透出来)。
|
|
109
|
+
- 顺手删掉 `.goalbar-hint` 里那句被同规则后句覆盖的 `background: transparent`(正是它让「没有填充」看起来像有意为之)。
|
|
110
|
+
- 新增静态体检 `tests/unit/css-tokens.test.ts`:扫 `web/src` + `plugins` + `themes` + `web/index.html` 里每个 `var()` 引用,要求全仓某处有 `--x:` 声明(带 fallback 的也查);豁免运行时注入(`--fp-zoom` / `--left-w` / `--right-w` / `--rail-gap` / `--msgs-gutter`)、刻意的中性兜底(`--bg-input` / `--border-subtle` / `--muted` / `--warning`)与 vendored 的 `--vscode-*`。改前跑它报 14 + 5 处(styles.css 14 处无 fallback,插件 5 处带 fallback),改后归零 —— prettier / oxlint / 浏览器 E2E 都拦不住这种静默退化,所以让它进 CI。
|
|
111
|
+
|
|
112
|
+
- **MCP 桥的子进程崩溃后不再永久失效(自动重启,不用重启服务)**(PR #129)——外部 MCP 服务器被 OOM 杀掉、被外部 kill、或自己崩了之后,桥原来只把在途请求报错、把子进程句柄置空,**之后所有工具调用都写进空气**:挂满 60 秒报一句 `tools/call 超时`,而且**每次都是**这样,只有重启服务才能恢复。现在下一次工具调用会**先惰性重启**(重新 spawn + `initialize` 握手 + `tools/list`)再发;重启失败就直接抛「服务器进程已退出且自动重启失败:<根因>」,不再干等 60 秒。惰性而非退出即重启是刻意的:配置写错的服务器只在真被调用时试一次,不会空转拉进程。
|
|
113
|
+
- 三条生命周期不变量:显式关闭后**永久停用**(不复活)、重启过程中被关闭会回收刚起的进程**不留孤儿**、**并发调用共享同一次重连**(先判 `starting` 再判 `child`,否则第二个调用会抢在 `initialize` 应答前发 `tools/call`)。顺手修掉 spawn/握手失败漏子进程、退出后写 stdin 的 EPIPE(可能触发未捕获异常)两个隐患。
|
|
114
|
+
- 回归:单测 5 例(在途调用立即报「进程退出」而非挂超时 → 下一次调用自动重启成功、启动即退出的服务器快速报错、`close()` 后不再重启、崩溃后经 `PluginAgentTool.execute` 真实转发路径恢复);夹具新增 `crash` 自杀工具(工具数 8→9,冒烟同步),并开始按真 MCP 语义**在 `initialize` 应答写出前拒绝 `tools/call`(-32002)** —— 并发抢跑会因此变成可见失败。
|
|
115
|
+
|
|
116
|
+
- **page-picker 扩展:拾取时顶部信息条不再被挤成「竖排文字」** —— 它一直是 `left:50% + translateX(-50%)`(没有 `right`),也就是可用宽度只有 `50vw`,`max-width: 92vw` 实际从没生效;窗口一窄或提示一多,里面的字就被压成一行一个字。改成 `width: fit-content; margin: 0 auto`(居中效果不变)并允许**整项**换行;底部的提示条(toast)同一处理。
|
|
117
|
+
|
|
118
|
+
- **子代理会话里的扩展不再因为调用新 UI API 崩溃,也不会卡在无人应答的弹窗上**(PR #128)——子代理原来只拿到 `{ theme, setStatus, setWidget, notify }` 四个方法的 mock,扩展一旦调用 `ExtensionUIContext` 上的其他方法(`setWorkingVisible` / `setToolsExpanded` / `setTheme` …)就 TypeError,浏览器上还多一条 error toast;补成完整 `WebUIContext` 之后换了个坑:那个上下文没有浏览器面板,`select / confirm / input` 照旧挂 Promise,第一个在子代理里问用户问题的扩展会**永久 await**(只有 20 分钟的工具看门狗兜底)。现在子代理用 `WebUIContext.headless()`:方法面与主对话完全一致,但 UI 输出全部丢弃(不会与主对话的 widget/status 串台)、widget 组件工厂不调用(不留下没人 dispose 的组件)、弹窗立即按「取消」返回。
|
|
119
|
+
- 回归:`tests/unit/webui-context.test.ts`(弹窗立即取消 / 输出丢弃 / 不构造组件)+ `tests/subagent-ui-context-test.mjs`(零 token 端到端:探针扩展把 21 个新 UI 方法都调一遍,再断言子代理侧 `confirm=null`、主对话侧仍是「没人答」;改动前这两条分别挂 4 项与 1 项)。
|
|
120
|
+
|
|
121
|
+
暂无其他未发布内容。
|
|
122
|
+
|
|
123
|
+
<!-- auto-i18n:start -->
|
|
124
|
+
### i18n
|
|
125
|
+
|
|
126
|
+
- 前端新增 key(28):`browserPageEnabledDesc`、`browserPageOffHint`、`browserControl`、`browserControlTip`、`browserControlChecking`、`browserControlOffline`、`browserControlEmpty`、`browserControlDisabled`、`browserControlPages`、`browserControlPageOpen`、`browserControlPageClosed`、`browserControlExamples`、`browserControlExample1`、`browserControlExample2`、`browserControlOpenOptions`、`browserControlRefresh`、`browserControlCite`、`browserControlCiteTip`、`browserControlCiteNote`、`browserControlCited`、`browserControlCiteFailed`、`browserControlOpenPanel`、`browserControlSingleTip`、`attachPage`、`attachPageShort`、`tplThinkingLabel`、`tplThinkingFollowMain`、`tplThinkingHint`
|
|
127
|
+
- 前端中文变更(2):`settingsSubagentTemplatesDesc`、`noSubagentTemplates`
|
|
128
|
+
- 前端英文变更(2):`settingsSubagentTemplatesDesc`、`noSubagentTemplates`
|
|
129
|
+
<!-- auto-i18n:end -->
|
|
130
|
+
|
|
131
|
+
## [0.82.0] — 2026-09-13
|
|
132
|
+
|
|
133
|
+
### Added
|
|
134
|
+
|
|
135
|
+
- **page-picker 扩展:「发送什么」改成逐项多选(另配 6 个预设)**——原来只有三档详细度(精简/标准/完整),要么一起多、要么一起少;实际用起来常是「这次只要源码位置」「这次只要样式,别的别发」。现在设置页可以逐项勾:页面上下文 / 定位信息(选择器+尺寸)/ XPath 与 DOM 路径 / 源码位置(React/Vue 文件:行号 + 组件链)/ 文本 / 命中的 CSS 规则 / 计算样式 / HTML 骨架(元素截图仍是单独一项)。**没勾的在采集层就不采**,不只是渲染时丢掉——生成 HTML 骨架、读 CSSOM 这些本身就有开销,顺手也把这点省掉。预设覆盖常见组合:精简 / 标准(默认)/ 完整 / 只要能改对地方(选择器+源码)/ 只排查样式(命中 CSS+计算样式)/ 只看文案结构(文本+骨架),一键勾好之后还可以手动增减(预设同时决定采集深浅:文本长度、骨架深度、选择器深度)。老设置(只有 `detail`)升级后按原档位预勾,行为不变。
|
|
136
|
+
- 回归:`sectionsForDepth`/`normalizeSections`/`presetForSections` 单测、采集层「没勾就不采」单测(jsdom)、渲染层「只输出勾选项」单测、设置页多选 UI 单测(勾选真的落盘 / 预设联动 / 全不勾会提示并回落标准组合)、E2E 用自定义组合真投递一遍。
|
|
137
|
+
|
|
138
|
+
暂无其他未发布内容。
|
|
139
|
+
|
|
140
|
+
## [0.81.2] — 2026-09-13
|
|
141
|
+
|
|
142
|
+
### Fixed
|
|
143
|
+
|
|
144
|
+
- **page-picker 扩展:修「pi-web-ui 页面明明开着,却报『没找到打开的 pi-web-ui 页面』」**——0.2.0 查找目标标签页时传的过滤条件是 `["<地址>/*", "<地址>"]`,而**裸地址(没有路径的 origin)不是合法 match pattern**:真 Chrome/Edge 的 `chrome.tabs.query` 会直接抛 `Invalid url pattern 'http://localhost:8787'`,那个异常被 catch 成了「没找到页面」,于是拾取结果只能退化成「复制到剪贴板」(选项页「测试连接」里的同名查询也一并修)。现在查询只用 origin 级模式(`http://localhost:8787/*`),路径前缀仍由 `tabMatchesBase` 严格复核(子路径反代、前缀相似的站点都不受影响)。
|
|
145
|
+
- 这个 bug 能活着发布,是因为单测/E2E 用的是**假 chrome**,它不校验 match pattern:现在假 chrome 也按真 Chrome 的规则校验入参(`isValidMatchPattern`),这类坑会直接挂在单测上。
|
|
146
|
+
- 另加一条**装真扩展**的 E2E(`tests/page-picker-edge-ext-test.mjs`):实测 Edge(152,headless)仍接受 `--load-extension`,所以能在真 `chrome.*` 上把「拾取 → 投递 → Markdown 真的落进 pi-web-ui 输入框」跑一遍(没装 Edge 自动 SKIP)。
|
|
147
|
+
- **page-picker 扩展:修「在 pi-web-ui 页面上点图标没任何反应」**——绑定浮条原来完全依赖 background 的 MAIN world 探测(`__piWebUiHost` / `/api/health`),那个注入一旦被 CSP/权限/环境挡住,就会静默回落到拾取器,用户看到的就是「新功能没出现」。现在:探测不可用时也照旧注入浮条,**浮条自己再认一次页面**(同源 `/api/health` + 标题/输入框 DOM 兵形),认出是 pi-web-ui 就正常问「要不要绑成服务地址」,不是就自己退场并请 worker 补注入拾取器——**「点了图标什么都没发生」在三条路上都不可能发生**;路由决策同时打进 service worker 控制台,方便排障。
|
|
148
|
+
|
|
149
|
+
暂无其他未发布内容。
|
|
150
|
+
|
|
151
|
+
## [0.81.1] — 2026-09-13
|
|
152
|
+
|
|
153
|
+
### Added
|
|
154
|
+
|
|
155
|
+
- **page-picker 扩展:在 pi-web-ui 页面上点一下图标就能绑定服务地址**——远程/局域网部署时地址是 `http://39.99.235.208:8787` 这种、端口也不固定,原来只能去选项页手打地址再点「授权该地址」。现在点扩展图标会**先认当前页**:页面上有宿主动作桥 `__piWebUiHost` 即认定,老版本则退一步探一次同源 `/api/health`(`{ok, piVersion}` 才算数,所以任何「所有路径都回 200」的站点都不会被误认);认出是 pi-web-ui 就在页面底部弹浮条问「要不要把它设为拾取服务地址」,点一下即可(地址/端口/子路径全部按当前页面算,`?token=`、hash、尾斜杠都会归一掉),已经是当前地址时只说明现状不再多问,浮条上另有「在本页拾取元素」(开发 pi-web-ui 自己时用得上)。缺那一个 origin 的授权时,浮条会提示并给一个「打开设置页授权」按钮 —— `chrome.permissions.request` 必须在扩展自己的页面里点(网页上的按钮给不了浏览器要的手势),那个页面带 `?bind=` 预填地址、一键授权 + 绑定。**普通页面点图标的行为一点没变**(仍是进入拾取模式),也**绝不静默改地址**(改前一定在页面上问一次)。回归:`detectPiWebUi`/`bindView` 单测 + service worker 分流单测 + `?bind=` 面板单测(真 options.html)+ E2E(真 pi-web-ui 页 / 真夹具页各自认定 + 浮条绑定后照常投递)。
|
|
14
156
|
|
|
15
157
|
## [0.81.0] — 2026-09-12
|
|
16
158
|
|
|
@@ -566,7 +708,9 @@
|
|
|
566
708
|
- 0.35.1(2026-08-27):编辑重问保留附件(#18)+ 全窗口拖放(#19)。
|
|
567
709
|
- 0.29.0(2026-08-23):全局搜索弹窗(Ctrl+K)+ 消息列表惰性窗口化。
|
|
568
710
|
|
|
569
|
-
[Unreleased]: https://github.com/xing-shuyin/pi-web-ui/compare/v0.
|
|
711
|
+
[Unreleased]: https://github.com/xing-shuyin/pi-web-ui/compare/v0.84.0...main
|
|
712
|
+
[0.84.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.84.0
|
|
713
|
+
[0.83.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.83.0
|
|
570
714
|
[0.80.1]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.80.1
|
|
571
715
|
[0.80.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.80.0
|
|
572
716
|
[0.79.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.79.0
|
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ theme switching, and a full settings panel — tuned for daily development.
|
|
|
101
101
|
### 🤖 Subagents & templates
|
|
102
102
|
|
|
103
103
|
- **First-party subagents** — spawn independent background conversations for parallel exploration / implementation / review (`subagent_spawn`, with optional `model` override or a template's model); collect results without polling via `subagent_wait_all` (blocks until every subagent finishes, then summarizes results/errors). Manage them like a chat right in the left panel: view live output, inject follow-ups (steer), abort, dismiss — failed runs surface a red dot in the running list and an error notice in the main chat. In-memory sessions — they never touch the history / resume list, and can be nested.
|
|
104
|
-
- **Subagent templates** — configure reusable presets in Settings → Subagent templates: a role system prompt (append or replace), skills & extensions whitelists,
|
|
104
|
+
- **Subagent templates** — configure reusable presets in Settings → Subagent templates: a role system prompt (append or replace), skills & extensions whitelists, an optional per-template model, and an optional thinking level. The AI picks one via the `subagent_templates` tool and `subagent_spawn(template="…")`, or spawns without one (default = follow the main conversation's current model **and thinking level**, or the global default subagent model set in the same panel). A template that sets them pins that exact combination (unsupported thinking levels are clamped by the SDK to the nearest one the model supports). Disabled templates stay in the panel for re-enabling but become invisible to the AI tools (can't be listed or picked). Templates are shared globally across browser clients (`<dataDir>/subagent-templates.json`). Six built-in templates (review / implement / research / scout / audit / delegate, adapted from the pi-subagents community projects) seed the list on first run — marked 「Built-in」, editable and deletable like any other.
|
|
105
105
|
|
|
106
106
|
### 🖼️ Files, images & attachments
|
|
107
107
|
|
package/README.zh-CN.md
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
**子代理与模板**
|
|
63
63
|
|
|
64
64
|
- **第一方子代理** —— 后台派发独立对话并行做调研 / 实现 / 审查(`subagent_spawn`);与普通对话一样在左栏管理:实时查看输出、补充(steer)、中止、移出。内存会话——不进历史 / resume 列表,可嵌套派发。
|
|
65
|
-
- **子代理模板** —— 设置面板「子代理模板」里配置可复用预设:角色系统提示词(追加或整体替换)+
|
|
65
|
+
- **子代理模板** —— 设置面板「子代理模板」里配置可复用预设:角色系统提示词(追加或整体替换)+ 技能/扩展白名单 + 可选模型 + 可选思考强度。AI 用 `subagent_templates` 工具查询清单、`subagent_spawn(template="…")` 选用,也可以不传模板按主会话默认配置运行。模型与思考强度都留空 = 跟随主对话当前设置(与「跟随主对话」的模型回落同语义);模板指定了就固定用那个组合(模型不支持的思考档位会自动收敛)。停用的模板保留在面板可随时重新启用,但对 AI 工具不可见(查不到、不能选)。模板全局共享(`<dataDir>/subagent-templates.json`,所有浏览器客户端一致)。首次运行自带 6 个内置模板(review / implement / research / scout / audit / delegate,改编自 pi-subagents 社区项目),面板标「默认」徽标,可像普通模板一样修改或删除。
|
|
66
66
|
|
|
67
67
|
**文件、图片与附件**
|
|
68
68
|
|