npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@feecc/dsh-session-delete

v1.0.0

Published

Permanently delete conversations from the sidebar, in bulk, or together with a workspace.

Readme

Delete Conversations (@feecc/dsh-session-delete)

A DeepSeek Harness bundle that adds the one destructive capability the shipped packages deliberately omit: permanent deletion of conversations — one at a time, in bulk, or together with a workspace.

Harness itself only ever archives sessions and only ever removes a workspace registration: session-persistence exposes no removal API, the Workspace registry never touches logs, and @deepseek-ai/dsh-client-ui-workspace lists "no Session deletion" as a known limitation. This bundle adds that capability behind one Host route so every surface shares one implementation.

Surfaces

| # | Surface | Where | |---|---|---| | 1 | 删除会话 / Delete conversation | A destructive row in each conversation's … menu (sidebar.workspaces.session.menu.item, order 500, after Archive and Copy Session ID) and a trash button in the conversation header. Both open one confirmation dialog. | | 2 | 批量选择对话 | A checklist toggle in the Workspace browser header, immediately right of the search button (a square-with-check glyph). It turns the conversation list into a selection surface; a floating bar offers 全选 / 退出 / 删除所选 — 全选 becomes 清空 once everything visible is selected — and deleting asks for confirmation once. A batch that contains a running conversation still deletes every idle one and reports the rest as 无法删除运行中的会话. | | 3 | 同时删除该工作区下的全部对话 | A checkbox added to the shipped "删除工作区" dialog, aligned with the dialog's own description text. Confirming with it checked replaces the shipped delete with one Host operation that refuses the whole request while any conversation in the workspace is still running (workspace-active), so a running conversation can never be orphaned under Ungrouped. On success the workspace and its conversations are gone; on refusal the dialog stays open with the reason. |

Notices appear at the top of the window, not at the bottom.

All destructive paths ask for confirmation first, and the confirmation names what will disappear and states that it cannot be undone.

What "permanently delete" does (Host)

index.js serves one authenticated POST /api/session-delete route with four actions: delete-sessions, delete-workspace, workspace-sessions, and diagnose. Deleting one conversation:

  1. refuses while that conversation still has work in progress (session-active) — the activity check is the same workspace/session-activity waterfall that archiveSession uses — because a running turn keeps writing to the log;
  2. removes the session-owned directory under the persistence root (<root>/--<cwd>--/<session-id>/ or <root>/_no-cwd/<session-id>/), including every historical format generation;
  3. removes the write-behind projection-cache record;
  4. drops the Workspace accounting slot, the archive mark, and the pin;
  5. announces api-session/removed, so every connected Client drops the row.

A conversation that is still loaded in this process cannot be disposed from outside its own Agent (Harness exposes no disposal API), and the Host list merges loaded Sessions back in from memory. Such a conversation is therefore made unreachable instead: after its log is gone, its row is withdrawn and an archive mark keeps the default sidebar filter from listing it again. The plugin also remembers the id in-process, withdraws any later re-announcement of it, and at activation sweeps every loaded conversation whose log is already gone (plus any archive mark that refers to nothing), so leftover rows clean themselves up. Once the process restarts, the loaded Session is gone and nothing remains.

Direct subagent descendants of a deleted conversation are deleted with it (sessionQuery.traceSession), so a conversation tree leaves nothing behind. Requests are idempotent: deleting an id whose log is already gone still withdraws the row, purges its stored references, and reports success.

Running work and counting

One request classifies the whole batch before deleting anything, so a batch's outcome does not depend on where the sweep happens to reach a conversation whose turn ends mid-request: idle conversations are deleted, running ones come back as session-active refusals, and the Client reports them as 无法删除运行中的会话 while still confirming the deletions that succeeded. Deleting a workspace with its conversations is all-or-nothing instead: while any conversation in it runs, the whole request is refused and nothing is removed.

The count shown in the workspace dialog is the number of real conversations, not the raw accounting: a Workspace's sessionIds also collects blank placeholders (an unused "New Session" whose log carries only setup events) and ids whose log is gone. Classification uses the cached sessionListMetadata.blank projection — the same signal the sidebar uses to hide such rows — with the log size as a fallback.

Extension points used

Slots: sidebar.workspaces.session.menu.item, conversation.session.header.actions, shell.overlay (confirmation, selection bar, toast), and sidebar.footer.action (fallback for the multi-select toggle).

