DSH Plugins Marketplace

DSH Plugins

Plugins

/

Sessions & Messages

/

dsh-plugin-session-delete

S

dsh-plugin-session-delete

Manifest valid

A DSH plugin that deletes a conversation from disk — the destructive Session action DSH deliberately omits — plus bulk cleanup of empty Sessions. For DSH 0.2.x.

UI (client)hasBundlePatch

description: "Delete one conversation permanently from the Web sidebar, plus bulk cleanup of unused blank Sessions." kind: "package-reference"

dsh-plugin-session-delete

English | 中文

License: MIT DSH 0.2.x verify

A DSH plugin: delete a conversation, for real, from disk — plus bulk cleanup of empty Sessions.

DSH has no delete on purpose: archiving is the shipped non-destructive path, and its storage backend (SessionPersistence) only creates, opens, flushes, stats and lists. This plugin exists for the other case — when you want the bytes gone — and keeps every policy decision (what may be removed, what must be refused) in the plugin rather than in a storage layer.

The delete row at the end of a Session's "..." menu

Before you install: removal is a move, and there is a window to undo it. The whole Session directory — every format generation of its log — plus its projection-cache record is renamed into a trash beside the sessions root (the same volume, so the move is atomic), where it stays restorable for a retention window — seven days by default, or whatever the profile configures. The sidebar foot lists what is in there so you can put one back or destroy it now; anything left when the window closes is purged. A Session that is live in the process is refused (session-live), and the cleanup action additionally skips the open Session, anything running, and anything with activity inside a one-hour grace window. Attachments, exported files and workspace files are never touched.

The confirmation names the conversation and what is removed

Install

# from GitHub — use the https form (see the note below)
dsh plugin --profile <profile> add https://github.com/SereinHK/dsh-plugin-session-delete.git

# or the packaged tarball attached to the latest release: no git needed
dsh plugin --profile <profile> add https://github.com/SereinHK/dsh-plugin-session-delete/releases/download/v0.1.2/dsh-plugin-session-delete-0.1.2.tgz

Or paste the same spec into the sidebar's Plugins page. The package carries its own bundle patch (dsh.bundle.patch), so the plugin row is inserted for you — no hand-editing of the profile's cordis.patch.yml. Reload the window (or the instance) afterwards; lib/ ships prebuilt, so nothing is compiled during install.

Two distribution forms, one package. The git address is what the project itself uses and follows a branch; the release tarball is a frozen snapshot of the same bytes and needs no git on the installing machine — pnpm fetches an https URL like any other. Both install identically: the tarball ships lib/, cordis.patch.yml and the manifest that declares the bundle patch.

Not published to npm. This repository is the distribution channel, so the registry form (dsh plugin --profile <profile> add dsh-plugin-session-delete) has nothing to resolve — use the https://… address above.

Use the https://… address, not the github:owner/repo shorthand. pnpm normalizes the shorthand to an SSH URL (git+ssh://git@github.com/…), which fails on any machine without an SSH key for GitHub. Both forms select the same commit; the https one needs nothing but the public repo.

What gets pinned, and what does not. The install records the dependency without a ref, so it follows the default branch — but pnpm's lockfile pins the exact commit it resolved, so re-running the install does not move it on its own. To follow a release deliberately, write the ref into the profile's package.json and re-run the install:

"dsh-plugin-session-delete": "git+https://github.com/SereinHK/dsh-plugin-session-delete.git#v0.1.0"

The desktop app supplies its own package manager, so the Plugins page works without pnpm on PATH. A bare dsh plugin command needs pnpm available.

Requirements: DSH 0.2.x. The row menu extension point this plugin registers into (sidebar.workspaces.session.menu.item) does not exist in the 0.1.x line — on that build only the sidebar cleanup action can appear. Verified against @deepseek-ai/dsh-desktop-runtime 0.2.0-rc.2.

Uninstall: remove it from the Plugins page, or dsh plugin --profile <profile> remove dsh-plugin-session-delete. From a source checkout, node install.mjs --uninstall.

Summary

  • sidebar.workspaces.session.menu.item — a red “Delete conversation” row in every Session's ... menu, after the shipped pin/rename/fork/archive rows.
  • sidebar.footer.action — “Clean up empty conversations” beside Settings: removes blank Sessions (blank: true, i.e. never started a turn) in one run, and “Trash” next to it, listing what was removed and offering restore or destroy.
  • shell.overlay — the confirmations and the trash listing, each naming Sessions by their title (or path) and saying what they hold: the cleanup shows what a run would reclaim, and the trash shows how long each entry has left.
  • The node half registers five routes inside Connection's authenticated /api fence: POST /api/session.delete (the move into the trash), POST /api/session.unused (the durable per-Session facts — is the log empty, and when was it last prompted — which the page cannot derive for itself), POST /api/session.trash (the listing, which purges what has expired before answering), POST /api/session.restore, and POST /api/session.purge (one entry, or every one of them).

