




















@@ -1,3 +1,4 @@
1+// Session disk-budget enforcement prunes orphaned artifacts before deleting store entries.
12import fs from "node:fs";
23import path from "node:path";
34import {
@@ -144,6 +145,8 @@ function resolveSessionTranscriptPathForEntry(params: {
144145const resolvedSessionsDir = canonicalizePathForComparison(params.sessionsDir);
145146const resolvedPath = canonicalizePathForComparison(resolved);
146147const relative = path.relative(resolvedSessionsDir, resolvedPath);
148+// Cleanup only owns artifacts under the sessions directory; absolute/parent escapes are
149+// ignored even if a stale entry points there.
147150if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
148151return null;
149152}
@@ -373,6 +376,8 @@ async function removeFileForBudget(params: {
373376const resolvedPath = path.resolve(params.filePath);
374377const canonicalPath = params.canonicalPath ?? canonicalizePathForComparison(resolvedPath);
375378if (params.dryRun) {
379+// Dry-run deletion is path-deduped so a transcript and pointer alias cannot count the same
380+// artifact twice against the simulated budget.
376381if (params.simulatedRemovedPaths.has(canonicalPath)) {
377382return 0;
378383}
@@ -453,6 +458,8 @@ export async function pruneUnreferencedSessionArtifacts(params: {
453458 sessionsDir,
454459store: params.store,
455460});
461+// Prompt refs are projected through the persistence layer so inline snapshots and externalized
462+// prompt blobs are judged against the bytes that would actually hit disk.
456463const projectedPromptBlobRefCounts = buildProjectedPromptBlobRefCounts(
457464projectSessionStoreForPersistence({
458465storePath: params.storePath,
@@ -583,6 +590,8 @@ export async function enforceSessionDiskBudget(params: {
583590(sum, bytes) => sum + bytes,
5845910,
585592);
593+// Budget starts from current files, then swaps in the projected store/prompt bytes that the next
594+// persistence pass will write.
586595let total =
587596[...files, ...promptBlobFiles].reduce((sum, file) => sum + file.size, 0) -
588597(storeFile?.size ?? 0) +
@@ -642,6 +651,7 @@ export async function enforceSessionDiskBudget(params: {
642651);
643652})
644653.toSorted((a, b) => a.mtimeMs - b.mtimeMs);
654+// Cheapest cleanup first: orphaned prompt blobs can relieve pressure without losing sessions.
645655for (const file of unreferencedPromptBlobQueue) {
646656if (total <= highWaterBytes) {
647657break;
@@ -669,6 +679,7 @@ export async function enforceSessionDiskBudget(params: {
669679isDiskBudgetRemovableSessionFile(file, referencedPaths, tempStaleCutoffMs, storeBasename),
670680)
671681.toSorted((a, b) => a.mtimeMs - b.mtimeMs);
682+// Then remove stale artifacts already detached from live entries.
672683for (const file of removableFileQueue) {
673684if (total <= highWaterBytes) {
674685break;
@@ -698,6 +709,7 @@ export async function enforceSessionDiskBudget(params: {
698709const bTime = getEntryUpdatedAt(params.store[b]);
699710return aTime - bTime;
700711});
712+// Last resort: delete oldest non-preserved sessions, then their now-unreferenced artifacts.
701713for (const key of keys) {
702714if (total <= highWaterBytes) {
703715break;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。