fix(gateway): normalize session key to canonical form before store writes

Ensure 'main' alias is always stored as 'agent:main:main' to prevent
duplicate entries. Also update loadSessionEntry to check both forms
when looking up entries.

Fixes duplicate main sessions in session store.
This commit is contained in:
user
2026-01-11 07:06:25 +00:00
parent f34d7e0fe0
commit 029db06477
4 changed files with 68 additions and 15 deletions

View File

@@ -24,6 +24,7 @@ import { buildConfigSchema } from "../config/schema.js";
import {
loadSessionStore,
mergeSessionEntry,
resolveAgentMainSessionKey,
resolveMainSessionKeyFromConfig,
type SessionEntry,
saveSessionStore,
@@ -34,7 +35,10 @@ import {
setVoiceWakeTriggers,
} from "../infra/voicewake.js";
import { clearCommandLane } from "../process/command-queue.js";
import { normalizeMainKey } from "../routing/session-key.js";
import {
normalizeMainKey,
resolveAgentIdFromSessionKey,
} from "../routing/session-key.js";
import { defaultRuntime } from "../runtime.js";
import {
abortChatRunById,
@@ -917,8 +921,14 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) {
clientRunId,
});
// Normalize short main key alias to canonical form before store write
const agentId = resolveAgentIdFromSessionKey(p.sessionKey);
const mainSessionKey = resolveAgentMainSessionKey({ cfg, agentId });
const rawMainKey = normalizeMainKey(cfg.session?.mainKey);
const storeKey =
p.sessionKey === rawMainKey ? mainSessionKey : p.sessionKey;
if (store) {
store[p.sessionKey] = sessionEntry;
store[storeKey] = sessionEntry;
if (storePath) {
await saveSessionStore(storePath, store);
}
@@ -1031,12 +1041,18 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) {
if (text.length > 20_000) return;
const sessionKeyRaw =
typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : "";
const mainKey = normalizeMainKey(loadConfig().session?.mainKey);
const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : mainKey;
const cfg = loadConfig();
const rawMainKey = normalizeMainKey(cfg.session?.mainKey);
const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : rawMainKey;
const { storePath, store, entry } = loadSessionEntry(sessionKey);
// Normalize short main key alias to canonical form before store write
const agentId = resolveAgentIdFromSessionKey(sessionKey);
const mainSessionKey = resolveAgentMainSessionKey({ cfg, agentId });
const storeKey =
sessionKey === rawMainKey ? mainSessionKey : sessionKey;
const now = Date.now();
const sessionId = entry?.sessionId ?? randomUUID();
store[sessionKey] = {
store[storeKey] = {
sessionId,
updatedAt: now,
thinkingLevel: entry?.thinkingLevel,
@@ -1118,9 +1134,19 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) {
const sessionKey =
sessionKeyRaw.length > 0 ? sessionKeyRaw : `node-${nodeId}`;
const { storePath, store, entry } = loadSessionEntry(sessionKey);
// Normalize short main key alias to canonical form before store write
const nodeCfg = loadConfig();
const nodeAgentId = resolveAgentIdFromSessionKey(sessionKey);
const nodeMainSessionKey = resolveAgentMainSessionKey({
cfg: nodeCfg,
agentId: nodeAgentId,
});
const nodeRawMainKey = normalizeMainKey(nodeCfg.session?.mainKey);
const nodeStoreKey =
sessionKey === nodeRawMainKey ? nodeMainSessionKey : sessionKey;
const now = Date.now();
const sessionId = entry?.sessionId ?? randomUUID();
store[sessionKey] = {
store[nodeStoreKey] = {
sessionId,
updatedAt: now,
thinkingLevel: entry?.thinkingLevel,

View File

@@ -192,12 +192,6 @@ export const agentHandlers: GatewayRequestHandlers = {
);
return;
}
if (store) {
store[requestedSessionKey] = nextEntry;
if (storePath) {
await saveSessionStore(storePath, store);
}
}
resolvedSessionId = sessionId;
const agentId = resolveAgentIdFromSessionKey(requestedSessionKey);
const mainSessionKey = resolveAgentMainSessionKey({
@@ -205,6 +199,15 @@ export const agentHandlers: GatewayRequestHandlers = {
agentId,
});
const rawMainKey = normalizeMainKey(cfg.session?.mainKey);
// Normalize short main key alias to canonical form before store write
const storeKey =
requestedSessionKey === rawMainKey ? mainSessionKey : requestedSessionKey;
if (store) {
store[storeKey] = nextEntry;
if (storePath) {
await saveSessionStore(storePath, store);
}
}
if (
requestedSessionKey === mainSessionKey ||
requestedSessionKey === rawMainKey

View File

@@ -3,7 +3,15 @@ import { randomUUID } from "node:crypto";
import { resolveThinkingDefault } from "../../agents/model-selection.js";
import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
import { agentCommand } from "../../commands/agent.js";
import { mergeSessionEntry, saveSessionStore } from "../../config/sessions.js";
import {
mergeSessionEntry,
resolveAgentMainSessionKey,
saveSessionStore,
} from "../../config/sessions.js";
import {
normalizeMainKey,
resolveAgentIdFromSessionKey,
} from "../../routing/session-key.js";
import { registerAgentRunContext } from "../../infra/agent-events.js";
import { defaultRuntime } from "../../runtime.js";
import { resolveSendPolicy } from "../../sessions/send-policy.js";
@@ -305,8 +313,14 @@ export const chatHandlers: GatewayRequestHandlers = {
clientRunId,
});
// Normalize short main key alias to canonical form before store write
const agentId = resolveAgentIdFromSessionKey(p.sessionKey);
const mainSessionKey = resolveAgentMainSessionKey({ cfg, agentId });
const rawMainKey = normalizeMainKey(cfg.session?.mainKey);
const storeKey =
p.sessionKey === rawMainKey ? mainSessionKey : p.sessionKey;
if (store) {
store[p.sessionKey] = sessionEntry;
store[storeKey] = sessionEntry;
if (storePath) {
await saveSessionStore(storePath, store);
}

View File

@@ -15,6 +15,7 @@ import {
buildGroupDisplayName,
loadSessionStore,
resolveAgentIdFromSessionKey,
resolveAgentMainSessionKey,
resolveSessionTranscriptPath,
resolveStorePath,
type SessionEntry,
@@ -172,7 +173,16 @@ export function loadSessionEntry(sessionKey: string) {
const store = loadSessionStore(storePath);
const parsed = parseAgentSessionKey(sessionKey);
const legacyKey = parsed?.rest;
const entry = store[sessionKey] ?? (legacyKey ? store[legacyKey] : undefined);
// Also try the canonical key if sessionKey is the short mainKey alias
const rawMainKey = normalizeMainKey(sessionCfg?.mainKey);
const canonicalKey =
sessionKey === rawMainKey
? resolveAgentMainSessionKey({ cfg, agentId })
: undefined;
const entry =
store[sessionKey] ??
(legacyKey ? store[legacyKey] : undefined) ??
(canonicalKey ? store[canonicalKey] : undefined);
return { cfg, storePath, store, entry };
}