dsh-packycode-api 1.4.5 → 1.4.6

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.6
4
+
5
+ - **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.
6
+ - **Its efforts are `off` and `high`.** TokenHub's [deep thinking page](https://cloud.tencent.com/document/product/1823/131208) lists `reasoning_effort` `high` and `no_think` for Hy4, with thinking on by default. `high` is sent as is, and `off` sends `reasoning.effort: "no_think"`, the value Hy4's own row names. pi-ai's default `none` answered the same, and TokenHub's [Responses compatibility page](https://cloud.tencent.com/document/product/1823/133813) accepts both. The gateway also accepts `minimal`, `low`, `medium`, `xhigh` and `max`, but Hy4 does not document them and did not answer them differently from `high`, so they are not offered. Session titles run on Hy4 itself at `off`, the least effort it offers.
7
+ - **Reasoning, caching and tools.** Reasoning streams as `response.reasoning_summary_text.delta` into `summary_text` items, which pi-ai shows as thinking and replays unchanged with the turn's tool calls and in later turns, as TokenHub's [preserved thinking](https://cloud.tencent.com/document/product/1823/133534) asks for Hy4. The prefix cache hits as often without `prompt_cache_key` as with it, so Hy4 is sent none, like DeepSeek. Tools go without `strict`: TokenHub documents a function tool's `strict` as defaulting to true, but Hy4 filled DSH's optional sandbox fields about as rarely with `strict` omitted as with `strict: false`.
8
+ - **Hy4 is told who made it.** DSH's system prompt opens with "You are an AI agent powered by DeepSeek Harness." and "You are a coding agent powered by the hy4 model.": it names the harness, and the model only by its id. In the desktop app Hy4 then said it was a DeepSeek model, and asked whether it was Hunyuan, it denied it and claimed `hy` did not stand for Hunyuan. Every Hy4 request now ends its leading instructions with one sentence (`src/hy4-identity.js`): the hy4 model is Hy4 from Tencent Hunyuan, reached through PackyCode, and DeepSeek Harness is the agent app it runs in, not its model or its maker. DSH's text stays as written, and the sentence never changes, so the cached prefix stays stable; a caller's own payload hook still sees the final body. No other model's request changes: under DSH's prompt with their own ids, GPT-6.1 Sol and Terra named OpenAI, Grok 4.7 xAI, Gemini 3.8 Flash Google, and DeepSeek Flash and V4 Pro DeepSeek.
9
+ - Validation, 2026-11-04:
10
+ - **Offline:** 557 tests (new: Hy4's catalog entry, efforts, wire dialect and resolved metadata; `high` sent without the cache key or `strict`; `off` sent as `no_think`; summary reasoning streamed as thinking and replayed unchanged with its tool call and in the next user turn; undocumented efforts refused through the route before any request; Hy4 titling its own sessions at `off`; an image reaching Responses through DSH's `LlmRuntime`; pricing and coverage for `hunyuan-officially`; the identity sentence appended once after DSH's instructions, or sent alone without them, ahead of a caller's payload hook, and absent from the other models' requests), both rebuilt bundles and the versioned tarball. The suite also passed on Node 22.23.1.
11
+ - **Live, direct to `/v1/responses` with the test key (`hunyuan-officially` among its groups):** with no effort the gateway echoed `high`. `no_think` and `none` both answered with no reasoning item and 4 output tokens, at 52 prompt tokens against 49 for every thinking level, and a made-up effort was refused with 400. On one arithmetic problem, four requests per level all had 69 prompt tokens, and mean reasoning tokens were 4,160 at `low`, 2,917 at `medium`, 4,117 at `high`, 3,823 at `xhigh` and 3,506 at `max`, against a spread of 1,270–7,737 within the levels. With DSH's bash tool, the optional sandbox pair was filled in 1 of 14 calls with `strict` omitted, 1 of 14 with `strict: false` and 0 of 14 with `strict: true`. A prefix of about 13,000 tokens sent 8 times was cached on 4 of the 7 repeats both with and without `prompt_cache_key`.
12
+ - **Live, through the built route:** `off` sent `no_think` and answered `ok` without thinking, and `high` answered `ok` with streamed thinking; `minimal`, `low`, `medium`, `xhigh` and `max` were refused before any request. An `echo` tool call continued to `ok` 2 of 2 times at `high` and 2 of 2 at `off`, replaying the reasoning item and the `fc_` call item, and a following user turn carried every earlier reasoning item and answered with the echoed text, 4 of 4. DSH's title request at `off` came back as 整理十月发货单并标缺货. `scripts/live-check.mjs --model hy4 --with-vision` answered `ok` and named a red and a blue image. With the identity sentence in place, the efforts, 3 more `echo` continuations and the title came back the same; the vision check named both images in 1 of 3 runs, and in the other two Hy4 reasoned past the script's 2,048-token output cap at `high` on the red image and was cut off without an answer. Sent directly with the same image, 5 of 10 requests without the sentence and 4 of 10 with it ended at that cap, so the sentence is not the cause; DSH's own requests use the model's 64,000-token ceiling. In the full `scripts/live-check.mjs`, the key's groups read as reaching Hy4 and every model answered except `gemini-4-argon`: `/api/pricing` now lists Argon only in `gemini-officially`, which the test key lacks, and the gateway refused it with `分组 gemini-slb 下模型 gemini-4-argon 无可用渠道(distributor)`. That is upstream, and nothing in this change touches Gemini. Key rotation, the rejected-key classification, both auto reviewers and the RPC projections were unchanged. Not checked in the desktop app.
13
+ - **Live, Hy4's identity:** the desktop session's log showed both answers coming from `packycode / hy4` in Hy4's own reply format. Sent directly without a system prompt, Hy4 answered "我是混元,是由腾讯开发的大模型" 3 of 3 times. With DSH's system prompt it named DeepSeek Harness or no maker at all, never Tencent, in 3 of 3; after the desktop session's first answer it denied being Hunyuan 6 of 6 times; with the harness renamed it no longer claimed DeepSeek. With the sentence, sent directly with DSH's prompt, it named Tencent Hunyuan in 4 of 4 first answers, confirmed being Hunyuan in 4 of 4 follow-ups and named Tencent in 4 of 4 English answers; through the built route, 3 of 3 for each turn. The sentence did not leak into other answers: `ok` 3 of 3, a JavaScript question 3 of 3 and DSH titles 3 of 3 carried no identity talk, and a bare 你好 introduced itself as Tencent Hunyuan's Hy4 in 2 of 3. Not checked in the desktop app.
14
+
3
15
  ## 1.4.5
4
16
 
5
17
  - **Diagnostics now say how a request to PackyCode failed on the network.** An Ubuntu user running `dsh --profile web` saw model requests retried over and over: one session had 39 failed attempts in 12 steps, logged as `Request timed out.`, a later one `Connection error.`, and the balance read `fetch failed (ETIMEDOUT)` from every status host. Each attempt failed about 0.5 s after it started. `cf.api.fan` and `www.packyapi.ai` resolve to two IPv4 and two IPv6 addresses. Node tries them in turn and gives every address but the last 250 ms (`net.getDefaultAutoSelectFamilyAttemptTimeout()`). On that machine the lookup returned the IPv6 addresses but they could not be reached, so the last address failed at once, and the connection failed whenever both IPv4 handshakes took longer than 250 ms. Node's own undici reports that as an `AggregateError`, which the OpenAI SDK calls `Connection error.`. The undici 8 that DSH installs with a proxy policy rewrites it as a `ConnectTimeoutError` that still says `timeout: 10000ms`, which the SDK calls `Request timed out.`. The report kept neither the cause nor the addresses, so the two texts looked like two problems.
package/README.en.md CHANGED
@@ -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.5
51
+ dsh-packycode-api@1.4.6
52
52
  ```
53
53
 
54
- Choose the **official** install source, then click **Install**. `1.4.5` 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.6` 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
 
@@ -65,14 +65,14 @@ Open **Settings → PackyCode**. Paste the API key under **Add a key**. A label
65
65
 
66
66
  ### 3. Select a model
67
67
 
68
- Choose a model from the `PackyCode` group. Of the twelve models, Claude runs through this device's Claude Code CLI on `cc` keys; everything else uses the plugin’s HTTP route. Your key’s groups must cover the selected model.
68
+ Choose a model from the `PackyCode` group. Of the thirteen models, Claude runs through this device's Claude Code CLI on `cc` keys; everything else uses the plugin’s HTTP route. Your key’s groups must cover the selected model.
69
69
 
70
70
  <details>
71
71
  <summary>Verify the install / other profiles / a local checkout</summary>
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.5`. 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.6`. 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
 
@@ -86,7 +86,7 @@ A profile without a browser can still use the model route. It just has no settin
86
86
 
87
87
  **Switch keys.** Keys are used in the order they were added. **Use this key** picks the preferred key, effective from the next turn; **Rename** changes its label (at most 60 characters, empty names are refused); **Re-enable** puts a disabled key back after a top-up or replacement.
88
88
 
89
- **See groups.** Under **Accounts and keys**, each key lists its PackyCode groups (hover for the group's description) and which models they reach: ✓ yes, ◐ partly, ✕ no. The groups come from the gateway itself (its refusal of a model id it cannot route names the token's groups, as “已选分组=[…]”, or for a single-group key as “分组 … 下模型 … 无可用渠道”; nothing is billed), matched against PackyCode's public model-group table. Check Claude group coverage per model; do not assume `cc-sale` serves all three models; GPT needs `codex` / `codex-sale`; DeepSeek needs `deepseek-officially` / `deepseek-sale`.
89
+ **See groups.** Under **Accounts and keys**, each key lists its PackyCode groups (hover for the group's description) and which models they reach: ✓ yes, ◐ partly, ✕ no. The groups come from the gateway itself (its refusal of a model id it cannot route names the token's groups, as “已选分组=[…]”, or for a single-group key as “分组 … 下模型 … 无可用渠道”; nothing is billed), matched against PackyCode's public model-group table. Check Claude group coverage per model; do not assume `cc-sale` serves all three models; GPT needs `codex` / `codex-sale`; DeepSeek needs `deepseek-officially` / `deepseek-sale`; Hy4 needs `hunyuan-officially`.
90
90
 
91
91
  **Read the balance.** Each key under **Accounts and keys** shows its own remaining, used, and limit (limit = remaining + used); an unlimited key shows **No quota limit**. Keys are not added up into a total. An API key can read only its own quota, not the PackyCode account's balance, so check the account balance in the PackyCode console. A key that could not be read says why on its own row; a key whose read failed this time but succeeded before keeps showing its last balance with the time it was read (the composer shows it as `≈US$…`) until DSH restarts. The composer shows the active key's remaining balance only while a PackyCode model is selected. **Refresh** bypasses the roughly 60-second cache. The default model host is `https://cf.api.fan` and the balance host is `https://www.packyapi.ai`; both can be changed under **Advanced: endpoints**. The old default, `https://www.packyapi.com`, is DNS-poisoned on mainland networks and answers `fetch failed` without a proxy; even when settings still name it, `.ai` is tried first.
92
92
 
@@ -227,6 +227,7 @@ The card shows whether DSH's plugin is on, how many reviews this plugin answered
227
227
  | `gpt-6-astra` | Same | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | Same |
228
228
  | `gpt-5.6-sol` | Same | Same | Same |
229
229
  | `grok-4.7` (Grok 4.7) | HTTP, Responses, text/image input; 500K context | Cannot be off; `low` / `medium` / `high` / `xhigh` | The Grok coverage read from the gateway in Settings |
230
+ | `hy4` (Hy4, Tencent Hunyuan) | HTTP, Responses, text/image input; 1024K context (at most 960K input), 64K output | Can be off; `high` only | `hunyuan-officially` |
230
231
  | `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` |
231
232
  | `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` |
232
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,7 +238,7 @@ A model in the list does not mean the current token group allows it. When changi
237
238
 
238
239
  `jev-latest` (TypeSafe Jev) is not in the model picker: it is a decision model that answers classification questions and cannot hold a conversation, so it only serves [Auto review](#auto-review). The plugin calls `/v1/systemone` host-side; it needs a key in the `typesafe-officially` group, and each key's Jev entry under Accounts and keys shows whether it can.
239
240
 
240
- Of the twelve models in the current catalog, all but DeepSeek V4 Pro declare text and image input. DeepSeek Flash follows the [official vision guide](https://api-docs.deepseek.com/guides/vision/); images are read through DSH's attachment service and sent with the Responses request. DeepSeek V4 Pro takes text only, per DeepSeek's [models and pricing page](https://api-docs.deepseek.com/quick_start/pricing/): DSH does not let you attach an image to it, and an image already in the session (say, one switched over from Flash) is sent as a one-line "image omitted" note. PackyCode's gateway does not refuse an image sent to V4 Pro; it drops it.
241
+ Of the thirteen models in the current catalog, all but DeepSeek V4 Pro declare text and image input. DeepSeek Flash follows the [official vision guide](https://api-docs.deepseek.com/guides/vision/); images are read through DSH's attachment service and sent with the Responses request. DeepSeek V4 Pro takes text only, per DeepSeek's [models and pricing page](https://api-docs.deepseek.com/quick_start/pricing/): DSH does not let you attach an image to it, and an image already in the session (say, one switched over from Flash) is sent as a one-line "image omitted" note. PackyCode's gateway does not refuse an image sent to V4 Pro; it drops it.
241
242
 
242
243
  Gemini capabilities and limits follow Google's model pages: [Gemini 4 Argon](https://ai.google.dev/gemini-api/docs/models/gemini-4-argon), [Gemini 3.8 Flash](https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash). Both models use the native Gemini protocol through `/v1beta/models/{model}:streamGenerateContent`, streaming text, thinking and tool calls; images are read through DSH's attachment service. Thinking levels are sent with `thinkingLevel`. Both routes use the single-stream output limit without automatic continuation beyond 65,536 tokens. Available endpoints and groups follow the [PackyAPI live model catalog](https://www.packyapi.com/api/pricing).
243
244
 
@@ -245,7 +246,9 @@ Gemini capabilities and limits follow Google's model pages: [Gemini 4 Argon](htt
245
246
 
246
247
  Grok 4.7's context and reasoning levels follow the [official guide](https://docs.x.ai/developers/grok-4-7). There is no separate text-output limit; the plugin uses the context size as its numeric output ceiling, bounded by the remaining context per request.
247
248
 
248
- Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit it by default. This gateway's Grok route reports a misleading `Incorrect API key provided` error when the field is present, and DeepSeek Flash hits its prefix cache without it. GPT tool definitions also carry an explicit `strict: false`: PackyCode's GPT route treats a tool without `strict` as strict, which forces the model to fill every optional parameter (see the FAQ).
249
+ Hy4's protocols, context, output limit and image input follow Tencent Cloud TokenHub's [Hunyuan guide](https://cloud.tencent.com/document/product/1823/132252), and its effort levels follow the [deep thinking page](https://cloud.tencent.com/document/product/1823/131208): Hy4 thinks by default, and `reasoning_effort` takes only `high` and `no_think`. The plugin sends `high` as is; with effort off it sends `reasoning.effort: "no_think"`, and the answer comes back without reasoning. The gateway also accepts `low`, `medium`, `xhigh` and `max`, but Hy4's documentation does not list them, and in testing they came back with the same prompt tokens as `high` and no consistent difference in reasoning, so the plugin does not offer them. Images are read through DSH's attachment service and sent with the Responses request, as for DeepSeek Flash. Reasoning comes back as `summary_text` items; as TokenHub's [preserved thinking](https://cloud.tencent.com/document/product/1823/133534) asks, the plugin sends it back unchanged with tool continuations and in later turns. DSH's system prompt says only "powered by DeepSeek Harness" and "powered by the hy4 model", without saying whose model hy4 is, and Hy4 then calls itself a DeepSeek model and denies being Hunyuan (the other models recognize their own ids). So the plugin ends the system prompt of every Hy4 request with one sentence: hy4 is Tencent Hunyuan's Hy4, reached through PackyCode, and DeepSeek Harness is only the app it runs in. DSH's own prompt is kept as written, and other models' requests are unchanged.
250
+
251
+ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit it by default. This gateway's Grok route reports a misleading `Incorrect API key provided` error when the field is present, and DeepSeek Flash and Hy4 hit their prefix caches without it. GPT tool definitions also carry an explicit `strict: false`: PackyCode's GPT route treats a tool without `strict` as strict, which forces the model to fill every optional parameter (see the FAQ).
249
252
 
250
253
  GPT-6.1 Terra's context, output limit and effort levels follow OpenAI's [model page](https://developers.openai.com/api/docs/models/gpt-6.1-terra). It is the only GPT model whose reasoning can be off: with effort off it sends `reasoning.effort: "none"`, which the gateway passes through, and the answer comes back without reasoning. GPT-5.6 Sol's documentation lists `none` too, but through PackyCode it answers at `medium`, so it cannot be off.
251
254
 
@@ -272,9 +275,9 @@ DeepSeek Flash uses the gateway's Responses endpoint (`/api/pricing` lists `open
272
275
 
273
276
  ## Update and uninstall
274
277
 
275
- > **Upgrading from 1.3.2 or earlier:** install `dsh-packycode-api@1.4.5` 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.
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.
276
279
 
277
- **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.5`); without it you may get a version that is merely the newest one published at least 24 hours ago.
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.
278
281
 
279
282
  **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.
280
283
 
@@ -290,7 +293,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
290
293
 
291
294
  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.
292
295
 
293
- 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.5`), 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.
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.
294
297
 
295
298
  **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:
296
299
 
package/README.md CHANGED
@@ -48,10 +48,10 @@
48
48
  打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
49
49
 
50
50
  ```text
51
- dsh-packycode-api@1.4.5
51
+ dsh-packycode-api@1.4.6
52
52
  ```
53
53
 
54
- 在**安装源**中选择**官方源**,然后点击**安装**。`1.4.5` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
54
+ 在**安装源**中选择**官方源**,然后点击**安装**。`1.4.6` 是写这份说明时的最新版,当前最新版本号以 [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
 
@@ -65,14 +65,14 @@ dsh-packycode-api@1.4.5
65
65
 
66
66
  ### 3. 选择模型
67
67
 
68
- 回到对话,在模型选择器里选 `PackyCode` 分组下的模型。十二个模型里,Claude 在 `cc` 分组的 Key 上由本机 Claude Code CLI 执行,其余都通过插件内 HTTP 调用;Key 的分组必须覆盖所选模型。
68
+ 回到对话,在模型选择器里选 `PackyCode` 分组下的模型。十三个模型里,Claude 在 `cc` 分组的 Key 上由本机 Claude Code CLI 执行,其余都通过插件内 HTTP 调用;Key 的分组必须覆盖所选模型。
69
69
 
70
70
  <details>
71
71
  <summary>检查安装 / 其它 profile / 本地目录</summary>
72
72
 
73
73
  桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
74
74
 
75
- **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.5`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
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`)。
76
76
 
77
77
  本地开发要加 `link:` 前缀:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。**不要直接写路径(或 `file:`)** —— 那样的安装会把整个仓库目录(连同 `node_modules`)复制进 profile,之后它会在 profile 内部挡住 DSH 自带的包,插件看起来像没装,见「常见问题」。已经那样装过的话,先卸载再按上面的方式重装。
78
78
 
@@ -86,7 +86,7 @@ dsh-packycode-api@1.4.5
86
86
 
87
87
  **换 Key。** Key 按添加顺序使用。点「切到此 Key」指定优先使用的 Key,下一轮就生效;「重命名」改备注(最多 60 字,不能为空);充值或更换后点「重新启用」。
88
88
 
89
- **看分组。** 「账户与 Key」里每个 Key 下面列出它所在的 PackyCode 分组(鼠标悬停可看分组说明),以及能用哪些模型:✓ 可用、◐ 部分可用、✕ 不可用。分组由网关自己报出(对一个不存在的模型 id 的拒绝里会写明「已选分组」,单分组的 Key 则是「分组 … 下模型 … 无可用渠道」,不产生费用),再对照 PackyCode 公开的模型分组表。Claude 的分组覆盖要按具体模型确认,不能假定 `cc-sale` 支持全部三个型号;GPT 需要 `codex` / `codex-sale`;DeepSeek 需要 `deepseek-officially` / `deepseek-sale`。
89
+ **看分组。** 「账户与 Key」里每个 Key 下面列出它所在的 PackyCode 分组(鼠标悬停可看分组说明),以及能用哪些模型:✓ 可用、◐ 部分可用、✕ 不可用。分组由网关自己报出(对一个不存在的模型 id 的拒绝里会写明「已选分组」,单分组的 Key 则是「分组 … 下模型 … 无可用渠道」,不产生费用),再对照 PackyCode 公开的模型分组表。Claude 的分组覆盖要按具体模型确认,不能假定 `cc-sale` 支持全部三个型号;GPT 需要 `codex` / `codex-sale`;DeepSeek 需要 `deepseek-officially` / `deepseek-sale`;Hy4 需要 `hunyuan-officially`。
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
 
@@ -227,6 +227,7 @@ DSH 自带一个实验性插件「自动授权审查」,默认关闭。开启
227
227
  | `gpt-6-astra` | 同上 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | 同上 |
228
228
  | `gpt-5.6-sol` | 同上 | 同上 | 同上 |
229
229
  | `grok-4.7`(Grok 4.7) | HTTP,Responses,可带图;500k 上下文 | 不能关;`low` / `medium` / `high` / `xhigh` | 以设置页从网关读取的 Grok 分组覆盖为准 |
230
+ | `hy4`(Hy4,腾讯混元) | HTTP,Responses,可带图;1024K 上下文(输入最多 960K),64K 输出 | 可关;只有 `high` | `hunyuan-officially` |
230
231
  | `gemini-4-argon`(Gemini 4 Argon) | HTTP,Gemini 原生流式,可带图;1,048,576 上下文,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
231
232
  | `gemini-3.8-flash`(Gemini 3.8 Flash) | HTTP,Gemini 原生流式,可带图;1,048,576 输入,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
232
233
  | `claude-opus-5-5` | Anthropic Messages,可带图和原生工具:`cc` 分组由本机 Claude Code CLI 当 agent,其它分组走 HTTP | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | 以设置页读取的 Claude 分组覆盖为准 |
@@ -237,7 +238,7 @@ DSH 自带一个实验性插件「自动授权审查」,默认关闭。开启
237
238
 
238
239
  `jev-latest`(TypeSafe Jev)不在模型选择器里:它是只回答分类问题的决策模型,不能对话,只用于[自动审查](#自动审查)。插件在宿主侧直接调用 `/v1/systemone`,需要 Key 在 `typesafe-officially` 分组;「账户与 Key」里每个 Key 的「Jev」一项会显示能否使用。
239
240
 
240
- 当前目录的 12 个模型中,除 DeepSeek V4 Pro 外都声明了文字和图像输入。DeepSeek Flash 的识图能力按 [官方图像理解文档](https://api-docs.deepseek.com/zh-cn/guides/vision/) 配置;图片通过 DSH 附件服务读取,再随 Responses 请求发送。DeepSeek V4 Pro 按 [官方模型与价格页](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/) 只收文字:DSH 不让给它附图,会话里已有的图片(比如从 Flash 切过来的会话)会换成一行「图片已省略」的说明再发送。PackyCode 网关收到发给 V4 Pro 的图片不报错,而是直接丢掉。
241
+ 当前目录的 13 个模型中,除 DeepSeek V4 Pro 外都声明了文字和图像输入。DeepSeek Flash 的识图能力按 [官方图像理解文档](https://api-docs.deepseek.com/zh-cn/guides/vision/) 配置;图片通过 DSH 附件服务读取,再随 Responses 请求发送。DeepSeek V4 Pro 按 [官方模型与价格页](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/) 只收文字:DSH 不让给它附图,会话里已有的图片(比如从 Flash 切过来的会话)会换成一行「图片已省略」的说明再发送。PackyCode 网关收到发给 V4 Pro 的图片不报错,而是直接丢掉。
241
242
 
242
243
  Gemini 的能力与限制来自 Google 官方模型页:[Gemini 4 Argon](https://ai.google.dev/gemini-api/docs/models/gemini-4-argon)、[Gemini 3.8 Flash](https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash)。两款模型统一使用 Gemini 原生协议,通过 `/v1beta/models/{model}:streamGenerateContent` 流式返回文字、思考和工具调用,图片仍由 DSH 附件服务读取。思考档位通过 `thinkingLevel` 传入。两者均使用单次流式输出上限,不执行超过 65,536 token 的自动续写。可用端点与分组以 [PackyAPI 实时模型目录](https://www.packyapi.com/api/pricing) 为准。
243
244
 
@@ -245,7 +246,9 @@ Gemini 的能力与限制来自 Google 官方模型页:[Gemini 4 Argon](https:
245
246
 
246
247
  Grok 4.7 的上下文与推理档位来自 [官方文档](https://docs.x.ai/developers/grok-4-7)。官方没有单独限制文字输出长度;插件将输出上限设为上下文大小,并按每次请求的剩余上下文收紧。
247
248
 
248
- Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses 模型默认省略。当前网关的 Grok 路由会将这个字段误报成 `Incorrect API key provided`;DeepSeek Flash 不带它也能命中前缀缓存。GPT 的工具定义还会显式带上 `strict: false`:PackyCode 的 GPT 通道把没写 `strict` 的工具当作严格模式,模型会被迫填满所有可选参数(见「常见问题」)。
249
+ Hy4 的协议、上下文、输出上限和识图能力来自腾讯云 TokenHub 的 [混元调用指南](https://cloud.tencent.com/document/product/1823/132252),推理档位来自 [深度思考](https://cloud.tencent.com/document/product/1823/131208):Hy4 默认开启思考,`reasoning_effort` 只有 `high` 和 `no_think` 两档。插件按 `high` 发送,关闭推理时发送 `reasoning.effort: "no_think"`,返回时不带思考。网关也接受 `low`、`medium`、`xhigh`、`max`,但 Hy4 的文档没有这些档位,实测时它们的提示 token 与 `high` 相同,推理长度也看不出差别,所以插件不提供。图片和 DeepSeek Flash 一样由 DSH 附件服务读取,随 Responses 请求发送。推理以 `summary_text` 条目返回;按 TokenHub [保留式思考](https://cloud.tencent.com/document/product/1823/133534) 的要求,插件把推理原样回传给工具续聊和之后的轮次。DSH 的系统提示词只写了「powered by DeepSeek Harness」和「powered by the hy4 model」,没说 hy4 是谁家的模型,hy4 会因此自称 DeepSeek 的模型、否认自己是混元(其它模型都认得出自己的模型名)。所以插件在 hy4 每个请求的系统提示词末尾补一句英文说明:hy4 是腾讯混元的 Hy4,经 PackyCode 接入,DeepSeek Harness 只是运行它的应用。DSH 自己的提示词原样保留,其它模型的请求不受影响。
250
+
251
+ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses 模型默认省略。当前网关的 Grok 路由会将这个字段误报成 `Incorrect API key provided`;DeepSeek Flash 和 Hy4 不带它也能命中前缀缓存。GPT 的工具定义还会显式带上 `strict: false`:PackyCode 的 GPT 通道把没写 `strict` 的工具当作严格模式,模型会被迫填满所有可选参数(见「常见问题」)。
249
252
 
250
253
  GPT-6.1 Terra 的上下文、输出上限和推理档位来自 [OpenAI 模型页](https://developers.openai.com/api/docs/models/gpt-6.1-terra)。它是唯一可以关闭推理的 GPT 模型:关闭时发送 `reasoning.effort: "none"`,网关原样转发,返回时不带思考。GPT-5.6 Sol 的文档虽然也列出 `none`,经 PackyCode 却按 `medium` 回答,所以不能关。
251
254
 
@@ -272,9 +275,9 @@ DeepSeek Flash 走网关的 Responses 接口(`/api/pricing` 列出 `openai-res
272
275
 
273
276
  ## 更新与卸载
274
277
 
275
- > **从 1.3.2 或更早版本升级:** 请在插件页填写 `dsh-packycode-api@1.4.5` 安装,不要点旧版设置页里的「更新插件」。旧按钮安装时会绕过 pnpm 的发布冷却期:装到的版本如果发布还不满 24 小时,这个 profile 在它满 24 小时之前就装不了其他插件。装好并重启后,按钮就换成了修好的版本,以后直接点即可。
278
+ > **从 1.3.2 或更早版本升级:** 请在插件页填写 `dsh-packycode-api@1.4.6` 安装,不要点旧版设置页里的「更新插件」。旧按钮安装时会绕过 pnpm 的发布冷却期:装到的版本如果发布还不满 24 小时,这个 profile 在它满 24 小时之前就装不了其他插件。装好并重启后,按钮就换成了修好的版本,以后直接点即可。
276
279
 
277
- **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本号是从 npm 读到的,pnpm 却从 profile 配置的安装源下载:刚发布的版本,安装源(尤其是国内镜像)可能还没同步到,这时按钮会提示过几分钟再更新;连不上安装源时,会提示检查网络或代理。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.4.5`),否则装上的可能是发布满 24 小时的旧版。
280
+ **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本号是从 npm 读到的,pnpm 却从 profile 配置的安装源下载:刚发布的版本,安装源(尤其是国内镜像)可能还没同步到,这时按钮会提示过几分钟再更新;连不上安装源时,会提示检查网络或代理。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.4.6`),否则装上的可能是发布满 24 小时的旧版。
278
281
 
279
282
  **从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
280
283
 
@@ -290,7 +293,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
290
293
 
291
294
  有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
292
295
 
293
- 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.4.5`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
296
+ 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.4.6`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
294
297
 
295
298
  **首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
296
299
 
package/lib/client.js CHANGED
@@ -1959,6 +1959,7 @@ window.__ModuleLoader__.load({
1959
1959
  "gpt-5.6-sol"
1960
1960
  ]],
1961
1961
  ["Grok", ["grok-4.7"]],
1962
+ ["Hunyuan", ["hy4"]],
1962
1963
  ["Gemini", ["gemini-4-argon", "gemini-3.8-flash"]],
1963
1964
  ["Claude", [
1964
1965
  "claude-opus-5-5",
package/lib/index.js CHANGED
@@ -1083,7 +1083,9 @@ const DEFAULT_REASONING_LEVEL = "high";
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
1086
- * (docs.x.ai/developers/grok-4-7), and Gemini 4 Argon and 3.8 Flash
1086
+ * (docs.x.ai/developers/grok-4-7), Hy4 (`high` or `no_think`, TokenHub's
1087
+ * deep-thinking page cloud.tencent.com/document/product/1823/131208), and
1088
+ * Gemini 4 Argon and 3.8 Flash
1087
1089
  * (ai.google.dev/gemini-api/docs/models/gemini-4-argon and gemini-3.8-flash).
1088
1090
  *
1089
1091
  * @module dsh-packycode-api/catalog
@@ -1104,8 +1106,8 @@ const GEMINI_BASE = `${DEFAULT_API_HOST}/v1beta`;
1104
1106
  * escalation pair then came back as `workspace-write` with an empty
1105
1107
  * justification, which DSH's edit and write refuse. Declaring strict-mode
1106
1108
  * support makes pi-ai send `strict: false` on each tool, as the official
1107
- * Codex client does; the schemas stay as DSH wrote them. DeepSeek and Grok
1108
- * leave optional parameters out either way.
1109
+ * Codex client does; the schemas stay as DSH wrote them. DeepSeek, Grok and
1110
+ * Hy4 fill optional parameters no more often without `strict` than with it.
1109
1111
  */
1110
1112
  const GPT_COMPAT = Object.freeze({ supportsStrictMode: true });
1111
1113
  /**
@@ -1155,6 +1157,22 @@ const GROK_LEVELS = Object.freeze({
1155
1157
  ...ALWAYS_THINKING_LEVELS,
1156
1158
  max: null
1157
1159
  });
1160
+ /**
1161
+ * Hy4 documents `reasoning_effort` high | no_think and nothing else, with
1162
+ * thinking on by default. Through PackyCode `no_think` answers without
1163
+ * reasoning, so `off` sends it. The gateway does not refuse low, medium, xhigh
1164
+ * or max, but they are undocumented, and they came back with high's prompt
1165
+ * tokens and no consistent difference in reasoning, so they are not offered.
1166
+ */
1167
+ const HY4_LEVELS = Object.freeze({
1168
+ off: "no_think",
1169
+ minimal: null,
1170
+ low: null,
1171
+ medium: null,
1172
+ high: "high",
1173
+ xhigh: null,
1174
+ max: null
1175
+ });
1158
1176
  /** Gemini 4 Argon and 3.8 Flash support low | medium | high, with no off. */
1159
1177
  const GEMINI_LEVELS = Object.freeze({
1160
1178
  ...ALWAYS_THINKING_LEVELS,
@@ -1258,6 +1276,19 @@ const PACKYCODE_MODELS = Object.freeze([
1258
1276
  thinkingLevelMap: GROK_LEVELS,
1259
1277
  compat: Object.freeze({ supportsLongCacheRetention: false })
1260
1278
  }),
1279
+ Object.freeze({
1280
+ id: "hy4",
1281
+ name: "Hy4",
1282
+ api: "openai-responses",
1283
+ provider: PROVIDER,
1284
+ baseUrl: RESPONSES_BASE,
1285
+ reasoning: true,
1286
+ input: Object.freeze(["text", "image"]),
1287
+ cost: NO_COST,
1288
+ contextWindow: 1024e3,
1289
+ maxTokens: 64e3,
1290
+ thinkingLevelMap: HY4_LEVELS
1291
+ }),
1261
1292
  Object.freeze({
1262
1293
  id: "gemini-4-argon",
1263
1294
  name: "Gemini 4 Argon",
@@ -8086,6 +8117,49 @@ function createGeminiApi() {
8086
8117
  };
8087
8118
  }
8088
8119
  //#endregion
8120
+ //#region src/hy4-identity.js
8121
+ /** The sentence appended to Hy4's instructions; checked live as worded. */
8122
+ const HY4_IDENTITY_NOTE = "About yourself: the hy4 model is Hy4 from Tencent Hunyuan (腾讯混元), reached through PackyCode. DeepSeek Harness is the agent app you run in, not your model or its maker.";
8123
+ const INSTRUCTION_ROLES = /* @__PURE__ */ new Set(["developer", "system"]);
8124
+ const carriesNote = (item) => typeof item?.content === "string" && item.content.includes("About yourself: the hy4 model is Hy4 from Tencent Hunyuan (腾讯混元), reached through PackyCode. DeepSeek Harness is the agent app you run in, not your model or its maker.");
8125
+ /**
8126
+ * Add the note to a Responses payload: after the leading instructions, or as
8127
+ * the only ones when the request has none. A payload that already carries it
8128
+ * is returned unchanged, and the given payload is never modified.
8129
+ * @param payload - the Responses request body pi-ai built.
8130
+ * @returns the body to send.
8131
+ */
8132
+ function withIdentityNote(payload) {
8133
+ if (!Array.isArray(payload?.input) || payload.input.some(carriesNote)) return payload;
8134
+ const [first, ...rest] = payload.input;
8135
+ const input = INSTRUCTION_ROLES.has(first?.role) && typeof first.content === "string" ? [{
8136
+ ...first,
8137
+ content: `${first.content}\n\n${HY4_IDENTITY_NOTE}`
8138
+ }, ...rest] : [{
8139
+ role: "developer",
8140
+ content: HY4_IDENTITY_NOTE
8141
+ }, ...payload.input];
8142
+ return {
8143
+ ...payload,
8144
+ input
8145
+ };
8146
+ }
8147
+ /**
8148
+ * pi-ai options whose payload carries the note before any caller hook sees
8149
+ * it, so a caller's own `onPayload` still has the last word.
8150
+ * @param options - the request's pi-ai options.
8151
+ * @returns the same options with the note added on the way out.
8152
+ */
8153
+ function withHy4Identity(options) {
8154
+ return {
8155
+ ...options,
8156
+ onPayload: async (payload, model) => {
8157
+ const noted = withIdentityNote(payload);
8158
+ return await options?.onPayload?.(noted, model) ?? noted;
8159
+ }
8160
+ };
8161
+ }
8162
+ //#endregion
8089
8163
  //#region src/provider.js
8090
8164
  /**
8091
8165
  * The PackyCode pi-ai provider, built in code so the user configures no route.
@@ -8141,10 +8215,13 @@ function createPackyCodeProvider({ claudeCli } = {}) {
8141
8215
  "google-generative-ai": createGeminiApi()
8142
8216
  }
8143
8217
  });
8144
- const requestOptions = (model, options) => model.api === "openai-responses" && !model.id.startsWith("gpt-") ? {
8145
- ...options,
8146
- cacheRetention: "none"
8147
- } : options;
8218
+ const requestOptions = (model, options) => {
8219
+ const cached = model.api === "openai-responses" && !model.id.startsWith("gpt-") ? {
8220
+ ...options,
8221
+ cacheRetention: "none"
8222
+ } : options;
8223
+ return model.id === "hy4" ? withHy4Identity(cached) : cached;
8224
+ };
8148
8225
  return Object.freeze({
8149
8226
  ...provider,
8150
8227
  stream: (model, context, options) => provider.stream(model, context, requestOptions(model, options)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-packycode-api",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
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",