fix: prevent TTS replay of historical messages on session resume
Browse filesuseAutoTTS now detects session switches (resume) via UUID discontinuity
and treats all loaded messages as history to skip TTS. Only subsequent
real-time assistant replies will trigger speech.
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com>
- src/hooks/useAutoTTS.ts +28 -5
src/hooks/useAutoTTS.ts
CHANGED
|
@@ -40,11 +40,34 @@ export function useAutoTTS(messages: RenderableMessage[], isLoading?: boolean):
|
|
| 40 |
const settings = getInitialSettings()
|
| 41 |
if (!settings.voiceAutoTTS || !settings.voiceEnabled) return
|
| 42 |
|
| 43 |
-
//
|
| 44 |
-
//
|
| 45 |
-
//
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
}
|
| 49 |
wasLoadingRef.current = isLoading ?? false
|
| 50 |
|
|
|
|
| 40 |
const settings = getInitialSettings()
|
| 41 |
if (!settings.voiceAutoTTS || !settings.voiceEnabled) return
|
| 42 |
|
| 43 |
+
// Detect session switch (mid-session /resume): if we already took a
|
| 44 |
+
// history snapshot for a previous session but the incoming messages are
|
| 45 |
+
// entirely different (no overlapping UUIDs), reset and re-snapshot so the
|
| 46 |
+
// resumed session's history isn't re-spoken.
|
| 47 |
+
if (historyIdsRef.current && messages.length > 0) {
|
| 48 |
+
const isSessionSwitch = messages.every(
|
| 49 |
+
m => !historyIdsRef.current!.has(m.uuid),
|
| 50 |
+
)
|
| 51 |
+
if (isSessionSwitch) {
|
| 52 |
+
historyIdsRef.current = null
|
| 53 |
+
wasLoadingRef.current = false
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
// Capture history snapshot on the transition from idle → active, or on
|
| 58 |
+
// first render with pre-loaded messages (resume path).
|
| 59 |
+
if (!historyIdsRef.current && !wasLoadingRef.current) {
|
| 60 |
+
if (isLoading || messages.length > 0) {
|
| 61 |
+
// Normal path (isLoading=true): user just submitted their first
|
| 62 |
+
// message in a fresh session. Snapshot current messages so pre-loaded
|
| 63 |
+
// tool-call results / system messages are excluded from TTS.
|
| 64 |
+
//
|
| 65 |
+
// Resume path (messages.length>0, isLoading=false): messages were
|
| 66 |
+
// pre-loaded without a loading transition (e.g. /resume,
|
| 67 |
+
// --resume-session). Treat all displayed messages as history so they
|
| 68 |
+
// are not re-spoken. Only subsequent real-time replies will be spoken.
|
| 69 |
+
historyIdsRef.current = new Set(messages.map(m => m.uuid))
|
| 70 |
+
}
|
| 71 |
}
|
| 72 |
wasLoadingRef.current = isLoading ?? false
|
| 73 |
|