import crypto from "node:crypto"; import type { MsgContext } from "../../auto-reply/templating.js"; import { normalizeThinkLevel, normalizeVerboseLevel, type ThinkLevel, type VerboseLevel, } from "../../auto-reply/thinking.js"; import type { ClawdbotConfig } from "../../config/config.js"; import { DEFAULT_IDLE_MINUTES, loadSessionStore, resolveAgentIdFromSessionKey, resolveSessionKey, resolveStorePath, type SessionEntry, } from "../../config/sessions.js"; import { normalizeMainKey } from "../../routing/session-key.js"; export type SessionResolution = { sessionId: string; sessionKey?: string; sessionEntry?: SessionEntry; sessionStore?: Record; storePath: string; isNewSession: boolean; persistedThinking?: ThinkLevel; persistedVerbose?: VerboseLevel; }; export function resolveSession(opts: { cfg: ClawdbotConfig; to?: string; sessionId?: string; sessionKey?: string; }): SessionResolution { const sessionCfg = opts.cfg.session; const scope = sessionCfg?.scope ?? "per-sender"; const mainKey = normalizeMainKey(sessionCfg?.mainKey); const idleMinutes = Math.max(sessionCfg?.idleMinutes ?? DEFAULT_IDLE_MINUTES, 1); const idleMs = idleMinutes * 60_000; const explicitSessionKey = opts.sessionKey?.trim(); const storeAgentId = resolveAgentIdFromSessionKey(explicitSessionKey); const storePath = resolveStorePath(sessionCfg?.store, { agentId: storeAgentId, }); const sessionStore = loadSessionStore(storePath); const now = Date.now(); const ctx: MsgContext | undefined = opts.to?.trim() ? { From: opts.to } : undefined; let sessionKey: string | undefined = explicitSessionKey ?? (ctx ? resolveSessionKey(scope, ctx, mainKey) : undefined); let sessionEntry = sessionKey ? sessionStore[sessionKey] : undefined; // If a session id was provided, prefer to re-use its entry (by id) even when no key was derived. if ( !explicitSessionKey && opts.sessionId && (!sessionEntry || sessionEntry.sessionId !== opts.sessionId) ) { const foundKey = Object.keys(sessionStore).find( (key) => sessionStore[key]?.sessionId === opts.sessionId, ); if (foundKey) { sessionKey = sessionKey ?? foundKey; sessionEntry = sessionStore[foundKey]; } } const fresh = sessionEntry && sessionEntry.updatedAt >= now - idleMs; const sessionId = opts.sessionId?.trim() || (fresh ? sessionEntry?.sessionId : undefined) || crypto.randomUUID(); const isNewSession = !fresh && !opts.sessionId; const persistedThinking = fresh && sessionEntry?.thinkingLevel ? normalizeThinkLevel(sessionEntry.thinkingLevel) : undefined; const persistedVerbose = fresh && sessionEntry?.verboseLevel ? normalizeVerboseLevel(sessionEntry.verboseLevel) : undefined; return { sessionId, sessionKey, sessionEntry, sessionStore, storePath, isNewSession, persistedThinking, persistedVerbose, }; }