DeepSeek Harness plugin

blocker-notify

Real-time attention alerts for DeepSeek Harness: a global banner plus flashing workspace entries when the agent is blocked (approval request / sandbox denial), with an audible chime and OS-level

Jump to install

Source facts

Repository
Frost-Reed/blocker-notify
Latest update
Aug 14, 2026
Category
Notifications & Integrations
GitHub stars
2
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/Frost-Reed/blocker-notify
Plugin: blocker-notify
Author: Frost-Reed

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.en.mdSource · read only
README language

dsh-blocker-notify

English | 简体中文

Real-time attention alerts for DeepSeek Harness (DSH): when an agent gets stuck and needs you to step in, the web UI notifies you in real time — no need to watch the screen waiting for an action.

![示例](./assets/show_fullscreen.png)

The three kinds of "blocked" signals that trigger a notification:

SignalDescription
Approval / privilege escalation (approval/request)The agent requests wider sandbox permissions or an approval (e.g. a write escalation under read-only mode) → alerted
Sandbox denial (FS_SANDBOX_DENIED)write/edit refused under read-only / workspace-write mode → recorded in the host log only (diagnostics), no user-facing alert
Waiting for user interaction (question / plan-review)The agent is waiting for you to answer a question or review a plan → alerted

> Only events that need your action are alerted (approval / question / plan review). A sandbox denial is a completed event with nothing to act on, so it stays silent — even when the approval policy is never.

The four notification mechanisms

MechanismEffect
Global message (banner)An amber banner in the top-right corner lists every blocked session; each row shows only the session title + state (no tool name / reason); clicking a row jumps to the session and removes that row immediately (independent of the approval outcome); ✕ closes the whole banner; rows reappear when new blockers arrive
Workspace entry turns yellow and flashesThe yellow status dot on the affected session row in the left workspace flashes every 1.1s — the yellow mark itself is DSH's own "waiting for user" indicator; this plugin adds the flash animation; it stops automatically once the blocker is resolved
Sound (audible chime)When a new blocker appears, the page plays a short two-tone chime (Web Audio, no audio files); it only rings on blocker-set transitions, never on page load with pre-existing blockers; degrades silently when autoplay is locked or the environment lacks Web Audio
OS-level notificationWhen a new blocker appears, the browser raises an OS notification (Windows Action Center / macOS Notification Center) via the Notification API; clicking the notification focuses DSH (and opens the session directly for a single blocker); one blocker → body session title — kind (tool name — reason), several blockers → one merged notification; when permission is granted the banner header shows a small 🔔 (click for a self-test notification), while default/denied states show an action row in the banner (one-click enable / fix in browser site settings)

How to use

Option 1: Dynamic plugin (quick enable, ~5 minutes, recommended to try first)

Nothing to install, no DSH restart needed. Downside: process-memory only — re-enable after a DSH restart.

1. Get the source: git clone https://github.com/Frost-Reed/blocker-notify (or download from the GitHub page). 2. Open any session (preferably cordis / creator mode) and send this instruction to the agent (replace <repo-path> with your local clone path):

``text Please enable dsh-blocker-notify as a dynamic plugin: 1. Use the read tool to read <repo-path>/dynamic/host.js and <repo-path>/dynamic/client.js (these are the code.host and code.client function bodies — use the file contents directly). 2. Call cordis_define: plugin: { kind: "new", idPrefix: "ntfy" }; name: "blocker-notify"; purpose: one sentence; code.host = host.js content; code.client = client.js content. 3. Call cordis_run (mode: "run") with the pluginId/packageId returned by cordis_define. 4. The run may require the user to approve the Client half in the web UI; wait for the approval result, do not re-request. 5. When done, use cordis_inspect_self(pluginId, packageId) to confirm host/client are both running. ``

3. Approve the Client half's run request in the web UI (once). 4. Verify immediately (see "Verification" below).

Option 2: Formal bundle install (persistent, v0.2.0)

Packaged per DSH's official plugin spec (dsh.bundle + dsh.client); survives restarts once installed.

> If dsh is not on your PATH (e.g. running from a source checkout or not installed globally), replace dsh below with pnpm dsh (in the source repo root) or npx @deepseek-ai/dsh.

Install from GitHub (published, recommended):

dsh plugin --profile web add github:Frost-Reed/blocker-notify

Install from a local checkout (development):

dsh plugin --profile web add file:<local-clone-path>

After installing:

# Confirm it joined the layer stack
Get-Content $env:USERPROFILE\.dsh\profiles\web\package.json   # dsh-blocker-notify should appear in dsh.profile.bundles
# Confirm the row is in the config tree
dsh --dump-config --profile web | Select-String "dsh-blocker-notify"
# New dependencies/layers require a process restart
dsh web

> lib/ is committed build output (generated from src/ by tsdown). Git installs run the prepare build script, so pnpm needs a one-time allowBuilds authorization; alternatively install the npm package or a pnpm pack tarball (prebuilt, no authorization needed).

Verification (common to both options)

