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

@fufu1437/dsh-delete-chat

v0.1.4

Published

Permanently delete a DeepSeek Harness conversation and every local copy of its data: session log, derived caches, workspace index entries, spill files, and unreferenced attachments.

Readme

Conversation Deletion (@fufu1437/dsh-delete-chat)

A DeepSeek Harness (DSH) plugin that permanently deletes one conversation together with every local copy of its data.

Every Session row in the sidebar gains a "Delete conversation…" entry in its … menu. It opens the confirmation at once — no preview request, no artifact inventory, no byte totals — and confirming runs the deletion in the background: the dialog closes immediately and the sidebar entry disappears when the Host broadcasts the removal.

中文文档:README.zh.md

What gets deleted

A conversation leaves more on disk than most people expect. This plugin clears each of these:

| # | Artifact | Location | Notes | |---|---|---|---| | 1 | Session log directory | ~/.dsh/sessions/<project-key>/<session-id>/ | Every on-disk format generation (session.v3.jsonl.zstd, session.v4.jsonl.zstd, …) plus the write lease session.lock | | 2 | Projection cache record | ~/.dsh/storages/session_projcache/sessions/<id>.json | The cached title and first-prompt text, including .json.bak.* copies | | 3 | Legacy feedback sidecar | ~/.dsh/storages/message_feedback.json | A pre-release format the current service no longer reads; it can hold user-written notes | | 4 | Workspace index | ~/.dsh/storages/workspace.json | Account order, archive set, and pin set, written through the workspace registry's own API | | 5 | Spilled tool output | <spill root>/session-<first 12 hex of sha256(id)>/ | Every local spill root, including $TMPDIR/dsh-spill-* | | 6 | Attachments | ~/.dsh/attachments/v1/{objects,files,file-objects,request-images} | Removed only after proving no surviving session references them | | 7 | Subagent sessions | As 1–5 | Every durable subagent session the conversation spawned, recursively | | 8 | DeepSeek upload cache | ~/.dsh/llm-deepseek/files-v3.json | Records of deleted attachments plus the request-image derivatives their variantIds name |

Afterwards the plugin broadcasts api-session/removed to every connected client, so the sidebar row disappears immediately instead of waiting for the next list refresh.

Safety model

Deletion is irreversible, so the implementation follows two hard rules.

1. A conversation that is generating is left alone; one that is merely open is overwritten before it is unlinked. The Host keeps an Agent and an open log write handle for every conversation opened in this process, and DSH exposes no public "release this session" API, so a plain rm under a live writer would leave the bytes readable in the orphaned inode — a fake deletion. Therefore:

  • the conversation is generating a response (a turn is in flight) → refused with 409 session-running; stop it first;
  • it is open in this process but idle → refused once with 409 session-live (carrying forceable: true), and the UI asks for a second confirmation; on confirmation the files are overwritten with zeros and only then unlinked, so the bytes are really gone even though the writer still holds a descriptor;
  • one of its subagent sessions is still live → 409 descendant-live, same forced confirmation.

The cost: such a conversation leaves the session list only after the Harness restarts, and if it is still open in a tab and you keep sending messages it may create a fresh (empty) log. The second confirmation says exactly that.

2. Nothing is deleted unless its absence is proven safe. Attachment objects are content-addressed and may be shared between sessions, so an id is dropped only after every surviving session's log has been scanned and none of them mentions it. The scan streams and decompresses without materializing events and is bounded by a session ceiling and a decompressed-byte budget; when either is exceeded, or a log cannot be read, the bytes are kept and a warning is reported.

