dsh-config-manager 0.1.72 → 0.1.73

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 (130) hide show
  1. package/README.md +40 -4
  2. package/README.zh-CN.md +40 -4
  3. package/lib/adapters/index.d.ts +1 -0
  4. package/lib/adapters/index.js +5 -0
  5. package/lib/adapters/messages.d.ts +3 -0
  6. package/lib/adapters/messages.js +7 -0
  7. package/lib/adapters/plugin-data.d.ts +114 -0
  8. package/lib/adapters/plugin-data.js +309 -0
  9. package/lib/adapters/self.d.ts +1 -1
  10. package/lib/adapters/self.js +2 -1
  11. package/lib/adapters/test-helpers.d.ts +9 -0
  12. package/lib/adapters/test-helpers.js +14 -0
  13. package/lib/client.d.ts +95 -6
  14. package/lib/client.js +10630 -10133
  15. package/lib/core/analyzer.d.ts +11 -2
  16. package/lib/core/analyzer.js +31 -8
  17. package/lib/core/backup-plan.d.ts +10 -0
  18. package/lib/core/backup-plan.js +21 -3
  19. package/lib/core/backup.d.ts +20 -1
  20. package/lib/core/backup.js +48 -3
  21. package/lib/core/command-rollback.d.ts +6 -2
  22. package/lib/core/command-rollback.js +6 -2
  23. package/lib/core/messages-core.d.ts +2 -0
  24. package/lib/core/messages-core.js +4 -0
  25. package/lib/core/messages.d.ts +2 -1
  26. package/lib/core/messages.js +4 -2
  27. package/lib/core/plugin-data-registry.d.ts +124 -0
  28. package/lib/core/plugin-data-registry.js +198 -0
  29. package/lib/core/rollback-gate.d.ts +33 -0
  30. package/lib/core/rollback-gate.js +57 -0
  31. package/lib/core/rollback-ledger.d.ts +248 -0
  32. package/lib/core/rollback-ledger.js +1282 -0
  33. package/lib/core/rollback-point.d.ts +187 -4
  34. package/lib/core/rollback-point.js +689 -179
  35. package/lib/core/rollback.js +6 -4
  36. package/lib/core/types.d.ts +19 -0
  37. package/lib/foreign/registry.d.ts +1 -1
  38. package/lib/foreign/registry.js +1 -1
  39. package/lib/index.d.ts +5 -1
  40. package/lib/index.js +83 -13
  41. package/lib/routes/messages.d.ts +1 -0
  42. package/lib/routes/messages.js +2 -0
  43. package/lib/routes/prefs.js +26 -3
  44. package/lib/routes/recovery.d.ts +21 -0
  45. package/lib/routes/recovery.js +127 -7
  46. package/lib/routes/sync.js +11 -1
  47. package/lib/schema/section-registry.d.ts +1 -1
  48. package/lib/schema/section-registry.js +32 -3
  49. package/lib/schema/types.d.ts +1 -1
  50. package/lib/sync/sync-engine.d.ts +21 -7
  51. package/lib/sync/sync-engine.js +52 -11
  52. package/lib/sync/ui-prefs.d.ts +22 -0
  53. package/lib/sync/ui-prefs.js +28 -0
  54. package/lib/ui/diff-view.d.ts +10 -0
  55. package/lib/ui/diff-view.js +17 -0
  56. package/lib/ui/export-flow.js +2 -0
  57. package/lib/ui/i18n.d.ts +4 -2
  58. package/lib/ui/i18n.js +8 -4
  59. package/lib/ui/rollback-view.d.ts +59 -0
  60. package/lib/ui/rollback-view.js +14 -0
  61. package/lib/ui/test-helpers.js +2 -1
  62. package/package.json +1 -1
  63. package/src/adapters/index.ts +5 -0
  64. package/src/adapters/messages.ts +7 -0
  65. package/src/adapters/plugin-data.test.ts +225 -0
  66. package/src/adapters/plugin-data.ts +351 -0
  67. package/src/adapters/self.ts +2 -1
  68. package/src/adapters/test-helpers.ts +15 -0
  69. package/src/client/chat/ChatEntryView.tsx +21 -1
  70. package/src/client/chat/RollbackDiffTab.tsx +49 -2
  71. package/src/client/chat/RollbackReviewDialog.tsx +20 -4
  72. package/src/client/chat/chat-point-window.ts +11 -1
  73. package/src/client/chat/rollback-diff-tab.ts +13 -3
  74. package/src/client/chat/rollback-entry-view.ts +5 -0
  75. package/src/client/chat/sidebar-slots.ts +24 -2
  76. package/src/client/common/DiffTable.tsx +44 -8
  77. package/src/client/common/client-route-parity.test.ts +6 -0
  78. package/src/client/common/routes.ts +10 -0
  79. package/src/client/common/section-labels.test.ts +1 -0
  80. package/src/client/common/section-labels.ts +2 -0
  81. package/src/client/config-manager.module.css +113 -10
  82. package/src/client/index.ts +47 -1
  83. package/src/client/locales.ts +8 -4
  84. package/src/client/recovery/RollbackCard.tsx +201 -7
  85. package/src/client/recovery/RollbackDiffPane.tsx +13 -2
  86. package/src/client/recovery/recovery-api.ts +28 -0
  87. package/src/client/recovery/recovery-locales.ts +34 -2
  88. package/src/client/recovery/rollback-enabled.ts +41 -0
  89. package/src/client/recovery/rollback-file-changes.test.ts +41 -0
  90. package/src/client/sync/sync-view.test.ts +1 -1
  91. package/src/core/analyzer.ts +31 -9
  92. package/src/core/backup-plan.test.ts +20 -5
  93. package/src/core/backup-plan.ts +22 -3
  94. package/src/core/backup.ts +50 -4
  95. package/src/core/command-rollback.ts +6 -2
  96. package/src/core/messages-core.ts +4 -0
  97. package/src/core/messages.ts +4 -2
  98. package/src/core/plugin-data-registry.test.ts +113 -0
  99. package/src/core/plugin-data-registry.ts +261 -0
  100. package/src/core/rollback-gate.test.ts +66 -0
  101. package/src/core/rollback-gate.ts +61 -0
  102. package/src/core/rollback-ledger.test.ts +655 -0
  103. package/src/core/rollback-ledger.ts +1314 -0
  104. package/src/core/rollback-point.test.ts +274 -24
  105. package/src/core/rollback-point.ts +745 -160
  106. package/src/core/rollback.ts +5 -3
  107. package/src/core/types.ts +19 -0
  108. package/src/foreign/registry.test.ts +1 -1
  109. package/src/foreign/registry.ts +1 -1
  110. package/src/index.ts +73 -10
  111. package/src/routes/messages.ts +2 -0
  112. package/src/routes/prefs.ts +31 -2
  113. package/src/routes/recovery.ts +122 -7
  114. package/src/routes/sync.ts +15 -1
  115. package/src/schema/manifest-schema.test.ts +4 -4
  116. package/src/schema/registry.test.ts +9 -2
  117. package/src/schema/section-registry.ts +32 -3
  118. package/src/schema/types.ts +2 -1
  119. package/src/sync/sync-credentials.test.ts +119 -0
  120. package/src/sync/sync-engine.ts +51 -11
  121. package/src/sync/transport.test.ts +1 -0
  122. package/src/sync/ui-prefs.test.ts +44 -1
  123. package/src/sync/ui-prefs.ts +39 -0
  124. package/src/ui/diff-view.test.ts +9 -1
  125. package/src/ui/diff-view.ts +22 -0
  126. package/src/ui/export-flow.ts +2 -0
  127. package/src/ui/i18n.ts +8 -4
  128. package/src/ui/rollback-view.test.ts +17 -0
  129. package/src/ui/rollback-view.ts +66 -0
  130. package/src/ui/test-helpers.ts +2 -1
