jingkun05/dsh-session-delete-native

dsh-session-delete-native

Sidebar session deletion for DeepSeek Harness — a native "Delete session" row in the Session "…" menu. UI-only, registers no agent tools.

未分類deepseek-harnessdsh

dsh-session-delete-native

Sidebar session deletion for DeepSeek Harness — a native Delete session row in every Session's … menu, plus a permanent, confirmation-gated removal of that session's local records. Registers no agent tools.

中文 | English

中文

给 DSH(DeepSeek Harness)侧栏补上官方缺失的会话删除:在每个会话行的 … 菜单里用官方 Slot 原生多出一行「删除会话」,确认后从磁盘永久删除该会话的全部本地记录。

本包不注册任何 agent 工具:删除只能由界面点击触发。

与「克隆 DOM 注入菜单」做法的区别

本包占用官方 Slot sidebar.workspaces.session.menu.item(list / root scope / replaceRisk: none),由 ui-workspace 自己把这一行渲染进菜单:

  • 菜单的键盘走查、复制、焦点归还由菜单本身负责,不依赖 DOM 结构猜测;
  • DSH 升级侧栏结构时不会静默失效(Slot 消失会明确报错,而不是悄悄不注入);
  • 菜单行复用宿主组件 MenuItemButton;确认框用宿主 Modal(红色确认按钮 + 必须先勾选的不可恢复确认),外观/深浅色/焦点行为与内置的 pin/rename/fork/archive 一致。

若你已安装 npm 上的 dsh-session-delete(DOM 注入版),两者只能启用一个:它不依赖 Slot,会与本包在同一个 … 菜单里出现两个「删除会话」。

功能

入口菜单项行为
会话行 … 菜单删除会话永久删除该会话的日志目录 ~/.dsh/sessions/<项目目录>/<会话 id>/,不可恢复

位置:order: 500,排在 archive(400) 之后,并带一条分组细线(separatorBefore)。

安全设计

准入判定以**「是否有在跑的工作」**为准,而不是「是否已载入内存(live)」—— 把会话打开看一眼会让它 live,但不会让它有在跑的回合,这种情况应当可以删除。

  • 危险确认框:宿主 Modal + 警告图标 + 不可恢复说明 + 必须先勾选确认才能点主按钮;主按钮红色;Escape/取消/关闭都安全退出。
  • 有在跑的工作就拒绝:删除前先取写租赁,再用归档准入用的同一个 workspace/session-activity waterfall 判定 turn / job / subagent / schedule 四族;非空返回 409 busy。这与「归档会话」用的是同一套「什么算在跑」标准。
  • 写租赁是主闸门:ctx.sessionPersistence.open(id,'write') 只在没有其它写入者时成功 —— 有活 Agent 的会话因其自身持有写句柄而在此被拒(409 locked),其它进程的实例也由 Windows 文件锁/租赁挡住。
  • 已打开但空闲的会话可以删:它不持有写句柄、也没有在跑的工作,因此正常通过上述两道闸门。
  • 定位失败不猜测:无法唯一定位会话目录时返回 409 ambiguous 并不改动任何文件。
  • 先文件、后簿记:删除成功后才做归档簿记;任何失败都返回结构化 code,不会留下「已隐藏但没删掉」的半状态。
  • 同源且无 CORS:路由只在 Host 的 webServer 上注册精确路径,不设 CORS 头;跨源 JSON POST 需要预检,浏览器会在预检阶段拒绝。

删除后为什么侧栏立刻消失

Host 半删掉日志目录后,sessionController.list() → ctx.sessionQuery.listSessions() → SQLite _reconcile() 会用 sessionPersistence.list() 重扫,把消失的会话从索引里删掉;客户端再调 ctx.sessions.refresh()(remote.session.list)重新取基线,行就消失了。不需要手工清索引。

安装

把仓库 clone 到任意位置,然后把 clone 后的目录交给 dsh plugin add:

git clone https://github.com/jingkun05/dsh-session-delete-native.git
dsh plugin --profile <你的 profile 名> add ./dsh-session-delete-native

<你的 profile 名> 例如 web、desktop。安装会把本包加入该 profile 的 dsh.profile.bundles 层(包内 cordis.patch.yml 随该层生效,不要手动去改 profile 的 cordis.patch.yml),随后重启 DSH。

add 的参数必须是本机存在的路径(安装时会建立 link: 引用)。直接把仓库 URL 交给 add 不适用。

手动兜底(不使用 dsh plugin 时):把本目录 pnpm add 进 profile,然后在 profile 的 cordis.patch.yml 加入:

- insert:
    - id: dsh-session-delete-native
      name: dsh-session-delete-native

它对你的机器做了什么

删除是带外操作——DSH 官方没有会话删除 API(@deepseek-ai/dsh-session-persistence 明确写着 “No deletion or retention API”),所以本插件自己动文件系统。装之前请清楚这一点:

  • 删除时递归移除 $DSH_HOME/sessions/<项目目录>/<会话 id>/ 整个目录(含全部世代日志文件)。这是唯一会被删除的东西。
  • 不会删除:附件对象($DSH_HOME/attachments,内容寻址、可能与 fork 共享)、投影缓存($DSH_HOME/storages)、以及工作区注册表里的顺序记录。搜索索引由 DSH 自己在下一次 reconcile 时收敛。
  • 在 Host 上注册 3 条同源精确路由(/dsh-session-delete-native/{probe,delete,delete-many}),不设 CORS 头。仅由本插件的 Client 半调用。
  • 不注册任何 agent 工具:删除只能由界面点击触发。

