Commit Graph
1049 Commits
Author SHA1 Message Date
duffyduckandClaude Opus 4.8 778d75ae61 fix(fleet): Worker-RVS-URL ohne /ws — an Bridge/App/Satellit/Diagnostic angleichen
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>
2026-09-19 04:47:03 +02:00
duffyduck f3e3aedc6b debug(diagnostic): worker_hello-Empfang loggen (Flotten-Diagnose) 2026-09-19 04:19:30 +02:00
duffyduckandClaude Opus 4.8 2d2c182a0c fix(fleet): worker_hello periodisch wiederholen (wie Satellit) — Box wird nach Diagnostic/Bridge-Neustart wieder sichtbar
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>
2026-09-19 04:12:16 +02:00
duffyduck 831fbfab58 revert(compute): xtts RVS_PORT zurueck auf 444 — ARIA-Stack laeuft auf 444, nicht 443 2026-09-19 04:03:52 +02:00
duffyduckandClaude Opus 4.8 a0dedd8c97 fix(compute): xtts RVS_PORT-Default 444→443 (an ARIA-Stack angleichen)
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>
2026-09-19 03:58:01 +02:00
duffyduckandClaude Opus 4.8 8633840f94 fix(diagnostic): Modell-Katalog als Tabelle (Beschreibung + VRAM-Spalten), Button-Layout, Flotte nachziehen
- 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>
2026-09-19 03:48:38 +02:00
duffyduckandClaude Opus 4.8 45167b7bb5 refactor: ai-box/ (bootstrap + README) nach xtts/ verschieben
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>
2026-09-19 03:33:15 +02:00
duffyduckandClaude Opus 4.8 66781d8d90 feat(fleet): Auslastungs-Monitor pro Box — live nvidia-smi + Graphen (Stage E)
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>
2026-09-19 03:26:17 +02:00
duffyduckandClaude Opus 4.8 5c25d6abeb feat(llm): Modell-Download + HuggingFace-Katalog (Stage D)
Neue lokale GGUF-Modelle per Knopf auf eine Box laden — ohne Image-Rebuild.

- llm-adapter besitzt jetzt llama-swaps Config: generiert
  /models/llama-swap.config.yaml aus Basis-Template (xtts/llama-swap/config.yaml)
  + persistenter Registry /models/aria_models.json. Neue RVS-Handler
  llm_provision_model / llm_remove_model (targetInstance-gefiltert): Registry+
  Config schreiben, llama-swap-Reload anstossen, neu announcen, Warmup (zieht das
  GGUF via -hf, Fortschritt via service_status loading→ready). pyyaml ergaenzt.
- compose: llama-swap liest --config /models/llama-swap.config.yaml; llm-adapter
  mountet ./models (rw) + ./llama-swap (ro Template).
- diagnostic/server.js: /shared/config/llm_catalog.json (kuratierte GGUF-Liste)
  + GET /api/llm-catalog + POST /api/llm-catalog/refresh (HuggingFace-API-Merge);
  Actions llm_provision_model / llm_remove_model / llm_test; llm_provision_result
  an Browser durchgereicht.
- diagnostic/index.html: "Modell-Katalog"-Card (HF-Refresh, Ziel-Box waehlen,
  Laden, Verfuegbarkeit) + Test-Chat-Zeile ans lokale LLM (Antwort + Latenz).

Download nutzt llama-swaps vorhandenen -hf-Pfad (kein neuer Download-Code).
Reload ist der einzige Deploy-Verify-Punkt (llama-swap-Image); Fallback Box-up.
Deploy: diagnostic neu bauen (VM) + llm-Boxen neu bauen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 03:12:31 +02:00
duffyduckandClaude Opus 4.8 05c6c7687a feat(llm): Flotten-Katalog + modell-bewusstes LLM-Routing (Stage C)
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>
2026-09-19 02:49:30 +02:00
duffyduckandClaude Opus 4.8 0f122a1ad7 feat(voice): zentraler Stimmen-Store + Auto-Provisioning der f5tts-Flotte
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>
2026-09-19 01:32:06 +02:00
duffyduckandClaude Opus 4.8 a773832bca 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 17a60e5d3e refactor(diagnostic): Einstellungen in 3 Sub-Tabs gliedern
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>
2026-09-18 23:49:25 +02:00
duffyduckandClaude Opus 4.8 182351e022 docs(compute): Whisper-als-STT-Beispiel fuer kleine GPUs
.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>
2026-09-18 14:13:35 +02:00
duffyduckandClaude Opus 4.8 ec78eb8efe 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
duffyduckandClaude Opus 4.8 f2ead1242f feat(compute): Worker-Selbstanmeldung ueber RVS + Flotten-Anzeige (Stage 2)
Jeder GPU-Dienst meldet sich beim Connect mit worker_hello {instanceId,
service, node, gpus, model} und haelt die Registry per periodischem
worker_ping {instanceId, busy} (~10s) frisch. So weiss ARIA, was wo laeuft.

- xtts/{voxtral,whisper,f5tts}/bridge.py + llm-adapter/adapter.py:
  INSTANCE_ID=service@NODE_NAME, _worker_register()-Coroutine (hello + ping),
  busy-Quelle je Worker (aktive STT-Sessions / TTS-Render / in-flight LLM);
  Task sauber gecancelt bei Reconnect.
- bridge/aria_bridge.py: self._workers-Registry + Handler worker_hello/
  worker_ping (spiegelt sat_hello), _worker_list() (35s-Offline-TTL),
  _pick_worker() (Round-Robin freie Instanz, fuer Stage-3-Routing),
  /internal/worker-list-Endpoint.
