Commit Graph
110 Commits
Author SHA1 Message Date
duffyduckandClaude Opus 5 4589f4abc3 feat(android-agent): Einblendung geht weg, wenn Stefan etwas tut — statt nach fester Zeit
Eine feste Anzeigedauer ist immer geraten: mal zu kurz zum Lesen, mal steht sie
laengst im Weg. `seconds=0` liess die Einblendung bisher einfach stehen.

Jetzt heisst seconds=0: bleibt, bis der Nutzer etwas tut.

- Fernbedienung: der Accessibility-Dienst bekommt onKeyEvent und raeumt die
  Einblendung weg. Er gibt die Taste mit `return false` WEITER an den Film —
  es geht also kein Tastendruck verloren und der Player bleibt bedienbar.
  Dafuer brauchte der Dienst canRequestFilterKeyEvents + flagRequestFilterKeyEvents.
- Touch: FLAG_WATCH_OUTSIDE_TOUCH meldet eine Beruehrung ausserhalb. Die geht
  dank FLAG_NOT_TOUCHABLE gleichzeitig an die App darunter durch.
- Notbremse: nach 10 Minuten verschwindet sie in jedem Fall.

Bewusst NICHT der naheliegende Weg (Overlay faengt Tasten selbst ab): dafuer
muessten FLAG_NOT_FOCUSABLE und FLAG_NOT_TOUCHABLE weg, und dann nimmt die
Einblendung dem laufenden Film JEDE Eingabe, solange sie steht. Genau das soll
sie nicht.

host_notify beschreibt seconds=0 jetzt als den richtigen Wert fuer alles zum
Lesen (Wetter, Listen, Zahlen) und die feste Dauer fuer kurze Hinweise.

APK baut durch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-03 12:29:14 +02:00
duffyduckandClaude Opus 5 2c60a19ed6 feat(oauth): ARIA bekommt Zugangstokens nicht mehr zu sehen
oauth_get_token hat den access_token im Klartext als Tool-Ergebnis
zurueckgegeben. Tool-Ergebnisse sind Teil der Unterhaltung — der Token ging
damit an Anthropic und lag je nach Konto-Einstellung 30 Tage oder 5 Jahre dort.

Neu: das Brain fuehrt den API-Aufruf SELBST aus.

- `oauth_call(service, url, method, body)` haengt die Authorization an,
  schickt den Request und gibt nur die Antwort zurueck. Der Token verlaesst
  den Brain-Prozess nicht.
- `oauth_get_token` sagt nur noch, OB authentifiziert ist und wie lange noch.
  Refresh laeuft weiter automatisch.
- Skills bleiben unveraendert: die holen den Token serverseitig ueber
  {BRAIN_INTERNAL_URL}/oauth/<service>/token — der laeuft nie durch ein
  Gespraech.

Host-Sperre als Sicherheitskern: die URL kommt vom Modell, also koennte ein
Aufruf den Token sonst an einen beliebigen Server schicken. Erlaubt sind nur
Hosts, die zum Provider gehoeren (aus auth_url/token_url, plus optionale
api_hosts). Verglichen wird auf den letzten zwei Labels, NICHT per endswith —
"boesespotify.com" endet sonst auch auf "spotify.com". Dazu: nur https (sonst
ginge der Token im Klartext ueber die Leitung) und nur die ueblichen Methoden.

19 Pruefungen, darunter die Angriffsvarianten: boesespotify.com,
spotify.com.angreifer.de, accounts.spotify.com.evil.io, spotify.co, leerer
Host — alle blockiert; api.spotify.com und abschliessender Punkt erlaubt.

System-Prompt und seed_rules nachgezogen, sonst erklaert ARIA sich das alte
Verhalten selbst. Beide sagen jetzt zusaetzlich ausdruecklich: Secrets gehoeren
nie in den Chat, und ein Secret nie in einer Antwort wiederholen. Genau so ist
hier naemlich ein client_secret abgeflossen — nicht ueber ein Tool, sondern
weil es eingetippt und dann zurueckzitiert wurde.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-03 11:16:41 +02:00
duffyduckandClaude Opus 5 4c0d8ce3fe feat(agent): Bild mit Text einblenden — host_notify kann jetzt image_path
Der Android-Agent konnte schon immer ein Bild MIT Text einblenden
(OverlayView.show nimmt ein imageB64), aber ARIA kam nicht dran: in der
Parameter-Liste von host_notify fehlte das Feld einfach. Die Faehigkeit war
gebaut und unerreichbar.

ARIA gibt jetzt image_path an (ein Pfad unter /shared/uploads/, damit arbeitet
sie sowieso) und das Brain wandelt die Datei nach base64 — statt ARIA
megabyteweise base64 durch den Tool-Call schieben zu lassen. Pfad-Praefix wird
geprueft, und ueber 4 MB wird abgelehnt: fuer eine Einblendung auf einem
Fernseher ist mehr sinnlos und blaeht die WS-Nachricht auf.

Nur Android-Agenten koennen das; die Beschreibung sagt das ausdruecklich.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-03 10:34:04 +02:00
duffyduckandClaude Opus 5 12b8362d26 fix(trigger): Antwort landet im Projekt des Timers — und der Zustand luegt nicht mehr
Zwei Fehler, die zusammen wie "der Timer hat nicht gefeuert" aussahen.

1) Trigger hatten KEIN Projekt. Beim Feuern ging die Antwort ohne projectId
   durch _process_core_response — also immer in den Hauptchat, auch wenn der
   Timer mitten in einem Projekt-Thread gestellt wurde. Im Projekt blieb es
   still, und es sah aus wie "nicht gefeuert". Jetzt wird das Projekt des
   Requests mit angelegt, der getriggerte Turn laeuft darin, und die Antwort
   kommt dort zurueck.

2) trigger_list zeigte nur "aktiv" / "DEAKTIVIERT" und verschwieg fire_count
   und last_fired_at. Ein One-Shot-Timer wird NACH dem Feuern automatisch
   deaktiviert — "hat sauber gefeuert" und "war nie an" sahen damit identisch
   aus. ARIA hat genau das falsch gelesen und berichtet, sie habe vergessen den
   Timer zu aktivieren, obwohl er puenktlich gefeuert hatte. Die Ausgabe nennt
   jetzt ausdruecklich "SCHON GEFEUERT (Nx, zuletzt <Zeit>)" plus den Kontext,
   in dem die Antwort landet.

