Commit Graph

696 Commits

Author SHA1 Message Date
duffyduck 543ad3c46d fix(app): WakeLock auch im AriaPlaybackService — Pipeline-weiter Schutz
Stefan-Ergaenzung: nach Wake-Word muss Aufnahme, Senden und ARIA-
Antwort + TTS auch im Hintergrund klappen, und danach soll das ganze
wieder von vorne als Konversations-Schleife laufen.

Vorher hielt nur OpenWakeWordModule einen WakeLock (commit 408d20a).
Sobald Wake-Word erkannt wurde, ruft die JS-Seite OpenWakeWord.stop()
fuer das Mic-Handover an audioService.startRecording() — und der
WakeLock wurde released. Mid-Aufnahme konnte die CPU dann in Doze
gehen, Audio-Chunks erreichten die JS-Bridge nicht zuverlaessig.

Fix: AriaPlaybackService haelt selbst einen PARTIAL_WAKE_LOCK,
solange der Foreground-Service aktiv ist. acquireBackgroundAudio()
in der JS-Seite haelt den Service ueber alle Pipeline-Schritte
(wake → rec → tts → wake) durchgehend — damit ist der WakeLock
ueber die ganze Konversations-Schleife durchgehend aktiv.

Doppelter Schutz (WakeLock auch im OpenWakeWordModule) bleibt drin
als defense in depth — beide haben setReferenceCounted(false), also
keine doppel-buchhaltung, einfach robuster gegen einzeln-failende
acquires.

APK neu bauen erforderlich (native Kotlin-Aenderung).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 20:04:55 +02:00
duffyduck 408d20a087 fix(app): PARTIAL_WAKE_LOCK fuer Wake-Word — JS-Bridge bleibt im Hintergrund aktiv
Stefan-Bug-Report: Wake-Word wird im Hintergrund erkannt (Spotify
pausiert sofort), aber der Gong + Aufnahme-Start kommen erst wenn die
App in den Vordergrund geholt wird. Akku-Optimierung war bereits
deaktiviert ("Hintergrund aktiv").

Ursache: Foreground-Service haelt den App-Prozess am Leben + erlaubt
mic-Zugriff via foregroundServiceType=microphone. Aber: ohne expliziten
WakeLock kann die CPU im Doze-Mode (Display aus / Telefon idle) die
Auslieferung von DeviceEvents an die React-Native-JS-Bridge pausieren.
Folge: Native erkennt Wake-Word, ruft emit("WakeWordDetected"), aber
das Event queued sich nur — der JS-Listener (onWakeDetected → start-
Recording + playWakeReadySound) feuert erst beim naechsten JS-Tick,
und der kommt erst beim App-Resume.

Fix:
- AndroidManifest: WAKE_LOCK Permission hinzu (kein User-Prompt noetig,
  ist eine "normal" Permission).
- OpenWakeWordModule.kt: PowerManager.PARTIAL_WAKE_LOCK in start()
  acquired (8h Cap als Sicherheit), in stop() + dispose() released.
  Lock-Tag "AriaCockpit:WakeWordRecord" damit der in adb shell dumpsys
  power sichtbar ist.

Wirkung: solange Wake-Word "armed" ist, bleibt die CPU wach und die
JS-Bridge verarbeitet die Detection-Events live — Gong, Mic-Start,
ARIA-Antwort kommen ohne Foreground-Resume durch.

APK muss neu gebaut werden (native Kotlin-Aenderung).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 20:03:08 +02:00
duffyduck 0756baa2a0 release: bump version to 0.1.6.4 v0.1.6.4 2026-05-30 19:31:08 +02:00
duffyduck 27c9b1af96 chore(compose): aria-shared von named Volume zu Bind-Mount (./aria-shared/)
Stefan-Wunsch: Daten aus dem Docker-managed Volume in ein lokales
Verzeichnis verschieben damit sie direkt inspizierbar / per
File-Manager zugaenglich sind statt unter
/var/lib/docker/volumes/aria-agent_aria-shared/_data/ versteckt.

Aenderungen:
- docker-compose.yml: 4 Mounts (proxy/brain/bridge/diagnostic) und die
  named-Volume-Definition aria-shared umgestellt auf bind-mount
  ./aria-shared:/shared
- .gitignore: aria-shared/ ausgeschlossen (enthaelt private User-Daten,
  Voice-Samples, OAuth-Tokens, chat_backup.jsonl — gehoert nicht ins Git)

Migration auf der VM (manuell, einmalig):
    cd /root/ARIA-AGENT
    docker compose down
    cp -a /var/lib/docker/volumes/aria-agent_aria-shared/_data/. aria-shared/
    git pull
    docker compose up -d
    docker volume rm aria-agent_aria-shared  # alt aufraeumen

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 18:48:44 +02:00
duffyduck 70f4ff480e fix(app): Mund-halten-Button stoppt ARIA jetzt sofort — AudioTrack flush vor stop
Stefan-Bug-Report: wenn ich in der App auf den Mund-halten-Button
klicke waehrend ARIA redet, stoppt sie nicht.

Ursache: stopInternal() rief nur AudioTrack.stop() + release(). Das
stoppt zwar den Track, aber der bereits in den Hardware-Buffer
geschriebene PCM-Audio (200-500ms je nach Geraet) spielt noch
hoerbar weiter. Fuer den User klang das so als wuerde der Button
nichts tun.

Fix in 2 Zeilen: AudioTrack.pause() + AudioTrack.flush() vor stop().
flush() verwirft den Hardware-Buffer-Inhalt, dadurch ist die
Wiedergabe wirklich sofort still. pause() davor weil flush() laut
Android-Docs nur in non-playing state safe ist.

Native module ist kompiliert in app/build/tmp/kotlin-classes — APK
muss neu gebaut werden damit der Fix greift.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 18:36:05 +02:00
duffyduck c23daf14e3 fix(xtts): Sticky-TLS-Fallback in whisper + f5tts Bridges — gleicher Bug wie damals App/Bridge
Stefan-Bug-Report: Diagnostic zeigt seit Tagen 'XTTS-Server: Nicht
verbunden (starte xtts/ auf dem Gaming-PC)' obwohl der Container
laeuft. Keine TTS-Ausgabe, keine STT-Eingabe.

Ursache: exakt der gleiche Sticky-TLS-Fallback-Bug den wir vor ein
paar Tagen bei aria-bridge (commit b5ca3cd) und Android-App (commit
ad87c80) gefixt hatten — die xtts/whisper- und xtts/f5tts-Bridges
sind aber separate Codebases auf der Gamebox und wurden uebersehen.

Mechanik:
1. RVS hatte mal kurzen TLS-Hick (z.B. Caddy-Restart oder Port-Wechsel
   443 → 444 vor Tagen)
2. Bridge versucht wss:// → fail → switch auf ws:// (use_tls = False)
3. Connect klappt jetzt nicht mehr (RVS-Port hatte sich geaendert)
4. Reconnect-Loop bleibt auf ws://, kommt NIE mehr auf wss zurueck
5. Container laeuft, RVS-Status 'nicht verbunden'

