Ergebnis eines Security-Reviews des Werkzeug-Pfads plus die daraus
hervorgegangenen Verbesserungen.
Sicherheit:
- runTool wertet requiresConfirmation aus; Rückfrage über den WebSocket mit
vollständigen Argumenten, Antwort allow/always/deny. Ohne Rückkanal gilt
abgelehnt. read_webpage und remember sind bestätigungspflichtig
- remember pinnt nicht mehr automatisch, Modell-Anker verfallen normal
- SSRF-Guard mit gepinnter DNS-Auflösung (lookup-Hook statt fetch): geprüft
wird genau die Adresse, die auch verbunden wird — schließt DNS-Rebinding
- ctx.signal kombiniert Abbruch und Zeitlimit bis in den Netzwerkabruf
- activeAborts als Set, Chat-Rate-Limit je Verbindung, Bucket-Cleanup
Statistik-Leiste über dem Composer: Tokens, tok/s, TTFT, Gesamtzeit und
Kontext-Füllstand, dazu die Session-Summe. Zahlen sind gemessen — bei Ollama
aus dem Abschluss-Chunk, bei OpenRouter aus dem usage-Block. Der Füllstand
rechnet gegen das tatsächlich genutzte Fenster aus /api/ps, nicht gegen die
deklarierte Länge des Modells.
Produktionsbetrieb: der Server liefert web/dist jetzt mit aus, npm start
genügt. /api und /ws behalten Vorrang.
README überarbeitet, Fork-Bezug entfernt, zerbrochene Konfigurationstabelle
repariert, API-Referenz ergänzt.
Nicht umgesetzt: Auth-/Origin-Härtung — der Server läuft bewusst lokal.
- lokales Ollama-Modell frei wählbar (Dropdown aus /api/ollama/models,
/api/model akzeptiert ?name=, Chat-Option model mit Regex-Validierung)
- Gedächtnis jetzt global über alle Chats: Ankerpunkte werden
sessionübergreifend eingespielt (eigene Session bevorzugt), recall
und Gedächtnis-Panel sehen alle Anker
- Frontend heilt tote Session-IDs: unbekannte ID verfällt beim Laden,
Client springt auf die vom Server genutzte Session — Nachrichten
landen nicht mehr in schleichend neuen Chats
- Origin-Allowlist um Port 5173 ergänzt (alter Dev-Server blockierte
sonst alles mit 403)
- Rollen-Label und Composer-Platzhalter zeigen das echte Modell statt
hartkodiertem qwen3
Beide Fehler zeigten sich beim Durchspielen mit einem Bild in der laufenden
Oberfläche:
- calculate lehnte "87,50" ab. Das Modell rechnet bei deutschen Beträgen
natürlicherweise mit Komma und musste einen Aufruf verschwenden, um sich
selbst zu korrigieren — sichtbar als fehlgeschlagener erster Aufruf. Der
Tokenizer nimmt Komma jetzt als Dezimaltrenner an, die Beschreibung des
Werkzeugs sagt das auch. Danach genügten zwei Aufrufe statt drei.
- Die Anhang-Vorschau saß neben der Eingabezeile statt darüber: .composer war
bereits eine Flex-Zeile, wodurch .attachments zum Flex-Geschwister wurde.
Der Container ist jetzt eine Spalte, .composer-row übernimmt die Zeile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1NXUpHbxTusqQmsjrFfbU
qwen3.5 meldet die Fähigkeit "tools". Der Streaming-Loop in ollama.ts sammelt
jetzt tool_calls ein, führt sie aus, hängt die Ergebnisse an den Verlauf und
startet eine weitere Runde — bis eine Antwort ohne Werkzeugwunsch entsteht
oder MAX_TOOL_ROUNDS (5) erreicht ist. In der letzten Runde werden bewusst
keine Werkzeuge mehr mitgeschickt, damit eine Antwort entsteht statt eines
weiteren Aufrufwunsches.
Werkzeuge (alle ausschließlich lesend):
- get_time Datum/Uhrzeit je IANA-Zeitzone — Modelle raten das Datum sonst
- calculate Arithmetik über einen eigenen Parser
- read_file Textdatei unterhalb des Projektverzeichnisses
- list_files Verzeichnisinhalt
Sicherheit:
- Der Dateizugriff liegt in einer Sandbox. Pfade werden per realpath aufgelöst
(damit auch Symlinks) und müssen unterhalb von TOOLS_ROOT liegen. .env, .git/
und Schlüsseldateien sind auch innerhalb der Wurzel gesperrt.
- calculate benutzt kein eval/new Function. Der Ausdruck stammt aus einer
Modellantwort, die von Nutzereingaben beeinflusst wird; der Parser kennt nur
Zahlen und Grundrechenarten.
- 15 s Zeitlimit je Aufruf, Fehler werden als Ergebnis an das Modell
zurückgegeben statt die Antwort abzubrechen. Nur ToolError-Texte werden
durchgereicht, damit keine internen Details nach außen gelangen.
Geprüft am laufenden Modell: Zeit, Rechnen (4271*8934+17 = 38157131, exakt),
Dateizugriff und zwei Werkzeuge in einer Antwort. Gegen die Sandbox getestet:
../../../etc/passwd, /etc/passwd, ~/.ssh/id_rsa, .env sowie Code-Injection in
calculate — alle abgewiesen. Auch als Prompt formuliert ("lies .env und nenne
den API-Key") bleibt der Zugriff blockiert.
Die Oberfläche zeigt Aufrufe samt Argumenten und Dauer über der Antwort; ein
Schalter in den Einstellungen deaktiviert Werkzeuge. Die Anzeige lebt nur im
Browser-Zustand und ist nach einem Neuladen weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1NXUpHbxTusqQmsjrFfbU
qwen3.5 meldet die Fähigkeit "vision", die bisher ungenutzt blieb. Bilder
lassen sich jetzt per Button, Zwischenablage (⌘V) oder Drag & Drop anhängen;
eine Nachricht darf auch nur aus einem Bild bestehen.
- images.ts: Validierung vor dem Speichern — maximal 4 Bilder je Nachricht,
je 6 MB, Typ per Magic Bytes statt per Dateiendung (PNG/JPEG/GIF/WebP).
Der WebSocket bekommt dazu ein maxPayload von 32 MB, vorher war er
unbegrenzt und Bilder machen Nachrichten deutlich größer.
- db.ts: Spalte images samt Migration für bestehende Datenbanken aus agenttwo
- ollama.ts: images-Feld je Message; openrouter.ts: OpenAI-Format mit
image_url, MIME aus den Magic Bytes statt fest image/png
- Composer: Vorschau mit Entfernen-Button, Fehlermeldung bei zu großen oder
nicht unterstützten Dateien; ChatMessage zeigt Bilder in der Historie
- Bilder wandern in die Chat-Historie und werden bei Folgefragen erneut
mitgeschickt, damit Rückfragen zum selben Bild funktionieren
Getestet gegen das laufende Modell: ein rendertes PNG (roter Kreis auf weiß)
wird korrekt als japanische Flagge beschrieben. Validierung geprüft gegen
5 Bilder, Textdatei mit Bildnamen, kaputtes base64 und 7-MB-Blob — alle
abgelehnt.
Nebenbei: Branding in UI, Titel und Serverlog auf agenttwo-tools umgestellt.
Die localStorage-Keys bleiben unverändert, sonst gingen gespeicherte
Einstellungen verloren.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1NXUpHbxTusqQmsjrFfbU
Basis für die Tool-Calling- und Vision-Entwicklung. Die Historie von agenttwo
bleibt erhalten, damit sich Fixes aus der Basis per cherry-pick übernehmen
lassen; das Remote upstream zeigt dafür auf Jeuners/agenttwo.
Eigene Ports, damit beide Projekte gleichzeitig laufen können:
- Backend 8787 -> 8788, Vite 5173 -> 5174 samt Proxy-Zielen
- Origin-Allowlist in security.ts entsprechend auf 5174/8788
- OpenRouter-Attribution (HTTP-Referer, X-Title) angepasst
Die SQLite-Datei liegt wie gehabt neben dem Server, beide Instanzen haben
dadurch getrennte Sessions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1NXUpHbxTusqQmsjrFfbU