diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index f7a891d..158c8e8 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -295,6 +295,9 @@ const ChatScreen: React.FC = () => { const [projectNameById, setProjectNameById] = useState>({}); // Queue-Status pro Kontext — polled alle 2s, fuer Status-Dots im Drawer const [queueStatus, setQueueStatus] = useState>({}); + // Ref-Spiegel fuer Callbacks (interruptAriaIfBusy liest den aktuellen + // Busy-Status des fokussierten Kontexts ohne stale Closure). + const queueStatusRef = useRef>({}); const [searchIndex, setSearchIndex] = useState(0); // welcher Treffer aktiv ist const [pendingAttachments, setPendingAttachments] = useState<{file: any, isPhoto: boolean}[]>([]); const [agentActivity, setAgentActivity] = useState<{activity: string, tool: string}>({activity: 'idle', tool: ''}); @@ -517,6 +520,7 @@ const ChatScreen: React.FC = () => { const s = await brainApi.getProjectQueueStatus(); if (cancelled) return; setQueueStatus(s.contexts || {}); + queueStatusRef.current = s.contexts || {}; } catch {} }; poll(); @@ -1939,18 +1943,27 @@ const ChatScreen: React.FC = () => { // mach lieber X" sagen wie in einem echten Gespraech. const interruptAriaIfBusy = useCallback(() => { const speaking = audioService.isPlayingAudio(); - const thinking = agentActivity.activity !== 'idle'; - if (!speaking && !thinking) return false; - console.log('[Chat] Barge-In: speaking=%s thinking=%s — interrupting ARIA', - speaking, thinking); + // Multi-Threading: NUR den fokussierten Kontext als "busy" werten — nicht + // global. Sonst bricht eine Nachricht im Hauptchat die parallele Arbeit in + // einem Projekt ab (bzw. wird faelschlich als Barge-In behandelt und die + // eigene Anfrage geht unter). Der Busy-Status kommt kontextgenau aus + // /projects/queue-status (queueStatusRef). agentActivity ist global und + // taugt dafuer nicht. + const pid = focusedProjectIdRef.current || ''; + const focusKey = pid || '__main__'; + const focusBusy = !!queueStatusRef.current?.[focusKey]?.busy; + if (!speaking && !focusBusy) return false; + console.log('[Chat] Barge-In: speaking=%s focusBusy=%s (ctx=%s) — interrupting', + speaking, focusBusy, focusKey); + // TTS immer stoppen wenn ARIA gerade spricht — egal welcher Kontext. if (speaking) audioService.haltAllPlayback('user spricht (barge-in)'); - if (thinking) { - setAgentActivity({ activity: 'idle', tool: '' }); + // Brain-Arbeit nur abbrechen wenn GENAU dieser Kontext arbeitet. + if (focusBusy) { clearStuckWatchdog(); - rvs.send('cancel_request' as any, {}); + rvs.send('cancel_request' as any, { projectId: pid }); } return true; - }, [agentActivity]); + }, []); // Manueller Aufnahme-Knopf (VoiceButton) — Start. // Streaming-Variante: PcmStreamRecorder + Whisper-ML-Endpointer ersetzen