dsh-retrace 0.4.12 → 0.4.14
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 +12 -4
- package/lib/client.js +23 -4
- package/lib/dynamic-client.js +12 -4
- package/lib/dynamic-host.js +4 -0
- package/lib/host-core.js +4 -0
- package/package.json +2 -2
package/lib/client.bundle.js
CHANGED
|
@@ -457,7 +457,9 @@ var recallMarkerDefinition = {
|
|
|
457
457
|
// the action-row suppression check (useShadowed) but empty for legacy
|
|
458
458
|
// so no row is hidden and no action row is suppressed for legacy
|
|
459
459
|
// markers (rename must never make visible content disappear).
|
|
460
|
-
|
|
460
|
+
// 2026-09-01:优先读 data.shadowedSeqs(host 冗余写入),fallback 顶层
|
|
461
|
+
// sourceEventSeqs——部分客户端事件管道剥离顶层字段导致隐藏失效。
|
|
462
|
+
shadowedSeqs: legacy ? [] : Array.isArray(event.data?.shadowedSeqs) ? event.data.shadowedSeqs.slice() : Array.isArray(event.sourceEventSeqs) ? event.sourceEventSeqs.slice() : [],
|
|
461
463
|
targetSeq: event.data?.editor?.targetSeq,
|
|
462
464
|
text: event.data?.editor?.text
|
|
463
465
|
};
|
|
@@ -486,6 +488,10 @@ function useMessageSeq(useSession, messageId) {
|
|
|
486
488
|
});
|
|
487
489
|
}
|
|
488
490
|
var SHADOW_SAFETY_RATIO = 0.4;
|
|
491
|
+
var SHADOW_MIN_ROWS_FOR_RATIO = 10;
|
|
492
|
+
function shadowDegraded(keys, rowCount) {
|
|
493
|
+
return keys !== null && rowCount > SHADOW_MIN_ROWS_FOR_RATIO && keys.length / rowCount > SHADOW_SAFETY_RATIO;
|
|
494
|
+
}
|
|
489
495
|
function hiddenKeysFor(shadowedSeqs, nodes) {
|
|
490
496
|
if (!Array.isArray(shadowedSeqs) || shadowedSeqs.length === 0) return null;
|
|
491
497
|
const hidden = new Set(shadowedSeqs);
|
|
@@ -513,6 +519,7 @@ var EMPTY_HIDE_PLAN = Object.freeze({
|
|
|
513
519
|
hiddenFor: () => null,
|
|
514
520
|
planFor: () => null,
|
|
515
521
|
unionRatio: 0,
|
|
522
|
+
rowCount: 0,
|
|
516
523
|
firstMarkerKey: null
|
|
517
524
|
});
|
|
518
525
|
var PLUGIN_PSEUDO_KINDS = /* @__PURE__ */ new Set(["user-actions", "retrace-reference", "recall-marker"]);
|
|
@@ -543,7 +550,7 @@ function useMarkerHidePlan(useSession) {
|
|
|
543
550
|
const union = /* @__PURE__ */ new Set();
|
|
544
551
|
for (const marker of markers) {
|
|
545
552
|
const keys = hiddenKeysFor(marker.data?.shadowedSeqs, nodes);
|
|
546
|
-
const degraded = keys
|
|
553
|
+
const degraded = shadowDegraded(keys, rowCount);
|
|
547
554
|
plans.set(marker.key, { keys: degraded ? null : keys, degraded });
|
|
548
555
|
if (keys !== null) for (const key of keys) union.add(key);
|
|
549
556
|
}
|
|
@@ -552,6 +559,7 @@ function useMarkerHidePlan(useSession) {
|
|
|
552
559
|
planFor: (key) => plans.get(key) ?? null,
|
|
553
560
|
hiddenFor: (key) => plans.get(key)?.keys ?? null,
|
|
554
561
|
unionRatio: rowCount > 0 ? union.size / rowCount : 0,
|
|
562
|
+
rowCount,
|
|
555
563
|
firstMarkerKey: markers[0].key
|
|
556
564
|
};
|
|
557
565
|
return hidePlanCacheValue;
|
|
@@ -565,7 +573,7 @@ function rowHiddenByKey(snapshot, rowKey) {
|
|
|
565
573
|
if (node.kind !== "recall-marker" || node.data?.compact) continue;
|
|
566
574
|
const keys = hiddenKeysFor(node.data?.shadowedSeqs, nodes);
|
|
567
575
|
if (keys === null) continue;
|
|
568
|
-
if (
|
|
576
|
+
if (shadowDegraded(keys, rowCount)) continue;
|
|
569
577
|
if (keys.includes(rowKey)) return true;
|
|
570
578
|
}
|
|
571
579
|
return false;
|
|
@@ -804,7 +812,7 @@ function RecallMarkerRow({ node, useSession, t }) {
|
|
|
804
812
|
const count = Array.isArray(shadowedSeqs) ? shadowedSeqs.length : 0;
|
|
805
813
|
const label = op === "recall" ? count > 1 ? t("marker.recallMany", { count }) : t("marker.recallOne") : op === "regenerate" ? t("marker.regenerate") : t("marker.edit");
|
|
806
814
|
const degradedHint = !legacy && plan?.degraded === true ? (0, import_react.createElement)("div", { key: "degraded", className: "dsh-rt-marker-hint" }, t("marker.degradedHint")) : null;
|
|
807
|
-
const unionHint = !legacy && hidePlan.firstMarkerKey === node.key && hidePlan.unionRatio > SHADOW_SAFETY_RATIO ? (0, import_react.createElement)(
|
|
815
|
+
const unionHint = !legacy && hidePlan.firstMarkerKey === node.key && hidePlan.rowCount > SHADOW_MIN_ROWS_FOR_RATIO && hidePlan.unionRatio > SHADOW_SAFETY_RATIO ? (0, import_react.createElement)(
|
|
808
816
|
"div",
|
|
809
817
|
{ key: "union", className: "dsh-rt-marker-hint" },
|
|
810
818
|
t("marker.unionHint", { count: Math.round(hidePlan.unionRatio * 100) })
|
package/lib/client.js
CHANGED
|
@@ -508,7 +508,9 @@ const recallMarkerDefinition = {
|
|
|
508
508
|
// the action-row suppression check (useShadowed) but empty for legacy
|
|
509
509
|
// so no row is hidden and no action row is suppressed for legacy
|
|
510
510
|
// markers (rename must never make visible content disappear).
|
|
511
|
-
|
|
511
|
+
// 2026-09-01:优先读 data.shadowedSeqs(host 冗余写入),fallback 顶层
|
|
512
|
+
// sourceEventSeqs——部分客户端事件管道剥离顶层字段导致隐藏失效。
|
|
513
|
+
shadowedSeqs: legacy ? [] : (Array.isArray(event.data?.shadowedSeqs) ? event.data.shadowedSeqs.slice() : (Array.isArray(event.sourceEventSeqs) ? event.sourceEventSeqs.slice() : [])),
|
|
512
514
|
targetSeq: event.data?.editor?.targetSeq,
|
|
513
515
|
text: event.data?.editor?.text,
|
|
514
516
|
}
|
|
@@ -563,6 +565,21 @@ function useMessageSeq(useSession, messageId) {
|
|
|
563
565
|
*/
|
|
564
566
|
const SHADOW_SAFETY_RATIO = 0.4
|
|
565
567
|
|
|
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 轮,正常撤回/编辑远小于此。
|
|
575
|
+
*/
|
|
576
|
+
const SHADOW_MIN_ROWS_FOR_RATIO = 10
|
|
577
|
+
|
|
578
|
+
/** 40% 降级判定(短会话豁免版):行数足够大才按比例判定。 */
|
|
579
|
+
function shadowDegraded(keys, rowCount) {
|
|
580
|
+
return keys !== null && rowCount > SHADOW_MIN_ROWS_FOR_RATIO && keys.length / rowCount > SHADOW_SAFETY_RATIO
|
|
581
|
+
}
|
|
582
|
+
|
|
566
583
|
/**
|
|
567
584
|
* Every chat-node key that should disappear when `shadowedSeqs` are recalled:
|
|
568
585
|
* the shadowed message rows themselves, plus the per-turn action row (copy /
|
|
@@ -615,6 +632,7 @@ const EMPTY_HIDE_PLAN = Object.freeze({
|
|
|
615
632
|
hiddenFor: () => null,
|
|
616
633
|
planFor: () => null,
|
|
617
634
|
unionRatio: 0,
|
|
635
|
+
rowCount: 0,
|
|
618
636
|
firstMarkerKey: null,
|
|
619
637
|
})
|
|
620
638
|
|
|
@@ -658,7 +676,7 @@ function useMarkerHidePlan(useSession) {
|
|
|
658
676
|
const union = new Set()
|
|
659
677
|
for (const marker of markers) {
|
|
660
678
|
const keys = hiddenKeysFor(marker.data?.shadowedSeqs, nodes)
|
|
661
|
-
const degraded = keys
|
|
679
|
+
const degraded = shadowDegraded(keys, rowCount)
|
|
662
680
|
plans.set(marker.key, { keys: degraded ? null : keys, degraded })
|
|
663
681
|
if (keys !== null) for (const key of keys) union.add(key)
|
|
664
682
|
}
|
|
@@ -667,6 +685,7 @@ function useMarkerHidePlan(useSession) {
|
|
|
667
685
|
planFor: (key) => plans.get(key) ?? null,
|
|
668
686
|
hiddenFor: (key) => plans.get(key)?.keys ?? null,
|
|
669
687
|
unionRatio: rowCount > 0 ? union.size / rowCount : 0,
|
|
688
|
+
rowCount,
|
|
670
689
|
firstMarkerKey: markers[0].key,
|
|
671
690
|
}
|
|
672
691
|
return hidePlanCacheValue
|
|
@@ -687,7 +706,7 @@ function rowHiddenByKey(snapshot, rowKey) {
|
|
|
687
706
|
if (node.kind !== 'recall-marker' || node.data?.compact) continue // compact markers never hide rows
|
|
688
707
|
const keys = hiddenKeysFor(node.data?.shadowedSeqs, nodes)
|
|
689
708
|
if (keys === null) continue
|
|
690
|
-
if (
|
|
709
|
+
if (shadowDegraded(keys, rowCount)) continue // degraded: hides nothing
|
|
691
710
|
if (keys.includes(rowKey)) return true
|
|
692
711
|
}
|
|
693
712
|
return false
|
|
@@ -994,7 +1013,7 @@ function RecallMarkerRow({ node, useSession, t }) {
|
|
|
994
1013
|
const degradedHint = !legacy && plan?.degraded === true
|
|
995
1014
|
? createElement('div', { key: 'degraded', className: 'dsh-rt-marker-hint' }, t('marker.degradedHint'))
|
|
996
1015
|
: null
|
|
997
|
-
const unionHint = !legacy && hidePlan.firstMarkerKey === node.key && hidePlan.unionRatio > SHADOW_SAFETY_RATIO
|
|
1016
|
+
const unionHint = !legacy && hidePlan.firstMarkerKey === node.key && hidePlan.rowCount > SHADOW_MIN_ROWS_FOR_RATIO && hidePlan.unionRatio > SHADOW_SAFETY_RATIO
|
|
998
1017
|
? createElement('div', { key: 'union', className: 'dsh-rt-marker-hint' },
|
|
999
1018
|
t('marker.unionHint', { count: Math.round(hidePlan.unionRatio * 100) }))
|
|
1000
1019
|
: null
|
package/lib/dynamic-client.js
CHANGED
|
@@ -463,7 +463,9 @@ return {
|
|
|
463
463
|
// the action-row suppression check (useShadowed) but empty for legacy
|
|
464
464
|
// so no row is hidden and no action row is suppressed for legacy
|
|
465
465
|
// markers (rename must never make visible content disappear).
|
|
466
|
-
|
|
466
|
+
// 2026-09-01:优先读 data.shadowedSeqs(host 冗余写入),fallback 顶层
|
|
467
|
+
// sourceEventSeqs——部分客户端事件管道剥离顶层字段导致隐藏失效。
|
|
468
|
+
shadowedSeqs: legacy ? [] : Array.isArray(event.data?.shadowedSeqs) ? event.data.shadowedSeqs.slice() : Array.isArray(event.sourceEventSeqs) ? event.sourceEventSeqs.slice() : [],
|
|
467
469
|
targetSeq: event.data?.editor?.targetSeq,
|
|
468
470
|
text: event.data?.editor?.text
|
|
469
471
|
};
|
|
@@ -492,6 +494,10 @@ return {
|
|
|
492
494
|
});
|
|
493
495
|
}
|
|
494
496
|
var SHADOW_SAFETY_RATIO = 0.4;
|
|
497
|
+
var SHADOW_MIN_ROWS_FOR_RATIO = 10;
|
|
498
|
+
function shadowDegraded(keys, rowCount) {
|
|
499
|
+
return keys !== null && rowCount > SHADOW_MIN_ROWS_FOR_RATIO && keys.length / rowCount > SHADOW_SAFETY_RATIO;
|
|
500
|
+
}
|
|
495
501
|
function hiddenKeysFor(shadowedSeqs, nodes) {
|
|
496
502
|
if (!Array.isArray(shadowedSeqs) || shadowedSeqs.length === 0) return null;
|
|
497
503
|
const hidden = new Set(shadowedSeqs);
|
|
@@ -519,6 +525,7 @@ return {
|
|
|
519
525
|
hiddenFor: () => null,
|
|
520
526
|
planFor: () => null,
|
|
521
527
|
unionRatio: 0,
|
|
528
|
+
rowCount: 0,
|
|
522
529
|
firstMarkerKey: null
|
|
523
530
|
});
|
|
524
531
|
var PLUGIN_PSEUDO_KINDS = /* @__PURE__ */ new Set(["user-actions", "retrace-reference", "recall-marker"]);
|
|
@@ -549,7 +556,7 @@ return {
|
|
|
549
556
|
const union = /* @__PURE__ */ new Set();
|
|
550
557
|
for (const marker of markers) {
|
|
551
558
|
const keys = hiddenKeysFor(marker.data?.shadowedSeqs, nodes);
|
|
552
|
-
const degraded = keys
|
|
559
|
+
const degraded = shadowDegraded(keys, rowCount);
|
|
553
560
|
plans.set(marker.key, { keys: degraded ? null : keys, degraded });
|
|
554
561
|
if (keys !== null) for (const key of keys) union.add(key);
|
|
555
562
|
}
|
|
@@ -558,6 +565,7 @@ return {
|
|
|
558
565
|
planFor: (key) => plans.get(key) ?? null,
|
|
559
566
|
hiddenFor: (key) => plans.get(key)?.keys ?? null,
|
|
560
567
|
unionRatio: rowCount > 0 ? union.size / rowCount : 0,
|
|
568
|
+
rowCount,
|
|
561
569
|
firstMarkerKey: markers[0].key
|
|
562
570
|
};
|
|
563
571
|
return hidePlanCacheValue;
|
|
@@ -571,7 +579,7 @@ return {
|
|
|
571
579
|
if (node.kind !== "recall-marker" || node.data?.compact) continue;
|
|
572
580
|
const keys = hiddenKeysFor(node.data?.shadowedSeqs, nodes);
|
|
573
581
|
if (keys === null) continue;
|
|
574
|
-
if (
|
|
582
|
+
if (shadowDegraded(keys, rowCount)) continue;
|
|
575
583
|
if (keys.includes(rowKey)) return true;
|
|
576
584
|
}
|
|
577
585
|
return false;
|
|
@@ -810,7 +818,7 @@ return {
|
|
|
810
818
|
const count = Array.isArray(shadowedSeqs) ? shadowedSeqs.length : 0;
|
|
811
819
|
const label = op === "recall" ? count > 1 ? t("marker.recallMany", { count }) : t("marker.recallOne") : op === "regenerate" ? t("marker.regenerate") : t("marker.edit");
|
|
812
820
|
const degradedHint = !legacy && plan?.degraded === true ? (0, import_react.createElement)("div", { key: "degraded", className: "dsh-rt-marker-hint" }, t("marker.degradedHint")) : null;
|
|
813
|
-
const unionHint = !legacy && hidePlan.firstMarkerKey === node.key && hidePlan.unionRatio > SHADOW_SAFETY_RATIO ? (0, import_react.createElement)(
|
|
821
|
+
const unionHint = !legacy && hidePlan.firstMarkerKey === node.key && hidePlan.rowCount > SHADOW_MIN_ROWS_FOR_RATIO && hidePlan.unionRatio > SHADOW_SAFETY_RATIO ? (0, import_react.createElement)(
|
|
814
822
|
"div",
|
|
815
823
|
{ key: "union", className: "dsh-rt-marker-hint" },
|
|
816
824
|
t("marker.unionHint", { count: Math.round(hidePlan.unionRatio * 100) })
|
package/lib/dynamic-host.js
CHANGED
|
@@ -142,6 +142,10 @@ return {
|
|
|
142
142
|
}
|
|
143
143
|
const surfaceOp = { op: 'replace', start: span.start, end: span.end }
|
|
144
144
|
const sourceEventSeqs = span.shadowedSeqs.slice()
|
|
145
|
+
// 2026-09-01:把遮蔽 seq 冗余进 data.shadowedSeqs——部分客户端事件管道会剥离
|
|
146
|
+
// event 顶层 sourceEventSeqs(0.4.12 实测:撤回/编辑后消息不隐藏 = client 拿不到
|
|
147
|
+
// shadowedSeqs)。data 里的字段随事件体持久化,client 读 data 更稳(fallback 顶层)。
|
|
148
|
+
data.shadowedSeqs = sourceEventSeqs
|
|
145
149
|
if (typeof validate === 'function') {
|
|
146
150
|
const result = await validate(session, { type: 'assistant/message', data, surfaceOp, sourceEventSeqs })
|
|
147
151
|
if (result && result.t1Ok === false) {
|
package/lib/host-core.js
CHANGED
|
@@ -133,6 +133,10 @@ export async function appendEditorMarker(session, span, op, targetSeq, originalT
|
|
|
133
133
|
}
|
|
134
134
|
const surfaceOp = { op: 'replace', start: span.start, end: span.end }
|
|
135
135
|
const sourceEventSeqs = span.shadowedSeqs.slice()
|
|
136
|
+
// 2026-09-01:把遮蔽 seq 冗余进 data.shadowedSeqs——部分客户端事件管道会剥离
|
|
137
|
+
// event 顶层 sourceEventSeqs(0.4.12 实测:撤回/编辑后消息不隐藏 = client 拿不到
|
|
138
|
+
// shadowedSeqs)。data 里的字段随事件体持久化,client 读 data 更稳(fallback 顶层)。
|
|
139
|
+
data.shadowedSeqs = sourceEventSeqs
|
|
136
140
|
if (typeof validate === 'function') {
|
|
137
141
|
const result = await validate(session, { type: 'assistant/message', data, surfaceOp, sourceEventSeqs })
|
|
138
142
|
if (result && result.t1Ok === false) {
|
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.14",
|
|
5
5
|
"packageManager": "pnpm@11.7.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"react": "^18.2.0"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
|
-
"dsh-log-contract": "^0.3.
|
|
88
|
+
"dsh-log-contract": "^0.3.7",
|
|
89
89
|
"zod": "^4.4.3"
|
|
90
90
|
},
|
|
91
91
|
"repository": {
|