Preface¶
When running long tasks with DSH, there are three common pain points: confusion in the session list about which session is which; when a process stops waiting for a decision, the indicator “dot” is scattered in the logs or output; and when you are away, routine issues either pile up or get auto-passed. The dsh-watchkeeper introduced below is a plugin designed to address these three issues. It consolidates session numbering, human waiting for answers, and pluggable reviews into a single watch loop, maintained by Maravetech and categorized under the “Workflow” section in the DSH community plugin directory.
What is it¶
One-sentence summary: dsh-watchkeeper is a DSH plugin that guards unattended sessions for you—assigns a stable ID to each session, collects decision cards requiring human approval into a pending view, and allows for pluggable judges to answer routine questions on behalf of the session.
Open source, pure JavaScript, no build steps. The repo is marked Alpha, version 0.2.0, and the plugin API and configuration may change before version 1.0. License MIT.
First, do the following: confirm your Node version is greater than or equal to 22.5, and confirm that your DSH version falls within the range declared in engines.dsh; if not, dsh plugin add will refuse to install.
Three Capabilities¶
You can understand what it does by looking at three things.
Session Numbering¶
Each session receives a stable ID, displayed at the beginning of the row in the session list. The ID increments from 1, and sessions retain their original ID after restarts, as long as the ledger under WATCHKEEPER_HOME is not lost.
Pending Desk¶
Cards requiring human decision are collected in one location. When you answer once, the response is sent back to the session that initiated that card. The plugin’s cards are JSON wrapped in [DECISION_POINT] and [/DECISION_POINT], and must be placed outside code fences.
Watch Loop and Pluggable Judge¶
The judge configured in the watch loop can answer routine questions that arise during the session. Low confidence, corrupted output, and red-line topics always route to a human.
The plugin explicitly distinguishes default behavior: no configured judge means no automatic decision card answers; configuring fallbackEnabled only handles a small class of uncertain results after the judge actually runs; red-line cases and when the judge is unavailable still route to a human. The reply provider is also disabled by default.
Installation¶
The plugin consists of two parts: the plugin (host behavior) and the skill (agent instructions). The skill just explains the plugin; without installing the plugin, it won’t do anything.
Plugin¶
DSH’s own plugin command forwards to pnpm in the profile directory and registers the plugin as a bundle for that profile, so you don’t need to manually edit cordis.patch.yml. The line below installs from GitHub and pins to the tag:
dsh plugin --profile <your-profile> add github:Maravetech/dsh-watchkeeper#v0.2.0
To clone locally for development, clone it and add by directory:
git clone https://github.com/Maravetech/dsh-watchkeeper.git
dsh plugin --profile <your-profile> add "file:$PWD/dsh-watchkeeper"
Or download the tarball from releases and install via file::
dsh plugin --profile <your-profile> add ./dsh-watchkeeper-0.2.0.tgz
To pin to a specific commit, use github:Maravetech/dsh-watchkeeper#<commit-sha> instead of the tag. The plugin does not depend on build artifacts, so no extra allowBuilds item is needed.
Uninstall:
dsh plugin --profile <your-profile> remove dsh-watchkeeper
Restart DSH completely after installation.
Skill¶
The plugin comes with a skill; create a symbolic link to DSH’s skills directory:
ln -s "$DSH_PROFILE_DIR/node_modules/dsh-watchkeeper/skills/dsh-watchkeeper" "$DSH_HOME/skills/dsh-watchkeeper"
To install the skill only for other agents like Claude Code, Codex, use the CLI of skills.sh (Node 22.20+):
npx skills add Maravetech/dsh-watchkeeper --skill dsh-watchkeeper -g
Verification¶
First check the health endpoint to confirm the plugin loaded correctly:
curl -s "$DSH_WEB_URL/watchkeeper/health" | python3 -m json.tool
If DSH_WEB_URL is not set, use the port in the application URL, e.g., http://127.0.0.1:62630/watchkeeper/health.
Configuration¶
Configuration uses environment variables. The three most important ones:
WATCHKEEPER_HOME # Default ~/.dsh-watchkeeper, stores state, numbering ledger, and SQLite database
WATCHKEEPER_JUDGE_CMD # Judge command; if not set, all questions route to a human
WATCHKEEPER_CONTROL_TOKEN # Independent bearer token for watch control POST routes; if not set, these routes are unavailable
There are also binaries for the reply providers: WATCHKEEPER_CODEX_BIN, WATCHKEEPER_AGY_BIN, WATCHKEEPER_CUSTOM_CMD. For the full variable list, judge contract, and provider commands, see the repo’s docs/configuration.md.
When using the HTTP control interface, writing routes requires WATCHKEEPER_CONTROL_TOKEN, otherwise a 403 is returned; the web host must use a literal loopback address.
Typical Usage: A Decision Card¶
The minimal form of a card is as follows. The key point is to place the JSON between [DECISION_POINT] and [/DECISION_POINT], and outside code fences:
{
"audience": "human",
"requires_user_response": true,
"human_review": [
{
"question_id": "q-1",
"question": "Ship with the current cover image or wait for the new one?",
"type": "stakeholder_point",
"reason": "The new image is not approved yet.",
"manual_reason": "necessary information",
"blocking_scope": "Publishing is blocked until this is answered.",
"attempts": ["Checked the asset folder; the new image is still missing."],
"background": "The current image is usable but older.",
"recommendation": "Ship with the current image.",
"options": ["Ship with the current image", "Wait for the new image"],
"state": "needs_user",
"audience": "human",
"requires_user_response": true,
"agent_can_resolve": false,
"already_resolved": false
}
]
}
For field rules, option formatting, and common errors, see docs/decision-cards.md.
Where to Check When Errors Occur¶
First look at the health endpoint, then the persisted state, and finally read the plugin logs:
curl -s "$DSH_WEB_URL/watchkeeper/health" | python3 -m json.tool
ls "$WATCHKEEPER_HOME"
ls "$WATCHKEEPER_HOME/state"
tail -n 50 "$WATCHKEEPER_HOME/probe.log"
If a card does not appear, it is usually because the card was placed inside code fences, missing [DECISION_POINT] wrapping, or missing required fields.
Use Cases and Notes¶
Suitable for people who run long tasks or sessions requiring on-duty humans with DSH, want sessions to have traceable IDs, decision points concentrated in one place, and want to delegate routine questions to a replaceable judge while keeping critical questions for themselves.
Two points need to be clarified first. First, the plugin runs as a bundle within the DSH process for that profile, executing with the current DSH process’s permissions; it is recommended to read the source code before installing (the repo is pure JS, no build steps, so you can directly read index.js, lib/, and cordis.patch.yml), and confirm the license. Second, it targets unattended scenarios, with automation level determined by judge and provider configuration; the default is “no human means no automatic decision,” so start with default values and gradually loosen them.
Summary¶
dsh-watchkeeper solves three small things during unattended operation: sessions can be numbered, decision points have a place to go, routine questions can be answered on behalf of the user, and critical questions must stop for a human. Source code and documentation are on GitHub: https://github.com/Maravetech/dsh-watchkeeper, community directory page: https://www.skillhub.cn/plugins/Maravetech/dsh-watchkeeper.