DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Acp Rich — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

@smelt-ai/dsh-acp-rich

Acp Rich

面向 deepseek-harness 的展示完整 Agent Client Protocol 服务器:支持流式文本、推理、工具卡片、内联差异、计划、用量、命令、权限提示和可恢复会话

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add @smelt-ai/dsh-acp-rich@0.1.9
README兼容性版本

兼容性与来源证明

Acp Rich 以 @smelt-ai/dsh-acp-rich 发布,当前版本为 0.1.9。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
npm
Registry 更新时间
2026/9/20

版本

0.1.9stable
2026/9/4
0.1.8stable
2026/8/26
0.1.7stable
2026/8/26
查看其余 5 个版本收起版本
0.1.6stable
2026/8/21
0.1.5stable
2026/8/21
0.1.4stable
2026/8/21
0.1.3stable
2026/8/21
0.1.2stable
2026/8/20

相关插件

正在加载相关插件…

最新版
0.1.9
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
244.2 kB
文件数
37
Surface
any
许可证
Apache-2.0
发布源
npm
GitHub
★ 0
周下载
52
最近提交
2026/9/4
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 integrations-communication 分类下经过校验的插件。

Acp App@deepseek-ai/dsh-acp-appdsh ACP 配置文件包:基于 dsh-base 的仅限自动化的 JSON-RPC stdio 和进程生命周期管理Im@xmanrui/dsh-im将十一种 IM 渠道和一个公网 AI Office 接入本地 DeepSeek Harness。Pocketdsh-pocket把 DeepSeek Harness 装进你的口袋:一个包、一个设置页,手机扫码即同步访问电脑上的 DSH(局域网 + 公网,实时同屏)。DSCODE@toddzheng024/dscode-bundle完整的 DeepSeek 编码代理,支持持久化 shell、Ultra 协作和自动权限审查。

README

@smelt-ai/dsh-acp-rich

A presentation-complete Agent Client Protocol server for deepseek-harness (dsh), so a dsh session in smelt looks like a Claude or Codex session: streaming text, reasoning, tool cards, inline diffs, PLAN progress, usage, slash commands, permission prompts, and resume.

Why this exists

The harness already ships @deepseek-ai/dsh-acp. It calls itself an automation-only ACP server, and its own source says why:

// Emit only committed assistant text. Raw chunks, reasoning, tools, plans,
// titles, and retry markers are presentation or trace data and stay off the
// automation wire.

That is a deliberate product decision, not a gap in the harness — "interactive rendering and human questions belong to the Web host and client modules". But smelt is an interactive client, so connecting it to the automation server yields a plain text box: no tool cards, no diffs, no plan, no resume.

The data was never missing. session/event already carries assistant/chunk (text and reasoning deltas), tool/call, tool/result, todo/write, assistant/message.usage, and request/context; ToolDefinition already has presentCall() / presentResult() returning generic | terminal | diff cards whose diff shape is field-for-field identical to ACP's ToolCallContent::Diff. This package subscribes to the same event stream and forwards all of it.

Install and run

Install the package into each native dsh profile that should be available in smelt:

dsh plugin --profile web add @smelt-ai/dsh-acp-rich

The package contributes a dormant Cordis row through its native dsh.bundle.patch. A normal dsh --profile web invocation keeps using the profile's original Web or headless host. Smelt starts the same profile with one final overlay that disables output hosts and activates the ACP row:

dsh-acp-rich --profile web

Models, tools, policies, credentials, settings, persistence, and session IDs remain owned by the native profile. Smelt discovers installed profiles under $DSH_HOME/profiles and only replaces the host for its invocation.

The bundle also installs a session lease shared by both hosts. The same session cannot be resumed concurrently from native dsh and Smelt. A lock whose owner process is gone is taken over on the next resume — mkdir is not a kernel lock, so a crashed host must not block recovery.

Querying model capabilities

Reasoning efforts are not a fixed list. resolveModelInfo answers for a resolved route, so the same model id reports four efforts through a direct connection and none through a relay, and a connection pinned to thinking: disabled collapses to off alone. A client that hardcodes the DeepSeek ladder therefore offers options that fail every request made with them as soon as the profile points somewhere else.

The package ships a one-shot probe for exactly that question:

dsh-model-capabilities --profile web

It boots the profile's own runtime with every output host disabled, waits for the provider catalog to settle, resolves each advertised model, prints one JSON report on stdout, and exits:

