DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Session Delete — DSH Plugin for DeepSeek Harness
← Plugins

@szhl/dsh-session-delete

Session Delete

DeepSeek Harness plugin: delete a conversation for good — the log, the projection cache, the archive marker, the workspace registration and the live Session object, subagent sessions included. DSH ships archive only.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add @szhl/dsh-session-delete@1.0.1
READMECompatibilityVersions

Compatibility and provenance

Session Delete is published as @szhl/dsh-session-delete and currently resolves to version 1.0.1. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
10/16/2026

Versions

1.0.1stable
9/30/2026
1.0.0stable
9/30/2026

Related plugins

Loading related plugins…

Latest
1.0.1
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
91.9 kB
Files
9
Surface
web
License
MIT
Source
npm
Weekly downloads
59
View source ↗Project homepage ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in memory-context.

Memory Plugin@openviking/dsh-memory-pluginOpenViking memory and context bundle for DeepSeek HarnessContextdsh-contextA DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.Weknora@wxg-prc-cpg/dsh-weknoraWeKnora knowledge retrieval tools for DeepSeek Harness (dsh): semantic search, document reading and RAG/agent answers over your own knowledge bases.Mnemondsh-mnemonComposable, view-based memory for DeepSeek Harness. Pluggable sources and strategies, with layered memory out of the box.

README

@szhl/dsh-session-delete

在侧栏会话的「⋯」菜单里加一项删除会话,二次确认后把这次对话从磁盘上彻底抹掉,不留痕迹:日志、投影缓存、归档标记、置顶、工作区登记、浏览器里那份草稿,全部清掉,并且把它从宿主内存里摘掉,所以侧栏那一行不会再回来。

DSH 本身没有这个功能,而且是刻意没有:

  • @deepseek-ai/dsh-session-persistence 文档:「无删除或保留接口——剪枝已存储会话属于带外后端维护。」
  • @deepseek-ai/dsh-workspace 只有 archiveSession,没有 deleteSession;文档明确写着「会话删除与文件夹移除是彼此独立且尚未提供的功能」。
  • 侧栏文档明确写着:「没有 Session 删除:会话可以归档但绝不会被删除。」

归档只把会话从列表里藏起来,文件还占着磁盘。这个插件补的是真正的删除。它按 DSH 的插件架构实现(dsh.bundle.patch + dsh.client),不改任何 App 文件,也不碰任何生成的 RPC 接口。

安装

dsh plugin --profile desktop add @szhl/dsh-session-delete

--profile 换成你自己的 profile 名(桌面版是 desktop);dsh plugin 会把后面的参数转交给 profile 里的 pnpm。装完重启 DSH 生效。

(本地开发时也可以按文末「安装位置」用 link: 挂进 profile。)

用法

侧栏任意会话行悬停 → 点行尾「⋯」→ 菜单最下方(分隔线之后、红色)的删除会话…。

确认框会先列清楚要删掉什么:占用磁盘、子代理会话数量、工作目录、存储位置;如果该会话还有正在跑的回合/子代理/后台任务,会一并列出,主按钮变成停止并删除。

确认是两段式的,和内置的「停止并归档」一致:第一次提交不带 stopActivity,宿主若发现还有工作在跑就回 409 active 并附上清单,对话框把它列出来、主按钮才变成「停止并删除」,再点一次才真的动手。这样「掐掉正在跑的回合」永远是一次显式确认,而不是默认行为。

  • 当前正在主视图里打开的会话可以删。菜单项不再置灰;确认之后会先把主区域切到一个可见的普通会话,再执行删除 —— 否则主区域会停在一个刚被删掉的目录上。
    • 候选会话是严格筛过的:排除子代理会话、有父会话的会话、已归档的会话。 这一步不能省:session/list 返回的 ids 里含子代理和归档(实测本机 37 条里 29 条是子代理), 而且按 updatedAt 倒序排、子代理没有用户 prompt 所以永远排在最前 —— 随手写 ids.find(id => id !== target) 会挑中目标自己的子代理, 而它就在同一次删除的 descendants 里,等于"切到马上就要被删的那一个"。 归档会话更糟:切过去会被 clearArchivedCurrent() 把主视图清空。
    • 除目标外一个候选都没有时(例如全新 profile 的首次会话),会在同一工作区里新建一个空会话再删; 连工作区都定位不到才会报「切不走」。
  • 删掉的那一行会立即消失,不用刷新页面,刷新了也不会回来(见下)。
  • 运行中的 DSH 宿主进程自己的会话(DSH_SESSION_ID,只有宿主环境里设了才会生效,桌面宿主实测没有设)会被拒绝。
  • 刚创建、还没落盘的会话会被拒绝(409 unpersisted):它只存在于宿主内存里,磁盘上还没有日志目录,没有东西可删。给它发一条消息落盘后再删。
  • 上一次删除留下的残影可以再点一次删(对话框会换成「清理干净」)。那种行的日志已经没了,但归档标记还在 —— 它会把这行、内存里的对象、归档集和工作区登记一起清掉。

