Files
gott/ARCHITECTURE.md
T
MelekiandClaude Fable 5 81892d6e65 Agent: Max Output Tokens + Thinking deaktivieren einstellbar
Zwei neue Agent-Einstellungen (Frontend, DB-Migration additiv):
- MaxOutputTokens: hartes Ausgabelimit. CLI: CLAUDE_CODE_MAX_OUTPUT_TOKENS,
  API: max_tokens (statt hart 16384). 0 = aus.
- DisableThinking: CLI: CLAUDE_CODE_DISABLE_THINKING=1 + MAX_THINKING_TOKENS=0;
  entfernt CLAUDE_CODE_EFFORT_LEVEL (Explizit-Aus schlaegt Effort).
  API-Pfad ohne Aequivalent (Hint im UI).

Die einzigen zwei Parameter, die z.ai/GLM nachweislich honoriert
(Sampling-Parameter wie temperature/top_p werden dort still verworfen).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 17:54:11 +02:00

133 KiB
Raw Blame History

ARCHITECTURE.md – Gott

Gott ist eine selbst-gehostete AI-Chat-Plattform, gebaut auf .NET 10 mit Anthropic Claude API als Haupt-Provider (inkl. Claude CLI-Integration) sowie OpenAI-kompatiblen Providern (Nvidia, Pollinations, Gemini, OpenAI). Sie bietet Multi-User-Support, persistente Agent-Memory, 19 Tools (Dateien, Shell, Suche, Memory, Remote-Execution, Web-Search, GitHub Code Search), semantische Suche via lokalen Embeddings, Telegram-Integration, Cronjobs, Remote-Verbindungen, Krea-Bildgenerierung und einen Multi-Provider Key-Scanner – alles über ein Vanilla-JavaScript-Frontend gesteuert.


Inhaltsverzeichnis

  1. Überblick
  2. Tech-Stack
  3. Projektstruktur
  4. Datenmodell
  5. Backend-Architektur
  6. Frontend-Architektur
  7. Multi-Provider-System (OpenAI-kompatibel)
  8. Chat-Pipeline
  9. Tool-System
  10. Agent-System
  11. System-Prompt-Aufbau
  12. Memory-System
  13. Skill-System
  14. Telegram-Integration
  15. Cronjob-System
  16. Krea-Bildgenerierung
  17. Auth & Multi-User
  18. Admin-Benutzerverwaltung
  19. Multi-Agent System (Preparer + Review + Finalizer)
  20. Interaktive Rückfragen (ask_user_choice)
  21. Plan-Modus 21b. Ultracode & Workflow-Status
  22. Web-Search
  23. Nachrichten teilen (Shared Messages)
  24. Workspace & File-Explorer
  25. Avatar-System
  26. LaTeX/Math-Rendering
  27. Windows Desktop App
  28. OpenClaw Import
  29. Remote-Verbindungen
  30. Semantische Suche (Embedding)
  31. API-Key-Verwaltung
  32. Key-Scanner-System
  33. Claude CLI Integration
  34. Datenbank-Explorer
  35. MCP-Server-Integration (Internal API)
  36. Deployment
  37. Konfiguration
  38. 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
│   ├── WorkflowProgressTracker.cs # Registry der dynamischen Workflows (Ultracode) pro Session + WorkflowTelemetryReader
│   ├── 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, WorkflowStatus, 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, Payload, Command-Output, Krea-Image, Remote, Workflow-Status)
│   │   ├── stream-ui.js          # Stream-Bubble, Review-Streaming
│   │   ├── bubbles.js            # Message Bubbles (User, Assistant, Error, Warning, Choice, Review)
│   │   ├── chat.js               # Chat-Logic, SSE-Streaming, Plan-Mode
│   │   ├── history.js            # History-Rendering, Polling
│   │   ├── archive.js            # Archiv-Loading
│   │   ├── reply.js              # Reply/Quote-Handling
│   │   ├── files.js              # File-Upload, Workspace-Refs
│   │   ├── session.js            # Session-Verwaltung (currentSessionId, currentAgentId)
│   │   ├── file-explorer.js      # File-Explorer Komponente
│   │   ├── avatar-editor.js      # Wiederverwendbares Avatar-Editor-Modal
│   │   ├── commands-ui.js        # Custom-Commands UI
│   │   ├── emoji.js              # Emoji-Picker
│   │   ├── emoji.userdata.js     # Häufig genutzte Emojis
│   │   ├── tooltip.js            # Tooltip-System
│   │   ├── title.js              # Tab-Titel-Management + Embedding-Warnung
│   │   ├── typing-phrases.js     # Zufällige Typing-Indicator-Texte (Wienerisch)
│   │   ├── pages/                # Seiten-Komponenten
│   │   │   ├── chat-page.js      # Haupt-Chat-Ansicht (inkl. Remote-Button, Usage-Badge-Overlay, Key-Switch-Modal)
│   │   │   ├── dashboard.js      # Dashboard / Home (inkl. API-Keys-Link, DB-Explorer-Link)
│   │   │   ├── session-page.js   # Session-Einstellungen
│   │   │   ├── agent-page.js     # Agent-Editor
│   │   │   ├── telegram-bot-page.js # Telegram-Bot-Editor
│   │   │   ├── cronjob-page.js   # Cronjob-Verwaltung
│   │   │   ├── profile-page.js   # Profil-Bearbeitung
│   │   │   ├── admin-page.js      # Admin: Benutzerverwaltung
│   │   │   ├── api-keys-page.js   # Admin: API-Key-Verwaltung + Check + GitHub-Scanner + Event-Scanner
│   │   │   ├── db-explorer-page.js # Admin: Datenbank-Explorer (Tabellen + Daten)
│   │   │   ├── settings-page.js  # App-Einstellungen
│   │   │   ├── server-settings-page.js # Admin: Servereinstellungen
│   │   │   ├── login-page.js     # Login-Formular
│   │   │   └── shared-page.js    # Öffentliche geteilte Nachricht
│   │   └── lib/                  # Vendor-Libraries
│   │       ├── marked.min.js     # Markdown Parser
│   │       ├── codemirror.min.js # Code Editor
│   │       ├── codemirror.cjs    # CM CommonJS Wrapper
│   │       ├── easymde.min.js    # Markdown Editor
│   │       ├── katex.min.js      # KaTeX Math-Rendering Engine
│   │       └── cm-mode-*.min.js  # CodeMirror Syntax-Modes
│   ├── remote.ps1                # PowerShell-Script für Remote-Client-Verbindung
│   ├── fonts/                    # Self-hosted Fonts (Inter, JetBrains Mono)
│   └── lib/font-awesome/         # Font Awesome Icons
│
├── WindowsApp/                   # Windows Desktop Client (WPF)
│   ├── Gott.csproj               # .NET 10 Windows, WebView2
│   ├── App.xaml / App.xaml.cs     # WPF Application
│   ├── MainWindow.xaml/cs        # WebView2 Host Window
│   └── appsettings.json          # Server-URL Konfiguration
│
└── Build/                        # Build-Output (NICHT editieren!)
    ├── Linux/
    │   ├── bin/  obj/  publish/
    └── Windows/
        ├── bin/  obj/  publish/
    └── data/
        ├── gott.db               # SQLite Datenbank
        ├── agents/               # Agent Memory-Dateien
        │   └── {agentId}/
        │       ├── IDENTITY.md
        │       ├── SOUL.md
        │       ├── MEMORY.md
        │       └── memory/       # Tages-Logs etc.
        ├── skills/               # Skill-Verzeichnisse
        │   └── {slug}/
        │       ├── SKILL.md
        │       ├── PROMPT.md
        │       └── output/
        ├── images/               # Krea-generierte Bilder
        ├── models/               # ONNX-Modelle (Embedding)
        │   └── all-MiniLM-L6-v2/ # Sentence-Embedding-Modell (model.onnx + vocab.txt)
        ├── models_cache.json     # Gecachte Provider-Modelle (Nvidia, Pollinations, Gemini, OpenAI)
        ├── logs/                 # Payload-Logs (optional)
        └── backup/               # Backup-Archive

Datenmodell

Entity-Relationship

User (1) ─── (*) Agent
User (1) ─── (*) Session
Agent (1) ─── (*) Session     (Session hat optional einen Agent)
Agent (1) ─── (*) Session     (Session hat optional einen Review-Agent)
Agent (1) ─── (*) Session     (Session hat optional einen Finalizer-Agent)
Agent (*) ─── (*) Skill       (via agent_skills Tabelle)
Session (1) ─── (*) Chain     (ein Chat-Request = eine Chain)
Session (1) ─── (*) Archive   (archivierte Chains)
Chain (1) ─── (*) Message
Message (1) ─── (*) Attachment
Session (1) ─── (*) Cronjob
User (1) ─── (*) Cronjob
User (1) ─── (*) SharedMessage
Message (1) ─── (*) SharedMessage
User (1) ─── (*) TelegramBot
TelegramBot (1) ─── (*) Session  (via telegram_bot_id)

Tabellen

Tabelle Beschreibung
users Benutzer (username, password_hash, display_name, is_admin, persona, settings, default_path)
tokens Auth-Tokens (token → user_id)
agents AI-Agenten (model – z.B. claude-sonnet-4-6 oder nvidia:deepseek-ai/deepseek-r1, custom_commands, auto_commit, use_soul/identity/user_info/memory/auto_context, use_github_search, use_serper_search, use_web_search, web_search_version, allow_user_choice, task_instruction, avatar_scale/offset, avatar_small, is_public)
sessions Chat-Sessions (description, image, image_small, workspace, context, custom_commands, telegram_bot_id, telegram_chat_id, avatar_scale/offset, preparer_agent_id, review_agent_id, max_review_iterations, finalizer_agent_id, multi_model_mode, panel_agent_ids, moa_layers)
chains Request-Chains (model, tokens, iterations, timing, tool_choice, tool_names, stream_error_info, workflow_info, plan_mode, effort, archive_id)
messages Einzelne Nachrichten (text, tool_use, tool_result, error, web_search_result, auto_context), Tag für Sub-Agent-Markierung oder Import-Herkunft (openclaw-import), SortKey für korrekte Reihenfolge, thought_signature (Gemini)
attachments Datei-Anhänge (base64, image/document)
archives Archivierte Chain-Gruppen
agent_skills Agent ↔ Skill Zuordnung
cronjobs Geplante Tasks (cron expression, prompt, notify_session_id, is_enabled, last_run, next_run)
shared_messages Geteilte Nachrichten (Share-Token, Ablaufdatum)
telegram_bots Telegram Bots (bot_token, webhook_secret, name, is_public) – eigenständige Entität
api_keys API-Keys (key_id, provider, name, api_key, is_active, status, plan, usage_7d, usage_5h, source_url, checked_at, is_public, refresh_token, access_token_expires_at, oauth_provider) – mehrere Keys pro Provider möglich, Zuweisung pro Agent über agent_api_keys
agent_api_keys API-Key-Zuweisung pro User+Agent (user_id, agent_id, key_id) – ersetzt globales users.selected_api_key_id
scanned_keys Gescannte GitHub-Keys (scanned_key_id, api_key, source_url, source, type, status, plan, usage_7d, usage_5h, adopted_api_key_id, deleted)
scanner_logs Scanner-Logs (timestamp, type, message, scanner) – Logs von Key-Scanner und Event-Scanner
server_settings Server-Settings (key, value, type) – In-Memory-Cache über ServerSettingsService
message_embeddings Sentence-Embeddings für semantische Suche (message_id, vector BLOB, session_id, agent_id)
memory_file_embeddings Embeddings von Memory-Dateien (agent_id, file_path, chunk_index, vector BLOB)

Zentrale Records

ChainRecord – Repräsentiert einen kompletten Request-Response-Zyklus:

  • ChainId – Eindeutige ID
  • SessionId – Zugehörige Session
  • AgentId – Zugehöriger Agent
  • Model – Verwendetes Modell (z.B. claude-sonnet-4-6, nvidia:deepseek-ai/deepseek-r1, gemini:gemini-2.5-flash)
  • SystemPrompt – Der vollständige System-Prompt (gespeichert für Nachvollziehbarkeit)
  • InputTokens, OutputTokens, CacheCreationTokens, CacheReadTokens – Token-Verbrauch
  • Iterations – Anzahl der Tool-Loop-Durchläufe
  • StopReason – end_turn, tool_use, max_iterations, error, client_disconnected, awaiting_user_choice, preparer_query
  • ToolChoice – Verwendete Tool-Choice-Strategie (automatisch: any wenn Workspace/Skills/Memory, sonst auto)
  • ToolNames – Komma-separierte Liste der verwendeten Tools
  • ArchiveId – Zugehöriges Archiv (wenn archiviert)
  • StreamErrorInfo – Fehlerdetails bei Stream-Abbrüchen
  • WorkflowInfo – JSON-Telemetrie eines dynamischen Workflows (Ultracode): die reiche, beim Lauf-Ende aus der <runId>.json gelesene Telemetrie – Phasen + Per-Agent-Details (Label/Phase/State/Tokens/Tool-Calls/Dauer) + Summen. null wenn die Chain keinen Workflow gespawnt hat (siehe Workflow-Status)
  • PlanMode – Ob die Chain im Plan-Modus erstellt wurde (informativ)
  • DurationMs – Gesamtdauer der Chain