Nachgewiesen am Fall flugwetter-whv-freitag-0900: gefeuert 07:00:05 UTC
(= 09:00 lokal), Antwort 46s spaeter, fire_count=1 — aber active=false im
Manifest und die Antwort im Hauptchat statt im Projekt-Thread.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-03 10:17:35 +02:00
duffyduckandClaude Opus 4.8 dc77933014 fix(jellyfin): Erfolgsmeldung sagt ausdruecklich, dass kein Neustart noetig war
Nachgeprueft: der Selbstheilungs-Fix lief korrekt — die Wiedergabe startete auf
Anhieb (Nachkontrolle positiv), es gab KEINEN App-Neustart (kein host_app_launch
in der Agent-Historie). ARIA behauptete trotzdem 'Session war eingeschlafen, ich
hab Jellyfin neu gestartet' — nacherzaehlt aus dem vorherigen Turn, wo das
tatsaechlich noetig war.

Die Erfolgsmeldung stellt jetzt klar, dass nichts repariert werden musste, damit
das Modell die alte Panne nicht weitererzaehlt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-30 22:01:54 +02:00
duffyduckandClaude Opus 4.8 6a6b320f37 fix(jellyfin): eingeschlafene Sitzung erkennen und selbst neu aufbauen
Beim zweiten Film ging nichts mehr ('Session abgelaufen') — ARIA musste Jellyfin
von Hand neu starten. Ursache: Jellyfin listet Clients noch, wenn die App laengst
weg ist, und quittiert den Play-Befehl auch dann mit Erfolg. Es lief nur nichts.

- /Sessions wird jetzt mit activeWithinSeconds gefiltert (Play + devices) —
  Karteileichen tauchen gar nicht erst als Ziel auf.
- _jellyfin_playing(): nach dem Play-Befehl wird nachgesehen, ob NowPlayingItem
  wirklich gesetzt ist (bis ~6s). Ohne das meldet man faelschlich Erfolg.
- Laeuft nichts und ist 'host' gesetzt: Jellyfin wird ueber den Host-Agenten neu
  gestartet (host_app_launch), frische Sitzung geholt, einmal nachgelegt — also
  genau der Handgriff, den ARIA vorher manuell machen musste. Ohne 'host' gibt es
  eine klare Handlungsanweisung statt eines stillen Fehlschlags.
- Tool-Beschreibung draengt auf 'host', damit die Selbstheilung greift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-30 21:50:43 +02:00
duffyduckandClaude Opus 4.8 8f9e3e5c79 feat: Jellyfin-Integration — ARIA steuert die Wiedergabe ueber die API
Statt sich durch die Fire-TV-Oberflaeche zu klicken (fehleranfaellig: ARIA ist am
Titel vorbeigescrollt und auf Home gelandet) spricht ARIA den Jellyfin-Server
direkt an: suchen -> Sitzung des Abspielgeraets finden -> abspielen.

- Diagnostic (Einstellungen > Integrationen): Jellyfin-Abschnitt mit MEHREREN
  Servern (+-Button; eigener, vom Kumpel ...), einer davon Standard. Pro Server
  Name/URL/API-Key + Test-Knopf (prueft /System/Info, meldet Name und Version).
  Gespeichert in /shared/config/jellyfin.json mit Datei-Rechten 0600 (API-Keys).
- Brain: jellyfin_search / jellyfin_devices / jellyfin_play / jellyfin_control.
  Serverwahl per Name oder Standard. 'resume' ist echt implementiert (liest
  PlaybackPositionTicks und setzt startPositionTicks) statt nur zu existieren.
  Fehlertexte sind handlungsleitend: 401 -> API-Key pruefen; kein Geraet -> die
  Jellyfin-App muss offen sein, sonst erst host_app_launch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-30 03:23:30 +02:00
duffyduckandClaude Opus 4.8 c131820be0 feat(android-agent): Audio-Fokus — ARIA redet nicht mehr ueber den laufenden Film
Bisher wurde KEIN Audio-Fokus angefordert: Android mischt dann beide Tonspuren,
d.h. Netflix/Jellyfin/Spotify liefen in voller Lautstaerke weiter und ARIA war
kaum zu verstehen — und beim Zuhoeren nahm das Mikrofon den Filmton mit auf.

AudioFocusHelper mit drei Modi:
- speak/notify: AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ('duck', Standard) -> Medien
  werden leiser und danach automatisch wieder lauter. Per focus-Param auch
  'pause' (Medien anhalten) oder 'none'.
- listen: AUDIOFOCUS_GAIN_TRANSIENT ('pause') -> Medien halten an, solange das
  Mikrofon offen ist; danach laufen sie weiter.
- Fokus wird in allen Pfaden zurueckgegeben (Completion, Fehler, stop, shutdown),
  sonst blieben die Medien dauerhaft leise.
Brain: focus-Parameter in host_notify/host_speak. Android-Version 0.0.1.4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-30 02:46:14 +02:00
duffyduckandClaude Opus 4.8 586a2bf134 feat: Brain-Tools fuer Overlay/Sprache + Rechte automatisch per ADB erteilen
1) ARIA hatte KEIN Werkzeug fuer die Einblendung — der Agent kann notify/speak/
   volume laengst, aber im Brain fehlten die Tools. Sie hat deshalb improvisiert
   ('cmd notification post', gibt es auf Android 9 nicht) und ist gescheitert.
   Neu: host_notify (Overlay/Sprache, mode ueberschreibbar), host_speak,
   host_volume — mit dem ausdruecklichen Hinweis, es NICHT ueber adb zu versuchen.
2) _adb_grant_permissions: appops PROJECT_MEDIA (Screenshots ohne Dialog) +
   SYSTEM_ALERT_WINDOW (Overlay) + pm grant RECORD_AUDIO (Mikro) werden beim
   Einrichten automatisch gesetzt — kein Zusammenklicken mit der Fernbedienung
   mehr. Best effort; was scheitert, wird als 'manuell noetig' gemeldet. Neue
   Aktion adb.permissions setzt sie einzeln nach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-30 02:37:20 +02:00
duffyduckandClaude Opus 4.8 8301342728 feat(satellite): ADB-Rollout wartet auf die Geraete-Bestaetigung (ein Klick genuegt)
Bisher: 1. Klick -> 'unauthorized' (Fehler), am Fire TV bestaetigen, 2. Klick ->
klappt. Jetzt wartet der Satellit nach dem ersten connect bis zu ADB_AUTH_WAIT_SEC
(90s) auf die Autorisierung und installiert danach automatisch weiter — der
Dialog auf dem Fernseher erscheint ja erst DURCH das connect.

