dsh-sessions-manager 3.7.7 → 3.7.8

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 CHANGED
@@ -26,11 +26,11 @@ A persistent DSH plugin (host + browser halves) covering both the **settings pan
26
26
  - **Archive / Restore**: archive hides a session from the sidebar; restore unarchives it and puts it back into its original workspace group.
27
27
  - **Move to a workspace**: pick an **existing workspace**, or enter a **new directory path** (auto-created); the new-path mode also lets you open the **native OS directory picker** with a **「浏览…」** button. The session's working directory and log are migrated together. On the legacy runtime (`0.1.2-rc.1`) even an open session can be moved immediately; on newer runtimes session logs are **single-writer** (DSH claims write ownership when it opens a session and releases it only at process exit), so such a move is **queued automatically** instead of failing and completes on its own once the session is released — see "Moving an active session (queued moves)" below.
28
28
  - **Session details**: expand any session to see **disk usage**, **turns / steps / user·assistant messages / tool calls / image attachments** stats, **tool-usage breakdown**, **search·fetch records**, the **write/edit file list** (already filtered for paths that no longer exist on disk), and **lineage** (parent session / child sessions / subagents).
29
- - **Lineage disclosure**: the panel's **Grouping** select offers **Lineage (folded groups, default)** and **Flat (all inline)** views. Under lineage grouping, subagent sessions fold under their parents ("▸ N 子代理"), and sessions forked from the same source are **gathered into branch groups** ("▸ N 分支", oldest first by creation time; the source row stays in place, and if the parent is filtered out / archived / deleted the group degrades to a "来源:<短 ID>" header — no row is ever lost, so branches of an archived parent do hang under a "source" header in the Active view by design). Grouping lives in the panel; the sidebar only badges rows (React-owned DOM, never reordered). Fork sessions also carry the green **"⑂ 分支"** badge (hover shows the source parent; the title is late-arriving data and refreshes itself), and empty sessions carry a gray badge with a dedicated **Empty** view for reviewing and cleaning them up. Empty sessions are identified by an **event-type method**: since `0.1.3` the log header grew and every creation writes a lifecycle-metadata frame, so the old size threshold no longer holds — the plugin decodes candidate sessions through the official API and marks one empty only when no content event exists beyond the header (decode failures fall back to non-empty). Since 3.7.0 the checks run entirely off the request path: sidebar data answers immediately with empties marked "unknown" (unknown is never hidden and never counted as empty), a small background queue resolves candidates session by session, verdicts persist across restarts, and later polls converge automatically — zero-decode lineage such as the "⑂ branch" chip is no longer gated behind any refinement work.
29
+ - **Lineage disclosure**: the panel's **Grouping** select offers **Lineage (folded groups, default)** and **Flat (all inline)** views. Under lineage grouping, subagent sessions fold under their parents ("▸ N 子代理"), and sessions forked from the same source are **gathered into branch groups** ("▸ N 分支", oldest first by creation time; the source row stays in place, and if the parent is filtered out / archived / deleted the group degrades to a "来源:<短 ID>" header — no row is ever lost, so branches of an archived parent do hang under a "source" header in the Active view by design). Grouping lives in the panel; the sidebar only badges rows (React-owned DOM, never reordered). Subagent sessions likewise never occupy top-level rows: under lineage grouping they are excluded from the **All / Active / Archived / Empty** counts, from select-all, and only exist as the parent row's "N 子代理". Subagents whose parent is not in the current list (deleted / archived / filtered out) are collected into a single **"未归属的子代理会话"** card at the end of the list (collapsed by default; expand to open / archive / delete each of them — a search hit auto-expands it), instead of flooding the list with untitled rows. Switching to **Flat** keeps parity with native DSH: subagents sit inline and can be selected individually. Fork sessions also carry the green **"⑂ 分支"** badge (hover shows the source parent; the title is late-arriving data and refreshes itself), and empty sessions carry a gray badge with a dedicated **Empty** view for reviewing and cleaning them up. Empty sessions are identified by an **event-type method**: since `0.1.3` the log header grew and every creation writes a lifecycle-metadata frame, so the old size threshold no longer holds — the plugin decodes candidate sessions through the official API and marks one empty only when no content event exists beyond the header (decode failures fall back to non-empty); **plugin-injected context does not count as content** — an `agent/inbox/spliced` that only inserts messages whose `source.kind` starts with `plugin:` or whose `form` is `'instructions'` (and removes nothing) leaves the session empty, while real user messages and unrecognized payloads still count. Since 3.7.0 the checks run entirely off the request path: sidebar data answers immediately with empties marked "unknown" (unknown is never hidden and never counted as empty), a small background queue resolves candidates session by session, verdicts persist across restarts, and later polls converge automatically — zero-decode lineage such as the "⑂ branch" chip is no longer gated behind any refinement work.
30
30
  - **Export**: the detail panel offers two actions — "**Download raw log (ZIP)**" goes straight to DSH's official `session.export` endpoint (subsessions + attachments included, hidden automatically when the persistence backend lacks raw-artifact support); "**Export Markdown**" renders the session into a human-readable transcript (front matter + per-turn sections + user / assistant / tool-call summaries, with no streaming-delta duplication).
