mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-21 06:02:13 +00:00
fix: prefer freshest duplicate rows in session loads
This commit is contained in:
@@ -337,6 +337,43 @@ describe("gateway session utils", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("loadSessionEntry prefers the freshest duplicate row for a logical key", async () => {
|
||||
clearConfigCache();
|
||||
try {
|
||||
await withStateDirEnv("session-utils-load-entry-freshest-", async ({ stateDir }) => {
|
||||
const sessionsDir = path.join(stateDir, "agents", "main", "sessions");
|
||||
fs.mkdirSync(sessionsDir, { recursive: true });
|
||||
const storePath = path.join(sessionsDir, "sessions.json");
|
||||
fs.writeFileSync(
|
||||
storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
"agent:main:main": { sessionId: "sess-stale", updatedAt: 1 },
|
||||
"agent:main:MAIN": { sessionId: "sess-fresh", updatedAt: 2 },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
await writeConfigFile({
|
||||
session: {
|
||||
mainKey: "main",
|
||||
store: path.join(stateDir, "agents", "{agentId}", "sessions", "sessions.json"),
|
||||
},
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
});
|
||||
clearConfigCache();
|
||||
|
||||
const loaded = loadSessionEntry("agent:main:main");
|
||||
|
||||
expect(loaded.entry?.sessionId).toBe("sess-fresh");
|
||||
});
|
||||
} finally {
|
||||
clearConfigCache();
|
||||
}
|
||||
});
|
||||
|
||||
test("pruneLegacyStoreKeys removes alias and case-variant ghost keys", () => {
|
||||
const store: Record<string, unknown> = {
|
||||
"agent:ops:work": { sessionId: "canonical", updatedAt: 3 },
|
||||
|
||||
@@ -357,30 +357,46 @@ export function loadSessionEntry(sessionKey: string) {
|
||||
const cfg = loadConfig();
|
||||
const canonicalKey = resolveSessionStoreKey({ cfg, sessionKey });
|
||||
const agentId = resolveSessionStoreAgentId(cfg, canonicalKey);
|
||||
const { storePath, store, match } = resolveGatewaySessionStoreLookup({
|
||||
const { storePath, store } = resolveGatewaySessionStoreLookup({
|
||||
cfg,
|
||||
key: sessionKey.trim(),
|
||||
canonicalKey,
|
||||
agentId,
|
||||
});
|
||||
const legacyKey = match?.key !== canonicalKey ? match?.key : undefined;
|
||||
return { cfg, storePath, store, entry: match?.entry, canonicalKey, legacyKey };
|
||||
const target = resolveGatewaySessionStoreTarget({
|
||||
cfg,
|
||||
key: sessionKey.trim(),
|
||||
store,
|
||||
});
|
||||
const freshestMatch = resolveFreshestSessionStoreMatchFromStoreKeys(store, target.storeKeys);
|
||||
const legacyKey = freshestMatch?.key !== canonicalKey ? freshestMatch?.key : undefined;
|
||||
return { cfg, storePath, store, entry: freshestMatch?.entry, canonicalKey, legacyKey };
|
||||
}
|
||||
|
||||
export function resolveFreshestSessionEntryFromStoreKeys(
|
||||
export function resolveFreshestSessionStoreMatchFromStoreKeys(
|
||||
store: Record<string, SessionEntry>,
|
||||
storeKeys: string[],
|
||||
): SessionEntry | undefined {
|
||||
): { key: string; entry: SessionEntry } | undefined {
|
||||
const matches = storeKeys
|
||||
.map((key) => store[key])
|
||||
.filter((entry): entry is SessionEntry => Boolean(entry));
|
||||
.map((key) => {
|
||||
const entry = store[key];
|
||||
return entry ? { key, entry } : undefined;
|
||||
})
|
||||
.filter((match): match is { key: string; entry: SessionEntry } => match !== undefined);
|
||||
if (matches.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (matches.length === 1) {
|
||||
return matches[0];
|
||||
}
|
||||
return [...matches].toSorted((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0))[0];
|
||||
return [...matches].toSorted((a, b) => (b.entry.updatedAt ?? 0) - (a.entry.updatedAt ?? 0))[0];
|
||||
}
|
||||
|
||||
export function resolveFreshestSessionEntryFromStoreKeys(
|
||||
store: Record<string, SessionEntry>,
|
||||
storeKeys: string[],
|
||||
): SessionEntry | undefined {
|
||||
return resolveFreshestSessionStoreMatchFromStoreKeys(store, storeKeys)?.entry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user