# 更新记录 ## 未发布 - 文档整理:README 精简为中文入口并新增英文版 `README.en.md`;其余内容按主题拆到 `docs/guide/`,设计与调研记录移到 `docs/dev/`;`LOGIN.md` 并入 `docs/guide/platform-login.md` 并按当前的 opencli 优先做法改写(登录由用户自己完成,插件没有登录窗口);README 里的兼容表与升级说明移到本文件;新增 `scripts/check-docs-links.mjs`(`pnpm run check:docs`,已并入 `verify`)检查文档链接、锚点和中英文 README 的标题数量。没有改动运行时行为。 ## 从旧版本升级 适用于 0.2.x 各版本之间的升级(0.1.x 宿主或 dsh-browser 0.1.x 请不要升级,停留在 `dsh-web-search-pro@0.1.15`,见[兼容与发布通道(历史)](#兼容与发布通道历史))。DSH Desktop 用户在插件页卸载后重新 Add plugin,写法见 [安装与首次使用](docs/guide/install.md#用-dsh-desktop-安装)。 先确认宿主是 `dsh-v0.2.0-rc.2` 或 `dsh-v0.2.1-rc.2` ~ `rc.4`,浏览器插件(如果装了)是 `@anweat/dsh-browser@0.2.2`(或更新的 0.2.x;0.2.0-rc.2 宿主上 0.2.0 / 0.2.1 也可以);两者都需要作为 profile 的直接依赖。 ```bash dsh plugin --profile web add @anweat/dsh-browser@0.2.2 dsh-web-search-pro@0.2.3 ``` > **不再支持旧线**:宿主仍是 DSH 0.1.x,或浏览器插件仍是 `@anweat/dsh-browser` 0.1.x,请不要升级。只升级本插件、保留旧浏览器插件时,本插件照常工作,但所有浏览器相关能力都会返回 `CAPABILITY_UNAVAILABLE`,`sources.status` 显示 `browser: legacy (unsupported)`。 > **破坏性变更(工具面,0.2.0 起)**:旧的 `web_search_pro`、`web_fetch_pro` 等 11 个工具名不再注册,也没有兼容包装。旧会话里对它们的调用会失败;把调用改成对应动作即可,对照表见 [旧工具到新动作](docs/guide/tools-and-actions.md#旧工具到新动作)。模型在 `web_call` 里写旧工具名会得到新动作名和翻译后的参数,`web_index()` 根目录也列出同一张对照。已存储的数据(历史、页面、规则、证据、账本)与 `ctx.web` provider 不受影响。其他不兼容变化与新功能见下面各版本。 升级完成后需要**完整停止并重新启动 Web profile**;仅刷新网页不会重新扫描插件的 `client.js`。随后依次检查: 1. `browser_call({action:"runtime.status"})`:确认 OpenCLI、`playwright | patchright` 运行时、`automationMode` 与 `usagePolicy` 符合预期。 2. `web_call({action:"sources.status"})`:确认搜索、CLI、Agent Reach 与浏览器后端是否 ready(浏览器应显示 `ready`,不是 `legacy (unsupported)`)。 3. 打开 `插件 → 已安装` 中两个 bundle 各自的详情页,确认配置表单都已加载;浏览器表单负责自由度、运行时、OpenCLI 与调用缓冲。 > `automationMode` 和防止过度调用的 `usagePolicy` 都属于 dsh-browser,升级不会自动改写现有配置。生产 profile 建议保留 `standard`;`unrestricted` 只用于隔离的自动化测试 profile,并且仍受并发、突发、页数/深度和 429/503 退避保护。 升级到 0.2.3 时请特别注意:已有 `DEEPSEEK_API_KEY` 的用户,默认搜索会先走按请求计费的 DeepSeek 搜索(见下面 0.2.3 的“请先看:默认行为会变”)。 ## 0.2.3(2026-10-28) 接入 **DeepSeek Search / Fetch API**([文档](https://api-docs.deepseek.com/guides/search/),2026-10-17 上线)并标为“**常用**”。兼容范围见下面“同时支持 DSH 0.2.0-rc.2 与 0.2.1-rc.2 ~ rc.4”(DSH `>=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-rc.2 <0.2.2-0`,可选 `@anweat/dsh-browser ^0.2.0`),没有破坏性变更,Config schema 只增不改(`deepseekFetch`、`keyedSources.deepseek`;`playwright.snapshotDir` 现在声明为 volatile,见下文面板保存的修复)。 ### 请先看:默认行为会变 - **已经有 `DEEPSEEK_API_KEY`(环境变量、DSH 凭据或设置里的密钥字段)的用户,升级后默认的 `search.run` 和证据模式自动规划会先用 DeepSeek 搜索,而它按请求计费**:Search 与 Fetch 各为每 1000 次成功请求 $0.75(空结果、失败、超时不计费,`count` 不影响价格)。文档写明经 DeepSeek Harness / Claude Code / Codex 的服务端工具调用在 2026-11-01 00:00(北京时间)前免费,但没写插件直接调用算不算,**所以本插件按会计费处理**。 - 不想用:在 `sources.disabled` 里加 `deepseek`;只想在需要时用:自己写一份 `engines`(自定义过的 `engines` 列表保持你写的顺序,不会被自动加入);想限量:`sources.budget.deepseek: { total, daily }`;`evidence.sourcePolicy: anonymous-only` 也不会自动使用它。没有 Key 时行为与 0.2.2 完全一样。 - **登录类平台的默认后端顺序变了**:小红书、知乎、Reddit、X 现在先试独立 `opencli`(用你日常 Chrome 的登录态),再试独立 CLI(`xhs`、`zhihu`、`rdt`、`twitter`),最后是 dsh-browser 的后端——知乎、Reddit、Twitter 原来是独立 CLI 在前(小红书本来就是 opencli 在前)。已经登录了独立 CLI 并想继续优先用它的,在设置 `platformBackends` 里写出顺序(显式配置原样优先,不受影响)。没装独立 opencli 的用户行为不变:它被跳过并写明原因,链继续往下走。见下文“登录类平台改走 OpenCLI”。 ### 同时支持 DSH 0.2.0-rc.2 与 0.2.1-rc.2 ~ rc.4(2026-10-28) 0.2.2 把 peer 移到了 0.2.1 线,留在 0.2.0-rc.2 宿主上的用户(0.2.1 的 peer 是 `<0.2.1-0`)就没有可装的新版本;DSH 又出了 0.2.1-rc.4。0.2.3 两条线都接。**运行时代码没有为此改动**——评估了插件用到的每个宿主接口,0.2.0-rc.2 上都在、语义相同,真实宿主验证也都通过: - peer:每个受管 `@deepseek-ai/dsh-*` 改为 `>=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-rc.2 <0.2.2-0`,cordis 改为 `~4.0.4`(0.2.0-rc.2 宿主依赖 `~4.0.4`,0.2.1-rc.x 依赖 `~4.0.5`,干净安装都解析到 4.0.5)。 - 对照结果(`git diff dsh-v0.2.0-rc.2 dsh-v0.2.1-rc.3`):设置面板的原子 `mutate` 与 `.volatile()` 校验(`dsh-settings` / `dsh-client-ui-settings`)两个版本相同,只有 rc.3 去掉了远程浏览器的“内存模式”,插件不读它;私有 RPC 通道(`connection.rpc.handle`、`webServer`)的注册接口相同(rc.3 只多了 Peer 的概念,处理函数的签名不变);凭据服务(`credentialRef`、`credentials.resolve`、面板的 `remote.credentials`)相同,只是 rc.2 在“只读来源遮蔽引用”时拒绝写入、面板会原样显示这条拒绝;`tools/pre-execute` 的 `allow | deny | ask` 与“没有审批通道的 ask 变成拒绝”(`serviceAsk`)两个版本一致;`dsh-client-ui-primitives` 的 `Button / Modal / Switch / Tag / Tooltip`、`plugins.bundle.config` 槽位、`configForms.whileServed` 都在 rc.2 里。没有需要“缺了就降级”的分支,原有的降级(没有 `connection` / `webServer` 时不注册 RPC、没有 skill 注册表时用内嵌指南)继续保留。 - 真实宿主(隔离 `DSH_HOME`,从 npm 装进 scratchpad 的 `@deepseek-ai/dsh` 0.2.0-rc.2 / 0.2.1-rc.3 / 0.2.1-rc.4,用 `pnpm pack` 出的 tgz 以 `file:` 安装):三个宿主上 `dsh plugin add` 退出码 0、无警告;headless 下 `web_index` / `web_call` 都在,`sources.status`、`config.inspect|set_order|reset` 可用,`config.upsert_cli` 被拒绝而不是执行;Web profile 下设置卡片渲染,保存一项后 `cordis.patch.yml` 里恰好多一个键,恢复默认后移除,agent 覆盖层的“采纳到配置”(原子 `mutate`)和“撤销”(私有 RPC)、自定义 CLI 的试运行(RPC `trial`)都工作,保存之后插件**不重启**就读到新配置(`ctx.fiber.config` 热更新)。反向对照:未改 peer 的 0.2.3-dev.1 装进 0.2.0-rc.2、旧的 0.2.1 装进 rc.4,都被安装闸门以 `incompatible` 拒绝。 - `test/compat/check-peer-ranges.js`:受支持基线 `0.2.0-rc.2`、`0.2.1-rc.2` ~ `rc.4`;`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.1-alpha.5`、`0.2.1-rc.1`、`0.2.2-alpha.1` 在两种解析模式下都拒绝;cordis 4.0.4 / 4.0.5 通过、4.1.0 拒绝;`--selftest` 25 条已知行为(比之前多的 9 条是两段范围的断言),范围判定与 `semver` 7 逐版本核对一致。 - 可选 Browser peer `^0.2.0`:0.2.0 线宿主上 Browser `0.2.0` / `0.2.1`(及 0.2.2)都能装;0.2.1 线宿主上,装 Browser 0.2.0 / 0.2.1 会被安装闸门拒绝(`nothing was installed`,连同这次一起装的本插件也没装上),要用 0.2.2 起。 ### 修复:新用户没有浏览器时的提示(2026-10-28) `@anweat/dsh-browser` 装上不等于能开浏览器:新用户机器上没有 Playwright 的 Chromium。dsh-browser 0.2.2 会自己判断能否启动、有没有系统 Chrome / Edge 可用(`status().browser`);本插件把这一点说出来,而不是一律显示“ready”: - `sources.status` 的 `browser` 多一个 `launch: { ready, source?, path?, advice? }`;没有可启动的浏览器时那一行写成 `❌ browser:dsh-browser [loaded, no browser to launch] — `,用系统浏览器时写 `uses the system browser <路径>`。dsh-browser 没有报这个字段(旧版本)或查询失败时什么都不说。 - `read.fetch mode=auto`:浏览器渲染失败的原因原来只记成 160 个字符的 `detail`、正文里完全看不到;现在浏览器那一条保留 500 个字符,页面文字末尾多一行 `Browser render did not run: <原因>`,所有后端都失败时错误里带上 `(browser render failed: <原因>)`。 ### 分发:干净安装与同装(2026-10-28) 用户原话:npm 有可能遇上 opencli 或 playwright 的分发问题,可以顺便修。 - **本插件自己**:没有 `playwright` / `patchright` / `@jackwener/opencli` 依赖,也没有安装脚本;运行时只依赖 `cross-spawn`、`js-yaml`、`node-html-parser`。pnpm 11 下 `dsh plugin add ` 在 0.2.0-rc.2 / 0.2.1-rc.3 / 0.2.1-rc.4 三个宿主上退出码 0、无警告,共 20 个包、约 10 MB、1–2 秒(pnpm 存储已有缓存;没有测冷缓存)。 - **和旧 Browser 同装会被连带失败**(复现):`@anweat/dsh-browser` 0.2.1 及更早的 dev 版把 `@jackwener/opencli`(有 postinstall)列为依赖,pnpm 11 默认拦下它的构建脚本,`dsh plugin add` 以 `ERR_PNPM_IGNORED_BUILDS` 退出码 1 失败。包其实装上了,但宿主只把“新增依赖”加入 profile 的 `dsh.profile.bundles`,所以失败的那次里的**两个插件都没有被启用**;此后在这个 profile 里再 `add` 任何包(本插件单独装、甚至无关的 `is-odd`)都因为同一个被忽略的构建而失败,pnpm 还会往 `pnpm-workspace.yaml` 里写一条 `allowBuilds: '@jackwener/opencli': set this to true or false`。本插件不能在自己这边修这个问题;修在 Browser 0.2.2(OpenCLI 改为可选 peer,按 PATH、全局 npm 目录等找;系统 Chrome / Edge 顶替没下载过的 Playwright Chromium)。用 Browser 0.2.2 的候选包验证:三个宿主上 Browser + 本插件一起装、先本插件后 Browser、再装无关包,退出码都是 0,bundles 里两个插件都在。 - **playwright / patchright**:两个包都没有 install / postinstall 脚本,不会在安装时下载浏览器,所以不存在“脚本被忽略后怎么办”;缺的是浏览器本身,见上一节(本插件说清楚,dsh-browser 自动用系统 Chrome / Edge)。同装 74 MB(旧 Browser,含 OpenCLI)→ 50 MB(Browser 0.2.2,不含 OpenCLI)。 - **README**:新增“新用户第一次安装 / 第一次使用”两段,写明 Browser 0.2.2 起才能同装、OpenCLI 与浏览器怎么装、没有浏览器时会看到什么。 ### 验证(宿主两条线与分发,2026-10-28) 见 `docs/validation/2026-10-28-host-lines-and-install.md`。 ### 设置里的搜索顺序、自定义 CLI 与置信度(2026-10-27) 用户原话:“把默认搜索参考顺序和 cli 加入都做到设置中,允许自定义加入可用的 cli 软件和命令,并将结果打包返回,自定义的也可以加入置信度”。Config schema 只增不改(`sources.confidence`,volatile);兼容范围不变。 - **搜索顺序(新分组,在“常用”之后,默认折叠)**:`engines` 是可排序列表(位置、名称、就绪、置信度、上移 / 下移 / 移除、下拉添加、“恢复默认顺序”= `unset engines`),下面是实际生效顺序和一句“为什么和列表不同”(新纯函数 `explainChain`,与 `effectiveChain` 同一规则);平台后端顺序每个平台一行 + 弹窗,可选后端 = 内置后端 + 声明服务该平台的自定义 CLI,“恢复默认”删除该平台的键。`platformBackends` / `cliAdapters` 的原始 JSON 移到“高级”。 - **自定义 CLI(新分组)**:列表 + 表单弹窗(命令、参数 token、自动推出的子命令、输出格式、整段输出、结果路径、字段路径、超时、用途、置信度、高级项)+ “查看 JSON” 双向同步;服务端的校验函数实时校验,消息显示在对应控件旁。规格新增 `engine`、`confidence`、`search.output.lines: { mode: whole }`(见 README“CLI 适配”);`platforms` 现在是声明服务的平台(只做通用引擎时可为空)。**偏离旧行为**:`platformBackends.<平台>` 里放 `custom-cli:` 现在要求它的 `platforms` 声明了该平台(之前任何平台都接受)。 - **整段输出**:stdout 整体一条结果,标题“`: <查询>`”、链接 `cli://?q=…`、文本截断并注明;证据管线**不抓取**这种链接(只列为来源,不占读页名额)。 - **结果打包**:自定义 CLI 的结果与其他来源同一结构,另带 `via`、`confidence`(低于 1 时)、`tag`(“user-defined, not verified by the plugin (用户自定义,未经插件验证)”);`search.run` 输出 schema、文本渲染、证据摘录行、缓存与 `sources.status` 同步(`providers[].confidence`、`chain[].confidence`、`sources.confidence`)。 - **置信度**:`factor(c) = 1 − 0.2·(1 − c)`,乘到每个来源的 RRF 项上,并乘到“只有存疑来源返回的 URL”的加分上;c = 1 不乘,所以内置来源全是 1 时融合分数与原来逐位相同(单元测试固定,`bench:eval-pack` v1 / v2 报告与改动前逐字段相同,bench 94 项测试通过);存疑来源最好的一条仍保证留在展示的列表里。 - **试运行 / 实时就绪**:插件在宿主的连接传输上注册私有通道 `/dsh-web-search-pro`(照 dsh-browser 的做法,没有该服务的宿主上不注册,面板照常工作并提示改用 `web_call` 的 `sources.status` / `search.run` 测试):`status`(各来源和链上各后端的就绪、原因、置信度)和 `trial`(用面板里还没保存的规格真的探测并搜一次,显示解析出的前三条和工具原话;先过同一套只读校验,不登录,不保存任何东西,受 `enableCliBackends` 控制)。 - 测试:新增 55 项(置信度公式与不变性、整段输出、规格字段、顺序编辑产生的 patch、`explainChain`、表单 ↔ JSON、面板渲染、RPC)。 ### 设置顺序、重启项标注、agent 覆盖层与 `config.*` 动作(2026-10-27) 用户原话:“设置文件怎么调整下设置顺序,怎么方便用户配置也可以想想,要不要设置外置便于agent修改和实时载入,还是某些写在设置profile中需要重启插件列表。”批准的方案:profile 配置是用户的基线,留在原处并重新排序;标出要重启的项;新增一个 agent 可改、实时生效的覆盖层和 `config.*` 动作;**不做**“agent 直接改一个被监听的自由文件”。Config schema 的字段名、默认值和语义不变,兼容范围不变。 - **顺序**:`Config` 的字段按设置页的分组重排(常用 → 搜索顺序 → 来源与密钥 → 自定义 CLI → 读取与证据 → 平台与运行时 → 高级与诊断;`sources` 内为 priority、disabled、confidence、budget)。宿主的通用配置视图、`--dump-config` 和设置面板的文本视图都按这个顺序;文本视图改为按同样的分组分节,带中文 / 英文分节标题。一个测试把 schema 钉在 `src/config-groups.ts` 的分组表上,每个字段属于且只属于一个分组。 - **重启项**:查明启动时只读一次的设置有 8 个——`toolSurface`、`playwright.snapshotDir`、`providerId`、`registerProvider`、`timeoutMs`(每个动作的截止时间与宿主工具上限;搜索内部的协作超时预算是热的)、`memoryCacheEntries`、`dbPath`、`verbose`,集中在 `src/restart.ts` 的 `RESTART_FIELDS`。设置面板里这些行带“重启插件后生效”标记和原因,保存时改到了它们保存条会提示,状态摘要显示“待重启 N 项”;`sources.status` 和 `config.inspect` 写出“待重启:<字段>(当前生效 X,配置为 Y)”。一个测试扫描源码里对启动时配置副本的读取,新增而没登记会失败。 - **agent 覆盖层**:`$DSH_HOME/data/web-search-pro/overlay.json`(与 `dbPath` 同目录),含版本、`revision`(乐观锁)、sha256 校验和、`values`、50 条审计;临时文件 + 改名,0600;按 mtime 和大小缓存重读,无 fs.watch。只能覆盖 `engines`、`sources.priority` / `disabled`、`sources.confidence.`、`platformBackends.<平台>`、`cliAdapters.`,且只能新增或替换。`dynamic()` 返回“基线 + 覆盖层”,所有消费者下次调用就生效;加载时用与改动同一批函数严格校验,解析失败 / 校验和不符 / 值被拒 / 与基线不再一致就**整体忽略**并在 `sources.status` / `config.inspect` 报告,绝不只应用一半。做法照 `@anweat/dsh-browser` 的策略覆盖层,没有跨包 import。 - **`config.*`(新动作组,`web_index` 里出现)**:`inspect`、`set_order`、`set_confidence`、`upsert_cli`、`remove_cli`、`reset`、`export`。每次写入要有 3–300 字符的 `reason`,可带 `expectedRevision`;成功后 `revision + 1`、写审计(时间、动作、键、前后值摘要、reason、sessionId)、立即生效;没有变化的写入不落盘。审批:**`config.upsert_cli` 一律先问用户**(提示里写命令、搜索与探测参数模板、环境变量名),其余不问;`dsh-browser.automationMode=read-only` 时全部拒绝(偏离简报:简报只说“不需要审批”,这里让只读模式也拦住写入,与 `cache.clear` 等一致)。**headless 下**宿主对“询问”一律拒绝(无 `approval` 服务按插件理由拒绝;有服务但无应答者得 `unavailable` 也拒绝;策略 `never` 直接拒绝),所以 headless 的 agent 加不了 CLI;测试把宿主这几处逻辑固定住,宿主升级后若行为变了会报警。 - **设置面板**:被覆盖的行(引擎顺序、优先 / 停用、平台后端链、置信度、自定义 CLI)带“agent 修改”标记,悬停看值、reason、时间;新分组“agent 修改”列出全部覆盖项与最近审计,每项“采纳到配置”(用保存条同款的原子 `mutate` 写进 profile,成功后才经 RPC 删掉这一项覆盖)和“撤销”,另有“全部撤销”(也用来修复被忽略的坏文件)和“刷新”;只在覆盖层里的 CLI 列在自定义 CLI 列表下面。RPC 通道 `/dsh-web-search-pro` 新增 `overlay`、`overlay.revoke`,`status` 带上覆盖层摘要与待重启项。 - **测试**:新增 54 项(935 → 989,另有 8 项需真实命令的跳过)(分组顺序、覆盖层文件读写 / 损坏 / 冲突 / 审计上限 / 合并、各 `config.*` 的参数与效果、审批与 headless、重启项检测与扫描、面板标记与采纳 / 撤销的 patch 与 RPC 调用)。测试进程使用一次性的 `DSH_HOME`,不会碰真实目录。 ### 审批前先校验参数(2026-10-27) 要审批的动作(`config.upsert_cli`、`cache.clear`、`history.delete`、`rules.*`、`sources.install`)现在先做和 `runAction` 同一套的参数校验(参数 schema,外加动作可选的 `precheck`;`config.upsert_cli` 的 `precheck` 就是写覆盖层时用的那套完整 `id` / `spec` 校验,一条一条列出问题),不合法就不问用户:钩子直接放行,`runAction` 在 `execute` 之前返回 `INVALID_ARGS` 和 schema,不执行、不写覆盖层、不记审计。之前真实模型会话里 `config.upsert_cli` 连 `id` 和 `spec` 都没给就进了审批,headless 下被拒(“requires approval, but no approval channel is available”),模型拿不到 schema。参数合法时审批判定不变;`automationMode=read-only` 下,合法的写入照样被拒,不合法的返回 `INVALID_ARGS`(没有东西会执行,给模型 schema 比一句拒绝更有用)。校验自身出错不会卡住调用。新增 8 项测试(含 flat 工具面与无审批渠道的宿主)。 ### 修复:热重载后设置页样式丢失(2026-10-28) 宿主热重载插件时页面不刷新,旧版本注入的 `