Commit Graph
555 Commits
Author SHA1 Message Date
duffyduckandClaude Opus 5.5 7b4a1f4429 feat(diagnostic): Liste, wer gerade mit dem RVS-Raum verbunden ist
Einstellungen, zwischen Runtime-Konfiguration und App-Onboarding: alle
Verbindungen im Raum des konfigurierten Tokens — Art, Name, Details,
Adresse, verbunden seit, zuletzt aktiv.

RVS merkt sich pro Verbindung, wer sie ist: aus Nachrichten, die ohnehin
fliessen (sat_hello, host_hello, worker_hello, update_check der App,
llm_cluster_*), oder aus dem neuen client_hello (Bridge, Diagnostic, App
mit Geraetename). Adresse aus X-Forwarded-For hinter einem Reverse-Proxy.
room_clients_query liefert NUR den eigenen Raum; room_clients von Clients
wird nicht weitergereicht. Typen in der Allow-List ergaenzt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:34:14 +01:00
duffyduck 35436bff3c release: bump version to 0.2.5.7 2026-10-25 12:01:35 +01:00
duffyduck b82df7dfca release: bump version to 0.2.5.6 2026-10-25 10:40:09 +01:00
duffyduckandClaude Opus 5.5 3b63831c86 feat(app): Schloss-Symbol fuer die Verbindung + abgelaufenes Zertifikat erlaubbar
Neben dem Zahnrad zeigt ein Schloss, wie sicher die Verbindung zu ARIA ist:
Schloss = verschluesselt und in Ordnung, Schloss mit ! = Zertifikat-Problem
(abgelaufen oder abgelehnt), durchgestrichen = unverschluesselt (TLS-Fallback
oder so eingestellt). Antippen erklaert es in einfachen Worten; einmal pro
App-Start kommt ein Hinweis, wenn das Zertifikat abgelaufen ist.

Einstellungen → Verbindung: "Abgelaufenes Zertifikat akzeptieren". Toleriert
GENAU das Ablaufdatum: die Kette wird zum Zeitpunkt kurz vor dem Ablauf gegen
die System-CAs geprueft, der Hostname weiter von OkHttp. Selbstsignierte oder
fremde Zertifikate kommen auch mit Schalter nicht durch (gegen das echte,
abgelaufene RVS-Zertifikat und badssl.com getestet). Gilt fuer WebSocket und
fetch (z.B. App-Update vom RVS).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 17:31:51 +02:00
duffyduckandClaude Opus 5.5 6d978d72b3 feat: Modell pro Chat waehlen — und sehen, welches gerade laeuft
Nach einem Sicherheitsfilter-Treffer fuhr ein Projekt still auf dem
Ausweich-Modell weiter, und nirgends war zu sehen, auf welchem. Zurueck ging
nur ueber "Liste leeren" im Filter-Panel, und das gleich fuer alle Chats.

Jetzt drei Ebenen (aria-brain/context_model.py):
  Einstellung (brainModel) < pro Chat < Ausweich (Sicherheitsfilter)

- Pro Chat: Hauptchat und jedes Projekt koennen ein eigenes Modell haben, in
  /shared/config/context_models.json. Bewusst kein Feld im Projekt-Manifest —
  der Hauptchat ist kein Projekt und waere sonst aussen vor.
- "↩ zurueck" pro Chat: loescht nur die Filter-Vermerke DIESES Chats. Blockt
  der Filter erneut, weicht das Brain eben wieder aus.
- Angezeigt wird, was wirklich laeuft: chat() gibt das Modell aus modelUsage
  der CLI zurueck (aufgeloeste Version statt Alias) samt Grund; Brain → Bridge
  → chat_backup + Live-Payload → App und Diagnostic. Als Grundeinstellung
  zaehlt das Modell, mit dem das Brain laeuft, nicht der Dateiwert — so faellt
  ein ausstehender Neustart nach brainModel-Aenderung auf.

Diagnostic: 🧠-Auswahl neben "+ Projekt" fuer den fokussierten Chat, Modell im
Untertitel jeder Kontext-Karte, Modell statt "Claude" am Bubble-Badge (orange
mit ↪ bei Ausweich).

App: hinter der Quell-Anzeige (Einstellungen, Default aus — Kopfzeile bleibt
fuer Mama unveraendert) ein Modell-Chip in der Kopfzeile; Tippen oeffnet die
Auswahl fuer diesen Chat inkl. "Zurueck". Laeuft ueber den vorhandenen
brain_request-Kanal, also keine neuen RVS-Typen.

