Workflow-status: a new WorkflowProgressTracker singleton taps the
suppressed dynamic-workflow subagent events (those carrying
parent_tool_use_id) into per-session telemetry without altering the
existing guard; the snapshot is serialized into the new chains.workflow_info
column on FinalizeChain (ChainId-matched so a panel member of the same
session cannot clear it) and exposed via GET /api/sessions/{id}/workflow-status
(live -> persisted -> none). The header menu gains a 'Workflow-Status' entry
with a modal that live-polls every 2s while running and self-stops on close.
Panel on-demand: Fusion/MoA is now a transient per-request multiModelMode
flag (like plan/ultracode) instead of a persisted session column. Session
settings keep only the panel members + moa_layers; the mode <select> was
removed and the composer dropdown drives the mode per request.
Docs: ARCHITECTURE.md, ARCHITECTURE_QUICK.md and README.md updated for both
features plus the already-shipped z.ai/GLM provider, GLM 5.2+ effort
passthrough, web-search-prime MCP and the ultracode toggle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
396 lines
31 KiB
Markdown
396 lines
31 KiB
Markdown
# ARCHITECTURE_QUICK.md – Schnellreferenz
|
||
|
||
> Kurzfassung der [ARCHITECTURE.md](ARCHITECTURE.md) für schnelles Nachschlagen beim Entwickeln.
|
||
|
||
---
|
||
|
||
## Tech-Stack
|
||
|
||
**.NET 10** Minimal API · **SQLite** + Dapper · **Anthropic Claude API** (SSE Streaming + Claude CLI) · **OpenAI-kompatible APIs** (Nvidia, Pollinations, Gemini, OpenAI) · **ONNX Runtime** (Sentence-Embeddings) · **Vanilla JS** SPA · **KaTeX** (Math) · **Kestrel** auf Port 19000
|
||
|
||
---
|
||
|
||
## Verzeichnisstruktur (nur Schlüssel-Dateien)
|
||
|
||
```
|
||
gott/
|
||
├── Program.cs # Startup, DI, Middleware, Routing
|
||
├── appsettings.json # Tokens, API-Keys, Limits
|
||
├── Models/ # Records (reine Daten, inkl. ApiKeyRecord)
|
||
├── Services/ # Business-Logic (Singletons + Static Helpers)
|
||
│ ├── DatabaseService*.cs # Partial Classes (Core, Sessions, Agents, Users, Integrations)
|
||
│ ├── AnthropicService*.cs # Partial Classes (Core, Streaming, Agents, Cli)
|
||
│ ├── ClaudeCliService.cs # Claude Code CLI Wrapper (OAuth-basiert)
|
||
│ ├── SseHelper.cs # Shared SSE-Utilities
|
||
│ ├── EmbeddingService.cs # Lokales Sentence-Embedding (ONNX)
|
||
│ ├── VectorSearchService.cs # Semantische Suche
|
||
│ ├── RemoteConnectionManager.cs # WebSocket Remote-Verbindungen
|
||
│ ├── AppPaths.cs # Zentrale Pfade (WwwRoot, Dev vs. Publish)
|
||
│ ├── KeyScannerService.cs # GitHub API-Key-Scanner
|
||
│ ├── GitHubEventScannerService.cs # Echtzeit-Scanner (GitHub Events API)
|
||
│ ├── KeyPatterns.cs # Zentrale Key-Pattern-Definitionen (Regex + Typ-Erkennung)
|
||
│ ├── KeyCheckDispatcher.cs # Zentraler Key-Check-Dispatcher (alle Provider)
|
||
│ ├── AnthropicKeyChecker.cs # Anthropic Key-Validierung + Usage-Auswertung
|
||
│ ├── OpenAiKeyChecker.cs # OpenAI Key-Validierung
|
||
│ ├── GoogleKeyChecker.cs # Google/Gemini Key-Validierung
|
||
│ └── StripeKeyChecker.cs # Stripe Key-Validierung
|
||
├── Endpoints/ # HTTP API (Minimal API MapXxx-Pattern)
|
||
├── Middleware/ # AuthMiddleware
|
||
├── Templates/ # Vorlagen für neue Agents (IDENTITY, SOUL, USER)
|
||
├── wwwroot/ # ⚠️ HIER Frontend editieren!
|
||
│ ├── js/app.js # App-Init
|
||
│ ├── js/router.js # Hash-basiertes SPA-Routing
|
||
│ ├── js/api.js # fetch()-Wrapper
|
||
│ ├── js/ui-helpers.js # DOM-Helpers ($, esc, truncate), Toast, ScrollDown
|
||
│ ├── js/markdown.js # Markdown-Rendering (renderMarkdown, LaTeX-Schutz)
|
||
│ ├── js/cards.js # Card-Builder (buildSessionCard, buildAgentCard, buildTelegramBotCard)
|
||
│ ├── js/msg-actions.js # Nachrichten-Aktionen (Delete, Mark, Share, Reply)
|
||
│ ├── js/tool-ui.js # Tool-Boxes, Diffs, Visibility-Logik
|
||
│ ├── js/modals.js # Modale Dialoge (System-Prompt, Command-Output)
|
||
│ ├── js/stream-ui.js # Stream-Bubble, Review-Streaming
|
||
│ ├── js/bubbles.js # Message Bubbles (User, Assistant, Error, Choice)
|
||
│ ├── js/chat.js # SSE-Stream-Handling, sendMessage, Plan-Mode
|
||
│ ├── js/history.js # History laden, Polling, Rendering
|
||
│ ├── js/archive.js # Archiv-Loading
|
||
│ ├── js/reply.js # Reply/Quote-Handling
|
||
│ ├── js/files.js # File-Upload, Workspace-Refs
|
||
│ ├── js/typing-phrases.js # Zufällige Typing-Indicator-Texte
|
||
│ ├── js/pages/ # Seiten-Komponenten (inkl. api-keys-page.js, telegram-bot-page.js, db-explorer-page.js)
|
||
│ └── css/ # Styles (theme.css = CSS Variables)
|
||
├── WindowsApp/ # WPF + WebView2 Desktop Client
|
||
└── Build/ # ⛔ NICHT editieren!
|
||
├── Linux/
|
||
│ ├── bin/ obj/ publish/
|
||
└── Windows/
|
||
├── bin/ obj/ publish/
|
||
└── data/
|
||
├── gott.db # SQLite DB
|
||
├── agents/{id}/ # Memory-Dateien pro Agent
|
||
├── skills/{slug}/ # Skill-Dateien
|
||
└── images/ # Krea-generierte Bilder
|
||
```
|
||
|
||
---
|
||
|
||
## Datenmodell (Kurzform)
|
||
|
||
```
|
||
User 1──* Agent 1──* Session 1──* Chain 1──* Message 1──* Attachment
|
||
Agent *──* Skill (via agent_skills)
|
||
Session 1──* Cronjob
|
||
Session 1──* Archive
|
||
User 1──* SharedMessage ──1 Message
|
||
User 1──* TelegramBot 1──* Session (via telegram_bot_id)
|
||
```
|
||
|
||
| Tabelle | Zweck |
|
||
|---------|-------|
|
||
| `users` | Login, DisplayName, IsAdmin, Persona, Settings (JSON), DefaultPath |
|
||
| `tokens` | Auth-Tokens → User |
|
||
| `agents` | AI-Agenten (Model – z.B. `claude-sonnet-4-6` oder `nvidia:model-id`, CustomCommands, AutoCommit, UseSoul/Identity/UserInfo/Memory/AutoContext, UseGitHubSearch, UseSerperSearch, WebSearch, AllowUserChoice, TaskInstruction, Effort, AvatarScale/Offset, AvatarSmall, IsPublic) |
|
||
| `sessions` | Chat-Sessions (Description, Image, ImageSmall, Workspace, Context, CustomCommands, TelegramBotId, TelegramChatId, AvatarScale/Offset, PreparerAgentId, ReviewAgentId, MaxReviewIterations, FinalizerAgentId, MultiModelMode, PanelAgentIds, MoaLayers) |
|
||
| `chains` | Ein Request-Response-Zyklus (Tokens, Timing, Iterations, PlanMode, Effort, ToolChoice, ToolNames, StreamErrorInfo, ArchiveId, WorkflowInfo = JSON-Telemetrie eines dynamischen ultracode-Workflows, null = keiner) |
|
||
| `messages` | Einzelne Nachrichten (text / tool_use / tool_result / error / web_search_result / auto_context), Tag für Sub-Agent-Markierung oder Import-Herkunft (`openclaw-import`), thought_signature (Gemini) |
|
||
| `attachments` | Base64-Anhänge (Bilder, Dokumente) |
|
||
| `archives` | Archivierte Chain-Gruppen |
|
||
| `cronjobs` | Geplante Tasks (Cron-Expression + Prompt) |
|
||
| `agent_skills` | N:M Agent ↔ Skill |
|
||
| `shared_messages` | Geteilte Nachrichten (öffentliche Links via Share-Token) |
|
||
| `telegram_bots` | Telegram Bots (BotToken, WebhookSecret, Name, IsPublic) – eigenständige Entität, 1 Bot → N Sessions |
|
||
| `api_keys` | API-Keys (key_id, provider, name, api_key, is_active, status, plan, usage_7d, usage_5h, source_url, is_public, refresh_token, access_token_expires_at, oauth_provider) – mehrere Keys pro Provider möglich, Zuweisung pro Agent |
|
||
| `agent_api_keys` | API-Key-Zuweisung pro User+Agent (user_id, agent_id, key_id) – ersetzt globales `users.selected_api_key_id` |
|
||
| `scanned_keys` | Gescannte GitHub-Keys (api_key, source_url, source, type, status, plan, usage_7d, usage_5h, adopted_api_key_id, deleted) |
|
||
| `scanner_logs` | Scanner-Logs (timestamp, type, message, scanner) |
|
||
| `server_settings` | Server-Settings (key, value, type) – verwaltet über `ServerSettingsService` |
|
||
| `message_embeddings` | Sentence-Embeddings für semantische Suche (message_id, vector BLOB) |
|
||
| `memory_file_embeddings` | Embeddings von Memory-Dateien (agent_id, file_path, chunk_index, vector BLOB) |
|
||
|
||
---
|
||
|
||
## API-Endpunkte (Übersicht)
|
||
|
||
| Gruppe | Route | Wichtigstes |
|
||
|--------|-------|-------------|
|
||
| Auth | `/api/auth/` | `POST login`, `POST logout`, `GET me`, `GET autologin` |
|
||
| Chat | `/api/chat` | `POST` → SSE Stream, `POST stop` |
|
||
| History | `/api/history` | GET history, poll, archive, marks, delete |
|
||
| Sessions | `/api/sessions/` | CRUD + system-prompt, system-prompt-blocks, payload-preview, streaming, workflow-status, exec, git-status |
|
||
| Agents | `/api/agents/` | CRUD + available (inkl. public agents, inline Skills) |
|
||
| Admin | `/api/admin/` | User CRUD, impersonate, login-link (Admin-only) |
|
||
| Workspace | `/api/workspace/` | tree, file read/write, mkdir, rmdir, browse, agent-files |
|
||
| Telegram | `/api/telegram/` | Bot-CRUD, webhook/{botId} (unauth), session-config, registration |
|
||
| OpenClaw | `/api/openclaw/` | Import von OpenClaw-Sessions (sessions, import) |
|
||
| Cronjobs | `/api/cronjobs/` | CRUD + toggle, manual run |
|
||
| Krea | `/api/images/` | generate, list, file-serve, thumb, status (file/* ohne Auth) |
|
||
| Skills | `/api/skills` | list, agent-mapping, output-files |
|
||
| Profile | `/api/profile` | GET, PUT, password |
|
||
| Settings | `/api/settings` | GET, PUT (JSON blob) |
|
||
| Backup | `/api/backup` | POST → tar.gz download (Admin) |
|
||
| Shared | `/api/shared/`, `/api/messages/{id}/share` | Share erstellen, öffentlich abrufen, löschen |
|
||
| Models | `/api/models/` | `GET nvidia`, `GET pollinations`, `GET gemini`, `GET openai`, `POST refresh` |
|
||
| API Keys | `/api/admin/api-keys/` | CRUD + check, check-all, scan, event-scanner, scanned (recheck/adopt/delete), scanner-logs (Admin) + `/api/api-keys/` available, select, selected (User) |
|
||
| Remote | `/api/remote` | WebSocket für Remote-Clients, `GET connections`, `POST assign/unassign`, `GET /remote.ps1` (Windows), `GET /remote.sh` (Linux/macOS) |
|
||
| Internal | `/api/internal/` | MCP-Server: remote-execute, ask-user-choice, memory-read/write/list (Token-gesichert) + `POST /api/chat/choice-respond` (normale Auth) |
|
||
| DB Explorer | `/api/admin/db/` | tables, table-data (Admin-only) |
|
||
| Info | `/api/info` | GET → Build-Zeitstempel + Embedding-Verfügbarkeit |
|
||
| ServerSettings | `/api/admin/server-settings` | `GET`, `PUT` (Admin-only, liest/schreibt `appsettings.Production.json`) |
|
||
|
||
---
|
||
|
||
## Services (alle Singletons)
|
||
|
||
| Service | Was es tut |
|
||
|---------|-----------|
|
||
| **DatabaseService** | Alles DB: Schema, Migrationen, CRUD |
|
||
| **AuthService** | Token-Auth, Passwort-Hashing (SHA256) |
|
||
| **ChatService** | Chat-Kontext aufbauen, History aus DB laden |
|
||
| **AnthropicService** | Claude API: SSE Streaming + Non-Streaming + CLI-Fallback, passive Usage-Aktualisierung (partial class: Core, Streaming, Agents, Cli) |
|
||
| **ClaudeCliService** | Claude Code CLI Wrapper (OAuth-basiert, kein API-Key nötig) |
|
||
| **OpenAiService** | OpenAI-kompatible Provider (Nvidia, Pollinations, Gemini, OpenAI) mit Tool-Calling, Streaming + Non-Streaming, Modell-Caching |
|
||
| **SseHelper** | Shared SSE-Utilities: WriteSseEvent, TryWriteSse, FinalizeChain, TruncateToolResult, `TokenUsage.Add` (static) |
|
||
| **ToolService** | Tool-Definitionen + Ausführung (19 Tools, inkl. github_code_search, serper_web_search) |
|
||
| **WorkspaceHelper** | Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz (static) |
|
||
| **SystemPromptBuilder** | System-Prompt aus modularen Blöcken zusammenbauen + Reminder-Generierung (static) |
|
||
| **MemoryService** | Agent-Gedächtnis auf Dateisystem |
|
||
| **SkillService** | Skill-Discovery aus Dateisystem |
|
||
| **TelegramService** | Telegram Bot API (Bot-basiert, Multi-Session-Routing), Provider-/Key-Dreiteilung: CLI-Fallback für keylose Anthropic + Server-Key-Auflösung für nvidia/gemini/minimax/pollinations |
|
||
| **CronjobService** | BackgroundService, minütlicher Cron-Check, gleiche Provider-/Key-Dreiteilung wie TelegramService (CLI-Fallback + Server-Key-Auflösung) |
|
||
| **KreaService** | Krea API Bildgenerierung + Job-Polling (eine Anfrage pro Bild – Krea akzeptiert kein `batchSize` mehr) |
|
||
| **ImageService** | Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail) |
|
||
| **EmbeddingService** | Lokales Sentence-Embedding via all-MiniLM-L6-v2 (ONNX, 384 Dimensionen) |
|
||
| **VectorSearchService** | Semantische Suche über Message-Embeddings + Memory-File-Embeddings |
|
||
| **EmbeddingBackgroundService** | Backfill bestehender Messages + Live-Embedding neuer Messages/Memory-Files |
|
||
| **RemoteConnectionManager** | WebSocket-basierte Remote-Verbindungen, Command-Execution auf Remote-Clients |
|
||
| **StreamingTracker** | Tracking aktiver Streams + CancellationTokenSource pro Session für Stop |
|
||
| **WorkflowProgressTracker** | In-Memory-Telemetrie laufender dynamischer ultracode-Workflows pro Session (Subagent-Anzahl, Tool-Aufrufe + Aufschlüsselung, aggregierte Subagent-Tokens); LIVE während des Laufs, danach in `chains.workflow_info` persistiert (analog StreamingTracker) |
|
||
| **AppPaths** | Zentrale Pfade (WwwRoot, Dev vs. Publish-Modus) (static) |
|
||
| **KeyScannerService** | API-Key-Scanner (GitHub Code Search, GitHub Issues/PRs, HuggingFace Hub, Google/Serper), täglicher Scan |
|
||
| **GitHubEventScannerService** | Echtzeit-Scanner: GitHub Public Events API (PushEvent-Diffs nach geleakten Keys), 30s Poll-Intervall |
|
||
| **KeyPatterns** | Zentrale Key-Pattern-Definitionen für alle Scanner (Regex, Typ-Erkennung, Display-Names) (static) |
|
||
| **KeyCheckDispatcher** | Zentraler Dispatcher für Key-Checks aller Provider, normalisiertes Ergebnis (static) |
|
||
| **AnthropicKeyChecker** | Key-Validierung via Anthropic API, Usage-Header-Auswertung (Plan, 7d/5h Usage) |
|
||
| **OpenAiKeyChecker** | OpenAI Key-Validierung (Models-Endpoint, Rate-Limits, Organization) |
|
||
| **GoogleKeyChecker** | Google/Gemini Key-Validierung (Models + Quota-Check) |
|
||
| **StripeKeyChecker** | Stripe Key-Validierung (Balance, Account-Info, Payouts/Charges) |
|
||
| **ServerSettingsService** | Server-Settings Verwaltung (In-Memory-Cache + DB), Cleanup obsoleter Settings |
|
||
|
||
---
|
||
|
||
## Chat-Pipeline (Flow)
|
||
|
||
```
|
||
User-Input → POST /api/chat (SSE)
|
||
→ ChatService.BuildContext()
|
||
→ History aus DB laden (letzte N Chains)
|
||
→ SystemPromptBuilder: 11 Blöcke zusammensetzen (+ Plan-Modus/Preparer-Blöcke)
|
||
→ Tool-Definitionen vom ToolService holen (im Plan-Modus: nur Read-only)
|
||
→ API-Key auflösen: GetAgentApiKeyRecord(userId, agentId) – Anthropic ohne Key → CLI-Fallback oder Fehler, keyless Provider → OK
|
||
→ Preparer-Phase (optional, wenn PreparerAgentId gesetzt):
|
||
→ RunPreparer() → Provider-Weiche: Anthropic → RunAgentChat(), OpenAI → ChatNonStreaming()
|
||
→ Antwort ohne [QUESTION] → automatisch weiterleiten (Default = Forward)
|
||
→ [QUESTION] → Rückfrage an User, Flow stoppt
|
||
→ AnthropicService.StreamChat() / StreamChatViaCli() / OpenAiService.StreamChat()
|
||
→ API Call (SSE)
|
||
→ Token-für-Token an Client streamen
|
||
→ Bei tool_use → ToolService.Execute() → Ergebnis zurück → neuer API Call
|
||
→ Bei ask_user_choice → SSE user_choice Event → Flow stoppt (awaiting_user_choice)
|
||
→ Loop bis end_turn oder max_iterations (100)
|
||
→ Bei end_turn + ReviewAgent → Review-Phase:
|
||
→ Review-Agent prüft (eigene Tools, max 20 Tool-Iterationen pro Run, bis zu N Feedback-Runden)
|
||
→ [APPROVED] → Fertig | Feedback → Worker korrigiert → erneuter Review
|
||
→ Bei FinalizerAgent → Finalizer-Phase:
|
||
→ Finalizer-Agent arbeitet (eigene Tools, z.B. git commit)
|
||
→ Einmalig, kein Feedback-Loop
|
||
→ Stop-Button: POST /api/chat/stop → StreamingTracker.Cancel()
|
||
→ CancellationToken durchgereicht bis ToolService → Prozess-Kill
|
||
→ client_disconnected
|
||
→ Chain + Messages in DB speichern
|
||
```
|
||
|
||
**Passive Usage-Aktualisierung:** Nach jedem API-Request (Streaming, Sub-Agent, NonStreaming) werden die Response-Header ausgelesen und Plan/Usage des aktiven API-Keys in der DB aktualisiert – kein separater Check nötig.
|
||
|
||
**History-Optimierung:** Max 220 Messages (`MaxHistory`-Setting). Ältere Chains nur Text-Messages (keine Tool-Calls). Letzte Chain vollständig. Tool-Results auf 20.000 Zeichen gekürzt. Attachments nur für letzte User-Message. System-Prompt-Reminder alle ~30 Messages.
|
||
|
||
**Auto-Context (Vector-Search):** Wenn `UseAutoContext` aktiv, wird vor jedem Request via `VectorSearchService` semantisch passender Kontext aus früheren Messages + Memory-Files gesucht und als `<recalled_context>`-Block in den System-Prompt injiziert. Wird im Frontend als aufklappbare **Auto-Context-Box** angezeigt (Live via SSE-Event `auto_context` + History via `messages`-Eintrag mit `Role="system"`, `Type="auto_context"`). Wird aus der API-History gefiltert (kein Doppel-Send).
|
||
|
||
**Rendering:** Markdown via marked.js + LaTeX via KaTeX (`$$...$$` Block, `$...$` Inline). Timestamps im Format `dd.MM.yyyy HH:mm:ss` on hover.
|
||
|
||
---
|
||
|
||
## Tool-System (19 Tools)
|
||
|
||
| Tool | Funktion |
|
||
|------|----------|
|
||
| `read_file` | Datei lesen |
|
||
| `write_file` | Datei erstellen/überschreiben |
|
||
| `edit_file` | Exakten Textabschnitt ersetzen |
|
||
| `list_files` | Verzeichnisinhalt auflisten |
|
||
| `delete_file` | Datei/leeres Verzeichnis löschen |
|
||
| `execute` | Shell-Befehl ausführen (bash, konfigurierbares Timeout) |
|
||
| `background_execute` | Shell-Befehl als Hintergrund-Prozess starten (gibt PID zurück) |
|
||
| `glob` | Dateien per Glob-Pattern finden |
|
||
| `grep` | Dateien per Regex/Text durchsuchen |
|
||
| `todo_write` | Aufgabenliste schreiben (wird im Prompt angezeigt) |
|
||
| `memory_read` | Memory-Datei lesen |
|
||
| `memory_write` | Memory-Datei schreiben/appenden |
|
||
| `memory_list` | Memory-Dateien auflisten |
|
||
| `memory_search` | Semantische Suche über vergangene Chat-Nachrichten und Memory-Dateien |
|
||
| `remote_execute` | Shell-Befehl auf verbundenem Remote-Client ausführen (via WebSocket) |
|
||
| `send_to_session` | Nachricht an eine andere Session senden und Agent-Antwort auslösen |
|
||
| `ask_user_choice` | Interaktive Rückfrage mit 2-5 Auswahlmöglichkeiten (wenn AllowUserChoice aktiv) |
|
||
| `github_code_search` | Durchsucht öffentliche GitHub-Repos nach Code (GitHub Code Search API) |
|
||
| `serper_web_search` | Internet-Suche via Google (Serper.dev API) |
|
||
|
||
**Sicherheit:** Alle Pfade werden auf das Workspace-Verzeichnis beschränkt (`..` wird blockiert). Tools sind pro Agent konfigurierbar (Feld `tools` als komma-separierte Liste).
|
||
|
||
**Tool-Choice:** Automatisch `any` wenn Workspace/Skills/Memory vorhanden, sonst `auto`. Wird zu `none` bei `/no-tools`. Einzelne Tools forcierbar via `/tool:toolname`.
|
||
|
||
---
|
||
|
||
## System-Prompt (11+ Blöcke, in Reihenfolge)
|
||
|
||
1. **Identity** – `IDENTITY.md` des Agents
|
||
2. **Soul** – `SOUL.md` des Agents
|
||
3. **Anweisungen** – `TaskInstruction` des Agents (optional, wenn gesetzt)
|
||
4. **User** – `USER.md` (globale User-Infos) + Persona
|
||
5. **Workspace** – Pfad-Info, Tool-Liste
|
||
6. **Rückfragen** – Hinweis zu `ask_user_choice` (wenn AllowUserChoice aktiv)
|
||
7. **Context** – Session-Kontext (custom system prompt)
|
||
8. **Skills** – Zugeordnete Skill-Prompts
|
||
9. **Memory** – Aktueller MEMORY.md-Inhalt + Regeln
|
||
10. **Git** – Commit-Verhalten (wenn AutoCommit konfiguriert)
|
||
11. **Markierte Nachrichten** – Vom User als wichtig markiert
|
||
- **[Plan-Modus]** – Überspringt 10; Read-only Tools; "Nur analysieren"
|
||
- **[Preparer-Block]** – Instruktionen für Vorbereiter-Agents
|
||
|
||
**Tool-Choice:** Automatisch `any` wenn Workspace/Skills/Memory vorhanden, sonst `auto`. Nicht konfigurierbar pro Agent.
|
||
|
||
**History-Reminder:** Alle ~30 Messages wird ein automatisch generierter Reminder injiziert (`[System Reminder] Beachte weiterhin den System-Prompt (...)`) – bekämpft "Lost-in-the-Middle"-Problem.
|
||
|
||
---
|
||
|
||
## Frontend-Routing
|
||
|
||
| Hash-Route | Page-Datei | Beschreibung |
|
||
|------------|-----------|--------------|
|
||
| `#/` | `dashboard.js` | Session-Übersicht (sortiert nach letzter Chat-Nachricht) |
|
||
| `#/chat/{id}` | `chat-page.js` | Chat (inkl. Usage-Badge-Overlay + Key-Switch-Modal) |
|
||
| `#/session/{id}` | `session-page.js` | Session-Settings |
|
||
| `#/agent/{id}` | `agent-page.js` | Agent-Editor |
|
||
| `#/telegram-bot/{id}` | `telegram-bot-page.js` | Telegram-Bot-Editor |
|
||
| `#/cronjobs` | `cronjob-page.js` | Cronjobs |
|
||
| `#/profile` | `profile-page.js` | Profil |
|
||
| `#/settings` | `settings-page.js` | Einstellungen |
|
||
| `#/admin` | `admin-page.js` | Benutzerverwaltung (Admin) |
|
||
| `#/api-keys` | `api-keys-page.js` | API-Key-Verwaltung + GitHub-Scanner + Event-Scanner (Admin) |
|
||
| `#/db-explorer` | `db-explorer-page.js` | Datenbank-Explorer (Admin) |
|
||
| `#/server-settings` | `server-settings-page.js` | Servereinstellungen (Admin) |
|
||
| `#/login` | `login-page.js` | Login |
|
||
| `#/shared/{token}` | `shared-page.js` | Geteilte Nachricht (öffentlich, kein Auth) |
|
||
|
||
---
|
||
|
||
## Auth-Flow
|
||
|
||
1. `POST /api/auth/login` → Token generiert, als `gott_token` HttpOnly Cookie gesetzt
|
||
2. Jeder `/api/`-Request → `AuthMiddleware` prüft Cookie → setzt `UserId` + `IsAdmin`
|
||
3. Ausnahmen: Login, Autologin, Telegram-Webhook (`/api/telegram/webhook/{botId}`), `GET /api/shared/{token}`, `GET /api/images/file/*`, `/api/remote` (WebSocket) brauchen kein Auth
|
||
4. **Krea API-Key**: `/api/images/`-Routen akzeptieren `X-Api-Key`-Header (Config: `KreaApiKey`) für Automation ohne User-Kontext
|
||
5. **Impersonate**: Admin kann via `POST /api/admin/users/{id}/impersonate` als anderer User einloggen
|
||
6. **Login-Link**: Admin generiert via `POST /api/admin/users/{id}/login-link` einen Autologin-Link (Token-basiert, `GET /api/auth/autologin?token=xxx`)
|
||
7. **DefaultPath**: Pro User ein Basis-Ordner (nur vom Admin setzbar), Session-Workspaces sind relativ dazu
|
||
|
||
---
|
||
|
||
## Konfiguration (appsettings.json)
|
||
|
||
```json
|
||
{
|
||
"Port": 19000 // Server-Port (Default 19000, erfordert Neustart)
|
||
}
|
||
```
|
||
|
||
> **Hinweis:** `AnthropicToken` wird über die **API-Key-Verwaltung** im Admin-Bereich verwaltet (DB-Tabelle `api_keys`), nicht mehr über `appsettings.json`.
|
||
|
||
Verwaltung über **Admin-UI** (`#/server-settings`): Liest `appsettings.Production.json` (Fallback: `appsettings.json` → Default). Schreibt immer in `appsettings.Production.json`.
|
||
|
||
---
|
||
|
||
## Deployment
|
||
|
||
```bash
|
||
./deploy.sh # dotnet publish → systemctl restart gott
|
||
```
|
||
|
||
Läuft als `systemd`-Service, Kestrel auf konfigurierbarem Port (Default 19000), typisch hinter Nginx/Caddy Reverse Proxy.
|
||
|
||
---
|
||
|
||
## Multi-Agent System (Preparer + Review + Finalizer)
|
||
|
||
Eine Session kann bis zu **vier Agents** haben: Worker (Pflicht), Preparer (optional, `preparer_agent_id`), Review (optional, `review_agent_id`, `max_review_iterations`), Finalizer (optional, `finalizer_agent_id`).
|
||
|
||
**Flow:** Preparer analysiert User-Anfrage (→ Vorarbeit leisten oder `[QUESTION]` Rückfrage) → Worker-Agent arbeitet → Review-Agent prüft (`[APPROVED]` / Feedback-Loop) → Finalizer-Agent arbeitet (einmalig) → Fertig.
|
||
|
||
**Preparer:** Default = Forward (jede Antwort wird automatisch weitergeleitet, `[QUESTION]` stoppt). Bekommt eigene History mit `includeTag: "preparer"`. Aufbereiteter Prompt wird als `[Vorbereitet von AgentName]:` User-Message gespeichert.
|
||
|
||
**Backend:** `ChatService` baut `SubAgentContext` für Preparer, Review und Finalizer. `RunPreparer()` im ChatEndpoint (vor StreamChat). `RunReview()` + `RunFinalizer()` in AnthropicService. Alle nutzen Provider-Weiche: Anthropic → `RunAgentChat()`, OpenAI → `ChatNonStreaming()` (mit `Tag`-Markierung, Sub-Agent-Messages aus API-History gefiltert).
|
||
|
||
**CLI-Fallback für Sub-Agents:** Wenn ein Sub-Agent (Preparer, Review, Finalizer) keinem API-Key zugewiesen ist und die Claude CLI verfügbar ist, wird automatisch `RunSubAgentViaCli()` genutzt. Erst wenn weder Key noch CLI verfügbar sind, wird der Sub-Agent übersprungen (kein Hard-Error mehr).
|
||
|
||
**SSE-Events:**
|
||
- Preparer: `preparer_start`, `preparer_text`, `preparer_tool_use`, `preparer_done`
|
||
- Review: `review_start`, `review_text`, `review_tool_use`, `review_feedback`, `review_approved`
|
||
- Finalizer: `finalizer_start`, `finalizer_text`, `finalizer_tool_use`, `finalizer_done`
|
||
|
||
**Frontend-Rendering:**
|
||
- Streaming: `chat.js` rendert Preparer-/Review-/Finalizer-Events als eigene Bubbles
|
||
- History: `renderMarkdown` parst `[Preparer: AgentName]`, `[Vorbereitet von AgentName]`, `[Review: AgentName]`, `[Finalizer: AgentName]`, filtert `[APPROVED]`, `[FORWARD]`, `[QUESTION]`
|
||
- Bubble-Klassen: `.preparer-bubble` (Info-Farbe), `.review-bubble` (sekundäre Farbe), `.finalizer-bubble` (grüne Farbe)
|
||
|
||
## Multi-Modell-Panel (Fusion / MoA)
|
||
|
||
Zusätzlich zum Worker kann eine Session ein **Panel** aus mehreren Agenten haben. In den Session-Einstellungen werden nur die **Mitglieder** persistiert (`panel_agent_ids` = CSV der Agent-IDs, `moa_layers` = Proposer-Runden bei MoA, Default 2). Der Panel-**Modus** (`fusion`/`moa`) wird **on-demand pro Anfrage** über den Composer-Toggle gewählt (transienter Request-Flag `multiModelMode`, nicht persistiert – wie Plan-/Ultracode-Modus). Ist er aktiv, antworten alle Panel-Agenten **parallel** auf die Anfrage; ihre Antworten werden dem **Worker** als `<panel_responses>`-Block in die letzte User-Message gemergt, der daraus **eine** Gesamtantwort synthetisiert und normal streamt (OpenRouter-Fusion-/Mixture-of-Agents-Muster).
|
||
|
||
**Fusion vs. MoA:** Fusion = 1 Proposer-Runde → Synthese. **MoA** = `moa_layers` Runden (1–5, Backstop): ab Runde 2 sieht jeder Member die Antworten der Vorrunde (`<previous_layer_responses>`, isoliert pro Member via `CloneForMember` gemergt) und verfeinert. `RunPanel(layers)` ist EINE Engine (Fusion = `layers==1`). **Alle** Runden werden persistiert (F5-sicher); JSON-Transkript pro Member enthält `layer`/`totalLayers` + Text-/Tool-Segmente (`Iteration = layer*100+index+1` macht DB-Zeilen layer-aware).
|
||
|
||
**Member laufen read-only** (Anthropic: `FilterReadOnlyTools` via `PlanMode`, CLI: `--permission-mode plan`, OpenAI: tool-los via `OpenAiService.CompleteText`) → keine parallelen Schreibkonflikte. Concurrency-Cap (3) begrenzt parallele CLI-Children.
|
||
|
||
**Member streamen LIVE** (wie der Session-Agent): CLI forwardet `TextDelta`/`ToolStart`/`ToolResult`, Anthropic-API streamt pro Tool-Runde über `NonStreamingLoopCallbacks`, OpenAI = one-shot (Text am Ende). Ein `SemaphoreSlim`-Write-Lock in `SseHelper.TryWriteSse` serialisiert die SSE-Byte-Flushes paralleler Member (sonst vermischen sich die Frames).
|
||
|
||
**Subagent-Filterung:** Spawnt ein CLI-Member intern einen Subagenten (Claude-CLI-`Agent`-Tool), sendet die CLI dessen Interna als separate Top-Level-Events mit `parent_tool_use_id`. Der Parser (`ClaudeCliService.RunCliStreamingAsync`) verwirft diese **zentral am Ursprung** (Guard auf nicht-leeres `parent_tool_use_id`) → keine namenlosen Tool-Boxen, weder im Live-Stream noch in der History (beide lesen aus denselben gefilterten `CliStreamEvent`s). Die `Agent`-Tool-Box selbst (`parent_tool_use_id=null`) bleibt sichtbar; der finale `result`-Event (mit den **aggregierten** Subagent-Tokens) wird nicht gefiltert → Token-Bilanz vollständig. Dieselben unterdrückten Subagent-Events werden zusätzlich **additiv** in den `WorkflowProgressTracker` abgegriffen (`TapSubagentEvent`, wirft nie) → speist den Workflow-Status, ohne den Guard/Stream zu verändern.
|
||
|
||
**Backend:** `ChatService.PrepareContext` baut `ChatContext.PanelAgents` (`List<SubAgentContext>`) + `MoaLayers`. `RunPanel(layers)` (AnthropicService.Panel.cs) läuft im ChatEndpoint-try VOR dem Worker-Dispatch: pro Runde Member parallel (`Task.WhenAll` + `SemaphoreSlim`), Provider-Weiche pro Member, **jede** Runde als `[Panel: Name]`-Message mit `Tag="panel"` persistiert (aus Worker-History gefiltert); JSON-Transkript = `{name, model, layer, totalLayers, segments:[Text/Tool…]}`. `NonStreamingLoopCallbacks.Persist=false` verhindert Doppel-Persistenz. Member-Tokens via `seedUsage` in Worker-`usage` gefaltet → Live-Badge **und** DB (kein separates Panel-Usage-Event). Synthese-Block in letzte User-Message gemergt. Panel-Fehler graceful.
|
||
|
||
**SSE-Events:** `panel_start` (mode/layers), `panel_layer_start`/`panel_layer_done` (MoA-Runden), `panel_member_start` (mit `layer`), `panel_member_text`/`panel_member_tool_use`/`panel_member_tool_end`/`panel_member_tool` (live), `panel_member_done` (inkl. per-Member Token-Feldern `inputTokens`/`outputTokens`/`cacheReadTokens`/`cacheCreationTokens`), `panel_done`.
|
||
|
||
**Frontend:** `stream-ui.js` rendert pro Member eine `.panel-bubble`, gekeyt per `layer_index` (MoA: gleiche Member über mehrere Runden; aktive Runde pro Index wird bei `panel_member_start` gemerkt, da Runden sequenziell laufen). `panel_layer_start` zeigt bei >1 Runde einen `.panel-layer-divider`. **History:** `history.js _renderPanelMemberBubble` parst JSON-Transkript → Text + Tool-Boxen interleaved; bei MoA Runden-Divider beim Layer-Wechsel (Back-Compat für Alt-Format). Settings-Picker: **nur** die Panel-Mitglieder (`#panelAgentIds` via `_openMultiPickerModal`) + `#moaLayers` – beide immer sichtbar und **persistent**. Der Panel-**Modus** wird NICHT mehr in den Settings gewählt. Composer-Toggle: **ein** Button `#planModeBtn` (chat.js) öffnet ein **Dropdown-Menü** (`_COMPOSITE_MODES`: Normal/Plan/Ultracode/Fusion/MoA, Muster wie der Emoji-Picker; aktiver Eintrag markiert, Button trägt das Modus-Icon). **Alle** Modi sind **transient** (pro Request, nicht persistiert, beim Chat-Wechsel via `initCompositeMode` auf `off` zurückgesetzt): der gewählte Modus wird als Flag in den `/api/chat`-Payload gelegt (`planMode`/`ultracode`; Fusion/MoA als `multiModelMode`, nur diese beiden Werte). Das Backend baut das Panel daraus **on-demand** (`ChatService.PrepareContext(multiModelMode)` → `effectiveMultiModelMode`), die Mitglieder weiterhin aus der persistierten `session.PanelAgentIds`. **Per-Member-Token-Badge:** `buildPanelMemberMeta(d)` (`stream-ui.js`, global) baut ein kompaktes `.panel-member-meta`-Badge aus den Token-Feldern – Live aus `panel_member_done`, History aus dem persistierten JSON (gleicher Helper); graceful bei fehlenden Daten (OpenAI, Fehler-Member, Alt-History → kein Badge).
|
||
|
||
**Web-Search Sources:**
|
||
- `web_search_result`-Messages in DB gespeichert (Sources als JSON-Array, leere werden übersprungen)
|
||
- History + Shared-Page: Tool-Box mit klickbaren Source-Links (Domain + Title)
|
||
- Streaming: `pauseStreamBubble()` hält Bubble offen zwischen Web-Searches (keine Doppel-Bubbles)
|
||
|
||
---
|
||
|
||
## Workflow-Status (dynamische ultracode-Workflows)
|
||
|
||
Live-Telemetrie der Subagenten, die ein **ultracode**-Lauf über das `Workflow`/`Task`-Tool der Claude CLI spawnt (diese Subagent-Events sind im Haupt-Stream/Verlauf bewusst ausgeblendet, siehe Subagent-Filterung oben).
|
||
|
||
**Backend:** `WorkflowProgressTracker` (Singleton, lokale Instanz in `Program.cs`, weil `ClaudeCliService` befüllt und `AnthropicService` beim Finalize persistiert – dieselbe Instanz). `ClaudeCliService.TapSubagentEvent` → `RecordSubagent(...)` führt pro Session In-Memory: Subagent-Anzahl, Tool-Aufrufe (+ Aufschlüsselung), aggregierte Input/Output-Tokens. Beim `FinalizeChain` (`AnthropicService.cs`) wird der Stand als JSON in `chains.workflow_info` (`ChainRecord.WorkflowInfo`, Migration via `TryAddColumn` in `DatabaseService.cs`, geschrieben in `DatabaseService.Sessions.cs` `UpdateChain`) geschrieben – mit ChainId-Abgleich, damit ein Panel-Member derselben Session ihn nicht löscht.
|
||
|
||
**Endpoint:** `GET /api/sessions/{id}/workflow-status` → LIVE-Snapshot aus dem Tracker, sonst persistiertes `workflow_info` der jüngsten Chain, sonst `status: "none"`. JSON: `{ sessionId, streaming, status (running/completed/none), chainId, workflow }`; das `workflow`-Objekt trägt camelCase `subagentCount`, `subagentToolCalls`, `subagentInputTokens`, `subagentOutputTokens`, `toolBreakdown`, `recentTools`, `startedAt`, `lastActivityAt`.
|
||
|
||
**Frontend:** Header-Menü-Eintrag **„Workflow-Status"** (`#workflowStatusBtn` in `index.html`, direkt nach „Payload anzeigen"), Wrapper `apiGetWorkflowStatus` (`api.js`), `showWorkflowStatusModal` (`modals.js`, pollt alle 2 s solange `status === 'running'`, stoppt von selbst beim Schließen des Modals), Click-Handler in `pages/chat-page.js`, `.workflow-status-modal`-Styles in `css/chat.css`.
|
||
|
||
---
|
||
|
||
## Wichtige Regeln beim Entwickeln
|
||
|
||
- ⚠️ **Quelldateien in `wwwroot/`** editieren, NICHT in `Build/*/publish/wwwroot/`
|
||
- ⛔ **`Build/*/publish/data/gott.db`** niemals manuell modifizieren/löschen
|
||
- ⛔ **`Build/`** generell nicht anfassen
|
||
- 🔧 **.NET 10** ohne Nullable, ohne Implicit Usings
|
||
- 📦 DB-Migrationen sind idempotent (IF NOT EXISTS Pattern in `DatabaseService`)
|
||
- 🗂️ Native Pfade (Windows: `\`, Linux/macOS: `/`) – Backend liefert OS-Pfade, Frontend matcht beide via `[\\/]`
|