dsh-rewind-plugin 0.20.0 → 0.21.1
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 +2 -2
- package/README.md +2 -2
- package/SECURITY.md +80 -183
- package/lib/client.js +417 -387
- package/lib/index.js +223 -192
- package/lib/types/client/action.d.ts +40 -0
- package/lib/types/client/hidden.d.ts +2 -2
- package/lib/types/client/index.d.ts +14 -11
- package/lib/types/client/locales.d.ts +17 -21
- package/lib/types/client/log.d.ts +1 -1
- package/lib/types/client/mount.d.ts +79 -0
- package/lib/types/client/pending-mount.d.ts +36 -0
- package/lib/types/client/pending.d.ts +43 -57
- package/lib/types/client/popover.d.ts +19 -19
- package/lib/types/client/retract.d.ts +32 -0
- package/lib/types/client/rewind-flow.d.ts +86 -0
- package/lib/types/client/styles.d.ts +5 -14
- package/lib/types/snapshot-format.d.ts +479 -0
- package/lib/types/snapshot-fs.d.ts +85 -0
- package/lib/types/snapshot.d.ts +5 -373
- package/package.json +1 -1
- package/lib/types/client/portals.d.ts +0 -301
package/README.en.md
CHANGED
|
@@ -15,7 +15,7 @@ A deliberately focused plugin with one job: **rewind to any user message, no mat
|
|
|
15
15
|
|
|
16
16
|
- **In-place rewind, no new session** — the target message and everything after it (replies, tool calls) are withdrawn from the model context *and* the rendered transcript, and the target's text is filled back into the composer for editing and re-sending. It all happens inside the original session: no new branch, no leftover copy.
|
|
17
17
|
- **Conversation and files rewind together** — Claude Code-aligned behavior: files edited by the write tools are tracked and restored by the same rewind, and external changes to already-tracked files are restorable too. **Partial tracking, before-write backup, store only on change**, with no git dependency.
|
|
18
|
-
- **Safety guarantees** — the session log only ever appends rewind markers and is never deleted or rewritten; file backup and restore are hardened; tests are comprehensive, and maintenance continues across DSH upgrades. Security model: [
|
|
18
|
+
- **Safety guarantees** — the session log only ever appends rewind markers and is never deleted or rewritten; file backup and restore are hardened; tests are comprehensive, and maintenance continues across DSH upgrades. Security model: [docs/security-model.md](docs/security-model.md).
|
|
19
19
|
|
|
20
20
|
## Preview
|
|
21
21
|
|
|
@@ -166,7 +166,7 @@ This plugin deliberately stays lightweight and focused on one thing — "convers
|
|
|
166
166
|
|
|
167
167
|
## Security
|
|
168
168
|
|
|
169
|
-
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 files"; 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. For sessions you've left inactive for a long time, a snapshot 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).
|
|
169
|
+
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 files"; 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. For sessions you've left inactive for a long time, a snapshot auto-cleanup (off by default) can remove their snapshot directory in whole, leaving the active session and the chat log untouched. Full security model: [docs/security-model.md](docs/security-model.md); to report a vulnerability, see [SECURITY.md](SECURITY.md).
|
|
170
170
|
|
|
171
171
|
## Development
|
|
172
172
|
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ DeepSeek Harness 插件:**一键就地回退对话到任意更早的用户消
|
|
|
15
15
|
|
|
16
16
|
- **就地消息回退**——目标消息及其之后的内容同时从**模型上下文**和**渲染对话**中撤回,目标消息文本自动回填输入框。始终在原会话内完成,不新建分支、不残留副本。
|
|
17
17
|
- **轻量备份还原**——对齐 Claude Code:追踪写类工具编辑过的文件,回退时一并还原,已跟踪文件的外部变更也能还原。**局部追踪、写前备份、不变不存**,不依赖 git。
|
|
18
|
-
- **安全保障**——会话日志仅追加回退标记,从不删改;文件备份与还原经安全加固;**完备测试**,随 DSH 升级长期维护。安全模型:[
|
|
18
|
+
- **安全保障**——会话日志仅追加回退标记,从不删改;文件备份与还原经安全加固;**完备测试**,随 DSH 升级长期维护。安全模型:[docs/security-model.md](docs/security-model.md)。
|
|
19
19
|
|
|
20
20
|
## 效果预览
|
|
21
21
|
|
|
@@ -153,7 +153,7 @@ rm <dsh home>/snapshot-cleanup-last-sweep.json
|
|
|
153
153
|
|
|
154
154
|
## 安全
|
|
155
155
|
|
|
156
|
-
本插件只向会话日志追加回退标记事件,从不删除或改写已记录的历史。工作区文件仅在「回退对话和文件」时被改写,备份存储于 `<dsh home>/rewind-snapshots/`;还原以备份为唯一来源。不触碰你的 git 仓库,无网络请求,不访问任何凭据。对**长期不活跃**的会话,另有默认关闭的快照自动清理可整目录移除其快照,不影响活动会话与对话日志。完整安全模型:[SECURITY.md](SECURITY.md)。
|
|
156
|
+
本插件只向会话日志追加回退标记事件,从不删除或改写已记录的历史。工作区文件仅在「回退对话和文件」时被改写,备份存储于 `<dsh home>/rewind-snapshots/`;还原以备份为唯一来源。不触碰你的 git 仓库,无网络请求,不访问任何凭据。对**长期不活跃**的会话,另有默认关闭的快照自动清理可整目录移除其快照,不影响活动会话与对话日志。完整安全模型:[docs/security-model.md](docs/security-model.md);漏洞报告政策见 [SECURITY.md](SECURITY.md)。
|
|
157
157
|
|
|
158
158
|
## 开发
|
|
159
159
|
|
package/SECURITY.md
CHANGED
|
@@ -1,183 +1,80 @@
|
|
|
1
|
-
# Security
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
the
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
only the model-visible surface is cut, so the next request derives its context
|
|
82
|
-
from the target onward. The marker's constant content carries no untrusted text
|
|
83
|
-
into the model context, and the client hides the withdrawn span from the
|
|
84
|
-
rendered transcript. The marker shape is specified in
|
|
85
|
-
[architecture](docs/architecture.md).
|
|
86
|
-
|
|
87
|
-
## Filesystem containment
|
|
88
|
-
|
|
89
|
-
- **Store root**: `<dsh home>/rewind-snapshots/` by default (`$DSH_HOME` feeds
|
|
90
|
-
the base, `~/.dsh` is the fallback); the `snapshotDir` config overrides the
|
|
91
|
-
whole path. The plugin reads no environment variable of its own. Deleting the
|
|
92
|
-
root only removes file backups; the store rebuilds.
|
|
93
|
-
- **Path sanitization**: session and call ids are scrubbed to a safe character
|
|
94
|
-
set before joining the store root; bare `.` and `..` are replaced — hostile
|
|
95
|
-
ids cannot traverse out of the root.
|
|
96
|
-
- **Links are refused**: planning skips a path whose last component is a
|
|
97
|
-
symlink or a hard link and reports it — a symlink would redirect the write
|
|
98
|
-
outside the checkpoint, and a hard link would clobber every other name of the
|
|
99
|
-
same inode. A link swapped in after planning is not re-checked.
|
|
100
|
-
- **Recorded locations are re-checked**: every record written by this build
|
|
101
|
-
carries the directory location resolved at commit time; a restore re-checks
|
|
102
|
-
it before touching the path, including after a restart. A directory that no
|
|
103
|
-
longer resolves there is refused and reported; a missing parent chain is
|
|
104
|
-
recreated only inside the recorded location. Data with no recorded location
|
|
105
|
-
falls back to the link check. This matches Claude Code's checkpoint behavior.
|
|
106
|
-
- **Observation sync (best-effort)**: after a restore, the plugin re-emits the
|
|
107
|
-
host's `fs/observed` event for the paths it changed (absent, or present with
|
|
108
|
-
the current version), so a host with the observation policy enabled does not
|
|
109
|
-
judge the next write against a pre-restore observation. It heals only the
|
|
110
|
-
inconsistency the restore itself created; later external changes are still
|
|
111
|
-
seen normally.
|
|
112
|
-
- **Restores name only recorded paths**: the store contains resolved display
|
|
113
|
-
paths of the session's own write-class tool calls (plus boundary re-checks
|
|
114
|
-
over that same tracked set) — a restore can never write an arbitrary path.
|
|
115
|
-
- **Path resolution**: relative paths resolve against the session's current
|
|
116
|
-
working directory, read from the service the fs tools resolve through.
|
|
117
|
-
- **Bounded backups**: the store keeps a bounded number of anchor groups per
|
|
118
|
-
session, except groups a non-terminal restore still references (pinned so the
|
|
119
|
-
operation can be finished); the exact cap is in
|
|
120
|
-
[the store format](docs/format.md). Across sessions, the opt-in sweep removes
|
|
121
|
-
whole long-inactive session directories and never targets the active session.
|
|
122
|
-
|
|
123
|
-
## Crash safety
|
|
124
|
-
|
|
125
|
-
- **Ordered commits**: a checkpoint writes the before-sidecar (a raw byte copy)
|
|
126
|
-
before publishing the entry, so a crash can leave an unreferenced sidecar but
|
|
127
|
-
never an entry whose bytes are missing. Every JSON write goes to a sibling
|
|
128
|
-
temp file and is renamed over the target: a crash can leave only an inert
|
|
129
|
-
`.tmp`, never a readable half-written file, and readers never pick up temp
|
|
130
|
-
files.
|
|
131
|
-
- **Journaled restores**: before mutating anything, a restore captures each
|
|
132
|
-
planned path's pre-restore state and persists an intent journal, then marks
|
|
133
|
-
each action done as it is applied. A crash at any point leaves the journal on
|
|
134
|
-
disk.
|
|
135
|
-
- **Disk is truth**: an interrupted operation keeps its journal, and recovery
|
|
136
|
-
re-derives the true state from the real disk — which paths already match the
|
|
137
|
-
goal and which are pending, can finish the operation, and can undo it to the
|
|
138
|
-
exact pre-restore state. A journal whose goal is already reached auto-heals.
|
|
139
|
-
The plugin never runs recovery on its own: an interrupted operation stays
|
|
140
|
-
interrupted until an explicit caller resumes or undoes it.
|
|
141
|
-
- **Fail-loud vs fail-soft**: a corrupt journal is reported as
|
|
142
|
-
`recovery-required`, never silently dropped — dropping it would erase the
|
|
143
|
-
recovery record. Corrupt entries are ignored (they lose one backup, not the
|
|
144
|
-
recovery path), and a missing or truncated sidecar fails only that file —
|
|
145
|
-
never a delete of the live file.
|
|
146
|
-
- **Journal IO never fails the restore**: if the journal cannot be written, the
|
|
147
|
-
restore proceeds with pre-journal semantics; crash safety degrades, behavior
|
|
148
|
-
does not.
|
|
149
|
-
|
|
150
|
-
## Explicit non-goals
|
|
151
|
-
|
|
152
|
-
This section declares scope, not a gap list: each item names what stays outside
|
|
153
|
-
the plugin and what remains the host's or the user's responsibility. The
|
|
154
|
-
boundary contract is in [design boundaries](docs/boundaries.md).
|
|
155
|
-
|
|
156
|
-
- It does **not** sandbox other processes or stop them from changing files
|
|
157
|
-
concurrently.
|
|
158
|
-
- It does **not** provide confidentiality or tamper resistance against the same
|
|
159
|
-
OS user: **store** files use the process default permissions under the
|
|
160
|
-
process umask, and the host user remains trusted. Restored **workspace** files
|
|
161
|
-
carry their recorded permission bits (a content restore reapplies them,
|
|
162
|
-
best-effort), while a mode difference alone never triggers a restore.
|
|
163
|
-
- It does **not** exclude paths from the store, and it introduces no authority of
|
|
164
|
-
its own: sensitive files (e.g. a `.env` the model may edit) and files outside
|
|
165
|
-
the workspace the model may touch are backed up and restorable, unless over
|
|
166
|
-
the size cap. Keeping a path out is a **DSH model-permission** concern, not a
|
|
167
|
-
rewind feature.
|
|
168
|
-
- It does **not** touch git, make network requests, or access credentials.
|
|
169
|
-
- It does **not** roll back whole-log state: search, telemetry and `/export`
|
|
170
|
-
still see withdrawn messages (by design; see [boundaries](docs/boundaries.md)).
|
|
171
|
-
- It does **not** restore files written by a cancelled tool call that never
|
|
172
|
-
committed a backup.
|
|
173
|
-
- **Subagent session edits are not tracked**: a subagent runs its own session,
|
|
174
|
-
so its files cannot be restored by a rewind of the parent, and the plugin
|
|
175
|
-
offers no rewind inside one. See [boundaries](docs/boundaries.md).
|
|
176
|
-
|
|
177
|
-
## Reporting
|
|
178
|
-
|
|
179
|
-
Report a vulnerability through the repository's GitHub security channel or to
|
|
180
|
-
a repository maintainer. Include the plugin version/commit, the DSH
|
|
181
|
-
(`@deepseek-ai/*`) version, the platform, and a minimal reproduction — and
|
|
182
|
-
whether the failure happened before or after workspace mutation. Do not open a
|
|
183
|
-
public issue for a vulnerability.
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Only the latest stable release receives security fixes.
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| Latest stable `0.x` release | Yes |
|
|
10
|
+
| Earlier stable releases | No — upgrade to the latest release |
|
|
11
|
+
| `alpha` / `rc` pre-releases | No — published for testing only |
|
|
12
|
+
|
|
13
|
+
## Reporting a vulnerability
|
|
14
|
+
|
|
15
|
+
Report privately through GitHub's private vulnerability reporting:
|
|
16
|
+
**[Report a vulnerability](https://github.com/SiriLee/dsh-rewind/security/advisories/new)**
|
|
17
|
+
— or open **Security** → **Report a vulnerability** from the repository. It
|
|
18
|
+
creates a private advisory that only you and the maintainer can see.
|
|
19
|
+
|
|
20
|
+
Please do not open a public issue, pull request, or discussion for a
|
|
21
|
+
vulnerability before a fix has been released.
|
|
22
|
+
|
|
23
|
+
Include as much of the following as you can:
|
|
24
|
+
|
|
25
|
+
- the plugin version or commit;
|
|
26
|
+
- the DeepSeek Harness version (`@deepseek-ai/*` packages);
|
|
27
|
+
- the platform and Node.js version;
|
|
28
|
+
- a minimal reproduction: configuration, steps, and the relevant log output;
|
|
29
|
+
- whether the failure happened before or after a workspace file was mutated.
|
|
30
|
+
|
|
31
|
+
If the problem is in DSH itself or in another plugin, report it to that project.
|
|
32
|
+
|
|
33
|
+
## What to expect
|
|
34
|
+
|
|
35
|
+
- We aim to acknowledge a report within 7 days, with a first assessment
|
|
36
|
+
(in scope / out of scope / need more information) within a further 7 days.
|
|
37
|
+
- The report stays private while a fix is developed, and you are kept updated in
|
|
38
|
+
the advisory.
|
|
39
|
+
- We ask for up to 90 days before public disclosure; the advisory can be
|
|
40
|
+
published earlier once a fix is ready and you agree.
|
|
41
|
+
- With your consent, the published advisory credits you, and it enters the
|
|
42
|
+
GitHub Advisory Database so `npm audit` and Dependabot can flag affected
|
|
43
|
+
installs.
|
|
44
|
+
- If the report is not a vulnerability, or is out of scope, we explain why and
|
|
45
|
+
close the advisory.
|
|
46
|
+
|
|
47
|
+
## Scope
|
|
48
|
+
|
|
49
|
+
**In scope** — behavior that breaks a guarantee in the security model:
|
|
50
|
+
|
|
51
|
+
- a restore that writes outside the paths and locations the session recorded, or
|
|
52
|
+
that runs without an explicit user action;
|
|
53
|
+
- path traversal, or a symlink/hard-link escape, through a session id, a store
|
|
54
|
+
entry, or restore planning;
|
|
55
|
+
- rewriting, deleting, or truncating the session log, or otherwise changing
|
|
56
|
+
recorded history;
|
|
57
|
+
- a restore crossing sessions, or touching a path the session's own tools did
|
|
58
|
+
not record;
|
|
59
|
+
- a crash or a corrupt journal that loses a live file, or that silently drops a
|
|
60
|
+
recovery journal;
|
|
61
|
+
- a hostile session-log entry or tool argument escaping the containment that
|
|
62
|
+
document describes.
|
|
63
|
+
|
|
64
|
+
**Out of scope**:
|
|
65
|
+
|
|
66
|
+
- attackers who already have the same OS account, the host process, or the
|
|
67
|
+
workspace: the host user is trusted, and store files use the process umask;
|
|
68
|
+
- vulnerabilities in DSH or other plugins — report them upstream;
|
|
69
|
+
- actions the model is allowed to take under DSH's permission model (for
|
|
70
|
+
example, editing a `.env` file), unless they escape that model;
|
|
71
|
+
- behavior the security model explicitly lists as a non-goal.
|
|
72
|
+
|
|
73
|
+
## Security model
|
|
74
|
+
|
|
75
|
+
The guarantees, the containment rules, and the explicit non-goals are
|
|
76
|
+
documented at
|
|
77
|
+
**https://github.com/SiriLee/dsh-rewind/blob/main/docs/security-model.md**.
|
|
78
|
+
|
|
79
|
+
Reporters are welcome to test those claims: a mismatch between that document and
|
|
80
|
+
the code is itself worth reporting.
|