diff --git a/android/android/app/build.gradle b/android/android/app/build.gradle index d22d439..57803c1 100644 --- a/android/android/app/build.gradle +++ b/android/android/app/build.gradle @@ -79,7 +79,7 @@ android { applicationId "com.ariacockpit" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 + versionCode 107 versionName "0.0.1.7" // Fallback fuer Libraries mit Product Flavors missingDimensionStrategy 'react-native-camera', 'general' diff --git a/android/src/screens/SettingsScreen.tsx b/android/src/screens/SettingsScreen.tsx index 38cebbc..f9ae2bb 100644 --- a/android/src/screens/SettingsScreen.tsx +++ b/android/src/screens/SettingsScreen.tsx @@ -601,7 +601,7 @@ const SettingsScreen: React.FC = () => { {'\u00DC'}ber ARIA Cockpit - Version 0.0.1.6 + Version 0.0.1.7 Stefans Kommandozentrale f{'\u00FC'}r ARIA.{'\n'} Gebaut mit React Native + TypeScript. diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 26418a0..76a8d5c 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -1216,8 +1216,10 @@ class ARIABridge: logger.info("Keine Sprache erkannt — ignoriert") except sd.PortAudioError: - logger.error("Audio-Geraet nicht verfuegbar — warte 5 Sekunden") - await asyncio.sleep(5) + if not hasattr(self, '_audio_warned'): + logger.warning("Audio-Geraet nicht verfuegbar — lokales Mikrofon deaktiviert (kein Spam mehr)") + self._audio_warned = True + await asyncio.sleep(60) # 60s statt 5s — spart Log-Spam except Exception: logger.exception("Fehler in der Audio-Schleife") await asyncio.sleep(1) diff --git a/diagnostic/index.html b/diagnostic/index.html index 7485d89..ec5f4e6 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -201,6 +201,9 @@
+
@@ -216,6 +219,9 @@
+
@@ -507,6 +513,11 @@ if (msg.type === 'state') { updateState(msg.state); return; } if (msg.type === 'log') { addLog(msg.entry.level, msg.entry.source, msg.entry.message, msg.entry.ts); return; } + if (msg.type === 'agent_activity') { + updateThinkingIndicator(msg); + return; + } + if (msg.type === 'chat_final') { addChat('received', msg.text, 'chat:final'); return; @@ -883,6 +894,10 @@ return `${match}`; }); const html = `${linked}
${escapeHtml(meta)} — ${new Date().toLocaleTimeString('de-DE')}
`; + + // Thinking-Indikator ausblenden bei neuer Nachricht + updateThinkingIndicator({ activity: 'idle' }); + // In beide Chat-Boxen schreiben (normal + Vollbild) for (const box of [chatBox, document.getElementById('chat-box-fs')]) { if (!box) continue; @@ -930,6 +945,52 @@ if (e.key === 'Escape' && chatFullscreen) toggleChatFullscreen(); }); + // ── Thinking-Indikator ───────────────────────────── + let thinkingTimeout = null; + const TOOL_LABELS = { + 'Bash': '\uD83D\uDDA5\uFE0F Shell-Befehl', + 'WebFetch': '\uD83C\uDF10 Webseite abrufen', + 'WebSearch': '\uD83D\uDD0D Suche', + 'Read': '\uD83D\uDCC4 Datei lesen', + 'Write': '\u270D\uFE0F Datei schreiben', + 'Edit': '\u270D\uFE0F Datei bearbeiten', + 'Grep': '\uD83D\uDD0D Code durchsuchen', + 'Glob': '\uD83D\uDCC1 Dateien suchen', + 'Agent': '\uD83E\uDD16 Sub-Agent', + }; + function updateThinkingIndicator(msg) { + const indicators = [ + document.getElementById('thinking-indicator'), + document.getElementById('thinking-indicator-fs'), + ]; + const texts = [ + document.getElementById('thinking-text'), + document.getElementById('thinking-text-fs'), + ]; + + if (msg.activity === 'idle') { + indicators.forEach(el => { if (el) el.style.display = 'none'; }); + if (thinkingTimeout) { clearTimeout(thinkingTimeout); thinkingTimeout = null; } + return; + } + + let label = 'ARIA denkt...'; + if (msg.activity === 'tool' && msg.tool) { + label = TOOL_LABELS[msg.tool] || `\uD83D\uDD27 ${msg.tool}`; + } else if (msg.activity === 'assistant') { + label = 'ARIA schreibt...'; + } + + indicators.forEach(el => { if (el) el.style.display = 'block'; }); + texts.forEach(el => { if (el) el.textContent = label; }); + + // Auto-Hide nach 2min (falls idle Event verpasst wird — ARIA arbeitet max 15min) + if (thinkingTimeout) clearTimeout(thinkingTimeout); + thinkingTimeout = setTimeout(() => { + indicators.forEach(el => { if (el) el.style.display = 'none'; }); + }, 120000); + }); + function openLightbox(mediaType, url) { const lb = document.getElementById('lightbox'); if (mediaType === 'video') { diff --git a/diagnostic/server.js b/diagnostic/server.js index 0fb1d4e..95c6596 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -74,8 +74,8 @@ function pipelineStart(method, text) { pipelineStartTime = Date.now(); if (pipelineTimeout) clearTimeout(pipelineTimeout); pipelineTimeout = setTimeout(() => { - if (pipelineActive) pipelineEnd(false, "Timeout — keine Antwort nach 60s"); - }, 60000); + if (pipelineActive) pipelineEnd(false, "Timeout — keine Antwort nach 10min"); + }, 600000); plog(`━━━ Pipeline Start: ${method} ━━━`); plog(`Nachricht: "${text}"`); } @@ -319,10 +319,23 @@ function handleGatewayMessage(msg) { if (event === "agent") { const data = payload.data || {}; const delta = data.delta || ""; - if (delta && payload.stream === "assistant") { + const stream = payload.stream || ""; + + if (delta && stream === "assistant") { broadcast({ type: "chat_delta", delta, payload }); } - // agent Events nicht einzeln loggen (zu viele) + + // Tool-Nutzung erkennen und broadcasten + if (stream === "tool_use" || data.type === "tool_use") { + const toolName = data.name || data.tool || payload.tool || ""; + if (toolName) { + broadcast({ type: "agent_activity", activity: "tool", tool: toolName, data }); + log("info", "gateway", `Tool: ${toolName}`); + } + } + + // Genereller Activity-Heartbeat (ARIA denkt) + broadcast({ type: "agent_activity", activity: stream || "thinking" }); return; } @@ -338,6 +351,7 @@ function handleGatewayMessage(msg) { log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`); if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`); broadcast({ type: "chat_final", text, payload }); + broadcast({ type: "agent_activity", activity: "idle" }); return; } diff --git a/issue.md b/issue.md index 006a5f8..b56b77c 100644 --- a/issue.md +++ b/issue.md @@ -4,3 +4,4 @@ cache leeren, bilder werden nicht neu geladen beim antippen. autoload geht nicht wenn man auf das ohr zum hören klickt stürzt ab aria liest die nachrichten nicht vor +autoscroll geht doch noch nicht zur letzten nachricht diff --git a/release.sh b/release.sh index 68c2f62..e915378 100755 --- a/release.sh +++ b/release.sh @@ -58,17 +58,19 @@ echo -e "${GREEN}[1/5] Versionsnummern auf $VERSION setzen...${NC}" sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" android/package.json echo -e " ${GREEN}✓${NC} package.json → $VERSION" -# build.gradle: versionName + versionCode (aus Major.Minor.Patch berechnen) -MAJOR=$(echo "$VERSION" | cut -d. -f1) -MINOR=$(echo "$VERSION" | cut -d. -f2) -PATCH=$(echo "$VERSION" | cut -d. -f3) -VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) +# build.gradle: versionName + versionCode (aus Version berechnen) +# Unterstuetzt 3-stellig (1.2.3) und 4-stellig (0.0.1.7) +IFS='.' read -ra VER_PARTS <<< "$VERSION" +V1=${VER_PARTS[0]:-0}; V2=${VER_PARTS[1]:-0}; V3=${VER_PARTS[2]:-0}; V4=${VER_PARTS[3]:-0} +VERSION_CODE=$((V1 * 1000000 + V2 * 10000 + V3 * 100 + V4)) +# Mindestens 1 (Android erfordert versionCode >= 1) +[ "$VERSION_CODE" -lt 1 ] && VERSION_CODE=1 sed -i "s/versionName \"[^\"]*\"/versionName \"$VERSION\"/" android/android/app/build.gradle sed -i "s/versionCode [0-9]*/versionCode $VERSION_CODE/" android/android/app/build.gradle echo -e " ${GREEN}✓${NC} build.gradle → versionName $VERSION, versionCode $VERSION_CODE" -# SettingsScreen: Anzeige-Version -sed -i "s/Version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]* [^<]*/Version $VERSION /" android/src/screens/SettingsScreen.tsx +# SettingsScreen: Anzeige-Version (beliebiges Versionsformat) +sed -i "s/Version [0-9][0-9.]*[^<]*/Version $VERSION /" android/src/screens/SettingsScreen.tsx echo -e " ${GREEN}✓${NC} SettingsScreen → Version $VERSION" echo ""