# 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 System (Review + Finalizer)](#multi-agent-system-review--finalizer) 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 (1) ─── (*) Session (Session hat optional einen Finalizer-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, avatar_offset_x/y) | | `sessions` | Chat-Sessions (workspace, context, telegram-config, avatar_scale, avatar_offset_x/y, telegram_allowed_chat_ids, review_agent_id, max_review_iterations, finalizer_agent_id) | | `chains` | Request-Chains (model, tokens, iterations, timing) | | `messages` | Einzelne Nachrichten (text, tool_use, tool_result, web_search_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`, `web_search_result` - `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 17 Endpoint-Gruppen registriert 6. **Server** – Kestrel auf `http://0.0.0.0:{Port}` (Port aus Config, Default 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) | | **ServerSettings** | `/api/admin/server-settings` | `GET`, `PUT` (Admin-only, liest/schreibt `appsettings.Production.json`) | ### 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) | | `#/server-settings` | `server-settings-page.js` | Servereinstellungen (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 │ └─────────────────────────────────────────┘ ↓ 4c. Finalizer-Phase (optional, wenn FinalizerAgent konfiguriert): ┌─────────────────────────────────────────┐ │ Nach Review (oder direkt nach Worker): │ │ → RunFinalizer() mit Finalizer-Agent │ │ → Finalizer arbeitet (eigene Tools) │ │ → z.B. git commit, deploy, cleanup │ │ → Kein Feedback-Loop (einmalig) │ └─────────────────────────────────────────┘ ↓ 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 System (Review + Finalizer) Das Multi-Agent-System ermöglicht bis zu **drei Agents** pro Session: einen **Worker**, einen optionalen **Reviewer** und einen optionalen **Finalizer**. ### Architektur ``` User-Message → Worker-Agent arbeitet (Tools, Iterationen) → end_turn → Review-Phase (optional): → 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) → Finalizer-Phase (optional): → Finalizer-Agent bekommt: gesamte Konversation → Finalizer arbeitet (eigene Tools, z.B. git commit, deploy) → Kein Feedback-Loop – einmalige Ausführung → Fertig ``` ### 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) | | `finalizer_agent_id` | `FinalizerAgentId` | ID des Finalizer-Agents (optional, null = kein Finalizer) | ### 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 `RunAgentChat()` auf (generische Methode für Review/Finalizer, eigener API-Call an Claude) - 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 / FinalizerContext (Model) ```csharp // Identische Struktur für Review und Finalizer public class ReviewContext { public string Model { get; set; } // Claude-Modell des Agents public string SystemPrompt { get; set; } // Eigener System-Prompt public AgentRecord Agent { get; set; } // Agent-Record für Name/Avatar public List Tools { get; set; } // Tool-Definitionen public int MaxReviewIterations { get; set; } // Max Runden (nur bei Review relevant) } // ChatContext hat: ReviewContext Review + ReviewContext Finalizer ``` ### 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 ### Finalizer-Agent Der Finalizer wird **nach** dem Review (oder direkt nach dem Worker, falls kein Review-Agent konfiguriert) aufgerufen. Er führt abschließende Arbeiten durch (z.B. Git-Commit, Deployment, Cleanup) – **ohne Feedback-Loop**. #### Konfiguration | DB-Feld | SessionRecord | Beschreibung | |---------|---------------|-------------| | `finalizer_agent_id` | `FinalizerAgentId` | ID des Finalizer-Agents (optional, null = kein Finalizer) | #### Backend-Flow 1. **ChatService.PrepareContext()** – Wenn `session.FinalizerAgentId` gesetzt: - Finalizer-Agent aus DB laden - `FinalizerContext` aufbauen (analog zu `ReviewContext`: Model, SystemPrompt, Tools) 2. **AnthropicService.StreamChat()** – Nach Review-Phase (oder direkt nach Worker): - Prüft ob `FinalizerContext` vorhanden - Ruft `RunFinalizer()` auf 3. **RunFinalizer()** – Baut Messages auf (gesamte Konversation + Finalisierungs-Anweisung), ruft `RunAgentChat()` auf (generische Methode, auch vom Review genutzt) 4. **RunAgentChat()** – Generische Methode für Review/Finalizer: - Eigener API-Call an Claude mit Agent-Config - Tool-Loop mit SSE-Events (`{type}_text`, `{type}_tool_use`) - Antwort als Message in DB persistieren #### SSE-Events | Event | Daten | Beschreibung | |-------|-------|-------------| | `finalizer_start` | `{ agent: "Name" }` | Finalizer-Phase beginnt | | `finalizer_text` | `{ text: "..." }` | Finalizer-Agent-Text (Token-Stream) | | `finalizer_tool_use` | `{ tool: "name", input: "..." }` | Finalizer nutzt Tool | | `finalizer_done` | `{}` | Finalizer-Phase beendet | #### Frontend - **Session-Page**: Dritter Agent-Picker "Finalizer-Agent" im "Weitere Einstellungen"-Bereich - **Chat-UI**: Finalizer-Bubbles mit eigenem Styling (`.finalizer-bubble`, grüne Farbe, 🏁-Icon) - **History**: `[Finalizer: AgentName]` Prefix → styled Header, `.finalizer-bubble`-Klasse - **Dashboard**: 🏁-Badge auf Session-Cards mit Finalizer --- ## Web-Search Anthropic Claude unterstützt Web-Suche als Server-Tool. Das System integriert Web-Search mit Source-Tracking und Rendering. ### Backend - **Streaming**: `server_tool_use` (Suchanfrage) → `web_search_tool_result` (Ergebnisse mit Sources) - **Sources extrahieren**: Aus `web_search_tool_result`-Block werden `url` + `title` als JSON-Array extrahiert - **Persistierung**: `web_search_result`-Messages in DB (type=`web_search_result`, content = JSON-Array der Sources). Leere Ergebnisse (Sources=[]) werden **nicht** gespeichert. - **SSE-Events**: `web_search_start` (Suche beginnt) → `web_search_end` (Suche fertig, mit Sources) ### Frontend – Streaming - `web_search_start` → Tool-Box mit Spinner erstellen, Stream-Bubble **pausieren** (nicht finalisieren) - `web_search_end` → Tool-Box mit Sources finalisieren - `text_start` → in bestehende Bubble weiter schreiben (kein neuer Block) - `pauseStreamBubble()` / `endStreamBubble()` Konzept: Text-Blöcke zwischen Web-Searches werden in **einer** Bubble zusammengefasst - `text_end` pausiert nur (Cursor weg), `endStreamBubble()` nur bei finalen Events (`done`, `tool_start`, `error`) ### Frontend – History - `web_search_result`-Messages werden als Tool-Box mit klickbaren Source-Links gerendert (Domain + Title) - `_mergeConsecutiveAssistantTexts()` fügt Text-Messages zusammen, `web_search_result`-Messages dazwischen unterbrechen die Merge-Kette nicht - Leere `web_search_result`-Messages (Sources=[]) werden übersprungen ### Frontend – Shared-Page - `SharedEndpoint` gibt `web_search_result`-Messages mit aus (Typ-Filter: `text` + `web_search_result`) - `shared-page.js` rendert `web_search_result` als Tool-Box mit Source-Links - Merge-Funktion überspringt `web_search_result` beim Rückwärts-Suchen, damit Texte zusammengeführt werden --- ## 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) | | `avatar_offset_x` | `sessions`, `agents` | Horizontale Position (-50% – +50%, Default 0), via CSS `translate()` | | `avatar_offset_y` | `sessions`, `agents` | Vertikale Position (-50% – +50%, Default 0), via CSS `translate()` | Darstellung via CSS `transform: scale(S) translate(X%, Y%)` im `.card-image` Container (`overflow: hidden`). ### 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, initialOffsetX: 0, initialOffsetY: 0, onChange: (imageData, scale, offsetX, offsetY) => { /* Live-Preview */ } }); // Werte auslesen: editor.getImage(), editor.getScale(), editor.getOffsetX(), editor.getOffsetY() // 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 - **Offset X/Y**: Range-Slider -50% – +50% für horizontale/vertikale Positionierung - **Clear**: Bild entfernen ### Dashboard-Darstellung Cards zeigen Avatare mit `transform: scale(S) translate(X%, Y%)` im `.card-image` Container (`overflow: hidden`). Die Helper `buildAgentCard()` und `buildSessionCard()` in `ui.js` nutzen `buildTransformStyle(scale, offsetX, offsetY)` dafür. --- ## 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 ``` ### 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 { "Port": 19000, // Server-Port (Default 19000, erfordert Neustart) "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) } ``` ### appsettings.Production.json Überschreibt Werte aus `appsettings.json` im Production-Environment. Wird **nicht** beim Publish mitkopiert (liegt nur im `publish/`-Verzeichnis). Verwaltung über die **Admin-UI** (Servereinstellungen, `#/server-settings`). ### Environment - `ASPNETCORE_ENVIRONMENT=Production` → lädt `appsettings.Production.json` - Tokens und Secrets werden über die Server-Settings-UI in `appsettings.Production.json` geschrieben --- ## 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