Tests: 47/47 (12 neue fuer die Aufloesungsreihenfolge gegen agent.chat()),
tsc ohne Fehler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:39:30 +02:00
duffyduck 7af0b76ad3 release: bump version to 0.2.5.5 2026-10-02 07:35:59 +02:00
duffyduckandClaude Opus 5 614ba48531 feat(app): Sprachnachricht zu Anhaengen + Zwischenruf per Sprache
Bisher war die Eingabeleiste ein Entweder-oder: sobald Text im Feld stand oder
ein Anhang dranhing, verschwand das Mikro. Damit ging genau das nicht, was man
eigentlich will — ein Bild anhaengen und den Auftrag dazu SPRECHEN. Und ein
Zwischenruf ging nur getippt.

Das Mikro ist jetzt immer da; Senden-Pfeil und Mund-/Ohr-Knoepfe weichen je
nach Lage, damit die Zeile nicht ueberlaeuft.

Anhang + Sprachnachricht:
- Mikro antippen mit wartenden Anhaengen laedt sie SOFORT hoch (parallel zum
  Reden) und markiert sie mit awaitVoice + clientMsgId.
- Die Bridge schickt solche Anhaenge nicht nach 0.8s alleine ans Brain, sondern
  wartet auf den stt_endpoint und fuehrt Anhang + gesprochenen Satz zu EINEM
  Turn zusammen. Ohne das sah ARIA ein Bild ohne Auftrag und danach einen Satz
  ohne Bild.
- Der Merge ist strikt an die clientMsgId gebunden (require_match): eine
  Sprachnachricht, die zufaellig waehrend eines fremden Uploads fertig wird,
  reisst dessen Anhaenge nicht an sich. Der Text-Pfad behaelt seinen Fallback.
- Bild und gesprochener Text landen in EINER Bubble (gemeinsame audioRequestId).
- Klappt das Mikro nicht, bleibt die Bubble mit den Bildern stehen statt zu
  verschwinden — die Dateien sind ja schon auf dem Server.
- Das Notfall-Fenster (420s) liegt ueber dem Aufnahme-Hard-Cap von 5 Minuten,
  sonst waeren die Anhaenge bei einer langen Aufnahme vorher alleine raus.

Zwischenruf per Sprache:
- Der 📣-Knopf erscheint jetzt immer, wenn ARIA im Kontext arbeitet. Mit Text
  schickt er sofort (wie bisher), ohne Text schaltet er die naechste Aufnahme
  scharf (sichtbar hervorgehoben, gilt fuer genau eine Aufnahme).
- Eine so markierte Aufnahme geht per stt_stream_start als interject raus; die
  Bridge schiebt sie in den laufenden Turn statt sie anzustellen.
- Dabei wird der laufende Turn NICHT abgebrochen (das war der Sinn) — nur die
  Wiedergabe stoppt, damit ARIA nicht ins offene Mikro redet.
- Die Bubble zeigt 📣 Zwischenruf statt 🎤, damit man nicht auf eine eigene
  Antwort wartet.

Merge-Logik gegen den echten Bridge-Code getestet (6 Faelle), tsc sauber.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-02 02:11:06 +02:00
duffyduck 5b5591aa72 release: bump version to 0.2.5.4 2026-10-02 01:51:14 +02:00
duffyduckandClaude Opus 5 b70623bb78 fix(app): Absturzschleife — Auto-Re-Download holte ungefragt ein 150-MB-Video
Die Absturzschleife (alle ~70s, stundenlang) war selbsttragend:

  App startet → Auto-Re-Download fordert 2s spaeter ALLE fehlenden Anhaenge an,
  ohne Groessen-Grenze → Bridge schickt das 150-MB-Video als ~200 MB base64 in
  EINER WebSocket-Nachricht → die App muss das puffern und durch JSON.parse
  schicken → nativer OOM (kein JS-Fehler, darum nie ein Stacktrace) → Android
  startet den Vordergrund-Dienst neu → App startet → von vorn.

Dieselbe Nachricht hat parallel whisper/f5tts/voxtral mit Code 1009 umgebracht
— darum lag der Absturz immer direkt hinter dem Audio-Abbau, und darum kam
jede gerade laufende Sprachaufnahme nie an.

Drei Bremsen:
- Auto-Re-Download ueberspringt alles ueber 25 MB. Bleibt als "150 MB —
  tippen zum Laden" stehen; der User entscheidet.
- Ausdrueckliches Antippen bremst bei 64 MB mit einer ehrlichen Meldung samt
  Server-Pfad, statt die App zu killen.
- Der Brotkrumen wird von der Crash-Meldung nicht mehr ueberschrieben. Bisher
  merkte sich der zweite Absturz nur noch "letzte Aktivitaet: Crash-Meldung" —
  in einer Schleife war nach dem ersten Durchlauf jede Spur weg.