31
31
  - **Storage analysis**: a **Storage usage** button in the maintenance row expands on demand to aggregate per-workspace disk usage of session logs (share bars + session counts) and list the Top 10 largest sessions. Read-only statistics that never modify any data; collapsed by default, so nothing is scanned until you open it.
32
32
  - **Auto-archive**: optionally move sessions that have been **inactive for 30 / 60 / 90 days** into **Archived**, **off by default**. The session you are currently using and any starred session (that guard can be turned off) are never auto-archived; it only sets an archived flag and never deletes data, so recovery is always one click away. The check is lazily triggered when you open the panel, at most once per day, and can also be run manually with "Check now".
33
- - **Batch multi-select**: select-all / archive selected / restore selected / delete selected (single confirmation for batch delete). Once anything is selected, the batch action bar **pins to the top** of the panel (sticky), so a long list never forces you to scroll back up to act; a **"Move selected…"** action adds batch cross-workspace moves (reusing the single-move sheet: existing workspace / new directory / native directory picker). One failing session never blocks the rest — failures are reported one by one.
33
+ - **Batch multi-select**: select-all / archive selected / restore selected / delete selected (single confirmation for batch delete). **Select-all only covers rows that actually render and can be checked one by one** — folded subagents, gathered branch members and "source" headers are excluded, so nothing invisible is ever selected; batch delete cascades to subagents the same way and says how many it carried along. Once anything is selected, the batch action bar **pins to the top** of the panel (sticky), so a long list never forces you to scroll back up to act; a **"Move selected…"** action adds batch cross-workspace moves (reusing the single-move sheet: existing workspace / new directory / native directory picker). One failing session never blocks the rest — failures are reported one by one.
34
34
 
35
35
  ### Main sidebar: session ⋯ menu augmentation
36
36
 
@@ -64,6 +64,7 @@ The plugin hides DSH's own status dot and re-renders it using the palette above,
64
64
  - The **Recycle bin** is a dedicated Session Manager view with item counts and deletion dates.
65
65
  - Normally deleted sessions land in the recycle bin first; they are **not removed from disk immediately**, and they won't be grouped under "未分组".
66
66
  - Inside the recycle bin you can **restore** a session (back to its original workspace) or **permanently delete** it (physically remove the log).
67
+ - **Deleting a parent session takes its subagents with it**: the whole subagent subtree moves to the recycle bin by default (subagent lifetime follows the parent), and the delete dialog can opt out to keep them. Restoring or purging the parent cascades the same way, so no parentless orphan is left in the recycle bin — while a subagent you deleted on its own is never re-adopted by its parent (restoring the parent will not bring it back).
67
68
  - **Empty recycle bin** removes all items at once.
68
69
  - Keep items forever or automatically purge them after 7 / 30 / 90 days, and verify that their log files are intact.
69
70
  - The recycle-bin index uses a versioned schema, serialized mutations, and atomic writes; legacy array indexes migrate automatically.
