dsh-dupguard 1.1.4 → 1.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  本文件遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [1.2.0] - 2026-09-02
6
+
7
+ ### Added
8
+
9
+ - 适配 DSH 0.1.2 的 bundle 约定:本包自带补丁层 `cordis.patch.yml` 并声明
10
+ `dsh.bundle.patch`,安装只需一条命令 `dsh plugin --profile web add dsh-dupguard`
11
+ (`dsh plugin` 会自动把声明了 `dsh.bundle` 的依赖加入 `dsh.profile.bundles`,
12
+ DSH 按层应用补丁插入宿主行 `{ id: dupguard, name: dsh-dupguard }`,无需手改 YAML);
13
+ - package.json 增加 `dsh.compatibility`(`dsh >= 0.1.1-rc.1`)。
14
+
15
+ ### Changed
16
+
17
+ - 与 DSH 0.1.2-rc.1 逐项核对:`llm/stream` 瀑布事件与 `StreamChunk` 协议、`settings.register`
18
+ (`base` 选项)、客户端 `settingsScope.bind` / `decode(view.value)` / `describe`、
19
+ `settings.section` 槽位契约、`--dsw-*` 主题变量均未变化,插件行为无需改动;
20
+ - standing-mount 缺陷在 0.1.2-rc.1 仍未修复(上游保留 reclaim 代际的 TODO),
21
+ 兼容补丁继续默认开启;
22
+ - README 安装章节改为以 bundle 方式为主、手工补丁层为辅,并说明两者不可同时使用
23
+ (重复 entry id 会抛 `duplicate loader entry id: dupguard`)。
24
+
25
+ ## [1.1.5] - 2026-09-02
26
+
27
+ ### Changed
28
+
29
+ - 明确不支持 Node 18(与 DSH 一致):`engines` 提升为 `>=20`,CI 矩阵调整为
30
+ Node 20/22/24,README 同步说明。
31
+
5
32
  ## [1.1.4] - 2026-09-02
6
33
 
7
34
  ### Fixed
package/README.md CHANGED
@@ -49,13 +49,24 @@ Paste the entire content of [`plugin/host.js`](plugin/host.js) as `code.host` in
49
49
 