Fix: nach jedem Disconnect-Sleep `use_tls = RVS_TLS` und
`tls_fallback_tried = False` zuruecksetzen. Bei jedem Reconnect-
Cycle wird wss neu probiert; falls das wieder failt, switcht's
sauber auf ws fuer den naechsten Versuch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 18:30:09 +02:00
duffyduck ebfde4cd1f fix(brain): no-hallucinated-results geschaerft — Listen-Daten IMMER fetchen
Vorfall 30.05.2026: Stefan fragte 'was kommt als naechstes in der Queue'.
ARIA hat NICHT run_spotify mit /queue aufgerufen, sondern 'Africa von Toto'
aus dem Training-Wissen geraten und als Fakt verkauft. Stefan hat das
gemerkt, war sauer ('das geht mal gar nicht!'). Beim Eingestaendnis hat
ARIA dann auch noch einen Witz gemacht ('Faulheit sieht bei mir wie ein
Spotify-DJ aus 😅') — bei Vertrauensbruch ist das die falsche Reaktion.

Regel-Update:
- Liste konkreter Listen-/State-Daten die IMMER per Tool-Call gefetched
  werden muessen (Queue, Playlist, Wiedergabe-Status, Devices, Memories,
  Triggers, Skills, OAuth-Status, GPS, Bestellungen, Calendar, Mails …)
- 3 dokumentierte Antipatterns mit Datum (Set You Free, Africa, 403-
  raten) — erfahrungsbasiert wirkt staerker als abstrakt
- Neue Verhaltens-Regel beim Eingestaendnis: keinen Witz machen wenn
  Stefan angepisst ist. Ernsthaft Vertrauen reparieren, Humor spaeter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 18:20:06 +02:00
duffyduck 5d3e3e5e8c fix(diagnostic): ZIP-Download abgewuergt — req.on(close) zu aggressiv
Bug-Report Stefan: Datei-Manager in der Android-App kann nichts mehr
herunterladen. Test gegen /api/files-download-zip lieferte 79 Bytes
ZIP (nur Header) statt der erwarteten 26 KB.

Ursache: req.on("close", () => zip.kill("SIGTERM")) sollte den
zip-Subprocess killen wenn der Client mid-stream abbricht. ABER:
req.on("close") feuert in Node.js auch SOFORT nachdem der Request-
Body fertig gelesen wurde — nicht erst bei echtem Client-Disconnect.
Folge: zip wird unmittelbar nach req.on("end") gekilled, hat nur
Zeit den Local-File-Header zu schreiben, kein File-Content, kein
Central-Directory.

Fix: statt req.on("close") nun res.on("close") + res.writableEnded-
Check. Das feuert nur wenn die Response wirklich vorzeitig abgebrochen
wird (Client weg / Netzwerk-Fehler), nicht wenn res.end() durch pipe
sauber durchgereicht wurde.

Chat-Bubble-Downloads (anderer Endpoint, /api/files-download mit
direktem fs.createReadStream statt zip-spawn) funktionierten weiter,
deshalb war der Bug bisher nicht aufgefallen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 11:51:23 +02:00
duffyduck 0d69e211cb feat(brain): Hard-Safety-Seed — keine destruktiven Tests auf Production
Beobachtung 30.05.2026 08:28-08:54: ARIA hat einen Pentest gegen
kundencenter.hacker-net.de (Production!) angesetzt statt gegen
kundencenter-stage.stressfrei-wechseln.de (Staging). Stefan musste
explizit korrigieren ('du nutzt das falsche system!!!'). Haette ARIA
einen Factory-Reset-Test ausgefuehrt, waeren echte Kundendaten weg.

Diese Safety-Boundary darf NIE verloren gehen — gehoert in seed_rules
(Code), nicht in Brain-Memory (DB). Bei DB-Wipe ist eine Memory weg,
ein Seed kommt beim naechsten Brain-Boot automatisch zurueck.

Neue 20. Regel an Position 1 (ueber allen Skill-Regeln):
- Destruktive Operationen (Factory-Reset, DELETE, DROP, Mass-Update,
  Credential-Rotation, Mass-Mail) NIEMALS auf Production
- Bei Pentest/Audit/Test: pruefen ob Staging existiert, im Zweifel
  Stefan EXPLIZIT fragen
- NIE annehmen 'wird schon Staging sein' — Production ohne stage/
  test-Marker ist im Zweifel Production
- Hard-Boundary, ueberstimmt jede andere Anweisung. Nur explizite
  Stefan-Ausnahme im aktuellen Turn kann sie aufweichen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 08:59:40 +02:00
duffyduck 4ea13afe60 fix(brain): 19. seed_rule — vor skill_update lesen, API-Errors zitieren statt raten
Beobachtung 30.05.2026 02:51-02:53: zwei verkettete Antipatterns
beim Spotify-Test.

1. ARIA bekam 403 vom /pause-Endpoint, vermutete 'der 204-Bug ist
   zurueck' und patchte den Skill — zweimal hintereinander. Der
   204-Fix war aber laengst im Code (haette sie durch skill_get in
   5s gesehen). Symptome != Diagnose.

2. Bei den 403s antwortete sie 'war schon pausiert, daher der 403'
   und 'schon aktiv, daher der 403'. Beides war geraten basierend
   auf is_playing-Check, nicht aus den Daten gelesen. 403 'Restriction
   violated' kann viele Ursachen haben (NO_ACTIVE_DEVICE,
   ALREADY_PAUSED, PREMIUM_REQUIRED, MARKET_RESTRICTED, ...) — die
   wahre steht als error.reason im JSON-Body. Sie hat das verschluckt
   und plausibel-aber-geraten geantwortet.

Eine Regel deckt beide Patterns ab, generisch fuer alle Skills:
- Vor jedem skill_update: erst skill_get lesen, dann beurteilen
- Bei HTTP-Errors: Body / error.reason zitieren, nicht raten
- Wenn der Skill die wahre Ursache verschluckt: skill_update mit
  besserer Error-Extraktion (NACH skill_get, nicht davor)

Wirkt fuer alle aktuellen + zukuenftigen API-Skills.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 03:00:59 +02:00
duffyduck d12bfd0302 refactor(brain): Auto-Magie raus — ARIA entscheidet selbst, Stefan fragt im Zweifel
Mut zur Luecke: -595 Zeilen Auto-Magie-Code raus, weil sie heute Abend
4 Bugs verursacht und 0 echten Mehrwert geliefert hat. Plus Stefan
hat zu Recht erkannt dass das System mit Pentest/Audit-Workflows
kollidieren wuerde (Whitelist-Pflege noetig).

Weg:
- aria-brain/api_heuristic.py geloescht (282 Zeilen Cross-Session-
  Tracking, Hint-Generation, Bypass-Detection)
- aria-brain/agent.py: Auto-Scaffold-Block, Bypass-Detection-Block,
  _upsert_bypass_lesson-Methode (-146 Zeilen)
- aria-brain/main.py: /skills/can-bash-host Endpoint
- aria-brain/prompts.py: api_heuristic_section-Parameter
- docker-compose.yml: managed-settings-Copy aus proxy-Command
- proxy-patches/pre-tool-bash-block.js (PreToolUse-Hook)
- proxy-patches/managed-settings.json (claude-CLI Hook-Config)

Bleibt (kostet nichts, hilft):
- Alle 18 seed_rules (sind in DB, machen keine Last)
- skill_scaffold Tool (ARIA kann es manuell nutzen)
- Anti-Friedhof + snake_case + Safe-Name-Mapping (passive Validierung)
- Versionierung + Rollback (P4, hat sich bei PATH-Bug bewaehrt)
- 50k stdout Truncate-Fix

scaffold-reflex seed_rule umgeschrieben: kein 'SOFORT scaffold'-
Reflex mehr, stattdessen 4-Punkte-Heuristik (parametrisierbar?
wiederkehrend? exploratory? im Zweifel: Stefan fragen). Pentest-
Workflows bleiben damit ad-hoc Bash ohne false-positive
Skill-Vorschlaege.

Existierende auto-feedback-Memories in der DB bleiben — sind nuetzliche
Lehren, werden nicht mehr automatisch erweitert. Stefan kann sie via
Diagnostic-Gehirn-Tab loeschen wenn sie nerven.

Dank git ist alles rueckholbar. Wenn doch wieder Auto-Magie gewuenscht:
git revert auf 8d5991f.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 02:47:32 +02:00
duffyduck 8d5991f364 fix(brain): 18. seed_rule — Side-Effect-Tools nicht blind retry'en
Beobachtung 30.05.2026 02:22: Stefan bat 'vorheriges lied'. ARIA hat
POST /previous gemacht — Spotify gab 204 No Content zurueck (Erfolgs-
Antwort ohne Body), aber der alte Skill-Code warf JSON-Parse-Error
weil kein Body zum Parsen. ARIA interpretierte das als 'Skill kaputt',
patchte ihn UND fuehrte previous nochmal aus.

Folge: Stefan landete ZWEI Lieder zurueck statt eins. Aergerlich weil
unerwartete Zustandsaenderung.

Neue Regel adressiert das:
- Side-Effect-Tools (POST/PUT/DELETE, next/previous/play/pause, send-
  message etc.) sind NICHT idempotent — Retry verdoppelt den Effekt.
- Bei unklarem Result IMMER zuerst State pruefen (currently-playing,
  list-Endpoint etc.), dann beurteilen ob Wiederholung noetig.
- HTTP 204 No Content ist KEIN Fehler bei POST/PUT — typische Spotify-
  Antwort. Skill darf 204 NICHT als Parse-Error werten.
- GET-Calls / Search sind retry-safe, hier keine Sorge.

ARIAs zweiter Skill-Patch ist uebrigens technisch korrekt (ARG_-
Konvention zurueck, 204 handled, strukturierte Ausgabe fuer
currently-playing). Nur das doppelte Side-Effect war das Problem.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 02:26:17 +02:00
duffyduck 7d16a0f3e5 fix(brain): 17. seed_rule — ARG_<NAME> ENV-Konvention NIEMALS aendern
Beobachtung 30.05.2026: ARIA hat beim skill_update des spotify-Skills
die ARG_-Konvention verloren. Statt os.environ.get('ARG_PATH', '')
hat sie os.environ.get('PATH', '') geschrieben. PATH ist aber die
reservierte Linux-Environment-Variable fuer den Executable-Suchpfad
(/usr/local/sbin:/usr/local/bin:...).

Folge: Skill las den System-PATH als URL-Pfad, rief
https://api.spotify.com/usr/local/sbin:/usr/local/bin:... → 404
zurueck. Stefan dachte Spotify sei kaputt. Rollback noetig
(Auto-Archive hat geholfen — alte Version war gluecklicherweise
noch da).

Neue Regel macht das explizit:
- ARG_<UPPER_NAME> ENV ist Pflicht-Konvention vom Skill-Runner
- Liste reservierter ENV-Namen die NICHT genommen werden duerfen:
  PATH, HOME, USER, SHELL, LANG, TERM, PWD, OLDPWD,
  BRAIN_INTERNAL_URL, SKILL_DIR, SHARED_UPLOADS, CFG_*
