@twinsearth/dsh-master 3.2.8 → 3.2.9

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.md CHANGED
@@ -1,9 +1,12 @@
1
1
  # dsh-Master(房主)
2
2
 
3
3
  > 与瓮城 **dsh-Fang** 并行的主 Agent 插件。
4
- > 当前版本:`3.2.8`(审计修复发布线;tag / GitHub Release / NPM public)。
4
+ > 当前版本:`3.2.9`(面板交互修复线;tag / GitHub Release / NPM public)。
5
5
  > 仓库:https://github.com/TwinsEarth/dsh-Master
6
6
 
7
+ [![DSH 0.2.2-rc.1](https://img.shields.io/badge/DSH-0.2.2--rc.1-4D6BFE?style=flat)](https://github.com/deepseek-ai/deepseek-harness)
8
+ [![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-ai/deepseek-harness)
9
+
7
10
  ## 这是什么
8
11
 
9
12
  dsh-Master 不接管瓮城的「母体 / 原身 / 分身」生命周期;它是一块**并列的主插件**,
@@ -84,7 +87,8 @@ dsh plugin add <package> # 加载 dist-host/dist-worker
84
87
 
85
88
  ## 状态
86
89
 
90
+ - 3.2.9 发布线:房主面板改注册到 `shell.overlay` 常驻浮层——点击面板之外不收起、侧栏「房主」按钮二次点击收起、正文独立滚动;新增无浏览器 hook 运行时行为测试(开关、无 outside-click 监听、滚动容器、SSE 失败不卸载面板)。
87
91
  - 3.2.8 发布线:与瓮城通过 fang-master contract 1.0 互联(local/http/file/legacy 四级保底);GitHub Release + NPM public。
88
92
  - 当前工作树完成新一轮审计修复:工具名/死循环阈值、Router 鉴权与生命周期、真实路由分流、契约幂等与超时、PR 护栏与交付、飞书加密、长 Shell 后台作业、成本实测接线、PJM 派生契约。
89
- - 验收:`npm run check` / `npm run build` / `npm test` 全绿;真实双 Host/fork 落库、工具写读、SSE、回写、配额与桥清理断言通过。详见复盘文档。
90
- - 后续修复尚未发布,也未安装到用户主 profile;发布仍需明确指令。
93
+ - 验收:`npm run check` / `npm run build` / `npm test` 全绿(188/188);真实双 Host/fork 落库、工具写读、SSE、回写、配额与桥清理断言通过。详见复盘文档。
94
+ - 发布不等于安装:本流程不修改任何用户 profile(当前 desktop profile 仍为 3.2.8,升级需另行授权)。
package/client.js CHANGED
@@ -2,11 +2,18 @@
2
2
  // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
3
 
4
4
  /**
5
- * Dsh-Master(房主)Web Client:侧栏「房主」入口 + 只读状态抽屉。
5
+ * Dsh-Master(房主)Web Client:侧栏「房主」入口 + 只读状态面板。
6
6
  *
7
7
  * 只做观察面板(3.2.0 架构骨架):展示角色、三个子插件的激活状态、与瓮城的
8
8
  * 服务协作状态。数据来自 core 的只读路由 `/dsh-master/api/status`。
9
9
  * 不写任何业务状态,不接管宿主根节点。
10
+ *
11
+ * 面板与按钮分离(3.2.9):按钮注册在 `sidebar.footer.action`,面板注册在
12
+ * `shell.overlay`(DSH 的整框浮层,位于所有列与滚动容器之外,且本身
13
+ * click-through)。两者通过模块级 store 共享「是否展开」:
14
+ * - 点击页面其他位置不会关闭面板(浮层没有 outside-click 收回逻辑);
15
+ * - 再次点击「房主」按钮由 `togglePanel()` 收起;
16
+ * - 面板固定 viewport 高度,正文 `overflow-y: auto`,长内容可滚动查看。
10
17
  */
11
18
  window.__ModuleLoader__.load({
12
19
  id: '@twinsearth/dsh-master',
@@ -21,12 +28,22 @@ window.__ModuleLoader__.load({
21
28
  boxShadow: '-16px 0 40px rgba(16,17,20,.16)', display: 'flex', flexDirection: 'column',
22
29
  fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI","Microsoft YaHei",sans-serif',
23
30
  fontSize: 13, color: '#1D1D1F',
31
+ // shell.overlay 整层是 click-through:面板自己opt-in接收指针事件,
32
+ // 面板之外的点击穿透到下面的应用,不会触发任何收回。
33
+ pointerEvents: 'auto',
34
+ },
35
+ head: {
36
+ flex: '0 0 auto', display: 'flex', alignItems: 'center', gap: 8, padding: '14px 16px',
37
+ borderBottom: '1px solid rgba(16,17,20,.08)',
24
38
  },
25
- head: { display: 'flex', alignItems: 'center', gap: 8, padding: '14px 16px', borderBottom: '1px solid rgba(16,17,20,.08)' },
26
39
  title: { fontSize: 14, fontWeight: 700 },
27
40
  sub: { fontSize: 11.5, color: '#86868B' },
28
41
  close: { marginLeft: 'auto', width: 28, height: 28, borderRadius: 8, border: '1px solid rgba(16,17,20,.12)', background: '#fff', color: '#5F6368', cursor: 'pointer' },
29
- body: { flex: 1, overflowY: 'auto', padding: '12px 14px 18px', display: 'flex', flexDirection: 'column', gap: 10 },
42
+ body: {
43
+ flex: '1 1 auto', minHeight: 0, overflowY: 'auto', overscrollBehavior: 'contain',
44
+ scrollbarGutter: 'stable', scrollbarWidth: 'thin',
45
+ padding: '12px 14px 18px', display: 'flex', flexDirection: 'column', gap: 10,
46
+ },
30
47
  card: { border: '1px solid rgba(16,17,20,.1)', borderRadius: 12, padding: '10px 11px', background: '#FAFAFC', display: 'flex', flexDirection: 'column', gap: 4 },
31
48
  row: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' },
32
49
  name: { fontSize: 13, fontWeight: 600 },
@@ -40,7 +57,37 @@ window.__ModuleLoader__.load({
40
57
  borderRadius: 8, border: '1px solid rgba(16,17,20,.12)', background: '#fff', color: '#1D1D1F',
41
58
  cursor: 'pointer', fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
42
59
  },
43
- banner: { position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, zIndex: 2147482799, background: 'rgba(18,19,22,.18)' },
60
+ fabOpen: {
61
+ display: 'inline-flex', alignItems: 'center', gap: 6, height: 28, padding: '0 9px',
62
+ borderRadius: 8, border: '1px solid rgba(16,17,20,.24)', background: '#F2F2F5', color: '#1D1D1F',
63
+ cursor: 'pointer', fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
64
+ },
65
+ }
66
+
67
+ /**
68
+ * 面板展开状态:按钮(sidebar.footer.action)与面板(shell.overlay)是两个
69
+ * 独立注册的组件,状态必须放在模块级 store 里,不能放在任一组件内部。
70
+ */
71
+ const panelState = { open: false }
72
+ const panelListeners = new Set()
73
+ const getPanelOpen = () => panelState.open
74
+ const setPanelOpen = (next) => {
75
+ const value = Boolean(next)
76
+ if (panelState.open === value) return
77
+ panelState.open = value
78
+ for (const listener of panelListeners) {
79
+ try { listener() } catch { /* 订阅者异常不影响其他订阅者 */ }
80
+ }
81
+ }
82
+ const togglePanel = () => setPanelOpen(!panelState.open)
83
+ const subscribePanel = (listener) => {
84
+ panelListeners.add(listener)
85
+ return () => { panelListeners.delete(listener) }
86
+ }
87
+ function usePanelOpen() {
88
+ const [open, setOpen] = React.useState(getPanelOpen)
89
+ React.useEffect(() => subscribePanel(() => setOpen(getPanelOpen())), [])
90
+ return open
44
91
  }
45
92
 
46
93
  const tag = (text, color) => h('span', { style: { ...S.tag, ...(color ? { color, background: 'rgba(16,17,20,.05)' } : {}) } }, text)
@@ -194,7 +241,7 @@ window.__ModuleLoader__.load({
194
241
  )
195
242
  }
196
243
 
197
- function MasterPanel({ onClose }) {
244
+ function MasterPanel() {
198
245
  const [status, setStatus] = React.useState(null)
199
246
  const [error, setError] = React.useState('')
200
247
 
@@ -217,35 +264,51 @@ window.__ModuleLoader__.load({
217
264
  }, [])
218
265
 
219
266
  const items = status && Array.isArray(status.subplugins) ? status.subplugins : []
220
- return h('div', null,
221
- h('div', { style: S.fab, role: 'presentation' }),
222
- h('aside', { style: S.panel, 'aria-label': '房主状态' },
223
- h('div', { style: S.head },
224
- h('span', { style: S.title }, '房主 · dsh-Master'),
225
- h('span', { style: S.sub }, status ? `${status.version} · ${status.role}` : '读取中'),
226
- h('button', { type: 'button', style: S.close, title: '关闭', onClick: onClose }, '×')),
227
- h('div', { style: S.body },
228
- error ? h('div', { style: S.error }, error) : null,
229
- status ? h('div', { style: S.card },
230
- h('div', { style: S.row }, h('span', { style: S.name }, '主 Agent 控制面'), tag('与瓮城并行')),
231
- h('div', { style: S.desc }, '角色:' + status.role + ' · 版本:' + status.version),
232
- h('div', { style: S.desc }, '运行:' + Math.max(1, Math.round((status.uptimeMs || 0) / 60000)) + ' 分钟'),
233
- ) : null,
234
- ...items.map((item) => h(SubpluginCard, { key: item.id, item })),
235
- h(FangCard, { cooperation: status && status.cooperation }),
236
- h(LiveFeed),
237
- h('div', { style: S.desc }, '3.2.7:真实流式(SSE)+ token 轮换/审计 + Worker 并发配额 + 宿主 adapter;未发布。'),
238
- )),
239
- )
267
+ return h('aside', { style: S.panel, id: 'dsh-master-panel', 'aria-label': '房主状态' },
268
+ h('div', { style: S.head },
269
+ h('span', { style: S.title }, '房主 · dsh-Master'),
270
+ h('span', { style: S.sub }, status ? `${status.version} · ${status.role}` : '读取中'),
271
+ h('button', {
272
+ type: 'button',
273
+ style: S.close,
274
+ title: '收起面板',
275
+ 'aria-label': '收起房主面板',
276
+ onClick: () => setPanelOpen(false),
277
+ }, '×')),
278
+ h('div', { style: S.body },
279
+ error ? h('div', { style: S.error }, error) : null,
280
+ status ? h('div', { style: S.card },
281
+ h('div', { style: S.row }, h('span', { style: S.name }, '主 Agent 控制面'), tag('与瓮城并行')),
282
+ h('div', { style: S.desc }, '角色:' + status.role + ' · 版本:' + status.version),
283
+ h('div', { style: S.desc }, '运行:' + Math.max(1, Math.round((status.uptimeMs || 0) / 60000)) + ' 分钟'),
284
+ ) : null,
285
+ ...items.map((item) => h(SubpluginCard, { key: item.id, item })),
286
+ h(FangCard, { cooperation: status && status.cooperation }),
287
+ h(LiveFeed),
288
+ h('div', { style: S.desc }, '3.2.9:面板常驻(点面板外不收起、按钮二次点击收起、长内容可滚动);实时流(SSE)+ token 轮换/审计 + Worker 并发配额。'),
289
+ ))
240
290
  }
241
291
 
242
- /** sidebar.footer.action 槽:一个「房主」按钮,点击打开状态抽屉。 */
292
+ /** shell.overlay:整框浮层里的面板本体;关闭时不渲染任何节点。 */
293
+ function MasterOverlay() {
294
+ const open = usePanelOpen()
295
+ return open ? h(MasterPanel) : null
296
+ }
297
+
298
+ /**
299
+ * sidebar.footer.action 槽:一个「房主」按钮。
300
+ * 再点一次收起面板(之前只 setOpen(true),面板打开后按钮无法关闭)。
301
+ */
243
302
  function MasterSeat() {
244
- const [open, setOpen] = React.useState(false)
245
- return h('div', null,
246
- h('button', { type: 'button', style: S.fab, title: '房主 · 主 Agent 控制面', onClick: () => setOpen(true) }, '◈ 房主'),
247
- open ? h(MasterPanel, { onClose: () => setOpen(false) }) : null,
248
- )
303
+ const open = usePanelOpen()
304
+ return h('button', {
305
+ type: 'button',
306
+ style: open ? S.fabOpen : S.fab,
307
+ title: open ? '房主 · 点击收起控制面' : '房主 · 主 Agent 控制面',
308
+ 'aria-expanded': open ? 'true' : 'false',
309
+ 'aria-controls': 'dsh-master-panel',
310
+ onClick: () => togglePanel(),
311
+ }, '◈ 房主')
249
312
  }
250
313
 
251
314
  return {
@@ -257,6 +320,14 @@ window.__ModuleLoader__.load({
257
320
  order: 79,
258
321
  label: '房主',
259
322
  }, MasterSeat))
323
+ // 面板放在 shell.overlay:位于所有列与滚动容器之外,且整层 click-through,
324
+ // 因此不会随侧栏(或宿主弹层)卸载,点击面板外也不会收回。
325
+ ctx.slots.inject('shell.overlay', () => ctx.slots.register({
326
+ name: 'shell.overlay',
327
+ id: 'dsh-master-panel',
328
+ order: 78,
329
+ label: '房主面板',
330
+ }, MasterOverlay))
260
331
  },
261
332
  }
262
333
  },
@@ -7,7 +7,7 @@
7
7
  * 与瓮城 dsh-Fang 的协作只走 DSH 服务键,不 import fang 代码。
8
8
  */
9
9
  import crypto from 'node:crypto';
10
- export const MASTER_VERSION = '3.2.8';
10
+ export const MASTER_VERSION = '3.2.9';
11
11
  export const FANG_CONTRACT_MIN = '3.1.14';
12
12
  export const SERVICE = Object.freeze({
13
13
  master: 'dshMaster',
@@ -1,6 +1,6 @@
1
- # dsh-Master 架构(3.2.8)
1
+ # dsh-Master 架构(3.2.9)
2
2
 
3
- > 当前实现基线为 3.2.8;本文保留设计演进记录。最新实机证据与剩余限制见 `REVIEW-v3.2.7.md`。
3
+ > 当前实现基线为 3.2.9;本文保留设计演进记录。最新实机证据与剩余限制见 `REVIEW-v3.2.7.md`。
4
4
 
5
5
  ## 1. 为什么是“并行主插件”
6
6
 
package/docs/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 3.2.9(2026-11-12,tag / GitHub Release / NPM public)
2
+
3
+ **主题:房主面板交互修复(常驻 / 可收起 / 可滚动)+ 客户端行为测试。**
4
+
5
+ - 面板常驻:面板从 `sidebar.footer.action` 单元格移到 `shell.overlay` 整框浮层(位于所有列与宿主滚动容器之外,整层 click-through,面板自行 opt-in 指针事件),不再随侧栏或宿主弹层卸载;插件不注册任何 window/document 监听,因此点击面板之外不会收起。
6
+ - 按钮收起:侧栏「房主」按钮由 `setOpen(true)` 改为 `togglePanel()`,二次点击收起;按钮补 `aria-expanded` / `aria-controls` 与展开态样式。
7
+ - 面板滚动:`aside` 固定 viewport 高度,正文成为独立滚动容器(`flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable`),长内容不再被裁切。
8
+ - 状态共享:按钮与面板是两个独立注册的组件,展开态放在模块级 store(订阅 + `useState` 同步),不依赖任一组件生命周期。
9
+ - 测试:新增 `test/client-panel.test.mjs`,用最小 hook 运行时(`node:vm` + 假 React/槽位)真实渲染组件,断言开关、无 outside-click 监听、滚动容器样式,以及 SSE token 405 时面板不卸载;全量 188/188 通过。
10
+ - 顺带把面板底部过期的版本说明从 3.2.7 更新到 3.2.9。
11
+ - 已知限制(非本版范围):mother 角色下 Router 子插件按角色门禁禁用,`/superagent/stream-token` 路由不存在,DSH 未匹配路由对 POST 回 405,面板因此显示「实时流 token 获取失败(HTTP 405)」;这是角色门禁的预期表现,本版未改文案。
12
+
1
13
  ## 3.2.8(2026-11-12,tag / GitHub Release / NPM public)
2
14
 
3
15
  **主题:真实双 Host/fork 验收驱动的安全、生命周期、契约与 UI 收口。**
@@ -0,0 +1,27 @@
1
+ # dsh-Master v3.2.8 发布门禁
2
+
3
+ 日期:2026-11-12。
4
+
5
+ ## 内容
6
+
7
+ 本版将 v3.2.7 后的全面审计修复正式发布:Worker/工具、Router 生命周期与鉴权、契约幂等与文件桥清理、PR 交付护栏与接线、飞书加密、长 Shell 后台作业、HarnessMM 实测接线、PJM 派生契约、UI token 轮换与测试基础设施。
8
+
9
+ 完整证据见 `REVIEW-v3.2.7.md` 与 `SMOKE-3.2.7.md`。
10
+
11
+ ## 发布前实测
12
+
13
+ ```text
14
+ npm run check 0 error
15
+ npm run build 0 error(dist-host + dist-worker)
16
+ npm test 185 / 185 pass / 0 fail
17
+ npm run test:clean 185 / 185 pass / 0 fail(不使用 --test-force-exit)
18
+ ```
19
+
20
+ ## 三方发布目标
21
+
22
+ - `main`:发布提交;
23
+ - `v3.2.8`:annotated tag;
24
+ - GitHub Release:tarball + sha256 + manifest;
25
+ - NPM:`@twinsearth/dsh-master@3.2.8`,`access=public`。
26
+
27
+ 发布不等同于安装:本流程不修改任何用户 profile。
@@ -0,0 +1,29 @@
1
+ # dsh-Master v3.2.9 发布门禁
2
+
3
+ 日期:2026-11-12。
4
+
5
+ ## 内容
6
+
7
+ 本版只修房主面板的三项交互:面板常驻(点击面板之外不收起)、侧栏「房主」按钮二次点击收起、面板正文可滚动。面板从侧栏单元格改注册到 `shell.overlay` 整框浮层,展开态由模块级 store 共享,按钮与面板互不依赖对方生命周期。新增无浏览器行为测试 `test/client-panel.test.mjs`(最小 hook 运行时真实渲染注册组件)。
8
+
9
+ ## 发布前实测
10
+
11
+ ```text
12
+ npm run check 0 error
13
+ npm run build 0 error(dist-host + dist-worker)
14
+ npm test 188 / 188 pass / 0 fail
15
+ npm run test:clean 188 / 188 pass / 0 fail(不使用 --test-force-exit)
16
+ ```
17
+
18
+ ## 三方发布目标
19
+
20
+ - `main`:发布提交;
21
+ - `v3.2.9`:annotated tag;
22
+ - GitHub Release:tarball + sha256 + manifest;
23
+ - NPM:`@twinsearth/dsh-master@3.2.9`,`access=public`。
24
+
25
+ 发布不等同于安装:本流程不修改任何用户 profile(desktop profile 仍停在 3.2.8,升级需另行授权)。
26
+
27
+ ## 已知限制
28
+
29
+ mother 角色下 Router 子插件按角色门禁禁用,`/superagent/stream-token` 不存在,DSH 未匹配路由对 POST 返回 405,面板显示「实时流 token 获取失败(HTTP 405)」。这是角色门禁的预期表现,本版未改文案。
package/docs/ROADMAP.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # 路线图:3.2.0 → 3.4.0
2
2
 
3
3
  > 版本线:`3.2.0 – 3.2.9`(1 个大版本 + 9 个小版本),随后 3.3.0、3.4.0。
4
- > 本文保留版本演进记录;当前基线为 `3.2.8`。
4
+ > 本文保留版本演进记录;当前基线为 `3.2.9`。
5
5
  > 最新测试/实机状态与尚未完成的范围见 `REVIEW-v3.2.7.md`,下列旧版本待办不得再理解为当前状态。
6
6
 
7
7
  ## 三条主线
package/docs/SECURITY.md CHANGED
@@ -1,4 +1,4 @@
1
- # 安全边界(3.2.8)
1
+ # 安全边界(3.2.9)
2
2
 
3
3
  > 最新变化:worker-events 读取需 UI/匹配 worker token;旧 token 轮换 fail-closed;契约与 PR 网络调用全部有超时;DSH 同路径路由合并分流;飞书加密事件支持 AES-256-CBC。实机证据见 `REVIEW-v3.2.7.md`。
4
4
 
@@ -1,8 +1,8 @@
1
- # SuperAgent(v3.2.8)
1
+ # SuperAgent(v3.2.9)
2
2
 
3
3
  > 把“用户从多窗口注意力调度中解放出来”的架构跃迁:**Router(常驻控制面)** 与
4
4
  > **Worker(隔离执行体)** 分离,Ticket/Mandate 约束授权,渐进式信任控制自动接单。
5
- > 3.2.8 为审计修复发布线。历史限制与实机证据见 `REVIEW-v3.2.7.md`。
5
+ > 3.2.9 为面板交互修复发布线。历史限制与实机证据见 `REVIEW-v3.2.7.md`。
6
6
 
7
7
  ## 1. 双层结构
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/dsh-master",
3
- "version": "3.2.8",
3
+ "version": "3.2.9",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/kernel.ts CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  import crypto from 'node:crypto'
12
12
 
13
- export const MASTER_VERSION = '3.2.8'
13
+ export const MASTER_VERSION = '3.2.9'
14
14
  export const FANG_CONTRACT_MIN = '3.1.14'
15
15
 
16
16
  export const SERVICE = Object.freeze({