DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Session Backup — DSH Plugin for DeepSeek Harness
← Plugins

dsh-session-backup

Session Backup

Backup every session before it is deleted, batch-select sessions in the Web GUI, and restore a backup in one click

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-session-backup@0.1.0
READMECompatibilityVersions

Compatibility and provenance

Session Backup is published as dsh-session-backup and currently resolves to version 0.1.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
10/18/2026

Versions

0.1.0stable
10/18/2026

Related plugins

Loading related plugins…

Latest
0.1.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
67.9 kB
Files
7
Surface
web
License
MIT
Source
npm
GitHub
★ 1
Weekly downloads
0
Last push
10/18/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in memory-context.

Memory Plugin@openviking/dsh-memory-pluginOpenViking memory and context bundle for DeepSeek HarnessContextdsh-contextA DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.Ab Memorydsh-ab-memory--- description: "The dsh-ab-memory package: a cross-session, file-backed agent memory with five tools (remember / recall / forget / load_skill / distill) and an always-on light injection." kind: "package-reference" ---Weknora@wxg-prc-cpg/dsh-weknoraWeKnora knowledge retrieval tools for DeepSeek Harness (dsh): semantic search, document reading and RAG/agent answers over your own knowledge bases.

README

dsh-session-backup

Backs up every session before the DeepSeek Harness Web GUI deletes it, adds multi-select for batch delete/backup to the session list, and restores a backup in one click — all as a user plugin, without touching any installed harness file.

What you get

featurewhere you see it
automatic backup on deletenowhere — confirming 彻底删除… (Delete session…) silently writes a backup first
multi-select + batch backup/delete多选 (Select) in the sidebar footer, a checkbox on each row, and a 选择/加入选择 row in each session's "…" menu
backup manager with one-click restore备份 (Backups) in the sidebar footer

Batch delete reuses the same automatic backup, so a batch never removes a session that was not copied first. If a backup fails, the delete is refused and the error is shown — the only copy of a session never disappears because a backup could not be written.

Blank placeholder rows (the provisional 新建会话 / New session row) and archived rows carry no checkbox: the harness renders neither a status slot nor its own actions for them, so they cannot be selected. They hold no conversation to preserve, and the next 新建会话 adopts the blank one.

Where backups live

$DSH_HOME/backups/sessions/<sessionId>/<timestamp>-<title>/
├── session.v4.jsonl.zstd        # the session's own directory, copied verbatim
├── …
└── backup.json                  # manifest: sessionId, title, createdAt, sourcePath, size

The directory name is the local timestamp (20261017-184533) followed by the session's current title, sanitized for the filesystem (/ \ : * ? " < > | and control characters become spaces, capped at 48 characters, untitled when nothing usable remains). Two backups inside the same second get -1, -2 … suffixes. session.lock and .DS_Store never enter a backup; the manifest's backup.json never leaves one on restore.

Attachments are content-addressed and survive session deletion, so the session directory alone is a complete, restorable copy.

How the delete hook works

dsh-client-ui-workspace reads ctx.remote.session — a compound Cordis service name — and calls deleteSession on it. That read walks the framework's internal/get waterfall, which is where this plugin wraps the service: the normal resolution runs first (whatever resolved it, including dsh-remote-session-inject-fix's fallback), and this plugin wraps whatever comes back so deleteSession copies the session to the backup store before handing the request to the host.

The listener is registered with prepend so it stays outermost regardless of plugin order — the tests cover both orders.

Semantics of a failed backup:

failureresult
host route answers a real error (disk full, conflict, …)delete refused, failed RemoteResult returned, error dialog shown
session has no directory left to back updelete proceeds (nothing to copy), console note
this profile has no Host face mounted (404/405 without our error code)delete proceeds, console note — the plugin can never become a delete-blocker

HTTP routes

All four are registered on the Host connection below /api, address document-relative by the browser (.slice(1)), and answer JSON:

methodpathbodyanswers
GET/api/session.backup.list—{ backups: [...] } newest first
POST/api/session.backup.create{ sessionId }{ backup } with path, title, dirName, bytes, consistent
POST/api/session.backup.restore{ path }{ restored } with attached, workspaceId / reason; 409 if the session exists again
POST/api/session.backup.remove{ path }{ removed }; prunes an empty session group

sessionId must match a plain id pattern and every client-supplied path is checked to be strictly inside its root, so no request can reach outside the store. The title comes from the sessionQuery service (readTitle), falling back to untitled when it cannot be read. Before copying a live session the host flushes its in-memory log through the sessions service — the same barrier dsh-session-log-export uses — and each file's size is re-verified after the copy, so a log still being appended is re-copied and a copy that never settles is reported in the manifest (consistent: false) instead of silently shipping a torn file.

