惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Visual Studio Blog
IT之家
IT之家
博客园 - 聂微东
The Cloudflare Blog
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
爱范儿
爱范儿
H
Help Net Security
博客园 - 叶小钗
V
V2EX
WordPress大学
WordPress大学
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园_首页
C
Check Point Blog
B
Blog
D
DataBreaches.Net
美团技术团队
罗磊的独立博客

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix: prune orphan session artifacts · openclaw/openclaw@e...
steipete · 2026-05-05 · via Recent Commits to openclaw:main

@@ -1,10 +1,16 @@

11

import fs from "node:fs";

2+

import path from "node:path";

23

import { resolveDefaultAgentId } from "../../agents/agent-scope.js";

34

import { resolveStoredSessionOwnerAgentId } from "../../gateway/session-store-key.js";

45

import { getLogger } from "../../logging/logger.js";

56

import { normalizeAgentId } from "../../routing/session-key.js";

67

import type { OpenClawConfig } from "../types.openclaw.js";

7-

import { enforceSessionDiskBudget } from "./disk-budget.js";

8+

import {

9+

enforceSessionDiskBudget,

10+

pruneUnreferencedSessionArtifacts,

11+

resolveSessionArtifactCanonicalPathsForEntry,

12+

type SessionUnreferencedArtifactSweepResult,

13+

} from "./disk-budget.js";

814

