diff --git a/src/auto-reply/reply/dispatch-acp.test.ts b/src/auto-reply/reply/dispatch-acp.test.ts index d5e89fbc770..5f9fb367fa4 100644 --- a/src/auto-reply/reply/dispatch-acp.test.ts +++ b/src/auto-reply/reply/dispatch-acp.test.ts @@ -440,8 +440,8 @@ describe("tryDispatchAcpReply", () => { setReadyAcpResolution(); // Configure TTS mode as "final" but TTS synthesis returns no mediaUrl ttsMocks.resolveTtsConfig.mockReturnValue({ mode: "final" }); - // Mock TTS to return no mediaUrl for this test only (use Once to avoid cross-test leak) - ttsMocks.maybeApplyTtsToPayload.mockResolvedValueOnce( + // Mock TTS to return no mediaUrl for all calls in this test + ttsMocks.maybeApplyTtsToPayload.mockResolvedValue( {} as ReturnType, ); @@ -456,21 +456,16 @@ describe("tryDispatchAcpReply", () => { const result = await runDispatch({ bodyForAgent: "run acp", dispatcher, - shouldRouteToOriginating: true, + shouldRouteToOriginating: false, // Use non-routed flow to test fallback logic }); // Should deliver final text as fallback when TTS produced no media. - // Note: ACP sends block first (during flush on done), then final fallback. - // So routeReply is called twice: 1 for block + 1 for final. - expect(result?.counts.block).toBeGreaterThanOrEqual(1); + // In non-routed flow, block delivery is not tracked, so fallback should run. expect(result?.counts.final).toBe(1); - expect(routeMocks.routeReply).toHaveBeenCalledTimes(2); // Verify final delivery contains the expected text - expect(routeMocks.routeReply).toHaveBeenCalledWith( + expect(dispatcher.sendFinalReply).toHaveBeenCalledWith( expect.objectContaining({ - payload: expect.objectContaining({ - text: "CODEX_OK", - }), + text: "CODEX_OK", }), ); }); diff --git a/src/auto-reply/reply/dispatch-acp.ts b/src/auto-reply/reply/dispatch-acp.ts index 74979cef1ac..f5b46f077b0 100644 --- a/src/auto-reply/reply/dispatch-acp.ts +++ b/src/auto-reply/reply/dispatch-acp.ts @@ -315,10 +315,10 @@ export async function tryDispatchAcpReply(params: { const ttsMode = resolveTtsConfig(params.cfg).mode ?? "final"; const accumulatedBlockText = delivery.getAccumulatedBlockText(); const routedCounts = delivery.getRoutedCounts(); - // Attempt final TTS synthesis for ttsMode="final" (independent of delivery status). + // Attempt final TTS synthesis for ttsMode="final" (only if we have text to synthesize). // This ensures routed ACP flows still get final audio even after block delivery. let ttsSucceeded = false; - if (ttsMode === "final" && ttsMode !== "all") { + if (ttsMode === "final" && accumulatedBlockText.trim()) { try { const ttsSyntheticReply = await maybeApplyTtsToPayload({ payload: { text: accumulatedBlockText },