feat(plugins): tighten media runtime integration

This commit is contained in:
Peter Steinberger
2026-03-16 21:13:38 -07:00
parent 45cb02b1dd
commit f4fa84aea7
8 changed files with 63 additions and 117 deletions

View File

@@ -47,26 +47,20 @@ type RegistrablePlugin = {
register: (api: ReturnType<typeof createCapturedPluginRegistration>["api"]) => void;
};
type ProviderContractEntry = {
type CapabilityContractEntry<T> = {
pluginId: string;
provider: ProviderPlugin;
provider: T;
};
type WebSearchProviderContractEntry = {
pluginId: string;
provider: WebSearchProviderPlugin;
type ProviderContractEntry = CapabilityContractEntry<ProviderPlugin>;
type WebSearchProviderContractEntry = CapabilityContractEntry<WebSearchProviderPlugin> & {
credentialValue: unknown;
};
type SpeechProviderContractEntry = {
pluginId: string;
provider: SpeechProviderPlugin;
};
type MediaUnderstandingProviderContractEntry = {
pluginId: string;
provider: MediaUnderstandingProviderPlugin;
};
type SpeechProviderContractEntry = CapabilityContractEntry<SpeechProviderPlugin>;
type MediaUnderstandingProviderContractEntry =
CapabilityContractEntry<MediaUnderstandingProviderPlugin>;
type PluginRegistrationContractEntry = {
pluginId: string;
@@ -138,15 +132,23 @@ function captureRegistrations(plugin: RegistrablePlugin) {
return captured;
}
export const providerContractRegistry: ProviderContractEntry[] = bundledProviderPlugins.flatMap(
(plugin) => {
function buildCapabilityContractRegistry<T>(params: {
plugins: RegistrablePlugin[];
select: (captured: ReturnType<typeof createCapturedPluginRegistration>) => T[];
}): CapabilityContractEntry<T>[] {
return params.plugins.flatMap((plugin) => {
const captured = captureRegistrations(plugin);
return captured.providers.map((provider) => ({
return params.select(captured).map((provider) => ({
pluginId: plugin.id,
provider,
}));
},
);
});
}
export const providerContractRegistry: ProviderContractEntry[] = buildCapabilityContractRegistry({
plugins: bundledProviderPlugins,
select: (captured) => captured.providers,
});
export const webSearchProviderContractRegistry: WebSearchProviderContractEntry[] =
bundledWebSearchPlugins.flatMap((plugin) => {
@@ -159,21 +161,15 @@ export const webSearchProviderContractRegistry: WebSearchProviderContractEntry[]
});
export const speechProviderContractRegistry: SpeechProviderContractEntry[] =
bundledSpeechPlugins.flatMap((plugin) => {
const captured = captureRegistrations(plugin);
return captured.speechProviders.map((provider) => ({
pluginId: plugin.id,
provider,
}));
buildCapabilityContractRegistry({
plugins: bundledSpeechPlugins,
select: (captured) => captured.speechProviders,
});
export const mediaUnderstandingProviderContractRegistry: MediaUnderstandingProviderContractEntry[] =
bundledMediaUnderstandingPlugins.flatMap((plugin) => {
const captured = captureRegistrations(plugin);
return captured.mediaUnderstandingProviders.map((provider) => ({
pluginId: plugin.id,
provider,
}));
buildCapabilityContractRegistry({
plugins: bundledMediaUnderstandingPlugins,
select: (captured) => captured.mediaUnderstandingProviders,
});
const bundledPluginRegistrationList = [

View File

@@ -104,29 +104,20 @@ export type PluginProviderRegistration = {
rootDir?: string;
};
export type PluginWebSearchProviderRegistration = {
type PluginOwnedProviderRegistration<T extends { id: string }> = {
pluginId: string;
pluginName?: string;
provider: WebSearchProviderPlugin;
provider: T;
source: string;
rootDir?: string;
};
export type PluginSpeechProviderRegistration = {
pluginId: string;
pluginName?: string;
provider: SpeechProviderPlugin;
source: string;
rootDir?: string;
};
export type PluginMediaUnderstandingProviderRegistration = {
pluginId: string;
pluginName?: string;
provider: MediaUnderstandingProviderPlugin;
source: string;
rootDir?: string;
};
export type PluginSpeechProviderRegistration =
PluginOwnedProviderRegistration<SpeechProviderPlugin>;
export type PluginMediaUnderstandingProviderRegistration =
PluginOwnedProviderRegistration<MediaUnderstandingProviderPlugin>;
export type PluginWebSearchProviderRegistration =
PluginOwnedProviderRegistration<WebSearchProviderPlugin>;
export type PluginHookRegistration = {
pluginId: string;
@@ -576,13 +567,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
const registerUniqueProviderLike = <
T extends { id: string },
R extends {
pluginId: string;
pluginName?: string;
provider: T;
source: string;
rootDir?: string;
},
R extends PluginOwnedProviderRegistration<T>,
>(params: {
record: PluginRecord;
provider: T;

View File

@@ -55,6 +55,14 @@ describe("plugin runtime command execution", () => {
expect(runtime.events.onSessionTranscriptUpdate).toBe(onSessionTranscriptUpdate);
});
it("exposes runtime.mediaUnderstanding helpers and keeps stt as an alias", () => {
const runtime = createPluginRuntime();
expect(typeof runtime.mediaUnderstanding.runFile).toBe("function");
expect(typeof runtime.mediaUnderstanding.describeImageFile).toBe("function");
expect(typeof runtime.mediaUnderstanding.describeVideoFile).toBe("function");
expect(runtime.mediaUnderstanding.transcribeAudioFile).toBe(runtime.stt.transcribeAudioFile);
});
it("exposes runtime.system.requestHeartbeatNow", () => {
const runtime = createPluginRuntime();
expect(runtime.system.requestHeartbeatNow).toBe(requestHeartbeatNow);