Repository navigation
Releases: fuhefei/dsh-sentinel
Release list
v0.13.2 — DSH 0.2.1 alignment, plugin-manager metadata
DSH 0.2.1 alignment
The deployment moved from dsh 0.2.0-rc.2 to dsh 0.2.1-rc.2 and this release is the plugin's alignment pass. No plugin source change was needed: against the real 0.2.1-rc.2 packages tsc -b --noEmit passes, all 76 tests pass, and a rebuild produces byte-identical lib/ bundles.
- devDependencies moved to
0.2.1-rc.2(all twelve@deepseek-ai/*pins). engines.dshdeclared as>=0.2.0-0 <0.3.0. Since0.2.1-alpha.4the host's install gate decides onengines.dshalone whenever a manifest declares it (checked in the host'sevaluatePluginCompatibilitysource), so this field now carries the 0.2-line admission: both the0.2.0rollback line and0.2.1+pass, and older lines are refused.
Plugin Manager display metadata
0.2-generation managers read display metadata without activating the plugin, and this release ships it: locale/en.json + locale/zh.json meta titles/descriptions plus an exported icon. The display-metadata check in dsh-plugin-guide now passes.
Fixes
- The git-install example pinned a stale tag (#14).
github:fuhefei/dsh-sentinel#v0.11.0made copy-paste installs land on a version predating the 0.1.7/0.2 compatibility fixes — one reporter's watch fired and delivered, but the v4 session format silently rejected the wakeup. The example now pinsv0.13.2and notes that the tag tracks the latest release. - The README compatibility paragraph was corrected: the three harness packages are
peerDependencies(one host instance; a second copy owns a differentTOOL_RUNTIME_SCHEDULERsymbol and breaks real tool calls) since v0.13.0, not carried dependencies.
Verification
Upgraded deployment: clean profile reinstall on dsh 0.2.1-rc.2; the plugin activates, holds the duty lease, its client bundle carries sidebar.panellist, the state route answers, 71 boot rows, zero skipped bundles. Rehearsed first against a copy of the live profile; the launcher + profile backup is retained for rollback.
v0.13.1 — client poller fix, wider 0.2 support
Fixes
-
The client state pollers no longer overlap or leak in-flight requests (#13). Both the per-session hook and the page-wide shared poller armed a
setIntervaland fired a request on every tick without looking at the previous one, so a slow server accumulated outstanding requests — the reporter's isolation produced six from five ticks — and unloading only cleared the timer, leaving work in flight to resolve into a dead component.fetchcarried noAbortSignalat all, and the last global subscriber leaving did not cancel the shared request.Both now run on a self-scheduling poller: the next tick is armed only after the previous request settles, so at most one request is ever in flight, and every request carries a signal that aborts on teardown or on a per-request timeout. The timeout is load-bearing rather than defensive — because the loop re-arms after settlement, one request that never settled would otherwise stall polling permanently.
Improvements
-
The harness peer range now covers the 0.2 line instead of a single release. The three peers were pinned to exactly
0.2.0-rc.2, which means the plugin installs on precisely one host version and becomes uninstallable on the next — that is how the 0.1.7 → 0.2.0 step broke, and it would have repeated on every 0.2.x. They are now>=0.2.0-rc.2 <0.3.0, which still refuses anything older.Checked against the host's own compatibility gate rather than by inspection:
0.2.0-rc.2and0.2.1-alpha.1are admitted;0.2.0-rc.1and0.1.7-rc.2are refused. Verified on a rehearsal built from a copy of the live profile running0.2.1-alpha.1— the plugin activates, holds the duty lease, serves its client half and answers the state route (68 boot rows).
Notes
- Eight new unit tests cover the poller contract: non-accumulation under a hanging server, sequential re-arming, abort-on-stop, timeout recovery, abort-vs-error reporting, idempotent start and restart. They have teeth — against the previous
setIntervaldesign the non-accumulation assertion fails with 11 outstanding requests over ten intervals. Full suite: 76/76. dsh-web-shellremains pinned to<0.2.0in its own manifest, which0.2.0-rc.2happens to satisfy (a prerelease sorts below its release) but0.2.1-alpha.1does not. If you run the alpha, the terminal bundle is skipped until that is fixed upstream.
Full Changelog: v0.13.0...v0.13.1
v0.13.0 — DSH 0.2 alignment, by @WindLX
Breaking changes
- This release targets the DSH 0.2 line (
0.2.0-rc.2). The three harness value imports (@deepseek-ai/dsh-tools,@deepseek-ai/dsh-llm,@deepseek-ai/dsh-scope) moved fromdependenciestopeerDependencies, pinned to0.2.0-rc.2. A profile therefore no longer installs its own copy of the host's core modules. That duplication was not merely wasteful: two copies meant two distinctTOOL_RUNTIME_SCHEDULERsymbols, and real tool calls failed withCannot read properties of undefined (reading 'prepare'). If you are on DSH 0.1.x, use 0.12.1.
Fixes
- Reasoning effort was silently dropped when resuming a dormant session.
currentSelection()was destructured to just{ provider, model }, so the wakeup resumed with the default effort instead of the configured one. The fullModelSelectionis now passed through. - Historic agent presets were never restored. The resume path called
sessionPersistence.inspect(), which is not part of the host interface, so the optional call skipped silently and every resumed session fell back to the current default preset. It now reads the restored session'sagentPresetprojection and mounts that. - The duty lease could be released in the wrong directory. The lease path was recomputed on each use, so a
DSH_HOMEchange between claim and teardown pointed renewal and cleanup at a different home. It is now resolved once at construction. - Overlapping probe rounds. Heartbeat ticks could start a second round while the first was still running, duplicating lease renewals and probes. Rounds now coalesce, and
dispose()is async: it stops timers and sensors, waits for the in-flight round, then releases the lease — so teardown no longer races startup.
Internal
- The loose structural shims around
Context,ResumeAgentOptions,ModelSelectionand the preset registry are replaced with the host's real types, sotscnow checks those contracts instead of accepting anything. - Five new end-to-end tests cover the two resume-preservation paths, preset-free composition, the lease-home fix, and in-flight teardown. Suite: 68/68 passing.
Thanks
PR #11 by @WindLX — the DSH 0.2 alignment and all four fixes above, with the reproduction and regression evidence.
Full Changelog: v0.12.1...v0.13.0
v0.12.1 — DSH 0.1.7 alignment
Breaking changes
- This release targets the DSH 0.1.7 line. Under strict semver the previous dependency range (
>=0.1.5-rc.2 <0.2.0) does not admit0.1.7-rc.2— the prerelease rule only lets a prerelease match a comparator carrying the samemajor.minor.patch. Left alone, a 0.1.7 host would have resolved this plugin's harness imports to 0.1.5 copies while the host ran 0.1.7, which is the same silent drift the 0.1.5 alignment removed. The ranges are now pinned to the 0.1.7 line, so 0.12.1 will not install against DSH 0.1.5 — if you are staying on 0.1.5, use 0.12.0.
Fixes
- DSH 0.1.7 removed the shared catch-all
pluginmessage-source kind; every producer now declares its own. Watch wakeups carry{ kind: 'sentinel' }, declared by merging intoMessageSourceMap— the same idiom upstream's owngoalkind uses, and one that compiles against both the 0.1.5 and 0.1.7 type surfaces. - The end-to-end harness now asserts the wakeup's message source. It captured only message text before, so nothing caught a wrong kind.
Notes
- No user-visible change beyond one label: a wakeup now reads "Sentinel" in the trajectory inspector where it previously read "Plugin · dsh-sentinel" (0.1.7 dropped its
pluginbranch, so the old kind would have degraded there anyway). - Transcript placement is unchanged: both kinds land in
context, notsteering, so wakeups still render exactly where they did.
Full Changelog: v0.12.0...v0.12.1
v0.12.0 — DSH 0.1.5 alignment
Breaking changes
- The global watch view moved seats. DSH 0.1.5 removed the session-row extension holes the old surface grew under, so it disappeared silently after upgrading. The watch table now lives on the official seats: a
sidebar.panellistentry (idsentinel, showing a state dot while watches are active) that selects the layout's keyedmainpanel. The conversation dock card and the optional better-sidebar tab are unchanged. This release requires DSH 0.1.5 or later.
Fixes
- Declare the harness value imports (
@deepseek-ai/dsh-tools,@deepseek-ai/dsh-llm,@deepseek-ai/dsh-scope) as dependencies pinned to the verified line. A profile's hoisted store could previously resolve them to0.1.0-rc.8copies while the host ran 0.1.5-rc.2, and the loose shims hid the mismatch fromtsc. - Delete both loose shim files and compile the client half against the real 0.1.5 types, so
pnpm typechecknow actually checks the host contracts instead of papering over them. - Point the client build's external table at the 0.1.5 platform seed and drop the removed
dsh-client-runtime/web-react/schema-formnames.
Internal
- Drop seven unused devDependencies.
- Retire the session-row branch path; its bundled patch is kept for pre-0.1.5 trees only.
Full Changelog: v0.11.1...v0.12.0
v0.11.1
v0.11.0
Published to npm under the bare name dsh-sentinel; bilingual README and a security policy.
Distribution
- Now on npm:
dsh plugin --profile web add dsh-sentinel. The package dropped the@dsh-externalscope so it can live on the public registry without scope ownership, and the cordis plugin name changed with it (@dsh-external/dsh-sentinel→dsh-sentinel). Existing installs should update their profile to depend ondsh-sentineland drop the old scoped row; watches themselves survive the switch — they live in the sidecar log, not the install. - The git-source install still works and now pins
#v0.11.0. - The npm tarball ships
lib/, the mount manifest, both READMEs, SECURITY.md,patches/, and the preview screenshots. A straylib/client.js.bakbuild residue is excluded and now gitignored.
Docs
- Chinese README (
README.zh.md) with a language switcher on both sides. - SECURITY.md: the trust surfaces in scope (route fence, webhook URLs as secrets, the
commandsensor's trust domain) and private reporting through GitHub vulnerability reporting.
v0.10.0
Security hardening plus the missing observability surfaces.
Security
- All routes now sit behind a browser-trust fence. The host fences its own
/apichannels against cross-site POSTs and DNS rebinding, but plugin routes under/plugins/*were outside it — a malicious webpage could form-POST to your localhost and fire the webhook route (waking a full-access agent) or cancel watches; a DNS-rebound page could read state. Browser-marked cross-site requests and Host/Origin authorities that are DNS names now get 403. Headerless clients (curl, CI jobs) are completely unaffected.
Added
- Recent-fires table on the dashboard — time, watch, session, summary; answers "what fired while I was away".
- Duty heartbeat in the dashboard meta line — shows the lease heartbeat age and flags a stale resident process, so a silently-dead watcher is visible.
- Dropped-wakeup badges — wakeups dropped by the
maxPendingWakeupscap (dormant session, queue full) are now counted per session, exposed in the state route and shown as a red badge instead of vanishing into the server log. - Wakeup stop-guardrail — the wakeup message now ends with an explicit instruction to handle this trigger and stop, so a woken agent does not wander off into open-ended investigation.
Documented
- First-probe semantics: pattern-less watches absorb the first observation as baseline; a pattern watch whose target already matches fires on the first probe.
v0.9.0
Watches now have a manual kill switch, and queued wakeups are visible.
Added
- Manual cancel everywhere.
POST /plugins/dsh-sentinel/cancel?sessionId=…&id=watch-Ncancels a watch through the same durable sidecar path as every other mutation (logged with reasonuser). The dashboard table and every dock / sidebar-branch / tab row carry a ✕ that calls it — no more resuming a dormant session (and spending a model call) just to stop a watch. - Pending-wakeup visibility. Watch rows now expose
pendingWakeups— fires logged but not yet delivered to the session show as a badge (e.g.0/3 待投递 1) in the dashboard and UI rows instead of sitting silently in the queue. docs/invariants.mdrule 7: watches outlive their session's agent; the host has no session-deleted event, so orphaned watches of deleted sessions keep probing until cancelled by hand — the ✕ is the kill switch of last resort.
This closes a real semantic gap found while testing: an orphaned watch (its session deleted) could never be cancelled at all — the only entry point was its session's agent, which no longer existed.
Fixed
- Install line regression: the README had briefly regressed to
#v0.8.2after a stale-copy edit; it now pins#v0.9.0.
v0.8.3
Two correctness fixes, found by running a real agent session against a live install.
Fixed
- Pattern watches no longer false-fire on placeholder snapshots. A watch on a missing file used to expose the placeholder
<absent>to the pattern, so any-content regexes like[\s\S]+(what an agent naturally writes for "fire when the file has content") matched on the very first probe and burned the whole budget on a file that did not exist. Placeholders (<absent>,<unreachable>,<none>,<push-only>) are never pattern-matchable now; the edge a pattern waits for is placeholder → matching content. Pattern-less watches keep firing on any snapshot change. Thesentinel_watchpattern doc now names the JavaScript RegExp syntax and the placeholder rule. - Webhook hook URLs are scoped per session. Watch ids are allocated per session, so two sessions could hold the same
watch-1— and receive the identical hook URL?id=watch-1; an external POST then woke whichever session loaded first. Hook URLs now carry&s=<sessionId>and the route resolves the qualifier first. URLs withouts(baked into existing CI jobs) still work and deterministically resolve to the first matching webhook watch; a stray qualifier returns 404 instead of leaking across sessions.
Added
docs/invariants.md: the six load-bearing rules (id scope, snapshot domains, edge semantics, at-least-once delivery, duty lease, sidecar-as-truth).- Exhaustive property tests for the fire decision (no consecutive fires without an intervening non-match; placeholders are a non-match under every pattern) and a two-session same-id e2e regression.
Upgrade note: if a pattern of yours deliberately matched the literal placeholder text (e.g. absent), it will no longer fire; use a pattern-less watch to react to the target appearing or disappearing.