@michengai/dsh-archive-manager 0.1.42 → 0.1.44

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
@@ -4,6 +4,19 @@
4
4
 
5
5
  Published release notes are retained below; new versions are added without removing earlier entries.
6
6
 
7
+ ## Unreleased
8
+
9
+ ## 0.1.44 - 2026-09-18
10
+
11
+ - Opening an archived session from Settings no longer falsely reports that the host did not keep the conversation.
12
+ - Support DeepSeek Harness `0.1.6-alpha.2` while remaining compatible with earlier hosts. The archive page, opening archives, and permanent deletion keep working after the official API changes.
13
+
14
+ ## 0.1.43 - 2026-09-16
15
+
16
+ - Support DeepSeek Harness 0.1.6 while remaining compatible with earlier hosts. Settings keeps only this plugin's archive page, and the sidebar archive menu works on 0.1.6.
17
+ - Opening an archived session, or restoring and opening it, returns to the original workspace and group instead of being cleared immediately.
18
+ - Batch archive and restore continue one session at a time. A later failure keeps earlier changes, and missing sessions no longer break the action.
19
+
7
20
  ## 0.1.42 - 2026-09-14
8
21
 
9
22
  - Add Archived / Unarchived tabs with Archived selected by default. Select sessions across projects for batch archiving, or archive all chats in a project or ungrouped section from its menu.
@@ -4,6 +4,19 @@
4
4
 
5
5
  以下发布说明会持续保留;新增版本时不再删除较早记录。
6
6
 
7
+ ## Unreleased
8
+
9
+ ## 0.1.44 - 2026-09-18
10
+
11
+ - 设置里点「打开会话」不再误报「宿主未保留」。
12
+ - 支持 DeepSeek Harness `0.1.6-alpha.2`,并继续兼容更早版本。归档页、打开归档和永久删除在官方接口变更后仍可用。
13
+
14
+ ## 0.1.43 - 2026-09-16
15
+
16
+ - 支持 DeepSeek Harness 0.1.6,并继续兼容更早版本。设置里只保留本插件的归档页,侧栏归档菜单在 0.1.6 上也能用。
17
+ - 打开归档会话或「恢复并打开」会回到原来的工作区和分组,不再被立刻清掉。
18
+ - 批量归档和恢复改为逐条处理:中途失败会保留已完成的部分,找不到的会话不再报错打断。
19
+
7
20
  ## 0.1.42 - 2026-09-14
8
21
 
9
22
  - 新增「已归档 / 未归档」页签,默认展示已归档;支持跨项目选择会话批量归档,也可通过项目或未分组菜单归档该组全部聊天。
package/README.md CHANGED
@@ -40,7 +40,7 @@ Find, restore, and clean up chats in **Settings → Archived sessions**:
40
40
  ## Prerequisites
41
41
 
