dsh-kanban-watcher
⚠️ AI-GENERATED CODE NOTICE: This plugin was generated by DeepSeek AI and has NOT been human-reviewed. Use at your own risk; review security-critical paths (permission presets, output whitelist, file writes) before deployment. 中文版 README 见 README.zh-CN.md
The board-execution-side plugin of the DSH ↔ Hermes dual-agent collaboration pipeline: watches the Hermes kanban task queue, hands tasks to a DSH Agent in a Web GUI session, and writes results back to the board plus a done file.
- Runtime: a Cordis plugin inside the DeepSeek Harness (DSH) web profile (resident in the web host process)
- Peer: Hermes Agent (Python) — dispatches tasks, maintains the board, receives completion notices
- In one line: Hermes says "do X" → a task appears on the board → this plugin wakes a DSH Agent to do the work → results go back to the board + a done file notifies Hermes
When to use
This plugin solves how tasks cross systems when two independent agents (DSH + Hermes) run on the same machine:
| Scenario | Description |
|---|---|
| Dual-agent collaboration | You dispatch from the Hermes terminal (/dsh-send); DSH auto-opens a session in the Web GUI, fully visible and replayable |
| Kanban task queue | The queue uses Hermes' built-in kanban (~/.hermes/kanban.db, board dsh) with metadata, atomic claim, state machine, comments, crash recovery |
| Unattended execution | Watcher sessions use a dedicated permission preset (danger-full-access + never); whitelisted outputs write without approval |
| Results visible on both sides | DSH: the session stays in the GUI session list; Hermes: board comments carry the result summary, and the done file triggers external-event injection into the running session |
Not for: single-agent setups (no board needed); parallel multi-task execution (v1 is serial); cross-machine deployments (assumes DSH and Hermes share one machine and user).
How it works
Hermes terminal DSH web host Hermes side
───────────── ────────────── ─────────
/dsh-send "task" ──create──▶ board (kanban.db, board=dsh)
(skill) ──trigger──▶ ~/.dsh/kanban-trigger/<id>.trigger
│ fs.watch event-driven wake-up
▼
claim <id> (atomic)
show <id> --json (read metadata)
agents.create (open GUI session)
followup(task) → agent executes
│ withTimeout hard cap (600s)
▼
comment + complete --result ──▶ board write-back (/inbox visible)
writeDoneFile(<id>.done) ──▶ ~/.dsh/kanban-done/<id>.done
→ Hermes idle loop notices, injects into the session
Key design points:
- Event-driven wake-up: after creating a task, Hermes writes a trigger file
(
~/.dsh/kanban-trigger/<id>.trigger); the plugin'sfs.watchresponds in milliseconds, with a 30s low-frequency fallback poll in case events are lost. - Atomic claim prevents duplicate execution: only
status === "ready"tasks are claimed; kanban guarantees a single claim; arunningset prevents same-tick duplicates. - Serial execution: one task at a time, later tasks queue (v1 design).
- Crash recovery: on startup the watcher scans
runningtasks andreclaims them back toready(previous execution interrupted); it also holds a singleton lock (PID + liveness check). - Model translation: Hermes' model spec (
model_override/provider_override) is mapped to a DSH provider/model viamodelMap; unmapped specs fall back to__fallback__; the actual model used is written back into the result. - Session visibility: each task gets its own
session-hermes-<id>-<uuid>session attached to the workspace, so it appears in the GUI sidebar in real time and survives refresh.
Install
This plugin is a Cordis plugin package, not a dsh.bundle patch layer: once added as a
dependency in the web profile, the loader imports it directly.
1. Add the dependency (in the web profile dir)
cd ~/.dsh/profiles/web
pnpm add file:<your-clone-path>/dsh-side/plugins/dsh-kanban-watcher
Expected output
dsh: warning: dsh-kanban-watcher declares no dsh.bundle— this is NORMAL. The plugin only needs to be imported by the loader, not become a patch layer.
2. Mount the plugin line (~/.dsh/profiles/web/cordis.patch.yml)
- insert:
- id: kanban-watcher
name: 'dsh-kanban-watcher'
config:
board: 'dsh'
assignee: 'dsh'
triggerDir: '~/.dsh/kanban-trigger'
pollIntervalMs: 30000
allowedOutputDirs:
- '<your-DSH-workspace>'
modelMap:
'deepseek-v4-flash': { provider: 'deepseek-official', model: 'deepseek-v4-flash' }
'__fallback__': { provider: 'deepseek-official', model: 'deepseek-v4-flash' }
3. Restart web
pkill -f "dsh web --port 3080" # kill cleanly first (rule), then start
nohup <dsh-bin> web --port 3080 > /tmp/dsh-web.log 2>&1 &
Verify: tail -f /tmp/dsh-web.log shows kanban-watcher started.
Configuration
All config lives in the plugin line's config in cordis.patch.yml; every key has a default:
| Key | Default | Description |
|---|---|---|
hermesBin |
~/.local/bin/hermes |
Path to the hermes CLI (kanban subcommand entry) |
board |
dsh |
Board name; must match the board used by Hermes /dsh-send |
assignee |
dsh |
Claim owner; only tasks assigned to dsh are processed |
triggerDir |
~/.dsh/kanban-trigger |
Trigger file dir: Hermes writes <id>.trigger here; fs.watch wakes on events |
doneDir |
~/.dsh/kanban-done |
Done file dir: written on completion; Hermes' external-event injection watches here by default (HERMES_DONE_WATCH_DIR can override) |
pollIntervalMs |
30000 |
Fallback poll interval (fs.watch events may coalesce/lost) |
taskTimeoutMs |
600000 |
Per-task hard timeout (10 min); releases the agent session and reports the error, so an unanswered approval can't stall the pipeline |
permissionPreset |
hermes-trusted |
Watcher-session permission preset (danger-full-access + never): whitelisted outputs write without approval, upgrade requests fail fast |
cwd |
$DSH_WORKSPACE |
Default working dir when a task doesn't specify one |
allowedOutputDirs |
[$DSH_WORKSPACE, $DESKTOP] |
Output whitelist (mirrors the Hermes dispatch-side whitelist; enforcement stays on the Hermes side) |
modelMap |
flash→flash / __fallback__ |
Hermes model spec → DSH provider/model translation table; __fallback__ is the catch-all |
resumeTokenWarning |
500000 |
Token guard threshold: warns to create a new session when a resumed session is estimated above this |
⚠️ Security trade-off (must read):
hermes-trusted= danger-full-access + never, so a watcher session has full-disk write permission. The security boundary relies on the Hermes dispatch-side whitelist (source filtering), not a sandbox. Protect against untrusted task sources separately.
Usage
Dispatch from the Hermes side (upstream)
Hermes uses the /dsh-send skill (see hermes-side/dsh-send-skill/SKILL.md):
/dsh-send --model deepseek-v4-flash write this week's progress report
/dsh-send does: create a kanban task (hermes kanban --board dsh create ... --assignee dsh --created-by hermes --json) → write a trigger file ~/.dsh/kanban-trigger/<id>.trigger → return the task id.
Watcher claims and executes (this plugin)
fs.watchreceives a trigger-file event (or the 30s fallback poll),drainOncestarts;list --json --assignee dshfindsreadytasks →claim <id>(atomic) →show <id> --jsonreads metadata;model_override/provider_overrideare translated throughmodelMap,agents.createopens a GUI session (standard preset + hermes-trusted permission preset),followupinjects the task,withTimeoutcaps it;- On completion:
comment+complete --resultwrite back to the board, thenwriteDoneFilewrites<id>.done.
Session inheritance (v0.2.0, continue a previous conversation)
When the task body carries [会话继承:<keyword>] (or an explicit [会话继承:<session-id>]), the
watcher matches a historical session in the same workspace and continues its context instead of
starting a fresh conversation:
- live session → reuse directly (
agents.get+followup, scheme A) - cold session →
agents.resumerestores the event stream, then continue - no match / same-session double-occupancy → fall back to a new session (unchanged behavior)
/dsh-send --resume keep-discussing-X analyze the conclusion from before
# The task body will carry [会话继承:keep-discussing-X]; the watcher matches a historical
# session whose title contains the keyword
- Result write-back includes an audit field:
继承会话: <session-id> - Token guard: estimates tokens before resume; above
resumeTokenWarning(default 500k) it warns to create a new session - Resumed sessions keep their original cwd (header is immutable); conflicts with
--workspaceare only logged - Matching is limited to sessions in the target workspace; explicit session-ids are also workspace-scoped (no cross-workspace inheritance)
View results (Hermes side)
hermes kanban --board dsh show <id> # board detail + comments
/inbox # task queue (read-only)
DSH side: a "📥 from Hermes: ..." session appears in the Web GUI session list; open it to replay the full execution. The done file also makes the Hermes session auto-pop an "External Notification".
Directory layout
dsh-kanban-watcher/
├── lib/
│ └── index.js # plugin body (single file, ~590 lines)
├── package.json # package manifest (Cordis plugin, not a bundle patch)
└── README.md # this file (EN) / README.zh-CN.md (中文)
Developer notes
- Source of truth = this repository; deploy via
pnpm add file:into~/.dsh/profiles/web/node_modules/. After editing source, restart dsh web for changes to take effect (a running node process does not hot-reload plugin code). - Facts about the Hermes kanban CLI (
list --jsonreturns a plain array,showreturns.task,claimhas no--assignee,commenttakes positional args, terminal state isdone, etc.) are verified and documented in the header comment oflib/index.js.
License
MIT
还没有评论,来写第一条。