dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Session trash for DeepSeek Harness Desktop: move sessions to a recoverable trash from the sidebar, restore or purge them from a settings page.
Session Trash for DeepSeek Harness Desktop — move a session into a recoverable trash from the sidebar menu, then restore or permanently purge it from Settings → Session Trash.
English | 中文
Harness keeps every session log forever. sessionPersistence has no deletion
API — "Nothing deletes session files" — and the workspace registry only
archives, leaving the log on disk. This plugin adds the missing capability in
the safest shape it can take: a recoverable move, a user-only trigger,
and no filesystem path ever crossing the wire.
| Where | What it does |
|---|---|
Session row → … menu | Move to trash — the host's own MenuItemButton geometry (same height, padding, icon cell and label rhythm) with its danger colouring |
| Confirmation dialog | Shows the title, original path and size before anything moves; the destructive confirm is the host's outline-with-red-text pattern |
| Settings → Session Trash | Lists deleted sessions with Restore and Purge, plus Empty trash |
| Host startup | Removes entries older than retentionDays (default 30), silently |
A deletion moves the session's directory with a single rename into
$DSH_HOME/session-trash/<UTC stamp>-<session id>/, and writes a
manifest.json beside it. Nothing is copied, so an interruption cannot leave a
half-deleted session, and a failed move leaves both directories exactly as they
were.
The plugin is an ordinary Harness bundle, so it installs where every other plugin does:
dsh plugin --profile desktop add github:xxiiiixx/dsh-session-trash
From a local checkout, point Plugin Manager at the directory (Settings →
Plugins → Add, or the plugin_manager tool with install_bundle). The row it
inserts is named dsh-session-trash, and it is enabled immediately. Restart
Harness if Plugin Manager reports restart-required.
A GitHub install copies the package into the profile's node_modules; it does
not link back to a checkout. Editing a checkout therefore changes nothing
until the change is pushed and the installed copy is replaced.
cd <path-to-your-checkout>
git add -A && git commit -m "..." && git push
Then tell Plugin Manager to reinstall the bundle (remove_bundle followed by
install_bundle with github:xxiiiixx/dsh-session-trash, or the same two steps
in Settings → Plugins). Reinstalling a package requires a Harness restart to
load the fresh JavaScript generation for the Host half; the Client half is
picked up on the next page load.
If you would rather have a checkout's edits take effect immediately, install it by directory path instead — that produces a junction into the profile, and a moved or renamed checkout then breaks the plugin, because the link points at the old location.
The bundle patch inserts the Host row with no config: the plugin resolves both roots from the running DSH home at activation, so a normal install needs no per-machine editing. Every field stays overridable from the profile's own patch layer.
| Field | Default | Meaning |
|---|---|---|
sessionsRoot | $DSH_HOME/sessions | Where the session backend stores logs |
trashRoot | $DSH_HOME/session-trash | Where deleted sessions go; must be on the same volume as sessionsRoot |
retentionDays | 30 | Automatic purge age; 0 disables the sweep |
sweepOnStart | true | Run the retention sweep once per Host start |
maxTrashEntries | 500 | Refuse new deletions above this count; 0 means unlimited |
- id: dsh-session-trash
name: 'dsh-session-trash'
config:
retentionDays: 90
Deleting a session is destructive, so most of this plugin is refusals rather than deletion.
{ op, sessionId } or
{ op, trashId }. The Host resolves identifiers against its own roots; there
is no path field to smuggle.^[A-Za-z0-9._-]+$, ≤ 200 characters, never
./..) and every resolved path is checked with path.relative after
realpath, so a symlink or a crafted id cannot escape a root.header.id matches the requested
session.status is running. An idle Agent that merely stays resident in the
registry does not block — a session you already stopped is deletable. An
Agent surface that cannot report a status is treated as running, so the
plugin never guesses its way into moving a log that may still be written.origin: subagent exists only to carry out work for its parent,
so a stopped one moves into the same trash entry and returns with the
parent on restore. A fork (isSeeded) is seeded from the parent's history
and then stands on its own: it stays put and does not block. A derived child
that is still running, or that has children of its own, refuses the whole
carry and says why.rename only, never copy-then-delete. EXDEV, EPERM and EBUSY are
reported as cross-volume / busy; no fallback exists that could leave a
duplicate or a partial copy.POST /api/session-trash is
mounted on ctx.connection.fetch, so it inherits Harness's Host/Origin and
browser-credential checks. Bodies are capped at 64 KiB, operations are a
closed set, and failures answer with a stable code plus a fixed sentence —
never a filesystem path.api-session/removed, the same event
the sidebar already listens for, so the row disappears immediately without
any plugin-private state.npm test # 65 cases: paths, config, trash service, HTTP route, Client bundle
npm run e2e # end-to-end walk through the HTTP handler
npm run audit # read-only verdict for every real session on this machine
npm run check:css # diff the copied styles against the host's own stylesheet
npm run check:liveness
The suite builds throwaway session roots with real Zstandard logs and asserts:
read-only preflight, identity-mismatch refusal, running and derived-child
refusals, atomic move with rollback, manifest round-trip, restore collision,
purge containment (../../etc is rejected), corrupt-manifest handling,
retention sweep, capacity refusal, and serialized concurrency. The Client suite
executes client.js inside a stubbed module loader and asserts the stylesheet is
installed before any slot registers — the regression that once left the menu row
looking like a bare browser button.
@deepseek-ai/dsh-session-persistence-jsonl — the on-disk layout this plugin
reads and moves.@deepseek-ai/dsh-workspace — the registry whose sessionIds sets deletion
updates.This plugin deletes data. Read this before you install it.
sessionsRoot and can permanently remove them on request. A deletion you
confirm is a change to your own files; the author is not responsible for lost
sessions, lost history, or work that cannot be reconstructed. Back up anything
you cannot afford to lose before trying it for the first time.cross-volume,
busy, live, has-children and the other codes rather than forcing a
deletion, and it rolls back a partial carry. A refusal is the safe outcome, not
a defect — do not work around it by editing files by hand.$DSH_HOME/sessions/<project>/,
the shipped JSONL backend's layout. A different persistence backend would need
its own locator.trashRoot must sit on the same volume as
sessionsRoot. The default keeps both under $DSH_HOME, so this only matters
if you move one of them.MIT. See LICENSE.