dsh-packycode-api 1.4.6 → 1.4.8

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
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.8
4
+
5
+ - **Claude Fable 5.2 joins the catalog.** On 2026-11-11 `/api/pricing` lists `claude-fable-5-2` with the `anthropic` endpoint alone, in `cc`, `cc-sale`, `cc-expensive`, `claude-officially` and `aws-q`. It takes the route of the other Claude models: `anthropic-messages` at `https://cf.api.fan`, text and image input, 1,000,000 context and 128,000 output, first in the picker's Claude models. Anthropic's [Claude Fable 5.2 overview](https://platform.claude.com/docs/en/models/fable-5-2/overview) gives the 1M context window (its default and maximum) and 128K output, thinking that is always on and adaptive, `output_config.effort` `low` to `max` as the only depth control, and no `temperature`; so it offers `low` / `medium` / `high` / `xhigh` / `max` and cannot be turned off, like the 5.5 models. A key in `cc` runs it through the Claude Code CLI as the agent; the other Claude groups use HTTP, where it asks for progress-update thinking (`display: "updates"` with the `thinking-display-updates-2026-08-18` beta) as Opus and Sonnet 5.5 do. Its session titles run on `claude-haiku-5-5`. The settings page counts it in each key's Claude coverage, and `scripts/claude-check.mjs` and `scripts/claude-agent-check.mjs` include it; the six-hour `scripts/claude-stability.mjs` window still samples the three 5.5 models, which its request budget is sized for.
6
+ - **MiniMax H3 Image joins the image engines.** On 2026-11-11 `/api/pricing` lists `MiniMax-H3-Image` and `MiniMax-H3-Image-Highspeed` in the `image` group, with the `image-generation` endpoint (`/v1/images/generations` and `/v1/images/edits`). Both are offered in the settings card and the `packycode_image_generate` tool. MiniMax's [text-to-image](https://platform.minimax.io/docs/api-reference/image-generation-h3-generations) and [image edit](https://platform.minimax.io/docs/api-reference/image-generation-h3-edits) references give these paths their own fields, so the request differs from GPT Image's (`requestFields` in `src/images.js`):
7
+ - There is no `quality` or `background`: the only quality is `auto`, and neither field is sent.
8
+ - `output_format: "png"` and `response_format: "b64_json"` are sent, because MiniMax answers with a JPEG link by default and the plugin stores the PNG it is given.
9
+ - `size: auto` goes with `area: "2K"`: the model picks the shape from the prompt or the references, at about 4 million pixels. MiniMax renders its 4K tier when neither a size nor an area is given, at four times 2K's pixels and price and past the size the plugin stores. An explicit size is sent without `area` (MiniMax refuses the two together) and keeps the existing limits (3840 per edge, 8,294,400 pixels), so the 4K tier is not offered.
10
+ - A transparent background and a mask, which MiniMax does not support, are refused before the request instead of being dropped. A tool call that names a MiniMax engine while the saved quality belongs to another engine runs at `auto`; an explicit quality it does not have is still refused.
11
+ - **The image card shows each image key's groups and offers only the engines the key in use is served.** PackyCode lists the MiniMax engines in `image` and not in `image-sale`, and nothing in the card said which group an image key was in. The image key ring now gets the conversation keys' group reader (`createGroupReader` with `modelIds`, new RPC `images/groups`): the gateway's refusal of a model id it cannot route names the key's groups, `/v1/models` lists what the key reaches, and the public `/api/pricing` lists each engine's groups. None is billed, and the browser receives group names, their descriptions and per-engine booleans only. Each key row shows its groups. The model list leaves out engines the key in use is not served, and a note names them. A saved engine the key is not served stays in the list, marked, with a warning and the group it needs; the setting is never changed to another engine. With no reading, nothing is hidden. `/api/pricing` answers by where the request comes from (42 models and 13 groups to a direct connection from mainland China, with no GPT Image or Claude models, against 85 and 26 through a proxy), so an engine is hidden only when neither the groups nor the key's own model list reach it.
12
+ - **The group refusal no longer invites a model switch.** The gateway's routing refusal (`error.code: "model_not_found"`, `分组 image-sale 下模型 MiniMax-H3-Image 无可用渠道`) read as "check model, quality, and image key permissions". In the desktop app, DeepSeek Flash answered a first wording of the new message ("choose another model or use an image key…") by retrying with another engine on its own. The message now says the image key's group does not serve the selected engine, not to retry with another model, and to have the user pick a served engine in the settings or add a key in a group that lists it. The gateway's text is still never shown.
13
+ - `scripts/image-check.mjs` runs an engine without quality levels at `auto`, takes `--size` (`auto` or `WIDTHxHEIGHT`, `1024x1024` by default), and with `--all` reports an engine that fails and goes on to the next; the edit runs on the last engine that produced an image.
14
+ - Validation, 2026-11-11:
15
+ - **Offline:** 569 tests (new for Fable: its catalog entry, protocol, efforts and wire dialect; its native efforts and client envelope over HTTP; `updates` thinking with the beta header, and an explicit `summarized` kept; direct and prepared titles on Haiku; an HTTP turn on a `cc-sale` key; pricing and coverage for its groups. New for MiniMax: the exact generation body for both engines at `auto` (with `area`) and at an explicit size (without it); a quality, a transparent background, a mask and an out-of-range size refused before any request; a multipart edit with two references, `area` and no `quality`, `background` or `mask`; the group refusal's message, without the gateway text and telling the model not to switch engines; the saved quality not inherited by a MiniMax call; a real PNG mask refused and the same edit sent without it; both engines as saved choices with `auto` alone, in the Config and in the built settings card. New for image key groups: the reader over an `image-sale`, an `image` and a rejected key with the image engines as its models; the `images/groups` RPC's projection, its failure message and its invalidation on a key added or removed; in the built card, the group tag on the key row, engines hidden with their note, the saved engine kept and marked with its warning, everything offered again for an `image` key or an unread one, and a rejected key), both rebuilt bundles and the versioned tarball. The suite also passed on Node 22.23.1.
16
+ - **Live, `scripts/claude-check.mjs --transport cli --model claude-fable-5-2` with the test key (`cc` among its groups), Claude Code 2.1.295:** 7 of 7 checks passed in 16 requests with no client-anomaly refusal: a read-only lookup, an approved edit, a denied delete, plan mode through DSH's plan review, a question, an image named `red`, and a title from `claude-haiku-5-5`. `scripts/live-check.mjs --model claude-fable-5-2` read the key's coverage as reaching it and answered the order-total scenario through the registered route.
17
+ - **Live, `scripts/image-check.mjs` with the image key:** while the key was in `image-sale`, both MiniMax engines were refused by the gateway before any upstream (HTTP 503 `model_not_found`) and no image was made. After the user moved it to `image`, 6 of 6 requests returned PNGs with no retry: `MiniMax-H3-Image` generated and edited at `1024x1024` in 11 and 16 s, `MiniMax-H3-Image-Highspeed` in 8 and 10 s, and `MiniMax-H3-Image` at `auto` returned 2048×2048 for both in 15 and 26 s. The generated teapot was blue and the edited one orange. A direct request with `size: auto`, `area: "1K"` and a wide landscape prompt returned 1568×672, so the gateway forwards `area` and the model picks the shape. `gpt-image-2.6-flare` at `low` still generated (34 s) and edited (49 s), reporting `quality: low`; both PNGs came back 1254×1254 for a `1024x1024` request, where the same request returned 1024×1024 on 2026-11-05.
18
+ - **Live, the image key's groups:** the probe was refused with `分组 image 下模型 dsh-packycode-group-probe 无可用渠道`, `/v1/models` listed seven image models, and the built reader took 2.7 s and marked all five engines served, with no key material in the reading.
19
+ - **Not checked:** the settings card in the desktop app (its rendering is covered by the built-bundle test, not by a look at the app), MiniMax's 4K tier, several references in one edit, and a request longer than the plugin's 5-minute timeout (MiniMax's own guidance is 600 s).
20
+ - **Not checked live:** Fable's HTTP route. No stored key is in `cc-sale`, `cc-expensive` or `claude-officially`, and a `cc` key over HTTP is refused. Anthropic enforces preserved thinking's history check on Claude Fable 5.2 for every account, where the 5.5 models enforce it only for accounts created since 2026-08-31, so a session whose earlier messages, system prompt or tools change between turns can be refused there with `The block is bound to a different conversation`.
21
+
22
+ ## 1.4.7
23
+
24
+ - **Images move to GPT Image 2.6.** PackyCode added `gpt-image-2.6-flare` and `gpt-image-2.6-sunburst`: on 2026-11-05 `/api/pricing` lists them in the `image` group beside the 2.5 pair, with the `image-generation` endpoint. They replace `gpt-image-2.5-flare` and `gpt-image-2.5-sunburst` in the settings card, the `packycode_image_generate` tool and `scripts/image-check.mjs`, whose default is now 2.6 Sunburst. OpenAI's [image generation guide](https://developers.openai.com/api/docs/guides/image-generation) gives "GPT Image 2.6 and 2.5 Sunburst and Flare" one quality list, `low` to `max`, and its [Sunburst](https://developers.openai.com/api/docs/models/gpt-image-2.6-sunburst) and [Flare](https://developers.openai.com/api/docs/models/gpt-image-2.6-flare) pages list `/v1/images/edits`, so both keep `auto`, `low`, `medium`, `high`, `xhigh` and `max`, and requests, sizes and edits are sent as before. `gpt-image-2` is unchanged.
25
+ - **A saved 2.5 choice keeps working.** Taking the 2.5 ids out of the Config schema would make the plugin's Config fail to parse for anyone who had picked one (`expected "gpt-image-2" | … but got "gpt-image-2.5-sunburst"`). The schema keeps them as hidden values, and the image preference reads each as the 2.6 engine of the same name with the saved quality (`RETIRED_IMAGE_MODELS` in `src/image-models.js`). The card and the tool offer only the 2.6 pair and refuse a 2.5 id; images that earlier sessions made with 2.5 still show.
26
+ - Validation, 2026-11-05:
27
+ - **Offline:** 558 tests (new: a saved `gpt-image-2.5-flare` or `gpt-image-2.5-sunburst` parses through the plugin Config, stays out of the offered choices, and reads as its 2.6 successor with `max` or `xhigh` kept; a quality-only change on such a setting is written without touching the saved model; a 2.5 id is refused on write; an unknown id still fails the Config; the existing generation, settings and card tests now run on the 2.6 ids), both rebuilt bundles and the versioned tarball. The suite also passed on Node 22.23.1.
28
+ - **Live, `scripts/image-check.mjs` with the image key:** 15 requests to `https://cf.api.fan`, all 1024×1024 PNGs with no retry. 2.6 Flare and 2.6 Sunburst each passed generation and editing at `low`, `xhigh` and `max`, and every response reported the requested quality. `gpt-image-2` passed generation at `low`, and 2.6 Sunburst at `auto` came back reported as `low`. Generate/edit times were 6/9 s at `low`, 14/15 s at `xhigh` and 24/26 s at `max` on Flare, and 13/17 s, 51/64 s and 96/97 s on Sunburst. The edits turned the blue teapot orange and kept the composition. Responses carry no `model` field, so this shows the ids are served on this key, not which engine the gateway runs. Transparent backgrounds, masks, other sizes and the full chat flow in the desktop app were not checked.
29
+ - **The live check sends its images at each model's least effort.** `scripts/live-check.mjs --with-vision` sent its two plain-color images at `high` under the script's 2,048-token output cap, and Hy4 sometimes reasoned past the cap: of 20 direct requests on 2026-11-04, 9 came back incomplete after 55–72 s with no answer, so the check failed though the image had been read. The image turns now run at the least effort the model offers (pi-ai's `getSupportedThinkingLevels`, the rule DSH's titles follow): `off` for DeepSeek Flash, GPT-6.1 Terra and Hy4, `low` for the rest. The text turns keep `high`. A turn the cap cuts off is now reported as such instead of "no finish".
30
+ - Validation, 2026-11-04:
31
+ - **Offline:** 557 tests, both rebuilt bundles (unchanged) and the versioned tarball.
32
+ - **Live, `--model <id> --with-vision` with the test key:** Hy4 named both images at `off` in 3 of 3 runs, in about 15 s per run with the text turn at `high`. DeepSeek Flash and GPT-6.1 Terra at `off`, and GPT-6.1 Sol, GPT-6 Astra, GPT-5.6 Sol and Grok 4.7 at `low`, named both. Gemini 3.8 Flash ran past the script's 90 s timeout in each of 3 runs, once already on the text turn at `high`. Sent through the built route outside the script, 12 requests per effort over two rounds, it named the color in 11 of 12 at both `low` and `high`, with one 120 s stall at each and answers taking up to 93 s at `low` and 75 s at `high`: the `gemini-slb` channel's latency, not the effort. The Claude models (the CLI route on this key) and Gemini 4 Argon (outside the key's groups) were not run.
33
+
3
34
  ## 1.4.6
4
35
 
5
36
  - **Hy4 joins the catalog.** PackyCode added the `hunyuan-officially` group (腾讯混元官方渠道) and Tencent's `hy4` model: on 2026-11-04 `/api/pricing` lists `hy4` in that group alone, with `anthropic`, `openai` and `openai-response` endpoints. `hy4` takes the Responses route: `openai-responses` at `https://cf.api.fan/v1`, text and image input, 1,024,000 context and 64,000 output. TokenHub's [Hunyuan guide](https://cloud.tencent.com/document/product/1823/132252) gives Hy4 a 1024K context, of which at most 960K input, 64K output and image input on all three protocols, and its [model overview](https://cloud.tencent.com/document/product/1823/130079) marks Hy4's Responses support as native rather than converted. The gateway does not check `max_output_tokens` (1,000,000 was accepted), so the output ceiling is the documented 64K, read as 64,000 like DeepSeek's 384K. Hy4 sits after Grok 4.7 in the picker, and the settings panel counts it on a Hunyuan line under each key.
package/README.en.md CHANGED
@@ -31,7 +31,7 @@ The `PackyCode` model group ships with the plugin. No YAML edits and no separate
31
31
  | **Visible balance** | Settings lists each key's remaining, used, limit, reset period, and a usage bar. The composer shows the active key's remaining balance |
32
32
  | **Claude route by group** | The `cc` group only serves the official client, so Claude sessions on `cc` keys are carried out by this device's Claude Code itself, with its own system prompt and tools, while DSH shows every tool call and handles permissions, plan review and questions; `cc-sale`, `cc-expensive`, `claude-officially` and other groups use the plugin's HTTP route with DSH's own agent |
33
33
  | **Web search through PackyCode** | Optional: DSH's `web_search` searches through PackyCode, with a DeepSeek or GPT backend, for every request or for PackyCode models only |
34
- | **Image generation and editing** | Separate image keys, GPT Image 2 / 2.5 Flare / 2.5 Sunburst, model and quality settings, reference edits and original PNG storage; generation and editing verified against the gateway |
34
+ | **Image generation and editing** | Separate image keys, GPT Image 2 / 2.6 Flare / 2.6 Sunburst, model and quality settings, reference edits and original PNG storage; generation and editing verified against the gateway |
35
35
  | **Auto review on your model** | With DSH's built-in “Auto authorization review” switched on, the permission menu's Auto review can review each tool call with jev-latest or DeepSeek Flash; the rules and outcomes stay the official ones |
36
36
  | **Credentials stay on the machine** | Keys live in the DSH credential file. The browser receives labels, state, and balances only |
37
37
 
@@ -48,10 +48,10 @@ You need **DeepSeek Harness** and a **PackyCode (PackyAPI) API key**. For DSH se
48
48
  Open the desktop application's **Plugins** page, click **Add plugin**, and enter the package name **with a version** as the **package name or address**:
49
49
 
50
50
  ```text
51
- dsh-packycode-api@1.4.6
51
+ dsh-packycode-api@1.4.8
52
52
  ```
53
53
 
54
- Choose the **official** install source, then click **Install**. `1.4.6` was the latest release when this was written; the [npm page](https://www.npmjs.com/package/dsh-packycode-api) shows the current one.
54
+ Choose the **official** install source, then click **Install**. `1.4.8` was the latest release when this was written; the [npm page](https://www.npmjs.com/package/dsh-packycode-api) shows the current one.
55
55
 
56
56
  > **Always include the version.** With a bare `dsh-packycode-api`, the install prompt shows the newest version on npm, but DSH's bundled pnpm by default installs only versions published at least 24 hours ago, so you can end up several releases behind with no sign of it in the prompt. With an exact version, pnpm installs that version and adds it to the profile's `minimumReleaseAgeExclude` itself. Afterwards, check **Current version** under **Settings → PackyCode → Plugin version**.
57
57
 
@@ -72,7 +72,7 @@ Choose a model from the `PackyCode` group. Of the thirteen models, Claude runs t
72
72
 
73
73
  The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
74
74
 
75
- **For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.6`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
75
+ **For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.8`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
76
76
 
77
77
  For a local checkout, keep the `link:` prefix: `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`. **Do not pass a bare path or `file:`** — that install copies the whole checkout, `node_modules` included, into the profile, where it then shadows the packages the DSH installation itself provides and the plugin looks uninstalled; see the FAQ. If you already installed that way, uninstall first and reinstall as above.
78
78
 
@@ -161,13 +161,17 @@ Claude generates and edits images through DSH’s `packycode_image_generate`, wi
161
161
 
162
162
  Tool callers can set `model` and `quality` to `settings` to follow the saved preferences; `auto` is an explicit API quality. New images may omit `referenceImages` or pass an empty array. Explicit `operation: edit` requires valid references, and invalid real references are never discarded in favor of new-image generation.
163
163
 
164
- Image 2 accepts `auto / low / medium / high`; both 2.5 models also accept `xhigh / max`. These are Images API `quality` values, separate from text reasoning effort. There is no automatic model substitution, quality downgrade or paid retry. Each call returns one PNG; original bytes are saved under `~/.dsh/packycode-api/images/` (following `DSH_HOME` when set), alongside the chat preview. References and optional PNG masks must belong to the current session; file paths cannot replace attachment references.
164
+ Image 2 accepts `auto / low / medium / high`; both 2.6 models also accept `xhigh / max`. A saved 2.5 Flare or Sunburst setting is read as the 2.6 engine of the same name, with its quality kept. These are Images API `quality` values, separate from text reasoning effort. There is no automatic model substitution, quality downgrade or paid retry. Each call returns one PNG; original bytes are saved under `~/.dsh/packycode-api/images/` (following `DSH_HOME` when set), alongside the chat preview. References and optional PNG masks must belong to the current session; file paths cannot replace attachment references.
165
+
166
+ `MiniMax-H3-Image` and `MiniMax-H3-Image-Highspeed` (faster, at a higher price) use the same two endpoints with different parameters. They have no quality levels: the setting offers only Auto and no `quality` is sent. They do not support transparent backgrounds or masks; a request with either is refused before it is sent, never rendered without it. MiniMax bills by output pixels and renders 4K when no size is given, so for `auto` the plugin adds `area: "2K"`: the model picks the shape from the description or the references, at about 4 million pixels. A size can also be given in the conversation (for example `2720x1536`), within the same limits as GPT Image (3840 per edge, 8.29 million pixels).
167
+
168
+ The image card shows each image key's groups. The model list offers only the engines that the key in use is served, and a note names the ones hidden because its groups do not serve them. A saved engine the key is not served stays in the list, marked as not served, with a warning and the group it needs; it is never changed to another engine for you. PackyCode lists the two MiniMax engines in the `image` group only, for example, so a key in `image-sale` does not see them. When the groups could not be read, nothing is hidden.
165
169
 
166
170
  Click a generated image to open the in-app large preview. **Download original**, immediately left of Close, opens a save-location picker for the complete preserved PNG. Cancelling is harmless; a moved or deleted original reports a retryable error instead of downloading the chat thumbnail. Saved conversations also support this as long as their original file is still on this host.
167
171
 
168
- After adding the image key, `node scripts/image-check.mjs --all --edit` checks all three engines and one edit. Use `--model <id> --edit` for generation and editing on a specific engine, adding `--quality xhigh` or `--quality max` to check a quality. This consumes image quota, saves outputs under `.artifacts/`, and prints only a masked key tail plus the gateway-reported model/quality.
172
+ After adding the image key, `node scripts/image-check.mjs --all --edit` checks every engine and one edit (an engine the key's group does not serve reports its own error and the others still run). Use `--model <id> --edit` for generation and editing on a specific engine, adding `--quality xhigh` or `--quality max` to check a quality. This consumes image quota, saves outputs under `.artifacts/`, and prints only a masked key tail plus the gateway-reported model/quality.
169
173
 
170
- Live checks on 2026-10-15 passed generation and editing at `low` on all three engines. Sunburst also passed both operations at `xhigh` and `max`, with matching quality values in the responses. All 10 requests succeeded without retries and returned 1024×1024 PNGs; visual inspection confirmed the requested color edits with composition preserved. Generate/edit times were about 43/48 seconds at xhigh and 84/91 seconds at max. These are built-client gateway checks; no desktop conversation was created to verify the full chat flow in this run. See the [protocol research and gateway results](docs/research/image-api.md) for scope and evidence.
174
+ Live checks on 2026-11-05 passed generation and editing at `low`, `xhigh` and `max` on both 2.6 Flare and 2.6 Sunburst, with matching quality values in the responses; `gpt-image-2` passed generation at `low`, and the gateway reported Sunburst's `auto` as `low`. All 15 requests succeeded without retries and returned 1024×1024 PNGs; visual inspection confirmed the requested color edits with composition preserved. Generate/edit times were about 14/15 seconds at xhigh and 24/26 seconds at max on Flare, and 51/64 and 96/97 seconds on Sunburst. These are built-client gateway checks; no desktop conversation was created to verify the full chat flow in this run. See the [protocol research and gateway results](docs/research/image-api.md) for scope and evidence.
171
175
 
172
176
  ### Auto review
173
177
 
@@ -230,7 +234,8 @@ The card shows whether DSH's plugin is on, how many reviews this plugin answered
230
234
  | `hy4` (Hy4, Tencent Hunyuan) | HTTP, Responses, text/image input; 1024K context (at most 960K input), 64K output | Can be off; `high` only | `hunyuan-officially` |
231
235
  | `gemini-4-argon` (Gemini 4 Argon) | HTTP, native Gemini streaming, text/image input; 1,048,576 context, 65,536 output | Cannot be off; `low` / `medium` / `high` | `gemini-slb` |
232
236
  | `gemini-3.8-flash` (Gemini 3.8 Flash) | HTTP, native Gemini streaming, text/image input; 1,048,576 input, 65,536 output | Cannot be off; `low` / `medium` / `high` | `gemini-slb` |
233
- | `claude-opus-5-5` | Anthropic Messages, images and native tools: this device's Claude Code CLI as the agent for `cc` keys, HTTP otherwise | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | Claude-group coverage reported in Settings |
237
+ | `claude-fable-5-2` (Claude Fable 5.2) | Anthropic Messages, images and native tools: this device's Claude Code CLI as the agent for `cc` keys, HTTP otherwise; 1M context, 128K output | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | Claude-group coverage reported in Settings |
238
+ | `claude-opus-5-5` | Same | Same | Same |
234
239
  | `claude-sonnet-5-5` | Same | Same | Same |
235
240
  | `claude-haiku-5-5` | Same; also generates Claude session titles | Same | Same |
236
241
 
@@ -268,16 +273,16 @@ DeepSeek Flash uses the gateway's Responses endpoint (`/api/pricing` lists `open
268
273
  - Claude turns on both routes retain the full DSH tool loop, permissions and plan mode, using host search and image tools. Claude titles use Haiku on the same route as the conversation; GPT-6 Astra and GPT-5.6 Sol titles use GPT-6.1 Sol, and DeepSeek V4 Pro titles use DeepSeek Flash.
269
274
  - The 1.2 `claudeCliMode` / `claudeCliCwd` settings do not return: Claude Code's permissions follow the session's DSH permission preset, and its working directory is the session's. The tool named `claude_code` is now the mirror tool that shows Claude Code's tool calls in cc sessions; only the plugin uses it, and it is not the 1.2 delegation tool. `claudeCliPath` applies again and names the CLI (empty means the usual install places, then PATH). 1.3.4's `claudeTransport` is removed, and a value left in a profile is ignored. Separately installed Claude Code is only modified when you click **Update Claude Code CLI** in the CLI card.
270
275
  - Codex Responses requests still need `prompt_cache_key`; the plugin supplies a missing session identity. Other Responses families omit it.
271
- - After building, `node scripts/claude-check.mjs` checks all three Claude models on order totals, support triage, replenishment, read-only inventory tool roundtrips, product color labels, Chinese titles and plan mode (read-only lookup → plan submitted with `exit_plan_mode` → approval), validating the business answers; `--transport cli` runs the cc route's checks instead (`scripts/claude-agent-check.mjs`): in a scratch directory Claude Code reads a file and computes, edits one (approved), deletes one (refused), plans and then implements after review, asks a question, reads an image and names a session, and the files and answers are checked, with the gateway's client-anomaly and anti-abuse refusals counted. `node scripts/claude-stability.mjs --start --hours 6 --key-label <label>` starts a deadline- and request-budget-limited window. Invoke the same script on a schedule to sample again; `--status` reads the record. Each round records its build and scenario version. A test-message policy refusal fails that check while the other scheduled checks continue; real credential or quota failures still stop the window. Checks never change stored credentials or start DSH.
276
+ - After building, `node scripts/claude-check.mjs` checks all four Claude models on order totals, support triage, replenishment, read-only inventory tool roundtrips, product color labels, Chinese titles and plan mode (read-only lookup → plan submitted with `exit_plan_mode` → approval), validating the business answers; `--transport cli` runs the cc route's checks instead (`scripts/claude-agent-check.mjs`): in a scratch directory Claude Code reads a file and computes, edits one (approved), deletes one (refused), plans and then implements after review, asks a question, reads an image and names a session, and the files and answers are checked, with the gateway's client-anomaly and anti-abuse refusals counted. `node scripts/claude-stability.mjs --start --hours 6 --key-label <label>` starts a deadline- and request-budget-limited window. Invoke the same script on a schedule to sample again; `--status` reads the record. Each round records its build and scenario version. A test-message policy refusal fails that check while the other scheduled checks continue; real credential or quota failures still stop the window. Checks never change stored credentials or start DSH.
272
277
  - For continuous dialogs, use `node scripts/claude-conversation-check.mjs --model claude-haiku-5-5 --rounds 3 --key-label <label> --output .artifacts/claude-conversation.json`. One plugin adapter handles independent sessions; each group retains six user turns and full tool history, validates arithmetic, final JSON and unexpected characters, and stops on a failure without discarding evidence. Requests default to 65 seconds and 2048 output tokens. Diagnostic overrides use `--timeout-ms` (5000–300000) and `--max-tokens` (1024–128000). `--capture-wire` records the synthetic business requests, non-authentication headers and partial stream events for exact replay, without keys. Reports include scenario and adapter lifecycle, configured limits, response-header latency and total response time. These options do not alter the plugin's runtime settings.
273
278
 
274
279
  </details>
275
280
 
276
281
  ## Update and uninstall
277
282
 
278
- > **Upgrading from 1.3.2 or earlier:** install `dsh-packycode-api@1.4.6` on the Plugins page instead of clicking **Update plugin** in the old settings card. The old button bypasses pnpm's release-age policy: if the version it installs is less than 24 hours old, the profile refuses every other plugin install until that version is 24 hours old. After the install and a restart, the button is the fixed one, and later updates can use it.
283
+ > **Upgrading from 1.3.2 or earlier:** install `dsh-packycode-api@1.4.8` on the Plugins page instead of clicking **Update plugin** in the old settings card. The old button bypasses pnpm's release-age policy: if the version it installs is less than 24 hours old, the profile refuses every other plugin install until that version is 24 hours old. After the install and a restart, the button is the fixed one, and later updates can use it.
279
284
 
280
- **Update:** open **Settings → PackyCode** and, when a newer version is listed, click **Update plugin**. It installs that exact version into the owning profile with DSH's bundled pnpm and reads `package.json` back before reporting success. The install keeps pnpm's release-age policy instead of overriding it: by default pnpm installs the version at once and records it in the profile's `minimumReleaseAgeExclude`; under a stricter pnpm policy the button says when the update can run. The newer version is read from npm, while pnpm downloads it from the profile's own install source: right after a release that source (a regional mirror in particular) may not have it yet, and the button asks you to update again in a few minutes; when the source cannot be reached, it asks you to check the network or proxy. A local `link:` checkout does not get the button. Then quit and reopen the desktop application. A page refresh does not reload the Host. Reinstalling from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.4.6`); without it you may get a version that is merely the newest one published at least 24 hours ago.
285
+ **Update:** open **Settings → PackyCode** and, when a newer version is listed, click **Update plugin**. It installs that exact version into the owning profile with DSH's bundled pnpm and reads `package.json` back before reporting success. The install keeps pnpm's release-age policy instead of overriding it: by default pnpm installs the version at once and records it in the profile's `minimumReleaseAgeExclude`; under a stricter pnpm policy the button says when the update can run. The newer version is read from npm, while pnpm downloads it from the profile's own install source: right after a release that source (a regional mirror in particular) may not have it yet, and the button asks you to update again in a few minutes; when the source cannot be reached, it asks you to check the network or proxy. A local `link:` checkout does not get the button. Then quit and reopen the desktop application. A page refresh does not reload the Host. Reinstalling from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.4.8`); without it you may get a version that is merely the newest one published at least 24 hours ago.
281
286
 
282
287
  **Coming back from a local-path install, uninstall first.** An update is a `pnpm add`: it rewrites the files inside the package directory and leaves a `node_modules` that an earlier local-path install left there. That older tree keeps shadowing the packages the DSH installation provides, so the plugin row fails to load with no visible reason. **Uninstall** runs `pnpm remove`, which deletes the package directory outright — only a reinstall after that is clean. `node scripts/install-check.mjs` reports whether a profile is in that state.
283
288
 
@@ -293,7 +298,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
293
298
 
294
299
  Output means a leftover tree: a registry install ships no `node_modules`, so this one usually comes from an earlier local-path install (`file:`, or a bare path instead of `link:`). It sits inside the profile directory, and DSH resolves a plugin's imports from inside the profile first, so the plugin gets those old copies — for example `@deepseek-ai/schemastery` 3.18.2, which has no `Schema.volatile()`, so the module throws while it is imported. When an import fails the Loader creates no fiber for that row and no client bundle is composed either, so the model route, the RPC channel, and the settings panel all stay absent.
295
300
 
296
- Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.4.6`), then quit and reopen the desktop application. Uninstall runs `pnpm remove` and deletes the whole package directory; reinstalling or clicking **Update** only rewrites files and keeps the leftover tree. Deleting that `node_modules` by hand and restarting works too.
301
+ Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.4.8`), then quit and reopen the desktop application. Uninstall runs `pnpm remove` and deletes the whole package directory; reinstalling or clicking **Update** only rewrites files and keeps the leftover tree. Deleting that `node_modules` by hand and restarting works too.
297
302
 
298
303
  **Right after a first install, every card in Settings shows `transport failure for /packycode-api/…: HTTP 405`?** The installed version is 1.1.6 or earlier, usually because only the bare package name was entered: the install prompt shows the newest version, but pnpm installs the newest one published at least 24 hours ago (see **Install the plugin**). Those older versions can register their settings channel only after DSH restarts and rebuilds its `connection` service. So right after a first install `/packycode-api` does not exist yet, requests fall through to the static page, and it answers 405; the model route and the settings panel itself load normally. From 1.1.7 on, a first install needs no restart. Check which version was installed:
299
304
 
package/README.md CHANGED
@@ -31,7 +31,7 @@
31
31
  | **余额一眼可见** | 设置页列出每个 Key 的剩余、已用、额度、重置周期和用量条;输入框右侧显示当前 Key 的剩余额度 |
32
32
  | **Claude 按分组选通道** | `cc` 分组只接受官方客户端,这些 Key 的 Claude 会话由本机 Claude Code 自己完成:它用自己的系统提示词和工具做事,DSH 显示每次工具调用,并负责授权、计划审阅和提问;`cc-sale`、`cc-expensive`、`claude-officially` 等分组走插件内 HTTP,由 DSH 自己的 agent 执行 |
33
33
  | **网页搜索可走 PackyCode** | 可选:DSH 的 `web_search` 改用 PackyCode 搜索,后端在 DeepSeek 与 GPT 之间切换;可只对 PackyCode 模型生效 |
34
- | **图片生成与编辑** | 独立图片 Key,支持 GPT Image 2 / 2.5 Flare / 2.5 Sunburst、型号与质量设置、参考图编辑和 PNG 原图保存;已通过网关生图与编辑实测 |
34
+ | **图片生成与编辑** | 独立图片 Key,支持 GPT Image 2 / 2.6 Flare / 2.6 Sunburst、型号与质量设置、参考图编辑和 PNG 原图保存;已通过网关生图与编辑实测 |
35
35
  | **自动审查可换模型** | 开启 DSH 自带的「自动授权审查」后,权限菜单里的「自动审查」可改用 jev-latest 或 DeepSeek Flash 审查每次工具调用,审查规则和处理方式保持官方原样 |
36
36
  | **凭据不离开本机** | Key 只存在 DSH 凭据文件里。浏览器只能看到备注、状态和余额,拿不到 Key |
37
37
 
@@ -48,10 +48,10 @@
48
48
  打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
49
49
 
50
50
  ```text
51
- dsh-packycode-api@1.4.6
51
+ dsh-packycode-api@1.4.8
52
52
  ```
53
53
 
54
- 在**安装源**中选择**官方源**,然后点击**安装**。`1.4.6` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
54
+ 在**安装源**中选择**官方源**,然后点击**安装**。`1.4.8` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
55
55
 
56
56
  > **一定要带版本号。** 只填 `dsh-packycode-api` 时,安装前的提示显示的是 npm 上的最新版,但 DSH 自带的 pnpm 默认只安装发布满 24 小时的版本,实际装上的可能是好几个版本之前的旧版,而安装提示里看不出这一点。填写精确版本时,pnpm 会直接安装这个版本,并自动把它加入 profile 的 `minimumReleaseAgeExclude`。装好后可以在 **设置 → PackyCode → 插件版本** 核对「当前版本」。
57
57
 
@@ -72,7 +72,7 @@ dsh-packycode-api@1.4.6
72
72
 
73
73
  桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
74
74
 
75
- **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.6`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
75
+ **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.8`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
76
76
 
77
77
  本地开发要加 `link:` 前缀:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。**不要直接写路径(或 `file:`)** —— 那样的安装会把整个仓库目录(连同 `node_modules`)复制进 profile,之后它会在 profile 内部挡住 DSH 自带的包,插件看起来像没装,见「常见问题」。已经那样装过的话,先卸载再按上面的方式重装。
78
78
 
@@ -90,7 +90,7 @@ dsh-packycode-api@1.4.6
90
90
 
91
91
  **看余额。** 「账户与 Key」里每个 Key 显示它自己的剩余、已用与额度(额度 = 剩余 + 已用),不限额的 Key 显示「不限额度」;多个 Key 不相加成合计。API Key 只能读到自己的额度,读不到 PackyCode 账号的总余额,账号余额请到 PackyCode 控制台查看。读失败的 Key 会在它那一行注明原因;某个 Key 这次读失败但之前读到过,会继续显示上次的余额并注明读取时间(输入框里显示为 `≈US$…`),重启 DSH 后才清空。输入框右侧只在选中 PackyCode 模型时显示当前 Key 的剩余额度。「刷新」会跳过约 60 秒的缓存。默认模型接口是 `https://cf.api.fan`,余额接口是 `https://www.packyapi.ai`,可在「高级:接口地址」里改。旧默认值 `https://www.packyapi.com` 在国内网络下 DNS 被污染、直连会 `fetch failed`,即使设置里保存的是它,插件也会先试 `.ai`。
92
92
 
93
- **用 Claude。** 直接选择三个 Claude 模型之一。Key 在 `cc` 分组时,这个会话由本机的官方 Claude Code CLI 完成,需要先在 **设置 → PackyCode → Claude Code CLI** 一键安装(或自行 `npm install -g @anthropic-ai/claude-code --allow-scripts=@anthropic-ai/claude-code`),版本过低或有新版本时在同一处点「更新 Claude Code CLI」;`cc-sale`、`cc-expensive`、`claude-officially` 等分组直接走插件内 HTTP,不需要 CLI。「账户与 Key」里每个能用 Claude 的 Key 会标出「Claude 经官方 CLI」或「Claude 经 HTTP」,通道由分组决定,不能手动切换;分组还没读到时先走 CLI。经 HTTP 时工具由 DSH 执行,计划模式、提问、待办、子代理、附件、思考档位和历史都和其它模型一样;经 CLI 时由 Claude Code 自己执行,差别见下方「Claude Code CLI 通道」。
93
+ **用 Claude。** 直接选择四个 Claude 模型之一。Key 在 `cc` 分组时,这个会话由本机的官方 Claude Code CLI 完成,需要先在 **设置 → PackyCode → Claude Code CLI** 一键安装(或自行 `npm install -g @anthropic-ai/claude-code --allow-scripts=@anthropic-ai/claude-code`),版本过低或有新版本时在同一处点「更新 Claude Code CLI」;`cc-sale`、`cc-expensive`、`claude-officially` 等分组直接走插件内 HTTP,不需要 CLI。「账户与 Key」里每个能用 Claude 的 Key 会标出「Claude 经官方 CLI」或「Claude 经 HTTP」,通道由分组决定,不能手动切换;分组还没读到时先走 CLI。经 HTTP 时工具由 DSH 执行,计划模式、提问、待办、子代理、附件、思考档位和历史都和其它模型一样;经 CLI 时由 Claude Code 自己执行,差别见下方「Claude Code CLI 通道」。
94
94
 
95
95
  **网页搜索。** DSH 的 `web_search` 默认调用 DeepSeek 官方接口(用 `DEEPSEEK_API_KEY` 计费)。在 **设置 → PackyCode → 网页搜索** 里可以改走 PackyCode:
96
96
 
@@ -161,13 +161,17 @@ Claude 与其它模型一样,通过 DSH 的 `packycode_image_generate` 工具
161
161
 
162
162
  工具调用可将 `model` 和 `quality` 设为 `settings` 来跟随已保存的图片设置;`auto` 是一个明确的 API 质量档位。新图可省略 `referenceImages` 或传空数组;显式指定 `operation: edit` 时必须提供有效参考图,错误的真实参考图不会被丢弃后改生成新图。
163
163
 
164
- `gpt-image-2` 的质量为 `auto / low / medium / high`;两款 2.5 另支持 `xhigh / max`。质量按 Images API 的 `quality` 原样发送,不作为文字推理强度;不会自动换型号、降档或重试付费请求。输出先采用 PNG,每次一张,聊天预览之外的原图保存在 `~/.dsh/packycode-api/images/`(设置了 `DSH_HOME` 时跟随该目录)。参考图与可选 PNG mask 必须来自当前会话;不能用路径替代附件引用。
164
+ `gpt-image-2` 的质量为 `auto / low / medium / high`;两款 2.6 另支持 `xhigh / max`。设置里原来选的 2.5 Flare / Sunburst 会按同名的 2.6 型号读取,质量不变,无需重新设置。质量按 Images API 的 `quality` 原样发送,不作为文字推理强度;不会自动换型号、降档或重试付费请求。输出先采用 PNG,每次一张,聊天预览之外的原图保存在 `~/.dsh/packycode-api/images/`(设置了 `DSH_HOME` 时跟随该目录)。参考图与可选 PNG mask 必须来自当前会话;不能用路径替代附件引用。
165
+
166
+ `MiniMax-H3-Image` 和 `MiniMax-H3-Image-Highspeed`(出图更快、单价更高)走同样的两个端点,但参数不同:没有质量档位(设置里只有「自动」,请求不带 `quality`),不支持透明背景和 mask,带这两项的请求会在发出前被拒绝,不会丢掉后照常生图。MiniMax 按输出像素计费,不给尺寸时默认出 4K;插件在尺寸为 `auto` 时附带 `area: "2K"`,由模型按描述或参考图选比例、约 400 万像素出图,也可以在对话里指定尺寸(如 `2720x1536`),上限与 GPT Image 相同(单边 3840、829 万像素)。
167
+
168
+ 图片卡片会在每个图片 Key 下显示它的分组。型号列表只列出当前使用的 Key 所在分组支持的型号,并在下方说明哪些型号因分组不支持而隐藏;已保存的型号若不被当前 Key 支持,会保留在列表里并标注「当前 Key 不支持」,同时给出警告和所需分组,不会自动改成别的型号。例如 MiniMax 两个型号在 PackyCode 只开给 `image` 分组,`image-sale` 分组的 Key 看不到它们。分组没读到时不隐藏任何型号。
165
169
 
166
170
  点击生成图可在应用内查看大图,右上角关闭按钮左侧的「下载原图」可选择保存位置,另存本机保留的完整 PNG。取消保存不会报错;若原图已被移走或删除,会提示失败,不会改为下载聊天缩略图。已有会话也可使用此功能,前提是记录对应的本机原图仍存在。
167
171
 
168
- 用户填入图片 Key 后,可运行 `node scripts/image-check.mjs --all --edit` 验证三个型号与一次编辑;用 `--model <型号> --edit` 单独验证某个型号的生图和编辑,搭配 `--quality xhigh` 或 `--quality max` 验证质量。测试会消耗图片额度,输出写入 `.artifacts/`,日志只显示 Key 的掩码尾号,并记录网关回报的型号/质量。
172
+ 用户填入图片 Key 后,可运行 `node scripts/image-check.mjs --all --edit` 验证全部型号与一次编辑(Key 所在分组不提供的型号会单独报错,不影响其它型号);用 `--model <型号> --edit` 单独验证某个型号的生图和编辑,搭配 `--quality xhigh` 或 `--quality max` 验证质量。测试会消耗图片额度,输出写入 `.artifacts/`,日志只显示 Key 的掩码尾号,并记录网关回报的型号/质量。
169
173
 
170
- 2026-10-15 实测:三个型号均通过 `low` 生图与编辑;Sunburst 额外通过 `xhigh`、`max` 生图与编辑,两档响应的 quality 均与请求一致。合计 10 次成功请求,无重试,全部返回 1024×1024 PNG;编辑正确改色并保留构图。xhigh 生成/编辑约 43/48 秒,max 约 84/91 秒。这是已构建图片 client 的网关验证;本轮未新建 DSH 对话验证完整聊天流程。完整范围和记录见[协议核查与网关实测](docs/research/image-api.md)。
174
+ 2026-11-05 实测:2.6 Flare 和 2.6 Sunburst 都通过 `low`、`xhigh`、`max` 的生图与编辑,响应回报的 quality 与请求一致;`gpt-image-2` 通过 `low` 生图,Sunburst 的 `auto` 被网关回报为 `low`。合计 15 次请求全部成功,无重试,均返回 1024×1024 PNG;编辑正确改色并保留构图。Flare 的 xhigh 生图/编辑约 14/15 秒、max 约 24/26 秒;Sunburst 的 xhigh 约 51/64 秒、max 约 96/97 秒。这是已构建图片 client 的网关验证;本轮未新建 DSH 对话验证完整聊天流程。完整范围和记录见[协议核查与网关实测](docs/research/image-api.md)。
171
175
 
172
176
  ### 自动审查
173
177
 
@@ -230,7 +234,8 @@ DSH 自带一个实验性插件「自动授权审查」,默认关闭。开启
230
234
  | `hy4`(Hy4,腾讯混元) | HTTP,Responses,可带图;1024K 上下文(输入最多 960K),64K 输出 | 可关;只有 `high` | `hunyuan-officially` |
231
235
  | `gemini-4-argon`(Gemini 4 Argon) | HTTP,Gemini 原生流式,可带图;1,048,576 上下文,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
232
236
  | `gemini-3.8-flash`(Gemini 3.8 Flash) | HTTP,Gemini 原生流式,可带图;1,048,576 输入,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
233
- | `claude-opus-5-5` | Anthropic Messages,可带图和原生工具:`cc` 分组由本机 Claude Code CLI 当 agent,其它分组走 HTTP | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | 以设置页读取的 Claude 分组覆盖为准 |
237
+ | `claude-fable-5-2`(Claude Fable 5.2) | Anthropic Messages,可带图和原生工具:`cc` 分组由本机 Claude Code CLI 当 agent,其它分组走 HTTP;1M 上下文,128K 输出 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | 以设置页读取的 Claude 分组覆盖为准 |
238
+ | `claude-opus-5-5` | 同上 | 同上 | 同上 |
234
239
  | `claude-sonnet-5-5` | 同上 | 同上 | 同上 |
235
240
  | `claude-haiku-5-5` | 同上;Claude 会话标题固定走它 | 同上 | 同上 |
236
241
 
@@ -268,16 +273,16 @@ DeepSeek Flash 走网关的 Responses 接口(`/api/pricing` 列出 `openai-res
268
273
  - 两条通道的 Claude 对话都保留完整 DSH 工具循环、权限及计划模式;网页搜索和生图使用宿主工具。会话标题由 `claude-haiku-5-5` 生成(与对话走同一条通道);GPT-6 Astra 与 GPT-5.6 Sol 的标题交给 GPT-6.1 Sol,DeepSeek V4 Pro 的标题交给 DeepSeek Flash。
269
274
  - 1.2.x 的 `claudeCliMode`、`claudeCliCwd` 设置不再恢复:Claude Code 的权限由会话的 DSH 权限预设决定,工作目录就是会话目录。名为 `claude_code` 的工具现在是 cc 会话里显示 Claude Code 工具调用的镜像工具,只由插件使用,不是 1.2.x 的委派工具。`claudeCliPath` 重新生效,用于指定 CLI 位置(留空则按常见安装位置和 PATH 查找)。1.3.4 的 `claudeTransport` 已移除,profile 里残留的值会被忽略。除非你在 CLI 卡片里点「更新 Claude Code CLI」,用户独立安装的 Claude Code 不会被改动。
270
275
  - Codex 的 Responses 请求仍需要 `prompt_cache_key`;插件补上缺失的会话标识,其它 Responses 模型省略该字段。
271
- - 构建后可用 `node scripts/claude-check.mjs` 验证三个 Claude 模型的订单核算、工单归类、库存补货、只读库存查询工具回传、商品色卡标注、中文标题和计划模式(只读查询 → `exit_plan_mode` 提交计划 → 审批),并核对业务答案;加 `--transport cli` 则改跑 cc 通道的检查(`scripts/claude-agent-check.mjs`):在临时目录里让 Claude Code 自己读文件计算、改文件(授权批准)、删文件(授权拒绝)、计划审阅后实施、提问、看图和起标题,核对文件和答案,并统计网关的「客户端异常」和「反滥用」拒绝次数。`node scripts/claude-stability.mjs --start --hours 6 --key-label <备注名>` 开始有截止时间和请求上限的采样;定时执行同一脚本继续采样,用 `--status` 查看记录。业务场景版本和每轮构建分别记录;测试消息拒绝将对应检查记为失败,其余预定检查继续,真实凭据或额度错误仍会停止整个窗口。测试不改变已保存的凭据,也不启动 DSH。
276
+ - 构建后可用 `node scripts/claude-check.mjs` 验证四个 Claude 模型的订单核算、工单归类、库存补货、只读库存查询工具回传、商品色卡标注、中文标题和计划模式(只读查询 → `exit_plan_mode` 提交计划 → 审批),并核对业务答案;加 `--transport cli` 则改跑 cc 通道的检查(`scripts/claude-agent-check.mjs`):在临时目录里让 Claude Code 自己读文件计算、改文件(授权批准)、删文件(授权拒绝)、计划审阅后实施、提问、看图和起标题,核对文件和答案,并统计网关的「客户端异常」和「反滥用」拒绝次数。`node scripts/claude-stability.mjs --start --hours 6 --key-label <备注名>` 开始有截止时间和请求上限的采样;定时执行同一脚本继续采样,用 `--status` 查看记录。业务场景版本和每轮构建分别记录;测试消息拒绝将对应检查记为失败,其余预定检查继续,真实凭据或额度错误仍会停止整个窗口。测试不改变已保存的凭据,也不启动 DSH。
272
277
  - 连续对话可用 `node scripts/claude-conversation-check.mjs --model claude-haiku-5-5 --rounds 3 --key-label <备注名> --output .artifacts/claude-conversation.json`:同一插件适配器处理独立会话,每组保留六轮对话和完整工具历史,检查追问中的数值一致性、最终 JSON 和异常字符;遇到失败停止该批次并保留记录。单请求默认 65 秒、输出上限 2048 token;诊断可通过 `--timeout-ms`(5000–300000)和 `--max-tokens`(1024–128000)显式调整。`--capture-wire` 记录这组模拟业务的完整请求、非认证头和中断前的流事件,用于精确重放;不记录 Key。报告包含场景版本、适配器生命周期、所用上限、首响应和完整响应耗时。这些选项不修改插件的运行参数。
273
278
 
274
279
  </details>
275
280
 
276
281
  ## 更新与卸载
277
282
 
278
- > **从 1.3.2 或更早版本升级:** 请在插件页填写 `dsh-packycode-api@1.4.6` 安装,不要点旧版设置页里的「更新插件」。旧按钮安装时会绕过 pnpm 的发布冷却期:装到的版本如果发布还不满 24 小时,这个 profile 在它满 24 小时之前就装不了其他插件。装好并重启后,按钮就换成了修好的版本,以后直接点即可。
283
+ > **从 1.3.2 或更早版本升级:** 请在插件页填写 `dsh-packycode-api@1.4.8` 安装,不要点旧版设置页里的「更新插件」。旧按钮安装时会绕过 pnpm 的发布冷却期:装到的版本如果发布还不满 24 小时,这个 profile 在它满 24 小时之前就装不了其他插件。装好并重启后,按钮就换成了修好的版本,以后直接点即可。
279
284
 
280
- **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本号是从 npm 读到的,pnpm 却从 profile 配置的安装源下载:刚发布的版本,安装源(尤其是国内镜像)可能还没同步到,这时按钮会提示过几分钟再更新;连不上安装源时,会提示检查网络或代理。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.4.6`),否则装上的可能是发布满 24 小时的旧版。
285
+ **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本号是从 npm 读到的,pnpm 却从 profile 配置的安装源下载:刚发布的版本,安装源(尤其是国内镜像)可能还没同步到,这时按钮会提示过几分钟再更新;连不上安装源时,会提示检查网络或代理。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.4.8`),否则装上的可能是发布满 24 小时的旧版。
281
286
 
282
287
  **从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
283
288
 
@@ -293,7 +298,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
293
298
 
294
299
  有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
295
300
 
296
- 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.4.6`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
301
+ 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.4.8`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
297
302
 
298
303
  **首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
299
304
 
@@ -1,4 +1,4 @@
1
- # GPT Image 2 / 2.5 Images API 核查
1
+ # GPT Image 2 / 2.5 / 2.6 与 MiniMax H3 Image:Images API 核查
2
2
 
3
3
  核查日期:2026-10-15(Asia/Shanghai)。协议核查部分只查阅公开资料;末尾另记录用户添加独立图片 Key 并授权测试后的 PackyCode 网关结果。协议能力与实际网关结果分开记录,不记录任何完整凭据。
4
4
 
@@ -75,3 +75,86 @@ JSON 编辑的字段为 `images: [{ image_url: "…" }]` 或 `images: [{ file_id
75
75
  回归测试先复现同样错误,再验证修复:空引用数组表示没有参考图;只把明确的零值、空 ID / 全零 ID 遮罩占位视为未提供。`operation: edit` 仍要求有效参考图,`operation: generate` 拒绝非空参考图,真实无效附件和真实遮罩不能变成新图请求。工具专用的 `model: settings` / `quality: settings` 可明确跟随已保存的设置,`settings` 不会发给网关。
76
76
 
77
77
  修复后,通过已构建的 `createPackyCodeImageTool().execute()` 回放原生参数结构(提示词替换为公共茶壶测试描述),只发出一次 `/v1/images/generations` 请求并成功返回 PNG,耗时 12.267 秒,`size: auto` 的实际图片为 1329×1183。结果可渲染成图片附件,日志和结果均不含完整 Key。该检查使用真实专用图片 Key、真实网关和本地附件测试适配器;它验证了工具参数路径,仍不等同于自动驱动整个桌面对话。记录在本地 `.artifacts/native-image-argument-replay-results.json`。
78
+
79
+ ## GPT Image 2.6 替换 2.5(2026-11-05)
80
+
81
+ 2026-11-05 的 `/api/pricing` 在 `image` 分组列出 `gpt-image-2.6-flare` 和 `gpt-image-2.6-sunburst`,端点为 `image-generation`;2.5 两款仍在列表里。插件的设置卡、`packycode_image_generate` 工具和 `scripts/image-check.mjs` 改为 2.6 两款,`gpt-image-2` 不变。
82
+
83
+ 官方[生图指南](https://developers.openai.com/api/docs/guides/image-generation)把 2.6 和 2.5 的 Sunburst / Flare 写在同一句质量说明里:`low`、`medium`、`high`、`xhigh`、`max`。`auto` 依据同一指南的通用输出设置说明保留。2.6 的 [Sunburst](https://developers.openai.com/api/docs/models/gpt-image-2.6-sunburst) 和 [Flare](https://developers.openai.com/api/docs/models/gpt-image-2.6-flare) 模型页都把 `v1/images/edits` 标为支持、`v1/responses` 标为不支持。尺寸限制、透明背景和 mask 在指南里只写在 2.5 的章节中,2.6 没有单独说明;插件沿用原有尺寸校验,本轮没有单独验证这几项。指南另写 2.6 支持 complexity、markup 编辑、SVG 和 GIF 输出,插件没有接入这些参数。
84
+
85
+ 已保存的 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` 设置:从 Config schema 里直接删掉这两个值,会让选过它们的用户的插件 Config 解析失败。schema 把它们保留为隐藏值,读取图片偏好时映射到同名的 2.6 型号,质量不变;设置卡和工具只提供 2.6,并拒绝 2.5 的 id。
86
+
87
+ 网关实测同前:已构建的图片 client 调用 `https://cf.api.fan`,Key 只从 `packycode-api/image-keys` 读取(尾号 …ECMQ),提示和编辑指令与 2026-10-15 相同,每次 `1024x1024`、PNG、`n: 1`,无重试。
88
+
89
+ | 请求型号 | 请求 quality | 生图 | 编辑 | 响应回报 quality | 生图 / 编辑耗时 |
90
+ | --- | --- | --- | --- | --- | --- |
91
+ | `gpt-image-2` | `low` | 通过 | 未测 | `low` | 11.6 秒 |
92
+ | `gpt-image-2.6-flare` | `low` | 通过 | 通过 | `low` | 5.7 秒(另一轮 6.4)/ 9.0 秒 |
93
+ | `gpt-image-2.6-flare` | `xhigh` | 通过 | 通过 | `xhigh` | 13.7 / 14.7 秒 |
94
+ | `gpt-image-2.6-flare` | `max` | 通过 | 通过 | `max` | 24.3 / 26.1 秒 |
95
+ | `gpt-image-2.6-sunburst` | `auto` | 通过 | 未测 | `low` | 16.1 秒 |
96
+ | `gpt-image-2.6-sunburst` | `low` | 通过 | 通过 | `low` | 13.3 / 16.8 秒 |
97
+ | `gpt-image-2.6-sunburst` | `xhigh` | 通过 | 通过 | `xhigh` | 50.6 / 64.4 秒 |
98
+ | `gpt-image-2.6-sunburst` | `max` | 通过 | 通过 | `max` | 95.9 / 96.6 秒 |
99
+
100
+ 共 15 次请求全部成功,均返回 1024×1024 PNG。抽查的生图为白底蓝色茶壶,编辑后茶壶变橙、构图和白底保留。`auto` 发出后网关回报 `low`。响应没有可识别的 `model` 字段,所以结论仍是这些型号在当前 Key 和网关下可用、回报的 quality 与请求一致,不独立证明网关内部的模型身份。本轮没有在 DSH 桌面对话里跑完整的模型 → 工具 → 聊天预览流程。PNG 保存在本地 `.artifacts/image-check-*/`,不进入 Git 或 npm 包。
101
+
102
+ ## MiniMax H3 Image 接入(2026-11-11)
103
+
104
+ 2026-11-11 的 `/api/pricing` 在 `image` 分组列出 `MiniMax-H3-Image` 和 `MiniMax-H3-Image-Highspeed`,端点类型为 `image-generation`(`/v1/images/generations`,附加路径 `/v1/images/edits`),计费方式 `image_billing: "token"`;两款都不在 `image-sale` 分组。站内公告(`/api/status` 的 `announcements`,同日发布)写两个端点两款都支持、编辑支持 1~16 张参考图、「两款模型参数与限制相同,Highspeed 版本出图更快」。PackyCode 的文档仓库还没有这两个型号的页面。
105
+
106
+ ### MiniMax 官方协议
107
+
108
+ 来源:MiniMax 的[生图指南](https://platform.minimax.io/docs/guides/image-generation)、[文生图参考](https://platform.minimax.io/docs/api-reference/image-generation-h3-generations)和[图片编辑参考](https://platform.minimax.io/docs/api-reference/image-generation-h3-edits)。H3 Image 的原生接口就是这两个 Images API 路径,字段与 GPT Image 不同:
109
+
110
+ | 字段 | MiniMax H3 Image |
111
+ | --- | --- |
112
+ | `prompt` | 必填,最长 32000 字符 |
113
+ | `n` | 1–10,默认 1 |
114
+ | `size` | `auto`(默认)或 `WIDTHxHEIGHT`:面积 655,360–20,000,000 像素,宽高比 1:3 至 3:1,不缩放不取整,越界返回 400;没有 16 的倍数要求 |
115
+ | `area` | `1K` / `2K` / `4K`,**默认 `4K`**;与 `WIDTHxHEIGHT` 的 `size` 同时发送返回 400 |
116
+ | `aspect_ratio` | `auto` 或 `X:Y`;与 `WIDTHxHEIGHT` 的 `size` 同时发送返回 400 |
117
+ | `output_format` | `png` / `jpeg` / `jpg` / `webp`,**默认 `jpeg`** |
118
+ | `response_format` | `url`(默认)或 `b64_json`;`b64_json` 时 PNG 总面积上限 20,000,000 像素 |
119
+ | `quality`、`background` | schema 里没有;指南把透明背景列为不支持 |
120
+ | `mask` | 不支持,返回 400 |
121
+ | 编辑参考图 | multipart 的 `image` 或 `image[]`,1–16 张,JPG / PNG / WEBP / HEIC / HEIF,单张不超过 64 MiB,边长 128–8192 |
122
+
123
+ 响应为 `data[].b64_json`(或 `url`)加每张图的实际 `size` 和 `revised_prompt`,顶层有 `output_format` 和 `usage`,没有 `quality` 字段。官方写服务端最多等待 600 秒,接口不幂等,重试会再次生成并计费。两款参数与限制相同;Highspeed 的输入图片和输出单价是标准版的两倍。
124
+
125
+ ### 插件的请求形状
126
+
127
+ - 两款进入 `IMAGE_MODELS`,质量只有 `auto`,请求不带 `quality` 和 `background`。
128
+ - 显式发送 `output_format: "png"` 和 `response_format: "b64_json"`:插件只保存 PNG 原图,不下载网关给的链接。
129
+ - `size` 为 `auto` 时附带 `area: "2K"`(`MINIMAX_AUTO_AREA`),比例由模型按描述或参考图决定。不这样做,MiniMax 按默认 `4K` 出图,像素和费用是 2K 的四倍,并超过插件保存图片的上限(单边 3840、8,294,400 像素)。显式尺寸不带 `area`(两者同发返回 400)。
130
+ - 显式尺寸沿用原有校验(两边为 16 的倍数、单边不超过 3840、宽高比不超过 3:1、655,360–8,294,400 像素),是 MiniMax 允许范围的子集;4K 档暂不提供。
131
+ - 透明背景和 mask 在发出请求前拒绝,不丢弃后照常生图。工具调用为本次请求点名 MiniMax、而设置里保存的是另一型号的质量时,按 `auto` 处理;显式传入它没有的质量仍被拒绝。
132
+ - 网关的路由拒绝(`error.code: "model_not_found"`,如 `分组 image-sale 下模型 MiniMax-H3-Image 无可用渠道`)改为提示图片 Key 所在分组不提供该型号,并明确要求不要换型号重试、让用户在设置里选型号或换 Key,不回显网关原文。
133
+ - 超时仍是插件原有的 5 分钟,低于官方的 600 秒建议;实测 2K 生图和编辑都在 30 秒内。
134
+
135
+ ### 网关实测(2026-11-11)
136
+
137
+ 已构建的图片 client 调用 `https://cf.api.fan`,Key 只从 `packycode-api/image-keys` 读取(尾号 …ECMQ),提示和编辑指令同前(白底蓝色茶壶;编辑为改成橙色),每次 `n: 1`、PNG,无重试。
138
+
139
+ 这把 Key 当天先在 `image-sale` 分组:两款 MiniMax 型号都被网关在到达上游前拒绝(HTTP 503 `model_not_found`),没有产生图片。用户把它改到 `image` 分组后:
140
+
141
+ | 请求型号 | 请求尺寸 | 生图 | 编辑 | 返回尺寸 | 生图 / 编辑耗时 |
142
+ | --- | --- | --- | --- | --- | --- |
143
+ | `MiniMax-H3-Image` | `1024x1024` | 通过 | 通过 | 1024×1024 | 11.4 / 16.3 秒 |
144
+ | `MiniMax-H3-Image-Highspeed` | `1024x1024` | 通过 | 通过 | 1024×1024 | 8.1 / 9.8 秒 |
145
+ | `MiniMax-H3-Image` | `auto`(附带 `area: "2K"`) | 通过 | 通过 | 2048×2048 | 14.6 / 25.6 秒 |
146
+
147
+ 6 次请求全部返回可解码的 PNG(`data[].b64_json`),尺寸与请求一致。抽查的生图为白底蓝色茶壶,编辑后茶壶为橙色。响应没有 `model` 和 `quality` 字段,带每张图的 `size`、`revised_prompt` 和 `usage`。
148
+
149
+ 另用一次直接请求确认网关转发 `area`:JSON 生图发 `size: "auto"` 加 `area: "1K"` 和一段宽幅风景描述,返回 1568×672 的 PNG(约 105 万像素,比例由模型选定),`usage.output_tokens` 为 4116。multipart 编辑同样转发 `area`(上表第三行的编辑返回 2048×2048)。1K 这张图的输出 token 与 2K 档的起点(约 4096)接近,所以 `auto` 选 2K。本轮没有测透明背景和 mask 的上游行为(插件在发出前拒绝)、4K 档、多张参考图和 5 分钟以上的请求。
150
+
151
+ 同日用同一把 Key 回归 `gpt-image-2.6-flare` 的 `low`:生图 34.1 秒、编辑 48.5 秒,都成功,响应回报 `model: gpt-image-2.6-flare` 和 `quality: low`;请求的是 `1024x1024`,两次返回的 PNG 都是 1254×1254(2026-11-05 同样的请求返回 1024×1024)。GPT Image 的请求字段本次没有改动。
152
+
153
+ ### 图片 Key 的分组(2026-11-11)
154
+
155
+ 图片 Key 所在分组决定它能用哪些型号:`gpt-image-*` 同时列在 `image` 和 `image-sale`,MiniMax 两款只列在 `image`。桌面对话里用 `image-sale` 的 Key 选 MiniMax 生图时,工具返回分组提示,对话模型(DeepSeek Flash)随即自行换了型号重试,这违背「不自动换型号」。因此:
156
+
157
+ - 插件对图片 Key 复用对话 Key 的分组读取器(`createGroupReader`,`modelIds` 传图片型号):向 `/v1/chat/completions` 发一个网关无法路由的模型名,从拒绝文本里读出分组;读 `/v1/models` 得到这把 Key 能用的模型;读公开的 `/api/pricing` 得到每个型号所在的分组。三个读取都不计费、有超时、拒绝重定向,返回浏览器的只有分组名、分组说明和每个型号的布尔值。
158
+ - 实测(Key 在 `image` 分组):拒绝文本为 `分组 image 下模型 dsh-packycode-group-probe 无可用渠道(distributor)`,`/v1/models` 列出 `MiniMax-H3-Image`、`MiniMax-H3-Image-Highspeed`、`gpt-image-2`、`gpt-image-2.5-flare`、`gpt-image-2.5-sunburst`、`gpt-image-2.6-flare`、`gpt-image-2.6-sunburst`,已构建的读取器 2.7 秒读完,五个型号都判为可用。
159
+ - `/api/pricing` 的内容随请求出口变化:同一时刻从本机直连只返回 42 个模型、13 个分组,没有 Claude 和 `gpt-image-*`,也没有 `image-sale`;经代理返回 85 个模型、26 个分组。所以型号是否可用以「分组命中」或「`/v1/models` 列出」任一为准,只有两者都有反证时才判为不可用;定价里查不到型号时,设置卡不显示「需要分组」。
160
+ - 设置卡在每个图片 Key 下显示分组;型号列表只列当前使用的 Key 可用的型号,下方说明被隐藏的型号;已保存的型号若不可用,保留在列表里并标注,另给警告,不自动改设置。分组没读到时不隐藏任何型号。图片请求只用当前使用的 Key,不会换到另一把 Key。
package/lib/client.js CHANGED
@@ -57,7 +57,13 @@ window.__ModuleLoader__.load({
57
57
  imagesQuality_high: "高",
58
58
  imagesQuality_xhigh: "超高(xhigh)",
59
59
  imagesQuality_max: "最高(max)",
60
- imagesQualityHint: "2.5 支持额外的 xhigh / max;质量越高,耗时和费用通常越高。网关实际支持情况需用图片 Key 验证。不会自动换型号或降低质量。",
60
+ imagesQualityHint: "GPT Image 2.6 支持额外的 xhigh / max;质量越高,耗时和费用通常越高。MiniMax H3 Image 没有质量档位,按尺寸计费,未指定尺寸时按 2K(约 400 万像素)出图、比例由模型决定。型号列表只显示当前图片 Key 的分组支持的型号。网关实际支持情况需用图片 Key 验证。不会自动换型号或降低质量。",
61
+ imagesModelUnservedMark: "(当前 Key 不支持)",
62
+ imagesModelUnserved: "当前图片 Key 的分组({groups})不支持 {model},用它生图会被网关拒绝。请换一个型号,或添加分组包含它的图片 Key。",
63
+ imagesModelRequired: "需要分组:{required}。",
64
+ imagesModelKeyRejected: "当前图片 Key 被网关拒绝,{model} 和其它图片型号都用不了。请检查这个 Key,或换用另一个图片 Key。",
65
+ imagesModelsHidden: "当前图片 Key 的分组({groups})不支持这些图片型号,已从上面的列表隐藏:{models}。",
66
+ listSeparator: "、",
61
67
  imagesAddKey: "添加图片 Key",
62
68
  imagesKeyPlaceholder: "粘贴专用的图片 API Key",
63
69
  imagesNoKey: "尚未添加图片 Key。添加后可使用图片工具;对话 Key 不会用于图片请求。",
@@ -136,7 +142,7 @@ window.__ModuleLoader__.load({
136
142
  cliPath: "位置",
137
143
  cliRecheck: "重新检测",
138
144
  cliMissing: "未检测到 Claude Code CLI",
139
- cliMissingHint: "cc 分组的 Key 需要它才能使用 Claude 模型(claude-opus-5-5、claude-sonnet-5-5、claude-haiku-5-5)。其它分组和其它模型不受影响。",
145
+ cliMissingHint: "cc 分组的 Key 需要它才能使用 Claude 模型(claude-fable-5-2、claude-opus-5-5、claude-sonnet-5-5、claude-haiku-5-5)。其它分组和其它模型不受影响。",
140
146
  cliConfiguredMissing: "设置里指定的 CLI 路径不存在:{path}",
141
147
  cliBroken: "找到了 CLI,但无法运行",
142
148
  cliInstall: "安装 Claude Code CLI",
@@ -298,7 +304,13 @@ window.__ModuleLoader__.load({
298
304
  imagesQuality_high: "High",
299
305
  imagesQuality_xhigh: "Extra high (xhigh)",
300
306
  imagesQuality_max: "Maximum (max)",
301
- imagesQualityHint: "The 2.5 engines also support xhigh / max. Higher quality usually takes longer and costs more. Gateway support needs testing with an image key. Models and quality are never changed automatically.",
307
+ imagesQualityHint: "The GPT Image 2.6 engines also support xhigh / max. Higher quality usually takes longer and costs more. MiniMax H3 Image has no quality levels: it is billed by size and, when no size is given, renders at 2K (about 4 million pixels) in a shape the model picks. The model list shows only what the image key in use is served. Gateway support needs testing with an image key. Models and quality are never changed automatically.",
308
+ imagesModelUnservedMark: " (not served by this key)",
309
+ imagesModelUnserved: "The image key in use is in {groups}, which does not serve {model}: the gateway will refuse images made with it. Pick another model, or add an image key in a group that lists it.",
310
+ imagesModelRequired: " It needs: {required}.",
311
+ imagesModelKeyRejected: "The gateway rejected the image key in use, so {model} and every other image model are unavailable. Check this key, or use another image key.",
312
+ imagesModelsHidden: "The image key in use is in {groups}, which does not serve these image models; they are hidden from the list above: {models}.",
313
+ listSeparator: ", ",
302
314
  imagesAddKey: "Add an image key",
303
315
  imagesKeyPlaceholder: "Paste the dedicated image API key",
304
316
  imagesNoKey: "No image key yet. Add one to enable the image tool. Conversation keys are never used for images.",
@@ -377,7 +389,7 @@ window.__ModuleLoader__.load({
377
389
  cliPath: "Location",
378
390
  cliRecheck: "Check again",
379
391
  cliMissing: "Claude Code CLI not found",
380
- cliMissingHint: "Keys in the cc group need it to use the Claude models (claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5). Other groups and models are unaffected.",
392
+ cliMissingHint: "Keys in the cc group need it to use the Claude models (claude-fable-5-2, claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5). Other groups and models are unaffected.",
381
393
  cliConfiguredMissing: "The CLI path set in settings does not exist: {path}",
382
394
  cliBroken: "The CLI was found but could not run",
383
395
  cliInstall: "Install Claude Code CLI",
@@ -1255,7 +1267,36 @@ window.__ModuleLoader__.load({
1255
1267
  }
1256
1268
  //#endregion
1257
1269
  //#region src/client-images.jsx
1258
- const imageModelLabel = (id) => id.replace("gpt-image-", "GPT Image ").replace("-flare", " Flare").replace("-sunburst", " Sunburst");
1270
+ const imageModelLabel = (id) => id.startsWith("MiniMax-") ? id.replaceAll("-", " ") : id.replace("gpt-image-", "GPT Image ").replace("-flare", " Flare").replace("-sunburst", " Sunburst");
1271
+ /** An image key's groups, as the gateway named them. */
1272
+ function ImageKeyGroups({ group, descriptions, loading, t }) {
1273
+ if (group === void 0) return loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1274
+ className: "pkc-meta",
1275
+ children: t("groupsChecking")
1276
+ }) : null;
1277
+ if (group.rejected === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1278
+ className: "pkc-error",
1279
+ children: t("groupsRejected")
1280
+ });
1281
+ const names = Array.isArray(group.groups) ? group.groups : [];
1282
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1283
+ className: "pkc-groups",
1284
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1285
+ className: "pkc-label",
1286
+ children: t("groupsTitle")
1287
+ }), names.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1288
+ className: "pkc-meta",
1289
+ children: group.error === void 0 ? "—" : `${t("groupsUnknown")}: ${group.error}`
1290
+ }) : names.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1291
+ className: "pkc-tag",
1292
+ title: descriptions?.[name] ?? name,
1293
+ children: name
1294
+ }, name))]
1295
+ }), group.stale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1296
+ className: "pkc-meta",
1297
+ children: t("groupsStale")
1298
+ })] });
1299
+ }
1259
1300
  /** Only projections come back from the host; the password draft is cleared on success. */