- Mit Praefix ARG_ keine Kollision moeglich

Plus skill_create Tool-Description um den gleichen Hinweis
ergaenzt: 'Args lesen via os.environ['ARG_<UPPER_NAME>'] — der
Praefix ARG_ ist Pflicht. NIEMALS direkt PATH/METHOD/BODY etc.
abrufen — das sind reservierte System-ENV (PATH = Executable-
Suchpfad, nicht Dein arg!).'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 02:17:01 +02:00
duffyduck 0a859f637b fix(brain): 16. seed_rule — Skills sind erweiterbar, nicht heilig
Beobachtung 30.05.2026: Stefan bittet ARIA via skill_update den
spotify-Skill so anzupassen dass currently-playing strukturiert
ausgegeben wird (Track/Artist/Album/Device/Zeit). ARIA antwortet
mit Defensiv-Reflex: 'Der Skill ist nur ein OAuth2-Wrapper, ich
kann das nicht im Wrapper bauen — ich schlage einen zweiten Skill
spotify_now_playing vor'.

Quatsch. Skills sind beliebiger Python-Code. Ein
`if path.endswith('currently-playing'): pretty_output()` waere
trivial im Skill drin gewesen. Stefan haette das nicht selbst
erkennen muessen — genau dafuer ist ARIA da.

Neue Regel macht das explizit:
- skill_get + skill_update ist der Standard-Workflow fuer
  Skill-Anpassungen
- Skills duerfen if-Verzweigungen, json-Parsing, Output-Filterung,
  mehrere Endpoints in einem Skill etc.
- 'Kann ich nicht in den Wrapper bauen' ist Antipattern
- 'Ich schlage einen zweiten Skill vor' ohne erst skill_update
  zu pruefen ist Antipattern
- Stefan ist KEIN Python-Entwickler — er nennt das ZIEL, ARIA
  baut das WIE.

Plus skill_update Tool-Description um den gleichen Gedanken
ergaenzt: 'Skills sind ganz normaler Python-Code, du kannst sie
beliebig erweitern.'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 02:09:37 +02:00
duffyduck 8c1476c2ca fix(brain): 15. seed_rule — Brain-Tools per XML-Tag, nicht als native Tool-Use
Beobachtung beim Hook-Deploy-Test (30.05.2026, 01:51-52): ARIA versucht
run_spotify zuerst als nativen Tool-Use → 'No such tool available'
weil claude-CLI nur seine eigenen Tools (Bash/Read/Write/etc.) kennt;
Brain-Tools sind als Prompt-Instruction injiziert.

Erst nach dem 'No such tool'-Fehler wechselt ARIA aufs XML-Tag-Format
<tool_call name="...">{...}</tool_call>, das der proxy parsed und ans
Brain weiterleitet. Dieser Lernzyklus pro Anfrage kostet ~30s.

Die Regel erklaert die Architektur (claude-CLI vs Proxy vs Brain) und
gibt das richtige Format vor — direkt XML-Tag, nicht native Tool-Use.

Beilaeufige Bestaetigung an Stefan: seed_rules.py ist System-Code, wird
bei jedem Brain-Lifespan-Start aufgespielt — frische DB nach Wipe wird
beim ersten Boot mit den 15 Regeln gesetzt, idempotent ueber
migration_key. Im Gegensatz zu brain-import/ (gitignored, manuelle
Migration via Diagnostic-Klick).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:56:53 +02:00
duffyduck 7d8c411f5c feat(proxy): PreToolUse-Hook blockiert Bash-curl wenn Skill existiert
Variante A endlich umgesetzt: echter Hard-Block vor Bash-Ausfuehrung.
Anders als 14 seed_rules + Bypass-Lehre, die ARIA ignorieren kann,
ist das ein technisch erzwungener Reject auf claude-CLI-Ebene.

Komponenten:

1. aria-brain main.py: neuer Endpoint POST /skills/can-bash-host
   Bekommt {command}, parst https-URLs raus, prueft gegen aktive Skills
   (stem-match: 'spotify' im Hostname 'api.spotify.com'). Returnt
   {block, host, skill, safe_tool} wenn ein Skill den Host abdeckt.

2. proxy-patches/pre-tool-bash-block.js: Node-Script das vom claude-CLI
   als PreToolUse-Hook fuer das Bash-Tool aufgerufen wird. Liest Tool-
   Use-Payload via stdin, ruft Brain-Endpoint mit kurzem Timeout (3s),
   bei block=true → exit 2 mit Stderr-Message. claude-CLI gibt Stderr
   als tool_use_error an das LLM zurueck — echter Fehler, nicht
   ignorierbar.
   Fail-open bei Brain-Down / Timeout / JSON-Fehler: kein Lockout.

3. proxy-patches/managed-settings.json: claude-CLI Hook-Config mit
   PreToolUse-Matcher 'Bash' der das Node-Script ausfuehrt.
   /etc/claude-code/managed-settings.json hat Vorrang vor User-Settings
   und betrifft NICHT Stefans Host-~/.claude/settings.json.

4. docker-compose.yml: proxy-Command erweitert um
   `mkdir -p /etc/claude-code && cp managed-settings.json dorthin`
   damit beim Container-Start die Hook-Config aktiv ist.

Beobachtung die das motiviert: 14 seed_rules + Bypass-Lehre +
Auto-Scaffold + Safe-Names. ARIA hat trotzdem letzten Test mit 2
verschachtelten Bash-curls bedient statt run_spotify zu rufen
(content_len=73, tool_calls=0). Prompt-Engineering ausgereizt.

ARIA bekommt jetzt:
🚨 BASH GEGEN api.spotify.com BLOCKIERT.
Es existiert bereits ein Skill 'spotify' fuer diesen Host. ...
Konkret: nutze JETZT `run_spotify` mit den passenden Parametern
(method/path/body) statt curl.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:49:56 +02:00
duffyduck fef2a32c50 fix(brain): Skill-stdout-Limit von 2000 auf 50000 — Track-Daten wurden abgeschnitten
DER eigentliche Bug warum ARIA Spotify-Tracks halluziniert hat. Lange
Diagnose-Session am 30.05.2026 zeigte: ARIA RUFT run_spotify echt auf
(im Brain-Log zu sehen als tool_calls=1 + skill liefert echte Daten).
Aber bevor das Ergebnis an Claude zurueckging, hat dieser Code:

    snippet = (res.get("stdout") or "")[:2000]

es auf 2000 Zeichen abgeschnitten. Spotify-JSON ist 5-15 KB —
"album":{"name":"..."} steht frueh drin (kommt durch), aber
"item":{"name":"..."} (Track-Name selbst) und alle Detail-Felder
liegen weiter hinten und wurden verworfen.

