Files
gott/ARCHITECTURE.md
T
Meleki 95b3683910 docs: Multi-Agent Review-System in ARCHITECTURE.md und ARCHITECTURE_QUICK.md dokumentiert
ARCHITECTURE.md:
- Inhaltsverzeichnis: neues Kapitel 18 (Multi-Agent Review-System)
- Datenmodell: sessions-Tabelle um review_agent_id, max_review_iterations erweitert
- Entity-Relationship: Review-Agent-Beziehung
- ChatContext.cs Kommentar: + ReviewContext
- Chat-Pipeline: Review-Phase (4b) nach end_turn mit Diagramm
- SSE-Events: review_* Events
- Neues Kapitel mit Architektur, Konfiguration, Backend-Flow,
  ReviewContext Model, SSE-Events, DB-Persistierung, Frontend

ARCHITECTURE_QUICK.md:
- sessions-Tabelle: ReviewAgentId, MaxReviewIterations
- Chat-Pipeline: Review-Phase eingefügt
- ui.js Kommentar: Review-Rendering
- Neues Kapitel Multi-Agent Review-System (Kurzform)
2026-03-06 19:13:41 +01:00

1247 lines
59 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ARCHITECTURE.md – Gott
**Gott** ist eine selbst-gehostete AI-Chat-Plattform, gebaut auf **.NET 10** und der **Anthropic Claude API**. Sie bietet Multi-User-Support, persistente Agent-Memory, Datei-/Shell-Tools, Telegram-Integration, Cronjobs und eine Krea-Bildgenerierung – alles über ein Vanilla-JavaScript-Frontend gesteuert.
---
## Inhaltsverzeichnis
1. [Überblick](#überblick)
2. [Tech-Stack](#tech-stack)
3. [Projektstruktur](#projektstruktur)
4. [Datenmodell](#datenmodell)
5. [Backend-Architektur](#backend-architektur)
- [Program.cs – Startup](#programcs--startup)
- [Services](#services)
- [Endpoints (API)](#endpoints-api)
- [Middleware](#middleware)
- [Models](#models)
- [Templates](#templates)
6. [Frontend-Architektur](#frontend-architektur)
7. [Chat-Pipeline](#chat-pipeline)
8. [Tool-System](#tool-system)
9. [Agent-System](#agent-system)
10. [System-Prompt-Aufbau](#system-prompt-aufbau)
11. [Memory-System](#memory-system)
12. [Skill-System](#skill-system)
13. [Telegram-Integration](#telegram-integration)
14. [Cronjob-System](#cronjob-system)
15. [Krea-Bildgenerierung](#krea-bildgenerierung)
16. [Auth & Multi-User](#auth--multi-user)
17. [Admin-Benutzerverwaltung](#admin-benutzerverwaltung)
18. [Multi-Agent Review-System](#multi-agent-review-system)
19. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages)
20. [Workspace & File-Explorer](#workspace--file-explorer)
21. [Avatar-System](#avatar-system)
22. [LaTeX/Math-Rendering](#latexmath-rendering)
23. [Windows Desktop App](#windows-desktop-app)
24. [Deployment](#deployment)
25. [Konfiguration](#konfiguration)
26. [Datenbank-Migrationen](#datenbank-migrationen)
---
## Überblick
```
┌─────────────────────────────────────────────────────────────┐
│ Browser / WebView2 │
│ Vanilla JS SPA (wwwroot/) │
├─────────────────────────────────────────────────────────────┤
│ ASP.NET 10 Minimal API │
│ http://0.0.0.0:19000 │
├──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬────┤
│ Auth │ Chat │ Hist │ Sess │Agent │ Tele │ Cron │ Krea │Adm │
│ │ SSE │ │ │ │ gram │ jobs │ Img │ in │
├──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴────┤
│ Services Layer │
│ AnthropicService · ChatService · ToolService │
│ DatabaseService · MemoryService · SkillService │
│ TelegramService · CronjobService · KreaService │
├─────────────────────────────────────────────────────────────┤
│ SQLite (data/gott.db) via Dapper │
│ Filesystem (data/agents/, data/skills/) │
├─────────────────────────────────────────────────────────────┤
│ Anthropic Claude API (claude-sonnet/opus/haiku) │
│ Krea API (Bildgenerierung) │
│ Telegram Bot API │
└─────────────────────────────────────────────────────────────┘
```
---
## Tech-Stack
| Schicht | Technologie |
|---------------|-----------------------------------------------------------|
| **Runtime** | .NET 10 (ohne Nullable, ohne Implicit Usings) |
| **Web** | ASP.NET Minimal API, Kestrel |
| **Datenbank** | SQLite via Microsoft.Data.Sqlite + Dapper |
| **AI** | Anthropic Claude API (Streaming SSE + Non-Streaming) |
| **Images** | SixLabors.ImageSharp (Resize, Thumbnails) |
| **Cron** | Cronos (Cron-Expression-Parsing) |
| **Frontend** | Vanilla JavaScript SPA (kein Framework) |
| **Markdown** | marked.js (Client-side Rendering) |
| **LaTeX** | KaTeX 0.16.21 (Math-Rendering, self-hosted mit woff2-Fonts) |
| **Editor** | CodeMirror 5 + EasyMDE |
| **Icons** | Font Awesome |
| **Fonts** | Inter, JetBrains Mono (self-hosted) |
| **Desktop** | WPF + WebView2 (.NET 10, Windows-only) |
---
## Projektstruktur
```
gott/
├── Program.cs # Startup, DI, Middleware, Routing
├── gott.csproj # Projekt-Definition (.NET 10)
├── appsettings.json # Konfiguration (Tokens, Limits)
├── deploy.sh # Deployment-Script (systemctl)
│
├── Models/ # Daten-Modelle (Records)
│ ├── UserRecord.cs
│ ├── AgentRecord.cs
│ ├── SessionRecord.cs
│ ├── ChainRecord.cs
│ ├── MessageRecord.cs # + AttachmentRecord
│ ├── ArchiveRecord.cs
│ ├── CronjobRecord.cs
│ ├── KreaImageRecord.cs
│ ├── SharedMessageRecord.cs # Geteilte Nachrichten (Share-Token)
│ ├── ChatModels.cs # ChatMessage, ChatRequest, AttachmentInfo/Block
│ └── ChatContext.cs # Runtime-Kontext für einen Chat + ReviewContext
│
├── Services/ # Business-Logic
│ ├── DatabaseService.cs # SQLite CRUD, Schema, Migrationen
│ ├── AuthService.cs # Token-basierte Authentifizierung
│ ├── ChatService.cs # Kontext-Aufbau, History-Builder
│ ├── AnthropicService.cs # Claude API (Streaming + Non-Streaming)
│ ├── ToolService.cs # Tool-Definitionen + Ausführung
│ ├── SystemPromptBuilder.cs # Modularer System-Prompt aus Blöcken
│ ├── MemoryService.cs # Agent-Gedächtnis (Dateisystem)
│ ├── SkillService.cs # Skill-Verwaltung (Dateisystem)
│ ├── TelegramService.cs # Telegram Bot API Integration
│ ├── CronjobService.cs # BackgroundService für Cron-Tasks
│ ├── KreaService.cs # Krea Image Generation API
│ ├── ImageService.cs # Bild-Resize (Avatar, Session-Image)
│ ├── StreamingTracker.cs # Tracking aktiver Streams + CancellationTokenSource für Stop
│ └── WorkspaceHelper.cs # Workspace-Pfad-Auflösung (relativ → absolut)
│
├── Endpoints/ # HTTP API (Minimal API)
│ ├── AuthEndpoint.cs # Login, Logout, Me
│ ├── ChatEndpoint.cs # POST /api/chat (SSE Streaming)
│ ├── HistoryEndpoint.cs # History, Archive, Attachments, Marks
│ ├── SessionEndpoint.cs # CRUD + SystemPrompt, Exec, Git-Status
│ ├── AgentEndpoint.cs # CRUD
│ ├── WorkspaceEndpoint.cs # File-Tree, File-Read/Write, Browse, Agent-Files
│ ├── TelegramEndpoint.cs # Webhook, Config, Registration
│ ├── CronjobEndpoint.cs # CRUD + Toggle, Manual Run
│ ├── KreaEndpoint.cs # Generate, List, File-Serve
│ ├── SkillEndpoint.cs # List, Agent-Skill-Mapping, Output-Files
│ ├── SharedEndpoint.cs # Nachrichten teilen (öffentliche Links)
│ ├── AdminEndpoint.cs # Admin: User-CRUD, Impersonate (Admin-only)
│ ├── ProfileEndpoint.cs # Profil, Persona, Passwort
│ ├── SettingsEndpoint.cs # User-Settings (JSON blob)
│ ├── BackupEndpoint.cs # Backup als tar.gz Download
│ └── InfoEndpoint.cs # Build-Zeitstempel
│
├── Middleware/
│ └── AuthMiddleware.cs # Cookie-basierte Token-Validierung
│
├── Extensions/
│ └── HttpContextExtensions.cs # GetUserId(), IsAdmin()
│
├── Templates/ # Standard-Templates für neue Agents
│ ├── IDENTITY.md # Wer bin ich? (Name, Sprache)
│ ├── SOUL.md # Persönlichkeit, Verhalten
│ └── USER.md # Infos über den User
│
├── wwwroot/ # Frontend (Vanilla JS SPA)
│ ├── index.html # Single HTML Entry Point
│ ├── css/ # Stylesheets
│ │ ├── theme.css # CSS Custom Properties (Dark Theme)
│ │ ├── base.css # Reset, Grundlayout
│ │ ├── main.css # Sidebar, Layout-Struktur
│ │ ├── chat.css # Chat-Nachrichten, Bubbles
│ │ ├── dashboard.css # Dashboard-Grid
│ │ ├── form.css # Formulare, Inputs, Avatar-Editor-Modal
│ │ ├── markdown.css # Markdown + KaTeX Rendering-Styles
│ │ ├── file-explorer.css # File-Explorer Panel
│ │ ├── login.css # Login-Seite
│ │ ├── mobile.css # Responsive Anpassungen
│ │ ├── tooltip.css # Tooltip-Styles
│ │ ├── fonts.css # @font-face Definitionen
│ │ ├── placeholder.css # Placeholder/Loading States
│ │ ├── katex.min.css # KaTeX Math-Rendering Styles
│ │ ├── codemirror.min.css # CodeMirror Editor
│ │ ├── easymde.min.css # EasyMDE Editor
│ │ └── fonts/ # KaTeX woff2 Fonts (21 Dateien)
│ ├── js/
│ │ ├── app.js # App-Initialisierung, Auth-Check
│ │ ├── router.js # Client-side SPA Router
│ │ ├── api.js # API-Client (fetch wrapper)
│ │ ├── ui.js # UI-Utilities (Modal, Toast, etc.)
│ │ ├── chat.js # Chat-Logic, SSE-Streaming
│ │ ├── history.js # History-Rendering, Pagination
│ │ ├── session.js # Session-Verwaltung
│ │ ├── file-explorer.js # File-Explorer Komponente
│ │ ├── avatar-editor.js # Wiederverwendbares Avatar-Editor-Modal
│ │ ├── commands-ui.js # Custom-Commands UI
│ │ ├── emoji.js # Emoji-Picker
│ │ ├── emoji.userdata.js # Häufig genutzte Emojis
│ │ ├── tooltip.js # Tooltip-System
│ │ ├── title.js # Tab-Titel-Management
│ │ ├── pages/ # Seiten-Komponenten
│ │ │ ├── chat-page.js # Haupt-Chat-Ansicht
│ │ │ ├── dashboard.js # Dashboard / Home
│ │ │ ├── session-page.js # Session-Einstellungen
│ │ │ ├── agent-page.js # Agent-Editor
│ │ │ ├── cronjob-page.js # Cronjob-Verwaltung
│ │ │ ├── profile-page.js # Profil-Bearbeitung
│ │ │ ├── admin-page.js # Admin: Benutzerverwaltung
│ │ │ ├── settings-page.js # App-Einstellungen
│ │ │ ├── login-page.js # Login-Formular
│ │ │ └── shared-page.js # Öffentliche geteilte Nachricht
│ │ └── lib/ # Vendor-Libraries
│ │ ├── marked.min.js # Markdown Parser
│ │ ├── codemirror.min.js # Code Editor
│ │ ├── codemirror.cjs # CM CommonJS Wrapper
│ │ ├── easymde.min.js # Markdown Editor
│ │ ├── katex.min.js # KaTeX Math-Rendering Engine
│ │ └── cm-mode-*.min.js # CodeMirror Syntax-Modes
│ ├── fonts/ # Self-hosted Fonts (Inter, JetBrains Mono)
│ └── lib/font-awesome/ # Font Awesome Icons
│
├── WindowsApp/ # Windows Desktop Client (WPF)
│ ├── Gott.csproj # .NET 10 Windows, WebView2
│ ├── App.xaml / App.xaml.cs # WPF Application
│ ├── MainWindow.xaml/cs # WebView2 Host Window
│ └── appsettings.json # Server-URL Konfiguration
│
└── publish/ # Build-Output (NICHT editieren!)
└── data/
├── gott.db # SQLite Datenbank
├── agents/ # Agent Memory-Dateien
│ └── {agentId}/
│ ├── IDENTITY.md
│ ├── SOUL.md
│ ├── MEMORY.md
│ └── memory/ # Tages-Logs etc.
├── skills/ # Skill-Verzeichnisse
│ └── {slug}/
│ ├── SKILL.md
│ ├── PROMPT.md
│ └── output/
├── images/ # Krea-generierte Bilder
├── logs/ # Payload-Logs (optional)
└── backup/ # Backup-Archive
```
---
## Datenmodell
### Entity-Relationship
```
User (1) ─── (*) Agent
User (1) ─── (*) Session
Agent (1) ─── (*) Session (Session hat optional einen Agent)
Agent (1) ─── (*) Session (Session hat optional einen Review-Agent)
Agent (*) ─── (*) Skill (via agent_skills Tabelle)
Session (1) ─── (*) Chain (ein Chat-Request = eine Chain)
Session (1) ─── (*) Archive (archivierte Chains)
Chain (1) ─── (*) Message
Message (1) ─── (*) Attachment
Session (1) ─── (*) Cronjob
User (1) ─── (*) Cronjob
User (1) ─── (*) SharedMessage
Message (1) ─── (*) SharedMessage
```
### Tabellen
| Tabelle | Beschreibung |
|-------------------|-----------------------------------------------------|
| `users` | Benutzer (username, password_hash, persona, settings, default_path) |
| `tokens` | Auth-Tokens (token → user_id) |
| `agents` | AI-Agenten (model, soul, identity, memory, tools, avatar_scale) |
| `sessions` | Chat-Sessions (workspace, context, telegram-config, avatar_scale, telegram_allowed_chat_ids, review_agent_id, max_review_iterations) |
| `chains` | Request-Chains (model, tokens, iterations, timing) |
| `messages` | Einzelne Nachrichten (text, tool_use, tool_result) |
| `attachments` | Datei-Anhänge (base64, image/document) |
| `archives` | Archivierte Chain-Gruppen |
| `agent_skills` | Agent ↔ Skill Zuordnung |
| `cronjobs` | Geplante Tasks (cron expression, prompt) |
| `shared_messages` | Geteilte Nachrichten (Share-Token, Ablaufdatum) |
| `telegram_links` | Telegram Chat ↔ Session Verknüpfung |
### Zentrale Records
**ChainRecord** – Repräsentiert einen kompletten Request-Response-Zyklus:
- `ChainId` – Eindeutige ID
- `SessionId` – Zugehörige Session
- `Model` – Verwendetes Claude-Modell (z.B. `claude-sonnet-4-6`)
- `SystemPrompt` – Der vollständige System-Prompt (gespeichert für Nachvollziehbarkeit)
- `InputTokens`, `OutputTokens`, `CacheCreationTokens`, `CacheReadTokens` – Token-Verbrauch
- `Iterations` – Anzahl der Tool-Loop-Durchläufe
- `StopReason` – `end_turn`, `tool_use`, `max_iterations`, `error`, `client_disconnected`
- `DurationMs` – Gesamtdauer der Chain
**MessageRecord** – Eine einzelne Nachricht innerhalb einer Chain:
- `Role` – `user`, `assistant`, `system`
- `Type` – `text`, `tool_use`, `tool_result`, `error`
- `Content` – Textinhalt oder JSON (bei tool_use: Input-JSON)
- `ToolName`, `ToolCallId` – Bei Tool-Aufrufen
- `IsMarked` – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)
- `DeletedOn` – Soft-Delete Zeitstempel
---
## Backend-Architektur
### Program.cs – Startup
Die `Program.cs` ist der zentrale Einstiegspunkt und konfiguriert:
1. **Service-Registrierung** – Alle Services als Singletons im DI-Container
2. **Datenbank-Initialisierung** – `DatabaseService` erstellt Schema + Migrationen
3. **Default-User** – Wird automatisch angelegt falls keine User existieren
4. **Middleware** – Static Files → `AuthMiddleware` → Endpoints
5. **Endpoint-Mapping** – Alle 16 Endpoint-Gruppen registriert
6. **Server** – Kestrel auf `http://0.0.0.0:19000`
Initialisierungsreihenfolge:
```
AuthService → DatabaseService → EnsureDefaultUser
→ ImageService, StreamingTracker, MemoryService, SkillService
→ ToolService → ChatService
→ AnthropicService → TelegramService → KreaService
→ CronjobService (BackgroundService)
```
### Services
| Service | Typ | Aufgabe |
|------------------------|--------------|------------------------------------------------------------------|
| `DatabaseService` | Singleton | SQLite CRUD, Schema-Init, Migrationen, alle DB-Operationen |
| `AuthService` | Singleton | Token-Erzeugung/-Validierung, Passwort-Hashing (SHA256) |
| `ChatService` | Singleton | Chat-Kontext aufbauen, History aus DB rekonstruieren |
| `AnthropicService` | Singleton | Claude API Kommunikation (Streaming SSE + Non-Streaming) |
| `ToolService` | Singleton | Tool-Definitionen bereitstellen, Tool-Aufrufe ausführen |
| `WorkspaceHelper` | Static | Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz |
| `SystemPromptBuilder` | Static | Modularer System-Prompt aus konfigurierbaren Blöcken |
| `MemoryService` | Singleton | Agent-Gedächtnis auf Dateisystem (MEMORY.md, IDENTITY.md etc.) |
| `SkillService` | Singleton | Skill-Discovery und -Management aus Dateisystem |
| `TelegramService` | Singleton | Telegram Bot API, Webhook-Handling, Nachrichten senden |
| `CronjobService` | Background | Minütlicher Check auf fällige Cronjobs, Ausführung via Claude |
| `KreaService` | Singleton | Krea API für Bildgenerierung, Job-Polling, Image-Download |
| `ImageService` | Singleton | Bild-Resize (max 512px) für Avatare und Session-Bilder |
| `StreamingTracker` | Singleton | Trackt aktive Streams + CancellationTokenSource pro Session für Stop-Button |
### Endpoints (API)
Alle Endpoints folgen dem **Minimal API**-Pattern mit Extension Methods (`MapXxxEndpoints`).
| Gruppe | Basis-Route | Methoden |
|---------------|----------------------------|---------------------------------------------|
| **Auth** | `/api/auth/` | `POST login`, `POST logout`, `GET me`, `GET autologin` |
| **Chat** | `/api/chat` | `POST` (SSE Streaming), `POST stop` |
| **History** | `/api/history` | `GET history`, `GET poll`, `POST archive`, `GET archives`, `GET archive/{id}`, `GET attachments/{id}`, `PUT messages/{id}/mark`, `PUT messages/{id}/delete`, `GET chains/{id}` |
| **Sessions** | `/api/sessions/` | CRUD + `GET system-prompt`, `GET system-prompt-blocks`, `GET streaming`, `POST exec`, `GET git-status` |
| **Agents** | `/api/agents/` | CRUD |
| **Admin** | `/api/admin/` | `GET users`, `POST users`, `PUT users/{id}`, `PUT users/{id}/password`, `DELETE users/{id}`, `POST users/{id}/impersonate`, `POST users/{id}/login-link` (Admin-only) |
| **Workspace** | `/api/workspace/` | `GET tree`, `GET/PUT file`, `GET browse`, `POST mkdir`, `DELETE dir`, `POST browse/mkdir`, `GET agent/tree`, `GET/PUT agent/file`, `POST agent/mkdir`, `GET raw` |
| **Telegram** | `/api/telegram/` | `POST webhook/{sessionId}` (unauthenticated), Session-Config CRUD, Webhook-Management |
| **Cronjobs** | `/api/cronjobs/` | CRUD + `POST toggle`, `POST run` |
| **Krea** | `/api/images/` | `POST generate`, `GET list`, `GET file/{name}`, `GET file/{name}/thumb`, `GET status` |
| **Skills** | `/api/skills` | `GET list`, `GET/PUT agent skills`, Output-Files + Thumbnails |
| **Profile** | `/api/profile` | `GET`, `PUT`, `PUT password` |
| **Settings** | `/api/settings` | `GET`, `PUT` |
| **Backup** | `/api/backup` | `POST` (Admin only, tar.gz Download) |
| **Shared** | `/api/shared/`, `/api/messages/{id}/share` | Share erstellen, öffentlich abrufen, auflisten, löschen |
| **Info** | `/api/info` | `GET` (Build-Zeitstempel) |
### Middleware
**AuthMiddleware** prüft jede `/api/`-Anfrage auf ein gültiges `gott_token`-Cookie.
Ausnahmen (kein Auth nötig):
- Statische Dateien (nicht `/api/`)
- `POST /api/auth/login`
- `GET /api/auth/autologin` (Token-basierter Autologin via Link)
- `POST /api/telegram/webhook/{sessionId}` (authentifiziert via Secret-Header)
- `GET /api/shared/{token}` (öffentlicher Zugriff auf geteilte Nachrichten)
- `GET /api/images/file/*` (Krea-Bilder und Thumbnails, öffentlich)
**API-Key Auth** (ohne User-Kontext):
- `/api/images/*`-Routen akzeptieren einen `X-Api-Key`-Header. Wenn der Key mit `KreaApiKey` aus der Config übereinstimmt, wird der Request ohne User-Authentifizierung durchgelassen. Normaler Cookie-Auth funktioniert weiterhin als Fallback.
Bei Erfolg werden `UserId` und `IsAdmin` in `HttpContext.Items` gesetzt (bei API-Key-Auth sind diese nicht gesetzt).
### Models
Die Models in `Models/` sind reine Daten-Records ohne Business-Logic (Ausnahme: `ChatMessage` hat Helper-Methoden für Content-Extraktion und `UserRecord` hat `GetEffectivePersona()`).
**ChatContext** ist ein Runtime-Objekt das den vollständigen Kontext eines Chat-Requests hält:
- Chain-ID, Session-ID, Model-ID
- Aufgelöster System-Prompt (Text + strukturierte Blöcke)
- Workspace-Pfad, Tool-Choice
- Referenzen auf Agent und Session Records
### Templates
`Templates/` enthält Vorlagen die beim Erstellen eines neuen Agents in dessen Memory-Verzeichnis kopiert werden:
- **IDENTITY.md** – Name und Grundinfos (Platzhalter `{NAME}` wird ersetzt)
- **SOUL.md** – Persönlichkeit und Verhaltensgrundsätze
- **USER.md** – Informationen über den User (global, nicht agent-spezifisch)
---
## Frontend-Architektur
Das Frontend ist eine **Single-Page Application** ohne Build-Step, komplett in Vanilla JavaScript.
### Routing
`router.js` implementiert ein Hash-basiertes Client-Side-Routing:
| Route | Page | Beschreibung |
|--------------------------|---------------------|---------------------------|
| `#/` | `dashboard.js` | Session-Übersicht |
| `#/chat/{sessionId}` | `chat-page.js` | Chat-Ansicht |
| `#/session/{sessionId}` | `session-page.js` | Session-Einstellungen |
| `#/agent/{agentId}` | `agent-page.js` | Agent-Editor |
| `#/cronjobs` | `cronjob-page.js` | Cronjob-Verwaltung |
| `#/profile` | `profile-page.js` | Profil |
| `#/settings` | `settings-page.js` | App-Einstellungen |
| `#/admin` | `admin-page.js` | Benutzerverwaltung (Admin-only) |
| `#/login` | `login-page.js` | Login |
| `#/shared/{token}` | `shared-page.js` | Öffentliche geteilte Nachricht (kein Auth) |
### Kern-Module
- **app.js** – Initialisierung, Auth-Check, globale Event-Handler
- **api.js** – Zentraler API-Client mit `fetch()`-Wrapper, Auth-Redirect bei 401
- **ui.js** – UI-Utilities (Modals, Toasts, Confirmations), Card-Builder (`buildAgentCard()`, `buildSessionCard()`), Markdown-Rendering-Helpers (`esc()`, `prettyModelName()`)
- **chat.js** – SSE-Stream-Handling, Markdown-Rendering (mit KaTeX), Tool-Output-Darstellung
- **history.js** – Chat-History laden, paginieren, rendern
- **session.js** – Session-State-Management
- **file-explorer.js** – Workspace File-Explorer Panel (Dateien, Ordner erstellen/löschen, Folder-Browser)
- **avatar-editor.js** – Wiederverwendbares Avatar-Editor-Modal (Upload, Paste, AI-Generate, Scale)
- **commands-ui.js** – Custom-Commands UI-Komponente
### Wiederverwendbare Card-Builder (ui.js)
- **`buildAgentCard(agent, skills)`** – Erzeugt ein `.card` DOM-Element für einen Agent (Avatar, Name, Model-Badge, Feature-Badges: toolChoice, autoCommit, useSoul, useIdentity, useUserInfo, useMemory, webSearch, Skills). Wird im Dashboard und im Agent-Picker-Modal (Session-Formular) verwendet.
- **`buildSessionCard(session, agent)`** – Erzeugt ein `.card` DOM-Element für eine Session (Avatar, Name, Badges: Agent, Kontext, Telegram, Meta: Nachrichten/Archiv/Workspace). Wird im Dashboard und im Session-Picker-Modal (Cronjob-Formular) verwendet.
- **Picker-Modals** – Agent- und Session-Auswahl in Formularen (Session-/Cronjob-Seiten) erfolgt über modale Dialoge mit den Card-Buildern statt über Dropdowns.
- **emoji.js** – Emoji-Picker mit Kategorien und Suche
- **tooltip.js** – Tooltip-System für Hover-Infos
- **title.js** – Dynamisches Tab-Titel-Management
### Styling
CSS ist in modulare Dateien aufgeteilt mit CSS Custom Properties für Theming:
- `theme.css` – Alle Farben, Abstände, Schriften als CSS Variables
- `base.css` – Reset und Grundlayout
- `main.css` – Sidebar, Content-Area, Responsive Layout
- `chat.css` – Chat-Bubbles, Streaming-Animation, Tool-Outputs
- `markdown.css` – Rendering von Markdown in Nachrichten
- `mobile.css` – Responsive Breakpoints und Anpassungen
---
## Chat-Pipeline
Der Chat-Flow von User-Input bis zur gerenderten Antwort:
```
1. User tippt Nachricht + optionale Attachments (Bilder/Dokumente)
↓
2. Frontend: POST /api/chat { content, sessionId }
↓
3. ChatEndpoint:
a) User-Message + Attachments in DB speichern
b) ChatService.PrepareContext() → Agent, Model, System-Prompt, Chain
c) ChatService.BuildHistoryFromDb() → vollständige Message-History
d) AnthropicService.StreamChat() starten
↓
4. AnthropicService (Agentic Loop):
┌─────────────────────────────────────────┐
│ while (iterations < maxIterations) │
│ → Claude API Request (SSE) │
│ → Stream-Processing: │
│ • Text-Tokens → SSE an Frontend │
│ • Tool-Use → ToolService.Execute() │
│ • Tool-Results → nächste Iteration │
│ → Messages in DB persistieren │
│ → Stop wenn: end_turn / max_iterations│
│ → CancellationToken bei Abbruch │
└─────────────────────────────────────────┘
↓
4b. Review-Phase (optional, wenn ReviewAgent konfiguriert):
┌─────────────────────────────────────────┐
│ Nach end_turn des Worker-Agents: │
│ → RunReview() mit Review-Agent │
│ → Review-Agent prüft (eigene Tools) │
│ → [APPROVED] → Fertig │
│ → Feedback → an Worker als User-Msg │
│ → Worker korrigiert → erneuter Review │
│ → Max Review-Runden → Fertig │
└─────────────────────────────────────────┘
↓
5. Frontend: SSE Events empfangen und rendern
• "text" → Markdown parsen, Token für Token anzeigen
• "tool_*" → Tool-Aufrufe collapsible darstellen
• "review_*" → Review-Bubbles mit eigenem Styling
• "done" → Token-Stats, Dauer, Model anzeigen
```
### Abbruch (Stop-Button)
Der Stop-Mechanismus arbeitet auf zwei Ebenen:
**1. Expliziter Stop-Endpoint (`POST /api/chat/stop`):**
- Frontend ruft `apiStopChat(sessionId)` auf
- `StreamingTracker` hält pro Session einen `CancellationTokenSource`
- `StreamingTracker.Cancel(sessionId)` cancelt den Token sofort
- Der Chat-Handler verknüpft den Stop-Token mit dem Request-Token via `CreateLinkedTokenSource`
**2. CancellationToken-Propagierung:**
1. Frontend: `AbortController.abort()` → SSE-Fetch abgebrochen + `POST /api/chat/stop`
2. `StreamingTracker.Cancel()` → CancellationTokenSource wird gecancelled
3. `AnthropicService`: `ThrowIfCancellationRequested()` am Loop-Anfang, vor/nach Tool-Execution
4. `ToolService.ExecuteTool()`: CancellationToken wird durchgereicht → bei `execute` wird der Prozess gekillt
5. `WriteSseEvent()`: Alle SSE-Writes mit CancellationToken → sofortiger Abbruch
6. Chain wird mit `StopReason = "client_disconnected"` finalisiert
**Warum zwei Ebenen?** Kestrel erkennt einen Client-Disconnect erst beim nächsten Write auf den Response-Stream. Wenn das Backend in einer langen Tool-Ausführung steckt, wird nichts geschrieben – der explizite Stop-Endpoint garantiert sofortigen Abbruch.
### Streaming vs Non-Streaming
- **Web-Chat**: Streaming via SSE (`text/event-stream`). Tokens werden in Echtzeit gerendert.
- **Telegram**: Non-Streaming via `ChatNonStreaming()`. Vollständige Antwort wird abgewartet und als Telegram-Nachricht gesendet.
- **Cronjobs**: Non-Streaming, wie Telegram.
### History-Optimierung
Die History-Rekonstruktion in `BuildHistoryFromDb()` ist optimiert:
- **Ältere Chains**: Nur `text`-Messages (user + assistant), keine Tool-Calls
- **Letzte Chain**: Vollständig mit allen Tool-Use/Tool-Result Blöcken
- **Attachments**: Nur für die letzte User-Message geladen (spart enorme Token)
- **Tool-Results**: In der History auf 20.000 Zeichen gekürzt (vollständig in DB)
---
## Tool-System
Der Agent hat Zugriff auf folgende Tools, abhängig von Konfiguration:
### Datei-Tools (wenn Workspace konfiguriert)
| Tool | Beschreibung |
|---------------|--------------------------------------------------|
| `read_file` | Datei lesen (relativer Pfad zum Workspace) |
| `write_file` | Datei erstellen/überschreiben |
| `edit_file` | Exakten Textabschnitt in Datei ersetzen |
| `list_files` | Verzeichnis-Listing |
| `delete_file` | Datei oder leeres Verzeichnis löschen |
| `execute` | Shell-Befehl ausführen (bash, 180s Timeout) |
| `glob` | Dateien via Glob-Pattern finden |
| `grep` | Dateien nach Regex/Text durchsuchen |
| `todo_write` | Todo-Liste schreiben (als Datei im Workspace) |
### Memory-Tools (wenn UseMemory aktiviert)
| Tool | Beschreibung |
|----------------|-------------------------------------------|
| `memory_read` | Memory-Datei des Agents lesen |
| `memory_write` | Memory-Datei schreiben (überschreiben/append) |
| `memory_list` | Alle Memory-Dateien des Agents auflisten |
### Web-Suche (wenn WebSearchVersion konfiguriert)
Nutzt Anthropics eingebaute Web-Suche als Server-Side-Tool.
### Tool-Choice Strategien
- **`any`**: Erste Iteration erzwingt Tool-Nutzung, danach `auto`. Verhindert dass der Agent nur Text antwortet wenn Tools benötigt werden.
- **`auto`**: Claude entscheidet frei ob Tools nötig sind.
### Pfad-Sicherheit
Alle Dateipfade werden via `ResolvePath()` aufgelöst und gegen den Workspace/Memory-Ordner geprüft – Path-Traversal-Angriffe werden verhindert.
---
## Agent-System
Ein **Agent** definiert die Persönlichkeit und Fähigkeiten der AI:
| Eigenschaft | Beschreibung |
|-------------------|------------------------------------------------------------------|
| `Name` | Anzeigename |
| `Avatar` | Base64 Data-URL (auf 512px resized) |
| `Model` | `sonnet`, `sonnet-4-5`, `sonnet-4-6`, `opus`, `opus-4-5`, `opus-4-6`, `haiku`, `haiku-4-5` |
| `ToolChoice` | `any` oder `auto` |
| `CustomCommands` | JSON-Array mit Slash-Commands (Label + Prompt) |
| `AutoCommit` | `""` (nichts), `"mention"` (verbieten), `"enforce"` (erzwingen), `"forbid"` (strikt verboten) |
| `UseSoul` | SOUL.md in System-Prompt einbinden |
| `UseIdentity` | IDENTITY.md in System-Prompt einbinden |
| `UseUserInfo` | User-Persona in System-Prompt einbinden |
| `UseMemory` | Memory-Tools aktivieren |
| `WebSearchVersion`| Web-Suche aktivieren (z.B. `web_search_20260209`) |
### Model-Auflösung
```
"sonnet" → claude-sonnet-4-6
"sonnet-4-5" → claude-sonnet-4-5
"sonnet-4-6" → claude-sonnet-4-6
"opus" → claude-opus-4-6
"opus-4-5" → claude-opus-4-5
"opus-4-6" → claude-opus-4-6
"haiku" → claude-haiku-4-5
"haiku-4-5" → claude-haiku-4-5
(default) → claude-sonnet-4-6
```
---
## System-Prompt-Aufbau
Der `SystemPromptBuilder` erzeugt den System-Prompt modular aus konfigurierbaren **Blöcken**:
```
┌──────────────────────────────────────────┐
│ 1. IDENTITY.md (UseIdentity) │ → Wer ist der Agent?
│ 2. SOUL.md (UseSoul) │ → Persönlichkeit, Verhalten
│ 3. User-Persona (UseUserInfo) │ → Infos über den User
│ 4. Tool-Nutzung (Workspace + any) │ → "IMMER Tools benutzen"
│ 5. Workspace-Info │ → Tools + Workspace-Pfad
│ 6. Session-Kontext │ → Freiformtext
│ 7. Skills (Agent-Skills) │ → Skill-Prompts
│ 8. Memory (UseMemory) │ → Memory-Regeln + MEMORY.md Inhalt
│ 9. Git-Verhalten (AutoCommit) │ → Commit-Regeln
│10. Verhaltensregeln (Workspace + any) │ → "IMMER Tools aufrufen"
│11. Erinnerung (Workspace + any) │ → Höchste Priorität: Tool-Aufrufe!
│12. Markierte Nachrichten │ → Vom User als wichtig markierte Messages
└──────────────────────────────────────────┘
```
Jeder Block hat:
- **Icon** – Font Awesome Icon-Klasse
- **Label** – Beschreibender Titel
- **Content** – Markdown-Text
Die Blöcke sind über die API als JSON abrufbar (`GET /api/sessions/{id}/system-prompt-blocks`), sodass das Frontend sie einzeln inspizierbar darstellen kann.
---
## Memory-System
Jeder Agent hat ein eigenes **Dateisystem-basiertes Gedächtnis**:
```
data/agents/{agentId}/
├── IDENTITY.md # Identität (aus Template initialisiert)
├── SOUL.md # Persönlichkeit (aus Template initialisiert)
├── MEMORY.md # Haupt-Memory (kuratierte Informationen)
└── memory/ # Unterordner für Tages-Logs, Themen
├── YYYY-MM-DD.md
└── ...
```
### Funktionsweise
1. **Initialisierung**: Beim Erstellen eines Agents werden `IDENTITY.md` und `SOUL.md` aus `Templates/` kopiert
2. **Lesen**: Der Agent kann seine Memory-Dateien via `memory_read` Tool lesen
3. **Schreiben**: Via `memory_write` (überschreiben oder append)
4. **Auflisten**: Via `memory_list` alle Dateien durchsuchen
5. **System-Prompt**: `MEMORY.md` Inhalt wird automatisch in den System-Prompt eingebettet
6. **IDENTITY.md** und **SOUL.md**: Werden als eigene System-Prompt-Blöcke eingebunden (wenn aktiviert)
### Sandbox
Alle Pfade werden gegen das Agent-Verzeichnis geprüft – kein Zugriff außerhalb möglich.
---
## Skill-System
Skills sind **Dateisystem-basierte Erweiterungen** die einem Agent zugeordnet werden können:
```
data/skills/{slug}/
├── SKILL.md # Metadaten (Name, Description, Version im Frontmatter)
├── PROMPT.md # System-Prompt-Erweiterung (in Agent-Prompt eingebettet)
├── _meta.json # Alternative Metadaten (Fallback)
└── output/ # Skill-Output (Bilder, Dateien, etc.)
```
### SKILL.md Frontmatter
```yaml
---
name: Mein Skill
description: Beschreibung
version: 1.0
---
```
### Agent-Zuordnung
Skills werden über die `agent_skills` Tabelle einem Agent zugeordnet. Der `PROMPT.md`-Inhalt aller zugeordneten Skills wird als ein System-Prompt-Block eingefügt.
### Workspace-Implikation
Wenn ein Agent Skills hat aber kein Workspace konfiguriert ist, wird automatisch `/tmp` als Workspace gesetzt (damit `execute` für Skill-Commands funktioniert).
---
## Telegram-Integration
Sessions können mit einem **Telegram Bot** verbunden werden:
### Konfiguration
Pro Session werden gespeichert:
- `TelegramBotToken` – Bot-Token von @BotFather
- `TelegramWebhookSecret` – Geheimer Token für Webhook-Verifizierung
- `TelegramChatId` – Verknüpfte Chat-ID (Owner)
- `TelegramAllowedChatIds` – Kommagetrennte Chat-ID-Whitelist für Zugriffskontrolle
### Webhook-Flow
```
1. User konfiguriert Bot-Token in Session-Settings
2. User registriert Webhook (POST .../telegram/register-webhook)
→ Setzt Webhook-URL: {baseUrl}/api/telegram/webhook/{sessionId}
3. Telegram sendet Updates an Webhook
4. AuthMiddleware lässt Webhook-Requests durch (kein Cookie nötig)
5. TelegramEndpoint prüft X-Telegram-Bot-Api-Secret-Token Header
6. TelegramService verarbeitet Update:
- /start → Session verknüpfen (nur wenn noch kein Owner ODER Whitelist-Match)
- /clear → History archivieren
- /status → Session-Info anzeigen
- /unlink → Verknüpfung aufheben
- Text → An Claude weiterleiten (Non-Streaming)
```
### Zugriffskontrolle (Chat-ID-Whitelist)
Nicht jeder Telegram-User darf den Bot nutzen:
1. **Erster `/start`**: Kein Owner → User wird automatisch Owner (`TelegramChatId` gesetzt)
2. **Fremder `/start`**: User bekommt `🔒 Zugriff verweigert` + seine Chat-ID angezeigt
3. **Owner-Benachrichtigung**: Owner erhält `🔔 Zugriffsanfrage` mit Username + Chat-ID
4. **Freigabe**: Owner/Admin trägt Chat-ID in Session-Settings ein (`TelegramAllowedChatIds`)
5. **Prüfung**: Alle Commands + Nachrichten prüfen via `IsChatIdAllowed()` (Owner + Whitelist)
### Non-Streaming Chat
Telegram-Nachrichten werden über `AnthropicService.ChatNonStreaming()` verarbeitet. Die History wird genauso aus der DB aufgebaut wie beim Web-Chat. Antworten über 4096 Zeichen werden automatisch in Chunks aufgeteilt.
---
## Cronjob-System
Der `CronjobService` ist ein **BackgroundService** der minütlich nach fälligen Jobs prüft.
### Datenmodell
| Feld | Beschreibung |
|---------------------|------------------------------------------------|
| `CronExpression` | Standard-Cron-Format (5 Felder), via Cronos |
| `SessionId` | Session in der der Job läuft |
| `Prompt` | Text der als User-Message an Claude gesendet wird |
| `NotifySessionId` | Optionale Session für Benachrichtigung |
| `IsEnabled` | Aktiviert/Deaktiviert |
| `NextRunAt` | Nächste geplante Ausführung (UTC) |
### Ausführungsflow
```
1. CronjobService prüft jede Minute
2. Fällige Jobs (next_run_at <= jetzt) werden geladen
3. Für jeden Job:
a) ChatService.PrepareContext() – Chain + System-Prompt aufbauen
b) History aus DB laden
c) Prompt als User-Message anhängen + in DB speichern
d) AnthropicService.ChatNonStreaming() ausführen
e) Telegram-Benachrichtigung senden (auf Arbeits-Session)
f) Optional: Zusammenfassung in Notify-Session schreiben + Telegram dort
g) Nächste Ausführung berechnen + DB aktualisieren
```
### Benachrichtigungen
- **Arbeits-Session**: Wenn die Session Telegram hat, wird die Antwort dort gepostet
- **Notify-Session**: Wenn konfiguriert, wird eine Zusammenfassung in eine andere Session geschrieben und der Agent dort antwortet darauf
---
## Krea-Bildgenerierung
Die `KreaService` ermöglicht Bildgenerierung über die Krea API.
### Unterstützte Modelle
```
flux, flux-kontext, flux-1.1-pro, flux-1.1-pro-ultra,
nano-banana, nano-banana-pro, imagen-3, imagen-4, imagen-4-fast, imagen-4-ultra,
ideogram-2-turbo, ideogram-3, seedream-3, seedream-4,
chatgpt-image, runway-gen-4
```
### Flow
```
1. POST /api/images/generate { prompt, model, width, height, ... }
2. KreaService sendet Request an Krea API
3. Job-Polling bis Status "completed"
4. Bilder herunterladen nach data/images/
5. Metadaten als JSON speichern
6. Response mit Dateinamen + URLs
```
### Bild-Serving
- `GET /api/images` – Liste aller generierten Bilder (aus Metadaten-JSON)
- `GET /api/images/file/{filename}` – Einzelnes Bild ausliefern (ohne Auth)
- `GET /api/images/file/{filename}/thumb` – Thumbnail (320px WebP, gecacht in `data/cache/images/`, ohne Auth)
- `GET /api/images/status` – Generierungsstatus abfragen
Bild-Dateien und Thumbnails sind **ohne Authentifizierung** abrufbar (für externe Einbindung).
---
## Auth & Multi-User
### Authentifizierung
- **Login**: Username + Passwort → SHA256 Hash-Vergleich → Token generieren
- **Token**: 32 Bytes Random, als `gott_token` HttpOnly Cookie (365 Tage)
- **Validierung**: Jeder API-Call prüft Token → User-ID
- **Logout**: Token aus DB löschen + Cookie entfernen
### Multi-User-Isolation
- Sessions sind an `user_id` gebunden
- Agents sind an `user_id` gebunden
- Cronjobs sind an `user_id` gebunden
- Jeder User sieht nur seine eigenen Daten
- Admin-Flag (`is_admin`) für Backup-Endpoint, Admin-Benutzerverwaltung, Impersonate
### Default-Path (Base-Path)
Jeder User hat einen optionalen `DefaultPath` – den Basis-Ordner für alle Workspaces. Wird **nur vom Admin** gesetzt.
- **Session-Workspace**: Speichert nur den **relativen Pfad** zum DefaultPath (z.B. `myapp` statt `/home/user/projects/myapp`)
- **Auflösung**: `WorkspaceHelper.ResolveWorkspace()` kombiniert DefaultPath + relativen Workspace → absoluter Pfad
- **System-Prompt**: Bekommt den **aufgelösten absoluten Pfad**, damit der Agent korrekt navigieren kann
- **Path-Traversal-Schutz**: `..` in relativen Workspace-Pfaden wird blockiert (400 Bad Request)
- **Legacy-Support**: Absolute Pfade in `session.Workspace` funktionieren weiterhin als Fallback
- **Folder-Browser**: Startet beim DefaultPath, User kann nicht über den Base-Path hinaus navigieren
### Default-User
Beim ersten Start wird automatisch ein Default-User erstellt (`DatabaseService.EnsureDefaultUser()`).
### User-Persona
Jeder User hat eine optionale **Persona** – ein Freitext der beschreibt wer er ist. Diese wird (wenn `UseUserInfo` am Agent aktiv ist) in den System-Prompt als "Dein Gegenüber" eingebettet.
---
## Admin-Benutzerverwaltung
Admins können alle User verwalten über `AdminEndpoint.cs` (alle Endpoints erfordern `is_admin`).
### API-Endpunkte
| Methode | Route | Beschreibung |
|----------|-----------------------------------------|-------------------------------------------|
| `GET` | `/api/admin/users` | Alle User auflisten |
| `POST` | `/api/admin/users` | Neuen User erstellen |
| `PUT` | `/api/admin/users/{id}` | User bearbeiten (Name, Username, Admin, DefaultPath) |
| `PUT` | `/api/admin/users/{id}/password` | Passwort zurücksetzen |
| `DELETE` | `/api/admin/users/{id}` | User löschen (+ Tokens, Shared Messages) |
| `POST` | `/api/admin/users/{id}/impersonate` | Als anderer User einloggen (Login-as-User) |
| `POST` | `/api/admin/users/{id}/login-link` | Autologin-Token generieren → URL zurückgeben |
### Impersonate (Login-as-User)
Admins können sich als beliebiger User einloggen ohne dessen Passwort zu kennen:
1. `POST /api/admin/users/{id}/impersonate` → Erstellt Token für Zieluser
2. Setzt neuen `gott_token`-Cookie → Admin-Session wird zum Zieluser
3. `location.reload()` → Frontend lädt mit neuem User-Kontext
### Login-Link generieren
Admins können für jeden User einen **Autologin-Link** generieren – z.B. für Setup oder Support:
1. `POST /api/admin/users/{id}/login-link` → Erstellt Token, gibt `{ token }` zurück
2. Frontend baut URL: `{window.location.origin}/api/auth/autologin?token={token}`
3. Link wird im Modal angezeigt mit Copy-Button
4. `GET /api/auth/autologin?token={token}` → Setzt Cookie + Redirect zu `/` (ohne Auth, wie Login)
### Frontend (`admin-page.js`)
- Erreichbar über Dashboard-Link "Benutzerverwaltung" (nur für Admins sichtbar)
- User-Liste als Cards mit Edit/Delete/Login-Link-Buttons
- Inline-Formular für Create/Edit
- Login-Link-Modal mit Copy-Funktion
- Sich selbst löschen ist nicht möglich
---
## Multi-Agent Review-System
Das Review-System ermöglicht es, einen zweiten Agent als **Reviewer** einzusetzen. Nach jeder Antwort des Worker-Agents prüft der Review-Agent das Ergebnis und gibt entweder `[APPROVED]` oder konstruktives Feedback.
### Architektur
```
User-Message → Worker-Agent arbeitet (Tools, Iterationen) → end_turn
→ Review-Phase:
→ Review-Agent bekommt: Konversation als Kontext
→ Review-Agent prüft (kann Tools nutzen: read_file, execute etc.)
→ [APPROVED] → Fertig, Antwort an User
→ Feedback-Text → wird als User-Message an Worker-Agent eingefügt
→ Worker-Agent korrigiert → end_turn → Review-Agent prüft erneut
→ Max Review-Runden erreicht → Fertig (mit letzter Worker-Antwort)
```
### Konfiguration
Auf **Session-Ebene** (nicht Agent-Ebene):
| DB-Feld | SessionRecord | Beschreibung |
|---------|---------------|-------------|
| `review_agent_id` | `ReviewAgentId` | ID des Review-Agents (optional, null = kein Review) |
| `max_review_iterations` | `MaxReviewIterations` | Max. Review-Runden (Default: 3) |
### Backend-Flow
1. **ChatService.PrepareContext()** – Wenn `session.ReviewAgentId` gesetzt:
- Review-Agent aus DB laden
- Eigenen SystemPrompt für den Review-Agent bauen (Identity, Soul, Memory, Skills)
- Eigene Tool-Definitionen für den Review-Agent
- `ReviewContext` mit Model, SystemPrompt, Tools, Agent-Referenz erstellen
2. **ChatEndpoint** – Übergibt `ctx.Review` an `AnthropicService.StreamChat()`
3. **AnthropicService.StreamChat()** – Nach `end_turn` des Worker-Agents:
- Prüft ob `ReviewContext` vorhanden und `reviewRound < MaxReviewIterations`
- Ruft `RunReview()` auf
4. **RunReview()** – Orchestriert eine einzelne Review-Runde:
- Baut Review-Messages auf: Konversation als Context + Prüfanweisung
- SSE-Event `review_start` an Client senden
- Ruft `RunReviewAgentChat()` auf (eigener API-Call an Claude mit Review-Agent-Config)
- Review-Agent kann Tools nutzen (eigener Tool-Loop mit max. 25 Iterationen)
- SSE-Events `review_text` und `review_tool_use` für Live-Streaming
- Prüft ob Antwort `[APPROVED]` enthält:
- **Ja** → SSE `review_approved`, return `HasFeedback = false`
- **Nein** → SSE `review_feedback`, return `HasFeedback = true` + Feedback-Text
5. **Zurück in StreamChat()** – Bei Feedback:
- Feedback als User-Message in DB speichern (mit `[Review-Feedback von AgentName (Runde X/Y)]` Prefix)
- Worker-Agent bekommt Feedback als nächste User-Message
- `continue` → nächste while-Loop-Iteration → Worker korrigiert → Review erneut
- `reviewRound++` bis `MaxReviewIterations` erreicht
### ReviewContext (Model)
```csharp
public class ReviewContext
{
public string Model { get; set; } // Claude-Modell des Review-Agents
public string SystemPrompt { get; set; } // Eigener System-Prompt
public AgentRecord Agent { get; set; } // Agent-Record für Name/Avatar
public List<object> Tools { get; set; } // Tool-Definitionen
public int MaxReviewIterations { get; set; } // Max Runden
}
```
### SSE-Events
| Event | Daten | Beschreibung |
|-------|-------|-------------|
| `review_start` | `{ agent: "Name" }` | Review-Phase beginnt |
| `review_text` | `{ text: "..." }` | Review-Agent-Text (Token-Stream) |
| `review_tool_use` | `{ tool: "name", input: "..." }` | Review-Agent nutzt Tool |
| `review_feedback` | `{ feedback: "..." }` | Review nicht bestanden, Feedback an Worker |
| `review_approved` | `{ message: "..." }` | Review bestanden |
### DB-Persistierung
- **Worker-Antworten**: Normale Messages (role=assistant, type=text)
- **Review-Feedback**: Als User-Messages mit Prefix `[Review-Feedback von AgentName (Runde X/Y)]:`
- **Review-Agent-Antworten**: Werden **nicht** in der Chat-History persistiert (nur als SSE-Events gestreamt), da sie Zwischenergebnisse sind
### Frontend
**Session-Page** (`session-page.js`):
- Zweiter Agent-Picker: "Review-Agent" (nutzt `buildAgentCard()` + `openAgentPickerModal()`)
- Input für "Max. Review-Runden" (Number-Input, Default 3)
**Chat-UI** (`chat.js`):
- SSE-Event-Handler für `review_start`, `review_text`, `review_tool_use`, `review_feedback`, `review_approved`
- Review-Bubbles mit eigenem Styling (sekundäre Farbe, Agent-Name-Header)
**History-Rendering** (`history.js`, `ui.js`):
- `renderMarkdown` parst `[Review: AgentName]` → styled Header mit Icon
- `renderMarkdown` parst `[Review-Feedback von AgentName (...)]` → styled Feedback-Header
- `renderMarkdown` filtert `[APPROVED]` aus (wird ausgeblendet)
- History erkennt Review-Nachrichten und rendert sie als `.review-bubble` statt normaler Bubbles
- Review-Nachrichten werden nicht mit normalen Nachrichten gemergt
**Shared-Page** (`shared-page.js`):
- Gleiche Review-Bubble-Erkennung für geteilte Nachrichten
---
## Nachrichten teilen (Shared Messages)
Einzelne Nachrichten oder ganze Threads können als **öffentliche Links** geteilt werden – ohne Login aufrufbar.
### Datenmodell
**Tabelle `shared_messages`:**
| Spalte | Typ | Beschreibung |
|------------------|---------|-------------------------------------------|
| `id` | INTEGER | Primary Key |
| `share_token` | TEXT | UUID – öffentlicher Zugangsschlüssel |
| `message_id` | INTEGER | FK auf `messages.id` |
| `user_id` | INTEGER | Wer hat geteilt |
| `include_context`| INTEGER | 0 = Einzelnachricht, 1 = Thread bis dahin |
| `created_at` | TEXT | Erstellzeitpunkt |
| `expires_at` | TEXT | Optional: Ablaufdatum |
### API-Endpunkte (`SharedEndpoint.cs`)
| Methode | Route | Auth | Beschreibung |
|----------|--------------------------------|------|---------------------------------------|
| `POST` | `/api/messages/{id}/share` | ✅ | Share erstellen (Body: `{includeContext}`) |
| `GET` | `/api/shared/{token}` | ❌ | Öffentlich: Geteilte Nachricht abrufen |
| `GET` | `/api/shared` | ✅ | Eigene Shares auflisten |
| `DELETE` | `/api/shared/{token}` | ✅ | Share-Link widerrufen |
### Frontend-Flow
1. **Share-Button** (🔗) erscheint an jeder Nachricht im Chat (neben Reply, Delete, Mark)
2. Klick öffnet **Kontext-Menü**: "Einzelne Nachricht" oder "Thread teilen"
3. `POST /api/messages/{id}/share` → Token zurück → URL in Zwischenablage kopiert
4. **Toast**: "Link kopiert!"
### Öffentliche Ansicht (`#/shared/{token}`)
- Route `#/shared/{token}` → `shared-page.js`
- **Kein Auth nötig** – Auth-Bypass in `app.js` + `AuthMiddleware`
- Minimales Layout: Header ("// gott · Geteilte Nachricht"), Nachrichten mit Markdown-Rendering
- Zwei Modi: `single` (eine Nachricht) oder `context` (Thread mit User/Assistant-Wechsel)
---
## Workspace & File-Explorer
### Workspace
Eine Session kann einen **Workspace-Pfad** haben. Dieser wird **relativ** zum `DefaultPath` des Users gespeichert (z.B. `myapp`) und via `WorkspaceHelper.ResolveWorkspace()` zum absoluten Pfad aufgelöst. Der Workspace:
- Aktiviert die Datei-/Shell-Tools für den Agent
- Ermöglicht den File-Explorer im Frontend
- Bietet `git status` Informationen
- Erlaubt das Ausführen von Shell-Befehlen (foreground + background)
### WorkspaceHelper
`Services/WorkspaceHelper.cs` – Statische Hilfsklasse für Workspace-Pfade:
| Methode | Beschreibung |
|----------------------------|-----------------------------------------------------------|
| `ResolveWorkspace(session, user)` | Relativer Workspace + DefaultPath → absoluter Pfad |
| `ValidateRelativeWorkspace(workspace)` | Prüft auf `..`, `~`, absolute Pfade → null bei ungültig |
| `ToRelativeWorkspace(absolute, defaultPath)` | Absoluter Pfad → relativ zum DefaultPath |
### File-Explorer API
| Endpoint | Beschreibung |
|-------------------------------------|------------------------------------------|
| `GET /api/workspace/tree` | Verzeichnisbaum (mit Ignore-Filtern) |
| `GET /api/workspace/file` | Dateiinhalt lesen (max 2MB) |
| `PUT /api/workspace/file` | Datei speichern |
| `POST /api/workspace/mkdir` | Ordner erstellen im Workspace |
| `DELETE /api/workspace/dir` | Leeren Ordner löschen im Workspace |
| `GET /api/workspace/browse` | Verzeichnis-Browser (für Pfad-Auswahl) |
| `POST /api/workspace/browse/mkdir` | Ordner erstellen per absolutem Pfad |
| `GET /api/workspace/raw` | Raw-Datei ausliefern (Bilder, PDFs) |
| `GET /api/workspace/agent/tree` | Agent-Memory-Verzeichnis |
| `GET/PUT /api/workspace/agent/file` | Agent-Memory-Dateien lesen/schreiben |
| `POST /api/workspace/agent/mkdir` | Ordner erstellen im Agent-Memory |
### Ignored Directories
`bin`, `obj`, `node_modules`, `.git`, `.vs`, `.idea`, `publish`, `dist`, `out`, `build`, `target`, `.cache`
### Session-Exec
`POST /api/sessions/{id}/exec` führt Shell-Befehle im Workspace aus:
- **foreground**: Synchron, wartet auf Ergebnis (30s Timeout)
- **background**: Fire-and-forget via `systemd-run`
---
## Avatar-System
Sessions und Agents können ein **Bild/Avatar** haben mit konfigurierbarem Zoom.
### Datenfelder
| Feld | Tabellen | Beschreibung |
|------|----------|-------------|
| `image` / `avatar` | `sessions` / `agents` | Base64-kodiertes Bild (JPEG, max 512px via ImageService) |
| `avatar_scale` | `sessions`, `agents` | Zoom-Faktor (0.5× – 3×, Default 1.0), angewandt via CSS `transform: scale()` |
### Avatar-Editor (`avatar-editor.js`)
Wiederverwendbares Modal-Modul, genutzt in Session- und Agent-Pages:
```javascript
const editor = AvatarEditor.init({
previewEl: document.querySelector('.avatar-preview'),
currentImage: existingBase64OrNull,
currentScale: 1.0,
onImageChange: (base64) => { /* Preview aktualisieren */ },
onScaleChange: (scale) => { /* Live-Preview skalieren */ }
});
// Werte auslesen: editor.getImage(), editor.getScale()
// Aufräumen: editor.destroy()
```
**Features:**
- **Upload**: Datei-Input für Bild-Upload
- **Paste**: Ctrl+V → Bild aus Zwischenablage einfügen
- **AI-Generate**: Text-Prompt → Krea API → Bild generieren (nur wenn `KreaToken` konfiguriert)
- **Scale**: Range-Slider 0.5× – 3× mit Live-Preview
- **Clear**: Bild entfernen
### Dashboard-Darstellung
Cards zeigen Avatare mit `transform: scale(avatarScale)` im `.card-image` Container (`overflow: hidden`).
---
## LaTeX/Math-Rendering
Chat-Nachrichten unterstützen **LaTeX-Formeln** via KaTeX:
### Syntax
- `$$...$$` → **Block-Math** (zentriert, eigene Zeile)
- `$...$` → **Inline-Math** (im Fließtext)
### Rendering-Pipeline (in `ui.js` `renderMarkdown()`)
```
1. Code-Blöcke extrahieren und schützen (Platzhalter)
2. Block-LaTeX ($$...$$) extrahieren und schützen
3. Inline-LaTeX ($...$) extrahieren und schützen
4. Markdown-Rendering via marked.js
5. Code-Blöcke wiederherstellen
6. LaTeX-Platzhalter durch KaTeX.renderToString() ersetzen
7. Bei fehlerhaftem LaTeX: Fallback als <code class="katex-error">
```
### Abhängigkeiten
- `wwwroot/js/lib/katex.min.js` – KaTeX Engine (v0.16.21)
- `wwwroot/css/katex.min.css` – Styles
- `wwwroot/css/fonts/` – 21 woff2-Fonts (self-hosted)
Funktioniert überall wo `renderMarkdown()` aufgerufen wird: Chat, Streaming, History, Archiv, Shared Page.
---
## Windows Desktop App
Im `WindowsApp/`-Ordner liegt ein minimaler **WPF WebView2 Client**:
- .NET 10 Windows, `Microsoft.Web.WebView2`
- Lädt die Web-UI in einem nativen Fenster
- Server-URL konfigurierbar via `appsettings.json`
- Dark Background um weißen Flash zu vermeiden
- Externe Links öffnen im Standard-Browser
- Fenster: 930px breit, 95% Bildschirmhöhe, zentriert
---
## Deployment
### deploy.sh
```bash
#!/bin/bash
set -e
cd /app/gott
export PATH="$HOME/.dotnet:$PATH"
systemctl stop gott
dotnet publish -c Release -o publish
systemctl start gott
echo "OK"
```
### Verzeichnisse
- **Quell-Code**: `/app/gott/` (wwwroot/, Services/, etc.)
- **Build-Output**: `/app/gott/publish/`
- **Laufzeit-Daten**: `/app/gott/publish/data/` (DB, Agents, Images, Skills)
- **WICHTIG**: Änderungen IMMER in `wwwroot/` machen, NIEMALS in `publish/wwwroot/`
### systemd
Die Anwendung läuft als systemd Service (`gott.service`), Kestrel auf Port 19000.
---
## Konfiguration
### appsettings.json
```json
{
"AnthropicToken": "", // Anthropic API Key (REQUIRED)
"MaxIterations": 100, // Max Tool-Loop Iterationen pro Chat
"PayloadLogging": false, // API-Payloads loggen (Debug)
"PayloadLoggingGzip": false, // Payload-Logs als .gz komprimieren
"KreaToken": "", // Krea API Token (optional)
"KreaApiKey": "" // API-Key für Krea-Endpoints ohne User-Auth (optional)
}
```
### Environment
- `ASPNETCORE_ENVIRONMENT=Production` → lädt `appsettings.Production.json`
- Tokens und Secrets sollten in `appsettings.Production.json` stehen (nicht in Git)
---
## Datenbank-Migrationen
Die `DatabaseService.InitializeDatabase()` handhabt Schema-Erstellung und Migrationen in einem einzigen Methoden-Aufruf:
1. **Tabellen-Erstellung** via `CREATE TABLE IF NOT EXISTS`
2. **Spalten-Migration** via `TryAddColumn()` – fängt "column already exists" ab
3. **Daten-Migration** (z.B. `auto_commit` von INTEGER zu TEXT, `use_web_search` zu `web_search_version`)
Dieses Pattern ermöglicht:
- Idempotente Ausführung bei jedem App-Start
- Keine separate Migrations-Verwaltung nötig
- Neue Features können einfach Spalten hinzufügen
- Abwärtskompatibel – bestehende Daten bleiben erhalten