diff --git a/CHANGELOG.md b/CHANGELOG.md index df5a997b8a8..00c755f0b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Sessions: honor configured `session.maintenance` settings during load-time maintenance instead of falling back to default entry caps. Fixes #71356. Thanks @comolago. - Browser/sandbox: pass the resolved `browser.ssrfPolicy` into sandbox browser bridges and refresh cached bridges when the effective policy changes, so sandboxed browser navigation honors private-network opt-ins. Fixes #45153 and #57055. Thanks @jzakirov, @zuoanCo, and @kybrcore. - Browser/proxy: keep Gateway/provider proxy environment variables from proxying the OpenClaw-managed browser, so `HTTP_PROXY` and `HTTPS_PROXY` no longer block ordinary browser navigation. Fixes #71358. Thanks @Sanjays2402. - Dashboard/Windows: open Control UI and OAuth URLs through the system URL handler without `cmd.exe` parsing or PATH-based `rundll32` lookup, and reject non-HTTP browser-open inputs. Fixes #71098. Thanks @Sanjays2402. diff --git a/src/config/sessions/store-load.ts b/src/config/sessions/store-load.ts index 2543467c5c9..f4db2c41210 100644 --- a/src/config/sessions/store-load.ts +++ b/src/config/sessions/store-load.ts @@ -8,10 +8,10 @@ import { setSerializedSessionStore, writeSessionStoreCache, } from "./store-cache.js"; +import { resolveMaintenanceConfig } from "./store-maintenance-runtime.js"; import { capEntryCount, pruneStaleEntries, - resolveMaintenanceConfigFromInput, type ResolvedSessionMaintenanceConfig, } from "./store-maintenance.js"; import { applySessionStoreMigrations } from "./store-migrations.js"; @@ -128,7 +128,7 @@ export function loadSessionStore( applySessionStoreMigrations(store); normalizeSessionStore(store); - const maintenance = opts.maintenanceConfig ?? resolveMaintenanceConfigFromInput(); + const maintenance = opts.maintenanceConfig ?? resolveMaintenanceConfig(); if (maintenance.mode === "enforce" && Object.keys(store).length > maintenance.maxEntries) { const beforeCount = Object.keys(store).length; const pruned = pruneStaleEntries(store, maintenance.pruneAfterMs, { log: false }); diff --git a/src/config/sessions/store.pruning.integration.test.ts b/src/config/sessions/store.pruning.integration.test.ts index e3714098aef..3cee16a963c 100644 --- a/src/config/sessions/store.pruning.integration.test.ts +++ b/src/config/sessions/store.pruning.integration.test.ts @@ -287,6 +287,55 @@ describe("Integration: saveSessionStore with pruning", () => { expect(loaded.newest).toBeDefined(); }); + it("loadSessionStore honors configured maxEntries without an explicit override", async () => { + mockLoadConfig.mockReturnValue({ + session: { + maintenance: { + mode: "enforce", + pruneAfter: "365d", + maxEntries: 1000, + rotateBytes: 10_485_760, + }, + }, + }); + + const now = Date.now(); + const store = Object.fromEntries( + Array.from({ length: 501 }, (_, index) => [`session-${index}`, makeEntry(now - index)]), + ); + await fs.writeFile(storePath, JSON.stringify(store), "utf-8"); + + const loaded = loadSessionStore(storePath, { skipCache: true }); + + expect(Object.keys(loaded)).toHaveLength(501); + }); + + it("loadSessionStore honors configured warn mode without an explicit override", async () => { + mockLoadConfig.mockReturnValue({ + session: { + maintenance: { + mode: "warn", + pruneAfter: "365d", + maxEntries: 1, + rotateBytes: 10_485_760, + }, + }, + }); + + const now = Date.now(); + const store: Record = { + oldest: makeEntry(now - DAY_MS), + newest: makeEntry(now), + }; + await fs.writeFile(storePath, JSON.stringify(store), "utf-8"); + + const loaded = loadSessionStore(storePath, { skipCache: true }); + + expect(Object.keys(loaded)).toHaveLength(2); + expect(loaded.oldest).toBeDefined(); + expect(loaded.newest).toBeDefined(); + }); + it("archives transcript files for entries evicted by maxEntries capping", async () => { applyCappedMaintenanceConfig(mockLoadConfig);