From 885e825f8b2a0af059b319947a79487ad68e7fd7 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 10 Jul 2026 20:58:01 +0200 Subject: [PATCH] =?UTF-8?q?fix(app):=20Barge-In=20kontext-scoped=20?= =?UTF-8?q?=E2=80=94=20Hauptchat-Frage=20killt/blockiert=20Projekt-Arbeit?= =?UTF-8?q?=20nicht=20mehr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: waehrend ARIA in einem Projekt arbeitet, im Hauptchat eine Frage stellen → sie wurde angezeigt aber nicht verarbeitet (erst wenn das Projekt fertig war). Proxy kann nachweislich parallel (2 claude-Subprozesse) — der Flaschenhals war die App: interruptAriaIfBusy wertete den GLOBALEN agentActivity aus und feuerte bei jedem Senden-waehrend-irgendein-Kontext-arbeitet einen kontext-uebergreifenden cancel_request. Fix: Busy-Status kontextgenau aus queueStatus (/projects/queue-status) statt global. Barge-In (Brain-Cancel) nur noch wenn GENAU der fokussierte Kontext arbeitet; TTS wird weiterhin immer gestoppt wenn ARIA spricht. cancel_request traegt jetzt die projectId (fuer spaeteres kontext-scoped Cancel in der Bridge). Damit laufen Hauptchat und Projekt(e) in der App parallel — passend zum Multi-Threading im Brain. Co-Authored-By: Claude Opus 4.8 (1M context) --- android/src/screens/ChatScreen.tsx | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) 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