DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

renjianbuchai /

renjianbuchai/dsh-session-attention

Verified

Browser-side attention reminders for DeepSeek Harness — desktop notification, tab-title dot and PWA badge when a reply finishes in the background or a session needs your input. 会话在后台跑完或等你回应时:系统通知 + 标题红点 + PWA 徽标。

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@6e0c08b7

dsh-session-attention

English | 中文说明

Attention reminders for DeepSeek Harness — a client-only plugin. No DSH core file is patched.

When a session needs you, three signals light up together:

Signal What it is
Desktop notification A real system notification — title 回复已完成 / Reply finished, body names the session
Tab-title dot ● prefixed to the browser tab title
PWA taskbar badge navigator.setAppBadge(n) — the app-icon badge of an installed PWA

It fires at exactly two moments, and only while the window is in the background (document.hidden || !document.hasFocus()), so it never interrupts you while you are already looking at the app:

  1. A reply finished — a session's running goes true → false.
  2. A session is waiting for you — a session's pendingInteraction appears: approval, plan-review or question.

All three signals clear the moment you return to the window. Subagent sessions are skipped on purpose: they are not yours to answer.

Install

pnpm dsh plugin --profile web add github:renjianbuchai/dsh-session-attention

dsh plugin is a thin wrapper around pnpm, so any pnpm spec works — <dir> for a local checkout, github:<user>/<repo>, or the npm name once published. The command writes the dependency and enables the plugin in the profile; there is nothing else to toggle.

Once the plugin is listed in the awesome-dsh-plugin catalog, it can also be installed from the DSH plugin market with one click.

Two things that will otherwise waste your time:

  • If pnpm refuses with ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION, add --config.minimumReleaseAge=0. That is a pnpm release-age policy knob and has nothing to do with this plugin — a branch or publish can simply be too recent for the configured window.
  • Grant the notification permission when the browser asks. Without it the tab dot and the badge still work, but the desktop notification stays silent by design (nothing is prompted from a background event, because a browser ignores a permission request that has no user gesture behind it).

Requirements

  • DSH >= 0.2.1-alpha.1 < 0.3.0-0 (developed and verified on 0.2.1-alpha.1)
  • Node >= 22.19.0 — DSH itself requires ^22.19.0 || >=24.0.0
  • A web profile in a browser. The desktop notification needs Notification; the taskbar badge needs navigator.setAppBadge (installed PWA, Chromium-based). Both are presence-guarded: where the API is missing, that one signal no-ops and the others keep working.

How it works

  • The client half subscribes to two public host observables — uiSession.sessionStatus and sessions.list — and derives the two edges above. Nothing else is read, and no DSH source file is modified.
  • The title dot is maintained by a MutationObserver on <title>. The shell owns document.title and rewrites it on every navigation, so the plugin never takes ownership — it only re-adds the mark while events are unseen, and removes it when the count returns to zero.
  • Notifications are de-duplicated by a stable per-session tag (done:<id> / pending:<id>). The previous notification carrying that tag is explicitly closed and renotify: true is set, so a repeat event re-alerts instead of being silently replaced; live Notification objects are retained until they close.
  • Every notification title carries a DSH · brand prefix and the icon the page itself declares. The app name shown above a toast is chosen by the browser (Chrome uses its own App User Model Id), so a page cannot set it — the prefix is the plugin's only way to make the toast unmistakably DSH.
  • The host half (lib/index.js) declares the plugin and does nothing else: it writes no files and opens no sockets.

Debug surface

With the plugin loaded, the browser console exposes:

__DSH_ATTENTION__.permission  // current Notification permission
__DSH_ATTENTION__.log         // recent decisions: notified / skipped / failed + reason
__DSH_ATTENTION__.test()      // fire one notification through the plugin's own path

test() is the quickest way to separate "the browser or the OS is blocking toasts" from "the plugin never fired" — it uses the same code path as a real event.

Verification

