dsh-archived-chats 1.0.7 → 1.1.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.md CHANGED
@@ -54,6 +54,7 @@ dsh plugin --profile web update dsh-archived-chats
54
54
  | Area | What it provides |
55
55
  | --- | --- |
56
56
  | **Browse and search** | Workspace-grouped archive browsing, full-text search across messages and tool results, filters, sorting, tags, and notes. |
57
+ | **Archive a workspace** | From **Settings → Session Archive**, choose a workspace and open one confirmation for every eligible chat in it. Empty new-session windows are excluded; the workspace and its directory stay unchanged. |
57
58
  | **Read-only preview** | Native conversation layout for Markdown, reasoning, tool activity, JSON, code, and available stored images, with responsive turn navigation. |
58
59
  | **Local History** | Validated versions captured after archive, read-only snapshot preview, confirmed deletion, clear-history, and **Restore as copy** without overwriting the source. |
59
60
  | **Backup and restore** | JSON + Markdown ZIP export with preview-first, conflict-safe import. Existing session IDs are never overwritten. |
@@ -66,6 +67,7 @@ dsh plugin --profile web update dsh-archived-chats
66
67
  - **No silent overwrite:** imports and History restores create or select non-conflicting IDs; they never replace an existing session.
67
68
  - **Deletion stays explicit:** ordinary removal enters the Recycle Bin after snapshot protection. Physical removal is available only through confirmed permanent-purge actions.
68
69
  - **No automatic cleanup:** retention policies are saved separately from execution. Every cleanup starts with a short-lived preview and explicit selection.
70
+ - **Confirmed workspace archive:** the plugin prepares the exact set in the background, then shows one confirmation with the workspace and chat count. Only sessions whose inspected log contains a real `turn/start` are eligible; empty new-session windows and sessions whose content cannot be confirmed are skipped. Its five-minute, single-use credential excludes chats added later; a running chat is skipped, never stopped or moved.
69
71
  - **Backup scope is visible:** ZIP exports preserve complete session JSON and readable Markdown, but do not include attachment bytes or descendant sessions.
70
72
 
71
73
  ## Compatibility
@@ -75,7 +77,9 @@ Features activate from the public capabilities exposed by the DeepSeek Harness H
75
77
  | Host capability | Plugin behavior |
76
78
  | --- | --- |
77
79
  | Archive and session reads | Browsing, search, preview, History inventory, storage accounting, and lineage. |
80
+ | `settings.section` + public `archiveSession` | The plugin-owned settings page provides the workspace chooser and one-confirmation archive flow without requiring a workspace-menu extension slot. Without archive capability, preparation returns `workspace-archive-unsupported` and makes no change. |
78
81
  | Attachment reads | Stored images appear in conversation and snapshot previews; without it, text remains readable. |
82
+ | Session-scoped log location | Recycle Bin permanent deletion uses the persistence provider's public `locate(meta)` capability. Providers without a session-scoped location remain unsupported; failed operations retain their rows and display the reason. |
79
83
  | Public session writer | ZIP import, **Restore as copy**, and snapshot fallback when an original is missing all write through the Host's public `create` / `append` / `locate` capability, or a dedicated restore entry point where one exists. |
80
84
  | Missing write capability | The operation returns `restore-unsupported` without writing or overwriting data. |
81
85
 
@@ -116,7 +120,7 @@ The eight fixed screenshots below come from an isolated Simplified Chinese light
116
120
  | Architecture | [Maintainer architecture](docs/ARCHITECTURE.en.md) | [维护者架构](docs/ARCHITECTURE.md) |