Diagnose kam aus /shared/logs/app.log (app_log via RVS), kein ADB noetig.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-02 01:48:27 +02:00
duffyduck 07df538763 release: bump version to 0.2.5.3 2026-10-02 01:37:49 +02:00
duffyduckandClaude Opus 4.8 87755b9c58 fix(app): Aufnahme-Sperre fragt den Socket statt den gemerkten Zustand
Beweis aus app.log 02.10 01:22: beim Aufnehmen fehlte 'bg.start slot=rec' —
die Aufnahme wurde abgelehnt, BEVOR sie begann. Ursache ist die in 10e7faa
eingefuehrte Sperre 'ohne Verbindung nicht aufnehmen': sie prueft den gemerkten
Zustand, und der steht wegen des Zombie-Bugs faelschlich auf 'disconnected',
obwohl die Verbindung laeuft (TTS kam ja an). Ergebnis: Stefan konnte gar nicht
mehr sprechen — die Sperre hat das Problem verschlimmert statt es zu verhindern.

rvs.isSocketOpen() fragt jetzt ws.readyState direkt; audio.ts nutzt das. Damit
ist die Sperre immun gegen falsche Zustandsmeldungen und blockt nur, wenn der
Socket wirklich zu ist. tsc: 0 Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-02 01:24:57 +02:00
duffyduckandClaude Opus 4.8 b6195d3a53 feat(app): Teilen-Ziel — Bilder/Videos/Dateien aus anderen Apps an ARIA
'Teilen -> ARIA' aus WhatsApp, Galerie, Browser & Co. Ablauf wie gewuenscht:
sehen WAS geteilt wurde -> WOHIN (Projekt oder Hauptchat) -> optional Text dazu.
Ohne Text geht genauso.

- Manifest: intent-filter fuer ACTION_SEND (text/image/video/audio/application)
  und ACTION_SEND_MULTIPLE. launchMode=singleTask war bereits gesetzt.
- ShareIntentModule (Kotlin): liest den Intent und kopiert die Inhalte SOFORT in
  den App-Cache — die Leseerlaubnis fuer content://-URIs gilt nur fuer diesen
  Intent und waere nach der Projektauswahl womoeglich erloschen. Liefert echte
  Dateipfade, dadurch laeuft Teilen durch exakt dieselbe Upload-Strecke wie die
  Bueroklammer (RNFS -> Base64 -> Anhang im Chat).
- MainActivity.onNewIntent: setIntent + Event an JS (best effort). Zweitweg in
  JS ueber AppState 'active' -> getSharedData(), falls das Event je nach
  RN-Architektur nicht durchkommt; getSharedData markiert den Intent als
  verbraucht, also kein doppeltes Aufpoppen.
- ShareModal: Vorschau der geteilten Inhalte, Projektliste (inkl. Hauptchat),
  optionales Textfeld. Senden erst nach Zielwahl moeglich.
- sendPendingAttachments nimmt die Anhaenge jetzt optional als Parameter — beim
  Teilen waere der State zum Aufrufzeitpunkt noch leer.
tsc --noEmit: 0 Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-02 01:19:55 +02:00
duffyduck b9354283af release: bump version to 0.2.5.2 2026-10-02 01:14:25 +02:00
duffyduckandClaude Opus 4.8 bf1db6cbf9 fix(app): Diagnostic-Nachrichten verschwanden in Projekten (projectId fehlte)
Stefan: 'die antwort kam auf der app, aber meine frage ist nicht in der app'.
Die App zeigt Diagnostic-Nachrichten durchaus an (sender=diagnostic -> als
User-Bubble), legte sie aber OHNE projectId ab. Die Chat-Ansicht filtert nach
Projekt (ChatScreen.tsx:2078) — damit war die Nachricht nur im Hauptchat
sichtbar und in jedem Projekt unsichtbar. ARIAs Antwort trug die projectId und
erschien korrekt; nur die eigene Frage fehlte. Jetzt wird die mitgeschickte
projectId uebernommen. tsc: 0 Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-02 01:11:18 +02:00
duffyduckandClaude Opus 4.8 ea159d4ed7 fix(app): Zombie-Verbindung — App zeigt 'getrennt', empfaengt aber weiter
Symptom (live beobachtet): die App steht auf 'getrennt' und nichts, was Stefan
schreibt oder spricht, erreicht den Server — ARIAs Antwort und TTS kommen aber
trotzdem in der App an.

Ursache: Beim Verwerfen einer Verbindung wurden nur onclose und onerror
entfernt, NICHT onmessage. Die aufgegebene Verbindung lieferte deshalb weiter
Nachrichten an die App-Listener (daher die ankommende Antwort), waehrend this.ws
auf die neue/tote zeigte — send() lehnte ab und der Zustand blieb
'disconnected'. Betraf beide Abriss-Pfade (Neu-Connect und TLS-Fallback).

