dsh-rewind-plugin 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  DeepSeek Harness 插件:**同一会话窗口的 in-place 对话回退**(Claude Code `/rewind` 语义)。主交互为**用户消息旁的「回退」按钮**,点击后选择回退模式;命令仅作辅助。
4
4
 
5
- > 状态:v0.1.2 已实现并发布(`dsh-rewind-plugin`,npm + GitHub Actions Trusted Publishing)。交互以 Claude Code 行为为参考,并贴合 dsh Web 实际 UI(利用现有 DOM 锚点与运行时快照,纯插件、不改仓库核心)。
5
+ > 状态:v0.1.4 已实现并发布(`dsh-rewind-plugin`,npm + GitHub Actions Trusted Publishing)。交互以 Claude Code 行为为参考,并贴合 dsh Web 实际 UI(利用现有 DOM 锚点与运行时快照,纯插件、不改仓库核心)。
6
6
 
7
- ## 实现状态(v0.1.2
7
+ ## 实现状态(v0.1.4
8
8
 
9
9
  - ✅ host 端 `/rewind` 命令(两步文本引导 + 直接执行 + `preview` 影响清单)
10
10
  - ✅ host 端变更台账(`tools/execute` 捕获 before、`tools/post-execute` 提交),按会话隔离
@@ -73,8 +73,9 @@ git push --tags # push v<version> tag → 触发 .github/workflows/publish.
73
73
  提示"已撤回 seq N,可重新发送"。
74
74
  - 键盘流:`/rewind` → 选消息 → `/rewind <序号> chat|both`;`/rewind preview <目标>`
75
75
  只输出影响清单不执行。
76
- - 回退后:模型上下文从目标消息重新开始;会话日志与可见对话完整保留
77
- (append-only);标记节点不渲染为气泡(非 append surface 事件),结果以命令节点呈现。
76
+ - **回退后前端与 Agent 一致**:回退/撤回后,client 端自动隐藏回退标记、`/rewind`
77
+ 命令结果以及被回退范围内的消息(含 agent 回复),可见对话回到回退点之前的样子;
78
+ 之后新发的消息正常显示。会话日志(append-only 审计)不受影响。
78
79
 
79
80
  ## 已知限制(v0.1)
80
81
 
package/lib/client.js CHANGED
@@ -385,10 +385,43 @@ var name = "dsh-rewind";
385
385
  var inject = ["sessions", "locale"];
386
386
  var NS = "rewind";
387
387
  var USER_SEAT_SELECTOR = '[data-chat-flow-kind="user"]';
388
+ var CHAT_SEAT_SELECTOR = "[data-chat-anchor-key]";
388
389
  var ACTIONS_ROOT_SELECTOR = "[data-time-hover-root]";
390
+ var MARKER_TEXT = "__DSH_REWIND__";
391
+ function targetOfOutcome(text) {
392
+ if (text === void 0) return void 0;
393
+ const match = text.match(/seq (\d+)/);
394
+ return match !== null ? Number(match[1]) : void 0;
395
+ }
389
396
  function messagePreviewOf(node) {
390
397
  return node.content.map((block) => block.type === "text" && typeof block.text === "string" ? block.text : "").join("").replace(/\s+/g, " ").trim().slice(0, 80);
391
398
  }