删除都做了什么

宿主半边(index.js)按这个顺序执行,顺序是有意的:

  1. workspaceRegistry.archiveSession(id, { stopActivity: true }) —— 先写持久化归档集。它是"被停掉的工作再次唤醒"时的准入闸门,也让内置的停止事件把在跑的工作收掉。这一步写的标记在第 5 步会被摘掉,所以它只是过程中的护栏,不会留在最终状态里。若会话因某种原因不在注册表里,这一步只记 warning 并继续(真正的授权是"持久化里有它的 header")。
  2. 把活着的 Session 从宿主内存里摘掉(detachLive)—— 这是让"删除"名副其实的一步。见下节。
  3. 第一遍文件系统清理:删会话日志目录(不是单个文件):<DSH_HOME>/sessions/--<mangle(cwd)>--/<encode(id)>/,里面有 session.lock 和历史各代 session.vN.jsonl.zstd;同时删投影缓存 <DSH_HOME>/storages/session_projcache/sessions/<sessionId>.json。
  4. 反复扫尾:摘掉内存会触发 session/disposed,而后端要"最后排空并关闭写句柄"、投影缓存要"live→cold 落一次盘",两者都是不可 await 的异步写,都可能把我们刚删掉的文件重新写出来。所以扫描一直做到连续两轮什么都没找到为止(上限 12 轮 × 80ms)。
  5. 摘掉所有持久引用:unarchiveSession(归档标记)、unpinSession(置顶),以及每个 Workspace 实体上的 detachSession(工作区登记)。这些各是一次自己的持久化写入,都走注册表的同一条 mutation 链。
  6. 确认遍:再清一次,然后逐个核对日志目录与缓存是否真的都不在了;留下的会作为 warnings 返回,而不是假装成功。
  7. 为每个真正清干净的 id 触发 api-session/removed(摘内存时内置的 controller 已经发过一次,这一步是兜底)。
  8. 顺手删掉本插件早期版本留下的 ~/.dsh/session-delete-diag.json(那份取证文件里只列了会话 id,早就不写了)。

为什么必须把内存里的对象也摘掉

浏览器这边的侧栏画的是宿主给的列表,而宿主给的列表是"live 优先"的:

async listSessions(signal) {
  const persisted = ...            // 磁盘上的
  for (const session of this._ctx.sessions.list()) records.set(session.id, {...})   // 内存里的,覆盖前者
  ...
}

只要宿主还持有那个 Session 对象,它就一直在 session/list 里 —— 只删文件的话,刷新一次页面那一行就回来了,点开还能从内存里读到整段对话。而"能把它藏起来"的唯一现成手段就是归档标记,那正是"删了却只是归档了"的由来。

所以删除走的是 SessionStore 自己的卸载能力:enter() 给每个条目装了 detach,会话的持有方(fiber)在卸载时调的就是它;插件从 ctx.sessions.store 里取出同一个条目调 entry.detach(),效果与持有方自己卸载一致 —— 而且会发出 session/disposed,由 DSH 自己的消费者完成收尾:后端排空并关闭写句柄、投影缓存落盘、API-session controller 把那一行摘掉。实测(同一个宿主进程里):sessionQuery.listSessions() 摘之前包含它、摘之后不包含;session/disposed 确实派发;重复 detach 是幂等的。

