- Python MCP-Server (stdio, JSON-RPC 2.0) für remote_execute via Claude CLI
- Interner API-Endpoint /api/internal/remote-execute mit Token-Auth
- MCP-Config wird automatisch generiert wenn Remote-Verbindung vorhanden
- Auth-Middleware: /api/internal/ von Auth ausgenommen
- Frontend: :::ASK_USER_CHOICE::: Blöcke als interaktive Buttons rendern
- Streaming: Unvollständige Choice-Blöcke werden während des Streams versteckt
- tool-ui.js: MCP-Tool-Namen generisch normalisieren (mcp__server__tool → tool)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
workCt steuert den CLI-Prozess (nur Stop-Button), sseCt die SSE-Writes
(Client-Disconnect). Page-Reload cancelt nur sseCt → CLI läuft weiter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Tool-Namen/Inputs von CLI-Konvention auf gott-Konvention mappen (Edit→edit_file, file_path→path etc.)
- Text vor Tool-Use in DB flushen für korrekte Reihenfolge (statt alles am Ende als Blob)
- lineBuffer in ClaudeCliService vor Tool-Start und bei Text-Block-Ende flushen
- ToolResult-Events parsen und in DB speichern (CLI sendet diese als type:"user" Messages)
- Errors in DB speichern und als SSE-Event senden
- Bild-Attachments als Temp-Dateien für CLI bereitstellen (Read-Tool kann sie lesen)
- Frontend: tool_result Handler, Tool-Name-Normalisierung, appendToolResult()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ClaudeCliService: Marker-basierte ask_user_choice Erkennung im Stream-Parser
- AnthropicService: StreamChatViaCli mit allowUserChoice, planMode, webSearch
- BuildHistoryContext erweitert für tool_use/tool_result (Choice-Antworten)
- Tool-Use Records werden in DB persistiert (Poll-Kompatibilität)
- CLI Tools konfigurierbar: Workspace/Plan-Modus/WebSearch/kein Workspace
- OAuth DB-Felder für zukünftige Token-Verwaltung
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Neuer ServerSettingsService: zentrale Settings in SQLite mit In-Memory-Cache
- Auto-Migration: bestehende appsettings.Production.json wird einmalig in DB übernommen
- Alle Services umgestellt: IConfiguration → ServerSettingsService (dynamisch, kein Neustart)
- Port bleibt als einziges Setting in appsettings.Production.json (erfordert Neustart)
- ServerSettingsEndpoint: liest/schreibt DB statt JSON-Datei, Port als Sonder-Setting
- IConfiguration komplett entfernt aus allen Services (ChatService, ToolService, AnthropicService,
OpenAiService, KreaService, TelegramService, KeyScannerService, GitHubEventScannerService)
- AuthMiddleware: KreaApiKey dynamisch aus DB
- appsettings.json bereinigt: nur noch Port und Logging
- Dokumentation aktualisiert (ARCHITECTURE.md, ARCHITECTURE_QUICK.md, README.md)
Backend:
- is_active komplett entfernt, durch is_public (Admin-Toggle) ersetzt
- Neues selected_api_key_id auf users-Tabelle
- AnthropicService: globaler _token entfernt, apiToken pro Request
- Token-Durchreichung: StreamChat → ProcessStream → RunAgentChat →
RunPreparer/RunReview/RunFinalizer, ExecuteTool, send_to_session
- ChatEndpoint: User-Key aus DB laden, an StreamChat übergeben
- TelegramService: Key per bot.UserId auflösen
- CronjobService: Key per job.UserId auflösen
- Neue Endpoints: GET /api/api-keys/available, PUT /api/api-keys/select/{keyId},
GET /api/api-keys/selected, PUT /api/admin/api-keys/{keyId}/public,
POST /api/admin/api-keys/check-all
- Program.cs: AnthropicService ohne Token-Parameter
Frontend:
- API-Keys-Page: Public-Toggle (Globe/Lock) statt Activate/Deactivate
- Alle prüfen Button für Batch-Key-Check
- Chat: Key-Auswahl für alle User (nicht nur Admin)
- Dummy-Badge wenn kein Key ausgewählt
- Fallback-Badge bei Key ohne Usage-Daten
- Key-Switch-Modal: apiGetAvailableKeys + apiSelectApiKey
- New tool: github_code_search (GitHub Code Search API with text-match fragments)
- Tool available when GitHubToken is configured in server settings
- Registered in AnthropicService + ChatService tool pipelines
- ToolService: SetHttpClientFactory() pattern for lazy DI resolution
- ScannedKeyRecord: type field (anthropic_oauth, anthropic_api, anthropic_unknown, unknown)
- DetectKeyType() based on key prefix
- DB migration: type column with default 'anthropic_oauth'
- Frontend: type label in scanned keys list
- Neue Tabelle api_keys (Provider, Name, ApiKey, IsActive)
- Admin-CRUD: GET/POST/PUT/DELETE /api/admin/api-keys
- Aktivieren/Deaktivieren pro Provider (nur einer aktiv)
- Live-Token-Update im AnthropicService ohne Neustart
- Migration: AnthropicToken aus appsettings.Production.json → DB
- AnthropicToken aus ServerSettings-Definitionen + appsettings entfernt
- Frontend: API-Keys-Seite mit Erstellen/Bearbeiten/Löschen/Toggle
- Dashboard: API-Keys-Link für Admins
- AnthropicService: db/chatService/anthropicService/userId/telegramService auch im
RunAgentChat (Zeile 899) und ChatNonStreaming (Zeile 1105) ExecuteTool-Aufruf
durchreichen – Telegram-Flow nutzt ChatNonStreaming, hatte zuvor null-Parameter
- ChatService: FixOrphanedAskUserChoice → FixOrphanedToolUse ersetzt
Prüft gesamte History auf orphaned tool_use-Blöcke (nicht nur ask_user_choice am Ende)
Synthetische tool_results werden eingefügt um Anthropic API 400 zu verhindern
- ToolService: Debug-Logging für send_to_session nach /tmp/gott_send_to_session.log
- Neues Tool send_to_session: Agent kann Nachrichten in andere Sessions schicken
- Ziel-Session per ID oder Name (case-insensitive) auffindbar
- User-Message wird nicht in DB gespeichert (wie Cronjob) – keine Bubble im Frontend
- Nachricht wird als '[Nachricht von Agent "X"]' kontextualisiert injiziert
- Sicherheitscheck: Ziel-Session muss demselben User gehören, keine eigene Session
- Zirkuläre Abhängigkeit ChatService ↔ AnthropicService via SetChatService() gelöst
- tool-ui.js: Display-Name für send_to_session
- RemoteConnectionManager: Singleton-Service verwaltet WebSocket-Verbindungen
mit Command-Execution (Send/Receive mit Timeout + SemaphoreSlim-Locking)
- RemoteEndpoint: WebSocket-Endpoint /api/remote (auth-frei), REST-APIs für
Connections/Assign/Unassign, PowerShell-Script-Auslieferung mit dynamischer URL
- remote.ps1: PowerShell-Client für Windows – verbindet per WebSocket,
sendet Hostname/User/OS, empfängt und führt Befehle aus, Heartbeat
- remote_execute Tool: Neues Agent-Tool, nur verfügbar wenn Session eine
zugeordnete Remote-Verbindung hat, führt PowerShell-Befehle remote aus
- Frontend: Menüpunkt 'Remote-Verbindungen' im Chat-Dropdown, Modal zeigt
alle aktiven WebSocket-Verbindungen (Hostname, User, OS, Verbindungszeit),
Verbinden/Trennen-Buttons pro Verbindung und Session
- Auth: /api/remote und /remote.ps1 von Authentifizierung ausgenommen
Architektur: Windows-Client → WebSocket → GOTT-Server → Agent-Tool
Session-gebunden: Eine Verbindung wird einer Session zugeordnet
- Bei API 400 'prompt is too long' / 'exceed context limit':
- 20% der ältesten Messages aus In-Memory-History entfernen
- TrimMessagesCleanly() verhindert Trennung von tool_use/tool_result-Paaren
- Retry mit kürzerer History (max 5 Retries im Worker)
- Alle 3 API-Call-Stellen abgesichert (StreamChat, RunAgentChat, ChatNonStreaming)
- Frontend: Neues 'info'-Event als Warning-Bubble angezeigt
- MaxHistory 220 bleibt als Obergrenze, dynamische Kürzung bei Token-Überschreitung
Problem: API 400 'web_search tool use without corresponding
web_search_tool_result block' – weil web_search_tool_result nur als
Sources-Array gespeichert wurde und in BuildHistoryFromDb kein Handler
dafür existierte. Der server_tool_use-Block war in der History, aber
der zugehörige web_search_tool_result fehlte.
Backend (AnthropicService):
- 3 Speicher-Stellen (StreamChat, RunAgentChat, ChatNonStreaming):
Raw-JSON statt ExtractWebSearchSources-Array speichern
Backend (ChatService):
- Neuer Handler für web_search_result in BuildHistoryFromDb:
Raw-JSON als web_search_tool_result in assistant content einfügen
- Rückwärtskompatibilität: Alte Array-Einträge überspringen
- CleanOrphanedWebSearchBlocks(): Post-Processing entfernt
orphaned server_tool_use mit name web_search (alte DB-Einträge)
Frontend (history.js, shared-page.js):
- _extractWebSearchSources(): Unterstützt altes Array-Format und
neues Raw-JSON Format (content[].type === web_search_result)
Ursache: WriteSseEvent in ProcessStream warf OperationCanceledException
bei Client-Disconnect (Page-Refresh) → sseCt war gelinkt mit dem
ASP.NET cancellationToken → Exception wurde im catch-Block als
'stopped' behandelt → Agent brach ab obwohl nur der Browser refreshed wurde.
Fixes:
- ProcessStream: Alle WriteSseEvent durch TryWriteSse ersetzt
SSE-Writes bei Client-Disconnect werden jetzt zu stillen No-Ops
statt den gesamten Agent-Flow per Exception zu killen
- catch-Blöcke: OperationCanceledException nur bei workCt/hardCt als
'stopped' behandeln, neuer catch für Client-Disconnect als
'client_disconnected' (Fallback, sollte nicht mehr auftreten)
- done-Event nach Stop: sseCt → CancellationToken.None damit das
done-Event trotz gecancelltem sseCt noch gesendet wird
- Frontend: done-Handler rendert Chain-Badge direkt im apiGetChain-
Callback statt Race-Condition mit finally-Block
- Frontend: 'stopped' Badge wieder angezeigt (war nur fälschlich bei
Page-Refresh, jetzt korrekt nur bei echtem Stop)
Stop-Button: Zwei Stufen statt einer:
- 1. Klick: Graceful Stop (workCt) - laufende API-Calls/Tools abbrechen
- 2. Klick: Hard Stop (hardCt) - sofortiger Abbruch aus while-Loop
StreamingTracker: Zwei separate CancellationTokenSources (StopCts + HardCts),
Cancel() gibt 'graceful'/'hard'/null zurück je nach Stufe.
ReadLineAsync Cancellation-Fix: StreamReader.ReadLineAsync propagiert den
CancellationToken nicht zuverlässig an den underlying NetworkStream.
Bei extended thinking blockiert ReadLineAsync minutenlang ohne Reaktion
auf Cancellation. Fix: workCt.Register() disposed den HTTP-Stream,
damit ReadLineAsync sofort mit Exception abbricht.
Bug-Fix: OperationCanceledException in RunAgentChat/ChatNonStreaming wurde
vom generischen catch(Exception) geschluckt - Agent lief trotz Stop weiter.
Jetzt: catch(OperationCanceledException) { throw; } vor catch(Exception).
Frontend: Stop-Button zeigt Hard-Stop visuell (rot + Pulse-Animation),
SSE-Stream wird erst beim 2. Klick clientseitig abgebrochen.
- HttpResponseMessage mit using disposen (3 Stellen in AnthropicService)
verhindert Socket-Exhaustion unter Windows (2 Connections/Host Limit)
- SSE DisableBuffering() in ChatEndpoint für sofortigen Flush unter Windows
- Execute: stdout/stderr vor WaitForExitAsync lesen statt danach
verhindert Pipe-Deadlock bei vollem Buffer (Windows: 4KB)
- Plan-Modus: Frontend sendet planMode bei Choice-Response mit
Backend-Fallback auf vorherigen Chain entfernt - User-Entscheidung gilt
- server_tool_use-Blöcke werden jetzt in DB persistiert und in
History-Rekonstruktion als Raw-JSON eingefügt
- Generische *_tool_result-Behandlung (bash_code_execution_tool_result,
code_execution_tool_result etc.) in allen drei Chat-Pfaden:
StreamChat, ChatNonStreaming, RunAgentChat
- ProcessStream: serverToolResultBlocks analog zu webSearchResultBlocks
- BuildHistoryFromDb: server_tool_use + server_tool_result werden
korrekt in Assistant-Content-Array rekonstruiert
- Behebt API 400: tool_use without corresponding tool_result
- tool_end SSE-Event: 500-Zeichen-Limit entfernt (2 Stellen), damit
todo_write und andere Tools beim Streaming vollständig angezeigt werden
- BuildHistoryFromDb: TruncateToolResult (20k) auf tool_result-Messages
angewendet, um Token-Overflow bei History-Rebuild zu verhindern
(609k Tokens > 200k Limit nach awaiting_user_choice)
- TruncateToolResult/MaxToolResultChars auf internal geändert
- TODO.md: todo_write-Bug als erledigt markiert
BuildReviewMessages() komplett umgebaut:
- Tool-Calls und Tool-Results werden nicht mehr übergeben
- Nur noch die reine Text-Konversation (user/assistant) wird extrahiert
- Aufeinanderfolgende Messages gleicher Rolle werden zusammengeführt
- Korrekte user/assistant-Abwechslung sichergestellt
- Fallback wenn nur Tool-Calls ohne Text vorhanden
- Unnötigen Serialisierungs-Roundtrip für Rollen-Prüfung entfernt
Erwartete Token-Ersparnis: 50-100k+ Input-Tokens pro Review-Runde
bei typischen Worker-Sessions mit vielen write_file/edit_file-Calls.
ARCHITECTURE.md:
- Inhaltsverzeichnis-Nummerierung korrigiert (22-28 statt doppelt 20-21)
- ServerSettingsEndpoint.cs in Projektstruktur ergänzt
- Tabellenbeschreibungen vervollständigt (users, agents, sessions, chains,
messages, cronjobs) mit allen fehlenden Feldern
- ChainRecord/MessageRecord/SessionWithStats Felder aktualisiert
- AutoCommit mention-Beschreibung korrigiert
ARCHITECTURE_QUICK.md:
- ToolService 12 → 13 Tools korrigiert
- Tabellenbeschreibungen analog zur Hauptdoku aktualisiert
- esc() um "→" Escaping erweitert (Bug: Anführungszeichen in value="..."-Attributen brachen HTML)
- cache_control: ephemeral zu RunAgentChat() hinzugefügt (Preparer/Review/Finalizer hatten kein Prompt Caching)
- cache_control: ephemeral zu ChatNonStreaming() hinzugefügt (Telegram hatte kein Prompt Caching)
- Trailing-Leerzeile in commands-ui.js bereinigt
Kernproblem: Claude vergisst [FORWARD]-Tag → Preparer leitet nie weiter.
Lösung - Logik umgedreht:
- Jede Antwort wird automatisch an Session-Agent weitergeleitet (Default)
- Nur bei explizitem [QUESTION]-Tag wird gestoppt (Rückfrage an User)
- [FORWARD] wird weiterhin erkannt und entfernt, ist aber optional
Weitere Fixes:
- Preparer bekommt eigene History mit includeTag='preparer',
damit er den Kontext seiner vorherigen Rückfragen sieht
- Session-Agent bekommt saubere History ohne Sub-Agent-Messages
(nach Preparer-Phase frisch aus DB geladen)
- [QUESTION] wird im Frontend-Markdown ausgeblendet
- MessageRecord: neues Tag-Feld (null=normal, preparer/review/finalizer=Sub-Agent)
- DatabaseService: Migration tag-Spalte, INSERT-Statements erweitert
- AnthropicService: RunAgentChat setzt Tag=agentType bei allen 4 SaveMessage-Aufrufen
- ChatService: BuildHistoryFromDb filtert getaggte Messages aus API-History
Sub-Agent-Messages bleiben in der DB (Frontend-Anzeige), werden aber
nie an die Claude API geschickt – löst das Problem für Preparer,
Review und Finalizer gleichermaßen.
- Neues optionales Session-Feld 'preparerAgentId' (DB + Model + API)
- PreparerContext im ChatContext für Agent-Konfiguration
- RunPreparer() in AnthropicService: führt Preparer vor dem Worker aus
- [FORWARD] → aufbereiteter Prompt wird an Session-Agent weitergegeben
- Ohne [FORWARD] → Rückfrage an User, Flow stoppt (preparer_query)
- Preparer-System-Prompt: erklärt Rolle, [FORWARD]-Mechanismus, Vorarbeit
- Preparer kann eigene Tools + Fähigkeiten aktiv nutzen
- ChatEndpoint: Preparer-Phase vor StreamChat integriert
- Frontend: SSE-Events (preparer_start/text/tool_use/done)
- Preparer-Bubble (Info-Farbe) mit Status Weitergeleitet/Rückfrage
- [Preparer: Name] → hübscher Header, [FORWARD] ausgeblendet
- [Vorbereitet von Name] → dezenter Header in User-Bubble
- Agent-Picker im Session-Form (vor Review-Agent)
- SystemPromptBuilder.BuildBlocks/Build bekommt planMode-Parameter
- Im Plan-Modus: Tools-Block zeigt nur Read-only-Tools (read_file, list_files, glob, grep)
- Im Plan-Modus: KRITISCH, Verhalten, Erinnerung und Git-Blöcke werden übersprungen
- Im Plan-Modus: Skills als 'nicht ausführbar' markiert
- Plan-Modus-Block zentral im SystemPromptBuilder statt doppelt in AnthropicService + SessionEndpoint
- ChatService reicht planMode an SystemPromptBuilder durch
- SessionEndpoint reicht planMode Query-Parameter an BuildBlocks durch
- System-Prompt-Modal zeigt korrekte Blöcke je nach Plan-Modus-Status
- Toggle-Button (🗺️) im Chat-Input-Bereich neben Explorer
- Plan-Modus wird per ChatRequest.planMode an Backend gesendet
- Backend: Write-Tools gefiltert (nur read_file, list_files, glob, grep,
todo_write, memory_*, ask_user_choice bleiben), System-Prompt-Block
ergänzt der Änderungen verbietet, tool_choice nie 'any' erzwungen
- ChainRecord.PlanMode: pro Chain gespeichert (informativ für History)
- Chain-Meta-Badge '📋 Plan-Modus' bei Plan-Chains
- Choice-Response erbt planMode vom vorherigen Chain
- CSS: .plan-mode-active Styling (Primary-Color Highlight)
- Placeholder wechselt zu 'Plannen...' wenn aktiv
- Plan-Modus wird bei Session-Wechsel zurückgesetzt
- Neues Feld AllowUserChoice (bool, default true) im AgentRecord
- DB-Migration: allow_user_choice Spalte mit DEFAULT 1
- Agent-Form: Checkbox 'Rückfragen erlauben' nach 'Tool-Nutzung erzwingen'
- ToolService.GetToolDefinitions: neuer Parameter includeUserChoice
- ask_user_choice Tool wird nur eingebunden wenn Agent es erlaubt
- Review-Agents und Non-Streaming (Cronjobs) erhalten nie ask_user_choice
- ChatEndpoint reicht Agent.AllowUserChoice an StreamChat durch
Neues Tool 'ask_user_choice' analog zum AskUserQuestion aus dem Claude Agent SDK.
Claude kann dem User Multiple-Choice-Fragen stellen, wenn es mehrere sinnvolle
Wege gibt. Der User wählt per Button, Claude arbeitet mit der Antwort weiter.
Backend:
- ToolService: ask_user_choice Tool-Definition (question + choices[2-5])
- AnthropicService/ProcessStream: Tool wird NICHT ausgeführt, sondern als
SSE-Event 'user_choice' an den Client gesendet. stopReason wird auf
'awaiting_user_choice' gesetzt (mit Guard gegen Überschreibung durch
message_delta)
- ChatEndpoint: Erkennung von Choice-Responses (choiceToolCallId + choiceResult),
tool_result wird in die korrekte Chain gespeichert (wo der tool_use liegt),
toolChoice wird auf 'auto' gesetzt damit Claude frei antworten kann
- ChatService: FindChainWithToolCall() zum Auffinden der richtigen Chain,
BuildHistoryFromDb() erweitert um Choice-Chain-Erkennung (wenn letzte Chain
leer/nur tool_result → vorletzte Chain vollständig rekonstruieren)
- ChatModels: ChatRequest um ChoiceToolCallId + ChoiceResult erweitert
Frontend:
- api.js: apiChatChoice() für Choice-Responses
- chat.js: handleStreamEvent() als shared Handler extrahiert (sendMessage +
handleChoiceClick nutzen denselben Code), handleChoiceClick() startet neuen
Stream nach Button-Klick
- ui.js: addChoiceBubble() (live/interaktiv) + addChoiceBubbleStatic() (History)
- history.js: Choice-Bubbles in _renderMessagesIntoFragment + renderMessages,
Voraus-Suche nach zugehörigem tool_result, unbeantwortet = klickbare Buttons
- chat.css: Styling für Choice-Bubbles (Primary-Color-Outline-Buttons, Hover,
Selected/Unselected States)
Robustheit:
- Backend-Fallback wenn Claude choices als String statt Array sendet
- Frontend-Guard: Array.isArray(choices) an allen 4 Render-Stellen
- Page-Refresh mit offener Choice → aktive Buttons werden korrekt gerendert
- Session hat optionalen ReviewAgentId + MaxReviewIterations
- Nach end_turn des Worker-Agents wird der Review-Agent aufgerufen
- Review-Agent bekommt Worker-Konversation als Kontext + kann Tools nutzen
- [APPROVED] in Review-Antwort → fertig, sonst Feedback an Worker
- Worker korrigiert und wird erneut reviewed (max N Runden)
- SSE-Events: review_start, review_text, review_tool_use, review_feedback, review_approved
- Chat-UI: Review-Phase als eigene Bubble mit Header, Text, Status
- Session-Page: Review-Agent-Picker (gleicher Agent-Card-Helper)
- Dashboard: Review-Agent-Badge auf Session-Cards
- DB-Migration: review_agent_id, max_review_iterations auf sessions
- ThrowIfCancellationRequested() am Anfang jeder while-Loop-Iteration
- WriteSseEvent() erhält CancellationToken-Parameter, prüft vor Schreiben
- WriteAsync/FlushAsync mit CancellationToken aufgerufen
- Cancellation-Check vor und nach Tool-Execution
- OperationCanceledException in ExecuteTool wird re-thrown statt geschluckt
- Behebt: Stop-Button brach nur Frontend-Stream ab, Backend lief weiter
- Telegram-Config (Bot-Token, Webhook-Secret, ChatId) von User-Settings
in sessions-Tabelle verschoben → jede Session hat ihren eigenen Bot
- Link-Code-System komplett entfernt (TelegramLinkRecord gelöscht)
- /start verknüpft automatisch, kein /link mehr nötig
- Alle Endpoints session-basiert: /api/sessions/{id}/telegram/*
- Webhook-URL pro Session: /api/telegram/webhook/{sessionId}
Refactoring: Doppelten Chat-Code eliminiert
- Neuer ChatService: PrepareContext, BuildHistoryFromDb, ResolveModelId
- Neues ChatContext-Model als gemeinsame Datenstruktur
- AnthropicService.ChatNonStreaming() für Telegram (statt eigenem Loop)
- TelegramService um ~180 Zeilen reduziert, kein eigener Claude-Loop mehr
- ChatEndpoint nutzt ChatService.PrepareContext() statt eigener Setup-Logik
- Model-Mapping nur noch an einer Stelle (3x → 1x)
Frontend
- Telegram-Section von Settings-Page in Session-Bearbeitungsseite verschoben
- Webhook-Secret readonly, wird einmalig generiert und persistent gespeichert
- Status wird automatisch geladen, Register/Delete je nach Webhook-Status
- Toast-Notification-System (showToast) in ui.js
- Link-Code-Modal und Telegram-Menüpunkt im Dashboard entfernt
- Telegram-Badge auf Session-Cards (konfiguriert/verbunden)
Bugfix: Bearer statt x-api-key für OAuth-Tokens im TelegramService