fix(media): retain inbound media with recursive cleanup TTL (#38292)

* Config: add media retention TTL setting

* Media: recurse persisted media cleanup

* Gateway: add persisted media cleanup timer

* Media: harden retention cleanup sweep

* Media: make recursive retention cleanup opt-in

* Media: retry writes after empty-dir cleanup race
This commit is contained in:
Vincent Koc
2026-03-06 22:06:09 -05:00
committed by GitHub
parent 563a125c66
commit ba9eaf2ee2
11 changed files with 446 additions and 40 deletions

View File

@@ -119,6 +119,17 @@ export { __resetModelCatalogCacheForTest } from "./server-model-catalog.js";
ensureOpenClawCliOnPath();
const MAX_MEDIA_TTL_HOURS = 24 * 7;
function resolveMediaCleanupTtlMs(ttlHoursRaw: number): number {
const ttlHours = Math.min(Math.max(ttlHoursRaw, 1), MAX_MEDIA_TTL_HOURS);
const ttlMs = ttlHours * 60 * 60_000;
if (!Number.isFinite(ttlMs) || !Number.isSafeInteger(ttlMs)) {
throw new Error(`Invalid media.ttlHours: ${String(ttlHoursRaw)}`);
}
return ttlMs;
}
const log = createSubsystemLogger("gateway");
const logCanvas = log.child("canvas");
const logDiscovery = log.child("discovery");
@@ -680,8 +691,9 @@ export async function startGatewayServer(
let tickInterval = noopInterval();
let healthInterval = noopInterval();
let dedupeCleanup = noopInterval();
let mediaCleanup: ReturnType<typeof setInterval> | null = null;
if (!minimalTestGateway) {
({ tickInterval, healthInterval, dedupeCleanup } = startGatewayMaintenanceTimers({
({ tickInterval, healthInterval, dedupeCleanup, mediaCleanup } = startGatewayMaintenanceTimers({
broadcast,
nodeSendToAllSubscribed,
getPresenceVersion,
@@ -696,6 +708,9 @@ export async function startGatewayServer(
removeChatRun,
agentRunSeq,
nodeSendToSession,
...(typeof cfgAtStart.media?.ttlHours === "number"
? { mediaCleanupTtlMs: resolveMediaCleanupTtlMs(cfgAtStart.media.ttlHours) }
: {}),
}));
}
@@ -1002,6 +1017,7 @@ export async function startGatewayServer(
tickInterval,
healthInterval,
dedupeCleanup,
mediaCleanup,
agentUnsub,
heartbeatUnsub,
chatRunState,