@@ -1,9 +1,19 @@
1
1
  import { type DiffHunk } from '../utils/line-diff.ts';
2
+ import { ROLLBACK_DEFAULT_LEDGER_BYTES } from './rollback-ledger.ts';
2
3
  export declare const ROLLBACK_SCHEMA_VERSION = 2;
3
4
  /** 回滚点库目录名(dataDir/rollback-points)——路由族 /recovery/rollback-points/* 同源 */
4
5
  export declare const ROLLBACK_POINTS_DIR_NAME = "rollback-points";
6
+ /**
7
+ * 台账**清单**文件名(v3,issue #89):它只是一份小清单(元数据 + 段摘要),
8
+ * 完整记录在 records/<id>.json、工作区载荷在 payloads/<sha256>.json —— 清单里绝不含大数组。
9
+ */
5
10
  export declare const ROLLBACK_LEDGER_NAME = "index.json";
6
11
  export declare const ROLLBACK_OBJECTS_DIR_NAME = "objects";
12
+ /**
13
+ * 台账字节预算(records/ + payloads/)的缺省值,可由 RollbackEngineOptions.ledgerBytesBudget 覆盖。
14
+ * 为什么需要它:ROLLBACK_MAX_RECORDS(50)只限**条数**;本机实测 50 条 = 473 MB 直接把宿主撑爆。
15
+ */
16
+ export { ROLLBACK_DEFAULT_LEDGER_BYTES };
7
17
  /** 全局自动点上限(S6);protected / guard 点不占预算、只进不出 */
