From 31016c5ed9128d6ed5745d7271fdb148f6eba182 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 5 Apr 2026 20:02:56 +0100 Subject: [PATCH] refactor: derive plugin contracts from manifests --- src/agents/agent-scope.ts | 5 +- .../contracts/boundary-invariants.test.ts | 56 ++++++ src/plugins/contracts/registry.ts | 188 ++++++++++++------ .../contracts/speech-vitest-registry.ts | 38 ++-- 4 files changed, 205 insertions(+), 82 deletions(-) create mode 100644 src/plugins/contracts/boundary-invariants.test.ts diff --git a/src/agents/agent-scope.ts b/src/agents/agent-scope.ts index 1e5c833593d..c8d23fbef60 100644 --- a/src/agents/agent-scope.ts +++ b/src/agents/agent-scope.ts @@ -3,6 +3,7 @@ import path from "node:path"; import type { OpenClawConfig } from "../config/config.js"; import { resolveAgentModelFallbackValues } from "../config/model-input.js"; import { resolveStateDir } from "../config/paths.js"; +import type { AgentDefaultsConfig } from "../config/types.agent-defaults.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { DEFAULT_AGENT_ID, @@ -37,7 +38,7 @@ type ResolvedAgentConfig = { agentDir?: string; model?: AgentEntry["model"]; thinkingDefault?: AgentEntry["thinkingDefault"]; - verboseDefault?: AgentEntry["verboseDefault"]; + verboseDefault?: AgentDefaultsConfig["verboseDefault"]; reasoningDefault?: AgentEntry["reasoningDefault"]; fastModeDefault?: AgentEntry["fastModeDefault"]; skills?: AgentEntry["skills"]; @@ -143,7 +144,7 @@ export function resolveAgentConfig( ? entry.model : undefined, thinkingDefault: entry.thinkingDefault, - verboseDefault: entry.verboseDefault, + verboseDefault: cfg.agents?.defaults?.verboseDefault, reasoningDefault: entry.reasoningDefault, fastModeDefault: entry.fastModeDefault, skills: Array.isArray(entry.skills) ? entry.skills : undefined, diff --git a/src/plugins/contracts/boundary-invariants.test.ts b/src/plugins/contracts/boundary-invariants.test.ts new file mode 100644 index 00000000000..db46d816649 --- /dev/null +++ b/src/plugins/contracts/boundary-invariants.test.ts @@ -0,0 +1,56 @@ +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const SRC_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); +const REPO_ROOT = resolve(SRC_ROOT, ".."); + +const ALLOWED_BUNDLED_CAPABILITY_METADATA_CONSUMERS = new Set([ + "src/plugins/bundled-capability-metadata.test.ts", + "src/plugins/contracts/boundary-invariants.test.ts", +]); + +const ALLOWED_EXTENSION_PATH_STRING_TESTS = new Set([ + "src/channels/plugins/bundled.shape-guard.test.ts", + "src/plugins/contracts/bundled-extension-config-api-guardrails.test.ts", + "src/scripts/test-projects.test.ts", +]); + +describe("plugin contract boundary invariants", () => { + it("keeps bundled-capability-metadata confined to contract/test inventory", async () => { + const { globSync } = await import("glob"); + const files = globSync("src/**/*.ts", { + cwd: REPO_ROOT, + nodir: true, + }); + const offenders = files.filter((file) => { + if (ALLOWED_BUNDLED_CAPABILITY_METADATA_CONSUMERS.has(file)) { + return false; + } + const source = readFileSync(resolve(REPO_ROOT, file), "utf8"); + return source.includes("bundled-capability-metadata"); + }); + expect(offenders).toEqual([]); + }); + + it("keeps core tests off bundled extension deep imports", async () => { + const { globSync } = await import("glob"); + const files = globSync("src/**/*.test.ts", { + cwd: REPO_ROOT, + nodir: true, + }); + const offenders = files.filter((file) => { + if (ALLOWED_EXTENSION_PATH_STRING_TESTS.has(file)) { + return false; + } + const source = readFileSync(resolve(REPO_ROOT, file), "utf8"); + return ( + /from\s+["'][^"']*extensions\/.+(?:api|runtime-api|test-api)\.js["']/u.test(source) || + /vi\.(?:mock|doMock)\(\s*["'][^"']*extensions\/.+["']/u.test(source) || + /importActual<[^>]*>\(\s*["'][^"']*extensions\/.+["']/u.test(source) + ); + }); + expect(offenders).toEqual([]); + }); +}); diff --git a/src/plugins/contracts/registry.ts b/src/plugins/contracts/registry.ts index 2f2fb89aa55..de997f35b95 100644 --- a/src/plugins/contracts/registry.ts +++ b/src/plugins/contracts/registry.ts @@ -1,16 +1,8 @@ -import { - BUNDLED_IMAGE_GENERATION_PLUGIN_IDS, - BUNDLED_MEDIA_UNDERSTANDING_PLUGIN_IDS, - BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS, - BUNDLED_PROVIDER_PLUGIN_IDS, - BUNDLED_REALTIME_TRANSCRIPTION_PLUGIN_IDS, - BUNDLED_REALTIME_VOICE_PLUGIN_IDS, - BUNDLED_SPEECH_PLUGIN_IDS, - BUNDLED_WEB_FETCH_PLUGIN_IDS, - BUNDLED_WEB_SEARCH_PLUGIN_IDS, - BUNDLED_VIDEO_GENERATION_PLUGIN_IDS, -} from "../bundled-capability-metadata.js"; import { loadBundledCapabilityRuntimeRegistry } from "../bundled-capability-runtime.js"; +import { + loadPluginManifestRegistry, + resolveManifestContractPluginIds, +} from "../manifest-registry.js"; import type { ImageGenerationProviderPlugin, MediaUnderstandingProviderPlugin, @@ -38,14 +30,12 @@ type CapabilityContractEntry = { }; type ProviderContractEntry = CapabilityContractEntry; - type WebSearchProviderContractEntry = CapabilityContractEntry & { credentialValue: unknown; }; type WebFetchProviderContractEntry = CapabilityContractEntry & { credentialValue: unknown; }; - type SpeechProviderContractEntry = CapabilityContractEntry; type RealtimeTranscriptionProviderContractEntry = CapabilityContractEntry; @@ -69,19 +59,18 @@ type PluginRegistrationContractEntry = { toolNames: string[]; }; -function createProviderContractPluginIdsByProviderId(): Map { - const result = new Map(); - for (const entry of BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS) { - for (const providerId of entry.providerIds) { - const existing = result.get(providerId) ?? []; - if (!existing.includes(entry.pluginId)) { - existing.push(entry.pluginId); - } - result.set(providerId, existing); - } - } - return result; -} +type ManifestContractKey = + | "speechProviders" + | "realtimeTranscriptionProviders" + | "realtimeVoiceProviders" + | "mediaUnderstandingProviders" + | "imageGenerationProviders" + | "videoGenerationProviders" + | "webFetchProviders" + | "webSearchProviders" + | "tools"; + +type ManifestRegistryContractKey = "webFetchProviders" | "webSearchProviders"; function uniqueStrings(values: readonly string[]): string[] { const result: string[] = []; @@ -96,6 +85,99 @@ function uniqueStrings(values: readonly string[]): string[] { return result; } +function resolveBundledManifestContracts(): PluginRegistrationContractEntry[] { + return loadPluginManifestRegistry({}) + .plugins.filter( + (plugin) => + plugin.origin === "bundled" && + (plugin.providers.length > 0 || + (plugin.contracts?.speechProviders?.length ?? 0) > 0 || + (plugin.contracts?.realtimeTranscriptionProviders?.length ?? 0) > 0 || + (plugin.contracts?.realtimeVoiceProviders?.length ?? 0) > 0 || + (plugin.contracts?.mediaUnderstandingProviders?.length ?? 0) > 0 || + (plugin.contracts?.imageGenerationProviders?.length ?? 0) > 0 || + (plugin.contracts?.videoGenerationProviders?.length ?? 0) > 0 || + (plugin.contracts?.webFetchProviders?.length ?? 0) > 0 || + (plugin.contracts?.webSearchProviders?.length ?? 0) > 0 || + (plugin.contracts?.tools?.length ?? 0) > 0), + ) + .map((plugin) => ({ + pluginId: plugin.id, + providerIds: uniqueStrings(plugin.providers), + speechProviderIds: uniqueStrings(plugin.contracts?.speechProviders ?? []), + realtimeTranscriptionProviderIds: uniqueStrings( + plugin.contracts?.realtimeTranscriptionProviders ?? [], + ), + realtimeVoiceProviderIds: uniqueStrings(plugin.contracts?.realtimeVoiceProviders ?? []), + mediaUnderstandingProviderIds: uniqueStrings( + plugin.contracts?.mediaUnderstandingProviders ?? [], + ), + imageGenerationProviderIds: uniqueStrings(plugin.contracts?.imageGenerationProviders ?? []), + videoGenerationProviderIds: uniqueStrings(plugin.contracts?.videoGenerationProviders ?? []), + webFetchProviderIds: uniqueStrings(plugin.contracts?.webFetchProviders ?? []), + webSearchProviderIds: uniqueStrings(plugin.contracts?.webSearchProviders ?? []), + toolNames: uniqueStrings(plugin.contracts?.tools ?? []), + })); +} + +function resolveBundledProviderContractPluginIdsByProviderId(): Map { + const result = new Map(); + for (const entry of resolveBundledManifestContracts()) { + for (const providerId of entry.providerIds) { + const existing = result.get(providerId) ?? []; + if (!existing.includes(entry.pluginId)) { + existing.push(entry.pluginId); + } + result.set(providerId, existing); + } + } + return result; +} + +function resolveBundledProviderContractPluginIds(): string[] { + return uniqueStrings( + resolveBundledManifestContracts() + .filter((entry) => entry.providerIds.length > 0) + .map((entry) => entry.pluginId), + ).toSorted((left, right) => left.localeCompare(right)); +} + +function resolveBundledManifestContractPluginIds(contract: ManifestRegistryContractKey): string[] { + return resolveManifestContractPluginIds({ + contract, + origin: "bundled", + }); +} + +function resolveBundledManifestPluginIdsForContract(contract: ManifestContractKey): string[] { + return uniqueStrings( + resolveBundledManifestContracts() + .filter((entry) => { + switch (contract) { + case "speechProviders": + return entry.speechProviderIds.length > 0; + case "realtimeTranscriptionProviders": + return entry.realtimeTranscriptionProviderIds.length > 0; + case "realtimeVoiceProviders": + return entry.realtimeVoiceProviderIds.length > 0; + case "mediaUnderstandingProviders": + return entry.mediaUnderstandingProviderIds.length > 0; + case "imageGenerationProviders": + return entry.imageGenerationProviderIds.length > 0; + case "videoGenerationProviders": + return entry.videoGenerationProviderIds.length > 0; + case "webFetchProviders": + return entry.webFetchProviderIds.length > 0; + case "webSearchProviders": + return entry.webSearchProviderIds.length > 0; + case "tools": + return entry.toolNames.length > 0; + } + }) + .map((entry) => entry.pluginId), + ).toSorted((left, right) => left.localeCompare(right)); +} + let providerContractRegistryCache: ProviderContractEntry[] | null = null; let providerContractRegistryByPluginIdCache: Map | null = null; let webFetchProviderContractRegistryCache: WebFetchProviderContractEntry[] | null = null; @@ -120,7 +202,6 @@ let imageGenerationProviderContractRegistryCache: ImageGenerationProviderContrac null; let videoGenerationProviderContractRegistryCache: VideoGenerationProviderContractEntry[] | null = null; -const providerContractPluginIdsByProviderId = createProviderContractPluginIdsByProviderId(); export let providerContractLoadError: Error | undefined; @@ -242,7 +323,7 @@ function loadProviderContractRegistry(): ProviderContractEntry[] { try { providerContractLoadError = undefined; providerContractRegistryCache = loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_PROVIDER_PLUGIN_IDS, + pluginIds: resolveBundledProviderContractPluginIds(), pluginSdkResolution: "dist", }).providers.map((entry) => ({ pluginId: entry.pluginId, @@ -265,7 +346,7 @@ function loadUniqueProviderContractProviders(): ProviderPlugin[] { } function loadProviderContractPluginIds(): string[] { - return [...BUNDLED_PROVIDER_PLUGIN_IDS]; + return [...resolveBundledProviderContractPluginIds()]; } function loadProviderContractCompatPluginIds(): string[] { @@ -300,7 +381,7 @@ function resolveWebFetchCredentialValue(provider: WebFetchProviderPlugin): unkno function loadWebFetchProviderContractRegistry(): WebFetchProviderContractEntry[] { if (!webFetchProviderContractRegistryCache) { const registry = loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_WEB_FETCH_PLUGIN_IDS, + pluginIds: resolveBundledManifestContractPluginIds("webFetchProviders"), pluginSdkResolution: "dist", }); webFetchProviderContractRegistryCache = registry.webFetchProviders.map((entry) => ({ @@ -348,7 +429,7 @@ export function resolveWebFetchProviderContractEntriesForPluginId( function loadWebSearchProviderContractRegistry(): WebSearchProviderContractEntry[] { if (!webSearchProviderContractRegistryCache) { const registry = loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_WEB_SEARCH_PLUGIN_IDS, + pluginIds: resolveBundledManifestContractPluginIds("webSearchProviders"), pluginSdkResolution: "dist", }); webSearchProviderContractRegistryCache = registry.webSearchProviders.map((entry) => ({ @@ -398,7 +479,7 @@ function loadSpeechProviderContractRegistry(): SpeechProviderContractEntry[] { speechProviderContractRegistryCache = process.env.VITEST ? loadVitestSpeechProviderContractRegistry() : loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_SPEECH_PLUGIN_IDS, + pluginIds: resolveBundledManifestPluginIdsForContract("speechProviders"), pluginSdkResolution: "dist", }).speechProviders.map((entry) => ({ pluginId: entry.pluginId, @@ -413,7 +494,7 @@ function loadRealtimeVoiceProviderContractRegistry(): RealtimeVoiceProviderContr realtimeVoiceProviderContractRegistryCache = process.env.VITEST ? loadVitestRealtimeVoiceProviderContractRegistry() : loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_REALTIME_VOICE_PLUGIN_IDS, + pluginIds: resolveBundledManifestPluginIdsForContract("realtimeVoiceProviders"), pluginSdkResolution: "dist", }).realtimeVoiceProviders.map((entry) => ({ pluginId: entry.pluginId, @@ -428,7 +509,7 @@ function loadRealtimeTranscriptionProviderContractRegistry(): RealtimeTranscript realtimeTranscriptionProviderContractRegistryCache = process.env.VITEST ? loadVitestRealtimeTranscriptionProviderContractRegistry() : loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_REALTIME_TRANSCRIPTION_PLUGIN_IDS, + pluginIds: resolveBundledManifestPluginIdsForContract("realtimeTranscriptionProviders"), pluginSdkResolution: "dist", }).realtimeTranscriptionProviders.map((entry) => ({ pluginId: entry.pluginId, @@ -443,7 +524,7 @@ function loadMediaUnderstandingProviderContractRegistry(): MediaUnderstandingPro mediaUnderstandingProviderContractRegistryCache = process.env.VITEST ? loadVitestMediaUnderstandingProviderContractRegistry() : loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_MEDIA_UNDERSTANDING_PLUGIN_IDS, + pluginIds: resolveBundledManifestPluginIdsForContract("mediaUnderstandingProviders"), pluginSdkResolution: "dist", }).mediaUnderstandingProviders.map((entry) => ({ pluginId: entry.pluginId, @@ -458,7 +539,7 @@ function loadImageGenerationProviderContractRegistry(): ImageGenerationProviderC imageGenerationProviderContractRegistryCache = process.env.VITEST ? loadVitestImageGenerationProviderContractRegistry() : loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_IMAGE_GENERATION_PLUGIN_IDS, + pluginIds: resolveBundledManifestPluginIdsForContract("imageGenerationProviders"), pluginSdkResolution: "dist", }).imageGenerationProviders.map((entry) => ({ pluginId: entry.pluginId, @@ -473,7 +554,7 @@ function loadVideoGenerationProviderContractRegistry(): VideoGenerationProviderC videoGenerationProviderContractRegistryCache = process.env.VITEST ? loadVitestVideoGenerationProviderContractRegistry() : loadBundledCapabilityRuntimeRegistry({ - pluginIds: BUNDLED_VIDEO_GENERATION_PLUGIN_IDS, + pluginIds: resolveBundledManifestPluginIdsForContract("videoGenerationProviders"), pluginSdkResolution: "dist", }).videoGenerationProviders.map((entry) => ({ pluginId: entry.pluginId, @@ -518,30 +599,24 @@ function createLazyArrayView(load: () => T[]): T[] { export const providerContractRegistry: ProviderContractEntry[] = createLazyArrayView( loadProviderContractRegistry, ); - export const uniqueProviderContractProviders: ProviderPlugin[] = createLazyArrayView( loadUniqueProviderContractProviders, ); - export const providerContractPluginIds: string[] = createLazyArrayView( loadProviderContractPluginIds, ); - export const providerContractCompatPluginIds: string[] = createLazyArrayView( loadProviderContractCompatPluginIds, ); export function requireProviderContractProvider(providerId: string): ProviderPlugin { - const pluginIds = providerContractPluginIdsByProviderId.get(providerId) ?? []; + const pluginIds = resolveBundledProviderContractPluginIdsByProviderId().get(providerId) ?? []; const entries = loadProviderContractEntriesForPluginIds(pluginIds); const provider = entries.find((entry) => entry.provider.id === providerId)?.provider; if (!provider) { const pluginScopedProviders = [ ...new Map(entries.map((entry) => [entry.provider.id, entry.provider])).values(), ]; - // Paired catalogs may expose multiple runtime provider ids from one shared - // ProviderPlugin contract entry. Reuse that single contract surface for the - // manifest-owned alias ids instead of requiring duplicate registration. if (pluginIds.length === 1 && pluginScopedProviders.length === 1) { return pluginScopedProviders[0]; } @@ -558,7 +633,7 @@ export function requireProviderContractProvider(providerId: string): ProviderPlu export function resolveProviderContractPluginIdsForProvider( providerId: string, ): string[] | undefined { - const pluginIds = providerContractPluginIdsByProviderId.get(providerId) ?? []; + const pluginIds = resolveBundledProviderContractPluginIdsByProviderId().get(providerId) ?? []; return pluginIds.length > 0 ? pluginIds : undefined; } @@ -577,43 +652,24 @@ export function resolveProviderContractProvidersForPluginIds( export const webSearchProviderContractRegistry: WebSearchProviderContractEntry[] = createLazyArrayView(loadWebSearchProviderContractRegistry); - export const webFetchProviderContractRegistry: WebFetchProviderContractEntry[] = createLazyArrayView(loadWebFetchProviderContractRegistry); - export const speechProviderContractRegistry: SpeechProviderContractEntry[] = createLazyArrayView( loadSpeechProviderContractRegistry, ); - export const realtimeTranscriptionProviderContractRegistry: RealtimeTranscriptionProviderContractEntry[] = createLazyArrayView(loadRealtimeTranscriptionProviderContractRegistry); - export const realtimeVoiceProviderContractRegistry: RealtimeVoiceProviderContractEntry[] = createLazyArrayView(loadRealtimeVoiceProviderContractRegistry); - export const mediaUnderstandingProviderContractRegistry: MediaUnderstandingProviderContractEntry[] = createLazyArrayView(loadMediaUnderstandingProviderContractRegistry); - export const imageGenerationProviderContractRegistry: ImageGenerationProviderContractEntry[] = createLazyArrayView(loadImageGenerationProviderContractRegistry); - export const videoGenerationProviderContractRegistry: VideoGenerationProviderContractEntry[] = createLazyArrayView(loadVideoGenerationProviderContractRegistry); function loadPluginRegistrationContractRegistry(): PluginRegistrationContractEntry[] { - return BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.map((entry) => ({ - pluginId: entry.pluginId, - providerIds: uniqueStrings(entry.providerIds), - speechProviderIds: uniqueStrings(entry.speechProviderIds), - realtimeTranscriptionProviderIds: uniqueStrings(entry.realtimeTranscriptionProviderIds), - realtimeVoiceProviderIds: uniqueStrings(entry.realtimeVoiceProviderIds), - mediaUnderstandingProviderIds: uniqueStrings(entry.mediaUnderstandingProviderIds), - imageGenerationProviderIds: uniqueStrings(entry.imageGenerationProviderIds), - videoGenerationProviderIds: uniqueStrings(entry.videoGenerationProviderIds), - webFetchProviderIds: uniqueStrings(entry.webFetchProviderIds), - webSearchProviderIds: uniqueStrings(entry.webSearchProviderIds), - toolNames: uniqueStrings(entry.toolNames), - })); + return resolveBundledManifestContracts(); } export const pluginRegistrationContractRegistry: PluginRegistrationContractEntry[] = diff --git a/src/plugins/contracts/speech-vitest-registry.ts b/src/plugins/contracts/speech-vitest-registry.ts index 1554fac3034..4c797a01c03 100644 --- a/src/plugins/contracts/speech-vitest-registry.ts +++ b/src/plugins/contracts/speech-vitest-registry.ts @@ -2,14 +2,6 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { createJiti } from "jiti"; -import { - BUNDLED_IMAGE_GENERATION_PLUGIN_IDS, - BUNDLED_MEDIA_UNDERSTANDING_PLUGIN_IDS, - BUNDLED_REALTIME_TRANSCRIPTION_PLUGIN_IDS, - BUNDLED_REALTIME_VOICE_PLUGIN_IDS, - BUNDLED_SPEECH_PLUGIN_IDS, - BUNDLED_VIDEO_GENERATION_PLUGIN_IDS, -} from "../bundled-capability-metadata.js"; import { loadBundledCapabilityRuntimeRegistry } from "../bundled-capability-runtime.js"; import { loadPluginManifestRegistry } from "../manifest-registry.js"; import { buildPluginLoaderAliasMap, buildPluginLoaderJitiOptions } from "../sdk-alias.js"; @@ -52,6 +44,14 @@ export type VideoGenerationProviderContractEntry = { provider: VideoGenerationProviderPlugin; }; +type ManifestContractKey = + | "speechProviders" + | "mediaUnderstandingProviders" + | "realtimeVoiceProviders" + | "realtimeTranscriptionProviders" + | "imageGenerationProviders" + | "videoGenerationProviders"; + function buildVitestCapabilityAliasMap(modulePath: string): Record { const { ["openclaw/plugin-sdk"]: _ignoredRootAlias, ...scopedAliasMap } = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url, "dist"); @@ -114,6 +114,14 @@ function resolveNamedValues( return matches; } +function resolveBundledManifestPluginIds(contract: ManifestContractKey): string[] { + return loadPluginManifestRegistry({}) + .plugins.filter( + (plugin) => plugin.origin === "bundled" && (plugin.contracts?.[contract]?.length ?? 0) > 0, + ) + .map((plugin) => plugin.id); +} + function resolveTestApiModuleRecords(pluginIds: readonly string[]) { const unresolvedPluginIds = new Set(pluginIds); const manifests = loadPluginManifestRegistry({}).plugins.filter( @@ -140,7 +148,9 @@ function isMediaUnderstandingProvider(value: unknown): value is MediaUnderstandi export function loadVitestSpeechProviderContractRegistry(): SpeechProviderContractEntry[] { const registrations: SpeechProviderContractEntry[] = []; - const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords(BUNDLED_SPEECH_PLUGIN_IDS); + const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords( + resolveBundledManifestPluginIds("speechProviders"), + ); for (const plugin of manifests) { if (!plugin.rootDir) { @@ -184,7 +194,7 @@ export function loadVitestSpeechProviderContractRegistry(): SpeechProviderContra export function loadVitestMediaUnderstandingProviderContractRegistry(): MediaUnderstandingProviderContractEntry[] { const registrations: MediaUnderstandingProviderContractEntry[] = []; const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords( - BUNDLED_MEDIA_UNDERSTANDING_PLUGIN_IDS, + resolveBundledManifestPluginIds("mediaUnderstandingProviders"), ); for (const plugin of manifests) { @@ -227,7 +237,7 @@ export function loadVitestMediaUnderstandingProviderContractRegistry(): MediaUnd export function loadVitestRealtimeVoiceProviderContractRegistry(): RealtimeVoiceProviderContractEntry[] { const registrations: RealtimeVoiceProviderContractEntry[] = []; const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords( - BUNDLED_REALTIME_VOICE_PLUGIN_IDS, + resolveBundledManifestPluginIds("realtimeVoiceProviders"), ); for (const plugin of manifests) { @@ -272,7 +282,7 @@ export function loadVitestRealtimeVoiceProviderContractRegistry(): RealtimeVoice export function loadVitestRealtimeTranscriptionProviderContractRegistry(): RealtimeTranscriptionProviderContractEntry[] { const registrations: RealtimeTranscriptionProviderContractEntry[] = []; const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords( - BUNDLED_REALTIME_TRANSCRIPTION_PLUGIN_IDS, + resolveBundledManifestPluginIds("realtimeTranscriptionProviders"), ); for (const plugin of manifests) { @@ -317,7 +327,7 @@ export function loadVitestRealtimeTranscriptionProviderContractRegistry(): Realt export function loadVitestImageGenerationProviderContractRegistry(): ImageGenerationProviderContractEntry[] { const registrations: ImageGenerationProviderContractEntry[] = []; const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords( - BUNDLED_IMAGE_GENERATION_PLUGIN_IDS, + resolveBundledManifestPluginIds("imageGenerationProviders"), ); for (const plugin of manifests) { @@ -364,7 +374,7 @@ export function loadVitestImageGenerationProviderContractRegistry(): ImageGenera export function loadVitestVideoGenerationProviderContractRegistry(): VideoGenerationProviderContractEntry[] { const registrations: VideoGenerationProviderContractEntry[] = []; const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords( - BUNDLED_VIDEO_GENERATION_PLUGIN_IDS, + resolveBundledManifestPluginIds("videoGenerationProviders"), ); for (const plugin of manifests) {