Gateway: prefer newest session entries in merge (#1823)

This commit is contained in:
Shadow
2026-01-25 22:40:00 -06:00
parent 49ef62255e
commit 34ce004151
2 changed files with 38 additions and 11 deletions

View File

@@ -18,6 +18,7 @@ Status: unreleased.
- Docs: credit both contributors for Control UI refresh. (#1852) Thanks @EnzeD. - Docs: credit both contributors for Control UI refresh. (#1852) Thanks @EnzeD.
- Onboarding: add Venice API key to non-interactive flow. (#1893) Thanks @jonisjongithub. - Onboarding: add Venice API key to non-interactive flow. (#1893) Thanks @jonisjongithub.
- Tlon: format thread reply IDs as @ud. (#1837) Thanks @wca4a. - Tlon: format thread reply IDs as @ud. (#1837) Thanks @wca4a.
- Gateway: prefer newest session metadata when combining stores. (#1823) Thanks @emanuelst.
- CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi. - CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi.
- macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn. - macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn.
- Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev. - Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev.

View File

@@ -381,6 +381,31 @@ export function resolveGatewaySessionStoreTarget(params: { cfg: ClawdbotConfig;
}; };
} }
// Merge with existing entry based on latest timestamp to ensure data consistency and avoid overwriting with less complete data.
function mergeSessionEntryIntoCombined(params: {
combined: Record<string, SessionEntry>;
entry: SessionEntry;
agentId: string;
canonicalKey: string;
}) {
const { combined, entry, agentId, canonicalKey } = params;
const existing = combined[canonicalKey];
if (existing && (existing.updatedAt ?? 0) > (entry.updatedAt ?? 0)) {
combined[canonicalKey] = {
...entry,
...existing,
spawnedBy: canonicalizeSpawnedByForAgent(agentId, existing.spawnedBy ?? entry.spawnedBy),
};
} else {
combined[canonicalKey] = {
...existing,
...entry,
spawnedBy: canonicalizeSpawnedByForAgent(agentId, entry.spawnedBy ?? existing?.spawnedBy),
};
}
}
export function loadCombinedSessionStoreForGateway(cfg: ClawdbotConfig): { export function loadCombinedSessionStoreForGateway(cfg: ClawdbotConfig): {
storePath: string; storePath: string;
store: Record<string, SessionEntry>; store: Record<string, SessionEntry>;
@@ -393,10 +418,12 @@ export function loadCombinedSessionStoreForGateway(cfg: ClawdbotConfig): {
const combined: Record<string, SessionEntry> = {}; const combined: Record<string, SessionEntry> = {};
for (const [key, entry] of Object.entries(store)) { for (const [key, entry] of Object.entries(store)) {
const canonicalKey = canonicalizeSessionKeyForAgent(defaultAgentId, key); const canonicalKey = canonicalizeSessionKeyForAgent(defaultAgentId, key);
combined[canonicalKey] = { mergeSessionEntryIntoCombined({
...entry, combined,
spawnedBy: canonicalizeSpawnedByForAgent(defaultAgentId, entry.spawnedBy), entry,
}; agentId: defaultAgentId,
canonicalKey,
});
} }
return { storePath, store: combined }; return { storePath, store: combined };
} }
@@ -408,13 +435,12 @@ export function loadCombinedSessionStoreForGateway(cfg: ClawdbotConfig): {
const store = loadSessionStore(storePath); const store = loadSessionStore(storePath);
for (const [key, entry] of Object.entries(store)) { for (const [key, entry] of Object.entries(store)) {
const canonicalKey = canonicalizeSessionKeyForAgent(agentId, key); const canonicalKey = canonicalizeSessionKeyForAgent(agentId, key);
// Merge with existing entry if present (avoid overwriting with less complete data) mergeSessionEntryIntoCombined({
const existing = combined[canonicalKey]; combined,
combined[canonicalKey] = { entry,
...existing, agentId,
...entry, canonicalKey,
spawnedBy: canonicalizeSpawnedByForAgent(agentId, entry.spawnedBy ?? existing?.spawnedBy), });
};
} }
} }