dsh-trash
Delete DeepSeek Harness sessions from the sidebar — log, workspace accounting, and archive/pin sets, all at once.
DSH lets you archive a session. Nothing removes it: the row stays in the sidebar and the log stays on disk forever. This bundle makes deleting a session a first-class action — one menu row in the sidebar, plus the same operation exposed to the agent as a tool.
┌──────────────────────────┐
session-c9456202 ⋯ │ Rename │
│ Pin │
│ Archive │
│ ──────────────────────── │
│ 🗑 Delete session │ ← this plugin
└──────────────────────────┘
Table of contents
- Features
- Requirements
- Installation
- Usage
- What actually gets deleted
- Limits and safety
- How it works
- Configuration
- Troubleshooting
- Uninstalling
- Project layout
- Development
- Contributing
- License
Features
- One row in the session menu. Danger styling, its own glyph, a confirm dialog naming the session, and toast feedback — it looks and behaves like a built-in action because it is rendered by the built-in Context Menu.
- Nothing left behind. The row disappears immediately (no page reload), the log directory is gone, and the session no longer counts toward Workspace, archive, or pin bookkeeping.
- Same operation for the agent. The
session_deletetool takes a list of ids, so "delete yesterday's throwaway sessions" is one message. - Zero dependencies, zero build step. Plain ESM on the Host, a plain
window.__ModuleLoader__factory in the browser. No React copy, no bundler. - Zero prompt cost beyond the tool schema. No system-prompt section, no context injection.
Requirements
| DSH | 0.2.2-rc.1 (verified). Requires the shipped connection and webServer services for the menu path, i.e. any Web/Desktop profile. |
| Node.js | bundled with DSH — nothing to install |
| Persistence backend | @deepseek-ai/dsh-session-persistence-jsonl (the shipped one) for the log-directory path |
| Optional | workspaceRegistry for grouping cleanup — absent, everything else still works |
Installation
Clone, then install the bundle in the profile you actually use:
git clone https://github.com/BarryAlllen/dsh-trash.git
plugin_manager
action: install_bundle
target: /absolute/path/to/dsh-trash
install_bundle links the directory into the profile, registers the bundle
patch, and applies it live. The plugin affects every session in that profile
and survives restart. A linked directory means later git pulls are picked up
after a DSH restart (or a page reload for the Client half).
If the menu row does not show up, reload the page once — Client modules are served on boot.
Prefer the DSH UI? Plugins → Install → point it at the cloned directory does the same thing.
Usage
Sidebar
Hover a session row → ⋯ (or right-click it) → Delete session, at the end of
the menu behind a separator → confirm.
Agent tool
{ "sessionIds": ["session-c9456202-3d3d-4cf9-9aa8-a1fd883a0fbc"] }
Result is per session, so a partial batch tells you exactly which ones failed:
{
"results": [
{
"sessionId": "session-c9456202-3d3d-4cf9-9aa8-a1fd883a0fbc",
"ok": true,
"title": "杂活",
"dir": "/Users/you/.dsh/sessions/--Users-you-workspace-tmp--/session-c9456202-3d3d-4cf9-9aa8-a1fd883a0fbc",
"resident": false
},
{
"sessionId": "session-does-not-exist",
"ok": false,
"error": "session \"session-does-not-exist\" does not exist"
}
]
}
What actually gets deleted
In order, and only for a session that passes the checks below:
- Workspace accounting — the id is dropped from its Workspace's
sessionIds, so it stops being a member of that group. - Archive and pin sets — the id is dropped from both registry-global sets.
- Durable log — the session's own directory is removed recursively:
<session-root>/<project-key>/<encoded-id>/, which holds every format generation (session.v4.jsonl.zstd, older generations,session.lock). - Client notification — one
api-session/removedemit, which the shipped Client already relays into a listremovemutation. Rows disappear without a reload; no second copy of the delete logic lives in the browser.
Steps 1–2 are derived surfaces, so a failure there is logged and does not block step 3.
Limits and safety
- Irreversible. There is no trash folder, no undo, no backup. Use
session_search/session_listfirst if you are unsure. - Running sessions are refused. Stop the session, then delete it.
- Sessions open during the current run are deleted from disk but remain resident in memory until DSH restarts; the toast says so. They do not re-materialize on disk.
- The log directory is the only artifact deleted. Attachments, spill payloads, projections and the search index are derived or shared; leftovers are unreachable once the session is gone and are not touched.
- Path safety. The session directory is recomputed from the persistence backend's own layout and refused if the result escapes the session root.
How it works
| Piece | Mechanism |
|---|---|
| Menu row | ctx.contextMenus.register({ target: 'session' }) plus a glyph registered in the contextMenu.icon slot |
| Client → Host | POST /api/session-trash/delete, registered with ctx.connection.fetch.register — the browser transport's own cookie auth, Host/Origin fence, and owner-peer check apply |
| Agent → Host | session_delete tool on ctx.tools, with a JSON-Schema-validated output |
| Shared logic | one implementation in index.js; both callers return the same per-session result |
| Root discovery | read from the live persistence backend (ctx.sessionPersistence.root), falling back to profile configuration; never hard-coded |
| Path layout | copied from the JSONL backend's published layout (<root>/<project-key>/<encoded-id>/) |
Configuration
None. The plugin has no config schema, no credentials, no settings row.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
| No Delete session row | Reload the page once. If it persists, confirm the bundle entry is active in Plugins. |
HTTP 404 in the toast |
The Host half is inactive or the transport lacks the shared /api channel — check that the bundle entry is enabled. |
the session is running; stop it before deleting |
Stop the session first. |
Row disappears but the id is still in workspace.json |
Grouping cleanup was logged as a warning and skipped; the session is still gone. Open an issue with the log line. |
| Deleted session is still in the list | It is resident in memory for this run — restart DSH. |
Uninstalling
plugin_manager
action: remove_bundle
target: @local/dsh-session-trash
Removes the bundle row and the link. Sessions you deleted stay deleted — that is the point — and every other plugin is untouched.
Project layout
package.json bundle manifest: dsh.bundle.patch + dsh.client section
cordis.patch.yml one inserted row: session-trash
index.js Host half — deletion, Fetch route, tool
client.js Client half — menu row, glyph, confirm, toasts
locale/zh.json plugin page title and description (Chinese)
locale/en.json plugin page title and description (English)
icon.svg plugin artwork
Development
The bundle is plain JavaScript, so there is no build:
node --check index.js client.js # syntax gate
To iterate on a live profile:
- Edit
index.js. Host code reloads through the bundle entry (plugin_manager→ disable → enable); a brand-new module generation needs a DSH restart. - Edit
client.js. Client bundles are served on boot — reload the page. - Watch for
application: appliedin theplugin_managerresult — that, not a process list, decides whether a change is live.
The deletion path was verified against DSH 0.2.2-rc.1: tool success path (log
directory gone, no re-materialization after restart), the unknown-id error path,
and live Client slot registration.
Contributing
Issues and pull requests are welcome — see CONTRIBUTING.md. Security-relevant reports: SECURITY.md.
License
MIT © 2026 Barry
No comments yet. Be the first to write one.