在 Windows 系统托盘里按工作区显示 DSH 对话状态,并在对话需要批准 / 选择 / 输入、完成或失败时,由 DeepSeek Harness 应用本身弹出原生通知。仅支持 Windows 10/11 与 DSH Desktop 0.2.3-rc.1 及以上。托盘与通知必须先一次性安装桌面桥接(用户级、可卸载):它把 DSH 快捷方式改指向隐藏启动器,经启动时短暂开启的本机回环 Node Inspector 向 DSH 的 Electron 主进程注入钩子;不装桥接则插件只写出 state.json。空闲对话默认不进入托盘;托盘行数、顶部计数、页脚、通知类型、前台免打扰、提示音共 9 项可在「托盘随航」设置页实时调整。
安装
# Release 预构建包
dsh plugin --profile web add "https://github.com/Green233233/dsh_tray_follower/releases/download/v0.2.3/dsh-tray-follower-0.2.3.tgz"
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:Green233233/dsh_tray_follower#path:/packages/dsh-tray-follower
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
该插件的 README 只有英文版本。
A DSH bundle with a Host half and a small web Client half that tracks every
top-level conversation of the running DeepSeek Harness Host, grouped by
workspace, and publishes their status to a state file. The DSH Desktop shell
bridge (see bridge/shell-hook/ in the repository) reads that file to build
the tray icon's right-click menu and to raise native notifications while the
window is hidden or unfocused.
The plugin publishes state only. Notifications are emitted exclusively by the
DSH Desktop shell bridge (in-process Electron Notification, so they appear
under the DeepSeek Harness name) — the plugin never starts an external notifier
process (no PowerShell/WinRT/notify-send). Without the bridge the plugin still
publishes state.json, but no tray section and no OS notifications appear.
- Package name / row:
dsh-tray-follower - Host half:
index.jsexportsConfig,name,inject,apply. - Client half:
client.jsregisters the Settings → Tray follower page (slotsettings.section) that edits the HostConfiglive. - State contract: docs/DESIGN.md §4 / §4.1 — implemented exactly
(
schemaVersion: 2, includes the publishedoptions). - One runtime dependency:
@deepseek-ai/schemastery(the Host Config schema); the Client half imports only React from the browser module table.
What it publishes
%LOCALAPPDATA%\dsh-tray-follower\state.json
(override with DSH_TRAY_FOLLOWER_DIR), written atomically (tmp + rename) with a
≤250 ms debounce:
{
"schemaVersion": 2,
"updatedAt": 1730000000000,
"hostPid": 1234,
"webOrigin": "http://127.0.0.1:61983",
"options": {
"tray": { "showIdle": false, "maxSessionsPerWorkspace": 6, "showHeaderCounts": true, "showFooter": true },
"notifications": { "notifyOnCompleted": true, "notifyOnFailed": true, "notifyOnAttention": true, "suppressWhenFocused": true, "notificationSound": true }
},
"workspaces": [
{
"id": "ws_...",
"title": "dsh_tray_follower",
"path": "C:\\...\\dsh_tray_follower",
"sessions": [
{
"id": "session-...",
"title": "配置 Git 等工程管理",
"status": "running",
"detail": "Running bash",
"updatedAt": 1730000000000,
"startedAt": 1730000000000
}
]
}
],
"unreadEvents": [
{
"id": "evt_...",
"kind": "completed",
"sessionId": "session-...",
"workspaceId": "ws_...",
"title": "Task completed",
"body": "dsh_tray_follower • 配置 Git 等工程管理",
"createdAt": 1730000000000
}
]
}
Notes:
webOrigincomes from the Host'swebApp.getAdvertisedUrl()(falling back towebServer.getOrigin(), thenDSH_WEB_URL); it is""when unknown.workspaceskeeps theworkspaceRegistrydurable order. Conversations that belong to no workspace are published under the synthetic sectionid: "unassigned"instead of being dropped.- Only live top-level conversations are published. Subagent children
(
SessionHeader.origin === 'subagent'ordelegationDepth > 0) are ignored. A conversation that stops being live keeps its finalcompleted/failedstate for five minutes, then disappears from the file. unreadEventskeeps the last 50 events. Ids are deterministic (evt_+ SHA-1 of the logical occurrence), so re-delivered Host events and plugin reloads cannot duplicate a notification on the bridge side.- On unload the file is flushed and left in place; a stale
updatedAtis how the bridge can recognize that the Host is gone.
Status derivation
| status | meaning |
|---|---|
running |
the agent is running, or an active goal keeps continuing between rounds |
waiting-approval |
an approval/request is pending (or approval/asked was logged without its outcome) |
waiting-choice |
ask_user_question with options / plan review is pending |
waiting-input |
free-text ask_user_question is pending |
completed |
the last turn ended completed (or a goal completed) |
failed |
the last turn ended error / max-tokens / blocked, a goal was blocked, or agent/error was recorded and the agent stopped |
idle |
live, nothing pending, no outcome yet (user aborts and crash markers stay idle) |
detail is a short localized line for the menu (Running bash, Waiting for approval: …, Completed, …).
Unread events are emitted for user-relevant stops only:
| kind | source |
|---|---|
completed |
turn/end reason completed, goal complete |
failed |
turn/end reason error/max-tokens/blocked, goal block, agent/error (once the agent stops) |
needs-attention |
pending approval / question that is still pending after 1.2 s |
An immediately answered approval or question never notifies. A conversation
that is following an armed goal suppresses its between-round completed turns
and notifies only when the goal itself completes or blocks.
Notifications
Notifications are not sent by this plugin. The DSH Desktop shell bridge (see
bridge/) reads unreadEvents from state.json, checks the real window
focus/visibility state, and raises a native notification through the Electron
main process (new Notification(...)), which Windows attributes to DeepSeek
Harness itself. This matches the product requirement that notifications be sent
in DSH's own name and that no external PowerShell/WinRT notifier be used.
The bridge suppresses notifications while its window is focused/visible (unless
suppressWhenFocused is turned off), keeps a persistent cursor so events are
never replayed, silently baselines existing events on first run, and ignores
events older than 10 minutes. Kind filters and the sound toggle come from
state.json.options (Settings → Tray follower). When the bridge is not
installed (or injection failed), the Host keeps publishing state and no OS
notification is raised — a deliberately accepted degraded mode.
Settings
All options live in DSH Settings → Tray follower (native page registered by
client.js). They are declared as a .volatile() Host Config schema, so a
change is applied without restarting DSH: the Host polls the live values every
second and republishes state.json.options immediately; the bridge reads them on
its next poll (≤1 s).
| Field | Type / default | Group | Meaning |
|---|---|---|---|
showIdle |
boolean / false |
Tray menu | Also list idle conversations (off = idle conversations never appear in the tray menu) |
maxSessionsPerWorkspace |
number 1–20 / 6 |
Tray menu | Max conversations listed per workspace (overflow shows "还有 N 个会话") |
showHeaderCounts |
boolean / true |
Tray menu | Show running/pending counts in the menu header |
showFooter |
boolean / true |
Tray menu | Show the version footer |
notifyOnCompleted |
boolean / true |
Notifications | Notify when a conversation completes/stops |
notifyOnFailed |
boolean / true |
Notifications | Notify when a conversation fails |
notifyOnAttention |
boolean / true |
Notifications | Notify when an approval/answer is needed |
suppressWhenFocused |
boolean / true |
Notifications | Stay quiet while the DSH window is focused |
notificationSound |
boolean / true |
Notifications | Play the system alert sound with each notification |
Environment overrides are unchanged:
| Variable | Default | Meaning |
|---|---|---|
DSH_TRAY_FOLLOWER_DIR |
%LOCALAPPDATA%\dsh-tray-follower ($XDG_STATE_HOME/dsh-tray-follower, ~/Library/Application Support/... elsewhere) |
data directory holding state.json |
DSH_TRAY_FOLLOWER_LANG |
DSH_LOCALE, LC_ALL, LC_MESSAGES, LANG, JS locale → en |
runtime language for detail (en, zh) |
Bundle contents
package.json name, exports (., ./client, ./locale/*.json, ./icon), files, dsh.bundle.patch, dsh.client
cordis.patch.yml insert row: id dsh-tray-follower, name dsh-tray-follower
index.js Host plugin: Config schema, services, events, timers, state writer
lib/ pure modules (unit-tested with node --test)
client.js Client half: Settings → Tray follower page (settings.section)
locale/en.json meta.title "Tray follower" / meta.description
locale/zh.json meta.title "托盘随航" / meta.description
icon.svg original icon (SVG, < 64 KiB)
test/*.test.mjs node --test suites
Install
Installed by the repository's install step through plugin_manager
install_bundle with the absolute package directory; do not edit the profile's
package.json / cordis.patch.yml by hand. Row metadata
(id: dsh-tray-follower, name: dsh-tray-follower) lives in
cordis.patch.yml.
Develop / verify
# syntax
Get-ChildItem -Recurse packages/dsh-tray-follower -Include *.js,*.mjs | ForEach-Object { node --check $_.FullName }
# unit tests (Node 24 does not expand a positional directory argument; use the glob)
node --test "packages/dsh-tray-follower/test/*.test.mjs"
Known limitations
- Host half cannot know whether the DSH window is focused: focus-based suppression is the bridge's job. Without the bridge there is no tray section and no notification at all (by design — no external notifier process is started). The Settings page itself works without the bridge (it edits the Host Config), but the settings only change tray/notification behaviour once the bridge is installed and DSH was started from the bridge launcher.
- Titles depend on the optional
sessionTitleservice (sessionTitle.get) and itssession/titlelog event; without it the tray falls back to the working directory's last segment. - Only sessions live in the current Host process are tracked; conversations from previous runs are not listed.
- Waiting states from
approval/request/user-questions/requestadditionally depend on being reached by the waterfall: if an earlier answerer claims a request before this plugin's listener runs, the state falls back to the session log (approval/asked,tool/callofask_user_question), which covers tool approvals and questions but not arbitrary non-tool requests. webOrigin/bridging assume the desktop shell can read the data directory written by the Host process (same user profile).- Portable: the Host writes the state contract on every platform; tray menu and notifications require the DSH Desktop bridge (Windows-focused in this repo).
链接
同类插件
xmanrui/dsh-im★ 3118
通过二维码或机器人凭据将 IM 机器人接入 DeepSeek Harness(支持飞书、微信、钉钉、企业微信、QQ、Slack、Telegram、Discord 和 WhatsApp 共 9 种渠道)。
shaobeichen/dsh-pocket★ 2685
手机远程访问 DSH Web 界面:扫码即用局域网或公网(cloudflared 隧道)访问,实时同屏、移动端适配布局,带设置页管理。
OpenPetsHQ/openpets#dsh★ 1373
将 DeepSeek Harness 的生命周期状态、错误与审批请求,桥接到本地运行的 OpenPets 桌面伙伴。
c1overzz/dsh-bili-scroll★ 1150
让你的 DSH 自己刷 B站 —— 读字幕原文挑内容、写日报,遇到值得看的会发评论 @ 你。
inclusionAI/Avernet#deepseek-harness-channel-bcn★ 599
通过 WebSocket V2 将 DeepSeek Harness 接入 Avernet Bot 协作网络,支持自动注册、Agent 会话隔离、工具调用事件和多 Bot 路由工具。
zaofan-make/dsh-qqbot★ 213
接入 QQ 官方机器人的 DeepSeek Harness 通道(tencent-connect/dsh-qqbot 功能增强 fork): 群/私聊与完整 agent 对话、表情包图库(自动打标+AI 斗图)、群管理(入群审批/禁言)、botplay 互动事件卡与翻页目录、用户扩展注册表(自定义斜杠命令与 QQ 工具)、定时唤醒、QQ 远程审批、悬浮球 dock Web UI。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。