dsh-rewind-plugin 0.7.5 → 0.8.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 +2 -1
- package/README.md +2 -1
- package/docs/architecture.md +5 -4
- package/docs/compat/audit.md +23 -25
- package/docs/contract/client-contract.md +1 -1
- package/docs/contract/client-contract.zh.md +1 -1
- package/docs/harness-reference.md +2 -2
- package/docs/release/release.md +80 -24
- package/docs/release/release.zh.md +61 -16
- package/lib/client.js +15 -41
- package/lib/index.js +14 -28
- 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/snapshot-cleanup.d.ts +3 -3
- package/package.json +44 -41
- package/lib/types/session-events.d.ts +0 -37
- package/lib/types/settings-locale.d.ts +0 -33
package/README.en.md
CHANGED
|
@@ -156,7 +156,8 @@ withdrew should consume the stable, locale-independent helpers exported from
|
|
|
156
156
|
2. **Lightweight file rewind has a cost** — in specific cases not all changes can be rewound. Consistent with Claude Code. See: [File-rewind tracking boundary](docs/compat/tracking-boundary.md).
|
|
157
157
|
3. **Rewinds from `≤ v0.2.4`** — sessions rewound with these versions may **fail to load history** after more conversation. Install a v0.3.3-or-earlier release and use its bundled repair tool ([docs/compat/troubleshooting.md](docs/compat/troubleshooting.md)).
|
|
158
158
|
4. **Rewinds from `≤ v0.3.3`** — compaction (`/compact`) is unavailable for those sessions. Newer versions are compatible; for affected old sessions, start a new session.
|
|
159
|
-
5. **The turn-rail shows rewound turns** — the right-side rail added in DSH `v0.1.2
|
|
159
|
+
5. **The turn-rail shows rewound turns** — the right-side rail added in DSH `v0.1.2` keeps ticks for withdrawn messages, and hovering shows the withdrawn text. Only a display difference; no functional impact.
|
|
160
|
+
6. **The system prompt is re-displayed after a rewind** — in DSH `v0.1.2`, rewinding and resending a message shows the "System prompt" component again, just like `/compact`. Only a display difference; no functional impact.
|
|
160
161
|
|
|
161
162
|
> [!NOTE]
|
|
162
163
|
> Browser diagnostics are available; see [Browser diagnostics](docs/compat/diagnostics.md).
|
package/README.md
CHANGED
|
@@ -143,7 +143,8 @@ dsh plugin --profile web add dsh-rewind-plugin
|
|
|
143
143
|
2. **轻量文件回退存在代价**——特定情况可能无法回退所有修改。行为与 Claude Code 一致。详见:[文件回退的追踪边界](docs/compat/tracking-boundary.zh.md)。
|
|
144
144
|
3. **v0.2.4 及更早版本**回退过的会话,继续对话后可能加载历史失败。可安装 v0.3.3 及之前版本的随附修复工具处理([完整步骤](docs/compat/troubleshooting.zh.md))。
|
|
145
145
|
4. **v0.3.3 及更早版本**回退过的会话,压缩对话(compact)不可用。新版本已兼容;受影响的旧会话建议新建会话。
|
|
146
|
-
5. **导轨显示已回退轮次**——DSH `v0.1.2
|
|
146
|
+
5. **导轨显示已回退轮次**——DSH `v0.1.2` 新增右侧导轨,为已撤回消息保留刻度,悬浮显示已撤回正文。仅显示差异,无功能影响。
|
|
147
|
+
6. **回退重显系统提示词**——DSH `v0.1.2` 回退重发消息时,与 `/compact` 一样重显“系统提示词”组件。仅显示差异,无功能影响。
|
|
147
148
|
|
|
148
149
|
> [!NOTE]
|
|
149
150
|
> 本插件提供浏览器端诊断输出;详见 [浏览器诊断](docs/compat/diagnostics.zh.md)。
|
package/docs/architecture.md
CHANGED
|
@@ -98,10 +98,11 @@ pruneStale cross-session auto-cleanup (default off): whole
|
|
|
98
98
|
|
|
99
99
|
## Compatibility strategy
|
|
100
100
|
|
|
101
|
-
- **Peer ranges as
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
a new tuple
|
|
101
|
+
- **Peer ranges as one tuple per DSH line** (`^0.1.2-rc.1`): npm's prerelease
|
|
102
|
+
rules require the peer range to share the host's `[major, minor, patch]`
|
|
103
|
+
tuple, so a new DSH tuple replaces the peer tuple (the single-line model);
|
|
104
|
+
`scripts/check-dsh-version.mjs` flags when a new tuple arrives. See
|
|
105
|
+
`docs/release/release.md`.
|
|
105
106
|
- **Test-driven investigation**: `tests/compat-invariants.ts` /
|
|
106
107
|
`compat-interop` / `compat-gaps` probe harness behavior and pin findings in
|
|
107
108
|
`docs/compat/audit.md`; `scripts/verify-host.mjs` runs a real end-to-end
|
package/docs/compat/audit.md
CHANGED
|
@@ -7,35 +7,33 @@
|
|
|
7
7
|
> compatibility invariants. A probe failure is a finding; it enters the
|
|
8
8
|
> fix/pin/record loop.
|
|
9
9
|
>
|
|
10
|
-
> Targeted version: npm `@deepseek-ai/*@0.1.
|
|
10
|
+
> Targeted version: npm `@deepseek-ai/*@0.1.2-rc.1` (matches `package-lock.json`).
|
|
11
11
|
> Source reference: the upstream [github.com/deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
12
12
|
>
|
|
13
|
-
> Version alignment: `peerDependencies` use
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
> tuple
|
|
18
|
-
> is a no-op. Signal: `npm view @deepseek-ai/dsh version`;
|
|
13
|
+
> Version alignment: `peerDependencies` use one tuple per DSH line
|
|
14
|
+
> (e.g. `^0.1.2-rc.1`). npm's prerelease matching rules require a candidate to
|
|
15
|
+
> share the range comparator's `[major, minor, patch]` tuple, so a new DSH tuple
|
|
16
|
+
> replaces the peer tuple (single-line model); any prerelease within the SAME
|
|
17
|
+
> tuple is a no-op. Signal: `npm view @deepseek-ai/dsh version`;
|
|
19
18
|
> flow: `scripts/check-dsh-version.mjs` (it reads the `latest` dist-tag only; a
|
|
20
|
-
>
|
|
21
|
-
> `alpha`, is a manual pre-release check).
|
|
19
|
+
> prerelease published under another tag is a manual pre-release check).
|
|
22
20
|
>
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
> `^0.1.2-alpha.2` peer member covers all six. `rc.2` (`latest`) stays the primary
|
|
28
|
-
> baseline; verified: rc.2, alpha.2, alpha.3, alpha.4, alpha.5, rc.1.
|
|
21
|
+
> The plugin targets a single DSH version line (`0.1.2-rc.1`); compatibility
|
|
22
|
+
> with earlier lines is not kept.
|
|
23
|
+
|
|
24
|
+
### Single channel (`0.1.2-rc.1`)
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
The plugin targets one DSH channel. Each seam below reads the 0.1.2-rc.1 shape
|
|
27
|
+
only (no `Session.events` / `[data-time-hover-root]` / `<textarea>` / face-`chat`
|
|
28
|
+
legacy branch).
|
|
31
29
|
|
|
32
|
-
| Seam |
|
|
33
|
-
| --- | --- |
|
|
34
|
-
| Host session log
|
|
35
|
-
| Client chat snapshot
|
|
36
|
-
| Client composer refill
|
|
37
|
-
| Client settings card | nested `ctx.inject(['settingsScope'])` |
|
|
38
|
-
| Client seat-button DOM
|
|
30
|
+
| Seam | 0.1.2-rc.1 implementation |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| Host session log | `session.snapshotEvents()` |
|
|
33
|
+
| Client chat snapshot | `uiConversation` `chat` view (`chatSnapshotOf`) |
|
|
34
|
+
| Client composer refill | `conversation.input.setDraft` facade |
|
|
35
|
+
| Client settings card | nested `ctx.inject(['settingsScope'])` |
|
|
36
|
+
| Client seat-button DOM | structural locate of the copy-`<button>` container (`actionsContainerOf`) |
|
|
39
37
|
|
|
40
38
|
## Definition of "fully compatible" (invariants)
|
|
41
39
|
|
|
@@ -61,7 +59,7 @@
|
|
|
61
59
|
- **rewind across a compact checkpoint**: `RewindError('not-on-surface')` refuses cleanly, no crash.
|
|
62
60
|
- **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`).
|
|
63
61
|
- **agent-loop cancellation**: `finally` guarantees step/turn closure; the rewind force-stop path leaves no dangling frame.
|
|
64
|
-
- **settings-card
|
|
62
|
+
- **settings-card registration**: the Snapshot cleanup card must be registered through a **nested** `ctx.inject(['settingsScope'], …)` — naming `settingsScope` in the module-level inject would keep the whole client plugin unmounted on a host without that service (card and rewind button would disappear). It reads `getSnapshot().value` + `set`, never the `mutate` write API.
|
|
65
63
|
|
|
66
64
|
## Known behavior boundaries (deterministic differences, non-crash, documented)
|
|
67
65
|
|
|
@@ -80,7 +78,7 @@ The plugin treats these as harness-side defects it does not compensate for. Each
|
|
|
80
78
|
### RU-I18N: host-side locale preference is not reliably readable at command registration (harness-side structural timing defect; plugin reads once, never retries)
|
|
81
79
|
|
|
82
80
|
- **Root cause (harness-side)**: the plugin resolves `activeLocale` in an
|
|
83
|
-
`ctx.inject(['settings'])` callback that reads `settings.get(
|
|
81
|
+
`ctx.inject(['settings'])` callback that reads `settings.get('locale').preference`
|
|
84
82
|
**once, with no retry** (`src/index.ts`). `dsh-client-locale`'s host half
|
|
85
83
|
registers that `locale` settings section **through its own `ctx.inject(['settings'])`**
|
|
86
84
|
(`packages/client/locale/src/index.ts`). Both callbacks wait only on `settings` and are
|
|
@@ -21,7 +21,7 @@ the plugin itself uses (it never reads the DOM or host copy):
|
|
|
21
21
|
```ts
|
|
22
22
|
import { hiddenSeqsOf, type HiddenChat } from 'dsh-rewind-plugin/client'
|
|
23
23
|
|
|
24
|
-
const chat =
|
|
24
|
+
const chat = uiConversation.binding(sessionId).target('chat')?.getSnapshot() // the 0.1.2-rc.1 "chat" view
|
|
25
25
|
const hidden = hiddenSeqsOf(chat as HiddenChat) // Set<number> of anchor seqs
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
```ts
|
|
21
21
|
import { hiddenSeqsOf, type HiddenChat } from 'dsh-rewind-plugin/client'
|
|
22
22
|
|
|
23
|
-
const chat =
|
|
23
|
+
const chat = uiConversation.binding(sessionId).target('chat')?.getSnapshot() // 0.1.2-rc.1 的 "chat" 视图
|
|
24
24
|
const hidden = hiddenSeqsOf(chat as HiddenChat) // 被隐藏的 anchor seq 集合
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -39,8 +39,8 @@ Also under `docs/` at the repo root: `persistence-catalog.md` (full
|
|
|
39
39
|
| `tools/pre-execute` / `execute` / `post-execute` | [packages/core/tools/src/index.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/core/tools/src/index.ts) |
|
|
40
40
|
| Client DOM anchors (`data-chat-flow-kind` / `data-chat-anchor-key`) | [packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx) |
|
|
41
41
|
| 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) |
|
|
42
|
-
| Client `SessionFace` (`command` / `cancel`) | [packages/
|
|
43
|
-
| Client
|
|
42
|
+
| Client `SessionFace` (`command` / `cancel`) | [packages/api/session-controller/src/client/contract/session.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/api/session-controller/src/client/contract/session.ts) |
|
|
43
|
+
| Client pending interaction (SessionPendingInteraction) | [packages/client/ui-session/src/client/index.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/ui-session/src/client/index.ts) |
|
|
44
44
|
|
|
45
45
|
## Plugin source layout
|
|
46
46
|
|
package/docs/release/release.md
CHANGED
|
@@ -33,45 +33,101 @@ 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 tag is the **lightweight `npm version` tag** (on the `chore: release …`
|
|
53
|
+
commit). Push it as-is — **do not** create the tag or the release beforehand
|
|
54
|
+
with `gh release create <tag>`: that tags the remote's current `main` head, so
|
|
55
|
+
an unpushed version bump leaves the tag on the wrong commit and the
|
|
56
|
+
tag/version match check fails.
|
|
41
57
|
- **Before bumping, manually confirm there is no newer DSH version the plugin
|
|
42
58
|
has not been verified against** (a pre-release can ship in DSH Desktop
|
|
43
|
-
without being on npm
|
|
59
|
+
without being on npm; see docs/compat/audit.md).
|
|
44
60
|
- The workflow verifies the tag matches `package.json`, runs typecheck + tests +
|
|
45
|
-
a full build + artifact verification, publishes with `--provenance`
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
a full build + artifact verification, publishes with `--provenance` (Sigstore)
|
|
62
|
+
to the version-derived dist-tag, and creates a GitHub Release (a pre-release
|
|
63
|
+
is created as a GitHub pre-release, not `latest`). It is **idempotent** — an
|
|
64
|
+
already published version is skipped.
|
|
48
65
|
- CI (`.github/workflows/ci.yml`) runs `npm run check` — typecheck + tests +
|
|
49
66
|
build + artifact verification + a `npm pack --dry-run` — on every push / PR
|
|
50
67
|
across both Node engines boundary versions; the tarball layout is guarded by
|
|
51
68
|
`tests/package-layout.test.ts`.
|
|
69
|
+
- The GitHub Release body is auto-created with `--generate-notes` as a
|
|
70
|
+
**placeholder** (`--latest` / `--prerelease` per version). After the publish
|
|
71
|
+
run succeeds, overwrite the body by hand in the repo's bilingual style
|
|
72
|
+
(Chinese first, then English) — never keep the auto text as the final note.
|
|
52
73
|
|
|
53
|
-
## DSH version alignment (peer
|
|
74
|
+
## DSH version alignment (single peer tuple)
|
|
54
75
|
|
|
55
76
|
DSH is still in rc; npm's prerelease matching rules require a peer range to
|
|
56
77
|
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).
|
|
78
|
+
uses one peer tuple per DSH line (e.g. `^0.1.2-rc.1`), replaced (never
|
|
79
|
+
appended) when DSH releases a new tuple.
|
|
80
|
+
|
|
81
|
+
- **When to update**: only when DSH releases a new tuple (`0.1.2 → 0.1.3 →
|
|
82
|
+
0.2.x`); rc rolling within a tuple (`0.1.2-rc.1 → rc.2`) needs nothing. All
|
|
83
|
+
`@deepseek-ai/*` packages release together; `npm view @deepseek-ai/dsh version`
|
|
84
|
+
is the authoritative signal.
|
|
67
85
|
- **Published-tuple check (optional)**: `node scripts/check-dsh-version.mjs`
|
|
68
|
-
compares the `latest` dist-tag version against the
|
|
86
|
+
compares the `latest` dist-tag version against the tuple the peers cover
|
|
69
87
|
(exit 0 = nothing to do, exit 1 = update). It reads the `latest` tag only; a
|
|
70
|
-
pre-release published under another tag
|
|
88
|
+
pre-release published under another tag or bundled without
|
|
71
89
|
going to npm is a manual pre-release check — see the "Before bumping" step above.
|
|
72
|
-
- **Update steps**:
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
- **Update steps**: replace every `@deepseek-ai/dsh-*` peer's tuple with
|
|
91
|
+
`^<latest>` → bump devDependencies to the latest → `npm install` →
|
|
92
|
+
`npm run check` → release.
|
|
75
93
|
- **After DSH goes final**: final releases are not bound by the prerelease
|
|
76
94
|
tuple rule, so the peers can converge to a single stable range (e.g.
|
|
77
95
|
`^0.1.x`); this section can then be deleted.
|
|
96
|
+
|
|
97
|
+
## Versioned-line release model
|
|
98
|
+
|
|
99
|
+
**One release targets one DSH version line.** The plugin's own version is
|
|
100
|
+
independent of the host; a release declares its DSH line through the peer
|
|
101
|
+
constraint (a single companion tuple), never through the plugin version.
|
|
102
|
+
|
|
103
|
+
| Plugin version | DSH line | Notes |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `0.7.x` | `0.1.1` + `0.1.2` (broad) | frozen / EOL |
|
|
106
|
+
| `0.8.x` | `0.1.2-rc.1` (single) | current stable |
|
|
107
|
+
| `0.9.x` | `0.1.3` (single) | next line |
|
|
108
|
+
|
|
109
|
+
**Versioning.** A DSH version-line break is a MAJOR bump (incompatible with the
|
|
110
|
+
prior DSH line). Within a line, MINOR/PATCH remain compatible.
|
|
111
|
+
|
|
112
|
+
**Branching (trunk-based).** `main` is the single integration and release line
|
|
113
|
+
and is always releasable. The currently-shipped stable is cut into a short-lived
|
|
114
|
+
`release/<version>.x` maintenance branch from its release commit; that branch
|
|
115
|
+
receives backported fixes while `main` advances to the next line. The prior
|
|
116
|
+
(broad-compat) line is frozen as a tag, with no branch.
|
|
117
|
+
|
|
118
|
+
**dist-tag routing.** The release workflow derives the npm dist-tag from the
|
|
119
|
+
version: a stable version publishes to `latest`; a pre-release publishes to the
|
|
120
|
+
dist-tag named by its pre-release identifier (`0.9.0-alpha.1` → `alpha`,
|
|
121
|
+
`0.9.0-rc.1` → `rc`). A pre-release never occupies `latest`.
|
|
122
|
+
|
|
123
|
+
**Support window / EOL.** A DSH line is supported within a declared window. By
|
|
124
|
+
default the window runs until the next DSH line ships as `latest`; after that
|
|
125
|
+
the line is EOL, frozen, and receives no further patches. Here `0.8.x`
|
|
126
|
+
(`0.1.2-rc.1`) is supported until `0.9.x` (`0.1.3`) ships as `latest`.
|
|
127
|
+
|
|
128
|
+
**Bug-fix flow (forward-fix then backport).** A fix affecting multiple supported
|
|
129
|
+
lines is applied on `main` first, then backported to each still-supported
|
|
130
|
+
release branch. A fix specific to one line is applied only on that line.
|
|
131
|
+
|
|
132
|
+
The single-line model uses one peer tuple per release (see DSH version
|
|
133
|
+
alignment above); the OR-union multi-line practice it replaced is not used.
|
|
@@ -32,37 +32,82 @@ 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
|
+
- tag 即 `npm version` 生成的**轻量 tag**(落在 `chore: release …` 提交上)。
|
|
50
|
+
直接原样 push 即可——**不要**用 `gh release create <tag>` 事先建 tag 或
|
|
51
|
+
release:它会给远端当前 `main` 的 HEAD 打 tag,若版本 bump 尚未 push,tag
|
|
52
|
+
就会落在错误提交上,导致 tag/版本一致性校验失败。
|
|
53
|
+
- **升版前手动确认**:确认无插件未针对其验证过的更新 DSH 版本(pre-release
|
|
54
|
+
可能只随 Desktop 捆绑、而不发到 npm;见 docs/compat/audit.md)。
|
|
41
55
|
- workflow 校验 tag 与 `package.json` 版本一致,跑 typecheck + 测试 + 完整
|
|
42
|
-
构建 + 产物验证,以 `--provenance`(Sigstore
|
|
56
|
+
构建 + 产物验证,以 `--provenance`(Sigstore)发布到版本推导出的 dist-tag,
|
|
57
|
+
并创建 GitHub Release(pre-release 会建成 GitHub pre-release,而非 `latest`)。
|
|
43
58
|
**幂等**——已发布的版本会跳过。
|
|
44
59
|
- CI(`.github/workflows/ci.yml`)在每次 push / PR 跑 `npm run check`——
|
|
45
60
|
typecheck + 测试 + 构建 + 产物验证 + `npm pack --dry-run`,且覆盖
|
|
46
61
|
engines 两个边界版本;tarball 布局由 `tests/package-layout.test.ts` 守护。
|
|
62
|
+
- GitHub Release 正文由 workflow 以 `--generate-notes` 自动生成,仅是**占位**
|
|
63
|
+
(按版本取 `--latest` / `--prerelease`)。发布运行成功后,请用**手写双语**
|
|
64
|
+
正文(中文在前、英文在后)覆盖——切勿把自动文本当作最终发布说明。
|
|
47
65
|
|
|
48
|
-
## DSH
|
|
66
|
+
## DSH 版本适配(单一 peer 元组)
|
|
49
67
|
|
|
50
68
|
DSH 仍在 rc 阶段,npm 的 prerelease 匹配规则要求 peer 范围与宿主版本
|
|
51
69
|
**同 `[major, minor, patch]` 元组**才能匹配。因此 peerDependencies 采用
|
|
52
|
-
|
|
53
|
-
并随 DSH 发版追加。
|
|
70
|
+
**每一条 DSH 线一个 peer 元组**(如 `^0.1.2-rc.1`),DSH 发新元组时**替换**而非追加。
|
|
54
71
|
|
|
55
|
-
- **何时需要更新**:仅当 DSH 发布新元组(`0.1.
|
|
56
|
-
同元组内 rc 滚动(`0.1.
|
|
72
|
+
- **何时需要更新**:仅当 DSH 发布新元组(`0.1.2 → 0.1.3 → 0.2.x`)时;
|
|
73
|
+
同元组内 rc 滚动(`0.1.2-rc.1 → rc.2`)无需动作。DSH 所有包同版本发布,
|
|
57
74
|
`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
75
|
- **已发布元组检查(可选)**:`node scripts/check-dsh-version.mjs` 用 npm `latest`
|
|
62
76
|
dist-tag 版本对比 peer 覆盖的元组(exit 0 无需动作,exit 1 需要)。它**只读
|
|
63
|
-
`latest` tag**;发布在其它 tag
|
|
77
|
+
`latest` tag**;发布在其它 tag 的 pre-release 走**手动发布前检查**
|
|
64
78
|
——见上文"升版前手动确认"。
|
|
65
|
-
-
|
|
79
|
+
- **更新步骤**:把每个 `@deepseek-ai/dsh-*` peer 的元组**替换**为 `^<最新版>`
|
|
66
80
|
→ devDependencies 同步升到最新 → `npm install` → `npm run check` → 发版。
|
|
67
81
|
- **正式版后收敛**:DSH 发布 final 版本后,正式版不受 prerelease 元组规则
|
|
68
82
|
限制,peer 可收敛为稳定的 `^0.1.x` 单范围,此节即可删除。
|
|
83
|
+
|
|
84
|
+
## 发布版本线模型
|
|
85
|
+
|
|
86
|
+
**一个发布对准一条 DSH 版本线。** 插件自身版本号与宿主解耦;某发布所对准的
|
|
87
|
+
DSH 线由 peer 约束(单一 companion 元组)声明,而非插件版本号。
|
|
88
|
+
|
|
89
|
+
| 插件版本 | DSH 线 | 说明 |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| `0.7.x` | `0.1.1` + `0.1.2`(广兼容) | 冻结 / EOL |
|
|
92
|
+
| `0.8.x` | `0.1.2-rc.1`(单线) | 当前稳定 |
|
|
93
|
+
| `0.9.x` | `0.1.3`(单线) | 下一线 |
|
|
94
|
+
|
|
95
|
+
**版本号。** DSH 版本线破坏性变更 = MAJOR 升版(与更早 DSH 线向后不兼容)。
|
|
96
|
+
同一线内 MINOR/PATCH 保持向后兼容。
|
|
97
|
+
|
|
98
|
+
**分支(主干开发)。** `main` 是唯一集成与发布线,始终可发版。当前已发布
|
|
99
|
+
稳定版从发布提交点切出短命 `release/<版本>.x` 维护分支;该分支承接
|
|
100
|
+
backport 修复,同时 `main` 前进到下一线。更早的(广兼容)线冻结成 tag,无分支。
|
|
101
|
+
|
|
102
|
+
**dist-tag 路由。** 发布 workflow 按版本号取 npm dist-tag:稳定版发 `latest`;
|
|
103
|
+
pre-release 发到与其 pre-release 标识符同名的 dist-tag(`0.9.0-alpha.1` →
|
|
104
|
+
`alpha`、`0.9.0-rc.1` → `rc`)。pre-release 不占据 `latest`。
|
|
105
|
+
|
|
106
|
+
**支持窗口 / EOL。** 一条 DSH 线仅在声明的窗口内受支持。默认窗口到下一
|
|
107
|
+
DSH 线作为 `latest` 发布为止;此后该线 EOL、冻结、不再发补丁。此处 `0.8.x`
|
|
108
|
+
(`0.1.2-rc.1`)支持到 `0.9.x`(`0.1.3`)作为 `latest` 发布为止。
|
|
109
|
+
|
|
110
|
+
**Bug 修复流程(先向前修,再回迁)。** 跨多条支持线的修复,先在 `main` 上
|
|
111
|
+
修复,再回迁到各仍受支持的 release 分支。仅特定线的修复,只在对应线修复。
|
|
112
|
+
|
|
113
|
+
单线模型每个发布使用单一 peer 元组(见上文「DSH 版本适配」);其取代的 OR 并集多线做法已不使用。
|
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.8.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
|
};
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { createAssistantMessage } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { unlink as unlink2 } from "node:fs/promises";
|
|
4
|
-
import * as dshSettings from "@deepseek-ai/dsh-settings";
|
|
5
4
|
import "@deepseek-ai/schemastery";
|
|
6
5
|
|
|
7
6
|
// src/locales.ts
|
|
@@ -111,20 +110,6 @@ function translate(lang, key, params = {}) {
|
|
|
111
110
|
return text;
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
// src/session-events.ts
|
|
115
|
-
function eventsOf(session) {
|
|
116
|
-
const s = session;
|
|
117
|
-
if (s.snapshotEvents !== void 0) return s.snapshotEvents();
|
|
118
|
-
if (s.events !== void 0) return s.events;
|
|
119
|
-
return [];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// src/settings-locale.ts
|
|
123
|
-
function readSettingsSection(provider, ns, brand) {
|
|
124
|
-
const key = brand?.(ns) ?? ns;
|
|
125
|
-
return provider.get(key);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
113
|
// src/rewind.ts
|
|
129
114
|
var RewindError = class extends Error {
|
|
130
115
|
constructor(code, message) {
|
|
@@ -1524,7 +1509,7 @@ function mutationPathOf(exec) {
|
|
|
1524
1509
|
return void 0;
|
|
1525
1510
|
}
|
|
1526
1511
|
function anchorSeqOf(session, cache) {
|
|
1527
|
-
const events =
|
|
1512
|
+
const events = session.snapshotEvents();
|
|
1528
1513
|
const cached = cache.get(session);
|
|
1529
1514
|
if (cached !== void 0 && cached.eventsLength === events.length) return cached.anchor;
|
|
1530
1515
|
let anchor = cached?.anchor;
|
|
@@ -1702,15 +1687,15 @@ async function executeRewind(ctx, store, fs, invocation, rawTarget, mode, inflig
|
|
|
1702
1687
|
}
|
|
1703
1688
|
let plan;
|
|
1704
1689
|
try {
|
|
1705
|
-
plan = resolveOrError(
|
|
1690
|
+
plan = resolveOrError(agent.session.snapshotEvents(), agent.session.surface.nodes, rawTarget);
|
|
1706
1691
|
} catch (error) {
|
|
1707
1692
|
return rewindErrorResult(error);
|
|
1708
1693
|
}
|
|
1709
1694
|
const marker = buildMarker();
|
|
1710
1695
|
let event;
|
|
1711
1696
|
try {
|
|
1712
|
-
const turn = markerTurnOf(
|
|
1713
|
-
const step = markerStepOf(
|
|
1697
|
+
const turn = markerTurnOf(agent.session.snapshotEvents());
|
|
1698
|
+
const step = markerStepOf(agent.session.snapshotEvents(), turn);
|
|
1714
1699
|
agent.session.append("step/start", { turn, step });
|
|
1715
1700
|
try {
|
|
1716
1701
|
event = agent.session.append("assistant/message", { turn, step, message: marker }, {
|
|
@@ -1764,7 +1749,7 @@ async function handleRewind(ctx, store, fs, invocation, inflight) {
|
|
|
1764
1749
|
const session = invocation.agent.session;
|
|
1765
1750
|
const input = invocation.rawInput.trim();
|
|
1766
1751
|
if (input === "") {
|
|
1767
|
-
const candidates = listRewindCandidates(
|
|
1752
|
+
const candidates = listRewindCandidates(session.snapshotEvents(), session.surface.nodes, 1);
|
|
1768
1753
|
if (candidates.length === 0) {
|
|
1769
1754
|
return { kind: "error", text: t("noUserMessages") };
|
|
1770
1755
|
}
|
|
@@ -1776,7 +1761,7 @@ async function handleRewind(ctx, store, fs, invocation, inflight) {
|
|
|
1776
1761
|
if (target2 === void 0) return { kind: "error", text: usage() };
|
|
1777
1762
|
let plan;
|
|
1778
1763
|
try {
|
|
1779
|
-
plan = resolveOrError(
|
|
1764
|
+
plan = resolveOrError(session.snapshotEvents(), session.surface.nodes, target2);
|
|
1780
1765
|
} catch (error) {
|
|
1781
1766
|
return rewindErrorResult(error);
|
|
1782
1767
|
}
|
|
@@ -1784,7 +1769,7 @@ async function handleRewind(ctx, store, fs, invocation, inflight) {
|
|
|
1784
1769
|
return { kind: "success", text: formatPlan(plan, impacts) };
|
|
1785
1770
|
}
|
|
1786
1771
|
if (parts[0] === "__candidates") {
|
|
1787
|
-
const candidates = listRewindCandidates(
|
|
1772
|
+
const candidates = listRewindCandidates(session.snapshotEvents(), session.surface.nodes);
|
|
1788
1773
|
return { kind: "success", text: formatCandidateList(candidates) };
|
|
1789
1774
|
}
|
|
1790
1775
|
const target = parts[0];
|
|
@@ -1930,15 +1915,16 @@ function apply(ctx, config) {
|
|
|
1930
1915
|
const trackedBySession = /* @__PURE__ */ new Map();
|
|
1931
1916
|
let fsService;
|
|
1932
1917
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
1933
|
-
const
|
|
1934
|
-
|
|
1935
|
-
"locale",
|
|
1936
|
-
dshSettings.settingsNamespace
|
|
1937
|
-
);
|
|
1918
|
+
const settings = settingsCtx;
|
|
1919
|
+
const section = settings.settings.get("locale");
|
|
1938
1920
|
if (section?.preference === "zh" || section?.preference === "en") {
|
|
1939
1921
|
activeLocale = section.preference;
|
|
1940
1922
|
}
|
|
1941
|
-
const cleanupScope =
|
|
1923
|
+
const cleanupScope = settings.settings.register(
|
|
1924
|
+
CLEANUP_SETTINGS_NAMESPACE,
|
|
1925
|
+
CleanupConfigSchema,
|
|
1926
|
+
{ base: DEFAULT_CLEANUP_CONFIG }
|
|
1927
|
+
);
|
|
1942
1928
|
cleanupStore = settingsCleanupStore(cleanupScope);
|
|
1943
1929
|
void migrateLegacyCleanupConfig(
|
|
1944
1930
|
resolveCleanupConfigPath(dshHome),
|
|
@@ -5,7 +5,17 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module dsh-rewind/client/hidden
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { CommandNode } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
9
|
+
/** A chat-snapshot view node as the hiding / composer-refill logic reads it.
|
|
10
|
+
* The harness's view node shape plus the `anchorSeq` the chat snapshot carries
|
|
11
|
+
* (the plugin reads `anchorSeq` off each node; it is not declared on the
|
|
12
|
+
* harness's `ConversationViewNode`). */
|
|
13
|
+
export interface ChatConversationViewNode {
|
|
14
|
+
readonly key: string;
|
|
15
|
+
readonly kind?: string;
|
|
16
|
+
readonly data?: unknown;
|
|
17
|
+
readonly anchorSeq: number;
|
|
18
|
+
}
|
|
9
19
|
/** Minimal chat snapshot reader the hiding logic needs. */
|
|
10
20
|
export interface HiddenChat {
|
|
11
21
|
readonly order: readonly string[];
|
|
@@ -14,55 +24,36 @@ export interface HiddenChat {
|
|
|
14
24
|
};
|
|
15
25
|
}
|
|
16
26
|
/**
|
|
17
|
-
* Reader for one session's live chat snapshot.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* dsh-client-ui-chat through the uiSession slot hook). See
|
|
22
|
-
* SiriLee/dsh-rewind#7.
|
|
27
|
+
* Reader for one session's live chat snapshot. On the 0.1.2-rc.1 line (the
|
|
28
|
+
* plugin's single baseline) the chat is served by the `uiConversation`
|
|
29
|
+
* service's named "chat" view (contributed by dsh-client-ui-chat through the
|
|
30
|
+
* uiSession slot hook).
|
|
23
31
|
*/
|
|
24
32
|
export type ChatOf = (session: {
|
|
25
33
|
readonly sessionId: string;
|
|
26
|
-
getSnapshot(): {
|
|
27
|
-
chat?: unknown;
|
|
28
|
-
};
|
|
29
34
|
} | undefined) => HiddenChat | undefined;
|
|
30
35
|
/**
|
|
31
36
|
* Subscribe to one session's live chat-update signal, for waiting on a chat
|
|
32
|
-
* snapshot change without polling.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* `subscribe` is the chat-update signal). `cb` fires whenever the chat
|
|
36
|
-
* snapshot invalidates.
|
|
37
|
+
* snapshot change without polling. The 0.1.2-rc.1 `uiConversation` "chat"
|
|
38
|
+
* view's own `subscribe` is the chat-update signal; `cb` fires whenever the
|
|
39
|
+
* chat snapshot invalidates.
|
|
37
40
|
*/
|
|
38
41
|
export type ChatWatch = (sessionId: string, cb: () => void) => () => void;
|
|
39
42
|
/**
|
|
40
|
-
* Choose the chat-update subscription for `waitForCommand`:
|
|
41
|
-
* `uiConversation` "chat" view's own `subscribe
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* the chat-update signal). Extracted as a pure channel-selection step so the
|
|
45
|
-
* "view vs face" branch is unit-testable; the resolvers are injected by the
|
|
46
|
-
* caller (see `watchChat` in index.ts). Never throws.
|
|
43
|
+
* Choose the chat-update subscription for `waitForCommand`: the
|
|
44
|
+
* `uiConversation` "chat" view's own `subscribe`. Extracted as a pure
|
|
45
|
+
* channel-selection step so the resolver is unit-testable; the resolver is
|
|
46
|
+
* injected by the caller (see `watchChat` in index.ts). Never throws.
|
|
47
47
|
*/
|
|
48
48
|
export declare function resolveChatWatch(resolveView: (sessionId: string) => {
|
|
49
49
|
subscribe?(cb: () => void): () => void;
|
|
50
|
-
} | undefined, resolveFace: (sessionId: string) => {
|
|
51
|
-
subscribe(cb: () => void): () => void;
|
|
52
50
|
} | undefined, sessionId: string, cb: () => void): () => void;
|
|
53
51
|
/**
|
|
54
|
-
* Resolve the chat snapshot
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* view. The view's `getSnapshot()` returns undefined until the named view is
|
|
58
|
-
* registered, so both channels missing degrades to `undefined` (no targets, no
|
|
59
|
-
* hiding — never a crash).
|
|
52
|
+
* Resolve the chat snapshot from the `uiConversation` "chat" view. The view's
|
|
53
|
+
* `getSnapshot()` returns undefined until the named view is registered, which
|
|
54
|
+
* degrades to `undefined` (no targets, no hiding — never a crash).
|
|
60
55
|
*/
|
|
61
|
-
export declare function chatSnapshotOf(
|
|
62
|
-
getSnapshot(): {
|
|
63
|
-
chat?: unknown;
|
|
64
|
-
};
|
|
65
|
-
} | undefined, chatView: {
|
|
56
|
+
export declare function chatSnapshotOf(chatView: {
|
|
66
57
|
getSnapshot(): unknown;
|
|
67
58
|
} | undefined): HiddenChat | undefined;
|
|
68
59
|
/**
|
|
@@ -26,9 +26,27 @@
|
|
|
26
26
|
*
|
|
27
27
|
* @module dsh-rewind/client
|
|
28
28
|
*/
|
|
29
|
-
import type {
|
|
29
|
+
import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
30
30
|
export declare const name = "dsh-rewind";
|
|
31
31
|
export declare const inject: string[];
|
|
32
|
+
/**
|
|
33
|
+
* The client plugin root context read by `apply(ctx)`. Local structural face:
|
|
34
|
+
* the harness client context is a cordis `Context` augmented by runtime
|
|
35
|
+
* services, so the plugin declares the subset it reads. `sessions` is the real
|
|
36
|
+
* `ISessions` from `@deepseek-ai/dsh-api-session-controller`.
|
|
37
|
+
*/
|
|
38
|
+
export interface ClientContext {
|
|
39
|
+
effect(execute: () => Iterable<unknown>, label?: string): unknown;
|
|
40
|
+
locale: {
|
|
41
|
+
register(namespace: string, messages: Record<string, Record<string, string>>): unknown;
|
|
42
|
+
bind(namespace: string): (key: string) => string;
|
|
43
|
+
subscribe(cb: () => void): () => void;
|
|
44
|
+
};
|
|
45
|
+
sessions: ISessions;
|
|
46
|
+
get(name: string): unknown;
|
|
47
|
+
slots: unknown;
|
|
48
|
+
commandUi: unknown;
|
|
49
|
+
}
|
|
32
50
|
/**
|
|
33
51
|
* Client plugin body: command decoration + parameterized guard + locale + the
|
|
34
52
|
* portal bridge.
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module dsh-rewind/client/popover
|
|
15
15
|
*/
|
|
16
|
-
import type { SessionFace } from '@deepseek-ai/dsh-
|
|
17
|
-
import type { CommandNode } from '@deepseek-ai/dsh-client-
|
|
16
|
+
import type { SessionFace } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
17
|
+
import type { CommandNode } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
18
18
|
import { type ChatOf, type ChatWatch } from './hidden.ts';
|
|
19
19
|
import type { RewindKey } from './locales.ts';
|
|
20
20
|
type Translate = (key: RewindKey, params?: Record<string, unknown>) => string;
|
|
@@ -33,16 +33,15 @@ export interface PopoverOptions {
|
|
|
33
33
|
readonly onRetract?: () => void;
|
|
34
34
|
readonly preview: string;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* Chat reader on the 0.1.2-rc.1 `uiConversation` "chat" view: the durable
|
|
37
|
+
* variant's command probes scan the chat through it. Unused by the
|
|
38
|
+
* pending-retract variant.
|
|
39
39
|
*/
|
|
40
40
|
readonly chatOf: ChatOf;
|
|
41
41
|
/**
|
|
42
42
|
* Subscribe to one session's live chat-update signal, so a probe waiting on
|
|
43
|
-
* a command's chat node can be woken when the chat snapshot changes
|
|
44
|
-
*
|
|
45
|
-
* to `waitForCommand`.
|
|
43
|
+
* a command's chat node can be woken when the chat snapshot changes. Passed
|
|
44
|
+
* straight through to `waitForCommand`.
|
|
46
45
|
*/
|
|
47
46
|
readonly watchChat: ChatWatch;
|
|
48
47
|
/** The button that opened the popover (outside-click ignore target). */
|
|
@@ -70,7 +69,7 @@ export declare function knownCommandSeqs(session: SessionFace, chatOf: ChatOf, m
|
|
|
70
69
|
* session snapshot (command/run + command/done land as one CommandNode).
|
|
71
70
|
* @returns the outcome text-bearing node, or null on timeout.
|
|
72
71
|
*/
|
|
73
|
-
export declare function waitForCommand(session: SessionFace, chatOf: ChatOf, match: (node: CommandNode) => boolean, timeoutMs
|
|
72
|
+
export declare function waitForCommand(session: SessionFace, chatOf: ChatOf, match: (node: CommandNode) => boolean, timeoutMs: number | undefined, watch: (cb: () => void) => () => void): Promise<{
|
|
74
73
|
kind: 'success' | 'error';
|
|
75
74
|
text?: string;
|
|
76
75
|
} | null>;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* @module dsh-rewind/client/portals
|
|
24
24
|
*/
|
|
25
25
|
import { type ReactNode } from 'react';
|
|
26
|
-
import type { SessionFace } from '@deepseek-ai/dsh-
|
|
26
|
+
import type { SessionFace } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
27
27
|
import { type ChatOf, type ChatWatch, type HiddenChat } from './hidden.ts';
|
|
28
28
|
import type { RewindKey } from './locales.ts';
|
|
29
29
|
type Translate = (key: RewindKey, params?: Record<string, unknown>) => string;
|
|
@@ -53,26 +53,23 @@ export type PortalTarget = {
|
|
|
53
53
|
export interface RewindBridgeDeps {
|
|
54
54
|
readonly sessionOf: (sessionId: string) => SessionFace | undefined;
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* `chatSnapshotOf` in hidden.ts for the channel precedence.
|
|
56
|
+
* Chat reader on the 0.1.2-rc.1 `uiConversation` "chat" view: every chat
|
|
57
|
+
* snapshot read goes through it. See `chatSnapshotOf` in hidden.ts.
|
|
59
58
|
*/
|
|
60
59
|
readonly chatOf: ChatOf;
|
|
61
60
|
/**
|
|
62
61
|
* Subscribe to one session's live chat-update signal, so the composer refill
|
|
63
62
|
* waiting on an executed rewind's chat node can be woken when the chat
|
|
64
|
-
* snapshot changes
|
|
65
|
-
* update). Passed through to `waitForCommand`.
|
|
63
|
+
* snapshot changes. Passed through to `waitForCommand`.
|
|
66
64
|
*/
|
|
67
65
|
readonly watchChat: ChatWatch;
|
|
68
66
|
readonly currentSessionId: () => string | undefined;
|
|
69
67
|
readonly t: Translate;
|
|
70
68
|
readonly subscribeLocale: (cb: () => void) => () => void;
|
|
71
69
|
/**
|
|
72
|
-
* Session-aware
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* the session that just rewound.
|
|
70
|
+
* Session-aware composer writer (see `writeComposer`): the 0.1.2-rc.1
|
|
71
|
+
* `conversation.input` facade `setDraft` when reachable, else the DOM fill.
|
|
72
|
+
* Session-scoped so the refill only lands in the session that just rewound.
|
|
76
73
|
*/
|
|
77
74
|
readonly setComposerText: (sessionId: string, text: string) => boolean;
|
|
78
75
|
}
|
|
@@ -99,20 +96,18 @@ interface ComposerDraftWriter {
|
|
|
99
96
|
setDraft(text: string): void;
|
|
100
97
|
}
|
|
101
98
|
/**
|
|
102
|
-
* Fill the dsh composer with `text
|
|
103
|
-
*
|
|
104
|
-
* `
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* never a throw.
|
|
99
|
+
* Fill the dsh composer with `text` through the 0.1.2-rc.1 `contenteditable`
|
|
100
|
+
* DOM path. Used by `setComposerText` (the harness-facade-aware writer) as the
|
|
101
|
+
* last-resort and by `runRewindAndFill` to put the withdrawn target message
|
|
102
|
+
* back into the composer after a rewind. Best-effort — no composer match means
|
|
103
|
+
* false, never a throw.
|
|
108
104
|
*/
|
|
109
105
|
export declare function fillComposer(text: string): boolean;
|
|
110
106
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* still restores the text. Never throws.
|
|
107
|
+
* Composer write: prefer the harness facade's `setDraft` (0.1.2-rc.1, correct
|
|
108
|
+
* whole-draft replace), then degrade to the DOM `fillComposer` (0.1.2-rc.1
|
|
109
|
+
* contenteditable). A facade that throws (session teardown) is treated as
|
|
110
|
+
* absent so the DOM path still restores the text. Never throws.
|
|
116
111
|
* @param text - the withdrawn target message text.
|
|
117
112
|
* @param facade - the 0.1.2-rc.1 session input draft writer, when reachable.
|
|
118
113
|
* @returns whether a channel applied the text.
|
|
@@ -136,24 +131,15 @@ export declare function runRewindAndFill(session: SessionFace, seq: number, mode
|
|
|
136
131
|
* Locate the actions container of a user/steering seat row — the element the
|
|
137
132
|
* ↶ button portals into (the copy/branch IconActions row).
|
|
138
133
|
*
|
|
139
|
-
*
|
|
140
|
-
* -
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* per-turn tail footer, `TurnTailNodeView`), and the user action row is
|
|
145
|
-
* revealed via CSS `:has()`. The container is instead located structurally:
|
|
146
|
-
* the direct holder of the copy `<button>` (the `MessageIconActions`
|
|
147
|
-
* container, which mounts that button as a direct child —
|
|
148
|
-
* `MessageIconActions.tsx:83,86`).
|
|
149
|
-
*
|
|
150
|
-
* The 0.1.2-alpha.2/3 `[data-actions-reveal]` attribute-marked rows (see
|
|
151
|
-
* `ACTIONS_ROOT_SELECTOR`) are an intermediate state absorbed by the
|
|
152
|
-
* attribute branch below; they are not needed by either endpoint.
|
|
134
|
+
* On the 0.1.2-rc.1 line the `data-time-hover-root` marker lives only on the
|
|
135
|
+
* per-turn tail footer (`TurnTailNodeView`), and the user action row is
|
|
136
|
+
* revealed via CSS `:has()`. The container is located structurally: the direct
|
|
137
|
+
* holder of the copy `<button>` (the `MessageIconActions` container, which
|
|
138
|
+
* mounts that button as a direct child — `MessageIconActions.tsx:83,86`).
|
|
153
139
|
*
|
|
154
140
|
* Returns undefined when no qualifying container is found; the caller refuses
|
|
155
141
|
* to portal (never a crash, never a wrong attachment). Exported as a test seam
|
|
156
|
-
* (see `collectTargets`) so the
|
|
142
|
+
* (see `collectTargets`) so the row-shape finder is exercised directly for
|
|
157
143
|
* both durable and pending row shapes without a full React portal render.
|
|
158
144
|
*/
|
|
159
145
|
export declare function actionsContainerOf(row: HTMLElement | undefined): HTMLElement | undefined;
|
|
@@ -174,10 +160,9 @@ interface RewindPortalsProps extends RewindBridgeDeps {
|
|
|
174
160
|
* synchronous full-transcript scan inside a commit microtask.
|
|
175
161
|
*/
|
|
176
162
|
export declare function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, watchChat, t, subscribeLocale, setComposerText }: RewindPortalsProps): ReactNode;
|
|
177
|
-
/** The current composer draft
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* `retractPending`). */
|
|
163
|
+
/** The current composer draft: the 0.1.2-rc.1 contenteditable `textContent`.
|
|
164
|
+
* Empty when the composer is absent. Exported as a test seam (the
|
|
165
|
+
* empty-composer guard in `retractPending`). */
|
|
181
166
|
export declare function composerText(): string;
|
|
182
167
|
/**
|
|
183
168
|
* Build the slot-entry component for the plugin apply(): a tiny bridge that
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
*
|
|
13
13
|
* It neither imports the client settings typed contract nor depends on the
|
|
14
14
|
* 0.1.2-rc.1-only `mutate` write API: it reads `getSnapshot().value` and writes
|
|
15
|
-
* via the `set(field, value)` method
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* unit-testable in isolation.
|
|
15
|
+
* via the `set(field, value)` method, and the card receives a tiny structural
|
|
16
|
+
* `CleanupCardApi` supplied by `src/client/index.ts` so the component stays
|
|
17
|
+
* harness-agnostic and unit-testable in isolation.
|
|
19
18
|
*
|
|
20
19
|
* @module dsh-rewind/client/settings-card
|
|
21
20
|
*/
|
|
@@ -23,4 +23,4 @@ export declare const CLASS: {
|
|
|
23
23
|
/** The ↶ glyph, drawn inline so the bundle stays dependency-free. */
|
|
24
24
|
export declare const REWIND_ICON_SVG: string;
|
|
25
25
|
/** One injected stylesheet (scoped under `.dsh-rewind-*`). */
|
|
26
|
-
export declare const STYLE = "\n.dsh-rewind-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border: none;\n border-radius: 28px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n.dsh-rewind-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.dsh-rewind-popover {\n position: fixed;\n z-index: 1000;\n width: 288px;\n padding: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 14px;\n line-height: 20px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-popover-title {\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n.dsh-rewind-popover-target {\n margin: 4px 0 10px;\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-all;\n}\n.dsh-rewind-popover-option {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n margin: 0 0 6px;\n padding: 8px 10px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.dsh-rewind-popover-option:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n.dsh-rewind-popover-option:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-option-label {\n font-weight: 500;\n}\n.dsh-rewind-popover-option-hint {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-popover-impact {\n margin: 4px 0 10px;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover);\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n max-height: 160px;\n overflow: auto;\n}\n.dsh-rewind-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.dsh-rewind-popover-primary,\n.dsh-rewind-popover-ghost {\n padding: 5px 12px;\n border: none;\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n}\n.dsh-rewind-popover-primary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n.dsh-rewind-popover-primary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n.dsh-rewind-popover-primary:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-ghost {\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-popover-ghost:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-rewind-guard-hint {\n position: fixed;\n z-index: 1000;\n max-width: min(440px, calc(100vw - 24px));\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 13px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n pointer-events: none;\n}\n\n/* ---- Snapshot-cleanup settings card (mirrors the harness PluginCard look) ---- */\n/*
|
|
26
|
+
export declare const STYLE = "\n.dsh-rewind-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border: none;\n border-radius: 28px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n.dsh-rewind-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.dsh-rewind-popover {\n position: fixed;\n z-index: 1000;\n width: 288px;\n padding: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 14px;\n line-height: 20px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-popover-title {\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n.dsh-rewind-popover-target {\n margin: 4px 0 10px;\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-all;\n}\n.dsh-rewind-popover-option {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n margin: 0 0 6px;\n padding: 8px 10px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.dsh-rewind-popover-option:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n.dsh-rewind-popover-option:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-option-label {\n font-weight: 500;\n}\n.dsh-rewind-popover-option-hint {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-popover-impact {\n margin: 4px 0 10px;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover);\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n max-height: 160px;\n overflow: auto;\n}\n.dsh-rewind-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.dsh-rewind-popover-primary,\n.dsh-rewind-popover-ghost {\n padding: 5px 12px;\n border: none;\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n}\n.dsh-rewind-popover-primary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n.dsh-rewind-popover-primary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n.dsh-rewind-popover-primary:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-ghost {\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-popover-ghost:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-rewind-guard-hint {\n position: fixed;\n z-index: 1000;\n max-width: min(440px, calc(100vw - 24px));\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 13px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n pointer-events: none;\n}\n\n/* ---- Snapshot-cleanup settings card (mirrors the harness PluginCard look) ---- */\n/* Standardized on the 0.1.2 harness card look (border-radius 16px + 0.5px\n border-l4, 0.5px internal separators). */\n.dsh-rewind-cleanup-card {\n list-style: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 16px;\n background: var(--dsw-alias-bg-layer-3);\n transition: border-color .16s, background .16s;\n}\n.dsh-rewind-cleanup-card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-card-open {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n.dsh-rewind-cleanup-header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n.dsh-rewind-cleanup-head-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dsh-rewind-cleanup-name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-desc {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transition: transform .16s;\n}\n.dsh-rewind-cleanup-chevron-open {\n transform: rotate(180deg);\n}\n.dsh-rewind-cleanup-pending {\n flex: none;\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-cleanup-body {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding: 4px 0 8px;\n}\n.dsh-rewind-cleanup-readonly {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-permission {\n display: grid;\n gap: 6px;\n padding: 12px 0;\n}\n.dsh-rewind-cleanup-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 0;\n}\n.dsh-rewind-cleanup-field + .dsh-rewind-cleanup-field {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n.dsh-rewind-cleanup-head {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dsh-rewind-cleanup-label {\n flex: 1;\n min-width: 0;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-hint {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-error {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n/* Switch row: label left, role=switch button right, hint below (Subagent module). */\n.dsh-rewind-cleanup-toggle-row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-toggle-label {\n flex: 1;\n min-width: 0;\n}\n.dsh-rewind-cleanup-switch {\n box-sizing: border-box;\n position: relative;\n flex: 0 0 auto;\n width: 36px;\n height: 20px;\n padding: 2px;\n border: 0;\n border-radius: 10px;\n background: var(--dsw-alias-border-l3);\n cursor: pointer;\n}\n.dsh-rewind-cleanup-switch-on {\n background: var(--dsw-alias-brand-primary);\n}\n.dsh-rewind-cleanup-switch:disabled {\n cursor: default;\n opacity: 0.5;\n}\n.dsh-rewind-cleanup-switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n}\n.dsh-rewind-cleanup-thumb {\n display: block;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n corner-shape: round;\n background: var(--dsw-alias-label-primary-foreground);\n transition: transform 120ms ease;\n}\n.dsh-rewind-cleanup-switch-on .dsh-rewind-cleanup-thumb {\n transform: translateX(16px);\n}\n.dsh-rewind-cleanup-input {\n box-sizing: border-box;\n height: 34px;\n padding: 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh-rewind-cleanup-input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n.dsh-rewind-cleanup-input-invalid {\n border-color: var(--dsw-alias-label-error);\n}\n.dsh-rewind-cleanup-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-rewind-cleanup-failed {\n flex: 1;\n min-width: 0;\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n.dsh-rewind-cleanup-discard,\n.dsh-rewind-cleanup-save {\n appearance: none;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n.dsh-rewind-cleanup-discard {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-cleanup-discard:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-save {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n.dsh-rewind-cleanup-discard:disabled,\n.dsh-rewind-cleanup-save:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh-rewind-cleanup-discard:focus-visible,\n.dsh-rewind-cleanup-save:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n";
|
|
@@ -46,9 +46,9 @@ export declare const CleanupConfigSchema: z<CleanupConfig>;
|
|
|
46
46
|
/**
|
|
47
47
|
* Structural face of the settings scope the host needs for the policy: a
|
|
48
48
|
* resolved read and a validated write. Kept local (never imports the settings
|
|
49
|
-
* contract) so the host bundle
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* contract) so the host bundle does not type-couple on the client settings
|
|
50
|
+
* API (0.1.2 adds `mutate`; it is unused here), and the seam the host passes
|
|
51
|
+
* in isolates the drift to this module.
|
|
52
52
|
*/
|
|
53
53
|
export interface CleanupSettingsScope {
|
|
54
54
|
/** The resolved policy: schema defaults, then base, then the user layer. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-rewind-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-alpha.1",
|
|
4
4
|
"description": "DSH 插件:真正便捷无感的同窗口内对话回退,从不新建分支;自带轻量工作区备份,可一并还原文件(完整 Claude Code /rewind 语义)。 · DSH plugin: genuinely effortless in-window conversation rewind — never forking a new session; ships a lightweight workspace backup that restores files together with the rewind (full Claude Code /rewind semantics).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -52,7 +52,6 @@
|
|
|
52
52
|
"client": {
|
|
53
53
|
"inject": [
|
|
54
54
|
"@deepseek-ai/dsh-client-locale",
|
|
55
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
56
55
|
"@deepseek-ai/dsh-client-ui-commands",
|
|
57
56
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
58
57
|
"@deepseek-ai/dsh-client-ui-settings"
|
|
@@ -74,19 +73,18 @@
|
|
|
74
73
|
"peerDependencies": {
|
|
75
74
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
76
75
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
77
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
78
|
-
"@deepseek-ai/dsh-client-
|
|
79
|
-
"@deepseek-ai/dsh-client-ui-
|
|
80
|
-
"@deepseek-ai/dsh-client-ui-
|
|
81
|
-
"@deepseek-ai/dsh-
|
|
82
|
-
"@deepseek-ai/dsh-
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-
|
|
85
|
-
"@deepseek-ai/dsh-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-
|
|
89
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-alpha.2"
|
|
76
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-commands": "^0.1.2-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1",
|
|
79
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-commands": "^0.1.2-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-fs": "^0.1.2-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.2-rc.1",
|
|
83
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-session": "^0.1.2-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-rc.1"
|
|
90
88
|
},
|
|
91
89
|
"peerDependenciesMeta": {
|
|
92
90
|
"@deepseek-ai/cordis": {
|
|
@@ -98,9 +96,6 @@
|
|
|
98
96
|
"@deepseek-ai/dsh-client-locale": {
|
|
99
97
|
"optional": true
|
|
100
98
|
},
|
|
101
|
-
"@deepseek-ai/dsh-client-runtime": {
|
|
102
|
-
"optional": true
|
|
103
|
-
},
|
|
104
99
|
"@deepseek-ai/dsh-client-ui-commands": {
|
|
105
100
|
"optional": true
|
|
106
101
|
},
|
|
@@ -138,29 +133,37 @@
|
|
|
138
133
|
"devDependencies": {
|
|
139
134
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
140
135
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
141
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
142
|
-
"@deepseek-ai/dsh-
|
|
143
|
-
"@deepseek-ai/dsh-client-
|
|
144
|
-
"@deepseek-ai/dsh-client-
|
|
145
|
-
"@deepseek-ai/dsh-client-
|
|
146
|
-
"@deepseek-ai/dsh-client-ui-
|
|
147
|
-
"@deepseek-ai/dsh-
|
|
148
|
-
"@deepseek-ai/dsh-
|
|
149
|
-
"@deepseek-ai/dsh-
|
|
150
|
-
"@deepseek-ai/dsh-
|
|
151
|
-
"@deepseek-ai/dsh-
|
|
152
|
-
"@deepseek-ai/dsh-
|
|
153
|
-
"@deepseek-ai/dsh-
|
|
154
|
-
"@deepseek-ai/dsh-
|
|
155
|
-
"@deepseek-ai/dsh-
|
|
156
|
-
"@deepseek-ai/dsh-
|
|
157
|
-
"@deepseek-ai/dsh-
|
|
158
|
-
"@deepseek-ai/dsh-
|
|
159
|
-
"@deepseek-ai/dsh-
|
|
160
|
-
"@deepseek-ai/dsh-
|
|
161
|
-
"@deepseek-ai/dsh-
|
|
162
|
-
"@deepseek-ai/dsh-
|
|
163
|
-
"@deepseek-ai/dsh-
|
|
136
|
+
"@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
|
|
137
|
+
"@deepseek-ai/dsh-attachment": "^0.1.2-rc.1",
|
|
138
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
|
|
139
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.2-rc.1",
|
|
140
|
+
"@deepseek-ai/dsh-client-store": "^0.1.2-rc.1",
|
|
141
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1",
|
|
142
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.2-rc.1",
|
|
143
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
|
|
144
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-rc.1",
|
|
145
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1",
|
|
146
|
+
"@deepseek-ai/dsh-user-questions": "^0.1.2-rc.1",
|
|
147
|
+
"@deepseek-ai/dsh-client-ui-commands": "^0.1.2-rc.1",
|
|
148
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-rc.1",
|
|
149
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
|
|
150
|
+
"@deepseek-ai/dsh-command-compact": "^0.1.2-rc.1",
|
|
151
|
+
"@deepseek-ai/dsh-commands": "^0.1.2-rc.1",
|
|
152
|
+
"@deepseek-ai/dsh-compaction": "^0.1.2-rc.1",
|
|
153
|
+
"@deepseek-ai/dsh-compaction-basic": "^0.1.2-rc.1",
|
|
154
|
+
"@deepseek-ai/dsh-fs": "^0.1.2-rc.1",
|
|
155
|
+
"@deepseek-ai/dsh-goal": "^0.1.2-rc.1",
|
|
156
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.2-rc.1",
|
|
157
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
|
|
158
|
+
"@deepseek-ai/dsh-plan-mode": "^0.1.2-rc.1",
|
|
159
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-rc.1",
|
|
160
|
+
"@deepseek-ai/dsh-session": "^0.1.2-rc.1",
|
|
161
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.2-rc.1",
|
|
162
|
+
"@deepseek-ai/dsh-session-stats": "^0.1.2-rc.1",
|
|
163
|
+
"@deepseek-ai/dsh-session-title": "^0.1.2-rc.1",
|
|
164
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
|
|
165
|
+
"@deepseek-ai/dsh-token-meter": "^0.1.2-rc.1",
|
|
166
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
|
|
164
167
|
"@types/node": "^24.0.0",
|
|
165
168
|
"@types/react": "^18.3.31",
|
|
166
169
|
"@types/react-dom": "^18.3.7",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-channel session event-log reader.
|
|
3
|
-
*
|
|
4
|
-
* This is a "0.1.1-rc.2 ↔ 0.1.2-rc.1" dual-channel reader. Harness 0.1.1-rc.2
|
|
5
|
-
* exposes the full log as `Session.events` (`get events(): readonly
|
|
6
|
-
* SessionEvent[]`); the 0.1.2 line (alpha.4+, now 0.1.2-rc.1) removed that
|
|
7
|
-
* member and replaced it with the on-demand APIs `snapshotEvents(from?, to?)`
|
|
8
|
-
* (half-open range, internally cached), `eventAt(seq)`, `ownEvents()` and
|
|
9
|
-
* `seq`. This module reads the full log through whichever channel the host
|
|
10
|
-
* exposes, so the plugin keeps one code path across both endpoints.
|
|
11
|
-
*
|
|
12
|
-
* `snapshotEvents()` is deliberately preferred over `ownEvents()`:
|
|
13
|
-
* `snapshotEvents()` defaults to the whole log (including any fork-inherited
|
|
14
|
-
* prefix), which is exactly what `Session.events` returned on 0.1.1-rc.2 — the
|
|
15
|
-
* rewind semantics are preserved for fork-seeded sessions. `ownEvents()`
|
|
16
|
-
* would drop the inherited prefix and change behaviour.
|
|
17
|
-
*
|
|
18
|
-
* @module dsh-rewind/session-events
|
|
19
|
-
*/
|
|
20
|
-
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session';
|
|
21
|
-
/**
|
|
22
|
-
* Read the full event log of a session, transparently across harness channels:
|
|
23
|
-
* - 0.1.1-rc.2 (`Session.events`): the live immutable log array.
|
|
24
|
-
* - 0.1.2-rc.1 (`Session.snapshotEvents()`): a deep-frozen snapshot, cached
|
|
25
|
-
* by the session until the next append. This is the single 0.1.2-line
|
|
26
|
-
* channel (alpha.2/3 still exposed `events` and thus fall into the
|
|
27
|
-
* 0.1.1-rc.2 branch below).
|
|
28
|
-
*
|
|
29
|
-
* Falls back to an empty log when the session exposes neither member (a future
|
|
30
|
-
* harness shape) rather than throwing — callers already treat the empty log as
|
|
31
|
-
* "no candidates".
|
|
32
|
-
*
|
|
33
|
-
* @param session - the session to read.
|
|
34
|
-
* @returns the full event log in log order (the same shape 0.1.1-rc.2's `events`
|
|
35
|
-
* returned, so existing consumers are unchanged).
|
|
36
|
-
*/
|
|
37
|
-
export declare function eventsOf(session: Session): readonly SessionEvent[];
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Version-neutral settings-namespace reading for the host half.
|
|
3
|
-
*
|
|
4
|
-
* This is a "0.1.1-rc.2 ↔ 0.1.2-rc.1" dual channel. DSH 0.1.1-rc.2 exposes a
|
|
5
|
-
* `settingsNamespace(value)` brand helper, and `settings.get` is typed to
|
|
6
|
-
* require a branded `SettingsNamespace`. The 0.1.2 line (0.1.2-rc.1) removed
|
|
7
|
-
* that helper (the `SettingsNamespace` *type* remains) and `settings.get`
|
|
8
|
-
* accepts the raw namespace string. This module collapses both into one runtime
|
|
9
|
-
* call: on 0.1.1-rc.2 the brand is a compile-time marker erased at runtime (so
|
|
10
|
-
* `settingsNamespace(ns)` returns `ns`), on 0.1.2-rc.1 the brand helper is
|
|
11
|
-
* absent and the raw `ns` string is used directly. That is what lets a single
|
|
12
|
-
* compiled host bundle link and run on both harness generations.
|
|
13
|
-
*
|
|
14
|
-
* @module dsh-rewind/settings-locale
|
|
15
|
-
*/
|
|
16
|
-
/** Minimal structural face of the settings provider the host reads from. */
|
|
17
|
-
export interface SettingsProviderLike {
|
|
18
|
-
/**
|
|
19
|
-
* Read one registered settings section by namespace. Accepts whatever the
|
|
20
|
-
* running DSH generation passes: a branded `SettingsNamespace` (0.1.1-rc.2,
|
|
21
|
-
* brand erased) or the raw namespace string (0.1.2-rc.1).
|
|
22
|
-
*/
|
|
23
|
-
get(ns: string): unknown;
|
|
24
|
-
}
|
|
25
|
-
/** The `settingsNamespace(ns)` brand helper, or `undefined` when removed (0.1.2-rc.1). */
|
|
26
|
-
export type SettingsNamespaceBrand = ((value: string) => string) | undefined;
|
|
27
|
-
/**
|
|
28
|
-
* Read one settings section keyed by `ns`, tolerant of the settings-namespace
|
|
29
|
-
* brand across DSH generations. Pass the brand helper when available (the
|
|
30
|
-
* 0.1.1-rc.2 path); it is `undefined` on 0.1.2-rc.1, where the raw `ns` string
|
|
31
|
-
* is used directly. Never throws: an absent section simply returns `undefined`.
|
|
32
|
-
*/
|
|
33
|
-
export declare function readSettingsSection(provider: SettingsProviderLike, ns: string, brand: SettingsNamespaceBrand): unknown;
|