8
18
  export declare const ROLLBACK_MAX_RECORDS = 50;
9
19
  /** 每会话自动点上限(S6) */
@@ -81,6 +91,16 @@ export declare const ROLLBACK_DEFAULT_SESSION_QUIESCENT_MS = 30000;
81
91
  /** 轮询回退的间隔上界(S1:会话日志 mtime ≤5 s 轮询) */
82
92
  export declare const ROLLBACK_DEFAULT_POLL_INTERVAL_MS = 5000;
83
93
  export declare const ROLLBACK_LEFTOVER_LIST_LIMIT = 20;
94
+ /**
95
+ * 工作区捕获的**挂钟时间预算**(缺省 30 s)。
96
+ *
97
+ * 为什么需要(对标 dsh-turn-rewind 的 `deadline.ts`、dsh-checkpoint-rewind 至今未合并的 PR #25):
98
+ * 捕获路径此前只有「文件数 / 总字节 / 单文件字节」三个**规模**上限,没有**时间**上限 ——
99
+ * 一个「文件数很多但总量不大」的工作区能让 walk + stat + sha256 跑很久。
100
+ * 超预算**不是失败**:已收条目照常成点(ok=true),未走查的部分按 `budget-time` 逐条留痕、
101
+ * `truncated` 置真 ⇒ 删除集自动 fail-closed(证明名单不完整就一条都不删)。
102
+ */
103
+ export declare const ROLLBACK_DEFAULT_CAPTURE_BUDGET_MS = 30000;
84
104
  /**
85
105
  * 「删除回退点之后新建的路径」的**整套上限**(用户 2026-10-11 定案 A)。
86
106
  * 超过就整套拒绝(fail-closed),绝不删一半 —— 半套删除既没有可解释的状态,也无法整体回滚。
@@ -157,7 +177,14 @@ export type RollbackCode = 'ok' | 'deleted' | 'storage-unavailable' | 'ledger-un
157
177
  * 界面必须说两句不同的话(「这点没记录文件」vs「工作区已与该点一致」),
158
178
  * 而 `detail` 是技术细节、**不能**当用户可见分派依据。只出现在 segments[].reasonCode。
159
179
  */
