dshplugin.devDeepSeek Harness Plugins
DSH Sentinel plugin logo
DeepSeek Harness Plugin

DSH Sentinel

2
Published by fuhefei

Condition-driven wakeup for DeepSeek Harness: durable file/command/http/process/webhook watches that wake the agent, with dock, sidebar branch, and a global dashboard.

Automationdshdsh-plugin

Get this plugin

Review the source, then continue to the publisher.

dsh plugin add @dsh-external/dsh-sentinel@latest
Get this plugin
Share on X ↗
DSH Sentinel interface preview

About this plugin

Source snapshot 8/13/2026

dsh-sentinel

Condition-driven wakeup for DeepSeek Harness: the agent registers a watch, goes to sleep — even closes the session — and the sentinel wakes it when the condition happens. Every subscription and every fire is a user-visible session event, and the browser dock shows what is on duty.

Sentinel dock panel, expanded

How it works

The node half owns one server-lifetime runtime that folds a plugin-owned sidecar log ($DSH_HOME/sentinel.jsonl) into live subscriptions, probes every sensor on a shared 5s heartbeat, and delivers wakeups through the official followup channel — resuming a dormant session's agent first when needed. Subscriptions therefore survive process restarts, and conditions that become true while the server is down late-fire on the next probe.

The browser half is a dock card above the composer (the conversation.input.dock family) listing the session's active watches — sensor, target, live probe state, fire budget, next-probe countdown — plus recent fire history when expanded. It polls the read-only state route and renders nothing when the session has no watches.

Two surfaces make the server-global watch set visible. A sidebar branch grows under every session row that has active watches (sidebar.workspaces.sessionRow.branch, one shared poller for all rows) — collapsed it is a 👁 count, expanded it lists the session's watches and links to the dashboard. The dashboard is a standalone table of every watch across every session: session (active/dormant), sensor, target, pattern, fire budget, last probe state, next probe.

Sidebar branchGlobal dashboard
Sidebar branchDashboard

Sensors

KindEngineFires on
filepath snapshot + inotify pushsnapshot change (sub-second); accelerated by fs events
commandread-only shell line, probed on an intervaloutput/exit-code change
httpURL probed on an intervalstatus/body change
processpgrep -f pattern, probed on an intervalmatch-set change
webhookpure pushany POST to the returned hook URL

With pattern, probe kinds fire on the no-match→match edge of that regex and webhooks accept only matching payloads; without it, probe kinds fire on any change after the baseline.

Tools

  • sentinel_watch — register a watch: kind, target, optional pattern, interval (1–3600s, default 30), note (delivered verbatim with every wakeup), maxFires (default 1: one-shot), cooldown (default 60s), optional ttl.
  • sentinel_list — active watches with live probe state.
  • sentinel_cancel — cancel one watch by id.

Routes

  • GET /plugins/dsh-sentinel/state?sessionId=… — read-only state for the dock and the sidebar branch (omit sessionId for every session).
  • GET /plugins/dsh-sentinel/dashboard — the server-global watch table.
  • POST /plugins/dsh-sentinel/hook?id=watch-N — webhook entry; put a curl into a CI job, git hook, or another machine's script to wake the agent.

Install

One line through the official bundle channel (build artifacts are committed, so the git-source install runs no build):

dsh plugin --profile web add "github:fuhefei/dsh-sentinel#main"

Alternatively, add the node half manually through a patch-list configuration over the shipped base:

# cordis.patch.yml
- insert:
    - id: dsh-sentinel
      name: '@dsh-external/dsh-sentinel'

The browser half ships in the same package (./client) and is injected by the Web UI's plugin loader.

Sidebar branch prerequisite

The dock and the dashboard work on a stock host. The sidebar branch needs the session-row extension holes, which the official tree does not declare yet; apply the bundled patch to your DSH source checkout and rebuild ui-workspace:

git apply /path/to/dsh-sentinel/patches/session-row-holes.patch

The patch declares sidebar.workspaces.sessionRow and sidebar.workspaces.sessionRow.branch as list holes (every registrant renders, in order) at root scope (sidebar rows render outside any session binding; the row passes its sessionId through owner props). dsh-subagent-tree ships a patch for the same hole names with different semantics (keyed/session); apply one or the other, not both.

Develop

npm install
npm run build     # tsc -b + tsdown (lib/index.js, lib/client.js)
npm test          # vitest: domain fold/normalize, sensors, dashboard escaping, e2e wakeup flow

License

BSD 3-Clause. See LICENSE.