Skip to content
ActiveOct 29, 2026

DSH Runtime Tuning

A long-running experiment on the agent runtime: find where time goes in real DeepSeek Harness sessions and what is wasted, then fix it with plugins, each change verified with data.

DeepSeek HarnessNode.jsJavaScript
Status
Active
Published
Oct 29, 2026
Updated
Nov 6, 2026
Tech
DeepSeek Harness · Node.js · JavaScript

A long-running experiment on the agent runtime: find where time goes in real DeepSeek Harness (DSH) session logs and what is wasted, then fix as much of it as possible with plugins, each change verified with data.

It is both a set of installable DSH plugins and a record of that process, and more plugins will be added as the analysis continues. The full analysis and experiments are in the article “从一份 9.7 小时的真实会话开始” (in Chinese).

Starting point

One everyday DSH session: 119 turns, 2347 tool calls, spanning 9.7 hours. Counted event by event, a large share of the time besides model generation went to blocking waits on background jobs, waiting for answers to questions, waiting for approvals and foreground sleep; the same GitHub token also appeared in dozens of commands and model replies.

Plugins so far

Plugin What it does
wait-guard Blocks long foreground sleeps and long blocking waits; wakes the model as a backstop when a background job never reports back; an optional task_status tool
ask-nonblocking Makes ask_user_question stop blocking once the question is asked
secret-guard Keeps tokens out of the model’s context
chat-answer-fix Replaces the DSH chat UI check so answers are not folded away when a turn ends with a tool call
secret-files Hides credential files at the sandbox mount layer so file tools and child processes see a notice instead of the real contents
fs-view Lets edit replace exact text even when the file was not read first; read_image detects image format from the file header
session-tmp Makes file tools and bash share the same /tmp (only when bwrap /tmp has been remapped)
stream-stall Once an answer has started streaming and only heartbeats arrive, cuts the request after stallSeconds (default 180) with no new content, and DSH retries it as a transport failure; thinking time and output length are not limited, and nothing is done before the first content arrives
todo-reminder When the todo list still has unfinished items and has not been updated for 15 steps, appends a reminder and the current list after a tool result so the model can check progress; no reminder if no list was written or everything is done; frequency is configurable

Takeaways

  • The gain is “not holding the conversation”, not “faster”: once a long wait is split into “run in the background → end the turn → the completion event wakes a new turn”, the user can step in at any time while a build runs
  • Don’t rely on the model to report its own state: in long sessions the model almost never calls task_status, so the runtime now judges state from facts and task_status is optional
  • Not every problem is worth fixing: a plugin can only deny a call, not change its arguments, so for cases that trigger often but are rarely a real problem, denying costs more than it saves, and those are left alone

Current status

  • Version: 0.2.8, open source (MIT license)
  • Environment: verified only on the web profile of DSH 0.2.1-rc.5, on Windows 11 + WSL2; headless, sdk and other profiles are not supported yet
  • Tuned preset: an optional “tuned” mode that drops tools unused for everyday coding and adds guidance for using the remaining ones well; the standard mode is unchanged; since v0.2.7 it is off by default, and if you have used the tuned preset, add - id: runtime-tuning-preset-tuned and disabled: false to your own cordis.patch.yml, otherwise conversations started with that preset show “session unavailable” after the upgrade
  • Known limits: ask-nonblocking relies on DSH internals and may stop working after a DSH upgrade, in which case questions simply fall back to blocking; the hard backstop’s “wake when the timer fires” path is covered only by unit tests so far
  • Tests: 109 tests in total; they use only Node.js’s built-in node:test with no dependencies to install, and run on Node.js 22 / 24 through GitHub Actions
  • Completion notices and reminders come from the companion plugin DSH Sentinel (successor to dsh-task-notify), which also handles the tray, crash protection and plugin health checks
  • DSH version requirement: from v0.2.6 it targets DSH 0.2.2-rc.1, and the required DSH version is now >=0.2.2-rc.1; if you are still on 0.2.1, keep using 0.2.5
  • v0.2.8 adds: todo-reminder. Official todo_write only records and never reminds, so in long tasks the list is often written once and then stuck on the first item; now, when unfinished items remain and the list has not been updated for 15 steps, a reminder and the current list are appended after a tool result, asking the model to update in the same step as the next tool call rather than spending a step on its own. No reminder if no list was written or everything is done; frequency is configurable
  • v0.2.7 changes: the tuned preset is now off by default. A conversation started with a preset stays bound to it, and if the preset is gone or fails to load those conversations cannot be opened; and there is still no same-task timing data showing that it saves time, so it should not be on by default. New test: it compares the tuned preset item by item against the local DSH’s standard preset and reports any difference other than the deliberate removals and additions, so after a DSH upgrade you run the tests first and then update the preset by hand
  • v0.2.6 fixes: adapts to DSH 0.2.2-rc.1. 0.2.2 removed the dsh-tool-subagent-control/list-agents sub-path (merged into dsh-tool-subagent-control), and the old tuned preset referenced it, so sessions using the tuned preset could not be opened (showing “session unavailable”) and new ones could not be created; the preset is updated to match 0.2.2’s standard. chat-answer-fix: the display-settings fields are read according to the current official Config (0.2.2 added foldedReplies); right after DSH starts, the loader is still inside a reload transaction and moving the settings failed, so it now retries after a short wait; the new loader hands back iterators, and the old code searched the same iterator twice and so rewrote the settings on every start, so it now converts to an array first
  • v0.2.5 adds: stream-stall. DSH’s own stream idle timeout is reset by heartbeats, so when the server degrades the UI can sit on “thinking” forever; now, once an answer has started streaming, the request is cut after 180 seconds (the default, adjustable through stallSeconds) with no new content, and DSH retries it automatically. The docs also now name dsh-sentinel instead of dsh-task-notify
  • v0.2.4 fixes: display settings such as how tool steps are shown no longer reset to “detailed” on restart; the official chat UI is found even when DSH was not installed with pnpm