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

会话停止与删除 BG0EXM/dsh-session-remove

DeepSeek Harness
BG0EXM3 天前有提交已提供中文文档扫描:含敏感能力

给 DeepSeek Harness 侧栏的会话菜单补上「停止」与「删除」,并在有新版本时提示更新。

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · browser
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 3 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 16 处,展示前 15 条)
  • 删除本地文件递归
    index.js:642
    await rm(directory, { recursive: true, force: true });
  • 读取本地文件
    index.js:726
    root = await realpath(cwd);
  • 读取本地文件
    index.js:734
    excluded.push(await realpath(entry));
  • 读取本地文件
    index.js:749
    entries = await readdir(directory, { withFileTypes: true });
  • 读取本地文件
    index.js:764
    info = await lstat(path);
  • 读取本地文件
    index.js:807
    const info = await lstat(file.path);
  • 删除本地文件
    index.js:811
    await rm(file.path, { force: true });
  • 读取本地文件
    index.js:821
    const info = await lstat(dir.path);
  • 删除本地文件
    index.js:823
    await rmdir(dir.path);
  • 删除本地文件
    index.js:901
    await rm(path, { force: true });
  • 删除本地文件递归
    index.js:915
    await rm(aliasDir, { recursive: true, force: true });
  • 删除本地文件
    index.js:917
    await rm(object, { force: true });
  • 发起网络请求
    index.js:1087
    const response = await fetch(url, {
  • 写入本地文件
    index.js:1272
    void appendFile(state.logPath, `${entry.time} ${line}\n`, "utf8").catch(() => {});
  • 发起网络请求
    client.js:307
    const response = await fetch(path, {
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 一键停止会话
  • 永久删除会话数据
  • 清理日志与缓存

在侧边栏菜单里停止并删除 DeepSeek Harness 会话:先取消正在运行的回合、子代理、后台任务和提醒,再删掉日志、投影缓存和工作区记录。

README

由 DeepSeek 最新模型翻译生成
dsh-session-remove 给 DeepSeek Harness 侧栏的会话菜单补上「停止」与「删除」,并在有新版本时提示更新。 中文 · English 版本:1.0.6 | 更新:2026-10-31 1.0.6 变更:适配并实测 DSH 0.2.1-rc.5;更新提示改为「只提示 + 打开插件页」,并在标题/正文标明插件名与包名(装了很多插件也能认出是谁)——官方把插件升级定义为 profile 级操作,第三方插件需卸载后重装(HMR 下管理器还会直接抛 already-installed);附件回收补齐文件类附件的本体与别名;peer 上界收紧为 <0.3.0-0,并补充内部 API 依赖与多实例说明。 DSH 的会话行菜单只有 重命名 / 分叉 / 归档 —— 归档只是把会话藏起来,日志一个字节都没少;而想停掉一个会话的工作,你得先打开它、再按停止。这个插件把这两件事都放回你最常点的位置(会话行的 ⋯ 菜单)。 ⋯ 菜单 → 停止会话… 取消运行中的回合、子代理、后台任务与定时提醒 → 删除会话… 永久删除该会话的本地数据(不可恢复) 安装后不需要重启 DSH,也不需要刷新页面。 为什么不是又一个 DOM 补丁 同类插件普遍靠注入 DOM 或沿 React fiber 取行数据来塞菜单项,核心 UI 一改就可能失效。
展开完整文档(README 剩余 1 节)
dsh-session-remove

给 DeepSeek Harness 侧栏的会话菜单补上「停止」与「删除」,并在有新版本时提示更新。

中文 · English

版本:1.0.6 | 更新:2026-10-31

1.0.6 变更:适配并实测 DSH 0.2.1-rc.5;更新提示改为「只提示 + 打开插件页」,并在标题/正文标明插件名与包名(装了很多插件也能认出是谁)——官方把插件升级定义为 profile 级操作,第三方插件需卸载后重装(HMR 下管理器还会直接抛 already-installed);附件回收补齐文件类附件的本体与别名;peer 上界收紧为 <0.3.0-0,并补充内部 API 依赖与多实例说明。

DSH 的会话行菜单只有 重命名 / 分叉 / 归档 —— 归档只是把会话藏起来,日志一个字节都没少;而想停掉一个会话的工作,你得先打开它、再按停止。这个插件把这两件事都放回你最常点的位置(会话行的 ⋯ 菜单)。

⋯ 菜单 → 停止会话… 取消运行中的回合、子代理、后台任务与定时提醒 → 删除会话… 永久删除该会话的本地数据(不可恢复)

安装后不需要重启 DSH,也不需要刷新页面。

为什么不是又一个 DOM 补丁

同类插件普遍靠注入 DOM 或沿 React fiber 取行数据来塞菜单项,核心 UI 一改就可能失效。
本插件走的是官方开放的扩展点:

- 菜单项注册进官方 slot sidebar.workspaces.session.menu.item —— Pin / Rename / Fork / Archive 本身就是这个 slot 里的注册项,插件行按 order 排序;
- 停止走官方的 workspace/session-stop 准入(ctx.workspaceRegistry.stopSessionActivity()),由 dsh-agent / dsh-subagent / dsh-jobs / dsh-schedule 四个 provider 分别停掉回合、子代理、后台任务与提醒;
- 删除通过 ctx.workspaceRegistry 与 storageDomain 的公开 API 完成,不 shell 出 rm;
- 删完 emit 官方事件 api-session/removed(会话被销毁时会话控制器自己发的那条),侧栏该行当帧消失;
- 只删能自证身份的目录:日志路径由持久化后端的 locate() 给出,并要求 basename(dir) === sessionId,否则直接拒绝而不是猜。

安装

桌面版:侧栏 → 插件 → 添加插件 → 填包名 dsh-session-remove

其它 profile(web / tui / 自建)
dsh plugin --profile web add dsh-session-remove

中国用户注意:如果你的 npm 默认源是 registry.npmmirror.com,新发布的包在镜像上可能有同步延迟。若安装时报「declares no dsh.bundle」,请在安装对话框里把安装源改成
npm 官方源,或改用 tarball 地址:
https://registry.npmjs.org/dsh-session-remove/-/dsh-session-remove-<版本号>.tgz

升级:本插件带更新提示(见下),也可以在插件页卸载后重新安装。

用法

鼠标移到侧栏任意会话行 → 点 ⋯:

| 菜单项 | 做什么 |
| 停止会话… | 取消运行中的回合,并停掉该会话的子代理、后台任务与定时提醒 |
| 删除会话… | 永久删除该会话的本地数据 |

停止的一个要点:这条停止路径与归档用的是同一条(workspace/session-stop),它丢弃排队中的输入(界面上的停止按钮会保留)。确认框里写明了这一点。

0.2.1-rc.5 的官方归档对话框也提供「停止并归档」(停止后还会把会话从列表藏起来);本插件的「停止会话…」只停止、不归档、不改变会话可见性,这是它与官方归档的差别。

删除遇到仍在跑的工作时,弹窗会说明原因并多给一个 停止并删除 按钮,一步完成。

删除的是什么

| 位置 | 内容 |
| $DSH_HOME/sessions/<项目>/<会话id>/ | 会话日志(session.v4.jsonl.zstd)与写租约,整个目录移除 |
| $DSH_HOME/sessions/<项目>/<子代理会话id>/ | 该会话名下每一层子代理 / 团队会话的日志与写租约,同样整目录移除 |
| session_projcache 存储域 | 该会话及每个子代理会话的投影检查点记录 |
| $DSH_HOME/storages/workspace.json | 工作区成员位置、置顶、归档记录 |
| 可选:会话工作区里的新建文件 | 勾选「同时删除本会话产生的文件」后,删除会话创建之后新建的文件,并移除因此变空的目录 |
| 可选:$DSH_HOME/attachments/v1/… | 同一勾选会回收只被该会话引用的内容寻址附件:图片删除 objects/… 本体;文件删除 files/… 别名目录与 file-objects/… 本体;仍被其它会话引用的附件保留 |
| 页面 | api-session/removed 事件让该行立即消失,无需刷新 |

子代理会话是独立的存储会话:它们的目录和父会话并排放在同一个项目目录下,一旦父会话被删,侧栏、搜索和子代理寻址就都够不到它们了,所以删除必须连带清理。可以按需关闭:

- id: session-remove
name: 'dsh-session-remove'
config:
deleteSubagentSessions: false # 默认 true;关闭后只删选中的会话

分叉会话不连带删除:fork 产生的会话虽然也带 parentSession,但它是独立对话,不会随源会话消失。
工作区文件与附件默认不动,可显式勾选清理。工作区是项目目录,可能被多个会话共用;附件按哈希跨会话去重。因此这两类数据默认不会删除。删除对话框会先询问宿主,存在候选时给出带数量的选项:

- 「同时删除本会话产生的文件(N 个工作区文件、M 个附件)」——默认不勾选;
- 勾选后只删除会话创建之后新建的工作区文件(已存在的文件即使被覆盖也不会删),然后移除因此变空的目录;
- 附件只回收不再被任何其它持久化会话引用的对象;枚举失败或其它会话超过 64 个时跳过并告警;
- 想让它每次都自动执行(不再询问),配置 deleteSessionFiles: always;完全不清理用 off:

- id: session-remove
name: 'dsh-session-remove'
config:
deleteSessionFiles: confirm # confirm(默认)| always | off

搜索索引不需要额外处理:它按持久化列表对账,会话从列表消失即被清理。

安全边界

| 情况 | 行为 |
| 仍有回合 / 子代理 / 后台任务 / 定时提醒 | 拒绝,并提供「停止并删除」 |
| 停止后仍在收尾(有界等待内未清零) | 拒绝,提示稍候再试 |
| 某个子代理会话仍在运行 | 先停止并等待;收尾不过来则拒绝,父会话的文件此时一个都还没动 |
| 会话仍被进程持有 | 先释放它再删(见下) |
| 释放后的持久化屏障失败 | 拒绝,避免活句柄把刚删的日志写回来 |
| 子代理会话定位失败 | 父会话照常删除,对话框报告哪些子代理数据没清掉 |
| 无法枚举子代理会话(存储列表读取失败) | 拒绝,避免只删掉一半数据 |
| 工作区文件候选 | 仅在显式勾选(或 always)时删除;扫描按创建时间归属,已存在的文件绝不删除 |
| 附件仍被其它会话引用 | 保留,不回收 |

为什么删除前要先释放:一个还活着的写句柄会在下一次 flush 时把刚删掉的日志重新创建出来。
注册表的归档流程把释放交给会话的登记方,而 seam 没给第三方留受支持的入口,所以插件调用存储服务的公开方法 detachEntered(entry),并在删除文件前跑一次持久化屏障:

释放会话 → 停止工作 → 等待清零 → flush 屏障 → 删除文件

两处顺序都不能颠倒:先释放再停止,否则被停止的会话会唤醒 agent 去处理被杀任务的结果、甚至重新启动新的工作,那份新工作会活过删除变成孤儿;先释放再删文件,否则写句柄会把日志写回来。
取不到释放凭据(不同版本)时会退化为拒绝并提示先切换会话——功能降级,但绝不会留下半删状态。

每个子代理会话也走同一条链路:先停掉并等它清零、释放它,再随父会话的文件一起删除;任何一个子代理会话还在跑或还持有写句柄,都会在父会话的文件被碰之前拒绝掉。
删除父会话后若有子代理数据没能清掉,对话框会明确列出会话 id,不会假装全部成功。

删除当前正在查看的会话后,主区域会自动回到空状态(与官方归档当前会话的行为一致),不会停在已经不存在的旧页面。

更新提示

插件会定期问一次注册表(默认 npm 官方源,失败回退 npmmirror)自己是不是最新版, 只提示、不自动安装:右下角出现一条可关闭的提示,按钮是「打开插件页」与「稍后」。
提示标题写明插件名(「会话停止与删除 有新版本」),正文重复 npm 包名与新旧版本号——同时装了很多插件时,用户也能一眼认出这条提示属于哪个插件。

这是对齐 DSH 的插件管理模型:插件包属于 profile 级状态,「插件」页负责安装与卸载;官方文案明确 “按需安装的官方插件才提供『更新』,其他插件请先卸载再安装新版”,并且挂了 HMR 时
pluginManager.installBundle() 会对已安装的包直接抛 already-installed。因此本插件不尝试
覆盖自己(那还会在请求中途卸载正在响应它的宿主半),只把用户送到插件页完成升级。
session-delete.install-update 路由保留给旧版页面半,返回同一段指引而不是安装。

包名取自自身的 package.json,默认即可工作;只有把包部署成别名时才需要覆盖 updatePackageName。

配置

所有字段都是可选的,写在插件行的 config 下:

| 字段 | 默认值 | 说明 |
| deleteSubagentSessions | true | 删除会话时连带删除其名下所有子代理 / 团队会话;false 只删选中的会话 |
| deleteSessionFiles | confirm | 工作区文件与附件清理策略:confirm(弹窗勾选)、always(每次自动)、off(从不);也接受 true(等于 always)与 false(等于 off) |
| updateMode | notify | notify 只提示新版本;off 关闭更新检查 |
| updateRegistry | https://registry.npmjs.org/ | 更新检查使用的注册表 |
| updateFallbackRegistry | https://registry.npmmirror.com/ | 主注册表失败时的回退 |
| updateIntervalMs | 21600000(6 小时) | 检查间隔,最小 60000(1 分钟) |
| updatePackageName | 自身包名 | 仅在以别名发布时需要覆盖 |
| stopWaitMs | 10000 | 停止后等待工作清零的上限(毫秒),最小 500 |
| logPath | $DSH_HOME/plugin-session-remove.log | 调试日志路径 |
| debugLog | true | 设为 false 关闭调试日志 |

工作原理

| 半边 | 文件 | 职责 |
| Host | index.js | 在 /api 鉴权围栏内注册 7 条精确 Fetch 路由:session-delete.delete / .inspect / .stop / .update-state / .install-update(仅返回升级指引)/ .client-ready / .status |
| Client | client.js | 注册两个菜单项与一个 shell.overlay 条目(确认框 + 更新提示),中英双语跟随界面语言 |
浏览器半与宿主半同源通信,凭据由浏览器会话自动携带。

兼容性

- 实测 DSH 0.2.1-rc.5(DeepSeek Harness Desktop,2026-10-31;宿主半与页面半均真实加载、 client-ready 路由连通),以及 0.2.0-rc.2 / 0.2.1-rc.1
- 0.2.1-rc.1 → rc.5:本插件依赖的 24 个 DSH 包运行时 JS 逐字节相同(仅版本戳);宿主 API、持久化 locate/stat/list/open/flush、workspace 注册表、session_projcache 域、 api-session/removed、两个客户端 slot 与 connection.fetch.register 均未变化
- 会话日志格式仍为 v4(session.v4.jsonl.zstd,目录内另有 session.lock);删除时先从持久化后端
locate() 拿到目录,再整目录移除,不依赖文件名
- 兼容性门禁按 includePrerelease 评估 peer 范围:>=0.2.0-rc.2 <0.3.0-0 覆盖 rc.5;上界写成 -0 是为了不把下一个 minor 的预发布(如 0.3.0-rc.1)误放进来
- 依赖 0.2.1-rc.5 的一些内部面:sessions.store + detachEntered、
workspaceRegistry.headers/sessionPaths、storageDomain.get()(诊断面)。取不到时会明确拒绝删除
而不是误删;宿主升级后建议回归一次
- 需要组合里存在 connection / sessionPersistence / workspaceRegistry(Web profile 默认满足)
- 仅面向单实例桌面:多实例或共享 persistence root 时删除会移除 session.lock,写租约互斥会失效
- 零依赖、零构建

排查

插件会写一行/次操作到 $DSH_HOME/plugin-session-remove.log:

host half active (build r5-unified@1.0.6, 7 routes, update mode notify)
client half mounted (features=stop,delete,update+menu=sidebar.workspaces.session.menu.item,overlay=shell.overlay)
update check: current 1.0.6, latest 1.0.7 (update available)
stop session-…: asked job (settled: true) released session-… from the live store found 2 subagent session(s) under session-…
session files for session-…: 1 workspace file(s), 1 dir(s), 1 attachment(s) deleted session-… (+2 subagent sessions +3 session files) (session-dir:…, projection-cache) refused session-…: session-held

- 没有 client half mounted → 页面半没装载(检查插件是否启用、页面是否刷新过)
- refused <id>: → 宿主拒绝了这次操作,代码含义见「安全边界」
- refused <id>: session-release-unsupported → 当前 DSH 版本没有暴露安全释放入口;先切换到别的会话再删,或把 DSH / 本插件升级到相互匹配的版本
- 安装时报 declares no dsh.bundle → 见「安装」里的镜像同步说明

已知限制

- 删除不可恢复:没有回收站,也没有撤销。
- 子代理会话默认随父会话一并删除;用 deleteSubagentSessions: false 可关闭这一连带行为。
- 分叉会话是独立对话,删除源会话不会删掉它(即使它带 parentSession)。
- 子代理会话清理失败时父会话仍然会被删除(用户的主要意图已完成),残余目录只能在磁盘上手工处理;对话框会列出会话 id。
- 工作区清理是按创建时间的启发式归属:会话期间被覆盖的已有文件不会被删除(只会留下新内容),扫描有 2 万条目上限,超出部分会跳过并告警。
- 附件是内容寻址去重的;只有确认不再被其它会话引用的对象才会回收,被共享的附件保留。
文件类附件会连同 files/… 别名目录与 file-objects/… 本体一起删除;无法识别的宿主布局只删别名并保留本体。
- 删除正被界面打开的会话时会被先释放(主区域回到空状态)。
- 「停止」会丢弃排队中的输入,这是它所复用的官方停止路径的语义。
- 更新必须到「插件」页卸载后重新安装新版:插件不自行覆盖已安装的包(HMR 下管理器会以 already-installed 拒绝,且卸载正在响应请求的宿主半会中断请求);重装后旧宿主模块留到重启/重载,这是 DSH 的模块世代语义。
- 多实例 / 共享 persistence root 场景不要使用删除功能(会移除 session.lock,跨进程写租约失效)。

测试

测试不随 npm 包分发(files 白名单中没有 test/),安装后的插件目录不含测试文件。
仓库内运行:node --test test/.test.mjs(Node 22 下不要写成 node --test test/)。

Version: 1.0.6 | Updated: 2026-10-31

1.0.6 changes: verified against DSH 0.2.1-rc.5; the update notice is now report-only and hands the
user to the Plugins page, naming the plugin and its package in the title/body so a user with many
plugins can tell whose notice it is — DSH treats plugin upgrades as profile-level state and 第三方插件通过卸载再安装来更新(开启 HMR 时,管理器也会拒绝,提示 already-installed);文件附件连同其 digest body 一起回收;peer 上限收紧到 <0.3.0-0;内部 API 依赖与单实例边界已写入文档。

DeepSeek Harness 的会话行菜单只提供 Rename / Fork / Archive —— 归档只是隐藏会话,磁盘上的每个字节都还在;要停止会话的工作,得先打开会话再按 Stop。这个插件把这两项放回你本来就在点的位置:会话行的 ⋯ 菜单。

⋯ menu → Stop Session… cancels the running turn, subagents, background jobs and reminders → Delete Session… permanently removes that session's local data (no undo)

安装后无需重启 DSH,也无需刷新页面。

为什么不用另一种 DOM 补丁

同类插件靠注入 DOM 节点或遍历 React fiber 来添加菜单项,核心 UI 一改就可能失效。这个插件只用开放的扩展点:

- 菜单项注册进官方插槽 sidebar.workspaces.session.menu.item —— Pin / Rename / Fork / Archive 也是注册在同一插槽里的条目,所以插件菜单项按自己的 order 排序;
- 停止走官方 workspace/session-stop 准入(ctx.workspaceRegistry.stopSessionActivity()),由 dsh-agent / dsh-subagent / dsh-jobs / dsh-schedule 分别应答轮次、子代理、后台任务和提醒;
- 删除使用公开的 ctx.workspaceRegistry 和 storageDomain API —— 从不调用 rm;
- 删除会发出官方 api-session/removed 事件(Session 控制器在销毁活动 Session 时自己发出的就是它),菜单项当场消失;
- 只删除能证明自身身份的目录:日志路径取自持久化后端的 locate(),且必须满足 basename(dir) === sessionId,否则插件直接拒绝,不做猜测。

安装

Desktop: sidebar → Plugins → Add plugin → enter the package name
dsh-session-remove

Other profiles (web / tui / custom)
dsh plugin --profile web add dsh-session-remove

用的是 npmmirror 镜像源? 刚发布的包在镜像上可能滞后。如果安装被拒并提示 declares no dsh.bundle,在对话框里把 Install source 切到官方 npm 源,或用 tarball URL 安装 https://registry.npmjs.org/dsh-session-remove/-/dsh-session-remove-<version>.tgz。

升级:这个插件会给出更新提示(见下文),也可以在 Plugins 页面卸载后重装。

用法

把鼠标悬停在侧边栏任意会话行上 → 点击 ⋯:

| 菜单项 | 作用 |
| Stop Session… | 取消正在运行的轮次,并停止该会话的子代理、后台任务和提醒 |
| Delete Session… | 永久删除该会话的本地数据 |
关于停止,有一点需要知道:它和 archive 走同一条路径(workspace/session-stop),并且会丢弃排队中的输入(会话内的停止按钮会保留这些输入)。确认对话框里写明了这一点。

在 0.2.1-rc.5 中,内置的 archive 对话框还提供「停止并归档」(先停止工作,再隐藏会话);本插件的 Stop Session… 只停止——不归档,行仍然可见。这就是它与内置 archive 的区别。

当删除遇到正在运行的工作时,对话框会说明原因,并提供一个 Stop and delete 按钮,一步完成两件事。

删除的内容

| 位置 | 内容 |
| $DSH_HOME/sessions/<project>/<session-id>/ | 会话日志(session.v4.jsonl.zstd)及其写入租约——整个目录 |
| $DSH_HOME/sessions/<project>/<subagent-session-id>/ | 其下每个子代理 / 队友会话的日志和写入租约,任意深度——同样是整个目录 |
| session_projcache 存储域 | 该会话及每个子代理会话的投影检查点记录 |
| $DSH_HOME/storages/workspace.json | 工作区成员关系、固定和归档条目 |
| 可选:会话在其工作区中创建的文件 | 勾选对话框中的「同时删除本会话产生的文件」后,会话启动后创建的文件会被删除,随后删除因此变空的目录 |
| 可选:$DSH_HOME/attachments/v1/… | 同一选项会回收仅被该会话引用的内容寻址附件:图片的 objects/… 正文、文件的 files/… 别名目录及其 file-objects/… 正文;其他会话仍在引用的对象会保留 |
| 页面 | api-session/removed 事件移除该行,无需刷新 |

子代理会话是单独存储的 Session:它的目录与父会话目录同级,位于同一项目目录下;父会话一旦删除,侧边栏、搜索和子代理寻址都无法再找到它——所以删除必须一并带走它。可按配置档关闭:

- id: session-remove
name: 'dsh-session-remove'
config:
deleteSubagentSessions: false # default true; then only the selected session is deleted

分叉不会级联删除:分叉的对话也会记录 parentSession,但它是独立对话,源会话删除后它仍然存在。

工作区文件和附件默认不动,清理它们是明确的选择。 工作区是项目目录,其他会话可能共用;附件按内容哈希跨会话去重,所以两者都不会被隐式删除。删除对话框向 Host 询问候选,并提供一个带计数的选项:

- 「同时删除本会话产生的文件(N 个工作区文件,M 个附件)」——默认不勾选;
- 勾选后,只删除会话启动后创建的文件(预先存在的文件即使会话覆盖了它的内容,也从不删除),然后删除它们留下的空目录;
- 只有当没有其他已存储会话引用附件对象时,才回收它们;如果无法枚举其他会话,或这类会话超过 64 个,就跳过清扫并给出警告;
- 用 deleteSessionFiles: always 让它自动执行,或用 off 完全关闭:

- id: session-remove
name: 'dsh-session-remove'
config:
deleteSessionFiles: confirm # confirm (default) | always | off

搜索索引不需要额外处理:它会与持久化列表对账,所以会话离开该列表时,也会离开索引。

安全边界

| 情况 | 行为 |
| 轮次 / 子代理 / 后台任务 / 提醒仍在运行 | 拒绝,并提供 Stop and delete 操作 |
| 停止在限时等待内没有落定 | 拒绝,请你稍后重试 |
| 子代理会话仍在运行 | 先停止并等待;如果仍未落定,就在触碰任何父文件之前拒绝 |
| 会话仍被当前进程持有 | 先释放,再删除(见下文) |
| 释放后的持久化屏障失败 | 拒绝,这样打开的句柄无法重写已删除的日志 |
| 找不到子代理会话 | 仍删除父会话;对话框会列出被留下的子代理数据 |
| 无法枚举子代理会话(持久化列表失败) | 拒绝,这样在未知子会话仍存在时,父会话绝不会被删除 |
| 工作区文件候选 | 仅在显式检查后(或 always)删除;归属按创建时间判断,已存在的文件绝不删除 |
| 另一个会话仍引用的附件 | 保留,绝不回收 |

为什么先释放被持有的会话:活跃的写句柄会在我们删除日志后立刻把它刷回存在状态。注册表的归档流程依赖 Session 自己的所有者来释放它,而接缝没有给第三方插件提供受支持的释放方式,所以插件调用存储服务的公开方法 detachEntered(entry),并在触碰任何文件之前运行持久化屏障:

release the session → stop its work → wait for it to settle → flush barrier → remove files

两个顺序都不能交换:先释放再停止,否则被停止的会话会唤醒它的智能体去消费被终止任务的结果,甚至可能启动新工作——这会作为孤儿活过删除;先释放再删除文件,否则写句柄会重建日志。当释放凭据不可用(DSH 版本不同)时,插件降级为拒绝,并请你切换会话——绝不会出现删了一半的状态。

每个子代理会话都走同一条流水线:停止它、等待它落定、释放它,然后连同父会话的文件一起删除。仍在运行或仍持有写 handle 会在改动任何父文件之前,拒绝整个删除操作。如果父文件已经删除,部分子代理数据却清理不掉,对话框会列出会话 id,而不是假装全部成功。

删除主区域正在显示的会话会清空该视图(内置归档对当前会话也是这个效果),所以你永远不会停留在一个已经不存在的对话上。

更新提示

插件会定期向 registry 查询是否有新版本(优先官方 npm,失败时回退到 npmmirror),并且只做提示:右下角出现一条可关闭的提示,带 Open Plugins 和 Later 按钮。标题里写明插件名(「Stop & Delete Sessions has an update」),正文重复 npm 包名和两个版本号,这样装了很多插件的用户也能看出这条提示属于谁。

这遵循 DSH 的插件模型:插件包属于配置档级状态,安装和卸载由 Plugins 面板负责。面板自己的文案说,按需加载的 Official 插件有 Update 按钮,其他插件则通过卸载再重装来更新;挂载 HMR 后,pluginManager.installBundle() 还会以 already-installed 拒绝已安装的包。所以本插件从不覆盖自身(那也会卸载正在应答请求的 Host 那一半),只把用户引导到面板。session-delete.install-update 路由为旧版页面保留,返回同样的指引,而不是执行安装。

包名取自插件自己的 package.json,所以无需配置即可工作;只有以别名部署时,updatePackageName 才有意义。

配置

每个字段都是可选的,写在插件行的 config 下:

| 字段 | 默认值 | 含义 |
| deleteSubagentSessions | true | 同时删除该会话下所有子代理 / 队友会话;false 只删除选中的会话 |
| deleteSessionFiles | confirm | 工作区文件和附件策略:confirm(在对话框中询问)、always(每次删除都删)、off(从不删);true 视为 always,false 视为 off |
| updateMode | notify | notify 只提示新版本;off 关闭更新检查 |
| updateRegistry | https://registry.npmjs.org/ | 更新检查使用的 registry |
| updateFallbackRegistry | https://registry.npmmirror.com/ | 主 registry 失败时尝试 |
| updateIntervalMs | 21600000(6 小时) | 检查间隔;最小 60000(1 分钟) |
| updatePackageName | 包自身的名称 | 仅在以别名部署时需要 |
| stopWaitMs | 10000 | 等待已停止的工作清理完毕的上限,单位毫秒;最小 500 |
| logPath | $DSH_HOME/plugin-session-remove.log | 诊断日志路径 |
| debugLog | true | 设为 false 关闭诊断日志 |

工作原理

| 部分 | 文件 | 职责 |
| Host | index.js | /api 鉴权围栏内的七条精确 Fetch 路由:session-delete.delete / .inspect / .stop / .update-state / .install-update(仅提示)/ .client-ready / .status |
| Client | client.js | 两行菜单和一条 shell.overlay 条目(确认框 + 更新提示),双语,跟随 UI 语言 |

浏览器端与 Host 端同源通信;浏览器会话携带凭据。

兼容性

- 已在 DSH 0.2.1-rc.5 上验证(DeepSeek Harness Desktop,2026-10-31:两端均加载成功, client-ready 路由完成),另外也验证了 0.2.0-rc.2 / 0.2.1-rc.1
- rc.1 → rc.5:本插件依赖的 24 个 DSH 包在运行时字节一致(仅版本号不同);Host API、持久化 locate/stat/list/open/flush、工作区注册表、session_projcache 域、 api-session/removed、两个客户端插槽以及 connection.fetch.register 均未变化
- 存储的会话格式仍是 v4(session.v4.jsonl.zstd,旁边有 session.lock);删除时从持久化后端的 locate() 取得目录,整个删除,因此不依赖文件名
- 兼容性门禁用 includePrerelease 评估 peer 范围,>=0.2.0-rc.2 <0.3.0-0 覆盖 rc.5;上界 -0 把下一个小版本的预发布版(如 0.3.0-rc.1)排除在外
- 用到了一些内部接缝:sessions.store + detachEntered、
workspaceRegistry.headers/sessionPaths、storageDomain.get()(诊断接口)。缺少其中任何一个时,
插件会以具名错误码拒绝,而不是在活动句柄下执行删除;Host 升级后请重新测试
- 组合中需要 connection / sessionPersistence / workspaceRegistry(Web 配置档提供全部三项)
- 仅支持单实例桌面:多实例或共享持久化根目录时,删除会移除 session.lock,跨进程写租约不再保护日志
- 零依赖,无需构建步骤

故障排查

插件每发生一个事件就向 $DSH_HOME/plugin-session-remove.log 追加一行:

host half active (build r5-unified@1.0.6, 7 routes, update mode notify)
client half mounted (features=stop,delete,update+menu=sidebar.workspaces.session.menu.item,overlay=shell.overlay)
update check: current 1.0.6, latest 1.0.7 (update available)
stop session-…: asked job (settled: true) released session-… from the live store found 2 subagent session(s) under session-…
session files for session-…: 1 workspace file(s), 1 dir(s), 1 attachment(s) deleted session-… (+2 subagent sessions +3 session files) (session-dir:…, projection-cache) refused session-…: session-held

- 没有 client half mounted → 页面端未加载(检查插件是否启用,并刷新页面)
- refused <id>: → Host 拒绝;错误码见「安全边界」一节
- refused <id>: session-release-unsupported → 当前 DSH 版本没有暴露安全的释放接口;删除前先切换到其他会话,或者把 DSH / 本插件升级到匹配的版本
- 安装时报 declares no dsh.bundle 并拒绝 → 见 Install 下的 registry 说明

已知限制

- 删除是永久的:没有回收站,没有撤销。
- 默认情况下,子代理会话随父会话一起删除;deleteSubagentSessions: false 可关闭这层级联。
- 分叉出的对话是独立的:删除它的来源不会删除分叉,即使分叉记录了 parentSession。
- 如果子代理会话清理不掉,父会话仍然会被删除(用户的主要意图已经达成),残留项需要手动处理;对话框会列出这些会话 id。
- 工作区清理是创建时启发式:会话覆盖过的既有文件不会被删除(新内容保留),遍历上限为 20,000 个条目,跳过的其余部分会报告出来。
- 附件按内容寻址并去重;只回收没有其他会话引用的对象,共享的保留。文件附件会同时失去 files/… 别名目录和 file-objects/… 正文;遇到无法识别的 Host 布局时只删除别名。
- 删除 UI 中打开的会话会先释放它(主区域回到空状态)。
- 停止会丢弃排队的输入——这是它复用的官方停止路径的语义。
- 更新必须从 Plugins 页面执行(先卸载,再安装新版本):插件从不覆盖已安装的包(HMR 下管理器会以 already-installed 拒绝,而卸载处理该请求的 Host 半边会中止它)。重装后旧 Host 模块留在内存里,直到下次重启/重载——这是 DSH 的模块代际语义。
- 不要在多个实例或共享持久化根的情况下使用删除:它会移除 session.lock,跨进程写租约就不再保护日志。

测试

测试不属于 npm 包(test/ 不在 files 白名单里),所以安装后的插件副本不含测试文件。在检出目录里运行 node --test test/.test.mjs(Node 22 上不要写 node --test test/)。

License

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

给 DeepSeek Harness 侧栏的会话菜单补上「停止」与「删除」,并在有新版本时提示更新。

这是你的仓库?认领条目

回到顶部