dsh-live-compact 1.1.2 → 1.1.3

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.
Files changed (3) hide show
  1. package/README.md +24 -14
  2. package/index.js +33 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,13 +14,13 @@
14
14
  headless:
15
15
 
16
16
  ```sh
17
- dsh plugin --profile headless add dsh-live-compact@1.1.2
17
+ dsh plugin --profile headless add dsh-live-compact@1.1.3
18
18
  ```
19
19
 
20
20
  网页端:
21
21
 
22
22
  ```sh
23
- dsh plugin --profile web add dsh-live-compact@1.1.2
23
+ dsh plugin --profile web add dsh-live-compact@1.1.3
24
24
  ```
25
25
 
26
26
  想先装到本机看版本:
@@ -30,6 +30,14 @@ npm install -g dsh-live-compact
30
30
  dsh-live-compact --version
31
31
  ```
32
32
 
33
+ ## 1.1.3
34
+
35
+ - 修复 #34:工具结果事件不再同步执行 shadow/ACL 文件操作,改为事件循环后排队并按 toolCallId 去重;维护开始前会等待已排队捕获,`captureNew` 仍负责补抓和重试。
36
+ - 这条路径避免在模型收到工具结果前被同步磁盘操作卡住;权限失败仍然只拒绝备份,不让宿主 fatal 退出。
37
+ - Windows hosted 验收:Windows 11 ARM、Windows 2022 x64 的 Node 22/24 core,以及三套 DSH 0.2.0/0.2.1 普通用户 runtime 均通过;官方 Desktop 普通用户安装、启用、设置保存和重启读取在 Windows 11 ARM 与 Windows 2022 均通过。
38
+ - 同一候选又做了 29 轮持久会话回放:Windows 2022 用时 14 分 05 秒,Windows 11 ARM 用时 17 分 08 秒,含第 21 轮进程重启边界和 58 次工具结果捕获。原报告的 93M/第 29 轮冻结没有在这组受控回放中重现;Node 24 首次双进程 cursor 测试的 299/300 竞态重跑通过,作为后续观察项保留。
39
+ - Config、验证/头脑风暴模式和自动主题分类继续后置,不改变 1.1.2 的小切默认参数。
40
+
33
41
  ## 1.1.2
34
42
 
35
43
  - 常用压缩参数可以在 DSH 插件设置里改,保留原有环境变量用法。
@@ -43,7 +51,7 @@ dsh-live-compact --version
43
51
 
44
52
  ### Windows 备份权限
45
53
 
46
- Windows 双用户实测(Actions run `42367713007`,Windows 11 Enterprise ARM / Server 2022 x64)确认:1.1.1 的 `/inheritance:r /grant:r` 会留下其他主体的显式 ACE,第二个普通用户仍能读取备份。1.1.2 改为完整替换 DACL。本次运行时代码修改后,最终候选仍须重新完成原生 Windows 与 Desktop 验收,结果随最终候选记录。
54
+ Windows 双用户实测(Actions run `42367713007`,Windows 11 Enterprise ARM / Server 2022 x64)确认:1.1.1 的 `/inheritance:r /grant:r` 会留下其他主体的显式 ACE,第二个普通用户仍能读取备份。1.1.2 改为完整替换 DACL。1.1.3 候选又通过了 hosted Windows core/runtime、官方 Desktop 普通用户安装与设置保存/重启,以及 29 轮持久会话回放;原报告的 93M/第 29 轮冻结未在受控回放中重现,慢存储、超大历史和共享目录压力仍属于后续观察范围。
47
55
 
48
56
  权限写入改为固定的 `%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe`(Windows PowerShell 5.1 / .NET Framework),使用 `-NoProfile -NonInteractive -EncodedCommand`。脚本固定内嵌在已打包的 `shadow.js`,路径、SID、文件类型通过子进程环境传递,不拼进脚本。当前用户 SID 仍由固定 System32 `whoami.exe` 每进程获取一次,脚本再对照自身 Windows 身份。新建受保护的 DACL,只允许当前用户、SYSTEM、Administrators 完全控制;目录 ACE 带 OI/CI,直接修正的文件 ACE 不带继承标志。只修改 DACL,不提权,不改 owner、group 或 SACL,不使用临时宽松 ACL。写后读取实际 DACL,核验保护位、完整 ACE 数量、SID、Allow、权限及继承标志,收到确认才缓存。工具失败、超时、SID 非法或读回不符都会熔断本进程后续备份,不回退到 chmod。
49
57
 
@@ -75,17 +83,19 @@ Windows 双用户实测(Actions run `42367713007`,Windows 11 Enterprise ARM
75
83
 
76
84
  这轮连的是插件版。10 组同一套 31 轮填日志,组内串行,两个模型同时开。原版 DSH 压缩对比留到 1.1.2。Coder 390 27B 这轮没跑,后补。测评图在下面,也在 [v1.1.1](https://github.com/Tranquillitatis-Lab/dsh-live-compact/releases/tag/v1.1.1)。
77
85
 
86
+ 下面三张图沿用已完成的 1.1.1 实测数据;1.1.2 只更新运行时修复和门禁,图标题按当前发布版本标注。
87
+
78
88
  看图时,先看日志题答对多少,再看首步缓存和压缩节省。首步缓存取每轮第一个 `step_end`,把命中 token 加起来,再除以这些请求的总输入 token。压缩节省是 `spine-cuts.jsonl` 里 `saved` 的合计。压缩轮次耗时是发生压缩的各轮总耗时之和,压缩操作本身为毫秒级。任务墙钟是从第 1 轮开始到第 31 轮结束的实际用时。结束上下文取最后一轮的 `totalTokens`,包含此前保留下来的内容。
79
89
 
80
- DS V4.1 Flash 跑了 10 组,填日志 230/230,跳过 0。首步缓存 98.64%,压缩节省 65 180 token,总用时 36 分 18 秒。多数组结束在 3.5 万到 4.6 万 token。第 1 组读了 25 次全文,4 次压缩共节省 21 493 token,结束时仍有 282 871。第 2 组几乎只用 bash,只读了一次全文,未触发压缩,结束时是 37 781。
90
+ DS V4.1 Flash 跑了 10 组,填日志 230/230,跳过 0。首步缓存 98.64%,压缩节省 65 180 token,总墙钟 36 分 18 秒。多数组结束在 3.5 万到 4.6 万 token。第 1 组读了 25 次全文,4 次压缩共节省 21 493 token,结束时仍有 282 871。第 2 组几乎只用 bash,只读了一次全文,未触发压缩,结束时是 37 781。图中的“含切割轮次墙钟”是整轮模型、工具和网络等待,不是插件小切耗时;小切操作本身为毫秒级。
81
91
 
82
- GLM 5.3 Flash 用了同一个插件、同一套题。有 7 组填日志达到 23/23。第 5、6 组全程连接失败。第 7 组前段连接失败,完成 14/23。有用量记录的轮次,首步缓存是 98.97%。第 1、2 组主要用 grep,未触发压缩,结束时分别是 39 511 和 29 987 token。第 4、9 组反复读全文,结束时分别是 285 535 和 281 515,和 DS 第 1 组很像。可比的 7 组里,DS 每组都更快。
92
+ GLM 5.3 Flash 用了同一个插件、同一套题。完整跑完的 7 组为 23/23;第 5、6 组连接失败;第 7 组前段连接失败,完成 14/23。有用量记录的轮次,首步缓存是 98.97%。第 1、2 组主要用 grep,未触发压缩,结束时分别是 39 511 和 29 987 token。第 4、9 组反复读全文,结束时分别是 285 535 和 281 515。GLM 图只展示插件实测;没有对应 stock 配对数据的地方不推断原版差值。
83
93
 
84
- ![插件版对比](docs/slide-111-overview.jpg)
94
+ ![插件版对比](docs/slide-112-overview.jpg)
85
95
 
86
- ![DS V4.1 Flash](docs/slide-111-ds.jpg)
96
+ ![DS V4.1 Flash](docs/slide-112-ds.jpg)
87
97
 
88
- ![GLM 5.3 Flash](docs/slide-111-glm.jpg)
98
+ ![GLM 5.3 Flash](docs/slide-112-glm.jpg)
89
99
 
90
100
  ## 1.0.9
91
101
 
@@ -297,8 +307,8 @@ GLM 5.3 Flash 用了同一个插件、同一套题。有 7 组填日志达到 23
297
307
  从 npm 装。headless 和网页是两个 profile,要各装一次。桌面端是另一个 profile,普通 `dsh plugin --profile desktop` 会被拒绝。
298
308
 
299
309
  ```sh
