dsh-archived-chats 0.9.0 → 0.11.0
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 +44 -16
- package/README.md +44 -16
- package/assets/screenshots/8-conversation-preview.png +0 -0
- package/assets/screenshots/9-recycle-bin.png +0 -0
- package/docs/ARCHITECTURE.en.md +151 -0
- package/docs/ARCHITECTURE.md +151 -0
- package/lib/client.js +1143 -80
- package/lib/index.js +319 -31
- package/lib/recycle.js +446 -0
- package/lib/search.js +383 -0
- package/lib/snapshot.js +512 -0
- package/lib/trash.js +359 -0
- package/lib/types/client/index.d.ts +36 -4
- package/lib/types/index.d.ts +17 -5
- package/package.json +14 -2
package/README.en.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.en.md) | [中文](README.md)
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> 🔎 **Archived no longer means lost.** Search conversation content, read complete messages and tool calls, then back up, restore, or delete safely.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> ♻️ **Ordinary deletion is now undoable.** The plugin first creates a local protection snapshot containing the session and attachments, then moves it to the Recycle Bin. Physical removal happens only after an explicit **Delete permanently** action in the Recycle Bin.
|
|
8
|
+
|
|
9
|
+
A local conversation archive center for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): recover, search, read, back up, restore, or delete archived sessions.
|
|
8
10
|
|
|
9
11
|
Once a conversation is archived in DeepSeek Harness it disappears from the sidebar, and there is no built-in way to browse it again — only the workspace store (`~/.dsh/storages/workspace.json`) still remembers it. This plugin adds an **Archived Chats** page under Settings where every archived session is visible, searchable, and manageable.
|
|
10
12
|
|
|
@@ -24,15 +26,16 @@ dsh plugin --profile web update dsh-archived-chats
|
|
|
24
26
|
|
|
25
27
|
## Compatibility
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
The full 0.11.0 feature target is DeepSeek Harness `0.1.1-rc.2`. Older hosts can still expose the archive list, but a missing persistence, attachment, or live-session lifecycle capability produces an explicit capability error instead of guessed internal writes or an incomplete snapshot. The v0.9.0 UI was checked on Harness `0.1.0-rc.8`; v0.10.0 search, preview, and stored-image reads were checked on `0.1.1-rc.2`.
|
|
28
30
|
|
|
29
31
|
## Preview
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
Screenshots 1–7 were captured from 0.9.0 in a local DeepSeek Harness `0.1.0-rc.8` web profile. The archived preview is from 0.10.0 and the Recycle Bin is from 0.11.0, both captured in an isolated real DeepSeek Harness `0.1.1-rc.2` host.
|
|
32
34
|
|
|
33
35
|

|
|
34
36
|

|
|
35
|
-

|
|
38
|
+

|
|
36
39
|

|
|
37
40
|

|
|
38
41
|

