deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
Permanently delete a single session from the DeepSeek Harness Web sidebar — one extra entry in the session row's
⋯menu.
English · 中文
dsh-delete-session adds a Delete session action to the sidebar session-row menu (next to Rename / Fork / Archive) and permanently removes that session's transcript from disk.
It does this without replacing or disabling any official plugin row and without taking over any host service, so it cannot break the plugin tree the way a service-replacing plugin can.
| Permanent delete | Removes the session's per-session JSONL transcript directory from disk. No recycle bin, no undo. |
| Confirmation is mandatory | A modal naming the session is required, and the request carries a non-simple custom header that a plain HTML form or link cannot forge. |
| Running sessions are stopped first | The live agent is torn down through its owned host handle; persistence is drained and the session is verified detached before any file is touched. |
| Bookkeeping reconciled | The session is detached from its workspace, removed from the archive set, and the UI is told to drop the row — no manual refresh needed. |
| Zero takeover | The bundle inserts exactly one plugin row. It disables nothing, replaces nothing, and publishes no service. |
| Theme-aware | The confirmation dialog uses DSH's own theme variables, so it follows light and dark mode automatically. |
| Localised | Menu entry and dialog follow the host locale (zh / en) through the host locale service, with a built-in fallback table when that service is absent. |
dsh web).session-persistence-jsonl), which is the stock backend.0.1.5-rc.2. The delete route is fail-soft: if a precondition is missing the route is simply not registered and the UI reports a request failure — the plugin never throws during activation.# from a git checkout
dsh plugin --profile web add github:masknull/dsh-delete-session
# or from a local directory
dsh plugin --profile web add link:/path/to/dsh-delete-session
Restart dsh web afterwards so the plugin tree is recomposed.
dsh plugin --profile web remove dsh-delete-session
All that was needed was the delete action. Session-management plugins of this kind usually bundle a whole suite along with it — archiving, restore, search — and there were also intermittent dsh web startup failures that took an afternoon to pin down. Since only deletion was needed, this plugin was written separately to do exactly that and nothing else.
DSH currently exposes no public API for deleting a session: ctx.workspaceRegistry offers archiveSession (soft, marker-only) and deleteKnown (which deletes a workspace, not a session), and the api-session-controller remote surface has create / rename / fork / list / search — but no delete. On top of that, the session-row ⋯ menu is not an extension point: its items are hardcoded inside the official ui-workspace SessionNodeItem, and no slot exists anywhere inside a session row.
So instead of forking the official UI, this plugin uses two DOM seams that were verified live against the running Web UI:
Menu portal component sharing one [role="menu"] DOM under document.body — DOM structure alone cannot tell them apart. The open menu element's React fiber chain still reaches its owner, and only a SessionNodeItem owner (memoizedProps carrying node.id plus onRename, a shape unique in the client tree) is eligible for injection. The session id and title are resolved from those props at injection time; there is no cached "last clicked row" state that could go stale and act on the wrong session.IconTrashOutline16 geometry lifted from the shipped frontend bundle, so it matches the three native icons.If either seam is gone (a future official UI change), the entry is simply never injected — the plugin never falls back to guessing a session id.
One route: POST /plugins/dsh-delete-session/delete.
The deletion itself is a port of dsh-chat-manager's implementation (MIT — see License), which is careful in ways worth keeping:
dev/ino/mode/size/birthtimeNs before the recursive delete (defence against TOCTOU and symlink swaps);agents.create / resume / enter, and a reservation refuses to reopen the session while its deletion is in flight;Added on top of the port: after the physical delete, the plugin reconciles accounting through public API —
workspace.list() → find the owning entity → entity.detachSession(id) (public, idempotent, never touches the session log),
then removes the id from archivedSessionIds, then emits api-session/removed so the official client drops the row.
The projection cache row (session_projcache) is intentionally left behind. The session list is built from a disk scan plus a host/client double join filter, so an orphaned cache row never renders as a visible entry. Cleaning it would require replacing the official session-projection-cache service — exactly the kind of takeover this plugin exists to avoid.
The confirmation dialog takes every colour from DSH's own theme variables. Those are declared on <body> and re-declared under body[data-ds-dark-theme], so one declaration follows both themes:
| Purpose | Variable |
|---|---|
| Dialog surface | --dsw-specific-menu → --dsw-alias-bg-layer-3 |
| Primary text | --dsw-alias-label-primary |
| Border | --dsw-alias-border-l2 |
| Danger | --dsw-alias-state-error-primary |
| Scrim | --dsw-alias-bg-mask-1 |
| Shadow (carries the 0.5px hairline) | --dsw-elevation-panel |
| Font | --dsw-font-family |
Two traps learned the hard way, both documented in README.zh.md:
--dsw-alias-bg-elevated; they do not exist, so the surface fell back to a hardcoded dark grey while the text variable did resolve to the theme's dark colour — producing dark-on-dark in light mode.<body>, not :root. Reading them from document.documentElement returns empty strings.The plugin depends on the two DOM seams above. If an official UI change removes either one, the symptom is the entry missing from the menu — never a wrong deletion.
Check, in order:
[role="menu"] element whose fiber chain still reaches props carrying node.id plus onRename (the session row's menu)?[role="menu"] with [role="menuitem"] items?__reactFiber$ / __reactInternalInstance$ prefixes)?MIT. The deletion implementation under src/host/ is ported from
dsh-chat-manager (MIT); each ported
file keeps a header stating its origin and the changes made.
Thanks to dsh-chat-manager: this
plugin's core is built on its Host-side deletion implementation. The genuinely
hard parts — capturing the agent handle, the quarantine rename with dev/ino
identity re-checks, and the request boundary — were already solved well there,
which is exactly why this plugin could stay this small.
Thanks as well to the DeepSeek Harness team: the theme variables, the locale
service, and the IconTrashOutline16 geometry used here all come from the
official frontend.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。