dsh-retrace 0.4.14 → 0.4.16

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.
@@ -488,7 +488,7 @@ function useMessageSeq(useSession, messageId) {
488
488
  });
489
489
  }
490
490
  var SHADOW_SAFETY_RATIO = 0.4;
491
- var SHADOW_MIN_ROWS_FOR_RATIO = 10;
491
+ var SHADOW_MIN_ROWS_FOR_RATIO = 20;
492
492
  function shadowDegraded(keys, rowCount) {
493
493
  return keys !== null && rowCount > SHADOW_MIN_ROWS_FOR_RATIO && keys.length / rowCount > SHADOW_SAFETY_RATIO;
494
494
  }
package/lib/client.js CHANGED
@@ -566,14 +566,14 @@ function useMessageSeq(useSession, messageId) {
566
566
  const SHADOW_SAFETY_RATIO = 0.4
567
567
 
568
568
  /**
569
- * 短会话豁免阈值(2026-09-01,独立审查问题 B 的 client 侧另一半):
570
- * 行数 ≤ 本值的会话永不触发 40% 降级——否则 5-10 行的小会话撤回/编辑
571
- * 2-4 条消息就 >40%,被误判为「大范围操作」降级为不隐藏(用户实测:
572
- * 「已撤回 4 条消息 + 此操作涉及大范围对话…」= 小会话误伤)。
573
- * 与 host 侧 ROLLBACK_MIN_SURFACE=6(surface 节点)同源:短会话豁免只在
574
- * 大会话按比例判定。10 行 ≈ 5 轮,正常撤回/编辑远小于此。
569
+ * 短会话豁免阈值(2026-09-01,独立审查问题 B 的 client 侧另一半;
570
+ * 09-01 用户实测调大至 20):行数 ≤ 本值的会话永不触发 40% 降级——否则
571
+ * 小会话撤回/编辑 2-4 条消息就 >40%,被误判为「大范围操作」降级为不隐藏
572
+ * (用户实测:「已撤回 4 条消息 + 此操作涉及大范围对话…」= 小会话误伤)。
573
+ * 与 host 侧 ROLLBACK_MIN_SURFACE=20(surface 节点)同源:短会话豁免只在
574
+ * 大会话按比例判定。20 行 ≈ 10 轮 ≈ 20 条消息,正常撤回/编辑远小于此。
575
575
  */
576
- const SHADOW_MIN_ROWS_FOR_RATIO = 10
576
+ const SHADOW_MIN_ROWS_FOR_RATIO = 20
577
577
 
578
578
  /** 40% 降级判定(短会话豁免版):行数足够大才按比例判定。 */
579
579
  function shadowDegraded(keys, rowCount) {
@@ -494,7 +494,7 @@ return {
494
494
  });
495
495
  }
496
496
  var SHADOW_SAFETY_RATIO = 0.4;
497
- var SHADOW_MIN_ROWS_FOR_RATIO = 10;
497
+ var SHADOW_MIN_ROWS_FOR_RATIO = 20;
498
498
  function shadowDegraded(keys, rowCount) {
499
499
  return keys !== null && rowCount > SHADOW_MIN_ROWS_FOR_RATIO && keys.length / rowCount > SHADOW_SAFETY_RATIO;
500
500
  }
@@ -44,11 +44,27 @@ import { editorError } from './host-core.js'
44
44
  export const ROLLBACK_RATIO = 0.4
45
45
 
46
46
  /**
47
- * 短会话豁免阈值(2026-08-31 独立审查问题 B):surface 节点 ≤ 6(≈ 3 轮)
48
- * 的会话不做回档幅度拦截——否则 1-2 轮新会话编辑/撤回/重发 100% 被拒,
49
- * 现有功能回归。事故防御针对大会话的「编辑早期消息遮蔽几十轮」,短会话无此风险。
47
+ * 回档幅度判定(2026-09-01 重设计——不再用「占比」):
48
+ *
49
+ * 事故复盘:DSH 2.0.3 的 host Session 用「事件窗口」构造(官方 Session
50
+ * constructor(log, baseSeq) 注释「complete log or loaded event window」),
51
+ * `session.surface.nodes` 可能只是**窗口化 surface**(编辑最后一条消息时
52
+ * 只有 ~12 节点)。用占比做分母 → 12/12 = 100% → 编辑最新消息也被误拦
53
+ * (用户实测:长对话编辑最后一条报「遮蔽 100%」)。
54
+ *
55
+ * 新判定:**绝对遮蔽数**(shadowedSeqs.length,不依赖 surface 总数)——
56
+ * - ≤ ROLLBACK_MIN_SHADOWED 节点(≈ 20 轮):任何会话都允许编辑/撤回;
57
+ * - > 阈值:仅当**会话足够大**(事件数 > ROLLBACK_MIN_EVENTS)才拦
58
+ * (事故防御针对大会话的早期编辑,避免小会话误伤)。
59
+ * 分母信号:优先用 `session.events.length`(host 全量事件数,不窗口化)。
50
60
  */
51
- export const ROLLBACK_MIN_SURFACE = 6
61
+ export const ROLLBACK_MIN_SURFACE = 20
62
+
63
+ /** 绝对遮蔽阈值:遮蔽 ≤ 40 个节点(≈ 20 轮)永不在任何会话拦截。 */
64
+ export const ROLLBACK_MIN_SHADOWED = 40
65
+
66
+ /** 会话规模阈值:事件数 > 2000 才算「大会话」(短会话永不拦)。 */
67
+ export const ROLLBACK_MIN_EVENTS = 2000
52
68
 
