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.
- package/lib/client.bundle.js +1 -1
- package/lib/client.js +7 -7
- package/lib/dynamic-client.js +1 -1
- package/lib/prewrite-guard.js +38 -15
- package/package.json +1 -1
package/lib/client.bundle.js
CHANGED
|
@@ -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 =
|
|
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
|
-
*
|
|
571
|
-
* 2-4 条消息就 >40
|
|
572
|
-
*
|
|
573
|
-
* 与 host 侧 ROLLBACK_MIN_SURFACE=
|
|
574
|
-
* 大会话按比例判定。
|
|
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 =
|
|
576
|
+
const SHADOW_MIN_ROWS_FOR_RATIO = 20
|
|
577
577
|
|
|
578
578
|
/** 40% 降级判定(短会话豁免版):行数足够大才按比例判定。 */
|
|
579
579
|
function shadowDegraded(keys, rowCount) {
|
package/lib/dynamic-client.js
CHANGED
|
@@ -494,7 +494,7 @@ return {
|
|
|
494
494
|
});
|
|
495
495
|
}
|
|
496
496
|
var SHADOW_SAFETY_RATIO = 0.4;
|
|
497
|
-
var SHADOW_MIN_ROWS_FOR_RATIO =
|
|
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
|
}
|
package/lib/prewrite-guard.js
CHANGED
|
@@ -44,11 +44,27 @@ import { editorError } from './host-core.js'
|
|
|
44
44
|
export const ROLLBACK_RATIO = 0.4
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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 =
|
|
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
|
-
/**
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
//
|
|
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 >
|
|
125
|
-
const
|
|
147
|
+
if (share > 0) {
|
|
148
|
+
const shadowed = envelope.sourceEventSeqs.length
|
|
126
149
|
log(
|
|
127
|
-
`retrace: rollback guard — replace shadows ${
|
|
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
|
-
`此操作将回档到较早的快照点(遮蔽 ${
|
|
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.
|
|
4
|
+
"version": "0.4.16",
|
|
5
5
|
"packageManager": "pnpm@11.7.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|