@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/lib/client.js CHANGED
@@ -10,18 +10,43 @@
10
10
  function formatArchiveNavigationError(error, t) {
11
11
  return error instanceof ArchiveNavigationError ? t(`archives.${error.code}`) : String(error?.message ?? error);
12
12
  }
13
+ function retainInfoSnapshot(sessions, id) {
14
+ if (typeof sessions?.retainInfo !== "function") return;
15
+ const source = sessions.retainInfo(id);
16
+ return typeof source?.getSnapshot === "function" ? source.getSnapshot() : source;
17
+ }
18
+ function mainViewSessionId(list) {
19
+ const byId = list?.byId;
20
+ if (byId === void 0) return;
21
+ for (const session of Object.values(byId)) {
22
+ if ((session?.retainedBy?.mainView ?? 0) > 0) return session.id;
23
+ }
24
+ }
25
+ function currentSessionId(list) {
26
+ return mainViewSessionId(list) ?? list?.current;
27
+ }
28
+ function sessionIsCurrent(list, id, sessions) {
29
+ if (id === void 0 || id === "") return false;
30
+ if ((list?.byId?.[id]?.retainedBy?.mainView ?? 0) > 0) return true;
31
+ if ((retainInfoSnapshot(sessions, id)?.retainedBy?.mainView ?? 0) > 0) return true;
32
+ return list?.current === id;
33
+ }
13
34
  async function openArchivedConversation(actions, sessionId, restore, isActive = () => true) {
14
- if (restore) await actions.restore(sessionId);
35
+ if (restore) {
36
+ if (typeof actions.prepare === "function") await actions.prepare(sessionId);
37
+ if (!isActive()) return;
38
+ await actions.restore(sessionId);
39
+ }
15
40
  if (isActive()) await actions.open(sessionId);
16
41
  }
17
42
  function allowArchivedNavigation(navigation, sessions, workspaces, { onOpened = () => {
18
- }, warn = (...args) => console.warn(...args) } = {}) {
43
+ }, beginNavigation, warn = (...args) => console.warn(...args) } = {}) {
19
44
  let allowed;
20
45
  const original = navigation?.clearArchivedCurrent;
21
46
  const descriptor = navigation && Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent");
47
+ const viewing = (id) => sessionIsCurrent(sessions.list.getSnapshot(), id, sessions);
22
48
  const wrapped = function(...args) {
23
- const current = sessions.list.getSnapshot().current;
24
- if (current === allowed && current !== void 0 && workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
49
+ if (allowed !== void 0 && viewing(allowed) && workspaces.list.getSnapshot().archivedSessionIds.includes(allowed)) return false;
25
50
  allowed = void 0;
26
51
  return original.apply(this, args);
27
52
  };
@@ -42,8 +67,15 @@
42
67
  warn("archive-manager: \u5F52\u6863\u5BFC\u822A\u9002\u914D\u4E0D\u53EF\u7528\u6216\u5DF2\u88AB\u66FF\u6362\u3002");
43
68
  throw new ArchiveNavigationError("navigationUnavailable");
44
69
  }
45
- sessions.open(id);
46
- if (sessions.list.getSnapshot().current !== id) {
70
+ beginNavigation?.();
71
+ if (typeof sessions.retain === "function" && typeof navigation?.openSession === "function") navigation.openSession(id);
72
+ else if (typeof sessions.open === "function") sessions.open(id);
73
+ else if (typeof navigation?.openSession === "function") navigation.openSession(id);
74
+ else {
75
+ warn("archive-manager: \u5BBF\u4E3B\u672A\u63D0\u4F9B\u4F1A\u8BDD\u6253\u5F00\u63A5\u53E3\u3002");
76
+ throw new ArchiveNavigationError("navigationUnavailable");
77
+ }
78
+ if (!viewing(id)) {
47
79
  warn("archive-manager: \u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\u3002", { cleanupAvailable: typeof original === "function" });
48
80
  throw new ArchiveNavigationError("sessionNotRetained");
49
81
  }
@@ -468,12 +500,6 @@
468
500
  return value;
469
501
  }
470
502
  };
471
- const workspaceIdSchema = {
472
- parse(value) {
473
- if (typeof value !== "string" || value.length === 0) throw new TypeError(`workspaceId must be a non-empty string, got ${String(value)}`);
474
- return value;
475
- }
476
- };
477
503
  const archivedSetSchema = {
478
504
  parse(value) {
479
505
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
@@ -497,28 +523,6 @@
497
523
  throw new TypeError("target.scope must be all, ungrouped, workspace with a non-empty workspaceId, or sessions with non-empty sessionIds");
498
524
  }
499
525
  };
