dsh-live-notify
停靠在 DSH Web UI 输入框上方的实时进度窗,外加一个给模型用的 UpdateNotify 工具。
中文 | English
模型在干活的过程中随时播报「刚刚发生了什么、发现了什么、在等什么」,你不需要等它 把整轮跑完才知道它卡在哪。
它解决什么问题
待办清单和进度播报回答的是两个不同的问题,所以这个插件是在 todo 工具旁边再加一条 通道,而不是把 todo 做大:
TodoList(todo_write 之类) |
UpdateNotify(本插件) | |
|---|---|---|
| 本质 | 结构化任务清单 | 叙事式进度播报 |
| 回答 | "还剩哪些事、做到哪了" | "刚刚发生了什么、发现了什么、在等什么" |
| 数据 | 一组 {content, status},整表替换 |
一条 title + 一段轻 Markdown message |
| 生命周期 | 跨回合持久的计划 | 回合内的流式更新,新回合清空 |
| 适合 | 开工前列计划、跟踪完成度 | 多步任务中报阶段结论、长耗时等待、阻塞、方向调整 |
| 信息量 | 短、逐项 | 可带证据、文件路径、报错片段(~1000 字符) |
| 未使用时的风险 | 用户不知道你当前在做什么 | 计划无法表达"为什么"和"发现了什么" |
简单说:TodoList 是计划与完成度,UpdateNotify 是过程与结论。
前置条件
- 一个带插件机制的 DSH(Harness)宿主,且宿主已提供
@deepseek-ai/dsh-client-ui-conversation(本插件的面板挂在它提供的conversation.input.dock槽位上)。 - 已在 DSH
0.2.1-rc.5上验证,package.json的engines.dsh声明了兼容的宿主版本范围。 - 插件本身零运行时依赖:
dependencies是空的,装插件不会往 profile 里拖任何包。 - 只有在改这个仓库时才需要 Node.js ≥ 22 和 Bun(Bun 负责装依赖、跑
lint/format 和打包;测试用 Node 内置的
node:test)。
面板位置和可用性取决于宿主版本:宿主没有该槽位时,工具与投影照常工作,只是没有面板。
安装
本包发布到 npm,同时以 tarball 形式可用:
# 方式一:直接按包名安装到当前 DSH profile
# 在 Harness 的插件管理里执行 install_bundle,target 用包名 dsh-live-notify
# 方式二:离线 / 指定版本,用打包产物安装
bun pm pack # 或 npm pack;得到 dsh-live-notify-<version>.tgz
# 再执行 install_bundle,target 用该 tarball 的绝对路径
安装会写进 $DSH_PROFILE_DIR/node_modules/dsh-live-notify。
升级时的注意点:Client 半(client.js)随页面刷新就会换新;Host 半(host.js)
是已加载的 JS 模块,改完必须重启一次 dsh web 才会生效。重启不会丢会话历史——
投影是按 stateVersion 从会话日志重新折叠出来的。
使用
模型侧只有两条参数:
title(可选):单行纯文本,建议 ≲60 字符,作为结论或当前阶段独立可读。message(必填):轻 Markdown,开头 2–4 句散文讲证据与推理,细节用要点 / 短段落 / key-value 行(可带文件路径、报错片段),建议 ≲1000 字符。
宿主侧的限制:title 硬上限 120 字符(超出截断加省略号),message 硬上限 4000 字符,
面板保留本回合最近 12 条。参数校验与工具描述一致(title 不许换行且不许为空,
message 不许为空)。
你不需要手动做什么:系统提示里有一段 guidance 要求模型主动播报,并且连续 8 个工具 调用轮次没有更新时会通过动态 runtime context 提醒模型补一条。
设计要点
- 结果确认后才上屏:
tool/call先落到 projection 的pending,只有配对的tool/result且不是错误时才提交到面板。模型谎报「已通知」也骗不到面板。 - 不新增会话事件类型(这是本插件最要紧的一条约束):进度已经天然存在于普通
tool/call/tool/result事件里,投影只是对既有事件的纯折叠。会话日志仍是唯一 事实来源,所以恢复、分叉、回放都免费可用;也不会出现「打开一个含未知事件类型的旧 会话就报错」这类问题——Session.append无法把仓库外的事件类型标记为ignorable。 - 回合内保留、结束后仍可见:新回合开始才清空,上一回合的结论留到你读完。
- 只用宿主主题 token:CSS 只消费
--dsw-alias-*/--dsh-*变量,不写死颜色, 跟随宿主的深色 / 浅色主题。 - 可访问:键盘可展开,
aria-live="polite"播报新标题,尊重prefers-reduced-motion。
实现
- Host 半(
host.js):注册UpdateNotify工具 +liveNotify会话投影 (stateVersion: 3)+ 系统提示 section + 每 agent 的轮次计数器(agent/created/agent/session/event)驱动的 8 轮提醒。 - Client 半(
client.js):在conversation.input.dock注册一条 entry,通过槽位标准 的useProjection(PROJECTION_KEY)读取宿主机算好的值,客户端不做任何会话折叠;zh/en双语。
文件
| 文件 | 用途 |
|---|---|
host.js |
Host:UpdateNotify 工具 + liveNotify 投影 + 提示/nudge |
client.js |
Client:输入框上方的实时进度窗 |
cordis.patch.yml |
Loader 行(id: live-notify) |
package.json |
bundle manifest + dsh.client 声明 |
locale/*.json |
插件管理页显示文案 |
icon.svg |
插件管理页图标 |
test/*.test.js |
node --test 测试(host 折叠 / 工具契约 / 打包与客户端不变量) |
.oxfmtrc.json |
oxfmt 配置(Markdown 交给 .prettierignore 排除,CJK 段落手工折行) |
开发
包管理器是 Bun(bun.lock 已提交);测试跑在 Node 内置的
node:test 上。
bun install
bun run test # node --test test/*.test.js
bun run lint # oxlint .
bun run fmt # oxfmt .(格式化;bun run fmt:check 只检查)
bun run check # 语法检查 host.js / client.js
bun pm pack # 打包产物
bun publish # 发布到 npm(需要先 bunx npm login)
测试不需要宿主:apply() 通过一个 stub registrant context 驱动,逐条事件重放投影;
另外一组静态检查盯着打包清单、loader id、双语词表对齐、主题 token 与「模型输出不走
HTML 注入」这类不变量。CI 在 Node 22 / 24 上跑 check → lint → fmt:check → test → pack。
安全
模型输出按纯文本处理:轻 Markdown 渲染器把段落、要点、行内代码渲染成 React 元素,
不用 dangerouslySetInnerHTML,也不解释 HTML。面板不发起网络请求,不读本地文件;
数据来源就是会话日志本身。
许可证
Mozilla Public License 2.0(MPL-2.0)。
MPL 是文件级 copyleft:可以自由使用、修改、再分发,包括商用与闭源组合;但对
本仓库源文件的修改,在分发时需要同样以 MPL-2.0 提供对应源文件。npm 包内已包含全部
源码(host.js / client.js),因此「源码可得性」这一条随之满足。
English
A live progress panel docked above the composer in the DSH Web UI, plus an
UpdateNotify tool for the model.
While a task runs, the model narrates what it just did, what it found, and what it is waiting on — so you are not left guessing until the whole turn finishes.
Why not just a todo list
A todo list is a durable, structured plan: what is left. This is a narrative progress channel: what just happened, what was learned, what is being waited on. They are complementary, so the plugin sits next to a todo tool instead of replacing it. Todos persist across turns; progress updates live inside a turn and stay on screen after it ends.
Requirements
- A DSH (Harness) host with the plugin mechanism, providing
@deepseek-ai/dsh-client-ui-conversation— the panel is registered on theconversation.input.dockslot that package contributes. Without that slot the tool and the projection still work, there is just no panel. - Verified on DSH
0.2.1-rc.5;package.jsondeclares the compatible host range inengines.dsh. - The plugin itself has no runtime dependencies (
dependenciesis empty), so installing it pulls nothing extra into your profile. - Working on this repo needs Node.js ≥ 22 and Bun; Bun installs
dev dependencies, runs lint/format and packs, while tests run on Node's built-in
node:test.
Install
Published to npm as dsh-live-notify:
# install_bundle with target: dsh-live-notify (in the Harness plugin manager)
# or, from a packed tarball:
bun pm pack # or npm pack
# install_bundle with target: /abs/path/to/dsh-live-notify-<version>.tgz
The bundle lands in $DSH_PROFILE_DIR/node_modules/dsh-live-notify.
After upgrading: the client half reloads on page refresh, but the host half is
a loaded JS module — restart dsh web once to pick up a new host.js. Session
history survives the restart; the projection is refolded from the log.
Usage
The model calls UpdateNotify with an optional single-line title (roughly ≤60
characters, plain text, no trailing period) and a required light-Markdown
message (a short paragraph of evidence and reasoning, then a few bullets or
key-value lines, roughly ≤1000 characters). Hard limits on the host side: 120
characters for the title, 4000 for the message, and the newest 12 updates per
turn are kept.
System-prompt guidance asks the model to keep you posted, and after eight tool rounds without an update a runtime-context reminder nudges it to send one.
Design notes
- Result-gated: a
tool/callonly becomes a panel row once its pairedtool/resultarrives and is not an error. - No new session event type: the projection is a pure fold over ordinary
tool/call/tool/result/turn/start/turn/endevents, so the session log stays the single source of truth and resume, fork and replay keep working. (An out-of-repo event type cannot be markedignorablebySession.append, so a session containing one could not be reopened.) - Host theme tokens only, so the panel follows the host's dark and light
themes; keyboard expandable,
aria-live="polite", honoursprefers-reduced-motion.
Development
bun install
bun run test # node --test test/*.test.js
bun run lint # oxlint .
bun run fmt # oxfmt . (fmt:check verifies only)
bun run check # syntax check for host.js / client.js
bun pm pack # tarball
bun publish # publish to npm (log in with bunx npm login first)
The tests need no host: apply() is driven through a stub registrant context that
replays projections event by event, and a second group of static checks guards the
packaging manifest, the loader id, locale key parity, theme tokens, and the "model
output never arrives as HTML" invariant. CI runs check → lint → fmt:check → test →
pack on Node 22 and 24.
License
MPL-2.0. File-level copyleft: use, modify and redistribute freely, including commercially; modifications to these source files must be made available under MPL-2.0 when distributed. All source is shipped inside the npm package, which satisfies the source-availability requirement.
No comments yet. Be the first to write one.