Preface

If you have used ask_user_question in DeepSeek Harness (DSH), you may have encountered a scenario where the agent asks a question, and the question card is pushed to the browser once via a mux WebSocket. Once the page is switched to the background, the laptop goes to sleep, or the connection enters a half-open state, that frame is lost—the client has no heartbeat and cannot perceive the existence of the question, so the tool waits indefinitely. We have observed hangs lasting over 6 hours in practice, and the only recovery method is manually pressing Stop. Upstream feedback can be found in deepseek-ai/deepseek-harness#1554 (discussion).

dsh-ask-guard is a plugin designed for this problem: questions that are lost or unanswered will end with a structured ASK_TIMEOUT instead of letting the turn hang forever. Below is an introduction to its principles, installation, and configuration.

What is this

dsh-ask-guard is a timeout guard plugin for ask_user_question in DSH, maintained by Q1hangL, currently version 0.1.0, with an MIT license (marked in both README and package.json). It embodies the DSH philosophy of “everything is a plugin”: instead of modifying the harness itself, it fills the gap that the official dsh-tool-call-timeout-policy does not cover through a tool execution wrapper. The latter only enforces budgets declared by tool plugins, while ask_user_question declared no budget, so there was previously no fallback mechanism.

How it works

The plugin’s function can be broken down into three steps:

  1. Register a tools/execute wrapper to set a cooperative deadline (deadline) for every ask_user_question call.

  2. When the deadline is triggered, the abort handler of the waiting provider is triggered: the web provider broadcasts question/resolved cancelled to clean up the stuck input box (composer).

  3. The wrapper replaces the normalized abort with a structured error result (code: ASK_TIMEOUT, name: AskTimeoutError) and attaches a message for the model, allowing the agent to gracefully end the turn instead of blocking there.

There are two noteworthy design choices here:

  • The timeout is cooperative. It is signaled via exec.signal by @deepseek-ai/dsh-timeout, and the web user-questions provider responds to this signal to terminate for real, rather than just faking a timeout at the tool layer.
  • Other tools pass through unchanged. The wrapper only targets ask_user_question and does not affect the execution path of other tools.

Installation and Enablement

Official installation command:

dsh plugin --profile web add dsh-ask-guard

After installation, restart dsh web. The plugin is a dsh.bundle package; patch lines are automatically added to the profile composition during reconcile, so you don’t need to manually modify the composition file.

If you want to install from a git checkout:

dsh plugin --profile web add github:Q1hangL/dsh-ask-guard

Regarding dependencies, the plugin’s peerDependencies are @deepseek-ai/dsh-timeout ^0.1.0-rc.6, @deepseek-ai/dsh-tools ^0.1.0-rc.6, and @deepseek-ai/schemastery ^3.18.1. You can confirm the environment versions before installation.

Configuring Timeout

There is only one configuration item, timeoutMs, which is the deadline for a single ask_user_question call, with a default of 300000 ms (5 minutes).

To adjust it, add a section in the profile’s cordis.patch.yml, for example changing it to 10 minutes:

- id: ask-guard
  config:
    timeoutMs: 600000

The value should be determined by your actual interaction rhythm: questions are usually answered within a few minutes, so the default is sufficient; if you often leave and return after asking, you can increase it appropriately.

Regarding Recovery

First, let’s clarify the current situation: a page refresh (F5) can already resync pending unanswered questions—the host replays unanswered questions to the reconnected client. After installing this plugin, even without refreshing the page, the turn will not hang forever and will end with ASK_TIMEOUT after a timeout. Both solve different aspects of the same problem: refresh restores the “question is waiting for an answer” scenario, while the plugin handles the “question was never delivered” scenario.

Running Tests

If you want to modify the source code or verify behavior, the repository comes with tests; the standard two steps are:

npm install
node --test

Use Cases and Notes

Who is it for: Developers using DSH in the web interface and relying on ask_user_question for human-computer interaction. As long as you have experienced an agent getting stuck waiting for an answer and only being able to press Stop, this plugin is the right solution.

Two reminders:

  1. The plugin runs with the permissions of the current dsh process; it is recommended to review the source code and license before installation. The code is fully reviewable on GitHub, and the license is MIT.

  2. Do not set the timeout value too short, otherwise questions that haven’t had time to be answered under normal rhythm may be misjudged as timed out.

Conclusion

dsh-ask-guard solves a very specific problem: ensuring that lost or unanswered questions end with a structured ASK_TIMEOUT, allowing the agent to gracefully end the turn instead of waiting indefinitely. The implementation is not complex, and the mechanism is restrained—it only wraps ask_user_question and does not touch other tools. If you have similar hanging issues in your workflow, it is worth a try.