- _adb_wait_authorized(): pollt adb devices alle 2s und verbindet neu (nach dem
  Bestaetigen wechselt der Zustand oft erst mit frischem connect auf 'device').
- adb.install/adb.connect nutzen es; scheitert es doch, nennt die Meldung Wartezeit
  und Zustand statt nur 'unauthorized'.
- Brain-Timeout fuer adb.* auf 320s (Warten + Download + Installation).
- Diagnostic: Hinweis erscheint SOFORT beim Klick ('Jetzt auf den Bildschirm des
  Geraets schauen'), nicht erst als Fehler hinterher.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-30 01:34:18 +02:00
duffyduckandClaude Opus 4.8 47735642f2 feat(satellite): APK-Rollout per ADB — Fire TV & Android aus der Ferne bespielen
Der Satellit kann Android-Geraete im LAN erkennen und die Agent-APK ausrollen,
statt sie von Hand aufs Geraet zu kopieren.

- satellite.py: adb.devices/connect/install/shell. _apk_fetch laedt die APK per
  HTTP (gecached in /data/apk, sha256 optional) statt sie durchs RVS zu schicken —
  der RVS ist ein BROADCAST-Relay (jede base64-APK ginge an alle Clients, und das
  Satelliten-max_size liegt bei 8 MB). adb.install ist selbstheilend (connect ->
  install -r) und uebersetzt die typischen Fehler in klare Ansagen (unauthorized,
  Signatur-Mismatch, Speicher voll). Imports lokal in den Funktionen (Hausmuster).
- Discovery: mDNS-Typen _amzn-wplay/_adb-tls-connect/_androidtvremote2, _mdns_kind
  -> firetv/android-adb/androidtv, plus _enrich_adb (TCP-Probe 5555) -> adbCapable.
- Dockerfile: adb; compose: HOME=/data (ADB-Key persistent, sonst fragt das Geraet
  nach jedem Rebuild neu) + ANDROID_ADB_SERVER_PORT=5038 (Host-Kollision).
- .env.example: neue Aktionen + Warnung, dass die Allowlist den Code-Default
  ueberschreibt (sonst lehnt ein bestehender Satellit adb.* ab).
- Brain: adb.* in der satellite_command-Beschreibung; WICHTIG Timeout 200s fuer
  adb.*/ssh.exec — der Bridge-Default von 20s haette jede Installation abgewuergt.
- Diagnostic: sat_command (Browser->RVS) und sat_result (RVS->Browser) gab es
  bisher GAR NICHT; beide ergaenzt. 📦-Button je ADB-faehigem Geraet + Rollout-
  Modal mit Status/Log, ADB-Tag in der Geraeteliste.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-29 19:17:53 +02:00
duffyduckandClaude Opus 4.8 176997a800 feat(host-agent): GUI-Steuerung fuer Desktop (capability-gated)
Desktop-Agenten (Win/Linux/Mac) koennen jetzt bedienen, nicht nur sehen — mit
DENSELBEN Actions/Brain-Tools wie Android (ui_tap/ui_text/ui_key/ui_swipe/
app_launch), ein Werkzeugset fuer Handy und Rechner.

- host_agent.py: _gui_input_method() erkennt X11(xdotool)/Wayland(ydotool)/
  macOS(osascript+cliclick)/Windows(PowerShell). CAPS werden dynamisch erweitert
  -> reiner Terminal-Server (kein DISPLAY) meldet KEINE ui_*-Caps. _do_ui_tap/
  text/key/swipe + _do_app_launch je Methode; info liefert 'gui'.
- Brain: Tool-Beschreibungen decken Desktop ab (Koordinaten aus dem Screenshot,
  kein ui_dump; button/double bei tap; command bei app_launch); _UI_ACTIONS
  reicht die neuen Params durch.
- README: Fähigkeiten/Plattform-Tabelle + Helfer je OS + HiDPI-Hinweis.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-25 14:30:38 +02:00
duffyduckandClaude Opus 4.8 f4b7148cf9 feat(diagnostic): Agent-Historie pro Agent — Akkordeon je Nachricht
Jede Host-Aktion wird als Historie-Eintrag festgehalten und in der Diagnostic
pro User-Nachricht gruppiert aufklappbar dargestellt (mit Screenshot-Thumbnails).

- Brain: trace_id+msg pro chat(); _dispatch_host als Wrapper um _dispatch_host_inner
  emittiert je Aktion einen Eintrag an POST /internal/agent-history (Datei-Pfad aus
  Screenshot-Text erkannt).
- Bridge: /internal/agent-history persistiert nach /shared/config/agent_history/
  <hostId>.jsonl (Ringpuffer 200) + broadcastet agent_history; agent_history_query
  -> agent_history_list.
- RVS: agent_history/_query/_list in ALLOWED_TYPES (sonst still verworfen).
- Diagnostic-Server: relay browser<->RVS + /uploads/<file> Bild-Endpoint (nur
  /shared/uploads, kein Traversal).
- index.html: 'Historie'-Button je Host, Modal mit Akkordeon (Gruppe=Nachricht,
  aufklappbar, Screenshot-Thumbs), Live-Refresh bei agent_history.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-25 13:32:40 +02:00
duffyduckandClaude Opus 4.8 2038b676e6 feat(android-agent): Meilenstein 3 — steuern (tap/text/swipe/key/app_launch)
Bedienungshilfe kann jetzt bedienen: canPerformGestures + dispatchGesture (tap/
swipe), ACTION_SET_TEXT (text), performGlobalAction (back/home/recents/notif).
app_launch via getLaunchIntentForPackage (+ Label-Suche, QUERY_ALL_PACKAGES).
caps erweitert. Brain-Tools host_ui_tap/_text/_swipe/_key/host_app_launch ->
generischer _UI_ACTIONS-Dispatch. Tap-Koordinaten = ui_dump-x/y (echte Pixel,
nicht der skalierte Screenshot). Damit voller Ablauf sehen->steuern. Version 0.0.0.6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 21:53:02 +02:00
duffyduckandClaude Opus 4.8 be202b1fa1 feat(android-agent): Meilenstein 2 — sehen (Screenshot + ui_dump)
Screenshot via MediaProjection (ScreenCapturer, gleicher {format,bytes,base64}-
Vertrag wie der Desktop-Agent -> host_screenshot, inkl. Vision). UI-Baum via
AriaAccessibilityService (nur lesend) -> neues Brain-Tool host_ui_dump. Freigabe
einmalig in der App: 'Bildschirm-Zugriff erlauben' + 'Bedienungshilfe oeffnen'.
caps = [info, screenshot, ui_dump]. targetSdk 33 -> keine mediaProjection-FGS-
Typpflicht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 20:22:52 +02:00
duffyduckandClaude Opus 4.8 6b5d847615 fix(app+brain): leere Bubble bei Tool-Loop/Fehler beheben + Limit hoch
Symptom: ARIA laeuft ins Tool-Loop-Limit (host_agent-Exploration), postet die
Meldung, aber die App zeigt eine LEERE Bubble.

Ursache: stripSystemHints (App) entfernt fuehrende [..]-Bloecke (fuer Hinweise
wie '[Kontext:..] Hallo'). Die Tool-Loop-/Fehler-Meldung ist KOMPLETT ein
[..]-Block -> restlos gestrippt -> leer.

Fixes:
- App (ChatScreen): stripSystemHints zeigt das Original, wenn nach dem Strippen
  nichts uebrig bleibt -> Fehler-/Meta-Meldungen (auch '[Fehler: ...]') sind
  wieder sichtbar statt leer.
- Brain: MAX_TOOL_ITERATIONS 8 -> 20 (env-tunebar) — 8 war zu knapp fuer echte
  agentische Arbeit (Host-Exploration); ARIA brach mittendrin ab.
- Brain: Loop-Limit-Meldung ARIA-stimmig + handlungsleitend statt technischer
  Marker (liest sich natuerlich in der Bubble).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 18:47:58 +02:00
duffyduckandClaude Opus 4.8 b73e89e9bb feat(host-agent): ARIA SIEHT den Screenshot (via eigenes Read-Tool)
Der Proxy reduziert Multimodal-Content zu Text (openai-to-cli), ARIA laeuft auf
der Claude-Code-CLI. Aber: der Proxy-Container hat /shared gemountet und ARIA
laeuft mit vollen Tools (--dangerously-skip-permissions) -> sie kann die unter
/shared/uploads/ gespeicherte Screenshot-PNG mit ihrem eigenen Read-Tool
oeffnen, das Claude Code nativ als Bild rendert. Damit SIEHT sie den Bildschirm
und kann 'was ist auf meinem Bildschirm' beantworten — ganz ohne Proxy-/Vision-
Umbau. host_screenshot-Result + Tool-Beschreibung weisen sie an, den Pfad zu
Read'en (sehen) UND als [FILE:]-Marker zu setzen (im Chat zeigen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 16:20:08 +02:00
duffyduckandClaude Opus 4.8 2b4428d823 feat(host-agent): host_screenshot erscheint inline im Chat (FILE-Marker)
Statt den Screenshot nur nach /shared/host-screenshots zu legen und den Pfad
zu nennen, speichert host_screenshot das PNG jetzt nach /shared/uploads/ und
liefert ARIA die Anweisung, den Pfad als [FILE: ...]-Marker in die Antwort zu
schreiben — exakt das flux_generate-Muster. Die Bridge erkennt den Marker,
broadcastet file_from_aria, und App/Diagnostic zeigen das Bild inline im Chat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 16:16:15 +02:00
duffyduckandClaude Opus 4.8 c7761284c7 feat(host-agent): Backend-Anbindung — RVS + Bridge-Registry + Brain-Tools (Phase B-D)
Damit kann ARIA Host-Agenten tatsaechlich nutzen (Ende-zu-Ende):

- RVS: host_hello/host_ping/host_command/host_result in ALLOWED_TYPES.
- Bridge: _hosts-Registry (host_hello/host_ping/host_result), _host_list,
  _host_request (requestId->Future wie Satelliten), HTTP-Endpoints
  /internal/host-list und /internal/host (op via action).
- Brain: Tools host_list/host_exec/host_read/host_write/host_info/
  host_screenshot + _dispatch_host (ruft /internal/host*). host_screenshot
  speichert das PNG unter /shared/host-screenshots. host_exec kann sudo=true.

Naechste (optionale) Phase E: Host-Agenten in der Diagnostic-Flotte anzeigen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 16:11:06 +02:00
duffyduckandClaude Opus 4.8 a99ea32664 feat(satellite): ssh.exec — ARIA fuehrt Kommandos per SSH auf Geraeten aus
Neuer Credential-Typ 'ssh' (Benutzer + Passwort ODER privater Key, Port) und
Aktion ssh.exec (params: ip, cmd). paramiko-Client; grosse Ausgaben werden wie
bei http.get gefenstert (contains/offset/max_chars gemeinsam via _window_text),
Antwort mit exit_code + stdout + stderr. Auth kommt aus dem Credential-Store,
ARIA muss keine Passwoerter mitgeben.

- satellite: _do_ssh + _ssh_load_key (RSA/Ed25519/ECDSA/DSS aus String),
  ssh.exec in _control + Allowlist, 'ssh' in beide Cred-Typ-Listen; paramiko
  in requirements; .env.example ergaenzt.
- diagnostic: SSH-Sektion im Credentials-Modal (User/Port/Passwort/Key) +
  Save-Logik (User + Passwort|Key).
- brain: satellite_command-Tool um ssh.exec erweitert.

Laeuft ueber den bestehenden sat_command/sat_result-Pfad — keine RVS-Aenderung.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-24 16:00:54 +02:00
duffyduckandClaude Opus 4.8 9f741df10e feat(satellite): Credential-Store + snmp.ports/info + FritzBox-Reader
Baut SNMP zur generellen Geraete-Auskunft aus und macht Zugangsdaten pro Geraet
hinterlegbar, damit der Satellit tiefer lesen kann.

Satellit:
- Verschluesselter Credential-Store (Fernet) pro IP im Bind-Volume ./data:
  SNMP (community/v2c oder v3 user/auth/priv), HTTP-Basic, FritzBox-Login.
  Neue Messages sat_creds_set/list/delete + *_result; Secrets werden NIE
  zurueckgeliefert (list gibt nur Typen). snmp.*/http/fritzbox nutzen die
  Creds automatisch (params haben Vorrang).
- snmp.ports: ifTable -> aktive/freie Ports, Linkspeed ('sind noch Ports frei').
- snmp.info: sysName/Descr + Entity-MIB Modell/Serial/Firmware (installierte
  Version; Update-Check ist Hersteller-Sache, kein SNMP).
- fritzbox.info/hosts: TR-064 (SOAP+Digest) — Verbindung/Datenrate/externe IP
  bzw. verbundene Geraete. Bewusst als Reader, weil TR-064 fuer on-the-fly
  http.post zu fummelig ist.
- CONTROL_ALLOWLIST-Default + .env.example erweitert; data/ ge-gitignore-t.

RVS: sat_creds_* in ALLOWED_TYPES (sonst verworfen).

Diagnostic: pro entdecktem Geraet ein Schluessel-Button -> Modal (SNMP v2c/v3,
HTTP-Basic, FritzBox); Speichern/Loeschen via RVS an den Satelliten; gesetzte
Typen werden pro Geraet angezeigt (🔑 ✓). Server relayed die Cred-Messages.

Brain: satellite_command-Tool um snmp.ports/info + fritzbox.* erweitert; ARIA
muss keine Passwoerter mitgeben (Satellit nutzt den Store).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 17:16:06 +02:00
duffyduckandClaude Opus 4.8 22f5f5c954 feat(satellite): SNMP-Anreicherung bei Discovery — Switches/Router/APs/NAS geben Infos preis
SNMP soll nicht nur Drucker abfragen: jedes beim Scan entdeckte Geraet mit IP
wird jetzt kurz nach seiner SNMP-System-Group (RFC 1213) gefragt. Antwortet es,
haengt der Satellit ein 'snmp'-Feld an (sysName/sysDescr/sysContact/sysLocation/
sysUpTime) und leitet einen praeziseren 'type' aus sysDescr ab (switch/router/
access-point/nas/printer/ups). Aus einer nackten ARP-IP wird so ein benanntes,
klassifiziertes Geraet im Inventar, das ARIA via satellite_devices sieht.

- parallel (Semaphore, Default 16) mit kurzem Timeout (-t1 -r0, 2s) -> Nicht-
  SNMP-Hosts fallen sofort raus, der Scan bleibt flott.
- SNMP_DISCOVERY (Default true) schaltet es ab; Concurrency/Timeout per env.
- satellite_devices-Tool: ARIA weiss jetzt vom snmp-Feld + genaueren type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 17:03:50 +02:00
duffyduckandClaude Opus 4.8 e3f6b3626d feat(satellite): SNMP-Aktionen (snmp.get/walk/printer) — Drucker-Tinte zuverlaessig
HTML-Scrapen der Drucker-Statusseite ist fragil (grosse Seite, Werte teils in
JS/Grafik). SNMP/Printer-MIB liefert die Fuellstaende als saubere Zahlen.

- Dockerfile: net-snmp-CLI (Paket 'snmp') installiert.
- satellite.py: Aktionen snmp.get / snmp.walk (generisch, params ip+oid) und
  snmp.printer (Komfort: liest prtMarkerSupplies aus der Printer-MIB und rechnet
  je Patrone name+level+max+percent). net-snmp via subprocess, numerische OIDs
  (keine MIB-Files noetig), -t2 -r1 + Timeout = schnell scheitern statt haengen.
  RFC-Sonderwerte (-1/-2 unbekannt, -3 vorhanden) behandelt.
- CONTROL_ALLOWLIST-Default + .env.example um snmp.* erweitert, SNMP_*-Envs
  dokumentiert.
- agent.py + seed_rules.py: satellite_command-Tool + Drucker-Beispiel — snmp.
  printer ist jetzt der BEVORZUGTE Weg fuer Tinte, http.get nur Fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 17:01:08 +02:00
duffyduckandClaude Opus 4.8 d6cca88455 fix(satellite): http.get liefert ganze Statusseiten + contains-Filter, kein Scan-Overhead
ARIA erreichte den Drucker per Satellit, bekam aber nur die ersten 2000
Zeichen (r.text[:2000]) — die Tintenbalken stehen weiter unten und fielen
weg; ein Offset-Versuch wurde ignoriert (nicht unterstuetzt). Zudem lief vor
JEDEM sat_command ein voller LAN-Scan, auch fuer http.get, das die URL direkt
nutzt -> sehr lange Antwortzeiten.

_do_http:
- Body-Limit env-konfigurierbar (HTTP_MAX_CHARS Default 20000, harter Deckel
  HTTP_MAX_CHARS_HARD 200000) statt fixer 2000.
- params.offset + params.max_chars zum Paginieren.
- params.contains (String/Liste): nur Zeilen mit einem der Begriffe -> zieht
  aus einer grossen Statusseite gezielt die Tintenwerte, ohne Paging.
- Antwort meldet total_chars/returned_chars/offset/truncated/filtered.
- HTTP_TIMEOUT_SEC (Default 10s) statt fixer 6s.

sat_command: LAN-Scan nur noch bei dial.launch oder wenn ein device-Ref
mitkommt; http.get/http.post/wol nutzen die gecachte Liste -> deutlich schneller.

agent.py + seed_rules.py: satellite_command-Tool + Drucker-Beispiel um
contains/offset/max_chars ergaenzt, damit ARIA den Filter nutzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 12:09:52 +02:00
duffyduckandClaude Opus 4.8 f1c042c60a fix(satellite): ARIA nutzt Satelliten fuer Geraete in fremden Netzen — statt zu fabulieren
Symptom: Frage nach dem Patronenstand des Heim-Druckers -> ARIA antwortet
"da komm ich nicht ran", obwohl im Heimnetz ein Satellit online ist, der den
Drucker findet UND http.get kann. Sie zieht Drucker-Fakten aus dem Gedaechtnis
und gibt auf, ohne den Satelliten zu nutzen.

Ursachen + Fixes:
- Claude-Pfad: die Seed-Regel triggerte nur auf Formulierungen wie "im Buero"/
  "im Netz X". Eine schlichte Geraetefrage (Patronenstand) griff sie nicht.
  -> Regel umformuliert: IMMER wenn ein Geraet/Host in einem Netz liegt, auf
  dem ARIA nicht direkt sitzt (Zuhause/Buero/private IP), ZUERST satellite_list
  pruefen; NIEMALS "erreiche ich nicht" sagen, bevor satellite_list lief.
  Drucker-http.get-Beispiel ergaenzt.
- satellite_list-Tool-Beschreibung als dauerhaften Anker verstaerkt (gilt auch,
  wenn ein Seed mal driftet).
- Lokales Tier hatte gar keine Satelliten-Tools (_LOCAL_TOOL_NAMES) -> konnte
  strukturell kein Geraet im fremden Netz erreichen und fabulierte. satellite_
  list/devices/command ergaenzt, damit auch das lokale Qwen Satelliten nutzt.

Greift beim naechsten Brain-Neustart (seed_rules.apply im Lifespan, idempotent
per migration_key).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 11:35:35 +02:00
duffyduckandClaude Opus 4.8 254031bb7d refactor: "Gamebox" ueberall durch "AI-Box(en)" ersetzen
Rein terminologisch: die feste "Gamebox" ist konzeptuell zu beliebig vielen
AI-Boxen geworden (Multi-Node-Compute-Fleet). Wort-Sweep ueber Code-Kommentare,
Strings, README, docs, .env.example, CHANGELOG — keine Identifier/Keys betroffen
(Gamebox kam nirgends in Variablennamen vor). Casing erhalten: Gamebox→AI-Box,
gamebox→ai-box. NODE_NAME-Default gamebox→ai-box, Instanz-IDs f5tts@ai-box.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-19 00:34:21 +02:00
duffyduckandClaude Opus 4.8 4f68a2a411 fix(voice): 'gute Nacht' loest nicht mehr faelschlich Ohr-Aus aus
Regression aus dem Schlaf-Idiom-Commit (70fbc16): 'gute nacht' im wake-off-Regex
matchte jeden Verabschiedungs-Gruss ('Ich wünsche dir eine gute Nacht') → Ohr
ging aus, Stefan hing in einer 'Ohr aus'-Schleife fest. 'gute nacht'/'schlaf
gut' sind Gruesse, kein Ohr-Aus-Befehl → raus aus dem Regex. Nur klare Imperative
an ARIA bleiben ('geh/leg dich schlafen', 'leg dich aufs Ohr/hin'); mehrdeutige
Faelle faengt ARIA per ear_control aus dem Kontext ab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 22:43:18 +02:00
duffyduckandClaude Opus 4.8 5689474c03 feat(voice): ear_control-Tool — ARIA versteht Ohr-Aus/-An in jeder Formulierung
Statt jede Schlaf-/Aus-Formulierung per Regex aufzuzählen: ein Tool ear_control
(action off|on), das ARIA aufruft, wenn Stefan sinngemäß "hör auf/wieder zu
zuhören" sagt — egal wie ("leg dich schlafen", "ich geh ins Bett, du kannst
aus", "mach Pause vom Zuhören"). Tools ruft ARIA zuverlässig (im Gegensatz zu
den [[..]]-Markern, die sie oft ignoriert).

