Repository navigation
Replies: 1 comment
|
Confirmed at
private scratchDir(): Promise<string> {
this.scratch ??= mkdtemp(join(this.env.tempRoot, 'dsh-workspace-changes-'))
return this.scratch
}one per Session, created on first use, and async dispose(): Promise<void> {
...
if (this.scratch !== undefined) await rm(await this.scratch, { recursive: true, force: true })
Two things worth adding to the report. The sibling leak has a better handle than this one. The per-directory sizes in your inventory say where the bytes are: 1.1-6.8 GB and up to ~15k files each, with the layout being If you want a minimal fix to propose: record the pid (or the process start time) in a file inside the scratch directory at creation, and sweep |
Uh oh!
There was an error while loading. Please reload this page.
Summary
@deepseek-ai/dsh-workspace-changescreates onemkdtempscratch directory per Session under the system temp root.dispose()removes it, but when a session ends without reachingdispose()(crash, force-quit, kill, update/restart) the directory is left behind forever, and nothing sweeps orphans on the next start.On this machine it has accumulated to 60.45 GB across 131 orphan directories / 302,971 files, out of a 168 GB
%TEMP%.Environment
0.1.7-rc.2(desktop + CLI)@deepseek-ai/dsh-workspace-changes@0.1.7-rc.2C:\Users\ROG\AppData\Roaming\npm\node_modules\@deepseek-ai\dshEvidence
Read-only inventory of
%TEMP%(168.68 GB / 748,655 files total):dsh-workspace-changes-*dsh-workspace-changes-*specifics:2026-09-29 20:31, newest2026-10-20 00:12<scratch>/objects/…and, for non-git workspaces,<scratch>/captures/…Root cause (code references)
packages/deliverables/workspace-changes,lib/index.js::808—this.scratch ??= mkdtemp(join(this.env.tempRoot, "dsh-workspace-changes-")):639— doc comment: "Temporary directory holding this Session's snapshot objects, scratch indexes, and captured copies.":781—async dispose() { this.lifetime.abort(); this.records.clear(); await this.chain; if (this.scratch !== void 0) await rm(await this.scratch, { recursive: true, force: true }); }So the lifetime is exactly "until
dispose()runs". Any path that skips it leaks a multi-GB directory:taskkill /F, Task Manager, restart during app update)There is no orphan sweep at startup and no size cap on a scratch directory.
Amplifier: non-git working directories
For a cwd that is not a git repository, the recorder falls back to whole-file captures (
captures/,:692and:891, bounded per file bymaxFileBytes). A few-hundred-MB non-git workspace therefore produces GB-scale scratch per session. The project I was working in was non-git for its first two weeks, which matches the largest directories.Repro
dshin a non-git directory with a few hundred MB of files;taskkill /F);%TEMP%keeps adsh-workspace-changes-*directory that is never removed.Count them (read-only):
Suggested fixes (any of these stops the growth)
tempRoot/dsh-workspace-changes-*older than N days that are not owned by a live process. Writing an.ownerfile (pid+ boot id + start time) into each scratch makes "live" checkable without heuristics.tempRoot/dsh/workspace-changes/<uuid>so the sweep target is a single directory and cannot touch unrelated temp entries.process.on('exit')/SIGINThandler that removes the scratch synchronously, plus mark-and-sweep on next start if that fails.maxScratchByteslimit that stops capture (or warns) when one scratch exceeds, say, 512 MB.dsh doctorthat lists orphan scratch dirs (size, age) and prunes them with dry-run default.Notes
All reactions