Saikel-Orado-Liu
dsh-archive-manager
Archived-session management (show/unarchive/permanently delete) for the DSH Web GUI, with zero changes to official packages.
- Stars
- 1
- Language
- JavaScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
DSH Archive Manager
English · 简体中文
DSH Archive Manager is an archived-session management plugin for the DeepSeek Harness (DSH) Web GUI — a "Show archived" view toggle, archived-session styling and guarding (red title, tinted background, "Archived" badge, not openable), unarchive, and permanent session deletion (transcript directory, workspace accounting, archive marker, and projection cache row are all removed; live sessions are disposed first so the UI never crashes).
- Host half (internal
dsh-archive-manager-workspace/dsh-archive-manager-projcache):WorkspaceRegistryandSessionProjectionCachesubclasses that addunarchiveSession/deleteSessionanddelete(id)/whenIdle(), exposed as Typert Remote endpoints. - Client half (internal
dsh-archive-manager-client): a forkeddsh-client-ui-workspacebrowser bundle — view toggle, archived row treatment, guarded open, row menus, confirmation dialogs, and toasts, in Simplified Chinese and English.
The plugin ships as a single npm package (@gamegeek-saikel/dsh-archive-manager) containing the three implementations as internal submodules. Its cordis.patch.yml disables the stock rows and inserts the archive-manager rows. Official package files are never modified.
Installation
Published package (recommended)
npx @deepseek-ai/dsh plugin --profile web add @gamegeek-saikel/dsh-archive-manager
Then start DSH Web:
npx @deepseek-ai/dsh web
If you have the DSH CLI installed globally, you can use
dshinstead ofnpx @deepseek-ai/dsh.
This installs the single npm package through the DSH CLI, which applies the package's root cordis.patch.yml (disables the stock workspace, session-projection-cache, and ui-workspace rows; inserts workspace-archive-manager, session-projection-cache-archive-manager, and ui-workspace-archive-manager).
Local development install
Run install.ps1 from an elevated PowerShell (it must write under %USERPROFILE%\.dsh):
.\install.ps1
It performs three idempotent steps:
- Copies the three internal package sources to
%USERPROFILE%\.dsh\profiles\archive-manager\; - Creates junction links for the three internal package names under
%USERPROFILE%\.dsh\profiles\web\node_modules\(ESM dependencies resolve through the Node parent-directory walk to the DSH flat fallback%USERPROFILE%\.dsh\profiles\node_modules, so the same module instances as the runtime are used); - Backs up
%USERPROFILE%\.dsh\profiles\web\cordis.patch.yml→cordis.patch.yml.bak-<timestamp>and appends the local patch block.
Restart dsh web to activate (the session is interrupted; verify with the checklist below).
Overview
DSH Web keeps a registry-global archivedSessionIds set, but the stock UI offers no way to see archived sessions in the sidebar, no way to unarchive them, and no way to permanently delete a session. Naively hiding archived sessions from the list makes them unrecoverable through the GUI, and deleting a session touches several independent stores (transcript directory, workspace accounting, archive marker, projection cache) whose ordering matters.
Archive Manager solves this with a small, disciplined patch layer:
- One visibility toggle —
showArchivedlives in the same persisted store as grouping/sorting (dsh.workspace.view.v5), so the choice survives browser restarts and old preferences deserialize as "off" without breaking anything. - One derivation path — grouping, the flat list, and search all share
sessionVisible, so archived sessions appear consistently in every surface (or disappear consistently when the toggle is off). - One serialized deletion flow —
deleteSessionruns inside the registry's operation queue with strict ordering: flush → detach (session/disposed) → wait for the projection cache's dispose write-behind (whenIdle) → remove the transcript directory → clear the archive marker → remove workspace accounting → delete the cache row → best-effort subagent cascade + spill cleanup. Every failing step is idempotent and re-runnable, so a retry heals a half-delete.
Key Properties
| Property | Value |
|---|---|
| Scope | Show-archived toggle, archived styling + guard, unarchive, permanent delete |
| Delivery | Single npm package; official packages untouched; web-profile patch layer (cordis.patch.yml) |
| Install / rollback | npx @deepseek-ai/dsh plugin --profile web add @gamegeek-saikel/dsh-archive-manager; local dev via install.ps1 / rollback.ps1 |
| Remote API | Typert SRC endpoints workspaceRegistry/unarchiveSession, workspaceRegistry/deleteSession; legacy /api/workspace.* untouched |
| Delete semantics | Permanent; live session flush → detach → session/disposed; cache write-behind awaited before row delete; subagent children cascade (origin subagent only — fork branches never) |
| UI surfaces | Sidebar session/workspace browser · view options menu · row menus · confirmation dialogs · toast |
| Locale | Simplified Chinese (source) + English |
| Tests | 22 cases across 4 node:test suites (host, client bundle, client remote, installed copies) |
Usage
Once installed and restarted:
| Surface | Description |
|---|---|
| View options | New "Show archived" item (separator-separated) alongside group-by/order-by; persisted in the same store |
| Session rows | Archived rows: red title + tinted background + "Archived" badge (theme danger token), click guarded with a toast; row menu = [Unarchive, Delete session] |
| Search results | Archived matches get the same red treatment; the browser-level guard keeps them from opening |
| Row menus | Normal sessions gain a danger-styled "Delete session" item; archived sessions show only [Unarchive, Delete session] |
| Delete dialog | Two-step confirmation ("This permanently deletes session … This cannot be undone."), pending state, failure keeps the dialog open |
| Live session delete | Composer greys out, row disappears, UI does not crash |
Restart verification checklist
- View options → "Show archived": toggling shows/hides archived sessions in their workspace groups (flat list and search included).
- Clicking an archived session does not open it; a toast says "This session is archived. Unarchive it to continue the conversation." and messaging stays blocked.
- An archived row's menu contains only [Unarchive, Delete session]; unarchive restores normal styling and openability at the session's original workspace position.
- A normal row's menu contains "Delete session"; confirming permanently removes the session with no disk residue:
~/.dsh\sessions\…\session-<id>\no longer exists;~/.dsh\storages\workspace.json—global.archivedSessionIdsand every workspace'ssessionIdsno longer contain the id;~/.dsh\storages\session_projcache.json—tables.sessionsno longer contains the id.
- Deleting the currently open session greys the composer and removes the row without crashing.
- Rename / fork / archive / drag-sort / search / flat list / workspace CRUD all keep working.
- Both locales render correctly (switch the browser language and reload).
How It Works
Host half — workspace (dsh-archive-manager-workspace)
ArchiveWorkspaceRegistry extends WorkspaceRegistry (same service name workspaceRegistry, same accounting invariants) and adds:
unarchiveSession(sessionId)— removes the id from the registry-globalarchivedSessionIdsset. Archiving never moves the accounting seat, so unarchiving restores the session at its original workspace position. Idempotent; unknown ids reject likearchiveSessiondoes.deleteSession(sessionId)— the serialized permanent deletion described in the overview: validate → flush → detach/session/disposed→whenIdle→ remove transcript dir → clear archive marker → remove workspace accounting → delete cache row → best-effort cascade/spill.
Both are exported as Typert Remote endpoints through the service's typertRemote binding plus Remote markers. The browser reaches them through the standard typert gateway SRC path, keeping the legacy /api/workspace.* gateway untouched.
Host half — projection cache (dsh-archive-manager-projcache)
ArchiveProjectionCache extends SessionProjectionCache (same service name sessionProjectionCache, same fail-soft write path) and adds:
delete(id)— permanently removes one session's cached projection row (table.delete).whenIdle()— resolves once every in-flight fail-soft checkpoint write has settled. Session disposal triggers one final write-behind (flushSoft(session, "detach")); the deletion flow must wait for it to land before deleting the row, otherwise the row is written back after deletion and resurrects the cache entry.
Client half (dsh-archive-manager-client)
The package is a full fork of the @deepseek-ai/dsh-client-ui-workspace bundle (the stock package has no inheritable exports, so lib/client.js is a self-registering bundle copied wholesale and patched at 12 points — see dsh-archive-manager-client/PATCHES.md for the exact list). Highlights:
createWorkspaceViewStoregainsshowArchived(setShowArchivedcoerces to boolean) in the same persisted store family;sessionVisiblegains a 4th parameter, andderiveGroups/deriveFlat/deriveSearchResultsthread it through and stamparchivedon rows;ARCHIVE_MANAGER_REMOTEmounts the two Remote descriptors with dependency-free strict-codec shims (no second zod copy in the bundle);- The
applyfiber is async: it$mounts the Remote contribution before registering slots, then readsctx.get("remote.workspaceRegistry")explicitly (declaringremote.workspaceRegistryin inject would deadlock with the same fiber performing the mount).
Project Structure
dsh-archive-manager/
package.json # Single npm package @gamegeek-saikel/dsh-archive-manager
lib/index.js # Root host entry (empty apply; client via dsh.client)
cordis.patch.yml # DSH bundle patch (disables stock rows, inserts archive rows)
scripts/check-package.mjs # Publish preflight (pnpm build)
install.ps1 / rollback.ps1 # Local development install / rollback (idempotent)
README.md / README.zh-CN.md # Bilingual docs
test/ # node:test suites (22 cases)
host.test.mjs # Workspace + projcache behavior, typert gateway E2E
client.test.mjs # Forked bundle derivation + view store
client-remote.test.mjs # Client Remote $mount / ctx.get integration
installed.test.mjs # Smoke tests against the installed copies
dsh-archive-manager-workspace/ # Internal: WorkspaceRegistry subclass + Remote methods
lib/index.js
dsh-archive-manager-projcache/ # Internal: SessionProjectionCache subclass (delete/whenIdle)
lib/index.js
dsh-archive-manager-client/ # Internal: forked ui-workspace bundle
lib/index.js # Host plugin body (empty apply)
lib/client.js # Forked browser bundle (PATCHES.md lists the 12 edits)
PATCHES.md # Fork patch notes
Development
There is no compile step — the package is plain ESM. pnpm build runs a lightweight publish preflight (scripts/check-package.mjs) that verifies the single-package structure.
Self-tests need the test tree's node_modules junction (created by install.ps1) so the real @deepseek-ai packages resolve to the same flat fallback as the runtime:
.\install.ps1 # copies sources + creates junctions + local patch block (idempotent)
pnpm build # publish preflight
node --test test/ # or list the four files explicitly
The suites cover: unarchive/delete idempotency, unknown-id errors, accounting + archive-marker cleanup, transcript-directory removal, live-session flush → detach → session/disposed, whenIdle-before-row-delete ordering, subagent cascade (origin subagent only — fork branches with parentSession are never cascade-deleted), the intact legacy API surface, projcache delete/whenIdle timing, typert gateway claims + dispatch end to end, the client bundle's real-load derivation behavior, and the installed copies' real deleteSession flow.
Documentation
dsh-archive-manager-client/PATCHES.md— the 12 fork patch points and why the Typert Remote path is used instead of extending/api/workspace.*README.zh-CN.md— 简体中文版本
License
This repository (source, tests, README, and the DSH plugin bundle shape) is licensed under the MIT License — see LICENSE.
Copyright (c) 2026 Saikel-Orado-Liu aka GameGeek-Saikel