DeepSeek Harness plugin

dsh-autogate

DeepSeek Harness 自动审批插件:在 workspace-write 沙箱之上增加 Auto 档——确定性规则放行/拒绝,模糊操作由 LLM 裁决,危险操作转人工。保留工作区沙箱边界,不放宽为 full-access。

Jump to install

Source facts

Repository
wangxing-git/dsh-autogate
Latest update
Aug 21, 2026
Category
Security & Permissions
GitHub stars
3
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/wangxing-git/dsh-autogate
Plugin: dsh-autogate
Author: wangxing-git

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.en.mdSource · read only
README language

Languages: 简体中文 · English (this page)

dsh-autogate

DeepSeek Harness auto-approval plugin: adds two permission presets — semi-auto (auto-ask) + full-auto (auto) — on top of the workspace-write sandbox, using layered decisions: deterministic rules + LLM safety approval + (in semi-auto) proactive human approval initiated by the denied party. It keeps the workspace sandbox boundary and never relaxes to full-access.

Layered design

LayerDecisionDescription
L0 Deterministic rulesallow / denyZero cost, zero LLM: read-only ops, session state, in-workspace edits and deletes, build/test, and the run_code container pass directly; reads of ordinary paths outside the workspace pass directly; writes/deletes outside the workspace (except writes to sensitive shell/credential config files) pass and rely on the workspace-write sandbox to intercept + escalation popup; writes to sensitive config files outside the workspace go to LLM review; empty commands, dynamic command names, and missing arguments fall through to the sandbox; privilege escalation, self-destruction, credential exfiltration, mutation/deletion of the filesystem root and system/credential-critical paths, and deletion of the home root are hard-denied; home-root mutation and DSH_HOME mutation/deletion go through the generic outside-workspace path (sandbox + escalation)
L1 LLM safety approvalallow / denyOperations the sandbox doesn't intercept but are semantically dangerous (unrecognized tools, ambiguous shell, sensitive path reads, dynamic targets, block devices, persistent terminals, git state changes, network/database operations, writes to protected in-workspace paths) go to a two-state LLM decision: operations explicitly authorized by the user are allowed, reducing manual approvals. Classifier inputs are sanitized then tag-separated (<untrusted> data vs <user-authority> authority) with built-in injection defense; agent instruction files (AGENTS.md / CLAUDE.md / .dsh, etc.) are classified as routine configuration, editable when explicitly authorized by the user
L2 Human approvalaskApproval popups pass the LLM first: reasonable requests are approved directly without a popup, dangerous/uncertain ones fall back to a human popup. Covers three kinds of approval request: ① the AI uses ask_user_question to confirm the operation is legitimate, then re-runs it and passes the LLM again; ② the AI retries with sandbox_permissions + justification to go through DSH's built-in sandbox escalation; ③ a tool/plugin itself declares it requires approval (pre-execute returns ask)

Two modes

Preset keyModeescalation approval fallback
auto-askSemi-auto (default)LLM deny/error → delegate to a human popup (L2 fallback)
autoFull-autoLLM deny/error → deny directly, no human popup (LLM decision is final)

Both modes share the same L0 deterministic rules and L1 LLM classifier; the only difference is the L2 human fallback: semi-auto keeps the human popup, full-auto treats the LLM decision as final. Hard deny (L0 guard) and the preflight switch behave identically in both modes.

Key differences from similar plugins

  • Ordinary calls stay workspace-write: L0/L1 decisions never widen the sandbox, so even if the L1 LLM misjudges, ordinary file writes stay confined to the workspace (unlike similar plugins that run every call with danger-full-access). The L2 escalation channel is the exception: an approved escalation runs that single call with the requested wider sandbox — see the security disclaimer.
  • Unrecognized tools go to LLM classification by default instead of being allowed — but run_code passes directly as a code-execution container; every tool call inside it is still evaluated by this policy and the sandbox.
  • fail-closed: classifier errors / timeouts / no route / malformed output always deny; the denied party (AI) proactively escalates to human approval as appropriate.

⚠️ Security disclaimer

This plugin is a decision layer that reduces manual approvals — not a security boundary. The real enforcement boundary remains DSH's workspace-write sandbox and its escalation approval.

  • The L1 LLM classifier is heuristic and can misjudge (allow a dangerous operation or deny a safe one). fail-closed reduces false allows but cannot eliminate them.
  • The prompt-injection defense (sanitization + <untrusted>/<user-authority> tag separation + anti-injection clause) is a soft defense: it raises the bar but cannot eliminate injection; the hard guarantee remains the L0 deterministic rules and the workspace-write sandbox fallback.
  • Static path checks (including the symlink realpath hardening) still have a TOCTOU window: a symlink can be retargeted after the check passes and before the actual write.
  • In full-auto (auto) mode the LLM decision is final with no human popup — use it only in environments you trust.
  • You remain responsible for the final effect of every approved operation. Review the approval trail, and prefer semi-auto (auto-ask) when in doubt.
  • The L2 escalation path is a one-shot widening: when the LLM approves a sandbox escalation, that single call runs with the requested wider sandbox (typically full-access), not workspace-write. It does not widen other calls, but it is a real one-time elevation — do not read "sandbox stays workspace-write" as covering escalations.