500
- const unarchivedBatchSchema = {
501
- parse(value) {
502
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
503
- if (!Array.isArray(value.archivedSessionIds) || value.archivedSessionIds.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIds must be a string array");
504
- if (!Array.isArray(value.unarchivedSessionIds) || value.unarchivedSessionIds.some((id) => typeof id !== "string")) throw new TypeError("unarchivedSessionIds must be a string array");
505
- return value;
506
- }
507
- };
508
- const archiveSessionIdsSchema = {
509
- parse(value) {
510
- if (!Array.isArray(value)) throw new TypeError("sessionIds must be an array");
511
- return [...new Set(value.map((id) => sessionIdSchema.parse(id)))];
512
- }
513
- };
514
- const archivedWorkspaceBatchSchema = {
515
- parse(value) {
516
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
517
- if (!Array.isArray(value.archivedSessionIds) || value.archivedSessionIds.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIds must be a string array");
518
- if (!Array.isArray(value.archivedSessionIdsAdded) || value.archivedSessionIdsAdded.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIdsAdded must be a string array");
519
- return value;
520
- }
521
- };
522
526
  const deletedBatchSchema = {
523
527
  parse(value) {
524
528
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
@@ -537,6 +541,9 @@
537
541
  return value;
538
542
  }
539
543
  };
544
+ function strictCodec(typeSymbol, schema) {
545
+ return { mode: "strict", typeSymbol, create: () => schema, schema };
546
+ }
540
547
  const ARCHIVE_MANAGER_REMOTE = {
541
548
  package: "@michengai/dsh-archive-manager",
542
549
  descriptors: [
@@ -550,13 +557,9 @@
550
557
  name: "sessionId",
551
558
  wire: "sessionId",
552
559
  source: "json",
553
- codec: { mode: "strict", typeSymbol: "@deepseek-ai/dsh-session/types#SessionId", schema: sessionIdSchema }
560
+ codec: strictCodec("@deepseek-ai/dsh-session/types#SessionId", sessionIdSchema)
554
561
  }],
555
- result: {
556
- mode: "strict",
557
- typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedSessionIds",
558
- schema: archivedSetSchema
559
- },
562
+ result: strictCodec("@michengai/dsh-archive-manager/types#ArchivedSessionIds", archivedSetSchema),
560
563
  sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
561
564
  },
562
565
  {
@@ -569,70 +572,9 @@
569
572
  name: "sessionId",
570
573
  wire: "sessionId",
571
574
  source: "json",
572
- codec: { mode: "strict", typeSymbol: "@deepseek-ai/dsh-session/types#SessionId", schema: sessionIdSchema }
573
- }],
574
- result: {
575
- mode: "strict",
576
- typeSymbol: "@michengai/dsh-archive-manager/types#Deleted",
577
- schema: deletedSchema
578
- },
579
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
580
- },
581
- {
582
- id: "@michengai/dsh-archive-manager#workspaceRegistry/unarchiveSessions",
583
- service: "workspaceRegistry",
584
- namespace: "workspaceRegistry",
585
- method: "unarchiveSessions",
586
- invocation: { kind: "direct" },
587
- parameters: [{
588
- name: "target",
589
- wire: "target",
590
- source: "json",
591
- codec: { mode: "strict", typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedBatchTarget", schema: archivedBatchTargetSchema }
592
- }],
593
- result: {
594
- mode: "strict",
595
- typeSymbol: "@michengai/dsh-archive-manager/types#UnarchivedBatch",
596
- schema: unarchivedBatchSchema
597
- },
598
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
599
- },
600
- {
601
- id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveSessions",
602
- service: "workspaceRegistry",
603
- namespace: "workspaceRegistry",
604
- method: "archiveSessions",
605
- invocation: { kind: "direct" },
606
- parameters: [{
607
- name: "sessionIds",
608
- wire: "sessionIds",
609
- source: "json",
610
- codec: { mode: "strict", typeSymbol: "@michengai/dsh-archive-manager/types#ArchiveSessionIds", schema: archiveSessionIdsSchema }
575
+ codec: strictCodec("@deepseek-ai/dsh-session/types#SessionId", sessionIdSchema)
611
576
  }],
612
- result: {
613
- mode: "strict",
614
- typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedWorkspaceBatch",
615
- schema: archivedWorkspaceBatchSchema
616
- },
617
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
618
- },
619
- {
620
- id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveWorkspaceSessions",
621
- service: "workspaceRegistry",
622
- namespace: "workspaceRegistry",
623
- method: "archiveWorkspaceSessions",
624
- invocation: { kind: "direct" },
625
- parameters: [{
626
- name: "workspaceId",
627
- wire: "workspaceId",
628
- source: "json",
629
- codec: { mode: "strict", typeSymbol: "@deepseek-ai/dsh-workspace/types#WorkspaceId", schema: workspaceIdSchema }
630
- }],
631
- result: {
632
- mode: "strict",
633
- typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedWorkspaceBatch",
634
- schema: archivedWorkspaceBatchSchema
635
- },
577
+ result: strictCodec("@michengai/dsh-archive-manager/types#Deleted", deletedSchema),
636
578
  sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
637
579
  },
638
580
  {
@@ -645,13 +587,9 @@
645
587
  name: "target",
646
588
  wire: "target",
647
589
  source: "json",
648
- codec: { mode: "strict", typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedBatchTarget", schema: archivedBatchTargetSchema }
590
+ codec: strictCodec("@michengai/dsh-archive-manager/types#ArchivedBatchTarget", archivedBatchTargetSchema)
649
591
  }],
650
- result: {
651
- mode: "strict",
652
- typeSymbol: "@michengai/dsh-archive-manager/types#DeletedBatch",
653
- schema: deletedBatchSchema
654
- },
592
+ result: strictCodec("@michengai/dsh-archive-manager/types#DeletedBatch", deletedBatchSchema),
655
593
  sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
656
594
  },
657
595
  {
@@ -661,11 +599,7 @@
661
599
  method: "archivedSessionMetadata",
662
600
  invocation: { kind: "direct" },
663
601
  parameters: [],
664
- result: {
665
- mode: "strict",
666
- typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedSessionMetadata",
667
- schema: archivedSessionMetadataSchema
668
- },
602
+ result: strictCodec("@michengai/dsh-archive-manager/types#ArchivedSessionMetadata", archivedSessionMetadataSchema),
669
603
  sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
670
604
  }
671
605
  ]
@@ -711,7 +645,23 @@
711
645
  return indexed;
712
646
  }
713
647
  const FLAT_SESSION_ORDER_KEY = "__flat_session_order__";
