feat: Max-Aufnahmedauer konfigurierbar + Barge-In gibt aria-core Kontext

Max-Aufnahme:
Default rauf von 2 auf 5 Minuten, in den App-Settings konfigurierbar
zwischen 1 und 30 Minuten (loadMaxRecordingMs aus AsyncStorage,
Storage-Key aria_max_recording_sec). Notbremse-Verhalten bleibt:
nach Ablauf wird die Aufnahme automatisch beendet und gesendet.

Barge-In Kontext:
Wenn der User waehrend ARIA noch redet/arbeitet eine neue Sprach-
oder Text-Nachricht sendet, geht jetzt ein 'interrupted: true' Flag
mit. Bridge praefixed den Text fuer aria-core dann mit:

  "[Hinweis: Stefan hat dich gerade unterbrochen waehrend du noch
  gesprochen oder gearbeitet hast. Folgendes ist eine Korrektur,
  Ergaenzung oder ein Themenwechsel zu deiner letzten Antwort.]"

So weiss ARIA dass die neue Message KEINE eigenstaendige Folgefrage
ist sondern auf den abgebrochenen Run bezogen. Der User sieht in
seinem Chat nur den reinen Text — der Hint geht nur an aria-core.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-06 21:58:11 +02:00
parent 406f4cb3cc
commit 31ff20c846
4 changed files with 111 additions and 18 deletions
+10 -5
View File
@@ -505,7 +505,7 @@ const ChatScreen: React.FC = () => {
if (result && result.durationMs > 500) {
// User hat im Fenster gesprochen → Sprachnachricht senden
// Barge-In: laufende ARIA-Aktivitaet abbrechen wenn welche da ist.
interruptAriaIfBusy();
const wasInterrupted = interruptAriaIfBusy();
const location = await getCurrentLocation();
const userMsg: ChatMessage = {
id: nextId(),
@@ -521,6 +521,7 @@ const ChatScreen: React.FC = () => {
mimeType: result.mimeType,
voice: localXttsVoiceRef.current,
speed: ttsSpeedRef.current,
interrupted: wasInterrupted,
...(location && { location }),
});
// resume() wird durch onPlaybackFinished nach ARIAs Antwort getriggert.
@@ -623,6 +624,8 @@ const ChatScreen: React.FC = () => {
setInputText('');
// Barge-In: laufende ARIA-Aktivitaet abbrechen wenn welche da ist.
const wasInterrupted = interruptAriaIfBusy();
const location = await getCurrentLocation();
const userMsg: ChatMessage = {
@@ -633,16 +636,17 @@ const ChatScreen: React.FC = () => {
};
setMessages(prev => capMessages([...prev, userMsg]));
console.log('[Chat] sende mit voice=%s speed=%s',
localXttsVoiceRef.current || '(default)', ttsSpeedRef.current);
console.log('[Chat] sende mit voice=%s speed=%s interrupted=%s',
localXttsVoiceRef.current || '(default)', ttsSpeedRef.current, wasInterrupted);
// An RVS senden — mit geraetelokaler Voice (Bridge nutzt sie fuer die Antwort)
rvs.send('chat', {
text,
voice: localXttsVoiceRef.current,
speed: ttsSpeedRef.current,
interrupted: wasInterrupted,
...(location && { location }),
});
}, [inputText, getCurrentLocation, pendingAttachments, sendPendingAttachments]);
}, [inputText, getCurrentLocation, pendingAttachments, sendPendingAttachments, interruptAriaIfBusy]);
// Anfrage abbrechen — sofort lokalen Indicator weg, Bridge triggert doctor --fix
const cancelRequest = useCallback(() => {
@@ -671,7 +675,7 @@ const ChatScreen: React.FC = () => {
// Sprachaufnahme abgeschlossen
const handleVoiceRecording = useCallback(async (result: RecordingResult) => {
// Barge-In: laufende ARIA-Aktivitaet abbrechen falls aktiv.
interruptAriaIfBusy();
const wasInterrupted = interruptAriaIfBusy();
const location = await getCurrentLocation();
const userMsg: ChatMessage = {
@@ -688,6 +692,7 @@ const ChatScreen: React.FC = () => {
mimeType: result.mimeType,
voice: localXttsVoiceRef.current,
speed: ttsSpeedRef.current,
interrupted: wasInterrupted,
...(location && { location }),
});
}, [getCurrentLocation, interruptAriaIfBusy]);