Repository navigation
Releases: G57651/dsh-session-manager
Release list
v0.4.1 — 插件内回收站:删除双写 + 恢复到原位
🗃 v0.4.1:插件内回收站 —— 删除双写 + 恢复到原位
新增:插件内回收站
用户从资源弹窗删除的文件/目录先复制到插件内回收站(内容 + 元数据),再移入系统废纸篓。恢复时从插件回收站写回原位并清除废纸篓中的已知副本。
- 存储布局:
$DSH_HOME/dsh-session-manager/recycle/<entryId>/{meta.json, payload/},0700 权限。 - 每条元数据记录:会话 ID、原始路径、类型(file/directory)、SHA-256、大小、删除时间、工作区路径、系统废纸篓路径。
恢复流程
- 从插件回收站读出元数据(原始路径 + 工作区 + 指纹);
- 写回原位(目标已存在则拒绝覆盖;文件恢复后 SHA-256 校验;目录树整体恢复);
- 移除系统废纸篓中已知路径的副本(仅当移入策略为 rename 且路径可知);
- 从索引中移除该条目。
安全边界:entryId 严格校验(正则 + 包含性);恢复目标仍限于原工作区内;已存在目标拒绝覆盖。
修改
deleteResources现为双写(先复制到回收站,再移入废纸篓);复制失败则不动文件。
验证
| 验证项 | 结果 |
|---|---|
smoke-lifecycle.mjs 332 项断言(新增:双写/按会话列表/恢复到原位含目录树/目标已存在拒绝/entryId 穿越拒绝) |
连跑三轮全绿 |
真机:deleteResources → recycleList → recycleRestore 全链路(文件进废纸篓 → 还原到原位 → 废纸篓副本清除 → 回收站消费) |
通过 |
升级
从 0.4.0 直接升级,无迁移。
v0.4.0 — 资源删除:移入本机废纸篓 + 弹窗批量删除
🗑 v0.4.0:资源删除 —— 移入本机废纸篓 + 弹窗批量删除
新增
- 单个资源删除:资源弹窗内每个文件类资源行的「删除」按钮——文件移入本机废纸篓(macOS 废纸篓 / Windows 回收站 / Linux XDG trash),可随时从系统废纸篓恢复,绝不直接销毁。
- 批量删除:勾选资源后工具栏「删除所选(N)」批量移入废纸篓;「清除选择」保留(一键取消勾选)。
- 删除后自动刷新:弹窗内资源状态即时更新(被删资源转为已不存在)。
平台策略(lifecycle/trash.js)
- macOS:优先同卷
rename进~/.Trash(重名自动编号);跨卷(iCloud 工作区)回退 Finder 删除(osascript,正确处理 iCloud 与分卷废纸篓)。 - Windows:PowerShell 回收站 COM API(
Shell.Application Namespace(10).MoveHere),参数单引号转义。 - Linux:优先
gio trash,无 GVfs 时直接实现 XDG trash 规范(Trash/files+Trash/info/*.trashinfo)。 - 测试隔离:
DSH_SM_TRASH_DIR可重定向废纸篓目录(冒烟套件永不触碰真实废纸篓)。
新增 RPC
| 端点 | 入参 | 说明 |
|---|---|---|
deleteResources |
{ id, paths } |
将会话资源移入本机废纸篓;路径仅限该会话工作区内,逐条返回结果(unsafe-path / resource-missing / untracked) |
安全边界不变:进程启动全部 argv 数组无 shell 拼接(Windows PowerShell 参数做元字符校验);工作区根本身不可删除。
验证
smoke-lifecycle.mjs309 项断言(新增:移入废纸篓且内容完好、路径越界/缺失守卫、双文件批量删除),连跑三轮全绿;- 真机(隔离宿主 + 真实 agent 回合):驱动 29/29、agent 回合 8/8、
deleteResources真机验证(文件经rename进入隔离废纸篓且内容完好)。
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)并刷新页面。
v0.3.9 — 资源弹窗布局修复 + 一键清除选择
🖼 v0.3.9:资源弹窗布局修复 + 一键清除选择
修复:资源行被勾选框标签挤扁(显示异常)
行内勾选框的无障碍标签文本就是完整路径(会话删除的文件路径很长),未约束宽度时吞掉整行宽度,把路径列挤压成一字宽竖排、行高暴涨——即你截图中的显示异常。
修复沿用批量操作栏的既有模式:视觉隐藏标签文本、保留无障碍名(屏幕阅读器仍可朗读),路径回到宽列正常显示、长路径自动换行。
新增:一键清除选择
工具栏新增**「清除选择」**按钮(无选中时禁用),与全选 / 反选并列——勾选后一键清空,不必逐个取消。
验证
- 渲染冒烟新增断言:清除按钮存在、路径列(dsm-resId)存在;
smoke-lifecycle295 项断言通过;smoke-host66 项通过。
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)并刷新页面。
v0.3.8 — 第三轮审计:修复大工作区跟踪死锁(全树遍历 + 哈希预算)
🔬 v0.3.8:第三轮审计 —— 修复大工作区跟踪死锁(本轮最重要发现)
本轮最重要的发现
探针实测(5000 文件工作区,与你的 default-workspace 同条件):基线一旦收录满 trackingMaxFiles(5000)个文件,之后会话创建/修改/删除的所有文件都落在截断线之外——每一轮 diff 都看不见它们,Journal 永远为空。资源弹窗显示"没有记录到资源变化"、删除会话清理不到任何东西——这正是你在真机截图里看到的那个会话的表现,此前被误判为"该会话没动过文件"。
修复:扫描器重构——全树始终遍历(不再有条目截断导致的跟踪死锁),trackingMaxFiles 语义变为内容哈希预算(昂贵的部分才封顶):已知未变文件(size+mtime+mode 相同)直接复用上一轮条目(零读取零哈希);预算耗尽后的文件仍记录存在性(无哈希,如实报告截断),由 birthtime 收养保护(既有文件绝不被清理移除);mtime 严格相等(<2ms 容差曾吞掉同窗口重写——审计探针发现)。
性能实测(5000 文件):
| 指标 | 修复前 | 修复后 |
|---|---|---|
| diff(无变化) | 548 ms | 174 ms(-68%,未变文件零读取) |
| diff(50 新文件) | 540 ms / journaled 0 | 151 ms / journaled 50 |
| 450 条 Journal 后 diff | — | 128 ms(无退化) |
第三轮其余交叉验证(全部通过)
- 镜像与真实宿主源码逐项一致:
encodeSegment/projectKey/会话目录布局与session-persistence-jsonl的真实实现逐字符语义一致;RPC 分发handler(endpoint, payload, …)与结果形状{ok,value}/{ok,error}与真实 connection 源一致;session/event/session/created监听签名与真实发射点一致;无 cwd 会话的_no-cwd布局由插件的扫描式定位天然覆盖。 - R2 修复回归验证:15 项修复逐条复核,未发现回归。
- client 契约(locale.register/bind、panelInfo.getSnapshot/subscribe、slots.inject/register)与真实客户端包一致。
验证
| 验证项 | 结果 |
|---|---|
smoke-lifecycle.mjs 295 项断言(新增满预算死锁回归用例:预算耗尽后创建/修改仍被跟踪、删除行为正确) |
连跑三轮全绿 |
smoke-host.mjs 66 项 + client 渲染冒烟 |
通过 |
| 真机:驱动 29/29、真实 agent 回合 8/8、kill -9 崩溃恢复 | 全部通过 |
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)。
v0.3.7 — 第二轮技能化审计:15 处隐形 bug 修复(红灯先行)
🛡 v0.3.7:第二轮技能化审计 —— 15 处隐形 bug 修复(全部红灯先行)
第二轮审计(code-review 双轴隔离法 + diagnosing-bugs 红灯纪律)换了四个视角:修复回归验证 / 未覆盖角落与并发 / 安全与隐私 / 测试有效性。每个修复都先写出能红的回归用例,再修,再验证转绿;并发去重与 env 解析用例做了红检确认。
HIGH
- 会话新建的目录从来清理不掉:
rm(recursive:false)对任何目录恒抛EISDIR,"非空保守跳过"分支是死代码。改用rmdir(真正的空目录契约)。 - 显式 resume 跳过回滚直接销毁 Journal:
cleanupRPC 的resume对delete_requested/rolling_back(崩溃残局)状态直接拆除——现在这三态一律先回滚、失败即止。 - Journal 并发序号全为 0:seq 分配是读-改-写且无串行化;env/进程记录与各类 diff 并发时全部拿到 seq 0(回放顺序失去意义)。append 改为单链串行;撕裂尾行也不再吞掉下一条记录(写前补换行修复)。
- 密钥泄漏面(三处一起堵):下载 URL 的
user:pass@与token=/X-Amz-Signature=等签名参数会原文进入 Journal 与 RPC → 落盘前脱敏;后台命令里的Authorization: Bearer …与TOKEN=…赋值 → 遮蔽;环境变量值一律只存指纹(不再依赖"名字像不像密钥"的启发式,DATABASE_URL=postgres://user:pw@…不再明文)。 - 快照库路径逃逸:Journal 里被伪造的
sha256(如../x)能读到快照目录之外的文件 → 只接受 64 位十六进制。 - 符号链接越出工作区:词法包含性检查挡不住符号链接父目录 → 写入/删除前追加
realpath复核。 - 权限变更不被恢复:仅 chmod 的既有文件命中
already-restored,权限原样保留 → mode 不一致同样执行恢复。
MEDIUM
- 显式 cleanup 被 autoCleanup 门控;且清理被跳过时 delete/purge 现在保留 tracking 目录(原会销毁显式清理的唯一依据)。
- 并发 diff 重复记账:防抖 diff + 显式 track + 列表自愈 + 删除前最终 diff 并发时同一修改被记录 2–3 次(红检确认)→ 按会话共享在途 diff。
- 卸载后仍在写入:HMR 重挂载后旧实例继续追加 →
disposed守卫;同会话 prepareRemoval 在途锁。 - 归档失败留下假墓碑:会话被隐藏却未删除 → 失败即回滚回收站条目。
- 进程误判/漏判:URL 查询里的
&、make &&被当作后台启动;cmd1 &\ncmd2漏判 → 按行单&判定。 - 下载归因错配:一条下载意图贴到第一个创建的文件 → 按
-o/--output与 URL 文件名提示优先匹配。 - pnpm 符号链接依赖不被跟踪 → 适配器接受符号链接(卸载只删链接、不动 store,真机用例验证目标目录完好)。
- env 解析:
FOO=bar cmd前缀被误记(现在只有显式export才算持久变更)、export A=1 B=2漏第二个、"x;y"被错误拆分。
其他
文档漂移全部修正(env 语义、purge 不归档、幽灵行两步失效、activity.json、断言数等);tracking 目录以 0700 创建;build-client 的 useStore 选择器检查加固(支持无空格/任意参数名、只认顶层 init 键)。
测试端同步修复:dispose/flush 路径现在被真实行使;isSessionDir 断言指向真实存在的目录;弱断言文案修正。
验证
| 验证项 | 结果 |
|---|---|
smoke-lifecycle.mjs(284 项断言,第二轮新增 15 组回归用例) |
连跑三轮全绿 |
smoke-host.mjs 66 项 + client 渲染冒烟 |
通过 |
| 真机:驱动 29/29、真实 agent 回合 8/8、kill -9 崩溃恢复、截断场景 5/5 | 全部通过 |
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)。
v0.3.6 — 技能化双轴审计:7 处隐形 bug 修复 + 死代码清理
🔬 v0.3.6:技能化双轴审计(code-review + diagnosing-bugs)—— 7 处隐形 bug 修复 + 死代码清理
用两个社区技能对全部约 7300 行代码做了一轮完整审计:
- code-review(67 万安装):双轴隔离并行审计 —— Standards(仓库规范 + Fowler 坏味道基线)与 Spec(需求符合度),四个子代理并行、报告不合并不重排;
- diagnosing-bugs(72 万安装):修复纪律 —— 每处 bug 先写能红的反馈循环(回归用例),看它红,再修,再看它绿。
修复的隐形 bug(全部红灯先行验证)
- env 清理会写宿主 env → 改为纯报告:插件从不改写宿主环境(子 shell 的 export 不会到达宿主),清理自然也不写;不再产生伪冲突。
- remove 分支缺少冲突检查:会话「创建→删除」的路径被外部重建时,旧代码因 after 为空跳过校验、静默删除;现在判冲突保留,无哈希的大文件增加 size 兜底。
- 恢复流程双重编码(两层真 bug):清理目录名是
encodeSegment(id);resume 把已编码目录名再喂给路径构造会二次编码——连 cleanup.json 都读不到、静默跳过;且 rollback 会把编码名写回状态文件,第二次 resume 定位失败、销毁 Journal 却留下会话目录。现在新增decodeSegment,以目录名为主源直接寻址。 trackingExclude替换默认排除表:自定义一项排除即丢失 node_modules/.git 等默认项 → 改为合并。- 所有权标签错报:会话自建文件再次编辑被标成
session_modified→ 标签改为跟随基线归属。 - 快照缺失报告误导:未存快照的恢复失败报
snapshot-missing→ 统一为文档一致的snapshot-unavailable;基线对超大文件跳过快照时输出日志。 - 清理意图晚于最终 diff 落盘:diff 窗口内崩溃会留下「删除中但不可续跑」的中间态 → 状态先落盘(对齐文档流程 step 1)。
client 侧:未知资源类型归入「其他」且不可打开(原来冒充文件并可点开);仅含环境变量的会话仍显示工具栏;已删除视图行恢复显示工作目录;config 调用失败不再静默关闭自动刷新。
死代码清理
safeList、snapshots.pathFor、未用图标与常量、死 locale 键、死 CSS、测试脚本死片段、trackers 未用参数;记录 id 去掉恒为 0 的 seq;index.js 端点与 RPC 回退分支去重;轮询扫描移入 manager 域层。导出/导入双扫描器复查归零。
验证
| 验证项 | 结果 |
|---|---|
smoke-lifecycle.mjs(214 项,含 6 组审计回归用例) |
连跑三轮全绿 |
smoke-host.mjs 66 项 + client 渲染冒烟(新增 3 组断言) |
通过 |
| 真机:驱动 29/29、真实 agent 回合 8/8、kill -9 崩溃恢复、截断场景 5/5 | 全部通过 |
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)。
v0.3.5 — 代码审计:4 处隐形 bug 修复 + 死代码清理
🔍 v0.3.5:全量代码审计 —— 4 处隐形 bug 修复 + 死代码清理
对全部源码做了一轮系统审计(死代码扫描 + 逐文件精读 + 真机复验),结果如下。
修复的隐形 bug(均已补回归用例)
1. 环境的「外部改动」被静默覆盖(与文件域保护不一致)
- 会话创建的变量若其后被外部改过值:清理会盲目 unset,删掉别人的值 → 现在仅当环境仍与 Journal 记录完全一致才 unset,否则记
externally-modified-after-session冲突并保留; - 既有变量被外部 unset 后:恢复逻辑会把它复活 → 现在判为冲突,保留外部的删除。
2. 权限变更丢失「既有」血统
收养的既有文件若仅被 chmod(内容不变),permission_changed 记录没有携带 unbaselined 标记——删除会话会把这个既有文件删掉。已修复:权限记录的血统与其它动作一致。
3. verify 阶段自相矛盾
非空的会话新建目录在 path 阶段是「保守跳过(非失败)」,verify 阶段却被判定为失败(rollback_failed、Journal 保留)。现在跳过结果统一传递,行为一致。
4. ENOENT 竞态被记成失败
文件在「状态检查」与「删除」之间消失会被记为 failed;现在视为幂等成功(目标状态已达成)。
另:resume 分支的「先删目录后写标记」顺序与其它移除路径对齐(幂等性本就兜底,统一更稳妥)。
死代码清理
files.js(pathExists/relativeTo/absoluteIn/parentDirOf/noteDir/knownDirList/resourceType 属性及未用 path 导入)、baseline.js(BASELINE_FILE_NAME)、journal.js(destroy)、snapshots.js(destroyAll/limits/logger)、opener.js(platform getter/logger)、manager.js(未用的公共面与导出)、session-manage.js、smoke-host.mjs 的未用导入。两个引用扫描器(导出/导入)复查归零。
验证
| 验证项 | 结果 |
|---|---|
smoke-lifecycle.mjs(新增 env 外部改/外部删、chmod 收养文件保留用例) |
193 项 × 3 轮全绿 |
smoke-host.mjs + client 渲染冒烟 |
66 项 + 全状态通过 |
| 真机:驱动 29/29、真实 agent 回合 8/8、kill-9 崩溃恢复、截断场景 5/5 | 全部通过 |
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)。
v0.3.4 — 截断误删防护(birthtime 判定)· 环境变量只记录不污染宿主
🛡 v0.3.4:修复两个高危隐形问题(截断误删防护 + 环境变量不再污染宿主)
1. 基线截断可能误删既有文件 —— 已修复
工作区文件数超过 trackingMaxFiles(默认 5000)时基线会被截断;未被基线收录的既有文件在首次 diff 时会被误判为「会话新建」——删除会话时可能把它们一起删掉(你的 default-workspace 已触发截断条件)。
修复:用 birthtime 二次判定(扫描时已记录;无 birthtime 的文件系统回退 mtime)——早于会话开始的文件一律静默收养为既有资源:不写 Journal、不出现在资源视图、绝不被清理移除。会话若修改/删除了这类文件,回滚时跳过并如实报告 preexisting-unbaselined(原内容未被快照,无法恢复)。状态刷新时保留血统标记,before 侧全程可追溯。
真机截断场景实测(基线强行截断到 1 个文件):两个既有文件全部保留 ✅,同时会话真新建的文件仍被正常清理 ✅——安全与核心功能同时成立。
2. 环境变量解析会真实污染宿主进程 —— 已修复
旧行为把解析出的 export FOO=bar 写进宿主进程 env——一条会话命令(如 export PATH=...、NODE_OPTIONS=...)会真实影响 harness 之后启动的所有子进程。
修复:只记录、绝不改写宿主 env;并把 PATH / NODE_OPTIONS / NODE_PATH / LD_* / DYLD_* / HOME / TMPDIR / SHELL / IFS / DSH_HOME 等运行性变量直接排除出跟踪。清理阶段的 env 恢复对「宿主从未改变过」的变量是刻意的 no-op。
3. 附带修复
session-manage.js 资源清理异常处理器引用了不在作用域的 logger——会把真实清理错误掩盖成 logger is not defined(本轮回归首次触发暴露,已修)。
验证
- 离线:
smoke-lifecycle181 项断言(新增收养/保留/新建照常清理/PATH 排除/宿主 env 不被改写),连跑三轮全绿;smoke-host66 项通过; - 真机(隔离宿主 + 真实 agent 回合):驱动 29/29、agent 回合端到端 8/8、截断场景 5/5。
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)。
v0.3.3 — 修复已删除会话的 tracking 目录复活(真机验证发现)
🩹 v0.3.3:修复已删除会话的 tracking 目录被"复活"
真机验证发现并修复两个竞态缺陷——删除会话后,其资源跟踪目录可能重新出现:
- 宿主重启的基线扫描依据过期的内存语料,把一个已被删除的会话重新建基线,把刚销毁的 tracking 目录建了回来。修复:
ensureBaseline守卫扩展为凡在回收站清单中的会话一律不再建基线(用户在回收站里恢复该会话后自动解除,可重新跟踪)。 - **activity.json 的防抖写入(1.5 秒)**可能在删除完成之后才落盘,
mkdir把目录建回来(残留一个 activity.json)。修复:写入落盘前核对回收站清单,且销毁 Journal 前取消未决的写入定时器。
真机验证(隔离实例:npm 发布版宿主 0.2.0-rc.2 + 真实 agent 回合)
| 项目 | 结果 |
|---|---|
| 生命周期驱动(基线 / 跟踪 / 删除回滚 / 外部修改保护 / openResource 守卫与真实定位) | 29/29 通过 |
| 真实 agent 回合端到端(mock LLM → 真实 bash 工具 → 真实 tool/call+tool/result 事件 → 事件驱动 Journal → 删除回滚) | 8/8 通过 |
| kill -9 崩溃恢复(重启自动续跑:junk 回滚、会话目录与 Journal 销毁、日志无异常) | 通过 |
| 修复复验:建→跟踪→删除→等 3 秒→重启,tracking 保持清理 | 通过 |
离线:smoke-lifecycle 163 项、smoke-host 66 项断言通过。
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)。
v0.3.2 — 打开方式收敛为本机文件管理器(移除应用选择)
🧹 v0.3.2:打开方式收敛为本机文件管理器
按要求,会话资源的打开方式只保留本机自带的文件管理器,其余打开方式全部移除:
- 移除:应用下拉选择、本机应用扫描(
listApplications端点及其扫描代码)、按指定应用打开的整条路径(openWith); - 保留:单个「打开」、勾选后的批量「打开所选(N)」、「全选 / 反选」、「打开工作区」——全部统一走文件管理器定位:
- macOS:访达(
open -R,在访达中显示该文件); - Windows:资源管理器(
explorer /select,<path>,选中该文件); - Linux:
xdg-open打开所在目录。
- macOS:访达(
openResource端点签名收敛为{ id, path };工具栏改为一行说明文案("将在本机文件管理器中显示(访达 / 资源管理器)")+ 操作按钮。
安全边界不变:所有进程启动均为 argv 数组、无 shell 拼接;可打开路径严格限制在该会话的工作区内(越界 / 已不存在 / 未跟踪分别报错)。
验证
smoke-lifecycle.mjs156 项断言(open 用例:reveal argv、工作区根、路径越界、文件缺失、未跟踪会话);smoke-host.mjs66 项断言通过;- client 渲染冒烟:断言应用下拉已不存在、工具栏与全选 / 反选按钮存在,全部面板状态渲染正常。
升级
从 0.3.x 直接升级,无迁移。更新后在 Plugins 面板重载插件(或重启 Harness)并刷新页面。