dsh-retrace 0.4.15 → 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.
@@ -44,14 +44,28 @@ import { editorError } from './host-core.js'
44
44
  export const ROLLBACK_RATIO = 0.4
45
45
 
46
46
  /**
47
- * 短会话豁免阈值(2026-08-31 独立审查问题 B,2026-09-01 用户实测调大):
48
- * surface 节点 ≤ 20(≈ 10 轮 ≈ 20 条消息)的会话不做回档幅度拦截——
49
- * 否则 4-7 轮小会话撤回/编辑 100% 被拒(用户实测「一共没几个对话,点击就提示
50
- * 遮蔽 100%」)。事故防御针对**大会话**的「编辑早期消息遮蔽几十轮」,
51
- * 20 条以内的小会话无此风险。与 client 侧 SHADOW_MIN_ROWS_FOR_RATIO 同源。
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 全量事件数,不窗口化)。
52
60
  */
53
61
  export const ROLLBACK_MIN_SURFACE = 20
54
62
 
63
+ /** 绝对遮蔽阈值:遮蔽 ≤ 40 个节点(≈ 20 轮)永不在任何会话拦截。 */
64
+ export const ROLLBACK_MIN_SHADOWED = 40
65
+
66
+ /** 会话规模阈值:事件数 > 2000 才算「大会话」(短会话永不拦)。 */
67
+ export const ROLLBACK_MIN_EVENTS = 2000
68
+
55
69
  /**
56
70
  * 该 marker 是否来自 rollback(restore op)——rollback 本身就是「从快照点
57
71
  * 回档」的官方机制,不应再被回档幅度守卫拦截(否则大范围回档永远不可用,
@@ -62,15 +76,22 @@ export function isRestoreMarker(envelope) {
62
76
  return typeof id === 'string' && id.startsWith('retrace-restore-')
63
77
  }
64
78
 
65
- /** 计算一次 replace 的遮蔽占比(0..1)。无 surface/无 sourceEventSeqs/短会话 → 0(不拦截)。 */
79
+ /**
80
+ * 回档幅度判定(返回 true = 应拦截)。
81
+ * 不依赖窗口化 surface 总数:遮蔽绝对数 > 阈值 且 会话足够大 才拦。
82
+ */
66
83
  export function rollbackShareOf(session, envelope) {
67
84
  if (!session || !envelope) return 0
68
85
  const shadowed = Array.isArray(envelope.sourceEventSeqs) ? envelope.sourceEventSeqs.length : 0
69
86
  if (shadowed <= 0) return 0
70
- const surfaceNodes = session.surface?.nodes
71
- const total = Array.isArray(surfaceNodes) ? surfaceNodes.length : 0
72
- if (total <= ROLLBACK_MIN_SURFACE) return 0 // 短会话豁免(问题 B)
73
- 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)
74
95
  }
75
96
 
76
97
  /** R2 —— T1 折叠自检:与 dsh-token-meter/_foldEvent 及 checks.js T1 同语义。 */
@@ -113,24 +134,24 @@ export function createMarkerGuard({ log = () => {}, prewriterFactory, enabled =
113
134
  */
114
135
  async validateMarkerAppend(session, envelope) {
115
136
  if (typeof enabled === 'function' && enabled(session?.id) === false) return { t1Ok: true }
116
- // 快照点守卫(2026-08-31 c2d05ce9 事故闭环):回档请求(遮蔽 > 阈值)
137
+ // 快照点守卫(2026-08-31 c2d05ce9 事故闭环):回档请求(遮蔽过大)
117
138
  // 拒绝落盘——生产基线不支持原地大幅改写,引导从快照点创建官方分支。
118
139
  // 在契约校验之前独立检查(业务规则,与合法性正交);host 层强制,UI 绕过也拦。
119
- // 豁免(2026-08-31 独立审查):restore(rollback 回档本身合法)与短会话(≤3 轮)。
140
+ // 豁免:restore(rollback 回档本身合法);判定用绝对遮蔽数(不依赖窗口化 surface)。
120
141
  if (
121
142
  envelope?.surfaceOp?.op === 'replace' &&
122
143
  Array.isArray(envelope.sourceEventSeqs) &&
123
144
  !isRestoreMarker(envelope)
124
145
  ) {
125
146
  const share = rollbackShareOf(session, envelope)
126
- if (share > rollbackRatio) {
127
- const pct = Math.round(share * 100)
147
+ if (share > 0) {
148
+ const shadowed = envelope.sourceEventSeqs.length
128
149
  log(
129
- `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`,
130
151
  )
131
152
  throw editorError(
132
153
  'rollback-guide',
133
- `此操作将回档到较早的快照点(遮蔽 ${pct}% 的对话)。生产基线不支持原地大幅改写;请从快照点创建会话分支,在新分支中继续——原会话完整保留。`,
154
+ `此操作将回档到较早的快照点(遮蔽 ${shadowed} 个对话节点)。生产基线不支持原地大幅改写;请从快照点创建会话分支,在新分支中继续——原会话完整保留。`,
134
155
  )
135
156
  }
136
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.15",
4
+ "version": "0.4.16",
5
5
  "packageManager": "pnpm@11.7.0",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",