dsh-session-manager 0.5.2 → 0.5.3

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/CHANGELOG.md CHANGED
@@ -1,247 +1,260 @@
1
- # Changelog
2
-
3
- ## 0.5.2 — 2026-09-23
4
-
5
- - **fix(bulk management)**: add a missing entry-point for batch operations. The previous build gated the row checkboxes and `BulkActionBar` behind `selectedIds.size > 0`, so neither was ever reachable from the UI. A new **Select / 选择** toggle in the panel header now reveals the row checkboxes and the bulk action bar; toggling it a second time clears the selection and exits selection mode. Selection-mode state is also reset whenever the panel closes.
6
-
7
- - **fix(bulk management)**: the SessionManagerPanel had a duplicated `return` statement above the bulk-dialog declarations (`bulkPreviewDialog`, `bulkProgressDialog`, `bulkResultDialog`, `bulkTagDialog`, `bulkPriorityDialog`, `bulkMoveDialog`, `bulkPresetDialog`). The early return made every bulk dialog unreachable, so the user never saw the confirmation preview, progress bar, or per-id success / failed / skipped result dialog. The duplicate return has been removed; the panel now keeps every dialog declaration live and renders them all in the final Fragment.
8
-
9
- - **feat(bulk management)**: add the **Migrate preset…** button to the bulk action bar. Selecting rows and clicking the new button opens a preset picker (sourced from `/preset-scan`) and, on confirm, runs the `preset-migrate` action against every selected session through the existing `/batch` endpoint. Sessions already on the chosen preset are reported as skipped in the result dialog; failed sessions can be retried individually.
10
-
11
- - **fix(host /batch)**: the `/session-manager/api/batch` host handler had four regressions that were hidden by the bulk-dialog UI bug fixed in the same release:
12
-
13
- 1. **archive** called `ctx.workspaces.archiveSession(sessionId)` directly from the per-request dispatch; Cordis rejected it with `cannot get property 'workspaces' without inject`. The host now exposes an `archiveSession` helper that mirrors `unarchiveSession` and updates `workspaceRegistry.archivedSessionIds` atomically.
14
-
15
- 2. **favorite / review / set-priority / add-tags / remove-tags** threw `annotations is not a function` on the first id because the original `runBatchAction` signature destructured `annotations` from its parameter object and callers did not pass it. `runBatchAction` now resolves the annotation accessor from the surrounding closure so it can never again be silently `undefined`.
16
-
17
- 3. **unfavorite / unreview** were not in the `BATCH_ACTIONS` set and were rejected with `action 不支持: unfavorite`. Both are now first-class annotation actions; `annotationPatchFromBatchAction` maps them to `{ favorite: false }` / `{ reviewLater: false }`.
18
-
19
- 4. the `BATCH_ACTIONS` set, the annotation action set inside `runBatchAction`, and `annotationPatchFromBatchAction` have been kept in sync.
20
-
21
- As a hygiene cleanup, an orphan copy of the same handler that was left inside the file header JSDoc (between `/**` and the real `* @dsh-session-manager` description) has been removed. **Important:** if any of these errors were seen before this fix, hard-refresh DSH (Ctrl+Shift+R) so the cached plugin bundle is replaced with the new one.
22
-
23
- - **fix(bulk dialog positioning)**: the bulk preview / progress / result / tag-input / priority / move / preset-migrate dialogs had only a `z-index` rule on `.sm-bulkDialog.sm-nativeDialogLayer` and inherited the default `position: static`, so they rendered in normal document flow at the bottom of the panel (below the row list). They now reuse the same fixed-position `inset: calc(50vh - 90px) auto auto calc(50vw + 308px)` as `.sm-confirmDialog.sm-nativeDialogLayer` and pop up to the right of the panel, matching every other per-row dialog.
24
-
25
- - **fix(bulk dialog dark mode)**: every `[data-sm-theme=dark]` override that previously covered `.sm-panelDialog` / `.sm-confirmDialog` / `.sm-migrateDialog` now also covers `.sm-bulkDialog`. Without this, dark mode rendered the bulk dialog body, header, footer, list, result list, progress bar and progress fill in default white-on-white, making the dialog text invisible.
26
-
27
- - **fix(footer)**: FooterAction now reads `props.wide` from `SidebarFooterActionOwnerProps` and renders differently for collapsed (`scope: 'root'` rail, 36x36 icon-only button) vs expanded (full-width row, icon + label, left-aligned) sidebar (DSH 0.1.6+ `sidebar.footer.action` slot contract).
28
- - **test(bulk management)**: add client-side coverage for issue #13: est/client-bulk-selection.test.mjs (static guards on the selection-state hooks), est/client-bulk-actions.test.mjs (static guards on the BulkActionBar wiring + locale coverage), est/client-bulk-runbatch.test.mjs (unit coverage for the runBatch wrapper via runInNewContext with a stubbed fetch), and est/client-bulk-static-guards.test.mjs (cross-cutting structural invariants -- namespace ownership, panel dialog sibling layout, fan-out refresh, host/client action vocabulary). Total tests: 225 (188 pre-existing + 37 new).
29
- - **feat(bulk management)**: add multi-select checkboxes to session rows plus a sticky bulk action bar with archive, unarchive, favorite, unfavorite, mark-for-review, clear-review, add-tags, clear-tags, set-priority, move-to-workspace, and delete actions. Destructive actions run through a BatchPreviewDialog with per-id skip/fail grouping; non-destructive ones fire immediately. Progress, success/failure counts, per-item error reasons, and a one-click **Retry failed** re-arm the failed ids back into the selection. The /batch endpoint is reused so per-id errors surface as partial failures without aborting the batch. Bulk state is reset when the panel closes or the filter excludes a selected row.
30
- - **feat(sessions)**: `ARTIFACT_NAMES` now lists `session.v4.jsonl.zstd` first so DSH 0.1.7 V4-default session artifacts are picked up by the list-snapshot reader. Existing V3/V2/V1 files remain readable; the reader is version-agnostic and parses the header JSON regardless of declared version, so no per-version code paths are required.
31
-
32
- ## 0.5.1 — 2026-09-18
33
-
34
- - **feat(annotations)**: add favorites, manual review flags, tags, multiline notes and priority (1 highest → 5 lowest, default **3 Normal**) to the manager and title bar. Add annotation search/filtering and priority sorting. Persist separately from session history with atomic writes, an inter-process lock, strict limits, conflict detection and deletion cleanup; synchronize browser surfaces and preserve unsaved drafts on failure.
35
-
36
- - **feat(annotations)**: add an opt-in AI-assisted workflow in the annotation editor. A **Copy Prompt** / **复制 Prompt** button copies a strict-JSON prompt (Chinese or English, matched to the UI locale) to the clipboard for the user to paste into the current conversation. An **Import** / **导入** button reads the clipboard, extracts the first JSON object (tolerating Markdown fences, conversational wrappers, smart quotes, stray backslashes and a leading BOM), validates tags/note/priority against the same limits, and populates the editor fields. Oversized notes are truncated and flagged in the status message; invalid tags/priority are dropped with reasons. Importing into a dirty draft triggers a confirm. Both buttons stay out of the conversation history — the plugin never calls the model directly. The parser is also exported as `parseClipboardAnnotation` from `lib/clipboard-parser.js` for tests and potential server-side reuse.
37
-
38
- - **feat(annotations)**: add inline clear buttons inside the **Tags** and **Note** fields of the annotation editor. Each button only appears while the corresponding field has content and clears it without touching the other controls. Both buttons are disabled while a save is in flight and respect the existing Escape / IME handling.
39
-
40
- - **feat(annotations)**: redesign the annotation editor layout. Favorite and review flags stack vertically on the left; priority and its small help text occupy the right column. The **Tags**, **Note**, and AI **paste** textareas all share the same `sm-noteInput` style and `rows: 3` height (60px min-height), so the three input boxes line up visually. The "{count} / 2000 字符" note counter and the "仅用于会话整理" privacy hint are removed; help text is moved into each input's `placeholder`. In the AI paste block the two buttons now sit **above** the paste textarea (导入 on the left, 复制 Prompt on the right) so the editor footer stays consistent. The priority label now uses the same 13px font as the favorite / review checkboxes.
41
-
42
- - **feat(annotations)**: remove the "未设置 / Not set" priority option. Priority is always one of 1–5, and the default is **3 (Normal)**; legacy data with `priority: null` is normalized to 3 in display, sort and filter, so there is no longer a separate "always-sorts-last" state. The priority filter dropdown, row badges and header badge all reflect the unified 1–5 scale; AI-returned `"priority": null` is also normalized to 3 by the clipboard parser. The priority help text now reads "1 最高,5 最低,默认 3(普通)" / "1 is highest, 5 is lowest. Default is 3 (Normal)."
43
-
44
- - **fix(annotations)**: in the manager's row badges, P1–P5 now always render (legacy `null` renders as P3) so the priority column is visually consistent across all rows instead of being absent for unset entries. The header shortcut button likewise always shows the current P-number badge.
45
-
46
- - **fix(annotations)**: the AI copy/paste prompt now follows the active UI language. The dialog detects the language from the t() function (probing `marks.favorite`) instead of relying on `window.__smActiveLanguage`, which was never set; the prompt button writes Chinese under a Chinese UI and English under an English UI even when the global flag is missing.
47
-
48
- - **fix(annotations)**: the AI paste workflow's error message now appends the actual `JSON.parse` error position from each recovery attempt (原始 / 修复引号/反斜杠 / 扫描对象 / 扫描对象+修复), so users can see exactly which character broke parsing when the auto-repair still fails. The parser also strips a leading UTF-8 BOM, normalizes smart quotes, and repairs stray single backslashes inside string values.
49
-
50
- - **feat(annotations)**: tag input accepts both English `,` and Chinese `,` as separators (regex `/[,,\n]/`), trims whitespace around each tag, drops empty entries, and merges case-insensitive duplicates — so AI outputs in either locale parse cleanly without the user having to re-type the separator.
51
-
52
- - **feat(manager)**: add case-insensitive title/session-ID search, workspace/ungrouped filtering, four time-order modes, matching/total counts and reset controls; combine them with the existing archive filter. Creation times are supplied from cached host headers when DSH summaries omit them, without reading logs. Add workspace load/retry handling, stale-request cancellation, narrow-screen layout, IME-safe Escape handling and real-bundle interaction tests.
53
-
54
- - **fix(safety)**: validate session IDs, directory containment, symlinks/junctions and artifact identity before deletion or file rewrites; reject an already occupied move destination.
55
- - **fix(persistence)**: separate backup/publication/rollback phases; never delete the original after a failed backup rename. Preserve recovery files and report their paths if rollback fails, and clean uncommitted temporary files after write failures.
56
- - **fix(zstd)**: use structural frame decoding on every rewrite path and reject corrupt/torn logs instead of publishing a decoded prefix.
57
- - **fix(startup)**: distinguish incomplete scans from empty libraries; normalize snapshot headers, preserve live/concurrently attached sessions, and reconcile using a fresh immutable registry state.
58
- - **perf**: read only orphan artifact headers during enumeration and avoid duplicate full readRaw decoding for mutations.
59
- - **chore(test)**: test the real plugin routes instead of a copied move implementation; cover traversal, junctions, corruption, rollback failures, concurrent mutations, and startup read failures. Run module imports, tests, and package checks on Windows/Linux with Node 22.15.0/24; declare the Zstd-capable Node requirement.
60
-
61
- ## 0.4.11 — 2026-09-14
62
-
63
- - **chore(client)**: drop `@deepseek-ai/dsh-client-runtime` from `dsh.client.inject`. The package is no longer shipped by DSH 0.1.5-rc.2 / 0.1.2-alpha or newer (its client-bootstrap role was folded into `@deepseek-ai/dsh-client-store`). This plugin's bundle never required it, so removing the stale reference is a no-op at runtime and only cleans up the published manifest (#12).
64
-
65
- ## 0.4.10 — 2026-09-13
66
-
67
- - **fix(move)**: cross-workspace move no longer breaks the live JSONL writer. The DSH JSONL backend keeps one `JsonlSessionHandle` per session id in an in-process tracker; its `header.cwd` is captured at construction, and the api-gateway's `session/event` router writes through that handle, so a session whose header was rewritten in memory but whose writer was still pointing at the pre-move directory started throwing `ENOENT` on the first new message (issue #8). `moveSession` now mutates the live writer's `header` in place so its persist path flips to the target `cwd` while the same handle, queue, cursor, and lease are retained; the Agent's owned handle therefore stays consistent with the tracker entry, and no `session/disposed` is fabricated. If the runtime does not expose a rebindable writer (older DSH builds or a custom backend), the move now refuses up front with a clear message instead of silently leaving the live session writing to a deleted path. Adds `test/issue-8-move-enoent.test.mjs` (post-move writer identity stability + a "no-fix ENOENT" regression guard) and `test/issue-8-repro/` (a standalone reproducer script).
68
-
69
- - **chore**: bump version to 0.4.10.
70
-
71
- ## 0.4.9 — 2026-09-12
72
-
73
- - **fix(ui)**: Session manager panel and inner dialogs (Delete / Move /
74
- Migrate confirmations) now close on Esc regardless of focus
75
- position (issue #7). The original layer-bound onKeyDown was
76
- unreachable because the sidebar toggle / row button that opened
77
- each modal stayed focused -- focus is a sibling of the layer,
78
- not a descendant, so keydown never bubbles into the modal subtree.
79
- ConfirmDialog / MoveDialog are also used by the title-bar Delete
80
- / Move actions, so this fix applies there too.
81
-
82
- - **fix(ui)**: Modals now close ONLY via Esc or their explicit close
83
- buttons. The original 0.4.7 behavior (close on backdrop click)
84
- was removed per user feedback -- a stray click outside the panel
85
- was dismissing it accidentally. Every `.sm-nativeDialogBackdrop`
86
- `onMouseDown` handler is gone; the dim backdrop itself is also
87
- gone so opening a modal never darkens the page (sidebar or
88
- content area).
89
-
90
- - **refactor(ui)**: Esc handling moved from per-layer `onKeyDown`
91
- to a `window`-level `keydown` listener via `useEffect` on every
92
- modal. A `useRef` lets the listener see the latest state values
93
- without re-subscribing on every render. The panel listener
94
- returns early when any inner dialog is open so the child
95
- dialog's listener gets the first shot at Esc.
96
-
97
- - **fix(ui)**: After a keyboard-driven close (Esc), the originally
98
- focused trigger button (sidebar toggle / row button / Cancel
99
- button) no longer leaves a lingering `:focus-visible` ring. The
100
- four Esc handlers `blur()` the active element after the close
101
- call. Mouse-driven closes are not affected -- `:focus-visible`
102
- only activates for keyboard-acquired focus.
103
-
104
- - **fix(ui)**: ConfirmDialog no longer listens for Enter at the
105
- layer level. The previous handler raced with the focused Cancel
106
- button's native Enter handler -- both fired (`onCancel` +
107
- `onConfirm`). Enter now lives only on the focused button.
108
-
109
- - **cleanup(ui)**: Remove three dead `<section>` `ref={(el) =>
110
- el.focus()}` callbacks (sections have no `tabindex` and are
111
- not focusable, so the focus calls were silent no-ops). Also
112
- remove the now-unused `onPanelKey` callback and `tabIndex: -1`
113
- attributes on the modal layers. The `.sm-nativeDialogBackdrop`
114
- divs are no longer rendered at all -- they had no behavior left
115
- after removing the click-to-close.
116
-
117
- - **chore**: bump version to 0.4.9.
118
-
119
- ## 0.4.7 — 2026-09-10
120
-
121
- - **fix(disk scan)**: include `session.v3.jsonl.zstd` in the on-disk
122
- filename list used by readSessionArtifact() and listSessionHeaders().
123
- DSH 0.1.5-rc.1's persistence backend writes generation v3 artifacts at
124
- that filename; the 0.4.6 release scanned only v2/plaintext names, so
125
- fresh sessions appeared to have no disk record and the move/migrate
126
- endpoints failed with "会话没有磁盘记录" / "session has no artifact".
127
-
128
- - **chore**: bump version to 0.4.7.
129
-
130
-
131
- ## 0.4.6 — 2026-09-05
132
-
133
- - **fix(persistence)**: read all concatenated Zstandard frames in a session
134
- artifact. DSH writes one frame for the header and additional frames for
135
- event batches; reading only the first frame made sessions appear to lose
136
- their event history after refresh.
137
- - **fix(move)**: preserve the session generation/header version when
138
- re-homing an artifact, and synchronize the live session, persistence
139
- coordinator, registry indexes, and workspace accounting after a move.
140
- This avoids stale-path `ENOENT` failures and v2/v0 filename/header
141
- mismatches on the next DSH startup.
142
- - **fix(preset migration)**: fall back to an id-based artifact scan when a
143
- persistence locate result points at a stale path, and mirror cold-path
144
- migrations into the live session projection.
145
- - **fix(client)**: stop calling the removed/unavailable
146
- `noteAgentPreset` client method; refresh the session projection instead.
147
- - **recovery**: add `scripts/heal-v2-sessions.ps1` for the DSH 0.1.1-rc.2
148
- boot-time quirk where `session.v2.jsonl.zstd` contains a `version: 0`
149
- header. Run it before starting DSH; this repair must happen before DSH's
150
- workspace initialization, which is earlier than user plugin `apply()`.
151
-
152
- - **docs**: bump version to 0.4.6.
153
- ## 0.4.5 — 2026-09-05
154
-
155
- - **fix(move)**: keep the live session and agent in place during a cross-workspace
156
- move. The previous code tore down the live agent/session, moved the file, then
157
- called `ctx.agents.resume` to re-create the agent. DSH's `agent/status` event
158
- is only emitted on phase changes, so a freshly resumed agent never told the
159
- client it was now idle, leaving the sidebar's model selector and send button
160
- disabled ("会话不可用") until a manual browser refresh. The new path flushes
161
- pending events to disk, updates the in-memory session header + coordinator
162
- state + workspace accounting in place, and atomically renames the artifact,
163
- so the agent's UI keeps showing the same in-memory session with no client
164
- re-init.
165
- - **fix(preset migration)**: drop the over-strict `persistence.readRaw` /
166
- `persistence.list` precondition that caused the
167
- `/session-manager/api/preset-scan` endpoint to fail with
168
- `current persistence backend does not support readRaw/list` on a default DSH
169
- build. The actual `JsonlSessionPersistence` backend exposes both methods, so
170
- the precondition is replaced with a try/catch around `listSessionHeaders` that
171
- converts any missing-method failure into a useful
172
- `failed to enumerate sessions: <detail>` message.
173
- - **chore**: remove the now-unused `quietLive` / `releaseLiveSession` helpers
174
- and squash the per-route indentation noise around `/move` and `/workspaces`.
175
-
176
- ## 0.4.4 — 2026-09-03
177
-
178
- - **docs**: rename the English README wording from `conversation` to `session` to align with the plugin name (`dsh-session-manager`), the Chinese README (`会话`), the DSH host APIs, and the [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) registry entry.
179
- - **chore**: rewrite `package.json` `description` to use `Session manager` / `sessions` for the same alignment, and bump the version to `0.4.4`.
180
- - **chore(repo)**: update the GitHub repository description to match.
181
-
182
- ## 0.4.3 — 2026-09-03
183
-
184
- - **docs**: add the [Awesome DSH Plugin](https://awesome-dsh-plugin.com) badge to `README.md` / `README.zh.md` so the repo surfaces its curated registry membership.
185
-
186
- ## 0.4.2 — 2026-09-03
187
-
188
- - **feat(theme)**: dialogs now auto-follow DSH''s dark/light theme (`data-ds-dark-theme` / ` `code-scheme` / `data-theme`) via a single `data-sm-theme` attribute and scoped CSS variables, with inline `background` / `color` / `border-color` applied to each dialog root so they stay opaque regardless of how DSH resolves its own tokens. The previous manual light/dark toggle button is removed.
189
- - **docs**: aligned bilingual README structure, dropped the obsolete "no bulk migration" wording, and added an Acknowledgments section that thanks the users and the contributors filing issues and opening PRs. Listed [dsh-market](https://github.com/dsh-market/dsh-market) and [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) as install sources.
190
- - **chore**: rewrote `package.json` `description` to match the new English summary.
191
-
192
- ## 0.4.1 — 2026-08-28
193
-
194
- - **fix(ui)**: keep the title-bar **Delete conversation** button readable on hover with a red background, white text, and red border; remove the unused legacy danger-button rules.
195
-
196
- ## 0.4.0 — 2026-08-28
197
-
198
- - **feat(session preset migration)**: replaces the former bulk workflow with a
199
- per-conversation **Migrate preset** action in Session manager. It resolves the
200
- effective preset from the latest `agent-preset/selected` event or, when absent,
201
- the session header, then safely updates that one conversation.
202
- - **fix(lifecycle)**: moving a conversation or migrating its preset now retires stale
203
- live agents and persistence owners before refresh. This prevents resume failures
204
- such as `already has a live persistence owner`.
205
- - **fix(move)**: refreshes session and workspace state immediately and once more after
206
- the host event race, so a moved conversation reappears in its target workspace
207
- without a manual browser refresh.
208
- - **ui**: finalizes header actions and Session manager dialogs: red delete actions,
209
- per-row preset migration, consistent dialog placement, readable hover states, and
210
- a close button beside the manager title.
211
- - **docs**: refreshes bilingual documentation and npm metadata for the single-session
212
- preset migration workflow.
213
-
214
- ## 0.3.0 — 2026-08-27
215
-
216
- - **feat(preset migration)**: introduced preset migration support for conversations
217
- whose configured Agent preset was renamed or removed.
218
- - **feat(move)**: added workspace move handling and client-side workspace refreshes.
219
-
220
- ## 0.2.1 — 2026-08-26
221
-
222
- - **fix(move)**: reimplemented cross-workspace moves so the session artifact, stored
223
- `cwd`, and workspace accounting are moved together while preserving history,
224
- title, archive state, and derived-session relationships.
225
- - **feat(workspaces API)**: added the workspace projection endpoint used by the move UI.
226
- - **guard**: reject subagent and transient blank-session placeholders for move actions.
227
-
228
- ## 0.2.0 — 2026-08-16
229
-
230
- > ⚠️ The initial workspace-move implementation was superseded by 0.2.1.
231
-
232
- - **feat(move)**: added the initial move-to-workspace UI and host endpoints.
233
-
234
- ## 0.1.2 — 2026-08-16
235
-
236
- - **docs**: synchronized package metadata and bilingual README files for publication.
237
-
238
- ## 0.1.1 — 2026-08-16
239
-
240
- - **fix(panel)**: hide transient blank-session placeholders from the manager panel.
241
- - **test**: added the host API smoke test.
242
- - **ci**: added syntax and package-content verification.
243
-
244
- ## 0.1.0 — 2026-08-16
245
-
246
- - **feat**: initial session deletion with confirmation, archive management, and the
247
- Session manager panel.
1
+
2
+ ## 0.5.3 — 2026-09-26
3
+
4
+ - **fix**: issue #17.2 (panel `MoveDialog` now forwards `t` so labels render translated) and issue #17.4 (row Open unarchives archived sessions first).
5
+
6
+ - **fix**: issue #18 — `moveSession` stamps `header.version` from the target filename so v4-named artifacts don't carry stale v0 headers (which broke DSH startup's `listArtifacts`).
7
+
8
+ - **fix**: dialog descriptions (`confirm.move.desc`, `confirm.delete.desc`) show only the friendly `displayTitle`, never the raw id. The title-bar lookup is resolved once per render via IIFE — an earlier attempt invoked `useSessions()` inside an event handler, which crashed the slot framework's `useSyncExternalStore` subscriber and let `SlotErrorBoundary` replace the whole `conversation.session.header.actions` slot with `<div data-slot-error="…" />`, taking every title-bar button down.
9
+
10
+ - **fix**: drop UTF-8 BOM from `package.json` (`JSON.parse` was rejecting it and surfacing the plugin as "all components disabled").
11
+
12
+ - **chore**: remove leftover `TEMP DEBUG` block in `lib/index.js` (issue #18 debugging residue that spammed the log every 2 s).
13
+
14
+ - **test**: regression guard added in `test/issue-17-static-guards.test.mjs`; existing `test/issue-18-move-version.test.mjs` covers #18.
15
+
16
+ ## 0.5.2 — 2026-09-23
17
+
18
+ - **fix(bulk management)**: add a missing entry-point for batch operations. The previous build gated the row checkboxes and `BulkActionBar` behind `selectedIds.size > 0`, so neither was ever reachable from the UI. A new **Select / 选择** toggle in the panel header now reveals the row checkboxes and the bulk action bar; toggling it a second time clears the selection and exits selection mode. Selection-mode state is also reset whenever the panel closes.
19
+
20
+ - **fix(bulk management)**: the SessionManagerPanel had a duplicated `return` statement above the bulk-dialog declarations (`bulkPreviewDialog`, `bulkProgressDialog`, `bulkResultDialog`, `bulkTagDialog`, `bulkPriorityDialog`, `bulkMoveDialog`, `bulkPresetDialog`). The early return made every bulk dialog unreachable, so the user never saw the confirmation preview, progress bar, or per-id success / failed / skipped result dialog. The duplicate return has been removed; the panel now keeps every dialog declaration live and renders them all in the final Fragment.
21
+
22
+ - **feat(bulk management)**: add the **Migrate preset…** button to the bulk action bar. Selecting rows and clicking the new button opens a preset picker (sourced from `/preset-scan`) and, on confirm, runs the `preset-migrate` action against every selected session through the existing `/batch` endpoint. Sessions already on the chosen preset are reported as skipped in the result dialog; failed sessions can be retried individually.
23
+
24
+ - **fix(host /batch)**: the `/session-manager/api/batch` host handler had four regressions that were hidden by the bulk-dialog UI bug fixed in the same release:
25
+
26
+ 1. **archive** called `ctx.workspaces.archiveSession(sessionId)` directly from the per-request dispatch; Cordis rejected it with `cannot get property 'workspaces' without inject`. The host now exposes an `archiveSession` helper that mirrors `unarchiveSession` and updates `workspaceRegistry.archivedSessionIds` atomically.
27
+
28
+ 2. **favorite / review / set-priority / add-tags / remove-tags** threw `annotations is not a function` on the first id because the original `runBatchAction` signature destructured `annotations` from its parameter object and callers did not pass it. `runBatchAction` now resolves the annotation accessor from the surrounding closure so it can never again be silently `undefined`.
29
+
30
+ 3. **unfavorite / unreview** were not in the `BATCH_ACTIONS` set and were rejected with `action 不支持: unfavorite`. Both are now first-class annotation actions; `annotationPatchFromBatchAction` maps them to `{ favorite: false }` / `{ reviewLater: false }`.
31
+
32
+ 4. the `BATCH_ACTIONS` set, the annotation action set inside `runBatchAction`, and `annotationPatchFromBatchAction` have been kept in sync.
33
+
34
+ As a hygiene cleanup, an orphan copy of the same handler that was left inside the file header JSDoc (between `/**` and the real `* @dsh-session-manager` description) has been removed. **Important:** if any of these errors were seen before this fix, hard-refresh DSH (Ctrl+Shift+R) so the cached plugin bundle is replaced with the new one.
35
+
36
+ - **fix(bulk dialog positioning)**: the bulk preview / progress / result / tag-input / priority / move / preset-migrate dialogs had only a `z-index` rule on `.sm-bulkDialog.sm-nativeDialogLayer` and inherited the default `position: static`, so they rendered in normal document flow at the bottom of the panel (below the row list). They now reuse the same fixed-position `inset: calc(50vh - 90px) auto auto calc(50vw + 308px)` as `.sm-confirmDialog.sm-nativeDialogLayer` and pop up to the right of the panel, matching every other per-row dialog.
37
+
38
+ - **fix(bulk dialog dark mode)**: every `[data-sm-theme=dark]` override that previously covered `.sm-panelDialog` / `.sm-confirmDialog` / `.sm-migrateDialog` now also covers `.sm-bulkDialog`. Without this, dark mode rendered the bulk dialog body, header, footer, list, result list, progress bar and progress fill in default white-on-white, making the dialog text invisible.
39
+
40
+ - **fix(footer)**: FooterAction now reads `props.wide` from `SidebarFooterActionOwnerProps` and renders differently for collapsed (`scope: 'root'` rail, 36x36 icon-only button) vs expanded (full-width row, icon + label, left-aligned) sidebar (DSH 0.1.6+ `sidebar.footer.action` slot contract).
41
+ - **test(bulk management)**: add client-side coverage for issue #13: est/client-bulk-selection.test.mjs (static guards on the selection-state hooks), est/client-bulk-actions.test.mjs (static guards on the BulkActionBar wiring + locale coverage), est/client-bulk-runbatch.test.mjs (unit coverage for the runBatch wrapper via runInNewContext with a stubbed fetch), and est/client-bulk-static-guards.test.mjs (cross-cutting structural invariants -- namespace ownership, panel dialog sibling layout, fan-out refresh, host/client action vocabulary). Total tests: 225 (188 pre-existing + 37 new).
42
+ - **feat(bulk management)**: add multi-select checkboxes to session rows plus a sticky bulk action bar with archive, unarchive, favorite, unfavorite, mark-for-review, clear-review, add-tags, clear-tags, set-priority, move-to-workspace, and delete actions. Destructive actions run through a BatchPreviewDialog with per-id skip/fail grouping; non-destructive ones fire immediately. Progress, success/failure counts, per-item error reasons, and a one-click **Retry failed** re-arm the failed ids back into the selection. The /batch endpoint is reused so per-id errors surface as partial failures without aborting the batch. Bulk state is reset when the panel closes or the filter excludes a selected row.
43
+ - **feat(sessions)**: `ARTIFACT_NAMES` now lists `session.v4.jsonl.zstd` first so DSH 0.1.7 V4-default session artifacts are picked up by the list-snapshot reader. Existing V3/V2/V1 files remain readable; the reader is version-agnostic and parses the header JSON regardless of declared version, so no per-version code paths are required.
44
+
45
+ ## 0.5.1 — 2026-09-18
46
+
47
+ - **feat(annotations)**: add favorites, manual review flags, tags, multiline notes and priority (1 highest → 5 lowest, default **3 Normal**) to the manager and title bar. Add annotation search/filtering and priority sorting. Persist separately from session history with atomic writes, an inter-process lock, strict limits, conflict detection and deletion cleanup; synchronize browser surfaces and preserve unsaved drafts on failure.
48
+
49
+ - **feat(annotations)**: add an opt-in AI-assisted workflow in the annotation editor. A **Copy Prompt** / **复制 Prompt** button copies a strict-JSON prompt (Chinese or English, matched to the UI locale) to the clipboard for the user to paste into the current conversation. An **Import** / **导入** button reads the clipboard, extracts the first JSON object (tolerating Markdown fences, conversational wrappers, smart quotes, stray backslashes and a leading BOM), validates tags/note/priority against the same limits, and populates the editor fields. Oversized notes are truncated and flagged in the status message; invalid tags/priority are dropped with reasons. Importing into a dirty draft triggers a confirm. Both buttons stay out of the conversation history — the plugin never calls the model directly. The parser is also exported as `parseClipboardAnnotation` from `lib/clipboard-parser.js` for tests and potential server-side reuse.
50
+
51
+ - **feat(annotations)**: add inline clear buttons inside the **Tags** and **Note** fields of the annotation editor. Each button only appears while the corresponding field has content and clears it without touching the other controls. Both buttons are disabled while a save is in flight and respect the existing Escape / IME handling.
52
+
53
+ - **feat(annotations)**: redesign the annotation editor layout. Favorite and review flags stack vertically on the left; priority and its small help text occupy the right column. The **Tags**, **Note**, and AI **paste** textareas all share the same `sm-noteInput` style and `rows: 3` height (60px min-height), so the three input boxes line up visually. The "{count} / 2000 字符" note counter and the "仅用于会话整理" privacy hint are removed; help text is moved into each input's `placeholder`. In the AI paste block the two buttons now sit **above** the paste textarea (导入 on the left, 复制 Prompt on the right) so the editor footer stays consistent. The priority label now uses the same 13px font as the favorite / review checkboxes.
54
+
55
+ - **feat(annotations)**: remove the "未设置 / Not set" priority option. Priority is always one of 1–5, and the default is **3 (Normal)**; legacy data with `priority: null` is normalized to 3 in display, sort and filter, so there is no longer a separate "always-sorts-last" state. The priority filter dropdown, row badges and header badge all reflect the unified 1–5 scale; AI-returned `"priority": null` is also normalized to 3 by the clipboard parser. The priority help text now reads "1 最高,5 最低,默认 3(普通)" / "1 is highest, 5 is lowest. Default is 3 (Normal)."
56
+
57
+ - **fix(annotations)**: in the manager's row badges, P1–P5 now always render (legacy `null` renders as P3) so the priority column is visually consistent across all rows instead of being absent for unset entries. The header shortcut button likewise always shows the current P-number badge.
58
+
59
+ - **fix(annotations)**: the AI copy/paste prompt now follows the active UI language. The dialog detects the language from the t() function (probing `marks.favorite`) instead of relying on `window.__smActiveLanguage`, which was never set; the prompt button writes Chinese under a Chinese UI and English under an English UI even when the global flag is missing.
60
+
61
+ - **fix(annotations)**: the AI paste workflow's error message now appends the actual `JSON.parse` error position from each recovery attempt (原始 / 修复引号/反斜杠 / 扫描对象 / 扫描对象+修复), so users can see exactly which character broke parsing when the auto-repair still fails. The parser also strips a leading UTF-8 BOM, normalizes smart quotes, and repairs stray single backslashes inside string values.
62
+
63
+ - **feat(annotations)**: tag input accepts both English `,` and Chinese `,` as separators (regex `/[,,\n]/`), trims whitespace around each tag, drops empty entries, and merges case-insensitive duplicates — so AI outputs in either locale parse cleanly without the user having to re-type the separator.
64
+
65
+ - **feat(manager)**: add case-insensitive title/session-ID search, workspace/ungrouped filtering, four time-order modes, matching/total counts and reset controls; combine them with the existing archive filter. Creation times are supplied from cached host headers when DSH summaries omit them, without reading logs. Add workspace load/retry handling, stale-request cancellation, narrow-screen layout, IME-safe Escape handling and real-bundle interaction tests.
66
+
67
+ - **fix(safety)**: validate session IDs, directory containment, symlinks/junctions and artifact identity before deletion or file rewrites; reject an already occupied move destination.
68
+ - **fix(persistence)**: separate backup/publication/rollback phases; never delete the original after a failed backup rename. Preserve recovery files and report their paths if rollback fails, and clean uncommitted temporary files after write failures.
69
+ - **fix(zstd)**: use structural frame decoding on every rewrite path and reject corrupt/torn logs instead of publishing a decoded prefix.
70
+ - **fix(startup)**: distinguish incomplete scans from empty libraries; normalize snapshot headers, preserve live/concurrently attached sessions, and reconcile using a fresh immutable registry state.
71
+ - **perf**: read only orphan artifact headers during enumeration and avoid duplicate full readRaw decoding for mutations.
72
+ - **chore(test)**: test the real plugin routes instead of a copied move implementation; cover traversal, junctions, corruption, rollback failures, concurrent mutations, and startup read failures. Run module imports, tests, and package checks on Windows/Linux with Node 22.15.0/24; declare the Zstd-capable Node requirement.
73
+
74
+ ## 0.4.11 — 2026-09-14
75
+
76
+ - **chore(client)**: drop `@deepseek-ai/dsh-client-runtime` from `dsh.client.inject`. The package is no longer shipped by DSH 0.1.5-rc.2 / 0.1.2-alpha or newer (its client-bootstrap role was folded into `@deepseek-ai/dsh-client-store`). This plugin's bundle never required it, so removing the stale reference is a no-op at runtime and only cleans up the published manifest (#12).
77
+
78
+ ## 0.4.10 — 2026-09-13
79
+
80
+ - **fix(move)**: cross-workspace move no longer breaks the live JSONL writer. The DSH JSONL backend keeps one `JsonlSessionHandle` per session id in an in-process tracker; its `header.cwd` is captured at construction, and the api-gateway's `session/event` router writes through that handle, so a session whose header was rewritten in memory but whose writer was still pointing at the pre-move directory started throwing `ENOENT` on the first new message (issue #8). `moveSession` now mutates the live writer's `header` in place so its persist path flips to the target `cwd` while the same handle, queue, cursor, and lease are retained; the Agent's owned handle therefore stays consistent with the tracker entry, and no `session/disposed` is fabricated. If the runtime does not expose a rebindable writer (older DSH builds or a custom backend), the move now refuses up front with a clear message instead of silently leaving the live session writing to a deleted path. Adds `test/issue-8-move-enoent.test.mjs` (post-move writer identity stability + a "no-fix ENOENT" regression guard) and `test/issue-8-repro/` (a standalone reproducer script).
81
+
82
+ - **chore**: bump version to 0.4.10.
83
+
84
+ ## 0.4.9 — 2026-09-12
85
+
86
+ - **fix(ui)**: Session manager panel and inner dialogs (Delete / Move /
87
+ Migrate confirmations) now close on Esc regardless of focus
88
+ position (issue #7). The original layer-bound onKeyDown was
89
+ unreachable because the sidebar toggle / row button that opened
90
+ each modal stayed focused -- focus is a sibling of the layer,
91
+ not a descendant, so keydown never bubbles into the modal subtree.
92
+ ConfirmDialog / MoveDialog are also used by the title-bar Delete
93
+ / Move actions, so this fix applies there too.
94
+
95
+ - **fix(ui)**: Modals now close ONLY via Esc or their explicit close
96
+ buttons. The original 0.4.7 behavior (close on backdrop click)
97
+ was removed per user feedback -- a stray click outside the panel
98
+ was dismissing it accidentally. Every `.sm-nativeDialogBackdrop`
99
+ `onMouseDown` handler is gone; the dim backdrop itself is also
100
+ gone so opening a modal never darkens the page (sidebar or
101
+ content area).
102
+
103
+ - **refactor(ui)**: Esc handling moved from per-layer `onKeyDown`
104
+ to a `window`-level `keydown` listener via `useEffect` on every
105
+ modal. A `useRef` lets the listener see the latest state values
106
+ without re-subscribing on every render. The panel listener
107
+ returns early when any inner dialog is open so the child
108
+ dialog's listener gets the first shot at Esc.
109
+
110
+ - **fix(ui)**: After a keyboard-driven close (Esc), the originally
111
+ focused trigger button (sidebar toggle / row button / Cancel
112
+ button) no longer leaves a lingering `:focus-visible` ring. The
113
+ four Esc handlers `blur()` the active element after the close
114
+ call. Mouse-driven closes are not affected -- `:focus-visible`
115
+ only activates for keyboard-acquired focus.
116
+
117
+ - **fix(ui)**: ConfirmDialog no longer listens for Enter at the
118
+ layer level. The previous handler raced with the focused Cancel
119
+ button's native Enter handler -- both fired (`onCancel` +
120
+ `onConfirm`). Enter now lives only on the focused button.
121
+
122
+ - **cleanup(ui)**: Remove three dead `<section>` `ref={(el) =>
123
+ el.focus()}` callbacks (sections have no `tabindex` and are
124
+ not focusable, so the focus calls were silent no-ops). Also
125
+ remove the now-unused `onPanelKey` callback and `tabIndex: -1`
126
+ attributes on the modal layers. The `.sm-nativeDialogBackdrop`
127
+ divs are no longer rendered at all -- they had no behavior left
128
+ after removing the click-to-close.
129
+
130
+ - **chore**: bump version to 0.4.9.
131
+
132
+ ## 0.4.7 — 2026-09-10
133
+
134
+ - **fix(disk scan)**: include `session.v3.jsonl.zstd` in the on-disk
135
+ filename list used by readSessionArtifact() and listSessionHeaders().
136
+ DSH 0.1.5-rc.1's persistence backend writes generation v3 artifacts at
137
+ that filename; the 0.4.6 release scanned only v2/plaintext names, so
138
+ fresh sessions appeared to have no disk record and the move/migrate
139
+ endpoints failed with "会话没有磁盘记录" / "session has no artifact".
140
+
141
+ - **chore**: bump version to 0.4.7.
142
+
143
+
144
+ ## 0.4.6 — 2026-09-05
145
+
146
+ - **fix(persistence)**: read all concatenated Zstandard frames in a session
147
+ artifact. DSH writes one frame for the header and additional frames for
148
+ event batches; reading only the first frame made sessions appear to lose
149
+ their event history after refresh.
150
+ - **fix(move)**: preserve the session generation/header version when
151
+ re-homing an artifact, and synchronize the live session, persistence
152
+ coordinator, registry indexes, and workspace accounting after a move.
153
+ This avoids stale-path `ENOENT` failures and v2/v0 filename/header
154
+ mismatches on the next DSH startup.
155
+ - **fix(preset migration)**: fall back to an id-based artifact scan when a
156
+ persistence locate result points at a stale path, and mirror cold-path
157
+ migrations into the live session projection.
158
+ - **fix(client)**: stop calling the removed/unavailable
159
+ `noteAgentPreset` client method; refresh the session projection instead.
160
+ - **recovery**: add `scripts/heal-v2-sessions.ps1` for the DSH 0.1.1-rc.2
161
+ boot-time quirk where `session.v2.jsonl.zstd` contains a `version: 0`
162
+ header. Run it before starting DSH; this repair must happen before DSH's
163
+ workspace initialization, which is earlier than user plugin `apply()`.
164
+
165
+ - **docs**: bump version to 0.4.6.
166
+ ## 0.4.5 — 2026-09-05
167
+
168
+ - **fix(move)**: keep the live session and agent in place during a cross-workspace
169
+ move. The previous code tore down the live agent/session, moved the file, then
170
+ called `ctx.agents.resume` to re-create the agent. DSH's `agent/status` event
171
+ is only emitted on phase changes, so a freshly resumed agent never told the
172
+ client it was now idle, leaving the sidebar's model selector and send button
173
+ disabled ("会话不可用") until a manual browser refresh. The new path flushes
174
+ pending events to disk, updates the in-memory session header + coordinator
175
+ state + workspace accounting in place, and atomically renames the artifact,
176
+ so the agent's UI keeps showing the same in-memory session with no client
177
+ re-init.
178
+ - **fix(preset migration)**: drop the over-strict `persistence.readRaw` /
179
+ `persistence.list` precondition that caused the
180
+ `/session-manager/api/preset-scan` endpoint to fail with
181
+ `current persistence backend does not support readRaw/list` on a default DSH
182
+ build. The actual `JsonlSessionPersistence` backend exposes both methods, so
183
+ the precondition is replaced with a try/catch around `listSessionHeaders` that
184
+ converts any missing-method failure into a useful
185
+ `failed to enumerate sessions: <detail>` message.
186
+ - **chore**: remove the now-unused `quietLive` / `releaseLiveSession` helpers
187
+ and squash the per-route indentation noise around `/move` and `/workspaces`.
188
+
189
+ ## 0.4.4 — 2026-09-03
190
+
191
+ - **docs**: rename the English README wording from `conversation` to `session` to align with the plugin name (`dsh-session-manager`), the Chinese README (`会话`), the DSH host APIs, and the [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) registry entry.
192
+ - **chore**: rewrite `package.json` `description` to use `Session manager` / `sessions` for the same alignment, and bump the version to `0.4.4`.
193
+ - **chore(repo)**: update the GitHub repository description to match.
194
+
195
+ ## 0.4.3 — 2026-09-03
196
+
197
+ - **docs**: add the [Awesome DSH Plugin](https://awesome-dsh-plugin.com) badge to `README.md` / `README.zh.md` so the repo surfaces its curated registry membership.
198
+
199
+ ## 0.4.2 — 2026-09-03
200
+
201
+ - **feat(theme)**: dialogs now auto-follow DSH''s dark/light theme (`data-ds-dark-theme` / ` `code-scheme` / `data-theme`) via a single `data-sm-theme` attribute and scoped CSS variables, with inline `background` / `color` / `border-color` applied to each dialog root so they stay opaque regardless of how DSH resolves its own tokens. The previous manual light/dark toggle button is removed.
202
+ - **docs**: aligned bilingual README structure, dropped the obsolete "no bulk migration" wording, and added an Acknowledgments section that thanks the users and the contributors filing issues and opening PRs. Listed [dsh-market](https://github.com/dsh-market/dsh-market) and [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) as install sources.
203
+ - **chore**: rewrote `package.json` `description` to match the new English summary.
204
+
205
+ ## 0.4.1 — 2026-08-28
206
+
207
+ - **fix(ui)**: keep the title-bar **Delete conversation** button readable on hover with a red background, white text, and red border; remove the unused legacy danger-button rules.
208
+
209
+ ## 0.4.0 — 2026-08-28
210
+
211
+ - **feat(session preset migration)**: replaces the former bulk workflow with a
212
+ per-conversation **Migrate preset** action in Session manager. It resolves the
213
+ effective preset from the latest `agent-preset/selected` event or, when absent,
214
+ the session header, then safely updates that one conversation.
215
+ - **fix(lifecycle)**: moving a conversation or migrating its preset now retires stale
216
+ live agents and persistence owners before refresh. This prevents resume failures
217
+ such as `already has a live persistence owner`.
218
+ - **fix(move)**: refreshes session and workspace state immediately and once more after
219
+ the host event race, so a moved conversation reappears in its target workspace
220
+ without a manual browser refresh.
221
+ - **ui**: finalizes header actions and Session manager dialogs: red delete actions,
222
+ per-row preset migration, consistent dialog placement, readable hover states, and
223
+ a close button beside the manager title.
224
+ - **docs**: refreshes bilingual documentation and npm metadata for the single-session
225
+ preset migration workflow.
226
+
227
+ ## 0.3.0 — 2026-08-27
228
+
229
+ - **feat(preset migration)**: introduced preset migration support for conversations
230
+ whose configured Agent preset was renamed or removed.
231
+ - **feat(move)**: added workspace move handling and client-side workspace refreshes.
232
+
233
+ ## 0.2.1 — 2026-08-26
234
+
235
+ - **fix(move)**: reimplemented cross-workspace moves so the session artifact, stored
236
+ `cwd`, and workspace accounting are moved together while preserving history,
237
+ title, archive state, and derived-session relationships.
238
+ - **feat(workspaces API)**: added the workspace projection endpoint used by the move UI.
239
+ - **guard**: reject subagent and transient blank-session placeholders for move actions.
240
+
241
+ ## 0.2.0 — 2026-08-16
242
+
243
+ > ⚠️ The initial workspace-move implementation was superseded by 0.2.1.
244
+
245
+ - **feat(move)**: added the initial move-to-workspace UI and host endpoints.
246
+
247
+ ## 0.1.2 — 2026-08-16
248
+
249
+ - **docs**: synchronized package metadata and bilingual README files for publication.
250
+
251
+ ## 0.1.1 — 2026-08-16
252
+
253
+ - **fix(panel)**: hide transient blank-session placeholders from the manager panel.
254
+ - **test**: added the host API smoke test.
255
+ - **ci**: added syntax and package-content verification.
256
+
257
+ ## 0.1.0 — 2026-08-16
258
+
259
+ - **feat**: initial session deletion with confirmation, archive management, and the
260
+ Session manager panel.