grunmin/dsh-acp-enhanced ↗★ 7

dsh-acp-enhanced

提供增强型ACP服务,支持流式传输与监控 适合使用Zed等ACP编辑器的开发者,提供更丰富的遥测与控制。

包名
dsh-acp-enhanced
兼容性
待验证
Harness 依赖范围
^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.2.0-rc.2 || ^0.2.1-rc.2
Cordis 依赖范围
^4.0.1-rc.1
版本
0.11.1
许可证
MIT
最近更新
2026年10月22日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:grunmin/dsh-acp-enhanced

中文 | English · Changelog

dsh-acp-enhanced

An enhanced Agent Client Protocol (ACP) server for DeepSeek Harness (dsh), built for ACP editors like Zed. It is a drop-in replacement for the official @deepseek-ai/dsh-acp bridge: the official bridge only streams plain text, this one exposes the Web GUI's capabilities — streaming, telemetry, model/permission control, session management, MCP — over the ACP wire.

Features

Output & telemetry

  • Token-level streaming, on by default: the reply and the model's thinking reach the editor while it is still writing (agent_message_chunk / agent_thought_chunk, coalesced on a 75 ms timer). The one cost of sending text before its block commits is that a mid-block retry cannot take its abandoned partial back: a visible _[stream interrupted — retrying]_ marker separates it from the retried attempt. Set streamDeltas: false on the acp-enhanced row (DSH_ACP_STREAM_DELTAS=false) for the older block-granularity behaviour, where cancelled/retried attempts never leak torn output at all but nothing renders until the block commits
  • Full telemetry: context usage ring plus cache hit rate / TPS / input-output-reasoning tokens / tool timing / turn counts (usage_update._meta carries the full breakdown). The ring's denominator is the routed model's declared contextWindow, folded from the session log so a resumed thread keeps it (the harness logs request/context once and does not re-emit it for an unchanged route). dsh's own dsh-compaction-basic row auto-compacts at ~80% of that window and is mounted by the standard/ptc/cordis presets (never minimal); /compact forces it manually. A route whose adapter declares no contextWindow has no denominator, so the ring reports used === size.
  • Image support (multimodal): when the dsh composition mounts an attachment store (dsh-attachment-local, mounted by default in dsh-base), promptCapabilities.image is advertised and pasted/uploaded images are ingested into the harness's durable attachment store — a vision-capable model (e.g. deepseek-v4-flash-vision-exp) reads them natively, in wire order with surrounding text. Older stacks (no attachment store) automatically downgrade: image is not advertised and an image prompt is refused with a clear error.

Model & permissions

  • Model switching: live provider/model catalog dropdown (ACP grouped-select wire shape)
  • Reasoning effort: reasoning_effort dropdown — only when the routed model exposes selectable efforts; each model remembers the effort it last used (persisted per profile), so switching back restores it, and a first-time model falls back to its own default — or its first offered effort — instead of an empty "unknown" selection
  • Permission presets: read-only / workspace-write / full-access session modes
  • Approval: native allow-once / reject-once prompts per tool call
  • Agent presets: per-session model-facing composition (tools + prompt sections) from the dsh agent-preset roster. standard is the full coding agent (default), minimal (极简模式) is a bare persistent shell with no subagent/web/todo/plan tools — nothing from the host layer leaks into a minimal agent; ptc and cordis ship alongside. Your own presets are declared in the profile (see Your own presets) — dsh 0.1.7 no longer discovers ~/.dsh/.agent-presets. Choose via the agent_preset config option, the /preset command, or the DSH_ACP_PRESET env var (per-session default); switching is only allowed while the session is still blank (no turn has run), so history never straddles two tool sets. A configured preset this roster no longer has does not close the panel: a blank session composes under the roster default and says so on stderr.