- diagnostic/server.js: workers-Map, worker_hello/worker_ping-Tracking,
  worker_update-Broadcast + worker_list-Action + on-connect-Snapshot.
- diagnostic/index.html: "Compute-Flotte"-Panel im Satelliten-Tab — pro Node
  gruppiert, mit Dienst/Modell/GPU und frei/beschaeftigt/offline-Status.

Stage 2 von 3. Reine Sichtbarkeit, kein Routing-Verhalten geaendert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-18 12:22:47 +02:00
duffyduckandClaude Opus 4.8 e75f1eeb6a feat(compute): Multi-Node-Aufteilung via Compose-Profile + GPU-Wahl per .env
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>
2026-09-18 12:14:58 +02:00
duffyduckandClaude Opus 4.8 2bd7c747d9 fix(diagnostic): Bild-Upload landet im fokussierten Projekt statt Hauptchat
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>
2026-09-03 08:38:58 +02:00
duffyduckandClaude Opus 4.8 d0694cb637 fix(voice): 'gute Nacht' loest nicht mehr faelschlich Ohr-Aus aus
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>
2026-08-16 22:43:18 +02:00
duffyduckandClaude Opus 4.8 8f313eb68b feat(voice): ear_control-Tool — ARIA versteht Ohr-Aus/-An in jeder Formulierung
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>
2026-08-16 22:24:04 +02:00
duffyduckandClaude Opus 4.8 954ad9ade8 fix(voice): wake-off erkennt mehr Schlaf-Idiome
"leg dich schlafen" / "leg dich aufs Ohr" / "leg dich hin" / "gute Nacht" /
"schlaf gut" zusätzlich zu "geh schlafen". Sofort-Netz (instant, kein Claude-
Call). Der robustere Weg (ear_control-Tool, ARIA versteht jede Formulierung)
folgt separat, wenn Stefan will.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 22:19:29 +02:00
duffyduck 75fa574a85 release: bump version to 0.2.4.8 v0.2.4.8 2026-08-16 22:01:07 +02:00
duffyduckandClaude Opus 4.8 a3d7933949 fix(voxtral): Speech-Endpoint gegen laute Umgebungsmusik
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>
2026-08-16 21:57:06 +02:00
duffyduck 626281dead release: bump version to 0.2.4.7 v0.2.4.7 2026-08-16 21:39:35 +02:00
duffyduckandClaude Opus 4.8 49ea172617 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 a3650bb0e4 release: bump version to 0.2.4.6 v0.2.4.6 2026-08-16 21:17:07 +02:00
duffyduckandClaude Opus 4.8 514ba44e51 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 617dddf3d8 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 88faf57253 release: bump version to 0.2.4.5 v0.2.4.5 2026-08-16 20:40:47 +02:00
duffyduckandClaude Opus 4.8 41714f7cf1 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 93401d42d1 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
duffyduckandClaude Opus 4.8 c4e658446d feat(voxtral): Silero VAD — echte Sprach-Erkennung gegen generische Phantome + Musik
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>
2026-08-16 20:18:06 +02:00
duffyduck 3693157210 release: bump version to 0.2.4.4 v0.2.4.4 2026-08-16 19:57:14 +02:00
duffyduckandClaude Opus 4.8 64670cdd12 fix(voxtral): Repetition-Loop bremsen ('vergiss das'-Schleife)
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>
2026-08-16 19:42:22 +02:00
duffyduckandClaude Opus 4.8 c82616ebbd fix(voxtral): No-Speech-Timeout — Stille-Fenster schließt selbst
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>
2026-08-16 19:38:38 +02:00
duffyduck b226e1da11 release: bump version to 0.2.4.3 v0.2.4.3 2026-08-16 11:33:45 +02:00
duffyduckandClaude Opus 4.8 0b7ed241b4 fix(voxtral): Phantom-Filter greift auch bei stream_end
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>
2026-08-16 11:30:20 +02:00
duffyduck a2b7e3a48d release: bump version to 0.2.4.2 v0.2.4.2 2026-08-16 11:26:49 +02:00
duffyduckandClaude Opus 4.8 7b72149671 fix(voxtral): Halluzinations-Filter gegen Phantom-Text aus Stille
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>
2026-08-16 11:19:06 +02:00
duffyduckandClaude Opus 4.8 e9439dbccb 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
duffyduckandClaude Opus 4.8 219de091d2 feat(brain): Titel-Index fürs kalte Gedächtnis + 'beendet' erkennen
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>
2026-08-16 11:19:06 +02:00
duffyduckandClaude Opus 4.8 5992a7e441 feat(brain): Gedächtnis nach scope trennen (system/personal)
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>
2026-08-16 11:19:06 +02:00
duffyduck 07ccf05429 release: bump version to 0.2.4.1 v0.2.4.1 2026-08-16 02:02:10 +02:00
duffyduckandClaude Opus 4.8 4ab0e68245 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 9636a702d3 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ü. ebe0e80 — 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 5f09e2bca3 release: bump version to 0.2.4.0 v0.2.4.0 2026-08-16 01:48:43 +02:00
duffyduckandClaude Opus 4.8 ebe0e8065f 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
duffyduckandClaude Opus 4.8 9d2c07d8d1 feat(voice): "Konversation fortführen" hält offen — auch bei Fast-Path-Befehl
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>
2026-08-16 01:35:49 +02:00
duffyduck 9aae5af6a9 release: bump version to 0.2.3.9 v0.2.3.9 2026-08-16 01:06:18 +02:00
duffyduckandClaude Opus 4.8 a8ff73f93d feat(speaker-id): Gating als bewusster Schalter — Default AUS (fail-open)
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>
2026-08-16 01:00:58 +02:00