@michengai/dsh-archive-manager 1.1.12 → 1.1.13

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/lib/workspace.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { confirmColdCacheWrite } from "./cache-rebuild.js";
2
+ import { cacheIdentity, cacheRebuildInput, cacheRebuildInvocations } from "./cache-rebuild-contract.js";
1
3
  import { symbols } from "@deepseek-ai/cordis";
2
4
  import { record, errorMessage, errorCode } from "./contracts.js";
3
5
  import { discoveryInvocations, searchInputSchema, previewInputSchema, extractConversation, findContentMatch, previewConversation } from "./archive-discovery.js";
@@ -101,6 +103,8 @@ const ARCHIVE_REMOTE_METHODS = [
101
103
  "searchSessionContent",
102
104
  "previewArchivedSession",
103
105
  "diagnoseOrphanSubagents",
106
+ "diagnoseSessionCaches",
107
+ "rebuildSessionCaches",
104
108
  "cleanOrphanSubagents"
105
109
  ];
106
110
  function markRemoteMethod(instance, method) {
@@ -209,6 +213,7 @@ const orphanCleanResultSchema = {
209
213
  }
210
214
  };
211
215
  const ARCHIVE_MANAGER_INVOCATIONS = [
216
+ ...cacheRebuildInvocations(),
212
217
  ...favoriteInvocations(),
213
218
  ...discoveryInvocations(),
214
219
  {
@@ -583,6 +588,68 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
583
588
  return { items };
584
589
  }
585
590
  /** 只读句柄必须关闭,避免激活 Agent 或写入会话日志。 */
591
+ /** 普通冷会话(登记工作区、未分组及已归档)均支持重建,排除子代理、活跃及活动中会话。 */
592
+ async cacheRebuildEligible(header) {
593
+ if (header.origin !== void 0 || header.id.startsWith("im:") || this.ctx.get("sessions")?.get(header.id) !== void 0) return false;
594
+ const activity = await this.ctx.waterfall("workspace/session-activity", { sessionId: header.id }, () => Promise.resolve([]));
595
+ return activity.length === 0 && this.ctx.get("sessions")?.get(header.id) === void 0;
596
+ }
597
+ /** 检测仅读取头部与官方缓存,不读取会话正文。 */
598
+ usableSessionListCache(header) {
599
+ const snapshot = this.ctx.get("sessionProjectionCache")?.cachedSnapshot(header);
600
+ return snapshot !== void 0 && Object.hasOwn(snapshot.values, "title");
601
+ }
602
+ async diagnoseSessionCaches() {
603
+ const cache = this.ctx.get("sessionProjectionCache");
604
+ if (!cache) throw new Error("Official session projection cache is unavailable");
605
+ const items = [];
606
+ const headers = this.headers?.size ? Array.from(this.headers.values()) : await this.listStoredHeaders();
607
+ for (const header of headers) {
608
+ if (await this.cacheRebuildEligible(header) && !this.usableSessionListCache(header)) {
609
+ items.push({ sessionId: header.id, identity: cacheIdentity(header) });
610
+ }
611
+ }
612
+ return { count: items.length, items };
613
+ }
614
+ /** 定向读取持久化头部,优先单会话 stat 避免全量枚举,无 stat 时安全回退。 */
615
+ async readSessionHeaderDirect(sessionId) {
616
+ const persistence = this.ctx.sessionPersistence;
617
+ if (typeof persistence?.stat === "function") {
618
+ const snapshot = await persistence.stat(sessionId);
619
+ return snapshot?.header;
620
+ }
621
+ return (await this.listStoredHeaders()).find((h) => h.id === sessionId);
622
+ }
623
+ /** 与删除共用官方工作区队列;冷写入会同步加入官方域写入链,
624
+ * 回读超时也不会取消已入队的写入,因此后续删除会排在该写入之后。 */
625
+ async rebuildSessionCaches(input) {
626
+ const { items } = cacheRebuildInput.parse(input);
627
+ const result = { rebuiltSessionIds: [], skippedSessionIds: [], failures: [] };
628
+ for (const item of [...new Map(items.map((row) => [row.sessionId, row])).values()]) {
629
+ await this.enqueueOperation(async () => {
630
+ try {
631
+ const cache = this.ctx.get("sessionProjectionCache");
632
+ if (!cache) throw new Error("Official session projection cache is unavailable");
633
+ const header = await this.readSessionHeaderDirect(item.sessionId);
634
+ if (!header || cacheIdentity(header) !== item.identity || !await this.cacheRebuildEligible(header) || this.usableSessionListCache(header)) {
635
+ result.skippedSessionIds.push(item.sessionId);
636
+ return;
637
+ }
638
+ const source = await this.readStoredProjectionSource(this.ctx.sessionPersistence, item.sessionId);
639
+ const current = await this.readSessionHeaderDirect(item.sessionId);
640
+ if (!current || cacheIdentity(current) !== item.identity || cacheIdentity(source.meta) !== item.identity || !await this.cacheRebuildEligible(current)) {
641
+ result.skippedSessionIds.push(item.sessionId);
642
+ return;
643
+ }
644
+ await confirmColdCacheWrite(cache, source);
645
+ result.rebuiltSessionIds.push(item.sessionId);
646
+ } catch (error) {
647
+ result.failures.push({ sessionId: item.sessionId, message: (error instanceof Error ? error.message : String(error)).slice(0, 500) });
648
+ }
649
+ });
650
+ }
651
+ return result;
652
+ }
586
653
  async readStoredProjectionSource(persistence, sessionId) {
587
654
  const handle = await persistence.open(sessionId, "read");
588
655
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-archive-manager",
3
- "version": "1.1.12",
3
+ "version": "1.1.13",
4
4
  "description": "归档、恢复、收藏和永久删除会话,并诊断损坏的会话日志。 · Archive, restore, favorite, and permanently delete sessions, and diagnose damaged session logs.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.22.0",