@michengai/dsh-archive-manager 0.1.17 → 0.1.18
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 +8 -0
- package/CHANGELOG.zh-CN.md +8 -0
- package/lib/client.js +209 -8
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
Published release notes are retained below; new versions are added without removing earlier entries.
|
|
6
6
|
|
|
7
|
+
## 0.1.18 — 2026-08-28
|
|
8
|
+
|
|
9
|
+
- Added client compatibility for the split store layout in DSH `v0.1.2-alpha.1` while preserving the legacy runtime fallback used by `v0.1.1-rc.2`.
|
|
10
|
+
- Restored the alpha Workspace navigation service required by the sidebar, conversation view, and directory picker when Archive Manager replaces the stock Workspace UI.
|
|
11
|
+
- Bound observable stores at the Settings boundary and kept subagent-lineage indexing local, preventing archived-chat startup and rendering failures on the alpha release.
|
|
12
|
+
|
|
13
|
+
Published package: [`@michengai/dsh-archive-manager@0.1.18`](https://www.npmjs.com/package/@michengai/dsh-archive-manager/v/0.1.18).
|
|
14
|
+
|
|
7
15
|
## 0.1.17 — 2026-08-28
|
|
8
16
|
|
|
9
17
|
- Enabled npm Trusted Publishing through the repository's GitHub Actions release workflow.
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
以下发布说明会持续保留;新增版本时不再删除较早记录。
|
|
6
6
|
|
|
7
|
+
## 0.1.18 — 2026-08-28
|
|
8
|
+
|
|
9
|
+
- 兼容 DSH `v0.1.2-alpha.1` 拆分后的客户端 Store 布局,同时保留 `v0.1.1-rc.2` 使用的旧版 Runtime 回退路径。
|
|
10
|
+
- 在归档管理器替换官方工作区 UI 后补齐 alpha 版侧栏、会话视图和目录选择器依赖的工作区导航服务。
|
|
11
|
+
- 在设置页注入边界绑定可观察 Store,并将子代理谱系索引保留在插件内部,避免 alpha 版启动和归档页渲染失败。
|
|
12
|
+
|
|
13
|
+
发布包:[`@michengai/dsh-archive-manager@0.1.18`](https://www.npmjs.com/package/@michengai/dsh-archive-manager/v/0.1.18)。
|
|
14
|
+
|
|
7
15
|
## 0.1.17 — 2026-08-28
|
|
8
16
|
|
|
9
17
|
- 启用仓库 GitHub Actions 发布工作流的 npm Trusted Publishing。
|
package/lib/client.js
CHANGED
|
@@ -4,7 +4,16 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
-
let
|
|
7
|
+
let _deepseek_ai_dsh_client_store;
|
|
8
|
+
let hasSplitClientStore = true;
|
|
9
|
+
try {
|
|
10
|
+
_deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
11
|
+
} catch {
|
|
12
|
+
// DSH <= 0.1.1 owns the store engine in client-runtime; 0.1.2+
|
|
13
|
+
// seeds the split client-store package directly into the module table.
|
|
14
|
+
hasSplitClientStore = false;
|
|
15
|
+
_deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-runtime/client");
|
|
16
|
+
}
|
|
8
17
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
18
|
let react = require("react");
|
|
10
19
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
@@ -187,6 +196,38 @@ window.__ModuleLoader__.load({
|
|
|
187
196
|
document.head.appendChild(tag);
|
|
188
197
|
}
|
|
189
198
|
//#endregion
|
|
199
|
+
//#region lib/types/client/subagent-lineage.js
|
|
200
|
+
/**
|
|
201
|
+
* Index uninterrupted subagent descendants under each ancestor.
|
|
202
|
+
* DSH 0.1.2 made this a UI-package-private projection, so the archive
|
|
203
|
+
* manager keeps the small pure helper locally instead of importing the
|
|
204
|
+
* removed client-runtime package.
|
|
205
|
+
* @param summaries - Session summaries keyed by id.
|
|
206
|
+
* @returns descendant totals keyed by possible parent id.
|
|
207
|
+
*/
|
|
208
|
+
function indexSubagentDescendants(summaries) {
|
|
209
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
210
|
+
for (const descendant of Object.values(summaries)) {
|
|
211
|
+
if (descendant.origin !== "subagent") continue;
|
|
212
|
+
const seen = /* @__PURE__ */ new Set();
|
|
213
|
+
let current = descendant;
|
|
214
|
+
while (current?.origin === "subagent" && current.parentId !== void 0 && !seen.has(current.id)) {
|
|
215
|
+
seen.add(current.id);
|
|
216
|
+
const aggregate = indexed.get(current.parentId);
|
|
217
|
+
if (aggregate === void 0) indexed.set(current.parentId, {
|
|
218
|
+
count: 1,
|
|
219
|
+
runningCount: descendant.running ? 1 : 0
|
|
220
|
+
});
|
|
221
|
+
else {
|
|
222
|
+
aggregate.count += 1;
|
|
223
|
+
if (descendant.running) aggregate.runningCount += 1;
|
|
224
|
+
}
|
|
225
|
+
current = summaries[current.parentId];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return indexed;
|
|
229
|
+
}
|
|
230
|
+
//#endregion
|
|
190
231
|
//#region lib/types/client/stores.js
|
|
191
232
|
/**
|
|
192
233
|
* The workspace browser's viewing store: the session-list grouping mode,
|
|
@@ -202,7 +243,7 @@ window.__ModuleLoader__.load({
|
|
|
202
243
|
* @returns the store handle (spec + type + identity + factory in one).
|
|
203
244
|
*/
|
|
204
245
|
function createWorkspaceViewStore() {
|
|
205
|
-
return (0,
|
|
246
|
+
return (0, _deepseek_ai_dsh_client_store.defineStore)({
|
|
206
247
|
init: () => ({
|
|
207
248
|
groupBy: "workspace",
|
|
208
249
|
orderBy: "updated",
|
|
@@ -402,7 +443,7 @@ window.__ModuleLoader__.load({
|
|
|
402
443
|
function deriveGroups(list, workspaces, archivedSessionIds, view) {
|
|
403
444
|
const archived = new Set(archivedSessionIds);
|
|
404
445
|
const expandedGroups = new Set(view.expandedGroups);
|
|
405
|
-
const descendants =
|
|
446
|
+
const descendants = indexSubagentDescendants(list.byId);
|
|
406
447
|
const currentGroup = list.current === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(list.current))?.workspaceId ?? "";
|
|
407
448
|
const groups = [];
|
|
408
449
|
for (const g of groupByWorkspace(list, workspaces, archived, view.ungroupedOrder, view.showArchived)) {
|
|
@@ -435,7 +476,7 @@ window.__ModuleLoader__.load({
|
|
|
435
476
|
*/
|
|
436
477
|
function deriveFlat(list, archivedSessionIds, showArchived) {
|
|
437
478
|
const archived = new Set(archivedSessionIds);
|
|
438
|
-
const descendants =
|
|
479
|
+
const descendants = indexSubagentDescendants(list.byId);
|
|
439
480
|
const rows = [];
|
|
440
481
|
for (const id of list.ids) {
|
|
441
482
|
const s = list.byId[id];
|
|
@@ -466,7 +507,7 @@ window.__ModuleLoader__.load({
|
|
|
466
507
|
hasMore: false
|
|
467
508
|
};
|
|
468
509
|
const archived = new Set(archivedSessionIds);
|
|
469
|
-
const descendants =
|
|
510
|
+
const descendants = indexSubagentDescendants(list.byId);
|
|
470
511
|
const workspaceBySession = /* @__PURE__ */ new Map();
|
|
471
512
|
for (const workspace of workspaces) for (const sessionId of workspace.sessionIds) if (!workspaceBySession.has(sessionId)) workspaceBySession.set(sessionId, workspace.title);
|
|
472
513
|
const labelOf = (summary) => workspaceBySession.get(summary.id) ?? workspaceLabel(summary.cwd);
|
|
@@ -3242,6 +3283,158 @@ window.__ModuleLoader__.load({
|
|
|
3242
3283
|
"remote",
|
|
3243
3284
|
"typert"
|
|
3244
3285
|
];
|
|
3286
|
+
/** Preserve the receiver required by alpha-era observable stores. */
|
|
3287
|
+
function bindObservable(source) {
|
|
3288
|
+
return {
|
|
3289
|
+
getSnapshot: source.getSnapshot.bind(source),
|
|
3290
|
+
subscribe: source.subscribe.bind(source)
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
class DirectoryBrowseError extends Error {
|
|
3294
|
+
constructor(rpcError) {
|
|
3295
|
+
super(`directory browse failed: ${rpcError.code}: ${rpcError.message}`);
|
|
3296
|
+
this.name = "DirectoryBrowseError";
|
|
3297
|
+
this.rpcError = rpcError;
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
/**
|
|
3301
|
+
* Provide the Workspace navigation capability introduced by DSH 0.1.2.
|
|
3302
|
+
* The archive manager replaces the stock ui-workspace row, so it must also
|
|
3303
|
+
* preserve the service that alpha-era sidebar/conversation plugins wait for.
|
|
3304
|
+
* Older hosts do not consume this service, making the provider harmless there.
|
|
3305
|
+
* @param ctx - client root context.
|
|
3306
|
+
* @returns disposer for the compatibility service, or a no-op when supplied.
|
|
3307
|
+
*/
|
|
3308
|
+
function provideUiWorkspace(ctx) {
|
|
3309
|
+
if (ctx.get("uiWorkspace") !== void 0) return () => {};
|
|
3310
|
+
const sessions = ctx.sessions;
|
|
3311
|
+
const workspaces = ctx.workspaces;
|
|
3312
|
+
const connecting = new Map();
|
|
3313
|
+
const recentWorkspace = () => {
|
|
3314
|
+
const workspaceState = workspaces.list.getSnapshot();
|
|
3315
|
+
const sessionState = sessions.list.getSnapshot();
|
|
3316
|
+
let selected;
|
|
3317
|
+
let selectedTime = Number.NEGATIVE_INFINITY;
|
|
3318
|
+
for (const workspace of workspaceState.items ?? []) {
|
|
3319
|
+
let latest = Number.NEGATIVE_INFINITY;
|
|
3320
|
+
for (const sessionId of workspace.sessionIds) {
|
|
3321
|
+
const session = sessionState.byId[sessionId];
|
|
3322
|
+
if (session !== void 0) latest = Math.max(latest, session.updatedAt);
|
|
3323
|
+
}
|
|
3324
|
+
if (latest === Number.NEGATIVE_INFINITY) latest = Date.parse(workspace.createdAt);
|
|
3325
|
+
if (selected === void 0 || latest > selectedTime) {
|
|
3326
|
+
selected = workspace.workspaceId;
|
|
3327
|
+
selectedTime = latest;
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
return selected;
|
|
3331
|
+
};
|
|
3332
|
+
const directoryPicker = () => ctx.get("remote.directoryPicker") ?? ctx.get("remote")?.directoryPicker;
|
|
3333
|
+
const service = {
|
|
3334
|
+
connectWorkspace(workspaceId) {
|
|
3335
|
+
const workspaceState = workspaces.list.getSnapshot();
|
|
3336
|
+
const workspace = (workspaceState.items ?? []).find((item) => item.workspaceId === workspaceId);
|
|
3337
|
+
if (workspace === void 0) return Promise.reject(new Error(`uiWorkspace.connectWorkspace: unknown workspace ${workspaceId}`));
|
|
3338
|
+
const inflight = connecting.get(workspaceId);
|
|
3339
|
+
if (inflight !== void 0) return inflight;
|
|
3340
|
+
const sessionState = sessions.list.getSnapshot();
|
|
3341
|
+
const archived = new Set(workspaceState.archivedSessionIds ?? []);
|
|
3342
|
+
for (const id of sessionState.ids) {
|
|
3343
|
+
const summary = sessionState.byId[id];
|
|
3344
|
+
if (summary !== void 0 && summary.blank && summary.cwd === workspace.path && workspace.sessionIds.includes(summary.id) && !archived.has(summary.id)) return Promise.resolve(summary.id);
|
|
3345
|
+
}
|
|
3346
|
+
const attempt = sessions.create({ workspaceId }).finally(() => {
|
|
3347
|
+
connecting.delete(workspaceId);
|
|
3348
|
+
});
|
|
3349
|
+
connecting.set(workspaceId, attempt);
|
|
3350
|
+
return attempt;
|
|
3351
|
+
},
|
|
3352
|
+
startSession(workspaceId) {
|
|
3353
|
+
const workspaceState = workspaces.list.getSnapshot();
|
|
3354
|
+
const sessionState = sessions.list.getSnapshot();
|
|
3355
|
+
const currentWorkspaceId = sessionState.current === void 0 ? void 0 : (workspaceState.items ?? []).find((item) => item.sessionIds.includes(sessionState.current))?.workspaceId;
|
|
3356
|
+
const recent = workspaceState.phase === "ready" && sessionState.phase === "ready" ? recentWorkspace() : void 0;
|
|
3357
|
+
const target = workspaceId ?? currentWorkspaceId ?? recent;
|
|
3358
|
+
if (target === void 0) {
|
|
3359
|
+
sessions.clear();
|
|
3360
|
+
return;
|
|
3361
|
+
}
|
|
3362
|
+
void service.connectWorkspace(target).then((sessionId) => {
|
|
3363
|
+
sessions.open(sessionId);
|
|
3364
|
+
}, (reason) => {
|
|
3365
|
+
console.warn("new session failed:", reason);
|
|
3366
|
+
});
|
|
3367
|
+
},
|
|
3368
|
+
archiveSession(sessionId) {
|
|
3369
|
+
return workspaces.archiveSession(sessionId);
|
|
3370
|
+
},
|
|
3371
|
+
async pickDirectory() {
|
|
3372
|
+
const picker = directoryPicker();
|
|
3373
|
+
if (picker === void 0) throw new Error("directory picker is unavailable");
|
|
3374
|
+
const result = await picker.pick();
|
|
3375
|
+
if (!result.ok) throw new Error(`directory picker failed: ${result.error.message}`);
|
|
3376
|
+
return result.value;
|
|
3377
|
+
},
|
|
3378
|
+
async listDirectory(path, signal) {
|
|
3379
|
+
const picker = directoryPicker();
|
|
3380
|
+
if (picker === void 0) throw new Error("directory picker is unavailable");
|
|
3381
|
+
const result = await picker.list(path, signal);
|
|
3382
|
+
if (!result.ok) throw new DirectoryBrowseError(result.error);
|
|
3383
|
+
return result.value;
|
|
3384
|
+
},
|
|
3385
|
+
async createDirectory(path, name) {
|
|
3386
|
+
const picker = directoryPicker();
|
|
3387
|
+
if (picker === void 0) throw new Error("directory picker is unavailable");
|
|
3388
|
+
const result = await picker.createDirectory(path, name);
|
|
3389
|
+
if (!result.ok) throw new DirectoryBrowseError(result.error);
|
|
3390
|
+
return result.value;
|
|
3391
|
+
}
|
|
3392
|
+
};
|
|
3393
|
+
const dispose = ctx.provide("uiWorkspace", service);
|
|
3394
|
+
ctx.slots.provideRoot?.({ hooks: { workspaces: workspaces.list } });
|
|
3395
|
+
ctx.effect(() => {
|
|
3396
|
+
let initial = "waiting";
|
|
3397
|
+
let disposed = false;
|
|
3398
|
+
const reconcile = () => {
|
|
3399
|
+
if (disposed) return;
|
|
3400
|
+
const workspaceState = workspaces.list.getSnapshot();
|
|
3401
|
+
const sessionState = sessions.list.getSnapshot();
|
|
3402
|
+
if (sessionState.current !== void 0 && (workspaceState.archivedSessionIds ?? []).includes(sessionState.current)) {
|
|
3403
|
+
sessions.clear();
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3406
|
+
if (initial !== "waiting" || workspaceState.phase !== "ready" || sessionState.phase !== "ready") return;
|
|
3407
|
+
if (sessionState.current !== void 0) {
|
|
3408
|
+
initial = "done";
|
|
3409
|
+
return;
|
|
3410
|
+
}
|
|
3411
|
+
const target = recentWorkspace();
|
|
3412
|
+
if (target === void 0) {
|
|
3413
|
+
initial = "done";
|
|
3414
|
+
return;
|
|
3415
|
+
}
|
|
3416
|
+
initial = "connecting";
|
|
3417
|
+
void service.connectWorkspace(target).then((sessionId) => {
|
|
3418
|
+
if (disposed) return;
|
|
3419
|
+
if (sessions.list.getSnapshot().current === void 0) sessions.open(sessionId);
|
|
3420
|
+
initial = "done";
|
|
3421
|
+
}, (reason) => {
|
|
3422
|
+
if (disposed) return;
|
|
3423
|
+
initial = "waiting";
|
|
3424
|
+
console.warn("initial workspace selection failed:", reason);
|
|
3425
|
+
});
|
|
3426
|
+
};
|
|
3427
|
+
const disposeWorkspaces = workspaces.list.subscribe(reconcile);
|
|
3428
|
+
const disposeSessions = sessions.list.subscribe(reconcile);
|
|
3429
|
+
reconcile();
|
|
3430
|
+
return () => {
|
|
3431
|
+
disposed = true;
|
|
3432
|
+
disposeSessions();
|
|
3433
|
+
disposeWorkspaces();
|
|
3434
|
+
};
|
|
3435
|
+
}, "dsh-archive-manager: uiWorkspace navigation policy");
|
|
3436
|
+
return dispose;
|
|
3437
|
+
}
|
|
3245
3438
|
/**
|
|
3246
3439
|
* Plugin body: mount the archive-manager Remote contribution, then
|
|
3247
3440
|
* register the workspace browser. `$mount` must complete before the
|
|
@@ -3255,8 +3448,10 @@ window.__ModuleLoader__.load({
|
|
|
3255
3448
|
const remote = ctx.get("remote");
|
|
3256
3449
|
let disposeRemote = () => {};
|
|
3257
3450
|
if (remote !== void 0) disposeRemote = await remote.$mount(ARCHIVE_MANAGER_REMOTE);
|
|
3451
|
+
const disposeUiWorkspace = hasSplitClientStore ? provideUiWorkspace(ctx) : () => {};
|
|
3258
3452
|
applyWorkspaceBrowser(ctx);
|
|
3259
3453
|
return async () => {
|
|
3454
|
+
disposeUiWorkspace();
|
|
3260
3455
|
await disposeRemote();
|
|
3261
3456
|
};
|
|
3262
3457
|
}
|
|
@@ -3271,6 +3466,7 @@ window.__ModuleLoader__.load({
|
|
|
3271
3466
|
zh,
|
|
3272
3467
|
en
|
|
3273
3468
|
}), "dsh-archive-manager: dictionaries");
|
|
3469
|
+
const uiWorkspace = ctx.get("uiWorkspace");
|
|
3274
3470
|
const searchSessions = async (query, signal) => {
|
|
3275
3471
|
const result = await ctx.sessions.search(query, signal);
|
|
3276
3472
|
if (!result.ok) throw new Error(result.error.message);
|
|
@@ -3317,7 +3513,8 @@ window.__ModuleLoader__.load({
|
|
|
3317
3513
|
};
|
|
3318
3514
|
const browserInjected = () => ({
|
|
3319
3515
|
startSession: (workspaceId) => {
|
|
3320
|
-
|
|
3516
|
+
if (uiWorkspace !== void 0) uiWorkspace.startSession(workspaceId);
|
|
3517
|
+
else ctx.workspaces.startSession(workspaceId);
|
|
3321
3518
|
},
|
|
3322
3519
|
open: (sessionId) => {
|
|
3323
3520
|
ctx.sessions.open(sessionId);
|
|
@@ -3387,8 +3584,8 @@ window.__ModuleLoader__.load({
|
|
|
3387
3584
|
icon: "archive",
|
|
3388
3585
|
locale: NS,
|
|
3389
3586
|
inject: () => ({
|
|
3390
|
-
sessionStore: ctx.sessions.list,
|
|
3391
|
-
workspaceStore: ctx.workspaces.list,
|
|
3587
|
+
sessionStore: bindObservable(ctx.sessions.list),
|
|
3588
|
+
workspaceStore: bindObservable(ctx.workspaces.list),
|
|
3392
3589
|
unarchiveSession,
|
|
3393
3590
|
deleteSession,
|
|
3394
3591
|
unarchiveSessions,
|
|
@@ -3403,6 +3600,7 @@ window.__ModuleLoader__.load({
|
|
|
3403
3600
|
exports.__test = {
|
|
3404
3601
|
displayTitle,
|
|
3405
3602
|
sessionVisible,
|
|
3603
|
+
indexSubagentDescendants,
|
|
3406
3604
|
isUnknownSessionError,
|
|
3407
3605
|
deriveGroups,
|
|
3408
3606
|
deriveFlat,
|
|
@@ -3412,6 +3610,9 @@ window.__ModuleLoader__.load({
|
|
|
3412
3610
|
deriveArchivedBatchIds,
|
|
3413
3611
|
archivedDeleteFeedback,
|
|
3414
3612
|
createWorkspaceViewStore,
|
|
3613
|
+
bindObservable,
|
|
3614
|
+
hasSplitClientStore,
|
|
3615
|
+
provideUiWorkspace,
|
|
3415
3616
|
groupByWorkspace,
|
|
3416
3617
|
byRecency,
|
|
3417
3618
|
ARCHIVE_MANAGER_REMOTE
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michengai/dsh-archive-manager",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "NPM-installable DSH Web plugin for managing archived sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.21.0",
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
"@deepseek-ai/dsh-typert-registry": ">=0.1.0-rc.5 <0.2.0",
|
|
48
48
|
"@deepseek-ai/dsh-workspace": ">=0.1.0-rc.5 <0.2.0"
|
|
49
49
|
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@deepseek-ai/dsh-client-runtime": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
50
55
|
"devDependencies": {
|
|
51
56
|
"@deepseek-ai/cordis": "4.0.1",
|
|
52
57
|
"@deepseek-ai/dsh-api-gateway": "0.1.0-rc.6",
|
|
@@ -69,7 +74,6 @@
|
|
|
69
74
|
"client": {
|
|
70
75
|
"inject": [
|
|
71
76
|
"@deepseek-ai/dsh-client-locale",
|
|
72
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
73
77
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
74
78
|
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
75
79
|
"@deepseek-ai/dsh-typert-registry",
|