{"providers":[
  {"id":"deepseek-official","name":"DeepSeek","models":[
    {"id":"deepseek-v4-pro","name":"DeepSeek-V4-Pro",
     "efforts":[{"id":"off","name":"Off"},{"id":"max","name":"Max"}],
     "defaultEffort":"high"}]},
  {"id":"sub2api","name":"Sub2Api","models":[
    {"id":"deepseek-v4-pro","name":"Deepseek-V4-Pro"}]}]}

efforts is omitted, not emptied, when a route advertises no reasoning control, mirroring resolveModelInfo itself. Consumers must read that absence as "render no picker". Substituting a default list is the bug this tool exists to remove.

Expect a few seconds per call: assembling the runtime is the only way to get a truthful answer, so the cost is a process launch rather than a file read.

Discovering an endpoint's models

A relay gateway can front dozens of models. Asking the user to retype every id into a settings form is a transcription exercise that also loses the context window and max-token figures the endpoint already publishes.

dsh answers this itself: dsh-llm-pi-ai registers a model discovery that calls GET {baseURL}/models with the provider's stored bearer token. This package only exposes it:

echo '{"provider":"sub2api","api":"openai-responses",
       "baseURL":"https://example.invalid"}' \
  | dsh-discover-models --profile web
{"models":[{"id":"deepseek-v4-pro","name":"DeepSeek-V4-Pro",
            "contextWindow":131072,"maxTokens":32768}]}

The request goes over stdin, not argv, because it may carry an apiKey and argv is world-readable through ps.

Two limits are dsh's, and are deliberate:

  • Only openai-completions and openai-responses can be listed. Azure (api-key header plus an api-version query) and Codex (OAuth) are excluded, and anything else answers DISCOVERY_UNSUPPORTED. That answer means fall back to typing the model in by hand — it is not a failure to retry.
  • A provider with a built-in catalog answers from the catalog and makes no network call, so baseURL is only required for providers dsh does not know.

Credentials come from the profile's own provider map when apiKey is omitted, which is why the probe waits for the provider catalog to settle before asking. Skipping that wait yields NO_DISCOVERY first and then a 401, in that order.

Keeping a route's catalog current

A route can be marked auto by the client, meaning "I did not choose these models; re-read them from the endpoint." dsh refuses a declared route that resolves no models, so the catalog written to settings.yaml is always a concrete list — auto is a statement about who maintains it, not a way to omit it.

dsh-model-settings --action refresh-models writes that refreshed list:

echo '{"id":"sub2api","models":[{"id":"gpt-5.6"}]}' \
  | dsh-model-settings --action refresh-models

It sets only the provider's models key. save-custom replaces the whole provider node, which is correct for an edit the user is watching but wrong for a refresh that runs unattended at every launch: it would silently drop headers, timeoutMs, reasoning and every other field a deployment hand-wrote beside the ones this bridge models. An empty list and an unknown provider are both refused rather than written.

What is mapped

ACP SessionUpdatedsh source
agent_message_chunkassistant/chunk text delta
agent_thought_chunkassistant/chunk reasoning delta
tool_calltool/call + presentCall()
tool_call_updatetool/result + presentResult()
plantodo/write
usage_updateassistant/message.usage + request/context
user_message_chunkuser/message (replay only, see below)
available_commands_updatethe commands registry
config_option_updatectx.llm catalog + the agent's ModelSelectionRef

Plus session/load resume, image prompts through ctx.attachments, cancel, permission prompts, per-session MCP server passthrough, and session/set_config_option.

Deliberate departures

These are choices, not oversights.

Terminal cards are static. ACP's terminal content variant addresses a terminalId the client minted via terminal/create. A bridge cannot fabricate one for a command the harness already owns and runs. So a pending terminal card shows the command as its title, and the result card is a ```console fence with an exit/signal pill. Live streaming would require the harness to hand the command over to the client instead of running it.

Persistent authorization is ours, not the harness's. ApprovalOutcome is 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable' and the source says 'allowed-once' is the only grant — at the core layer, not just in the ACP bridge. "Always allow" therefore lives in src/grants.ts as an in-memory cache that sits on the approval/request waterfall and answers matching later requests itself. It is keyed by tool name within one session, the finest identity stable across calls (callId is per-invocation, reason is free prose), and the option labels say exactly that so nobody is surprised by a broader grant. It is not persisted across restarts.

Live user/message is not echoed. It is either the prompt the client just sent (double-render) or a synthetic harness injection — skill content, AGENTS.md, cron notices — that can be enormous. Echo happens only while replaying a loaded session, where the client genuinely has nothing.

A committed assistant/message renders only if nothing streamed for its step. This protects non-streaming adapters and pruned-chunk replays without double-rendering the normal case.