648
+ const WORKSPACE_VIEW_PERSIST_KEY = "dsh.archive-manager.workspace.view.v1";
649
+ const LEGACY_WORKSPACE_VIEW_PERSIST_KEY = "dsh.workspace.view.v5";
650
+ function migrateWorkspaceViewPersist(storage) {
651
+ const target = storage ?? globalThis.localStorage;
652
+ if (target == null || typeof target.getItem !== "function" || typeof target.setItem !== "function") return false;
653
+ try {
654
+ if (target.getItem(WORKSPACE_VIEW_PERSIST_KEY) != null) return false;
655
+ const legacy = target.getItem(LEGACY_WORKSPACE_VIEW_PERSIST_KEY);
656
+ if (legacy == null) return false;
657
+ target.setItem(WORKSPACE_VIEW_PERSIST_KEY, legacy);
658
+ return true;
659
+ } catch {
660
+ return false;
661
+ }
662
+ }
714
663
  function createWorkspaceViewStore() {
664
+ migrateWorkspaceViewPersist();
715
665
  return (0, _deepseek_ai_dsh_client_store.defineStore)({
716
666
  init: () => ({
717
667
  groupBy: "workspace",
@@ -721,7 +671,7 @@
721
671
  sessionOrderByAccount: {},
722
672
  sessionUpdatedAtByAccount: {}
723
673
  }),
724
- persist: "dsh.workspace.view.v5",
674
+ persist: WORKSPACE_VIEW_PERSIST_KEY,
725
675
  actions: {
726
676
  setGroupBy: (d, mode) => {
727
677
  d.groupBy = mode;
@@ -754,7 +704,7 @@
754
704
  function GithubMark16() {
755
705
  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" }) });
756
706
  }
757
- function ArchivedSessionsSection({ archiveSessions, sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, openConversation, viewState, close, t }) {
707
+ function ArchivedSessionsSection({ archiveSessions, sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, openConversation, focusSessionWorkspace, viewState, close, t }) {
758
708
  const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
759
709
  const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
760
710
  const [archiveTab, setArchiveTab] = (0, react.useState)("archived");
@@ -799,13 +749,29 @@
799
749
  navigationPending.current = true;
800
750
  setBusy(true);
801
751
  setError(null);
752
+ let restored = false;
802
753
  try {
803
- await openArchivedConversation({ restore: unarchiveSession, open: async (id) => {
804
- await openConversation(id);
805
- if (navigationActive.current) close?.();
806
- } }, session.id, restore, () => navigationActive.current);
754
+ await openArchivedConversation({
755
+ prepare: restore ? async (id) => {
756
+ await focusSessionWorkspace?.(id);
757
+ } : void 0,
758
+ restore: async (id) => {
759
+ await unarchiveSession(id);
760
+ restored = true;
761
+ },
762
+ open: async (id) => {
763
+ await openConversation(id);
764
+ if (navigationActive.current) close?.();
765
+ }
766
+ }, session.id, restore, () => navigationActive.current);
807
767
  } catch (reason) {
808
- if (navigationActive.current) setError(formatArchiveNavigationError(reason, t));
768
+ if (!navigationActive.current) return;
769
+ if (restored) {
770
+ setArchiveTab("unarchived");
771
+ const group = groups.find((item) => item.sessions.some((row) => row.id === session.id));
772
+ if (group !== void 0) setProject(group.key);
773
+ }
774
+ setError(formatArchiveNavigationError(reason, t));
809
775
  } finally {
810
776
  navigationPending.current = false;
811
777
  if (navigationActive.current) setBusy(false);
@@ -836,7 +802,10 @@
836
802
  setNotice(null);
837
803
  try {
838
804
  const result = await archiveSessions(archiveTarget);
839
- setSelectedSessionIds((ids) => ids.filter((id) => !result.archivedSessionIds.includes(id)));
805
+ setSelectedSessionIds([]);
806
+ setArchiveTab("archived");
807
+ setProject("all");
808
+ setQuery("");
840
809
  setNotice(t("archives.archiveSuccess", { n: result.archivedSessionIdsAdded.length }));
841
810
  setArchiveTarget(null);
842
811
  setArchiveGroup(null);
@@ -1150,12 +1119,12 @@
1150
1119
  const summary = list.byId[id];
1151
1120
  if (summary === void 0) continue;
1152
1121
  accounted.add(id);
1153
- if (!sessionVisible(summary, list.current, archived, showArchived)) continue;
1122
+ if (!sessionVisible(summary, currentSessionId(list), archived, showArchived)) continue;
1154
1123
  members.push(summary);
1155
1124
  }
1156
1125
  groups.push(buildGroup(workspace.workspaceId, workspace.workspaceId, workspace.path, Date.parse(workspace.createdAt), workspace.title, members, "account"));
1157
1126
  }
1158
- const stray = list.ids.map((id) => list.byId[id]).filter((s) => s !== void 0 && !accounted.has(s.id) && sessionVisible(s, list.current, archived, showArchived));
1127
+ const stray = list.ids.map((id) => list.byId[id]).filter((s) => s !== void 0 && !accounted.has(s.id) && sessionVisible(s, currentSessionId(list), archived, showArchived));
1159
1128
  if (stray.length > 0) groups.push(buildGroup("", void 0, void 0, void 0, UNGROUPED_LABEL, ungroupedOrder === void 0 ? stray : orderedUngrouped(stray, ungroupedOrder), ungroupedOrder === void 0 ? "recency" : "account"));
1160
1129
  return groups;
1161
1130
  }
@@ -1188,18 +1157,49 @@
1188
1157
  return visiblePendingKind(kind);
1189
1158
  }
1190
1159
  const EMPTY_PENDING_INTERACTIONS = /* @__PURE__ */ new Map();
1160
+ const EMPTY_COMPLETED_SESSIONS = /* @__PURE__ */ new Set();
1161
+ const EMPTY_SESSION_STATUS = /* @__PURE__ */ new Map();
1191
1162
  function useEmptySessionPendingInteraction(selector) {
1192
1163
  return selector(EMPTY_PENDING_INTERACTIONS);
1193
1164
  }
1165
+ function useEmptySessionStatus(selector) {
1166
+ return selector(EMPTY_SESSION_STATUS);
1167
+ }
1168
+ function pendingFacts(pendingInteractions) {
1169
+ if (pendingInteractions instanceof Map || pendingInteractions == null) {
1170
+ return { pending: pendingInteractions ?? EMPTY_PENDING_INTERACTIONS, completed: EMPTY_COMPLETED_SESSIONS };
1171
+ }
1172
+ return {
1173
+ pending: pendingInteractions.pending ?? EMPTY_PENDING_INTERACTIONS,
1174
+ completed: pendingInteractions.completed ?? EMPTY_COMPLETED_SESSIONS
1175
+ };
1176
+ }
1177
+ function factsFromSessionStatus(status) {
1178
+ const pending = /* @__PURE__ */ new Map();
1179
+ const completed = /* @__PURE__ */ new Set();
1180
+ if (status instanceof Map) {
1181
+ for (const [id, entry] of status) {
1182
+ if (entry?.pendingInteraction !== void 0) pending.set(id, entry.pendingInteraction);
1183
+ if (entry?.completionUnread === true) completed.add(id);
1184
+ }
1185
+ }
1186
+ return { pending, completed };
1187
+ }
1188
+ function useSessionUiFacts(usePending, useStatus, preferStatus) {
1189
+ const pending = usePending((s) => s);
1190
+ const status = useStatus((s) => s);
1191
+ return preferStatus ? factsFromSessionStatus(status) : { pending, completed: EMPTY_COMPLETED_SESSIONS };
1192
+ }
1194
1193
  function sessionNode(s, descendants, archived, pendingInteractions) {
1195
- const pendingInteraction = pendingInteractionForSession(s, pendingInteractions);
1194
+ const facts = pendingFacts(pendingInteractions);
1195
+ const pendingInteraction = pendingInteractionForSession(s, facts.pending);
1196
1196
  return {
1197
1197
  id: s.id,
1198
1198
  title: sessionTitle(s),
1199
1199
  blank: s.blank,
1200
1200
  running: s.running,
1201
1201
  runningSubagentCount: descendants.get(s.id)?.runningCount ?? 0,
1202
- completed: s.completed === true,
1202
+ completed: s.completed === true || facts.completed.has(s.id),
1203
1203
  updatedAt: s.updatedAt,
1204
1204
  archived: archived.has(s.id),
1205
1205
  ...pendingInteraction === void 0 ? {} : { pendingInteraction }
@@ -1209,7 +1209,8 @@
1209
1209
  const archived = new Set(archivedSessionIds);
1210
1210
  const expandedGroups = new Set(view.expandedGroups);
1211
1211
  const descendants = indexSubagentDescendants(list.byId);
1212
- const currentGroup = list.current === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(list.current))?.workspaceId ?? "";
1212
+ const current = currentSessionId(list);
1213
+ const currentGroup = current === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(current))?.workspaceId ?? "";
1213
1214
  const groups = [];
1214
1215
  for (const g of groupByWorkspace(list, workspaces, archived, view.ungroupedOrder, view.showArchived)) {
1215
1216
  const expanded = expandedGroups.has(g.key);
@@ -1233,7 +1234,7 @@
1233
1234
  const rows = [];
1234
1235
  for (const id of list.ids) {
1235
1236
  const s = list.byId[id];
1236
- if (s === void 0 || !sessionVisible(s, list.current, archived, showArchived)) continue;
1237
+ if (s === void 0 || !sessionVisible(s, currentSessionId(list), archived, showArchived)) continue;
1237
1238
  rows.push(s);
1238
1239
  }
1239
1240
  rows.sort(byRecency);
@@ -1255,7 +1256,7 @@
1255
1256
  const local = [];
1256
1257
  for (const id of list.ids) {
1257
1258
  const summary = list.byId[id];
1258
- if (summary === void 0 || summary.blank || !sessionVisible(summary, list.current, archived, showArchived)) continue;
1259
+ if (summary === void 0 || summary.blank || !sessionVisible(summary, currentSessionId(list), archived, showArchived)) continue;
1259
1260
  if (sessionTitle(summary).toLowerCase().includes(q) || labelOf(summary).toLowerCase().includes(q)) local.push(summary);
1260
1261
  }
1261
1262
  local.sort(byRecency);
@@ -1269,12 +1270,13 @@
1269
1270
  for (const summary of local) include(summary);
1270
1271
  for (const item of content.items) {
1271
1272
  const summary = list.byId[item.sessionId];
1272
- if (summary !== void 0 && !summary.blank && sessionVisible(summary, list.current, archived, showArchived)) include(summary);
1273
+ if (summary !== void 0 && !summary.blank && sessionVisible(summary, currentSessionId(list), archived, showArchived)) include(summary);
1273
1274
  }
1275
+ const facts = pendingFacts(pendingInteractions);
1274
1276
  return {
1275
1277
  items: ordered.slice(0, limit).map((summary) => {
1276
1278
  const match = contentBySession.get(summary.id);
1277
- const pendingInteraction = pendingInteractionForSession(summary, pendingInteractions);
1279
+ const pendingInteraction = pendingInteractionForSession(summary, facts.pending);
1278
1280
  return {
1279
1281
  id: summary.id,
1280
1282
  title: sessionTitle(summary),
@@ -1283,7 +1285,7 @@
1283
1285
  runningSubagentCount: descendants.get(summary.id)?.runningCount ?? 0,
1284
1286
  archived: archived.has(summary.id),
1285
1287
  ...pendingInteraction === void 0 ? {} : { pendingInteraction },
1286
- completed: summary.completed === true,
1288
+ completed: summary.completed === true || facts.completed.has(summary.id),
1287
1289
  ...match === void 0 ? {} : { snippet: match.snippet }
1288
1290
  };
1289
1291
  }),
@@ -1629,7 +1631,7 @@
1629
1631
  })]
1630
1632
  });
1631
1633
  }
1632
- function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onUnarchive, onDeleteSession, drag, flat = false, t }) {
1634
+ function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onUnarchive, onDeleteSession, onReveal, drag, flat = false, t }) {
1633
1635
  const row = node;
1634
1636
  const title = displayTitle(node, t);
1635
1637
  const selected = node.id === currentId;
@@ -1637,6 +1639,12 @@
1637
1639
  const statuses = sessionStatuses(node, t);
1638
1640
  const showStatus = statuses[0].state !== "done" || row.completed;
1639
1641
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
1642
+ const rowRef = (0, react.useRef)(null);
1643
+ (0, react.useEffect)(() => {
1644
+ if (onReveal === void 0) return;
1645
+ rowRef.current?.scrollIntoView({ block: "nearest" });
1646
+ onReveal();
1647
+ }, [onReveal]);
1640
1648
  const sessionMenuItems = archived ? [
1641
1649
  {
1642
1650
  id: "unarchive",
@@ -1674,6 +1682,7 @@
1674
1682
  ];
1675
1683
  return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
1676
1684
  anchor: (0, react_jsx_runtime.jsxs)("div", {
1685
+ ref: rowRef,
1677
1686
  className: clsx(Rows_module_css_default.sessionRow, selected && Rows_module_css_default.selected, menuOpen && Rows_module_css_default.menuOpen, flat && !showStatus && Rows_module_css_default.flatSessionRowWithoutStatus, archived && ARCHIVED_CLASSES.row, drag?.marker === "before" && Rows_module_css_default.dropBefore, drag?.marker === "after" && Rows_module_css_default.dropAfter),
1678
1687
  role: "treeitem",
1679
1688
  "aria-selected": selected,
@@ -2123,10 +2132,10 @@
2123
2132
  const rect = e.currentTarget.getBoundingClientRect();
2124
2133
  return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
2125
2134
  }
2126
- function SessionTree({ useSessions, useSessionPendingInteraction, startSession, open, forkSession, workspaces, archivedSessionIds, showArchived, onRenameRequest, onArchiveRequest, onDeleteRequest, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, insertWorkspaceBefore, insertSessionBefore, orderBy, groupExpansion, setGroupExpanded, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t }) {
2135
+ function SessionTree({ useSessions, useSessionPendingInteraction, useSessionStatus, preferSessionStatus, startSession, open, forkSession, workspaces, archivedSessionIds, showArchived, onRenameRequest, onArchiveRequest, onDeleteRequest, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, insertWorkspaceBefore, insertSessionBefore, orderBy, groupExpansion, setGroupExpanded, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t, revealSessionId, onSessionRevealed }) {
2127
2136
  const list = useSessions((s) => s);
2128
- const pendingInteractions = useSessionPendingInteraction((s) => s);
2129
- const current = list.current;
2137
+ const pendingInteractions = useSessionUiFacts(useSessionPendingInteraction, useSessionStatus, preferSessionStatus);
2138
+ const current = currentSessionId(list);
2130
2139
  const [expandedSessionGroups, setExpandedSessionGroups] = (0, react.useState)([]);
2131
2140
  const [drag, setDrag] = (0, react.useState)(null);
2132
2141
  const sessionDropCommitted = (0, react.useRef)(false);
@@ -2135,6 +2144,7 @@
2135
2144
  const previousOrderBy = (0, react.useRef)(orderBy);
2136
2145
  useNativeDragAcceptance(drag !== null || workspaceDrag !== null);
2137
2146
  const currentGroup = current === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(current))?.workspaceId ?? "";
2147
+ const revealGroup = revealSessionId === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(revealSessionId))?.workspaceId ?? "";
2138
2148
  (0, react.useEffect)(() => {
2139
2149
  if (current === void 0 || currentGroup === void 0 || Object.hasOwn(groupExpansion, currentGroup)) return;
2140
2150
  setGroupExpanded(currentGroup, true);
@@ -2144,6 +2154,14 @@
2144
2154
  setGroupExpanded,
2145
2155
  groupExpansion
2146
2156
  ]);
2157
+ (0, react.useEffect)(() => {
2158
+ if (revealGroup === void 0 || groupExpansion[revealGroup] === true) return;
2159
+ setGroupExpanded(revealGroup, true);
2160
+ }, [
2161
+ groupExpansion,
2162
+ revealGroup,
2163
+ setGroupExpanded
2164
+ ]);
2147
2165
  const expandedGroups = (0, react.useMemo)(() => Object.entries(groupExpansion).filter(([, expanded]) => expanded).map(([key]) => key), [groupExpansion]);
2148
2166
  const ungroupedSessionIds = (0, react.useMemo)(() => {
2149
2167
  const accounted = new Set(workspaces.flatMap((workspace) => workspace.sessionIds));
@@ -2170,7 +2188,7 @@
2170
2188
  orderBy,
2171
2189
  sortByRecency: orderBy === "updated" && (previousOrder === void 0 || switchedToUpdated)
2172
2190
  });
2173
- if (next.changed) syncSessionOrderAccount(key, next.order.map((id) => id), next.updatedAt);
2191
+ if (next.changed && typeof syncSessionOrderAccount === "function") syncSessionOrderAccount(key, next.order.map((id) => id), next.updatedAt);
2174
2192
  }
2175
2193
  }, [
2176
2194
  list,
@@ -2205,6 +2223,17 @@
2205
2223
  expandedGroups,
2206
2224
  sessionOrderByAccount
2207
2225
  ]);
