DeepSeek Harness 的 %TEMP% 被 dsh-workspace-changes 占满怎么清理
打开 %TEMP%,看到一堆 dsh-workspace-changes- 开头的目录,单个 1 到 6 GB,合计几十 GB——这是 DeepSeek Harness(DSH)的 workspace-changes 在会话没走到 dispose 时留下的孤儿。 报告者的机器上累积到 60.45 GB、131 个目录、302,971 个文件,而整个 %TEMP% 是 168 GB。社区在 master 上核对后确认形状一致,不是某个旧版本的特例(#10336)。
这类泄漏不会报错、不会拖慢启动,只会悄悄吃掉系统盘。下面先教你确认,再给清理与根治路线。
DeepSeek Harness 临时目录泄漏怎么确认:判据与 dispose 机制
判据是「目录名前缀固定、位于系统临时根、修改时间对应某次异常退出或升级重启」。 四步核对:
- 在 PowerShell 里列出匹配前缀的目录,并统计总大小。预期:得到目录数与 GB 数,量级与讨论里的样本接近或更小。
- 按修改时间排序,看最近一次写入对应哪个时间点。预期:多数集中在崩溃、强退或更新重启附近。
- 检查目录内部结构。预期:非 git 工作区能看到
objects/与captures/,说明是文件捕获的数据。 - 确认当前没有 DSH 会话正在使用它们。预期:DSH 已完全退出,或目录修改时间明显早于当前会话开始时间。
macOS 与 Linux 上同样存在这类目录,只是临时根换成了系统的 TMPDIR,前缀不变。
机制上,删除动作只挂在 session/disposed 上,而这个事件在崩溃、强退、kill、更新重启时都不会发出,启动时也没有任何清扫。 据社区核对源码,链路是:
- 每个会话首次用到 workspace-changes 时,调用
mkdtemp在临时根下建一个带随机后缀的目录。 - 目录里按需写入对象与捕获文件,单文件受
maxFileBytes限制。 - 唯一的删除点是
dispose(),通过session/disposed触发。 - 进程异常结束时事件不发出,目录原样留在磁盘上。
- 下一次启动不扫描历史目录,泄漏因此只增不减。
还有一个放大因素:单个会话只要碰过几个大二进制,captures/ 就能长到几 GB。报告者的清单里,单个目录 1.1 到 6.8 GB、最多约 1.5 万个文件,所以「按天清理」对每周只跑一次大项目的机器并不够。
泄漏增长有多快
增长由「异常退出次数 × 单次会话碰过的文件体积」决定,和会话跑了多久关系不大。
- 每个异常结束的会话至少留下一个目录,目录数约等于异常退出次数。
- 目录大小取决于该会话碰过多少文件、单个文件多大,而不是会话时长。
- 若工作区里有构建产物或镜像层,单次会话就能留下数 GB。
- 因此「磁盘还剩多少」比「过了几天」更适合作为清理阈值。
DSH插件造成的存量目录怎么清理
清理本身很安全,前提是先确认没有活跃会话在使用;不要一边跑长任务一边删。 七步:
- 完全退出 DSH,包括托盘与后台进程。预期:任务管理器里没有 DSH 相关进程。
- 列出所有匹配目录并记录大小,先看清再动手。预期:得到一份可核对的清单。
- 先删修改时间早于 7 天的目录,保留最近几天的作为对照。预期:释放大部分空间,最近会话不受影响。
- 重新启动 DSH,新建一个会话并触发一次文件改动。预期:功能正常,新目录照常创建。
- 观察一天后,再决定是否清理剩余的老目录。预期:确认没有误删正在使用的数据。
- 再扫一次系统临时根,确认没有遗漏其它前缀的 DSH 临时目录。预期:只剩最近的活跃目录。
- 记录本次释放的空间与目录数,作为下次判断泄漏速度的基线。预期:有了可对比的数据。
删除前把清单另存一份;如果之后发现某次会话的变更记录缺失,至少知道删了哪些。不想自己写清扫逻辑的话,可以在 DSH Plugin Hub 找现成的临时目录清理插件,但别用按名字通配删除的那种。
DeepSeek Harness 路径一:启动时按前缀清扫
不改目录内容、只加一次启动扫描,是成本最低的根治方向;按修改时间判断,能覆盖绝大多数孤儿。 适合能改宿主启动流程的人。
- 在宿主启动的早期阶段,读取临时根并列出匹配
dsh-workspace-changes-的目录。预期:拿到候选列表。 - 对每个目录读取修改时间,超过阈值(例如 24 小时)才进入删除队列。预期:正在使用的目录被排除。
- 删除失败只记日志,不阻塞启动。预期:权限或占用问题不会让 DSH 起不来。
- 首次上线先跑 dry-run,只打印将删列表。预期:人工核对一轮再真正启用。
- 补一个测试:造两个目录,一个旧一个新,确认只删旧的。预期:测试通过。
DeepSeek Harness 路径二:写入 pid/owner,定期回收
按时间清扫会误判「跑了一周的长会话」,写入持有者信息才能准确判断目录是否还有主人。 这也是讨论里更推荐的形状。
- 创建 scratch 目录后立刻写入一个 owner 文件,内容包含 pid、进程启动时间与会话 id。预期:每个目录都有可读的持有者信息。
- 回收时先读 owner,探测 pid 是否存活;pid 复用要用启动时间二次确认。预期:不会把新进程误认成旧持有者。
- 用定时任务回收超过一定时长、且持有者已不存在的目录。预期:泄漏在小时内被发现,而不是攒到几十 GB。
- 删除时先重命名成带删除标记的名字,再递归删除。预期:中途失败不会留下半个目录被下次误判。
- 记录每次回收的数量与字节数。预期:能从日志判断泄漏是否还在发生。
DeepSeek Harness 路径三:把缓存迁入工作区
把 scratch 根从系统临时目录换到工作区内的缓存目录,能让「删工作区」顺带清掉缓存,但会引入同步与仓库污染问题。 只适合特定场景。
- 选择一个工作区内的固定缓存目录,例如
.dsh/cache/workspace-changes。预期:路径确定,便于统一清理。 - 把该目录加入版本控制忽略规则与同步盘排除列表。预期:
git status干净,同步客户端不扫描。 - 修改创建逻辑,改用这个根目录,并在工作区删除时一并清理。预期:换工作区即清缓存。
- 评估工作区所在磁盘:网络盘或同步盘上不要启用。预期:采集延迟没有明显上升。
- 保留迁移前的存量清理步骤,否则老目录仍占着系统盘。预期:磁盘占用真正下降。
DeepSeek Harness 三条修法怎么选、边界在哪
先清存量,再决定要不要改代码;能改启动流程就选路径一,要长期准确就选路径二,工作区本身就是临时目录才考虑路径三。 四点提醒:
- 不要把「临时目录」理解成「可以随便删」:正在写入的对象被删,会让当前会话的变更视图缺块。
- 同目录下还有一类带 pid 的 spill 文件,它们的名字里就有持有者信息,清扫可以直接判断;workspace-changes 目录没有这个信息,所以更需要补 owner。
- 系统清理工具可能已经动过这些目录,排查时以实际存在为准,不要只信计划任务日志。
- 讨论里没有官方修复版本,0.1.7-rc.2 可复现、master 形状相同;相关工作区与任务问题另见 工作区与后台任务。
- 清理前后各记一次磁盘可用空间,确认释放量与被删目录的大小对得上,避免把别处的增长误算成泄漏。
常见问题
确认没有 DeepSeek Harness 进程正在使用它们之后可以删。这些目录是 workspace-changes 为每个会话建的 scratch 数据,正常结束会话时会被删掉;留下的是崩溃或强退的孤儿。先完全退出 DSH,再按修改时间筛掉最近几小时的目录,删除更早的即可,不会影响会话历史。
DeepSeek Harness 只在 session/disposed 事件上删除目录,而关窗口不等于每个会话都走到 dispose。进程被强杀、更新时重启、系统休眠后终止,都不会触发这个回调;下一次启动也没有清扫逻辑。所以「关过窗口」不能保证目录已清理,要看目录的修改时间与当前是否有会话在跑。
DeepSeek Harness 有两条路:启动时扫描 temp 根下匹配前缀的目录,按修改时间清理超过阈值的;或者创建目录时写入 pid 或 owner 文件,回收时判断持有进程是否还活着。前者不需要改目录内容,后者更准确,但要同时处理 pid 复用与启动时间。
DeepSeek Harness 把缓存迁进工作区会带来仓库污染和同步压力。缓存放进工作区必须先加进忽略规则,否则会出现在 git 状态里;工作区若在同步盘或网络盘上,还会把大量对象同步出去,并显著拖慢采集。它适合工作区本身就是临时目录的场景,不适合长期项目目录。
相关术语
- scratch 目录
- scratch 目录是 workspace-changes 为单个会话创建的临时工作区副本,用来存放对象与文件捕获。它按会话隔离,正常销毁会话时删除,进程异常退出时不会被清理。— https://github.com/deepseek-ai/deepseek-harness/discussions/10336
- mkdtemp
- mkdtemp 是创建带随机后缀的唯一临时目录的系统调用封装,保证并发创建不冲突。它只负责创建,不负责删除,生命周期要由调用方自己管理。— https://github.com/deepseek-ai/deepseek-harness/discussions/10336
- session/disposed
- session/disposed 是 DeepSeek Harness 在会话销毁时发出的事件,workspace-changes 订阅它来删除 scratch 目录。进程崩溃或强退时该事件不会发出,清理也就不会发生。— https://github.com/deepseek-ai/deepseek-harness/discussions/10336
- maxFileBytes
- maxFileBytes 是 workspace-changes 在捕获文件内容时的单文件大小上限。它限制单个文件写入 captures 的体积,但目录总量仍会随捕获文件数量增长。— https://github.com/deepseek-ai/deepseek-harness/discussions/10336
来源
- #10336 — workspace-changes 的每会话 scratch 目录在 dispose 未执行时泄漏· deepseek-ai(GitHub Discussions)
- DeepSeek Harness 官方仓库· deepseek-ai(GitHub)