import type { ClawdbotConfig } from "../../config/config.js"; import { updateSessionStore, type SessionEntry } from "../../config/sessions.js"; import { normalizeProviderId } from "../model-selection.js"; import { ensureAuthProfileStore, isProfileInCooldown, resolveAuthProfileOrder, } from "../auth-profiles.js"; function isProfileForProvider(params: { provider: string; profileId: string; store: ReturnType; }): boolean { const entry = params.store.profiles[params.profileId]; if (!entry?.provider) return false; return normalizeProviderId(entry.provider) === normalizeProviderId(params.provider); } export async function clearSessionAuthProfileOverride(params: { sessionEntry: SessionEntry; sessionStore: Record; sessionKey: string; storePath?: string; }) { const { sessionEntry, sessionStore, sessionKey, storePath } = params; delete sessionEntry.authProfileOverride; delete sessionEntry.authProfileOverrideSource; delete sessionEntry.authProfileOverrideCompactionCount; sessionEntry.updatedAt = Date.now(); sessionStore[sessionKey] = sessionEntry; if (storePath) { await updateSessionStore(storePath, (store) => { store[sessionKey] = sessionEntry; }); } } export async function resolveSessionAuthProfileOverride(params: { cfg: ClawdbotConfig; provider: string; agentDir: string; sessionEntry?: SessionEntry; sessionStore?: Record; sessionKey?: string; storePath?: string; isNewSession: boolean; }): Promise { const { cfg, provider, agentDir, sessionEntry, sessionStore, sessionKey, storePath, isNewSession, } = params; if (!sessionEntry || !sessionStore || !sessionKey) return sessionEntry?.authProfileOverride; const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false }); const order = resolveAuthProfileOrder({ cfg, store, provider }); let current = sessionEntry.authProfileOverride?.trim(); if (current && !store.profiles[current]) { await clearSessionAuthProfileOverride({ sessionEntry, sessionStore, sessionKey, storePath }); current = undefined; } if (current && !isProfileForProvider({ provider, profileId: current, store })) { await clearSessionAuthProfileOverride({ sessionEntry, sessionStore, sessionKey, storePath }); current = undefined; } if (current && order.length > 0 && !order.includes(current)) { await clearSessionAuthProfileOverride({ sessionEntry, sessionStore, sessionKey, storePath }); current = undefined; } if (order.length === 0) return undefined; const pickFirstAvailable = () => order.find((profileId) => !isProfileInCooldown(store, profileId)) ?? order[0]; const pickNextAvailable = (active: string) => { const startIndex = order.indexOf(active); if (startIndex < 0) return pickFirstAvailable(); for (let offset = 1; offset <= order.length; offset += 1) { const candidate = order[(startIndex + offset) % order.length]; if (!isProfileInCooldown(store, candidate)) return candidate; } return order[startIndex] ?? order[0]; }; const compactionCount = sessionEntry.compactionCount ?? 0; const storedCompaction = typeof sessionEntry.authProfileOverrideCompactionCount === "number" ? sessionEntry.authProfileOverrideCompactionCount : compactionCount; const source = sessionEntry.authProfileOverrideSource ?? (typeof sessionEntry.authProfileOverrideCompactionCount === "number" ? "auto" : current ? "user" : undefined); if (source === "user" && current && !isNewSession) { return current; } let next = current; if (isNewSession) { next = current ? pickNextAvailable(current) : pickFirstAvailable(); } else if (current && compactionCount > storedCompaction) { next = pickNextAvailable(current); } else if (!current || isProfileInCooldown(store, current)) { next = pickFirstAvailable(); } if (!next) return current; const shouldPersist = next !== sessionEntry.authProfileOverride || sessionEntry.authProfileOverrideSource !== "auto" || sessionEntry.authProfileOverrideCompactionCount !== compactionCount; if (shouldPersist) { sessionEntry.authProfileOverride = next; sessionEntry.authProfileOverrideSource = "auto"; sessionEntry.authProfileOverrideCompactionCount = compactionCount; sessionEntry.updatedAt = Date.now(); sessionStore[sessionKey] = sessionEntry; if (storePath) { await updateSessionStore(storePath, (store) => { store[sessionKey] = sessionEntry; }); } } return next; }