- Neuer Endpoint GET /api/sessions/{id}/payload-preview
Gibt den exakten Anthropic-API-Payload als JSON zurück (read-only)
- Chat-Menü: 'Payload anzeigen' öffnet Modal mit formatiertem JSON
- Reminder-Logik umgebaut: halbes Intervall (15) vor Ende,
dann volle Intervalle (30) rückwärts statt von vorne gezählt
- CSS: Payload-Modal Styling, kleine Variable-Fixes
- <hint> wird an jede User-Nachricht angehängt wenn Plan-Modus aktiv
- Erinnert Agent an Einschränkungen: keine Dateiänderungen, keine Shell-Befehle (außer read-only), keine Commits
- Analog zum bestehenden cat -n Hint
- POST /api/admin/api-keys/scanned/recheck-all: prüft alle nicht-adoptierten Scanned-Keys sequenziell
- Button mit Loading-State, Ergebnis-Toast und Auto-Reload
- EasyMDE/CodeMirror refresh() nach initMoreButton() Einblenden (behebt leeren Editor-Bug)
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
- ScanSerper() durchsucht Google via Serper.dev nach geleakten API-Keys
- Exakte Phrase-Suche pro KeyPattern, bis zu 100 Ergebnisse pro Query
- Keys aus Snippets und Titles via Regex extrahiert
- Source-Feld (github/serper/gist/pastebin) in ScannedKeyRecord + DB-Migration
- RunScan() kombiniert GitHub + Serper mit Deduplizierung
- Service startet auch wenn nur SerperApiKey konfiguriert ist
- Frontend: Source-Icons und verkürzte URLs für Nicht-GitHub-Quellen
- Config: SerperApiKey in appsettings.json
- Usage-Badge oben zentriert im Chat (nur Admin)
- Zeigt Sitzung/Woche-Balken des aktiven API-Keys
- Blendet bei Scroll-Up aus, bei Scroll-Down ein
- Klick auf Badge öffnet Key-Switch-Modal (via _createModal)
- Ganze Row klickbar für Key-Wechsel
- Refresh-Button neben Badge für manuellen Key-Check
- Badge aktualisiert sich automatisch bei neuen Nachrichten (pollHistory)
- Cleanup in destroy()
- 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
- router.js: Back-Buttons navigieren zu data-page oder dashboard statt history.back()
- chat-page.js: Chat Back-Button mit gleichem Fallback
- Behebt Problem bei Direktlinks (z.B. #chat/123) ohne vorherigen SPA-Eintrag
- InfoEndpoint: EmbeddingService injiziert, embeddingAvailable in /api/info Response
- title.js: _showEmbeddingWarning() Modal via _createModal() wenn Modell fehlt
- base.css: ol margin-left wie ul (geordnete Listen)
- typing-phrases.js in index.html eingebunden
- startTypingIndicator() / stopTypingIndicator() in chat.js
- Agent-Name via setCurrentAgentName() aus chat-page.js gesetzt
- Phrase wechselt alle 3 Sekunden zufällig
- Cleanup bei destroy() und finally-Block
- 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.
tool_use case setzt jetzt dataset.toolName auf das DOM-Element.
tool_result case erkennt todo_write und ruft buildTodoList() auf
statt ein hidden text-div anzuhängen.
Backend: sb.AppendLine() durch sb.Append()+\n ersetzt für
konsistente Zeilenenden auf allen Plattformen.
Frontend: \r defensiv entfernen vor Regex-Matching, da
JS-Regex (.+)$ kein \r matcht und nur letzte Zeile geparsed wurde.
- 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
_mergeConsecutiveAssistantTexts aktualisierte beim Zusammenführen
aufeinanderfolgender Assistant-Text-Messages die id und createdAt,
aber nicht den sortKey. Dadurch blieb lastKnownMessageId auf dem
niedrigeren sortKey der ersten Message stehen.
Folge: Beim nächsten Poll wurden bereits geladene Messages erneut
geliefert (afterSortKey zu niedrig) → Duplikate im Chat.
lastKnownMessageId wurde pro Poll nur um 1 erhöht statt auf den
tatsächlich höchsten Wert.