Conversation deletion design
August 14, 2026 · View on GitHub
DeepSeek Harness currently treats Session persistence as append-only. The public persistence service can locate and inspect a per-Session JSONL artifact, but it does not expose deletion, and a live Agent's teardown handle belongs only to its creator. This plugin therefore does not remove a file from underneath a live writer.
The /delete-conversation command writes one request keyed by the exact Session lifecycle (id plus createdAt). At the next process start, the plugin runs after Session persistence becomes available and before the Web Workspace registry is allowed to build its header index. It validates the current header, location, and inspected identity, then atomically renames the whole Session directory into stateDir/trash/<lifecycle-key>. A recovery manifest records the original directory.
The browser half owns a narrow DOM adapter because Harness does not yet expose a public Session-menu slot. A capture listener admits only action buttons inside semantic treeitem[aria-selected] Session rows. It resolves the exact Session id from the row's React owner chain, with a unique visible-title match as a fail-closed fallback, then a MutationObserver creates one stable document-level React controller and portals only its action into the newly opened menu. Ambiguous identity produces no destructive action.
The Host's pointer-leave boundary follows its own React tree, so entering the separately rooted portal would otherwise arm its close timer despite remaining physically inside the menu. A capture-phase bridge suppresses only the false Host-to-plugin pointerout; entering the action also dispatches pointerover to the Host trigger to cancel an already armed grace timer when a browser reports a null relatedTarget. When the pointer leaves the plugin action, the bridge relays that transition through the Host anchor so movement outside the whole menu still arms the normal grace. Selecting the action synchronously marks the controller active before opening the shared Web Modal card with the concise confirmation “Delete this conversation?” and explicit cancel/delete actions; it never invokes native window.confirm or window.alert. The adapter then programmatically closes the owner menu. Because the controller is not a descendant of that menu, blur, pointer-leave, or automatic menu teardown cannot unmount the card. Cancellation or completion unmounts the controller. Plugin disposal disconnects the observer and listeners, removes styles, and unmounts every retained controller. During the request, dismissal and actions are disabled; failures remain visible inside the card.
The confirmed action addresses the clicked row's Session id through the plugin's root-scoped conversationDeletion.queue Remote. Unlike the commands Remote, this interface does not require a live Agent, so historical rows can reach the same Host runtime that validates and durably queues Session lifecycles. The slash command remains a separate human adapter over that runtime. Only after the root Remote succeeds does the browser call the standard Workspace archiveSession service. The returned archive set updates the browser projection immediately, hides the row across grouping surfaces, and clears a current selection through the Host's existing rule.
Archive state is intentionally durable and independent from log movement. If the archive call fails after queue admission, the pending deletion remains valid and the UI reports the partial outcome. The next cold start can still move the artifact safely; no optimistic browser-only filter can diverge from other tabs or survive reloads.
The request file remains in stateDir/pending if validation or the move fails. A crash after the directory rename is also recoverable: the deterministic trash target proves the move committed, and the next boot writes any missing manifest before retiring the request.
Recovery also checks the process-local Session store. If the plugin is hot-reloaded while a queued Session remains live, the request is retained rather than treating HMR as a cold start.
This design deliberately provides recoverable product deletion rather than permanent erasure. Permanent trash retention policy belongs to an administrator or a future storage-level deletion API with sidecar cascading.