@mars-sea/dsh-commandcode-provider 0.15.0 → 0.15.2

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,43 @@ 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
+ ## [0.15.2] - 2026-10-23
8
+
9
+ ### Changed
10
+
11
+ - **「模型白名单」说明精简成一句**:「只列出勾选的模型,都不勾选=显示全部。网页与终端共用。」(27 字,合并回单键 `visibleModelsHint`)。纯呈现层,白名单的暂存、校验与提交路径不动。
12
+ - **设置页尺寸回到设计画布**(用户反馈「整体大小好像被改掉了,没有那么协调了」):撤回同批早先加的两类调整——① 分组头、账户卡片列表、「添加账户」操作行的 `padding-right:36px` 右内缩,卡片与分组头恢复满宽;② API 地址行与模型白名单行的 `cc-rowFluid`(不折行、API 地址输入框拉伸到约 380–600px),API 地址输入框回到 280px,两行恢复普通行。
13
+
14
+ - **同步 Command Code CLI `1.87.1` → `1.87.3`,并清理四个已下线模型(纯请求头 + 快照清理)。** `1.87.2`(10-22T20:43Z)四条修复、`1.87.3`(10-22T21:30Z)两条新功能,两条都有官方变更条目——`cmd acp` 可加载 mod 选项、无效推理档位不再让所有请求失败、进程名不再每 5 秒重设、流中途失败显示供应商真实错误;`--permission-mode` 接受 Claude Code 的权限模式别名、更新 BYOK 包——全部属 CLI 自身行为,插件不经过这些路径。产物级比对:端点集合(64 个)、自定义请求头(5 个)、`/alpha/generate` 调用点、`promptCache` 与 `reasoning_effort` 字面量、模型注册表、efforts(推理档位)、订阅表与 ZDR(零数据保留)例外集逐项相同(审计 `cliDiff` 四类全空),故 `COMMAND_CODE_CLI_VERSION` 只随请求头更新。服务端目录 `catalogVersion` 由 `501a4bf9…` 变为 `02bfc7ac…`,默认视图 96 → 92 而 `?view=client` 仍 104:两个限时免费促销到期(`ornith/ornith-1.5-122b:free` `2026-10-22T06:00Z`、`minimax/minimax-m3.1-flash:free` `2026-10-22T10:45Z`),MiMo V2.5 两款下架(`xiaomi/mimo-v2.5`、`xiaomi/mimo-v2.5-pro`——V2.6 以原促销价作为常规价,属同价换代而非临时隐藏)。官方 models / pricing 页与 go / goat / pro / max 计划页同步删除这些行与小米合作区块,ZDR 页与 What's New 无变化。快照随之清理:`KNOWN_PLANS` 96 → 92、`KNOWN_EFFORTS` 66 → 65、`KNOWN_IMAGE_MODELS` 74 → 71、`KNOWN_THINKING_MODELS` 17 → 16、`KNOWN_DEALS` 8 → 4、内置价格行 94 → 92、输出上限行 82 → 79,ZDR 例外集 18 不变;其余 88 个模型的价格、上下文、计划档位与输入模态零变化。**证据边界**:公开 GET 采集 14 个源全部 HTTP 200;审计 `incomplete` 的 2 条 error 是 MiniMax M3.1 Flash 与 Ornith 缺定价别名(上一轮 4 条,随两个 `:free` 行下线减为 2 条),非抓取失败;未做真实账号登录、付费生成、浏览器渲染与服务端协议验证。
15
+
16
+ ### Fixed
17
+
18
+ - **设置页右侧只剩一条边界**(用户反馈「右侧没有设计图好看」):此前每行控件后都留 28px「恢复默认」空位,控件比账户卡片、分组头「刷新」、「查看模型用量」那一行多缩进 36px,形成两条右边界。现在「恢复默认」改为标题行「已自定义」徽标后的 20px 圆形图标按钮(只在字段被改过时出现),行末不再留空位,控件、卡片、分组头动作全部贴内容右缘;`FieldOverride` 组件与 `cc-resetSlot` 样式删除。
19
+
20
+ - **窄设置面板下宽控件不再折到第二行**(用户截图:约 560px 内容区里,模型白名单的「显示全部」+ 下拉框、API 地址输入框都掉到第二行右侧):设置行文字列下限从 300px 降到 200px,控件保持设计稿尺寸(输入框 280px、下拉 200px)并始终与标题同行,代价是窄面板下说明文字多折一两行;门槛由约 640px 降到约 540px。
21
+ - **服务状态行不再从中间断开「13 小时前更新」**:「打开状态页」链接移到「服务状态」标题行右侧(带外链图标),两行状态获得整行宽度;官方行的可用率、平均响应、更新时间各为不可断开的一段(视图字段 `detail` 改为 `detailParts`),放不下时整段换到下一行,换行后的行首不会是「·」。
22
+
23
+ ### Verification
24
+
25
+ - `npm run typecheck` 通过;`npm test` **1111/1111** 通过;`npm run build` 完成,`lib/client.js` 已不含 `cc-rowFluid` 与 `padding-right:36px`。**未做**:在运行中的 dsh web 宿主内实测(宿主重启后需新的带令牌地址)。
26
+ - 上游同步同批:`npm run typecheck` 退出码 0、`npm test` **1111/1111**、`npm run build` 两段 `Build complete`(`lib/` 已重建,含 `1.87.3`、不含 `xiaomi/mimo-v2.5`);`node scripts/sync-model-prices.mjs --check` 报 `up to date (92 rows)`、`node scripts/sync-output-limits.mjs --check` 报 `table already up to date`,退出码均 0。
27
+
28
+ ## [0.15.1] - 2026-10-22
29
+
30
+ ### Changed
31
+
32
+ - **同步 Command Code CLI `1.86.3` → `1.87.1`(纯请求头同步)。** `1.87.0`(10-21T18:02Z)与 `1.87.1`(10-21T23:26Z)先后发布,官方变更日志页面的 CLI 条目仍停在 `1.84.0`(`1.85.0`–`1.87.1` 全部没有条目),因此结论来自产物级静态比对:端点集合(64 个)、自定义请求头(5 个,均 `x-` 前缀)、`/alpha/generate` 调用点、模型注册表、efforts(推理档位)、订阅表与 ZDR(零数据保留)例外集逐项相同(首轮审计 `cliDiff` 四类全空);新增内容全属 CLI 自身——`/btw` 回合中旁问、磁盘写满保护、权限模式由 `standard` 改名 `default` 并新增 `dont-ask`,插件不经过这些路径。`COMMAND_CODE_CLI_VERSION` 只随请求头更新,运行时行为不变。**证据边界**:公开 GET 采集 14 个源全部 HTTP 200;审计 `incomplete` 的 4 条 error 仍是 MiniMax M3.1 Flash 与 Ornith 四个 id 缺定价映射别名(目录侧单源,与上一轮同因);未做真实账号登录、付费生成、浏览器渲染与服务端协议验证。
33
+
34
+ - **官方定价页两行月度额度下调,同步到内置价格表。** `deepseek-v4.1-flash` 的 GOAT 额度 `$100` → `$80`、Pro `$110` → `$90`(Go `$10` 不变);`mistral-large-4` 的 Go `$10` → `$6`、GOAT `$40` → `$20`、Pro `$50` → `$30`。逐 token 费率、促销与峰值时段均未变(价格表仍 94 行),由 `node scripts/sync-model-prices.mjs` 生成。
35
+
36
+ - **输出上限表新增 `thinkingmachines/inkling-small`(65,536)。** 该模型此前已在 `KNOWN_PLANS`/`KNOWN_THINKING_MODELS` 中,只是 models.dev 聚合目录这次才补上供应商行,由 `node scripts/sync-output-limits.mjs` 写入(81 → 82 行)。
37
+
38
+ - **服务端目录版本变化不涉及能力快照。** `catalogVersion` 由 `2a2c334e…` 变为 `501a4bf9…`,默认视图仍 96 个 id、`?view=client` 仍 104 个,`reasoningEfforts`/`zdr`/`inputModalities`/`minimumPlan`/`pricing` 与上一轮逐项一致,故 `src/capabilities.ts` 的能力表未改。两个免费促销按快照里已有的 `expiresAt` 自然失效(`ornith/ornith-1.5-122b:free` 2026-10-22T06:00Z、`minimax/minimax-m3.1-flash:free` 2026-10-22T10:45Z),无需改代码。
39
+
40
+ ### Verification
41
+
42
+ - `npm run typecheck` 退出码 0;`npm test` **1111/1111** 通过;`npm run build` 两段 `Build complete`,`lib/index.js` 与 `lib/index.d.ts` 已重建(含 `1.87.1`、两行 allowance、inkling-small 上限)。`node scripts/sync-model-prices.mjs --check` 与 `node scripts/sync-output-limits.mjs --check` 均回到 `up to date`(退出码 0)。
43
+
7
44
  ## [0.15.0] - 2026-10-21
