


















@@ -2352,6 +2352,47 @@ describe("dispatchReplyFromConfig", () => {
23522352expect(finalPayload?.text).toBeUndefined();
23532353});
235423542355+it("normalizes accumulated block TTS-only media before final delivery", async () => {
2356+setNoAbort();
2357+ttsMocks.state.synthesizeFinalAudio = true;
2358+replyMediaPathMocks.createReplyMediaPathNormalizer.mockReturnValue(
2359+async (payload: ReplyPayload) => ({
2360+ ...payload,
2361+mediaUrl: "/tmp/openclaw-media/normalized-tts.ogg",
2362+mediaUrls: ["/tmp/openclaw-media/normalized-tts.ogg"],
2363+}),
2364+);
2365+const dispatcher = createDispatcher();
2366+const ctx = buildTestCtx({
2367+Provider: "feishu",
2368+Surface: "feishu",
2369+SessionKey: "agent:main:feishu:ou_user",
2370+});
2371+const replyResolver = async (
2372+_ctx: MsgContext,
2373+opts?: GetReplyOptions,
2374+): Promise<ReplyPayload | undefined> => {
2375+await opts?.onBlockReply?.({ text: "Hello from block streaming." });
2376+return undefined;
2377+};
2378+2379+await dispatchReplyFromConfig({ ctx, cfg: emptyConfig, dispatcher, replyResolver });
2380+2381+expect(replyMediaPathMocks.createReplyMediaPathNormalizer).toHaveBeenCalledWith(
2382+expect.objectContaining({
2383+messageProvider: "feishu",
2384+}),
2385+);
2386+expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(
2387+expect.objectContaining({
2388+mediaUrl: "/tmp/openclaw-media/normalized-tts.ogg",
2389+mediaUrls: ["/tmp/openclaw-media/normalized-tts.ogg"],
2390+audioAsVoice: true,
2391+spokenText: "Hello from block streaming.",
2392+}),
2393+);
2394+});
2395+23552396it("closes oneshot ACP sessions after the turn completes", async () => {
23562397setNoAbort();
23572398const runtime = createAcpRuntime([{ type: "done" }]);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。