dsh-packycode-api 1.2.9 → 1.2.11
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/AGENTS.md +12 -3
- package/CHANGELOG.md +15 -0
- package/README.en.md +7 -7
- package/README.md +7 -7
- package/lib/index.js +125 -19
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -31,6 +31,7 @@ This file governs the entire `dsh-packycode-api` repository.
|
|
|
31
31
|
- Edit `src/`; never hand-edit generated `lib/index.js` or `lib/client.js`.
|
|
32
32
|
- `lib/` is tracked and must be regenerated with `pnpm run build` after source changes.
|
|
33
33
|
- `.artifacts/` and `node_modules/` are ignored and must never be committed.
|
|
34
|
+
- `docs/releases/v<version>.md` holds each version's GitHub Release notes. It is tracked and not packaged.
|
|
34
35
|
- Add or update tests under `tests/` for behavior changes.
|
|
35
36
|
|
|
36
37
|
## Required validation
|
|
@@ -71,7 +72,13 @@ Never reuse, move, delete, or overwrite a published npm version or Git tag. If a
|
|
|
71
72
|
|
|
72
73
|
For an unused npm version, `npm view` normally returns `E404`. An existing npm version or Git tag requires choosing a new version.
|
|
73
74
|
|
|
74
|
-
3. Update all release metadata together: `package.json` version, `CHANGELOG.md`,
|
|
75
|
+
3. Update all release metadata together: `package.json` version, `CHANGELOG.md`, any version pinned in `README.md` / `README.en.md`, and the GitHub Release notes in `docs/releases/v<version>.md`. The notes are in Chinese, like every earlier release:
|
|
76
|
+
- The first line is `# v<version>:<短标题>`, which becomes the release title.
|
|
77
|
+
- Then a one-sentence summary and bullets with bold labels.
|
|
78
|
+
- Then a `验证:…` paragraph.
|
|
79
|
+
- They close with `在 DSH 插件管理中更新至 **<version>**,然后重新打开 DSH 加载新版。`
|
|
80
|
+
|
|
81
|
+
`node scripts/release-notes.mjs <version>` checks the file, and `pnpm run check` fails while the current `package.json` version has no valid notes.
|
|
75
82
|
|
|
76
83
|
4. Validate and inspect the exact release artifact:
|
|
77
84
|
|
|
@@ -85,7 +92,7 @@ Never reuse, move, delete, or overwrite a published npm version or Git tag. If a
|
|
|
85
92
|
|
|
86
93
|
6. Confirm the package has a GitHub Actions trusted publisher before pushing the tag: repository `BaronCyrus/dsh-packycode-api`, workflow filename `publish.yml`, environment empty. Never request that a token, password, or one-time code be pasted into chat.
|
|
87
94
|
|
|
88
|
-
7. Create the annotated tag and push it. That push publishes with GitHub OIDC. Do not run `npm publish` locally for a release. A local publish cannot attach provenance. Do not set `NODE_AUTH_TOKEN` or a setup-node `registry-url` in the publish workflow; npm treats either as token auth and skips OIDC.
|
|
95
|
+
7. Create the annotated tag and push it. That push publishes with GitHub OIDC. The workflow's `release` job then creates the GitHub Release from `docs/releases/v<version>.md` and attaches the tarball npm published. It is the only job with `contents: write`, and it runs no dependency code. Do not run `npm publish` locally for a release. A local publish cannot attach provenance. Do not set `NODE_AUTH_TOKEN` or a setup-node `registry-url` in the publish workflow; npm treats either as token auth and skips OIDC.
|
|
89
96
|
|
|
90
97
|
```sh
|
|
91
98
|
git tag -a v<version> -m "dsh-packycode-api v<version>"
|
|
@@ -94,4 +101,6 @@ Never reuse, move, delete, or overwrite a published npm version or Git tag. If a
|
|
|
94
101
|
|
|
95
102
|
`workflow_dispatch` on the same workflow only prints the OIDC identity npm will match. It does not publish.
|
|
96
103
|
|
|
97
|
-
8. Confirm the publish workflow succeeded for that tag before declaring the release public. On an OIDC `package not found` error, fix the trusted publisher and re-run the same workflow. Do not move the tag. If the tagged tree itself must change, publish a new patch version.
|
|
104
|
+
8. Confirm that both jobs of the publish workflow succeeded for that tag before declaring the release public. Then check that npm serves the version and that `gh release view v<version>` shows the notes, the `dsh-packycode-api-<version>.tgz` asset and, for the newest version, the Latest mark. On an OIDC `package not found` error, fix the trusted publisher and re-run the same workflow. Do not move the tag. If the tagged tree itself must change, publish a new patch version.
|
|
105
|
+
|
|
106
|
+
If npm published but the release job failed, re-run only that failed job. If the Release must be made by hand, attach the tarball npm serves, not a local `.artifacts` build: CI packs its own, so the local bytes differ. Fetch it with `npm pack dsh-packycode-api@<version>`, and compare its SHA-512 with the registry's `dist.integrity`. Then run `gh release create v<version> <tarball> --verify-tag --title "<the notes' title>" --notes-file <the notes' body>`. The release job leaves an existing Release unchanged.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.11
|
|
4
|
+
|
|
5
|
+
- **Gemini tool continuations send no real thought signature.** Gemini 4 Argon long tasks ended in HTTP 400 `Invalid thought signature.` (`packy_upstream_error`) after DSH's retries. PackyCode support attributes this to its native Gemini route not handling the signature field in tool calls. It advises against resending through that route and recommends that clients ignore the field. Requests for both Gemini models now replace every thought signature with Google's documented `skip_thought_signature_validator`, which also fills the first function call of each step whose response came back unsigned. Only the signature field changes: the protocol (`google-generative-ai`), base URL, model IDs, thinking levels and DSH's stored replay state are unchanged, and no request is routed elsewhere. Google notes that skipped signatures can weaken reasoning continuity across tool steps. Flash keeps its one resend after an HTTP 400 signature refusal; such a refusal still never disables or rotates a key.
|
|
6
|
+
- Validation, 2026-10-16: 458 offline tests (one new; four failed without the change), both rebuilt bundles and the versioned tarball. Live, only the signature field changed between runs.
|
|
7
|
+
- Before: a 14-step Argon tool chain at low effort got 2 `Corrupted thought signature` refusals in 17 requests. A captured request was still accepted after pruning a tool result, editing call arguments or editing the user text, so DSH's history rewriting does not cause the refusals.
|
|
8
|
+
- After: 95 long-chain requests through the built route, carrying up to 26 skip-signed parts and no real signature, all returned HTTP 200 with no refusal. The runs covered Argon at low (14 steps plus a second turn), Argon at high (8 steps), Flash at low (12 steps plus a second turn), and Argon and Flash at high (24 steps plus a second turn each). Every answer was correct.
|
|
9
|
+
- `scripts/gemini-tool-check.mjs` passed 3/3 rounds at high for both models with no resend. `scripts/live-check.mjs` passed for every HTTP model, key rotation and the RPC projections. No DSH restart.
|
|
10
|
+
- **The release job receives the published tarball.** The v1.2.10 tag run published to npm, then its hand-off step found no tarball: `actions/upload-artifact@v4` skips files under a dot directory such as `.artifacts/` unless `include-hidden-files` is set. The release job was therefore skipped, and the v1.2.10 Release was made by hand with the tarball npm serves (its SHA-512 matched `dist.integrity`). The hand-off now sets `include-hidden-files: true`; its glob still matches only the published tarball.
|
|
11
|
+
|
|
12
|
+
## 1.2.10
|
|
13
|
+
|
|
14
|
+
- **OpenAI's transient server error is retried instead of failing the turn.** On the Codex group, a `gpt-6.1-sol` turn sometimes ended in 本轮运行失败 "An error occurred while processing your request. You can retry your request, or contact us through our help center at help.openai.com if the error persists." with `PI_AI_ERROR`. The gateway had already answered HTTP 200 and started the stream when the upstream failed: an SSE `error` event carrying a 503 `server_error`, then `response.failed`, before any output. The OpenAI SDK raises that without an HTTP status, so DSH's adapter, which reads a failure's category from status words, filed it as `PI_AI_ERROR`, a code no retry policy re-runs. The same failure sent as an HTTP 500 is `SERVER` and was already retried. The route now files OpenAI's `server_error` (its sentence, or pi-ai's `server_error:` prefix) as `SERVER` when the adapter left it unclassified, on prepared and direct calls alike, so the route's existing policy (up to 20 retries with backoff) resends the request. Only the code changes: if every retry fails, the upstream sentence still reaches the user, after up to about three minutes, as for an HTTP 500. The key stays enabled and is not rotated. The official Codex client also retries this failure (`ApiError::Retryable`).
|
|
15
|
+
- Validation, 2026-10-16: 457 offline tests (5 new; three of them failed with `PI_AI_ERROR` before the fix), both rebuilt bundles and the versioned tarball. Live on a `codex`-group key at high effort: through the 1.2.9 route, 2 of 14 `gpt-6.1-sol` requests received the relayed 503 at the fourth stream event, before any output, and ended as `PI_AI_ERROR`. One of them was a session's first request with no reasoning replay, and both identical resends succeeded. Through this build, 2 of 20 requests (a first attempt and its immediate resend) received it, and both ended as `SERVER` with the upstream sentence unchanged. A further 16 requests under the route's own retry policy met no upstream failure. `scripts/live-check.mjs` passed: every HTTP model answered, and key rotation and the RPC projections were unchanged. About $0.32 of test quota. No DSH restart.
|
|
16
|
+
- **A release also creates its GitHub Release.** A `v*` tag push publishes to npm and then, in a separate `release` job, creates the GitHub Release from `docs/releases/v<version>.md` with the tarball npm published attached. The publish job refuses a tag whose notes are missing or malformed before npm publishes it. Only the release job holds `contents: write`, and it runs no dependency code. `workflow_dispatch` still only checks the OIDC identity. The notes for 1.2.7–1.2.9, whose Releases were made by hand, are in `docs/releases/`. AGENTS.md's release checklist now covers writing the notes, checking the Release, and making one by hand.
|
|
17
|
+
|
|
3
18
|
## 1.2.9
|
|
4
19
|
|
|
5
20
|
- **Remove all works again.** **Remove all** under **Accounts and keys** answered "读取 PackyCode 状态失败: PackyCode request failed" and kept every key. Emptying the ring wrote `activeId: undefined`, and DSH's credential store (`credentials-local`) accepts only plain JSON in a grant payload, so it refused the whole write. The same refusal hit removing the last conversation or image key on its own, and marking the only key unusable after its quota ran out or the gateway rejected it. That last case was only logged ("could not move past key …"), so a lone exhausted key was never shown as disabled. The ring now leaves out a field that has no value: an empty ring stores no `activeId`, and a disabled mark from a hand-edited record stores no empty reason.
|
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.2.
|
|
51
|
+
dsh-packycode-api@1.2.11
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Choose the **official** install source, then click **Install**. `1.2.
|
|
54
|
+
Choose the **official** install source, then click **Install**. `1.2.11` 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. `deepseek-flash`, `gpt-6.1-sol`, `gpt
|
|
|
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.2.
|
|
75
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.2.11`. 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
|
|
|
@@ -184,7 +184,7 @@ All nine models in the current catalog declare text and image input. DeepSeek Fl
|
|
|
184
184
|
|
|
185
185
|
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).
|
|
186
186
|
|
|
187
|
-
**
|
|
187
|
+
**Gemini tool continuation and thought signatures:** PackyCode's native Gemini route does not handle thought signatures in tool calls correctly. It answers HTTP 400 `Invalid thought signature` / `Corrupted thought signature`, and resending the same request does not help (PackyCode support's answer). For Gemini 4 Argon and Gemini 3.8 Flash the plugin therefore sends none of the signatures the model returned. Wherever a signature is required, it sends Google's documented validator-skip value `skip_thought_signature_validator` ([Google's guide](https://ai.google.dev/gemini-api/docs/thought-signatures)). The protocol, endpoint and model stay the same, and DSH's replay record keeps the original signatures. Google notes that skipped signatures can weaken reasoning continuity across tool steps. Flash keeps its earlier safeguard: after an HTTP 400 signature refusal before any stream event, it waits one second and resends once. Such a 400 is never treated as a failed key.
|
|
188
188
|
|
|
189
189
|
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.
|
|
190
190
|
|
|
@@ -213,7 +213,7 @@ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit
|
|
|
213
213
|
|
|
214
214
|
## Update and uninstall
|
|
215
215
|
|
|
216
|
-
**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. 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.2.
|
|
216
|
+
**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. 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.2.11`); without it you may get a version that is merely the newest one published at least 24 hours ago.
|
|
217
217
|
|
|
218
218
|
**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.
|
|
219
219
|
|
|
@@ -229,7 +229,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
229
229
|
|
|
230
230
|
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.
|
|
231
231
|
|
|
232
|
-
Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.2.
|
|
232
|
+
Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.2.11`), 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.
|
|
233
233
|
|
|
234
234
|
**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:
|
|
235
235
|
|
|
@@ -288,7 +288,7 @@ pnpm run check
|
|
|
288
288
|
|
|
289
289
|
`node scripts/install-check.mjs` diagnoses an installed plugin that never appears: it reads one profile, lists any host package shadowed by a leftover `node_modules` inside an installed package (with versions) plus the recovery steps, and exits 1 when it finds one. `--profile <name>`, `--home <dir>`, and `--json` are available.
|
|
290
290
|
|
|
291
|
-
Read [AGENTS.md](AGENTS.md) before agent-assisted changes or a release. Publishing is the [Publish workflow](.github/workflows/publish.yml), triggered by pushing a `v*` tag. Do not run `npm publish` locally.
|
|
291
|
+
Read [AGENTS.md](AGENTS.md) before agent-assisted changes or a release. Publishing is the [Publish workflow](.github/workflows/publish.yml), triggered by pushing a `v*` tag: it publishes to npm, then creates the GitHub Release from `docs/releases/v<version>.md` with the same tarball. Do not run `npm publish` locally.
|
|
292
292
|
|
|
293
293
|
## License
|
|
294
294
|
|
package/README.md
CHANGED
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
|
|
49
49
|
|
|
50
50
|
```text
|
|
51
|
-
dsh-packycode-api@1.2.
|
|
51
|
+
dsh-packycode-api@1.2.11
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
在**安装源**中选择**官方源**,然后点击**安装**。`1.2.
|
|
54
|
+
在**安装源**中选择**官方源**,然后点击**安装**。`1.2.11` 是写这份说明时的最新版,当前最新版本号以 [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.2.9
|
|
|
72
72
|
|
|
73
73
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
74
74
|
|
|
75
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.2.
|
|
75
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.2.11`。不要对 `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
|
|
|
@@ -184,7 +184,7 @@ Claude Code CLI → 本机临时 MCP web_search → 插件选定的 DeepSeek/GPT
|
|
|
184
184
|
|
|
185
185
|
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) 为准。
|
|
186
186
|
|
|
187
|
-
**
|
|
187
|
+
**Gemini 工具续聊的思维签名:** PackyCode 的 Gemini 原生通道在工具调用场景下不能正确处理思维签名,会返回 HTTP 400 `Invalid thought signature` / `Corrupted thought signature`,原样重发也无济于事(PackyCode 客服的答复)。因此插件对 Gemini 4 Argon 和 Gemini 3.8 Flash 不再回传模型返回的签名,而是按 [Google 文档](https://ai.google.dev/gemini-api/docs/thought-signatures) 在需要签名的位置填入跳过校验的值 `skip_thought_signature_validator`。协议、端点和模型都不变,DSH 的回放记录仍保留原始签名。Google 提示跳过签名可能削弱多步工具调用之间的推理连贯性。Flash 仍保留原有兜底:HTTP 400 签名拒绝且尚未输出任何流事件时,等待 1 秒原样重发一次。这类 400 不会被当作 Key 失效。
|
|
188
188
|
|
|
189
189
|
Grok 4.7 的上下文与推理档位来自 [官方文档](https://docs.x.ai/developers/grok-4-7)。官方没有单独限制文字输出长度;插件将输出上限设为上下文大小,并按每次请求的剩余上下文收紧。
|
|
190
190
|
|
|
@@ -213,7 +213,7 @@ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses
|
|
|
213
213
|
|
|
214
214
|
## 更新与卸载
|
|
215
215
|
|
|
216
|
-
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.2.
|
|
216
|
+
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.2.11`),否则装上的可能是发布满 24 小时的旧版。
|
|
217
217
|
|
|
218
218
|
**从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
|
|
219
219
|
|
|
@@ -229,7 +229,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
229
229
|
|
|
230
230
|
有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
|
|
231
231
|
|
|
232
|
-
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.2.
|
|
232
|
+
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.2.11`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
|
|
233
233
|
|
|
234
234
|
**首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
|
|
235
235
|
|
|
@@ -288,7 +288,7 @@ Claude CLI 的 MCP 接口验证用 `node scripts/cli-search-check.mjs`:运行
|
|
|
288
288
|
|
|
289
289
|
排查「装了却看不到插件」用 `node scripts/install-check.mjs`:它只读当前 profile,列出被包内残留 `node_modules` 挡住的宿主包(含版本)和对应的修复步骤,发现问题时以退出码 1 结束;`--profile <name>`、`--home <dir>` 可指定其它 profile,`--json` 输出结构化结果。
|
|
290
290
|
|
|
291
|
-
使用 Agent 修改或发布时,先读 [AGENTS.md](AGENTS.md)。发布由推送 `v*` 标签触发 [Publish workflow](.github/workflows/publish.yml)
|
|
291
|
+
使用 Agent 修改或发布时,先读 [AGENTS.md](AGENTS.md)。发布由推送 `v*` 标签触发 [Publish workflow](.github/workflows/publish.yml):先发布到 npm,再用 `docs/releases/v<版本>.md` 和同一个包创建 GitHub Release。不要在本机执行 `npm publish`。
|
|
292
292
|
|
|
293
293
|
## License
|
|
294
294
|
|
package/lib/index.js
CHANGED
|
@@ -852,6 +852,23 @@ function classifyClientPolicy(message) {
|
|
|
852
852
|
return CLIENT_POLICY_WORDING.test(typeof message === "string" ? message : "");
|
|
853
853
|
}
|
|
854
854
|
/**
|
|
855
|
+
* OpenAI's generic transient failure: the sentence its `server_error` carries,
|
|
856
|
+
* or the `server_error:` prefix pi-ai writes when the event names the code.
|
|
857
|
+
* The gateway can relay it inside a stream it has already answered 200 for —
|
|
858
|
+
* an SSE `error` event with a 503 `server_error`, then `response.failed` — so
|
|
859
|
+
* no HTTP status comes with it.
|
|
860
|
+
*/
|
|
861
|
+
const UPSTREAM_SERVER_ERROR = /an error occurred while processing your request\b[\s\S]*\bhelp\.openai\.com\b|^(?:error code\s+)?server_error:/iu;
|
|
862
|
+
/**
|
|
863
|
+
* Whether a failure is the upstream's transient server error, which a resent
|
|
864
|
+
* request clears. The key is not at fault.
|
|
865
|
+
* @param message - provider failure text, as pi-ai carries it.
|
|
866
|
+
* @returns true for OpenAI's generic `server_error`.
|
|
867
|
+
*/
|
|
868
|
+
function classifyUpstreamServerError(message) {
|
|
869
|
+
return UPSTREAM_SERVER_ERROR.test(typeof message === "string" ? message : "");
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
855
872
|
* Classify a gateway failure message.
|
|
856
873
|
*
|
|
857
874
|
* Quota wording wins over a status token: an exhausted balance is often
|
|
@@ -6963,37 +6980,90 @@ function guardGeminiReplay(open, { model, context, signal, wait = waitForRetry }
|
|
|
6963
6980
|
//#endregion
|
|
6964
6981
|
//#region src/gemini-api.js
|
|
6965
6982
|
/** Gemini's native API, including level-based thinking on older pi-ai hosts. */
|
|
6983
|
+
const GEMINI_MODELS = ["gemini-4-argon", "gemini-3.8-flash"];
|
|
6984
|
+
/**
|
|
6985
|
+
* Google's documented thought signature for a function call whose real one
|
|
6986
|
+
* cannot be sent: the API skips signature validation for it.
|
|
6987
|
+
*/
|
|
6988
|
+
const SKIP_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
|
|
6989
|
+
/**
|
|
6990
|
+
* Send no real thought signature.
|
|
6991
|
+
*
|
|
6992
|
+
* PackyCode's native Gemini route refuses signed tool continuations with
|
|
6993
|
+
* HTTP 400 "Invalid thought signature" / "Corrupted thought signature", and
|
|
6994
|
+
* its support attributes this to the route not handling the signature field
|
|
6995
|
+
* in tool calls: resending does not help, and clients should ignore the field.
|
|
6996
|
+
* Every signature in the request therefore becomes Google's skip value. Gemini
|
|
6997
|
+
* 3 and later still require a signature on the first function call of each
|
|
6998
|
+
* step, so that part carries the skip value even when its response had none.
|
|
6999
|
+
* DSH's replay state keeps the signatures the model returned; only the wire
|
|
7000
|
+
* request changes.
|
|
7001
|
+
* @param contents - the request's `contents`.
|
|
7002
|
+
* @returns contents without a real signature.
|
|
7003
|
+
*/
|
|
7004
|
+
function skipThoughtSignatures(contents) {
|
|
7005
|
+
if (!Array.isArray(contents)) return contents;
|
|
7006
|
+
return contents.map((content) => {
|
|
7007
|
+
if (!Array.isArray(content?.parts)) return content;
|
|
7008
|
+
let stepStart = content.role === "model";
|
|
7009
|
+
return {
|
|
7010
|
+
...content,
|
|
7011
|
+
parts: content.parts.map((part) => {
|
|
7012
|
+
const firstCall = stepStart && part?.functionCall !== void 0;
|
|
7013
|
+
if (firstCall) stepStart = false;
|
|
7014
|
+
return firstCall || typeof part?.thoughtSignature === "string" ? {
|
|
7015
|
+
...part,
|
|
7016
|
+
thoughtSignature: SKIP_THOUGHT_SIGNATURE
|
|
7017
|
+
} : part;
|
|
7018
|
+
})
|
|
7019
|
+
};
|
|
7020
|
+
});
|
|
7021
|
+
}
|
|
7022
|
+
/** Run `rewrite` on the request payload ahead of any caller hook. */
|
|
7023
|
+
function withPayload(options, rewrite) {
|
|
7024
|
+
return {
|
|
7025
|
+
...options,
|
|
7026
|
+
onPayload: async (payload, descriptor) => {
|
|
7027
|
+
const rewritten = rewrite(payload);
|
|
7028
|
+
return await options?.onPayload?.(rewritten, descriptor) ?? rewritten;
|
|
7029
|
+
}
|
|
7030
|
+
};
|
|
7031
|
+
}
|
|
7032
|
+
const skipped = (payload) => ({
|
|
7033
|
+
...payload,
|
|
7034
|
+
contents: skipThoughtSignatures(payload.contents)
|
|
7035
|
+
});
|
|
6966
7036
|
/**
|
|
6967
7037
|
* pi-ai 0.85 treats Argon as a budget-based model, and defaults Flash to
|
|
6968
7038
|
* MINIMAL when reasoning is omitted. Both models support LOW/MEDIUM/HIGH,
|
|
6969
7039
|
* default to MEDIUM and cannot disable thinking. Keep the native SDK stream,
|
|
6970
|
-
* credentials
|
|
7040
|
+
* credentials and tools; correct that config and send no real signature.
|
|
6971
7041
|
*/
|
|
6972
7042
|
function createGeminiApi() {
|
|
6973
7043
|
const api = googleGenerativeAIApi();
|
|
6974
7044
|
return {
|
|
6975
7045
|
...api,
|
|
7046
|
+
stream(model, context, options) {
|
|
7047
|
+
if (!GEMINI_MODELS.includes(model.id)) return api.stream(model, context, options);
|
|
7048
|
+
return api.stream(model, context, withPayload(options, skipped));
|
|
7049
|
+
},
|
|
6976
7050
|
streamSimple(model, context, options) {
|
|
6977
|
-
if (!
|
|
7051
|
+
if (!GEMINI_MODELS.includes(model.id)) return api.streamSimple(model, context, options);
|
|
6978
7052
|
const level = options?.reasoning === void 0 ? "medium" : clampThinkingLevel(model, options.reasoning);
|
|
6979
7053
|
const wireLevel = (model.thinkingLevelMap?.[level] ?? level).toUpperCase();
|
|
6980
|
-
return api.streamSimple(model, context, {
|
|
6981
|
-
...
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
...payload,
|
|
6986
|
-
|
|
6987
|
-
...
|
|
6988
|
-
|
|
6989
|
-
...thinking,
|
|
6990
|
-
thinkingLevel: wireLevel
|
|
6991
|
-
}
|
|
7054
|
+
return api.streamSimple(model, context, withPayload(options, (payload) => {
|
|
7055
|
+
const { thinkingBudget: _budget, ...thinking } = payload.config?.thinkingConfig ?? {};
|
|
7056
|
+
return skipped({
|
|
7057
|
+
...payload,
|
|
7058
|
+
config: {
|
|
7059
|
+
...payload.config,
|
|
7060
|
+
thinkingConfig: {
|
|
7061
|
+
...thinking,
|
|
7062
|
+
thinkingLevel: wireLevel
|
|
6992
7063
|
}
|
|
6993
|
-
}
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
});
|
|
7064
|
+
}
|
|
7065
|
+
});
|
|
7066
|
+
}));
|
|
6997
7067
|
}
|
|
6998
7068
|
};
|
|
6999
7069
|
}
|
|
@@ -7145,6 +7215,32 @@ const REQUEST_LIMITS = Object.freeze({
|
|
|
7145
7215
|
requestImageMaxBytes: 1024 * 1024
|
|
7146
7216
|
});
|
|
7147
7217
|
/**
|
|
7218
|
+
* Give the retry policy a failure the host adapter cannot classify.
|
|
7219
|
+
*
|
|
7220
|
+
* The host reads a failure's category from status words in its text. An
|
|
7221
|
+
* upstream `server_error` relayed inside an accepted stream carries none, so
|
|
7222
|
+
* it arrives as the catch-all PI_AI_ERROR, which no retry re-runs — while the
|
|
7223
|
+
* same failure as an HTTP 500 is SERVER and is retried. Only the code
|
|
7224
|
+
* changes: if every retry fails, the upstream sentence reaches the user as is.
|
|
7225
|
+
* @param stream - the adapter's harness chunks.
|
|
7226
|
+
* @returns the same chunks, with that failure filed as SERVER.
|
|
7227
|
+
*/
|
|
7228
|
+
async function* classifyRouteFailures(stream) {
|
|
7229
|
+
for await (const chunk of stream) {
|
|
7230
|
+
const failure = chunk.type === "finish" && chunk.reason?.kind === "error" ? chunk.reason.failure : void 0;
|
|
7231
|
+
yield failure?.code === "PI_AI_ERROR" && classifyUpstreamServerError(failure.message) ? {
|
|
7232
|
+
...chunk,
|
|
7233
|
+
reason: {
|
|
7234
|
+
...chunk.reason,
|
|
7235
|
+
failure: {
|
|
7236
|
+
...failure,
|
|
7237
|
+
code: "SERVER"
|
|
7238
|
+
}
|
|
7239
|
+
}
|
|
7240
|
+
} : chunk;
|
|
7241
|
+
}
|
|
7242
|
+
}
|
|
7243
|
+
/**
|
|
7148
7244
|
* pi-ai's credential store over the key ring.
|
|
7149
7245
|
*
|
|
7150
7246
|
* Reads answer with the active key so pi-ai's own auth reporting stays honest,
|
|
@@ -7257,6 +7353,16 @@ function createRouteAdapter({ ring, keys, fallbackSessionId, resolveAttachments,
|
|
|
7257
7353
|
});
|
|
7258
7354
|
const profiles = /* @__PURE__ */ new Map([[PROVIDER, createPackyCodeProfile(provider)]]);
|
|
7259
7355
|
return new class extends PiAiAdapter {
|
|
7356
|
+
async prepareCall(provider, model, signal) {
|
|
7357
|
+
const prepared = await super.prepareCall(provider, model, signal);
|
|
7358
|
+
return {
|
|
7359
|
+
...prepared,
|
|
7360
|
+
stream: (options) => classifyRouteFailures(prepared.stream(options))
|
|
7361
|
+
};
|
|
7362
|
+
}
|
|
7363
|
+
stream(options) {
|
|
7364
|
+
return classifyRouteFailures(super.stream(options));
|
|
7365
|
+
}
|
|
7260
7366
|
async listModels(provider) {
|
|
7261
7367
|
return (await super.listModels(provider)).filter((model) => isModelVisible(model.id));
|
|
7262
7368
|
}
|
|
@@ -8684,4 +8790,4 @@ function apply(ctx, config) {
|
|
|
8684
8790
|
});
|
|
8685
8791
|
}
|
|
8686
8792
|
//#endregion
|
|
8687
|
-
export { AUTO_CLAUDE_MODE, CHANNEL, CLAUDE_CLI_MODES, CLAUDE_INSTALL_COMMAND, CLAUDE_MISSING_MESSAGE, CLAUDE_NPM_PACKAGE, CLAUDE_TOOL_INSTRUCTIONS, CLI_BACKED_MODEL_IDS, CLI_IMAGE_LIMITS, Config, DEFAULT_API_HOST, DEFAULT_APPEND_SYSTEM_PROMPT, DEFAULT_IMAGE_MODEL, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DIAGNOSTICS_FORMAT, DIAGNOSTICS_LIMITS, DISPLAY_NAME, IMAGE_MODELS, IMAGE_MODEL_IDS, IMAGE_QUALITIES, IMAGE_TIMEOUT_MS, IMAGE_TOOL_NAME, LEGACY_STATUS_HOSTS, MAX_CLI_IMAGE_CALLS, MAX_IMAGE_BYTES, MCP_IMAGE_SCHEMA, PACKYCODE_AUTO_SEARCH_PROVIDER_ID, PACKYCODE_MODELS, PACKYCODE_MODEL_IDS, PACKYCODE_SEARCH_PROVIDER_ID, PLUGIN_ID, PROVIDER, PackyCodeKeyRing, SEARCH_BACKENDS, SEARCH_BACKEND_MODELS, SEARCH_MODES, SETTINGS_NAMESPACE, TITLE_MODEL_ID, TITLE_MODEL_ROUTES, accountHostOrder, apply, assertApiKey, buildClaudeArgs, buildClaudeEnvironment, buildClaudeInput, buildDelegatedContent, buildSearchRequest, capClaudeMode, catalogCoverage, classifyClientPolicy, classifyGatewayFailure, claudeBinaryCandidates, claudeModeFor, claudeModeForSession, cliSearchInstructions, createBalanceReader, createClaudeCliAdapter, createClaudeCliManager, createClaudeCodeTool, createClaudeImageService, createClaudeRunner, createClaudeSearchBridge, createDiagnostics, createGroupReader, createImageController, createKeyChooser, createModelSelectionController, createPackyCodeAutoSearchProvider, createPackyCodeImageClient, createPackyCodeImageTool, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, decodeImagePng, delegationInstructions, describeRunKey, explainCliClientPolicyFailure, explainCliGroupFailure, explainClientPolicyFailure, explainNpmFailure, findClaudeBinary, guardKeyRotation, inject, keyNote, locateClaudeBinary, locateNpm, name, normalizeFailureMessage, normalizeHost, npmCandidates, parseBillingSubscription, parseBillingUsage, parseClaudeResult, parseClaudeVersion, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, planModeForSession, planModeForToolExecution, pngDimensions, readClaudeStreamEvents, readHiddenModels, readImagePreference, readSearchChoice, renderAuxiliaryPrompt, renderDelegatedPrompt, renderTitlePrompt, resolveSpawnCommand, statusHostOrder, summarizeSessionEvents, summarizeToolUse, unwrapTitle, validateImageOptions };
|
|
8793
|
+
export { AUTO_CLAUDE_MODE, CHANNEL, CLAUDE_CLI_MODES, CLAUDE_INSTALL_COMMAND, CLAUDE_MISSING_MESSAGE, CLAUDE_NPM_PACKAGE, CLAUDE_TOOL_INSTRUCTIONS, CLI_BACKED_MODEL_IDS, CLI_IMAGE_LIMITS, Config, DEFAULT_API_HOST, DEFAULT_APPEND_SYSTEM_PROMPT, DEFAULT_IMAGE_MODEL, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DIAGNOSTICS_FORMAT, DIAGNOSTICS_LIMITS, DISPLAY_NAME, IMAGE_MODELS, IMAGE_MODEL_IDS, IMAGE_QUALITIES, IMAGE_TIMEOUT_MS, IMAGE_TOOL_NAME, LEGACY_STATUS_HOSTS, MAX_CLI_IMAGE_CALLS, MAX_IMAGE_BYTES, MCP_IMAGE_SCHEMA, PACKYCODE_AUTO_SEARCH_PROVIDER_ID, PACKYCODE_MODELS, PACKYCODE_MODEL_IDS, PACKYCODE_SEARCH_PROVIDER_ID, PLUGIN_ID, PROVIDER, PackyCodeKeyRing, SEARCH_BACKENDS, SEARCH_BACKEND_MODELS, SEARCH_MODES, SETTINGS_NAMESPACE, TITLE_MODEL_ID, TITLE_MODEL_ROUTES, accountHostOrder, apply, assertApiKey, buildClaudeArgs, buildClaudeEnvironment, buildClaudeInput, buildDelegatedContent, buildSearchRequest, capClaudeMode, catalogCoverage, classifyClientPolicy, classifyGatewayFailure, classifyUpstreamServerError, claudeBinaryCandidates, claudeModeFor, claudeModeForSession, cliSearchInstructions, createBalanceReader, createClaudeCliAdapter, createClaudeCliManager, createClaudeCodeTool, createClaudeImageService, createClaudeRunner, createClaudeSearchBridge, createDiagnostics, createGroupReader, createImageController, createKeyChooser, createModelSelectionController, createPackyCodeAutoSearchProvider, createPackyCodeImageClient, createPackyCodeImageTool, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, decodeImagePng, delegationInstructions, describeRunKey, explainCliClientPolicyFailure, explainCliGroupFailure, explainClientPolicyFailure, explainNpmFailure, findClaudeBinary, guardKeyRotation, inject, keyNote, locateClaudeBinary, locateNpm, name, normalizeFailureMessage, normalizeHost, npmCandidates, parseBillingSubscription, parseBillingUsage, parseClaudeResult, parseClaudeVersion, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, planModeForSession, planModeForToolExecution, pngDimensions, readClaudeStreamEvents, readHiddenModels, readImagePreference, readSearchChoice, renderAuxiliaryPrompt, renderDelegatedPrompt, renderTitlePrompt, resolveSpawnCommand, statusHostOrder, summarizeSessionEvents, summarizeToolUse, unwrapTitle, validateImageOptions };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-packycode-api",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.11",
|
|
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",
|