🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话永久删除 spidu-lee/dsh-session-delete

DeepSeek Harness
spidu-lee33 天前有提交已提供中文文档扫描:未发现敏感能力

从侧边栏彻底删除会话及其磁盘日志与缓存

28/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/21 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add spidu-lee/dsh-session-delete
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么[目录]—
dsh 原生插件 · other
暂无时间戳·判据 →
装得上吗[实装验证]—
本站尚未做安装检查
暂无时间戳·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/03·判据 →
还在维护吗[仓库]—
更新放缓:最近一次提交在 33 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安全扫描自动 · 静态分析源码
v2.7.0
🟢 扫描:未发现敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

发起外部网络请求
查看扫描证据(共 1 处,展示前 1 条)
  • 发起网络请求
    lib/client.js:209
    const response = await fetch(url, {
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

README

由 DeepSeek 最新模型翻译生成
DSH 会话删除 永久删除会话——DeepSeek Harness 唯一做不到的事。 English · 中文 在侧边栏会话的 「⋯」菜单里加一行 永久删除会话。确认对话框之后,会话被彻底抹掉:磁盘上的日志目录、投影缓存、以及它到每个工作区的链接——所有打开的窗口立刻丢掉这一行。 为什么需要这个插件 DSH 只提供 archiveSession,别的没有。两种行为会留下永远无法从界面移除的会话: - 删除工作区只是把它的会话取消分组。 确认框原话如此:"This removes “…” from the workspace list. The folder and session logs will be kept. Its sessions will appear under Ungrouped." 于是会话落进 Ungrouped,之后再删文件夹也无济于事——日志仍然躺在 $DSH_HOME/sessions 下。 - 持久化接缝没有删除 API。 它自己的文档写着:"Nothing deletes session files — logs accumulate under root until removed externally; the seam has no deletion API." 这个插件补上这个缺口。
展开完整文档(README 剩余 1 节)
DSH 会话删除

永久删除会话——DeepSeek Harness 唯一做不到的事。

English · 中文

在侧边栏会话的 「⋯」菜单里加一行 永久删除会话。确认对话框之后,会话被彻底抹掉:磁盘上的日志目录、投影缓存、以及它到每个工作区的链接——所有打开的窗口立刻丢掉这一行。

为什么需要这个插件

DSH 只提供 archiveSession,别的没有。两种行为会留下永远无法从界面移除的会话:

- 删除工作区只是把它的会话取消分组。 确认框原话如此:"This removes “…” from the workspace list. The folder and session logs will be kept. Its sessions will appear under Ungrouped." 于是会话落进 Ungrouped,之后再删文件夹也无济于事——日志仍然躺在 $DSH_HOME/sessions 下。
- 持久化接缝没有删除 API。 它自己的文档写着:"Nothing deletes session files — logs accumulate under root until removed externally; the seam has no deletion API."

这个插件补上这个缺口。它与其他会话菜单插件的区别在于:不止步于界面动作,而是删掉磁盘上的会话记录,并清理所有指向它的东西。

它做什么

| 步骤 | 动作 |
| 1 | 校验 sessionId(单个路径段,不含 ..) |
| 2 | 扫描 <sessionsRoot>/<projectKey>/<sessionId>(project key 是 cwd 的有损编码,所以只扫描,绝不反推) |
| 3 | 没有可删的东西时返回 404 |
| 4 | 向 workspace/session-activity 瀑布询问还有什么在跑——archive 问的是同一个问题——若有,派发 workspace/session-stop(archive 为 stopActivity 用的扇出),并反复轮询直到稳定 |
| 5 | 仅当那项工作拒绝稳定时返回 409;消息里点名仍在运行的东西。瀑布逐条报告,这是唯一诚实的忙碌信号:窗口打开的任何会话都存在活的 Agent,无论是否在工作 |
| 6 | 对仅仅加载在内存里的会话派发 workspace/session-stop,免得它在删除之后把头部刷回磁盘 |
| 7 | fs.rm(dir, { recursive: true, force: true })——真正的删除 |
| 8 | 再次 stat 目录,最多 4 轮、间隔 350 ms;若有写入者把它放回来,返回 delete-resurrected 而不是成功 |
| 9 | 通过 registry 实体 API 把会话从每个工作区解绑并取消固定——只在文件消失之后做,这样失败不会留下孤立的工作区链接 |
| 10 | 删除 storages/session_projcache/sessions/<id>.json(尽力而为,失败记为警告) |
| 11 | 询问会话是否仍在列表里——活在宿主 store 中,或被持久化层当作已创建但未落地——若是则给出警告,而不是宣称干净成功 |
| 12 | 发出 api-session/removed,让每个已打开的客户端都移除该行 |

客户端随后在本地列表里应用这次移除,这和刷新不是一回事:见下文。

为什么需要步骤 4–6 和 8。 仍驻留在宿主里的会话,可能在移除后零点几秒内把头部刷回磁盘——已删除的对话以前就是这样在重启后重新出现在侧边栏的。浏览器端现在先离开会话(见下文),再由宿主确认没有东西回来。

为什么需要步骤 11。 删掉文件并不会让宿主忘记会话。侧边栏渲染的列表是优先实时的:它把内存存储和持久化语料合并,持久化层还会列出本进程创建过、但从未落盘的会话。该行会在发起删除的窗口里移除(见下文),但这两个持有者还在,而且它们的风险并不相同:

- 宿主存储里的驻留 Session 能把头部刷回磁盘,所以那份日志可能重新出现,重启后对话可能再次被列出;
- 持久化层里的待定条目只活在本进程内,进程结束就消失。

插件清不掉其中任何一个——sessions 上没有移除 API,tracker 的 pending map 是内部的(只有 hasPendingSession(id) 是公开的)——所以删除操作会报告它找到了哪一种,而不是声称操作完全干净。

为什么存活判断走瀑布式查询,而不是 agents.get。 早先的版本只要 Agent 注册表持有该会话就拒绝删除。这不是忙碌信号:DSH 会为任何窗口打开的会话保活一个 Agent,于是一个完全空闲的对话也会被以「它还在运行」为由拒绝。workspace/session-activity 才是 archiveSession 自己查询的东西,它的服务商才是真正知情的一方——Agent 注册表报告的是正在运行的轮次,同时还有归属任务、子代理后代和活跃调度。

安装

从市场安装(上架后):

dsh plugin --profile web add github:spidu-lee/dsh-session-delete

或者手动加进配置档补丁:

cordis.patch.yml
- insert:
- id: session-delete
name: 'dsh-session-delete'

包同时声明了 dsh.bundle.patch 和 dsh.client(platform: web),所以按普通 bundle 安装即可。安装后重启 DSH:宿主端在启动时挂载它的路由族。

"private": true 是有意为之。npm 上已有一个无关的包占用了 dsh-session-delete 这个名字,所以这个插件只从 GitHub 分发。

菜单行与对话框

| 插槽 | sidebar.workspaces.session.menu.item |
| 行 id | dsh-session-delete.session.delete |
| 顺序 | 500(排在自带的 pin 100 / rename 200 / fork 300 / archive 400 之后) |
| 样式 | ui-primitives 的 MenuItemButton,带 danger + separatorBefore,图标 IconTrashOutlineRegular |

确认框不是 window.confirm —— 那是 Electron 原生对话框,它的外框就是窗口标题,既不能换主题也不能本地化。这个插件复用 ui-primitives 的 Modal,与已发布的归档确认框用的是同一个,所以遮罩、卡片、圆角、阴影和焦点陷阱都来自宿主自身。

| 插槽 | shell.overlay |
| 入口 id | dsh-session-delete.session-delete-confirm |

- 行和对话框通过模块级的待处理请求存储(useSyncExternalStore)解耦:行只提交请求,shell.overlay 针对当前请求只渲染一个对话框,所以进行中的状态和错误状态不会泄漏。
- 对话框通过 ctx.get("sessions").retainInfo(id) 读取会话的本地保留状态,并在一开始就说明当前属于哪种情况:dialog.noteOpen(「已在此窗口打开」)、dialog.noteHeld(「在此处持有」)或 dialog.note。
- 删除你正在查看的会话是受支持的,不会被拒绝。 如果你点击的行正是 Main 视图里的那个会话,对话框会先导航到另一个会话(uiWorkspace.openSession;工作区没有其他行时用 startSession()),等 mainView 的引用计数降下来,让释放操作到达宿主,然后才删除。如果 5 s 内没有完成释放,删除会被取消,并显示 dialog.stillOpen —— 明确拒绝好过会话复活。
- 危险按钮有主题:文字和边框用 --dsw-alias-state-error-primary,悬停背景用 --dsw-alias-interactive-bg-hover-danger。样式表只注入一次(),插件卸载时移除。
- 失败时对话框保持打开,显示宿主自己的错误文本,已知的拒绝原因会映射到各自的提示(工作无法结束时用 dialog.blockedRunning,日志又回来时用 dialog.blockedResurrected)。拒绝时附带的警告也会列出来 —— 下一步该怎么做就写在那里。
- 删除成功后,通过调用 sessions.handleSessionRemoved(id) 移除该行 —— 与 api-session/removed 中继用的是同一个入口 —— 而不是刷新基线。两者并不等价:刷新会拿宿主的返回做合并,规则是「基线里没有的身份就移除」,于是宿主仍然列出的会话会被重新插回去,行就卡在 Ungrouped 下面。刷新只是客户端没有暴露移除入口时的兜底。
- 如果宿主报告非致命的 warnings(投影缓存未命中、某个工作区无法取消固定),对话框会切换到只读的完成状态并列出这些警告,而不是在虚假的成功上关闭。

本地化

- UI 字符串在命名空间 dsh-session-delete 里用 ctx.locale.register(ns, locale, dict) 注册;槽位注册时带上 locale: NS,就能收到注入的 t,切换界面语言立即生效。键名:menu.deleteSession、dialog.title、dialog.desc、dialog.descUntitled、dialog.note、dialog.noteOpen、dialog.noteHeld、dialog.sessionId、dialog.confirm、dialog.switching、dialog.pending、dialog.cancel、dialog.close、dialog.failed、dialog.stillOpen、dialog.noTarget、dialog.blockedRunning、dialog.blockedResurrected、dialog.notFound、dialog.badRequest、dialog.forbidden、dialog.deleteFailed、dialog.warnings,另外每个警告码对应一个 warning. 键(zh + en)。
- 宿主从不发送成句文本。 它不知道发起请求的窗口用的是哪种语言,所以警告以 { code, params, message } 的形式过线,错误以 { code, message, params } 的形式过线。对话框通过注入的 t 渲染 warning. / dialog.,并插值 params;message 是宿主的英文句子,只在浏览器端不认识该码时兜底(旧客户端连新宿主,不能渲染出空行)。目前的码有 corpus-held-live、corpus-held-pending、activity-stop-failed、activity-unavailable、workspace-registry-missing、workspace-list-failed、workspace-detach-failed、unpin-failed、projection-cache-failed、directory-rewritten 和 emit-failed。
- 包元数据(插件卡片上显示的名称和描述)遵循 DSH 官方约定:package.json 旁边放 locale/en.json 和 locale/zh.json,每个文件形如 {"meta": {"title": …, "description": …}},并导出 "./locale/.json"。宿主的 readPluginMeta() 会自己识别语言。

安全

- 这组路由位于共享的回环信任围栏之后:对端 socket 地址必须是 127/8 或 ::1,Host 必须是回环地址,sec-fetch-site: cross-site 直接拒绝,带 Origin 时必须与 Host 匹配。永不信任 X-Forwarded-For。
- 只删除 <sessionsRoot>/<projectKey>/<sessionId>,删除前用 path.relative 再次确认目标在根目录内。
- 请求体上限 64 KiB,只接受 POST。
- 删除不可逆,所以必须确认。故意不设回收站。
- POST api/dsh-session-delete/info 报告当前生效的会话根目录、每个服务是否真的解析成功(services.sessions / .workspaceRegistry / .sessionPersistence)、workspace/session-activity 瀑布流能否派发(activityWaterfall),以及当前加载的 id。任何一处为 false,都说明某个存活检查已悄悄退化成「什么都没在跑」,而这正是那个 bug 让已删除的会话被写回——所以删除被拒绝时,先检查它。
- 在同一个请求里加上 ?sessionId=<id>,它还会回答那一个会话的存活问题——{"asked":true,"summary":"turn×1","entries":[…]}——并给出一个 corpus 块,说明现在哪个来源仍会列出该会话(live、pending、listed、persisted)。「我的删除为什么被拒绝,或者这一行为什么还在?」它直接给出答案。

离线自测

test/dry-run.mjs 用临时目录和伪造的 cordis 上下文驱动两个路由处理器,覆盖非法 id、未知 id、非回环 Host、被要求时停止的工作、拒绝结束的工作、仅加载的会话、空闲会话、延迟的校验轮次、并发的第二次删除、仍驻留在宿主存储中的会话、持久化层仍视为待处理的会话,以及 service/waterfall/corpus 探测:

$env:ELECTRON_RUN_AS_NODE='1'
& 'D:\Program Files\DeepSeek Harness\DeepSeek Harness.exe' 'D:\DSHWorkSpace\dsh-session-delete\test\dry-run.mjs'

伪造上下文无法证明真实宿主能解析 ctx.sessions 或派发 workspace/session-activity。务必在重启 DSH 后用 POST …/info 跟进,读取它的 services 和 activityWaterfall 字段;原来那个静默守卫的 bug 通过了所有离线检查。

兼容性与已知限制

- 基于 DSH 0.2.0-rc.2(桌面运行时)构建。宿主侧只导入 node:os、node:path 和 node:fs;浏览器侧通过 harness 模块加载器需要 react、react/jsx-runtime 和 @deepseek-ai/dsh-client-ui-primitives。
- 宿主侧声明 inject = ["webServer", "sessions"]。声明会话存储是有意为之:未解析的服务不能看起来像「没有加载会话」,因为那会静默禁用移除校验策略。agents 有意不声明——见上文「为什么存活问题是 waterfall」。
- 在另一个窗口打开的会话会在那里保持活跃的 generation。宿主会停止它的活动并重新检查目录,但之后重新打开该会话的窗口仍可能改写头部;插件会将其报告为 delete-resurrected,而不是声称成功。
- 删除日志目录就是删除;没有回收站,也没有撤销。

许可证

MIT

在左侧会话行的 「⋯」菜单里加一行 「彻底删除会话」。确认之后它会把会话真正抹掉:磁盘上的日志目录、投影缓存、以及它在每个工作区里的关联,并且所有已打开的窗口立刻移除该行。

为什么需要它

DSH 内置只有 archiveSession,没有删除。有两种行为会留下永远删不掉的会话:

- 删除工作区只是把会话「解组」。 确认框原文写着:「这会把『…』从工作区列表里移除。
文件夹和会话日志会保留。它的会话将出现在未分组下。」 于是会话掉进「未分组」,之后再去删文件夹也没用——日志仍然躺在 $DSH_HOME/sessions 里。
- 持久化层没有删除 API。 它自己的文档写得很直白:「没有任何东西会删除会话文件——日志在 root 下不断堆积,直到被外部删除;这个接缝没有删除 API。」

本插件补上这个缺口。这也是它和其他会话菜单插件的区别:它不停留在界面动作,而是真正删掉磁盘上的会话记录,并清理所有指向它的东西。

它做了什么

| 步骤 | 动作 |
| 1 | 校验 sessionId(单段路径名,禁止 ..) |
| 2 | 扫描 <sessionsRoot>/<projectKey>/<sessionId>(projectKey 是 cwd 的有损编码,只能扫,不能反推) |
| 3 | 没找到 → 404 |
| 4 | 用 workspace/session-activity waterfall 询问该会话还有什么在跑——与内置归档 archiveSession 问的是同一个问题;若有,就派发 workspace/session-stop(归档 stopActivity 用的同一条扇出)并轮询直到它真的停下来 |
| 5 | 只有在那份工作停不下来时才 409,且消息里点名还在跑的是什么。这个 waterfall 逐项报告,是唯一诚实的「忙」信号:Agent 注册表对任何被窗口打开过的会话都有一条记录,不管它在不在干活 |
| 6 | 会话只是加载在内存里 → 也派发 workspace/session-stop,让它没法在删除之后把 header 再写回磁盘 |
| 7 | fs.rm(dir, { recursive: true, force: true }) —— 真正的删除 |
| 8 | 再次 stat 该目录,最多 4 轮、每轮间隔 350 ms;若被重新写回,返回 delete-resurrected 而不是假装成功 |
| 9 | 通过 registry 实体 API 把会话从所有工作区解绑,并取消置顶——只在文件确实删掉之后才做,失败时不会留下「工作区里没了、磁盘上还在」的孤儿状态 |
| 10 | 删除 storages/session_projcache/sessions/<id>.json(尽力而为,失败记入告警) |
| 11 | 追问该会话是否仍被列为存在——在宿主内存里活着,或被持久化层记作「已创建但尚未落盘」——并把这些如实写成告警,而不是谎报干净成功 |
| 12 | 广播 api-session/removed,所有已打开的客户端据此移除该行 |

随后客户端会把这个移除直接应用到自己的列表上——这和「刷新基线」不是一回事,见下文。

为什么需要第 11 步。 删掉文件并不会让宿主忘记这个会话。侧边栏渲染的列表是内存优先的:它把内存中的会话存储与磁盘语料合并,而持久化层还会额外列出「本进程已创建但尚未落盘」的会话。
行本身会在发起删除的那个窗口里被移除(见下文),但这两个来源会留下,而且它们的风险并不相同:

- 宿主存储里活着的 Session 能把 header 再次落盘,所以那份日志可能回来,重启后这个会话可能又被列出来;
- 持久化层里的 pending 记录只活在本次进程内,随进程结束一起消失。

插件清不掉任何一个——sessions 上没有任何移除 API,tracker 的 pending 表也是内部的(公开的只有 hasPendingSession(id))——所以删除会如实报告碰到的是哪一种,而不是声称完全干净。

为什么需要第 4–6、8 步。 仍然驻留在宿主内存里的会话,会在目录被删掉之后的一瞬间把 header 重新 flush 回磁盘——这正是「删掉的对话重启后又冒出来」的成因。现在浏览器半边会先离开该会话(见下文),宿主再复查确认没有被写回。

为什么「在不在运行」要问 waterfall,而不是 agents.get。 早先的版本只要 Agent 注册表里有这个会话就拒绝删除。那不是「忙」的信号:DSH 对任何被窗口打开过的会话都会保留一个 Agent,于是一个完全空闲的会话会被回一句「该会话仍在运行」。workspace/session-activity 才是 archiveSession 自己会问的那个问题,它的提供方才真正知道答案——Agent 注册表报的是 正在跑的回合,另外还有所属任务、subagent 子孙和活跃提醒。

安装

上架之后可以在市场里直接装:

dsh plugin --profile web add github:spidu-lee/dsh-session-delete

或者手动写进 profile patch:

cordis.patch.yml
- insert:
- id: session-delete
name: 'dsh-session-delete'

包同时声明了 dsh.bundle.patch 与 dsh.client(platform: web),可以作为一个普通 bundle 安装。
装完请重启 DSH:宿主半边在启动时挂载路由。

"private": true 是刻意的。npm 上的 dsh-session-delete 已经被一个不相关的包占用,所以本插件只从 GitHub 分发。

菜单行与确认框

| 座位(slot) | sidebar.workspaces.session.menu.item |
| 行 id | dsh-session-delete.session.delete |
| order | 500(内置 pin 100 / rename 200 / fork 300 / archive 400 之后) |
| 样式 | ui-primitives MenuItemButton 的 danger + separatorBefore,图标 IconTrashOutlineRegular |

确认框不是 window.confirm——那是 Electron 原生对话框,标题栏就是窗口标题,既无法改样式也无法本地化。本插件复用内置归档确认框所用的 ui-primitives Modal:遮罩、卡片、圆角、层级阴影、焦点陷阱都由组件自带,外观与 DSH 一致。

| 座位(slot) | shell.overlay |
| 条目 id | dsh-session-delete.session-delete-confirm |

- 菜单行与对话框通过模块内的 pending-request store(useSyncExternalStore)解耦:菜单行只登记请求,shell.overlay 只渲染当前这一个确认框,取消/关闭即清空,因此在途与错误状态不会残留。
- 确认框会通过 ctx.get("sessions").retainInfo(id) 读取该会话在本窗口的保留计数,并据此在开头就说清是哪种情况:dialog.noteOpen(正打开在当前窗口)、 dialog.noteHeld(被本窗口持有)、或 dialog.note。
- 删「正在看的这一条」是被处理的,不是被拒绝的。 如果点的那一行正是主视图里的会话,确认框会先切到另一个会话(uiWorkspace.openSession;工作区里没有别的会话时用
startSession()),等 mainView 引用计数归零、并给释放留出到宿主的时间,然后才真正删除。
若 5 秒内释放不掉,删除会被取消并提示 dialog.stillOpen——宁可明确拒绝,也不要制造幽灵会话。
- 危险按钮用主题变量上色:文字与描边 --dsw-alias-state-error-primary,hover 底色 --dsw-alias-interactive-bg-hover-danger。样式表只注入一次(),随插件卸载一并移除。
- 删除失败时对话框不关闭,把宿主返回的错误原文显示在框内;已知的几种拒绝各有专属文案(dialog.blockedRunning = 有活儿停不下来,dialog.blockedResurrected = 日志被写回)。
随同拒绝一起返回的告警也会列出来——「接下来该怎么做」通常就写在那里。
- 删除成功后,那一行是通过调用 sessions.handleSessionRemoved(id) 移除的——就是 api-session/removed 中继自己用的那个入口——而不是刷新基线。两者不等价:刷新会拿宿主的回答做合并,规则是「基线里没有的 id 才删」,于是宿主仍在列出的会话会被重新插回来,那一行就卡在「未分组」里。只有客户端不提供该移除入口时才回退到刷新。
- 宿主返回非致命 warnings(投影缓存没删掉、某个工作区解绑失败等)时,对话框进入只读的 完成态把它们列出来,而不是假成功直接关掉。

本地化

- 界面文案由 ctx.locale.register(ns, locale, dict) 提供,命名空间 dsh-session-delete;槽注册时带 locale: NS 以取得注入的 t,切换界面语言即时生效。键:menu.deleteSession、 dialog.title、dialog.desc、dialog.descUntitled、dialog.note、dialog.noteOpen、 dialog.noteHeld、dialog.sessionId、dialog.confirm、dialog.switching、dialog.pending、 dialog.cancel、dialog.close、dialog.failed、dialog.stillOpen、dialog.noTarget、 dialog.blockedRunning、dialog.blockedResurrected、dialog.notFound、dialog.badRequest、 dialog.forbidden、dialog.deleteFailed、dialog.warnings,外加每个告警码一个 warning. 键(中文 + 英文)。
- 宿主从不发送散文。 它根本不知道发起请求的窗口是什么语言,所以告警以 { code, params, message } 过线,错误以 { code, message, params } 过线。对话框用注入的 t 渲染 warning. / dialog. 并把 params 插进去;message 是宿主那句英文,只在浏览器半边不认识这个码时兜底(旧客户端遇上新宿主不能渲染出空白行)。
目前的告警码:corpus-held-live、corpus-held-pending、activity-stop-failed、 activity-unavailable、workspace-registry-missing、workspace-list-failed、 workspace-detach-failed、unpin-failed、projection-cache-failed、directory-rewritten、 emit-failed。
- 包元信息(插件卡片上的名称与介绍)遵循 DSH 官方约定:package.json 旁边放 locale/en.json 与 locale/zh.json,结构为 {"meta": {"title": …, "description": …}},并在 exports 里暴露 "./locale/.json"。宿主的 readPluginMeta() 会自行取用。

安全

- 路由走共享的 loopback 信任栅栏:对端 socket 地址必须是 127/8 或 ::1,Host 必须是
loopback 权威,sec-fetch-site: cross-site 直接拒绝,带 Origin 时其 host 必须与 Host 相同。X-Forwarded-For 永不采信。
- 只删 <sessionsRoot>/<projectKey>/<sessionId>,且删除前再次用 path.relative 断言目标在根目录之内。
- 请求体上限 64 KiB;只接受 POST。
- 删除不可恢复,所以确认框是强制的,且刻意不做「回收站」。
- POST api/dsh-session-delete/info 返回当前会话根目录、每个服务是否真的解析成功 (services.sessions / .workspaceRegistry / .sessionPersistence)、 workspace/session-activity waterfall 能不能派发(activityWaterfall),以及当前加载在内存里的 id。任何一项为 false 就是「活性检查已静默退化成永远认为没有东西在跑」的特征——也就是当初让被删会话被写回磁盘的那个 bug,所以删除被拒绝时先看这一项。
- 同一个请求加上 ?sessionId=<id>,它会顺带回答那一个会话的活性问题
({"asked":true,"summary":"turn×1","entries":[…]}),并给出一个 corpus 块,说明现在还有哪个来源会列出这个会话(live / pending / listed / persisted)。「我的删除为什么被拒」和「行为什么还在」都能直接由它回答。

离线自测

test/dry-run.mjs 用临时目录 + 伪造的 cordis 上下文直接驱动两个路由处理器,覆盖这些情形:非法 id、未知 id、非 loopback Host、能停下来的运行中工作、停不下来的工作、仅仅加载在内存里的会话、空闲会话、仍驻留在宿主存储里的会话、被持久化层记作 pending 的会话、延迟复查、并发二次删除,以及服务/waterfall/corpus 探针:

$env:ELECTRON_RUN_AS_NODE='1'
& 'D:\Program Files\DeepSeek Harness\DeepSeek Harness.exe' 'D:\DSHWorkSpace\dsh-session-delete\test\dry-run.mjs'

伪造的上下文无法证明真实宿主能解析 ctx.sessions,也无法证明它能派发 workspace/session-activity。务必在重启过的 DSH 上用 POST …/info 复查它的 services 与 activityWaterfall 字段——当初那个「守卫静默失效」的 bug 通过了全部离线检查。

兼容性与已知限制

- 针对 DSH 0.2.0-rc.2(桌面运行时)开发。宿主半边只 import node:os、node:path、node:fs;浏览器半边通过 harness 模块加载器 require react、react/jsx-runtime、@deepseek-ai/dsh-client-ui-primitives。
- 宿主半边声明 inject = ["webServer", "sessions"]。声明会话存储是刻意的:服务没解析时,绝不能看起来像「没有会话被加载」,那正是会让复查策略静默失效的情形。agents 则刻意不声明——原因见上文「为什么『在不在运行』要问 waterfall」。
- 会话若在其它窗口打开,那边会保留一个活着的 generation。宿主会停掉它的活动并复查目录,但如果那个窗口之后又把它打开,仍然可能把 header 写回去;此时插件报 delete-resurrected,而不是声称成功。
- 删掉日志目录就是删除本身:没有回收站,没有撤销。

许可证

MIT
预览分享卡片长什么样 ↓spidu-lee/dsh-session-delete
分享 · DeepSeek Harness Hub
spidu-lee/dsh-session-delete

从侧边栏彻底删除会话及其磁盘日志与缓存

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部