Commit Graph
943 Commits
Author SHA1 Message Date
duffyduckandClaude Opus 4.8 55938c3173 fix(brain): Trigger-Zeit UTC-konsistent, project_summary liest Hauptchat, Privatkopie-Regel
- Trigger-Zeit (Issue 4): fires_at wird beim Anlegen IMMER auf UTC normalisiert
  (naiv = lokale Wanduhrzeit Europe/Berlin → UTC, aware → astimezone). Passt zur
  UTC-Ausfuehrung in background.py → feuert zur gemeinten Ortszeit, zeitzonen-
  portabel. Tool-Desc + Prompt: fires_at = lokale Wanduhrzeit ohne Zone schreiben,
  Server rechnet um. Behebt den 2h-Versatz.
- project_summary (Issue 2): liest jetzt auch den Hauptchat (name='Hauptchat')
  und die Beschreibung sagt ARIA, es fuer 'schau mal in Projekt X / Hauptchat
  rein' / 'hol dir die Infos aus ...' zu nutzen.
- Privatkopie (Issue 3): IDENTITY_ANCHOR-Regel — YouTube-Downloads fuer Stefans
  PRIVATgebrauch sind eine Privatkopie (§53 UrhG), nicht reflexartig ablehnen/
  moralisieren; Urteil bleibt fuer Weiterverbreitung/kommerziell.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:12:02 +02:00
duffyduck 761217cb5a release: bump version to 0.2.1.6 v0.2.1.6 2026-07-12 10:09:49 +02:00
duffyduckandClaude Opus 4.8 ff6d31acbd docs(readme): Pro-Projekt-Queue, Rueckfrage-Loop, Drafts + TTS-Abspiel-Queue
App-Features + Completed-Log aktualisiert; veraltetes Barge-In-Cancel-Verhalten
durch das neue Anstellen-statt-Abbrechen ersetzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 10:07:18 +02:00
duffyduckandClaude Opus 4.8 2d7a5784c2 docs(changelog): 0.2.1.5 — Pro-Projekt-Queue mit Rueckfrage-Loop
Queue pro Projekt (anstellen statt abbrechen) + [[AWAIT]]-Rueckfrage-Loop,
Pro-Projekt-Textfeld-Entwuerfe, TTS-Abspiel-Queue (back-to-back sprechen
nacheinander), Voice bricht nicht mehr ab. App + Diagnostic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 10:05:08 +02:00
duffyduckandClaude Opus 4.8 fa871219ae feat(queue): TTS-Abspiel-Queue — back-to-back-Antworten sprechen nacheinander
Bisher war die serialisierte Sprachausgabe zweier fast gleichzeitig fertiger
Antworten Timing-Glueck: PcmStreamPlayer.start() ruft stopInternal() (flush+
release), eine neue Antwort haette die laufende also abgeschnitten, sobald ihr
Audio waehrend der Wiedergabe der ersten ankam.

Jetzt echte Abspiel-Queue im audioService: kommt eine neue HOERBARE Antwort
waehrend eine andere noch hoerbar spielt (pcmAudiblePlaying bis
PcmPlaybackFinished, nicht nur bis Stream-Ende), werden ihre PCM-Chunks
gepuffert und erst nach dem Drain der laufenden nachgespielt. Bei wartender
Antwort meldet PcmPlaybackFinished NICHT 'fertig' (kein Wake-Word-Re-Arm).
Harter Stop/Barge-In/Mute verwirft die Queue. Race gegen gleichzeitige Chunks
einer dritten Antwort geschlossen (Flags vor await gesetzt). onPcmCached meldet
den WAV-Pfad nachgespielter Antworten fuer Mund-Button-Replay.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 10:03:31 +02:00
duffyduck e61a0ff871 release: bump version to 0.2.1.5 v0.2.1.5 2026-07-12 09:44:34 +02:00
duffyduckandClaude Opus 4.8 c9a1d80696 feat(queue): Diagnostic spiegelt Pro-Kontext-Queue + Rueckfrage + Drafts
Diagnostic bekommt denselben Queue-Automaten wie die App: anstellen statt
abbrechen, awaiting_reply pausiert die Queue (naechste Eingabe beantwortet die
Rueckfrage), Queue-Banner mit loeschbaren Eintraegen ueber dem Eingabefeld,
und Pro-Kontext-Textfeld-Entwuerfe (Feldinhalt bleibt beim Kontextwechsel
erhalten). rvs_chat-Handler liest awaiting_reply aus der Bridge-Payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 09:41:24 +02:00
duffyduckandClaude Opus 4.8 8a0670a3d2 feat(queue): Pro-Projekt-Nachrichten-Queue mit Rueckfrage-Loop + Textfeld-Entwuerfe (App)
Zweite Nachricht waehrend ARIA arbeitet wird jetzt ANGESTELLT statt den
laufenden Task abzubrechen. Stellt ARIA eine blockierende Rueckfrage, pausiert
die Queue und die naechste Eingabe beantwortet sie — bis eine finale Antwort
kommt, dann laeuft der naechste Queue-Eintrag (pro Projekt unabhaengig).