2226
+ (0, react.useEffect)(() => {
2227
+ if (revealSessionId === void 0 || revealGroup === void 0) return;
2228
+ const group = groups.find((candidate) => candidate.key === revealGroup);
2229
+ if (group === void 0 || !group.expanded || !group.sessions.some((row) => row.id === revealSessionId)) return;
2230
+ if (group.sessions.slice(0, COLLAPSED_SESSION_LIMIT).some((row) => row.id === revealSessionId)) return;
2231
+ setExpandedSessionGroups((keys) => keys.includes(revealGroup) ? keys : [...keys, revealGroup]);
2232
+ }, [
2233
+ groups,
2234
+ revealGroup,
2235
+ revealSessionId
2236
+ ]);
2208
2237
  const now = Date.now();
2209
2238
  const commitSessionDrag = (activeDrag, over) => {
2210
2239
  if (sessionDropCommitted.current) return;
@@ -2351,6 +2380,9 @@
2351
2380
  onArchive: onSessionArchive,
2352
2381
  onUnarchive: onSessionUnarchive,
2353
2382
  onDeleteSession: onSessionDelete,
2383
+ onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
2384
+ onSessionRevealed?.(node.id);
2385
+ } : void 0,
2354
2386
  drag: {
2355
2387
  start: () => {
2356
2388
  sessionDropCommitted.current = false;
@@ -2404,9 +2436,10 @@
2404
2436
  ]
2405
2437
  });