MessageRecord – Eine einzelne Nachricht innerhalb einer Chain:

  • Role – user, assistant, system (für auto_context)
  • Type – text, tool_use, tool_result, error, web_search_result, auto_context
  • Content – Textinhalt oder JSON (bei tool_use: Input-JSON)
  • ToolName, ToolCallId – Bei Tool-Aufrufen
  • IsError – Ob die Nachricht ein Fehler ist
  • Iteration – Welche Tool-Loop-Iteration
  • AttachmentCount – Anzahl angehängter Dateien
  • AttachmentNames – JSON-Array mit Dateinamen
  • Tag – Markierung (null = normal, "preparer"/"review"/"finalizer"/"panel" = Sub-Agent-Message (wird aus API-History gefiltert), "openclaw-import" = importierte Nachricht (wird immer in History einbezogen))
  • IsMarked – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)
  • ThoughtSignature – Gemini thought_signature (für Gemini-Tool-Calls, wird im nächsten Request zurückgesendet)
  • DeletedOn – Soft-Delete Zeitstempel

SessionWithStats – Erweitertes Session-Objekt für Listen-Endpunkt:

  • Enthält alle SessionRecord-Felder + MessageCount, ArchiveMessageCount, TotalMessageCount
  • TelegramConfigured, TelegramLinked – Telegram-Status (Bot zugewiesen / Chat-ID verknüpft)
  • GitStatus – Inline Git-Status-Summary (Add, Edit, Delete, Outgoing) oder null wenn kein Workspace (wird im Endpoint hinzugefügt, nicht Teil des Records)
  • Image wird durch ImageSmall ersetzt (Thumbnail für Dashboard-Performance)

GitStatusSummary – Kompakte Git-Status-Info:

  • Add, Edit, Delete – Anzahl geänderter Dateien nach Typ
  • Outgoing – Anzahl ausgehender (nicht gepushter) Commits

Backend-Architektur

Program.cs – Startup

Die Program.cs ist der zentrale Einstiegspunkt und konfiguriert:

  1. Service-Registrierung – Alle Services als Singletons im DI-Container
  2. Datenbank-Initialisierung – DatabaseService erstellt Schema + Migrationen
  3. Default-User – Wird automatisch angelegt falls keine User existieren
  4. Middleware – Static Files → AuthMiddleware → Endpoints
  5. Endpoint-Mapping – Alle 17 Endpoint-Gruppen registriert
  6. Server – Kestrel auf http://0.0.0.0:{Port} (Port aus Config, Default 19000)

Initialisierungsreihenfolge:

RemoteConnectionManager → AuthService → DatabaseService → EnsureDefaultUser
→ ImageService, StreamingTracker, MemoryService, SkillService
→ EmbeddingService → VectorSearchService
→ ToolService → ChatService
→ AnthropicService (Token aus DB: api_keys) → ClaudeCliService → OpenAiService (+ LoadModelsAsync)
→ TelegramService → KreaService
→ CronjobService (BackgroundService) + EmbeddingBackgroundService (BackgroundService) + GitHubEventScannerService (BackgroundService)

Services

Service Typ Aufgabe
DatabaseService Singleton SQLite CRUD, Schema-Init, Migrationen, alle DB-Operationen
AuthService Singleton Token-Erzeugung/-Validierung, Passwort-Hashing (SHA256)
ChatService Singleton Chat-Kontext aufbauen, History aus DB rekonstruieren
AnthropicService Singleton Claude API Kommunikation (partial class: Core, Streaming, Agents, Cli), passive Usage-Aktualisierung nach jedem Request
ClaudeCliService Singleton Claude Code CLI Wrapper (OAuth-basiert, kein API-Key nötig), prüft CLI-Verfügbarkeit beim Start
OpenAiService Singleton OpenAI-kompatible Provider (Nvidia, Pollinations, Gemini, OpenAI) mit Tool-Calling, Streaming + Non-Streaming, Modell-Caching
ToolService Singleton Tool-Definitionen bereitstellen, Tool-Aufrufe ausführen (19 Tools)
WorkspaceHelper Static Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz
AppPaths Static Zentrale Pfade (WwwRoot), Dev-Modus vs. Publish-Modus Erkennung
SystemPromptBuilder Static Modularer System-Prompt aus konfigurierbaren Blöcken + Reminder-Generierung
MemoryService Singleton Agent-Gedächtnis auf Dateisystem (MEMORY.md, IDENTITY.md etc.)
SkillService Singleton Skill-Discovery und -Management aus Dateisystem
TelegramService Singleton Telegram Bot API, Webhook-Handling, Nachrichten senden
CronjobService Background Minütlicher Check auf fällige Cronjobs, Ausführung via Claude
SseHelper Static Shared SSE-Utilities: WriteSseEvent, TryWriteSse, FinalizeChain, TruncateToolResult
EmbeddingService Singleton Lokales Sentence-Embedding via all-MiniLM-L6-v2 (ONNX, 384 Dimensionen), WordPiece-Tokenisierung
VectorSearchService Singleton Semantische Suche über Message-Embeddings + Memory-File-Embeddings (Cosine Similarity)
EmbeddingBackgroundService Background Backfill bestehender Messages + Live-Embedding neuer Messages/Memory-Files
RemoteConnectionManager Singleton WebSocket-basierte Remote-Verbindungen, Command-Execution auf verbundenen Clients
KreaService Singleton Krea API für Bildgenerierung, Job-Polling, Image-Download
ImageService Singleton Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail) für Avatare und Session-Bilder
StreamingTracker Singleton Trackt aktive Streams + CancellationTokenSource pro Session für Stop-Button
WorkflowProgressTracker Singleton Registry laufender dynamischer Workflows (Ultracode) pro Session: merkt sich pro Session die aus dem Workflow-tool_result abgegriffenen Transcript-Pfade (journal.jsonl / <runId>.json). Die Telemetrie wird on demand via WorkflowTelemetryReader aus diesen Dateien gelesen (live aus journal.jsonl, abgeschlossen aus <runId>.json) und beim Chain-Finalize in chains.workflow_info persistiert (analog StreamingTracker)
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, GET workflow-status, POST exec, GET git-status (Liste inkl. inline Git-Status + Thumbnail)
Agents /api/agents/ CRUD + GET available (inkl. public agents, inline Skills + Thumbnail)
Admin /api/admin/ GET users, POST users, PUT users/{id}, PUT users/{id}/password, DELETE users/{id}, POST users/{id}/impersonate, POST users/{id}/login-link (Admin-only)
Workspace /api/workspace/ GET tree, GET/PUT file, GET browse, POST mkdir, DELETE dir, POST browse/mkdir, GET agent/tree, GET/PUT agent/file, POST agent/mkdir, GET raw
Telegram /api/telegram/ Bot-CRUD (/bots, /bots/available), POST webhook/{botId} (unauthenticated), Session-Config, Webhook-Management
OpenClaw /api/openclaw/ GET sessions (Liste importierbarer Sessions), POST import (Import in bestehende Session)
Cronjobs /api/cronjobs/ CRUD + POST toggle, POST run
Krea /api/images/ POST generate, GET list, GET file/{name}, GET file/{name}/thumb, GET status
Skills /api/skills GET list, GET/PUT agent skills, Output-Files + Thumbnails
Profile /api/profile GET, PUT, PUT password
Users /api/users GET (minimales User-Verzeichnis: id/username/displayName – für jeden eingeloggten User, z. B. Session-Zuweisung)
Settings /api/settings GET, PUT
Backup /api/backup POST (Admin only, tar.gz Download)
Shared /api/shared/, /api/messages/{id}/share Share erstellen, öffentlich abrufen, auflisten, löschen
Models /api/models/ GET nvidia, GET pollinations, GET gemini, GET openai, POST refresh
API Keys /api/admin/api-keys/ CRUD + PUT {id}/public, POST check, POST check-all, POST scan, GET scan/status, GET scanned, GET scanned/summary, POST scanned/recheck-all, POST scanned/{id}/recheck, POST scanned/{id}/adopt, DELETE scanned/{id}, GET event-scanner/status, GET event-scanner/log, GET scanner/logs (Admin) + /api/api-keys/ GET available, PUT select/{keyId}, GET selected (User)
Remote /api/remote WebSocket-Endpoint für Remote-Clients, GET connections, POST assign/unassign, GET /remote.ps1
Internal /api/internal/ POST remote-execute, POST ask-user-choice, POST memory-read/write/list (Token-gesichert für MCP-Server) + POST /api/chat/choice-respond (normale Auth)
DB Explorer /api/admin/db/ GET tables, GET tables/{name} (Admin-only)
Info /api/info GET (Build-Zeitstempel + Embedding-Verfügbarkeit)
ServerSettings /api/admin/server-settings GET, PUT (Admin-only, liest/schreibt appsettings.Production.json)

Middleware

AuthMiddleware prüft jede /api/-Anfrage auf ein gültiges gott_token-Cookie.