1260
1301
  function PackyCodeImagesCard({ call, t }) {
1261
1302
  const [state, setState] = react.useState();
@@ -1269,7 +1310,23 @@ window.__ModuleLoader__.load({
1269
1310
  const [balanceError, setBalanceError] = react.useState();
1270
1311
  const [balanceLoading, setBalanceLoading] = react.useState(true);
1271
1312
  const balanceGeneration = react.useRef(0);
1313
+ const [groups, setGroups] = react.useState();
1314
+ const [groupsLoading, setGroupsLoading] = react.useState(true);
1315
+ const groupsGeneration = react.useRef(0);
1272
1316
  const mounted = react.useRef(false);
1317
+ const readGroups = async (force = false) => {
1318
+ if (!mounted.current) return;
1319
+ const generation = ++groupsGeneration.current;
1320
+ setGroupsLoading(true);
1321
+ try {
1322
+ const value = await call("images/groups", { force });
1323
+ if (mounted.current && groupsGeneration.current === generation) setGroups(value);
1324
+ } catch {
1325
+ if (mounted.current && groupsGeneration.current === generation) setGroups(void 0);
1326
+ } finally {
1327
+ if (mounted.current && groupsGeneration.current === generation) setGroupsLoading(false);
1328
+ }
1329
+ };
1273
1330
  const readBalance = async (force = false) => {
1274
1331
  if (!mounted.current) return;
1275
1332
  const generation = ++balanceGeneration.current;
@@ -1292,12 +1349,14 @@ window.__ModuleLoader__.load({
1292
1349
  if (mounted.current) setError(t("imagesLoadFailed"));
1293
1350
  });
1294
1351
  readBalance();
1352
+ readGroups();
1295
1353
  const timer = window.setInterval(() => {
1296
1354
  readBalance();
1297
1355
  }, 6e4);
1298
1356
  return () => {
1299
1357
  mounted.current = false;
1300
1358
  balanceGeneration.current += 1;
1359
+ groupsGeneration.current += 1;
1301
1360
  window.clearInterval(timer);
1302
1361
  };
1303
1362
  }, [call, t]);
@@ -1310,6 +1369,7 @@ window.__ModuleLoader__.load({
1310
1369
  if (mounted.current && endpoint.startsWith("images/keys/")) {
1311
1370
  setBalance(void 0);
1312
1371
  readBalance(true);
1372
+ readGroups(endpoint === "images/keys/add" || endpoint === "images/keys/remove");
1313
1373
  }
1314
1374
  window.dispatchEvent(new Event("packycode-images-updated"));
1315
1375
  return true;
@@ -1327,6 +1387,13 @@ window.__ModuleLoader__.load({
1327
1387
  const disabled = busy || state?.writable !== true;
1328
1388
  const models = state?.models ?? [];
1329
1389
  const qualities = models.find((model) => model.id === state?.imageModel)?.qualities ?? [];
1390
+ const activeGroup = groups?.keys?.find((entry) => entry.id === (state?.keys ?? []).find((key) => key.active)?.id);
1391
+ const unserved = (id) => activeGroup?.coverage?.[id] === false;
1392
+ const groupNames = (activeGroup?.groups ?? []).join(" / ") || "—";
1393
+ const hidden = models.filter((model) => unserved(model.id) && model.id !== state?.imageModel);
1394
+ const offered = models.filter((model) => !hidden.includes(model));
1395
+ const savedUnserved = state !== void 0 && unserved(state.imageModel);
1396
+ const required = groups?.requirements?.[state?.imageModel] ?? [];
1330
1397
  const rows = (state?.keys ?? []).map((entry) => balance?.keys?.find((row) => row.id === entry.id) ?? { id: entry.id });
1331
1398
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1332
1399
  className: "pkc-card",
@@ -1344,6 +1411,7 @@ window.__ModuleLoader__.load({
1344
1411
  disabled: busy || balanceLoading,
1345
1412
  onClick: () => {
1346
1413
  readBalance(true);
1414
+ readGroups(true);
1347
1415
  },
1348
1416
  children: balanceLoading ? t("refreshing") : t("refresh")
1349
1417
  })
@@ -1390,12 +1458,26 @@ window.__ModuleLoader__.load({
1390
1458
  imageQuality: "auto"
1391
1459
  });
1392
1460
  },
1393
- children: models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1461
+ children: offered.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1394
1462
  value: model.id,
1395
- children: imageModelLabel(model.id)
1463
+ children: `${imageModelLabel(model.id)}${unserved(model.id) ? t("imagesModelUnservedMark") : ""}`
1396
1464
  }, model.id))
1397
1465
  })]
