# 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 (Preparer + Review + Finalizer)](#multi-agent-system-preparer--review--finalizer) 19. [Interaktive Rückfragen (ask_user_choice)](#interaktive-rückfragen-ask_user_choice) 20. [Plan-Modus](#plan-modus) 21. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages) 22. [Workspace & File-Explorer](#workspace--file-explorer) 23. [Avatar-System](#avatar-system) 24. [LaTeX/Math-Rendering](#latexmath-rendering) 25. [Windows Desktop App](#windows-desktop-app) 26. [Deployment](#deployment) 27. [Konfiguration](#konfiguration) 28. [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) │ ├── TelegramBotRecord.cs # Telegram Bot Entität │ ├── 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 + WebP-Konvertierung (512px + 256px Thumbnail) │ ├── 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 # Bot-CRUD, Webhook, Session-Config, Registration │ ├── OpenClawEndpoint.cs # OpenClaw Session Import │ ├── 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) │ ├── ServerSettingsEndpoint.cs # Admin: Server-Settings lesen/schreiben (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-helpers.js # DOM-Helpers ($, esc, truncate), Toast, ScrollDown, Meta-Bar │ │ ├── markdown.js # Markdown-Rendering (renderMarkdown, LaTeX/Code-Schutz) │ │ ├── cards.js # Card-Builder (buildSessionCard, buildAgentCard) │ │ ├── msg-actions.js # Nachrichten-Aktionen (Delete, Mark, Share, Reply, Timestamp) │ │ ├── tool-ui.js # Tool-Boxes, Diffs, Todo-Liste, Visibility-Logik │ │ ├── modals.js # Modale Dialoge (System-Prompt, Command-Output, Krea-Image) │ │ ├── stream-ui.js # Stream-Bubble, Review-Streaming │ │ ├── bubbles.js # Message Bubbles (User, Assistant, Error, Warning, Choice, Review) │ │ ├── chat.js # Chat-Logic, SSE-Streaming, Plan-Mode │ │ ├── history.js # History-Rendering, Polling │ │ ├── archive.js # Archiv-Loading │ │ ├── reply.js # Reply/Quote-Handling │ │ ├── files.js # File-Upload, Workspace-Refs │ │ ├── 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 │ │ │ ├── telegram-bot-page.js # Telegram-Bot-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 │ └── Build/ # Build-Output (NICHT editieren!) ├── Linux/ │ ├── bin/ obj/ publish/ └── Windows/ ├── bin/ obj/ publish/ └── 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 User (1) ─── (*) TelegramBot TelegramBot (1) ─── (*) Session (via telegram_bot_id) ``` ### Tabellen | Tabelle | Beschreibung | |-------------------|-----------------------------------------------------| | `users` | Benutzer (username, password_hash, display_name, is_admin, persona, settings, default_path) | | `tokens` | Auth-Tokens (token → user_id) | | `agents` | AI-Agenten (model, custom_commands, auto_commit, use_soul/identity/user_info/memory, use_web_search, web_search_version, allow_user_choice, task_instruction, avatar_scale/offset, avatar_small, is_public) | | `sessions` | Chat-Sessions (description, image, image_small, workspace, context, custom_commands, telegram_bot_id, telegram_chat_id, avatar_scale/offset, preparer_agent_id, review_agent_id, max_review_iterations, finalizer_agent_id) | | `chains` | Request-Chains (model, tokens, iterations, timing, tool_choice, tool_names, stream_error_info, plan_mode, archive_id) | | `messages` | Einzelne Nachrichten (text, tool_use, tool_result, error, web_search_result), Tag für Sub-Agent-Markierung oder Import-Herkunft (`openclaw-import`), SortKey für korrekte Reihenfolge | | `attachments` | Datei-Anhänge (base64, image/document) | | `archives` | Archivierte Chain-Gruppen | | `agent_skills` | Agent ↔ Skill Zuordnung | | `cronjobs` | Geplante Tasks (cron expression, prompt, notify_session_id, is_enabled, last_run, next_run) | | `shared_messages` | Geteilte Nachrichten (Share-Token, Ablaufdatum) | | `telegram_bots` | Telegram Bots (bot_token, webhook_secret, name, is_public) – eigenständige Entität | ### Zentrale Records **ChainRecord** – Repräsentiert einen kompletten Request-Response-Zyklus: - `ChainId` – Eindeutige ID - `SessionId` – Zugehörige Session - `AgentId` – Zugehöriger Agent - `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`, `awaiting_user_choice`, `preparer_query` - `ToolChoice` – Verwendete Tool-Choice-Strategie (automatisch: `any` wenn Workspace/Skills/Memory, sonst `auto`) - `ToolNames` – Komma-separierte Liste der verwendeten Tools - `ArchiveId` – Zugehöriges Archiv (wenn archiviert) - `StreamErrorInfo` – Fehlerdetails bei Stream-Abbrüchen - `PlanMode` – Ob die Chain im Plan-Modus erstellt wurde (informativ) - `DurationMs` – Gesamtdauer der Chain **MessageRecord** – Eine einzelne Nachricht innerhalb einer Chain: - `Role` – `user`, `assistant` - `Type` – `text`, `tool_use`, `tool_result`, `error`, `web_search_result` - `Content` – Textinhalt oder JSON (bei tool_use: Input-JSON) - `ToolName`, `ToolCallId` – Bei Tool-Aufrufen - `IsError` – Ob die Nachricht ein Fehler ist - `Iteration` – Welche Tool-Loop-Iteration - `AttachmentCount` – Anzahl angehängter Dateien - `AttachmentNames` – JSON-Array mit Dateinamen - `Tag` – Markierung (`null` = normal, `"preparer"/"review"/"finalizer"` = Sub-Agent-Message (wird aus API-History gefiltert), `"openclaw-import"` = importierte Nachricht (wird immer in History einbezogen)) - `IsMarked` – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen) - `DeletedOn` – Soft-Delete Zeitstempel **SessionWithStats** – Erweitertes Session-Objekt für Listen-Endpunkt: - Enthält alle `SessionRecord`-Felder + `MessageCount`, `ArchiveMessageCount`, `TotalMessageCount` - `TelegramConfigured`, `TelegramLinked` – Telegram-Status (Bot zugewiesen / Chat-ID verknüpft) - `GitStatus` – Inline Git-Status-Summary (`Add`, `Edit`, `Delete`, `Outgoing`) oder `null` wenn kein Workspace (wird im Endpoint hinzugefügt, nicht Teil des Records) - `Image` wird durch `ImageSmall` ersetzt (Thumbnail für Dashboard-Performance) **GitStatusSummary** – Kompakte Git-Status-Info: - `Add`, `Edit`, `Delete` – Anzahl geänderter Dateien nach Typ - `Outgoing` – Anzahl ausgehender (nicht gepushter) Commits --- ## 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 + Reminder-Generierung | | `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 + WebP-Konvertierung (512px + 256px Thumbnail) 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` (Liste inkl. inline Git-Status + Thumbnail) | | **Agents** | `/api/agents/` | CRUD (Liste inkl. inline Skills + Thumbnail) | | **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/` | Bot-CRUD (`/bots`, `/bots/available`), `POST webhook/{botId}` (unauthenticated), Session-Config, Webhook-Management | | **OpenClaw** | `/api/openclaw/` | `GET sessions` (Liste importierbarer Sessions), `POST import` (Import in bestehende Session) | | **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/{botId}` (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 (automatisch bestimmt) - Prompt-Blöcke (`PromptBlocks`) – für Reminder-Generierung in der History - 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 | | `#/telegram-bot/{botId}` | `telegram-bot-page.js` | Telegram-Bot-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-helpers.js** – DOM-Helpers (`$`, `esc`, `truncate`), Toast-System, ScrollDown, Meta-Bar, Button-Builder - **markdown.js** – Markdown-Rendering (`renderMarkdown`), LaTeX/Code-Schutz, Krea-Gallery-Wrapping - **cards.js** – Card-Builder (`buildAgentCard()`, `buildSessionCard()`, `buildTelegramBotCard()`) - **msg-actions.js** – Nachrichten-Aktionen (Delete, Mark, Share, Reply-Button, Timestamp) - **tool-ui.js** – Tool-Boxes (Diffs, Todos, File-Content), Visibility-Logik - **modals.js** – Modale Dialoge (System-Prompt, Command-Output, Krea-Image) - **stream-ui.js** – Stream-Bubble, Review-Streaming-Rendering - **bubbles.js** – Message Bubbles (User, Assistant, Error, Warning, Choice, Review, Attachments) - **chat.js** – SSE-Stream-Handling, sendMessage, Plan-Mode - **history.js** – Chat-History laden, paginieren, rendern, Polling - **archive.js** – Archiv-Loading und -Rendering - **reply.js** – Reply/Quote-Handling (Set, Clear, Format) - **files.js** – File-Upload, Workspace-Refs - **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 (cards.js) - **`buildAgentCard(agent)`** – Erzeugt ein `.card` DOM-Element für einen Agent (Avatar, Name, Model-Badge, Feature-Badges: autoCommit, useSoul, useIdentity, useUserInfo, useMemory, webSearch, Skills, Public/Fremd). Skills werden inline vom API-Endpunkt mitgeliefert. 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, Git-Status). Git-Status wird inline vom API-Endpunkt mitgeliefert. Wird im Dashboard und im Session-Picker-Modal (Cronjob-Formular) verwendet. - **`buildTelegramBotCard(bot)`** – Erzeugt ein `.card` DOM-Element für einen Telegram Bot (Name, maskierter Token, Session-Count, Public/Fremd-Badges). Wird im Dashboard und im Bot-Picker-Modal (Session-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: - **Max 120 Messages**: Die History wird auf maximal 120 Messages begrenzt (älteste entfernt) - **Ä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) - **System-Prompt-Reminder**: Alle ~30 Messages wird ein automatisch generierter Reminder als User/Assistant-Paar injiziert (z.B. `[System Reminder] Beachte weiterhin den System-Prompt (Identity, Workspace, Kontext, Memory, ...)`). Bekämpft das "Lost-in-the-Middle"-Problem bei langen Konversationen. Der Reminder-Text wird aus den aktiven Prompt-Block-Labels generiert (`SystemPromptBuilder.BuildReminder()`). Nur aktiv wenn ≥2 Blöcke vorhanden. --- ## 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 | ### Interaktive Rückfragen (wenn AllowUserChoice aktiviert) | Tool | Beschreibung | |--------------------|-----------------------------------------------------| | `ask_user_choice` | Stellt dem User eine Frage mit 2-5 Auswahlmöglichkeiten | Dieses Tool wird **nicht ausgeführt**, sondern als SSE-Event an den Client weitergeleitet. Siehe [Interaktive Rückfragen](#interaktive-rückfragen-ask_user_choice). ### Web-Suche (wenn WebSearchVersion konfiguriert) Nutzt Anthropics eingebaute Web-Suche als Server-Side-Tool. ### Plan-Modus: Read-only Tools Im Plan-Modus werden nur folgende Tools bereitgestellt: `read_file`, `list_files`, `glob`, `grep`, `execute`, `todo_write`, `memory_*`, `ask_user_choice`. Write-Tools (`write_file`, `edit_file`, `delete_file`) werden gefiltert. ### Tool-Choice (automatisch) Die Tool-Choice-Strategie wird automatisch bestimmt (nicht konfigurierbar pro Agent): - **`any`**: Automatisch wenn Workspace, Skills oder Memory vorhanden. Erste Iteration erzwingt Tool-Nutzung, danach `auto`. Verhindert dass der Agent nur Text antwortet wenn Tools benötigt werden. - **`auto`**: Wenn keine Tools verfügbar. Claude entscheidet frei. - **`none`**: Im `/no-tools`-Modus. ### 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 (WebP, 512px) + `AvatarSmall` (WebP, 256px Thumbnail) | | `Model` | `sonnet`, `sonnet-4-5`, `sonnet-4-6`, `opus`, `opus-4-5`, `opus-4-6`, `haiku`, `haiku-4-5` | | `CustomCommands` | JSON-Array mit Slash-Commands (Label + Prompt) | | `AutoCommit` | `""` (nichts), `"mention"` (anmerken – nur auf User-Request), `"enforce"` (erzwingen – nach jeder Änderung), `"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`) | | `AllowUserChoice` | `ask_user_choice`-Tool verfügbar (Default: true) | | `TaskInstruction` | Aufgabe/Anweisung – optionaler Text, wird als `## Anweisungen`-Block in System-Prompt eingefügt | | `IsPublic` | Andere User können den Agent in ihren Sessions verwenden (aber nicht bearbeiten) | ### 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. Anweisungen (TaskInstruction) │ → Aufgaben-/Anweisungstext (optional) │ 4. User-Persona (UseUserInfo) │ → Infos über den User │ 5. Workspace-Info │ → Tools + Workspace-Pfad │ 6. Rückfragen (AllowUserChoice) │ → Hinweis zu ask_user_choice Tool │ 7. Session-Kontext │ → Freiformtext │ 8. Skills (Agent-Skills) │ → Skill-Prompts │ 9. Memory (UseMemory) │ → Memory-Regeln + MEMORY.md Inhalt │10. Git-Verhalten (AutoCommit) │ → Commit-Regeln │11. Markierte Nachrichten │ → Vom User als wichtig markierte Messages │ [Plan-Modus-Block] │ → Nur wenn planMode=true (überspringt 10) │ [Preparer-Instruktionsblock] │ → Nur für Preparer-Agents └──────────────────────────────────────────┘ ``` 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 Telegram Bots sind **eigenständige Entitäten** mit einer eigenen Tabelle (`telegram_bots`), die am Dashboard verwaltet werden (wie Agents, Sessions, Cronjobs). Ein Bot kann **mehreren Sessions** zugewiesen werden – jede Session hat ihren eigenen Chat-Kontext. ### Datenmodell **Telegram Bot** (`telegram_bots`): - `BotId` – UUID - `UserId` – Owner - `Name` – Anzeigename - `BotToken` – Bot-Token von @BotFather - `WebhookSecret` – Einmalig generierter Token für Webhook-Verifizierung - `IsPublic` – Andere User können den Bot in ihren Sessions verwenden (aber nicht bearbeiten) **Session-Felder:** - `TelegramBotId` – Verweis auf den zugewiesenen Bot (wie `AgentId`) - `TelegramChatId` – Verknüpfte Chat-ID (1 Session = max. 1 Telegram-Chat) ### Webhook-Flow ``` 1. User erstellt Telegram Bot im Dashboard (Name + Token) 2. User registriert Webhook auf der Bot-Page → Setzt Webhook-URL: {baseUrl}/api/telegram/webhook/{botId} 3. User weist Bot einer Session zu (Session-Settings, Bot-Picker) 4. Telegram sendet Updates an Webhook 5. AuthMiddleware lässt Webhook-Requests durch (kein Cookie nötig) 6. TelegramEndpoint prüft X-Telegram-Bot-Api-Secret-Token Header 7. TelegramService verarbeitet Update: - Bot laden via botId - Chat-ID extrahieren - Session suchen: telegram_bot_id = botId AND telegram_chat_id = chatId - /start → Auto-Link (wenn genau 1 unverknüpfte Session) oder Chat-ID anzeigen - /clear → History archivieren - /status → Session-Info anzeigen - Text → An Claude weiterleiten (Non-Streaming) ``` ### Auto-Link-Logik Bei `/start` oder einer normalen Nachricht, wenn keine Session für den Chat verknüpft ist: 1. Suche Sessions mit diesem Bot, die **noch keine Chat-ID** haben 2. **Genau 1 gefunden** → Automatisch verknüpfen + Bestätigungsnachricht 3. **0 oder mehrere gefunden** → "Deine Chat-ID ist X, bitte im Web-UI zuweisen" Unlink ist **nur über das Web-UI** möglich (kein Telegram-Befehl). ### 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. ### Dashboard & Frontend - **Dashboard**: Telegram-Bots-Section mit Cards (Name, maskierter Token, Session-Count) - **Bot-Page** (`#/telegram-bot/{botId}`): Name, Token, Webhook-Secret (readonly), Webhook-Verwaltung (registrieren/status/entfernen), Public-Checkbox, Löschen - **Session-Page**: Bot-Picker (wie Agent-Picker), readonly Chat-ID mit Unlink-Button --- ## 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 (Preparer + Review + Finalizer) Das Multi-Agent-System ermöglicht bis zu **vier Agents** pro Session: einen **Worker**, einen optionalen **Vorbereiter (Preparer)**, einen optionalen **Reviewer** und einen optionalen **Finalizer**. ### Architektur ``` User-Message → Preparer-Phase (optional): → Preparer-Agent bekommt: User-Message + History (inkl. eigener vorheriger Messages) → Preparer arbeitet (kann Tools nutzen: read_file, execute etc.) → Erledigt Vorarbeit mit seinen Fähigkeiten (z.B. JSON bauen, Daten aufbereiten) → [QUESTION] → Rückfrage an User, Flow stoppt, User muss erneut eingeben → Antwort ohne [QUESTION] → automatisch weiterleiten an Worker → 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 | |---------|---------------|-------------| | `preparer_agent_id` | `PreparerAgentId` | ID des Vorbereiter-Agents (optional, null = kein Preparer) | | `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) | ### Vorbereiter-Agent (Preparer) Der Preparer wird **vor** dem Worker-Agent aufgerufen. Er analysiert die User-Anfrage, kann Vorarbeit leisten (z.B. Daten aufbereiten, JSON bauen, Code analysieren) und gibt das Ergebnis an den Worker weiter. Bei unklaren Anfragen stellt er Rückfragen. #### Logik-Pattern: Default = Forward Die Logik ist **invertiert** gegenüber dem Review-Pattern: Jede Antwort wird automatisch weitergeleitet, **außer** sie enthält explizit `[QUESTION]`. Das ist robuster als ein Pflicht-Tag wie `[FORWARD]`, weil LLMs dazu neigen, Tags zu vergessen. | Antwort | Ergebnis | |---------|----------| | Ohne `[QUESTION]` | Automatisch weitergeleitet (`ShouldForward = true`) | | Mit `[QUESTION]` | Rückfrage an User, Flow stoppt (`ShouldForward = false`) | #### Backend-Flow (Preparer) 1. **ChatEndpoint** – Prüft ob `ctx.Preparer` vorhanden 2. **AnthropicService.RunPreparer()** – Ruft `RunAgentChat()` auf mit `agentType = "preparer"` 3. **Entscheidung** – `ShouldForward` prüft ob `[QUESTION]` im Text: - **Forward**: Aufbereiteter Text wird als `[Vorbereitet von AgentName]:`-User-Message in DB gespeichert, History wird neu geladen, StreamChat startet - **Rückfrage**: SSE `preparer_done` mit `forwarded: false`, Stream endet mit `stopReason: "preparer_query"`, User muss erneut eingeben #### Preparer-History Der Preparer bekommt seine eigene History (`BuildHistoryFromDb(sessionId, includeTag: "preparer")`), damit er seine vorherigen Rückfragen und User-Antworten sieht. Der Session-Agent bekommt die History **ohne** Preparer-Messages (Tag-Filter). #### System-Prompt Der Preparer bekommt einen speziellen Instruktionsblock an seinen System-Prompt angehängt: - Erklärt die Rolle als Vorbereiter - Definiert `[QUESTION]`-Mechanismus für Rückfragen - Instruiert aktive Vorarbeit mit eigenen Fähigkeiten und Tools - Betont: Antworten werden automatisch weitergeleitet #### SSE-Events (Preparer) | Event | Daten | Beschreibung | |-------|-------|-------------| | `preparer_start` | `{ agent: "Name" }` | Preparer-Phase beginnt | | `preparer_text` | `{ text: "..." }` | Preparer-Agent-Text (Token-Stream) | | `preparer_tool_use` | `{ tool: "name", input: "..." }` | Preparer nutzt Tool | | `preparer_done` | `{ forwarded: true/false }` | Preparer-Phase beendet | ### Backend-Flow (Review) 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, tag=null) - **Preparer-Antworten**: Messages mit `tag="preparer"` (aus API-History gefiltert, nur für Frontend-Anzeige) - **Aufbereiteter Prompt**: Als User-Message mit Prefix `[Vorbereitet von AgentName]:` - **Review-Feedback**: Als User-Messages mit Prefix `[Review-Feedback von AgentName (Runde X/Y)]:` - **Review-Agent-Antworten**: Messages mit `tag="review"` (aus API-History gefiltert) - **Finalizer-Antworten**: Messages mit `tag="finalizer"` (aus API-History gefiltert) ### 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`, `bubbles.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 Preparer/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 (mit `Tag = agentType`, wird aus API-History gefiltert) #### 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**: Vierter Agent-Picker: "Vorbereiter-Agent", "Review-Agent", "Finalizer-Agent" im "Weitere Einstellungen"-Bereich - **Chat-UI**: Preparer-Bubbles (`.preparer-bubble`, Info-Farbe, 🔍-Icon), Finalizer-Bubbles (`.finalizer-bubble`, grüne Farbe, 🏁-Icon) - **History**: `[Preparer: AgentName]` → styled Header mit `fa-filter` Icon, `[Vorbereitet von AgentName]` → styled Forwarded-Header, `[Finalizer: AgentName]` → styled Header, `[FORWARD]`/`[QUESTION]` werden ausgeblendet - **Dashboard**: 🏁-Badge auf Session-Cards mit Finalizer --- ## Interaktive Rückfragen (ask_user_choice) Das `ask_user_choice`-Tool ermöglicht dem Agent, dem User **klickbare Auswahlmöglichkeiten** zu präsentieren, wenn es mehrere sinnvolle Wege gibt oder eine Entscheidung vom User getroffen werden muss. ### Tool-Definition ```json { "name": "ask_user_choice", "description": "Stellt dem User eine Frage mit Auswahlmöglichkeiten", "input_schema": { "properties": { "question": { "type": "string" }, "choices": { "type": "array", "items": { "type": "string" }, "minItems": 2, "maxItems": 5 } } } } ``` ### Agent-Konfiguration - **`allow_user_choice`** (AgentRecord) – Boolean, Default `true`. Steuert ob das Tool verfügbar ist. - Dashboard-Badge: `🔵 Rückfragen` wenn aktiv - System-Prompt-Hinweis: "Nutze `ask_user_choice` sparsam – nur wenn die Entscheidung vom User getroffen werden soll" - Bei Review/Finalizer/Cronjobs: **Immer deaktiviert** (kein User zum Fragen) ### Backend-Flow 1. Claude ruft `ask_user_choice` auf → Backend erkennt im `ProcessStream` 2. Tool wird **nicht ausgeführt** – stattdessen SSE-Event `user_choice` gesendet 3. `stopReason` wird auf `"awaiting_user_choice"` gesetzt (Guard-Clause verhindert Überschreibung durch `message_delta`) 4. `tool_use`-Block wird in DB gespeichert, Chain wird finalisiert 5. User wählt Option → Frontend sendet `POST /api/chat` mit `choiceToolCallId` + `choiceResult` 6. Backend findet die Chain mit dem offenen `tool_use`, speichert `tool_result` dort 7. Neuer `StreamChat` startet – History enthält `tool_use` + `tool_result`, Claude arbeitet weiter ### Frontend-Flow - **Live**: SSE-Event `user_choice` → `addChoiceBubble()` rendert Frage + klickbare Buttons - **History**: `tool_use` mit `toolName === 'ask_user_choice'` → Voraus-Suche nach `tool_result`: - Gefunden: `addChoiceBubbleStatic()` mit ausgewählter Option (hervorgehoben, andere ausgegraut) - Nicht gefunden: Aktive Buttons (klickbar, z.B. nach Page-Refresh) - **Robustheit**: Guards gegen `choices` als String statt Array (`Array.isArray()` Check) - **CSS**: `.choice-bubble`, `.choice-btn` (Primary-Color-Outline), `.choice-selected`/`.choice-unselected` ### SSE-Events | Event | Daten | Beschreibung | |-------|-------|-------------| | `user_choice` | `{ toolCallId, question, choices[] }` | Rückfrage mit Auswahloptionen | --- ## Plan-Modus Der Plan-Modus ist ein **Read-only-Betriebsmodus**, in dem der Agent den Code analysieren, Rückfragen stellen und Pläne erstellen kann – aber keine Dateien ändern oder Befehle ausführen darf. ### Aktivierung - **Frontend**: Toggle-Button (🗺️ `fa-map`) im Chat-Input-Bereich - **API**: `planMode: true` im `ChatRequest` - **Pro Chain**: `plan_mode` Feld in der Chain (rein informativ für History-Anzeige) - Kein Agent-Setting – wird **pro Nachricht** vom User gesteuert ### Backend-Auswirkungen | Aspekt | Normal | Plan-Modus | |--------|--------|-----------| | **Tools** | Alle (read_file, write_file, edit_file, delete_file, execute, ...) | Nur read-only: `read_file`, `list_files`, `glob`, `grep`, `execute`, `todo_write`, `memory_*`, `ask_user_choice` | | **System-Prompt** | Normal | + Plan-Modus-Block: "Du darfst NUR lesen und analysieren, KEINE Änderungen vornehmen" | | **toolChoice** | `"any"` (automatisch wenn Workspace/Skills/Memory) | Immer `"auto"` (Claude darf frei antworten) | | **Chain-Speicherung** | `plan_mode = 0` | `plan_mode = 1` | ### System-Prompt-Anpassungen im Plan-Modus Folgende Blöcke werden **übersprungen**: Git-Commits. Der Tools-Block listet nur Read-only-Tools auf. Skills werden als "nur zur Kenntnis, nicht ausführbar" markiert. ### Frontend - **Toggle**: `#planModeBtn` – wechselt `_planModeActive`, ändert Button-Styling + Placeholder-Text - **Chain-Meta**: Badge `📋 Plan-Modus` bei Chains die im Plan-Modus erstellt wurden - **System-Prompt-Modal**: Zeigt den angepassten System-Prompt wenn Plan-Modus aktiv - **Session-Wechsel**: Plan-Modus wird zurückgesetzt ### Choice-Response im Plan-Modus Wenn im Plan-Modus eine `ask_user_choice`-Rückfrage kommt und der User antwortet, erbt die Choice-Response den `planMode` des vorherigen Chains. --- ## 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 (WebP, max 512px via ImageService) | | `image_small` / `avatar_small` | `sessions` / `agents` | Thumbnail (WebP, 256px, Q70) für Listen/Dashboard | | `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`). ### Bild-Verarbeitung (ImageService) Alle Bilder werden beim Upload in **WebP** konvertiert (statt PNG), was ~60-80% Speicherersparnis bringt: | Variante | Methode | Max. Größe | Qualität | Verwendung | |----------|---------|------------|----------|------------| | Voll | `ResizeDataUrl()` | 512px | Q80 | Editor, Detail-Ansicht (`GET /api/agents/{id}`, `GET /api/sessions/{id}`) | | Thumbnail | `ResizeDataUrlSmall()` | 256px | Q70 | Listen, Dashboard (`GET /api/agents`, `GET /api/sessions`) | Die Listen-Endpoints liefern das Thumbnail als `avatar`/`image`-Feld (Fallback auf Voll-Bild wenn kein Thumbnail existiert). Die Detail-Endpoints liefern das volle Bild. ### 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 `cards.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 `markdown.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 --- ## OpenClaw Import Sessions können mit Chatverlauf aus einer **OpenClaw-Instanz** befüllt werden. Die Import-Quelle ist eine SQLite-Datenbank (`Build//publish/data/openclaw-sessions.db`). ### Funktionsweise - **`GET /api/openclaw/sessions`** – Listet alle Sessions aus der OpenClaw-DB mit Metadaten (Message-Count, Model, Summary, Preview-Messages) - **`POST /api/openclaw/import`** – Importiert eine OpenClaw-Session in eine bestehende Gott-Session ### Import-Logik 1. Liest alle `message`-Entries aus der OpenClaw-DB, gruppiert nach `chainId` 2. **Nur Text-Nachrichten** werden importiert – Tool-Use/Tool-Result werden übersprungen 3. **User-Message-Bereinigung**: Webchat-Prefixe (`[Direct 1:1 webchat ...]`, `[Current message ...]`, `[cron:...]`), `Conversation info`-Blöcke werden entfernt 4. Pro Chain wird ein `ChainRecord` + `MessageRecords` erstellt, alle mit `tag = 'openclaw-import'` 5. Timestamps werden aus OpenClaw übernommen (`created_at`) 6. **Re-Import = Update**: Vorherige Import-Chains (erkennbar am Tag) werden gelöscht und neu importiert. Bestehende Compactions werden ebenfalls gelöscht. ### Sortierung (sort_key) Importierte Messages werden **immer vor** normalen Messages einsortiert, da sie historisch älter sind: ```sql sort_key = CASE WHEN tag = 'openclaw-import' THEN id - 10000000000 ELSE id END ``` Alle History-Queries (initial load, before/after-Paging, Polling) sortieren nach `sort_key`. OpenClaw-Messages bekommen negative sort_keys → erscheinen immer ganz oben. Innerhalb jeder Gruppe (OpenClaw / normal) wird nach `id` sortiert. ### Frontend - **Dashboard**: Session-Menü → „OpenClaw Import" → Modal mit Session-Auswahl + Preview - **Chat**: Importierte Nachrichten zeigen ein blaues „OC"-Badge --- ## Deployment **WICHTIG:** Das Deployment ist ein **manueller Vorgang**, der durch den Benutzer auf dem Server durchgeführt werden muss. Das System führt kein automatisches Deployment durch. ### Goldene Regel für Änderungen * **Arbeiten Sie AUSSCHLIESSLICH im Verzeichnis `wwwroot/`** für Frontend-Änderungen. * **Editiere NIEMALS Dateien in `Build/*/publish/wwwroot/`!** Diese Verzeichnisse werden bei jedem Build überschrieben. * Das Verzeichnis `Build/*/publish/data/gott.db` darf niemals manuell gelöscht, geleert oder direkt modifiziert werden. ### Deployment-Vorgang (Beispiel für Linux) Das vorhandene Shell-Skript `deploy.sh` dient lediglich als Hilfestellung für den manuellen Prozess: 1. **Frontend-Anpassungen vornehmen** (in `wwwroot/`). 2. **Manuelles Deployment** auf dem Server ausführen: ```bash # Beispiel-Ablauf: systemctl stop gott dotnet publish -c Release -o Build/Linux/publish systemctl start gott ``` ### systemd Die Anwendung ist als systemd-Service konfiguriert und läuft standardmäßig 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 `Build//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`) 4. **Bild-Migration** – Konvertierung bestehender PNG/JPEG-Avatare zu WebP + Generierung von Thumbnails (`avatar_small`, `image_small`) 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