Ausnahmen (kein Auth nötig):

  • Statische Dateien (nicht /api/)
  • POST /api/auth/login
  • GET /api/auth/autologin (Token-basierter Autologin via Link)
  • POST /api/telegram/webhook/{botId} (authentifiziert via Secret-Header)
  • GET /api/shared/{token} (öffentlicher Zugriff auf geteilte Nachrichten)
  • /api/remote (WebSocket-Endpoint für Remote-Clients)
  • GET /api/images/file/* (Krea-Bilder und Thumbnails, öffentlich)

API-Key Auth (ohne User-Kontext):

  • /api/images/*-Routen akzeptieren einen X-Api-Key-Header. Wenn der Key mit KreaApiKey aus der Config übereinstimmt, wird der Request ohne User-Authentifizierung durchgelassen. Normaler Cookie-Auth funktioniert weiterhin als Fallback.

Bei Erfolg werden UserId und IsAdmin in HttpContext.Items gesetzt (bei API-Key-Auth sind diese nicht gesetzt).

Models

Die Models in Models/ sind reine Daten-Records ohne Business-Logic (Ausnahme: ChatMessage hat Helper-Methoden für Content-Extraktion und UserRecord hat GetEffectivePersona()).

ChatContext ist ein Runtime-Objekt das den vollständigen Kontext eines Chat-Requests hält:

  • Chain-ID, Session-ID, Model-ID
  • Aufgelöster System-Prompt (Text + strukturierte Blöcke)
  • Workspace-Pfad, Tool-Choice (automatisch bestimmt)
  • Prompt-Blöcke (PromptBlocks) – für Reminder-Generierung in der History
  • Referenzen auf Agent und Session Records

Templates

Templates/ enthält Vorlagen die beim Erstellen eines neuen Agents in dessen Memory-Verzeichnis kopiert werden:

  • IDENTITY.md – Name und Grundinfos (Platzhalter {NAME} wird ersetzt)
  • SOUL.md – Persönlichkeit und Verhaltensgrundsätze
  • USER.md – Informationen über den User (global, nicht agent-spezifisch)

Frontend-Architektur

Das Frontend ist eine Single-Page Application ohne Build-Step, komplett in Vanilla JavaScript.

Routing

router.js implementiert ein Hash-basiertes Client-Side-Routing:

Route Page Beschreibung
#/ dashboard.js Session-Übersicht (sortiert nach letzter Chat-Nachricht, Fallback: updated_at)
#/chat/{sessionId} chat-page.js Chat-Ansicht
#/session/{sessionId} session-page.js Session-Einstellungen
#/agent/{agentId} agent-page.js Agent-Editor
#/telegram-bot/{botId} telegram-bot-page.js Telegram-Bot-Editor
#/cronjobs cronjob-page.js Cronjob-Verwaltung
#/profile profile-page.js Profil
#/settings settings-page.js App-Einstellungen
#/admin admin-page.js Benutzerverwaltung (Admin-only)
#/api-keys api-keys-page.js API-Key-Verwaltung + GitHub-Scanner + Event-Scanner (Admin)
#/db-explorer db-explorer-page.js Datenbank-Explorer (Admin-only)
#/server-settings server-settings-page.js Servereinstellungen (Admin-only)
#/login login-page.js Login
#/shared/{token} shared-page.js Öffentliche geteilte Nachricht (kein Auth)

Kern-Module

  • app.js – Initialisierung, Auth-Check, globale Event-Handler
  • api.js – Zentraler API-Client mit fetch()-Wrapper, Auth-Redirect bei 401
  • ui-helpers.js – DOM-Helpers ($, esc, truncate), Toast-System, ScrollDown, Meta-Bar, Button-Builder
  • markdown.js – Markdown-Rendering (renderMarkdown), LaTeX/Code-Schutz, Krea-Gallery-Wrapping
  • cards.js – Card-Builder (buildAgentCard(), buildSessionCard(), buildTelegramBotCard())
  • msg-actions.js – Nachrichten-Aktionen (Delete, Mark, Share, Reply-Button, Timestamp)
  • tool-ui.js – Tool-Boxes (Diffs, Todos, File-Content), Visibility-Logik
  • modals.js – Modale Dialoge (System-Prompt, Command-Output, Krea-Image)
  • stream-ui.js – Stream-Bubble, Review-Streaming-Rendering, addAutoContextBubble() für Auto-Context-Box
  • bubbles.js – Message Bubbles (User, Assistant, Error, Warning, Choice, Review, Attachments)
  • chat.js – SSE-Stream-Handling, sendMessage, Plan-Mode
  • history.js – Chat-History laden, paginieren, rendern, Polling
  • archive.js – Archiv-Loading und -Rendering
  • reply.js – Reply/Quote-Handling (Set, Clear, Format)
  • files.js – File-Upload, Workspace-Refs
  • session.js – Session-State-Management (currentSessionId, currentAgentId, getCurrentAgentId())
  • file-explorer.js – Workspace File-Explorer Panel (Dateien, Ordner erstellen/löschen, Folder-Browser)
  • typing-phrases.js – Zufällige Typing-Indicator-Texte (Wienerisch/Österreichisch)
  • avatar-editor.js – Wiederverwendbares Avatar-Editor-Modal (Upload, Paste, AI-Generate, Scale)
  • commands-ui.js – Custom-Commands UI-Komponente

Wiederverwendbare Card-Builder (cards.js)

  • buildAgentCard(agent) – Erzeugt ein .card DOM-Element für einen Agent (Avatar, Name, Model-Badge, Feature-Badges: autoCommit, useSoul, useIdentity, useUserInfo, useMemory, webSearch, Skills, Public/Fremd). Skills werden inline vom API-Endpunkt mitgeliefert. Wird im Dashboard und im Agent-Picker-Modal (Session-Formular) verwendet.
  • buildSessionCard(session, agent) – Erzeugt ein .card DOM-Element für eine Session (Avatar, Name, Badges: Agent, Kontext, Telegram, Meta: Nachrichten/Archiv/Workspace, Git-Status). Git-Status wird inline vom API-Endpunkt mitgeliefert. Wird im Dashboard und im Session-Picker-Modal (Cronjob-Formular) verwendet.
  • buildTelegramBotCard(bot) – Erzeugt ein .card DOM-Element für einen Telegram Bot (Name, maskierter Token, Session-Count, Public/Fremd-Badges). Wird im Dashboard und im Bot-Picker-Modal (Session-Formular) verwendet.
  • Picker-Modals – Agent- und Session-Auswahl in Formularen (Session-/Cronjob-Seiten) erfolgt über modale Dialoge mit den Card-Buildern statt über Dropdowns.
  • emoji.js – Emoji-Picker mit Kategorien und Suche
  • tooltip.js – Tooltip-System für Hover-Infos
  • title.js – Dynamisches Tab-Titel-Management

Styling

CSS ist in modulare Dateien aufgeteilt mit CSS Custom Properties für Theming:

  • theme.css – Alle Farben, Abstände, Schriften als CSS Variables
  • base.css – Reset und Grundlayout
  • main.css – Sidebar, Content-Area, Responsive Layout
  • chat.css – Chat-Bubbles, Streaming-Animation, Tool-Outputs
  • markdown.css – Rendering von Markdown in Nachrichten
  • mobile.css – Responsive Breakpoints und Anpassungen

Multi-Provider-System (OpenAI-kompatibel)

Neben Anthropic Claude unterstützt Gott drei OpenAI-kompatible Provider. Der Agent-Model-String bestimmt den Provider:

Prefix Provider Base-URL API-Key
(kein) Anthropic Claude api.anthropic.com via agent_api_keys (pro Agent zugewiesen)
nvidia: Nvidia (g4f.space Proxy) g4f.space/api/nvidia/v1 keiner
pollinations: Pollinations text.pollinations.ai/openai keiner
gemini: Google Gemini generativelanguage.googleapis.com/v1beta/openai GeminiApiKey
openai: OpenAI api.openai.com/v1 agent_api_keys, sonst Server-Setting OpenAiApiKey
minimax: MiniMax api.minimax.io/v1 MinimaxApiKey

Model-String-Format

provider:model-id – z.B. nvidia:deepseek-ai/deepseek-r1, gemini:gemini-2.5-flash, pollinations:openai-fast

Modell-Caching

Beim Server-Start werden verfügbare Modelle von den Provider-APIs geladen und in data/models_cache.json gecacht. Bei nachfolgenden Starts wird der Cache verwendet (kein API-Call). Manueller Refresh via POST /api/models/refresh.

OpenAiService

OpenAiService implementiert:

  • Streaming via SSE (StreamChat(), OpenAI-Format: choices[0].delta)
  • Non-Streaming (ChatNonStreaming(), für Telegram, Cronjobs, Sub-Agents – agentic loop mit Tool-Calling)
  • Tool-Calling (identische Tools wie AnthropicService)
  • Agentic Loop (Tool-Results → nächste Iteration)
  • Gemini thought_signature – wird aus extra_content.google.thought_signature gelesen, in DB persistiert (MessageRecord.ThoughtSignature), und beim History-Rebuild in extra_content.google.thought_signature zurückgesendet

Routing

ChatService.ResolveModelId() prüft den Model-String:

  • Enthält : mit bekanntem OpenAI-Provider-Prefix (nvidia:/pollinations:/gemini:/openai:/…) → OpenAiService.StreamChat()
  • glm:-Prefix → Prefix wird gestrippt, Provider="" → Claude-CLI-Pfad mit z.ai-Env-Injection (siehe z.ai / GLM-Provider)
  • Sonst → AnthropicService.StreamChat()

Chat-Pipeline

Der Chat-Flow von User-Input bis zur gerenderten Antwort:

1. User tippt Nachricht + optionale Attachments (Bilder/Dokumente)
   ↓
2. Frontend: POST /api/chat { content, sessionId }
   ↓
3. ChatEndpoint:
   a) User-Message + Attachments in DB speichern
   b) ChatService.PrepareContext() → Agent, Model, System-Prompt, Chain
   c) API-Key auflösen: `GetAgentApiKeyRecord(userId, agentId)` – Anthropic ohne Key → CLI-Fallback oder Fehler, OpenAI ohne Key → OK (keyless Provider)
   d) ChatService.BuildHistoryFromDb() → vollständige Message-History
   e) Model-Routing: Anthropic → AnthropicService.StreamChat() (oder StreamChatViaCli() bei CLI-Modus), OpenAI-Provider (nvidia:/pollinations:/gemini:/openai:) → OpenAiService.StreamChat()
   ↓
4. Agentic Loop (AnthropicService oder OpenAiService):
   ┌─────────────────────────────────────────┐
   │  while (iterations < maxIterations)      │
   │    → Claude API Request (SSE)            │
   │    → Stream-Processing:                  │
   │      • Text-Tokens → SSE an Frontend     │
   │      • Tool-Use → ToolService.Execute()  │
   │      • Tool-Results → nächste Iteration  │
   │    → Messages in DB persistieren         │
   │    → Stop wenn: end_turn / max_iterations│
   │    → CancellationToken bei Abbruch       │
   └─────────────────────────────────────────┘
   ↓
4b. Review-Phase (optional, wenn ReviewAgent konfiguriert):
   ┌─────────────────────────────────────────┐
   │  Nach end_turn des Worker-Agents:        │
   │    → RunReview() mit Review-Agent        │
   │    → Review-Agent prüft (eigene Tools)   │
   │    → [APPROVED] → Fertig                 │
   │    → Feedback → an Worker als User-Msg   │
   │    → Worker korrigiert → erneuter Review  │
   │    → Max Review-Runden → Fertig          │
   └─────────────────────────────────────────┘
   ↓
4c. Finalizer-Phase (optional, wenn FinalizerAgent konfiguriert):
   ┌─────────────────────────────────────────┐
   │  Nach Review (oder direkt nach Worker):  │
   │    → RunFinalizer() mit Finalizer-Agent  │
   │    → Finalizer arbeitet (eigene Tools)   │
   │    → z.B. git commit, deploy, cleanup    │
   │    → Kein Feedback-Loop (einmalig)       │
   └─────────────────────────────────────────┘
   ↓
5. Frontend: SSE Events empfangen und rendern
   • "text" → Markdown parsen, Token für Token anzeigen
   • "tool_*" → Tool-Aufrufe collapsible darstellen
   • "review_*" → Review-Bubbles mit eigenem Styling
   • "done" → Token-Stats, Dauer, Model anzeigen

Abbruch (Stop-Button)

Der Stop-Mechanismus arbeitet auf zwei Ebenen:

1. Expliziter Stop-Endpoint (POST /api/chat/stop):

  • Frontend ruft apiStopChat(sessionId) auf
  • StreamingTracker hält pro Session einen CancellationTokenSource
  • StreamingTracker.Cancel(sessionId) cancelt den Token sofort
  • Der Chat-Handler verknüpft den Stop-Token mit dem Request-Token via CreateLinkedTokenSource

2. CancellationToken-Propagierung:

  1. Frontend: AbortController.abort() → SSE-Fetch abgebrochen + POST /api/chat/stop
  2. StreamingTracker.Cancel() → CancellationTokenSource wird gecancelled
  3. AnthropicService: ThrowIfCancellationRequested() am Loop-Anfang, vor/nach Tool-Execution
  4. ToolService.ExecuteTool(): CancellationToken wird durchgereicht → bei execute wird der Prozess gekillt
  5. WriteSseEvent(): Alle SSE-Writes mit CancellationToken → sofortiger Abbruch
  6. Chain wird mit StopReason = "client_disconnected" finalisiert

Warum zwei Ebenen? Kestrel erkennt einen Client-Disconnect erst beim nächsten Write auf den Response-Stream. Wenn das Backend in einer langen Tool-Ausführung steckt, wird nichts geschrieben – der explizite Stop-Endpoint garantiert sofortigen Abbruch.

Streaming vs Non-Streaming

  • Web-Chat: Streaming via SSE (text/event-stream). Tokens werden in Echtzeit gerendert.
  • Telegram: Non-Streaming – Provider-/Key-Weiche: Anthropic + Key → ChatNonStreaming(); ohne Key + CLI → CLI-Fallback; openai ohne User-Key → Fehler; keyless Provider (nvidia/gemini/minimax/pollinations) → ChatNonStreaming() via Server-Key.
  • Cronjobs: Non-Streaming, identische Provider-/Key-Weiche 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.

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, danach auto. Verhindert dass der Agent nur Text antwortet wenn Tools benötigt werden.
  • auto: Wenn keine Tools verfügbar. Claude entscheidet frei.
  • none: Im /no-tools-Modus.

Pfad-Sicherheit

Alle Dateipfade werden via ResolvePath() aufgelöst und gegen den Workspace/Memory-Ordner geprüft – Path-Traversal-Angriffe werden verhindert.


Agent-System

Ein Agent definiert die Persönlichkeit und Fähigkeiten der AI:

Eigenschaft Beschreibung
Name Anzeigename
Avatar Base64 Data-URL (WebP, 512px) + AvatarSmall (WebP, 256px Thumbnail)
Model sonnet, sonnet-4-5, sonnet-4-6, sonnet-5, opus, opus-3, opus-4-5, opus-4-6, opus-4-7, opus-4-8, opus-5, fable, fable-5, haiku, haiku-4-5
CustomCommands JSON-Array mit Slash-Commands (Label + Prompt)
AutoCommit "" (nichts), "mention" (anmerken – nur auf User-Request), "enforce" (erzwingen – nach jeder Änderung), "forbid" (strikt verboten)
UseSoul SOUL.md in System-Prompt einbinden
UseIdentity IDENTITY.md in System-Prompt einbinden
UseUserInfo User-Persona in System-Prompt einbinden
UseMemory Memory-Tools aktivieren
WebSearchVersion Web-Suche aktivieren (z.B. web_search_20260209)
AllowUserChoice ask_user_choice-Tool verfügbar (Default: true)
TaskInstruction Aufgabe/Anweisung – optionaler Text, wird als ## Anweisungen-Block in System-Prompt eingefügt
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; bei z.ai/GLM nur ab GLM 5.2 durchgereicht (sonst entfernt). Ultracode hebt es auf xhigh an.
MaxOutputTokens Hartes Limit der Ausgabelänge in Tokens. 0 = aus (Modell-Default bzw. API-Default 16384). CLI: via Env-Var CLAUDE_CODE_MAX_OUTPUT_TOKENS. API: als max_tokens im Payload. Wirkt auch über z.ai/GLM.
DisableThinking Reasoning/Thinking komplett aus. CLI: CLAUDE_CODE_DISABLE_THINKING=1 + MAX_THINKING_TOKENS=0, und CLAUDE_CODE_EFFORT_LEVEL wird entfernt (Explizit-Aus schlägt Effort). API-Pfad: kein Äquivalent → still 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
"sonnet-5"        → claude-sonnet-5
"opus"            → claude-opus-4-8
"opus-3"          → claude-opus-3
"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
"opus-5"          → claude-opus-5
"fable"           → claude-fable-5
"fable-5"         → claude-fable-5
"haiku"           → claude-haiku-4-5
"haiku-4-5"       → claude-haiku-4-5
(default)         → claude-sonnet-5

1-Million-Token-Context ist seit März 2026 Default bei Opus 4.6/4.7/4.8 + Sonnet 4.6 – kein separater Beta-Header und kein [1m]-Suffix mehr nötig. Alte DB-Einträge mit [1m]-Suffix werden bei Startup automatisch migriert (UPDATE agents/chains SET model = REPLACE(model, '[1m]', '')).


System-Prompt-Aufbau

Der SystemPromptBuilder erzeugt den System-Prompt modular aus konfigurierbaren Blöcken:

┌──────────────────────────────────────────┐
│ 1. IDENTITY.md (UseIdentity)             │  → Wer ist der Agent?
│ 2. SOUL.md (UseSoul)                     │  → Persönlichkeit, Verhalten
│ 3. Anweisungen (TaskInstruction)         │  → Aufgaben-/Anweisungstext (optional)
│ 4. User-Persona (UseUserInfo)            │  → Infos über den User
│ 5. Workspace-Info                        │  → Tools + Workspace-Pfad
│ 6. Rückfragen (AllowUserChoice)          │  → Hinweis zu ask_user_choice Tool
│ 7. Session-Kontext                       │  → Freiformtext
│ 8. Skills (Agent-Skills)                 │  → Skill-Prompts
│ 9. Memory (UseMemory)                    │  → Memory-Regeln + MEMORY.md Inhalt
│10. Git-Verhalten (AutoCommit)            │  → Commit-Regeln
│11. Markierte Nachrichten                 │  → Vom User als wichtig markierte Messages
│    [Plan-Modus-Block]                    │  → Nur wenn planMode=true (überspringt 10)
│    [Preparer-Instruktionsblock]          │  → Nur für Preparer-Agents
└──────────────────────────────────────────┘

Jeder Block hat:

  • Icon – Font Awesome Icon-Klasse
  • Label – Beschreibender Titel
  • Content – Markdown-Text

Die Blöcke sind über die API als JSON abrufbar (GET /api/sessions/{id}/system-prompt-blocks), sodass das Frontend sie einzeln inspizierbar darstellen kann.


Memory-System

Jeder Agent hat ein eigenes Dateisystem-basiertes Gedächtnis:

data/agents/{agentId}/
├── IDENTITY.md      # Identität (aus Template initialisiert)
├── SOUL.md          # Persönlichkeit (aus Template initialisiert)
├── MEMORY.md        # Haupt-Memory (kuratierte Informationen)
└── memory/          # Unterordner für Tages-Logs, Themen
    ├── YYYY-MM-DD.md
    └── ...

Funktionsweise

  1. Initialisierung: Beim Erstellen eines Agents werden IDENTITY.md und SOUL.md aus Templates/ kopiert
  2. Lesen: Der Agent kann seine Memory-Dateien via memory_read Tool lesen
  3. Schreiben: Via memory_write (überschreiben oder append)
  4. Auflisten: Via memory_list alle Dateien durchsuchen
  5. System-Prompt: MEMORY.md Inhalt wird automatisch in den System-Prompt eingebettet
  6. IDENTITY.md und SOUL.md: Werden als eigene System-Prompt-Blöcke eingebunden (wenn aktiviert)

Sandbox

Alle Pfade werden gegen das Agent-Verzeichnis geprüft – kein Zugriff außerhalb möglich.


Skill-System

Skills sind Dateisystem-basierte Erweiterungen die einem Agent zugeordnet werden können:

data/skills/{slug}/
├── SKILL.md          # Metadaten (Name, Description, Version im Frontmatter)
├── PROMPT.md         # System-Prompt-Erweiterung (in Agent-Prompt eingebettet)
├── _meta.json        # Alternative Metadaten (Fallback)
└── output/           # Skill-Output (Bilder, Dateien, etc.)

SKILL.md Frontmatter

---
name: Mein Skill
description: Beschreibung
version: 1.0
---

Agent-Zuordnung

Skills werden über die agent_skills Tabelle einem Agent zugeordnet. Der PROMPT.md-Inhalt aller zugeordneten Skills wird als ein System-Prompt-Block eingefügt.

Workspace-Implikation

Wenn ein Agent Skills hat aber kein Workspace konfiguriert ist, wird automatisch /tmp als Workspace gesetzt (damit execute für Skill-Commands funktioniert).


Telegram-Integration

Telegram Bots sind eigenständige Entitäten mit einer eigenen Tabelle (telegram_bots), die am Dashboard verwaltet werden (wie Agents, Sessions, Cronjobs). Ein Bot kann mehreren Sessions zugewiesen werden – jede Session hat ihren eigenen Chat-Kontext.

Datenmodell

Telegram Bot (telegram_bots):

  • BotId – UUID
  • UserId – Owner
  • Name – Anzeigename
  • BotToken – Bot-Token von @BotFather
  • WebhookSecret – Einmalig generierter Token für Webhook-Verifizierung
  • IsPublic – Andere User können den Bot in ihren Sessions verwenden (aber nicht bearbeiten)

Session-Felder:

  • TelegramBotId – Verweis auf den zugewiesenen Bot (wie AgentId)
  • TelegramChatId – Verknüpfte Chat-ID (1 Session = max. 1 Telegram-Chat)

Webhook-Flow

1. User erstellt Telegram Bot im Dashboard (Name + Token)
2. User registriert Webhook auf der Bot-Page
   → Setzt Webhook-URL: {baseUrl}/api/telegram/webhook/{botId}
3. User weist Bot einer Session zu (Session-Settings, Bot-Picker)
4. Telegram sendet Updates an Webhook
5. AuthMiddleware lässt Webhook-Requests durch (kein Cookie nötig)
6. TelegramEndpoint prüft X-Telegram-Bot-Api-Secret-Token Header
7. TelegramService verarbeitet Update:
   - Bot laden via botId
   - Chat-ID extrahieren
   - Session suchen: telegram_bot_id = botId AND telegram_chat_id = chatId
   - /start → Auto-Link (wenn genau 1 unverknüpfte Session) oder Chat-ID anzeigen
   - /clear → History archivieren
   - /status → Session-Info anzeigen
   - Text → An Claude weiterleiten (Non-Streaming)

Bei /start oder einer normalen Nachricht, wenn keine Session für den Chat verknüpft ist:

  1. Suche Sessions mit diesem Bot, die noch keine Chat-ID haben
  2. Genau 1 gefunden → Automatisch verknüpfen + Bestätigungsnachricht
  3. 0 oder mehrere gefunden → "Deine Chat-ID ist X, bitte im Web-UI zuweisen"

Unlink ist nur über das Web-UI möglich (kein Telegram-Befehl).

Non-Streaming Chat

Telegram-Nachrichten werden Provider-aware verarbeitet: Anthropic + API-Key → ChatNonStreaming(); Anthropic ohne Key + CLI verfügbar → CLI-Fallback; openai ohne User-Key → Fehler; keyless Provider (nvidia/gemini/minimax/pollinations) → OpenAiService.ChatNonStreaming() via Server-Key (ResolveProviderConfig). 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-/Key-Weiche: Anthropic + API-Key → `ChatNonStreaming()`; Anthropic ohne Key + CLI verfügbar → CLI-Fallback; `openai` ohne User-Key → Fehler; keyless Provider (nvidia/gemini/minimax/pollinations) → `ChatNonStreaming()` via Server-Key (`ResolveProviderConfig`)
   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, batchSize, ... }
2. KreaService sendet pro gewünschtem Bild EINE eigene Anfrage an die Krea API
   (Krea unterstützt kein 'batchSize' mehr – batchSize=N (1–4) steuert nur,
    wie viele Einzel-Requests in einer Schleife gesendet werden)
3. Job-Polling pro Bild bis Status "completed"
4. Bilder herunterladen nach data/images/
5. Metadaten als JSON speichern (batch_size bleibt informativ erhalten)
6. Response mit Dateinamen + URLs

Bild-Serving

  • GET /api/images – Liste aller generierten Bilder (aus Metadaten-JSON)
  • GET /api/images/file/{filename} – Einzelnes Bild ausliefern (ohne Auth)
  • GET /api/images/file/{filename}/thumb – Thumbnail (320px WebP, gecacht in data/cache/images/, ohne Auth)
  • GET /api/images/status – Generierungsstatus abfragen

Bild-Dateien und Thumbnails sind ohne Authentifizierung abrufbar (für externe Einbindung).


Auth & Multi-User

Authentifizierung

  • Login: Username + Passwort → SHA256 Hash-Vergleich → Token generieren
  • Token: 32 Bytes Random, als gott_token HttpOnly Cookie (365 Tage)
  • Validierung: Jeder API-Call prüft Token → User-ID
  • Logout: Token aus DB löschen + Cookie entfernen

Multi-User-Isolation

  • Sessions sind an user_id gebunden
  • Agents sind an user_id gebunden
  • Cronjobs sind an user_id gebunden
  • Jeder User sieht nur seine eigenen Daten
  • Admin-Flag (is_admin) für Backup-Endpoint, Admin-Benutzerverwaltung, Impersonate

Default-Path (Base-Path)

Jeder User hat einen optionalen DefaultPath – den Basis-Ordner für alle Workspaces. Wird nur vom Admin gesetzt.

  • Session-Workspace: Speichert nur den relativen Pfad zum DefaultPath (z.B. myapp statt /home/user/projects/myapp)
  • Auflösung: WorkspaceHelper.ResolveWorkspace() kombiniert DefaultPath + relativen Workspace → absoluter Pfad
  • System-Prompt: Bekommt den aufgelösten absoluten Pfad, damit der Agent korrekt navigieren kann
  • Path-Traversal-Schutz: .. in relativen Workspace-Pfaden wird blockiert (400 Bad Request)
  • Legacy-Support: Absolute Pfade in session.Workspace funktionieren weiterhin als Fallback
  • Folder-Browser: Startet beim DefaultPath, User kann nicht über den Base-Path hinaus navigieren

Default-User

Beim ersten Start wird automatisch ein Default-User erstellt (DatabaseService.EnsureDefaultUser()).

User-Persona

Jeder User hat eine optionale Persona – ein Freitext der beschreibt wer er ist. Diese wird (wenn UseUserInfo am Agent aktiv ist) in den System-Prompt als "Dein Gegenüber" eingebettet.


Admin-Benutzerverwaltung

Admins können alle User verwalten über AdminEndpoint.cs (alle Endpoints erfordern is_admin).

API-Endpunkte

Methode Route Beschreibung
GET /api/admin/users Alle User auflisten
POST /api/admin/users Neuen User erstellen
PUT /api/admin/users/{id} User bearbeiten (Name, Username, Admin, DefaultPath)
PUT /api/admin/users/{id}/password Passwort zurücksetzen
DELETE /api/admin/users/{id} User löschen (+ Tokens, Shared Messages)
POST /api/admin/users/{id}/impersonate Als anderer User einloggen (Login-as-User)
POST /api/admin/users/{id}/login-link Autologin-Token generieren → URL zurückgeben

Impersonate (Login-as-User)

Admins können sich als beliebiger User einloggen ohne dessen Passwort zu kennen:

  1. POST /api/admin/users/{id}/impersonate → Erstellt Token für Zieluser
  2. Setzt neuen gott_token-Cookie → Admin-Session wird zum Zieluser
  3. location.reload() → Frontend lädt mit neuem User-Kontext

Admins können für jeden User einen Autologin-Link generieren – z.B. für Setup oder Support:

  1. POST /api/admin/users/{id}/login-link → Erstellt Token, gibt { token } zurück
  2. Frontend baut URL: {window.location.origin}/api/auth/autologin?token={token}
  3. Link wird im Modal angezeigt mit Copy-Button
  4. GET /api/auth/autologin?token={token} → Setzt Cookie + Redirect zu / (ohne Auth, wie Login)

Frontend (admin-page.js)

  • Erreichbar über Dashboard-Link "Benutzerverwaltung" (nur für Admins sichtbar)
  • User-Liste als Cards mit Edit/Delete/Login-Link-Buttons
  • Inline-Formular für Create/Edit
  • Login-Link-Modal mit Copy-Funktion
  • Sich selbst löschen ist nicht möglich

Multi-Agent System (Preparer + Review + Finalizer)

Das Multi-Agent-System ermöglicht bis zu vier Agents pro Session: einen Worker, einen optionalen Vorbereiter (Preparer), einen optionalen Reviewer und einen optionalen Finalizer.

Multi-Modell-Panel (Fusion / MoA): Orthogonal dazu kann eine Session zusätzlich ein Panel aus mehreren Agenten haben (multi_model_mode="fusion"/"moa", panel_agent_ids=CSV, moa_layers=Proposer-Runden bei MoA). Bei jeder Nachricht antworten alle Panel-Agenten parallel (read-only) und streamen live (CLI: TextDelta/ToolStart/ToolResult; Anthropic-API: pro Tool-Runde; OpenAI: one-shot); ihre Antworten werden dem Worker als <panel_responses>-Block vorgelegt, der eine Gesamtantwort synthetisiert und normal streamt (OpenRouter-Fusion-/MoA-Muster). MoA = mehrere Proposer-Runden: ab Runde 2 sieht jeder Member die Vorrunde (<previous_layer_responses>) und verfeinert; RunPanel(layers) ist EINE Engine (Fusion = layers==1). Alle Runden werden persistiert (F5-sicher); JSON-Transkript pro Member (layer/totalLayers/Segmente, Iteration = layer*100+index+1). Member-Tokens via seedUsage in Worker-usage gefaltet (Live-Badge + DB, kein separates Panel-Usage-Event). Backend: RunPanel() in AnthropicService.Panel.cs (im ChatEndpoint-try vor dem Worker-Dispatch), Member-Provider-Weiche (Anthropic read-only via PlanMode, CLI via --permission-mode plan, OpenAI tool-los via CompleteText), Proposals als [Panel: Name]-Messages mit Tag="panel", Write-Lock in SseHelper serialisiert parallele SSE-Flushes. SSE: panel_start/panel_layer_start/panel_member_start/panel_member_text/panel_member_tool*/panel_member_done/panel_layer_done/panel_done. Der CLI-Parser filtert Subagent-Interna zentral per parent_tool_use_id-Guard (ClaudeCliService.RunCliStreamingAsync) → keine namenlosen Tool-Boxen im Stream oder in der History. Das panel_member_done-Event und das persistierte JSON tragen per-Member Token-Felder (inputTokens/outputTokens/cacheReadTokens/cacheCreationTokens); buildPanelMemberMeta() rendert daraus ein .panel-member-meta-Badge (Live + History, gleicher Helper). Composer-Toggle-PATCHes laufen serialisiert über die _modePersist-Kette, die sendMessage abwartet → kein MoA-Timing-Bug (Modus garantiert committed, bevor das Backend die Session lädt). Frontend: .panel-bubble pro Member (gekeyt layer_index), .panel-layer-divider bei MoA. Siehe ARCHITECTURE_QUICK.md.

