fix: preserve plugin maintenance config compatibility

This commit is contained in:
Josh Lehman
2026-06-24 13:42:39 -07:00
parent 4a7659920c
commit fab0048d7b
6 changed files with 81 additions and 8 deletions
+15
View File
@@ -47,6 +47,12 @@ export type ResolvedSessionMaintenanceConfig = {
highWaterBytes: number | null;
};
export type ResolvedSessionMaintenanceConfigInput = Omit<
ResolvedSessionMaintenanceConfig,
"modelRunPruneAfterMs"
> &
Partial<Pick<ResolvedSessionMaintenanceConfig, "modelRunPruneAfterMs">>;
function resolvePruneAfterMs(maintenance?: SessionMaintenanceConfig): number {
const raw = maintenance?.pruneAfter ?? maintenance?.pruneDays;
const normalized = normalizeStringifiedOptionalString(raw);
@@ -147,6 +153,15 @@ export function resolveMaintenanceConfigFromInput(
};
}
export function normalizeResolvedMaintenanceConfigInput(
maintenance: ResolvedSessionMaintenanceConfigInput,
): ResolvedSessionMaintenanceConfig {
return {
...maintenance,
modelRunPruneAfterMs: maintenance.modelRunPruneAfterMs ?? DEFAULT_MODEL_RUN_PRUNE_AFTER_MS,
};
}
export function resolveSessionEntryMaintenanceHighWater(maxEntries: number): number {
if (!Number.isSafeInteger(maxEntries) || maxEntries <= 0) {
return 1;
+6 -1
View File
@@ -69,6 +69,7 @@ import {
pruneStaleModelRunEntries,
pruneStaleEntries,
type ResolvedSessionMaintenanceConfig,
type ResolvedSessionMaintenanceConfigInput,
type SessionMaintenanceWarning,
} from "./store-maintenance.js";
import { runExclusiveSessionStoreWrite } from "./store-writer.js";
@@ -173,7 +174,11 @@ export {
resolveMaintenanceConfig,
};
export type { SessionMaintenanceApplyReport } from "./store-maintenance-operations.js";
export type { ResolvedSessionMaintenanceConfig, SessionMaintenanceWarning };
export type {
ResolvedSessionMaintenanceConfig,
ResolvedSessionMaintenanceConfigInput,
SessionMaintenanceWarning,
};
type SaveSessionStoreOptions = {
/** Skip pruning, capping, and rotation (e.g. during one-time migrations). */
@@ -165,6 +165,51 @@ describe("session-store-runtime compatibility surface", () => {
expect(getSessionEntry({ sessionKey: staleSessionKey, storePath })).toBeUndefined();
});
it("accepts pre-model-run maintenance configs through entry patches", async () => {
const staleModelRunKey = "agent:main:explicit:model-run-123e4567-e89b-12d3-a456-426614174000";
const activeSessionKey = "agent:main:active";
const now = Date.now();
await saveSessionStore(
storePath,
{
[staleModelRunKey]: {
sessionId: "session-probe",
updatedAt: now - 2 * DAY_MS,
},
[activeSessionKey]: {
sessionId: "session-active",
updatedAt: now,
},
},
{ skipMaintenance: true },
);
const legacyMaintenanceConfig = {
mode: "enforce" as const,
pruneAfterMs: 7 * DAY_MS,
maxEntries: 500,
resetArchiveRetentionMs: 7 * DAY_MS,
maxDiskBytes: null,
highWaterBytes: null,
};
await expect(
patchSessionEntry({
sessionKey: activeSessionKey,
storePath,
maintenanceConfig: legacyMaintenanceConfig,
update: () => ({ model: "gpt-5.5" }),
}),
).resolves.toMatchObject({
model: "gpt-5.5",
sessionId: "session-active",
});
expect(getSessionEntry({ sessionKey: staleModelRunKey, storePath })).toMatchObject({
sessionId: "session-probe",
});
});
it("keeps deprecated whole-store mutations grouped as one compatibility operation", async () => {
const firstSessionKey = "agent:main:first";
const secondSessionKey = "agent:main:second";
+7 -3
View File
@@ -12,7 +12,8 @@ import {
updateSessionEntry,
} from "../config/sessions/session-accessor.js";
import { loadSessionStore as loadSessionStoreImpl } from "../config/sessions/store-load.js";
import type { ResolvedSessionMaintenanceConfig } from "../config/sessions/store.js";
import { normalizeResolvedMaintenanceConfigInput } from "../config/sessions/store-maintenance.js";
import type { ResolvedSessionMaintenanceConfigInput } from "../config/sessions/store.js";
import type { SessionEntry } from "../config/sessions/types.js";
type SessionStoreReadParams = {
@@ -42,7 +43,7 @@ type SessionStoreEntryPatch = (
type PatchSessionEntryParams = SessionStoreReadParams & {
fallbackEntry?: SessionEntry;
maintenanceConfig?: ResolvedSessionMaintenanceConfig;
maintenanceConfig?: ResolvedSessionMaintenanceConfigInput;
preserveActivity?: boolean;
replaceEntry?: boolean;
update: SessionStoreEntryPatch;
@@ -128,7 +129,10 @@ export async function patchSessionEntry(
): Promise<SessionEntry | null> {
return await patchAccessorSessionEntry(toSessionAccessScope(params), params.update, {
fallbackEntry: params.fallbackEntry,
maintenanceConfig: params.maintenanceConfig,
maintenanceConfig:
params.maintenanceConfig !== undefined
? normalizeResolvedMaintenanceConfigInput(params.maintenanceConfig)
: undefined,
preserveActivity: params.preserveActivity,
replaceEntry: params.replaceEntry,
});
+7 -3
View File
@@ -19,11 +19,12 @@ import {
type SessionAccessScope,
updateSessionEntry,
} from "../../config/sessions/session-accessor.js";
import { normalizeResolvedMaintenanceConfigInput } from "../../config/sessions/store-maintenance.js";
import {
loadSessionStore,
saveSessionStore,
updateSessionStore,
type ResolvedSessionMaintenanceConfig,
type ResolvedSessionMaintenanceConfigInput,
} from "../../config/sessions/store.js";
import type { SessionEntry } from "../../config/sessions/types.js";
import { createLazyRuntimeMethod, createLazyRuntimeModule } from "../../shared/lazy-runtime.js";
@@ -59,7 +60,7 @@ type RuntimeSessionStoreEntryUpdateParams = {
type RuntimeSessionStoreEntryPatchParams = RuntimeSessionStoreReadParams & {
fallbackEntry?: SessionEntry;
maintenanceConfig?: ResolvedSessionMaintenanceConfig;
maintenanceConfig?: ResolvedSessionMaintenanceConfigInput;
preserveActivity?: boolean;
replaceEntry?: boolean;
update: (
@@ -123,7 +124,10 @@ async function patchSessionEntry(
): Promise<SessionEntry | null> {
return await patchAccessorSessionEntry(toSessionAccessScope(params), params.update, {
fallbackEntry: params.fallbackEntry,
maintenanceConfig: params.maintenanceConfig,
maintenanceConfig:
params.maintenanceConfig !== undefined
? normalizeResolvedMaintenanceConfigInput(params.maintenanceConfig)
: undefined,
preserveActivity: params.preserveActivity,
replaceEntry: params.replaceEntry,
});
+1 -1
View File
@@ -75,7 +75,7 @@ type RuntimeSessionStoreEntrySummary = {
};
type RuntimeSessionStoreEntryPatchParams = RuntimeSessionStoreReadParams & {
fallbackEntry?: RuntimeSessionEntry;
maintenanceConfig?: import("../../config/sessions/store.js").ResolvedSessionMaintenanceConfig;
maintenanceConfig?: import("../../config/sessions/store.js").ResolvedSessionMaintenanceConfigInput;
preserveActivity?: boolean;
replaceEntry?: boolean;
update: (