ScenarioActionExpected
Question self-testAsk the agent to call ask_user_question onceWhile the question dialog is open: banner "等待回答" + chime + OS notification; clicking the banner row jumps and removes it
Approval / escalationIn settings set the approval policy to ask and the sandbox to read-only, then have the agent attempt a write with sandbox_permissionsWhen the approval card appears: banner "等待授权" + chime + OS notification (with tool name and reason); clicking the banner row jumps and removes it (whether or not the approval is granted)
Sandbox denialHave the agent write to a denied path without escalation argumentsSilent (no banner, no chime, no OS notification; the host log records it for diagnostics only)

How it works

Host half (in-process, untagged scope → observes every agent's approvals/tool results)
  ├─ ctx.on('approval/request', (req, next) => …)   // waterfall: observe only, must return next()
  └─ ctx.on('tools/result', …)                       // detects FS_SANDBOX_DENIED
        │  writes to the in-process alert log (deduped by key, 10-min TTL, cap 60)
        ▼
  Details channel: formal = webServer route GET /api/dsh-blocker-notify/alerts
                   dynamic = harness.handle('blocker-notify/alerts') RPC
        ▼
Client half (browser page, root scope)
  ├─ useSessions (shell.overlay standard prop) → live pendingInteraction (approval / plan-review / question)
  ├─ polls details (fetch / host.call) → merges approval details (tool name / reason)
  ├─ CSS injection → flashes [data-state="warning"] dots
  ├─ Web Audio → plays a chime when an actionable blocker appears (AudioContext primed on first interaction)
  └─ Notification API → raises an OS notification for actionable blockers (one-click enable + self-test in the banner; clicking focuses the page)
  • Host half: captures blocking signals and maintains the in-process alert log (plain JS, no UI).
  • Client half: the four notification channels. Data comes from the shell.overlay standard prop useSessions (snapshot { ids, byId, … }; byId entries carry id / title / pendingInteraction) plus the Host details poll.
  • The formal and dynamic versions share the same logic; only the "details channel" differs (webServer route vs. dynamic RPC) — see docs/ROUTE-B.md.

FAQ

SymptomFix
No notification at allConfirm a blocker actually exists (question/approval/sandbox denial); for the dynamic version confirm cordis_inspect_self shows host/client running; refresh the page and retry
Flash only, no bannerThe banner reads the { ids, byId } snapshot from useSessions — make sure you are on pkg-4 / v0.2.0 or later
Banner only, no flashThe flash is the CSS animation (span[data-state=warning]) — make sure you are on pkg-3 / v0.2.0 or later
Dynamic version gone after restartExpected — dynamic plugins are process-memory; switch to Option 2 (formal install)
Formal install not visible in the GUINew dependencies/layers require a dsh web restart; the plugin list is in Settings — reopen to refresh
First dsh plugin add github:... failsThe package has a prepare build script (tsdown), so pnpm needs authorization: copy the exact package key from the error into allowBuilds in $DSH_HOME/profiles/web/pnpm-workspace.yaml and re-run; lib/ is committed, so an npm package or pnpm pack tarball installs without authorization

Development & build

  • src/ is TypeScript source (written the official source-repo way); lib/ is the tsdown build output (committed). pnpm build (tsdown, replicating the official clientBundle preset) generates lib/index.js (ESM named-export plugin) and lib/client.js (official window.__ModuleLoader__.load({ id, factory }) web bundle).
  • Committing lib/ means git installs work; the prepare script (= tsdown) rebuilds on git install.
  • Offline smoke test: node scripts/smoke.mjs (validates both halves' exports, inject, event listeners, route JSON, CSS injection, slot registration, rendering, UTF-8 copy).
  • dynamic/ holds the dynamic-plugin function bodies (used by Option 1), logically identical to lib/ with a different channel.
  • Full Route-B research and implementation record: docs/ROUTE-B.md; fresh-deployment enablement guide: docs/ENABLE.md.

Repository layout

dsh-blocker-notify/
├── LICENSE               # MIT
├── package.json          # bundle (dsh.bundle) + client (dsh.client) manifests
├── README.md             # Chinese readme
├── cordis.patch.yml      # bundle patch: inserts the Host row
├── src/                  # TypeScript source (official source-repo method)
│   ├── index.ts          # Host half (ESM name/inject/apply + webServer route)
│   └── client/index.ts   # Client half (built into the ModuleLoader web bundle)
├── tsdown.config.ts      # build config (replicates the official clientBundle preset)
├── tsconfig.json
├── lib/                  # tsdown build output (committed)
│   ├── index.js          # Host half
│   └── client.js         # Client half (ModuleLoader format)
├── scripts/
│   └── smoke.mjs         # offline smoke test
├── dynamic/              # dynamic-plugin function bodies (Option 1)
│   ├── host.js
│   └── client.js
└── docs/
    ├── ENABLE.md         # fresh-deployment enablement guide
    └── ROUTE-B.md        # Route-B formalization record

License

[MIT](LICENSE)