Zed deep integration

  • Tool cards: one-line summary in the collapsed header — Read , the model's own intent line for shell commands (description, Codex-style — the exact command stays one click away), Search: , Fetch: , etc. The card body follows the ACP best practice: file edits render as a real diff, bash/pwsh commands as a real terminal card (codex-acp wire shape: command line + output + exit pill inside a terminal panel — no more raw-JSON cards), other executors as a syntax-highlighted code block, and touched files as clickable locations that open the file — with rawInput / rawOutput kept one click away for transparency, plus per-kind icons and a proper in-progress → completed/failed status lifecycle
  • Zed files & terminal: zed_read_text_file / zed_write_text_file / zed_terminal put file edits into Zed's "edited files" area (diff + accept/reject) and commands into a real Zed terminal
  • Native form questions: ask_user_question → elicitation/create form, click an option — or type a custom answer when none of them fit: options render with their descriptions, each option-backed question gets a free-text "Custom answer" field, and a custom answer replaces the single selection / accompanies a multi-select (same semantics as dsh's native question card)
  • Plan panel: plan mode toggle → "planning" status bar in Zed

Sessions

  • Resume & archive: session/load restores past threads (full replay); session/list lists the thread archive (titled, sorted by last activity); session/close drops the in-memory record so a later session/load resumes from the persisted log; live title updates. session/delete is deliberately not advertised — the harness declares no public persistence delete (see Compatibility)
  • Multi-root workspaces: sessionCapabilities.additionalDirectories is advertised, so Zed no longer shows "this agent doesn't currently support multi-root workspaces" and instead passes every workspace root on session/new / session/load. All roots are described to the model in the system prompt and reported on session/list; the sandbox keeps the primary cwd as its single writable root (see Known limitations)

Commands

  • Slash commands: typing / reveals the command list (available_commands_update): /status shows the route and telemetry, /model lists or switches the model, /preset lists or switches the agent preset (listings render as monospace code blocks — readable at a glance), everything else (/compact /goal /permission /plan…) runs straight through the harness command registry — all executed without a model turn. Every user-invocable skill is advertised as a command too, so /ask-matt, /code-review, /tdd, … reach the bridge instead of being rejected by the editor, and the skill's instructions are injected into the message (the @deepseek-ai/dsh-tool-skill style of user invocation). Images pasted next to a slash line ride along as command attachments (e.g. reference screenshots for a /goal objective), the same way the Web composer submits them

MCP

  • MCP servers: session/new mcpServers mount any MCP server (stdio + streamable HTTP); tools join as mcp____; a failing server never takes the session down

Preview

After picking dsh-acp-enhanced in Zed's AI Agent panel:

Quick start

Requires dsh ≥ 0.1.6-alpha.1 (npm install -g @deepseek-ai/dsh@0.2.1-rc.2, or any version in the peer range below); the bridge consumes one declared harness surface on every supported line — 0.1.6-alpha.1 through 0.2.1 — and never probes older generations at runtime.

This package follows the official dsh plugin conventions (it declares dsh.bundle), so installation matches any official bundle: one command — auto-initializes the profile, installs the package, appends the bundle layer; no profile YAML to write.

Install (2 steps)

Step 1 — install (from the npm registry; no source checkout needed):

dsh plugin --profile acp-enhanced add dsh-acp-enhanced

When hacking on the code, use link: to a local checkout instead (live edits): dsh plugin --profile acp-enhanced add "link:/absolute/path/to/dsh-acp-enhanced"

Step 2 — register in Zed (under agent_servers in ~/.config/zed/settings.json; Zed spawns agents with a minimal PATH, so use the shipped launcher scripts/dsh-acp-zed.sh, which locates node/dsh itself)

The launcher ships with the package. Its absolute path depends on how you installed in Step 1:

  • npm install (default): $HOME/.dsh/profiles/acp-enhanced/node_modules/dsh-acp-enhanced/scripts/dsh-acp-zed.sh — replace $HOME with your home directory (e.g. /Users/you); Zed does not expand ~ or env vars, so write the full literal path.
  • link: dev install: /scripts/dsh-acp-zed.sh.
Most common: DeepSeek official API (the default route)
{
  // ...your existing settings...
  "agent_servers": {
    "dsh-acp-enhanced": {
      "type": "custom",
      "command": "/bin/bash",
      "args": ["/absolute/path/to/dsh-acp-enhanced/scripts/dsh-acp-zed.sh"],
      "env": {
        "DSH_ACP_PROVIDER": "deepseek-official",  // the official provider id
        "DSH_ACP_MODEL": "deepseek-v4-flash",     // the official model id
        "DSH_ACP_PRESET": "standard",             // optional: agent preset id (minimal / standard / code / cordis / yours)
        "DSH_ACP_INCLUDE_ALL_PROVIDERS": "true"   // optional: list every registered provider in the model dropdown
      }
    }
  }
}

Both env vars match the shipped patch's defaults, so they can be omitted entirely — writing them out just makes the route explicit. DSH_ACP_PRESET defaults to standard on the roster side; set it when you want every new session to start in a specific mode. The API key does not have to live in Zed: store it in ~/.dsh/.credentials.yaml (DEEPSEEK_API_KEY) and the dsh credentials service resolves it; the launcher also falls back to a running dsh web process's key.

DSH_ACP_INCLUDE_ALL_PROVIDERS=true opts the model dropdown into every provider the llm service has registered, instead of only the routed one. It is off by default for a reason: a provider without a usable credential still appears, and picking it parks the session on a route that cannot dispatch — the switch fails as an invalid-params error naming the route and the session keeps the one it had. Set it when one profile routes across several providers and you want to pick per session.

Debugging a stalled turn (is it the model request or the tool?):

"env": {
  // ...existing vars...
  "ACP_LOG": "/Users/you/.dsh/dsh-acp-enhanced.trace.jsonl"  // append-only JSONL event trace
}

Each line is one session event with wall-clock time (ms epoch); a turn that appears to hang is attributable afterwards: a model request stall shows a long gap between step/start and the first assistant/chunk, while a tool-execution stall shows a long gap between tool/call and tool/result (the result line carries elapsedMs). prompt/settled lines cover the full user-message round trip (stopReason + elapsed).

Optional: pin the panel's default config options (all still changeable in the panel):

"dsh-acp-enhanced": {
  // ...the type/command/args/env above...
  "default_config_options": {
    "model": "deepseek-official/deepseek-v4-flash",
    "agent_preset": "standard",
    "plan_mode": false,
    "reasoning_effort": "high"
  },
  "favorite_config_option_values": {
    "model": ["deepseek-official/deepseek-v4-flash", "deepseek-official/deepseek-v4-pro"]
  }
}
Extended: route through an OpenAI-Responses gateway (e.g. a company model gateway)

Same install path; only the env values change to the provider/model the gateway exposes plus the key env var it requires:

"dsh-acp-enhanced": {
  "type": "custom",
  "command": "/bin/bash",
  "args": ["/absolute/path/to/dsh-acp-enhanced/scripts/dsh-acp-zed.sh"],
  "env": {
    "DSH_ACP_PROVIDER": "",  // provider id exposed by the gateway
    "DSH_ACP_MODEL": "",         // model id exposed by the gateway
    "": ""                      // the key env var the gateway reads
  }
}

`` can also be omitted and the key stored in ~/.dsh/.credentials.yaml instead.

Zed hot-reloads settings. Open the AI Agent panel (Cmd+Shift+A) → pick dsh-acp-enhanced in the agent selector → send your first message: replies stream in real time, the status bar shows context usage, the panel exposes Model / Permission preset / Plan mode options plus three modes, and the thread archive lists and resumes past sessions.

Verify locally (no Zed needed):

node 
/scripts/acp-doctor.mjs              # bundles + versions, the peer range, and one real boot
node scripts/acp-client.mjs                    # dev checkout only: full ACP e2e, expect ALL CHECKS PASSED
DSH_ACP_PROVIDER=... DSH_ACP_MODEL=... node scripts/acp-client.mjs   # only for a custom route

Web search

The bridge ships no search provider and takes no position on which one you use: the model-facing web_search tool rides on the web seam's searchProvider, so mount any ctx.web provider into the profile — a package with dsh.bundle via dsh plugin --profile acp-enhanced add , or a plain package via your user-layer insert rows (see below). Which provider exists in your dsh deployment is a profile concern, not a bridge one.

Note what that costs: a provider bundle sits on the boot path of every ACP thread, so if it fails to load, the whole profile dies and Zed shows an opaque hang. Prefer a preset composition when the plugin only adds model-facing tools (see Keep the profile minimal); a provider that must configure the host web row belongs in the host composition (the profile) — mount it deliberately, and re-run the doctor after changing it.

Managing the profile's plugins

dsh-acp-enhanced runs in its own profile — acp-enhanced, created at ~/.dsh/profiles/acp-enhanced/ — inside the same dsh home as dsh web. The profile is what isolates the composition, so plugin changes here never affect your web setup, while credentials, settings, sessions and presets stay shared.

The profile composes its plugin tree from three sources, each layer patching the ones before it:

  1. Bundle layers — dsh.profile.bundles in the profile's package.json: the template's @deepseek-ai/dsh-base first, then every installed package that declares dsh.bundle (like dsh-acp-enhanced), in array order.
  2. Your user layer — ~/.dsh/profiles/acp-enhanced/cordis.patch.yml: id-targeted row config overrides, disabled: true row disables, and insert lists (how a package without dsh.bundle — e.g. a hand-mounted custom provider — gets mounted).
  3. Per-run overlays — dsh --profile acp-enhanced --patch extra.yml.

Adjust the set with:

dsh plugin --profile acp-enhanced add 
     # install; a dsh.bundle package auto-joins the layer stack
dsh plugin --profile acp-enhanced remove 
  # uninstall; auto-leaves the stack
dsh plugin --profile acp-enhanced update [package]  # update one/all, then reconcile
dsh --profile acp-enhanced --dump-config             # inspect the composed tree (per-layer provenance)

dsh plugin is a thin pnpm forwarder (run inside the profile directory) that reconciles dsh.profile.bundles against the installed state after every run. Two consequences worth knowing:

  • **Disabling a bundle by deleting it from bundles do