Fix: detachSocket() klemmt eine Verbindung vollstaendig ab (onopen/onmessage/
onclose/onerror + close) und wird in beiden Pfaden genutzt. Zusaetzlich ignoriert
der onmessage-Handler Nachrichten, die nicht von der AKTUELLEN Verbindung
stammen — sonst taeuscht ein Zombie weiter Betrieb vor. tsc: 0 Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-02 01:08:37 +02:00
duffyduckandClaude Opus 4.8 dd1e1ac458 fix(app): verlorene Aufnahme sichtbar machen statt still zu loeschen
Beweis aus den Logs: die Whisper-Bridge startete MITTEN in der ersten Aufnahme
neu (stream.start 16:35:43 -> boot 16:36:02), damit war die Sitzung weg. Die App
fiel in den 3s-Fallback (reason=...no-response) und entfernte die Bubble
kommentarlos — fuer den Nutzer sah es aus, als waere nie etwas gesendet worden.

Jetzt wird bei 'no-response' die Platzhalter-Bubble durch einen Hinweis ersetzt
('Aufnahme ging verloren — Spracherkennung war nicht erreichbar'). Der stille
no-speech-Fall (Du hast wirklich nichts gesagt) bleibt wie bisher. tsc: 0 Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-01 17:08:42 +02:00
duffyduck cc91497923 release: bump version to 0.2.5.1 2026-09-30 21:18:09 +02:00
duffyduckandClaude Opus 4.8 10e7faa92b fix(app): verlorene erste Sprachaufnahme — Watchdog, Reihenfolge, Startzustand
Drei App-seitige Ursachen fuer 'erst beim zweiten Aufnehmen' (tsc --noEmit: 0 Fehler):

1) No-Speech-Watchdog: Der Aufrufer uebergibt die Sprechpausen-Toleranz
   (sttEndpointMs, Default 2400ms, per Setting bis 1000ms). Die misst ab dem
   ENDE des Sprechens, der Watchdog lief aber ab dem START der Aufnahme. Bis ein
   Zwischenergebnis zurueck sein KANN, vergehen realistisch 1,3-2,2s (600ms
   Mindestpuffer + 700ms Transkriptions-Takt + Netzweg). Schon kurzes Ueberlegen
   nach dem Gong brach die Aufnahme ab. Jetzt +2500ms Zuschlag.

2) stt_stream_start ging ERST NACH PcmStreamRecorder.start() raus — die ersten
   Chunks (200ms-Takt) trafen auf eine Bridge ohne Sitzung und wurden verworfen
   (Wortanfang weg; bei kurzen Kommandos blieb nichts uebrig). Jetzt davor.

3) recordingState wurde erst nach mehreren awaits auf 'recording' gesetzt. In
   diesem Fenster war isStreamingRecording() true, der Zustand aber 'idle': ein
   Abbruch dort liess den Start weiterlaufen, der Recorder blieb haengen und die
   NAECHSTE Aufnahme wurde abgelehnt. Zustand wird jetzt sofort gesetzt, plus
   Session-Guard nach den awaits.

Zusaetzlich: ohne RVS-Verbindung wird gar nicht erst aufgenommen (send()
verwirft still, kein Puffer) — nach App-Resume fiel sonst die erste Aufnahme
komplett aus. Nachtraeglich gepuffertes Audio waere sinnlos, die STT-Sitzung
laeuft in Echtzeit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-30 13:02:55 +02:00
duffyduck 29bf3370b4 release: bump version to 0.2.5.0 2026-09-24 18:51:30 +02:00
duffyduckandClaude Opus 4.8 6b5d847615 fix(app+brain): leere Bubble bei Tool-Loop/Fehler beheben + Limit hoch
Symptom: ARIA laeuft ins Tool-Loop-Limit (host_agent-Exploration), postet die
Meldung, aber die App zeigt eine LEERE Bubble.

Ursache: stripSystemHints (App) entfernt fuehrende [..]-Bloecke (fuer Hinweise
wie '[Kontext:..] Hallo'). Die Tool-Loop-/Fehler-Meldung ist KOMPLETT ein
[..]-Block -> restlos gestrippt -> leer.

Fixes:
- App (ChatScreen): stripSystemHints zeigt das Original, wenn nach dem Strippen
  nichts uebrig bleibt -> Fehler-/Meta-Meldungen (auch '[Fehler: ...]') sind
  wieder sichtbar statt leer.
- Brain: MAX_TOOL_ITERATIONS 8 -> 20 (env-tunebar) — 8 war zu knapp fuer echte
  agentische Arbeit (Host-Exploration); ARIA brach mittendrin ab.
- Brain: Loop-Limit-Meldung ARIA-stimmig + handlungsleitend statt technischer
  Marker (liest sich natuerlich in der Bubble).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 18:47:58 +02:00