1398
1466
  }),
1467
+ savedUnserved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1468
+ className: "pkc-warn",
1469
+ children: `${fill(t(activeGroup.rejected === true ? "imagesModelKeyRejected" : "imagesModelUnserved"), {
1470
+ model: imageModelLabel(state.imageModel),
1471
+ groups: groupNames
1472
+ })}${activeGroup.rejected !== true && required.length > 0 ? fill(t("imagesModelRequired"), { required: required.join(" / ") }) : ""}`
1473
+ }),
1474
+ hidden.length > 0 && activeGroup.rejected !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1475
+ className: "pkc-note",
1476
+ children: fill(t("imagesModelsHidden"), {
1477
+ groups: groupNames,
1478
+ models: hidden.map((model) => imageModelLabel(model.id)).join(t("listSeparator"))
1479
+ })
1480
+ }),
1399
1481
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1400
1482
  className: "pkc-setting",
1401
1483
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -1426,58 +1508,67 @@ window.__ModuleLoader__.load({
1426
1508
  className: "pkc-list",
1427
1509
  children: (state.keys ?? []).map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1428
1510
  className: "pkc-row",
1429
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1430
- className: "pkc-row-top",
1431
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1432
- className: "pkc-row-name",
1433
- children: [rename.renaming?.id === entry.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyRenameForm, {
1434
- rename,
1435
- busy,
1436
- t
1437
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1438
- className: "pkc-name",
1439
- children: entry.label
1440
- }), entry.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1441
- className: "pkc-chip",
1442
- "data-tone": "success",
1443
- children: t("keyActive")
1511
+ children: [
1512
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1513
+ className: "pkc-row-top",
1514
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1515
+ className: "pkc-row-name",
1516
+ children: [rename.renaming?.id === entry.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyRenameForm, {
1517
+ rename,
1518
+ busy,
1519
+ t
1520
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1521
+ className: "pkc-name",
1522
+ children: entry.label
1523
+ }), entry.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1524
+ className: "pkc-chip",
1525
+ "data-tone": "success",
1526
+ children: t("keyActive")
1527
+ })]
1528
+ }), rename.renaming?.id !== entry.id && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1529
+ className: "pkc-actions",
1530
+ children: [
1531
+ !entry.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1532
+ className: "pkc-btn",
1533
+ type: "button",
1534
+ disabled: busy,
1535
+ onClick: () => {
1536
+ mutate("images/keys/select", { id: entry.id });
1537
+ },
1538
+ children: t("keyUse")
1539
+ }),
1540
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1541
+ className: "pkc-btn",
1542
+ "data-variant": "ghost",
1543
+ type: "button",
1544
+ disabled: busy,
1545
+ onClick: () => rename.start(entry),
1546
+ children: t("keyRename")
1547
+ }),
1548
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1549
+ className: "pkc-btn",
1550
+ "data-variant": "danger",
1551
+ type: "button",
1552
+ disabled: busy,
1553
+ onClick: () => {
1554
+ mutate("images/keys/remove", { id: entry.id });
1555
+ },
1556
+ children: t("keyRemove")
1557
+ })
1558
+ ]
1444
1559
  })]
