diff --git a/src/OpenClaw.Shared/Models.cs b/src/OpenClaw.Shared/Models.cs index e0dc52fd0..cb3a77cff 100644 --- a/src/OpenClaw.Shared/Models.cs +++ b/src/OpenClaw.Shared/Models.cs @@ -98,6 +98,7 @@ public class OpenClawNotification { public string Title { get; set; } = ""; public string Message { get; set; } = ""; + public string? FullMessage { get; set; } public string Type { get; set; } = ""; public bool IsChat { get; set; } = false; // True if from chat response diff --git a/src/OpenClaw.Shared/OpenClawGatewayClient.cs b/src/OpenClaw.Shared/OpenClawGatewayClient.cs index a5e758a25..5d00b6604 100644 --- a/src/OpenClaw.Shared/OpenClawGatewayClient.cs +++ b/src/OpenClaw.Shared/OpenClawGatewayClient.cs @@ -3248,6 +3248,7 @@ private void EmitChatNotification(string text, string? sessionKey = null) var notification = new OpenClawNotification { Message = displayText, + FullMessage = text, IsChat = true, SessionKey = sessionKey }; diff --git a/src/OpenClaw.Tray.WinUI/App.xaml.cs b/src/OpenClaw.Tray.WinUI/App.xaml.cs index 4126d4def..d2ca42036 100644 --- a/src/OpenClaw.Tray.WinUI/App.xaml.cs +++ b/src/OpenClaw.Tray.WinUI/App.xaml.cs @@ -2672,6 +2672,8 @@ private void OnGatewayNotificationReceived(object? sender, OpenClawNotification // if the user enabled "Read responses aloud". if (notification.IsChat && !string.IsNullOrEmpty(notification.Message)) { + var speechText = notification.FullMessage ?? notification.Message; + // Suppress TTS/voice overlay when the user has aborted the response. if (ChatProvider?.IsResponseSuppressed == true) return; @@ -2692,7 +2694,7 @@ private void OnGatewayNotificationReceived(object? sender, OpenClawNotification // TTS: read response aloud whenever the toggle is on (any chat surface). if (_settings?.VoiceTtsEnabled == true) { - _ = (_chatCoordinator?.SpeakResponseAsync(notification.Message) ?? Task.CompletedTask); + _ = (_chatCoordinator?.SpeakResponseAsync(speechText) ?? Task.CompletedTask); } } diff --git a/tests/OpenClaw.Shared.Tests/OpenClawGatewayClientTests.cs b/tests/OpenClaw.Shared.Tests/OpenClawGatewayClientTests.cs index be62eee5c..1008e1560 100644 --- a/tests/OpenClaw.Shared.Tests/OpenClawGatewayClientTests.cs +++ b/tests/OpenClaw.Shared.Tests/OpenClawGatewayClientTests.cs @@ -990,6 +990,37 @@ public void ProcessRawMessage_SessionMessageAssistantNotification_DependsOnFinal } } + [Fact] + public void ProcessRawMessage_SessionMessageAssistantNotification_PreservesFullMessage() + { + var helper = new GatewayClientTestHelper(); + OpenClawNotification? notification = null; + helper.Client.NotificationReceived += (_, value) => notification = value; + + var fullMessage = new string('x', 240); + + helper.ProcessRawMessage($$""" + { + "type": "event", + "event": "session.message", + "payload": { + "sessionKey": "agent:main:whatsapp:direct:+15551234567", + "message": { + "role": "assistant", + "content": "{{fullMessage}}", + "timestamp": 1781631280633 + }, + "state": "final" + } + } + """); + + Assert.NotNull(notification); + Assert.True(notification!.IsChat); + Assert.Equal(fullMessage[..200] + "…", notification.Message); + Assert.Equal(fullMessage, notification.FullMessage); + } + [Fact] public void ProcessRawMessage_AgentEventLogsRawLengthWithoutPayloadContent() {