2406
2438
  }
2407
- function FlatList({ useSessions, useSessionPendingInteraction, open, forkSession, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, archivedSessionIds, showArchived, orderBy, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t }) {
2439
+ function FlatList({ useSessions, useSessionPendingInteraction, useSessionStatus, preferSessionStatus, open, forkSession, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, archivedSessionIds, showArchived, orderBy, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t, revealSessionId, onSessionRevealed }) {
2408
2440
  const list = useSessions((s) => s);
2409
- const pendingInteractions = useSessionPendingInteraction((s) => s);
2441
+ const pendingInteractions = useSessionUiFacts(useSessionPendingInteraction, useSessionStatus, preferSessionStatus);
2442
+ const current = currentSessionId(list);
2410
2443
  const baseRows = (0, react.useMemo)(() => deriveFlat(list, archivedSessionIds, pendingInteractions, showArchived), [
2411
2444
  list,
2412
2445
  archivedSessionIds,
@@ -2482,7 +2515,7 @@
2482
2515
  const active = drag !== null;
2483
2516
  return (0, react_jsx_runtime.jsx)(SessionNodeItem, {
2484
2517
  node,
2485
- currentId: list.current,
2518
+ currentId: current,
2486
2519
  now,
2487
2520
  onOpen: open,
2488
2521
  onRename: onSessionRename,
@@ -2494,6 +2527,9 @@
2494
2527
  onArchive: onSessionArchive,
2495
2528
  onUnarchive: onSessionUnarchive,
2496
2529
  onDeleteSession: onSessionDelete,
2530
+ onReveal: node.id === revealSessionId ? () => {
2531
+ onSessionRevealed?.(node.id);
2532
+ } : void 0,
2497
2533
  flat: true,
2498
2534
  drag: {
2499
2535
  start: () => {
@@ -2507,8 +2543,8 @@
2507
2543
  active,
2508
2544
  marker: active && drag.over?.id === node.id ? drag.over.half : null,
2509
2545
  hover: (half) => {
2510
- setDrag((current) => current === null ? current : {
2511
- ...current,
2546
+ setDrag((current2) => current2 === null ? current2 : {
2547
+ ...current2,
2512
2548
  over: {
2513
2549
  id: node.id,
2514
2550
  half
@@ -2533,9 +2569,9 @@
2533
2569
  }), (0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })]
2534
2570
  });
2535
2571
  }
2536
- function SearchResults({ useSessions, useSessionPendingInteraction, open, workspaces, archivedSessionIds, showArchived, query, remote, resultLimit, t }) {
2572
+ function SearchResults({ useSessions, useSessionPendingInteraction, useSessionStatus, preferSessionStatus, open, workspaces, archivedSessionIds, showArchived, query, remote, resultLimit, t }) {
2537
2573
  const list = useSessions((s) => s);
2538
- const pendingInteractions = useSessionPendingInteraction((s) => s);
2574
+ const pendingInteractions = useSessionUiFacts(useSessionPendingInteraction, useSessionStatus, preferSessionStatus);
2539
2575
  const ungroupedLabel = t("group.ungrouped");
2540
2576
  const currentRemote = remote.query === query ? remote : {
2541
2577
  query,
@@ -2567,7 +2603,7 @@
2567
2603
  "aria-label": t("search.results.aria"),
2568
2604
  children: results.items.map((result) => (0, react_jsx_runtime.jsx)(SearchResultItem, {
2569
2605
  result,
2570
- currentId: list.current,
2606
+ currentId: currentSessionId(list),
2571
2607
  onOpen: open,
2572
2608
  t
2573
2609
  }, result.id))
@@ -2594,8 +2630,22 @@
2594
2630
  }), (0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })]
2595
2631
  });
2596
2632
  }
2597
- function WorkspaceBrowser({ wide, expandSidebar, useSessions, useSessionPendingInteraction, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, archiveWorkspaceSessions, unarchiveSession, deleteSession, insertSessionBefore, createWorkspace, searchSessions, searchResultLimit, useDirectoryFlow, renderSlot, t }) {
2633
+ const sidebarReveal = {
2634
+ listeners: /* @__PURE__ */ new Set(),
2635
+ request(sessionId) {
2636
+ for (const listener of this.listeners) listener(sessionId);
2637
+ },
2638
+ subscribe(listener) {
2639
+ this.listeners.add(listener);
2640
+ return () => {
2641
+ this.listeners.delete(listener);
2642
+ };
2643
+ }
2644
+ };
2645
+ function WorkspaceBrowser({ wide, expandSidebar, useSessions, useSessionPendingInteraction, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, archiveWorkspaceSessions, unarchiveSession, deleteSession, insertSessionBefore, createWorkspace, searchSessions, searchResultLimit, useDirectoryFlow, renderSlot, t }) {
2646
+ const preferSessionStatus = typeof useSessionStatus === "function";
2598
2647
  const useEffectiveSessionPendingInteraction = useSessionPendingInteraction ?? useEmptySessionPendingInteraction;
2648
+ const useEffectiveSessionStatus = useSessionStatus ?? useEmptySessionStatus;
2599
2649
  const workspaces = useWorkspaces((state) => state.items);
2600
2650
  const workspacePhase = useWorkspaces((state) => state.phase);
2601
2651
  const archivedSessionIds = useWorkspaces((state) => state.archivedSessionIds);
@@ -2608,6 +2658,11 @@
2608
2658
  const sessionOrderByAccount = useStore((s) => s.sessionOrderByAccount);
2609
2659
  const sessionUpdatedAtByAccount = useStore((s) => s.sessionUpdatedAtByAccount);
2610
2660
  const archivedSet = (0, react.useMemo)(() => new Set(archivedSessionIds), [archivedSessionIds]);
2661
+ const [revealSessionId, setRevealSessionId] = (0, react.useState)(void 0);
2662
+ (0, react.useEffect)(() => sidebarReveal.subscribe((sessionId) => setRevealSessionId(sessionId)), []);
2663
+ const acknowledgeSessionReveal = (sessionId) => {
2664
+ setRevealSessionId((current) => current === sessionId ? void 0 : current);
2665
+ };
2611
2666
  const [archivedToast, setArchivedToast] = (0, react.useState)(null);
2612
2667
  const archivedToastSeq = (0, react.useRef)(0);
2613
2668
  const showArchivedToast2 = (text) => {
@@ -3015,7 +3070,14 @@
3015
3070
  children: wide && (normalizedQuery !== "" ? (0, react_jsx_runtime.jsx)(SearchResults, {
3016
3071
  useSessions,
3017
3072
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
3018
- open: guardedOpen,
3073
+ useSessionStatus: useEffectiveSessionStatus,
3074
+ preferSessionStatus,
3075
+ open: (sessionId) => {
3076
+ setRevealSessionId(sessionId);
3077
+ setQuery("");
3078
+ setSearchExpanded(false);
3079
+ guardedOpen(sessionId);
3080
+ },
3019
3081
  workspaces,
3020
3082
  archivedSessionIds,
3021
3083
  showArchived,
@@ -3026,7 +3088,11 @@
3026
3088
  }) : groupBy === "flat" ? (0, react_jsx_runtime.jsx)(FlatList, {
3027
3089
  useSessions,
3028
3090
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
3091
+ useSessionStatus: useEffectiveSessionStatus,
3092
+ preferSessionStatus,
3029
3093
  open: guardedOpen,
3094
+ revealSessionId,
3095
+ onSessionRevealed: acknowledgeSessionReveal,
3030
3096
  forkSession,
3031
3097
  onSessionRename,
3032
3098
  onSessionArchive,
@@ -3046,6 +3112,10 @@
3046
3112
  }) : (0, react_jsx_runtime.jsx)(SessionTree, {
3047
3113
  useSessions,
3048
3114
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
3115
+ useSessionStatus: useEffectiveSessionStatus,
3116
+ preferSessionStatus,
3117
+ revealSessionId,
3118
+ onSessionRevealed: acknowledgeSessionReveal,
3049
3119
  onSessionRename,
3050
3120
  onSessionArchive,
3051
3121
  onSessionUnarchive,
@@ -3908,8 +3978,7 @@
3908
3978
  "workspaces",
3909
3979
  "locale",
3910
3980
  "remote",
3911
- "typert",
3912
- ...hasSplitClientStore ? ["uiWorkspace"] : []
3981
+ "typert"
3913
3982
  ];
3914
3983
  function bindObservable(source) {
3915
3984
  return {
@@ -3917,18 +3986,58 @@
3917
3986
  subscribe: source.subscribe.bind(source)
3918
3987
  };
3919
3988
  }
3920
- async function archiveWorkspaceSessionsAndRefresh(registry, workspaceId, refresh) {
3921
- const result = await registry.archiveWorkspaceSessions(workspaceId);
3922
- if (!result.ok) throw new Error(result.error.message);
3923
- await refresh();
3924
- return result.value;
3989
+ function createUnarchiveSession(workspaces, getRegistry) {
3990
+ return async (sessionId) => {
3991
+ if (typeof workspaces?.unarchiveSession === "function") {
3992
+ await workspaces.unarchiveSession(sessionId);
3993
+ return;
3994
+ }
3995
+ const registry = typeof getRegistry === "function" ? getRegistry() : getRegistry;
3996
+ if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3997
+ const result = await registry.unarchiveSession(sessionId);
3998
+ if (!result.ok) throw new Error(result.error.message);
3999
+ return result.value;
4000
+ };
4001
+ }
4002
+ async function archiveSessionsViaOfficial(workspaces, sessionIds, refresh) {
4003
+ const before = new Set(workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []);
4004
+ const seen = /* @__PURE__ */ new Set();
4005
+ for (const sessionId of sessionIds) {
4006
+ if (typeof sessionId !== "string" || sessionId.length === 0 || seen.has(sessionId)) continue;
4007
+ seen.add(sessionId);
4008
+ await workspaces.archiveSession(sessionId);
4009
+ }
4010
+ if (typeof refresh === "function") await refresh();
4011
+ const archivedSessionIds = [...workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []];
4012
+ return {
4013
+ archivedSessionIds,
4014
+ archivedSessionIdsAdded: archivedSessionIds.filter((id) => !before.has(id))
4015
+ };
4016
+ }
4017
+ async function unarchiveSessionsViaOfficial(workspaces, sessionIds, refresh, unarchive) {
4018
+ const before = new Set(workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []);
4019
+ const seen = /* @__PURE__ */ new Set();
4020
+ const run = typeof unarchive === "function" ? unarchive : (id) => workspaces.unarchiveSession(id);
4021
+ let archivedSessionIds;
4022
+ for (const sessionId of sessionIds) {
4023
+ if (typeof sessionId !== "string" || sessionId.length === 0 || seen.has(sessionId)) continue;
4024
+ seen.add(sessionId);
4025
+ const value = await run(sessionId);
4026
+ if (Array.isArray(value?.archivedSessionIds)) archivedSessionIds = [...value.archivedSessionIds];
4027
+ }
4028
+ if (typeof refresh === "function") await refresh();
4029
+ archivedSessionIds ??= [...workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []];
4030
+ return {
4031
+ archivedSessionIds,
4032
+ unarchivedSessionIds: [...before].filter((id) => !archivedSessionIds.includes(id))
4033
+ };
3925
4034
  }
3926
4035
  async function apply(ctx) {
4036
+ applyWorkspaceBrowser(ctx);
3927
4037
  const remote = ctx.get("remote");
3928
4038
  let disposeRemote = () => {
3929
4039
  };
3930
4040
  if (remote !== void 0) disposeRemote = await remote.$mount(ARCHIVE_MANAGER_REMOTE);
3931
- applyWorkspaceBrowser(ctx);
3932
4041
  return async () => {
3933
4042
  await disposeRemote();
3934
4043
  };
@@ -3950,13 +4059,24 @@
3950
4059
  const uiWorkspaceAt = () => ctx.get("uiWorkspace");
3951
4060
  const archiveViewState = {};
3952
4061
  let archiveNavigation;
3953
- ctx.effect(() => {
3954
- archiveNavigation = allowArchivedNavigation(uiWorkspaceAt(), ctx.sessions, ctx.workspaces, {
3955
- onOpened: () => ctx.get("layout")?.selectPanel(null)
4062
+ const attachArchiveNavigation = (navigation) => {
4063
+ archiveNavigation = allowArchivedNavigation(navigation, ctx.sessions, ctx.workspaces, {
4064
+ onOpened: () => ctx.get("layout")?.selectPanel(null),
4065
+ beginNavigation: () => ctx.get("layout")?.beginNavigation?.()
3956
4066
  });
3957
4067
  return () => archiveNavigation.dispose();
3958
- }, "archive-manager: explicit archived navigation");
3959
- const openConversation = (sessionId) => archiveNavigation.open(sessionId);
4068
+ };
4069
+ if (typeof ctx.inject === "function") ctx.inject(["uiWorkspace"], (ready) => attachArchiveNavigation(ready.uiWorkspace ?? uiWorkspaceAt()));
4070
+ else ctx.effect(() => attachArchiveNavigation(uiWorkspaceAt()), "archive-manager: explicit archived navigation");
4071
+ const openConversation = (sessionId) => {
4072
+ if (archiveNavigation === void 0) throw new ArchiveNavigationError("navigationUnavailable");
4073
+ sidebarReveal.request(sessionId);
4074
+ return archiveNavigation.open(sessionId);
4075
+ };
4076
+ const focusSessionWorkspace = async (sessionId) => {
4077
+ ctx.get("layout")?.beginNavigation?.();
4078
+ sidebarReveal.request(sessionId);
4079
+ };
3960
4080
  const searchSessions = async (query, signal) => {
3961
4081
  const result = await ctx.sessions.search(query, signal);
3962
4082
  if (!result.ok) throw new Error(result.error.message);
@@ -3975,17 +4095,16 @@
3975
4095
  console.warn("archive-manager: restored archived sessions but session list refresh failed:", error);
3976
4096
  }
3977
4097
  };
4098
+ const unarchiveOne = createUnarchiveSession(ctx.workspaces, () => ctx.get("remote.workspaceRegistry"));
3978
4099
  const unarchiveSession = async (sessionId) => {
3979
- const registry = ctx.get("remote.workspaceRegistry");
3980
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3981
- const result = await registry.unarchiveSession(sessionId);
3982
- if (!result.ok) throw new Error(result.error.message);
4100
+ await unarchiveOne(sessionId);
3983
4101
  await refreshSessionList();
3984
4102
  };
3985
4103
  const archiveWorkspaceSessions = async (workspaceId) => {
3986
- const registry = ctx.get("remote.workspaceRegistry");
3987
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3988
- return archiveWorkspaceSessionsAndRefresh(registry, workspaceId, refreshSessionList);
4104
+ const items = ctx.workspaces.list?.getSnapshot?.()?.items ?? [];
4105
+ const workspace = items.find((item) => item.workspaceId === workspaceId);
4106
+ if (workspace === void 0) throw new Error(`unknown workspace "${workspaceId}"`);
4107
+ return archiveSessionsViaOfficial(ctx.workspaces, workspace.sessionIds, refreshSessionList);
3989
4108
  };
3990
4109
  const deleteSession = async (sessionId) => {
3991
4110
  const registry = ctx.get("remote.workspaceRegistry");
@@ -3994,21 +4113,11 @@
3994
4113
  if (!result.ok) throw new Error(result.error.message);
3995
4114
  await refreshSessionList();
3996
4115
  };
3997
- const archiveSessions = async (sessionIds) => {
3998
- const registry = ctx.get("remote.workspaceRegistry");
3999
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
4000
- const result = await registry.archiveSessions(sessionIds);
4001
- if (!result.ok) throw new Error(result.error.message);
4002
- await refreshSessionList();
4003
- return result.value;
4004
- };
4116
+ const archiveSessions = async (sessionIds) => archiveSessionsViaOfficial(ctx.workspaces, sessionIds, refreshSessionList);
4005
4117
  const unarchiveSessions = async (target) => {
4006
- const registry = ctx.get("remote.workspaceRegistry");
4007
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
4008
- const result = await registry.unarchiveSessions(target);
4009
- if (!result.ok) throw new Error(result.error.message);
4010
- await refreshSessionList();
4011
- return result.value;
4118
+ const snapshot = ctx.workspaces.list?.getSnapshot?.() ?? { archivedSessionIds: [], items: [] };
4119
+ const sessionIds = deriveArchivedBatchIds(snapshot.archivedSessionIds, snapshot.items, target);
4120
+ return unarchiveSessionsViaOfficial(ctx.workspaces, sessionIds, refreshSessionList, unarchiveOne);
4012
4121
  };
4013
4122
  const deleteArchivedSessions = async (target) => {
4014
4123
  const registry = ctx.get("remote.workspaceRegistry");
@@ -4046,16 +4155,24 @@
4046
4155
  searchSessions,
4047
4156
  searchResultLimit: ctx.sessions.searchResultLimit,
4048
4157
  renameSession: async (sessionId, title) => {
4158
+ const rename = async (session2) => {
4159
+ const result = await session2.rename(title);
4160
+ if (!result.ok) throw new Error(result.error.message);
4161
+ };
4162
+ if (typeof ctx.sessions.using === "function") {
4163
+ await ctx.sessions.using(sessionId, { source: "workspaceOperation" }, (reference) => rename(reference.binding.session));
4164
+ return;
4165
+ }
4049
4166
  const session = ctx.sessions.binding(sessionId)?.session;
4050
4167
  if (session === void 0) throw new Error(`unknown session "${sessionId}"`);
4051
- const result = await session.rename(title);
4052
- if (!result.ok) throw new Error(result.error.message);
4168
+ await rename(session);
4053
4169
  },
4054
4170
  forkSession: (sessionId) => {
4055
4171
  const uiWorkspace = uiWorkspaceAt();
4056
4172
  if (typeof uiWorkspace?.forkSession === "function") return uiWorkspace.forkSession(sessionId);
4057
4173
  return ctx.sessions.fork({ sessionId, increaseTitle: true }).then((childId) => {
4058
- ctx.sessions.open(childId);
4174
+ if (typeof uiWorkspace?.openSession === "function") uiWorkspace.openSession(childId);
4175
+ else ctx.sessions.open(childId);
4059
4176
  });
4060
4177
  },
4061
4178
  renameWorkspace: async (workspaceId, title) => {
@@ -4079,18 +4196,28 @@
4079
4196
  createWorkspace: (input) => ctx.workspaces.create(input),
4080
4197
  hooks: { directoryFlow: browserFlowSource }
4081
4198
  });
4082
- ctx.slots.inject("sidebar.workspaces", () => ctx.slots.register({
4083
- name: "sidebar.workspaces",
4084
- // 显示顺序:Codex UI(-1)→ 归档(-0.5)→ 官方默认(0)。
4085
- priority: -0.5,
4086
- children: { [DIRECTORY_FLOW_SLOT]: {
4087
- kind: "single",
4088
- scope: "root"
4089
- } },
4090
- store: createWorkspaceViewStore(),
4091
- inject: browserInjected,
4092
- locale: NS
4093
- }, WorkspaceBrowser));
4199
+ ctx.slots.inject("sidebar.workspaces", () => {
4200
+ const common = {
4201
+ name: "sidebar.workspaces",
4202
+ // 低于官方 0,高于 Codex -1:必须盖住官方三项菜单才能露出删除。
4203
+ priority: -0.5,
4204
+ store: createWorkspaceViewStore(),
4205
+ inject: browserInjected,
4206
+ locale: NS
4207
+ };
4208
+ try {
4209
+ return ctx.slots.register({
4210
+ ...common,
4211
+ children: { [DIRECTORY_FLOW_SLOT]: {
4212
+ kind: "single",
4213
+ scope: "root"
4214
+ } }
4215
+ }, WorkspaceBrowser);
4216
+ } catch (error) {
4217
+ console.warn("archive-manager: sidebar registration with directory child failed, retrying without children", error);
4218
+ return ctx.slots.register(common, WorkspaceBrowser);
4219
+ }
4220
+ });
4094
4221
  mirrorDirectoryFlow(ctx, DIRECTORY_FLOW_SLOT);
4095
4222
  ctx.slots.inject("settings.section", () => ctx.slots.register({
4096
4223
  name: "settings.section",
@@ -4109,6 +4236,7 @@
4109
4236
  deleteArchivedSessions,
4110
4237
  archivedSessionMetadata,
4111
4238
  openConversation,
4239
+ focusSessionWorkspace,
4112
4240
  viewState: archiveViewState,
4113
4241
  t: ctx.locale.bind(NS)
4114
4242
  })
@@ -4136,13 +4264,19 @@
4136
4264
  archiveableWorkspaceSessionCount,
4137
4265
  archiveWorkspaceDialogTarget,
4138
4266
  archiveWorkspaceDialogFailureState,
4139
- archiveWorkspaceSessionsAndRefresh,
4267
+ archiveSessionsViaOfficial,
4268
+ unarchiveSessionsViaOfficial,
4269
+ createUnarchiveSession,
4140
4270
  createWorkspaceViewStore,
4271
+ migrateWorkspaceViewPersist,
4272
+ WORKSPACE_VIEW_PERSIST_KEY,
4273
+ LEGACY_WORKSPACE_VIEW_PERSIST_KEY,
4141
4274
  bindObservable,
4142
4275
  hasSplitClientStore,
4143
4276
  groupByWorkspace,
4144
4277
  byRecency,
4145
- ARCHIVE_MANAGER_REMOTE
4278
+ ARCHIVE_MANAGER_REMOTE,
4279
+ currentSessionId
4146
4280
  };
4147
4281
  exports.apply = apply;
4148
4282
  exports.inject = inject;