- Brain: ARIA deklariert Rueckfragen per unsichtbarem [[AWAIT]]-Marker
  (wie speak/converse; kein '?'-Raten). _extract_await_marker strippt ihn,
  chat() gibt 5-Tupel (+awaiting_reply), System-Prompt erklaert den Marker.
- Bridge: awaiting_reply aus Brain-Response in die chat-Broadcast-Payload.
- App: app-lokale Queue + Zustandsautomat (idle/running/awaiting_reply) pro
  Projekt; Send-Flow von Abbruch auf Anstellen; Stop-Button (cancelRequest)
  schaltet die Queue weiter; sichtbare pending_queue-Bubbles (tippen loescht);
  Rueckfrage-/Queue-Banner ueber dem Eingabefeld. Voice bricht nicht mehr ab
  (haltet nur TTS, serialisiert im Brain-Lock); Text-Send erkennt Brain-busy
  als Fallback. Pro-Projekt-Textfeld-Entwuerfe (Draft-Map + AsyncStorage).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 09:35:46 +02:00
duffyduckandClaude Opus 4.8 2d9c42a7ea docs(changelog): 0.2.1.1–0.2.1.4 nachgetragen — der große Tag
Vier Versionen aufgeholt: manifest.speak (0.2.1.1); Standort-Intelligenz +
speak/converse-pro-Aufruf + Anti-Halluzination (0.2.1.2); skill_get + converse-
Fast-Path + Voice-Bubble-Fix (0.2.1.3); tool-loses local + Info-Halluzination-
Guard + leeres-<voice>-Fix + Live-Datei-Anhang + TDZ- & QRScanner-Cleanup und
ARIAs live geschärfter Spotify-/yt-dlp-Skill (0.2.1.4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 02:13:42 +02:00
duffyduck ff1205eb6b release: bump version to 0.2.1.4 v0.2.1.4 2026-07-12 02:10:13 +02:00
duffyduckandClaude Opus 4.8 d12f67320b fix(app): QRScanner tsc-clean — toter Prop raus, kaputte camera-kit-Typen umgangen
colorForScannerFrame existiert in react-native-camera-kit v13 nicht (No-Op) —
entfernt. Die Lib markiert zudem etliche optionale CameraScreen-Props faelschlich
als required (defaultProps fuellen sie zur Laufzeit); Props lokal als any
gespreadet, um die fehlerhaften .d.ts zu umgehen ohne Runtime-Verhalten zu
aendern. scanBarcode/onReadCode bleibt die korrekte v13-Barcode-API.

Projekt jetzt komplett tsc-clean (0 Fehler).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 02:08:35 +02:00
duffyduckandClaude Opus 4.8 24a1b4d837 fix(app): sendTextMessage nach seine deps verschoben — TDZ-Fehler weg
sendTextMessage stand vor interruptAriaIfBusy und sendPendingAttachments,
hatte beide aber im deps-Array → Temporal Dead Zone (TS2448/2454). Lief nur,
weil Babel const→var hebt (deps auf erstem Render undefined, Body laeuft erst
bei Interaktion). Deklaration hinter beide verschoben — Abhaengigkeit ist
einseitig, sendTextMessage wird nur in der JSX genutzt. tsc-clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 02:05:52 +02:00
duffyduckandClaude Opus 4.8 db96258f7d fix(app): ARIA-Datei-Anhang erscheint live an der Nachricht, nicht erst nach Seitenwechsel
Die Live-chat-Payload trug keine files — Anhaenge kamen nur als separates
file_from_aria-Event (eigene, teils unsichtbare Bubble). An der Text-Nachricht
tauchte die Datei erst nach einem Seitenwechsel auf (Reload aus chat_backup,
das die files kennt).

- Bridge: files jetzt direkt in der chat-Payload (selbe Struktur wie Backup).
- App: chat-Handler haengt payload.files an die Text-Bubble (wie der Reload-Pfad)
  und entfernt die redundante Solo-file_from_aria-Bubble mit passendem serverPath.
- App: file_from_aria legt keine Doppel-Bubble an, wenn die Datei schon an einer
  Nachricht haengt (Event-Reihenfolge-unabhaengig).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 02:01:37 +02:00
duffyduckandClaude Opus 4.8 becc83d9e3 fix(bridge): leeres <voice></voice> macht TTS nicht mehr stumm
Claude haengt manchmal reflexartig ein leeres <voice></voice> an (v.a. bei
Spotify-Antworten). clean_text_for_tts nahm dann group(1)='' → text='' → gar
keine Sprachausgabe. Das erklaerte, warum Playlist-Wechsel 'Fliegen' vorgelesen
wurde (Claude schrieb Inhalt ins Tag), 'Prodigy' aber stumm blieb (leeres Tag)
— reiner Claude-Output-Zufall, nicht Skill/Playlist-Name.

Fix: leeres/whitespace <voice> ignorieren und den normalen Anzeigetext lesen.
Deterministisch, unabhaengig von Claudes Tag-Reflex.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 00:45:43 +02:00
duffyduckandClaude Opus 4.8 44220edbd1 revert(router): Live-Wortliste raus — Modell soll selbst eskalieren
_LIVE_HINTS war die falsche Idee: aus offenem Freitext per Wortliste die
Absicht raten ist nie vollstaendig, jeder Miss = Halo (nur von per-Skill auf
per-Wort verschoben). Generisch heisst nicht 'groessere Regex', sondern: das
Modell entscheidet selbst ob es Grundwahrheit/ein Tool braucht (<<ESCALATE>>,
Prompt). Der 8B kann das noch nicht zuverlaessig → local bleibt per Einstellung
abschaltbar (aus, nicht raus); ein staerkeres lokales Modell uebernimmt spaeter
die Selbst-Erkennung. Absicherung bleibt der Output-Guard, nicht der Input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 00:20:02 +02:00
duffyduckandClaude Opus 4.8 fc13957000 fix(router): 'lieder'/'songs'/'springe...weiter' matchen jetzt auch (Live-Gate)
'lied' hatte eine Wortgrenze → 'lieder' (lied+er) matchte nicht, 'springe
zwei lieder weiter' rutschte an local vorbei. lied\w*/song\w* + spring\w*
...weiter ergaenzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 00:06:40 +02:00
duffyduckandClaude Opus 4.8 60580fd67e refactor(brain): local wieder tool-los (B1a) — 8B nur noch Reden
B1b hatte dem 8B run_*/web_search-Tools gegeben. Das war die Wurzel von
BEIDEM: (1) ein 8B ist ein unzuverlaessiger Tool-Caller und erfindet lieber
eine plausible Antwort ('der Song ist X') statt das Tool zu rufen → halo;
(2) das zwang zu per-Skill-Guards (skaliert nicht).

Fix ohne eine einzige per-Skill-Zeile:
- Local: tools=[] — kann kein Skill-Ergebnis mehr faelschen.
- Router: _LIVE_HINTS schickt alles mit aktueller Grundwahrheit
  (Wetter/News/Uhrzeit/Musik/Preise/Timer/…) VORAB an Claude, nicht erst
  nach einem gescheiterten Local-Versuch (kein Doppel-Zahlen). Topic-Ebene,
  nicht per-Skill — ein neuer Skill braucht hier keine Zeile.
- Prompt: _AWARENESS sagt local explizit, dass es tool-los ist und fuer
  Live/Aktion eskaliert.

Arbeitsteilung: Fast-Path (Regex→Skill, deterministisch, 0 halo) fuer
Befehle · Local (tool-los) fuers Reden · Claude fuer alles mit Tool/Fakt/
Gedaechtnis/Urteil.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 23:59:31 +02:00
duffyduckandClaude Opus 4.8 2d83e4ad8b fix(brain): Info-Halluzination-Guard + expliziter Claude-Wunsch im Router
Local erfand Live-Auskuenfte (aktueller Song, Restzeit, Skip-Titel) ohne
run_spotify zu rufen. Neuer Detektor _claims_live_media_state() + Guard
eskaliert solche Behauptungen ohne echten Skill-Call an Claude. Local-Prompt
gehaertet: nie Titel/Interpret/Restzeit ohne Tool-Ergebnis nennen.

Router: _FORCE_CLAUDE respektiert 'nimm Claude/Clodi' → nie lokal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 23:52:36 +02:00
duffyduck 629a3d82ac release: bump version to 0.2.1.3 v0.2.1.3 2026-07-11 23:18:27 +02:00
duffyduckandClaude Opus 4.8 9bb90a777a fix(app): Sprachnachricht-Bubble verschwindet nach manuellem Stop nicht mehr
Ohne Ohr (manuelle Aufnahme) + Stop-Button: das echte STT-Endpoint (mit Text)
fuellt die Bubble, aber danach klappt ein zweites, LEERES stream_end-Endpoint
nach (Audio schon transkribiert). Der Empty-Handler loeschte die Bubble
bedingungslos per audioRequestId — auch die schon mit Text gefuellte.
Ergebnis: Bubble weg, obwohl ARIA an der Antwort arbeitet; erst nach
App-Neustart (aus chat_backup) wieder da.

Fix: leeres Endpoint entfernt nur noch den NOCH UNAUFGELOESTEN Platzhalter
(Text enthaelt 'Spracheingabe wird verarbeitet'), nicht eine bereits
aufgeloeste Bubble.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 23:17:04 +02:00
duffyduckandClaude Opus 4.8 c3bb7985a2 fix(skills): skill_get-Tool nachgereicht — kein Blind-Rewrite mehr
Skandal-Ursache: die skill_update-Anleitung sagt "erst skill_get lesen, dann
updaten" — aber skill_get existierte GAR NICHT als Tool. ARIA/Claude konnte
einen Skill also nie LESEN vor dem Aendern → Blind-Rewrite aus Beschreibung +
Gedaechtnis. (Claude sagte korrekt "skill_get steht mir nicht zur Verfuegung"
und hat den Spotify-Skill trotzdem komplett neu geschrieben.)

Neu: skill_get(name) liefert Manifest (args/fast_patterns/speak/converse) +
kompletten entry_code (echter Python) + README. skills.read_skill_source().
So sieht ARIA den Ist-Code und aendert gezielt statt blind zu ueberschreiben.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 23:04:37 +02:00
duffyduckandClaude Opus 4.8 debd96b16f fix(fast-path): converse folgt dem Skill (Manifest/Output), nicht hart False
Fast-Path hatte converse hart auf False — ein Fast-Path-Skill konnte damit nie
ein Dialog-Skill sein. Jetzt konsistent zu local/Claude: speak UND converse
kommen aus dem Skill-Manifest-Default und werden vom Skill-JSON-Output pro
Aufruf ueberschrieben. Default (kein Flag) bleibt false/false = stumm/stop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:57:52 +02:00
duffyduck 486d7dedbb release: bump version to 0.2.1.2 v0.2.1.2 2026-07-11 22:48:43 +02:00
duffyduckandClaude Opus 4.8 d5bcbb4814 feat(skills): Skill steuert Ausgabe selbst — speak + converse, pro Aufruf
Verallgemeinert die Ausgabesteuerung, dynamisch statt hardcoded:
- Zwei Flags: speak (vorlesen?) + converse (danach 30s weiterlauschen?).
  Getrennt, weil 'vorlesen' und 'Dialog offen halten' verschiedene Dinge sind
  ('was laeuft gerade' → vorlesen JA, aber keine 30s).
- Statischer Manifest-Default (speak/converse) PLUS: der Skill kann beide im
  JSON-Output PRO AUFRUF setzen und den Default ueberschreiben — so kann EIN
  Skill gemischt sein (Spotify: 'next' stumm/stop, 'was laeuft' vorlesen/stop).
- chat() gibt jetzt (reply, answered_by, speak, converse) zurueck; gilt fuer
  Fast-Path (converse immer False), local UND Claude (run_*-Skill setzt beide).
- Brain: _last_skill_flags aus Skill-stdout-JSON, _skill_response_flags mergt
  Output > Manifest > False. main.py ChatOut.converse, background.py angepasst.
- Bridge: converse aus /chat gelesen + in Chat-Payload + _process_core_response.
- App: converseRef aus der Payload; onPlaybackFinished endet mit skipPassive
  wenn converse=false (vorlesen ohne 30s).
- Skill-Bau-Anleitung (skill_create/update-Schema): speak+converse dokumentiert
  MIT dem WARUM, damit ARIA sie beim Bauen sinnvoll setzt (nicht nur mechanisch).
- Prompt-Hardcode fuer Spotify-Faehigkeiten raus → Skills beschreiben sich selbst.

Bestehende Skills ohne Flags = false/false = stumm/stop (kein Verhaltenswechsel).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:47:30 +02:00
duffyduckandClaude Opus 4.8 54ebd57990 fix: generischer Skill-Prompt (kein Hardcode) + Stop im 30s-Lauschen beendet
- router.py: die run_spotify-Faehigkeiten NICHT mehr im Prompt aufzaehlen
  (war selbst Hardcoding). Generisch: "run_*-Skills — was sie koennen steht in
  IHRER Tool-Beschreibung, lies + nutze sie fuer alles Passende". Skills
  beschreiben sich selbst (dynamisch, ARIA-authored). Anti-Halluzination bleibt.
- App: Stop-Button waehrend passivem 30s-Lauschen ('listening') beendet jetzt
  sauber (exitPassiveListening) statt via leerem Endpoint den Passiv-Stream neu
  zu starten — die 30s liefen sonst von vorne los.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:36:01 +02:00
duffyduckandClaude Opus 4.8 fb06ed928c fix(local-llm): Anti-Halluzination — behauptete Skill-Aktion ohne Tool → Claude
Local hat "Spotify: überspringe 3 Titel" / "Playlist X abspielen" geantwortet
OHNE run_spotify je zu rufen (Skill-Logs: kein Aufruf) — reine Fabrikation,
real passierte nichts. Zwei Ursachen behoben:
- Prompt: run_spotify-Beschreibung von "next/pause/weiter" auf JEDE
  Musiksteuerung erweitert (Playlist, Gerät, überspringen mehrerer, Lautstärke)
  + harte Anti-Halluzinations-Regel (nie eine Aktion behaupten ohne Tool-Call).
- Guard: sagt local eine Steuerbefehl-Quittung ('Spotify: …', 'Playlist …
  abspielen', 'überspringe … Titel') aber hat KEINEN run_*-Skill gerufen
  (_local_ran_skill=False) → eskalieren, Claude ruft das Tool zuverlässig.

_looks_like_skill_action bewusst eng (Doppelpunkt-Praefix / spezifische Verben),
trifft normale Musik-Konversation nicht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:30:00 +02:00
duffyduckandClaude Opus 4.8 08bb257791 fix(bridge): Ortsname rechtzeitig da — Geocode vor Präfix-Bau awaiten
Der Reverse-Geocode lief nur async im Hintergrund — die ERSTE Nachricht an
einem Ort (v.a. direkt nach Bridge-Neustart, Cache leer) wurde gebaut BEVOR
der Name fertig war → Präfix ohne Ort → local suchte Wetter mit rohen
Koordinaten und fand nichts.

Neu: _ensure_place_name() awaitet den (laufenden oder frisch gestarteten)
Geocode kurz mit Timeout, bevor der Standort-Präfix gebaut wird. Gecacht →
nur die erste Nachricht an einem neuen Ort zahlt ~0,5s, danach instant.
Timeout/Fehler → Fallback auf reine Koordinaten (kein Hänger).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:18:42 +02:00
duffyduckandClaude Opus 4.8 44982a9b3c feat(bridge): exakte Peilung (Grad) zusätzlich zur Himmelsrichtung im Präfix
Neben "nordwestwaerts" jetzt auch die genaue Bearing-Gradzahl (0-359) —
"unterwegs nordwestwaerts (304 Grad)". Die KI nutzt, was die Frage braucht
(Pannenhilfe → Ort/km, Luftfahrt/Navigation → exakte Peilung).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:12:38 +02:00
duffyduckandClaude Opus 4.8 ddb1bfac6a feat(bridge): Fahrtrichtung (Himmelsrichtung) im Standort-Präfix
Aus dem permanenten GPS wird die Bewegungsrichtung berechnet: Bearing zwischen
einem Anker-Punkt und der aktuellen Position, gemappt auf 8-Wind-Adverb
(nordwestwaerts …). Der Anker rueckt erst bei echter Bewegung (>25 m) nach,
sonst zaehlt GPS-Jitter im Stand nicht — Stehenbleiben behaelt die letzte
Richtung, Umdrehen liefert automatisch die neue.

Praefix jetzt z.B.:
  [Stefans aktueller Standort: A 28 bei km 55,6, Oldenburg, Niedersachsen
   (GPS 53.12, 8.22), unterwegs nordwestwaerts. ...]

Reiner Bridge-Fix (Haversine + Bearing lokal, kein Dienst noetig).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:11:07 +02:00
duffyduckandClaude Opus 4.8 4605698b02 feat(bridge): Standort-Präfix mit Straße+Hausnr, Straßen-Ref + km (Autobahn)
Erweitert den Reverse-Geocode: Nominatim zoom=18 → Straße + Hausnummer, PLZ,
Stadt, Bundesland ("Am Wunderburgpark 6, 26135 Oldenburg, Niedersachsen").
Bei Autobahn/Bundes-/Kreisstraße wird die Ref (A 28 / B 75 / K …, aus
extratags) vorangestellt + best-effort Kilometer via Overpass-Milestone
(highway=milestone, distance-Tag, naechster im Umkreis) → z.B.
"A 28 bei km 55,6, Oldenburg, Niedersachsen". Für Pannenhilfe & Co.

Cache-Raster von ~1km auf ~110m verfeinert (Straßen-Genauigkeit). Overpass
nur wenn's nach Straße/Autobahn aussieht (ref/Autobahn im Namen). Alles
best-effort mit stiller Fallback-Kette (Fehler → gröberer Ort → nur GPS).
NOMINATIM_URL + OVERPASS_URL env → self-hostbar.

Richtung ("Richtung Leer") bewusst weggelassen — nicht zuverlaessig aus
einem Punkt ableitbar (braucht Fahrtrichtung/Routing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:06:15 +02:00
duffyduckandClaude Opus 4.8 1c11cb6a2f feat(bridge): GPS→Ortsname im Standort-Präfix (Reverse-Geocode, keine Tokens)
Das lokale 8B kann Koordinaten nicht zuverlässig in eine Stadt uebersetzen
(riet "Berlin" fuer Oldenburg). Claude kann's, aber das kostet Tokens.
Loesung: die Bridge loest lat/lon EINMAL via Nominatim (OpenStreetMap, gratis,
keyless) auf und schreibt den Namen in den Praefix:
  [Stefans aktueller Standort: Oldenburg, Niedersachsen (GPS 53.12, 8.22). ...]
So muss KEIN Modell mehr raten — local UND Claude lesen den Ort direkt ab,
Token-Ersparnis bleibt voll erhalten.

- _persist_location merkt sich den letzten Ort + triggert den Geocode async
  (im Hintergrund, gecacht pro ~1km-Raster, nur bei Bewegung → Nominatim-
  Rate-Limit locker eingehalten).
- _build_core_text nutzt frische ODER letzte bekannte Position (Praefix faellt
  beim passiven Weiterreden ohne frisches GPS nicht mehr weg) + den Ortsnamen.
- NOMINATIM_URL env (Default oeffentlicher Dienst) → spaeter self-hostbar.
Reiner Bridge-Fix, kein APK/Brain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:02:47 +02:00
duffyduck 6cf75644d8 release: bump version to 0.2.1.1 v0.2.1.1 2026-07-11 21:46:56 +02:00
duffyduckandClaude Opus 4.8 2c1de6705b feat(skills): speak-Flag gilt jetzt auch im Claude-Pfad
Vorher gab Claude immer speak=true zurück — führte Claude einen speak=false-
Steuerbefehl aus (z.B. "spiele auf duffy desktop weiter"), wurde die Antwort
trotzdem vorgelesen + 30s. Jetzt konsistent zu Fast-Path/local: führt Claude
einen run_*-Skill mit speak=false erfolgreich aus → speak=false (kein TTS,
App STOP). Der Text landet aber normal in der Bubble (Stefans Wunsch: Text ok,
nur nicht vorlesen). Antwort-Skills (speak=true) + reine Konversation bleiben
gesprochen; bei Skill-Fehler bleibt's gesprochen (Erklaerung soll man hoeren).

Reiner Brain-Fix — App liest speak bereits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:45:27 +02:00
duffyduckandClaude Opus 4.8 2716bc62ff feat(skills): Skill entscheidet selbst ob vorgelesen wird (manifest.speak)
Verallgemeinert das "run_* → stumm"-Heuristik: jeder Skill deklariert im
Manifest ein speak-Flag.
- speak=false (Default) = Steuerbefehl (Spotify, Licht) → kein TTS, App
  beendet direkt (STOP), wie bisher.
- speak=true = Antwort-Skill (Info/Ergebnis) → Antwort wird vorgelesen +
  Gespraechs-Fenster bleibt offen.

Gilt für BEIDE Pfade: Fast-Path (_fast_path_speak aus skill.speak) und
lokale Skill-Ausführung (_local_turn_speak = _skill_speak_flag(run_*)).
Info-Tools (web_search/memory_search/trigger_timer) bleiben gesprochen.

- skills.py: speak in create_skill + update_skill-allowed.
- agent.py: skill_create/skill_update Tool-Schema dokumentiert speak (damit
  ARIA es beim Skill-Bau setzen kann), Dispatch reicht es durch.
- App: _isSilent nur noch speak===false (kein answeredBy=fast-path-Fallback
  mehr, sonst waere ein speak=true-Fast-Path faelschlich stumm).

Bestehende Skills ohne Feld = false = stumm (kein Verhaltenswechsel).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:39:55 +02:00
duffyduckandClaude Opus 4.8 a9e1a46a2f feat(local-llm): Skill-Ausführung via local = kein TTS (speak=False)
Wenn das lokale LLM einen echten Skill (run_*, z.B. Spotify) ausführt, ist es
ein Steuerbefehl — genau wie Fast-Path. Jetzt speak=False: kein Vorlesen, und
die App beendet direkt (STOP) statt ins 30s-Gespräch zu gehen. Deckt den Fall
ab, wo Whisper sich verhört ("Nächster Slick") und local statt Fast-Path
den Skill auffängt.

Info-Tools (web_search/memory_search/trigger_timer) zählen NICHT — deren
Antwort/Bestätigung bleibt gesprochen. Reiner Brain-Fix: die App liest speak
bereits (2b48e5c), kein neuer APK nötig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:33:17 +02:00
duffyduck 245dfc4d73 release: bump version to 0.2.1.0 v0.2.1.0 2026-07-11 21:10:02 +02:00
duffyduckandClaude Opus 4.8 6a94b574f2 fix(app): Mund-Button stoppt laufendes Vorlesen wirklich (Cache bleibt)
Zwei Fehler: (1) War der PCM-Stream schon komplett empfangen (isFinal durch),
sind pcmStreamActive+isPlaying false — der AudioTrack spielt aber seinen
Buffer noch sekundenlang aus. stopPlayback() returnte dann früh ("nichts
aktiv") und rief PcmStreamPlayer.stop() NIE → Mund-Button wirkungslos.
(2) stopPlayback() wirft pcmBuffer weg → die Cache-WAV waere unvollstaendig,
Nachhoeren via Lautsprecher-Symbol kaputt.

Neue _silenceAudibleOutput(): stoppt den AudioTrack IMMER (auch im Drain-Fall),
laesst aber pcmBuffer/pcmMessageId/pcmStreamActive stehen — restliche Chunks
cachen stumm weiter, isFinal schreibt die vollstaendige WAV. setMuted(true)
nutzt das statt stopPlayback(). stopPlayback bleibt fuer Barge-In/Cancel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:07:41 +02:00
duffyduckandClaude Opus 4.8 a5e2256a44 feat(app): Wake-Word-Empfindlichkeit + Weiterreden-Fenster in Settings
Fehlauslösung im Auto: Spotify läuft über die Lautsprecher, das Mikro hört
mit, openWakeWord halluziniert "computer" rein (Log: wake.detect state=armed
→ leerer Transkript). Der Echo-Canceler kann nur ARIAs eigenes TTS
rausrechnen, nicht Spotify (fremde App, kein Referenzsignal).

- Wake-Word-Threshold jetzt konfigurierbar (loadWakeThreshold), Default von
  0.5 auf 0.6 hoch (strenger → weniger Fehlauslösung). Slider im Wake-Word-
  Settings-Bereich (0.30–0.90, greift beim "Speichern + Aktivieren").
- Weiterreden-Fenster (passives Lauschen) als Slider, Default 30s (10–60s).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:01:48 +02:00
duffyduckandClaude Opus 4.8 9fb29aa517 fix(voice): klarer Befehl = STOP, Gespraech = 30s passiv (kein zweiter Gong)
Nach dem Re-Arm-Fix rief die App resume() → das spielte einen zweiten Gong
und oeffnete ein neues Aufnahme-Fenster, obwohl Stefan nur einen Steuerbefehl
gab. Sein gewuenschtes Verhalten:
- Klarer Befehl (Fast-Path, speak=false, z.B. Liedersteuerung) = KEINE
  Konversation → STOP: direkt zurueck aufs Wake-Word (kein Gong, keine
  Aufnahme, kein 30s-Fenster).
- Gespraech (gesprochene Antwort) = kein neuer Gong, direkt 30s passives
  Lauschen (weiterreden wie mit einem Menschen), 30s still → Wake-Word.

endConversation(skipPassive) neu: true springt direkt zu armed statt in
passives Lauschen. onPlaybackFinished (Gespraech) → passiv (Vordergrund) bzw.
direkt armed (Hintergrund); stille Fast-Path-Antwort → skipPassive; manueller
Stop-Button → skipPassive. resume() bleibt nur noch Mic-Fail-Retry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:52:04 +02:00
duffyduck a0494e90ee release: bump version to 0.2.0.9 v0.2.0.9 2026-07-11 20:42:25 +02:00
duffyduckandClaude Opus 4.8 2b48e5cac6 fix(voice): Ohr re-armt nach Fast-Path-Befehl (kein TTS → kein Re-Arm-Trigger)
Fast-Path-Antworten sind speak=False → kein TTS → onPlaybackFinished feuert
nie. Das Wake-Word-Re-Arm haengt aber genau daran → nach "nächster Titel"
blieb das Ohr grau in 'conversing' stecken (Stefan im Auto, 2 Min gewartet,
kein Re-Arm). Log bestaetigt: nach stream.final kam kein wake.end/wake.start.

- Bridge: chat-Payload traegt jetzt 'speak' (war nur answeredBy).
- App: bei stiller Antwort (speak=false ODER answeredBy=fast-path) und
  laufender Konversation dieselbe Re-Arm-Logik wie bei TTS-Ende anstossen
  (aktiv → resume/Konversationsfenster, sonst → endConversation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:40:57 +02:00
duffyduck aefdff89dc release: bump version to 0.2.0.8 v0.2.0.8 2026-07-11 20:33:12 +02:00
duffyduckandClaude Opus 4.8 f99e90f524 fix(voice): Doppel-Ausfuehrung — identischen STT-Endpoint-Text <5s deduppen
Im Auto cancelt der App-seitige No-Speech-Watchdog den conversing-Stream
vorzeitig (Whisper-Partials kommen bei Fahrgeraeusch verspaetet) und startet
passives Lauschen, waehrend die Bridge dieselbe Aeusserung parallel noch
finalisiert. Ergebnis: derselbe Befehl ("nächstes lied") wird zweimal
ausgefuehrt — aus zwei verschiedenen audioRequestIds, darum griff die
bestehende ID-Idempotenz nicht.

Text-Fenster-Dedup: identischer normalisierter Endpoint-Text innerhalb 5s
wird verworfen. Serverseitig, kein APK noetig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:30:37 +02:00
duffyduckandClaude Opus 4.8 1dd47888a8 fix(app): STT-Endpoint großzügiger + Mikro vor Re-Arm freigeben
#2 Vorzeitiges Absenden: endpointMs war hart 1500ms — im Auto (Sprechpausen)
schnitt das mitten im Satz ab (die 11.8s-Frage wurde bei "…ohne dass ein"
gekappt). Jetzt konfigurierbar (aria_stt_endpoint_ms, Default 2400, 1000-4000).

#3 Ohr bleibt ausgegraut: beim Re-Arm rief der Wake-Word-Service
OpenWakeWord.start(), waehrend die passive Streaming-Aufnahme noch das Mikro
hielt → start() schlug fehl → state=off. Neuer micReleaseHook cancelt die
Aufnahme VOR start() (endConversation / exitPassiveListening /
discardIfFreshlyTriggered). ChatScreen registriert den Hook.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:29:04 +02:00
duffyduckandClaude Opus 4.8 8b22557ca5 fix(fast-path): Voice-Befehle matchen wieder (GPS-Praefix, Umlaute, Kommas)
Voice-Nachrichten tragen den GPS-Hint-Praefix der Bridge
("[Stefans aktuelle GPS-Position: ...] nächstes lied bitte") und echte
Umlaute + Whisper-Kommas. Die ^...$-verankerten fast_patterns matchten damit
NIE — jeder Voice-Spotify-Befehl ging unnoetig an local/Claude (Tokens +
Latenz + TTS statt 0-Token-Fast-Path, was das Doppel-Ausfuehren mit anheizte).

_normalize_for_fast_match: fuehrende [ ... ]-Hint-Bloecke strippen, Umlaute
falten (ä→ae …), interne Satzzeichen (Komma/Punkt) raus. Pattern wird gleich
gefaltet. Router strippt den Praefix ebenfalls (Laengen-/Hint-Heuristik).

Wirkung: "nächstes lied bitte", "play", "nächster titel" -> Fast-Path
(instant, speak=False). "spiele auf duffy desktop weiter" bleibt Router-Sache.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:25:26 +02:00
duffyduckandClaude Opus 4.8 bd78f384a0 fix(diagnostic): Verstecken im Kontext-Streifen (Main-Tab) statt nur Settings
Das Auge/Toggle war faelschlich nur in der vertikalen Projektliste unter
Einstellungen — Stefan managed Projekte aber ueber die Ordner-Bubbles im
Kontext-Streifen auf dem Main-Tab, und der filterte hidden gar nicht
(verstecktes Projekt blieb sichtbar, kein Auge).

renderContextStrip: versteckte Projekte standardmaessig raus, 🙈/👁-Auge pro
Bubble (eigenes onclick + stopPropagation, wechselt nicht den Kontext),
"versteckte anzeigen (N)"-Toggle-Chip am Ende; setStripProjectHidden patcht
+ raeumt Focus wenn noetig. project_changed refresht jetzt Streifen UND Liste.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 17:18:07 +02:00
duffyduckandClaude Opus 4.8 14bdd4dbf4 fix(diagnostic): no-store fuer index.html — Browser servierte alte Version
Die /-Response hatte keinen Cache-Control-Header. Der Browser cachte das
grosse Single-HTML-Dashboard heuristisch und servierte trotz (soft) Reload
die alte Inline-JS/CSS — neue Features (Projekte-verstecken-Button, Auge,
Token-Ersparnis-Card) tauchten erst nach Hard-Reload auf. Jetzt no-store +
no-cache, so dass jeder Load die frische Version bekommt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 17:15:34 +02:00
duffyduck bbfa1f73f6 release: bump version to 0.2.0.7 v0.2.0.7 2026-07-11 17:14:32 +02:00
duffyduckandClaude Opus 4.8 fed3cb9f18 fix(projects): Live-Sync verstecken zwischen App und Diagnostic (ohne Refresh)
Kern-Bug: der Diagnostic-Server reichte ein von RVS empfangenes
project_changed NIE an die Browser weiter — der Handler in index.html war
toter Code, der Diagnostic aktualisierte die Projektliste also nie live.

- server.js: RVS-Handler forwardet project_changed jetzt an die Browser-Tabs;
  der /api/brain-Proxy broadcastet project_changed (RVS + lokal) nach jeder
  erfolgreichen Projekt-Mutation (create/switch/end/archive/PATCH inkl. hidden).
- App ProjectsBrowser: sendet project_changed nach dem Verstecken/Sichtbar-
  machen und laedt bei eingehendem project_changed selbst neu.

Damit: verstecken in der App -> sofort im Diagnostic weg (und umgekehrt),
ohne Seiten-Refresh. Diagnostic-Hide funktionierte schon (Daten/Proxy/Logik
korrekt) — versteckte sind per "Versteckte anzeigen (N)"-Toggle sichtbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 17:11:51 +02:00
duffyduckandClaude Opus 4.8 9ec9119bd9 docs(changelog): 0.2.0.6 abgebunden (Unreleased → [0.2.0.6])
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 16:51:10 +02:00