Neue /einstellungen-Seite (Zahnrad im Header) mit allen bisherigen .env-Werten
(Ollama, E-Mail, CalDAV, REST-API), gespeichert in SQLite (app_state, cfg:-Prefix).
Libs lesen per lib/config.js zur Laufzeit statt beim Start -> Aenderungen
wirken sofort, kein Neustart. Bestehende .env wird beim ersten Start einmalig
migriert.
Co-Authored-By: Claude <noreply@anthropic.com>
Rolle, Tonfall und Regeln der KI (Unterlagen, Chat, E-Mail-Antwort,
E-Mail-Absage, gemeinsame Stilregeln) lagen fest im Code. Sie liegen jetzt
als Defaults in lib/prompts.js und lassen sich auf der Vorlagen-Seite je
Prompt anpassen und wieder zuruecksetzen.
Nur die System-Prompts sind editierbar. Die User-Prompts tragen das
JSON-Skeleton, gegen das die Antwort geparst wird - ein Tippfehler dort
wuerde die Generierung lahmlegen, also bleiben sie im Code.
Gespeichert wird nur, was abweicht: ein Override ist eine Zeile in der neuen
Tabelle `prompts`, "Zuruecksetzen" loescht sie. Damit bleiben die Defaults im
Code die Wahrheit und wandern bei Updates automatisch mit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Systemprompt enthielt früher nur die 12 jüngsten Bewerbungen (LIMIT 12),
daher fand die KI ältere Firmen wie QBS KLIMTAX nicht. Alle Bewerbungen in den
Prompt zu laden machte den Chat sehr langsam, da der Prompt bei jedem Turn
voll neu verarbeitet wird.
Jetzt nutzt der Assistent Ollama-Tool-Calling, um Datenbankdaten on demand
nachzuschlagen. Der Systemprompt bleibt klein und konstant (nur Profil +
Datum), unabhängig von der Anzahl der Bewerbungen.
- lib/chat.js: streamChat mit tools + runChat-Tool-Schleife (max 4 Runden)
- server.js: Tools suche_bewerbungen/list_bewerbungen/bewerbung_detail/
kommende_termine mit SQL-Queries; gatherChatContext auf Kern reduziert
- public/js/chat.js: Tool-Indikator ("durchsucht Bewerbungen…") im UI
Co-Authored-By: Claude <noreply@anthropic.com>
Der Assistent kannte nur Firma/Stelle/Status/Notizen und konnte daher keine
firmenspezifischen Fragen beantworten. gatherChatContext lädt jetzt pro
Bewerbung: Ort, interne Notizen, Stellenbeschreibung (bzw. verknüpftes
Jobangebot), Kontakt/Ansprechpartner und die E-Mail-Korrespondenz (Betreffe).
Zusätzlich wird der Lebenslauf des Bewerbers injiziert, damit Antworten auf
"was für mich wichtig" zugeschnitten werden können.
Relevante Bewerbungen = Termin-Bewerbungen + 12 jüngste (gebunden im Token-Budget).
Co-Authored-By: Claude <noreply@anthropic.com>
gatherChatContext() brach mit SQLITE_ERROR ab, weil "e.from_addr AS from"
das SQL-Schlüsselwort from als Alias nutzte. Der Fehler wurde im Route-Handler
geschluckt (context={}), sodass der Assistent keinen Zugriff auf Bewerbungen/
Termine/E-Mails hatte. Alias entfernt.
Zusätzlich erhält der System-Prompt jetzt das heutige Datum, damit der
Assistent "morgen"/"heute" aus den UTC-Termin-Zeitstempeln ableiten kann.
Co-Authored-By: Claude <noreply@anthropic.com>
Eigenes Chat-Interface mit SSE-Streaming gegen das hinterlegte Ollama-Modell,
gegroundet in den Bewerbungs-/E-Mail-/Termindaten des Nutzers.
- lib/chat.js: streamChat (Ollama stream:true, NDJSON-Token) + buildContextPrompt
- chat_threads/chat_messages Tabellen (CASCADE, Index)
- Routen: GET /chat, Thread-CRUD, POST /messages (SSE, AbortController)
- views/chat.ejs + public/js/chat.js + Floating-Button im Footer
- hasApiKey-Gating (503 ohne OLLAMA_API_KEY)
Co-Authored-By: Claude <noreply@anthropic.com>