dsh-messager 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +18 -11
- package/README.md +22 -7
- package/lib/client.js +46 -9
- package/lib/client.js.map +1 -1
- package/lib/config-route.d.ts +3 -4
- package/lib/config-route.d.ts.map +1 -1
- package/lib/config-route.js +1 -2
- package/lib/config-route.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/settings.d.ts.map +1 -1
- package/lib/settings.js +1 -2
- package/lib/settings.js.map +1 -1
- package/lib/types/client/card-controller.d.ts +2 -4
- package/lib/types/client/card-controller.d.ts.map +1 -1
- package/lib/types/client/diff.d.ts +13 -3
- package/lib/types/client/diff.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/messager-card.d.ts +18 -0
- package/lib/types/client/messager-card.d.ts.map +1 -0
- package/lib/types/client/panel-keys.d.ts +27 -0
- package/lib/types/client/panel-keys.d.ts.map +1 -0
- package/lib/types/client/panel.d.ts +22 -0
- package/lib/types/client/panel.d.ts.map +1 -0
- package/lib/types/client/patch-script.d.ts +9 -0
- package/lib/types/client/patch-script.d.ts.map +1 -0
- package/package.json +212 -33
package/README.en.md
CHANGED
|
@@ -24,14 +24,14 @@ Trigger semantics align with the Web UI status dots: **orange = needs interactio
|
|
|
24
24
|
|
|
25
25
|
**One step for a formal install** (host + browser client both take effect; then just start with `dsh web` — **no** `--patch` needed):
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
``sh
|
|
28
28
|
# Build inside the plugin repo
|
|
29
29
|
pnpm install
|
|
30
30
|
pnpm build
|
|
31
31
|
|
|
32
32
|
dsh plugin --profile web add <plugin-path>
|
|
33
33
|
dsh web # or dsh --profile web
|
|
34
|
-
|
|
34
|
+
``
|
|
35
35
|
|
|
36
36
|
> `pnpm install` and `pnpm build` run inside your plugin checkout; replace `<plugin-path>` with that directory (absolute or relative both work).
|
|
37
37
|
|
|
@@ -50,10 +50,10 @@ After installation, a **「通知&信使」** (Messenger) section appears in the
|
|
|
50
50
|
- **Host side (quick)**: from the DSH repo root run
|
|
51
51
|
`pnpm dsh web --patch <plugin-path>/cordis.yml` — loads the TS source directly (HMR works). In source mode the host runs via tsx, so no build is needed to load that path.
|
|
52
52
|
- **Full dual-runtime**: the browser (client) side requires the plugin to enter the Loader as a package for clientModules to scan it into the Web bundle (`--patch` file-path entries are not scanned), so do a full install into the profile:
|
|
53
|
-
|
|
53
|
+
``sh
|
|
54
54
|
dsh plugin --profile web add <plugin-path> # source mode: pnpm dsh plugin ...
|
|
55
55
|
pnpm dsh web # run from the DSH source repo
|
|
56
|
-
|
|
56
|
+
``
|
|
57
57
|
**Restart** `pnpm dsh web` after `plugin add` (clientModules scans at startup; a running instance does not hot-add new bundles). After changing client code, re-run `pnpm run build:client` in your own repo and refresh the page (the bundle carries a rev hash so it is re-fetched; the DSH repo's `dev:web` watcher only watches in-workspace client plugins, not external ones).
|
|
58
58
|
|
|
59
59
|
Browser notifications require user permission: the plugin requests it once on load when the permission is `default`; if denied, the browser channel degrades silently (other channels are unaffected) — re-authorize in the browser's site settings.
|
|
@@ -130,16 +130,16 @@ Verbosity: `minimal` = title only; `normal` adds session title/tool name/end rea
|
|
|
130
130
|
|
|
131
131
|
Add a third-party channel (DingTalk / WeCom / Telegram…) by implementing the `NotifyChannel` interface and registering it in `buildChannels()` in `src/index.ts`:
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
``ts
|
|
134
134
|
export interface NotifyChannel {
|
|
135
135
|
readonly id: string
|
|
136
136
|
send(payload: NotificationPayload): Promise<void>
|
|
137
137
|
}
|
|
138
|
-
|
|
138
|
+
``
|
|
139
139
|
|
|
140
140
|
## Project structure
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
``
|
|
143
143
|
dsh-messager/
|
|
144
144
|
├── package.json # dsh.bundle + dsh.client dual declarations; exports["./client"]
|
|
145
145
|
├── tsconfig.json # host side (Node)
|
|
@@ -168,16 +168,23 @@ dsh-messager/
|
|
|
168
168
|
│ ├── config.ts # browser-notification config handle (via the config route)
|
|
169
169
|
│ └── diff.ts # session summary diff (pure functions)
|
|
170
170
|
└── tests/ # vitest unit tests (126)
|
|
171
|
-
|
|
171
|
+
``
|
|
172
172
|
|
|
173
173
|
## Testing
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
``sh
|
|
176
176
|
pnpm test # 126 unit tests: signal extraction/templates/dispatch/channel payloads & signatures/config parsing/client diff/config route/fetch scope/locale consistency/form gating
|
|
177
177
|
pnpm typecheck # host side
|
|
178
178
|
pnpm build # host tsc + client declarations + client bundle (lib/)
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
``
|
|
180
|
+
|
|
181
|
+
## Version compatibility (dsh-messager 0.3.0 / DSH 0.1.2-alpha.5)
|
|
182
|
+
|
|
183
|
+
- **v0.3.0 supports only DSH `0.1.2-alpha.5`**; all `@deepseek-ai/dsh-*` peerDependencies are pinned to that version.
|
|
184
|
+
- Compatibility notes:
|
|
185
|
+
- The removed `dsh-client-runtime` is no longer used. Client session lists use `dsh-api-session-controller`, interaction state uses `dsh-client-ui-session`, and slot services use `dsh-client-ui-renderer`.
|
|
186
|
+
- The `messager` settings namespace is registered from the Loader config and can also be read/written through the plugin's own `/dsh-messager/config` route, which is not subject to DSH's settings namespace allowlist.
|
|
187
|
+
|
|
181
188
|
## Known limitations
|
|
182
189
|
|
|
183
190
|
- Browser notifications require site permission; with `onlyWhenHidden=false` they also pop while visible.
|
package/README.md
CHANGED
|
@@ -13,11 +13,11 @@ DeepSeek Harness(DSH)**任务状态通知插件**:会话需要交互、任
|
|
|
13
13
|
|
|
14
14
|
| 需求 | 实现 |
|
|
15
15
|
| --- | --- |
|
|
16
|
-
| 触发时机 | 需要交互(审批 `approval/asked`、提问/计划待审 `ask_user_question`、客户端
|
|
16
|
+
| 触发时机 | 需要交互(审批 `approval/asked`、提问/计划待审 `ask_user_question`、客户端 `uiSession.pendingInteractions`)、任务完成(`agent/status` running→idle 且仅根会话 + `turn/end` 原因)、任务出错(`agent/error`) |
|
|
17
17
|
| 推送路径 | 系统通知(node-notifier toast)、浏览器通知(Notification API)、飞书(interactive 卡片 + HMAC-SHA256 签名)、企业微信(markdown + 可选加签)、Discord(embed 卡片)、钉钉(actionCard + 可选加签)、Telegram(Bot API HTML 消息);`NotifyChannel` 接口可扩展 |
|
|
18
18
|
| 可配置 | 触发开关、各通道启停/verbosity/icon、去重冷却、标题前缀等,见[配置](#配置) |
|
|
19
19
|
|
|
20
|
-
触发语义与 Web UI 状态圆点完全对齐:**橙点 = 需要交互**(`
|
|
20
|
+
触发语义与 Web UI 状态圆点完全对齐:**橙点 = 需要交互**(`uiSession.pendingInteractions`),**绿点 = 任务完成**
|
|
21
21
|
(`running→idle` 且非当前会话),**蓝点 = 运行中**(不通知)。
|
|
22
22
|
|
|
23
23
|
## 安装
|
|
@@ -151,8 +151,8 @@ host 端把 Loader config 注册为 settings 命名空间 `messager` 的 base
|
|
|
151
151
|
|
|
152
152
|
| 触发 | host 端(system/feishu/wecom/discord/dingtalk/telegram) | client 端(browser) |
|
|
153
153
|
| --- | --- | --- |
|
|
154
|
-
| 审批 | `session/event` `approval/asked` |
|
|
155
|
-
| 提问/计划待审 | `session/event` `tool/call`(`ask_user_question`) | `
|
|
154
|
+
| 审批 | `session/event` `approval/asked` | `ctx.uiSession.pendingInteractions` 中 `kind==='approval'` 从无到有 |
|
|
155
|
+
| 提问/计划待审 | `session/event` `tool/call`(`ask_user_question`) | `ctx.uiSession.pendingInteractions` 中 `kind==='question'/'plan-review'` 从无到有 |
|
|
156
156
|
| 任务完成 | `agent/status` running→idle(仅根会话)+`turn/end` 原因 | 摘要 `running:true→false` 且非当前会话 |
|
|
157
157
|
| 任务出错 | `agent/error` | -(host 端覆盖) |
|
|
158
158
|
|
|
@@ -179,6 +179,7 @@ dsh-messager/
|
|
|
179
179
|
├── cordis.yml # 本地开发覆盖层(host 端)
|
|
180
180
|
├── cordis.patch.yml # 分发包配置层(安装后生效)
|
|
181
181
|
├── assets/icon.png # 默认通知图标
|
|
182
|
+
├── doc/plan/ # 规划存档(01 起编号)
|
|
182
183
|
├── src/
|
|
183
184
|
│ ├── index.ts # host apply:事件接线 + settings 注册 + 通道构建 + 路由挂载
|
|
184
185
|
│ ├── config.ts # Config schema(Loader config 与 settings 共用)
|
|
@@ -197,18 +198,30 @@ dsh-messager/
|
|
|
197
198
|
│ ├── fetch-scope.ts # ScopeLike 的 fetch 适配层(配置路由)
|
|
198
199
|
│ ├── locales.ts # zh/en 字典(ctx.locale 注册)
|
|
199
200
|
│ ├── config.ts # 浏览器通知的配置句柄(走配置路由)
|
|
200
|
-
│ └── diff.ts #
|
|
201
|
-
└── tests/ # vitest 单元测试(
|
|
201
|
+
│ └── diff.ts # 完成摘要 / 待交互状态 diff(纯函数)
|
|
202
|
+
└── tests/ # vitest 单元测试(129 个)
|
|
202
203
|
```
|
|
203
204
|
|
|
204
205
|
## 测试
|
|
205
206
|
|
|
206
207
|
```sh
|
|
207
|
-
pnpm test #
|
|
208
|
+
pnpm test # 129 个单元测试:信号提取/模板/调度/各通道签名与载荷/配置解析/client diff/配置路由/fetch scope/字典一致性/表单门控
|
|
208
209
|
pnpm typecheck # host 端
|
|
209
210
|
pnpm build # host tsc + client 声明 + client bundle(lib/)
|
|
210
211
|
```
|
|
211
212
|
|
|
213
|
+
## 版本兼容(dsh-messager 0.3.0 / DSH 0.1.2-alpha.5)
|
|
214
|
+
|
|
215
|
+
- **v0.3.0 仅支持 DSH `0.1.2-alpha.5`**;所有 `@deepseek-ai/dsh-*` peerDependencies
|
|
216
|
+
统一锁定该版本,不再兼容旧 RC 接口。
|
|
217
|
+
- client 端适配新版拆分:会话列表来自 `dsh-api-session-controller`,交互状态来自
|
|
218
|
+
`dsh-client-ui-session` 的 `ctx.uiSession.pendingInteractions`,`ctx.slots` 由
|
|
219
|
+
`dsh-client-ui-renderer` 提供;不再依赖已移除的 `dsh-client-runtime`。
|
|
220
|
+
- 完成通知仍按会话摘要 `running: true → false` 且非当前会话触发;交互通知仅在
|
|
221
|
+
`approval` / `question` / `plan-review` 从无到有时触发,首次订阅只建立基线。
|
|
222
|
+
- host 设置使用字符串命名空间 `messager`;配置读写继续走插件自有 webServer 路由
|
|
223
|
+
`/dsh-messager/config`,现有 schema、settings 数据和第三方通道配置无需迁移或重置。
|
|
224
|
+
|
|
212
225
|
## 已知边界
|
|
213
226
|
|
|
214
227
|
- 浏览器通知需站点权限;`onlyWhenHidden=false` 时页面可见也会弹。
|
|
@@ -238,3 +251,5 @@ pnpm build # host tsc + client 声明 + client bundle(lib/)
|
|
|
238
251
|
- 第三方通道扩展:邮件
|
|
239
252
|
- 触发扩展:后台 job 完成、goal 轮次完成
|
|
240
253
|
- 通知历史、按会话静音、勿扰时段
|
|
254
|
+
|
|
255
|
+
规划存档见 `doc/plan/01-通知插件实施规划.md`。
|
package/lib/client.js
CHANGED
|
@@ -5,7 +5,7 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
-
//#region node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.
|
|
8
|
+
//#region node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.3/node_modules/@deepseek-ai/cosmokit/lib/index.js
|
|
9
9
|
/** Return true when a value is `null` or `undefined`. */
|
|
10
10
|
function isNullable(value) {
|
|
11
11
|
return value === null || value === void 0;
|
|
@@ -201,7 +201,7 @@ window.__ModuleLoader__.load({
|
|
|
201
201
|
Time.template = template;
|
|
202
202
|
})(Time || (Time = {}));
|
|
203
203
|
//#endregion
|
|
204
|
-
//#region node_modules/.pnpm/@deepseek-ai+schemastery@3.18.
|
|
204
|
+
//#region node_modules/.pnpm/@deepseek-ai+schemastery@3.18.2/node_modules/@deepseek-ai/schemastery/lib/index.mjs
|
|
205
205
|
const kSchema = Symbol.for("schemastery");
|
|
206
206
|
const kValidationError = Symbol.for("ValidationError");
|
|
207
207
|
globalThis.__schemastery_index__ ??= 0;
|
|
@@ -935,12 +935,6 @@ window.__ModuleLoader__.load({
|
|
|
935
935
|
if (summary === void 0) continue;
|
|
936
936
|
const before = previous[id];
|
|
937
937
|
if (before === void 0) continue;
|
|
938
|
-
if (summary.pendingInteraction !== void 0 && before.pendingInteraction === void 0) notices.push({
|
|
939
|
-
kind: "interaction",
|
|
940
|
-
sessionId: id,
|
|
941
|
-
interaction: summary.pendingInteraction,
|
|
942
|
-
title: summary.displayTitle
|
|
943
|
-
});
|
|
944
938
|
if (before.running && !summary.running && id !== current) notices.push({
|
|
945
939
|
kind: "completed",
|
|
946
940
|
sessionId: id,
|
|
@@ -949,6 +943,35 @@ window.__ModuleLoader__.load({
|
|
|
949
943
|
}
|
|
950
944
|
return notices;
|
|
951
945
|
}
|
|
946
|
+
/** 仅把 DSH Web UI 有专用展示语义的待交互 kind 映射为通知类型。 */
|
|
947
|
+
function clientInteractionKindOf(kind) {
|
|
948
|
+
switch (kind) {
|
|
949
|
+
case "approval":
|
|
950
|
+
case "plan-review":
|
|
951
|
+
case "question": return kind;
|
|
952
|
+
default: return;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* 对比两次 uiSession 待交互快照。
|
|
957
|
+
* 仅从无到有时通知;首次订阅由调用方把当前快照设为 previous,因此不会补发历史通知。
|
|
958
|
+
*/
|
|
959
|
+
function diffPendingInteractions(previous, next, summaries) {
|
|
960
|
+
const notices = [];
|
|
961
|
+
for (const [sessionId, pending] of next) {
|
|
962
|
+
if (previous.has(sessionId)) continue;
|
|
963
|
+
const interaction = clientInteractionKindOf(pending.kind);
|
|
964
|
+
if (interaction === void 0) continue;
|
|
965
|
+
const title = summaries[sessionId]?.displayTitle;
|
|
966
|
+
notices.push({
|
|
967
|
+
kind: "interaction",
|
|
968
|
+
sessionId,
|
|
969
|
+
interaction,
|
|
970
|
+
...title === void 0 ? {} : { title }
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
return notices;
|
|
974
|
+
}
|
|
952
975
|
//#endregion
|
|
953
976
|
//#region src/client/card-controller.ts
|
|
954
977
|
/** 字段键:`group.field`。 */
|
|
@@ -2182,6 +2205,7 @@ window.__ModuleLoader__.load({
|
|
|
2182
2205
|
/** 依赖的客户端服务:会话列表、远程事件(document-updated)、槽位、locale。 */
|
|
2183
2206
|
const inject = [
|
|
2184
2207
|
"sessions",
|
|
2208
|
+
"uiSession",
|
|
2185
2209
|
"remote",
|
|
2186
2210
|
"slots",
|
|
2187
2211
|
"locale"
|
|
@@ -2201,12 +2225,18 @@ window.__ModuleLoader__.load({
|
|
|
2201
2225
|
}
|
|
2202
2226
|
/** 列表快照变化 → 通知。 */
|
|
2203
2227
|
onListChange(previous, next) {
|
|
2228
|
+
this.onNotices(diffSessionSummaries(previous.byId, next.byId, next.current));
|
|
2229
|
+
}
|
|
2230
|
+
/** 待交互快照变化 → 通知。 */
|
|
2231
|
+
onPendingChange(previous, next, sessions) {
|
|
2232
|
+
this.onNotices(diffPendingInteractions(previous, next, sessions.byId));
|
|
2233
|
+
}
|
|
2234
|
+
onNotices(notices) {
|
|
2204
2235
|
const config = this.config.get();
|
|
2205
2236
|
if (!config.browser.enabled) return;
|
|
2206
2237
|
if (typeof Notification === "undefined") return;
|
|
2207
2238
|
if (Notification.permission !== "granted") return;
|
|
2208
2239
|
if (config.browser.onlyWhenHidden && document.visibilityState !== "hidden") return;
|
|
2209
|
-
const notices = diffSessionSummaries(previous.byId, next.byId, next.current);
|
|
2210
2240
|
for (const notice of notices) {
|
|
2211
2241
|
if (!this.allow(notice, config)) continue;
|
|
2212
2242
|
this.show(notice, config);
|
|
@@ -2299,6 +2329,13 @@ window.__ModuleLoader__.load({
|
|
|
2299
2329
|
previous = next;
|
|
2300
2330
|
});
|
|
2301
2331
|
ctx.effect(() => () => offList(), "dsh-messager: sessions subscription");
|
|
2332
|
+
let previousPending = ctx.uiSession.pendingInteractions.getSnapshot();
|
|
2333
|
+
const offPending = ctx.uiSession.pendingInteractions.subscribe(() => {
|
|
2334
|
+
const next = ctx.uiSession.pendingInteractions.getSnapshot();
|
|
2335
|
+
notifier.onPendingChange(previousPending, next, ctx.sessions.list.getSnapshot());
|
|
2336
|
+
previousPending = next;
|
|
2337
|
+
});
|
|
2338
|
+
ctx.effect(() => () => offPending(), "dsh-messager: pending interactions subscription");
|
|
2302
2339
|
const controller = new MessagerCardController(fetchScope.scope, CARD_FIELDS);
|
|
2303
2340
|
ctx.effect(() => ctx.locale.register(LOCALE_NS, {
|
|
2304
2341
|
zh,
|