42
42
  - A working [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web installation with `dsh` available in your terminal.
43
- - Supported DSH versions: `0.1.0-rc.8`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, and `0.1.5-rc.2`. Other versions are not currently supported.
43
+ - Supported DSH versions: `0.1.0-rc.8`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.6-alpha.1`, and `0.1.6-alpha.2`. Other versions are not currently supported.
44
44
  - Node.js matching `^22.19.0 || >=24.0.0`. Source installation also requires pnpm.
45
45
 
46
46
  ## Installation
@@ -85,7 +85,7 @@ The page opens on **Archived**, with **Unarchived** on the right. Switching tabs
85
85
 
86
86
  **Restore all / Delete all** remain on the Archived tab and apply to archived sessions across all projects, regardless of filters. Unarchived excludes subagents and blank placeholders and supports sorting by update time or title.
87
87
 
88
- Developer API: Typert `workspaceRegistry/archiveSessions` accepts `{ sessionIds: string[] }` and returns `{ archivedSessionIds, archivedSessionIdsAdded }`. Call `registry.archiveSessions(ids)` and check the Typert `ok` result; the method uses the existing host authentication and RPC channel. The host validates and deduplicates the batch before one state write. Already archived IDs are idempotent; unknown sessions return `UNKNOWN_SESSION` without a partial archive.
88
+ Settings and sidebar batch archive/restore call the single-session APIs serially: official `ctx.workspaces.archiveSession` on every supported host, official `ctx.workspaces.unarchiveSession` on DSH 0.1.6+, and this plugin's `workspaceRegistry.unarchiveSession` on older hosts.
89
89
 
90
90
  ### View and continue archived conversations
91
91
 
@@ -111,7 +111,7 @@ $OutputEncoding = [System.Text.Encoding]::UTF8
111
111
  dsh --profile web --dump-config
112
112
  ```
113
113
 
114
- The configuration should include `workspace-archive-manager` and `ui-workspace-archive-manager`. If you previously set the official `ui-workspace` to `disabled: true` in your profile's `cordis.patch.yml`, remove that disabling override and restart.
114
+ The configuration should include `workspace-archive-manager` and `ui-workspace-archive-manager`. On DSH 0.1.6+, official `ui-settings-unarchive-sessions` should be `disabled: true` so Settings keeps only this plugin's Archived sessions page. If you previously set the official `ui-workspace` to `disabled: true` in your profile's `cordis.patch.yml`, remove that disabling override and restart.
115
115
 
116
116
  ### How is archiving different from deletion?
117
117
 
package/README.zh-CN.md CHANGED
@@ -40,7 +40,7 @@
40
40
  ## 前置条件
41
41
 
42
42
  - 已能正常使用 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web,并可在终端运行 `dsh`。
43
- - 当前支持 DSH `0.1.0-rc.8`、`0.1.1-rc.2`、`0.1.2-rc.1`、`0.1.5-rc.1`、`0.1.5-rc.2`;其他版本暂未纳入支持范围。
43
+ - 当前支持 DSH `0.1.0-rc.8`、`0.1.1-rc.2`、`0.1.2-rc.1`、`0.1.5-rc.1`、`0.1.5-rc.2`、`0.1.6-alpha.1`、`0.1.6-alpha.2`;其他版本暂未纳入支持范围。
44
44
  - Node.js 版本需满足 `^22.19.0 || >=24.0.0`;从源码安装还需要 pnpm。
45
45
 
46
46
  ## 安装
@@ -85,7 +85,7 @@ dsh plugin --profile web add @michengai/dsh-archive-manager@latest --registry=ht
85
85
 
86
86
  「全部恢复 / 全部删除」仅在已归档页显示,作用于所有项目的归档会话,不受筛选影响。未归档页排除子代理和空白占位会话,支持按更新时间或标题排序。
87
87
 
88
- 开发接口:Typert `workspaceRegistry/archiveSessions` 接收 `{ sessionIds: string[] }`,返回 `{ archivedSessionIds, archivedSessionIdsAdded }`。客户端调用 `registry.archiveSessions(ids)` 并检查 Typert 的 `ok` 结果;接口复用宿主连接的认证与调用通道。宿主先校验、去重,再一次写入;已归档项幂等跳过,未知会话返回 `UNKNOWN_SESSION` 且不部分归档。
88
+ 设置页与侧栏的批量归档、批量恢复均串行调用单条接口:归档在所有支持的宿主走官方 `ctx.workspaces.archiveSession`;恢复在 DSH 0.1.6+ 走官方 `ctx.workspaces.unarchiveSession`,更早宿主回退到本插件的 `workspaceRegistry.unarchiveSession`。
89
89
 
90
90
  ### 查看并继续归档对话
91
91
 
@@ -111,7 +111,7 @@ $OutputEncoding = [System.Text.Encoding]::UTF8
111
111
  dsh --profile web --dump-config
112
112
  ```
113
113
 
114
- 配置中应包含 `workspace-archive-manager` 和 `ui-workspace-archive-manager`。若曾在 profile 的 `cordis.patch.yml` 中手动将官方 `ui-workspace` 设为 `disabled: true`,请移除该禁用覆盖,再重启。
114
+ 配置中应包含 `workspace-archive-manager` 和 `ui-workspace-archive-manager`。DSH 0.1.6+ 上官方 `ui-settings-unarchive-sessions` 应为 `disabled: true`,设置里只保留本插件的「归档会话」。若曾在 profile 的 `cordis.patch.yml` 中手动将官方 `ui-workspace` 设为 `disabled: true`,请移除该禁用覆盖,再重启。
115
115
 
116
116
  ### 归档和删除有什么区别?
117
117
 
package/cordis.patch.yml CHANGED
@@ -1,10 +1,17 @@
1
1
  # 保留官方工作区 UI,仅替换归档需要的后端服务。
2
2
  # 自定义侧栏通过优先级覆盖插槽;发布后的安装命令:
3
3
  # dsh plugin --profile web add @michengai/dsh-archive-manager
4
+ #
5
+ # workspace / session-projection-cache:宿主注册表与投影缓存,不是设置页。
6
+ # 换成本插件子类,才能加批量归档、永久删除和安全缓存域。
7
+ # ui-settings-unarchive-sessions:DSH 0.1.6+官方「已归档会话」设置页,只能单条恢复;
8
+ # 禁用后只保留本插件的归档管理入口。旧宿主没有该 id 时补丁被忽略。
4
9
  - id: workspace
5
10
  disabled: true
6
11
  - id: session-projection-cache
7
12
  disabled: true
13
+ - id: ui-settings-unarchive-sessions
14
+ disabled: true
8
15
  - id: ui-workspace
9
16
  disabled: false
10
17
  - insert:
@@ -8,18 +8,43 @@ class ArchiveNavigationError extends Error {
8
8
  function formatArchiveNavigationError(error, t) {
9
9
  return error instanceof ArchiveNavigationError ? t(`archives.${error.code}`) : String(error?.message ?? error);
10
10
  }
11
+ function retainInfoSnapshot(sessions, id) {
12
+ if (typeof sessions?.retainInfo !== "function") return;
13
+ const source = sessions.retainInfo(id);
14
+ return typeof source?.getSnapshot === "function" ? source.getSnapshot() : source;
15
+ }
16
+ function mainViewSessionId(list) {
17
+ const byId = list?.byId;
18
+ if (byId === void 0) return;
19
+ for (const session of Object.values(byId)) {
20
+ if ((session?.retainedBy?.mainView ?? 0) > 0) return session.id;
21
+ }
22
+ }
23
+ function currentSessionId(list) {
24
+ return mainViewSessionId(list) ?? list?.current;
25
+ }
26
+ function sessionIsCurrent(list, id, sessions) {
27
+ if (id === void 0 || id === "") return false;
28
+ if ((list?.byId?.[id]?.retainedBy?.mainView ?? 0) > 0) return true;
29
+ if ((retainInfoSnapshot(sessions, id)?.retainedBy?.mainView ?? 0) > 0) return true;
30
+ return list?.current === id;
31
+ }
11
32
  async function openArchivedConversation(actions, sessionId, restore, isActive = () => true) {
12
- if (restore) await actions.restore(sessionId);
33
+ if (restore) {
34
+ if (typeof actions.prepare === "function") await actions.prepare(sessionId);
35
+ if (!isActive()) return;
36
+ await actions.restore(sessionId);
37
+ }
13
38
  if (isActive()) await actions.open(sessionId);
14
39
  }
15
40
  function allowArchivedNavigation(navigation, sessions, workspaces, { onOpened = () => {
16
- }, warn = (...args) => console.warn(...args) } = {}) {
41
+ }, beginNavigation, warn = (...args) => console.warn(...args) } = {}) {
17
42
  let allowed;
18
43
  const original = navigation?.clearArchivedCurrent;
19
44
  const descriptor = navigation && Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent");
45
+ const viewing = (id) => sessionIsCurrent(sessions.list.getSnapshot(), id, sessions);
20
46
  const wrapped = function(...args) {
21
- const current = sessions.list.getSnapshot().current;
22
- if (current === allowed && current !== void 0 && workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
47
+ if (allowed !== void 0 && viewing(allowed) && workspaces.list.getSnapshot().archivedSessionIds.includes(allowed)) return false;
23
48
  allowed = void 0;
24
49
  return original.apply(this, args);
25
50
  };
@@ -40,8 +65,15 @@ function allowArchivedNavigation(navigation, sessions, workspaces, { onOpened =
40
65
  warn("archive-manager: \u5F52\u6863\u5BFC\u822A\u9002\u914D\u4E0D\u53EF\u7528\u6216\u5DF2\u88AB\u66FF\u6362\u3002");
41
66
  throw new ArchiveNavigationError("navigationUnavailable");
42
67
  }
43
- sessions.open(id);
44
- if (sessions.list.getSnapshot().current !== id) {
68
+ beginNavigation?.();
69
+ if (typeof sessions.retain === "function" && typeof navigation?.openSession === "function") navigation.openSession(id);
70
+ else if (typeof sessions.open === "function") sessions.open(id);
71
+ else if (typeof navigation?.openSession === "function") navigation.openSession(id);
72
+ else {
73
+ warn("archive-manager: \u5BBF\u4E3B\u672A\u63D0\u4F9B\u4F1A\u8BDD\u6253\u5F00\u63A5\u53E3\u3002");
74
+ throw new ArchiveNavigationError("navigationUnavailable");
75
+ }
76
+ if (!viewing(id)) {
45
77
  warn("archive-manager: \u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\u3002", { cleanupAvailable: typeof original === "function" });
46
78
  throw new ArchiveNavigationError("sessionNotRetained");
47
79
  }
@@ -63,6 +95,8 @@ function allowArchivedNavigation(navigation, sessions, workspaces, { onOpened =
63
95
  export {
64
96
  ArchiveNavigationError,
65
97
  allowArchivedNavigation,
98
+ currentSessionId,
66
99
  formatArchiveNavigationError,
67
- openArchivedConversation
100
+ openArchivedConversation,
101
+ sessionIsCurrent
68
102
  };