
DSH Sentinel
☆ 2Condition-driven wakeup for DeepSeek Harness: durable file/command/http/process/webhook watches that wake the agent, with dock, sidebar branch, and a global dashboard.
Get this plugin
Review the source, then continue to the publisher.
dsh plugin add @dsh-external/dsh-sentinel@latest
About this plugin
Source snapshot 8/13/2026dsh-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.

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 branch | Global dashboard |
|---|---|
![]() | ![]() |
Sensors
| Kind | Engine | Fires on |
|---|---|---|
file | path snapshot + inotify push | snapshot change (sub-second); accelerated by fs events |
command | read-only shell line, probed on an interval | output/exit-code change |
http | URL probed on an interval | status/body change |
process | pgrep -f pattern, probed on an interval | match-set change |
webhook | pure push | any 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, optionalpattern,interval(1–3600s, default 30),note(delivered verbatim with every wakeup),maxFires(default 1: one-shot),cooldown(default 60s), optionalttl.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 (omitsessionIdfor every session).GET /plugins/dsh-sentinel/dashboard— the server-global watch table.POST /plugins/dsh-sentinel/hook?id=watch-N— webhook entry; put acurlinto 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.