Other engineering constraints:

  • Session ids are escaped with an encodeSegment byte-for-byte identical to the JSONL backend's before touching a path, so ../, absolute paths, and NUL cannot escape the roots;
  • The package imports only node: builtins and no private @deepseek-ai/* package, so it publishes as an ordinary npm package;
  • Both HTTP routes pass the composition's connection.requestRejection trust fence first (Host/Origin checks plus the browser session cookie); unauthenticated requests get 401/403.

Install

DSH 0.2.x, whose profiles are pnpm-managed. engines.dsh declares >=0.2.1-rc.2 <0.3.0, which the 0.2.1 plugin manager checks before it installs or activates the bundle.

From npm, once published:

install_bundle   target: @fufu1437/dsh-delete-chat

From a local checkout:

install_bundle   target: link:/absolute/path/to/dsh-delete-chat

One install_bundle call adds the profile dependency, selects the bundle layer, and reloads the live profile; the browser half arrives with the next page load. From a shell, the two steps the manager performs:

dsh plugin --profile web add @fufu1437/dsh-delete-chat
# then select the bundle: Settings → Plugins, or append the package name to
# dsh.profile.bundles in ~/.dsh/profiles/web/package.json

A by-hand profile edit needs a Harness restart; a Plugin Manager operation applies live.

After a DSH update

An upgrade can rebuild the profile and drop third-party bundles: the dependency and its dsh.profile.bundles entry disappear even though cordis.patch.yml survives, so the menu row silently stops appearing. Re-run the install step above; nothing on disk was deleted in the meantime.

Usage

  1. Hover a conversation row in the left sidebar and open its … menu;
  2. choose "Delete conversation…";
  3. the dialog shows the title, the irreversibility warning, and Cancel / Delete permanently — nothing is measured or listed first;
  4. press "Delete permanently": the dialog closes, the deletion continues in the background, and the row disappears when the Host broadcasts the removal;
  5. if the conversation is still open in this Harness process, a second confirmation appears ("Delete anyway") explaining that the files are overwritten in place and that it leaves the list only after a restart;
  6. any other failure (a response currently generating) raises a dismissible alert card in the corner with the reason.

Configuration

Override in the profile's cordis.patch.yml:

- id: fufu-delete-chat
  name: '@fufu1437/dsh-delete-chat'
  config:
    dshHome: /home/me/.dsh
    deleteAttachments: true
    deleteDescendants: true
    scanLimit: 500
    scanByteLimit: 2147483648

| Field | Default | Meaning | |---|---|---| | dshHome | $DSH_HOME → ~/.dsh | Harness home; every other root derives from it | | sessionsRoot | <dshHome>/sessions | Session log root | | storagesRoot | <dshHome>/storages | storage-json root | | attachmentsRoot | <dshHome>/attachments/v1 | Attachment root | | llmFilesRoot | <dshHome>/llm-deepseek | DeepSeek upload-cache root | | spillRoots | discovered $TMPDIR/dsh-spill-* | Spill roots to sweep | | deleteAttachments | true | Run the attachment proof and erase unreferenced uploads | | deleteDescendants | true | Also delete durable subagent sessions | | scanLimit | 500 | Maximum session logs the attachment proof reads | | scanByteLimit | 2 GiB | Maximum decompressed bytes the proof reads |

Verification

pnpm test          # fixture self-test: 68 assertions, touches no real data
pnpm run check     # syntax-check both halves

scripts/selftest.mjs builds a complete throwaway harness home (session logs, projection cache, workspace index, legacy feedback sidecar, spill files, attachments, upload cache), drives the plan/execute pair with a scripted Host context, and asserts what survived:

  • every artifact class is erased while another session's data stays intact;
  • live, running, and live-descendant conversations are refused;
  • an attachment referenced by another session is kept; an exclusively referenced one is erased;
  • a truncated scan or an undecompressable log keeps the bytes and warns;
  • a hostile session id cannot escape the roots;
  • the in-place overwrite really happens: the test keeps an open descriptor on the log (standing in for the live writer) and reads zeros through it after the deletion, proving the content was erased rather than merely unlinked.

.tmp/e2e.mjs is an end-to-end script against the running Host (never published): it mints the same browser-session cookie the page uses, creates a synthetic session in the real DSH home (log + projection cache + spill + attachment), calls delete through the real trust fence, asserts the artifacts are gone, and verifies that a live conversation is refused with 409 and that the removed /inspect route is gone. It needs write access and cleans up after itself.

.tmp/proof-scan.mjs runs the full attachment proof read-only over the real corpus (187 session logs on this machine complete in about 1.5 s).

Known limitations

  • A conversation that is generating a response cannot be deleted until it stops; that is the one hard refusal, and it is deliberate.
  • A conversation that was merely opened can be deleted, but its live session object survives until the Harness restarts (DSH has no public session-release API): the bytes are overwritten and unlinked, yet the session can still appear in the list or create a fresh empty log if you keep sending messages in its open tab.
  • Telemetry already exported cannot be recalled: session-telemetry-otel may already have shipped a log prefix to a remote collector in FEEDBACK_ONLY mode; this plugin can only erase local data.
  • Derived-cache boundary: removing a request-image cache entry requires the matching record in files-v3.json; other derived copies that leave no attachment reference in the log are out of scope.
  • The legacy message_feedback.json sidecar is not read by the current service; this plugin reads and rewrites it directly.
  • workspace.json is edited through the workspace registry API when that service exists, and directly only when it does not (so nothing can overwrite the change from memory).
  • The default local spill root is a temp directory; if a Harness once used a different root that has since been cleaned up, there is nothing left to delete.

Publishing

pnpm run check && pnpm test
pnpm publish --access public     # scoped packages need public access

prepublishOnly runs the syntax check and the self-test first.

License

MIT