import type { OpenClawConfig } from "../config/types.openclaw.js"; import { withBundledPluginAllowlistCompat, withBundledPluginEnablementCompat, withBundledPluginVitestCompat, } from "./bundled-compat.js"; import { resolveRuntimePluginRegistry } from "./loader.js"; import { loadPluginManifestRegistry } from "./manifest-registry.js"; import type { PluginRegistry } from "./registry-types.js"; type CapabilityProviderRegistryKey = | "memoryEmbeddingProviders" | "speechProviders" | "realtimeTranscriptionProviders" | "realtimeVoiceProviders" | "mediaUnderstandingProviders" | "imageGenerationProviders" | "videoGenerationProviders" | "musicGenerationProviders"; type CapabilityContractKey = | "memoryEmbeddingProviders" | "speechProviders" | "realtimeTranscriptionProviders" | "realtimeVoiceProviders" | "mediaUnderstandingProviders" | "imageGenerationProviders" | "videoGenerationProviders" | "musicGenerationProviders"; type CapabilityProviderForKey = PluginRegistry[K][number] extends { provider: infer T } ? T : never; const CAPABILITY_CONTRACT_KEY: Record = { memoryEmbeddingProviders: "memoryEmbeddingProviders", speechProviders: "speechProviders", realtimeTranscriptionProviders: "realtimeTranscriptionProviders", realtimeVoiceProviders: "realtimeVoiceProviders", mediaUnderstandingProviders: "mediaUnderstandingProviders", imageGenerationProviders: "imageGenerationProviders", videoGenerationProviders: "videoGenerationProviders", musicGenerationProviders: "musicGenerationProviders", }; function resolveBundledCapabilityCompatPluginIds(params: { key: CapabilityProviderRegistryKey; cfg?: OpenClawConfig; providerId?: string; }): string[] { const contractKey = CAPABILITY_CONTRACT_KEY[params.key]; return loadPluginManifestRegistry({ config: params.cfg, env: process.env, }) .plugins.filter( (plugin) => plugin.origin === "bundled" && (plugin.contracts?.[contractKey]?.length ?? 0) > 0 && (!params.providerId || (plugin.contracts?.[contractKey] ?? []).includes(params.providerId)), ) .map((plugin) => plugin.id) .toSorted((left, right) => left.localeCompare(right)); } function resolveCapabilityProviderConfig(params: { key: CapabilityProviderRegistryKey; cfg?: OpenClawConfig; pluginIds?: string[]; }) { const pluginIds = params.pluginIds ?? resolveBundledCapabilityCompatPluginIds(params); const allowlistCompat = withBundledPluginAllowlistCompat({ config: params.cfg, pluginIds, }); const enablementCompat = withBundledPluginEnablementCompat({ config: allowlistCompat, pluginIds, }); return withBundledPluginVitestCompat({ config: enablementCompat, pluginIds, env: process.env, }); } function findProviderById( entries: PluginRegistry[K], providerId: string, ): CapabilityProviderForKey | undefined { const providerEntries = entries as unknown as Array<{ provider: CapabilityProviderForKey & { id?: unknown }; }>; for (const entry of providerEntries) { if (entry.provider.id === providerId) { return entry.provider; } } return undefined; } export function resolvePluginCapabilityProvider(params: { key: K; providerId: string; cfg?: OpenClawConfig; }): CapabilityProviderForKey | undefined { const activeRegistry = resolveRuntimePluginRegistry(); const activeProvider = findProviderById(activeRegistry?.[params.key] ?? [], params.providerId); if (activeProvider) { return activeProvider; } const pluginIds = resolveBundledCapabilityCompatPluginIds({ key: params.key, cfg: params.cfg, providerId: params.providerId, }); if (pluginIds.length === 0) { return undefined; } const compatConfig = resolveCapabilityProviderConfig({ key: params.key, cfg: params.cfg, pluginIds, }); const loadOptions = compatConfig === undefined ? undefined : { config: compatConfig }; const registry = resolveRuntimePluginRegistry(loadOptions); return findProviderById(registry?.[params.key] ?? [], params.providerId); } export function resolvePluginCapabilityProviders(params: { key: K; cfg?: OpenClawConfig; }): CapabilityProviderForKey[] { const activeRegistry = resolveRuntimePluginRegistry(); const activeProviders = activeRegistry?.[params.key] ?? []; if (activeProviders.length > 0 && params.key !== "memoryEmbeddingProviders") { return activeProviders.map((entry) => entry.provider) as CapabilityProviderForKey[]; } const compatConfig = resolveCapabilityProviderConfig({ key: params.key, cfg: params.cfg }); const loadOptions = compatConfig === undefined ? undefined : { config: compatConfig }; const registry = resolveRuntimePluginRegistry(loadOptions); if (params.key !== "memoryEmbeddingProviders") { return (registry?.[params.key] ?? []).map( (entry) => entry.provider, ) as CapabilityProviderForKey[]; } const merged = new Map>(); for (const entry of activeProviders) { const provider = entry.provider as CapabilityProviderForKey & { id?: string }; if (provider.id) { merged.set(provider.id, provider); } } for (const entry of registry?.[params.key] ?? []) { const provider = entry.provider as CapabilityProviderForKey & { id?: string }; if (provider.id && !merged.has(provider.id)) { merged.set(provider.id, provider); } } return [...merged.values()]; }