Install

# Install from GitHub (compiled lib/ is committed) dsh plugin --profile web add github:wangxing-git/dsh-autogate # or, if dsh is not on your PATH: npx @deepseek-ai/dsh plugin --profile web add github:wangxing-git/dsh-autogate

# Restart dsh

Configuration

Configuration is wired through the DSH settings service (ctx.settings): write an autogate: section in $DSH_HOME/settings.yaml and it hot-reloads immediately; when the settings service is not mounted, it falls back to the entry config in cordis.patch.yml (config: {}).

> About the settings UI: DSH 0.1.0-rc.7 drops the rc.6 hard-coded allowlist (WEB_SETTINGS_NAMESPACES) for third-party namespaces and lets plugins register their own settings cards through the keyed slot (settings.plugin.item, keyed by namespace). This plugin's settings card reads and writes through the official DSH client settings API (describe / mutate on ctx.connection.api.settings; the batch mutate keeps cross-field constraints such as the provider/model pairing atomic). The approval trail panel still pulls through the plugin's own /autogate RPC endpoint (trail). Writes land in $DSH_HOME/settings.yaml under autogate: and hot-reload, exactly as the manual section below.

autogate: preflight: false # pre-sandbox interception switch: true runs deterministic rules + LLM classification, false (default) relies entirely on the sandbox showTrail: true # approval trail overlay switch: false hides the bottom-right panel and stops polling the trail RPC (default true) presetName: auto-ask # semi-auto preset key (default auto-ask): delegates to a human fallback popup after LLM deny fullAutoPresetName: auto # full-auto preset key (default auto): LLM decision is final, no human popup classifierTimeoutMs: 8000 # classifier timeout (100–60000ms), fail-closed on timeout classifierMaxOutputTokens: 1024 # classifier max output tokens (64–4096) classifierRetry: true # retry once on classifier output parse failure (default true)

# classifierPrompt: | # review (classification) system prompt; empty uses the built-in default

# (custom review prompt judging intent / type / reversibility / impact) # Fixed classifier model (defaults to the current session's provider/model; both fields must be set together) # classifierProvider: deepseek # classifierModel: deepseek-chat # Standalone OpenAI-compatible classification endpoint (optional; must be HTTPS, loopback may use http) # classifierEndpoint: https://api.example.com/v1/chat/completions # classifierApiKeyEnv: DEEPSEEK_API_KEY # environment variable name for the HTTP endpoint API key # workspaceRoot: /path/to/ws # override workspace root (default: session cwd) # tempRoots: [/tmp] # trusted temporary directories (default: system temp dir)

Decision flow

> preflight switch (default false): controls whether the two pre-sandbox steps ("ordinary deterministic rules + LLM classification") run. When false, steps 3 and 4 below are skipped and tool calls go straight into the workspace-write sandbox (fully relying on the sandbox policy); step 2 hard deny and step 5 approval pre-screening (sandbox escalation + tool-declared ask) always apply, regardless of this switch. Set true to restore the full pre-sandbox interception.

1. Non-Auto session: pass through unchanged, official behavior untouched. 2. Auto session: synchronous hard deny (privilege escalation, self-destruction, credential exfiltration, mutation/deletion of the filesystem root and system/credential-critical paths, deletion of the home root) → cannot be overridden by later listeners or the LLM; home-root mutation and DSH_HOME mutation/deletion go through the generic outside-workspace path (sandbox interception + escalation approval). 3. Deterministic allow (read-only, session state, in-workspace edits and deletes, read-only shell, build/test, version probing, run_code container; reads of ordinary paths outside the workspace pass directly; writes/deletes outside the workspace (except writes to sensitive shell/credential config files) pass and rely on the workspace-write sandbox + escalation, writes to sensitive config files outside the workspace go to LLM review; empty commands, dynamic command names, missing arguments fall through to the sandbox). 4. Operations the sandbox doesn't intercept but are semantically dangerous (ambiguous shell, sensitive path reads, dynamic targets, block devices, persistent terminals, git state changes, network/database, writes to protected in-workspace paths) → LLM two-state decision (allow / deny). 5. Approval requests uniformly pass the LLM first (semi-auto auto-ask mode keeps the human fallback): a. use ask_user_question to confirm the operation is legitimate, then re-run after the user confirms and pass LLM approval again; b. retry with sandbox_permissions + justification on bash/pwsh to go through DSH's built-in sandbox escalation — a reasonable escalation (explicitly authorized by the user) is approved directly without a popup, and that single call then runs with the requested wider sandbox (typically full-access); dangerous/uncertain cases show a human popup; c. a tool/plugin itself declares it requires approval (pre-execute returns ask) — this plugin likewise runs the LLM first: reasonable requests are approved directly without a popup, dangerous/uncertain cases show a human popup.

