Commit Graph
305 Commits
Author SHA1 Message Date
duffyduckandClaude Opus 5.5 9529bb0b16 feat(app): Aufnahme verwerfen — Muelleimer neben dem Aufnahmeknopf
Bisher hiess "Stopp" immer "senden"; wer es sich anders ueberlegt hatte,
konnte die Aufnahme nicht zuruecknehmen.

- App: waehrend der Aufnahme 🗑 links neben dem roten Knopf. Verwirft ueber
  ariaStopRecording('user-discard') → cancelStreamingRecording; das leere
  Endpoint raeumt die Sprechblase ab, zurueck aufs Wake-Word.
- Fund dabei: "cancel" schickte der STT-Bridge nur stt_stream_end — die
  transkribierte trotzdem und der Text ging an ARIA (fiel nur nicht auf,
  weil cancel bisher bei Stille kam). Jetzt:
  - Whisper- und Voxtral-Bridge: stream_end mit reason "cancel:*" →
    discard, kein Transcribe, leerer Text (reason "cancelled").
  - aria-bridge merkt sich verworfene requestIds (2 min) und wirft einen
    trotzdem ankommenden Text weg — Schutz auch bei alter STT-Bridge.

Getestet: Whisper-Bridge mit nachgebautem Runner (Stopp → Text, Muelleimer
→ kein Transcribe, leer). App: TypeScript ohne Fehler; auf dem Handy noch
nicht ausprobiert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 20:05:35 +01:00
duffyduckandClaude Opus 5.5 ae2024ef55 feat(app): "Auf Updates pruefen" sagt immer, was los ist
Keine neue Version: Meldung mit installierter Version, neuester Version auf
dem Server und ob Update-Server und Download-Weg erreichbar sind (HEAD auf
/update/latest.apk). Keine Verbindung: Meldung mit Server-Adresse, Zustand
und letztem Verbindungsfehler. Keine Antwort in 10 s: Hinweis auf alten RVS.
Neue Version: wie bisher der Update-Dialog (nur einmal, nicht doppelt ueber
den ChatScreen-Listener).

RVS antwortet auf update_check jetzt auch ohne neue Version (update_none
mit latest/current).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:26:23 +01:00
duffyduckandClaude Opus 5.5 a00199c121 feat(update): App laedt Updates per HTTP mit Fortschrittsanzeige statt als 65-MB-WebSocket-Block
Bisher kam die APK als EINE WebSocket-Nachricht (~65 MB Base64) mit starrem
60-s-Timeout — ueber den externen Proxy (Heim-Upload → Hetzner → Handy) zu
knapp, Abbruch ohne Fortschritt.

RVS: GET/HEAD /update/latest.apk — nur mit gueltigem Raum-Token im Header
X-RVS-Token (sonst 403), Content-Length, Range/206 zum Fortsetzen. Version
im Dateinamen wird ohne angehaengten Punkt erkannt.