readTitle answers with either a bare string or the title provider's snapshot record ({ title, source, eventSeq, … }), depending on which query service the profile has; both are unwrapped, so backups are named after the title the Sidebar shows rather than a generic untitled.

Restore also puts the session back in the Sidebar

Copying the directory back is only half of a restore. Deleting a session does two things — it removes the directory and drops the id from its Workspace record — and the Sidebar lists Workspace membership, not the raw directory tree, so a restore that only copies files leaves an invisible session behind (verified: it stays hidden even after a full dsh web restart).

So restore finishes by re-attaching the id through the harness's own API, workspace.attachSession(sessionId). That call re-reads the restored session's header from persistence and refuses unless its cwd resolves to that Workspace's path, so the restore cannot attach a session to the wrong project. The response reports the outcome:

restored.attachedmeaning
truere-attached; workspaceId names the Workspace, and the session is listed again (reload the page if the list was already open)
falsefiles are restored but the session stays unlisted; reason says why — no Workspace matches the log's cwd, the header is unreadable, or this profile has no workspaceRegistry

A failed attachment never fails the request: the bytes are already back, and the copy would only be refused as a conflict on a retry.

Two faces: index.js and client.js

The Harness loads this package twice, from two different module systems:

facefileconsumed by
host (Node)index.jsdsh importing exports["."]
browserclient.jsthe web host, via exports["./client"]

The web host concatenates every dsh.client.platform: "web" package's client file into a classic script batch served from /plugins/..., so client.js must register itself with window.__ModuleLoader__.load({ id, factory }). Handing it raw ESM makes the whole batch fail with SyntaxError: Unexpected token 'export' and the boot screen reports import failed (see console for the import error).

client.js is generated from src/client.mjs — never edit it directly:

node scripts/build-client.mjs   # regenerate client.js after editing src/client.mjs
node --test "tests/**/*.test.mjs"

The build rewrites each ESM import into a require() of the same specifier (react and @deepseek-ai/dsh-client-ui-primitives are seed words), strips export while recording the exported names, and fails loudly on Node built-ins, export default, or any form it cannot carry. The test suite fails if client.js is stale.

Install

From npm:

dsh plugin --profile <your-profile> add dsh-session-backup

From GitHub (pin a tag or commit for a reproducible install):

dsh plugin --profile <your-profile> add github:overspread/dsh-session-backup

From a local checkout:

dsh plugin --profile <your-profile> add /path/to/session-backup
dsh --profile <your-profile> --dump-config   # expect a "# == dsh-session-backup" layer

Then restart the dsh web server — the host caches each package's resolved client-bundle path in memory, so an added, removed, or re-pointed exports["./client"] only takes effect on a fresh boot.

Tests

npm test
filecovers
tests/backup-store.test.mjsnaming/sanitizing, inbound guards, locate/create/list/restore/remove against temp dirs, both readTitle answer shapes, and the restore → re-attach matrix (match, symlinked path, missing registry, unreadable header, unknown cwd, failing attach)
tests/routes.test.mjsthe four HTTP handlers, status codes, and unregistration on dispose
tests/client-bundle.test.mjsbuild freshness, the classic-script wrap, and the forms the build rejects
tests/delete-interceptor.test.mjsthe interceptor against a real Cordis context: backup ordering, refusal on failure, non-blocking when the Host face is absent, pass-through of other properties, and both registration orders alongside dsh-remote-session-inject-fix

Every path above was also driven against a live dsh web instance with headless Chrome: boot with the plugin loaded, select → batch backup (directory named <timestamp>-<title> with the real title), the harness's own delete dialog writing its automatic backup, one-click restore re-listing the session, batch delete backing up before removing, and deleting backups from the manager.

Survives harness upgrades

The plugin lives in your profile (~/.dsh/profiles/<name>), not inside the installed harness, so upgrading dsh does not remove it. Uninstall with dsh plugin --profile <name> remove dsh-session-backup.

Distribution

Releasing a new version:

node scripts/build-client.mjs && npm test
npm version patch
npm publish                # needs an npm login; the tarball ships client.js as built
git push --follow-tags

The package has no runtime dependencies; run the build before packing so the tarball ships a client.js matching the current src/client.mjs (the tests check this). The files list in package.json keeps node_modules, the tests, and src/ out of the tarball.

License

MIT © overspread.

This is a personal, community plugin. It is not developed, endorsed, or supported by DeepSeek, and it is not part of the DeepSeek Harness distribution. It only reads and writes inside your own $DSH_HOME; review the source before installing, as with any third-party plugin that runs on your machine.