ctx.sessions 没有公开的"按 id 释放"入口,所以这里是能力探测:store 不是 Map、条目没有 detach、或正在派发(announcing/appending)时会如实上报(stillLive: true / deferred),文件照样删干净;对话框在这种降级情况下会改用另一句提示,告诉操作者"页面刷新后这一行可能回来,DSH 重启后彻底消失",而不是假装一切正常。

为什么最后还要发一次 api-session/removed

文件在宿主上没了,并不等于浏览器列表里的那一行会消失:侧栏画的是客户端自己那份会话目录。摘内存会经由内置 controller 转发这个事件,冷会话则没有这条边;两种情况插件都自己补一次。客户端半边在删除成功后也会直接调用同一个入口(handleSessionRemoved)兜底,所以即使事件没送到,本地这一行也会立即消失。

子代理会话一并删除:靠 SessionHeader.parentSession 递归,但要求 origin === 'subagent'。fork 也带 parentSession 却没有 origin —— fork 是独立对话,绝不能被连带删除。

会清掉哪些痕迹

痕迹位置处理
会话日志目录(各代日志 + 锁)<DSH_HOME>/sessions/--<mangle(cwd)>--/<id>/删除
投影缓存<DSH_HOME>/storages/session_projcache/sessions/<id>.json删除,并扫掉卸载时重写的那份
归档标记workspace.json → global.archivedSessionIds摘除
置顶workspace.json → global.pinnedSessionIds摘除
工作区登记workspace.json → tables.workspaces[*].sessionIds摘除
宿主内存里的 Session宿主进程摘除(行不会再回来,对话不再可读)
浏览器里的会话分片localStorage 里键名含该 id 的条目(dsh.conversation.<id> 存着未发送的草稿)删除
本插件早期的取证文件<DSH_HOME>/session-delete-diag.json删除

不删的东西:~/.dsh/attachments/v1/objects/ 是按内容寻址、跨会话共享的,删除会话不会回收其中的对象(也就不会误删其他会话还在用的附件)。子代理会话的日志与缓存会一并删除;它们的附件对象同理保留。

安全边界

  • 要真正删文件,会话 id 必须能在 sessionPersistence.list() 里查到才会动手。这是首要的目录穿越防线。(唯一的例外是"归档集里的残影":它的日志目录必须已经不存在,此时不删任何文件,只清理内存与登记。)
  • 日志目录优先用后端的 locate({ id, cwd }) 求值;该方法未在 seam 文档中公开,因此另有等价回退实现(复刻 projectKey / encodeSegment,不依赖会话格式版本号)。
  • 计算出的目录必须严格位于 <DSH_HOME>/sessions 之内,否则拒绝并记入 warnings(此时该 id 也不会出现在 deleted 里)。
  • 投影缓存文件名用的是原始会话 id,只在 id 满足存储域的 ^[A-Za-z0-9_-]+$ 记录键字符集时才删除。
  • 请求体上限 4 KiB;路由经 ctx.connection.requestRejection 只应答本机操作者自己的浏览器。
  • 删除不会在失败时假装成功:deleted 只列真正清干净的 id,其余进 warnings,客户端用红色横幅如实显示(不再只 console.warn)。

限制

  • 内存里的 agent 对象会留到 DSH 退出。ctx.agents 没有按 id 释放的入口,agent 的生命周期绑在注册表自己的 fiber 上(AgentRegistry.create/resume 用的是 this.ctx),所以删不掉的只有这一个对象;但它已经没有对应的 Session(列表、打开、读取都查不到它),而且它的写句柄已经被 session/disposed 关掉 —— session/event 找不到 writer,不会再写盘。所以它既不占 UI,也不会重新长出文件。
  • 有条加载时的收尾:apply 扫一遍归档集,凡"持久化里已经没有、日志目录也不在"的 id 都按残影处理 —— 摘内存、取消归档、退出工作区登记、补发一次 api-session/removed。这是给本插件早期版本或被打断的删除擦屁股的修补路径;本版本跑完的删除不会留下这种标记。
  • sessionPersistence 没有删除接口,所以这里直接操作文件系统。DSH 未来版本若改变存储布局,日志目录的回退实现需要跟着改(locate() 存在时不受影响);若改变 ctx.sessions 的内部结构,摘内存会降级为"只删磁盘 + 如实告警",删除本身不会失败。