Nutzt die komplette bestehende Plumbing: der Tool-Call setzt _ear_control →
answered_by wird "wake-off"/"wake-on" → main.py setzt wake_off/wake_on → Bridge
→ App stoppt/startet Listener. Reiner Steuerbefehl (still, kein Weiterlauschen).
Der deterministische Regex-Detektor bleibt als schnelles Netz für die
Standard-Sätze (instant, kein Claude-Call). Prompt-Hinweis in der Voice-Flow-
Sektion ergänzt. Brain-only — kein neues APK nötig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 22:24:04 +02:00
duffyduckandClaude Opus 4.8 70fbc164f4 fix(voice): wake-off erkennt mehr Schlaf-Idiome
"leg dich schlafen" / "leg dich aufs Ohr" / "leg dich hin" / "gute Nacht" /
"schlaf gut" zusätzlich zu "geh schlafen". Sofort-Netz (instant, kein Claude-
Call). Der robustere Weg (ear_control-Tool, ARIA versteht jede Formulierung)
folgt separat, wenn Stefan will.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 22:19:29 +02:00
duffyduckandClaude Opus 4.8 31444f1442 feat(voice): Wake-Word per Befehl wieder anschalten (Gegenstück zu wake_off)
"Wake-Word an" / "mach das Ohr an" / "hör wieder zu" / "wach auf" → App startet
den Listener wieder. Geht per Text-Nachricht ODER manuellem Aufnahme-Button —
beide laufen unabhängig vom Wake-Word-Listener, also funktioniert das Wieder-An
auch wenn das Ohr gerade taub ist (nur nicht per "Computer", das hört ja nicht).

