From a42b5b08f4585fd0e130ffe978261e9a5a51d344 Mon Sep 17 00:00:00 2001 From: Sanju Sivalingam Date: Fri, 20 Feb 2026 02:16:39 +0530 Subject: [PATCH] fix: address critical review issues in voice overlay - Clean up voice sessions on WebSocket disconnect (prevents timer leak) - Guard against missing LLM config in voice_stop send path - Return overlay to idle on goal_failed (prevents stuck UI) --- .../com/thisux/droidclaw/connection/CommandRouter.kt | 1 + server/src/ws/device.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/thisux/droidclaw/connection/CommandRouter.kt b/android/app/src/main/java/com/thisux/droidclaw/connection/CommandRouter.kt index f51e7d8..cce74e7 100644 --- a/android/app/src/main/java/com/thisux/droidclaw/connection/CommandRouter.kt +++ b/android/app/src/main/java/com/thisux/droidclaw/connection/CommandRouter.kt @@ -79,6 +79,7 @@ class CommandRouter( } "goal_failed" -> { currentGoalStatus.value = GoalStatus.Failed + ConnectionService.instance?.overlay?.returnToIdle() Log.i(TAG, "Goal failed: ${msg.message}") } diff --git a/server/src/ws/device.ts b/server/src/ws/device.ts index 4b462d1..0990efd 100644 --- a/server/src/ws/device.ts +++ b/server/src/ws/device.ts @@ -410,7 +410,13 @@ export async function handleDeviceMessage( .where(eq(llmConfig.userId, userId)) .limit(1); - const groqKey = configs[0]?.apiKey ?? ""; + if (configs.length === 0 || !configs[0].apiKey) { + handleVoiceCancel(deviceId); + sendToDevice(ws, { type: "transcript_final", text: "" }); + break; + } + + const groqKey = configs[0].apiKey; const transcript = await handleVoiceSend(ws, deviceId, groqKey); if (transcript) { @@ -488,6 +494,7 @@ export function handleDeviceClose( active.abort.abort(); activeSessions.delete(deviceId); } + handleVoiceCancel(deviceId); sessions.removeDevice(deviceId); // Update device status in DB