Architektur

User-Message
  → Preparer-Phase (optional):
      → Preparer-Agent bekommt: User-Message + History (inkl. eigener vorheriger Messages)
      → Preparer arbeitet (kann Tools nutzen: read_file, execute etc.)
      → Erledigt Vorarbeit mit seinen Fähigkeiten (z.B. JSON bauen, Daten aufbereiten)
      → [QUESTION] → Rückfrage an User, Flow stoppt, User muss erneut eingeben
      → Antwort ohne [QUESTION] → automatisch weiterleiten an Worker
  → Worker-Agent arbeitet (Tools, Iterationen) → end_turn
  → Review-Phase (optional):
      → Review-Agent bekommt: Konversation als Kontext
      → Review-Agent prüft (kann Tools nutzen: read_file, execute etc.)
      → [APPROVED] → Fertig, Antwort an User
      → Feedback-Text → wird als User-Message an Worker-Agent eingefügt
      → Worker-Agent korrigiert → end_turn → Review-Agent prüft erneut
      → Max Review-Runden erreicht → Fertig (mit letzter Worker-Antwort)
  → Finalizer-Phase (optional):
      → Finalizer-Agent bekommt: gesamte Konversation
      → Finalizer arbeitet (eigene Tools, z.B. git commit, deploy)
      → Kein Feedback-Loop – einmalige Ausführung
      → Fertig

