Previously a session could only be reassigned to another user via a manual DB UPDATE. This adds it to the session edit UI for every owner of a session (not just admins) - the ownership check in HandleUpdate (existing.UserId == userId) already guarantees the caller owns the session, so handing over one's own session is safe. Backend: - ProfileEndpoint: new GET /api/users returning a minimal user directory (userId/username/displayName only) for any logged-in user. The admin-gated /api/admin/users stays the source for sensitive fields (defaultPath/canUseCli/isAdmin). - SessionEndpoint.HandleUpdate: validate the target user exists (400 if not) and pass allowUserReassign: true to UpdateSession. - DatabaseService.UpdateSession: new allowUserReassign flag; only then is a non-empty userId applied as user_id (empty/null is ignored - a session must always have an owner). Frontend: - index.html: hidden #sessionOwnerRow with a user picker card in the session edit form (existing sessions only). - cards.js: buildUserCard helper for the picker. - session-page.js: initSessionUserPicker loads users via apiGetUsers(), preselects the current owner, and on an actual owner change asks for confirmation (irreversible - the session leaves the user's own list). - api.js: apiGetUsers() -> /api/users. - ARCHITECTURE.md: document /api/users. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
118 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, auto_context), Tag für Sub-Agent-Markierung oder Import-Herkunft (openclaw-import), SortKey für korrekte Reihenfolge, thought_signature (Gemini) |
attachments |
Datei-Anhänge (base64, image/document) |
archives |
Archivierte Chain-Gruppen |
agent_skills |
Agent ↔ Skill Zuordnung |
cronjobs |
Geplante Tasks (cron expression, prompt, notify_session_id, is_enabled, last_run, next_run) |
shared_messages |
Geteilte Nachrichten (Share-Token, Ablaufdatum) |
telegram_bots |
Telegram Bots (bot_token, webhook_secret, name, is_public) – eigenständige Entität |
api_keys |
API-Keys (key_id, provider, name, api_key, is_active, status, plan, usage_7d, usage_5h, source_url, checked_at, is_public, refresh_token, access_token_expires_at, oauth_provider) – mehrere Keys pro Provider möglich, Zuweisung pro Agent über agent_api_keys |
agent_api_keys |
API-Key-Zuweisung pro User+Agent (user_id, agent_id, key_id) – ersetzt globales users.selected_api_key_id |
scanned_keys |
Gescannte GitHub-Keys (scanned_key_id, api_key, source_url, source, type, status, plan, usage_7d, usage_5h, adopted_api_key_id, deleted) |
scanner_logs |
Scanner-Logs (timestamp, type, message, scanner) – Logs von Key-Scanner und Event-Scanner |
server_settings |
Server-Settings (key, value, type) – In-Memory-Cache über ServerSettingsService |
message_embeddings |
Sentence-Embeddings für semantische Suche (message_id, vector BLOB, session_id, agent_id) |
memory_file_embeddings |
Embeddings von Memory-Dateien (agent_id, file_path, chunk_index, vector BLOB) |
Zentrale Records
ChainRecord – Repräsentiert einen kompletten Request-Response-Zyklus:
ChainId– Eindeutige 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,assistant,system(fürauto_context)Type–text,tool_use,tool_result,error,web_search_result,auto_contextContent– 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 |
| Users | /api/users |
GET (minimales User-Verzeichnis: id/username/displayName – für jeden eingeloggten User, z. B. Session-Zuweisung) |
| Settings | /api/settings |
GET, PUT |
| Backup | /api/backup |
POST (Admin only, tar.gz Download) |
| Shared | /api/shared/, /api/messages/{id}/share |
Share erstellen, öffentlich abrufen, auflisten, löschen |
| Models | /api/models/ |
GET nvidia, GET pollinations, GET gemini, GET openai, POST refresh |
| API Keys | /api/admin/api-keys/ |
CRUD + PUT {id}/public, POST check, POST check-all, POST scan, GET scan/status, GET scanned, GET scanned/summary, POST scanned/recheck-all, POST scanned/{id}/recheck, POST scanned/{id}/adopt, DELETE scanned/{id}, GET event-scanner/status, GET event-scanner/log, GET scanner/logs (Admin) + /api/api-keys/ GET available, PUT select/{keyId}, GET selected (User) |
| Remote | /api/remote |
WebSocket-Endpoint für Remote-Clients, GET connections, POST assign/unassign, GET /remote.ps1 |
| Internal | /api/internal/ |
POST remote-execute, POST ask-user-choice, POST memory-read/write/list (Token-gesichert für MCP-Server) + POST /api/chat/choice-respond (normale Auth) |
| DB Explorer | /api/admin/db/ |
GET tables, GET tables/{name} (Admin-only) |
| Info | /api/info |
GET (Build-Zeitstempel + Embedding-Verfügbarkeit) |
| ServerSettings | /api/admin/server-settings |
GET, PUT (Admin-only, liest/schreibt appsettings.Production.json) |
Middleware
AuthMiddleware prüft jede /api/-Anfrage auf ein gültiges gott_token-Cookie.
Ausnahmen (kein Auth nötig):
- Statische Dateien (nicht
/api/) POST /api/auth/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,
addAutoContextBubble()für Auto-Context-Box - bubbles.js – Message Bubbles (User, Assistant, Error, Warning, Choice, Review, Attachments)
- chat.js – SSE-Stream-Handling, sendMessage, Plan-Mode
- history.js – Chat-History laden, paginieren, rendern, Polling
- archive.js – Archiv-Loading und -Rendering
- reply.js – Reply/Quote-Handling (Set, Clear, Format)
- files.js – File-Upload, Workspace-Refs
- session.js – Session-State-Management (currentSessionId, currentAgentId, getCurrentAgentId())
- file-explorer.js – Workspace File-Explorer Panel (Dateien, Ordner erstellen/löschen, Folder-Browser)
- typing-phrases.js – Zufällige Typing-Indicator-Texte (Wienerisch/Österreichisch)
- avatar-editor.js – Wiederverwendbares Avatar-Editor-Modal (Upload, Paste, AI-Generate, Scale)
- commands-ui.js – Custom-Commands UI-Komponente
Wiederverwendbare Card-Builder (cards.js)
buildAgentCard(agent)– Erzeugt ein.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 220 Messages (
MaxHistory-Setting, konfigurierbar): Die History wird auf maximal N Messages begrenzt (älteste entfernt) - Ältere Chains: Nur
text-Messages (user + assistant), keine Tool-Calls - Letzte Chain: Vollständig mit allen Tool-Use/Tool-Result Blöcken
- Attachments: Nur für die letzte User-Message geladen (spart enorme Token)
- Tool-Results: In der History auf 20.000 Zeichen gekürzt (vollständig in DB)
- System-Prompt-Reminder: Alle ~30 Messages wird ein automatisch generierter Reminder als User/Assistant-Paar injiziert (z.B.
[System Reminder] Beachte weiterhin den System-Prompt (Identity, Workspace, Kontext, Memory, ...)). Bekämpft das "Lost-in-the-Middle"-Problem bei langen Konversationen. Der Reminder-Text wird aus den aktiven Prompt-Block-Labels generiert (SystemPromptBuilder.BuildReminder()). Nur aktiv wenn ≥2 Blöcke vorhanden.
Auto-Context (Vector-Search)
Wenn am Agent UseAutoContext aktiv ist, wird vor jedem Request über VectorSearchService ein semantisch passender Kontext zur aktuellen User-Anfrage gesucht (aus früheren Chat-Nachrichten + Memory-Files). Der Kontext wird als <recalled_context>-Block in den System-Prompt injiziert.
Header-Anweisung: Der Block hat eine bewusst direkte Anweisung an das Model:
"WICHTIG: Der folgende Kontext wurde automatisch per Vektor-Suche aus früheren Gesprächen und Memory abgerufen. Prüfe diesen Kontext ZUERST, bevor du antwortest. Wenn er zur aktuellen Frage passt, beziehe dich aktiv darauf und nutze die Informationen in deiner Antwort. Ignoriere ihn nur, wenn er eindeutig irrelevant ist."
Die starke Formulierung verhindert, dass das Model den Kontext ignoriert (Default = Nutzung statt Ignorieren).
SSE-Event: Der Auto-Context wird zusätzlich als auto_context-SSE-Event vor dem Streaming an das Frontend gesendet, damit der User sieht, was der Agent als Kontext bekommen hat (aufklappbare Auto-Context-Box im Chat).
DB-Persistierung: Der Block wird auch als Message in der DB gespeichert (Role = "system", Type = "auto_context"), damit er beim Laden der History sichtbar bleibt. BuildHistoryFromDb filtert auto_context-Messages explizit, sie werden also nicht an die API zurückgesendet.
Implementierung: Sowohl AnthropicService.Streaming.StreamChat() als auch AnthropicService.Cli.StreamChatViaCli() rufen BuildAutoContextAsync() auf, senden das SSE-Event und persistieren die Message – damit funktioniert die Anzeige sowohl im API-Streaming-Pfad als auch im CLI-Pfad.
Tool-System
Der Agent hat Zugriff auf folgende Tools, abhängig von Konfiguration:
Datei-Tools (wenn Workspace konfiguriert)
| Tool | Beschreibung |
|---|---|
read_file |
Datei lesen (relativer Pfad zum Workspace) |
write_file |
Datei erstellen/überschreiben |
edit_file |
Exakten Textabschnitt in Datei ersetzen |
list_files |
Verzeichnis-Listing |
delete_file |
Datei oder leeres Verzeichnis löschen |
execute |
Shell-Befehl ausführen (bash, konfigurierbares Timeout) |
background_execute |
Shell-Befehl als Hintergrund-Prozess starten (gibt PID zurück, wartet nicht auf Ende) |
glob |
Dateien via Glob-Pattern finden |
grep |
Dateien nach Regex/Text durchsuchen |
todo_write |
Todo-Liste schreiben (als Datei im Workspace) |
Memory-Tools (wenn UseMemory aktiviert)
| Tool | Beschreibung |
|---|---|
memory_read |
Memory-Datei des Agents lesen |
memory_write |
Memory-Datei schreiben (überschreiben/append) |
memory_list |
Alle Memory-Dateien des Agents auflisten |
memory_search |
Semantische Suche über vergangene Chat-Nachrichten und Memory-Dateien (benötigt Embedding-Modell) |
Remote-Execution (wenn Remote-Verbindung zugeordnet)
| Tool | Beschreibung |
|---|---|
remote_execute |
Shell-Befehl auf einem verbundenen Remote-Client ausführen (via WebSocket) |
Cross-Session Messaging (wenn konfiguriert)
| Tool | Beschreibung |
|---|---|
send_to_session |
Nachricht an eine andere Session senden und dort eine Agent-Antwort auslösen |
Interaktive Rückfragen (wenn AllowUserChoice aktiviert)
| Tool | Beschreibung |
|---|---|
ask_user_choice |
Stellt dem User eine Frage mit 2-5 Auswahlmöglichkeiten |
Dieses Tool wird nicht ausgeführt, sondern als SSE-Event an den Client weitergeleitet. Siehe Interaktive Rückfragen.
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, opus-4-7, opus-4-8, fable, fable-5, haiku, haiku-4-5 (Default für opus ist claude-opus-4-8) |
CustomCommands |
JSON-Array mit Slash-Commands (Label + Prompt) |
AutoCommit |
"" (nichts), "mention" (anmerken – nur auf User-Request), "enforce" (erzwingen – nach jeder Änderung), "forbid" (strikt verboten) |
UseSoul |
SOUL.md in System-Prompt einbinden |
UseIdentity |
IDENTITY.md in System-Prompt einbinden |
UseUserInfo |
User-Persona in System-Prompt einbinden |
UseMemory |
Memory-Tools aktivieren |
WebSearchVersion |
Web-Suche aktivieren (z.B. web_search_20260209) |
AllowUserChoice |
ask_user_choice-Tool verfügbar (Default: true) |
TaskInstruction |
Aufgabe/Anweisung – optionaler Text, wird als ## Anweisungen-Block in System-Prompt eingefügt |
Effort |
Reasoning-Effort-Level für Claude. "" (=high, Default), low, medium, high, xhigh (nur Opus 4.7+), max (nur Opus 4.6+/Sonnet 4.6). API: als output_config.effort gesendet. CLI: via Env-Var CLAUDE_CODE_EFFORT_LEVEL. Bei OpenAI-Providern ignoriert. |
IsPublic |
Andere User können den Agent in ihren Sessions verwenden (aber nicht bearbeiten) |
Model-Auflösung
"sonnet" → claude-sonnet-4-6
"sonnet-4-5" → claude-sonnet-4-5
"sonnet-4-6" → claude-sonnet-4-6
"opus" → claude-opus-4-8
"opus-4-5" → claude-opus-4-5
"opus-4-6" → claude-opus-4-6
"opus-4-7" → claude-opus-4-7
"opus-4-8" → claude-opus-4-8
"fable" → claude-fable-5
"fable-5" → claude-fable-5
"haiku" → claude-haiku-4-5
"haiku-4-5" → claude-haiku-4-5
(default) → claude-sonnet-4-6
1-Million-Token-Context ist seit März 2026 Default bei Opus 4.6/4.7/4.8 + Sonnet 4.6 – kein separater Beta-Header und kein [1m]-Suffix mehr nötig. Alte DB-Einträge mit [1m]-Suffix werden bei Startup automatisch migriert (UPDATE agents/chains SET model = REPLACE(model, '[1m]', '')).
System-Prompt-Aufbau
Der SystemPromptBuilder erzeugt den System-Prompt modular aus konfigurierbaren Blöcken:
┌──────────────────────────────────────────┐
│ 1. IDENTITY.md (UseIdentity) │ → Wer ist der Agent?
│ 2. SOUL.md (UseSoul) │ → Persönlichkeit, Verhalten
│ 3. Anweisungen (TaskInstruction) │ → Aufgaben-/Anweisungstext (optional)
│ 4. User-Persona (UseUserInfo) │ → Infos über den User
│ 5. Workspace-Info │ → Tools + Workspace-Pfad
│ 6. Rückfragen (AllowUserChoice) │ → Hinweis zu ask_user_choice Tool
│ 7. Session-Kontext │ → Freiformtext
│ 8. Skills (Agent-Skills) │ → Skill-Prompts
│ 9. Memory (UseMemory) │ → Memory-Regeln + MEMORY.md Inhalt
│10. Git-Verhalten (AutoCommit) │ → Commit-Regeln
│11. Markierte Nachrichten │ → Vom User als wichtig markierte Messages
│ [Plan-Modus-Block] │ → Nur wenn planMode=true (überspringt 10)
│ [Preparer-Instruktionsblock] │ → Nur für Preparer-Agents
└──────────────────────────────────────────┘
Jeder Block hat:
- Icon – Font Awesome Icon-Klasse
- Label – Beschreibender Titel
- Content – Markdown-Text
Die Blöcke sind über die API als JSON abrufbar (GET /api/sessions/{id}/system-prompt-blocks), sodass das Frontend sie einzeln inspizierbar darstellen kann.
Memory-System
Jeder Agent hat ein eigenes Dateisystem-basiertes Gedächtnis:
data/agents/{agentId}/
├── IDENTITY.md # Identität (aus Template initialisiert)
├── SOUL.md # Persönlichkeit (aus Template initialisiert)
├── MEMORY.md # Haupt-Memory (kuratierte Informationen)
└── memory/ # Unterordner für Tages-Logs, Themen
├── YYYY-MM-DD.md
└── ...
Funktionsweise
- 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, nano-banana-2,
imagen-3, imagen-4, imagen-4-fast, imagen-4-ultra,
ideogram-2-turbo, ideogram-3,
seedream-3, seedream-4, seedream-5-lite, seededit,
z-image, qwen-2512,
chatgpt-image, runway-gen-4
Modelle in MINIMAL_MODELS (seedream-3, runway-gen-4, seedream-5-lite, seededit) unterstützen kein width/height/steps/guidance und werden nur mit Prompt aufgerufen.
Flow
1. POST /api/images/generate { prompt, model, width, height, ... }
2. KreaService sendet Request an Krea API
3. Job-Polling bis Status "completed"
4. Bilder herunterladen nach data/images/
5. Metadaten als JSON speichern
6. Response mit Dateinamen + URLs
Bild-Serving
GET /api/images– Liste aller generierten Bilder (aus Metadaten-JSON)GET /api/images/file/{filename}– Einzelnes Bild ausliefern (ohne Auth)GET /api/images/file/{filename}/thumb– Thumbnail (320px WebP, gecacht 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. 20 Iterationen pro Review-Run)
- 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() / RunSubAgentViaCli() – Provider-Weiche für Preparer/Review/Finalizer:
- Anthropic mit API-Key: Eigener API-Call an Claude mit Agent-Config (RunAgentChat, Streaming + Tool-Loop)
- Anthropic ohne API-Key + CLI verfügbar:
RunSubAgentViaCli()– nutztClaudeCliService.ChatStreamingAsync(), sammelt Text, sendet identische SSE-Events ({type}_text,{type}_tool_use,{type}_done) und persistiert die Message mitTag = agentType - Anthropic ohne API-Key + ohne CLI: Sub-Agent wird übersprungen (kein Hard-Error)
- OpenAI-kompatibel:
OpenAiService.ChatNonStreaming()(Non-Streaming agentic loop mit Tool-Calling) - Tool-Loop mit SSE-Events (
{type}_text,{type}_tool_use) - Antwort als Message in DB persistieren (mit
Tag = agentType, wird aus API-History gefiltert)
SSE-Events
| Event | Daten | Beschreibung |
|---|---|---|
finalizer_start |
{ agent: "Name" } |
Finalizer-Phase beginnt |
finalizer_text |
{ text: "..." } |
Finalizer-Agent-Text (Token-Stream) |
finalizer_tool_use |
{ tool: "name", input: "..." } |
Finalizer nutzt Tool |
finalizer_done |
{} |
Finalizer-Phase beendet |
Frontend
- Session-Page: Vierter Agent-Picker: "Vorbereiter-Agent", "Review-Agent", "Finalizer-Agent" im "Weitere Einstellungen"-Bereich
- Chat-UI: Preparer-Bubbles (
.preparer-bubble, Info-Farbe, 🔍-Icon), Finalizer-Bubbles (.finalizer-bubble, grüne Farbe, 🏁-Icon) - History:
[Preparer: AgentName]→ styled Header 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
Native Pfade (Windows vs. Linux)
WorkspaceHelper.GetRelativePath() und WorkspaceEndpoint.HandleBrowse() liefern native Pfade zurück (Windows: \, Linux/macOS: /). Das Frontend (file-explorer.js) unterstützt beide Separatoren über Regex-Patterns wie [\\/]+$. Auf Windows kann von C:\ aus zur Laufwerksübersicht ("" = leerer Pfad) navigiert werden.
Session-Exec
POST /api/sessions/{id}/exec führt Shell-Befehle im Workspace aus:
- foreground: Synchron, wartet auf Ergebnis (30s Timeout)
- background: Fire-and-forget via
systemd-run
Avatar-System
Sessions und Agents können ein Bild/Avatar haben mit konfigurierbarem Zoom.
Datenfelder
| Feld | Tabellen | Beschreibung |
|---|---|---|
image / avatar |
sessions / agents |
Base64-kodiertes Bild (WebP, max 512px via ImageService) |
image_small / avatar_small |
sessions / agents |
Thumbnail (WebP, 256px, Q70) für Listen/Dashboard |
avatar_scale |
sessions, agents |
Zoom-Faktor (0.5× – 3×, Default 1.0) |
avatar_offset_x |
sessions, agents |
Horizontale Position (-50% – +50%, Default 0), via CSS translate() |
avatar_offset_y |
sessions, agents |
Vertikale Position (-50% – +50%, Default 0), via CSS translate() |
Darstellung via CSS transform: scale(S) translate(X%, Y%) im .card-image Container (overflow: hidden).
Bild-Verarbeitung (ImageService)
Alle Bilder werden beim Upload in WebP konvertiert (statt PNG), was ~60-80% Speicherersparnis bringt:
| Variante | Methode | Max. Größe | Qualität | Verwendung |
|---|---|---|---|---|
| Voll | ResizeDataUrl() |
512px | Q80 | Editor, Detail-Ansicht (GET /api/agents/{id}, GET /api/sessions/{id}) |
| Thumbnail | ResizeDataUrlSmall() |
256px | Q70 | Listen, Dashboard (GET /api/agents, GET /api/sessions) |
Die Listen-Endpoints liefern das Thumbnail als avatar/image-Feld (Fallback auf Voll-Bild wenn kein Thumbnail existiert). Die Detail-Endpoints liefern das volle Bild.
Avatar-Editor (avatar-editor.js)
Wiederverwendbares Modal-Modul, genutzt in Session- und Agent-Pages:
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
Windows (PowerShell):
irm https://server/remote.ps1 | iex
Linux/macOS (Bash):
curl -s https://server/remote.sh | bash
Beide Scripts werden über den RemoteEndpoint ausgeliefert, wobei der {{WS_URL}}-Platzhalter beim Request mit der korrekten WebSocket-URL ersetzt wird. Das Bash-Script enthält einen embedded Python WebSocket-Client (RFC 6455) und sucht automatisch python3 → python → py.
- Remote-Client lädt das Script und baut WebSocket-Verbindung zu
/api/remoteauf - Sendet
hello-Message mit Hostname, Username, OS-Version RemoteConnectionManagerregistriert die Verbindung- Modal-UI auf Dashboard / Chat zeigt beide Befehle (Windows + Linux/macOS)
Session-Zuweisung
- Im Chat: Remote-Button → Modal zeigt aktive Verbindungen
- Admin weist Verbindung einer Session zu (
POST /api/remote/assign) - AI-Agent bekommt
remote_execute-Tool → führt Befehle auf dem Remote-Client aus - Ergebnis wird synchron via WebSocket zurückgeliefert (Timeout: konfigurierbarer
ExecuteTimeout)
OS-aware Tool-Description
Das remote_execute-Tool (sowohl im internen ToolService als auch im MCP-Server) bekommt eine dynamische Tool-Description basierend auf dem OS des verbundenen Remote-Clients:
- Linux: "Bash-Befehl auf der verbundenen Remote-Linux-Workstation ausführen"
- Windows: "PowerShell-Befehl auf der verbundenen Remote-Windows-Workstation ausführen"
Das OS wird aus der hello-Message des Clients (RemoteConnection.OsVersion) bezogen und über ChatEndpoint → ChatService.PrepareContext(remoteOs) → ToolPayloadOptions.RemoteOs → ToolDefinitionOptions.RemoteOs bis zu ToolService.GetRemoteExecuteTool(remoteOs) durchgereicht. Im CLI-Pfad zusätzlich an die MCP-Config (GenerateMcpConfig(remoteOs)) und von dort als Env-Variable GOTT_REMOTE_OS an den Python-MCP-Server. Damit generiert Claude direkt passende Befehle für die jeweilige Shell. Cronjobs/Telegram-Bots rufen PrepareContext aktuell ohne remoteOs auf (Default = leer → PowerShell-Description als Fallback).
Protokoll (JSON über WebSocket)
| Richtung | Type | Payload |
|---|---|---|
| Client → Server | hello |
hostname, username, os |
| Server → Client | execute |
command |
| Client → Server | result |
exitCode, output, error |
| Client → Server | ping |
– |
| Server → Client | pong |
– |
Semantische Suche (Embedding)
Lokale Sentence-Embeddings ermöglichen semantische Suche über Chat-Nachrichten und Memory-Dateien.
Technologie
- Modell: all-MiniLM-L6-v2 (ONNX Runtime, 384-dimensionale Embeddings)
- Tokenisierung: WordPiece (BERT-kompatibel, eigene Implementierung)
- Speicherung: SQLite-Tabellen
message_embeddings+memory_file_embeddings(Vektor als BLOB) - Suche: Brute-Force Cosine Similarity (ausreichend für <100k Embeddings)
Komponenten
| Service | Aufgabe |
|---|---|
EmbeddingService |
ONNX-Modell laden, Embeddings generieren (thread-safe via SemaphoreSlim) |
VectorSearchService |
Schema-Management, Embedding-Storage, Similarity-Search |
EmbeddingBackgroundService |
Backfill beim Start + Live-Queue für neue Messages/Memory-Files |
Funktionsweise
- 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
CLI-Fallback für Sub-Agents
Wenn ein Sub-Agent (Preparer, Review, Finalizer) keinem API-Key zugewiesen ist, wird – sofern die CLI verfügbar ist – automatisch RunSubAgentViaCli() (in AnthropicService.Agents.cs) aufgerufen. Damit funktionieren optionale Sub-Agents auch ohne explizit zugewiesenen Anthropic-Key. Erst wenn weder Key noch CLI vorhanden sind, wird der Sub-Agent übersprungen statt mit einem Hard-Error abzubrechen.
Python-Detection für MCP-Config
Die MCP-Config (AnthropicService.Cli.GenerateMcpConfig()) sucht beim Start dynamisch nach einer Python-Installation in der Reihenfolge python3 → python → py (das Ergebnis wird gecached). Auf Linux ist üblicherweise python3 da, auf Windows oft nur python oder py. Wird kein Python gefunden, wird im Chat ein SSE-warning-Event gesendet ("Python wurde nicht gefunden... MCP-Tools sind nicht verfügbar.") – die normalen Tools funktionieren weiterhin.
Datenbank-Explorer
Admin-only-Feature zum direkten Inspizieren der SQLite-Datenbank über die Web-UI.
Backend
DatabaseExplorerEndpoint.cs bietet zwei Endpoints:
GET /api/admin/db/tables– Listet alle Tabellen mit Spalten- und 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