53
69
  /**
54
70
  * 该 marker 是否来自 rollback(restore op)——rollback 本身就是「从快照点
@@ -60,15 +76,22 @@ export function isRestoreMarker(envelope) {
60
76
  return typeof id === 'string' && id.startsWith('retrace-restore-')
61
77
  }
62
78
 
63
- /** 计算一次 replace 的遮蔽占比(0..1)。无 surface/无 sourceEventSeqs/短会话 → 0(不拦截)。 */
79
+ /**
80
+ * 回档幅度判定(返回 true = 应拦截)。
81
+ * 不依赖窗口化 surface 总数:遮蔽绝对数 > 阈值 且 会话足够大 才拦。
82
+ */
64
83
  export function rollbackShareOf(session, envelope) {
65
84
  if (!session || !envelope) return 0
66
85
  const shadowed = Array.isArray(envelope.sourceEventSeqs) ? envelope.sourceEventSeqs.length : 0
67
86
  if (shadowed <= 0) return 0
68
- const surfaceNodes = session.surface?.nodes
69
- const total = Array.isArray(surfaceNodes) ? surfaceNodes.length : 0
70
- if (total <= ROLLBACK_MIN_SURFACE) return 0 // 短会话豁免(问题 B)
71
- return shadowed / total
87
+ if (shadowed <= ROLLBACK_MIN_SHADOWED) return 0 // 遮蔽 ≤ 40 节点:允许
88
+ // 会话规模:优先 events 全量,fallback surface(窗口化也够判断「大会话」)
89
+ const eventCount = Array.isArray(session.events) ? session.events.length : 0
90
+ const surfaceTotal = Array.isArray(session.surface?.nodes) ? session.surface.nodes.length : 0
91
+ const scale = Math.max(eventCount, surfaceTotal)
92
+ if (scale <= ROLLBACK_MIN_EVENTS) return 0 // 小会话豁免
93
+ // 返回占比仅作日志参考(拦截判定已由绝对阈值完成)
94
+ return shadowed / Math.max(scale, 1)
72
95
  }
73
96
 
74
97
  /** R2 —— T1 折叠自检:与 dsh-token-meter/_foldEvent 及 checks.js T1 同语义。 */
@@ -111,24 +134,24 @@ export function createMarkerGuard({ log = () => {}, prewriterFactory, enabled =
111
134
  */
112
135
  async validateMarkerAppend(session, envelope) {
113
136
  if (typeof enabled === 'function' && enabled(session?.id) === false) return { t1Ok: true }
114
- // 快照点守卫(2026-08-31 c2d05ce9 事故闭环):回档请求(遮蔽 > 阈值)
137
+ // 快照点守卫(2026-08-31 c2d05ce9 事故闭环):回档请求(遮蔽过大)
115
138
  // 拒绝落盘——生产基线不支持原地大幅改写,引导从快照点创建官方分支。
116
139
  // 在契约校验之前独立检查(业务规则,与合法性正交);host 层强制,UI 绕过也拦。
117
- // 豁免(2026-08-31 独立审查):restore(rollback 回档本身合法)与短会话(≤3 轮)。
140
+ // 豁免:restore(rollback 回档本身合法);判定用绝对遮蔽数(不依赖窗口化 surface)。
118
141
  if (
119
142
  envelope?.surfaceOp?.op === 'replace' &&
120
143
  Array.isArray(envelope.sourceEventSeqs) &&
121
144
  !isRestoreMarker(envelope)
122
145
  ) {
123
146
  const share = rollbackShareOf(session, envelope)
124
- if (share > rollbackRatio) {
125
- const pct = Math.round(share * 100)
147
+ if (share > 0) {
148
+ const shadowed = envelope.sourceEventSeqs.length
126
149
  log(
127
- `retrace: rollback guard — replace shadows ${envelope.sourceEventSeqs.length}/${session?.surface?.nodes?.length ?? '?'} surface nodes (${pct}% > ${Math.round(rollbackRatio * 100)}%); refusing in-place rewrite; guide user to fork a branch from the snapshot point`,
150
+ `retrace: rollback guard — replace shadows ${shadowed} surface nodes in a large session; refusing in-place rewrite; guide user to fork a branch from the snapshot point`,
128
151
  )
129
152
  throw editorError(
130
153
  'rollback-guide',
131
- `此操作将回档到较早的快照点(遮蔽 ${pct}% 的对话)。生产基线不支持原地大幅改写;请从快照点创建会话分支,在新分支中继续——原会话完整保留。`,
154
+ `此操作将回档到较早的快照点(遮蔽 ${shadowed} 个对话节点)。生产基线不支持原地大幅改写;请从快照点创建会话分支,在新分支中继续——原会话完整保留。`,
132
155
  )
133
156
  }
134
157
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-retrace",
3
3
  "description": "Retrace · 回溯 — Recall, edit-and-resend, regenerate, and conversation/artifact versioning (timeline, rollback, fork map) for DeepSeek Harness — Web and Desktop",
4
- "version": "0.4.14",
4
+ "version": "0.4.16",
5
5
  "packageManager": "pnpm@11.7.0",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",