Skip to content

Commit fb40ed9

Browse files
committed
fix(sessions): remove session store rotation
1 parent ad57a6d commit fb40ed9

21 files changed

Lines changed: 183 additions & 236 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
1414
- Cron/Telegram: preserve explicit `:topic:` delivery targets over stale session-derived thread IDs when isolated cron announces to Telegram forum topics. Carries forward #59069; refs #49704 and #43808. Thanks @roytong9.
1515
- CLI/model probes: reject empty or whitespace-only `infer model run --prompt` values before calling local providers or the Gateway, so smoke checks do not spend provider calls on invalid turns. Fixes #73185. Thanks @iot2edge.
1616
- Gateway/media: route text-only `chat.send` image offloads through media-understanding fields so `agents.defaults.imageModel` can describe WebChat attachments instead of leaving only an opaque `media://inbound` marker. Fixes #72968. Thanks @vorajeeah.
17+
- Gateway/sessions: remove automatic oversized `sessions.json` rotation backups, deprecate `session.maintenance.rotateBytes`, and teach `openclaw doctor --fix` to remove the ignored key so hot session writes no longer copy multi-MB stores. Refs #72338. Thanks @midhunmonachan and @DougButdorf.
1718
- Channels/Telegram: fail fast when Telegram rejects the startup `getMe` token probe with 401, so invalid or stale BotFather tokens are reported as token auth failures instead of misleading `deleteWebhook` cleanup failures. Fixes #47674. Thanks @samaedan-arch.
1819
- ACPX: keep generated Codex and Claude ACP wrapper startup paths working when remote or special state filesystems reject chmod, since OpenClaw invokes the wrappers through Node instead of executing them directly. Fixes #73333. Thanks @david-garcia-garcia.
1920
- CLI/onboarding: infer image input for common custom-provider vision model IDs, ask only for unknown models, and keep `--custom-image-input`/`--custom-text-input` overrides so vision-capable proxies do not get saved as text-only configs. Fixes #51869. Thanks @Antsoldier1974.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
4fd357ae137b920586ce5760d461be586f4f9a94e49b73cad1f81110167cd9da config-baseline.json
2-
f874cddd0744be277af58ef14261af7994aba669c642f613be10f92b095998ba config-baseline.core.json
1+
f888e19429506211e4b8b4113594641825d300c0c0a721121092cae2201b721f config-baseline.json
2+
481eb68ecf9538d8f6d9808af1a7416b05a3b5d00080552b955a77dbd90819e3 config-baseline.core.json
33
a9f058ee9616e189dab7fc223e1207a49ae52b8490b8028935c9d0a2b16f81b2 config-baseline.channel.json
44
1f5592bfd141ba1e982ce31763a253c10afb080ab4ea2b6538299b114e29cee1 config-baseline.plugin.json

docs/gateway/config-agents.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,6 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
11531153
mode: "warn", // warn | enforce
11541154
pruneAfter: "30d",
11551155
maxEntries: 500,
1156-
rotateBytes: "10mb",
11571156
resetArchiveRetention: "30d", // duration or false
11581157
maxDiskBytes: "500mb", // optional hard budget
11591158
highWaterBytes: "400mb", // optional cleanup target
@@ -1196,7 +1195,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
11961195
- `mode`: `warn` emits warnings only; `enforce` applies cleanup.
11971196
- `pruneAfter`: age cutoff for stale entries (default `30d`).
11981197
- `maxEntries`: maximum number of entries in `sessions.json` (default `500`). Runtime writes batch cleanup with a small high-water buffer for production-sized caps; `openclaw sessions cleanup --enforce` applies the cap immediately.
1199-
- `rotateBytes`: rotate `sessions.json` when it exceeds this size (default `10mb`).
1198+
- `rotateBytes`: deprecated and ignored; `openclaw doctor --fix` removes it from older configs.
12001199
- `resetArchiveRetention`: retention for `*.reset.<timestamp>` transcript archives. Defaults to `pruneAfter`; set `false` to disable.
12011200
- `maxDiskBytes`: optional sessions-directory disk budget. In `warn` mode it logs warnings; in `enforce` mode it removes oldest artifacts/sessions first.
12021201
- `highWaterBytes`: optional target after budget cleanup. Defaults to `80%` of `maxDiskBytes`.

docs/gateway/configuration-examples.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
163163
mode: "warn",
164164
pruneAfter: "30d",
165165
maxEntries: 500,
166-
rotateBytes: "10mb",
167166
resetArchiveRetention: "30d", // duration or false
168167
maxDiskBytes: "500mb", // optional
169168
highWaterBytes: "400mb", // optional (defaults to 80% of maxDiskBytes)