Full-auto auto mode: approval requests (sandbox escalation + tool-declared ask) are decided by the LLM as final — allow approves directly, deny / classifier error denies directly, no human popup.

Approval trail UI

While the plugin is active, a floating Approval trail toggle appears in the bottom-right corner of the DSH web UI (injected into the shell.overlay slot). It is only visible once at least one decision has been recorded.

  • The toggle shows the current record count, a decision-colored dot for the latest record, and allow/deny/ask counts; it expands/collapses the panel.
  • The panel lists the most recent 50 records (the trail itself is a process-level ring buffer capped at 200; the panel shows the newest window).
  • The panel is scoped to the current session: it shows only records produced by the currently selected session (subagent calls are attributed to their top-level parent session); a scope toggle temporarily switches to All sessions, and with no session selected it shows all records (the toggle is transient and resets to current-session scoping after a reload).
  • Each entry shows the decision as a colored badge (green = allow, red = deny, orange = ask), a layer badge (L0 deterministic / L1 LLM / L2 human), the tool name (monospace), and the local time; the collapsed state still previews the one-line operation summary.
  • Expanding an entry reveals the one-line operation summary, the deny/allow reason, the tool callId, the local time, and the decision duration.
  • The Locate (定位) button scrolls the session view to the corresponding tool call.
  • Data is polled from the plugin's trail RPC every 2 seconds (the last snapshot is kept on failure).
  • The overlay can be disabled with showTrail: false (or in the settings card): the panel is hidden and the client stops polling the trail RPC entirely; the trail itself keeps recording server-side.
  • The trail is process-level and in-memory only: it resets when dsh restarts and is never persisted.
  • The deny/allow reason follows the DSH setting language (zh/en): it is English when en is explicitly set, otherwise (including when unset) it falls back to Simplified Chinese, matching the UI language.

Directory structure

src/ index.ts Entry: guard + tools/pre-execute two-state decision + escalation retry allow + approval-trail RPC policy.ts Tool-level deterministic rules (L0) and danger detection shell.ts bash/pwsh static analysis (L0 hard deny + dangerous shell detection) classifier.ts LLM classifier (DSH built-in LLM / optional HTTP endpoint) + sanitization + tag separation + injection defense + system prompt paths.ts Path normalization, dangerous-path detection, workspace-root resolution trail.ts Approval trail (process-level ring buffer, append-only, not persisted) types.ts Shared types client.tsx Settings UI card + approval-trail panel (client bundle) client-logic.ts Client UI logic (official settings API source / form controller / trail controller / i18n strings) tests/ Tests (paths / shell / policy / classifier / trail / settings / client-logic / index) scripts/ build-client.mjs Client bundle build script fix-session-zstd.py Session zstd repair script cordis.patch.yml Permission preset table (inserts auto-ask semi-auto + auto full-auto, sandbox=workspace-write) lib/ Compiled output (generated by build and version-controlled; do not edit by hand)

Known limitations

  • Path containment is evaluated on real identity (the deepest existing ancestor is resolved through realpath), so a symlink inside the workspace no longer bypasses L0/L1 classification; the remaining TOCTOU window is a symlink retargeted between the check and the actual write, which the workspace-write sandbox still catches.
  • Deleting files inside the workspace is allowed directly, relying on the workspace-write sandbox as the fallback; session artifact tracking is not performed, and deletes outside the workspace also pass and rely on sandbox interception + escalation.
  • The classifier defaults to reusing the current session model; if the session uses a third-party provider, classification requests go to that provider (sanitized and bounded).
  • The preflight switch defaults to false: ordinary tool calls rely entirely on the workspace-write sandbox, and only the hard-deny guard and the escalation pre-approval run by default. Set preflight: true to add deterministic rules + LLM classification on every call.
  • Credential-exfiltration detection is a shallow text pattern (it cannot see base64-encoded or chunked secrets); treat it as a tripwire, not a guarantee.
  • The classifier's injection defense is a prompt-level soft constraint (sanitization + tag separation + anti-injection); adversarial injection may still induce misjudgment. Final interception of dangerous operations relies on L0 hard deny and the workspace-write sandbox.

License

MIT