Files
gott/ARCHITECTURE.md
T
Meleki f4dc5fd812 docs: Dokumentation aktualisiert – Preparer, ask_user_choice, Plan-Modus, TaskInstruction, Message-Tag
ARCHITECTURE.md:
- Multi-Agent System: 'Review + Finalizer' → 'Preparer + Review + Finalizer'
- Neue Sektion: Vorbereiter-Agent (Preparer) mit Flow, SSE-Events, History-Logik
- Neue Sektion: Interaktive Rückfragen (ask_user_choice) mit Tool-Definition, Backend/Frontend-Flow
- Neue Sektion: Plan-Modus mit Tool-Filterung, System-Prompt-Anpassungen
- System-Prompt-Aufbau: 12 → 14+ Blöcke (Anweisungen, Rückfragen, Plan-Modus, Preparer)
- Tool-System: ask_user_choice + Plan-Modus Read-only Filter dokumentiert
- Agent-System: AllowUserChoice + TaskInstruction Felder
- Datenmodell: preparer_agent_id, plan_mode, Tag, neue StopReasons
- DB-Persistierung: Tag-Markierung für Sub-Agent-Messages

ARCHITECTURE_QUICK.md:
- Sessions/Chains/Messages Tabellen aktualisiert
- Chat-Pipeline: Preparer-Phase + ask_user_choice ergänzt
- Tool-System: 12 → 13 Tools
- System-Prompt: 12 → 14+ Blöcke
- Multi-Agent: Preparer + Tag-System

README.md:
- Features: Vorbereiter-Agent, Interaktive Rückfragen, Plan-Modus
- 12 → 13 Tools
2026-03-07 18:46:29 +01:00