duffyduck f89e7ac36d release: bump version to 0.2.4.9 2026-09-24 16:38:20 +02:00
duffyduckandClaude Opus 4.8 4e1ab95684 feat(app): Datei-Upload erlaubt alle Dateitypen (Bueroklammer)
Der DocumentPicker war auf images/pdf/docx/plainText beschraenkt -> vom
Smartphone liessen sich nur Bilder/Dokumente hochladen. Auf allFiles
umgestellt; die Komponente verarbeitet beliebige Typen ohnehin (Base64 +
octet-stream-Fallback), und die Server-Seite hat kein MIME-Gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 15:57:56 +02:00
duffyduckandClaude Opus 4.8 254031bb7d refactor: "Gamebox" ueberall durch "AI-Box(en)" ersetzen
Rein terminologisch: die feste "Gamebox" ist konzeptuell zu beliebig vielen
AI-Boxen geworden (Multi-Node-Compute-Fleet). Wort-Sweep ueber Code-Kommentare,
Strings, README, docs, .env.example, CHANGELOG — keine Identifier/Keys betroffen
(Gamebox kam nirgends in Variablennamen vor). Casing erhalten: Gamebox→AI-Box,
gamebox→ai-box. NODE_NAME-Default gamebox→ai-box, Instanz-IDs f5tts@ai-box.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 00:34:21 +02:00
duffyduckandClaude Opus 4.8 e39599ecea feat(compute): Redundanz-Routing per targetInstance (Stage 3)
Mehrere Instanzen pro Dienst nutzbar: Anfragen werden gezielt an eine freie
Instanz adressiert statt an alle gebroadcastet. Mehrere f5tts → naechstes
freies; mehrere LLM → parallele Turns (Multitasking); STT-Redundanz ueber
mehrere Apps via Lease.

- Worker (alle vier): filtern am Loop-Eingang — targetInstance gesetzt und
  != eigener INSTANCE_ID → Nachricht ignorieren. Feld fehlt → wie bisher.
- bridge (TTS/LLM, emittiert die Bridge selbst): _pick_worker() waehlt eine
  online+freie Instanz (Round-Robin), stempelt targetInstance auf
  xtts_request / llm_request. Keine Instanz bekannt → Broadcast.
- bridge (STT-Lease, emittiert die App): neuer stt_lease_request-Handler →
  _pick_stt_worker() (voxtral vor whisper) → stt_lease {instanceId}.
- app (audio.ts): requestSttLease() vor dem Stream, stempelt targetInstance
  auf stt_stream_start / stt_audio_chunk / stt_stream_end (+cancel). Kurzer
  Timeout → '' (Broadcast), Aufnahme haengt nie.

