DIE Ursache, warum die Box nie in der Compute-Flotte auftauchte: die vier
xtts-Worker verbanden zu wss://host:PORT/ws?token=… — ALLE anderen Komponenten
(Bridge, App, Satellit, Diagnostic) verbinden ohne /ws (…:PORT?token=…). Das RVS
selbst ignoriert den Pfad, aber der TLS-Terminator vor :444 routet nach Pfad →
/ws landete in einem anderen Endpunkt/Raum. Folge: worker_hello/ping erreichten
den ARIA-Raum nie (rooms:1 = nur ARIA), das Diagnostic sah die Worker nicht (und
das erklaert auch das RVS-Flappen, das NUR die Worker hatten).
Fix: /ws aus der URL aller vier Worker entfernt → gleicher Pfad wie alle anderen
→ gleicher Raum. Danach: Box neu bauen, Worker erscheinen in der Flotte.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Worker sendeten worker_hello nur EINMAL beim Connect. Startet das Diagnostic
oder die Bridge NACH der Box neu, verpassen sie das hello (RVS spielt es nicht
nach) und sehen nur noch worker_ping → die Box taucht nicht in der Compute-
Flotte auf. Der Satellit ist genau deshalb zuverlaessig sichtbar: er wiederholt
sat_hello periodisch.
- alle vier Worker (f5tts/whisper/voxtral/llm-adapter): worker_hello wird jetzt
zusaetzlich alle ~30s (jeder 3. Ping-Zyklus) wiederholt → ein neu gestartetes
Diagnostic/Bridge lernt die Box innerhalb von 30s, ohne Box-Neustart.
- diagnostic/server.js: Voice-Reconcile (f5tts) laeuft nur beim ERSTEN/erneuten
Auftauchen der Box, nicht bei jedem 30s-hello-Resend (sonst Push/Pull-Dauerlauf).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der ganze Stack (Bridge/Diagnostic/Satellit/App) nutzt Port 443, nur
xtts/.env.example stand auf 444. Das RVS gruppiert pro Server+Token in einen
Raum — bei abweichendem Port landet die Box in einem ANDEREN Raum: sie taucht
nicht in der Compute-Flotte auf und STT/TTS/LLM erreichen ARIA nicht (Satellit
auf 443 blieb sichtbar → typisches Symptom). Kommentar verschaerft: Host+Port
+Token muessen EXAKT zum ARIA-Stack passen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Katalog jetzt echte Tabelle: Spalten Modell | Kann gut/Beschreibung | VRAM |
Status | Ziel-Box | Laden. VRAM aus sizeGB (>=12 GB gelb markiert).
- "Von HuggingFace aktualisieren" sitzt jetzt direkt neben der Ueberschrift
(statt space-between ans rechte Ende); Card breiter (720→1100px), Tabelle
horizontal scrollbar.
- loadLlmCatalog() zieht die Worker-Flotte aktiv nach (requestWorkers) — falls
ein worker_update-Push waehrend RVS-Flappens verpasst wurde, erscheint die
Box beim Oeffnen trotzdem.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Das Host-Bootstrap bereitet genau den xtts-Stack vor (legt xtts/.env an, startet
xtts-Compose) und gilt fuer alle Dienste (f5tts/whisper/voxtral/llm). Nach dem
Klonen sucht man das in xtts/ — dort war bisher kein README.
- git mv ai-box/bootstrap.sh xtts/bootstrap.sh; ai-box/README.md → xtts/README.md
- interne Verweise cd ARIA-AGENT/ai-box → cd ARIA-AGENT/xtts (Pfad-Logik
SCRIPT_DIR/../xtts funktioniert unveraendert, da xtts jetzt das Script-Dir ist)
- README neu betitelt: "xtts — AI-Box (Compute-Node) Setup & Bootstrap" +
Kontext (Profile/GPU im Haupt-README)
- leeres ai-box/ entfernt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pro Box ein "Auslastung"-Button in der Compute-Flotte → Modal mit live
nvidia-smi (1s), Graphen (GPU-Auslastung + Tokens/Intervall) und Besen-Reset.
Historie liegt auf der Box, Diagnostic holt sie via RVS.
- node_stats.py (identisch in allen 4 Worker-Build-Contexts): Sampler alle 15s
(nvidia-smi + Token-Delta → Ringpuffer ~500 Punkte, persistent als JSON auf
der Box), Live-Stream (node_stats, 1s, Auto-Stop 300s), History-Request,
Reset. nvidia-smi via async subprocess, fail-safe ohne GPU.
- Worker-Wiring (f5tts/whisper/voxtral/llm-adapter): Import, Sampler-Task,
_stats.handle() nach dem targetInstance-Filter. llm-adapter zaehlt Tokens
(usage.total_tokens) → Token-Graph nur bei LLM-Boxen. Dockerfiles kopieren
node_stats.py.
- compose: llm-adapter bekommt runtime:nvidia + NVIDIA_VISIBLE_DEVICES=all +
DRIVER_CAPABILITIES=utility (nur nvidia-smi, KEIN VRAM/Compute).
- diagnostic/server.js: relay node_stats_* (Browser→Box) + forward (Box→Browser).
- diagnostic/index.html: Auslastung-Button pro Node (Ziel bevorzugt llm-Instanz),
Modal mit live nvidia-smi + Inline-SVG-Sparklines, Besen-Reset.
Reporter-Wahl bevorzugt die llm-Instanz (sieht alle GPUs + Tokens); GPU-Worker
sehen ihre gepinnte Karte. Gitignored Historie stoert git-Baum der Box nicht.
Deploy: diagnostic + GPU-Boxen neu bauen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ARIA nutzt lokale LLMs auf mehreren ai-boxen; jede Box (llama-swap) kann
mehrere Modelle fahren. Auswahl nach MODELL, Box wird automatisch gewaehlt.
- xtts/llm-adapter/adapter.py: fragt beim Connect llama-swap GET /v1/models ab
und meldet die Modell-Liste in worker_hello (models:[...]), Fallback [LLM_MODEL].
- bridge/aria_bridge.py: Worker-Registry speichert models[]; _pick_worker(service,
model=) beruecksichtigt nur Boxen, die das Modell fahren koennen (nachsichtig:
keine → None → Broadcast/Claude-Fallback); Round-Robin je service+model
verteilt mehrere Projekte auf mehrere Boxen; _local_llm reicht das Modell durch;
_worker_list traegt models[].
- diagnostic/server.js: workers-Map + workerList um models[] erweitert.
- diagnostic/index.html: Compute-Flotte zeigt bei llm die Modell-Liste; das
"Lokales Modell"-Dropdown wird LIVE aus den angemeldeten Boxen gebaut
(Vereinigung + kuratierte Namen aus local_models.json, "· N Box(en)"/"offline"),
darunter eine kompakte LLM-Box-Liste (Node→Modelle→Health). Speisung aus dem
vorhandenen worker_update-Broadcast.
Kein Brain-/App-Eingriff. Routing greift nur bei aktivem Lokal-Schalter.
Deploy: diagnostic + bridge + llm-Boxen neu bauen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stimmen lagen bisher nur pro Box (xtts/voices/). Jetzt haelt der Diagnostic-
Server sie zentral als /shared/voices/{name}.tar.gz und versorgt jede f5tts-Box
automatisch.
- diagnostic/server.js (Bibliothekar):
* zentraler Store + Helfer (list/read/write/delete tar.gz)
* reconcileVoices() beim worker_hello einer f5tts-Box: push fehlende zentrale
Stimmen (xtts_import_voice, targetInstance) + pull box-eigene, zentral
fehlende (xtts_export_voice) → seedet den Store aus der ersten Box
* autonome RVS-Listener: xtts_voice_saved → zentraler Ingest (auch App-Uploads);
xtts_voice_exported (nur eigene requestId) → in Store schreiben;
xtts_delete_voice → zentrale Kopie mitloeschen
* Delete-Action loescht zentrale Kopie direkt mit
- xtts/f5tts/bridge.py: worker_hello traegt jetzt voices:[names] (ohne
default_ref) fuer den Abgleich; handle_export_voice echot requestId zurueck
(Korrelation zentral vs. browser-initiiert)
Wiederverwendet den vorhandenen export/import-tar.gz-Transport + Stage-3-
targetInstance-Filter. Playback (preview_voice) und Lastrouting (freieste Box)
existierten bereits. Keine App-/aria-bridge-Aenderung.
Deploy: diagnostic + f5tts-Boxen neu bauen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Der Settings-Tab war auf 13 Sektionen untereinander gewachsen (unuebersichtlich).
Jetzt drei Sub-Tabs im #tab-settings:
- Modelle & KI: Sprachmodell (Brain), Lokales LLM, Externe Anbieter (OpenRouter
& Co — Platzhalter), Sprachausgabe/F5-TTS, Spracherkennung (STT: Voxtral/
Whisper), Voice-ID
- Integrationen: OAuth-Apps, FLUX Bildgenerierung
- System & Wartung: Reparatur/Restart, Komplett-Reset, Betriebsmodus,
Runtime-Konfiguration, App-Onboarding-QR
Nur Re-Parenting: jede Sektion wird byte-identisch in ihr Panel verschoben,
alle Panels bleiben immer im DOM (nur CSS zeigt/versteckt), Parent bleibt
#tab-settings → alle Loader (get_voice_config, loadRuntimeConfig, …) und
Save/Load-Handler laufen unveraendert. Verifiziert: 46 Element-ids vorher =
46 nachher, keine verloren/doppelt; JS-Block node --check sauber.
Neu: settings-subnav + switchSettingsTab() (merkt den zuletzt gewaehlten
Sub-Tab in localStorage), STT-Section-Hinweis dass die Engine pro Compute-Node
via COMPOSE_PROFILES gewaehlt wird, Platzhalter-Section fuer externe Anbieter
(kein Backend, nur reservierter Platz). App unveraendert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
.env.example + README: eigene Beispiel-Zeile "kleine Karte" —
COMPOSE_PROFILES=whisper,f5tts (Whisper statt Voxtral, beide auf EINER GPU),
Default der Vorlage auf whisper,f5tts gesetzt (Voxtral-3B braucht ~9 GB).
GPU-Defaults angepasst (WHISPER_GPU=0 neben F5TTS_GPU=0) + Klarstellung:
pro Node genau EIN STT (Voxtral ODER Whisper).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Die feste "Gamebox" wird zu beliebig vielen Compute-Nodes. Jeder Node startet
ueber COMPOSE_PROFILES nur die Dienste, die er anbieten soll, und pinnt sie per
*_GPU auf bestimmte Grafikkarten.
- xtts/docker-compose.yml: jeder GPU-Dienst hinter einem Profil
(voxtral/whisper/f5tts/llm); deploy.devices-Block ersetzt durch
runtime: nvidia + NVIDIA_VISIBLE_DEVICES=${SVC_GPU} (erlaubt auch "0,1");
NODE_NAME an alle RVS-Dienste.
- xtts/.env.example: COMPOSE_PROFILES, NODE_NAME, VOXTRAL/WHISPER/F5TTS/LLM_GPU
mit Beschreibungen; Beispiele STT-Box / TTS-Box / LLM-Box / All-in-One.
- README: "Gamebox-Stack" → "Compute-Nodes"; Diagramm, Deploy-Tabelle und
Setup-Abschnitt auf Multi-Node umgeschrieben.
Stage 1 von 3 (Aufteilung+Config+Docs). Reine Config/Docs, kein Verhaltens-
Risiko: Single-Node mit COMPOSE_PROFILES=voxtral,f5tts,llm laeuft wie bisher.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Datei-Uploads (Copy-Paste UND Datei-Picker) trugen im Diagnostic-Portal
den projectId des fokussierten Projekts nicht mit — anders als Text und
Zwischenrufe. Die Bridge las payload.projectId, bekam nichts und routete
die Datei-Bubble in den Hauptchat.
- index.html: sendDiagAttachments schickt projectId: focusedContextId
- server.js: send_file-Relay reicht projectId in die RVS-Payload weiter
Beide Upload-Wege laufen ueber handleDiagFileSelect → ein Fix deckt beide.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regression aus dem Schlaf-Idiom-Commit (954ad9a): 'gute nacht' im wake-off-Regex
matchte jeden Verabschiedungs-Gruss ('Ich wünsche dir eine gute Nacht') → Ohr
ging aus, Stefan hing in einer 'Ohr aus'-Schleife fest. 'gute nacht'/'schlaf
gut' sind Gruesse, kein Ohr-Aus-Befehl → raus aus dem Regex. Nur klare Imperative
an ARIA bleiben ('geh/leg dich schlafen', 'leg dich aufs Ohr/hin'); mehrdeutige
Faelle faengt ARIA per ear_control aus dem Kontext ab.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Statt jede Schlaf-/Aus-Formulierung per Regex aufzuzählen: ein Tool ear_control
(action off|on), das ARIA aufruft, wenn Stefan sinngemäß "hör auf/wieder zu
zuhören" sagt — egal wie ("leg dich schlafen", "ich geh ins Bett, du kannst
aus", "mach Pause vom Zuhören"). Tools ruft ARIA zuverlässig (im Gegensatz zu
den [[..]]-Markern, die sie oft ignoriert).
Nutzt die komplette bestehende Plumbing: der Tool-Call setzt _ear_control →
answered_by wird "wake-off"/"wake-on" → main.py setzt wake_off/wake_on → Bridge
→ App stoppt/startet Listener. Reiner Steuerbefehl (still, kein Weiterlauschen).
Der deterministische Regex-Detektor bleibt als schnelles Netz für die
Standard-Sätze (instant, kein Claude-Call). Prompt-Hinweis in der Voice-Flow-
Sektion ergänzt. Brain-only — kein neues APK nötig.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Problem (Stefan): laeuft Musik, endet eine Aufnahme nie von selbst — die Energie
bleibt oben, der RMS-Stille-Endpoint feuert nicht (last_voice_at immer frisch).
Erst Stop druecken oder Musik leiser (dann echte Stille) beendet.
Fix: waehrend lauter Phasen (rms ueber Schwelle) periodisch (~700ms, gedrosselt)
mit Silero pruefen, ob im letzten endpoint_ms-Fenster ueberhaupt Sprache ist.
Nur Musik/Stille → Turn beenden (finalize endpoint). Real gesprochene Turns
haben Sprache im Fenster → laufen normal weiter; eine kurze Denk-Pause auch,
solange innerhalb der Toleranz noch Sprache im Fenster liegt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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>
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>
"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>
"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>
Der Muster-Filter fängt nur spezifische Artefakte; generische Silence-Phantome
("Ich bin ein guter Mann", "Die Serie wurde in 13 Episoden gedreht") kann ein
Text-Regex prinzipiell nicht vorhersehen. Lösung an der Wurzel: Silero VAD
(neuronal, ~2MB) entscheidet am AUDIO, ob überhaupt echte Sprache drin ist.
Im _finalize vor dem Transkribieren: kein Speech-Segment → no-speech, gar nicht
erst transkribieren → kein Phantom. Trennt zuverlässig Sprache von Stille /
Rauschen / MUSIK (löst damit auch den Musik-False-Positive). Bei Speech wird auf
die Sprach-Spanne getrimmt (Stille-Ränder weg → weniger Edge-Halluzination).
FAIL-OPEN: Import/Load/Inferenz-Fehler → normal transkribieren (die STT darf nie
komplett sterben — Speaker-ID-Lektion). Alles env-tunebar: SILERO_VAD_ENABLED,
_THRESHOLD (0.5), _MIN_SPEECH_MS (150), _PAD_MS (200). Modell im pip-Paket
gebündelt, kein Runtime-Download.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stefans Repro: eine echte Nachricht endete mit "...vergiss das, das ist nur..."
und Voxtral hat den Satz ~15x geloopt, bis zur Brain durch. Klassische
Repetition-Halluzination bei Stille/Rauschen am Ende.
Zwei Ebenen: (1) Generation bekommt no_repeat_ngram_size=4 + repetition_penalty
=1.15 → erste echte Nennung bleibt, exakte 4-Gramm-Wiederholung verboten, Loop
bricht an der Quelle ab. (2) Post-Detektor _collapse_repetitions kassiert einen
durchgerutschten Loop auf eine Kopie ein. Gegen den echten Loop + legitime
Doppelungen ('ja ja ja', 'sehr sehr') verifiziert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stefans Repro: "die Stille-Ende wird nie erreicht, stop ich selbst ist es weg,
und geht automatisch auf lausche Computer". Ursache: der Endpoint feuert nur
wenn schon Stimme da war (last_voice_at>0). Bei totaler Stille bleibt
last_voice_at==0 → Endpoint feuert NIE → Fenster offen bis Hardcap/manuellem
Stop (→ stream_end → Phantom).
Fix: No-Speech-Timeout im _tick — wenn nach endpoint_ms (Stille-Toleranz) ab
Start noch KEINE Stimme kam, schließt der Bridge das Fenster selbst als
no-speech (leer, lautlos, zurück aufs Wake-Word). voiced_frames==0 →
_finalize verwirft ohne Transkript, also kein Phantom. Logik gegen totale
Stille / Sprache-dann-still / Dauer-Sprache verifiziert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aus dem ai-box-Log gelernt: die realen Silence-Phantome ('Die Stadt hat eine
Fläche von 1,5 km²') kommen ALLE mit reason=stream_end — Passiv-/Wake-Fenster
enden auch per stream_end, wenn sie auf Stille zumachen. stream_end ist also
NICHT gleich 'manueller Stop mit bewusster Sprache'. Meine vorige Fassung nahm
stream_end aus → Phantome liefen durch.
Jetzt: (1) Pre-Guard greift auch bei stream_end, aber mit Schwelle voiced==0
(kurze bewusste Wörter am Button gehen durch, echte Stille nicht). (2) Phrase-
Filter gilt für ALLE reasons; das borderline-Band (wenig voiced_frames) schützt
echte, klar gesprochene Geo-Fragen. Gegen alle 3 Log-Fälle + reale Eingaben
verifiziert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Punkt 3: 2. Netz nach der Transkription. Im borderline-Band (wenig echte
Stimme) werden leere/Artefakt-Transkripte verworfen statt als Phantom ans
Brain zu gehen ('Die Stadt hat eine Fläche von 1,5 km²' aus Fast-Stille).
Bekannte Voxtral-Silence-Artefakte (Untertitel-Credits, Geo-/Städte-Fakten)
per Regex, gegated auf voiced_frames — echte Geo-FRAGEN (normale Energie)
gehen durch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Punkt 4B: jeder System-Prompt bekommt einen kompakten Titel-Index der bewusst
gespeicherten Nachschlage-Memories (Zugangsdaten, Infra, Projekte) — ARIA
sieht WAS sie hat und holt es via memory_search, statt Stefan nach etwas zu
fragen, das schon da ist (Git-Credentials-Vorfall). Auto-distillierte Fakten
+ Conversation-Logs sind ausgefiltert → billig.
Punkt 1: _CONV_END_VERB erkennt jetzt auch 'beendet' (beend\w*) und 'stoppe'
(stop\w*) — 'Konversation beendet' schloss vorher das Mikro nicht.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neues Feld scope=system|personal auf jedem Memory-Punkt. Bootstrap-Export
getrennt: System-Regeln (generisch, teilbar) vs. Persönliches (Name,
Zugangsdaten, Projekte). Import ist scope-sicher — ein System-Import löscht
NICHT die persönlichen pinned Memories. seed_rules + AGENT.md/TOOLING.md →
system, USER.md-Präferenzen → personal. Backfill für Bestand (57 system /
617 personal). Diagnostic: zwei Export-Buttons, scope-Badge (SYS/PRIV) +
Umschalter pro Memory.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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ü. ebe0e80 — nur Aufräumen. tsc grün.
Deploy: neue APK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Stefans Repro: "spiel Spotify auf Smartphone ab ABER Konversation fortführen" →
die Kette endete trotzdem. Grund: "spiel spotify" trifft den Fast-Path (Regex),
der den Satz-Rest nicht versteht → converse=false aus dem Skill-Manifest; ARIAs
[[WEITER]]-Marker lebt in Claude, der wurde uebersprungen.
Fix: _user_wants_conversation_continue() — Gegenstueck zu _user_wants_conversation_
end(). Erkennt "Konversation/Gespraech fortfuehren/weiterfuehren/offen halten/nicht
beenden", "weiter reden/sprechen" etc. und erzwingt converse=true an ALLEN Returns
(fast-path/local/claude), auch wenn das Manifest false sagt. [[ENDE]]/_wants_end hat
Vorrang bei Widerspruch. Getestet inkl. Negativfaelle (sofort/spotify ab).
Deploy: Brain-Neustart.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stefans Repro "nichts geht mehr" kam NICHT von den Marker/Guard-Aenderungen,
sondern von der Speaker-ID: die Bridge-Logs zeigten fast durchgehend
"stt_endpoint mit leerem Text — ignoriert (reason=speaker_mismatch)" — ein aus
einem kaputten Enroll (AAC-als-PCM) entstandener Muell-Fingerprint hat Stefans
EIGENE Stimme abgelehnt und damit die komplette STT lahmgelegt.
Fix: Speaker-ID-Gating ist jetzt ein expliziter Schalter, Default AUS. Bei aus
laeuft die Pruefung GAR NICHT (fail-open, alle Stimmen durch) — ein schlechter
Enroll kann nie wieder alles abwuergen. Damit ist Stefans Problem schon durch den
Voxtral-Rebuild geloest (kein Loeschen noetig, der Check greift einfach nicht).
- voxtral + whisper bridge: SPEAKER_ID_ENABLED (Default False, ENV VOICE_ID_ENABLED),
_check_speaker faellt bei aus sofort fail-open zurueck; config-Broadcast
voiceIdEnabled setzt es zur Laufzeit.
- diagnostic: Schalter "Nur meine Stimme" in der Voice-ID-Sektion (Default aus,
Hinweis: erst an wenn enrollt), broadcastet + persistiert voiceIdEnabled.
Reihenfolge lt. Stefan: erst Konversation sauber, dann Multi-Person/nur-ich.
Deploy: docker compose up -d --build voxtral-bridge; aria-diagnostic neu starten.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>