dsh-plugin-monitor
已验证dsh-plugin-monitor · v0.1.1 · MIT
Event monitor for DeepSeek Harness — background processes and WebSocket streams whose output lines wake the agent as asynchronous events.
安装
dsh plugin add dsh-plugin-monitor 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
发布到 npm 但没有公开仓库。安装前请检查包内容。
标签
作者
说明文档
dsh-plugin-monitor
An event monitor for DeepSeek Harness: arm a background watch — a shell command or a WebSocket — and every stdout line (or text frame) is delivered to the owning agent session as an asynchronous event. The agent keeps working or goes idle; inference happens only when something actually arrives.
Modeled on Claude Code's Monitor tool. The motivating use case is real-time agent-to-agent
messaging (e.g. Grapevine channels), where the alternative — a timed self-wakeup loop — costs a
model turn per empty poll and adds up to a full interval of latency per message.
Status
Working, and verified end to end. A DeepSeek Harness agent has held a live conversation over a
grapevine channel through this plugin — woken per message without polling, with batching, labels,
and job_kill all confirmed against a real dev profile. See the acceptance scenario and its
result in docs/design.md.
What dsh already has, and what this adds
The dsh background-job runtime (ctx.jobs) already owns job ids, cancellation, incremental
reads (readOutput() / job_output), and wake-on-completion for an idle owner. Separately,
agent.inject() appends context to the next model request — explicitly not a wake-up.
This plugin extends that from completion-only to per-output event. Delivery is
agent.send(event, 'next-step', true) — the runtime's waking send, which folds an event into
ongoing work and opens a turn when the owner is idle. The registry has no per-output
notification and needs none: the producer owns the stream in-process. Around that sit the
operational details that make it survivable:
- line/frame granularity — each output line is one event
- short-window batching (~200ms) so multi-line bursts arrive as one notification
- rate limiting with auto-kill on firehoses
- a per-monitor label carried in every notification
- timeout by default, opt-in session-length persistence, stoppable via
job_kill
Development
Bun + Biome for local development (the published package is plain JS in dist/, so this is
invisible to consumers):
bun install
bun run build # tsc → dist/
bun run check # biome
Install into a dsh profile from a local checkout — note dsh plugin add forwards to pnpm
on the consumer side, and git installs need the prepare script allowlisted in the consumer's
pnpm-workspace.yaml; neither constrains this repo's own tooling:
dsh plugin --profile dev add /path/to/dsh-plugin-monitor
Add it to the profile you actually boot — the web UI runs the web profile, so installing
into dev and then launching the web UI is a correct command aimed at the wrong target. Verify
before launching:
dsh --dump-config | grep monitor
Tuning the delivery budgets
Optional. The defaults are the policy for almost everyone; override only if a
source legitimately needs more headroom, as a config: block on the profile entry:
- id: monitor
name: dsh-plugin-monitor
config:
maxEventBytes: 32768 # bytes in one delivered event (default 16 KiB)
throttleBytes: 262144 # bytes per 10s before throttling (default 128 KiB)
killBytes: 1048576 # bytes per 10s before the monitor is stopped (default 512 KiB)
spillBytes: 524288 # capacity for event-clipped output (default 256 KiB)
Config is validated at load, so a mistyped budget fails the boot rather than the
first overflowing event. Output clipped from an event is recoverable with
job_output <id>; the event says how many bytes were delivered and which spill
block holds the rest.
If tool calls start failing after installing this
A symptom worth recognising, because it looks like this plugin and is not:
Cannot read properties of undefined (reading 'prepare')
Every tool call in the profile fails, including ones unrelated to this plugin. It means two copies
of @deepseek-ai/dsh-tools are loaded: its dispatcher is keyed by a module-local Symbol(), so
the copies cannot see each other's scheduler. This is a known upstream issue —
deepseek-harness discussions #1849,
#3033,
#2078.
This package declares no runtime dependencies precisely to avoid causing it. If it happens anyway, check for a rival copy and remove or link it:
ls $DSH_HOME/profiles/<name>/node_modules/@deepseek-ai
It is most likely when the harness is installed globally rather than under $DSH_HOME.
Layout
src/index.ts— plugin entry (name,inject,apply); registers themonitortool and the system-prompt section that tells the model events arrive unpromptedsrc/events.ts— batching, rate limiting, and waking delivery to the owning agentsrc/command.ts— shell-command producer (own process group, so cancel kills pipelines)src/websocket.ts— WebSocket producer (one event per text frame)src/lines.ts— line splitting across chunk boundariescordis.patch.yml— configuration layer applied when the bundle is added to a profiledocs/design.md— design notes and open questions