The cleanup lists what it would remove, and says what it skips

Deletion is irreversible: the whole Session directory under the sessions root (every format generation) and its projection-cache record are removed. Exported files, attachments and workspace files are untouched.

Use this package

A Session row's ... menu gains:

Pin / Rename / Fork / Archive
─────────────────────────────
Delete conversation          ← this package, order 900, danger

The dialog names the conversation, states the scope, and only then offers Delete permanently. After a successful removal the Host emits api-session/removed (the row disappears immediately) and the browser re-pulls the authoritative list.

Cleanup lists the blank Sessions it found — workspace path and idle age — before offering a count-labelled confirm, then runs one removal at a time and reports deleted / failed / skipped when it settles.

What is skipped, and why

SituationBehaviourReason
The conversation is open in a windowHost answers 409 session-liveIts write handle would recreate the log or corrupt it
A blank Session has activity inside the last hourSkipped by the client plan“New Session” is blank too, and another window may be sitting in it
A blank Session's Agent is runningSkipped by the client planNot an abandoned draft
The id is not session-id shapedHost answers 400 invalid-session-idThe id becomes a path segment; it is validated before any path work
The target is not <root>/<project>/<encoded id>Host answers 500 unsafe-targetLast gate before a recursive removal
Attachments (<DSH_HOME>/attachments)Never touchedContent-addressed and shared across Sessions, with no refcount

Failures

The route answers { ok: false, error: { code, message } }; the dialog words the codes it owns (session-live, session-not-found, unsafe-target) in the active language and shows the Host's own diagnostic beneath it.

How it works

SessionPersistence exposes create/open/flush/stat/list and nothing else, /clear only drops the selection, and archive only hides a row — so a plugin that deletes must own the removal. This one addresses the layout the shipped JSONL backend writes:

<root>/<projectKey(header.cwd)>/<encodeSegment(sessionId)>/session[.vN].jsonl[.zstd]
<root>                                        → dshHomePath('sessions')

projectKey and encodeSegment are re-implemented here (the backend does not export them) and are exercised against the real session root by the test suite. A directory scan under the root backs them up, so a layout change degrades to session-not-found rather than to a deletion somewhere else; containmentRefusal then re-checks that the target is exactly one project directory below the root and named after the encoded id.

The whole surface arrives through one route rather than Typert Remotes, because a plugin's browser half cannot ship a generated ./remote contribution without the in-tree generator — ctx.connection.fetch.register is the supported seam for a plugin-owned endpoint, and it inherits the Host/Origin + browser-token fence.

Verified against

ItemValue
Runtime@deepseek-ai/dsh-desktop-runtime 0.2.0-rc.2 (cordis 4.0.4)
Slotssidebar.workspaces.session.menu.item, sidebar.footer.action, shell.overlay
Host servicesconnection, sessionPersistence, sessions
Browser seed modulesreact, @deepseek-ai/dsh-client-ui-primitives
StorageJSONL session backend at dshHomePath('sessions'); projection cache at dshHomePath('storages')/session_projcache

Known limitations

  • A live Session cannot be deleted. This is the deliberate half of the contract: switching away and deleting it afterwards is the supported path. A core SessionPersistence.remove plus a session-controller endpoint would let a Host stop and release the Session first; see the proposal in this workspace's docs/upstream-issue-session-persistence-location.md.
  • Deleting a parent orphans its children. Child logs are separate (parentSession is lineage metadata); sessionQuery.traceSession degrades to the first unresolvable parent instead of failing.
  • No trash, no undo. The removal is rm -rf on the Session directory. Export first (/export) when a conversation matters.
  • Version gating is deliberately absent. A @deepseek-ai/dsh-* peer would be checked against the runtime version by app-boot, so ^0.2.0 would reject a running 0.2.0-rc.2. This package codes against stable service and slot shapes and would rather fail loudly at use than refuse to mount.
  • README.i18n.yaml is not hand-written here: their pairing tooling generates the section hashes (pnpm run verify-translation-pairing --write …).

Development

The repository root is the package, so that a git install resolves its manifest.

node tools/build.mjs            # src/ -> lib/  (also: node tools/build.mjs --check)
node --test "tests/*.test.ts"   # 30 tests: host routes, the trash, cleanup plan, dictionaries
node tools/verify-artifact.mjs  # 87 host-route checks against the built bytes
node tools/verify-browser.mjs   # 64 render/interaction checks, refusals included
node tools/verify-bundle.mjs    # is the package installable, and installed coherently?
node tools/verify-runtime-contract.mjs   # do this plugin's assumptions about the
                                    # installed runtime still hold? (run after a DSH upgrade)