App: Download per HTTP (react-native-fs) mit Fortschritt in % und MB,
Abbrechen-Knopf, Abbruch nur bei 30 s ohne Daten statt fester Gesamtzeit,
3 Versuche, Groessen-Check gegen Content-Length. Alter RVS (404): Fallback
auf den WebSocket-Weg mit 5 min Timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:10:07 +01:00
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
duffyduckandClaude Opus 4.8 a490513f25 feat(voice): Halb-Duplex-Default + Barge-in-Schalter + echter Cancel (E2)
Behebt den Konversations-Mischmasch: (1) Barge-in-Einstellung (Default AUS = Halb-Duplex) gated das Mikro-Lauschen waehrend TTS -> ARIA spricht ungestoert zu Ende. (2) interruptAriaIfBusy bricht die Brain-Antwort WIRKLICH ab (cancel_request) statt nur TTS zu muten -> sie antwortet nicht mehr weiter waehrend man redet. (3) Stop-Button stoppt vorhersehbar alles (TTS + Brain-Cancel). Musik: nativer Focus ist schon GAIN_TRANSIENT (pausiert) -> der saubere Flow behebt das Focus-Geflacker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 12:57:24 +02:00
duffyduckandClaude Opus 4.8 d61ce316a6 feat(wake): schnellere/empfindlichere Wake-Word-Defaults (E1)
Gegen 'traege': patience 2->1, STARTUP_SUPPRESSION_MS 1500->600, Foreground-Cooldown 3000->1000, Resume-Cooldown 1500->500, Threshold-Default 0.6->0.45. Fehlausloeser faengt die Speaker-ID (E3) ab. Wort bleibt 'computer'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 12:57:24 +02:00
duffyduckandClaude Opus 4.8 fea503a6f3 feat(voice): Stille-Toleranz bis 8s stellbar (Denkpausen)
STT_ENDPOINT_MAX_MS 4000->8000. Der (in a) auf den aktiven Endpoint umgeklemmte 'Stille-Toleranz'-Regler geht damit bis 8s statt nur 4s — genug Zeit zum Nachdenken, ohne dass die Aufnahme endet. Fliesst per endpointMs an Voxtral/Whisper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 12:04:40 +02:00
duffyduckandClaude Opus 4.8 4a743737d3 fix(settings): 'Stille-Toleranz' steuert aktiven Endpoint, dB-Regler raus
Der sichtbare 'Stille-Toleranz'-Regler verstellte den toten Legacy-dB-VAD-Pfad; jetzt steuert er STT_ENDPOINT_MS (die echte Streaming-Pausen-Toleranz, 1-4s). Der obsolete 'Stille-Pegel (dB)'-Regler ist entfernt — der aktive STT nutzt adaptiven Rausch-Boden, Rauschen-als-Wort verhindert der no_speech_prob-Filter des Modells.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 10:48:01 +02:00
duffyduckandClaude Opus 4.8 bd04305b34 fix(voice): Hard-Cap an 'Max. Aufnahmedauer'-Setting + Dauer an Bubble
Wake-Word/Barge-In waren hart auf 60s gecappt (schnitt lange Diktate bei 1 min ab). Jetzt lesen sie loadMaxRecordingMs() — der bestehende, aber vom Streaming-Pfad abgeklemmte 'Maximale Aufnahmedauer'-Regler (1-30 min) steuert nun wirklich. Voice-Bubbles zeigen die Aufnahmedauer (durationS aus stt_endpoint) als 'M:SS'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 10:48:01 +02:00
duffyduckandClaude Opus 4.8 0b760d44a0 feat(m1): generative Flaeche — Orb + Karten-Renderer (present_view)
Visueller Renderer fuer aria_view: Orb (reanimated-Puls je Zustand), CardView (text/image/list/map/code, Sci-Fi-Chrome), AriaViewCanvas (pannbare Flaeche, 2-Finger-Pan + Pinch, Karten materialisieren gestaffelt). WorkspaceScreen blendet die Flaeche als Overlay ueber Chat/Cockpit ein, sobald ARIA fuers fokussierte Projekt eine Ansicht komponiert. v1/Vorgeschmack, tsc-clean; Markdown=Klartext, Map=Marker-Liste.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 03:22:08 +02:00
duffyduckandClaude Opus 4.8 3889d72726 feat: GPS-Trigger-Kopplung + generatives Cockpit (aria_view)
GPS: near()/entered_near()/left_near()-Watcher schalten das Tracking automatisch an bzw. beim Loeschen des letzten wieder aus; gpsTracking.start() sichert jetzt die Background-Permission. Cockpit-Fundament: neues Brain-Tool present_view emittiert aria_view (Orb + Karten), Bridge/RVS leiten weiter, ariaView.ts haelt die Spec App-seitig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 01:21:43 +02:00
duffyduckandClaude Opus 4.8 5700b53b56 fix(stt): adaptiver VAD-Schwellwert gegen Satz-Cutoff
Feste RMS-Grenze (0.012) durch rauschboden-relativen Schwellwert ersetzt (fast-down/slow-up, geklammert). Leises/entferntes Sprechen gilt nicht mehr faelschlich als Stille und wird nicht mitten im Satz gecuttet. audio.ts: Fallback-endpointMs 1500->2400 vereinheitlicht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 01:21:43 +02:00
duffyduckandClaude Opus 4.8 d6d7805509 feat(logger): native App-Crashes ueber RVS erkennen (ohne adb)
Native Crashes/OOM schreiben keinen JS-Fehler → tauchten in app.log
nirgends auf (nur der Whisper-Reboot als Symptom). Jetzt: ein RUN_MARKER
bleibt gesetzt solange die App aktiv laeuft, wird bei sauberem Hintergrund-
Wechsel geloescht. Ist er beim naechsten Start noch da, ist der vorige Lauf
unsauber gestorben → Report via RVS ('app.crash-detected') mit dem letzten
Breadcrumb (was die App zuletzt tat) + Zeitabstand. Breadcrumbs kommen aus
reportAppError/reportAppDebug, throttled in AsyncStorage persistiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-08 16:51:18 +02:00
duffyduckandClaude Opus 4.8 7ab2e3c101 feat(zwischenruf): Korrektur mitten in den laufenden Turn schieben
Neben Senden ein 'Zwischenruf' (App: oranger Button, nur wenn ARIA im
aktiven Kontext arbeitet; Diagnostic: Buttons neben beiden Senden). Geht
NICHT in die Queue und bricht NICHT ab — die Nachricht wird in den
laufenden claude-Subprozess geschoben; er greift sie an der naechsten
Tool-Grenze auf.