50
50
  插件已发布到 npm:[`dsh-dupguard`](https://www.npmjs.com/package/dsh-dupguard)。
51
51
 
52
- **1. 在 DSH profile 目录安装依赖**(例如 web GUI 的 `$DSH_HOME/profiles/web`):
52
+ **1. 安装(DSH ≥ 0.1.2-rc.1,一条命令)**:
53
53
 
54
54
  ```bash
55
- pnpm add dsh-dupguard # 或 npm install dsh-dupguard
55
+ dsh plugin --profile web add dsh-dupguard
56
56
  ```
57
57
 
58
- **2. 在 profile 的用户补丁层 `cordis.patch.yml` 插入组合行**:
58
+ 本包自带 bundle 补丁层(`dsh.bundle.patch` → [`cordis.patch.yml`](cordis.patch.yml)):
59
+ `dsh plugin` 把参数转发给 profile 目录下的 pnpm,安装后自动把声明了 `dsh.bundle` 的依赖
60
+ 加入 `dsh.profile.bundles`;DSH 按 bundles 顺序应用各层补丁,本插件的层插入宿主行
61
+ `{ id: dupguard, name: dsh-dupguard }`。**无需手改任何 YAML。**
62
+
63
+ - 验证:`dsh --profile web --dump-config` 末尾应出现 `dupguard` 行;loader 日志出现
64
+ `apply plugin dupguard`。
65
+ - 升级:`dsh plugin --profile web update dsh-dupguard`;
66
+ 卸载:`dsh plugin --profile web remove dsh-dupguard`(依赖与 bundles 层一并移除)。
67
+
68
+ **2. 手工补丁层(旧版 DSH 或不想加入 bundles 时仍受支持)**:在 profile 的用户补丁层
69
+ `cordis.patch.yml` 自行插入同一行:
59
70
 
60
71
  ```yaml
61
72
  # $DSH_HOME/profiles/web/cordis.patch.yml
@@ -64,33 +75,36 @@ pnpm add dsh-dupguard # 或 npm install dsh-dupguard
64
75
  name: dsh-dupguard
65
76
  ```
66
77
 
67
- - 用户补丁层在 bundle 层之后、`--patch` 之前应用;运行中的 DSH 通过 `watchUserPatches`
68
- **热重载**它——保存即生效,无需重启;加载失败会事务性回滚,不影响应用。
69
- - 验证:`dsh --profile web --dump-config` 末尾应出现 `dupguard` 行;loader 日志会出现
70
- `apply plugin dupguard`。
71
- - **撤销**:删掉该 `insert` 项即可热卸载。
78
+ 用户补丁层在 bundle 层之后应用;运行中的 DSH 通过 `watchUserPatches` **热重载**它——
79
+ 保存即生效,无需重启,加载失败会事务性回滚。
72
80
 
73
- **本机当前状态**:`profiles/web` 的组合行仍以 `file:` URL 直连仓库 `lib/index.js`
74
- (发布前的过渡形态)。切换到 npm 包只需两步:在 `$DSH_HOME/profiles/web` 下执行
75
- `pnpm add dsh-dupguard`,再把 `cordis.patch.yml` 中该行的 `name` 改为 `dsh-dupguard`。
81
+ ⚠️ 两种方式**不要同时使用**:loader 对重复 entry id 直接抛
82
+ `duplicate loader entry id: dupguard`。从手工方式切换到 bundle 方式时,请删除手工 `insert` 项。
76
83
 
77
- **本地开发替代**:未发布/调试时,`name` 也可直接用 `file:` URL 指向仓库内的
84
+ **本地开发**:未发布/调试时,`name` 也可直接用 `file:` URL 指向仓库内的
78
85
  [`lib/index.js`](lib/index.js)(CJS 导出 `{ name, apply }`,与 loader 的
79
86
  `unwrapExports` 兼容,零构建)。
80
87
 
81
- The plugin is published on npm as [`dsh-dupguard`](https://www.npmjs.com/package/dsh-dupguard).
82
- Install it inside the DSH profile (e.g. `pnpm add dsh-dupguard` under
83
- `$DSH_HOME/profiles/web`), then insert the row `{ id: dupguard, name: dsh-dupguard }` into the
84
- profile's user patch layer `cordis.patch.yml`. The running DSH hot-reloads that file
85
- (`watchUserPatches`) — no restart needed; a failed reload rolls back transactionally. Verify with
86
- `dsh --profile web --dump-config` or the loader log line `apply plugin dupguard`; remove the
87
- `insert` entry to uninstall.
88
-
89
- **Current state on this machine**: the `profiles/web` row still points at the repo's
90
- `lib/index.js` via a `file:` URL (pre-publish transitional form). To switch to the npm package,
91
- run `pnpm add dsh-dupguard` in `$DSH_HOME/profiles/web` and change that row's `name` to
92
- `dsh-dupguard`. The `file:` URL form also remains handy for local development against
93
- [`lib/index.js`](lib/index.js) (CJS, `module.exports = { name, apply }`, compatible with the
88
+ The plugin is published on npm as [`dsh-dupguard`](https://www.npmjs.com/package/dsh-dupguard)
89
+ and ships its own bundle patch layer (`dsh.bundle.patch` → [`cordis.patch.yml`](cordis.patch.yml)).
90
+ Install it with one command (DSH ≥ 0.1.2-rc.1):
91
+
92
+ ```bash
93
+ dsh plugin --profile web add dsh-dupguard
94
+ ```
95
+
96
+ `dsh plugin` forwards its arguments to pnpm in the profile directory and appends any dependency
97
+ declaring `dsh.bundle` to `dsh.profile.bundles`; DSH then applies each bundle layer's patch in
98
+ order, and this plugin's layer inserts the host row `{ id: dupguard, name: dsh-dupguard }`. No YAML
99
+ editing needed. Verify with `dsh --profile web --dump-config` or the loader log line
100
+ `apply plugin dupguard`; upgrade with `dsh plugin --profile web update dsh-dupguard` and uninstall
101
+ with `dsh plugin --profile web remove dsh-dupguard`.
102
+
103
+ **Manual layer (still supported)**: without a bundles entry, insert the same row into the profile's
104
+ user patch layer `cordis.patch.yml`; the running DSH hot-reloads that file (`watchUserPatches`) with
105
+ a transactional rollback on failure. Do **not** combine both — the loader throws
106
+ `duplicate loader entry id: dupguard`. The `file:` URL form also remains handy for local development
107
+ against [`lib/index.js`](lib/index.js) (CJS, `module.exports = { name, apply }`, compatible with the
94
108
  loader's `unwrapExports`, no build step).
95
109
 
96
110
  ---
@@ -184,7 +198,8 @@ default), Markdown table separator rows and horizontal rules (whitelisted by def
184
198
  ├── tests/
185
199
  │ ├── detector.test.js # 端到端测试:双入口防漂移 + reasoning 开关 + settings 集成
186
200
  │ └── experiment-cancel.mjs # 诊断实验(不进 CI):验证截停不阻塞于底层流取消
187
- ├── .github/workflows/ci.yml # GitHub Actions:Node 18/20/22
201
+ ├── .github/workflows/ci.yml # GitHub Actions:Node 20/22/24
202
+ ├── cordis.patch.yml # bundle 补丁层(dsh.bundle.patch:插入宿主行)
188
203
  ├── package.json
189
204
  ├── CHANGELOG.md
190
205
  ├── LICENSE # MIT
@@ -199,11 +214,11 @@ node tests/detector.test.js # 或 npm test
199
214
 
200
215
  同一套 15 项用例分别驱动两个入口(`plugin/host.js` 经 `new Function` 求值、`lib/index.js` 经
201
216
  `require` 加载),覆盖:透传完整性、各类复读形态、阈值边界、协议闭合、上游 `return()` 调用、
202
- 默认不检测 reasoning/工具参数、未闭合工具调用块的闭合、多次调用状态隔离等。CI 在 Node 18/20/22
203
- 上运行。
217
+ 默认不检测 reasoning/工具参数、未闭合工具调用块的闭合、多次调用状态隔离等。CI 在 Node 20/22/24
218
+ 上运行(与 DSH 一致,不支持 Node 18)。
204
219
 
205
220
  The same 15-test suite drives both entries, guarding against drift between the two forms. CI runs on
206
- Node 18/20/22.
221
+ Node 20/22/24 (matching DSH; Node 18 is not supported).
207
222
 
208
223
  ---
209
224
 
@@ -213,7 +228,7 @@ Node 18/20/22.
213
228
  - 服务端停止依赖适配器在流关闭时中止底层请求的语义(已验证 `dsh-llm-deepseek`;自定义适配器需自查)。
214
229
  - 阈值语义为 `>= threshold`:第 10 次重复出现时即停止。
215
230
 
216
- ### DSH 运行期间编辑 preset 后的 standing-mount 冲突(DSH ≤ 0.1.1-rc.1 缺陷,本插件已内置补丁)
231
+ ### DSH 运行期间编辑 preset 后的 standing-mount 冲突(DSH ≤ 0.1.2-rc.1 缺陷,本插件已内置补丁)
217
232
 
218
233
  **现象**:对某个会话执行模型选择等操作时报
219
234
  `resume failed ... preset ... failed to mount ... Host Cordis inspect provider "Service" is already registered`,
@@ -229,7 +244,9 @@ while the process lives")。`tool-cordis` 在每次挂载时向**进程全局*
229
244
  **本插件的修复(默认开启)**:`apply` 时把 `cordisInspect.register` **幂等化**——同 id 已有注册时
230
245
  共享既有注册并返回 no-op disposer,多代并存不再冲突。补丁进程内常驻(卸载本插件后仍生效,
231
246
  重启后由本插件重新安装;HMR 重载不会叠加)。依赖 `cordisInspect.providers` 为可读 Map
232
- (rc.6 / rc.7 / 0.1.1-rc.1 实测如此);DSH 升级修复后可将 `CONFIG.fixStandingMountConflict` 置为 `false` 关闭。
247
+ (rc.6 / rc.7 / 0.1.1-rc.1 / 0.1.2-rc.1 实测如此);0.1.2-rc.1 上游仍留有
248
+ "reclaim the superseded generation" 的 TODO,缺陷未修复,故默认开启。
249
+ DSH 升级修复后可将 `CONFIG.fixStandingMountConflict` 置为 `false` 关闭。
233
250
 
234
251
  **仍建议的操作纪律**:运行期间编辑已挂载 preset 后重启 DSH(补丁消除的是报错,旧代残留的
235
252
  组合仍占用资源,这是 DSH 的既有行为);根治仍待上游修复。
@@ -242,7 +259,8 @@ its process-global Host inspect providers (`Service` …) twice and every retry
242
259
  patches it by default**: `cordisInspect.register` is made idempotent (a same-id registration shares
243
260
  the existing one and gets a no-op disposer), so coexisting generations no longer collide. The patch
244
261
  is process-resident (survives plugin unload, reinstalled on restart; HMR reload does not stack it);
245
- set `CONFIG.fixStandingMountConflict` to `false` once a fixed DSH ships.
262
+ set `CONFIG.fixStandingMountConflict` to `false` once a fixed DSH ships. Still open in 0.1.2-rc.1:
263
+ upstream carries a TODO to reclaim superseded generations.
246
264
 
247
265
  ## License
248
266
 
@@ -0,0 +1,6 @@
1
+ # dsh-dupguard bundle layer: insert the plugin row when this bundle is active.
2
+ # The row name resolves from the profile's own node_modules, where
3
+ # `dsh plugin --profile <name> add dsh-dupguard` installed this package.
4
+ - insert:
5
+ - id: dupguard
6
+ name: dsh-dupguard
package/lib/index.js CHANGED
@@ -66,7 +66,7 @@ const CONFIG = {
66
66
  monitorReasoning: true,
67
67
  // 是否同时检测工具调用参数(JSON 片段)。默认关闭:JSON / base64 中重复字符很常见。
68
68
  monitorToolArguments: false,
69
- // DSH ≤ 0.1.1-rc.1 兼容补丁(默认开启;实测 0.1.1-rc.1 仍未修复):preset 的 standing mount
69
+ // DSH ≤ 0.1.2-rc.1 兼容补丁(默认开启;实测 0.1.1-rc.1 / 0.1.2-rc.1 仍未修复):preset 的 standing mount
70
70
  // 在 composition 文件变化后
71
71
  // 会新建一代而旧代永不销毁,tool-cordis 每次挂载都向进程全局的 cordisInspect 注册表
72
72
  // 注册 Service/Event/Builtin/Tool provider,两代并存即抛
@@ -271,7 +271,7 @@ function createStreamGuard(options, runtime) {
271
271
  }
272
272
 
273
273
  /**
274
- * DSH ≤ 0.1.1-rc.1 standing-mount 兼容补丁:把 cordisInspect.register 幂等化。
274
+ * DSH ≤ 0.1.2-rc.1 standing-mount 兼容补丁:把 cordisInspect.register 幂等化。
275
275
  *
276
276
  * 背景:preset 的 standing mount 在 composition 文件变化后新建一代、旧代永不销毁;
277
277
  * tool-cordis 每次挂载都向进程全局的 cordisInspect 注册 Service/Event/Builtin/Tool
@@ -279,7 +279,7 @@ function createStreamGuard(options, runtime) {
279
279
  * 注册(返回 no-op disposer),多代并存不再冲突。
280
280
  *
281
281
  * 说明:补丁有意不做撤销(常驻进程,重启后由本插件重新安装);依赖
282
- * cordisInspect.providers 为可读 Map(实测 rc.6 / rc.7 / 0.1.1-rc.1 如此)。
282
+ * cordisInspect.providers 为可读 Map(实测 rc.6 / rc.7 / 0.1.1-rc.1 / 0.1.2-rc.1 如此)。
283
283
  * DSH 升级修复后可将 CONFIG.fixStandingMountConflict 置为 false。
284
284
  */
285
285
  function installStandingMountPatch(ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-dupguard",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "description": "Real-time repetition guard for DeepSeek Harness (DSH): stops model generation when the same string repeats >=10 times in the streamed output. 实时检测 DSH 大模型流式输出中的重复内容,同一字符串重复十次以上立即停止生成。",
5
5
  "keywords": [
6
6
  "dsh-plugin",
@@ -23,19 +23,26 @@
23
23
  "./package.json": "./package.json"
24
24
  },
25
25
  "dsh": {
26
+ "bundle": {
27
+ "patch": "./cordis.patch.yml"
28
+ },
26
29
  "client": {
27
30
  "platform": "web"
31
+ },
32
+ "compatibility": {
33
+ "dsh": ">=0.1.1-rc.1"
28
34
  }
29
35
  },
30
36
  "files": [
31
37
  "lib",
32
38
  "plugin",
39
+ "cordis.patch.yml",
33
40
  "README.md",
34
41
  "LICENSE",
35
42
  "CHANGELOG.md"
36
43
  ],
37
44
  "engines": {
38
- "node": ">=18"
45
+ "node": ">=20"
39
46
  },
40
47
  "scripts": {
41
48
  "test": "node tests/detector.test.js"
package/plugin/host.js CHANGED
@@ -40,7 +40,7 @@ const CONFIG = {
40
40
  monitorReasoning: true,
41
41
  // 是否同时检测工具调用参数(JSON 片段)。默认关闭:JSON / base64 中重复字符很常见。
42
42
  monitorToolArguments: false,
43
- // DSH ≤ 0.1.1-rc.1 兼容补丁(默认开启;实测 0.1.1-rc.1 仍未修复):preset 的 standing mount
43
+ // DSH ≤ 0.1.2-rc.1 兼容补丁(默认开启;实测 0.1.1-rc.1 / 0.1.2-rc.1 仍未修复):preset 的 standing mount
44
44
  // 在 composition 文件变化后
45
45
  // 会新建一代而旧代永不销毁,tool-cordis 每次挂载都向进程全局的 cordisInspect 注册表
46
46
  // 注册 Service/Event/Builtin/Tool provider,两代并存即抛
@@ -243,7 +243,7 @@ function createStreamGuard(options) {
243
243
  }
244
244
 
245
245
  /**
246
- * DSH ≤ 0.1.1-rc.1 standing-mount 兼容补丁:把 cordisInspect.register 幂等化。
246
+ * DSH ≤ 0.1.2-rc.1 standing-mount 兼容补丁:把 cordisInspect.register 幂等化。
247
247
  *
248
248
  * 背景:preset 的 standing mount 在 composition 文件变化后新建一代、旧代永不销毁;
249
249
  * tool-cordis 每次挂载都向进程全局的 cordisInspect 注册 Service/Event/Builtin/Tool
@@ -251,7 +251,7 @@ function createStreamGuard(options) {
251
251
  * 注册(返回 no-op disposer),多代并存不再冲突。
252
252
  *
253
253
  * 说明:补丁有意不做撤销(常驻进程,重启后由本插件重新安装);依赖
254
- * cordisInspect.providers 为可读 Map(实测 rc.6 / rc.7 / 0.1.1-rc.1 如此)。
254
+ * cordisInspect.providers 为可读 Map(实测 rc.6 / rc.7 / 0.1.1-rc.1 / 0.1.2-rc.1 如此)。
255
255
  * DSH 升级修复后可将 CONFIG.fixStandingMountConflict 置为 false。
256
256
  */
257
257
  function installStandingMountPatch(ctx) {