Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/session-cleanup-stop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@kilocode/cli": minor
"kilo-code": minor
---

Add a stop button to session cleanup: a running cleanup pass can now be stopped from the settings screen. Already-deleted sessions stay deleted, the interrupted pass records its partial result, and the session database reclaims the freed disk space.
5 changes: 5 additions & 0 deletions .changeset/session-cleanup-vacuum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---

Reclaim disk space after session cleanup: the session database now shrinks on disk once a cleanup pass frees enough pages, instead of keeping the old file size forever.
2 changes: 2 additions & 0 deletions packages/kilo-docs/pages/code-with-ai/features/checkpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ The policy lives in `kilo.json` under the `retention` key, so it applies no matt

Once enabled, the VS Code extension triggers cleanup about once a day while it is running. You can also run it immediately with the **Run Cleanup Now** button, which asks for confirmation first because deletion is permanent. A spinner and live status show the scanning or deleting phase and the number of sessions processed out of the total. During deletion, the status also shows deleted and failed counts. Reopening Settings shows the current progress. After each run, the **Last cleanup** line shows how many sessions were deleted, how many were skipped, and whether anything failed.

A long pass can be halted with the **Stop cleanup** button while it runs. Stopping keeps what the pass already deleted and skips the rest; the **Last cleanup** line marks an interrupted run. After a pass frees a large share of the session database, Kilo also reclaims the disk space, so the storage file shrinks instead of keeping its old size.

### What Is Protected

- **Active sessions**: Cleanup skips sessions reported as busy by the current backend and sessions with message or part writes in the last hour, including writes from another window or terminal. Merely keeping an idle session open does not protect it.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3553,6 +3553,23 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
})
return true
}
if (message.type === "stopAutoCleanupNow") {
const service = this.autoCleanup()
const requested = await service?.cancel().then(
Comment thread
vkeerthivikram marked this conversation as resolved.
Resolved
() => true,
() => false,
)
const status = await service?.status().catch(() => null)
this.postMessage({
type: "autoCleanupStateLoaded",
requestID,
last: status?.last ?? service?.lastResult() ?? null,
progress: status?.progress,
pending: service?.running,
...(!requested && !status ? { error: "run" } : {}),
})
return true
}
return false
}

Expand Down
12 changes: 11 additions & 1 deletion packages/kilo-vscode/src/services/task-cleanup/retention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ export interface RetentionResult {
skippedActive: number
failed: number
durationMs: number
cancelled?: boolean
reclaimedBytes?: number
}