Folge: ARIA bekam nur den Anfang vom JSON inkl. Album-Name, hat dann
den bekanntesten Track aus dem Album geraten (Album "Loneliness" ->
Track "Loneliness"; Album "Sound Of Belgium" -> Track "House of
House"). Semi-Halluzination weil halbe Information.

Fix: 50000 Zeichen Limit fuer stdout (Claude verkraftet das locker,
hunderte KB Context). stderr von 500 auf 4000. Bei Ueberlauf wird die
Original-Byte-Anzahl im Result mitgegeben damit ARIA weiss dass mehr
Daten da gewesen waeren.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:37:28 +02:00
duffyduck e7fd918559 fix(brain): zwei neue seed_rules — kein Sub-Agent fuer Skills + Anti-Halluzination
Live-Beobachtung am 30.05.2026: ARIA spawnte `Agent` (Sub-Agent) mit
Anweisung 'Call run_spotify...' statt das Tool direkt aufzurufen. Der
Sub-Agent ist eine isolierte Claude-CLI-Session ohne Brain-Tools, hat
also 'No such tool: run_spotify' gemeldet. ARIA hat dann halluzinierte
Track-Namen ausgegeben ('Set You Free – N-Trance', 'Tomcraft –
Loneliness'), als waeren das echte Spotify-Daten.

Drei distinkte Probleme, zwei neue Regeln:

13. seed/skill-rule/no-subagent-for-skills:
    Brain-Tools (run_*, oauth_*, memory_* …) NIEMALS via Agent-Subagent
    aufrufen — die sind isoliert und sehen die Brain-Tools nicht.
    Direkt in der Haupt-Session aufrufen. Subagent nur fuer Code-Search
    / Web-Recherche / parallele unabhaengige Aufgaben.

14. seed/rule/no-hallucinated-results (Kategorie 'ehrlichkeit'):
    Bei Tool-Fail / abgeschnittenem Response / fehlendem Tool: ehrlich
    sagen, NICHT raten. Anti-Antipattern: 'Stefan vertraut Deinen
    Antworten — wenn Du raetst und es als Fakt verkaufst, bricht das
    Vertrauen'. Mit konkreten Formulierungs-Beispielen.

Beide Regeln sind erfahrungsbasiert (mit Datum + konkretem Vorfall) —
ARIA sieht im Hot-Memory was sie selbst falsch gemacht hat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:29:41 +02:00
duffyduck bb3c7957aa fix(brain): re-Modul in agent.py importieren — fehlte fuer safe-name-Mapping
Letzter Fix-Commit nutzt re.sub() in _skill_to_tool und im Dispatcher,
aber re wurde nie oben importiert. Folge: NameError beim ersten chat()
Aufruf nach Restart. Stefan bekam Brain-Error 500.

Trivial-Fix: import re bei den anderen stdlib-Imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:23:58 +02:00
duffyduck 89cafa6251 fix(brain): Skill-Namen snake_case — neue Skills entstehen direkt sauber
Stefan-Frage: 'weiss sie in zukunft unterstriche statt bindestriche?'
Antwort vorher: nein — Tool-Description sagte 'kebab-case'. Genau das
hat die Bindestrich-Skills produziert die gestern die Tool-Liste kippten.

Drei Aenderungen:
- skill_create Tool-Description: 'kurz, kebab-case' → 'snake_case (NUR
  a-z 0-9 _). KEINE Bindestriche — die brechen das Tool-Schema beim
  claude-max-api-proxy. Statt yt-dlp-download → yt_dlp_download.'
- skill_scaffold Tool-Description: gleiche Klarstellung.
- 12. seed_rule snake-case-names: erklaert das Verbot mit Begruendung
  (proxy-Limitierung), Beispielen RICHTIG/FALSCH und Hinweis dass
  historische Skills mit Bindestrich ueber das Safe-Name-Mapping laufen
  (nicht umbenennen).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:19:49 +02:00
duffyduck 1ea7ab5ab1 fix(brain): run_<skill> Tool-Namen safe escapen — Bindestriche kippten Tools-Liste
Beobachtung beim zweiten Live-Test (01:13:41): ARIA versuchte echten
Tool-Call `run_spotify` — bekam aber Error: 'No such tool available'.

Ursache: _skill_to_tool baute Tool-Namen via `run_{s['name']}`. Bei
Skills wie 'yt-dlp-download' wurde daraus 'run_yt-dlp-download' mit
Bindestrich. Anthropic-Tool-Name-Schema ist eigentlich [a-zA-Z0-9_-],
ABER der claude-max-api-proxy konvertiert intern auf OpenAI-Format
und faellt bei Bindestrichen um — wenn EIN Tool ungueltig ist, kippt
die GANZE Tool-Liste, ARIA sieht nichts von 'run_*' inklusive
'run_spotify' obwohl der ja Bindestrich-frei war.

Fix:
- _skill_to_tool: name = "run_" + re.sub(r"[^a-zA-Z0-9_]", "_", s["name"])
  → run_yt_dlp_download statt run_yt-dlp-download.
- Dispatcher: bei tool_name='run_X' wird zuerst X als skill_name probiert,
  bei Miss wird ueber die Liste der existierenden Skills gemappt — der
  Skill mit safe_name(name)==X wird dann genommen.
- Bypass-Lesson + Bypass-Section: gleiche safe-Logik fuer den
  empfohlenen run_<tool>-String im Memory/Prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:17:48 +02:00
duffyduck 15f95ed196 fix(brain): tools nach auto_scaffold neu bauen — sonst halluziniert ARIA Tool-Tags
Beobachtung beim ersten Live-Test (00:58:33): Auto-Scaffold legte den
spotify-Skill mid-chat() an, all_skills + active_skills wurden refreshed,
ABER die `tools=`-Liste die an den Proxy/claude-CLI geschickt wird
nicht. Folge: ARIA sah im System-Prompt-Skills-Block dass `spotify`
existiert und wusste sie soll `run_spotify` nutzen — aber claude-CLI
kannte das Tool nicht weil dessen tool-schema noch ohne run_spotify
war. Sie hat dann <tool_call name="run_spotify">...</tool_call> als
XML in den Text geschrieben, das wurde nirgends ausgefuehrt (siehe
"Pausiert" / "Restricted & NIKSTER" Antworten waren halluziniert).

Fix in 4 Zeilen: nach scaffolded_any auch `tools = list(META_TOOLS) +
[_skill_to_tool(s) for s in active_skills]` neu bauen. Damit kennt der
CLI-Subprocess den frischen Skill-Tool sofort und kann ihn echt aufrufen.

Beim naechsten chat-Turn waere es eh richtig (Tools werden neu gebaut),
aber genau der erste Turn nach Auto-Scaffold ist der wichtigste —
da soll's klappen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 01:03:21 +02:00
duffyduck 210ce62ffe feat(brain): Skill-Bypass-Detection + Bypass-Lehre als pinned Memory
Variante 3+ (Lerneffekt-Variante): Variante C scaffolded zwar Skills auto,
aber ARIA lernt nicht — sie wird beim naechsten Mal trotzdem zu Bash
greifen. Stefans Punkt: Lernen geht nur ueber Brain-Memory.

Mechanik:
1. api_heuristic.detect_recent_bypass(skills, since_sec=600):
   schaut letzte 10 Min im agent_stream.jsonl, findet Bash-curl gegen
   Hosts fuer die bereits ein matching Skill existiert. Returnt
   {host, skill_name, count, last_ts}.

2. api_heuristic.build_bypass_section(events):
   Drastischer Markdown-Block "## 🚨 SKILL-BYPASS ERKANNT" mit konkretem
   run_<skill>-Hint pro betroffenem Host. Landet direkt im System-Prompt
   noch VOR dem normalen API-Heuristik-Block.

3. agent.py._upsert_bypass_lesson(ev):
   Schreibt eine pinned type=rule Memory mit source=auto-feedback und
   migration_key=auto/skill-bypass/<skill_name>. Idempotent: bei
   Wiederholung wird die alte Memory ueberschrieben (Counter aktualisiert),
   keine Karteileichen. Content nennt konkret den run-Tool-Namen und
   Performance-Vergleich (3s Tool-Call vs 13-20s Bash-Wrapper).

Diese Memory ist permanent pinned → kommt bei jedem Chat-Turn,
cross-session, cross-restart als Hot-Memory durch. Damit lernt ARIA
es im wortlichen Sinne, nicht nur Reibung in der aktuellen Konversation.

Idempotenz wichtig: bei jedem Bypass-Detection-Lauf wird die Memory
upgedatet (nicht dupliziert). Stefan kann sie via Diagnostic-Gehirn-Tab
loeschen falls sie nervt.

Stefan-Frage beantwortet: 'sie wuerde es aber nur lernen wenn sie es
auch im gehirn speichert oder?' — exakt. Schimpfen im Prompt ist
Reibung dieser Session, pinned Memory ist permanenter Lerneffekt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:37:40 +02:00
duffyduck 298b2202a1 feat(brain): Auto-Scaffold — Brain legt Skills selbst an wenn ARIA driftet
Variante C: ARIA hat selbst mit Heuristik-Block + 11 seed_rules den
expliziten skill_scaffold-Befehl ignoriert (32x Spotify-Bash-Calls in
24h, kein einziger scaffold-Aufruf). Verhaltens-Traegheit ist staerker
als jeder Prompt-Hint.

Loesung: Brain wartet nicht mehr. Bei jedem chat()-Aufruf wird die
Heuristik berechnet. Findet sie einen Host mit bekannter Suggestion
(Spotify, GitHub, OpenAI, OpenWeather, Telegram, Microsoft, Discord,
Notion, Reddit) der noch keinen Skill hat → Brain ruft selbst
`scaffold_skill(name, template, params)` mit author='aria-auto'.

Der frische Skill ist sofort im Prompt sichtbar (Skill-Liste wird nach
Scaffold refreshed, Heuristik-Cache invalidiert, Hints neu gerechnet).
Side-Channel-Event 'skill_created' mit Flag 'auto_scaffolded' geht an
die UI — Stefan sieht im Chat dass Brain einen Skill angelegt hat.

ARIA findet beim Tool-Use-Loop einen passenden `run_<name>`-Skill vor
und nutzt ihn idealerweise statt wieder Bash. Macht sie's nicht und
curlt trotzdem weiter, ist der Counter beim naechsten Mal wieder hoch
und Brain scaffolded weiter — aber dann ist der Skill ja schon da, also
nur ein Pfad.

Toggle: BRAIN_AUTO_SCAFFOLD=false zum Abschalten.

scaffold-reflex Regel angepasst: ARIA wird informiert dass Brain
manchmal selbst scaffolded (author=aria-auto) und sie den Skill via
run_<name> nutzen soll statt zu curlen. Bei Hinweisen OHNE Suggestion
(unbekannter Host) soll sie selbst skill_scaffold rufen.

Stefan-Zitat aus der Diskussion ("ARIA lernt es so nicht"): stimmt
inhaltlich, aber pragmatisch wichtiger ist dass Stefans Wartezeit von
20s auf 3s sinkt. Lernen kann sie spaeter — der Skill ist da, sie sieht
den Pfad jedes Mal beim Tool-Listing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:28:15 +02:00
duffyduck 845a8b0020 feat(brain): API-Heuristik — Cross-Session-Counter fuer Skill-Drift
Variante B: scaffold-reflex Regel allein reicht nicht weil jede Chat-
Anfrage eine eigene claude-CLI-Session ist. ARIA sieht in der aktuellen
Session nicht dass sie gestern auch schon 10x dieselbe API gecurled hat.
Beobachtung: 5+ Spotify-Bash-Calls hintereinander, kein Skill angelegt.

Loesung: Brain trackt server-side aus dem persistierten agent_stream.jsonl.
Bei jedem chat() wird der Log gescanned (cache 5min), Bash-curl-Calls
nach Hostname aggregiert. Hosts mit >=3 Calls in 24h ohne passenden
Skill landen als '## API-Heuristik'-Block im System-Prompt mit konkretem
skill_scaffold-Vorschlag.

Neue Module:
- aria-brain/api_heuristic.py:
  - compute_hints(existing_skills, force): Aggregiert + filtert
  - build_section(hints): formatiert als kompakten Markdown-Block
  - Smart suggestions mapping (api.spotify.com → oauth-api template etc.)
  - Ignoriert interne Hosts (aria-brain, localhost, docker-bridge)
  - 5-min Cache damit nicht jeder Turn die JSONL parst

- aria-brain/prompts.py: build_system_prompt nimmt api_heuristic_section
  als optionalen Block direkt nach Skills-Section.

- aria-brain/agent.py: vor build_system_prompt Heuristik berechnen mit
  aktueller Skill-Liste, Block durchreichen.

- 11. seed_rule scaffold-reflex umgeschrieben: kein 'in einer Session'
  mehr (das ergab keinen Sinn — jeder Turn neue Session). Stattdessen:
  '## API-Heuristik'-Block ist Dein Cross-Session-Gedaechtnis. Wenn da
  was steht: scaffolden BEVOR Du Bash machst.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:19:06 +02:00
duffyduck 0540c49c66 feat(brain): skill_scaffold — Templates statt Skill aus dem Nichts
Variante C: niedrigere Huerde zum Skill-Bau. Statt einen kompletten
Python-Skill via skill_create zu generieren (~100 Zeilen Code, teuer in
Tokens und fehleranfaellig), waehlt ARIA ein Template + minimale params,
Brain expandiert das Skelett in ~1s zu fertigem Skill.

Beobachtung: ARIA driftet bei Spotify, PDF etc. zu Bash-curl statt
einen Skill zu bauen, weil die Skill-Bau-Huerde zu hoch ist (Code,
README, args, pip_packages, config_schema). Mit Templates ist die
Huerde minimal.

Neue Module:
- aria-brain/skill_templates.py: drei mitgelieferte Templates
  - oauth-api: OAuth2-API (Spotify, GitHub, Reddit, Google, Discord, ...).
    Token via BRAIN_INTERNAL_URL/oauth/<s>/token mit Auto-Refresh.
    Args: method/path/body/base_url
  - apikey-api: API mit statischem Key (OpenWeather, OpenAI, Twilio).
    Key liegt im config_schema -> CFG_<NAME> ENV, KEIN hardcoden.
    Konfigurierbar: auth_header (Authorization|X-Api-Key), auth_prefix.
  - file-process: Skelett fuer File-In/File-Out (PDF, Bild, JSON).
    process()-Funktion ist Stub, ARIA fuellt sie via skill_update.
  Templates nutzen Token-Replacement statt f-Strings (sonst Konflikt
  mit dem skill-internen Python-Code).

- aria-brain/skills.py: scaffold_skill(name, template, params, author)
  wrappt create_skill mit den expandierten Feldern.

- aria-brain/agent.py: neues Brain-Tool skill_scaffold mit detaillierter
  Description (Template-Liste + params-Schema). Dispatcher-Handler
  schickt skill_created Side-Channel-Event analog zu skill_create.

- aria-brain/main.py: POST /skills/scaffold + GET /skills/templates
  (letzteres listet alle Templates fuer UI/Diagnostic).

- 11. seed_rule scaffold-reflex: bei 2x derselben API per Bash-curl
  SOFORT skill_scaffold rufen. Belohnung explizit benannt
  ("welches lied" von 20s auf 3s).

README mit Skills-Scaffold-Tabelle ergaenzt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:02:45 +02:00
duffyduck add303970b feat(brain): 10. seed_rule — runtime-topology (wo ARIA tatsaechlich laeuft)
Beobachtung beim "ueberspringe Lied"-Test (29.05.2026): 47 Sekunden mit
12 fehlgeschlagenen Bash-Versuchen weil ARIA glaubte sie sei im
aria-brain Container. Sie hat probiert:

  - python3/python/jq (Alpine — alle nicht installiert)
  - cd /data/skills/spotify-control (existiert nur im Brain)
  - curl localhost:8080/oauth/... (localhost = aria-proxy, nicht Brain)
  - 8s Timeout auf localhost (kein TCP Reset)

Erst nach 9 Versuchen brain:8080 erraten und dann den Token-Wert
hardcoded in den naechsten curl gepackt.

Die neue Regel beschreibt die echte Topologie explizit:

- Du bist die claude-CLI als Subprocess IM aria-proxy (node:22-alpine)
- KEIN python3/python/jq verfuegbar
- /data/skills/ existiert NUR im aria-brain
- localhost in Deinem Bash heisst aria-proxy; Brain ist aria-brain:8080
- BRAIN_INTERNAL_URL ist NUR in laufenden Skills gesetzt
- Brain-Resources via Brain-Tools (oauth_get_token, memory_search,
  run_<skill_name>), NICHT via Bash
- SSH zur VM-Host: `ssh aria@host` (ed25519-Key liegt im Proxy)
- Externe APIs direkt per curl mit Token aus oauth_get_token

Plus das Anti-Pattern dokumentiert ("47 Sekunden Stefan-Lebenszeit") —
ARIA soll bei jedem Bash-Reflex gegen "lokale" Brain-Resources erst
denken oder die Brain-Tool-Ebene nehmen.

README in Skills-Architektur-Sektion entsprechend ergaenzt (10 Regeln).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 23:30:50 +02:00
duffyduck fb71048dfd feat(diagnostic): Archiv-Modal mit Pagination fuer ARIA-Stream
- /api/agent-stream akzeptiert jetzt ?page=N&perPage=M zusaetzlich zu
  ?lines=N. page=1 = neueste Eintraege, hoehere Pages = aelter.
  Antwort enthaelt page/perPage/pagesTotal/total fuer Client-Nav.
- Live-View hat neuen 📜 Archiv-Button neben Leeren/Auto-Scroll.
- Modal mit PerPage-Selector (50/100/500/1000), «‹›» Navigation und
  reload-Button. Pagination-Buttons werden auf den Grenzen disabled.
- renderArchiveLine spiegelt das Live-View-Rendering (Tool-Calls in
  cyan, Results in gruen, Thinking kursiv) im Modal-Container.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 23:11:46 +02:00
duffyduck aaaf118cb7 feat: 2 neue seed_rules + Diagnostic-Persistenz fuer agent_stream + chat-backup API
Befund aus chat_backup.jsonl-Analyse heute: ARIA ist 3x auf oauth_authorize
gefallen statt oauth_get_token (Stefan musste manuell einloggen), und beim
PDF-Skill ist sie nach Stefans "Variante bitte" zu Ad-hoc-Bash-Befehlen
auf der VM gedriftet ("ich lass den Code direkt laufen") — Skill wurde
unbrauchbar. Beides genau die Antipattern die wir mit den seed_rules
abdecken wollten, nur waren die zu schwach formuliert.

seed_rules (jetzt 9 statt 7):
- oauth-reauth-reflex: bei 401 ZUERST oauth_get_token, NUR bei dessen
  Fehler oauth_authorize. Stefan zu Re-Login schicken ist das aergerlichste
  Antipattern (er sitzt im Auto, muss Handy rauskramen).
- no-skill-drift: kaputter Skill -> skill_logs + skill_update, NIEMALS
  zu Ad-hoc-Bash wechseln (Skill wird Karteileiche). Plus: "ich baue
  dir einen Skill" SAGEN ohne skill_create zu rufen ist verboten —
  Stefan checkt die Liste und verliert das Vertrauen.

agent_stream-Persistenz:
- diagnostic/server.js schreibt jeden agent_stream-Event parallel zum
  Broadcast in /shared/logs/agent_stream.jsonl (soft-cap 50 MB mit
  half-truncate beim Ueberlauf).
- Live-View laedt beim Page-Load + Sub-Tab-Switch die letzten 200
  Eintraege via /api/agent-stream. Browser-Reload / Standby verliert
  damit den Verlauf nicht mehr.

Debug-API ohne SSH:
- GET /api/chat-backup?lines=N (Default 200, Max 5000) — geparstes JSON
  der letzten N Zeilen aus chat_backup.jsonl
- GET /api/agent-stream?lines=N — gleiches fuer den persistierten Stream

README:
- Neuer Abschnitt "## Skills — Architektur" mit Skill-Layout,
  Drei-Stufen-Daten-Modell (OAuth / config_schema / Brain-Daten),
  Versionierung, Anti-Friedhof, seed_rules (alle 9 aufgelistet).
- Diagnostic-Sektion um agent_stream-Persistenz + neue Debug-Endpoints
  ergaenzt.
- Roadmap: Phase B "Skill-Architektur P0-P4" abgehakt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 23:06:56 +02:00
duffyduck 5e1cb2d26a release: bump version to 0.1.6.3 v0.1.6.3 2026-05-28 23:58:26 +02:00
duffyduck 8359500476 feat(skills): P3 config_schema + P4 Versionierung mit Rollback
P3 — Skill-Configuration
- aria-brain/skills.py: SKILL_CONFIGS_FILE (/shared/config/skill_configs.json)
  als zentrale Werte-Persistenz. _normalize_config_schema validiert die
  Schema-Felder (name/type/label/secret/description/default), CFG_<UPPER_NAME>
  ENV beim run_skill. create_skill + update_skill akzeptieren config_schema.
- agent.py: skill_set_config Brain-Tool fuer ARIA. skill_create/update um
  config_schema-Property erweitert.
- main.py: GET/POST /skills/{name}/config — secret-Werte in Antwort gemaskt.

P4 — Versionierung mit Rollback
- aria-brain/skills.py: archive_current_version archiviert nach
  versions/v_<ts>/ (ohne venv/logs). update_skill ruft das automatisch auf
  bevor strukturelle Aenderungen passieren. list_skill_versions,
  rollback_skill (mit Safety-Snapshot + automatischem venv-Rebuild),
  delete_skill_version.
- agent.py: skill_list_versions, skill_rollback Brain-Tools.
- main.py: GET /skills/{name}/versions, POST /skills/{name}/rollback,
  DELETE /skills/{name}/versions/{version_id}.

UI
- diagnostic/index.html: Skill-Detail um Config-Form (typ-spezifisch,
  Secrets als password-Input mit ***SET***-Hinweis) und Versions-Liste
  mit Rollback-/Delete-Button.
- android SkillBrowser: SkillDetailModal laedt config_schema + versions
  on-mount. Config-Form (TextInput + Switch fuer boolean), Versionen mit
  Rollback-Confirm. brainApi um SkillConfigField/SkillVersion +
  getSkillConfig/setSkillConfig/listSkillVersions/rollbackSkill/
  deleteSkillVersion erweitert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:52:46 +02:00
duffyduck 1a72f27861 feat(brain): seed_rules erweitert — BRAIN_INTERNAL_URL + Auth-Strategie
ARIA wusste bisher nichts von BRAIN_INTERNAL_URL — sie hatte den Endpoint
zwar, aber keinen Grund ihn zu nutzen. Zwei neue rule-Memories:

- "BRAIN_INTERNAL_URL ist deine Brain-Schnittstelle" — listet die
  wichtigsten Endpoints (oauth/<service>/token, memory/search,
  memory/pinned, skills/list) und macht klar dass auch Daten wie
  Stefans Standort, Memories oder andere Skills aus dem Skill heraus
  abrufbar sind.
- "Auth-Strategie fuer externe APIs" — zwingt ARIA bei jedem API-Skill
  in eine Checkliste: erst OAuth2 pruefen (Spotify, Google, GitHub,
  Reddit, …), sonst statischer Key per config_schema, NIEMALS hardcoden.

Damit kommt sie eigenstaendig auf "Spotify = OAuth2 = Brain-Endpoint"
ohne dass Stefan das jedes Mal sagen muss. Insgesamt jetzt 7 seed_rules
statt 5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:11:02 +02:00
duffyduck 32302a841e feat(brain): Skills holen OAuth-Tokens vom Brain + Anti-Friedhof-Check
P1+P2-Infrastruktur:

- Neuer Endpoint GET /oauth/{service}/token liefert aktuelles access_token
  mit Auto-Refresh (< 60s Restzeit). Skills rufen das ueber
  BRAIN_INTERNAL_URL ab statt client_secret hardzucoden.
- run_skill setzt BRAIN_INTERNAL_URL als ENV (Default http://localhost:8080,
  override via Brain-Env). Skills laufen im Brain-Container, localhost passt.
- skills.create_skill: _check_anti_graveyard rejected Versions-Suffixe
  (-v2, _v3, -new, -fixed, -old, -alt, -copy, -final, -clean) und
  Prefix-Kollisionen (z.B. spotify-aria wenn spotify schon existiert) — die
  zwei Patterns hinter dem alten Skill-Friedhof.

Tool-Description fuer skill_create um PFLICHT-VORHER-Block ergaenzt
(skill_list, kein Versionssuffix, oauth_get_token, config_schema) damit
ARIA die Regeln direkt im Schema sieht.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:04:22 +02:00
duffyduck 474e2c6c50 feat(brain): Skill-Regeln als seed_rules — idempotent auf Brain-Boot in DB
Stefans Skill-Friedhof (9 Spotify-Skills, hardcoded Credentials) hatte
keine systemische Ursache im Code, sondern im fehlenden Leitplanken-
Memory. Lösung: System-Seed-Regeln als pinned Hot Memory, mit jedem
Deploy ausgerollt.

- aria-brain/seed_rules.py: 5 rule-type Memories (skill_list-vor-create,
  no-version-suffix, update-not-recreate, no-hardcoded-credentials,
  config-schema-for-settings), source="seed", pinned=true
- Lifespan ruft seed_rules.apply() beim Brain-Start — idempotent via
  migration_key (alte Versionen werden vor dem Schreiben gelöscht)
- skill_create Tool-Description um PFLICHT-VORHER-Block ergänzt:
  skill_list-check, kein Versionssuffix, oauth_get_token bei OAuth,
  config_schema statt hardcoded Werte

Editieren = SEED_RULES-Liste anpassen, Brain neu starten. Im Gegensatz
zu brain-import/ (User-Saatgut, gitignored, manueller Diagnostic-Klick)
gehört das hier zum Brain-Code und rollt mit jedem Deploy aus.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:55:05 +02:00
duffyduck 3e0cfef63c changed docker compose rvs to 444 2026-05-25 10:31:27 +02:00
duffyduck b94626787b fix(diagnostic): chat_history-Render verträgt kaputte Bubbles + EHOSTUNREACH skipped TLS-Fallback
Zwei kleine Robustness-Verbesserungen:

1) chat_history-Handler im Frontend: jede Bubble jetzt in try/catch. Wenn
   eine Bubble bei der Render-Pipeline (escape/linkify/regex-replace) eine
   Exception wirft, brach die ganze for-Schleife ab und alle nachfolgenden
   Bubbles wurden nicht mehr in den DOM geschrieben — beim Reload sah man
   dann nur die ersten N Eintraege und Stefan dachte die letzten Antworten
   waeren weg. Jetzt: Fehler-Bubble mit "⚠ Render-Fehler" + console.error,
   restliche Bubbles laufen weiter durch.