160
- | 'no-recorded-files' | 'mutation-locked' | 'method-not-allowed' | 'session-missing' | 'session-out-of-root' | 'session-too-large' | 'session-active' | 'session-fork-unavailable' | 'fork-failed' | 'capture-failed' | 'capture-truncated' | 'capture-degraded' | 'no-chunks' | 'chunk-not-absolute' | 'chunk-not-found'
180
+ | 'no-recorded-files' | 'mutation-locked' | 'method-not-allowed' | 'session-missing' | 'session-out-of-root' | 'session-too-large' | 'session-active'
181
+ /**
182
+ * 子代理会话(`header.origin === 'subagent'`):**不自动留点**。
183
+ * 它的工作区与父会话相同、回合号是自己的,留点既与父会话重复又只挤占预算;
184
+ * 用户在侧栏看到的是嵌套在父对话下的子代理,不需要单独回退。
185
+ * 读不到 `origin`(老宿主)时**不加判定** —— 绝不因为读不到就少留点。
186
+ */
187
+ | 'subagent-session' | 'session-fork-unavailable' | 'fork-failed' | 'capture-failed' | 'capture-truncated' | 'capture-degraded' | 'no-chunks' | 'chunk-not-absolute' | 'chunk-not-found'
161
188
  /** 单文件差异:该路径**不在**这个回滚点的记录里(收窄:绝不把本端点做成任意路径读取器)。 */
162
189
  | 'file-not-recorded';
163
190
  /** 结构化指引 / 告警:code 供界面映射字典键,detail 是技术标识(路径 / errno / 计数)。 */
