diff --git a/docs/.generated/plugin-sdk-api-baseline.sha256 b/docs/.generated/plugin-sdk-api-baseline.sha256 index 84528541cbf..b05d07c3929 100644 --- a/docs/.generated/plugin-sdk-api-baseline.sha256 +++ b/docs/.generated/plugin-sdk-api-baseline.sha256 @@ -1,2 +1,2 @@ -83c7b0a2953a24cac8d576bb561948ccd70d4bac3c06d0a39814a766b7a330b6 plugin-sdk-api-baseline.json -387c0a4b34b0edd3c576658d71f13cdeb64d74bc949c36698798563de08f570d plugin-sdk-api-baseline.jsonl +51b5c2078621b696301cbeb45035ace7498f9ab8433517f172cf83a60bbe7692 plugin-sdk-api-baseline.json +212725570b9e5577c7d36c652a9203450bbfecb4a43cfcd58b743e7014e1dba3 plugin-sdk-api-baseline.jsonl diff --git a/scripts/lib/plugin-sdk-doc-metadata.ts b/scripts/lib/plugin-sdk-doc-metadata.ts index 7ce8e43d4c0..6e57d5f9ed0 100644 --- a/scripts/lib/plugin-sdk-doc-metadata.ts +++ b/scripts/lib/plugin-sdk-doc-metadata.ts @@ -101,6 +101,15 @@ export const pluginSdkDocMetadata = { "runtime-store": { category: "runtime", }, + "agent-runtime": { + category: "runtime", + }, + "speech-core": { + category: "provider", + }, + "tts-runtime": { + category: "runtime", + }, "allow-from": { category: "utilities", }, diff --git a/src/plugin-sdk/agent-runtime.ts b/src/plugin-sdk/agent-runtime.ts index 3a689433964..3c127510108 100644 --- a/src/plugin-sdk/agent-runtime.ts +++ b/src/plugin-sdk/agent-runtime.ts @@ -10,6 +10,7 @@ export * from "../agents/identity.js"; export * from "../agents/model-auth-markers.js"; export * from "../agents/model-auth.js"; export * from "../agents/model-catalog.js"; +export * from "../agents/model-catalog-scope.js"; export * from "../agents/model-selection.js"; export * from "../agents/simple-completion-runtime.js"; export * from "../agents/pi-embedded-block-chunker.js"; diff --git a/src/plugin-sdk/channel-entry-contract.ts b/src/plugin-sdk/channel-entry-contract.ts index cc3f6bdc7e4..c762bacd111 100644 --- a/src/plugin-sdk/channel-entry-contract.ts +++ b/src/plugin-sdk/channel-entry-contract.ts @@ -3,6 +3,7 @@ import { createRequire } from "node:module"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { emptyChannelConfigSchema } from "../channels/plugins/config-schema.js"; +import type { ChannelOutboundAdapter } from "../channels/plugins/types.adapters.js"; import type { ChannelConfigSchema } from "../channels/plugins/types.config.js"; import type { ChannelLegacyStateMigrationPlan } from "../channels/plugins/types.core.js"; import type { ChannelPlugin } from "../channels/plugins/types.plugin.js"; @@ -52,6 +53,7 @@ type DefineBundledChannelEntryOptions = { description: string; importMetaUrl: string; plugin: BundledEntryModuleRef; + outbound?: BundledEntryModuleRef; secrets?: BundledEntryModuleRef; configSchema?: ChannelEntryConfigSchema | (() => ChannelEntryConfigSchema); runtime?: BundledEntryModuleRef; @@ -108,6 +110,9 @@ export type BundledChannelEntryContract = { features?: BundledChannelEntryFeatures; register: (api: OpenClawPluginApi) => void; loadChannelPlugin: (options?: BundledEntryModuleLoadOptions) => TPlugin; + loadChannelOutbound?: ( + options?: BundledEntryModuleLoadOptions, + ) => ChannelOutboundAdapter | undefined; loadChannelSecrets?: ( options?: BundledEntryModuleLoadOptions, ) => ChannelPlugin["secrets"] | undefined; @@ -435,6 +440,7 @@ export function defineBundledChannelEntry({ description, importMetaUrl, plugin, + outbound, secrets, configSchema, runtime, @@ -449,6 +455,14 @@ export function defineBundledChannelEntry({ : ((configSchema ?? emptyChannelConfigSchema()) as ChannelEntryConfigSchema); const loadChannelPlugin = (options?: BundledEntryModuleLoadOptions) => loadBundledEntryExportSync(importMetaUrl, plugin, options); + const loadChannelOutbound = outbound + ? (options?: BundledEntryModuleLoadOptions) => + loadBundledEntryExportSync( + importMetaUrl, + outbound, + options, + ) + : undefined; const loadChannelSecrets = secrets ? (options?: BundledEntryModuleLoadOptions) => loadBundledEntryExportSync( @@ -511,6 +525,7 @@ export function defineBundledChannelEntry({ profile("bundled-register:registerFull", () => registerFull?.(api)); }, loadChannelPlugin, + ...(loadChannelOutbound ? { loadChannelOutbound } : {}), ...(loadChannelSecrets ? { loadChannelSecrets } : {}), ...(loadChannelAccountInspector ? { loadChannelAccountInspector } : {}), ...(setChannelRuntime ? { setChannelRuntime } : {}), diff --git a/src/plugin-sdk/speech-core.ts b/src/plugin-sdk/speech-core.ts index 76de5dfe624..7e198504095 100644 --- a/src/plugin-sdk/speech-core.ts +++ b/src/plugin-sdk/speech-core.ts @@ -35,6 +35,7 @@ export { parseTtsDirectives } from "../tts/directives.js"; export { canonicalizeSpeechProviderId, getSpeechProvider, + listLoadedSpeechProviders, listSpeechProviders, normalizeSpeechProviderId, } from "../tts/provider-registry.js"; diff --git a/src/plugin-sdk/tts-runtime.ts b/src/plugin-sdk/tts-runtime.ts index 57c10f98dae..709f8ee56ba 100644 --- a/src/plugin-sdk/tts-runtime.ts +++ b/src/plugin-sdk/tts-runtime.ts @@ -31,6 +31,10 @@ function loadFacadeModule(): FacadeModule { }); } +export function prewarmTtsRuntimeFacade(): void { + loadFacadeModule(); +} + export const _test: FacadeModule["_test"] = createLazyFacadeObjectValue( () => loadFacadeModule()._test, ); diff --git a/src/tts/provider-registry.ts b/src/tts/provider-registry.ts index b673c21790e..308b158f56c 100644 --- a/src/tts/provider-registry.ts +++ b/src/tts/provider-registry.ts @@ -1,4 +1,5 @@ import type { OpenClawConfig } from "../config/types.js"; +import { getActiveRuntimePluginRegistry } from "../plugins/active-runtime-registry.js"; import { resolvePluginCapabilityProvider, resolvePluginCapabilityProviders, @@ -17,6 +18,10 @@ function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProvide }); } +function resolveLoadedSpeechProviderPluginEntries(): SpeechProviderPlugin[] { + return (getActiveRuntimePluginRegistry()?.speechProviders ?? []).map((entry) => entry.provider); +} + const defaultSpeechProviderRegistryResolver: SpeechProviderRegistryResolver = { getProvider: (providerId, cfg) => resolvePluginCapabilityProvider({ @@ -31,7 +36,19 @@ const defaultSpeechProviderRegistry = createSpeechProviderRegistry( defaultSpeechProviderRegistryResolver, ); +const loadedSpeechProviderRegistry = createSpeechProviderRegistry({ + getProvider: (providerId) => + resolveLoadedSpeechProviderPluginEntries().find((provider) => { + if (provider.id === providerId) { + return true; + } + return provider.aliases?.includes(providerId) ?? false; + }), + listProviders: () => resolveLoadedSpeechProviderPluginEntries(), +}); + export const listSpeechProviders = defaultSpeechProviderRegistry.listSpeechProviders; +export const listLoadedSpeechProviders = loadedSpeechProviderRegistry.listSpeechProviders; export const getSpeechProvider = defaultSpeechProviderRegistry.getSpeechProvider; export const canonicalizeSpeechProviderId = defaultSpeechProviderRegistry.canonicalizeSpeechProviderId;