docs/reference/session-management-compaction.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,14 @@ Session persistence has automatic maintenance controls (`session.maintenance`) f
7575
- `mode`: `warn` (default) or `enforce`
7676
- `pruneAfter`: stale-entry age cutoff (default `30d`)
7777
- `maxEntries`: cap entries in `sessions.json` (default `500`)
78-
- `rotateBytes`: rotate `sessions.json` when oversized (default `10mb`)
7978
- `resetArchiveRetention`: retention for `*.reset.<timestamp>` transcript archives (default: same as `pruneAfter`; `false` disables cleanup)
8079
- `maxDiskBytes`: optional sessions-directory budget
8180
- `highWaterBytes`: optional target after cleanup (default `80%` of `maxDiskBytes`)
8281

8382
Normal Gateway writes batch `maxEntries` cleanup for production-sized caps, so a store may briefly exceed the configured cap before the next high-water cleanup rewrites it back down. `openclaw sessions cleanup --enforce` still applies the configured cap immediately.
8483

84+
OpenClaw no longer creates automatic `sessions.json.bak.*` rotation backups during Gateway writes. The legacy `session.maintenance.rotateBytes` key is ignored and `openclaw doctor --fix` removes it from older configs.
85+
8586
Enforcement order for disk budget cleanup (`mode: "enforce"`):
8687

8788
1. Remove oldest archived, orphan transcript, or orphan trajectory artifacts first.

