Files
gott/ARCHITECTURE.md
T
Meleki 145441fa7b feat: Gemini thought_signature korrekt lesen und zurücksenden
- thought_signature aus delta.tool_calls[n].extra_content.google.thought_signature lesen (korrekter Pfad)
- ThoughtSignature-Feld in MessageRecord + DB-Migration (ALTER TABLE messages ADD COLUMN thought_signature)
- SaveMessage speichert ThoughtSignature pro Tool-Call
- OpenAiService: toolCallAccumulators Tuple um ThoughtSignature erweitert
- Tool-Calls mit thought_signature werden mit extra_content.google.thought_signature zurückgesendet (nicht function.thought_signature)
- ConvertMessage (History-Rebuild) setzt thought_signature ebenfalls korrekt
- ChatService.BuildApiMessages übergibt thought_signature aus DB-Record
- Gemini-Modelle dynamisch laden statt hardcodiert
- Tool-Call-Akkumulator nutzt id als primären Key (Gemini sendet parallele Calls ohne index)
- content=null → content='' für Gemini-Kompatibilität
2026-03-12 06:34:11 +01:00

1634 lines
84 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ARCHITECTURE.md – Gott
**Gott** ist eine selbst-gehostete AI-Chat-Plattform, gebaut auf **.NET 10** und der **Anthropic Claude API**. Sie bietet Multi-User-Support, persistente Agent-Memory, Datei-/Shell-Tools, Telegram-Integration, Cronjobs und eine Krea-Bildgenerierung – alles über ein Vanilla-JavaScript-Frontend gesteuert.
---
## Inhaltsverzeichnis
1. [Überblick](#überblick)
2. [Tech-Stack](#tech-stack)
3. [Projektstruktur](#projektstruktur)
4. [Datenmodell](#datenmodell)
5. [Backend-Architektur](#backend-architektur)
- [Program.cs – Startup](#programcs--startup)
- [Services](#services)
- [Endpoints (API)](#endpoints-api)
- [Middleware](#middleware)
- [Models](#models)
- [Templates](#templates)
6. [Frontend-Architektur](#frontend-architektur)
7. [Chat-Pipeline](#chat-pipeline)
8. [Tool-System](#tool-system)
9. [Agent-System](#agent-system)
10. [System-Prompt-Aufbau](#system-prompt-aufbau)
11. [Memory-System](#memory-system)
12. [Skill-System](#skill-system)
13. [Telegram-Integration](#telegram-integration)
14. [Cronjob-System](#cronjob-system)
15. [Krea-Bildgenerierung](#krea-bildgenerierung)
16. [Auth & Multi-User](#auth--multi-user)
17. [Admin-Benutzerverwaltung](#admin-benutzerverwaltung)
18. [Multi-Agent System (Preparer + Review + Finalizer)](#multi-agent-system-preparer--review--finalizer)
19. [Interaktive Rückfragen (ask_user_choice)](#interaktive-rückfragen-ask_user_choice)
20. [Plan-Modus](#plan-modus)
21. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages)
22. [Workspace & File-Explorer](#workspace--file-explorer)
23. [Avatar-System](#avatar-system)
24. [LaTeX/Math-Rendering](#latexmath-rendering)
25. [Windows Desktop App](#windows-desktop-app)
26. [Deployment](#deployment)
27. [Konfiguration](#konfiguration)
28. [Datenbank-Migrationen](#datenbank-migrationen)
---
## Überblick
```
┌─────────────────────────────────────────────────────────────┐
│ Browser / WebView2 │
│ Vanilla JS SPA (wwwroot/) │
├─────────────────────────────────────────────────────────────┤
│ ASP.NET 10 Minimal API │
│ http://0.0.0.0:19000 │
├──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬────┤
│ Auth │ Chat │ Hist │ Sess │Agent │ Tele │ Cron │ Krea │Adm │
│ │ SSE │ │ │ │ gram │ jobs │ Img │ in │
├──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴────┤
│ Services Layer │
│ AnthropicService · ChatService · ToolService │
│ DatabaseService · MemoryService · SkillService │
│ TelegramService · CronjobService · KreaService │
├─────────────────────────────────────────────────────────────┤
│ SQLite (data/gott.db) via Dapper │
│ Filesystem (data/agents/, data/skills/) │
├─────────────────────────────────────────────────────────────┤
│ Anthropic Claude API (claude-sonnet/opus/haiku) │
│ Krea API (Bildgenerierung) │
│ Telegram Bot API │
└─────────────────────────────────────────────────────────────┘
```
---
## Tech-Stack
| Schicht | Technologie |
|---------------|-----------------------------------------------------------|
| **Runtime** | .NET 10 (ohne Nullable, ohne Implicit Usings) |
| **Web** | ASP.NET Minimal API, Kestrel |
| **Datenbank** | SQLite via Microsoft.Data.Sqlite + Dapper |
| **AI** | Anthropic Claude API (Streaming SSE + Non-Streaming) |
| **Images** | SixLabors.ImageSharp (Resize, Thumbnails) |
| **Cron** | Cronos (Cron-Expression-Parsing) |
| **Frontend** | Vanilla JavaScript SPA (kein Framework) |
| **Markdown** | marked.js (Client-side Rendering) |
| **LaTeX** | KaTeX 0.16.21 (Math-Rendering, self-hosted mit woff2-Fonts) |
| **Editor** | CodeMirror 5 + EasyMDE |
| **Icons** | Font Awesome |
| **Fonts** | Inter, JetBrains Mono (self-hosted) |
| **Desktop** | WPF + WebView2 (.NET 10, Windows-only) |
---
## Projektstruktur
```
gott/
├── Program.cs # Startup, DI, Middleware, Routing
├── gott.csproj # Projekt-Definition (.NET 10)
├── appsettings.json # Konfiguration (Tokens, Limits)
├── deploy.sh # Deployment-Script (systemctl)
│
├── Models/ # Daten-Modelle (Records)
│ ├── UserRecord.cs
│ ├── AgentRecord.cs
│ ├── SessionRecord.cs
│ ├── ChainRecord.cs
│ ├── MessageRecord.cs # + AttachmentRecord
│ ├── ArchiveRecord.cs
│ ├── CronjobRecord.cs
│ ├── KreaImageRecord.cs
│ ├── SharedMessageRecord.cs # Geteilte Nachrichten (Share-Token)
│ ├── TelegramBotRecord.cs # Telegram Bot Entität
│ ├── ChatModels.cs # ChatMessage, ChatRequest, AttachmentInfo/Block
│ └── ChatContext.cs # Runtime-Kontext für einen Chat + ReviewContext
│
├── Services/ # Business-Logic
│ ├── DatabaseService.cs # SQLite CRUD, Schema, Migrationen
│ ├── AuthService.cs # Token-basierte Authentifizierung
│ ├── ChatService.cs # Kontext-Aufbau, History-Builder
│ ├── AnthropicService.cs # Claude API (Streaming + Non-Streaming)
│ ├── ToolService.cs # Tool-Definitionen + Ausführung
│ ├── SystemPromptBuilder.cs # Modularer System-Prompt aus Blöcken
│ ├── MemoryService.cs # Agent-Gedächtnis (Dateisystem)
│ ├── SkillService.cs # Skill-Verwaltung (Dateisystem)
│ ├── TelegramService.cs # Telegram Bot API Integration
│ ├── CronjobService.cs # BackgroundService für Cron-Tasks
│ ├── KreaService.cs # Krea Image Generation API
│ ├── ImageService.cs # Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail)
│ ├── StreamingTracker.cs # Tracking aktiver Streams + CancellationTokenSource für Stop
│ └── WorkspaceHelper.cs # Workspace-Pfad-Auflösung (relativ → absolut)
│
├── Endpoints/ # HTTP API (Minimal API)
│ ├── AuthEndpoint.cs # Login, Logout, Me
│ ├── ChatEndpoint.cs # POST /api/chat (SSE Streaming)
│ ├── HistoryEndpoint.cs # History, Archive, Attachments, Marks
│ ├── SessionEndpoint.cs # CRUD + SystemPrompt, Exec, Git-Status
│ ├── AgentEndpoint.cs # CRUD
│ ├── WorkspaceEndpoint.cs # File-Tree, File-Read/Write, Browse, Agent-Files
│ ├── TelegramEndpoint.cs # Bot-CRUD, Webhook, Session-Config, Registration
│ ├── OpenClawEndpoint.cs # OpenClaw Session Import
│ ├── CronjobEndpoint.cs # CRUD + Toggle, Manual Run
│ ├── KreaEndpoint.cs # Generate, List, File-Serve
│ ├── SkillEndpoint.cs # List, Agent-Skill-Mapping, Output-Files
│ ├── SharedEndpoint.cs # Nachrichten teilen (öffentliche Links)
│ ├── AdminEndpoint.cs # Admin: User-CRUD, Impersonate (Admin-only)
│ ├── ServerSettingsEndpoint.cs # Admin: Server-Settings lesen/schreiben (Admin-only)
│ ├── ProfileEndpoint.cs # Profil, Persona, Passwort
│ ├── SettingsEndpoint.cs # User-Settings (JSON blob)
│ ├── BackupEndpoint.cs # Backup als tar.gz Download
│ └── InfoEndpoint.cs # Build-Zeitstempel
│
├── Middleware/
│ └── AuthMiddleware.cs # Cookie-basierte Token-Validierung
│
├── Extensions/
│ └── HttpContextExtensions.cs # GetUserId(), IsAdmin()
│
├── Templates/ # Standard-Templates für neue Agents
│ ├── IDENTITY.md # Wer bin ich? (Name, Sprache)
│ ├── SOUL.md # Persönlichkeit, Verhalten
│ └── USER.md # Infos über den User
│
├── wwwroot/ # Frontend (Vanilla JS SPA)
│ ├── index.html # Single HTML Entry Point
│ ├── css/ # Stylesheets
│ │ ├── theme.css # CSS Custom Properties (Dark Theme)
│ │ ├── base.css # Reset, Grundlayout
│ │ ├── main.css # Sidebar, Layout-Struktur
│ │ ├── chat.css # Chat-Nachrichten, Bubbles
│ │ ├── dashboard.css # Dashboard-Grid
│ │ ├── form.css # Formulare, Inputs, Avatar-Editor-Modal
│ │ ├── markdown.css # Markdown + KaTeX Rendering-Styles
│ │ ├── file-explorer.css # File-Explorer Panel
│ │ ├── login.css # Login-Seite
│ │ ├── mobile.css # Responsive Anpassungen
│ │ ├── tooltip.css # Tooltip-Styles
│ │ ├── fonts.css # @font-face Definitionen
│ │ ├── placeholder.css # Placeholder/Loading States
│ │ ├── katex.min.css # KaTeX Math-Rendering Styles
│ │ ├── codemirror.min.css # CodeMirror Editor
│ │ ├── easymde.min.css # EasyMDE Editor
│ │ └── fonts/ # KaTeX woff2 Fonts (21 Dateien)
│ ├── js/
│ │ ├── app.js # App-Initialisierung, Auth-Check
│ │ ├── router.js # Client-side SPA Router
│ │ ├── api.js # API-Client (fetch wrapper)
│ │ ├── ui-helpers.js # DOM-Helpers ($, esc, truncate), Toast, ScrollDown, Meta-Bar
│ │ ├── markdown.js # Markdown-Rendering (renderMarkdown, LaTeX/Code-Schutz)
│ │ ├── cards.js # Card-Builder (buildSessionCard, buildAgentCard)
│ │ ├── msg-actions.js # Nachrichten-Aktionen (Delete, Mark, Share, Reply, Timestamp)
│ │ ├── tool-ui.js # Tool-Boxes, Diffs, Todo-Liste, Visibility-Logik
│ │ ├── modals.js # Modale Dialoge (System-Prompt, Command-Output, Krea-Image)
│ │ ├── stream-ui.js # Stream-Bubble, Review-Streaming
│ │ ├── bubbles.js # Message Bubbles (User, Assistant, Error, Warning, Choice, Review)
│ │ ├── chat.js # Chat-Logic, SSE-Streaming, Plan-Mode
│ │ ├── history.js # History-Rendering, Polling
│ │ ├── archive.js # Archiv-Loading
│ │ ├── reply.js # Reply/Quote-Handling
│ │ ├── files.js # File-Upload, Workspace-Refs
│ │ ├── session.js # Session-Verwaltung
│ │ ├── file-explorer.js # File-Explorer Komponente
│ │ ├── avatar-editor.js # Wiederverwendbares Avatar-Editor-Modal
│ │ ├── commands-ui.js # Custom-Commands UI
│ │ ├── emoji.js # Emoji-Picker
│ │ ├── emoji.userdata.js # Häufig genutzte Emojis
│ │ ├── tooltip.js # Tooltip-System
│ │ ├── title.js # Tab-Titel-Management
│ │ ├── pages/ # Seiten-Komponenten
│ │ │ ├── chat-page.js # Haupt-Chat-Ansicht
│ │ │ ├── dashboard.js # Dashboard / Home
│ │ │ ├── session-page.js # Session-Einstellungen
│ │ │ ├── agent-page.js # Agent-Editor
│ │ │ ├── telegram-bot-page.js # Telegram-Bot-Editor
│ │ │ ├── cronjob-page.js # Cronjob-Verwaltung
│ │ │ ├── profile-page.js # Profil-Bearbeitung
│ │ │ ├── admin-page.js # Admin: Benutzerverwaltung
│ │ │ ├── settings-page.js # App-Einstellungen
│ │ │ ├── login-page.js # Login-Formular
│ │ │ └── shared-page.js # Öffentliche geteilte Nachricht
│ │ └── lib/ # Vendor-Libraries
│ │ ├── marked.min.js # Markdown Parser
│ │ ├── codemirror.min.js # Code Editor
│ │ ├── codemirror.cjs # CM CommonJS Wrapper
│ │ ├── easymde.min.js # Markdown Editor
│ │ ├── katex.min.js # KaTeX Math-Rendering Engine
│ │ └── cm-mode-*.min.js # CodeMirror Syntax-Modes
│ ├── fonts/ # Self-hosted Fonts (Inter, JetBrains Mono)
│ └── lib/font-awesome/ # Font Awesome Icons
│
├── WindowsApp/ # Windows Desktop Client (WPF)
│ ├── Gott.csproj # .NET 10 Windows, WebView2
│ ├── App.xaml / App.xaml.cs # WPF Application
│ ├── MainWindow.xaml/cs # WebView2 Host Window
│ └── appsettings.json # Server-URL Konfiguration
│
└── Build/ # Build-Output (NICHT editieren!)
├── Linux/
│ ├── bin/ obj/ publish/
└── Windows/
├── bin/ obj/ publish/
└── data/
├── gott.db # SQLite Datenbank
├── agents/ # Agent Memory-Dateien
│ └── {agentId}/
│ ├── IDENTITY.md
│ ├── SOUL.md
│ ├── MEMORY.md
│ └── memory/ # Tages-Logs etc.
├── skills/ # Skill-Verzeichnisse
│ └── {slug}/
│ ├── SKILL.md
│ ├── PROMPT.md
│ └── output/
├── images/ # Krea-generierte Bilder
├── logs/ # Payload-Logs (optional)
└── backup/ # Backup-Archive
```
---
## Datenmodell
### Entity-Relationship
```
User (1) ─── (*) Agent
User (1) ─── (*) Session
Agent (1) ─── (*) Session (Session hat optional einen Agent)
Agent (1) ─── (*) Session (Session hat optional einen Review-Agent)
Agent (1) ─── (*) Session (Session hat optional einen Finalizer-Agent)
Agent (*) ─── (*) Skill (via agent_skills Tabelle)
Session (1) ─── (*) Chain (ein Chat-Request = eine Chain)
Session (1) ─── (*) Archive (archivierte Chains)
Chain (1) ─── (*) Message
Message (1) ─── (*) Attachment
Session (1) ─── (*) Cronjob
User (1) ─── (*) Cronjob
User (1) ─── (*) SharedMessage
Message (1) ─── (*) SharedMessage
User (1) ─── (*) TelegramBot
TelegramBot (1) ─── (*) Session (via telegram_bot_id)
```
### Tabellen
| Tabelle | Beschreibung |
|-------------------|-----------------------------------------------------|
| `users` | Benutzer (username, password_hash, display_name, is_admin, persona, settings, default_path) |
| `tokens` | Auth-Tokens (token → user_id) |
| `agents` | AI-Agenten (model, custom_commands, auto_commit, use_soul/identity/user_info/memory, use_web_search, web_search_version, allow_user_choice, task_instruction, avatar_scale/offset, avatar_small, is_public) |
| `sessions` | Chat-Sessions (description, image, image_small, workspace, context, custom_commands, telegram_bot_id, telegram_chat_id, avatar_scale/offset, preparer_agent_id, review_agent_id, max_review_iterations, finalizer_agent_id) |
| `chains` | Request-Chains (model, tokens, iterations, timing, tool_choice, tool_names, stream_error_info, plan_mode, archive_id) |
| `messages` | Einzelne Nachrichten (text, tool_use, tool_result, error, web_search_result), Tag für Sub-Agent-Markierung oder Import-Herkunft (`openclaw-import`), SortKey für korrekte Reihenfolge |
| `attachments` | Datei-Anhänge (base64, image/document) |
| `archives` | Archivierte Chain-Gruppen |
| `agent_skills` | Agent ↔ Skill Zuordnung |
| `cronjobs` | Geplante Tasks (cron expression, prompt, notify_session_id, is_enabled, last_run, next_run) |
| `shared_messages` | Geteilte Nachrichten (Share-Token, Ablaufdatum) |
| `telegram_bots` | Telegram Bots (bot_token, webhook_secret, name, is_public) – eigenständige Entität |
### Zentrale Records
**ChainRecord** – Repräsentiert einen kompletten Request-Response-Zyklus:
- `ChainId` – Eindeutige ID
- `SessionId` – Zugehörige Session
- `AgentId` – Zugehöriger Agent
- `Model` – Verwendetes Claude-Modell (z.B. `claude-sonnet-4-6`)
- `SystemPrompt` – Der vollständige System-Prompt (gespeichert für Nachvollziehbarkeit)
- `InputTokens`, `OutputTokens`, `CacheCreationTokens`, `CacheReadTokens` – Token-Verbrauch
- `Iterations` – Anzahl der Tool-Loop-Durchläufe
- `StopReason` – `end_turn`, `tool_use`, `max_iterations`, `error`, `client_disconnected`, `awaiting_user_choice`, `preparer_query`
- `ToolChoice` – Verwendete Tool-Choice-Strategie (automatisch: `any` wenn Workspace/Skills/Memory, sonst `auto`)
- `ToolNames` – Komma-separierte Liste der verwendeten Tools
- `ArchiveId` – Zugehöriges Archiv (wenn archiviert)
- `StreamErrorInfo` – Fehlerdetails bei Stream-Abbrüchen
- `PlanMode` – Ob die Chain im Plan-Modus erstellt wurde (informativ)
- `DurationMs` – Gesamtdauer der Chain
**MessageRecord** – Eine einzelne Nachricht innerhalb einer Chain:
- `Role` – `user`, `assistant`
- `Type` – `text`, `tool_use`, `tool_result`, `error`, `web_search_result`
- `Content` – Textinhalt oder JSON (bei tool_use: Input-JSON)
- `ToolName`, `ToolCallId` – Bei Tool-Aufrufen
- `IsError` – Ob die Nachricht ein Fehler ist
- `Iteration` – Welche Tool-Loop-Iteration
- `AttachmentCount` – Anzahl angehängter Dateien
- `AttachmentNames` – JSON-Array mit Dateinamen
- `Tag` – Markierung (`null` = normal, `"preparer"/"review"/"finalizer"` = Sub-Agent-Message (wird aus API-History gefiltert), `"openclaw-import"` = importierte Nachricht (wird immer in History einbezogen))
- `IsMarked` – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)
- `DeletedOn` – Soft-Delete Zeitstempel
**SessionWithStats** – Erweitertes Session-Objekt für Listen-Endpunkt:
- Enthält alle `SessionRecord`-Felder + `MessageCount`, `ArchiveMessageCount`, `TotalMessageCount`
- `TelegramConfigured`, `TelegramLinked` – Telegram-Status (Bot zugewiesen / Chat-ID verknüpft)
- `GitStatus` – Inline Git-Status-Summary (`Add`, `Edit`, `Delete`, `Outgoing`) oder `null` wenn kein Workspace (wird im Endpoint hinzugefügt, nicht Teil des Records)
- `Image` wird durch `ImageSmall` ersetzt (Thumbnail für Dashboard-Performance)
**GitStatusSummary** – Kompakte Git-Status-Info:
- `Add`, `Edit`, `Delete` – Anzahl geänderter Dateien nach Typ
- `Outgoing` – Anzahl ausgehender (nicht gepushter) Commits
---
## Backend-Architektur
### Program.cs – Startup
Die `Program.cs` ist der zentrale Einstiegspunkt und konfiguriert:
1. **Service-Registrierung** – Alle Services als Singletons im DI-Container
2. **Datenbank-Initialisierung** – `DatabaseService` erstellt Schema + Migrationen
3. **Default-User** – Wird automatisch angelegt falls keine User existieren
4. **Middleware** – Static Files → `AuthMiddleware` → Endpoints
5. **Endpoint-Mapping** – Alle 17 Endpoint-Gruppen registriert
6. **Server** – Kestrel auf `http://0.0.0.0:{Port}` (Port aus Config, Default 19000)
Initialisierungsreihenfolge:
```
AuthService → DatabaseService → EnsureDefaultUser
→ ImageService, StreamingTracker, MemoryService, SkillService
→ ToolService → ChatService
→ AnthropicService → TelegramService → KreaService
→ CronjobService (BackgroundService)
```
### Services
| Service | Typ | Aufgabe |
|------------------------|--------------|------------------------------------------------------------------|
| `DatabaseService` | Singleton | SQLite CRUD, Schema-Init, Migrationen, alle DB-Operationen |
| `AuthService` | Singleton | Token-Erzeugung/-Validierung, Passwort-Hashing (SHA256) |
| `ChatService` | Singleton | Chat-Kontext aufbauen, History aus DB rekonstruieren |
| `AnthropicService` | Singleton | Claude API Kommunikation (Streaming SSE + Non-Streaming) |
| `ToolService` | Singleton | Tool-Definitionen bereitstellen, Tool-Aufrufe ausführen |
| `WorkspaceHelper` | Static | Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz |
| `SystemPromptBuilder` | Static | Modularer System-Prompt aus konfigurierbaren Blöcken + Reminder-Generierung |
| `MemoryService` | Singleton | Agent-Gedächtnis auf Dateisystem (MEMORY.md, IDENTITY.md etc.) |
| `SkillService` | Singleton | Skill-Discovery und -Management aus Dateisystem |
| `TelegramService` | Singleton | Telegram Bot API, Webhook-Handling, Nachrichten senden |
| `CronjobService` | Background | Minütlicher Check auf fällige Cronjobs, Ausführung via Claude |
| `KreaService` | Singleton | Krea API für Bildgenerierung, Job-Polling, Image-Download |
| `ImageService` | Singleton | Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail) für Avatare und Session-Bilder |
| `StreamingTracker` | Singleton | Trackt aktive Streams + CancellationTokenSource pro Session für Stop-Button |
### Endpoints (API)
Alle Endpoints folgen dem **Minimal API**-Pattern mit Extension Methods (`MapXxxEndpoints`).
| Gruppe | Basis-Route | Methoden |
|---------------|----------------------------|---------------------------------------------|
| **Auth** | `/api/auth/` | `POST login`, `POST logout`, `GET me`, `GET autologin` |
| **Chat** | `/api/chat` | `POST` (SSE Streaming), `POST stop` |
| **History** | `/api/history` | `GET history`, `GET poll`, `POST archive`, `GET archives`, `GET archive/{id}`, `GET attachments/{id}`, `PUT messages/{id}/mark`, `PUT messages/{id}/delete`, `GET chains/{id}` |
| **Sessions** | `/api/sessions/` | CRUD + `GET system-prompt`, `GET system-prompt-blocks`, `GET streaming`, `POST exec`, `GET git-status` (Liste inkl. inline Git-Status + Thumbnail) |
| **Agents** | `/api/agents/` | CRUD (Liste inkl. inline Skills + Thumbnail) |
| **Admin** | `/api/admin/` | `GET users`, `POST users`, `PUT users/{id}`, `PUT users/{id}/password`, `DELETE users/{id}`, `POST users/{id}/impersonate`, `POST users/{id}/login-link` (Admin-only) |
| **Workspace** | `/api/workspace/` | `GET tree`, `GET/PUT file`, `GET browse`, `POST mkdir`, `DELETE dir`, `POST browse/mkdir`, `GET agent/tree`, `GET/PUT agent/file`, `POST agent/mkdir`, `GET raw` |
| **Telegram** | `/api/telegram/` | Bot-CRUD (`/bots`, `/bots/available`), `POST webhook/{botId}` (unauthenticated), Session-Config, Webhook-Management |
| **OpenClaw** | `/api/openclaw/` | `GET sessions` (Liste importierbarer Sessions), `POST import` (Import in bestehende Session) |
| **Cronjobs** | `/api/cronjobs/` | CRUD + `POST toggle`, `POST run` |
| **Krea** | `/api/images/` | `POST generate`, `GET list`, `GET file/{name}`, `GET file/{name}/thumb`, `GET status` |
| **Skills** | `/api/skills` | `GET list`, `GET/PUT agent skills`, Output-Files + Thumbnails |
| **Profile** | `/api/profile` | `GET`, `PUT`, `PUT password` |
| **Settings** | `/api/settings` | `GET`, `PUT` |
| **Backup** | `/api/backup` | `POST` (Admin only, tar.gz Download) |
| **Shared** | `/api/shared/`, `/api/messages/{id}/share` | Share erstellen, öffentlich abrufen, auflisten, löschen |
| **Info** | `/api/info` | `GET` (Build-Zeitstempel) |
| **ServerSettings** | `/api/admin/server-settings` | `GET`, `PUT` (Admin-only, liest/schreibt `appsettings.Production.json`) |
### Middleware
**AuthMiddleware** prüft jede `/api/`-Anfrage auf ein gültiges `gott_token`-Cookie.
Ausnahmen (kein Auth nötig):
- Statische Dateien (nicht `/api/`)
- `POST /api/auth/login`
- `GET /api/auth/autologin` (Token-basierter Autologin via Link)
- `POST /api/telegram/webhook/{botId}` (authentifiziert via Secret-Header)
- `GET /api/shared/{token}` (öffentlicher Zugriff auf geteilte Nachrichten)
- `GET /api/images/file/*` (Krea-Bilder und Thumbnails, öffentlich)
**API-Key Auth** (ohne User-Kontext):
- `/api/images/*`-Routen akzeptieren einen `X-Api-Key`-Header. Wenn der Key mit `KreaApiKey` aus der Config übereinstimmt, wird der Request ohne User-Authentifizierung durchgelassen. Normaler Cookie-Auth funktioniert weiterhin als Fallback.
Bei Erfolg werden `UserId` und `IsAdmin` in `HttpContext.Items` gesetzt (bei API-Key-Auth sind diese nicht gesetzt).
### Models
Die Models in `Models/` sind reine Daten-Records ohne Business-Logic (Ausnahme: `ChatMessage` hat Helper-Methoden für Content-Extraktion und `UserRecord` hat `GetEffectivePersona()`).
**ChatContext** ist ein Runtime-Objekt das den vollständigen Kontext eines Chat-Requests hält:
- Chain-ID, Session-ID, Model-ID
- Aufgelöster System-Prompt (Text + strukturierte Blöcke)
- Workspace-Pfad, Tool-Choice (automatisch bestimmt)
- Prompt-Blöcke (`PromptBlocks`) – für Reminder-Generierung in der History
- Referenzen auf Agent und Session Records
### Templates
`Templates/` enthält Vorlagen die beim Erstellen eines neuen Agents in dessen Memory-Verzeichnis kopiert werden:
- **IDENTITY.md** – Name und Grundinfos (Platzhalter `{NAME}` wird ersetzt)
- **SOUL.md** – Persönlichkeit und Verhaltensgrundsätze
- **USER.md** – Informationen über den User (global, nicht agent-spezifisch)
---
## Frontend-Architektur
Das Frontend ist eine **Single-Page Application** ohne Build-Step, komplett in Vanilla JavaScript.
### Routing
`router.js` implementiert ein Hash-basiertes Client-Side-Routing:
| Route | Page | Beschreibung |
|--------------------------|---------------------|---------------------------|
| `#/` | `dashboard.js` | Session-Übersicht |
| `#/chat/{sessionId}` | `chat-page.js` | Chat-Ansicht |
| `#/session/{sessionId}` | `session-page.js` | Session-Einstellungen |
| `#/agent/{agentId}` | `agent-page.js` | Agent-Editor |
| `#/telegram-bot/{botId}` | `telegram-bot-page.js` | Telegram-Bot-Editor |
| `#/cronjobs` | `cronjob-page.js` | Cronjob-Verwaltung |
| `#/profile` | `profile-page.js` | Profil |
| `#/settings` | `settings-page.js` | App-Einstellungen |
| `#/admin` | `admin-page.js` | Benutzerverwaltung (Admin-only) |
| `#/server-settings` | `server-settings-page.js` | Servereinstellungen (Admin-only) |
| `#/login` | `login-page.js` | Login |
| `#/shared/{token}` | `shared-page.js` | Öffentliche geteilte Nachricht (kein Auth) |
### Kern-Module
- **app.js** – Initialisierung, Auth-Check, globale Event-Handler
- **api.js** – Zentraler API-Client mit `fetch()`-Wrapper, Auth-Redirect bei 401
- **ui-helpers.js** – DOM-Helpers (`$`, `esc`, `truncate`), Toast-System, ScrollDown, Meta-Bar, Button-Builder
- **markdown.js** – Markdown-Rendering (`renderMarkdown`), LaTeX/Code-Schutz, Krea-Gallery-Wrapping
- **cards.js** – Card-Builder (`buildAgentCard()`, `buildSessionCard()`, `buildTelegramBotCard()`)
- **msg-actions.js** – Nachrichten-Aktionen (Delete, Mark, Share, Reply-Button, Timestamp)
- **tool-ui.js** – Tool-Boxes (Diffs, Todos, File-Content), Visibility-Logik
- **modals.js** – Modale Dialoge (System-Prompt, Command-Output, Krea-Image)
- **stream-ui.js** – Stream-Bubble, Review-Streaming-Rendering
- **bubbles.js** – Message Bubbles (User, Assistant, Error, Warning, Choice, Review, Attachments)
- **chat.js** – SSE-Stream-Handling, sendMessage, Plan-Mode
- **history.js** – Chat-History laden, paginieren, rendern, Polling
- **archive.js** – Archiv-Loading und -Rendering
- **reply.js** – Reply/Quote-Handling (Set, Clear, Format)
- **files.js** – File-Upload, Workspace-Refs
- **session.js** – Session-State-Management
- **file-explorer.js** – Workspace File-Explorer Panel (Dateien, Ordner erstellen/löschen, Folder-Browser)
- **avatar-editor.js** – Wiederverwendbares Avatar-Editor-Modal (Upload, Paste, AI-Generate, Scale)
- **commands-ui.js** – Custom-Commands UI-Komponente
### Wiederverwendbare Card-Builder (cards.js)
- **`buildAgentCard(agent)`** – Erzeugt ein `.card` DOM-Element für einen Agent (Avatar, Name, Model-Badge, Feature-Badges: autoCommit, useSoul, useIdentity, useUserInfo, useMemory, webSearch, Skills, Public/Fremd). Skills werden inline vom API-Endpunkt mitgeliefert. Wird im Dashboard und im Agent-Picker-Modal (Session-Formular) verwendet.
- **`buildSessionCard(session, agent)`** – Erzeugt ein `.card` DOM-Element für eine Session (Avatar, Name, Badges: Agent, Kontext, Telegram, Meta: Nachrichten/Archiv/Workspace, Git-Status). Git-Status wird inline vom API-Endpunkt mitgeliefert. Wird im Dashboard und im Session-Picker-Modal (Cronjob-Formular) verwendet.
- **`buildTelegramBotCard(bot)`** – Erzeugt ein `.card` DOM-Element für einen Telegram Bot (Name, maskierter Token, Session-Count, Public/Fremd-Badges). Wird im Dashboard und im Bot-Picker-Modal (Session-Formular) verwendet.
- **Picker-Modals** – Agent- und Session-Auswahl in Formularen (Session-/Cronjob-Seiten) erfolgt über modale Dialoge mit den Card-Buildern statt über Dropdowns.
- **emoji.js** – Emoji-Picker mit Kategorien und Suche
- **tooltip.js** – Tooltip-System für Hover-Infos
- **title.js** – Dynamisches Tab-Titel-Management
### Styling
CSS ist in modulare Dateien aufgeteilt mit CSS Custom Properties für Theming:
- `theme.css` – Alle Farben, Abstände, Schriften als CSS Variables
- `base.css` – Reset und Grundlayout
- `main.css` – Sidebar, Content-Area, Responsive Layout
- `chat.css` – Chat-Bubbles, Streaming-Animation, Tool-Outputs
- `markdown.css` – Rendering von Markdown in Nachrichten
- `mobile.css` – Responsive Breakpoints und Anpassungen
---
## Chat-Pipeline
Der Chat-Flow von User-Input bis zur gerenderten Antwort:
```
1. User tippt Nachricht + optionale Attachments (Bilder/Dokumente)
↓
2. Frontend: POST /api/chat { content, sessionId }
↓
3. ChatEndpoint:
a) User-Message + Attachments in DB speichern
b) ChatService.PrepareContext() → Agent, Model, System-Prompt, Chain
c) ChatService.BuildHistoryFromDb() → vollständige Message-History
d) AnthropicService.StreamChat() starten
↓
4. AnthropicService (Agentic Loop):
┌─────────────────────────────────────────┐
│ while (iterations < maxIterations) │
│ → Claude API Request (SSE) │
│ → Stream-Processing: │
│ • Text-Tokens → SSE an Frontend │
│ • Tool-Use → ToolService.Execute() │
│ • Tool-Results → nächste Iteration │
│ → Messages in DB persistieren │
│ → Stop wenn: end_turn / max_iterations│
│ → CancellationToken bei Abbruch │
└─────────────────────────────────────────┘
↓
4b. Review-Phase (optional, wenn ReviewAgent konfiguriert):
┌─────────────────────────────────────────┐
│ Nach end_turn des Worker-Agents: │
│ → RunReview() mit Review-Agent │
│ → Review-Agent prüft (eigene Tools) │
│ → [APPROVED] → Fertig │
│ → Feedback → an Worker als User-Msg │
│ → Worker korrigiert → erneuter Review │
│ → Max Review-Runden → Fertig │
└─────────────────────────────────────────┘
↓
4c. Finalizer-Phase (optional, wenn FinalizerAgent konfiguriert):
┌─────────────────────────────────────────┐
│ Nach Review (oder direkt nach Worker): │
│ → RunFinalizer() mit Finalizer-Agent │
│ → Finalizer arbeitet (eigene Tools) │
│ → z.B. git commit, deploy, cleanup │
│ → Kein Feedback-Loop (einmalig) │
└─────────────────────────────────────────┘
↓
5. Frontend: SSE Events empfangen und rendern
• "text" → Markdown parsen, Token für Token anzeigen
• "tool_*" → Tool-Aufrufe collapsible darstellen
• "review_*" → Review-Bubbles mit eigenem Styling
• "done" → Token-Stats, Dauer, Model anzeigen
```
### Abbruch (Stop-Button)
Der Stop-Mechanismus arbeitet auf zwei Ebenen:
**1. Expliziter Stop-Endpoint (`POST /api/chat/stop`):**
- Frontend ruft `apiStopChat(sessionId)` auf
- `StreamingTracker` hält pro Session einen `CancellationTokenSource`
- `StreamingTracker.Cancel(sessionId)` cancelt den Token sofort
- Der Chat-Handler verknüpft den Stop-Token mit dem Request-Token via `CreateLinkedTokenSource`
**2. CancellationToken-Propagierung:**
1. Frontend: `AbortController.abort()` → SSE-Fetch abgebrochen + `POST /api/chat/stop`
2. `StreamingTracker.Cancel()` → CancellationTokenSource wird gecancelled
3. `AnthropicService`: `ThrowIfCancellationRequested()` am Loop-Anfang, vor/nach Tool-Execution
4. `ToolService.ExecuteTool()`: CancellationToken wird durchgereicht → bei `execute` wird der Prozess gekillt
5. `WriteSseEvent()`: Alle SSE-Writes mit CancellationToken → sofortiger Abbruch
6. Chain wird mit `StopReason = "client_disconnected"` finalisiert
**Warum zwei Ebenen?** Kestrel erkennt einen Client-Disconnect erst beim nächsten Write auf den Response-Stream. Wenn das Backend in einer langen Tool-Ausführung steckt, wird nichts geschrieben – der explizite Stop-Endpoint garantiert sofortigen Abbruch.
### Streaming vs Non-Streaming
- **Web-Chat**: Streaming via SSE (`text/event-stream`). Tokens werden in Echtzeit gerendert.
- **Telegram**: Non-Streaming via `ChatNonStreaming()`. Vollständige Antwort wird abgewartet und als Telegram-Nachricht gesendet.
- **Cronjobs**: Non-Streaming, wie Telegram.
### History-Optimierung
Die History-Rekonstruktion in `BuildHistoryFromDb()` ist optimiert:
- **Max 120 Messages**: Die History wird auf maximal 120 Messages begrenzt (älteste entfernt)
- **Ältere Chains**: Nur `text`-Messages (user + assistant), keine Tool-Calls
- **Letzte Chain**: Vollständig mit allen Tool-Use/Tool-Result Blöcken
- **Attachments**: Nur für die letzte User-Message geladen (spart enorme Token)
- **Tool-Results**: In der History auf 20.000 Zeichen gekürzt (vollständig in DB)
- **System-Prompt-Reminder**: Alle ~30 Messages wird ein automatisch generierter Reminder als User/Assistant-Paar injiziert (z.B. `[System Reminder] Beachte weiterhin den System-Prompt (Identity, Workspace, Kontext, Memory, ...)`). Bekämpft das "Lost-in-the-Middle"-Problem bei langen Konversationen. Der Reminder-Text wird aus den aktiven Prompt-Block-Labels generiert (`SystemPromptBuilder.BuildReminder()`). Nur aktiv wenn ≥2 Blöcke vorhanden.
---
## Tool-System
Der Agent hat Zugriff auf folgende Tools, abhängig von Konfiguration:
### Datei-Tools (wenn Workspace konfiguriert)
| Tool | Beschreibung |
|---------------|--------------------------------------------------|
| `read_file` | Datei lesen (relativer Pfad zum Workspace) |
| `write_file` | Datei erstellen/überschreiben |
| `edit_file` | Exakten Textabschnitt in Datei ersetzen |
| `list_files` | Verzeichnis-Listing |
| `delete_file` | Datei oder leeres Verzeichnis löschen |
| `execute` | Shell-Befehl ausführen (bash, 180s Timeout) |
| `glob` | Dateien via Glob-Pattern finden |
| `grep` | Dateien nach Regex/Text durchsuchen |
| `todo_write` | Todo-Liste schreiben (als Datei im Workspace) |
### Memory-Tools (wenn UseMemory aktiviert)
| Tool | Beschreibung |
|----------------|-------------------------------------------|
| `memory_read` | Memory-Datei des Agents lesen |
| `memory_write` | Memory-Datei schreiben (überschreiben/append) |
| `memory_list` | Alle Memory-Dateien des Agents auflisten |
### Interaktive Rückfragen (wenn AllowUserChoice aktiviert)
| Tool | Beschreibung |
|--------------------|-----------------------------------------------------|
| `ask_user_choice` | Stellt dem User eine Frage mit 2-5 Auswahlmöglichkeiten |
Dieses Tool wird **nicht ausgeführt**, sondern als SSE-Event an den Client weitergeleitet. Siehe [Interaktive Rückfragen](#interaktive-rückfragen-ask_user_choice).
### Web-Suche (wenn WebSearchVersion konfiguriert)
Nutzt Anthropics eingebaute Web-Suche als Server-Side-Tool.
### Plan-Modus: Read-only Tools
Im Plan-Modus werden nur folgende Tools bereitgestellt: `read_file`, `list_files`, `glob`, `grep`, `execute`, `todo_write`, `memory_*`, `ask_user_choice`. Write-Tools (`write_file`, `edit_file`, `delete_file`) werden gefiltert.
### Tool-Choice (automatisch)
Die Tool-Choice-Strategie wird automatisch bestimmt (nicht konfigurierbar pro Agent):
- **`any`**: Automatisch wenn Workspace, Skills oder Memory vorhanden. Erste Iteration erzwingt Tool-Nutzung, danach `auto`. Verhindert dass der Agent nur Text antwortet wenn Tools benötigt werden.
- **`auto`**: Wenn keine Tools verfügbar. Claude entscheidet frei.
- **`none`**: Im `/no-tools`-Modus.
### Pfad-Sicherheit
Alle Dateipfade werden via `ResolvePath()` aufgelöst und gegen den Workspace/Memory-Ordner geprüft – Path-Traversal-Angriffe werden verhindert.
---
## Agent-System
Ein **Agent** definiert die Persönlichkeit und Fähigkeiten der AI:
| Eigenschaft | Beschreibung |
|-------------------|------------------------------------------------------------------|
| `Name` | Anzeigename |
| `Avatar` | Base64 Data-URL (WebP, 512px) + `AvatarSmall` (WebP, 256px Thumbnail) |
| `Model` | `sonnet`, `sonnet-4-5`, `sonnet-4-6`, `opus`, `opus-4-5`, `opus-4-6`, `haiku`, `haiku-4-5` |
| `CustomCommands` | JSON-Array mit Slash-Commands (Label + Prompt) |
| `AutoCommit` | `""` (nichts), `"mention"` (anmerken – nur auf User-Request), `"enforce"` (erzwingen – nach jeder Änderung), `"forbid"` (strikt verboten) |
| `UseSoul` | SOUL.md in System-Prompt einbinden |
| `UseIdentity` | IDENTITY.md in System-Prompt einbinden |
| `UseUserInfo` | User-Persona in System-Prompt einbinden |
| `UseMemory` | Memory-Tools aktivieren |
| `WebSearchVersion`| Web-Suche aktivieren (z.B. `web_search_20260209`) |
| `AllowUserChoice` | `ask_user_choice`-Tool verfügbar (Default: true) |
| `TaskInstruction` | Aufgabe/Anweisung – optionaler Text, wird als `## Anweisungen`-Block in System-Prompt eingefügt |
| `IsPublic` | Andere User können den Agent in ihren Sessions verwenden (aber nicht bearbeiten) |
### Model-Auflösung
```
"sonnet" → claude-sonnet-4-6
"sonnet-4-5" → claude-sonnet-4-5
"sonnet-4-6" → claude-sonnet-4-6
"opus" → claude-opus-4-6
"opus-4-5" → claude-opus-4-5
"opus-4-6" → claude-opus-4-6
"haiku" → claude-haiku-4-5
"haiku-4-5" → claude-haiku-4-5
(default) → claude-sonnet-4-6
```
---
## System-Prompt-Aufbau
Der `SystemPromptBuilder` erzeugt den System-Prompt modular aus konfigurierbaren **Blöcken**:
```
┌──────────────────────────────────────────┐
│ 1. IDENTITY.md (UseIdentity) │ → Wer ist der Agent?
│ 2. SOUL.md (UseSoul) │ → Persönlichkeit, Verhalten
│ 3. Anweisungen (TaskInstruction) │ → Aufgaben-/Anweisungstext (optional)
│ 4. User-Persona (UseUserInfo) │ → Infos über den User
│ 5. Workspace-Info │ → Tools + Workspace-Pfad
│ 6. Rückfragen (AllowUserChoice) │ → Hinweis zu ask_user_choice Tool
│ 7. Session-Kontext │ → Freiformtext
│ 8. Skills (Agent-Skills) │ → Skill-Prompts
│ 9. Memory (UseMemory) │ → Memory-Regeln + MEMORY.md Inhalt
│10. Git-Verhalten (AutoCommit) │ → Commit-Regeln
│11. Markierte Nachrichten │ → Vom User als wichtig markierte Messages
│ [Plan-Modus-Block] │ → Nur wenn planMode=true (überspringt 10)
│ [Preparer-Instruktionsblock] │ → Nur für Preparer-Agents
└──────────────────────────────────────────┘
```
Jeder Block hat:
- **Icon** – Font Awesome Icon-Klasse
- **Label** – Beschreibender Titel
- **Content** – Markdown-Text
Die Blöcke sind über die API als JSON abrufbar (`GET /api/sessions/{id}/system-prompt-blocks`), sodass das Frontend sie einzeln inspizierbar darstellen kann.
---
## Memory-System
Jeder Agent hat ein eigenes **Dateisystem-basiertes Gedächtnis**:
```
data/agents/{agentId}/
├── IDENTITY.md # Identität (aus Template initialisiert)
├── SOUL.md # Persönlichkeit (aus Template initialisiert)
├── MEMORY.md # Haupt-Memory (kuratierte Informationen)
└── memory/ # Unterordner für Tages-Logs, Themen
├── YYYY-MM-DD.md
└── ...
```
### Funktionsweise
1. **Initialisierung**: Beim Erstellen eines Agents werden `IDENTITY.md` und `SOUL.md` aus `Templates/` kopiert
2. **Lesen**: Der Agent kann seine Memory-Dateien via `memory_read` Tool lesen
3. **Schreiben**: Via `memory_write` (überschreiben oder append)
4. **Auflisten**: Via `memory_list` alle Dateien durchsuchen
5. **System-Prompt**: `MEMORY.md` Inhalt wird automatisch in den System-Prompt eingebettet
6. **IDENTITY.md** und **SOUL.md**: Werden als eigene System-Prompt-Blöcke eingebunden (wenn aktiviert)
### Sandbox
Alle Pfade werden gegen das Agent-Verzeichnis geprüft – kein Zugriff außerhalb möglich.
---
## Skill-System
Skills sind **Dateisystem-basierte Erweiterungen** die einem Agent zugeordnet werden können:
```
data/skills/{slug}/
├── SKILL.md # Metadaten (Name, Description, Version im Frontmatter)
├── PROMPT.md # System-Prompt-Erweiterung (in Agent-Prompt eingebettet)
├── _meta.json # Alternative Metadaten (Fallback)
└── output/ # Skill-Output (Bilder, Dateien, etc.)
```
### SKILL.md Frontmatter
```yaml
---
name: Mein Skill
description: Beschreibung
version: 1.0
---
```
### Agent-Zuordnung
Skills werden über die `agent_skills` Tabelle einem Agent zugeordnet. Der `PROMPT.md`-Inhalt aller zugeordneten Skills wird als ein System-Prompt-Block eingefügt.
### Workspace-Implikation
Wenn ein Agent Skills hat aber kein Workspace konfiguriert ist, wird automatisch `/tmp` als Workspace gesetzt (damit `execute` für Skill-Commands funktioniert).
---
## Telegram-Integration
Telegram Bots sind **eigenständige Entitäten** mit einer eigenen Tabelle (`telegram_bots`), die am Dashboard verwaltet werden (wie Agents, Sessions, Cronjobs). Ein Bot kann **mehreren Sessions** zugewiesen werden – jede Session hat ihren eigenen Chat-Kontext.
### Datenmodell
**Telegram Bot** (`telegram_bots`):
- `BotId` – UUID
- `UserId` – Owner
- `Name` – Anzeigename
- `BotToken` – Bot-Token von @BotFather
- `WebhookSecret` – Einmalig generierter Token für Webhook-Verifizierung
- `IsPublic` – Andere User können den Bot in ihren Sessions verwenden (aber nicht bearbeiten)
**Session-Felder:**
- `TelegramBotId` – Verweis auf den zugewiesenen Bot (wie `AgentId`)
- `TelegramChatId` – Verknüpfte Chat-ID (1 Session = max. 1 Telegram-Chat)
### Webhook-Flow
```
1. User erstellt Telegram Bot im Dashboard (Name + Token)
2. User registriert Webhook auf der Bot-Page
→ Setzt Webhook-URL: {baseUrl}/api/telegram/webhook/{botId}
3. User weist Bot einer Session zu (Session-Settings, Bot-Picker)
4. Telegram sendet Updates an Webhook
5. AuthMiddleware lässt Webhook-Requests durch (kein Cookie nötig)
6. TelegramEndpoint prüft X-Telegram-Bot-Api-Secret-Token Header
7. TelegramService verarbeitet Update:
- Bot laden via botId
- Chat-ID extrahieren
- Session suchen: telegram_bot_id = botId AND telegram_chat_id = chatId
- /start → Auto-Link (wenn genau 1 unverknüpfte Session) oder Chat-ID anzeigen
- /clear → History archivieren
- /status → Session-Info anzeigen
- Text → An Claude weiterleiten (Non-Streaming)
```
### Auto-Link-Logik
Bei `/start` oder einer normalen Nachricht, wenn keine Session für den Chat verknüpft ist:
1. Suche Sessions mit diesem Bot, die **noch keine Chat-ID** haben
2. **Genau 1 gefunden** → Automatisch verknüpfen + Bestätigungsnachricht
3. **0 oder mehrere gefunden** → "Deine Chat-ID ist X, bitte im Web-UI zuweisen"
Unlink ist **nur über das Web-UI** möglich (kein Telegram-Befehl).
### Non-Streaming Chat
Telegram-Nachrichten werden über `AnthropicService.ChatNonStreaming()` verarbeitet. Die History wird genauso aus der DB aufgebaut wie beim Web-Chat. Antworten über 4096 Zeichen werden automatisch in Chunks aufgeteilt.
### Dashboard & Frontend
- **Dashboard**: Telegram-Bots-Section mit Cards (Name, maskierter Token, Session-Count)
- **Bot-Page** (`#/telegram-bot/{botId}`): Name, Token, Webhook-Secret (readonly), Webhook-Verwaltung (registrieren/status/entfernen), Public-Checkbox, Löschen
- **Session-Page**: Bot-Picker (wie Agent-Picker), readonly Chat-ID mit Unlink-Button
---
## Cronjob-System
Der `CronjobService` ist ein **BackgroundService** der minütlich nach fälligen Jobs prüft.
### Datenmodell
| Feld | Beschreibung |
|---------------------|------------------------------------------------|
| `CronExpression` | Standard-Cron-Format (5 Felder), via Cronos |
| `SessionId` | Session in der der Job läuft |
| `Prompt` | Text der als User-Message an Claude gesendet wird |
| `NotifySessionId` | Optionale Session für Benachrichtigung |
| `IsEnabled` | Aktiviert/Deaktiviert |
| `NextRunAt` | Nächste geplante Ausführung (UTC) |
### Ausführungsflow
```
1. CronjobService prüft jede Minute
2. Fällige Jobs (next_run_at <= jetzt) werden geladen
3. Für jeden Job:
a) ChatService.PrepareContext() – Chain + System-Prompt aufbauen
b) History aus DB laden
c) Prompt als User-Message anhängen + in DB speichern
d) AnthropicService.ChatNonStreaming() ausführen
e) Telegram-Benachrichtigung senden (auf Arbeits-Session)
f) Optional: Zusammenfassung in Notify-Session schreiben + Telegram dort
g) Nächste Ausführung berechnen + DB aktualisieren
```
### Benachrichtigungen
- **Arbeits-Session**: Wenn die Session Telegram hat, wird die Antwort dort gepostet
- **Notify-Session**: Wenn konfiguriert, wird eine Zusammenfassung in eine andere Session geschrieben und der Agent dort antwortet darauf
---
## Krea-Bildgenerierung
Die `KreaService` ermöglicht Bildgenerierung über die Krea API.
### Unterstützte Modelle
```
flux, flux-kontext, flux-1.1-pro, flux-1.1-pro-ultra,
nano-banana, nano-banana-pro, imagen-3, imagen-4, imagen-4-fast, imagen-4-ultra,
ideogram-2-turbo, ideogram-3, seedream-3, seedream-4,
chatgpt-image, runway-gen-4
```
### Flow
```
1. POST /api/images/generate { prompt, model, width, height, ... }
2. KreaService sendet Request an Krea API
3. Job-Polling bis Status "completed"
4. Bilder herunterladen nach data/images/
5. Metadaten als JSON speichern
6. Response mit Dateinamen + URLs
```
### Bild-Serving
- `GET /api/images` – Liste aller generierten Bilder (aus Metadaten-JSON)
- `GET /api/images/file/{filename}` – Einzelnes Bild ausliefern (ohne Auth)
- `GET /api/images/file/{filename}/thumb` – Thumbnail (320px WebP, gecacht in `data/cache/images/`, ohne Auth)
- `GET /api/images/status` – Generierungsstatus abfragen
Bild-Dateien und Thumbnails sind **ohne Authentifizierung** abrufbar (für externe Einbindung).
---
## Auth & Multi-User
### Authentifizierung
- **Login**: Username + Passwort → SHA256 Hash-Vergleich → Token generieren
- **Token**: 32 Bytes Random, als `gott_token` HttpOnly Cookie (365 Tage)
- **Validierung**: Jeder API-Call prüft Token → User-ID
- **Logout**: Token aus DB löschen + Cookie entfernen
### Multi-User-Isolation
- Sessions sind an `user_id` gebunden
- Agents sind an `user_id` gebunden
- Cronjobs sind an `user_id` gebunden
- Jeder User sieht nur seine eigenen Daten
- Admin-Flag (`is_admin`) für Backup-Endpoint, Admin-Benutzerverwaltung, Impersonate
### Default-Path (Base-Path)
Jeder User hat einen optionalen `DefaultPath` – den Basis-Ordner für alle Workspaces. Wird **nur vom Admin** gesetzt.
- **Session-Workspace**: Speichert nur den **relativen Pfad** zum DefaultPath (z.B. `myapp` statt `/home/user/projects/myapp`)
- **Auflösung**: `WorkspaceHelper.ResolveWorkspace()` kombiniert DefaultPath + relativen Workspace → absoluter Pfad
- **System-Prompt**: Bekommt den **aufgelösten absoluten Pfad**, damit der Agent korrekt navigieren kann
- **Path-Traversal-Schutz**: `..` in relativen Workspace-Pfaden wird blockiert (400 Bad Request)
- **Legacy-Support**: Absolute Pfade in `session.Workspace` funktionieren weiterhin als Fallback
- **Folder-Browser**: Startet beim DefaultPath, User kann nicht über den Base-Path hinaus navigieren
### Default-User
Beim ersten Start wird automatisch ein Default-User erstellt (`DatabaseService.EnsureDefaultUser()`).
### User-Persona
Jeder User hat eine optionale **Persona** – ein Freitext der beschreibt wer er ist. Diese wird (wenn `UseUserInfo` am Agent aktiv ist) in den System-Prompt als "Dein Gegenüber" eingebettet.
---
## Admin-Benutzerverwaltung
Admins können alle User verwalten über `AdminEndpoint.cs` (alle Endpoints erfordern `is_admin`).
### API-Endpunkte
| Methode | Route | Beschreibung |
|----------|-----------------------------------------|-------------------------------------------|
| `GET` | `/api/admin/users` | Alle User auflisten |
| `POST` | `/api/admin/users` | Neuen User erstellen |
| `PUT` | `/api/admin/users/{id}` | User bearbeiten (Name, Username, Admin, DefaultPath) |
| `PUT` | `/api/admin/users/{id}/password` | Passwort zurücksetzen |
| `DELETE` | `/api/admin/users/{id}` | User löschen (+ Tokens, Shared Messages) |
| `POST` | `/api/admin/users/{id}/impersonate` | Als anderer User einloggen (Login-as-User) |
| `POST` | `/api/admin/users/{id}/login-link` | Autologin-Token generieren → URL zurückgeben |
### Impersonate (Login-as-User)
Admins können sich als beliebiger User einloggen ohne dessen Passwort zu kennen:
1. `POST /api/admin/users/{id}/impersonate` → Erstellt Token für Zieluser
2. Setzt neuen `gott_token`-Cookie → Admin-Session wird zum Zieluser
3. `location.reload()` → Frontend lädt mit neuem User-Kontext
### Login-Link generieren
Admins können für jeden User einen **Autologin-Link** generieren – z.B. für Setup oder Support:
1. `POST /api/admin/users/{id}/login-link` → Erstellt Token, gibt `{ token }` zurück
2. Frontend baut URL: `{window.location.origin}/api/auth/autologin?token={token}`
3. Link wird im Modal angezeigt mit Copy-Button
4. `GET /api/auth/autologin?token={token}` → Setzt Cookie + Redirect zu `/` (ohne Auth, wie Login)
### Frontend (`admin-page.js`)
- Erreichbar über Dashboard-Link "Benutzerverwaltung" (nur für Admins sichtbar)
- User-Liste als Cards mit Edit/Delete/Login-Link-Buttons
- Inline-Formular für Create/Edit
- Login-Link-Modal mit Copy-Funktion
- Sich selbst löschen ist nicht möglich
---
## Multi-Agent System (Preparer + Review + Finalizer)
Das Multi-Agent-System ermöglicht bis zu **vier Agents** pro Session: einen **Worker**, einen optionalen **Vorbereiter (Preparer)**, einen optionalen **Reviewer** und einen optionalen **Finalizer**.
### Architektur
```
User-Message
→ Preparer-Phase (optional):
→ Preparer-Agent bekommt: User-Message + History (inkl. eigener vorheriger Messages)
→ Preparer arbeitet (kann Tools nutzen: read_file, execute etc.)
→ Erledigt Vorarbeit mit seinen Fähigkeiten (z.B. JSON bauen, Daten aufbereiten)
→ [QUESTION] → Rückfrage an User, Flow stoppt, User muss erneut eingeben
→ Antwort ohne [QUESTION] → automatisch weiterleiten an Worker
→ Worker-Agent arbeitet (Tools, Iterationen) → end_turn
→ Review-Phase (optional):
→ Review-Agent bekommt: Konversation als Kontext
→ Review-Agent prüft (kann Tools nutzen: read_file, execute etc.)
→ [APPROVED] → Fertig, Antwort an User
→ Feedback-Text → wird als User-Message an Worker-Agent eingefügt
→ Worker-Agent korrigiert → end_turn → Review-Agent prüft erneut
→ Max Review-Runden erreicht → Fertig (mit letzter Worker-Antwort)
→ Finalizer-Phase (optional):
→ Finalizer-Agent bekommt: gesamte Konversation
→ Finalizer arbeitet (eigene Tools, z.B. git commit, deploy)
→ Kein Feedback-Loop – einmalige Ausführung
→ Fertig
```
### Konfiguration
Auf **Session-Ebene** (nicht Agent-Ebene):
| DB-Feld | SessionRecord | Beschreibung |
|---------|---------------|-------------|
| `preparer_agent_id` | `PreparerAgentId` | ID des Vorbereiter-Agents (optional, null = kein Preparer) |
| `review_agent_id` | `ReviewAgentId` | ID des Review-Agents (optional, null = kein Review) |
| `max_review_iterations` | `MaxReviewIterations` | Max. Review-Runden (Default: 3) |
| `finalizer_agent_id` | `FinalizerAgentId` | ID des Finalizer-Agents (optional, null = kein Finalizer) |
### Vorbereiter-Agent (Preparer)
Der Preparer wird **vor** dem Worker-Agent aufgerufen. Er analysiert die User-Anfrage, kann Vorarbeit leisten (z.B. Daten aufbereiten, JSON bauen, Code analysieren) und gibt das Ergebnis an den Worker weiter. Bei unklaren Anfragen stellt er Rückfragen.
#### Logik-Pattern: Default = Forward
Die Logik ist **invertiert** gegenüber dem Review-Pattern: Jede Antwort wird automatisch weitergeleitet, **außer** sie enthält explizit `[QUESTION]`. Das ist robuster als ein Pflicht-Tag wie `[FORWARD]`, weil LLMs dazu neigen, Tags zu vergessen.
| Antwort | Ergebnis |
|---------|----------|
| Ohne `[QUESTION]` | Automatisch weitergeleitet (`ShouldForward = true`) |
| Mit `[QUESTION]` | Rückfrage an User, Flow stoppt (`ShouldForward = false`) |
#### Backend-Flow (Preparer)
1. **ChatEndpoint** – Prüft ob `ctx.Preparer` vorhanden
2. **AnthropicService.RunPreparer()** – Ruft `RunAgentChat()` auf mit `agentType = "preparer"`
3. **Entscheidung** – `ShouldForward` prüft ob `[QUESTION]` im Text:
- **Forward**: Aufbereiteter Text wird als `[Vorbereitet von AgentName]:`-User-Message in DB gespeichert, History wird neu geladen, StreamChat startet
- **Rückfrage**: SSE `preparer_done` mit `forwarded: false`, Stream endet mit `stopReason: "preparer_query"`, User muss erneut eingeben
#### Preparer-History
Der Preparer bekommt seine eigene History (`BuildHistoryFromDb(sessionId, includeTag: "preparer")`), damit er seine vorherigen Rückfragen und User-Antworten sieht. Der Session-Agent bekommt die History **ohne** Preparer-Messages (Tag-Filter).
#### System-Prompt
Der Preparer bekommt einen speziellen Instruktionsblock an seinen System-Prompt angehängt:
- Erklärt die Rolle als Vorbereiter
- Definiert `[QUESTION]`-Mechanismus für Rückfragen
- Instruiert aktive Vorarbeit mit eigenen Fähigkeiten und Tools
- Betont: Antworten werden automatisch weitergeleitet
#### SSE-Events (Preparer)
| Event | Daten | Beschreibung |
|-------|-------|-------------|
| `preparer_start` | `{ agent: "Name" }` | Preparer-Phase beginnt |
| `preparer_text` | `{ text: "..." }` | Preparer-Agent-Text (Token-Stream) |
| `preparer_tool_use` | `{ tool: "name", input: "..." }` | Preparer nutzt Tool |
| `preparer_done` | `{ forwarded: true/false }` | Preparer-Phase beendet |
### Backend-Flow (Review)
1. **ChatService.PrepareContext()** – Wenn `session.ReviewAgentId` gesetzt:
- Review-Agent aus DB laden
- Eigenen SystemPrompt für den Review-Agent bauen (Identity, Soul, Memory, Skills)
- Eigene Tool-Definitionen für den Review-Agent
- `ReviewContext` mit Model, SystemPrompt, Tools, Agent-Referenz erstellen
2. **ChatEndpoint** – Übergibt `ctx.Review` an `AnthropicService.StreamChat()`
3. **AnthropicService.StreamChat()** – Nach `end_turn` des Worker-Agents:
- Prüft ob `ReviewContext` vorhanden und `reviewRound < MaxReviewIterations`
- Ruft `RunReview()` auf
4. **RunReview()** – Orchestriert eine einzelne Review-Runde:
- Baut Review-Messages auf: Konversation als Context + Prüfanweisung
- SSE-Event `review_start` an Client senden
- Ruft `RunAgentChat()` auf (generische Methode für Review/Finalizer, eigener API-Call an Claude)
- Review-Agent kann Tools nutzen (eigener Tool-Loop mit max. 25 Iterationen)
- SSE-Events `review_text` und `review_tool_use` für Live-Streaming
- Prüft ob Antwort `[APPROVED]` enthält:
- **Ja** → SSE `review_approved`, return `HasFeedback = false`
- **Nein** → SSE `review_feedback`, return `HasFeedback = true` + Feedback-Text
5. **Zurück in StreamChat()** – Bei Feedback:
- Feedback als User-Message in DB speichern (mit `[Review-Feedback von AgentName (Runde X/Y)]` Prefix)
- Worker-Agent bekommt Feedback als nächste User-Message
- `continue` → nächste while-Loop-Iteration → Worker korrigiert → Review erneut
- `reviewRound++` bis `MaxReviewIterations` erreicht
### ReviewContext / FinalizerContext (Model)
```csharp
// Identische Struktur für Review und Finalizer
public class ReviewContext
{
public string Model { get; set; } // Claude-Modell des Agents
public string SystemPrompt { get; set; } // Eigener System-Prompt
public AgentRecord Agent { get; set; } // Agent-Record für Name/Avatar
public List<object> Tools { get; set; } // Tool-Definitionen
public int MaxReviewIterations { get; set; } // Max Runden (nur bei Review relevant)
}
// ChatContext hat: ReviewContext Review + ReviewContext Finalizer
```
### SSE-Events
| Event | Daten | Beschreibung |
|-------|-------|-------------|
| `review_start` | `{ agent: "Name" }` | Review-Phase beginnt |
| `review_text` | `{ text: "..." }` | Review-Agent-Text (Token-Stream) |
| `review_tool_use` | `{ tool: "name", input: "..." }` | Review-Agent nutzt Tool |
| `review_feedback` | `{ feedback: "..." }` | Review nicht bestanden, Feedback an Worker |
| `review_approved` | `{ message: "..." }` | Review bestanden |
### DB-Persistierung
- **Worker-Antworten**: Normale Messages (role=assistant, type=text, tag=null)
- **Preparer-Antworten**: Messages mit `tag="preparer"` (aus API-History gefiltert, nur für Frontend-Anzeige)
- **Aufbereiteter Prompt**: Als User-Message mit Prefix `[Vorbereitet von AgentName]:`
- **Review-Feedback**: Als User-Messages mit Prefix `[Review-Feedback von AgentName (Runde X/Y)]:`
- **Review-Agent-Antworten**: Messages mit `tag="review"` (aus API-History gefiltert)
- **Finalizer-Antworten**: Messages mit `tag="finalizer"` (aus API-History gefiltert)
### Frontend
**Session-Page** (`session-page.js`):
- Zweiter Agent-Picker: "Review-Agent" (nutzt `buildAgentCard()` + `openAgentPickerModal()`)
- Input für "Max. Review-Runden" (Number-Input, Default 3)
**Chat-UI** (`chat.js`):
- SSE-Event-Handler für `review_start`, `review_text`, `review_tool_use`, `review_feedback`, `review_approved`
- Review-Bubbles mit eigenem Styling (sekundäre Farbe, Agent-Name-Header)
**History-Rendering** (`history.js`, `bubbles.js`):
- `renderMarkdown` parst `[Review: AgentName]` → styled Header mit Icon
- `renderMarkdown` parst `[Review-Feedback von AgentName (...)]` → styled Feedback-Header
- `renderMarkdown` filtert `[APPROVED]` aus (wird ausgeblendet)
- History erkennt Review-Nachrichten und rendert sie als `.review-bubble` statt normaler Bubbles
- Review-Nachrichten werden nicht mit normalen Nachrichten gemergt
**Shared-Page** (`shared-page.js`):
- Gleiche Review-Bubble-Erkennung für geteilte Nachrichten
### Finalizer-Agent
Der Finalizer wird **nach** dem Review (oder direkt nach dem Worker, falls kein Review-Agent konfiguriert) aufgerufen. Er führt abschließende Arbeiten durch (z.B. Git-Commit, Deployment, Cleanup) – **ohne Feedback-Loop**.
#### Konfiguration
| DB-Feld | SessionRecord | Beschreibung |
|---------|---------------|-------------|
| `finalizer_agent_id` | `FinalizerAgentId` | ID des Finalizer-Agents (optional, null = kein Finalizer) |
#### Backend-Flow
1. **ChatService.PrepareContext()** – Wenn `session.FinalizerAgentId` gesetzt:
- Finalizer-Agent aus DB laden
- `FinalizerContext` aufbauen (analog zu `ReviewContext`: Model, SystemPrompt, Tools)
2. **AnthropicService.StreamChat()** – Nach Review-Phase (oder direkt nach Worker):
- Prüft ob `FinalizerContext` vorhanden
- Ruft `RunFinalizer()` auf
3. **RunFinalizer()** – Baut Messages auf (gesamte Konversation + Finalisierungs-Anweisung), ruft `RunAgentChat()` auf (generische Methode, auch vom Review genutzt)
4. **RunAgentChat()** – Generische Methode für Preparer/Review/Finalizer:
- Eigener API-Call an Claude mit Agent-Config
- Tool-Loop mit SSE-Events (`{type}_text`, `{type}_tool_use`)
- Antwort als Message in DB persistieren (mit `Tag = agentType`, wird aus API-History gefiltert)
#### SSE-Events
| Event | Daten | Beschreibung |
|-------|-------|-------------|
| `finalizer_start` | `{ agent: "Name" }` | Finalizer-Phase beginnt |
| `finalizer_text` | `{ text: "..." }` | Finalizer-Agent-Text (Token-Stream) |
| `finalizer_tool_use` | `{ tool: "name", input: "..." }` | Finalizer nutzt Tool |
| `finalizer_done` | `{}` | Finalizer-Phase beendet |
#### Frontend
- **Session-Page**: Vierter Agent-Picker: "Vorbereiter-Agent", "Review-Agent", "Finalizer-Agent" im "Weitere Einstellungen"-Bereich
- **Chat-UI**: Preparer-Bubbles (`.preparer-bubble`, Info-Farbe, 🔍-Icon), Finalizer-Bubbles (`.finalizer-bubble`, grüne Farbe, 🏁-Icon)
- **History**: `[Preparer: AgentName]` → styled Header mit `fa-filter` Icon, `[Vorbereitet von AgentName]` → styled Forwarded-Header, `[Finalizer: AgentName]` → styled Header, `[FORWARD]`/`[QUESTION]` werden ausgeblendet
- **Dashboard**: 🏁-Badge auf Session-Cards mit Finalizer
---
## Interaktive Rückfragen (ask_user_choice)
Das `ask_user_choice`-Tool ermöglicht dem Agent, dem User **klickbare Auswahlmöglichkeiten** zu präsentieren, wenn es mehrere sinnvolle Wege gibt oder eine Entscheidung vom User getroffen werden muss.
### Tool-Definition
```json
{
"name": "ask_user_choice",
"description": "Stellt dem User eine Frage mit Auswahlmöglichkeiten",
"input_schema": {
"properties": {
"question": { "type": "string" },
"choices": { "type": "array", "items": { "type": "string" }, "minItems": 2, "maxItems": 5 }
}
}
}
```
### Agent-Konfiguration
- **`allow_user_choice`** (AgentRecord) – Boolean, Default `true`. Steuert ob das Tool verfügbar ist.
- Dashboard-Badge: `🔵 Rückfragen` wenn aktiv
- System-Prompt-Hinweis: "Nutze `ask_user_choice` sparsam – nur wenn die Entscheidung vom User getroffen werden soll"
- Bei Review/Finalizer/Cronjobs: **Immer deaktiviert** (kein User zum Fragen)
### Backend-Flow
1. Claude ruft `ask_user_choice` auf → Backend erkennt im `ProcessStream`
2. Tool wird **nicht ausgeführt** – stattdessen SSE-Event `user_choice` gesendet
3. `stopReason` wird auf `"awaiting_user_choice"` gesetzt (Guard-Clause verhindert Überschreibung durch `message_delta`)
4. `tool_use`-Block wird in DB gespeichert, Chain wird finalisiert
5. User wählt Option → Frontend sendet `POST /api/chat` mit `choiceToolCallId` + `choiceResult`
6. Backend findet die Chain mit dem offenen `tool_use`, speichert `tool_result` dort
7. Neuer `StreamChat` startet – History enthält `tool_use` + `tool_result`, Claude arbeitet weiter
### Frontend-Flow
- **Live**: SSE-Event `user_choice` → `addChoiceBubble()` rendert Frage + klickbare Buttons
- **History**: `tool_use` mit `toolName === 'ask_user_choice'` → Voraus-Suche nach `tool_result`:
- Gefunden: `addChoiceBubbleStatic()` mit ausgewählter Option (hervorgehoben, andere ausgegraut)
- Nicht gefunden: Aktive Buttons (klickbar, z.B. nach Page-Refresh)
- **Robustheit**: Guards gegen `choices` als String statt Array (`Array.isArray()` Check)
- **CSS**: `.choice-bubble`, `.choice-btn` (Primary-Color-Outline), `.choice-selected`/`.choice-unselected`
### SSE-Events
| Event | Daten | Beschreibung |
|-------|-------|-------------|
| `user_choice` | `{ toolCallId, question, choices[] }` | Rückfrage mit Auswahloptionen |
---
## Plan-Modus
Der Plan-Modus ist ein **Read-only-Betriebsmodus**, in dem der Agent den Code analysieren, Rückfragen stellen und Pläne erstellen kann – aber keine Dateien ändern oder Befehle ausführen darf.
### Aktivierung
- **Frontend**: Toggle-Button (🗺️ `fa-map`) im Chat-Input-Bereich
- **API**: `planMode: true` im `ChatRequest`
- **Pro Chain**: `plan_mode` Feld in der Chain (rein informativ für History-Anzeige)
- Kein Agent-Setting – wird **pro Nachricht** vom User gesteuert
### Backend-Auswirkungen
| Aspekt | Normal | Plan-Modus |
|--------|--------|-----------|
| **Tools** | Alle (read_file, write_file, edit_file, delete_file, execute, ...) | Nur read-only: `read_file`, `list_files`, `glob`, `grep`, `execute`, `todo_write`, `memory_*`, `ask_user_choice` |
| **System-Prompt** | Normal | + Plan-Modus-Block: "Du darfst NUR lesen und analysieren, KEINE Änderungen vornehmen" |
| **toolChoice** | `"any"` (automatisch wenn Workspace/Skills/Memory) | Immer `"auto"` (Claude darf frei antworten) |
| **Chain-Speicherung** | `plan_mode = 0` | `plan_mode = 1` |
### System-Prompt-Anpassungen im Plan-Modus
Folgende Blöcke werden **übersprungen**: Git-Commits. Der Tools-Block listet nur Read-only-Tools auf. Skills werden als "nur zur Kenntnis, nicht ausführbar" markiert.
### Frontend
- **Toggle**: `#planModeBtn` – wechselt `_planModeActive`, ändert Button-Styling + Placeholder-Text
- **Chain-Meta**: Badge `📋 Plan-Modus` bei Chains die im Plan-Modus erstellt wurden
- **System-Prompt-Modal**: Zeigt den angepassten System-Prompt wenn Plan-Modus aktiv
- **Session-Wechsel**: Plan-Modus wird zurückgesetzt
### Choice-Response im Plan-Modus
Wenn im Plan-Modus eine `ask_user_choice`-Rückfrage kommt und der User antwortet, erbt die Choice-Response den `planMode` des vorherigen Chains.
---
## Web-Search
Anthropic Claude unterstützt Web-Suche als Server-Tool. Das System integriert Web-Search mit Source-Tracking und Rendering.
### Backend
- **Streaming**: `server_tool_use` (Suchanfrage) → `web_search_tool_result` (Ergebnisse mit Sources)
- **Sources extrahieren**: Aus `web_search_tool_result`-Block werden `url` + `title` als JSON-Array extrahiert
- **Persistierung**: `web_search_result`-Messages in DB (type=`web_search_result`, content = JSON-Array der Sources). Leere Ergebnisse (Sources=[]) werden **nicht** gespeichert.
- **SSE-Events**: `web_search_start` (Suche beginnt) → `web_search_end` (Suche fertig, mit Sources)
### Frontend – Streaming
- `web_search_start` → Tool-Box mit Spinner erstellen, Stream-Bubble **pausieren** (nicht finalisieren)
- `web_search_end` → Tool-Box mit Sources finalisieren
- `text_start` → in bestehende Bubble weiter schreiben (kein neuer Block)
- `pauseStreamBubble()` / `endStreamBubble()` Konzept: Text-Blöcke zwischen Web-Searches werden in **einer** Bubble zusammengefasst
- `text_end` pausiert nur (Cursor weg), `endStreamBubble()` nur bei finalen Events (`done`, `tool_start`, `error`)
### Frontend – History
- `web_search_result`-Messages werden als Tool-Box mit klickbaren Source-Links gerendert (Domain + Title)
- `_mergeConsecutiveAssistantTexts()` fügt Text-Messages zusammen, `web_search_result`-Messages dazwischen unterbrechen die Merge-Kette nicht
- Leere `web_search_result`-Messages (Sources=[]) werden übersprungen
### Frontend – Shared-Page
- `SharedEndpoint` gibt `web_search_result`-Messages mit aus (Typ-Filter: `text` + `web_search_result`)
- `shared-page.js` rendert `web_search_result` als Tool-Box mit Source-Links
- Merge-Funktion überspringt `web_search_result` beim Rückwärts-Suchen, damit Texte zusammengeführt werden
---
## Nachrichten teilen (Shared Messages)
Einzelne Nachrichten oder ganze Threads können als **öffentliche Links** geteilt werden – ohne Login aufrufbar.
### Datenmodell
**Tabelle `shared_messages`:**
| Spalte | Typ | Beschreibung |
|------------------|---------|-------------------------------------------|
| `id` | INTEGER | Primary Key |
| `share_token` | TEXT | UUID – öffentlicher Zugangsschlüssel |
| `message_id` | INTEGER | FK auf `messages.id` |
| `user_id` | INTEGER | Wer hat geteilt |
| `include_context`| INTEGER | 0 = Einzelnachricht, 1 = Thread bis dahin |
| `created_at` | TEXT | Erstellzeitpunkt |
| `expires_at` | TEXT | Optional: Ablaufdatum |
### API-Endpunkte (`SharedEndpoint.cs`)
| Methode | Route | Auth | Beschreibung |
|----------|--------------------------------|------|---------------------------------------|
| `POST` | `/api/messages/{id}/share` | ✅ | Share erstellen (Body: `{includeContext}`) |
| `GET` | `/api/shared/{token}` | ❌ | Öffentlich: Geteilte Nachricht abrufen |
| `GET` | `/api/shared` | ✅ | Eigene Shares auflisten |
| `DELETE` | `/api/shared/{token}` | ✅ | Share-Link widerrufen |
### Frontend-Flow
1. **Share-Button** (🔗) erscheint an jeder Nachricht im Chat (neben Reply, Delete, Mark)
2. Klick öffnet **Kontext-Menü**: "Einzelne Nachricht" oder "Thread teilen"
3. `POST /api/messages/{id}/share` → Token zurück → URL in Zwischenablage kopiert
4. **Toast**: "Link kopiert!"
### Öffentliche Ansicht (`#/shared/{token}`)
- Route `#/shared/{token}` → `shared-page.js`
- **Kein Auth nötig** – Auth-Bypass in `app.js` + `AuthMiddleware`
- Minimales Layout: Header ("// gott · Geteilte Nachricht"), Nachrichten mit Markdown-Rendering
- Zwei Modi: `single` (eine Nachricht) oder `context` (Thread mit User/Assistant-Wechsel)
---
## Workspace & File-Explorer
### Workspace
Eine Session kann einen **Workspace-Pfad** haben. Dieser wird **relativ** zum `DefaultPath` des Users gespeichert (z.B. `myapp`) und via `WorkspaceHelper.ResolveWorkspace()` zum absoluten Pfad aufgelöst. Der Workspace:
- Aktiviert die Datei-/Shell-Tools für den Agent
- Ermöglicht den File-Explorer im Frontend
- Bietet `git status` Informationen
- Erlaubt das Ausführen von Shell-Befehlen (foreground + background)
### WorkspaceHelper
`Services/WorkspaceHelper.cs` – Statische Hilfsklasse für Workspace-Pfade:
| Methode | Beschreibung |
|----------------------------|-----------------------------------------------------------|
| `ResolveWorkspace(session, user)` | Relativer Workspace + DefaultPath → absoluter Pfad |
| `ValidateRelativeWorkspace(workspace)` | Prüft auf `..`, `~`, absolute Pfade → null bei ungültig |
| `ToRelativeWorkspace(absolute, defaultPath)` | Absoluter Pfad → relativ zum DefaultPath |
### File-Explorer API
| Endpoint | Beschreibung |
|-------------------------------------|------------------------------------------|
| `GET /api/workspace/tree` | Verzeichnisbaum (mit Ignore-Filtern) |
| `GET /api/workspace/file` | Dateiinhalt lesen (max 2MB) |
| `PUT /api/workspace/file` | Datei speichern |
| `POST /api/workspace/mkdir` | Ordner erstellen im Workspace |
| `DELETE /api/workspace/dir` | Leeren Ordner löschen im Workspace |
| `GET /api/workspace/browse` | Verzeichnis-Browser (für Pfad-Auswahl) |
| `POST /api/workspace/browse/mkdir` | Ordner erstellen per absolutem Pfad |
| `GET /api/workspace/raw` | Raw-Datei ausliefern (Bilder, PDFs) |
| `GET /api/workspace/agent/tree` | Agent-Memory-Verzeichnis |
| `GET/PUT /api/workspace/agent/file` | Agent-Memory-Dateien lesen/schreiben |
| `POST /api/workspace/agent/mkdir` | Ordner erstellen im Agent-Memory |
### Ignored Directories
`bin`, `obj`, `node_modules`, `.git`, `.vs`, `.idea`, `publish`, `dist`, `out`, `build`, `target`, `.cache`
### Session-Exec
`POST /api/sessions/{id}/exec` führt Shell-Befehle im Workspace aus:
- **foreground**: Synchron, wartet auf Ergebnis (30s Timeout)
- **background**: Fire-and-forget via `systemd-run`
---
## Avatar-System
Sessions und Agents können ein **Bild/Avatar** haben mit konfigurierbarem Zoom.
### Datenfelder
| Feld | Tabellen | Beschreibung |
|------|----------|-------------|
| `image` / `avatar` | `sessions` / `agents` | Base64-kodiertes Bild (WebP, max 512px via ImageService) |
| `image_small` / `avatar_small` | `sessions` / `agents` | Thumbnail (WebP, 256px, Q70) für Listen/Dashboard |
| `avatar_scale` | `sessions`, `agents` | Zoom-Faktor (0.5× – 3×, Default 1.0) |
| `avatar_offset_x` | `sessions`, `agents` | Horizontale Position (-50% – +50%, Default 0), via CSS `translate()` |
| `avatar_offset_y` | `sessions`, `agents` | Vertikale Position (-50% – +50%, Default 0), via CSS `translate()` |
Darstellung via CSS `transform: scale(S) translate(X%, Y%)` im `.card-image` Container (`overflow: hidden`).
### Bild-Verarbeitung (ImageService)
Alle Bilder werden beim Upload in **WebP** konvertiert (statt PNG), was ~60-80% Speicherersparnis bringt:
| Variante | Methode | Max. Größe | Qualität | Verwendung |
|----------|---------|------------|----------|------------|
| Voll | `ResizeDataUrl()` | 512px | Q80 | Editor, Detail-Ansicht (`GET /api/agents/{id}`, `GET /api/sessions/{id}`) |
| Thumbnail | `ResizeDataUrlSmall()` | 256px | Q70 | Listen, Dashboard (`GET /api/agents`, `GET /api/sessions`) |
Die Listen-Endpoints liefern das Thumbnail als `avatar`/`image`-Feld (Fallback auf Voll-Bild wenn kein Thumbnail existiert). Die Detail-Endpoints liefern das volle Bild.
### Avatar-Editor (`avatar-editor.js`)
Wiederverwendbares Modal-Modul, genutzt in Session- und Agent-Pages:
```javascript
const editor = AvatarEditor.init({
previewEl: document.querySelector('.avatar-preview'),
currentImage: existingBase64OrNull,
currentScale: 1.0,
initialOffsetX: 0,
initialOffsetY: 0,
onChange: (imageData, scale, offsetX, offsetY) => { /* Live-Preview */ }
});
// Werte auslesen: editor.getImage(), editor.getScale(), editor.getOffsetX(), editor.getOffsetY()
// Aufräumen: editor.destroy()
```
**Features:**
- **Upload**: Datei-Input für Bild-Upload
- **Paste**: Ctrl+V → Bild aus Zwischenablage einfügen
- **AI-Generate**: Text-Prompt → Krea API → Bild generieren (nur wenn `KreaToken` konfiguriert)
- **Scale**: Range-Slider 0.5× – 3× mit Live-Preview
- **Offset X/Y**: Range-Slider -50% – +50% für horizontale/vertikale Positionierung
- **Clear**: Bild entfernen
### Dashboard-Darstellung
Cards zeigen Avatare mit `transform: scale(S) translate(X%, Y%)` im `.card-image` Container (`overflow: hidden`). Die Helper `buildAgentCard()` und `buildSessionCard()` in `cards.js` nutzen `buildTransformStyle(scale, offsetX, offsetY)` dafür.
---
## LaTeX/Math-Rendering
Chat-Nachrichten unterstützen **LaTeX-Formeln** via KaTeX:
### Syntax
- `$$...$$` → **Block-Math** (zentriert, eigene Zeile)
- `$...$` → **Inline-Math** (im Fließtext)
### Rendering-Pipeline (in `markdown.js` `renderMarkdown()`)
```
1. Code-Blöcke extrahieren und schützen (Platzhalter)
2. Block-LaTeX ($$...$$) extrahieren und schützen
3. Inline-LaTeX ($...$) extrahieren und schützen
4. Markdown-Rendering via marked.js
5. Code-Blöcke wiederherstellen
6. LaTeX-Platzhalter durch KaTeX.renderToString() ersetzen
7. Bei fehlerhaftem LaTeX: Fallback als <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:
```sql
sort_key = CASE WHEN tag = 'openclaw-import' THEN id - 10000000000 ELSE id END
```
Alle History-Queries (initial load, before/after-Paging, Polling) sortieren nach `sort_key`. OpenClaw-Messages bekommen negative sort_keys → erscheinen immer ganz oben. Innerhalb jeder Gruppe (OpenClaw / normal) wird nach `id` sortiert.
### Frontend
- **Dashboard**: Session-Menü → „OpenClaw Import" → Modal mit Session-Auswahl + Preview
- **Chat**: Importierte Nachrichten zeigen ein blaues „OC"-Badge
---
## Deployment
**WICHTIG:** Das Deployment ist ein **manueller Vorgang**, der durch den Benutzer auf dem Server durchgeführt werden muss. Das System führt kein automatisches Deployment durch.
### Goldene Regel für Änderungen
* **Arbeiten Sie AUSSCHLIESSLICH im Verzeichnis `wwwroot/`** für Frontend-Änderungen.
* **Editiere NIEMALS Dateien in `Build/*/publish/wwwroot/`!** Diese Verzeichnisse werden bei jedem Build überschrieben.
* Das Verzeichnis `Build/*/publish/data/gott.db` darf niemals manuell gelöscht, geleert oder direkt modifiziert werden.
### Deployment-Vorgang (Beispiel für Linux)
Das vorhandene Shell-Skript `deploy.sh` dient lediglich als Hilfestellung für den manuellen Prozess:
1. **Frontend-Anpassungen vornehmen** (in `wwwroot/`).
2. **Manuelles Deployment** auf dem Server ausführen:
```bash
# Beispiel-Ablauf:
systemctl stop gott
dotnet publish -c Release -o Build/Linux/publish
systemctl start gott
```
### systemd
Die Anwendung ist als systemd-Service konfiguriert und läuft standardmäßig auf Port 19000.
---
## Konfiguration
### appsettings.json
```json
{
"Port": 19000, // Server-Port (Default 19000, erfordert Neustart)
"AnthropicToken": "", // Anthropic API Key (REQUIRED)
"MaxIterations": 100, // Max Tool-Loop Iterationen pro Chat
"PayloadLogging": false, // API-Payloads loggen (Debug)
"PayloadLoggingGzip": false, // Payload-Logs als .gz komprimieren
"KreaToken": "", // Krea API Token (optional)
"KreaApiKey": "" // API-Key für Krea-Endpoints ohne User-Auth (optional)
}
```
### appsettings.Production.json
Überschreibt Werte aus `appsettings.json` im Production-Environment. Wird **nicht** beim Publish mitkopiert (liegt nur im `Build/<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. **Bild-Migration** – Konvertierung bestehender PNG/JPEG-Avatare zu WebP + Generierung von Thumbnails (`avatar_small`, `image_small`)
Dieses Pattern ermöglicht:
- Idempotente Ausführung bei jedem App-Start
- Keine separate Migrations-Verwaltung nötig
- Neue Features können einfach Spalten hinzufügen
- Abwärtskompatibel – bestehende Daten bleiben erhalten