agenttwo-tools/HANDOFF.md
Jeuner 695fcf82f4 docs: Handoff für die nächste Sitzung
Arbeitsnotiz mit Stand, den beiden nächsten Schritten (Export/Snapshots,
Papierkorb) und dem zurückgestellten Cron-Entwurf. Dazu die Fallstricke
dieser Sitzung, damit sie nicht zweimal Zeit kosten — vor allem das
falsche gh-Remote, Ollamas effektives Kontextfenster und die Reihenfolge
von onStats und onDone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AUP4R3rgq4XwVs4bVf7uh
2026-08-28 16:04:38 +02:00

5.6 KiB

Handoff

Stand: 28.08.2026. Arbeitsnotiz für die nächste Sitzung — kein Teil der Projektdokumentation, die steht im README.

Wo wir stehen

Auf main:

Commit Inhalt
8a489df Werkzeug-Bestätigung, DNS-Pinning, Statistik-Leiste, Produktionsbetrieb (Squash aus PR #1)
a36e366 Mic-/Anhang-Icons als SVG, blinkender Warte-Cursor

Arbeitsverzeichnis sauber, npm run typecheck und vite build grün.

Als Nächstes

1. Export und Snapshots

Anlass: Am 28.08. waren die vier Chats des Nutzers plötzlich weg. Rettbar waren sie nur, weil die Löschungen noch im SQLite-WAL standen und der Hauptfile den Stand des letzten Checkpoints hielt. Das war Glück, keine Vorkehrung.

  • Export eines Chats als Markdown (Nachrichten, Anhänge als Dateinamen, Werkzeugaufrufe optional). Naheliegender Ort: Button im Sidebar-Kontext oder GET /api/sessions/:id/export.
  • Snapshot der Datenbank: beim Serverstart und danach täglich eine Kopie nach server/backups/data-JJJJ-MM-TT.sqlite, die letzten ~7 behalten. Wichtig: VACUUM INTO statt cp — das schreibt einen konsistenten Stand ohne WAL-Abhängigkeit. Verzeichnis in .gitignore aufnehmen.

2. Papierkorb statt Sofortlöschung

Der ✕-Button in web/src/components/Sidebar.tsx:38 ruft onDelete ohne jede Rückfrage. Am 28.08. sind so vier Chats mit 34 Nachrichten in 1,1 Sekunden verschwunden (vier Klicks, je ~250 ms auseinander, jeweils auf den obersten Eintrag, der nach dem Löschen nachrückt).

  • Spalte deleted_at in sessions; ✕ setzt sie, statt zu löschen
  • Listenabfragen filtern deleted_at IS NULL
  • Endgültiges Löschen nach 30 Tagen oder manuell im Papierkorb
  • Optional zusätzlich: kurzes „Rückgängig" nach dem Klick

Eine reine Bestätigungsabfrage wäre billiger, aber schwächer — sie schützt nicht vor dem schnellen Durchklicken, das hier genau passiert ist.

3. Datenbankpfad konfigurierbar machen (klein, aber Voraussetzung)

server/src/db.ts:5 hat den Pfad fest verdrahtet. Jeder Testlauf schreibt damit in die echte Datenbank des Nutzers — genau das hat am 28.08. Testchats in seiner Historie hinterlassen. Eine Variable DB_PATH (Default wie bisher) erlaubt Tests gegen eine Wegwerf-Datei und gehört vor allem, was automatisiert testet.

Später: geplante Aufgaben (Cron)

Idee des Nutzers, bewusst zurückgestellt. Passt gut — Werkzeug-Loop, Gedächtnis und TTS liegen bereit, und mit dreamTimers (index.ts:71) existiert schon ein kleiner Scheduler. Zwei Punkte entscheiden das Design:

Bestätigungspflicht. read_webpage und remember laufen nur nach Freigabe, und ohne Rückkanal gilt „abgelehnt" (tools/index.ts:107). Ein Job um 8 Uhr hat niemanden zum Fragen — ausgerechnet der nützlichste Job würde zuverlässig scheitern. Auflösung: Freigabe wandert vom Ausführungs- zum Anlegezeitpunkt. Beim Erstellen hakt der Nutzer die erlaubten Werkzeuge an und sieht sie zusammen mit dem Prompt; der Job trägt diese Allowlist als ToolContext.confirm. Das ist Einwilligung, nicht ihre Umgehung.

Dabei eng bleiben: remember nicht in die Standard-Allowlist. Ein unbeaufsichtigter Job, der Fremdinhalt liest und daraus dauerhafte „Fakten über den Nutzer" ableitet, ist genau die Kette, die der Security-Review geschlossen hat. Jeder Job schreibt in einen eigenen Chat.

Laufzeit. Cron feuert nur, solange der Server läuft; gestartet wird er heute von Hand. Statt launchd lieber Verpasstes beim nächsten Start nachholen — deutlich weniger Systemeingriff.

Offen beim Nutzer

  • Die vier wiederhergestellten Chats: unklar, ob die Löschung am 28.08. um 15:02 Absicht war. Falls ja, gehören sie wieder raus. Sicherungen liegen in ~/agenttwo-backups/ (vor-wal.sqlite = 8 Sessions inkl. vier Testchats, geloeschter-stand.sqlite* = leerer Stand).
  • Static-Serving im Entwicklungsbetrieb: seit 8a489df liefert :8788 auch web/dist aus, sobald ein Build existiert — ein Standbild neben dem lebenden Vite auf :5174. Abschaltbar über NODE_ENV im dev-Script, bisher nicht gemacht.

Fallstricke, teuer gelernt

  • gh zielt auf das falsche Repo. Es löst auf Jeuners/agenttwo (das upstream-Remote) auf, nicht auf agenttwo-tools. Ohne --repo Jeuners/agenttwo-tools scheitert gh pr create mit einem irreführenden „No commits between". Dauerhaft: gh repo set-default Jeuners/agenttwo-tools.
  • Ollamas Kontextfenster ist nicht das deklarierte. qwen3.5 meldet 262144, geladen läuft es mit 4096. Der wahre Wert steht in GET /api/ps, nicht in /api/show.
  • Emoji lassen sich nicht einfärben. 🎙 und 📎 sind in den Fonts fest grau. Zustandsfarben in CSS verpuffen an ihnen — deshalb jetzt SVG mit currentColor.
  • .caret ist belegt (Aufklapp-Dreieck des Denkprozesses). Der Warte-Cursor heißt .stream-caret.
  • onStats muss vor onDone abgewartet werden. Der /api/ps-Lookup ist asynchron; ohne await geht die Stats-Nachricht hinter dem done raus und der Client verwirft sie. Fiel nur auf, weil der erste (ungecachte) Lauf leer blieb und der zweite nicht.
  • OpenRouters usage kommt nach finish_reason. Ein früher Return verschluckt die Tokenzahlen. Es braucht stream_options.include_usage.
  • Werkzeuge ohne Rückkanal werden abgelehnt. Skripte und Tests, die runTool direkt aufrufen, bekommen bei bestätigungspflichtigen Werkzeugen „abgelehnt". Das ist Absicht — beim Testen ein confirm mitgeben.