# DSH-DELETE **English** | [中文](README.zh.md) Recoverable conversation deletion for DeepSeek Harness: adds a **Delete conversation** item to the session row `…` menu and registers the `/delete-conversation` human command. After confirmation the target Session enters a deletion queue and is immediately hidden from the list through the Workspace persisted archive projection; on the next DSH startup the plugin validates the Session's full lifecycle before the Web Workspace builds its session index, then atomically moves its log directory into a recoverable trash. > Derived from [omdsh-dev/dsh-essential](https://github.com/omdsh-dev/dsh-essential) (MIT, Copyright (c) 2026 ExDragine). This repository renames the package / Remote IDs / UI class names to `dsh-delete` and adds a `stateDir` default (`~/.dsh/conversation-trash`) so marketplace one-click installs need no manual config. ## Why deletion is two-phase DSH's Session persistence is append-only and the public services expose no delete method; an active Agent's destroy handle belongs to its creator only. The plugin cannot safely move logs out from under an active writer. Deletion therefore happens in two phases: the running process only writes a durable request and hides the row immediately; the next startup completes the log move before any session recovery. This boundary avoids log rebuilds, partial writes, and lifecycle races. ## Install ### Option 1: DSH plugin marketplace (recommended) This repository ships built artifacts (`lib/`), so marketplace one-click install needs no build and no config: 1. DSH Web GUI → Settings → DSH plugin marketplace 2. Search `DSH-DELETE`, click **Install** 3. Restart DSH ### Option 2: official bundle install ```sh node /path/to/DSH/apps/cli/lib/bin.js plugin --profile web add /path/to/DSH-DELETE ``` Restart DSH afterwards. The bundle mounts `dsh-delete` and makes the Web Workspace wait for the `conversationDeletion` service to finish startup recovery before building the Session index. ### Option 3: write cordis.patch.yml directly ```yaml - insert: - id: dsh-delete name: dsh-delete config: stateDir: /absolute/path/to/conversation-trash - id: workspace inject: [conversationDeletion] ``` `stateDir` must be absolute and outside Session artifact directories; it defaults to `~/.dsh/conversation-trash`. ## Usage - Open the target session's `…` menu in the session list and choose **Delete conversation**. Confirmation is an in-page card (title "Confirm deleting this conversation?", actions "Cancel / Delete conversation") — no native browser dialogs. - A confirmed request archives the Session immediately, hiding it across all session groups (the row disappearing is the success feedback). - If the request was written but the archive call failed, the card reports "queued but cannot hide yet" instead of misreporting a failed deletion. - Human command: `/delete-conversation CONFIRM` (deletes the current conversation); `/delete-conversation CONFIRM` (deletes another conversation by Session id). After confirming, restart DSH; logs are moved to `/trash//`. `deletion.json` inside records the original directory. ## Recovery Stop DSH, delete the `deletion.json` inside the recovery directory, then move that directory back to `originalDirectory`. Do not recover while DSH is running. ## Config | Field | Type | Default | Description | |---|---|---|---| | `stateDir` | string | `~/.dsh/conversation-trash` | Directory holding pending requests and the recoverable trash; must be absolute and outside Session artifact directories | | `commandName` | string | `delete-conversation` | Optional human-command alias (no leading `/`); the menu always uses the stable `/delete-conversation` | ## Safety and recovery semantics - The command requires the literal `CONFIRM`; nothing is written without it. - Requests bind `sessionId`, `createdAt`, and `cwd`; a reused id will not delete a new Session lifecycle. - The move re-validates the persisted list, backend location, and full inspect results. - The product operation uses same-filesystem directory `rename`, never `unlink` of Session logs; post-move crashes are recoverable. - Validation/move failures keep the pending request and record an error; the original log stays untouched. - **Per-Session JSONL backends only**: SQLite has no independent artifact location, and the plugin refuses it explicitly. - **Not permanent erasure**: logs go to a recoverable trash; retention and final cleanup are deployment-administrator policy. ## Building from source Requires Node `^22.19.0 || >=24.0.0` (tsdown needs `Promise.withResolvers`). On Windows keep the Node 22+ used for building first in PATH: ```sh pnpm install pnpm add -D -w unrun # optional peer of tsdown; must be explicit when autoInstallPeers=false pnpm run build pnpm run verify:self-contained pnpm run typecheck pnpm test ``` ## License MIT. Original copyright belongs to [dsh-essential](https://github.com/omdsh-dev/dsh-essential) (Copyright (c) 2026 ExDragine); see [LICENSE](LICENSE).