@@ -171,12 +172,12 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
171
172
  | POST | `/archived-sessions/archive-many` | Archive many |
172
173
  | POST | `/archived-sessions/restore` | Unarchive a single session |
173
174
  | POST | `/archived-sessions/restore-many` | Unarchive many |
174
- | POST | `/archived-sessions/delete` | Delete a single session — **moves it to the recycle bin** (not an immediate physical delete) |
175
- | POST | `/archived-sessions/delete-many` | Move many sessions to the recycle bin |
175
+ | POST | `/archived-sessions/delete` | Delete a single session — **moves it to the recycle bin** (not an immediate physical delete); carries the whole subagent subtree by default (`cascadeSubagents: false` keeps them) and returns `cascaded` / `cascadedIds` |
176
+ | POST | `/archived-sessions/delete-many` | Move many sessions to the recycle bin (cascades to subagents per parent, returns the total `cascaded`) |
176
177
  | POST | `/archived-sessions/trash/list` | List sessions in the recycle bin |
177
- | POST | `/archived-sessions/trash/restore` | Restore a session from the recycle bin |
178
- | POST | `/archived-sessions/trash/purge` | Permanently delete a single session in the recycle bin |
179
- | POST | `/archived-sessions/trash/purge-many` | Empty / batch permanently delete recycle-bin sessions |
178
+ | POST | `/archived-sessions/trash/restore` | Restore a session from the recycle bin (restores the subagents that were carried out with it, returns `cascaded`) |
179
+ | POST | `/archived-sessions/trash/purge` | Permanently delete a single session in the recycle bin (cascades to the same carried-along entries, returns `cascaded`) |
180
+ | POST | `/archived-sessions/trash/purge-many` | Empty / batch permanently delete recycle-bin sessions (cascades per parent) |
180
181
  | POST | `/archived-sessions/trash/settings` | Read or update automatic cleanup `{ retentionDays }` |
181
182
  | POST | `/archived-sessions/trash/verify` | Verify that recycle-bin logs still exist |
182
183
  | POST | `/archived-sessions/workspaces` | List available target workspaces |
package/README.md CHANGED
@@ -26,11 +26,11 @@
26
26
  - **归档 / 恢复**:归档把会话从侧栏隐藏;恢复取消归档并放回原工作区分组。
27
27
  - **移动到工作区**:任选**已有工作区**或**新建目录路径**(自动创建),新建目录支持点击 **「浏览…」** 调用系统目录选择窗口。会话的工作目录与日志一起迁移。旧版 Runtime(`0.1.2-rc.1`)下已打开的会话也可立即移动;新版 Runtime 为会话日志引入**单写者所有权**(DSH 打开会话即持有、只在进程退出时释放),此时移动会**自动排队**而不是失败——释放后由插件自动完成,详见下文「移动活跃会话(排队移动)」。
28
28
  - **会话详情**:展开单条会话查看**磁盘占用**、**轮次 / 步骤 / 用户·助手消息 / 工具调用 / 图片附件**统计、**工具使用分布**、**搜索·抓取记录**、**write/edit 写过的文件列表**(已过滤磁盘上已不存在的路径),以及**血缘**(父会话 / 子会话 / 子代理)。
