diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e5d3225ad..023291ba749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Docs: https://docs.openclaw.ai - Feishu: suppress duplicate text when replies send native voice media while preserving captions for ordinary audio files and falling back to text plus attachment links when voice uploads fail. - Feishu: send the skipped reply text when `audioAsVoice` falls back to a generic file attachment after transcode failure, so voice-intent replies do not lose their caption. - TTS/plugins: activate the configured speech provider plugin during Gateway startup, so Microsoft and Local CLI voice replies work immediately after selecting them instead of staying invisible in the startup plugin set. Fixes #76481. Thanks @amknight. +- TTS/plugins: include speech providers selected through inherited agent, channel, and account TTS personas during Gateway startup, matching the runtime TTS config merge. Carries forward #76481. Thanks @amknight. - Feishu: keep packaged Feishu startup from bundling the Lark SDK's ESM `__dirname` path by loading the SDK as a plugin-local runtime dependency. Fixes #76291 and #76494. (#76392) Thanks @zqchris. - Plugins/npm: build package-local runtime dist files for publishable plugins and stop listing root-package-excluded plugin sidecars in the core package metadata, so npm plugin installs such as `@openclaw/diffs` and `@openclaw/discord` no longer publish source-only runtime payloads. Fixes #76426. Thanks @PrinceOfEgypt. - Channels/secrets: resolve SecretRef-backed channel credentials through external plugin secret contracts after the plugin split, covering runtime startup, target discovery, webhook auth, disabled-account enumeration, and late-bound web_search config. Fixes #76371. (#76449) Thanks @joshavant and @neeravmakwana. diff --git a/src/plugins/channel-plugin-ids.test.ts b/src/plugins/channel-plugin-ids.test.ts index 6ea59c958cd..728863676b5 100644 --- a/src/plugins/channel-plugin-ids.test.ts +++ b/src/plugins/channel-plugin-ids.test.ts @@ -635,6 +635,68 @@ describe("resolveGatewayStartupPluginIds", () => { } as OpenClawConfig, ["browser", "microsoft", "memory-core"], ], + [ + "includes agent-inherited active persona speech providers at startup", + { + channels: {}, + messages: { + tts: { + personas: { + narrator: { + label: "Narrator", + provider: "microsoft", + }, + }, + }, + }, + agents: { + list: [{ id: "reader", tts: { persona: "narrator" } }], + }, + } as OpenClawConfig, + ["browser", "microsoft", "memory-core"], + ], + [ + "includes channel-inherited active persona speech providers at startup", + { + channels: { + "demo-channel": { tts: { persona: "narrator" } }, + }, + messages: { + tts: { + personas: { + narrator: { + label: "Narrator", + provider: "microsoft", + }, + }, + }, + }, + } as OpenClawConfig, + ["demo-channel", "browser", "microsoft", "memory-core"], + ], + [ + "includes account-inherited active persona speech providers at startup", + { + channels: { + "demo-channel": { + accounts: { + primary: { tts: { persona: "narrator" } }, + }, + }, + }, + messages: { + tts: { + personas: { + narrator: { + label: "Narrator", + provider: "microsoft", + }, + }, + }, + }, + } as OpenClawConfig, + ["demo-channel", "browser", "microsoft", "memory-core"], + ], [ "honors disabled speech provider config blocks at startup", { diff --git a/src/plugins/gateway-startup-speech-providers.ts b/src/plugins/gateway-startup-speech-providers.ts index c42771a7288..b7f0bc60cd6 100644 --- a/src/plugins/gateway-startup-speech-providers.ts +++ b/src/plugins/gateway-startup-speech-providers.ts @@ -1,5 +1,6 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; +import { resolveEffectiveTtsConfig } from "../tts/tts-config.js"; const TTS_PROVIDER_CONFIG_RESERVED_KEYS = new Set([ "auto", @@ -141,33 +142,43 @@ function addConfiguredTtsProviderIds(target: Set, value: unknown): void export function collectConfiguredSpeechProviderIds(config: OpenClawConfig): ReadonlySet { const configured = new Set(); - addConfiguredTtsProviderIds(configured, config.messages?.tts); + addConfiguredTtsProviderIds(configured, resolveEffectiveTtsConfig(config)); const agents = config.agents; if (isRecord(agents) && Array.isArray(agents.list)) { for (const agent of agents.list) { if (isRecord(agent)) { - addConfiguredTtsProviderIds(configured, agent.tts); + if (typeof agent.id === "string") { + addConfiguredTtsProviderIds( + configured, + resolveEffectiveTtsConfig(config, { agentId: agent.id }), + ); + } else { + addConfiguredTtsProviderIds(configured, agent.tts); + } } } } const channels = config.channels; if (isRecord(channels)) { - for (const channelConfig of Object.values(channels)) { + for (const [channelId, channelConfig] of Object.entries(channels)) { if (!isRecord(channelConfig)) { continue; } - addConfiguredTtsProviderIds(configured, channelConfig.tts); + addConfiguredTtsProviderIds(configured, resolveEffectiveTtsConfig(config, { channelId })); if (isRecord(channelConfig.voice)) { addConfiguredTtsProviderIds(configured, channelConfig.voice.tts); } if (isRecord(channelConfig.accounts)) { - for (const accountConfig of Object.values(channelConfig.accounts)) { + for (const [accountId, accountConfig] of Object.entries(channelConfig.accounts)) { if (!isRecord(accountConfig)) { continue; } - addConfiguredTtsProviderIds(configured, accountConfig.tts); + addConfiguredTtsProviderIds( + configured, + resolveEffectiveTtsConfig(config, { channelId, accountId }), + ); if (isRecord(accountConfig.voice)) { addConfiguredTtsProviderIds(configured, accountConfig.voice.tts); }