node tools/verify-boot.mjs --scan   # which running instance carries the row
node tools/demo-home.mjs            # an isolated home (own DSH_HOME) with blank
                                    # Sessions, for screenshots or a safe review
node install.mjs [--profile web] [--bundle] [--register-only] [--uninstall]

tools/demo-home.mjs copies a working profile into a separate DSH_HOME and seeds blank Sessions there, so the UI can be exercised (or photographed) without exposing a real workspace. Run that home with DSH_HOME=<dir> dsh --profile demo --port <n> — note that with a profile name, dsh takes no subcommand: --profile <name> is the request to run it.

Requires Node 22.6+: the build and the suites use Node's own TypeScript stripping rather than a toolchain. CI runs exactly the commands above on Node 24 (.github/workflows/verify.yml), with no install step and no network.

lib/ is committed on purpose: DSH serves prebuilt client bundles and runs no bundler at install time, so a git install needs the built bytes in the tree. That is the one place this package differs from the upstream monorepo convention, which ignores lib/ and builds in CI before publishing. After editing src/, run node tools/build.mjs and commit lib/ alongside it — a stale lib/ is a plugin that silently does nothing.

The build is dependency-free: Node's own module.stripTypeScriptTypes plus a small assembly of the src/client/* graph into the exact window.__ModuleLoader__.load({ id, factory }) script the host serves. It accepts a declared subset of TypeScript and fails loudly outside it (a require() for anything but a platform seed module would take the whole page's boot down). Upstream would run pnpm run bundle (tsdown) instead; the sources are written to their conventions.

tools/ also carries the diagnostics this plugin was debugged with, and they are worth keeping for the next plugin: verify-boot.mjs --scan (every instance on the machine, and whether it carries the row), force-reload.mjs (does the Loader still rebuild the profile tree?), probe-reload.mjs (do patch edits still apply?), verify-live.mjs (are the served bundle bytes the installed ones?), asar.mjs (read the running app's app.asar).

Repository layout

package.json  cordis.patch.yml  LICENSE  .gitignore   # the package itself
src/          lib/            tests/                  # sources, built artifacts, suites
tools/        install.mjs     install.ps1             # build, verification, local install
docs/development.md                                   # debugging notes and pitfalls
PR.md  docs/upstream-issue-*.md  upstream/            # material for an upstream PR; unrelated to installing

Comments

Loading…

From the same category

billion-context

by ranxianglei

基本稳定可用 100K tokens is enough. Universal context-compression proxy for ALL AI coding agents,10w上下文足矣

Sessions & MessagesManifest valid

★ 814

↓ 423.4k/wk

MIT

TypeScript

Oct 11, 2026

dsh plugin --profile web add billion-context

by Han-1413141

DeepSeek Harness session cost meter plugin: session/daily cost, budget, history, OpenCode Go quota, official & custom-provider balance, Codex-like token heatmap, peak/off-peak pricing with pre-switch

Sessions & MessagesManifest valid

★ 388

↓ 23k/wk

MIT

JavaScript

Oct 10, 2026

dsh plugin --profile web add dsh-cost-meter

by Nwflower

Import 14+ external agent chat histories (Claude Code, Codex, ChatGPT, Cursor, Gemini, Reasonix, opencode, ZCode, Grok Build, OpenClaw, Pi, Hermes, Kimi CLI, DSH) into DeepSeek Harness as resumable se

Sessions & MessagesManifest valid

★ 224

MIT

JavaScript

Oct 7, 2026

dsh plugin --profile web add dsh-chat-import

by Totoro-qaq

DeepSeek Harness plugin for previewable cross-preset session migration. Fixed-schema handoffs preserve state, source-model intent, and unresolved images; the original session stays untouched.

Sessions & MessagesManifest valid

★ 165

MIT

JavaScript

Oct 11, 2026

dsh plugin --profile web add dsh-plugin-bridge

by Anionex

deepseek harness对话和代码状态回退插件 | DSH — rewind conversation and workspace state, powered by a persistent Change Ledger

Sessions & MessagesManifest valid

★ 131

BSD-3-Clause

JavaScript

Oct 1, 2026

dsh plugin --profile web add @anionex/dsh-turn-rewind

by SiriLee

DSH 插件:真正便捷无感的同窗口内对话回退,从不新建分支;自带轻量工作区备份,可一并还原文件(完整 Claude Code /rewind 语义)。 · DSH plugin: genuinely effortless in-window conversation rewind — never forking a new session; ships a lightweight workspac

Sessions & MessagesManifest valid

★ 123

↓ 5.5k/wk

MIT

TypeScript

Oct 9, 2026

dsh plugin --profile web add dsh-rewind-plugin