1445
- }), rename.renaming?.id !== entry.id && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1446
- className: "pkc-actions",
1447
- children: [
1448
- !entry.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1449
- className: "pkc-btn",
1450
- type: "button",
1451
- disabled: busy,
1452
- onClick: () => {
1453
- mutate("images/keys/select", { id: entry.id });
1454
- },
1455
- children: t("keyUse")
1456
- }),
1457
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1458
- className: "pkc-btn",
1459
- "data-variant": "ghost",
1460
- type: "button",
1461
- disabled: busy,
1462
- onClick: () => rename.start(entry),
1463
- children: t("keyRename")
1464
- }),
1465
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1466
- className: "pkc-btn",
1467
- "data-variant": "danger",
1468
- type: "button",
1469
- disabled: busy,
1470
- onClick: () => {
1471
- mutate("images/keys/remove", { id: entry.id });
1472
- },
1473
- children: t("keyRemove")
1474
- })
1475
- ]
1476
- })]
1477
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyBalance, {
1478
- row: rows.find((row) => row.id === entry.id),
1479
- t
1480
- })]
1560
+ }),
1561
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageKeyGroups, {
1562
+ group: groups?.keys?.find((group) => group.id === entry.id),
1563
+ descriptions: groups?.descriptions,
1564
+ loading: groupsLoading,
1565
+ t
1566
+ }),
1567
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyBalance, {
1568
+ row: rows.find((row) => row.id === entry.id),
1569
+ t
1570
+ })
1571
+ ]
1481
1572
  }, entry.id))