8
45
 
9
46
  ### Added
package/README.md CHANGED
@@ -12,136 +12,106 @@
12
12
 
13
13
  Unofficial [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/) LLM provider plugin for **Command Code**, ported from [pi-commandcode-provider](https://github.com/patlux/pi-commandcode-provider) (MIT).
14
14
 
15
- > This is a community integration. You need your own Command Code account and API key or subscription, and Command Code's terms apply. This project is not affiliated with Command Code, Inc.
16
-
17
- ## What you get
18
-
19
- - **Plugin bundle** — install into any dsh profile with `dsh plugin add`; registers a `commandcode` provider route with a live model catalog.
20
- - **Dedicated settings page** — one account list (keys, sign-in, live quota, dedicated models), model visibility, privacy switches, and connection options.
21
- - **Service status** — the top of *Integrations & display* shows the official status page's published record (operational/degraded/down, 7- and 30-day uptime, 7-day average response time) with the time the official data was published, not a misleading "last checked" — Upptime only publishes on a status change or the daily summary. The host also probes the configured API base live, so the row shows an *Official* line and a *This device* line separately and answers "can I reach it right now" — when this machine cannot get through, the group heading says so; data is cached locally, a failed fetch is labeled as the last known result, and saving is never blocked.
22
- - **Model analysis** — open from Settings to inspect tokens, estimated cost, response timing and failures/retries by recent sessions or date range, including closed sessions with inherited fork usage excluded. Compare fixed-workload prices and objective capabilities without automatic recommendations or quality scores. Missing data and scan limits remain visible; complete date ranges can be extrapolated to 30 days. Estimates use current prices and are not invoices. See [scope and limitations](docs/模型成本与性能分析.md).
23
- - **Works in the terminal too** — the same install serves a [dsh-TUI](#terminal-ui-dsh-tui) profile, with its own **`/settings` → Command Code** page for the API key and the model controls.
24
- - **Models-page key card** — the **Settings → Models → Command Code** card carries the key status, a paste field, and the sign-in button inline.
25
- - **In-browser sign-in for keys** — start the official authorization flow (the same one `cmd login` runs) from the settings page; the approved key lands in the local credential service automatically. Manual paste remains the fallback.
26
- - **Multi-account rotation** — when one account hits its usage limit, requests switch to the next account automatically. See [Account rotation](#account-rotation).
27
- - **Flexible API key setup** — via the settings page, an environment variable, or the official CLI login file.
28
- - **Model-picker annotations** — minimum plan, active deal or `FREE` badge, peak/off-peak state, image support, and context window; free models listed first.
29
- - **Custom context length** — override the context window the harness meters and compacts against: one global value plus per-model overrides, edited on the settings page or in config (issue #86); unset follows the catalog.
30
- - **Plan-aware picker** — models above your subscription tier are hidden by default (toggleable); an optional **Model allowlist** keeps only your favorites in the picker.
31
- - **Reasoning-effort support** — models with selectable reasoning effort levels expose them in the picker.
32
- - **Image input** — Vision-capable models accept images.
33
- - **Plans & quota panel** — an optional Command Code card at the bottom of the sidebar (directly above Settings) shows the serving account's plan and its 5-hour and weekly windows; clicking it opens a dashboard with the billing period, both windows as progress bars with reset times, monthly credit consumption, and the purchased/free balances. The dashboard's **×** button hands the center column back to your conversation (the current session is untouched). **Off by default** — turn on *"Show the quota card in the sidebar"* under **Settings → Command Code → Integrations & display** to show it, and the same switch hides it again (a hidden card draws nothing at all and runs no background usage poll); the change lands as soon as you save. The panel follows your harness language (中文 / English).
34
- - **Session cost estimate** — published-rate estimates (`≈`) beside the composer token counter and in its usage dialog, using each request's model, request time and context tier from durable session history. Model switches and viewing the session later do not reprice earlier requests. Mixed-provider or unpriced usage shows a labeled subtotal (`≥`); missing history or wholly unpriceable usage stays hidden. Automatic and manual compaction inside a session sends one extra model request, recorded on the `compaction/summary` event; that usage is now priced into the Command Code cost, and into the historical model analysis, while summaries written by other providers stay unpriced. These are estimates from the installed price snapshot, not provider invoices. Also English on every harness language.
35
- - **Web search** — the dsh `web_search` tool is backed by the Command Code Provider API (`/alpha/web-search`) with the same key/endpoint as chat, so no separate search key or base URL is needed. See [Web search](#web-search).
36
-
37
- See [Screenshots](#screenshots) below for what the UI looks like.
15
+ > Community integration. You need your own Command Code account, API key or subscription, and Command Code's terms apply. Not affiliated with Command Code, Inc.
16
+
17
+ ## Quick start
18
+
19
+ | | |
20
+ |---|---|
21
+ | Verified engines | dsh `0.2.0-rc.2` (npm `latest`) · dsh `0.2.1-rc.2` (npm `next`) |
22
+ | Install — web | `dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest` |
23
+ | Install — terminal | `dsh plugin --profile dsh-tui add @mars-sea/dsh-commandcode-provider@latest` |
24
+ | Config file | `$DSH_HOME/settings.yaml` → `llm-commandcode:` |
25
+ | API key env | `COMMANDCODE_API_KEY` |
26
+ | Usage dashboard | `/commandcode` |
27
+
28
+ ## Features
29
+
30
+ - **Plugin bundle** — one `dsh plugin add` installs a `commandcode` provider route with the live model catalog.
31
+ - **Settings page** — accounts (keys, browser sign-in, live quota, dedicated models), model visibility, privacy switches, connection options.
32
+ - **Annotated catalog** — plan tier, deal/`FREE` badge, peak/off-peak, image support and context window in the picker; free models first.
33
+ - **Multi-account rotation** — an exhausted account hands over to the next one; pin a preferred account or route models to dedicated ones.
34
+ - **Browser sign-in** — run the official authorization flow from the settings page; the approved key lands in the local credential store.
35
+ - **Plans & quota panel** — optional sidebar card with the serving account's plan, 5-hour and weekly windows, balances and reset times.
36
+ - **Session cost estimate** — published-rate estimates (`≈`) beside the composer token counter, priced per request from durable session history.
37
+ - **Model analysis** — tokens, estimated cost, response timing and failures by session or date range. [Scope](docs/模型成本与性能分析.md).
38
+ - **Web search** — the dsh `web_search` tool is served by Command Code (`/alpha/web-search`) with the same key and endpoint as chat.
39
+ - **Zero data retention** — optional ZDR routing (`x-cmd-zdr: 1`), off by default.
40
+ - **Context & output limits** — override the metered context window globally or per model, and cap or omit the per-turn `max_tokens`.
41
+ - **Reasoning effort & images** — effort levels in the picker; Vision models accept images.
42
+ - **Terminal UI** — the same install serves a [dsh-TUI](#terminal-ui-dsh-tui) profile with its own `/settings` → Command Code page.
38
43
 
39
- ## Install
44
+ ## Screenshots
40
45
 
41
- This release supports **dsh 0.2.0-rc.2 and 0.2.1-rc.2** — two verified engines, both on the RC
42
- (release-candidate) channel. `0.2.0-rc.2` is what npm `latest` still points at (re-checked
43
- 2026-10-18), so it is the engine an official default install runs; `0.2.1-rc.2` ships on the `next`
44
- tag. The peer range and the compatibility records name both as exact versions, and no alpha release
45
- is retained: nothing has been verified since `0.2.1-alpha.5`. `@deepseek-ai/cordis` /
46
- `@deepseek-ai/schemastery` carry the paired disjunction `~4.0.4 || ~4.0.5` / `~3.18.4 || ~3.18.5`,
47
- because the two engines pair differently (`0.2.0-rc.2` needs cordis `~4.0.4` and schemastery
48
- `~3.18.4`, `0.2.1-rc.2` ships `4.0.5` / `3.18.5`) and one branch cannot resolve both. Note that
49
- **dsh 0.2.1-rc.2 reads
50
- `engines.dsh` alone** when it decides whether a plugin is compatible; the dsh peer ranges are
51
- consulted only when that field is absent, so a build that declares the wrong `engines.dsh` is refused
52
- outright there — while on `0.2.0-rc.2` the peer ranges are what decides. A profile on
53
- `0.2.1-alpha.5` or any earlier release must upgrade its engine before installing this build:
46
+ **Model picker** — plan tier, deal/FREE, peak/off-peak, Image and context annotations:
54
47
 
55
- ```sh
56
- dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
57
- ```
48
+ <img src="assets/screenshots/model-picker.png" alt="Model picker with plan, deal, image and context annotations" width="320">
58
49
 
59
- - **Older dsh releases.** The 0.1.2–0.1.7 line is no longer supported: those
60
- engines predate the 0.1.7 settings rewrite, the `RequestMessage` envelope, and
61
- the durable image-offload contract, and the compatibility code that bridged
62
- the two was removed. The last plugin release covering 0.1.7 is 0.11.17; the
63
- 0.1.2–0.1.6 line's last release is 0.11.11; the 0.5.0-era Harness line's last
64
- release is 0.9.1. All are installed by exact version and none is maintained:
50
+ **Usage dashboard** — `/commandcode` per-account report:
65
51
 
66
- ```sh
67
- dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.17 # dsh 0.1.7
68
- dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.11 # dsh 0.1.2–0.1.6
69
- dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.9.1 # dsh 0.5.0 line
70
- ```
52
+ <img src="assets/screenshots/usage-dashboard.png" alt="Usage dashboard" width="520">
71
53
 
72
- **pnpm 11 holds back new releases.** Its `minimumReleaseAge` defaults to 1440 minutes, so a version published less than a day ago is skipped and `@latest` resolves to the *previous* release — silently, with a success exit code. To install a release from the last 24 hours, name it exactly:
54
+ **Settings page** — accounts with live quota, model visibility, privacy switches, connection options:
55
+
56
+ <img src="assets/screenshots/settings-page.png" alt="Command Code settings page with the account usage card" width="640">
57
+
58
+ ## Install
73
59
 
74
60
  ```sh
75
- dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.11.15
61
+ dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@latest
76
62
  ```
77
63
 
78
- The same applies to every profile you install into, including the terminal UI below.
64
+ This build targets **dsh `0.2.0-rc.2` and `0.2.1-rc.2`** — two verified engines, both on the RC channel. `0.2.0-rc.2` is what npm `latest` points at, so it is the engine a default install runs; `0.2.1-rc.2` ships on `next`. A profile on `0.2.1-alpha.5` or older must upgrade its engine first. Compatibility details — peer ranges, the `engines.dsh` admission rule, per-engine package pairings — live in [engine compatibility](docs/agent-reference/engine-compatibility.md).
79
65
 
80
- 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.
66
+ **Older dsh releases** are no longer supported: the last plugin release for dsh 0.1.7 is `0.11.17`, for 0.1.2–0.1.6 it is `0.11.11`, and for the 0.5.0-era line `0.9.1` — all installed by exact version, none maintained.
81
67
 
82
- ## Updating
68
+ > **pnpm 11 holds back new releases.** `minimumReleaseAge` defaults to 1440 minutes, so a version published less than a day ago is skipped and `@latest` resolves to the *previous* release — silently, with a success exit code. To install a release from the last 24 hours, name it exactly: `@mars-sea/dsh-commandcode-provider@0.11.15`. This applies to every profile, including the terminal UI below.
83
69
 
84
- Update with the same tag you installed with:
70
+ **Updating** — same tag, then restart the web app. Each profile updates separately, because the terminal UI owns its own plugin list:
85
71
 
86
72
  ```sh
87
- dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest # dsh 0.2.0-rc.2 / 0.2.1-rc.2
88
- dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@0.9.1 # older dsh (0.5.0 line, unmaintained)
73
+ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider@latest
74
+ dsh plugin --profile dsh-tui update @mars-sea/dsh-commandcode-provider@latest
89
75
  ```
90
76
 
91
- Each profile updates separately — the terminal UI owns its own plugin list (see below):
92
-
93
- ```sh
94
- dsh plugin --profile dsh-tui update @mars-sea/dsh-commandcode-provider@0.11.15
95
- ```
77
+ ## Get an API key and verify it works
96
78
 
97
- To move to a version published less than 24 hours ago, name it exactly as in Install above; pnpm 11's age gate resolves `@latest` to the previous release instead.
98
-
99
- Then restart the web app.
100
-
101
- ## Getting an API key
102
-
103
- The easiest path is the official CLI (Node.js 22+):
79
+ Easiest via the official CLI (Node.js 22+):
104
80
 
105
81
  ```sh
106
82
  npm i -g command-code@latest
107
83
  cmd login # macOS/Linux; native Windows: cmdc login
108
84
  ```
109
85
 
110
- Or skip the CLI: click **Sign in to Command Code** under **Settings → Command Code** — your browser opens the commandcode.ai authorization page (the same flow `cmd login` uses) and the key is stored in the local credential service when you approve. You can still create a key on the [Keys settings page](https://commandcode.ai/mars-sea/settings/keys) and paste it into **Settings → Command Code**, or `export COMMANDCODE_API_KEY="user_..."`.
86
+ Or skip the CLI: click **Sign in to Command Code** under **Settings → Command Code** — the browser opens the commandcode.ai authorization page (the same flow `cmd login` runs) and the key is stored locally once you approve. You can also create a key on the [Keys page](https://commandcode.ai/mars-sea/settings/keys) and paste it, or `export COMMANDCODE_API_KEY="user_..."`.
111
87
 
112
88
  > The sign-in flow needs the Host and your browser on the same machine (loopback callback). With a remote Host, paste the key manually; a literal composition-level `apiKey`, if set, still takes precedence over a signed-in credential.
113
89
 
114
- ## Verify it works
115
-
116
- After restart, enter your API key in **Settings → Command Code** and save; **Settings → Models** shows a **Command Code** card, and the model picker lists the live catalog under **commandcode**. Send a message with a model your plan includes.
90
+ **To verify:** after restart, enter your API key in **Settings → Command Code** and save. **Settings → Models** shows a **Command Code** card, and the model picker lists the live catalog under **commandcode**. Send a message with a model your plan includes.
117
91
 
118
92
  ## Terminal UI (dsh-TUI)
119
93
 
120
- The plugin also works under a terminal front door. **Each dsh profile owns its own plugin list**, so the web install above does not reach the terminal — add the plugin to the `dsh-tui` profile as well:
94
+ **Each dsh profile owns its own plugin list**, so the web install does not reach the terminal — add the plugin to the `dsh-tui` profile as well, pinned to an exact version (within 24 hours of a release a bare name resolves to the previous build, leaving a fresh install with no `/settings` → Command Code page and no `commandcode` models):
121
95
 
122
96
  ```sh
123
- dsh plugin --profile dsh-tui add @mars-sea/dsh-commandcode-provider@0.11.15
97
+ dsh plugin --profile dsh-tui add @mars-sea/dsh-commandcode-provider@latest
124
98
  ```
125
99
 
126
- Pin the exact version here. For the first 24 hours after a release, a bare package name (or `@latest`) is silently resolved to the previous one: the install succeeds, but the profile gets the older build — which is how a fresh terminal install ends up with no **`/settings` → Command Code** page and no `commandcode` models at all.
127
-
128
100
  Then pick the provider in the model selector, or name it directly:
129
101
 
130
102
  ```text
131
103
  /model commandcode/deepseek/deepseek-v4.1-flash
132
104
  ```
133
105
 
134
- `/model` lists every registered provider, with Command Code's live catalog and its plan/deal/context annotations. The `/commandcode` usage dashboard works there too.
135
-
136
- **Entering the key.** The TUI has no web Models page, so the plugin declares its own page in the TUI settings screen — **`/settings` → Command Code** — with the API key, the API base, the out-of-plan model filter, the active account, and the command language. The key field is write-only: it shows whether a key is configured and writes what you type to the credential store, never to a settings document. The same page is the only place a TUI-only user needs to visit.
106
+ `/model` lists every registered provider, with the live catalog and its plan/deal/context annotations; the `/commandcode` dashboard works there too.
137
107
 
138
- **Picking models.** That page lists the whole catalog as checkboxes, grouped by plan tier (Go → GOAT → Pro → Provider) with the free models first, so nothing has to be typed from memory. Everything starts checked, because an unset allowlist means "show every model" — uncheck the ones you do not want in the picker. The choice is stored as per-model overrides next to the `visibleModels` list the web page edits, so both surfaces can be used interchangeably and a plain `visibleModels` written by hand still works.
108
+ **Entering the key.** The TUI has no web Models page, so the plugin declares its own page — **`/settings` → Command Code** — with the API key, API base, out-of-plan filter, active account and command language. The key field is write-only: it shows whether a key is configured and writes what you type to the credential store, never to a settings document. The same page lists the whole catalog as checkboxes grouped by plan tier, free models first; everything starts checked, and the choice is stored as per-model overrides next to the `visibleModels` list the web page edits.
139
109
 
140
- Alternatively, set the key outside the TUI. The first match wins:
110
+ Alternatively set the key outside the TUI. The first match wins:
141
111
 
142
- - A literal `apiKey` written into the composition config wins over everything else and is always used.
143
- - Otherwise the credential reference is resolved (`apiKeyEnv`, `COMMANDCODE_API_KEY` by default). In a profile that **mounts the credentials service** — Web, desktop, SDK, ACP — that service owns the layer order: **from 0.2.1-alpha.3 on, a saved credential wins over the launching environment**, with the environment and `.env` as the fallback; 0.2.1-alpha.2 and earlier were the other way round, with the environment first.
144
- - A **headless profile deliberately mounts no credentials service**, so only the launching environment, the project `.env`, and `$DSH_HOME/.env` apply — **a key saved on the page never reaches a headless run**. Export the variable or write a `.env` for those runs.
112
+ - A literal `apiKey` in the composition config wins over everything and is always used.
113
+ - Otherwise the credential reference is resolved (`apiKeyEnv`, `COMMANDCODE_API_KEY` by default). In a profile that mounts the credentials service (Web, desktop, SDK, ACP) that service owns the order: **since 0.2.1-alpha.3 a saved credential wins over the launching environment**; earlier releases had the environment first.
114
+ - A **headless profile mounts no credentials service on purpose**, so only the launching environment, the project `.env` and `$DSH_HOME/.env` apply — **a key saved on the page never reaches a headless run**.
145
115
  - The last resort is `~/.commandcode/auth.json`, written by the official CLI.
146
116
 
147
117
  ```sh
@@ -149,7 +119,7 @@ export COMMANDCODE_API_KEY="user_..." # launching environment
149
119
  cmd login # writes ~/.commandcode/auth.json
150
120
  ```
151
121
 
152
- **Making Command Code the default.** dsh-TUI pins its own agent route, and its `agent-default-model` setting does not override it. To start every session on Command Code, override the `agent-loop` row in your profile patch (`$DSH_HOME/profiles/dsh-tui/cordis.patch.yml`):
122
+ **Making Command Code the default.** dsh-TUI pins its own agent route, so its `agent-default-model` setting does not override it. Override the `agent-loop` row in your profile patch (`$DSH_HOME/profiles/dsh-tui/cordis.patch.yml`):
153
123
 
154
124
  ```yaml
155
125
  - id: agent-loop
@@ -163,8 +133,6 @@ cmd login # writes ~/.commandcode/auth.json
163
133
  cwd: !!js process.cwd()
164
134
  ```
165
135
 
166
- **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.
167
-
168
136
  ## Usage dashboard
169
137
 
170
138
  The plugin registers a `/commandcode` slash command showing per-account usage:
@@ -173,98 +141,66 @@ The plugin registers a `/commandcode` slash command showing per-account usage:
173
141
  /commandcode (or /commandcode status)
174
142
  ```
175
143
 
176
- The command's user-facing copy follows the shell's locale: explicit `lang: 'en' | 'zh'` in the `llm-commandcode` plugin config wins, otherwise `LC_ALL`/`LANG` is read, otherwise it falls back to `zh`. The web settings page is independent — it follows the browser's language preference on its own.
144
+ Its copy follows the shell's locale: explicit `lang: 'en' | 'zh'` in the `llm-commandcode` plugin config wins, otherwise `LC_ALL`/`LANG` is read, otherwise it falls back to `zh`. The web settings page is independent — it follows the browser's language preference.
177
145
 
178
146
  ## Account rotation
179
147
 
180
148
  With several Command Code subscriptions, the plugin **switches to the next account automatically** when one hits its usage limit:
181
149
 
182
- - **Setup** — click **Add account** in the **Accounts** card at Settings → **Command Code**, optionally name it, then either **Sign in to Command Code** (the key is stored when you approve in the browser; a sign-in that does not complete leaves no empty account behind) or paste an API key. Account actions — add, rename, replace or clear a key, remove, pin — apply immediately; only the other settings on the page wait for the Save bar. The top-level key always serves first as the `default` account.
183
- - **Manual switching** — **Pin this account** in an account's **⋯** menu makes it the preferred account; if it is exhausted, requests fall back to other accounts and return once its window resets. **Unpin** returns to automatic rotation.
184
- - **Dedicated models** — expand an account and pick its **Dedicated models** from the live catalog. A request for one of those models serves from that account while it is usable; an exhausted or invalid account falls back to the normal rotation. A model belongs to one account at a time — picking it for another account moves it. (Stored as `modelAccountRules`.)
185
- - **Show only favorite models** — the **Visible models** picker in the **Models & context** card keeps only the checked models in the model picker; unchecked shows all (the default).
186
- - **Status** — each account row shows its plan, status and 5-hour/weekly meters; expand it for the full report. `/commandcode` reports the same per-account state.
150
+ - **Setup** — **Add account** in the **Accounts** card at Settings → **Command Code**, then sign in or paste an API key. Account actions (add, rename, replace or clear a key, remove, pin) apply immediately; only the other settings wait for the Save bar. The top-level key always serves first as the `default` account.
151
+ - **Manual switching** — **Pin this account** in an account's **⋯** menu makes it preferred; if it is exhausted, requests fall back to the others and return once its window resets. **Unpin** returns to automatic rotation.
152
+ - **Dedicated models** — expand an account and pick its **Dedicated models** from the live catalog. Those requests serve from that account while it is usable; an exhausted account falls back to the normal rotation. A model belongs to one account at a time (stored as `modelAccountRules`).
153
+ - **Show only favorite models** — the **Visible models** picker in the **Models & context** card keeps only the checked models in the picker; unchecked shows all (the default).
154
+
155
+ Each account row shows its plan, status and 5-hour/weekly meters; expand it for the full report, or run `/commandcode`. The equivalent YAML is in [Configure](#configure).
187
156
 
188
- The equivalent YAML (`$DSH_HOME/settings.yaml` or composition config):
157
+ ## Configure
158
+
159
+ **Settings → Command Code** groups the options as **Accounts** (keys, sign-in, live quota, pinning, dedicated models), **Models & context** (out-of-plan filter, visible models, context length, output limit), **Privacy & security** (zero data retention), **Integrations & display** (web search, sidebar quota card) and **Advanced** (API base URL, timeouts, transport retries, image offload). The same options live in `$DSH_HOME/settings.yaml` and apply immediately, without a restart:
189
160
 
190
161
  ```yaml
191
162
  llm-commandcode:
192
163
  apiKeyEnv: COMMANDCODE_API_KEY # first (default) account
193
- activeAccount: COMMANDCODE_API_KEY_2 # optional: pin the active account (`default` or an account's credential ref)
194
- accounts: # rotation order after it
164
+ activeAccount: COMMANDCODE_API_KEY_2 # optional: pin the active account
165
+ accounts: # optional: rotation order after it
195
166
  - label: Go #2
196
167
  apiKeyEnv: COMMANDCODE_API_KEY_2
197
- - label: Go #3
198
- apiKeyEnv: COMMANDCODE_API_KEY_3
199
168
  modelAccountRules: # optional: route models to accounts (first match wins)
200
169
  - models: # catalog model ids (multi-select)
201
170
  - deepseek/deepseek-v4-pro
202
- - deepseek/deepseek-v4-flash-vision-exp
203
171
  account: COMMANDCODE_API_KEY_2
204
- - models:
205
- - tencent/hy4-preview
206
- account: default
207
- visibleModels: # optional: show only these models in pickers (catalog model ids); unset shows all
172
+ visibleModels: # optional: only these models in pickers; unset shows all
208
173
  - deepseek/deepseek-v4-pro
209
- - tencent/hy4-preview
210
- ```
211
-
212
- ## Configure
213
-
214
- **Settings → Command Code** is organized as **Accounts** (keys, sign-in, live quota, pinning, dedicated models), a collapsed **Models & context** group (click its heading to unfold: hide out-of-plan models, visible models, then the **Custom context length** field and the **Per-model overrides** rows right after the allowlist — global and per model, see below), **Privacy & security** (zero data retention, off by default, see below), **Integrations & display** (serve web search with Command Code, show the quota card in the sidebar, off by default), and a collapsed **Advanced** section (API base URL, request/stream timeouts, transport retries, cache-aware image offload). Both foldable groups toggle from their whole heading row, and neither remembers its expanded state across visits. The working directory is no longer on the page; `workingDir` in config still works.
215
-
216
- The same options live in `$DSH_HOME/settings.yaml` (changes apply immediately, no restart):
217
-
218
- ```yaml
219
- llm-commandcode:
220
- apiKeyEnv: COMMANDCODE_API_KEY # credential reference
221
174
  apiBase: https://api.commandcode.ai
222
- workingDir: /path/to/project # optional
175
+ workingDir: /path/to/project # optional
223
176
  modelsCachePath: ~/.commandcode/models-cache.json
224
- requestTimeoutMs: 300000 # default 300s (the official CLI's own budget)
225
- streamIdleTimeoutMs: 300000 # default 300s
226
- # offloadSeenImagesForCache: true # opt in to CLI cache mitigation; later requests omit old pixels
227
- showSidebarQuota: true # optional: show the plans & quota card in the sidebar (default off)
228
- zdr: true # optional: route requests only through zero-data-retention upstreams (default off)
229
- # contextWindow: 131072 # optional: custom context length (tokens) for every model; unset follows the catalog
230
- # contextWindowOverrides: # optional: per-model custom context lengths; wins over contextWindow
177
+ requestTimeoutMs: 300000 # default 300s (the official CLI's own budget)
178
+ streamIdleTimeoutMs: 300000 # default 300s
179
+ # offloadSeenImagesForCache: true # opt in to CLI cache mitigation
180
+ showSidebarQuota: true # optional: sidebar plans & quota card (default off)
181
+ zdr: true # optional: zero-data-retention upstreams only (default off)
182
+ # contextWindow: 131072 # optional: custom context length for every model
183
+ # contextWindowOverrides: # optional: per-model context lengths; wins over contextWindow
231
184
  # deepseek/deepseek-v4-pro: 65536
232
- # maxOutputTokens: 65536 # optional: per-turn output limit; 0 sends no max_tokens, unset follows the model ceiling
185
+ # maxOutputTokens: 65536 # optional: per-turn output limit; 0 sends no max_tokens
233
186
  ```
234
187
 
235
- ### Custom context length
236
-
237
- The context window the harness meters against — and when it compacts — normally comes from the model catalog's `context_length`. The opt-in **Custom context length** field and **Per-model overrides** rows inside **Settings → Command Code → Models & context**, directly after the visible-models allowlist (or the two config fields above, issue #86), replace it per your own numbers: `contextWindow` is the global custom value applied to every model without its own row, and `contextWindowOverrides` sets one model by catalog id, winning over the global value. Unset follows the catalog; nothing changes by default.
238
-
239
- - **When it helps**: a catalog window that over-reports turns into `context_length_exceeded` failures once a session grows past the model's real capacity — advertise the lower real number and the harness compacts before the gateway refuses. An under-reported window makes long sessions compact earlier than they need to; raise it to match the model's true capacity.
240
- - The model-picker context annotation, `defaultMaxTokens` default and actual requests follow the same resolution. Raising the window removes the old catalog-window output cap while retaining the model's independent output ceiling, runtime-learned ceiling and gateway-wide cap; explicit output counts obey those caps too. Session-cost tier pricing keeps using each request's actual token counts.
241
- - 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.
242
- - 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.
243
-
244
- ### Per-turn output limit
245
-
246
- 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:
247
-
248
- - **Empty (default)**: exactly the derivation above, so nothing changes.
249
- - **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.
250
- - **`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.
251
-
252
- `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`.
253
-
254
- An empty field **clears** the setting back to the derived default; it is not the same as writing `0`.
188
+ ### Context, output and image tuning
255
189
 
256
- ### Repeated image cache misses
257
-
258
- 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).
190
+ | Option | Effect |
191
+ |---|---|
192
+ | `contextWindow`, `contextWindowOverrides` | Replace the catalog `context_length` the harness meters and compacts on (issue #86). Use it when the catalog over-reports the window (sessions then fail with `context_length_exceeded`) or under-reports it (sessions compact too early). Whole tokens in 1 – 10,000,000; `32K`/`1M` shorthand accepted. |
193
+ | `maxOutputTokens` | Per-turn `max_tokens` cap. Every request already carries `max_tokens`, resolved as the smallest of the model's published ceiling, the runtime-learned ceiling and the gateway cap of 131,072 — a passive hard limit that cuts the answer with no warning to the model. A positive number can only **tighten** it; `0` sends **no** field at all, which is the fix for answers cut off mid-sentence (`0` affects the CLI and OpenAI transports only; Claude's Anthropic Messages endpoint requires the field). An empty field clears the setting — it is not the same as writing `0`. |
194
+ | `offloadSeenImagesForCache` | On the CLI transport a new image can make the reported prompt cache retreat to the first historical image even when every previous request message is byte-identical. Enabling it replaces an older image with a stable text placeholder once this model has answered with it, so later text stays cached without replaying old pixels. It mitigates the cost spike but does not repair Command Code's multimodal cache behavior, and the model cannot inspect an offloaded image again without rereading the source file. Off by default. [Live evidence](docs/issue-64-cache-review.md). |
259
195
 
260
196
  ## Web search
261
197
 
262
- 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.
198
+ When your deployment 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, so no separate search key, endpoint or model is needed.
263
199
 
264
- **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.
200
+ **On by default.** The *"Serve dsh web search with Command Code"* toggle (`webSearch`) selects `commandcode` as the active search backend; turning it off hands the selection back to whichever backend was there before (a sibling search plugin such as modsearch keeps working). The change takes effect on the next search — no restart.
265
201
 
266
202
  - The provider registers as `commandcode` on `ctx.web` only when the web service is present; without it this stays a chat-only plugin.
267
- - 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.
203
+ - When the official **Web search** settings already pin another backend, that explicit choice wins: the plugin keeps `commandcode` registered but unselected and reports that state. 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. To pin it durably, set `searchProvider: commandcode` (or `$DSH_WEB_SEARCH_PROVIDER=commandcode`).
268
204
  - `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`).
269
205
 
270
206
  > 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.
@@ -273,38 +209,28 @@ When your deployment's dsh shell mounts the web capability (`@deepseek-ai/dsh-we
273
209
 
274
210
  Command Code can serve a request only through upstreams that retain no prompts or completions and never train on them — the same opt-in the official CLI exposes as `CMD_ZDR=1`, and the `x-cmd-zdr: 1` header on the Provider API ([official docs](https://commandcode.ai/docs/resources/zdr)).
275
211
 
276
- **Off by default.** Turn it on with the *"Zero data retention (ZDR)"* toggle (`zdr`) in the Privacy & security card, or in your profile config. How this plugin implements it:
212
+ **Off by default.** Turn it on with the *"Zero data retention (ZDR)"* toggle (`zdr`) in the Privacy & security card, or in your profile config.
277
213
 
278
- - The header is sent on **every chat request** while ZDR is on. The plugin maintains an informational exception list (`KNOWN_NON_ZDR_MODELS`, synced from the official CLI — 18 models as of 2026-10-21, e.g. `claude-fable-5`, `stealth/glyph-cluster:free`, `stepfun/Step-3.7-Flash`, `tencent/hy4-preview`). A model without an available ZDR upstream fails with `422 cmd_zdr_no_providers`; the request is never retried without the header.
279
- - If a refusal still happens (coverage churn, or no ZDR upstream with spare capacity at that moment), the error names the cause and how to turn ZDR off, instead of surfacing as a bare HTTP 422.
214
+ - The header is sent on **every chat request** while ZDR is on. A model without an available ZDR upstream fails with `422 cmd_zdr_no_providers`; the request is never retried without the header.
215
+ - If a refusal still happens (coverage churn, or no spare ZDR capacity at that moment), the error names the cause and how to turn ZDR off instead of surfacing as a bare HTTP 422.
280
216
  - **ZDR usually costs more**: capacity is limited and billed at each upstream's pass-through rates, and which upstream serves a request can change per request. The session-cost readout keeps quoting the ordinary catalog rates; the real per-request price shows in Command Code's Studio usage page.
281
- - Works on every plan; plan credits meter ZDR requests at the plan's default allowance.
282
217
 
283
218
  ## Notes & limitations
284
219
 
285
- - **Image input is model-gated** — only Vision models accept images; text-only models refuse them.
286
- - **Image size is reported as sent** — the adapter overrides `imageRequestTarget` with the target this route actually puts on the wire: long edge 1568, encoded bytes capped at 1 MiB. The harness's screenshot callers therefore size against what the gateway receives instead of the normalized original.
287
- - Switching to a text-only model in an image-bearing session is rejected by dsh — pick a model marked *`Image`* or remove the images first.
288
- - **Long image-heavy sessions keep working** — the provider caps the size of a single request (around 50 MB, and it is undocumented), so a session that accumulates many screenshots would otherwise start failing every request once it grew past that. The oldest images beyond the budget are replaced by a short "image omitted" note that names the attachment, the newest ones are sent as usual, and a request the provider still rejects as too large is retried once with a much smaller image budget before failing.
220
+ - **Image input is model-gated** — only Vision models accept images; text-only models refuse them, and dsh rejects switching a text-only model into an image-bearing session. The adapter reports the size actually put on the wire (long edge 1568, encoded bytes capped at 1 MiB), and the provider caps a single request (around 50 MB, undocumented); images beyond that budget are replaced by a short "image omitted" note, and an over-sized request is retried once with a smaller image budget.
289
221
  - **No `stop` sequences** — requests carrying one fail.
290
- - Historical reasoning is replayed on both transports so a tool-calling loop keeps its chain of thought: `/provider/v1/chat/completions` passes it back as `reasoning_content`, and `/alpha/generate` carries it as a `reasoning` block inside the rebuilt assistant turn (the shape the official CLI sends) — a DeepSeek thinking-mode tool loop is rejected without it. Only tool calls with a paired tool result are replayed on either transport.
291
- - Every tool's parameter schema is normalized to the object root the provider requires, so a tool declared without one (a third-party plugin's hand-written schema, an MCP server's own `inputSchema`, a generated root `$ref`) no longer fails the whole turn with `schema must be a JSON Schema of 'type: "object"'`.
222
+ - **Historical reasoning is replayed on both transports** so a tool-calling loop keeps its chain of thought: `/provider/v1/chat/completions` passes it back as `reasoning_content`, `/alpha/generate` carries it as a `reasoning` block inside the rebuilt assistant turn. Only tool calls with a paired tool result are replayed.
223
+ - **Tool parameter schemas are normalized** to the object root the provider requires, so a tool declared without one (a third-party plugin's hand-written schema, an MCP server's `inputSchema`, a generated root `$ref`) no longer fails the turn with `schema must be a JSON Schema of 'type: "object"'`.
292
224
  - The model catalog is browsable without a key; chat requests need one.
293
225
 
294
226
  ## Permissions & privacy
295
227
 
296
- The plugin only communicates between your local dsh profile and your Command Code account: locally it touches only the credential store and the models cache (plus `~/.commandcode/auth.json` as a last-resort fallback); on the network it calls only the Command Code API. No telemetry. With **zero data retention** on (also off by default), chat requests require a ZDR upstream; models without one fail instead of running without the protection.
228
+ The plugin only communicates between your local dsh profile and your Command Code account: locally it touches only the credential store and the models cache (plus `~/.commandcode/auth.json` as a last-resort fallback); on the network it calls only the Command Code API. No telemetry. With **zero data retention** on, chat requests require a ZDR upstream; models without one fail instead of running without the protection.
297
229
 
298
230
  ## Disabling / uninstalling
299
231
 
300
232
  - **Disable** without removing: edit your profile's `cordis.patch.yml` and comment out (or remove) the `llm-commandcode` row, or set `disabled: true`, then restart.
301
- - **Uninstall** completely:
302
-
303
- ```sh
304
- dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider
305
- ```
306
-
307
- Your API key in the dsh credential store and `~/.commandcode/auth.json` are left untouched.
233
+ - **Uninstall** completely: `dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider`. Your API key in the dsh credential store and `~/.commandcode/auth.json` are left untouched.
308
234
 
309
235
  ## Development
310
236
 
@@ -314,39 +240,25 @@ npm run typecheck # tsc --noEmit
314
240
  npm run build # tsdown -> lib/
315
241
  ```
316
242
 
317
- To try a local build in a profile:
243
+ To try a local build in a profile: `dsh plugin --profile web add /path/to/dsh-commandcode-provider`. After changing `src/`, re-run `npm run build` and restart the app.
318
244
 
319
- ```sh
320
- dsh plugin --profile web add /path/to/dsh-commandcode-provider
321
- ```
245
+ The package exports the plugin root, `/client`, `/locale/*.json`, and `/package.json`. Source remains in the repository; the unpackaged `./src/*` export was removed. `npm run test:pack` checks declared entry points against the actual packed file list.
246
+
247
+ ## Reference docs
248
+
249
+ The engineering detail behind this README lives under [`docs/agent-reference/`](docs/agent-reference/):
322
250
 
323
- After changing `src/`, re-run `npm run build` and restart the app.
251
+ - [adapter and wire contracts](docs/agent-reference/adapter-protocol.md) — transports, streaming, images, tool calls, reasoning replay, output budget, ZDR header
252
+ - [accounts and retry](docs/agent-reference/accounts-and-retry.md) — credential resolution, account rotation, rate limits, web search selection
253
+ - [client and settings](docs/agent-reference/client-and-settings.md) — settings pages, browser sign-in, TUI settings, model selection
254
+ - [usage and cost](docs/agent-reference/usage-and-cost.md) — session cost readout, price snapshot, compaction accounting
255
+ - [engine compatibility](docs/agent-reference/engine-compatibility.md) — supported dsh versions, peer ranges, `engines.dsh` admission
256
+ - [model catalog](docs/agent-reference/model-catalog.md) — catalog sync, plan tiers, effort levels, deals, ZDR exception set
324
257
 
325
258
  ## Community & feedback
326
259
 
327
- - <img src="https://cdn.simpleicons.org/github/111827" width="16" alt="GitHub" /> [GitHub Repository](https://github.com/Mars-Sea/dsh-commandcode-provider)
328
- - <img src="https://cdn.simpleicons.org/github/111827" width="16" alt="Releases" /> [GitHub Releases](https://github.com/Mars-Sea/dsh-commandcode-provider/releases)
329
- - <img src="https://cdn.simpleicons.org/npm/111827" width="16" alt="npm" /> [npm Package](https://www.npmjs.com/package/@mars-sea/dsh-commandcode-provider)
330
- - <img src="https://cdn.simpleicons.org/discourse/111827" width="16" alt="Linux.do" /> [Linux.do 社区](https://linux.do/)
260
+ [GitHub Repository](https://github.com/Mars-Sea/dsh-commandcode-provider) · [GitHub Releases](https://github.com/Mars-Sea/dsh-commandcode-provider/releases) · [npm Package](https://www.npmjs.com/package/@mars-sea/dsh-commandcode-provider) · [Linux.do 社区](https://linux.do/)
331
261
 
332
262
  ## License
333
263
 
334
264
  MIT — see [LICENSE](./LICENSE). Portions ported from [pi-commandcode-provider](https://github.com/patlux/pi-commandcode-provider) (MIT).
335
-
336
- ## Screenshots
337
-
338
- **Model picker** — plan tier, deal/FREE, peak/off-peak, Image and context annotations:
339
-
340
- <img src="assets/screenshots/model-picker.png" alt="Model picker with plan, deal, image and context annotations" width="320">
341
-
342
- **Usage dashboard** — `/commandcode` per-account report:
343
-
344
- <img src="assets/screenshots/usage-dashboard.png" alt="Usage dashboard" width="520">
345
-
346
- **Settings page** — the account list with live quota, model visibility, privacy switches and connection options:
347
-
348
- <img src="assets/screenshots/settings-page.png" alt="Command Code settings page with the account usage card" width="640">
349
-
350
- ### Package entry points
351
-
352
- The package exports the plugin root, `/client`, `/locale/*.json`, and `/package.json`. Source remains in the repository; the unpackaged `./src/*` export was removed. `npm run test:pack` checks declared entry points against the actual packed file list.