300
- dsh plugin --profile headless add dsh-live-compact@1.1.2
301
- dsh plugin --profile web add dsh-live-compact@1.1.2
310
+ dsh plugin --profile headless add dsh-live-compact@1.1.3
311
+ dsh plugin --profile web add dsh-live-compact@1.1.3
302
312
  ```
303
313
 
304
314
  想先装到本机看版本:
@@ -308,12 +318,12 @@ npm install -g dsh-live-compact
308
318
  dsh-live-compact --version
309
319
  ```
310
320
 
311
- 旧版 1.1.1 的国内镜像安装命令:
321
+ 上不了 npm 时的镜像安装命令:
312
322
 
313
323
  ```sh
314
- dsh plugin --profile headless add https://download.merkyorlynn.com/downloads/plugins/dsh-live-compact-1.1.1.tgz
315
- dsh plugin --profile web add https://download.merkyorlynn.com/downloads/plugins/dsh-live-compact-1.1.1.tgz
316
- npm install -g --force "https://download.merkyorlynn.com/downloads/plugins/dsh-live-compact-1.1.1.tgz"
324
+ dsh plugin --profile headless add https://download.merkyorlynn.com/downloads/plugins/dsh-live-compact-1.1.3.tgz
325
+ dsh plugin --profile web add https://download.merkyorlynn.com/downloads/plugins/dsh-live-compact-1.1.3.tgz
326
+ npm install -g --force "https://download.merkyorlynn.com/downloads/plugins/dsh-live-compact-1.1.3.tgz"
317
327
  dsh-live-compact --version
318
328
  ```