The two placements the shipped packages expose no slot for are small React roots appended to host markup this bundle owns:

  • the multi-select toggle, inserted right after the Workspace browser's search slot (before the view-options / add-workspace actions), hidden while the search field is expanded;
  • the extra option, appended below the description inside the aria-label-identified delete-workspace dialog.

Both are removed on dispose, are driven by one requestAnimationFrame-throttled MutationObserver, never read another plugin's source, and style through --dsw-alias-* theme tokens only.

Installing

The bundle is an ordinary profile bundle. It has no build step and no runtime dependencies (the Host half imports Node built-ins only; the browser half uses the module table's react and @deepseek-ai/dsh-client-ui-primitives seeds), so the source directory is the complete package.

From a registry, a git URL, or a packed tarball, install it as a profile bundle:

plugin_manager install_bundle target: "@feecc/dsh-session-delete"   # or github:owner/repo, or D:\x.tgz
dsh plugin --profile <name> add @feecc/dsh-session-delete           # CLI profile

From a directory on disk, either point install_bundle at its absolute path, or install it by hand:

  • place the directory in <profile>/node_modules/@feecc/dsh-session-delete (a link:/junction to the checkout also works, and makes edits live);

  • declare it in the profile package.json:

    {
      "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "@feecc/dsh-session-delete"] } },
      "dependencies": { "@feecc/dsh-session-delete": "link:D:\\plugins\\dsh-session-delete" }
    }

The profile's own patch layer stays untouched; cordis.patch.yml here inserts the single Host row. A configuration refresh (any edit to the profile's cordis.patch.yml, or a plugin-manager operation) mounts it live; no application restart is needed.

peerDependencies pin the Host packages this bundle is built against, so a DSH version mismatch is refused by the plugin manager's compatibility check instead of loading a plugin whose assumptions no longer hold.

Uninstalling

Remove @feecc/dsh-session-delete from dsh.profile.bundles (and from dependencies), delete the package directory, and refresh the profile configuration.

Sharing and publishing

Three routes, in increasing formality:

  1. Send the directory or a tarball. npm pack produces feecc-dsh-session-delete-1.0.0.tgz (about 24 kB) that contains only the files listed in files; install_bundle accepts the .tgz path directly. A zip of the directory works the same way for a manual install.
  2. Push a git repository and have the colleague install github:owner/repo; the repository root is the package root.
  3. Publish to npm. npm publish uses publishConfig.access: public, so the scoped package is public. Keep the name identical in package.json, cordis.patch.yml (name:) and client.js (window.__ModuleLoader__.load({ id: ... })): the Client module system keys the browser bundle by package name, and a mismatch reports bundle loaded without registering "<id>".

After installing, check the three surfaces: 删除会话 in a conversation's … menu, the square-with-check toggle right of the sidebar search, and 同时删除该工作区下的全部对话 in the delete-workspace dialog.

Verification

  • node test/host.test.mjs drives the real route handler against a fake Context and a disposable session root: running-work refusal, batch classification, subagent tree deletion, _no-cwd lookup, projection-cache removal, Workspace/archive/pin purge, api-session/removed, re-announcement withdrawal, workspace all-or-nothing deletion, blank-session classification, malformed input, and idempotence. All assertions pass.
  • node test/client.test.mjs evaluates the real client.js in a vm sandbox with a fake DOM and stubbed React: bundle registration, inject list, all six slot registrations, the menu row, the header button, the selection bar and its 全选/清空 toggle, the confirmation dialog and its Host call, deterministic partial/refused outcomes, the main-view recovery, the toast, header-seat placement between the search slot and the actions, the search-expansion hiding, the workspace-dialog option (inset and conversation count), the row-click selection interception, workspace-wide deletion, and disposal. All assertions pass.
  • Against the live profile: the composed configuration resolves the bundle (dsh --profile <copy> --dump-config), the Host row mounts on a configuration refresh, and in-process calls confirmed the real persistence root, the Workspace registry, the archive-set purge, and the conversation count (Downloads 2, TypeScript 1 for the observed data).
  • Verified interactively in the running Web UI: all three surfaces render, the destructive flows delete what they promise, a running conversation is refused, and a workspace that contains one is left untouched.

Renderer placement is verified by use of the live page; the seat containers are still plain DOM insertion into host markup this plugin does not own, so a future harness layout change can require updating findWorkspaceHeader (search field) or matchWorkspaceDeleteDialog (dialog label).