Klappbares Panel unter dem Suchbalken — Stefan kann bis zu 3 Begriffe
eingeben und mit AND/OR verknuepfen, links nach rechts ausgewertet.
Backend bleibt simpel: pro Begriff einmal /memory/search-text aufgerufen,
die Treffer-Set-IDs werden client-seitig per AND (intersect) oder OR
(union) kombiniert.
UI:
- "⌃ Erweitert" Button rechts neben ✕ klappt das Panel auf
- 3 Eingabefelder mit 2 Operator-Dropdowns dazwischen (UND/ODER)
- "Suchen"-Button im Panel
- "Felder leeren" reseted
- Leere Felder werden ignoriert — sind nur 2 belegt, gibt's nur 1 Operator
- Typ-Filter aus dem Hauptbalken wird mit angewandt
- Info-Banner zeigt die kombinierte Suchformel zurueck
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stefan wollte ne richtige Suche statt nur "klingt aehnlich". Beide
Modi sind jetzt verfuegbar, Default ist Volltext:
- 📝 Wortlich (Substring, case-insensitive ueber Title + Content +
Category + Tags) — neuer Endpoint /memory/search-text. Full-Scan
via Qdrant scroll, k=50. Findet "cessna" exakt im Content. Bei
kleiner DB (<1000 Eintraege) unkritisch performant.
- 🧠 Semantisch (Embedder + score_threshold 0.30) — bestehender
/memory/search Endpoint. Findet konzeptuell verwandte Eintraege.
Diagnostic UI: Dropdown neben dem Suchfeld zum Modus-Wechsel.
Info-Banner zeigt klar welcher Modus aktiv ist.
Warum Wortlich Default: bei kleiner DB liefert Semantic gern False
Positives mit Score 0.30-0.45 fuer komplett unverwandte Begriffe
(z.B. "cessna" matched "Tageslog fuehren" mit 0.43). Wortlich ist
deterministisch und vermeidet das Rauschen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stefan kann jetzt einzelne Chat-Bubbles loeschen (mit Rueckfrage).
Die Bubble verschwindet aus chat_backup.jsonl (Bridge), Brain-
Conversation (rolling window + jsonl) und allen Clients (App +
Diagnostic). Genauso wichtig fuer ARIA: der gloeschte Turn ist im
naechsten Chat-Prompt nicht mehr im Window.
Pipeline:
UI 🗑 + confirm
→ RVS delete_message_request {ts}
→ Bridge._delete_chat_message:
- chat_backup.jsonl Zeile mit ts entfernen (atomar via tmp+rename)
- Brain POST /conversation/delete-turn (role+content match)
- RVS broadcast chat_message_deleted {ts}
→ App + Diagnostic entfernen Bubble lokal per ts-Match
Backend-Aenderungen:
- aria-brain/conversation.py: remove_by_match(role, content, ts_hint)
+ _rewrite_file (atomar). Match nahester Turn bei mehrfach gleichem
content.
- aria-brain/main.py: POST /conversation/delete-turn (POST statt DELETE
weil FastAPI keine Bodys auf DELETE erlaubt)
- bridge/aria_bridge.py: HTTP-Listener /internal/delete-chat-message
+ RVS-Handler delete_message_request. _append_chat_backup gibt jetzt
ts zurueck, _process_core_response packt backupTs ins chat-Event.
- rvs/server.js: ALLOWED_TYPES um delete_message_request +
chat_message_deleted erweitert.
- diagnostic/server.js: delete_chat_message-Action + chat_message_deleted
Relay zum Browser.
Frontend-Aenderungen:
- diagnostic/index.html: 🗑 erscheint on-hover in Bubbles mit data-ts,
confirm()-Dialog, addChat + chat_history setzen data-ts. WS-Listener
fuer chat_message_deleted entfernt Bubble per data-ts.
- android/ChatScreen.tsx: backupTs in ChatMessage, Muelltonne-Button
unten rechts in jeder Bubble, Alert-confirm, RVS-Listener fuer
chat_message_deleted entfernt aus messages-State.
Live-User-Bubbles (sofort gerendert vom eigenen Send) haben noch
keinen backupTs bis der Bridge-Roundtrip durch ist — die Muelltonne
erscheint dort erst nach kurzer Verzoegerung / Reload. Folgekommit
kann das polieren wenn noetig.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: bei kleiner DB (31 Eintraege) lieferte die Suche fuer JEDES Wort
fast alles als Treffer zurueck — k=20 Top-N ohne Threshold sorgte
dafuer dass auch "banane" zehn vermeintliche Treffer mit Scores
0.09-0.22 (= Rauschen) zurueckgab.
Fix:
- vector_store.search() bekommt optional score_threshold (an Qdrant
durchgereicht, das nimmt's nativ)
- /memory/search endpoint hat score_threshold-Query-Param (default 0.30)
- Diagnostic schickt k=10 + score_threshold=0.30 statt k=20 ohne Threshold
- "Keine Treffer"-Info-Box wenn alle Treffer < Threshold
MiniLM-multilingual liefert typischerweise:
>0.50 → starker Treffer
0.30-0.50 → relevant
0.20-0.30 → grenzwertig
<0.20 → Rauschen
Mit score_threshold=0 (oder None) bleibt die alte Top-N-Semantik
fuer Aufrufer die Rauschen explizit wollen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stefan wirft MDs rein wenn er was migrieren will, klickt im
Diagnostic-Gehirn-Tab auf "Migration aus brain-import/", fertig.
Was nicht migriert ist, liegt halt rum — gehoert aber nicht ins Repo
(private Daten, ephemerer Kram).
.gitignore-Pattern:
aria-data/brain-import/*
!.gitkeep
!README.md
Alte spezifische USER.md-Zeile durch das catch-all ersetzt — wir
mussten USER.md.example und Co. eh nicht mehr im Repo halten.
README in dem Verzeichnis entsprechend angepasst (Drop-Folder, nicht
"leerer Restposten").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AGENT.md/BOOTSTRAP.md enthielten Duplikate, OpenClaw-Referenzen und
fast-Memory-Hinweise auf das alte file-basierte System. Nach dem
DB-Cleanup (60 → 31 Eintraege) sind die alten MDs nicht mehr nuetzlich
— Stefan kuratiert direkt im Diagnostic-Gehirn-Tab, Backup laeuft via
Bootstrap-Snapshot (JSON) oder Komplett-tar.gz.
TOOLING.md.example + USER.md.example mit raus (auch obsolet).
.gitkeep haelt das Verzeichnis im Repo, README dokumentiert wofuer
es mal war und wann man es wieder braucht (Disaster-Recovery ohne
Snapshot, neues ARIA von Null).
Migration-Code (aria-brain/migration.py) bleibt — falls jemand mal
frische MDs reinpackt um sie zu parsen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Damit kann die Dev-Maschine (wo Claude Code laeuft) die aria-wohnung-VM
ueber Diagnostic Port 3001 erreichen, ohne die interne IP im Git zu
haben. Pro Maschine wird .claude/aria-vm.env aus dem .example kopiert
und mit der lokalen Routing-Info gefuellt.
Nutzung:
source .claude/aria-vm.env
curl -s "$ARIA_BRAIN_URL/memory/stats"
Im docker-compose-Netz aria-net leben die Hostnamen (aria-brain etc.)
weiterhin direkt — das brauchst nur Hosts AUSSERHALB der VM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Beim ersten Aufruf (kein localStorage-Eintrag) sind alle Type-Sections
collapsed. Stefan klappt gezielt auf was er sehen will, statt eine
Wand of Text zu sehen. Sobald er einmal getoggelt hat, ueberschreibt
sein persistiertes State den Default — also nicht aufdringlich.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neuer Button "📄 Drucken / PDF" im Gehirn-Tab oeffnet eine sauber
formatierte Print-View in neuem Tab. Druck-CSS optimiert (page-break-
inside:avoid pro Entry, schwarze Borders fuer Print, Action-Bar wird
versteckt). Aktueller Type+Pinned-Filter wird respektiert.
Browser-eigenes "Als PDF speichern" greift dann — kein Tool noetig.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UX im Memory-Browser geschaerft, Stefan-Wunsch:
1. Klappbare Type-Gruppen:
Jeder Type-Header (Identität, Regeln, ...) hat jetzt einen ▼/▶
Indikator und reagiert auf Click. Eingeklappte Sektionen werden
in localStorage gemerkt — bleiben ueber Reloads stabil.
2. Category-AutoSuggest:
Das Kategorie-Feld im Neu/Edit-Modal hat jetzt ein <datalist>
mit allen schon in der DB existierenden Categories als Vorschlag.
Neue Categories sind weiterhin frei eintippbar. Liste wird bei
jedem renderBrainList-Aufruf aus dem Cache aktualisiert.
3. Info-Button (ℹ) neben dem Typ-Dropdown:
Erklaert welche Types FEST im System-Prompt eine eigene Sektion
bekommen (identity/rule/preference/tool/skill — Hot Memory)
und welche nur via semantischer Cold-Search reinkommen (fact/
conversation/reminder). Konsistent mit prompts.py:TYPE_HEADINGS.
Auch dokumentiert dass Category ein freier Tag ist und den
Prompt nicht direkt beeinflusst.
Type-Dropdown-Labels selbst zeigen jetzt (FEST) / (Cold) als Hinweis.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: Wenn der Brain-Background-Loop einen Timer/Watcher feuert, ruft
er agent.chat() direkt im eigenen Prozess. Die Antwort wurde nur ins
Trigger-Log geschrieben — kein RVS-Broadcast, kein TTS, nichts in
App/Diagnostic sichtbar.
Fix: Bridge ↔ Brain bekommen einen internen HTTP-Push-Kanal.
Bridge (Port 8090, nicht exposed, nur aria-net intern):
asyncio.start_server-basierter HTTP-Listener.
POST /internal/trigger-fired
body: {reply, trigger_name, type, events}
→ _handle_trigger_fired feuert Side-Channel-Events
(trigger_created/skill_created/location_tracking) erst,
dann _process_core_response(reply) — exakt der gleiche Pfad
wie normale Chat-Antworten (Chat-Bubble + TTS + chat_backup).
Brain background.py:
Nach agent.chat() in _fire wird agent.pop_events() ausgelesen
und zusammen mit dem Reply via urllib an aria-bridge:8090
gepostet (run_in_executor damit es den asyncio-Loop nicht
blockiert). Failures werden geloggt, der Trigger selbst bleibt
trotzdem als 'fired' markiert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Der claude-max-api-proxy ignoriert das OpenAI-tools-Feld komplett:
openai-to-cli.js wandelt nur messages in einen String, manager.js
spawnt 'claude --print' ohne Tools. Claude Code nutzt dann ihre
internen Tools (Bash, etc.) — bei 'Timer in 2min' macht sie ein
'sleep 120' intern und meldet 'erledigt' ohne dass wir je einen
trigger_timer-Call sehen.
Fix: zwei eigene Adapter-Files unter proxy-patches/ die zur
Container-Startzeit ueber die npm-Version kopiert werden:
openai-to-cli.js:
- tools-Feld wird als <system>-Block mit Tool-Schemas + klarer
Anweisung "Antworte <tool_call name=...>{json}</tool_call>"
in den Prompt injiziert
- role=tool messages werden als <tool_result>-Blocks eingewoben
→ Claude sieht den ganzen Tool-Use-Loop
- assistant tool_calls werden als <tool_call>-Bloecke
re-serialisiert, damit History-Roundtrips funktionieren
- Multimodal-content (Array von text-Parts) unveraendert
unterstuetzt (Original-sed-Patch eingebaut)
cli-to-openai.js:
- parsed <tool_call name="X">{json}</tool_call> aus result.result
- liefert OpenAI-konforme tool_calls + finish_reason=tool_calls
- Pre-Tool-Text bleibt im content erhalten
- normalizeModelName null-safe (Original-sed-Patch eingebaut)
docker-compose.yml: zwei sed-Patches die jetzt in den Files leben
sind raus, dafuer ein /proxy-patches:ro-Mount + zwei cp-Kommandos.
Smoke-Tests mit Node lokal alle gruen (single + multi tool_calls,
mit/ohne Pre-Text, History-Replay mit tool_result).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diagnose-Log um Trigger-Hang zu klaeren: warum legt ARIA keinen Timer
an, obwohl trigger_timer als Tool definiert ist? Wir loggen jetzt nach
jedem Proxy-Call:
- finish_reason
- alle Keys aus der message
- tool_calls-Anzahl + content-Laenge
- die rohe message (truncated 1500 chars)
So sehen wir ob der Proxy tool_calls leer liefert (Proxy schluckt
tools-Feld?), ob Claude ignoriert (Anthropic-Native-Format statt
OpenAI?), oder ob unser Dispatch falsch parsed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ARIA wusste nicht wieviel Uhr es ist (kein Bash, kein Time-Tool, kein
Timestamp im System-Prompt) und konnte fires_at als ISO-UTC schlicht
nicht ausrechnen. Zwei Fixes:
1. prompts.py: build_time_section() injiziert UTC + lokale Zeit
(Europa/Berlin Sommer/Winter-Heuristik) als '## Aktuelle Zeit'-Block
oben in den System-Prompt. Hilft auch beim Einordnen von
Watcher-Conditions wie hour_of_day == 8.
2. agent.py trigger_timer-Tool: neuer Parameter `in_seconds` als
Alternative zu fires_at. Bei relativen Angaben ('in 2 Minuten')
rechnet jetzt der Server den absoluten Timestamp aus — keine
Rechnerei in der LLM noetig. fires_at bleibt fuer feste Termine.
required nur noch name + message.
Diagnostic-API (/triggers/timer) bleibt absolute-only, da der Browser
selbst datetime hat.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ARIA kann jetzt GPS-Watcher mit near() effektiv nutzen: die App liefert
kontinuierliche Position, Brain wertet sie in den Background-Triggers aus.
rvs/server.js
ALLOWED_TYPES: location_update (App→Bridge) + location_tracking (Brain→App).
bridge/aria_bridge.py
location_update Handler: persistiert {lat, lon} via _persist_location in
/shared/state/location.json — selber Pfad wie chat/audio-events, aber als
eigenes Event ohne Chat-Overhead.
aria-brain/agent.py
Neues Meta-Tool request_location_tracking(on, reason). Dispatcher fuegt
{type: "location_tracking", on, reason} zu _pending_events hinzu →
Bridge forwarded als RVS-Message zur App.
aria-brain/prompts.py
Trigger-Section bekam neuen Block "GPS-Watcher mit near()": ARIA wird
angewiesen request_location_tracking(on=true) zu rufen wenn sie einen
near()-Watcher anlegt, und wieder false beim Loeschen des letzten.
android/src/services/gpsTracking.ts (NEU)
Singleton-Service. start(reason) → Geolocation.watchPosition mit
distanceFilter 30m + interval 15s, sendet location_update an RVS.
stop(reason) → clearWatch. Persistiert Status in 'aria_gps_tracking',
restoreFromStorage() beim Settings-Mount. Permission-Request fuer
ACCESS_FINE_LOCATION + Toast-Benachrichtigung bei An/Aus.
android/src/screens/SettingsScreen.tsx
Neuer Switch im "Standort"-Block: "GPS-Tracking (kontinuierlich)" mit
Hinweis-Text. Subscribe auf gpsTrackingService.onChange damit Toggle
reflektiert wenn ARIA das per Tool umschaltet.
RVS-Handler: location_tracking → gpsTrackingService.start/stop mit
Reason aus Brain-Tool.
Ablauf Stefan→ARIA→Blitzer:
1. Stefan: "Warn mich vor Blitzern auf Route nach Rhauderfehn"
2. ARIA: skill_create("blitzer-warner") falls noch nicht da
3. ARIA: run_blitzer-warner → Liste {lat,lon,name}
4. ARIA: pro Eintrag trigger_watcher mit near(lat,lon,500)
5. ARIA: request_location_tracking(on=true, reason="Blitzer-Warner aktiv")
6. App: GPS-Tracking startet, sendet alle 15s location_update
7. Bridge: /shared/state/location.json wird aktuell gehalten
8. Brain-Background-Loop: alle 30s near()-Check pro Trigger
9. Bei Erfolg: ARIA spricht "Blitzer A31 km 12 in 500m"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ARIA kann jetzt GPS-basierte Watcher-Trigger anlegen (Blitzer-Warner-Use-Case),
plus erweiterte Time-, System- und Activity-Variablen.
bridge/aria_bridge.py
_persist_state() schreibt atomar nach /shared/state/<key>.json.
Bei jedem chat- und audio-Event:
- location → /shared/state/location.json {lat, lon, ts_unix}
- last_user_ts → /shared/state/activity.json
Brain-Watcher lesen das fuer die GPS- und Activity-Variablen.
aria-brain/watcher.py — komplett ueberarbeitet
Neue Variablen-Sets:
GPS: current_lat, current_lon, location_age_sec (-1 = nie gesehen)
Zeit (+): minute_of_hour, day_of_month, month, year, is_weekend, unix_timestamp
System: ram_free_mb (MemAvailable), cpu_load_1min (loadavg)
Activity: last_user_message_ago_sec
Memory: pinned_count (zusaetzlich zu memory_count)
Neue Funktion fuer Conditions:
near(lat, lon, radius_m) Haversine-Distanz von current_lat/lon
zum Punkt. False wenn keine Position bekannt.
Parser-Erweiterung:
ast.Call jetzt erlaubt, ABER nur fuer direkte Funktionsnamen aus der
Whitelist (_ALLOWED_FUNCTIONS = {"near"}). Keine Attribute-Access,
keine Keywords, Args nur Constants/Names/UnaryOp.
Selbsttest blockt korrekt:
__import__("os")... → "Funktionsaufruf nur ueber direkten Namen"
memory_count.__class__ → "Verbotener Ausdruck: Attribute"
(lambda: 1)() → "Funktionsaufruf nur ueber direkten Namen"
aria-brain/main.py
/triggers/conditions liefert jetzt zusaetzlich {functions:[...]} mit
Signaturen + Beschreibungen. current-Snapshot filtert callable() raus
damit JSON serialisierbar bleibt.
aria-brain/prompts.py + agent.py
build_triggers_section bekommt condition_funcs als 4tes Argument und
listet die im System-Prompt unter "Verfuegbare Funktionen". Operatoren-
Hinweis ergaenzt mit Beispielen + Regeln (keine Variablen in Funktions-
Args, keine Schachtelung).
diagnostic/index.html
Trigger-Create-Modal: Variablen-Info-Block zeigt jetzt sowohl Variablen
(mit aktuellen Werten) als auch Funktionen (Signatur + Beschreibung).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ARIA hatte bisher nur ein "User fragt → Brain antwortet"-Modell. Neu:
Trigger laufen passiv im Hintergrund (kein LLM-Call) und wecken ARIA
nur dann auf wenn ein Event tatsaechlich passiert.
Drei Typen, zwei aktuell implementiert:
timer — einmalig zu festem ISO-Timestamp ("erinner mich in 10min")
watcher — Polling alle N Sek einer Condition, feuert bei True mit Throttle
(z.B. "disk_free_gb < 5", max 1x/h)
cron — Platzhalter fuer spaeter
aria-brain/triggers.py
CRUD auf /data/triggers/<name>.json + /data/triggers/logs/<name>.jsonl.
create_timer, create_watcher, mark_fired, list_logs, etc.
aria-brain/watcher.py
Built-in Condition-Variablen: disk_free_gb, disk_free_pct, uptime_sec,
hour_of_day, day_of_week, rvs_connected, memory_count.
Sicherer Condition-Parser via ast — Whitelist auf Vergleich + BoolOp +
Name + Const. Kein eval, kein exec, keine Builtins.
aria-brain/background.py
Async Loop laeuft alle 30s, sammelt einmalig Variables, geht durch
Trigger-Liste, _should_fire-Check (Timer: fires_at vergangen / Watcher:
check_interval + throttle respektiert + condition true). Fire ruft
agent.chat(prompt, source="trigger") — ARIA bekommt das wie eine
Push-Nachricht und antwortet via Bridge → RVS → App.
aria-brain/main.py
/triggers/list, /{name}, /{name}/logs, /timer, /watcher, PATCH, DELETE,
/triggers/conditions (Variablen + aktuelle Werte). Lifespan-Handler
startet den Background-Loop beim Container-Start, stoppt beim Shutdown.
aria-brain/agent.py
Meta-Tools fuer ARIA: trigger_timer, trigger_watcher, trigger_cancel,
trigger_list. ARIA legt Trigger via Tool-Call selbst an wenn Stefan das
wuenscht. Side-Channel-Event 'trigger_created' wird in chat-Response
mitgeschickt damit App + Diagnostic eine Bubble zeigen.
aria-brain/prompts.py
Neue System-Prompt-Section: Liste aktiver Triggers + verfuegbare
Condition-Variablen mit aktuellen Werten + Operatoren-Erklaerung.
ARIA weiss damit immer was es schon gibt und welche Vars sie nutzen kann.
bridge/aria_bridge.py + rvs/server.js
trigger_created als neuer RVS-Message-Type, Bridge forwarded das aus
data.events analog zu skill_created.
diagnostic/index.html
Neuer Top-Tab "Trigger". Liste mit Type-Badges (⏱ TIMER / 👁 WATCHER),
Status, Fire-Count, last_fired. Aktivieren/Deaktivieren + Löschen pro
Trigger. "+ Neu"-Modal mit Type-Dropdown, Timer-Minuten oder
Watcher-Condition + Vars-Anzeige + Throttle. Info-Modal-Eintrag mit
Erklaerung. Live-Bubble im Chat wenn ARIA selbst einen anlegt.
android/src/screens/ChatScreen.tsx
trigger_created RVS-Handler → eigene Bubble (gelber Border, "⏰ ARIA
hat einen Trigger angelegt", Type/Detail/Message/Zeit). ChatMessage
bekam triggerCreated-Feld. Lokal-only-Schutz beim Server-Sync analog
zu skill_created.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptom: Diagnostic-Chat zeigt nur ARIA-Dateien (file_from_aria), Text-
Antworten kamen nicht an. STT-Eintraege + User-Messages waren sichtbar.
Ursache: Im rvs_chat-Handler stand
if (sender === 'aria') return;
Die alte Begruendung war "ARIA-Antworten kommen schon via Gateway (chat:final)".
Das galt zu OpenClaw-Zeit, wo Diagnostic eine direkte WS zum aria-core hatte.
Gateway ist seit dem Abriss weg, ARIA-Antworten kommen jetzt ausschliesslich
via RVS → der return blockte sie still.
Fix: chatType + label je nach sender:
- aria → received-Bubble, Label "ARIA"
- stt → sent-Bubble, Label "🎤 Spracheingabe" (wie vorher)
- sonst → sent-Bubble, Label "via RVS (<sender>)"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug 1: "ARIA denkt..." in der App bleibt stehen
_process_core_response setzte am Ende kein idle — die alten Aufrufe waren
in der OpenClaw-WS-Loop, in der Brain-HTTP-Variante fehlten sie. Plus
send_to_core schickte agent_activity direkt via _send_to_rvs ohne den
_last_activity_state-Cache zu pflegen → _emit_activity("idle") wurde
spaeter dedupliziert.
Fix:
- _emit_activity statt direktem _send_to_rvs fuer thinking
- _emit_activity("idle") am Ende von _process_core_response
- _last_chat_final_at bewusst NICHT setzen — die 3s-Cooldown war fuer
trailing OpenClaw-Events, wuerde bei Voice die naechste thinking-Welle
unterdruecken
Bug 2: App Chat-Suche scrollt nicht zur Stelle
scrollToIndex wurde zu fruh aufgerufen (Layout noch nicht fertig) und
viewPosition: 0.4 in inverted-FlatList war ungenau.
Fix:
- requestAnimationFrame um den Scroll-Aufruf
- viewPosition: 0.5 (mittig)
- onScrollToIndexFailed: erst grob scrollen via averageItemLength,
dann nach 250ms praeziser nachfassen
Bug 3: Voice-Bubble bekommt STT-Text erst mit ARIA-Antwort
_process_app_audio rief erst send_to_core (blockt synchron auf Brain,
kann 300s dauern), DANN STT-Broadcast. App sah den eigenen Text erst
wenn ARIA fertig war.
Fix: Reihenfolge getauscht — STT-Broadcast zuerst, dann send_to_core.
Voice-Bubble bekommt jetzt den erkannten Text sofort.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App-Chat-Sync ist seit Commit 3497aa2 "Server is Source of Truth" — bei
jedem Reconnect KOMPLETTER Server-Stand statt incremental. Doku angepasst:
- App leert sich wenn Server leer ist (z.B. nach "Konversation zuruecksetzen")
- Lokal-only Bubbles bleiben erhalten (Skill-Notifications, Voice ohne STT)
- Bridge schreibt chat_backup.jsonl pro Turn — als Server-Backing-Store
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptom: Diagnostic-Server hat leere Chat-History (z.B. nach "Konversation
zuruecksetzen" oder Wipe), App zeigt aber weiterhin ihren alten lokalen
Stand. Wer das Wipe-Event verpasst hat (App offline), bleibt veraltet.
Ursache: App schickte beim Reconnect chat_history_request {since: lastSync}
und ignorierte leere Antworten. Wenn der Server ueberhaupt nichts mehr hat
liefert er korrekt [] zurueck — App behielt aber lokalen State.
Fix:
- App schickt jetzt {since: 0, limit: 200} → KOMPLETTER Server-Stand
- Handler ersetzt die persistierte Chat-History mit dem Server-Stand
(statt zu mergen)
- Lokal-only Bubbles bleiben erhalten:
* Skill-Created-Notifications (skillCreated gesetzt)
* Laufende Sprachnachrichten ohne STT-Result (audioRequestId gesetzt
und text leer/Placeholder)
- Wenn Server leer: lastSync ebenfalls geloescht (sauberer Restart-State)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README.md
- Phase B erledigt-Liste um zwei Punkte erweitert:
Token/Call-Metrics + Multi-Select-Datei-Manager
- Diagnostic-Tabs in der Installations-Sektion aktualisiert
(5 Tabs statt 4 — Skills als eigener Tab)
- Diagnostic-Sektion weiter unten: Tabs-Liste komplett ueberholt,
Token-Metrics als eigener Bullet in "Was zusaetzlich drin steckt"
issue.md
- "Token/Call-Metrics" als erledigter Punkt in "Diagnostic/App-Features"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stefan hat den Max 5x Plan (~\$90-100/Monat), ungefaehres Limit 225 Calls pro
5h-Fenster fuer Sonnet. Damit nicht in eine Tool-Loop-Schleife laufen ohne
es zu merken: kleine Metrics-Pipeline, sichtbar in der Diagnostic.
aria-brain/metrics.py
Append-only JSONL Logger unter /data/metrics.jsonl. Pro Claude-Call eine
Zeile {ts, model, in, out} mit Token-Schaetzung (chars/4, Anthropic-
Heuristik). aggregate(window) zaehlt die letzten N Sekunden.
Auto-Rotate bei 50k Zeilen → 25k behalten (~70 KB/Monat bei 1k Calls/Tag,
Cap also weit oben).
aria-brain/proxy_client.py
chat_full() ruft am Ende metrics.log_call(model, messages_in, reply).
Failed/exception-Pfade loggen nicht (sonst false positives).
aria-brain/main.py
GET /metrics/calls → {h1, h5, h24, d30}, jedes Window mit calls,
tokens_in, tokens_out, by_model.
diagnostic/index.html
Neue Card "Token / Calls" im Gehirn-Tab. Plan-Dropdown
(Pro / Max 5x / Max 20x / Custom), localStorage-persistiert. 4 Metric-
Zellen fuer 1h/5h/24h/30d mit Calls + Tokens. Progress-Bar oben zeigt
5h-Counter gegen Plan-Limit. Warn-Klassen: gelb bei 80%, rot bei 90%.
Auto-Refresh alle 30s wenn Gehirn-Tab offen, plus bei Tab-Wechsel.
Info-Modal erklaert die Limits + dass HTTP-Call != User-Frage (Tool-Use
kann pro Frage bis zu 8 Calls verursachen).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App Chat-Suche umgebaut von Filter zu Highlight+Navigation
Vorher: searchQuery filtert die FlatList, zeigt nur Treffer.
Jetzt: Suche filtert NICHT mehr, alle Nachrichten bleiben sichtbar.
Treffer wird gelb (FFD60A) umrandet, FlatList scrollt automatisch
dorthin.
- Suchleiste: Input + Counter "N/M" + ▲ + ▼ + ✕
- ▲ / ▼ navigieren chronologisch durch alle Matches (zyklisch)
- searchMatchIds via useMemo, searchIndex separates State
- scrollToIndex mit viewPosition: 0.4 (Treffer landet im oberen Drittel)
- onScrollToIndexFailed Fallback nach 200ms (Layout noch nicht fertig)
Diagnostic Sprachausgabe-Layout
Export/Import-Buttons wandern aus dem Section-Header in den Details-Block
neben "Anwenden" (Stefan's Wunsch). Header zeigt nur noch den Titel.
File-Input bleibt versteckt im Section-Top, wird vom neuen Button-Block
unten ueber click() getriggert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diagnostic + App bekommen Mehrfach-Auswahl im Datei-Manager. Mehr als eine
Datei ausgewaehlt → Download als ZIP. Genau eine ausgewaehlt → einzeln.
Bulk-Delete loescht alle markierten in einem Rutsch.
diagnostic/Dockerfile
zip via apk add — fuer das ZIP-Streaming im /api/files-download-zip.
diagnostic/server.js
POST /api/files-download-zip Body: {paths:[...]} → spawnt 'zip -j -q -',
Pipes stdout in Response. Whitelist auf
/shared/uploads/.
POST /api/files-delete-batch Body: {paths:[...]} → loescht alle, broadcastet
file_deleted pro Pfad an Browser + RVS.
diagnostic/index.html
filesSelected Set + Checkbox-UI pro Datei + "Alle markieren". Wenn 2+
ausgewaehlt: POST an /api/files-download-zip, Browser saugt das als
Blob runter. Bei 1: normaler Single-Download.
bridge/aria_bridge.py
file_delete_batch_request → ruft Diagnostic /api/files-delete-batch,
antwortet mit file_delete_batch_response.
file_zip_request {paths,reqId} → ruft Diagnostic /api/files-download-zip,
base64-kodiert, capped auf 30 MB,
sendet file_zip_response.
rvs/server.js
ALLOWED_TYPES: file_delete_batch_request/response, file_zip_request/response.
android/src/screens/SettingsScreen.tsx
fileManagerSelected Set + Checkbox-UI pro Datei + "Alle markieren"-Zeile
oben. Bulk-Bar oben mit count, "⬇ ZIP" / "⬇ Download" (je nach Anzahl),
und "🗑 Löschen". ZIP-Response landet base64 → RNFS in Downloads-Folder
(aria-files-<timestamp>.zip), Toast mit Pfad.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Zwei zusammenhaengende Bugs:
1. App aktualisierte nicht wenn die Diagnostic "Konversation komplett
zuruecksetzen" gedrueckt hat — die App hatte den lokalen Stand weiter
2. Nachrichten die kamen waehrend die App offline/geschlossen war,
wurden nicht nachgeladen
Loesung: chat_backup.jsonl wird wieder geschrieben (Bridge statt Diagnostic,
weil OpenClaw-Code-Pfad tot ist) und dient als Server-Truth fuer App+Diagnostic.
bridge/aria_bridge.py
_append_chat_backup() schreibt jeden Turn (User + ARIA) als JSONL-Zeile
in /shared/config/chat_backup.jsonl. Trigger: send_to_core (User) +
_process_core_response (Assistant, inkl. file-Attachments).
_read_chat_backup_since(since_ms, limit) liest die Datei, filtert auf
ts > since_ms, gibt max limit neueste zurueck. Honoriert file_deleted-Marker.
Neuer RVS-Handler chat_history_request {since, limit?} → antwortet mit
chat_history_response {messages: [...], since}.
diagnostic/server.js
/api/chat-history-clear broadcastet jetzt zusaetzlich chat_cleared via
RVS (sendToRVS_raw), damit App ihre lokale Liste auch leert. Vorher nur
Browser-Clients via broadcast() — App war aussen vor.
rvs/server.js
ALLOWED_TYPES um chat_history_request, chat_history_response, chat_cleared.
android/src/screens/ChatScreen.tsx
- Bei (re)connect: AsyncStorage 'aria_chat_last_sync' lesen → send
chat_history_request {since}
- Handler chat_history_response: incoming → ChatMessage[] mappen,
Attachments aus 'files'-Array rekonstruieren, mergen (Dedup via timestamp),
lastSync hochziehen
- Handler chat_cleared: setMessages([]) + AsyncStorage 'chat_messages' +
'last_sync' weg
- Bei jeder eingehenden chat-Message: 'aria_chat_last_sync' updaten damit
Reconnect nicht doppelt nachzieht
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptom: Main-Tab "ARIA BRAIN"-Card blieb auf "Lade..." haengen. Klick auf
"Status pruefen" tat scheinbar nichts.
Ursache: loadBrainStatus() suchte nur brain-status (Gehirn-Tab). Die Card
im Main-Tab hat aber andere IDs (brain-dot + brain-status-short + brain-error),
die wurden nirgends mehr befuellt seit updateState() das nicht mehr macht.
Fix: loadBrainStatus update jetzt BEIDE Anzeigen synchron — kompakte Main-Card
mit Dot/Status/Error UND die ausfuehrliche Gehirn-Tab-Zeile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reusable Info-Modal-System: kleines (ℹ)-Button neben Ueberschriften, beim
Klick oeffnet ein Modal mit ausfuehrlicher Erklaerung. Fuer die 4 wichtigsten
Brain-Konzepte sind die Texte vor-definiert (INFO_TEXTS dict).
- Gehirn — Status online/offline, N Memories, Qdrant-Endpoint
- Konversation Rolling Window 50, Schwelle 60, Destillat-Logik,
Hinweis warum chat_backup ≠ conversation.jsonl
- Memories Hot vs. Cold, alle 8 Typen erklaert, semantische Suche
- Bootstrap Die drei Wege (Migration / Snapshot / Komplett-Gehirn)
Plus inline-ℹ-Button neben der "Konversation: N Turns"-Zeile in der
Status-Card, damit man dort wo's relevant ist sofort die Erklaerung findet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drei zusammenhaengende Fixes — alle aus der OpenClaw-Umstellung uebrig.
Brain-Card im Main-Tab
- updateState() schrieb "aria-core entfernt — Brain-Loop in Arbeit" in
die Card, weil state.gateway noch den alten OpenClaw-Status-Text
transportiert hat. Brain ist online, sah aber auf der Diagnostic
aus als waere alles tot.
- Fix: updateState laesst die Brain-Card jetzt in Ruhe. Daten kommen
exklusiv von loadBrainStatus() (fetch /api/brain/health).
- WS-onopen ruft jetzt loadBrainStatus(), plus 15s-Interval fuer
Live-Refresh.
Runtime-Konfiguration
- Hinweis-Text erwaehnte noch aria.env + "Bridge-Container neu starten".
aria.env gibt's nicht mehr, und Brain liest die Config auch.
- Neu: ".env / Bridge UND Brain lesen sie beim Start — nach Aenderung
den jeweiligen Container neu starten (Reparatur-Section oben)."
Konversation-Reset
- Vorher zwei verschiedene Konversations-Stores ohne klare UI:
chat_backup.jsonl (Diagnostic-Anzeige) und conversation.jsonl
(Brain Rolling Window). "Konversation leeren" leerte nur Brain,
die UI zeigte alle Turns weiter → verwirrend.
- Neu: ein Button "🧹 Konversation komplett zurücksetzen" leert
beides parallel (Promise.all auf /api/brain/conversation/reset
+ /api/chat-history-clear) plus die lokale Chat-View.
- Erklaerungstext darunter macht klar was passiert + was bleibt
(destillierte Facts + Memories in der Vector-DB).
- Neuer Endpoint /api/chat-history-clear loescht chat_backup.jsonl
und broadcastet leere chat_history an alle Browser-Clients.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Die Diagnostic-Seite lud nicht mehr richtig: bei jedem state-Update vom
Server crashte updateState() auf null.className weil 'gw-dot' nicht mehr
existiert (Gateway-Card wurde durch Brain-Card ersetzt). Mehrere weitere
Stellen waren ebenfalls auf nicht mehr existierende Elemente geleitet.
Bereinigt:
- updateState() nutzt jetzt brain-dot/short/error (null-safe)
- openCoreTerminal entfernt (aria-core ist raus)
- closeTermModal null-safe fuer btn-core-term
- 'core_auth' WS-Event entfernt
- 'session_restarted' WS-Event entfernt (perms-status-Element war auch raus)
- 'openclaw_config' WS-Event entfernt
- rc-compact-after read/write aus loadRuntimeConfig/saveRuntimeConfig raus
(Compact-After-Messages-Setting wurde mit aria-core entfernt)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
War kaputt nach OpenClaw-Abriss: handleGetModel/handleSetModel haben gegen
aria-core (dockerExec + node-script in den Container) gearbeitet, der gibt's
nicht mehr.
diagnostic/server.js
- handleGetModel/handleSetModel lesen/schreiben jetzt brainModel in
/shared/config/runtime.json
- RUNTIME_CONFIG_FIELDS um "brainModel" erweitert
- Tote Variante (findSettingsFile + base64-node-script) komplett raus
aria-brain/proxy_client.py
- Liest brainModel aus runtime.json beim Container-Start
- Fallback: BRAIN_MODEL env → "claude-sonnet-4" Default
- Bei Aenderung in Diagnostic: aria-brain restarten damit's greift
(Hinweis steht in der UI)
diagnostic/index.html
- Section "Model" → "Sprachmodell (Brain)"
- Hinweis-Block mit Default-Erklaerung und Restart-Hinweis
- Modelle: claude-sonnet-4 (default), claude-opus-4, claude-haiku-4-5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tools/export-jsonl-to-md.js
CLI-Konverter fuer OpenClaw-Session-JSONL zu Markdown. Mit OpenClaw raus.
tools/-Ordner ist jetzt leer und auch entfernt.
aria-data/docs/tool-permissions.md
Geschichts-Dokument zum OpenClaw-Tool-Permission-Bug. Nicht mehr relevant —
Brain hat eigenes Tool-Use-System (Skills + Meta-Tools), keine granulare
Permission-Maske. aria-data/docs/ Ordner war leer, mit raus.
diagnostic/index.html
- "Tool-Berechtigungen"-Section aus den Einstellungen raus. War nur ein
disabled Info-Toggle aus OpenClaw-Zeit ("Claude darf alle Tools").
Brain weiss zur Laufzeit selbst welche Tools es hat (META_TOOLS +
pro aktivem Skill ein dynamisches run_<skill>).
- .perm-grid / .perm-item / .perm-info CSS-Klassen entfernt (sonst nirgends
referenziert).
issue.md
- tools/export-jsonl-to-md.js-Eintrag mit "(mit OpenClaw raus)" markiert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aria-setup.sh
Von 142 Zeilen auf 50 zurechtgestutzt. Alle OpenClaw-Schritte raus
(openclaw.json, exec-approvals, Permissions fuer .openclaw, doctor).
Es bleibt nur der SSH-Key-Setup fuer aria-wohnung — Brain + Proxy
teilen sich denselben Key via Bind-Mount aria-data/ssh/.
init.sh
Legt jetzt aria-data/brain/data + aria-data/brain/qdrant an damit die
Bind-Mounts beim ersten docker compose up nicht ins Leere zeigen.
OpenClaw Tutorial _ MI.pdf
Geloescht — OpenClaw ist abgerissen, das Tutorial gehoert nicht
mehr ins Repo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Zwei neue RVS-Handler in der F5-TTS-Bridge:
xtts_export_voice
Packt <name>.wav + <name>.txt aus VOICES_DIR als tar.gz in-memory,
sendet base64-codiert als xtts_voice_exported zurueck. Diagnostic
baut daraus den Browser-Download.
xtts_import_voice
Empfaengt base64 tar.gz mit <name>.wav (+ optional <name>.txt),
legt sie in VOICES_DIR ab (sanitized name, Path-Traversal-Schutz),
sendet xtts_voice_imported zurueck. Anschliessend handle_list_voices
damit App/Diagnostic die neue Stimme sofort sehen.
So koennen Stimmen zwischen mehreren Gameboxen wandern, ohne die
WAV-Files manuell rumzukopieren.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drei groessere Aenderungen in der Android-App.
Datei-Manager (Settings → Dateien)
- Neuer Eintrag im Settings-Menue → Modal mit Liste
- Suche + Filter (Alle / Von ARIA / Vom User)
- Per Eintrag: ARIA/USER-Badge, Groesse, Datum, Loeschen-Button
- file_list_request via RVS → Bridge → Diagnostic-HTTP → response
- file_delete_request loescht serverseitig, file_deleted-Event
aktualisiert ALLE Chat-Bubbles (Attachment.deleted = true mit
Strikethrough-Name + 🗑️-Icon)
Skill-Created-Bubble
- Neuer ChatMessage.skillCreated Typ — eigenes Render mit gelbem
Border, Skill-Name, Beschreibung, Execution-Mode, Active-Status
- Falls Skill-Setup fehlschlug: ⚠ Setup-Fehler-Zeile direkt in der Bubble
- Stefan sieht in der Chat-History immer wenn ARIA selbst einen
Skill angelegt hat — Transparenz statt schweigend im Hintergrund
Pinch-Zoom rewriten (ZoomableImage.tsx)
- Multi-Touch-Race-Bugs in der alten Variante geloest:
* Touch-Count jetzt aus e.nativeEvent.touches.length statt
gestureState.numberActiveTouches (war nicht zuverlaessig)
* Re-Snapshot bei JEDEM Finger-Wechsel (1↔2) → keine Spruenge mehr
* Doppel-Tap via onPanResponderRelease + Bewegungs-Cap
* pointerEvents="none" auf Image-Wrapper → Touches gehen garantiert
an PanResponder-View
* collapsable={false} verhindert Android-View-Flattening
- 2-Finger-Pinch 1x..5x, simultaner Pan via Focal,
1-Finger-Pan nur wenn gezoomt (>1.02x), Doppel-Tap toggelt 1x↔2.5x
App SettingsScreen Repair-Section
- aria-core-spezifische Buttons raus: 🔧 Reparieren, 🚨 ARIA hart neu,
🧹 Konversation komprimieren (OpenClaw ist abgerissen)
- Stattdessen generischer container_restart fuer aria-bridge/brain/qdrant
- Repair-Buttons aus der "ARIA denkt..."-Bubble entfernt (nur Abbrechen)
ChatScreen
- skill_created und file_deleted Handler im RVS-Message-Switch
- file_list_response (Modal-State liegt in SettingsScreen)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vorbereitung fuer Bridge-als-Agent (OpenClaw raus). Phase-A-Cleanup:
- aria-data/brain-import/ angelegt — System-Prompt-Files BOOTSTRAP.md
+ AGENT.md + USER.md.example + TOOLING.md.example dort archiviert.
Werden vom neuen Agent-Framework spaeter importiert.
- aria-data/config/aria.env + .example geloescht (alle Eintraege waren
tot oder via runtime.json/Hardcode-Default abgedeckt)
- aria-data/config/openclaw.env + openclaw-auth.json geloescht
(Provider-Config landet im Bridge-Code)
- docker-compose: AGENT/BOOTSTRAP/USER/openclaw.env-Mounts aus aria-core
raus; aria.env-Mount aus bridge raus; COMPACT_AFTER_MESSAGES env raus
(kommt jetzt aus runtime.json)
- bridge: CONFIG_PATH-Lesen aus /config/aria.env entfernt, load_config
nutzt nur noch runtime.json; _compact_after liest compactAfterMessages
aus runtime.json (Default 140)
- diagnostic: Eingabefeld "Compact nach Messages" in Runtime-Config-UI;
load/save schreiben/lesen compactAfterMessages
- init.sh: nur noch .env-Bootstrap (Rest landet eh in runtime.json/DB)
ARIA antwortet im Phase-A-Status ohne ihre Persoenlichkeit
(System-Prompt nicht mehr gemountet, "raw Claude" durch Proxy) —
das ist Absicht und Uebergang bis das neue Gehirn live ist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neue ZoomableImage-Komponente — reine RN-Implementation mit
PanResponder + Animated, ohne extra Dependency.
- 2-Finger-Pinch: Zoom 1x..5x, Focal-Point folgt der Geste
- 1-Finger-Pan: nur aktiv wenn gezoomt, mit Bounds-Clamping
- Doppel-Tap: Toggle 1x ↔ 2.5x
Vollbild-Modal ersetzt das simple <Image> durch ZoomableImage fuer
JPEG/PNG/etc. SVGs bleiben non-zoomable (SvgUri-Limitation), Tap
schliesst sie. Plus dedicated ✕-Close-Button oben rechts da Tap-to-
Close mit PanResponder kollidiert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Beim Page-Reload laedt Diagnostic die Chat-History aus dem OpenClaw-
Session-File. file_from_aria-Events sind nur live-broadcast, nicht im
jsonl gespeichert → nach F5 waren die Anhang-Bubbles weg.
Fix: Server parsed [FILE: ...]-Marker aus assistant-messages beim
History-Load und schickt fuer existierende Files ein "aria_file"-
Message-Stueck mit allen Metadaten (Pfad, MIME, Groesse). Frontend
ruft addAriaFile mit denen, sodass die Bubbles wieder erscheinen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bridge-Logs zeigten: ARIA setzt zwei Marker (aria_rave2.mid und .mp3),
hat die .mid aber nie wirklich erstellt. Bridge filterte sie silent →
Stefan sah nur eine Bubble und dachte das Marker-System ist kaputt.
Jetzt: _extract_file_markers gibt auch eine Liste der "missing"-Pfade
zurueck, und im Antworttext steht ein Hinweis-Block fuer den User
welche Files versprochen aber nicht erstellt wurden.
Plus System-Prompt geschaerft: ARIA soll vor dem Marker pruefen ob
das File wirklich existiert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
E2BIG (Argument list too long) tritt auf wenn aria-core's Subprocess-
Spawn das Linux argv-Limit (~128KB-2MB) sprengt. Bei >140 Messages
samt Memory + System-Prompt + Tools laeuft das voll, ARIA antwortet
nur noch leer auf jede Anfrage.
Bridge zaehlt jetzt User-Nachrichten in send_to_core; bei COMPACT_AFTER_MESSAGES
(env, default 140) wird automatisch:
- Sessions geleert (rm sessions/*.jsonl + sessions.json = {})
- aria-core neu gestartet
- User informiert "Konversation komprimiert, letzte Nachricht nochmal"
Plus manueller "🧹 Konversation komprimieren"-Button in App-Settings
und 🧹 Compact-Button in Diagnostic-Thinking-Indicator.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Zweiter Eskalations-Button neben dem Reparieren-Button — fuer Faelle
wo doctor --fix nicht reicht (Run alive aber haengt im Tool-Call).
Mit Confirmation-Dialog damit's nicht versehentlich gedrueckt wird.
Wege:
- App-Settings: Reparatur-Sektion, zwei Buttons (Reparieren + Hart neu)
- App-Thinking-Bubble: 🔧 + 🚨 + Abbrechen
- Diagnostic-Thinking-Indicator: 🔧 + 🚨 + Abbrechen
- Diagnostic-Server: POST /api/aria-restart → child_process exec
`docker restart aria-core`
- Bridge: rvs aria_restart → HTTP zu Diagnostic
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bei stuck OpenClaw-Runs (ARIA antwortet nicht mehr / "Antwort ohne Text"
auf jede Anfrage) kann der User jetzt selbst openclaw doctor --fix
anstossen — ohne SSH/docker exec.
Pfad:
- App-Button → rvs.send('doctor_fix') → Bridge → HTTP POST an
Diagnostic /api/doctor-fix → dockerExec aria-core
- Diagnostic-Button → direkt HTTP POST /api/doctor-fix
Zwei Plaetze in der App: oben in der Thinking-Bubble (wenn ARIA denkt
aber haengt) und in Settings → Reparatur (immer erreichbar). In
Diagnostic neben dem Abbrechen-Button im Thinking-Indicator.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MessageText erkennt http(s)-URLs auf Bilder (jpg/png/gif/webp/bmp/ico)
und rendert sie als <Image> unter dem Text. Markdown-Syntax
 wird durch dasselbe Regex erfasst weil die URL drin ist.
SVGs ausgespart — React Native Image kann SVG nicht ohne Extra-Lib.
Aspect-Ratio wird via Image.getSize ermittelt, gecapped auf 0.5..2.5
damit Panorama-/Streifen-Bilder die Bubble nicht sprengen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frisch geclonte Repo / git pull nach .gitignore-Aenderungen lassen
USER.md (und andere Config-Files) fehlen — docker compose up failt
dann beim Bind-Mount. init.sh kopiert idempotent alle *.example zu
ihren Originalen wenn die noch nicht existieren.
Nach git clone und git pull empfohlen: bash init.sh
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
USER.md hatte Stefan-spezifische Infos (Gitea-URL hackersoft.de,
OpenCRM, STARFACE, RustDesk Tool-Stack). Hat im Repo nichts zu suchen.
USER.md ist jetzt in .gitignore, USER.md.example als Vorlage
eingecheckt. Lokale USER.md bleibt funktional unangetastet — nur
nicht mehr getrackt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ARIA setzt im Antworttext einen Marker `[FILE: /shared/uploads/aria_xxx.ext]`.
Bridge extrahiert ihn (Marker wird aus dem TTS-Text entfernt) und sendet
ein neues file_from_aria-Event ueber RVS an App + Diagnostic.
Diagnostic:
- Eigene Bubble mit Datei-Icon + Klick-Handler
- PDF/Bild → neuer Browser-Tab via /shared/* HTTP-Route
- Andere → Download via download-Attribut
App:
- Neues FileOpenerModule (Kotlin) — Intent.ACTION_VIEW mit FileProvider,
Android-Picker waehlt App nach MIME-Type
- file_paths.xml erweitert (cache + files + external)
- file_response liefert jetzt mimeType mit
- Klick auf ARIA-Anhang: lokal vorhanden → direkt oeffnen, sonst
file_request mit autoOpen-Flag → bei Empfang persistAttachment + open
Stefan muss noch im aria-core/OpenClaw System-Prompt einen Hinweis
einbauen: "Wenn du dem User eine Datei erstellt hast (Pfad in
/shared/uploads/), haenge am Ende deiner Antwort einmalig
[FILE: /shared/uploads/aria_<name>.<ext>] an. Der Marker wird aus dem
sichtbaren Text entfernt und als Anhang in App und Diagnostic angezeigt."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logs zeigen jetzt KEINEN haengenden RNSound-Focus mehr (Library-Version
oder Sound-Lifecycle hat sich geaendert). Der Kick mit AUDIOFOCUS_GAIN
(permanent) sagte Spotify "user hat manuell etwas anderes gestartet" →
Spotify resumed nicht automatisch.
Ohne Kick: unser Focus war AUDIOFOCUS_GAIN_TRANSIENT (USAGE_ASSISTANT) —
beim release bekommt Spotify einen sauberen GAIN nach TRANSIENT-Loss
und resumed automatisch.
Native kickReleaseMedia bleibt fuer den Fall dass es nochmal gebraucht
wird, wird aber nicht mehr gerufen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bisheriges Verhalten: Mute drueckt → stopPlayback. Mute zurueck → noch
eingehende chunks der gleichen Antwort starteten einen neuen Stream und
ARIA redete weiter wo sie war. Funktionierte nur 2x weil dann isFinal
schon kam und keine chunks mehr fluten.
Stefan: "Mund verbieten = Stop, fertig". Neue Antworten sollen normal
spielen.
Fix: _stoppedMessageId-Tracking. Bei Mute=true wird die aktuelle msgId
gemerkt — alle weiteren chunks dieser msgId bleiben silent, auch wenn
Mute zurueckgenommen wird. Reset bei neuer msgId.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stefan: ältere Nachrichten (deren Cache-WAV weg ist) gehen ueber
tts_request neu rendern → kommen als PCM-Stream zurueck → werden ueber
PcmStreamPlayer abgespielt. Beim Mute lief stopPlayback aber ohne den
Spotify-resume-Kick weil hadRnSound=false war (kein currentSound).
Jetzt: kickReleaseMedia immer in stopPlayback rufen — kostet nichts,
deckt PCM- und RNSound-Pfad ab.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>