From 4082a6bf2a1a4f7e718db077a14aef9f35231399 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Mon, 11 May 2026 02:24:30 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Auto-Compact=20nach=20N=20User-Messages?= =?UTF-8?q?=20=E2=80=94=20verhindert=20E2BIG=20bei=20langer=20Session?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E2BIG (Argument list too long) tritt auf wenn aria-core's Subprocess- Spawn das Linux argv-Limit (~128KB-2MB) sprengt. Bei >140 Messages samt Memory + System-Prompt + Tools laeuft das voll, ARIA antwortet nur noch leer auf jede Anfrage. Bridge zaehlt jetzt User-Nachrichten in send_to_core; bei COMPACT_AFTER_MESSAGES (env, default 140) wird automatisch: - Sessions geleert (rm sessions/*.jsonl + sessions.json = {}) - aria-core neu gestartet - User informiert "Konversation komprimiert, letzte Nachricht nochmal" Plus manueller "🧹 Konversation komprimieren"-Button in App-Settings und 🧹 Compact-Button in Diagnostic-Thinking-Indicator. Co-Authored-By: Claude Opus 4.7 (1M context) --- android/src/screens/SettingsScreen.tsx | 25 ++++++++++++ bridge/aria_bridge.py | 54 ++++++++++++++++++++++++++ diagnostic/index.html | 13 +++++++ diagnostic/server.js | 37 ++++++++++++++++++ docker-compose.yml | 1 + rvs/server.js | 1 + 6 files changed, 131 insertions(+) diff --git a/android/src/screens/SettingsScreen.tsx b/android/src/screens/SettingsScreen.tsx index 1c537b9..9895032 100644 --- a/android/src/screens/SettingsScreen.tsx +++ b/android/src/screens/SettingsScreen.tsx @@ -1329,6 +1329,31 @@ const SettingsScreen: React.FC = () => { > {'🚨 ARIA hart neu starten'} + + Konversation komplett zuruecksetzen β€” alle bisherigen Nachrichten + aus ARIA's Session loeschen + Container neu. Anders als der harte + Restart wird hier auch ARIA's Erinnerung an die laufende + Konversation gewipt. Geschieht automatisch alle 140 Nachrichten + (Bridge-Setting COMPACT_AFTER_MESSAGES). + + { + Alert.alert( + 'Konversation komprimieren?', + 'Alle Nachrichten in ARIAs aktueller Session werden geloescht und der Container neu gestartet. ARIA vergisst den bisherigen Gespraechsverlauf.', + [ + { text: 'Abbrechen', style: 'cancel' }, + { text: 'Komprimieren', style: 'destructive', onPress: () => { + rvs.send('aria_session_reset' as any, {}); + ToastAndroid.show('Session wird zurueckgesetzt…', ToastAndroid.LONG); + }}, + ], + ); + }} + > + {'🧹 Konversation komprimieren'} + )} diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index b993ee8..36b4e41 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -549,6 +549,12 @@ class ARIABridge: # Beeinflusst das Timeout fuer stt_request β€” bei "loading" warten wir laenger, # weil das Modell beim ersten Request noch ~1-2 Min runtergeladen werden kann. self._remote_stt_ready: bool = False + # User-Message-Counter fuer Auto-Compact. Bei zu langer Konversation + # sprengt die argv-Liste beim Claude-Subprocess-Spawn (E2BIG). Bei + # COMPACT_AFTER erreicht β†’ Sessions reset + Container restart. + # Counter ueberlebt Bridge-Restart nicht (frischer Zaehler beim Start ok). + self._user_message_count: int = 0 + self._compact_after = int(os.getenv("COMPACT_AFTER_MESSAGES", "140")) # Pending Files: wenn die App ein Bild + Text gleichzeitig schickt, kommen # zwei separate RVS-Events ('file' und 'chat') β€” wir buffern die Files # kurz und mergen sie mit dem nachfolgenden Chat-Text zu einer einzigen @@ -1170,12 +1176,53 @@ class ARIABridge: await self.send_to_core(text, source="app-file+chat") return True + async def _trigger_session_reset(self) -> None: + """Sessions loeschen + Container restart via Diagnostic HTTP-API.""" + try: + req = urllib.request.Request( + "http://localhost:3001/api/aria-session-reset", + data=b"{}", + method="POST", + headers={"Content-Type": "application/json"}, + ) + def _do_reset(): + try: + with urllib.request.urlopen(req, timeout=45) as resp: + return resp.status + except Exception as e: + return f"err:{e}" + result = await asyncio.get_event_loop().run_in_executor(None, _do_reset) + logger.info("[core] Session-Reset Result: %s", result) + except Exception as e: + logger.warning("[core] Session-Reset Trigger fehlgeschlagen: %s", e) + async def send_to_core(self, text: str, source: str = "bridge") -> None: """Sendet Text an aria-core (OpenClaw chat.send Protokoll).""" if self.ws_core is None: logger.error("[core] Nicht verbunden β€” Nachricht verworfen: '%s'", text[:60]) return + # Auto-Compact: bei zu vielen User-Messages laeuft argv beim Subprocess- + # Spawn ueber (E2BIG). Vor send pruefen, ggf. Sessions resetten. + if source.startswith("app") and self._compact_after > 0: + self._user_message_count += 1 + if self._user_message_count >= self._compact_after: + logger.warning("[core] Auto-Compact: %d Messages erreicht β€” Session-Reset", + self._user_message_count) + self._user_message_count = 0 + # Reset triggern via Diagnostic (asynchron, blockiert send nicht) + asyncio.create_task(self._trigger_session_reset()) + # User informieren β€” der naechste Request kommt erst nach Restart durch + await self._send_to_rvs({ + "type": "chat", + "payload": { + "text": f"[Compact] Konversation war lang ({self._compact_after} Nachrichten) β€” Session wurde geleert, ARIA startet frisch. Deine letzte Nachricht bitte gleich nochmal senden.", + "sender": "aria", + }, + "timestamp": int(asyncio.get_event_loop().time() * 1000), + }) + return + # Aktive Session vom Diagnostic holen self._fetch_active_session() @@ -1580,6 +1627,13 @@ class ARIABridge: except Exception as e: logger.warning("[rvs] file_saved konnte nicht an App gesendet werden: %s", e) + elif msg_type == "aria_session_reset": + # Manueller Compact-Trigger: Sessions weg + Restart + logger.warning("[rvs] aria_session_reset Request von App") + self._user_message_count = 0 + asyncio.create_task(self._trigger_session_reset()) + return + elif msg_type == "aria_restart": # App-Button "ARIA hart neu starten" β†’ docker restart aria-core # via Diagnostic (der hat den Docker-Socket gemountet). diff --git a/diagnostic/index.html b/diagnostic/index.html index 993f728..3cb1d75 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -290,6 +290,7 @@ 💭 ARIA denkt...
+
@@ -1891,6 +1892,18 @@ .catch(err => addLog('error', 'server', 'Restart Request fehlgeschlagen: ' + err.message)); } + // ── Compact / Session-Reset ────── + function ariaSessionReset() { + if (!confirm('Konversation komprimieren: alle Nachrichten in ARIAs aktueller Session werden geloescht und der Container neu gestartet. ARIA vergisst den bisherigen Gespraechsverlauf. Sicher?')) return; + fetch('/api/aria-session-reset', { method: 'POST' }) + .then(r => r.json()) + .then(data => { + if (data.ok) addLog('info', 'server', 'Session geleert, ARIA neu gestartet'); + else addLog('error', 'server', 'Reset fehlgeschlagen: ' + (data.error || '')); + }) + .catch(err => addLog('error', 'server', 'Reset Request fehlgeschlagen: ' + err.message)); + } + // ── Abbrechen ────────────────────────────── function cancelRequest() { send({ action: 'cancel_request' }); diff --git a/diagnostic/server.js b/diagnostic/server.js index c3ff2dc..6136c91 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -1358,6 +1358,43 @@ const server = http.createServer((req, res) => { res.end(JSON.stringify({ ok: false, error: err.message })); }); return; + } else if (req.url === "/api/aria-session-reset" && req.method === "POST") { + // Sessions weg + Container neu β€” fuer Compact-After-N-Messages. + // E2BIG bei zu langen Sessions: argv beim Subprocess-spawn ueberschritten. + log("warn", "server", "HTTP /api/aria-session-reset β€” Sessions loeschen + Restart"); + broadcast({ type: "watchdog", status: "fixing", message: "Sessions werden geleert β€” ARIA bekommt frischen Start" }); + dockerExec("aria-core", "rm -f /home/node/.openclaw/agents/main/sessions/*.jsonl /home/node/.openclaw/agents/main/sessions/*.lock 2>&1 && echo '{}' > /home/node/.openclaw/agents/main/sessions/sessions.json") + .then(() => { + // Restart via Docker-API (gleicher Pfad wie /api/aria-restart) + const restartReq = http.request({ + socketPath: "/var/run/docker.sock", + path: "/containers/aria-core/restart?t=10", + method: "POST", + headers: { "Content-Length": 0 }, + timeout: 30000, + }, (dRes) => { + if (dRes.statusCode === 204) { + log("info", "server", "aria-session-reset OK"); + broadcast({ type: "watchdog", status: "fixed", message: "Sessions geleert, ARIA neu gestartet" }); + res.writeHead(200, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ ok: true })); + } else { + res.writeHead(500, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ ok: false, error: `Docker-API ${dRes.statusCode}` })); + } + }); + restartReq.on("error", (err) => { + res.writeHead(500, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ ok: false, error: err.message })); + }); + restartReq.end(); + }) + .catch((err) => { + log("error", "server", `aria-session-reset Cleanup fehlgeschlagen: ${err.message}`); + res.writeHead(500, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ ok: false, error: err.message })); + }); + return; } else if (req.url === "/api/aria-restart" && req.method === "POST") { // Harter Restart β€” fuer Faelle wo doctor --fix nicht reicht (alive aber // haengender Run). Geht ueber Docker-API (Socket), kein CLI noetig. diff --git a/docker-compose.yml b/docker-compose.yml index b58483c..a612513 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,6 +87,7 @@ services: - RVS_TLS=${RVS_TLS:-true} - RVS_TLS_FALLBACK=${RVS_TLS_FALLBACK:-true} - RVS_TOKEN=${RVS_TOKEN:-} + - COMPACT_AFTER_MESSAGES=${COMPACT_AFTER_MESSAGES:-140} restart: unless-stopped # ─── Diagnostic (Selbstcheck-UI und Einstellungen) ──── diff --git a/rvs/server.js b/rvs/server.js index 55570e6..fd8a086 100644 --- a/rvs/server.js +++ b/rvs/server.js @@ -21,6 +21,7 @@ const ALLOWED_TYPES = new Set([ "file_from_aria", "doctor_fix", "aria_restart", + "aria_session_reset", "xtts_delete_voice", "voice_preload", "voice_ready", "stt_request", "stt_response",