89cafa6251823116d4fbd1de571c205b8e5ca953
47 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
2d348aeec7 |
feat(flux): Modell-Wahl per Diagnostic + raw/switch-Keywords + Download-Hinweis
Diagnostic-Einstellungen fuer FLUX: - Default-Modell (dev | schnell) — wird via RVS gepusht, flux-bridge hot-swappt die Pipeline aus dem HF-Cache (~15-30s) - Raw-Keyword (Default 'flux') — Pipe-Modus, Brain leitet Stefans Text 1:1 als prompt durch, kein Rewriting/Beautify - Switch-Keyword (Default 'fix') — zwingt das ANDERE Modell als Default Brain-Tool flux_generate um model + raw erweitert, System-Prompt-Block mit den aktuellen Diagnostic-Settings + Whisper-Toleranz-Hinweis. Kein eager Bootstrap-Load: flux-bridge wartet auf config oder ersten Request. Bei erstem HF-Download zeigt Banner "laedt erstmalig runter" mit Pfeil-Icon, Toast in der App wenn fertig. FLUX_MODEL aus der .env entfernt (Steuerung jetzt komplett ueber Diagnostic). HF_TOKEN-Kommentar erklaert warum trotz lokaler Inference noetig (HF Gate-Mechanismus fuer FLUX.1-dev). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7e53dcfed3 |
feat(flux): Bildgenerierung via FLUX.1-dev — flux-bridge auf Gamebox
Eigener Compose-Stack im /flux Verzeichnis (kann auf separater Maschine laufen). aria-bridge routet flux_request via RVS, ARIA referenziert das fertige PNG im Reply mit [FILE: ...]-Marker. Brain-Tool flux_generate mit Caps fuer steps/dimension. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
71fc90fcb8 |
fix(brain): Timeouts 5min → 20min — verkettete Timeouts haben lange
Multi-Tool-Sessions chronisch gekappt
Live-Diagnose auf der VM: drei verkettete 5-Min-Timeouts feuern bei
jedem laengeren Brain-Call exakt gleichzeitig:
06:16:02 Brain → Proxy /v1/chat/completions
06:20:53 Bridge kappt (4m51s, urlopen timeout=300)
06:21:02 Brain bekommt HTTP 500 vom Proxy ('timed out after 300000ms')
Stefan's Karten-Rekonstruktion (curl gegen Nominatim/OSRM + viele Bash-
Tool-Calls + DB-Inserts) braucht locker 8–15 Min — alle Brain-Calls
ueber 5 Min sind reihenweise mit 'Brain-Fehler: timed out' verreckt,
auch wenn die Arbeit zu 80% durch war.
Drei Stellen patchen:
- bridge/aria_bridge.py: urlopen 300 → 1200 (20 Min)
- aria-brain/proxy_client.py: PROXY_TIMEOUT_SEC default 300 → 1200
- docker-compose.yml: dritter sed-Patch im proxy-Service
setzt DEFAULT_TIMEOUT im claude-max-api-proxy von 300000 auf 1200000
Plus App-Watchdog: 180s → 1260s (21 Min, knapp ueber Brain-Timeout)
damit der lokale Stuck-Watchdog nicht waehrend legitimer langer
Sessions feuert. Echte Verbindungsabbrueche kappen vorher per WS-
Disconnect.
UX-Tradeoff bewusst akzeptiert: User sieht jetzt bis zu 20 Min nur
'ARIA denkt...' ohne Zwischen-Updates. Echte Loesung waere Streaming
oder async-Job-API (siehe Etappe B/C im Vorschlag) — das ist groesseres
Refactoring, hier reicht erst mal der Quick-Fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6037b62612 |
fix(brain): ARIA legt nicht mehr ungefragt Skills an
Prompt sagte 'Harte Regel — IMMER Skill anlegen wenn pip-Library noetig'. ARIA hat das wortwoertlich genommen: bei einer einfachen pdf-extract-Frage hat sie sofort skill_create gerufen → Brain blockiert 12 Min im venv+pip-Install-subprocess.run, App zeigt 'ARIA denkt', Diagnostic emitted nach 5 Min Timeout idle, Stefan blieb stundenlang ohne Antwort. Neue Regel: - Goldene Regel: NIE ungefragt Skills anlegen. - Aufgabe zuerst inline loesen (Bash, direkter pip install, Workaround). - Skill nur wenn Stefan EXPLIZIT sagt 'mach daraus einen Skill' / 'leg den als Skill an'. - Die vier Kriterien (wiederkehrend/nicht-trivial/parametrisierbar/ wiederverwendbar) sind jetzt Checkliste NACH expliziter Anfrage — fehlt eines, soll ARIA nachfragen statt blind anzulegen. - Begruendung steht jetzt im Prompt: Setup blockt Brain bis zu 12 Min. Greift auf der VM ohne Re-Build, prompts.py wird beim Start geladen (docker compose restart aria-brain reicht). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
435b77e1df |
feat(trigger): entered_near + left_near — drei Modi fuer near()-Watcher
Stefan: bei aktuellen near()-Watcher gibt's nur "solange drin". Reale Szenarien wollen aber differenzieren: - VORWARNUNG vor Ziel (Blitzer-Warner 2 km vorher) → entered_near mit grossem r - ANKUNFT exakt am Ziel → entered_near mit kleinem r - VERLASSEN (Parkplatz, hast du was vergessen) → left_near - KONTINUIERLICH-DRIN (bin noch in der Naehe?) → near (Default, throttled) Zwei neue Funktionen in der Condition-Whitelist: - entered_near(lat, lon, r): True NUR im Moment des Uebergangs draussen → innen. Fires einmal pro Eintritt. - left_near(lat, lon, r): True NUR im Moment des Uebergangs innen → draussen. Fires einmal pro Austritt. State-Tracking: - pro Trigger pro near-Aufruf wird der letzte Auswertungs-Wert (true/ false) im Watcher-Manifest gespeichert (Field "near_states", Key "lat.6,lon.6,radius"). Background-Loop liest's vor dem Eval, gibt's per collect_variables(prev_near_states=...) in die Closure, schreibt nach dem Eval die neuen Werte zurueck — UNABHAENGIG ob gefeuert wurde, sonst greift die Uebergangs-Erkennung nicht. Background _tick: - Aufteilung in Watcher-Pass (mit prev_near_states pro Trigger) und Timer-Pass (ohne State, gemeinsame vars). Bisher war collect_variables einmal pro Tick — jetzt einmal pro Watcher. Disk-Stats sind teuer aber unter 30 Watchern unkritisch; bei mehr koennen wir cachen. ARIA-Tool-Description erweitert (trigger_watcher): erklaert die drei Modi mit Use-Cases und empfohlenen Throttle-Werten (kurz fuer entered/ left, lang fuer near). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
6f80e442cf |
fix(trigger): near() fired bei Auto-Vorbeifahrten verpasst — Loop schneller + event-getrieben
Stefan ist mehrmals an einem 300m-near()-Watcher (DRK Kreyenbrueck)
vorbeigefahren, kein Fire. Ursache: Background-Loop tickte alle 30s,
Auto-Durchfahrt durch 600m-Durchmesser-Radius dauert bei 50-120 km/h
nur 18-43 Sekunden — der Tick konnte komplett dazwischen liegen.
Drei Fixes (A + B aus Stefans Vorschlag):
A1. Background-Loop-Frequenz: TICK_SEC 30 → 8.
Garantiert mind. 2 Checks auch bei 120 km/h durch 300m. Loop ist
billig (paar Dateilesungen + AST-Eval), Brain merkt das nicht.
A2. near() bekommt Age-Schutz (watcher.py NEAR_MAX_AGE_SEC=300):
Wenn location_age_sec > 5 min, gilt die Position als unbekannt
und near() liefert False. Verhindert Phantom-Fires wenn Tracking
aus ist oder Mobilfunk weg war — vorher haette der letzte
bekannte Wert weiter ausgewertet werden koennen.
B. Event-getriebener Tick:
- background.py: tick_now()-Funktion + Module-Slot fuer
agent_factory damit man von ausserhalb des Lifespan-Pfads
einen Tick triggern kann
- main.py: POST /triggers/check-now Endpoint ruft tick_now()
- bridge: _persist_location feuert nach jedem Save ein fire-and-
forget POST /triggers/check-now (run_in_executor, timeout 8s,
blockt nichts wenn Brain stockt)
Damit fires near() sofort wenn die App ein location_update schickt —
Polling ist nur noch der Fallback fuer Watcher OHNE GPS-Bezug
(disk_free, hour_of_day etc.) und als Sicherheits-Tick falls
location_update mal ausfaellt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f71936da86 |
feat(memory): Tap auf Memory-Bubble oeffnet Detail+Edit-Modal in der App (Etappen 2+3)
Stefans naechste Wunsch-Etappe — komplettes Edit eines Memory-Eintrags
aus der App heraus, inkl. Anhang-Upload, ohne Diagnostic-Browser
auszuklappen.
Backend-Fundament (Phase A):
- Brain bekommt GET /memory/get/{id} fuer Einzel-Lookup mit allen Feldern
- RVS ALLOWED_TYPES um brain_request + brain_response erweitert
- Bridge implementiert generischen RVS-Brain-Proxy:
payload {requestId, method, path, body|bodyBase64, contentType}
→ ruft Brain-HTTP-API → broadcastet brain_response {requestId,
status, json|text|base64+contentType}. Damit kann die App
beliebige Brain-Endpoints ueber RVS adressieren — nicht nur Memory.
App-Service (Phase B):
- services/brainApi.ts: Promise-basierter Client. _send() schickt
brain_request mit requestId, _ensureListener() filtert die passende
brain_response. Methoden: getMemory, listMemories, searchText,
searchSemantic, saveMemory, updateMemory, deleteMemory,
uploadAttachment (Base64), deleteAttachment, getAttachmentBytes.
App-UI (Phasen C+D):
- components/MemoryDetailModal.tsx: Modal mit zwei Modi.
- Read: Titel, Type, Category, Tags, voller Content, Anhang-Liste
(Tap = Bild im Vollbild oder Datei-Info), Stift-Icon → Edit.
- Edit: Titel/Content/Category/Tags/Pinned editierbar, Save via
brainApi.updateMemory.
- DocumentPicker + RNFS.readFile(base64) → uploadAttachment(...).
- Anhang loeschen, kompletter Memory loeschen (mit Alert-confirm).
- ChatScreen: TouchableOpacity-Wrapper um die memorySaved-Bubble,
Tap setzt memoryDetailId → Modal oeffnet. Hint im Footer
"tippen für Details" wenn die Bubble eine ID hat.
Etappen 4 (Notizen-Inbox neben Lupe) + 5 (Memory-Editor in App-
Settings) folgen — diese nutzen die gleiche MemoryDetailModal-
Komponente, sind also schnell aufgesetzt sobald 2+3 verifiziert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6239037fa7 |
feat(memory): Bubble-Header zeigt jetzt Aktion (angelegt/geaendert/geloescht)
Etappe 1 von Stefans App-Memory-UX-Wunsch:
Brain agent.py: memory_save Dispatcher pushed jetzt action="created",
memory_update Dispatcher pushed action="updated" mit demselben
memory_saved-Event-Typ. Bridge reicht das action-Feld im Payload mit
durch (in beiden Side-Channel-Pfaden — send_to_core + trigger-fired).
App ChatScreen: ChatMessage.memorySaved.action ('created' | 'updated'
| 'deleted'). Bubble-Header je nach Aktion:
- created → "🧠 ARIA hat etwas gemerkt" (gelb)
- updated → "🧠 ARIA hat eine Notiz geändert" (gelb)
- deleted → "🧠 ARIA hat eine Notiz gelöscht" (rot)
Naechste Etappen folgen (Detail-Modal beim Tap, Edit + Anhang-Upload,
Notizen-Inbox neben Lupe, Memory-Editor in Settings).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
16ebaa652f |
feat(brain): memory_search + memory_update Tools — ARIA findet Updates aktiv
Bug-Report von Stefan: er hat im Diagnostic den Baujahr-Memory von
1972 auf 1974 geaendert, ARIA wusste das nicht und beharrte auf 1972
(weil ihr letzter Conversation-Turn noch '1972' enthielt). Sie konnte
auch nicht nachpruefen, sagte selbst: "Qdrant kann ich nicht aktiv
durchsuchen".
Fix: zwei neue Meta-Tools im agent.py.
memory_search(query, mode='text'|'semantic', k=5):
- Volltext oder semantic via store.search_text / store.search
- Liefert Liste mit Titel, ID, Content, Anhaengen
- Tool-Description sagt explizit: "Memory ist Truth ueber dem
Conversation-Window" — wenn beide unterschiedlich sind, gilt
Memory. Plus Anker-Anwendungsfaelle: 'schau in deinem Gedaechtnis',
'ich hab das aktualisiert', 'pruef ob's schon was zum Thema gibt'
memory_update(id, title?, content?, category?, tags?, pinned?):
- Patch existierender Memory per ID (aus memory_search oder Cold-Memory)
- Content-Change triggert Re-Embedding fuer Search, sonst nur
Payload-Update
- Pushed memory_saved-Event analog zu memory_save (App/Diagnostic
refreshen)
- Tool-Description empfiehlt explizit Update statt neuem Save bei
Korrekturen/Ergaenzungen — vermeidet Fragmentierung
Damit kann Stefan jetzt sagen "schau in deinem Gedaechtnis" und ARIA
findet den aktualisierten Eintrag. Plus bei spaeteren Korrekturen
("ach nee, 1974") nutzt ARIA memory_update statt memory_save +
hinterlaesst einen sauberen Eintrag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
31a1370050 |
feat(brain): memory_save mit attach_paths — ARIA haengt Bilder selbst an
Letzter Baustein vor Stefan's End-to-End-Test:
memory_attachments.attach_from_path(memory_id, src_path):
- Kopiert eine bestehende Datei aus /shared/uploads/ oder
/shared/memory-attachments/ in das Anhang-Verzeichnis der Memory
- Pfadschutz: nur ALLOWED_SOURCE_PREFIXES (/shared/uploads/,
/shared/memory-attachments/) — kein Zugriff auf Root-FS oder
SSH-Keys
- Groessen-Limit wie save_attachment (20 MB Default)
agent.py memory_save:
- Neuer optionaler Parameter `attach_paths: List[str]`
- Nach dem upsert: pro Pfad attach_from_path → Payload update mit
neuen Anhang-Metadaten
- Fehler beim Anhang sind nicht fatal (Memory bleibt gespeichert,
Hinweis in der Tool-Response)
- Tool-Description deutlich erweitert: expliziter Workflow-Hinweis
bei Bildern → erst `Read <pfad>` aufrufen (Claude Code Read ist
multi-modal), Texte/Kennungen/Marken in den content extrahieren,
dann erst memory_save mit attach_paths. Beispiel-Workflow als
Pseudocode mit Cessna 172 / Kennung D-EAAA.
End-to-End-Workflow ist jetzt einarmig moeglich:
User: "Ich hab eine Cessna 172" + Bild im Attachment
ARIA: Read /shared/uploads/aria_xy.jpg → sieht "Kennung D-EAAA"
ARIA: memory_save(content="Stefan besitzt eine Cessna 172,
Kennung D-EAAA, weiss/rot lackiert.",
attach_paths=["/shared/uploads/aria_xy.jpg"])
→ 🧠-Bubble mit Anhang in der App
→ Spaetere Frage "welche Kennung hat mein Flieger?" liefert via
Cold-Memory den Eintrag inkl. Kennung aus dem content
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
933dd50367 |
feat(brain): Stufe E — ARIA sieht Bilder ueber Claude Codes Read-Tool
Wir mussten den Proxy nicht patchen. Claude Code's eingebautes Read-Tool ist multi-modal-faehig — uebergibt man eine Bilddatei, geht die durch das gleiche Vision-Modell wie via Anthropic-Vision-API. ARIA hat eh "Tool-Freigaben — Vollzugriff" pinned (inkl. Read), also muss sie nur wissen dass sie das nutzen darf. prompts._attachments_line erweitert: bei image/* im Anhang haengen wir den Hinweis an "Bilder kannst du via `Read <pfad>` direkt ansehen". ARIA ruft dann selbststaendig Read mit dem Memory-Anhang-Pfad, sieht das Bild und kann antworten was drauf ist. Heisst: Stefan sagt "schau dir mein Cessna-Foto an" → ARIA findet Memory via Cold-Search → sieht die Read-Anweisung → ruft Read auf → Vision-Modell beschreibt das Bild → ARIA antwortet im Chat. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d5531521fa |
feat(memory): Anhaenge in App-Bubble + System-Prompt (Stufe C + D)
Stufe C — App:
- ChatMessage.memorySaved.attachments [{name, mime, size, path, localUri}]
- memory_saved-Listener uebernimmt payload.attachments
- renderMessage memorySaved-Bubble zeigt Anhaenge als Tap-Reihen
(Icon 🖼/📄 + Filename + Hint). Tap → file_request via Bridge,
beim ersten Mal "(tippen zum Laden)" → nach file_response cached
+ bei Bildern setFullscreenImage, bei anderen openFileWithIntent
- file_response-Handler updated zusaetzlich memorySaved.attachments
per serverPath-Match
- Styles fuer memoryAttachmentRow/Icon/Name/Meta
Stufe D — System-Prompt:
- prompts._attachments_line: pro Memory eine Zeile
"📎 Anhaenge: foo.jpg (image/jpeg, 109 KB) — Pfad: /shared/memory-attachments/<id>/"
- Wird in build_hot_memory_section + build_cold_memory_section
nach dem Content angehangen
- ARIA "weiss" damit dass Anhaenge da sind und kann via Bash darauf
zugreifen (file, head, base64 …). Echt sehen kann sie sie erst mit
Multi-Modal-Pipeline (Stufe E)
- memory_save Dispatcher: attachments-Liste auch im memory_saved-Event
(vermutlich [] beim Save, aber konsistent fuer spaeteres
Speichern-mit-Anhaengen-Pattern)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
da4e970a31 |
feat(brain): Memory-Anhaenge — multipart/form-data Endpoint daneben Base64
Stefan's Test scheiterte: ein normales Handy-Foto als Base64 in der
curl-d-Argumentliste sprengt Bash's ARG_MAX (typisch 128KB-2MB). Plus:
Browser-FormData und curl -F sind eh der Standard fuer File-Uploads.
Fix: zusaetzlicher Endpoint
POST /memory/{id}/attachments/upload (multipart/form-data, field: file)
Beispiel auf der VM:
curl -F file=@/pfad/zu/foto.jpg \
"$ARIA_BRAIN_URL/memory/<id>/attachments/upload" | jq
Base64-Endpoint (/memory/{id}/attachments) bleibt fuer kleine
Uploads + interne JSON-Tools. Beide rufen am Ende den gleichen
_commit_attachment_meta-Helper, der das Memory-Payload um den
neuen Anhang updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c677cfed24 |
feat(brain): Anhaenge an Memory-Eintraege (Stufe A — Backend)
Pro Memory koennen jetzt Dateien (Bilder, PDFs, Sound, ...) angehaengt
werden. Use-Case: Stefan sagt "ich hab eine Cessna 172" und pinnt
gleich ein Foto dran — ARIA sieht spaeter neben dem Memory auch die
visuelle Referenz (Stufe E = Multi-Modal-Pipeline).
Stufe A baut nur den Backend-Layer; UI kommt in Stufe B (Diagnostic)
und C (App). Anhaenge werden in Stufe A nur via HTTP-API gepflegt
(curl), ARIA selbst kann sie noch nicht hochladen — sinnvoll erst
wenn die Vision-Pipeline (Stufe E) steht.
Komponenten:
- memory_attachments.py: neuer Storage-Helper. Layout
/shared/memory-attachments/<memory-id>/<safe-filename>.
Filename-Sanitization (kein Path-Traversal), Limit 20 MB
konfigurierbar, save/list/delete/read_bytes + delete_all fuer
Cleanup beim Memory-Delete.
- vector_store.py: MemoryPoint.attachments (List[dict]) — Metadaten
{name, mime, size, path} im Qdrant-Payload damit Suche/Anzeige
sie ohne Filesystem-Lookup kennt.
- main.py:
- MemoryIn akzeptiert attachments-Liste (fuer Restore-Faelle)
- MemoryOut liefert attachments
- GET /memory/{id}/attachments → Liste vom FS
- POST /memory/{id}/attachments → Base64-Upload,
schreibt FS + updated Payload-Liste
- DELETE /memory/{id}/attachments/{filename} → FS + Payload-Eintrag weg
- GET /memory/{id}/attachments/{filename} → Bytes mit MIME serve
- /memory/delete cleanup: ruft attachments.delete_all damit kein
Verzeichnis verwaist
Smoke-Test nach Brain-Rebuild (Stefan auf VM):
# Memory-ID rauspicken
ID=$(curl -s "$ARIA_BRAIN_URL/memory/list?type=fact" | python3 -c "import sys,json;print(json.load(sys.stdin)[0]['id'])")
# Bild als Base64 hochladen
B64=$(base64 -w0 /pfad/zu/foto.jpg)
curl -s -X POST "$ARIA_BRAIN_URL/memory/$ID/attachments" \
-H 'Content-Type: application/json' \
-d "{\"name\":\"foto.jpg\",\"data_base64\":\"$B64\"}" | jq
# Liste anzeigen
curl -s "$ARIA_BRAIN_URL/memory/$ID/attachments" | jq
# Datei wieder laden
curl -s "$ARIA_BRAIN_URL/memory/$ID/attachments/foto.jpg" -o /tmp/back.jpg
Stufe B (Diagnostic-UI) folgt sobald A getestet ist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1e754910ee |
fix(brain): Cold Memory mit Score-Threshold — kein Crosstalk mehr
Bug: Agent.chat() rief store.search() OHNE score_threshold — die
Top-5 wurden ungefiltert in den 'Moeglicherweise relevant'-Block
des System-Prompts gepackt. Bei kleiner DB hatte das absurde Folgen:
Stefan fragte 'hab ich ein flugzeug?', Cold-Search lieferte Top-1
'Watcher-Latenzproblem' mit Score 0.138 + 'Firmenadresse' mit 0.094,
ARIA wob die Firmenadresse in die Antwort ein ('Die Adresse habe ich
aus meinem Gedaechtnis...') — obwohl der User gar nicht danach gefragt
hat.
Fix: Konstante COLD_SCORE_THRESHOLD=0.30 in Agent eingefuehrt und an
store.search() durchgereicht. Treffer unter 0.30 werden als Rauschen
verworfen, ARIA bekommt nur substantielle Memories ins Cold-Set.
Konsistent mit dem Threshold im /memory/search HTTP-Endpoint und dem
Diagnostic UI.
MiniLM-multilingual gibt fuer unverwandte deutsche Texte gerne 0.10-
0.25 Score — alles darunter ist Embedder-Noise, kein echter Bezug.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
df60bb6d74 |
fix(brain): Cessna-Beispiel aus System-Prompt raus — keine Phantom-Wissens-Hinweise
ARIA hatte beim 'weisst du ob ich ein Flugzeug habe?'-Test richtig
geantwortet ('nein'), aber transparent erklaert dass sie das Wort
'Cessna' aus dem memory_save Tool-Description kennt — wo es als
Beispiel fuer den fact-Type stand. Ein Beispiel-Text der jedes
Chat-Turn im System-Prompt landet ist suboptimal, auch wenn ARIA
ihn korrekt einordnet.
Fix: das konkrete Beispiel durch eine generische Aufzaehlung
ersetzt (Vorlieben/Besitz/Orte/Termine/Personen). Ohne Stefan-
spezifisches Phantom-Wissen. Selber Spirit in der search-text
Docstring im main.py (geht zwar nicht in den Prompt, aber lieber
konsistent).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
5f96ace469 |
feat(brain): memory_save Tool — ARIA schreibt selber in die Qdrant-DB
ARIA hatte bisher KEIN Tool um eigene Notizen sauber zu persistieren — sie ist deshalb aufs Claude-Code-File-Memory ausgewichen (das wir mit dem letzten Commit per tmpfs abgeklemmt haben). Jetzt schliesst sich der Loop: ein echtes memory_save-Tool gegen die Qdrant-DB. Brain: - agent.py: memory_save als Meta-Tool mit Schema (title, content, type, optional category/tags/pinned). Tool-Description erklaert die Type-Wahl (identity/rule/preference/tool/skill = pinned, fact/conversation/reminder = cold) und sagt explizit: "Du hast KEIN File-Memory mehr, schreibe nicht in ~/.claude/projects/..." - Dispatcher: validiert type-enum, ruft self.embedder.embed + self.store.upsert, pushed memory_saved als _pending_events damit Bridge eine Bubble broadcasten kann. Side-Channel-Pipeline (gleich wie skill_created/trigger_created): - Bridge send_to_core + _handle_trigger_fired: forwarden memory_saved als RVS-Event - rvs/server.js: ALLOWED_TYPES += memory_saved - diagnostic/server.js: relayed memory_saved von RVS an Browser - diagnostic UI: addMemorySavedBubble (gelber Border) + Auto-Refresh des Gehirn-Tabs wenn aktiv - android: ChatMessage.memorySaved-Feld, Listener fuer memory_saved, renderMessage-Spezialbubble, History-Replace-Schutz (lokal-only) Damit ist die Architektur konsistent: "merk dir X" → ARIA ruft memory_save → Eintrag in Qdrant → Diagnostic-Gehirn-Tab zeigt's sofort → bei naechstem Turn liefert Cold Memory (Semantic Search) das Wissen wieder rein. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
6549fcbce8 |
feat(brain): Volltext-Suche zusaetzlich zu Semantic — Default ist jetzt Wortlich
Stefan wollte ne richtige Suche statt nur "klingt aehnlich". Beide Modi sind jetzt verfuegbar, Default ist Volltext: - 📝 Wortlich (Substring, case-insensitive ueber Title + Content + Category + Tags) — neuer Endpoint /memory/search-text. Full-Scan via Qdrant scroll, k=50. Findet "cessna" exakt im Content. Bei kleiner DB (<1000 Eintraege) unkritisch performant. - 🧠 Semantisch (Embedder + score_threshold 0.30) — bestehender /memory/search Endpoint. Findet konzeptuell verwandte Eintraege. Diagnostic UI: Dropdown neben dem Suchfeld zum Modus-Wechsel. Info-Banner zeigt klar welcher Modus aktiv ist. Warum Wortlich Default: bei kleiner DB liefert Semantic gern False Positives mit Score 0.30-0.45 fuer komplett unverwandte Begriffe (z.B. "cessna" matched "Tageslog fuehren" mit 0.43). Wortlich ist deterministisch und vermeidet das Rauschen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3f2499b528 |
feat(chat): Muelltonne pro Bubble — gezielt eine Nachricht loeschen
Stefan kann jetzt einzelne Chat-Bubbles loeschen (mit Rueckfrage).
Die Bubble verschwindet aus chat_backup.jsonl (Bridge), Brain-
Conversation (rolling window + jsonl) und allen Clients (App +
Diagnostic). Genauso wichtig fuer ARIA: der gloeschte Turn ist im
naechsten Chat-Prompt nicht mehr im Window.
Pipeline:
UI 🗑 + confirm
→ RVS delete_message_request {ts}
→ Bridge._delete_chat_message:
- chat_backup.jsonl Zeile mit ts entfernen (atomar via tmp+rename)
- Brain POST /conversation/delete-turn (role+content match)
- RVS broadcast chat_message_deleted {ts}
→ App + Diagnostic entfernen Bubble lokal per ts-Match
Backend-Aenderungen:
- aria-brain/conversation.py: remove_by_match(role, content, ts_hint)
+ _rewrite_file (atomar). Match nahester Turn bei mehrfach gleichem
content.
- aria-brain/main.py: POST /conversation/delete-turn (POST statt DELETE
weil FastAPI keine Bodys auf DELETE erlaubt)
- bridge/aria_bridge.py: HTTP-Listener /internal/delete-chat-message
+ RVS-Handler delete_message_request. _append_chat_backup gibt jetzt
ts zurueck, _process_core_response packt backupTs ins chat-Event.
- rvs/server.js: ALLOWED_TYPES um delete_message_request +
chat_message_deleted erweitert.
- diagnostic/server.js: delete_chat_message-Action + chat_message_deleted
Relay zum Browser.
Frontend-Aenderungen:
- diagnostic/index.html: 🗑 erscheint on-hover in Bubbles mit data-ts,
confirm()-Dialog, addChat + chat_history setzen data-ts. WS-Listener
fuer chat_message_deleted entfernt Bubble per data-ts.
- android/ChatScreen.tsx: backupTs in ChatMessage, Muelltonne-Button
unten rechts in jeder Bubble, Alert-confirm, RVS-Listener fuer
chat_message_deleted entfernt aus messages-State.
Live-User-Bubbles (sofort gerendert vom eigenen Send) haben noch
keinen backupTs bis der Bridge-Roundtrip durch ist — die Muelltonne
erscheint dort erst nach kurzer Verzoegerung / Reload. Folgekommit
kann das polieren wenn noetig.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
daf0d44dd7 |
fix(brain): Memory-Suche filtert jetzt Rauschen — score_threshold + kleineres k
Bug: bei kleiner DB (31 Eintraege) lieferte die Suche fuer JEDES Wort fast alles als Treffer zurueck — k=20 Top-N ohne Threshold sorgte dafuer dass auch "banane" zehn vermeintliche Treffer mit Scores 0.09-0.22 (= Rauschen) zurueckgab. Fix: - vector_store.search() bekommt optional score_threshold (an Qdrant durchgereicht, das nimmt's nativ) - /memory/search endpoint hat score_threshold-Query-Param (default 0.30) - Diagnostic schickt k=10 + score_threshold=0.30 statt k=20 ohne Threshold - "Keine Treffer"-Info-Box wenn alle Treffer < Threshold MiniLM-multilingual liefert typischerweise: >0.50 → starker Treffer 0.30-0.50 → relevant 0.20-0.30 → grenzwertig <0.20 → Rauschen Mit score_threshold=0 (oder None) bleibt die alte Top-N-Semantik fuer Aufrufer die Rauschen explizit wollen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7237f05344 |
fix(trigger): Trigger-Antworten landen jetzt im Chat — Brain → Bridge Push
Bug: Wenn der Brain-Background-Loop einen Timer/Watcher feuert, ruft
er agent.chat() direkt im eigenen Prozess. Die Antwort wurde nur ins
Trigger-Log geschrieben — kein RVS-Broadcast, kein TTS, nichts in
App/Diagnostic sichtbar.
Fix: Bridge ↔ Brain bekommen einen internen HTTP-Push-Kanal.
Bridge (Port 8090, nicht exposed, nur aria-net intern):
asyncio.start_server-basierter HTTP-Listener.
POST /internal/trigger-fired
body: {reply, trigger_name, type, events}
→ _handle_trigger_fired feuert Side-Channel-Events
(trigger_created/skill_created/location_tracking) erst,
dann _process_core_response(reply) — exakt der gleiche Pfad
wie normale Chat-Antworten (Chat-Bubble + TTS + chat_backup).
Brain background.py:
Nach agent.chat() in _fire wird agent.pop_events() ausgelesen
und zusammen mit dem Reply via urllib an aria-bridge:8090
gepostet (run_in_executor damit es den asyncio-Loop nicht
blockiert). Failures werden geloggt, der Trigger selbst bleibt
trotzdem als 'fired' markiert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
0d13118f7e |
debug(brain): Proxy-Response loggen — finish_reason + raw-msg + tool_calls-Anzahl
Diagnose-Log um Trigger-Hang zu klaeren: warum legt ARIA keinen Timer an, obwohl trigger_timer als Tool definiert ist? Wir loggen jetzt nach jedem Proxy-Call: - finish_reason - alle Keys aus der message - tool_calls-Anzahl + content-Laenge - die rohe message (truncated 1500 chars) So sehen wir ob der Proxy tool_calls leer liefert (Proxy schluckt tools-Feld?), ob Claude ignoriert (Anthropic-Native-Format statt OpenAI?), oder ob unser Dispatch falsch parsed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8c74b3fed8 |
fix(brain): Timer in 2min funktioniert wieder — Zeit im Prompt + in_seconds-Param
ARIA wusste nicht wieviel Uhr es ist (kein Bash, kein Time-Tool, kein
Timestamp im System-Prompt) und konnte fires_at als ISO-UTC schlicht
nicht ausrechnen. Zwei Fixes:
1. prompts.py: build_time_section() injiziert UTC + lokale Zeit
(Europa/Berlin Sommer/Winter-Heuristik) als '## Aktuelle Zeit'-Block
oben in den System-Prompt. Hilft auch beim Einordnen von
Watcher-Conditions wie hour_of_day == 8.
2. agent.py trigger_timer-Tool: neuer Parameter `in_seconds` als
Alternative zu fires_at. Bei relativen Angaben ('in 2 Minuten')
rechnet jetzt der Server den absoluten Timestamp aus — keine
Rechnerei in der LLM noetig. fires_at bleibt fuer feste Termine.
required nur noch name + message.
Diagnostic-API (/triggers/timer) bleibt absolute-only, da der Browser
selbst datetime hat.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
fa47068d6d |
feat(gps): kontinuierliches GPS-Tracking — Blitzer-Warner-Pipeline komplett
ARIA kann jetzt GPS-Watcher mit near() effektiv nutzen: die App liefert
kontinuierliche Position, Brain wertet sie in den Background-Triggers aus.
rvs/server.js
ALLOWED_TYPES: location_update (App→Bridge) + location_tracking (Brain→App).
bridge/aria_bridge.py
location_update Handler: persistiert {lat, lon} via _persist_location in
/shared/state/location.json — selber Pfad wie chat/audio-events, aber als
eigenes Event ohne Chat-Overhead.
aria-brain/agent.py
Neues Meta-Tool request_location_tracking(on, reason). Dispatcher fuegt
{type: "location_tracking", on, reason} zu _pending_events hinzu →
Bridge forwarded als RVS-Message zur App.
aria-brain/prompts.py
Trigger-Section bekam neuen Block "GPS-Watcher mit near()": ARIA wird
angewiesen request_location_tracking(on=true) zu rufen wenn sie einen
near()-Watcher anlegt, und wieder false beim Loeschen des letzten.
android/src/services/gpsTracking.ts (NEU)
Singleton-Service. start(reason) → Geolocation.watchPosition mit
distanceFilter 30m + interval 15s, sendet location_update an RVS.
stop(reason) → clearWatch. Persistiert Status in 'aria_gps_tracking',
restoreFromStorage() beim Settings-Mount. Permission-Request fuer
ACCESS_FINE_LOCATION + Toast-Benachrichtigung bei An/Aus.
android/src/screens/SettingsScreen.tsx
Neuer Switch im "Standort"-Block: "GPS-Tracking (kontinuierlich)" mit
Hinweis-Text. Subscribe auf gpsTrackingService.onChange damit Toggle
reflektiert wenn ARIA das per Tool umschaltet.
RVS-Handler: location_tracking → gpsTrackingService.start/stop mit
Reason aus Brain-Tool.
Ablauf Stefan→ARIA→Blitzer:
1. Stefan: "Warn mich vor Blitzern auf Route nach Rhauderfehn"
2. ARIA: skill_create("blitzer-warner") falls noch nicht da
3. ARIA: run_blitzer-warner → Liste {lat,lon,name}
4. ARIA: pro Eintrag trigger_watcher mit near(lat,lon,500)
5. ARIA: request_location_tracking(on=true, reason="Blitzer-Warner aktiv")
6. App: GPS-Tracking startet, sendet alle 15s location_update
7. Bridge: /shared/state/location.json wird aktuell gehalten
8. Brain-Background-Loop: alle 30s near()-Check pro Trigger
9. Bei Erfolg: ARIA spricht "Blitzer A31 km 12 in 500m"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
07c761fc72 |
feat(brain): GPS-Variablen + near()-Helper + erweiterte Condition-Vars
ARIA kann jetzt GPS-basierte Watcher-Trigger anlegen (Blitzer-Warner-Use-Case),
plus erweiterte Time-, System- und Activity-Variablen.
bridge/aria_bridge.py
_persist_state() schreibt atomar nach /shared/state/<key>.json.
Bei jedem chat- und audio-Event:
- location → /shared/state/location.json {lat, lon, ts_unix}
- last_user_ts → /shared/state/activity.json
Brain-Watcher lesen das fuer die GPS- und Activity-Variablen.
aria-brain/watcher.py — komplett ueberarbeitet
Neue Variablen-Sets:
GPS: current_lat, current_lon, location_age_sec (-1 = nie gesehen)
Zeit (+): minute_of_hour, day_of_month, month, year, is_weekend, unix_timestamp
System: ram_free_mb (MemAvailable), cpu_load_1min (loadavg)
Activity: last_user_message_ago_sec
Memory: pinned_count (zusaetzlich zu memory_count)
Neue Funktion fuer Conditions:
near(lat, lon, radius_m) Haversine-Distanz von current_lat/lon
zum Punkt. False wenn keine Position bekannt.
Parser-Erweiterung:
ast.Call jetzt erlaubt, ABER nur fuer direkte Funktionsnamen aus der
Whitelist (_ALLOWED_FUNCTIONS = {"near"}). Keine Attribute-Access,
keine Keywords, Args nur Constants/Names/UnaryOp.
Selbsttest blockt korrekt:
__import__("os")... → "Funktionsaufruf nur ueber direkten Namen"
memory_count.__class__ → "Verbotener Ausdruck: Attribute"
(lambda: 1)() → "Funktionsaufruf nur ueber direkten Namen"
aria-brain/main.py
/triggers/conditions liefert jetzt zusaetzlich {functions:[...]} mit
Signaturen + Beschreibungen. current-Snapshot filtert callable() raus
damit JSON serialisierbar bleibt.
aria-brain/prompts.py + agent.py
build_triggers_section bekommt condition_funcs als 4tes Argument und
listet die im System-Prompt unter "Verfuegbare Funktionen". Operatoren-
Hinweis ergaenzt mit Beispielen + Regeln (keine Variablen in Funktions-
Args, keine Schachtelung).
diagnostic/index.html
Trigger-Create-Modal: Variablen-Info-Block zeigt jetzt sowohl Variablen
(mit aktuellen Werten) als auch Funktionen (Signatur + Beschreibung).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
31aa86a2a9 |
feat(brain+ui+app): Triggers — passive Aufweck-Quellen fuer ARIA
ARIA hatte bisher nur ein "User fragt → Brain antwortet"-Modell. Neu:
Trigger laufen passiv im Hintergrund (kein LLM-Call) und wecken ARIA
nur dann auf wenn ein Event tatsaechlich passiert.
Drei Typen, zwei aktuell implementiert:
timer — einmalig zu festem ISO-Timestamp ("erinner mich in 10min")
watcher — Polling alle N Sek einer Condition, feuert bei True mit Throttle
(z.B. "disk_free_gb < 5", max 1x/h)
cron — Platzhalter fuer spaeter
aria-brain/triggers.py
CRUD auf /data/triggers/<name>.json + /data/triggers/logs/<name>.jsonl.
create_timer, create_watcher, mark_fired, list_logs, etc.
aria-brain/watcher.py
Built-in Condition-Variablen: disk_free_gb, disk_free_pct, uptime_sec,
hour_of_day, day_of_week, rvs_connected, memory_count.
Sicherer Condition-Parser via ast — Whitelist auf Vergleich + BoolOp +
Name + Const. Kein eval, kein exec, keine Builtins.
aria-brain/background.py
Async Loop laeuft alle 30s, sammelt einmalig Variables, geht durch
Trigger-Liste, _should_fire-Check (Timer: fires_at vergangen / Watcher:
check_interval + throttle respektiert + condition true). Fire ruft
agent.chat(prompt, source="trigger") — ARIA bekommt das wie eine
Push-Nachricht und antwortet via Bridge → RVS → App.
aria-brain/main.py
/triggers/list, /{name}, /{name}/logs, /timer, /watcher, PATCH, DELETE,
/triggers/conditions (Variablen + aktuelle Werte). Lifespan-Handler
startet den Background-Loop beim Container-Start, stoppt beim Shutdown.
aria-brain/agent.py
Meta-Tools fuer ARIA: trigger_timer, trigger_watcher, trigger_cancel,
trigger_list. ARIA legt Trigger via Tool-Call selbst an wenn Stefan das
wuenscht. Side-Channel-Event 'trigger_created' wird in chat-Response
mitgeschickt damit App + Diagnostic eine Bubble zeigen.
aria-brain/prompts.py
Neue System-Prompt-Section: Liste aktiver Triggers + verfuegbare
Condition-Variablen mit aktuellen Werten + Operatoren-Erklaerung.
ARIA weiss damit immer was es schon gibt und welche Vars sie nutzen kann.
bridge/aria_bridge.py + rvs/server.js
trigger_created als neuer RVS-Message-Type, Bridge forwarded das aus
data.events analog zu skill_created.
diagnostic/index.html
Neuer Top-Tab "Trigger". Liste mit Type-Badges (⏱ TIMER / 👁 WATCHER),
Status, Fire-Count, last_fired. Aktivieren/Deaktivieren + Löschen pro
Trigger. "+ Neu"-Modal mit Type-Dropdown, Timer-Minuten oder
Watcher-Condition + Vars-Anzeige + Throttle. Info-Modal-Eintrag mit
Erklaerung. Live-Bubble im Chat wenn ARIA selbst einen anlegt.
android/src/screens/ChatScreen.tsx
trigger_created RVS-Handler → eigene Bubble (gelber Border, "⏰ ARIA
hat einen Trigger angelegt", Type/Detail/Message/Zeit). ChatMessage
bekam triggerCreated-Feld. Lokal-only-Schutz beim Server-Sync analog
zu skill_created.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
b2f7d6dda2 |
feat(brain+diagnostic): Token/Call-Metrics mit Subscription-Plan-Tracking
Stefan hat den Max 5x Plan (~\$90-100/Monat), ungefaehres Limit 225 Calls pro
5h-Fenster fuer Sonnet. Damit nicht in eine Tool-Loop-Schleife laufen ohne
es zu merken: kleine Metrics-Pipeline, sichtbar in der Diagnostic.
aria-brain/metrics.py
Append-only JSONL Logger unter /data/metrics.jsonl. Pro Claude-Call eine
Zeile {ts, model, in, out} mit Token-Schaetzung (chars/4, Anthropic-
Heuristik). aggregate(window) zaehlt die letzten N Sekunden.
Auto-Rotate bei 50k Zeilen → 25k behalten (~70 KB/Monat bei 1k Calls/Tag,
Cap also weit oben).
aria-brain/proxy_client.py
chat_full() ruft am Ende metrics.log_call(model, messages_in, reply).
Failed/exception-Pfade loggen nicht (sonst false positives).
aria-brain/main.py
GET /metrics/calls → {h1, h5, h24, d30}, jedes Window mit calls,
tokens_in, tokens_out, by_model.
diagnostic/index.html
Neue Card "Token / Calls" im Gehirn-Tab. Plan-Dropdown
(Pro / Max 5x / Max 20x / Custom), localStorage-persistiert. 4 Metric-
Zellen fuer 1h/5h/24h/30d mit Calls + Tokens. Progress-Bar oben zeigt
5h-Counter gegen Plan-Limit. Warn-Klassen: gelb bei 80%, rot bei 90%.
Auto-Refresh alle 30s wenn Gehirn-Tab offen, plus bei Tab-Wechsel.
Info-Modal erklaert die Limits + dass HTTP-Call != User-Frage (Tool-Use
kann pro Frage bis zu 8 Calls verursachen).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
aa077f60e6 |
fix(diagnostic+brain): Sprachmodell-Einstellung auf runtime.json umgestellt
War kaputt nach OpenClaw-Abriss: handleGetModel/handleSetModel haben gegen
aria-core (dockerExec + node-script in den Container) gearbeitet, der gibt's
nicht mehr.
diagnostic/server.js
- handleGetModel/handleSetModel lesen/schreiben jetzt brainModel in
/shared/config/runtime.json
- RUNTIME_CONFIG_FIELDS um "brainModel" erweitert
- Tote Variante (findSettingsFile + base64-node-script) komplett raus
aria-brain/proxy_client.py
- Liest brainModel aus runtime.json beim Container-Start
- Fallback: BRAIN_MODEL env → "claude-sonnet-4" Default
- Bei Aenderung in Diagnostic: aria-brain restarten damit's greift
(Hinweis steht in der UI)
diagnostic/index.html
- Section "Model" → "Sprachmodell (Brain)"
- Hinweis-Block mit Default-Erklaerung und Restart-Hinweis
- Modelle: claude-sonnet-4 (default), claude-opus-4, claude-haiku-4-5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
70d1500096 |
feat(brain): Phase B — Vector-DB-Memory, Conversation-Loop, Skills, Tool-Use
OpenClaw (aria-core) ist raus, ARIA laeuft jetzt mit eigenem Agent-Framework
im aria-brain Container. Vector-DB-basiertes Gedaechtnis statt Sessions,
eigener Conversation-Loop mit Hot+Cold-Memory + Rolling Window, Tool-Use
fuer Skills, Memory-Destillat-Pipeline.
aria-brain/ (neuer Container)
- main.py FastAPI auf 8080, alle Endpoints
- agent.py Conversation-Loop mit Tool-Use (skill_create + run_<skill>)
- conversation.py Rolling Window, JSONL-Persistenz, Distill-Marker
- proxy_client.py httpx-Wrapper zum Claude-Proxy, OpenAI-Format
- prompts.py System-Prompt aus Hot+Cold+Skills
- migration.py Markdown-Parser fuer brain-import/ → atomare Memories
- skills.py Filesystem-Layer fuer /data/skills/<name>/ (Python-only,
venv pro Skill, tar.gz Export/Import, Run-Logs)
- memory/ Embedder (sentence-transformers, multilingual MiniLM)
+ VectorStore (Qdrant-Wrapper)
docker-compose.yml
- aria-core (OpenClaw) raus, openclaw-config Volume raus
- aria-brain Service (FastAPI + Memory)
- aria-qdrant Service (Vector-DB) mit Bind-Mount aria-data/brain/qdrant/
- Diagnostic teilt jetzt Netzwerk mit Bridge (vorher: aria-core)
- Brain bekommt SSH-Mount fuer aria-wohnung + /import fuer brain-import/
bridge/aria_bridge.py
- send_to_core → HTTP-Call an aria-brain:8080/chat (statt OpenClaw-WS)
- aria-core-spezifische Handler raus: doctor_fix, aria_restart,
aria_session_reset, Auto-Compact-Logik, OpenClaw-Handshake
- Generischer container_restart-Handler (Whitelist Bridge/Brain/Qdrant)
- Side-Channel-Events aus /chat-Response (z.B. skill_created) werden
als RVS-Events forwarded
- file_list_request / file_delete_request → an Diagnostic forwarded
- Tote OpenClaw-Connection-Logik bleibt im Code als Referenz (nicht aktiv)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|