Symmetrisch zu wake_off: Detektor _user_wants_wake_on → wake_on durch ChatOut →
Bridge → App löst wakeWordService.start() + setWakeWordActive(true) aus. An/Aus
kollidieren nicht (12 Fälle getestet). Damit: Ohr per Befehl ein UND aus, plus
weiterhin der Ohr-Button.

Fix nebenbei: gerades " in den Reply-Strings (hätte den Brain-Start gecrasht) →
einfache Anführungszeichen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 20:33:16 +02:00
duffyduckandClaude Opus 4.8 5398640078 feat(voice): Wake-Word per Sprachbefehl ausschalten
"Computer, Wake-Word aus" / "mach das Ohr aus" / "hör auf zuzuhören" / "geh
schlafen" → die App stoppt den Wake-Word-Listener KOMPLETT (Mikro frei, echte
Ruhe). Wieder-An nur über den Ohr-Button (bewusst, weil dann taub — Voice-
Wieder-An ist physikalisch unmöglich, wenn nichts mehr hört).

Deterministischer Detektor _user_wants_wake_off im Brain (kein LLM-Call nötig,
still). Signal fließt als wake_off durch ChatOut → Bridge → App-Payload; die App
löst denselben Pfad wie toggleWakeWord-off aus (cancelRecording +
wakeWordService.stop() + setWakeWordActive(false)). Detektor gegen 13 Positiv-
+ 9 Negativ-Fällen verifiziert (fängt nicht 'Licht aus' / 'Konversation Ende').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 20:27:48 +02:00
duffyduckandClaude Opus 4.8 a15b8ce51a feat(brain): Titel-Index fürs kalte Gedächtnis + 'beendet' erkennen
Punkt 4B: jeder System-Prompt bekommt einen kompakten Titel-Index der bewusst
gespeicherten Nachschlage-Memories (Zugangsdaten, Infra, Projekte) — ARIA
sieht WAS sie hat und holt es via memory_search, statt Stefan nach etwas zu
fragen, das schon da ist (Git-Credentials-Vorfall). Auto-distillierte Fakten
+ Conversation-Logs sind ausgefiltert → billig.