29
- - **血缘分层披露**:面板的「分组」下拉提供**血缘(折叠分组,默认)**与**平铺(全部并列)**两种视图。血缘视图下:子代理会话折叠在其父会话下(「▸ N 子代理」);由同一来源会话派生的**分支(fork)自动聚拢成组**(「▸ N 分支」,组内按创建时间升序,来源父行原位保留;父会话被筛掉/归档/删除时降级为「来源:<短 ID>」组头,一行不丢——因此**「活动」视图里已归档父会话的分支会挂在「来源」组头下,这是设计不是 bug**)。注意:聚拢在设置面板的血缘视图;侧栏受官方 React 托管行 DOM 限制只打标记不重排。分支会话带绿色「⑂ 分支」标记(悬停显示来源父会话;父标题为后到数据,到达后自动刷新),空白会话带灰色标记并可进入「空白」视图统一查看与清理。空白会话识别采用**事件类型法**:`0.1.3` 起日志头部扩容且创建时恒写一帧生命周期元数据,旧体积阈值失效——插件对候选会话做官方解码精判,头部之外没有任何内容事件才判空白(解码失败按非空兜底)。3.7.0 起精判完全移出数据返回路径:侧栏数据**首拍即回**,空白与否先按「未知」处理(未知一律不隐藏、不计入空白分类),后台小队列逐条判定、结论落盘跨重启复用,后续拍自动收敛——「⑂ 分支」等零解码信息不再被任何精判解码挡住。
29
+ - **血缘分层披露**:面板的「分组」下拉提供**血缘(折叠分组,默认)**与**平铺(全部并列)**两种视图。血缘视图下:子代理会话折叠在其父会话下(「▸ N 子代理」);由同一来源会话派生的**分支(fork)自动聚拢成组**(「▸ N 分支」,组内按创建时间升序,来源父行原位保留;父会话被筛掉/归档/删除时降级为「来源:<短 ID>」组头,一行不丢——因此**「活动」视图里已归档父会话的分支会挂在「来源」组头下,这是设计不是 bug**)。注意:聚拢在设置面板的血缘视图;侧栏受官方 React 托管行 DOM 限制只打标记不重排。子代理会话同理不占顶层行:血缘视图下它们不计入「全部 / 活动 / 已归档 / 空白」的计数、不参与全选,只作为父行的「N 子代理」存在;父会话不在当前列表(已删除 / 已归档 / 被筛掉)的子代理统一收进列表末尾的**「未归属的子代理会话」**卡片(默认收起,展开后可逐条打开 / 归档 / 删除,搜索命中自动展开),不会再以「(无标题)」行铺满列表。切到**平铺视图**则与 DSH 原生一致,子代理逐行并列、可单独勾选。分支会话带绿色「⑂ 分支」标记(悬停显示来源父会话;父标题为后到数据,到达后自动刷新),空白会话带灰色标记并可进入「空白」视图统一查看与清理。空白会话识别采用**事件类型法**:`0.1.3` 起日志头部扩容且创建时恒写一帧生命周期元数据,旧体积阈值失效——插件对候选会话做官方解码精判,头部之外没有任何内容事件才判空白(解码失败按非空兜底);**插件注入的上下文不算内容**——`agent/inbox/spliced` 只插入 `source.kind` 以 `plugin:` 开头或 `form: 'instructions'` 的消息、且未删除任何消息时,该会话仍判空白(真用户消息与无法识别的载荷照旧算内容)。3.7.0 起精判完全移出数据返回路径:侧栏数据**首拍即回**,空白与否先按「未知」处理(未知一律不隐藏、不计入空白分类),后台小队列逐条判定、结论落盘跨重启复用,后续拍自动收敛——「⑂ 分支」等零解码信息不再被任何精判解码挡住。
30
30
  - **导出**:详情面板底部提供两个入口——「**下载原始日志 (ZIP)**」直接走 DSH 官方 `session.export` 端点(含子会话与附件,持久化后端不支持时自动隐藏);「**导出 Markdown**」由本插件把会话渲染为人类可读对话记录(front matter + 按轮分节 + 用户 / 助手 / 工具调用摘要,流式增量不重复)。
31
31
  - **存储占用分析**:维护栏的「存储占用」按钮按需展开,按工作区聚合会话日志的磁盘占用(占比条 + 会话数),并列出占用最大的会话 Top 10。纯只读统计,不修改任何数据;默认收起、展开时才统计,因此不会拖慢会话列表的加载。
32
32
  - **自动归档**:可设为把 **30 / 60 / 90 天未活跃**的会话自动收进「已归档」,**默认关闭**。当前正在使用的会话与已收藏的会话(该保护可关闭)永不自动归档;只做归档标记,不删除任何数据,随时可恢复。检查在打开面板时惰性触发,每天最多一次,也可点「立即检查」手动执行。
