dsh-rewind-plugin 0.2.7 → 0.2.8

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.zh.md CHANGED
@@ -1,46 +1,26 @@
1
1
  # dsh-rewind
2
2
 
3
- [English](README.md)
4
-
5
3
  [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件:**同一会话窗口的 in-place 对话回退**(Claude Code `/rewind` 语义)——把模型上下文剪回更早的一条用户消息,并可基于**落盘的写前备份**还原工作区文件。
6
4
 
7
- > **状态**:已发布 npm(`dsh-rewind-plugin`,v0.2.7),经 GitHub Actions Trusted Publishing + Sigstore provenance 构建发布。目标为 web 配置档(`dsh --profile web`)。交互以 Claude Code 的 rewind 为参考,并贴合 dsh Web 实际 UI。
8
- >
9
- > **v0.2.7 要点**:输入框回填改为事件驱动(重开会话不再复活已撤回文本);子代理编辑不跟踪(对齐 Claude Code);并发回退有防护;「回退代码」选项仅在存在跟踪变更时显示(兼容新旧 host/client 混合版本)。
10
-
11
5
  [![npm version](https://img.shields.io/npm/v/dsh-rewind-plugin.svg)](https://www.npmjs.com/package/dsh-rewind-plugin)
12
6
  [![npm license](https://img.shields.io/npm/l/dsh-rewind-plugin.svg)](https://github.com/SiriLee/dsh-rewind/blob/main/LICENSE)
13
7
 
14
- ## 目录
15
-
16
- - [✨ 功能特性](#-功能特性)
17
- - [📸 截图](#-截图)
18
- - [工作原理](#工作原理)
19
- - [📦 安装](#-安装)
20
- - [使用](#使用)
21
- - [行为细节与限制](#行为细节与限制)
22
- - [明确不包含](#明确不包含)
23
- - [兼容性](#兼容性)
24
- - [开发](#开发)
25
- - [发布](#发布)
26
- - [目录结构](#目录结构)
27
- - [License](#license)
28
-
29
- ## 功能特性
30
-
31
- | 特性 | 说明 |
32
- | --- | --- |
33
- | 同窗口原地回退 | 在**任意**用户消息旁点 ↶ 按钮:该消息及之后全部内容(agent 回复、工具调用)从模型上下文**与**渲染对话中撤回——不新建会话、不切换窗口 |
34
- | 时间回溯语义 | 回退到某消息会**连同撤回该消息本身**;其文本自动填入输入框,可修改后重发 |
35
- | Claude Code 式文件还原 | 写类编辑在执行**前**被备份并落盘;「回退对话和代码」把文件还原到编辑前内容、删除目标之后新建的文件 |
36
- | 影响清单确认 | 「回退对话和代码」先展示要还原/删除的文件清单再确认(目标之后无跟踪变更时不显示该选项,对齐 Claude Code 的 code-restore 可见性) |
37
- | 与审批类插件共存 | 捕获在 `tools/execute`(around-dispatch 阶段):其他插件的 pre-execute 审批短路(如 dsh-edit-approval)无法跳过备份,被拒绝的调用也不会记录 |
38
- | 路径按会话 cwd 解析 | 相对路径按 fs-tools 的 session-cwd 规则解析到**真实文件**,记录解析后的 display path |
39
- | 还原直写真实文件 | 还原走纯 `node:fs` 直接落盘;符号链接与硬链接跳过并警告(不通过共享 inode 误伤) |
40
- | 重启后仍可还原 | 备份落在磁盘 `~/.dsh/rewind-snapshots/<会话>/<锚点 seq>/`,每会话保留最近 100 组 |
41
- | 本地化 | `zh` / `en` 文案,注册进 dsh 的 locale 体系 |
42
-
43
- ## 📸 截图
8
+ > [English](README.md) | 中文
9
+
10
+ 刻意保持聚焦,只做一件事:**就地回退到任意更早的用户消息**。
11
+
12
+ | 模式 | 对话 | 工作区文件 |
13
+ | --- | --- | --- |
14
+ | **仅回退对话** | 剪回目标消息之前 | 不动 |
15
+ | **回退对话和代码** | 剪回目标消息之前 | 还原到目标之前的 state(改过的文件写回、之后新建的文件删除) |
16
+
17
+ 回退即**时间回溯**:目标消息及其之后全部内容(agent 回复、工具调用)从模型上下文**与**渲染对话中撤回——不新建会话、不切换窗口——目标消息的文本会回填输入框,可修改后重发。
18
+
19
+ 插件从不改写 append-only 会话日志,从不触碰你的 git 仓库。
20
+
21
+ ## 效果预览
22
+
23
+ 每条用户消息的操作行上多出一个 **↶ 回退** 按钮。点击后弹出模式选择浮层;「回退对话和代码」会先展示待还原/删除的文件清单再确认(目标之后无跟踪变更时不显示该选项,对齐 Claude Code 的 code-restore 可见性)。
44
24
 
45
25
  <table>
46
26
  <tr>
@@ -53,123 +33,78 @@
53
33
  </tr>
54
34
  </table>
55
35
 
56
- ## 工作原理
36
+ 在输入框手动输入 `/rewind`(含裸命令)会被**拦截**——提交时弹出临时提示,指向消息旁的 ↶ 按钮。
57
37
 
58
- 两部分协同:**对话回退**(同窗口 in-place)与 **checkpoint 文件还原**(Claude Code 式写前备份)。
59
-
60
- ### 1. 对话回退
61
-
62
- 插件向会话日志追加一条**空内容标记** `assistant/message`,其 `surfaceOp: { op: 'replace', start, end }` 把目标消息之后的全部 surface 节点替换为标记本身:
63
-
64
- - 标记携带 `sourceEventSeqs` 覆盖所有被遮蔽节点,`Session.append` 的 surface 规则校验切割合法性(仅限当前 surface 上的连续区间)。
65
- - 因为标记**内容为空**,harness 会将其派生为 `null`——永不进入模型上下文、也永不渲染成对话内容。agent 与用户看到的对话都回到目标消息当时的样子。
66
- - 标记的 **turn 号复用最后一个已开始的回合**(`markerTurnOf`),而不是「最后回合 + 1」:harness 的 agent loop 恰好用 `最后 turn/start + 1` 编号下一条真实回合。若标记也取这个数,日志里就会出现同一 turn 的 `assistant/message` 先于 `turn/start` 的乱序,客户端 conversation 构建器会以 `conversation Context …:turn-tail… received an update before its start Match` 拒绝重放——历史加载失败、整个对话从界面消失(0.2.4 及之前的真实缺陷,已在 0.2.5 修复)。复用已消费的 turn 号则标记只是上一个已完成回合尾部的一次无害追加,永不与新回合冲突。
67
- - append-only 日志**不被改写**——审计轨迹完整保留每条被撤回的事件,只有模型可见的 surface 被剪掉,下一条请求从目标消息起派生上下文。
68
-
69
- 若 agent 正在运行(LLM 思考/流式输出),会先强制停止(`cancel({ kind: 'user' })`)并等待 quiescence 再回退;停不下来则中止并报错。
70
-
71
- ### 2. Checkpoint 文件还原
72
-
73
- 插件跟踪写类工具:`write`、`edit`、`str_replace_editor`(变更子命令 `create` / `str_replace` / `insert`):
74
-
75
- 1. **写前备份**(`tools/execute`,around-dispatch 阶段):读取目标文件,把解析后的路径与内容放入 pending 表。此阶段只在任何 pre-execute 审批门放行之后运行——所以审批 `ask` 短路(dsh-edit-approval)**无法跳过**备份,被拒绝的调用也不会记录。
76
- 2. **落盘提交**(`tools/post-execute`):备份按当前轮**锚点消息 seq** 写入 `~/.dsh/rewind-snapshots/<会话>/<锚点 seq>/<callId>.json`。
77
- 3. **还原**(`/rewind @<seq> both`):锚点 ≥ 目标的每条备份生效——被修改的文件写回其**最早一次**捕获的 before 内容,目标之后新建的文件被删除,符号/硬链接跳过。写入走纯 `node:fs`,不经 fs 服务。
78
- 4. 工具体**抛异常**会跳过 `tools/post-execute`;`tools/result` 兜底清掉 pending,避免内存泄漏。
79
-
80
- 备份跨 host 重启持久化,每会话有界保留最近 100 组锚点。
81
-
82
- ## 🔧 故障修复:历史加载失败(`…turn-tail… received an update before its start Match`)
83
-
84
- 0.2.4 及之前版本在回退**之后继续对话**的场景下会损坏会话的客户端重放:标记的 turn 号
85
- 与下一条真实回合的 `turn/start` 编号冲突,重新打开会话时界面报
86
- `历史加载失败:conversation Context …:turn-tail… received an update before its start Match(internal)`,
87
- 历史整段消失。0.2.5 起新的回退不再产生该冲突;但**已损坏的会话需要离线修复**(日志是
88
- append-only 的,不能在内存中改写)。
89
-
90
- 修复工具**已随 npm 包发布**(`dsh-rewind-repair`)——无需下载源码:
38
+ ## 安装
91
39
 
92
40
  ```sh
93
- # 1. 先完全退出 dsh web / host(会话处于驻留内存时,磁盘修复会被下次 checkpoint 覆盖)
94
- # 2. 运行离线修复(扫描 ~/.dsh/sessions 下所有会话,把标记 turn 改回最后一个已开始的回合)
95
- npm exec --yes --package=dsh-rewind-plugin -- dsh-rewind-repair
96
- npm exec --yes --package=dsh-rewind-plugin -- dsh-rewind-repair -- --dry-run # 只预览不写盘
97
- # 3. 重启 dsh web,损坏的会话即可正常加载历史
41
+ dsh plugin --profile web add dsh-rewind-plugin
98
42
  ```
99
43
 
100
- 也可以全局安装一次(`npm i -g dsh-rewind-plugin`)后直接运行 `dsh-rewind-repair`;
101
- 源码方式为 `node scripts/repair-markers.mjs`(参数相同)。
44
+ 装完重启 `dsh web`(`--profile web`)。
102
45
 
103
- 工具只改写 `dsh-rewind` 空标记事件的 `data.turn` 字段(保持 seq / 顺序 / zstd 帧结构不变),
104
- 改前自动备份原文件为 `session.jsonl.zstd.bak-<时间戳>`;不改动任何其它事件,可安全重复运行。
46
+ > ⚠️ npm 上的 `dsh-rewind` 属于其他作者,请用 `dsh-rewind-plugin` 安装。
105
47
 
106
- ## 📦 安装
48
+ 本地 checkout 或 pin 一个 GitHub commit:
107
49
 
108
- 已发布 npm——推荐走 registry 路径。**装完重启 dsh web(`--profile web`)。**
50
+ ```sh
51
+ dsh plugin --profile web add /path/to/dsh-rewind # 本地 checkout
52
+ dsh plugin --profile web add github:SiriLee/dsh-rewind#<sha> # pin commit
53
+ ```
109
54
 
110
- > ⚠️ 注意:npm 上的 `dsh-rewind` 属于其他作者,请用 `dsh-rewind-plugin` 安装。
55
+ git 安装首次会失败:pnpm 默认禁止 git 依赖执行构建脚本。按 CLI 提示在 profile 的 `pnpm-workspace.yaml` 中加 `allowBuilds` 后重试——pnpm 会执行插件的 `prepare`(完整构建)并装入 profile。
111
56
 
112
- ### 方式 A:registry(推荐)
57
+ ## 使用
113
58
 
114
- ```sh
115
- dsh plugin --profile web add dsh-rewind-plugin
116
- ```
59
+ 1. **hover** 任意你发送过的用户消息——操作行出现 **↶ 回退** 按钮。
60
+ 2. **点击它。** 目标即这条消息;小浮层提供两种模式(目标之后没有跟踪的变更时,「回退对话和代码」不显示)。
61
+ 3. 回退以一条会话内命令执行;结果消息确认,被撤回消息的文本自动填入输入框,可编辑后重发。
117
62
 
118
- ### 方式 B:本地 checkout(作者 / 贡献者)
63
+ 按钮只出现在**当前会话视图**渲染的用户消息行上——回退其他会话前先切换到该会话。回退本身可再回退(标记进入日志),但文件还原动作不再记录新备份。
119
64
 
120
- ```sh
121
- cd dsh-rewind
122
- npm install # devDeps 来自 npm registry,无需 harness checkout
123
- npm run build # 完整构建:lib/(host ESM + client bundle + .d.ts)
124
- dsh plugin --profile web add /path/to/dsh-rewind # link 安装
125
- ```
65
+ ## 原理
126
66
 
127
- ### 方式 C:GitHub(pin commit,可复现)
67
+ ### 1. 对话回退(同窗口就地)
128
68
 
129
- ```sh
130
- dsh plugin --profile web add github:SiriLee/dsh-rewind#<commit-sha>
131
- ```
69
+ 插件向会话日志追加一条**空内容标记** `assistant/message`,其 `surfaceOp: { op: 'replace', start, end }` 把目标消息之后的全部 surface 节点替换为标记本身:
132
70
 
133
- 首次安装会失败:pnpm 默认禁止 git 依赖执行构建脚本。按 CLI 提示在 profile
134
- `pnpm-workspace.yaml`(如 `$DSH_HOME/profiles/web/pnpm-workspace.yaml`)中加
135
- `allowBuilds` 后重试;pnpm 随后会执行插件的 `prepare`(完整构建)并装入 profile。
71
+ - 标记携带 `sourceEventSeqs` 覆盖所有被遮蔽节点,`Session.append` surface 规则校验切割合法性(仅限当前 surface 上的连续区间)。
72
+ - 因为标记**内容为空**,harness 会将其派生为 `null`——永不进入模型上下文、也永不渲染成对话内容。agent 与用户看到的对话都回到目标消息当时的样子。
73
+ - 标记的 **turn 号复用最后一个已开始的回合**(`markerTurnOf`),而不是「最后回合 + 1」:harness 恰好用 `最后 turn/start + 1` 编号下一条真实回合。若标记也取这个数,日志里就会出现同一 turn 的 `assistant/message` 先于 `turn/start` 的乱序,客户端 conversation 构建器会以 `conversation Context …:turn-tail… received an update before its start Match` 拒绝重放——历史加载失败、整个对话从界面消失(0.2.4 及之前的真实缺陷,已在 0.2.5 修复)。复用已消费的 turn 号则标记只是上一个已完成回合尾部的一次无害追加,永不与新回合冲突。
74
+ - append-only 日志**不被改写**——审计轨迹完整保留每条被撤回的事件,只有模型可见的 surface 被剪掉,下一条请求从目标消息起派生上下文。
136
75
 
137
- ## 使用
76
+ agent 正在运行(LLM 思考/流式输出),会先强制停止(`cancel({ kind: 'user' })`)并等待 quiescence 再回退;停不下来则中止并报错。
138
77
 
139
- ### 通过消息旁的按钮回退
78
+ ### 2. Checkpoint 文件还原
140
79
 
141
- 1. **hover** 任意你发送过的用户消息——操作行出现 **↶ 回退** 按钮。
142
- 2. **点击它。** 目标即这条消息(第一步完成)。弹出小浮层(第二步):
143
- - **仅回退对话** —— 把模型上下文剪回这条消息之前;工作区文件不动。
144
- - **回退对话和代码** —— 同样的上下文裁剪,并把工作区文件还原到该消息之前的状态。先显示影响清单(待还原/删除的文件),确认后执行。
145
- - 目标之后**没有**跟踪的文件变更时,该选项**不显示**(对齐 Claude Code 行为)。
146
- 3. 回退以一条会话内命令执行;结果消息确认(如「已撤回 seq N 及之后内容;还原 M 个文件」),被撤回消息的文本自动填入输入框,可编辑后重发。
80
+ 插件跟踪写类工具:`write`、`edit`、`str_replace_editor`(变更子命令 `create` / `str_replace` / `insert`):
147
81
 
148
- ### 回退 = 撤回(时间回溯)
82
+ 1. **写前备份**(`tools/execute`,around-dispatch 阶段):读取目标文件,把解析后的路径与内容放入 pending 表。此阶段只在任何 pre-execute 审批门放行之后运行——审批 `ask` 短路(dsh-edit-approval)**无法跳过**备份,被拒绝的调用也不会记录。若读取失败(如权限错误),该次变更直接不入备份——插件只在日志中警告,**不会阻塞写操作**。
83
+ 2. **落盘提交**(`tools/post-execute`):备份按当前轮**锚点消息 seq** 写入 `~/.dsh/rewind-snapshots/<会话>/<锚点 seq>/<callId>.json`。
84
+ 3. **还原**(`/rewind @<seq> both`):锚点 ≥ 目标的每条备份生效——被修改的文件写回其**最早一次**捕获的 before 内容,目标之后新建的文件被删除,符号/硬链接跳过(它们与另一名字共享 inode,透过一个还原会误伤两个)。写入走纯 `node:fs`,不经 fs 服务——sandbox / 远程 backend 下路径解析可能受限。
85
+ 4. 工具体**抛异常**会跳过 `tools/post-execute`;`tools/result` 兜底清掉 pending,避免内存泄漏。
149
86
 
150
- 回退到某消息会**撤回该消息及它之后的所有内容**——渲染对话与 agent 上下文都回到这条消息之前。命令结果会说明,且该消息文本会填回输入框供重发。
87
+ 备份跨 host 重启持久化,每会话有界保留最近 100 组锚点。
151
88
 
152
- ### 手动 `/rewind` 不支持
89
+ ## 明确不做的事
153
90
 
154
- `/rewind` 命令仅作为按钮的内部调用通道存在。在输入框手动输入 `/rewind`(含裸命令)会被**拦截**——提交时弹出临时提示,指向消息旁的 按钮。
91
+ - **整树 / git-first 快照**——只备份写类工具编辑。`bash`、其他工具与外部程序的修改不在备份内、无法还原:与 Claude Code 相同,官方同样不覆盖,此类回退交由用户 git 处理。
92
+ - **子代理(subagent)的编辑**——不跟踪(同 Claude Code):子代理运行在自己的会话里,其备份无法被父会话的回退还原。
93
+ - **fork / 分支回退与 `/compact`**——harness 已内置(「在新对话中分支」、compact)。
94
+ - **快捷键**(esc+esc 打开回退菜单)——规划中的后续项。
155
95
 
156
- ## 行为细节与限制
96
+ ## 与同类项目对比
157
97
 
158
- - 只跟踪**插件运行期间、经写类工具**的变更(`write` / `edit` / `str_replace_editor`)。`bash`、其他工具或外部程序的修改不在备份内、无法还原——与 Claude Code 相同,官方同样不覆盖,此类回退交由用户 git 处理。
159
- - **子代理(subagent)的编辑不跟踪**——与 Claude Code 相同。子代理运行在自己的会话里,其备份无法被父会话的回退还原;插件直接跳过捕获,而不是记录到永远读不到的位置。
160
- - 写前备份读取失败时(如权限错误)该次变更不会入备份,`both` 回退无法还原它——插件会在日志中警告,但**不会阻塞写操作**。
161
- - 文件还原/删除直写**真实本地文件系统**;sandbox / 远程 backend 下路径解析可能受限。
162
- - 符号链接与硬链接不写入(它们与另一名字共享 inode,还原会互相污染)——跳过并在结果中提示。
163
- - 回退本身可再回退(标记进入日志),但文件还原动作不再记录新备份。
164
- - ↶ 按钮只出现在**当前会话视图**渲染的用户消息行上;回退其他会话前先切换到该会话。
165
- - 目标之后没有跟踪的文件变更时,模式浮层只显示「仅回退对话」(Claude Code 同样隐藏 code-restore 选项)。
98
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 还有 [Anionex/dsh-turn-rewind](https://github.com/Anionex/dsh-turn-rewind)——同样是回退插件,用户面想法相同(每条消息下挂一个动作,回退对话并还原工作区文件),但路线不同:
166
99
 
167
- ## 明确不包含
100
+ | 维度 | dsh-rewind(本项目) | Anionex dsh-turn-rewind |
101
+ | --- | --- | --- |
102
+ | 对话回退 | **同窗口就地**——把模型可见 surface 剪回目标;append-only 日志原封不动 | 在上一 `turn/end` 处**派生新 Session**;原会话永远保留 |
103
+ | 文件还原引擎 | **轻量写前备份**,只跟踪写类工具,纯 `node:fs` 还原 | **Change Ledger**——持久化还原点引擎,带 Git 围栏、审批门、救援点与崩溃对账 |
104
+ | 跟踪范围 | 仅写类工具编辑(同 Claude Code) | 任意 Git 管理文件(要求 Git worktree) |
105
+ | 公共服务 API | 无——聚焦单用途插件 | 有——`ctx.changeLedger` 服务 + `/turn-rewind` HTTP 端点 |
168
106
 
169
- - 快捷键(esc+esc 打开回退菜单)——规划中的后续项。
170
- - `/compact` —— harness 已内置。
171
- - fork / 分支回退 —— harness 内置的「在新对话中分支」。
172
- - 整树 / git-first 快照(覆盖 bash 与外部修改)——**明确不做**,与 Claude Code 原生 rewind 保持一致(官方同样不覆盖,把此类回退交给用户 git)。
107
+ 本质区别:dsh-turn-rewind 因保持日志不可变而必须派生新会话;本插件用空标记**就地剪掉**模型可见 surface,于是原对话在同一个窗口继续——这段并不平凡的实现(见[已知问题](#已知问题))正是 dsh-turn-rewind 绕开的部分。
173
108
 
174
109
  ## 兼容性
175
110
 
@@ -180,6 +115,16 @@ dsh plugin --profile web add github:SiriLee/dsh-rewind#<commit-sha>
180
115
  > 本项目与 DeepSeek Harness 均处于开发者预览阶段。可复现环境请 pin 精确版本,
181
116
  > 并阅读上述行为说明。
182
117
 
118
+ ## 已知问题
119
+
120
+ `v0.2.4` 及之前版本创建的回退在随后继续对话时可能损坏客户端重放(标记 turn 与下一个 `turn/start` 撞号)。离线修复工具**已随 npm 包内置**(`dsh-rewind-repair`)。只影响升级前就已存在的旧会话——全新安装永不触发。
121
+
122
+ 完整步骤见:[docs/troubleshooting.zh.md](docs/troubleshooting.zh.md)
123
+
124
+ ## 安全
125
+
126
+ 本插件只向会话日志追加回退标记事件,从不删除或改写已记录的历史。文件写入仅在你选择「回退对话和代码」时发生,备份与还原都限定在 `~/.dsh/rewind-snapshots/` 内。不触碰你的 git 仓库,无网络请求,不访问任何凭据。
127
+
183
128
  ## 开发
184
129
 
185
130
  ```sh
@@ -190,53 +135,20 @@ npm run build # esbuild:lib/index.js(host ESM)+ lib/client.js(l
190
135
  node scripts/verify-host.mjs # 端到端验证构建产物(18 项检查)
191
136
  ```
192
137
 
193
- `prepare` 执行完整构建,所以 git 安装与 `npm pack` / `npm publish` 总会产出完整的
194
- `lib/` 与 `LICENSE`。
138
+ `prepare` 执行完整构建,所以 git 安装与 `npm pack` / `npm publish` 总会产出完整的 `lib/` 与 `LICENSE`。
195
139
 
196
- 维护者参考:[docs/harness-reference.md](docs/harness-reference.md) 收录 DeepSeek
197
- Harness 接口文档(子系统文档 + 关键源码索引)。
140
+ 维护者:模块地图与 harness 接口参考见 [docs/harness-reference.md](docs/harness-reference.md);发布步骤见 [docs/release.md](docs/release.md)。
198
141
 
199
142
  ## 发布
200
143
 
201
- 通过 GitHub Actions Trusted Publishing(OIDC,无存储 `NPM_TOKEN`)发布:
144
+ 通过 GitHub Actions Trusted Publishing(OIDC,无存储 `NPM_TOKEN`)发布:推送 `v<版本>` tag,CI 即带 Sigstore provenance 发布。
202
145
 
203
146
  ```sh
204
- npm version patch && git push origin main --tags # 触发 .github/workflows/publish.yml
147
+ npm version patch && git push origin main --tags
205
148
  ```
206
149
 
207
- - workflow 校验 tag 与 `package.json` 版本一致,跑 typecheck + 测试 + 完整构建 +
208
- 产物验证,以 `--provenance`(Sigstore)发布,并创建 GitHub Release。**幂等**——
209
- 已发布的版本会跳过。CI(`.github/workflows/ci.yml`)在每次 push / PR 跑相同检查,
210
- 外加 `npm pack --dry-run` 校验 tarball 含 `lib/` 与 `LICENSE`。
211
- - 一次性 npm 侧配置(仓库内无法代做):打开
212
- [dsh-rewind-plugin](https://www.npmjs.com/package/dsh-rewind-plugin) →
213
- **settings → Trusted Publisher → Add**,Provider **GitHub Actions** ·
214
- Organization or user **`SiriLee`** · Repository **`dsh-rewind`**(GitHub 仓库名,
215
- 与 npm 包名可不同)· Workflow filename **`publish.yml`** · Environment
216
- **留空** · Allowed actions **`npm publish`**。配置好后 push `v<version>` tag
217
- 即自动发布。
218
-
219
- ## 目录结构
220
-
221
- ```
222
- src/index.ts host 插件:/rewind 命令 + checkpoint 流水线(tools/execute|post-execute)
223
- src/rewind.ts 纯函数规划:目标解析、surface 范围、候选列表
224
- src/snapshot.ts checkpoint 存储(磁盘写前备份、还原/preview、有界清理)
225
- src/session-cwd.ts session-cwd 解析(fs-tools 规则)
226
- src/client/index.ts client 插件:消息行 ↶ 按钮 + 手动 /rewind 拦截
227
- src/client/popover.ts 模式选择浮层(both 模式影响清单确认)
228
- src/client/hidden.ts 被撤回区间计算(hiddenSeqsOf),纯函数
229
- src/client/locales.ts zh / en 文案(LocaleNamespaceMap)
230
- src/client/styles.ts 注入样式(dsh 设计 token)
231
- scripts/build.mjs esbuild:lib/index.js(host ESM)+ lib/client.js(loader 闭包)+ .d.ts
232
- scripts/verify-host.mjs 端到端验证构建产物(18 项检查)
233
- tests/ vitest 套件(rewind / snapshot / hidden / session-cwd / 集成,46 例)
234
- docs/harness-reference.md 维护者文档:DeepSeek Harness 接口参考
235
- assets/screenshots/ 界面截图
236
- cordis.patch.yml bundle patch(挂载双面插件行)
237
- package.json dsh.bundle + dsh.client 声明、optional peerDependencies
238
- ```
150
+ 一次性 npm 侧配置与完整流程:见 [docs/release.md](docs/release.md)。
239
151
 
240
- ## License
152
+ ## 许可
241
153
 
242
154
  [MIT](LICENSE)
@@ -32,3 +32,26 @@ Also under `docs/` at the repo root: `persistence-catalog.md` (full
32
32
  | User bubble rendering | [packages/client/ui-conversation/src/client/chat/MessageItem.tsx](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/ui-conversation/src/client/chat/MessageItem.tsx) |
33
33
  | Client `SessionFace` (`command` / `cancel`) | [packages/client/runtime/src/client/contract/session.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/runtime/src/client/contract/session.ts) |
34
34
  | Client `PendingWait` (`respond`) | [packages/client/runtime/src/client/sessions/pending.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/runtime/src/client/sessions/pending.ts) |
35
+
36
+ ## Plugin source layout
37
+
38
+ ```
39
+ src/index.ts host plugin: /rewind command + checkpoint pipeline (tools/execute|post-execute)
40
+ src/rewind.ts pure planning: target resolution, surface range, candidate listing
41
+ src/snapshot.ts checkpoint store (disk before-backups, restore/preview, bounded prune)
42
+ src/session-cwd.ts session-cwd resolution (fs-tools rule)
43
+ src/client/index.ts client plugin: per-message ↶ button + manual /rewind guard
44
+ src/client/popover.ts mode-selection popover (both-mode impact confirm)
45
+ src/client/hidden.ts withdrawn-span computation (hiddenSeqsOf), pure
46
+ src/client/locales.ts zh / en copy (LocaleNamespaceMap)
47
+ src/client/styles.ts injected styles (dsh design tokens)
48
+ scripts/build.mjs esbuild: lib/index.js (host ESM) + lib/client.js (loader closure) + .d.ts
49
+ scripts/verify-host.mjs end-to-end host verification (18 checks)
50
+ scripts/repair-markers.mjs offline marker-turn repair tool (ships as `dsh-rewind-repair`)
51
+ tests/ vitest suites (rewind / snapshot / hidden / session-cwd / integration, 46 cases)
52
+ docs/ maintainer docs: harness reference, troubleshooting, release steps
53
+ assets/screenshots/ UI screenshots
54
+ cordis.patch.yml bundle patch (mounts the dual-face plugin row)
55
+ package.json dsh.bundle + dsh.client manifests, optional peerDependencies
56
+ ```
57
+
@@ -0,0 +1,90 @@
1
+ # Release
2
+
3
+ ## First release (manual, one-time)
4
+
5
+ Trusted Publisher can only be configured once the package exists, so the first
6
+ version is published locally:
7
+
8
+ ```sh
9
+ npm login
10
+ npm publish --access public
11
+ ```
12
+
13
+ - If prompted for `EOTP`: complete the browser auth link the CLI prints, or
14
+ retry with a 6-digit code — `npm publish --otp=<code>`.
15
+ - The first version carries no provenance (local path) — acceptable; every CI
16
+ release after that publishes with Sigstore/SLSA provenance automatically.
17
+
18
+ ## Configure Trusted Publisher (npmjs.com, one-time)
19
+
20
+ Open `https://www.npmjs.com/package/dsh-rewind-plugin` → package **settings** →
21
+ **Trusted Publisher**:
22
+
23
+ | Field | Value |
24
+ | --- | --- |
25
+ | Provider | GitHub Actions |
26
+ | Organization or user | `SiriLee` |
27
+ | Repository | `dsh-rewind` (the GitHub repo, not the npm name) |
28
+ | Workflow filename | `publish.yml` |
29
+ | Environment | empty |
30
+ | Allowed actions | `npm publish` |
31
+
32
+ ## Subsequent releases (CI, automatic)
33
+
34
+ ```sh
35
+ npm version patch
36
+ git push origin main --tags # triggers .github/workflows/publish.yml
37
+ ```
38
+
39
+ - The workflow verifies the tag matches `package.json`, runs typecheck + tests +
40
+ a full build + artifact verification, publishes with `--provenance`
41
+ (Sigstore), and creates a GitHub Release. It is **idempotent** — an already
42
+ published version is skipped.
43
+ - CI (`.github/workflows/ci.yml`) runs the same checks on every push / PR, plus
44
+ a `npm pack --dry-run` sanity check that the tarball carries `lib/` and
45
+ `LICENSE`.
46
+
47
+ ---
48
+
49
+ # 发布流程
50
+
51
+ ## 首次发布(手动,一次性)
52
+
53
+ Trusted Publisher 要求**包已存在**才能配置,因此首个版本走本地发布:
54
+
55
+ ```sh
56
+ npm login
57
+ npm publish --access public
58
+ ```
59
+
60
+ - 若提示 `EOTP`(一次性密码):按 CLI 输出的浏览器认证链接完成认证,或用
61
+ authenticator 的 6 位码 `npm publish --otp=<code>` 重试。
62
+ - 首个版本无 provenance(本地路径),合规;之后每次 CI 发布自动带
63
+ Sigstore/SLSA provenance。
64
+
65
+ ## 配置 Trusted Publisher(npmjs.com,一次性)
66
+
67
+ 打开 `https://www.npmjs.com/package/dsh-rewind-plugin` → 包右上角 **settings**
68
+ → **Trusted Publisher**:
69
+
70
+ | 字段 | 值 |
71
+ | --- | --- |
72
+ | Provider | GitHub Actions |
73
+ | Organization or user | `SiriLee` |
74
+ | Repository | `dsh-rewind`(GitHub 仓库名,与 npm 包名可不同) |
75
+ | Workflow filename | `publish.yml` |
76
+ | Environment | 留空 |
77
+ | Allowed actions | `npm publish` |
78
+
79
+ ## 后续发布(CI 自动)
80
+
81
+ ```sh
82
+ npm version patch
83
+ git push origin main --tags # 触发 .github/workflows/publish.yml
84
+ ```
85
+
86
+ - workflow 校验 tag 与 `package.json` 版本一致,跑 typecheck + 测试 + 完整
87
+ 构建 + 产物验证,以 `--provenance`(Sigstore)发布并创建 GitHub Release。
88
+ **幂等**——已发布的版本会跳过。
89
+ - CI(`.github/workflows/ci.yml`)在每次 push / PR 跑相同检查,外加
90
+ `npm pack --dry-run` 校验 tarball 含 `lib/` 与 `LICENSE`。
@@ -0,0 +1,39 @@
1
+ # Troubleshooting
2
+
3
+ [简体中文](troubleshooting.zh.md)
4
+
5
+ ## History load failure: `…turn-tail… received an update before its start Match`
6
+
7
+ Versions `≤ 0.2.4` corrupted client replay when a rewind was **followed by further
8
+ conversation**: the marker's turn number collided with the next real turn's
9
+ `turn/start`, so reopening the session showed
10
+
11
+ ```
12
+ Failed to load history: conversation Context …:turn-tail… received an update before its start Match (internal)
13
+ ```
14
+
15
+ and the history vanished. Rewinds created from `0.2.5` on no longer produce the
16
+ collision, but **already-corrupted sessions need an offline repair** (the log is
17
+ append-only — it cannot be rewritten in memory).
18
+
19
+ The repair tool ships **inside the npm package** (`dsh-rewind-repair`) — no
20
+ source checkout needed:
21
+
22
+ ```sh
23
+ # 1. Fully quit dsh web / host first (while a session is resident in memory,
24
+ # a disk repair is overwritten by the next checkpoint)
25
+ # 2. Run the offline repair (scans every session under ~/.dsh/sessions,
26
+ # rewriting each marker's turn back to the last started turn)
27
+ npm exec --yes --package=dsh-rewind-plugin -- dsh-rewind-repair
28
+ npm exec --yes --package=dsh-rewind-plugin -- dsh-rewind-repair -- --dry-run # preview only
29
+ # 3. Restart dsh web — the repaired sessions load their history again
30
+ ```
31
+
32
+ Or install it globally once (`npm i -g dsh-rewind-plugin`) and run
33
+ `dsh-rewind-repair` directly; from a source checkout the same tool is
34
+ `node scripts/repair-markers.mjs` (identical flags).
35
+
36
+ The tool only rewrites the `data.turn` of `dsh-rewind` empty-marker events
37
+ (keeping seqs, order, and the zstd frame structure intact), backs up the original
38
+ file to `session.jsonl.zstd.bak-<timestamp>` before writing, and never touches
39
+ any other event — safe to run repeatedly.
@@ -0,0 +1,31 @@
1
+ # 故障修复
2
+
3
+ [English](troubleshooting.md)
4
+
5
+ ## 历史加载失败:`…turn-tail… received an update before its start Match`
6
+
7
+ 0.2.4 及之前版本在回退**之后继续对话**的场景下会损坏会话的客户端重放:标记的 turn 号
8
+ 与下一条真实回合的 `turn/start` 编号冲突,重新打开会话时界面报
9
+
10
+ ```
11
+ 历史加载失败:conversation Context …:turn-tail… received an update before its start Match(internal)
12
+ ```
13
+
14
+ 历史整段消失。0.2.5 起新的回退不再产生该冲突;但**已损坏的会话需要离线修复**(日志是
15
+ append-only 的,不能在内存中改写)。
16
+
17
+ 修复工具**已随 npm 包发布**(`dsh-rewind-repair`)——无需下载源码:
18
+
19
+ ```sh
20
+ # 1. 先完全退出 dsh web / host(会话处于驻留内存时,磁盘修复会被下次 checkpoint 覆盖)
21
+ # 2. 运行离线修复(扫描 ~/.dsh/sessions 下所有会话,把标记 turn 改回最后一个已开始的回合)
22
+ npm exec --yes --package=dsh-rewind-plugin -- dsh-rewind-repair
23
+ npm exec --yes --package=dsh-rewind-plugin -- dsh-rewind-repair -- --dry-run # 只预览不写盘
24
+ # 3. 重启 dsh web,损坏的会话即可正常加载历史
25
+ ```
26
+
27
+ 也可以全局安装一次(`npm i -g dsh-rewind-plugin`)后直接运行 `dsh-rewind-repair`;
28
+ 源码方式为 `node scripts/repair-markers.mjs`(参数相同)。
29
+
30
+ 工具只改写 `dsh-rewind` 空标记事件的 `data.turn` 字段(保持 seq / 顺序 / zstd 帧结构不变),
31
+ 改前自动备份原文件为 `session.jsonl.zstd.bak-<时间戳>`;不改动任何其它事件,可安全重复运行。