Technik:
- proxy-patches/manager.js: claude laeuft jetzt im --input-format
  stream-json-Modus, initialer Prompt als stream-json User-Message,
  stdin bleibt OFFEN; sendMessage() schiebt weitere User-Messages nach;
  bei 'result' wird stdin geschlossen (Turn endet sauber). Ersetzt die
  bisherigen sed-Patches (jetzt volle Datei via cp, docker-compose.yml).
- proxy-patches/routes.js: Side-Channel POST /interject {projectId,text}
  → subprocess.sendMessage der Kontext-Subprozesse.
- rvs: 'interject' erlaubt. bridge: RVS interject → Proxy /interject.
- App/Diagnostic: Zwischenruf-Buttons + lokale '📣 Zwischenruf'-Bubble.

Empirisch verifiziert: mid-turn injizierte Message wird an der naechsten
Tool-Grenze aufgegriffen (nicht mitten in einem blockierenden Befehl).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 05:05:13 +02:00
duffyduckandClaude Opus 4.8 4eeba3cf5f fix(files): Bilder korrekt zuordnen bei Queue (clientMsgId-Korrelation)
Die App schickt Datei (fire-and-forget) und Text (ACK-getrackt, bei Queue
verzoegert) als getrennte RVS-Nachrichten; die Datei trug keine clientMsgId.
Die Bridge mergte gepufferte Files rein per Timing an den naechsten Text →
bei mehreren schnellen Nachrichten landeten Bilder beim falschen Text.

App: file-Send traegt jetzt dieselbe clientMsgId wie der Text.
Bridge: puffert Files mit cmid und merged beim Text-Flush NUR die Files mit
passender cmid; Rest bleibt fuer seine eigene Nachricht gepuffert. Fallback
(Legacy/kein Treffer) = altes Verhalten, damit nie ein Bild verloren geht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:41:14 +02:00
duffyduckandClaude Opus 4.8 71bb910672 fix(vnc): Tastatur-Button folgt echter Tastatur-Sichtbarkeit
Androids Zurueck-Taste versteckt die Tastatur ohne den TextInput zu
blurren → ⌨-Button blieb an, erst der uebernaechste Tap oeffnete wieder.
Jetzt setzt keyboardDidShow/Hide den Button-Zustand; Oeffnen erzwingt
blur→focus, damit ein noch fokussiertes Feld die Tastatur neu aufklappt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 01:19:37 +02:00
duffyduckandClaude Opus 4.8 fde8c3e9c0 feat(vnc): Fn-Steuertasten-Leiste + Enter-Fix fuer VM-Bedienung
Die Software-Tastatur liefert weder Enter (Haken) noch Esc/Pfeile/F-Tasten/
Strg/Alt. Neu:
- Haken der Tastatur → onSubmitEditing sendet jetzt Enter (returnKeyType=send).
- Fn-Leiste (Toggle in der ctlBar): Esc, Tab, Pfeile, Pos1/Ende/Bild, Einfg/
  Entf, Enter, F1–F12, Strg+Alt+Entf — direkt an rfb.sendKey.
- Sticky-Modifier Strg/Alt/Shift (one-shot): kombinieren mit der naechsten
  Taste ODER dem naechsten getippten Zeichen → Strg+C, Strg+Alt+Entf, Alt+F …