import {

915

resolveSessionFilePath,

1016

resolveSessionFilePathOptions,

@@ -54,6 +60,7 @@ export type SessionCleanupSummary = {

5460

missing: number;

5561

pruned: number;

5662

capped: number;

63+

unreferencedArtifacts: SessionUnreferencedArtifactSweepResult;

5764

diskBudget: Awaited<ReturnType<typeof enforceSessionDiskBudget>>;

5865

wouldMutate: boolean;

5966

applied?: true;

@@ -143,14 +150,35 @@ function pruneMissingTranscriptEntries(params: {

143150

return removed;

144151

}

145152153+

function addEntryArtifactPathsToSet(params: {

154+

paths: Set<string>;

155+

store: Record<string, SessionEntry>;

156+

storePath: string;

157+

keys: ReadonlySet<string>;

158+

}): void {

159+

const sessionsDir = path.dirname(params.storePath);

160+

for (const key of params.keys) {

161+

const entry = params.store[key];

162+

if (!entry) {

163+

continue;

164+

}

165+

for (const artifactPath of resolveSessionArtifactCanonicalPathsForEntry({

166+

sessionsDir,

167+

entry,

168+

})) {

169+

params.paths.add(artifactPath);

170+

}

171+

}

172+

}

173+146174

async function previewStoreCleanup(params: {

147175

target: SessionStoreTarget;

176+

maintenance: ResolvedSessionMaintenanceConfig;

148177

mode: ResolvedSessionMaintenanceConfig["mode"];

149178

dryRun: boolean;

150179

activeKey?: string;

151180

fixMissing?: boolean;

152181

}) {

153-

const maintenance = resolveMaintenanceConfig();

154182

const beforeStore = loadSessionStore(params.target.storePath, { skipCache: true });

155183

const previewStore = cloneSessionStoreRecord(beforeStore);

156184

const staleKeys = new Set<string>();

@@ -166,26 +194,50 @@ async function previewStoreCleanup(params: {

166194

},

167195

})

168196

: 0;

169-

const pruned = pruneStaleEntries(previewStore, maintenance.pruneAfterMs, {

197+

const pruned = pruneStaleEntries(previewStore, params.maintenance.pruneAfterMs, {

170198

log: false,

171199

onPruned: ({ key }) => {

172200

staleKeys.add(key);

173201

},

174202

});

175-

const capped = capEntryCount(previewStore, maintenance.maxEntries, {

203+

const capped = capEntryCount(previewStore, params.maintenance.maxEntries, {

176204

log: false,

177205

onCapped: ({ key }) => {

178206

cappedKeys.add(key);

179207

},

180208

});

209+

const entryCleanupArtifactPaths = new Set<string>();

210+

addEntryArtifactPathsToSet({

211+

paths: entryCleanupArtifactPaths,

212+

store: beforeStore,

213+

storePath: params.target.storePath,

214+

keys: staleKeys,

215+

});

216+

addEntryArtifactPathsToSet({

217+

paths: entryCleanupArtifactPaths,

218+

store: beforeStore,

219+

storePath: params.target.storePath,

220+

keys: cappedKeys,

221+

});

181222

const beforeBudgetStore = cloneSessionStoreRecord(previewStore);

223+

const budgetRemovedFilePaths = new Set<string>();

182224

const diskBudget = await enforceSessionDiskBudget({

183225

store: previewStore,

184226

storePath: params.target.storePath,

185227

activeSessionKey: params.activeKey,

186-

maintenance,

228+

maintenance: params.maintenance,

187229

warnOnly: false,

188230

dryRun: true,

231+

onRemoveFile: (canonicalPath) => {

232+

budgetRemovedFilePaths.add(canonicalPath);

233+

},

234+

});

235+

const unreferencedArtifacts = await pruneUnreferencedSessionArtifacts({

236+

store: previewStore,

237+

storePath: params.target.storePath,

238+

olderThanMs: params.maintenance.pruneAfterMs,

239+

dryRun: true,

240+

excludeCanonicalPaths: new Set([...budgetRemovedFilePaths, ...entryCleanupArtifactPaths]),

189241

});

190242

const budgetEvictedKeys = new Set<string>();

191243

for (const key of Object.keys(beforeBudgetStore)) {

@@ -199,6 +251,7 @@ async function previewStoreCleanup(params: {

199251

missing > 0 ||

200252

pruned > 0 ||

201253

capped > 0 ||

254+

unreferencedArtifacts.removedFiles > 0 ||

202255

(diskBudget?.removedEntries ?? 0) > 0 ||

203256

(diskBudget?.removedFiles ?? 0) > 0;

204257

@@ -212,6 +265,7 @@ async function previewStoreCleanup(params: {

212265

missing,

213266

pruned,

214267

capped,

268+

unreferencedArtifacts,

215269

diskBudget,

216270

wouldMutate,

217271

};

@@ -232,7 +286,8 @@ export async function runSessionsCleanup(params: {

232286

targets?: SessionStoreTarget[];

233287

}): Promise<SessionsCleanupRunResult> {

234288

const { cfg, opts } = params;

235-

const mode = opts.enforce ? "enforce" : resolveMaintenanceConfig().mode;

289+

const maintenance = resolveMaintenanceConfig();

290+

const mode = opts.enforce ? "enforce" : maintenance.mode;

236291

const targets =

237292

params.targets ??

238293

resolveSessionStoreTargets(cfg, {

@@ -245,6 +300,7 @@ export async function runSessionsCleanup(params: {

245300

for (const target of targets) {

246301

const result = await previewStoreCleanup({

247302

target,

303+

maintenance,

248304

mode,

249305

dryRun: Boolean(opts.dryRun),

250306

activeKey: opts.activeKey,

@@ -281,6 +337,20 @@ export async function runSessionsCleanup(params: {

281337

},

282338

);

283339

const afterStore = loadSessionStore(target.storePath, { skipCache: true });

340+

const unreferencedArtifacts =

341+

mode === "warn"

342+

? {

343+

scannedFiles: 0,

344+

removedFiles: 0,

345+

freedBytes: 0,

346+

olderThanMs: maintenance.pruneAfterMs,

347+

}

348+

: await pruneUnreferencedSessionArtifacts({

349+

store: afterStore,

350+

storePath: target.storePath,

351+

olderThanMs: maintenance.pruneAfterMs,

352+

dryRun: false,

353+

});

284354

const preview = previewResults.find(

285355

(result) => result.summary.storePath === target.storePath,

286356

);

@@ -298,10 +368,14 @@ export async function runSessionsCleanup(params: {

298368

missing: 0,

299369

pruned: 0,

300370

capped: 0,

371+

unreferencedArtifacts,

301372

diskBudget: null,

302373

wouldMutate: false,

303374

}),

304375

dryRun: false,

376+

unreferencedArtifacts,

377+

wouldMutate:

378+

(preview?.summary.wouldMutate ?? false) || unreferencedArtifacts.removedFiles > 0,

305379

applied: true,

306380

appliedCount: Object.keys(afterStore).length,

307381

}

@@ -315,11 +389,13 @@ export async function runSessionsCleanup(params: {

315389

missing: missingApplied,

316390

pruned: appliedReport.pruned,

317391

capped: appliedReport.capped,

392+

unreferencedArtifacts,

318393

diskBudget: appliedReport.diskBudget,

319394

wouldMutate:

320395

missingApplied > 0 ||

321396

appliedReport.pruned > 0 ||

322397

appliedReport.capped > 0 ||

398+

unreferencedArtifacts.removedFiles > 0 ||

323399

(appliedReport.diskBudget?.removedEntries ?? 0) > 0 ||

324400

(appliedReport.diskBudget?.removedFiles ?? 0) > 0,

325401

applied: true,