@mars-sea/dsh-commandcode-provider 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@ 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.2.0] - 2026-08-16
8
+
9
+ ### Fixed
10
+
11
+ - **`KNOWN_EFFORTS` was missing ten models that the official command-code@1.26.0 model table defines reasoning-effort levels for** (`claude-haiku-4-5-20251001`, `moonshotai/Kimi-K2.5`, `moonshotai/Kimi-K2.6`, `moonshotai/Kimi-K2.7-Code-Highspeed`, `MiniMaxAI/MiniMax-M2.5`, `xiaomi/mimo-v2.5`, `xiaomi/mimo-v2.5-pro`, `tencent/hy3-paid`, `tencent/Hy3`, `meta/muse-spark-1.2-contributor`). These models previously showed no reasoning-effort selector in the harness even though the official CLI exposes one; they now do. The snapshot was re-verified against the CLI's authoritative `ZA` model table (dist/cli.mjs) rather than the docs page alone.
12
+
13
+ ### Added
14
+
15
+ - **`KNOWN_THINKING_MODELS`** — models the official registry marks `Reasoning` but for which the CLI defines no selectable effort levels (e.g. `MiniMaxAI/MiniMax-M3`, `moonshotai/Kimi-K3`, `Qwen/Qwen3.7-Max`, `thinkingmachines/inkling`). The model picker now labels these "Supports thinking (auto)" instead of the misleading "Text only", so a reasoning-capable model is no longer mistaken for a text-only one. Selectable efforts remain exclusively driven by `KNOWN_EFFORTS`, matching the official CLI (which omits `reasoning_effort` for these models too).
16
+ - **Plan-tier annotation in the model picker** (`KNOWN_PLANS` in `src/adapter.ts`, synced from the [official plan pages](https://commandcode.ai/docs/plans/go)) — every catalog model is tagged with the minimum plan that includes it, per the official plan model lists: **Go** (33 models), **GOAT** (+3), **Pro** (+14), and **Provider/Max** (+5, the Claude Opus/Fable and Fugu Ultra tier). The picker's `description` leads with the plan label — e.g. `Go · 50% off · Image · 1M`, `Pro · Image · 1M` — so "which plan do I need to actually use this model?" is answerable at a glance instead of only after a 403 `MODEL_NOT_IN_PLAN` on the first request. New `KNOWN_PLANS`, `PLAN_LABELS`, `planLabel()`, and `capabilityDescription()` exports.
17
+ - **Deal and free-model annotations** (`KNOWN_DEALS` in `src/adapter.ts`, synced from the [official pricing page](https://commandcode.ai/docs/resources/pricing-limits#deals)) — the picker now shows active discounts (`75% off`, `50% off`, `98% off`, `99% off`) and the `FREE` badge (Laguna S 2.1) next to the plan tier. **Expiry-aware**: each deal records its official end date; `dealLabel()` hides a deal the moment `Date.now()` passes that date, so an un-updated plugin never shows a lapsed discount as if it were still live (only Gemini 3.7 Flash's 50% off is time-limited — through December 31, 2026; the rest are permanent).
18
+ - **Compact Image + context-window markers** — the picker now shows `Image` for Vision-capable models (the verbose *"Supports image input"* is gone) and the context window in human form (`1M`, `256K`, `262K`) via `formatContext()`. Text-only models show no capability marker at all — plan tier (and deal, if any) plus context is enough.
19
+ - **`capabilityDescription()` is now compact**: `Go · 50% off · Image · 1M`, `Provider · 1M`, `Go · FREE · 256K` — plan tier first, then active deal, then `Image`, then context. Text-only models simply omit the Image marker.
20
+ - **The model picker sorts by plan tier, then name** (`compareByPlan()` in `src/adapter.ts`) — Go models lead the list, then GOAT, Pro, and Provider/Max last, alphabetically within each tier (new `PLAN_ORDER` weights + `compareByPlan()` exports). A Go-plan user sees the models they can actually use at the top instead of hunting through a flat alphabetical catalog; unknown/untracked models sort last.
21
+
22
+ ## [0.1.9] - 2026-08-15
23
+
24
+ ### Added
25
+
26
+ - **Image input for Vision-capable models.** Models the official Command Code registry lists with Vision (see `KNOWN_IMAGE_MODELS` in `src/adapter.ts`, synced from the [official model registry](https://commandcode.ai/docs/reference/cli/models)) now accept attached images: bytes resolve through the dsh attachment service (`ctx.attachments`) and are sent in the official CLI wire shape `{ type: 'image', source: { type: 'base64', media_type, data } }`. Text-only models (e.g. `deepseek/deepseek-v4-flash`) refuse images loudly (`UNSUPPORTED_CONTENT`) rather than silently dropping them; a request carrying images also requires the attachment service. The `CommandCodeAdapterDeps` seam gains an optional `resolveAttachments` resolver (used lazily, only when a request actually has images).
27
+ - **The model picker now shows each Command Code model's image capability** (`listModels`/`resolveModel` return a `description`: *"Supports image input"* / *"Text only"*), so switching in an image-bearing session is informed instead of surprising.
28
+ - **A client half for the bundle** (`dsh.client` + `exports["./client"]` → `lib/client.js`): it wraps the shared `session.selectModel` face and rewrites the harness's image-session `model-unavailable` rejection into a clear, actionable message — `当前会话已包含图片,而模型 <model> 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。` — while passing the error code and details through unchanged. The rejection itself is a deliberate `dsh-host-apiproxy` guard that cannot be relaxed from the plugin side; this makes it friendlier. Both READMEs document the behavior.
29
+
7
30
  ## [0.1.8] - 2026-08-15
8
31
 
9
32
  ### Fixed
@@ -16,6 +39,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
16
39
 
17
40
  - `CommandCodeConnectionOptions` gains `requestTimeoutMs` and `streamIdleTimeoutMs`; both are optional in the `Config` schema and default to 60s/120s. New `DEFAULT_REQUEST_TIMEOUT_MS` / `DEFAULT_STREAM_IDLE_TIMEOUT_MS` exports.
18
41
  - Both READMEs document the new knobs and the transport-failure troubleshooting entry (notably: Node's fetch ignores `HTTP_PROXY`/`HTTPS_PROXY`, so proxy-dependent networks fail here while the browser works).
42
+ - Both READMEs gain an **Updating** section: since the bundle patch layer is read from the installed package at boot, updating the package fixes the patch row automatically; the section covers npm/git/local update commands and the ≤0.1.6 hand-copied-patch caveat.
43
+ - Both READMEs restructure the install docs: **npm is now the recommended install path** (one command, always the latest published release), GitHub moves below it, and the uninstall command is documented (use the scoped name `@mars-sea/dsh-commandcode-provider`, since pnpm records dependencies under the real package name).
19
44
 
20
45
  ## [0.1.7] - 2026-08-15
21
46
 
package/README.md CHANGED
@@ -16,7 +16,11 @@ Unofficial [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harnes
16
16
  - A **`commandcode` provider route** registered on the `llm` service, selectable in the model picker, with the **live model catalog** fetched from `GET {apiBase}/provider/v1/models` (cached at `~/.commandcode/models-cache.json`).
17
17
  - A **Models-page card** ("Command Code") with an API-key field — credentials are stored through the dsh credentials service, same as the DeepSeek card.
18
18
  - **API key resolution** in this order: `config.apiKey` → credential reference `apiKeyEnv` (the web Models page writes it, default `COMMANDCODE_API_KEY`) → the launching environment → the official Command Code CLI auth file (`~/.commandcode/auth.json`, written by `command-code login`).
19
- - **Reasoning-effort support** for the models Command Code's catalog marks as such (e.g. `claude-opus-5`, `gpt-5.5`, `deepseek/deepseek-v4-pro`, …) via `KNOWN_EFFORTS`, matching the official command-code@1.26.0 bundled catalog.
19
+ - **Reasoning-effort support** for the models Command Code's catalog marks as such (e.g. `claude-opus-5`, `gpt-5.5`, `deepseek/deepseek-v4-pro`, `moonshotai/Kimi-K2.5`, …) via `KNOWN_EFFORTS`, matching the official command-code@1.26.0 bundled model table. Reasoning models without selectable effort levels (e.g. `MiniMaxAI/MiniMax-M3`, `moonshotai/Kimi-K3`) still think — Command Code drives their reasoning depth automatically, exactly like the official CLI.
20
+ - **Plan-tier annotation in the model picker**: every Command Code model is tagged with the minimum plan that includes it (`KNOWN_PLANS`, synced from the [official plan pages](https://commandcode.ai/docs/plans/go)) — **Go** (33 models), **GOAT** (+3), **Pro** (+14), or **Provider/Max** (+5: Claude Opus/Fable, Fugu Ultra). The picker's `description` leads with the plan label, e.g. *"Go · 50% off · Image · 1M"*, *"Pro · Image · 1M"*, so you know which plan a model needs before switching — no more 403 `MODEL_NOT_IN_PLAN` surprises. **The list itself is sorted by plan tier** (`compareByPlan()`): Go models first, then GOAT, Pro, Provider/Max, alphabetical within each tier — the models your plan can actually use lead the picker.
21
+ - **Deal and free-model annotations**: active discounts (`75% off`, `50% off`, `98% off`, `99% off`) and the `FREE` badge (Laguna S 2.1) show next to the plan tier (`KNOWN_DEALS`, synced from the [official pricing page](https://commandcode.ai/docs/resources/pricing-limits#deals)). **Expiry-aware**: each deal records its official end date and is hidden the moment it passes — an un-updated plugin never shows a lapsed discount as if it were live (only Gemini 3.7 Flash's 50% off is time-limited, through December 31, 2026; the rest are permanent).
22
+ - **Image + context markers**: the picker shows `Image` for Vision-capable models and the context window in human form (`1M`, `256K`, `262K`); text-only models show neither — plan tier plus context is enough.
23
+ - **Image input for Vision-capable models**: models the official registry lists with Vision (e.g. `claude-sonnet-5`, `gpt-5.4`, `google/gemini-3.5-flash`, …) accept attached images, resolved through the dsh attachment service and sent in the official Command Code wire format. Text-only models (e.g. `deepseek/deepseek-v4-flash`, `zai-org/GLM-5.3`) refuse images loudly rather than silently dropping them.
20
24
 
21
25
  ## Getting an API key
22
26
 
@@ -31,11 +35,19 @@ cmd login # macOS/Linux; native Windows: cmdc login
31
35
 
32
36
  ## Install
33
37
 
34
- ### From GitHub (recommended)
38
+ ### From npm (recommended)
39
+
40
+ The plugin is published to the npm registry as **`@mars-sea/dsh-commandcode-provider`** (the bare name `dsh-commandcode-provider` is taken by an unrelated package):
41
+
42
+ ```sh
43
+ dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
44
+ ```
45
+
46
+ ### From GitHub
35
47
 
36
48
  ```sh
37
49
  # Pin a release tag (recommended — readable and immutable)
38
- dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.6
50
+ dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.8
39
51
  # Or pin any exact commit by its SHA
40
52
  dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#<full-commit-sha>
41
53
  ```
@@ -51,14 +63,6 @@ allowBuilds:
51
63
 
52
64
  and re-run the `add`. Only allow packages whose source you trust (and pin a commit).
53
65
 
54
- ### From npm
55
-
56
- Published as **`@mars-sea/dsh-commandcode-provider`** (the bare name `dsh-commandcode-provider` is taken on the npm registry by an unrelated package):
57
-
58
- ```sh
59
- dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
60
- ```
61
-
62
66
  ### From a local checkout
63
67
 
64
68
  ```sh
@@ -90,6 +94,41 @@ dsh --profile web --dump-config # shows a "# == @mars-sea/dsh-commandco
90
94
  dsh web # or restart your running instance
91
95
  ```
92
96
 
97
+ ## Updating
98
+
99
+ The bundle's patch layer is read from the **installed package** at every boot, so updating the package brings in the fixed patch row automatically — you do not need to hand-edit `cordis.patch.yml` unless you copied its contents into your own profile layer.
100
+
101
+ Update according to how you installed it:
102
+
103
+ ```sh
104
+ # From npm (recommended): always the latest published release
105
+ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider
106
+
107
+ # From GitHub pinned to a tag: point at the new tag
108
+ # (no need to uninstall first — pnpm swaps the pinned revision in place,
109
+ # and the bundle layer is re-read from the installed package on next boot)
110
+ dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.9
111
+
112
+ # From a local checkout: pull the new code, rebuild, restart
113
+ git -C /path/to/dsh-commandcode-provider pull
114
+ npm run build --prefix /path/to/dsh-commandcode-provider
115
+ dsh web
116
+ ```
117
+
118
+ Then restart the web app (`dsh web`, or restart the service). Verify the running version with `dsh --profile web --dump-config` — the layer should show `name: '@mars-sea/dsh-commandcode-provider'`.
119
+
120
+ > **`update` says "Already up to date" but the version did not move (pnpm ≥ 11)?** pnpm 11's `minimumReleaseAge` supply-chain policy can refuse to update to a freshly published version and report "Already up to date" even though a newer release exists. Pin the exact version instead:
121
+ >
122
+ > ```sh
123
+ > dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.1.9
124
+ > ```
125
+ >
126
+ > `add` with an explicit version installs it (and moves your spec to `^0.1.9`). If you trust your registry you can also disable the gate with `pnpm config set minimumReleaseAge 0 --location project` inside the profile directory (or delete the `minimumReleaseAgeExclude` entry pnpm wrote into `pnpm-workspace.yaml`).
127
+
128
+ > **Upgrading from ≤0.1.6** (or a broken hand-edited profile): the installed package's patch layer now carries the corrected, quoted `name`. If you previously *copied* the old patch row into your profile's own `cordis.patch.yml`, that copy still wins over the bundle layer — fix it manually to `name: "@mars-sea/dsh-commandcode-provider"` (see [Troubleshooting](#troubleshooting)) or remove it and let the bundle layer apply.
129
+
130
+ > **To uninstall instead of upgrading** (e.g. you are on a broken pre-0.1.7 tag and want to start clean): `dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider` (the scoped name — pnpm records the dependency under its real package name, so the bare `dsh-commandcode-provider` form does not match). This removes the dependency and its layer; your API key in the dsh credential store and `~/.commandcode/auth.json` are left untouched. Then install the current version with the npm or GitHub command above.
131
+
93
132
  ## Verify it works
94
133
 
95
134
  After restart, in the web UI: **Settings → Models** shows a **Command Code** card; the model picker lists the live catalog under **commandcode** (54 models at the time of writing). Send a message with a model your plan includes — the default `deepseek/deepseek-v4-flash` works on entry-level plans; open-weight models (DeepSeek/Qwen/Kimi/MiniMax) generally do, while frontier models (Claude/GPT/Gemini/Grok) may require Pro/Max plans or on-demand usage (see FAQ).
@@ -154,11 +193,12 @@ The composition-entry config (`cordis.patch.yml` / your profile `cordis.patch.ym
154
193
  - **`MISSING_CREDENTIAL`** — no key anywhere. Store one via the Models page card, export `COMMANDCODE_API_KEY`, set `config.apiKey`, or run `command-code login`. The route stays registered and the catalog stays browsable without a key.
155
194
  - **The Models page card shows "not configured" but requests work** — the key came from `~/.commandcode/auth.json` (the `cmd login` fallback), not the dsh credential store. Paste it into the card once to make the card show as configured; both coexist fine.
156
195
  - **A reasoning model returns no visible text on short requests** — reasoning models (e.g. `deepseek/deepseek-v4-*`) consume output tokens on reasoning first; a small `maxTokens` can be exhausted before any visible text. This is normal.
157
- - **`allowBuilds` errors on `dsh plugin add` from git** — copy the exact package key pnpm printed (with the commit hash) into `pnpm-workspace.yaml` and re-run (see [Install](#from-github-recommended)).
196
+ - **`allowBuilds` errors on `dsh plugin add` from git** — copy the exact package key pnpm printed (with the commit hash) into `pnpm-workspace.yaml` and re-run (see [Install](#from-github)).
158
197
 
159
198
  ## Notes & limitations
160
199
 
161
- - **Text-only for now**: image input throws `UNSUPPORTED_CONTENT` (wiring the attachment service to resolve image bytes is future work). The pi plugin's `MODEL_INPUT_MODALITIES` table is intentionally not claimed.
200
+ - **Image input is model-gated**: only models the official Command Code registry lists with Vision accept images (see the `KNOWN_IMAGE_MODELS` snapshot in `src/adapter.ts`, synced from the [official model registry](https://commandcode.ai/docs/reference/cli/models)). The model picker marks Vision-capable models with *`Image`* (e.g. *"Go · 50% off · Image · 1M"*); text-only models carry no marker, so the capability is visible before you switch. Sending an image to a text-only model throws `UNSUPPORTED_CONTENT`. Command Code's own CLI falls back to a client-side *VISION* side-call for text-only models; this adapter does **not** reproduce that interactive feature — switch to a Vision-capable model instead. Image input also requires the dsh **attachment service** (`ctx.attachments`); without it, requests carrying images throw `UNSUPPORTED_CONTENT`.
201
+ - **Switching to a text-only model in an image-bearing session is rejected by dsh itself** — a harness-level guard (`dsh-host-apiproxy`'s `selectModel` handler) refuses `model-unavailable` when the session history or the pending input already contains images and the target model does not declare `image` input. The rejection is intentional and cannot be relaxed from the plugin side (the picker rows this adapter provides are the input that makes the guard work — a text-only model correctly reports `inputModalities: ['text']`). What this bundle **does** do is make the message friendlier: its client half wraps `session.selectModel` and rewrites that rejection to `当前会话已包含图片,而模型 <model> 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。` (the error code and details pass through unchanged, so any caller switching on `error.code` keeps working). To keep using images, select a model the picker marks *`Image`*, or remove the images from the session first; alternatively an image-routing bundle (e.g. `@deepseek-ai/dsh-llm-image-routing`) can transparently route image turns to a vision fallback.
162
202
  - **No `stop` sequences**: the wire format has no stop field; requests carrying one throw `UNSUPPORTED_OPTION`.
163
203
  - Reasoning blocks are **not replayed** into later turns (matches the official CLI: prior private reasoning must not leak).
164
204
  - Only tool calls with a paired tool result are replayed into the conversation.
package/README.zh-CN.md CHANGED
@@ -16,7 +16,11 @@
16
16
  - **`commandcode` provider 路由**:注册在 `llm` 服务上,可在模型选择器中选择,并带 **实时模型目录**(从 `GET {apiBase}/provider/v1/models` 拉取,缓存于 `~/.commandcode/models-cache.json`)。
17
17
  - **Models 页面卡片**("Command Code")带 API key 输入框——凭据通过 dsh 凭据服务存储,与 DeepSeek 卡片一致。
18
18
  - **API key 解析顺序**:`config.apiKey` → 凭据引用 `apiKeyEnv`(Web Models 页面写入,默认 `COMMANDCODE_API_KEY`)→ 启动环境变量 → 官方 Command Code CLI 认证文件(`~/.commandcode/auth.json`,由 `command-code login` 写入)。
19
- - **推理强度(reasoning-effort)支持**:针对 Command Code 目录中标为推理模型的模型(如 `claude-opus-5`、`gpt-5.5`、`deepseek/deepseek-v4-pro` 等),通过 `KNOWN_EFFORTS` 实现,与官方 command-code@1.26.0 内置目录一致。
19
+ - **推理强度(reasoning-effort)支持**:针对 Command Code 目录中标为推理模型的模型(如 `claude-opus-5`、`gpt-5.5`、`deepseek/deepseek-v4-pro`、`moonshotai/Kimi-K2.5` 等),通过 `KNOWN_EFFORTS` 实现,与官方 command-code@1.26.0 内置模型表一致。没有可选推理档位但仍支持思考的模型(如 `MiniMaxAI/MiniMax-M3`、`moonshotai/Kimi-K3`)同样会思考——由 Command Code 自动控制推理深度,与官方 CLI 行为完全一致。
20
+ - **模型选择器标注套餐档位**:每个 Command Code 模型都标注了包含它的最低套餐(`KNOWN_PLANS`,与[官方套餐页](https://commandcode.ai/docs/plans/go)同步)——**Go**(33 个)、**GOAT**(+3)、**Pro**(+14)、**Provider/Max**(+5:Claude Opus/Fable、Fugu Ultra)。选择器的 `description` 以套餐标签开头,例如 *"Go · 50% off · Image · 1M"*、*"Pro · Image · 1M"*,切换前即可知道该模型需要什么套餐——不再等到第一次请求才收到 403 `MODEL_NOT_IN_PLAN`。**列表本身也按套餐排序**(`compareByPlan()`):Go 模型在最前,随后 GOAT、Pro、Provider/Max,档内按字母序——你当前套餐能用的模型总是排在列表最前面。
21
+ - **折扣与免费标注**:活动折扣(`75% off`、`50% off`、`98% off`、`99% off`)与 `FREE` 徽章(Laguna S 2.1)会显示在套餐旁(`KNOWN_DEALS`,与[官方定价页](https://commandcode.ai/docs/resources/pricing-limits#deals)同步)。**到期感知**:每个折扣记录官方结束日期,一旦过期立即隐藏——插件未更新时也不会把已失效的折扣当成仍在生效(只有 Gemini 3.7 Flash 的 50% off 限时,至 2026 年 12 月 31 日;其余为永久)。
22
+ - **Image 与上下文标注**:支持视觉的模型显示 `Image`,并显示人类可读的上下文长度(`1M`、`256K`、`262K`);纯文本模型两者都不显示——套餐 + 上下文已足够。
23
+ - **支持视觉模型的图片输入**:官方注册表中带 Vision 能力的模型(如 `claude-sonnet-5`、`gpt-5.4`、`google/gemini-3.5-flash` 等)可接收附加图片——通过 dsh 附件服务解析字节,并以官方 Command Code wire 格式发送。纯文本模型(如 `deepseek/deepseek-v4-flash`、`zai-org/GLM-5.3`)会明确拒绝图片而非静默丢弃。
20
24
 
21
25
  ## 获取 API key
22
26
 
@@ -31,11 +35,19 @@ cmd login # macOS/Linux;Windows 原生版:cmdc login
31
35
 
32
36
  ## 安装
33
37
 
34
- ### 从 GitHub 安装(推荐)
38
+ ### 从 npm 安装(推荐)
39
+
40
+ 插件发布在 npm 上,包名 **`@mars-sea/dsh-commandcode-provider`**(npm 上裸名 `dsh-commandcode-provider` 已被无关包占用):
41
+
42
+ ```sh
43
+ dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
44
+ ```
45
+
46
+ ### 从 GitHub 安装
35
47
 
36
48
  ```sh
37
49
  # 推荐:锁定发布 tag(可读、不可变)
38
- dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.6
50
+ dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.9
39
51
  # 或按完整 commit SHA 锁定任意提交
40
52
  dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#<完整-commit-sha>
41
53
  ```
@@ -51,14 +63,6 @@ allowBuilds:
51
63
 
52
64
  然后重新运行 `add`。只允许信任其源码的包(并固定 commit)。
53
65
 
54
- ### 从 npm 安装
55
-
56
- 发布为 **`@mars-sea/dsh-commandcode-provider`**(npm 上裸名 `dsh-commandcode-provider` 已被无关包占用):
57
-
58
- ```sh
59
- dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
60
- ```
61
-
62
66
  ### 从本地检出安装
63
67
 
64
68
  ```sh
@@ -90,6 +94,40 @@ dsh --profile web --dump-config # 会显示 "# == @mars-sea/dsh-command
90
94
  dsh web # 或重启你正在运行的实例
91
95
  ```
92
96
 
97
+ ## 更新
98
+
99
+ bundle 的 patch 层在每次启动时都从**已安装的包**读取,所以更新包本身就会带入修复后的 patch 行——**不需要**手工改 `cordis.patch.yml`(除非你把它的内容复制到了自己 profile 的层里)。
100
+
101
+ 按安装方式选择更新命令:
102
+
103
+ ```sh
104
+ # 从 npm 安装(推荐):总是升到最新发布版本
105
+ dsh plugin --profile web update @mars-sea/dsh-commandcode-provider
106
+
107
+ # 从 GitHub 按 tag 安装:指向新 tag
108
+ # (无需先卸载——pnpm 会就地替换固定的版本,下次启动时 bundle 层会从新安装的包重新读取)
109
+ dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.9
110
+
111
+ # 从本地检出安装:拉取新代码、重新构建、重启
112
+ git -C /path/to/dsh-commandcode-provider pull
113
+ npm run build --prefix /path/to/dsh-commandcode-provider
114
+ dsh web
115
+ ```
116
+
117
+ 然后重启 Web 应用(`dsh web`,或重启服务)。用 `dsh --profile web --dump-config` 验证运行的版本——层里应显示 `name: '@mars-sea/dsh-commandcode-provider'`。
118
+
119
+ > **`update` 提示 "Already up to date" 但版本没变(pnpm ≥ 11)?** pnpm 11 的 `minimumReleaseAge` 供应链安全策略可能拒绝更新到刚发布的新版本,并报 "Already up to date",即使已经有更新的发布。请改用**显式版本**安装:
120
+ >
121
+ > ```sh
122
+ > dsh plugin --profile web add @mars-sea/dsh-commandcode-provider@0.1.9
123
+ > ```
124
+ >
125
+ > `add` 加显式版本会真正装上新版本(并把你的 spec 升为 `^0.1.9`)。如果你信任所用 registry,也可以在 profile 目录里执行 `pnpm config set minimumReleaseAge 0 --location project` 关掉该门禁(或删除 pnpm 写进 `pnpm-workspace.yaml` 的 `minimumReleaseAgeExclude` 条目)。
126
+
127
+ > **从 ≤0.1.6 升级**(或手改坏的 profile):安装包自带的 patch 层现在已经带着修正后的带引号 `name`。如果你之前**手工复制**过旧的 patch 行到你 profile 自己的 `cordis.patch.yml`,那份拷贝会覆盖 bundle 层——请手动改成 `name: "@mars-sea/dsh-commandcode-provider"`(见[故障排查](#故障排查)),或删掉它让 bundle 层生效。
128
+
129
+ > **想卸载而不是升级**(例如正卡在 0.1.7 之前坏掉的 tag,想干净重来):`dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider`(用 **scoped 名**——pnpm 按真实包名记录依赖,裸名 `dsh-commandcode-provider` 对不上)。这会移除依赖及其配置层;你在 dsh 凭据库和 `~/.commandcode/auth.json` 里的 API key 不受影响。然后用上面的 npm 或 GitHub 命令安装当前版本。
130
+
93
131
  ## 验证是否生效
94
132
 
95
133
  重启后,在 Web UI 中:**设置 → Models** 会显示 **Command Code** 卡片;模型选择器会在 **commandcode** 下列出实时目录(撰写本文时有 54 个模型)。发送一条消息,选择你套餐中包含的模型——默认的 `deepseek/deepseek-v4-flash` 适用于入门级套餐;开放权重模型(DeepSeek/Qwen/Kimi/MiniMax)通常都可用,而前沿模型(Claude/GPT/Gemini/Grok)可能需要 Pro/Max 套餐或按需计费(见 FAQ)。
@@ -154,11 +192,12 @@ llm-commandcode:
154
192
  - **`MISSING_CREDENTIAL`** ——任何地方都没有 key。通过 Models 页面卡片存储一个、`export COMMANDCODE_API_KEY`、设置 `config.apiKey`,或运行 `command-code login`。没有 key 时路由保持注册、目录保持可浏览。
155
193
  - **Models 页面卡片显示"未配置"但请求可用** ——key 来自 `~/.commandcode/auth.json`(`cmd login` 兜底),而不是 dsh 凭据存储。把它粘贴到卡片一次即可让卡片显示为已配置;两者可以共存。
156
194
  - **推理模型在短请求下不返回可见文本** ——推理模型(如 `deepseek/deepseek-v4-*`)会先消耗输出 token 进行推理;`maxTokens` 较小时可能在出现可见文本前就用完。这属于正常现象。
157
- - **git 安装时 `dsh plugin add` 报 `allowBuilds` 错误** ——把 pnpm 打印的确切包 key(含 commit hash)复制到 `pnpm-workspace.yaml` 并重新运行(见[从 GitHub 安装(推荐)](#从-github-安装推荐))。
195
+ - **git 安装时 `dsh plugin add` 报 `allowBuilds` 错误** ——把 pnpm 打印的确切包 key(含 commit hash)复制到 `pnpm-workspace.yaml` 并重新运行(见[从 GitHub 安装](#从-github-安装))。
158
196
 
159
197
  ## 注意事项与限制
160
198
 
161
- - **目前仅支持文本**:图片输入会抛出 `UNSUPPORTED_CONTENT`(接入附件服务以解析图片字节是后续工作)。有意不声明 pi 插件的 `MODEL_INPUT_MODALITIES` 表。
199
+ - **图片输入按模型能力限制**:只有官方 Command Code 注册表标记为 Vision 的模型接受图片(见 `src/adapter.ts` 中的 `KNOWN_IMAGE_MODELS` 快照,与[官方模型注册表](https://commandcode.ai/docs/reference/cli/models)同步)。模型选择器会给支持视觉的模型标注 *`Image`*(如 *"Go · 50% off · Image · 1M"*),纯文本模型不带该标记,切换前即可看出能力。向纯文本模型发送图片会抛出 `UNSUPPORTED_CONTENT`。官方 CLI 对纯文本模型会回退到客户端 *VISION* 副调用转文字;本适配器**不**复现该交互功能——请改用支持 Vision 的模型。图片输入还需要 dsh 的**附件服务**(`ctx.attachments`);缺失时携带图片的请求会抛出 `UNSUPPORTED_CONTENT`。
200
+ - **在含图片的会话里切换到纯文本模型会被 dsh 自身拒绝**——这是 harness 层的守卫(`dsh-host-apiproxy` 的 `selectModel` 处理器):当会话历史或待处理输入已包含图片、而目标模型未声明 `image` 输入时,会返回 `model-unavailable`。该拒绝是刻意设计,无法从插件侧放宽(适配器提供的模型行正是让守卫生效的输入——纯文本模型如实上报 `inputModalities: ['text']`)。本 bundle **能**做的是让提示更友好:它的客户端插件会包装 `session.selectModel`,把这条拒绝改写为「当前会话已包含图片,而模型 `<model>` 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。」(错误码与 details 原样透传,按 `error.code` 分支的调用方不受影响)。要继续使用图片,请选择选择器中带 *`Image`* 标记的模型,或先清空会话中的图片;也可安装图片路由 bundle(如 `@deepseek-ai/dsh-llm-image-routing`)把图片轮透明路由到视觉回退模型。
162
201
  - **不支持 `stop` 序列**:线上格式没有 stop 字段;携带它的请求会抛出 `UNSUPPORTED_OPTION`。
163
202
  - 推理块**不会**重放到后续轮次(与官方 CLI 一致:先前的私有推理不得泄漏)。
164
203
  - 只有带配对工具结果的工具调用会被重放到对话中。
package/lib/client.js ADDED
@@ -0,0 +1,57 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@mars-sea/dsh-commandcode-provider",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region src/client/index.ts
8
+ /** Whether a selectModel rejection is the harness's image-session gate. */
9
+ function isImageSessionRejection(result) {
10
+ return !result.result.ok && result.result.error.code === "model-unavailable" && result.result.error.message.includes("does not accept image input");
11
+ }
12
+ /** Wrap the shared sessions API so selectModel failures read friendlier. */
13
+ function withFriendlyImageError(sessions) {
14
+ const selectModel = sessions.selectModel.bind(sessions);
15
+ return {
16
+ ...sessions,
17
+ selectModel: async (payload, signal) => {
18
+ const result = await selectModel(payload, signal);
19
+ if (!isImageSessionRejection(result)) return result;
20
+ const model = result.result.error.details?.model ?? payload.model;
21
+ return {
22
+ ...result,
23
+ result: {
24
+ ...result.result,
25
+ error: {
26
+ ...result.result.error,
27
+ message: `当前会话已包含图片,而模型 ${model} 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。`
28
+ }
29
+ }
30
+ };
31
+ }
32
+ };
33
+ }
34
+ /**
35
+ * Client plugin body: install the selectModel wrapper on the connection's
36
+ * shared api. `inject: ['connection']` gates activation until the connection
37
+ * service is provided (the same pattern the harness's own client plugins
38
+ * use), and `connection.api.sessions` is a stable object the model-selection
39
+ * UI reads fresh on every call — so wrapping it once covers both the /model
40
+ * popup and the composer seat, across reconnects.
41
+ */
42
+ function apply(ctx) {
43
+ const connection = ctx.get("connection");
44
+ if (connection === void 0) return;
45
+ connection.api.sessions = withFriendlyImageError(connection.api.sessions);
46
+ }
47
+ const inject = ["connection"];
48
+ //#endregion
49
+ exports.apply = apply;
50
+ exports.inject = inject;
51
+ exports.isImageSessionRejection = isImageSessionRejection;
52
+ exports.withFriendlyImageError = withFriendlyImageError;
53
+ return module.exports;
54
+ }
55
+ });
56
+
57
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../src/client/index.ts"],"sourcesContent":["/**\n * Browser half of the dsh-commandcode-provider bundle.\n *\n * The host rejects switching to a text-only model while the session already\n * contains images with a harness-level `model-unavailable` error\n * (`dsh-host-apiproxy`'s `session.selectModel` handler). That rejection is\n * intentional and cannot be relaxed from the plugin side — the adapter's\n * `inputModalities` is exactly what makes the guard work. What we CAN do is\n * make the error message friendlier: this client plugin wraps the shared\n * `connection.api.sessions.selectModel` face so a `model-unavailable`\n * rejection shows a clear, actionable hint (with the requested model name)\n * instead of the raw English harness message.\n *\n * The wrapper is deliberately narrow: only the `model-unavailable` code is\n * rewritten, only when the message matches the image-session gate, and only\n * the message text changes — the error code and details pass through\n * untouched so any caller that switches on `error.code` keeps working.\n *\n * The wire types are spelled structurally here (not imported from\n * `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag an\n * extra peer dependency into the package; the shapes are stable and the\n * client build inlines them anyway.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n\n/** The `model-unavailable` error details: provider + model id. */\ninterface ModelUnavailableDetails {\n provider: string\n model: string\n}\n\n/** The narrow slice of the RPC error we need to inspect and rewrite. */\ninterface RpcErrorLike {\n code: string\n message: string\n details?: ModelUnavailableDetails\n}\n\n/**\n * The narrow slice of a unary RPC result we need to inspect and rewrite.\n * The wire shape from `sessions.selectModel` (via `AbstractApiClient.callUnary`)\n * is the full envelope `{ rpcId, result: { ok, error? } }` — the error lives\n * under `result.result`, not at the top level. `RpcResultLike` models that.\n */\ninterface RpcResultLike {\n rpcId: string\n result:\n | { ok: true; value?: unknown }\n | { ok: false; error: RpcErrorLike }\n}\n\n/** One selectModel call: payload in, envelope out. */\ntype SelectModelCall = (\n payload: { sessionId: string; provider: string; model: string; reasoningEffort?: string },\n signal?: AbortSignal,\n) => Promise<RpcResultLike>\n\n/** The shared sessions wire face we wrap. */\ninterface SessionsLike {\n selectModel: SelectModelCall\n}\n\n/** The connection handle shape we read `api.sessions` from. */\ninterface ConnectionLike {\n api: { sessions: SessionsLike }\n}\n\n/** Whether a selectModel rejection is the harness's image-session gate. */\nexport function isImageSessionRejection(\n result: RpcResultLike,\n): result is RpcResultLike & { result: { ok: false; error: RpcErrorLike } } {\n return (\n !result.result.ok &&\n result.result.error.code === 'model-unavailable' &&\n result.result.error.message.includes('does not accept image input')\n )\n}\n\n/** Wrap the shared sessions API so selectModel failures read friendlier. */\nexport function withFriendlyImageError(sessions: SessionsLike): SessionsLike {\n const selectModel = sessions.selectModel.bind(sessions)\n return {\n ...sessions,\n selectModel: async (payload, signal) => {\n const result = await selectModel(payload, signal)\n if (!isImageSessionRejection(result)) return result\n const model = result.result.error.details?.model ?? payload.model\n return {\n ...result,\n result: {\n ...result.result,\n error: {\n ...result.result.error,\n message:\n `当前会话已包含图片,而模型 ${model} 不支持图片输入;`\n + '请选择支持图片的模型,或先移除会话中的图片。',\n },\n },\n }\n },\n }\n}\n\n/**\n * Client plugin body: install the selectModel wrapper on the connection's\n * shared api. `inject: ['connection']` gates activation until the connection\n * service is provided (the same pattern the harness's own client plugins\n * use), and `connection.api.sessions` is a stable object the model-selection\n * UI reads fresh on every call — so wrapping it once covers both the /model\n * popup and the composer seat, across reconnects.\n */\nexport function apply(ctx: Context): void {\n const connection = ctx.get('connection') as ConnectionLike | undefined\n if (connection === undefined) return\n connection.api.sessions = withFriendlyImageError(connection.api.sessions)\n}\n\nexport const inject: readonly string[] = ['connection']\n"],"mappings":";;;;;;;;EAqEA,SAAgB,wBACd,QAC0E;GAC1E,OACE,CAAC,OAAO,OAAO,MACf,OAAO,OAAO,MAAM,SAAS,uBAC7B,OAAO,OAAO,MAAM,QAAQ,SAAS,6BAA6B;EAEtE;;EAGA,SAAgB,uBAAuB,UAAsC;GAC3E,MAAM,cAAc,SAAS,YAAY,KAAK,QAAQ;GACtD,OAAO;IACL,GAAG;IACH,aAAa,OAAO,SAAS,WAAW;KACtC,MAAM,SAAS,MAAM,YAAY,SAAS,MAAM;KAChD,IAAI,CAAC,wBAAwB,MAAM,GAAG,OAAO;KAC7C,MAAM,QAAQ,OAAO,OAAO,MAAM,SAAS,SAAS,QAAQ;KAC5D,OAAO;MACL,GAAG;MACH,QAAQ;OACN,GAAG,OAAO;OACV,OAAO;QACL,GAAG,OAAO,OAAO;QACjB,SACE,iBAAiB,MAAM;OAE3B;MACF;KACF;IACF;GACF;EACF;;;;;;;;;EAUA,SAAgB,MAAM,KAAoB;GACxC,MAAM,aAAa,IAAI,IAAI,YAAY;GACvC,IAAI,eAAe,KAAA,GAAW;GAC9B,WAAW,IAAI,WAAW,uBAAuB,WAAW,IAAI,QAAQ;EAC1E;EAEA,MAAa,SAA4B,CAAC,YAAY"}
package/lib/index.d.ts CHANGED
@@ -2,9 +2,100 @@ import z from "@deepseek-ai/schemastery";
2
2
  import { GenerateOptions, LlmAdapter, LlmModelInfo, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk } from "@deepseek-ai/dsh-llm";
3
3
  import { CredentialRef } from "@deepseek-ai/dsh-credentials";
4
4
  import { Context } from "@deepseek-ai/cordis";
5
+ import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
5
6
  import { CommandDefinition } from "@deepseek-ai/dsh-commands";
6
7
  //#region src/adapter.d.ts
7
8
  declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
9
+ /**
10
+ * Models whose Capabilities include Vision, per the official Command Code
11
+ * model registry (`https://commandcode.ai/docs/reference/cli/models`, generated
12
+ * from the same registry as `cmd --list-models` / the `/model` picker).
13
+ *
14
+ * The Provider API does not expose modality metadata, so this snapshot is the
15
+ * source of truth for image-input gating. Command Code's own CLI falls back to
16
+ * a client-side VISION side-call for text-only models; this adapter does not
17
+ * reproduce that interactive feature, so images sent to a model outside this
18
+ * list are refused loudly (`UNSUPPORTED_CONTENT`) instead of being dropped or
19
+ * sent to a model that cannot read them.
20
+ *
21
+ * Keep in sync with the official registry when new models ship (see the
22
+ * dsh-commandcode-upstream skill).
23
+ */
24
+ declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
25
+ /**
26
+ * Models whose official registry entry lists the `Reasoning` capability but
27
+ * for which the official CLI defines no selectable `reasoning_effort` levels
28
+ * (they are absent from the CLI's model-table efforts; see the authoritative
29
+ * `ZA` table in command-code@1.26.0). The model still thinks — Command Code
30
+ * drives its reasoning depth automatically — so the picker labels them
31
+ * "Supports thinking (auto)" instead of the misleading "Text only", while
32
+ * `KNOWN_EFFORTS` (which mirrors the CLI's effort map exactly) stays the sole
33
+ * source for selectable effort levels.
34
+ *
35
+ * Source: https://commandcode.ai/docs/reference/cli/models (Reasoning flag) and
36
+ * the command-code@1.26.0 bundled model table. Keep in sync via the
37
+ * dsh-commandcode-upstream skill.
38
+ */
39
+ declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
40
+ /**
41
+ * The minimum subscription plan a model is included in, per the official plan
42
+ * pages (`/docs/plans/go`, `/docs/plans/goat`, `/docs/plans/pro`, `/docs/plans/max`
43
+ * and `/docs/resources/pricing-limits`). Each plan's model list is a superset of
44
+ * the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max. Models absent from every
45
+ * plan list (Claude Opus/Fable, Fugu Ultra) are Provider-tier.
46
+ *
47
+ * The Provider API exposes no plan metadata, so this snapshot is the source of
48
+ * truth for the picker's plan annotation — it answers "which plan do I need to
49
+ * actually use this model?" at a glance. Plan labels use the official tier
50
+ * names (`Go`, `GOAT`, `Pro`, `Provider`), with `Max` implying Provider.
51
+ *
52
+ * Keep in sync with the official plan pages when they change (see the
53
+ * dsh-commandcode-upstream skill).
54
+ */
55
+ declare const KNOWN_PLANS: Readonly<Record<string, string>>;
56
+ /** Official display labels for each plan tier. */
57
+ declare const PLAN_LABELS: Readonly<Record<string, string>>;
58
+ /**
59
+ * Plan-tier sort weights, low to high. Models outside the snapshot (unknown
60
+ * plans) sort after every known tier, keeping known models predictable.
61
+ */
62
+ declare const PLAN_ORDER: Readonly<Record<string, number>>;
63
+ /**
64
+ * Comparator for the model picker: sort by plan tier (lowest first), then by
65
+ * model name, then by id as a tiebreak. Models with no known plan sort last.
66
+ */
67
+ declare function compareByPlan(a: {
68
+ id: string;
69
+ name: string;
70
+ }, b: {
71
+ id: string;
72
+ name: string;
73
+ }): number;
74
+ /**
75
+ * Active pricing deals per the official pricing page
76
+ * (`/docs/resources/pricing-limits#deals`). Each entry records the model's
77
+ * promotional label and — critically — when it expires, so the picker never
78
+ * shows a stale discount after the plugin's snapshot has gone out of date.
79
+ *
80
+ * - `expiresAt` is an ISO timestamp. When it is in the past (checked at
81
+ * render time against `Date.now()`), the deal label is hidden until the
82
+ * snapshot is refreshed from the official page. `undefined` means
83
+ * "no expiry" (permanent).
84
+ * - `free` marks models whose requests cost no credits (Laguna S 2.1), shown
85
+ * as a `FREE` badge; it degrades to a plain discount once the deal lapses.
86
+ *
87
+ * Keep in sync with the official pricing page when deals change (see the
88
+ * dsh-commandcode-upstream skill).
89
+ */
90
+ interface KnownDeal {
91
+ /** Promotional label, e.g. "50% off" or "2× usage". */
92
+ label: string;
93
+ /** Deal end date (ISO). `undefined` = permanent / no expiry. */
94
+ expiresAt?: string;
95
+ /** Model is free (requests cost no credits). */
96
+ free?: boolean;
97
+ }
98
+ declare const KNOWN_DEALS: Readonly<Record<string, KnownDeal>>;
8
99
  declare const COMMAND_CODE_CLI_VERSION = "1.26.0";
9
100
  declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
10
101
  declare const DEFAULT_GENERATE_MAX_TOKENS = 64000;
@@ -13,6 +104,32 @@ declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
13
104
  declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
14
105
  /** Stream idle timeout: a generation that stalls this long is a dead connection. */
15
106
  declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 120000;
107
+ /**
108
+ * Official display label for a model's minimum plan, or undefined for models
109
+ * outside the snapshot (e.g. future catalog additions).
110
+ */
111
+ declare function planLabel(modelId: string): string | undefined;
112
+ /**
113
+ * The active deal label for a model, or undefined when the model has no deal
114
+ * or the deal has expired. Expiry is judged against `now` (defaults to
115
+ * `Date.now()`), so a snapshot that has gone stale stops showing its discount
116
+ * the moment the official end date passes — the user never believes a lapsed
117
+ * deal is still live. Permanent deals (no `expiresAt`) never lapse.
118
+ */
119
+ declare function dealLabel(modelId: string, now?: number): string | undefined;
120
+ /**
121
+ * Compact human-readable context window, e.g. `1_000_000 -> "1M"`,
122
+ * `256_000 -> "256K"`, `262_144 -> "256K"` (floor to the nearest K).
123
+ * Returns undefined for unknown/absent sizes.
124
+ */
125
+ declare function formatContext(contextWindow: number | undefined): string | undefined;
126
+ /**
127
+ * Compact one-line summary for the model picker: plan tier, then any active
128
+ * deal (discount or FREE), then `Image` for Vision-capable models, then the
129
+ * context window. Text-only models simply omit the Image marker — "Text only"
130
+ * adds nothing the picker needs to show.
131
+ */
132
+ declare function capabilityDescription(modelId: string, contextWindow?: number, now?: number): string;
16
133
  declare function projectSlugFromPath(pathName: string): string;
17
134
  /** Read a usable Command Code credential from the official CLI auth file. */
18
135
  declare function resolveAuthFileApiKey(): string | undefined;
@@ -29,6 +146,12 @@ interface CommandCodeConnectionOptions {
29
146
  /** Milliseconds a stream may stall before it is treated as a dead connection (default 120s). */
30
147
  streamIdleTimeoutMs: number;
31
148
  }
149
+ /**
150
+ * Resolve the durable attachment service, or undefined when the host does not
151
+ * provide one. Called lazily only when a request actually carries images, so a
152
+ * text-only request never depends on the attachment seam.
153
+ */
154
+ type ResolveAttachments = () => AttachmentStore | undefined;
32
155
  /** Everything the adapter needs beyond the request itself. */
33
156
  interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
34
157
  /** Resolve the current connection facts (fresh per request, settings-aware). */
@@ -37,6 +160,8 @@ interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = Comman
37
160
  resolveApiKey: (connection: C) => Promise<string>;
38
161
  /** HTTP transport override (tests); defaults to the global `fetch`. */
39
162
  fetchImpl?: typeof fetch;
163
+ /** Resolve the optional durable attachment service for image input (tests); defaults to none. */
164
+ resolveAttachments?: ResolveAttachments;
40
165
  }
41
166
  /** Account identity from `/alpha/whoami`. */
42
167
  interface CommandCodeAccount {
@@ -88,6 +213,7 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
88
213
  private readonly deps;
89
214
  private catalog;
90
215
  private readonly fetchImpl;
216
+ private readonly resolveAttachments;
91
217
  constructor(deps: CommandCodeAdapterDeps<C>);
92
218
  /**
93
219
  * Command Code is a metered subscription API: 429 (rate limit) and 5xx
@@ -168,5 +294,5 @@ interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {
168
294
  declare function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions;
169
295
  declare function apply(ctx: Context, config: Config): void;
170
296
  //#endregion
171
- export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeUsageReport, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_EFFORTS, PROVIDER, ResolvedCommandCodeOptions, apply, applyCommands, commandDefinition, inject, name, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
297
+ export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeUsageReport, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PLANS, KNOWN_THINKING_MODELS, PLAN_LABELS, PLAN_ORDER, PROVIDER, type ResolveAttachments, ResolvedCommandCodeOptions, apply, applyCommands, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, name, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
172
298
  //# sourceMappingURL=index.d.ts.map