# ARCHITECTURE.md – Gott **Gott** ist eine selbst-gehostete AI-Chat-Plattform, gebaut auf **.NET 10** mit **Anthropic Claude API** als Haupt-Provider (inkl. Claude CLI-Integration) sowie **OpenAI-kompatiblen Providern** (Nvidia, Pollinations, Gemini, OpenAI). Sie bietet Multi-User-Support, persistente Agent-Memory, 19 Tools (Dateien, Shell, Suche, Memory, Remote-Execution, Web-Search, GitHub Code Search), semantische Suche via lokalen Embeddings, Telegram-Integration, Cronjobs, Remote-Verbindungen, Krea-Bildgenerierung und einen Multi-Provider Key-Scanner – 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. [Multi-Provider-System (OpenAI-kompatibel)](#multi-provider-system-openai-kompatibel) 8. [Chat-Pipeline](#chat-pipeline) 9. [Tool-System](#tool-system) 10. [Agent-System](#agent-system) 11. [System-Prompt-Aufbau](#system-prompt-aufbau) 12. [Memory-System](#memory-system) 13. [Skill-System](#skill-system) 14. [Telegram-Integration](#telegram-integration) 15. [Cronjob-System](#cronjob-system) 16. [Krea-Bildgenerierung](#krea-bildgenerierung) 17. [Auth & Multi-User](#auth--multi-user) 18. [Admin-Benutzerverwaltung](#admin-benutzerverwaltung) 19. [Multi-Agent System (Preparer + Review + Finalizer)](#multi-agent-system-preparer--review--finalizer) 20. [Interaktive Rückfragen (ask_user_choice)](#interaktive-rückfragen-ask_user_choice) 21. [Plan-Modus](#plan-modus) 22. [Web-Search](#web-search) 23. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages) 24. [Workspace & File-Explorer](#workspace--file-explorer) 25. [Avatar-System](#avatar-system) 26. [LaTeX/Math-Rendering](#latexmath-rendering) 27. [Windows Desktop App](#windows-desktop-app) 28. [OpenClaw Import](#openclaw-import) 29. [Remote-Verbindungen](#remote-verbindungen) 30. [Semantische Suche (Embedding)](#semantische-suche-embedding) 31. [API-Key-Verwaltung](#api-key-verwaltung) 32. [Key-Scanner-System](#key-scanner-system) 33. [Claude CLI Integration](#claude-cli-integration) 34. [Datenbank-Explorer](#datenbank-explorer) 35. [MCP-Server-Integration (Internal API)](#mcp-server-integration-internal-api) 36. [Deployment](#deployment) 37. [Konfiguration](#konfiguration) 38. [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 │ │ │ │ │ │ │ │ │ │Keys│ │ │ CLI │ │ │ │ │ │ │DbEx│ ├──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴────┤ │ Services Layer │ │ AnthropicService · OpenAiService · ChatService · ToolService│ │ ClaudeCliService · DatabaseService · MemoryService · SkillService│ │ SseHelper · EmbeddingService · VectorSearchService │ │ RemoteConnectionManager · TelegramService │ │ CronjobService · KreaService · KeyCheckDispatcher │ │ KeyScannerService · GitHubEventScannerService │ ├─────────────────────────────────────────────────────────────┤ │ SQLite (data/gott.db) via Dapper │ │ ONNX Runtime (Embeddings) │ │ Filesystem (data/agents/, data/skills/) │ ├─────────────────────────────────────────────────────────────┤ │ Anthropic Claude API (claude-sonnet/opus/haiku) │ │ Claude CLI (OAuth-basiert, kein API-Key nötig) │ │ OpenAI-kompatible APIs (Nvidia, Pollinations, Gemini, OpenAI) │ │ 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), Claude CLI (OAuth), OpenAI-kompatible APIs (Nvidia, Pollinations, Gemini, OpenAI) | | **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 | | **Embeddings**| Microsoft.ML.OnnxRuntime + all-MiniLM-L6-v2 (384-dim Sentence-Embeddings) | | **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 │ ├── ApiKeyRecord.cs # API-Key-Verwaltung (Provider, Name, Key, IsActive, Status, Plan, Usage, SourceUrl) │ ├── ScannedKeyRecord.cs # Gescannte GitHub-Keys (SourceUrl, Status, Plan, Usage, AdoptedApiKeyId) │ └── ChatContext.cs # Runtime-Kontext für einen Chat + SubAgentContext │ ├── Services/ # Business-Logic │ ├── DatabaseService.cs # SQLite Schema, Migrationen, Core (partial class) │ ├── DatabaseService.Sessions.cs # Sessions, Archives, Chains, Messages, Attachments │ ├── DatabaseService.Agents.cs # Agents, Agent Skills │ ├── DatabaseService.Users.cs # Users, Shared Messages │ ├── DatabaseService.Integrations.cs # Telegram, Cronjobs, API Keys, OpenClaw Import │ ├── AuthService.cs # Token-basierte Authentifizierung │ ├── ChatService.cs # Kontext-Aufbau, History-Builder │ ├── AnthropicService.cs # Claude API – Haupt-Service (partial class) │ ├── AnthropicService.Streaming.cs # Claude API – Streaming-Logic (StreamChat) │ ├── AnthropicService.Agents.cs # Claude API – Sub-Agent-Logic (Preparer, Review, Finalizer) │ ├── AnthropicService.Cli.cs # Claude API – CLI-basiertes Streaming (StreamChatViaCli) │ ├── ClaudeCliService.cs # Claude Code CLI Wrapper (OAuth-basiert, kein API-Key nötig) │ ├── OpenAiService.cs # OpenAI-kompatible Provider (Nvidia, Pollinations, Gemini, OpenAI) │ ├── SseHelper.cs # Shared SSE-Utilities (WriteSseEvent, TryWriteSse, FinalizeChain) │ ├── ToolService.cs # Tool-Definitionen + Ausführung (19 Tools) │ ├── SystemPromptBuilder.cs # Modularer System-Prompt aus Blöcken │ ├── MemoryService.cs # Agent-Gedächtnis (Dateisystem) │ ├── SkillService.cs # Skill-Verwaltung (Dateisystem) │ ├── EmbeddingService.cs # Lokales Sentence-Embedding (all-MiniLM-L6-v2, ONNX) │ ├── VectorSearchService.cs # Semantische Suche über Message-Embeddings + Memory-Files │ ├── EmbeddingBackgroundService.cs # Backfill + Live-Embedding neuer Messages (BackgroundService) │ ├── RemoteConnectionManager.cs # WebSocket-basierte Remote-Verbindungen (Command-Execution) │ ├── 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) │ ├── AppPaths.cs # Zentrale Pfade (WwwRoot) – Dev vs. Publish-Modus │ ├── KeyScannerService.cs # API-Key-Scanner (GitHub Code Search, GitHub Issues/PRs, HuggingFace Hub, Google/Serper) │ ├── GitHubEventScannerService.cs # Echtzeit-Scanner: GitHub Public Events API (PushEvent-Diffs nach Keys scannen) │ ├── KeyPatterns.cs # Zentrale Key-Pattern-Definitionen für alle Scanner (Regex + Typ-Erkennung) │ ├── KeyCheckDispatcher.cs # Zentraler Dispatcher für Key-Checks aller Provider (normalisiertes Ergebnis) │ ├── AnthropicKeyChecker.cs # Anthropic Key-Validierung + Usage-Header-Auswertung │ ├── OpenAiKeyChecker.cs # OpenAI Key-Validierung (Models-Endpoint, Rate-Limits) │ ├── GoogleKeyChecker.cs # Google/Gemini Key-Validierung (Models + Quota-Check) │ ├── StripeKeyChecker.cs # Stripe Key-Validierung (Balance, Account-Info) │ └── ServerSettingsService.cs # Server-Settings Verwaltung (In-Memory-Cache + DB) │ ├── Endpoints/ # HTTP API (Minimal API) │ ├── AuthEndpoint.cs # Login, Logout, Me, Autologin │ ├── ChatEndpoint.cs # POST /api/chat (SSE Streaming + CLI-Fallback) │ ├── HistoryEndpoint.cs # History, Archive, Attachments, Marks, Chain-Details │ ├── SessionEndpoint.cs # CRUD + SystemPrompt, SystemPromptBlocks, PayloadPreview, Streaming, Exec, Git-Status │ ├── AgentEndpoint.cs # CRUD + Available (inkl. public agents) │ ├── WorkspaceEndpoint.cs # File-Tree, File-Read/Write, Browse, Agent-Files, Raw-Download │ ├── 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 │ ├── ModelsEndpoint.cs # Provider-Modelle (nvidia, pollinations, gemini, openai, refresh) │ ├── SharedEndpoint.cs # Nachrichten teilen (öffentliche Links) │ ├── AdminEndpoint.cs # Admin: User-CRUD, Impersonate, Login-Link (Admin-only) │ ├── ApiKeysEndpoint.cs # Admin: API-Key-CRUD, Check, CheckAll, Scan, Event-Scanner, Adopt + User: Available, Select (Admin-only / User) │ ├── RemoteEndpoint.cs # Remote-Verbindungen: WebSocket, Assign/Unassign, PowerShell-Script │ ├── InternalEndpoint.cs # Interne API für MCP-Server (Remote-Execute, Ask-User-Choice, Memory-CRUD, Token-gesichert) │ ├── DatabaseExplorerEndpoint.cs # Admin: DB-Tabellen lesen (Tables, TableData) (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 + Embedding-Status │ ├── 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, Remote) │ │ ├── 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 (currentSessionId, currentAgentId) │ │ ├── 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 + Embedding-Warnung │ │ ├── typing-phrases.js # Zufällige Typing-Indicator-Texte (Wienerisch) │ │ ├── pages/ # Seiten-Komponenten │ │ │ ├── chat-page.js # Haupt-Chat-Ansicht (inkl. Remote-Button, Usage-Badge-Overlay, Key-Switch-Modal) │ │ │ ├── dashboard.js # Dashboard / Home (inkl. API-Keys-Link, DB-Explorer-Link) │ │ │ ├── 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 │ │ │ ├── api-keys-page.js # Admin: API-Key-Verwaltung + Check + GitHub-Scanner + Event-Scanner │ │ │ ├── db-explorer-page.js # Admin: Datenbank-Explorer (Tabellen + Daten) │ │ │ ├── settings-page.js # App-Einstellungen │ │ │ ├── server-settings-page.js # Admin: Servereinstellungen │ │ │ ├── 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 │ ├── remote.ps1 # PowerShell-Script für Remote-Client-Verbindung │ ├── 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 ├── models/ # ONNX-Modelle (Embedding) │ └── all-MiniLM-L6-v2/ # Sentence-Embedding-Modell (model.onnx + vocab.txt) ├── models_cache.json # Gecachte Provider-Modelle (Nvidia, Pollinations, Gemini, OpenAI) ├── 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 – z.B. `claude-sonnet-4-6` oder `nvidia:deepseek-ai/deepseek-r1`, custom_commands, auto_commit, use_soul/identity/user_info/memory/auto_context, use_github_search, use_serper_search, 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, multi_model_mode, panel_agent_ids) | | `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, auto_context), Tag für Sub-Agent-Markierung oder Import-Herkunft (`openclaw-import`), SortKey für korrekte Reihenfolge, thought_signature (Gemini) | | `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 | | `api_keys` | API-Keys (key_id, provider, name, api_key, is_active, status, plan, usage_7d, usage_5h, source_url, checked_at, is_public, refresh_token, access_token_expires_at, oauth_provider) – mehrere Keys pro Provider möglich, Zuweisung pro Agent über `agent_api_keys` | | `agent_api_keys` | API-Key-Zuweisung pro User+Agent (user_id, agent_id, key_id) – ersetzt globales `users.selected_api_key_id` | | `scanned_keys` | Gescannte GitHub-Keys (scanned_key_id, api_key, source_url, source, type, status, plan, usage_7d, usage_5h, adopted_api_key_id, deleted) | | `scanner_logs` | Scanner-Logs (timestamp, type, message, scanner) – Logs von Key-Scanner und Event-Scanner | | `server_settings` | Server-Settings (key, value, type) – In-Memory-Cache über `ServerSettingsService` | | `message_embeddings` | Sentence-Embeddings für semantische Suche (message_id, vector BLOB, session_id, agent_id) | | `memory_file_embeddings` | Embeddings von Memory-Dateien (agent_id, file_path, chunk_index, vector BLOB) | ### Zentrale Records **ChainRecord** – Repräsentiert einen kompletten Request-Response-Zyklus: - `ChainId` – Eindeutige ID - `SessionId` – Zugehörige Session - `AgentId` – Zugehöriger Agent - `Model` – Verwendetes Modell (z.B. `claude-sonnet-4-6`, `nvidia:deepseek-ai/deepseek-r1`, `gemini:gemini-2.5-flash`) - `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`, `system` (für `auto_context`) - `Type` – `text`, `tool_use`, `tool_result`, `error`, `web_search_result`, `auto_context` - `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"/"panel"` = 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) - `ThoughtSignature` – Gemini thought_signature (für Gemini-Tool-Calls, wird im nächsten Request zurückgesendet) - `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: ``` RemoteConnectionManager → AuthService → DatabaseService → EnsureDefaultUser → ImageService, StreamingTracker, MemoryService, SkillService → EmbeddingService → VectorSearchService → ToolService → ChatService → AnthropicService (Token aus DB: api_keys) → ClaudeCliService → OpenAiService (+ LoadModelsAsync) → TelegramService → KreaService → CronjobService (BackgroundService) + EmbeddingBackgroundService (BackgroundService) + GitHubEventScannerService (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 (partial class: Core, Streaming, Agents, Cli), passive Usage-Aktualisierung nach jedem Request | | `ClaudeCliService` | Singleton | Claude Code CLI Wrapper (OAuth-basiert, kein API-Key nötig), prüft CLI-Verfügbarkeit beim Start | | `OpenAiService` | Singleton | OpenAI-kompatible Provider (Nvidia, Pollinations, Gemini, OpenAI) mit Tool-Calling, Streaming + Non-Streaming, Modell-Caching | | `ToolService` | Singleton | Tool-Definitionen bereitstellen, Tool-Aufrufe ausführen (19 Tools) | | `WorkspaceHelper` | Static | Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz | | `AppPaths` | Static | Zentrale Pfade (WwwRoot), Dev-Modus vs. Publish-Modus Erkennung | | `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 | | `SseHelper` | Static | Shared SSE-Utilities: `WriteSseEvent`, `TryWriteSse`, `FinalizeChain`, `TruncateToolResult` | | `EmbeddingService` | Singleton | Lokales Sentence-Embedding via all-MiniLM-L6-v2 (ONNX, 384 Dimensionen), WordPiece-Tokenisierung | | `VectorSearchService` | Singleton | Semantische Suche über Message-Embeddings + Memory-File-Embeddings (Cosine Similarity) | | `EmbeddingBackgroundService` | Background | Backfill bestehender Messages + Live-Embedding neuer Messages/Memory-Files | | `RemoteConnectionManager` | Singleton | WebSocket-basierte Remote-Verbindungen, Command-Execution auf verbundenen Clients | | `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 | | `KeyScannerService` | Singleton | API-Key-Scanner (GitHub Code Search, GitHub Issues/PRs, HuggingFace Hub, Google/Serper), täglicher Scan | | `GitHubEventScannerService` | Background | Echtzeit-Scanner: GitHub Public Events API (PushEvent-Diffs nach geleakten Keys), 30s Poll-Intervall | | `KeyPatterns` | Static | Zentrale Key-Pattern-Definitionen für alle Scanner (Regex, Typ-Erkennung, Display-Names) | | `KeyCheckDispatcher` | Static | Zentraler Dispatcher für Key-Checks aller Provider, normalisiertes UnifiedKeyCheckResult | | `AnthropicKeyChecker` | Static | Anthropic Key-Validierung via API-Request, Usage-Header-Auswertung (Plan, 7d/5h Usage) | | `OpenAiKeyChecker` | Static | OpenAI Key-Validierung (GET /v1/models, Rate-Limits, Organization) | | `GoogleKeyChecker` | Static | Google/Gemini Key-Validierung (Models-Listing + Quota-Check via GenerateContent) | | `StripeKeyChecker` | Static | Stripe Key-Validierung (Balance, Account-Info, Payouts/Charges) | | `ServerSettingsService` | Singleton | Server-Settings Verwaltung (In-Memory-Cache + DB), Cleanup obsoleter Settings | ### 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 payload-preview`, `GET streaming`, `POST exec`, `GET git-status` (Liste inkl. inline Git-Status + Thumbnail) | | **Agents** | `/api/agents/` | CRUD + `GET available` (inkl. public agents, 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` | | **Users** | `/api/users` | `GET` (minimales User-Verzeichnis: id/username/displayName – für jeden eingeloggten User, z. B. Session-Zuweisung) | | **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 | | **Models** | `/api/models/` | `GET nvidia`, `GET pollinations`, `GET gemini`, `GET openai`, `POST refresh` | | **API Keys** | `/api/admin/api-keys/` | CRUD + `PUT {id}/public`, `POST check`, `POST check-all`, `POST scan`, `GET scan/status`, `GET scanned`, `GET scanned/summary`, `POST scanned/recheck-all`, `POST scanned/{id}/recheck`, `POST scanned/{id}/adopt`, `DELETE scanned/{id}`, `GET event-scanner/status`, `GET event-scanner/log`, `GET scanner/logs` (Admin) + `/api/api-keys/` `GET available`, `PUT select/{keyId}`, `GET selected` (User) | | **Remote** | `/api/remote` | WebSocket-Endpoint für Remote-Clients, `GET connections`, `POST assign/unassign`, `GET /remote.ps1` | | **Internal** | `/api/internal/` | `POST remote-execute`, `POST ask-user-choice`, `POST memory-read/write/list` (Token-gesichert für MCP-Server) + `POST /api/chat/choice-respond` (normale Auth) | | **DB Explorer** | `/api/admin/db/` | `GET tables`, `GET tables/{name}` (Admin-only) | | **Info** | `/api/info` | `GET` (Build-Zeitstempel + Embedding-Verfügbarkeit) | | **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) - `/api/remote` (WebSocket-Endpoint für Remote-Clients) - `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 (sortiert nach letzter Chat-Nachricht, Fallback: `updated_at`) | | `#/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) | | `#/api-keys` | `api-keys-page.js` | API-Key-Verwaltung + GitHub-Scanner + Event-Scanner (Admin) | | `#/db-explorer` | `db-explorer-page.js` | Datenbank-Explorer (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, `addAutoContextBubble()` für Auto-Context-Box - **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 (currentSessionId, currentAgentId, getCurrentAgentId()) - **file-explorer.js** – Workspace File-Explorer Panel (Dateien, Ordner erstellen/löschen, Folder-Browser) - **typing-phrases.js** – Zufällige Typing-Indicator-Texte (Wienerisch/Österreichisch) - **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 --- ## Multi-Provider-System (OpenAI-kompatibel) Neben Anthropic Claude unterstützt Gott drei **OpenAI-kompatible Provider**. Der Agent-Model-String bestimmt den Provider: | Prefix | Provider | Base-URL | API-Key | |--------|----------|----------|---------| | (kein) | Anthropic Claude | `api.anthropic.com` | via `agent_api_keys` (pro Agent zugewiesen) | | `nvidia:` | Nvidia (g4f.space Proxy) | `g4f.space/api/nvidia/v1` | keiner | | `pollinations:` | Pollinations | `text.pollinations.ai/openai` | keiner | | `gemini:` | Google Gemini | `generativelanguage.googleapis.com/v1beta/openai` | `GeminiApiKey` | ### Model-String-Format `provider:model-id` – z.B. `nvidia:deepseek-ai/deepseek-r1`, `gemini:gemini-2.5-flash`, `pollinations:openai-fast` ### Modell-Caching Beim Server-Start werden verfügbare Modelle von den Provider-APIs geladen und in `data/models_cache.json` gecacht. Bei nachfolgenden Starts wird der Cache verwendet (kein API-Call). Manueller Refresh via `POST /api/models/refresh`. ### OpenAiService `OpenAiService` implementiert: - **Streaming via SSE** (`StreamChat()`, OpenAI-Format: `choices[0].delta`) - **Non-Streaming** (`ChatNonStreaming()`, für Telegram, Cronjobs, Sub-Agents – agentic loop mit Tool-Calling) - **Tool-Calling** (identische Tools wie AnthropicService) - **Agentic Loop** (Tool-Results → nächste Iteration) - **Gemini thought_signature** – wird aus `extra_content.google.thought_signature` gelesen, in DB persistiert (`MessageRecord.ThoughtSignature`), und beim History-Rebuild in `extra_content.google.thought_signature` zurückgesendet ### Routing `ChatService.ResolveModelId()` prüft den Model-String: - Enthält `:` mit bekanntem Provider-Prefix → `OpenAiService.StreamChat()` - Sonst → `AnthropicService.StreamChat()` --- ## 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) API-Key auflösen: `GetAgentApiKeyRecord(userId, agentId)` – Anthropic ohne Key → CLI-Fallback oder Fehler, OpenAI ohne Key → OK (keyless Provider) d) ChatService.BuildHistoryFromDb() → vollständige Message-History e) Model-Routing: Anthropic → AnthropicService.StreamChat() (oder StreamChatViaCli() bei CLI-Modus), OpenAI-Provider (nvidia:/pollinations:/gemini:/openai:) → OpenAiService.StreamChat() ↓ 4. Agentic Loop (AnthropicService oder OpenAiService): ┌─────────────────────────────────────────┐ │ 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 – Provider-Weiche: Anthropic → `AnthropicService.ChatNonStreaming()`, OpenAI → `OpenAiService.ChatNonStreaming()`. - **Cronjobs**: Non-Streaming, wie Telegram. Provider wird aus dem Agent-Modell bestimmt. - **Sub-Agents** (Preparer/Review/Finalizer): Anthropic → `RunAgentChat()` (Streaming mit SSE-Events), OpenAI → `OpenAiService.ChatNonStreaming()`. ### History-Optimierung Die History-Rekonstruktion in `BuildHistoryFromDb()` ist optimiert: - **Max 220 Messages** (`MaxHistory`-Setting, konfigurierbar): Die History wird auf maximal N 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. ### Auto-Context (Vector-Search) Wenn am Agent `UseAutoContext` aktiv ist, wird **vor** jedem Request über `VectorSearchService` ein semantisch passender Kontext zur aktuellen User-Anfrage gesucht (aus früheren Chat-Nachrichten + Memory-Files). Der Kontext wird als ``-Block in den System-Prompt injiziert. **Header-Anweisung:** Der Block hat eine bewusst direkte Anweisung an das Model: > *"WICHTIG: Der folgende Kontext wurde automatisch per Vektor-Suche aus früheren Gesprächen und Memory abgerufen. Prüfe diesen Kontext ZUERST, bevor du antwortest. Wenn er zur aktuellen Frage passt, beziehe dich aktiv darauf und nutze die Informationen in deiner Antwort. Ignoriere ihn nur, wenn er eindeutig irrelevant ist."* Die starke Formulierung verhindert, dass das Model den Kontext ignoriert (Default = Nutzung statt Ignorieren). **SSE-Event:** Der Auto-Context wird zusätzlich als `auto_context`-SSE-Event vor dem Streaming an das Frontend gesendet, damit der User sieht, was der Agent als Kontext bekommen hat (aufklappbare **Auto-Context-Box** im Chat). **DB-Persistierung:** Der Block wird auch als Message in der DB gespeichert (`Role = "system"`, `Type = "auto_context"`), damit er beim Laden der History sichtbar bleibt. `BuildHistoryFromDb` filtert `auto_context`-Messages explizit, sie werden also **nicht** an die API zurückgesendet. **Implementierung:** Sowohl `AnthropicService.Streaming.StreamChat()` als auch `AnthropicService.Cli.StreamChatViaCli()` rufen `BuildAutoContextAsync()` auf, senden das SSE-Event und persistieren die Message – damit funktioniert die Anzeige sowohl im API-Streaming-Pfad als auch im CLI-Pfad. --- ## 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, konfigurierbares Timeout) | | `background_execute` | Shell-Befehl als Hintergrund-Prozess starten (gibt PID zurück, wartet nicht auf Ende) | | `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 | | `memory_search` | Semantische Suche über vergangene Chat-Nachrichten und Memory-Dateien (benötigt Embedding-Modell) | ### Remote-Execution (wenn Remote-Verbindung zugeordnet) | Tool | Beschreibung | |--------------------|-----------------------------------------------------| | `remote_execute` | Shell-Befehl auf einem verbundenen Remote-Client ausführen (via WebSocket) | ### Cross-Session Messaging (wenn konfiguriert) | Tool | Beschreibung | |--------------------|-----------------------------------------------------| | `send_to_session` | Nachricht an eine andere Session senden und dort eine Agent-Antwort auslösen | ### 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. ### GitHub Code Search + Serper Web Search (eigene Tools) | Tool | Beschreibung | |-----------------------|-----------------------------------------------------| | `github_code_search` | Durchsucht öffentliche GitHub-Repos nach Code (GitHub Code Search API) | | `serper_web_search` | Internet-Suche via Google (Serper.dev API) | Diese Tools werden zusätzlich zur Anthropic Web-Suche bereitgestellt und direkt vom ToolService ausgeführt. ### Plan-Modus: Read-only Tools Im Plan-Modus werden nur folgende Tools bereitgestellt: `read_file`, `list_files`, `glob`, `grep`, `execute`, `todo_write`, `memory_*`, `memory_search`, `remote_execute`, `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`, `opus-4-7`, `opus-4-8`, `fable`, `fable-5`, `haiku`, `haiku-4-5` (Default für `opus` ist `claude-opus-4-8`) | | `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 | | `Effort` | Reasoning-Effort-Level für Claude. `""` (=high, Default), `low`, `medium`, `high`, `xhigh` (nur Opus 4.7+), `max` (nur Opus 4.6+/Sonnet 4.6). API: als `output_config.effort` gesendet. CLI: via Env-Var `CLAUDE_CODE_EFFORT_LEVEL`. Bei OpenAI-Providern ignoriert. | | `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-8 "opus-4-5" → claude-opus-4-5 "opus-4-6" → claude-opus-4-6 "opus-4-7" → claude-opus-4-7 "opus-4-8" → claude-opus-4-8 "fable" → claude-fable-5 "fable-5" → claude-fable-5 "haiku" → claude-haiku-4-5 "haiku-4-5" → claude-haiku-4-5 (default) → claude-sonnet-4-6 ``` **1-Million-Token-Context** ist seit März 2026 Default bei **Opus 4.6/4.7/4.8 + Sonnet 4.6** – kein separater Beta-Header und kein `[1m]`-Suffix mehr nötig. Alte DB-Einträge mit `[1m]`-Suffix werden bei Startup automatisch migriert (`UPDATE agents/chains SET model = REPLACE(model, '[1m]', '')`). --- ## 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 **Provider-aware** verarbeitet: Anthropic → `AnthropicService.ChatNonStreaming()`, OpenAI-kompatibel → `OpenAiService.ChatNonStreaming()`. 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) Provider-Weiche: Anthropic → `AnthropicService.ChatNonStreaming()`, OpenAI → `OpenAiService.ChatNonStreaming()` 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, nano-banana-2, imagen-3, imagen-4, imagen-4-fast, imagen-4-ultra, ideogram-2-turbo, ideogram-3, seedream-3, seedream-4, seedream-5-lite, seededit, z-image, qwen-2512, chatgpt-image, runway-gen-4 ``` Modelle in `MINIMAL_MODELS` (`seedream-3`, `runway-gen-4`, `seedream-5-lite`, `seededit`) unterstützen kein `width`/`height`/`steps`/`guidance` und werden nur mit Prompt aufgerufen. ### 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**. > **Multi-Modell-Panel (Fusion):** Orthogonal dazu kann eine Session zusätzlich ein **Panel** aus mehreren Agenten haben (`multi_model_mode="fusion"`, `panel_agent_ids`=CSV). Bei jeder Nachricht antworten alle Panel-Agenten **parallel** (read-only) auf die Anfrage; ihre Antworten werden dem Worker als ``-Block vorgelegt, der **eine** Gesamtantwort synthetisiert und normal streamt (OpenRouter-Fusion-/MoA-Muster). Backend: `RunPanel()` in `AnthropicService.Panel.cs` (im ChatEndpoint-try vor dem Worker-Dispatch), Member-Provider-Weiche (Anthropic read-only via `PlanMode`, CLI via `--permission-mode plan`, OpenAI tool-los via `CompleteText`), Proposals als `[Panel: Name]`-Messages mit `Tag="panel"`. SSE: `panel_start`/`panel_member_start`/`panel_member_done`/`panel_done`. Frontend: `.panel-bubble` pro Member. Mehrschichtiges MoA ist als Aufbau geplant. Siehe ARCHITECTURE_QUICK.md. ### 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()** – Provider-Weiche: Anthropic → `RunAgentChat()`, OpenAI → `OpenAiService.ChatNonStreaming()` 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) - `SubAgentContext` mit ModelId, Provider, SystemPrompt, Workspace, ToolChoice, Agent erstellen 2. **ChatEndpoint** – Übergibt `ctx.Review` an `AnthropicService.StreamChat()` 3. **AnthropicService.StreamChat()** – Nach `end_turn` des Worker-Agents: - Prüft ob `Review`-SubAgentContext 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. 20 Iterationen pro Review-Run) - 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 ### SubAgentContext (Model) ```csharp // Einheitliche Struktur für Preparer, Review und Finalizer public class SubAgentContext { public string ModelId { get; set; } // Modell-ID (z.B. "claude-sonnet-4-6" oder "deepseek-ai/deepseek-r1") public string Provider { get; set; } // "" = Anthropic, "nvidia"/"openai"/etc. = OpenAI-kompatibel public List SystemPrompt { get; set; } // Eigener System-Prompt public string AgentId { get; set; } public string Workspace { get; set; } public bool UseMemory { get; set; } public string WebSearchVersion { get; set; } public string ToolChoice { get; set; } public AgentRecord Agent { get; set; } // Agent-Record für Name/Avatar public bool AllowUserChoice { get; set; } // Preparer-spezifisch public int MaxReviewIterations { get; set; } // Review-spezifisch } // ChatContext hat: SubAgentContext Preparer + SubAgentContext Review + SubAgentContext 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 - `SubAgentContext` aufbauen (Model, Provider, SystemPrompt, Workspace, ToolChoice) 2. **AnthropicService.StreamChat()** – Nach Review-Phase (oder direkt nach Worker): - Prüft ob `Finalizer`-SubAgentContext vorhanden - Ruft `RunFinalizer()` auf 3. **RunFinalizer()** – Baut Messages auf (gesamte Konversation + Finalisierungs-Anweisung), Provider-Weiche wie bei RunPreparer/RunReview 4. **RunAgentChat()** / **ChatNonStreaming()** / **RunSubAgentViaCli()** – Provider-Weiche für Preparer/Review/Finalizer: - **Anthropic mit API-Key:** Eigener API-Call an Claude mit Agent-Config (RunAgentChat, Streaming + Tool-Loop) - **Anthropic ohne API-Key + CLI verfügbar:** `RunSubAgentViaCli()` – nutzt `ClaudeCliService.ChatStreamingAsync()`, sammelt Text, sendet identische SSE-Events (`{type}_text`, `{type}_tool_use`, `{type}_done`) und persistiert die Message mit `Tag = agentType` - **Anthropic ohne API-Key + ohne CLI:** Sub-Agent wird übersprungen (kein Hard-Error) - **OpenAI-kompatibel:** `OpenAiService.ChatNonStreaming()` (Non-Streaming agentic loop mit Tool-Calling) - 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 - **Hint**: Bei aktivem Plan-Modus wird ein `` an jede User-Nachricht angehängt (Reminder: keine Dateien ändern, keine Shell-Befehle außer read-only, keine Commits) - **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` ### Native Pfade (Windows vs. Linux) `WorkspaceHelper.GetRelativePath()` und `WorkspaceEndpoint.HandleBrowse()` liefern **native Pfade** zurück (Windows: `\`, Linux/macOS: `/`). Das Frontend (`file-explorer.js`) unterstützt beide Separatoren über Regex-Patterns wie `[\\/]+$`. Auf Windows kann von `C:\` aus zur Laufwerksübersicht (`""` = leerer Pfad) navigiert werden. ### 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 Sessions werden nach der **letzten Chat-Nachricht** sortiert (neueste zuerst), nicht nach `updated_at`. Sessions ohne Nachrichten werden per `COALESCE` nach ihrem `updated_at` einsortiert. 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 --- ## Remote-Verbindungen Remote-Clients (z.B. Windows-PCs) können sich via **WebSocket** mit dem Server verbinden. Über die Verbindung kann der AI-Agent Shell-Befehle auf dem Remote-Client ausführen. ### Architektur ``` ┌──────────────┐ WebSocket ┌───────────────┐ Tool-Call ┌───────────────┐ │ Remote-Client│◄────────────►│ Gott Server │◄───────────►│ AI-Agent │ │ (PowerShell) │ (bidirekt.) │ RemoteEndpoint│ │ remote_execute│ └──────────────┘ └───────────────┘ └───────────────┘ ``` ### Verbindungsaufbau **Windows (PowerShell):** ```powershell irm https://server/remote.ps1 | iex ``` **Linux/macOS (Bash):** ```bash curl -s https://server/remote.sh | bash ``` Beide Scripts werden über den `RemoteEndpoint` ausgeliefert, wobei der `{{WS_URL}}`-Platzhalter beim Request mit der korrekten WebSocket-URL ersetzt wird. Das Bash-Script enthält einen embedded Python WebSocket-Client (RFC 6455) und sucht automatisch `python3` → `python` → `py`. 1. Remote-Client lädt das Script und baut WebSocket-Verbindung zu `/api/remote` auf 2. Sendet `hello`-Message mit Hostname, Username, OS-Version 3. `RemoteConnectionManager` registriert die Verbindung 4. Modal-UI auf Dashboard / Chat zeigt beide Befehle (Windows + Linux/macOS) ### Session-Zuweisung - Im Chat: Remote-Button → Modal zeigt aktive Verbindungen - Admin weist Verbindung einer Session zu (`POST /api/remote/assign`) - AI-Agent bekommt `remote_execute`-Tool → führt Befehle auf dem Remote-Client aus - Ergebnis wird synchron via WebSocket zurückgeliefert (Timeout: konfigurierbarer `ExecuteTimeout`) ### OS-aware Tool-Description Das `remote_execute`-Tool (sowohl im internen ToolService als auch im MCP-Server) bekommt eine **dynamische Tool-Description** basierend auf dem OS des verbundenen Remote-Clients: - **Linux:** *"Bash-Befehl auf der verbundenen Remote-Linux-Workstation ausführen"* - **Windows:** *"PowerShell-Befehl auf der verbundenen Remote-Windows-Workstation ausführen"* Das OS wird aus der `hello`-Message des Clients (`RemoteConnection.OsVersion`) bezogen und über `ChatEndpoint` → `ChatService.PrepareContext(remoteOs)` → `ToolPayloadOptions.RemoteOs` → `ToolDefinitionOptions.RemoteOs` bis zu `ToolService.GetRemoteExecuteTool(remoteOs)` durchgereicht. Im CLI-Pfad zusätzlich an die MCP-Config (`GenerateMcpConfig(remoteOs)`) und von dort als Env-Variable `GOTT_REMOTE_OS` an den Python-MCP-Server. Damit generiert Claude direkt passende Befehle für die jeweilige Shell. Cronjobs/Telegram-Bots rufen `PrepareContext` aktuell ohne `remoteOs` auf (Default = leer → PowerShell-Description als Fallback). ### Protokoll (JSON über WebSocket) | Richtung | Type | Payload | |----------|------|---------| | Client → Server | `hello` | `hostname`, `username`, `os` | | Server → Client | `execute` | `command` | | Client → Server | `result` | `exitCode`, `output`, `error` | | Client → Server | `ping` | – | | Server → Client | `pong` | – | --- ## Semantische Suche (Embedding) Lokale Sentence-Embeddings ermöglichen semantische Suche über Chat-Nachrichten und Memory-Dateien. ### Technologie - **Modell:** all-MiniLM-L6-v2 (ONNX Runtime, 384-dimensionale Embeddings) - **Tokenisierung:** WordPiece (BERT-kompatibel, eigene Implementierung) - **Speicherung:** SQLite-Tabellen `message_embeddings` + `memory_file_embeddings` (Vektor als BLOB) - **Suche:** Brute-Force Cosine Similarity (ausreichend für <100k Embeddings) ### Komponenten | Service | Aufgabe | |---------|---------| | `EmbeddingService` | ONNX-Modell laden, Embeddings generieren (thread-safe via SemaphoreSlim) | | `VectorSearchService` | Schema-Management, Embedding-Storage, Similarity-Search | | `EmbeddingBackgroundService` | Backfill beim Start + Live-Queue für neue Messages/Memory-Files | ### Funktionsweise 1. **Backfill beim Start:** Alle bestehenden Text-Messages (user + assistant, ≥10 Zeichen) werden embedded 2. **Memory-Backfill:** Alle `.md`-Dateien in Agent-Verzeichnissen werden in 500-Zeichen-Chunks embedded 3. **Live-Embedding:** Neue Messages/Memory-Writes werden via Queue sofort embedded 4. **Suche (`memory_search` Tool):** Query wird embedded, Cosine Similarity gegen alle Embeddings berechnet, Top-N Ergebnisse zurückgegeben ### Konfiguration Das ONNX-Modell muss unter `data/models/all-MiniLM-L6-v2/` liegen (`model.onnx` + `vocab.txt`). Wenn nicht vorhanden, wird der Service deaktiviert und das Frontend zeigt eine Warnung. --- ## API-Key-Verwaltung API-Keys werden in der Datenbank verwaltet (Tabellen `api_keys` + `scanned_keys`) statt in der Konfigurationsdatei. ### Funktionsweise - **Mehrere Keys** pro Provider (z.B. `anthropic`) können gespeichert werden - **Zuweisung pro Agent:** Keys werden über `agent_api_keys` (user_id + agent_id → key_id) zugewiesen, nicht mehr global pro User - **"Kein Key"-Option:** Key-Zuweisung kann entfernt werden – Anthropic-Modelle werden blockiert, keyless Provider (Pollinations, Nvidia) funktionieren weiterhin - **Live-Aktualisierung:** Bei Änderung wird der Token sofort im laufenden Service aktualisiert (kein Neustart nötig) - **Migration:** Bestehender `AnthropicToken` aus `appsettings.Production.json` wird beim ersten Start automatisch migriert - **Migration:** `users.selected_api_key_id` wird beim Start für alle Agents des Users in `agent_api_keys` übernommen - **Source-URL:** Bei adoptierten Scan-Keys wird die GitHub-URL gespeichert ### Key-Check (nur Anthropic) `POST /api/admin/api-keys/{id}/check` sendet eine minimale Anfrage an die Anthropic API (`claude-sonnet-4-20250514`, `max_tokens=1`) mit denselben Headers wie der echte Chat-Client (`Authorization: Bearer`, `anthropic-beta`, `User-Agent`, `x-app`, `anthropic-dangerous-direct-browser-access`) und liest die Response-Headers aus: - **Status:** `valid`, `invalid`, `rate_limited`, `quota_exceeded`, `error` - **Plan-Erkennung:** `Max` (overage > 0), `Pro` (kein fallback-Header), `Pro/Max` (sonst), `API Console` (keine unified-Headers) - **Budget-Auslastung:** 5h und 7d Prozentwerte aus `unified-*` Headers ### Passive Usage-Aktualisierung Nach **jedem** API-Request an Anthropic (Streaming, Sub-Agent, NonStreaming) werden die Response-Headers automatisch ausgelesen und Plan/Usage des aktiven API-Keys in der DB aktualisiert (`AnthropicKeyChecker.ExtractMetaFromHeaders()`). Kein separater Check-Request nötig – die Daten sind immer aktuell. ### API-Key pro Agent API-Keys werden **pro Agent** zugewiesen (Tabelle `agent_api_keys`), nicht mehr global pro User: - **`PUT /api/api-keys/select/{keyId}?agentId=xxx`** – Key einem Agent zuweisen - **`PUT /api/api-keys/select/none?agentId=xxx`** – Key-Zuweisung entfernen ("Kein Key") - **`GET /api/api-keys/selected?agentId=xxx`** – Zugewiesenen Key abfragen - **Ohne Key:** Chat mit Anthropic-Modellen blockiert, OpenAI-kompatible Modelle (z.B. Pollinations) funktionieren weiterhin - **Migration:** Bestehende `users.selected_api_key_id` wird beim Start für alle Agents des Users in `agent_api_keys` kopiert ### Key-Scanner (`KeyScannerService`) Durchsucht mehrere Quellen nach geleakten API-Keys: **Quellen:** | Quelle | Quick | Web | Full | |---|---|---|---| | GitHub Code Search | ✅ (reduziert) | ❌ | ✅ | | GitHub Issues/PR Comments | ✅ (nur Body) | ❌ | ✅ (Body + Comments) | | Serper/Google | ❌ | ✅ | ✅ | | HuggingFace Hub | ❌ | ✅ | ✅ | **GitHub Code Search:** - **13 Sprachen** (Python, JavaScript, TypeScript, Shell, Markdown, JSON, YAML, TOML, Go, Rust, Ruby, Java, PHP) - **24 Pfad-Extensions** (.env, .cfg, .ini, .conf, .txt, .log, .md, .json, .yaml, .yml, .toml, .py, .js, .ts, .sh, .bash, .go, .rs, .rb, .java, .php, .config, .properties, .xml) **GitHub Issues/PR Comments:** - Durchsucht Issue-Bodies nach Key-Patterns via GitHub Search API - Im Full-Mode: Lädt zusätzlich alle Kommentare (max 100 Issues, je 100 Kommentare) - Rate-Limiting: 2.5s Delay (Search), 1s Delay (Comments), 60s Pause bei 403 **HuggingFace Hub:** - Durchsucht Spaces und Models nach API-Keys in Konfigurationsdateien - Max 50 Dateien pro Repo, max 500 KB pro Datei - Optionaler HuggingFace Token für höhere Rate-Limits **Zeitplanung:** `ScanDailyHour` (0-23 = täglicher Scan zur vollen Stunde, -1 = nur manuell) - **Ablauf:** `POST /api/admin/api-keys/scan` → Quellen durchsuchen → neue Keys in `scanned_keys` speichern → jeden Key mit `AnthropicKeyChecker` prüfen → Status + Plan + Usage aktualisieren - **Adopt:** `POST /api/admin/api-keys/scanned/{id}/adopt` → Key in `api_keys` übernehmen, Name = `user/repo`, Source-URL = vollständige GitHub-URL - **Live-Updates:** Scan-Ergebnisse werden nach jeder Iteration in der DB gespeichert (nicht erst am Ende) ### Chat Usage-Badge (Frontend) - **Overlay** oben zentriert im Chat (nur für Admins) mit visuellen Usage-Balken (Sitzung + Woche) - **Farbstufen:** Grün (<70%), Orange (70-89%), Rot (≥90%) - **Auto-Hide:** Ausgeblendet beim Hochscrollen (wenn Auto-Scroll deaktiviert) - **Auto-Refresh:** Badge aktualisiert sich bei neuen Nachrichten (via `pollHistory`) - **Key-Switch-Modal:** Klick auf Badge öffnet Modal mit allen API-Keys + "Kein Key"-Option, Zeile klickbar zum Wechseln (pro Agent) - **Manueller Check:** 🔄-Button neben Badge prüft Key und aktualisiert --- ## Key-Scanner-System Das Key-Scanner-System besteht aus drei Komponenten, die zusammenarbeiten: ### KeyPatterns (static) Zentrale Definition aller Key-Patterns (Regex + Suchbegriffe + Typ-Erkennung). Wird von `KeyScannerService` und `GitHubEventScannerService` gemeinsam genutzt. **Unterstützte Key-Typen:** Anthropic (OAuth + API), OpenAI (Project + Service + Legacy), Google/Gemini, AWS, GitHub (PAT + OAuth), GitLab, HuggingFace, Stripe (Live + Test), SendGrid, Slack, Krea AI. ### KeyCheckDispatcher (static) Zentraler Dispatcher für Key-Checks aller Provider. Normalisiert die Provider-spezifischen Ergebnisse in ein einheitliches `UnifiedKeyCheckResult` mit standardisiertem DB-Status (`valid`, `invalid`, `rate_limited`, `quota_exceeded`, `error`). **Provider-Checker:** | Provider | Checker | Prüfmethode | |----------|---------|-------------| | `anthropic` | `AnthropicKeyChecker` | Minimale API-Anfrage + Usage-Header-Auswertung | | `openai` | `OpenAiKeyChecker` | GET /v1/models + Rate-Limit-Headers | | `google` | `GoogleKeyChecker` | GET /v1/models + Quota-Check (GenerateContent) | | `stripe` | `StripeKeyChecker` | GET /v1/balance + Account-Info | ### GitHubEventScannerService (BackgroundService) Echtzeit-Scanner: Pollt die GitHub Public Events API alle 30 Sekunden, filtert PushEvents und scannt Commit-Diffs nach geleakten API-Keys. - **Konfiguration:** `GitHubEventScanner` (bool, aktiviert/deaktiviert), `GitHubToken` (für höhere Rate-Limits) - **Deduplication:** Ringbuffer für Event-IDs (max 10.000) - **Performance:** Max 30 Commit-Diffs pro Poll-Zyklus, Diffs > 100KB werden übersprungen - **Live-Log:** Internes Ringbuffer (max 50 Einträge), abrufbar über `GET /api/admin/api-keys/event-scanner/log` - **Status:** `GET /api/admin/api-keys/event-scanner/status` → isRunning, lastPoll, eventsPerHour --- ## Claude CLI Integration Die Claude Code CLI kann als alternativer Streaming-Weg verwendet werden (OAuth-basiert, kein API-Key nötig). ### Architektur - **ClaudeCliService** – Wrapper für die Claude Code CLI (`~/.claude/local/claude`). Prüft beim Start ob die CLI verfügbar ist. - **AnthropicService.Cli.cs** – `StreamChatViaCli()`: Streaming-Chat über die CLI statt der HTTP-API. Verwendet das lokale OAuth-Token aus `~/.claude/.credentials.json`. ### Ablauf 1. `ChatEndpoint` prüft ob kein API-Key vorhanden und die CLI verfügbar ist 2. Bei Verfügbarkeit wird `StreamChatViaCli()` statt `StreamChat()` aufgerufen 3. Die CLI wird als Kindprozess gestartet mit JSON-Streaming-Output 4. SSE-Events werden identisch zum HTTP-API-Streaming generiert ### Voraussetzungen - Claude Code CLI installiert (`~/.claude/local/claude`) - Gültiges OAuth-Token in `~/.claude/.credentials.json` ### CLI-Fallback für Sub-Agents Wenn ein **Sub-Agent** (Preparer, Review, Finalizer) keinem API-Key zugewiesen ist, wird – sofern die CLI verfügbar ist – automatisch `RunSubAgentViaCli()` (in `AnthropicService.Agents.cs`) aufgerufen. Damit funktionieren optionale Sub-Agents auch ohne explizit zugewiesenen Anthropic-Key. Erst wenn weder Key noch CLI vorhanden sind, wird der Sub-Agent übersprungen statt mit einem Hard-Error abzubrechen. ### Python-Detection für MCP-Config Die MCP-Config (`AnthropicService.Cli.GenerateMcpConfig()`) sucht beim Start dynamisch nach einer Python-Installation in der Reihenfolge `python3` → `python` → `py` (das Ergebnis wird gecached). Auf Linux ist üblicherweise `python3` da, auf Windows oft nur `python` oder `py`. Wird kein Python gefunden, wird im Chat ein SSE-`warning`-Event gesendet (*"Python wurde nicht gefunden... MCP-Tools sind nicht verfügbar."*) – die normalen Tools funktionieren weiterhin. --- ## Datenbank-Explorer Admin-only-Feature zum direkten Inspizieren der SQLite-Datenbank über die Web-UI. ### Backend `DatabaseExplorerEndpoint.cs` bietet zwei Endpoints: - `GET /api/admin/db/tables` – Listet alle Tabellen mit Spalten- und Zeilenanzahl - `GET /api/admin/db/tables/{name}` – Liest Tabellendaten (mit Paging: `?limit=100&offset=0`, Sortierung: `?sort=column&order=desc`) ### Frontend `db-explorer-page.js` (`#/db-explorer`) – Admin-only Seite mit klickbarer Tabellenliste und Daten-Grid. Erreichbar über das Dashboard (Button "Datenbank"). --- ## MCP-Server-Integration (Internal API) `InternalEndpoint.cs` stellt eine interne API bereit, die von externen MCP-Servern genutzt werden kann. ### Authentifizierung - Beim Start wird ein einmaliger `InternalToken` generiert (GUID) - Alle `/api/internal/*`-Endpoints erfordern `X-Internal-Token`-Header - Ausnahme: `POST /api/chat/choice-respond` nutzt normale Cookie-Auth ### Endpoints | Endpoint | Funktion | |----------|----------| | `POST /api/internal/remote-execute` | Shell-Befehl auf Remote-Client ausführen (via WebSocket) | | `POST /api/internal/ask-user-choice` | Short-Poll für User-Choice-Antworten (202 = pending, 200 = answered) | | `POST /api/chat/choice-respond` | Frontend: User-Antwort auf Choice-Frage speichern | | `POST /api/internal/memory-read` | Memory-Datei eines Agents lesen | | `POST /api/internal/memory-write` | Memory-Datei schreiben/appenden | | `POST /api/internal/memory-list` | Memory-Dateien eines Agents auflisten | ### User-Choice-Flow (CLI-Modus) 1. MCP-Server ruft `ask-user-choice` auf → gibt Frage + Optionen an Frontend 2. Frontend zeigt Choice-Buttons 3. User klickt → `choice-respond` speichert Antwort 4. MCP-Server pollt `ask-user-choice` → erhält Antwort --- ## 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) } ``` > **Hinweis:** `AnthropicToken` wird nicht mehr über `appsettings.json` konfiguriert, sondern über die **API-Key-Verwaltung** im Admin-Bereich (DB-Tabelle `api_keys`). Bestehende Tokens werden beim ersten Start automatisch aus `appsettings.Production.json` migriert. ### 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. **Token-Migration** – Bestehender `AnthropicToken` aus `appsettings.Production.json` wird automatisch in `api_keys`-Tabelle migriert 5. **Bild-Migration** – Konvertierung bestehender PNG/JPEG-Avatare zu WebP + Generierung von Thumbnails (`avatar_small`, `image_small`) 6. **API-Key-Migration** – `users.selected_api_key_id` → `agent_api_keys` (pro Agent statt global pro User) 7. **Settings-Cleanup** – Obsolete Server-Settings (z.B. `ScanIntervalHours`) werden aus der DB entfernt 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