- noVNC: window.ariaVncKey.combo(mods,ks) haelt Modifier, tappt, laesst los.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 01:17:56 +02:00
duffyduckandClaude Opus 4.8 59b09e3491 fix(app): gruener Desktop-Punkt nur bei laufender VM im aktiven Projekt
Badge haing bisher am globalen desktop_status (irgendwo ein Desktop
erreichbar) → wurde generell angezeigt. Jetzt aus der projektbezogenen
VM-Liste (listProjectVms → running), pid-Guard, 6s-Refresh fuer Start/Stop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 01:08:37 +02:00
duffyduckandClaude Opus 4.8 41590c90a8 fix(vnc): Tastatur ueber echtes RN-TextInput statt WebView-Hidden-Input
Der versteckte WebView-Input oeffnete die Android-Software-Tastatur
unzuverlaessig. Jetzt haelt VncTile ein echtes RN-<TextInput>
(keyboardType=visible-password), der ⌨-Button fokussiert es. Getippte
Zeichen gehen per injectJavaScript an window.ariaVncKey.char/keysym →
rfb.sendKey. Prefix-Diff fuer Druckbares, onKeyPress fuer Backspace/Enter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 01:06:27 +02:00
duffyduckandClaude Opus 4.8 fad9aceb0e fix(app): kein 404 bei Dateien/Desktop im Hauptchat (leere project_id)
Im Hauptchat (projectId='') riefen FilesTile/DesktopTile /projects//files bzw.
/vms auf → Brain 404. Beide guarden jetzt leere projectId: kein Fetch, klare
Meldung "Kein aktives Projekt — wechsle in ein Projekt".

tsc clean. App-only, Deploy: APK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 00:59:50 +02:00
duffyduckandClaude Opus 4.8 f3f8002061 feat(vnc): echtes Vollbild beim Verbinden (randlos ueber das ganze Display)
Der ⤢-Knopf war nur der Fit/1:1-Umschalter (Zoom) INNERHALB des eingerahmten
Streifens (Header+Dock drumrum) — sah deshalb wie Zoom aus, fuellte nie das
Display. Jetzt oeffnet "Verbinden" die VM in einem Fullscreen-Modal: randlos
ueber alles (Header + Dock weg), scaleViewport passt den VM-Screen sauber ein,
Landscape erlaubt. Buttons schweben drueber (‹ VMs links, ⌨/Strg+Alt+Entf/⤢
rechts, unter der Statusleiste). Android-Back verlaesst Vollbild.

tsc clean. App-only, Deploy: APK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 00:51:59 +02:00
duffyduckandClaude Opus 4.8 c5449641ef fix(vm): Screenshot ohne /root-Rechte + VNC-Tastatur robuster
Screenshot: aria-vm lief als User 'aria', SHOT_DIR war aber /root/... →
"mkdir: cannot create directory /root". Jetzt schreibt aria-vm das PNG ins
VM-Verzeichnis (aria-schreibbar) und der Brain holt es per SSH (base64,
_ssh_host) — unabhaengig von Volume-Rechten. End-to-end validiert (gueltiges
PNG). Wird weiter ins Projekt (screenshots/) kopiert.

VNC-Tastatur: verstecktes Input-Feld war off-screen (opacity:0, left:-1000px) →
Android oeffnete die Tastatur oft nicht / lieferte keine Events. Jetzt on-screen
(bottom, 1px, opacity:0, pointer-events:none) + beforeinput als primaerer
Handler (Android-robust) mit input-Fallback. Strg+Alt+Entf ging schon.

py/bash/tsc clean. aria-vm bereits live. Deploy: brain rebuild + APK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 00:45:41 +02:00
duffyduckandClaude Opus 4.8 c07a59e36e feat(app): Dateien-Panel im Dock (zwischen Chat und Code)
Neues 📁-Panel listet ALLE Projektdateien (/shared/projects/<id>/) — auch
erzeugte Bilder, nicht nur Code; dieselben, die in der Projektliste als 📄
gezaehlt werden. Bild antippen → Vollbild-Vorschau; Textdatei → Text-Vorschau.
- Brain: /projects/<id>/file?binary=1 → Base64 + MIME (fuer Bilder, max 8 MB).
- App: brainApi.readProjectFileBinary; layout 'files'-Tile; Dock-Reihenfolge
  Chat · Dateien · Code · Desktop; FilesTile mit Bild-/Text-Modal.

py/tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 00:09:34 +02:00