1482
1573
  }),
1483
1574
  (state.keys ?? []).length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -1937,6 +2028,7 @@ window.__ModuleLoader__.load({
1937
2028
  /** The auto review counts are re-read this often while the page is open. */
1938
2029
  const REVIEW_POLL_MS = 5e3;
1939
2030
  const CLAUDE_MODELS = [
2031
+ "claude-fable-5-2",
1940
2032
  "claude-opus-5-5",
1941
2033
  "claude-sonnet-5-5",
1942
2034
  "claude-haiku-5-5"
@@ -1962,6 +2054,7 @@ window.__ModuleLoader__.load({
1962
2054
  ["Hunyuan", ["hy4"]],
1963
2055
  ["Gemini", ["gemini-4-argon", "gemini-3.8-flash"]],
1964
2056
  ["Claude", [
2057
+ "claude-fable-5-2",
1965
2058
  "claude-opus-5-5",
1966
2059
  "claude-sonnet-5-5",
1967
2060
  "claude-haiku-5-5"
package/lib/index.js CHANGED
@@ -1078,8 +1078,8 @@ const DEFAULT_REASONING_LEVEL = "high";
1078
1078
  * ends early. So every model pins the protocol its vendor serves here.
1079
1079
  *
1080
1080
  * Effort levels come from each vendor's documentation, because an effort the
1081
- * model does not support is refused before the request is sent: Claude Opus
1082
- * 5.5 (platform.claude.com effort docs), GPT-6.1 Sol, GPT-6.1 Terra, GPT-6
1081
+ * model does not support is refused before the request is sent: Claude Fable
1082
+ * 5.2 and Opus 5.5 (platform.claude.com effort docs), GPT-6.1 Sol, GPT-6.1 Terra, GPT-6
1083
1083
  * Astra and GPT-5.6 Sol `reasoning.effort` (developers.openai.com model
1084
1084
  * pages), DeepSeek thinking mode (`low|high|max` effort, or off, for Flash
1085
1085
  * and V4 Pro alike), Grok 4.7
@@ -1125,7 +1125,7 @@ const DEEPSEEK_LEVELS = Object.freeze({
1125
1125
  max: "max"
1126
1126
  });
1127
1127
  /**
1128
- * Claude 5.5 models, GPT-6.1 Sol, and GPT-6 Astra cannot disable reasoning, so `off` is
1128
+ * Claude Fable 5.2, the Claude 5.5 models, GPT-6.1 Sol, and GPT-6 Astra cannot disable reasoning, so `off` is
1129
1129
  * pinned unsupported; GPT-5.6 Sol is kept on the same levels. Claude effort is
1130
1130
  * sent through the native Messages API.
1131
1131
  */
@@ -1315,6 +1315,23 @@ const PACKYCODE_MODELS = Object.freeze([
1315
1315
  maxTokens: 65536,
1316
1316
  thinkingLevelMap: GEMINI_LEVELS
1317
1317
  }),
1318
+ Object.freeze({
1319
+ id: "claude-fable-5-2",
1320
+ name: "Claude Fable 5.2",
1321
+ api: "anthropic-messages",
1322
+ provider: PROVIDER,
1323
+ baseUrl: MESSAGES_BASE,
1324
+ reasoning: true,
1325
+ input: Object.freeze(["text", "image"]),
1326
+ cost: NO_COST,
1327
+ contextWindow: 1e6,
1328
+ maxTokens: 128e3,
1329
+ thinkingLevelMap: ALWAYS_THINKING_LEVELS,
1330
+ compat: Object.freeze({
1331
+ forceAdaptiveThinking: true,
1332
+ supportsTemperature: false
1333
+ })
1334
+ }),
1318
1335
  Object.freeze({
1319
1336
  id: "claude-opus-5-5",
1320
1337
  name: "Claude Opus 5.5",
@@ -1510,13 +1527,15 @@ function parseSelectedGroups(message) {
1510
1527
  * Normalize `/api/pricing`: the groups each covered model is enabled in, and
1511
1528
  * each group's public description.
1512
1529
  * @param payload - the parsed pricing body.
1530
+ * @param modelIds - the models to keep: the conversation catalog's, or the
1531
+ * image engines' for the image key ring.
1513
1532
  * @returns `{ modelGroups, descriptions }`, or undefined when unrecognized.
1514
1533
  */
1515
- function parsePricing(payload) {
1534
+ function parsePricing(payload, modelIds = PACKYCODE_COVERED_MODEL_IDS) {
1516
1535
  if (!record$5(payload) || !Array.isArray(payload.data)) return void 0;
1517
1536
  const modelGroups = {};
1518
1537
  for (const item of payload.data) {
1519
- if (!record$5(item) || !PACKYCODE_COVERED_MODEL_IDS.includes(item.model_name) || !Array.isArray(item.enable_groups)) continue;
1538
+ if (!record$5(item) || !modelIds.includes(item.model_name) || !Array.isArray(item.enable_groups)) continue;
1520
1539
  modelGroups[item.model_name] = [...new Set(item.enable_groups.filter((name) => typeof name === "string" && GROUP_NAME.test(name)))].sort();
1521
1540
  }
1522
1541
  const descriptions = {};
@@ -1534,12 +1553,13 @@ function parsePricing(payload) {
1534
1553
  * Which covered models a key can reach. Either source decides: a model the
1535
1554
  * key's groups are enabled for, or one its model list shows. A model is only
1536
1555
  * marked unreachable on evidence; with neither source it is left unknown.
1537
- * @param options - the key's groups, its model ids, and pricing's model groups.
1556
+ * @param options - the key's groups, its model ids, pricing's model groups,
1557
+ * and the models to decide (the conversation catalog's by default).
1538
1558
  * @returns covered model id → reachable, for the models with evidence.
1539
1559
  */
1540
- function catalogCoverage({ groups, models, modelGroups }) {
1560
+ function catalogCoverage({ groups, models, modelGroups, modelIds = PACKYCODE_COVERED_MODEL_IDS }) {
1541
1561
  const coverage = {};
1542
- for (const id of PACKYCODE_COVERED_MODEL_IDS) {
1562
+ for (const id of modelIds) {
1543
1563
  const enabled = modelGroups?.[id];
1544
1564
  const byGroups = groups === void 0 || enabled === void 0 ? void 0 : enabled.some((name) => groups.includes(name));
1545
1565
  const byModels = models === void 0 ? void 0 : models.has(id);
@@ -1565,10 +1585,12 @@ function errorMessage(text) {
1565
1585
  }
1566
1586
  /**
1567
1587
  * Cached, single-flight group reader.
1568
- * @param options - key listing, host resolution, and transport knobs.
1588
+ * @param options - key listing, host resolution, the models whose coverage
1589
+ * is read (`modelIds`: the conversation catalog's by default, the image
1590
+ * engines' for the image key ring), and transport knobs.
1569
1591
  * @returns `{ read, snapshot, covers, requiredGroups, invalidate }`.
1570
1592
  */
1571
- function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now = Date.now, ttlMs = DEFAULT_GROUP_TTL_MS, retryMs = FAILED_GROUP_RETRY_MS, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS } = {}) {
1593
+ function createGroupReader({ listKeys, getHosts, modelIds = PACKYCODE_COVERED_MODEL_IDS, fetchImpl = ambientFetch, now = Date.now, ttlMs = DEFAULT_GROUP_TTL_MS, retryMs = FAILED_GROUP_RETRY_MS, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS } = {}) {
1572
1594
  if (typeof listKeys !== "function") throw new Error("PackyCode group reader requires listKeys");
1573
1595
  if (typeof getHosts !== "function") throw new Error("PackyCode group reader requires getHosts");
1574
1596
  /** Last good reading per key id, kept when a later read of that key fails. */
@@ -1584,7 +1606,7 @@ function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now =
1584
1606
  for (const host of statusHostOrder(hosts)) try {
1585
1607
  const response = await request(`${host}/api/pricing`, { headers: { Accept: "application/json" } }, signal);
1586
1608
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
1587
- const pricing = parsePricing(await response.json());
1609
+ const pricing = parsePricing(await response.json(), modelIds);
1588
1610
  if (pricing === void 0) throw new Error("unrecognized pricing payload");
1589
1611
  return pricing;
1590
1612
  } catch (error) {
@@ -1669,7 +1691,7 @@ function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now =
1669
1691
  return {
1670
1692
  id: entry.id,
1671
1693
  rejected: true,
1672
- coverage: Object.fromEntries(PACKYCODE_COVERED_MODEL_IDS.map((id) => [id, false])),
1694
+ coverage: Object.fromEntries(modelIds.map((id) => [id, false])),
1673
1695
  error: new RejectedKeyError().message
1674
1696
  };
1675
1697
  }
@@ -1680,7 +1702,8 @@ function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now =
1680
1702
  coverage: catalogCoverage({
1681
1703
  groups: value.groups,
1682
1704
  models: value.models,
1683
- modelGroups: pricing?.modelGroups
1705
+ modelGroups: pricing?.modelGroups,
1706
+ modelIds
1684
1707
  })
1685
1708
  };
1686
1709
  lastGood.set(entry.id, {
@@ -3320,7 +3343,8 @@ const UPDATES_BETA = "thinking-display-updates-2026-08-18";
3320
3343
  const THINKING_DISPLAY = Object.freeze({
3321
3344
  "claude-haiku-5-5": "omitted",
3322
3345
  "claude-sonnet-5-5": "updates",
3323
- "claude-opus-5-5": "updates"
3346
+ "claude-opus-5-5": "updates",
3347
+ "claude-fable-5-2": "updates"
3324
3348
  });
3325
3349
  const IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK.";
3326
3350
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
@@ -6535,7 +6559,7 @@ const IMAGE_MODELS = Object.freeze({
6535
6559
  "medium",
6536
6560
  "high"
6537
6561
  ]),
6538
- "gpt-image-2.5-flare": Object.freeze([
6562
+ "gpt-image-2.6-flare": Object.freeze([
6539
6563
  "auto",
6540
6564
  "low",
6541
6565
  "medium",
@@ -6543,16 +6567,35 @@ const IMAGE_MODELS = Object.freeze({
6543
6567
  "xhigh",
6544
6568
  "max"
6545
6569
  ]),
6546
- "gpt-image-2.5-sunburst": Object.freeze([
6570
+ "gpt-image-2.6-sunburst": Object.freeze([
6547
6571
  "auto",
6548
6572
  "low",
6549
6573
  "medium",
6550
6574
  "high",
6551
6575
  "xhigh",
6552
6576
  "max"
6553
- ])
6577
+ ]),
6578
+ "MiniMax-H3-Image": Object.freeze(["auto"]),
6579
+ "MiniMax-H3-Image-Highspeed": Object.freeze(["auto"])
6554
6580
  });
6555
6581
  const IMAGE_MODEL_IDS = Object.freeze(Object.keys(IMAGE_MODELS));
6582
+ /**
6583
+ * MiniMax's H3 Image models take the Images API paths with their own fields
6584
+ * (platform.minimax.io/docs/api-reference/image-generation-h3-generations and
6585
+ * -h3-edits): no `quality`, `background` or `mask`, a JPEG link unless PNG and
6586
+ * `b64_json` are asked for, and 4K output unless a size is given.
6587
+ */
6588
+ const isMiniMaxImageModel = (model) => typeof model === "string" && model.startsWith("MiniMax-H3-Image");
6589
+ /**
6590
+ * Engines the settings once offered, mapped to the ones that replaced them.
6591
+ * A saved setting still names them, so the Config schema keeps them as hidden
6592
+ * values (dropping them would fail the plugin's Config at load) and reading
6593
+ * the preference moves them to their successor with the same quality.
6594
+ */
6595
+ const RETIRED_IMAGE_MODELS = Object.freeze({
6596
+ "gpt-image-2.5-flare": "gpt-image-2.6-flare",
6597
+ "gpt-image-2.5-sunburst": "gpt-image-2.6-sunburst"
6598
+ });
6556
6599
  const IMAGE_QUALITIES = Object.freeze([
6557
6600
  "auto",
6558
6601
  "low",
@@ -6569,6 +6612,7 @@ function validateImageOptions({ model = DEFAULT_IMAGE_MODEL, quality = "auto", s
6569
6612
  "transparent",
6570
6613
  "opaque"
6571
6614
  ].includes(background)) throw new Error("PackyCode image background is not supported");
6615
+ if (background === "transparent" && isMiniMaxImageModel(model)) throw new Error("PackyCode image transparent background is not supported by the selected model");
6572
6616
  if (size !== "auto") {
6573
6617
  const match = typeof size === "string" ? /^(\d{1,4})x(\d{1,4})$/u.exec(size) : null;
6574
6618
  const width = Number(match?.[1]);
@@ -6586,7 +6630,8 @@ function validateImageOptions({ model = DEFAULT_IMAGE_MODEL, quality = "auto", s
6586
6630
  //#endregion
6587
6631
  //#region src/image-settings.js
6588
6632
  function readImagePreference(value = {}) {
6589
- const model = fieldValue(value.imageModel);
6633
+ const saved = fieldValue(value.imageModel);
6634
+ const model = Object.hasOwn(RETIRED_IMAGE_MODELS, saved) ? RETIRED_IMAGE_MODELS[saved] : saved;
6590
6635
  const imageModel = IMAGE_MODEL_IDS.includes(model) ? model : DEFAULT_IMAGE_MODEL;
6591
6636
  const quality = fieldValue(value.imageQuality);
6592
6637
  return {
@@ -6596,7 +6641,7 @@ function readImagePreference(value = {}) {
6596
6641
  };
6597
6642
  }
6598
6643
  /** No request is sent by a settings or credential mutation. */
6599
- function createImageController({ ring, balance, readPreference, writePreference, isWritable }) {
6644
+ function createImageController({ ring, balance, groups, readPreference, writePreference, isWritable }) {
6600
6645
  let mounted;
6601
6646
  let disposeTool;
6602
6647
  let generation = 0;
@@ -6628,6 +6673,7 @@ function createImageController({ ring, balance, readPreference, writePreference,
6628
6673
  preference,
6629
6674
  status,
6630
6675
  invalidateBalance: () => balance?.invalidate(),
6676
+ invalidateGroups: () => groups?.invalidate(),
6631
6677
  async readBalance({ force = false, signal } = {}) {
6632
6678
  if (balance === void 0) throw new Error("PackyCode image balance is unavailable in this runtime");
6633
6679
  try {
@@ -6643,6 +6689,25 @@ function createImageController({ ring, balance, readPreference, writePreference,
6643
6689
  throw new Error("PackyCode image balance could not be read");
6644
6690
  }
6645
6691
  },
6692
+ /**
6693
+ * Each image key's groups and the image engines they serve. Group names,
6694
+ * their public descriptions and per-engine booleans only; no key material.
6695
+ */
6696
+ async readGroups({ force = false, signal } = {}) {
6697
+ if (groups === void 0) throw new Error("PackyCode image key groups are unavailable in this runtime");
6698
+ try {
6699
+ const [value, state] = await Promise.all([groups.read({
6700
+ force,
6701
+ signal
6702
+ }), ring.list()]);
6703
+ return {
6704
+ ...value,
6705
+ activeId: state.activeId
6706
+ };
6707
+ } catch {
6708
+ throw new Error("PackyCode image key groups could not be read");
6709
+ }
6710
+ },
6646
6711
  reconcile,
6647
6712
  mount(register) {
6648
6713
  mounted = register;
@@ -6693,6 +6758,7 @@ function createImageController({ ring, balance, readPreference, writePreference,
6693
6758
  if (!Object.hasOwn(operations, operation)) throw new Error("Unknown PackyCode image key operation");
6694
6759
  await operations[operation]();
6695
6760
  balance?.invalidate();
6761
+ if (operation === "add" || operation === "remove") groups?.invalidate();
6696
6762
  await reconcile();
6697
6763
  return status();
6698
6764
  }
@@ -6851,8 +6917,33 @@ function decodeImagePng(encoded, maximum = MAX_IMAGE_BYTES) {
6851
6917
  pngDimensions(data);
6852
6918
  return data;
6853
6919
  }
6920
+ /**
6921
+ * The fields each engine's Images API takes. GPT Image gets the validated
6922
+ * options as they are. MiniMax H3 Image has no `quality` or `background`,
6923
+ * answers with a JPEG link unless PNG and `b64_json` are asked for, and
6924
+ * renders 4K unless told otherwise, so `auto` carries the 2K `area` tier.
6925
+ */
6926
+ function requestFields(options, prompt) {
6927
+ if (!isMiniMaxImageModel(options.model)) return {
6928
+ ...options,
6929
+ prompt,
6930
+ n: 1,
6931
+ output_format: "png"
6932
+ };
6933
+ return {
6934
+ model: options.model,
6935
+ prompt,
6936
+ n: 1,
6937
+ size: options.size,
6938
+ ...options.size === "auto" ? { area: "2K" } : {},
6939
+ output_format: "png",
6940
+ response_format: "b64_json"
6941
+ };
6942
+ }
6854
6943
  function imageFailure(status, value) {
6855
- if (value?.error?.code === "moderation_blocked") return "PackyCode image request was blocked by the image safety check; revise the prompt or references";
6944
+ const code = value?.error?.code;
6945
+ if (code === "model_not_found") return "PackyCode image key is in a group that does not serve the selected image model. Do not retry with another model: tell the user to pick a model their key serves under Settings → PackyCode → Images, or to add an image key in a group that lists this one";
6946
+ if (code === "moderation_blocked") return "PackyCode image request was blocked by the image safety check; revise the prompt or references";
6856
6947
  if (status === 401 || status === 403) return "PackyCode image key was rejected; check the separate image key and its model permissions";
6857
6948
  if (status === 429) return "PackyCode image quota or rate limit is unavailable; check the separate image key";
6858
6949
  return `PackyCode image request failed (HTTP ${status}); check model, quality, and image key permissions`;
@@ -6865,6 +6956,7 @@ function createPackyCodeImageClient({ resolveKey, getHost, fetchImpl = ambientFe
6865
6956
  if (prompt === void 0 || prompt.length > 32e3) throw new ImageRequestError("PackyCode image prompt must contain 1-32000 characters");
6866
6957
  if (images !== void 0 && (!Array.isArray(images) || images.length === 0 || images.length > MAX_REFERENCE_IMAGES)) throw new ImageRequestError("PackyCode image edits require 1-16 reference images");
6867
6958
  if (mask !== void 0 && images === void 0) throw new ImageRequestError("PackyCode image mask requires a reference image");
6959
+ if (mask !== void 0 && isMiniMaxImageModel(options.model)) throw new ImageRequestError("PackyCode image masks are not supported by the selected model; omit maskImage or choose a GPT Image model");
6868
6960
  const timeout = AbortSignal.timeout(timeoutMs);
6869
6961
  const requestSignal = signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
6870
6962
  let selected;
@@ -6876,12 +6968,7 @@ function createPackyCodeImageClient({ resolveKey, getHost, fetchImpl = ambientFe
6876
6968
  throw new ImageRequestError("PackyCode image credentials could not be read");
6877
6969
  }
6878
6970
  if (typeof selected?.key !== "string" || selected.key === "") throw new ImageRequestError("PackyCode images need a separate image key; add one under Settings → PackyCode → Images");
6879
- const fields = {
6880
- ...options,
6881
- prompt,
6882
- n: 1,
6883
- output_format: "png"
6884
- };
6971
+ const fields = requestFields(options, prompt);
6885
6972
  let body;
6886
6973
  const headers = {
6887
6974
  authorization: `Bearer ${selected.key}`,
@@ -7095,11 +7182,11 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
7095
7182
  quality: {
7096
7183
  type: "string",
7097
7184
  enum: ["settings", ...IMAGE_QUALITIES],
7098
- description: "Use settings (or omit) to follow the user image setting. auto is an explicit API quality, not a request to follow settings. xhigh and max require a 2.5 model; never downgrade automatically."
7185
+ description: "Use settings (or omit) to follow the user image setting. auto is an explicit API quality, not a request to follow settings. xhigh and max require a GPT Image 2.6 model. MiniMax H3 Image takes no quality: leave it to settings or auto. Never downgrade automatically."
7099
7186
  },
7100
7187
  size: {
7101
7188
  type: "string",
7102
- description: "auto or widthxheight, e.g. 1024x1024; both edges multiples of 16, each <=3840, ratio <=3:1, pixels 655360..8294400."
7189
+ description: "auto or widthxheight, e.g. 1024x1024; both edges multiples of 16, each <=3840, ratio <=3:1, pixels 655360..8294400. On MiniMax H3 Image auto lets the model pick the shape at about 4 million pixels (2K); give a size to fix it, e.g. 2048x2048, 2720x1536 or 1536x2720."
7103
7190
  },
7104
7191
  background: {
7105
7192
  type: "string",
@@ -7107,7 +7194,8 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
7107
7194
  "auto",
7108
7195
  "opaque",
7109
7196
  "transparent"
7110
- ]
7197
+ ],
7198
+ description: "transparent needs a GPT Image model; MiniMax H3 Image does not support it."
7111
7199
  },
7112
7200
  referenceImages: {
7113
7201
  type: "array",
@@ -7116,7 +7204,7 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
7116
7204
  },
7117
7205
  maskImage: {
7118
7206
  oneOf: [{ type: "null" }, referenceSchema],
7119
- description: "Omit or use null for new images and ordinary reference-image edits, including refining a sketch. Only supply an actual PNG mask provided for masking, matching the first reference and under 4 MB; alpha=0 marks the area to edit. Do not copy an ordinary reference into this field or invent a mask."
7207
+ description: "Omit or use null for new images and ordinary reference-image edits, including refining a sketch. Only supply an actual PNG mask provided for masking, matching the first reference and under 4 MB; alpha=0 marks the area to edit. Do not copy an ordinary reference into this field or invent a mask. MiniMax H3 Image does not support masks."
7120
7208
  }
7121
7209
  },
7122
7210
  output: {
@@ -7153,10 +7241,12 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
7153
7241
  const repeatedNonPng = repeatedIndex >= 0 && images[repeatedIndex].mediaType !== "image/png";
7154
7242
  const masks = maskImage === void 0 || repeatedNonPng ? void 0 : await readReferences([maskImage], attachments, messages, signal);
7155
7243
  if (!getPreference().imageEnabled) throw new ImageRequestError("PackyCode images are disabled in settings");
7244
+ const model = args.model === "settings" ? preference.imageModel : args.model ?? preference.imageModel;
7245
+ const savedQuality = IMAGE_MODELS[model]?.length === 1 ? IMAGE_MODELS[model][0] : preference.imageQuality;
7156
7246
  const result = await client.request({
7157
7247
  ...args,
7158
- model: args.model === "settings" ? preference.imageModel : args.model ?? preference.imageModel,
7159
- quality: args.quality === "settings" ? preference.imageQuality : args.quality ?? preference.imageQuality
7248
+ model,
7249
+ quality: args.quality === "settings" ? savedQuality : args.quality ?? savedQuality
7160
7250
  }, {
7161
7251
  signal,
7162
7252
  images,
@@ -8407,6 +8497,7 @@ const TITLE_MODELS = Object.freeze({
8407
8497
  "deepseek-v4-pro": "deepseek-flash",
8408
8498
  "gpt-6-astra": "gpt-6.1-sol",
8409
8499
  "gpt-5.6-sol": "gpt-6.1-sol",
8500
+ "claude-fable-5-2": "claude-haiku-5-5",
8410
8501
  "claude-opus-5-5": "claude-haiku-5-5",
8411
8502
  "claude-sonnet-5-5": "claude-haiku-5-5"
8412
8503
  });
@@ -8734,6 +8825,7 @@ function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claude,
8734
8825
  balance.invalidate();
8735
8826
  groups?.invalidate();
8736
8827
  images?.invalidateBalance?.();
8828
+ images?.invalidateGroups?.();
8737
8829
  return status();
8738
8830
  },
8739
8831
  "claude-cli/status": (payload) => cli().status({ force: payload?.force === true }),
@@ -8764,6 +8856,10 @@ function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claude,
8764
8856
  force: payload?.force === true,
8765
8857
  signal
8766
8858
  }),
8859
+ "images/groups": (payload, signal) => imageSupport().readGroups({
8860
+ force: payload?.force === true,
8861
+ signal
8862
+ }),
8767
8863
  "images/original": (payload, signal) => originals().info(payload, signal),
8768
8864
  "images/original/chunk": (payload, signal) => originals().chunk(payload, signal),
8769
8865
  "images/update": (payload) => imageSupport().update(payload),
@@ -9834,7 +9930,8 @@ const Config = z.object({
9834
9930
  /** Only the picker is filtered; an existing session can keep its model. */
9835
9931
  hiddenModels: live(z.array(z.string()).default([])),
9836
9932
  imageEnabled: live(z.boolean().default(true)),
9837
- imageModel: live(z.union([...IMAGE_MODEL_IDS]).default(DEFAULT_IMAGE_MODEL)),
9933
+ /** A retired engine still parses, hidden, and is read as its successor. */
9934
+ imageModel: live(z.union([...IMAGE_MODEL_IDS, ...Object.keys(RETIRED_IMAGE_MODELS).map((id) => z.const(id).hidden())]).default(DEFAULT_IMAGE_MODEL)),
9838
9935
  imageQuality: live(z.union([...IMAGE_QUALITIES]).default("auto")),
9839
9936
  /** Path to the Claude Code CLI; empty means "the usual install places, then PATH". */
9840
9937
  claudeCliPath: live(z.string().default("")),
@@ -10109,9 +10206,16 @@ function apply(ctx, config) {
10109
10206
  set: void 0
10110
10207
  };
10111
10208
  const dshHome = typeof process.env.DSH_HOME === "string" && process.env.DSH_HOME !== "" ? process.env.DSH_HOME : join(homedir(), ".dsh");
10209
+ /** The image keys' groups, read when the settings card asks: an engine may be listed in `image` and not in `image-sale`. */
10210
+ const imageGroups = createGroupReader({
10211
+ listKeys: () => imageRing.secrets(),
10212
+ getHosts: hosts,
10213
+ modelIds: IMAGE_MODEL_IDS
10214
+ });
10112
10215
  const images = createImageController({
10113
10216
  ring: imageRing,
10114
10217
  balance: imageBalance,
10218
+ groups: imageGroups,
10115
10219
  readPreference: () => source() ?? {},
10116
10220
  writePreference: (patch) => imagePreference.set(patch),
10117
10221
  isWritable: () => imagePreference.writable === true && typeof imagePreference.set === "function"
@@ -10181,6 +10285,7 @@ function apply(ctx, config) {
10181
10285
  balance.invalidate();
10182
10286
  imageBalance.invalidate();
10183
10287
  groups.invalidate();
10288
+ imageGroups.invalidate();
10184
10289
  modelSelection.reconcile();
10185
10290
  images.reconcile().catch(() => void 0);
10186
10291
  search.reconcile();
@@ -10286,4 +10391,4 @@ function apply(ctx, config) {
10286
10391
  });
10287
10392
  }
10288
10393
  //#endregion
10289
- export { AUTO_PRESET, AUTO_REVIEW_FLASH, AUTO_REVIEW_JEV, AUTO_REVIEW_MODELS, AUTO_REVIEW_MODEL_NAMES, AUTO_REVIEW_SESSION, AUTO_REVIEW_TIMEOUT_MS, CHANNEL, CLAUDE_INSTALL_COMMAND, CLAUDE_MIN_VERSION, CLAUDE_MISSING_MESSAGE, CLAUDE_NPM_LATEST_URL, CLAUDE_NPM_PACKAGE, CLAUDE_NPM_UPDATE_COMMAND, CLAUDE_PERMISSION_MODES, CLAUDE_SELF_UPDATE_COMMAND, CLI_ONLY_GROUPS, CLI_PROJECT_DIR_NAME, CLI_SESSION_IDLE_MS, Config, DEFAULT_API_HOST, DEFAULT_AUTO_REVIEW_MODEL, DEFAULT_CLAUDE_CONFIG_DIR, DEFAULT_IMAGE_MODEL, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DIAGNOSTICS_FORMAT, DIAGNOSTICS_LIMITS, DISPLAY_NAME, DSH_AUTO_REVIEW_ROW, HTTP_CLAUDE_GROUPS, IMAGE_MODELS, IMAGE_MODEL_IDS, IMAGE_QUALITIES, IMAGE_TIMEOUT_MS, IMAGE_TOOL_NAME, JEV_ALLOW_THRESHOLD, JEV_HIGH_RISK_LIMIT, JEV_LIMITS, JEV_QUESTIONS, LEGACY_STATUS_HOSTS, MAX_IMAGE_BYTES, MAX_LIVE_CLI_SESSIONS, MIRROR_TOOL, PACKYCODE_AUTO_SEARCH_PROVIDER_ID, PACKYCODE_CLASSIFIER_MODELS, PACKYCODE_COVERED_MODEL_IDS, PACKYCODE_MODELS, PACKYCODE_MODEL_IDS, PACKYCODE_SEARCH_PROVIDER_ID, PLAN_TOOL, PLUGIN_ID, PROVIDER, PackyCodeKeyRing, SEARCH_BACKENDS, SEARCH_BACKEND_MODELS, SEARCH_MODES, SETTINGS_NAMESPACE, TEST_MESSAGE_POLICY_CODE, answerChunks, answersFor, apply, approvalReason, assertApiKey, baseUrlOf, buildCliArgs, buildCliEnvironment, buildSearchRequest, buildTranscript, catalogCoverage, classifyCliOutdated, classifyCliUnavailable, classifyClientPolicy, classifyGatewayFailure, classifyTestMessagePolicy, classifyUpstreamServerError, claudeBinaryCandidates, claudePermissionMode, claudeTransportFor, cliErrorMessage, compactionSummary, createAutoReviewController, createBalanceReader, createClaudeAgentBridge, createClaudeCliManager, createClaudeCliTransport, createClaudeLatestReader, createClaudeToolNames, createDiagnostics, createGroupReader, createImageController, createJevReviewer, createKeyChooser, createModelSelectionController, createPackyCodeAutoSearchProvider, createPackyCodeImageClient, createPackyCodeImageTool, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, decideFromJev, decodeImagePng, describeRequest, dshSystemText, explainCliClientPolicyFailure, explainCliOutdated, explainCliUnavailable, explainClientPolicyFailure, explainNpmFailure, explainTestMessagePolicyFailure, failureChunk, guardKeyRotation, inject, isClaudeCliOutdated, isNpmClaudeInstall, isOfficialReviewRequest, jevState, locateClaudeBinary, locateNpm, messageFingerprint, name, normalizeFailureMessage, normalizeHost, officialAnswer, orderKeysFor, parseClaudeVersion, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, planAnswer, planContinuation, pngDimensions, presentClaudeCall, presentClaudeResult, questionPrompt, questionsFor, readAutoReviewChoice, readHiddenModels, readImagePreference, readReviewSections, readSearchChoice, refusalMessage, resolveSpawnCommand, responseFingerprint, sessionUuid, splitAtLastAnswer, startClaudeCliSession, statusHostOrder, summarizeSessionEvents, textResponse, titlePrompt, toCliHistory, toolResultText, validateImageOptions };
10394
+ export { AUTO_PRESET, AUTO_REVIEW_FLASH, AUTO_REVIEW_JEV, AUTO_REVIEW_MODELS, AUTO_REVIEW_MODEL_NAMES, AUTO_REVIEW_SESSION, AUTO_REVIEW_TIMEOUT_MS, CHANNEL, CLAUDE_INSTALL_COMMAND, CLAUDE_MIN_VERSION, CLAUDE_MISSING_MESSAGE, CLAUDE_NPM_LATEST_URL, CLAUDE_NPM_PACKAGE, CLAUDE_NPM_UPDATE_COMMAND, CLAUDE_PERMISSION_MODES, CLAUDE_SELF_UPDATE_COMMAND, CLI_ONLY_GROUPS, CLI_PROJECT_DIR_NAME, CLI_SESSION_IDLE_MS, Config, DEFAULT_API_HOST, DEFAULT_AUTO_REVIEW_MODEL, DEFAULT_CLAUDE_CONFIG_DIR, DEFAULT_IMAGE_MODEL, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DIAGNOSTICS_FORMAT, DIAGNOSTICS_LIMITS, DISPLAY_NAME, DSH_AUTO_REVIEW_ROW, HTTP_CLAUDE_GROUPS, IMAGE_MODELS, IMAGE_MODEL_IDS, IMAGE_QUALITIES, IMAGE_TIMEOUT_MS, IMAGE_TOOL_NAME, JEV_ALLOW_THRESHOLD, JEV_HIGH_RISK_LIMIT, JEV_LIMITS, JEV_QUESTIONS, LEGACY_STATUS_HOSTS, MAX_IMAGE_BYTES, MAX_LIVE_CLI_SESSIONS, MIRROR_TOOL, PACKYCODE_AUTO_SEARCH_PROVIDER_ID, PACKYCODE_CLASSIFIER_MODELS, PACKYCODE_COVERED_MODEL_IDS, PACKYCODE_MODELS, PACKYCODE_MODEL_IDS, PACKYCODE_SEARCH_PROVIDER_ID, PLAN_TOOL, PLUGIN_ID, PROVIDER, PackyCodeKeyRing, RETIRED_IMAGE_MODELS, SEARCH_BACKENDS, SEARCH_BACKEND_MODELS, SEARCH_MODES, SETTINGS_NAMESPACE, TEST_MESSAGE_POLICY_CODE, answerChunks, answersFor, apply, approvalReason, assertApiKey, baseUrlOf, buildCliArgs, buildCliEnvironment, buildSearchRequest, buildTranscript, catalogCoverage, classifyCliOutdated, classifyCliUnavailable, classifyClientPolicy, classifyGatewayFailure, classifyTestMessagePolicy, classifyUpstreamServerError, claudeBinaryCandidates, claudePermissionMode, claudeTransportFor, cliErrorMessage, compactionSummary, createAutoReviewController, createBalanceReader, createClaudeAgentBridge, createClaudeCliManager, createClaudeCliTransport, createClaudeLatestReader, createClaudeToolNames, createDiagnostics, createGroupReader, createImageController, createJevReviewer, createKeyChooser, createModelSelectionController, createPackyCodeAutoSearchProvider, createPackyCodeImageClient, createPackyCodeImageTool, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, decideFromJev, decodeImagePng, describeRequest, dshSystemText, explainCliClientPolicyFailure, explainCliOutdated, explainCliUnavailable, explainClientPolicyFailure, explainNpmFailure, explainTestMessagePolicyFailure, failureChunk, guardKeyRotation, inject, isClaudeCliOutdated, isNpmClaudeInstall, isOfficialReviewRequest, jevState, locateClaudeBinary, locateNpm, messageFingerprint, name, normalizeFailureMessage, normalizeHost, officialAnswer, orderKeysFor, parseClaudeVersion, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, planAnswer, planContinuation, pngDimensions, presentClaudeCall, presentClaudeResult, questionPrompt, questionsFor, readAutoReviewChoice, readHiddenModels, readImagePreference, readReviewSections, readSearchChoice, refusalMessage, resolveSpawnCommand, responseFingerprint, sessionUuid, splitAtLastAnswer, startClaudeCliSession, statusHostOrder, summarizeSessionEvents, textResponse, titlePrompt, toCliHistory, toolResultText, validateImageOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-packycode-api",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "description": "Use PackyCode API keys in DeepSeek Harness with multi-protocol models, image generation and editing, separate key rings, and balance display",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",