dsh-rewind-plugin 0.7.4 → 0.7.5
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/docs/compat/audit.md +10 -11
- package/docs/contract/client-contract.md +1 -1
- package/docs/contract/client-contract.zh.md +1 -1
- package/docs/harness-reference.md +1 -2
- package/lib/client.js +10 -9
- package/lib/types/client/hidden.d.ts +18 -16
- package/lib/types/client/popover.d.ts +4 -4
- package/lib/types/client/portals.d.ts +31 -27
- package/lib/types/client/settings-card.d.ts +5 -4
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/session-events.d.ts +10 -7
- package/lib/types/settings-locale.d.ts +11 -10
- package/lib/types/snapshot-cleanup.d.ts +3 -3
- package/package.json +1 -1
package/docs/compat/audit.md
CHANGED
|
@@ -7,30 +7,29 @@
|
|
|
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.1-rc.2` (matches `package-lock.json`)
|
|
11
|
-
>
|
|
10
|
+
> Targeted version: npm `@deepseek-ai/*@0.1.1-rc.2` (matches `package-lock.json`).
|
|
11
|
+
> Source reference: the upstream [github.com/deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
12
12
|
>
|
|
13
13
|
> Version alignment: `peerDependencies` use an OR-union (e.g.
|
|
14
14
|
> `^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-alpha.2`) covering each published tuple series. npm's
|
|
15
15
|
> prerelease matching rules require a candidate to share the range comparator's
|
|
16
|
-
> `[major, minor, patch]` tuple, so
|
|
17
|
-
>
|
|
18
|
-
>
|
|
16
|
+
> `[major, minor, patch]` tuple, so appending is needed only for a genuinely new
|
|
17
|
+
> tuple (e.g. a future `0.2.x`); any prerelease within the SAME tuple (alpha → rc)
|
|
18
|
+
> is a no-op. Signal: `npm view @deepseek-ai/dsh version`;
|
|
19
19
|
> flow: `scripts/check-dsh-version.mjs` (it reads the `latest` dist-tag only; a
|
|
20
20
|
> `-alpha` prerelease published under another tag, e.g. `0.1.2-alpha.2` under
|
|
21
21
|
> `alpha`, is a manual pre-release check).
|
|
22
22
|
>
|
|
23
23
|
> `0.1.2-alpha.1` was never published to npm, so the peer OR-union is declared only from `0.1.2-alpha.2`; the plugin code still supports the `alpha.1`+ client.
|
|
24
24
|
>
|
|
25
|
-
> `0.1.2-alpha.2` … `0.1.2-alpha.5` verified-compatible
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
>
|
|
29
|
-
> primary baseline; five versions verified: rc.2, alpha.2, alpha.3, alpha.4, alpha.5.
|
|
25
|
+
> `0.1.2-alpha.2` … `0.1.2-alpha.5` and `0.1.2-rc.1` verified-compatible; `rc.1` is
|
|
26
|
+
> source-identical to `alpha.5`. They share the `0.1.2` tuple, so the single
|
|
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.
|
|
30
29
|
|
|
31
30
|
### Channeled seams (version × channel)
|
|
32
31
|
|
|
33
|
-
| Seam | rc.2 (`0.1.1-rc.2`) | alpha.2 / alpha.3 | alpha.4 / alpha.5 |
|
|
32
|
+
| Seam | rc.2 (`0.1.1-rc.2`) | alpha.2 / alpha.3 | alpha.4 / alpha.5 / rc.1 |
|
|
34
33
|
| --- | --- | --- | --- |
|
|
35
34
|
| Host session log (`eventsOf`) | `Session.events` | `Session.events` | `session.snapshotEvents()` |
|
|
36
35
|
| Client chat snapshot (`chatSnapshotOf`) | session-face `chat` field | `uiConversation` `chat` view | `uiConversation` `chat` view |
|
|
@@ -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 = session.getSnapshot().chat // rc.2 source; 0.1.2-
|
|
24
|
+
const chat = session.getSnapshot().chat // rc.2 source; 0.1.2-rc.1 uses the uiConversation "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 = session.getSnapshot().chat // rc.2 来源;0.1.2-
|
|
23
|
+
const chat = session.getSnapshot().chat // rc.2 来源;0.1.2-rc.1 用 uiConversation "chat" 视图
|
|
24
24
|
const hidden = hiddenSeqsOf(chat as HiddenChat) // 被隐藏的 anchor seq 集合
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# DeepSeek Harness interface reference
|
|
2
2
|
|
|
3
3
|
> Maintainer doc: the harness subsystems this plugin depends on, and the key
|
|
4
|
-
> source files behind each interface.
|
|
5
|
-
> `<workspace>/oss/deepseek-harness/` — official repo:
|
|
4
|
+
> source files behind each interface. Official repo:
|
|
6
5
|
> [github.com/deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)
|
|
7
6
|
|
|
8
7
|
## Subsystem docs (`docs/subsystems/`)
|
package/lib/client.js
CHANGED
|
@@ -329,11 +329,11 @@ var STYLE = `
|
|
|
329
329
|
|
|
330
330
|
/* ---- Snapshot-cleanup settings card (mirrors the harness PluginCard look) ---- */
|
|
331
331
|
/* Dual-channel note: the harness card corner/border drifted across versions \u2014
|
|
332
|
-
rc.2 uses border-radius 12px + 1px solid border-l2, while
|
|
333
|
-
0.5px border-l4 (and adds corner-shape: round). We intentionally
|
|
334
|
-
rc.2 values here for now; if the
|
|
335
|
-
host version like the other dual-channel surfaces (see the
|
|
336
|
-
in src/client/index.ts). */
|
|
332
|
+
0.1.1-rc.2 uses border-radius 12px + 1px solid border-l2, while 0.1.2-rc.1
|
|
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). */
|
|
337
337
|
.dsh-rewind-cleanup-card {
|
|
338
338
|
list-style: none;
|
|
339
339
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
@@ -1116,8 +1116,9 @@ function actionsContainerOf(row) {
|
|
|
1116
1116
|
const root = row?.matches(ACTIONS_ROOT_SELECTOR) ? row : row?.querySelector(ACTIONS_ROOT_SELECTOR);
|
|
1117
1117
|
const actions = root?.lastElementChild;
|
|
1118
1118
|
if (actions instanceof HTMLElement && actions.querySelector("button") !== null) return actions;
|
|
1119
|
-
const
|
|
1120
|
-
const
|
|
1119
|
+
const buttons = Array.from(row?.querySelectorAll("button") ?? []);
|
|
1120
|
+
const lastButton = buttons.filter((button) => !button.classList.contains(CLASS.button)).at(-1);
|
|
1121
|
+
const structural = lastButton?.parentElement;
|
|
1121
1122
|
if (structural instanceof HTMLElement && structural.querySelector("button") !== null) return structural;
|
|
1122
1123
|
return void 0;
|
|
1123
1124
|
}
|
|
@@ -1356,8 +1357,8 @@ function createRewindBridge(deps) {
|
|
|
1356
1357
|
}
|
|
1357
1358
|
|
|
1358
1359
|
// src/client/build-info.ts
|
|
1359
|
-
var PLUGIN_VERSION = true ? "0.7.
|
|
1360
|
-
var BUILD_HASH = true ? "
|
|
1360
|
+
var PLUGIN_VERSION = true ? "0.7.5" : "dev";
|
|
1361
|
+
var BUILD_HASH = true ? "25e8b3ca" : "dev";
|
|
1361
1362
|
|
|
1362
1363
|
// src/client/locales.ts
|
|
1363
1364
|
var zh = {
|
|
@@ -14,11 +14,12 @@ export interface HiddenChat {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Reader for one session's live chat snapshot.
|
|
18
|
-
*
|
|
19
|
-
* session face snapshot, while 0.1.2-
|
|
20
|
-
* `uiConversation` service's named "chat" view (contributed by
|
|
21
|
-
* dsh-client-ui-chat through the uiSession slot hook).
|
|
17
|
+
* Reader for one session's live chat snapshot. This is one of the plugin's
|
|
18
|
+
* "0.1.1-rc.2 ↔ 0.1.2-rc.1" dual channels: 0.1.1-rc.2 serves the chat from the
|
|
19
|
+
* session face snapshot, while 0.1.2-rc.1 (and the whole 0.1.2 line) serves it
|
|
20
|
+
* from the `uiConversation` service's named "chat" view (contributed by
|
|
21
|
+
* dsh-client-ui-chat through the uiSession slot hook). See
|
|
22
|
+
* SiriLee/dsh-rewind#7.
|
|
22
23
|
*/
|
|
23
24
|
export type ChatOf = (session: {
|
|
24
25
|
readonly sessionId: string;
|
|
@@ -29,17 +30,18 @@ export type ChatOf = (session: {
|
|
|
29
30
|
/**
|
|
30
31
|
* Subscribe to one session's live chat-update signal, for waiting on a chat
|
|
31
32
|
* snapshot change without polling. Dual-channel, mirroring `chatOf`: the
|
|
32
|
-
*
|
|
33
|
-
* (rc.2, whose snapshot still carries `chat`, so its own
|
|
34
|
-
* chat-update signal). `cb` fires whenever the chat
|
|
33
|
+
* 0.1.2-rc.1 `uiConversation` "chat" view when registered, else the session
|
|
34
|
+
* face (0.1.1-rc.2, whose snapshot still carries `chat`, so its own
|
|
35
|
+
* `subscribe` is the chat-update signal). `cb` fires whenever the chat
|
|
36
|
+
* snapshot invalidates.
|
|
35
37
|
*/
|
|
36
38
|
export type ChatWatch = (sessionId: string, cb: () => void) => () => void;
|
|
37
39
|
/**
|
|
38
40
|
* Choose the chat-update subscription for `waitForCommand`: prefer the
|
|
39
|
-
* `uiConversation` "chat" view's own `subscribe` when available (
|
|
41
|
+
* `uiConversation` "chat" view's own `subscribe` when available (0.1.2-rc.1,
|
|
40
42
|
* where the chat-update signal lives), else the session face's `subscribe`
|
|
41
|
-
* (rc.2, whose snapshot still carries the chat, so its own subscribe is
|
|
42
|
-
* chat-update signal). Extracted as a pure channel-selection step so the
|
|
43
|
+
* (0.1.1-rc.2, whose snapshot still carries the chat, so its own subscribe is
|
|
44
|
+
* the chat-update signal). Extracted as a pure channel-selection step so the
|
|
43
45
|
* "view vs face" branch is unit-testable; the resolvers are injected by the
|
|
44
46
|
* caller (see `watchChat` in index.ts). Never throws.
|
|
45
47
|
*/
|
|
@@ -50,11 +52,11 @@ export declare function resolveChatWatch(resolveView: (sessionId: string) => {
|
|
|
50
52
|
} | undefined, sessionId: string, cb: () => void): () => void;
|
|
51
53
|
/**
|
|
52
54
|
* Resolve the chat snapshot across the two harness channels: the session-face
|
|
53
|
-
* snapshot first (rc.2 — on
|
|
54
|
-
* field reads `undefined`), then the `uiConversation` "chat"
|
|
55
|
-
* `getSnapshot()` returns undefined until the named view is
|
|
56
|
-
* both channels missing degrades to `undefined` (no targets, no
|
|
57
|
-
* never a crash).
|
|
55
|
+
* snapshot first (0.1.1-rc.2 — on 0.1.2-rc.1 the face no longer carries
|
|
56
|
+
* `chat`, so the field reads `undefined`), then the `uiConversation` "chat"
|
|
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).
|
|
58
60
|
*/
|
|
59
61
|
export declare function chatSnapshotOf(face: {
|
|
60
62
|
getSnapshot(): {
|
|
@@ -33,14 +33,14 @@ export interface PopoverOptions {
|
|
|
33
33
|
readonly onRetract?: () => void;
|
|
34
34
|
readonly preview: string;
|
|
35
35
|
/**
|
|
36
|
-
* Dual-channel chat reader (rc.2 session face /
|
|
37
|
-
* view): the durable variant's command probes scan the chat
|
|
38
|
-
* Unused by the pending-retract variant.
|
|
36
|
+
* Dual-channel chat reader (0.1.1-rc.2 session face / 0.1.2-rc.1
|
|
37
|
+
* uiConversation view): the durable variant's command probes scan the chat
|
|
38
|
+
* through it. Unused by the 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 (
|
|
43
|
+
* a command's chat node can be woken when the chat snapshot changes (0.1.2-rc.1
|
|
44
44
|
* the session face no longer fires on a chat update). Passed straight through
|
|
45
45
|
* to `waitForCommand`.
|
|
46
46
|
*/
|
|
@@ -53,15 +53,15 @@ export type PortalTarget = {
|
|
|
53
53
|
export interface RewindBridgeDeps {
|
|
54
54
|
readonly sessionOf: (sessionId: string) => SessionFace | undefined;
|
|
55
55
|
/**
|
|
56
|
-
* Dual-channel chat reader (rc.2 session face /
|
|
57
|
-
* "chat" view): every chat snapshot read goes through it. See
|
|
56
|
+
* Dual-channel chat reader (0.1.1-rc.2 session face / 0.1.2-rc.1
|
|
57
|
+
* uiConversation "chat" view): every chat snapshot read goes through it. See
|
|
58
58
|
* `chatSnapshotOf` in hidden.ts for the channel precedence.
|
|
59
59
|
*/
|
|
60
60
|
readonly chatOf: ChatOf;
|
|
61
61
|
/**
|
|
62
62
|
* Subscribe to one session's live chat-update signal, so the composer refill
|
|
63
63
|
* waiting on an executed rewind's chat node can be woken when the chat
|
|
64
|
-
* snapshot changes (
|
|
64
|
+
* snapshot changes (0.1.2-rc.1 the session face no longer fires on a chat
|
|
65
65
|
* update). Passed through to `waitForCommand`.
|
|
66
66
|
*/
|
|
67
67
|
readonly watchChat: ChatWatch;
|
|
@@ -70,9 +70,9 @@ export interface RewindBridgeDeps {
|
|
|
70
70
|
readonly subscribeLocale: (cb: () => void) => () => void;
|
|
71
71
|
/**
|
|
72
72
|
* Session-aware dual-channel composer writer (see `writeComposer`): the
|
|
73
|
-
*
|
|
74
|
-
* rc.2/
|
|
75
|
-
* session that just rewound.
|
|
73
|
+
* 0.1.2-rc.1 `conversation.input` facade `setDraft` when reachable, else the
|
|
74
|
+
* 0.1.1-rc.2/0.1.2-rc.1 DOM fill. Session-scoped so the refill only lands in
|
|
75
|
+
* the session that just rewound.
|
|
76
76
|
*/
|
|
77
77
|
readonly setComposerText: (sessionId: string, text: string) => boolean;
|
|
78
78
|
}
|
|
@@ -90,7 +90,7 @@ export interface SlotsLike {
|
|
|
90
90
|
}
|
|
91
91
|
/** Join the text blocks of a user message into one plain preview. */
|
|
92
92
|
/**
|
|
93
|
-
* The
|
|
93
|
+
* The 0.1.2-rc.1 session input facade's write face (structural, so the plugin
|
|
94
94
|
* never imports the conversation UI package). `setDraft` replaces the whole
|
|
95
95
|
* composer draft through the harness's own Lexical editor — the correct way
|
|
96
96
|
* to restore the withdrawn text.
|
|
@@ -99,8 +99,8 @@ interface ComposerDraftWriter {
|
|
|
99
99
|
setDraft(text: string): void;
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
|
-
* Fill the dsh composer with `text`. DOM dual-channel fallback: the rc.2
|
|
103
|
-
* `<textarea>` path, then the
|
|
102
|
+
* Fill the dsh composer with `text`. DOM dual-channel fallback: the 0.1.1-rc.2
|
|
103
|
+
* `<textarea>` path, then the 0.1.2-rc.1 `contenteditable` path. Used by
|
|
104
104
|
* `setComposerText` (the harness-facade-aware writer) as the last-resort and
|
|
105
105
|
* by `runRewindAndFill` to put the withdrawn target message back into the
|
|
106
106
|
* composer after a rewind. Best-effort — no composer match means false,
|
|
@@ -109,12 +109,12 @@ interface ComposerDraftWriter {
|
|
|
109
109
|
export declare function fillComposer(text: string): boolean;
|
|
110
110
|
/**
|
|
111
111
|
* Dual-channel composer write, mirroring `chatSnapshotOf`: prefer the harness
|
|
112
|
-
* facade's `setDraft` (
|
|
113
|
-
* to the DOM `fillComposer` (rc.2 textarea /
|
|
114
|
-
* facade that throws (session teardown) is treated as absent so the DOM path
|
|
112
|
+
* facade's `setDraft` (0.1.2-rc.1, correct whole-draft replace), then degrade
|
|
113
|
+
* to the DOM `fillComposer` (0.1.1-rc.2 textarea / 0.1.2-rc.1 contenteditable).
|
|
114
|
+
* A facade that throws (session teardown) is treated as absent so the DOM path
|
|
115
115
|
* still restores the text. Never throws.
|
|
116
116
|
* @param text - the withdrawn target message text.
|
|
117
|
-
* @param facade - the
|
|
117
|
+
* @param facade - the 0.1.2-rc.1 session input draft writer, when reachable.
|
|
118
118
|
* @returns whether a channel applied the text.
|
|
119
119
|
*/
|
|
120
120
|
export declare function writeComposer(text: string, facade: ComposerDraftWriter | undefined): boolean;
|
|
@@ -136,17 +136,20 @@ export declare function runRewindAndFill(session: SessionFace, seq: number, mode
|
|
|
136
136
|
* Locate the actions container of a user/steering seat row — the element the
|
|
137
137
|
* ↶ button portals into (the copy/branch IconActions row).
|
|
138
138
|
*
|
|
139
|
-
*
|
|
140
|
-
* -
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* `MessageIconActions`
|
|
149
|
-
*
|
|
139
|
+
* @dualmode 0.1.1-rc.2 ↔ 0.1.2-rc.1
|
|
140
|
+
* - 0.1.1-rc.2: the hover-reveal root is attribute-marked
|
|
141
|
+
* `[data-time-hover-root]` and mounts the actions row as its LAST child. A
|
|
142
|
+
* pending row IS that root; a durable row CONTAINS it as a descendant.
|
|
143
|
+
* - 0.1.2-rc.1: the marker was removed from user rows (it now lives only on the
|
|
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.
|
|
150
153
|
*
|
|
151
154
|
* Returns undefined when no qualifying container is found; the caller refuses
|
|
152
155
|
* to portal (never a crash, never a wrong attachment). Exported as a test seam
|
|
@@ -171,9 +174,10 @@ interface RewindPortalsProps extends RewindBridgeDeps {
|
|
|
171
174
|
* synchronous full-transcript scan inside a commit microtask.
|
|
172
175
|
*/
|
|
173
176
|
export declare function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, watchChat, t, subscribeLocale, setComposerText }: RewindPortalsProps): ReactNode;
|
|
174
|
-
/** The current composer draft, on whichever channel: rc.2 textarea
|
|
175
|
-
*
|
|
176
|
-
* Exported as a test seam (the empty-composer guard in
|
|
177
|
+
/** The current composer draft, on whichever channel: 0.1.1-rc.2 textarea
|
|
178
|
+
* `.value`, 0.1.2-rc.1 contenteditable `textContent`. Empty when the composer
|
|
179
|
+
* is absent. Exported as a test seam (the empty-composer guard in
|
|
180
|
+
* `retractPending`). */
|
|
177
181
|
export declare function composerText(): string;
|
|
178
182
|
/**
|
|
179
183
|
* Build the slot-entry component for the plugin apply(): a tiny bridge that
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
* host schema enforces). "Discard changes" restores the last-read baseline.
|
|
12
12
|
*
|
|
13
13
|
* It neither imports the client settings typed contract nor depends on the
|
|
14
|
-
*
|
|
15
|
-
* the `set(field, value)` method present on both rc.2 and
|
|
16
|
-
* receives a tiny structural `CleanupCardApi` supplied by
|
|
17
|
-
* so the component stays harness-agnostic and
|
|
14
|
+
* 0.1.2-rc.1-only `mutate` write API: it reads `getSnapshot().value` and writes
|
|
15
|
+
* via the `set(field, value)` method present on both 0.1.1-rc.2 and 0.1.2-rc.1,
|
|
16
|
+
* and the card receives a tiny structural `CleanupCardApi` supplied by
|
|
17
|
+
* `src/client/index.ts` so the component stays harness-agnostic and
|
|
18
|
+
* unit-testable in isolation.
|
|
18
19
|
*
|
|
19
20
|
* @module dsh-rewind/client/settings-card
|
|
20
21
|
*/
|
|
@@ -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/* Dual-channel note: the harness card corner/border drifted across versions \u2014\n rc.2 uses border-radius 12px + 1px solid border-l2, while
|
|
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/* Dual-channel note: the harness card corner/border drifted across versions \u2014\n 0.1.1-rc.2 uses border-radius 12px + 1px solid border-l2, while 0.1.2-rc.1\n uses 16px + 0.5px border-l4 (and adds corner-shape: round). We intentionally\n keep the 0.1.1-rc.2 values here for now; if the 0.1.2-rc.1 look is wanted,\n switch these on the host version like the other dual-channel surfaces (see the\n host-version probe in src/client/index.ts). */\n.dsh-rewind-cleanup-card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\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: 1px 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: 1px 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";
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-channel session event-log reader.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* (`get events(): readonly
|
|
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
|
|
6
7
|
* member and replaced it with the on-demand APIs `snapshotEvents(from?, to?)`
|
|
7
8
|
* (half-open range, internally cached), `eventAt(seq)`, `ownEvents()` and
|
|
8
9
|
* `seq`. This module reads the full log through whichever channel the host
|
|
9
|
-
* exposes, so the plugin keeps one code path across both.
|
|
10
|
+
* exposes, so the plugin keeps one code path across both endpoints.
|
|
10
11
|
*
|
|
11
12
|
* `snapshotEvents()` is deliberately preferred over `ownEvents()`:
|
|
12
13
|
* `snapshotEvents()` defaults to the whole log (including any fork-inherited
|
|
13
|
-
* prefix), which is exactly what `Session.events` returned on rc.2 — the
|
|
14
|
+
* prefix), which is exactly what `Session.events` returned on 0.1.1-rc.2 — the
|
|
14
15
|
* rewind semantics are preserved for fork-seeded sessions. `ownEvents()`
|
|
15
16
|
* would drop the inherited prefix and change behaviour.
|
|
16
17
|
*
|
|
@@ -20,15 +21,17 @@ import type { Session, SessionEvent } from '@deepseek-ai/dsh-session';
|
|
|
20
21
|
/**
|
|
21
22
|
* Read the full event log of a session, transparently across harness channels:
|
|
22
23
|
* - 0.1.1-rc.2 (`Session.events`): the live immutable log array.
|
|
23
|
-
* - 0.1.2-
|
|
24
|
-
* by the session until the next append.
|
|
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).
|
|
25
28
|
*
|
|
26
29
|
* Falls back to an empty log when the session exposes neither member (a future
|
|
27
30
|
* harness shape) rather than throwing — callers already treat the empty log as
|
|
28
31
|
* "no candidates".
|
|
29
32
|
*
|
|
30
33
|
* @param session - the session to read.
|
|
31
|
-
* @returns the full event log in log order (the same shape rc.2's `events`
|
|
34
|
+
* @returns the full event log in log order (the same shape 0.1.1-rc.2's `events`
|
|
32
35
|
* returned, so existing consumers are unchanged).
|
|
33
36
|
*/
|
|
34
37
|
export declare function eventsOf(session: Session): readonly SessionEvent[];
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Version-neutral settings-namespace reading for the host half.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
6
7
|
* that helper (the `SettingsNamespace` *type* remains) and `settings.get`
|
|
7
8
|
* accepts the raw namespace string. This module collapses both into one runtime
|
|
8
|
-
* call: on rc.2 the brand is a compile-time marker erased at runtime (so
|
|
9
|
-
* `settingsNamespace(ns)` returns `ns`), on 0.1.2-
|
|
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
|
|
10
11
|
* absent and the raw `ns` string is used directly. That is what lets a single
|
|
11
12
|
* compiled host bundle link and run on both harness generations.
|
|
12
13
|
*
|
|
@@ -16,17 +17,17 @@
|
|
|
16
17
|
export interface SettingsProviderLike {
|
|
17
18
|
/**
|
|
18
19
|
* Read one registered settings section by namespace. Accepts whatever the
|
|
19
|
-
* running DSH generation passes: a branded `SettingsNamespace` (rc.2,
|
|
20
|
-
* erased) or the raw namespace string (0.1.2-
|
|
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).
|
|
21
22
|
*/
|
|
22
23
|
get(ns: string): unknown;
|
|
23
24
|
}
|
|
24
|
-
/** The `settingsNamespace(ns)` brand helper, or `undefined` when removed (0.1.2-
|
|
25
|
+
/** The `settingsNamespace(ns)` brand helper, or `undefined` when removed (0.1.2-rc.1). */
|
|
25
26
|
export type SettingsNamespaceBrand = ((value: string) => string) | undefined;
|
|
26
27
|
/**
|
|
27
28
|
* Read one settings section keyed by `ns`, tolerant of the settings-namespace
|
|
28
|
-
* brand across DSH generations. Pass the brand helper when available (the
|
|
29
|
-
* path); it is `undefined` on 0.1.2-
|
|
30
|
-
* directly. Never throws: an absent section simply returns `undefined`.
|
|
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`.
|
|
31
32
|
*/
|
|
32
33
|
export declare function readSettingsSection(provider: SettingsProviderLike, ns: string, brand: SettingsNamespaceBrand): unknown;
|
|
@@ -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 links on both rc.2 and
|
|
50
|
-
* API drift (
|
|
51
|
-
* host passes in, never to this module.
|
|
49
|
+
* contract) so the host bundle links on both 0.1.1-rc.2 and 0.1.2-rc.1 — the
|
|
50
|
+
* settings API drift (0.1.2 adds `mutate`, 0.1.1 does not) is confined to the
|
|
51
|
+
* seam the host passes in, never 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.7.
|
|
3
|
+
"version": "0.7.5",
|
|
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",
|