安装位置

已注册进 desktop profile:

  • ~/.dsh/profiles/desktop/package.json —— dependencies 加 link:,dsh.profile.bundles 加入本包
  • 本包的 cordis.patch.yml(经 dsh.bundle.patch 生效)—— 插入本插件行,并把本插件目录(dsh-session-delete)加进 hmr 的 root,与原有的 tencent-docs 两个 root 并列

卸载:从 profile 的 package.json 里删掉这两处,再 pnpm install,重启 DSH。

验证状态

  • index.js 与 client.js 语法、package.json 与两份 locale JSON、以及客户端字典的中英键位/占位符一致性均已校验。
  • 宿主侧行为测试:.tools/test-session-delete.mjs,84/84 通过(合成目录树 + 假 ctx,包含一个带 detach 能力的真 Map 形状 store 和一个会重写投影缓存的 session/disposed 副作用)。覆盖:只扫 subagent 血缘不碰 fork、子代理与孙代理一并删除、无 cwd 会话、仓库外 cwd、投影缓存精确删除、卸载时重写的缓存被扫尾清掉、活动工作拒绝(409/active)、未知会话 404、注册表不认识该 id 也不阻断删除(只记 warning)、内存中未落盘会话 409/unpersisted、宿主自身会话 409、越界 id 不逃逸、locate() 指到仓库外时拒绝删除并如实告警、优先采用 locate() 的路径、归档标记/置顶/工作区登记全都被摘掉、活着的会话被摘出内存且 stillLive: false、ctx.sessions.store 结构不符时降级但仍删文件并如实报告、Workspace 实体没有 detachSession 也不报错、残影的 inspect/delete 返回 200 且清理登记、加载时清掉不可达标记并同时摘掉那一行、仍被内存持有的残影在加载时也被摘掉、正常的已归档会话在加载时不受影响、早期版本的调试转储文件在删除时被一并清掉。
  • 在真实宿主进程里确认过摘内存这条关键路径(临时探针,已移除):ctx.sessions.store 是 Map、条目带 detach;对一个临时建的活会话调用它之后,ctx.sessions.get() 变 undefined、sessionQuery.listSessions() 不再包含它、session/disposed 确实派发、重复调用是幂等的。
  • 两条宿主路由已在运行中的 App 里确认注册并受信任围栏保护:GET /dsh-session-delete.state 不带 cookie → 401、POST /dsh-session-delete.delete 不带 cookie → 401,对照不存在路径 → 404。
  • 宿主半边确实会热重载:实测改 index.js 后约 8–15 秒内在同一个宿主进程里再次 apply(HMR 只换模块,不换进程)。
  • 客户端模块热重载不需要刷新页面:client-hmr 每 500ms stat 轮询一次各 bundle,而本插件的 client.js 是源码直供(exports["./client"] 直接指向它,无构建步骤),所以改文件即时生效。
  • 仍需人工点一次:删一个会话,确认 ①那一行立刻消失 ②刷新页面后不再出现 ③~/.dsh/sessions 与 session_projcache 里都没有它 ④workspace.json 的 archivedSessionIds / sessionIds 里都没有它。

排查用的历史遗留文件

插件早期版本在 ~/.dsh/session-delete-diag.json 写过一次取证快照(只含会话 id 与 header 键名),本版本已不再写,并会在下一次删除时顺手删掉它。同类的历史遗留还有:

  • ~/.dsh/storages/workspace.json.bak —— 早期手工 shell 脚本留下的备份
  • <workspace>/delete-session-357295a6.sh —— 同一批手工脚本

这两个都含旧会话 id,确认不需要回滚后可以自行删除。