dsh-rewind-plugin 0.7.5 → 0.9.0-alpha.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 +12 -9
- package/README.md +12 -9
- package/SECURITY.md +6 -7
- package/docs/README.md +2 -1
- package/docs/architecture.md +39 -21
- package/docs/compat/audit.md +32 -34
- package/docs/contract/client-contract.md +1 -1
- package/docs/contract/client-contract.zh.md +1 -1
- package/docs/format.md +11 -8
- package/docs/harness-reference.md +17 -4
- package/docs/release/release.md +82 -24
- package/docs/release/release.zh.md +63 -16
- package/docs/rewind-fix.md +80 -0
- package/docs/rewind-fix.zh.md +80 -0
- package/lib/client.js +15 -41
- package/lib/index.js +531 -73
- package/lib/types/client/hidden.d.ts +26 -35
- package/lib/types/client/index.d.ts +19 -1
- package/lib/types/client/popover.d.ts +8 -9
- package/lib/types/client/portals.d.ts +25 -40
- package/lib/types/client/settings-card.d.ts +3 -4
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/index.d.ts +11 -5
- package/lib/types/locales.d.ts +19 -0
- package/lib/types/rewind-fix.d.ts +109 -0
- package/lib/types/rewind-marker-repair.d.ts +83 -0
- package/lib/types/rewind.d.ts +14 -57
- package/lib/types/session-log-io.d.ts +57 -0
- package/lib/types/snapshot-cleanup.d.ts +3 -3
- package/package.json +44 -41
- package/docs/compat/troubleshooting.md +0 -41
- package/docs/compat/troubleshooting.zh.md +0 -29
- package/lib/types/session-events.d.ts +0 -37
- package/lib/types/settings-locale.d.ts +0 -33
package/docs/release/release.md
CHANGED
|
@@ -33,45 +33,103 @@ Open `https://www.npmjs.com/package/dsh-rewind-plugin` → package **settings**
|
|
|
33
33
|
|
|
34
34
|
## Subsequent releases (CI, automatic)
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
The release workflow is **version-driven**: it derives the npm dist-tag from the
|
|
37
|
+
version in `package.json`, independent of branch. A stable version publishes to
|
|
38
|
+
`latest`; a pre-release publishes to the dist-tag named by its pre-release
|
|
39
|
+
identifier (e.g. `0.9.0-alpha.1` → `alpha`, `0.9.0-rc.1` → `rc`). The dist-tag is
|
|
40
|
+
never passed by hand.
|
|
41
|
+
|
|
42
|
+
The line being released determines the branch and the bump:
|
|
43
|
+
|
|
44
|
+
| Release | Branch | Bump | dist-tag |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| Stable patch (current line) | `release/0.8.x` | `npm version patch` | `latest` |
|
|
47
|
+
| Pre-release (next line) | `main` | `npm version prerelease --preid=alpha` | `alpha` |
|
|
48
|
+
| Stable (next line) | `main` | `npm version 0.9.0` | `latest` |
|
|
40
49
|
|
|
50
|
+
Each release is `git push <branch>` followed by `git push <branch> --tags`.
|
|
51
|
+
|
|
52
|
+
- The release commit is **`chore: release vX.Y.Z`**; the **lightweight `vX.Y.Z`
|
|
53
|
+
tag** sits on it. Do **not** create tag/release first with `gh release create
|
|
54
|
+
<tag>` (it tags the remote `main` head and breaks the tag/version match).
|
|
41
55
|
- **Before bumping, manually confirm there is no newer DSH version the plugin
|
|
42
56
|
has not been verified against** (a pre-release can ship in DSH Desktop
|
|
43
|
-
without being on npm
|
|
57
|
+
without being on npm; see docs/compat/audit.md).
|
|
44
58
|
- The workflow verifies the tag matches `package.json`, runs typecheck + tests +
|
|
45
|
-
a full build + artifact verification, publishes with `--provenance`
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
a full build + artifact verification, publishes with `--provenance` (Sigstore)
|
|
60
|
+
to the version-derived dist-tag, and creates a GitHub Release (a pre-release
|
|
61
|
+
is created as a GitHub pre-release, not `latest`). It is **idempotent** — an
|
|
62
|
+
already published version is skipped.
|
|
48
63
|
- CI (`.github/workflows/ci.yml`) runs `npm run check` — typecheck + tests +
|
|
49
64
|
build + artifact verification + a `npm pack --dry-run` — on every push / PR
|
|
50
65
|
across both Node engines boundary versions; the tarball layout is guarded by
|
|
51
66
|
`tests/package-layout.test.ts`.
|
|
67
|
+
- The GitHub Release body is auto-created with `--generate-notes` as a
|
|
68
|
+
**placeholder** (`--latest` / `--prerelease` per version). After the publish
|
|
69
|
+
run succeeds, overwrite the body by hand in the repo's bilingual style
|
|
70
|
+
(Chinese first, then English) — never keep the auto text as the final note.
|
|
52
71
|
|
|
53
|
-
## DSH version alignment (peer
|
|
72
|
+
## DSH version alignment (single peer tuple)
|
|
54
73
|
|
|
55
74
|
DSH is still in rc; npm's prerelease matching rules require a peer range to
|
|
56
75
|
share the host version's `[major, minor, patch]` tuple. So `peerDependencies`
|
|
57
|
-
uses
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- **When to update**: only when DSH releases a new tuple
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- **Exception — `@deepseek-ai/dsh-client-runtime`**: it never published a
|
|
65
|
-
`0.1.2-alpha.*` (npm `next` is `0.1.1-rc.2`) and is imported `import type`
|
|
66
|
-
only, so keep it at `^0.1.0-rc.6 || ^0.1.1-rc.2` (no `0.1.2` member).
|
|
76
|
+
uses one peer tuple per DSH line (e.g. `^0.1.2-rc.1`), replaced (never
|
|
77
|
+
appended) when DSH releases a new tuple.
|
|
78
|
+
|
|
79
|
+
- **When to update**: only when DSH releases a new tuple (`0.1.2 → 0.1.3 →
|
|
80
|
+
0.2.x`); rc rolling within a tuple (`0.1.2-rc.1 → rc.2`) needs nothing. All
|
|
81
|
+
`@deepseek-ai/*` packages release together; `npm view @deepseek-ai/dsh version`
|
|
82
|
+
is the authoritative signal.
|
|
67
83
|
- **Published-tuple check (optional)**: `node scripts/check-dsh-version.mjs`
|
|
68
|
-
compares the `latest` dist-tag version against the
|
|
84
|
+
compares the `latest` dist-tag version against the tuple the peers cover
|
|
69
85
|
(exit 0 = nothing to do, exit 1 = update). It reads the `latest` tag only; a
|
|
70
|
-
pre-release published under another tag
|
|
86
|
+
pre-release published under another tag or bundled without
|
|
71
87
|
going to npm is a manual pre-release check — see the "Before bumping" step above.
|
|
72
|
-
- **Update steps**:
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
- **Update steps**: replace every `@deepseek-ai/dsh-*` peer's tuple with
|
|
89
|
+
`^<latest>` → bump devDependencies to the latest → `npm install` →
|
|
90
|
+
`npm run check` → release.
|
|
75
91
|
- **After DSH goes final**: final releases are not bound by the prerelease
|
|
76
92
|
tuple rule, so the peers can converge to a single stable range (e.g.
|
|
77
93
|
`^0.1.x`); this section can then be deleted.
|
|
94
|
+
|
|
95
|
+
## Versioned-line release model
|
|
96
|
+
|
|
97
|
+
**One release targets one DSH version line.** The plugin's own version is
|
|
98
|
+
independent of the host; a release declares its DSH line through the peer
|
|
99
|
+
constraint (a single companion tuple), never through the plugin version.
|
|
100
|
+
|
|
101
|
+
| Plugin version | DSH line | Role (example) |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| `0.7.x` | `0.1.1` + `0.1.2` (broad) | frozen / EOL |
|
|
104
|
+
| `0.8.x` | `0.1.2-rc.1` (single) | current line |
|
|
105
|
+
| `0.9.x` | `0.1.3` (single) | following line |
|
|
106
|
+
|
|
107
|
+
The rows are illustrative — the DSH line a release targets is the peer
|
|
108
|
+
constraint, and its npm dist-tag is derived from the version (see above), so
|
|
109
|
+
this model does not track the plugin's own version number.
|
|
110
|
+
|
|
111
|
+
**Versioning.** A DSH version-line break is a MAJOR bump (incompatible with the
|
|
112
|
+
prior DSH line). Within a line, MINOR/PATCH remain compatible.
|
|
113
|
+
|
|
114
|
+
**Branching (trunk-based).** `main` is the single integration and release line
|
|
115
|
+
and is always releasable. The currently-shipped stable is cut into a short-lived
|
|
116
|
+
`release/<version>.x` maintenance branch from its release commit; that branch
|
|
117
|
+
receives backported fixes while `main` advances to the next line. The prior
|
|
118
|
+
(broad-compat) line is frozen as a tag, with no branch.
|
|
119
|
+
|
|
120
|
+
**dist-tag routing.** The release workflow derives the npm dist-tag from the
|
|
121
|
+
version: a stable version publishes to `latest`; a pre-release publishes to the
|
|
122
|
+
dist-tag named by its pre-release identifier (`0.9.0-alpha.1` → `alpha`,
|
|
123
|
+
`0.9.0-rc.1` → `rc`). A pre-release never occupies `latest`.
|
|
124
|
+
|
|
125
|
+
**Support window / EOL.** A DSH line is supported within a declared window. By
|
|
126
|
+
default the window runs until the next DSH line ships as `latest`; after that
|
|
127
|
+
the line is EOL, frozen, and receives no further patches. Here `0.8.x`
|
|
128
|
+
(`0.1.2-rc.1`) is supported until `0.9.x` (`0.1.3`) ships as `latest`.
|
|
129
|
+
|
|
130
|
+
**Bug-fix flow (forward-fix then backport).** A fix affecting multiple supported
|
|
131
|
+
lines is applied on `main` first, then backported to each still-supported
|
|
132
|
+
release branch. A fix specific to one line is applied only on that line.
|
|
133
|
+
|
|
134
|
+
The single-line model uses one peer tuple per release (see DSH version
|
|
135
|
+
alignment above); the OR-union multi-line practice it replaced is not used.
|
|
@@ -32,37 +32,84 @@ npm publish --access public
|
|
|
32
32
|
|
|
33
33
|
## 后续发布(CI 自动)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
发布 workflow 为**版本驱动**:npm dist-tag 由 `package.json` 中的版本号决定、与分支无关。
|
|
36
|
+
稳定版发 `latest`;pre-release 发到与其 pre-release 标识符同名的 dist-tag
|
|
37
|
+
(如 `0.9.0-alpha.1` → `alpha`、`0.9.0-rc.1` → `rc`)。dist-tag 无需手传。
|
|
38
|
+
|
|
39
|
+
按要发布的版本线选择分支与版本步进:
|
|
40
|
+
|
|
41
|
+
| 发布类型 | 分支 | 版本步进 | dist-tag |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| 当前线稳定补丁 | `release/0.8.x` | `npm version patch` | `latest` |
|
|
44
|
+
| 下一线 pre-release | `main` | `npm version prerelease --preid=alpha` | `alpha` |
|
|
45
|
+
| 下一线正式版 | `main` | `npm version 0.9.0` | `latest` |
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
每次发布为 `git push <分支>`,再 `git push <分支> --tags`。
|
|
48
|
+
|
|
49
|
+
- 发布提交为 **`chore: release vX.Y.Z`**,**轻量 `vX.Y.Z` tag** 落在该提交上。
|
|
50
|
+
**不要**用 `gh release create <tag>` 事先建 tag/release(会打到远端 `main`
|
|
51
|
+
HEAD,导致 tag/版本校验失败)。
|
|
52
|
+
- **升版前手动确认**:确认无插件未针对其验证过的更新 DSH 版本(pre-release
|
|
53
|
+
可能只随 Desktop 捆绑、而不发到 npm;见 docs/compat/audit.md)。
|
|
41
54
|
- workflow 校验 tag 与 `package.json` 版本一致,跑 typecheck + 测试 + 完整
|
|
42
|
-
构建 + 产物验证,以 `--provenance`(Sigstore
|
|
55
|
+
构建 + 产物验证,以 `--provenance`(Sigstore)发布到版本推导出的 dist-tag,
|
|
56
|
+
并创建 GitHub Release(pre-release 会建成 GitHub pre-release,而非 `latest`)。
|
|
43
57
|
**幂等**——已发布的版本会跳过。
|
|
44
58
|
- CI(`.github/workflows/ci.yml`)在每次 push / PR 跑 `npm run check`——
|
|
45
59
|
typecheck + 测试 + 构建 + 产物验证 + `npm pack --dry-run`,且覆盖
|
|
46
60
|
engines 两个边界版本;tarball 布局由 `tests/package-layout.test.ts` 守护。
|
|
61
|
+
- GitHub Release 正文由 workflow 以 `--generate-notes` 自动生成,仅是**占位**
|
|
62
|
+
(按版本取 `--latest` / `--prerelease`)。发布运行成功后,请用**手写双语**
|
|
63
|
+
正文(中文在前、英文在后)覆盖——切勿把自动文本当作最终发布说明。
|
|
47
64
|
|
|
48
|
-
## DSH
|
|
65
|
+
## DSH 版本适配(单一 peer 元组)
|
|
49
66
|
|
|
50
67
|
DSH 仍在 rc 阶段,npm 的 prerelease 匹配规则要求 peer 范围与宿主版本
|
|
51
68
|
**同 `[major, minor, patch]` 元组**才能匹配。因此 peerDependencies 采用
|
|
52
|
-
|
|
53
|
-
并随 DSH 发版追加。
|
|
69
|
+
**每一条 DSH 线一个 peer 元组**(如 `^0.1.2-rc.1`),DSH 发新元组时**替换**而非追加。
|
|
54
70
|
|
|
55
|
-
- **何时需要更新**:仅当 DSH 发布新元组(`0.1.
|
|
56
|
-
同元组内 rc 滚动(`0.1.
|
|
71
|
+
- **何时需要更新**:仅当 DSH 发布新元组(`0.1.2 → 0.1.3 → 0.2.x`)时;
|
|
72
|
+
同元组内 rc 滚动(`0.1.2-rc.1 → rc.2`)无需动作。DSH 所有包同版本发布,
|
|
57
73
|
`npm view @deepseek-ai/dsh version` 即权威信号。
|
|
58
|
-
- **例外 —— `@deepseek-ai/dsh-client-runtime`**:它从未发布 `0.1.2-alpha.*`
|
|
59
|
-
(npm `next` 为 `0.1.1-rc.2`)且仅被 `import type` 引用,保持
|
|
60
|
-
`^0.1.0-rc.6 || ^0.1.1-rc.2` 即可(无 `0.1.2` 元组项)。
|
|
61
74
|
- **已发布元组检查(可选)**:`node scripts/check-dsh-version.mjs` 用 npm `latest`
|
|
62
75
|
dist-tag 版本对比 peer 覆盖的元组(exit 0 无需动作,exit 1 需要)。它**只读
|
|
63
|
-
`latest` tag**;发布在其它 tag
|
|
76
|
+
`latest` tag**;发布在其它 tag 的 pre-release 走**手动发布前检查**
|
|
64
77
|
——见上文"升版前手动确认"。
|
|
65
|
-
-
|
|
78
|
+
- **更新步骤**:把每个 `@deepseek-ai/dsh-*` peer 的元组**替换**为 `^<最新版>`
|
|
66
79
|
→ devDependencies 同步升到最新 → `npm install` → `npm run check` → 发版。
|
|
67
80
|
- **正式版后收敛**:DSH 发布 final 版本后,正式版不受 prerelease 元组规则
|
|
68
81
|
限制,peer 可收敛为稳定的 `^0.1.x` 单范围,此节即可删除。
|
|
82
|
+
|
|
83
|
+
## 发布版本线模型
|
|
84
|
+
|
|
85
|
+
**一个发布对准一条 DSH 版本线。** 插件自身版本号与宿主解耦;某发布所对准的
|
|
86
|
+
DSH 线由 peer 约束(单一 companion 元组)声明,而非插件版本号。
|
|
87
|
+
|
|
88
|
+
| 插件版本 | DSH 线 | 角色(示例) |
|
|
89
|
+
| --- | --- | --- |
|
|
90
|
+
| `0.7.x` | `0.1.1` + `0.1.2`(广兼容) | 冻结 / EOL |
|
|
91
|
+
| `0.8.x` | `0.1.2-rc.1`(单线) | 当前线 |
|
|
92
|
+
| `0.9.x` | `0.1.3`(单线) | 后续线 |
|
|
93
|
+
|
|
94
|
+
各行仅为示例——某个发布对准的 DSH 线由 peer 约束声明,其 npm dist-tag 按版本号
|
|
95
|
+
派生(见上文),因此本模型不跟随插件自身版本号变化。
|
|
96
|
+
|
|
97
|
+
**版本号。** DSH 版本线破坏性变更 = MAJOR 升版(与更早 DSH 线向后不兼容)。
|
|
98
|
+
同一线内 MINOR/PATCH 保持向后兼容。
|
|
99
|
+
|
|
100
|
+
**分支(主干开发)。** `main` 是唯一集成与发布线,始终可发版。当前已发布
|
|
101
|
+
稳定版从发布提交点切出短命 `release/<版本>.x` 维护分支;该分支承接
|
|
102
|
+
backport 修复,同时 `main` 前进到下一线。更早的(广兼容)线冻结成 tag,无分支。
|
|
103
|
+
|
|
104
|
+
**dist-tag 路由。** 发布 workflow 按版本号取 npm dist-tag:稳定版发 `latest`;
|
|
105
|
+
pre-release 发到与其 pre-release 标识符同名的 dist-tag(`0.9.0-alpha.1` →
|
|
106
|
+
`alpha`、`0.9.0-rc.1` → `rc`)。pre-release 不占据 `latest`。
|
|
107
|
+
|
|
108
|
+
**支持窗口 / EOL。** 一条 DSH 线仅在声明的窗口内受支持。默认窗口到下一
|
|
109
|
+
DSH 线作为 `latest` 发布为止;此后该线 EOL、冻结、不再发补丁。此处 `0.8.x`
|
|
110
|
+
(`0.1.2-rc.1`)支持到 `0.9.x`(`0.1.3`)作为 `latest` 发布为止。
|
|
111
|
+
|
|
112
|
+
**Bug 修复流程(先向前修,再回迁)。** 跨多条支持线的修复,先在 `main` 上
|
|
113
|
+
修复,再回迁到各仍受支持的 release 分支。仅特定线的修复,只在对应线修复。
|
|
114
|
+
|
|
115
|
+
单线模型每个发布使用单一 peer 元组(见上文「DSH 版本适配」);其取代的 OR 并集多线做法已不使用。
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Rewind-marker update guide
|
|
2
|
+
|
|
3
|
+
> English | [中文](rewind-fix.zh.md)
|
|
4
|
+
|
|
5
|
+
## Applies to
|
|
6
|
+
|
|
7
|
+
- **DSH**: `0.1.2-rc.1`
|
|
8
|
+
- **dsh-rewind**: `0.9.0-alpha.1`
|
|
9
|
+
|
|
10
|
+
## Background
|
|
11
|
+
|
|
12
|
+
`/rewind` appends a **marker** to the session log, telling DSH "everything from this screen onward is withdrawn". Aged plugin versions used a "ghost step frame" for this; the current version uses a more canonical, functionally-equivalent shape — a content-empty `user/message` that replaces the content after the target (see the [README](../README.en.md), the "How it works" section).
|
|
13
|
+
|
|
14
|
+
The next DSH line (`v0.1.3`) will reject the old shape, so **sessions rewound by an older plugin may fail to open after an upgrade**. That's why the plugin ships `/dsh-rewind-fix`: it translates those old markers into the new shape and makes the sessions usable again. The new shape is also fully compatible with older DSH versions.
|
|
15
|
+
|
|
16
|
+
## Warnings
|
|
17
|
+
|
|
18
|
+
1. **It directly edits the session log** — it rewrites old rewind markers into the new format. The design already handles the common cases safely (**multiple web windows**, **exit / restart**, **switching sessions**, **power loss**): it only touches **closed** sessions, locks and **backs up** each one before writing, **rolls back** on failure, and is **idempotent** (safe to re-run). It changes the marker only — it deletes no conversation content and **loses no information**.
|
|
19
|
+
|
|
20
|
+
2. **It resets the matching snapshot backups** — the format update renumbers event seqs, so a session that was **updated** has its snapshot backups (the lightweight backups used to **restore files** for "conversation and code") no longer match, and the plugin **clears** them. This only affects **file restore** (those sessions can't use snapshots to restore files); backups start recording fresh. Sessions that weren't updated keep their snapshots.
|
|
21
|
+
|
|
22
|
+
## Steps
|
|
23
|
+
|
|
24
|
+
### Step 1 · Confirm your versions
|
|
25
|
+
|
|
26
|
+
First confirm your DSH and plugin versions are in the "Applies to" range — it's a prerequisite. If either is out of range, the tool may be unavailable or behave differently. You can just ask the AI for your current DSH and dsh-rewind plugin versions and check they're in range.
|
|
27
|
+
|
|
28
|
+
### Step 2 · Back up manually (optional)
|
|
29
|
+
|
|
30
|
+
The command rewrites session files and clears some snapshots, so a backup is a good idea. The default data directory is `~/.dsh` (if you've set `$DSH_HOME`, use its value):
|
|
31
|
+
```sh
|
|
32
|
+
cp -r ~/.dsh/sessions ~/.dsh/sessions.backup
|
|
33
|
+
cp -r ~/.dsh/rewind-snapshots ~/.dsh/rewind-snapshots.backup
|
|
34
|
+
```
|
|
35
|
+
Or copy them anywhere convenient.
|
|
36
|
+
|
|
37
|
+
### Step 3 · Start a new session, or pick one that was never rewound
|
|
38
|
+
|
|
39
|
+
The command only handles **closed** sessions, and it can't run on itself. So:
|
|
40
|
+
- **Start a new session** — ideally send any message first to initialize the window so you can see the command's progress and output.
|
|
41
|
+
- Or pick a session that was **never rewound** (one that needs no update).
|
|
42
|
+
|
|
43
|
+
Then run the command **in this new session**, and it will update the old sessions that need it.
|
|
44
|
+
|
|
45
|
+
> ⚠️ Don't run it in a session that itself needs updating and is currently open — the command skips this session, and any other currently-open/in-use session.
|
|
46
|
+
|
|
47
|
+
### Step 4 · Preview the update scope
|
|
48
|
+
|
|
49
|
+
Type `/dsh-rewind-fix` in the composer and send it. It only scans, doesn't write — it reports how many sessions were scanned, how many will be updated, how many skipped, and how many failed. Confirm those are what you expect before moving on.
|
|
50
|
+
```
|
|
51
|
+
/dsh-rewind-fix
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Step 5 · Execute
|
|
55
|
+
|
|
56
|
+
Once you're sure, re-enter the command with `--apply` to actually run it:
|
|
57
|
+
```
|
|
58
|
+
/dsh-rewind-fix --apply
|
|
59
|
+
```
|
|
60
|
+
This really rewrites the session logs and updates the old markers, and **may take a few minutes**. Please let it finish — **don't switch to another session window or close DSH** while it runs: a session you switch to is **safely skipped** (no corruption, but it won't be updated this pass and you'll need to re-run). A session that fails stays as it was, unharmed, and can be updated again later. Sessions that are updated also get their matching **snapshot backups cleared**, so file restore to earlier messages is no longer available for them.
|
|
61
|
+
|
|
62
|
+
### Step 6 · Restart DSH and preview again (optional)
|
|
63
|
+
|
|
64
|
+
After the run, restart DSH, then **preview once more**:
|
|
65
|
+
```
|
|
66
|
+
/dsh-rewind-fix
|
|
67
|
+
```
|
|
68
|
+
If it says there are no more sessions to update, the markers are all current and the old sessions will open normally.
|
|
69
|
+
|
|
70
|
+
### Step 7 · Delete the backup after confirming (optional)
|
|
71
|
+
|
|
72
|
+
Use it for a while, and once everything works, delete the backup from step 2:
|
|
73
|
+
```sh
|
|
74
|
+
rm -rf ~/.dsh/sessions.backup ~/.dsh/rewind-snapshots.backup
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- The session logs are still v0 format; when DSH's later `v0 → v1 → v2` migration (in `v0.1.3`) arrives, if you've completed the `rewind` marker update, the markers themselves won't block it.
|
|
80
|
+
- Some sessions may still be blocked by an **unclosed turn** (a turn that was interrupted/cancelled and never wrote `turn/end`). Per the empirical analysis these are widespread and **unrelated to rewind** — they're a DSH-side issue and this tool doesn't handle them. **So updating the markers doesn't guarantee you can upgrade to the next DSH line.**
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# 回退标记更新指南
|
|
2
|
+
|
|
3
|
+
> [English](rewind-fix.md) | 中文
|
|
4
|
+
|
|
5
|
+
## 适配
|
|
6
|
+
|
|
7
|
+
- **DSH**:`0.1.2-rc.1`
|
|
8
|
+
- **dsh-rewind**:`0.9.0-alpha.1`
|
|
9
|
+
|
|
10
|
+
## 背景
|
|
11
|
+
|
|
12
|
+
`/rewind` 回退会往会话日志追加一条**标记**,告诉 DSH 从某一屏往后都算「已撤回」。旧版插件用的是「幽灵步骤框架」写法;新版本已换成更规范且功能一致的形态——一条内容为空的 `user/message`,把目标之后的内容整体替换掉(原理见 [README](../README.md) 的「原理」一节)。
|
|
13
|
+
|
|
14
|
+
新一代 DSH(`v0.1.3`)会拒绝旧写法,所以**用旧版插件回退过的会话,升级后可能打不开**。因此本插件提供了 `/dsh-rewind-fix` 便捷命令,把这些旧标记「翻译」成新格式,让会话重新可用。新格式同样对旧版本 DSH 完全兼容。
|
|
15
|
+
|
|
16
|
+
## 警告
|
|
17
|
+
|
|
18
|
+
1. **会直接编辑会话日志** —— 把旧回退标记更新为新格式。设计上对**多个网页窗口、退出重启、切换会话、关机断电**等场景都已做安全处理:只处理**已关闭**的会话,写前加锁+备份、失败自动回滚、可重复运行(**幂等**);只动标记本身,不删任何对话内容、**无信息丢失**。
|
|
19
|
+
|
|
20
|
+
2. **会重置对应的快照备份** —— 格式更新会改变事件序号,**被更新过的会话**,其用于「回退对话和代码」时**还原文件**的对应快照备份将不再匹配,因此会被插件**清除**。这只影响**文件还原**,备份会重新开始记录;没被更新的会话快照不变。
|
|
21
|
+
|
|
22
|
+
## 操作步骤
|
|
23
|
+
|
|
24
|
+
### 第一步 · 确认版本
|
|
25
|
+
|
|
26
|
+
先确认你当前的 DSH 与插件版本落在「适配」范围里——这是前提。版本不符时,工具可能不可用或行为不符。你可以直接问 AI「当前的 DSH 与 dsh-rewind 插件版本」,并确认是否在「适配」范围内。
|
|
27
|
+
|
|
28
|
+
### 第二步 · 手动备份(可选)
|
|
29
|
+
|
|
30
|
+
命令会改写会话文件、删掉部分快照,稳妥起见先备份一下。默认数据目录是 `~/.dsh`(若你设置了 `$DSH_HOME`,请用它的值):
|
|
31
|
+
```sh
|
|
32
|
+
cp -r ~/.dsh/sessions ~/.dsh/sessions.backup
|
|
33
|
+
cp -r ~/.dsh/rewind-snapshots ~/.dsh/rewind-snapshots.backup
|
|
34
|
+
```
|
|
35
|
+
或者复制到任意合适的位置。
|
|
36
|
+
|
|
37
|
+
### 第三步 · 新建会话,或选一个从未回退过的会话
|
|
38
|
+
|
|
39
|
+
命令只处理已关闭的会话,而且不能在自己身上运行——建议你:
|
|
40
|
+
- **新建一个会话**,先随便发一条消息初始化窗口,确保能看到命令执行过程和结果
|
|
41
|
+
- 选择一个**从未回退过**(不需要更新)的会话
|
|
42
|
+
|
|
43
|
+
然后在**这个新会话窗口里**运行命令,去更新那些需要处理的旧会话。
|
|
44
|
+
|
|
45
|
+
> ⚠️ 别在「本身就需要更新、且正开着」的会话里运行——命令会跳过本会话,以及任何正被打开/使用的会话。
|
|
46
|
+
|
|
47
|
+
### 第四步 · 预览更新范围
|
|
48
|
+
|
|
49
|
+
在编辑区域输入 `/dsh-rewind-fix` 并发送。它只扫描、不写盘,会告诉你:扫描了几个会话、其中几个将被更新、几个跳过、几个失败。确认是你预期的那几项,再进下一步。
|
|
50
|
+
```
|
|
51
|
+
/dsh-rewind-fix
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 第五步 · 执行
|
|
55
|
+
|
|
56
|
+
确认无误后,重新输入命令并加 `--apply` 真正执行:
|
|
57
|
+
```
|
|
58
|
+
/dsh-rewind-fix --apply
|
|
59
|
+
```
|
|
60
|
+
这一步会真的改动会话日志、把旧标记更新成新格式,**可能需要几分钟**。请耐心等它跑完,**不要中途切换会话窗口**或关闭 DSH——被切到的会话会被**安全地跳过**(不损坏,但这次不会更新,需重新执行)。更新失败的会话保持原状态不变、无破坏,可重新进行更新。被更新的会话会同时**清掉对应的文件快照备份**,更早消息回退时文件还原不可用。
|
|
61
|
+
|
|
62
|
+
### 第六步 · 重启 DSH,再预览确认(可选)
|
|
63
|
+
|
|
64
|
+
执行完后重启一下 DSH,然后**再跑一次预览**:
|
|
65
|
+
```
|
|
66
|
+
/dsh-rewind-fix
|
|
67
|
+
```
|
|
68
|
+
如果提示已经没有待更新的会话,说明标记已全部更新到位,旧会话就能正常打开了。
|
|
69
|
+
|
|
70
|
+
### 第七步 · 确认正常后删除备份(可选)
|
|
71
|
+
|
|
72
|
+
继续使用一段时间,确认一切功能正常后,删掉第二步拷贝的备份即可:
|
|
73
|
+
```sh
|
|
74
|
+
rm -rf ~/.dsh/sessions.backup ~/.dsh/rewind-snapshots.backup
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 补充说明
|
|
78
|
+
|
|
79
|
+
- 当前会话日志仍为 v0 格式;DSH 后续(`v0.1.3`)的 `v0 → v1 → v2` 迁移到来时,如果你完成了 `rewind` 标记更新,标记本身就不会阻断它。
|
|
80
|
+
- 个别会话还可能因**未闭合的 turn**(某次回合被打断/取消、没写 `turn/end`)等问题而卡住迁移——根据实测分析,这些问题普遍存在,**与 rewind 无关**,属 DSH 侧自身问题,本工具不处理。**所以更新完标记并不保证一定能升到下一 DSH 线。**
|
package/lib/client.js
CHANGED
|
@@ -35,16 +35,12 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
37
|
// src/client/hidden.ts
|
|
38
|
-
function resolveChatWatch(resolveView,
|
|
38
|
+
function resolveChatWatch(resolveView, sessionId, cb) {
|
|
39
39
|
const view = resolveView(sessionId);
|
|
40
|
-
|
|
41
|
-
const face = resolveFace(sessionId);
|
|
42
|
-
return face?.subscribe(cb) ?? (() => {
|
|
40
|
+
return view?.subscribe?.(cb) ?? (() => {
|
|
43
41
|
});
|
|
44
42
|
}
|
|
45
|
-
function chatSnapshotOf(
|
|
46
|
-
const legacy = face?.getSnapshot().chat;
|
|
47
|
-
if (legacy !== void 0) return legacy;
|
|
43
|
+
function chatSnapshotOf(chatView) {
|
|
48
44
|
return chatView?.getSnapshot() ?? void 0;
|
|
49
45
|
}
|
|
50
46
|
function messageTextAt(chat, seq) {
|
|
@@ -328,16 +324,12 @@ var STYLE = `
|
|
|
328
324
|
}
|
|
329
325
|
|
|
330
326
|
/* ---- Snapshot-cleanup settings card (mirrors the harness PluginCard look) ---- */
|
|
331
|
-
/*
|
|
332
|
-
|
|
333
|
-
uses 16px + 0.5px border-l4 (and adds corner-shape: round). We intentionally
|
|
334
|
-
keep the 0.1.1-rc.2 values here for now; if the 0.1.2-rc.1 look is wanted,
|
|
335
|
-
switch these on the host version like the other dual-channel surfaces (see the
|
|
336
|
-
host-version probe in src/client/index.ts). */
|
|
327
|
+
/* Standardized on the 0.1.2 harness card look (border-radius 16px + 0.5px
|
|
328
|
+
border-l4, 0.5px internal separators). */
|
|
337
329
|
.dsh-rewind-cleanup-card {
|
|
338
330
|
list-style: none;
|
|
339
|
-
border:
|
|
340
|
-
border-radius:
|
|
331
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
332
|
+
border-radius: 16px;
|
|
341
333
|
background: var(--dsw-alias-bg-layer-3);
|
|
342
334
|
transition: border-color .16s, background .16s;
|
|
343
335
|
}
|
|
@@ -405,7 +397,7 @@ var STYLE = `
|
|
|
405
397
|
color: var(--dsw-alias-label-secondary);
|
|
406
398
|
}
|
|
407
399
|
.dsh-rewind-cleanup-body {
|
|
408
|
-
border-top:
|
|
400
|
+
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
409
401
|
margin: 0 16px;
|
|
410
402
|
padding: 4px 0 8px;
|
|
411
403
|
}
|
|
@@ -427,7 +419,7 @@ var STYLE = `
|
|
|
427
419
|
padding: 12px 0;
|
|
428
420
|
}
|
|
429
421
|
.dsh-rewind-cleanup-field + .dsh-rewind-cleanup-field {
|
|
430
|
-
border-top:
|
|
422
|
+
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
431
423
|
}
|
|
432
424
|
.dsh-rewind-cleanup-head {
|
|
433
425
|
display: flex;
|
|
@@ -648,7 +640,7 @@ function waitForCommand(session, chatOf, match, timeoutMs = 8e3, watch) {
|
|
|
648
640
|
settle({ kind: node.outcome.kind, text: node.outcome.text });
|
|
649
641
|
}
|
|
650
642
|
};
|
|
651
|
-
const unsubscribe =
|
|
643
|
+
const unsubscribe = watch(check);
|
|
652
644
|
const timer = setTimeout(() => settle(null), timeoutMs);
|
|
653
645
|
check();
|
|
654
646
|
});
|
|
@@ -988,15 +980,6 @@ var rewindLog = {
|
|
|
988
980
|
|
|
989
981
|
// src/client/portals.tsx
|
|
990
982
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
991
|
-
function fillComposerTextarea(text) {
|
|
992
|
-
const textarea = document.querySelector(COMPOSER_TEXTAREA_SELECTOR);
|
|
993
|
-
if (textarea === null) return false;
|
|
994
|
-
const setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
|
|
995
|
-
setter?.call(textarea, text);
|
|
996
|
-
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
997
|
-
textarea.focus();
|
|
998
|
-
return true;
|
|
999
|
-
}
|
|
1000
983
|
function fillComposerEditable(text) {
|
|
1001
984
|
const editable = document.querySelector(COMPOSER_EDITABLE_SELECTOR);
|
|
1002
985
|
if (editable === null) return false;
|
|
@@ -1020,7 +1003,6 @@ function fillComposerEditable(text) {
|
|
|
1020
1003
|
return true;
|
|
1021
1004
|
}
|
|
1022
1005
|
function fillComposer(text) {
|
|
1023
|
-
if (fillComposerTextarea(text)) return true;
|
|
1024
1006
|
return fillComposerEditable(text);
|
|
1025
1007
|
}
|
|
1026
1008
|
function writeComposer(text, facade) {
|
|
@@ -1089,7 +1071,7 @@ async function runRewindAndFill(session, seq, mode, currentSessionId, chatOf, wa
|
|
|
1089
1071
|
}
|
|
1090
1072
|
}
|
|
1091
1073
|
function composerSurface() {
|
|
1092
|
-
return document.querySelector(
|
|
1074
|
+
return document.querySelector(COMPOSER_EDITABLE_SELECTOR);
|
|
1093
1075
|
}
|
|
1094
1076
|
function showHint(text) {
|
|
1095
1077
|
const surface = composerSurface();
|
|
@@ -1106,16 +1088,11 @@ function showHint(text) {
|
|
|
1106
1088
|
}
|
|
1107
1089
|
window.setTimeout(() => hint.remove(), 3200);
|
|
1108
1090
|
}
|
|
1109
|
-
var COMPOSER_TEXTAREA_SELECTOR = "[data-input-scroll] textarea, textarea[data-phase]";
|
|
1110
1091
|
var COMPOSER_EDITABLE_SELECTOR = "[data-composer-input]";
|
|
1111
1092
|
var USER_SEAT_SELECTOR = '[data-chat-flow-kind="user"][data-chat-anchor-key], [data-chat-flow-kind="steering"][data-chat-anchor-key]';
|
|
1112
1093
|
var CHAT_SEAT_SELECTOR = "[data-chat-anchor-key]";
|
|
1113
|
-
var ACTIONS_ROOT_SELECTOR = "[data-time-hover-root], [data-actions-reveal]";
|
|
1114
1094
|
var PENDING_SEAT_SELECTOR = "[data-pending-steering]";
|
|
1115
1095
|
function actionsContainerOf(row) {
|
|
1116
|
-
const root = row?.matches(ACTIONS_ROOT_SELECTOR) ? row : row?.querySelector(ACTIONS_ROOT_SELECTOR);
|
|
1117
|
-
const actions = root?.lastElementChild;
|
|
1118
|
-
if (actions instanceof HTMLElement && actions.querySelector("button") !== null) return actions;
|
|
1119
1096
|
const buttons = Array.from(row?.querySelectorAll("button") ?? []);
|
|
1120
1097
|
const lastButton = buttons.filter((button) => !button.classList.contains(CLASS.button)).at(-1);
|
|
1121
1098
|
const structural = lastButton?.parentElement;
|
|
@@ -1306,7 +1283,6 @@ function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat, current
|
|
|
1306
1283
|
function composerText() {
|
|
1307
1284
|
const surface = composerSurface();
|
|
1308
1285
|
if (surface === null) return "";
|
|
1309
|
-
if (surface instanceof HTMLTextAreaElement) return surface.value;
|
|
1310
1286
|
return surface.textContent ?? "";
|
|
1311
1287
|
}
|
|
1312
1288
|
async function retractPending(session, itemId, text, setComposerText) {
|
|
@@ -1357,8 +1333,8 @@ function createRewindBridge(deps) {
|
|
|
1357
1333
|
}
|
|
1358
1334
|
|
|
1359
1335
|
// src/client/build-info.ts
|
|
1360
|
-
var PLUGIN_VERSION = true ? "0.
|
|
1361
|
-
var BUILD_HASH = true ? "
|
|
1336
|
+
var PLUGIN_VERSION = true ? "0.9.0-alpha.1" : "dev";
|
|
1337
|
+
var BUILD_HASH = true ? "ca24fc4e" : "dev";
|
|
1362
1338
|
|
|
1363
1339
|
// src/client/locales.ts
|
|
1364
1340
|
var zh = {
|
|
@@ -1616,7 +1592,6 @@ var name = "dsh-rewind";
|
|
|
1616
1592
|
var inject = ["slots", "sessions", "locale", "commandUi"];
|
|
1617
1593
|
var NS2 = "rewind";
|
|
1618
1594
|
var HEADER_ACTIONS_SLOT = "conversation.session.header.actions";
|
|
1619
|
-
var COMPOSER_TEXTAREA_SELECTOR2 = "[data-input-scroll] textarea, textarea[data-phase]";
|
|
1620
1595
|
var COMPOSER_EDITABLE_SELECTOR2 = "[data-composer-input]";
|
|
1621
1596
|
function apply(ctx) {
|
|
1622
1597
|
rewindLog.info("boot", `loaded v${PLUGIN_VERSION} (build ${BUILD_HASH})`);
|
|
@@ -1636,7 +1611,7 @@ function apply(ctx) {
|
|
|
1636
1611
|
if (session === void 0) return void 0;
|
|
1637
1612
|
try {
|
|
1638
1613
|
const view = uiConversation()?.binding(session.sessionId).target(CHAT_VIEW);
|
|
1639
|
-
return chatSnapshotOf(
|
|
1614
|
+
return chatSnapshotOf(view);
|
|
1640
1615
|
} catch {
|
|
1641
1616
|
return void 0;
|
|
1642
1617
|
}
|
|
@@ -1667,7 +1642,6 @@ function apply(ctx) {
|
|
|
1667
1642
|
return void 0;
|
|
1668
1643
|
}
|
|
1669
1644
|
},
|
|
1670
|
-
(id) => sessionOf(id),
|
|
1671
1645
|
sessionId,
|
|
1672
1646
|
cb
|
|
1673
1647
|
);
|
|
@@ -1777,7 +1751,7 @@ function apply(ctx) {
|
|
|
1777
1751
|
for (const name2 of ["rewind", "undo"]) {
|
|
1778
1752
|
yield commandUi.decorate({ name: name2, ...rewindPopupSpec });
|
|
1779
1753
|
}
|
|
1780
|
-
const composerSurface2 = () => document.querySelector(
|
|
1754
|
+
const composerSurface2 = () => document.querySelector(COMPOSER_EDITABLE_SELECTOR2);
|
|
1781
1755
|
yield () => {
|
|
1782
1756
|
style.remove();
|
|
1783
1757
|
};
|