import { normalizeProviderId } from "../agents/provider-id.js"; import type { ModelProviderConfig } from "../config/types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveBundledPluginsDir } from "./bundled-dir.js"; import { loadPluginManifestRegistry, type PluginManifestRegistry } from "./manifest-registry.js"; import type { ProviderApplyConfigDefaultsContext, ProviderNormalizeConfigContext, ProviderResolveConfigApiKeyContext, } from "./provider-config-context.types.js"; import type { ProviderDefaultThinkingPolicyContext, ProviderThinkingProfile, } from "./provider-thinking.types.js"; import { loadBundledPluginPublicArtifactModuleSync } from "./public-surface-loader.js"; const PROVIDER_POLICY_ARTIFACT_CANDIDATES = ["provider-policy-api.js"] as const; export type BundledProviderPolicySurface = { normalizeConfig?: (ctx: ProviderNormalizeConfigContext) => ModelProviderConfig | null | undefined; applyConfigDefaults?: ( ctx: ProviderApplyConfigDefaultsContext, ) => OpenClawConfig | null | undefined; resolveConfigApiKey?: (ctx: ProviderResolveConfigApiKeyContext) => string | null | undefined; resolveThinkingProfile?: ( ctx: ProviderDefaultThinkingPolicyContext, ) => ProviderThinkingProfile | null | undefined; }; function hasProviderPolicyHook( mod: Record, ): mod is Record & BundledProviderPolicySurface { return ( typeof mod.normalizeConfig === "function" || typeof mod.applyConfigDefaults === "function" || typeof mod.resolveConfigApiKey === "function" || typeof mod.resolveThinkingProfile === "function" ); } function tryLoadBundledProviderPolicySurface( pluginId: string, ): BundledProviderPolicySurface | null { for (const artifactBasename of PROVIDER_POLICY_ARTIFACT_CANDIDATES) { try { const mod = loadBundledPluginPublicArtifactModuleSync>({ dirName: pluginId, artifactBasename, }); if (hasProviderPolicyHook(mod)) { return mod; } } catch (error) { if ( error instanceof Error && error.message.startsWith("Unable to resolve bundled plugin public surface ") ) { continue; } throw error; } } return null; } function resolveBundledProviderPolicyPluginId( providerId: string, options: { manifestRegistry?: Pick } = {}, ): string | null { const normalizedProviderId = normalizeProviderId(providerId); if (!normalizedProviderId) { return null; } const bundledPluginsDir = resolveBundledPluginsDir(); if (!bundledPluginsDir) { return null; } const registry = options.manifestRegistry ?? loadPluginManifestRegistry(); for (const plugin of registry.plugins.toSorted((left, right) => left.id.localeCompare(right.id), )) { if (plugin.origin !== "bundled") { continue; } const ownsProvider = plugin.providers.some( (provider) => normalizeProviderId(provider) === normalizedProviderId, ); if (ownsProvider) { return plugin.id; } } return null; } export function resolveBundledProviderPolicySurface( providerId: string, options: { manifestRegistry?: Pick } = {}, ): BundledProviderPolicySurface | null { const normalizedProviderId = normalizeProviderId(providerId); if (!normalizedProviderId) { return null; } const directSurface = tryLoadBundledProviderPolicySurface(normalizedProviderId); if (directSurface) { return directSurface; } const ownerPluginId = resolveBundledProviderPolicyPluginId(normalizedProviderId, options); if (!ownerPluginId || ownerPluginId === normalizedProviderId) { return null; } return tryLoadBundledProviderPolicySurface(ownerPluginId); }