399
+ function hiddenSeqsOf(session) {
400
+ const hidden = /* @__PURE__ */ new Set();
401
+ const snap = session.getSnapshot();
402
+ let latest = null;
403
+ for (const key of snap.chat.order) {
404
+ const node = snap.chat.nodes.get(key);
405
+ if (node === void 0 || node.kind !== "command") continue;
406
+ const command = node.data;
407
+ if (command.name !== "rewind") continue;
408
+ hidden.add(command.seq);
409
+ const marker = command.outcome?.sourceEventSeq;
410
+ const target = targetOfOutcome(command.outcome?.text);
411
+ if (marker !== void 0 && target !== void 0 && (latest === null || marker > latest.marker)) {
412
+ latest = { marker, target };
413
+ }
414
+ }
415
+ if (latest !== null) {
416
+ for (const key of snap.chat.order) {
417
+ const node = snap.chat.nodes.get(key);
418
+ if (node === void 0) continue;
419
+ const anchor = node.anchorSeq;
420
+ if (anchor > latest.target && anchor <= latest.marker) hidden.add(anchor);
421
+ }
422
+ }
423
+ return hidden;
424
+ }
392
425
  function apply(ctx) {
393
426
  ctx.effect(function* () {
394
427
  yield ctx.locale.register(NS, { zh, en });
@@ -398,6 +431,7 @@ function apply(ctx) {
398
431
  style.textContent = STYLE;
399
432
  document.head.appendChild(style);
400
433
  const attached = /* @__PURE__ */ new WeakSet();
434
+ const hidden = /* @__PURE__ */ new WeakSet();
401
435
  const buttons = /* @__PURE__ */ new Map();
402
436
  let observer = null;
403
437
  const sessionFor = () => {
@@ -449,7 +483,23 @@ function apply(ctx) {
449
483
  for (const [key, button] of buttons) {
450
484
  if (!button.isConnected) buttons.delete(key);
451
485
  }
452
- for (const seat of document.querySelectorAll(USER_SEAT_SELECTOR)) attach(seat);
486
+ const session = sessionFor();
487
+ const hiddenSeqs = session !== void 0 ? hiddenSeqsOf(session) : /* @__PURE__ */ new Set();
488
+ for (const seat of document.querySelectorAll(CHAT_SEAT_SELECTOR)) {
489
+ const key = seat.dataset.chatAnchorKey;
490
+ const anchor = key !== void 0 && session !== void 0 ? session.getSnapshot().chat.nodes.get(key)?.anchorSeq : void 0;
491
+ const isMarker = anchor === void 0 && seat.textContent?.includes(MARKER_TEXT) === true;
492
+ if (anchor !== void 0 && hiddenSeqs.has(anchor) || isMarker) {
493
+ seat.style.display = "none";
494
+ hidden.add(seat);
495
+ } else if (hidden.has(seat)) {
496
+ seat.style.display = "";
497
+ hidden.delete(seat);
498
+ }
499
+ }
500
+ for (const seat of document.querySelectorAll(USER_SEAT_SELECTOR)) {
501
+ if (!hidden.has(seat)) attach(seat);
502
+ }
453
503
  };
454
504
  observer = new MutationObserver(scan);
455
505
  observer.observe(document.body, { childList: true, subtree: true });
package/lib/index.js CHANGED
@@ -284,7 +284,8 @@ function buildMarker(targetSeq) {
284
284
  return createUserMessage({
285
285
  content: [{
286
286
  type: "text",
287
- text: `[\u56DE\u9000\u6807\u8BB0 / rewind marker] \u5BF9\u8BDD\u5DF2\u56DE\u9000\u5230 seq ${targetSeq}\uFF0C\u6B64\u6807\u8BB0\u4E4B\u540E\u7684\u5386\u53F2\u5DF2\u4ECE\u6A21\u578B\u4E0A\u4E0B\u6587\u4E2D\u79FB\u9664\u3002\u8BF7\u5FFD\u7565\u6B64\u6807\u8BB0\u672C\u8EAB\uFF0C\u7B49\u5F85\u7528\u6237\u7684\u4E0B\u4E00\u6761\u6D88\u606F\u3002`
287
+ text: `__DSH_REWIND__:{"target":${targetSeq}}
288
+ [\u56DE\u9000\u6807\u8BB0 / rewind marker] \u5BF9\u8BDD\u5DF2\u56DE\u9000\u5230 seq ${targetSeq}\uFF0C\u6B64\u6807\u8BB0\u4E4B\u540E\u7684\u5386\u53F2\u5DF2\u4ECE\u6A21\u578B\u4E0A\u4E0B\u6587\u4E2D\u79FB\u9664\u3002\u8BF7\u5FFD\u7565\u6B64\u6807\u8BB0\u672C\u8EAB\uFF0C\u7B49\u5F85\u7528\u6237\u7684\u4E0B\u4E00\u6761\u6D88\u606F\u3002`
288
289
  }],
289
290
  source: { kind: "user" }
290
291
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-rewind-plugin",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "DeepSeek Harness plugin: in-place conversation rewind in the same session window (Claude Code /rewind semantics) with optional workspace file restore",
5
5
  "keywords": [
6
6
  "deepseek-harness",