# DeepSeek Harness (dsh) Tool-Call Control — Install Guide

Install ACP for DeepSeek Harness via its native plugin system. Every tool call — bash, file ops, web, subagents, Code Mode sub-calls — policy-checked before it runs, with dsh's own approval prompt handling asks.

# DeepSeek Harness (dsh)

**TL;DR:**

```bash
dsh plugin --profile <your-profile> add @agenticcontrolplane/dsh
export ACP_BEARER_TOKEN=gsk_...   # or keep it in ~/.acp/credentials
dsh --profile <your-profile>
```

From the next boot, every tool call flows through your ACP workspace before it runs. Already ran [the installer](/install-explained)? It detects dsh and installs the plugin into your existing profiles automatically.

dsh is in developer preview with breaking changes promised — the plugin tracks it, and this page records the sharp edges we've hit.

*(Looking for `@deepseek-ai/dsh-acp`, the Agent Client Protocol server for editors? That's an unrelated project sharing the acronym — [/acp-vs-acp](/acp-vs-acp) routes you.)*

## Why a native plugin instead of a shell hook

dsh is built on Cordis: the harness's own tools and policy are plugins on typed extension points, and [@agenticcontrolplane/dsh](https://github.com/agentic-control-plane/dsh-acp-plugin) is an ordinary plugin on the same points — no subprocess per tool call, no JSON-over-stdin dialect. ~190 lines of plain ESM, zero dependencies, no build step.

(dsh also ships a `hooks-claude-code` bridge that runs an unmodified Claude Code `hooks.json`, so our shell-hook `govern.mjs` works today with zero new code — deny and ask are honored, input rewriting is not. Use the native plugin; the bridge is a compatibility note, not the recommended path.)

## How it works

| dsh extension point | ACP endpoint | What happens |
|---|---|---|
| `tools/pre-execute` | `POST /govern/tool-use` | Server returns `allow` / `ask` / `deny`. Deny skips the call with the reason in the trajectory; ask feeds dsh's own approval prompt. |
| `tools/post-execute` | `POST /govern/tool-output` | Output scanning. A server block turns the result into corrective feedback; a redact replaces the content the model reads. |

Everything dispatches through that one pipeline — `bash`, `pwsh`, `read`/`write`/`edit`, `glob`/`grep`, `web_fetch`/`web_search`, subagent spawns, and the serialized sub-calls inside Code Mode. Coverage details on the [harness coverage matrix](/coverage).

**Asks with nobody watching deny themselves.** In a headless composition with no approval service mounted, dsh resolves `ask` to deny by design — an unattended agent cannot self-approve. That's the fail-closed posture for unattended runs, shipped by the harness itself.

**Failure posture** is the standard one: interactive sessions fail open with a loud `[ACP] ⚠ UNGOVERNED` warning and a `~/.acp/lapse.log` entry when the gateway is unreachable; unattended tiers fail closed. Policy denies are unaffected.

## Configuration

Override the plugin row in your profile's `cordis.patch.yml`:

```yaml
- id: acp
  name: "@agenticcontrolplane/dsh"
  config:
    governBase: https://govern.agenticcontrolplane.com  # or self-hosted
    agentTier: interactive   # default: interactive when an approval service is mounted, background otherwise
    timeoutMs: 4000
```

`ACP_GOVERN_BASE`, `ACP_BEARER_TOKEN`, `ACP_AGENT_TIER`, and `ACP_SHADOW=off` work as environment variables.

## Three sharp edges (developer preview)

**`packages/acp` inside dsh is not this.** It's Zed's Agent Client Protocol — an editor-integration protocol that shares the acronym. `dsh-plugin-acp` is the Agentic Control Plane.

**Node 22 required.** dsh uses `Promise.withResolvers` and zstd streams from `node:zlib`; under Node 20 it fails at boot with errors that don't mention Node. `fnm install 22` first.

**Bare `npx` launches can't resolve profile-installed plugins (rc.6).** `dsh plugin add` links the package into the profile, but an npx-run dsh resolves bare names from the npx cache. A `--patch` overlay loading the plugin by file path works everywhere; a normal dsh install resolves fine.

## Add the cost X-ray

The plugin governs and audits tool calls; it never sees the model call, so it cannot price it. The installer writes an `acp` provider into `$DSH_HOME/settings.yaml` (verbatim shape from dsh's providers guide, key read from `ACP_BEARER_TOKEN` via `apiKeyEnv`, nothing pasted) and installs `dsh-acp` next to `claude-acp` and `codex-acp`:

```bash
dsh-acp --profile <profile>
```

`dsh-acp` reads your workspace key from `~/.acp/credentials`, exports it as `ACP_BEARER_TOKEN`, and starts plain `dsh` with a one-line note if the gateway is unreachable. dsh has no model flag, so choose provider `acp` (model `gemini-3.5-flash`, or any `gpt-*` / `claude-*` / `gemini-*` id the proxy routes) as the default model of the profile you launch. If your settings.yaml already has an `llm-pi-ai.providers` section the installer leaves it untouched and tells you to add the block by hand. Full walkthrough: [Turn on Cost X-Ray](/cost-tracking).

## What you'll see in the dashboard

A `dsh-plugin` client row on [cloud.agenticcontrolplane.com](https://cloud.agenticcontrolplane.com): tool name, input preview, decision, reason, latency, session and workspace attribution — same shape as every harness. Since 0.1.1 the plugin forwards dsh's per-call id, so the tool-use and tool-output rows pair exactly in the Calls view. dsh's own append-only trajectory independently records the same calls: two witnesses to one history.

## Troubleshooting

**`[ACP] ⚠ UNGOVERNED: no credential` at boot.** No `ACP_BEARER_TOKEN` and no `~/.acp/credentials`. Get a key at [cloud.agenticcontrolplane.com](https://cloud.agenticcontrolplane.com) — without one the plugin stays out of the way rather than bricking the session.

**Boot fails with `Promise.withResolvers is not a function`.** Your Node is 20; dsh needs 22. `fnm use 22` (or equivalent) and relaunch.

**`failed to import loader entry acp`.** You're launching via bare `npx` — see the sharp edges above; use a `--patch` overlay by file path or a normal install.

**Every call asks.** Same as any harness: an over-broad ask rule in your workspace policies. Approve with "make this a rule" to scope it down.

## Related integrations

- [Claude Code](/integrations/claude-code) — shell-hook runtime, same backend contract
- [Hermes Agent](/integrations/hermes) — the other full-coverage native plugin
- [Codex CLI](/integrations/codex) — partial hook coverage, needs MCP supplement
- [Harness coverage matrix](/coverage) — the living cross-harness table

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "Install Agentic Control Plane in DeepSeek Harness",
  "description": "Install ACP tool-call control for DeepSeek Harness (dsh) via its native plugin system.",
  "totalTime": "PT1M",
  "step": [
    {"@type": "HowToStep", "name": "Install plugin", "text": "dsh plugin --profile <your-profile> add @agenticcontrolplane/dsh"},
    {"@type": "HowToStep", "name": "Authenticate", "text": "export ACP_BEARER_TOKEN=gsk_... or keep the key in ~/.acp/credentials"},
    {"@type": "HowToStep", "name": "Relaunch", "text": "dsh --profile <your-profile> — the policy check is active from boot"}
  ]
}
</script>
