refactor: derive plugin contracts from manifests

This commit is contained in:
Peter Steinberger 2026-04-05 20:02:56 +01:00
parent cac40c01e9
commit 31016c5ed9
No known key found for this signature in database
4 changed files with 205 additions and 82 deletions

View File

@ -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,

View File

@ -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([]);
});
});

View File

@ -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<T> = {
};
type ProviderContractEntry = CapabilityContractEntry<ProviderPlugin>;
type WebSearchProviderContractEntry = CapabilityContractEntry<WebSearchProviderPlugin> & {
credentialValue: unknown;
};
type WebFetchProviderContractEntry = CapabilityContractEntry<WebFetchProviderPlugin> & {
credentialValue: unknown;
};
type SpeechProviderContractEntry = CapabilityContractEntry<SpeechProviderPlugin>;
type RealtimeTranscriptionProviderContractEntry =
CapabilityContractEntry<RealtimeTranscriptionProviderPlugin>;
@ -69,19 +59,18 @@ type PluginRegistrationContractEntry = {
toolNames: string[];
};
function createProviderContractPluginIdsByProviderId(): Map<string, string[]> {
const result = new Map<string, string[]>();
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<string, string[]> {
const result = new Map<string, string[]>();
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<string, ProviderContractEntry[]> | 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<T>(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[] =

View File

@ -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<string, string> {
const { ["openclaw/plugin-sdk"]: _ignoredRootAlias, ...scopedAliasMap } =
buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url, "dist");
@ -114,6 +114,14 @@ function resolveNamedValues<T>(
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) {