Konfiguration

Auf Session-Ebene (nicht Agent-Ebene):

DB-Feld SessionRecord Beschreibung
preparer_agent_id PreparerAgentId ID des Vorbereiter-Agents (optional, null = kein Preparer)
review_agent_id ReviewAgentId ID des Review-Agents (optional, null = kein Review)
max_review_iterations MaxReviewIterations Max. Review-Runden (Default: 3)
finalizer_agent_id FinalizerAgentId ID des Finalizer-Agents (optional, null = kein Finalizer)

Vorbereiter-Agent (Preparer)

Der Preparer wird vor dem Worker-Agent aufgerufen. Er analysiert die User-Anfrage, kann Vorarbeit leisten (z.B. Daten aufbereiten, JSON bauen, Code analysieren) und gibt das Ergebnis an den Worker weiter. Bei unklaren Anfragen stellt er Rückfragen.

Logik-Pattern: Default = Forward

Die Logik ist invertiert gegenüber dem Review-Pattern: Jede Antwort wird automatisch weitergeleitet, außer sie enthält explizit [QUESTION]. Das ist robuster als ein Pflicht-Tag wie [FORWARD], weil LLMs dazu neigen, Tags zu vergessen.

Antwort Ergebnis
Ohne [QUESTION] Automatisch weitergeleitet (ShouldForward = true)
Mit [QUESTION] Rückfrage an User, Flow stoppt (ShouldForward = false)

Backend-Flow (Preparer)

  1. ChatEndpoint – Prüft ob ctx.Preparer vorhanden
  2. AnthropicService.RunPreparer() – Provider-Weiche: Anthropic → RunAgentChat(), OpenAI → OpenAiService.ChatNonStreaming() mit agentType = "preparer"
  3. Entscheidung – ShouldForward prüft ob [QUESTION] im Text:
    • Forward: Aufbereiteter Text wird als [Vorbereitet von AgentName]:-User-Message in DB gespeichert, History wird neu geladen, StreamChat startet
    • Rückfrage: SSE preparer_done mit forwarded: false, Stream endet mit stopReason: "preparer_query", User muss erneut eingeben

Preparer-History

Der Preparer bekommt seine eigene History (BuildHistoryFromDb(sessionId, includeTag: "preparer")), damit er seine vorherigen Rückfragen und User-Antworten sieht. Der Session-Agent bekommt die History ohne Preparer-Messages (Tag-Filter).

System-Prompt

Der Preparer bekommt einen speziellen Instruktionsblock an seinen System-Prompt angehängt:

  • Erklärt die Rolle als Vorbereiter
  • Definiert [QUESTION]-Mechanismus für Rückfragen
  • Instruiert aktive Vorarbeit mit eigenen Fähigkeiten und Tools
  • Betont: Antworten werden automatisch weitergeleitet

SSE-Events (Preparer)

Event Daten Beschreibung
preparer_start { agent: "Name" } Preparer-Phase beginnt
preparer_text { text: "..." } Preparer-Agent-Text (Token-Stream)
preparer_tool_use { tool: "name", input: "..." } Preparer nutzt Tool
preparer_done { forwarded: true/false } Preparer-Phase beendet

Backend-Flow (Review)

  1. ChatService.PrepareContext() – Wenn session.ReviewAgentId gesetzt:

    • Review-Agent aus DB laden
    • Eigenen SystemPrompt für den Review-Agent bauen (Identity, Soul, Memory, Skills)
    • SubAgentContext mit ModelId, Provider, SystemPrompt, Workspace, ToolChoice, Agent erstellen
  2. ChatEndpoint – Übergibt ctx.Review an AnthropicService.StreamChat()

  3. AnthropicService.StreamChat() – Nach end_turn des Worker-Agents:

    • Prüft ob Review-SubAgentContext vorhanden und reviewRound < MaxReviewIterations
    • Ruft RunReview() auf
  4. RunReview() – Orchestriert eine einzelne Review-Runde:

    • Baut Review-Messages auf: Konversation als Context + Prüfanweisung
    • SSE-Event review_start an Client senden
    • Ruft RunAgentChat() auf (generische Methode für Review/Finalizer, eigener API-Call an Claude)
    • Review-Agent kann Tools nutzen (eigener Tool-Loop mit max. 20 Iterationen pro Review-Run)
    • SSE-Events review_text und review_tool_use für Live-Streaming
    • Prüft ob Antwort [APPROVED] enthält:
      • Ja → SSE review_approved, return HasFeedback = false
      • Nein → SSE review_feedback, return HasFeedback = true + Feedback-Text
  5. Zurück in StreamChat() – Bei Feedback:

    • Feedback als User-Message in DB speichern (mit [Review-Feedback von AgentName (Runde X/Y)] Prefix)
    • Worker-Agent bekommt Feedback als nächste User-Message
    • continue → nächste while-Loop-Iteration → Worker korrigiert → Review erneut
    • reviewRound++ bis MaxReviewIterations erreicht

SubAgentContext (Model)

// 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):

  • renderMarkdown parst [Review: AgentName] → styled Header mit Icon
  • renderMarkdown parst [Review-Feedback von AgentName (...)] → styled Feedback-Header
  • renderMarkdown filtert [APPROVED] aus (wird ausgeblendet)
  • History erkennt Review-Nachrichten und rendert sie als .review-bubble statt normaler Bubbles
  • Review-Nachrichten werden nicht mit normalen Nachrichten gemergt

Shared-Page (shared-page.js):

  • Gleiche Review-Bubble-Erkennung für geteilte Nachrichten

Finalizer-Agent

Der Finalizer wird nach dem Review (oder direkt nach dem Worker, falls kein Review-Agent konfiguriert) aufgerufen. Er führt abschließende Arbeiten durch (z.B. Git-Commit, Deployment, Cleanup) – ohne Feedback-Loop.

Konfiguration

DB-Feld SessionRecord Beschreibung
finalizer_agent_id FinalizerAgentId ID des Finalizer-Agents (optional, null = kein Finalizer)

Backend-Flow

  1. ChatService.PrepareContext() – Wenn session.FinalizerAgentId gesetzt:

    • Finalizer-Agent aus DB laden
    • SubAgentContext aufbauen (Model, Provider, SystemPrompt, Workspace, ToolChoice)
  2. AnthropicService.StreamChat() – Nach Review-Phase (oder direkt nach Worker):

    • Prüft ob Finalizer-SubAgentContext vorhanden
    • Ruft RunFinalizer() auf
  3. RunFinalizer() – Baut Messages auf (gesamte Konversation + Finalisierungs-Anweisung), Provider-Weiche wie bei RunPreparer/RunReview

  4. RunAgentChat() / ChatNonStreaming() / RunSubAgentViaCli() – Provider-Weiche für Preparer/Review/Finalizer:

    • Anthropic mit API-Key: Eigener API-Call an Claude mit Agent-Config (RunAgentChat, Streaming + Tool-Loop)
    • Anthropic ohne API-Key + CLI verfügbar: RunSubAgentViaCli() – nutzt ClaudeCliService.ChatStreamingAsync(), sammelt Text, sendet identische SSE-Events ({type}_text, {type}_tool_use, {type}_done) und persistiert die Message mit Tag = agentType
    • Anthropic ohne API-Key + ohne CLI: Sub-Agent wird übersprungen (kein Hard-Error)
    • OpenAI-kompatibel: OpenAiService.ChatNonStreaming() (Non-Streaming agentic loop mit Tool-Calling)
    • Tool-Loop mit SSE-Events ({type}_text, {type}_tool_use)
    • Antwort als Message in DB persistieren (mit Tag = agentType, wird aus API-History gefiltert)

SSE-Events

Event Daten Beschreibung
finalizer_start { agent: "Name" } Finalizer-Phase beginnt
finalizer_text { text: "..." } Finalizer-Agent-Text (Token-Stream)
finalizer_tool_use { tool: "name", input: "..." } Finalizer nutzt Tool
finalizer_done {} Finalizer-Phase beendet

Frontend

  • Session-Page: Vierter Agent-Picker: "Vorbereiter-Agent", "Review-Agent", "Finalizer-Agent" im "Weitere Einstellungen"-Bereich
  • Chat-UI: Preparer-Bubbles (.preparer-bubble, Info-Farbe, 🔍-Icon), Finalizer-Bubbles (.finalizer-bubble, grüne Farbe, 🏁-Icon)
  • History: [Preparer: AgentName] → styled Header mit fa-filter Icon, [Vorbereitet von AgentName] → styled Forwarded-Header, [Finalizer: AgentName] → styled Header, [FORWARD]/[QUESTION] werden ausgeblendet
  • Dashboard: 🏁-Badge auf Session-Cards mit Finalizer

Interaktive Rückfragen (ask_user_choice)

Das ask_user_choice-Tool ermöglicht dem Agent, dem User klickbare Auswahlmöglichkeiten zu präsentieren, wenn es mehrere sinnvolle Wege gibt oder eine Entscheidung vom User getroffen werden muss.

Tool-Definition

{
  "name": "ask_user_choice",
  "description": "Stellt dem User eine Frage mit Auswahlmöglichkeiten",
  "input_schema": {
    "properties": {
      "question": { "type": "string" },
      "choices": { "type": "array", "items": { "type": "string" }, "minItems": 2, "maxItems": 5 }
    }
  }
}

Agent-Konfiguration

  • allow_user_choice (AgentRecord) – Boolean, Default true. Steuert ob das Tool verfügbar ist.
  • Dashboard-Badge: 🔵 Rückfragen wenn aktiv
  • System-Prompt-Hinweis: "Nutze ask_user_choice sparsam – nur wenn die Entscheidung vom User getroffen werden soll"
  • Bei Review/Finalizer/Cronjobs: Immer deaktiviert (kein User zum Fragen)

Backend-Flow

  1. Claude ruft ask_user_choice auf → Backend erkennt im ProcessStream
  2. Tool wird nicht ausgeführt – stattdessen SSE-Event user_choice gesendet
  3. stopReason wird auf "awaiting_user_choice" gesetzt (Guard-Clause verhindert Überschreibung durch message_delta)
  4. tool_use-Block wird in DB gespeichert, Chain wird finalisiert
  5. User wählt Option → Frontend sendet POST /api/chat mit choiceToolCallId + choiceResult
  6. Backend findet die Chain mit dem offenen tool_use, speichert tool_result dort
  7. Neuer StreamChat startet – History enthält tool_use + tool_result, Claude arbeitet weiter

Frontend-Flow

  • Live: SSE-Event user_choice → addChoiceBubble() rendert Frage + klickbare Buttons
  • History: tool_use mit toolName === 'ask_user_choice' → Voraus-Suche nach tool_result:
    • Gefunden: addChoiceBubbleStatic() mit ausgewählter Option (hervorgehoben, andere ausgegraut)
    • Nicht gefunden: Aktive Buttons (klickbar, z.B. nach Page-Refresh)
  • Robustheit: Guards gegen choices als String statt Array (Array.isArray() Check)
  • CSS: .choice-bubble, .choice-btn (Primary-Color-Outline), .choice-selected/.choice-unselected

SSE-Events

Event Daten Beschreibung
user_choice { toolCallId, question, choices[] } Rückfrage mit Auswahloptionen

Plan-Modus

Der Plan-Modus ist ein Read-only-Betriebsmodus, in dem der Agent den Code analysieren, Rückfragen stellen und Pläne erstellen kann – aber keine Dateien ändern oder Befehle ausführen darf.

Aktivierung

  • Frontend: Toggle-Button (🗺️ fa-map) im Chat-Input-Bereich
  • API: planMode: true im ChatRequest
  • Pro Chain: plan_mode Feld in der Chain (rein informativ für History-Anzeige)
  • Kein Agent-Setting – wird pro Nachricht vom User gesteuert

Backend-Auswirkungen

Aspekt Normal Plan-Modus
Tools Alle (read_file, write_file, edit_file, delete_file, execute, ...) Nur read-only: read_file, list_files, glob, grep, execute, todo_write, memory_*, ask_user_choice
System-Prompt Normal + Plan-Modus-Block: "Du darfst NUR lesen und analysieren, KEINE Änderungen vornehmen"
toolChoice "any" (automatisch wenn Workspace/Skills/Memory) Immer "auto" (Claude darf frei antworten)
Chain-Speicherung plan_mode = 0 plan_mode = 1

System-Prompt-Anpassungen im Plan-Modus

Folgende Blöcke werden übersprungen: Git-Commits. Der Tools-Block listet nur Read-only-Tools auf. Skills werden als "nur zur Kenntnis, nicht ausführbar" markiert.

Frontend

  • Toggle: #planModeBtn öffnet ein Dropdown-Menü (_COMPOSITE_MODES: Normal/Plan/Ultracode/Fusion/MoA). Alle Modi sind transient (pro Request, nicht persistiert, Reset beim Chat-Wechsel) und werden als Flag mitgeschickt (planMode/ultracode/multiModelMode). In den Session-Einstellungen werden nur noch die Panel-Mitglieder (panelAgentIds) + moaLayers festgelegt (persistent); der Panel-Modus selbst kommt on-demand aus dem Request.
  • 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-Modus bei Chains die im Plan-Modus erstellt wurden
  • System-Prompt-Modal: Zeigt den angepassten System-Prompt wenn Plan-Modus aktiv
  • Session-Wechsel: Plan-Modus wird zurückgesetzt

