From 6c7a27c01087c6345d0ff0bdb5d3fee994608f9a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 9 Jan 2026 22:30:10 +0100 Subject: [PATCH] refactor: normalize main session key handling --- src/agents/sandbox.ts | 6 +++--- src/agents/tools/sessions-helpers.ts | 3 ++- src/auto-reply/reply.ts | 3 ++- src/auto-reply/reply/session.ts | 3 ++- src/commands/agent-via-gateway.ts | 3 ++- src/commands/agent.ts | 3 ++- src/config/sessions.ts | 12 ++++-------- src/gateway/server-bridge.ts | 4 ++-- src/gateway/server-methods/agent.ts | 3 ++- src/gateway/session-utils.ts | 5 ++--- src/routing/session-key.ts | 8 ++++++-- src/slack/monitor.ts | 7 +++++-- src/tui/tui.ts | 5 +++-- src/web/auto-reply.ts | 5 +++-- 14 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/agents/sandbox.ts b/src/agents/sandbox.ts index e367df51f..0f1477a0b 100644 --- a/src/agents/sandbox.ts +++ b/src/agents/sandbox.ts @@ -19,7 +19,7 @@ import { loadConfig, STATE_DIR_CLAWDBOT, } from "../config/config.js"; -import { normalizeAgentId } from "../routing/session-key.js"; +import { normalizeAgentId, normalizeMainKey } from "../routing/session-key.js"; import { defaultRuntime } from "../runtime.js"; import { resolveUserPath } from "../utils.js"; import { @@ -1045,7 +1045,7 @@ export async function resolveSandboxContext(params: { if (!rawSessionKey) return null; const agentId = resolveAgentIdFromSessionKey(rawSessionKey); const cfg = resolveSandboxConfigForAgent(params.config, agentId); - const mainKey = params.config?.session?.mainKey?.trim() || "main"; + const mainKey = normalizeMainKey(params.config?.session?.mainKey); if (!shouldSandboxSession(cfg, rawSessionKey, mainKey)) return null; await maybePruneSandboxes(cfg); @@ -1121,7 +1121,7 @@ export async function ensureSandboxWorkspaceForSession(params: { if (!rawSessionKey) return null; const agentId = resolveAgentIdFromSessionKey(rawSessionKey); const cfg = resolveSandboxConfigForAgent(params.config, agentId); - const mainKey = params.config?.session?.mainKey?.trim() || "main"; + const mainKey = normalizeMainKey(params.config?.session?.mainKey); if (!shouldSandboxSession(cfg, rawSessionKey, mainKey)) return null; const agentWorkspaceDir = resolveUserPath( diff --git a/src/agents/tools/sessions-helpers.ts b/src/agents/tools/sessions-helpers.ts index c1dbc6f95..a3f862ea5 100644 --- a/src/agents/tools/sessions-helpers.ts +++ b/src/agents/tools/sessions-helpers.ts @@ -1,4 +1,5 @@ import type { ClawdbotConfig } from "../../config/config.js"; +import { normalizeMainKey } from "../../routing/session-key.js"; export type SessionKind = "main" | "group" | "cron" | "hook" | "node" | "other"; @@ -8,7 +9,7 @@ function normalizeKey(value?: string) { } export function resolveMainSessionAlias(cfg: ClawdbotConfig) { - const mainKey = normalizeKey(cfg.session?.mainKey) ?? "main"; + const mainKey = normalizeMainKey(cfg.session?.mainKey); const scope = cfg.session?.scope ?? "per-sender"; const alias = scope === "global" ? "global" : mainKey; return { mainKey, alias, scope }; diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index d762953d1..b5118f1db 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -29,6 +29,7 @@ import { import { resolveSessionFilePath } from "../config/sessions.js"; import { logVerbose } from "../globals.js"; import { clearCommandLane, getQueueSize } from "../process/command-queue.js"; +import { normalizeMainKey } from "../routing/session-key.js"; import { defaultRuntime } from "../runtime.js"; import { resolveCommandAuthorization } from "./command-auth.js"; import { hasControlCommand } from "./command-detection.js"; @@ -780,7 +781,7 @@ export async function getReplyFromConfig( const isGroupSession = sessionEntry?.chatType === "group" || sessionEntry?.chatType === "room"; const isMainSession = - !isGroupSession && sessionKey === (sessionCfg?.mainKey ?? "main"); + !isGroupSession && sessionKey === normalizeMainKey(sessionCfg?.mainKey); prefixedBodyBase = await prependSystemEvents({ cfg, sessionKey, diff --git a/src/auto-reply/reply/session.ts b/src/auto-reply/reply/session.ts index 199afe7f6..94a6b8c60 100644 --- a/src/auto-reply/reply/session.ts +++ b/src/auto-reply/reply/session.ts @@ -23,6 +23,7 @@ import { type SessionScope, saveSessionStore, } from "../../config/sessions.js"; +import { normalizeMainKey } from "../../routing/session-key.js"; import { resolveCommandAuthorization } from "../command-auth.js"; import type { MsgContext, TemplateContext } from "../templating.js"; import { stripMentions, stripStructuralPrefixes } from "./mentions.js"; @@ -90,7 +91,7 @@ export async function initSessionState(params: { }): Promise { const { ctx, cfg, commandAuthorized } = params; const sessionCfg = cfg.session; - const mainKey = sessionCfg?.mainKey ?? "main"; + const mainKey = normalizeMainKey(sessionCfg?.mainKey); const agentId = resolveAgentIdFromSessionKey(ctx.SessionKey); const resetTriggers = sessionCfg?.resetTriggers?.length ? sessionCfg.resetTriggers diff --git a/src/commands/agent-via-gateway.ts b/src/commands/agent-via-gateway.ts index 2db5b43c7..d0c6857f5 100644 --- a/src/commands/agent-via-gateway.ts +++ b/src/commands/agent-via-gateway.ts @@ -7,6 +7,7 @@ import { resolveStorePath, } from "../config/sessions.js"; import { callGateway, randomIdempotencyKey } from "../gateway/call.js"; +import { normalizeMainKey } from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { normalizeMessageProvider } from "../utils/message-provider.js"; import { agentCommand } from "./agent.js"; @@ -51,7 +52,7 @@ function resolveGatewaySessionKey(opts: { }): string | undefined { const sessionCfg = opts.cfg.session; const scope = sessionCfg?.scope ?? "per-sender"; - const mainKey = sessionCfg?.mainKey ?? "main"; + const mainKey = normalizeMainKey(sessionCfg?.mainKey); const storePath = resolveStorePath(sessionCfg?.store); const store = loadSessionStore(storePath); diff --git a/src/commands/agent.ts b/src/commands/agent.ts index 2941fa1cb..6ccdeaa4b 100644 --- a/src/commands/agent.ts +++ b/src/commands/agent.ts @@ -56,6 +56,7 @@ import { normalizeOutboundPayloadsForJson, } from "../infra/outbound/payloads.js"; import { resolveOutboundTarget } from "../infra/outbound/targets.js"; +import { normalizeMainKey } from "../routing/session-key.js"; import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; import { resolveSendPolicy } from "../sessions/send-policy.js"; import { @@ -104,7 +105,7 @@ function resolveSession(opts: { }): SessionResolution { const sessionCfg = opts.cfg.session; const scope = sessionCfg?.scope ?? "per-sender"; - const mainKey = sessionCfg?.mainKey ?? "main"; + const mainKey = normalizeMainKey(sessionCfg?.mainKey); const idleMinutes = Math.max( sessionCfg?.idleMinutes ?? DEFAULT_IDLE_MINUTES, 1, diff --git a/src/config/sessions.ts b/src/config/sessions.ts index 2ffb655ae..052658afb 100644 --- a/src/config/sessions.ts +++ b/src/config/sessions.ts @@ -9,8 +9,8 @@ import type { MsgContext } from "../auto-reply/templating.js"; import { buildAgentMainSessionKey, DEFAULT_AGENT_ID, - DEFAULT_MAIN_KEY, normalizeAgentId, + normalizeMainKey, resolveAgentIdFromSessionKey, } from "../routing/session-key.js"; import { normalizeE164 } from "../utils.js"; @@ -228,8 +228,7 @@ export function resolveMainSessionKey(cfg?: { agents[0]?.id ?? DEFAULT_AGENT_ID; const agentId = normalizeAgentId(defaultAgentId); - const mainKey = - (cfg?.session?.mainKey ?? DEFAULT_MAIN_KEY).trim() || DEFAULT_MAIN_KEY; + const mainKey = normalizeMainKey(cfg?.session?.mainKey); return buildAgentMainSessionKey({ agentId, mainKey }); } @@ -243,9 +242,7 @@ export function resolveAgentMainSessionKey(params: { cfg?: { session?: { mainKey?: string } }; agentId: string; }): string { - const mainKey = - (params.cfg?.session?.mainKey ?? DEFAULT_MAIN_KEY).trim() || - DEFAULT_MAIN_KEY; + const mainKey = normalizeMainKey(params.cfg?.session?.mainKey); return buildAgentMainSessionKey({ agentId: params.agentId, mainKey }); } @@ -548,8 +545,7 @@ export function resolveSessionKey( if (explicit) return explicit; const raw = deriveSessionKey(scope, ctx); if (scope === "global") return raw; - const canonicalMainKey = - (mainKey ?? DEFAULT_MAIN_KEY).trim() || DEFAULT_MAIN_KEY; + const canonicalMainKey = normalizeMainKey(mainKey); const canonical = buildAgentMainSessionKey({ agentId: DEFAULT_AGENT_ID, mainKey: canonicalMainKey, diff --git a/src/gateway/server-bridge.ts b/src/gateway/server-bridge.ts index b939a3892..bd1753362 100644 --- a/src/gateway/server-bridge.ts +++ b/src/gateway/server-bridge.ts @@ -33,6 +33,7 @@ import { setVoiceWakeTriggers, } from "../infra/voicewake.js"; import { clearCommandLane } from "../process/command-queue.js"; +import { normalizeMainKey } from "../routing/session-key.js"; import { defaultRuntime } from "../runtime.js"; import { buildMessageWithAttachments } from "./chat-attachments.js"; import { @@ -944,8 +945,7 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) { if (text.length > 20_000) return; const sessionKeyRaw = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : ""; - const mainKey = - (loadConfig().session?.mainKey ?? "main").trim() || "main"; + const mainKey = normalizeMainKey(loadConfig().session?.mainKey); const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : mainKey; const { storePath, store, entry } = loadSessionEntry(sessionKey); const now = Date.now(); diff --git a/src/gateway/server-methods/agent.ts b/src/gateway/server-methods/agent.ts index a96e67b3a..124c59b72 100644 --- a/src/gateway/server-methods/agent.ts +++ b/src/gateway/server-methods/agent.ts @@ -9,6 +9,7 @@ import { saveSessionStore, } from "../../config/sessions.js"; import { registerAgentRunContext } from "../../infra/agent-events.js"; +import { normalizeMainKey } from "../../routing/session-key.js"; import { defaultRuntime } from "../../runtime.js"; import { resolveSendPolicy } from "../../sessions/send-policy.js"; import { normalizeMessageProvider } from "../../utils/message-provider.js"; @@ -129,7 +130,7 @@ export const agentHandlers: GatewayRequestHandlers = { cfg, agentId, }); - const rawMainKey = (cfg.session?.mainKey ?? "main").trim() || "main"; + const rawMainKey = normalizeMainKey(cfg.session?.mainKey); if ( requestedSessionKey === mainSessionKey || requestedSessionKey === rawMainKey diff --git a/src/gateway/session-utils.ts b/src/gateway/session-utils.ts index 644fc2f93..9ddc90b8d 100644 --- a/src/gateway/session-utils.ts +++ b/src/gateway/session-utils.ts @@ -21,8 +21,8 @@ import { type SessionScope, } from "../config/sessions.js"; import { - DEFAULT_MAIN_KEY, normalizeAgentId, + normalizeMainKey, parseAgentSessionKey, } from "../routing/session-key.js"; @@ -255,8 +255,7 @@ export function listAgentsForGateway(cfg: ClawdbotConfig): { agents: GatewayAgentRow[]; } { const defaultId = normalizeAgentId(resolveDefaultAgentId(cfg)); - const mainKey = - (cfg.session?.mainKey ?? DEFAULT_MAIN_KEY).trim() || DEFAULT_MAIN_KEY; + const mainKey = normalizeMainKey(cfg.session?.mainKey); const scope = cfg.session?.scope ?? "per-sender"; const configuredById = new Map(); for (const entry of cfg.agents?.list ?? []) { diff --git a/src/routing/session-key.ts b/src/routing/session-key.ts index 1506b1f8a..89d0ae8df 100644 --- a/src/routing/session-key.ts +++ b/src/routing/session-key.ts @@ -6,6 +6,11 @@ function normalizeToken(value: string | undefined | null): string { return (value ?? "").trim().toLowerCase(); } +export function normalizeMainKey(value: string | undefined | null): string { + const trimmed = (value ?? "").trim(); + return trimmed ? trimmed : DEFAULT_MAIN_KEY; +} + export type ParsedAgentSessionKey = { agentId: string; rest: string; @@ -77,8 +82,7 @@ export function buildAgentMainSessionKey(params: { mainKey?: string | undefined; }): string { const agentId = normalizeAgentId(params.agentId); - const mainKey = - (params.mainKey ?? DEFAULT_MAIN_KEY).trim() || DEFAULT_MAIN_KEY; + const mainKey = normalizeMainKey(params.mainKey); return `agent:${agentId}:${mainKey}`; } diff --git a/src/slack/monitor.ts b/src/slack/monitor.ts index a83d1452f..11be09501 100644 --- a/src/slack/monitor.ts +++ b/src/slack/monitor.ts @@ -53,7 +53,10 @@ import { upsertProviderPairingRequest, } from "../pairing/pairing-store.js"; import { resolveAgentRoute } from "../routing/resolve-route.js"; -import { resolveThreadSessionKeys } from "../routing/session-key.js"; +import { + normalizeMainKey, + resolveThreadSessionKeys, +} from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { resolveSlackAccount } from "./accounts.js"; import { reactSlackMessage } from "./actions.js"; @@ -427,7 +430,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { }); const sessionCfg = cfg.session; const sessionScope = sessionCfg?.scope ?? "per-sender"; - const mainKey = (sessionCfg?.mainKey ?? "main").trim() || "main"; + const mainKey = normalizeMainKey(sessionCfg?.mainKey); const resolveSlackSystemEventSessionKey = (params: { channelId?: string | null; diff --git a/src/tui/tui.ts b/src/tui/tui.ts index bda0d8bd5..1852dba8b 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -12,6 +12,7 @@ import { loadConfig } from "../config/config.js"; import { buildAgentMainSessionKey, normalizeAgentId, + normalizeMainKey, parseAgentSessionKey, } from "../routing/session-key.js"; import { formatTokenCount } from "../utils/usage-format.js"; @@ -132,7 +133,7 @@ export async function runTui(opts: TuiOptions) { const initialSessionInput = (opts.session ?? "").trim(); let sessionScope: SessionScope = (config.session?.scope ?? "per-sender") as SessionScope; - let sessionMainKey = (config.session?.mainKey ?? "main").trim() || "main"; + let sessionMainKey = normalizeMainKey(config.session?.mainKey); let agentDefaultId = resolveDefaultAgentId(config); let currentAgentId = agentDefaultId; let agents: AgentSummary[] = []; @@ -263,7 +264,7 @@ export async function runTui(opts: TuiOptions) { const applyAgentsResult = (result: GatewayAgentsList) => { agentDefaultId = normalizeAgentId(result.defaultId); - sessionMainKey = result.mainKey.trim() || sessionMainKey; + sessionMainKey = normalizeMainKey(result.mainKey); sessionScope = result.scope ?? sessionScope; agents = result.agents.map((agent) => ({ id: normalizeAgentId(agent.id), diff --git a/src/web/auto-reply.ts b/src/web/auto-reply.ts index cd8e54f66..28b1805aa 100644 --- a/src/web/auto-reply.ts +++ b/src/web/auto-reply.ts @@ -57,6 +57,7 @@ import { buildGroupHistoryKey, DEFAULT_MAIN_KEY, normalizeAgentId, + normalizeMainKey, } from "../routing/session-key.js"; import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; import { isSelfChatMode, jidToE164, normalizeE164 } from "../utils.js"; @@ -291,7 +292,7 @@ export async function runWebHeartbeatOnce(opts: { const cfg = cfgOverride ?? loadConfig(); const sessionCfg = cfg.session; const sessionScope = sessionCfg?.scope ?? "per-sender"; - const mainKey = sessionCfg?.mainKey; + const mainKey = normalizeMainKey(sessionCfg?.mainKey); const sessionKey = resolveSessionKey(sessionScope, { From: to }, mainKey); if (sessionId) { const storePath = resolveStorePath(cfg.session?.store); @@ -539,7 +540,7 @@ function getSessionSnapshot( const key = resolveSessionKey( scope, { From: from, To: "", Body: "" }, - sessionCfg?.mainKey, + normalizeMainKey(sessionCfg?.mainKey), ); const store = loadSessionStore(resolveStorePath(sessionCfg?.store)); const entry = store[key];