|
|
@@ -42,25 +45,35 @@ All screenshots below were captured from 0.9.0 in a local DeepSeek Harness `0.1.
|
|
|
42
45
|
|
|
43
46
|
1. Archive a conversation from the normal DSH session menu. Archiving removes it from the sidebar but keeps its session data in the workspace store.
|
|
44
47
|
2. Open **Settings → Archived Chats**. The page groups archived conversations by workspace and remembers collapsed groups in this browser.
|
|
45
|
-
3. Search,
|
|
48
|
+
3. Search titles, tags, notes, conversation text, or tool output. Open the row preview to read an archived conversation without unarchiving it. Click **Select multiple** only when you need bulk actions.
|
|
46
49
|
4. Click **Import backup** to choose a ZIP produced by this plugin and confirm non-conflicting sessions after the preview. Click **Export backup** to export the current selection, or every archived chat when nothing is selected. Individual rows also have an export action.
|
|
47
|
-
5. Choose **Unarchive** to return a conversation to the sidebar.
|
|
50
|
+
5. Choose **Unarchive** to return a conversation to the sidebar. **Move to Recycle Bin** creates a protection snapshot and offers immediate **Undo**; the session can also be restored later from the **Recycle Bin** tab. Only **Delete permanently / Empty Recycle Bin** removes originals and snapshots irreversibly.
|
|
48
51
|
|
|
49
52
|
## Features
|
|
50
53
|
|
|
51
54
|
- **Complete archived-session list**, grouped by workspace (project) with a per-group count. Every group can be collapsed or expanded, and the state is remembered per browser.
|
|
52
|
-
- **
|
|
55
|
+
- **Full-text conversation search**: one search field matches titles, workspaces, tags, notes, user messages, assistant answers, and tool results, with a readable hit excerpt on each matching row.
|
|
56
|
+
- **Native archived conversation preview and turn navigation**: follow the Harness conversation layout with user messages on the right and assistant messages on the left; present Markdown, reasoning, tool activity, JSON, code, and available stored images read-only, while retaining a responsive turn rail for quick jumps. If the host lacks attachment capability, only images degrade and the rest of the preview remains readable.
|
|
57
|
+
- **Filter and sort** by type (all / regular / subagent), project, and tag; then order results by newest, oldest, or title.
|
|
53
58
|
- **Tags and notes**: open an editor from any row to attach up to 8 tags (24 Unicode characters each) and a note (2,000 Unicode characters). Tag chips render per row, overflowing past three into a `+N` indicator, and the tag filter narrows the list case-insensitively.
|
|
54
59
|
- **Storage insights**: a summary strip reports the archived count, total measured size, and how many sessions could not be measured; each row shows its own size. Measurement never follows symbolic links and skips sessions whose directories are unreadable.
|
|
55
60
|
- **JSON + Markdown backups**: export one row, the current selection, or every archived chat as a ZIP. Each package has a versioned manifest, a lossless machine-readable session record, and a human-readable transcript for every included session.
|
|
56
61
|
- **Preview-first import and restore**: choose a ZIP backup, inspect every session before writing, preselect only non-conflicting IDs, and restore selected sessions as archived chats. Existing IDs are skipped and never overwritten.
|
|
57
62
|
- **Compact top-level actions**: common **Import backup** / **Export backup** actions are direct, while the low-frequency destructive action lives under **More**. The page stays focused on DSH archive management without a persistent source selector or redundant menus.
|
|
58
|
-
- **On-demand multi-select**: checkboxes stay hidden by default and appear only after clicking **Select multiple**. Select individual chats, every visible result, or an entire project; the selection bar can export, unarchive, or
|
|
63
|
+
- **On-demand multi-select**: checkboxes stay hidden by default and appear only after clicking **Select multiple**. Select individual chats, every visible result, or an entire project; the selection bar can export, unarchive, or move the chosen chats to the Recycle Bin, while selections hidden by another filter remain intact.
|
|
59
64
|
- **Unarchive** a single chat or a whole project group from the group's `⋯` menu — restored chats reappear in the sidebar immediately.
|
|
60
|
-
- **
|
|
61
|
-
-
|
|
65
|
+
- **Archived / Recycle Bin tabs**: recycled rows stay grouped by their original workspace, each group can collapse independently, and rows show trash time, snapshot size, attachment count, and `trashed` / `degraded` / `purge-pending` state. Checkboxes stay hidden until **Select multiple** is activated.
|
|
66
|
+
- **Automatic protection snapshots**: moving a session captures all conversation events plus verified stored-image bytes before the recycle record commits. One current protection snapshot is kept per session; 0.11 does not provide historical retention or conversation-tree restoration.
|
|
67
|
+
- **Two-level restore**: when the original session is intact, restore only removes the recycle marker and does not rewrite persistence. If the original is missing, the plugin falls back to the validated session-and-attachment snapshot through public writer capabilities, never overwriting an existing ID.
|
|
68
|
+
- **Explicit permanent purge**: only the Recycle Bin exposes permanent delete and empty. The plugin records durable `purge-pending` crash intent before deleting the original and snapshot; interrupted purges retry at startup.
|
|
62
69
|
- Works in light and dark schemes; localized in English and 中文.
|
|
63
70
|
|
|
71
|
+
## Recycle Bin, privacy, and attachment limits
|
|
72
|
+
|
|
73
|
+
The recycle catalog (`trash.json`) and protection snapshots live under `$DSH_HOME/plugin-data/archived-chats/` and stay on this machine. Attachment bytes are read one at a time, digest-verified, and atomically published; neither conversations nor attachments are uploaded. Recycle previews use a separate authorized scope.
|
|
74
|
+
|
|
75
|
+
Permanent purge removes the snapshot's attachment copies, but Harness's global attachment store may retain identical bytes because another session still references them or because the host applies its own garbage-collection policy. This plugin does not claim immediate global attachment GC.
|
|
76
|
+
|
|
64
77
|
## Tags, notes, and statistics
|
|
65
78
|
|
|
66
79
|
Tags and notes live **only on your machine** in `$DSH_HOME/plugin-data/archived-chats/metadata.json` — they are never uploaded, synced, or sent anywhere else. Unarchiving a session keeps its metadata; a completed physical deletion removes it, while a deferred or failed deletion keeps it intact. Metadata and statistics failures are always non-blocking: the list, unarchive, and deletion keep working even when the metadata store is unreadable or a session directory cannot be measured.
|
|
@@ -107,15 +120,15 @@ Attachment references are preserved in `session.json`, but attachment bytes and
|
|
|
107
120
|
</details>
|
|
108
121
|
|
|
109
122
|
<details>
|
|
110
|
-
<summary><b>
|
|
123
|
+
<summary><b>Can I restore immediately after moving a session to the Recycle Bin?</b></summary>
|
|
111
124
|
|
|
112
|
-
|
|
125
|
+
Yes. The success notice includes **Undo**, and the Recycle Bin keeps a Restore action. A live session is safely disposed or parked before the recycle record commits. If the host lacks the required capability, the move fails explicitly and leaves the archived session intact.
|
|
113
126
|
|
|
114
127
|
</details>
|
|
115
128
|
|
|
116
129
|
## Implementation overview
|
|
117
130
|
|
|
118
|
-
The plugin has two halves: the Host service
|
|
131
|
+
The plugin has two halves: the Host service manages archives, snapshots, the recycle catalog, and restore/purge transactions, while the browser page provides search, preview, backup, restore, and explicit confirmations. Mutations go through guarded local routes. Ordinary removal commits only a recycle record; physical removal is reachable only through the Recycle Bin's crash-safe purge flow.
|
|
119
132
|
|
|
120
133
|
User-facing storage, backup limits, deletion outcomes, and compatibility notes stay in this README. Maintainer details such as route contracts, data flow, restore transactions, live-deletion lifecycle, and failure fallbacks are documented in [ARCHITECTURE.md](docs/ARCHITECTURE.en.md).
|
|
121
134
|
|
|
@@ -125,10 +138,25 @@ User-facing storage, backup limits, deletion outcomes, and compatibility notes s
|
|
|
125
138
|
npm test
|
|
126
139
|
```
|
|
127
140
|
|
|
128
|
-
The suite (`test/*.test.mjs`) covers export records and real ZIP decoding, bounded import validation, restore transactions,
|
|
141
|
+
The suite (`test/*.test.mjs`) covers export records and real ZIP decoding, bounded import validation, restore transactions, metadata and statistics, full-text search, conversation preview, and host-and-browser smoke tests. It uses an isolated temporary DSH home plus mocked host and browser runtimes; it never reads or changes real sessions.
|
|
129
142
|
|
|
130
143
|
## Version history
|
|
131
144
|
|
|
145
|
+
### 0.11.0
|
|
146
|
+
|
|
147
|
+
- Added persistent **Archived / Recycle Bin** tabs, independent recycle selection, trash-scoped preview, restore, permanent purge, and empty actions.
|
|
148
|
+
- Ordinary deletion now captures a complete local protection snapshot and moves the session to recoverable trash, with immediate **Undo**.
|
|
149
|
+
- Restore prefers the intact original and falls back to a validated session-plus-attachment snapshot without overwriting an existing ID.
|
|
150
|
+
- Added durable `purge-pending` recovery, snapshot recovery/degraded states, and safe migration of legacy `pending-deletions.json` IDs into recoverable trash without silent boot deletion.
|
|
151
|
+
- **Downgrade warning:** 0.10 does not understand 0.11 recycle records or snapshots. Before downgrading, restore needed sessions in 0.11 and back up `$DSH_HOME/plugin-data/archived-chats/`.
|
|
152
|
+
|
|
153
|
+
### 0.10.0
|
|
154
|
+
|
|
155
|
+
- Added archived conversation preview that follows the Harness conversation layout, with user messages on the right, assistant messages on the left, paginated message loading, and a responsive turn rail.
|
|
156
|
+
- Markdown, reasoning, tool activity, JSON, code, and available stored images are presented read-only; a missing host attachment capability affects images only, not the rest of the conversation.
|
|
157
|
+
- Added full-text search over Unicode conversation text and tool output, merged with the existing title/tag/note filters and displayed as row excerpts.
|
|
158
|
+
- Hardened preview and search with guarded local POST routes, bounded bodies and results, four-way inspection concurrency, partial-failure degradation, and a bounded TTL/LRU memory cache.
|
|
159
|
+
|
|
132
160
|
### 0.9.0
|
|
133
161
|
|
|
134
162
|
- Added an on-demand multi-select mode: list checkboxes stay hidden until requested, then disappear automatically after a completed bulk action.
|
|
@@ -189,7 +217,7 @@ The suite (`test/*.test.mjs`) covers export records and real ZIP decoding, bound
|
|
|
189
217
|
dsh plugin --profile web remove dsh-archived-chats
|
|
190
218
|
```
|
|
191
219
|
|
|
192
|
-
|
|
220
|
+
Uninstalling does not remove `metadata.json`, `trash.json`, protection snapshots, or a legacy `pending-deletions.json` under `$DSH_HOME/plugin-data/archived-chats/`, and it never triggers permanent purge. Restore or back up anything you need before manually handling that directory.
|
|
193
221
|
|
|
194
222
|
## License
|
|
195
223
|
|
package/README.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.en.md) | 中文
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> 🔎 **归档不再等于消失。** 直接搜索聊天正文、阅读完整对话和工具调用,然后安全备份、恢复或删除。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> ♻️ **普通删除现在可撤销。** 插件会先创建包含会话与附件的本地保护快照,再移入回收站;只有在回收站中明确选择「永久删除」才会物理清除。
|
|
8
|
+
|
|
9
|
+
为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 新增一个本地会话归档中心:找回、搜索、阅读、备份、恢复或删除被归档的会话。
|
|
8
10
|
|
|
9
11
|
在 DeepSeek Harness 里,聊天一旦归档就会从侧边栏消失,界面中没有任何入口可以再看到它,只有工作区存档(`~/.dsh/storages/workspace.json`)还记得它。这个插件在「设置」中补上一个「已归档的聊天」页面,让所有归档会话都可见、可搜索、可管理。
|
|
10
12
|
|
|
@@ -24,15 +26,16 @@ dsh plugin --profile web update dsh-archived-chats
|
|
|
24
26
|
|
|
25
27
|
## 兼容性
|
|
26
28
|
|
|
27
|
-
0.
|
|
29
|
+
0.11.0 的完整功能目标是 DeepSeek Harness `0.1.1-rc.2`。较旧宿主仍可使用归档列表,但缺少持久层、附件或运行中会话生命周期能力时,回收操作会返回明确的能力错误,不会猜测宿主内部结构或写入不完整快照。v0.9.0 界面已在 Harness `0.1.0-rc.8` 复核;v0.10.0 搜索、预览和图片读取已在 `0.1.1-rc.2` 复核。
|
|
28
30
|
|
|
29
31
|
## 预览
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
截图 1–7 来自 `0.9.0` 在本地 DeepSeek Harness `0.1.0-rc.8` Web profile 中的实际操作;归档对话预览来自 `0.10.0`,回收站来自 `0.11.0`,两者都在 DeepSeek Harness `0.1.1-rc.2` 隔离真实宿主中捕获。
|
|
32
34
|
|
|
33
35
|

|
|
34
36
|

|
|
35
|
-

|
|
38
|
+

|
|
36
39
|

|
|
37
40
|

|
|
38
41
|

|
|
@@ -42,25 +45,35 @@ dsh plugin --profile web update dsh-archived-chats
|
|
|
42
45
|
|
|
43
46
|
1. 在 DSH 正常聊天的会话菜单中点击归档。归档只会把会话从侧边栏隐藏,工作区存档仍会保留会话数据。
|
|
44
47
|
2. 打开 **设置 → 已归档的聊天**。页面按工作区分组,并在当前浏览器中记住分组的折叠状态。
|
|
45
|
-
3.
|
|
48
|
+
3. 搜索标题、标签、备注、聊天正文或工具结果;点击行内预览按钮可直接阅读归档对话,无需先取消归档。需要多选时点击 **批量选择** 显示复选框。
|
|
46
49
|
4. 点击顶部 **导入备份** 选择本插件导出的 ZIP,预览后确认无冲突会话;点击 **导出备份** 导出当前选中项,未选择时导出全部归档会话。单条会话也可以从行内操作导出。
|
|
47
|
-
5. 点击 **取消归档**
|
|
50
|
+
5. 点击 **取消归档** 将会话放回侧边栏;点击 **移至回收站** 会创建保护快照,可立即点击 **撤销**,也可稍后在 **回收站** 标签恢复。只有回收站中的 **永久删除 / 清空回收站** 会不可撤销地移除原会话和快照。
|
|
48
51
|
|
|
49
52
|
## 功能
|
|
50
53
|
|
|
51
54
|
- **完整归档列表**:按工作区(项目)分组并显示每组数量;每个分组都可折叠/展开,状态按浏览器记忆。
|
|
52
|
-
-
|
|
55
|
+
- **聊天正文全文搜索**:同一个搜索框同时匹配标题、项目、标签、备注、用户消息、助手回答与工具结果,并在结果行显示命中摘要。
|
|
56
|
+
- **原生归档对话预览与轮次导航**:沿用 Harness 会话布局,用户消息靠右、助手消息靠左;以只读方式展示 Markdown、思考过程、工具活动、JSON、代码和可用的已存储图片,并保留可快速跳转的响应式轮次轨道。宿主缺少附件能力时只影响图片,其他预览内容仍可阅读。
|
|
57
|
+
- **筛选与排序**:用类型(全部 / 普通会话 / 子代理会话)、项目和标签筛选,并按最新、最早或标题排序。
|
|
53
58
|
- **标签与备注**:任意行打开编辑器即可添加最多 8 个标签(每个最多 24 个 Unicode 字符)和一条备注(最多 2,000 个 Unicode 字符)。每行渲染标签小徽章,超过 3 个折叠为 `+N`,标签筛选不区分大小写。
|
|
54
59
|
- **存储统计**:概览条显示归档数量、已统计总大小与无法统计的会话数;每行显示各自占用。统计不会跟随符号链接,无法读取的会话目录显示为「无法统计」而非让请求失败。
|
|
55
60
|
- **JSON + Markdown 备份**:可导出单条、当前选中项或全部归档会话。每个 ZIP 都包含带版本的清单、用于机器恢复的完整会话 JSON,以及方便阅读的 Markdown 对话稿。
|
|
56
61
|
- **预览后导入与恢复**:选择 ZIP 备份后先检查全部会话,默认选中无冲突 ID 的项目,确认后作为已归档聊天恢复。已有 ID 会跳过,绝不会覆盖。
|
|
57
62
|
- **紧凑顶部操作**:常用的 **导入备份** / **导出备份** 直接可用,低频危险操作收纳在 **更多**;页面专注于 DSH 归档管理,不常驻来源选择器或冗余菜单。
|
|
58
|
-
- **按需多选**:复选框默认隐藏,点击 **批量选择**
|
|
63
|
+
- **按需多选**:复选框默认隐藏,点击 **批量选择** 后才显示;可逐条选择、选择当前筛选结果或整个项目。选中后可一次导出、取消归档或移至回收站,隐藏在其他筛选结果中的选择不会丢失。
|
|
59
64
|
- **取消归档**单个聊天,或从分组的 `⋯` 菜单整组取消——恢复的聊天会立刻回到侧边栏。
|
|
60
|
-
-
|
|
61
|
-
-
|
|
65
|
+
- **归档 / 回收站双标签**:回收站按原工作区分组并可独立折叠,显示移入时间、快照大小、附件数和 `trashed` / `degraded` / `purge-pending` 状态;复选框默认隐藏,点击 **批量选择** 后才显示。
|
|
66
|
+
- **自动保护快照**:移入回收站前保存完整会话事件和经校验的图片附件字节。每个会话只保留一个当前保护快照;本版不提供按时间的多代保留或会话树恢复。
|
|
67
|
+
- **两级恢复**:原会话仍完好时只移除回收标记,不重写持久层;原件丢失时才使用已验证快照和官方写入能力回退恢复,且绝不覆盖同 ID 会话。
|
|
68
|
+
- **明确的永久删除**:仅回收站提供永久删除与清空。插件先写入 `purge-pending` 崩溃恢复意图,再删除原会话和保护快照;中途失败会在下次启动重试。
|
|
62
69
|
- 适配浅色/深色主题,支持中文和英文界面。
|
|
63
70
|
|
|
71
|
+
## 回收站、隐私与附件限制
|
|
72
|
+
|
|
73
|
+
回收目录 `trash.json` 与保护快照位于 `$DSH_HOME/plugin-data/archived-chats/`,全部只保存在本机。快照会逐个读取附件、校验摘要并使用原子发布;不会上传会话或附件。回收站中的预览也使用单独授权范围。
|
|
74
|
+
|
|
75
|
+
永久删除会删掉该会话的快照附件副本,但 Harness 全局附件存储可能仍因其他会话引用或宿主垃圾回收策略保留相同字节;本插件不声称会立即清理宿主的全局附件库。
|
|
76
|
+
|
|
64
77
|
## 标签、备注与统计
|
|
65
78
|
|
|
66
79
|
标签和备注**只保存在本机**的 `$DSH_HOME/plugin-data/archived-chats/metadata.json` 中——不会被上传、同步或发送到任何其他地方。取消归档会保留元数据;物理删除完成后会移除它,而延后或失败的删除会保留它。元数据与统计失败永远不阻塞:即使元数据存储无法读取或某个会话目录无法统计,列表、取消归档和删除仍然可用。
|
|
@@ -107,15 +120,15 @@ JSON 会保留附件引用,但**本版不复制附件二进制,也不包含
|
|
|
107
120
|
</details>
|
|
108
121
|
|
|
109
122
|
<details>
|
|
110
|
-
<summary><b
|
|
123
|
+
<summary><b>移入回收站后可以马上恢复吗?</b></summary>
|
|
111
124
|
|
|
112
|
-
|
|
125
|
+
可以。完成移入后的提示会提供 **撤销**,回收站中也可随时恢复。运行中会话会先按宿主生命周期安全停用或停放,然后才提交回收记录;如果宿主能力不足,操作会明确失败并保留归档会话。
|
|
113
126
|
|
|
114
127
|
</details>
|
|
115
128
|
|
|
116
129
|
## 实现概览
|
|
117
130
|
|
|
118
|
-
插件由两部分组成:Host
|
|
131
|
+
插件由两部分组成:Host 服务层负责读取本地归档、快照、回收目录和恢复/清除事务,浏览器设置页负责搜索、预览、备份、恢复与明确确认。所有修改都通过受保护的本地路由完成;普通移除只提交回收记录,物理清除仅由回收站的崩溃安全 purge 流程触发。
|
|
119
132
|
|
|
120
133
|
普通用户需要了解的数据保存、备份限制、删除结果和兼容性说明已列在本 README 中。路由清单、数据流、恢复事务、实时删除生命周期和失败回退等维护者细节请参阅 [架构文档](docs/ARCHITECTURE.md)。
|
|
121
134
|
|
|
@@ -125,10 +138,25 @@ JSON 会保留附件引用,但**本版不复制附件二进制,也不包含
|
|
|
125
138
|
npm test
|
|
126
139
|
```
|
|
127
140
|
|
|
128
|
-
测试套件(`test/*.test.mjs`)覆盖导出记录与真实 ZIP
|
|
141
|
+
测试套件(`test/*.test.mjs`)覆盖导出记录与真实 ZIP 解包、有界导入校验、恢复事务、元数据存储、统计服务、全文搜索、对话预览,以及宿主+浏览器冒烟测试。测试使用隔离的临时 DSH 主目录和模拟运行时,不会读取或修改真实会话。
|
|
129
142
|
|
|
130
143
|
## 版本更新记录
|
|
131
144
|
|
|
145
|
+
### 0.11.0
|
|
146
|
+
|
|
147
|
+
- 新增 **归档 / 回收站** 双标签、独立批量选择、回收站范围预览、恢复、永久删除和清空。
|
|
148
|
+
- 普通删除改为创建完整本地保护快照并移入回收站,成功后可立即 **撤销**。
|
|
149
|
+
- 恢复优先使用完好原会话;原件丢失时改用已校验的会话+附件快照,不覆盖同 ID 会话。
|
|
150
|
+
- 新增 `purge-pending` 崩溃恢复意图、快照恢复扫描、降级状态,并将旧版 `pending-deletions.json` 安全迁移为可恢复回收记录,不在启动时静默删除。
|
|
151
|
+
- **降级警告**:安装 0.11 后如回退到 0.10,旧版不会识别回收目录和保护快照;回退前请先在 0.11 恢复需要的会话并备份 `$DSH_HOME/plugin-data/archived-chats/`。
|
|
152
|
+
|
|
153
|
+
### 0.10.0
|
|
154
|
+
|
|
155
|
+
- 新增遵循 Harness 会话布局的归档对话预览:用户消息靠右,助手消息靠左,并支持分页与响应式轮次导航。
|
|
156
|
+
- Markdown、思考过程、工具活动、JSON、代码和可用的已存储图片均以只读方式呈现;宿主缺少附件能力时只影响图片,不影响其余对话内容。
|
|
157
|
+
- 新增归档聊天正文全文搜索:匹配 Unicode 文本和工具结果,在原有标题/标签/备注筛选上合并命中结果。
|
|
158
|
+
- 搜索与预览使用受保护的本地 POST 路由、有界请求、并发 4 的读取、部分失败降级和有上限的 TTL/LRU 内存缓存。
|
|
159
|
+
|
|
132
160
|
### 0.9.0
|
|
133
161
|
|
|
134
162
|
- 新增按需显示的批量选择模式:列表默认不展示复选框,点击入口后才显示,完成批量操作后自动退出。
|
|
@@ -189,7 +217,7 @@ npm test
|
|
|
189
217
|
dsh plugin --profile web remove dsh-archived-chats
|
|
190
218
|
```
|
|
191
219
|
|
|
192
|
-
|
|
220
|
+
卸载不会删除 `$DSH_HOME/plugin-data/archived-chats/` 中的 `metadata.json`、`trash.json`、保护快照或旧版 `pending-deletions.json`,也不会触发永久删除。这是故意的本地数据保护;请先恢复或备份需要的会话,再手动处理该目录。
|
|
193
221
|
|
|
194
222
|
## License
|
|
195
223
|
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Architecture and Maintainer Notes
|
|
2
|
+
|
|
3
|
+
[English](ARCHITECTURE.en.md) | [中文](ARCHITECTURE.md)
|
|
4
|
+
|
|
5
|
+
This document is for maintainers and developers who need to understand data behavior. End users should start with the repository README.md, whose installation, usage, privacy, and limitation notes take precedence.
|
|
6
|
+
|
|
7
|
+
## Architecture boundaries
|
|
8
|
+
|
|
9
|
+
The plugin has a Host service half and a browser client half:
|
|
10
|
+
|
|
11
|
+
- The Host service in lib/index.js runs inside the DSH Web host, reads the workspace registry and session persistence, and exposes local HTTP routes.
|
|
12
|
+
- The browser client in lib/client.js registers the Archived Chats settings.section and renders state and actions.
|
|
13
|
+
- Pure domain logic lives in lib/export.js, lib/import.js, lib/restore.js, lib/metadata.js, lib/search.js, and lib/stats.js. lib/trash.js owns the versioned recycle catalog, lib/snapshot.js owns verified protection snapshots, and lib/recycle.js composes move, restore, purge, empty, startup recovery, and legacy migration.
|
|
14
|
+
|
|
15
|
+
The browser never reads session files directly. All reads and writes go through Host routes.
|
|
16
|
+
|
|
17
|
+
## Host routes
|
|
18
|
+
|
|
19
|
+
Current routes:
|
|
20
|
+
|
|
21
|
+
~~~text
|
|
22
|
+
GET /plugins/dsh-archived-chats/state
|
|
23
|
+
GET /plugins/dsh-archived-chats/stats
|
|
24
|
+
POST /plugins/dsh-archived-chats/preview
|
|
25
|
+
POST /plugins/dsh-archived-chats/preview/image
|
|
26
|
+
POST /plugins/dsh-archived-chats/search
|
|
27
|
+
POST /plugins/dsh-archived-chats/export
|
|
28
|
+
POST /plugins/dsh-archived-chats/import/inspect
|
|
29
|
+
POST /plugins/dsh-archived-chats/import/restore
|
|
30
|
+
POST /plugins/dsh-archived-chats/metadata
|
|
31
|
+
GET /plugins/dsh-archived-chats/trash
|
|
32
|
+
POST /plugins/dsh-archived-chats/trash/restore
|
|
33
|
+
POST /plugins/dsh-archived-chats/trash/purge
|
|
34
|
+
POST /plugins/dsh-archived-chats/trash/empty
|
|
35
|
+
POST /plugins/dsh-archived-chats/unarchive
|
|
36
|
+
POST /plugins/dsh-archived-chats/unarchive-all
|
|
37
|
+
POST /plugins/dsh-archived-chats/delete
|
|
38
|
+
POST /plugins/dsh-archived-chats/delete-all
|
|
39
|
+
~~~
|
|
40
|
+
|
|
41
|
+
Every mutating route, plus the preview, preview/image, and search routes that return conversation content, requires the `x-dsh-archived-chats: 1` header. GET trash, preview/image, and export are read-only. `delete` / `delete-all` return `trashed` and `failed`; `trash/restore` returns `restored`; `trash/purge` / `trash/empty` return `purged`, preserving first-request order.
|
|
42
|
+
|
|
43
|
+
## State and local data
|
|
44
|
+
|
|
45
|
+
The state route joins archived sessions, workspace, tags, notes, and metadataUpdatedAt for the browser list. Tags and notes are stored only at:
|
|
46
|
+
|
|
47
|
+
~~~text
|
|
48
|
+
$DSH_HOME/plugin-data/archived-chats/metadata.json
|
|
49
|
+
$DSH_HOME/plugin-data/archived-chats/trash.json
|
|
50
|
+
$DSH_HOME/plugin-data/archived-chats/snapshots/
|
|
51
|
+
~~~
|
|
52
|
+
|
|
53
|
+
Metadata and recycle catalogs are versioned. Writes serialize and atomically replace their documents through temporary files. An unreadable or unsupported `trash.json` is preserved byte-for-byte, hides no archived sessions, and disables recycle mutations.
|
|
54
|
+
|
|
55
|
+
The stats route measures session directories with concurrency four, skips symbolic links, and caches results for 30 seconds. A measurement failure marks only that row unavailable; list and mutation actions continue. Delete invalidates the affected cache row.
|
|
56
|
+
|
|
57
|
+
## Preview and full-text search
|
|
58
|
+
|
|
59
|
+
Preview accepts visible archived IDs by default and only recycle-catalog IDs with explicit `scope: "trash"`; search remains archive-only. lib/search.js uses Harness append-origin message projection, so replacement copies are never indexed twice. User, assistant, reasoning, tool-call, and tool-result text is searchable, while preview returns bounded pages of structured segments and sanitized image descriptors.
|
|
60
|
+
|
|
61
|
+
The preview/image authorization sequence is fixed: first require POST and `x-dsh-archived-chats: 1`, then bounded-parse `sessionId` and `attachmentId`; next confirm that the session is still in the currently visible archive set, find an exact image-descriptor match in that session's canonical projection, and only then read bytes through the optional `attachments.readImage` service. Both preview and preview/image recheck visible archive state after asynchronous reads and immediately before sending a response, preventing an overlapping unarchive or delete from exposing stale content. Image bytes use `no-store` and `nosniff`; cross-session, non-archived, and unprojected references are rejected, and error responses never echo filesystem paths. A host without attachment-read capability returns `preview-image-unsupported`; this degrades images only and does not block text, Markdown, reasoning, tool, JSON, or code preview.
|
|
62
|
+
|
|
63
|
+
Cross-session persistence inspection is limited to four concurrent reads. A broken session is reported in `skipped` while other hits still succeed. Canonical projections use a 30-second TTL, a 64-session LRU, and a per-session cached-code-point cap; oversized sessions remain searchable but do not stay resident. Unarchive, delete, and restore invalidate affected cache entries.
|
|
64
|
+
|
|
65
|
+
## Export flow
|
|
66
|
+
|
|
67
|
+
The export route accepts a bounded native form request and export.js writes a versioned ZIP:
|
|
68
|
+
|
|
69
|
+
~~~text
|
|
70
|
+
manifest.json
|
|
71
|
+
sessions/001-safe-title-id/session.json
|
|
72
|
+
sessions/001-safe-title-id/transcript.md
|
|
73
|
+
~~~
|
|
74
|
+
|
|
75
|
+
session.json preserves the complete metadata and event values returned by persistence, plus archive title, workspace, timestamps, origin, tags, notes, and storage facts. transcript.md is produced with Harness's canonical message projection.
|
|
76
|
+
|
|
77
|
+
ZIP paths are sanitized and collision-safe. Batch export inspects and writes sessions sequentially, retaining at most one inspected payload. Attachment references can remain in JSON, but attachment bytes and descendant sessions are outside the version-one format.
|
|
78
|
+
|
|
79
|
+
## Import and restore flow
|
|
80
|
+
|
|
81
|
+
import/inspect accepts only version-one ZIPs produced by this plugin. Host validation is bounded and checks the manifest, paths, versions, session records, and cross-file consistency before returning a preview:
|
|
82
|
+
|
|
83
|
+
1. The browser uploads the ZIP and receives session summaries, versions, size, and warnings.
|
|
84
|
+
2. Existing session IDs are marked as conflicts and deselected by default.
|
|
85
|
+
3. Unresolved workspaces and attachment references are warnings, never invented data.
|
|
86
|
+
4. After confirmation, the browser submits a single-use token and selected non-conflicting IDs.
|
|
87
|
+
5. restore.js uses a feature-detected adapter to write sessions, metadata, and archive state.
|
|
88
|
+
6. Any failure rolls back staged data and never overwrites an existing session.
|
|
89
|
+
|
|
90
|
+
The confirmation token expires quickly and can be used once. Hosts without the supported writer capability return restore-unsupported without writing.
|
|
91
|
+
|
|
92
|
+
## Recycle and protection-snapshot lifecycle
|
|
93
|
+
|
|
94
|
+
`trash.json` permits only `trashed`, `purge-pending`, and `degraded`. Legal transitions are `missing -> trashed`, `trashed/degraded -> purge-pending`, and removal of an existing state after a committed transaction. A `purge-pending` record cannot restore.
|
|
95
|
+
|
|
96
|
+
Protection manifests use `dsh-archived-chats/snapshot` v1 and session payloads use `dsh-archived-chats/snapshot-session` v1. There is at most one active snapshot per session; replacement publishes the new snapshot before removing the old one. Exact limits are a 4 MiB manifest, 512 MiB session JSON, 10,000 attachments, 32 MiB per attachment, and 8 GiB total. Attachments are processed sequentially, and paths use generated UUIDs and digests rather than user data.
|
|
97
|
+
|
|
98
|
+
Move ordering is: validate archive ownership → dispose or park a live session → capture and verify snapshot → recheck ownership → atomically commit `trashed` → invalidate caches. Ordinary move never removes the persistence artifact.
|
|
99
|
+
|
|
100
|
+
Restore first rejects an existing-ID conflict. With an intact original it only restores archive visibility and removes the recycle record/snapshot, without rewriting persistence. With a missing original it completes all validation and attachment-identity republishing before writing only through public `create` / `append` / `saveImage` capabilities. A failure rolls back the new artifact and retains trash.
|
|
101
|
+
|
|
102
|
+
Permanent purge persists `purge-pending` before physical writes, then removes the original, snapshot, and recycle record in that order. Startup recovery retries only `purge-pending`, never plain `trashed`. Legacy `pending-deletions.json` is strict read-only migration input: each still-archived ID becomes recoverable trash and is never boot-deleted merely because of the old marker.
|
|
103
|
+
|
|
104
|
+
## Browser client
|
|
105
|
+
|
|
106
|
+
client.js registers an order-30 settings.section and uses the DSH rc.7 overlay, state, and design tokens. The page state includes:
|
|
107
|
+
|
|
108
|
+
- Archived sessions and workspace groups.
|
|
109
|
+
- Search, type/project/tag filters, and sorting.
|
|
110
|
+
- Tag and note editor.
|
|
111
|
+
- Selected-item export, unarchive, and move to Recycle Bin.
|
|
112
|
+
- Archived/Recycle Bin tabs with independent recycle selection, status, restore, permanent purge, and empty actions.
|
|
113
|
+
- Import preview, disabled conflicts, and restore results.
|
|
114
|
+
- Responsive settings-page markers and sidebar refresh injection.
|
|
115
|
+
|
|
116
|
+
The preview prefers Harness's publicly exported `MarkdownText`, `DisclosureRow`, and `JsonBlock`. When a public primitive is unavailable, only that content falls back to escaped plain text, native `details`/`summary`, or `pre`; the plugin never reaches into a private chat renderer. A tool result folds into an earlier call only when its `toolCallId` exactly matches the call's `callId`, consuming matches in chronological order. Unmatched results remain standalone, and errors use the semantic error token. Images are read from the protected route into Blob URLs, may load lazily before entering the viewport, and abort their read and call `URL.revokeObjectURL` when the preview closes or the image node unmounts.
|
|
117
|
+
|
|
118
|
+
The turn rail remains part of the preview: on desktop it stays to the left of the feed, jumps and follows feed scrolling, and exposes the active turn through `aria-current`; at 640px or narrower it moves above the feed and scrolls horizontally while user bubbles retain useful width. It is not replaced by a private host navigation component.
|
|
119
|
+
|
|
120
|
+
The browser never mutates files directly. After an operation, the Host response becomes the new list baseline. Closing a preview or switching to another session aborts the pending request, and a request sequence ignores late responses so a closed dialog cannot reopen and an older session cannot replace the newest preview.
|
|
121
|
+
|
|
122
|
+
## Security and failure policy
|
|
123
|
+
|
|
124
|
+
- All state-changing routes require POST and the guard header.
|
|
125
|
+
- Import limits ZIP size, entries, paths, versions, and JSON structure, rejecting traversal, duplicates, and prototype-pollution keys.
|
|
126
|
+
- Ordinary delete never invokes physical purge; only a committed recycle record can enter purge.
|
|
127
|
+
- Snapshot and recycle documents use `0600`, directories use `0700`, and publication is temporary write, sync, atomic rename.
|
|
128
|
+
- Purge removes snapshot attachment copies but does not promise immediate cleanup of identical bytes still retained by Harness's global attachment store.
|
|
129
|
+
- Unknown host capabilities must degrade or return a clear error; they must not be inferred.
|
|
130
|
+
|
|
131
|
+
## Compatibility and testing
|
|
132
|
+
|
|
133
|
+
The complete 0.11.0 target is DeepSeek Harness 0.1.1-rc.2; older hosts degrade through explicit capability errors. When host slots, design tokens, attachments, or session internals change, run the smoke suite first and then repeat a real-host check. Before downgrading to 0.10, restore needed sessions and back up plugin data because 0.10 does not understand recycle records or snapshots.
|
|
134
|
+
|
|
135
|
+
Coverage includes:
|
|
136
|
+
|
|
137
|
+
- export.js records, transcripts, and ZIP streaming.
|
|
138
|
+
- import.js bounded validation and unsafe-path rejection.
|
|
139
|
+
- restore.js transactional commit, rollback, and unsupported capabilities.
|
|
140
|
+
- metadata.js versioning, concurrency, and atomic writes.
|
|
141
|
+
- stats.js symlink handling, caching, and concurrency limits.
|
|
142
|
+
- search.js message projection, Unicode search, pagination, partial failures, and TTL/LRU caching.
|
|
143
|
+
- trash.js, snapshot.js, and recycle.js format validation, concurrency, recovery, rollback, crash intent, and legacy migration.
|
|
144
|
+
- Host routes and browser settings smoke/responsive behavior.
|
|
145
|
+
|
|
146
|
+
Run:
|
|
147
|
+
|
|
148
|
+
~~~sh
|
|
149
|
+
npm test
|
|
150
|
+
npm pack --dry-run --json
|
|
151
|
+
~~~
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# 架构与维护者说明
|
|
2
|
+
|
|
3
|
+
[English](ARCHITECTURE.en.md) | 中文
|
|
4
|
+
|
|
5
|
+
本文面向维护者和需要理解数据行为的开发者。普通用户请先阅读仓库根目录的 README.md;其中的安装、使用、隐私和限制说明优先于本文。
|
|
6
|
+
|
|
7
|
+
## 架构边界
|
|
8
|
+
|
|
9
|
+
插件由 Host 服务层和浏览器客户端两部分组成:
|
|
10
|
+
|
|
11
|
+
- Host 服务层位于 lib/index.js,运行在 DSH Web 宿主中,读取工作区注册表和会话持久层,并提供本地 HTTP 路由。
|
|
12
|
+
- 浏览器客户端位于 lib/client.js,通过 settings.section 注册「已归档的聊天」设置页,负责展示状态和发起操作。
|
|
13
|
+
- 纯领域逻辑拆分在 lib/export.js、lib/import.js、lib/restore.js、lib/metadata.js、lib/search.js 和 lib/stats.js 中。lib/trash.js 负责版本化回收目录,lib/snapshot.js 负责可验证保护快照,lib/recycle.js 组合移入、恢复、永久删除、清空、启动恢复与旧数据迁移。
|
|
14
|
+
|
|
15
|
+
浏览器不直接访问会话文件。所有读取和写入都经 Host 路由完成。
|
|
16
|
+
|
|
17
|
+
## Host 路由
|
|
18
|
+
|
|
19
|
+
当前注册的路由:
|
|
20
|
+
|
|
21
|
+
~~~text
|
|
22
|
+
GET /plugins/dsh-archived-chats/state
|
|
23
|
+
GET /plugins/dsh-archived-chats/stats
|
|
24
|
+
POST /plugins/dsh-archived-chats/preview
|
|
25
|
+
POST /plugins/dsh-archived-chats/preview/image
|
|
26
|
+
POST /plugins/dsh-archived-chats/search
|
|
27
|
+
POST /plugins/dsh-archived-chats/export
|
|
28
|
+
POST /plugins/dsh-archived-chats/import/inspect
|
|
29
|
+
POST /plugins/dsh-archived-chats/import/restore
|
|
30
|
+
POST /plugins/dsh-archived-chats/metadata
|
|
31
|
+
GET /plugins/dsh-archived-chats/trash
|
|
32
|
+
POST /plugins/dsh-archived-chats/trash/restore
|
|
33
|
+
POST /plugins/dsh-archived-chats/trash/purge
|
|
34
|
+
POST /plugins/dsh-archived-chats/trash/empty
|
|
35
|
+
POST /plugins/dsh-archived-chats/unarchive
|
|
36
|
+
POST /plugins/dsh-archived-chats/unarchive-all
|
|
37
|
+
POST /plugins/dsh-archived-chats/delete
|
|
38
|
+
POST /plugins/dsh-archived-chats/delete-all
|
|
39
|
+
~~~
|
|
40
|
+
|
|
41
|
+
所有修改路由以及会返回对话内容的 preview、preview/image、search 路由都要求 `x-dsh-archived-chats: 1` 请求头。GET trash、preview/image 和 export 是只读操作。`delete` / `delete-all` 只返回 `trashed` 和 `failed`;`trash/restore` 返回 `restored`,`trash/purge` / `trash/empty` 返回 `purged`,并保留请求的首次出现顺序。
|
|
42
|
+
|
|
43
|
+
## 状态和本地数据
|
|
44
|
+
|
|
45
|
+
state 路由把归档会话、工作区、标签、备注和 metadataUpdatedAt 组合成浏览器列表。标签和备注只写入:
|
|
46
|
+
|
|
47
|
+
~~~text
|
|
48
|
+
$DSH_HOME/plugin-data/archived-chats/metadata.json
|
|
49
|
+
$DSH_HOME/plugin-data/archived-chats/trash.json
|
|
50
|
+
$DSH_HOME/plugin-data/archived-chats/snapshots/
|
|
51
|
+
~~~
|
|
52
|
+
|
|
53
|
+
元数据和回收目录均带版本号,写入通过队列串行化并用临时文件原子替换。无法解析或不支持的 `trash.json` 保留原始字节、不隐藏任何归档会话,并禁用回收修改。
|
|
54
|
+
|
|
55
|
+
stats 路由以并发 4 测量会话目录,跳过符号链接,结果缓存 30 秒。测量失败只标记当前行不可用,不阻塞列表和其他操作;删除会使对应缓存失效。
|
|
56
|
+
|
|
57
|
+
## 预览和全文搜索
|
|
58
|
+
|
|
59
|
+
preview 默认只接受当前可见归档 ID;显式 `scope: "trash"` 时仅接受回收目录中的 ID。search 只搜索可见归档。lib/search.js 使用 Harness 的 append-origin 消息投影,不会将 replacement 副本重复索引。用户、助手、思考、工具调用与工具结果均可搜索,预览窗口以分页方式返回有界段落和净化后的图片描述符。
|
|
60
|
+
|
|
61
|
+
preview/image 的授权顺序固定为:先验证 POST 和 `x-dsh-archived-chats: 1`,再有界解析 `sessionId` 与 `attachmentId`;随后确认会话仍在当前可见归档集合中,从该会话的规范投影中查找完全匹配的图片描述符,最后才通过可选的 `attachments.readImage` 服务读取。preview 和 preview/image 都会在异步读取完成后、响应发送前再次检查可见归档状态,避免并发取消归档或删除泄露旧内容。图片字节以 `no-store`、`nosniff` 返回;跨会话、非归档或不在投影中的引用均会被拒绝,错误响应不回显文件路径。宿主没有附件读取能力时返回 `preview-image-unsupported`;这只降级图片,不阻塞文本、Markdown、思考、工具、JSON 或代码预览。
|
|
62
|
+
|
|
63
|
+
跨会话搜索的持久层读取并发上限为 4;单个会话失败会记入 skipped,其他命中仍正常返回。规范投影使用 30 秒 TTL、64 会话 LRU 和单会话最大缓存字符数保护内存;超大会话仍可搜索,但不会常驻缓存。取消归档、删除和恢复会使相关缓存失效。
|
|
64
|
+
|
|
65
|
+
## 导出流程
|
|
66
|
+
|
|
67
|
+
export 路由接收有界的原生表单请求,由 export.js 生成版本化 ZIP:
|
|
68
|
+
|
|
69
|
+
~~~text
|
|
70
|
+
manifest.json
|
|
71
|
+
sessions/001-safe-title-id/session.json
|
|
72
|
+
sessions/001-safe-title-id/transcript.md
|
|
73
|
+
~~~
|
|
74
|
+
|
|
75
|
+
session.json 保留持久层返回的完整元数据和事件,并附加归档标题、工作区、时间、来源、标签、备注和存储信息。transcript.md 使用 Harness 的规范消息投影生成。
|
|
76
|
+
|
|
77
|
+
ZIP 路径会清理遍历字符并处理重名。批量导出按会话顺序逐个检查和写入,最多保留一个已检查的会话载荷。附件引用可保留在 JSON 中,但附件二进制和子会话不属于版本一格式。
|
|
78
|
+
|
|
79
|
+
## 导入和恢复流程
|
|
80
|
+
|
|
81
|
+
import/inspect 只接受本插件版本一导出的 ZIP。Host 会有界读取和校验 manifest、路径、版本、会话记录及跨文件一致性,然后生成预览:
|
|
82
|
+
|
|
83
|
+
1. 浏览器上传 ZIP,Host 返回会话摘要、版本、大小和警告。
|
|
84
|
+
2. 已存在的会话 ID 标记为冲突并默认取消选择。
|
|
85
|
+
3. 未解析的工作区和附件引用只显示警告,不伪造数据。
|
|
86
|
+
4. 用户确认后,浏览器提交一次性令牌和选中的非冲突 ID。
|
|
87
|
+
5. restore.js 通过能力探测的适配器写入会话、元数据和归档状态。
|
|
88
|
+
6. 任一步骤失败都回滚暂存数据,不覆盖已有会话。
|
|
89
|
+
|
|
90
|
+
确认令牌短期有效且只能使用一次。宿主不支持写入能力时返回 restore-unsupported,不执行任何写入。
|
|
91
|
+
|
|
92
|
+
## 回收与保护快照生命周期
|
|
93
|
+
|
|
94
|
+
`trash.json` 的合法状态只有 `trashed`、`purge-pending`、`degraded`。合法转换为 `missing -> trashed`、`trashed/degraded -> purge-pending`,以及任一现有状态在事务成功后移除。`purge-pending` 不得恢复。
|
|
95
|
+
|
|
96
|
+
保护快照格式是 `dsh-archived-chats/snapshot` v1,会话载荷是 `dsh-archived-chats/snapshot-session` v1。每个会话最多一个活跃快照;替换时先发布新快照,再删除旧快照。精确上限为:manifest 4 MiB、session JSON 512 MiB、10,000 个附件、单附件 32 MiB、总计 8 GiB。附件按顺序逐个读写,文件名使用 UUID 和摘要,而不是用户数据。
|
|
97
|
+
|
|
98
|
+
移入顺序为:校验归档所有权 → 处置/停放运行中会话 → 捕获并验证快照 → 再次校验所有权 → 原子写入 `trashed` 记录 → 使缓存失效。普通移入不删除持久层文件。
|
|
99
|
+
|
|
100
|
+
恢复先检查同 ID 冲突。原会话完好时只恢复归档可见性、移除回收记录和快照,不重写持久层;原件丢失时先完成所有校验和附件身份重发,然后仅通过公开 `create` / `append` / `saveImage` 能力写入。失败会回滚新建件并保留回收记录。
|
|
101
|
+
|
|
102
|
+
永久删除在任何物理写入前持久化 `purge-pending`,再删除原会话、快照和回收记录。启动恢复仅重试 `purge-pending`,从不删除普通 `trashed`。旧 `pending-deletions.json` 是严格、只读的迁移输入:每个仍归档的 ID 都转成可恢复回收记录,绝不因旧标记在启动时直接删除。
|
|
103
|
+
|
|
104
|
+
## 浏览器客户端
|
|
105
|
+
|
|
106
|
+
client.js 注册 order 30 的 settings.section,并使用 DSH rc.7 的浮层、状态和设计令牌。页面状态包括:
|
|
107
|
+
|
|
108
|
+
- 归档列表和工作区分组。
|
|
109
|
+
- 搜索、类型/项目/标签筛选和排序。
|
|
110
|
+
- 标签备注编辑器。
|
|
111
|
+
- 选中项批量导出、取消归档和移入回收站。
|
|
112
|
+
- 归档/回收站双标签,回收站独立选择、状态、恢复、永久删除和清空。
|
|
113
|
+
- 导入预览、冲突禁用和恢复结果。
|
|
114
|
+
- 响应式设置页标记和侧边栏刷新注入面。
|
|
115
|
+
|
|
116
|
+
预览优先使用 Harness 公开导出的 `MarkdownText`、`DisclosureRow` 和 `JsonBlock`;某个公开原语不可用时,只把对应内容降级为转义的纯文本、原生 `details`/`summary` 或 `pre`,不调用私有聊天渲染器。工具结果仅在其 `toolCallId` 与更早工具调用的 `callId` 精确匹配时折叠进该调用,匹配按时间顺序消费;未匹配结果保留为独立条目,错误状态使用语义错误令牌。图片由受保护路由读取为 Blob URL,离开视口前可按需加载,预览关闭或图片节点卸载时会中止读取并调用 `URL.revokeObjectURL`。
|
|
117
|
+
|
|
118
|
+
轮次轨道保留在预览内:桌面位于消息流左侧,跳转后随消息流滚动并用 `aria-current` 标出当前轮次;宽度不超过 640px 时轨道移到消息流上方并水平滚动,用户气泡仍保留可用宽度。轨道不会被替换为宿主私有导航组件。
|
|
119
|
+
|
|
120
|
+
浏览器操作不会直接改变本地文件;操作完成后以 Host 返回的状态作为新的列表基线。关闭预览或切换到另一条会话会取消未完成的预览请求;客户端同时使用请求序号忽略迟到响应,避免已关闭的弹窗重新出现或旧会话覆盖新会话。
|
|
121
|
+
|
|
122
|
+
## 安全和失败策略
|
|
123
|
+
|
|
124
|
+
- 所有状态变更路由都要求 POST 和 guard header。
|
|
125
|
+
- 导入限制 ZIP 大小、条目数量、路径格式、版本和 JSON 结构,拒绝遍历、重复和原型污染字段。
|
|
126
|
+
- 普通删除从不调用物理清除;仅已提交回收记录可进入 purge。
|
|
127
|
+
- 快照和回收文件使用 `0600`,目录使用 `0700`,发布为临时写入、sync、原子 rename。
|
|
128
|
+
- 物理 purge 删除快照副本,但不承诺立即清理 Harness 全局附件库中仍被其他会话引用的字节。
|
|
129
|
+
- 未知宿主能力必须降级或返回明确错误,不得猜测内部对象结构。
|
|
130
|
+
|
|
131
|
+
## 兼容性和测试
|
|
132
|
+
|
|
133
|
+
0.11.0 的完整能力目标是 DeepSeek Harness 0.1.1-rc.2;较旧宿主通过明确能力错误降级。宿主插槽、设计令牌、附件或会话内部接口变化时,应先运行冒烟测试,再做真实宿主检查。降级到 0.10 前必须先在 0.11 恢复所需会话并备份插件数据,因为 0.10 不识别回收目录和快照。
|
|
134
|
+
|
|
135
|
+
测试覆盖:
|
|
136
|
+
|
|
137
|
+
- export.js 的记录、转录和 ZIP 流。
|
|
138
|
+
- import.js 的有界校验和拒绝路径。
|
|
139
|
+
- restore.js 的事务提交、回滚和能力缺失。
|
|
140
|
+
- metadata.js 的版本、并发和原子写入。
|
|
141
|
+
- stats.js 的符号链接、缓存和并发限制。
|
|
142
|
+
- search.js 的消息投影、Unicode 搜索、分页、部分失败与 TTL/LRU 缓存。
|
|
143
|
+
- trash.js、snapshot.js 和 recycle.js 的格式验证、并发、恢复、回滚、崩溃意图和旧标记迁移。
|
|
144
|
+
- Host 路由和浏览器设置页的冒烟及响应式行为。
|
|
145
|
+
|
|
146
|
+
运行:
|
|
147
|
+
|
|
148
|
+
~~~sh
|
|
149
|
+
npm test
|
|
150
|
+
npm pack --dry-run --json
|
|
151
|
+
~~~
|