Punkt 1: _CONV_END_VERB erkennt jetzt auch 'beendet' (beend\w*) und 'stoppe'
(stop\w*) — 'Konversation beendet' schloss vorher das Mikro nicht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 11:19:06 +02:00
duffyduckandClaude Opus 4.8 7249e2cf61 feat(voice): "Konversation fortführen" hält offen — auch bei Fast-Path-Befehl
Stefans Repro: "spiel Spotify auf Smartphone ab ABER Konversation fortführen" →
die Kette endete trotzdem. Grund: "spiel spotify" trifft den Fast-Path (Regex),
der den Satz-Rest nicht versteht → converse=false aus dem Skill-Manifest; ARIAs
[[WEITER]]-Marker lebt in Claude, der wurde uebersprungen.

Fix: _user_wants_conversation_continue() — Gegenstueck zu _user_wants_conversation_
end(). Erkennt "Konversation/Gespraech fortfuehren/weiterfuehren/offen halten/nicht
beenden", "weiter reden/sprechen" etc. und erzwingt converse=true an ALLEN Returns
(fast-path/local/claude), auch wenn das Manifest false sagt. [[ENDE]]/_wants_end hat
Vorrang bei Widerspruch. Getestet inkl. Negativfaelle (sofort/spotify ab).

Deploy: Brain-Neustart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 01:35:49 +02:00
duffyduckandClaude Opus 4.8 0344f249b8 feat(voice): "Konversation Ende" deterministisch — nicht nur ARIAs [[ENDE]]-Marker
Beobachtung Stefan: ARIA haelt die Konversation offen (Default, korrekt), aber
auf "Konversation Ende" hin schloss sie NICHT — sie hat den [[ENDE]]-Marker nicht
gesetzt. Das aufs LLM allein zu verlassen ist zu unzuverlaessig fuer einen festen
Trigger.