The usage gauge is suppressed without a denominator. contextWindow arrives on request/context, not with the usage. A number with no scale is worse than no gauge. Note that the four dsh token counts are disjoint — cached input is reported separately, not folded into inputTokens — so "used" is their sum.

The model picker writes the selection cell, not a setting. The harness has no global "current model" to set: whoever creates an agent owns its ModelSelectionRef, and installModelSelection couples that cell to prompt assembly and request routing. So this bridge holds the cell for each session and writes it. agentDefaultModel is deliberately not that seam — it is the default handed to future agents, so writing it would leave the running session on its old model while the picker claimed otherwise. It is read exactly once, at session creation, for the same reason.

Two consequences worth knowing:

  • If @deepseek-ai/dsh-agent cannot be loaded, no selector is published at all. A picker that silently changes nothing is worse than no picker.
  • Until the bridge knows the route — no configured model, no deployment default — nothing is published either. The first request/context reveals what the session actually ran, and the selectors appear then, via config_option_update. That adoption never overwrites a user's choice.

Switching model drops a reasoning effort the new model does not declare, and refuses outright if the session already carries images and the target model's inputModalities explicitly omits image. An adapter that declares no modalities is treated as unknown, not text-only.

Capabilities are reported from what the deployment composed, never from what this package can do in principle. No ctx.attachments turns off promptCapabilities.image; no sessionPersistence provider turns off loadSession and makes session/load answer -32601, which smelt classifies as UnsupportedLoad and handles by opening a fresh session. An advertised capability the runtime cannot serve turns a graceful degradation into a mid-turn protocol error.

Not implemented

  • Permission presets and other settings sections as selectors. Only the model and reasoning effort are published. registerSessionConfig is the seam for adding more without editing the bridge.
  • Agent presets as a selector. The harness only lets a session change preset while it is still blank, so a picker that stops working after the first turn would be worse than none.
  • Live terminal output, per the terminal-card note above.
  • sse and acp MCP transports. The harness MCP client speaks stdio and Streamable HTTP only; mcpCapabilities reports this honestly, and a server using another transport is skipped with a named warning rather than failing the session.
  • Compressed session history in smelt's sidebar. smelt reads session.jsonl directly and has no zstd decoder, which is why the reference profile sets compression: none.
  • Image input — blocked upstream, not a choice here. The bridge advertises promptCapabilities.image from the presence of an attachments service, and the only published implementation, @deepseek-ai/dsh-attachment-local, exists solely on the 0.0.1-rc.1 train, which will not resolve against a 0.1.0-rc.8 tree. Until it is republished, a composed runtime reports image: false and that report is accurate. The bridge code path needs no change when it lands; mount the service and the capability flips.

What the unit tests cannot catch

tests/harness.ts fakes the harness's services. It does not model cordis's loader, and the first live boot failed three times before serving a single frame — each time with all unit tests green:

  1. export default apply — the loader takes a module's default as the whole plugin when present, and a bare function carries no name and no inject. Every first-party harness plugin exports the named triple and nothing else.
  2. inject = { required, optional } — this loader's Inject.resolve accepts an array or a name -> intercept config map, so that form asked for two services literally named required and optional and the entry waited for them forever (1 entry did not activate).
  3. A profile that fails schema validation — dsh-session-query-sqlite requires path, and an invalid block takes down the whole tree, not just its plugin.

tests/plugin-contract.spec.ts now asserts (1) and (2) against the loader's own normalization rules, plus the package layout that made main dangle. (3) has no unit-test equivalent: boot the tree after editing the profile. The two-frame initialize + session/new check under "Install and run" is the whole gate and it needs no API key.

Publishing

The @smelt-ai org exists on npmjs.org and is separate from the GitHub org of the same name. Releases are cut by hand from a logged-in machine:

cd dsh-acp-rich
npm version <patch|minor|major>   # or edit "version" and commit
npm publish --otp=<code>          # prepublishOnly: build → typecheck → test → verify

The --otp is not optional: the publishing account has two-factor auth set to auth-and-writes, so a publish without a current code fails with EOTP after building and packing. A granular automation token is the only way to publish unattended — those bypass 2FA by design.

publishConfig.access is public, which matters: scoped packages publish as restricted by default, and a restricted publish is rejected outright without a paid plan.