src/commands/doctor-config-flow.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ const legacyConfigMigrationForTest = vi.hoisted(() => {
156156
}
157157

158158
migrateThreadBinding(next.session, changes, "session");
159+
const sessionMaintenance = asRecord(asRecord(next.session)?.maintenance);
160+
if (sessionMaintenance && "rotateBytes" in sessionMaintenance) {
161+
delete sessionMaintenance.rotateBytes;
162+
changes.push("Removed deprecated session.maintenance.rotateBytes.");
163+
}
159164
const channels = asRecord(next.channels);
160165
for (const [channelId, channelRaw] of Object.entries(channels ?? {})) {
161166
if (channelId === "defaults") {
@@ -333,6 +338,14 @@ vi.mock("../config/legacy.js", () => {
333338
'session.threadBindings.ttlHours is legacy; use session.threadBindings.idleHours. Run "openclaw doctor --fix".',
334339
);
335340
}
341+
const sessionMaintenance = asRecord(asRecord(root.session)?.maintenance);
342+
if (sessionMaintenance && "rotateBytes" in sessionMaintenance) {
343+
addIssue(
344+
issues,
345+
["session", "maintenance"],
346+
'session.maintenance.rotateBytes is deprecated and ignored; run "openclaw doctor --fix" to remove it.',
347+
);
348+
}
336349
const xSearch = asRecord(asRecord(asRecord(root.tools)?.web)?.x_search);
337350
if (xSearch && "apiKey" in xSearch) {
338351
addIssue(
@@ -1563,6 +1576,11 @@ describe("doctor config flow", () => {
15631576
bridge: { bind: "auto" },
15641577
gateway: { auth: { mode: "token", token: "ok", extra: true } },
15651578
agents: { list: [{ id: "pi" }] },
1579+
session: {
1580+
maintenance: {
1581+
rotateBytes: "10mb",
1582+
},
1583+
},
15661584
browser: {
15671585
relayBindHost: "0.0.0.0",
15681586
profiles: {
@@ -2304,6 +2322,9 @@ describe("doctor config flow", () => {
23042322
bind?: string;
23052323
};
23062324
session?: {
2325+
maintenance?: {
2326+
rotateBytes?: unknown;
2327+
};
23072328
threadBindings?: {
23082329
idleHours?: number;
23092330
ttlHours?: number;
@@ -2348,6 +2369,7 @@ describe("doctor config flow", () => {
23482369
every: "30m",
23492370
});
23502371
expect(cfg.gateway?.bind).toBe("lan");
2372+
expect(cfg.session?.maintenance?.rotateBytes).toBeUndefined();
23512373
expect(cfg.session?.threadBindings).toMatchObject({
23522374
idleHours: 24,
23532375
});
@@ -2414,6 +2436,9 @@ describe("doctor config flow", () => {
24142436
},
24152437
},
24162438
session: {
2439+
maintenance: {
2440+
rotateBytes: "10mb",
2441+
},
24172442
threadBindings: {
24182443
ttlHours: 24,
24192444
},
@@ -2454,6 +2479,8 @@ describe("doctor config flow", () => {
24542479
expect(legacyMessages).toContain("does not rewrite this shape automatically");
24552480
expect(legacyMessages).toContain("session.threadBindings.ttlHours");
24562481
expect(legacyMessages).toContain("session.threadBindings.idleHours");
2482+
expect(legacyMessages).toContain("session.maintenance.rotateBytes");
2483+
expect(legacyMessages).toContain("deprecated and ignored");
24572484
expect(legacyMessages).toContain("channels.<id>.threadBindings.ttlHours");
24582485
expect(legacyMessages).toContain("channels.<id>.threadBindings.idleHours");
24592486
expect(legacyMessages).toContain("talk:");

src/commands/doctor/shared/legacy-config-migrate.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,28 @@ function migrateLegacyConfigForTest(raw: unknown): {
1919
: { config: next as OpenClawConfig, changes };
2020
}
2121

22+
describe("legacy session maintenance migrate", () => {
23+
it("removes deprecated session.maintenance.rotateBytes", () => {
24+
const res = migrateLegacyConfigForTest({
25+
session: {
26+
maintenance: {
27+
mode: "enforce",
28+
pruneAfter: "30d",
29+
maxEntries: 500,
30+
rotateBytes: "10mb",
31+
},
32+
},
33+
});
34+
35+
expect(res.config?.session?.maintenance).toEqual({
36+
mode: "enforce",
37+
pruneAfter: "30d",
38+
maxEntries: 500,
39+
});
40+
expect(res.changes).toContain("Removed deprecated session.maintenance.rotateBytes.");
41+
});
42+
});
43+
2244
describe("legacy migrate audio transcription", () => {
2345
it("does not rewrite removed routing.transcribeAudio migrations", () => {
2446
const res = migrateLegacyConfigForTest({
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
defineLegacyConfigMigration,
3+
getRecord,
4+
type LegacyConfigMigrationSpec,
5+
type LegacyConfigRule,
6+
} from "../../../config/legacy.shared.js";
7+
8+
function hasLegacyRotateBytes(value: unknown): boolean {
9+
const maintenance = getRecord(value);
10+
return Boolean(maintenance && Object.prototype.hasOwnProperty.call(maintenance, "rotateBytes"));
11+
}
12+
13+
const LEGACY_SESSION_MAINTENANCE_ROTATE_BYTES_RULE: LegacyConfigRule = {
14+
path: ["session", "maintenance"],
15+
message:
16+
'session.maintenance.rotateBytes is deprecated and ignored; run "openclaw doctor --fix" to remove it.',
17+
match: hasLegacyRotateBytes,
18+
};
19+
20+
export const LEGACY_CONFIG_MIGRATIONS_RUNTIME_SESSION: LegacyConfigMigrationSpec[] = [
21+
defineLegacyConfigMigration({
22+
id: "session.maintenance.rotateBytes",
23+
describe: "Remove deprecated session.maintenance.rotateBytes",
24+
legacyRules: [LEGACY_SESSION_MAINTENANCE_ROTATE_BYTES_RULE],
25+
apply: (raw, changes) => {
26+
const maintenance = getRecord(getRecord(raw.session)?.maintenance);
27+
if (!maintenance || !Object.prototype.hasOwnProperty.call(maintenance, "rotateBytes")) {
28+
return;
29+
}
30+
delete maintenance.rotateBytes;
31+
changes.push("Removed deprecated session.maintenance.rotateBytes.");
32+
},
33+
}),
34+
];

src/commands/doctor/shared/legacy-config-migrations.runtime.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_AGENTS } from "./legacy-config-migrati
33
import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_GATEWAY } from "./legacy-config-migrations.runtime.gateway.js";
44
import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_MCP } from "./legacy-config-migrations.runtime.mcp.js";
55
import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_PROVIDERS } from "./legacy-config-migrations.runtime.providers.js";
6+
import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_SESSION } from "./legacy-config-migrations.runtime.session.js";
67
import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_TTS } from "./legacy-config-migrations.runtime.tts.js";
78

89
export const LEGACY_CONFIG_MIGRATIONS_RUNTIME: LegacyConfigMigrationSpec[] = [
910
...LEGACY_CONFIG_MIGRATIONS_RUNTIME_AGENTS,
1011
...LEGACY_CONFIG_MIGRATIONS_RUNTIME_GATEWAY,
1112
...LEGACY_CONFIG_MIGRATIONS_RUNTIME_MCP,
1213
...LEGACY_CONFIG_MIGRATIONS_RUNTIME_PROVIDERS,
14+
...LEGACY_CONFIG_MIGRATIONS_RUNTIME_SESSION,
1315
...LEGACY_CONFIG_MIGRATIONS_RUNTIME_TTS,
1416
];

src/commands/sessions-cleanup.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ describe("sessionsCleanupCommand", () => {
7373
mode: "warn",
7474
pruneAfterMs: 7 * 24 * 60 * 60 * 1000,
7575
maxEntries: 500,
76-
rotateBytes: 10_485_760,
7776
resetArchiveRetentionMs: 7 * 24 * 60 * 60 * 1000,
7877
maxDiskBytes: null,
7978
highWaterBytes: null,

0 commit comments

Comments
 (0)