Fix: _user_wants_conversation_end() erkennt explizite Beenden-Phrasen im User-Text
(konversation/gespraech/befehlskette + ende/beenden/aus/stop/schluss, beide
Reihenfolgen, ein Satzteil) und erzwingt converse=false an ALLEN drei Returns
(fast-path/local/claude). ARIA beantwortet eine evtl. enthaltene Frage noch normal
(speak bleibt), danach zurueck aufs Wake-Word. "befehls?kette" statt bare "kette",
damit "Lieferkette" u.ae. nicht faelschlich matchen (per Test abgesichert).

Deploy: Brain-Neustart (nicht waehrend ARIA arbeitet).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 14:25:09 +02:00
duffyduckandClaude Opus 4.8 23d3188205 feat(voice): ARIA erkennt Gespraechs-Phase selbst (Befehl/Frage, Kette/Ende)
Voice-First: ARIA deklariert die Phase aus dem Text per Inline-Marker — dasselbe
Muster wie [[AWAIT]], die Marker werden vor Anzeige/TTS/History entfernt. Loest:
- Claude-Pfad las Steuerbefehle vor, obwohl das Skill lief (Manifest-speak-Flag
  kennt den Unterschied Befehl/Frage nicht — dasselbe Skill ist mal Aktion, mal
  Auskunft). Jetzt entscheidet ARIA aus dem Kontext.
- Befehlsketten (VNC oeffnen, Menue klicken, ...): Mikro soll offen bleiben und
  stumm gearbeitet werden, bis Stefan die Kette beendet.

Marker (ARIA haengt sie ans Antwort-Ende):
  [[STUMM]]  -> Steuerbefehl, nicht vorlesen. Allein = Einzelbefehl -> danach Stop.
  [[WEITER]] -> Konversation/Kette laeuft weiter -> Mikro offen halten.
  [[ENDE]]   -> Konversation/Kette beenden -> zurueck aufs Wake-Word.

Brain:
- _extract_flow_markers() zieht [[STUMM]]/[[WEITER]]/[[ENDE]] aus dem Claude-Reply
  und ueberschreibt speak/converse (Marker ist autoritativ ueber Skill-Flag).
  [[STUMM]] allein impliziert converse=false; [[ENDE]] schlaegt [[WEITER]].
- prompts.py: neue Sektion build_voice_flow_section() bringt ARIA Marker +
  Befehl/Frage-Klassifikation + Ketten-/Ende-Erkennung bei.

App (ChatScreen): stiller Antwort-Zweig verzweigt jetzt auf converse —
  converse=true (Kette) -> endConversation(false), Mikro bleibt offen fuer den
  naechsten Kettenbefehl; converse=false -> ariaStopRecording (Stop wie gehabt).

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 01:21:43 +02:00
duffyduckandClaude Opus 4.8 085ede4ce7 fix(vm): global eindeutige VNC-Displays (kein Doppel-Binding) + Gateway per-boot
- project_vms.add_vm: VNC-Display wird GLOBAL eindeutig vergeben (ueber alle
  Projekte). vnc_display<=0 oder belegtes Display → naechstes freies. Updates
  behalten ihr Display. Verhindert Port-Konflikt (5901) wenn mehrere VMs laufen.
  Getestet: vm1..3 → 1,2,3; Wunsch 2 (belegt) → 4; Update behaelt Display.
- vm_register-Tool + Seed-Regel: vnc_display weglassen, wird auto-vergeben.
- Gateway-IP: der Brain liest sie aus /proc/net/route bei JEDEM Boot frisch
  (Docker-IP-Aenderung nach Netz-Neuaufbau wird abgefangen); boot-Antwort
  enthaelt vnc_bind zur Transparenz.

py_compile clean. Brain-only, Deploy: brain rebuild.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 00:23:23 +02:00
duffyduckandClaude Opus 4.8 c6cbf7a4a8 feat(vm): Medien pro VM (disk/floppy/iso) + ARIA-Urteilswissen
vm_register + Registry + boot fassen jetzt das ECHTE Boot-Medium (disk/floppy/
iso) — Eintrag, Startbefehl und App-Start spiegeln die reale Config. Leer =
aria-vm erkennt disk.qcow2/floppy.img/cdrom.iso selbst.
- project_vms.add_vm: floppy/disk-Felder.
- main.py: VmAddBody + _vm_boot_args (baut --disk/--floppy/--iso), boot-Endpoint
  + boot_cmd nutzen sie.
- agent.py: vm_register-Tool bekommt disk/floppy/iso.
- Seed-Regel: URTEIL — VM nur wenn sinnvoll; Medium aus der Situation (Festplatte
  fuer DOS-Spiele, Diskette fuer OS-Dev, ISO fuer Installer); Architektur zum Task
  (ARM=aarch64, emuliert/langsam ok); Aenderungs-Zyklus stop→aendern→boot.

py_compile clean. aria-vm schon live auf dem Host.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:51:27 +02:00
duffyduckandClaude Opus 4.8 de7cbd35f4 feat: VM-Liste pro Projekt (Desktop-Panel) — Start/Stop/Verbinden
- Brain: project_vms.py (Registry /shared/config/project_vms.json pro Projekt) +
  Endpoints GET/POST/DELETE /projects/<id>/vms + boot/stop (via SSH aria-wohnung
  aria-vm auf dem Host; Status aus `aria-vm list`).