Voll rueckwaertskompatibel: Routing aktiviert sich erst, wenn Worker sich per
worker_hello (Stage 2) registriert haben — sonst bleibt alles Broadcast.
Braucht APK-Rebuild fuer die STT-Lease-Seite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-18 12:27:51 +02:00
duffyduck 603ce93197 release: bump version to 0.2.4.8 2026-08-16 22:01:07 +02:00
duffyduck daba5c0457 release: bump version to 0.2.4.7 2026-08-16 21:39:35 +02:00
duffyduckandClaude Opus 4.8 e96b97b1b9 feat(wake): Voxtral-Bestätigungsstufe gegen Musik-Fehltrigger
openWakeWord triggert oft auf Musik (Pet Shop Boys "West End Girls" & Co.). Neu:
nach dem Trigger prüft Voxtral den Vor-Trigger-Audio ("war das wirklich das
Wake-Wort oder nur Musik?") — erst bei Bestätigung Gong + Mikro, sonst still
verworfen + re-arm. Kostet ~0,5-1s vor dem Gong.

- Native (OpenWakeWordModule.kt): 2s Roh-PCM-Ringpuffer; bei Erkennung wird der
  1,5s-Vor-Trigger-Schnipsel base64 (s16le 16kHz) ans WakeWordDetected-Event
  gehängt (preTriggerPcm).
- Bridge (voxtral): neuer One-Shot-Handler stt_transcribe_blob → Silero + Voxtral
  → stt_transcribe_result. Musik/Rauschen → leerer Text.
- App: audio.transcribeBlob() schickt den Schnipsel, wakeword.confirmWake() prüft
  ob das distinktive Keyword (>=4 Zeichen) im Transkript steht. Gate sitzt in
  onWakeDetected VOR Gong/Dialog. Setting "Wake-Wort per Voxtral bestätigen"
  (default aus, opt-in).

FAIL-OPEN durchgängig: kein preTriggerPcm (altes APK) / Timeout / Fehler / VAD
weg → Wake läuft normal durch. Nie schlechter als heute. NATIVER TEIL UNGETESTET
(kein Gerät hier) — braucht Build + Test auf dem Handy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 21:37:15 +02:00
duffyduck fcd73d8171 release: bump version to 0.2.4.6 2026-08-16 21:17:07 +02:00
duffyduckandClaude Opus 4.8 d0b00d5c36 fix(app): Umgebungsmusik killt Weiterreden-Fenster nicht mehr (re-listen)
Wechselwirkung entdeckt: im Passiv-Lausch-Fenster (Weiterreden nach ARIAs
Antwort) nimmt das Mikro Umgebungsmusik auf → Silero verwirft sie korrekt als
vad_no_speech → leerer Endpoint. Die App wertete JEDEN leeren Endpoint als
"nichts gesagt" → sofort zurueck aufs Wake-Word. Bei laufender Musik in einer
Sprechpause endete so das Gespraech, obwohl die Stille-Toleranz noch lief.

Fix: bei leerem Endpoint im Passiv-Fenster wird — solange die Stille-Toleranz ab
Fenster-Oeffnung noch laeuft (Budget) und < 15 Runden — nochmal gelauscht statt
zu beenden. Musik wird verworfen, das Fenster bleibt bis zur Toleranz offen, du
kannst innerhalb reden. Erst wenn die Toleranz wirklich um ist → aufs Wake-Word.
Sprichst du (Text-Endpoint), geht es sofort normal weiter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 21:14:37 +02:00
duffyduckandClaude Opus 4.8 9d63a73135 fix(app): Konversationsmodus auch im Hintergrund, wenn Background-Wake an
Diagnose aus dem App-State-Trail (Bridge-Log): nach jeder gesprochenen Antwort
rief die App endConversation(bg || !converse) — der bg-Term (AppState != active)
erzwang "zurueck aufs Wake-Word", sobald die App im Hintergrund war. Das war vor
dem Background-Wake-Feature Absicht (kein Multi-Turn im Hintergrund). Jetzt, mit
aktivem Background-Wake, killt genau das den Konversationsmodus im Hintergrund —
im Vordergrund lief er weiter (daher "kommt beim Vorholen wieder"). converse kam
korrekt als true vom Brain (keine Marker, Default true); die App warf es im
Hintergrund weg.

Fix: bg erzwingt "armed" nur noch, wenn Background-Wake AUS ist
(isBgWakeEnabled()). Bei aktivem Background-Wake bleibt der Konversationsmodus
auch im Hintergrund offen — der User hat das Zuhoeren ja bewusst eingeschaltet.
Vordergrund-Verhalten unveraendert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 21:06:40 +02:00
duffyduck 9981f0523d release: bump version to 0.2.4.5 2026-08-16 20:40:47 +02:00
duffyduckandClaude Opus 4.8 31444f1442 feat(voice): Wake-Word per Befehl wieder anschalten (Gegenstück zu wake_off)
"Wake-Word an" / "mach das Ohr an" / "hör wieder zu" / "wach auf" → App startet
den Listener wieder. Geht per Text-Nachricht ODER manuellem Aufnahme-Button —
beide laufen unabhängig vom Wake-Word-Listener, also funktioniert das Wieder-An
auch wenn das Ohr gerade taub ist (nur nicht per "Computer", das hört ja nicht).

Symmetrisch zu wake_off: Detektor _user_wants_wake_on → wake_on durch ChatOut →
Bridge → App löst wakeWordService.start() + setWakeWordActive(true) aus. An/Aus
kollidieren nicht (12 Fälle getestet). Damit: Ohr per Befehl ein UND aus, plus
weiterhin der Ohr-Button.

Fix nebenbei: gerades " in den Reply-Strings (hätte den Brain-Start gecrasht) →
einfache Anführungszeichen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 20:33:16 +02:00
duffyduckandClaude Opus 4.8 5398640078 feat(voice): Wake-Word per Sprachbefehl ausschalten
"Computer, Wake-Word aus" / "mach das Ohr aus" / "hör auf zuzuhören" / "geh
schlafen" → die App stoppt den Wake-Word-Listener KOMPLETT (Mikro frei, echte
Ruhe). Wieder-An nur über den Ohr-Button (bewusst, weil dann taub — Voice-
Wieder-An ist physikalisch unmöglich, wenn nichts mehr hört).