1519 lines
75 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)
20. [Workspace & File-Explorer](#workspace--file-explorer)
21. [Avatar-System](#avatar-system)
22. [LaTeX/Math-Rendering](#latexmath-rendering)
23. [Windows Desktop App](#windows-desktop-app)
24. [Deployment](#deployment)
25. [Konfiguration](#konfiguration)
26. [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)
│ ├── 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 (Avatar, Session-Image)
│ ├── 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 # Webhook, Config, Registration
│ ├── 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)
│ ├── 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.js # UI-Utilities (Modal, Toast, etc.)
│ │ ├── chat.js # Chat-Logic, SSE-Streaming
│ │ ├── history.js # History-Rendering, Pagination
│ │ ├── 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
│ │ │ ├── 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
│
└── publish/ # Build-Output (NICHT editieren!)
└── 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
```
### Tabellen
| Tabelle | Beschreibung |
|-------------------|-----------------------------------------------------|
| `users` | Benutzer (username, password_hash, persona, settings, default_path) |
| `tokens` | Auth-Tokens (token → user_id) |
| `agents` | AI-Agenten (model, soul, identity, memory, tools, avatar_scale, avatar_offset_x/y, allow_user_choice, task_instruction) |
| `sessions` | Chat-Sessions (workspace, context, telegram-config, avatar_scale, avatar_offset_x/y, telegram_allowed_chat_ids, preparer_agent_id, review_agent_id, max_review_iterations, finalizer_agent_id) |
| `chains` | Request-Chains (model, tokens, iterations, timing) |
| `messages` | Einzelne Nachrichten (text, tool_use, tool_result, web_search_result) |
| `attachments` | Datei-Anhänge (base64, image/document) |
| `archives` | Archivierte Chain-Gruppen |
| `agent_skills` | Agent ↔ Skill Zuordnung |
| `cronjobs` | Geplante Tasks (cron expression, prompt) |
| `shared_messages` | Geteilte Nachrichten (Share-Token, Ablaufdatum) |
| `telegram_links` | Telegram Chat ↔ Session Verknüpfung |
### Zentrale Records
**ChainRecord** – Repräsentiert einen kompletten Request-Response-Zyklus:
- `ChainId` – Eindeutige ID
- `SessionId` – Zugehörige Session
- `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`
- `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`
- `Type` – `text`, `tool_use`, `tool_result`, `error`, `web_search_result`
- `Content` – Textinhalt oder JSON (bei tool_use: Input-JSON)
- `ToolName`, `ToolCallId` – Bei Tool-Aufrufen
- `IsMarked` – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)
- `Tag` – Sub-Agent-Markierung (`null` = normal, `"preparer"/"review"/"finalizer"` = Sub-Agent-Message, wird aus API-History gefiltert)
- `DeletedOn` – Soft-Delete Zeitstempel
---
## 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 |
| `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 (max 512px) 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` |
| **Agents** | `/api/agents/` | CRUD |
| **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/` | `POST webhook/{sessionId}` (unauthenticated), Session-Config CRUD, Webhook-Management |
| **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/{sessionId}` (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
- 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 |
| `#/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.js** – UI-Utilities (Modals, Toasts, Confirmations), Card-Builder (`buildAgentCard()`, `buildSessionCard()`), Markdown-Rendering-Helpers (`esc()`, `prettyModelName()`)
- **chat.js** – SSE-Stream-Handling, Markdown-Rendering (mit KaTeX), Tool-Output-Darstellung
- **history.js** – Chat-History laden, paginieren, rendern
- **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 (ui.js)
- **`buildAgentCard(agent, skills)`** – Erzeugt ein `.card` DOM-Element für einen Agent (Avatar, Name, Model-Badge, Feature-Badges: toolChoice, autoCommit, useSoul, useIdentity, useUserInfo, useMemory, webSearch, Skills). 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). Wird im Dashboard und im Session-Picker-Modal (Cronjob-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:
- **Ä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)
---
## 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 Strategien
- **`any`**: Erste Iteration erzwingt Tool-Nutzung, danach `auto`. Verhindert dass der Agent nur Text antwortet wenn Tools benötigt werden.
- **`auto`**: Claude entscheidet frei ob Tools nötig sind.
### 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 (auf 512px resized) |
| `Model` | `sonnet`, `sonnet-4-5`, `sonnet-4-6`, `opus`, `opus-4-5`, `opus-4-6`, `haiku`, `haiku-4-5` |
| `ToolChoice` | `any` oder `auto` |
| `CustomCommands` | JSON-Array mit Slash-Commands (Label + Prompt) |
| `AutoCommit` | `""` (nichts), `"mention"` (verbieten), `"enforce"` (erzwingen), `"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 |
### 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. Tool-Nutzung (Workspace + any) │ → "IMMER Tools benutzen"
│ 6. Workspace-Info │ → Tools + Workspace-Pfad
│ 7. Rückfragen (AllowUserChoice) │ → Hinweis zu ask_user_choice Tool
│ 8. Session-Kontext │ → Freiformtext
│ 9. Skills (Agent-Skills) │ → Skill-Prompts
│10. Memory (UseMemory) │ → Memory-Regeln + MEMORY.md Inhalt
│11. Git-Verhalten (AutoCommit) │ → Commit-Regeln
│12. Verhaltensregeln (Workspace + any) │ → "IMMER Tools aufrufen"
│13. Erinnerung (Workspace + any) │ → Höchste Priorität: Tool-Aufrufe!
│14. Markierte Nachrichten │ → Vom User als wichtig markierte Messages
│ [Plan-Modus-Block] │ → Nur wenn planMode=true (überspringt 5,11,12,13)
│ [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
Sessions können mit einem **Telegram Bot** verbunden werden:
### Konfiguration
Pro Session werden gespeichert:
- `TelegramBotToken` – Bot-Token von @BotFather
- `TelegramWebhookSecret` – Geheimer Token für Webhook-Verifizierung
- `TelegramChatId` – Verknüpfte Chat-ID (Owner)
- `TelegramAllowedChatIds` – Kommagetrennte Chat-ID-Whitelist für Zugriffskontrolle
### Webhook-Flow
```
1. User konfiguriert Bot-Token in Session-Settings
2. User registriert Webhook (POST .../telegram/register-webhook)
→ Setzt Webhook-URL: {baseUrl}/api/telegram/webhook/{sessionId}
3. Telegram sendet Updates an Webhook
4. AuthMiddleware lässt Webhook-Requests durch (kein Cookie nötig)
5. TelegramEndpoint prüft X-Telegram-Bot-Api-Secret-Token Header
6. TelegramService verarbeitet Update:
- /start → Session verknüpfen (nur wenn noch kein Owner ODER Whitelist-Match)
- /clear → History archivieren
- /status → Session-Info anzeigen
- /unlink → Verknüpfung aufheben
- Text → An Claude weiterleiten (Non-Streaming)
```
### Zugriffskontrolle (Chat-ID-Whitelist)
Nicht jeder Telegram-User darf den Bot nutzen:
1. **Erster `/start`**: Kein Owner → User wird automatisch Owner (`TelegramChatId` gesetzt)
2. **Fremder `/start`**: User bekommt `🔒 Zugriff verweigert` + seine Chat-ID angezeigt
3. **Owner-Benachrichtigung**: Owner erhält `🔔 Zugriffsanfrage` mit Username + Chat-ID
4. **Freigabe**: Owner/Admin trägt Chat-ID in Session-Settings ein (`TelegramAllowedChatIds`)
5. **Prüfung**: Alle Commands + Nachrichten prüfen via `IsChatIdAllowed()` (Owner + Whitelist)
### 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.
---
## 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`, `ui.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"` (wenn Agent so konfiguriert) | 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**: KRITISCH (Tool-Nutzung erzwingen), Verhalten ("handle sofort"), Erinnerung ("MUSS Tool aufrufen"), 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 (JPEG, max 512px via ImageService) |
| `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`).
### 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 `ui.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 `ui.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
---
## Deployment
### deploy.sh
```bash
#!/bin/bash
set -e
cd /app/gott
export PATH="$HOME/.dotnet:$PATH"
systemctl stop gott
dotnet publish -c Release -o publish
systemctl start gott
echo "OK"
```
### Verzeichnisse
- **Quell-Code**: `/app/gott/` (wwwroot/, Services/, etc.)
- **Build-Output**: `/app/gott/publish/`
- **Laufzeit-Daten**: `/app/gott/publish/data/` (DB, Agents, Images, Skills)
- **WICHTIG**: Änderungen IMMER in `wwwroot/` machen, NIEMALS in `publish/wwwroot/`
### systemd
Die Anwendung läuft als systemd Service (`gott.service`), Kestrel 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 `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`)
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