prepublishOnly exists because of a failure mode npm does not catch on its own. lib/ is gitignored build output, so packing a tree that was never built succeeds with exit 0 and produces a tarball containing the README, the bin, and no source whatsoever. A published version number can never be reused and unpublishing is restricted after 72 hours, so that mistake is permanent. scripts/verify-package.mjs therefore asserts against npm pack --dry-run --json — the real artifact, not a re-implementation of npm's files semantics — that main, types, every bin, the profile, the README, and the LICENSE are all present, that more than one module compiled, and that no test-only file leaked in.

Smelt launches the binary from the selected native profile's dependency tree, so Node resolves the exact plugin version installed by dsh plugin. The launch path is only worth changing if smelt also learns to provision that tree.

Upgrades

Supported dsh trains

peerDependencies names 0.1.0-rc.8 || 0.1.1-rc.2 || ^0.1.2-rc.1 rather than a single range, because npm's semver rejects a prerelease whose major.minor.patch tuple no comparator matches: >=0.1.0-rc.8 <0.1.3 would not admit 0.1.1-rc.2.

0.1.1-rc.2 is byte-for-byte compatible with 0.1.0-rc.8 at the package surface. For 0.1.2-rc.1 (and 0.1.2 releases):

  • Session events on-demand: dsh 0.1.2 removed Session.events in favor of snapshotEvents(). This bridge transparently reads from snapshotEvents() when available and falls back to .events on older runtimes via readSessionEvents.
  • Presets: The shipped agent presets moved to @deepseek-ai/dsh-agent-presets (under presets/, with code renamed to ptc). In a 0.1.2 profile, ensure agent-presets.roots points to the presets directory if custom or shipped presets are joined.
  • Official @deepseek-ai/dsh-acp: The official ACP server in dsh remains automation-only ("never private DSH presentation data"). This bridge provides full presentation (rich diffs, tool cards, plans, reasoning, and lease management).

dsh is a developer preview whose README says "THERE WILL BE COMPATIBILITY-BREAKING CHANGES", and SessionEventMap, ContentBlockMap, and ToolCallView are declaration-merged open vocabularies — an upstream rename never surfaces as a compile error even with hard type imports. That is why this package restates the shapes it reads (src/harness.ts) behind runtime guards instead of importing them: an unknown shape degrades to "no card" rather than throwing inside a session-event listener, and the whole blast radius is one file.

After bumping the supported dsh release train, re-run:

npm test          # 216 unit + contract tests, no harness install needed
npm run typecheck
npm run build && npm run verify-package

then boot the tree — a green unit suite has already shipped a bridge that could not start (see "What the unit tests cannot catch"):

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{}}}' \
| dsh-acp-rich --profile web

then check by hand, against a real session:

  • Streaming text and reasoning both arrive incrementally
  • An edit shows an inline diff, not a generic card
  • A bash call shows the command, then its output and exit status
  • todo_write moves the PLAN panel
  • The usage gauge has a denominator
  • Slash commands are listed
  • A permission prompt appears, and "always allow" suppresses the next one
  • ESC cancels mid-turn
  • A session resumes from the sidebar with its transcript
  • Cross-agent send_message works (proves MCP passthrough)
  • The model picker lists every provider route, and switching it actually changes the model the next step runs on (a switch lands on the next step by design, not mid-request)

A silent regression in this bridge looks like a missing card, not a crash, so the checklist is the test.

Layout

FileRole
src/harness.tsEvery harness shape this package reads, with runtime guards. The single blast radius for upstream drift.
src/cards.tsTool-card mapping. A registry, not a switch: registerCallCard / registerResultCard return restore-disposers, so a deployment adds a card without patching this package.
src/codec.tsPure mappers (chunks, plans, usage, prompt content).
src/grants.tsThe self-built authorization cache.
src/mcp.tsACP mcpServers → mcp-client configuration, mounted per agent scope.
src/config.tsSession config options. Also a registry: registerSessionConfig adds a selector without patching this package.
src/index.tsThe cordis plugin: a dispatch table over harness events, plus the ACP method surface.
src/capabilities.tsOne-shot probe behind dsh-model-capabilities: reports the reasoning efforts each route actually advertises, so clients need not hardcode them.
src/discovery.tsOne-shot probe behind dsh-discover-models: forwards dsh's own GET /models discovery so a relay's catalog need not be retyped.
bin/dsh-discover-models.mjsIts launcher. Reads the request from stdin so an apiKey never lands in ps output.
profile/smelt-discovery.patch.ymlBundle patch that mounts the discovery probe; shipped disabled and enabled only by that bin.
scripts/verify-package.mjsPublish guard. Asserts the real npm pack output carries every file the manifest promises, so an unbuilt tree cannot reach the registry.