dsh-rewind-plugin 0.6.2 → 0.7.0
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.en.md +16 -12
- package/README.md +12 -9
- package/SECURITY.md +14 -7
- package/assets/screenshots/cleanup-setting.png +0 -0
- package/docs/architecture.md +1 -1
- package/docs/compat/audit.md +31 -20
- package/docs/format.md +11 -0
- package/docs/release/release.md +6 -6
- package/docs/release/release.zh.md +5 -4
- package/docs/snapshot-auto-cleanup.md +4 -12
- package/docs/snapshot-auto-cleanup.zh.md +4 -8
- package/lib/client.js +528 -17
- package/lib/index.js +82 -34
- package/lib/types/client/hidden.d.ts +10 -0
- package/lib/types/client/locales.d.ts +34 -0
- package/lib/types/client/portals.d.ts +8 -6
- package/lib/types/client/settings-card.d.ts +76 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/locales.d.ts +0 -2
- package/lib/types/settings-locale.d.ts +32 -0
- package/lib/types/snapshot-cleanup.d.ts +67 -7
- package/package.json +24 -13
package/README.en.md
CHANGED
|
@@ -12,7 +12,7 @@ A deliberately focused plugin with one job: **rewind to any user message, no mat
|
|
|
12
12
|
|
|
13
13
|
- **Rewinding is time-travel** — the target message and everything after it (agent replies, tool calls) are withdrawn from the model context *and* the rendered transcript at once, with no new session and no window switch; the target's text is offered back in the composer so you can edit and re-send it — **truly seamless and convenient by design**.
|
|
14
14
|
- **Lightweight workspace backup** — Claude Code-aligned behavior: tracks files edited by the write-class tools, and external changes to **already-tracked** files are restorable too. Selective tracking, before-write backup, store only on change. One lightweight plugin gives you a **complete** agentic rewind capability.
|
|
15
|
-
- **Privacy-first** — the plugin never deletes or rewrites the session log (append-only) and never actually deletes any of your conversation;
|
|
15
|
+
- **Privacy-first** — the plugin never deletes or rewrites the session log (append-only) and never actually deletes any of your conversation; backups live in the plugin's own snapshot directory; restores draw only from those backups. Full security model: [SECURITY.md](SECURITY.md).
|
|
16
16
|
- **A complete test system** — unit, probe, and end-to-end host verification, covering compatibility probing, log replay, resume, cross-restart and other scenarios; maintained continuously as the harness evolves to ensure feature stability.
|
|
17
17
|
|
|
18
18
|
## Preview
|
|
@@ -66,10 +66,13 @@ clears the file backups (chat rewinds are unaffected) and the plugin rebuilds th
|
|
|
66
66
|
automatically.
|
|
67
67
|
|
|
68
68
|
A **global auto-cleanup** (off by default) removes the snapshot directories of
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
long-inactive sessions, leaving the active session and chat log untouched. Configure
|
|
70
|
+
and review it in the **Settings > Plugins > Plugin configuration > Snapshot
|
|
71
|
+
cleanup** panel (the auto-cleanup switch and the idle-day cutoff), or use the
|
|
72
|
+
`/snapshot-auto-cleanup` command to **view, configure, and run** it. See:
|
|
73
|
+
[Snapshot cleanup](docs/snapshot-auto-cleanup.md).
|
|
74
|
+
|
|
75
|
+
<img src="assets/screenshots/cleanup-setting.png" alt="Snapshot cleanup settings: auto-cleanup and idle days" width="600">
|
|
73
76
|
|
|
74
77
|
## Why it stands out
|
|
75
78
|
|
|
@@ -98,14 +101,15 @@ The whole design rests on two principles, simple but deliberate: **the conversat
|
|
|
98
101
|
|
|
99
102
|
### 2. File restore: lightweight checkpointing, "back up before the change"
|
|
100
103
|
|
|
101
|
-
The file half follows Claude Code's checkpoint semantics — **
|
|
104
|
+
The file half follows Claude Code's checkpoint semantics — **partial tracking + before-write backup, plus a re-scan of tracked files at each message**, not a whole-tree snapshot. This trade-off saves space, and it's actually more complete:
|
|
102
105
|
|
|
103
|
-
- **Before-backup**: tracks the write-class tools (`write`, `edit`, `str_replace_editor`)
|
|
104
|
-
- **External changes count too**: at every user-message boundary the plugin re-checks all tracked files —
|
|
105
|
-
- **
|
|
106
|
+
- **Before-write backup**: tracks only the write-class tools (`write`, `edit`, `str_replace_editor`) — backs up the original content before a write and records/tracks the files it touches; it never backs up the whole workspace, so it's lightweight.
|
|
107
|
+
- **External changes count too**: at every user-message boundary the plugin re-checks all tracked files — external changes such as a command run or a manual edit are recorded as well and restored by a later rewind. "Lightweight" but not "incomplete".
|
|
108
|
+
- **Unchanged-not-recorded, identical-content-as-link**: an entry is written only when something changed — at the message-boundary re-check, an unchanged file is never backed up (no record); at before-write time, when the new content matches the path's prior record, only a **link to it** (`ref`) is stored instead of a copy. Repeated writes cost almost nothing, and a link is materialized before its group is evicted — never left dangling.
|
|
109
|
+
- **Reconcile against the real disk before restoring**: restore takes each path's **earliest** record, then reads the live file and compares — **only files that actually differ are touched**: modified files are written back to the earliest backup, files created after the target are deleted, already-matching files are skipped. Repeated rewinds are therefore **idempotent with zero side effects** and never produce "ghost impact".
|
|
106
110
|
- **Safety boundary**: symlinks / hard links are skipped so one restore can't clobber another name of the same file; paths are sanitized so nothing ever escapes the backup root; a per-file failure never aborts the pass.
|
|
107
111
|
|
|
108
|
-
> **Design highlight**:
|
|
112
|
+
> **Design highlight**: this checkpoint's light footprint comes from **recording only what was actually touched and really changed** — before-write backup makes it restorable, unchanged-not-recorded and content-as-link drop the repetition; which files to touch is decided against the real disk at restore time.
|
|
109
113
|
|
|
110
114
|
### Design highlights
|
|
111
115
|
|
|
@@ -132,7 +136,7 @@ This plugin deliberately stays lightweight and focused on one thing — "convers
|
|
|
132
136
|
## Compatibility
|
|
133
137
|
|
|
134
138
|
- Node.js `^22.19.0 || >=24.0.0`.
|
|
135
|
-
-
|
|
139
|
+
- Compatibility definition, verification method, and version alignment: see [docs/compat/audit.md](docs/compat/audit.md); supported DSH versions are declared by `package.json` `peerDependencies`.
|
|
136
140
|
|
|
137
141
|
> [!WARNING]
|
|
138
142
|
> This project and DeepSeek Harness are both in developer preview. Pin exact
|
|
@@ -156,7 +160,7 @@ withdrew should consume the stable, locale-independent helpers exported from
|
|
|
156
160
|
|
|
157
161
|
## Security
|
|
158
162
|
|
|
159
|
-
This plugin only appends rewind-marker events to the session log; it never deletes or rewrites logged history. Workspace files are written only when you choose "conversation and code"; backups
|
|
163
|
+
This plugin only appends rewind-marker events to the session log; it never deletes or rewrites logged history. Workspace files are written only when you choose "conversation and code"; backups are stored under `<dsh home>/rewind-snapshots/`; restores draw only from those backups. It never touches your git repository, makes no network requests, and accesses no credentials. Delete `~/.dsh/rewind-snapshots/` to wipe file backups only (chat rewinds are unaffected); the plugin rebuilds automatically. For sessions you've left inactive for a long time, a global auto-cleanup (off by default) can remove their snapshot directory in whole, leaving the active session and the chat log untouched. Full security model: [SECURITY.md](SECURITY.md).
|
|
160
164
|
|
|
161
165
|
## Development
|
|
162
166
|
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ DeepSeek Harness 插件:**一键就地回退对话到任意更早的用户消
|
|
|
12
12
|
|
|
13
13
|
- **回退 = 时间回溯**——目标消息及其之后的全部内容(agent 回复、工具调用)同时从**模型上下文**和**渲染对话**中撤回,不新建会话、不切换窗口;目标消息文本会回填输入框,改完可重发。**在原理上就真正无感、便捷**。
|
|
14
14
|
- **轻量工作区备份**——对齐 Claude Code:追踪写类工具编辑过的文件,**已跟踪文件的外部变更也能还原**。局部追踪、写前备份、不变不存。一个轻型插件,即拥有**完备的智能体回退能力**。
|
|
15
|
-
- **信息安全优先**——插件从不删改会话日志(append-only
|
|
15
|
+
- **信息安全优先**——插件从不删改会话日志(append-only),从不真正删除你的任何对话;备份存于自己的快照目录;还原只用这些备份。完整安全模型:[SECURITY.md](SECURITY.md)。
|
|
16
16
|
- **完备测试系统**——单元、探针、端到端主机验证,覆盖兼容性探测、日志重放、续接、跨重启等场景;随 harness 升级持续维护,确保功能稳定。
|
|
17
17
|
|
|
18
18
|
## 效果预览
|
|
@@ -60,7 +60,9 @@ dsh plugin --profile web add dsh-rewind-plugin
|
|
|
60
60
|
|
|
61
61
|
快照(写前备份)存储于 `<dsh home>/rewind-snapshots/`(未设 `$DSH_HOME` 时即 `~/.dsh/rewind-snapshots/`)。插件对**同一会话**的快照做内容去重(内容未变则存为链接)并保留最近 100 组锚点;**手动删除该目录**仅清除文件备份(对话回退不受影响),插件会自动重建。
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
另提供**全局自动清理**(默认关闭):把长期不活跃的会话快照整目录移除,不影响活动会话与对话日志。可在 `设置→插件→插件配置→快照清理` 面板查看与配置(自动清理开关、失活天数),也可用 `/snapshot-auto-cleanup` 命令查看、设置和运行。详见:[快照自动清理](docs/snapshot-auto-cleanup.zh.md)。
|
|
64
|
+
|
|
65
|
+
<img src="assets/screenshots/cleanup-setting.png" alt="快照清理设置:自动清理与失活天数" width="600">
|
|
64
66
|
|
|
65
67
|
## 本插件的优势
|
|
66
68
|
|
|
@@ -89,14 +91,15 @@ dsh plugin --profile web add dsh-rewind-plugin
|
|
|
89
91
|
|
|
90
92
|
### 2. 文件还原:轻量检查点,「改前备份」
|
|
91
93
|
|
|
92
|
-
文件部分对齐 Claude Code
|
|
94
|
+
文件部分对齐 Claude Code 的检查点语义——**局部追踪、写前备份 + 每条消息重扫已跟踪文件**,而不是整树快照。这项取舍既省空间,又更完整:
|
|
93
95
|
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
96
|
+
- **写前备份**:只追踪写类工具(`write`、`edit`、`str_replace_editor`),写前**备份原内容**,并**记录、追踪**被处理的文件——从不备份整个工作区,因此轻量。
|
|
97
|
+
- **外部变更也追**:每条用户消息边界,插件重新检查所有已跟踪文件——命令执行、手动修改等外部变更同样被记录,回退时一并还原。这让「轻量」却不「残缺」。
|
|
98
|
+
- **不变不存、同内容存链接**:记录只在有变化时发生——消息边界重扫时无变更的不备份(不留记录);写前备份时若与前一条记录一致,只存**指向它的链接**(`ref`)而非复制内容。重复写入几乎不占空间,链接也先落地、绝不悬空。
|
|
99
|
+
- **还原时对照真实磁盘**:先取每条路径的**最早**记录,再实时读取文件当前内容与之比对——**只操作真正不一致的文件**:改过的写回最早期内容、目标之后新建的删除、已经一致的跳过。重复回退因此**零副作用、幂等**,不会出现「幽灵影响」。
|
|
97
100
|
- **安全边界**:符号/硬链接跳过,避免透过一个还原误伤另一个名字;路径经安全化处理,**绝不越出备份根目录**;单个文件失败绝不中止整轮还原。
|
|
98
101
|
|
|
99
|
-
>
|
|
102
|
+
> **设计点睛**:这套检查点的「轻」,来自**只记录被工具动过、且确实变化的文件**——写前备份保证可还原,不变不存与存链接压掉重复;还原时再对照真实磁盘,只动不一致的文件。
|
|
100
103
|
|
|
101
104
|
### 设计亮点一览
|
|
102
105
|
|
|
@@ -123,7 +126,7 @@ dsh plugin --profile web add dsh-rewind-plugin
|
|
|
123
126
|
## 兼容性
|
|
124
127
|
|
|
125
128
|
- Node.js `^22.19.0 || >=24.0.0`。
|
|
126
|
-
-
|
|
129
|
+
- 兼容性定义、验证方法与版本对齐详见 [docs/compat/audit.md](docs/compat/audit.md);支持的 DSH 版本由 `package.json` 的 `peerDependencies` 声明。
|
|
127
130
|
|
|
128
131
|
> [!WARNING]
|
|
129
132
|
> 本项目与 DeepSeek Harness 均处于开发者预览阶段。可复现环境请 pin 精确版本,
|
|
@@ -144,7 +147,7 @@ dsh plugin --profile web add dsh-rewind-plugin
|
|
|
144
147
|
|
|
145
148
|
## 安全
|
|
146
149
|
|
|
147
|
-
|
|
150
|
+
本插件只向会话日志追加回退标记事件,从不删除或改写已记录的历史。工作区文件仅在「回退对话和代码」时被改写,备份存储于 `~/.dsh/rewind-snapshots/`;还原以备份为唯一来源。不触碰你的 git 仓库,无网络请求,不访问任何凭据。对**长期不活跃**的会话,另有默认关闭的全局自动清理可整目录移除其快照,不影响活动会话与对话日志。完整安全模型:[SECURITY.md](SECURITY.md)。
|
|
148
151
|
|
|
149
152
|
## 开发
|
|
150
153
|
|
package/SECURITY.md
CHANGED
|
@@ -83,9 +83,8 @@ and is repaired offline (see `docs/compat/troubleshooting.md`).
|
|
|
83
83
|
|
|
84
84
|
- **Store root**: `<harness home>/rewind-snapshots/` by default (resolved via
|
|
85
85
|
`resolveDshHome`, so `~/.dsh/...` when `DSH_HOME` is unset); the `snapshotDir`
|
|
86
|
-
config, then `DSH_REWIND_SNAPSHOT_DIR` env, override it.
|
|
87
|
-
|
|
88
|
-
rebuilds from scratch.
|
|
86
|
+
config, then `DSH_REWIND_SNAPSHOT_DIR` env, override it. Deleting it only
|
|
87
|
+
removes file backups and the store rebuilds from scratch.
|
|
89
88
|
- **Path sanitization**: session ids and call ids are scrubbed to
|
|
90
89
|
`[a-zA-Z0-9._-]` (`safeSessionId` / `safeFileId`) before joining the store
|
|
91
90
|
root; `.` and `..` bare values are replaced — hostile ids cannot traverse
|
|
@@ -138,10 +137,18 @@ and is repaired offline (see `docs/compat/troubleshooting.md`).
|
|
|
138
137
|
same operating-system user: state files are created with the process
|
|
139
138
|
default permissions (no special modes are set — a standard umask applies),
|
|
140
139
|
and the host user remains trusted.
|
|
141
|
-
- It does **not** exclude paths from the store
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
- It does **not** exclude paths from the store, and it never oversteps: the
|
|
141
|
+
plugin only adds backup/restore on top of permissions DSH already holds. Two
|
|
142
|
+
distinct cases follow:
|
|
143
|
+
1. **Sensitive / personal-information files** (e.g. a `.env` file, which may
|
|
144
|
+
sit inside the workspace) the model may read or edit are backed up and
|
|
145
|
+
restorable.
|
|
146
|
+
2. **Files outside the session workspace** the model is allowed to touch are
|
|
147
|
+
likewise backed up and restored.
|
|
148
|
+
In both cases the plugin introduces no authority of its own — DSH already
|
|
149
|
+
granted the read/edit; the plugin merely records a before-backup and can
|
|
150
|
+
restore it. Keeping a path out is a **DSH model-permission** concern (a
|
|
151
|
+
per-path deny), not a rewind feature.
|
|
145
152
|
- It does **not** touch git (no refs, index, or worktree operations), makes
|
|
146
153
|
**no network requests**, and does **not** access credentials.
|
|
147
154
|
- It does **not** roll back whole-log state: telemetry, search, and `/export`
|
|
Binary file
|
package/docs/architecture.md
CHANGED
|
@@ -98,7 +98,7 @@ pruneStale cross-session auto-cleanup (default off): whole
|
|
|
98
98
|
|
|
99
99
|
## Compatibility strategy
|
|
100
100
|
|
|
101
|
-
- **Peer ranges as OR-union of DSH
|
|
101
|
+
- **Peer ranges as OR-union of DSH version tuples** (`^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-alpha.2`):
|
|
102
102
|
npm's prerelease rules require the peer range to share the host's
|
|
103
103
|
`[major, minor, patch]` tuple; `scripts/check-dsh-version.mjs` detects when
|
|
104
104
|
a new tuple requires appending. See `docs/release/release.md`.
|
package/docs/compat/audit.md
CHANGED
|
@@ -11,32 +11,41 @@
|
|
|
11
11
|
> source reference: the `oss/deepseek-harness` local fork.
|
|
12
12
|
>
|
|
13
13
|
> Version alignment: `peerDependencies` use an OR-union (e.g.
|
|
14
|
-
> `^0.1.0-rc.6 || ^0.1.1-rc.2`) covering each published
|
|
14
|
+
> `^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-alpha.2`) covering each published tuple series. npm's
|
|
15
15
|
> prerelease matching rules require a candidate to share the range comparator's
|
|
16
16
|
> `[major, minor, patch]` tuple, so each new tuple series (e.g. a future
|
|
17
17
|
> `0.1.2-rc.x`, `0.2.x`) requires appending a union member; rc rolling within a
|
|
18
18
|
> tuple (`0.1.1-rc.2 → rc.3`) is a no-op. Signal: `npm view @deepseek-ai/dsh version`;
|
|
19
|
-
> flow: `scripts/check-dsh-version.mjs
|
|
19
|
+
> flow: `scripts/check-dsh-version.mjs` (it reads the `latest` dist-tag only; a
|
|
20
|
+
> `-alpha` prerelease published under another tag, e.g. `0.1.2-alpha.2` under
|
|
21
|
+
> `alpha`, is a manual pre-release check).
|
|
20
22
|
>
|
|
21
|
-
>
|
|
22
|
-
>
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
23
|
+
> `0.1.2-alpha.1` was never published to npm, so the peer OR-union is declared only from `0.1.2-alpha.2`; the plugin code still supports the `alpha.1`+ client.
|
|
24
|
+
>
|
|
25
|
+
> `0.1.2-alpha.3` verified-compatible (`v0.6.3` runs without issues); `rc.2` (`latest`) remains the primary baseline.
|
|
26
|
+
>
|
|
27
|
+
> Client channel (harness `0.1.2-alpha.x`; `0.1.2-alpha.2` is published to npm
|
|
28
|
+
> under the `alpha` dist-tag while `latest` stays `0.1.1-rc.2`): the plugin reads
|
|
29
|
+
> the session chat through `uiConversation` whenever the DSH client exposes it.
|
|
30
|
+
> `uiConversation` surfaces the chat as a per-session named `"chat"` view
|
|
26
31
|
> (contributed by `dsh-client-ui-chat` through the `uiSession` slot hook) instead
|
|
27
32
|
> of the session-face `chat` field. `src/client/hidden.ts` (`chatSnapshotOf`) reads
|
|
28
33
|
> the session-face snapshot first (rc.2 path), then the `uiConversation` `"chat"`
|
|
29
|
-
> view (alpha
|
|
30
|
-
> crash).
|
|
31
|
-
>
|
|
32
|
-
> `^0.1.0-rc.6 || ^0.1.1-rc.2
|
|
33
|
-
>
|
|
34
|
-
>
|
|
35
|
-
>
|
|
36
|
-
>
|
|
37
|
-
>
|
|
38
|
-
>
|
|
39
|
-
>
|
|
34
|
+
> view (alpha path); both missing degrades to `undefined` (no targets, never a
|
|
35
|
+
> crash). The channel is held via a lazy `ctx.get` (never a declared `inject`), so
|
|
36
|
+
> the plugin keeps the rc.2 type baseline while the OR-union covers the published
|
|
37
|
+
> `0.1.2` tuple: `^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-alpha.2`. Pinned by
|
|
38
|
+
> `tests/chat-channel.test.ts` (channel precedence + the alpha snapshot shape) and
|
|
39
|
+
> `tests/client-dom.test.ts` (the button-target pairing that consumes the chat).
|
|
40
|
+
> The composer refill is dual-channel the same way: on alpha the withdrawn text is
|
|
41
|
+
> written through the `conversation` service's `input` resolver's `setDraft` (the
|
|
42
|
+
> harness's own Lexical editor), else the rc.2 `<textarea>` / alpha `contenteditable`
|
|
43
|
+
> DOM write (`writeComposer` in `src/client/portals.tsx`); pinned by
|
|
44
|
+
> `tests/client-composer.test.ts`. The session-seat button DOM is dual-channel
|
|
45
|
+
> the same way: the actions (copy/branch) row was located by `[data-time-hover-root]`
|
|
46
|
+
> on ≤ 0.1.1-rc.x and by `[data-actions-reveal]` on 0.1.2-alpha.2
|
|
47
|
+
> (`ACTIONS_ROOT_SELECTOR` / `PENDING_SEAT_SELECTOR` in `src/client/portals.tsx`);
|
|
48
|
+
> pinned by `tests/client-dom.test.ts`.
|
|
40
49
|
|
|
41
50
|
## Definition of "fully compatible" (invariants)
|
|
42
51
|
|
|
@@ -60,8 +69,9 @@
|
|
|
60
69
|
- **session-title / goal fold**: a marker does not disturb `foldSessionTitle` / `foldGoal`.
|
|
61
70
|
- **client ordering**: turn-tail ordering + `step/start` uniqueness hold for tool turns + marker logs.
|
|
62
71
|
- **rewind across a compact checkpoint**: `RewindError('not-on-surface')` refuses cleanly, no crash.
|
|
63
|
-
- **plan-mode**:
|
|
72
|
+
- **plan-mode**: a marker reuses the last started turn (no phantom turn); a rewind never touches the log-only `plan/mode` state (plan mode stays active; the user leaves it with `/plan off`) and the log stays replayable (`compat-invariants` I1/I3 marker + `plan/mode` probe, `verify-host`).
|
|
64
73
|
- **agent-loop cancellation**: `finally` guarantees step/turn closure; the rewind force-stop path leaves no dangling frame.
|
|
74
|
+
- **settings-card cross-version reach**: the Snapshot cleanup card must be registered through a **nested** `ctx.inject(['settingsScope'], …)` — naming `settingsScope` in the module-level inject unmounts the whole client plugin on rc.2 (card and rewind button disappear). It uses only the rc.2↔alpha-common scope subset (`getSnapshot().value` + `set`), never the alpha-only `mutate`.
|
|
65
75
|
|
|
66
76
|
## Known behavior boundaries (deterministic differences, non-crash, documented)
|
|
67
77
|
|
|
@@ -71,6 +81,7 @@
|
|
|
71
81
|
- **Session title auto-regeneration**: the title derives from the surface, so an automatically-derived title may change after a rewind.
|
|
72
82
|
- **Files written but uncommitted in a cancelled turn**: a `both` rewind cannot restore them (tool side-effect timing; same as Claude Code).
|
|
73
83
|
- **Attachment files left after a message is shadowed**: attachment storage is not cleaned with the surface (`dsh-attachment-local` not installed, not automatically verified).
|
|
84
|
+
- **Rewind leaves plan mode untouched**: `/plan text` is two independent actions (enter plan mode + steer the message). Rewinding the message undoes only the message — the log-only `plan/mode` state stays active, and the user leaves plan mode with `/plan off`, which still commits after a rewind (the marker creates no open turn). Pin: `verify-host` plan checks (`plan rewind leaves plan mode active`, `/plan off after rewind turns plan mode off`), `tests/hidden.test.ts` `messageTextAt`.
|
|
74
85
|
|
|
75
86
|
## Upstream (harness) issues and the plugin's no-compensation stance
|
|
76
87
|
|
|
@@ -161,7 +172,7 @@ new turn at `phase.turn + 1` (`agent.ts:251-255`) **without closing the leftover
|
|
|
161
172
|
| resume / session-query replay | ✓ | — | ✓ | — | — | — | ✓ | — |
|
|
162
173
|
| tool pipeline (snapshot/restore) | — | — | — | — | — | ✓ | — | ✓ |
|
|
163
174
|
| client ordering | — | — | ✓ | — | — | — | ✓ | — |
|
|
164
|
-
| plan-mode |
|
|
175
|
+
| plan-mode | ✓ | — | ✓ | — | — | — | — | ✓ (static) |
|
|
165
176
|
|
|
166
177
|
✓ = probe passes; — = not applicable. RU-I18N and R-OPENSTEP are upstream issues the plugin
|
|
167
178
|
does not compensate for (see above); G3 is a confirmed-non-defect behavior pinned in
|
package/docs/format.md
CHANGED
|
@@ -149,3 +149,14 @@ version field. A future incompatible format must either bump the journal
|
|
|
149
149
|
`version` (readers reject unknown values — there is no best-effort fallback or
|
|
150
150
|
legacy coercion) or move the state root (e.g. `rewind-snapshots/v2`) and ship
|
|
151
151
|
an explicit migration tool. Old-format data is never silently re-interpreted.
|
|
152
|
+
|
|
153
|
+
## Cleanup policy persistence
|
|
154
|
+
|
|
155
|
+
The snapshot auto-cleanup policy (the `enabled` switch and the `maxAgeDays`
|
|
156
|
+
idle cutoff) is no longer a file: it lives in the **dsh-settings document** under
|
|
157
|
+
the `dsh-rewind-snapshot-cleanup` namespace (validated by a schemastery schema;
|
|
158
|
+
defaults are the `base` layer). On first run after this change, a legacy
|
|
159
|
+
`<dsh home>/snapshot-cleanup.json` is imported into the document **once** and
|
|
160
|
+
then deleted (an absent file is a no-op; an invalid file imports the safe
|
|
161
|
+
default and is dropped). The last-sweep clock stays in its own
|
|
162
|
+
`<dsh home>/snapshot-cleanup-last-sweep.json` state file, which is unchanged.
|
package/docs/release/release.md
CHANGED
|
@@ -54,18 +54,18 @@ git push origin main --tags # triggers .github/workflows/publish.yml
|
|
|
54
54
|
|
|
55
55
|
DSH is still in rc; npm's prerelease matching rules require a peer range to
|
|
56
56
|
share the host version's `[major, minor, patch]` tuple. So `peerDependencies`
|
|
57
|
-
uses an OR-union covering every published
|
|
58
|
-
(e.g. `^0.1.0-rc.6 || ^0.1.1-rc.2`), extended as DSH releases new tuples.
|
|
57
|
+
uses an OR-union covering every published tuple series
|
|
58
|
+
(e.g. `^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-alpha.2`), extended as DSH releases new tuples.
|
|
59
59
|
|
|
60
60
|
- **When to update**: only when DSH releases a new tuple
|
|
61
61
|
(`0.1.1 → 0.1.2 → 0.2.x`); rc rolling within a tuple (`0.1.1-rc.2 → rc.3`)
|
|
62
62
|
needs nothing. All `@deepseek-ai/*` packages release together;
|
|
63
63
|
`npm view @deepseek-ai/dsh version` is the authoritative signal.
|
|
64
64
|
- **Published-tuple check (optional)**: `node scripts/check-dsh-version.mjs`
|
|
65
|
-
compares the latest
|
|
66
|
-
nothing to do, exit 1 = update). It reads
|
|
67
|
-
|
|
68
|
-
"Before bumping" step above.
|
|
65
|
+
compares the `latest` dist-tag version against the tuples the peers cover
|
|
66
|
+
(exit 0 = nothing to do, exit 1 = update). It reads the `latest` tag only; a
|
|
67
|
+
pre-release published under another tag (e.g. `alpha`) or bundled without
|
|
68
|
+
going to npm is a manual pre-release check — see the "Before bumping" step above.
|
|
69
69
|
- **Update steps**: append `|| ^<new-tuple>-rc.<n>` to every
|
|
70
70
|
`@deepseek-ai/dsh-*` peer → bump devDependencies to the latest → `npm
|
|
71
71
|
install` → `npm run check` → release.
|
|
@@ -49,15 +49,16 @@ git push origin main --tags # 触发 .github/workflows/publish.yml
|
|
|
49
49
|
|
|
50
50
|
DSH 仍在 rc 阶段,npm 的 prerelease 匹配规则要求 peer 范围与宿主版本
|
|
51
51
|
**同 `[major, minor, patch]` 元组**才能匹配。因此 peerDependencies 采用
|
|
52
|
-
OR 并集覆盖 DSH
|
|
52
|
+
OR 并集覆盖 DSH 已发布的每个元组系列(如 `^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-alpha.2`),
|
|
53
53
|
并随 DSH 发版追加。
|
|
54
54
|
|
|
55
55
|
- **何时需要更新**:仅当 DSH 发布新元组(`0.1.1 → 0.1.2 → 0.2.x`)时;
|
|
56
56
|
同元组内 rc 滚动(`0.1.1-rc.2 → rc.3`)无需动作。DSH 所有包同版本发布,
|
|
57
57
|
`npm view @deepseek-ai/dsh version` 即权威信号。
|
|
58
|
-
- **已发布元组检查(可选)**:`node scripts/check-dsh-version.mjs` 用 npm
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
- **已发布元组检查(可选)**:`node scripts/check-dsh-version.mjs` 用 npm `latest`
|
|
59
|
+
dist-tag 版本对比 peer 覆盖的元组(exit 0 无需动作,exit 1 需要)。它**只读
|
|
60
|
+
`latest` tag**;发布在其它 tag(如 `alpha`)的 pre-release 走**手动发布前检查**
|
|
61
|
+
——见上文"升版前手动确认"。
|
|
61
62
|
- **更新步骤**:给每个 `@deepseek-ai/dsh-*` peer 追加 `|| ^<新元组>-rc.<n>`
|
|
62
63
|
→ devDependencies 同步升到最新 → `npm install` → `npm run check` → 发版。
|
|
63
64
|
- **正式版后收敛**:DSH 发布 final 版本后,正式版不受 prerelease 元组规则
|
|
@@ -15,7 +15,7 @@ you've stopped using, and now and then remove their snapshots to keep disk usage
|
|
|
15
15
|
down. It never touches your active session, and it never touches your
|
|
16
16
|
conversation.
|
|
17
17
|
|
|
18
|
-
**Manual
|
|
18
|
+
**Manual**: even with automatic cleanup off, you can run the cleanup yourself:
|
|
19
19
|
|
|
20
20
|
- `/snapshot-auto-cleanup run [--apply]` — preview, then actually remove the
|
|
21
21
|
snapshots of sessions you haven't used for a while.
|
|
@@ -41,20 +41,12 @@ whether or not automatic cleanup is on.
|
|
|
41
41
|
|
|
42
42
|
## Settings
|
|
43
43
|
|
|
44
|
-
The
|
|
44
|
+
The auto-cleanup switch and the idle-day cutoff live in the **dsh-settings configuration document**. View and edit them in the **Settings > Plugins > Plugin configuration > Snapshot cleanup** panel (the auto-cleanup switch and the idle days), or view and set them with the `/snapshot-auto-cleanup` command:
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
{ "enabled": false, "maxAgeDays": 30 }
|
|
48
|
-
```
|
|
46
|
+
<img src="../assets/screenshots/cleanup-setting.png" alt="Snapshot cleanup settings: auto-cleanup and idle days" width="600">
|
|
49
47
|
|
|
50
48
|
- `enabled` — whether automatic cleanup runs (default `false`).
|
|
51
|
-
- `maxAgeDays` — how many idle days before a session's snapshots are removed
|
|
52
|
-
(default `30`). Only positive numbers are accepted, so a broken setting can
|
|
53
|
-
never delete everything.
|
|
54
|
-
|
|
55
|
-
You can point the plugin at a different file with the `DSH_SNAPSHOT_CLEANUP_CONFIG`
|
|
56
|
-
environment variable. The file is changed only when you turn cleanup on/off or
|
|
57
|
-
set `max-age`; a missing file reads as the safe default (off).
|
|
49
|
+
- `maxAgeDays` — how many idle days before a session's snapshots are removed (default `30`). Only positive integers are accepted, so a broken setting can never delete everything.
|
|
58
50
|
|
|
59
51
|
## When automatic cleanup runs
|
|
60
52
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
**自动清理**(默认关闭):插件会记住哪些会话你已不再使用,并定期移除它们的快照以节省磁盘。它**绝不**触碰你当前活跃的会话,也**绝不**触碰你的对话。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**手动**:即使自动清理是关闭的,你也可以自己执行清理:
|
|
12
12
|
|
|
13
13
|
- `/snapshot-auto-cleanup run [--apply]` — 先预演,再真正移除你已有一阵子没用的会话的快照。
|
|
14
14
|
- `/snapshot-auto-cleanup run --current [--apply]` — 先预演,再真正清空**当前**会话的快照。这会让它从"现在"重新开始记录回退历史(你的对话不受影响)。若此刻有正在进行中的回合,插件会先暂停它,再清空。
|
|
@@ -29,16 +29,12 @@
|
|
|
29
29
|
|
|
30
30
|
## 设置
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
自动清理开关与失活天数保存在 **dsh-settings 配置文档**。可在 **设置→插件→插件配置→快照清理** 面板查看与修改(自动清理开关、失活天数),也可用 `/snapshot-auto-cleanup` 命令查看和设置:
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
{ "enabled": false, "maxAgeDays": 30 }
|
|
36
|
-
```
|
|
34
|
+
<img src="../assets/screenshots/cleanup-setting.png" alt="快照清理设置:自动清理与失活天数" width="600">
|
|
37
35
|
|
|
38
36
|
- `enabled` — 是否运行自动清理(默认 `false`)。
|
|
39
|
-
- `maxAgeDays` — 会话失活多少天后移除其快照(默认 `30
|
|
40
|
-
|
|
41
|
-
你可以用环境变量 `DSH_SNAPSHOT_CLEANUP_CONFIG` 指向别的文件。该文件只在运行 `/snapshot-auto-cleanup` 时写入;文件缺失按安全默认值(关闭)处理。
|
|
37
|
+
- `maxAgeDays` — 会话失活多少天后移除其快照(默认 `30`)。只接受正整数,因此一个损坏的设置永远不可能"删掉一切"。
|
|
42
38
|
|
|
43
39
|
## 自动清理何时运行
|
|
44
40
|
|