Deterministischer Detektor _user_wants_wake_off im Brain (kein LLM-Call nötig,
still). Signal fließt als wake_off durch ChatOut → Bridge → App-Payload; die App
löst denselben Pfad wie toggleWakeWord-off aus (cancelRecording +
wakeWordService.stop() + setWakeWordActive(false)). Detektor gegen 13 Positiv-
+ 9 Negativ-Fällen verifiziert (fängt nicht 'Licht aus' / 'Konversation Ende').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 20:27:48 +02:00
duffyduck dd71b0ce39 release: bump version to 0.2.4.4 2026-08-16 19:57:14 +02:00
duffyduck 3267b9a3cc release: bump version to 0.2.4.3 2026-08-16 11:33:45 +02:00
duffyduck d984125624 release: bump version to 0.2.4.2 2026-08-16 11:26:49 +02:00
duffyduckandClaude Opus 4.8 54dc4498e5 feat(app): Hintergrund-Wake als Schalter (default aus)
Punkt 2: das Wake-Wort triggerte nur im Vordergrund — eine bewusste JS-Zeile
verwarf jede Hintergrund-Erkennung (native Erkennung + Foreground-Service
liefen längst durch). Jetzt hinter Einstellung 'Auch bei gesperrtem Bildschirm
zuhören' (default aus, mehr Fehltrigger möglich). Greift live via
setBgWakeEnabled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 11:19:06 +02:00
duffyduck ff4c1b997a release: bump version to 0.2.4.1 2026-08-16 02:02:10 +02:00
duffyduckandClaude Opus 4.8 7d2fce46cb fix(voice): Passiv-Backstop lang genug — schneidet lange Antworten nicht mehr ab
Log-Analyse (alter Build): eine lange gesprochene Antwort wurde vom 30s-Passiv-
Timer mitten im Wort gekappt (exit reason=timeout, dann stt_endpoint reason=
stream_end mit abgeschnittenem Text). Genau das Fenster ist raus — ABER mein
Ersatz-Backstop (15s) hätte sogar früher abgeschnitten.

Der Backstop ist eine reine HANG-Notbremse und darf aktives Reden NIE kappen →
jetzt 10min (länger als der ~5min-Hardcap der Aufnahme). Das echte Ende regelt
immer die Aufnahme selbst (Stille-Toleranz / No-Speech / Hardcap). Lange
zusammenhängende Antworten laufen jetzt durch, bis du ≥ Stille-Toleranz pausierst.

Hinweis: der geloggte Fehler ist der ALTE Build — die Fixes sind noch nicht
ausgerollt. Dieser Commit korrigiert den noch-nicht-deployten Stand.

Deploy: neue APK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 01:59:45 +02:00
duffyduckandClaude Opus 4.8 334c5887b0 refactor(voice): tote Fenster-Settings ausgeräumt (Konv-Fenster/Passiv-Lauschen)
Nachdem die Stille-Toleranz jetzt überall der einzige Wert ist, sind die alten
Settings obsolet — raus damit:

- audio.ts: CONV_WINDOW_* Konstanten + loadConvWindowMs() entfernt.
- wakeword.ts: PASSIVE_LISTEN_* + load/savePassiveListenMs() entfernt; der Passiv-
  Master-Timer nutzt jetzt einen festen internen Backstop (PASSIVE_BACKSTOP_MS,
  15s) statt eines Nutzer-Werts — das echte Ende regelt die Stille-Toleranz.
- SettingsScreen: "Konversations-Fenster"- und "Weiterreden-Fenster"-Slider raus;
  Letzterer durch eine kurze Erklärung ersetzt (verweist auf Stille-Toleranz).
- ChatScreen: tote Imports weg.

Kein Verhaltensänderung ggü. d294895 — nur Aufräumen. tsc grün.

Deploy: neue APK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 01:53:55 +02:00
duffyduck ef51c85cb0 release: bump version to 0.2.4.0 2026-08-16 01:48:43 +02:00
duffyduckandClaude Opus 4.8 d294895583 feat(voice): 30s-Passiv-Fenster raus — Stille-Toleranz regiert alles
Stefans Modell: es braucht keinen separaten 30s-Wert. Nach ARIAs Antwort geht das
Mikro auf; fängst du nicht innerhalb der Stille-Toleranz (z.B. 5s) an zu reden, ist
Schluss → zurück aufs Wake-Word. Derselbe Wert wie die Pause-Toleranz beim Reden.
Ein Befehl ohne "fortführen" = Ende; nach einer normalen Antwort = ein Stille-
Fenster zum Weiterreden.

- Alle Aufnahme-Pfade (wake/barge/passiv): noSpeechTimeoutMs = endpointMs =
  loadSttEndpointMs() statt loadConvWindowMs()/loadPassiveListenMs(). Ein Wert.
- Passiv-Hardcap: loadMaxRecordingMs() statt fix 35s (schnitt langes Reden ab).
- Leeres Endpoint im listening-State: KEIN Re-Arm mehr → exitPassiveListening
  (ein 5s-Fenster, dann Ende). Der 30s-Master-Timer in wakeword.ts wird dadurch
  nie mehr scharf (harmloser Backstop).