117
121
  | Release history | [GitHub Releases](https://github.com/Ultronen/dsh-archived-chats/releases) | [GitHub Releases](https://github.com/Ultronen/dsh-archived-chats/releases) |
118
122
 
119
- See also [Support](SUPPORT.md), [Security](SECURITY.md), [Contributing](CONTRIBUTING.md), [Code of Conduct](CODE_OF_CONDUCT.md), and [Discussions](https://github.com/Ultronen/dsh-archived-chats/discussions).
123
+ See also [Support](SUPPORT.md), [Security](SECURITY.md), [Contributing](CONTRIBUTING.md), [Code of Conduct](CODE_OF_CONDUCT.md), and [Discussions](https://github.com/Ultronen/dsh-archived-chats/discussions). Before claiming work or opening a pull request, contributors must read the [Contributing Guide](CONTRIBUTING.md) in full.
120
124
 
121
125
  ## Project status
122
126
 
package/README.zh-CN.md CHANGED
@@ -54,6 +54,7 @@ dsh plugin --profile web update dsh-archived-chats
54
54
  | 范围 | 提供的能力 |
55
55
  | --- | --- |
56
56
  | **浏览与搜索** | 按工作区浏览归档聊天,全文搜索消息和工具结果,并支持筛选、排序、标签与备注。 |
57
+ | **归档整个工作区** | 从 **设置 → 会话档案** 打开工作区选择器,再用一次确认归档其中全部符合条件的聊天;空白的新会话窗口不会计入,工作区及其目录保持不变。 |
57
58
  | **原生只读预览** | 以原生对话布局展示 Markdown、思考过程、工具活动、JSON、代码和可用的已存储图片,并提供响应式轮次导航。 |
58
59
  | **本地历史版本** | 归档后保存已验证版本,支持只读快照预览、确认删除、清空历史,以及不覆盖来源的 **恢复为副本**。 |
59
60
  | **备份与恢复** | 导出 JSON + Markdown ZIP,并通过预览优先、冲突安全的流程导入;已有会话 ID 永不覆盖。 |
@@ -66,6 +67,7 @@ dsh plugin --profile web update dsh-archived-chats
66
67
  - **不静默覆盖:** 导入和历史恢复只创建或选择无冲突 ID,绝不覆盖已有会话。
67
68
  - **删除必须明确:** 普通移除会在快照保护后进入回收站;只有经过确认的永久删除操作才会物理清除。
68
69
  - **不自动清理:** 保留策略的保存与执行分离,每次清理都从短效预览和明确选择开始。
70
+ - **确认工作区归档:** 插件在后台准备精确集合,再显示一次包含工作区与会话数量的确认。只有检查到真实 `turn/start` 的会话才符合条件;空白的新会话窗口及无法确认内容的会话都会跳过。5 分钟有效、只能使用一次的凭据不会纳入之后新建的聊天;运行中的聊天只会跳过,绝不停止或移动。
69
71
  - **备份范围清楚:** ZIP 保留完整会话 JSON 和可读 Markdown,但不包含附件二进制或后代会话。
70
72
 
71
73
  ## 兼容性
@@ -75,7 +77,9 @@ dsh plugin --profile web update dsh-archived-chats
75
77
  | Host 能力 | 插件行为 |
76
78
  | --- | --- |
77
79
  | 归档与会话读取 | 浏览、搜索、预览、历史清单、空间分账和会话血缘。 |
80
+ | `settings.section` + 公开 `archiveSession` | 插件自己的设置页提供工作区选择器和一次确认归档流程,不依赖工作区菜单扩展 slot;缺少归档能力时,准备请求返回 `workspace-archive-unsupported`,且不作任何修改。 |
78
81
  | 附件读取 | 对话和快照预览可显示已存储图片;缺少时文本内容仍可阅读。 |
82
+ | 会话独立日志位置 | 回收站永久删除使用持久化后端公开的 `locate(meta)` 能力。后端不提供会话独立位置时不支持永久删除;失败的条目保留在列表中并显示具体原因。 |
79
83
  | 公开会话 writer | ZIP 导入、**恢复为副本** 和原件丢失时的快照回退,都通过 Host 公开的 `create` / `append` / `locate` 能力写入;Host 提供专用恢复入口时优先使用。 |
80
84
  | 缺少写入能力 | 操作返回 `restore-unsupported`,不会写入或覆盖数据。 |
81
85
 
@@ -116,7 +120,7 @@ dsh plugin --profile web update dsh-archived-chats
116
120
  | 架构说明 | [Maintainer architecture](docs/ARCHITECTURE.en.md) | [维护者架构](docs/ARCHITECTURE.md) |
117
121
  | 版本历史 | [GitHub Releases](https://github.com/Ultronen/dsh-archived-chats/releases) | [GitHub Releases](https://github.com/Ultronen/dsh-archived-chats/releases) |
118
122
 
119
- 另见 [支持说明](SUPPORT.md)、[安全说明](SECURITY.md)、[贡献指南](CONTRIBUTING.md)、[行为准则](CODE_OF_CONDUCT.md)和[问题交流](https://github.com/Ultronen/dsh-archived-chats/discussions)。
123
+ 另见 [支持说明](SUPPORT.md)、[安全说明](SECURITY.md)、[贡献指南](CONTRIBUTING.md)、[行为准则](CODE_OF_CONDUCT.md)和[问题交流](https://github.com/Ultronen/dsh-archived-chats/discussions)。认领任务或提交 Pull Request 前,贡献者务必完整阅读[贡献指南](CONTRIBUTING.md)。
120
124
 
121
125
  ## 项目状态
122
126
 
@@ -10,7 +10,7 @@ The plugin has a Host service half and a browser client half:
10
10
 
11
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
12
  - The browser client in lib/client.js registers the Session Archive 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, lib/stats.js, lib/insights.js, lib/retention.js, lib/retention-service.js, and lib/lineage.js. lib/history.js owns capture, safe inventory, and preview authorization; lib/history-restore.js owns single-use restore-as-copy transactions. lib/trash.js owns the recycle catalog, lib/snapshot.js owns verified snapshots, and lib/recycle.js composes recycle lifecycle operations.
13
+ - Pure domain logic lives in lib/export.js, lib/import.js, lib/restore.js, lib/metadata.js, lib/search.js, lib/stats.js, lib/insights.js, lib/retention.js, lib/retention-service.js, lib/lineage.js, and lib/workspace-bulk-archive.js. lib/persistence-compat.js narrows the current Host's handle-based reads to a private read-only view; lib/history.js owns capture, safe inventory, and preview authorization; lib/history-restore.js owns single-use restore-as-copy transactions. lib/trash.js owns the recycle catalog, lib/snapshot.js owns verified snapshots, and lib/recycle.js composes recycle lifecycle operations.
14
14
 
15
15
  The browser never reads session files directly. All reads and writes go through Host routes.
16
16
 
@@ -22,6 +22,9 @@ Current routes:
22
22
  GET /plugins/dsh-archived-chats/state
23
23
  GET /plugins/dsh-archived-chats/stats
24
24
  GET /plugins/dsh-archived-chats/insights
25
+ GET /plugins/dsh-archived-chats/workspace-archive/workspaces
26
+ POST /plugins/dsh-archived-chats/workspace-archive/preview
27
+ POST /plugins/dsh-archived-chats/workspace-archive/apply
25
28
  POST /plugins/dsh-archived-chats/retention/policy
26
29
  POST /plugins/dsh-archived-chats/retention/preview
27
30
  POST /plugins/dsh-archived-chats/retention/apply
@@ -53,6 +56,8 @@ POST /plugins/dsh-archived-chats/delete-all
53
56
 
54
57
  Every mutating route, plus preview, preview/image, search, history/preview, and history/preview/image, requires the `x-dsh-archived-chats: 1` header. `GET /history` returns only bounded safe inventory. History images require both the snapshot identity and the complete projected descriptor to match.
55
58
 
59
+ Workspace archive lists only safe workspace summaries. Preview accepts exactly one workspace ID and creates a five-minute, single-use token/nonce for at most 2,000 ordered eligible IDs. Apply accepts only that token and nonce, never caller-selected IDs. Eligibility requires unarchived membership in the existing workspace, no non-idle Host agent, and an inspected live or persisted event log containing `turn/start`; an empty new-session window is classified as `session-empty`, while an inspection failure is `session-unavailable`, and both fail closed. On Hosts without agent status, a loaded session is conservatively considered live. Candidate inspection is bounded to eight concurrent reads. Each item revalidates membership, archive state, agent status, and conversation content inside the shared lifecycle queue, then invokes the public `workspaceRegistry.archiveSession()` receiver-bound to the registry. New chats after preview are excluded; an item that became live, empty, unavailable, archived, or detached is reported and skipped. Successful archives attempt History capture under the held lifecycle lock; a capture failure is reported without rolling back the archive, and later items continue. This feature changes neither workspace membership nor a workspace path or directory, and never moves a chat across workspaces. A Host without public `archiveSession` returns `workspace-archive-unsupported` without mutation.
60
+
56
61
  ## State and local data
57
62
 
58
63
  The state route joins archived sessions, workspace, tags, notes, and metadataUpdatedAt for the browser list. Tags and notes are stored only at:
@@ -129,9 +134,10 @@ Permanent purge persists `purge-pending` before physical writes, then removes ev
129
134
 
130
135
  ## Browser client
131
136
 
132
- client.js registers an order-30 settings.section and uses the public Harness overlay, state, and design tokens. The page state includes:
137
+ client.js registers an order-30 `settings.section` plus a `shell.overlay`, and uses public Host archive services and design tokens. Workspace archive UI state stays inside the plugin-owned settings section and requires no workspace-action slot or shared client store. The page state includes:
133
138
 
134
139
  - A frame-wide archive success notice in `shell.overlay`: during its effect lifetime the plugin wraps public `workspaces.archiveSession` and starts history capture only after the original succeeds. Capture pauses the three-second dismissal; success resumes it, while failure retains retry-save without rolling back archive. View and Undo remain available.
140
+ - An **Archive workspace chats** action in **Settings → Session Archive**: it opens a workspace chooser, then prepares the selected workspace and shows one confirmation with the exact eligible count, Archived destination, and only a nonzero live-skip count, with no visible session preview. Full success refreshes consumers and closes; any skipped, failed, or snapshot-failed outcome retains the per-item result until dismissed.
135
141
  - Archived sessions and workspace groups.
136
142
  - Search, type/project/tag filters, and sorting.
137
143
  - Tag and note editor.
@@ -140,6 +146,8 @@ client.js registers an order-30 settings.section and uses the public Harness ove
140
146
  - Import preview, disabled conflicts, and restore results.
141
147
  - Responsive settings-page markers and sidebar refresh injection.
142
148
 
149
+ When `MenuAction`, `defineStore`, and `sidebar.workspaces.workspace.action` are available, one handle declared per plugin apply is shared by the workspace action, `shell.overlay`, and `settings.section`. The Host menu owner closes its menu before invoking the contributed callback and supplies `restoreFocus`; the plugin keeps that callback only in its apply closure, while components receive actions and selector hooks from the slot renderer. Contributor disposal is the plugin's responsibility: apply cleanup marks the contribution disposed synchronously and the deferred callback checks that guard before retaining focus or opening state. The Host separately owns cancellation when its row/browser unmounts. Missing optional support omits only this action and dialog.
150
+
143
151
  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.
144
152
 
145
153
  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.
@@ -150,6 +158,7 @@ The browser never mutates files directly. After an operation, the Host response
150
158
 
151
159
  - All state-changing routes require POST and the guard header.
152
160
  - History responses exclude workspace/snapshot/attachment paths, raw events, notes, and confirmation tokens; logs contain only IDs and stable codes.
161
+ - Workspace archive responses expose only safe workspace IDs/titles, eligible counts, session IDs/titles/timestamps in a confirmed preview, and stable per-item outcomes; workspace paths, event bodies, notes, attachment paths, and confirmation credentials stay out of logs and rendered results.
153
162
  - Import limits ZIP size, entries, paths, versions, and JSON structure, rejecting traversal, duplicates, and prototype-pollution keys.
154
163
  - Ordinary delete never invokes physical purge; only a committed recycle record can enter purge.
155
164
  - Snapshot and recycle documents use `0600`, directories use `0700`, and snapshot files are reopened with write access before sync; publication remains temporary write, sync, atomic rename with matching durability semantics on Windows, macOS, and Linux.
@@ -158,7 +167,11 @@ The browser never mutates files directly. After an operation, the Host response
158
167
 
159
168
  ## Compatibility and testing
160
169
 
161
- The plugin adapts through Host capability detection: archive reads, attachment reads, persistence writes, and live-session lifecycle support are evaluated independently, and missing capabilities must degrade safely or return explicit errors. Import, History restore-as-copy, and snapshot fallback when the original is missing all write through the public `create` / `append` / `locate` capability, or a dedicated restore entry point where the Host offers one; only a Host exposing neither returns `restore-unsupported` without mutation. A capability set that no shipped Host satisfies is not an acceptable guard — it makes the feature permanently dead rather than gracefully degraded. Back up the complete plugin-data directory before downgrading to a release that does not display History or understand recycle snapshots.
170
+ The plugin adapts through Host capability detection: archive reads, attachment reads, persistence writes, physical location, and live-session lifecycle support are evaluated independently, and missing capabilities must degrade safely or return explicit errors. A legacy persistence object with native `inspect` is retained unchanged. The current Host's `list()` snapshots and `open(id, 'read')` handles are adapted into a private `list` / `listSnapshots` / `inspect` read view; every inspection reads from offset 0 and closes its handle after success or failure. The view does not invent `create`, `append`, or `locate`. Ordinary sessions remain browsable, exportable, and snapshot-capable, while session-directory accounting, restore writes, and permanent purge are unavailable through this read-only view. Without physical location, purge returns `purge-unsupported` before changing recycle state, snapshots, pending markers, or live sessions.
171
+
172
+ Snapshot and ZIP schema v1 cannot preserve `inheritedEventCount`. When a current read handle reports an inherited prefix greater than zero, the adapter returns `session-inspection-unsupported` before reading events and still closes the handle, avoiding a silently flattened branch history. Workspace archive itself retains the Host's successful result, while the associated History capture reports its existing per-item snapshot failure. This UI change does not widen the snapshot or restore protocol.
173
+
174
+ On Hosts with the compatible legacy writer surface, Import, History restore-as-copy, and snapshot fallback when the original is missing still write through the public `create` / `append` / `locate` capability, or a dedicated restore entry point where the Host offers one; only a Host exposing neither returns `restore-unsupported` without mutation. A capability set that no shipped Host satisfies is not an acceptable guard — it makes the feature permanently dead rather than gracefully degraded. Back up the complete plugin-data directory before downgrading to a release that does not display History or understand recycle snapshots.
162
175
 
163
176
  Coverage includes:
164
177
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  - Host 服务层位于 lib/index.js,运行在 DSH Web 宿主中,读取工作区注册表和会话持久层,并提供本地 HTTP 路由。
12
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/insights.js、lib/retention.js、lib/retention-service.js 和 lib/lineage.js 中。lib/history.js 负责历史抓取、安全清单和预览授权,lib/history-restore.js 负责单次确认的恢复为副本事务。lib/trash.js 负责版本化回收目录,lib/snapshot.js 负责可验证快照,lib/recycle.js 组合回收生命周期。
13
+ - 纯领域逻辑拆分在 lib/export.js、lib/import.js、lib/restore.js、lib/metadata.js、lib/search.js、lib/stats.js、lib/insights.js、lib/retention.js、lib/retention-service.js、lib/lineage.js 和 lib/workspace-bulk-archive.js 中。lib/persistence-compat.js 将新版 Host 的句柄读取面收敛为插件内部只读视图;lib/history.js 负责历史抓取、安全清单和预览授权,lib/history-restore.js 负责单次确认的恢复为副本事务。lib/trash.js 负责版本化回收目录,lib/snapshot.js 负责可验证快照,lib/recycle.js 组合回收生命周期。
14
14
 
15
15
  浏览器不直接访问会话文件。所有读取和写入都经 Host 路由完成。
16
16
 
@@ -22,6 +22,9 @@
22
22
  GET /plugins/dsh-archived-chats/state
23
23
  GET /plugins/dsh-archived-chats/stats
24
24
  GET /plugins/dsh-archived-chats/insights
25
+ GET /plugins/dsh-archived-chats/workspace-archive/workspaces
26
+ POST /plugins/dsh-archived-chats/workspace-archive/preview
27
+ POST /plugins/dsh-archived-chats/workspace-archive/apply
25
28
  POST /plugins/dsh-archived-chats/retention/policy
26
29
  POST /plugins/dsh-archived-chats/retention/preview
27
30
  POST /plugins/dsh-archived-chats/retention/apply
@@ -53,6 +56,8 @@ POST /plugins/dsh-archived-chats/delete-all
53
56
 
54
57
  所有修改路由以及会返回对话内容的 preview、preview/image、search、history/preview 和 history/preview/image 路由都要求 `x-dsh-archived-chats: 1` 请求头。`GET /history` 只返回有界安全清单;历史图片只在快照身份与完整描述符同时匹配时返回。
55
58
 
59
+ 工作区归档只列出安全的工作区摘要。preview 只接受一个工作区 ID,并为最多 2,000 条符合条件且有序的会话 ID 签发 5 分钟有效、只能使用一次的 token/nonce;apply 只接受该 token 和 nonce,绝不接受调用方指定的会话 ID。符合条件要求会话仍属于现有工作区、尚未归档、没有非空闲 Host agent,并且检查到活动或持久化事件日志包含 `turn/start`;空白的新会话窗口标记为 `session-empty`,检查失败标记为 `session-unavailable`,两者都会保守跳过。Host 不提供 agent 状态时,已加载会话会被保守视为活动;候选内容检查最多并发读取 8 条。每一项都会在共享生命周期队列内重新检查归属、归档状态、agent 状态和对话内容,然后以 registry 为 receiver 调用公开的 `workspaceRegistry.archiveSession()`。预览后新增的聊天不会纳入;已变为运行中、空白、不可确认、已归档或脱离工作区的项目会明确跳过。成功归档后会在持有生命周期锁时尝试保存历史版本;抓取失败会报告但不撤销归档,后续项目仍继续。此功能不改变工作区成员关系、路径或目录,也不会在工作区之间移动聊天。Host 没有公开 `archiveSession` 时返回 `workspace-archive-unsupported`,且不作修改。
60
+
56
61
  ## 状态和本地数据
57
62
 
58
63
  state 路由把归档会话、工作区、标签、备注和 metadataUpdatedAt 组合成浏览器列表。标签和备注只写入:
@@ -129,9 +134,10 @@ import/inspect 只接受本插件版本一导出的 ZIP。Host 以有界压缩
129
134
 
130
135
  ## 浏览器客户端
131
136
 
132
- client.js 注册 order 30 的 settings.section,并使用 Harness 公开的浮层、状态和设计令牌。页面状态包括:
137
+ client.js 注册 order 30 的 `settings.section` 与 `shell.overlay`,并使用 Host 公开的归档服务和设计令牌。工作区归档 UI 状态保存在插件自己的设置区内,不依赖工作区操作 slot 或共享客户端 store。页面状态包括:
133
138
 
134
139
  - `shell.overlay` 中的归档成功提示:插件在 effect 生命周期内包装公开的 `workspaces.archiveSession`,只在原调用成功后发起历史抓取。抓取进行时暂停 3 秒关闭计时,成功后恢复,失败时显示不回滚归档的重试保存;查看与撤销继续可用。
140
+ - **设置 → 会话档案** 中的 **批量归档工作区** 操作:先打开工作区选择器,再准备所选工作区并显示一次包含精确符合条件数量、已归档去向和仅在非零时出现的活动会话跳过数量的确认,不显示会话预览。全部成功后刷新消费者并关闭;存在跳过、失败或快照失败时保留逐项结果直至关闭。
135
141
  - 归档列表和工作区分组。
136
142
  - 搜索、类型/项目/标签筛选和排序。
137
143
  - 标签备注编辑器。
@@ -140,6 +146,8 @@ client.js 注册 order 30 的 settings.section,并使用 Harness 公开的浮
140
146
  - 导入预览、冲突禁用和恢复结果。
141
147
  - 响应式设置页标记和侧边栏刷新注入面。
142
148
 
149
+ 当 `MenuAction`、`defineStore` 和 `sidebar.workspaces.workspace.action` 可用时,每次插件 apply 声明一个 handle,并由工作区操作、`shell.overlay` 与 `settings.section` 共用。Host 菜单所有者先关闭菜单,再调用贡献者回调并提供 `restoreFocus`;插件只在 apply 闭包中保存该回调,组件则从 slot 渲染器接收 actions 与 selector hook。贡献者卸载由插件负责:apply 清理会同步标记已卸载,延迟回调必须先检查该 guard,随后才能保存焦点或打开状态。Host 另行负责工作区行或浏览器卸载时的取消。缺少这些可选能力时只省略这个操作与对话框。
150
+
143
151
  预览优先使用 Harness 公开导出的 `MarkdownText`、`DisclosureRow` 和 `JsonBlock`;某个公开原语不可用时,只把对应内容降级为转义的纯文本、原生 `details`/`summary` 或 `pre`,不调用私有聊天渲染器。工具结果仅在其 `toolCallId` 与更早工具调用的 `callId` 精确匹配时折叠进该调用,匹配按时间顺序消费;未匹配结果保留为独立条目,错误状态使用语义错误令牌。图片由受保护路由读取为 Blob URL,离开视口前可按需加载,预览关闭或图片节点卸载时会中止读取并调用 `URL.revokeObjectURL`。
144
152
 
145
153
  轮次轨道保留在预览内:桌面位于消息流左侧,跳转后随消息流滚动并用 `aria-current` 标出当前轮次;宽度不超过 640px 时轨道移到消息流上方并水平滚动,用户气泡仍保留可用宽度。轨道不会被替换为宿主私有导航组件。
@@ -150,6 +158,7 @@ client.js 注册 order 30 的 settings.section,并使用 Harness 公开的浮
150
158
 
151
159
  - 所有状态变更路由都要求 POST 和 guard header。
152
160
  - 历史响应不包含工作区/快照/附件路径、原始事件、备注或确认 token;日志只记 ID 和稳定代码。
161
+ - 工作区归档响应只公开安全的工作区 ID/标题、符合条件数量、已确认预览中的会话 ID/标题/时间和稳定的逐项结果;工作区路径、事件正文、备注、附件路径与确认凭据不会进入日志或渲染结果。
153
162
  - 导入限制 ZIP 大小、条目数量、路径格式、版本和 JSON 结构,拒绝遍历、重复和原型污染字段。
154
163
  - 普通删除从不调用物理清除;仅已提交回收记录可进入 purge。
155
164
  - 快照和回收文件使用 `0600`,目录使用 `0700`;快照文件在 sync 前以可写句柄重新打开,发布顺序为临时写入、sync、原子 rename,以保持 Windows、macOS 和 Linux 的持久化语义一致。
@@ -158,7 +167,11 @@ client.js 注册 order 30 的 settings.section,并使用 Harness 公开的浮
158
167
 
159
168
  ## 兼容性和测试
160
169
 
161
- 插件通过能力检测适配 Host:归档读取、附件读取、持久层写入和运行中会话生命周期能力分别判断,缺失能力必须安全降级或返回明确错误。导入、历史版本恢复为副本和原件丢失时的快照回退都通过公开的 `create` / `append` / `locate` 能力写入,Host 提供专用恢复入口时优先使用;只有两者都不存在才返回 `restore-unsupported` 且不写入数据。要求一组没有任何已发布 Host 能满足的能力不是合格的守卫——那会让功能永久失效,而不是优雅降级。旧版若不显示历史页或不识别回收快照,降级前应备份整个插件数据目录。
170
+ 插件通过能力检测适配 Host:归档读取、附件读取、持久层写入、物理定位和运行中会话生命周期能力分别判断,缺失能力必须安全降级或返回明确错误。带原生 `inspect` 的旧版持久层对象保持原样;新版 Host 的 `list()` 快照和 `open(id, 'read')` 句柄被适配为内部 `list` / `listSnapshots` / `inspect` 只读视图,读取始终从偏移 0 开始并在成功或失败后关闭句柄。这个视图不虚构 `create`、`append` 或 `locate`。因此普通会话可以继续浏览、导出和抓取快照,但会话目录空间显示不可用,这个只读视图上的恢复写入与永久清除也不可用;缺少物理定位时,永久清除会在修改回收状态、快照、待处理标记或运行中会话之前以 `purge-unsupported` 拒绝。
171
+
172
+ 当前 v1 快照和 ZIP schema 不能保存 `inheritedEventCount`。新版读取句柄报告大于 0 的继承前缀时,适配器会在读取事件前以 `session-inspection-unsupported` 拒绝并关闭句柄,避免把分支历史静默展平。工作区归档本身仍按 Host 结果成功,关联的历史抓取则通过现有逐项结果显示失败。这个 UI 变更不扩展快照/恢复协议。
173
+
174
+ 兼容旧写入面的 Host 仍可让导入、历史版本恢复为副本和原件丢失时的快照回退通过公开的 `create` / `append` / `locate` 能力写入,Host 提供专用恢复入口时优先使用;只有两者都不存在才返回 `restore-unsupported` 且不写入数据。要求一组没有任何已发布 Host 能满足的能力不是合格的守卫——那会让功能永久失效,而不是优雅降级。旧版若不显示历史页或不识别回收快照,降级前应备份整个插件数据目录。
162
175
 
163
176
  测试覆盖:
164
177