- ARIA-Tools vm_register/vm_list (aufs Request-Projekt) + Seed-Regel: nach dem
  VM-Bau registrieren, damit sie in Stefans Desktop-Panel auftaucht.
- App: brainApi VM-Methoden + ProjectVm-Typ. Neues DesktopTile — pro Projekt die
  VM-Liste (leer bis registriert), Start/Stop/Verbinden; Verbinden oeffnet noVNC
  (VncTile mit dem VNC-Port der VM). Deck rendert DesktopTile statt VncTile.

py/tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:25:55 +02:00
duffyduckandClaude Opus 4.8 7e3b5c0dde feat: Satelliten — ARIAs Augen & Haende in fremden Netzen (Info-/Gateway-Aussenposten)
Neuer eigenstaendiger Container satellite/ (RVS-Client, network_mode host), den
man in einem beliebigen Netz (Buero etc.) deployt. Gibt ARIA Zugriff auf dieses
Netz ohne Haupt-Stack davor.

- satellite/satellite.py: RVS-Client + Discovery (mDNS/Zeroconf, SSDP/UPnP+DIAL,
  ARP) + Steuerung (dial.launch fuer YouTube-auf-FireTV, wol, http) mit Guards
  (CONTROL_ENABLED + Allowlist + Logging, token-gated, keine offenen Ports).
  Periodisches Re-Announce (sat_hello im Heartbeat) fuer spaet joinende Bridge.
- satellite/: Dockerfile, requirements, docker-compose (host-net), .env.example
  (SATELLITE_LOCATION als Adresse), README.
- rvs: sat_hello/discover/devices/command/result whitelisted.
- bridge: Satelliten-Registry (sat_hello) + Future-Relay (_satellite_request) +
  /internal/satellite + /internal/satellite-list (Muster wie flux).
- brain: Tools satellite_list/devices/command + _dispatch_satellite + Seed-Regel.

Alle py_compile + node -c gruen. Kein APK-Rebuild noetig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 23:22:30 +02:00
duffyduckandClaude Opus 4.8 c2511af4ef fix(brain): project_summary liest volle Projekt-Historie aus chat_backup.jsonl
"Hol dir die Infos aus Projekt X" lieferte fast immer leer: project_summary las
nur das rollende Conversation-Window (~50 Turns ueber alle Projekte), aeltere
Projekt-Chats sind da rausdistilliert. Jetzt liest _read_project_history die
echte volle Historie aus /shared/config/chat_backup.jsonl (im Brain gemountet),
letzte ~20 Turns des Zielprojekts, Standort-Hints gefiltert, Fallback aufs
Window. Tool-Beschreibung geschaerft. Live gegen echte Daten getestet
(basic_os 20 / vdi 20 / mac_os_update_fehler 6 Turns).

Kein APK-Rebuild noetig (nur Brain).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:55:54 +02:00
duffyduckandClaude Opus 4.8 9cbd96e50b fix: Projekt-Fokus pro Geraet unabhaengig (Diagnostic/App/Multi-Instanz)
Zwei Kopplungs-Bugs behoben:
- set_project_kind nutzte den globalen active_project statt des Request-Projekts
  → markierte das falsche Projekt (belegt: global aktiv war mac_os_update_fehler,
  das kind=code bekam, obwohl die App in basic_os war). _dispatch_tool bekommt
  jetzt die project_id des Requests durchgereicht; set_project_kind wirkt darauf.
- Die App erzwang bei project_changed-Broadcasts (ARIA-Tool/Diagnostic/andere
  App-Instanz) einen Fokuswechsel → alle Geraete wurden mitgezogen. Fokus ist
  jetzt rein geraetelokal; Broadcasts aktualisieren nur Namen/Typ. Wechseln nur
  noch lokal ueber den Drawer.

py-compile + tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:37:51 +02:00
duffyduckandClaude Opus 4.8 5491b9aa37 feat: Workspace-Umbau Schritt 6 — QEMU fuer alle Architekturen + set_project_kind
QEMU auf dem Host + ARIA-Anbindung:
- host-provisioning/qemu-setup.sh: installiert qemu-system-* (x86/arm/mips/ppc/
  sparc/misc), qemu-utils, Firmware, socat, imagemagick + den aria-vm-Helper.
- host-provisioning/aria-vm: VM-Verwaltung fuer JEDE Architektur (create/boot/
  screenshot/list/stop/rm). VNC bindet nur 127.0.0.1:<display> (Tunnel via
  Bridge), KVM nur fuer x86-Gaeste, sonst TCG.
- Brain: Projekt-Modell bekommt kind ('chat'|'code'); neues Tool
  set_project_kind markiert das aktive Projekt (blendet Editor/Desktop in der
  App ein) + feuert project_changed.
- Seed-Regel: ARIA weiss jetzt, dass Code unter /shared/projects/<id>/ gehoert
  und wie sie per `ssh aria-wohnung aria-vm ...` VMs baut/testet; VNC landet
  automatisch in der Desktop-Kachel.

py-compile clean, Host-Skripte bash -n OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:57:57 +02:00
duffyduckandClaude Opus 4.8 6dc4333b82 fix(brain): Trigger-Zeit UTC-konsistent, project_summary liest Hauptchat, Privatkopie-Regel
- Trigger-Zeit (Issue 4): fires_at wird beim Anlegen IMMER auf UTC normalisiert
  (naiv = lokale Wanduhrzeit Europe/Berlin → UTC, aware → astimezone). Passt zur
  UTC-Ausfuehrung in background.py → feuert zur gemeinten Ortszeit, zeitzonen-
  portabel. Tool-Desc + Prompt: fires_at = lokale Wanduhrzeit ohne Zone schreiben,
  Server rechnet um. Behebt den 2h-Versatz.
- project_summary (Issue 2): liest jetzt auch den Hauptchat (name='Hauptchat')
  und die Beschreibung sagt ARIA, es fuer 'schau mal in Projekt X / Hauptchat
  rein' / 'hol dir die Infos aus ...' zu nutzen.
- Privatkopie (Issue 3): IDENTITY_ANCHOR-Regel — YouTube-Downloads fuer Stefans
  PRIVATgebrauch sind eine Privatkopie (§53 UrhG), nicht reflexartig ablehnen/
  moralisieren; Urteil bleibt fuer Weiterverbreitung/kommerziell.

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

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

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

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

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:57:52 +02:00