diff --git a/CLAUDE.md b/CLAUDE.md index 0a9354d..dc82045 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,7 @@ ```yaml projekt: AegisSight-Monitor url: https://monitor.aegis-sight.de -server: ssh monitor (178.104.43.177, User: claude-dev) +server: ssh monitor (46.225.141.13, User: claude-dev) pfad: /home/claude-dev/AegisSight-Monitor quellcode: /home/claude-dev/AegisSight-Monitor/src/ datenbank: /mnt/gitea/osint-data/osint.db (SQLite WAL, geteilt mit Verwaltungsportal + Globe) @@ -203,3 +203,20 @@ regeln: - "Frontend-Aenderungen (HTML/JS/CSS) brauchen keinen Neustart" - "Backup-Dateien (.bak) nicht committen, vor Push loeschen" ``` + +## Changelog-Workflow + +Bei JEDER Aenderung am Monitor muessen zwei Dinge passieren: + +1. **TaskMate Wissensdatenbank** (Kategorie: "Changelog Monitor", category_id=31): + + +2. **Git Commit + Push zu Gitea** + +Changelog-Kategorien in TaskMate: +- 31 = Changelog Monitor +- 32 = Changelog Globe +- 33 = Changelog Netzwerkanalyse +- 34 = Changelog Verwaltung +- 35 = Changelog Website +- 36 = Changelog TaskMate diff --git a/data b/data index e4e4dbf..ceabf72 120000 --- a/data +++ b/data @@ -1 +1 @@ -/mnt/gitea/osint-data \ No newline at end of file +/home/claude-dev/osint-data \ No newline at end of file diff --git a/src/agents/analyzer.py b/src/agents/analyzer.py index 447a832..8cd3172 100644 --- a/src/agents/analyzer.py +++ b/src/agents/analyzer.py @@ -67,7 +67,7 @@ Erstelle ein strukturiertes Briefing auf {output_language} mit folgenden Abschni Verwende durchgehend Inline-Quellenverweise [1], [2], [3] etc. im Text. ## ZUSAMMENFASSUNG -Kompakte Übersicht als Aufzählung (4-8 Bullet Points mit "- "). Jeder Punkt fasst einen Kernaspekt des Themas in 1-2 Sätzen zusammen. Der Leser soll nach dieser Sektion das Wesentliche erfasst haben, ohne den Rest lesen zu müssen. +Kompakte Übersicht als Aufzählung (4-8 Bullet Points mit "- "). Jeder Punkt fasst einen Kernaspekt des Themas in 1-2 Sätzen zusammen. Der Leser soll nach dieser Sektion das Wesentliche erfasst haben, ohne den Rest lesen zu müssen. WICHTIG: Die ZUSAMMENFASSUNG besteht AUSSCHLIESSLICH aus Bullet Points. KEIN Fliesstext vor, zwischen oder nach den Bullet Points. Detaillierte Ausführungen gehören in die anderen Sektionen (HINTERGRUND, AKTUELLE LAGE etc.). ## HINTERGRUND Historischer Kontext, relevante Vorgeschichte @@ -178,6 +178,7 @@ WICHTIG zur Sektion ZUSAMMENFASSUNG: - Falls das bisherige Briefing eine Sektion "## ÜBERBLICK" hat, benenne sie in "## ZUSAMMENFASSUNG" um - Die ZUSAMMENFASSUNG muss als Aufzählung formatiert sein (4-8 Bullet Points mit "- "). Jeder Punkt fasst einen Kernaspekt in 1-2 Sätzen zusammen - Falls der bisherige ÜBERBLICK Fliesstext ist, wandle ihn in Bullet Points um +- KEIN Fliesstext vor, zwischen oder nach den Bullet Points. Die ZUSAMMENFASSUNG besteht AUSSCHLIESSLICH aus Bullet Points. Detaillierte Ausführungen gehören in die anderen Sektionen ## AKTEURE ## AKTUELLE LAGE ## EINSCHÄTZUNG diff --git a/src/agents/claude_client.py b/src/agents/claude_client.py index 6dd6c01..e624c90 100644 --- a/src/agents/claude_client.py +++ b/src/agents/claude_client.py @@ -4,7 +4,7 @@ import contextvars import json import logging from dataclasses import dataclass -from config import CLAUDE_PATH, CLAUDE_TIMEOUT, CLAUDE_MODEL_FAST +from config import CLAUDE_PATH, CLAUDE_TIMEOUT, CLAUDE_MODEL_FAST, CLAUDE_MODEL_STANDARD # ContextVar fuer Cancel-Event: Wird vom Orchestrator gesetzt, # call_claude prueft automatisch darauf -- kein Durchreichen noetig. @@ -56,11 +56,10 @@ async def call_claude(prompt: str, tools: str | None = "WebSearch,WebFetch", mod Args: prompt: Der Prompt fuer Claude tools: Kommagetrennte erlaubte Tools (None = keine Tools, --max-turns 1) - model: Optionales Modell (z.B. CLAUDE_MODEL_FAST fuer Haiku). None = CLI-Default (Opus). + model: Optionales Modell (z.B. CLAUDE_MODEL_FAST fuer Haiku). None = CLAUDE_MODEL_STANDARD (Opus 4.7). """ - cmd = [CLAUDE_PATH, "-p", "-", "--output-format", "json"] - if model: - cmd.extend(["--model", model]) + effective_model = model or CLAUDE_MODEL_STANDARD + cmd = [CLAUDE_PATH, "-p", "-", "--output-format", "json", "--model", effective_model] if tools: cmd.extend(["--allowedTools", tools]) else: diff --git a/src/config.py b/src/config.py index 68936ce..338a2ef 100644 --- a/src/config.py +++ b/src/config.py @@ -32,6 +32,7 @@ CLAUDE_TIMEOUT = 1800 # Sekunden (30 Min - Lage-Updates mit vielen Artikeln bra # Claude Modelle CLAUDE_MODEL_FAST = "claude-haiku-4-5-20251001" # Für einfache Aufgaben (Feed-Selektion) CLAUDE_MODEL_MEDIUM = "claude-sonnet-4-6" # Für qualitätskritische Aufgaben (Netzwerkanalyse) +CLAUDE_MODEL_STANDARD = "claude-opus-4-7" # Standard-Opus für Recherche, Analyse, Faktencheck # Ausgabesprache (Lagebilder, Faktenchecks, Zusammenfassungen) OUTPUT_LANGUAGE = "Deutsch" diff --git a/src/static/js/app.js b/src/static/js/app.js index e592016..7eece18 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -606,6 +606,9 @@ const App = { } } catch (e) { /* Kein kritischer Fehler */ } + // Heartbeat: periodischer Status-Abgleich als Sicherheitsnetz + this._statusSyncInterval = setInterval(() => this.syncRefreshStatus(), 60000); + // Zuletzt ausgewählte Lage wiederherstellen const savedId = localStorage.getItem('selectedIncidentId'); if (savedId) { @@ -2241,6 +2244,44 @@ async handleRefresh() { UI.showToast('Recherche abgebrochen.', 'info'); }, + /** + * Gleicht den lokalen Refresh-Status mit dem Server ab. + * Bereinigt verwaiste Status-Anzeigen, die durch verpasste WebSocket-Nachrichten entstehen. + */ + async syncRefreshStatus() { + if (this._refreshingIncidents.size === 0) return; + try { + const data = await API.getRefreshingIncidents(); + const serverRefreshing = new Set(data.refreshing || []); + const serverQueued = new Set(data.queued || []); + const serverAll = new Set([...serverRefreshing, ...serverQueued]); + + // Finde lokal als refreshing/queued markierte IDs, die serverseitig nicht mehr laufen + const stale = []; + this._refreshingIncidents.forEach(id => { + if (!serverAll.has(id)) stale.push(id); + }); + + if (stale.length > 0) { + console.log('Status-Sync: Bereinige verwaiste Refreshes:', stale); + stale.forEach(id => { + this._refreshingIncidents.delete(id); + this._updateSidebarDot(id); + UI._removeSidebarRefreshStatus(id); + delete UI._progressState[id]; + if (id === this.currentIncidentId) { + this._updateRefreshButton(false); + UI.hideProgress(id); + } + }); + UI._reindexQueuePositions(); + this.renderSidebar(); + } + } catch (e) { + // Netzwerkfehler ignorieren, naechster Zyklus probiert erneut + } + }, + minimizeProgress() { UI.minimizeProgress(this.currentIncidentId); }, diff --git a/src/static/js/components.js b/src/static/js/components.js index d2f4b38..1c55304 100644 --- a/src/static/js/components.js +++ b/src/static/js/components.js @@ -716,7 +716,10 @@ const UI = { if (!text) return 'Noch keine Zusammenfassung.'; let sources = []; try { sources = JSON.parse(sourcesJson || '[]'); } catch(e) {} - let html = this.escape(text); + // Nur Bullet-Point-Zeilen behalten, Fliesstext herausfiltern + const bulletLines = text.split("\n").filter(line => line.trim().startsWith("- ")); + const bulletText = bulletLines.length > 0 ? bulletLines.join("\n") : text; + let html = this.escape(bulletText); // Bullet points html = html.replace(/^- (.+)$/gm, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>\n?)+/gs, ''); diff --git a/src/static/js/ws.js b/src/static/js/ws.js index 667c336..2596254 100644 --- a/src/static/js/ws.js +++ b/src/static/js/ws.js @@ -34,6 +34,10 @@ const WS = { console.log('WebSocket verbunden'); this.reconnectDelay = 2000; this._startPing(); + // Nach Reconnect: Refresh-Status mit Server abgleichen + if (typeof App !== 'undefined' && App.syncRefreshStatus) { + App.syncRefreshStatus(); + } return; } try {