- agents-Tabelle: use_auto_context, use_github_search, use_serper_search ergänzt - api_keys-Tabelle: is_public, refresh_token, access_token_expires_at, oauth_provider ergänzt - scanned_keys-Tabelle: source, type Spalten ergänzt - scanner_logs-Tabelle: nicht-existierende details-Spalte entfernt - models_cache.json: OpenAI als vierten Provider ergänzt - Internal-Endpunkt: POST /api/chat/choice-respond dokumentiert
112 KiB
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
- Überblick
- Tech-Stack
- Projektstruktur
- Datenmodell
- Backend-Architektur
- Frontend-Architektur
- Multi-Provider-System (OpenAI-kompatibel)
- Chat-Pipeline
- Tool-System
- Agent-System
- System-Prompt-Aufbau
- Memory-System
- Skill-System
- Telegram-Integration
- Cronjob-System
- Krea-Bildgenerierung
- Auth & Multi-User
- Admin-Benutzerverwaltung
- Multi-Agent System (Preparer + Review + Finalizer)
- Interaktive Rückfragen (ask_user_choice)
- Plan-Modus
- Web-Search
- Nachrichten teilen (Shared Messages)
- Workspace & File-Explorer
- Avatar-System
- LaTeX/Math-Rendering
- Windows Desktop App
- OpenClaw Import
- Remote-Verbindungen
- Semantische Suche (Embedding)
- API-Key-Verwaltung
- Key-Scanner-System
- Claude CLI Integration
- Datenbank-Explorer
- MCP-Server-Integration (Internal API)
- Deployment
- Konfiguration
- 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) |
chains |
Request-Chains (model, tokens, iterations, timing, tool_choice, tool_names, stream_error_info, plan_mode, archive_id) |
messages |
Einzelne Nachrichten (text, tool_use, tool_result, error, web_search_result), Tag für Sub-Agent-Markierung oder Import-Herkunft (openclaw-import), SortKey für korrekte Reihenfolge, 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 IDSessionId– Zugehörige SessionAgentId– Zugehöriger AgentModel– 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-VerbrauchIterations– Anzahl der Tool-Loop-DurchläufeStopReason–end_turn,tool_use,max_iterations,error,client_disconnected,awaiting_user_choice,preparer_queryToolChoice– Verwendete Tool-Choice-Strategie (automatisch:anywenn Workspace/Skills/Memory, sonstauto)ToolNames– Komma-separierte Liste der verwendeten ToolsArchiveId– Zugehöriges Archiv (wenn archiviert)StreamErrorInfo– Fehlerdetails bei Stream-AbbrüchenPlanMode– Ob die Chain im Plan-Modus erstellt wurde (informativ)DurationMs– Gesamtdauer der Chain
MessageRecord – Eine einzelne Nachricht innerhalb einer Chain:
Role–user,assistantType–text,tool_use,tool_result,error,web_search_resultContent– Textinhalt oder JSON (bei tool_use: Input-JSON)ToolName,ToolCallId– Bei Tool-AufrufenIsError– Ob die Nachricht ein Fehler istIteration– Welche Tool-Loop-IterationAttachmentCount– Anzahl angehängter DateienAttachmentNames– JSON-Array mit DateinamenTag– Markierung (null= normal,"preparer"/"review"/"finalizer"= Sub-Agent-Message (wird aus API-History gefiltert),"openclaw-import"= importierte Nachricht (wird immer in History einbezogen))IsMarked– Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)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) odernullwenn kein Workspace (wird im Endpoint hinzugefügt, nicht Teil des Records)Imagewird durchImageSmallersetzt (Thumbnail für Dashboard-Performance)
GitStatusSummary – Kompakte Git-Status-Info:
Add,Edit,Delete– Anzahl geänderter Dateien nach TypOutgoing– Anzahl ausgehender (nicht gepushter) Commits
Backend-Architektur
Program.cs – Startup
Die Program.cs ist der zentrale Einstiegspunkt und konfiguriert:
- Service-Registrierung – Alle Services als Singletons im DI-Container
- Datenbank-Initialisierung –
DatabaseServiceerstellt Schema + Migrationen - Default-User – Wird automatisch angelegt falls keine User existieren
- Middleware – Static Files →
AuthMiddleware→ Endpoints - Endpoint-Mapping – Alle 17 Endpoint-Gruppen registriert
- 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 |
| 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/loginGET /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 einenX-Api-Key-Header. Wenn der Key mitKreaApiKeyaus 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
- 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.cardDOM-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.cardDOM-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.cardDOM-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 Variablesbase.css– Reset und Grundlayoutmain.css– Sidebar, Content-Area, Responsive Layoutchat.css– Chat-Bubbles, Streaming-Animation, Tool-Outputsmarkdown.css– Rendering von Markdown in Nachrichtenmobile.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_signaturegelesen, in DB persistiert (MessageRecord.ThoughtSignature), und beim History-Rebuild inextra_content.google.thought_signaturezurü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 StreamingTrackerhält pro Session einenCancellationTokenSourceStreamingTracker.Cancel(sessionId)cancelt den Token sofort- Der Chat-Handler verknüpft den Stop-Token mit dem Request-Token via
CreateLinkedTokenSource
2. CancellationToken-Propagierung:
- Frontend:
AbortController.abort()→ SSE-Fetch abgebrochen +POST /api/chat/stop StreamingTracker.Cancel()→ CancellationTokenSource wird gecancelledAnthropicService:ThrowIfCancellationRequested()am Loop-Anfang, vor/nach Tool-ExecutionToolService.ExecuteTool(): CancellationToken wird durchgereicht → beiexecutewird der Prozess gekilltWriteSseEvent(): Alle SSE-Writes mit CancellationToken → sofortiger Abbruch- 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 120 Messages: Die History wird auf maximal 120 Messages begrenzt (älteste entfernt)
- Ältere Chains: Nur
text-Messages (user + assistant), keine Tool-Calls - Letzte Chain: Vollständig mit allen Tool-Use/Tool-Result Blöcken
- Attachments: Nur für die letzte User-Message geladen (spart enorme Token)
- Tool-Results: In der History auf 20.000 Zeichen gekürzt (vollständig in DB)
- System-Prompt-Reminder: Alle ~30 Messages wird ein automatisch generierter Reminder als User/Assistant-Paar injiziert (z.B.
[System Reminder] Beachte weiterhin den System-Prompt (Identity, Workspace, Kontext, Memory, ...)). Bekämpft das "Lost-in-the-Middle"-Problem bei langen Konversationen. Der Reminder-Text wird aus den aktiven Prompt-Block-Labels generiert (SystemPromptBuilder.BuildReminder()). Nur aktiv wenn ≥2 Blöcke vorhanden.
Tool-System
Der Agent hat Zugriff auf folgende Tools, abhängig von Konfiguration:
Datei-Tools (wenn Workspace konfiguriert)
| Tool | Beschreibung |
|---|---|
read_file |
Datei lesen (relativer Pfad zum Workspace) |
write_file |
Datei erstellen/überschreiben |
edit_file |
Exakten Textabschnitt in Datei ersetzen |
list_files |
Verzeichnis-Listing |
delete_file |
Datei oder leeres Verzeichnis löschen |
execute |
Shell-Befehl ausführen (bash, 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.
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, danachauto. Verhindert dass der Agent nur Text antwortet wenn Tools benötigt werden.auto: Wenn keine Tools verfügbar. Claude entscheidet frei.none: Im/no-tools-Modus.
Pfad-Sicherheit
Alle Dateipfade werden via ResolvePath() aufgelöst und gegen den Workspace/Memory-Ordner geprüft – Path-Traversal-Angriffe werden verhindert.
Agent-System
Ein Agent definiert die Persönlichkeit und Fähigkeiten der AI:
| Eigenschaft | Beschreibung |
|---|---|
Name |
Anzeigename |
Avatar |
Base64 Data-URL (WebP, 512px) + AvatarSmall (WebP, 256px Thumbnail) |
Model |
sonnet, sonnet-4-5, sonnet-4-6, opus, opus-4-5, opus-4-6, haiku, haiku-4-5 |
CustomCommands |
JSON-Array mit Slash-Commands (Label + Prompt) |
AutoCommit |
"" (nichts), "mention" (anmerken – nur auf User-Request), "enforce" (erzwingen – nach jeder Änderung), "forbid" (strikt verboten) |
UseSoul |
SOUL.md in System-Prompt einbinden |
UseIdentity |
IDENTITY.md in System-Prompt einbinden |
UseUserInfo |
User-Persona in System-Prompt einbinden |
UseMemory |
Memory-Tools aktivieren |
WebSearchVersion |
Web-Suche aktivieren (z.B. web_search_20260209) |
AllowUserChoice |
ask_user_choice-Tool verfügbar (Default: true) |
TaskInstruction |
Aufgabe/Anweisung – optionaler Text, wird als ## Anweisungen-Block in System-Prompt eingefügt |
IsPublic |
Andere User können den Agent in ihren Sessions verwenden (aber nicht bearbeiten) |
Model-Auflösung
"sonnet" → claude-sonnet-4-6
"sonnet-4-5" → claude-sonnet-4-5
"sonnet-4-6" → claude-sonnet-4-6
"opus" → claude-opus-4-6
"opus-4-5" → claude-opus-4-5
"opus-4-6" → claude-opus-4-6
"haiku" → claude-haiku-4-5
"haiku-4-5" → claude-haiku-4-5
(default) → claude-sonnet-4-6
System-Prompt-Aufbau
Der SystemPromptBuilder erzeugt den System-Prompt modular aus konfigurierbaren Blöcken:
┌──────────────────────────────────────────┐
│ 1. IDENTITY.md (UseIdentity) │ → Wer ist der Agent?
│ 2. SOUL.md (UseSoul) │ → Persönlichkeit, Verhalten
│ 3. Anweisungen (TaskInstruction) │ → Aufgaben-/Anweisungstext (optional)
│ 4. User-Persona (UseUserInfo) │ → Infos über den User
│ 5. Workspace-Info │ → Tools + Workspace-Pfad
│ 6. Rückfragen (AllowUserChoice) │ → Hinweis zu ask_user_choice Tool
│ 7. Session-Kontext │ → Freiformtext
│ 8. Skills (Agent-Skills) │ → Skill-Prompts
│ 9. Memory (UseMemory) │ → Memory-Regeln + MEMORY.md Inhalt
│10. Git-Verhalten (AutoCommit) │ → Commit-Regeln
│11. Markierte Nachrichten │ → Vom User als wichtig markierte Messages
│ [Plan-Modus-Block] │ → Nur wenn planMode=true (überspringt 10)
│ [Preparer-Instruktionsblock] │ → Nur für Preparer-Agents
└──────────────────────────────────────────┘
Jeder Block hat:
- Icon – Font Awesome Icon-Klasse
- Label – Beschreibender Titel
- Content – Markdown-Text
Die Blöcke sind über die API als JSON abrufbar (GET /api/sessions/{id}/system-prompt-blocks), sodass das Frontend sie einzeln inspizierbar darstellen kann.
Memory-System
Jeder Agent hat ein eigenes Dateisystem-basiertes Gedächtnis:
data/agents/{agentId}/
├── IDENTITY.md # Identität (aus Template initialisiert)
├── SOUL.md # Persönlichkeit (aus Template initialisiert)
├── MEMORY.md # Haupt-Memory (kuratierte Informationen)
└── memory/ # Unterordner für Tages-Logs, Themen
├── YYYY-MM-DD.md
└── ...
Funktionsweise
- Initialisierung: Beim Erstellen eines Agents werden
IDENTITY.mdundSOUL.mdausTemplates/kopiert - Lesen: Der Agent kann seine Memory-Dateien via
memory_readTool lesen - Schreiben: Via
memory_write(überschreiben oder append) - Auflisten: Via
memory_listalle Dateien durchsuchen - System-Prompt:
MEMORY.mdInhalt wird automatisch in den System-Prompt eingebettet - 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
---
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– UUIDUserId– OwnerName– AnzeigenameBotToken– Bot-Token von @BotFatherWebhookSecret– Einmalig generierter Token für Webhook-VerifizierungIsPublic– Andere User können den Bot in ihren Sessions verwenden (aber nicht bearbeiten)
Session-Felder:
TelegramBotId– Verweis auf den zugewiesenen Bot (wieAgentId)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:
- Suche Sessions mit diesem Bot, die noch keine Chat-ID haben
- Genau 1 gefunden → Automatisch verknüpfen + Bestätigungsnachricht
- 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, imagen-3, imagen-4, imagen-4-fast, imagen-4-ultra,
ideogram-2-turbo, ideogram-3, seedream-3, seedream-4,
chatgpt-image, runway-gen-4
Flow
1. POST /api/images/generate { prompt, model, width, height, ... }
2. KreaService sendet Request an Krea API
3. Job-Polling bis Status "completed"
4. Bilder herunterladen nach data/images/
5. Metadaten als JSON speichern
6. Response mit Dateinamen + URLs
Bild-Serving
GET /api/images– Liste aller generierten Bilder (aus Metadaten-JSON)GET /api/images/file/{filename}– Einzelnes Bild ausliefern (ohne Auth)GET /api/images/file/{filename}/thumb– Thumbnail (320px WebP, gecacht indata/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_tokenHttpOnly 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_idgebunden - Agents sind an
user_idgebunden - Cronjobs sind an
user_idgebunden - 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.
myappstatt/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.Workspacefunktionieren 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:
POST /api/admin/users/{id}/impersonate→ Erstellt Token für Zieluser- Setzt neuen
gott_token-Cookie → Admin-Session wird zum Zieluser 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:
POST /api/admin/users/{id}/login-link→ Erstellt Token, gibt{ token }zurück- Frontend baut URL:
{window.location.origin}/api/auth/autologin?token={token} - Link wird im Modal angezeigt mit Copy-Button
GET /api/auth/autologin?token={token}→ Setzt Cookie + Redirect zu/(ohne Auth, wie Login)
Frontend (admin-page.js)
- Erreichbar über Dashboard-Link "Benutzerverwaltung" (nur für Admins sichtbar)
- User-Liste als Cards mit Edit/Delete/Login-Link-Buttons
- Inline-Formular für Create/Edit
- Login-Link-Modal mit Copy-Funktion
- Sich selbst löschen ist nicht möglich
Multi-Agent System (Preparer + Review + Finalizer)
Das Multi-Agent-System ermöglicht bis zu vier Agents pro Session: einen Worker, einen optionalen Vorbereiter (Preparer), einen optionalen Reviewer und einen optionalen Finalizer.
Architektur
User-Message
→ Preparer-Phase (optional):
→ Preparer-Agent bekommt: User-Message + History (inkl. eigener vorheriger Messages)
→ Preparer arbeitet (kann Tools nutzen: read_file, execute etc.)
→ Erledigt Vorarbeit mit seinen Fähigkeiten (z.B. JSON bauen, Daten aufbereiten)
→ [QUESTION] → Rückfrage an User, Flow stoppt, User muss erneut eingeben
→ Antwort ohne [QUESTION] → automatisch weiterleiten an Worker
→ Worker-Agent arbeitet (Tools, Iterationen) → end_turn
→ Review-Phase (optional):
→ Review-Agent bekommt: Konversation als Kontext
→ Review-Agent prüft (kann Tools nutzen: read_file, execute etc.)
→ [APPROVED] → Fertig, Antwort an User
→ Feedback-Text → wird als User-Message an Worker-Agent eingefügt
→ Worker-Agent korrigiert → end_turn → Review-Agent prüft erneut
→ Max Review-Runden erreicht → Fertig (mit letzter Worker-Antwort)
→ Finalizer-Phase (optional):
→ Finalizer-Agent bekommt: gesamte Konversation
→ Finalizer arbeitet (eigene Tools, z.B. git commit, deploy)
→ Kein Feedback-Loop – einmalige Ausführung
→ Fertig
Konfiguration
Auf Session-Ebene (nicht Agent-Ebene):
| DB-Feld | SessionRecord | Beschreibung |
|---|---|---|
preparer_agent_id |
PreparerAgentId |
ID des Vorbereiter-Agents (optional, null = kein Preparer) |
review_agent_id |
ReviewAgentId |
ID des Review-Agents (optional, null = kein Review) |
max_review_iterations |
MaxReviewIterations |
Max. Review-Runden (Default: 3) |
finalizer_agent_id |
FinalizerAgentId |
ID des Finalizer-Agents (optional, null = kein Finalizer) |
Vorbereiter-Agent (Preparer)
Der Preparer wird vor dem Worker-Agent aufgerufen. Er analysiert die User-Anfrage, kann Vorarbeit leisten (z.B. Daten aufbereiten, JSON bauen, Code analysieren) und gibt das Ergebnis an den Worker weiter. Bei unklaren Anfragen stellt er Rückfragen.
Logik-Pattern: Default = Forward
Die Logik ist invertiert gegenüber dem Review-Pattern: Jede Antwort wird automatisch weitergeleitet, außer sie enthält explizit [QUESTION]. Das ist robuster als ein Pflicht-Tag wie [FORWARD], weil LLMs dazu neigen, Tags zu vergessen.
| Antwort | Ergebnis |
|---|---|
Ohne [QUESTION] |
Automatisch weitergeleitet (ShouldForward = true) |
Mit [QUESTION] |
Rückfrage an User, Flow stoppt (ShouldForward = false) |
Backend-Flow (Preparer)
- ChatEndpoint – Prüft ob
ctx.Preparervorhanden - AnthropicService.RunPreparer() – Provider-Weiche: Anthropic →
RunAgentChat(), OpenAI →OpenAiService.ChatNonStreaming()mitagentType = "preparer" - Entscheidung –
ShouldForwardprü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_donemitforwarded: false, Stream endet mitstopReason: "preparer_query", User muss erneut eingeben
- Forward: Aufbereiteter Text wird als
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)
-
ChatService.PrepareContext() – Wenn
session.ReviewAgentIdgesetzt:- Review-Agent aus DB laden
- Eigenen SystemPrompt für den Review-Agent bauen (Identity, Soul, Memory, Skills)
SubAgentContextmit ModelId, Provider, SystemPrompt, Workspace, ToolChoice, Agent erstellen
-
ChatEndpoint – Übergibt
ctx.ReviewanAnthropicService.StreamChat() -
AnthropicService.StreamChat() – Nach
end_turndes Worker-Agents:- Prüft ob
Review-SubAgentContext vorhanden undreviewRound < MaxReviewIterations - Ruft
RunReview()auf
- Prüft ob
-
RunReview() – Orchestriert eine einzelne Review-Runde:
- Baut Review-Messages auf: Konversation als Context + Prüfanweisung
- SSE-Event
review_startan Client senden - Ruft
RunAgentChat()auf (generische Methode für Review/Finalizer, eigener API-Call an Claude) - Review-Agent kann Tools nutzen (eigener Tool-Loop mit max. 25 Iterationen)
- SSE-Events
review_textundreview_tool_usefür Live-Streaming - Prüft ob Antwort
[APPROVED]enthält:- Ja → SSE
review_approved, returnHasFeedback = false - Nein → SSE
review_feedback, returnHasFeedback = true+ Feedback-Text
- Ja → SSE
-
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 erneutreviewRound++bisMaxReviewIterationserreicht
- Feedback als User-Message in DB speichern (mit
SubAgentContext (Model)
// 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<object> 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):
renderMarkdownparst[Review: AgentName]→ styled Header mit IconrenderMarkdownparst[Review-Feedback von AgentName (...)]→ styled Feedback-HeaderrenderMarkdownfiltert[APPROVED]aus (wird ausgeblendet)- History erkennt Review-Nachrichten und rendert sie als
.review-bubblestatt 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
-
ChatService.PrepareContext() – Wenn
session.FinalizerAgentIdgesetzt:- Finalizer-Agent aus DB laden
SubAgentContextaufbauen (Model, Provider, SystemPrompt, Workspace, ToolChoice)
-
AnthropicService.StreamChat() – Nach Review-Phase (oder direkt nach Worker):
- Prüft ob
Finalizer-SubAgentContext vorhanden - Ruft
RunFinalizer()auf
- Prüft ob
-
RunFinalizer() – Baut Messages auf (gesamte Konversation + Finalisierungs-Anweisung), Provider-Weiche wie bei RunPreparer/RunReview
-
RunAgentChat() / ChatNonStreaming() – Provider-Weiche für Preparer/Review/Finalizer:
- Anthropic: Eigener API-Call an Claude mit Agent-Config (RunAgentChat, Streaming + Tool-Loop)
- 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 mitfa-filterIcon,[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
{
"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, Defaulttrue. Steuert ob das Tool verfügbar ist.- Dashboard-Badge:
🔵 Rückfragenwenn aktiv - System-Prompt-Hinweis: "Nutze
ask_user_choicesparsam – nur wenn die Entscheidung vom User getroffen werden soll" - Bei Review/Finalizer/Cronjobs: Immer deaktiviert (kein User zum Fragen)
Backend-Flow
- Claude ruft
ask_user_choiceauf → Backend erkennt imProcessStream - Tool wird nicht ausgeführt – stattdessen SSE-Event
user_choicegesendet stopReasonwird auf"awaiting_user_choice"gesetzt (Guard-Clause verhindert Überschreibung durchmessage_delta)tool_use-Block wird in DB gespeichert, Chain wird finalisiert- User wählt Option → Frontend sendet
POST /api/chatmitchoiceToolCallId+choiceResult - Backend findet die Chain mit dem offenen
tool_use, speicherttool_resultdort - Neuer
StreamChatstartet – History enthälttool_use+tool_result, Claude arbeitet weiter
Frontend-Flow
- Live: SSE-Event
user_choice→addChoiceBubble()rendert Frage + klickbare Buttons - History:
tool_usemittoolName === 'ask_user_choice'→ Voraus-Suche nachtool_result:- Gefunden:
addChoiceBubbleStatic()mit ausgewählter Option (hervorgehoben, andere ausgegraut) - Nicht gefunden: Aktive Buttons (klickbar, z.B. nach Page-Refresh)
- Gefunden:
- Robustheit: Guards gegen
choicesals 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: trueimChatRequest - Pro Chain:
plan_modeFeld 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
<hint>an jede User-Nachricht angehängt (Reminder: keine Dateien ändern, keine Shell-Befehle außer read-only, keine Commits) - Chain-Meta: Badge
📋 Plan-Modusbei 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 werdenurl+titleals 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 finalisierentext_start→ in bestehende Bubble weiter schreiben (kein neuer Block)pauseStreamBubble()/endStreamBubble()Konzept: Text-Blöcke zwischen Web-Searches werden in einer Bubble zusammengefassttext_endpausiert 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
SharedEndpointgibtweb_search_result-Messages mit aus (Typ-Filter:text+web_search_result)shared-page.jsrendertweb_search_resultals Tool-Box mit Source-Links- Merge-Funktion überspringt
web_search_resultbeim 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
- Share-Button (🔗) erscheint an jeder Nachricht im Chat (neben Reply, Delete, Mark)
- Klick öffnet Kontext-Menü: "Einzelne Nachricht" oder "Thread teilen"
POST /api/messages/{id}/share→ Token zurück → URL in Zwischenablage kopiert- 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) odercontext(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 statusInformationen - Erlaubt das Ausführen von Shell-Befehlen (foreground + background)
WorkspaceHelper
Services/WorkspaceHelper.cs – Statische Hilfsklasse für Workspace-Pfade:
| Methode | Beschreibung |
|---|---|
ResolveWorkspace(session, user) |
Relativer Workspace + DefaultPath → absoluter Pfad |
ValidateRelativeWorkspace(workspace) |
Prüft auf .., ~, absolute Pfade → null bei ungültig |
ToRelativeWorkspace(absolute, defaultPath) |
Absoluter Pfad → relativ zum DefaultPath |
File-Explorer API
| Endpoint | Beschreibung |
|---|---|
GET /api/workspace/tree |
Verzeichnisbaum (mit Ignore-Filtern) |
GET /api/workspace/file |
Dateiinhalt lesen (max 2MB) |
PUT /api/workspace/file |
Datei speichern |
POST /api/workspace/mkdir |
Ordner erstellen im Workspace |
DELETE /api/workspace/dir |
Leeren Ordner löschen im Workspace |
GET /api/workspace/browse |
Verzeichnis-Browser (für Pfad-Auswahl) |
POST /api/workspace/browse/mkdir |
Ordner erstellen per absolutem Pfad |
GET /api/workspace/raw |
Raw-Datei ausliefern (Bilder, PDFs) |
GET /api/workspace/agent/tree |
Agent-Memory-Verzeichnis |
GET/PUT /api/workspace/agent/file |
Agent-Memory-Dateien lesen/schreiben |
POST /api/workspace/agent/mkdir |
Ordner erstellen im Agent-Memory |
Ignored Directories
bin, obj, node_modules, .git, .vs, .idea, publish, dist, out, build, target, .cache
Session-Exec
POST /api/sessions/{id}/exec führt Shell-Befehle im Workspace aus:
- foreground: Synchron, wartet auf Ergebnis (30s Timeout)
- background: Fire-and-forget via
systemd-run
Avatar-System
Sessions und Agents können ein Bild/Avatar haben mit konfigurierbarem Zoom.
Datenfelder
| Feld | Tabellen | Beschreibung |
|---|---|---|
image / avatar |
sessions / agents |
Base64-kodiertes Bild (WebP, max 512px via ImageService) |
image_small / avatar_small |
sessions / agents |
Thumbnail (WebP, 256px, Q70) für Listen/Dashboard |
avatar_scale |
sessions, agents |
Zoom-Faktor (0.5× – 3×, Default 1.0) |
avatar_offset_x |
sessions, agents |
Horizontale Position (-50% – +50%, Default 0), via CSS translate() |
avatar_offset_y |
sessions, agents |
Vertikale Position (-50% – +50%, Default 0), via CSS translate() |
Darstellung via CSS transform: scale(S) translate(X%, Y%) im .card-image Container (overflow: hidden).
Bild-Verarbeitung (ImageService)
Alle Bilder werden beim Upload in WebP konvertiert (statt PNG), was ~60-80% Speicherersparnis bringt:
| Variante | Methode | Max. Größe | Qualität | Verwendung |
|---|---|---|---|---|
| Voll | ResizeDataUrl() |
512px | Q80 | Editor, Detail-Ansicht (GET /api/agents/{id}, GET /api/sessions/{id}) |
| Thumbnail | ResizeDataUrlSmall() |
256px | Q70 | Listen, Dashboard (GET /api/agents, GET /api/sessions) |
Die Listen-Endpoints liefern das Thumbnail als avatar/image-Feld (Fallback auf Voll-Bild wenn kein Thumbnail existiert). Die Detail-Endpoints liefern das volle Bild.
Avatar-Editor (avatar-editor.js)
Wiederverwendbares Modal-Modul, genutzt in Session- und Agent-Pages:
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
KreaTokenkonfiguriert) - 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 <code class="katex-error">
Abhängigkeiten
wwwroot/js/lib/katex.min.js– KaTeX Engine (v0.16.21)wwwroot/css/katex.min.css– Styleswwwroot/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/<OS>/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
- Liest alle
message-Entries aus der OpenClaw-DB, gruppiert nachchainId - Nur Text-Nachrichten werden importiert – Tool-Use/Tool-Result werden übersprungen
- User-Message-Bereinigung: Webchat-Prefixe (
[Direct 1:1 webchat ...],[Current message ...],[cron:...]),Conversation info-Blöcke werden entfernt - Pro Chain wird ein
ChainRecord+MessageRecordserstellt, alle mittag = 'openclaw-import' - Timestamps werden aus OpenClaw übernommen (
created_at) - 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:
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
- Remote-Client führt
irm https://server/remote | iexaus → lädtremote.ps1 - Script baut WebSocket-Verbindung zu
/api/remoteauf - Sendet
hello-Message mit Hostname, Username, OS-Version RemoteConnectionManagerregistriert die Verbindung
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)
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
- Backfill beim Start: Alle bestehenden Text-Messages (user + assistant, ≥10 Zeichen) werden embedded
- Memory-Backfill: Alle
.md-Dateien in Agent-Verzeichnissen werden in 500-Zeichen-Chunks embedded - Live-Embedding: Neue Messages/Memory-Writes werden via Queue sofort embedded
- Suche (
memory_searchTool): 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
AnthropicTokenausappsettings.Production.jsonwird beim ersten Start automatisch migriert - Migration:
users.selected_api_key_idwird beim Start für alle Agents des Users inagent_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 zuweisenPUT /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_idwird beim Start für alle Agents des Users inagent_api_keyskopiert
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 inscanned_keysspeichern → jeden Key mitAnthropicKeyCheckerprüfen → Status + Plan + Usage aktualisieren - Adopt:
POST /api/admin/api-keys/scanned/{id}/adopt→ Key inapi_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
ChatEndpointprüft ob kein API-Key vorhanden und die CLI verfügbar ist- Bei Verfügbarkeit wird
StreamChatViaCli()stattStreamChat()aufgerufen - Die CLI wird als Kindprozess gestartet mit JSON-Streaming-Output
- 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
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 ZeilenanzahlGET /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
InternalTokengeneriert (GUID) - Alle
/api/internal/*-Endpoints erfordernX-Internal-Token-Header - Ausnahme:
POST /api/chat/choice-respondnutzt 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)
- MCP-Server ruft
ask-user-choiceauf → gibt Frage + Optionen an Frontend - Frontend zeigt Choice-Buttons
- User klickt →
choice-respondspeichert Antwort - 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.dbdarf 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:
- Frontend-Anpassungen vornehmen (in
wwwroot/). - Manuelles Deployment auf dem Server ausführen:
# 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
{
"Port": 19000 // Server-Port (Default 19000, erfordert Neustart)
}
Hinweis:
AnthropicTokenwird nicht mehr überappsettings.jsonkonfiguriert, sondern über die API-Key-Verwaltung im Admin-Bereich (DB-Tabelleapi_keys). Bestehende Tokens werden beim ersten Start automatisch ausappsettings.Production.jsonmigriert.
appsettings.Production.json
Überschreibt Werte aus appsettings.json im Production-Environment. Wird nicht beim Publish mitkopiert (liegt nur im Build/<OS>/publish/-Verzeichnis). Verwaltung über die Admin-UI (Servereinstellungen, #/server-settings).
Environment
ASPNETCORE_ENVIRONMENT=Production→ lädtappsettings.Production.json- Tokens und Secrets werden über die Server-Settings-UI in
appsettings.Production.jsongeschrieben
Datenbank-Migrationen
Die DatabaseService.InitializeDatabase() handhabt Schema-Erstellung und Migrationen in einem einzigen Methoden-Aufruf:
- Tabellen-Erstellung via
CREATE TABLE IF NOT EXISTS - Spalten-Migration via
TryAddColumn()– fängt "column already exists" ab - Daten-Migration (z.B.
auto_commitvon INTEGER zu TEXT,use_web_searchzuweb_search_version) - Token-Migration – Bestehender
AnthropicTokenausappsettings.Production.jsonwird automatisch inapi_keys-Tabelle migriert - Bild-Migration – Konvertierung bestehender PNG/JPEG-Avatare zu WebP + Generierung von Thumbnails (
avatar_small,image_small) - API-Key-Migration –
users.selected_api_key_id→agent_api_keys(pro Agent statt global pro User) - 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