Vision & Multimodal
dsh-packycode-api
BaronCyrus/dsh-packycode-api · ★ 25
Use PackyCode API keys in DeepSeek Harness with multi-protocol models, image generation and editing, separate key rings, and balance display
Manifest details
安装
dsh plugin --profile web add 'dsh-packycode-api@1.4.5'从 npm 安装预构建包
For install and environment setup, plus plugin development tutorials, see DSH community docs ↗
README
Table of contents
DSH PackyCode API
简体中文 · English
Pick models, rotate keys, and read balances in DSH with a PackyCode API key.
The PackyCode model group ships with the plugin. No YAML edits and no separate provider setup.
Features · Install · Daily use · Models and groups · Update and uninstall · Support
Features
| Capability | What it does |
|---|---|
| Built-in model route | Adds a PackyCode group to the model picker. No provider configuration and no cordis.patch.yml edits |
| Choose visible models | All models are selected by default in Settings. Unchecking a model immediately hides it from the picker; existing sessions can keep using it |
| Keys used in order | An exhausted or rejected key is disabled and the next usable key takes over; the failed step is retried on that key |
| Keys chosen by group | Each key lists its PackyCode groups and the models they reach; when the active key's groups cannot reach the selected model (no cc group for Claude, say), that turn runs on a stored key whose groups can |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Credentials stay on the machine | Keys live in the DSH credential file. The browser receives labels, state, and balances only |
Install
You need DeepSeek Harness and a PackyCode (PackyAPI) API key. For DSH setup, see the official instructions. No API key yet? You can register with PackyCode and apply for one through this invite link.
1. Install the plugin
Open the desktop application's Plugins page, click Add plugin, and enter the package name with a version as the package name or address:
dsh-packycode-api@1.4.5
Choose the official install source, then click Install. 1.4.5 was the latest release when this was written; the npm page shows the current one.
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'sminimumReleaseAgeExcludeitself. Afterwards, check Current version under Settings → PackyCode → Plugin version.
A first install requires no DSH restart: open Settings → PackyCode and add a key immediately. This holds from 1.1.7 on; earlier versions need one restart after a first install, see the FAQ. When updating an already loaded older version, follow the restart guidance in the Plugin version card.
2. Add a key
Open Settings → PackyCode. Paste the API key under Add a key. A label is optional and only distinguishes keys. Click Add. Until a key has been added, Accounts and keys shows the same registration invite link, which opens in the system browser; it disappears once a key is stored.
Do not paste a key into a public issue, chat, or screenshot. The plugin does not read
PACKY_CODE_API_KEYfrom the environment, and it does not silently switch to another provider when a request fails.
3. Select a model
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.
The Plugins page shows the installed state and version, and is also where you enable, disable, or uninstall.
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).
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.
A profile without a browser can still use the model route. It just has no settings panel and no composer balance.
Daily use
Choose visible models. Under Settings → PackyCode → Visible models, check the models you want in the picker. All are selected by default, with Select all / Select none controls. The selection is saved in the current profile and applies immediately; unchecking a model does not interrupt an existing session. New catalog models are selected by default.
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.
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.
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.
Use Claude. Select any Claude model. When the key is in the cc group, the session is carried out by this device's official Claude Code CLI, so install it first under Settings → PackyCode → Claude Code CLI (or run npm install -g @anthropic-ai/claude-code --allow-scripts=@anthropic-ai/claude-code), and click Update Claude Code CLI there when it is too old or a newer version is out; keys in cc-sale, cc-expensive, claude-officially and other groups use the plugin's HTTP route and need no CLI. Under Accounts and keys, every key that reaches Claude is marked “Claude via official CLI” or “Claude via HTTP”; the groups decide the route, and it cannot be switched by hand. Until a key's groups are read, it uses the CLI. Over HTTP, DSH executes every tool, and plan mode, questions, todos, subagents, attachments, reasoning effort and history behave as for any other model; through the CLI, Claude Code does the work itself — see “Claude Code CLI route” below for the differences.
Web search. DSH's web_search calls DeepSeek's official API by default (billed to DEEPSEEK_API_KEY). Settings → PackyCode → Web search can route it through PackyCode instead:
| Routing | Effect |
|---|---|
| Do not take over (default) | DSH's search settings stay as they are |
| Auto (route by model) | PackyCode models search through PackyCode; other models keep their search |
| Always use PackyCode search | Every request DSH's default search would have served goes through PackyCode |
| Backend | Model and protocol | Notes |
|---|---|---|
| DeepSeek (default) | deepseek-flash, /v1/messages + web_search_20250305 |
A few thousand tokens per search; needs a DeepSeek group |
| GPT | gpt-6.1-sol, /v1/responses + web_search |
Usually fresher and more precise, about 20–30K input tokens per search; needs the gateway to route this key's GPT requests to its group |
PackyCode has no dedicated search endpoint: one search is one model call with a server-side search tool, billed to the keys stored here. An exhausted or rejected key rotates exactly as on the model route.
Claude search. Claude uses DSH’s web_search with the routing choices above and the same stored-key rules as other native models.
- The plugin registers two search providers with DSH's
webservice:packycode-api(always PackyCode) andpackycode-api-auto(routes by the initiating session's model). Registering them changes nothing by itself. - Auto and Always append one
- id: webentry, marked# >>> dsh-packycode-api: web search provider, to the end of the profile'scordis.patch.yml. It points the web row's basesearchProviderat one of those providers; DSH hot-applies the file, no restart needed. Switching back to the default removes the entry and leaves everything else as it was. - The plugin does not write the web runtime's search slot. Subscription plugins such as Codex and Kimi rewrite that slot whenever the web row restarts, so two writers would restart each other forever. They fall back to the web row's base config for models they do not own, so writing the base coexists with them: Codex models keep Codex search and only the remaining requests reach the choice made here.
- The entry records the base search it replaced as a comment (
# fallback: …). In Auto, requests from non-PackyCode models go there; if that provider hands a request back to this plugin, it goes straight to DSH's built-indeepseek-officialinstead of looping. - Results come from structured data only: DeepSeek's
web_search_tool_resultblocks, GPT'surl_citationannotations andweb_search_callrecords. The model's summary is returned ascontent. - When a key's group does not cover the backend's model (the gateway says
当前令牌未覆盖供应商), the search tries the next key without disabling this one, and finally suggests switching backends. - Settings shows the base and runtime search provider in effect, and warns when another plugin's later patch overrides the base.
- Rotates and disables the current key: the gateway reports exhausted quota, or it rejects the key (HTTP 401 / invalid token). The harness retries the failed step, which is what actually sends it on the next key.
- Passes over a key by its groups, without disabling it: when the group reading shows the active key cannot reach this turn's model and another stored key can, the turn runs on that key. The active key stays active for every other model. Only evidence counts: a key whose groups are not read yet is used as before.
- Does not rotate: rate limits (429); a 400 that this gateway uses for a dead upstream channel; and a 403 that means the group accepts only the vendor's official client. The separate 403 saying the client is already authorized and should not send connectivity test messages is classified as
GATEWAY_TEST_MESSAGE. It explains the refusal without disabling the key. Only Haiku tool context may replay it once under the bounds below. - When every stored key is disabled, the request fails and says so. There is no fallback credential.
Claude Code CLI route
PackyCode's cc group (“claude code专用”) only serves the official Claude Code client, and it judges each request's content, not the program that sends it: from 1.3.4 to 1.3.9 the CLI carried DSH's system prompt and tools, which the gateway refuses as a “client anomaly”. From 1.4.0, Claude sessions on these keys are carried out by Claude Code as the agent, with DSH as the interface and the gatekeeper — the way tools such as paseo use the official CLI:
- Claude Code uses its own prompt and tools. DSH's system prompt is not appended and DSH's tools are not attached; Claude Code reads and writes files, runs commands and runs subagents itself, directly on this device. A project's
CLAUDE.mdapplies as usual. What DSH adds for its own agent (AGENTS.md, the runtime and time context, its skill catalog, and notices about approval, plan mode or a model change) is not passed to Claude Code; it receives only the messages you send. - Every tool call is a DSH tool card. Commands show as terminal cards and edits as diffs, with the output Claude Code actually got (the tool is named
claude_code). - DSH decides permissions. When Claude Code asks for permission, the session's permission preset applies: under
workspace-write(the default) edits inside the working directory go ahead and anything else that needs permission (a command that is not read-only, for example) shows DSH's approval prompt; underread-onlyedits ask too; underautoAuto review decides and asks you when it refuses; underdanger-full-accessnothing asks. A refusal reaches Claude Code with its reason. - Plan mode and questions happen in DSH. After
/plan, Claude Code enters its own plan mode, writes the plan to a file and submits it; DSH shows it in its own plan review, approval continues under the preset, and “keep planning” sends your feedback back for revision. Claude Code's questions appear in DSH's question UI. - One Claude Code session per DSH session. A DSH session keeps one long-lived CLI process; after it idles out (20 minutes), DSH restarts, or the model or key changes, the next request resumes Claude Code's own saved session. After an edited message, a fork, or a switch from another model, a new Claude Code session starts from DSH's history. When you add a note while Claude Code is working, the plugin stops its current request and gives it the note as its next message. Compaction DSH starts is answered with a local summary; Claude Code compacts its own context itself.
- Isolated from your own Claude Code. It runs with its own config home,
~/.dsh/packycode-claude-cli, and reads neither~/.claudesettings and logins nor a project's.claude/settings.json, hooks or.mcp.json; only the key stored in the plugin is passed to the child. Auto-update, telemetry, auto-memory, background tasks and model fallback are off, and retries are left to DSH's retry policy, which rotates keys. - An outdated CLI is one click away from current. The plugin needs Claude Code 2.1.259 or later (tested from that release on with the permission prompts and mode switches the plugin uses). An older CLI is stopped before the request with an explanation, and the CLI card marks it too old and offers Update Claude Code CLI: an install from the official installer (
~/.local/bin/claude) runsclaude update; an npm global install runsnpm install -g @anthropic-ai/claude-code@latest --allow-scripts=@anthropic-ai/claude-code, because npm 12 skips install scripts by default andclaude updatedoes not allow them on an npm install, which leaves a CLI that cannot run. When the CLI works but is not the latest release, the card names the new version and offers the same button: the plugin readslatestof@anthropic-ai/claude-codefrom npm on this device (the same version as the native installer'slatestchannel), at most once every 30 minutes, and again when you click Check again; without an answer it offers nothing. Your Claude Code is only updated when you click that button. - Failures say why. A missing CLI, an outdated CLI, and a request the gateway still refuses as a client anomaly each get their own explanation; nothing switches to another route or provider, and no key is disabled.
What differs from DSH running the tools: commands and edits are run by Claude Code directly on this device, outside DSH's sandbox, gated only by the permissions above; DSH's own tools (subagents, todos, background jobs, image generation, DSH's search, goals and schedules, and so on) are not available in these sessions, and Claude Code uses its own equivalents (its WebSearch may be refused by PackyCode); DSH's file-changes panel does not record Claude Code's edits; approvals are one-time only. Under auto, read-only calls Claude Code allows itself are reviewed as well. Claude Code's session records and plan files are kept in ~/.dsh/packycode-claude-cli/. Starting a process takes about 0.3–1 s. Requires Claude Code 2.1.259 or later (tested with 2.1.259, 2.1.295 and 2.1.310). On Windows, Claude Code also needs Git for Windows.
Image generation and editing
Add the dedicated key under Settings → PackyCode → Image generation and editing → Add an image key. This credential record is separate from conversation keys. Image calls use only the selected image key. Adding one does not start a paid test.
The image card shows each image key's remaining, used and limit, its reset period and a usage bar, without adding the keys up. It refreshes every minute or with Refresh, preserving a dated last-good reading when a refresh fails. Image key changes and successful generation invalidate the independent balance cache. Only image key quotas are queried, shown apart from conversation key balances.
Choose the image model and quality, then click Create image in a PackyCode conversation or explicitly request packycode_image_generate. Attach references and describe changes to use /v1/images/edits; new images use /v1/images/generations. The image model is independent of the conversation model and other image plugins can coexist.
Claude generates and edits images through DSH’s packycode_image_generate, with the same separate image key, preferences and balance cache as other models. Saved images from older CLI-backed sessions remain viewable without regeneration. Claude sessions on cc keys are carried out by Claude Code itself and cannot use this tool.
Image visible in tool details but unavailable in the final answer: a standalone main-chat gallery remains visible when reasoning/tool details are collapsed, including PTC child calls. Reopening an existing conversation restores the gallery from its saved attachments without regenerating or spending image quota. An old invalid Markdown image link to a host path does not affect this gallery.
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.
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.
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.
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.
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 for scope and evidence.
Auto review
DSH ships an experimental plugin, “Auto authorization review”, switched off by default. Switched on, it adds Auto review (marked EXP) to the permission menu under the composer (Read only / Workspace write / Full access). In a session set to Auto review, a model reviews every tool call first:
- A call it allows runs at once, without a sandbox.
- A call it denies asks for your approval; in a subagent it is refused outright.
- When the review itself fails, the call does not run.
By default DSH reviews with the conversation's own model. This plugin leaves the review logic alone and only swaps the reviewer model. Choose it under Settings → PackyCode → Auto review:
| Reviewer | Notes | Group needed |
|---|---|---|
| jev-latest (default) | TypeSafe's decision classifier: about 0.4 s per review, billed by input only; a denial names only the risk level | typesafe-officially |
| DeepSeek Flash | About 2 s per review, and a denial says why | deepseek-officially / deepseek-sale |
| Conversation model | Leave the review alone, as DSH does by default | — |
Steps:
- Switch on “Auto authorization review” on DSH's Plugins page in the sidebar.
- Pick Auto review in the composer's permission menu and confirm the risk notice.
- Choose the reviewer under Settings → PackyCode → Auto review.
The card shows whether DSH's plugin is on, how many reviews this plugin answered (allowed / denied / failed), and the last one. It warns when no key reaches the chosen reviewer.
- DSH's reviewer sends one model request per review:
- its fixed
REVIEW_POLICYas the system prompt; - four JSON sections as the user message: ENVIRONMENT, PROJECT_INSTRUCTIONS, FILTERED_HISTORY, PENDING_ACTION;
- and it expects one
{"risk","decision","reason"?}object back.
- its fixed
- Every model request passes DSH's
llm/streamwaterfall. The plugin recognizes only this request there; conversation turns and everything else go on untouched. - DeepSeek Flash gets the same request, re-addressed to the PackyCode route; DSH still reads its answer.
- jev-latest cannot write that object. The plugin passes the four sections and the official policy text to System One as the state and asks two questions: the risk, and whether you explicitly asked for this action.
- It allows a call when the probability of "low risk, or medium risk you asked for" is at least 0.6 and that of high risk is below 0.2.
- A denial says that jev-latest judged the call medium or high risk.
- jev-latest has a small context, so a long history is cut starting from the oldest entries; your instructions are kept first.
- What is reviewed, the rules, and what an allow or a denial does all stay DSH's. DSH refuses a call whose review failed (for example, no key reaches jev-latest), and the plugin keeps that.
- Measured on 37 scenarios with the official request format and the official parser: both reviewers matched all 35 scenarios that have a clear answer.
- jev-latest's median was 0.35 s, DeepSeek Flash's 2.3 s.
- If a later DSH changes the review request:
- a request the plugin no longer recognizes goes on untouched, reviewed by the conversation model;
- a format jev-latest cannot read fails that review, which shows in the card's counts.
- The description under Auto review in the menu says the same model reviews; that is DSH's text. With jev-latest or DeepSeek Flash chosen, that model reviews.
Models and groups
| Model | How it runs | Effort | Token group |
|---|---|---|---|
deepseek-flash |
HTTP, Responses, images allowed | Can be off; low / high / max |
deepseek-officially, deepseek-sale |
deepseek-v4-pro (DeepSeek V4 Pro) |
HTTP, Responses, text only; 1M context, 384K output | Same | Same |
gpt-6.1-sol |
HTTP, Responses, images allowed | Cannot be off; low / medium / high / xhigh / max |
codex, codex-sale |
gpt-6.1-terra (GPT-6.1 Terra) |
Same | Can be off; low / medium / high / xhigh / max |
Same |
gpt-6-astra |
Same | Cannot be off; low / medium / high / xhigh / max |
Same |
gpt-5.6-sol |
Same | Same | Same |
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 |
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 |
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 |
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 |
claude-sonnet-5-5 |
Same | Same | Same |
claude-haiku-5-5 |
Same; also generates Claude session titles | Same | Same |
A model in the list does not mean the current token group allows it. When changing groups, do not drop the group that serves the model you still want.
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. 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.
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; 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: 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.
Gemini capabilities and limits follow Google's model pages: Gemini 4 Argon, 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.
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). 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.
Grok 4.7's context and reasoning levels follow the official guide. 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.
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).
GPT-6.1 Terra's context, output limit and effort levels follow OpenAI's model page. 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.
DeepSeek Flash uses the gateway's Responses endpoint (/api/pricing lists openai-response). With effort off it sends reasoning.effort: "none", and the gateway answers without reasoning; otherwise reasoning comes back as reasoning_text and is sent back with the same turn's tool calls.
- Every model uses HTTP inside the plugin, except Claude sessions on
cckeys, which this device's Claude Code CLI carries out. Claude keepsanthropic-messages, with a base URL without/v1; the SDK appends/v1/messages. Model ids, protocols, context limits and effort mappings are unchanged. Through the CLI, pi-ai still turns DSH's request into a Messages request, but only what is new in it — the user's next message, or tool calls DSH has dealt with — reaches Claude Code; each API request Claude Code makes is one DSH step, its raw stream events are parsed by the same protocol code, and its tool calls becomeclaude_codeorexit_plan_mode. - Claude requests over HTTP use the live-verified CLI-compatible headers, bearer authentication, session metadata and identity system prefix while preserving DSH instructions, tool schemas, images and signed replay. The compatibility version is pinned to
2.1.295. - The bearer comes only from the plugin’s stored key chosen for the model; the CLI route passes that same key to the child as
ANTHROPIC_AUTH_TOKENand clears inheritedANTHROPIC_*/CLAUDE_*variables. No CLI login, external configuration, ambient key or alternate provider is used. - The key's groups decide the route: it cannot be pinned, and a refusal never switches routes on its own. A key whose groups are not read yet uses the CLI: a
cckey is always refused over HTTP, while every Claude group serves the official client; once the reading shows another group, HTTP takes over. A Claude request waits up to about 6 seconds for the key's groups; a key whose reading failed is read again after about 30 seconds, and opening a session starts a reading early. A turn that starts on the CLI finishes there, and the next turn follows the groups. Client-policy refusals are explained without disabling a key. Server changes may invalidate the envelope; see PackyCode group policy. - Identical Haiku and Sonnet tool requests have been both refused with the captured client-anomaly 403 and accepted. The plugin waits one second and repeats the unchanged request once for that response. Haiku also covers the captured already-authorized/test-message 403, sharing the same one-replay limit. A second refusal remains visible. Opus, plain text, ordinary official-CLI-only refusals, 429s and accepted streams do not trigger this replay. Records retain the original failures; this does not establish a repair of the gateway's internal classifier.
- Claude streams finish at
message_stopwithout waiting for the gateway to close the HTTP body, and CRLF delimiters remain intact across network chunks. Stream errors and cancellation still surface; waiting for response headers is not counted as success. - Offered DSH bash/read/write/edit/glob/grep tools use canonical CLI names such as
BashandReadover HTTP; other tools use MCP names. Through the CLI, DSH's tools are not sent to Claude Code. Only aliases declared for the current request regain their original names before DSH dispatch; unknown names never grant execution. Arguments, call IDs, results and signed history remain intact. Thinking display follows the CLI defaults:omittedfor Haiku andupdateswith its required beta for Sonnet and Opus. Reasoning effort and output limits remain unchanged, signatures on empty thinking blocks survive replay, and explicit display options take precedence. - When the legacy working-directory sentence agrees with the initiating session metadata, Claude receives the exact path as a separate JSON string. Sentence punctuation stays outside the path; real trailing dots, Windows drive/UNC paths, spaces and backslashes remain intact. Commands are never trimmed or rewritten. Shell guidance prefers the current directory or
workdir, preserving PowerShell syntax for Windows tools. - 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.
- The 1.2
claudeCliMode/claudeCliCwdsettings do not return: Claude Code's permissions follow the session's DSH permission preset, and its working directory is the session's. The tool namedclaude_codeis 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.claudeCliPathapplies again and names the CLI (empty means the usual install places, then PATH). 1.3.4'sclaudeTransportis 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. - Codex Responses requests still need
prompt_cache_key; the plugin supplies a missing session identity. Other Responses families omit it. - After building,
node scripts/claude-check.mjschecks 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 withexit_plan_mode→ approval), validating the business answers;--transport cliruns 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;--statusreads 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. - 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-wirerecords 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.
Update and uninstall
Upgrading from 1.3.2 or earlier: install
dsh-packycode-api@1.4.5on 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.
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.
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.
Uninstall: if you used PackyCode web search, first switch Settings → PackyCode → Web search back to the default. Then remove this plugin on the Plugins page and restart the desktop application. Otherwise cordis.patch.yml keeps a web entry naming a provider that no longer exists, and web_search fails with configured web provider "packycode-api-auto" is not registered; delete the block marked dsh-packycode-api: web search provider by hand to recover. Uninstalling does not remove other plugins, the profile, or the token in the PackyCode console.
FAQ
Installed, but Settings shows no PackyCode at all? The plugin row failed to load at startup, and DSH only records that in the host log — the settings page shows no reason. Check with node scripts/install-check.mjs from this repository (read-only, no network); by hand, look for this directory:
ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
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.
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.
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:
grep '"version"' ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/package.json
Recovery: quit and reopen the desktop application, and the old version works. Then click Check for updates → Update plugin under Settings → PackyCode → Plugin version (it installs the exact latest version and is not subject to the 24-hour rule), and restart once more.
Web search says "no stored API key's group covers …"? The search backend's model is not in your token groups (or the gateway currently routes it to another supplier). Pick the other backend under Settings → PackyCode → Web search, or add a key whose group covers it.
One model fails, but the key is valid? Look at that key's groups and ✓ / ✕ under Accounts and keys. When its groups do not cover the model, use a key whose groups do, or add the group to the key in the PackyCode console. With such a key stored, the plugin runs that model on it automatically. A group refusal never disables a key.
A GPT model's file edit fails with invalid justification: expected a non-empty sentence? This affected 1.4.2 and earlier; update to 1.4.3. PackyCode's GPT route treats a tool without strict as strict, so the model had to fill every optional parameter. It sent DSH's sandbox escalation pair as sandbox_permissions: "workspace-write" with an empty justification, and DSH's edit and write refused the call. From 1.4.3 GPT tool definitions carry an explicit strict: false. The model usually finishes the step another way, and existing sessions need nothing.
Claude reports an official-client-only or client-anomaly refusal? This is a gateway client-policy refusal, not proof of an invalid key. See whether the key is marked “Claude via HTTP” or “via official CLI”: a cc key always uses the CLI, and other groups use HTTP. The cc group judges whether a request comes from Claude Code itself; from 1.4.0 cc sessions are carried out by Claude Code and are normally accepted. If one is still refused, resend it; if it keeps happening, send the gateway's own words from the error (with the request id) to PackyCode support, or use a cc-sale / cc-expensive / claude-officially key. Updating the Claude Code CLI does not help with this refusal. The plugin does not disable the key or switch providers.
“Claude Code CLI is not available”? The key is in cc, which needs the CLI on this device. When the message says the key's groups are not read yet, the group reading failed for now (common on an unstable network or proxy): resend shortly, and a key outside cc moves to HTTP once its groups are read. Under Settings → PackyCode → Claude Code CLI click Install, or run npm install -g @anthropic-ai/claude-code --allow-scripts=@anthropic-ai/claude-code and click Check again; set claudeCliPath for an unusual location. Otherwise use a cc-sale / cc-expensive key.
“The installed Claude Code CLI is too old”? This device's CLI is older than 2.1.259. Under Settings → PackyCode → Claude Code CLI click Update Claude Code CLI, then resend; or run claude update in a terminal (for an npm install, the npm command the card shows) and click Check again. For a Homebrew install, run brew upgrade claude-code.
The balance does not match the account bill? Settings shows only each key stored here, with its own quota, not other tokens on the same PackyCode account. An API key cannot read the account's balance; check it in the PackyCode console. A key that failed to read says so on its own row.
The balance reads fetch failed from https://www.packyapi.com? That host, the default through 1.0.4, is DNS-poisoned on mainland networks and only answers through a proxy, so it works on and off. The default is now https://www.packyapi.ai. The model list is built into the plugin and never queried from PackyCode, so it is unaffected.
A Claude model stays on “thinking”? Check DSH errors or diagnostics. HTTP 429/503 means gateway queueing or throttling and follows the host retry policy; client-policy refusals are explained directly
…