Check Result
pnpm typecheck host + client halves, both --noEmit — 0 errors
pnpm selftest 45 / 45 assertions against the built lib/client.js, run both on the local artifact and on the exact bytes the DSH web server serves
pnpm e2e 17 / 17 against a real Chromium, a real running DSH instance and a real model turn: notification title/body/tag, ● in the title, setAppBadge(1), dot removal and clearAppBadge() on return, and that the document was not reloaded mid-wait (a reload would silently drop the test's background simulation and look like a plugin bug)

Limits, stated plainly:

  • The desktop toast is painted by the browser and the OS. A headless browser cannot assert that, so e2e verifies that the notification is constructed with the right arguments — not that Windows drew it.
  • Background state in e2e is a controlled document.hidden / hasFocus() simulation, because headless Chromium has no real tab focus.
  • Windows Focus Assist (Do Not Disturb) and the per-app notification switch can suppress toasts independently of any plugin. If __DSH_ATTENTION__.test() produces no toast while __DSH_ATTENTION__.log says notified, the block is on that side.

Provenance

The behavior is ported from DSH 0.1.x's built-in completion attention (packages/client/ui-renderer/src/client/completion-attention.ts and use-completion-attention.ts, commits 29d556d7b2, 3cb88b4aa5, 3e0553ed90) and rebuilt as an installable plugin that uses only public platform APIs — the same behavior on 0.2.x, without patching the renderer.

Development

DSH_HOME=<dsh home> DSH_CHECKOUT=<deepseek-harness checkout> pnpm prepare:links
pnpm typecheck && pnpm build && pnpm selftest

prepare:links mirrors the @deepseek-ai/* packages out of the profile's node_modules (and the checkout's vendor/, where cordis and friends live) into this project so tsc can see the host types. Those packages are pre-release and are not reliably available from the public registry; at runtime the profile resolves them, so the links only serve type-checking. Both paths come from environment variables — the script intentionally hard-codes no machine-specific path.

pnpm e2e needs a running DSH web instance and takes its target from environment variables (DSH_URL, DSH_WORKSPACE, DSH_TASK, PLAYWRIGHT_BROWSERS_PATH).

License

MIT — see LICENSE.


中文说明

给 DeepSeek Harness 的后台提醒 插件,纯客户端,不修改 DSH 核心任何文件。

会话需要你时,三处同时亮起:

信号 具体是什么
系统通知 真正的桌面通知 —— 标题带 DSH · 品牌前缀(如 DSH · 回复已完成;英文环境为 DSH · Reply completed),正文写出会话名,图标取页面自己声明的那个(气泡上方那行应用名由浏览器决定,网页改不了,所以用标题前缀来表明身份)
标题页红点 浏览器标签标题前加 ●
PWA 任务栏徽标 navigator.setAppBadge(n) —— 已安装 PWA 的应用图标角标

只在两个时刻触发,且只在你不在窗口时(document.hidden || !document.hasFocus())—— 你正看着它的时候不会打扰你:

  1. 回复完成 —— 某个会话的 running 由 true 变 false。
  2. 会话在等你 —— 某个会话出现 pendingInteraction:审批、计划审阅或提问。

回到窗口时三处信号立刻清除。子代理会话按设计跳过 —— 它们不该由你回答。

安装

pnpm dsh plugin --profile web add github:renjianbuchai/dsh-session-attention

dsh plugin 是 pnpm 的薄封装,因此任何 pnpm 写法都可用 —— 本地目录 <dir>、 github:<用户>/<仓库>、或将来发布后的 npm 包名。这条命令会同时写入依赖并在 profile 里启用插件,没有别的开关要动。

等本插件进入 awesome-dsh-plugin 精选目录后, 也可以直接在 DSH 插件市场里一键安装。

两个会浪费时间的地方,先说清:

  • 若 pnpm 报 ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION,加上 --config.minimumReleaseAge=0。那是 pnpm 的「发布冷却期」策略,与本插件无关 —— 新分支或新发布可能刚好落在冷却窗口内。
  • 浏览器询问通知权限时请允许。 没授权时标题红点与徽标照常工作,但桌面通知 会按设计保持静默(后台事件里不会去申请权限:浏览器会忽略没有用户手势的权限请求)。

环境要求

  • DSH >= 0.2.1-alpha.1 < 0.3.0-0(在 0.2.1-alpha.1 上开发与验证)
  • Node >= 22.19.0 —— DSH 自身要求 ^22.19.0 || >=24.0.0
  • 浏览器里的 web profile。系统通知需要 Notification;任务栏徽标需要 navigator.setAppBadge(已安装 PWA、Chromium 系)。两者都做了存在性守卫: 缺哪个 API,就那一项静默不生效,其余照常。

实现要点

  • 客户端半体只订阅两个公开的宿主可观察量 —— uiSession.sessionStatus 与 sessions.list —— 由它们推出上面两个边沿;不读别的东西,也不改 DSH 源码。
  • 标题红点由 <title> 上的 MutationObserver 维持。document.title 归 shell 所有 且每次导航都会被重写,所以插件从不抢占所有权:只在仍有未读事件时把标记补回去, 未读数归零时摘掉。
  • 通知按会话固定标签去重(done:<id> / pending:<id>);同标签的旧通知会被显式 关闭并设置 renotify: true,使重复事件重新提醒而不是被浏览器静默替换; 活动的 Notification 对象会保留引用直到关闭。
  • 宿主半体(lib/index.js)只声明插件、不做别的:不写文件、不开端口。

排查入口

插件加载后,浏览器控制台可用:

__DSH_ATTENTION__.permission  // 当前通知权限
__DSH_ATTENTION__.log         // 最近的决策:notified / skipped / failed 及原因
__DSH_ATTENTION__.test()      // 走插件自己的代码路径发一条通知

test() 是区分「浏览器或系统这一侧被挡」与「插件根本没触发」最快的办法 —— 它与真实事件走同一条路径。

验证结果

项目 结果
pnpm typecheck 宿主 + 客户端两侧,均 --noEmit —— 0 错误
pnpm selftest 对构建产物 lib/client.js 的 45 / 45 项断言,既跑本地产物,也跑 DSH web 服务端实际下发的那份字节
pnpm e2e 真实 Chromium + 真实运行的 DSH + 真实模型回合:17 / 17 —— 通知的标题/正文/标签、标题红点、setAppBadge(1)、回窗后的摘红点与 clearAppBadge(),以及"等待期间文档没有被重新加载"(文档若被重载,测试的后台模拟会静默失效,看起来就像插件坏了)

如实说明局限:

  • 桌面气泡由浏览器与操作系统绘制,无头浏览器无法断言 —— 所以 e2e 验证的是 通知以正确参数被构造,而不是 Windows 真的画出了气泡。
  • e2e 里的「后台」是受控的 document.hidden / hasFocus() 模拟,因为无头 Chromium 没有真实的标签焦点。
  • Windows 的专注助手(免打扰)与应用级通知开关都可能在这插件之外压制气泡。 如果 __DSH_ATTENTION__.test() 没冒出气泡、而 __DSH_ATTENTION__.log 显示 notified,那么被挡的是那一侧。

来历

行为移植自 DSH 0.1.x 内置的完成提醒 (packages/client/ui-renderer/src/client/completion-attention.ts 与 use-completion-attention.ts,提交 29d556d7b2、3cb88b4aa5、3e0553ed90), 重做成可安装插件:只用公开平台 API,不补丁渲染器,在 0.2.x 上给出同一套行为。

开发

DSH_HOME=<dsh home> DSH_CHECKOUT=<deepseek-harness checkout> pnpm prepare:links
pnpm typecheck && pnpm build && pnpm selftest

prepare:links 把 profile node_modules 里的 @deepseek-ai/*(以及 checkout vendor/ 下的 cordis 等未发布包)镜像进本项目,供 tsc 读取宿主类型;运行期由 profile 解析这些包,链接只服务类型检查。两个路径都来自环境变量 —— 脚本刻意不写死 任何本机路径。

pnpm e2e 需要一个正在运行的 DSH web 实例,目标由环境变量给出(DSH_URL、 DSH_WORKSPACE、DSH_TASK、PLAYWRIGHT_BROWSERS_PATH)。

许可

MIT —— 见 LICENSE。

—/ 5

No ratings yet

Verified DSH bundle

Commit 6e0c08b71463

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout