dsh-retrace 0.4.13 → 0.4.15

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,6 +488,10 @@ function useMessageSeq(useSession, messageId) {
488
488
  });
489
489
  }
490
490
  var SHADOW_SAFETY_RATIO = 0.4;
491
+ var SHADOW_MIN_ROWS_FOR_RATIO = 20;
492
+ function shadowDegraded(keys, rowCount) {
493
+ return keys !== null && rowCount > SHADOW_MIN_ROWS_FOR_RATIO && keys.length / rowCount > SHADOW_SAFETY_RATIO;
494
+ }
491
495
  function hiddenKeysFor(shadowedSeqs, nodes) {
492
496
  if (!Array.isArray(shadowedSeqs) || shadowedSeqs.length === 0) return null;
493
497
  const hidden = new Set(shadowedSeqs);
@@ -515,6 +519,7 @@ var EMPTY_HIDE_PLAN = Object.freeze({
515
519
  hiddenFor: () => null,
516
520
  planFor: () => null,
517
521
  unionRatio: 0,
522
+ rowCount: 0,
518
523
  firstMarkerKey: null
519
524
  });
520
525
  var PLUGIN_PSEUDO_KINDS = /* @__PURE__ */ new Set(["user-actions", "retrace-reference", "recall-marker"]);
@@ -545,7 +550,7 @@ function useMarkerHidePlan(useSession) {
545
550
  const union = /* @__PURE__ */ new Set();
546
551
  for (const marker of markers) {
547
552
  const keys = hiddenKeysFor(marker.data?.shadowedSeqs, nodes);
548
- const degraded = keys !== null && rowCount > 0 && keys.length / rowCount > SHADOW_SAFETY_RATIO;
553
+ const degraded = shadowDegraded(keys, rowCount);
549
554
  plans.set(marker.key, { keys: degraded ? null : keys, degraded });
550
555
  if (keys !== null) for (const key of keys) union.add(key);
551
556
  }
@@ -554,6 +559,7 @@ function useMarkerHidePlan(useSession) {
554
559
  planFor: (key) => plans.get(key) ?? null,
555
560
  hiddenFor: (key) => plans.get(key)?.keys ?? null,
556
561
  unionRatio: rowCount > 0 ? union.size / rowCount : 0,
562
+ rowCount,
557
563
  firstMarkerKey: markers[0].key
558
564
  };
559
565
  return hidePlanCacheValue;
@@ -567,7 +573,7 @@ function rowHiddenByKey(snapshot, rowKey) {
567
573
  if (node.kind !== "recall-marker" || node.data?.compact) continue;
568
574
  const keys = hiddenKeysFor(node.data?.shadowedSeqs, nodes);
569
575
  if (keys === null) continue;
570
- if (rowCount > 0 && keys.length / rowCount > SHADOW_SAFETY_RATIO) continue;
576
+ if (shadowDegraded(keys, rowCount)) continue;
571
577
  if (keys.includes(rowKey)) return true;
572
578
  }
573
579
  return false;
@@ -806,7 +812,7 @@ function RecallMarkerRow({ node, useSession, t }) {
806
812
  const count = Array.isArray(shadowedSeqs) ? shadowedSeqs.length : 0;
807
813
  const label = op === "recall" ? count > 1 ? t("marker.recallMany", { count }) : t("marker.recallOne") : op === "regenerate" ? t("marker.regenerate") : t("marker.edit");
808
814
  const degradedHint = !legacy && plan?.degraded === true ? (0, import_react.createElement)("div", { key: "degraded", className: "dsh-rt-marker-hint" }, t("marker.degradedHint")) : null;
809
- 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)(
810
816
  "div",
811
817
  { key: "union", className: "dsh-rt-marker-hint" },
812
818
  t("marker.unionHint", { count: Math.round(hidePlan.unionRatio * 100) })
package/lib/client.js CHANGED
@@ -565,6 +565,21 @@ function useMessageSeq(useSession, messageId) {
565
565
  */
566
566
  const SHADOW_SAFETY_RATIO = 0.4
567
567
 
568
+ /**
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
+ */
576
+ const SHADOW_MIN_ROWS_FOR_RATIO = 20
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
+
568
583
  /**
569
584
  * Every chat-node key that should disappear when `shadowedSeqs` are recalled:
570
585
  * the shadowed message rows themselves, plus the per-turn action row (copy /
@@ -617,6 +632,7 @@ const EMPTY_HIDE_PLAN = Object.freeze({
617
632
  hiddenFor: () => null,
618
633
  planFor: () => null,
619
634
  unionRatio: 0,
635
+ rowCount: 0,
620
636
  firstMarkerKey: null,
621
637
  })
622
638
 
@@ -660,7 +676,7 @@ function useMarkerHidePlan(useSession) {
660
676
  const union = new Set()
661
677
  for (const marker of markers) {
662
678
  const keys = hiddenKeysFor(marker.data?.shadowedSeqs, nodes)
663
- const degraded = keys !== null && rowCount > 0 && keys.length / rowCount > SHADOW_SAFETY_RATIO
679
+ const degraded = shadowDegraded(keys, rowCount)
664
680
  plans.set(marker.key, { keys: degraded ? null : keys, degraded })
665
681
  if (keys !== null) for (const key of keys) union.add(key)
666
682
  }
@@ -669,6 +685,7 @@ function useMarkerHidePlan(useSession) {
669
685
  planFor: (key) => plans.get(key) ?? null,
670
686
  hiddenFor: (key) => plans.get(key)?.keys ?? null,
671
687
  unionRatio: rowCount > 0 ? union.size / rowCount : 0,
688
+ rowCount,
672
689
  firstMarkerKey: markers[0].key,
673
690
  }
674
691
  return hidePlanCacheValue
@@ -689,7 +706,7 @@ function rowHiddenByKey(snapshot, rowKey) {
689
706
  if (node.kind !== 'recall-marker' || node.data?.compact) continue // compact markers never hide rows
690
707
  const keys = hiddenKeysFor(node.data?.shadowedSeqs, nodes)
691
708
  if (keys === null) continue
692
- if (rowCount > 0 && keys.length / rowCount > SHADOW_SAFETY_RATIO) continue // degraded: hides nothing
709
+ if (shadowDegraded(keys, rowCount)) continue // degraded: hides nothing
693
710
  if (keys.includes(rowKey)) return true
694
711
  }
695
712
  return false
@@ -996,7 +1013,7 @@ function RecallMarkerRow({ node, useSession, t }) {
996
1013
  const degradedHint = !legacy && plan?.degraded === true
997
1014
  ? createElement('div', { key: 'degraded', className: 'dsh-rt-marker-hint' }, t('marker.degradedHint'))
998
1015
  : null
999
- 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
1000
1017
  ? createElement('div', { key: 'union', className: 'dsh-rt-marker-hint' },
1001
1018
  t('marker.unionHint', { count: Math.round(hidePlan.unionRatio * 100) }))
1002
1019
  : null
@@ -494,6 +494,10 @@ return {
494
494
  });
495
495
  }
496
496
  var SHADOW_SAFETY_RATIO = 0.4;
497
+ var SHADOW_MIN_ROWS_FOR_RATIO = 20;
498
+ function shadowDegraded(keys, rowCount) {
499
+ return keys !== null && rowCount > SHADOW_MIN_ROWS_FOR_RATIO && keys.length / rowCount > SHADOW_SAFETY_RATIO;
500
+ }
497
501
  function hiddenKeysFor(shadowedSeqs, nodes) {
498
502
  if (!Array.isArray(shadowedSeqs) || shadowedSeqs.length === 0) return null;
499
503
  const hidden = new Set(shadowedSeqs);
@@ -521,6 +525,7 @@ return {
521
525
  hiddenFor: () => null,
522
526
  planFor: () => null,
523
527
  unionRatio: 0,
528
+ rowCount: 0,
524
529
  firstMarkerKey: null
525
530
  });
526
531
  var PLUGIN_PSEUDO_KINDS = /* @__PURE__ */ new Set(["user-actions", "retrace-reference", "recall-marker"]);
@@ -551,7 +556,7 @@ return {
551
556
  const union = /* @__PURE__ */ new Set();
552
557
  for (const marker of markers) {
553
558
  const keys = hiddenKeysFor(marker.data?.shadowedSeqs, nodes);
554
- const degraded = keys !== null && rowCount > 0 && keys.length / rowCount > SHADOW_SAFETY_RATIO;
559
+ const degraded = shadowDegraded(keys, rowCount);
555
560
  plans.set(marker.key, { keys: degraded ? null : keys, degraded });
556
561
  if (keys !== null) for (const key of keys) union.add(key);
557
562
  }
@@ -560,6 +565,7 @@ return {
560
565
  planFor: (key) => plans.get(key) ?? null,
561
566
  hiddenFor: (key) => plans.get(key)?.keys ?? null,
562
567
  unionRatio: rowCount > 0 ? union.size / rowCount : 0,
568
+ rowCount,
563
569
  firstMarkerKey: markers[0].key
564
570
  };
565
571
  return hidePlanCacheValue;
@@ -573,7 +579,7 @@ return {
573
579
  if (node.kind !== "recall-marker" || node.data?.compact) continue;
574
580
  const keys = hiddenKeysFor(node.data?.shadowedSeqs, nodes);
575
581
  if (keys === null) continue;
576
- if (rowCount > 0 && keys.length / rowCount > SHADOW_SAFETY_RATIO) continue;
582
+ if (shadowDegraded(keys, rowCount)) continue;
577
583
  if (keys.includes(rowKey)) return true;
578
584
  }
579
585
  return false;
@@ -812,7 +818,7 @@ return {
812
818
  const count = Array.isArray(shadowedSeqs) ? shadowedSeqs.length : 0;
813
819
  const label = op === "recall" ? count > 1 ? t("marker.recallMany", { count }) : t("marker.recallOne") : op === "regenerate" ? t("marker.regenerate") : t("marker.edit");
814
820
  const degradedHint = !legacy && plan?.degraded === true ? (0, import_react.createElement)("div", { key: "degraded", className: "dsh-rt-marker-hint" }, t("marker.degradedHint")) : null;
815
- 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)(
816
822
  "div",
817
823
  { key: "union", className: "dsh-rt-marker-hint" },
818
824
  t("marker.unionHint", { count: Math.round(hidePlan.unionRatio * 100) })
@@ -44,11 +44,13 @@ 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-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 同源。
50
52
  */
51
- export const ROLLBACK_MIN_SURFACE = 6
53
+ export const ROLLBACK_MIN_SURFACE = 20
52
54
 
53
55
  /**
54
56
  * 该 marker 是否来自 rollback(restore op)——rollback 本身就是「从快照点
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.13",
4
+ "version": "0.4.15",
5
5
  "packageManager": "pnpm@11.7.0",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",