Preface

If you are running agents using DSH (DeepSeek Harness) in web mode, you have likely encountered these two problems:

  1. When tool calling triggers the approval window, if you click “Reject”, the model doesn’t stop; instead, it continues to the next round of thinking, guessing the reason for your rejection and running a different plan.
  2. When you manually stop a task, the rejection event isn’t added to the model’s context, so the next request proceeds as if nothing happened.

In real-world collaboration, rejections usually come with a reason: “Don’t touch this file, move it to the working directory.” The default approval flow neither receives this reason nor allows the model to rethink based on it. The dsh-approval-comment plugin was written to fill this gap.

What is this

dsh-approval-comment is a DSH Web approval enhancement plugin, maintained by MaYiFei1995, under the MIT license, current version 0.1.0. Its positioning is to seamlessly replace the built-in approval window: the approval window remains unchanged (warning bar, tool name, Allow Once/Reject), but a new “Comment” input box is added; after rejection, it terminates the current turn and lets the model rethink combining the comment.

It is a pure host + client split plugin, zero third-party dependencies, zero build steps (pure JS), and does not modify any @deepseek-ai/dsh* core packages, so core upgrades won’t overwrite this plugin. Install it in the web profile and it takes effect; it automatically loads on the next dsh web start.

The README states: This project’s code and documentation are generated by the deepseek-v4-pro model.

Core Behavior

The plugin supports four combinations: “Allow Once / Reject × No Comment / Has Comment”:

Allow Once Reject
No Comment Continue Execution Interrupt Task (Stop, wait for next instruction)
Has Comment Comment takes effect, continue execution Adjust based on comment and auto-continue (Do not stop)

Several key points:

  • Reject with Comment: The comment is injected into the current turn via session.prompt(…, 'steer'). After the model reads “Reject + Comment”, it adjusts accordingly, auto-continues, and does not stop.
  • Pure Reject without Comment: The agent/pre-step guard returns { kind: 'reject' } to terminate the current turn; the model stops and waits for your next instruction.
  • Allow with Comment: The comment is also injected via steer; the tool executes normally, and the model reads the comment in the next step and adjusts based on it.
  • grant-next One-time Pass: The over-privileged call that the model adjusts after adding a comment is automatically allowed. The approval window only pops up once.
  • Both rejection and comment are left in the transcript; context is not lost even after manual stopping.

Compatibility for unattended scenarios is also provided: approval/policy = 'never' auto-rejection short-circuits before the response chain and is not affected by the stop guard; CI behavior remains consistent with the current state.

How it works

The plugin consists of two halves:

Half File Function
Host lib/index.js approval/request pre-listener: observes rejection results + grant-next one-time pass; agent/pre-step guard: pure reject -> { kind: 'reject' } terminate turn; reject+comment -> allow
Client lib/client.js Replaces the built-in approval panel with conversation.composer linked at priority: 0, adds comment box; when Allow/Reject has a comment, the comment is injected via session.prompt(…, 'steer') before responding

Taking “Reject with Comment” as an example, the full sequence is:

Model calls a tool requiring over-privilege
  -> Approval panel (this plugin's version) pops up, comment box is available
  -> User fills in explanation and clicks "Reject"
      a) Comment is injected into current turn via `session.prompt(…, 'steer')` (interruption)
      b) Response `rejected` -> Tool result = the user rejected tool "X" (Core text, immutable)
  -> `agent/pre-step` guard: detects just rejected and comment in this step's message -> Allow (do not terminate)
  -> Model reads "Reject + Comment" -> Adjusts based on comment and auto-continues
  -> Adjusted over-privileged call hits `grant-next` -> Automatically allowed, no second approval window pops up

Installation and Enablement

Prerequisites: Node >= 18. The official recommendation is to install remotely from GitHub Release:

dsh plugin --profile web add https://github.com/MaYiFei1995/dsh-approval-comment/releases/download/v0.1.0/dsh-approval-comment-0.1.0.tgz

If the target device cannot access the network, download the .tgz from the Release (or generate it locally with npm pack) first, then install using a local path:

# Absolute path
dsh plugin --profile web add C:\path\to\dsh-approval-comment-0.1.0.tgz

# Relative path (anchored to the directory where the command is executed)
dsh plugin --profile web add ./dsh-approval-comment-0.1.0.tgz

You can also install directly from git source:

dsh plugin --profile web add git+https://github.com/MaYiFei1995/dsh-approval-comment.git

Note: Installing from a git source runs the prepare script. pnpm blocks this by default, so you need to add the package name to allowBuilds in the profile’s pnpm-workspace.yaml as prompted by pnpm. Release tarballs are pre-built artifacts and do not require this step.

After installation, restart dsh web (Ctrl+C then dsh web again) for it to take effect. Verify with the following command:

dsh --profile web --dump-config   # You should see the approval-comment line

Uninstall:

dsh plugin --profile web remove dsh-approval-comment

Restart dsh web after uninstallation for it to take effect. You can use --dump-config again to confirm the approval-comment line has been removed.

After changes to the client half, a browser hard refresh is required (no hot update in non-dev mode). Confirm this first when troubleshooting “changes not taking effect”.

Build and Distribution

If you want to modify the code or publish it yourself:

npm run check   # Contract validation (host/client halves, package.json, cordis.patch.yml mount contracts)
npm run build   # Alias: same as check (this plugin is pure JS, no transpilation needed)
npm pack        # Produces dsh-approval-comment-<version>.tgz

The repository comes with GitHub Actions: push runs syntax + contract validation and produces a tarball, and pushing a v* tag automatically publishes the tarball as a Release asset.

Known Boundaries

You need to understand a few boundaries before using:

  • grant-next is one-time authorization and turn-scoped: It only allows the over-privileged call immediately following the comment; turn/end or leaving the comment adjustment step invalidates it, and it does not persist across turns.
  • When Allow + Comment, original command executes first: The approval semantics are “approve this call”. The comment cannot rewrite already determined parameters. The old parameter command executes first, and the model automatically corrects it based on the comment. To skip the old parameters entirely requires upstream support for parameter modification execution.
  • The approval panel uses reason (or “Tool X requests over-privileged execution”) as the title. The original command text displayed additionally by the built-in panel is not replicated.
  • If the target device has no available approval UI (non-web scenarios), the response chain falls back to unavailable, behaving consistently with the current state.

Applicable Scenarios and Notes

This plugin is suitable for developers who frequently interact with the approval window in dsh web: you want to attach a reason when rejecting and let the model correct based on your intent, rather than guessing; you also don’t want conversation context to be lost after manual stopping. If you mainly run unattended/CI workflows, the existing approval/policy = 'never' behavior is not affected, so there is no need to install this.

Finally, a reminder: The plugin runs with the permissions of the current dsh process. Before installing, it is recommended to check the source code (this plugin consists of just two files lib/index.js and lib/client.js, both pure JS and zero dependencies, so the review cost is low) and confirm the license terms.

Conclusion

dsh-approval-comment solves a very specific problem: making rejections in the approval interaction include a reason and allowing the model to rethink based on that reason, rather than guessing or losing context. Implementation-wise, it doesn’t touch core packages, has zero dependencies, and can be rolled back anytime during installation or uninstallation.

  • GitHub: https://github.com/MaYiFei1995/dsh-approval-comment
  • Community Directory: https://www.skillhub.cn/plugins/MaYiFei1995/dsh-approval-comment

The community directory is an independent site with no official affiliation to DeepSeek or Fangzhuan.