@@ -232,6 +259,16 @@ export interface RollbackWorkspacePayload {
232
259
  * 文件级忽略**不**置它:被忽略的文件既进 skipped、也进 walked(路径索引来源)⇒ 名单照样完整。
233
260
  */
234
261
  ignoreIncomplete?: boolean;
262
+ /**
263
+ * true = 走查被**时间预算 / 取消信号**中断 ⇒ 剩余子树**一条都没进任何名单**
264
+ * (不在 files / skipped / walked 里)⇒ 证明名单**不完整** ⇒ 删除集整体拒绝(fail-closed)。
265
+ *
266
+ * 与 `truncated` 的**关键区别**:`truncated` 的三种成因(文件数 / 总字节 / 单文件超限)都是
267
+ * 「继续遍历 + 逐条留痕」,名单依然完整,所以**不**拒删;只有「整棵子树根本没被走查」才必须拒删
268
+ * —— 否则那些文件会被判成「留点后才新建」而被删掉(数据丢失)。
269
+ * 老台账(本字段诞生前写下的点)一律没有它:按「名单完整」处理(与旧行为一致)。
270
+ */
271
+ walkIncomplete?: boolean;
235
272
  /** 命中文件数 / 字节上限(内容未全部进来,已如实留痕) */
236
273
  truncated: boolean;
237
274
  bytes: number;
@@ -347,12 +384,33 @@ export interface RollbackPointSummary {
347
384
  /** 继承视图里「状态真正的归属会话」(= 祖先会话 id;与 `inheritedFrom` 成对出现) */
348
385
  inheritedSessionId?: string | null;
349
386
  }
387
+ /**
388
+ * 台账(records/ + payloads/)的可观测快照。
389
+ * 路由层转发给界面时必须**照抄字段名与形状**(issue #89 的交付契约)。
390
+ */
391
+ export interface RollbackLedgerStatus {
392
+ /** 清单里的记录条数 */
393
+ records: number;
394
+ /** records/ + payloads/ 当前总字节 */
395
+ bytes: number;
396
+ /** 生效的字节预算(缺省 ROLLBACK_DEFAULT_LEDGER_BYTES) */
397
+ budgetBytes: number;
398
+ /** 本进程内被**预算**淘汰的自动点累计数(条数淘汰不计入) */
399
+ evicted: number;
400
+ /**
401
+ * 当前磁盘字节**已超**预算、且已无处可淘汰(典型:单条点本身就大于预算)。
402
+ * 界面据此说「台账已超预算、无法再自动回收」,绝不静默成「一切正常」。
403
+ */
404
+ overBudget: boolean;
405
+ }
350
406
  export interface RollbackStorageStatus {
351
407
  available: boolean;
352
408
  root: string;
353
409
  detail?: string;
354
410
  guidance: RollbackGuidance[];
355
411
  requirements: readonly string[];
412
+ /** 新增,可选(不破坏既有构造点);存储栈不可用时缺省 */
413
+ ledger?: RollbackLedgerStatus;
356
414
  }
357
415
  /** 捕获一个回滚点的输入(宿主/测试构造;无 HTTP capture 端点) */
358
416
  export interface RollbackPointInput {
@@ -372,6 +430,11 @@ export interface RollbackPointInput {
372
430
  chunks?: readonly string[];
373
431
  note?: string | null;
374
432
  protect?: boolean;
433
+ /**
434
+ * 调用方的取消信号(插件卸载 / 会话销毁):已中止时捕获在**下一个文件边界**停下,
435
+ * 已收条目照常成点(部分点 + truncated)。缺省不传 = 只受时间预算约束。
436
+ */
437
+ signal?: AbortSignal;
375
438
  }
376
439
  export interface RollbackCaptureResult {
377
440
  ok: boolean;
@@ -710,6 +773,16 @@ export interface RollbackEngineOptions {
710
773
  maxGuardsPerSession?: number;
711
774
  /** **全局** guard 点上限(缺省 50,见 ROLLBACK_MAX_GUARDS;与自动点的 maxRecords 无关) */
712
775
  maxGuards?: number;
776
+ /**
777
+ * 台账字节预算(records/ + payloads/ 的总字节,缺省 ROLLBACK_DEFAULT_LEDGER_BYTES)。
778
+ * 超预算时**只淘汰最旧的 auto 点**(guard / protected 不淘汰,沿用 selectPrune 的类别口径)。
779
+ */
780
+ ledgerBytesBudget?: number;
781
+ /**
782
+ * 工作区捕获的挂钟时间预算(毫秒,缺省 ROLLBACK_DEFAULT_CAPTURE_BUDGET_MS)。
783
+ * 超时留**部分点**,不拒绝留点;见该常量的说明。
784
+ */
785
+ captureBudgetMs?: number;
713
786
  guardPolicy?: RollbackGuardPolicy;
714
787
  /** 存储栈探针(缺省 = 真实磁盘探测);注入失败探针可测「缺失时仍可挂载」 */
715
788
  storage?: RollbackStorageProbe;
@@ -722,6 +795,12 @@ export interface RollbackEngineOptions {
722
795
  * 测试用它计数「真的读了几次」,从而钉住路径身份缓存命中时**零重读**。
723
796
  */
724
797
  readFile?: (target: string) => Promise<Uint8Array>;
798
+ /**
799
+ * 台账读盘观测 seam(缺省不观测):每次「读一条记录文件 / 一份载荷文件」前回调一次。
800
+ * 与 readFile seam 同一用途 —— 测试用它**确定性地**计数「一次留点到底读了几份记录」,
801
+ * 而不是靠无法复现的堆采样(issue #89 的内存护栏)。
802
+ */
803
+ ledgerReadFile?: (file: string) => void;
725
804
  /** E1:预览行级统计的规模上限(缺省见 ROLLBACK_PREVIEW_DIFF_*);注入小值即可确定性地覆盖 truncated 分支 */
726
805
  previewDiffMaxFileBytes?: number;
727
806
  previewDiffMaxTotalBytes?: number;
@@ -821,8 +900,21 @@ export declare class RollbackPointEngine {
821
900
  private forkLinksComplete;
822
901
  /** 会话日志头缓存(mtime + size 失效;只服务 fork 链扫描) */
823
902
  private readonly forkHeaderCache;
824
- /** 继承判据用的台账快照(TTL 缓存;避免每个 turn/start 都读整份台账) */
903
+ /** 继承判据用的台账快照(TTL 缓存;只缓存**清单条目**,绝不缓存完整记录) */
825
904
  private ledgerSnapshotCache;
905
+ /** 台账存储层(v3:清单 + 单条记录 + 内容寻址载荷;issue #89) */
906
+ private readonly ledger;
907
+ private readonly ledgerBytesBudget;
908
+ /** 工作区捕获的挂钟时间预算(毫秒;见 ROLLBACK_DEFAULT_CAPTURE_BUDGET_MS) */
909
+ private readonly captureBudgetMs;
910
+ /**
911
+ * 启动期一次性初始化(流式迁移 v2 -> v3 + 孤儿 tmp 清理)的惰性单飞。
912
+ * 为什么惰取而不是构造时同步跑:构造发生在每个请求路径上,同步扫盘/迁移会把请求卡死;
913
+ * 而「每回合都扫一遍」正是要避免的浪费 —— 所以只跑一次并记住结果。
914
+ */
915
+ private initPromise;
916
+ /** 启动期初始化失败的如实记录(读路径必须报出来,绝不假装台账是空的) */
917
+ private initError;
826
918
  constructor(options: RollbackEngineOptions);
827
919
  /** 回滚点库根目录(只读侦察用;写路径全部经本类的门) */
828
920
  get storeDir(): string;
@@ -835,8 +927,24 @@ export declare class RollbackPointEngine {
835
927
  * node_modules)逐条记录、**不计入 skippedCount**,因此不会把点判成 degraded。
836
928
  */
837
929
  private recordSkip;
930
+ /**
931
+ * 打开一次回退的前像暂存目录(`<root>/undo/<pid>-<ts36>-<8hex>/`)。
932
+ * 返回 null = 建不出来(权限 / 磁盘满)⇒ 调用方必须**零写入失败**。
933
+ */
934
+ private openUndoStager;
838
935
  private writeFile;
936
+ /**
937
+ * 启动期一次性初始化(惰性单飞):先**流式**迁移 v2 单文件 -> v3,再清理崩溃残留的原子写 tmp。
938
+ *
939
+ * 为什么惰取而不是在构造里同步跑:引擎在每个请求路径上都会被新建(路由层不缓存引擎),
940
+ * 构造里同步扫盘/迁移会把请求卡死;而「每回合扫一遍」又是纯浪费 —— 所以只跑一次并记住结果。
941
+ * 迁移失败**不抛**:如实记进 initError,由读路径报出来,并**拒写**(绝不覆盖还没迁完的 v2 文件)。
942
+ */
943
+ private ensureReady;
944
+ private runInit;
839
945
  storageStatus(): Promise<RollbackStorageStatus>;
946
+ /** 台账可观测快照(清单条数 + 磁盘字节 + 预算 + 本进程淘汰计数) */
947
+ private ledgerStatus;
840
948
  private writeObjectWithHash;
841
949
  /** 对象仓里是否真有这个 blob(缓存命中时校验;任何异常一律按「没有」处理) */
842
950
  private objectExists;
@@ -889,7 +997,19 @@ export declare class RollbackPointEngine {
889
997
  private gcPathCache;
890
998
  /** 拒删成因的如实说明(没有可解释成因时返回空数组,绝不编理由) */
891
999
  private createdRefusalWarnings;
892
- private readLedger;
1000
+ /**
1001
+ * 读台账**清单**(只读元数据 + 段摘要;代价与工作区文件数**无关**)。
1002
+ *
1003
+ * 与 v2 的差别只在实现:v2 每回合把整份 index.json 读进内存并 parse(473 MB 那条路),
1004
+ * v3 读的是一份小清单。**外部语义一个字没变**:返回 entries + 同一套 error 文案。
1005
+ * 启动期初始化失败时如实回报(不读 v2 文件、不假装台账为空)。
1006
+ */
1007
+ private readManifest;
1008
+ /**
1009
+ * 读**完整**记录(引用解析对调用方透明)。失败一律如实回报,**绝不返回半个记录**:
1010
+ * 载荷缺失 / 哈希不符 / 形状不符 ⇒ error(调用方走 record-incomplete 路径)。
1011
+ */
1012
+ private readFullRecord;
893
1013
  /**
894
1014
  * S6 淘汰(t14 冻结 + 2026-10-12 guard 全局上限):三类点各按各的预算裁,**只删最旧的**。
895
1015
  *
@@ -899,13 +1019,50 @@ export declare class RollbackPointEngine {
899
1019
  * | guard 救援点 | 每会话 `maxGuardsPerSession`(20)→ 全局 `maxGuards`(`ROLLBACK_MAX_GUARDS`,50) | **不占**自动点预算、**不受**自动点的 20/50 影响;用户删不掉 ⇒ 必须有自己的两道预算(单会话挡住不够,跨会话也要收敛) |
900
1020
  * | protected 点 | 无上限(只进不出) | 生产路径**造不出来**(capture 端点已下线、tracker 从不设 protect),只有测试能造 |
901
1021
  */
902
- private selectPrune;
1022
+ /**
1023
+ * 条数淘汰(口径与 v2 逐字相同,只是从「记录」换成「清单条目」):三类点各按各的预算裁,**只删最旧的**。
1024
+ *
1025
+ * | 类别 | 预算 | 说明 |
1026
+ * |---|---|---|
1027
+ * | auto 自动点 | 每会话 maxRecordsPerSession(20)→ 全局 maxRecords(50) | 两次裁剪都只删最旧 |
1028
+ * | guard 救援点 | 每会话 maxGuardsPerSession(20)→ 全局 maxGuards(ROLLBACK_MAX_GUARDS,50) | **不占**自动点预算、**不受**自动点的 20/50 影响;用户删不掉 ⇒ 必须有自己的两道预算(单会话挡住不够,跨会话也要收敛) |
1029
+ * | protected 点 | 无上限(只进不出) | 生产路径**造不出来**(capture 端点已下线、tracker 从不设 protect),只有测试能造 |
1030
+ */
1031
+ private selectPruneEntries;
1032
+ /**
1033
+ * 字节预算淘汰(issue #89 的**根因修复**):条数没超、字节超了照样会 OOM。
1034
+ *
1035
+ * 判据与条数淘汰共用同一类别口径:**只淘汰最旧的 auto 点**(guard / protected 永不淘汰)。
1036
+ * 释放量按「记录文件字节 + 载荷**最后一个引用**消失时的载荷字节」估算(引用计数靠清单,不读载荷)。
1037
+ * 返回淘汰后的清单(调用方负责落盘与删文件)。
1038
+ */
1039
+ private applyByteBudget;
1040
+ /**
1041
+ * 对象仓回收(**重写版**,issue #89):账本字节数不再进内存。
1042
+ *
1043
+ * 旧实现要从「内存里所有记录的全部 files 数组」累计引用 ⇒ 回收一次 = 把整份台账读进内存。
1044
+ * 现在:扫**清单** -> 逐个读引用到的**载荷文件**(一次只驻留一份)-> 累计 referenced 对象键 -> 扫 objects/。
1045
+ * 峰值内存 ∝ 单份载荷 + 对象键集合,**不 ∝ 台账总字节**。
1046
+ * 任何一份被引用的载荷读不出来 ⇒ 整体放弃回收(fail-closed:读不到的载荷里有什么对象谁也不知道)。
1047
+ */
903
1048
  private gcObjects;
904
1049
  /**
905
1050
  * 把记录写进台账(**留点侧唯一写入口**):整段「读全文 → 合并 → S6 淘汰 → 原子写 → 回收对象」
906
1051
  * 都在 {@link withLedgerWriteLock} 的进程内串行队列里 —— 绝不与 tracker / 路由的另一次写交错。
907
1052
  */
1053
+ /**
1054
+ * 写一条记录(**最多三次尝试**)—— 跨进程乐观并发(①B)。
1055
+ *
1056
+ * dataDir 由 `$DSH_HOME` 决定、**不随 profile 分家**(与本插件自带的实例锁同一套多实例假设),
1057
+ * 所以「另一个 DSH 实例正在写同一份清单」是设计内的场景,而 `withLedgerWriteLock` 只串行本进程。
1058
+ * 策略:读清单前后各取一次**代际戳**,被改过就重读重试;三次都变 ⇒ **放弃本次留点并 warn**,
1059
+ * 绝不拿自己的快照覆盖别人的清单(宁可少一条点,也不丢别人的点)。
1060
+ * 诚实的边界:检测与写入之间仍有极小窗口,无跨进程锁时无法彻底消除 —— 这里保证的是
1061
+ * 「不静默覆盖 + 尽量合并」,不是原子性。
1062
+ */
908
1063
  private appendRecord;
1064
+ /** `appendRecord` 的单次尝试;`conflict: true` = 这期间清单被别的实例改过,调用方应当重试。 */
1065
+ private appendRecordOnce;
909
1066
  /**
910
1067
  * 留一个回滚点(宿主自动留点 / 恢复前救援点 / 测试构造都走这一条)。
911
1068
  * 没有 HTTP capture 端点 —— 手动「拍点」已随规格 S1 下线。
@@ -1047,6 +1204,30 @@ export declare class RollbackPointEngine {
1047
1204
  * 会在本方法的读与写之间插入一次 append,把刚删掉的点**复活**(评审 20 轮实测 12 轮复现)。
1048
1205
  */
1049
1206
  remove(id: string): Promise<RollbackRemoveResult>;
1207
+ /**
1208
+ * 磁盘清理入口(引擎侧):删**全部**自动点(includeGuards=true 时连 guard 也删),
1209
+ * protected 点**永不删**(生产路径造不出来,只有测试能造)。随后回收无人引用的载荷与对象。
1210
+ *
1211
+ * 零写入拒绝路径照旧:存储不可用 / 清单读不出来(形状不符、迁移失败、迁移中)⇒ **抛带 code 的错误**,
1212
+ * 一个字节都不动、也绝不静默返回全 0(「什么都没删」与「读不出来所以没删」是两件事)。
1213
+ * 返回的 bytes = 本次真正删掉的字节(记录文件 + 载荷文件 + 对象文件)。
1214
+ */
1215
+ purgeLedger(options?: {
1216
+ includeGuards?: boolean;
1217
+ }): Promise<{
1218
+ removedRecords: number;
1219
+ removedPayloads: number;
1220
+ removedObjects: number;
1221
+ bytes: number;
1222
+ }>;
1223
+ }
1224
+ /**
1225
+ * 台账清理的**显式拒绝**(形状不符 / 存储不可用):带机器可读 code,绝不静默返回全 0。
1226
+ * 路由层按 code 映射 HTTP(与既有 RollbackCode 冻结集同源)。
1227
+ */
1228
+ export declare class RollbackLedgerRefusedError extends Error {
1229
+ readonly code: RollbackCode;
1230
+ constructor(code: RollbackCode, detail: string);
1050
1231
  }
1051
1232
  /** 会话登记(宿主订阅 session/event 时按会话登记一次)。 */
1052
1233
  export interface RollbackSessionBinding {
@@ -1167,6 +1348,8 @@ export declare class RollbackAutoTracker {
1167
1348
  private readonly lastStat;
1168
1349
  private timer;
1169
1350
  private mechanism;
1351
+ /** 在途捕获的取消信号(stop() 时 abort;重新 start() 时换一个新的) */
1352
+ private captureAbort;
1170
1353
  constructor(options: RollbackTrackerOptions);
1171
1354
  /** 实际采用的留点机制(output / 诊断用) */
1172
1355
  get mechanismKind(): 'event' | 'poll';