export interface RetentionStatus {
policy: { enabled: boolean; maxAgeDays: number }
last: RetentionResult | null
progress?: {
phase: "scanning" | "deleting"
phase: "scanning" | "deleting" | "cancelling"
total: number
processed: number
deleted: number
Expand Down Expand Up @@ -125,6 +127,14 @@ export class RetentionService {
return this.normalize(response.data)
}

/** Asks the backend to stop the active pass; false when none is running. */
async cancel(): Promise<boolean> {
const client = this.connection.getClient()
const directory = this.directory()
const response = await client.kilocode.retention.cancel(directory ? { directory } : {}, { throwOnError: true })
return response.data.requested
}

/** The SDK types JSON numbers as a NaN/Infinity union; the wire format is a plain number. */
private normalize(data: unknown): RetentionStatus {
const status = data as RetentionStatus
Expand Down
25 changes: 25 additions & 0 deletions packages/kilo-vscode/tests/unit/cleanup-poll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,29 @@ describe("session cleanup polling", () => {
f.poll.execute()
expect(f.sent).toHaveLength(2)
})

test("stop posts once per running pass and never while idle", () => {
const f = fixture()
f.poll.stop()
expect(f.sent).toHaveLength(0)
f.poll.start()
f.reply(0, { pending: true, progress })
f.poll.stop()
f.poll.stop()
expect(f.sent.filter((message) => message.type === "stopAutoCleanupNow")).toHaveLength(1)
const halt = f.sent.at(1)
if (!halt || !("requestID" in halt)) throw new Error("Missing stop request")
f.poll.receive({
type: "autoCleanupStateLoaded",
requestID: halt.requestID,
last: null,
progress: { ...progress, phase: "cancelling" },
})
expect(f.states.at(-1)?.message.progress?.phase).toBe("cancelling")
// A later pass can be stopped again.
f.poll.start()
f.reply(2, { pending: true, progress })
f.poll.stop()
expect(f.sent.filter((message) => message.type === "stopAutoCleanupNow")).toHaveLength(2)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,17 @@ const CheckpointsTab: Component = () => {
const lastText = () => {
const run = state().last
if (!run) return language.t("settings.autoCleanup.lastRun.never")
return language.t("settings.autoCleanup.result", {
date: new Date(run.at).toLocaleString(),
deleted: String(run.deleted),
scanned: String(run.scanned),
failed: String(run.failed),
active: String(run.skippedActive),
seconds: String(Math.max(0.1, Math.round(run.durationMs / 100) / 10)),
})
const cancelled = run.cancelled ? ` — ${language.t("settings.autoCleanup.lastRun.cancelled")}` : ""
return (
language.t("settings.autoCleanup.result", {
date: new Date(run.at).toLocaleString(),
deleted: String(run.deleted),
scanned: String(run.scanned),
failed: String(run.failed),
active: String(run.skippedActive),
seconds: String(Math.max(0.1, Math.round(run.durationMs / 100) / 10)),
}) + cancelled
)
}

const progressText = () => {
Expand Down Expand Up @@ -157,17 +160,29 @@ const CheckpointsTab: Component = () => {
)}
</For>
<SettingsRow title={language.t("settings.autoCleanup.lastRun.title")} description={lastText()} last>
<Button
variant="secondary"
size="normal"
disabled={running() || isDirty() || !enabled()}
onClick={confirmRun}
>
<>
<Button
variant="secondary"
size="normal"
disabled={running() || isDirty() || !enabled()}
onClick={confirmRun}
>
<Show when={running()}>
<Spinner />
</Show>
{language.t("settings.autoCleanup.runNow")}
</Button>
<Show when={running()}>
<Spinner />
<Button
variant="ghost"
size="normal"
disabled={state().progress?.phase === "cancelling"}
onClick={() => poll.stop()}
>
{language.t("settings.autoCleanup.stop")}
</Button>
</Show>
{language.t("settings.autoCleanup.runNow")}
</Button>
</>
</SettingsRow>
<div role="status" aria-live="polite" aria-atomic="true">
<Show when={running()}>{progressText()}</Show>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export class CleanupPoll {
private timer?: ReturnType<typeof setTimeout>
private request?: string
private run?: string
private halt?: string
private disposed = false
private stale = false
private failure?: AutoCleanupStateLoadedMessage["error"]
Expand All @@ -29,6 +30,12 @@ export class CleanupPoll {
this.post({ type: "runAutoCleanupNow", requestID: this.run })
}

stop(): void {
if (this.disposed || this.halt || !this.state?.progress) return
Comment thread
vkeerthivikram marked this conversation as resolved.
Resolved
this.halt = crypto.randomUUID()
this.post({ type: "stopAutoCleanupNow", requestID: this.halt })
}

receive(message: AutoCleanupStateLoadedMessage): void {
if (this.disposed || !message.requestID) return
if (message.requestID === this.run) {
Expand All @@ -43,7 +50,8 @@ export class CleanupPoll {
this.start()
return
}
} else return
} else if (message.requestID !== this.halt) return
if (message.requestID === this.halt) this.halt = undefined
Comment thread
vkeerthivikram marked this conversation as resolved.
Resolved
const unavailable = message.error === "status" || message.error === "timeout"
this.state = {
...message,
Expand Down
3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/ar.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/br.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/bs.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/da.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,9 @@ export const dict = {
"settings.autoCleanup.runNow": "Bereinigung jetzt ausführen",
"settings.autoCleanup.runNow.confirm":
"Abgelaufene Sitzungen endgültig in allen Projekten und allen Kilo-Clients auf diesem Rechner löschen?",
"settings.autoCleanup.stop": "Bereinigung stoppen",
"settings.autoCleanup.progress.cancelling": "Sitzungsbereinigung wird gestoppt...",
"settings.autoCleanup.lastRun.cancelled": "abgebrochen",
"settings.context.autoCompaction.title": "Automatische Komprimierung",
"settings.context.autoCompaction.description": "Kontext automatisch komprimieren, bevor er das Limit erreicht",
"settings.context.compaction.title": "Komprimierung",
Expand Down
3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,9 @@ export const dict = {
"settings.autoCleanup.runNow": "Run Cleanup Now",
"settings.autoCleanup.runNow.confirm":
"Permanently delete expired sessions across all projects and every Kilo client on this machine?",
"settings.autoCleanup.stop": "Stop cleanup",
"settings.autoCleanup.progress.cancelling": "Stopping session cleanup...",
"settings.autoCleanup.lastRun.cancelled": "interrupted",

"settings.context.autoCompaction.title": "Auto Compaction",
"settings.context.autoCompaction.description": "Automatically compact context before it reaches the limit",
Expand Down
3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/es.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/fa.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/fr.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/it.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/ja.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/ko.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/nl.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/no.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/pl.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/ru.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading