openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
370 lines (369 loc) • 15.7 kB
JavaScript
import { t as createLazyImportLoader } from "./lazy-promise-DGqyc4Y4.js";
import { r as resolveProviderModelRoutes } from "./provider-model-routes-jLTliGaX.js";
import { t as splitTrailingAuthProfile } from "./model-ref-profile-BIKs-96s.js";
import { j as resolveSessionAuthProfileOverrideSource } from "./agent-scope-DbtJyKUL.js";
import { a as listOpenAIAuthProfileProvidersForAgentRuntime } from "./openai-routing-Ex_3E9TF.js";
import { l as resolveProviderModelRouteAuthRequirement } from "./openai-model-routes-Bte2KVl5.js";
import { i as isUserModelAuthProfileId } from "./profile-usage-stats-dkZh2v8y.js";
import { l as resolveUserProfileAuthLink } from "./user-model-accounts-BMkC9h8s.js";
import { r as ensureAuthProfileStore } from "./store-F1B2duCT.js";
import { t as hasAnyAuthProfileStoreSource } from "./source-check-BuLDxD9s.js";
import { a as isModelScopedCooldownReason, o as isProfileInCooldown, r as isActiveUnusableWindow } from "./usage-state-CAKmPrwS.js";
import { n as isStoredCredentialCompatibleWithAuthProvider, o as resolveAuthProfileOrderWithMetadata, t as isConfiguredAwsSdkAuthProfileForProvider } from "./order-CC2RBzI5.js";
import "./usage-_yfLJGtN.js";
//#region src/agents/auth-profiles/session-override.ts
/** Keeps automatic auth profiles stable within sessions while rotating at lifecycle boundaries. */
const sessionAccessorLoader = createLazyImportLoader(() => import("./session-accessor-pyriiR4V.js"));
function loadSessionAccessor() {
return sessionAccessorLoader.load();
}
function profileAuthRequirement(params) {
return resolveProviderModelRouteAuthRequirement(params.store?.profiles[params.profileId]?.type ?? params.cfg.auth?.profiles?.[params.profileId]?.mode);
}
function applySessionAuthProfileOverrideState(entry, state, updatedAt) {
if (state.authProfileOverride === void 0) delete entry.authProfileOverride;
else entry.authProfileOverride = state.authProfileOverride;
if (state.authProfileOverrideSource === void 0) delete entry.authProfileOverrideSource;
else entry.authProfileOverrideSource = state.authProfileOverrideSource;
if (state.authProfileOverrideCompactionCount === void 0) delete entry.authProfileOverrideCompactionCount;
else entry.authProfileOverrideCompactionCount = state.authProfileOverrideCompactionCount;
entry.updatedAt = Math.max(entry.updatedAt ?? 0, updatedAt);
}
function matchesSessionAuthProfileOverrideSnapshot(entry, snapshot) {
return entry.sessionId === snapshot.sessionId && entry.authProfileOverride === snapshot.authProfileOverride && entry.authProfileOverrideSource === snapshot.authProfileOverrideSource && entry.authProfileOverrideCompactionCount === snapshot.authProfileOverrideCompactionCount;
}
function synchronizeSessionEntry(entry, latest) {
for (const key of Object.keys(entry)) if (!Object.hasOwn(latest, key)) Reflect.deleteProperty(entry, key);
Object.assign(entry, latest);
}
async function persistSessionAuthProfileOverrideState(params) {
const { sessionEntry, sessionStore, sessionKey, state, storePath, expectedSnapshot } = params;
const updatedAt = Date.now();
if (!storePath) {
if (expectedSnapshot && !Object.hasOwn(sessionStore, sessionKey)) return;
const latest = sessionStore[sessionKey] ?? sessionEntry;
if (expectedSnapshot && !matchesSessionAuthProfileOverrideSnapshot(latest, expectedSnapshot)) {
synchronizeSessionEntry(sessionEntry, latest);
return latest;
}
const target = expectedSnapshot ? latest : sessionEntry;
applySessionAuthProfileOverrideState(target, state, updatedAt);
if (target !== sessionEntry) synchronizeSessionEntry(sessionEntry, target);
sessionStore[sessionKey] = target;
return target;
}
if (!expectedSnapshot) {
applySessionAuthProfileOverrideState(sessionEntry, state, updatedAt);
sessionStore[sessionKey] = sessionEntry;
}
const persisted = await (await loadSessionAccessor()).patchSessionEntryCore({
storePath,
sessionKey
}, (current) => {
if (expectedSnapshot && !matchesSessionAuthProfileOverrideSnapshot(current, expectedSnapshot)) return null;
return {
...state,
updatedAt: Math.max(current.updatedAt ?? 0, updatedAt)
};
}, expectedSnapshot ? void 0 : { fallbackEntry: sessionEntry });
if (persisted) {
if (expectedSnapshot) synchronizeSessionEntry(sessionEntry, persisted);
sessionStore[sessionKey] = persisted;
}
return persisted ?? (expectedSnapshot ? void 0 : sessionEntry);
}
function isProfileForProvider(params) {
const entry = params.store.profiles[params.profileId];
if (entry) {
if (!entry.provider) return false;
return params.providers.some((provider) => isStoredCredentialCompatibleWithAuthProvider({
cfg: params.cfg,
provider,
credential: entry
}));
}
return params.providers.some((provider) => isConfiguredAwsSdkAuthProfileForProvider({
cfg: params.cfg,
provider,
profileId: params.profileId
}));
}
function uniqueProviders(provider, acceptedProviderIds) {
const providers = /* @__PURE__ */ new Set();
const push = (value) => {
const normalized = value?.trim();
if (normalized) providers.add(normalized);
};
(acceptedProviderIds && acceptedProviderIds.length > 0 ? acceptedProviderIds : [provider]).forEach(push);
return [...providers];
}
/** Resolve a person's new-session default through the canonical credential store. */
function resolveUserLinkedAuthProfile(params) {
const providers = uniqueProviders(params.provider, params.acceptedProviderIds);
const profileId = resolveUserProfileAuthLink({
profileId: params.requesterProfileId,
providers
});
if (!profileId) return;
const store = !params.store || isUserModelAuthProfileId(profileId) ? ensureAuthProfileStore(params.agentDir, {
allowKeychainPrompt: false,
profileId
}) : params.store;
return isProfileForProvider({
cfg: params.cfg,
providers,
profileId,
store
}) ? {
profileId,
store
} : void 0;
}
function isProfileGloballyInCooldown(store, profileId) {
if (!isProfileInCooldown(store, profileId)) return false;
const usage = store.usageStats?.[profileId];
if (!usage) return true;
const now = Date.now();
return isActiveUnusableWindow(usage.disabledUntil, now) || isActiveUnusableWindow(usage.blockedUntil, now) && (usage.blockedScope !== "model" || !usage.blockedModel) || isActiveUnusableWindow(usage.cooldownUntil, now) && (!isModelScopedCooldownReason(usage.cooldownReason) || !usage.cooldownModel);
}
/** Clears an auth-profile override from a session and persists it when possible. */
async function clearSessionAuthProfileOverride(params) {
const { sessionEntry, sessionStore, sessionKey, storePath } = params;
await persistSessionAuthProfileOverrideState({
sessionEntry,
sessionStore,
sessionKey,
state: {
authProfileOverride: void 0,
authProfileOverrideSource: void 0,
authProfileOverrideCompactionCount: void 0
},
storePath
});
}
async function resolveSessionAuthProfileOverride(params) {
const { cfg, provider, agentDir, sessionEntry, sessionStore, sessionKey, storePath, isNewSession } = params;
if (!sessionEntry || !sessionStore || !sessionKey) return {
profileId: sessionEntry?.authProfileOverride,
store: void 0
};
const hasConfiguredAuthProfiles = Boolean(params.cfg.auth?.profiles && Object.keys(params.cfg.auth.profiles).length > 0) || Boolean(params.cfg.auth?.order && Object.keys(params.cfg.auth.order).length > 0);
if (!sessionEntry.authProfileOverride?.trim() && !params.requesterProfileId && !hasConfiguredAuthProfiles && !hasAnyAuthProfileStoreSource(agentDir)) return {
profileId: void 0,
store: void 0
};
const store = ensureAuthProfileStore(agentDir, {
allowKeychainPrompt: false,
profileId: sessionEntry.authProfileOverride
});
const providers = uniqueProviders(provider, params.acceptedProviderIds);
const orderResolutions = providers.map((candidateProvider) => resolveAuthProfileOrderWithMetadata({
cfg,
store,
provider: candidateProvider,
forModel: sessionEntry.model
}));
const order = [...new Set(orderResolutions.flatMap((resolution) => resolution.profileIds))];
let current = sessionEntry.authProfileOverride?.trim();
const source = resolveSessionAuthProfileOverrideSource(sessionEntry);
const currentProfileId = current;
if (currentProfileId && !store.profiles[currentProfileId] && !providers.some((candidateProvider) => isConfiguredAwsSdkAuthProfileForProvider({
cfg,
provider: candidateProvider,
profileId: currentProfileId
}))) {
if (isUserModelAuthProfileId(currentProfileId)) throw new Error("This session's personal model account is unavailable. Select another account for this session, or reconnect your account and start a new session.");
await clearSessionAuthProfileOverride({
sessionEntry,
sessionStore,
sessionKey,
storePath
});
current = void 0;
}
if (current && !isProfileForProvider({
cfg,
providers,
profileId: current,
store
})) {
await clearSessionAuthProfileOverride({
sessionEntry,
sessionStore,
sessionKey,
storePath
});
current = void 0;
}
if ((source === "user" || source === "user-link") && current) return {
profileId: current,
store
};
if (params.requesterProfileId && isNewSession) {
const linked = resolveUserLinkedAuthProfile({
cfg,
agentDir,
provider,
requesterProfileId: params.requesterProfileId,
acceptedProviderIds: providers,
store
});
if (linked) {
await persistSessionAuthProfileOverrideState({
sessionEntry,
sessionStore,
sessionKey,
state: {
authProfileOverride: linked.profileId,
authProfileOverrideSource: "user-link",
authProfileOverrideCompactionCount: void 0
},
storePath
});
return linked;
}
}
if (current && order.length > 0 && !order.includes(current)) {
await clearSessionAuthProfileOverride({
sessionEntry,
sessionStore,
sessionKey,
storePath
});
current = void 0;
}
if (order.length === 0) return {
profileId: void 0,
store
};
if (order.every((profileId) => isProfileGloballyInCooldown(store, profileId))) {
if (current) {
const latest = await persistSessionAuthProfileOverrideState({
sessionEntry,
sessionStore,
sessionKey,
state: {
authProfileOverride: void 0,
authProfileOverrideSource: void 0,
authProfileOverrideCompactionCount: void 0
},
storePath,
expectedSnapshot: {
sessionId: sessionEntry.sessionId,
authProfileOverride: sessionEntry.authProfileOverride,
authProfileOverrideSource: sessionEntry.authProfileOverrideSource,
authProfileOverrideCompactionCount: sessionEntry.authProfileOverrideCompactionCount
}
});
const latestProfileId = latest?.authProfileOverride;
const latestSource = resolveSessionAuthProfileOverrideSource(latest);
return {
profileId: latestProfileId && (latestSource === "user" || latestSource === "user-link") && isProfileForProvider({
cfg,
providers,
profileId: latestProfileId,
store
}) ? latestProfileId : void 0,
store
};
}
return {
profileId: void 0,
store
};
}
const isProfileUnavailableForSessionModel = (profileId) => isProfileInCooldown(store, profileId, void 0, sessionEntry.model);
const currentUnavailable = current ? isProfileUnavailableForSessionModel(current) : false;
const compactionCount = sessionEntry.compactionCount ?? 0;
const storedCompaction = typeof sessionEntry.authProfileOverrideCompactionCount === "number" ? sessionEntry.authProfileOverrideCompactionCount : compactionCount;
const retryableHigherPriorityProfile = source === "auto" && !currentUnavailable && compactionCount <= storedCompaction && current ? orderResolutions.filter((resolution) => resolution.hasExplicitOrder).flatMap((resolution) => {
const currentOrderIndex = resolution.profileIds.indexOf(current);
return currentOrderIndex > 0 ? resolution.profileIds.slice(0, currentOrderIndex) : [];
}).find((profileId) => (store.usageStats?.[profileId]?.failureCounts?.rate_limit ?? 0) > 0 && !isProfileUnavailableForSessionModel(profileId)) : void 0;
const shouldRotateCurrent = Boolean(current) && !isNewSession && (currentUnavailable || compactionCount > storedCompaction || retryableHigherPriorityProfile !== void 0);
const routeResolution = shouldRotateCurrent && !retryableHigherPriorityProfile ? resolveProviderModelRoutes({
provider,
modelId: params.modelId,
config: cfg
}) : null;
const currentAuthRequirement = current && routeResolution?.kind === "routes" && routeResolution.routes.length > 1 ? profileAuthRequirement({
cfg,
store,
profileId: current
}) : void 0;
const rotationOrder = currentAuthRequirement ? order.filter((profileId) => profileAuthRequirement({
cfg,
store,
profileId
}) === currentAuthRequirement) : order;
const pickAvailable = (active) => {
const startIndex = active ? rotationOrder.indexOf(active) : -1;
for (let offset = 1; offset <= rotationOrder.length; offset += 1) {
const candidate = rotationOrder[(startIndex + offset) % rotationOrder.length];
if (candidate && !isProfileUnavailableForSessionModel(candidate)) return candidate;
}
return rotationOrder[startIndex] ?? rotationOrder[0];
};
let next = current;
if (retryableHigherPriorityProfile) next = retryableHigherPriorityProfile;
else if (isNewSession || shouldRotateCurrent) next = pickAvailable(currentUnavailable ? void 0 : current);
else if (!current) next = pickAvailable();
if (!next) return {
profileId: current,
store
};
if (next !== sessionEntry.authProfileOverride || sessionEntry.authProfileOverrideSource !== "auto" || sessionEntry.authProfileOverrideCompactionCount !== compactionCount) await persistSessionAuthProfileOverrideState({
sessionEntry,
sessionStore,
sessionKey,
state: {
authProfileOverride: next,
authProfileOverrideSource: "auto",
authProfileOverrideCompactionCount: compactionCount
},
storePath
});
return {
profileId: next,
store
};
}
/** Resolves the session credential and its prepared route facts. */
async function resolveSessionAuthSelection(params) {
const acceptedProviderIds = listOpenAIAuthProfileProvidersForAgentRuntime({
provider: params.provider,
harnessRuntime: params.harnessRuntime,
config: params.cfg
});
const { profileId: rotatedProfileId, store } = await resolveSessionAuthProfileOverride({
...params,
modelId: splitTrailingAuthProfile(params.modelId).model,
acceptedProviderIds
});
const rotatedSource = rotatedProfileId ? params.sessionEntry?.authProfileOverride?.trim() === rotatedProfileId ? resolveSessionAuthProfileOverrideSource(params.sessionEntry) ?? "auto" : "auto" : void 0;
const rotatedPinnedProfileId = rotatedSource === "user" || rotatedSource === "user-link" ? rotatedProfileId : void 0;
const configuredProfileId = params.configuredProfileId?.trim() || void 0;
const profileId = rotatedPinnedProfileId ?? configuredProfileId ?? rotatedProfileId;
if (!profileId) return;
const authStore = !store || isUserModelAuthProfileId(profileId) && !store.profiles[profileId] ? ensureAuthProfileStore(params.agentDir, {
allowKeychainPrompt: false,
profileId
}) : store;
if (profileId === configuredProfileId && !isProfileForProvider({
cfg: params.cfg,
providers: uniqueProviders(params.provider, acceptedProviderIds),
profileId,
store: authStore
})) throw new Error(`Auth profile "${configuredProfileId}" is not configured for ${params.provider}.`);
return {
profileId,
source: rotatedPinnedProfileId || configuredProfileId ? "user" : "auto",
routeRequirement: profileAuthRequirement({
cfg: params.cfg,
store: authStore,
profileId
})
};
}
//#endregion
export { resolveSessionAuthSelection as n, resolveUserLinkedAuthProfile as r, clearSessionAuthProfileOverride as t };