319
329
 
package/index.js CHANGED
@@ -15,6 +15,7 @@
15
15
  import { randomBytes, randomUUID } from "node:crypto";
16
16
  import { closeSync, existsSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
17
17
  import { createRequire } from "node:module";
18
+ import { setImmediate as scheduleImmediate } from "node:timers";
18
19
  import { dirname, join } from "node:path";
19
20
  import { pathToFileURL } from "node:url";
20
21
  import { loadBase, loadCompaction, loadLlm } from "./adapter.js";
@@ -459,6 +460,8 @@ export default class SpineCompactionEngine extends BasicCompactionEngine {
459
460
  rawSeen = new Map();
460
461
  callIndex = new Map();
461
462
  captureCursor = new Map();
463
+ pendingCaptures = new Set();
464
+ pendingCaptureKeys = new Set();
462
465
  pruneGuarded = false;
463
466
 
464
467
  constructor(ctx, config, resolvedConfig) {
@@ -470,13 +473,35 @@ export default class SpineCompactionEngine extends BasicCompactionEngine {
470
473
  try {
471
474
  const pendingCuts = new Set();
472
475
  ctx.effect(() => async () => {
473
- await Promise.allSettled(pendingCuts);
476
+ await Promise.allSettled([...pendingCuts, ...this.pendingCaptures]);
474
477
  });
475
478
  ctx.on("session/event", (session, event) => {
476
479
  try {
477
480
  this.noteEvent(session, event);
478
481
  if (event?.type !== "tool/result") return;
479
- this.captureOne(session, event);
482
+ const id = event.data?.message?.toolCallId;
483
+ if (!id) return;
484
+ const key = this.rawKey(session, id);
485
+ const cached = this.rawSeen.get(key);
486
+ if (cached && !cached.retryable) return;
487
+ if (this.pendingCaptureKeys.has(key)) return;
488
+ this.pendingCaptureKeys.add(key);
489
+ let job;
490
+ job = new Promise((resolve) => {
491
+ scheduleImmediate(() => {
492
+ try {
493
+ const current = this.rawSeen.get(key);
494
+ if (!current || current.retryable) this.captureOne(session, event);
495
+ } catch (error) {
496
+ noteShadowWarning(ctx.logger, error);
497
+ } finally {
498
+ this.pendingCaptureKeys.delete(key);
499
+ this.pendingCaptures.delete(job);
500
+ resolve();
501
+ }
502
+ });
503
+ });
504
+ this.pendingCaptures.add(job);
480
505
  } catch (error) {
481
506
  noteShadowWarning(ctx.logger, error);
482
507
  }
@@ -550,6 +575,7 @@ export default class SpineCompactionEngine extends BasicCompactionEngine {
550
575
  const state = this.stateFor(agent);
551
576
  const signal = AbortSignal.timeout(30000);
552
577
  const ev = events || agent.session.snapshotEvents();
578
+ await this.flushPendingCaptures();
553
579
  this.captureNew(agent.session, ev, settings);
554
580
  const jobs = collectJobs(ev);
555
581
  const calls = indexToolCalls(ev);
@@ -652,6 +678,11 @@ export default class SpineCompactionEngine extends BasicCompactionEngine {
652
678
  }
653
679
  }
654
680
 
681
+ async flushPendingCaptures() {
682
+ if (!this.pendingCaptures?.size) return;
683
+ await Promise.allSettled([...this.pendingCaptures]);
684
+ }
685
+
655
686
  async commitSelectedSpan(agent, chunk, signal) {
656
687
  signal?.throwIfAborted();
657
688
  const session = agent.session;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-live-compact",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "In-place newest-turn compaction for DeepSeek Harness. Compress while the task runs and keep the prefix.",
5
5
  "license": "MIT",
6
6
  "author": "MerkyorLynn <268568828+MerkyorLynn@users.noreply.github.com>",