Task-queue execution endpoint for DSH: requires a separately self-deployed Hermes Agent gateway plus your own WeChat inbound script; verified on a single environment — expect to adapt it.
Install
# from npm (prebuilt)
dsh plugin --profile web add @dsh-pulse/dsh-hermes-bridge
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:dsh-pulse/dsh-hermes-bridge
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
DSH ↔ WeChat bridge plugin: inbound dispatch (WeChat → DSH agent) + outbound push (DSH → WeChat), powered by the Hermes gateway as the WeChat transport. Zero-LLM outbound push via hermes send, and a persistent per-cwd session pool that reuses DSH agent sessions (15–20× cheaper than one-shot headless invocations).
Scope: this plugin is a task-queue execution endpoint for DSH — it does not implement the WeChat protocol itself. The WeChat side (both the inbound trigger and the transport) comes from a self-deployed Hermes Agent gateway plus your own inbound script; without those, the WeChat half of the chain will not work.
微信 ↔ DSH 双向桥插件:入站调度(微信消息 → DSH agent 执行)+ 出站推送(任务进度/结果 → 微信)。出站走 hermes send 零 LLM 消耗;按工作目录复用常驻会话,比一次性 headless 调用省 15–20× token。
⚠️ Read this first — environment assumptions. This plugin was developed against one specific environment (see Environment assumptions). DSH and Hermes deployments vary wildly between users; this plugin cannot work out-of-the-box everywhere. Please review the assumptions and adapt the configuration to your setup before expecting it to run.
⚠️ 先读环境假设。 本插件基于特定开发环境编写(见「环境假设」章节)。DSH 与 Hermes 的部署方式千奇百怪,本插件无法开箱即用于所有环境,请先核对假设并适配你的配置。
Why
The one-way manual chain "WeChat → Hermes skill → dsh --profile headless" works but burns tokens on cold starts, loses session context between messages, and cannot notify you when a long task finishes. This plugin productizes the chain as a first-class DSH plugin:
- Session reuse — agents stay alive per working directory (
ctx.agents.create/resume + followup), so follow-up messages continue the same conversation. - Automatic pushback — every task reports
接单 → 开跑 → 完成/失败to WeChat with structuredchanges / verification / leftovers. - Zero-LLM outbound — notifications go through
hermes send(iLink REST), no model call, no gateway dependency for bot-token platforms. - Web UI visible — tasks are registered via
ctx.jobs(falls back gracefully when the job controller is absent).
How it works
WeChat ──▶ Hermes gateway ──▶ dsh-run.sh (bridge-first) ──▶ POST 127.0.0.1:8643/v1/tasks
│
▼
persistent session pool (per cwd)
│
DSH agent (full toolset, followup + whenIdle)
│
WeChat ◀── hermes send ──◀── structured result pushback ──◀──┘
Inbound: Hermes (or any HTTP client) POSTs a task to the plugin's loopback endpoint. The plugin dispatches to the per-cwd resident agent session and pushes progress back via hermes send. If the bridge is down, the shell script falls back to one-shot dsh --profile headless.
Crash recovery / overnight hosting (M3)
The task queue is persisted to disk (~/.dsh/hermes-bridge/tasks.jsonl, JSONL append per state change). On startup the plugin reloads it and:
queuedtasks are resumed and run;runningtasks (interrupted by a restart) are re-queued with their originalsessionId, so the agent session is resumed and work continues where it left off — no cold restart, no lost work;- a task interrupted more than
maxRunstimes (default 2) is marked failed instead of looping forever; - done/failed tasks are kept for status queries only, and the file is rewritten to match memory (bounded).
Because the dsh server itself runs under systemd with Restart=always, this closes the loop: submit tasks in the evening (e.g. via WeChat), the server can restart overnight for any reason, and the queue picks right back up — results are pushed to WeChat as each task finishes.
Environment assumptions
Developed and verified on one specific machine (see the deployment notes in DEVELOPMENT.md). Your environment will differ; check each item:
| Assumption | Value used here | What to adapt on your side |
|---|---|---|
| DSH version | 0.1.0-rc.7 (Node ≥ 22) |
ctx.agents / ctx.agentPresets / session APIs are rc-stage snapshots — a DSH upgrade may break them. Pin the rc range you run, or adjust code. |
| DSH profile | web profile (persistent host; dsh web keeps the plugin resident) |
A headless-only deployment must host the plugin some other way (its HTTP loopback must stay alive). |
| DSH model config | provider: tokenrhythm / model: deepseek-v4-flash-0731 (from your ~/.dsh/settings.yaml agent-default-model) |
Point provider/model at your default model. {{model}} is resolved from agentOptions.model — if the persona template says {{model}} has no value, you are missing model. |
| Hermes install | venv at ~/.hermes/hermes-agent/venv/bin/hermes, gateway service hermes-gateway.service (user systemd) |
hermesBin config must point at your hermes CLI; the gateway service name/port may differ. |
| WeChat channel | iLink personal-bot channel (ilinkai.weixin.qq.com) reached by Hermes |
Your channel may be Telegram/Discord/Slack/etc. The outbound path only needs hermes send --to <platform>:<target> — switch pushTarget accordingly. The inbound trigger (Hermes skill / hook) is channel-specific. |
hermes send targets |
verified with hermes send --list |
Run hermes send --list yourself; chat IDs are environment-specific. |
| HTTP loopback | 127.0.0.1:8643 |
Change port if it collides; the host is hardcoded to loopback (security). |
| Session persistence | ~/.dsh/sessions (per-user) |
If your DSH_HOME differs, session resume paths change accordingly. |
Known limits (not bugs):
- WeChat rate limit: the iLink channel throttles
sendmessage(ret=-2→ 30s cooldown, handled by Hermes). Bursts of pushes can be dropped/serialized. Keep push volume low (the plugin pushes once per task state, which is fine). - No auth layer on the host:
dsh webhas no authentication by design. Run it loopback-only; do not expose it. - rc-stage API drift: any DSH upgrade needs a regression pass (see
DEVELOPMENT.mdfor what to re-verify).
Install
From npm (published as @dsh-pulse/dsh-hermes-bridge):
dsh plugin --profile web add @dsh-pulse/dsh-hermes-bridge
Or from GitHub:
git clone https://github.com/dsh-pulse/dsh-hermes-bridge.git
cd dsh-hermes-bridge && npm install # installs peer deps if missing
# register in your web profile patch
cat >> ~/.dsh/profiles/web/cordis.patch.yml <<'EOF'
- insert:
- id: hermes-bridge
name: 'file:/absolute/path/to/dsh-hermes-bridge/lib/index.js'
config:
port: 8643
authToken: '${env.DSH_BRIDGE_TOKEN}' # REQUIRED — plugin refuses to start without it
pushTarget: 'weixin:<chat_id>' # REQUIRED
hermesBin: '/path/to/hermes' # optional, default 'hermes'
workspaceRoots: ['/path/to/workspace'] # optional cwd whitelist
EOF
Requires @deepseek-ai/cordis (peer dependency) — ships inside DSH. The plugin is designed for the web profile (persistent host); do not install into headless.
Configuration (Config schema)
| key | type | default | description |
|---|---|---|---|
port |
number | 8643 |
loopback listen port (host is hardcoded 127.0.0.1) |
authToken |
string | — | required; Bearer token checked on every request (constant-time compare) |
pushTarget |
string | — | required; e.g. weixin:<chat_id> — resolved by hermes send |
hermesBin |
string | hermes |
path to the hermes CLI |
retries |
number | 1 |
outbound push retries |
maxTextLen |
number | 1500 |
push text truncation |
preset |
string | standard |
agent preset mounted in session setup |
provider / model |
string | dsh defaults | agent model (align with your agent-default-model) |
workspaceRoots |
string[] | [] |
cwd whitelist (realpath prefix check; empty = unrestricted) |
maxAgents |
number | 3 |
resident session cap (LRU eviction) |
maxQueue |
number | 8 |
task queue cap (429 when full) |
taskTtlMs |
number | 86400000 |
finished task retention (24h) |
queueFile |
string | ~/.dsh/hermes-bridge/tasks.jsonl |
JSONL queue persistence path (~ expands) |
maxRuns |
number | 2 |
max interrupted-and-resumed attempts per task before it's marked failed (guards restart loops) |
recoverOnStart |
boolean | true |
reload and resume unfinished tasks on startup (set false to disable auto-recovery) |
taskTtlMs |
number | 86400000 |
finished task retention (24h) |
API (all endpoints require Authorization: Bearer <token>)
| Endpoint | Description |
|---|---|
POST /v1/tasks |
Inbound task {task, context?, cwd?, sessionId?, title?} → 202 {taskId} |
GET /v1/tasks/:id |
Task status + structured result (changes/verification/leftovers) |
POST /v1/tasks/:id/cancel |
Cancel (queued → failed immediately; running → intent flagged) |
POST /v1/notify |
Pure push {text} (no agent involved) |
GET /v1/health |
Liveness |
Pushback messages (one per state, no spam):
📥 已接单 br-xxxxxxxx
🔧 开跑 br-xxxxxxxx
✅ br-xxxxxxxx(6m12s)
改动:…
验证:…
遗留:…
Security
- Loopback only:
server.listen(port, '127.0.0.1')— the host is hardcoded, exposing it requires source changes. - Auth required:
authTokenis mandatory; the plugin refuses to activate without it. Token lives in your profile patch (600 perms) or env, never in code. - cwd whitelist: when
workspaceRootsis set, paths outside it are rejected (403) both at ingress and insideexecuteTask. - Zero shell: all child processes use
spawn(args[])— task text never passes through a shell. - Redaction: outbound push strips
sk-/sk_tr_/ghp_token patterns and credentials paths before sending.
⚠️ This plugin executes arbitrary DSH agent work with full tool access. Only feed it trusted instructions (keep the WeChat-side discipline in your Hermes skill layer), and never expose the HTTP port beyond loopback.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
prompt variable "{{model}}" has no value |
agentOptions.model missing — set model (align with your agent-default-model). |
Task reports done but result is empty |
Check the session log for turn/end with reason.kind == "error" — the plugin now flags those as failed; verify model/provider. |
hermes send fails Could not resolve target |
Run hermes send --list, put the exact target in pushTarget. |
EADDRINUSE on the port |
Another instance holds it — change port, or restart the host once after code edits (ESM module cache). |
| WeChat pushes missing/serialized | iLink rate limit (ret=-2, 30s cooldown) — keep push volume low. |
| Hermes still runs headless instead of bridge | The plugin is invoked via dsh-run.sh which internally tries the bridge first (then falls back). If your skill calls something else, point it at dsh-run.sh. See DEVELOPMENT.md §Troubleshooting-Hermes. |
Development
See DEVELOPMENT.md (EN) / DEVELOPMENT.zh-CN.md (中文) — architecture decisions, the problems we hit and how they were solved, test strategy, and what to re-verify after a DSH upgrade.
License
MIT
Links
More in this category
xmanrui/dsh-im★ 2147
Connect IM bots to DeepSeek Harness via QR codes or bot credentials (9 channels: Feishu, WeChat, DingTalk, WeCom, QQ, Slack, Telegram, Discord, and WhatsApp).
shaobeichen/dsh-pocket★ 1929
Remote phone access to the DSH Web UI: scan a QR code for LAN or public (cloudflared tunnel) access with real-time sync, a mobile-adaptive layout, and a settings tab.
OpenPetsHQ/openpets#dsh★ 1309
Bridges DeepSeek Harness lifecycle status, errors, and approval requests to a locally running OpenPets desktop companion.
inclusionAI/Avernet#deepseek-harness-channel-bcn★ 641
Connects DeepSeek Harness to Avernet's Bot Collaboration Network over WebSocket V2, with automatic onboarding, isolated agent sessions, tool-call events, and multi-bot routing tools.
c1overzz/dsh-bili-scroll★ 203
Let your DSH agent browse Bilibili on its own, read subtitles, write a daily digest, and @ you when it finds something worth your attention.
Oscar-Williams/dsh-deepcanary★ 91
Evidence-first attention supervision for DeepSeek Harness with C0–C3 policy, deduplicated Inbox alerts, quiet hours, and redacted outcome records.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.