From 1e5d740bf28fc29877c930c4987f1ad5aec8d6bc Mon Sep 17 00:00:00 2001 From: KoheiYamashita Date: Sat, 21 Feb 2026 04:16:36 +0900 Subject: [PATCH] feat: add warning/error message type and skip TTS in voice mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `type: "warning"` to context compression and memory optimization notifications on the Go server. Propagate messageType through the Android data layer (Entity → Mapper → Domain) so voice mode can detect warning/error messages and display them without reading aloud. Co-Authored-By: Claude Opus 4.6 --- .../android/core/data/local/entity/MessageEntity.kt | 3 ++- .../io/picoclaw/android/core/data/mapper/MessageMapper.kt | 6 ++++-- .../io/picoclaw/android/core/domain/model/ChatMessage.kt | 3 ++- .../picoclaw/android/feature/chat/voice/VoiceModeManager.kt | 6 +++++- .../picoclaw/android/feature/chat/voice/VoiceModeOverlay.kt | 2 +- pkg/agent/loop.go | 2 ++ 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/android/core/data/src/main/java/io/picoclaw/android/core/data/local/entity/MessageEntity.kt b/android/core/data/src/main/java/io/picoclaw/android/core/data/local/entity/MessageEntity.kt index a722a6ad8..61ba48928 100644 --- a/android/core/data/src/main/java/io/picoclaw/android/core/data/local/entity/MessageEntity.kt +++ b/android/core/data/src/main/java/io/picoclaw/android/core/data/local/entity/MessageEntity.kt @@ -10,5 +10,6 @@ data class MessageEntity( val sender: String, val imagePathList: String?, val timestamp: Long, - val status: String + val status: String, + val messageType: String? = null ) diff --git a/android/core/data/src/main/java/io/picoclaw/android/core/data/mapper/MessageMapper.kt b/android/core/data/src/main/java/io/picoclaw/android/core/data/mapper/MessageMapper.kt index 674ae404c..2954b8422 100644 --- a/android/core/data/src/main/java/io/picoclaw/android/core/data/mapper/MessageMapper.kt +++ b/android/core/data/src/main/java/io/picoclaw/android/core/data/mapper/MessageMapper.kt @@ -36,7 +36,8 @@ object MessageMapper { sender = MessageSender.valueOf(entity.sender), images = images, timestamp = entity.timestamp, - status = MessageStatus.valueOf(entity.status) + status = MessageStatus.valueOf(entity.status), + messageType = entity.messageType ) } @@ -47,7 +48,8 @@ object MessageMapper { sender = MessageSender.AGENT.name, imagePathList = null, timestamp = System.currentTimeMillis(), - status = MessageStatus.RECEIVED.name + status = MessageStatus.RECEIVED.name, + messageType = dto.type ) } diff --git a/android/core/domain/src/main/java/io/picoclaw/android/core/domain/model/ChatMessage.kt b/android/core/domain/src/main/java/io/picoclaw/android/core/domain/model/ChatMessage.kt index 719193cd8..59716f7d3 100644 --- a/android/core/domain/src/main/java/io/picoclaw/android/core/domain/model/ChatMessage.kt +++ b/android/core/domain/src/main/java/io/picoclaw/android/core/domain/model/ChatMessage.kt @@ -6,7 +6,8 @@ data class ChatMessage( val sender: MessageSender, val images: List = emptyList(), val timestamp: Long, - val status: MessageStatus + val status: MessageStatus, + val messageType: String? = null ) enum class MessageSender { USER, AGENT } diff --git a/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeManager.kt b/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeManager.kt index 55ec53306..b0ccf6674 100644 --- a/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeManager.kt +++ b/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeManager.kt @@ -97,7 +97,11 @@ class VoiceModeManager( msg.status == MessageStatus.RECEIVED ) { spokenIds.add(msg.id) - speechQueue.send(msg.content) + if (msg.messageType == "warning" || msg.messageType == "error") { + _state.update { it.copy(responseText = msg.content) } + } else { + speechQueue.send(msg.content) + } } } } diff --git a/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeOverlay.kt b/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeOverlay.kt index 8d5cb1f02..8a6930575 100644 --- a/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeOverlay.kt +++ b/android/feature/chat/src/main/java/io/picoclaw/android/feature/chat/voice/VoiceModeOverlay.kt @@ -144,7 +144,7 @@ fun VoiceModeOverlay( ) } - if (state.responseText.isNotEmpty() && state.phase == VoicePhase.SPEAKING) { + if (state.responseText.isNotEmpty() && (state.phase == VoicePhase.SPEAKING || state.phase == VoicePhase.THINKING)) { Spacer(modifier = Modifier.height(16.dp)) Text( text = state.responseText, diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index dd247b973..350d4264c 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -663,6 +663,7 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, messages []providers.M Channel: opts.Channel, ChatID: opts.ChatID, Content: "⚠️ Context window exceeded. Compressing history and retrying...", + Type: "warning", }) } @@ -947,6 +948,7 @@ func (al *AgentLoop) maybeSummarize(sessionKey, channel, chatID string) { Channel: channel, ChatID: chatID, Content: "⚠️ Memory threshold reached. Optimizing conversation history...", + Type: "warning", }) } al.summarizeSession(sessionKey)