Choice-Response im Plan-Modus

Wenn im Plan-Modus eine ask_user_choice-Rückfrage kommt und der User antwortet, erbt die Choice-Response den planMode des vorherigen Chains.


Ultracode & Workflow-Status

Ultracode ist ein Composite-Modus, der eine einzelne Anfrage als nativen dynamischen Workflow der Claude CLI ausführt: Claude fächert die Arbeit über mehrere gespawnte Subagenten parallel auf, lässt Befunde gegenprüfen und synthetisiert am Ende. Wie Plan-/Fusion-/MoA-Modus wird Ultracode on-demand pro Request über das Composer-Dropdown (#planModeBtn, _COMPOSITE_MODES) gewählt – nicht am Agent/an der Session persistiert.

Aktivierung & Backend-Auswirkungen

  • API: transientes Flag ultracode: true im ChatRequest (ChatModels.cs, Ultracode)
  • Hint: Bei aktivem Ultracode wird an jede User-Nachricht ein <hint> angehängt ("Bearbeite diese Anfrage als dynamischen Workflow – use a workflow ... Nutze dafür das Workflow-Tool")
  • Nur CLI-Pfad / nur Claude: Ultracode greift ausschließlich im Claude-CLI-Pfad (StreamChatViaCli). Es ist nur für Claude-Modelle aktiv – nicht für z.ai/GLM (kein Workflow-Runtime) und nicht im Plan-Modus.
  • Effort: Ultracode hebt das Reasoning-Effort auf xhigh an (bzw. max, wenn der Agent bereits max setzt) – via Env-Var CLAUDE_CODE_EFFORT_LEVEL
  • Allowed-Tools: Im CLI-Aufruf (ClaudeCliService.ChatStreamingAsync) werden zusätzlich Workflow (die nativen dynamischen Workflows) und Task (die vom Workflow gespawnten Subagenten) zu --allowed-tools hinzugefügt. Ohne diese Freigabe lehnt die Permission-Engine das Workflow-Tool headless still ab (kein interaktiver Gate im -p-Modus).

Workflow-Status

Dynamische Ultracode-Workflows laufen als detachter Hintergrund-Task mit eigenem Transcript-Verzeichnis – ihre Subagent-Events fließen nicht durch den Haupt-(-p)-Stream (anders als klassische Task-Subagenten, die mit gesetztem parent_tool_use_id inline im Parent-Stream ankommen). Die echte Telemetrie liegt daher in zwei Dateien des CLI-Transcripts und wird on demand gelesen, nicht aus dem Stream akkumuliert. Der Workflow-Status macht diese Telemetrie live und nachträglich abrufbar.

Backend:

  • Capture (ClaudeCliService.TapWorkflowLaunch) – Im tool_result-Pfad: Enthält der Inhalt "Workflow launched", parst die Methode die Zeile Transcript dir: <absoluter Pfad> und ruft _workflowTracker.Register(...). Rein additiv, wirft nie. Der parent_tool_use_id-Guard in ClaudeCliService.RunCliStreamingAsync blendet klassische Subagent-Events weiterhin per continue aus dem sichtbaren Stream aus – tappt sie aber nicht mehr ab.
  • Registry (WorkflowProgressTracker) (Singleton, registriert in Program.cs) – ist nur eine Registry, kein Stream-Akkumulator. Speichert pro Session einen WorkflowRunRef (ChainId, RunId, JournalPath, RichJsonPath, StartedAt), abgeleitet aus der Transcript-Dir. Methoden: Register / Get / ChainIdFor / End. In-Memory pro Session, defensiv.
  • Reader (WorkflowTelemetryReader, statisch) – liest die Telemetrie aus zwei Dateien:
    • Live: <transcriptDir>/journal.jsonl (append-only; pro Subagent started/result-Zeilen). ReadLive ⇒ status: "running", subagentCount (= Anzahl started), completedCount (= Anzahl result), durationMs. Keine Tokens/Tool-Calls live verfügbar.
    • Abgeschlossen: <proj>/workflows/<runId>.json (wird erst am Lauf-Ende geschrieben). ReadCompleted ⇒ status: "completed", workflowName, summary, durationMs, phases[] (index/title/detail), agents[] (index/label/phaseTitle/state/model/tokens/toolCalls/durationMs/resultPreview), totalTokens, totalToolCalls, subagentCount, completedCount.
    • Read() bevorzugt die reiche, fertige <runId>.json und fällt sonst auf das Live-journal.jsonl zurück.
  • Persistierung: Spalte chains.workflow_info (TEXT, via TryAddColumn in DatabaseService.cs; geschrieben in DatabaseService.Sessions.cs UpdateChain), Feld ChainRecord.WorkflowInfo. Beim FinalizeChain (AnthropicService.cs) wird die reiche WorkflowProgress-JSON in die Chain serialisiert und der Tracker-Eintrag freigegeben – mit ChainId-Abgleich (ChainIdFor), damit ein anderer Finalize-Pfad derselben Session (z.B. ein Panel-Member) den Workflow-Eintrag nicht fälschlich abräumt.
  • Endpoint: GET /api/sessions/{id}/workflow-status (SessionEndpoint.HandleWorkflowStatus) liefert in dieser Reihenfolge: (1) ist für die Session ein Lauf registriert → Telemetrie aus den Transcript-Dateien lesen (running aus journal.jsonl / completed aus <runId>.json); (2) sonst die jüngste aktive Chain mit nicht-leerem workflow_info → status: "completed"; (3) sonst status: "none", workflow: null. JSON-Shape: running/completed → { sessionId, streaming, status, chainId, workflow }; none → { sessionId, streaming, status: "none", workflow: null } (kein chainId im none-Zweig).

Felder im workflow-Objekt (camelCase, via WriteAsJsonAsync): status, chainId, workflowName, summary, startedAt, subagentCount, completedCount, totalTokens, totalToolCalls, durationMs, phases[] (index, title, detail), agents[] (index, label, phaseTitle, state, model, tokens, toolCalls, durationMs, resultPreview).

Frontend:

  • Header-Menü-Eintrag "Workflow-Status" (Button #workflowStatusBtn in index.html, direkt nach "Payload anzeigen")
  • API-Wrapper apiGetWorkflowStatus(sessionId) in js/api.js
  • showWorkflowStatusModal(sessionId) in js/modals.js – rendert Status-Head, Summen-Kacheln (Subagenten/Fertig/Tokens/Tool-Aufrufe), die Phasen und eine Per-Agent-Liste (Label, Phase, State, Tokens, Tool-Calls, Dauer). Pollt alle 2 s, solange status === "running", und stoppt automatisch, sobald das Modal geschlossen wird (Overlay nicht mehr im DOM) oder der Workflow endet.
  • Click-Handler in js/pages/chat-page.js, Styles .workflow-status-modal (+ .wf-*) in css/chat.css

Anthropic Claude unterstützt Web-Suche als Server-Tool. Das System integriert Web-Search mit Source-Tracking und Rendering.

Backend

  • Streaming: server_tool_use (Suchanfrage) → web_search_tool_result (Ergebnisse mit Sources)
  • Sources extrahieren: Aus web_search_tool_result-Block werden url + title als JSON-Array extrahiert
  • Persistierung: web_search_result-Messages in DB (type=web_search_result, content = JSON-Array der Sources). Leere Ergebnisse (Sources=[]) werden nicht gespeichert.
  • SSE-Events: web_search_start (Suche beginnt) → web_search_end (Suche fertig, mit Sources)

Frontend – Streaming

  • web_search_start → Tool-Box mit Spinner erstellen, Stream-Bubble pausieren (nicht finalisieren)
  • web_search_end → Tool-Box mit Sources finalisieren
  • text_start → in bestehende Bubble weiter schreiben (kein neuer Block)
  • pauseStreamBubble() / endStreamBubble() Konzept: Text-Blöcke zwischen Web-Searches werden in einer Bubble zusammengefasst
  • text_end pausiert nur (Cursor weg), endStreamBubble() nur bei finalen Events (done, tool_start, error)

Frontend – History

  • web_search_result-Messages werden als Tool-Box mit klickbaren Source-Links gerendert (Domain + Title)
  • _mergeConsecutiveAssistantTexts() fügt Text-Messages zusammen, web_search_result-Messages dazwischen unterbrechen die Merge-Kette nicht
  • Leere web_search_result-Messages (Sources=[]) werden übersprungen

Frontend – Shared-Page

  • SharedEndpoint gibt web_search_result-Messages mit aus (Typ-Filter: text + web_search_result)
  • shared-page.js rendert web_search_result als Tool-Box mit Source-Links
  • Merge-Funktion überspringt web_search_result beim Rückwärts-Suchen, damit Texte zusammengeführt werden

Nachrichten teilen (Shared Messages)

Einzelne Nachrichten oder ganze Threads können als öffentliche Links geteilt werden – ohne Login aufrufbar.

Datenmodell

Tabelle shared_messages:

Spalte Typ Beschreibung
id INTEGER Primary Key
share_token TEXT UUID – öffentlicher Zugangsschlüssel
message_id INTEGER FK auf messages.id
user_id INTEGER Wer hat geteilt
include_context INTEGER 0 = Einzelnachricht, 1 = Thread bis dahin
created_at TEXT Erstellzeitpunkt
expires_at TEXT Optional: Ablaufdatum

API-Endpunkte (SharedEndpoint.cs)

Methode Route Auth Beschreibung
POST /api/messages/{id}/share ✅ Share erstellen (Body: {includeContext})
GET /api/shared/{token} ❌ Öffentlich: Geteilte Nachricht abrufen
GET /api/shared ✅ Eigene Shares auflisten
DELETE /api/shared/{token} ✅ Share-Link widerrufen

Frontend-Flow

  1. Share-Button (🔗) erscheint an jeder Nachricht im Chat (neben Reply, Delete, Mark)
  2. Klick öffnet Kontext-Menü: "Einzelne Nachricht" oder "Thread teilen"
  3. POST /api/messages/{id}/share → Token zurück → URL in Zwischenablage kopiert
  4. Toast: "Link kopiert!"

Öffentliche Ansicht (#/shared/{token})

  • Route #/shared/{token} → shared-page.js
  • Kein Auth nötig – Auth-Bypass in app.js + AuthMiddleware
  • Minimales Layout: Header ("// gott · Geteilte Nachricht"), Nachrichten mit Markdown-Rendering
  • Zwei Modi: single (eine Nachricht) oder context (Thread mit User/Assistant-Wechsel)

Workspace & File-Explorer

Workspace

Eine Session kann einen Workspace-Pfad haben. Dieser wird relativ zum DefaultPath des Users gespeichert (z.B. myapp) und via WorkspaceHelper.ResolveWorkspace() zum absoluten Pfad aufgelöst. Der Workspace:

  • Aktiviert die Datei-/Shell-Tools für den Agent
  • Ermöglicht den File-Explorer im Frontend
  • Bietet git status Informationen
  • Erlaubt das Ausführen von Shell-Befehlen (foreground + background)

WorkspaceHelper

Services/WorkspaceHelper.cs – Statische Hilfsklasse für Workspace-Pfade:

Methode Beschreibung
ResolveWorkspace(session, user) Relativer Workspace + DefaultPath → absoluter Pfad
ValidateRelativeWorkspace(workspace) Prüft auf .., ~, absolute Pfade → null bei ungültig
ToRelativeWorkspace(absolute, defaultPath) Absoluter Pfad → relativ zum DefaultPath

File-Explorer API

Endpoint Beschreibung
GET /api/workspace/tree Verzeichnisbaum (mit Ignore-Filtern)
GET /api/workspace/file Dateiinhalt lesen (max 2MB)
PUT /api/workspace/file Datei speichern
POST /api/workspace/mkdir Ordner erstellen im Workspace
DELETE /api/workspace/dir Leeren Ordner löschen im Workspace
GET /api/workspace/browse Verzeichnis-Browser (für Pfad-Auswahl)
POST /api/workspace/browse/mkdir Ordner erstellen per absolutem Pfad
GET /api/workspace/raw Raw-Datei ausliefern (Bilder, PDFs)
GET /api/workspace/agent/tree Agent-Memory-Verzeichnis
GET/PUT /api/workspace/agent/file Agent-Memory-Dateien lesen/schreiben
POST /api/workspace/agent/mkdir Ordner erstellen im Agent-Memory

Ignored Directories

bin, obj, node_modules, .git, .vs, .idea, publish, dist, out, build, target, .cache

Native Pfade (Windows vs. Linux)

WorkspaceHelper.GetRelativePath() und WorkspaceEndpoint.HandleBrowse() liefern native Pfade zurück (Windows: \, Linux/macOS: /). Das Frontend (file-explorer.js) unterstützt beide Separatoren über Regex-Patterns wie [\\/]+$. Auf Windows kann von C:\ aus zur Laufwerksübersicht ("" = leerer Pfad) navigiert werden.

Session-Exec

POST /api/sessions/{id}/exec führt Shell-Befehle im Workspace aus:

  • foreground: Synchron, wartet auf Ergebnis (30s Timeout)
  • background: Fire-and-forget via systemd-run

Avatar-System

Sessions und Agents können ein Bild/Avatar haben mit konfigurierbarem Zoom.

Datenfelder

Feld Tabellen Beschreibung
image / avatar sessions / agents Base64-kodiertes Bild (WebP, max 512px via ImageService)
image_small / avatar_small sessions / agents Thumbnail (WebP, 256px, Q70) für Listen/Dashboard
avatar_scale sessions, agents Zoom-Faktor (0.5× – 3×, Default 1.0)
avatar_offset_x sessions, agents Horizontale Position (-50% – +50%, Default 0), via CSS translate()
avatar_offset_y sessions, agents Vertikale Position (-50% – +50%, Default 0), via CSS translate()

Darstellung via CSS transform: scale(S) translate(X%, Y%) im .card-image Container (overflow: hidden).

Bild-Verarbeitung (ImageService)

Alle Bilder werden beim Upload in WebP konvertiert (statt PNG), was ~60-80% Speicherersparnis bringt:

Variante Methode Max. Größe Qualität Verwendung
Voll ResizeDataUrl() 512px Q80 Editor, Detail-Ansicht (GET /api/agents/{id}, GET /api/sessions/{id})
Thumbnail ResizeDataUrlSmall() 256px Q70 Listen, Dashboard (GET /api/agents, GET /api/sessions)

Die Listen-Endpoints liefern das Thumbnail als avatar/image-Feld (Fallback auf Voll-Bild wenn kein Thumbnail existiert). Die Detail-Endpoints liefern das volle Bild.

Avatar-Editor (avatar-editor.js)

Wiederverwendbares Modal-Modul, genutzt in Session- und Agent-Pages:

const editor = AvatarEditor.init({
    previewEl:      document.querySelector('.avatar-preview'),
    currentImage:   existingBase64OrNull,
    currentScale:   1.0,
    initialOffsetX: 0,
    initialOffsetY: 0,
    onChange:        (imageData, scale, offsetX, offsetY) => { /* Live-Preview */ }
});
// Werte auslesen: editor.getImage(), editor.getScale(), editor.getOffsetX(), editor.getOffsetY()
// Aufräumen: editor.destroy()

Features:

  • Upload: Datei-Input für Bild-Upload
  • Paste: Ctrl+V → Bild aus Zwischenablage einfügen
  • AI-Generate: Text-Prompt → Krea API → Bild generieren (nur wenn KreaToken konfiguriert)
  • Scale: Range-Slider 0.5× – 3× mit Live-Preview
  • Offset X/Y: Range-Slider -50% – +50% für horizontale/vertikale Positionierung
  • Clear: Bild entfernen

Dashboard-Darstellung

Sessions werden nach der letzten Chat-Nachricht sortiert (neueste zuerst), nicht nach updated_at. Sessions ohne Nachrichten werden per COALESCE nach ihrem updated_at einsortiert.

Cards zeigen Avatare mit transform: scale(S) translate(X%, Y%) im .card-image Container (overflow: hidden). Die Helper buildAgentCard() und buildSessionCard() in cards.js nutzen buildTransformStyle(scale, offsetX, offsetY) dafür.


LaTeX/Math-Rendering

Chat-Nachrichten unterstützen LaTeX-Formeln via KaTeX:

Syntax

  • $$...$$ → Block-Math (zentriert, eigene Zeile)
  • $...$ → Inline-Math (im Fließtext)

Rendering-Pipeline (in markdown.js renderMarkdown())

1. Code-Blöcke extrahieren und schützen (Platzhalter)
2. Block-LaTeX ($$...$$) extrahieren und schützen
3. Inline-LaTeX ($...$) extrahieren und schützen
4. Markdown-Rendering via marked.js
5. Code-Blöcke wiederherstellen
6. LaTeX-Platzhalter durch KaTeX.renderToString() ersetzen
7. Bei fehlerhaftem LaTeX: Fallback als <code class="katex-error">

Abhängigkeiten

  • wwwroot/js/lib/katex.min.js – KaTeX Engine (v0.16.21)
  • wwwroot/css/katex.min.css – Styles
  • wwwroot/css/fonts/ – 21 woff2-Fonts (self-hosted)

Funktioniert überall wo renderMarkdown() aufgerufen wird: Chat, Streaming, History, Archiv, Shared Page.


Windows Desktop App

Im WindowsApp/-Ordner liegt ein minimaler WPF WebView2 Client:

  • .NET 10 Windows, Microsoft.Web.WebView2
  • Lädt die Web-UI in einem nativen Fenster
  • Server-URL konfigurierbar via appsettings.json
  • Dark Background um weißen Flash zu vermeiden
  • Externe Links öffnen im Standard-Browser
  • Fenster: 930px breit, 95% Bildschirmhöhe, zentriert

OpenClaw Import

Sessions können mit Chatverlauf aus einer OpenClaw-Instanz befüllt werden. Die Import-Quelle ist eine SQLite-Datenbank (Build/<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

  1. Liest alle message-Entries aus der OpenClaw-DB, gruppiert nach chainId
  2. Nur Text-Nachrichten werden importiert – Tool-Use/Tool-Result werden übersprungen
  3. User-Message-Bereinigung: Webchat-Prefixe ([Direct 1:1 webchat ...], [Current message ...], [cron:...]), Conversation info-Blöcke werden entfernt
  4. Pro Chain wird ein ChainRecord + MessageRecords erstellt, alle mit tag = 'openclaw-import'
  5. Timestamps werden aus OpenClaw übernommen (created_at)
  6. Re-Import = Update: Vorherige Import-Chains (erkennbar am Tag) werden gelöscht und neu importiert. Bestehende Compactions werden ebenfalls gelöscht.

Sortierung (sort_key)

Importierte Messages werden immer vor normalen Messages einsortiert, da sie historisch älter sind:

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.

  1. Remote-Client lädt das Script und baut WebSocket-Verbindung zu /api/remote auf
  2. Sendet hello-Message mit Hostname, Username, OS-Version
  3. RemoteConnectionManager registriert die Verbindung
  4. Modal-UI auf Dashboard / Chat zeigt beide Befehle (Windows + Linux/macOS)

Session-Zuweisung

  • Im Chat: Remote-Button → Modal zeigt aktive Verbindungen
  • Admin weist Verbindung einer Session zu (POST /api/remote/assign)
  • AI-Agent bekommt remote_execute-Tool → führt Befehle auf dem Remote-Client aus
  • Ergebnis wird synchron via WebSocket zurückgeliefert (Timeout: konfigurierbarer ExecuteTimeout)

OS-aware Tool-Description

Das remote_execute-Tool (sowohl im internen ToolService als auch im MCP-Server) bekommt eine dynamische Tool-Description basierend auf dem OS des verbundenen Remote-Clients:

  • Linux: "Bash-Befehl auf der verbundenen Remote-Linux-Workstation ausführen"
  • Windows: "PowerShell-Befehl auf der verbundenen Remote-Windows-Workstation ausführen"

Das OS wird aus der hello-Message des Clients (RemoteConnection.OsVersion) bezogen und über ChatEndpoint → ChatService.PrepareContext(remoteOs) → ToolPayloadOptions.RemoteOs → ToolDefinitionOptions.RemoteOs bis zu ToolService.GetRemoteExecuteTool(remoteOs) durchgereicht. Im CLI-Pfad zusätzlich an die MCP-Config (GenerateMcpConfig(remoteOs)) und von dort als Env-Variable GOTT_REMOTE_OS an den Python-MCP-Server. Damit generiert Claude direkt passende Befehle für die jeweilige Shell. Cronjobs/Telegram-Bots rufen PrepareContext aktuell ohne remoteOs auf (Default = leer → PowerShell-Description als Fallback).

Protokoll (JSON über WebSocket)

Richtung Type Payload
Client → Server hello hostname, username, os
Server → Client execute command
Client → Server result exitCode, output, error
Client → Server ping –
Server → Client pong –

Semantische Suche (Embedding)

Lokale Sentence-Embeddings ermöglichen semantische Suche über Chat-Nachrichten und Memory-Dateien.

Technologie

  • Modell: all-MiniLM-L6-v2 (ONNX Runtime, 384-dimensionale Embeddings)
  • Tokenisierung: WordPiece (BERT-kompatibel, eigene Implementierung)
  • Speicherung: SQLite-Tabellen message_embeddings + memory_file_embeddings (Vektor als BLOB)
  • Suche: Brute-Force Cosine Similarity (ausreichend für <100k Embeddings)

Komponenten

Service Aufgabe
EmbeddingService ONNX-Modell laden, Embeddings generieren (thread-safe via SemaphoreSlim)
VectorSearchService Schema-Management, Embedding-Storage, Similarity-Search
EmbeddingBackgroundService Backfill beim Start + Live-Queue für neue Messages/Memory-Files

Funktionsweise

  1. Backfill beim Start: Alle bestehenden Text-Messages (user + assistant, ≥10 Zeichen) werden embedded
  2. Memory-Backfill: Alle .md-Dateien in Agent-Verzeichnissen werden in 500-Zeichen-Chunks embedded
  3. Live-Embedding: Neue Messages/Memory-Writes werden via Queue sofort embedded
  4. Suche (memory_search Tool): Query wird embedded, Cosine Similarity gegen alle Embeddings berechnet, Top-N Ergebnisse zurückgegeben

Konfiguration

Das ONNX-Modell muss unter data/models/all-MiniLM-L6-v2/ liegen (model.onnx + vocab.txt). Wenn nicht vorhanden, wird der Service deaktiviert und das Frontend zeigt eine Warnung.


API-Key-Verwaltung

API-Keys werden in der Datenbank verwaltet (Tabellen api_keys + scanned_keys) statt in der Konfigurationsdatei.

Funktionsweise

  • Mehrere Keys pro Provider (z.B. anthropic) können gespeichert werden
  • Zuweisung pro Agent: Keys werden über agent_api_keys (user_id + agent_id → key_id) zugewiesen, nicht mehr global pro User
  • "Kein Key"-Option: Key-Zuweisung kann entfernt werden – Anthropic-Modelle werden blockiert, keyless Provider (Pollinations, Nvidia) funktionieren weiterhin
  • Live-Aktualisierung: Bei Änderung wird der Token sofort im laufenden Service aktualisiert (kein Neustart nötig)
  • Migration: Bestehender AnthropicToken aus appsettings.Production.json wird beim ersten Start automatisch migriert
  • Migration: users.selected_api_key_id wird beim Start für alle Agents des Users in agent_api_keys übernommen
  • Source-URL: Bei adoptierten Scan-Keys wird die GitHub-URL gespeichert

Key-Check (nur Anthropic)

POST /api/admin/api-keys/{id}/check sendet eine minimale Anfrage an die Anthropic API (claude-sonnet-4-20250514, max_tokens=1) mit denselben Headers wie der echte Chat-Client (Authorization: Bearer, anthropic-beta, User-Agent, x-app, anthropic-dangerous-direct-browser-access) und liest die Response-Headers aus:

  • Status: valid, invalid, rate_limited, quota_exceeded, error
  • Plan-Erkennung: Max (overage > 0), Pro (kein fallback-Header), Pro/Max (sonst), API Console (keine unified-Headers)
  • Budget-Auslastung: 5h und 7d Prozentwerte aus unified-* Headers

Passive Usage-Aktualisierung

Nach jedem API-Request an Anthropic (Streaming, Sub-Agent, NonStreaming) werden die Response-Headers automatisch ausgelesen und Plan/Usage des aktiven API-Keys in der DB aktualisiert (AnthropicKeyChecker.ExtractMetaFromHeaders()). Kein separater Check-Request nötig – die Daten sind immer aktuell.

API-Key pro Agent

API-Keys werden pro Agent zugewiesen (Tabelle agent_api_keys), nicht mehr global pro User:

  • PUT /api/api-keys/select/{keyId}?agentId=xxx – Key einem Agent zuweisen
  • PUT /api/api-keys/select/none?agentId=xxx – Key-Zuweisung entfernen ("Kein Key")
  • GET /api/api-keys/selected?agentId=xxx – Zugewiesenen Key abfragen
  • Ohne Key: Chat mit Anthropic-Modellen blockiert, OpenAI-kompatible Modelle (z.B. Pollinations) funktionieren weiterhin
  • Migration: Bestehende users.selected_api_key_id wird beim Start für alle Agents des Users in agent_api_keys kopiert

Key-Scanner (KeyScannerService)

Durchsucht mehrere Quellen nach geleakten API-Keys:

Quellen:

Quelle Quick Web Full
GitHub Code Search ✅ (reduziert) ❌ ✅
GitHub Issues/PR Comments ✅ (nur Body) ❌ ✅ (Body + Comments)
Serper/Google ❌ ✅ ✅
HuggingFace Hub ❌ ✅ ✅

GitHub Code Search:

  • 13 Sprachen (Python, JavaScript, TypeScript, Shell, Markdown, JSON, YAML, TOML, Go, Rust, Ruby, Java, PHP)
  • 24 Pfad-Extensions (.env, .cfg, .ini, .conf, .txt, .log, .md, .json, .yaml, .yml, .toml, .py, .js, .ts, .sh, .bash, .go, .rs, .rb, .java, .php, .config, .properties, .xml)

GitHub Issues/PR Comments:

  • Durchsucht Issue-Bodies nach Key-Patterns via GitHub Search API
  • Im Full-Mode: Lädt zusätzlich alle Kommentare (max 100 Issues, je 100 Kommentare)
  • Rate-Limiting: 2.5s Delay (Search), 1s Delay (Comments), 60s Pause bei 403

HuggingFace Hub:

  • Durchsucht Spaces und Models nach API-Keys in Konfigurationsdateien
  • Max 50 Dateien pro Repo, max 500 KB pro Datei
  • Optionaler HuggingFace Token für höhere Rate-Limits

Zeitplanung: ScanDailyHour (0-23 = täglicher Scan zur vollen Stunde, -1 = nur manuell)

  • Ablauf: POST /api/admin/api-keys/scan → Quellen durchsuchen → neue Keys in scanned_keys speichern → jeden Key mit AnthropicKeyChecker prüfen → Status + Plan + Usage aktualisieren
  • Adopt: POST /api/admin/api-keys/scanned/{id}/adopt → Key in api_keys übernehmen, Name = user/repo, Source-URL = vollständige GitHub-URL
  • Live-Updates: Scan-Ergebnisse werden nach jeder Iteration in der DB gespeichert (nicht erst am Ende)

Chat Usage-Badge (Frontend)

  • Overlay oben zentriert im Chat (nur für Admins) mit visuellen Usage-Balken (Sitzung + Woche)
  • Farbstufen: Grün (<70%), Orange (70-89%), Rot (≥90%)
  • Auto-Hide: Ausgeblendet beim Hochscrollen (wenn Auto-Scroll deaktiviert)
  • Auto-Refresh: Badge aktualisiert sich bei neuen Nachrichten (via pollHistory)
  • Key-Switch-Modal: Klick auf Badge öffnet Modal mit allen API-Keys + "Kein Key"-Option, Zeile klickbar zum Wechseln (pro Agent)
  • Manueller Check: 🔄-Button neben Badge prüft Key und aktualisiert

Key-Scanner-System

Das Key-Scanner-System besteht aus drei Komponenten, die zusammenarbeiten:

KeyPatterns (static)

Zentrale Definition aller Key-Patterns (Regex + Suchbegriffe + Typ-Erkennung). Wird von KeyScannerService und GitHubEventScannerService gemeinsam genutzt.

Unterstützte Key-Typen: Anthropic (OAuth + API), OpenAI (Project + Service + Legacy), Google/Gemini, AWS, GitHub (PAT + OAuth), GitLab, HuggingFace, Stripe (Live + Test), SendGrid, Slack, Krea AI.

KeyCheckDispatcher (static)

Zentraler Dispatcher für Key-Checks aller Provider. Normalisiert die Provider-spezifischen Ergebnisse in ein einheitliches UnifiedKeyCheckResult mit standardisiertem DB-Status (valid, invalid, rate_limited, quota_exceeded, error).

Provider-Checker:

Provider Checker Prüfmethode
anthropic AnthropicKeyChecker Minimale API-Anfrage + Usage-Header-Auswertung
openai OpenAiKeyChecker GET /v1/models + Rate-Limit-Headers
google GoogleKeyChecker GET /v1/models + Quota-Check (GenerateContent)
stripe StripeKeyChecker GET /v1/balance + Account-Info

GitHubEventScannerService (BackgroundService)

Echtzeit-Scanner: Pollt die GitHub Public Events API alle 30 Sekunden, filtert PushEvents und scannt Commit-Diffs nach geleakten API-Keys.

  • Konfiguration: GitHubEventScanner (bool, aktiviert/deaktiviert), GitHubToken (für höhere Rate-Limits)
  • Deduplication: Ringbuffer für Event-IDs (max 10.000)
  • Performance: Max 30 Commit-Diffs pro Poll-Zyklus, Diffs > 100KB werden übersprungen
  • Live-Log: Internes Ringbuffer (max 50 Einträge), abrufbar über GET /api/admin/api-keys/event-scanner/log
  • Status: GET /api/admin/api-keys/event-scanner/status → isRunning, lastPoll, eventsPerHour

Claude CLI Integration

Die Claude Code CLI kann als alternativer Streaming-Weg verwendet werden (OAuth-basiert, kein API-Key nötig).

Architektur

  • ClaudeCliService – Wrapper für die Claude Code CLI (~/.claude/local/claude). Prüft beim Start ob die CLI verfügbar ist.
  • AnthropicService.Cli.cs – StreamChatViaCli(): Streaming-Chat über die CLI statt der HTTP-API. Verwendet das lokale OAuth-Token aus ~/.claude/.credentials.json.

Ablauf

  1. ChatEndpoint prüft ob kein API-Key vorhanden und die CLI verfügbar ist
  2. Bei Verfügbarkeit wird StreamChatViaCli() statt StreamChat() aufgerufen
  3. Die CLI wird als Kindprozess gestartet mit JSON-Streaming-Output
  4. SSE-Events werden identisch zum HTTP-API-Streaming generiert

Voraussetzungen

  • Claude Code CLI installiert (~/.claude/local/claude)
  • Gültiges OAuth-Token in ~/.claude/.credentials.json

CLI-Fallback für Sub-Agents, Cronjobs und Telegram

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.

Dasselbe Muster gilt für Cronjobs (CronjobService.ExecuteCronjob + SendCronjobNotification) und Telegram (TelegramService.HandleMessage) – exakt die gleiche Provider-/Key-Dreiteilung wie im ChatEndpoint:

  • Anthropic ohne zugewiesenen Key → ChatNonStreamingViaCli() (CLI-Fallback), sofern der User CanUseCli/IsAdmin hat und die CLI verfügbar ist
  • openai ohne Agent-Key → Server-Setting OpenAiApiKey (in ResolveProviderConfig aufgelöst, analog Gemini/MiniMax); fehlt auch das → harter Fehler (kein CLI-Äquivalent)
  • keyless Provider (nvidia/gemini/minimax/pollinations) → Request wird ohne User-Key durchgelassen; der Server-seitige Key wird in ResolveProviderConfig aufgelöst

Der frühere harte Abbruch („Kein API-Key zugewiesen") blockte irrtümlich jeden nicht-leeren Provider – jetzt blockiert nur noch openai ganz ohne Key (weder Agent-Key noch OpenAiApiKey). Die Prüfung erfolgt im CronjobService über OpenAiService.HasServerOpenAiKey(), in ChatEndpoint/TelegramService direkt über settings.Get("OpenAiApiKey").

Modell-Refresh (POST /api/models/refresh)

RefreshModelsFromApis() gibt pro Provider ein ProviderRefreshResult (provider, loaded, count, note) zurück; der Endpoint liefert das als providers[] mit. Wird ein Provider übersprungen (kein Key, API leer), bleibt seine alte Liste im Cache stehen – das wird jetzt explizit gemeldet (Log + Toast im Agent-Formular), statt pauschal „Modelle neu geladen" zu behaupten.

Der OpenAI-Key für die Modell-Liste kommt aus OpenAiApiKey (Setting), sonst aus dem ersten aktiven openai-Eintrag der api_keys-Tabelle (ResolveOpenAiKey()). Der Chat-Modell-Filter (IsOpenAiChatModel) erfasst gpt-*, chatgpt-* und die komplette o-Reihe inklusive bare IDs (o1, o3, künftig o5) – der frühere StartsWith("o3-")-Filter hat die verschluckt.

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.

z.ai / GLM-Provider (über den CLI-Pfad)

Ein Agent kann z.ai/GLM-Modelle verwenden, indem die Modell-ID mit glm: prefixt wird (z.B. glm:glm-5.2, glm:glm-4.6). Diese laufen über den Anthropic-kompatiblen z.ai-Endpoint – also über den Claude-CLI-Pfad, nicht über den OpenAI-Provider-Pfad.

  • Routing: ChatService.ResolveModelId() strippt das glm:-Prefix → die rohe Modell-ID (glm-5.2) geht mit Provider="" an den CLI-Pfad. ClaudeCliService.IsZaiModel() erkennt sie am glm--Prefix.
  • Per-Prozess Env-Injection: Für GLM-Modelle setzt ClaudeCliService ANTHROPIC_BASE_URL = https://api.z.ai/api/anthropic und ANTHROPIC_AUTH_TOKEN = <ZaiApiKey> (Server-Setting ZaiApiKey) nur im jeweiligen Kindprozess. Ohne ZaiApiKey läuft der Request nicht über z.ai (Warnung im Log).
  • Effort (GLM 5.2+): CLAUDE_CODE_EFFORT_LEVEL wird für GLM 5.2 oder höher durchgereicht (IsZaiEffortModel per Versionsvergleich >= 5.2, damit künftige Releases automatisch mitgenommen werden); für ältere GLM-Versionen (4.x, 5, 5.1, 5-turbo) wird die Env-Var entfernt. Effort bleibt damit Claude-/GLM-5.2+-only.
  • Web-Search (web-search-prime): Wenn der Agent Web-Suche aktiviert hat und das Modell GLM + ZaiApiKey ist, werden die nativen WebSearch/WebFetch-Tools verboten (--disallowed-tools, sonst scheitert das Modell headless an der Berechtigung) und durch z.ai's web-search-prime-MCP-Server ersetzt (registriert in GenerateMcpConfig als web-search-prime → https://api.z.ai/api/mcp/web_search_prime/mcp mit Authorization: Bearer <ZaiApiKey>, freigegeben via mcp__web-search-prime__*). Für Claude-Modelle bleiben die nativen WebSearch/WebFetch-Tools.
  • Kein Ultracode: Dynamische Workflows (Ultracode) sind nur für Claude-Modelle aktiv – GLM hat kein Workflow-Runtime (Workflow/Task werden für GLM nicht freigegeben).

Datenbank-Explorer

Admin-only-Feature zum direkten Inspizieren der SQLite-Datenbank über die Web-UI.

Backend

DatabaseExplorerEndpoint.cs bietet zwei Endpoints:

  • GET /api/admin/db/tables – Listet alle Tabellen mit Spalten- und Zeilenanzahl
  • GET /api/admin/db/tables/{name} – Liest Tabellendaten (mit Paging: ?limit=100&offset=0, Sortierung: ?sort=column&order=desc)

Frontend

db-explorer-page.js (#/db-explorer) – Admin-only Seite mit klickbarer Tabellenliste und Daten-Grid. Erreichbar über das Dashboard (Button "Datenbank").


MCP-Server-Integration (Internal API)

InternalEndpoint.cs stellt eine interne API bereit, die von externen MCP-Servern genutzt werden kann.

Authentifizierung

  • Beim Start wird ein einmaliger InternalToken generiert (GUID)
  • Alle /api/internal/*-Endpoints erfordern X-Internal-Token-Header
  • Ausnahme: POST /api/chat/choice-respond nutzt normale Cookie-Auth

Endpoints

Endpoint Funktion
POST /api/internal/remote-execute Shell-Befehl auf Remote-Client ausführen (via WebSocket)
POST /api/internal/ask-user-choice Short-Poll für User-Choice-Antworten (202 = pending, 200 = answered)
POST /api/chat/choice-respond Frontend: User-Antwort auf Choice-Frage speichern
POST /api/internal/memory-read Memory-Datei eines Agents lesen
POST /api/internal/memory-write Memory-Datei schreiben/appenden
POST /api/internal/memory-list Memory-Dateien eines Agents auflisten

User-Choice-Flow (CLI-Modus)

  1. MCP-Server ruft ask-user-choice auf → gibt Frage + Optionen an Frontend
  2. Frontend zeigt Choice-Buttons
  3. User klickt → choice-respond speichert Antwort
  4. MCP-Server pollt ask-user-choice → erhält Antwort

Deployment

WICHTIG: Das Deployment ist ein manueller Vorgang, der durch den Benutzer auf dem Server durchgeführt werden muss. Das System führt kein automatisches Deployment durch.

Goldene Regel für Änderungen

  • Arbeiten Sie AUSSCHLIESSLICH im Verzeichnis wwwroot/ für Frontend-Änderungen.
  • Editiere NIEMALS Dateien in Build/*/publish/wwwroot/! Diese Verzeichnisse werden bei jedem Build überschrieben.
  • Das Verzeichnis Build/*/publish/data/gott.db darf niemals manuell gelöscht, geleert oder direkt modifiziert werden.

Deployment-Vorgang (Beispiel für Linux)

Das vorhandene Shell-Skript deploy.sh dient lediglich als Hilfestellung für den manuellen Prozess:

  1. Frontend-Anpassungen vornehmen (in wwwroot/).
  2. Manuelles Deployment auf dem Server ausführen:
    # 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: AnthropicToken wird nicht mehr über appsettings.json konfiguriert, sondern über die API-Key-Verwaltung im Admin-Bereich (DB-Tabelle api_keys). Bestehende Tokens werden beim ersten Start automatisch aus appsettings.Production.json migriert.

appsettings.Production.json

Überschreibt Werte aus appsettings.json im Production-Environment. Wird nicht beim Publish mitkopiert (liegt nur im Build/<OS>/publish/-Verzeichnis). Verwaltung über die Admin-UI (Servereinstellungen, #/server-settings).

Environment

  • ASPNETCORE_ENVIRONMENT=Production → lädt appsettings.Production.json
  • Tokens und Secrets werden über die Server-Settings-UI in appsettings.Production.json geschrieben

Datenbank-Migrationen

Die DatabaseService.InitializeDatabase() handhabt Schema-Erstellung und Migrationen in einem einzigen Methoden-Aufruf:

  1. Tabellen-Erstellung via CREATE TABLE IF NOT EXISTS
  2. Spalten-Migration via TryAddColumn() – fängt "column already exists" ab
  3. Daten-Migration (z.B. auto_commit von INTEGER zu TEXT, use_web_search zu web_search_version)
  4. Token-Migration – Bestehender AnthropicToken aus appsettings.Production.json wird automatisch in api_keys-Tabelle migriert
  5. Bild-Migration – Konvertierung bestehender PNG/JPEG-Avatare zu WebP + Generierung von Thumbnails (avatar_small, image_small)
  6. API-Key-Migration – users.selected_api_key_id → agent_api_keys (pro Agent statt global pro User)
  7. Settings-Cleanup – Obsolete Server-Settings (z.B. ScanIntervalHours) werden aus der DB entfernt

Dieses Pattern ermöglicht:

  • Idempotente Ausführung bei jedem App-Start
  • Keine separate Migrations-Verwaltung nötig
  • Neue Features können einfach Spalten hinzufügen
  • Abwärtskompatibel – bestehende Daten bleiben erhalten