Redest du weiter → Antwort → wieder ein Stille-Fenster (Multi-Turn bleibt, nur ohne
30s-Leerlauf). Die Settings "Konversations-Fenster"/"Passiv-Lauschen" sind damit
obsolet (UI-Cleanup später).

Deploy: neue APK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 01:46:26 +02:00
duffyduck de34011b93 release: bump version to 0.2.3.9 2026-08-16 01:06:18 +02:00
duffyduck 9389b5ae06 release: bump version to 0.2.3.8 2026-08-15 14:06:34 +02:00
duffyduckandClaude Opus 4.8 23d3188205 feat(voice): ARIA erkennt Gespraechs-Phase selbst (Befehl/Frage, Kette/Ende)
Voice-First: ARIA deklariert die Phase aus dem Text per Inline-Marker — dasselbe
Muster wie [[AWAIT]], die Marker werden vor Anzeige/TTS/History entfernt. Loest:
- Claude-Pfad las Steuerbefehle vor, obwohl das Skill lief (Manifest-speak-Flag
  kennt den Unterschied Befehl/Frage nicht — dasselbe Skill ist mal Aktion, mal
  Auskunft). Jetzt entscheidet ARIA aus dem Kontext.
- Befehlsketten (VNC oeffnen, Menue klicken, ...): Mikro soll offen bleiben und
  stumm gearbeitet werden, bis Stefan die Kette beendet.

Marker (ARIA haengt sie ans Antwort-Ende):
  [[STUMM]]  -> Steuerbefehl, nicht vorlesen. Allein = Einzelbefehl -> danach Stop.
  [[WEITER]] -> Konversation/Kette laeuft weiter -> Mikro offen halten.
  [[ENDE]]   -> Konversation/Kette beenden -> zurueck aufs Wake-Word.

Brain:
- _extract_flow_markers() zieht [[STUMM]]/[[WEITER]]/[[ENDE]] aus dem Claude-Reply
  und ueberschreibt speak/converse (Marker ist autoritativ ueber Skill-Flag).
  [[STUMM]] allein impliziert converse=false; [[ENDE]] schlaegt [[WEITER]].
- prompts.py: neue Sektion build_voice_flow_section() bringt ARIA Marker +
  Befehl/Frage-Klassifikation + Ketten-/Ende-Erkennung bei.

App (ChatScreen): stiller Antwort-Zweig verzweigt jetzt auf converse —
  converse=true (Kette) -> endConversation(false), Mikro bleibt offen fuer den
  naechsten Kettenbefehl; converse=false -> ariaStopRecording (Stop wie gehabt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 14:00:51 +02:00
duffyduckandClaude Opus 4.8 560328bebe fix(voice): ARIA schliesst bei Steuerbefehl die Aufnahme selbst (stiller Stop)
Steuerbefehle laufen stumm (speak=false in Fast-Path/Local/Claude-Pfad) — ohne
TTS feuert aber onPlaybackFinished nie, und daran hing bisher das Aufraeumen der
Aufnahme. Folge: nach "spotify play" blieb das Mikro-/Konversations-Fenster offen
(30s Leerlauf), obwohl der Befehl laengst ausgefuehrt war.

Neu: ariaStopRecording() — das programmatische Gegenstueck zum Stop-Button. Nach
einer stillen Antwort (speak=false) schliesst ARIA jede offene Aufnahme selbst und
geht zurueck aufs Wake-Word, egal in welchem Zustand: passives Lauschen sauber
beenden, offene Streaming-Aufnahme (aktiv/Barge) verwerfen, sonst endConversation.
Vorher nur der conversing-Fall abgedeckt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 13:49:18 +02:00
duffyduck d72518e617 release: bump version to 0.2.3.7 2026-08-15 13:27:32 +02:00
duffyduckandClaude Opus 4.8 0415b2e16d fix(voice): manueller Stop unterdrueckt Passiv-Fenster nach der Antwort
Stop finalisierte die Aufnahme, aber die danach kommende onPlaybackFinished oeffnete via converseRef erneut das 30s-Passiv-Fenster. Jetzt setzt handleVoiceButtonStop converse=false → nach manuellem Stop kein Passiv-Lauschen mehr.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 13:16:46 +02:00
duffyduckandClaude Opus 4.8 f41f7fb585 fix(voice): Passiv-Lauschen nur bei converse:true (kein 30s-Linger nach Befehl)
converse defaultete true → jeder Befehl mit gesprochener Bestaetigung ('Spiele Spotify') ging ins 30s-Passiv-Fenster. Jetzt nur bei explizitem converse:true vom Brain; einzelne Befehle enden sofort → zurueck aufs Wake-Word, Spotify resumed gleich.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 13:15:38 +02:00
duffyduck c623901385 release: bump version to 0.2.3.6 2026-08-15 13:05:46 +02:00