Preface¶
When developing with DeepSeek Harness (DSH), many people have more than one coding agent locally: ZCode, MiniMax Code, and subscribed ones like Codex, Claude, Grok, Cursor, and Qoder. They are scattered across their respective CLI or desktop apps, with their own quotas, keys, and configurations that do not communicate with each other; to route to different agents within a single DSH session, one has to rely on manual copy-paste, or simply give up on a unified entry point.
The plugin introduced below, goodjin/dsh-acp-agent (npm package @goodjin2/dsh-acp-agent), solves this problem: it registers external coding agents on the DSH native session interface and unifies the display of each provider’s quota and model switching back to the host process. It is maintained by goodjin and is categorized as “Client” in the community plugin directory.
What is it¶
One-line positioning: A DSH plugin that integrates external coding agents (GLM/ZCode, MiniMax, Codex, Claude, Grok, Cursor, Qoder — a total of seven providers) into the DSH native session interface, while also embedding quota capsules and account balance displays for each provider.
The plugin is divided into two layers: Host side and Browser side:
- Host side: Loads with the DSH process and is responsible for external agent process management, model clients, and quota interfaces.
- Browser side: (Entry point
./client, auto-loaded via DSH Web) is responsible for quota capsules on the session interface, model chips, switching feedback, and process step rendering.
The directory page shows a star count of 0, based on the data at the time the page was viewed.
Core Features¶
ACP Agent Framework¶
The Host side implements the ACP (Agent Client Protocol) runtime framework, covering the transport layer (stdio, WebSocket), dialect adaptation for each agent, frame projection, and terminal and file sub-protocols. Seven external agents are registered as DSH presets through this layer, taking over sessions, routing dialogue turns, and handling model switching.
GLM (ZCode) Model Client¶
Directly reads the coding-plan key saved by the local ZCode desktop app, calls the GLM Coding Plan, and returns results streamingly. There are two key reading paths by priority:
~/.zcode/v2/config.json— The key is stored in plaintext in theoptions.apiKeyfield, placed together with theoptions.baseURLof that provider entry. Entries are selected based on the gateway they serve, rather than by name.~/.zcode/v2/credentials.json— The old version of the app (currently 3.17.2, README note checks date 2026-11-06) no longer writes to this entry, it is kept only as an old path.
Read in real-time per request without caching—because the app rewrites these files when users re-login or switch plans. Two environment variables can change the location: ZCODE_DATA_BASE_DIR to migrate the data directory overall, and ZCODE_ACP_CONFIG_FILE and ZCODE_ACP_CREDENTIALS_FILE to override individual file paths.
MiniMax Model Client¶
Borrows the local MiniMax Code login state (~/.minimax/auth/<buildEnv>/<region>/mcode-public/auth.json), using the same official OAuth core for refresh; a successful refresh does not affect the desktop app or CLI because they share the same credential storage and cross-process lock. The plugin writes to the same location after refresh, so the desktop side continues to work normally.
When refresh fails, the plugin no longer silently degrades but instead explains where to authorize: resolveCredential throws a MiniMaxLoginRequiredError (reason: ‘missing’ | ‘expired’); the quota interface returns { ok: false, needsLogin: true, login: { url, userCode?, expiresInSec? } }; when a model request fails authentication, the authorization link is placed in the stream error, and users can click directly in the session. This link comes from the device authorization flow used by the desktop app (POST /oauth2/device/code → verification_uri_complete). The plugin starts this process in the background, waiting up to 3 seconds for the prompt, because MiniMax Code holds a credential lock at runtime, and the plugin’s login request needs to queue—the first response might only carry the account source, and the next poll 30 seconds later will return the complete link with the user code. Tokens will not appear in the interface response, logs, or error messages.
Quota Interface and Browser-side Capsules¶
The Host side exposes quota routing for GLM, MiniMax, Codex, Claude, and Grok, with credentials never leaving the host process. The Browser side displays quota capsules for each provider below the input box and polls the above routing; there is also a model visibility panel where any provider or model can be hidden in settings; the model chip next to the input box shows the current provider; and it provides takeover hints and a progress bar when the session switches to a new preset.
Text Recovery for Interrupted Turns and ACP Process View¶
The input box supports recovering interrupted turns without text (expand button to resend) and also supports resending requests that haven’t been entered into the conversation history. Turns from external agents will render ACP process steps (terminal, file operations), available when running independently, and when @goodjin2/dsh-chat-area is installed, it hooks into the assistant-step rendering chain of that package via the seat conversation.chat.node.acp-segment-renderer.
Health Check¶
The plugin comes with a leaf module ./health that does not import any other modules within the package: even if all other lines fail to mount, it can still load and report. After starting, it prints a log line, for example:
acp-agent health: 12/13 lines mounted; 1 line unmounted — acp-agent-minimaxi-quota (MiniMax plan quota interface): SyntaxError: TypeScript parameter property is not supported in strip-only mode
The same ledger is provided in JSON at GET /acp-agent/health, with status taking values mounted, failed, pending, absent, disabled. The log also appends a line with a timestamp to $DSH_HOME/logs/acp-agent-health.log, one line per startup. The origin of this module was a real incident: on 2026-11-03, a constructor parameter property in src/minimaxi/credential.ts threw an error under DSH’s strip-only TypeScript loader, causing all lines referencing it, along with both the host and browser sides, to disappear without a trace in the logs, making it only possible to locate the cause by probing whether the quota routing exists. Now there is another tests/entry-loadability.spec.ts that imports each host entry using real node (no transformation) before release to act as a gatekeeper.
Installation and Enablement¶
Install from GitHub Packages (original README command):
# One-time npm auth configuration
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> ~/.npmrc
echo "@goodjin:registry=https://npm.pkg.github.com" >> ~/.npmrc
dsh plugin --profile web add @goodjin2/dsh-acp-agent
Or add the package to the profile’s package.json:
{
"dependencies": { "@goodjin2/dsh-acp-agent": "^0.1.0" }
}
After installation, you need to add the desired agent presets to cordis.patch.yml in the profile. The repository comes with a sample cordis.patch.yml containing seven agent presets and one browser roster entry (acp-agent-client), declared by bare package name — DSH only treats the line that happens to be named this as the client entry.
Before 0.3.0, the generic chat area UI (answer cards, session file cards, Mermaid renderer and its host routing, short session ID bar) has been migrated to @goodjin2/dsh-chat-area. If the profile still writes @goodjin2/dsh-acp-agent/mermaid-asset, it needs to be replaced with @goodjin2/dsh-chat-area/mermaid-asset (or deleted, as chat-area’s built-in bundle patch declares it). Browser layering when both packages are installed: the assistant-step rendering entry has the lowest priority; chat-area wraps at -50, this plugin at -40; only chat-area declares the seat, and duplicate declarations are swallowed and cause an error.
Runtime environment (package.json): node ^22.19.0 || >=24.0.0, dsh >=0.2.1-rc.2 <0.3, pnpm@11.7.0; current version 0.3.0.
Suitable Scenarios and Notes¶
Suitable for users already working in DSH who subscribe to multiple external coding agents and want to switch models and check balances in a unified session; also suitable for teams that want to unify the quotas and routing of the seven providers using DSH as the entry point.
Two reminders:
- The plugin runs with the permissions of the current DSH process; the Host side will read local credential files (e.g.,
~/.zcode/v2/config.json,~/.minimax/auth/…/auth.json). Before installation, check the source code andLICENSE(theLICENSEis listed in the repositoryfiles, the specific license type is not marked in the README). - Quota and credential interfaces only flow within the host process; the plugin does not put tokens into the page payload or logs, but if performing a security audit on third-party plugins, they are still treated as third-party code.
Conclusion¶
The value of this plugin lies in converging the “external coding agents + each provider’s quota” into DSH’s native session interface: model switching, quota checking, and retry on failure no longer require opening each client individually, and the health check mechanism also addresses the common silent failure issue in plugin development.
- Directory page: https://www.skillhub.cn/plugins/goodjin/dsh-acp-agent
- GitHub: https://github.com/goodjin/dsh-acp-agent