@michengai/dsh-archive-manager 0.1.23 → 0.1.24

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,12 @@
4
4
 
5
5
  Published release notes are retained below; new versions are added without removing earlier entries.
6
6
 
7
+ ## 0.1.24 — 2026-09-02
8
+
9
+ - Upgraded the development test stack to DSH `0.1.2-alpha.5` and `@deepseek-ai/cordis` `4.0.2`, while retaining the existing `>=0.1.0-rc.5 <0.2.0` DSH runtime peer declarations.
10
+ - Switched primary client integration tests to the current `dsh-client-store` contract and retained a tested optional `dsh-client-runtime` fallback for older hosts.
11
+ - Added a reproducible pnpm alpha-resolution policy that disables automatic peer installation and records the approved newly published packages.
12
+
7
13
  ## 0.1.23 — 2026-09-01
8
14
 
9
15
  - Fixed missing sidebar pending-state indicators on DSH `v0.1.2-alpha.2`: workspace rows now read pending UI interactions from the `ui-session` `pendingInteractions` Map.
@@ -4,6 +4,12 @@
4
4
 
5
5
  以下发布说明会持续保留;新增版本时不再删除较早记录。
6
6
 
7
+ ## 0.1.24 — 2026-09-02
8
+
9
+ - 将开发测试依赖升级至 DSH `0.1.2-alpha.5` 与 `@deepseek-ai/cordis` `4.0.2`,同时保留原有的 DSH 运行时 peer 声明 `>=0.1.0-rc.5 <0.2.0`。
10
+ - 客户端集成测试以当前 `dsh-client-store` 为主路径,并保留经过测试的可选 `dsh-client-runtime` 回退,兼容旧版宿主。
11
+ - 新增可复现的 pnpm alpha 解析策略:关闭自动安装 peer,并记录已批准的刚发布依赖。
12
+
7
13
  ## 0.1.23 — 2026-09-01
8
14
 
9
15
  - 修复 DSH `v0.1.2-alpha.2` 后会话侧栏“等待回答 / 等待审批”状态点不显示的问题:侧栏行现在从 `ui-session` 的 `pendingInteractions` Map 读取待处理交互状态。
package/lib/client.js CHANGED
@@ -284,6 +284,10 @@ window.__ModuleLoader__.load({
284
284
  }
285
285
  });
286
286
  }
287
+ /** GitHub 品牌标识未由宿主图标库提供,内联以保持离线可用和主题适配。 */
288
+ function GithubMark16() {
289
+ return (0, react_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", width: 16, height: 16, "aria-hidden": true, focusable: "false", children: (0, react_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M8 0a8 8 0 0 0-2.53 15.59c.4.074.547-.173.547-.385 0-.19-.007-.693-.01-1.36-2.226.484-2.695-1.073-2.695-1.073-.364-.924-.89-1.17-.89-1.17-.726-.496.055-.486.055-.486.803.056 1.225.824 1.225.824.714 1.223 1.872.87 2.328.665.072-.517.28-.87.508-1.07-1.777-.202-3.645-.888-3.645-3.956 0-.874.31-1.588.823-2.148-.083-.202-.357-1.017.078-2.12 0 0 .672-.215 2.2.82A7.65 7.65 0 0 1 8 4.8c.68.003 1.365.092 2.004.27 1.527-1.035 2.197-.82 2.197-.82.437 1.103.162 1.918.08 2.12.513.56.822 1.274.822 2.148 0 3.076-1.872 3.752-3.654 3.95.288.248.544.735.544 1.482 0 1.07-.01 1.932-.01 2.195 0 .214.144.463.55.384A8.001 8.001 0 0 0 8 0Z" }) });
290
+ }
287
291
  /**
288
292
  * 归档多选原型。保留既有设置页作为兼容回退,原型直接使用相同宿主
289
293
  * 批量接口,因此其恢复、删除和并发安全语义与原有项目级操作一致。
@@ -423,10 +427,10 @@ window.__ModuleLoader__.load({
423
427
  return (0, react_jsx_runtime.jsxs)("section", {
424
428
  className: "dsham_settings",
425
429
  "aria-label": t("archives.title"),
426
- children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_SELECTION_CSS }), (0, react_jsx_runtime.jsxs)("header", {
430
+ children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS + ARCHIVE_SETTINGS_SELECTION_CSS + ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS }), (0, react_jsx_runtime.jsxs)("header", {
427
431
  className: "dsham_settingsHeader",
428
432
  children: [(0, react_jsx_runtime.jsxs)("div", {
429
- children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsIntro", children: t("archives.description") })]
433
+ children: [(0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsTitleRow", children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsLinks", children: [(0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager", target: "_blank", rel: "noreferrer", "aria-label": t("archives.viewProject"), children: [(0, react_jsx_runtime.jsx)(GithubMark16, {}), t("archives.viewProject")] }), (0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager/issues", target: "_blank", rel: "noreferrer", "aria-label": t("archives.feedback"), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, {}), t("archives.feedback")] })] })] }), (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsIntro", children: t("archives.description") })]
430
434
  }), (0, react_jsx_runtime.jsxs)("div", {
431
435
  className: "dsham_settingsHeaderActions",
432
436
  children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsRestoreAll", disabled: busy || allBatchSessionIds.length === 0, onClick: () => onBatchUnarchive(allBatchTarget), children: t("archives.restoreAll") }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || allBatchSessionIds.length === 0, onClick: () => setDeleteTarget({ kind: "batch", target: allBatchTarget, title: t("archives.allProjects"), count: allBatchSessionIds.length }), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}), t("archives.deleteAll")] })]
@@ -468,7 +472,7 @@ window.__ModuleLoader__.load({
468
472
  closeLabel: t("close"),
469
473
  title: deleteDialogTitle,
470
474
  ...deleteDialogDescription === void 0 ? {} : { description: deleteDialogDescription },
471
- footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeDelete, children: t("cancel") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: confirmDelete, children: deleteConfirmLabel })] }),
475
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeDelete, children: t("cancel") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", className: "dsham_settingsDeleteConfirm", disabled: busy, onClick: confirmDelete, children: deleteConfirmLabel })] }),
472
476
  children: busy && (0, react_jsx_runtime.jsx)("div", { role: "status", children: deleteTarget?.kind === "batch" ? t("archives.deleteBatchPending") : t("deleteSession.pending") })
473
477
  })]
474
478
  });
@@ -496,8 +500,8 @@ window.__ModuleLoader__.load({
496
500
  * @param cwd - directory path, or undefined for the ungrouped bucket.
497
501
  * @returns basename, the raw cwd when it has no basename, or the ungrouped label.
498
502
  */