33
- - **批量多选**:全选 / 批量归档 / 恢复所选 / 删除所选(批量删除一次二次确认)。勾选后批量操作栏**吸顶**钉在面板顶部,列表再长也无需滑回顶部操作;另提供「**移动所选…**」批量跨工作区移动(复用单移动面板:已有工作区 / 新建目录 / 系统目录浏览),单条失败不阻断整批,失败明细逐条回报。
33
+ - **批量多选**:全选 / 批量归档 / 恢复所选 / 删除所选(批量删除一次二次确认)。**全选只覆盖当前列表里真正占一行、可单独勾选的会话**——折叠中的子代理、聚拢的分支成员与「来源:」组头都不参与,避免选中界面上看不见的会话;批量删除同样按父会话级联子代理,提示会写明连带数量。勾选后批量操作栏**吸顶**钉在面板顶部,列表再长也无需滑回顶部操作;另提供「**移动所选…**」批量跨工作区移动(复用单移动面板:已有工作区 / 新建目录 / 系统目录浏览),单条失败不阻断整批,失败明细逐条回报。
34
34
 
35
35
  ### 主页侧边栏:会话 ⋯ 菜单增强
36
36
 
@@ -64,6 +64,7 @@
64
64
  - 回收站是会话管理中的独立视图,显示待清理数量和删除时间。
65
65
  - 正常删除的会话先进入回收站,**不会立即从磁盘移除**,也不会被归到「未分组」。
66
66
  - 回收站内可对单条会话执行**恢复**(回到原工作区)或**彻底删除**(物理清理日志)。
67
+ - **删除父会话会连带回收它的子代理**:默认把整棵子代理子树一起移入回收站(子代理生命周期跟随父会话),删除确认框可取消勾选以保留它们;恢复或彻底删除父会话时,这些连带条目同样级联处理,不会在回收站里留下无父的孤儿。用户自己单独删掉的子代理不会被父会话重新认领(恢复父会话也不会把它带回来)。
67
68
  - 支持**清空回收站**一键彻底删除全部内容。
68
69
  - 支持永久保留或 7 / 30 / 90 天自动清理,并可校验日志完整性。
69
70
  - 回收站索引采用版本化 schema、串行变更和原子写入;旧版数组索引会自动迁移。
@@ -171,12 +172,12 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
171
172
  | POST | `/archived-sessions/archive-many` | 批量归档 |
172
173
  | POST | `/archived-sessions/restore` | 取消归档单个 |
173
174
  | POST | `/archived-sessions/restore-many` | 批量取消归档 |
174
- | POST | `/archived-sessions/delete` | 删除单个会话——**移入回收站**(非立即物理删除) |
175
- | POST | `/archived-sessions/delete-many` | 批量移入回收站 |
175
+ | POST | `/archived-sessions/delete` | 删除单个会话——**移入回收站**(非立即物理删除);默认连带整棵子代理子树(`cascadeSubagents: false` 可保留),返回 `cascaded` / `cascadedIds` |
176
+ | POST | `/archived-sessions/delete-many` | 批量移入回收站(同样按父会话级联子代理,返回 `cascaded` 总数) |
176
177
  | POST | `/archived-sessions/trash/list` | 列出回收站中的会话 |
177
- | POST | `/archived-sessions/trash/restore` | 从回收站恢复会话 |
178
- | POST | `/archived-sessions/trash/purge` | 彻底删除回收站中的单个会话 |
179
- | POST | `/archived-sessions/trash/purge-many` | 清空/批量彻底删除回收站会话 |
178
+ | POST | `/archived-sessions/trash/restore` | 从回收站恢复会话(连带恢复当初跟随它一起回收的子代理,返回 `cascaded`) |
179
+ | POST | `/archived-sessions/trash/purge` | 彻底删除回收站中的单个会话(连带条目同样级联,返回 `cascaded`) |
180
+ | POST | `/archived-sessions/trash/purge-many` | 清空/批量彻底删除回收站会话(按父会话级联) |
180
181
  | POST | `/archived-sessions/trash/settings` | 读取或更新自动清理策略 `{ retentionDays }` |
181
182
  | POST | `/archived-sessions/trash/verify` | 校验回收站日志是否仍存在 |
182
183
  | POST | `/archived-sessions/workspaces` | 列出可选目标工作区 |