StreamConsumer.Incremental now returns StreamState (content, reasoning, tools, query, sources) instead of just visible text. All sentinel sections are parsed and structured: - \x01 REASONING: captured as separate string - \x02 SOURCES: parsed as List<SearchSource> (title, url, snippet) - \x03 TOOL: parsed as List<ToolEvent> (type, name, args, result, elapsed) - \x04 QUERY: captured as search query string UI components: - ReasoningBlock: collapsible "Gedankengang" with expand/collapse animation - ToolEventsBlock: shows tool-call steps with status icons (start/end/error) - SourcesBlock: web search sources as compact cards with domain extraction Message model extended with reasoning, tools, query, sources fields. KaizenApi.chat() returns Flow<StreamState> instead of Flow<String>. ChatScreen wires all fields to Message during streaming. Fixed: empty append() on Incremental no longer prematurely ends tool phase. StreamConsumerTest rewritten to verify all parsed sections. |
||
|---|---|---|
| app | ||
| gradle | ||
| .gitignore | ||
| build.gradle.kts | ||
| CLAUDE.md | ||
| DESIGN.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LATER.md | ||
| OFFLINE_CACHE_ADD.md | ||
| README.md | ||
| settings.gradle.kts | ||
Kaizen — Android
Native Android-App für Kaizen (Jetpack Compose + Kotlin). Kein WebView, kein Tauri.
Phase 1 — echtes Backend (aktiv)
Der Feel-Prototyp redet jetzt mit dem echten Kaizen-Backend — echte, gestreamte
Antworten statt fakeReply. Die App ist deployment-agnostisch: ein Binary, das auf
die offizielle Instanz ODER jeden Self-Host zeigt, über einen einheitlichen
Authorization: Bearer-Contract. SaaS vs. Self-Host ist nur „welche Base-URL".
Drin:
- In-App-Login (
auth/LoginScreen.kt): Server-URL (Default = offizielle Instanz, editierbar = die „offiziell/Self-Host"-Adresse) + E-Mail + Passwort →POST {baseUrl}/api/v1/auth/token→kzn_-Token. Kein Token-Copy-Paste. - Sichere Token-Ablage (
net/SecureStore.kt):EncryptedSharedPreferences(AES-256-GCM, Schlüssel im Android-KeyStore/StrongBox) — der Token liegt nie im Klartext, auch nicht auf gerooteten Geräten. - Modell vom Server (
net/SessionViewModel.kt): nach dem Login holt die App perGET /api/v1/medas vom Nutzer unter/settings/modelsgewählte Default-Modell — kein hartgecodetes Modell. - Echtes Streaming (
net/KaizenApi.kt+net/StreamConsumer.kt):POST /api/v1/chat, liest dentext/plain-Chunked-Stream inkrementell und strippt die Sentinels (Port vonlib/chat/stream-consumer.tsaus dem Backend, JVM-unit-getestet). - Design + Haptik + Orb unverändert — nur die Datenquelle hat sich geändert.
Bewusst noch nicht drin (siehe LATER.md): Conversation-History vom Server,
Passkey-Login nativ, Bild/Video, Biometric-Lock, WebSocket-Streaming.
Bauen & laufen
Wichtig: Auf dem echten S25 Ultra laufen lassen, nicht im Emulator — der Emulator kann keine echte Haptik, und genau die wollen wir fühlen.
- Projekt in Android Studio öffnen, Gradle syncen (neue Deps: OkHttp, kotlinx-serialization, security-crypto + INTERNET-Permission).
- S25 Ultra per Wireless Debugging oder USB verbinden.
- Run ▶ auf das Gerät → Login-Screen erscheint. Server-URL prüfen, einloggen.
DEFAULT_BASE_URL (net/ServerConfig.kt) ist die vorausgefüllte Instanz-Adresse —
bei Domainwechsel hier anpassen.
Der Test
Eine Woche nutzen. Geht der Finger anfangen hierher statt zu Gemini → wir bauen voll aus (History, Passkey, Medien = Phase 2). Wenn nicht → wir wissen billig, woran's liegt.