@mars-sea/dsh-commandcode-provider 0.13.4 → 0.14.1

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 CHANGED
@@ -4,6 +4,66 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ### Changed
10
+
11
+ - 同步 Command Code CLI `1.82.5` → `1.84.0`:请求版本标识(`x-command-code-version`)随之上调。这是**纯请求头同步**,不伴随运行时行为变更。官方日志两版共 8 条:`1.83.0`(2026-10-16)7 条**全部为 FEAT(新功能)**,集中在 CLI 的 mods 扩展体系——`panes`/`widgets`/`toasts`、mods 热重载、权限应答、会话记录脱敏、按请求路由模型、注入面 `{cmd.state,cmd.store,cmd.clock,cmd.model,cmd.prompt}` 与子代理类型、`/mods` 命令与 `cmd mods inspect`、`--safe-mode`(安全模式)、`hooks.transformTextDelta`、`cmd.process.spawn`、`cmd.ui.override`,另有一条「Planning and Implementation models follow the live model catalog」;`1.84.0` 仅 1 条,新增 `/statusline`。**四类关切(推理强度/ZDR/套餐与定价/协议)在官方条目中零命中**,且新增的 `/mods` 与 `/statusline` 都是本地 TUI(终端界面)命令而不是 API 路径,因此插件侧的协议面与数据面本身没有跟着变化。
12
+
13
+ - **产物级对照:`1.82.5` 与 `1.84.0` 的能力数据逐项相等。**两版 CLI 产物的模型注册表 100 条、efforts(推理档位)66 条、subscriptions(订阅表)9 条、ZDR 例外 24 条逐项相同,内嵌 `catalogVersion` 两版同为 `d6177bab…`;端点主机集合与自定义请求头集合零差异,因此版本号之外没有任何需要跟随的改动——`1.82.4` → `1.82.5` 那一轮把实时目录结果折回内嵌快照之后,`1.84.0` 没有再动这份快照。该结论来自产物级静态比对:官方日志的 8 条覆盖不到数据面,只有逐项比对才能排除"日志没写但数据动了"。
14
+
15
+ - 实时目录漂移与快照同步(`?view=client` 101 条,`catalogVersion` `e6fe850d…`;默认视图 93 条):`KNOWN_PLANS` 92 → 93、`KNOWN_EFFORTS` 64 → 65,两处新增的都是 `reflection/beam:free`(免费预览、Go 档、仅文本、五档 `low`/`medium`/`high`/`xhigh`/`max`);`KNOWN_NON_ZDR_MODELS` 24 → 25,撤出 `meituan/LongCat-2.0:free`、补入 `meituan/LongCat-2.0` 与 `reflection/beam:free`(目录对付费 id 判 `zdr:false`、对被撤下的 `:free` 行改判 `zdr:true`,属同一位置的改名而非覆盖变化);`KNOWN_DEALS` 6 → 7,新增 `reflection/beam:free` 免费促销。`KNOWN_IMAGE_MODELS` 保持 70 条不变——新模型只支持文本输入。目录 `catalogVersion` 由 `d6177bab…` 推进到 `e6fe850d…`,与产物内嵌快照之间的差距继续拉大,再次印证第七章第 9 条:内嵌快照发布后约两天即过期,同步一律以实时目录为准。
16
+
17
+ - 三张 id 表与 ZDR 例外表的收录口径本轮写进[《决策记录》第七章第 12 条](docs/决策记录.md#七上游快照同步边界):`KNOWN_PLANS`/`KNOWN_EFFORTS`/`KNOWN_IMAGE_MODELS` 只收**默认目录视图**(`GET /provider/v1/models`,本轮 93 个 id)里的 id,该视图不返回 `hidden` 字段、也没有隐藏行,8 个 `hidden:true` 行只存在于 `?view=client`,故天然不进入这三张表;`KNOWN_NON_ZDR_MODELS` 收 **artifact 排除集与 `?view=client` 目录 `zdr:false` 行的并集**(单源不完整,`reflection/beam:free` 就是只有目录侧提名的一例)。本轮 `reflection/beam:free` 在默认视图可见,因此 92 → 93 与口径自洽,不需要任何"CLI 注册表未认的行例外收录"式说明。
18
+
19
+ - 价格表与输出上限表重新生成:`deepseek-v4.1-flash` 与 `glm-5.3-flash` 两行的每月额度由 Go $10/GOAT $100/Pro $100 下调为 **Go $10/GOAT $80/Pro $90**,两行的费率不变(前者 $0.15/$0.6/$0.003,后者 $0.15/$0.5/$0.03)。`MODEL_OUTPUT_TOKEN_LIMITS` 新增 3 行(`deepseek/deepseek-v4-flash-fast` 384 000、`Qwen/Qwen3.8-Max-0902` 131 072、`tencent/hy3-paid` 128 000),写入数 75 → **78**。`reflection/beam:free` 按页面发布的字面零价,不进价格表。
20
+
21
+ - 复核**无变化**的项:峰谷计价仍是 5 个 DeepSeek 模型、窗口 `01–04 & 06–10 UTC, Mon–Fri` 与本地常量逐值一致;订阅条目与每月额度零变化;`reflection/beam:free` 支持图像输入这一点未被目录宣告,图像能力表保持 70 条;effort 词汇表仍是 `off|low|medium|high|xhigh|max`。
22
+
23
+ - **证据边界**:本轮 `scripts/` 巡检工具的状态为 `incomplete`、`readyToApply=false`、`protocol=not-verified`;唯一一条 error 是 `reflection/beam:free: absent from CLI registry`——该模型只出现在两个目录视图里,`1.84.0` 产物注册表完全不认它,因此 `KNOWN_EFFORTS` 与 `KNOWN_NON_ZDR_MODELS` 里这一行属**目录侧单源证据**(与 `1.82.4` 时 `stealth/cat-jam:free` 的处境相同)。16 个数据源全部返回 HTTP 200。**未做**:真实账号登录、付费生成、浏览器渲染验证,也没有在真实账号上验证价格与订阅额度的页面读数;`?view=client` 与定价页均为公开数据采集。
24
+
25
+ ## [0.14.1] - 2026-10-18
26
+
27
+ ### Changed
28
+
29
+ - **DSH 兼容矩阵反转为两引擎向下兼容 `0.2.0-rc.2 || 0.2.1-rc.2`**,撤销 `[0.14.0]` 的单一引擎收敛。官方 npm `latest` 至今仍指向 `0.2.0-rc.2`(2026-10-18 复核),`0.2.1-rc.2` 挂在 `next` 上,而宿主准入判定只读 `engines.dsh`——只声明 rc.2 会让走官方默认安装路径的用户被直接拒绝(插件行被预检禁用、作为 bundle 被静默跳过、`dsh plugin add` 在 pnpm 之前就拒绝,机制见[《决策记录》第八章第12条](docs/决策记录.md#八dsh-引擎兼容范围与验证边界))。`engines.dsh`、`dsh.compatibility.dsh`、15 个 `@deepseek-ai/dsh-*` 对等依赖与 19 个开发依赖同批回到析取式,`dsh.compatibility.dshReleases` 回到**两条记录**;两条记录**都是 RC**,alpha(早期测试版)通道不保留——自 `0.2.1-alpha.5` 之后没有新的已验证版本。每个分支仍必须是裸版本号,`0.2.0-rc.1`/`0.2.1-rc.1`/`0.3.0-alpha.1` 等相邻未验证版本继续被拒。`react ^18.2.0` 不变;`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 由 rc.2 轮次的单分支 `~4.0.5`/`~3.18.5` 恢复为**按引擎配对的析取式** `~4.0.4 || ~4.0.5`/`~3.18.4 || ~3.18.5`——两代引擎的配对不同(`0.2.0-rc.2` 随 cordis `~4.0.4`/schemastery `~3.18.4` 发布,`0.2.1-rc.2` 随正式版 `4.0.5`/`3.18.5` 发布),单分支解析不到旧引擎的配对,只适用于单引擎矩阵。反转理由、判据与 PR #93 的评审结论见[《决策记录》第八章第14条](docs/决策记录.md#八dsh-引擎兼容范围与验证边界)。
30
+
31
+ - **网页搜索的选择写入同时支持两种引擎形状。**rc.2 继续走官方易变写入面(`ctx.settings.update()`/`replace()`,写入 profile 的易变字段由 loader 原地提交,且不夺取他人显式选择、写后读回校验);pre-rc.2 引擎(含 `0.2.0-rc.2`)既没有该易变引用也没有 `web` 的设置行,选择是实例自有字段 `searchProviderId`,改为直写该字段,沿用既有「记住被顶掉的后端、只归还自己拿走的」语义——该值只存在于内存,随实例结束失效,且看不到用户层的他人显式选择。形状按每次观察解析,一个构建同时服务两个引擎;`tests/web-search.test.ts` 新增旧形状用例。
32
+
33
+ - **升级审计工具同步双引擎。**`.agents/skills/dsh-release-upgrade/` 的 `SKILL.md` 维护策略改为「官方默认安装版本(npm `latest`)必须可用 + 通道最新,不保留 alpha 通道」,与声明面口径一致;`scripts/collect.py` 的隐式 baseline 由「`package.json` 必须恰好一条 `dshReleases`」改为取其中 **semver 最大**者(升级审计总是从最新已验证引擎出发),并把实际选用值打到 stderr,`--baseline` 显式覆盖优先、空记录仍退出 2。技能单测 5/5 通过(新增 4 项:单条/两条取值、semver 排序、空记录报错、CLI 空清单退出 2)。
34
+
35
+ - 文档同步:两份 README、[对接说明](docs/对接说明.md)、[引擎兼容](docs/agent-reference/engine-compatibility.md)、[账号与重试](docs/agent-reference/accounts-and-retry.md)、[模型成本与性能分析](docs/模型成本与性能分析.md)、[决策记录](docs/决策记录.md)与[提交历史](docs/提交历史.md)。**验证**(本机静态与合成依赖):`npm run test:engine` 退出码 0,矩阵 **2/2**——关键行 `host compatibility preflight admits this plugin on dsh 0.2.0-rc.2` 与 `... on dsh 0.2.1-rc.2`,末行 `engine-load smoke passed on all 2 declared-compatible engines (0.2.0-rc.2, 0.2.1-rc.2)`;`npm run test:install` 退出码 0(pnpm `10.34.5`),输出 `isolated install passed with pnpm 10.34.5 (dsh 0.2.0-rc.2)` 与 `(dsh 0.2.1-rc.2)`。Cordis/Schemastery 改回析取式后**已重跑**,两次矩阵同为 **2/2**——一个引擎全绿不算通过,沿[《决策记录》第八章第2条](docs/决策记录.md#八dsh-引擎兼容范围与验证边界)。**未做**:真实浏览器渲染、真实账号登录、付费生成、跨平台与长期稳定性;真实引擎内的网络响应与凭据为合成依赖。**真实旧引擎(`0.2.0-rc.2`)上的端到端搜索选择写入未验证**——`test:engine` 只证明插件在该引擎上加载并通过宿主准入,不驱动 `webSearch` 开关,旧形状由单元级替身覆盖。
36
+
37
+ ### Fixed
38
+
39
+ - 修掉 `[0.14.0]` 引入的「按官方默认方式安装 dsh 的用户装不上本插件」:`0.2.0-rc.2` 仍是 npm `latest`(官方默认安装路径会跑的版本),本版本重新声明它。
40
+
41
+ - 补齐 `[0.14.0]` 漏改的两处引擎记录表述:[客户端与设置](docs/agent-reference/client-and-settings.md)与[仓库地图](docs/agent-reference/repository-map.md)此前写的 `dshReleases` 条数与当时的单引擎声明不一致,现按两条 RC 记录写明。
42
+
43
+ ## [0.14.0] - 2026-10-18
44
+
45
+ ### Added
46
+
47
+ - 问题 #74:新增**单轮输出上限**配置 `maxOutputTokens` 与网页设置页字段(「模型与上下文」分组,紧随自定义上下文长度)。不填(默认)=沿用既有推导(模型公布或运行时学到的输出上限、网关总上限三者取最小),行为完全不变;填正整数=与该推导值取最小,永远不会突破模型上限或网关总上限 131 072;填 **`0`=请求中不发送 `max_tokens` 字段**,把单轮输出规模交回平台自行限制——这是报告者要的「不再被硬切」,因为该字段本身是被动硬限制,单纯调大数值只能把断点往后挪。省略仅对**接受缺省该字段的传输**生效:CLI(`/alpha/generate`)与 OpenAI(`/provider/v1/chat/completions`);Claude 走的 Anthropic Messages 端点必填该字段(缺失会被上游以 `Invalid input: expected number, received undefined at max_tokens` 直接拒绝),因此那条链路仍按推导值发送,填 `0` 对它无效。宿主显式给定的每次调用预算(如会话标题的 64 词元)同样优先于此设置。`0` 与正整数共用一个数值字段,边界由 Host 与浏览器设置页共用的纯模块 `src/output-budget.ts` 判定;正数取值还会同步压低选择器上宣告的 `defaultMaxTokens`。字段留空=清除该配置(回到推导默认),而不是写入 0。输入接受 `64K` 简写。设置页的上下文长度与输出预算两个 token 计数字段合并为同一个 `tokenCountField`(简写文法与边界检查只有一份)。
48
+
49
+ ### Fixed
50
+
51
+ - 问题 #74(第二半):**7 处裸 `error instanceof Error ? error.message : String(error)` 收进统一的可读错误兜底函数**。抛出的值不是 `Error` 时 `String()` 会退化成 `[object Object]`,信息全丢;0.12.2 只在浏览器侧的 RPC 边界修了这一点,Host 侧的服务端路径(通用 HTTP 用量查询、`/commandcode` 命令、账号卡片用量读取、浏览器登录流程、终端设置页注册失败、网页搜索的请求与凭据解析)仍是裸写法。支撑 `readableErrorText()` 的实现移入 Host 与客户端共用的 `src/readable-error.ts`,Host 侧七个调用点与浏览器侧六个调用点(分布在五个文件)直接导入它(不再保留中间再导出模块),两侧不会再各自演化。
52
+
53
+ - 问题 #90:**英文界面残留中文文案**。`en` 字典补齐 14 个 `write*` 设置写入提示与 `usagePeriodUnknown` 的英文翻译;`settingsWriteNotice()` 里硬编码的「(原引用:…)」改走本地化(新增 `writeCredentialRefNote` 键,中文输出与改动前逐字一致)。合并前另补齐**同一设置页界面层**漏掉的 9 处硬编码中文——`section.tsx` 的「若登录页面未自动打开,请点击下方链接。」「打开浏览器登录」「重试保存名称」「接受已有名称」「取消开通」「重试清理」,以及账号恢复行里硬编码的全角冒号分隔符;这些文案与 `settingsWriteNotice` 同页,此前在英文界面下仍显示中文。新增 7 个键(`accountAddLinkHint`、`accountAddOpenLink`、`accountAddCancel`、`accountNameSaveRetry`、`accountNameAcceptExisting`、`accountEnrollmentCleanupRetry`、`accountRefSeparator`),联合类型 / 中文 / 英文三处同步,全角冒号在英文侧改为半角加空格。新增**英文侧 CJK 守护测试**:`en` 字典出现任何中文字符即失败;`PANEL_COPY_EN` 只放行 `analytics-copy.ts` 声明的「错误、缺失与读取状态各语言统一中文」既定约定,并断言该约定仍在生效;三组变异测试确认守护会真正失败。`lib/client.js` 与 sourcemap 同步重建。**边界**:账号开通与恢复流程的状态消息此时仍由宿主产出的中文字面量构成,英文界面下那几行仍显示中文,已由下一条修复。
54
+
55
+ - **英文界面残留中文(账号开通状态消息)**。账号开通的状态消息此前是宿主产出的中文字面量,经 `EnrollmentState.message` 直传页面后原样显示,英文界面下三处状态行(新增账号面板的状态行、待处理恢复记录行、`cleanup-needed` 行)与传输失败提示都仍是中文。现改为**语义键**:`EnrollmentState.message` 换成 `messageKey: EnrollmentMessageKey`,共 22 个键(宿主 15 个、页面控制器 7 个),键表与枚举校验单点放在 `src/enrollment-wire.ts`(`parseEnrollmentState` 拒绝自由文本),中英文案集中进设置页词典、渲染时才按界面语言取,两侧不会再各写一份;「正在创建账号」与「收尾未完成,请重试」这两条本来两侧各有独立文案的状态也就此合并为一处。**为什么不是按 `phase` 渲染**:同一 `phase` 对应多种语义——`naming` 既有「正在保存名称」也有「名称保存失败」,`cleanup-needed` 既有「收尾未完成」也有「清理未完成」,`cancelled` 既有「已取消」也有「已取消并清理」,按阶段渲染无法区分。**验证**:新增 wire 拒绝自由文本/非枚举键的用例,并把原按中文子串断言的三处改为断言语义键;英文侧 CJK 守护测试覆盖新键(变异测试确认把任一 `en` 值改回中文即变红)。**边界**:宿主侧约 20 处 `throw new Error('…')` 的中文不在本次改动内——它们不上屏,客户端在所有失败路径都用自己这一侧的键覆盖;登录流程(`src/login.ts`)的状态消息仍由宿主产出中文,同样未包含。
56
+
57
+ ### Changed
58
+
59
+ - DSH 兼容矩阵收敛为**单一引擎 `0.2.1-rc.2`**:RC(候选发布版)通道由 `0.2.0-rc.2` 替换为 `0.2.1-rc.2`(2026-10-17 发布),alpha 通道自 `0.2.1-alpha.5` 之后没有新的已验证版本,随之退出支持矩阵。`dsh.compatibility.dshReleases` 只剩一条,`dsh.compatibility.dsh`、`engines.dsh`、15 个对等依赖与 19 个开发依赖的 `@deepseek-ai/dsh-*` 全部为裸版本 `0.2.1-rc.2`;`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 由双通道时代的析取收紧为随该引擎配对的正式版范围 `~4.0.5`/`~3.18.5`(`maxSatisfying` 实测命中,不放行已无对应引擎的 `4.0.5-alpha.2`)。`tests/package.test.ts` 的「恰好两条通道」断言改为单版本契约。
60
+
61
+ - **适配本轮唯一的运行代码破坏性变更**:`@deepseek-ai/dsh-web` 把搜索/抓取的选择契约改为易变引用(`WebRuntimeConfig.searchProvider`/`fetchProvider` 变为 `Volatile`),构造函数 `config` 由可选变必填,并删除了插件此前直写的私有实例字段 `searchProviderId`/`fetchProviderId`。旧写法在 rc.2 上会写到一个没有读取方的属性:不抛错、功能静默失效。本轮改按 rc.2 官方易变写入路径实现运行时选择(写入 profile 的易变字段,由 loader 原地提交进运行中的 `web` 条目,不重挂载),并同步改造测试夹具。该字段与官方「Web 搜索」卡片的新版 API 开关是同一个:**用户层已存在他人的显式选择时插件不夺取**,只在无人 pin 时接管,并把接管状态暴露出来(开关的中英双语提示,外加一行宿主日志说明「官方页面已选定其他后端,本提供方保持已注册但未被选中」)。除该接缝外,其余 16 个直接依赖包与 9 个 vendor 包源码零差异(`cordis` 4.0.4 → 4.0.5、`schemastery` 3.18.4 → 3.18.5 逐文件 diff 仅版本行),exports 漂移(7 个包新增 `./locale/*.json` 并删除 `./invariant`,`typert-protocol` 新增 `./remote-info`)插件均未引用。rc.2 首发的官方网页搜索后端与模型标签插槽不接入(后者是 `single` 单占类插槽,官方已占用,重复注册会抛错)。
62
+
63
+ - DSH 升级技能、引擎兼容参考、[对接说明](docs/对接说明.md)、[审计清单](docs/DSH升级审计清单.md)与两份 README 同步到单引擎矩阵;保留历史条目作为历史证据,不删除既往诊断。本轮验证**全部通过**(本机静态与合成依赖验证):`npm run typecheck` 退出码 0;`npm test` 退出码 0、**1038 项全绿 / 0 失败**(本轮开工前基线为 1009 项、其中 2 项失败,失败的正是网页搜索选择缺陷;与 `origin/main` 的 11 个提交合并后复跑);`npm run build` 退出码 0(重建 `lib/index.js`、`lib/index.d.ts`、`lib/client.js`);`npm run test:pack` 退出码 0(发布入口 18 个文件,版本 `0.14.0`);`npm run test:engine` 退出码 0,矩阵 1/1 落在 `dsh 0.2.1-rc.2` 且宿主准入 preflight 通过;`npm run test:install` 退出码 0(pnpm `10.34.5`,`engine matrix verified: 0.2.1-rc.2`)。**未做**:真实浏览器渲染、官方网页搜索卡片与插件开关的并发双写、真实账号登录、付费生成、跨平台与长期稳定性;引擎内网络响应与凭据均为合成依赖。两条决策见[《决策记录》第八章第13条](docs/决策记录.md#八dsh-引擎兼容范围与验证边界)。
64
+
65
+ - DSH 兼容范围的 alpha(早期测试版)通道由 `0.2.1-alpha.4` 替换为 `0.2.1-alpha.5`,保留 `0.2.0-rc.2`(候选发布版)。兼容声明、对等/开发依赖与锁文件同步;29 个宿主包采用官方发布元数据精准更新,第三方及 10 个平台可选依赖条目保持原值,锁文件根版本号对齐现有 `0.13.4`。完整源码与发布清单比对未发现插件运行代码或测试夹具必须改动的契约;新增模型名称插槽和官方账号通知不接入。类型检查、1009 项测试、构建、双引擎及双版本隔离安装均通过,隔离 `npm ci --dry-run`(仅检查安装)退出码为 0。浏览器与真实提供商未实测。详见[对接说明](docs/对接说明.md)及[审计清单](docs/DSH升级审计清单.md)。该轮次随后被上面的单引擎收敛取代:`0.2.1-alpha.5` 已退出兼容矩阵,其验证结论作为历史证据保留。
66
+
7
67
  ## [0.13.4] - 2026-10-16
8
68
 
9
69
  ### Added
package/README.md CHANGED
@@ -37,15 +37,19 @@ See [Screenshots](#screenshots) below for what the UI looks like.
37
37
 
38
38
  ## Install
39
39
 
40
- This release supports **dsh 0.2.0-rc.2 (RC) and dsh 0.2.1-alpha.4 (alpha)**.
41
- The compatibility policy keeps the latest verified official release candidate
42
- and alpha release, one per channel. These match npm's `latest` and `alpha` tags
43
- as checked on 2026-10-15. The peer range and compatibility records name only
44
- these two exact versions. Note that **dsh 0.2.1-alpha.4 reads `engines.dsh`
45
- alone** when it decides whether a plugin is compatible; the dsh peer ranges are
46
- consulted only when that field is absent, so a build that declares the wrong
47
- `engines.dsh` is refused outright. A profile on `0.2.1-alpha.3` must upgrade its engine
48
- before installing this build:
40
+ This release supports **dsh 0.2.0-rc.2 and 0.2.1-rc.2** — two verified engines, both on the RC
41
+ (release-candidate) channel. `0.2.0-rc.2` is what npm `latest` still points at (re-checked
42
+ 2026-10-18), so it is the engine an official default install runs; `0.2.1-rc.2` ships on the `next`
43
+ tag. The peer range and the compatibility records name both as exact versions, and no alpha release
44
+ is retained: nothing has been verified since `0.2.1-alpha.5`. `@deepseek-ai/cordis` /
45
+ `@deepseek-ai/schemastery` carry the paired disjunction `~4.0.4 || ~4.0.5` / `~3.18.4 || ~3.18.5`,
46
+ because the two engines pair differently (`0.2.0-rc.2` needs cordis `~4.0.4` and schemastery
47
+ `~3.18.4`, `0.2.1-rc.2` ships `4.0.5` / `3.18.5`) and one branch cannot resolve both. Note that
48
+ **dsh 0.2.1-rc.2 reads
49
+ `engines.dsh` alone** when it decides whether a plugin is compatible; the dsh peer ranges are
50
+ consulted only when that field is absent, so a build that declares the wrong `engines.dsh` is refused
51
+ outright there — while on `0.2.0-rc.2` the peer ranges are what decides. A profile on
52
+ `0.2.1-alpha.5` or any earlier release must upgrade its engine before installing this build:
49
53
 
50
54
  ```sh
51
55
  dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
@@ -72,14 +76,14 @@ dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.15
72
76
 
73
77
  The same applies to every profile you install into, including the terminal UI below.
74
78
 
75
- Fresh pnpm 10 marketplace generations are supported directly. Every Harness package is declared by the plugin as a Host peer, so the active dsh profile remains their owner; do not add a separate dependency for any of them. The `@deepseek-ai/dsh-*` packages share one exact-version disjunction; `@deepseek-ai/cordis` and `@deepseek-ai/schemastery` carry the paired ranges `~4.0.4 || ~4.0.5-alpha.1` and `~3.18.4 || ~3.18.5-alpha.1` instead, because publishing an engine rewrites both to the version that shipped alongside it. Upstream removed the `@deepseek-ai/dsh-invariants` package in `0.2.1-alpha.1`, so the plugin no longer declares it.
79
+ Fresh pnpm 10 marketplace generations are supported directly. Every Harness package is declared by the plugin as a Host peer, so the active dsh profile remains their owner; do not add a separate dependency for any of them. The `@deepseek-ai/dsh-*` packages all declare the same exact-version disjunction `0.2.0-rc.2 || 0.2.1-rc.2`; `@deepseek-ai/cordis` and `@deepseek-ai/schemastery` carry the paired disjunctions `~4.0.4 || ~4.0.5` and `~3.18.4 || ~3.18.5` instead, because publishing an engine rewrites both to the version that shipped alongside it and the two retained engines were published against different pairs. Upstream removed the `@deepseek-ai/dsh-invariants` package in `0.2.1-alpha.1`, so the plugin no longer declares it.
76
80
 
77
81
  ## Updating
78
82
 
79
83
  Update with the same tag you installed with:
80
84
 
81
85
  ```sh
82
- dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2 or 0.2.1-alpha.4
86
+ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2 / 0.2.1-rc.2
83
87
  dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@0.9.1 # older dsh (0.5.0 line, unmaintained)
84
88
  ```
85
89
 
@@ -158,7 +162,7 @@ cmd login # writes ~/.commandcode/auth.json
158
162
  cwd: !!js process.cwd()
159
163
  ```
160
164
 
161
- **Engine version.** The plugin is maintained against two engines: **dsh 0.2.0-rc.2** and **dsh 0.2.1-alpha.4**. Its `@deepseek-ai/dsh-*` peers use `0.2.0-rc.2 || 0.2.1-alpha.4`, while `@deepseek-ai/cordis` and `@deepseek-ai/schemastery` retain their engine-paired ranges `~4.0.4 || ~4.0.5-alpha.1` and `~3.18.4 || ~3.18.5-alpha.1` (alpha.4 pairs the same Cordis and Schemastery versions as alpha.3, so those ranges do not move). Compatibility records contain exactly these two engines. Each channel upgrade replaces its previous version after verification; it does not accumulate supported releases or admit unverified neighbours. Both `npm run test:engine` and `npm run test:install` check each retained version, and `test:engine` also calls that engine's own `evaluatePluginCompatibility` on the published manifest — from alpha.4 on, that verdict reads `engines.dsh` alone, so a wrong engine declaration fails there instead of only at install time. See [Install](#install) for older engines.
165
+ **Engine version.** The plugin is maintained against **two verified engines: dsh `0.2.0-rc.2` and dsh `0.2.1-rc.2`**. Its `@deepseek-ai/dsh-*` peers use the exact-version disjunction `0.2.0-rc.2 || 0.2.1-rc.2`, while `@deepseek-ai/cordis` and `@deepseek-ai/schemastery` use the paired disjunctions `~4.0.4 || ~4.0.5` and `~3.18.4 || ~3.18.5` — one branch per engine, because `0.2.0-rc.2` was published against cordis `~4.0.4` / schemastery `~3.18.4` and `0.2.1-rc.2` against the stable `4.0.5` / `3.18.5`. Compatibility records contain exactly these two engines — both RC, with no alpha channel, because nothing has been verified since `0.2.1-alpha.5`. `0.2.0-rc.2` is retained deliberately even though it is the older of the two: it is still what npm `latest` resolves to, so a build that dropped it would be refused by the host's `engines.dsh` admission on every default install. Every declared branch must stay a bare version, so unverified neighbours (`0.2.0-rc.1`, `0.2.1-rc.1`, `0.3.0-alpha.1`) are still rejected. Both `npm run test:engine` and `npm run test:install` verify every declared engine in turn rather than one of them, and `test:engine` also calls that engine's own `evaluatePluginCompatibility` on the published manifest — from alpha.4 on, that verdict reads `engines.dsh` alone, so a wrong engine declaration fails there instead of only at install time. See [Install](#install) for older engines.
162
166
 
163
167
  ## Usage dashboard
164
168
 
@@ -224,6 +228,7 @@ llm-commandcode:
224
228
  # contextWindow: 131072 # optional: custom context length (tokens) for every model; unset follows the catalog
225
229
  # contextWindowOverrides: # optional: per-model custom context lengths; wins over contextWindow
226
230
  # deepseek/deepseek-v4-pro: 65536
231
+ # maxOutputTokens: 65536 # optional: per-turn output limit; 0 sends no max_tokens, unset follows the model ceiling
227
232
  ```
228
233
 
229
234
  ### Custom context length
@@ -235,6 +240,18 @@ The context window the harness meters against — and when it compacts — norma
235
240
  - Values must be whole token counts in 1 – 10,000,000. The page blocks invalid input; config-file non-numbers, fractions and out-of-range values fail schema validation rather than silently falling back. Only programmatic calls that bypass the schema discard invalid values and fall through valid per-model override → valid global value → catalog. A model the catalog does not list keeps its "no context advertised" shape.
236
241
  - The inputs accept compact shorthand in binary units: `32K` = 32,768 and `1M` = 1,048,576 (so `128K`/`256K` write the exact power-of-two windows 131,072/262,144); the stored value is always a plain number. The per-model override's model dropdown lists only the models kept by the **visible-models allowlist** (the whole catalog while showing all), and the rows carry "Model / Context length" column heads.
237
242
 
243
+ ### Per-turn output limit
244
+
245
+ Every request carries `max_tokens`, resolved as the smallest of the model's published output ceiling, the ceiling learned at runtime, and the gateway-wide cap of 131,072. That field is a **passive hard limit**: the platform cuts the answer when it is reached, and the model receives no "nearly out of room" signal. The **Output limit per turn** field (**Settings → Command Code → Models & context**, or the `maxOutputTokens` config key) offers three choices:
246
+
247
+ - **Empty (default)**: exactly the derivation above, so nothing changes.
248
+ - **A positive number**: taken as a cap alongside that derived value. It can only **tighten** — it never exceeds the model ceiling or the gateway's `131072`, and out-of-range input fails config validation. Shorthand like `64K` is accepted.
249
+ - **`0`**: sends **no** `max_tokens` field at all, leaving the per-turn output size to the platform. This is the fix for answers being cut off mid-sentence, because while the field is present the cut happens at some point no matter how large the figure is.
250
+
251
+ `0` only takes effect on the two transports that accept an absent field: the CLI (`/alpha/generate`) and OpenAI (`/provider/v1/chat/completions`). The Anthropic Messages endpoint Claude uses **requires** the field — omitting it is refused upstream with `Invalid input: expected number, received undefined at max_tokens` — so that route keeps sending its derived budget and `0` has no effect for Claude. Auxiliary calls whose budget the host fixes itself (the session title is pinned at 64 tokens) also outrank this setting and are not unbound by `0`.
252
+
253
+ An empty field **clears** the setting back to the derived default; it is not the same as writing `0`.
254
+
238
255
  ### Repeated image cache misses
239
256
 
240
257
  On the CLI transport (`/alpha/generate`), a new image can make Command Code's reported prompt cache retreat to the first historical image even when every previous request message is byte-identical. The opt-in **Stop replaying images already seen** setting (`offloadSeenImagesForCache`) asks dsh's durable `image/offload` surface to replace an older image with a stable text placeholder after this same model has answered with it. The next request keeps its new image; later text can be cached without replaying old pixels. The default is off because the model cannot inspect an offloaded image again unless it rereads the source file or the user reattaches it. This mitigates the observed cost spike; it does not repair Command Code's underlying multimodal cache behavior. [Live evidence and limits](docs/issue-64-cache-review.md).
@@ -243,10 +260,10 @@ On the CLI transport (`/alpha/generate`), a new image can make Command Code's re
243
260
 
244
261
  When your deployment's dsh shell mounts the web capability (`@deepseek-ai/dsh-web` + `@deepseek-ai/dsh-tool-web`), the model's `web_search` tool is served by this plugin's `commandcode` search provider — it calls the Command Code Provider API's `/alpha/web-search` endpoint with the **same API key and base URL** as chat. You do not configure a separate search key, endpoint, or model.
245
262
 
246
- **On by default.** The plugin's **Settings → Command Code** page has a *"Serve dsh web search with Command Code"* toggle (`webSearch`, default on). When on, the plugin selects `commandcode` as the active search backend automatically; turn it off to hand the selection back to whichever backend was there before (a sibling search plugin such as modsearch keeps working — it is never forced back to dsh's shipped DeepSeek search). The toggle takes effect on the next search — no restart needed.
263
+ **On by default.** The plugin's **Settings → Command Code** page has a *"Serve dsh web search with Command Code"* toggle (`webSearch`, default on). When on, the plugin selects `commandcode` as the active search backend automatically; turn it off to hand the selection back to whichever backend was there before (a sibling search plugin such as modsearch keeps working — it is never forced back to dsh's shipped DeepSeek search). The toggle takes effect on the next search — no restart needed. On dsh 0.2.1-rc.2 this uses the official volatile-settings write path, so the change is committed into the running `web` entry in place rather than by remounting it; on 0.2.0-rc.2 the engine instead keeps its selection in an instance field with no settings surface, which the plugin assigns directly (memory-only, gone with the instance). **When the official Web search settings already pin another backend, this toggle has no effect** — that explicit choice wins; on 0.2.0-rc.2 there is no such settings row, so no pin can be seen and the toggle always takes over.
247
264
 
248
265
  - The provider registers as `commandcode` on `ctx.web` only when the web service is present; without it this stays a chat-only plugin.
249
- - The toggle works by selecting `commandcode` in the web seam at boot and on every settings change, remembering the backend it displaced; turning the toggle off (or unloading the plugin) restores that backend. If you'd rather pin it durably, set `searchProvider: commandcode` (or `$DSH_WEB_SEARCH_PROVIDER=commandcode`); that remains effective even if this plugin's runtime selection is unavailable.
266
+ - The toggle selects `commandcode` through the engine's volatile-settings write path on `0.2.1-rc.2` — the same `web.searchProvider` field the official **Web search** card's *Use the new Web Search API* switch writes (that switch also writes `fetchProvider`). The plugin **defers to an explicit choice already present in the user layer**: when the Web search settings pin another backend, it keeps `commandcode` registered but unselected and reports that state (the toggle's hint, plus a host log line) instead of overwriting it; it takes over only while nothing else pins the field, remembers the backend it displaced, and restores it when the toggle goes off or the plugin unloads. On `0.2.0-rc.2` (and the alphas) the engine has neither that reference nor a `web` settings row, so the selection is its own `searchProviderId` field: the plugin assigns it in memory under the same "remember what we displaced, hand back only what we took" rule, and no other writer's pin is observable there. If you would rather pin the choice durably, set `searchProvider: commandcode` (or `$DSH_WEB_SEARCH_PROVIDER=commandcode`); that keeps working even if the runtime selection cannot be committed.
250
267
  - `numResults` from the dsh tool is clamped to the Command Code range (1–10, default 5); results map to the dsh `WebSearchSource` shape (`url`/`title`/`snippet`).
251
268
 
252
269
  > This reuses the Command Code Provider API directly (like the official CLI's built-in `web_search`), so it is distinct from a DeepSeek-native search backend.
package/README.zh-CN.md CHANGED
@@ -35,7 +35,7 @@
35
35
 
36
36
  ## 安装
37
37
 
38
- 本版本**只支持 dsh 0.2.0-rc.2(RC,候选发布版)与 dsh 0.2.1-alpha.4(alpha,早期测试版)**。维护策略为每条通道只保留官方最新且经过验证的一个版本;2026-10-15 核对时分别对应 npm 的 `latest` 与 `alpha` 发布标签。peer(对等依赖)范围和兼容记录只列这两个精确版本。注意 **dsh 0.2.1-alpha.4 判定插件兼容性时只读 `engines.dsh`**,dsh 对等依赖范围仅在该字段缺失时才被参考,因此 `engines.dsh` 声明错误会被直接拒绝。使用 `0.2.1-alpha.3` 及更早的配置需先升级引擎,再安装本构建:
38
+ 本版本支持 **dsh 0.2.0-rc.2 与 0.2.1-rc.2 两个已验证引擎**,两者都在 RC(候选发布版)通道上。`0.2.0-rc.2` 至今仍是 npm `latest` 指向的版本(2026-10-18 复核),也就是官方默认安装会跑的那一版;`0.2.1-rc.2` 挂在 `next` 标签上。peer(对等依赖)范围与兼容记录把两者都写成精确版本;alpha(早期测试版)通道不保留——自 `0.2.1-alpha.5` 之后没有新的已验证版本。`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 写成按引擎配对的析取式 `~4.0.4 || ~4.0.5`/`~3.18.4 || ~3.18.5`——两代引擎的配对不同(`0.2.0-rc.2` 需要 cordis `~4.0.4` 与 schemastery `~3.18.4`,`0.2.1-rc.2` 随 `4.0.5`/`3.18.5` 发布),单分支覆盖不了两代。注意 **dsh 0.2.1-rc.2 判定插件兼容性时只读 `engines.dsh`**,dsh 对等依赖范围仅在该字段缺失时才被参考,因此 `engines.dsh` 声明错误会被它直接拒绝;而在 `0.2.0-rc.2` 上起作用的正是对等依赖范围。使用 `0.2.1-alpha.5` 或更早版本的配置需先升级引擎,再安装本构建:
39
39
 
40
40
  ```sh
41
41
  dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
@@ -57,14 +57,14 @@ dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.15
57
57
 
58
58
  这一点对每个 profile 都成立,包括下面的终端界面。
59
59
 
60
- 插件可直接在 pnpm 10 的全新插件市场 generation 中安装。所有 Harness 包都由插件声明为 Host peer,由当前 dsh profile 统一管理,不要另行添加 dependency。其中 `@deepseek-ai/dsh-*` 共用同一个精确版本析取;`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 则分别写成配对范围 `~4.0.4 || ~4.0.5-alpha.1` 与 `~3.18.4 || ~3.18.5-alpha.1`——上游每次发引擎都会把这两个包重写成随引擎发布的版本。上游曾在 `0.2.1-alpha.1` 删除 `@deepseek-ai/dsh-invariants` 这个包,因此插件不再声明它。
60
+ 插件可直接在 pnpm 10 的全新插件市场 generation 中安装。所有 Harness 包都由插件声明为 Host peer,由当前 dsh profile 统一管理,不要另行添加 dependency。其中 `@deepseek-ai/dsh-*` 全部声明同一个精确版本析取式 `0.2.0-rc.2 || 0.2.1-rc.2`;`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 则写成按引擎配对的析取式 `~4.0.4 || ~4.0.5` 与 `~3.18.4 || ~3.18.5`——上游每次发引擎都会把这两个包重写成随引擎发布的版本,而两个保留引擎发布时的配对不同,单分支覆盖不了两代。上游曾在 `0.2.1-alpha.1` 删除 `@deepseek-ai/dsh-invariants` 这个包,因此插件不再声明它。
61
61
 
62
62
  ## 更新
63
63
 
64
64
  用与安装时相同的 tag 更新:
65
65
 
66
66
  ```sh
67
- dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2 或 0.2.1-alpha.4
67
+ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2 / 0.2.1-rc.2
68
68
  dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@0.9.1 # 更早的 dsh(0.5.0 线,不再维护)
69
69
  ```
70
70
 
@@ -143,7 +143,7 @@ cmd login # 写入 ~/.commandcode/auth.json
143
143
  cwd: !!js process.cwd()
144
144
  ```
145
145
 
146
- **引擎版本要求。** 插件维护两个引擎:**dsh 0.2.0-rc.2** 与 **dsh 0.2.1-alpha.4**。`@deepseek-ai/dsh-*` 的 peer(对等依赖)为 `0.2.0-rc.2 || 0.2.1-alpha.4`;`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 仍使用随引擎配对的范围 `~4.0.4 || ~4.0.5-alpha.1` 与 `~3.18.4 || ~3.18.5-alpha.1`(alpha.4 与 alpha.3 配对同两个版本,因此范围不动)。兼容记录恰好包含两个版本,同通道升级验证后替换旧版,不累加已支持版本、不放行未验证的邻近版本。`npm run test:engine` 与 `npm run test:install` 均逐版本验证,前者还会用该引擎自己的 `evaluatePluginCompatibility` 对发布清单求值——自 alpha.4 起该判定只读 `engines.dsh`,因此引擎声明写错会在这一步失败,而不是拖到安装时才暴露。旧引擎安装说明见[安装](#安装)。
146
+ **引擎版本要求。** 插件维护**两个已验证引擎:dsh `0.2.0-rc.2` 与 dsh `0.2.1-rc.2`**。`@deepseek-ai/dsh-*` 的 peer(对等依赖)为精确版本析取式 `0.2.0-rc.2 || 0.2.1-rc.2`;`@deepseek-ai/cordis` 与 `@deepseek-ai/schemastery` 使用按引擎配对的析取式 `~4.0.4 || ~4.0.5` 与 `~3.18.4 || ~3.18.5`——每个分支对应一代引擎的配对:`0.2.0-rc.2` 随 cordis `~4.0.4`/schemastery `~3.18.4` 发布,`0.2.1-rc.2` 随正式版 `4.0.5`/`3.18.5` 发布。兼容记录恰好包含这两个引擎——**两条都是 RC,不含 alpha 通道**,因为自 `0.2.1-alpha.5` 之后没有新的已验证版本。`0.2.0-rc.2` 虽然更旧,但被有意保留:它仍是 npm `latest` 解析到的版本,去掉它会让跑默认安装的宿主用 `engines.dsh` 准入直接拒绝本插件。每个分支仍必须是裸版本号,`0.2.0-rc.1`、`0.2.1-rc.1`、`0.3.0-alpha.1` 等未验证的邻近版本继续被拒。`npm run test:engine` 与 `npm run test:install` 对每个已声明引擎各跑一遍而不是只跑其中一个,前者还会用该引擎自己的 `evaluatePluginCompatibility` 对发布清单求值——自 alpha.4 起该判定只读 `engines.dsh`,因此引擎声明写错会在这一步失败,而不是拖到安装时才暴露。旧引擎安装说明见[安装](#安装)。
147
147
 
148
148
  ## 用量面板
149
149
 
@@ -209,6 +209,7 @@ llm-commandcode:
209
209
  # contextWindow: 131072 # 可选:自定义上下文长度(token),对所有模型生效;不填跟随目录
210
210
  # contextWindowOverrides: # 可选:按模型的自定义上下文长度;优先于 contextWindow
211
211
  # deepseek/deepseek-v4-pro: 65536
212
+ # maxOutputTokens: 65536 # 可选:单轮输出上限;0 = 不发送 max_tokens,留空跟随模型上限
212
213
  ```
213
214
 
214
215
  ### 自定义上下文长度
@@ -220,6 +221,18 @@ llm-commandcode:
220
221
  - 取值单位为 token,范围 1 – 10 000 000,必须为整数。网页阻止保存无效输入;配置文件中的非数值、小数或越界值由配置校验拒绝,不会静默回落。只有绕过配置校验的程序化调用会丢弃无效值,按「有效按模型值 → 有效全局值 → 目录」回落。目录没有的模型保持「无上下文宣告」的既有形状。
221
222
  - 输入框接受 `32K`、`1M` 这样的简写(二进制换算:`32K` = 32 768、`1M` = 1 048 576,131 072、262 144 这类 2 的幂窗口可直接写成 `128K`、`256K`;落盘仍是纯数字)。按模型覆盖的模型下拉只列出**模型白名单**中可见的模型(白名单为「显示全部」时即整个目录),行列表带「模型 / 上下文长度」列头。
222
223
 
224
+ ### 单轮输出上限
225
+
226
+ 插件每次请求都会带上 `max_tokens`,取值是「模型公布的输出上限、运行时学到的上限、网关总上限 131 072」三者取最小。这个字段是**被动硬限制**:到点就被平台切断,模型自己收不到「快到上限了」的信号。**设置 → Command Code → 模型与上下文**中的**单轮输出上限**(或配置项 `maxOutputTokens`)给了三个选择:
227
+
228
+ - **留空(默认)**:完全沿用上面的推导,行为与之前一致。
229
+ - **正整数**:与推导值取较小者。它只能**收紧**,永远不会突破模型上限或网关上限 `131072`;越界值由配置校验拒绝。输入接受 `64K` 这样的简写。
230
+ - **`0`**:请求中**不发送** `max_tokens` 字段,把单轮输出规模交回平台自行限制——这是「长回答写到一半被切断」的解法,因为只要字段在,无论填多大都会在某个点被切。
231
+
232
+ `0` 只在**接受缺省该字段**的两条传输上生效:CLI(`/alpha/generate`)与 OpenAI(`/provider/v1/chat/completions`)。Claude 走的 Anthropic Messages 端点**必填**该字段,缺了会被上游直接拒绝(`Invalid input: expected number, received undefined at max_tokens`),所以那条链路仍按推导值发送,填 `0` 对 Claude 无效。会话标题这类由宿主明确限定预算的辅助调用(会话标题固定 64 词元)同样优先于此设置,不会被 `0` 解除约束。
233
+
234
+ 留空表示**清除**该配置(回到推导默认),不是写入 0。
235
+
223
236
  ### 连续图片导致的缓存回退
224
237
 
225
238
  在 CLI 传输(`/alpha/generate`)上,即使此前所有请求消息逐字节一致,新图片也可能让服务端报告的缓存命中量退回第一张历史图片处。可在「高级设置」打开「已看过的图片不再重复发送」(`offloadSeenImagesForCache`):同一模型看过图片并回复后,插件通过 dsh 持久的 `image/offload` 事件把旧图替换成稳定文字占位;当前新图仍会送给模型,后续文本更容易保持缓存。**默认关闭**,因为模型如需重新查看已 offload 的图片,必须重新读取原文件或由用户再次附加。这是费用缓解方案,未修复 Command Code 服务端的多模态缓存行为。[实测证据与限制](docs/issue-64-cache-review.md)。
@@ -228,10 +241,10 @@ llm-commandcode:
228
241
 
229
242
  当你的 dsh 部署加载了 web 能力(`@deepseek-ai/dsh-web` + `@deepseek-ai/dsh-tool-web`)时,模型所用的 `web_search` 工具会由本插件的 `commandcode` 搜索 provider 承载——它用**与聊天相同的 API key 与 base URL** 调用 Command Code Provider API 的 `/alpha/web-search` 端点。你无需另外配置搜索 key、端点或模型。
230
243
 
231
- **默认开启。** 插件的 **设置 → Command Code** 页里有一个「用 Command Code 承载联网搜索」开关(`webSearch`,默认开)。开启时插件会自动把 `commandcode` 选为当前搜索后端;关闭则把选择权交还给之前的后端(比如 modsearch 等其他搜索插件可继续工作——不会被强制回退到 dsh 自带的 DeepSeek 搜索)。该开关在**下一次搜索时生效**,无需重启。
244
+ **默认开启。** 插件的 **设置 → Command Code** 页里有一个「用 Command Code 承载联网搜索」开关(`webSearch`,默认开)。开启时插件会自动把 `commandcode` 选为当前搜索后端;关闭则把选择权交还给之前的后端(比如 modsearch 等其他搜索插件可继续工作——不会被强制回退到 dsh 自带的 DeepSeek 搜索)。该开关在**下一次搜索时生效**,无需重启;在 dsh 0.2.1-rc.2 上它走官方易变设置写入路径,改动原地提交进运行中的 `web` 条目,而不是重挂载该条目;在 0.2.0-rc.2 上引擎把选择放在一个没有设置面的实例字段里,插件直接赋值(只存在于内存,随实例结束失效)。**若官方「网页搜索」设置里已显式选择其他后端,本开关不生效**——那个显式选择优先;0.2.0-rc.2 上没有这一设置行,看不到他人的 pin,因此开关总是接管。
232
245
 
233
246
  - 该 provider 仅在 web 服务存在时以 `commandcode` 注册进 `ctx.web`;没有它,本插件仍是纯聊天插件。
234
- - 开关通过启动时与每次设置变更时在 web 接缝里选中 `commandcode` 来实现,同时记住被顶掉的后端;关闭开关(或卸载插件)时会恢复那个后端。若你想更稳妥地固定,可设置 `searchProvider: commandcode`(或 `$DSH_WEB_SEARCH_PROVIDER=commandcode`);即使本插件的运行时选中不可用,该配置仍然生效。
247
+ - 开关在 `0.2.1-rc.2` 上通过引擎的易变设置写入路径选中 `commandcode`(写的就是官方「Web 搜索」卡片里「启用新版 Web 搜索 API」开关所用的同一个 `web.searchProvider` 字段,该开关还会一并写 `fetchProvider`)。插件**尊重用户层已有的显式选择**:当「网页搜索」设置已 pin 其他后端时,它让 `commandcode` 保持已注册但未被选中,并把这一状态报出来(开关的提示文案,外加一行宿主日志),而不是覆盖对方;只有在没人 pin 该字段时才接管,并记住被顶掉的后端,在关闭开关或卸载插件时恢复。`0.2.0-rc.2`(以及各 alpha)既没有该易变引用也没有 `web` 的设置行,选择是引擎自己的 `searchProviderId` 实例字段:插件按同一套「记住被顶掉的后端、只归还自己拿走的」规则直写内存,也看不到其他写者的 pin。若你想更稳妥地固定,可设置 `searchProvider: commandcode`(或 `$DSH_WEB_SEARCH_PROVIDER=commandcode`);即使运行时选择无法提交,该配置仍然生效。
235
248
  - dsh 工具的 `numResults` 会被收敛到 Command Code 的取值范围(1–10,默认 5);结果映射为 dsh 的 `WebSearchSource` 结构(`url`/`title`/`snippet`)。
236
249
 
237
250
  > 这里直接使用 Command Code Provider API(与官方 CLI 内置的 `web_search` 相同),因此与 DeepSeek 原生搜索后端不同。