From b84e57fca38deacfa9834a82f7e270b32562ae1b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 05:18:34 +0100 Subject: [PATCH] fix(tts): use resolved config for tool synthesis --- CHANGELOG.md | 1 + src/agents/openclaw-tools.ts | 2 +- src/agents/openclaw-tools.tts-config.test.ts | 62 ++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/agents/openclaw-tools.tts-config.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 909285547e6..d5787104b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Docs: https://docs.openclaw.ai - Exec approvals: allow bare command-name allowlist patterns to match PATH-resolved executable basenames without trusting `./tool` or absolute path-selected binaries. Fixes #71315. Thanks @chen-zhang-cs-code and @dengluozhang. - Config/recovery: skip whole-file last-known-good rollback when invalidity is scoped to `plugins.entries.*`, preserving unrelated user settings during plugin schema or host-version skew. Fixes #71289. Thanks @jalehman. - Agents/tools: keep resolved reply-run configs from being overwritten by stale runtime snapshots, and let empty web runtime metadata fall back to configured provider auto-detection so standard and queued turns expose the same tool set. Fixes #71355. Thanks @c-g14. +- Agents/TTS: pass the resolved shared config into the `tts` tool, so tool-triggered speech uses configured providers and voices instead of falling back to a fresh config load. - Agents/TTS: preserve voice media when a tool-generated reply is paired with an exact `NO_REPLY` sentinel, stripping the sentinel text instead of dropping the audio payload. Fixes #66092. - Compaction: honor explicit `agents.defaults.compaction.keepRecentTokens` for manual `/compact`, re-distill safeguard summaries instead of snowballing previous summaries, and enable safeguard summary quality checks by default. Fixes #71357. Thanks @WhiteGiverMa. - Sessions: honor configured `session.maintenance` settings during load-time maintenance instead of falling back to default entry caps. Fixes #71356. Thanks @comolago. diff --git a/src/agents/openclaw-tools.ts b/src/agents/openclaw-tools.ts index ce3e4eaf48a..e3055ba0a5b 100644 --- a/src/agents/openclaw-tools.ts +++ b/src/agents/openclaw-tools.ts @@ -246,7 +246,7 @@ export function createOpenClawTools( ...(!embedded && messageTool ? [messageTool] : []), createTtsTool({ agentChannel: options?.agentChannel, - config: options?.config, + config: resolvedConfig, }), ...collectPresentOpenClawTools([imageGenerateTool, musicGenerateTool, videoGenerateTool]), ...(embedded diff --git a/src/agents/openclaw-tools.tts-config.test.ts b/src/agents/openclaw-tools.tts-config.test.ts new file mode 100644 index 00000000000..0758ff22f4b --- /dev/null +++ b/src/agents/openclaw-tools.tts-config.test.ts @@ -0,0 +1,62 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; + +const mocks = vi.hoisted(() => ({ + textToSpeech: vi.fn(async () => ({ + success: true, + audioPath: "/tmp/openclaw/tts-config-test.opus", + provider: "microsoft", + voiceCompatible: true, + })), +})); + +vi.mock("../tts/tts.js", () => ({ + textToSpeech: mocks.textToSpeech, +})); + +describe("createOpenClawTools TTS config wiring", () => { + beforeEach(() => { + mocks.textToSpeech.mockClear(); + }); + + it("passes the resolved shared config into the tts tool", async () => { + const injectedConfig = { + messages: { + tts: { + auto: "always", + provider: "microsoft", + providers: { + microsoft: { + voice: "en-US-AvaNeural", + }, + }, + }, + }, + } satisfies OpenClawConfig; + + const { __testing, createOpenClawTools } = await import("./openclaw-tools.js"); + __testing.setDepsForTest({ config: injectedConfig }); + + try { + const tool = createOpenClawTools({ + disableMessageTool: true, + disablePluginTools: true, + }).find((candidate) => candidate.name === "tts"); + + if (!tool) { + throw new Error("missing tts tool"); + } + + await tool.execute("call-1", { text: "hello from config" }); + + expect(mocks.textToSpeech).toHaveBeenCalledWith( + expect.objectContaining({ + text: "hello from config", + cfg: injectedConfig, + }), + ); + } finally { + __testing.setDepsForTest(); + } + }); +});