兼容性

  • 实测环境:DSH 0.2.0-rc.2 + Web 组合(@deepseek-ai/dsh-base + @deepseek-ai/dsh-web-app)。
  • 依赖的 Host 服务:webServer、sessionPersistence、sessions、agents、workspaceRegistry。缺少任何一个该插件不会激活(在 inject 里声明)。
  • 依赖的客户端能力:官方 Slot sidebar.workspaces.session.menu.item、shell.overlay,以及 Module Loader 的 baseline external @deepseek-ai/dsh-client-ui-primitives(Modal / Button / MenuItemButton 及图标)。
  • 未声明 peerDependencies,因此不会因版本范围不匹配而被 DSH 拒绝加载;但也意味着 DSH 大版本升级后需要自行验证上述 Slot 与服务是否仍在。

配置

字段默认说明
root$DSH_HOME/sessions会话日志根目录。仅当你的组合把 dsh-session-persistence-jsonl 的 root 配到了非默认位置时才需要设置。

已知限制

  • 有在跑的工作的会话不能删:正在跑回合、等待审批/回答、有在跑的子 agent/后台任务/定时提醒的会话会被拒绝(409 busy 或 409 locked)。先停止它再删。已打开但空闲的会话可以删。
  • 不清理派生数据:附件对象(内容寻址、可能与 fork 共享)与投影缓存(~/.dsh/storages/...)保持原样;后者的孤儿条目无害,会在将来重写时消失。
  • 不提供回收站:删除不可恢复。
  • 未做视觉自动化验证(无浏览器控制能力),请在页面上实测一次外观。

English

Adds the missing session deletion to the DSH sidebar: it puts a Delete session row in every Session's … menu through the official sidebar.workspaces.session.menu.item slot, and permanently removes that session's local records from disk after confirmation.

This package registers no agent tools: deletion can only be triggered from the UI.

Why not DOM injection

The package occupies the official slot, so ui-workspace renders the row itself: the menu keeps its own keyboard walk, copy and focus return, and a sidebar refactor in a future DSH release fails loudly instead of silently dropping the entry. The row reuses the host's MenuItemButton; the dialog reuses the host's Modal with a red confirm button and a mandatory acknowledgement checkbox.

If you already run the npm dsh-session-delete (DOM-injection) package, enable only one of the two.

Behavior

  • Entry: Session row … menu → Delete session (order: 500, after archive, with a separatorBefore group hairline).
  • Removes ~/.dsh/sessions/<project>/<session-id>/ recursively. Irreversible.

Safety

Admission is based on running work, not on whether the session is loaded in memory: opening a session to look at it makes it live but gives it no running turn, and such a session can be deleted.

Two gates: the host takes the in-process single-writer lease (sessionPersistence.open(id,'write')) — a session with a live Agent is refused here because that Agent already holds the write handle (409 locked), and the Windows file lock is the cross-process guard — then it asks the same workspace/session-activity waterfall the archive path uses, refusing with 409 busy while any turn / job / subagent / schedule is running. An unresolvable session directory aborts without touching any file. Files are removed first and bookkeeping runs afterwards, so a failure never leaves a half-deleted state. Routes are exact, same-origin, and send no CORS headers.

Install

Clone the repository anywhere, then hand the cloned directory to dsh plugin add:

git clone https://github.com/jingkun05/dsh-session-delete-native.git
dsh plugin --profile <your-profile> add ./dsh-session-delete-native

This adds the package to that profile's dsh.profile.bundles layer (its cordis.patch.yml then applies as that layer — do not hand-edit the profile patch). Restart DSH afterwards. The add argument must be a path that exists on your machine; passing the repository URL to add does not work.

What it does to your machine

Deletion is out-of-band: DSH ships no session-deletion API (@deepseek-ai/dsh-session-persistence says "No deletion or retention API"), so this plugin touches the filesystem itself.

  • It recursively removes $DSH_HOME/sessions/<project>/<session-id>/ (all format generations). That is the only thing it deletes.
  • It does not delete attachment objects ($DSH_HOME/attachments, content-addressed and possibly shared with forks), the projection cache ($DSH_HOME/storages), or the workspace registry's ordering. The search index converges on DSH's next reconcile.
  • It registers three same-origin exact routes on the Host (/dsh-session-delete-native/{probe,delete,delete-many}) with no CORS headers, called only by this package's client half.
  • It registers no agent tools: deletion can only be triggered from the UI.

Compatibility

  • Verified against DSH 0.2.0-rc.2 with the Web composition (@deepseek-ai/dsh-base + @deepseek-ai/dsh-web-app).
  • Host services required: webServer, sessionPersistence, sessions, agents, workspaceRegistry (declared in inject; the plugin stays inactive without them).
  • Client capability required: the official sidebar.workspaces.session.menu.item and shell.overlay slots, plus the Module Loader baseline external @deepseek-ai/dsh-client-ui-primitives (Modal / Button / MenuItemButton and icons).
  • No peerDependencies are declared, so DSH will not refuse to load it on a version mismatch — which also means a DSH major upgrade needs a manual check that the slots and services above still exist.

Not an official package

This is a community plugin, not part of DeepSeek Harness. See the Chinese section for the configuration field and known limitations.

License

MIT