499
- function workspaceLabel(cwd) {
500
- if (cwd === void 0 || cwd === "") return UNGROUPED_LABEL;
503
+ function workspaceLabel(cwd, ungroupedLabel = UNGROUPED_LABEL) {
504
+ if (cwd === void 0 || cwd === "") return ungroupedLabel;
501
505
  const base = cwd.replace(/[/\\]+$/, "").split(/[/\\]/).pop();
502
506
  return base !== void 0 && base !== "" ? base : cwd;
503
507
  }
@@ -711,9 +715,10 @@ window.__ModuleLoader__.load({
711
715
  * @param content - ranked Host content-search page.
712
716
  * @param limit - protocol-owned maximum merged row count.
713
717
  * @param showArchived - "show archived" view toggle.
718
+ * @param ungroupedLabel - 未归属工作区会话的本地化回退标签。
714
719
  * @returns bounded deduplicated flat rows and a refine-query hint bit.
715
720
  */
716
- function deriveSearchResults(list, workspaces, query, archivedSessionIds, pendingInteractions, content, limit, showArchived) {
721
+ function deriveSearchResults(list, workspaces, query, archivedSessionIds, pendingInteractions, content, limit, showArchived, ungroupedLabel = UNGROUPED_LABEL) {
717
722
  const q = query.trim().toLowerCase();
718
723
  if (q === "") return {
719
724
  items: [],
@@ -723,7 +728,7 @@ window.__ModuleLoader__.load({
723
728
  const descendants = indexSubagentDescendants(list.byId);
724
729
  const workspaceBySession = /* @__PURE__ */ new Map();
725
730
  for (const workspace of workspaces) for (const sessionId of workspace.sessionIds) if (!workspaceBySession.has(sessionId)) workspaceBySession.set(sessionId, workspace.title);
726
- const labelOf = (summary) => workspaceBySession.get(summary.id) ?? workspaceLabel(summary.cwd);
731
+ const labelOf = (summary) => workspaceBySession.get(summary.id) ?? workspaceLabel(summary.cwd, ungroupedLabel);
727
732
  const contentBySession = /* @__PURE__ */ new Map();
728
733
  for (const item of content.items) if (!contentBySession.has(item.sessionId)) contentBySession.set(item.sessionId, item);
729
734
  const local = [];
@@ -2148,13 +2153,14 @@ window.__ModuleLoader__.load({
2148
2153
  function SearchResults({ useSessions, useSessionPendingInteraction, open, workspaces, archivedSessionIds, showArchived, query, remote, resultLimit, t }) {
2149
2154
  const list = useSessions((s) => s);
2150
2155
  const pendingInteractions = useSessionPendingInteraction((s) => s);
2156
+ const ungroupedLabel = t("group.ungrouped");
2151
2157
  const currentRemote = remote.query === query ? remote : {
2152
2158
  query,
2153
2159
  status: "loading",
2154
2160
  items: [],
2155
2161
  hasMore: false
2156
2162
  };
2157
- const results = (0, react.useMemo)(() => deriveSearchResults(list, workspaces, query, archivedSessionIds, pendingInteractions, currentRemote, resultLimit, showArchived), [
2163
+ const results = (0, react.useMemo)(() => deriveSearchResults(list, workspaces, query, archivedSessionIds, pendingInteractions, currentRemote, resultLimit, showArchived, ungroupedLabel), [
2158
2164
  list,
2159
2165
  workspaces,
2160
2166
  query,
@@ -2162,7 +2168,8 @@ window.__ModuleLoader__.load({
2162
2168
  pendingInteractions,
2163
2169
  currentRemote,
2164
2170
  resultLimit,
2165
- showArchived
2171
+ showArchived,
2172
+ ungroupedLabel
2166
2173
  ]);
2167
2174
  const pending = currentRemote.status === "loading";
2168
2175
  const failed = currentRemote.status === "error";
@@ -2849,13 +2856,15 @@ window.__ModuleLoader__.load({
2849
2856
  //#region dsh-archive-manager: settings section
2850
2857
  const ARCHIVE_SETTINGS_CSS = ".dsham_settings{box-sizing:border-box;width:min(100%,760px);margin:0 auto;padding:0 0 32px;color:var(--dsw-alias-label-primary)}.dsham_settingsHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:12px}.dsham_settings h2{margin:0;font-size:20px;font-weight:650;letter-spacing:-.2px;line-height:28px}.dsham_settingsIntro{margin:4px 0 0;max-width:42em;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.dsham_settingsDanger{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:0 12px;color:var(--dsw-alias-state-error-primary);background:transparent;border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsDanger:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)}.dsham_settingsToolbar{display:flex;gap:8px;margin-bottom:16px}.dsham_settingsSearch{display:flex;align-items:center;gap:8px;min-width:0;flex:1;height:32px;padding:0 12px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-button-elevated-fill));border:1px solid var(--dsw-alias-border-l2);border-radius:8px}.dsham_settingsSearch:focus-within{border-color:var(--dsw-alias-label-tertiary)}.dsham_settingsSearch input{width:100%;min-width:0;padding:0;color:var(--dsw-alias-label-primary);background:transparent;border:0;outline:0;font:inherit;font-size:12px}.dsham_settingsSearch input::placeholder{color:var(--dsw-alias-label-tertiary)}.dsham_settingsFilter{position:relative;min-width:168px;flex:none}.dsham_selectTrigger{box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;min-height:32px;padding:0 10px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-button-elevated-fill));border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;line-height:20px;text-align:left}.dsham_selectTrigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsham_selectTrigger:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_selectTrigger[aria-expanded='true']{border-color:var(--dsw-alias-state-success-primary)}.dsham_selectValue{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsham_selectCaret{flex:none;width:12px;height:12px;color:var(--dsw-alias-label-tertiary)}.dsham_selectMenu{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:30;box-sizing:border-box;min-width:100%;max-height:280px;overflow:auto;padding:4px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-shadow-lv3)}.dsham_selectOption{box-sizing:border-box;display:flex;align-items:center;width:100%;min-height:32px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;text-align:left;cursor:pointer}.dsham_selectOption:hover,.dsham_selectOption[data-active='true']{background:var(--dsw-alias-interactive-bg-hover)}.dsham_selectOption[aria-selected='true']{color:var(--dsw-alias-state-success-primary)}.dsham_settingsGroup{margin:0 0 20px}.dsham_settingsGroupHeading{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 14px}.dsham_settingsGroupTitle{display:flex;align-items:center;gap:8px;min-width:0;margin:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.dsham_settingsGroupTitle svg{flex:none;color:var(--dsw-alias-label-secondary)}.dsham_settingsCount{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsham_settingsList{overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-button-elevated-fill))}.dsham_settingsRow{display:flex;align-items:center;gap:12px;min-height:60px;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_settingsRow:last-child{border-bottom:0}.dsham_settingsContent{min-width:0;flex:1}.dsham_settingsTitle{overflow:hidden;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.dsham_settingsMeta{margin-top:2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.dsham_settingsActions{display:flex;align-items:center;gap:8px}.dsham_settingsAction{min-height:32px;padding:0 12px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsAction:hover{filter:brightness(1.12)}.dsham_settingsDelete{display:flex;align-items:center;justify-content:center;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:transparent;border:0;border-radius:8px;cursor:pointer}.dsham_settingsDelete:hover{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsEmpty{padding:28px 8px;color:var(--dsw-alias-label-secondary);text-align:center}.dsham_settingsError{margin-top:10px;color:var(--dsw-alias-state-error-primary);font-size:12px}@media(max-width:720px){.dsham_settings{width:100%;margin:28px auto 48px;padding:0 16px}.dsham_settingsHeader{margin-bottom:28px}.dsham_settingsToolbar{flex-wrap:wrap;margin-bottom:28px}.dsham_settingsSearch{flex-basis:100%}.dsham_settingsFilter{flex:1;min-width:0}.dsham_settingsGroup{margin-bottom:32px}.dsham_settingsRow{padding:10px 12px}.dsham_settingsActions{gap:4px}}";
2851
2858
  const ARCHIVE_SETTINGS_BATCH_CSS = ".dsham_settingsHeaderActions,.dsham_settingsGroupMeta{display:flex;align-items:center;gap:8px;flex:none}.dsham_settingsRestoreAll{display:inline-flex;align-items:center;min-height:32px;padding:0 12px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsRestoreAll:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsRestoreAll:disabled,.dsham_settingsDanger:disabled,.dsham_settingsGroupMenu:disabled{cursor:not-allowed;opacity:.5}.dsham_settingsGroupMenu{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;color:var(--dsw-alias-label-tertiary);background:transparent;border:0;border-radius:8px;cursor:pointer}.dsham_settingsGroupMenu:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsStatus{margin-top:10px;color:var(--dsw-alias-label-secondary);font-size:12px}@media(max-width:720px){.dsham_settingsHeader{flex-direction:column}.dsham_settingsHeaderActions{align-self:flex-end}}";
2859
+ const ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS = ".dsham_settingsTitleRow{display:flex;align-items:center;gap:8px;min-width:0}.dsham_settingsLinks{display:flex;align-items:center;gap:4px}.dsham_settingsExternalLink{display:inline-flex;align-items:center;gap:5px;min-height:28px;padding:0 8px;color:var(--dsw-alias-label-secondary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;font-size:12px;font-weight:500;line-height:18px;text-decoration:none;white-space:nowrap}.dsham_settingsExternalLink:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsExternalLink:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_settingsExternalLink svg{flex:none}@media(max-width:720px){.dsham_settingsTitleRow{flex-wrap:wrap}}";
2860
+ const ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS = ".dsham_settingsDeleteConfirm{color:var(--dsw-alias-state-error-primary)!important;background:transparent!important;border-color:var(--dsw-alias-state-error-primary)!important}.dsham_settingsDeleteConfirm:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)!important}.dsham_settingsDeleteConfirm:focus-visible{outline:2px solid var(--dsw-alias-state-error-secondary);outline-offset:2px}.dsham_settingsDeleteConfirm:disabled{cursor:not-allowed;opacity:.5}";
2852
2861
  const ARCHIVE_SETTINGS_SELECTION_CSS = ".dsham_settingsSelection{display:flex;align-items:center;gap:10px;min-height:40px;margin:-8px 0 16px;padding:4px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-button-elevated-fill))}.dsham_settingsSelectionToggle{display:inline-flex;align-items:center;gap:8px;min-width:0;color:var(--dsw-alias-label-primary);font-size:13px;cursor:pointer}.dsham_settingsCheckbox{width:16px;height:16px;margin:0;accent-color:#3b82f6}.dsham_settingsSelectionCount{margin-right:auto;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsham_settingsSelectionAction{min-height:28px;padding:0 10px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;cursor:pointer;font:inherit;font-size:12px;font-weight:500}.dsham_settingsSelectionAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsSelectionDelete{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.dsham_settingsSelectionAction:disabled{cursor:not-allowed;opacity:.5}@media(max-width:720px){.dsham_settingsSelection{flex-wrap:wrap}.dsham_settingsSelectionCount{margin-right:0}.dsham_settingsSelectionAction{margin-left:auto}.dsham_settingsSelectionAction+.dsham_settingsSelectionAction{margin-left:-2px}}";
2853
2862
  const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:0 auto!important}@media(max-width:720px){.dsham_settings{margin:0 auto!important}}";
2854
2863
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/ArchiveSettings.layout.css") + "]") === null) {
2855
2864
  const tag = document.createElement("style");
2856
2865
  tag.dataset.plugin = "@michengai/dsh-archive-manager";
2857
2866
  tag.dataset.pluginCss = "dsh-archive-manager/ArchiveSettings.layout.css";
2858
- tag.textContent = ARCHIVE_SETTINGS_LAYOUT_OVERRIDE;
2867
+ tag.textContent = ARCHIVE_SETTINGS_LAYOUT_OVERRIDE + ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS;
2859
2868
  document.head.appendChild(tag);
2860
2869
  }
2861
2870
  /** 设置页筛选/排序:自定义菜单,避免原生 select 弹出系统浅色下拉。 */
@@ -3291,7 +3300,7 @@ window.__ModuleLoader__.load({
3291
3300
  return (0, react_jsx_runtime.jsxs)("section", {
3292
3301
  className: "dsham_settings",
3293
3302
  "aria-label": t("archives.title"),
3294
- children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS }), (0, react_jsx_runtime.jsxs)("header", { className: "dsham_settingsHeader", children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsIntro", children: t("archives.description") })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsHeaderActions", children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsRestoreAll", disabled: busy || allBatchSessionIds.length === 0, onClick: () => onBatchUnarchive(allBatchTarget), children: t("archives.restoreAll") }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || allBatchSessionIds.length === 0, onClick: () => setDeleteTarget({ kind: "batch", target: allBatchTarget, title: t("archives.allProjects"), count: allBatchSessionIds.length }), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}), t("archives.deleteAll")] })] })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsToolbar", children: [(0, react_jsx_runtime.jsxs)("label", { className: "dsham_settingsSearch", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("archives.searchPlaceholder"), "aria-label": t("archives.searchPlaceholder") })] }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-sort-filter", value: sortBy, options: [{ value: "updated", label: t("archives.sortUpdated") }, { value: "created", label: t("archives.sortCreated") }, { value: "alphabetical", label: t("archives.sortAlphabetical") }], onChange: setSortBy, "aria-label": t("archives.sortBy") }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-project-filter", value: project, options: [{ value: "all", label: t("archives.allProjects") }, ...sortedGroups.map((group) => ({ value: group.key, label: group.title }))], onChange: setProject, "aria-label": t("archives.projectFilter") })] }), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.empty") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => {
3303
+ children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS }), (0, react_jsx_runtime.jsxs)("header", { className: "dsham_settingsHeader", children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsTitleRow", children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsLinks", children: [(0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager", target: "_blank", rel: "noreferrer", "aria-label": t("archives.viewProject"), children: [(0, react_jsx_runtime.jsx)(GithubMark16, {}), t("archives.viewProject")] }), (0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager/issues", target: "_blank", rel: "noreferrer", "aria-label": t("archives.feedback"), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, {}), t("archives.feedback")] })] })] }), (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsIntro", children: t("archives.description") })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsHeaderActions", children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsRestoreAll", disabled: busy || allBatchSessionIds.length === 0, onClick: () => onBatchUnarchive(allBatchTarget), children: t("archives.restoreAll") }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || allBatchSessionIds.length === 0, onClick: () => setDeleteTarget({ kind: "batch", target: allBatchTarget, title: t("archives.allProjects"), count: allBatchSessionIds.length }), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}), t("archives.deleteAll")] })] })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsToolbar", children: [(0, react_jsx_runtime.jsxs)("label", { className: "dsham_settingsSearch", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("archives.searchPlaceholder"), "aria-label": t("archives.searchPlaceholder") })] }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-sort-filter", value: sortBy, options: [{ value: "updated", label: t("archives.sortUpdated") }, { value: "created", label: t("archives.sortCreated") }, { value: "alphabetical", label: t("archives.sortAlphabetical") }], onChange: setSortBy, "aria-label": t("archives.sortBy") }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-project-filter", value: project, options: [{ value: "all", label: t("archives.allProjects") }, ...sortedGroups.map((group) => ({ value: group.key, label: group.title }))], onChange: setProject, "aria-label": t("archives.projectFilter") })] }), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.empty") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => {
3295
3304
  const target = archivedBatchTargetForGroup(group.key);