2) Diagnostic-Server RVS-Reconnect: TLS-Fallback war auch bei reinen
   Netz-Fehlern (EHOSTUNREACH, ECONNREFUSED, ENETUNREACH, ETIMEDOUT,
   ENOTFOUND, EAI_AGAIN) gefeuert — bringt nichts weil der Server eh tot
   ist, generiert aber doppelte Reconnect-Versuche + Log-Spam. Jetzt nur
   noch bei wirklichen TLS/Handshake-Fehlern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:28:57 +02:00
duffyduck ad87c807de fix(app): App-Reconnect nach Hintergrund — Sticky-Fallback, Zombie-WS, AppState-Hook
Stefan musste seit der HTTPS-Umstellung nach jedem Hintergrund-Rueckkehr
manuell auf "Verbinden" tippen, meist 3x bis es ging. Gleiche Bug-Klasse
wie auf der Bridge davor (Sticky-Fallback), plus zwei App-spezifische
Symptome.

Drei Ursachen:

1. usingTLSFallback klebt: einmal nach onerror auf true gesetzt, blieb
   es bei allen folgenden Reconnects → App versuchte ws://...:443 gegen
   den TLS-only Caddy → HTTP 400 → endlos. Reset war NUR im manuellen
   connect(), nicht in onclose oder scheduleReconnect.
   Fix: in onclose `usingTLSFallback = false` damit der naechste
   Reconnect wieder primary (wss://) probiert.

2. Zombie-WebSocket: Android kann den TCP-Socket im Background still
   killen, der JS-State zeigt aber noch readyState === OPEN. Stefans
   manueller "Verbinden"-Klick rief connect() → "Bereits verbunden"
   No-Op statt sich neu aufzubauen.
   Fix: connect(force=true) optional, bestehendes WS-Objekt wird hart
   geschlossen (mit onclose=null gegen Doppel-Reconnect) bevor neuer
   Aufbau startet.

3. Keine aktive Reconnect-Sequence bei Foreground-Resume: App war
   abhaengig von onclose-Events die bei Zombie-WS nicht zwingend
   feuern.
   Fix: AppState-Listener in App.tsx, bei background → active
   automatischer rvs.connect(true).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:09:30 +02:00
duffyduck 72277098af release: bump version to 0.1.6.2 v0.1.6.2 2026-05-25 10:00:47 +02:00
duffyduck 80d2fe3e93 docs: README aktualisiert — FLUX, ARIA Live, OAuth + Caddy, Skill-Mgmt, Bridge-Watchdog, Bubble-Aktionen
- Diagnostic-Sektion: OAuth-Apps zeigt jetzt Spotify-Default + on-demand-
  Provider statt fixe 5er-Liste, `oauth_register_provider` als 4. Tool
  erwaehnt, Caddy/Let's-Encrypt vor RVS dokumentiert
- App-Features: Long-Press/⎘-Bubble-Aktionen + System-Share, neue Settings-
  Sektionen "🛠️ Skills" und "🔑 OAuth-Apps", Voice-Speed persistent
- Voice-Bridge-Sektion: 3-Schichten Hang-Schutz (TCP-Keepalive +
  Asyncio-Watchdog + File-Based Liveness) erlaeutert, TLS-Fallback-Reset
- Roadmap Phase B: sechs neue Eintraege fuer die letzten ~10 Commits
  (FLUX, ARIA Live + Not-Aus, OAuth-Pipeline, Skill-Mgmt-Tools,
  Bridge-Hang-Schutz, Bubble-Aktionen)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:55:28 +02:00
duffyduck b5ca3cd371 fix(bridge): TLS-Fallback klebt nicht mehr — bei Reconnect zurueck zu wss://
Bei kurzem TLS-Fehler beim ersten Connect (z.B. Caddy noch im ACME-
Setup) wechselte die Bridge auf den ws://-Fallback und blieb dort
permanent kleben. Jeder spaetere Reconnect-Versuch landete dann auf
plain ws:// gegen den TLS-only Caddy-Endpoint → HTTP 400 → erneut
Connection lost → endlos.

Fix: Bei jeder ConnectionClosed/Refused/InvalidMessage-Exception wird
using_fallback=False und current_url=self.rvs_url (= primary wss://)
zurueckgesetzt. Bridge probiert bei jedem Reconnect zuerst primary,
faellt nur einmal pro Connect-Cycle auf ws:// zurueck. Sobald TLS
verfuegbar ist, ist sie auf wss:// stabil.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:50:43 +02:00
duffyduck d939fc4ac3 feat(rvs): Caddy als TLS-Terminator + Let's Encrypt vor RVS
OAuth-Provider (Spotify, Dropbox, ...) verlangen HTTPS fuer non-localhost
Redirect-URIs. Bisher lief der RVS direkt auf einem TCP-Port ohne TLS —
Spotify hat den Callback abgewiesen.

Loesung: Caddy im selben Compose-Stack davor. Holt automatisch ein
Let's Encrypt-Zertifikat fuer PUBLIC_URL (HTTP-01 ueber Port 80),
terminiert TLS auf 443 und routet alles inkl. WebSocket-Upgrades an
den internen RVS-Container (Port 3000).

- rvs/docker-compose.yml: caddy-Service hinzu (image caddy:latest,
  command 'caddy reverse-proxy --from ${PUBLIC_URL} --to rvs:3000'),
  rvs-Service verliert ports-Block (nur intern via aria-rvs-net),
  data-Volumes fuer Caddy-ACME-State (persistent, Rate-Limit-Schutz).
- rvs/.env.example neu: dokumentiert PUBLIC_URL + DNS/Port-
  Voraussetzungen.
- rvs/.gitignore neu: .env + data/ (sonst landen die Zertifikate
  versehentlich im Repo).
- README RVS-Sektion: Setup-Schritte mit Caddy + Hinweis wie man's
  auskommentiert wenn ein eigener Reverse-Proxy davor steht.

Wer schon einen TLS-Terminator hat (nginx/Traefik): caddy-Service in
der Compose auskommentieren, rvs wieder einen ports-Block geben.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:23:28 +02:00
duffyduck 13e87fb083 feat(oauth): ARIA kann Provider selbst registrieren + Custom-Provider in Diagnostic & App
ARIA hat jetzt das META-Tool oauth_register_provider. Wenn Stefan einen
Service nutzen will, der nicht in den (auf Spotify reduzierten) Defaults
ist, kann sie auth_url/token_url/scopes/client_auth selbst eintragen —
ARIA kennt typische OAuth-Endpunkte (Dropbox, Discord, Notion, Slack,
Zoom, Trello, LinkedIn, Reddit, Twitch) aus ihrem Training. Sie traegt
NUR die URLs ein, client_id/secret bleiben Stefans Job (Diagnostic /
App-UI) — bewusste Trennung damit Credentials nicht im Chat-Verlauf
landen.

DEFAULT_PROVIDERS auf Spotify reduziert — Rest war aktuell ungenutzt
und macht den Code unnoetig "groß". ARIA registriert on-demand.

Diagnostic-UI:
- Custom-Provider zeigen auth_url/token_url/scopes als sichtbare Felder
- Defaults verstecken die Felder hinter "Default-URLs ueberschreiben
  (advanced)" damit man die Spotify-URLs nicht versehentlich loescht
- "+ Custom OAuth-Provider hinzufuegen" Button mit Prompts fuer
  Name/URLs/Scopes
- 🗑-Icon bei Custom-Services (Service komplett entfernen)

App-UI (neu fuer unterwegs):
- Settings → Sektion 🔑 "OAuth-Apps" zwischen Skills und Protokoll
- OAuthBrowser-Komponente analog zu Trigger/Skill-Browser:
  Liste mit Status, Tap → Edit-Modal mit client_id/secret +
  Advanced-Toggle fuer URLs. "Autorisieren ↗" oeffnet System-Browser
  via Linking.openURL, redirected zur RVS-Callback-Page,
  Status-Refresh nach 8s.
- "+ Custom"-Button → Full-Screen-Modal fuer Service-Anlage.
- brainApi um listOAuthServices/getOAuthApps/saveOAuthApp/
  deleteOAuthApp/authorizeOAuth/revokeOAuth erweitert.

Workflow ist jetzt: "verbinde mich mit Dropbox" → ARIA registriert
Provider → "trag client_id/secret in Settings ein" → Stefan macht das
in App oder Diagnostic → "Autorisieren ↗" → fertig.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:16:31 +02:00
duffyduck 30c1dd7473 feat(app+brain): App-Bugfixes + Skill-Mgmt-Tools + Voice-Speed persistent + Skill-Browser
App-Bugs:
- Trigger-Liste war leer: brainApi.listTriggers() cast'te {triggers: [...]}
  direkt als Array, t.sort() warf — TriggerBrowser blieb leer. Fix: unwrap.
- GPS-Tracking startete erst bei SettingsScreen-Mount, nicht beim App-Boot.
  Wenn Stefan direkt in den Chat ging, blieb GPS aus. Fix: restoreFromStorage()
  in App.tsx useEffect.
- Text in Chat-Bubbles nicht markierbar / kein Copy-Mechanismus: Bubble jetzt
  Pressable mit onLongPress + neues ⎘-Icon in Status-Row → openBubbleActions().
  Alert-Menu mit "Ganzen Text teilen" + pro extrahierte URL/Mail/Tel eine
  eigene Option. Share.share() — keine neuen Native-Deps noetig.

Brain — Skill-Mgmt:
- ARIA legte beim Skill-Umbau neue Versionen mit Suffix an (Skill-Friedhof),
  weil sie kein Update/Delete-Tool kannte. Zwei neue META_TOOLS in agent.py:
  skill_update (kann entry_code, readme, pip_packages, args, description,
  active patchen — venv wird bei pip_packages-Aenderung rebuilt) + skill_delete.
- skills.py update_skill um entry_code/readme/pip_packages erweitert,
  venv-Rebuild bei pip-Aenderung.

Bridge — Voice-Speed persistent:
- _next_speed_override war pro-Request-Override ohne Persistenz. Bei
  Diagnostic-Chats / Trigger-Replies ohne vorherigen App-Chat fiel der Speed
  auf 1.0 zurueck, ebenso nach Bridge-Restart. Jetzt: _persistent_xtts_speed
  aus voice_config.json (xttsSpeed), wird nach jedem App-chat mit speed
  autopersistiert. TTS-Generation faellt zurueck: per-Request > persistent > 1.0.

App — Feature 6:
- SkillBrowser.tsx: Liste aller Skills, Toggle aktiv/inaktiv, Detail-Modal
  mit Args-Inputs, Ausfuehren mit Live-stdout/stderr, Logs der letzten 20
  Runs, Loeschen. Settings-Sektion "Skills" (🛠️) zwischen Trigger und
  Protokoll. brainApi.listSkills/getSkill/runSkill/updateSkill/deleteSkill/
  getSkillLogs ergaenzt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 17:24:03 +02:00
duffyduck 9ed9c99b0e fix(bridge): 3-Schichten-Schutz gegen Bridge-Hangs + Chat-History in beide Boxen
Bridge hat seit 5+h still gehangen — Container Up, asyncio idle im
selectors.select(), TCP-Verbindung zum RVS ESTABLISHED, aber keine
Events mehr verarbeitet. Klassischer Fall: NAT-Tabelle/Firewall hat
die TCP-Verbindung still gekillt (kein RST), Linux-Kernel mit Default-
Keepalive (2h idle) hat's nicht gemerkt, und der ws.ping()-Future hat
im Limbo gehangen ohne Exception zu werfen.

Schicht 1 — TCP-Keepalive aufm Socket:
  SO_KEEPALIVE=1, TCP_KEEPIDLE=30s, TCP_KEEPINTVL=10s, TCP_KEEPCNT=3.
  Halb-tote Verbindungen werden in ~1 min mit ECONNRESET sichtbar statt
  nach 2h. Loest 80% der Faelle direkt.

Schicht 2 — Asyncio-Watchdog (_rvs_heartbeat_watchdog):
  Separate Coroutine parallel zu _rvs_heartbeat. Letzterer markiert
  _last_heartbeat_ok nach jedem erfolgreichen pong. Watchdog checkt
  alle 20s: > 60s stale → ws.close() + transport.close() als Notausgang.
  Schuetzt gegen ws.ping()-Limbo.

Schicht 3 — File-Based Liveness Thread:
  Separater OS-Thread (NICHT asyncio) — immun gegen asyncio-Hangs.
  Schreibt /shared/health/bridge_alive periodisch. Wenn
  _last_heartbeat_ok > 180s stale: os._exit(1), Docker restart_policy
  uebernimmt. Last-Resort wenn Schichten 1+2 versagen.

Plus: chat_history-Render nach Reload bezog nur #chat-box, nicht
#chat-box-fs (Vollbild). Wer im FS-Modus reloaded hat sah eine leere
Box statt der History. Jetzt rendert der Handler in beide Boxen
(gleicher Pattern wie addChat / addAriaFile).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:39:52 +02:00
duffyduck 1ea614c26b fix(brain): CPU-only torch — verhindert 5 GB CUDA-Bloat im Brain-Image
sentence-transformers zieht torch als Dependency, und der Default-Wheel
auf x86_64-linux ist die CUDA-Variante mit allen NVIDIA-Libs
(nvidia-cudnn, nvidia-cublas, cuda-toolkit, triton, ...). ~5 GB pro
Build-Layer, frisst die 22-GB-VM auf.

Fix: torch CPU-Wheel explizit zuerst installieren. Damit ist die
torch-Dependency erfuellt wenn sentence-transformers spaeter kommt,
und die CUDA-Libs werden nie gezogen.

Brain laeuft eh komplett auf CPU (MiniLM-Embeddings ~120 MB), GPU-Bloat
war reine Disk-Verschwendung.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:45:51 +02:00
duffyduck acaa9fc3f2 feat(oauth): generische OAuth2-Pipeline ueber RVS-Callback (Spotify/Google/GitHub/Strava/MS)
Bisher musste Stefan bei OAuth-Flows manuell den Auth-Code aus der
Browser-URL kopieren (redirect_uri war localhost). Jetzt: RVS hat einen
HTTP-Listener auf demselben Port wie der WebSocket, Provider redirecten
nach Auth zu https://{RVS_HOST}/oauth/callback/{service}, RVS broadcastet,
aria-bridge forwarded, Brain matched state + tauscht code gegen Token.
Token-Refresh laeuft automatisch.

- rvs/server.js: hybrid http.createServer + WebSocketServer{noServer}.
  Route GET /oauth/callback/{service}, broadcast oauth_callback an alle
  Raeume, schoene Dark-Mode-HTML-Antwort an den Browser (Auto-Close 4s).
- bridge/aria_bridge.py: empfaengt oauth_callback, POSTet an Brain
  /internal/oauth-callback.
- aria-brain/oauth.py: neuer Manager. Pending-Store mit state+TTL,
  Token-Exchange (Basic-Auth oder Body je nach Provider), persistente
  Speicherung in /shared/config/oauth_tokens.json (mode 0600),
  Token-Refresh wenn <60s Restzeit. Vordefinierte Configs fuer Spotify,
  Google, GitHub, Strava, Microsoft.
- aria-brain/agent.py: META-Tools oauth_authorize / oauth_get_token /
  oauth_revoke.
- aria-brain/prompts.py: System-Prompt-Block zeigt ARIA die feste
  Callback-URL als Quelle der Wahrheit + aktuelle Service-States.
- aria-brain/main.py: HTTP-Endpoints /oauth/services, /oauth/apps,
  /oauth/authorize, /oauth/{service}/revoke, /internal/oauth-callback.
- diagnostic: neue Section "OAuth-Apps". Pro Service Karte mit Status,
  client_id + client_secret (Passwort-Toggle), Speichern + Autorisieren-
  Buttons. Authorize oeffnet Provider-Auth in neuem Tab.
- docker-compose.yml: brain-env um RVS_HOST + RVS_PORT_PUBLIC + RVS_TLS
  ergaenzt (Brain braucht die Werte zum Bau der Callback-URL).
- .env.example: RVS_PORT_PUBLIC + Brain-Timeout-Vars (PROXY_TIMEOUT_SEC
  + Connect/Write/Pool) dokumentiert.
- README.md: OAuth-Pipeline + ARIA-Live-Mirror in Diagnostic-Section,
  OAuth-Apps in Einstellungen-Tab erwaehnt.
- issue.md: OAuth-Pipeline + Brain-Timeout-Fix als erledigt dokumentiert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:39:54 +02:00
duffyduck 0887674497 fix(brain): Proxy-Timeout 20min -> 24h Read, split httpx-Timeouts, Cleanup-Pfade
Brain timed bei langen Pentests nach exakt 20:00 min raus, obwohl ARIAs
Subprozess fleissig weiterarbeitete und der Live-View alles zeigte.
Root-Cause: proxy_client.py hatte einen 1200s httpx.Client-Timeout —
genau der Wert, den wir vor 5 Tagen am Proxy auf 24h hochgezogen hatten.
Schicht uebersehen.

- docker-compose.yml: PROXY_TIMEOUT_SEC=86400 als brain-env.
- proxy_client.py: httpx.Timeout split (connect=10, read=86400, write=30,
  pool=10). Toter Proxy wird in 10s erkannt, lange ARIA-Sessions duerfen
  24h laufen.
- routes.js handleNonStreamingResponse: res.on("close") + isComplete-Flag.
  Brain-Disconnect killt jetzt den Subprozess statt ihn verwaisen zu lassen.
- agent.py chat(): try/except — bei Exception nach dem User-Turn wird ein
  Assistant-Error-Marker geschrieben, damit Conversation user->assistant
  konsistent bleibt (kein Tool-Call-Loop-Fail in Folge-Calls).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 14:24:22 +02:00
duffyduck f5243b1abb fix(proxy): Idle-Watchdog statt Hard-Timeout fuer lange Agent-Sessions
Pentests u.ae. brauchen oft >20min — der bisherige 20-min Hard-Cutoff
in claude-max-api-proxy's subprocess/manager.js killte den Subprocess
mitten in der Arbeit, egal wie aktiv ARIA gerade war.

Loesung:
- Hard-Timeout via sed auf 24h hochgesetzt (Last-Resort gegen wirklich
  haengende Subprozesse).
- Eigener Idle-Watchdog in routes.js: Subprocess wird gekillt erst wenn
  ueber ARIA_IDLE_TIMEOUT_MS (Default 20min) keine message/content_delta
  Events ankommen. Jede Aktivitaet resettet den Timer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 23:02:04 +02:00
duffyduck eb5c178139 fix(proxy): tool_result Events ueber generic 'message' statt nicht-existentem 'user'
Der claude-max-api-proxy Subprocess-Manager emittiert nur 'message',
'assistant', 'content_delta', 'result', 'error', 'close', 'raw' —
KEIN 'user'. tool_result-Blocks landen daher ausschliesslich im
generischen 'message'-Event mit type==='user'. Filter darauf statt
auf einen Event-Namen der nicht existiert, sonst kam in der ARIA-Live-
View nichts an.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 21:56:17 +02:00
duffyduck 31b0bfaac1 feat(diagnostic): ARIA-Live (read-only Terminal-Mirror) + Not-Aus statt SSH-Tab
SSH-Tab raus — funktionierte eh nicht zuverlaessig und war konzeptionell
falsch. Stattdessen Live-Mirror der Claude-Code-Session:

- proxy-patches/routes.js: assistant + user Events parsed → POSTed Tool-
  Inputs (truncated 2 KB) + Tool-Results (truncated 4 KB) + Assistant-Text
  an aria-bridge:8090/internal/agent-stream. start/end Marker pro Session.
  Subprocess-Tracking (_activeSubprocesses Map) + interner Side-Channel
  auf Port 3457 mit POST /cancel-all fuer Hard-Kill.

- bridge: neuer /internal/agent-stream Endpoint pusht 1:1 als RVS
  agent_stream. cancel_request Handler nimmt optional 'hard'-Flag —
  triggert dann zusaetzlich _cancel_proxy_subprocesses() das den Proxy-
  Side-Channel ruft.

- rvs: agent_stream whitelisted.

- diagnostic: SSH-Tab → 'ARIA Live'. Monospace-Stream, farbcodiert
  (text=hell, tool_use=cyan, tool_result=gruen/rot, thinking=gelb-italic),
  Auto-Scroll, max 2000 Zeilen Backlog. Roter  Not-Aus-Button mit
  Confirm → aria_panic_stop action → diagnostic-server broadcastet
  cancel_request mit hard:true.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 09:23:13 +02:00