3296
3305
  const count = deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, target).length;
3297
3306
  return (0, react_jsx_runtime.jsxs)("section", {
@@ -3313,7 +3322,7 @@ window.__ModuleLoader__.load({
3313
3322
  closeLabel: t("close"),
3314
3323
  title: deleteDialogTitle,
3315
3324
  ...deleteDialogDescription === void 0 ? {} : { description: deleteDialogDescription },
3316
- footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeDelete, children: t("cancel") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: confirmDelete, children: deleteConfirmLabel })] }),
3325
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeDelete, children: t("cancel") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", className: "dsham_settingsDeleteConfirm", disabled: busy, onClick: confirmDelete, children: deleteConfirmLabel })] }),
3317
3326
  children: busy && (0, react_jsx_runtime.jsx)("div", { role: "status", children: deleteTarget?.kind === "batch" ? t("archives.deleteBatchPending") : t("deleteSession.pending") })
3318
3327
  })]
3319
3328
  });
@@ -3358,6 +3367,8 @@ window.__ModuleLoader__.load({
3358
3367
  "archived.notOpenable": "已归档,取消归档后可继续对话",
3359
3368
  "archives.title": "已归档的聊天",
3360
3369
  "archives.description": "管理已归档的会话。",
3370
+ "archives.viewProject": "GitHub",
3371
+ "archives.feedback": "问题反馈",
3361
3372
  "archives.empty": "暂无已归档会话",
3362
3373
  "archives.emptyFiltered": "没有匹配的已归档聊天",
3363
3374
  "archives.searchPlaceholder": "搜索已归档聊天",
@@ -3482,6 +3493,8 @@ window.__ModuleLoader__.load({
3482
3493
  "archived.notOpenable": "This session is archived. Unarchive it to continue the conversation.",
3483
3494
  "archives.title": "Archived chats",
3484
3495
  "archives.description": "Manage archived sessions.",
3496
+ "archives.viewProject": "GitHub",
3497
+ "archives.feedback": "Issues",
3485
3498
  "archives.empty": "No archived sessions.",
3486
3499
  "archives.emptyFiltered": "No archived chats match your filters.",
3487
3500
  "archives.searchPlaceholder": "Search archived chats",
package/lib/projcache.js CHANGED
@@ -25,17 +25,23 @@ const storedCheckpointRecord = checkpointRecord.extend({
25
25
  });
26
26
  const safeDomainInput = {
27
27
  name: "session_projcache_archive_manager",
28
- version: 1,
28
+ version: 2,
29
29
  tables: { sessions: domainTable(storedCheckpointRecord) },
30
30
  ...(projectionCacheDomainSpec.layout === "per-record" ? { layout: "per-record" } : {})
31
31
  };
32
32
  const safeProjectionCacheDomainSpec = defineDomain(safeDomainInput);
33
33
 
34
34
  /** The last official whole-file cache format used by DSH rc.2. */
35
+ const legacyCheckpointRecord = checkpointRecord.extend({
36
+ identity: checkpointIdentity.partial({
37
+ isSeeded: true,
38
+ inheritedEventCount: true
39
+ })
40
+ });
35
41
  const legacyProjectionCacheDomainSpec = defineDomain({
36
42
  name: "session_projcache",
37
43
  version: 3,
38
- tables: { sessions: domainTable(checkpointRecord) }
44
+ tables: { sessions: domainTable(legacyCheckpointRecord) }
39
45
  });
40
46
 
41
47
  function projectionCacheStorageKey(sessionId) {
@@ -107,12 +113,42 @@ async function readSourceRecords(ctx, spec, label) {
107
113
  }
108
114
  }
109
115
 
116
+ /**
117
+ * 旧版记录没有完整身份时,只能安全补齐未播种会话的零继承切点。
118
+ * 播种会话无法从头部反推出精确切点,宁可跳过并按需重建。
119
+ */
120
+ function normalizeLegacyRecord(record) {
121
+ if (record === null || typeof record !== "object" || record.identity === null || typeof record.identity !== "object") return void 0;
122
+ const identity = record.identity;
123
+ const inheritedEventCount = identity.inheritedEventCount;
124
+ const validOffset = Number.isSafeInteger(inheritedEventCount) && inheritedEventCount >= 0;
125
+ if (identity.isSeeded === true) {
126
+ if (!validOffset) return void 0;
127
+ return record;
128
+ }
129
+ if (identity.isSeeded !== void 0 && identity.isSeeded !== false) return void 0;
130
+ if (inheritedEventCount !== void 0 && inheritedEventCount !== 0) return void 0;
131
+ return {
132
+ ...record,
133
+ identity: {
134
+ ...identity,
135
+ isSeeded: false,
136
+ inheritedEventCount: 0
137
+ }
138
+ };
139
+ }
140
+
110
141
  async function importMissingRecords(ctx, target, records, label) {
111
142
  let imported = 0;
112
143
  for (const [sessionId, record] of records) {
113
144
  if (target.has(sessionId)) continue;
114
145
  try {
115
- await target.put(sessionId, record);
146
+ const normalized = normalizeLegacyRecord(record);
147
+ if (normalized === void 0) {
148
+ ctx.logger.warn(`archive-manager projcache: ${label} row "${sessionId}" import skipped because its lifecycle identity is incomplete`);
149
+ continue;
150
+ }
151
+ await target.put(sessionId, normalized);
116
152
  imported += 1;
117
153
  } catch (error) {
118
154
  ctx.logger.warn(`archive-manager projcache: ${label} row "${sessionId}" import failed: ${String(error)}`);
@@ -122,22 +158,20 @@ async function importMissingRecords(ctx, target, records, label) {
122
158
  }
123
159
 
124
160
  /**
125
- * Seed the compatibility domain without opening alpha.2's unsafe per-record
126
- * source while the v3 whole-file cache contains rows. An empty v3 source may
127
- * fall through to the current source; source-open failures stay fail-soft.
128
- * Imports are add-only: a newer compatibility-domain checkpoint always wins,
129
- * and a partially completed import resumes on the next startup.
161
+ * 合并官方新版逐会话缓存与旧版整文件缓存。归档管理器自身记录最高优先,
162
+ * 新版记录优先于旧版;任一来源失败时仍继续尝试另一个来源。
130
163
  */
131
164
  async function importPreviousProjectionCache(ctx, target) {
132
165
  const legacy = await readSourceRecords(ctx, legacyProjectionCacheDomainSpec, "legacy v3");
133
- if (!legacy.opened) return 0;
134
- if (legacy.records.length > 0) return importMissingRecords(ctx, target, legacy.records, "legacy v3");
135
166
  const sameSpec = projectionCacheDomainSpec.version === legacyProjectionCacheDomainSpec.version
136
167
  && projectionCacheDomainSpec.layout === legacyProjectionCacheDomainSpec.layout;
137
- if (sameSpec) return 0;
138
- const current = await readSourceRecords(ctx, projectionCacheDomainSpec, "current");
139
- if (!current.opened) return 0;
140
- return importMissingRecords(ctx, target, current.records, "current");
168
+ let imported = 0;
169
+ if (!sameSpec) {
170
+ const current = await readSourceRecords(ctx, projectionCacheDomainSpec, "current");
171
+ if (current.opened) imported += await importMissingRecords(ctx, target, current.records, "current");
172
+ }
173
+ if (legacy.opened) imported += await importMissingRecords(ctx, target, legacy.records, "legacy v3");
174
+ return imported;
141
175
  }
142
176
  //#region lib/types/index.js
143
177
  /**
package/lib/workspace.js CHANGED
@@ -279,14 +279,27 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
279
279
  if (cache === void 0 || typeof cache.cachedSnapshot !== "function" || typeof cache.put !== "function") return false;
280
280
  if (persistence === void 0 || typeof persistence.readFrom !== "function" || projections === void 0 || typeof projections.restore !== "function") return false;
281
281
  try {
282
- if (cache.cachedSnapshot(header) !== void 0) return false;
282
+ // 未播种会话的继承事件数恒为零,先查缓存可避免读取完整会话原文。
283
+ if (!header.isSeeded && cache.cachedSnapshot(header, 0) !== void 0) return false;
283
284
  const stored = await persistence.readFrom(header.id, 0);
284
285
  const meta = stored.meta ?? header;
285
- const restored = projections.restore({}, stored.events, 0, meta);
286
+ if (meta.isSeeded === true && stored.inheritedEventCount === void 0) {
287
+ this.ctx.logger.warn(`archive-manager: projection repair for seeded archived session "${header.id}" skipped because its inherited event count is unavailable`);
288
+ return false;
289
+ }
290
+ const inheritedEventCount = stored.inheritedEventCount ?? 0;
291
+ if (meta.isSeeded !== true && inheritedEventCount !== 0) {
292
+ this.ctx.logger.warn(`archive-manager: projection repair for unseeded archived session "${header.id}" skipped because its inherited event count is not zero`);
293
+ return false;
294
+ }
295
+ if (cache.cachedSnapshot(meta, inheritedEventCount) !== void 0) return false;
296
+ const restored = projections.restore({}, stored.events, 0, meta, inheritedEventCount);
286
297
  if (restored === void 0 || typeof restored !== "object" || restored.checkpoint === void 0) return false;
287
298
  await cache.put(header.id, {
288
299
  createdAt: meta.createdAt,
289
- ...meta.cwd === void 0 ? {} : { cwd: meta.cwd }
300
+ ...(meta.cwd === void 0 ? {} : { cwd: meta.cwd }),
301
+ isSeeded: meta.isSeeded ?? false,
302
+ inheritedEventCount
290
303
  }, restored.checkpoint);
291
304
  return true;
292
305
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-archive-manager",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "NPM-installable DSH Web plugin for managing archived sessions.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.21.0",
@@ -36,14 +36,15 @@
36
36
  "@deepseek-ai/dsh-client-connection": ">=0.1.0-rc.5 <0.2.0",
37
37
  "@deepseek-ai/dsh-client-locale": ">=0.1.0-rc.5 <0.2.0",
38
38
  "@deepseek-ai/dsh-client-runtime": ">=0.1.0-rc.5 <0.2.0",
39
+ "@deepseek-ai/dsh-client-store": ">=0.1.0-rc.5 <0.2.0",
39
40
  "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.0-rc.5 <0.2.0",
40
41
  "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.0-rc.5 <0.2.0",
41
42
  "@deepseek-ai/dsh-client-ui-sidebar": ">=0.1.0-rc.5 <0.2.0",
42
43
  "@deepseek-ai/dsh-client-ui-workspace": ">=0.1.0-rc.5 <0.2.0",
43
44
  "@deepseek-ai/dsh-session": ">=0.1.0-rc.5 <0.2.0",
44
45
  "@deepseek-ai/dsh-session-projection-cache": ">=0.1.0-rc.5 <0.2.0",
45
- "@deepseek-ai/dsh-storage-domain": ">=0.1.0-rc.5 <0.2.0",
46
46
  "@deepseek-ai/dsh-spill-local": ">=0.1.0-rc.5 <0.2.0",
47
+ "@deepseek-ai/dsh-storage-domain": ">=0.1.0-rc.5 <0.2.0",
47
48
  "@deepseek-ai/dsh-typert-protocol": ">=0.1.0-rc.5 <0.2.0",
48
49
  "@deepseek-ai/dsh-typert-registry": ">=0.1.0-rc.5 <0.2.0",
49
50
  "@deepseek-ai/dsh-workspace": ">=0.1.0-rc.5 <0.2.0"
@@ -51,20 +52,35 @@
51
52
  "peerDependenciesMeta": {
52
53
  "@deepseek-ai/dsh-client-runtime": {
53
54
  "optional": true
55
+ },
56
+ "@deepseek-ai/dsh-client-store": {
57
+ "optional": true
54
58
  }
55
59
  },
56
60
  "devDependencies": {
57
- "@deepseek-ai/cordis": "4.0.1",
58
- "@deepseek-ai/dsh-api-gateway": "0.1.0-rc.6",
59
- "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
60
- "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
61
- "@deepseek-ai/dsh-client-web-react": "0.1.0-rc.6",
62
- "@deepseek-ai/dsh-session-projection-cache": "0.1.0-rc.6",
63
- "@deepseek-ai/dsh-storage-domain": "0.1.0-rc.6",
64
- "@deepseek-ai/dsh-spill-local": "0.1.0-rc.6",
65
- "@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.6",
66
- "@deepseek-ai/dsh-typert-registry": "0.1.0-rc.6",
67
- "@deepseek-ai/dsh-workspace": "0.1.0-rc.6",
61
+ "@deepseek-ai/cordis": "4.0.2",
62
+ "@deepseek-ai/dsh-api-gateway": "0.1.2-alpha.5",
63
+ "@deepseek-ai/dsh-client-connection": "0.1.2-alpha.5",
64
+ "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.5",
65
+ "@deepseek-ai/dsh-client-store": "0.1.2-alpha.5",
66
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-alpha.5",
67
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.5",
68
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.1.2-alpha.5",
69
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.5",
70
+ "@deepseek-ai/dsh-client-ui-workspace": "0.1.2-alpha.5",
71
+ "@deepseek-ai/dsh-invariants": "0.1.2-alpha.5",
72
+ "@deepseek-ai/dsh-scope": "0.1.2-alpha.5",
73
+ "@deepseek-ai/dsh-session": "0.1.2-alpha.5",
74
+ "@deepseek-ai/dsh-session-persistence": "0.1.2-alpha.5",
75
+ "@deepseek-ai/dsh-session-projection": "0.1.2-alpha.5",
76
+ "@deepseek-ai/dsh-session-projection-cache": "0.1.2-alpha.5",
77
+ "@deepseek-ai/dsh-spill": "0.1.2-alpha.5",
78
+ "@deepseek-ai/dsh-spill-local": "0.1.2-alpha.5",
79
+ "@deepseek-ai/dsh-storage": "0.1.2-alpha.5",
80
+ "@deepseek-ai/dsh-storage-domain": "0.1.2-alpha.5",
81
+ "@deepseek-ai/dsh-typert-protocol": "0.1.2-alpha.5",
82
+ "@deepseek-ai/dsh-typert-registry": "0.1.2-alpha.5",
83
+ "@deepseek-ai/dsh-workspace": "0.1.2-alpha.5",
68
84
  "react": "18.3.1",
69
85
  "react-dom": "18.3.1"
70
86
  },